v1.91.158.0 feat(config): plan_mode_preamble=off drops the plan-mode preamble from installed skills (port of #3213) (#3308)

* 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>
This commit is contained in:
authored and GitHub committed 2026-10-19 23:31:59 +00:00
1 parent 6237994016
commit cb9ccf2e80
17 files changed
+644 -44

No files matched your search

+10 -2
View File
@@ -52,6 +52,7 @@ export interface GenerationOptions {
explainLevel?: 'default' | 'terse';
respectDetection?: boolean;
log?: (message: string) => void;
planMode?: boolean;
}
interface RenderOptions {
@@ -63,6 +64,7 @@ interface RenderOptions {
catalogMode: 'trim' | 'full';
explainLevel: 'default' | 'terse';
gbrainDetected: boolean;
planMode: boolean;
}
export interface GeneratedArtifact {
@@ -130,6 +132,11 @@ function parseGenerationArgs(args: string[]): GenerationOptions {
if (explainLevel !== 'default' && explainLevel !== 'terse') {
throw new Error(`Unknown explain level: ${explainLevel}. Use 'default' or 'terse'.`);
}
const planModeValue = value('--plan-mode');
if (planModeValue !== undefined && planModeValue !== 'on' && planModeValue !== 'off') {
throw new Error(`Unknown plan mode: ${planModeValue}. Use 'on' or 'off'.`);
}
const planMode = planModeValue === 'off' ? false : true;
const outDir = value('--out-dir');
const linkRoot = value('--link-root');
const disabledValue = value('--disabled-skills');
@@ -143,7 +150,7 @@ function parseGenerationArgs(args: string[]): GenerationOptions {
// Swap-in callers use --link-root for the FINAL serving path (#2692).
// Direct --out-dir callers retain their existing links into the render.
return {
host, model, catalogMode, explainLevel,
host, model, catalogMode, explainLevel, planMode,
dryRun: args.includes('--dry-run'),
respectDetection: args.includes('--respect-detection'),
outputRoot: outDir === undefined ? ROOT : path.resolve(outDir),
@@ -687,7 +694,7 @@ function buildContext(
const interactive = interactiveMatch ? interactiveMatch[1] === 'true' : undefined;
return {
skillName, tmplPath, benefitsFrom, host, paths: HOST_PATHS[host],
preambleTier, model: options.model ?? getHostConfig(host).defaultModel, interactive, explainLevel: options.explainLevel, installRoot: options.installRoot,
preambleTier, model: options.model ?? getHostConfig(host).defaultModel, interactive, explainLevel: options.explainLevel, installRoot: options.installRoot, planMode: options.planMode,
};
}
@@ -920,6 +927,7 @@ export async function runGeneration(settings: GenerationOptions = {}): Promise<G
catalogMode: settings.catalogMode ?? 'trim',
explainLevel: settings.explainLevel ?? 'default',
gbrainDetected: loadGbrainOverride(settings.respectDetection ?? false),
planMode: settings.planMode ?? true,
};
const hosts = settings.host === 'all' ? ALL_HOST_NAMES as Host[] : [settings.host ?? 'claude'];
const log = settings.log ?? (() => {});
@@ -25,6 +25,7 @@ import { getHostConfig } from '../../../hosts/index';
*/
export function generatePlanModeInfo(ctx: TemplateContext): string {
if (!getHostConfig(ctx.host).capabilities.planMode) return '';
if (ctx.planMode === false) return '';
const entryOrder = ctx.skillName === 'plan-eng-review'
? 'starting from the Scope gate, then follow its Startup sequence'
: 'starting from the Scope gate (current target announcement or answered question), then preamble and Step 0';
+8
View File
@@ -117,6 +117,14 @@ export interface TemplateContext {
* null/undefined = the host's default root (committed render, unchanged bytes).
*/
installRoot?: string | null;
/**
* Plan-mode preamble block. Defaults to true (block ships).
* false (opt-in via `--plan-mode=off`): the "## Plan Mode Safe Operations" +
* "## Skill Invocation During Plan Mode" section is dropped from the
* preamble. "PLAN MODE EXCEPTION — ALWAYS RUN" markers inside skill bodies
* still apply.
*/
planMode?: boolean;
}
/** Resolver function signature. args is populated for parameterized placeholders like {{INVOKE_SKILL:name}}. */