Commit Graph

3440 Commits

Author SHA1 Message Date
Colin Johnson
bf8813b7a0 Merge branch 'next' into kimi-runtime-support 2026-06-08 23:04:37 -04:00
Tom Boucher
d12809985e fix(#905): preserve STATE.md frontmatter scalars in syncStateFrontmatter (#907)
syncStateFrontmatter was silently dropping current_phase, current_phase_name,
current_plan, and progress when body annotations were absent (e.g. after an
agent or tool rewrote the body). These scalars can only be derived from body
annotations — when absent, buildStateFrontmatter returns nothing for those
keys. Added existingFm fallbacks mirroring the same pattern already applied in
cmdStateJson, so every writeStateMd call preserves the existing values instead
of stripping them. Also extended cmdStateJson with the same fallbacks for the
three non-progress scalars.

Adds regression test (7 cases) + lint-test-file-count allowlist entry.

Closes #905

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 22:52:43 -04:00
Tom Boucher
808df9110c fix(#892): parse checklist-style roadmap phases in validate/verify (#908)
buildRoadmapPhaseVariants() only matched heading-style phases (## Phase N:),
silently skipping the supported checklist format (- [x] **Phase N: name**).
This caused W007 false-positives for every on-disk phase dir when the project
uses a checklist ROADMAP. Fix adds a second regex pass (mirroring the existing
buildNotStartedPhaseVariants() approach). Also refactors the duplicate
inline heading-only regex in cmdValidateConsistency() to delegate to
buildRoadmapPhaseVariants() (DRY). Regression test in
tests/bug-892-validate-checklist-roadmap-phases.test.cjs covers both paths.

Closes #892

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 22:52:19 -04:00
Tom Boucher
6edc39c4eb chore(#893): remove dead loadConfig from configuration.cts (#912)
`loadConfig` in configuration.cts was superseded by config-loader.cts
(ADR-857 phase 2e, #885). Exhaustive grep confirms no caller imports
loadConfig from configuration.cjs — all live callers use config-loader.cjs
or the core.cjs back-compat re-export. configuration.cts now provides only
the pure normalization and defaults primitives (normalizeLegacyKeys,
mergeDefaults, migrateOnDisk, CONFIG_DEFAULTS) that config-loader.cts
depends on. Updated CONTEXT.md and docs/INVENTORY.md to reflect the
narrowed module surface.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 22:51:43 -04:00
Tom Boucher
a90c654745 fix(#891): probe non-Claude runtime homes in gsd-tools launcher shim detection (#911)
- Updated `gsd-core/workflows/_runtime-launcher.snippet.sh` with 15 new
  `elif` arms covering Hermes, Cursor, Codex, Gemini, Copilot, Windsurf,
  Augment, Trae, Qwen, CodeBuddy, Cline, Grok, Antigravity, OpenCode, and
  Kilo (respecting each runtime's env-var override with a `$HOME`-relative
  default).
- Re-ran `scripts/sync-runtime-launcher.cjs` to propagate the expanded
  snippet into all `gsd-core/workflows/*.md` files (~70 files).
- Manually applied the same snippet update to `commands/gsd/import.md`
  (1 occurrence) and `commands/gsd/graphify.md` (5 occurrences) — these
  are not covered by the sync script.
- Updated `tests/workflow-size-budget.test.cjs` budgets (XL/LARGE/DEFAULT
  + discuss-phase target) to account for the ~3 KB snippet expansion.
- Added regression test `tests/bug-891-non-claude-runtime-home-fallback.test.cjs`
  (6 tests: structural probe presence, ordering, behavioral HERMES_HOME
  env-var + default-path stubs, resolution order, and workflow propagation).
- Added `.changeset/891-launcher-non-claude-runtime-homes.md` (Fixed).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 22:51:40 -04:00
Tom Boucher
cb2cda1865 fix(#904): normalize phase number in init.execute-phase branch_name (#909)
Wrap the {phase} substitution in normalizePhaseName() at both fix sites:
- src/init.cts  — cmdInitExecutePhase branch_name output
- src/commands.cts — cmdCommit pre-execution branch derivation

When project_code is set (e.g. "CK"), extractPhaseToken returns the
full prefixed token "CK-01" as phase_number.  Without normalization the
generated branch was "gsd/phase-CK-01-foundation"; after this fix it is
"gsd/phase-01-foundation", matching the documented {phase} contract
(padded numeric only).

Adds a regression test in tests/init.test.cjs.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 22:51:38 -04:00
Tom Boucher
48cc27bd84 feat(#903): generate Loop Host Contract from workflow markers (ADR-857 phase 3a-impl-2) (#906)
Replace the inline LOOP_HOST_CONTRACT constant in the Capability Registry
generator with a generated-from-workflows contract (ADR-894 §3). The contract
is now derived from inert `<!-- gsd:loop-host ... -->` marker blocks in the five
step workflows, emitted as the committed gsd-core/bin/lib/loop-host-contract.cjs,
and required by gen-capability-registry.cjs — one source of truth, no drift.

Drift guards in gen-loop-host-contract.cjs: per-step point ownership (each step
must declare exactly its canonical loop points), multiple-block + duplicate-key
hard errors, and a word-boundary agent-role cross-check. Contract content is
byte-identical to the former constant; registry-only, nothing wired into the
live loop.

Closes #903

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 22:16:58 -04:00
Tom Boucher
ad754ca6cd feat(#896): Capability Registry generator + UI pilot (ADR-857 phase 3a-impl) (#902)
* feat(#896): Capability Registry generator + UI pilot (ADR-857 phase 3a-impl)

First phase-3 code: the Capability Registry generation pipeline, built against
the ADR-894 contract and NOT wired into the live loop (registry-only, per the
staged-cutover design).

- capabilities/ui/capability.json — the UI pilot (ADR-894 worked example): 2
  skills, 2 agents, 3 config keys, 2 steps + 1 gate, with `when` activation.
- scripts/gen-capability-registry.cjs — --write/--check generator. Hand-rolled
  schema validation (envelope + role-typed feature/runtime bodies + typed
  steps/contributions/gates + when + gate-check variants); cross-capability
  invariants (single ownership; requires exist+acyclic+tier-monotone; config-key
  ownership exclusive, collision-vs-central as a pending-migration warning);
  hooks validated against an inline LOOP_HOST_CONTRACT (3a-impl-2 swaps its
  source to the generated-from-workflows contract); GLOBAL point-ordered
  consumes-satisfiability; materialized byLoopPoint ordering (produces/consumes
  topo-sort); emits gsd-core/bin/lib/capability-registry.cjs (role-partitioned
  indexes + requiresClosure). Prototype-pollution guards (Object.create(null) +
  inline literal key checks) + fragment.path traversal guard.
- gsd-core/bin/lib/capability-registry.cjs — committed generated artifact
  (mirrors package-identity.cjs: script-generated, tracked, linted, regenerated
  on build, drift-tested), wired via the new `gen:capability-registry` build step.
- tests/capability-registry.test.cjs — 72 tests: schema + invariant + hook +
  ordering + adversarial (path-traversal, proto-pollution, runtime body,
  self-consume, cycles, collisions) + committed-file staleness guard.

New-CLI-module checklist (INVENTORY 97->98, MANIFEST, ARCHITECTURE), CONTEXT.md
"Capability Registry" un-[Planned]'d. Nothing wired into install/surface/loop.

Gates: lint, code-review (4 bugs fixed), security-review (path-traversal +
prototype-pollution fixed), codex adversarial-review ×3 (8+ findings fixed,
confirmed sound), clean-build docker 13190 pass / 0 fail.

Closes #896

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(#896): CRLF-agnostic --check for capability-registry staleness (Windows)

The committed capability-registry.cjs staleness guard failed on Windows CI only:
git checks out the committed .cjs as CRLF (autocrlf, no .gitattributes) while the
generator emits LF, so the byte-for-byte --check comparison mismatched. Normalize
line endings on both sides of the --check comparison (no .gitattributes change,
no change to the LF the generator writes). Adds a regression test simulating the
Windows CRLF checkout.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 21:15:41 -04:00
Tom Boucher
46d7cdffae chore(#899): auto-close fork PRs without a pre-approved issue (#901) 2026-06-08 20:45:59 -04:00
Tom Boucher
197d6bffe2 docs(#894): ADR-894 Capability declaration format + registry generation (#895)
* docs(#894): ADR-894 Capability declaration format + registry generation

ADR-857 rollout phase 3a (design-only). Resolve ADR-857's deferred open
question — the on-disk Capability declaration format — as a reviewable design
ADR before any generator code.

Specifies: the capabilities/<id>/capability.json folder layout (migration-staged
ownership — declarations reference existing stems until the phase-6 move); the
capability.json schema for role:feature (skills/agents/hooks/federated config/
loopHooks) and role:runtime (the six closed projection-primitive axes); the 12
named Loop Extension Points; the gen-capability-registry.cjs generator design
(validation + cross-capability invariants + --write/--check drift gate, mirroring
gen-inventory-manifest); the generated capability-registry.cjs shape (by-id /
by-skill / by-loop-point indexes + requires-closure); and a full worked example
(the UI capability: ui-phase + ui-review + agents + config + two loop hooks).

No code — design artifact only; the generator build, federated config loader
(3b), and loop seam (3c) implement against this contract.

Closes #894

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(#894): amend ADR-894 with grilled capability declaration format

Stress-tested the declaration format before merge; the format changed
materially. Amendments:
- loopHooks[] -> three typed arrays (steps/contributions/gates), each with its
  own shape (step: ref+produces/consumes; contribution: fragment+into agent-role;
  gate: check+blocking).
- Add the Loop Host Contract (§3): each step publishes its points, agent roles,
  and core artifacts so the generator validates hooks against reality, not
  trusted strings.
- requires = capability ids only (host implicit); add tier-monotone invariant;
  drop the requires:["plan"] error from the example.
- Config federation = atomic move: a migrated key leaves the central schema in
  the same PR; presence in both is a collision (invariant stays).
- One registry, role-partitioned indexes (feature indexes vs runtimes index).
- Rework the UI worked example to the split-array shape (2 steps + 1 gate) +
  a contribution illustration.

Adds a "Grilling amendments" section recording the six changes.

* docs(#894): amend ADR-894 with round-2 grilling (operational reality)

Second design-grill round, folded in before merge:
- Loop Host Contract is GENERATED from structured workflow markers
  (<loop-point>/<agent-role>/<loop-artifact>) via gen-loop-host-contract.cjs —
  it can't drift from the real workflows.
- Hook activation `when`: cheap deterministic config-level gating evaluated by
  loop.render-hooks; deeper phase-context applicability self-gates inside the
  dispatched skill (no phase-context vocabulary to keep honest).
- `tier` is the source of install-profile + cluster membership; profiles and
  clusters are generated from tier + requires-closure (/gsd:surface operates on
  capabilities) — collapses ADR-857's multiple toggle systems.
- Gate `check` = query | declarative-predicate | agentVerdict; agentVerdict is
  forced advisory; only deterministic checks may block.
- byLoopPoint ordering is materialized in the registry; render-hooks filters the
  active set + renders. Same-capability hooks degrade gracefully when an entry
  step self-gates.
- Rollout: registry-only until atomic per-feature cutover (no double-execution
  with still-inlined workflow features).

Updates the Grilling amendments / Consequences / Alternatives / Open questions
sections; reworks the UI example with `when`.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 18:51:20 -04:00
Tom Boucher
185935379a refactor(#888): extract model+effort resolution into model-resolver.cts (#890)
ADR-857 rollout phase 2f — the FINAL core.cts decomposition. Move the model and
effort resolution cluster (resolveModelInternal, resolveModelPolicy,
resolveTierEntry, _resolveRuntimeTier, resolveModelForTier,
resolveGranularityInternal, assertValidGranularityOverride, resolveEffortInternal,
resolveFastModeInternal, resolveEffortForTier, nextEffort + VALID_GRANULARITIES/
VALID_EFFORTS/EFFORT_SET + interfaces) out of core.cts into a new leaf module
src/model-resolver.cts. core.cts re-exports the 13 public symbols (callers in
init/docs/commands unchanged; export= set byte-identical).

Cycle-free: model-resolver imports only leaves (config-loader for loadConfig,
configuration for defaults, model-profiles + model-catalog for the static
tables). Removed 6 now-unused imports from core (verified zero remaining
references, none re-exported).

This completes the god-module decomposition: core.cts 2271 -> 389 lines (~83%),
now a thin re-export spine over seven clean leaves (io, phase-id, roadmap-parser,
core-utils, phase-locator, config-loader, model-resolver).

New-CLI-module checklist done (.gitignore, eslint, INVENTORY 96->97 + row,
manifest, ARCHITECTURE, CONTEXT.md "Model Resolver Module"). Adds
tests/model-resolver.test.cjs (81 tests: behavioral + shim-identity + adversarial).

Gates: lint, code-review (export set byte-identical; import-removal verified),
security-review, codex adversarial-review (all 0 findings; verbatim move). Mac
4303 pass; clean-build docker 13117 pass, 0 fail.

Closes #888

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 17:26:40 -04:00
Viktorplus
f2dd125593 Merge branch 'next' into kimi-runtime-support 2026-06-08 22:11:07 +02:00
Tom Boucher
b6199460ed refactor(#887): consolidate duplicated CHILD_ROUTER test maps into shared helper (#889)
Three test files copy-pasted the concrete-skill->namespace-router CHILD_ROUTER
map verbatim, and two more duplicated an identical local parseRouterRequires
regex. Introduce tests/helpers/nested-layout.cjs that derives the child->router
map from the authoritative commands/gsd/ns-*.md requires: lists once, reusing
the production parseRequires (now exported from install-profiles) plus a
nestedSkillPath(skillsRoot, prefix, stem) helper. Refactor all five test files
to import from it.

Test-only + a single internal export addition; no production behavior change.

Closes #887

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 16:05:59 -04:00
Viktorplus
69b7bbd062 Merge branch 'next' into kimi-runtime-support 2026-06-08 22:00:34 +02:00
Tom Boucher
a74e71b049 refactor(#885): extract loadConfig cluster into config-loader.cts (#886)
ADR-857 rollout phase 2e — the largest core.cts extraction. Move the
configuration-loading subsystem (loadConfig + _getConfigDefault/
_getNestedConfigDefault/CONFIG_DEFAULTS/_deepMergeConfig, isGitIgnored +
_gitIgnoredCache, _warnUnknownProfileOverrides + RUNTIME_OVERRIDE_TIERS + the
dedup Sets, _resetRuntimeWarningCacheForTests) out of core.cts into a new leaf
module src/config-loader.cts. core.cts re-exports the public surface
(loadConfig, isGitIgnored, CONFIG_DEFAULTS, RUNTIME_OVERRIDE_TIERS,
_resetRuntimeWarningCacheForTests); 12+ callers unchanged.

Cycle-free: config-loader imports only leaves (configuration, config-schema,
planning-workspace, shell-command-projection, core-utils, model-catalog). All
core-internal helpers loadConfig touches moved with it to avoid a cycle. core
keeps CANONICAL_CONFIG_DEFAULTS for its model-resolver functions, which now
resolve loadConfig via the binding — this unblocks the final model-resolver
extraction (2f). core.cts: 1275 -> 792 lines.

Repointed tests/config-field-docs.test.cjs (a docs-parity source check) to read
the CONFIG_DEFAULTS literal from its new home (config-loader.cjs). New-CLI-module
checklist done (.gitignore, eslint, INVENTORY 95->96 + row, manifest,
ARCHITECTURE, CONTEXT.md "Config Loader Module"). Adds tests/config-loader.test.cjs
(27 tests: behavioral + shim-identity + adversarial config fixtures).

Gates: lint, code-review, security-review (prototype-pollution guard confirmed
intact), codex adversarial-review (0 findings; byte-identical move). Mac 4115
pass; clean-build docker: full-suite hit the local mirror's known incremental-tsc
non-determinism on an unrelated re-exported symbol (findPhaseInternal, from
already-merged 2d), but a clean targeted rebuild of the affected file passed
163/0 — CI's clean full matrix is the authoritative gate.

Closes #885

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 15:36:38 -04:00
Viktorplus
8050516094 Merge remote-tracking branch 'origin/next' into kimi-runtime-support
# Conflicts:
#	docs/ARCHITECTURE.md
2026-06-08 21:27:05 +02:00
Tom Boucher
0a11d361ca feat(#69): nest concrete skills under namespace routers at install (#883)
Emit the 6 gsd-ns-* routers as the only top-level skill bundles and nest
the ~61 concrete skills under <router>/skills/<name>/SKILL.md on runtimes
with confirmed non-recursive skill loaders (claude global, cline, qwen,
hermes, augment, trae, antigravity). Router bodies rewrite their routing
tables from Skill-tool dispatch to a Read skills/<name>/SKILL.md pattern.
Recursive/unconfirmed loaders (cursor, codex, copilot, windsurf, codebuddy,
opencode, kilo) keep the flat layout. Completes the v1.40 namespace
architecture (#2792) so the eager skill listing drops to ~6 entries.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 15:09:34 -04:00
Tom Boucher
dd81e3d120 refactor(#881): extract phase-locator fs-search into phase-locator.cts (#882)
ADR-857 rollout phase 2d. Move the phase-directory search/location functions
(searchPhaseInDir, findPhaseInternal, getArchivedPhaseDirs) + their interfaces
(PhaseSearchResult, ArchivedPhaseDir) out of core.cts into a new module
src/phase-locator.cts. core.cts re-exports all three (callers unchanged).

Cycle-free: phase-locator depends only on leaves (phase-id for token/name
matching, core-utils for fs-scan/path helpers, planning-workspace for
planningDir) — unblocked by the core-utils leaf (2c). This completes the
phase-search split: parsing in phase-id (2a), fs-search in phase-locator.

New-CLI-module checklist done (.gitignore, eslint, INVENTORY 94->95 + row,
manifest, ARCHITECTURE, CONTEXT.md "Phase Locator Module"). Adds
tests/phase-locator.test.cjs (37 tests: behavioral + shim-identity +
adversarial phase-dir fixtures).

Gates: lint, code-review, security-review, codex adversarial-review (0
findings; verbatim move checksum-verified). Mac 4078 pass; clean-build docker
12972 pass, 0 fail.

Closes #881

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 14:38:26 -04:00
Tom Boucher
b5a02da106 fix(#875): make getMilestonePhaseFilter fence-aware (#880)
Closes #875
2026-06-08 14:04:47 -04:00
Tom Boucher
a7b288b9df fix(#866): route profile-pipeline temp output under reaped root + fixture teardown (#879)
Closes #866
2026-06-08 14:04:26 -04:00
Viktorplus
87b7ab2c9f Merge branch 'next' into kimi-runtime-support 2026-06-08 20:02:57 +02:00
Viktorplus
69f6efebf1 docs(#743): address Kimi review — runtime count + support boundary
Address remaining review feedback on PR #743:

- Bump supported-runtime count 15 -> 16 across user-facing READMEs
  (docs/README.md + ja-JP/zh-CN translations) now that Kimi is added.
- Add an explicit support-boundary note in the Kimi CLI install section:
  the kimi --agent-file custom-agent contract targets legacy/Python
  kimi-cli; newer npm Kimi Code (@moonshot-ai/kimi-code) rejects
  --agent-file and consumes the same /skill:gsd-* skills via --skills-dir.
- Mirror the boundary in the changeset so release notes are unambiguous.

Historical ADR/changeset/INVENTORY references to '15 runtimes' are left
intact as point-in-time records.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 19:47:41 +02:00
Tom Boucher
282f145745 refactor(#877): extract shared low-level utilities into core-utils.cts (#878)
ADR-857 rollout phase 2c. Move 11 shared low-level utilities out of core.cts
into a new leaf module src/core-utils.cts: POSIX path normalization (toPosixPath),
filesystem scanning (detectSubRepos, readSubdirectories, getPhaseFileStats,
pathExistsInternal), and small pure helpers (generateSlugInternal,
extractOneLinerFromBody, filterPlanFiles, filterSummaryFiles, timeAgo, and the
private extractCanonicalPlanId). core.cts re-exports the 10 public ones
(callers unchanged); extractCanonicalPlanId stays private (exported from the
leaf for core's fs-search functions).

Cycle-free: core-utils depends only on Node built-ins + already-leafed modules
(phase-id for comparePhaseNum, planning-workspace for findContextMdIn). This is
the shared leaf that unblocks the phase-locator fs-search extraction (2d) —
searchPhaseInDir/findPhaseInternal/getArchivedPhaseDirs can now take their
utilities from a leaf instead of from core.

New-CLI-module checklist done (.gitignore, eslint, INVENTORY 93->94 + row,
manifest, ARCHITECTURE, CONTEXT.md "Core Utilities Module"). Adds
tests/core-utils.test.cjs (88 tests: behavioral + shim-identity + adversarial).

Gates: lint, code-review, security-review, codex adversarial-review (0
findings). Mac 4041 pass; clean-build docker 12935 pass, 0 fail.

Closes #877

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 13:29:13 -04:00
Viktorplus
4b0fcd83a0 Merge branch 'next' into kimi-runtime-support 2026-06-08 19:05:16 +02:00
Tom Boucher
fa1118afa7 refactor(#870): extract ROADMAP.md parsing into roadmap-parser.cts (#876)
ADR-857 rollout phase 2b. Move the 6 ROADMAP.md-parsing functions
(stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone,
getRoadmapPhaseInternal, getMilestoneInfo, getMilestonePhaseFilter) + their
interfaces out of core.cts into a new leaf module src/roadmap-parser.cts.
core.cts re-exports them (behavior-preserving); ~9 external callers unchanged.

Resolves the parse/write straddle: roadmap.cts (ROADMAP.md mutation) now imports
its 3 parsing helpers from roadmap-parser.cjs directly instead of reaching
through core. roadmap-parser depends only on leaves (phase-id, planning-workspace,
shell-command-projection) — cycle-free, enabled by phase 2a.

New-CLI-module checklist done (.gitignore, eslint, INVENTORY 92->93 + row,
manifest, ARCHITECTURE, CONTEXT.md "Roadmap Parser Module"). Adds
tests/roadmap-parser.test.cjs (46 tests: behavioral + shim-identity +
adversarial ROADMAP.md fixtures).

Adversarial review surfaced a pre-existing fence-blindness bug in
getMilestonePhaseFilter (matches phase headings inside fenced code blocks);
filed as #875 and left for a separate fix (out of scope for this
behavior-preserving extraction). The two fenced-fixture tests characterize the
current behavior with a #875 reference and flip when it's fixed.

Gates: lint, code-review, security-review, codex adversarial-review (0
correctness findings). gsd-test: clean-build docker + Mac green; the full-suite
docker run's "X is not a function" errors on re-exported symbols were local
incremental-tsc staleness (verified: clean rebuild of the affected files = 195
pass, 0 fail; Mac = 4001 pass).

Closes #870

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:34:17 -04:00
Tom Boucher
a480510f54 fix(#872): make roadmap-phase-fallback tests hermetic against ambient GSD env (#873)
extractCurrentMilestone reads STATE.md via planningDir(cwd), which is
workstream-aware (honours GSD_PROJECT/GSD_WORKSTREAM). The fixtures write
STATE.md to the plain <tmp>/.planning/STATE.md, so a developer shell inside a
GSD workstream (GSD_WORKSTREAM exported) redirected the read to a non-existent
workstream subdir -> version=null -> closed milestone sections leaked into the
slice and assertions failed. Clean CI/Docker env never hit it. Not a Node-26
regex bug; reproduces identically on any Node with GSD_WORKSTREAM set.

- scripts/run-tests.cjs: strip GSD_PROJECT/GSD_WORKSTREAM before spawning test
  children so the local runner env matches clean CI/Docker.
- tests/roadmap-phase-fallback.test.cjs: file-level beforeEach/afterEach
  save/delete/restore of both vars; new regression test pinning workstream-aware
  STATE.md resolution.
- tests/run-tests-harness.test.cjs: guard asserting the runner strips both vars
  (so removing the deletion fails clean CI).

Closes #872

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:04:26 -04:00
Tom Boucher
35174ce9b0 chore(#57): add runtime install no-drift guard tests (#867)
Add tests/issue-57-runtime-install-no-drift.test.cjs protecting the Runtime
Install Policy Module boundary (ADR-58) and the explicit Runtime Config Adapter
Registry (#60), now that #58/#60/#56 have landed and the seam exists.

The guards fail when:
- (AC1) supported-runtime metadata is added to an installer/query call site
  (allRuntimes, the interactive runtimeMap menu) without a matching registry
  adapter entry — enforced by three-way set equality across allRuntimes,
  runtimeMap values, and ALLOWED_CONFIG_RUNTIMES.
- (AC2) config-mutation dispatch escapes the registry: every intent uses a
  registry-declared install surface, every permission writer is null or a
  registry-known runtime, unknown/prototype-key runtimes fail loudly, and a new
  inline 'runtime === "..."' branch against an unregistered runtime is rejected.

Assertions are behavioral (require + reflect on live exports) where behavior can
cover the contract (AC3); two annotated structural guards cover what it cannot.
Existing installer/runtime-policy/runtime-global-skills suites stay green (AC4).

Gates: eslint, lint-test-file-count, full Mac suite (12715 pass / 0 fail) and
Linux Docker (14709 pass / 0 fail) all green; Codex adversarial-review,
/code-review, and /security-review run with findings addressed.

Closes #57

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 11:46:19 -04:00
Tom Boucher
9e2e71de7b ci(#869): raise full-test Windows lane timeout 15m->20m (#871)
The 'full test (windows-latest, 22)' job runs all 656 unit files in one lane and
has crept to ~14m+ over recent PRs (12m31s -> 13m45s -> 14m18s), so the 15m cap
started cancelling jobs mid-run and red-blocking product PRs even when every test
passes (the job is killed on wall-clock, not a test failure). Raise the full-test
job timeout to 20m to restore headroom. Durable fix (shard the Windows full unit
lane) tracked as a separate follow-up.

Closes #869

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 11:30:11 -04:00
Tom Boucher
2988a21c46 refactor(#865): extract pure phase-id helpers from core.cts into phase-id.cts (#868)
ADR-857 rollout phase 2a — the first cut of the core.cts decomposition.
Move the 9 pure phase-id parsing/matching helpers (escapeRegex,
normalizePhaseName, comparePhaseNum, extractPhaseToken, phaseTokenMatches,
phaseMarkdownRegexSource/Exact, getMilestoneFromPhaseId, getPhaseDirFromPhaseId)
out of core.cts into a new leaf module src/phase-id.cts. core.cts re-exports
them (behavior-preserving); its internal callers resolve the destructured
bindings.

Cycle-safe by design: phase-id depends on nothing in core, so re-export creates
no circular require (the property that made phase-1 io.cts clean). This is the
leaf-first ordering — it unblocks the roadmap-parser extraction (2b), which
imports phaseMarkdownRegexSource.

New-CLI-module checklist: .gitignore, eslint.config.mjs ignores, INVENTORY.md
count 91->92 + row, INVENTORY-MANIFEST.json, ARCHITECTURE.md row, CONTEXT.md
"Phase Id Module" glossary entry. Adds tests/phase-id.test.cjs (63 behavioral
tests incl. shim-identity + adversarial inputs).

Gates: lint, code-review, security-review, codex adversarial-review (all 0
findings), and gsd-test-both (14814 pass on Mac + Linux Docker, 0 fail).

Closes #865

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 11:12:08 -04:00
Tom Boucher
3697e6768f fix(#853): gate manager/autonomous background dispatch by runtime (#863)
* fix(#853): gate manager/autonomous bg dispatch by runtime

/gsd-manager and /gsd-autonomous --interactive dispatched Plan/Execute
via Agent(run_in_background=true). On Claude Code a backgrounded agent
has no Agent/Task tool, so it cannot spawn the nested subagents those
pipelines need — per-plan worktree-isolated executors, the plan-checker,
and the verifier. The phases reported complete but isolation and
independent verification silently never ran, even with use_worktrees /
plan_check / verifier enabled.

Both workflows now resolve the runtime (config-get runtime, default
claude) before dispatching: run plan/execute INLINE on Claude Code so
the nested pipeline runs, and background-dispatch only on runtimes where
a backgrounded agent can still nest. Mirrors execute-phase.md's existing
Codex fail-closed precedent. Reconciles the stale unconditional
background/overlap/lean-context claims elsewhere in both workflows and
in the docs. Adds a content regression test pinning the gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(#853): add changeset for runtime-gated bg dispatch

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 10:42:05 -04:00
Tom Boucher
ac56672dba fix(#856): remove gsd-cmd-rewrites temp dirs after install (#862)
* fix(#856): remove gsd-cmd-rewrites temp dirs after install

applyRuntimeContentRewritesForCommandsInPlace() returns a fresh
mkdtemp dir under os.tmpdir() (gsd-cmd-rewrites-*) with rewritten
command markdown. installRuntimeArtifacts() copied from it but never
removed it, leaking one temp dir per commands kind per install — on
tmpfs /tmp hosts these accumulate and consume RAM-backed storage.

Wrap the per-kind copy in try/finally and rmSync the temp dir (only
when it differs from the staged source, i.e. the commands kind) once
the copy completes or fails. dest creation moved inside the try so a
mkdir failure still triggers cleanup.

Regression test isolates os.tmpdir() to a private TMPDIR root and
asserts no gsd-cmd-rewrites-* dir survives the install.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(#856): add changeset for installer temp-dir cleanup

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 10:41:54 -04:00
Tom Boucher
a5d82bf98a refactor(#859): extract CLI I/O primitives from core.cts into io.cts (#864)
ADR-857 rollout phase 1. Move the CLI I/O primitives — output(), error(),
ERROR_REASON, setJsonErrorMode/getJsonErrorMode, and the output() large-payload
temp-file spillover helpers (GSD_TEMP_DIR, ensureGsdTempDir, reapStaleTempFiles)
— out of the 2271-line core.cts god-module into a new, small src/io.cts. core.cts
re-exports them so existing consumers are unaffected (behavior-preserving).

Repoint src/profile-pipeline.cts to import output/error/reapStaleTempFiles from
io directly; graphify/intel/audit were verified not to import these symbols. Net:
the leaf feature modules no longer depend on core just for I/O — the enabling
first cut toward Capability extraction.

New-CLI-module checklist: .gitignore (bin/lib/io.cjs), eslint.config.mjs ignores,
INVENTORY.md count 90→91 + io.cjs row, INVENTORY-MANIFEST.json, ARCHITECTURE.md
core.cjs/io.cjs rows, CONTEXT.md "I/O Module" glossary entry. Adds tests/io.test.cjs
(28 behavioral tests incl. shim-identity and the @file: spillover branch).

Gates: lint, code-review, security-review, codex adversarial-review, and
gsd-test-both (14742 pass on Mac + Linux Docker, 0 fail) all green.

Closes #859

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 10:24:32 -04:00
Tom Boucher
03b467f2e7 docs(#857): ADR-857 Capability system — five-step loop core, features as plug-ins (#858)
Record the final-state architecture: the five-step loop (Discuss → Plan →
Execute → Verify → Ship) plus shared-infrastructure skills are the privileged
host/core; every other feature is a Capability (plug-in) selectable at install
and toggleable after restart, attaching through ~12 Loop Extension Points.

Adds docs/adr/857-capability-system.md (Status: Proposed) capturing the eight
resolved design decisions, the resolved design details (extension points,
contribution merge, declaration shape, runtime descriptor, deferred trust gate),
alternatives, consequences, and a six-phase rollout. Records the new domain
terms in CONTEXT.md: Capability, Capability Registry, Loop Extension Point,
Runtime Capability.

Runtime/CLI support is itself a Capability (role: runtime) — Claude Code, Codex,
Antigravity tier-1; the seam is declarative-over-primitives so third-party CLI
support lands later as an additive loader, no rework.

Closes #857

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 10:24:11 -04:00
Viktorplus
8e6a8a14eb Merge branch 'next' into kimi-runtime-support 2026-06-08 09:14:38 +02:00
Tom Boucher
29c0a2f5a1 docs(#849): capture 1.4.0 release features across the docs base (#850)
Diataxis review of the 1.4.0 content (52 changesets, multi-runtime maturation
plus native packaging and new flags) against the existing docs base found most
per-feature docs already landed with their PRs. Fill the four remaining gaps,
each in its Diataxis quadrant:

- Reference: FEATURES.md Feature #36 (Multi-Runtime Support) updated in place
  with 1.4.0 additions — native skills emission (Cline/Kilo/OpenCode), new
  slash-command surfaces (CodeBuddy/Augment/Cursor), cross-runtime lifecycle
  hooks for context-headroom tracking, and the Gemini CLI extension package.
- Reference: CONFIGURATION.md gains a dedicated worktree.baseRef entry (values,
  .claude/settings.local.json location, auto-set-on-install behaviour).
- How-to: plan-a-phase.md gains an 'override planning granularity for one phase'
  section for the --granularity flag.
- Explanation: context-engineering.md gains a 'Lifecycle hooks and context
  headroom' section (the why of lifecycle hooks + forked context), cross-linked
  from multi-agent-orchestration.md.

Docs-only; documents already-shipped features, so no changeset required
(docs/ is not in the changeset-lint user-facing prefixes).

Closes #849

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 23:22:15 -04:00
Tom Boucher
e20985a15f fix(#834): derive installer --help skill counts from PROFILES (#847)
* fix(#834): derive installer --help skill counts from PROFILES

The installer --help text undercounted profile skill counts: it claimed
"core — 7 main-loop skills" and "standard — ~13 skills" when the real
counts in PROFILES (bin/lib/install-profiles.cjs) are 8 and 14. The
`surface` skill was added to core after the help string was written, and
standard was never updated.

Derive the core and standard counts from PROFILES.core.length and
PROFILES.standard.length so the help text cannot drift again. Replace the
stale hardcoded "all 66 skills" (actually 67) on the full line with
drift-proof "all skills" — full is the '*' sentinel with no array length
and no cheap authoritative total in the help path.

Add regression tests that run `node bin/install.js --help` and assert the
printed core/standard counts equal the PROFILES lengths, plus a guard
that the full line carries no hardcoded numeric count.

Closes #834

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(#834): add changeset for installer --help count fix

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 22:55:10 -04:00
Viktorplus
cb897124ef Merge branch 'next' into kimi-runtime-support 2026-06-08 04:05:46 +02:00
Tom Boucher
606363c416 chore(#846): remove unused PR-size labeler (size/S–XL) workflow (#848)
The PR Gate workflow's only job, size-check, labeled every PR with
size/S–size/XL based on lines changed. Those labels aren't used in any
review, triage, or automation flow, so the workflow was pure noise.

- Delete .github/workflows/pr-gate.yml
- Drop size-check from required status checks in both rulesets so PRs
  don't block forever on a check that never reports
- Remove pr-gate.yml from INERT_WORKFLOWS (ci-test-scope.cjs) and the
  knownInert list (ci-test-scope.test.cjs)
- Remove "PR Gate / size-check" from setup-branch-protection.sh

Closes #846

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 22:03:48 -04:00
Viktorplus
54191f6c6c Merge branch 'next' into kimi-runtime-support 2026-06-08 03:57:58 +02:00
Tom Boucher
543e51e71f fix(#844): sync runtime manifest versions on npm version bump (#845)
* fix(#844): sync runtime manifest versions on npm version bump

The release workflow bumps package.json via `npm version` but never
stamped the runtime-integration manifests that must track it
(.claude-plugin/plugin.json #766, gemini-extension.json #775), so the
first RC/finalize whose version diverged from the -dev stream failed the
test suite before tagging/publishing.

Add scripts/sync-manifest-versions.cjs (single VERSIONED_MANIFESTS
registry) wired to a `version` npm lifecycle hook that stamps + stages
the manifests on every `npm version` — covering all four release bump
sites and local bumps with no workflow edits. A regression guard test
fails if any repo JSON whose version matches package.json is not
registered, forcing future version-bearing manifests into the sync.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(#844): add changeset for manifest version sync fix

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 21:43:30 -04:00
Tom Boucher
1b6bd66f2c feat(#770): register Claude Code lifecycle hooks (SubagentStop/Stop/PreCompact/FileChanged) (#821)
* feat(#770): register Claude Code lifecycle hooks (SubagentStop/Stop/PreCompact/FileChanged)

Wire three new context-tracking events (SubagentStop, Stop, PreCompact) to
gsd-context-monitor so context-headroom warnings surface at model-stop and
subagent-finalisation moments — not just on PostToolUse.  Add a new
FileChanged hook (gsd-config-reload.js) that hot-reloads .planning/config.json
context mid-session when the user edits it, injecting a config summary as
hookSpecificOutput.additionalContext.  Updates plugin manifest hooks.json,
managed-hooks-registry, installer-migration-report allowlist, and
shell-command-projection cleanup tables.  Tests: 21 new assertions in
enh-770-claude-hook-events.test.cjs; enh-788 and issue-766 test suites updated.

Closes #770

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(#770): document newly-registered Claude Code lifecycle hooks

Add a Hook coverage table to the Claude Code npm installer section of
docs/how-to/install-on-your-runtime.md describing SubagentStop, Stop,
PreCompact, and the new FileChanged (gsd-config-reload.js) hook that
hot-reloads .planning/config.json mid-session. Also fixes the changeset
frontmatter (adds type: Added + pr: 821) so docs-lint can consume the
fragment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(#770): add gsd-config-reload.js to INVENTORY.md and regenerate manifest

The feat commit added hooks/gsd-config-reload.js but did not bump the
Hooks count in docs/INVENTORY.md (14→15) or add the new row, and did not
regenerate docs/INVENTORY-MANIFEST.json. Both inventory-counts and
inventory-manifest-sync tests failed across the full CI matrix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(#770): make lifecycle-hook tests deterministic on scoped runner

Replace the shared hooks/dist/ ensemble setup (ensureHooksDist /
teardownHooksDist) in the Claude hook tests with per-test isolation:
pre-populate each test's own tmpDir/.claude/hooks/ with stub files and
pass installerMigrations:[] to install() so the first-time-baseline
migration does not remove the stubs before the copy step can run.

Root cause: hooks/dist/ is gitignored and absent on a fresh npm ci.
ensureHooksDist() created it and teardownHooksDist() deleted it, but
with --test-concurrency=4 both test files ran concurrently as separate
Node.js worker processes sharing the same filesystem.  One file's
afterEach teardown deleted hooks/dist/ while the other file's install()
was copying from it, producing an ENOENT (reproduced 2/10 runs locally).

The additional issue: even with pre-placed stubs surviving the copy race,
the 000-first-time-baseline migration classified hooks/gsd-*.js as
bundled-gsd-hook artifacts, auto-removed them, and the copy step never
re-ran (hooks/dist/ absent) — leaving contextMonitorFile missing and all
hook registrations silently skipped (the 'got: []' symptom).

Fix: pre-populate targetDir/hooks/ per-test (isolated temp dir) AND pass
installerMigrations:[] so the baseline scan is skipped.  The Qwen suites
already used this pattern correctly; the Claude suites are aligned to it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(#770): ship gsd-config-reload.js by adding it to build-hooks HOOKS_TO_COPY

The #770 feature added hooks/gsd-config-reload.js and registered it in
MANAGED_HOOKS, the installer, INVENTORY, and the test EXPECTED_ALL_HOOKS
list — but never added it to scripts/build-hooks.js HOOKS_TO_COPY. As a
result the hook was never copied into hooks/dist/ during the build, so:

  - the hook would never ship to users (real production bug — the
    FileChanged config-reload feature was dead-on-arrival), and
  - install-minimal-hooks.test.cjs #1755 ("all expected hooks are copied
    from hooks/dist/ to target", ".js hooks are executable after copy",
    "manifest contains .js hook entries") failed on any environment with
    a clean checkout (no pre-existing hooks/dist/): coverage, full test
    macos-22/macos-24, test ubuntu-24.

The failures were masked locally only by a stale hooks/dist/ left from a
prior build (build-hooks copies into dist without clearing it). On CI's
fresh `npm ci` there is no dist, so the omission surfaced.

Fix: add 'gsd-config-reload.js' to HOOKS_TO_COPY so build-hooks stages it
into hooks/dist/ alongside the other JS hooks. Verified by removing
hooks/dist/ and rerunning the full suite green (0 fail).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(#770): make config prototype-pollution beforeEach deterministic on scoped runner

Root cause: the #663 and alert-#26 prototype-pollution describe blocks
seeded .planning/config.json in beforeEach via a bare
runGsdTools('config-ensure-section') whose result was discarded. That
command runs in a spawned gsd-tools child; on the scoped CI lane
(--test-concurrency=4, config.test.cjs scheduled alongside the heavy
install/tarball suites that #770 pulled into the targeted set) the child
can be transiently killed under resource pressure (non-zero exit, empty
stderr — an OS-level kill, not an app error). The swallowed failure left
config.json absent, so the first subtest's readConfig() threw ENOENT
opening <tmp>/.planning/config.json. Only 1 of 4 subtests failed,
confirming a per-invocation transient, not a deterministic miss; the full
suite schedules files differently so config.test.cjs did not collide with
those heavy neighbors → passed there.

Fix: add ensureConfigReady(tmpDir) which retries config-ensure-section on
ANY failure or missing file and throws a clear diagnostic if it still
cannot create config.json, then use it in both prototype-pollution
beforeEach blocks. Setup is now deterministic under load; the #663/alert-#26
security assertions are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 20:36:11 -04:00
Tom Boucher
d32b8db635 feat(#836): no-LLM duplicate-issue detection + challenge + 1-day auto-close (#843)
* feat(#836): no-LLM duplicate-issue detection + challenge + 1-day auto-close

Adds a deterministic (no-LLM) duplicate-issue governance lifecycle:

- scripts/issue-dedupe.cjs: pure, unit-tested module (tokenize, Sørensen–Dice
  title similarity, scoreCandidates, renderChallengeComment, shouldClose) with
  fail-safe destructive-action guards.
- duplicate-check.yml (issues:opened): scores new-issue title against open
  issues, posts a challenge comment + applies the pending `possible-duplicate`
  label on a clear match.
- duplicate-sweep.yml (daily cron): closes possible-duplicate issues whose
  challenge comment is >24h old with no human reply and no 👎 veto; honors
  exempt labels; re-checks the label immediately before close (TOCTOU guard);
  strips the label on close to avoid reopen loops.
- remove-duplicate-label.yml (issue_comment:created): clears the label and
  applies needs-maintainer-review when any human responds.
- bug_report.yml / docs_issue.yml: add the required "I searched existing
  issues" preflight checkbox so all five forms force a pre-search attestation.
- docs/agents/triage-labels.md: document the label + lifecycle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(#836): add changeset fragment for duplicate-issue detection

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 20:35:43 -04:00
Tom Boucher
988024c1a3 fix(#837): three-dot diff in ci-test-scope so docs-only PRs skip the heavy matrix (#841)
CI test-scope detection diffed changed files with a two-dot
`git diff --name-only base head`, where base is the moving tip of `next`.
A PR branch cut from a slightly older `next` surfaced every product file
`next` had gained since the merge-base, flipping product_changed/full_matrix
and running the full Windows/macOS matrix + coverage on docs-only PRs.

Switch to a three-dot `git diff --name-only base...head` (vs the merge-base),
matching GitHub's PR "Files changed" semantics. Add a regression test that
builds a stale-base topology, plus a guard test pinning `fetch-depth: 0` on
the `changes` job (required for the merge-base to be locally available).

Closes #837

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 20:28:45 -04:00
Tom Boucher
40d48c0508 feat(#815): add /gsd-update --next to install the @next RC channel (#839)
Adds an opt-in --next (alias --rc) flag to /gsd-update targeting the @next RC dist-tag (ADR #660), with a {latest,next} allowlist enforced at three layers, channel-aware version check + banner, and byte-for-byte unchanged default @latest behavior.

Closes #815
2026-06-07 20:22:07 -04:00
Tom Boucher
5b4880522b docs(#832): add how-to guide for minimal install / skill profiles (#835)
Add docs/how-to/install-minimal-and-add-skills.md covering the --minimal
/ --core-only / --profile=core install, the core/standard/full profiles,
and growing the surface live via /gsd:surface or on reinstall. Register
it in the docs/README.md How-to guides index.

Closes #832

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 19:36:28 -04:00
Tom Boucher
1040fb792e feat(#777): register Cursor-native hooks (.cursor/hooks.json) for session-start/post-tool parity (#831)
* feat(#777): register Cursor-native hooks (.cursor/hooks.json) for session-start/post-tool parity

- Add gsd-cursor-session-start.js: injects STATE.md presence reminder (or
  new-project nudge) into Cursor sessions via the sessionStart hook event
- Add gsd-cursor-post-tool.js: emits an additional_context nudge when
  write-class tool calls touch .planning/ files (postToolUse hook event)
- Add 'cursor-hooks-json' installSurface to runtime-config-adapter-registry;
  writeCursorHooksJson/reconcileCursorHooksJson write the canonical
  { version: 1, hooks: { sessionStart, postToolUse } } JSON shape with
  idempotent reconciliation that preserves user-owned hook entries
- Hook scripts are copied with /gsd:→gsd- rewrite so installed files
  contain no colon-form slash-command refs (bug-376 invariant)
- 20 new tests in tests/cursor-hooks.test.cjs cover all reconciler paths,
  entry helpers, removal, runtime adapter surface, and hook script behavior
- Update CONTEXT.md, ARCHITECTURE.md, installer-migrations.md, and
  000-first-time-baseline.cts to include Cursor hooks.json surface

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(#777): build hooks/dist on demand in bug-376 test for scoped/windows CI

hooks/dist is gitignored and only produced by `npm run build:hooks`.
The CI scoped (ubuntu-latest/node-22) and windows (windows-latest/node-24)
test jobs do NOT run build:hooks before executing tests, so bug-376's
prerequisite suite was failing with "hooks/dist not found" on both legs.

Add ensureHooksDist() helper (mirrors bug-3357 pattern) that builds
hooks/dist on demand in the before() hooks of prerequisite and Suite 3.
Also add ensureHooksDist() call to Suite 3's before() so the snapshot
step is also hermetic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 19:22:48 -04:00
Tom Boucher
e04e757672 feat(#776): Gemini hook events (BeforeAgent/AfterAgent/BeforeModel) + hooksConfig.enabled check (#829)
* feat(#776): Gemini hook events (BeforeAgent/AfterAgent/BeforeModel) + hooksConfig.enabled check

Register three new Gemini-CLI hook events on install:
  - BeforeAgent: fires before agent planning; wired to gsd-context-monitor
  - AfterAgent: fires after final response generation; wired to gsd-context-monitor
  - BeforeModel: fires before each LLM call (per-turn); wired to gsd-context-monitor

All three reuse gsd-context-monitor.js (no new hook files). Uninstall cleanup
loop extended to remove the new events. Non-array guard added for robustness
against malformed settings.

Also detect hooksConfig.enabled:false in Gemini settings and emit a clear
warning — without this check, all registered hooks silently do nothing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: update changeset pr: 829

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(#776): document Gemini hook events

Add hook coverage table to the Gemini CLI section of install-on-your-runtime.md,
covering the three new events (BeforeAgent/AfterAgent/BeforeModel wired to
gsd-context-monitor) plus a callout for the hooksConfig.enabled:false silent
failure mode detected by the installer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 19:16:32 -04:00
Viktorplus
4d6f27ae32 Merge branch 'next' into kimi-runtime-support 2026-06-08 01:08:28 +02:00
Tom Boucher
cb284962bf feat(#789): elevate CodeBuddy — slash commands (#830)
* feat(#789): elevate CodeBuddy — emit slash commands (+ document subagent/MCP scope)

Emit a CodeBuddy slash-command surface so GSD workflows appear in the
'/' menu, reaching parity with other elevated runtimes.

- Add convertClaudeCommandToCodebuddyCommand and register a commands/
  artifact kind for the codebuddy runtime (commands/gsd-<name>.md),
  consistent with the Cursor (#785) and Augment (#790) commands surfaces.
- Mark emitted skills user-invocable:false so the commands surface is the
  sole '/' entry point (no duplicate /gsd-* entries); skills stay
  model-invocable. CodeBuddy's SKILL.md supports this field.
- Normalize $HOME/.codebuddy (bare + slash) path forms in runtime
  rewrites so --config-dir/local installs don't leak the default home.
- Report installed commands/ count on install; uninstall prunes gsd-*
  commands while preserving user-owned commands.

Scope: subagents (~/.codebuddy/agents/) are already emitted by the
generic agents block (unchanged); no mcp.json is written (gsd ships no
MCP server, and CodeBuddy's mcp.json registers only external servers).

Closes #789

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(#789): set changeset pr number to 830

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 18:56:47 -04:00
Tom Boucher
28c8d524fe feat(#778): cross-runtime command enrichment (Gemini {{args}}/!{}, Qwen priority) (#825)
* feat(#778): cross-runtime command enrichment (Gemini {{args}}/!{}, Qwen priority)

Enrich the installer's per-runtime command/skill generators with native,
verified, additive fields:

- Gemini CLI: map Claude's $ARGUMENTS -> Gemini's {{args}} in generated TOML
  commands so typed arguments interpolate; inject live .planning/STATE.md into
  /gsd:progress via a fixed, injection-safe !{cat .planning/STATE.md 2>/dev/null}
  shell block (no interpolated input).
- Qwen Code: emit the optional numeric `priority` field on main-loop skills so
  the most-used workflows sort first in the /skills list (higher = earlier per
  the Qwen skills spec; the issue's inverse numbering was corrected).

OpenCode per-command model/agent/subtask/variant enrichment was evaluated and
intentionally not implemented: `model` reintroduces the #1156
ProviderModelNotFoundError regression for non-Anthropic providers (the converter
deliberately strips model:), `subtask`/`agent` change execution semantics for
GSD's interactive commands, and `variant` is not in the OpenCode command schema.

Schemas verified against primary docs (Gemini custom-commands, Qwen skills,
OpenCode commands/skills). Adds tests/enh-778-* and how-to + USER-GUIDE docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(#778): set changeset PR number to 825

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 18:56:42 -04:00