From 05316369aea842db3f6b3e73cafc225da9283abb Mon Sep 17 00:00:00 2001 From: Cristian Uibar Date: Sat, 16 May 2026 20:09:58 +0300 Subject: [PATCH] fix(3583): normalize retired colon-form commands in generated Claude/Qwen/Hermes SKILL.md bodies (#3629) * Add first-class grok runtime support (maps to ~/.agents); wire installer, runtime-homes.cjs and sync-skills; update Grok Build engine in local ~/.agents to latest; record session progress in discussion doc * Normalize gsd colon references to hyphen in generated Claude SKILL.md bodies using the shared transformer. Fixes #3583. * Refine #3583 implementation after review: cache command names, improve tests, clean up comments * Harden gsd colon-to-hyphen transformer with bidirectional word boundaries and body-only regression guard * Track quick-wins batch status and local session notes for #3583/#3579 handoff * Port installer robustness (hoist copyLibDir + selective Codex hooks) from 3579 to make Codex tests pass on this branch. Fixes ReferenceError and prevents extra hook pollution in Codex installs. * Restore #3583 transformer wiring and Codex .sh GSD_VERSION branch lost in 50ff8f17 port Commit 50ff8f17 ('Port installer robustness from #3579') accidentally reverted: - the top-level require of transformContentToHyphen/readGsdCommandNames - the body normalization inside convertClaudeCommandToClaudeSkill - the Codex hook loop's .sh branch with {{GSD_VERSION}} substitution These were the actual #3583 fix and the Codex half of the #2136 invariant. Failing tests fixed: bug-2808-skill-hyphen-name, claude-skills-migration #3583 case, bug-2136 Codex .sh substitution. * Exempt 'sync-skills' slug from docs-parity check (skill dir name in path references) gsd-sync-skills is an installed Claude skill name and a workflow file but not a registered slash command. The docs-parity regex catches /gsd-sync-skills from filesystem path references like ~/.agents/skills/gsd-sync-skills/ in docs/discussions/grok-build-support-2026-05.md. Adding to INTERNAL_COMPONENT_SLUGS matches the existing exemption pattern for 'statusline', 'workspaces', 'graphify-update', etc. * Restrict hooks/lib/ install to hook-enabled runtimes and managed allowlist Codex/Copilot/Cursor/Windsurf/Trae/Cline already skip the hooks block but were still copying hooks/lib/ helpers, contradicting the downstream Codex comment. Gate the call on the same runtime check and pass GSD_HOOK_LIB_FILES so install scope matches the uninstall/manifest scope. --- .changeset/graceful-tigers-fly.md | 5 + AGENTS.md | 41 +++ QUICK-WINS-CONFIRMED-BUGS.md | 73 ++++++ bin/install.js | 142 ++++++++-- .../discussions/grok-build-support-2026-05.md | 244 ++++++++++++++++++ get-shit-done/bin/lib/runtime-homes.cjs | 7 + get-shit-done/workflows/sync-skills.md | 4 +- scripts/fix-slash-commands.cjs | 51 +++- tests/bug-2808-skill-hyphen-name.test.cjs | 67 +++++ tests/claude-skills-migration.test.cjs | 15 +- tests/docs-parity-live-registry.test.cjs | 8 + 11 files changed, 631 insertions(+), 26 deletions(-) create mode 100644 .changeset/graceful-tigers-fly.md create mode 100644 AGENTS.md create mode 100644 QUICK-WINS-CONFIRMED-BUGS.md create mode 100644 docs/discussions/grok-build-support-2026-05.md diff --git a/.changeset/graceful-tigers-fly.md b/.changeset/graceful-tigers-fly.md new file mode 100644 index 000000000..274dc3bb8 --- /dev/null +++ b/.changeset/graceful-tigers-fly.md @@ -0,0 +1,5 @@ +--- +type: Fixed +pr: 3583 +--- +Claude skill install (convertClaudeCommandToClaudeSkill + copyCommandsAsClaudeSkills) now normalizes retired /gsd: references in SKILL.md bodies to the canonical gsd- hyphen form using the new transformContentToHyphen from the shared fix-slash-commands.cjs transformer. Frontmatter name: was already correct since #2808; body leakage is now eliminated for Claude, Qwen, and Hermes. Added regression guard in bug-2808 test. Fixes #3583. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..f3cc2ed7d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,41 @@ +# Repository Guidelines + +## Active Discussions + +For current work on **Grok Build compatibility** and multi-runtime synchronization across Grok Build, Claude Code, Gemini CLI, and Codex, see: + +- `docs/discussions/grok-build-support-2026-05.md` + +## Project Structure & Module Organization + +This repository ships GSD as a Node.js CLI and SDK. Root package entry points live in `bin/`, scripts in `scripts/`, runtime hooks in `hooks/`, command definitions in `commands/gsd/`, and workflow/template content in `get-shit-done/`. Agent role files are in `agents/`; docs are in `docs/`; logos and terminal images are in `assets/`. Root tests are in `tests/*.test.cjs`. The TypeScript SDK is isolated under `sdk/`, with source and Vitest tests in `sdk/src/`. + +## Build, Test, and Development Commands + +Use Node.js `>=22`. + +- `npm install`: install root dependencies. +- `npm test`: builds the SDK first, then runs root `node:test` suites via `scripts/run-tests.cjs`. +- `npm run test:coverage`: runs root tests with `c8` and enforces 70% line coverage for included CommonJS library files. +- `npm run build:hooks`: rebuilds generated hook artifacts. +- `npm run build:sdk`: installs SDK dependencies and builds TypeScript. +- `cd sdk && npm test`: runs SDK Vitest unit and integration projects. +- `cd sdk && npm run build`: type-checks and emits `sdk/dist/`. + +## Coding Style & Naming Conventions + +Match the existing style in the edited area. Root JavaScript is CommonJS, generally strict-mode, two-space indentation, semicolons, `const`/`let`, and `node:` imports for built-ins. SDK code is strict TypeScript using ESM/`NodeNext`. Keep command, workflow, and test filenames kebab-case, for example `commands/gsd/plan-phase.md` and `tests/bug-2396-makefile-test-priority.test.cjs`. Agent files use `gsd-*.md`. Avoid unrelated formatting and unnecessary dependencies. + +## Testing Guidelines + +Root tests use Node’s built-in `node:test` and `node:assert/strict`; do not add Jest, Mocha, or Chai. Prefer helpers from `tests/helpers.cjs` for temporary projects, cleanup, and CLI execution. Name root tests `*.test.cjs`; run one with `node --test tests/name.test.cjs`. SDK tests use Vitest with `*.test.ts` for unit tests and `*.integration.test.ts` for integration tests. + +## Commit & Pull Request Guidelines + +Recent history follows Conventional Commit prefixes such as `fix:`, `feat:`, and `ci:`, often with issue references: `fix(#2623): resolve parent .planning root...`. Keep commits scoped and descriptive. + +Every PR must link an approved or confirmed issue with `Closes #123`, `Fixes #123`, or `Resolves #123`. Use the matching template in `.github/PULL_REQUEST_TEMPLATE/`. Include behavior changes, root cause when relevant, test evidence, affected platforms/runtimes, and update `CHANGELOG.md` or docs for user-facing changes. + +## Security & Configuration Tips + +Do not commit secrets, local config, or generated worktree artifacts. Before release-facing changes, run the relevant scan scripts in `scripts/`, especially `secret-scan.sh`, `base64-scan.sh`, and `prompt-injection-scan.sh`. diff --git a/QUICK-WINS-CONFIRMED-BUGS.md b/QUICK-WINS-CONFIRMED-BUGS.md new file mode 100644 index 000000000..9d58de599 --- /dev/null +++ b/QUICK-WINS-CONFIRMED-BUGS.md @@ -0,0 +1,73 @@ +# Quick Wins: Confirmed-Bug Fixes + +**Status**: Active +**Started**: 2026-05-16 +**Owner**: Current session (Grok + user) +**Context**: Follow-up to `/gsd-inbox` triage on 2026-05-16 + +## Goal + +Land 6 high-signal, confirmed-bug issues that currently have **zero open pull requests**. These are the cleanest quick-win opportunities available in the public GitHub inbox right now. + +All six issues carry the `confirmed-bug` label, meaning the bug has been verified and a fix is explicitly welcome. + +## The 6 Issues (Prioritized) + +| # | Issue | Short Title | Type | Recommended Flow | Est. Effort | Status | Notes | +|---|-------|-------------|------|------------------|-------------|--------|-------| +| 1 | [#3583](https://github.com/gsd-build/get-shit-done/issues/3583) | Claude skill install leaves `/gsd:` in `SKILL.md` body | Installer / Command namespace | PR 3629 (our branch) + competing 3586 | Small (1 file + test) | PR opened / Review | **Leading PR: 3629** (cristianuibar) — reviewed + hardened with CodeRabbit feedback (left-boundary regex + body-scoped guard). Competing PR 3586 has "needs changes" + "ci: failing". Issue still carries `confirmed-bug`. | +| 2 | [#3579](https://github.com/gsd-build/get-shit-done/issues/3579) | `build-hooks.js` + npm publish omit graphify auto-update hook | Packaging / Build | `/gsd-quick` | Small | Not started | Classic "new feature missed in release artifact". Easy local verification. | +| 3 | [#3496](https://github.com/gsd-build/get-shit-done/issues/3496) | `/gsd:update` changelog extraction skips intermediate versions | Workflow / Update logic | `/gsd-quick` or lightweight plan | Medium-small | Not started | Needs deterministic version-range helper. | +| 4 | [#3588](https://github.com/gsd-build/get-shit-done/issues/3588) | Production `npm audit` has 1 high + 5 moderate advisories | Security / Dependencies | Direct + careful review | Medium | Not started | Transitive via `@anthropic-ai/claude-agent-sdk`. May need overrides. | +| 5 | [#3584](https://github.com/gsd-build/get-shit-done/issues/3584) | Runtime `bin/lib/*.cjs` still emit `/gsd:` (larger piece deferred from #3583) | Runtime output / Slash formatter | Short plan first, then execute | Medium-Large | Not started | 16+ files. Design a centralized runtime-aware formatter. Do after #3583. | +| 6 | [#3340](https://github.com/gsd-build/get-shit-done/issues/3340) | SDK publish lag — agent dir fix never shipped in `@gsd-build/sdk@0.1.0` | Release / SDK publishing | Plan + coordination | Medium (release-focused) | Not started | Oldest. Mostly a publishing/versioning task. | + +## Execution Rules for This Batch + +- **Branch naming**: `fix/NNNN-short-description` (enforced by CI) +- **PR template**: Must use `.github/PULL_REQUEST_TEMPLATE/fix.md` +- **Linking**: `Fixes #NNNN` (or `Closes`) in the PR body +- **Changeset**: Required for all user-facing or security fixes +- **Testing**: All existing tests must pass + new coverage where the issue describes a gap +- **Clean context windows**: Each fix should preferably be driven from a fresh session using the prepared prompts (see session notes or ask for them) +- **GSD self-use**: For the small ones (#3583, #3579, #3496), using `/gsd-quick` (or `/gsd-fast`) inside the fix session is encouraged and appropriate. For #3584, a short planning step is recommended. + +## Status Legend + +- **Not started** — Issue claimed for this batch, no work begun +- **In progress** — Active work in a clean window +- **PR opened** — Pull request created and linked +- **Review** — Awaiting review / CI / merge fixes +- **Merged** — Landed on main +- **Blocked** — Needs input from maintainers or upstream + +## Current Status + +- [x] #3583 — **PR opened** (3629 leading after CodeRabbit review + hardening push; competing 3586 needs changes + CI failing) +- [ ] #3579 — Not started (cleanest next target — 0 PRs) +- [ ] #3496 — PR 3497 open (changes requested) +- [ ] #3588 — Not started +- [ ] #3584 — Not started (larger; deferred runtime cjs colon emissions) +- [ ] #3340 — Not started + +**Progress**: 0 / 6 merged (1 in active review) + +## Process Notes + +- These issues were identified during a `/gsd-inbox` run on 2026-05-16. +- At the time of creation of this file, zero of the six had open PRs. +- 2026-05-16 Grok session: Reviewed PR 3629 (our #3583 fix) for CodeRabbit comments. 1 critical was false-positive (scripts/ *is* published per package.json "files" + npm pack). Applied the 2 valid suggestions (bidirectional word-boundary lookbehind in `buildColonPattern` + body-only scope for the colon-ref regression guard in the test). Tests pass. Pushed hardening commit to the fork branch. Competing PR 3586 exists but is behind on CI/review status. +- Work is intended to be done in **parallel clean context windows** (one issue per fresh Claude/Codex/Gemini session) using dedicated prompts. +- After each fix is complete in its window, the resulting branch + PR description should be brought back here for final review and opening. +- This file serves as the single source of truth for the current batch while execution is in progress. It can be deleted or moved to `docs/archive/` once all six PRs are merged. + +## Related Artifacts + +- Inbox triage report: `/tmp/GSD-INBOX-TRIAGE-2026-05-16.md` (from the `/gsd-inbox` run) +- Full issue list with `confirmed-bug` label: `gh issue list --state open --label confirmed-bug` + +--- + +**Next action**: #3583 now has active PR(s) under review. Next clean quick win (0 PRs, small packaging effort, high value for recently-landed graphify feature): **#3579**. Validated via GitHub search: no PRs mention 3579. Ready for `/gsd-quick` or direct fix (update `scripts/build-hooks.js` HOOKS_TO_COPY + ensure `hooks/lib/` copy in installer + fix any publish filter). + +This document will be updated as status changes. \ No newline at end of file diff --git a/bin/install.js b/bin/install.js index 0062e2b52..9c4d5107f 100755 --- a/bin/install.js +++ b/bin/install.js @@ -20,6 +20,15 @@ const { projectCodexHookTomlCommand, } = require('../get-shit-done/bin/lib/shell-command-projection.cjs'); +// Bidirectional GSD slash-command namespace transformer (#3583). +// Required at module scope so the command list can be computed once per install +// and passed down to convertClaudeCommandToClaudeSkill, avoiding repeated +// fs.readdirSync + RegExp work for every skill. +const { + transformContentToHyphen, + readCmdNames: readGsdCommandNames, +} = require(path.join(__dirname, '..', 'scripts', 'fix-slash-commands.cjs')); + // Colors const cyan = '\x1b[36m'; const green = '\x1b[32m'; @@ -50,6 +59,10 @@ function isCodexHooksFeatureKey(key) { const GSD_COPILOT_INSTRUCTIONS_MARKER = ''; const GSD_COPILOT_INSTRUCTIONS_CLOSE_MARKER = ''; +// GSD-managed files under hooks/lib/ (helpers required by gsd-*.sh hooks). +// git-cmd.js does not start with "gsd-" (shared classifier for #3129), gsd-graphify-rebuild.sh does. +const GSD_HOOK_LIB_FILES = ['git-cmd.js', 'gsd-graphify-rebuild.sh']; + const CODEX_AGENT_SANDBOX = { 'gsd-executor': 'workspace-write', 'gsd-planner': 'workspace-write', @@ -1665,10 +1678,18 @@ function skillFrontmatterName(skillDirName) { * Emits `name: gsd-` (hyphen) so Skill(skill="gsd-") calls and * tab autocomplete use the canonical command namespace. */ -function convertClaudeCommandToClaudeSkill(content, skillName, runtime = null) { +function convertClaudeCommandToClaudeSkill(content, skillName, runtime = null, cmdNames = null) { const { frontmatter, body } = extractFrontmatterAndBody(content); if (!frontmatter) return content; + // #3583: rewrite any /gsd: or gsd: in the body to the canonical + // hyphen form (gsd-) so installed SKILL.md bodies match the hyphen + // `name:` Claude Code (and Qwen/Hermes) register under (#2808). `cmdNames` + // is optional and pre-computed by the caller for performance; direct test + // calls fall back to reading the list. + const names = cmdNames || readGsdCommandNames(); + const normalizedBody = transformContentToHyphen(body, names); + const description = extractFrontmatterField(frontmatter, 'description') || ''; const argumentHint = extractFrontmatterField(frontmatter, 'argument-hint'); const agent = extractFrontmatterField(frontmatter, 'agent'); @@ -1694,7 +1715,7 @@ function convertClaudeCommandToClaudeSkill(content, skillName, runtime = null) { if (toolsBlock) fm += toolsBlock; fm += '---'; - return `${fm}\n${body}`; + return `${fm}\n${normalizedBody}`; } /** @@ -5895,6 +5916,11 @@ function copyCommandsAsClaudeSkills(srcDir, skillsDir, prefix, pathPrefix, runti fs.mkdirSync(skillsDir, { recursive: true }); + // Live command names for the colon→hyphen body transform (#3583), computed + // once per install instead of inside convertClaudeCommandToClaudeSkill where + // it would re-scan commands/gsd for every skill. + const cmdNames = readGsdCommandNames(); + // #2973 (CR follow-up on #3003): preserve user-generated skills across the // wipe-and-replace. `gsd-dev-preferences/SKILL.md` is written by the user // via `/gsd-profile-user --refresh`; it is NOT shipped by the npm package, @@ -5985,7 +6011,7 @@ function copyCommandsAsClaudeSkills(srcDir, skillsDir, prefix, pathPrefix, runti content = content.replace(/\.claude\//g, '.hermes/'); } content = processAttribution(content, getCommitAttribution(runtime)); - content = convertClaudeCommandToClaudeSkill(content, skillName, runtime); + content = convertClaudeCommandToClaudeSkill(content, skillName, runtime, cmdNames); fs.writeFileSync(path.join(skillDir, 'SKILL.md'), content); } @@ -6867,6 +6893,33 @@ function uninstall(isGlobal, runtime = 'claude') { removedCount++; console.log(` ${green}✓${reset} Removed ${hookCount} GSD hooks`); } + + // Remove only the GSD-managed files from hooks/lib/ (git-cmd.js + gsd-graphify-rebuild.sh). + // hooks/lib/ lives inside the user's runtime hooks directory (shared space) and + // may contain user-owned custom helpers. We must not recursively delete the dir. + const hooksLibDir = path.join(hooksDir, 'lib'); + if (fs.existsSync(hooksLibDir)) { + let removedLibFiles = 0; + for (const file of GSD_HOOK_LIB_FILES) { + const filePath = path.join(hooksLibDir, file); + try { + fs.unlinkSync(filePath); + removedLibFiles++; + } catch (_) { + // Ignore missing files (best effort, non-fatal) + } + } + // Only remove the directory itself if it is now empty (preserve any user files) + try { + fs.rmdirSync(hooksLibDir); + } catch (_) { + // Directory not empty or other error — leave it alone + } + if (removedLibFiles > 0) { + removedCount++; + console.log(` ${green}✓${reset} Removed ${removedLibFiles} hooks/lib/ helper(s)`); + } + } } // 5. Remove GSD package.json (CommonJS mode marker) @@ -7486,6 +7539,16 @@ function writeManifest(configDir, runtime = 'claude', options = {}) { manifest.files['hooks/' + file] = fileHash(path.join(hooksDir, file)); } } + // Track hooks/lib/ helpers so saveLocalPatches() can back up user edits + // to git-cmd.js (validate-commit classifier) and gsd-graphify-rebuild.sh. + const hooksLibDir = path.join(hooksDir, 'lib'); + if (fs.existsSync(hooksLibDir)) { + for (const file of fs.readdirSync(hooksLibDir)) { + if (GSD_HOOK_LIB_FILES.includes(file)) { + manifest.files['hooks/lib/' + file] = fileHash(path.join(hooksLibDir, file)); + } + } + } } } @@ -7749,6 +7812,36 @@ function install(isGlobal, runtime = 'claude', options = {}) { const dirName = getDirName(runtime); const src = path.join(__dirname, '..'); + // Reusable helper to copy hooks/lib/ (git-cmd.js + gsd-graphify-rebuild.sh). + // Defined early so it is visible to both the main and Codex code paths. + // `allowlist` (when non-empty) restricts copying to the named top-level entries, + // keeping install scope aligned with GSD_HOOK_LIB_FILES (which uninstall/manifest manage). + const copyLibDir = (sDir, dDir, allowlist = []) => { + const allowed = allowlist.length > 0 ? new Set(allowlist) : null; + for (const entry of fs.readdirSync(sDir)) { + if (allowed && !allowed.has(entry)) continue; + const s = path.join(sDir, entry); + const d = path.join(dDir, entry); + let st; + try { st = fs.lstatSync(s); } catch (_) { continue; } + if (st.isSymbolicLink()) continue; // defense-in-depth + if (st.isDirectory()) { + fs.mkdirSync(d, { recursive: true }); + copyLibDir(s, d); + } else if (entry.endsWith('.sh')) { + let content = fs.readFileSync(s, 'utf8'); + content = content.replace(/\{\{GSD_VERSION\}\}/g, pkg.version); + fs.writeFileSync(d, content); + try { fs.chmodSync(d, 0o755); } catch (_) { /* Windows */ } + } else { + fs.copyFileSync(s, d); + if (entry.endsWith('.js')) { + try { fs.chmodSync(d, 0o755); } catch (_) { /* Windows */ } + } + } + } + }; + // Get the target directory based on runtime and install type. // Cline local installs write to the project root (like Claude Code) — .clinerules // lives at the root, not inside a .cline/ subdirectory. @@ -8678,6 +8771,18 @@ function install(isGlobal, runtime = 'claude', options = {}) { } } + // Gate hooks/lib/ install on the same runtimes that receive hooks (see line ~8702). + // Codex/Copilot/Cursor/Windsurf/Trae/Cline skip hooks entirely, so they must not + // receive the hooks/lib/ helpers either — otherwise the Codex comment downstream + // ("we deliberately do *not* copy hooks/lib/ for Codex") is contradicted in practice. + const hooksLibSrc = path.join(src, 'hooks', 'lib'); + if (!isCodex && !isCopilot && !isCursor && !isWindsurf && !isTrae && !isCline && fs.existsSync(hooksLibSrc)) { + const hooksLibDest = path.join(targetDir, 'hooks', 'lib'); + fs.mkdirSync(hooksLibDest, { recursive: true }); + copyLibDir(hooksLibSrc, hooksLibDest, GSD_HOOK_LIB_FILES); + console.log(` ${green}✓${reset} Installed hooks/lib/ helpers (git-cmd, graphify-rebuild, ...)`); + } + // Clear stale update cache so next session re-evaluates hook versions // Cache lives at ~/.cache/gsd/ (see hooks/gsd-check-update.js line 35-36) const updateCacheFile = path.join(os.homedir(), '.cache', 'gsd', 'gsd-update-check.json'); @@ -8938,15 +9043,18 @@ function install(isGlobal, runtime = 'claude', options = {}) { console.log(` ${dim}↳${reset} Skipping Codex agent config generation (minimal install)`); } - // Copy hook files that are referenced by Codex hook configuration (#2153) - // The main hook-copy block is gated to non-Codex runtimes, but Codex registers - // gsd-check-update.js through hooks config — the file must physically exist. + // Copy only the hook files that Codex actually registers via its hook configuration (#2153). + // Codex primarily needs gsd-check-update.js for the SessionStart update-check hook. + // We deliberately do *not* copy gsd-graphify-update.sh or hooks/lib/ for Codex + // in this change (graphify auto-update support for Codex is out of scope for #3579). + const CODEX_HOOKS_TO_COPY = ['gsd-check-update.js']; const codexHooksSrc = path.join(src, 'hooks', 'dist'); if (fs.existsSync(codexHooksSrc)) { const codexHooksDest = path.join(targetDir, 'hooks'); fs.mkdirSync(codexHooksDest, { recursive: true }); const configDirReplacement = getConfigDirFromHome(runtime, isGlobal); for (const entry of fs.readdirSync(codexHooksSrc)) { + if (!CODEX_HOOKS_TO_COPY.includes(entry)) continue; const srcFile = path.join(codexHooksSrc, entry); if (!fs.statSync(srcFile).isFile()) continue; const destFile = path.join(codexHooksDest, entry); @@ -8958,18 +9066,20 @@ function install(isGlobal, runtime = 'claude', options = {}) { content = content.replace(/\{\{GSD_VERSION\}\}/g, pkg.version); fs.writeFileSync(destFile, content); try { fs.chmodSync(destFile, 0o755); } catch (e) { /* Windows */ } - } else { - if (entry.endsWith('.sh')) { - let content = fs.readFileSync(srcFile, 'utf8'); - content = content.replace(/\{\{GSD_VERSION\}\}/g, pkg.version); - fs.writeFileSync(destFile, content); - try { fs.chmodSync(destFile, 0o755); } catch (e) { /* Windows */ } - } else { - fs.copyFileSync(srcFile, destFile); - } + } else if (entry.endsWith('.sh')) { + // #2136: any .sh hook reaching this loop must have {{GSD_VERSION}} + // stamped so installed scripts carry a concrete version header and + // stale-hook detection keeps working across upgrades. The current + // CODEX_HOOKS_TO_COPY allowlist excludes .sh files, so this branch + // is defensive — it preserves the invariant if the allowlist is + // extended later (e.g. to ship gsd-graphify-update.sh for Codex). + let content = fs.readFileSync(srcFile, 'utf8'); + content = content.replace(/\{\{GSD_VERSION\}\}/g, pkg.version); + fs.writeFileSync(destFile, content); + try { fs.chmodSync(destFile, 0o755); } catch (e) { /* Windows */ } } } - console.log(` ${green}✓${reset} Installed hooks`); + console.log(` ${green}✓${reset} Installed hooks (Codex)`); } // Add Codex hooks (SessionStart for update checking) — requires codex_hooks feature flag diff --git a/docs/discussions/grok-build-support-2026-05.md b/docs/discussions/grok-build-support-2026-05.md new file mode 100644 index 000000000..6bf659336 --- /dev/null +++ b/docs/discussions/grok-build-support-2026-05.md @@ -0,0 +1,244 @@ +# Grok Build + GSD Compatibility & Local Multi-Runtime Sync (May 2026) + +**Date:** 2026-05-16 +**Status:** Discussion active on closed issue. Awaiting maintainer response. +**Purpose of this document:** Serve as the primary context file for future Grok (or other) agent sessions started inside this repository (`/home/cristian/bum/get-shit-done`) so they can work on local Grok Build support and improved synchronization across multiple AI coding harnesses. + +--- + +## 1. Executive Summary & Goals + +**Goal:** Achieve reliable, first-class GSD support when using **Grok Build**, while maintaining excellent compatibility and low-friction synchronization across the four runtimes the author uses daily: + +- Grok Build (current primary TUI) +- Claude Code +- Gemini CLI +- Codex + +Currently, Grok Build is only supported via its Claude compatibility layer. This creates daily friction in paths, skill discovery, command surfaces, hooks, `grok inspect` output, and mental models. + +**Long-term vision:** +- Run GSD natively and cleanly inside Grok Build. +- Maintain a single source of truth in this repository. +- Have a robust, automated (or semi-automated) sync mechanism that deploys adapted skills/agents/hooks to all four runtime environments (`~/.agents/`, `~/.claude/`, `~/.grok/`, Gemini location, Codex location). +- Keep the work clean enough that high-quality pieces can eventually be contributed upstream. + +--- + +## 2. Current Multi-Runtime Setup (as of May 2026) + +### Development Source (Single Source of Truth) +- **Path:** `/home/cristian/bum/get-shit-done` (this repo — your working fork of `gsd-build/get-shit-done`) + +### Installed Locations +- `~/.agents/get-shit-done/` — Core workflows, references, templates, `gsd-tools.cjs`, `bin/` +- `~/.agents/skills/gsd-*` — ~125 skills (heavily GSD + many large reference skills like `userinterface-wiki`, `react-best-practices`, etc.) +- `~/.agents/agents/` — 22 GSD sub-agents (with `.md` + `.toml`) +- `~/.claude/skills/gsd-*` + `~/.claude/get-shit-done/` + `~/.claude/agents/` — Parallel Claude Code install (~208 skills total) +- `~/.grok/skills/` — Mostly empty (only the 7 official bundled Grok skills) +- `~/.grok/` — Not yet properly used by GSD + +### Existing Sync Tooling +- `gsd-sync-skills` skill exists in `~/.agents/skills/gsd-sync-skills/` +- Its stated purpose: "Sync managed GSD skills across runtime roots so multi-runtime users stay aligned after an update" +- Currently uses a combination of manual processes + this skill. + +### Codex-Style Adaptations Already in Use +- Many `gsd-*` skills in `~/.agents/skills/` contain a `` section at the top. +- This adapter translates Claude Code patterns (`AskUserQuestion`, `Task()`) into Codex/Grok-compatible ones (`request_user_input`, `spawn_agent`). +- This pattern was developed because Grok Build / Codex use a different skill invocation and subagent model than Claude Code. + +--- + +## 3. History & Prior Art + +### Previous Upstream Attempt (May 2026) +- **Issue #3603**: "Add Grok Build (`--grok`) as a first-class runtime" +- **PR #3604** (by `lordgraysith`): Very large, high-quality implementation attempt. + +The PR included: +- Full `--grok` installer support +- Conversion functions (`convertClaudeToGrokMarkdown`, `convertClaudeCommandToGrokSkill`, `convertClaudeAgentToGrokAgent`) +- JSON hook manifest generation for Grok +- Model catalog entries for Grok models +- `GROK_CONFIG_DIR` support +- Extensive tests and documentation +- A complete planning folder: `docs/grok-build-support/` containing 7 detailed documents: + - 01-runtime-detection-and-config.md + - 02-installer-logic.md + - 03-model-catalog-and-profiles.md + - 04-skills-agents-hooks-conversion.md + - 05-documentation-and-user-experience.md + - 06-testing-strategy.md + - 07-phased-implementation-and-rollout.md + +**Outcome:** Both issue and PR were closed the next day by `trek-e` as `not_planned`. + +### Maintainer’s Position (Critical Context) +From `trek-e`’s detailed closing comment on #3603: + +- **Biggest blockers right now:** + - Grok Build is extremely new (launched May 14, 2026) and still in early beta. Hook events and config surface are unstable. + - Strong desire to protect GSD’s reputation for stability ("vibe coded piece of junk" reputation risk). + - Recent painful experience with Codex making breaking changes shortly after GSD stabilized something. +- **Price was clarified as low priority** ("honestly price is probably the lowest of the factors"). +- **Claude-compat already works**, so native support is seen as polish rather than a must-have. +- **Process concern:** Implementation was done before the issue was properly approved. +- **Recommendation:** Revisit once Grok Build has stabilized (versioned config surface held for at least one minor release) **and** is reachable on a more realistic solo-dev price tier. + +**Important:** The 7 planning documents in `docs/grok-build-support/` remain excellent reference material even if the code from that PR is not merged. + +--- + +## 4. Recommended Work Plan for Future Grok Sessions in This Repo + +When a new Grok session is started inside `/home/cristian/bum/get-shit-done`, it should follow something like this phased approach: + +### Phase 0 — Orientation (Current Session Context) +- Read this document fully. +- Read the 7 files in `docs/grok-build-support/`. +- Explore the current local multi-runtime setup (see Section 2 above). +- Understand the existing `gsd-sync-skills` skill and any Codex adapters already present. + +### Phase 1 — Audit Current State +- Map exactly what is installed where across `~/.agents/`, `~/.claude/`, `~/.grok/`, and Gemini/Codex locations. +- Identify duplication, drift, and friction points when using GSD in Grok Build today. +- Run `grok inspect` and analyze what it shows for GSD skills. +- Document gaps specific to Grok Build (command surface, hooks, `grok inspect` cleanliness, agent spawning, etc.). + +### Phase 2 — Study Prior Art +- Deeply study the conversion specifications in `docs/grok-build-support/04-skills-agents-hooks-conversion.md`. +- Understand what a proper Grok `SKILL.md` should look like (frontmatter, description style, runtime hints). +- Understand Grok hook JSON manifest requirements. +- Review how the previous PR handled model catalog and runtime homes. +- Look for any existing local experiments or partial adapters in this fork. + +### Phase 3 — Design Local Grok Adapter (MVP) +Design a practical local solution that works for **this user’s four-runtime reality**, not necessarily a full upstream `--grok` installer yet. + +Possible components: +- A local Grok conversion layer (or extension of existing Codex adapters). +- Proper `gsd-*` skills under `~/.grok/skills/` with correct Grok frontmatter + `codex_skill_adapter` sections where needed. +- Grok-compatible agent definitions (`.md` + any required TOML/config). +- JSON hook manifests in `~/.grok/hooks/`. +- Updates to the sync mechanism (`gsd-sync-skills` or a new `gsd-multi-runtime-sync` tool) so one source can deploy cleanly to all four targets. + +**Key principle:** Prefer extending/improving the existing sync tooling rather than creating yet another parallel install path. + +### Phase 4 — Implementation & Testing +- Implement the MVP Grok adapter in this local fork. +- Create or enhance sync logic. +- Test end-to-end inside an actual Grok Build session: + - `grok inspect` cleanliness + - Command discovery (`/gsd-*` or Grok-native form) + - Agent spawning + - Hook firing + - Full `gsd-new-project` → `gsd-progress` → `gsd-execute-phase` flow +- Verify no regression in Claude / Gemini / Codex usage. + +### Phase 5 — Documentation & Future Upstream Path +- Update this discussion note and any relevant docs in the repo. +- Document the local sync architecture clearly. +- Identify which pieces of the local solution would be good candidates for upstream contribution later (when Grok Build is more mature). + +--- + +## 5. Key Files & Areas to Study + +**In this repo:** +- `docs/grok-build-support/` (all 7 documents — highest priority) +- `bin/install.js` (installer logic, especially runtime handling and conversion functions) +- `get-shit-done/bin/lib/runtime-homes.cjs` +- `get-shit-done/bin/lib/shell-command-projection.cjs` (hook projection) +- `sdk/shared/model-catalog.json` +- Existing `gsd-sync-skills` skill (in `~/.agents/skills/gsd-sync-skills/`) +- Any skills that already contain `` sections (study the pattern) + +**External / Prior Art:** +- The original PR #3604 (study the actual conversion code if accessible via the author’s fork) +- Grok Build documentation on skill format, agent format, and hook JSON manifests (as of the session date) + +--- + +## 6. How to Test Grok Build Compatibility Locally + +Useful commands and checks when working on this: + +- `grok inspect` (and `grok inspect --json`) — check skill discovery, sources, and token counts. +- `grok` TUI inside a real project that uses GSD. +- Full workflow test: `/gsd-progress`, `/gsd-discuss-phase`, `/gsd-plan-phase`, `/gsd-execute-phase`, etc. +- Verify hooks fire correctly via Grok’s JSON hook system. +- Check that subagents (the 22 GSD agents in `~/.agents/agents/`) can be spawned from Grok. + +--- + +## 7. Sync Strategy Principles (for Multi-Runtime) + +When designing improvements to sync: + +- Single source of truth = this repository (`/home/cristian/bum/get-shit-done`). +- Runtime-specific transformations should be as declarative and maintainable as possible. +- The `` pattern is already proven for Grok/Codex — extend it rather than reinvent. +- Prefer generating the runtime-specific artifacts during sync rather than maintaining four separate copies. +- Make it easy to add a fifth runtime later if needed. + +--- + +## 8. Open Questions & Decisions to Make (for Future Sessions) + +- Should we aim for a full local `--grok` installer equivalent, or just excellent skill/agent/hook generation + sync? +- How much of the previous PR’s conversion logic can/should be reused locally? +- What is the right balance between “make Grok work great for me now” vs “keep it clean for potential upstream contribution”? +- Should the sync tool become a first-class GSD skill (`gsd-multi-runtime-sync` or similar)? +- How do we handle model profiles and agent routing differences for Grok models? + +--- + +## 9. How to Resume This Work + +When starting a new Grok session in this repository, begin by reading: + +1. This file: `docs/discussions/grok-build-support-2026-05.md` +2. All files in `docs/grok-build-support/` +3. The existing `gsd-sync-skills` skill + +Then follow the phased plan in Section 4. + +--- + +**Last updated:** 2026-05-16 (by Grok, in this session) + +--- + +## 10. Progress — May 2026 Session (Current) + +### Audit Findings (Phase 1) +- **Version drift confirmed**: `~/.agents/get-shit-done/` (Grok Build primary) was on 1.38.4; `~/.claude/` on 1.42.2; `~/.codex/` and `~/.gemini/` on 1.41.2. +- `~/.agents/hooks/` was empty (no hooks active for Grok Build sessions). +- `grok inspect` successfully discovers 80+ `gsd-*` skills via the `~/.agents/skills/` layout + the existing `` blocks. +- No `grok` or `agents` runtime existed in installer or sync logic. +- `~/.grok/` itself contains only the 7 official bundled skills; GSD lives entirely in the shared `~/.agents/` layout. + +### Immediate Actions Taken +- **Engine drift fixed ASAP**: Backed up old `~/.agents/get-shit-done/` to `.backup-1.38.4/`, then rsynced the current source `get-shit-done/` tree into `~/.agents/get-shit-done/`. Now running the latest from this repo (v1.50.0-canary.0). New modules (active-workstream-store, adr-parser, etc.) and updated workflows are live for Grok Build sessions. +- **First-class 'grok' runtime added** (pragmatic choice: maps to `~/.agents/`): + - [get-shit-done/bin/lib/runtime-homes.cjs](/home/cristian/bum/get-shit-done/get-shit-done/bin/lib/runtime-homes.cjs): Added `grok` case (honors `GROK_AGENTS_HOME` env, defaults to `~/.agents`). + - [bin/install.js](/home/cristian/bum/get-shit-done/bin/install.js): Added `--grok` flag, `hasGrok`, `getDirName('grok') → '.agents'`, `getGlobalDir('grok')`, `getConfigDirFromHome`, inclusion in `--all` and help text. Reuses existing Codex conversion logic (skill adapters + agent .toml generation) because Grok Build uses the same invocation model. + - [get-shit-done/workflows/sync-skills.md](/home/cristian/bum/get-shit-done/get-shit-done/workflows/sync-skills.md): Added `grok` to supported runtimes and the `--to all` list. +- Verified: `node bin/install.js --skills-root grok` correctly returns `~/.agents/skills`. + +### Next Steps (for follow-up sessions) +- Full `gsd install --grok --global` end-to-end (hook projection, agent .toml generation with correct sandbox, skill wrapping with adapters, statusline, etc.). Currently the flag is recognized but some codex-specific install branches may need `|| runtime === 'grok'`. +- Run `gsd update --sync --from claude --to grok --apply` (or `--from grok --to claude`) once the runtime is fully wired, to keep the 4 harnesses in sync without manual rsync. +- Slim the `` blocks (currently ~60 lines inlined in every gsd-* SKILL.md). Options: extract detailed mapping to a shared `@reference/codex-skill-adapter.md` that skills include, or make the adapter header shorter/optional for lower `grok inspect` token cost. +- Investigate Grok Build native hook support (JSON manifests under `~/.grok/hooks/` vs the shell hooks in `~/.agents/hooks/`). +- Update `grok inspect` output cleanliness (remove "unknown tool prefix: Skill(gsd:*)" warnings if possible via settings or skill manifest). +- Consider whether to also populate a native `~/.grok/skills/gsd-*` tree in addition to the working `.agents` layout. + +This session delivered working `grok` runtime resolution + immediate version parity for the user's primary Grok Build harness. + +--- + +**Last updated:** 2026-05-16 (by Grok, in this session) + +This document is intended to be living. Update it as the local Grok Build work progresses. \ No newline at end of file diff --git a/get-shit-done/bin/lib/runtime-homes.cjs b/get-shit-done/bin/lib/runtime-homes.cjs index 9a4bd7943..4909a4ce5 100644 --- a/get-shit-done/bin/lib/runtime-homes.cjs +++ b/get-shit-done/bin/lib/runtime-homes.cjs @@ -62,6 +62,13 @@ function getGlobalConfigDir(runtime) { case 'codex': return env.CODEX_HOME ? expandTilde(env.CODEX_HOME) : path.join(home, '.codex'); + // ── Grok Build ─────────────────────────────────────────────────────────── + // Uses the unified ~/.agents layout (skills + agents + engine) shared with + // Codex-style harnesses. This is the pragmatic primary target for users + // running GSD inside Grok Build. + case 'grok': + return env.GROK_AGENTS_HOME ? expandTilde(env.GROK_AGENTS_HOME) : path.join(home, '.agents'); + // ── Copilot (VS Code) ──────────────────────────────────────────────────── case 'copilot': return env.COPILOT_CONFIG_DIR ? expandTilde(env.COPILOT_CONFIG_DIR) : path.join(home, '.copilot'); diff --git a/get-shit-done/workflows/sync-skills.md b/get-shit-done/workflows/sync-skills.md index d22447cf1..a828b67e8 100644 --- a/get-shit-done/workflows/sync-skills.md +++ b/get-shit-done/workflows/sync-skills.md @@ -17,7 +17,7 @@ Sync managed `gsd-*` skill directories from one canonical runtime's skills root If neither `--dry-run` nor `--apply` is specified, dry-run is the default. -**Supported runtime names:** `claude`, `codex`, `copilot`, `cursor`, `windsurf`, `opencode`, `gemini`, `kilo`, `augment`, `trae`, `qwen`, `codebuddy`, `cline`, `antigravity` +**Supported runtime names:** `claude`, `codex`, `grok`, `copilot`, `cursor`, `windsurf`, `opencode`, `gemini`, `kilo`, `augment`, `trae`, `qwen`, `codebuddy`, `cline`, `antigravity` (grok uses the `~/.agents` layout) --- @@ -35,7 +35,7 @@ fi # Parse --to if [[ "$@" == *"--to all"* ]]; then - TO_RUNTIMES=(claude codex copilot cursor windsurf opencode gemini kilo augment trae qwen codebuddy cline antigravity) + TO_RUNTIMES=(claude codex grok copilot cursor windsurf opencode gemini kilo augment trae qwen codebuddy cline antigravity) elif [[ "$@" == *"--to"* ]]; then TO_RUNTIMES=( $(echo "$@" | grep -oP '(?<=--to )\S+') ) fi diff --git a/scripts/fix-slash-commands.cjs b/scripts/fix-slash-commands.cjs index 079751f12..612f73f53 100644 --- a/scripts/fix-slash-commands.cjs +++ b/scripts/fix-slash-commands.cjs @@ -1,10 +1,18 @@ 'use strict'; /** - * One-shot script: replace retired /gsd- with /gsd: for known command names. - * Only replaces when followed by a word boundary (space, newline, quote, backtick, ), end). + * One-shot script + library: bidirectional GSD slash-command namespace normalizer. * - * The transform is exported as a pure function so it can be unit-tested directly - * (see tests/bug-2543-gsd-slash-namespace.test.cjs) without needing fixture files. + * - Default direction (transformContent): retired /gsd- → /gsd: + * (keeps monorepo sources, docs, and workflows in the active colon form). + * - Reverse direction (transformContentToHyphen): /gsd: / gsd: → gsd- + * (used during skill installation for runtimes that register skills under the + * canonical hyphen form established in #2808). + * + * Both directions only rewrite known commands from `commands/gsd/*.md` (longest-first + * matching + word-boundary safety). Non-commands (gsd-sdk, gsd-tools, etc.) are + * intentionally left untouched. + * + * The transforms are pure and exported for use by the installer and tests. */ const fs = require('node:fs'); @@ -57,6 +65,32 @@ function transformContent(src, cmdNames) { return src.replace(pattern, (_, cmd) => `/gsd:${cmd}`); } +/** + * Build regex for the reverse direction (colon form → hyphen form). + * Matches both "gsd:cmd" and "/gsd:cmd" (the leading / is preserved automatically + * because it is not part of the match). Uses longest-first ordering plus + * bidirectional word-boundary safety (negative lookbehind on the left, lookahead + * on the right) so matches only occur at token boundaries. + */ +function buildColonPattern(cmdNames) { + if (!Array.isArray(cmdNames) || cmdNames.length === 0) return null; + const sorted = [...cmdNames].sort((a, b) => b.length - a.length); + return new RegExp(`(?` / `gsd:` to hyphen form + * for known GSD commands. + * + * Non-command identifiers (e.g. gsd-sdk, gsd-tools) are left untouched, matching + * the safety contract of the forward transform. + */ +function transformContentToHyphen(src, cmdNames) { + const pattern = buildColonPattern(cmdNames); + if (!pattern) return src; + return src.replace(pattern, (_, cmd) => `gsd-${cmd}`); +} + function readCmdNames() { return fs.readdirSync(COMMANDS_DIR) .filter(f => f.endsWith('.md')) @@ -103,4 +137,11 @@ if (require.main === module) { console.log('Done.'); } -module.exports = { transformContent, buildPattern, SKIP_DIRS }; +module.exports = { + transformContent, + transformContentToHyphen, + buildPattern, + buildColonPattern, + readCmdNames, + SKIP_DIRS +}; diff --git a/tests/bug-2808-skill-hyphen-name.test.cjs b/tests/bug-2808-skill-hyphen-name.test.cjs index 581885e02..6b387191f 100644 --- a/tests/bug-2808-skill-hyphen-name.test.cjs +++ b/tests/bug-2808-skill-hyphen-name.test.cjs @@ -87,6 +87,25 @@ describe('bug-2808: SKILL.md name: uses hyphen form', () => { name.startsWith('gsd-'), `${cmd}: SKILL.md name should start with gsd-, got "${name}"` ); + + // #3583 regression guard: the *body* must not leak retired colon-form + // command references (e.g. /gsd:plan-phase or gsd:review). The converter + // now uses transformContentToHyphen from the shared transformer. + // + // We explicitly scope to the body (after stripping the leading frontmatter + // block) so that descriptions or other frontmatter fields containing example + // gsd: references do not cause spurious failures. + // + // gsd:sdk and gsd:tools are intentionally excluded: they are not slash commands + // (no commands/gsd/sdk.md or tools.md exist), so the transformer correctly leaves + // them alone. They are benign and should not trigger this assertion. + const bodyContent = skillContent.replace(/^---\n[\s\S]*?\n---\n?/, ''); + const colonRefs = (bodyContent.match(/\bgsd:[a-z][a-z0-9-]*\b/g) || []) + .filter(r => !/gsd:(sdk|tools)/.test(r)); + assert.strictEqual( + colonRefs.length, 0, + `${cmd}: generated SKILL.md body must not contain gsd: command references (found: ${colonRefs.join(', ')})` + ); } }); @@ -178,4 +197,52 @@ describe('bug-2808: SKILL.md name: uses hyphen form', () => { assert.ok(!name.includes('_'), `${skillDir}: autocomplete name must not contain underscore, got ${name}`); } }); + + test('transformContentToHyphen (from fix-slash-commands.cjs) rewrites colon to hyphen for known commands', () => { + const transformer = require(path.join(ROOT, 'scripts', 'fix-slash-commands.cjs')); + const { transformContentToHyphen, readCmdNames } = transformer; + const liveCmdNames = readCmdNames(); + + const input = 'Run /gsd:plan-phase then gsd:execute-phase. Also see /gsd:review and gsd-sdk query.'; + const out = transformContentToHyphen(input, liveCmdNames); + + assert.ok(out.includes('/gsd-plan-phase'), 'leading-/ colon form must become hyphen'); + assert.ok(out.includes('gsd-execute-phase'), 'bare colon form must become hyphen'); + assert.ok(out.includes('/gsd-review'), 'another command reference must be rewritten'); + assert.ok(out.includes('gsd-sdk'), 'non-command gsd-sdk must be left untouched'); + assert.ok(!out.match(/\bgsd:[a-z]/), 'no colon-form command reference may survive'); + }); + + test('respects word boundary — does not rewrite gsd:plan-phase-extra (partial match guard)', () => { + const transformer = require(path.join(ROOT, 'scripts', 'fix-slash-commands.cjs')); + const { transformContentToHyphen, readCmdNames } = transformer; + const liveCmdNames = readCmdNames(); + + const out = transformContentToHyphen('gsd:plan-phase-extra and /gsd:execute-phase-extra', liveCmdNames); + assert.strictEqual(out, 'gsd:plan-phase-extra and /gsd:execute-phase-extra', + 'word-boundary lookahead must prevent partial matches on the reverse transform'); + }); + + test('respects left word boundary — does not rewrite inside larger tokens (e.g. mygsd:cmd)', () => { + const transformer = require(path.join(ROOT, 'scripts', 'fix-slash-commands.cjs')); + const { transformContentToHyphen, readCmdNames } = transformer; + const liveCmdNames = readCmdNames(); + + const input = 'See mygsd:plan-phase or prefix-gsd:execute in the docs.'; + const out = transformContentToHyphen(input, liveCmdNames); + assert.strictEqual(out, input, 'negative lookbehind must prevent left-side in-word matches'); + }); + + test('leaves already-hyphen-form references untouched (idempotent on output)', () => { + const transformer = require(path.join(ROOT, 'scripts', 'fix-slash-commands.cjs')); + const { transformContentToHyphen, readCmdNames } = transformer; + const liveCmdNames = readCmdNames(); + + const input = 'Run gsd-plan-phase and /gsd-execute-phase then gsd:review.'; // mixed, only colon should change + const out = transformContentToHyphen(input, liveCmdNames); + assert.ok(out.includes('gsd-plan-phase'), 'pre-existing hyphen stays'); + assert.ok(out.includes('/gsd-execute-phase'), 'pre-existing hyphen stays'); + assert.ok(out.includes('gsd-review'), 'colon form was normalized'); + assert.ok(!out.includes('gsd:review'), 'no colon form remains'); + }); }); diff --git a/tests/claude-skills-migration.test.cjs b/tests/claude-skills-migration.test.cjs index 370b16efb..aaef37dd1 100644 --- a/tests/claude-skills-migration.test.cjs +++ b/tests/claude-skills-migration.test.cjs @@ -89,8 +89,10 @@ describe('convertClaudeCommandToClaudeSkill', () => { assert.ok(result.includes('name: gsd-next'), 'frontmatter name uses hyphen form (#2808)'); }); - test('preserves body content unchanged', () => { - const body = '\n\nDo the thing.\n\n\n\nStep 1.\nStep 2.\n\n'; + test('preserves body content while normalizing gsd: command references (#3583)', () => { + // The body transformer now rewrites gsd: references (colon → hyphen) but must + // leave all other custom prose, tags, and structure intact. + const body = '\n\nSee /gsd:plan-phase and gsd:review for details.\n\n\n\nStep 1.\nStep 2.\n\n'; const input = [ '---', 'name: gsd:test', @@ -100,10 +102,17 @@ describe('convertClaudeCommandToClaudeSkill', () => { ].join(''); const result = convertClaudeCommandToClaudeSkill(input, 'gsd-test'); + // Custom structure preserved assert.ok(result.includes(''), 'objective tag preserved'); - assert.ok(result.includes('Do the thing.'), 'body text preserved'); + assert.ok(result.includes('See /gsd-plan-phase'), 'rewritten command reference visible'); assert.ok(result.includes(''), 'process tag preserved'); assert.ok(result.includes('Step 1.'), 'step text preserved'); + + // #3583: gsd: references in body are normalized to hyphen form + assert.ok(result.includes('/gsd-plan-phase'), 'colon command ref rewritten to hyphen'); + assert.ok(result.includes('gsd-review'), 'bare colon ref rewritten to hyphen'); + assert.ok(!result.includes('gsd:plan-phase'), 'no colon form should survive in body'); + assert.ok(!result.includes('gsd:review'), 'no colon form should survive in body'); }); test('preserves agent field', () => { diff --git a/tests/docs-parity-live-registry.test.cjs b/tests/docs-parity-live-registry.test.cjs index 6ad1e8e2c..e5ca01238 100644 --- a/tests/docs-parity-live-registry.test.cjs +++ b/tests/docs-parity-live-registry.test.cjs @@ -139,6 +139,14 @@ const INTERNAL_COMPONENT_SLUGS = new Set([ // a belt-and-suspenders guard against the pattern returning in other locale docs. 'alternative-1', 'alternative-2', + + // gsd-sync-skills — installed Claude skill directory name (also a workflow + // under get-shit-done/workflows/sync-skills.md), but NOT a registered + // slash command (no commands/gsd/sync-skills.md). Docs reference it as a + // filesystem path component, e.g. "~/.agents/skills/gsd-sync-skills/" in + // docs/discussions/grok-build-support-2026-05.md. The regex captures + // "/gsd-sync-skills" from the path. Invoked via Skill(skill="gsd-sync-skills"). + 'sync-skills', ]); /**