mirror of https://github.com/garrytan/gstack.git
Merge 3bc9bcabc8 into a3259400a3
This commit is contained in:
commit
7b03a32b55
|
|
@ -128,8 +128,24 @@ const CATALOG_MODE: 'trim' | 'full' = (() => {
|
|||
// the model skips them when EXPLAIN_LEVEL: terse appears in the preamble echo).
|
||||
// Opt-in via the build flag so most users get the runtime-flexible default.
|
||||
const EXPLAIN_LEVEL_ARG = process.argv.find(a => a.startsWith('--explain-level'));
|
||||
function loadConfiguredExplainLevel(): 'default' | 'terse' {
|
||||
if (!RESPECT_DETECTION) return 'default';
|
||||
const stateDir = process.env.GSTACK_STATE_ROOT
|
||||
|| process.env.GSTACK_HOME
|
||||
|| process.env.GSTACK_STATE_DIR
|
||||
|| path.join(process.env.HOME || '', '.gstack');
|
||||
const configPath = path.join(stateDir, 'config.yaml');
|
||||
try {
|
||||
const config = fs.readFileSync(configPath, 'utf-8');
|
||||
const match = config.match(/^\s*explain_level\s*:\s*(default|terse)\s*$/m);
|
||||
return match?.[1] === 'terse' ? 'terse' : 'default';
|
||||
} catch {
|
||||
return 'default';
|
||||
}
|
||||
}
|
||||
|
||||
const EXPLAIN_LEVEL: 'default' | 'terse' = (() => {
|
||||
if (!EXPLAIN_LEVEL_ARG) return 'default';
|
||||
if (!EXPLAIN_LEVEL_ARG) return loadConfiguredExplainLevel();
|
||||
const val = EXPLAIN_LEVEL_ARG.includes('=')
|
||||
? EXPLAIN_LEVEL_ARG.split('=')[1]
|
||||
: process.argv[process.argv.indexOf(EXPLAIN_LEVEL_ARG) + 1];
|
||||
|
|
|
|||
49
setup
49
setup
|
|
@ -1277,24 +1277,26 @@ if [ "$NO_TEAM_MODE" -eq 1 ]; then
|
|||
log "Team mode disabled: auto-update hook removed."
|
||||
fi
|
||||
|
||||
# ─── GBrain detection + conditional SKILL.md regen ──────────────────────
|
||||
# ─── User-local SKILL.md regen ───────────────────────────────────────────
|
||||
#
|
||||
# Detect whether gbrain is installed and persist the result to
|
||||
# ~/.gstack/gbrain-detection.json so gen-skill-docs can decide whether to
|
||||
# render GBRAIN_CONTEXT_LOAD and GBRAIN_SAVE_RESULTS blocks. If detected,
|
||||
# regenerate the Claude-host SKILL.md files with the un-suppressed
|
||||
# (compressed) brain-aware blocks via `bun run gen:skill-docs:user`.
|
||||
# render GBRAIN_CONTEXT_LOAD and GBRAIN_SAVE_RESULTS blocks.
|
||||
#
|
||||
# If gbrain is not detected, the canonical no-gbrain SKILL.md files
|
||||
# (which were just generated above by `gen:skill-docs --host claude` if
|
||||
# applicable, or which are checked in) stay as-is. Zero token overhead
|
||||
# for non-gbrain users.
|
||||
# Also honor user-local explain_level=terse at setup/session-update time. The
|
||||
# canonical checked-in SKILL.md files stay full-prose; `gen:skill-docs:user`
|
||||
# applies local config only when setup updates a user's installed skills.
|
||||
#
|
||||
# Users who install gbrain after running ./setup should re-run setup OR
|
||||
# call `gstack-config gbrain-refresh` + `bun run gen:skill-docs:user`.
|
||||
DETECT_BIN="$SOURCE_GSTACK_DIR/bin/gstack-gbrain-detect"
|
||||
GBRAIN_STATE_DIR="${GSTACK_HOME:-$HOME/.gstack}"
|
||||
DETECTION_FILE="$GBRAIN_STATE_DIR/gbrain-detection.json"
|
||||
USER_LOCAL_RENDER_REASON=""
|
||||
USER_EXPLAIN_LEVEL="$("$GSTACK_CONFIG" get explain_level 2>/dev/null || echo default)"
|
||||
if [ "$USER_EXPLAIN_LEVEL" = "terse" ]; then
|
||||
USER_LOCAL_RENDER_REASON="explain_level=terse"
|
||||
fi
|
||||
# PID-unique tmp so concurrent setups (parallel Conductor workspaces) can't
|
||||
# clobber each other's in-flight detection write.
|
||||
DETECTION_TMP="$DETECTION_FILE.$$.tmp"
|
||||
|
|
@ -1306,19 +1308,10 @@ if [ -x "$DETECT_BIN" ]; then
|
|||
# detection (exit 0 iff ok), so setup, bin/dev-setup, and gstack-config
|
||||
# all gate on the same check instead of re-grepping the JSON.
|
||||
if "$DETECT_BIN" --is-ok 2>/dev/null; then
|
||||
if [ -n "${GSTACK_SKIP_GBRAIN_REGEN:-}" ]; then
|
||||
# Dev/source tree (set by bin/dev-setup): never regenerate tracked
|
||||
# SKILL.md in place — that dirties checked-in source. Detection is
|
||||
# still persisted above; the dev workspace renders the :user variant
|
||||
# into an untracked dir, and other projects get blocks via
|
||||
# `gstack-config gbrain-refresh`.
|
||||
log "gbrain detected — GSTACK_SKIP_GBRAIN_REGEN set: leaving tracked SKILL.md canonical (dev/source tree)."
|
||||
if [ -n "$USER_LOCAL_RENDER_REASON" ]; then
|
||||
USER_LOCAL_RENDER_REASON="$USER_LOCAL_RENDER_REASON + brain-aware blocks"
|
||||
else
|
||||
log "gbrain detected — regenerating Claude SKILL.md with brain-aware blocks (~250 token overhead per planning skill)..."
|
||||
(
|
||||
cd "$SOURCE_GSTACK_DIR"
|
||||
bun_cmd run gen:skill-docs:user --host claude 2>&1 | tail -3
|
||||
) || log " warning: gen:skill-docs:user failed — run 'bun run gen:skill-docs:user' manually if you want brain-aware blocks"
|
||||
USER_LOCAL_RENDER_REASON="brain-aware blocks"
|
||||
fi
|
||||
else
|
||||
log "gbrain not detected — brain-aware blocks suppressed in planning-skill SKILL.md files (zero token overhead)."
|
||||
|
|
@ -1330,6 +1323,22 @@ if [ -x "$DETECT_BIN" ]; then
|
|||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$USER_LOCAL_RENDER_REASON" ]; then
|
||||
if [ -n "${GSTACK_SKIP_GBRAIN_REGEN:-}" ]; then
|
||||
# Dev/source tree (set by bin/dev-setup): never regenerate tracked
|
||||
# SKILL.md in place — that dirties checked-in source. Detection is still
|
||||
# persisted above; the dev workspace renders the :user variant into an
|
||||
# untracked dir, and other projects get local blocks via setup/session-update.
|
||||
log "user-local SKILL.md render needed ($USER_LOCAL_RENDER_REASON) — GSTACK_SKIP_GBRAIN_REGEN set: leaving tracked SKILL.md canonical (dev/source tree)."
|
||||
else
|
||||
log "regenerating Claude SKILL.md with user-local settings ($USER_LOCAL_RENDER_REASON)..."
|
||||
(
|
||||
cd "$SOURCE_GSTACK_DIR"
|
||||
bun_cmd run gen:skill-docs:user --host claude 2>&1 | tail -3
|
||||
) || log " warning: gen:skill-docs:user failed — run 'bun run gen:skill-docs:user --host claude' manually to apply user-local settings"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 11. Plan-tune cathedral hook install (T8).
|
||||
#
|
||||
# Registers PostToolUse (deterministic AUQ capture) + PreToolUse (preference
|
||||
|
|
|
|||
|
|
@ -393,6 +393,39 @@ describe('gen-skill-docs', () => {
|
|||
expect(Buffer.byteLength(writingStyle, 'utf-8')).toBeLessThan(2_000);
|
||||
});
|
||||
|
||||
test('user-local generation structurally honors explain_level: terse', () => {
|
||||
const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-user-config-'));
|
||||
const outDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-user-render-'));
|
||||
try {
|
||||
fs.writeFileSync(path.join(stateDir, 'config.yaml'), 'explain_level: terse\n');
|
||||
const result = Bun.spawnSync([
|
||||
'bun', 'run', 'scripts/gen-skill-docs.ts',
|
||||
'--respect-detection',
|
||||
'--host', 'claude',
|
||||
'--out-dir', outDir,
|
||||
], {
|
||||
cwd: ROOT,
|
||||
stdout: 'pipe',
|
||||
stderr: 'pipe',
|
||||
env: { ...process.env, GSTACK_HOME: stateDir, GSTACK_STATE_ROOT: stateDir },
|
||||
});
|
||||
const stderr = result.stderr.toString();
|
||||
const stdout = result.stdout.toString();
|
||||
|
||||
expect(result.exitCode, `${stdout}\n${stderr}`).toBe(0);
|
||||
|
||||
const content = fs.readFileSync(path.join(outDir, 'plan-eng-review', 'SKILL.md'), 'utf-8');
|
||||
expect(content).toContain('Terse mode (build-time)');
|
||||
expect(content).not.toContain('Curated jargon list lives');
|
||||
expect(content).not.toContain('## Completeness Principle — Boil the Ocean');
|
||||
expect(content).not.toContain('## Confusion Protocol');
|
||||
expect(content).not.toContain('## Context Health (soft directive)');
|
||||
} finally {
|
||||
fs.rmSync(stateDir, { recursive: true, force: true });
|
||||
fs.rmSync(outDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('slim voice section preserves the gstack voice contract', () => {
|
||||
const content = readSkillUnion('plan-eng-review'); // carved: review body moved to section
|
||||
const voice = extractMarkdownSection(content, '## Voice');
|
||||
|
|
@ -2507,6 +2540,20 @@ describe('setup script validation', () => {
|
|||
expect(setupContent).toContain('GSTACK_CONFIG');
|
||||
});
|
||||
|
||||
test('setup applies user-local skill rendering for terse mode without requiring gbrain', () => {
|
||||
const regenSectionStart = setupContent.indexOf('# ─── User-local SKILL.md regen');
|
||||
const regenSectionEnd = setupContent.indexOf('# 11. Plan-tune cathedral hook install', regenSectionStart);
|
||||
const regenSection = setupContent.slice(regenSectionStart, regenSectionEnd);
|
||||
const gbrainElseStart = regenSection.indexOf('gbrain not detected');
|
||||
const userRenderIndex = regenSection.indexOf('gen:skill-docs:user --host claude');
|
||||
|
||||
expect(regenSection).toContain('get explain_level');
|
||||
expect(regenSection).toContain('explain_level=terse');
|
||||
expect(userRenderIndex).toBeGreaterThan(-1);
|
||||
expect(gbrainElseStart).toBeGreaterThan(-1);
|
||||
expect(userRenderIndex).toBeGreaterThan(gbrainElseStart);
|
||||
});
|
||||
|
||||
test('setup supports --prefix flag', () => {
|
||||
expect(setupContent).toContain('--prefix)');
|
||||
expect(setupContent).toContain('SKILL_PREFIX=1; SKILL_PREFIX_FLAG=1');
|
||||
|
|
|
|||
Loading…
Reference in New Issue