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.
This commit is contained in:
Cristian Uibar
2026-05-16 20:09:58 +03:00
committed by GitHub
parent 7f8b5701bf
commit 05316369ae
11 changed files with 631 additions and 26 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 3583
---
Claude skill install (convertClaudeCommandToClaudeSkill + copyCommandsAsClaudeSkills) now normalizes retired /gsd:<cmd> references in SKILL.md bodies to the canonical gsd-<cmd> 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.

41
AGENTS.md Normal file
View File

@@ -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`.

View File

@@ -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:<cmd>` 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:<cmd>` (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.

View File

@@ -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 = '<!-- GSD Configuration \u2014 managed by get-shit-done installer -->';
const GSD_COPILOT_INSTRUCTIONS_CLOSE_MARKER = '<!-- /GSD Configuration -->';
// 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-<cmd>` (hyphen) so Skill(skill="gsd-<cmd>") 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:<cmd> or gsd:<cmd> in the body to the canonical
// hyphen form (gsd-<cmd>) 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

View File

@@ -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 `<codex_skill_adapter>` 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 `<codex_skill_adapter>` 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 `<codex_skill_adapter>` 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 `<codex_skill_adapter>` 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 `<codex_skill_adapter>` 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.

View File

@@ -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');

View File

@@ -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

View File

@@ -1,10 +1,18 @@
'use strict';
/**
* One-shot script: replace retired /gsd-<cmd> with /gsd:<cmd> 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-<cmd> → /gsd:<cmd>
* (keeps monorepo sources, docs, and workflows in the active colon form).
* - Reverse direction (transformContentToHyphen): /gsd:<cmd> / gsd:<cmd> → gsd-<cmd>
* (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(`(?<![a-zA-Z0-9_-])gsd:(${sorted.join('|')})(?=[^a-zA-Z0-9_-]|$)`, 'g');
}
/**
* Pure transform (reverse): rewrite `/gsd:<cmd>` / `gsd:<cmd>` 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
};

View File

@@ -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');
});
});

View File

@@ -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<objective>\nDo the thing.\n</objective>\n\n<process>\nStep 1.\nStep 2.\n</process>\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<objective>\nSee /gsd:plan-phase and gsd:review for details.\n</objective>\n\n<process>\nStep 1.\nStep 2.\n</process>\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>'), '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>'), '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', () => {

View File

@@ -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',
]);
/**