@opengsd/gsd-core 1.7.0-rc.2 → 1.7.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/GEMINI.md +25 -23
- package/README.ja-JP.md +4 -3
- package/README.ko-KR.md +4 -3
- package/README.md +7 -6
- package/README.pt-BR.md +4 -3
- package/README.zh-CN.md +4 -3
- package/agents/gsd-advisor-researcher.md +1 -1
- package/agents/gsd-ai-researcher.md +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-domain-researcher.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +4 -4
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +1 -1
- package/agents/gsd-phase-researcher.md +2 -2
- package/agents/gsd-plan-checker.md +1 -1
- package/agents/gsd-planner.md +2 -2
- package/agents/gsd-project-researcher.md +2 -2
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-researcher.md +2 -2
- package/agents/gsd-verifier.md +2 -2
- package/bin/install.js +132 -464
- package/commands/gsd/map-codebase.md +4 -4
- package/commands/gsd/mempalace-capture.md +1 -1
- package/commands/gsd/mempalace-recall.md +9 -5
- package/commands/gsd/next.md +29 -0
- package/commands/gsd/ns-project.md +2 -1
- package/commands/gsd/ns-workflow.md +5 -2
- package/commands/gsd/onboard.md +46 -0
- package/gsd-core/bin/ensure-runtime-build.cjs +246 -0
- package/gsd-core/bin/gsd-tools.cjs +72 -3
- package/gsd-core/bin/lib/capability-registry.cjs +684 -264
- package/gsd-core/bin/lib/capability-state.cjs +117 -18
- package/gsd-core/bin/lib/capability-writer.cjs +11 -1
- package/gsd-core/bin/lib/check-command-router.cjs +111 -1
- package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +136 -0
- package/gsd-core/bin/lib/claude-orchestration.cjs +404 -0
- package/gsd-core/bin/lib/clusters.cjs +2 -0
- package/gsd-core/bin/lib/command-aliases.cjs +8 -0
- package/gsd-core/bin/lib/config.cjs +19 -1
- package/gsd-core/bin/lib/core-utils.cjs +41 -0
- package/gsd-core/bin/lib/external-job.cjs +287 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +151 -0
- package/gsd-core/bin/lib/graphify.cjs +41 -9
- package/gsd-core/bin/lib/init-command-router.cjs +4 -0
- package/gsd-core/bin/lib/init.cjs +25 -69
- package/gsd-core/bin/lib/install-engine.cjs +8 -5
- package/gsd-core/bin/lib/install-profiles.cjs +2 -0
- package/gsd-core/bin/lib/loop-resolver.cjs +7 -1
- package/gsd-core/bin/lib/milestone.cjs +7 -1
- package/gsd-core/bin/lib/model-resolver.cjs +69 -4
- package/gsd-core/bin/lib/onboard-projection.cjs +309 -0
- package/gsd-core/bin/lib/phase.cjs +14 -2
- package/gsd-core/bin/lib/plan-scan.cjs +9 -1
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +21 -19
- package/gsd-core/bin/lib/roadmap.cjs +17 -2
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +18 -13
- package/gsd-core/bin/lib/runtime-name-policy.cjs +11 -11
- package/gsd-core/bin/lib/shell-command-projection.cjs +12 -8
- package/gsd-core/bin/lib/smart-entry.cjs +521 -0
- package/gsd-core/bin/lib/surface.cjs +63 -33
- package/gsd-core/bin/lib/update-context.cjs +0 -5
- package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
- package/gsd-core/bin/shared/model-catalog.json +5 -5
- package/gsd-core/bin/shared/runtime-aliases.manifest.json +0 -5
- package/gsd-core/references/gates.md +1 -1
- package/gsd-core/references/gsd-run-resolver.md +8 -0
- package/gsd-core/references/planning-config.md +1 -1
- package/gsd-core/templates/project.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +1 -1
- package/gsd-core/workflows/add-tests.md +1 -1
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +1 -1
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +1 -1
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous.md +1 -1
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +1 -1
- package/gsd-core/workflows/code-review-fix.md +1 -1
- package/gsd-core/workflows/code-review.md +1 -1
- package/gsd-core/workflows/complete-milestone.md +1 -1
- package/gsd-core/workflows/debug.md +1 -1
- package/gsd-core/workflows/diagnose-issues.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/auto.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
- package/gsd-core/workflows/discuss-phase.md +2 -2
- package/gsd-core/workflows/do.md +5 -4
- package/gsd-core/workflows/docs-update.md +1 -1
- package/gsd-core/workflows/edit-phase.md +1 -1
- package/gsd-core/workflows/eval-review.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +1 -1
- package/gsd-core/workflows/execute-phase.md +4 -5
- package/gsd-core/workflows/execute-plan.md +1 -1
- package/gsd-core/workflows/explore.md +1 -1
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +1 -1
- package/gsd-core/workflows/help/modes/brief.md +2 -1
- package/gsd-core/workflows/help/modes/default.md +2 -1
- package/gsd-core/workflows/help/modes/full.md +25 -1
- package/gsd-core/workflows/help/modes/topic.md +2 -1
- package/gsd-core/workflows/import.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- package/gsd-core/workflows/insert-phase.md +1 -1
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +1 -1
- package/gsd-core/workflows/map-codebase.md +1 -1
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +1 -1
- package/gsd-core/workflows/new-project.md +1 -1
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/onboard.md +277 -0
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-milestone-gaps.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase.md +9 -2
- package/gsd-core/workflows/plan-review-convergence.md +1 -1
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +1 -1
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress.md +1 -1
- package/gsd-core/workflows/quick.md +21 -1
- package/gsd-core/workflows/reapply-patches.md +1 -1
- package/gsd-core/workflows/remove-phase.md +1 -1
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +1 -1
- package/gsd-core/workflows/review.md +43 -7
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/settings-advanced.md +3 -1
- package/gsd-core/workflows/settings-integrations.md +1 -1
- package/gsd-core/workflows/settings.md +1 -1
- package/gsd-core/workflows/ship.md +1 -1
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +120 -0
- package/gsd-core/workflows/spec-phase.md +8 -6
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/thread.md +1 -1
- package/gsd-core/workflows/transition.md +1 -1
- package/gsd-core/workflows/ui-phase.md +1 -1
- package/gsd-core/workflows/ui-review.md +1 -1
- package/gsd-core/workflows/ultraplan-phase.md +1 -1
- package/gsd-core/workflows/update.md +3 -6
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-phase.md +1 -1
- package/gsd-core/workflows/verify-work.md +62 -4
- package/package.json +3 -5
- package/scripts/ci-test-scope.cjs +19 -1
- package/scripts/gen-golden-install-parity-zcode.cjs +77 -0
- package/scripts/generate-package-identity.cjs +27 -2
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -148
- package/scripts/lint-regression-test-names.allowlist.json +1 -273
- package/scripts/lint-test-file-count.allowlist.json +7 -91
- package/scripts/research-profiles.cjs +6 -6
- package/scripts/run-tests.cjs +18 -3
- package/scripts/slurm-adapter.cjs +269 -0
- package/scripts/sync-manifest-versions.cjs +3 -2
- package/scripts/sync-runtime-launcher.cjs +20 -2
- package/skills/gsd-map-codebase/SKILL.md +3 -3
- package/skills/gsd-mempalace-capture/SKILL.md +1 -1
- package/skills/gsd-mempalace-recall/SKILL.md +9 -5
- package/skills/gsd-next/SKILL.md +30 -0
- package/skills/gsd-ns-project/SKILL.md +1 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -1
- package/skills/gsd-onboard/SKILL.md +46 -0
- package/gemini-extension.json +0 -6
package/bin/install.js
CHANGED
|
@@ -434,7 +434,7 @@ if (hasMinimal && _profileArgRaw) {
|
|
|
434
434
|
|
|
435
435
|
function selectRuntimesFromArgs(runtimeArgs) {
|
|
436
436
|
if (runtimeArgs.includes('--all')) {
|
|
437
|
-
return ['claude', 'kimi', 'kilo', 'opencode', '
|
|
437
|
+
return ['claude', 'kimi', 'kilo', 'opencode', 'codex', 'copilot', 'antigravity', 'cursor', 'windsurf', 'augment', 'trae', 'qwen', 'hermes', 'codebuddy', 'cline', 'zcode'];
|
|
438
438
|
}
|
|
439
439
|
if (runtimeArgs.includes('--both')) {
|
|
440
440
|
return ['claude', 'opencode'];
|
|
@@ -443,7 +443,6 @@ function selectRuntimesFromArgs(runtimeArgs) {
|
|
|
443
443
|
const selected = [];
|
|
444
444
|
if (runtimeArgs.includes('--claude')) selected.push('claude');
|
|
445
445
|
if (runtimeArgs.includes('--opencode')) selected.push('opencode');
|
|
446
|
-
if (runtimeArgs.includes('--gemini')) selected.push('gemini');
|
|
447
446
|
if (runtimeArgs.includes('--kilo')) selected.push('kilo');
|
|
448
447
|
if (runtimeArgs.includes('--codex')) selected.push('codex');
|
|
449
448
|
if (runtimeArgs.includes('--copilot')) selected.push('copilot');
|
|
@@ -457,12 +456,44 @@ function selectRuntimesFromArgs(runtimeArgs) {
|
|
|
457
456
|
if (runtimeArgs.includes('--kimi')) selected.push('kimi');
|
|
458
457
|
if (runtimeArgs.includes('--codebuddy')) selected.push('codebuddy');
|
|
459
458
|
if (runtimeArgs.includes('--cline')) selected.push('cline');
|
|
459
|
+
if (runtimeArgs.includes('--zcode')) selected.push('zcode');
|
|
460
460
|
return selected;
|
|
461
461
|
}
|
|
462
462
|
|
|
463
463
|
// Runtime selection - can be set by flags or interactive prompt
|
|
464
464
|
let selectedRuntimes = selectRuntimesFromArgs(args);
|
|
465
465
|
|
|
466
|
+
// #1928: Google sunset Gemini CLI on 2026-06-18; Antigravity CLI is its
|
|
467
|
+
// official successor. `--gemini` is no longer a valid runtime selector —
|
|
468
|
+
// selectRuntimesFromArgs above no longer recognizes it, so it never lands in
|
|
469
|
+
// selectedRuntimes. Print a one-time redirect notice, and — when `--gemini`
|
|
470
|
+
// was the ONLY runtime flag supplied (selectedRuntimes is empty) — exit
|
|
471
|
+
// deterministically rather than silently falling through to the "no runtime
|
|
472
|
+
// specified" defaults below (which would install Claude Code, surprising a
|
|
473
|
+
// user who explicitly asked for Gemini). Other flags (e.g. `--codex`) still
|
|
474
|
+
// parse and install normally alongside the notice.
|
|
475
|
+
if (args.includes('--gemini')) {
|
|
476
|
+
const wantsHelp = args.includes('--help') || args.includes('-h');
|
|
477
|
+
console.error('Gemini CLI was sunset by Google on 2026-06-18 and is no longer served for free/Pro/Ultra tiers.');
|
|
478
|
+
console.error('GSD now supports Antigravity CLI (the official successor). Re-run with: --antigravity');
|
|
479
|
+
if (hasUninstall) {
|
|
480
|
+
// The gemini runtime was removed (#1928), so there is no automated
|
|
481
|
+
// `--gemini --uninstall`. Guide manual cleanup and exit — do NOT fall
|
|
482
|
+
// through to the uninstall dispatch below, which defaults an empty runtime
|
|
483
|
+
// selection to 'claude' and would wrongly uninstall the user's Claude install.
|
|
484
|
+
console.error('The gemini runtime was removed, so `--gemini --uninstall` is no longer available.');
|
|
485
|
+
console.error('To remove a prior Gemini install, delete GSD files under your Gemini config dir');
|
|
486
|
+
console.error('(e.g. ~/.gemini/commands/gsd) and GSD hook entries in ~/.gemini/settings.json.');
|
|
487
|
+
process.exit(1);
|
|
488
|
+
}
|
|
489
|
+
// For `--gemini --help`, fall through so the usage block still prints. For a
|
|
490
|
+
// bare install attempt (no other runtime selected), exit rather than silently
|
|
491
|
+
// installing Claude.
|
|
492
|
+
if (!wantsHelp && selectedRuntimes.length === 0) {
|
|
493
|
+
process.exit(1);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
466
497
|
// WSL + Windows Node.js detection
|
|
467
498
|
// When Windows-native Node runs on WSL, os.homedir() and path.join() produce
|
|
468
499
|
// backslash paths that don't resolve correctly on the Linux filesystem.
|
|
@@ -503,7 +534,7 @@ Then re-run: npx ${pkg.name}@latest
|
|
|
503
534
|
/**
|
|
504
535
|
* Get the config directory path relative to home directory for a runtime
|
|
505
536
|
* Used for templating hooks that use path.join(homeDir, '<configDir>', ...)
|
|
506
|
-
* @param {string} runtime - 'claude', 'opencode', '
|
|
537
|
+
* @param {string} runtime - 'claude', 'opencode', 'codex', or 'copilot'
|
|
507
538
|
* @param {boolean} isGlobal - Whether this is a global install
|
|
508
539
|
*/
|
|
509
540
|
function getConfigDirFromHome(runtime, isGlobal) {
|
|
@@ -549,7 +580,7 @@ const banner = '\n' +
|
|
|
549
580
|
' GSD Core ' + dim + 'v' + pkg.version + reset + '\n' +
|
|
550
581
|
' Git. Ship. Done.\n' +
|
|
551
582
|
' A meta-prompting, context engineering and spec-driven\n' +
|
|
552
|
-
' development workflows for Claude Code, OpenCode,
|
|
583
|
+
' development workflows for Claude Code, OpenCode, Kimi CLI, Kilo, Codex, Copilot, Antigravity, Cursor, Windsurf, Augment, Trae, Qwen Code, Hermes Agent, Cline, CodeBuddy and ZCode.\n';
|
|
553
584
|
|
|
554
585
|
// Pure seam: parse --config-dir / -c from an arbitrary args array.
|
|
555
586
|
// Returns the path string, '' for an empty equals-form value, or null when the
|
|
@@ -606,7 +637,7 @@ if (hasUninstall) {
|
|
|
606
637
|
|
|
607
638
|
// Show help if requested
|
|
608
639
|
if (hasHelp) {
|
|
609
|
-
console.log(` ${yellow}Usage:${reset} npx ${pkg.name} [options]\n\n ${yellow}Options:${reset}\n ${cyan}-g, --global${reset} Install globally (to config directory)\n ${cyan}-l, --local${reset} Install locally (to current directory)\n ${cyan}--claude${reset} Install for Claude Code only\n ${cyan}--opencode${reset} Install for OpenCode only\n ${cyan}--
|
|
640
|
+
console.log(` ${yellow}Usage:${reset} npx ${pkg.name} [options]\n\n ${yellow}Options:${reset}\n ${cyan}-g, --global${reset} Install globally (to config directory)\n ${cyan}-l, --local${reset} Install locally (to current directory)\n ${cyan}--claude${reset} Install for Claude Code only\n ${cyan}--opencode${reset} Install for OpenCode only\n ${cyan}--kilo${reset} Install for Kilo only\n ${cyan}--codex${reset} Install for Codex only\n ${cyan}--kimi${reset} Install for Kimi CLI only\n ${cyan}--copilot${reset} Install for Copilot only\n ${cyan}--antigravity${reset} Install for Antigravity only\n ${cyan}--cursor${reset} Install for Cursor only\n ${cyan}--windsurf${reset} Install for Windsurf only\n ${cyan}--augment${reset} Install for Augment only\n ${cyan}--trae${reset} Install for Trae only\n ${cyan}--qwen${reset} Install for Qwen Code only\n ${cyan}--hermes${reset} Install for Hermes Agent only\n ${cyan}--cline${reset} Install for Cline only\n ${cyan}--codebuddy${reset} Install for CodeBuddy only\n ${cyan}--zcode${reset} Install for ZCode only\n ${cyan}--all${reset} Install for all runtimes\n ${cyan}-u, --uninstall${reset} Uninstall GSD (remove all GSD files)\n ${cyan}-c, --config-dir <path>${reset} Specify custom config directory\n ${cyan}-h, --help${reset} Show this help message\n ${cyan}--force-statusline${reset} Replace existing statusline config\n ${cyan}--portable-hooks${reset} Emit \$HOME-relative hook paths in settings.json\n (for WSL/Docker bind-mount setups; also GSD_PORTABLE_HOOKS=1)\n ${cyan}--profile=<name>${reset} Install a named skill profile. Profiles:\n core — ${PROFILES.core.length} main-loop skills incl. phase (~130 desc tokens)\n standard — ${PROFILES.standard.length} skills incl. phase, review, config (~700)\n full — all skills (default)\n Composable: --profile=core,audit installs union of closures.\n Profile is persisted and respected by \`gsd update\`.\n ${cyan}--minimal${reset} Alias for --profile=core (back-compat).\n Cuts cold-start overhead from ~12k tokens to ~700.\n Alias: --core-only.\n\n ${yellow}Examples:${reset}\n ${dim}# Interactive install (prompts for runtime and location)${reset}\n npx ${pkg.name}\n\n ${dim}# Install for Claude Code globally${reset}\n npx ${pkg.name} --claude --global\n\n ${dim}# Install for Kilo globally${reset}\n npx ${pkg.name} --kilo --global\n\n ${dim}# Install for Codex globally${reset}\n npx ${pkg.name} --codex --global\n\n ${dim}# Install for Kimi CLI globally${reset}\n npx ${pkg.name} --kimi --global\n\n ${dim}# Install for Kimi CLI under ~/.kimi-code${reset}\n npx ${pkg.name} --kimi --global --config-dir ~/.kimi-code\n\n ${dim}# Install for Copilot globally${reset}\n npx ${pkg.name} --copilot --global\n\n ${dim}# Install for Copilot locally${reset}\n npx ${pkg.name} --copilot --local\n\n ${dim}# Install for Antigravity globally${reset}\n npx ${pkg.name} --antigravity --global\n\n ${dim}# Install for Antigravity locally${reset}\n npx ${pkg.name} --antigravity --local\n\n ${dim}# Install for Cursor globally${reset}\n npx ${pkg.name} --cursor --global\n\n ${dim}# Install for Cursor locally${reset}\n npx ${pkg.name} --cursor --local\n\n ${dim}# Install for Windsurf globally${reset}\n npx ${pkg.name} --windsurf --global\n\n ${dim}# Install for Windsurf locally${reset}\n npx ${pkg.name} --windsurf --local\n\n ${dim}# Install for Augment globally${reset}\n npx ${pkg.name} --augment --global\n\n ${dim}# Install for Augment locally${reset}\n npx ${pkg.name} --augment --local\n\n ${dim}# Install for Trae globally${reset}\n npx ${pkg.name} --trae --global\n\n ${dim}# Install for Trae locally${reset}\n npx ${pkg.name} --trae --local\n\n ${dim}# Install for Hermes Agent globally${reset}\n npx ${pkg.name} --hermes --global\n\n ${dim}# Install for Hermes Agent locally${reset}\n npx ${pkg.name} --hermes --local\n\n ${dim}# Install for Cline globally${reset}\n npx ${pkg.name} --cline --global\n\n ${dim}# Install for Cline locally${reset}\n npx ${pkg.name} --cline --local\n\n ${dim}# Install for CodeBuddy globally${reset}\n npx ${pkg.name} --codebuddy --global\n\n ${dim}# Install for CodeBuddy locally${reset}\n npx ${pkg.name} --codebuddy --local\n\n ${dim}# Install for all runtimes globally${reset}\n npx ${pkg.name} --all --global\n\n ${dim}# Install to custom config directory${reset}\n npx ${pkg.name} --kilo --global --config-dir ~/.kilo-work\n\n ${dim}# Install to current project only${reset}\n npx ${pkg.name} --claude --local\n\n ${dim}# Uninstall GSD from Cursor globally${reset}\n npx ${pkg.name} --cursor --global --uninstall\n\n ${yellow}Notes:${reset}\n The --config-dir option is useful when you have multiple configurations.\n It takes priority over CLAUDE_CONFIG_DIR / OPENCODE_CONFIG_DIR / KILO_CONFIG_DIR / CODEX_HOME / KIMI_CONFIG_DIR / COPILOT_CONFIG_DIR / COPILOT_HOME / ANTIGRAVITY_CONFIG_DIR / CURSOR_CONFIG_DIR / WINDSURF_CONFIG_DIR / AUGMENT_CONFIG_DIR / TRAE_CONFIG_DIR / QWEN_CONFIG_DIR / HERMES_HOME / CLINE_CONFIG_DIR / CODEBUDDY_CONFIG_DIR environment variables.\n Kimi CLI defaults to the first existing generic skills root: ${cyan}~/.config/agents/skills${reset}, then ${cyan}~/.agents/skills${reset}; if neither exists, GSD creates ${cyan}~/.config/agents${reset}.\n Use ${cyan}--config-dir ~/.kimi-code${reset} or ${cyan}KIMI_CONFIG_DIR=~/.kimi-code${reset} for brand-specific Kimi installs.\n`);
|
|
610
641
|
process.exit(0);
|
|
611
642
|
}
|
|
612
643
|
|
|
@@ -1379,7 +1410,7 @@ const attributionCache = new Map();
|
|
|
1379
1410
|
|
|
1380
1411
|
/**
|
|
1381
1412
|
* Get commit attribution setting for a runtime
|
|
1382
|
-
* @param {string} runtime - 'claude', 'opencode', '
|
|
1413
|
+
* @param {string} runtime - 'claude', 'opencode', 'codex', or 'copilot'
|
|
1383
1414
|
* @returns {null|undefined|string} null = remove, undefined = keep default, string = custom
|
|
1384
1415
|
*/
|
|
1385
1416
|
function getCommitAttribution(runtime) {
|
|
@@ -1396,16 +1427,6 @@ function getCommitAttribution(runtime) {
|
|
|
1396
1427
|
: resolveKiloConfigPath;
|
|
1397
1428
|
const config = readSettings(resolveConfigPath(getGlobalConfigDir(runtime, null)));
|
|
1398
1429
|
result = (config && config.disable_ai_attribution === true) ? null : undefined;
|
|
1399
|
-
} else if (runtime === 'gemini') {
|
|
1400
|
-
// Gemini: check gemini settings.json for attribution config
|
|
1401
|
-
const settings = readSettings(path.join(getGlobalConfigDir('gemini', explicitConfigDir), 'settings.json'));
|
|
1402
|
-
if (!settings || !settings.attribution || settings.attribution.commit === undefined) {
|
|
1403
|
-
result = undefined;
|
|
1404
|
-
} else if (settings.attribution.commit === '') {
|
|
1405
|
-
result = null;
|
|
1406
|
-
} else {
|
|
1407
|
-
result = settings.attribution.commit;
|
|
1408
|
-
}
|
|
1409
1430
|
} else if (runtime === 'claude') {
|
|
1410
1431
|
// Claude Code
|
|
1411
1432
|
const settings = readSettings(path.join(getGlobalConfigDir('claude', explicitConfigDir), 'settings.json'));
|
|
@@ -5718,11 +5739,6 @@ function installCodexConfig(targetDir, agentsSrc, sandboxTier = 'codex-agent-san
|
|
|
5718
5739
|
return agents.length;
|
|
5719
5740
|
}
|
|
5720
5741
|
|
|
5721
|
-
/**
|
|
5722
|
-
* Strip HTML <sub> tags for Gemini CLI output
|
|
5723
|
-
* Terminals don't support subscript — Gemini renders these as raw HTML.
|
|
5724
|
-
* Converts <sub>text</sub> to italic *(text)* for readable terminal output.
|
|
5725
|
-
*/
|
|
5726
5742
|
/**
|
|
5727
5743
|
* Runtime-neutral agent name and instruction file replacement.
|
|
5728
5744
|
* Used by ALL non-Claude runtime converters to avoid Claude-specific
|
|
@@ -5753,200 +5769,6 @@ function neutralizeAgentReferences(content, instructionFile) {
|
|
|
5753
5769
|
return c;
|
|
5754
5770
|
}
|
|
5755
5771
|
|
|
5756
|
-
function stripSubTags(content) {
|
|
5757
|
-
return content.replace(/<sub>(.*?)<\/sub>/g, '*($1)*');
|
|
5758
|
-
}
|
|
5759
|
-
|
|
5760
|
-
/**
|
|
5761
|
-
* Convert Claude Code agent frontmatter to Gemini CLI format
|
|
5762
|
-
* Gemini agents use .md files with YAML frontmatter, same as Claude,
|
|
5763
|
-
* but with different field names and formats:
|
|
5764
|
-
* - tools: must be a YAML array (not comma-separated string)
|
|
5765
|
-
* - tool names: must use Gemini built-in names (read_file, not Read)
|
|
5766
|
-
* - color: must be removed (causes validation error)
|
|
5767
|
-
* - skills: must be removed (causes validation error)
|
|
5768
|
-
* - mcp__* tools: must be excluded (auto-discovered at runtime)
|
|
5769
|
-
*/
|
|
5770
|
-
let _gsdCommandRoster = null;
|
|
5771
|
-
let _gsdCommandRosterWarned = false;
|
|
5772
|
-
|
|
5773
|
-
/**
|
|
5774
|
-
* Get the list of known GSD commands from the source directory.
|
|
5775
|
-
* Caches the result after the first scan. Emits a one-shot warning if the
|
|
5776
|
-
* source directory cannot be located — an empty roster silently neutralises
|
|
5777
|
-
* every Gemini slash-command conversion, which is the bug this code exists
|
|
5778
|
-
* to prevent. The warning is gated on GSD_TEST_MODE to keep test output clean.
|
|
5779
|
-
* @returns {Set<string>} Set of command names (without .md extension)
|
|
5780
|
-
*/
|
|
5781
|
-
function getGsdCommandRoster() {
|
|
5782
|
-
if (_gsdCommandRoster) return _gsdCommandRoster;
|
|
5783
|
-
const baseDir = (typeof __dirname !== 'undefined') ? __dirname : process.cwd();
|
|
5784
|
-
const gsdSrc = path.join(baseDir, '..', 'commands', 'gsd');
|
|
5785
|
-
if (fs.existsSync(gsdSrc)) {
|
|
5786
|
-
_gsdCommandRoster = new Set(
|
|
5787
|
-
fs.readdirSync(gsdSrc)
|
|
5788
|
-
.filter(f => f.endsWith('.md'))
|
|
5789
|
-
.map(f => f.replace('.md', ''))
|
|
5790
|
-
);
|
|
5791
|
-
} else {
|
|
5792
|
-
_gsdCommandRoster = new Set();
|
|
5793
|
-
if (!_gsdCommandRosterWarned && !process.env.GSD_TEST_MODE) {
|
|
5794
|
-
_gsdCommandRosterWarned = true;
|
|
5795
|
-
console.warn(
|
|
5796
|
-
`WARNING: GSD command roster not found at ${gsdSrc}. ` +
|
|
5797
|
-
`Gemini /gsd- → /gsd: conversion will be a no-op. ` +
|
|
5798
|
-
`This usually means the package was installed without commands/gsd/.`
|
|
5799
|
-
);
|
|
5800
|
-
}
|
|
5801
|
-
}
|
|
5802
|
-
return _gsdCommandRoster;
|
|
5803
|
-
}
|
|
5804
|
-
|
|
5805
|
-
// Test-only: reset the cached roster. Exported via GSD_TEST_MODE bundle below.
|
|
5806
|
-
function _resetGsdCommandRoster() {
|
|
5807
|
-
_gsdCommandRoster = null;
|
|
5808
|
-
_gsdCommandRosterWarned = false;
|
|
5809
|
-
}
|
|
5810
|
-
|
|
5811
|
-
function convertSlashCommandsToGeminiMentions(content) {
|
|
5812
|
-
const commands = getGsdCommandRoster();
|
|
5813
|
-
// Defense in depth: regex boundary AND roster lookup must both agree.
|
|
5814
|
-
//
|
|
5815
|
-
// - Lookbehind `(?<![A-Za-z0-9./])` rejects URLs (`example.com/gsd-…`),
|
|
5816
|
-
// sub-paths (`bin/gsd-…`), and root-relative file paths preceded by a
|
|
5817
|
-
// path char. Without it the roster alone is insufficient: a URL like
|
|
5818
|
-
// `https://example.com/gsd-plan-phase` ends in a known command name and
|
|
5819
|
-
// would convert incorrectly.
|
|
5820
|
-
// - `(?!\/)` rejects sub-path continuation (`/gsd-foo/bar`).
|
|
5821
|
-
// - `(?!\.[a-z])` rejects file extensions (`.cjs`, `.md`) but PERMITS
|
|
5822
|
-
// sentence-ending punctuation like `/gsd-help.` because `.` at end of
|
|
5823
|
-
// string or before whitespace is not followed by a lowercase letter.
|
|
5824
|
-
// - Roster lookup ensures only real commands convert — agent names like
|
|
5825
|
-
// `gsd-planner` (no leading slash anyway) and unknown tokens pass through.
|
|
5826
|
-
//
|
|
5827
|
-
// GSD commands are always lowercase, so no case-insensitive flag.
|
|
5828
|
-
return content.replace(/(?<![A-Za-z0-9./])\/gsd-([a-z0-9-]+)(?!\/)(?!\.[a-z])/g, (match, commandName) => {
|
|
5829
|
-
return commands.has(commandName) ? `/gsd:${commandName}` : match;
|
|
5830
|
-
});
|
|
5831
|
-
}
|
|
5832
|
-
|
|
5833
|
-
function convertClaudeToGeminiMarkdown(content, { isCommand = false, commandName = null } = {}) {
|
|
5834
|
-
// Apply Gemini-specific slash command namespacing
|
|
5835
|
-
let converted = convertSlashCommandsToGeminiMentions(content);
|
|
5836
|
-
// Gemini CLI does not expose Claude's AskUserQuestion tool. Convert body
|
|
5837
|
-
// references to runtime-neutral wording so converted agents do not instruct
|
|
5838
|
-
// Gemini to call a nonexistent tool (#3362).
|
|
5839
|
-
converted = converted.replace(/\b(?:AskUserQuestion|ask_user)\b/g, 'conversational prompting');
|
|
5840
|
-
// Strip HTML subscript tags — terminals can't render them. Done before
|
|
5841
|
-
// TOML conversion so the prompt body of a command file is also clean.
|
|
5842
|
-
converted = stripSubTags(converted);
|
|
5843
|
-
|
|
5844
|
-
if (isCommand) {
|
|
5845
|
-
// Convert to Gemini TOML format (threads the command name so per-command
|
|
5846
|
-
// enrichment — e.g. the #778 live-state injection — can target a command).
|
|
5847
|
-
converted = convertClaudeToGeminiToml(converted, { commandName });
|
|
5848
|
-
}
|
|
5849
|
-
|
|
5850
|
-
return converted;
|
|
5851
|
-
}
|
|
5852
|
-
|
|
5853
|
-
function convertClaudeToGeminiAgent(content) {
|
|
5854
|
-
if (!content.startsWith('---')) return content;
|
|
5855
|
-
|
|
5856
|
-
const endIndex = content.indexOf('---', 3);
|
|
5857
|
-
if (endIndex === -1) return content;
|
|
5858
|
-
|
|
5859
|
-
const frontmatter = content.substring(3, endIndex).trim();
|
|
5860
|
-
const body = content.substring(endIndex + 3);
|
|
5861
|
-
|
|
5862
|
-
const lines = frontmatter.split('\n');
|
|
5863
|
-
const newLines = [];
|
|
5864
|
-
let inAllowedTools = false;
|
|
5865
|
-
let inSkippedArrayField = false;
|
|
5866
|
-
const tools = [];
|
|
5867
|
-
|
|
5868
|
-
for (const line of lines) {
|
|
5869
|
-
const trimmed = line.trim();
|
|
5870
|
-
|
|
5871
|
-
if (inSkippedArrayField) {
|
|
5872
|
-
if (!trimmed || trimmed.startsWith('- ')) {
|
|
5873
|
-
continue;
|
|
5874
|
-
}
|
|
5875
|
-
inSkippedArrayField = false;
|
|
5876
|
-
}
|
|
5877
|
-
|
|
5878
|
-
// Convert allowed-tools YAML array to tools list
|
|
5879
|
-
if (trimmed.startsWith('allowed-tools:')) {
|
|
5880
|
-
inAllowedTools = true;
|
|
5881
|
-
continue;
|
|
5882
|
-
}
|
|
5883
|
-
|
|
5884
|
-
// Handle inline tools: field (comma-separated string)
|
|
5885
|
-
if (trimmed.startsWith('tools:')) {
|
|
5886
|
-
const toolsValue = trimmed.substring(6).trim();
|
|
5887
|
-
if (toolsValue) {
|
|
5888
|
-
const parsed = toolsValue.split(',').map(t => t.trim()).filter(t => t);
|
|
5889
|
-
for (const t of parsed) {
|
|
5890
|
-
const mapped = convertGeminiToolName(t);
|
|
5891
|
-
if (mapped) tools.push(mapped);
|
|
5892
|
-
}
|
|
5893
|
-
} else {
|
|
5894
|
-
// tools: with no value means YAML array follows
|
|
5895
|
-
inAllowedTools = true;
|
|
5896
|
-
}
|
|
5897
|
-
continue;
|
|
5898
|
-
}
|
|
5899
|
-
|
|
5900
|
-
// Strip color field (not supported by Gemini CLI, causes validation error)
|
|
5901
|
-
if (trimmed.startsWith('color:')) continue;
|
|
5902
|
-
|
|
5903
|
-
// Strip skills field (not supported by Gemini CLI, causes validation error)
|
|
5904
|
-
if (trimmed.startsWith('skills:')) {
|
|
5905
|
-
inSkippedArrayField = true;
|
|
5906
|
-
continue;
|
|
5907
|
-
}
|
|
5908
|
-
|
|
5909
|
-
// Collect allowed-tools/tools array items
|
|
5910
|
-
if (inAllowedTools) {
|
|
5911
|
-
if (trimmed.startsWith('- ')) {
|
|
5912
|
-
const mapped = convertGeminiToolName(trimmed.substring(2).trim());
|
|
5913
|
-
if (mapped) tools.push(mapped);
|
|
5914
|
-
continue;
|
|
5915
|
-
} else if (trimmed && !trimmed.startsWith('-')) {
|
|
5916
|
-
inAllowedTools = false;
|
|
5917
|
-
}
|
|
5918
|
-
}
|
|
5919
|
-
|
|
5920
|
-
if (!inAllowedTools) {
|
|
5921
|
-
newLines.push(line);
|
|
5922
|
-
}
|
|
5923
|
-
}
|
|
5924
|
-
|
|
5925
|
-
// Add tools as YAML array (Gemini requires array format)
|
|
5926
|
-
if (tools.length > 0) {
|
|
5927
|
-
newLines.push('tools:');
|
|
5928
|
-
for (const tool of tools) {
|
|
5929
|
-
newLines.push(` - ${tool}`);
|
|
5930
|
-
}
|
|
5931
|
-
}
|
|
5932
|
-
|
|
5933
|
-
const newFrontmatter = newLines.join('\n').trim();
|
|
5934
|
-
|
|
5935
|
-
// Escape ${VAR} patterns in agent body for Gemini CLI compatibility.
|
|
5936
|
-
// Gemini's templateString() treats all ${word} patterns as template variables
|
|
5937
|
-
// and throws "Template validation failed: Missing required input parameters"
|
|
5938
|
-
// when they can't be resolved. GSD agents use ${PHASE}, ${PLAN}, etc. as
|
|
5939
|
-
// shell variables in bash code blocks — convert to $VAR (no braces) which
|
|
5940
|
-
// is equivalent bash and invisible to Gemini's /\$\{(\w+)\}/g regex.
|
|
5941
|
-
const escapedBody = body.replace(/\$\{(\w+)\}/g, '$$$1');
|
|
5942
|
-
|
|
5943
|
-
// Runtime-neutral agent name replacement (#766)
|
|
5944
|
-
const neutralBody = neutralizeAgentReferences(escapedBody, 'GEMINI.md');
|
|
5945
|
-
// Apply Gemini-specific transformations (slash commands + sub-tag stripping)
|
|
5946
|
-
const geminiBody = convertClaudeToGeminiMarkdown(neutralBody);
|
|
5947
|
-
return `---\n${newFrontmatter}\n---${geminiBody}`;
|
|
5948
|
-
}
|
|
5949
|
-
|
|
5950
5772
|
function convertClaudeToOpencodeFrontmatter(content, { isAgent = false, modelOverride = null } = {}) {
|
|
5951
5773
|
// Replace tool name references in content (applies to all files)
|
|
5952
5774
|
let convertedContent = content;
|
|
@@ -6283,76 +6105,6 @@ function convertClaudeToKiloFrontmatter(content, { isAgent = false } = {}) {
|
|
|
6283
6105
|
// convertClaudeCommandToKiloSkill: moved to src/install-engine.cts (ADR-1239 Phase B).
|
|
6284
6106
|
// Imported from installEngine above.
|
|
6285
6107
|
|
|
6286
|
-
/**
|
|
6287
|
-
* Convert Claude Code markdown command to Gemini TOML format
|
|
6288
|
-
* @param {string} content - Markdown file content with YAML frontmatter
|
|
6289
|
-
* @returns {string} - TOML content
|
|
6290
|
-
*/
|
|
6291
|
-
function convertClaudeToGeminiToml(content, { commandName = null } = {}) {
|
|
6292
|
-
// #778 (c) — Gemini {{args}} interpolation. Claude's $ARGUMENTS placeholder
|
|
6293
|
-
// maps to Gemini's {{args}} so inline argument references interpolate into the
|
|
6294
|
-
// command body instead of being emitted as a dead literal. Applied before
|
|
6295
|
-
// frontmatter parsing so every return path benefits (a command's frontmatter
|
|
6296
|
-
// never contains $ARGUMENTS, so this is body-only in practice). Gemini injects
|
|
6297
|
-
// {{args}} as typed outside shell blocks; we never place it inside a !{...}
|
|
6298
|
-
// block, so there is no shell-escaping/injection interaction.
|
|
6299
|
-
content = content.replace(/\$ARGUMENTS\b/g, '{{args}}');
|
|
6300
|
-
|
|
6301
|
-
// Check if content has frontmatter
|
|
6302
|
-
if (!content.startsWith('---')) {
|
|
6303
|
-
return `prompt = ${JSON.stringify(content)}\n`;
|
|
6304
|
-
}
|
|
6305
|
-
|
|
6306
|
-
const endIndex = content.indexOf('---', 3);
|
|
6307
|
-
if (endIndex === -1) {
|
|
6308
|
-
return `prompt = ${JSON.stringify(content)}\n`;
|
|
6309
|
-
}
|
|
6310
|
-
|
|
6311
|
-
const frontmatter = content.substring(3, endIndex).trim();
|
|
6312
|
-
let body = content.substring(endIndex + 3).trim();
|
|
6313
|
-
|
|
6314
|
-
// #778 (c) — Gemini !{...} dynamic-output injection for the situational
|
|
6315
|
-
// `progress` command (GSD's status/dashboard surface). Inject the live
|
|
6316
|
-
// .planning/STATE.md so the model sees current project state without relying
|
|
6317
|
-
// on session memory.
|
|
6318
|
-
//
|
|
6319
|
-
// SECURITY: the shell command is a FIXED `cat` with NO interpolated user
|
|
6320
|
-
// input — no {{args}} appears inside the block — so there is no
|
|
6321
|
-
// shell-injection vector. Gemini still shows its standard per-invocation
|
|
6322
|
-
// confirmation dialog (verified behavior). `2>/dev/null` keeps an
|
|
6323
|
-
// uninitialized project (missing STATE.md) from injecting stderr noise.
|
|
6324
|
-
// Braces inside the block are balanced (none present), per Gemini's parser
|
|
6325
|
-
// requirement. The append happens AFTER the {{args}} mapping above so the
|
|
6326
|
-
// injected block can never accidentally carry interpolated arguments.
|
|
6327
|
-
if (commandName === 'progress') {
|
|
6328
|
-
body += '\n\n## Live project state\n'
|
|
6329
|
-
+ 'Current contents of `.planning/STATE.md` '
|
|
6330
|
-
+ '(empty if the project is not yet initialized):\n\n'
|
|
6331
|
-
+ '!{cat .planning/STATE.md 2>/dev/null}\n';
|
|
6332
|
-
}
|
|
6333
|
-
|
|
6334
|
-
// Extract description from frontmatter
|
|
6335
|
-
let description = '';
|
|
6336
|
-
const lines = frontmatter.split('\n');
|
|
6337
|
-
for (const line of lines) {
|
|
6338
|
-
const trimmed = line.trim();
|
|
6339
|
-
if (trimmed.startsWith('description:')) {
|
|
6340
|
-
description = trimmed.substring(12).trim();
|
|
6341
|
-
break;
|
|
6342
|
-
}
|
|
6343
|
-
}
|
|
6344
|
-
|
|
6345
|
-
// Construct TOML
|
|
6346
|
-
let toml = '';
|
|
6347
|
-
if (description) {
|
|
6348
|
-
toml += `description = ${JSON.stringify(description)}\n`;
|
|
6349
|
-
}
|
|
6350
|
-
|
|
6351
|
-
toml += `prompt = ${JSON.stringify(body)}\n`;
|
|
6352
|
-
|
|
6353
|
-
return toml;
|
|
6354
|
-
}
|
|
6355
|
-
|
|
6356
6108
|
/**
|
|
6357
6109
|
* Copy commands to a flat structure for OpenCode
|
|
6358
6110
|
* OpenCode expects: command/gsd-help.md (invoked as /gsd-help)
|
|
@@ -6530,7 +6282,9 @@ const _stampNonClaudeRuntimeDefaults = runtimeArtifactConversion._stampNonClaude
|
|
|
6530
6282
|
* mdSkipGenericRewrite?: boolean — skip the ~/.claude/ rewrite block (copilot, antigravity)
|
|
6531
6283
|
* md?: (content, ctx) => string — per-runtime .md transform
|
|
6532
6284
|
* mdReattributeAfter?: boolean — re-run processAttribution after md() (copilot, antigravity)
|
|
6533
|
-
* mdTomlRenameOnCommand?: boolean — when isCommand, rename dest .md → .toml
|
|
6285
|
+
* mdTomlRenameOnCommand?: boolean — when isCommand, rename dest .md → .toml
|
|
6286
|
+
* (unused since the gemini runtime was removed, #1928;
|
|
6287
|
+
* kept as generic dispatch infra for a future TOML-command runtime)
|
|
6534
6288
|
* js?: (content, ctx) => string — per-runtime .cjs/.js transform (absent = plain copyFileSync)
|
|
6535
6289
|
*
|
|
6536
6290
|
* ctx = { isCommand, isGlobal, dirName, pathPrefix, entryName, runtime }
|
|
@@ -6542,14 +6296,6 @@ const RUNTIME_CONTENT_DISPATCH = {
|
|
|
6542
6296
|
kilo: {
|
|
6543
6297
|
md: (content) => convertClaudeToKiloFrontmatter(content),
|
|
6544
6298
|
},
|
|
6545
|
-
gemini: {
|
|
6546
|
-
md: (content, ctx) =>
|
|
6547
|
-
convertClaudeToGeminiMarkdown(content, {
|
|
6548
|
-
isCommand: ctx.isCommand,
|
|
6549
|
-
commandName: ctx.isCommand ? ctx.entryName.replace(/\.md$/, '') : null,
|
|
6550
|
-
}),
|
|
6551
|
-
mdTomlRenameOnCommand: true,
|
|
6552
|
-
},
|
|
6553
6299
|
codex: {
|
|
6554
6300
|
md: (content) => convertClaudeToCodexMarkdown(content),
|
|
6555
6301
|
},
|
|
@@ -6645,7 +6391,7 @@ const RUNTIME_CONTENT_DISPATCH = {
|
|
|
6645
6391
|
* @param {string} srcDir - Source directory
|
|
6646
6392
|
* @param {string} destDir - Destination directory
|
|
6647
6393
|
* @param {string} pathPrefix - Path prefix for file references
|
|
6648
|
-
* @param {string} runtime - Target runtime ('claude', 'opencode', '
|
|
6394
|
+
* @param {string} runtime - Target runtime ('claude', 'opencode', 'codex')
|
|
6649
6395
|
* @param {boolean} isCommand - Whether the source is a command directory
|
|
6650
6396
|
* @param {boolean} isGlobal - Whether the install is global
|
|
6651
6397
|
*/
|
|
@@ -6724,7 +6470,7 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime, isCommand
|
|
|
6724
6470
|
// copyWithPathReplacement for runtimes that register commands under the
|
|
6725
6471
|
// hyphen form; normalizeAgentBodyForRuntime self-gates on
|
|
6726
6472
|
// shouldNormalizeHyphenNamespaceInAgentBody(runtime) and is a no-op for
|
|
6727
|
-
// colon-canonical runtimes
|
|
6473
|
+
// colon-canonical / self-converting runtimes.
|
|
6728
6474
|
content = normalizeAgentBodyForRuntime(content, runtime, readGsdCommandNames());
|
|
6729
6475
|
|
|
6730
6476
|
// Apply per-runtime .md converter (if any)
|
|
@@ -6733,7 +6479,7 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime, isCommand
|
|
|
6733
6479
|
// Re-run attribution after converter for runtimes that need it (copilot, antigravity)
|
|
6734
6480
|
if (dispatch.mdReattributeAfter) content = processAttribution(content, getCommitAttribution(runtime));
|
|
6735
6481
|
|
|
6736
|
-
//
|
|
6482
|
+
// Rename .md → .toml for command files (unused since gemini removal, #1928)
|
|
6737
6483
|
const finalPath = (dispatch.mdTomlRenameOnCommand && isCommand) ? destPath.replace(/\.md$/, '.toml') : destPath;
|
|
6738
6484
|
fs.writeFileSync(finalPath, content);
|
|
6739
6485
|
} else if (entry.name.endsWith('.cjs') || entry.name.endsWith('.js')) {
|
|
@@ -6916,10 +6662,10 @@ const GSD_UNINSTALL_HOOKS = [
|
|
|
6916
6662
|
* Uninstall GSD from the specified directory for a specific runtime
|
|
6917
6663
|
* Removes only GSD-specific files/directories, preserves user content
|
|
6918
6664
|
* @param {boolean} isGlobal - Whether to uninstall from global or local
|
|
6919
|
-
* @param {string} runtime - Target runtime ('claude', 'opencode', '
|
|
6665
|
+
* @param {string} runtime - Target runtime ('claude', 'opencode', 'codex', 'copilot')
|
|
6920
6666
|
*/
|
|
6921
6667
|
function uninstall(isGlobal, runtime = 'claude') {
|
|
6922
|
-
const { isOpencode, isKilo,
|
|
6668
|
+
const { isOpencode, isKilo, isCodex, isCopilot, isAntigravity, isCursor, isWindsurf, isAugment, isTrae, isQwen, isHermes, isCodebuddy, isCline, isKimi } = runtimeFlags(runtime);
|
|
6923
6669
|
const dirName = getDirName(runtime);
|
|
6924
6670
|
|
|
6925
6671
|
// Get the target directory based on runtime and install type. Cline local
|
|
@@ -7179,29 +6925,6 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
7179
6925
|
}
|
|
7180
6926
|
}
|
|
7181
6927
|
|
|
7182
|
-
// 1d. Gemini: remove commands/gsd/ with dev-preferences.md preservation.
|
|
7183
|
-
// The layout removes gsd-*.toml files but not the directory itself.
|
|
7184
|
-
// Preserve user files before removing the directory.
|
|
7185
|
-
if (isGemini) {
|
|
7186
|
-
const gsdCommandsDir = path.join(targetDir, 'commands', 'gsd');
|
|
7187
|
-
if (fs.existsSync(gsdCommandsDir)) {
|
|
7188
|
-
const devPrefsPath = path.join(gsdCommandsDir, 'dev-preferences.md');
|
|
7189
|
-
const preservedDevPrefs = fs.existsSync(devPrefsPath) ? fs.readFileSync(devPrefsPath, 'utf-8') : null;
|
|
7190
|
-
fs.rmSync(gsdCommandsDir, { recursive: true });
|
|
7191
|
-
removedCount++;
|
|
7192
|
-
console.log(` ${green}✓${reset} Removed commands/gsd/`);
|
|
7193
|
-
if (preservedDevPrefs) {
|
|
7194
|
-
try {
|
|
7195
|
-
fs.mkdirSync(gsdCommandsDir, { recursive: true });
|
|
7196
|
-
fs.writeFileSync(devPrefsPath, preservedDevPrefs);
|
|
7197
|
-
console.log(` ${green}✓${reset} Preserved commands/gsd/dev-preferences.md`);
|
|
7198
|
-
} catch (err) {
|
|
7199
|
-
console.error(` ${red}✗${reset} Failed to restore dev-preferences.md: ${err.message}`);
|
|
7200
|
-
}
|
|
7201
|
-
}
|
|
7202
|
-
}
|
|
7203
|
-
}
|
|
7204
|
-
|
|
7205
6928
|
// 1d. Qwen/Hermes: migrate dev-preferences.md from legacy commands/gsd/ location
|
|
7206
6929
|
// during uninstall. _runLegacyUninstallCleanup (called by uninstallRuntimeArtifacts)
|
|
7207
6930
|
// removes the directory; we must preserve/restore user artifacts before that path.
|
|
@@ -7425,7 +7148,7 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
7425
7148
|
// Remove GSD hooks from settings — per-hook granularity to preserve
|
|
7426
7149
|
// user hooks that share an entry with a GSD hook (#1755 followup).
|
|
7427
7150
|
// Includes the 3 Qwen-only events added in #788 (SubagentStop, Stop,
|
|
7428
|
-
// PreCompact, also registered for Claude in #770), the 3
|
|
7151
|
+
// PreCompact, also registered for Claude in #770), the 3 Antigravity-only
|
|
7429
7152
|
// events added in #776 (BeforeAgent, AfterAgent, BeforeModel), and the
|
|
7430
7153
|
// Claude-only FileChanged event added in #770 — safe to iterate for all
|
|
7431
7154
|
// runtimes; installs that don't register these events simply find no
|
|
@@ -7870,7 +7593,7 @@ function verifyFileInstalled(filePath, description) {
|
|
|
7870
7593
|
/**
|
|
7871
7594
|
* Install to the specified directory for a specific runtime
|
|
7872
7595
|
* @param {boolean} isGlobal - Whether to install globally or locally
|
|
7873
|
-
* @param {string} runtime - Target runtime ('claude', 'opencode', '
|
|
7596
|
+
* @param {string} runtime - Target runtime ('claude', 'opencode', 'codex')
|
|
7874
7597
|
*/
|
|
7875
7598
|
|
|
7876
7599
|
// ──────────────────────────────────────────────────────
|
|
@@ -7944,12 +7667,10 @@ function resolveInstallRelativePath(baseDir, relPath) {
|
|
|
7944
7667
|
* Write file manifest after installation for future modification detection
|
|
7945
7668
|
*/
|
|
7946
7669
|
function writeManifest(configDir, runtime = 'claude', options = {}) {
|
|
7947
|
-
const { isOpencode, isKilo,
|
|
7670
|
+
const { isOpencode, isKilo, isCodex, isCopilot, isAntigravity, isCursor, isWindsurf, isAugment, isTrae, isQwen, isHermes, isCodebuddy, isCline, isKimi } = runtimeFlags(runtime);
|
|
7948
7671
|
const gsdDir = path.join(configDir, 'gsd-core');
|
|
7949
7672
|
// #1367: Claude local now writes flat gsd-*.md files at commands/ (not commands/gsd/).
|
|
7950
|
-
// commandsDir points to the old location for Gemini (which still uses commands/gsd/).
|
|
7951
7673
|
// Claude local uses flatCommandsDir instead for manifest recording.
|
|
7952
|
-
const commandsDir = path.join(configDir, 'commands', 'gsd');
|
|
7953
7674
|
const flatCommandsDir = path.join(configDir, 'commands');
|
|
7954
7675
|
const opencodeCommandDir = path.join(configDir, 'command');
|
|
7955
7676
|
// Hermes nests GSD skills under skills/gsd/ as a single category (#2841).
|
|
@@ -7977,16 +7698,9 @@ function writeManifest(configDir, runtime = 'claude', options = {}) {
|
|
|
7977
7698
|
manifest.files['gsd-core/' + rel] = hash;
|
|
7978
7699
|
}
|
|
7979
7700
|
// Record commands surface for runtimes that emit it:
|
|
7980
|
-
// Gemini: commands/gsd/<cmd>.toml (nested, colon-namespaced)
|
|
7981
7701
|
// Claude local (#1367 fix): flat gsd-<cmd>.md at commands/ level
|
|
7982
7702
|
// Manifest must reflect everything on disk so saveLocalPatches() can detect
|
|
7983
7703
|
// user edits and per-runtime minimal-mode assertions can read manifest.files.
|
|
7984
|
-
if (isGemini && fs.existsSync(commandsDir)) {
|
|
7985
|
-
const cmdHashes = generateManifest(commandsDir);
|
|
7986
|
-
for (const [rel, hash] of Object.entries(cmdHashes)) {
|
|
7987
|
-
manifest.files['commands/gsd/' + rel] = hash;
|
|
7988
|
-
}
|
|
7989
|
-
}
|
|
7990
7704
|
// Claude local (#1367): flat gsd-*.md files at commands/ level.
|
|
7991
7705
|
// Only claude local writes gsd-*.md here; global installs don't emit commands,
|
|
7992
7706
|
// so this branch is a no-op for global (no matching files to find).
|
|
@@ -8004,7 +7718,7 @@ function writeManifest(configDir, runtime = 'claude', options = {}) {
|
|
|
8004
7718
|
}
|
|
8005
7719
|
}
|
|
8006
7720
|
}
|
|
8007
|
-
if ((isCodex || isCopilot || isAntigravity || isCursor || isWindsurf || isTrae ||
|
|
7721
|
+
if ((isCodex || isCopilot || isAntigravity || isCursor || isWindsurf || isTrae || !isOpencode) && fs.existsSync(codexSkillsDir)) {
|
|
8008
7722
|
// All runtimes (including Hermes post-#947) use the canonical 'gsd-' prefix.
|
|
8009
7723
|
const skillListPrefix = 'gsd-';
|
|
8010
7724
|
for (const skillName of listCodexSkillNames(codexSkillsDir, skillListPrefix)) {
|
|
@@ -8414,10 +8128,8 @@ function reportLocalPatches(configDir, runtime = 'claude') {
|
|
|
8414
8128
|
if (meta.files && meta.files.length > 0) {
|
|
8415
8129
|
const reapplyCommand = (runtime === 'opencode' || runtime === 'kilo' || runtime === 'copilot')
|
|
8416
8130
|
? '/gsd-update --reapply'
|
|
8417
|
-
: runtime === '
|
|
8418
|
-
? '
|
|
8419
|
-
: runtime === 'codex'
|
|
8420
|
-
? '$gsd-update --reapply'
|
|
8131
|
+
: runtime === 'codex'
|
|
8132
|
+
? '$gsd-update --reapply'
|
|
8421
8133
|
: runtime === 'cursor'
|
|
8422
8134
|
? 'gsd-update --reapply (mention the skill name)'
|
|
8423
8135
|
: runtime === 'kimi'
|
|
@@ -8449,7 +8161,7 @@ function reportInstallerMigrationResult(result) {
|
|
|
8449
8161
|
}
|
|
8450
8162
|
|
|
8451
8163
|
function install(isGlobal, runtime = 'claude', options = {}) {
|
|
8452
|
-
const { isOpencode, isKilo,
|
|
8164
|
+
const { isOpencode, isKilo, isCodex, isCopilot, isAntigravity, isCursor, isWindsurf, isAugment, isTrae, isQwen, isHermes, isCodebuddy, isCline, isKimi } = runtimeFlags(runtime);
|
|
8453
8165
|
const plan = resolveInstallPlan(runtime);
|
|
8454
8166
|
const dirName = getDirName(runtime);
|
|
8455
8167
|
const src = path.join(__dirname, '..');
|
|
@@ -8864,7 +8576,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
8864
8576
|
|
|
8865
8577
|
// Artifact install dispatcher — routes to layout-driven path for all
|
|
8866
8578
|
// skills-based runtimes (both full and minimal/core profiles); keeps
|
|
8867
|
-
// back-compat paths for commands-based runtimes (OpenCode/Kilo/
|
|
8579
|
+
// back-compat paths for commands-based runtimes (OpenCode/Kilo/
|
|
8868
8580
|
// Claude-local).
|
|
8869
8581
|
//
|
|
8870
8582
|
// installRuntimeArtifacts handles legacy migration + skill/agent staging
|
|
@@ -8875,7 +8587,6 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
8875
8587
|
// Hermes: writeHermesCategoryDescription (not a layout kind)
|
|
8876
8588
|
// Cline global: skills emitted via layout; .clinerules still written below (#782)
|
|
8877
8589
|
// Cline local: no skills (only .clinerules) — falls through to cline-rules surface
|
|
8878
|
-
// Gemini: conflict-detection logic (not expressible in layout)
|
|
8879
8590
|
// OpenCode/Kilo: copyFlattenedCommands (frontmatter conversion not in commandsKind)
|
|
8880
8591
|
// Claude local: copyWithPathReplacement + stale-skills cleanup
|
|
8881
8592
|
|
|
@@ -8883,11 +8594,24 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
8883
8594
|
// applyRuntimeContentRewritesInPlace (called inside installRuntimeArtifacts)
|
|
8884
8595
|
// handles per-runtime path + branding rewrites, including Qwen/Hermes.
|
|
8885
8596
|
// Cline global: emit skills to ~/.cline/skills/ (Cline >= v3.48.0 — #782).
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8597
|
+
// Descriptor-driven (ADR-1016 / ADR-1239): a runtime takes the layout-driven
|
|
8598
|
+
// installRuntimeArtifacts path when its scoped artifactLayout is non-empty
|
|
8599
|
+
// (it declares any skills/commands/agents/kimi-agents kind for this scope).
|
|
8600
|
+
// This replaces the prior hardcoded `isCodex || isCopilot || ...` roster so a
|
|
8601
|
+
// newly-added runtime with an artifact layout installs without a per-runtime
|
|
8602
|
+
// branch — the add-a-host tax ADR-1239 Phase B retires. Three legacy
|
|
8603
|
+
// special-cased paths are preserved: opencode/kilo (combined commands+skills
|
|
8604
|
+
// via copyFlattenedCommands + installOpencodeFamilySkills) and claude-local
|
|
8605
|
+
// (copyWithPathReplacement + stale-skills cleanup).
|
|
8606
|
+
const _isSkillsRuntime = (() => {
|
|
8607
|
+
if (isOpencode || isKilo) return false; // specialized combined path
|
|
8608
|
+
if (runtime === 'claude' && !isGlobal) return false; // claude-local legacy path
|
|
8609
|
+
const cap = _capabilityRegistry && _capabilityRegistry.runtimes && _capabilityRegistry.runtimes[runtime];
|
|
8610
|
+
const layout = cap && cap.runtime && cap.runtime.artifactLayout;
|
|
8611
|
+
if (!layout) return false;
|
|
8612
|
+
const scopeLayout = isGlobal ? layout.global : layout.local;
|
|
8613
|
+
return Array.isArray(scopeLayout) && scopeLayout.length > 0;
|
|
8614
|
+
})();
|
|
8891
8615
|
|
|
8892
8616
|
if (_isSkillsRuntime) {
|
|
8893
8617
|
// Layout-driven install for skills-based runtimes (full and minimal modes)
|
|
@@ -9096,57 +8820,6 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
9096
8820
|
// No skills/commands directory needed for local installs.
|
|
9097
8821
|
// Global installs are handled above by _isSkillsRuntime (#782).
|
|
9098
8822
|
console.log(` ${green}✓${reset} Cline: commands will be available via .clinerules`);
|
|
9099
|
-
} else if (isGemini) {
|
|
9100
|
-
// #3037: when running --local --gemini and a GSD-managed user-scope
|
|
9101
|
-
// command directory already exists at ~/.gemini/commands/gsd/, skip
|
|
9102
|
-
// the local copy. Gemini conflict-detects by command name across
|
|
9103
|
-
// scopes and renames every overlapping /gsd:* command to
|
|
9104
|
-
// /workspace.gsd:* and /user.gsd:*, breaking the documented namespace.
|
|
9105
|
-
// The user-scope install already provides the same commands, so the
|
|
9106
|
-
// local copy adds zero value at the cost of namespace conflicts.
|
|
9107
|
-
//
|
|
9108
|
-
// CR #3041 (Major): the detection must be specific to PACKAGE-MANAGED
|
|
9109
|
-
// GSD content, not just "directory is non-empty". A user who hand-
|
|
9110
|
-
// dropped a single override (e.g. ~/.gemini/commands/gsd/my-override
|
|
9111
|
-
// .toml) would otherwise be unable to run a local install at all.
|
|
9112
|
-
// Detection rule: at least 3 of the canonical GSD command files
|
|
9113
|
-
// ('help.toml', 'progress.toml', 'new-project.toml') must be present.
|
|
9114
|
-
// These three ship in every GSD Gemini install (minimal mode included
|
|
9115
|
-
// — they're in the core skill set per #2790's consolidation), and 3-of-
|
|
9116
|
-
// 3 with that specific basename set is structurally impossible to
|
|
9117
|
-
// produce by accident.
|
|
9118
|
-
const homeGeminiGsd = path.join(os.homedir(), '.gemini', 'commands', 'gsd');
|
|
9119
|
-
const GSD_MANAGED_CANARIES = ['help.toml', 'progress.toml', 'new-project.toml'];
|
|
9120
|
-
const userScopeHasGsd =
|
|
9121
|
-
!isGlobal &&
|
|
9122
|
-
path.resolve(targetDir) !== path.resolve(path.join(os.homedir(), '.gemini')) &&
|
|
9123
|
-
fs.existsSync(homeGeminiGsd) &&
|
|
9124
|
-
GSD_MANAGED_CANARIES.every((f) =>
|
|
9125
|
-
fs.existsSync(path.join(homeGeminiGsd, f))
|
|
9126
|
-
);
|
|
9127
|
-
|
|
9128
|
-
if (userScopeHasGsd) {
|
|
9129
|
-
console.log(
|
|
9130
|
-
` ${yellow}⚠${reset} Skipping commands/gsd/ for local install — GSD is already installed at user scope (${homeGeminiGsd}).`
|
|
9131
|
-
);
|
|
9132
|
-
console.log(
|
|
9133
|
-
` Gemini conflict-detects across scopes and would rename every /gsd:* command to /workspace.gsd:* and /user.gsd:*.`
|
|
9134
|
-
);
|
|
9135
|
-
console.log(
|
|
9136
|
-
` The user-scope install already provides /gsd:* commands in this project; no local copy is needed.`
|
|
9137
|
-
);
|
|
9138
|
-
} else {
|
|
9139
|
-
const commandsDir = path.join(targetDir, 'commands');
|
|
9140
|
-
fs.mkdirSync(commandsDir, { recursive: true });
|
|
9141
|
-
const gsdSrc = _stageSkills(_commandsDir);
|
|
9142
|
-
const gsdDest = path.join(commandsDir, 'gsd');
|
|
9143
|
-
copyWithPathReplacement(gsdSrc, gsdDest, pathPrefix, runtime, true, isGlobal, targetDir);
|
|
9144
|
-
if (verifyInstalled(gsdDest, 'commands/gsd')) {
|
|
9145
|
-
console.log(` ${green}✓${reset} Installed commands/gsd`);
|
|
9146
|
-
} else {
|
|
9147
|
-
failures.push('commands/gsd');
|
|
9148
|
-
}
|
|
9149
|
-
}
|
|
9150
8823
|
} else {
|
|
9151
8824
|
// Claude Code local: flat gsd-<cmd>.md layout — Claude Code registers
|
|
9152
8825
|
// commands from .claude/commands/ using the filename stem as the command
|
|
@@ -9230,6 +8903,33 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
9230
8903
|
failures.push('gsd-core');
|
|
9231
8904
|
}
|
|
9232
8905
|
|
|
8906
|
+
// Write the .gsd-source marker so runtime source resolution succeeds at
|
|
8907
|
+
// runtime (#1477). The Claude-global skills layout ships gsd-core/{bin,
|
|
8908
|
+
// contexts,references,templates,workflows} but NOT the commands/gsd source
|
|
8909
|
+
// tree, and _runLegacyUninstallCleanup actively removes any commands/gsd/
|
|
8910
|
+
// for that scope — so findInstallSourceRoot's walk-up has nothing to find
|
|
8911
|
+
// and /gsd-surface (list/status) throws. This is the writer half of the
|
|
8912
|
+
// marker that runtime-artifact-layout.cjs's finders already read (the reader
|
|
8913
|
+
// landed in #1476). It points at the package's own commands/gsd source.
|
|
8914
|
+
// Scoped to the Claude-global layout (issue #1477) — the only install path
|
|
8915
|
+
// that ships the skills layout without a commands/gsd source tree; every
|
|
8916
|
+
// other runtime/scope deploys commands/gsd, so its walk-up already resolves
|
|
8917
|
+
// and needs no marker. Guarded on source presence so a half-published
|
|
8918
|
+
// package never writes a dangling marker.
|
|
8919
|
+
if (runtime === 'claude' && isGlobal) {
|
|
8920
|
+
const gsdSourceCommands = path.join(src, 'commands', 'gsd');
|
|
8921
|
+
if (fs.existsSync(gsdSourceCommands)) {
|
|
8922
|
+
try {
|
|
8923
|
+
fs.writeFileSync(path.join(targetDir, '.gsd-source'), gsdSourceCommands + '\n', 'utf8');
|
|
8924
|
+
} catch (err) {
|
|
8925
|
+
// Non-fatal: install proceeds. But on the Claude-global layout walk-up
|
|
8926
|
+
// also fails (no commands/gsd source tree), so a silent write failure
|
|
8927
|
+
// still leaves /gsd-surface broken at runtime — warn so it's diagnosable.
|
|
8928
|
+
console.warn(` ${yellow}!${reset} Could not write .gsd-source marker (${err.message}); /gsd-surface list/status may fail`);
|
|
8929
|
+
}
|
|
8930
|
+
}
|
|
8931
|
+
}
|
|
8932
|
+
|
|
9233
8933
|
// #1629 critical fix: Windsurf workflow wrappers (convertClaudeCommandToWindsurfWorkflow)
|
|
9234
8934
|
// delegate to command bodies at <targetDir>/gsd-core/commands/gsd/${stem}.md via a
|
|
9235
8935
|
// hardcoded @~/.claude/gsd-core/commands/gsd/ path that _applyRuntimeRewrites rewrites
|
|
@@ -9292,9 +8992,11 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
9292
8992
|
// (by installRuntimeArtifacts at line 8912), which also performs its own
|
|
9293
8993
|
// stale-file prune pass. The inline stale-removal + inline loop both skip them.
|
|
9294
8994
|
// Trivial group (cursor/windsurf/augment/trae/codebuddy) cut over together.
|
|
9295
|
-
//
|
|
9296
|
-
//
|
|
9297
|
-
|
|
8995
|
+
// #1575: copilot and antigravity cut over — copilot gets .agent.md filename
|
|
8996
|
+
// rename via _copyStaged(runtime); antigravity uses scope-aware converter.
|
|
8997
|
+
// cline remains excluded: rules-only local branch + local/global complication
|
|
8998
|
+
// that the descriptor-driven path does not handle correctly.
|
|
8999
|
+
const _DESCRIPTOR_AGENTS_RUNTIMES = new Set(['cursor', 'windsurf', 'augment', 'trae', 'codebuddy', 'copilot', 'antigravity']);
|
|
9298
9000
|
|
|
9299
9001
|
// Always remove stale gsd-* agents first so re-installing with
|
|
9300
9002
|
// `--minimal` actually shrinks a previously-full install.
|
|
@@ -9379,8 +9081,6 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
9379
9081
|
content = convertClaudeToOpencodeFrontmatter(content, { isAgent: true, modelOverride: _ocModelOverride });
|
|
9380
9082
|
} else if (isKilo) {
|
|
9381
9083
|
content = convertClaudeToKiloFrontmatter(content, { isAgent: true });
|
|
9382
|
-
} else if (isGemini) {
|
|
9383
|
-
content = convertClaudeToGeminiAgent(content);
|
|
9384
9084
|
} else if (isCodex) {
|
|
9385
9085
|
content = convertClaudeAgentToCodexAgent(content);
|
|
9386
9086
|
} else if (isCopilot) {
|
|
@@ -9409,12 +9109,12 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
9409
9109
|
content = content.replace(/\.claude\//g, '.hermes/');
|
|
9410
9110
|
}
|
|
9411
9111
|
// #443 — Inject `effort:` into the Claude .md frontmatter ONLY.
|
|
9412
|
-
//
|
|
9112
|
+
// OpenCode/Qwen/Hermes also produce .md files but break on
|
|
9413
9113
|
// unknown frontmatter keys (the repo bans skills:/permissionMode: for
|
|
9414
9114
|
// the same reason — see tests/agent-frontmatter.test.cjs).
|
|
9415
9115
|
// Claude Code reads per-subagent `effort:` frontmatter (anthropics/claude-code #31536).
|
|
9416
9116
|
// Injection is per-runtime at install time because the canonical source
|
|
9417
|
-
// agents/*.md must stay
|
|
9117
|
+
// agents/*.md must stay runtime-safe (no effort: key in source).
|
|
9418
9118
|
if (runtime === 'claude') {
|
|
9419
9119
|
const _effortCfg = readGsdEffectiveEffortConfig(targetDir);
|
|
9420
9120
|
const _agentName = entry.name.replace(/\.md$/, '');
|
|
@@ -9426,8 +9126,8 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
9426
9126
|
}
|
|
9427
9127
|
// #3677 — normalize retired `/gsd:<cmd>` colon refs in the agent body
|
|
9428
9128
|
// to the canonical hyphen form `/gsd-<cmd>` for hyphen-`name:`
|
|
9429
|
-
// runtimes (claude / qwen / hermes). Self-converting
|
|
9430
|
-
//
|
|
9129
|
+
// runtimes (claude / qwen / hermes). Self-converting and
|
|
9130
|
+
// colon-canonical runtimes are skipped by the predicate — see
|
|
9431
9131
|
// shouldNormalizeHyphenNamespaceInAgentBody above. Mirrors the
|
|
9432
9132
|
// SKILL.md-body fix shipped via #3629.
|
|
9433
9133
|
content = normalizeAgentBodyForRuntime(content, runtime, readGsdCommandNames());
|
|
@@ -10209,7 +9909,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
10209
9909
|
// Configure statusline and hooks in settings.json (or settings.local.json for local Claude installs).
|
|
10210
9910
|
// ADR-857 phase 5f-2: drive the hook event dialect from the registry descriptor.
|
|
10211
9911
|
// runtimes with hookEvents='gemini' use AfterTool/BeforeTool; all others use PostToolUse/PreToolUse.
|
|
10212
|
-
// Equivalence: hookEvents='gemini' iff runtime
|
|
9912
|
+
// Equivalence: hookEvents='gemini' iff runtime===antigravity — identical to the old check.
|
|
10213
9913
|
// A missing registry or missing descriptor defaults to 'not gemini' → PostToolUse (safe).
|
|
10214
9914
|
const _hookEventsDialect = plan.hookEvents;
|
|
10215
9915
|
const postToolEvent = _hookEventsDialect === 'gemini' ? 'AfterTool' : 'PostToolUse';
|
|
@@ -10309,9 +10009,9 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
10309
10009
|
console.log(` ${green}✓${reset} Rewrote legacy bare-node managed-hook commands to absolute path (#2979)`);
|
|
10310
10010
|
}
|
|
10311
10011
|
// Local installs anchor hook paths so they resolve regardless of cwd (#1906).
|
|
10312
|
-
// Claude Code sets $CLAUDE_PROJECT_DIR;
|
|
10313
|
-
// Windows
|
|
10314
|
-
//
|
|
10012
|
+
// Claude Code sets $CLAUDE_PROJECT_DIR; Antigravity does not — and on
|
|
10013
|
+
// Windows its own substitution logic doubles the path (#2557). It runs
|
|
10014
|
+
// project hooks with the project dir as cwd, so bare relative paths work.
|
|
10315
10015
|
const localPrefix = projectLocalHookPrefix({ runtime, dirName });
|
|
10316
10016
|
const hookOpts = { portableHooks: hasPortableHooks, runtime };
|
|
10317
10017
|
// #2979: local-install hook commands also use the absolute node path so
|
|
@@ -10375,17 +10075,6 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
10375
10075
|
console.warn(` ${yellow}⚠${reset} Skipping managed JS hook registration — Node executable path unavailable (process.execPath is empty). See #2979 / #3002.`);
|
|
10376
10076
|
}
|
|
10377
10077
|
|
|
10378
|
-
// Enable experimental agents for Gemini CLI (required for custom sub-agents)
|
|
10379
|
-
if (isGemini) {
|
|
10380
|
-
if (!settings.experimental) {
|
|
10381
|
-
settings.experimental = {};
|
|
10382
|
-
}
|
|
10383
|
-
if (!settings.experimental.enableAgents) {
|
|
10384
|
-
settings.experimental.enableAgents = true;
|
|
10385
|
-
console.log(` ${green}✓${reset} Enabled experimental agents`);
|
|
10386
|
-
}
|
|
10387
|
-
}
|
|
10388
|
-
|
|
10389
10078
|
// Register all GSD-managed hook entries into settings.hooks.* for runtimes
|
|
10390
10079
|
// that use the settings.json hook surface (ADR-857 phase 5f-1b).
|
|
10391
10080
|
// settings is mutated in place by applySettingsJsonHooks.
|
|
@@ -10408,23 +10097,6 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
10408
10097
|
localShellCmd,
|
|
10409
10098
|
});
|
|
10410
10099
|
|
|
10411
|
-
// ── Gemini hooksConfig.enabled check (#776) ───────────────────────────────
|
|
10412
|
-
// Detect `hooksConfig.enabled: false` in the already-loaded settings object
|
|
10413
|
-
// and emit a clear warning. When this field is false the Gemini CLI silently
|
|
10414
|
-
// disables ALL hook execution — gsd hooks are registered but will never run.
|
|
10415
|
-
// The check is read-only (warning only; we do not mutate hooksConfig).
|
|
10416
|
-
// Note: we use the in-memory `settings` object (already read from disk and
|
|
10417
|
-
// cleaned up by validateHookFields/cleanupOrphanedHooks above) rather than
|
|
10418
|
-
// re-reading settings.json, avoiding a TOCTOU window between the two reads.
|
|
10419
|
-
if (isGemini && settings && settings.hooksConfig && settings.hooksConfig.enabled === false) {
|
|
10420
|
-
console.warn(
|
|
10421
|
-
` ${yellow}⚠${reset} Warning: hooksConfig.enabled is false in your Gemini settings.json.\n` +
|
|
10422
|
-
` gsd-core hooks are registered but will NOT run until you set\n` +
|
|
10423
|
-
` hooksConfig.enabled: true in ${path.join(targetDir, 'settings.json')}.`
|
|
10424
|
-
);
|
|
10425
|
-
}
|
|
10426
|
-
// ── end hooksConfig.enabled check ────────────────────────────────────────
|
|
10427
|
-
|
|
10428
10100
|
// Compute the update-banner hook command alongside the others so
|
|
10429
10101
|
// installAllRuntimes can register it at finalize time when the user opts
|
|
10430
10102
|
// in (#2795). Computed here (not in finishInstall) so the same buildHookCommand
|
|
@@ -10513,7 +10185,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
10513
10185
|
* Apply statusline config, then print completion message
|
|
10514
10186
|
*/
|
|
10515
10187
|
function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallStatusline, runtime = 'claude', isGlobal = true, configDir = null, bannerOpts = {}) {
|
|
10516
|
-
const { isOpencode, isKilo,
|
|
10188
|
+
const { isOpencode, isKilo, isCodex, isCopilot, isAntigravity, isCursor, isWindsurf, isAugment, isTrae, isQwen, isHermes, isCodebuddy, isCline, isKimi } = runtimeFlags(runtime);
|
|
10517
10189
|
const plan = resolveInstallPlan(runtime);
|
|
10518
10190
|
|
|
10519
10191
|
if (shouldInstallStatusline && plan.writesSharedSettings && !isOpencode) {
|
|
@@ -10569,7 +10241,7 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
|
|
|
10569
10241
|
|
|
10570
10242
|
// #768 — Pre-populate permissions.allow/deny for Claude Code installs.
|
|
10571
10243
|
// Merges GSD-owned entries non-destructively (preserves existing user permissions).
|
|
10572
|
-
// Scoped to Claude only:
|
|
10244
|
+
// Scoped to Claude only: antigravity/qwen/hermes/codebuddy also write
|
|
10573
10245
|
// settings.json but use different runtimes and do not use these permission strings.
|
|
10574
10246
|
if (runtime === 'claude') {
|
|
10575
10247
|
mergeClaudePermissions(settings);
|
|
@@ -10739,16 +10411,16 @@ const runtimeMap = {
|
|
|
10739
10411
|
'6': 'codex',
|
|
10740
10412
|
'7': 'copilot',
|
|
10741
10413
|
'8': 'cursor',
|
|
10742
|
-
'9': '
|
|
10743
|
-
'10': '
|
|
10744
|
-
'11': '
|
|
10745
|
-
'12': '
|
|
10746
|
-
'13': '
|
|
10747
|
-
'14': '
|
|
10748
|
-
'15': '
|
|
10749
|
-
'16': '
|
|
10414
|
+
'9': 'hermes',
|
|
10415
|
+
'10': 'kimi',
|
|
10416
|
+
'11': 'kilo',
|
|
10417
|
+
'12': 'opencode',
|
|
10418
|
+
'13': 'qwen',
|
|
10419
|
+
'14': 'trae',
|
|
10420
|
+
'15': 'windsurf',
|
|
10421
|
+
'16': 'zcode'
|
|
10750
10422
|
};
|
|
10751
|
-
const allRuntimes = ['claude', 'antigravity', 'augment', 'cline', 'codebuddy', 'codex', 'copilot', 'cursor', '
|
|
10423
|
+
const allRuntimes = ['claude', 'antigravity', 'augment', 'cline', 'codebuddy', 'codex', 'copilot', 'cursor', 'hermes', 'kimi', 'kilo', 'opencode', 'qwen', 'trae', 'windsurf', 'zcode'];
|
|
10752
10424
|
const ALL_RUNTIMES_OPTION = '17';
|
|
10753
10425
|
|
|
10754
10426
|
/**
|
|
@@ -10765,14 +10437,14 @@ function buildRuntimePromptText() {
|
|
|
10765
10437
|
${cyan}6${reset}) Codex ${dim}(~/.codex)${reset}
|
|
10766
10438
|
${cyan}7${reset}) Copilot ${dim}(~/.copilot)${reset}
|
|
10767
10439
|
${cyan}8${reset}) Cursor ${dim}(~/.cursor)${reset}
|
|
10768
|
-
${cyan}9${reset})
|
|
10769
|
-
${cyan}10${reset})
|
|
10770
|
-
${cyan}11${reset})
|
|
10771
|
-
${cyan}12${reset})
|
|
10772
|
-
${cyan}13${reset})
|
|
10773
|
-
${cyan}14${reset})
|
|
10774
|
-
${cyan}15${reset})
|
|
10775
|
-
${cyan}16${reset})
|
|
10440
|
+
${cyan}9${reset}) Hermes Agent ${dim}(~/.hermes)${reset}
|
|
10441
|
+
${cyan}10${reset}) Kimi ${dim}(~/.config/agents, then ~/.agents if existing)${reset}
|
|
10442
|
+
${cyan}11${reset}) Kilo ${dim}(~/.config/kilo)${reset}
|
|
10443
|
+
${cyan}12${reset}) OpenCode ${dim}(~/.config/opencode)${reset}
|
|
10444
|
+
${cyan}13${reset}) Qwen Code ${dim}(~/.qwen)${reset}
|
|
10445
|
+
${cyan}14${reset}) Trae ${dim}(~/.trae)${reset}
|
|
10446
|
+
${cyan}15${reset}) Windsurf ${dim}(~/.codeium/windsurf)${reset}
|
|
10447
|
+
${cyan}16${reset}) ZCode ${dim}(~/.zcode)${reset}
|
|
10776
10448
|
${cyan}17${reset}) All
|
|
10777
10449
|
|
|
10778
10450
|
${dim}Select multiple: 1,2,6 or 1 2 6${reset}
|
|
@@ -10784,7 +10456,7 @@ function buildRuntimePromptText() {
|
|
|
10784
10456
|
* Pure function — exported so tests can verify split/dedupe/fallback behavior.
|
|
10785
10457
|
* - Accepts comma- and/or whitespace-separated choices
|
|
10786
10458
|
* - Deduplicates while preserving order
|
|
10787
|
-
* - Maps option
|
|
10459
|
+
* - Maps option 16 ("All") to every runtime
|
|
10788
10460
|
* - Falls back to ['claude'] when nothing valid is selected
|
|
10789
10461
|
*/
|
|
10790
10462
|
function parseRuntimeInput(answer) {
|
|
@@ -11424,7 +11096,7 @@ function installAllRuntimes(runtimes, isGlobal, isInteractive) {
|
|
|
11424
11096
|
throw error;
|
|
11425
11097
|
}
|
|
11426
11098
|
|
|
11427
|
-
const statuslineRuntimes = ['claude'
|
|
11099
|
+
const statuslineRuntimes = ['claude'];
|
|
11428
11100
|
const primaryStatuslineResult = results.find(r => statuslineRuntimes.includes(r.runtime));
|
|
11429
11101
|
|
|
11430
11102
|
const finalize = (shouldInstallStatusline, shouldInstallBanner) => {
|
|
@@ -11517,10 +11189,6 @@ module.exports = {
|
|
|
11517
11189
|
convertClaudeCommandToCursorSkill,
|
|
11518
11190
|
convertClaudeCommandToCursorCommand,
|
|
11519
11191
|
convertClaudeAgentToCursorAgent,
|
|
11520
|
-
convertClaudeToGeminiMarkdown,
|
|
11521
|
-
convertSlashCommandsToGeminiMentions,
|
|
11522
|
-
_resetGsdCommandRoster,
|
|
11523
|
-
convertClaudeToGeminiAgent,
|
|
11524
11192
|
convertClaudeAgentToCodexAgent,
|
|
11525
11193
|
generateCodexAgentToml,
|
|
11526
11194
|
cleanupCodexSkillMetadataSidecars,
|