Commit Graph

385 Commits

Author SHA1 Message Date
Jeremy McSpadden
fb37fa7dd5 fix(#725): route Codex gsd-tools calls through shim (#731)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 14:44:43 -04:00
Tom Boucher
1fd5c86a1e fix(#983): rewrite bare .claude paths in Trae/Windsurf converters (Codex/Cline parity) (#995)
* fix(#983): rewrite bare .claude paths in Trae/Windsurf converters (Codex/Cline parity)

Both convertClaudeToWindsurfMarkdown and convertClaudeToTraeMarkdown only
handled trailing-slash .claude/ forms; bare ~/.claude and $HOME/.claude
references (e.g. configDir = ~/.claude, RUNTIME_CONFIG_DIR=".../$HOME/.claude")
survived conversion and pointed users at the wrong config dir.

Fix: add bare-form replacements using negative lookahead (?![\w-]) to protect
.claude-plugin and .claudeignore, mirroring Cline (#782) and Codex (#570) precedent.
Also adds CLAUDE_CONFIG_DIR -> WINDSURF_CONFIG_DIR / TRAE_CONFIG_DIR rewrite.
_applyRuntimeRewrites windsurf case gets matching \b-anchored bare-form lines,
mirroring the existing trae case.

Closes #983

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(#983): backfill changeset pr number (995)

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 11:43:31 -04:00
radioflyer28
d617735bed fix(codex): avoid partial model effort pinning (#842)
Co-authored-by: Andrew Kriz <akriz@vt.edu>
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
2026-06-10 11:41:01 -04:00
Tom Boucher
36b68ac81d fix(#977): map ephemeral fnm multishell execPath to a stable fnm alias in normalizeNodePath (#992)
* fix(#977): map ephemeral fnm multishell execPath to a stable fnm alias in normalizeNodePath

Closes #977

* chore(#977): backfill changeset pr number (992)

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-10 11:16:13 -04:00
Tom Boucher
898d55788e fix(#976): detect command+args (wrapped) hook registrations in installer presence checks (#994)
* fix(#976): detect command+args (wrapped) hook registrations in installer presence checks

Add referencesHook() helper that inspects both h.command (standard form) and
h.args[] (args-form / wrapped-launcher form) when checking whether a managed
hook is already registered.  Rewrite all has*Hook predicates and the
alreadyHas* guards to use it so args-form registrations suppress the duplicate
stock string-command entry that was previously appended on every install/update.

Also add an explicit args-form skip to rewriteLegacyManagedNodeHookCommands so
entries with a non-empty args[] are left untouched (they are intentional user
wrappers, not legacy bare-node commands to migrate).

Extend isManagedHookCommand() in shell-command-projection.cts with an optional
args: unknown[] parameter that checks whether any arg's basename matches the
managed hook surface set — backward compatible; existing callers are unaffected.

Regression test added to tests/install-regressions.test.cjs:
- two-pass install with an args-form SessionStart entry pre-written to
  settings.local.json asserts exactly 1 hook entry remains after reinstall
  (previously 2 — the original args-form + a new stock string-command duplicate)
- rewriteLegacyManagedNodeHookCommands test asserts args-form entries unchanged

Closes #976

* chore(#976): backfill changeset pr number (994)

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-10 10:44:24 -04:00
Tom Boucher
e8cfb560b2 fix(#851): correct Codex quick adapter for generic multi_agent_v1 schema (#958)
* fix(#851): correct Codex adapter for generic multi_agent_v1 schema

The Codex skill adapter header in getCodexSkillAdapterHeader() documented
typed spawn_agent(agent_type=...) as a direct, unconditional mapping for all
Task()/Agent() calls. In sessions exposing only the generic multi_agent_v1
schema (message/items/fork_context — no agent_type field), this mapping is
silently invalid: the orchestrator cannot natively dispatch typed gsd-planner/
gsd-executor agents and may fall back to inline execution or produce errors.

Fix: Section C now requires schema detection before spawning. It documents the
typed mapping as conditional on the agent_type-capable schema (e.g. multi_agent_v2)
and introduces an explicitly-labeled generic-agent workaround for multi_agent_v1
sessions — read the agent TOML, inject its instructions as a role-preamble, and
call spawn_agent(message=...) — clearly marking the result as NOT equivalent to
typed gsd-planner/gsd-executor execution.

Regression test: tests/bug-851-codex-quick-adapter-agent-type-fallback.test.cjs
asserts schema-awareness language, the multi_agent_v1 fallback, the workaround
label, and backward compat with the existing bug-279 typed-spawn contract.

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

* chore: add changeset for PR #958

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

* fix(#851): keep Codex adapter block consistent with materialized skill surface

The `~/.codex/agents/<agent-name>.toml` literal introduced in the #851 prose
was being rewritten to the real install path by `_applyRuntimeRewrites` (the
`~/.codex/` → pathPrefix substitution) before the SKILL.md was written to
disk.  `getCodexSkillAdapterHeader()` still returned `~/.codex/agents/...` so
the test assertion (exact match between builder output and materialized file)
always failed.

Fix: replace the `~/.codex/agents/` literal with the runtime-neutral form
`agents/<agent-name>.toml` plus a parenthetical naming `$CODEX_HOME/` — which
is not matched by any rewrite pattern and survives the path-substitution step
unchanged.  The #851 schema-detection + generic-subagent-fallback intent is
fully preserved.

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

* fix(#851): resolve active Codex config root in fallback; strengthen tests (adversarial review)

- Rewrites the generic-agent workaround step 1 to explicitly describe
  active config root resolution (priority: $CODEX_HOME → --config-dir →
  --local .codex → default global dir) without the literal ~/.codex/
  substring that _applyRuntimeRewrites replaces, preventing bug-3582
  divergence.
- Replaces OR/loose-includes test assertions in bug-851 with AND-logic
  checks covering all four required elements: (a) schema-detection step,
  (b) active-config-root resolution for the TOML path including all three
  override mechanisms, (c) NOT-equivalent-to-typed-gsd-planner/gsd-executor
  label, and (d) fail-closed rule when typed dispatch is mandatory.

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

* test(#851): register bug-851/947/948/950 in lint-regression-test-names allowlist

The ratchet (622e4be) bans NEW top-level bug-NNNN test files; the four
sibling PRs (#851, #947, #948, #950) landed AFTER the baseline was cut,
so their test files were not yet grandfathered. Add all four to the
identity allowlist so lint-regression-test-names passes.

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

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 00:45:27 -04:00
Tom Boucher
1c86368785 fix(#947): restore gsd- prefix on Hermes skills for canonical dispatch (#955)
* test(#947): add regression tests and update stale Hermes assertions

- Add bug-947-hermes-gsd-prefix.test.cjs: 12 TDD tests covering fresh
  install canonical layout, bare-stem migration, manifest key format,
  and non-Hermes runtime isolation
- Update hermes-skills-migration.test.cjs: bare-stem → gsd-prefixed
  path and name assertions (#947 canonical layout)
- Update install-nested-layout.test.cjs: Hermes NEST matrix prefix ''
  → 'gsd-'
- Update install-regressions.test.cjs: Defect #1 now seeds bare-stem
  dirs (help/, quick/) and asserts gsd-help/ canonical output; use
  real GSD stems so readGsdCommandNames() migration finds them
- Update install-runtime-artifacts.test.cjs: Hermes nested layout and
  legacy migration assertions align with gsd- prefix
- Update install.test.cjs: Hermes install test uses gsd- prefixed paths
- Update runtime-artifact-layout.test.cjs: prefix '' → 'gsd-'

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(#947): restore gsd- prefix on Hermes skills for canonical dispatch

Hermes skills were installing under bare-stem paths
(skills/gsd/<stem>/SKILL.md, name: <stem>) due to prefix: '' set in
ADR-3660 / #3664. This broke /gsd-<stem> dispatch and forced users to
invoke skills without the gsd- namespace prefix.

- src/runtime-artifact-layout.cts: change Hermes skillsKind prefix
  from '' to 'gsd-'; skills now land at skills/gsd/gsd-<stem>/SKILL.md
  with name: gsd-<stem>
- bin/install.js _runLegacyInstallMigrations: invert the #3664
  migration — remove stale bare-stem dirs (using readGsdCommandNames()
  to distinguish GSD-owned stems from user content), keep gsd-* dirs
  which are now canonical
- bin/install.js _runLegacyUninstallCleanup: also remove bare-stem
  dirs on uninstall for clean teardown
- bin/install.js uninstallRuntimeArtifacts: post-cleanup removes
  DESCRIPTION.md and empty skills/gsd/ category dir on Hermes
- bin/install.js: remove skillListPrefix Hermes exception (now uses
  shared 'gsd-' path)
- docs/adr/3660-runtime-artifact-layout-module.md: document #947
  reversal of the bare-stem sub-decision

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: add changeset for #947 fix (#955)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(#947): remove ALL pre-migration bare-stem Hermes skills on reinstall (adversarial review)

Replace readGsdCommandNames()-based bare-stem cleanup (which missed skills
not in the commands source tree, e.g. dev-preferences) with
_removeHermesBareStemDirs(), called AFTER the install loop when the exact
set of installed gsd-<stem>/ dirs is authoritative. For every gsd-<stem>/
written this run, the corresponding bare skills/gsd/<stem>/ is removed.
User-owned bare dirs with no gsd-<stem> counterpart are preserved.

Add two adversarial-review regression tests that FAIL on old code:
- bare skills/gsd/dev-preferences/ removed when gsd-dev-preferences/ installed
- user-owned bare dir with no gsd-<stem> counterpart is preserved (no over-deletion)

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

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 00:22:36 -04:00
Tom Boucher
19efcddfc7 fix(#941): track managed-hooks-registry.cjs in file manifest (#953)
* test(#941): regression test for managed-hooks-registry.cjs manifest omission

Adds bug-941-managed-hooks-registry-manifest.test.cjs which verifies:
- managed-hooks-registry.cjs appears in gsd-file-manifest.json after install
- manifest covers the full HOOKS_TO_COPY set (forward-proof)
- detect-custom-files reports 0 custom files after a clean install
- manifest hook keys use forward slashes (cross-platform)

All four assertions fail before the fix, confirming the bug is reproducible.

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

* fix(#941): track managed-hooks-registry.cjs in file manifest

The writeManifest() hooks loop in bin/install.js filtered hook filenames
with `file.startsWith('gsd-') && (file.endsWith('.js') || file.endsWith('.sh'))`.
managed-hooks-registry.cjs fails both predicates (wrong prefix, .cjs extension),
so it was never recorded in gsd-file-manifest.json even though it is shipped to
users as part of HOOKS_TO_COPY.

detect-custom-files scans the installed hooks/ dir and reports any file with no
manifest entry as a custom file, producing a perpetual false-positive
"Found 1 custom file(s)" warning on every /gsd-update for all users.

Fix: import HOOKS_TO_COPY from scripts/build-hooks.js and drive the manifest
hooks loop from that set (as a Set for O(1) lookup), so the manifest set is
structurally identical to the build set. Any future hook of any prefix or
extension added to HOOKS_TO_COPY is automatically covered. The new regression
test asserts full HOOKS_TO_COPY coverage and zero detect-custom-files
false-positives after a clean install.

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

* chore(#941): add changeset for managed-hooks-registry.cjs manifest fix

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

* test(#941): assert manifest hash matches installed hook contents (adversarial review)

Strengthen the regression test to not only verify that the manifest KEY
`hooks/managed-hooks-registry.cjs` is present after install, but also that
the stored hash equals the SHA256 of the actual installed file bytes — the
same algorithm used by the installer's fileHash() function.  A future
refactor that records the right key from the wrong path or content would
now fail this assertion immediately.

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

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:28:02 -04:00
Tom Boucher
dc139b38e0 feat(#949): install/surface consume derived profiles/clusters (ADR-857 phase 4c) (#954)
Make install + surface read the registry's derived profileMembership/
capabilityClusters so a capability's tier drives what installs + surfaces.
resolveProfile (when given the registry) unions capability skills for the
profiles its tier implies before the requires: closure; resolveSurface merges
capabilityClusters into the cluster map. bin/install.js, /gsd:surface, and the
capability-state resolver all thread the registry.

Shipped as a proven no-op: the UI capability is reconciled to tier:full (its
skills were full-only in the hand-authored profiles), so it contributes only to
the full profile (already the '*' sentinel) and core/standard are unchanged.
Equivalence tests prove resolveProfile/resolveSurface/listSurface/staging/
capability-state are identical with vs without the registry; the core-alias
staging path is verified equivalent (empty manifest → raw PROFILES.core).

Closes #949

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 22:35:42 -04:00
Tom Boucher
dcb0d8a28d fix(#935): install changeset CLI so /gsd-update changelog preview works (#938)
- bin/install.js now copies scripts/changeset/ and scripts/lib/ into
  <configDir>/scripts/ so $GSD_DIR/scripts/changeset/cli.cjs resolves
  at runtime; aborts install with an explicit failure if the source
  directory is missing from the package.
- gsd-core/workflows/update.md: corrected path from
  gsd-core/scripts/changeset/cli.cjs to scripts/changeset/cli.cjs;
  added an explicit [ ! -f ] guard so a missing CLI surfaces a clear
  message rather than silently swallowing the error; stderr captured
  via 2>&1 sentinel so node errors are visible in the preview output.
- release.yml's changeset-CLI invocations (node scripts/changeset/cli.cjs)
  remain at the repo-root path and are unaffected by this change.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 12:37:26 -04:00
Colin
560b59240f fix(#743): resolve Kimi branch lint failures 2026-06-08 23:07:48 -04:00
Viktorplus
4b0fcd83a0 Merge branch 'next' into kimi-runtime-support 2026-06-08 19:05:16 +02: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
Viktorplus
8e6a8a14eb Merge branch 'next' into kimi-runtime-support 2026-06-08 09:14:38 +02: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
54191f6c6c Merge branch 'next' into kimi-runtime-support 2026-06-08 03:57:58 +02: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
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
Tom Boucher
67703d1586 feat(#772): adopt stable Codex hook events + commandWindows for Windows parity (#827)
* feat(#772): adopt stable Codex hook events + commandWindows for Windows parity

Register three new stable Codex hook events (SubagentStart, Stop,
PostToolUse) wired to gsd-context-monitor.js so Codex installs get
the same context-headroom tracking at subagent and session boundaries
that Claude/Qwen already have.

Add commandWindows field to the SessionStart hook entry on Windows so
Codex uses the .cmd shim directly (Git Bash/MSYS cannot POSIX-exec
node.exe). commandWindows is only emitted on win32; POSIX is unchanged.

Refactor reconcileCodexHooksJsonSessionStart into a generic
reconcileCodexHooksJsonEvent so any event name can be reconciled with
the same dedup/preserve-user-entries logic.

Add gsd-context-monitor.js and .cmd to MANAGED_HOOK_COMMAND_BASENAMES
_BY_SURFACE so idempotent re-runs de-duplicate entries correctly.

30 new tests covering: export surface, event registration for each of
the three events, commandWindows parity (POSIX vs win32), idempotency,
uninstall, and user-entry preservation.

Closes #772

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

* fix(#772): windows path normalization + docs-lint

- Normalize scriptPath backslashes to forward slashes in
  ensureCodexHooksJsonEvent and ensureCodexHooksJsonSessionStart so that
  isManagedHookCommand can match stored commands against configDir on
  Windows CI runners. path.resolve returns backslash paths on Windows,
  but when platform is not 'win32' (e.g. platform:'linux' in tests),
  projectManagedHookCommand skips normalization — producing a mismatch
  that breaks idempotency deduplication (the same hook entry appended
  twice on re-register). Forward-slash paths are always valid in both
  Node.js and Codex, so the normalization is safe for all platforms.
- Fix changeset pr: 0 → 827 to resolve fail_malformed_fragment.
- Add Codex hook coverage table to docs/how-to/install-on-your-runtime.md
  documenting the SubagentStart/Stop/PostToolUse events + commandWindows
  Windows-parity field added by this enhancement.

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:36 -04:00
Tom Boucher
41f91b2e88 feat(#769): adopt context:fork + effort on heavy workflow skills (#820)
* feat(#769): emit context:fork + effort: frontmatter on heavy workflow skills

Add `context: fork` and `effort: xhigh` to the three heaviest workflow
commands (plan-phase, execute-phase, autonomous) and `effort: low` to the
two quick-status commands (progress, stats).

On Claude Code, `context: fork` runs the skill in an isolated subagent
context window so the main session's context budget is protected.
`effort: xhigh` / `effort: low` signal the appropriate token-budget tier to
the runtime. Both fields are silently ignored by runtimes that do not
recognise them (Gemini, Codex, Cursor, etc.) — no behaviour change outside
Claude Code.

Update convertClaudeCommandToClaudeSkill in bin/install.js to preserve
`context:` and `effort:` when rewriting source command files to SKILL.md for
a Claude global install. Add install-suite tests to assert the fields are
present in both source commands and the installed SKILL.md output.

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

* test(#769): tighten regex assertions + add execute/plan-phase effort coverage

Fix low-severity adversarial finding: tighten test regex patterns from
`\s*` to `[ \t]*` so they cannot match across newlines (CRLF parity).
Add missing effort: xhigh assertions for gsd-execute-phase and gsd-plan-phase
SKILL.md install output to complete the black-box coverage gap.

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:30 -04:00
Tom Boucher
19280510fe feat(#774): emit service_tier/model_verbosity in Codex agent TOML + agents/openai.yaml skill chip (#828)
* feat(#774): emit service_tier/model_verbosity in Codex agent TOML + agents/openai.yaml skill chip

- Add service_tier = "flex" and model_verbosity = "low" to the Codex
  ConfigProfile TOML for light-tier agents (gsd-research-synthesizer,
  gsd-codebase-mapper, gsd-plan-checker, and 8 others identified via
  AGENT_DEFAULT_TIERS). Field names/values verified against Codex schema
  (profile_toml.rs / config_types.rs Verbosity enum). Non-light agents
  are unaffected.

- Add generateCodexSkillMetadataYaml() and writeCodexSkillMetadataFiles():
  after installRuntimeArtifacts, iterate every gsd-* skill directory,
  read the short-description already emitted in the SKILL.md frontmatter
  by convertClaudeCommandToCodexSkill, and write agents/openai.yaml with
  interface.display_name and interface.short_description for the Codex
  TUI skill picker chip.
  - yamlQuote (JSON.stringify) handles all YAML-unsafe chars.
  - User-owned gsd-dev-preferences dir is never overwritten.
  - Errors per-skill are swallowed so a bad SKILL.md can't abort install.
  - agents/openai.yaml is covered by the snapshot/rollback system and
    manifest hash (writeManifest hashes skill dirs recursively).
  - Uninstall symmetry: _removeGsdEntries removes whole gsd-* dirs.

- 21 new tests in codex-config.test.cjs covering service_tier/verbosity
  TOML emission, YAML generation (round-trip via js-yaml), and
  writeCodexSkillMetadataFiles including an e2e integration test.

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

* fix(#774): correct docs-lint coverage — proper changeset format + USER-GUIDE entry

Rewrite the changeset fragment from old @opengsd/gsd-core:patch format to the
required type:/pr: schema so the docs-lint parser can consume it.  Add a new
"Codex skill picker and agent scheduling (#774)" section to docs/USER-GUIDE.md
describing the flex-tier scheduling and /skills TUI chip enrichments — both are
user-visible and belong in docs rather than behind a docs-exempt marker.

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:25 -04:00
Tom Boucher
dc7f1557c6 feat(#768): pre-populate settings.json permissions.allow/deny for Claude Code (#819)
* feat(#768): pre-populate settings.json permissions.allow/deny for Claude Code

Adds mergeClaudePermissions() to bin/install.js which non-destructively
appends GSD's known-safe tool-call patterns to permissions.allow and
defense-in-depth credential-file patterns to permissions.deny during
Claude Code installs. Merge is idempotent (no duplicates on reinstall)
and additive (existing user entries preserved). Uninstall removes only
the exact GSD-owned entries.

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

* chore: update changeset pr number to 819

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 18:56:20 -04:00
Viktorplus
7f868dcc6b fix: close Kimi runtime review gaps 2026-06-07 23:40:18 +02:00
Viktorplus
b5d2404b0e Merge remote-tracking branch 'upstream/next' into kimi-runtime-support 2026-06-07 22:29:45 +02:00
Viktorplus
e93568b4e2 Merge remote-tracking branch 'upstream/next' into kimi-runtime-support
# Conflicts:
#	bin/install.js
#	src/runtime-homes.cts
2026-06-07 22:27:05 +02:00
Tom Boucher
907cd01aa3 fix(#813): apply per-runtime skill path rewrites in applySurface (#817)
* fix(#813): apply per-runtime skill path rewrites in applySurface

applySurface() re-staged skill artifacts but, unlike installRuntimeArtifacts(),
never applied the per-runtime path rewrites. So /gsd:surface
(profile/enable/disable/reset) overwrote installed SKILL.md bodies with the
converter's default ~/.claude paths instead of the install target (pathPrefix),
silently regressing skill path references for every skillsKind runtime until
the next reinstall.

applySurface now mirrors installRuntimeArtifacts: for kind.kind === 'skills' it
derives pathPrefix the same way and applies applyRuntimeContentRewritesInPlace
on the staged dir before syncing.

- bin/install.js: export applyRuntimeContentRewritesInPlace
- runtime-artifact-layout.cts: carry resolved scope on Layout; export
  getInstallExports; type computePathPrefix/applyRuntimeContentRewritesInPlace
  on InstallExports
- surface.cts: lazily derive pathPrefix (only when a skills kind exists) and
  apply the rewrite via the shared getInstallExports accessor — single source of
  truth with install, only skills kinds rewritten (matches install)
- tests: regression test parameterized over cursor + codex asserting
  post-applySurface bodies carry the install pathPrefix, not ~/.claude
- CONTEXT.md: glossary updated for the applySurface rewrite parity + scope seam

Closes #813

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

* chore(#813): add changeset fragment for PR #817

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

* test(#813): normalize configDir prefix to forward slashes for Windows CI

The #813 regression assertion compared skill bodies against a raw
${configDir}/ prefix, but production derives pathPrefix via
path.resolve(configDir).replace(/\\/g, '/'). On Windows, mkdtempSync
returns backslash paths while the rewritten body uses forward slashes,
so the assertion would fail Windows-only (not covered by local gsd-test).
Normalize the expected prefix the same way production does.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 16:26:14 -04:00
Tom Boucher
ea0d8f09b1 enh(#784): emit native skills for OpenCode + Kilo runtimes (#810)
* feat(#784): emit native skills for OpenCode + Kilo runtimes

OpenCode and Kilo share a config schema and both discover on-demand
skills from skills/<name>/SKILL.md. The installer previously emitted
only flat commands (command/) and file-based agents (agents/) for these
runtimes. Add a shared OpenCode-family skill writer that stages each GSD
command as a spec-compliant SKILL.md (name matching the directory,
description 1-1024 chars), wired through the runtime artifact layout so
uninstall cleans skills/ automatically. Skills respect the active
install profile.

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

* fix(#784): correct skill body paths + preserve user dev-preferences

Address adversarial-review findings:
- Add opencode/kilo cases to _applyRuntimeRewrites so staged SKILL.md
  bodies are re-pointed from the converter's hardcoded default config dir
  to the actual install target (fixes --local / --config-dir installs;
  commands/agents already did this by applying pathPrefix pre-conversion).
- Preserve user-owned skills/gsd-dev-preferences across reinstall in
  installOpencodeFamilySkills (snapshot+restore around the gsd-* prune),
  matching installRuntimeArtifacts.
- Export installOpencodeFamilySkills and add regression tests.

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

* fix(#784): guarantee command/skill body parity, fix kilo-alt double-rewrite

Follow-up adversarial-review found the post-conversion path rewrite could
double-rewrite custom Kilo dirs (kilo -> kilo-alt -> kilo-alt-alt) because
the kilo pathPrefix is a $HOME (non-absolute) superset of the hardcoded
default base. Restructure so OpenCode/Kilo skills mirror copyFlattenedCommands
exactly: stage raw commands, apply pathPrefix BEFORE conversion via a new
shared applyOpencodeFamilyPathPrefix() helper (now used by both the command
and skill writers), then convert. This guarantees byte-for-byte command/
skill body parity for global, --local, and --config-dir installs and removes
the prefix-overlap hazard. Drop the fragile _applyRuntimeRewrites opencode/
kilo case. Strengthen the path regression test.

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

* refactor(#784): derive opencode/kilo skills from the same staged command set

Pass the installer's _stageSkills() output directly to
installOpencodeFamilySkills instead of re-staging via the layout, so the
command/ and skills/ surfaces always cover the identical profile-resolved
set — including the --minimal/--core-only alias path, which stages
differently from a plain --profile=core. Verified: minimal install now
emits 8 commands and 8 skills.

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

* chore(#784): set changeset PR number to 810

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

* test(#784): fully escape backslashes in test helper (CodeQL js/incomplete-string-escaping)

Replace the dot-only escape `replace(/[.]/g, '\\.')` with a complete
regex-escape pattern `replace(/[\\.*+?^${}()|[\]]/g, '\\$&')` so all
regex metacharacters (including backslash itself) in `defaultBase` are
safely escaped before interpolation into `new RegExp(...)`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 16:13:06 -04:00
Tom Boucher
10087a48f5 feat(#790): emit Augment slash commands (~/.augment/commands/) (#808)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 15:52:22 -04:00
Tom Boucher
5e4e7de1ff enhancement(#782): emit gsd skills to ~/.cline/skills for Cline >= v3.48 (#809)
Cline added a global skills system (~/.cline/skills/<name>/SKILL.md) in
v3.48.0, but gsd treated Cline as rules-only and emitted zero skills
(getGlobalSkillsBase('cline')=null, empty artifact kinds). This makes gsd
emit skills for Cline at global scope, alongside the existing .clinerules.

- runtime-homes: getGlobalSkillsBase('cline') -> ~/.cline/skills (was null)
- runtime-artifact-layout: cline emits a skills kind for GLOBAL scope only
  (local stays .clinerules-only), mirroring claude's scope dispatch
- install.js: convertClaudeCommandToClineSkill emits name+description-only
  SKILL.md frontmatter (Cline/agentskills.io spec; no Claude-specific
  allowed-tools/argument-hint/agent), hyphen-normalized + .cline/-rewritten
  body; global cline routed through the skills path while .clinerules is
  still written; _applyRuntimeRewrites cline case handles custom
  CLINE_CONFIG_DIR; convertClaudeToCliineMarkdown also rewrites bare
  ~/.claude and CLAUDE_CONFIG_DIR
- docs: install-on-your-runtime.md documents Cline global skills vs local rules
- tests: converter (name+description-only), global emission, skills+.clinerules
  coexistence, scope-aware layout, custom-dir paths, idempotency

Closes #782

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:48:40 -04:00
Tom Boucher
5618505f9a feat(#788): expand Qwen Code hook-event coverage (#807)
* feat(#788): expand Qwen Code hook-event coverage to 4 new events

Register SubagentStop, Stop, PreCompact (gsd-context-monitor.js) and
UserPromptSubmit (gsd-prompt-guard.js) in the Qwen Code installer.
Guard is isQwen-only — Claude Code and all other runtimes are unchanged.
Uninstall loop extended to include the 4 new event names.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(#788): reconcile to 3 Qwen-only events — defer UserPromptSubmit

gsd-prompt-guard exits unless tool_name is Write|Edit (PreToolUse
payload shape); UserPromptSubmit carries raw user-prompt text with no
tool_name field, so wiring it would be a silent no-op.  Deferred to a
follow-on issue.

Artifacts made consistent:
- bin/install.js: drop UserPromptSubmit registration block; uninstall
  loop drops UPS from event list
- .changeset/788-qwen-hook-events.md: corrected to 3 events + rationale
- docs/how-to/install-on-your-runtime.md: remove UPS row from hook table
- tests/enh-788-qwen-hook-events.test.cjs: assert UPS NOT registered;
  fix idempotency suite to persist settings between installs; drop
  UPS-specific assertions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(#788): update changeset PR number to #807

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(#788): prune stale install-bucket allowlist entry for enh-788 test

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 15:34:32 -04:00
Tom Boucher
3025a6846e fix(#812): honor COPILOT_HOME in Copilot global config-dir resolution (#814)
* fix(#812): honor COPILOT_HOME in Copilot global config-dir resolution

getGlobalConfigDir('copilot') resolved the global config directory using
only --config-dir > COPILOT_CONFIG_DIR > ~/.copilot, ignoring the
COPILOT_HOME env var. Per GitHub's Copilot CLI docs, COPILOT_HOME
overrides the default ~/.copilot location (and user-level hooks are read
from $COPILOT_HOME/hooks/), so a global --copilot install wrote all
artifacts (skills, agents, copilot-instructions.md, the gsd-session.json
hook) to ~/.copilot even when the user relocated their Copilot home,
making them undiscoverable by Copilot CLI.

Mirror the codex/CODEX_HOME branch: precedence is now
--config-dir > COPILOT_CONFIG_DIR > COPILOT_HOME > ~/.copilot. Uninstall
uses the same resolver, so it stays symmetric.

Also: document COPILOT_HOME in the installer --help notes, the
USER-GUIDE env-var table, and the installer-migrations Copilot row; and
clear COPILOT_HOME in the two default-path test suites so they stay
hermetic now that the resolver honors it.

Closes #812

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

* chore(#812): add changeset for PR #814

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:27:46 -04:00
Tom Boucher
eefef2ec19 feat(#787): elevate Cline — .clinerules/ dir form, PreToolUse hook, AGENTS.md (#803)
* feat(#787): elevate Cline — .clinerules/ dir form, PreToolUse hook, AGENTS.md

Migrate the installer's Cline output from a single-file .clinerules to the
.clinerules/ directory form (.clinerules/gsd.md), which is the prerequisite for
Cline's v3.36 hooks (a path cannot be both a file and a directory). Add a
.clinerules/hooks/PreToolUse lifecycle hook implementing Cline's JSON stdin ->
{cancel,errorMessage,contextModification} protocol; it guards .planning/
artifacts and fails open. On global installs, merge GSD instructions into the
cross-tool ~/.agents/AGENTS.md target (marker-delimited, merge-safe). A legacy
single-file .clinerules is migrated in place; --uninstall removes the new
artifacts and strips the AGENTS.md GSD block.

Also fixes the uninstall targetDir for Cline local installs (it pointed at
./.cline instead of the project root) and re-runs writeManifest after the
Cline artifacts are written so they are hash-tracked.

Self-contained: implemented independently of the #782 Cline skills work.

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

* fix(#787): address review findings

- Scope PreToolUse hook path-walk to PATH_KEY fields only (eliminates false
  positive when doc body content mentions .planning/)
- Use lstatSync + isSymbolicLink() for migration guard so GSD never writes
  through a user's symlinked .clinerules into an external directory
- Add regression tests for both cases

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(#787): set changeset pr: 803

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:20:55 -04:00
Tom Boucher
74a818308e feat(#785): write .cursor/commands/ Cursor 1.6 slash-command surface (#805)
* feat(#785): write .cursor/commands/ as Cursor 1.6 slash-command surface

Cursor 1.6 (released 2025-09-12) introduced plain-markdown slash commands
in `.cursor/commands/<name>.md` — no frontmatter, invocable via `/` in the
Agent input. GSD previously emitted only `~/.cursor/skills/` for Cursor.

This PR wires a second artifact kind for `cursor` in
`runtime-artifact-layout.cts`: `convertedCommandsKind('commands', 'gsd-',
'convertClaudeCommandToCursorCommand', configDir)`. The new kind applies the
same `convertClaudeToCursorMarkdown` transforms (tool renames, brand
substitution, slash-command normalisation) and then strips YAML frontmatter
so the output is plain prose. Skills output is unchanged.

`stageCommandsForRuntimeFlat` in `install-profiles.cts` stages each source
`.md` as a flat `<stem>.md` in a temp dir; the existing `_copyStaged` commands
path then prefixes and copies to `<configDir>/commands/`.

`.cursor/mcp.json` is explicitly OUT OF SCOPE: GSD ships no MCP server; the
`mcpServers` schema cannot be usefully populated by the installer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(#785): address review nit

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 15:07:14 -04:00
Tom Boucher
5e852611e3 feat(#786): elevate GitHub Copilot installer — lifecycle hook + AGENTS.md (#804)
* feat(#786): elevate Copilot installer with lifecycle hook + AGENTS.md

Emit a self-contained sessionStart hook config (.github/hooks/gsd-session.json
local, ~/.copilot/hooks/gsd-session.json global) and write AGENTS.md at the repo
root (Copilot CLI reads it as primary instructions) alongside
copilot-instructions.md. The hook is an inline `command` hook (no separate hook
script), so it cannot dangle. Uninstall removes both and preserves user content.

Verified against GitHub Copilot CLI primary docs: hooks-configuration (camelCase
events, version+hooks shape, inline bash/powershell command hooks) and
add-custom-instructions (AGENTS.md read at repo root as primary instructions).

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

* chore(#786): set changeset pr number to 804

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 14:53:32 -04:00
Viktorplus
64328bd2ae Merge remote-tracking branch 'upstream/next' into kimi-runtime-support
# Conflicts:
#	bin/install.js
2026-06-07 19:21:53 +02:00
Tom Boucher
7e1112c28f refactor(#56): retire legacy runtime directory helpers into runtime-homes projection (#802)
* refactor(#56): retire legacy runtime directory helpers into runtime-homes projection

Consolidate per-runtime global config-dir resolution onto the single
canonical projection runtime-homes:getGlobalConfigDir. Extend it with the
explicitDir override (CLI --config-dir) and the opencode/kilo
OPENCODE_CONFIG/KILO_CONFIG file-path precedence the installer helpers had,
making it byte-for-behavior equivalent to the old getGlobalDir across all
15 install runtimes.

Delete bin/install.js's getGlobalDir/getOpencodeGlobalDir/getKiloGlobalDir
(and the orphaned local expandTilde), repoint all 9 call-sites, and remove
getGlobalDir from module.exports (net -242 lines in the installer). Migrate
the 5 test importers to the canonical projection; harden default/XDG
assertions against ambient *_CONFIG env vars. getAgentsDir now respects
OPENCODE_CONFIG/KILO_CONFIG consistently with the installer (intentional
convergence). Update CONTEXT.md Installer Module entry.

Completes the installer-refactor chain #58 -> #60 -> #56.

Closes #56

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

* chore(#56): add changeset for runtime directory helper retirement

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 12:24:04 -04:00
Tom Boucher
2f07443119 refactor(#60): make runtime config adapter registry explicit (#795)
* refactor(#60): make runtime config adapter registry explicit

Replace scattered inline `runtime === '...'` config-mutation branching in
bin/install.js with an explicit, typed adapter registry. The new
src/runtime-config-adapter-registry.cts maps each of the 15 supported
runtimes to a config intent { installSurface, writesSharedSettings,
finishPermissionWriter }; install()/finishInstall() dispatch by resolved
intent instead of runtime-name checks (cursor/windsurf/trae collapse to one
profile-marker-only branch).

Behavior-preserving: the same config files are written for the same runtimes
(opencode still writes both settings.json and its permissions; kilo writes
only its permissions; codex minimal-mode and opencode GSD_TEST_MODE guards
unchanged). Unknown runtimes fail loudly via TypeError, with an Object.hasOwn
barrier so prototype-chain keys (__proto__/constructor) also throw rather than
returning a bogus intent. Leads the installer-refactor chain (#58 -> #60 ->
#56), building on ADR-58.

Closes #60

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

* chore(#60): add changeset for runtime config adapter registry

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

* docs(#60): register Runtime Config Adapter Registry in CONTEXT.md glossary

Per docs/contributor-standards.md, every new Module/seam must get a
`### <Name>` entry under the domain glossary. Adds the entry for the
runtime-config-adapter-registry seam introduced in this PR (interface,
policy boundary, source file, ADR-58 / #60 cross-references).

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 11:16:03 -04:00
Viktorplus
2d9e44c6a6 fix: align kimi install roots with docs 2026-06-07 12:05:13 +02:00
Viktorplus
4e12967683 Merge remote-tracking branch 'upstream/next' into kimi-runtime-support 2026-06-07 11:56:18 +02:00
Tom Boucher
cf8bd3cd5e fix(#683): auto-degrade phase execution to sequential on worktree base mismatch (#749)
* fix(#683): auto-degrade phase execution to sequential on worktree base mismatch

Claude Code forks worktree-isolated executors off the repository default
branch (origin/HEAD), not the orchestrator's HEAD. Running /gsd-execute-phase
on a branch diverged from the default (unmerged milestone/feature branch) left
every executor without the phase's plan files and tripped the
worktree-branch-check guard with `exit 42` — 100% reproducible, all OSes.

- New module src/worktree-base-ref.cts: HEAD-vs-fork-base drift detection
  (origin/HEAD with symbolic-ref fallback) and no-clobber worktree.baseRef
  management, exposed as `worktree base-check` / `worktree set-baseref`.
- execute-phase.md: pre-dispatch, for Claude Code with worktrees enabled,
  auto-degrades the run to sequential on the main tree when a base mismatch
  is detected, recommending worktree.baseRef:"head". The exit-42 guard stays
  as a backstop.
- Installer: fresh local Claude installs set worktree.baseRef:"head" in
  .claude/settings.local.json (no-clobber, respecting an explicit shared
  settings.json value); upgrades print an opt-in notice pointing at
  `gsd-tools worktree set-baseref`.
- Docs: how-to guide, CLI/config reference, planning-config cross-ref.

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

* fix(#683): auto-apply worktree.baseRef on upgrade; gate fresh+upgrade on use_worktrees

Per maintainer direction: on a local Claude Code UPGRADE, set
worktree.baseRef:"head" automatically (no opt-in notice) when the project's
workflow.use_worktrees is enabled, instead of merely printing a remediation
notice. For consistency the FRESH path is now gated the same way: both paths
compute worktrees-enabled once (bounded walk-up read of .planning/config.json,
default enabled unless workflow.use_worktrees === false) and apply the
no-clobber baseRef only when enabled — never overwriting an explicit value in
settings.local.json or a shared settings.json. gsd-tools worktree set-baseref
remains for manual use. Docs + changeset updated; tests hardened (file-exists
assertions, fresh+disabled case, upgrade idempotency).

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

* fix(#683): measure workflow byte-budget on LF, fixing Windows-only CI failure

The workflow-size-budget test failed only on Windows: git checks out the .md
files as CRLF (no eol=lf in .gitattributes) and byteCount used
fs.statSync().size (raw on-disk bytes), counting an extra \r per line. That
inflated execute-phase.md — the XL high-water-mark file pinned near its ceiling
by the tighten-only ratchet — from 88492 LF bytes to ~90245 on Windows, over
the 90000 XL ceiling, while passing on the LF-checkout Mac/Linux runners.

The ceilings are explicitly "calibrated against raw `wc -c`" on an LF checkout,
so the measurement should be LF-based on every platform. byteCount now reads the
file and counts Buffer.byteLength after stripping CR, making the budget
platform-independent (a no-op on LF checkouts; verified statSync === normalized
for all 88 workflow files). No ceilings changed. Added a regression test
asserting CRLF and LF content of the same file count identically.

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

* fix(#683): make worktree-base-ref test path mocks Windows-safe (path.join)

tests/worktree-base-ref.test.cjs keyed its injected readFile/writeFile mocks
(and a few expected `file` values) with forward-slash template literals like
`${claudeDir}/settings.local.json`. The module composes those paths with
path.join(), which emits backslashes on Windows, so the mock keys never matched
the module's lookup → readFile returned null → resolveEffectiveBaseRef /
cmdWorktreeBaseCheck / cmdWorktreeSetBaseRef (and the JSONC variants) failed on
the Windows full-test runner only (they passed on Mac/Linux, and the install
tests passed because they use the real filesystem). The module is correct;
only the test fixtures hardcoded '/'.

All mock keys and path assertions now use path.join(base, ...) mirroring the
module, so they match on every platform (no-op on POSIX). 19 path references
across 16 lines.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 23:40:24 -04:00
Tom Boucher
1c98d0c5aa refactor(#712): replace Codex slash-command denylist lookbehind with positive-boundary match (#747)
* refactor(#712): replace Codex slash-command denylist lookbehind with positive-boundary match

The hyphen-style /gsd-<cmd> -> $gsd-<cmd> conversion in
convertSlashCommandsToCodexSkillMentions used a negative-lookbehind DENYLIST
enumerating characters that must NOT precede a real mention. #637 -> #704 showed
this is an unbounded treadmill: each new unanticipated preceding char (/, ., word
chars, then }, )) leaked the same path-corruption bug class, and a backtick-wrapped
path (`/gsd-core/workflows/update.md`) still leaked through.

Replace it with a POSITIVE two-boundary definition of a mention:
  1. Left: opens at start-of-string, whitespace, or an inline-prose delimiter
     (backtick/quote/paren/bracket).
  2. Right: the command token is not followed by a path separator `/` (a path
     continues, a command does not). The (?![a-z0-9/-]) lookahead also blocks
     regex backtracking to a shorter command.

This closes the whole class by construction (no preceding-char denylist to
maintain) and fixes the backtick-wrapped-path corruption the #704 test
documented as a pre-existing gap, while preserving conversion of legitimate
backtick-wrapped mentions (e.g. CONTEXT.md's `/gsd-execute-phase` lists).

The colon-style /gsd: replace is intentionally left unguarded (it never appears
as a filesystem path segment) and is annotated as such.

Tests assert the regex directly (function now exported) across a convert/
don't-convert matrix plus one end-to-end pipeline assertion for the headline
backtick-path case.

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

* chore(#712): add changeset fragment for PR #747

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 19:40:52 -04:00
Viktorplus
40c3fec397 fix: align kimi runtime support with review 2026-06-07 01:27:12 +02:00
Viktorplus
9f84d50a0d docs: clarify Kimi install roots 2026-06-06 22:16:21 +02:00
Viktorplus
ca0370a7b5 fix(04-02): run Kimi install migrations before artifacts
- move Kimi global install after migration gate
- align migration integration assertions with Kimi skills and agents contract
2026-06-06 22:16:21 +02:00
Viktorplus
fba6cfe63d feat(03-02): install Kimi global agent artifacts
- stage generated Kimi root and subagent YAML/prompt files under agents/
- copy and remove only GSD-owned Kimi agent files while preserving user files
- print explicit kimi --agent-file launch hint
2026-06-06 22:16:20 +02:00
Viktorplus
8508ad35d8 feat(03-03): implement Kimi tool module mapper
- add exported Claude-to-Kimi tool conversion helpers
- route Kimi agent artifact tools through mapper diagnostics
2026-06-06 22:16:20 +02:00