Without shell:true, execFileSync('npm', ...) on Windows fails with
ENOENT because npm is distributed as npm.cmd, not as a literal 'npm'
binary. The silent try/catch swallows the error, latest stays null,
update_available becomes null, and the statusline never shows
"⬆ /gsd-update" — Windows users miss every release.
Adding shell:true makes execFileSync route through cmd.exe which
resolves npm.cmd via PATHEXT, identical behavior on POSIX.
Repro on Windows:
$env:GSD_CACHE_FILE = "$env:USERPROFILE\.cache\gsd\gsd-update-check.json"
node ~\.claude\hooks\gsd-check-update-worker.js
Get-Content "$env:USERPROFILE\.cache\gsd\gsd-update-check.json"
Before: {"update_available":null,"installed":"1.40.0","latest":"unknown",...}
After: {"update_available":false,"installed":"1.40.0","latest":"1.40.0",...}
* refactor(sdk): extract gsdtools transport seam with per-command policy
* fix(sdk): address CodeRabbit transport policy and timeout findings
* fix(sdk): harden raw transport formatting and raw-path coverage
* docs(en): update FEATURES/USER-GUIDE/COMMANDS for v1.40.0 surface
- FEATURES.md: append v1.40.0 section (#122 skill consolidation, #123
namespace meta-skills, #124 context-window guard, #125 phase-lifecycle
status-line read-side); add to TOC.
- USER-GUIDE.md: add slash-command form (hyphen vs colon) primer and
namespace routing primer; replace deleted slash forms in walkthroughs
(`/gsd-add-backlog`, `/gsd-plant-seed`, `/gsd-add-phase`,
`/gsd-set-profile`, `/gsd-list-workspaces`, etc.) with consolidated
forms (`/gsd-capture --backlog`, `/gsd-phase --insert`,
`/gsd-config --profile`, `/gsd-workspace --list`, etc.); fix
`/gsd-spike-wrap-up` and `/gsd-sketch-wrap-up` to flag form.
- COMMANDS.md: clarify Command Syntax (Gemini = colon form, others =
hyphen form); add Namespace Meta-Skills section with all six routers;
add `--context` to /gsd-health flag table.
Refs #3047
* docs(en): refresh INVENTORY/CLI-TOOLS/STATE-MD-LIFECYCLE for v1.40.0
- INVENTORY.md: workflow-row "Invoked by" column updated to point at
consolidated commands (`/gsd-phase` family, `/gsd-workspace --list`,
`/gsd-config --advanced/--integrations/--profile`,
`/gsd-sketch --wrap-up`, `/gsd-spike --wrap-up`); CLI-modules row for
`secrets.cjs` updated to `/gsd-config --integrations`. Command count
and namespace meta-skills section already reflect 65 shipped (= 59
consolidated sub-skills + 6 ns-* routers).
- CLI-TOOLS.md: add `validate context` row under Validation Commands
with the 60 %/70 % threshold envelope used by `/gsd-health --context`.
- STATE-MD-LIFECYCLE.md: flip status header from "proposed" to
"shipped in v1.40.0" since `parseStateMd()` and `formatGsdState()`
now read and render `active_phase`, `next_action`, `next_phases`,
and `progress`.
`docs/AGENTS.md` audited and verified clean — `gsd-code-fixer` row
already lists the correct `/gsd-code-review --fix` spawner; no
deleted-skill references found. `docs/INVENTORY-MANIFEST.json`
audited and verified clean — already enumerates the 65 commands
(including six ns-* routers) and contains no deleted slash forms.
Refs #3047
* docs(en): cleanup ARCHITECTURE/CONFIGURATION for v1.40.0
- ARCHITECTURE.md: split Commands install-target list to call out the
Gemini colon form (`/gsd:command-name`) vs hyphen form for every
other runtime. Add a new subsection covering two-stage hierarchical
routing via the six namespace meta-skills (#2792) and a paired note
on the MCP token-budget interaction so readers see the two big
per-turn cost levers in one place.
- CONFIGURATION.md: rewrite three references to the deleted
`/gsd-settings-advanced` and `/gsd-settings-integrations` slash
forms to use the consolidated `/gsd-config --advanced` /
`/gsd-config --integrations` invocations. Add a new "STATE.md
Frontmatter (Phase Lifecycle)" section documenting the four
optional fields (`active_phase`, `next_action`, `next_phases`,
`progress`) read by the v1.40 status-line, with a pointer to
STATE-MD-LIFECYCLE.md for the full reference.
`docs/manual-update.md` audited and verified clean — already documents
`/gsd-update --reapply` (the consolidated form), no reference to the
deleted `/gsd-reapply-patches`.
Refs #3047
* docs(i18n): mirror v1.40.0 slash-command rename into ja-JP/ko-KR/zh-CN/pt-BR
Mechanical token-level renames only — every reference to a deleted
micro-skill slash form is rewritten to the consolidated form on the
matching parent skill. No prose was machine-translated; new prose
sections (slash-form primer, namespace routing primer, v1.40 feature
entries, STATE.md frontmatter) were left for human translator
follow-up.
Renames applied uniformly across all four trees:
/gsd-add-todo, /gsd-add-note, /gsd-add-backlog,
/gsd-plant-seed, /gsd-check-todos → /gsd-capture[ --note|
--backlog|--seed|--list]
/gsd-add-phase, /gsd-insert-phase,
/gsd-remove-phase, /gsd-edit-phase → /gsd-phase[ --insert|
--remove|--edit]
/gsd-new-workspace, /gsd-list-workspaces,
/gsd-remove-workspace → /gsd-workspace[ --new|
--list|--remove]
/gsd-settings-advanced,
/gsd-settings-integrations,
/gsd-set-profile → /gsd-config[ --advanced|
--integrations|--profile]
/gsd-sketch-wrap-up → /gsd-sketch --wrap-up
/gsd-spike-wrap-up → /gsd-spike --wrap-up
/gsd-reapply-patches → /gsd-update --reapply
/gsd-code-review-fix → /gsd-code-review --fix
/gsd-plan-milestone-gaps → /gsd-audit-milestone
Refs #3047
* docs(changelog): regroup [Unreleased] under Feature/Enhancement/Fix
Replace the existing Keep-a-Changelog \`Added\` / \`Changed\` /
\`Performance\` / \`Removed\` / \`Fixed\` sub-headers in the [Unreleased]
block with the issue/PR template taxonomy:
Added → Feature
Changed / Performance → Enhancement
Removed → Enhancement
Fixed → Fix
Order within the release: Feature → Enhancement → Fix. Every bullet
preserved verbatim — only headers and grouping changed; the awkward
inline-versioned headers (\`### Added — 1.40.0-rc.1\`,
\`### Changed — 1.40.0-rc.1\`, \`### Fixed — 1.40.0-rc.1\`) folded into
the same buckets with the \`— 1.40.0-rc.1\` suffix dropped, since the
[Unreleased] block IS 1.40.0-rc.1.
The [1.39.2] hotfix block called out in #3047's spec does not yet
exist in CHANGELOG.md (the previously released hotfix is [1.39.1]),
so this commit only regroups [Unreleased]. Older release blocks
([1.39.1] and earlier) are frozen and untouched.
Refs #3047
* docs(changeset): add fragment for v1.40.0 doc audit
Refs #3047
* docs(en): strip leading / from deleted slash-command tokens in FEATURES
REQ-CONSOLIDATE-03 and REQ-CONSOLIDATE-04 listed deleted commands by
their `/gsd-foo` form for the historical record. The docs-parity tests
in bug-3010, bug-3029-3034, and bug-3042-3044 use the regex
`/\/gsd-[a-z0-9][a-z0-9-]*/g` to scan user-facing surfaces for any
remaining mention of removed slash forms — they cannot tell prose
about a deleted command from a live recommendation.
Strip the leading slash from the bare-name references (preserve the
historical text otherwise). Tests now require a `/` prefix to match,
so `gsd-add-todo` reads identically to a human but no longer trips
the parser.
Verified locally: 65/65 tests pass across the three docs-parity
suites that were red on CI run 25270072600.
Refs #3047
* docs(en): fix CR feedback + drop literal /gsd:plan-phase from USER-GUIDE
CI: tests/bug-2543-gsd-slash-namespace.test.cjs flagged
docs/USER-GUIDE.md:35 for embedding the literal `/gsd:plan-phase`
token in the parenthetical Gemini-form example. The test scans every
.md under docs/ for `/gsd:<live-cmd>` because non-Gemini surfaces must
not advertise the colon form. Replaced the literal example with a
prose substitution rule.
CR: docs/ARCHITECTURE.md:125 — the namespace meta-skills were listed
by file-prefix (`gsd-ns-workflow`) but the invocable frontmatter `name:`
is the bare form (`gsd-workflow`). Verified against the six
`commands/gsd/ns-*.md` files. Replaced with the canonical names and
noted the file/name disagreement in-line.
CR: docs/COMMANDS.md:723 — `v1.40` aligned to canonical `v1.40.0`.
CR: docs/FEATURES.md:2679 — REQ-CTX-GUARD-02 advertised the wrong
invocation (`gsd-tools validate context`). The shipped handler is
exposed via `gsd-sdk query validate.context` and requires explicit
`--tokens-used <int>` + `--context-window <int>` flags (verified
against sdk/src/query/validate.ts:849-882 and
get-shit-done/bin/lib/validate-command-router.cjs:19-36).
CR: docs/zh-CN/README.md:533 — added `inherit` to the profile-options
parenthetical to match the canonical set (verified against
model-profiles.cjs:29 `VALID_PROFILES = […MODEL_PROFILES['gsd-planner'], 'inherit']`).
Verified locally: 74/74 tests pass across the four docs-parity suites
that were red on CI runs 25270072600 and 25270182903.
Refs #3047
* feat(plan-phase): --research-phase flag absorbs deleted /gsd-research-phase + scrub stale refs (#3042, #3044)
#3042 (orphaned research-phase): /gsd-research-phase had a workflow file
but no slash-command stub. Rather than restore the orphan, the research-
only capability is now a flag on /gsd-plan-phase:
/gsd-plan-phase --research-phase <N>
When set, the workflow scopes to phase N, runs the research step (Section
5 of the existing plan-phase workflow), then early-exits before the
planner/plan-checker/verifier chain.
Per RCA against the deleted standalone, the flag adds two modifiers to
fully cover the original surface (Option B from the RCA discussion):
- --view : print existing RESEARCH.md to stdout, no spawn. Cheapest mode
for the correction-without-replanning loop the issue reporter
explicitly called out. Errors with a clear hint if RESEARCH.md is
missing.
- --research : reuse the existing "force re-research" semantics. In
research-only mode this skips the existing-RESEARCH.md prompt and
re-spawns unconditionally.
- Neither flag, RESEARCH.md exists : prompt update/view/skip. Mirrors
the deleted standalone's existing-artifact menu (#3042 RCA).
#3044 (stale slash-command refs): scrubbed five deleted commands from
all user-facing surfaces, including English docs, 4 localized doc sets
(ja-JP, ko-KR, zh-CN, pt-BR), workflows, templates, and references.
/gsd-check-todos → /gsd-capture --list
/gsd-new-workspace → /gsd-workspace --new
/gsd-status → /gsd-progress
/gsd-plan-milestone-gaps → table rows / orphan sections removed
(PR #3038 only scrubbed workflows/agent;
missed the docs surfaces this PR covers)
/gsd-research-phase → /gsd-plan-phase --research-phase
Includes a fix to docs/issue-driven-orchestration.md (PR #3036)
which itself referenced /gsd-new-workspace 4 times — self-correction.
Removed:
- get-shit-done/workflows/research-phase.md (orphan, capability
absorbed into --research-phase flag)
Tests:
- tests/bug-3042-3044-research-flag-and-stale-refs.test.cjs — 46
structural-IR tests across both bugs:
- argument-hint advertises --research-phase + --view
- workflow parses --research-phase, sets RESEARCH_ONLY,
early-exits before planner
- --view prints RESEARCH.md without spawning
- --research forces refresh in research-only mode
- existing-RESEARCH.md prompt path with update/view/skip
- workflows/research-phase.md is removed
- 5 deleted slash-commands absent from 17 English user-facing
surfaces + 16 localized doc surfaces (4 locales × 4 docs each)
- replacement command tokens present where deleted ones lived
6950/6950 full suite pass. Lints clean.
Closes#3042Closes#3044
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: address all 8 CR findings on PR #3045
Major (3):
- get-shit-done/workflows/plan-phase.md:344 — added explicit early-exit
guard at Section 5.1: "Skip if RESEARCH_ONLY=true". Without it, an LLM
could fall through "use existing, skip to step 6" → planner spawn,
violating the research-only contract. The guard makes the early-exit
unreachable from any non-research-only branch.
- get-shit-done/references/continuation-format.md (3 examples) +
zh-CN/.../continuation-format.md (3 examples) — pointed to
`/gsd-plan-phase --research-phase` but docs/COMMANDS.md didn't
document the flag. Added a full --research-phase + --view + --research
modifier section to the /gsd-plan-phase flag table in COMMANDS.md so
the canonical reference matches the continuation examples.
Minor (5):
- docs/FEATURES.md:1632 — `/gsd-plan-phase --research-phase` →
`/gsd-plan-phase --research-phase <N>` (include required arg).
- get-shit-done/templates/README.md:46 — NN-VALIDATION.md producer
reverted from `/gsd-plan-phase --research-phase` (Nyquist) to plain
`/gsd-plan-phase` (Nyquist). VALIDATION.md is created during normal
Nyquist flow, not research-only mode — the bulk replacement was
wrong for that line.
- get-shit-done/workflows/help.md:89 — signature line was missing
`--research`; added it alongside `--research-phase` and `--view`.
- tests/bug-3042-3044-...:197 — promptHasView/promptHasSkip were
tautological (matched anywhere in 1700-line workflow). Tightened
to a proximity check anchored on "RESEARCH.md already exists" prompt
header within a 600-char window. Updated workflow to emit that
literal phrase.
- tests/feat-2840-...:95 — workspace assertion used `/gsd-workspace`
but the documented replacement is `/gsd-workspace --new`. Tightened
to require both tokens (in 3 places: requiredCommands list, regex
in conceptPairs, error message).
6950/6950 full suite pass. Lint clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(install): skip Gemini local commands/gsd when global GSD present (#3037)
Reporter showed that running `npx get-shit-done-cc --gemini --global`
followed by `--gemini --local` in a project creates the same 65 GSD
command files in both Gemini scopes:
- ~/.gemini/commands/gsd/ (user scope)
- <project>/.gemini/commands/gsd/ (workspace scope)
Gemini conflict-detects by command name across scopes and renames every
overlapping /gsd:* command to /workspace.gsd:* and /user.gsd:*, breaking
the documented /gsd:* namespace.
Fix: in bin/install.js, when handling --gemini --local, detect whether
~/.gemini/commands/gsd/ already exists with managed-shape content. If
so, skip the local copy and print a clear three-line warning explaining
the conflict avoidance. The user-scope install already provides the
same /gsd:* commands in this project; the local copy adds zero value.
Sibling fixes (test isolation):
- tests/install-minimal-all-runtimes.test.cjs: pass HOME/USERPROFILE
through the spawned installer's env so the developer's real
~/.gemini/commands/gsd/ doesn't trigger the new skip path during
test runs that want to assert the local-install populates
commands/gsd/.
- tests/gemini-namespacing.test.cjs: the "Gemini Install (Behavioral)"
describe block now creates an isolated tmpHome and points
process.env.HOME at it before calling install(false, 'gemini'),
with proper restore in afterEach.
Test:
- tests/bug-3037-gemini-duplicate-commands.test.cjs — 4 structural
tests:
1. global install populates HOME/.gemini/commands/gsd
2. local install AFTER global skips the local copy
3. local install with NO existing global still populates locally
(no-regression)
4. local install when HOME has .gemini/ but no GSD-managed
commands/gsd/ still populates locally (non-GSD-Gemini-user
no-regression)
6909/6909 full suite pass. Lints clean.
Closes#3037
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: address CR feedback on PR #3041 — narrower detection + USERPROFILE restore
CR findings:
1. **bin/install.js (Major)** — userScopeHasGsd used
`fs.readdirSync(homeGeminiGsd).length > 0` which would skip the
local install for any non-empty directory, including a user who
hand-dropped a single override at ~/.gemini/commands/gsd/<thing>
.toml without ever running --gemini --global. Narrowed the
detection to require at least 3 canonical GSD command files
(help.toml, progress.toml, new-project.toml) — a marker that
ships in every GSD Gemini install (minimal mode included) and is
structurally impossible to produce by accident.
2. **tests/bug-3037-...:59 (Minor)** — beforeEach overwrites
process.env.USERPROFILE but afterEach only restores HOME, leaking
the temp home into later tests on Windows or any code path that
reads USERPROFILE. Added save/restore symmetric with HOME.
Plus added a 5th regression test covering the narrowed detection:
"local install when HOME has hand-dropped overrides UNDER commands/gsd/
(but no full GSD) still populates locally" — directly exercises the
edge case CR identified.
5/5 targeted tests pass. 6910/6910 full suite pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(workflows,docs): scrub stale /gsd-code-review-fix and /gsd-plan-milestone-gaps refs (#3029, #3034)
#2790 consolidated /gsd-code-review-fix into /gsd-code-review --fix and
deleted /gsd-plan-milestone-gaps in favor of inline gap planning as part
of /gsd-audit-milestone's output. The deletion was propagated through
some surfaces (#2950 covered help/do/settings/discuss-phase/etc.) but
several user-facing surfaces still emitted the old forms:
#3029 — /gsd-code-review-fix references in:
- agents/gsd-code-fixer.md (description, "Spawned by", recovery prose)
- get-shit-done/workflows/code-review.md (offer text)
- get-shit-done/workflows/execute-phase.md (offer text)
- get-shit-done/workflows/code-review-fix.md (internal retry hints)
- docs/INVENTORY.md (agent + workflow rows)
- docs/CONFIGURATION.md (workflow.code_review row)
- docs/USER-GUIDE.md (3 occurrences in walkthrough)
- docs/AGENTS.md (gsd-code-fixer agent stub)
- docs/FEATURES.md (commands list + REQ-REVIEW-04)
All replaced with /gsd-code-review --fix. Internal retry hints in the
workflow file itself updated to point at the new form. Release notes
(docs/RELEASE-*.md) and gsd-ns-review's "absorbed by" deletion note
left unchanged — historical/explanatory content.
#3034 — /gsd-plan-milestone-gaps references in:
- get-shit-done/workflows/audit-milestone.md (<offer_next> blocks for
gaps_found and tech_debt: lines 281, 323)
- commands/gsd/complete-milestone.md (gaps_found pre-flight: lines 46, 57)
Replaced with inline closure path:
/gsd-phase --insert <N> "Close gap: <REQ-ID> ..."
/gsd-discuss-phase <N>
/gsd-plan-phase <N>
/gsd-execute-phase <N>
Plus a Nyquist-coverage hint pointing at /gsd-validate-phase /
/gsd-secure-phase for retroactive audit-chain hygiene gaps. The
gsd-ns-project SKILL.md "deleted by #2790" note is preserved
(it's the canonical pointer for future readers asking what
happened to the command).
Tests:
- tests/bug-3029-3034-stale-command-routes.test.cjs — parser-based
assertions per fixed surface, plus a structural cross-check that
gsd-ns-project keeps the deletion note. 15 tests, all green.
- 6905/6905 full suite passes.
Closes#3029Closes#3034
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: address CR feedback on PR #3038 — argument order, structural tests, agent count
CR findings on PR #3038:
1. **docs/USER-GUIDE.md (Major)** — `--fix` examples used flag-first form
(`/gsd-code-review --fix 3`), but the supported CLI grammar is
phase-first (`/gsd-code-review 3 --fix`). The original sed-based
replacement preserved the position of the `gsd-code-review-fix`
token, producing the wrong order. Fixed in USER-GUIDE.md (3
occurrences) and the same drift in the workflow surfaces:
- get-shit-done/workflows/code-review-fix.md (2 retry hints)
- get-shit-done/workflows/code-review.md (offer text)
- get-shit-done/workflows/execute-phase.md (offer text)
2. **docs/AGENTS.md (Minor)** — internal count drift: line 483 said
"Ten additional agents" but line 725 said "12 advanced/specialized".
Filesystem reality: 33 agents total, 21 primary, 12 specialized
(count of `### ` stubs in the Advanced and Specialized section).
Updated lines 3, 13, 483 to use 12/33 and added the two missing
names (doc-classifier, doc-synthesizer) to the inline list at
line 13.
3. **tests:94 (Major refactor suggestion)** — `.includes()` token checks
were source-grep style. Refactored to a typed-IR pattern: extract
the SET of slash-command tokens via regex, assert membership on the
parsed Set instead of substring scanning the raw file text. Added
the `allow-test-rule` comment explaining the IR-build vs
IR-assertion split per scripts/lint-no-source-grep.cjs convention.
4. **tests:130 (Major)** — replacement-path assertion was file-wide and
could false-pass on generic mentions of "inline" elsewhere in the
file. Refactored: `extractOfferBlocks(content)` returns the typed
list of `<offer_next>` and "Pre-flight" blocks where the deleted
command previously lived, and the assertion runs against those
blocks specifically. Now requires `/gsd-phase --insert` or
inline-audit prose to appear in the same offer block, not just
somewhere in the file.
15/15 targeted tests pass. 6905/6905 full suite pass. Lints clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* docs: add issue-driven orchestration guide (#2840)
Adds docs/issue-driven-orchestration.md — a recipe for driving GSD from a
GitHub / Linear / Jira issue using existing primitives. Maps Symphony-style
orchestration concepts onto GSD commands without vendoring code, adding a
daemon, or introducing tracker integration.
Concept mapping covers:
- WORKFLOW.md → ROADMAP.md / STATE.md / phase CONTEXT.md / phase PLAN.md
- isolated agent workspace → /gsd-new-workspace --strategy worktree
- agent dispatch → /gsd-manager (interactive), /gsd-autonomous (unattended)
- per-phase steps → /gsd-discuss-phase → /gsd-plan-phase → /gsd-execute-phase
- proof-of-work → /gsd-verify-work (UAT.md persists across /clear)
- adversarial review → /gsd-review (cross-AI peer review)
- human merge gate → /gsd-ship
- follow-up capture → /gsd-note, /gsd-plant-seed, /gsd-new-milestone
End-to-end flow walks through 7 numbered steps from picking the tracker
issue to capturing follow-ups. Safety boundaries (isolated worktrees,
explicit human review, no automatic public posting, verification before
ship) and non-goals (no vendoring, no daemon, no mandatory tracker, no
gate bypass, no command-surface expansion) are spelled out explicitly so
the doc cannot drift into "let's just add one more flag".
Cross-linked from docs/README.md (Documentation Index) and
docs/USER-GUIDE.md (Table of Contents preamble).
Tests: tests/feat-2840-issue-driven-orchestration-guide.test.cjs — 9
structural-IR tests parse the guide into a typed record and assert on
flags (commandsPresent, conceptPairs, nonGoalFlags, safetyFlags,
numberedSteps). Fence-language MD040 check enforced. Cross-link
presence enforced. No raw-text assertions on prose.
6890/6890 tests pass. Lint:tests clean (allow-test-rule comment justifies
the doc-shape parser per scripts/lint-no-source-grep.cjs escape hatch).
Lint:changeset clean.
Closes#2840
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(test): guard USER-GUIDE.md existsSync before read (CR #3036)
CR Minor: cross-linked-from-USER-GUIDE.md test called fs.readFileSync
directly without first asserting fs.existsSync, asymmetric with the
README.md test above. A missing USER-GUIDE.md would throw ENOENT instead
of producing a meaningful assertion message. Mirror the null-guard
pattern.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat(hooks): opt-in SessionStart update banner for non-statusline users (#2795)
When a user declines (or keeps a non-GSD) statusline at install time, the
installer now offers an opt-in SessionStart banner that surfaces GSD update
availability. The banner reads the existing
~/.cache/gsd/gsd-update-check.json cache (written by
gsd-check-update-worker.js) and emits a single systemMessage line only when
update_available is true:
GSD update available: <installed> → <latest>. Run /gsd-update.
It is silent when up-to-date and rate-limits "check failed" diagnostics to
once per 24h via a sentinel file so a corrupt cache doesn't nag every
session. Removed cleanly by `npx get-shit-done-cc --uninstall` which strips
both the script and the SessionStart entry. The banner is never offered when
GSD's statusline is being installed (statusline already surfaces update
info, so re-prompting would be noise).
Implementation:
- hooks/gsd-update-banner.js — pure functions buildBannerOutput,
shouldSuppressFailureWarning, readCache; thin main() wires them.
- bin/install.js — handleUpdateBanner() prompt, parseUpdateBannerInput(),
buildUpdateBannerHookEntry(), buildUpdateBannerPromptText(); chained into
installAllRuntimes() so finalize() receives both flags. updateBannerCommand
computed alongside the other JS-hook commands; finishInstall() registers
the SessionStart entry only when shouldInstallBanner === true and the
hook file is present at the target.
- Hook ships in scripts/build-hooks.js HOOKS_TO_COPY, listed in
MANAGED_HOOKS for stale-detection in gsd-check-update-worker.js, in the
uninstall hook-removal lists in install.js, and in the
rewriteLegacyManagedNodeHookCommands allowlist.
Tests:
- tests/feat-2795-update-banner.test.cjs — 22 tests, structural-IR
assertions on parsed JSON envelopes (no raw-text matching). Covers
pure-function branches (cache present/absent, parseError, rate-limit
suppression, missing version fields), end-to-end hook invocation against
fixture cache states, and install.js wiring (prompt text, input parsing,
hook entry shape).
- tests/trae-install.test.cjs — updated install() return-shape assertion to
include updateBannerCommand: null for the no-settings runtime.
- 6881/6881 tests pass.
Docs (bundled in same commit per the bundle-docs-with-code skill):
- docs/USER-GUIDE.md — new "Surface GSD Update Notifications Without GSD's
Statusline" task section with opt-in/opt-out instructions.
- docs/FEATURES.md — REQ-HOOK-08 added; "Update Banner" subsection under
the Hook System feature with cache flow + removal path.
- docs/INVENTORY.md — hook count 11 → 12, new row for gsd-update-banner.js.
- docs/INVENTORY-MANIFEST.json — regenerated.
Closes#2795
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(install): gate banner prompt on actual installability (CR #3035)
CodeRabbit findings on PR #3035:
- bin/install.js (Major): continueAfterStatusline gated banner prompt on
the raw `shouldInstallStatusline` flag from handleStatusline. But
finishInstall later silently skips the statusline write on local
installs unless --force-statusline is set (#2248). Two consequences:
1. Interactive local Claude/Gemini installs got neither a statusline
nor a banner offer.
2. Codex/Cursor/Copilot/Windsurf/Trae/Cline-only installs (where
every result.updateBannerCommand is null) still got prompted even
though the choice was silently ignored.
Fix: derive willInstallStatusline = shouldInstallStatusline &&
(isGlobal || forceStatusline), and gate the banner prompt on a
canInstallBanner precondition computed from results[].updateBannerCommand.
Pass the raw shouldInstallStatusline through to finalize unchanged so
per-runtime statusline gating in finishInstall is unaffected.
- tests/feat-2795-update-banner.test.cjs (Minor): rate-limit suppression
test parsed r1.stdout without first asserting r1.status === 0. Other
e2e tests in this file (lines 210, 241) do this. A non-zero exit would
surface as a cryptic SyntaxError instead of a status assertion failure.
Fix applied verbatim.
6881/6881 tests pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>