@opengsd/gsd-core 1.14.0 → 1.16.0
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/.opencode/plugins/gsd-core.js +85 -5
- package/README.ja-JP.md +3 -3
- package/README.ko-KR.md +3 -3
- package/README.pt-BR.md +3 -3
- package/README.zh-CN.md +3 -3
- package/agents/gsd-code-fixer.compact.md +7 -6
- package/agents/gsd-code-fixer.md +9 -8
- package/agents/gsd-code-reviewer.compact.md +5 -3
- package/agents/gsd-code-reviewer.md +8 -6
- package/agents/gsd-debug-session-manager.compact.md +17 -2
- package/agents/gsd-debug-session-manager.md +17 -2
- package/agents/gsd-debugger.md +3 -3
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +17 -12
- package/agents/gsd-intel-updater.compact.md +1 -1
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +2 -2
- package/agents/gsd-phase-researcher.md +19 -11
- package/agents/gsd-plan-checker.md +15 -9
- package/agents/gsd-planner.md +15 -11
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +1 -1
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +21 -30
- package/agents/gsd-ui-auditor.md +166 -37
- package/agents/gsd-ui-researcher.compact.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +37 -14
- package/bin/install.js +764 -287
- package/commands/gsd/add-tests.md +6 -1
- package/commands/gsd/ai-integration-phase.md +6 -1
- package/commands/gsd/audit-fix.md +5 -0
- package/commands/gsd/audit-milestone.md +6 -1
- package/commands/gsd/autonomous.md +7 -2
- package/commands/gsd/capture.md +9 -5
- package/commands/gsd/code-review.md +7 -2
- package/commands/gsd/complete-milestone.md +4 -0
- package/commands/gsd/config.md +7 -3
- package/commands/gsd/debug.md +11 -7
- package/commands/gsd/discuss-phase.md +7 -3
- package/commands/gsd/docs-update.md +12 -7
- package/commands/gsd/eval-review.md +6 -1
- package/commands/gsd/execute-phase.md +12 -7
- package/commands/gsd/extract-learnings.md +5 -0
- package/commands/gsd/fast.md +4 -0
- package/commands/gsd/forensics.md +5 -1
- package/commands/gsd/graphify.md +10 -6
- package/commands/gsd/health.md +5 -0
- package/commands/gsd/help.md +7 -2
- package/commands/gsd/import.md +7 -3
- package/commands/gsd/inbox.md +5 -0
- package/commands/gsd/ingest-docs.md +5 -1
- package/commands/gsd/manager.md +6 -1
- package/commands/gsd/map-codebase.md +7 -3
- package/commands/gsd/mempalace-capture.md +12 -4
- package/commands/gsd/mempalace-recall.md +5 -1
- package/commands/gsd/milestone-summary.md +5 -1
- package/commands/gsd/mvp-phase.md +8 -3
- package/commands/gsd/new-milestone.md +6 -1
- package/commands/gsd/new-project.md +5 -0
- package/commands/gsd/next.md +6 -1
- package/commands/gsd/ns-context.md +4 -0
- package/commands/gsd/ns-ideate.md +4 -0
- package/commands/gsd/ns-manage.md +4 -0
- package/commands/gsd/ns-project.md +4 -0
- package/commands/gsd/ns-review.md +4 -0
- package/commands/gsd/ns-workflow.md +4 -0
- package/commands/gsd/onboard.md +6 -1
- package/commands/gsd/pause-work.md +5 -1
- package/commands/gsd/phase.md +8 -4
- package/commands/gsd/plan-phase.md +6 -1
- package/commands/gsd/plan-review-convergence.md +11 -7
- package/commands/gsd/pr-branch.md +4 -0
- package/commands/gsd/profile-user.md +5 -1
- package/commands/gsd/progress.md +7 -2
- package/commands/gsd/quick-batch.md +21 -9
- package/commands/gsd/quick.md +12 -7
- package/commands/gsd/review.md +7 -4
- package/commands/gsd/secure-phase.md +6 -1
- package/commands/gsd/ship.md +5 -0
- package/commands/gsd/sketch.md +7 -2
- package/commands/gsd/spec-phase.md +5 -1
- package/commands/gsd/spike.md +8 -3
- package/commands/gsd/surface.md +5 -1
- package/commands/gsd/thread.md +4 -0
- package/commands/gsd/ui-phase.md +6 -1
- package/commands/gsd/ui-review.md +6 -1
- package/commands/gsd/ultraplan-phase.md +5 -1
- package/commands/gsd/undo.md +5 -1
- package/commands/gsd/update.md +6 -2
- package/commands/gsd/validate-phase.md +6 -1
- package/commands/gsd/verify-work.md +6 -1
- package/commands/gsd/workspace.md +7 -3
- package/gsd-core/bin/gsd-tools.cjs +477 -78
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- package/gsd-core/bin/lib/adr-parser.cjs +3 -1
- package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
- package/gsd-core/bin/lib/audit.cjs +144 -42
- package/gsd-core/bin/lib/broken-windows.cjs +13 -13
- package/gsd-core/bin/lib/capability-activation.cjs +9 -4
- package/gsd-core/bin/lib/capability-registry.cjs +197 -222
- package/gsd-core/bin/lib/capability-validator.cjs +16 -1
- package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
- package/gsd-core/bin/lib/check-command-router.cjs +164 -1625
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
- package/gsd-core/bin/lib/cli-exit.cjs +12 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +32 -33
- package/gsd-core/bin/lib/command-aliases.cjs +7 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
- package/gsd-core/bin/lib/commands.cjs +343 -207
- package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
- package/gsd-core/bin/lib/config-loader.cjs +65 -4
- package/gsd-core/bin/lib/config.cjs +76 -19
- package/gsd-core/bin/lib/core-utils.cjs +6 -1
- package/gsd-core/bin/lib/coverage.cjs +4 -8
- package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
- package/gsd-core/bin/lib/decisions.cjs +30 -14
- package/gsd-core/bin/lib/drift.cjs +177 -42
- package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
- package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
- package/gsd-core/bin/lib/frontmatter.cjs +426 -234
- package/gsd-core/bin/lib/gap-checker.cjs +72 -29
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
- package/gsd-core/bin/lib/gate-args.cjs +53 -0
- package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
- package/gsd-core/bin/lib/gate-config.cjs +46 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
- package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
- package/gsd-core/bin/lib/gate-exit.cjs +27 -0
- package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
- package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
- package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
- package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
- package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
- package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
- package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
- package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
- package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
- package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
- package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
- package/gsd-core/bin/lib/graphify.cjs +10 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
- package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
- package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
- package/gsd-core/bin/lib/init.cjs +364 -132
- package/gsd-core/bin/lib/install-engine.cjs +30 -33
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- package/gsd-core/bin/lib/installer-migrations.cjs +8 -1
- package/gsd-core/bin/lib/io.cjs +122 -3
- package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
- package/gsd-core/bin/lib/milestone.cjs +37 -6
- package/gsd-core/bin/lib/model-resolver.cjs +171 -62
- package/gsd-core/bin/lib/observability/event.cjs +1 -1
- package/gsd-core/bin/lib/observability/logger.cjs +46 -1
- package/gsd-core/bin/lib/pattern.cjs +10 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +20 -5
- package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
- package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
- package/gsd-core/bin/lib/phase-id.cjs +110 -8
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
- package/gsd-core/bin/lib/phase-locator.cjs +29 -10
- package/gsd-core/bin/lib/phase-status.cjs +360 -0
- package/gsd-core/bin/lib/phase.cjs +489 -88
- package/gsd-core/bin/lib/plan-document.cjs +142 -20
- package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
- package/gsd-core/bin/lib/planning-document.cjs +692 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +60 -9
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +83 -55
- package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
- package/gsd-core/bin/lib/probe-core.cjs +7 -1
- package/gsd-core/bin/lib/profile-output.cjs +6 -3
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
- package/gsd-core/bin/lib/project-root.cjs +41 -2
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
- package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
- package/gsd-core/bin/lib/report-parser.cjs +269 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
- package/gsd-core/bin/lib/roadmap-command-router.cjs +25 -19
- package/gsd-core/bin/lib/roadmap-parser.cjs +242 -15
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +1653 -65
- package/gsd-core/bin/lib/roadmap.cjs +405 -88
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +373 -187
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +5 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
- package/gsd-core/bin/lib/runtime-homes.cjs +14 -7
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +522 -624
- package/gsd-core/bin/lib/runtime-name-policy.cjs +246 -21
- package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +47 -8
- package/gsd-core/bin/lib/smart-entry.cjs +19 -3
- package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
- package/gsd-core/bin/lib/state-contract.cjs +15 -18
- package/gsd-core/bin/lib/state-document.cjs +100 -22
- package/gsd-core/bin/lib/state-transition.cjs +39 -2
- package/gsd-core/bin/lib/state.cjs +256 -105
- package/gsd-core/bin/lib/surface.cjs +19 -2
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -79
- package/gsd-core/bin/lib/uat-predicate.cjs +359 -38
- package/gsd-core/bin/lib/uat.cjs +432 -7
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +167 -40
- package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
- package/gsd-core/bin/lib/vendor/README.md +31 -9
- package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
- package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +1378 -274
- package/gsd-core/bin/lib/verify-command-grounding.cjs +46 -2
- package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
- package/gsd-core/bin/lib/verify.cjs +357 -531
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
- package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
- package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
- package/gsd-core/bin/lib/workstream.cjs +11 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
- package/gsd-core/bin/lib/worktree-safety.cjs +784 -51
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
- package/gsd-core/references/autonomous-smart-discuss.md +2 -1
- package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
- package/gsd-core/references/checkpoints.md +5 -3
- package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe.md +195 -21
- package/gsd-core/references/execute-mvp-tdd.md +5 -10
- package/gsd-core/references/execute-phase-between-wave-reset.md +10 -6
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +22 -11
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +7 -1
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/offer-next.md +1 -1
- package/gsd-core/references/phase-argument-parsing.md +9 -7
- package/gsd-core/references/phase-id-convention.md +28 -0
- package/gsd-core/references/planner-gap-closure.md +2 -0
- package/gsd-core/references/planner-load-graph-context.md +24 -13
- package/gsd-core/references/planner-verify-command-grounding.md +14 -0
- package/gsd-core/references/planning-config.md +12 -3
- package/gsd-core/references/spidr-splitting.md +1 -1
- package/gsd-core/references/tdd.md +37 -8
- package/gsd-core/references/ui-consideration-probe.md +10 -5
- package/gsd-core/references/verifier-phase-gates.md +5 -2
- package/gsd-core/references/verify-command-path-resolvability.md +10 -2
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -3
- package/gsd-core/references/worktree-path-safety.md +321 -0
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/UAT.md +17 -1
- package/gsd-core/templates/config.json +2 -11
- package/gsd-core/templates/verification-report.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 +8 -7
- package/gsd-core/workflows/add-tests.md +4 -3
- package/gsd-core/workflows/add-todo.md +6 -5
- package/gsd-core/workflows/ai-integration-phase.md +13 -4
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +4 -3
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
- package/gsd-core/workflows/autonomous.md +43 -23
- package/gsd-core/workflows/check-todos.md +7 -6
- package/gsd-core/workflows/cleanup.md +2 -2
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
- package/gsd-core/workflows/code-review-fix.md +112 -25
- package/gsd-core/workflows/code-review.md +146 -135
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
- package/gsd-core/workflows/complete-milestone.md +13 -8
- package/gsd-core/workflows/debug.md +32 -7
- package/gsd-core/workflows/diagnose-issues.md +3 -2
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +4 -3
- package/gsd-core/workflows/do.md +2 -2
- package/gsd-core/workflows/docs-update.md +6 -5
- package/gsd-core/workflows/edit-phase.md +4 -3
- package/gsd-core/workflows/eval-review.md +14 -5
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1019 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +37 -3
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
- package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
- package/gsd-core/workflows/execute-phase.md +96 -178
- package/gsd-core/workflows/execute-plan.md +18 -23
- package/gsd-core/workflows/explore.md +4 -4
- package/gsd-core/workflows/extract-learnings.md +4 -2
- package/gsd-core/workflows/fast.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 +3 -2
- package/gsd-core/workflows/help/modes/full.compact.md +3 -3
- package/gsd-core/workflows/help/modes/full.md +5 -5
- package/gsd-core/workflows/help/modes/topic.md +15 -5
- package/gsd-core/workflows/import.md +4 -3
- package/gsd-core/workflows/inbox.md +2 -2
- package/gsd-core/workflows/ingest-docs.md +3 -3
- package/gsd-core/workflows/insert-phase.md +4 -3
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +6 -4
- package/gsd-core/workflows/map-codebase.md +5 -4
- package/gsd-core/workflows/milestone-summary.md +3 -2
- package/gsd-core/workflows/mvp-phase.md +14 -14
- package/gsd-core/workflows/new-milestone.md +11 -11
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
- package/gsd-core/workflows/new-project.md +7 -7
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/note.md +1 -1
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +2 -2
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
- package/gsd-core/workflows/plan-phase.md +50 -24
- package/gsd-core/workflows/plan-review-convergence.md +21 -5
- package/gsd-core/workflows/plant-seed.md +62 -20
- package/gsd-core/workflows/pr-branch.md +113 -13
- package/gsd-core/workflows/profile-user.md +2 -2
- package/gsd-core/workflows/progress.md +19 -49
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +27 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +15 -10
- package/gsd-core/workflows/quick.md +62 -39
- package/gsd-core/workflows/reapply-patches.md +9 -3
- package/gsd-core/workflows/remove-phase.md +3 -2
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +3 -2
- package/gsd-core/workflows/review.md +33 -17
- package/gsd-core/workflows/scan.md +3 -2
- package/gsd-core/workflows/secure-phase.md +13 -13
- package/gsd-core/workflows/settings-advanced.md +30 -10
- package/gsd-core/workflows/settings-integrations.md +2 -3
- package/gsd-core/workflows/settings.md +4 -4
- package/gsd-core/workflows/ship.md +14 -13
- 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 +2 -2
- package/gsd-core/workflows/spec-phase.md +15 -5
- 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/sync-skills.md +5 -5
- package/gsd-core/workflows/thread.md +2 -2
- package/gsd-core/workflows/transition.md +13 -23
- package/gsd-core/workflows/ui-phase.md +48 -11
- package/gsd-core/workflows/ui-review.md +21 -6
- package/gsd-core/workflows/ultraplan-phase.md +3 -2
- package/gsd-core/workflows/undo.md +339 -20
- package/gsd-core/workflows/update.md +7 -7
- package/gsd-core/workflows/validate-phase.md +12 -13
- package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
- package/gsd-core/workflows/verify-work.md +129 -55
- package/hooks/dist/gsd-agent-isolation-guard.js +32 -0
- package/hooks/dist/gsd-check-update-worker.js +8 -0
- package/hooks/dist/gsd-check-update.js +8 -0
- package/hooks/dist/gsd-context-monitor.js +31 -8
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
- package/hooks/dist/gsd-secret-read-guard.js +161 -4
- package/hooks/dist/gsd-statusline.js +85 -20
- package/hooks/dist/gsd-update-banner.js +8 -0
- package/hooks/dist/gsd-validate-commit.sh +63 -4
- package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
- package/hooks/dist/gsd-workflow-guard.js +5 -4
- package/hooks/dist/gsd-worktree-path-guard.js +6 -2
- package/hooks/dist/lib/cli-exit.js +12 -0
- package/hooks/dist/lib/git-probe.js +17 -1
- package/hooks/dist/lib/isolation-sentinel.js +2 -2
- package/hooks/gsd-agent-isolation-guard.js +32 -0
- package/hooks/gsd-check-update-worker.js +8 -0
- package/hooks/gsd-check-update.js +8 -0
- package/hooks/gsd-context-monitor.js +31 -8
- package/hooks/gsd-cursor-subagent-start.js +8 -0
- package/hooks/gsd-secret-read-guard.js +161 -4
- package/hooks/gsd-statusline.js +85 -20
- package/hooks/gsd-update-banner.js +8 -0
- package/hooks/gsd-validate-commit.sh +63 -4
- package/hooks/gsd-windsurf-pre-write.js +11 -2
- package/hooks/gsd-workflow-guard.js +5 -4
- package/hooks/gsd-worktree-path-guard.js +6 -2
- package/hooks/hooks.json +5 -5
- package/hooks/lib/cli-exit.js +12 -0
- package/hooks/lib/git-probe.js +17 -1
- package/hooks/lib/isolation-sentinel.js +2 -2
- package/package.json +22 -4
- package/scripts/build-hooks.js +15 -6
- package/scripts/changeset/parse.cjs +52 -4
- package/scripts/check-contract-drift.cjs +127 -11
- package/scripts/ci-timeout-report.cjs +770 -4
- package/scripts/command-contract-helpers.cjs +15 -8
- package/scripts/docs-guard-registry.cjs +34 -0
- package/scripts/gen-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +79 -1
- package/scripts/gen-platform-conformance-tier.cjs +187 -1
- package/scripts/gen-plugin-skills.cjs +87 -1
- package/scripts/gen-research-agents.cjs +24 -31
- package/scripts/gen-scripts-cli-exit.cjs +30 -3
- package/scripts/gen-test-timings.cjs +32 -7
- package/scripts/lib/cli-exit.cjs +12 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +34 -2
- package/scripts/lib/ndjson-reporter.cjs +31 -5
- package/scripts/lib/platform-conformance-tier.generated.cjs +45 -5
- package/scripts/lib/registration-ledger-preload.cjs +155 -0
- package/scripts/lib/vendor-bundle.cjs +59 -0
- package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-completion-predicate-drift.cjs +18 -19
- package/scripts/lint-descriptions.cjs +7 -3
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +38 -1
- package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
- package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
- package/scripts/lint-phase-arg-assignment.cjs +257 -0
- package/scripts/lint-phase-enumeration-drift.cjs +12 -8
- package/scripts/lint-phase-id-drift.cjs +319 -5
- package/scripts/lint-planning-document-positive-control.cjs +329 -0
- package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
- package/scripts/lint-response-language-coverage.cjs +3 -0
- package/scripts/lint-retired-runtime-name.cjs +619 -0
- package/scripts/lint-skill-deps.cjs +7 -3
- package/scripts/lint-state-write-path-drift.cjs +93 -0
- package/scripts/lint-test-file-count.allowlist.json +38 -9
- package/scripts/lint-test-file-count.cjs +34 -1
- package/scripts/lint-vendored-deps.cjs +41 -5
- package/scripts/lint-workflow-shellcheck-baseline.json +25 -10
- package/scripts/mutation-matrix.cjs +50 -5
- package/scripts/prompt-injection-scan.sh +4 -0
- package/scripts/release-tarball-smoke.cjs +194 -1
- package/scripts/require-issue-link-policy.cjs +6 -2
- package/scripts/sync-runtime-launcher.cjs +184 -2
- package/scripts/verify-npm-publish.cjs +76 -20
- package/skills/gsd-add-tests/SKILL.md +6 -1
- package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
- package/skills/gsd-audit-fix/SKILL.md +5 -0
- package/skills/gsd-audit-milestone/SKILL.md +6 -1
- package/skills/gsd-autonomous/SKILL.md +7 -2
- package/skills/gsd-capture/SKILL.md +9 -5
- package/skills/gsd-code-review/SKILL.md +7 -2
- package/skills/gsd-complete-milestone/SKILL.md +4 -0
- package/skills/gsd-config/SKILL.md +8 -4
- package/skills/gsd-debug/SKILL.md +11 -7
- package/skills/gsd-discuss-phase/SKILL.md +7 -3
- package/skills/gsd-docs-update/SKILL.md +12 -7
- package/skills/gsd-eval-review/SKILL.md +6 -1
- package/skills/gsd-execute-phase/SKILL.md +12 -7
- package/skills/gsd-extract-learnings/SKILL.md +5 -0
- package/skills/gsd-fast/SKILL.md +4 -0
- package/skills/gsd-forensics/SKILL.md +5 -1
- package/skills/gsd-graphify/SKILL.md +10 -6
- package/skills/gsd-health/SKILL.md +5 -0
- package/skills/gsd-help/SKILL.md +7 -2
- package/skills/gsd-import/SKILL.md +7 -3
- package/skills/gsd-inbox/SKILL.md +5 -0
- package/skills/gsd-ingest-docs/SKILL.md +5 -1
- package/skills/gsd-manager/SKILL.md +6 -1
- package/skills/gsd-map-codebase/SKILL.md +7 -3
- package/skills/gsd-mempalace-capture/SKILL.md +12 -4
- package/skills/gsd-mempalace-recall/SKILL.md +5 -1
- package/skills/gsd-milestone-summary/SKILL.md +5 -1
- package/skills/gsd-mvp-phase/SKILL.md +8 -3
- package/skills/gsd-new-milestone/SKILL.md +6 -1
- package/skills/gsd-new-project/SKILL.md +5 -0
- package/skills/gsd-next/SKILL.md +6 -1
- package/skills/gsd-ns-context/SKILL.md +4 -0
- package/skills/gsd-ns-ideate/SKILL.md +4 -0
- package/skills/gsd-ns-manage/SKILL.md +4 -0
- package/skills/gsd-ns-project/SKILL.md +4 -0
- package/skills/gsd-ns-review/SKILL.md +4 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -0
- package/skills/gsd-onboard/SKILL.md +6 -1
- package/skills/gsd-pause-work/SKILL.md +5 -1
- package/skills/gsd-phase/SKILL.md +8 -4
- package/skills/gsd-plan-phase/SKILL.md +6 -1
- package/skills/gsd-plan-review-convergence/SKILL.md +10 -6
- package/skills/gsd-pr-branch/SKILL.md +4 -0
- package/skills/gsd-profile-user/SKILL.md +5 -1
- package/skills/gsd-progress/SKILL.md +7 -2
- package/skills/gsd-quick/SKILL.md +16 -10
- package/skills/gsd-quick-batch/SKILL.md +21 -9
- package/skills/gsd-review/SKILL.md +7 -4
- package/skills/gsd-review-backlog/SKILL.md +3 -2
- package/skills/gsd-secure-phase/SKILL.md +6 -1
- package/skills/gsd-ship/SKILL.md +5 -0
- package/skills/gsd-sketch/SKILL.md +7 -2
- package/skills/gsd-spec-phase/SKILL.md +5 -1
- package/skills/gsd-spike/SKILL.md +8 -3
- package/skills/gsd-surface/SKILL.md +5 -1
- package/skills/gsd-thread/SKILL.md +4 -0
- package/skills/gsd-ui-phase/SKILL.md +6 -1
- package/skills/gsd-ui-review/SKILL.md +6 -1
- package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
- package/skills/gsd-undo/SKILL.md +5 -1
- package/skills/gsd-update/SKILL.md +6 -2
- package/skills/gsd-validate-phase/SKILL.md +6 -1
- package/skills/gsd-verify-work/SKILL.md +6 -1
- package/skills/gsd-workspace/SKILL.md +7 -3
- package/skills/gsd-workstreams/SKILL.md +6 -6
- package/vscode/package.json +1 -1
|
@@ -33,11 +33,12 @@ const { installFs, mkInstallTempDir } = installFsAdapter;
|
|
|
33
33
|
const commandRoster = require("./command-roster.cjs");
|
|
34
34
|
const { readGsdCommandNames, transformContentToHyphen } = commandRoster;
|
|
35
35
|
const runtimeNamePolicy = require("./runtime-name-policy.cjs");
|
|
36
|
-
const { getDirName } = runtimeNamePolicy;
|
|
36
|
+
const { getDirName, hostBehaviorsFor } = runtimeNamePolicy;
|
|
37
37
|
const capabilityRegistry = require("./capability-registry.cjs");
|
|
38
38
|
const hostIntegration = require("./host-integration.cjs");
|
|
39
39
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
40
40
|
const frontmatterModule = require("./frontmatter.cjs");
|
|
41
|
+
const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
|
|
41
42
|
const pattern_cjs_1 = require("./pattern.cjs");
|
|
42
43
|
const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
|
|
43
44
|
// #2870: install-scope.cts is a leaf-tier sibling (imports only
|
|
@@ -93,24 +94,6 @@ function gsdVersion() {
|
|
|
93
94
|
cachedVersion = resolveVersionFrom(__dirname);
|
|
94
95
|
return cachedVersion;
|
|
95
96
|
}
|
|
96
|
-
/**
|
|
97
|
-
* Host-specific install behaviors declared on the runtime descriptor
|
|
98
|
-
* (capabilities/<runtime>/capability.json -> runtime.hostBehaviors). Mirrors
|
|
99
|
-
* bin/install.js's / install-engine.cts's `_hostBehaviors` (ADR-1239 / #2086
|
|
100
|
-
* / #2092). Returns {} for runtimes that declare none, so every behavior
|
|
101
|
-
* branch degrades to the generic path by default. Unlike the bin/install.js
|
|
102
|
-
* and install-engine.cts variants, this module already imports
|
|
103
|
-
* `capabilityRegistry` statically (see NON_CLAUDE_RUNTIMES below), so this
|
|
104
|
-
* reads it directly rather than re-require()-ing inside a try/catch.
|
|
105
|
-
*/
|
|
106
|
-
function _hostBehaviors(runtime) {
|
|
107
|
-
return ((capabilityRegistry &&
|
|
108
|
-
capabilityRegistry.runtimes &&
|
|
109
|
-
capabilityRegistry.runtimes[runtime] &&
|
|
110
|
-
capabilityRegistry.runtimes[runtime].runtime &&
|
|
111
|
-
capabilityRegistry.runtimes[runtime].runtime.hostBehaviors) ||
|
|
112
|
-
{});
|
|
113
|
-
}
|
|
114
97
|
/**
|
|
115
98
|
* Public accessor for the `hostBehaviors.agentFileExtension` descriptor field
|
|
116
99
|
* (ADR-1239 / #2099 / #2103). Returns the runtime's declared agent-file
|
|
@@ -122,7 +105,7 @@ function _hostBehaviors(runtime) {
|
|
|
122
105
|
* duplicating a hardcoded `runtime === 'copilot'` check (#2103 fold).
|
|
123
106
|
*/
|
|
124
107
|
function agentFileExtensionFor(runtime) {
|
|
125
|
-
const ext =
|
|
108
|
+
const ext = hostBehaviorsFor(runtime).agentFileExtension;
|
|
126
109
|
return typeof ext === 'string' ? ext : undefined;
|
|
127
110
|
}
|
|
128
111
|
const colorNameToHex = {
|
|
@@ -457,7 +440,10 @@ function convertClaudeCommandToClaudeSkill(content, skillName, runtime = null, c
|
|
|
457
440
|
// calls fall back to reading the list.
|
|
458
441
|
const names = cmdNames || readGsdCommandNames();
|
|
459
442
|
const normalizedBody = transformContentToHyphen(body, names);
|
|
460
|
-
|
|
443
|
+
// #4324: the description is the text the host's skill picker renders, so it
|
|
444
|
+
// needs the same hyphen normalisation the body gets — otherwise a `/gsd:<cmd>`
|
|
445
|
+
// mention in a command description ships the retired colon form to the user.
|
|
446
|
+
const description = transformContentToHyphen(extractFrontmatterField(frontmatter, 'description') || '', names);
|
|
461
447
|
const argumentHint = extractFrontmatterField(frontmatter, 'argument-hint');
|
|
462
448
|
const agent = extractFrontmatterField(frontmatter, 'agent');
|
|
463
449
|
// #769: preserve context: from source command files so it is emitted into
|
|
@@ -482,7 +468,7 @@ function convertClaudeCommandToClaudeSkill(content, skillName, runtime = null, c
|
|
|
482
468
|
// Hermes' SKILL.md spec lists `version` as a required frontmatter field.
|
|
483
469
|
// Track GSD's package version so Hermes' skill_view() reports a stable
|
|
484
470
|
// identifier per install.
|
|
485
|
-
if (runtime
|
|
471
|
+
if (hostBehaviorsFor(runtime).skillFrontmatterVersion) {
|
|
486
472
|
const version = gsdVersion();
|
|
487
473
|
if (version)
|
|
488
474
|
fm += `version: ${yamlQuote(version)}\n`;
|
|
@@ -492,7 +478,7 @@ function convertClaudeCommandToClaudeSkill(content, skillName, runtime = null, c
|
|
|
492
478
|
// runtimes that declare the flag so Claude/Hermes skill frontmatter is
|
|
493
479
|
// unchanged (they ignore the field, but we keep their output byte-stable).
|
|
494
480
|
// skillName is the `gsd-<stem>` dir name. (ADR-1239 / #2092)
|
|
495
|
-
if (
|
|
481
|
+
if (hostBehaviorsFor(runtime).skillPriorityFrontmatter) {
|
|
496
482
|
const stem = typeof skillName === 'string' && skillName.startsWith('gsd-')
|
|
497
483
|
? skillName.slice(4)
|
|
498
484
|
: skillName;
|
|
@@ -727,10 +713,13 @@ function appendAgentTools(content, grants) {
|
|
|
727
713
|
return content;
|
|
728
714
|
const eol = content.includes('\r\n') ? '\r\n' : '\n';
|
|
729
715
|
const lines = content.split(eol);
|
|
730
|
-
|
|
716
|
+
// The block is the one the one fence owner finds; `frontmatterEnd` is its closing fence's
|
|
717
|
+
// line index in `lines`.
|
|
718
|
+
const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
|
|
719
|
+
if (!fence?.closed)
|
|
731
720
|
return content;
|
|
732
|
-
const frontmatterEnd =
|
|
733
|
-
if (frontmatterEnd
|
|
721
|
+
const frontmatterEnd = content.slice(0, fence.closingStart).split(eol).length - 1;
|
|
722
|
+
if (frontmatterEnd < 1)
|
|
734
723
|
return content;
|
|
735
724
|
const toolsIndex = lines.findIndex((line, index) => index < frontmatterEnd && /^tools:[ \t]*(.*)$/.test(line));
|
|
736
725
|
if (toolsIndex === -1)
|
|
@@ -1077,17 +1066,19 @@ function yamlIdentifier(value) {
|
|
|
1077
1066
|
}
|
|
1078
1067
|
return yamlQuote(text);
|
|
1079
1068
|
}
|
|
1069
|
+
/**
|
|
1070
|
+
* The frontmatter YAML (trimmed) and everything after the closing fence line's text, for the
|
|
1071
|
+
* block the one fence owner (`locateFrontmatterFence`) finds — so a `---` inside a value (a
|
|
1072
|
+
* description that mentions `a---b`) cannot end the block early.
|
|
1073
|
+
*/
|
|
1080
1074
|
function extractFrontmatterAndBody(content) {
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
}
|
|
1084
|
-
const endIndex = content.indexOf('---', 3);
|
|
1085
|
-
if (endIndex === -1) {
|
|
1075
|
+
const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
|
|
1076
|
+
if (!fence?.closed) {
|
|
1086
1077
|
return { frontmatter: null, body: content };
|
|
1087
1078
|
}
|
|
1088
1079
|
return {
|
|
1089
|
-
frontmatter: content.
|
|
1090
|
-
body: content.
|
|
1080
|
+
frontmatter: content.slice(fence.openEnd, fence.bodyEnd).trim(),
|
|
1081
|
+
body: content.slice(fence.closingFenceEnd),
|
|
1091
1082
|
};
|
|
1092
1083
|
}
|
|
1093
1084
|
function extractFrontmatterField(frontmatter, fieldName) {
|
|
@@ -1536,7 +1527,7 @@ function convertClaudeCommandToTraeSkill(content, skillName) {
|
|
|
1536
1527
|
// is not formally documented (thin SPA docs) — descriptor-driven, single
|
|
1537
1528
|
// fixed GSD-side value (runtime.hostBehaviors.soloStageMetadata), inferred/
|
|
1538
1529
|
// best-effort.
|
|
1539
|
-
const soloStage =
|
|
1530
|
+
const soloStage = hostBehaviorsFor('trae').soloStageMetadata;
|
|
1540
1531
|
if (soloStage)
|
|
1541
1532
|
fm += `stage: ${soloStage}\n`;
|
|
1542
1533
|
fm += '---';
|
|
@@ -1670,6 +1661,8 @@ function convertClaudeCommandToClineSkill(content, skillName, _runtime = null, c
|
|
|
1670
1661
|
if (!description)
|
|
1671
1662
|
description = `Run GSD workflow ${skillName}.`;
|
|
1672
1663
|
description = toSingleLine(description);
|
|
1664
|
+
// #4324: same reason as the Claude skill converter above.
|
|
1665
|
+
description = transformContentToHyphen(description, names);
|
|
1673
1666
|
// Cline documented max is 1024 code points (not UTF-16 code units).
|
|
1674
1667
|
// Use Array.from to iterate by code point so that multibyte characters
|
|
1675
1668
|
// (e.g. emoji, astral-plane chars) are never split, which would produce
|
|
@@ -1818,6 +1811,22 @@ Typed mapping (agent_type-capable schema only):
|
|
|
1818
1811
|
never fabricate a manual worktree protocol — route through the negotiated
|
|
1819
1812
|
isolation adapter, which still fails closed for hosts declaring \`none\` (#3360).
|
|
1820
1813
|
|
|
1814
|
+
Foreground handoffs:
|
|
1815
|
+
- spawn_agent is asynchronous. When the source Agent(...) or Task(...) declares
|
|
1816
|
+
run_in_background=false, call collaboration.wait_agent(timeout_ms=...) immediately after
|
|
1817
|
+
spawn and keep the parent turn active until that child returns a terminal result.
|
|
1818
|
+
- collaboration.wait_agent is a mailbox wakeup, NOT a completion oracle: "Wait completed"
|
|
1819
|
+
can mean only that a child sent an interim MESSAGE or status update. After every wakeup,
|
|
1820
|
+
inspect the named child's update/status. Only a FINAL_ANSWER or a terminal agent status
|
|
1821
|
+
(completed, failed, or cancelled) ends the foreground handoff.
|
|
1822
|
+
- On an interim MESSAGE or any non-terminal status, do not report an outcome, send a
|
|
1823
|
+
continuation, start parent work, or end the parent turn. Call collaboration.wait_agent
|
|
1824
|
+
again for the same child. If a terminal response is absent after an abnormal end, reconcile
|
|
1825
|
+
the workflow's durable artifacts before classifying the child.
|
|
1826
|
+
- This applies to one foreground child as well as fan-out. The child retains its workflow's
|
|
1827
|
+
own checkpoint loop; do not report an outcome or start any further parent work before its
|
|
1828
|
+
terminal result is available.
|
|
1829
|
+
|
|
1821
1830
|
Generic-agent workaround (multi_agent_v1 schema — NO agent_type field):
|
|
1822
1831
|
When only the generic \`multi_agent_v1\` schema is available, typed GSD agent dispatch
|
|
1823
1832
|
(\`gsd-planner\`, \`gsd-executor\`, etc.) is NOT possible. This is a known Codex limitation
|
|
@@ -1845,6 +1854,9 @@ Spawn restriction:
|
|
|
1845
1854
|
defaulting to inline execution.
|
|
1846
1855
|
|
|
1847
1856
|
Parallel fan-out:
|
|
1857
|
+
- For each child, loop on collaboration.wait_agent(timeout_ms=...) until its own terminal
|
|
1858
|
+
result is observed. A mailbox update from one child never completes another child, and an
|
|
1859
|
+
interim MESSAGE never completes its sender.
|
|
1848
1860
|
- Spawn multiple agents → collect agent IDs → \`collaboration.wait_agent(timeout_ms=...)\` for each to complete
|
|
1849
1861
|
- Do NOT use \`functions.wait(cell_id=...)\` — that is an unrelated exec-cell tool, not the collaboration wait
|
|
1850
1862
|
|
|
@@ -1931,17 +1943,11 @@ function convertClaudeToOpencodeFrontmatter(content, { isAgent = false, modelOve
|
|
|
1931
1943
|
convertedContent = convertedContent.replace(/subagent_type="general-purpose"/g, 'subagent_type="general"');
|
|
1932
1944
|
// Runtime-neutral agent name replacement (#766)
|
|
1933
1945
|
convertedContent = neutralizeAgentReferences(convertedContent, 'AGENTS.md');
|
|
1934
|
-
//
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
}
|
|
1938
|
-
// Find the end of frontmatter
|
|
1939
|
-
const endIndex = convertedContent.indexOf('---', 3);
|
|
1940
|
-
if (endIndex === -1) {
|
|
1946
|
+
// The frontmatter block, as the one fence owner finds it (none → nothing to convert).
|
|
1947
|
+
const { frontmatter, body } = extractFrontmatterAndBody(convertedContent);
|
|
1948
|
+
if (frontmatter === null) {
|
|
1941
1949
|
return convertedContent;
|
|
1942
1950
|
}
|
|
1943
|
-
const frontmatter = convertedContent.substring(3, endIndex).trim();
|
|
1944
|
-
const body = convertedContent.substring(endIndex + 3);
|
|
1945
1951
|
// Parse frontmatter line by line (simple YAML parsing)
|
|
1946
1952
|
const lines = frontmatter.split('\n');
|
|
1947
1953
|
const newLines = [];
|
|
@@ -2097,17 +2103,11 @@ function convertClaudeToKiloFrontmatter(content, { isAgent = false, modelOverrid
|
|
|
2097
2103
|
convertedContent = convertedContent.replace(/subagent_type="general-purpose"/g, 'subagent_type="general"');
|
|
2098
2104
|
// Runtime-neutral agent name replacement (#766)
|
|
2099
2105
|
convertedContent = neutralizeAgentReferences(convertedContent, 'AGENTS.md');
|
|
2100
|
-
//
|
|
2101
|
-
|
|
2106
|
+
// The frontmatter block, as the one fence owner finds it (none → nothing to convert).
|
|
2107
|
+
const { frontmatter, body } = extractFrontmatterAndBody(convertedContent);
|
|
2108
|
+
if (frontmatter === null) {
|
|
2102
2109
|
return convertedContent;
|
|
2103
2110
|
}
|
|
2104
|
-
// Find the end of frontmatter
|
|
2105
|
-
const endIndex = convertedContent.indexOf('---', 3);
|
|
2106
|
-
if (endIndex === -1) {
|
|
2107
|
-
return convertedContent;
|
|
2108
|
-
}
|
|
2109
|
-
const frontmatter = convertedContent.substring(3, endIndex).trim();
|
|
2110
|
-
const body = convertedContent.substring(endIndex + 3);
|
|
2111
2111
|
// Parse frontmatter line by line (simple YAML parsing)
|
|
2112
2112
|
const lines = frontmatter.split('\n');
|
|
2113
2113
|
const newLines = [];
|
|
@@ -2273,38 +2273,45 @@ const claudeToCopilotTools = {
|
|
|
2273
2273
|
AskUserQuestion: 'ask_user',
|
|
2274
2274
|
SlashCommand: 'skill',
|
|
2275
2275
|
};
|
|
2276
|
-
// Tool name mapping from Claude Code to
|
|
2277
|
-
//
|
|
2278
|
-
|
|
2279
|
-
|
|
2276
|
+
// Tool name mapping from Claude Code to Antigravity
|
|
2277
|
+
// Antigravity uses Gemini's snake_case built-in tool names
|
|
2278
|
+
// #4705: values are Antigravity-NATIVE tool names per the documented subagent
|
|
2279
|
+
// contract (antigravity.google/docs/subagents: view_file, replace_file_content,
|
|
2280
|
+
// grep_search, run_command are the documented examples; the catalog is
|
|
2281
|
+
// non-exhaustive, so unmapped entries keep their best-known grant rather than
|
|
2282
|
+
// being dropped — dropping would silently remove a restriction). The old
|
|
2283
|
+
// values were Gemini CLI dialect names, which Antigravity's tool validation
|
|
2284
|
+
// does not document and may hang the subagent on.
|
|
2285
|
+
const claudeToAntigravityTools = {
|
|
2286
|
+
Read: 'view_file',
|
|
2280
2287
|
Write: 'write_file',
|
|
2281
|
-
Edit: '
|
|
2282
|
-
Bash: '
|
|
2288
|
+
Edit: 'replace_file_content',
|
|
2289
|
+
Bash: 'run_command',
|
|
2283
2290
|
Glob: 'glob',
|
|
2284
|
-
Grep: '
|
|
2291
|
+
Grep: 'grep_search',
|
|
2285
2292
|
WebSearch: 'google_web_search',
|
|
2286
2293
|
WebFetch: 'web_fetch',
|
|
2287
2294
|
TodoWrite: 'write_todos',
|
|
2288
2295
|
};
|
|
2289
2296
|
/**
|
|
2290
|
-
* Convert a Claude Code tool name to
|
|
2291
|
-
* - Applies Claude→
|
|
2292
|
-
* - Filters out MCP tools (mcp__*) — they are auto-discovered at runtime in
|
|
2293
|
-
* - Filters out Task/Agent — agents are auto-registered as tools in
|
|
2294
|
-
* @returns {string|null}
|
|
2297
|
+
* Convert a Claude Code tool name to Antigravity format
|
|
2298
|
+
* - Applies Claude→Antigravity mapping (Read→read_file, Bash→run_shell_command, etc.)
|
|
2299
|
+
* - Filters out MCP tools (mcp__*) — they are auto-discovered at runtime in Antigravity
|
|
2300
|
+
* - Filters out Task/Agent — agents are auto-registered as tools in Antigravity
|
|
2301
|
+
* @returns {string|null} Antigravity tool name, or null if tool should be excluded
|
|
2295
2302
|
*/
|
|
2296
|
-
function
|
|
2303
|
+
function convertAntigravityToolName(claudeTool) {
|
|
2297
2304
|
// MCP tools: exclude — auto-discovered from mcpServers config at runtime
|
|
2298
2305
|
if (claudeTool.startsWith('mcp__')) {
|
|
2299
2306
|
return null;
|
|
2300
2307
|
}
|
|
2301
2308
|
// Task/Agent: exclude — agents are auto-registered as callable tools.
|
|
2302
|
-
// AskUserQuestion: exclude — Gemini
|
|
2303
|
-
// emitting it causes frontmatter validation errors (#3362).
|
|
2304
|
-
// Skill/SlashCommand: exclude — Gemini
|
|
2305
|
-
// the lowercase fallback would emit an invalid
|
|
2306
|
-
// that fails frontmatter validation
|
|
2307
|
-
// the entire agent load (#1394).
|
|
2309
|
+
// AskUserQuestion: exclude — Antigravity (Gemini tool dialect) does not expose
|
|
2310
|
+
// an ask_user tool; emitting it causes frontmatter validation errors (#3362).
|
|
2311
|
+
// Skill/SlashCommand: exclude — Antigravity (Gemini tool dialect) has no 'skill'
|
|
2312
|
+
// built-in tool; the lowercase fallback would emit an invalid
|
|
2313
|
+
// 'skill'/'slashcommand' name that fails frontmatter validation
|
|
2314
|
+
// (tools.N: Invalid tool name) and aborts the entire agent load (#1394).
|
|
2308
2315
|
if (claudeTool === 'Task' ||
|
|
2309
2316
|
claudeTool === 'Agent' ||
|
|
2310
2317
|
claudeTool === 'AskUserQuestion' ||
|
|
@@ -2314,8 +2321,8 @@ function convertGeminiToolName(claudeTool) {
|
|
|
2314
2321
|
return null;
|
|
2315
2322
|
}
|
|
2316
2323
|
// Check for explicit mapping
|
|
2317
|
-
if (
|
|
2318
|
-
return
|
|
2324
|
+
if (claudeToAntigravityTools[claudeTool]) {
|
|
2325
|
+
return claudeToAntigravityTools[claudeTool];
|
|
2319
2326
|
}
|
|
2320
2327
|
// Default: lowercase
|
|
2321
2328
|
return claudeTool.toLowerCase();
|
|
@@ -2371,7 +2378,7 @@ function convertClaudeAgentToCopilotAgent(content, isGlobal = false) {
|
|
|
2371
2378
|
}
|
|
2372
2379
|
/**
|
|
2373
2380
|
* Convert a Claude agent (.md) to an Antigravity agent.
|
|
2374
|
-
* Uses Gemini tool
|
|
2381
|
+
* Uses Antigravity's Gemini tool dialect since Antigravity runs on Gemini 3 backend.
|
|
2375
2382
|
*/
|
|
2376
2383
|
function convertClaudeAgentToAntigravityAgent(content, isGlobal = false) {
|
|
2377
2384
|
const converted = convertClaudeToAntigravityContent(content, isGlobal);
|
|
@@ -2382,11 +2389,17 @@ function convertClaudeAgentToAntigravityAgent(content, isGlobal = false) {
|
|
|
2382
2389
|
const description = extractFrontmatterField(frontmatter, 'description') || '';
|
|
2383
2390
|
const color = extractFrontmatterField(frontmatter, 'color');
|
|
2384
2391
|
const toolsRaw = extractFrontmatterField(frontmatter, 'tools') || '';
|
|
2385
|
-
// Map tools to
|
|
2392
|
+
// Map tools to Antigravity equivalents (reuse existing convertAntigravityToolName)
|
|
2386
2393
|
const claudeTools = toolsRaw.split(',').map(t => t.trim()).filter(Boolean);
|
|
2387
|
-
const mappedTools = claudeTools.map(t =>
|
|
2394
|
+
const mappedTools = claudeTools.map(t => convertAntigravityToolName(t)).filter(Boolean);
|
|
2388
2395
|
// #2876: quote description for the same reason as the skill variant.
|
|
2389
|
-
|
|
2396
|
+
// #4705: tools is a YAML SEQUENCE of native names (one `- name` item per
|
|
2397
|
+
// line), not a comma-separated scalar. Empty mapped set -> `tools: []` so an
|
|
2398
|
+
// agent whose every tool was filtered stays explicitly restricted.
|
|
2399
|
+
const toolsBlock = mappedTools.length > 0
|
|
2400
|
+
? `tools:\n${mappedTools.map((t) => `- ${t}`).join('\n')}\n`
|
|
2401
|
+
: 'tools: []\n';
|
|
2402
|
+
let fm = `---\nname: ${name}\ndescription: ${yamlQuote(description)}\n${toolsBlock}`;
|
|
2390
2403
|
if (color)
|
|
2391
2404
|
fm += `color: ${color}\n`;
|
|
2392
2405
|
fm += '---';
|
|
@@ -2489,7 +2502,7 @@ function convertClaudeAgentToTraeAgent(content) {
|
|
|
2489
2502
|
* forms to avoid double-rewriting the same substring.
|
|
2490
2503
|
*/
|
|
2491
2504
|
function convertClaudeAgentToQwenAgent(content) {
|
|
2492
|
-
const _b =
|
|
2505
|
+
const _b = hostBehaviorsFor('qwen').brandingRewrites || {};
|
|
2493
2506
|
let converted = content;
|
|
2494
2507
|
if (_b['CLAUDE.md'])
|
|
2495
2508
|
converted = converted.replace(/CLAUDE\.md/g, _b['CLAUDE.md']);
|
|
@@ -2554,18 +2567,14 @@ function convertClaudeAgentToZcodeAgent(content) {
|
|
|
2554
2567
|
// present. The unchanged scan below still preserves byte-identical content.
|
|
2555
2568
|
if (!content.includes('mcp__') && !content.includes('\\'))
|
|
2556
2569
|
return content;
|
|
2557
|
-
|
|
2558
|
-
|
|
2570
|
+
// The block is the one the one fence owner finds; unterminated (or absent) → leave verbatim.
|
|
2571
|
+
// The rewrite below re-joins lines on '\n', so a CRLF block is left verbatim too rather than
|
|
2572
|
+
// given mixed line endings.
|
|
2573
|
+
const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
|
|
2574
|
+
if (!fence?.closed || fence.eol !== '\n')
|
|
2559
2575
|
return content;
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
if (lines[i] === '---') {
|
|
2563
|
-
fmEnd = i;
|
|
2564
|
-
break;
|
|
2565
|
-
}
|
|
2566
|
-
}
|
|
2567
|
-
if (fmEnd === -1)
|
|
2568
|
-
return content; // unterminated frontmatter — leave verbatim
|
|
2576
|
+
const lines = content.split('\n');
|
|
2577
|
+
const fmEnd = content.slice(0, fence.closingStart).split('\n').length - 1;
|
|
2569
2578
|
const out = [];
|
|
2570
2579
|
let changed = false;
|
|
2571
2580
|
let i = 1;
|
|
@@ -2662,12 +2671,12 @@ function convertClaudeAgentToClineAgent(content) {
|
|
|
2662
2671
|
* rewrites applied automatically. A runtime with no `brandingRewrites`
|
|
2663
2672
|
* declared returns `content` unchanged (no rewrite table to apply).
|
|
2664
2673
|
*
|
|
2665
|
-
* Byte-identical to the inline loop's `else if (
|
|
2674
|
+
* Byte-identical to the inline loop's `else if (hostBehaviorsFor(runtime).brandingRewrites)`
|
|
2666
2675
|
* branch, including plain (non-word-boundary) `.replace(/\bClaude Code\b/g, ...)`
|
|
2667
2676
|
* semantics — J9.
|
|
2668
2677
|
*/
|
|
2669
2678
|
function applyAgentBrandingRewrites(content, runtime) {
|
|
2670
|
-
const _b =
|
|
2679
|
+
const _b = hostBehaviorsFor(runtime).brandingRewrites;
|
|
2671
2680
|
if (!_b)
|
|
2672
2681
|
return content;
|
|
2673
2682
|
let converted = content;
|
|
@@ -2769,15 +2778,53 @@ function convertClaudeCommandToKiloSkill(content, skillName) {
|
|
|
2769
2778
|
// Relocated from bin/install.js (#1511). Behavior is byte-for-behavior identical
|
|
2770
2779
|
// to the originals; the only change is the injected `attribution` 5th param in
|
|
2771
2780
|
// _applyRuntimeRewrites (replacing the internal getCommitAttribution() call).
|
|
2781
|
+
/**
|
|
2782
|
+
* #4377: is the project-relative include style opted in?
|
|
2783
|
+
*
|
|
2784
|
+
* Dual-sourced exactly like `--portable-hooks`/`GSD_PORTABLE_HOOKS`:
|
|
2785
|
+
* `bin/install.js` sets the variable when the flag is passed, so the flag and
|
|
2786
|
+
* the environment cannot disagree, and every seam that computes a path prefix
|
|
2787
|
+
* (the installer copy path, the install engine, the two rewrite entry points,
|
|
2788
|
+
* the install plan, and `applySurface`) reads the same answer without six signatures having to
|
|
2789
|
+
* grow a parameter each and stay in sync.
|
|
2790
|
+
*
|
|
2791
|
+
* Opt-in, not the new default. Making relative the default would change every
|
|
2792
|
+
* existing single-checkout local install — which works today — to fix a
|
|
2793
|
+
* multi-worktree case those users do not have.
|
|
2794
|
+
*
|
|
2795
|
+
* @param env - Environment to read (injectable for tests).
|
|
2796
|
+
* @returns Whether local installs should emit project-relative includes.
|
|
2797
|
+
*/
|
|
2798
|
+
function relativeIncludesEnabled(env = process.env) {
|
|
2799
|
+
return env.GSD_RELATIVE_INCLUDES === '1';
|
|
2800
|
+
}
|
|
2772
2801
|
/**
|
|
2773
2802
|
* Compute the path prefix for a runtime install.
|
|
2774
2803
|
* Global installs under $HOME use $HOME/... form; others use the resolved target.
|
|
2775
2804
|
* isOpencode excludes OpenCode (uses ~/.config/opencode which breaks $HOME shorthand).
|
|
2776
2805
|
* isWindowsHost is not used today but reserved for future Windows-specific logic.
|
|
2777
2806
|
*
|
|
2807
|
+
* #4377: a LOCAL install can instead emit a project-relative prefix, so a repo
|
|
2808
|
+
* worked from several git worktrees does not get every worktree's `@` includes
|
|
2809
|
+
* baked to whichever checkout happened to run the installer. Absolute is still
|
|
2810
|
+
* the default; `projectRelative` opts in.
|
|
2811
|
+
*
|
|
2812
|
+
* `localDirName` is the runtime's own `localConfigDir` descriptor value
|
|
2813
|
+
* (via `getDirName`), never a hardcoded literal — the same value the rewrite
|
|
2814
|
+
* engine already uses for its `./.claude/` -> `./<dir>/` substitutions, and
|
|
2815
|
+
* exactly what `resolveScope` joins onto the cwd to produce `resolvedTarget`
|
|
2816
|
+
* for a local install. Copilot and Antigravity have shipped this shape for
|
|
2817
|
+
* local installs since they were added, with hardcoded `.github/` and
|
|
2818
|
+
* `.agents/`; this is the same behavior, derived instead of written down.
|
|
2819
|
+
*
|
|
2820
|
+
* Fails safe to the absolute prefix: no opt-in, a global install, a missing
|
|
2821
|
+
* dir name, or the `configHome.kind === 'none'` sentinel all fall through. A
|
|
2822
|
+
* wrong-but-absolute include still resolves to a real file; a wrong relative
|
|
2823
|
+
* one silently resolves against whatever the reader's cwd happens to be.
|
|
2824
|
+
*
|
|
2778
2825
|
* @private — exported as `_computePathPrefix` for tests.
|
|
2779
2826
|
*/
|
|
2780
|
-
function computePathPrefix({ isGlobal, isOpencode, isWindowsHost: _isWindowsHost, resolvedTarget, homeDir }) {
|
|
2827
|
+
function computePathPrefix({ isGlobal, isOpencode, isWindowsHost: _isWindowsHost, resolvedTarget, homeDir, projectRelative = relativeIncludesEnabled(), localDirName, projectRoot = process.cwd(), projectRelativePath, }) {
|
|
2781
2828
|
// #1615: normalize Windows backslashes to forward slashes. This prefix is
|
|
2782
2829
|
// substituted into markdown @-references (e.g. Windsurf workflow files),
|
|
2783
2830
|
// which use POSIX paths universally. Idempotent on POSIX (no backslashes).
|
|
@@ -2789,8 +2836,62 @@ function computePathPrefix({ isGlobal, isOpencode, isWindowsHost: _isWindowsHost
|
|
|
2789
2836
|
if (isGlobal && posixTarget.startsWith(posixHome) && !isOpencode) {
|
|
2790
2837
|
return '$HOME' + posixTarget.slice(posixHome.length) + '/';
|
|
2791
2838
|
}
|
|
2839
|
+
if (!isGlobal && projectRelative) {
|
|
2840
|
+
// An explicit local config dir need not be the descriptor's conventional
|
|
2841
|
+
// `.claude`-style directory. Derive from the resolved target first; only
|
|
2842
|
+
// use the descriptor as the legacy fallback when no project root exists.
|
|
2843
|
+
const relative = projectRelativePrefix(projectRelativePath)
|
|
2844
|
+
|| projectRelativePrefixFromProjectRoot(projectRoot, resolvedTarget)
|
|
2845
|
+
|| projectRelativePrefix(localDirName);
|
|
2846
|
+
if (relative)
|
|
2847
|
+
return relative;
|
|
2848
|
+
}
|
|
2792
2849
|
return `${posixTarget}/`;
|
|
2793
2850
|
}
|
|
2851
|
+
/** Return a safe project-relative prefix for a resolved install target. */
|
|
2852
|
+
function projectRelativePrefixFromProjectRoot(projectRoot, resolvedTarget) {
|
|
2853
|
+
if (typeof projectRoot !== 'string' || typeof resolvedTarget !== 'string')
|
|
2854
|
+
return '';
|
|
2855
|
+
const relative = (0, shell_command_projection_cjs_1.posixNormalize)(node_path_1.default.relative(projectRoot, resolvedTarget));
|
|
2856
|
+
if (!relative || relative === '.' || relative === '..' || relative.startsWith('../') || node_path_1.default.isAbsolute(relative))
|
|
2857
|
+
return '';
|
|
2858
|
+
return projectRelativePrefix(relative);
|
|
2859
|
+
}
|
|
2860
|
+
/**
|
|
2861
|
+
* A runtime installed directly at the project root cannot use its descriptor
|
|
2862
|
+
* directory in a project-relative include: that directory was never created.
|
|
2863
|
+
*/
|
|
2864
|
+
function localIncludeDirName(runtime) {
|
|
2865
|
+
return hostBehaviorsFor(runtime).localTargetIsProjectRoot === true ? undefined : getDirName(runtime);
|
|
2866
|
+
}
|
|
2867
|
+
/**
|
|
2868
|
+
* #4377: the project-relative prefix for a local install, or `''` when the
|
|
2869
|
+
* runtime cannot express one and the caller must fall back to absolute.
|
|
2870
|
+
*
|
|
2871
|
+
* Rejects the `configHome.kind === 'none'` sentinel (a runtime with no local
|
|
2872
|
+
* config dir at all — interpolating it would produce a literal
|
|
2873
|
+
* `(no-local-config-dir)/` path segment), anything absolute, and anything that
|
|
2874
|
+
* climbs out of the project with `..`. Trailing slashes are normalized so a
|
|
2875
|
+
* descriptor value written either way yields one prefix.
|
|
2876
|
+
*
|
|
2877
|
+
* @param localDirName - The runtime's `localConfigDir` descriptor value.
|
|
2878
|
+
* @returns A `dir/` prefix, or `''` to signal "use the absolute form".
|
|
2879
|
+
*/
|
|
2880
|
+
function projectRelativePrefix(localDirName) {
|
|
2881
|
+
if (typeof localDirName !== 'string' || localDirName.length === 0)
|
|
2882
|
+
return '';
|
|
2883
|
+
if (localDirName === runtimeNamePolicy.NO_LOCAL_CONFIG_DIR_SENTINEL)
|
|
2884
|
+
return '';
|
|
2885
|
+
const normalized = (0, shell_command_projection_cjs_1.posixNormalize)(localDirName).replace(/\/+$/, '');
|
|
2886
|
+
if (!normalized || normalized.startsWith('/') || /^[A-Za-z]:/.test(normalized))
|
|
2887
|
+
return '';
|
|
2888
|
+
// Reject a climb in ANY segment, not only at the beginning. posixNormalize
|
|
2889
|
+
// deliberately normalizes separators rather than resolving path segments,
|
|
2890
|
+
// so `nested/../../outside` must be caught explicitly here.
|
|
2891
|
+
if (normalized.split('/').includes('..'))
|
|
2892
|
+
return '';
|
|
2893
|
+
return `${normalized}/`;
|
|
2894
|
+
}
|
|
2794
2895
|
/**
|
|
2795
2896
|
* Canonical list of every non-Claude runtime that gsd-core emits artifacts for.
|
|
2796
2897
|
* DERIVED from the capability registry (ADR-1239 Phase B, #1679) — the registry's
|
|
@@ -2957,18 +3058,124 @@ function restoreClaudeGlobalAtRefTilde(content, pathPrefix) {
|
|
|
2957
3058
|
*
|
|
2958
3059
|
* @private — exported as `_applyRuntimeRewrites` for tests.
|
|
2959
3060
|
*/
|
|
3061
|
+
/**
|
|
3062
|
+
* #4377: is this prefix a project-relative one?
|
|
3063
|
+
*
|
|
3064
|
+
* Anything not rooted -- no leading `/`, no `$HOME`, no `~`, no drive letter.
|
|
3065
|
+
* Used only to decide whether the shell-default guard below needs to run, so
|
|
3066
|
+
* an absolute install is byte-for-byte untouched by any of this.
|
|
3067
|
+
*
|
|
3068
|
+
* @param pathPrefix - The computed prefix.
|
|
3069
|
+
* @returns Whether it resolves relative to something.
|
|
3070
|
+
*/
|
|
3071
|
+
function isRelativePathPrefix(pathPrefix) {
|
|
3072
|
+
if (typeof pathPrefix !== 'string' || pathPrefix.length === 0)
|
|
3073
|
+
return false;
|
|
3074
|
+
return !(pathPrefix.startsWith('/')
|
|
3075
|
+
|| pathPrefix.startsWith('$HOME')
|
|
3076
|
+
|| pathPrefix.startsWith('~')
|
|
3077
|
+
|| /^[A-Za-z]:/.test(pathPrefix));
|
|
3078
|
+
}
|
|
3079
|
+
/**
|
|
3080
|
+
* #4377: shield `${VAR:-default}` shell defaults from a RELATIVE path prefix.
|
|
3081
|
+
*
|
|
3082
|
+
* The runtime launcher snippet probes for gsd-tools through a chain of shell
|
|
3083
|
+
* defaults -- `${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/...`, one per
|
|
3084
|
+
* runtime. Those are shell word expansions, not markdown `@` includes, and
|
|
3085
|
+
* they are the one place where substituting a project-relative prefix makes
|
|
3086
|
+
* things WORSE rather than better: `$HOME/.claude` resolves the same from
|
|
3087
|
+
* anywhere, while a bare `.claude` resolves against whatever directory the
|
|
3088
|
+
* shell happens to be sitting in. Trading an include that points at the wrong
|
|
3089
|
+
* checkout for a path that points at nothing is not a fix.
|
|
3090
|
+
*
|
|
3091
|
+
* The issue asked for project-relative INCLUDES, and this keeps the change to
|
|
3092
|
+
* exactly that. The launcher already handles the multi-worktree case on its
|
|
3093
|
+
* own, and better -- it probes `$(git rev-parse --show-toplevel)/.claude/...`
|
|
3094
|
+
* first, so it finds the current worktree's copy long before it reaches these
|
|
3095
|
+
* defaults.
|
|
3096
|
+
*
|
|
3097
|
+
* The mask token is `@@GSD4377:<n>@@`. It has to survive every substitution in
|
|
3098
|
+
* the rewrite body untouched, so it deliberately contains no `.claude`, no
|
|
3099
|
+
* `~`, no `$HOME` and no path separator -- there is nothing in it for those
|
|
3100
|
+
* regexes to match. A collision would need that literal to already exist in
|
|
3101
|
+
* the shipped corpus, which is asserted against in the tests.
|
|
3102
|
+
*
|
|
3103
|
+
* Only runs when the prefix is relative, so an absolute install never sees
|
|
3104
|
+
* this transformation at all.
|
|
3105
|
+
*
|
|
3106
|
+
* @param content - The body being rewritten.
|
|
3107
|
+
* @param rewrite - The substitution pass to run on the unguarded remainder.
|
|
3108
|
+
* @returns The rewritten body, with every shell default restored verbatim.
|
|
3109
|
+
*/
|
|
3110
|
+
function withShellDefaultsPreserved(content, rewrite) {
|
|
3111
|
+
const preserved = [];
|
|
3112
|
+
let masked = '';
|
|
3113
|
+
let copiedThrough = 0;
|
|
3114
|
+
let searchFrom = 0;
|
|
3115
|
+
// Scan balanced `${...}` expansions instead of stopping at the first `}`.
|
|
3116
|
+
// The launcher has nested defaults such as `${A:-${B:-$HOME/.x}}`; a
|
|
3117
|
+
// single `[^}]*` regex only recognizes a prefix of that expression and
|
|
3118
|
+
// makes preservation depend accidentally on where the rewritten text sits.
|
|
3119
|
+
while (searchFrom < content.length) {
|
|
3120
|
+
const start = content.indexOf('${', searchFrom);
|
|
3121
|
+
if (start === -1)
|
|
3122
|
+
break;
|
|
3123
|
+
const opener = /^\$\{[A-Za-z_][A-Za-z0-9_]*:-/.exec(content.slice(start));
|
|
3124
|
+
if (!opener) {
|
|
3125
|
+
searchFrom = start + 2;
|
|
3126
|
+
continue;
|
|
3127
|
+
}
|
|
3128
|
+
let depth = 1;
|
|
3129
|
+
let end = start + opener[0].length;
|
|
3130
|
+
while (end < content.length && depth > 0) {
|
|
3131
|
+
if (content.startsWith('${', end)) {
|
|
3132
|
+
depth += 1;
|
|
3133
|
+
end += 2;
|
|
3134
|
+
continue;
|
|
3135
|
+
}
|
|
3136
|
+
if (content[end] === '}')
|
|
3137
|
+
depth -= 1;
|
|
3138
|
+
end += 1;
|
|
3139
|
+
}
|
|
3140
|
+
if (depth !== 0) {
|
|
3141
|
+
searchFrom = start + 2;
|
|
3142
|
+
continue;
|
|
3143
|
+
}
|
|
3144
|
+
masked += content.slice(copiedThrough, start);
|
|
3145
|
+
preserved.push(content.slice(start, end));
|
|
3146
|
+
masked += `@@GSD4377:${preserved.length - 1}@@`;
|
|
3147
|
+
copiedThrough = end;
|
|
3148
|
+
searchFrom = end;
|
|
3149
|
+
}
|
|
3150
|
+
masked += content.slice(copiedThrough);
|
|
3151
|
+
return rewrite(masked).replace(/@@GSD4377:(\d+)@@/g, (_m, i) => preserved[Number(i)]);
|
|
3152
|
+
}
|
|
2960
3153
|
function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, attribution = undefined) {
|
|
3154
|
+
// #4377: with a project-relative prefix, run the whole substitution body
|
|
3155
|
+
// with `${VAR:-default}` shell defaults masked out, so the launcher shim
|
|
3156
|
+
// keeps its absolute fallbacks. A no-op for the absolute (default) prefix.
|
|
3157
|
+
if (isRelativePathPrefix(pathPrefix)) {
|
|
3158
|
+
return withShellDefaultsPreserved(content, (masked) => _applyRuntimeRewritesInner(masked, runtime, pathPrefix, isGlobal, attribution));
|
|
3159
|
+
}
|
|
3160
|
+
return _applyRuntimeRewritesInner(content, runtime, pathPrefix, isGlobal, attribution);
|
|
3161
|
+
}
|
|
3162
|
+
function _applyRuntimeRewritesInner(content, runtime, pathPrefix, isGlobal = false, attribution = undefined) {
|
|
2961
3163
|
const dirName = getDirName(runtime);
|
|
2962
3164
|
const normalizedPathPrefix = pathPrefix.replace(/\/$/, '');
|
|
2963
3165
|
content = filterRuntimeNotesForTarget(content, runtime);
|
|
2964
3166
|
// #1521: stamp runtime identity + use_worktrees=false for every non-Claude runtime
|
|
2965
3167
|
// before brand-specific path rewrites, so the replace operates on the pristine
|
|
2966
3168
|
// source line and is idempotent regardless of subsequent path substitutions.
|
|
2967
|
-
|
|
3169
|
+
// #5169: the descriptor declares which hosts skip the stamp
|
|
3170
|
+
// (`hostBehaviors.skipRuntimeDefaultsStamp`) and which rewrite profile applies
|
|
3171
|
+
// (`hostBehaviors.contentRewriteProfile`). The switch below is keyed by PROFILE
|
|
3172
|
+
// name, not runtime id, so two hosts that share a body share one case.
|
|
3173
|
+
const rewriteBehaviors = hostBehaviorsFor(runtime);
|
|
3174
|
+
if (!rewriteBehaviors.skipRuntimeDefaultsStamp) {
|
|
2968
3175
|
content = _stampNonClaudeRuntimeDefaults(content, runtime);
|
|
2969
3176
|
}
|
|
2970
|
-
switch (
|
|
2971
|
-
case '
|
|
3177
|
+
switch (rewriteBehaviors.contentRewriteProfile) {
|
|
3178
|
+
case 'slash-own-tilde':
|
|
2972
3179
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
2973
3180
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
2974
3181
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
@@ -2976,7 +3183,7 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
2976
3183
|
// #1515 stamp moved to _stampNonClaudeRuntimeDefaults (#1521 generalisation).
|
|
2977
3184
|
content = processAttribution(content, attribution);
|
|
2978
3185
|
break;
|
|
2979
|
-
case '
|
|
3186
|
+
case 'slash-bare-own-tilde':
|
|
2980
3187
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
2981
3188
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
2982
3189
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
@@ -2988,7 +3195,7 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
2988
3195
|
content = content.replace(/\$HOME\/\.cline\b/g, normalizedPathPrefix);
|
|
2989
3196
|
content = processAttribution(content, attribution);
|
|
2990
3197
|
break;
|
|
2991
|
-
case '
|
|
3198
|
+
case 'slash-lookahead-own-tilde':
|
|
2992
3199
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
2993
3200
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
2994
3201
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
@@ -2998,7 +3205,7 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
2998
3205
|
content = content.replace(/~\/\.cursor\//g, pathPrefix);
|
|
2999
3206
|
content = processAttribution(content, attribution);
|
|
3000
3207
|
break;
|
|
3001
|
-
case '
|
|
3208
|
+
case 'slash-lookahead-nested-own-devin': {
|
|
3002
3209
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
3003
3210
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
3004
3211
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
@@ -3014,7 +3221,7 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
3014
3221
|
content = processAttribution(content, attribution);
|
|
3015
3222
|
break;
|
|
3016
3223
|
}
|
|
3017
|
-
case '
|
|
3224
|
+
case 'slash-lookahead-own-derived': {
|
|
3018
3225
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
3019
3226
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
3020
3227
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
@@ -3032,7 +3239,7 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
3032
3239
|
content = processAttribution(content, attribution);
|
|
3033
3240
|
break;
|
|
3034
3241
|
}
|
|
3035
|
-
case '
|
|
3242
|
+
case 'slash-bare-own-derived':
|
|
3036
3243
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
3037
3244
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
3038
3245
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
@@ -3045,7 +3252,7 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
3045
3252
|
content = content.replace(new RegExp('~/' + (0, pattern_cjs_1.escapeRegex)(dirName) + '/', 'g'), pathPrefix);
|
|
3046
3253
|
content = processAttribution(content, attribution);
|
|
3047
3254
|
break;
|
|
3048
|
-
case '
|
|
3255
|
+
case 'slash-bare-own-tilde-bare':
|
|
3049
3256
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
3050
3257
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
3051
3258
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
@@ -3058,13 +3265,14 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
3058
3265
|
content = content.replace(/\$HOME\/\.codebuddy\b/g, normalizedPathPrefix);
|
|
3059
3266
|
content = processAttribution(content, attribution);
|
|
3060
3267
|
break;
|
|
3061
|
-
case '
|
|
3268
|
+
case 'attribution-only':
|
|
3062
3269
|
content = processAttribution(content, attribution);
|
|
3063
3270
|
break;
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3271
|
+
// claude and zcode share this profile. #4002: ZCode is a Claude-Code-shaped
|
|
3272
|
+
// host (dot-home `.zcode`, `@~`-ref expansion, `~/.zcode/...` documented
|
|
3273
|
+
// paths) whose commands install with `converter: null` — this pass is their
|
|
3274
|
+
// only chance to receive runtime-correct paths, including the tilde restore.
|
|
3275
|
+
case 'slash-tilde-restore':
|
|
3068
3276
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
3069
3277
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
3070
3278
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
@@ -3078,12 +3286,15 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
3078
3286
|
// brand VALUES (CLAUDE.md/Claude Code/.claude/ replacements) now read from
|
|
3079
3287
|
// runtime.hostBehaviors.brandingRewrites instead of hardcoded literals.
|
|
3080
3288
|
// EXACT regexes/order preserved — only the replacement values changed.
|
|
3081
|
-
|
|
3289
|
+
// qwen and hermes share this profile; their own dot-dir is derived from the
|
|
3290
|
+
// descriptor's `localConfigDir` (`getDirName`) rather than spelled per host.
|
|
3291
|
+
case 'branded-lookahead-own-derived': {
|
|
3082
3292
|
// Guarded (post-review #2092): brandingRewrites is undefined if the
|
|
3083
3293
|
// capability registry fails to load — degrade closed (skip the
|
|
3084
3294
|
// brand-literal replacements, still apply the non-branding path
|
|
3085
3295
|
// rewrites below) instead of throwing on `_b['CLAUDE.md']`.
|
|
3086
|
-
const _b =
|
|
3296
|
+
const _b = rewriteBehaviors.brandingRewrites;
|
|
3297
|
+
const _own = (0, pattern_cjs_1.escapeRegex)(dirName);
|
|
3087
3298
|
if (_b) {
|
|
3088
3299
|
content = content.replace(/CLAUDE\.md/g, _b['CLAUDE.md']);
|
|
3089
3300
|
// #2284(b): skips <runtime_compatibility> comparison-table content (protected region).
|
|
@@ -3091,46 +3302,21 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
3091
3302
|
}
|
|
3092
3303
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
3093
3304
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
3094
|
-
content = content.replace(
|
|
3095
|
-
content = content.replace(
|
|
3305
|
+
content = content.replace(new RegExp('~/' + _own + '/', 'g'), pathPrefix);
|
|
3306
|
+
content = content.replace(new RegExp('\\$HOME/' + _own + '/', 'g'), pathPrefix);
|
|
3096
3307
|
content = content.replace(/~\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
3097
3308
|
content = content.replace(/\$HOME\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
3098
|
-
content = content.replace(
|
|
3099
|
-
content = content.replace(
|
|
3309
|
+
content = content.replace(new RegExp('~/' + _own + '(?![\\w-])', 'g'), normalizedPathPrefix);
|
|
3310
|
+
content = content.replace(new RegExp('\\$HOME/' + _own + '(?![\\w-])', 'g'), normalizedPathPrefix);
|
|
3100
3311
|
if (_b) {
|
|
3101
3312
|
content = content.replace(/\.claude\//g, _b['.claude/']);
|
|
3102
3313
|
}
|
|
3103
3314
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
3104
|
-
content = content.replace(
|
|
3315
|
+
content = content.replace(new RegExp('\\./' + _own + '/', 'g'), `./${dirName}/`);
|
|
3105
3316
|
content = processAttribution(content, attribution);
|
|
3106
3317
|
break;
|
|
3107
3318
|
}
|
|
3108
|
-
case '
|
|
3109
|
-
// Guarded (post-review #2092): see qwen case above — same degrade-closed
|
|
3110
|
-
// rationale.
|
|
3111
|
-
const _b = _hostBehaviors(runtime).brandingRewrites;
|
|
3112
|
-
if (_b) {
|
|
3113
|
-
content = content.replace(/CLAUDE\.md/g, _b['CLAUDE.md']);
|
|
3114
|
-
// #2284(b): skips <runtime_compatibility> comparison-table content (protected region).
|
|
3115
|
-
content = applyClaudeCodeBrandSwap(content, _b['Claude Code']);
|
|
3116
|
-
}
|
|
3117
|
-
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
3118
|
-
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
3119
|
-
content = content.replace(/~\/\.hermes\//g, pathPrefix);
|
|
3120
|
-
content = content.replace(/\$HOME\/\.hermes\//g, pathPrefix);
|
|
3121
|
-
content = content.replace(/~\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
3122
|
-
content = content.replace(/\$HOME\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
3123
|
-
content = content.replace(/~\/\.hermes(?![\w-])/g, normalizedPathPrefix);
|
|
3124
|
-
content = content.replace(/\$HOME\/\.hermes(?![\w-])/g, normalizedPathPrefix);
|
|
3125
|
-
if (_b) {
|
|
3126
|
-
content = content.replace(/\.claude\//g, _b['.claude/']);
|
|
3127
|
-
}
|
|
3128
|
-
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
3129
|
-
content = content.replace(/\.\/\.hermes\//g, `./${dirName}/`);
|
|
3130
|
-
content = processAttribution(content, attribution);
|
|
3131
|
-
break;
|
|
3132
|
-
}
|
|
3133
|
-
case 'kimi':
|
|
3319
|
+
case 'slash-bare':
|
|
3134
3320
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
3135
3321
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
3136
3322
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
@@ -3139,22 +3325,8 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
3139
3325
|
content = content.replace(/\.\/\.claude\b/g, `./${dirName}`);
|
|
3140
3326
|
content = processAttribution(content, attribution);
|
|
3141
3327
|
break;
|
|
3142
|
-
case 'zcode':
|
|
3143
|
-
// #4002: ZCode is a Claude-Code-shaped host (dot-home `.zcode`, `@~`-ref
|
|
3144
|
-
// expansion, `~/.zcode/...` documented paths) whose commands install with
|
|
3145
|
-
// `converter: null` — this pass is their only chance to receive
|
|
3146
|
-
// runtime-correct paths. Same shape as `claude`, including the tilde
|
|
3147
|
-
// restore: the tilde form is what ZCode expands and what its docs use.
|
|
3148
|
-
// `${_GSD_RUNTIME_ROOT}/.claude/…` matches none of these regexes and
|
|
3149
|
-
// survives as the project-local fallback, exactly as on every sibling.
|
|
3150
|
-
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
3151
|
-
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
3152
|
-
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
3153
|
-
content = restoreClaudeGlobalAtRefTilde(content, pathPrefix);
|
|
3154
|
-
content = processAttribution(content, attribution);
|
|
3155
|
-
break;
|
|
3156
3328
|
default:
|
|
3157
|
-
//
|
|
3329
|
+
// No profile declared — no rewrites (OpenCode/Kilo handled by their own install path).
|
|
3158
3330
|
break;
|
|
3159
3331
|
}
|
|
3160
3332
|
return content;
|
|
@@ -3220,7 +3392,7 @@ function applyRuntimeContentRewritesForCommandsInPlace(stagedDir, runtime, pathP
|
|
|
3220
3392
|
content = _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal, attribution);
|
|
3221
3393
|
// #2097 (ADR-1239): descriptor-driven — commandBodyConverter name comes
|
|
3222
3394
|
// from runtime.hostBehaviors instead of a hardcoded runtime-name branch.
|
|
3223
|
-
const _cmdConv =
|
|
3395
|
+
const _cmdConv = hostBehaviorsFor(runtime).commandBodyConverter;
|
|
3224
3396
|
if (_cmdConv && COMMAND_BODY_CONVERTERS[_cmdConv]) {
|
|
3225
3397
|
content = COMMAND_BODY_CONVERTERS[_cmdConv](content);
|
|
3226
3398
|
}
|
|
@@ -3296,7 +3468,9 @@ function rewriteStagedSkillBodies(stagedDir, opts) {
|
|
|
3296
3468
|
const isGlobal = (0, install_scope_cjs_1.isGlobalScope)(scope);
|
|
3297
3469
|
const isOpencode = false; // #2087: opencode installs via the combined-family engine path, never through the generic rewrite
|
|
3298
3470
|
const isWindowsHost = platform === 'win32';
|
|
3299
|
-
|
|
3471
|
+
// #4377: localDirName lets a local install emit a project-relative prefix
|
|
3472
|
+
// when opted in; ignored for a global install and when the opt-in is off.
|
|
3473
|
+
const pathPrefix = computePathPrefix({ isGlobal, isOpencode, isWindowsHost, resolvedTarget, homeDir, localDirName: localIncludeDirName(runtime) });
|
|
3300
3474
|
const attribution = resolveAttribution ? resolveAttribution(runtime) : undefined;
|
|
3301
3475
|
applyRuntimeContentRewritesInPlace(stagedDir, runtime, pathPrefix, isGlobal, attribution);
|
|
3302
3476
|
// #2873 (4b): claude, global scope only — see
|
|
@@ -3307,7 +3481,7 @@ function rewriteStagedSkillBodies(stagedDir, opts) {
|
|
|
3307
3481
|
// no skills-kind entry at local scope, so this is already structurally
|
|
3308
3482
|
// scoped to global (row 23) — the explicit isGlobal check is defense-in-depth
|
|
3309
3483
|
// against that descriptor wiring ever changing.
|
|
3310
|
-
if (runtime
|
|
3484
|
+
if (hostBehaviorsFor(runtime).specRootSkillPass && isGlobal) {
|
|
3311
3485
|
applySpecRootReferenceToStagedSkills(stagedDir);
|
|
3312
3486
|
}
|
|
3313
3487
|
}
|
|
@@ -3339,7 +3513,9 @@ function rewriteStagedCommandBodies(stagedDir, opts) {
|
|
|
3339
3513
|
const isGlobal = (0, install_scope_cjs_1.isGlobalScope)(scope);
|
|
3340
3514
|
const isOpencode = false; // #2087: opencode installs via the combined-family engine path, never through the generic rewrite
|
|
3341
3515
|
const isWindowsHost = platform === 'win32';
|
|
3342
|
-
|
|
3516
|
+
// #4377: localDirName lets a local install emit a project-relative prefix
|
|
3517
|
+
// when opted in; ignored for a global install and when the opt-in is off.
|
|
3518
|
+
const pathPrefix = computePathPrefix({ isGlobal, isOpencode, isWindowsHost, resolvedTarget, homeDir, localDirName: localIncludeDirName(runtime) });
|
|
3343
3519
|
const attribution = resolveAttribution ? resolveAttribution(runtime) : undefined;
|
|
3344
3520
|
return applyRuntimeContentRewritesForCommandsInPlace(stagedDir, runtime, pathPrefix, isGlobal, attribution);
|
|
3345
3521
|
}
|
|
@@ -3357,7 +3533,7 @@ function rewriteStagedCommandBodies(stagedDir, opts) {
|
|
|
3357
3533
|
* @param cmdNames gsd command names from readGsdCommandNames()
|
|
3358
3534
|
*/
|
|
3359
3535
|
function normalizeAgentBodyForRuntime(content, runtime, cmdNames) {
|
|
3360
|
-
if (
|
|
3536
|
+
if (hostBehaviorsFor(runtime).hyphenNameAgentBody !== true)
|
|
3361
3537
|
return content;
|
|
3362
3538
|
return transformContentToHyphen(content, cmdNames);
|
|
3363
3539
|
}
|
|
@@ -3387,8 +3563,20 @@ function normalizeAgentBodyForRuntime(content, runtime, cmdNames) {
|
|
|
3387
3563
|
* @returns content with path-prefix rewrites applied (or unchanged for noPathRewrite runtimes, e.g. copilot)
|
|
3388
3564
|
*/
|
|
3389
3565
|
function applyAgentPathRewrites(content, runtime, pathPrefix) {
|
|
3390
|
-
if (
|
|
3566
|
+
if (hostBehaviorsFor(runtime).noPathRewrite === true)
|
|
3391
3567
|
return content;
|
|
3568
|
+
// #4377: the agents pipeline is the third emit path that substitutes this
|
|
3569
|
+
// prefix (skills/commands via _applyRuntimeRewrites, the gsd-core spec tree
|
|
3570
|
+
// via copyWithPathReplacement, and here). All three carry the same guard:
|
|
3571
|
+
// with a project-relative prefix, `${VAR:-default}` shell defaults are
|
|
3572
|
+
// masked out so the runtime launcher keeps its absolute fallbacks. A no-op
|
|
3573
|
+
// for the absolute prefix. See withShellDefaultsPreserved for why.
|
|
3574
|
+
if (isRelativePathPrefix(pathPrefix)) {
|
|
3575
|
+
return withShellDefaultsPreserved(content, (masked) => applyAgentPathRewritesInner(masked, runtime, pathPrefix));
|
|
3576
|
+
}
|
|
3577
|
+
return applyAgentPathRewritesInner(content, runtime, pathPrefix);
|
|
3578
|
+
}
|
|
3579
|
+
function applyAgentPathRewritesInner(content, runtime, pathPrefix) {
|
|
3392
3580
|
const normalizedPathPrefix = pathPrefix.replace(/\/$/, '');
|
|
3393
3581
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
3394
3582
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
@@ -3417,7 +3605,7 @@ function applyAgentPathRewrites(content, runtime, pathPrefix) {
|
|
|
3417
3605
|
// anchored to the exact prefix string it is handed — so `restore(pathPrefix)`
|
|
3418
3606
|
// fixes `@$HOME/.claude/x` and leaves a bare `@$HOME/.claude` broken. The
|
|
3419
3607
|
// normalized form is a PREFIX of both, so one call covers both.
|
|
3420
|
-
if (runtime
|
|
3608
|
+
if (hostBehaviorsFor(runtime).restoreAtRefTildeInAgents) {
|
|
3421
3609
|
content = restoreClaudeGlobalAtRefTilde(content, normalizedPathPrefix);
|
|
3422
3610
|
}
|
|
3423
3611
|
return content;
|
|
@@ -3441,8 +3629,8 @@ function deriveAgentName(fileName) {
|
|
|
3441
3629
|
* `applyAgentFrontmatterExtensions` below for the orchestration that calls it.
|
|
3442
3630
|
*
|
|
3443
3631
|
* The function:
|
|
3444
|
-
* -
|
|
3445
|
-
* otherwise LF).
|
|
3632
|
+
* - Reads the block the one fence owner (`locateFrontmatterFence`) finds, and
|
|
3633
|
+
* its EOL (CRLF if the opening `---` line ends with \r\n, otherwise LF).
|
|
3446
3634
|
* - Skips injection if an `effort:` key already exists in the frontmatter
|
|
3447
3635
|
* (idempotent).
|
|
3448
3636
|
* - Inserts `effort: <value>` immediately before the closing `---` delimiter,
|
|
@@ -3451,19 +3639,15 @@ function deriveAgentName(fileName) {
|
|
|
3451
3639
|
* - Returns the original content unchanged when no YAML frontmatter is found.
|
|
3452
3640
|
*/
|
|
3453
3641
|
function injectEffortFrontmatter(content, effortValue) {
|
|
3454
|
-
const
|
|
3455
|
-
|
|
3456
|
-
const match = fmRe.exec(content);
|
|
3457
|
-
if (!match)
|
|
3642
|
+
const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
|
|
3643
|
+
if (!fence?.closed)
|
|
3458
3644
|
return content; // no YAML frontmatter — leave unchanged
|
|
3459
|
-
const fmBody =
|
|
3645
|
+
const fmBody = content.slice(fence.openEnd, fence.closingStart); // content between the two fences
|
|
3460
3646
|
if (/^effort:/m.test(fmBody))
|
|
3461
3647
|
return content;
|
|
3462
|
-
const
|
|
3463
|
-
const
|
|
3464
|
-
|
|
3465
|
-
const after = content.slice(closingStart);
|
|
3466
|
-
return `${before}effort: ${effortValue}${eol}${after}`;
|
|
3648
|
+
const before = content.slice(0, fence.closingStart);
|
|
3649
|
+
const after = content.slice(fence.closingStart);
|
|
3650
|
+
return `${before}effort: ${effortValue}${fence.eol}${after}`;
|
|
3467
3651
|
}
|
|
3468
3652
|
/**
|
|
3469
3653
|
* #767 — Inject `disallowedTools: <value>` into the YAML frontmatter of a
|
|
@@ -3473,19 +3657,15 @@ function injectEffortFrontmatter(content, effortValue) {
|
|
|
3473
3657
|
* frontmatter keys. Relocated verbatim from bin/install.js (#2875 Part 2).
|
|
3474
3658
|
*/
|
|
3475
3659
|
function injectDisallowedToolsFrontmatter(content, disallowedValue) {
|
|
3476
|
-
const
|
|
3477
|
-
|
|
3478
|
-
const match = fmRe.exec(content);
|
|
3479
|
-
if (!match)
|
|
3660
|
+
const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
|
|
3661
|
+
if (!fence?.closed)
|
|
3480
3662
|
return content; // no YAML frontmatter — leave unchanged
|
|
3481
|
-
const fmBody =
|
|
3663
|
+
const fmBody = content.slice(fence.openEnd, fence.closingStart); // content between the two fences
|
|
3482
3664
|
if (/^disallowedTools:/m.test(fmBody))
|
|
3483
3665
|
return content;
|
|
3484
|
-
const
|
|
3485
|
-
const
|
|
3486
|
-
|
|
3487
|
-
const after = content.slice(closingStart);
|
|
3488
|
-
return `${before}disallowedTools: ${disallowedValue}${eol}${after}`;
|
|
3666
|
+
const before = content.slice(0, fence.closingStart);
|
|
3667
|
+
const after = content.slice(fence.closingStart);
|
|
3668
|
+
return `${before}disallowedTools: ${disallowedValue}${fence.eol}${after}`;
|
|
3489
3669
|
}
|
|
3490
3670
|
// #767 — Read-only verifier/auditor agents get a Claude-Code disallowedTools deny-list.
|
|
3491
3671
|
// Group A (pure read-only) deny Write,Edit,MultiEdit. Group B report-writers Write one
|
|
@@ -3511,7 +3691,7 @@ const READONLY_AGENT_DISALLOWED_TOOLS = {
|
|
|
3511
3691
|
* OpenCode/Qwen/Hermes reject unknown frontmatter keys.
|
|
3512
3692
|
*
|
|
3513
3693
|
* Byte-identical to the inline agent loop's
|
|
3514
|
-
* `if ((
|
|
3694
|
+
* `if ((hostBehaviorsFor(runtime).agentFrontmatterExtensions || []).includes('effort'))`
|
|
3515
3695
|
* block (bin/install.js): both the effort injection AND the disallowedTools
|
|
3516
3696
|
* injection are gated behind the SAME `'effort'` extension flag — there is no
|
|
3517
3697
|
* separate `'disallowedTools'` extension key, mirroring the loop exactly.
|
|
@@ -3526,7 +3706,7 @@ const READONLY_AGENT_DISALLOWED_TOOLS = {
|
|
|
3526
3706
|
* @param targetDir install root — resolves .planning/config.json + ~/.gsd/defaults.json
|
|
3527
3707
|
*/
|
|
3528
3708
|
function applyAgentFrontmatterExtensions(content, { runtime, agentName, targetDir }) {
|
|
3529
|
-
const extensions =
|
|
3709
|
+
const extensions = hostBehaviorsFor(runtime).agentFrontmatterExtensions || [];
|
|
3530
3710
|
if (!extensions.includes('effort'))
|
|
3531
3711
|
return content;
|
|
3532
3712
|
let result = content;
|
|
@@ -3645,8 +3825,8 @@ module.exports = {
|
|
|
3645
3825
|
// #1182: agent converters + tool-name table dependency closure
|
|
3646
3826
|
claudeToCopilotTools,
|
|
3647
3827
|
convertCopilotToolName,
|
|
3648
|
-
|
|
3649
|
-
|
|
3828
|
+
claudeToAntigravityTools,
|
|
3829
|
+
convertAntigravityToolName,
|
|
3650
3830
|
convertClaudeAgentToCopilotAgent,
|
|
3651
3831
|
convertClaudeAgentToAntigravityAgent,
|
|
3652
3832
|
convertClaudeAgentToCursorAgent,
|
|
@@ -3690,6 +3870,12 @@ module.exports = {
|
|
|
3690
3870
|
READONLY_AGENT_DISALLOWED_TOOLS,
|
|
3691
3871
|
applyAgentFrontmatterExtensions,
|
|
3692
3872
|
_computePathPrefix: computePathPrefix,
|
|
3873
|
+
_withShellDefaultsPreserved: withShellDefaultsPreserved,
|
|
3874
|
+
_isRelativePathPrefix: isRelativePathPrefix,
|
|
3875
|
+
_relativeIncludesEnabled: relativeIncludesEnabled,
|
|
3876
|
+
_projectRelativePrefix: projectRelativePrefix,
|
|
3877
|
+
_projectRelativePrefixFromProjectRoot: projectRelativePrefixFromProjectRoot,
|
|
3878
|
+
_localIncludeDirName: localIncludeDirName,
|
|
3693
3879
|
_restoreClaudeGlobalAtRefTilde: restoreClaudeGlobalAtRefTilde,
|
|
3694
3880
|
_applyRuntimeRewrites,
|
|
3695
3881
|
_stampNonClaudeRuntimeDefaults,
|