Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

## [1.91.158.0] - 2026-10-19

**`gstack-config set plan_mode_preamble off` leaves the plan-mode preamble out of every installed skill, for people who never use plan mode.** The "Plan Mode Safe Operations" and "Skill Invocation During Plan Mode" sections add about 1.6 KB to each of 50 Claude skills. With the key off, the next `./setup` renders every host's skills with `gen-skill-docs --plan-mode=off`, and the default Claude install serves that render instead of the committed files. The default is on, and with it on every render and the committed SKILL.md files are byte-for-byte unchanged.

### Added
- **`gen-skill-docs --plan-mode=off`** drops the two sections at generation time. Commands marked "PLAN MODE EXCEPTION — ALWAYS RUN" keep their marker. Hosts whose capabilities declare no plan mode already render the sections empty, so in practice the flag matters for Claude, Copilot and OpenCode. Fixes #3168. From #3213 by @xingzihai, finished with the config wiring.
- **The `plan_mode_preamble` config key** (`on` | `off`, default `on`). It is a closed domain like `explain_level`: an unknown value warns and stores `on`. Setting it outside `./setup` says it applies on the next setup run and prints the `./setup --refresh-registered` command that applies it now.
- **Every installed render carries the choice:** all 16 `gen:skill-docs` call sites in `setup`, the Claude render helper, `gstack-config gbrain-refresh`, and the router that `gstack-relink` re-renders in place (as it already does for `disabled_skills`). When setup has to build, `scripts/build.sh` renders Factory, OpenCode, Cursor, Copilot and Antigravity without the flag, so with the key off setup renders those hosts again afterwards.
- **The default Claude install gets a render when the key is off.** Without a working gbrain (absent, or the detector failed), `gstack_claude_render_action` returns `plain` instead of serving the committed files; with gbrain the brain-aware render already runs and now carries the flag too. The activation record (`~/.gstack/render/claude.overlay`) gains `plan_mode=off`. Turning the key back on removes that render on the next setup when gbrain is absent or its detector fails (a failed detector otherwise keeps a render), and re-renders the brain-aware render with the sections when gbrain is present. A failed render with the key off keeps the previous render. Records written with the key on are unchanged.

### Fixed
- **`gstack-relink` refreshes the `_gstack-command` router alias after it re-renders the router.** It used to copy the router before the re-render, so the alias kept the previous router (with `disabled_skills`, the full skill list) until the next relink.

