After #1540 migrated Claude Code to skills/ format, the uninstall
added a legacy commands/gsd/ cleanup that wiped the directory without
checking for user files. Add preserve logic to the legacy cleanup path
matching what Gemini's commands/gsd/ path already has.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address review feedback: wrap writeFileSync in try/catch so restore
failures surface a clear error instead of silently losing user files.
Add comment noting the naming convention approach for future scaling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The uninstall step wipes get-shit-done/ and commands/gsd/ directories
entirely, destroying user-generated files like USER-PROFILE.md (from
/gsd:profile-user) and dev-preferences.md. These files are now read
before rmSync and restored immediately after.
Closes#1423
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Port 3 community hooks from gsd-skill-creator, gated behind hooks.community config flag. All hooks are registered on install but are no-ops unless the project config has hooks: { community: true }.
gsd-session-state.sh (SessionStart): outputs STATE.md head for orientation. gsd-validate-commit.sh (PreToolUse/Bash): blocks non-Conventional-Commits messages. gsd-phase-boundary.sh (PostToolUse/Write|Edit): warns when .planning/ files are modified.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add --augment flag to install for Augment only
- Add Augment to --all and interactive menu (now 9 runtimes + All)
- Create conversion functions for Augment skills and agents:
- convertClaudeToAugmentMarkdown: path and brand replacement
- convertClaudeCommandToAugmentSkill: skill format with adapter header
- convertClaudeAgentToAugmentAgent: agent format conversion
- copyCommandsAsAugmentSkills: copy commands as skills
- Map tool names: Bash → launch-process, Edit → str-replace-editor, etc.
- Add runtime label and uninstall support for Augment
- Add tests: augment-conversion.test.cjs with 15 test cases
- Update multi-runtime-select.test.cjs to include Augment
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude Code 2.1.88+ deprecated commands/ subdirectory discovery in favor of
skills/*/SKILL.md format. This migrates the Claude Code installer to use the
same skills pattern already used by Codex, Copilot, Cursor, Windsurf, and
Antigravity.
Key changes:
- New convertClaudeCommandToClaudeSkill() preserving allowed-tools and argument-hint
- New copyCommandsAsClaudeSkills() mirroring Copilot pattern
- Install now writes skills/gsd-*/SKILL.md instead of commands/gsd/*.md
- Legacy commands/gsd/ cleaned up during install
- Manifest tracks skills/ for Claude Code
- Uninstall handles both skills/ and legacy commands/
Fixes#1504
Supersedes #1538
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix path replacement for ~/.claude to ~/.github
The current rules miss replacing this path because the current rules look for the .claude directory with a trailing slash, which this does not have.
* Fix regex to replace trailing .claude with .github
When settings.json contains comments (// or /* */), which many CLI tools
allow, JSON.parse() fails and readSettings() silently returned {}.
This empty object was then written back by writeSettings(), destroying
the user's entire configuration.
Changes:
- Add stripJsonComments() that handles line comments, block comments,
trailing commas, and preserves comments inside string values
- readSettings() tries standard JSON first (fast path), falls back to
JSONC stripping on parse failure
- On truly malformed files (even JSONC stripping fails), return null
with a warning instead of silently returning {} — prevents data loss
- All callers of readSettings() now guard against null return to skip
settings modification rather than overwriting with empty object
Closes#1461
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The reapply-patches workflow used a two-way comparison (user's backup vs
new version) which couldn't distinguish user customizations from version
drift. This caused 10/10 files to be misclassified as "no custom content"
in real-world usage, silently discarding user modifications.
Changes:
- Rewrite workflow with three-way merge strategy (pristine baseline vs
user-modified backup vs newly installed version)
- Add critical invariant: files in gsd-local-patches/ must NEVER be
classified as "no custom content" — they were backed up because the
installer's hash check detected modifications
- Add git-aware detection path using commit history when config dir is
a git repo
- Add pristine_hashes to backup-meta.json so the reapply workflow can
verify reconstructed baseline files
- Add from_manifest_timestamp to backup-meta.json for version tracking
- Conservative default: flag as CONFLICT when uncertain, not SKIP
Closes#1469
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
convertClaudeToCodexMarkdown() was missing path replacement — unlike
Copilot/Gemini/Antigravity converters which all replace $HOME/.claude/
paths. This left hardcoded .claude references in Codex agent files,
causing ENOENT when gsd-tools.cjs tried to load from ~/.claude/ on
Codex installations.
Closes#1430
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add installSdk() and promptSdk() to the installer so users can
optionally install @gsd-build/sdk during GSD setup. The --sdk flag
installs without prompting; interactive installs get a Y/N prompt
after runtime installation completes. SDK installs use @latest with
suppressed npm noise (--force --no-fund --loglevel=error, stdio: pipe).
Cherry-picked from fix/sdk-cli-runtime-bugs (de9f18f) which was
left out of #1407.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Node v25 preserves trailing slashes in path.join, causing
writeFileSync to fail with ENOENT when the converted path
ends in '/'. Affects all Windsurf users on Node v25+.
Fixesgsd-build/get-shit-done#1392
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-#1346 GSD installs prepended [features] before bare top-level keys
in ~/.codex/config.toml, trapping keys like model="gpt-5.3-codex" under
[features] where Codex expects only booleans. The #1346 fix prevented
NEW corruption but did not repair EXISTING corrupted configs. Re-installing
GSD left the trapped keys in place, causing "invalid type: string, expected
a boolean" on every Codex launch.
repairTrappedFeaturesKeys() now detects non-boolean key-value lines inside
[features] and relocates them before the [features] header during
ensureCodexHooksFeature(), so re-installs heal previously corrupted configs.
Fixes#1379
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add validateHookFields() that strips invalid hook entries before they
cause Claude Code's Zod schema to silently discard the entire
settings.json file. Agent hooks require "prompt", command hooks require
"command", and entries without a valid hooks sub-array are removed.
Uses a clean two-pass approach: first validate and build new arrays
(no mutation inside filter predicates), then collect-and-delete empty
event keys (no delete during Object.keys iteration). Result entries
are shallow copies so the original input objects are never mutated.
Includes 24 tests covering passthrough, removal, structural invalidity,
empty cleanup, mutation safety, unknown types, and iteration safety.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two fixes for Codex config.toml compatibility:
1. ensureCodexHooksFeature: insert [features] before the first table header
instead of prepending it before all content. Prepending traps bare
top-level keys (model, model_reasoning_effort) under [features], where
Codex rejects them with "invalid type: string, expected a boolean".
2. generateCodexConfigBlock: use absolute config_file paths when targetDir
is provided. Codex ≥0.116 requires AbsolutePathBuf and cannot resolve
relative "agents/..." paths, failing with "AbsolutePathBuf deserialized
without a base path".
Fixes#1202
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds full Windsurf (by Codeium) runtime integration, following the same
pattern as the existing Cursor support. Windsurf uses .windsurf/ for
local config and ~/.windsurf/ for global config, with skills in
.windsurf/skills/ using the SKILL.md structure.
What:
- CLI flag --windsurf and interactive prompt option (8)
- Directory mapping (.windsurf local, ~/.windsurf global)
- Content converter functions (tool names, path replacements, brand refs)
- Skill copy function (copyCommandsAsWindsurfSkills)
- Agent conversion (convertClaudeAgentToWindsurfAgent)
- Install/uninstall branches
- Banner, help text, and issue template updates
- Windsurf conversion test suite (windsurf-conversion.test.cjs)
- Updated multi-runtime selection tests for 8 runtimes
Closes#1336
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Gemini CLI uses BeforeTool (not PreToolUse) for pre-tool hooks, matching
the existing pattern where AfterTool is used instead of PostToolUse. The
prompt injection guard hook was hardcoded to PreToolUse, causing Gemini
CLI to log "Invalid hook event name: PreToolUse" on startup.
Apply the same runtime-conditional mapping used for post-tool hooks, and
update the uninstall cleanup to iterate both event names.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The interactive prompt now accepts comma-separated or space-separated
choices (e.g., "1,4,6" or "1 4 6") to install multiple runtimes in
one go, without needing --all or running the installer multiple times.
- Replaced if/else-if chain with runtimeMap lookup + split parser
- Added hint text: "Select multiple: 1,4,6 or 1 4 6"
- Invalid choices silently filtered, duplicates deduplicated
- Empty input still defaults to Claude Code
- Choice "8" still selects all runtimes
Tests: 10 new tests covering comma/space/mixed separators,
deduplication, invalid input filtering, order preservation,
and source-level assertions.
Closes#1281
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The installer pathPrefix for global installs replaced os.homedir()
with ~, which does NOT expand inside double-quoted shell commands
in POSIX shells. This caused MODULE_NOT_FOUND errors when executing
commands like: node "~/.claude/get-shit-done/bin/gsd-tools.cjs"
Changed pathPrefix to use $HOME instead of ~, which correctly expands
inside double quotes. Also fixed a quoted-tilde instance in do.md.
- bin/install.js: $HOME prefix instead of ~ for global installs
- get-shit-done/workflows/do.md: node "$HOME/..." instead of "~/"
- tests/path-replacement.test.cjs: updated + 3 new regression tests
Closes#1284
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extend resolve_model_ids to accept "omit" value: returns empty string
so non-Claude runtimes (OpenCode, Codex, Gemini, etc.) use their
configured default model instead of unresolvable Claude aliases.
- resolve_model_ids: "omit" short-circuits before alias resolution
- model_overrides still respected (checked first) for explicit IDs
- Installer sets resolve_model_ids: "omit" in ~/.gsd/defaults.json
for non-Claude runtimes during install
- 4 new tests covering omit behavior and override passthrough
- Fix websearch test mocks for fs.writeSync output change (#1276)
Closes#1156
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
gsd-check-update.js looked for hooks in configDir/hooks/ (e.g.,
~/.claude/hooks/) but the installer writes hooks to
configDir/get-shit-done/hooks/. This mismatch caused false stale
hook warnings that persisted even after updating.
Also clears the update cache during install so the next session
re-evaluates hook versions with the correct path.
Closes#1249
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The gsd-context-monitor PostToolUse hook was configured without a
matcher or timeout, causing it to fire on every tool use including
Read, Glob, and Grep. When multiple Read calls happen in parallel,
some hook processes failed with errors.
Added matcher: 'Bash|Edit|Write|MultiEdit|Agent|Task' to limit the
hook to tools that actually modify context significantly. Added
timeout: 10 to prevent hangs.
Includes migration logic: existing installations without matcher/timeout
get them added on next /gsd:update.
When GSD installs codex_hooks = true under [features], any non-boolean
keys already in that section (e.g. model = "gpt-5.4") cause Codex's
TOML parser to fail with 'invalid type: string, expected a boolean'.
Root cause: TOML sections extend until the next [section] header. If
the user placed model/model_reasoning_effort under [features] (common
since Codex's own config format encourages this), GSD's installer
didn't detect or correct the structural issue.
Fix: After injecting codex_hooks, scan the [features] section for
non-boolean values and move them above [features] to the top level.
This preserves the user's keys while keeping [features] clean for
Codex's strict boolean parser.
Includes 2 regression tests:
- Detects non-boolean keys under [features] (model, model_reasoning_effort)
- Confirms boolean keys (codex_hooks, multi_agent) are not flagged
Closes#1202
* fix: universal agent name replacement for non-Claude runtimes (#766)
Adds neutralizeAgentReferences() shared function that all non-Claude
runtime converters call to replace Claude-specific references:
- 'Claude' (standalone agent name) → 'the agent'
- 'CLAUDE.md' → runtime-specific file (AGENTS.md, GEMINI.md, COPILOT.md)
- Removes 'Do NOT load full AGENTS.md' (harmful for AGENTS.md runtimes)
Preserves: 'Claude Code' (product), 'Claude Opus/Sonnet/Haiku' (models),
'claude-' prefixes (packages, CSS classes).
Integrated into: OpenCode, Gemini, Copilot, Antigravity, and Codex
converters. Claude Code converter unchanged (references are correct).
Includes 7 new tests covering all replacement rules.
Closes#766
* fix: use copilot-instructions.md instead of COPILOT.md for Copilot runtime
Addresses review from Solvely-Colin: Copilot's actual instruction file
is copilot-instructions.md, not COPILOT.md. The neutralizer was mapping
CLAUDE.md -> COPILOT.md which would reference a non-existent file.
- install.js: pass 'copilot-instructions.md' to neutralizeAgentReferences
- runtime-converters.test.cjs: update test to validate correct filename
Keep `/gsd-*` command examples slash-prefixed during Cursor conversion while still normalizing legacy `gsd:` syntax, and add regression coverage for Next Up markdown output.
Made-with: Cursor
Prevent Cursor from treating frontmatter quotes as part of skill/subagent identifiers by emitting plain name scalars, and add regression tests to lock the conversion behavior.
Made-with: Cursor
OpenCode does not recognize 'model: inherit' as a valid model identifier —
it throws ProviderModelNotFoundError when spawning any GSD subagent.
Changes:
- Remove 'model: inherit' injection for OpenCode agents in install.js
- Strip 'model:' field entirely during OpenCode frontmatter conversion
(OpenCode uses its configured default model when no model is specified)
- Update tests to verify model: inherit is NOT added
This fixes all 15 GSD agent definitions and the gsd-set-profile command
for OpenCode users.
Fixes#1156