mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 05:56:37 +01:00
* feat(gen-skill-docs): add --plan-mode=off to omit the plan-mode preamble (#3168)
Generated skills ship a ~1.8 KB "## Plan Mode Safe Operations" +
"## Skill Invocation During Plan Mode" block on every skill, for hosts
and users who never enter plan mode. This adds a --plan-mode=off flag to
gen-skill-docs that drops that block at gen time. Default builds
(planMode undefined or true) keep the block byte-identically.
The "PLAN MODE EXCEPTION — ALWAYS RUN" markers inside skill bodies
still apply when the flag is off.
(cherry picked from commit 51edee9915)
* style(types): fix stray indentation on TemplateContext.installRoot
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* feat(config): plan_mode_preamble key wires --plan-mode=off into setup and every installed render
gstack-config plan_mode_preamble (on|off, default on) makes ./setup pass
--plan-mode=off to every gen:skill-docs render, gbrain-refresh and the
gstack-relink router render. The default Claude install, which otherwise
serves the committed SKILL.md files, gets a plain render with the key off;
the activation record notes plan_mode=off so switching back on removes the
render, including on the gbrain=unknown keep branch. With the key on or
unset every path and record is byte-for-byte unchanged.
Fixes #3168. Finishes #3213 by @xingzihai.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(setup,relink): plan_mode_preamble=off survives builds and failed renders; alias follows the router
Review findings on the plan_mode_preamble wiring:
- a build renders env hosts without --plan-mode=off, so with the key off
setup no longer skips their re-render after a build;
- with the key off the gbrain-absent branch leaves the render to the plain
re-render, so a failed render keeps the previous one;
- gstack-relink refreshes the _gstack-command alias after re-rendering the
router (also fixes the same staleness for disabled_skills).
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: xingzihai <xingzihai@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
75 lines
3.0 KiB
TypeScript
75 lines
3.0 KiB
TypeScript
/**
|
|
* gstack-config plan_mode_preamble (#3168): on | off, default on.
|
|
*
|
|
* off makes ./setup render every installed skill without the plan-mode
|
|
* preamble (--plan-mode=off). The key is a closed domain like explain_level:
|
|
* an unknown value warns and stores on. Setting it outside ./setup says the
|
|
* change lands on the next setup run and prints the command that applies it.
|
|
*/
|
|
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
|
|
import * as fs from 'fs';
|
|
import * as os from 'os';
|
|
import * as path from 'path';
|
|
|
|
import { runBin } from './helpers/run-bin';
|
|
|
|
const ROOT = path.resolve(import.meta.dir, '..');
|
|
const BIN_CONFIG = path.join(ROOT, 'bin', 'gstack-config');
|
|
|
|
let tmpHome: string;
|
|
|
|
beforeEach(() => {
|
|
tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-plan-mode-cfg-'));
|
|
});
|
|
|
|
afterEach(() => {
|
|
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
});
|
|
|
|
function run(args: string[], env: Record<string, string | undefined> = { GSTACK_SETUP_RUNNING: undefined }) {
|
|
return runBin(BIN_CONFIG, args, { gstackHome: tmpHome, cwd: ROOT, trim: true, env });
|
|
}
|
|
|
|
describe('gstack-config plan_mode_preamble', () => {
|
|
test('defaults to on, in get, list and defaults', () => {
|
|
expect(run(['get', 'plan_mode_preamble']).stdout).toBe('on');
|
|
expect(run(['defaults']).stdout).toMatch(/plan_mode_preamble:\s+on$/m);
|
|
expect(run(['list']).stdout).toMatch(/plan_mode_preamble:\s+on \(default\)$/m);
|
|
});
|
|
|
|
test('accepts off and on', () => {
|
|
expect(run(['set', 'plan_mode_preamble', 'off']).status).toBe(0);
|
|
expect(run(['get', 'plan_mode_preamble']).stdout).toBe('off');
|
|
expect(run(['list']).stdout).toMatch(/plan_mode_preamble:\s+off \(set\)$/m);
|
|
expect(run(['set', 'plan_mode_preamble', 'on']).status).toBe(0);
|
|
expect(run(['get', 'plan_mode_preamble']).stdout).toBe('on');
|
|
});
|
|
|
|
test('an unknown value warns and stores on', () => {
|
|
run(['set', 'plan_mode_preamble', 'off']);
|
|
const junk = run(['set', 'plan_mode_preamble', 'false']);
|
|
expect(junk.status).toBe(0);
|
|
expect(junk.stderr).toContain("plan_mode_preamble 'false' not recognized. Valid values: on, off. Using on.");
|
|
expect(run(['get', 'plan_mode_preamble']).stdout).toBe('on');
|
|
});
|
|
|
|
test('outside setup, set says it applies on the next ./setup run and gives the command', () => {
|
|
const r = run(['set', 'plan_mode_preamble', 'off']);
|
|
expect(r.status).toBe(0);
|
|
expect(r.stdout).toBe(`gstack-config: plan_mode_preamble=off applies on the next ./setup run. Apply now: cd ${ROOT} && ./setup --refresh-registered`);
|
|
});
|
|
|
|
test('inside setup, set prints nothing', () => {
|
|
const r = run(['set', 'plan_mode_preamble', 'off'], { GSTACK_SETUP_RUNNING: '1' });
|
|
expect(r.status).toBe(0);
|
|
expect(r.stdout).toBe('');
|
|
});
|
|
|
|
test('the config header documents the key and its values', () => {
|
|
run(['set', 'plan_mode_preamble', 'on']);
|
|
const cfg = fs.readFileSync(path.join(tmpHome, 'config.yaml'), 'utf-8');
|
|
expect(cfg).toContain('# plan_mode_preamble: on');
|
|
expect(cfg).toContain('# off = rendered skills omit the "Plan Mode Safe');
|
|
});
|
|
});
|