### Changed
- **Gate integrity:** new tests `test/plan-mode-off-build.test.ts` (from #3213, unchanged), `test/plan-mode-off-generation.test.ts`, `test/plan-mode-preamble-config.test.ts`, `test/plan-mode-preamble-render.test.ts` and `test/plan-mode-preamble-setup.test.ts`. No existing test was edited, and no threshold, trial count, case kind or judge changed.

## [1.91.157.0] - 2026-10-19

**Claude SKILL.md files no longer carry a "When to invoke" body section, which saves about 20 KB across 56 skills.** Claude Code reads a skill's body only after the skill is invoked, so routing prose there never routed anything. Routing still comes from the catalog description and the CLAUDE.md routing block, and voice triggers move into the root router's routing block. From #3129 by @thanhnguyentung95.
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,8 @@ types into that element; bare `browse type <text>` types into whatever has focus

**Don't use some skills?** `gstack-config set disabled_skills make-pdf,pair-agent` stops registering them on every host, so their descriptions stop loading into each session. Claude Code updates right away; other hosts on their next `./setup --host <name>` or `/gstack-upgrade`. Their files stay installed, so a skill that calls a disabled one still works (you get a warning). Typos are rejected with the closest skill name, `gstack-upgrade` can't be disabled, and `gstack-config set disabled_skills ""` re-enables everything.

**Never use plan mode?** `gstack-config set plan_mode_preamble off` leaves the "Plan Mode Safe Operations" and "Skill Invocation During Plan Mode" sections (about 1.6 KB per skill) out of every installed skill on the next `./setup` run; the command prints the exact `./setup --refresh-registered` line that applies it. Claude Code then serves a render under `~/.gstack/render/claude` instead of the committed files. Commands marked "PLAN MODE EXCEPTION — ALWAYS RUN" keep their marker. `gstack-config set plan_mode_preamble on` (the default) and another `./setup` bring the sections back. It matters for Claude Code, Copilot and OpenCode: hosts without plan mode (Codex and others) never carry these sections.

**Want namespaced commands?** `cd ~/.claude/skills/gstack && ./setup --prefix` — switches from `/qa` to `/gstack-qa`. Useful if you run other skill packs alongside gstack.

**Codex says "Skipped loading skill(s) due to invalid SKILL.md"?** Your Codex skill descriptions are stale. `${CODEX_HOME:-~/.codex}/skills/gstack` is a runtime directory, not the checkout: `./setup --status` shows the Codex row's source checkout. Fix: `cd <that source> && git pull && ./setup --host codex` — for a repo-local install, run it from inside the project.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.91.157.0
1.91.158.0
2 changes: 1 addition & 1 deletion agents-digest/gstack-AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# gstack digest v1.91.157.0 — regenerate/re-copy after upgrading gstack
# gstack digest v1.91.158.0 — regenerate/re-copy after upgrading gstack

Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
for agent hosts without a full skill install. The full skills add workflows,
Expand Down
27 changes: 23 additions & 4 deletions bin/gstack-config
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ CONFIG_HEADER='# gstack configuration — edit freely, changes take effect on ne
# # (for power users who know the terms)
# # Unknown values default to "default" with a warning.
# # See docs/designs/PLAN_TUNING_V1.md for rationale.
# plan_mode_preamble: on # on = every skill carries the plan-mode preamble
# # off = rendered skills omit the "Plan Mode Safe
# # Operations" and "Skill Invocation During
# # Plan Mode" sections (~1.8 KB each), for
# # users who never enter plan mode
# # Applies on the next ./setup run. Unknown values
# # default to "on" with a warning.
#
# ─── Artifacts sync (renamed from gbrain_sync_mode in v1.27.0.0) ─────
# artifacts_sync_mode: off # off | artifacts-only | full
Expand Down Expand Up @@ -279,6 +286,7 @@ lookup_default() {
update_check) echo "true" ;;
skill_prefix) echo "false" ;;
explain_level) echo "default" ;;
plan_mode_preamble) echo "on" ;; # on | off — off drops the plan-mode preamble on the next ./setup (#3168)
codex_reviews) echo "enabled" ;;
design_detector) echo "auto" ;; # auto | off — impeccable engine pre-pass in the design skills
design_detector_install_prompted) echo "false" ;; # true once the user answered the one-time engine install offer with "never ask again"
Expand Down Expand Up @@ -734,6 +742,10 @@ $_gstack_legacy_root"
echo "Warning: explain_level '$VALUE' not recognized. Valid values: default, terse. Using default." >&2
VALUE="default"
fi
if [ "$KEY" = "plan_mode_preamble" ] && [ "$VALUE" != "on" ] && [ "$VALUE" != "off" ]; then
echo "Warning: plan_mode_preamble '$VALUE' not recognized. Valid values: on, off. Using on." >&2
VALUE="on"
fi
if [ "$KEY" = "artifacts_sync_mode" ] && [ "$VALUE" != "off" ] && [ "$VALUE" != "artifacts-only" ] && [ "$VALUE" != "full" ]; then
echo "Warning: artifacts_sync_mode '$VALUE' not recognized. Valid values: off, artifacts-only, full. Using off." >&2
VALUE="off"
Expand Down Expand Up @@ -985,6 +997,11 @@ $_gstack_legacy_root"
gstack_install_registry_rows | awk -F '\t' '$1 != "claude" { print " apply to " $1 " (" $4 "): cd " $6 " && ./setup --host " $1 }' )
fi
fi
# Installed skills are rendered by ./setup, so the preamble choice lands
# on its next run (every registered host).
if [ "$KEY" = "plan_mode_preamble" ] && [ -z "${GSTACK_SETUP_RUNNING:-}" ]; then
printf 'gstack-config: plan_mode_preamble=%s applies on the next ./setup run. Apply now: cd %q && ./setup --refresh-registered\n' "$VALUE" "$(cd "$(dirname "$0")/.." && pwd)"
fi
;;
unset)
KEY="${2:?Usage: gstack-config unset <key>}"
Expand All @@ -1009,7 +1026,7 @@ $_gstack_legacy_root"
echo "# ─── Active values (including defaults for unset keys) ───"
report_root_disagreement
for KEY in proactive routing_declined telemetry auto_upgrade update_check \
skill_prefix explain_level \
skill_prefix explain_level plan_mode_preamble \
codex_reviews gstack_contributor skip_eng_review workspace_root \
artifacts_sync_mode artifacts_sync_mode_prompted artifacts_sync_removals plan_tune_hooks \
timeline_stop_hook design_detector design_detector_install_prompted memorable_recall \
Expand All @@ -1034,7 +1051,7 @@ $_gstack_legacy_root"
defaults)
echo "# gstack-config defaults"
for KEY in proactive routing_declined telemetry auto_upgrade update_check \
skill_prefix explain_level \
skill_prefix explain_level plan_mode_preamble \
codex_reviews gstack_contributor skip_eng_review workspace_root \
artifacts_sync_mode artifacts_sync_mode_prompted artifacts_sync_removals plan_tune_hooks \
timeline_stop_hook design_detector design_detector_install_prompted memorable_recall \
Expand Down Expand Up @@ -1065,6 +1082,8 @@ $_gstack_legacy_root"
# generated SKILL.md files match your installation state.
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
. "$SCRIPT_DIR/gstack-render-claude.sh" 2>/dev/null || { echo "$0: $SCRIPT_DIR/gstack-render-claude.sh is missing. fix: reinstall with ./setup or /gstack-upgrade" >&2; exit 1; }
_PLAN_MODE_ARG=""
[ "$("$0" get plan_mode_preamble 2>/dev/null || true)" != off ] || _PLAN_MODE_ARG="--plan-mode=off"
DETECT_BIN="$SCRIPT_DIR/gstack-gbrain-detect"
DETECTION_FILE="$STATE_DIR/gbrain-detection.json"
mkdir -p "$STATE_DIR"
Expand Down Expand Up @@ -1133,7 +1152,7 @@ $_gstack_legacy_root"
gstack_claude_overlay "$INSTALL_DIR" "$0"
RENDER_TMP="$RENDER_DIR.tmp.$$"
rm -rf "$RENDER_TMP"
if ( cd "$INSTALL_DIR" && bun run gen:skill-docs:user --host claude --out-dir "$RENDER_TMP" --link-root "$RENDER_DIR" --model "$_GSTACK_OVERLAY" >/dev/null 2>&1 ); then
if ( cd "$INSTALL_DIR" && bun run gen:skill-docs:user --host claude --out-dir "$RENDER_TMP" --link-root "$RENDER_DIR" --model "$_GSTACK_OVERLAY" ${_PLAN_MODE_ARG:+"$_PLAN_MODE_ARG"} >/dev/null 2>&1 ); then
gstack_render_lock "$RENDER_DIR"
_swap_in_render "$RENDER_DIR" "$RENDER_TMP"
gstack_render_unlock "$RENDER_DIR"
Expand All @@ -1159,7 +1178,7 @@ $_gstack_legacy_root"
# brain blocks, so removing gbrain keeps the chosen overlay.
INSTALL_DIR="$HOME/.claude/skills/gstack"
RENDER_DIR="${GSTACK_USER_RENDER_DIR:-$STATE_DIR/render/claude}"
if { [ -d "$RENDER_DIR" ] || "$0" has claude_overlay_model; } \
if { [ -d "$RENDER_DIR" ] || "$0" has claude_overlay_model || [ -n "$_PLAN_MODE_ARG" ]; } \
&& [ -d "$INSTALL_DIR" ] && [ ! -L "$INSTALL_DIR" ] && [ -f "$INSTALL_DIR/VERSION" ] && [ -f "$INSTALL_DIR/package.json" ]; then
if ! command -v bun >/dev/null 2>&1; then
echo "Skip: bun not on PATH — the Claude render at $RENDER_DIR was not refreshed. Install bun, then re-run 'gstack-config gbrain-refresh'."
Expand Down
13 changes: 10 additions & 3 deletions bin/gstack-relink
Original file line number Diff line number Diff line change
Expand Up @@ -461,19 +461,26 @@ done
# (in place, like gstack-patch-names). Clearing the list restores the committed
# bytes. Only a generated router is rewritten, and only from a checkout that
# carries the generator; a failed render keeps the current router.
# plan_mode_preamble=off (#3168) renders it without the plan-mode preamble the
# same way, and turning it back on restores the committed bytes.
_ROUTER="$INSTALL_DIR/SKILL.md"
_GEN_SRC="$(cd "$SCRIPT_DIR/.." && pwd)"
if { [ "$DISABLED" != " " ] || grep -q '^Disabled on this install' "$_ROUTER" 2>/dev/null; } \
_PLAN_MODE_ARG=""
[ "$("$GSTACK_CONFIG" get plan_mode_preamble 2>/dev/null || true)" != off ] || _PLAN_MODE_ARG="--plan-mode=off"
if { [ "$DISABLED" != " " ] || grep -q '^Disabled on this install' "$_ROUTER" 2>/dev/null \
|| [ -n "$_PLAN_MODE_ARG" ] \
|| { grep -q 'PLAN MODE EXCEPTION' "$_ROUTER" 2>/dev/null && ! grep -q '^## Plan Mode Safe Operations' "$_ROUTER" 2>/dev/null; }; } \
&& grep -q '<!-- AUTO-GENERATED from' "$_ROUTER" 2>/dev/null && [ -f "$_GEN_SRC/scripts/gen-skill-docs.ts" ]; then
_ROUTER_TMP="$(mktemp -d "${TMPDIR:-/tmp}/gstack-router.XXXXXX")" || _ROUTER_TMP=""
_DISABLED_CSV="$(printf '%s' "$DISABLED" | xargs | tr ' ' ',')"
if [ -n "$_ROUTER_TMP" ] && command -v bun >/dev/null 2>&1 \
&& (cd "$_GEN_SRC" && bun run scripts/gen-skill-docs.ts --host claude --out-dir "$_ROUTER_TMP" ${_DISABLED_CSV:+"--disabled-skills=$_DISABLED_CSV"} >/dev/null 2>&1) \
&& (cd "$_GEN_SRC" && bun run scripts/gen-skill-docs.ts --host claude --out-dir "$_ROUTER_TMP" ${_DISABLED_CSV:+"--disabled-skills=$_DISABLED_CSV"} ${_PLAN_MODE_ARG:+"$_PLAN_MODE_ARG"} >/dev/null 2>&1) \
&& [ -s "$_ROUTER_TMP/SKILL.md" ] && cp "$_ROUTER_TMP/SKILL.md" "$_ROUTER.tmp.$$" && mv "$_ROUTER.tmp.$$" "$_ROUTER"; then
_link_root_skill_alias
[ -n "$_DISABLED_CSV" ] && echo "Router: disabled skills left out of $_ROUTER"
else
rm -f "$_ROUTER.tmp.$$"
echo "gstack-relink: could not re-render the router for disabled_skills (bun missing or generation failed); $_ROUTER still lists every skill. Fix: install Bun, then run $0" >&2
echo "gstack-relink: could not re-render the router for disabled_skills or plan_mode_preamble (bun missing or generation failed); $_ROUTER is unchanged. Fix: install Bun, then run $0" >&2
fi
[ -z "$_ROUTER_TMP" ] || rm -rf "$_ROUTER_TMP"
fi
Expand Down
30 changes: 20 additions & 10 deletions bin/gstack-render-claude.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@
# upgrade falls back to `claude` with the reason printed.
#
# Each render keeps a plain-text activation record beside it
# (<render dir>.overlay: overlay, source, model, version, gbrain, render), so
# (<render dir>.overlay: overlay, source, model, version, gbrain, render, and
# plan_mode=off for a render without the plan-mode preamble), so
# `./setup --status` and the change-only banner never need bun. No record
# means the default `claude` overlay on the committed render.
#
# Callers set _PLAN_MODE_ARG to --plan-mode=off when gstack-config
# plan_mode_preamble is off (#3168); empty or unset means on. The committed
# files carry the preamble, so with it off the default install is rendered.
#
# Callers define `_swap_in_render` (setup and bin/gstack-config each keep
# their tested copy) and may define `log` and `bun_cmd`; plain echo and bun
# are used otherwise.
Expand Down Expand Up @@ -81,15 +86,18 @@ gstack_render_unlock() {

# gstack_claude_render_action RENDER_DIR GBRAIN — what the default install's
# render dir needs: `user` (brain-aware :user render), `plain` (pinned
# overlay, no brain blocks), `remove` (serve the committed files) or `keep`.
# overlay or plan-mode preamble off, no brain blocks), `remove` (serve the
# committed files) or `keep`.
# GBRAIN is ok, absent or unknown (detector missing or failed: no brain
# blocks, and a render is left alone unless an overlay change needs it).
# blocks, and a render is left alone unless an overlay or plan-mode change
# needs it).
gstack_claude_render_action() {
local render_dir="$1" gbrain="$2" previous
if [ "$gbrain" = ok ]; then _GSTACK_RENDER_ACTION=user; return 0; fi
if [ "$_GSTACK_OVERLAY" != claude ]; then _GSTACK_RENDER_ACTION=plain; return 0; fi
if [ "$_GSTACK_OVERLAY" != claude ] || [ -n "${_PLAN_MODE_ARG:-}" ]; then _GSTACK_RENDER_ACTION=plain; return 0; fi
previous="$(gstack_claude_overlay_field "$render_dir.overlay" overlay)"
if [ "$gbrain" = unknown ] && [ "${previous:-claude}" = claude ]; then
if [ "$gbrain" = unknown ] && [ "${previous:-claude}" = claude ] \
&& [ -z "$(gstack_claude_overlay_field "$render_dir.overlay" plan_mode)" ]; then
_GSTACK_RENDER_ACTION=keep
else
_GSTACK_RENDER_ACTION=remove
Expand All @@ -102,8 +110,8 @@ gstack_claude_render_plain() {
local src="$1" RENDER_DIR="$2" RENDER_TMP out rc=0
RENDER_TMP="$RENDER_DIR.tmp.$$"
rm -rf "$RENDER_TMP"
_gstack_render_say "Rendering Claude skills with the $_GSTACK_OVERLAY overlay into $RENDER_DIR (source checkout stays clean)..."
out="$(cd "$src" && _gstack_render_bun run gen:skill-docs --host claude --out-dir "$RENDER_TMP" --link-root "$RENDER_DIR" --model "$_GSTACK_OVERLAY" ${_DISABLED_CSV:+"--disabled-skills=$_DISABLED_CSV"} 2>&1)" || rc=$?
_gstack_render_say "Rendering Claude skills with the $_GSTACK_OVERLAY overlay${_PLAN_MODE_ARG:+ and no plan-mode preamble} into $RENDER_DIR (source checkout stays clean)..."
out="$(cd "$src" && _gstack_render_bun run gen:skill-docs --host claude --out-dir "$RENDER_TMP" --link-root "$RENDER_DIR" --model "$_GSTACK_OVERLAY" ${_DISABLED_CSV:+"--disabled-skills=$_DISABLED_CSV"} ${_PLAN_MODE_ARG:+"$_PLAN_MODE_ARG"} 2>&1)" || rc=$?
if [ "$rc" -ne 0 ]; then
rm -rf "$RENDER_TMP"
printf '%s\n' "$out" | tail -3
Expand Down Expand Up @@ -150,9 +158,11 @@ gstack_claude_render_settle() {
if [ "$render_kind" = committed ] && [ "$_GSTACK_OVERLAY_SOURCE" = default ]; then
rm -f "$record"
else
printf 'overlay=%s\nsource=%s\nmodel=%s\nversion=%s\ngbrain=%s\nrender=%s\n' \
"$served" "$_GSTACK_OVERLAY_SOURCE" "$_GSTACK_OVERLAY_ID" "${version:-unknown}" "$gbrain" "$render_kind" \
> "$record.tmp.$$" && mv "$record.tmp.$$" "$record"
{
printf 'overlay=%s\nsource=%s\nmodel=%s\nversion=%s\ngbrain=%s\nrender=%s\n' \
"$served" "$_GSTACK_OVERLAY_SOURCE" "$_GSTACK_OVERLAY_ID" "${version:-unknown}" "$gbrain" "$render_kind"
[ "$render_kind" != user ] || [ -z "${_PLAN_MODE_ARG:-}" ] || printf 'plan_mode=off\n'
} > "$record.tmp.$$" && mv "$record.tmp.$$" "$record"
fi
gstack_render_unlock "$render_dir"
if [ "$served" != "${previous:-claude}" ]; then
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gstack",
"version": "1.91.157",
"version": "1.91.158",
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
"license": "MIT",
"type": "module",
Expand Down
Loading
Loading