b5777572f71d557b971f7e273a644f1fc68f9268
2190 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b5777572f7 | docs(readme): add Hermes uninstall examples (CR #2920) | ||
|
|
861a7d972b |
test(install): replace source-grep prompt assertions with structured checks
Two test files were asserting installer prompt behavior by regex/.includes() against bin/install.js source. Per CONTRIBUTING.md "no-source-grep" testing standard, replace with structured assertions: - tests/kilo-install.test.cjs: import runtimeMap and buildRuntimePromptText from the install module; assert runtimeMap['11'] === 'kilo' and that the rendered prompt lists Kilo above OpenCode without marketing copy. - tests/multi-runtime-select.test.cjs: import runtimeMap, allRuntimes, parseRuntimeInput, buildRuntimePromptText. Assert exported runtimeMap matches the canonical option list, allRuntimes contains every runtime exactly once, prompt text lists Hermes (10), Qwen Code (13), Trae (14), All (16), and parser splits/dedupes by exercising parseRuntimeInput rather than regexing source code. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
bd0511988b |
fix(hermes): nest GSD skills under skills/gsd/ category (#2841)
Per spec in #2841, all 86 GSD skills must collapse into a single "gsd" category in Hermes' system prompt. Previous code passed skills/ as the install root, producing a flat skills/gsd-*/ layout that inflated Hermes' loader output to 86 top-level entries. Changes: - Install path now writes to skills/gsd/{DESCRIPTION.md, gsd-*/SKILL.md} - Uninstall removes the entire skills/gsd/ category dir plus any leftover flat-layout gsd-*/ from older installs (graceful migration) - writeManifest emits skills/gsd/<skill>/<file> paths for Hermes - --skills-root hermes returns the nested category path so /gsd-sync-skills syncs into the right directory - DESCRIPTION.md at category root carries name/version/description so Hermes' skill loader surfaces the GSD category in the system prompt Also extracts promptRuntime's runtimeMap, allRuntimes, parseRuntimeInput, and buildRuntimePromptText to module scope and exports them so tests can assert structurally instead of grepping bin/install.js source. Existing hermes-install tests updated to expect the nested layout and to verify the category DESCRIPTION.md frontmatter (name, version, description) using the shared parseFrontmatter helper. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
4ce72cdee7 |
fix(hermes): align with Hermes Agent conventions per docs review
Four fixes from review of hermes-agent.nousresearch.com docs: 1. SKILL.md frontmatter now declares `version` (required field per Hermes spec). Plumbed through `convertClaudeCommandToClaudeSkill` gated on runtime='hermes' so other runtimes' frontmatter is unchanged. 2. Project-context filename rewrite changed from `HERMES.md` (not discovered by Hermes) to `.hermes.md` (top of Hermes' discovery list: .hermes.md → AGENTS.md → CLAUDE.md → .cursorrules). 3. README + finishInstall now show `/gsd-help` and `/gsd-new-project` for Hermes; per docs, Hermes auto-exposes skills as slash commands. 4. Hermes tests now parse SKILL.md frontmatter structurally via the shared parseFrontmatter helper instead of substring-matching source text, and assert the version/name/description shape required by Hermes' skill_view(). Full suite: 6128/6128 pass (3 new structural assertions). |
||
|
|
b126c0579a |
feat(install): add Hermes Agent runtime support (#2841)
Adds Hermes Agent as a supported installation target. Users can run
\`npx get-shit-done-cc --hermes\` to install all 86 GSD commands as
skills under \`~/.hermes/skills/gsd-*/SKILL.md\`, following the same
open skill standard as Claude Code 2.1.88+, Qwen Code, Antigravity,
Trae, Augment, and Codebuddy.
Hermes Agent is an open-source AI agent framework by Nous Research
(NousResearch/hermes-agent, MIT). Its skill loader accepts the Claude
skill format as-is: frontmatter parsed with PyYAML SafeLoader (unknown
keys like \`allowed-tools\` / \`argument-hint\` ignored), body XML tags
(\`<objective>\`, \`<execution_context>\`, \`<process>\`) passed directly
to the model. Compatibility proven end-to-end with all 86 GSD skills
loading cleanly, \`skill_view()\` returning full bodies, and
\`build_skills_system_prompt()\` emitting them into the agent system
prompt — zero Hermes code changes required.
Changes:
- \`bin/install.js\`: --hermes flag, getDirName/getGlobalDir/getConfigDirFromHome
support, HERMES_HOME env var (native to Hermes — used for profile
mode / Docker deploys), install/uninstall pipelines, interactive
picker option 10 (alphabetical: between Gemini and Kilo), .hermes
path replacements in copyCommandsAsClaudeSkills and
copyWithPathReplacement, legacy commands/gsd cleanup, CLAUDE.md ->
HERMES.md and "Claude Code" -> "Hermes Agent" content rewrites in
skills/agents/hooks, runtime-appropriate finish message.
- \`get-shit-done/bin/lib/core.cjs\`: add hermes to KNOWN_RUNTIMES;
add RUNTIME_PROFILE_MAP.hermes with OpenRouter-slug defaults
(Hermes is provider-agnostic; these defaults resolve across
OpenRouter, native Anthropic, and Copilot via Hermes' aggregator-
aware resolver, and are overridable per-tier via
model_profile_overrides.hermes.{opus,sonnet,haiku}).
- \`README.md\`: Hermes Agent in tagline, runtime list, verification
command, install/uninstall examples, \`--hermes\` flag reference.
- \`tests/hermes-install.test.cjs\`: new, 14 tests covering directory
mapping, HERMES_HOME env var precedence, install/uninstall
lifecycle, user-skill preservation, engine cleanup.
- \`tests/hermes-skills-migration.test.cjs\`: new, 11 tests covering
frontmatter conversion, path replacement (~/.claude/ ->
\$HERMES_HOME/skills/), CLAUDE.md -> HERMES.md, "Claude Code" ->
"Hermes Agent", stale skill cleanup, SKILL.md format validation.
- \`tests/multi-runtime-select.test.cjs\`: updated for new option
numbering (hermes=10, kilo=11, opencode=12, qwen=13, trae=14,
windsurf=15, all=16).
- \`tests/kilo-install.test.cjs\`: updated assertions for Kilo having
moved from option 10 to option 11.
Closes #2841
Implementation notes:
- Zero custom code paths: Hermes reuses copyCommandsAsClaudeSkills()
identical to Qwen Code / Antigravity pattern.
- Path replacement: ~/.claude/, \$HOME/.claude/, ./.claude/ ->
.hermes equivalents in skill/agent/hook content.
- Config precedence: --config-dir > HERMES_HOME > ~/.hermes (matches
how Hermes itself resolves its home directory).
- Legacy cleanup: removes commands/gsd/ if present from a prior
install, preserving dev-preferences.md (same as Qwen).
- No external dependencies added.
Testing: 5841 / 5841 tests pass (0 failures, 0 regressions)
- 14 new tests in hermes-install.test.cjs
- 11 new tests in hermes-skills-migration.test.cjs
- multi-runtime-select.test.cjs renumbered + 1 new test (single choice for hermes)
|
||
|
|
006cdafe8f |
ci(drift): enforce alias freshness checks in CI and contributor flow (#2910)
Merging alias-drift guardrails and local hook hardening. |
||
|
|
8051bc4fd8 |
test(golden): expand phases/validate/roadmap parity matrix (#2909)
Merging parity-matrix expansion after stack foundation. |
||
|
|
444db1714b |
refactor(query): manifest-backed routing seam + family adapters (#2908)
Merging validated command-seam foundation. |
||
|
|
6dce1de4a7 |
fix: gap-analysis parses mixed requirement prefixes and skips table headers (#2902)
* fix: parse non-REQ IDs in gap-analysis and ignore table headers * fix: parse requirement IDs from first traceability column only --------- Co-authored-by: Tom Boucher <thomas.boucher@sas.com> |
||
|
|
abb2cb63f6 |
refactor: extract planning-workspace seam from core.cjs (#2901)
* refactor: extract planning workspace seam from core * docs: document planning-workspace module and inventory updates * fix: harden planning lock timeout and preserve workstream set contract --------- Co-authored-by: Tom Boucher <thomas.boucher@sas.com> |
||
|
|
8cbdbdd2de | feat(sdk): add durable planning runtime (#2898) | ||
|
|
951d5bf7c0 |
fix(#2893): surface non-canonical plan filenames instead of silently returning zero plans (#2896)
* fix(#2893): surface non-canonical plan filenames instead of silently returning zero plans Reporter saw `plan_count: 0` from `/gsd:execute-phase` even though five plan files existed on disk. Investigation showed the planner had written files like `01-PLAN-01-foundation.md`, while `phase-plan-index`'s strict filter (`f.endsWith('-PLAN.md') || f === 'PLAN.md'`) rejected them silently — collapsing two distinct states into the same `plans: []` return: - directory truly has no plans (legit empty) - directory has plans but the filter rejected them (user/agent error) The canonical contract is documented in three places: - `agents/gsd-planner.md` write_phase_prompt step (lines 1063-1080) - `commands/gsd/plan-phase.md` - `references/universal-anti-patterns.md` (rule 26) It mandates `{padded_phase}-{NN}-PLAN.md` and explicitly forbids `PLAN-NN.md` / `01-PLAN-01.md` / `plan-NN.md` etc. The strict filter is correct per that contract. The bug is that the executor never tells the user when the contract was violated — they just see `plan_count: 0` with no signal. Fix: add a diagnostic helper `describeNonCanonicalPlans()` that scans the phase directory for files matching `*PLAN*.md` (the diagnostic net) that the canonical filter rejected, excluding legit derivatives like `*-PLAN-OUTLINE.md` and `*-PLAN.pre-bounce.md`. When offenders exist, return a `warning` field naming each one and citing the canonical pattern so the user knows what to rename to. Wired into the three filter sites: - `phase-plan-index` (the executor's main entry point) - `phases list --type plans` - `find-phase` The strict filter itself is unchanged — existing canonical plans behave identically. This is purely a diagnostic that converts silent-empty into loud-with-actionable-error. Tests: - `phase-plan-index returns warning for reporter's exact filename pattern (`01-PLAN-01-foundation.md`)` - `truly empty dir does not emit a warning` - `canonical plans + outline + pre-bounce files do not emit a warning` Closes #2893 * test(#2893): add parity tests for find-phase and phases list --type plans warnings CodeRabbit's only finding on the prior commit: I wired the warning into three filter sites (`phase-plan-index`, `find-phase`, `phases list --type plans`) but only `phase-plan-index` had test coverage for the warning shape. The other two paths could silently diverge during future refactors — exactly the silent-drift class of bug this fix exists to prevent. Add four parity tests mirroring the existing two: - find-phase: non-canonical filenames produce a warning naming each offender + citing the canonical pattern. - find-phase: canonical plan + derivative files (PLAN-OUTLINE, pre-bounce) produce no warning. - phases list --type plans: same non-canonical case, but assert the warning is prefixed with `${dir}: ` (this path aggregates across phase directories so each offender is tagged with its dir). - phases list --type plans: canonical case, no warning. `node --test tests/phase.test.cjs`: 98/98 pass (was 94, +4 new). |
||
|
|
ca88429bf8 |
docs(#2888): release notes for 1.40.0-rc.1 (#2889)
Add docs/RELEASE-v1.40.0-rc.1.md following the rc.7 format. Cover the 11 commits on main since v1.39.0-rc.7's release notes landed: - #2790 — skill surface consolidated 86 → 59 - #2792 — namespace meta-skills + keyword-tag descriptions + context guard - #2833 — phase-lifecycle status-line read-side - #2876 — yamlQuote SKILL.md description (Copilot/Antigravity/Trae/CodeBuddy) - #2768 — Gemini slash command namespace - #2858 — gsd slash namespace drift cleanup - #2851 — bare gsd-tools → absolute path - #2866 — Codex installer trailing-newline preservation - #2868 — canary publish moved from main to dev - #2872 — auto-close PRs without issue link Update CHANGELOG.md [Unreleased] with the same 1.40.0-rc.1 entries. Closes #2888 |
||
|
|
5fdc950eb7 |
feat(#2792): namespace meta-skills + keyword-tag descriptions + context utilization guard (#2825)
* feat(#2792): namespace meta-skills retargeted at the post-#2790 surface This branch is now based on #2790's HEAD (the consolidation PR) instead of main, and every routing table targets the consolidated surface so a user routed by a namespace meta-skill never lands at a deleted / folded sub-skill. Cross-PR inconsistencies the original PR #2825 carried (vs #2790): - ns-ideate routed to gsd-note / gsd-add-todo / gsd-add-backlog / gsd-plant-seed → all folded into gsd-capture by #2790. Now routes to gsd-capture (the parent picks the mode from the user's intent). - ns-context routed to gsd-scan and gsd-intel → folded into gsd-map-codebase --fast / --query by #2790. Now routes to those flag forms. - ns-manage routed all workspace intent to gsd-list-workspaces (a list-only entry) → CR also flagged the over-narrow target. #2790 folds into gsd-workspace; routing now points there. - ns-workflow routed to gsd-research-phase → deleted outright by #2790. Removed. - ns-project routed to gsd-plan-milestone-gaps → deleted outright by #2790. Removed. - None of the namespaces previously surfaced #2790's new consolidated skills (gsd-capture, gsd-phase, gsd-config, gsd-workspace, gsd-progress). All five are now reachable through the routers. - extract_learnings → extract-learnings (canonicalized by #2858). Defect fixes within the namespace skills: - Hyphen-form `name:` (gsd-workflow, …) per the canonical naming contract — the colon-form addressed CR's drift complaint. - `Skill` added to allowed-tools on every router. The body instructs "Invoke the matched skill directly using the Skill tool" — without Skill in the permission list the meta-skill cannot route at all. New regression guard in tests/enh-2792-namespace-skills.test.cjs: every gsd-* token in any namespace router's table column resolves to a surviving commands/gsd/*.md file (or to a known consolidated parent for flag-form targets like gsd-map-codebase --fast). This single test would have caught every dead-end route the original PR shipped with. Skill-count cap in tests/enh-2790-skill-consolidation.test.cjs now filters out ns-*.md from its <= 63 cap. Namespace routers are descriptor-only entries, not part of the consolidation surface that cap is policing — they have their own contract in tests/enh-2792-namespace-skills.test.cjs. INVENTORY.md gains a "Namespace Meta-Skills" section with the 6 router rows; INVENTORY-MANIFEST.json gains 6 entries; the headline count moves 59 → 65 to match. Out of scope for this rebase: the gsd-health --context flag (PR #2825 advertised the contract but didn't implement it). That's a separate feature concern and is left untouched here. 5908/5908 on `npm test`. * feat(#2792): implement gsd-health --context utilization guard The original PR #2825 advertised a `--context` flag on gsd-health with a 60%/70% utilization threshold table but never implemented the workflow logic — CR caught it as a contract leak, the rebase deferred it. This commit closes the gap with TDD red/green/refactor. Math layer (pure): - get-shit-done/bin/lib/context-utilization.cjs classifyContextUtilization(tokensUsed, contextWindow) → { percent, state } State boundaries use the exact ratio: < 60% healthy / 60–70% warning / ≥ 70% critical (fracture point) Display percent rounded for humans. Throws TypeError on non-integer or out-of-range inputs. - STATES = Object.freeze({ HEALTHY, WARNING, CRITICAL }) exported so callers reference the names by symbol, not by literal string. SDK CLI integration: - get-shit-done/bin/gsd-tools.cjs `validate context --tokens-used N --context-window M [--json]` routes to the classifier, owns the recommendation copy (the classifier intentionally does not — keeps the renderer free to evolve without touching the math layer or its tests), and uses core.output's rawValue path for the sync-flush guarantee. - sdk/src/query/validate.ts + sdk/src/query/index.ts TypeScript validateContext handler registered at 'validate.context' and 'validate context'. Mirrors the CJS classifier inline (15 lines of arithmetic; not worth a shared cross-language module). User-facing wiring: - commands/gsd/health.md frontmatter advertises --context, body documents the three-state threshold table. - get-shit-done/workflows/health.md adds a `context_check` step that's reached only when --context is set. Step calls `gsd-sdk query validate.context` with self-reported tokensUsed and contextWindow, prints the SDK output verbatim, and ends. Includes a TEXT_MODE plain-text fallback for non-Claude runtimes per #2012. Tests: - tests/context-utilization.test.cjs (17 tests) — pure-function contract: state thresholds at every boundary, percent rounding, input validation, return-shape (no recommendation field — that's the renderer's job). - tests/validate-context.test.cjs (9 tests) — SDK CLI plumbing: arg parsing errors, JSON vs human rendering, recommendation copy pinned per state. - tests/enh-2792-namespace-skills.test.cjs (4 new tests) — markdown contract: --context advertised in argument-hint, threshold table in command body, context_check step exists in workflow, step invokes gsd-sdk query validate.context with both flags. Inventory bookkeeping: - docs/INVENTORY.md "CLI Modules" 31 → 32; new row for context-utilization.cjs. - docs/INVENTORY-MANIFEST.json mirror. 5939/5939 on `npm test`. |
||
|
|
c72b893916 |
fix(test): unbreak gemini-namespacing test after #2790 skill consolidation (#2886)
Closes #2876 follow-up — CI on main fails because the punctuation test in tests/gemini-namespacing.test.cjs hardcoded `/gsd-scan` as a known command, but #2824 (consolidate 86 → 59 skills) removed scan.md from commands/gsd/. The roster now correctly returns "scan is unknown, leave unchanged" — the conversion is right, the test fixture is stale. Swap `scan` for `health` in the punctuation test. Both are bedrock commands; the test still exercises the original intent (period vs exclamation handling on adjacent slash commands). Note added so the next consolidation reviewer knows the swap pattern. `npm test`: 5936/5936 pass. |
||
|
|
8fc1fa263c |
feat(#2833): phase-lifecycle status-line — read-side (parseStateMd + formatGsdState scenes + tests + docs) (#2884)
* feat(#2833): parseStateMd reads phase-lifecycle frontmatter fields Extend parseStateMd() to parse 4 new STATE.md frontmatter fields that drive the phase-lifecycle status-line proposed in #2833: - active_phase : phase number when orchestrator is in-flight, null when idle - next_action : recommended next command when idle - next_phases : YAML flow array of phase numbers for next_action - progress : nested block with completed_phases / total_phases / percent All fields default to undefined when absent — formatGsdState() (next commit) degrades gracefully so existing STATE.md files keep rendering as before. YAML scope intentionally narrow: - Only top-level scalar keys (status, milestone, active_phase, next_action) - Only single-line flow array for next_phases ([...]) - progress block requires 2-space indent for nested keys Block sequences (- item over multiple lines) and inline comments inside nested blocks are NOT parsed — keeping the regex-based parser predictable. Comments outside frontmatter or after the closing --- still work. Tests: all 27 existing tests still pass (no behavior change for STATE.md files that don't carry the new fields). Refs #2833 * feat(#2833): formatGsdState renders phase-lifecycle scenes + opt-in progress bar Extend formatGsdState() with three lifecycle scenes that activate when the new STATE.md frontmatter fields (added in the previous commit) are present. Also append an opt-in progress bar to the milestone segment when progress.percent is available. Scenes (first match wins; falls through to the existing path otherwise): 1. active_phase set → 'v2.0 [██░] X% · Phase 4.5 executing' (status field carries the lifecycle stage: discussing / planning / executing / verifying) 2. active_phase null + → 'v2.0 [██░] X% · next execute-phase 4.5' next_action set (idle state — surfaces what the user should run next without opening STATE.md) 3. percent=100 (or → 'v2.0 [██████████] 100% · milestone complete' completed=total) 4. (default fallback) → 'v1.9 Code Quality · executing · ph (1/5)' (existing rendering, byte-for-byte preserved when none of the new fields are populated) Backward compat is the design priority: - STATE.md files without the new fields render identically to v1.38.x - progress bar is opt-in (empty string when percent absent) - Each new scene only activates when its specific fields are populated A new helper renderProgressBar() generates the 10-segment bar that matches the existing context meter style (so the two bars on the status-line are visually consistent). Tests: 27/27 existing tests still pass. Refs #2833 * test(#2833): cover parseStateMd lifecycle fields + formatGsdState scenes 26 new tests organized in 5 describe blocks, modeled after the existing enh-2538-statusline-last-command.test.cjs convention: parseStateMd #2833 lifecycle fields (7 tests) - reads active_phase / next_action / next_phases / progress.percent - 'null' literal handled correctly - YAML flow array parsing (1 item, multiple items) - progress nested block (3 fields) - absent fields return undefined formatGsdState #2833 lifecycle scenes (6 tests) - Scene 1: active_phase set → 'Phase X.Y <stage>' - Scene 2: idle + next_action → 'next <action> <phases>' (1+ phases) - Scene 3: percent=100 OR completed=total → 'milestone complete' formatGsdState #2833 backward compatibility (4 tests) — CRITICAL - Legacy STATE.md (no new fields) renders byte-for-byte unchanged - Empty state, partial state, progress-bar-opt-in all preserved progress bar rendering (6 tests) - 0% / 50% / 100% / clamping / opt-in absence formatGsdState #2833 scene priority (3 tests) - active_phase wins over next_action when both populated - next_action wins over fallback when active_phase null - percent=100 wins over fallback even with phase set Combined run: 53/53 tests pass (existing 27 + new 26). Refs #2833 * docs(#2833): describe phase-lifecycle frontmatter fields and rendering scenes Add docs/STATE-MD-LIFECYCLE.md as the canonical reference for the four new STATE.md frontmatter fields and the four status-line rendering scenes introduced by this proposal: - Frontmatter field reference (active_phase / next_action / next_phases / progress.percent) with type and population semantics - Why progress.percent is intentionally the phase dimension and not the plans dimension (plans dimension trends optimistic when future phases are unplanned) - The four rendering scenes including their priority order - Stage-label convention for Scene 1 (discussing / planning / executing / verifying matching the four phase orchestrators) - Frontmatter parsing constraints — frontmatter must start at file head, no comments inside nested blocks, next_phases is single-line flow only - Backward-compatibility guarantee (locked in by the test suite) - Cross-links to the foundation issue #1989 and the read-side issues this proposal helps close The document deliberately scopes itself to the read-side (what the hook parses, what it renders). Write-side SDK and workflow changes that auto-maintain the fields are out of scope for this PR so each piece can be reviewed independently — see the issue thread for the full proposal. Refs #2833 * test(#2833): simplify '0% renders 10 empty segments' assertion Address CodeRabbit nitpick — drop the convoluted assert.equal that built the expected value via .replace() and rely on the existing assert.ok includes-check. The behavior under test is unchanged; the assertion is just easier to read. Refs #2884 review comment |
||
|
|
87917131f2 |
refactor(#2790): consolidate 86 gsd-* skills to 59 — fold flags, delete dead skills (#2824)
* feat(#2790): consolidate 86 gsd-* skills to 59 — zero functional loss Closes #2790 - `capture.md` — absorbs add-todo (default), note (--note), add-backlog (--backlog), plant-seed (--seed), check-todos (--list) - `phase.md` — absorbs add-phase (default), insert-phase (--insert), remove-phase (--remove), edit-phase (--edit) - `config.md` — absorbs settings-advanced (--advanced), settings-integrations (--integrations), set-profile (--profile); settings.md retained as-is - `workspace.md` — absorbs new-workspace (--new), list-workspaces (--list), remove-workspace (--remove) - `update.md` — adds --sync (absorbs sync-skills) and --reapply (absorbs reapply-patches) - `sketch.md` — adds --wrap-up (absorbs sketch-wrap-up) - `spike.md` — adds --wrap-up (absorbs spike-wrap-up) - `map-codebase.md` — adds --fast (absorbs scan) and --query (absorbs intel) - `code-review.md` — adds --fix (absorbs code-review-fix) - `progress.md` — adds --next (absorbs next) and --do (absorbs do) join-discord, research-phase, session-report, from-gsd2, analyze-dependencies, list-phase-assumptions, plan-milestone-gaps autonomous.md: updated Skill(skill="gsd:code-review-fix") → Skill(skill="gsd:code-review", args="--fix --auto") to match the consolidated skill name - New: tests/enh-2790-skill-consolidation.test.cjs (48 tests) - Updated: 14 existing test files redirected from deleted command paths to their consolidated equivalents - docs/INVENTORY.md: Commands count 86→59, ghost rows removed, new consolidated rows added - docs/INVENTORY-MANIFEST.json: regenerated to match filesystem Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(#2790): add CHANGELOG entry for skill consolidation * docs(#2790): update COMMANDS.md for 86→59 skill consolidation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2790): address CodeRabbit review findings - CHANGELOG.md: add --next alongside --do in progress flag list - config.md: remove trailing space from --profile code span (MD038) - COMMANDS.md: add required descriptions to /gsd-phase examples; /gsd-phase without args errors, not interactive - COMMANDS.md: add --next and --do to /gsd-progress flags table + examples - test: convert content.includes('--reapply') to structural frontmatter parse; add allow-test-rule comment for workflow content assertions - test: replace redundant existsSync duplicate with assertion that verifies the full consolidated flag surface (--sync | --reapply) in argument-hint Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2790): restore reapply-patches workflow and strengthen test assertions - Create get-shit-done/workflows/reapply-patches.md: the #2790 consolidation deleted the 14K combined command+workflow file (reapply-patches.md) but update.md already referenced the workflow via execution_context_extended. Restoring it fixes a silent behavioral gap where --reapply had no workflow to load. Includes full three-way merge logic, hunk verification table (Step 4), and the Hunk Verification Gate (Step 5) that blocks cleanup until all user-added hunks are confirmed present in the merged output. - Fix update.md: /gsd-reapply-patches → /gsd-update --reapply (stale ref) - Fix reapply-verify-hunks.test.cjs: was checking existsSync(update.md) 8×; now points to the workflow file and asserts real behavioral content (Post-merge verification, Hunk presence check, Line-count check, backup reference, per-file tracking, structural ordering) - Fix reapply-patches.test.cjs: replace content.includes() stubs with frontmatter-parsed argument-hint assertions; replace 4 existsSync(update.md) no-ops with real assertions against the workflow content - Fix edit-phase.test.cjs: /gsd-edit-phase → /gsd-phase (COMMANDS.md now documents the consolidated command with --edit flag) - Fix next-safety-gates.test.cjs: split OR predicates into independent assertions — --next in progress.md and --force in next.md workflow - Fix workspace.test.cjs: add allow-test-rule comment for routing content checks (command routing text IS the deployed behavioral contract) - Fix bug-2439 test: strengthen pre-flight assertion to verify gsd-sdk is referenced (not just --profile) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address CodeRabbit review findings (CR round 2) - INVENTORY.md: update sync-skills.md row to reference /gsd-update --sync instead of stale /gsd-sync-skills (absorbed in #2790) - enh-2380-sync-skills.test.cjs: align INVENTORY.md assertion with the corrected reference; was asserting the old /gsd-sync-skills name while the manifest test correctly asserted /gsd-update, creating conflicting expectations in the same suite - reapply-verify-hunks.test.cjs: add explicit notEqual(-1) assertions for all three anchors before the ordering check so a missing anchor produces a clear failure instead of a false positive (writeIdx=-1 < verifyIdx=5 is true) - bug-2439-set-profile-gsd-sdk-preflight.test.cjs: defer fs.readFileSync until after the existence assertion; eager describe-level read caused the suite to crash before the existence test could run, making it effectively dead code Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2790): address CR — INVENTORY routing + reapply test contract wording Two unresolved CodeRabbit findings (Major): - docs/INVENTORY.md: workflow-file table still pointed at obsolete /gsd-do, /gsd-next, /gsd-note, /gsd-add-todo, /gsd-add-backlog, /gsd-check-todos, /gsd-plant-seed slash commands. Re-route to the consolidated /gsd-progress (--next, --do) and /gsd-capture (--note, --backlog, --seed, --list) so the inventory is internally consistent. - tests/reapply-verify-hunks.test.cjs: 'verification tracks per-file status' asserted on phrasing that doesn't appear in reapply-patches.md (the 'per-file' substring only matched accidentally via 'sequential integer per file'). Switch to the actual contract text — Hunk Verification Table, one row per hunk per file, verified column. * test(#2790): update CR-INTEGRATION tests for consolidated --fix invocation After the merge of main (which carries #2843's hyphen-form fix), the consolidation in this branch absorbs gsd-code-review-fix into gsd-code-review as the --fix flag. Update the two CR-INTEGRATION tests that previously asserted on the standalone gsd-code-review-fix skill name to instead assert on a gsd-code-review invocation carrying --fix in its arg tokens. Tests still parse Skill() invocations structurally; only the asserted skill-name + arg-token shape changed. * test(#2790): scope success_criteria check to the <success_criteria> block CodeRabbit nitpick: 'success criteria includes verification' did a whole-file substring check, which can false-pass if the phrase appears elsewhere in the document. Extract the <success_criteria>...</success_criteria> block first via extractTagBlock() and assert against that scope only. * fix(#2790): post-rebase reconciliation with main - INVENTORY.md/JSON: add reapply-patches workflow row + bump count to 85 - autonomous.md: switch consolidated --fix invocation to hyphen Skill name - analyze-dependencies test: assert COMMANDS.md does NOT document the consolidated-away /gsd-analyze-dependencies entry (was: bare .includes()) * fix(#2790): address remaining CR findings — strengthen contract tests Doc-fixes: - INVENTORY.md: route transition.md & edit-phase.md rows to consolidated /gsd-progress --next and /gsd-phase --edit (was: deleted /gsd-next, /gsd-edit-phase) - config.md --profile branch: document #2439 pre-flight `command -v gsd-sdk` guard + install hint BEFORE the gsd-sdk invocation (closes opaque "command not found: gsd-sdk" regression path) Test discipline (no-source-grep contract): - bug-2439: replace bare `content.includes('gsd-sdk')` with structured parse of <context> block + --profile branch; assert pre-flight token, install hint, #2439 citation, and ordering vs gsd-sdk invocation - edit-phase: parse INVENTORY.md edit-phase.md row's "Invoked by" column and assert `/gsd-phase --edit` (not the deleted /gsd-edit-phase) - next-safety-gates: tighten `--next` documentation contract — require --next AND --force AND completeness routing (was OR-based, passed when only --next present) - reapply-patches: parse argument-hint flag list structurally; scan ALL <execution_context*> blocks for the @-include of reapply-patches.md; parse Hunk Verification Table header columns directly; locate Step 5 via heading parsing then assert (i) table reference, (ii) verified=no gate, (iii) STOP/halt directive, (iv) explicit absent-table halt path - workspace: parse frontmatter, tokenize argument-hint across multiple bracketed segments, parse @-include targets from <execution_context> rather than substring-matching the file body --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
55298b2f70 |
fix(#2876): yamlQuote SKILL.md description for Copilot/Antigravity/Trae/CodeBuddy (#2881)
* fix(#2876): yamlQuote description in Copilot/Antigravity/Trae/CodeBuddy SKILL.md A description starting with `[BETA]` (or any YAML flow indicator — `{`, `*`, `&`, `!`, `|`, `>`, `%`, `@`, backtick) is parsed as a flow sequence/mapping by YAML 1.2-strict loaders. gh-copilot's frontmatter loader fails closed: ✖ ~/.copilot/skills/gsd-ultraplan-phase/SKILL.md: failed to parse YAML frontmatter: Unexpected scalar at node end at line 2, column 21: description: [BETA] Offload plan phase to Claude Code's ultraplan… Six emission sites in `bin/install.js` re-wrote the description without quoting, while the Claude variant (`convertClaudeCommandToClaudeSkill`) already routed it through `yamlQuote`. Brought all six in line: - convertClaudeCommandToCopilotSkill - convertClaudeAgentToCopilotAgent - convertClaudeCommandToAntigravitySkill - convertClaudeAgentToAntigravityAgent - convertClaudeCommandToTraeSkill - convertClaudeCommandToCodebuddySkill Each now wraps the value in `yamlQuote(...)` so any leading character is parser-safe. Regression test (tests/bug-2876-skill-frontmatter-quote.test.cjs) drives the four command converters and two agent converters through the reporter's exact "[BETA] …" description plus a grab-bag of YAML flow indicators, asserting the emitted `description:` value is a quoted YAML scalar. Also round-trips the value through `JSON.parse` for converters that don't apply runtime-name substitution to confirm fidelity. Updated 7 pre-existing substring assertions in copilot-install.test.cjs and antigravity-install.test.cjs that hard-coded the unquoted form. Round trip: 5893/5893 pass on `npm test`. Closes #2876 * test(#2876): structurally parse frontmatter instead of substring-grep Addresses CodeRabbit's two nitpicks on PR #2881: the pre-existing substring assertions in copilot-install.test.cjs (4 sites) and antigravity-install.test.cjs (3 sites) only got bumped from the unquoted form (`description: Diagnose...`) to the quoted-prefix form (`description: "Diagnose...`). Both are still raw-string checks against rendered YAML and drift on any quoting/order change — exactly what the project's CONTRIBUTING.md "no-source-grep" testing standard exists to prevent. Add `parseFrontmatter()` to tests/helpers.cjs — a small parser that handles the YAML scalar forms the install converters emit (double-quoted JSON, single-quoted with `''` escape, bare). Throws if the content has no closed `---` block so a regression in the emitter shape fails loudly rather than silently returning {}. Refactor the 7 description-substring sites to compare on parsed values: the assertion now reads as `fm.description === 'Diagnose planning directory health'` rather than `result.includes('description: "Diagnose planning directory health')`. Same coverage of the #2876 quoting behavior, no coupling to byte-level quote style. `npm test`: 5893/5893 pass. Closes #2876 * test(#2876): make parseFrontmatter delimiter check CRLF/whitespace tolerant CR nitpick on PR #2881 (review at 03:08:08Z): parseFrontmatter() splits on '\n' and compares each line strictly to '---'. A Windows-authored skill file (CRLF endings) leaves a trailing '\r' on every line, so '---\r' fails the equality check, and the helper throws "no closed --- block" on perfectly valid input. Same problem with whitespace-padded delimiter lines. Switch to splitting on /\r?\n/ and comparing the trimmed line. Helper is used by tests/copilot-install.test.cjs and tests/antigravity-install.test.cjs, so this also de-flakes those suites on Windows runners. 5893/5893 on `npm test`. |
||
|
|
4d394a249d |
fix(commands): normalize gsd slash namespace drift (#2858)
* fix(commands): normalize gsd slash namespace drift * fix(#2855): address CodeRabbit findings on namespace drift PR Three CR findings, all valid: 1. autonomous.md line 783 still had `gsd:discuss-phase` (the PR's own normalization missed this line). Switched to `gsd-discuss-phase` and updated the matching test in autonomous-interactive.test.cjs that was asserting the now-retired colon form. 2. tests/bug-2543-gsd-slash-namespace.test.cjs source-grepped the fix-slash-commands.cjs script with .includes() rather than driving its transform behaviour. Refactored fix-slash-commands.cjs to export a pure transformContent(src, cmdNames) function, kept the CLI behaviour unchanged via require.main, and replaced the source-grep block with five behavioural cases: rewrite, multi-occurrence, idempotence on canonical input, no-op on gsd-sdk/gsd-tools, and word-boundary safety. 3. tests/bug-2808-skill-hyphen-name.test.cjs matched `name:` anywhere in SKILL.md; a stray name: in the body could satisfy the assertion. Scoped the lookup to the YAML frontmatter block via the suggested diff (parse the leading --- ... --- region first, then find name: inside it). Full suite: 5854/5854 passing. * fix(#2855): address remaining CodeRabbit findings on PR #2858 Three structural concerns flagged on the namespace-drift fix PR: 1. scripts/fix-slash-commands.cjs:24 — `buildPattern([])` compiled `/gsd:()(?=[^a-zA-Z0-9_-]|$)/g`. The empty capture group still matches any `/gsd:` token followed by a non-word boundary (whitespace, EOL, punctuation), rewriting it to a stray `/gsd-`. Verified live: `transformContent("/gsd:", [])` → `"/gsd-"`. Added a guard returning null from `buildPattern` on empty input and updated `transformContent` and `processDir` to no-op when the pattern is null. 2. tests/autonomous-interactive.test.cjs:44-47 — assertion was `content.includes('gsd-discuss-phase') && content.includes('INTERACTIVE')`, which would false-pass on any unrelated co-occurrence (e.g. `INTERACTIVE=""` initialization plus a stray `gsd-discuss-phase` prose mention). Replaced with a structural extraction: locate the `**If \`INTERACTIVE\` is set:**` branch, bound it by the next `**If` / `<step>` boundary, and assert the `Skill(skill="gsd-discuss-phase", ...)` invocation lives inside that region. Tolerates whitespace around `(`, `skill`, and `=`. 3. tests/bug-2808-skill-hyphen-name.test.cjs:104 — colon-call regex was `Skill\(skill=...` and missed valid formatting like `Skill(skill = "gsd:cmd")` or `Skill( skill = ...)`. Loosened to `Skill\(\s*skill\s*=\s*...` so reformatting drift can't slip past the namespace guard. Verification: 5854/5854 pass on `npm test` from the rebased branch. * fix(#2855): drop pre-validation filter that hid namespace drift CR finding on tests/bug-2808-skill-hyphen-name.test.cjs:128: the test collected generated skill directories with `.filter(entry => entry.isDirectory() && entry.name.startsWith('gsd-'))`, then validated namespace invariants over that filtered list. Anything that violated the prefix invariant — `gsd:extract-learnings` (colon form), `extract_learnings` without prefix, `Gsd-foo` mis-cased — would silently disappear from the iteration and the test would falsely pass. Drop the `startsWith('gsd-')` filter so every generated directory shows up. Add explicit assertions before the existing per-skill loop: - directory list is non-empty (catches a broken converter that produces nothing) - every directory begins with `gsd-` - every directory contains no `:` - every directory contains no `_` Re-audited the full PR diff for the same anti-pattern: only this one site filtered before validating the namespace; bug-2643 and commands-doc-parity also use `readdirSync().filter()` but only by file extension, which is correct. 5854/5854 on `npm test`. * fix(#2855): address remaining CR findings (1 active + 2 nitpicks) Three findings on PR #2858, all the same root cause: input narrowing before validation lets drift slip past the guards. 1. tests/bug-2808-...:104 (active) — `colonCallRe` captured local names with `[a-z0-9-]+`, which excluded the underscore. A drift like `Skill(skill="gsd:extract_learnings")` (deprecated colon syntax with the old underscore filename) silently slid through. Broadened the capture to `[^'"\s)]+` so any malformed local name is surfaced; surrounding pattern (whitespace tolerance, escape support, flags) unchanged. 2. tests/bug-2643-...:43 (nitpick) — `extractSkillNamesHyphen` and `extractSkillNamesColon` had the same over-strict capture plus relied on a single regex over raw bytes, which the project test- rigor memory bans (`feedback_no_source_grep_tests.md`). Replaced with `extractSkillCalls(content)` — a small structural extractor that walks `Skill(` openers, locates each call's matching `)`, parses the body's `skill = "..."` keyword argument with permissive whitespace + quoting + escape handling, and returns `{ name, raw }` records. The two namespace-form helpers become thin filters over the structured output. Tightened the body class to `[^'"\\]+` so a trailing escape `\` before the closing quote (as in `Skill(skill=\"gsd-foo\", …)` written inside another string context) doesn't get included in the captured name. 3. tests/bug-2543-...:44 (nitpick) — `DOC_SEARCH_FILES` was a hand- curated 7-entry array. Every doc added in the future would silently weaken drift detection until someone remembered to extend the list. Replaced with `discoverDocSearchFiles(ROOT)`: globs every `.md` under `docs/` and adds `README.md` if present. New docs are picked up automatically. Re-audited the diff surface for similar narrowings; no other sites filter or constrain before validating namespace invariants. 5854/5854 on `npm test`. * fix(#2855): recurse docs/ tree so localized translations are scanned too CR finding: discoverDocSearchFiles() stopped at docs/*.md, leaving localized translation trees (docs/ja-JP/, docs/zh-CN/, docs/ko-KR/, docs/pt-BR/) and other nested doc collections (docs/skills/, docs/superpowers/) invisible to the namespace-drift invariant. Verified the gap: docs/ has 6 nested directories with ~30 .md files that the previous top-level-only scan was skipping. None contain /gsd: references today, but a future translation update or new doc subdir could leak drift. Switch to an iterative stack walk so every .md under docs/ is scanned regardless of depth. Stack form (rather than recursion) avoids the risk of running into the call-stack limit on deep doc trees. 5854/5854 on `npm test`. --------- Co-authored-by: Tom Boucher <trekkie@nomorestars.com> |
||
|
|
73b9d1dac0 |
fix(install): use colon namespace for Gemini slash commands (#2768)
* fix(install): use colon namespace for Gemini slash commands and help reference This fixes unexecutable command recommendations in Gemini CLI by correctly namespacing slash commands (/gsd: instead of /gsd-) in all installed artifacts (agents, commands, workflows). - Implements a lazy command roster discovery to ensure 100% accurate conversion and protect file paths, URLs, and agent names. - Adds isolated behavioral and unit tests covering all boundary cases. - Fixes hardcoded command strings in banners and help output. Closes #2783 * fix(install): close roster gaps in Gemini /gsd- → /gsd: conversion (#2783) Addresses adversarial review findings on PR #2768: - Restore regex boundaries (lookbehind + extension lookahead). Roster-only matching was insufficient: a URL like `https://example.com/gsd-plan-phase` ends in a known command and would be incorrectly converted. Boundaries + roster now agree before any conversion fires. - Smarter trailing lookahead `(?!\.[a-z])` distinguishes file extensions (`.cjs`, `.md`) from sentence-ending punctuation (`.` at end of input or before whitespace), so `/gsd-help.` correctly converts. - Fail loud on missing roster. `commands/gsd/` not found previously fell through to an empty Set, silently no-op'ing every conversion — exactly the bug this code exists to prevent. Now emits a one-shot console.warn (gated on GSD_TEST_MODE) before returning the empty set. - Drop unnecessary `i` flag — GSD commands are always lowercase; matching uppercase tokens against a lowercase roster always misses anyway. - Export `_resetGsdCommandRoster` for test isolation against the module-level cache. Test additions pin the actual safety property of the roster check by using KNOWN command names embedded in URLs and sub-paths — the cases the prior tests didn't reach because they used `gsd-tools` (not in roster). Added a roster-load assertion that fails loudly if the empty-Set fallback path silently neutralises conversions. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(install): centralize <sub> stripping and add structural test assertions CodeRabbit findings on the prior commit: - (actionable) Centralizing the Gemini conversion through convertClaudeToGeminiMarkdown dropped the stripSubTags() call that the inline command path used to make before TOML conversion. Move stripSubTags inside convertClaudeToGeminiMarkdown so command/agent/non-command Gemini outputs all have <sub> consistently stripped. Remove the now-redundant stripSubTags call in convertClaudeToGeminiAgent (single source of truth). - (nitpick) Replace `.includes()` checks in the TOML test with structured parsing — JSON-decode each TOML value and assert on parsed fields, per the project's "tests parse, never grep" convention. - (nitpick) Strengthen the install behavioral test to read a real installed artifact (.gemini/commands/gsd/plan-phase.toml), parse it, and assert the prompt body actually contains a /gsd: reference and no unconverted /gsd-plan-phase. A directory-only check would have passed even if every conversion silently no-op'd. - Add a regression test that <sub> tags are stripped through the convertClaudeToGeminiMarkdown pipeline. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Tom Boucher <trekkie@nomorestars.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
99af76b3ba |
fix(#2851): replace bare gsd-tools invocations with absolute path (#2869)
* fix(#2851): replace bare gsd-tools invocations with absolute path `gsd-tools` is not a published bin entry — package.json declares only get-shit-done-cc and gsd-sdk. The shipped invocation pattern is `node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" <subcommand>`, used by every other workflow file. Two leaked bare invocations: - get-shit-done/workflows/plan-phase.md §13e (gap-analysis) — reported in #2851; gap-analysis silently skipped on every plan-phase run - get-shit-done/workflows/ingest-docs.md §finalize (commit) — caught by the new structural test; ingest-docs commit step was broken Both updated to canonical absolute-path form. Adds tests/bug-2851-workflow-bare-gsd-tools.test.cjs which parses every markdown file under get-shit-done/workflows/, extracts shell-fenced code blocks, tokenizes each line, and asserts no token in command position is the bare string `gsd-tools` (the trailing `.cjs` is a different token). The test also asserts plan-phase.md's gap-analysis call uses the canonical `node …/gsd-tools.cjs` form. Closes #2851 * fix(#2851): catch third bare gsd-tools call in ingest-docs.md init After the first commit, the structural test was strengthened to detect bare `gsd-tools` inside `$(...)` and backtick command-substitution forms. The improved test surfaced a third leak: ingest-docs.md:55: INIT=$(gsd-tools init ingest-docs) Fixed to canonical form INIT=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" init ingest-docs) plus the standard `@file:` handoff line that every other workflow uses when capturing INIT (required by tests/windows-robustness.test.cjs). Updated tests/bug-2801-ingest-docs-handler.test.cjs to match either the bare `gsd-tools init ingest-docs` or canonical `gsd-tools.cjs" init ingest-docs` form — the test's intent is to verify the dispatch handler is wired, not to lock the bare-bin form that #2851 removes. Closes #2851 * test(#2851): tighten ingest-docs and gap-analysis assertions to canonical form CodeRabbit caught two soft assertions in the regression tests: 1. tests/bug-2801: the init-ingest-docs assertion accepted both the legacy bare `gsd-tools` form and the canonical node-path form. Since #2851 is the fix that removes the bare form, the test should only accept the canonical absolute-path invocation. Switched to parsed-bash-block extraction with an anchored regex on the full `node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs"` path. 2. tests/bug-2851: the gap-analysis assertion used two loose .includes()/word-boundary checks. Replaced with a single assert.match() against the full canonical path so non-canonical forms fail. * test(#2851): env-assignment skip accepts lowercase identifiers too CodeRabbit caught: the cmdIdx-skip regex /^[A-Z_][A-Z0-9_]*=/ only matched uppercase variable names, so a line like `tmp=1 gsd-tools init` would tokenize to ['tmp=1','gsd-tools','init'], the regex would fail on 'tmp=1', cmdIdx would stay at 0, and the command-position check would compare 'tmp=1' against 'gsd-tools' — false negative. POSIX shell variable names are [A-Za-z_][A-Za-z0-9_]*. Widen the regex to match the actual lexical rule. Existing uppercase forms still work (FOO=bar gsd-tools); now lowercase forms (tmp=1 gsd-tools) and mixed case forms are also detected. |
||
|
|
ef08a89241 |
fix(#2866): Codex installer strips legacy hooks at EOF without trailing newline (#2870)
* fix(#2866): Codex installer strips legacy hooks at end-of-file without trailing newline The four shape-strip regexes in `bin/install.js` (Codex install path) required `\r?\n` at end. A stale GSD hook block sitting at end-of-file without a trailing newline (common — many editors strip them, and the legacy installer never wrote one) failed every shape, the installer saw `gsd-check-update` already present, skipped writing the new Nested-AoT block, and Codex 0.125+ refused to load with invalid type: map, expected a sequence in `hooks` Root cause + fix ================ Each shape's terminator changed from `\r?\n` to `(?:\r?\n|$)`, so end-of-file is also a valid terminator. Strip logic was lifted into a new pure helper `stripStaleGsdHookBlocks(configContent)` that the install pipeline now calls in place of the inline replace chain. The helper is exported via the GSD_TEST_MODE module.exports for direct unit-test coverage. Regression test =============== `tests/bug-2866-codex-strip-no-trailing-newline.test.cjs` exercises all four historical shapes (Shape 1 — pre-#1755 gsd-update-check; Shape 2 — flat [[hooks]]+gsd-check-update; Shape 3 — single [[hooks.SessionStart]] without nested .hooks; Shape 4 — correct two-block nested) twice each: once with a trailing newline (regression guard against the existing behavior) and once at end-of-file without a trailing newline (the reporter's exact repro). It also asserts: - the helper is a no-op when no GSD reference is present, and - Shape 4 strip does not leave an orphaned [[hooks.SessionStart]] header behind (the same ordering invariant the inline code relied on). The helper is loaded via `package.json` `bin` field, not a hardcoded path — `tests/bug-2866-codex-strip-no-trailing-newline.test.cjs` parses package.json and resolves `pkg.bin['get-shit-done-cc']` to require the installer. Closes #2866 * test(#2866): assert TOML structure, not raw-text substrings CodeRabbit caught the strip assertions using `.includes()` against raw TOML output. Added a small line-structural parseTomlShape() helper (table headers + dotted-path key/value map, comments stripped) and rewrote the assertions to: - Verify no [[hooks.* table header survives the strip - Verify no key carries a stale gsd-(update|check)-(check|update) value - Verify history.persistence is preserved as the parsed string "save-all" Behaviour is unchanged (the strip function under test is not modified). The assertions now check structural shape rather than substring presence, which catches re-shaping regressions that text matching would miss. No new dependencies — the parser is local to the test and handles only the small well-formed TOML these tests construct. * refactor(#2866): replace regex hook strip with TOML AST removal Per CR feedback on PR #2870: the regex-driven `stripStaleGsdHookBlocks` implementation was fragile to whitespace, indentation, and key-ordering variations the regression test never exercised. Variations the regex silently leaked (verified before the rewrite): - Shape 4 with an extra blank line between parent/child tables - Shape 2/3 with `command` ordered before `event` - Shape 3 with an extra `timeout = 5000` key — worse than a leak: the regex matched only the command line, leaving `timeout = 5000` orphaned outside any TOML table (invalid TOML) - Tight whitespace `event="SessionStart"` (no spaces around `=`) The structural rewrite uses the TOML parser already present in this file (`getTomlTableSections` + `getTomlLineRecords` + `parseTomlValue` + `removeContentRanges` + `collapseTomlBlankLines`): 1. Find every section whose path is `hooks` or starts with `hooks.`. 2. For each, walk the section's line records and parse `command` values structurally — match by basename equality (`gsd-update-check.js` or `gsd-check-update.js`), never by regex on raw bytes. 3. Detect orphaned `[[hooks.SessionStart]]` parents: empty body and a stale child immediately follows → mark for removal. 4. Extend each removal range backward through any preceding `# GSD Hooks` marker line (detected via line records, not text scan). 5. Remove ranges atomically and collapse resulting blank-line runs. Legacy hook basenames are hoisted to template-literal constants so the existing `install-hooks-copy.test.cjs` quoted-literal guard continues to catch accidental *registration* of the inverted filename, while strip detection (which legitimately needs both names) bypasses it. Test coverage added: 8 new sub-tests exercising the four whitespace/ ordering variations (with and without trailing newline) plus a `[[hooks.UserPromptSubmit]]` user-authored hook to guarantee the strip only touches GSD-managed sections. 20/20 in the file, 5867/5867 in the full suite. |
||
|
|
f2ada8500c |
chore(#2868): switch canary publish from main to dev branch (#2871)
* chore(#2868): switch canary publish from main to dev branch Swaps the four `if:` guards in `.github/workflows/canary.yml` from `refs/heads/main` to `refs/heads/dev` so the canary stream is owned by the new long-lived integration branch. Adds a policy comment at the top of the workflow documenting the branch->dist-tag mapping (dev=@canary, main=@next/@latest, no overlap). Closes #2868 * fix(#2868): summary block matches publish-step gate CodeRabbit caught: the Summary step keyed off DRY_RUN only, so a non-dry-run on main would falsely report "Published"/"Tagged" even though all four publish steps were skipped by the new dev-only gate. Add PUBLISH_ELIGIBLE env mirroring the publish-step `if:` expression and a VALIDATION ONLY branch in the summary so non-dev runs report honestly. |
||
|
|
f6a6e43226 |
fix(#2872): auto-close PRs that omit the issue-link keyword (#2873)
The Require Issue Link workflow was posting a comment and failing the status check, but never transitioning the PR to closed. PR templates promise auto-close behavior; PR #2863 demonstrated the gap (opened without a Closes #N, sat open until manually closed). Adds a `pulls.update({state: 'closed'})` call after the existing comment, updates the comment heading to 'PR auto-closed', and tells the author how to reopen after fixing the body. Closes #2872 |
||
|
|
107a83ebf7 |
docs(#2859): add release notes for 1.39.0-rc.7 (#2860)
rc.7 will be the first RC in the 1.39.0 train that actually rolls in the post-rc.5 fixes from main (rc.6 was content-identical to rc.5 — see #2856). Notes enumerate each fix with PR/issue link, recap rc.6 / rc.5 / rc.4, and follow the established docs/RELEASE-v1.39.0-rc.X.md format. No SDK-version pinning advice (consistent with the rc.6 doc cleanup). Markdownlint-clean fenced code blocks. Closes #2859 |
||
|
|
43a13217b7 |
docs(#2856): add docs/RELEASE-v1.39.0-rc.6.md (#2857)
* docs(#2856): add release notes for 1.39.0-rc.6 Documents what's actually in rc.6 (= rc.5 content + version-bump only — release/1.39.0 was not synced with main before the bump) plus the known SDK publish failure (@gsd-build/sdk@1.39.0-rc.6 is missing from npm with 404 PUT error). Format mirrors RELEASE-v1.39.0-rc.5.md. Closes #2856 * docs(#2856): drop SDK refs from rc.6 notes; tag git log fence Per maintainer + CodeRabbit review: - Strip the 'Known issue: split publish' section, the SDK pin Note, and the @gsd-build/sdk follow-up bullet. SDK publish failure is a known separate issue and shouldn't block the rc.6 docs. - Add bash language tag to the git log fence (markdownlint MD040). |
||
|
|
2498f5649d |
docs(release): backfill CHANGELOG with 17 RC-train entries before v1.39.0 final cut (#2854)
Adds [Unreleased] entries for PRs that landed between v1.39.0-rc.4 and v1.39.0-rc.6 but were missing from CHANGELOG.md. One bullet per PR, grouped Added (#2828) and Fixed (16 entries: #2788, #2791, #2794, #2796, #2798, #2801, #2803, #2805, #2808, #2829, #2831, #2832, #2835, #2836, #2838, #2839). Closes #2853 |
||
|
|
e81592878e |
feat(#2789): trim skill description anti-patterns; enforce 100-char budget (#2823)
* feat(#2789): trim skill description anti-patterns; enforce 100-char budget - Trim descriptions in all commands/gsd/*.md files over 100 chars - Remove flag documentation from descriptions (belongs in argument-hint) - Remove Triggers: keyword stuffing - Add scripts/lint-descriptions.cjs — fails on descriptions > 100 chars - Add npm script: lint:descriptions - Add tests/enh-2789-description-budget.test.cjs Closes #2789 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(#2789): add CHANGELOG entry for description budget lint * docs(#2789): update COMMANDS.md descriptions; add skill description standards note Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
4815b3c972 |
fix(#2838): SUMMARY rescue handles gitignored .planning (#2850)
* fix(#2838): SUMMARY rescue handles gitignored .planning explicitly The pre-fix rescue used `git ls-files --modified --others --exclude-standard` to detect uncommitted SUMMARY.md before worktree removal. When projects gitignore .planning/, --exclude-standard filters out the very files the rescue is meant to save, the rescue branch is skipped, and `git worktree remove --force` permanently deletes the SUMMARY. Replace both rescue blocks (quick.md, execute-phase.md) with a filesystem-level find + cp rescue that bypasses gitignore entirely and avoids the worktree↔main commit/merge cascade. cmp -s makes it idempotent. Adds tests/bug-2838-summary-rescue-gitignored-planning.test.cjs which extracts each rescue block, runs it against a real temp repo with a gitignored .planning/, and asserts the SUMMARY survives worktree removal. * test(#2838): assert rescue block exits 0 in idempotency test CodeRabbit (Minor): the idempotency test pre-creates the destination SUMMARY.md, so even a syntax/runtime error in the rescue block would silently false-pass. Add an explicit r.status === 0 assertion. |
||
|
|
f9ed47ac8b |
fix(#2832): gsd-sdk auto detects Codex runtime correctly (#2844)
* fix(#2832): gsd-sdk auto detects Codex runtime correctly Two-part fix for #2832 (gsd-sdk auto silently routing non-Claude runtime projects through the Claude Agent SDK): 1. Runtime gate at the `auto` entry point. New `runtime-gate.ts` exports `assertRuntimeSupportsAutoMode(config)` which throws an actionable error when `GSD_RUNTIME` / `config.runtime` resolves to a non-Claude runtime (codex, gemini, opencode, etc.). The autonomous orchestrator only knows how to drive `@anthropic-ai/claude-agent-sdk` today; failing fast with a clear pointer at the in-session slash commands beats the previous instant `[FAILED] $0.00 0.1s` flake. Wired into `cli.ts` before the GSD/InitRunner construction. 2. Runtime-aware `resolveModel()` in `session-runner.ts`. The profile -> id map (`balanced -> claude-sonnet-4-6`, etc.) was applied unconditionally, so even with `runtime: codex` and `resolve_model_ids: omit` the SDK forced a Claude id into `query()`. Now the profile map only fires when the runtime is Claude and the explicit `resolve_model_ids: "omit"` knob short-circuits to undefined, mirroring `query/config-query.ts`. Tests (vitest, sdk/src): - runtime-gate.test.ts (8 cases): claude / unset / unknown pass; codex, gemini, opencode throw; GSD_RUNTIME wins over config.runtime; error message references #2832 and the slash-command workaround. - session-runner.test.ts (4 new cases under "resolveModel runtime awareness (#2832)"): codex runtime + balanced profile -> no model injected; resolve_model_ids: omit -> no model; claude runtime still resolves to claude-sonnet-4-6 (no regression); explicit options.model wins on any runtime. * fix(#2832): address CR — env-precedence in resolveModel + accurate source attribution Two CodeRabbit findings on PR #2844: 1. session-runner.ts:resolveModel() (Major) — read runtime via detectRuntime() so GSD_RUNTIME env precedence is honored. Without this, a Codex run with a Claude-shaped config still fell into the Claude-only profile-id branch. 2. runtime-gate.ts:assertRuntimeSupportsAutoMode() (Minor) — when GSD_RUNTIME holds an unsupported value, detectRuntime() falls through to config but the source label still reported the discarded env value. Fix: validate env against SUPPORTED_RUNTIMES before attributing the source. Tests added for both: env-precedence in session-runner, source attribution in runtime-gate. 17/17 pass. |
||
|
|
91194cdbff |
chore(#2828): add canary release workflow (#2830)
* chore(#2828): add canary release workflow (dev builds on push to main) Publishes get-shit-done-cc@canary and @gsd-build/sdk@canary on every push to main. Version format: {base}-canary.{N} where base strips any pre-release suffix from package.json (1.39.0-rc.4 → 1.39.0-canary.1). Sequential canary number is auto-detected from existing git tags so reruns never collide. Concurrency group cancels stale in-flight canary runs when commits land quickly. Mirrors the structure and steps of release.yml: same checkout pins, Node 24, npm-publish environment, build:sdk, tarball verification, dry-run publish gate, and publish verification with sleep 10. Closes #2828 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2828): address CodeRabbit review findings on canary.yml - cancel-in-progress: false — was true, allowing a newer push to cancel a run mid-publish (after tag push but before SDK publish), leaving a partial release state that's unrecoverable since npm versions are immutable - Guard tag/publish/verify steps with github.ref == 'refs/heads/main' so a manual workflow_dispatch from a feature branch (dry_run defaults false) cannot accidentally publish unmerged code under the shared canary dist-tag - Replace fixed sleep 10 with exponential backoff retry loop (delays: 5 10 20 30 45s); fixed sleep is flaky against normal npm CDN replication lag and a false failure forces a new canary number since the tag already exists Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(plan-phase): expose --mvp flag in command frontmatter Adds --mvp to argument-hint and Flags doc. Workflow handler in next commit. * chore(#2828): remove push:main trigger from canary workflow Submission rate to main is too high to auto-publish a canary on every merge. Restrict the workflow to manual workflow_dispatch only. Closes #2828 --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
74b81379cf |
fix(#2836): audit-open quick SUMMARY filename + UAT terminal-status drift (#2847)
* fix(#2836): audit-open quick SUMMARY filename + UAT terminal-status drift Fixes two convention drifts in bin/lib/audit.cjs that produced false-positive "open" items at every milestone close: 1. scanQuickTasks: looked for bare `SUMMARY.md`, but workflows/quick.md mandates `${quick_id}-SUMMARY.md`. Now matches either filename so quick tasks created via the documented workflow are recognized. 2. scanUatGaps: only treated `status: complete` as terminal, but workflows/execute-phase.md uses `status: resolved` post-gap-closure. Now treats both `complete` and `resolved` as terminal, with `result: all_pass` as a fallback when status is absent. Also reconciles workflows/help.md one-liner that referenced bare `SUMMARY.md` so docs match the authoritative quick.md workflow. Adds tests/bug-2836-audit-open-summary-uat-drift.test.cjs with 6 structural regression tests covering both fixes plus no-regression cases. * refactor(#2836): hoist TERMINAL_UAT_STATUSES outside scanUatGaps loop Address CodeRabbit nitpick: the Set was being recreated on each UAT file iteration. Hoist to module scope so it is constructed once. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
12b6ba4e34 |
fix(#2829): gsd-sdk resolvable in local-mode installs (#2848)
* fix(#2829): gsd-sdk resolvable in local-mode installs Local-mode installs previously short-circuited installSdkIfNeeded() the moment opts.isLocal was true, leaving every `gsd-sdk query …` call site unable to resolve the binary on PATH. The published tarball ships sdk/dist/cli.js and bin/gsd-sdk.js regardless of mode, and the shim resolves the CLI relative to its own __dirname — so the same self-link strategy that powers npx-cache global installs (#2775) also works for local installs. We now run the shared self-link path whenever the dist is present, and only fall back to a non-fatal warning + early return when the dist is genuinely missing (preserving the #2678 contract). * test(#2829): correct precondition comment about ~/.local/bin Address CodeRabbit feedback — the test does not create ~/.local/bin, so reword the inline precondition to "any HOME bin candidate remains off-PATH" to match what the test actually sets up. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
f4412349f0 |
fix(#2835): align CR-INTEGRATION tests with hyphen-form skill names (#2843)
* fix(#2835): align CR-INTEGRATION tests with hyphen namespace PR #2819 changed autonomous.md skill invocations from `gsd:code-review` (colon) to `gsd-code-review` (hyphen). Tests still asserted the legacy colon form against the user-installed plugin dir (which lags the repo). Switch tests to: - Read autonomous.md from the canonical repo WORKFLOWS_DIR (not the plugin install location, which can be stale) - Parse `Skill(skill="...")` invocations structurally instead of substring matching, and assert the canonical hyphen form is present while explicitly rejecting the legacy colon form. Closes #2835 * test(#2835): parse Skill() invocations structurally in CR-INTEGRATION tests Replace raw-text regex/.includes() assertions with a proper parser that walks autonomous.md, skips escaped string contexts, and yields [{ skill, args }] objects. The three CR-INTEGRATION tests now assert against parsed fields and tokenized args (not substring matches), addressing CodeRabbit feedback on PR #2843. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
a7f83ee663 |
fix(#2831): expand HOME in OpenCode @file references on all platforms (#2842)
* fix(#2831): expand HOME in OpenCode skill/template paths OpenCode does not shell-expand $HOME in @file references on any platform — the literal `@$HOME/...` path is resolved relative to the config command/ dir, producing `command/$HOME/...` (file not found). The previous fix for #2376 only guarded Windows; extend to all platforms. Closes #2831 * test(#2831): assert behavior via exported computePathPrefix, not source grep Addresses CodeRabbit review on PR #2842: - Extracts pathPrefix logic into a named, test-exported computePathPrefix helper in bin/install.js (no behavior change at the call site). - Rewrites bug-2376 and bug-2831 regression tests to call the exported function directly instead of regex-matching install.js source text, per the repo's no-source-grep testing standard. - Wraps temp-dir test setup in try/finally so cleanup runs on assertion failures (no leaked tmp dirs). |
||
|
|
7fae804296 |
fix(#2839): transactional cleanup tail for /gsd-code-review-fix (#2846)
* fix(#2839): make /gsd-code-review-fix cleanup transactional Cleanup tail in agents/gsd-code-fixer.md previously did 'git worktree remove' without any recovery marker. If the process was killed between fix commits and worktree removal, the orphan worktree + branch survived with no resume path — the next run had no way to discover or finish the cleanup. Introduce a recovery sentinel at ${phase_dir}/.review-fix-recovery-pending.json with strict ordering: - Sentinel written AFTER 'git worktree add' succeeds (never points at a worktree that does not exist). - Sentinel removed ONLY AFTER 'git worktree remove' returns successfully (interruption between commits and removal leaves a sentinel behind). - New runs detect a pre-existing sentinel, force-remove the recorded orphan worktree, then drop the stale sentinel before continuing — making the agent self-healing after a crash. Closes #2839 * fix(#2839): harden sentinel JSON parse and scope ordering assertion Address CodeRabbit review feedback on PR #2846: - agents/gsd-code-fixer.md: Guard the recovery-sentinel JSON parse with try/catch so a corrupted/truncated sentinel (a realistic crash artifact) emits a warning and yields an empty prior_wt instead of aborting setup. This preserves the self-healing recovery path even when the sentinel itself is the casualty of the original crash. - tests/bug-2839-review-fix-transactional-cleanup.test.cjs: Scope the cleanup-ordering assertion to the cleanup-tail section of the setup_worktree step rather than first global occurrences. Previously the assertion could pass on pre-recovery references even if cleanup-tail ordering regressed. The regex also now accepts the shell-variable form (\`rm -f \"\$sentinel\"\`) used in the cleanup tail. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
c3a42d66f9 | Revert "feat(install): add Hermes Agent runtime support" (#2849) | ||
|
|
0acf1de88c |
Merge pull request #2845 from teknium1/feat/hermes-runtime
feat(install): add Hermes Agent runtime support |
||
|
|
5a636bc90a |
feat(install): add Hermes Agent runtime support (#2841)
Adds Hermes Agent as a supported installation target. Users can run
\`npx get-shit-done-cc --hermes\` to install all 86 GSD commands as
skills under \`~/.hermes/skills/gsd-*/SKILL.md\`, following the same
open skill standard as Claude Code 2.1.88+, Qwen Code, Antigravity,
Trae, Augment, and Codebuddy.
Hermes Agent is an open-source AI agent framework by Nous Research
(NousResearch/hermes-agent, MIT). Its skill loader accepts the Claude
skill format as-is: frontmatter parsed with PyYAML SafeLoader (unknown
keys like \`allowed-tools\` / \`argument-hint\` ignored), body XML tags
(\`<objective>\`, \`<execution_context>\`, \`<process>\`) passed directly
to the model. Compatibility proven end-to-end with all 86 GSD skills
loading cleanly, \`skill_view()\` returning full bodies, and
\`build_skills_system_prompt()\` emitting them into the agent system
prompt — zero Hermes code changes required.
Changes:
- \`bin/install.js\`: --hermes flag, getDirName/getGlobalDir/getConfigDirFromHome
support, HERMES_HOME env var (native to Hermes — used for profile
mode / Docker deploys), install/uninstall pipelines, interactive
picker option 10 (alphabetical: between Gemini and Kilo), .hermes
path replacements in copyCommandsAsClaudeSkills and
copyWithPathReplacement, legacy commands/gsd cleanup, CLAUDE.md ->
HERMES.md and "Claude Code" -> "Hermes Agent" content rewrites in
skills/agents/hooks, runtime-appropriate finish message.
- \`get-shit-done/bin/lib/core.cjs\`: add hermes to KNOWN_RUNTIMES;
add RUNTIME_PROFILE_MAP.hermes with OpenRouter-slug defaults
(Hermes is provider-agnostic; these defaults resolve across
OpenRouter, native Anthropic, and Copilot via Hermes' aggregator-
aware resolver, and are overridable per-tier via
model_profile_overrides.hermes.{opus,sonnet,haiku}).
- \`README.md\`: Hermes Agent in tagline, runtime list, verification
command, install/uninstall examples, \`--hermes\` flag reference.
- \`tests/hermes-install.test.cjs\`: new, 14 tests covering directory
mapping, HERMES_HOME env var precedence, install/uninstall
lifecycle, user-skill preservation, engine cleanup.
- \`tests/hermes-skills-migration.test.cjs\`: new, 11 tests covering
frontmatter conversion, path replacement (~/.claude/ ->
\$HERMES_HOME/skills/), CLAUDE.md -> HERMES.md, "Claude Code" ->
"Hermes Agent", stale skill cleanup, SKILL.md format validation.
- \`tests/multi-runtime-select.test.cjs\`: updated for new option
numbering (hermes=10, kilo=11, opencode=12, qwen=13, trae=14,
windsurf=15, all=16).
- \`tests/kilo-install.test.cjs\`: updated assertions for Kilo having
moved from option 10 to option 11.
Closes #2841
Implementation notes:
- Zero custom code paths: Hermes reuses copyCommandsAsClaudeSkills()
identical to Qwen Code / Antigravity pattern.
- Path replacement: ~/.claude/, \$HOME/.claude/, ./.claude/ ->
.hermes equivalents in skill/agent/hook content.
- Config precedence: --config-dir > HERMES_HOME > ~/.hermes (matches
how Hermes itself resolves its home directory).
- Legacy cleanup: removes commands/gsd/ if present from a prior
install, preserving dev-preferences.md (same as Qwen).
- No external dependencies added.
Testing: 5841 / 5841 tests pass (0 failures, 0 regressions)
- 14 new tests in hermes-install.test.cjs
- 11 new tests in hermes-skills-migration.test.cjs
- multi-runtime-select.test.cjs renumbered + 1 new test (single choice for hermes)
|
||
|
|
eeaf9c556f |
fix(#2787): track fenced code blocks in extractCurrentMilestone (#2812)
* fix(#2787): track fenced code blocks in extractCurrentMilestone The milestone-end search used a multiline regex against the raw restContent string. Lines inside fenced code blocks (``` or ~~~) that matched the milestone-heading pattern (e.g. `# note v1.0`) prematurely set sectionEnd, hiding all phases after the block from roadmap analyze, roadmap get-phase, and every downstream command. Replace the regex match with a line-by-line scan that tracks fence state. Lines inside an open fence are skipped regardless of content. Adds three regression tests covering backtick fences, tilde fences, and the roadmap get-phase code path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2787): track fence delimiter instead of toggling bare boolean Replace the inFence boolean with fenceChar/fenceLen tracking so that indented fences (up to 3 leading spaces) and mixed-delimiter content (~~~ inside a backtick fence) are parsed correctly. A closing fence is only recognised when it uses the same character as the opening delimiter and has at least the same run length, matching the CommonMark spec. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2787): require fence-only closing line — reject info-string lines as closers A closing fence delimiter must contain only optional trailing whitespace. A line like \`\`\`js inside an open fence has an info string and must not close it. The previous regex /^\s{0,3}([`~]{3,})/ matched the opening of any such line, so the closing check could toggle fenceChar off on an info-string line and expose subsequent heading-like content to the milestone-end detector. Fix: capture the trailing portion of every fence-candidate line and only clear fenceChar when trailing matches /^\s*$/ (per CommonMark §4.5). Adds a regression test covering the ```text / ```js nesting scenario. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
9e58c45ea1 |
fix(#2791): GSD_WORKSTREAM env var respected by gsd-sdk query + gsd-tools bin alias (#2821)
* fix(#2791): GSD_WORKSTREAM env var respected by gsd-sdk query + gsd-tools bin alias Two fixes for gsd-sdk binary issues: **Issue 1 — Binary name collision:** Both `get-shit-done-cc` and `@gsd-build/sdk` declare `bin: { "gsd-sdk": ... }`. Added `"gsd-tools": "bin/gsd-sdk.js"` to `package.json` bin so users with the collision can invoke `gsd-tools query <cmd>` as a conflict-free alternative. **Issue 2 — Query registry not workstream-aware:** `gsd-sdk query` commands ignored `GSD_WORKSTREAM` env var, always reading from the root `.planning/` even when a workstream was active. `gsd-tools.cjs` reads `GSD_WORKSTREAM` via `planningDir()`, so all ~35 `gsd-sdk query` call sites in workflow files were broken in workstream-scoped projects. Fix: added env var fallback in `sdk/src/cli.ts` — when `--ws` is not provided, `GSD_WORKSTREAM` is used (with name validation; invalid values are silently ignored, matching CJS behaviour). Regression test: `tests/bug-2791-sdk-workstream-env.test.cjs` Closes #2791 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2791): address CodeRabbit — precedence test, invalid env fallback assertion, bash fence --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
897cff6051 |
fix(#2805): find-phase returns null phase_dir for archived phases (not archive path) (#2818)
* fix(#2805): add regression test — archived phase fallback already fixed in source getPhaseInfoWithFallback already discards archived disk matches when the current ROADMAP lists the phase (line 133: phaseInfo?.archived && roadmapPhase?.found). The regression test confirms this behavior and prevents the bug from being reintroduced by future refactors. Regression test: tests/bug-2805-archived-phase-fallback.test.cjs (3 tests: phase_dir null, phase_found true, phase_name from ROADMAP) * fix(#2805): address CodeRabbit — exact phase_name assertion, bash fence |
||
|
|
a4e15d5616 |
fix(#2788): audit-uat reads human_verification items from frontmatter (#2814)
* fix(#2788): audit-uat reads frontmatter human_verification array parseVerificationItems only searched the body for a '## Human Verification' section. gsd-verifier writes items to the frontmatter human_verification: YAML array, so audit-uat returned total_items: 0 for all such files. Two fixes: 1. Read frontmatter human_verification: array first (via extractFrontmatter); return those items if present (primary path for gsd-verifier output). 2. Relax the body-section heading regex to accept underscore separators and parenthetical suffixes (e.g. '## human_verification (action required)'). Regression test: tests/bug-2788-audit-uat-frontmatter.test.cjs * fix(#2788): address CodeRabbit — trim whitespace entries, support hyphenated headings, bash fence |
||
|
|
eddb2a205b |
fix(#2801): add ingest-docs handler to gsd-tools init dispatch (#2820)
* fix(#2801): add ingest-docs handler to gsd-tools init dispatch The `/gsd-ingest-docs` workflow was broken because `workflows/ingest-docs.md` called `gsd-sdk query init.ingest-docs` but the installed binary is `gsd-tools`, and `gsd-tools init` had no `ingest-docs` case in its dispatch switch. - Added `cmdInitIngestDocs` function to `init.cjs` and exported it; returns `project_exists`, `planning_exists`, `has_git`, `project_path`, `commit_docs` - Added `case 'ingest-docs'` to the `init` switch in `gsd-tools.cjs` - Updated `workflows/ingest-docs.md` to call `gsd-tools init ingest-docs` (line 55) and `gsd-tools commit` (line 292) instead of `gsd-sdk query ...` - Regression test: `tests/bug-2801-ingest-docs-handler.test.cjs` Closes #2801 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2801): address CodeRabbit — commit_docs assertion, broader gsd-sdk detection, bash fence --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
5fe1f00a0d |
fix(#2808): SKILL.md files use hyphen name form (gsd-cmd not gsd:cmd) (#2819)
* fix(#2808): SKILL.md name uses hyphen form for Claude Code autocomplete skillFrontmatterName() was converting gsd-<cmd> to gsd:<cmd> (colon) so installed SKILL.md files had name: gsd:add-phase etc. Claude Code surfaces this name in autocomplete, showing the deprecated colon form to users even though the hyphen form is canonical everywhere else. Root cause: the colon form was needed because workflows called Skill(skill="gsd:<cmd>"). All 4 remaining colon-form Skill() calls in autonomous.md and execute-phase.md are updated to hyphen form. skillFrontmatterName() now returns the hyphen dir name unchanged. Updated 4 existing tests that asserted colon form. Regression test: tests/bug-2808-skill-hyphen-name.test.cjs * fix(#2808): address CodeRabbit — bash/text fences, structured test assertions, fail-loud on errors |
||
|
|
fa78692167 |
fix(#2796): roadmap-update-plan-progress accepts --phase flag form (#2815)
* fix(#2796): roadmap update-plan-progress accepts --phase flag form roadmap-update-plan-progress used positional-only arg parsing: args[0]. When execute-phase.md:228 calls it with --phase <N>, args[0] was the literal string "--phase", which findPhase received as the phase number. findPhase returned found:false, causing updated:false with no write. ROADMAP.md plan checkboxes silently never advanced. Fix: check for --phase <value> first; fall back to the first non-flag positional argument for backward-compatible direct calls. Regression test: tests/bug-2796-arg-parsing-regression.test.cjs * fix(#2796): address CodeRabbit — guard --phase against flag-like values, bash fence |
||
|
|
b959b1844f |
fix(#2803): config-get supports --default <value> fallback for missing keys (#2817)
* fix(#2803): honor --default flag in SDK config-get handler The gsd-sdk query config-get handler ignored the --default <value> flag. Missing keys always threw 'Key not found' (exit 1), making 8 workflow sites that rely on config-get --default fall through to error paths. The CJS path (gsd-tools.cjs) honored --default since #1893; this ports that behavior to the SDK configGet handler. Regression test: tests/bug-2803-config-get-default-flag.test.cjs * fix(#2803): address CodeRabbit — require --default value, keep missing config.json as error, bash fence |
||
|
|
7616309a32 |
fix(#2798): add context_window to VALID_CONFIG_KEYS allowlist (#2816)
* fix(#2798): add regression test — context_window key already in VALID_CONFIG_KEYS context_window was already added to both VALID_CONFIG_KEYS allowlists (CJS and SDK) in a prior fix. The regression test confirms it stays there and that config-set context_window succeeds end-to-end. Regression test: tests/bug-2798-context-window-config-key.test.cjs * fix(#2798): address CodeRabbit — add bash language to release notes fence |
||
|
|
d46efb4790 |
fix(#2784): clear shared ~/.cache/gsd/ update-check cache in update workflow (#2813)
* fix(#2784): clear shared ~/.cache/gsd/ cache in update workflow The SessionStart hook (hooks/gsd-check-update.js) writes update-check results to $HOME/.cache/gsd/gsd-update-check.json (shared, tool-agnostic). The update.md run_update step only cleared per-runtime paths like ~/.claude/cache/gsd-update-check.json, so the statusline kept showing the stale upgrade indicator after a successful update. Fix: add rm -f "$HOME/.cache/gsd/gsd-update-check.json" to the cache-clear block in the run_update step. Regression test: tests/bug-2784-update-cache-clear-path.test.cjs * fix(#2784): address CodeRabbit review — four edge-cases count, bash fence, structured test assertions |
||
|
|
055b43054f |
fix(#2794): embed model_profile_overrides.opencode.<tier> into generated OpenCode agents (#2822)
* docs: add CHANGELOG entry and rc.5 release notes for #2809 Codex hooks migrator fixes Covers the five correctness findings addressed in the round-5 CR of PR #2809: parseHooksBody key parser (hyphenated/quoted keys), buildNestedBlock empty-handler guard, legacyMapSections segment-count filter, quoted-dot regression test, and strengthened command path assertion. Closes #2810 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2794): embed model_profile_overrides.opencode.<tier> into generated OpenCode agents OpenCode agent files were missing `model:` frontmatter when the user configured tier-based model resolution via `model_profile_overrides.opencode.*`. Only explicit `model_overrides[agent]` was consulted; the runtime profile resolver (used by the Codex path since #2517) was never called for OpenCode agents. Added a tier-resolver fallback in the OpenCode agent conversion block in `bin/install.js`. Precedence (matching Codex behavior): model_overrides[agent] > model_profile_overrides.opencode.<tier> > omit Regression test: `tests/bug-2794-opencode-model-profile-overrides.test.cjs` Closes #2794 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |