@opengsd/gsd-core 1.10.0 → 1.12.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/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +12 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-doc-synthesizer.md +2 -4
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +22 -14
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +5 -3
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +23 -2
- package/agents/gsd-plan-checker.md +50 -53
- package/agents/gsd-planner.md +50 -50
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +2 -2
- package/agents/gsd-roadmapper.md +15 -11
- package/agents/gsd-ui-checker.md +63 -4
- package/agents/gsd-ui-researcher.md +41 -3
- package/agents/gsd-user-profiler.md +3 -0
- package/agents/gsd-verifier.md +13 -4
- package/bin/install.js +1448 -1103
- package/commands/gsd/code-review.md +1 -1
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/map-codebase.md +1 -1
- package/commands/gsd/mempalace-capture.md +1 -1
- package/commands/gsd/mempalace-recall.md +1 -1
- package/commands/gsd/new-milestone.md +1 -1
- package/commands/gsd/quick.md +9 -5
- package/commands/gsd/review-backlog.md +2 -1
- package/commands/gsd/verify-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +1035 -138
- package/gsd-core/bin/lib/active-workstream-store.cjs +146 -22
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +392 -32
- package/gsd-core/bin/lib/api-coverage.cjs +33 -14
- package/gsd-core/bin/lib/artifacts.cjs +5 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +32 -15
- package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
- package/gsd-core/bin/lib/audit.cjs +1026 -268
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- package/gsd-core/bin/lib/capability-consent.cjs +149 -15
- package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +845 -130
- package/gsd-core/bin/lib/capability-source.cjs +92 -0
- package/gsd-core/bin/lib/capability-state.cjs +18 -3
- package/gsd-core/bin/lib/capability-trust.cjs +444 -25
- package/gsd-core/bin/lib/capability-validator.cjs +700 -40
- package/gsd-core/bin/lib/capability-writer.cjs +3 -2
- package/gsd-core/bin/lib/check-command-router.cjs +216 -42
- package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +735 -0
- package/gsd-core/bin/lib/command-aliases.cjs +22 -0
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-roster.cjs +44 -1
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +1172 -108
- package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
- package/gsd-core/bin/lib/complexity-trigger.cjs +1192 -0
- package/gsd-core/bin/lib/config-loader.cjs +187 -23
- package/gsd-core/bin/lib/config.cjs +102 -3
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +208 -33
- package/gsd-core/bin/lib/decisions.cjs +23 -0
- package/gsd-core/bin/lib/edge-probe.cjs +9 -1
- package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
- package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
- package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
- package/gsd-core/bin/lib/frontmatter.cjs +899 -229
- package/gsd-core/bin/lib/gap-checker.cjs +95 -10
- package/gsd-core/bin/lib/git-base-branch.cjs +276 -39
- package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
- package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +149 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +268 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +187 -0
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +451 -0
- package/gsd-core/bin/lib/host-integration.cjs +39 -6
- package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
- package/gsd-core/bin/lib/init-command-router.cjs +118 -21
- package/gsd-core/bin/lib/init.cjs +439 -168
- package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
- package/gsd-core/bin/lib/install-engine.cjs +811 -259
- package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +235 -0
- package/gsd-core/bin/lib/install-profiles.cjs +212 -61
- package/gsd-core/bin/lib/install-scope.cjs +270 -0
- package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
- package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
- package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
- package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +148 -38
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +170 -15
- package/gsd-core/bin/lib/learnings.cjs +85 -14
- package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
- package/gsd-core/bin/lib/markdown-table.cjs +183 -22
- package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
- package/gsd-core/bin/lib/milestone.cjs +842 -73
- package/gsd-core/bin/lib/model-catalog.cjs +232 -16
- package/gsd-core/bin/lib/model-resolver.cjs +193 -68
- package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/pattern.cjs +122 -0
- package/gsd-core/bin/lib/phase-estimation.cjs +18 -9
- package/gsd-core/bin/lib/phase-id.cjs +514 -40
- package/gsd-core/bin/lib/phase-lifecycle.cjs +52 -19
- package/gsd-core/bin/lib/phase-locator.cjs +262 -34
- package/gsd-core/bin/lib/phase.cjs +1038 -214
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
- package/gsd-core/bin/lib/plan-document.cjs +263 -0
- package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
- package/gsd-core/bin/lib/plan-scan.cjs +98 -3
- package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
- package/gsd-core/bin/lib/planning-scope.cjs +31 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +894 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +112 -6
- package/gsd-core/bin/lib/probe-core.cjs +5 -2
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
- package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +766 -0
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -13
- package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
- package/gsd-core/bin/lib/roadmap-command-router.cjs +59 -11
- package/gsd-core/bin/lib/roadmap-parser.cjs +1006 -184
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
- package/gsd-core/bin/lib/roadmap.cjs +442 -96
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +702 -52
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +459 -55
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
- package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +402 -58
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
- package/gsd-core/bin/lib/runtime-slash.cjs +96 -8
- package/gsd-core/bin/lib/security.cjs +104 -5
- package/gsd-core/bin/lib/shell-command-projection.cjs +342 -7
- package/gsd-core/bin/lib/smart-entry.cjs +133 -23
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +52 -19
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +338 -8
- package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
- package/gsd-core/bin/lib/state-transition.cjs +846 -176
- package/gsd-core/bin/lib/state.cjs +2589 -369
- package/gsd-core/bin/lib/surface.cjs +33 -11
- package/gsd-core/bin/lib/task-command-router.cjs +111 -1
- package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
- package/gsd-core/bin/lib/teams-status.cjs +4 -1
- package/gsd-core/bin/lib/text-lines.cjs +80 -0
- package/gsd-core/bin/lib/token-scanner.cjs +76 -0
- package/gsd-core/bin/lib/uat-predicate.cjs +67 -23
- package/gsd-core/bin/lib/uat.cjs +1761 -167
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +51 -12
- package/gsd-core/bin/lib/unusable-input.cjs +37 -0
- package/gsd-core/bin/lib/update-context.cjs +8 -2
- package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +20 -6
- package/gsd-core/bin/lib/vendor/README.md +75 -0
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
- package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
- package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +272 -9
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify.cjs +453 -918
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
- package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
- package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/workstream.cjs +2 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +341 -18
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +12 -1
- package/gsd-core/bin/shared/exit-codes.json +8 -0
- package/gsd-core/bin/shared/exit-codes.sh +20 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -1
- package/gsd-core/references/agent-contracts.md +44 -26
- package/gsd-core/references/api-coverage.md +24 -2
- package/gsd-core/references/autonomous-smart-discuss.md +3 -3
- package/gsd-core/references/checkpoints.md +39 -21
- package/gsd-core/references/context-budget.md +1 -1
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/dispatch-isolation-gate.md +138 -0
- package/gsd-core/references/doc-conflict-engine.md +1 -1
- package/gsd-core/references/edge-probe.md +8 -0
- package/gsd-core/references/execute-mvp-tdd.md +4 -6
- package/gsd-core/references/execute-phase-between-wave-reset.md +15 -14
- package/gsd-core/references/execute-phase-context-guard.md +1 -1
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +17 -11
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-integration.md +5 -5
- package/gsd-core/references/git-planning-commit.md +5 -4
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +61 -2
- package/gsd-core/references/model-profiles.md +12 -4
- package/gsd-core/references/mvp-concepts.md +9 -9
- package/gsd-core/references/nyquist-compliance.md +74 -0
- package/gsd-core/references/offer-next.md +3 -5
- package/gsd-core/references/phase-argument-parsing.md +3 -3
- package/gsd-core/references/planner-failing-direction.md +53 -0
- package/gsd-core/references/planner-guidance.md +3 -9
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- package/gsd-core/references/planner-preconditions.md +1 -1
- package/gsd-core/references/planner-reviews.md +1 -1
- package/gsd-core/references/planner-revision.md +1 -1
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +44 -13
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/revision-loop.md +1 -1
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/specless-probe-fallback.md +1 -1
- package/gsd-core/references/tdd.md +1 -3
- package/gsd-core/references/ui-brand.md +65 -21
- package/gsd-core/references/ui-consideration-probe.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +5 -5
- package/gsd-core/references/verifier-phase-gates.md +192 -0
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -17
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/SECURITY.md +3 -3
- package/gsd-core/templates/UI-SPEC.md +25 -3
- package/gsd-core/templates/VALIDATION.md +3 -3
- package/gsd-core/templates/discussion-log.md +1 -1
- package/gsd-core/templates/phase-prompt.md +5 -4
- package/gsd-core/templates/state.md +11 -4
- package/gsd-core/templates/verification-report.md +9 -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 +3 -3
- package/gsd-core/workflows/add-tests.md +3 -8
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +13 -20
- package/gsd-core/workflows/audit-fix.md +12 -3
- package/gsd-core/workflows/audit-milestone.md +9 -9
- package/gsd-core/workflows/audit-uat.md +17 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +11 -27
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +64 -5
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +14 -4
- package/gsd-core/workflows/code-review-fix.md +38 -11
- package/gsd-core/workflows/code-review.md +159 -52
- package/gsd-core/workflows/complete-milestone.md +151 -23
- package/gsd-core/workflows/debug.md +12 -8
- package/gsd-core/workflows/diagnose-issues.md +47 -15
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -8
- package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +3 -6
- package/gsd-core/workflows/docs-update.md +5 -4
- package/gsd-core/workflows/edit-phase.md +27 -2
- package/gsd-core/workflows/eval-review.md +7 -14
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +142 -15
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +24 -4
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase.md +72 -100
- package/gsd-core/workflows/execute-plan.md +52 -15
- package/gsd-core/workflows/explore.md +131 -4
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/fast.md +10 -2
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +5 -5
- package/gsd-core/workflows/health.md +76 -10
- package/gsd-core/workflows/import.md +18 -15
- package/gsd-core/workflows/inbox.md +4 -5
- package/gsd-core/workflows/ingest-docs.md +49 -16
- package/gsd-core/workflows/insert-phase.md +5 -5
- package/gsd-core/workflows/list-seeds.md +5 -3
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +12 -23
- 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 +8 -5
- package/gsd-core/workflows/new-milestone.md +22 -29
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +26 -40
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +14 -2
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
- package/gsd-core/workflows/plan-phase.md +162 -59
- package/gsd-core/workflows/plan-review-convergence.md +96 -11
- package/gsd-core/workflows/plant-seed.md +2 -2
- package/gsd-core/workflows/pr-branch.md +187 -51
- package/gsd-core/workflows/profile-user.md +16 -14
- package/gsd-core/workflows/progress.md +61 -18
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +5 -7
- package/gsd-core/workflows/quick/steps/quick-verification.md +28 -9
- package/gsd-core/workflows/quick/steps/research-phase.md +4 -6
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick.md +55 -44
- package/gsd-core/workflows/remove-phase.md +4 -4
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +8 -12
- package/gsd-core/workflows/review.md +219 -20
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +3 -3
- package/gsd-core/workflows/session-report.md +2 -1
- package/gsd-core/workflows/settings-advanced.md +7 -9
- package/gsd-core/workflows/settings-integrations.md +64 -31
- package/gsd-core/workflows/settings.md +69 -7
- package/gsd-core/workflows/ship.md +116 -50
- package/gsd-core/workflows/sketch-wrap-up.md +11 -17
- package/gsd-core/workflows/sketch.md +12 -18
- package/gsd-core/workflows/smart-entry.md +3 -5
- package/gsd-core/workflows/spec-phase.md +53 -13
- package/gsd-core/workflows/spike-wrap-up.md +7 -11
- package/gsd-core/workflows/spike.md +20 -31
- package/gsd-core/workflows/stats.md +2 -2
- package/gsd-core/workflows/sync-skills.md +64 -9
- package/gsd-core/workflows/thread.md +11 -7
- package/gsd-core/workflows/transition.md +49 -14
- package/gsd-core/workflows/ui-phase.md +15 -21
- package/gsd-core/workflows/ui-review.md +8 -12
- package/gsd-core/workflows/ultraplan-phase.md +5 -13
- package/gsd-core/workflows/undo.md +8 -16
- package/gsd-core/workflows/update.md +7 -11
- package/gsd-core/workflows/validate-phase.md +3 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +66 -25
- package/hooks/dist/gsd-agent-isolation-guard.js +158 -30
- package/hooks/dist/gsd-check-update-worker.js +56 -13
- package/hooks/dist/gsd-check-update.js +19 -1
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +19 -10
- package/hooks/dist/gsd-cursor-post-tool.js +3 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +2 -3
- package/hooks/dist/gsd-cursor-session-start.js +2 -1
- package/hooks/dist/gsd-cursor-stop.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +83 -3
- package/hooks/dist/gsd-cursor-subagent-stop.js +6 -3
- package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
- package/hooks/dist/gsd-graphify-update.sh +22 -18
- package/hooks/dist/gsd-node-runner.sh +76 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +37 -27
- package/hooks/dist/gsd-read-guard.js +16 -7
- package/hooks/dist/gsd-read-injection-scanner.js +55 -32
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +231 -24
- package/hooks/dist/gsd-update-banner.js +22 -1
- package/hooks/dist/gsd-validate-commit.sh +80 -6
- package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
- package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
- package/hooks/dist/gsd-workflow-guard.js +162 -46
- package/hooks/dist/gsd-worktree-path-guard.js +36 -21
- package/hooks/dist/gsd-write-guard.js +35 -25
- package/hooks/dist/lib/cli-exit.js +560 -0
- package/hooks/dist/lib/exit-code-registry.js +98 -0
- package/hooks/dist/lib/git-cmd.js +92 -59
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/lib/injection-patterns.js +45 -0
- package/hooks/dist/lib/isolation-deny-reason.js +39 -0
- package/hooks/dist/lib/isolation-sentinel.js +9 -0
- package/hooks/dist/managed-hooks-registry.cjs +3 -0
- package/hooks/gsd-agent-isolation-guard.js +158 -30
- package/hooks/gsd-check-update-worker.js +56 -13
- package/hooks/gsd-check-update.js +19 -1
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +19 -10
- package/hooks/gsd-cursor-post-tool.js +3 -1
- package/hooks/gsd-cursor-pre-tool.js +2 -3
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +83 -3
- package/hooks/gsd-cursor-subagent-stop.js +6 -3
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +76 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +37 -27
- package/hooks/gsd-read-guard.js +16 -7
- package/hooks/gsd-read-injection-scanner.js +55 -32
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +231 -24
- package/hooks/gsd-update-banner.js +22 -1
- package/hooks/gsd-validate-commit.sh +80 -6
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +162 -46
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-cmd.js +92 -59
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/lib/injection-patterns.js +45 -0
- package/hooks/lib/isolation-deny-reason.js +39 -0
- package/hooks/lib/isolation-sentinel.js +9 -0
- package/hooks/managed-hooks-registry.cjs +3 -0
- package/package.json +28 -11
- package/pi/gsd.cjs +19 -5
- package/scripts/base64-scan.sh +74 -12
- package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
- package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
- package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
- package/scripts/build-hooks.js +5 -0
- package/scripts/changeset/lint.cjs +60 -5
- package/scripts/check-alias-drift.cjs +7 -43
- package/scripts/check-contract-drift.cjs +297 -0
- package/scripts/check-glossary-refs.cjs +77 -15
- package/scripts/check-mutation-score-ratchet.cjs +156 -0
- package/scripts/ci-check-job-near-cap.cjs +49 -0
- package/scripts/ci-pr-mergeability.cjs +262 -0
- package/scripts/ci-test-scope.cjs +64 -14
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/command-contract-helpers.cjs +903 -1
- package/scripts/docs-guard-registry.cjs +396 -0
- package/scripts/gen-adr-index.cjs +728 -38
- package/scripts/gen-capability-registry.cjs +11 -21
- package/scripts/gen-context-index.cjs +2 -11
- package/scripts/gen-exit-code-docs.cjs +318 -0
- package/scripts/gen-exit-code-registry.cjs +891 -0
- package/scripts/gen-features.cjs +836 -0
- package/scripts/gen-health-docs.cjs +390 -0
- package/scripts/gen-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-inventory-manifest.cjs +50 -4
- package/scripts/gen-loop-host-contract.cjs +138 -25
- package/scripts/gen-registry.cjs +3 -14
- package/scripts/gen-scripts-cli-exit.cjs +185 -0
- package/scripts/gen-state-md-docs.cjs +727 -0
- package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
- package/scripts/lib/alias-drift-families.cjs +46 -0
- package/scripts/lib/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +308 -0
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
- package/scripts/lint-canary-version-leak.cjs +73 -0
- package/scripts/lint-command-contract.cjs +96 -13
- package/scripts/lint-completion-predicate-drift.cjs +933 -0
- package/scripts/lint-completion-ratio-drift.cjs +214 -0
- package/scripts/lint-default-flip-documentation.cjs +193 -0
- package/scripts/lint-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +38 -0
- package/scripts/lint-eslint-glob-coverage.cjs +340 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
- package/scripts/lint-health-diagnostic-rule-table.cjs +461 -0
- package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
- package/scripts/lint-milestone-window-drift.cjs +468 -0
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +492 -0
- package/scripts/lint-plan-count-drift.cjs +318 -0
- package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
- package/scripts/lint-planning-prompt-drift.cjs +471 -0
- package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
- package/scripts/lint-regression-test-names.cjs +15 -13
- package/scripts/lint-removed-but-needed.cjs +488 -0
- package/scripts/lint-seam-enforcement.cjs +182 -0
- package/scripts/lint-slug-derivation-drift.cjs +921 -0
- package/scripts/lint-source-test-name-collision.cjs +241 -0
- package/scripts/lint-state-field-drift.cjs +805 -0
- package/scripts/lint-state-write-path-drift.cjs +950 -0
- package/scripts/lint-test-file-count.allowlist.json +137 -8
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +830 -0
- package/scripts/lint-vendored-deps.cjs +297 -0
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/pr-changed-files.cjs +63 -0
- package/scripts/pr-template-policy.cjs +14 -4
- package/scripts/prompt-injection-scan.sh +100 -14
- package/scripts/require-issue-link-policy.cjs +192 -0
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +24 -7
- package/skills/gsd-autonomous/SKILL.md +0 -1
- package/skills/gsd-code-review/SKILL.md +1 -1
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -2
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-map-codebase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +1 -1
- package/skills/gsd-mempalace-recall/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +1 -1
- package/skills/gsd-next/SKILL.md +0 -1
- package/skills/gsd-plan-phase/SKILL.md +0 -1
- package/skills/gsd-progress/SKILL.md +0 -1
- package/skills/gsd-quick/SKILL.md +9 -5
- package/skills/gsd-review-backlog/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +0 -1
- package/skills/gsd-verify-work/SKILL.md +1 -1
- package/vscode/package.json +1 -1
- package/bin/lib/ui-safety-gate.cjs +0 -107
- package/gsd-core/workflows/discovery-phase.md +0 -298
- package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
- package/gsd-core/workflows/verify-phase.md +0 -574
- package/scripts/affected-tests-lib.cjs +0 -554
- package/scripts/lint-allow-test-rule-refs.cjs +0 -162
- package/scripts/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/run-affected-tests.cjs +0 -7
- package/scripts/run-tests.cjs +0 -1051
|
@@ -21,12 +21,35 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
21
21
|
const node_path_1 = __importDefault(require("node:path"));
|
|
22
22
|
const node_os_1 = __importDefault(require("node:os"));
|
|
23
23
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
24
|
+
// #2874 (ADR-58 cleanup phase): route this module's content-rewrite-pass fs
|
|
25
|
+
// calls through the installRuntimeArtifacts call tree's injectable seam —
|
|
26
|
+
// see install-fs-adapter.cts's module doc. Resolves to real `node:fs` unless
|
|
27
|
+
// the top-level installRuntimeArtifacts call injected a `deps.fs`. These
|
|
28
|
+
// walkers operate on already-staged temp directories (never the real GSD
|
|
29
|
+
// source tree or the real install destination directly), so routing them is
|
|
30
|
+
// unconditionally safe.
|
|
31
|
+
const installFsAdapter = require("./install-fs-adapter.cjs");
|
|
32
|
+
const { installFs, mkInstallTempDir } = installFsAdapter;
|
|
24
33
|
const commandRoster = require("./command-roster.cjs");
|
|
25
34
|
const { readGsdCommandNames, transformContentToHyphen } = commandRoster;
|
|
26
35
|
const runtimeNamePolicy = require("./runtime-name-policy.cjs");
|
|
27
36
|
const { getDirName } = runtimeNamePolicy;
|
|
28
37
|
const capabilityRegistry = require("./capability-registry.cjs");
|
|
38
|
+
const hostIntegration = require("./host-integration.cjs");
|
|
29
39
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
40
|
+
const frontmatterModule = require("./frontmatter.cjs");
|
|
41
|
+
const pattern_cjs_1 = require("./pattern.cjs");
|
|
42
|
+
const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
|
|
43
|
+
// #2870: install-scope.cts is a leaf-tier sibling (imports only
|
|
44
|
+
// runtime-homes.cjs + node builtins, never this module) — no cycle. See the
|
|
45
|
+
// isGlobal sites below for why the boolean projection is centralized here too.
|
|
46
|
+
const install_scope_cjs_1 = require("./install-scope.cjs");
|
|
47
|
+
// #2875 Part 2: install-effort-resolver.cjs is a leaf-tier sibling (#2071) —
|
|
48
|
+
// used by applyAgentFrontmatterExtensions below to read the SAME merged
|
|
49
|
+
// effort config the install-time Claude .md injection has always read,
|
|
50
|
+
// without this module reaching upward into bin/install.js (ADR-1508).
|
|
51
|
+
const installEffortResolver = require("./install-effort-resolver.cjs");
|
|
52
|
+
const { readGsdEffectiveEffortConfig, resolveInstallTimeEffort, _getGsdEffortCatalog } = installEffortResolver;
|
|
30
53
|
// #1383: resolve GSD's version WITHOUT a top-level
|
|
31
54
|
// `require('../../../package.json')`. That require ran at module load on every
|
|
32
55
|
// gsd-tools invocation (this module sits in the gsd-tools loader chain) and
|
|
@@ -270,11 +293,8 @@ function buildKiloAgentPermissionBlock(claudeTools) {
|
|
|
270
293
|
}
|
|
271
294
|
return lines;
|
|
272
295
|
}
|
|
273
|
-
function escapeRegExp(value) {
|
|
274
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
275
|
-
}
|
|
276
296
|
function replaceRelativePathReference(content, fromPath, toPath) {
|
|
277
|
-
const escapedPath =
|
|
297
|
+
const escapedPath = (0, pattern_cjs_1.escapeRegex)(fromPath);
|
|
278
298
|
return content.replace(new RegExp(`(^|[^A-Za-z0-9_./-])${escapedPath}`, 'g'), (_, prefix) => `${prefix}${toPath}`);
|
|
279
299
|
}
|
|
280
300
|
/**
|
|
@@ -367,16 +387,6 @@ function skillFrontmatterName(skillDirName) {
|
|
|
367
387
|
// Return the hyphen form as-is (gsd-<cmd>) — canonical since #2808.
|
|
368
388
|
return skillDirName;
|
|
369
389
|
}
|
|
370
|
-
function normalizeClaudeSkillEffort(effort) {
|
|
371
|
-
// #3039: `max` is rejected by Anthropic models when extended thinking is
|
|
372
|
-
// disabled (400: output_config.effort 'max' is not supported when thinking
|
|
373
|
-
// is disabled). The frontmatter is static at install time and the installer
|
|
374
|
-
// cannot know whether thinking will be on or off at invocation. `high` is the
|
|
375
|
-
// maximum value that works in both states on all supported models.
|
|
376
|
-
if (effort === 'xhigh' || effort === 'max')
|
|
377
|
-
return 'high';
|
|
378
|
-
return effort;
|
|
379
|
-
}
|
|
380
390
|
/**
|
|
381
391
|
* Qwen Code skills accept an optional numeric `priority` frontmatter field.
|
|
382
392
|
* Per the Qwen skills spec (qwen-code/docs/users/features/skills.md, verified
|
|
@@ -431,10 +441,13 @@ function convertClaudeCommandToClaudeSkill(content, skillName, runtime = null, c
|
|
|
431
441
|
const description = extractFrontmatterField(frontmatter, 'description') || '';
|
|
432
442
|
const argumentHint = extractFrontmatterField(frontmatter, 'argument-hint');
|
|
433
443
|
const agent = extractFrontmatterField(frontmatter, 'agent');
|
|
434
|
-
// #769: preserve context:
|
|
435
|
-
//
|
|
444
|
+
// #769: preserve context: from source command files so it is emitted into
|
|
445
|
+
// the installed SKILL.md frontmatter unchanged. (#3151: effort: is no longer
|
|
446
|
+
// emitted into skill frontmatter — a static effort value changes
|
|
447
|
+
// output_config.effort on invocation and invalidates the caller's prompt
|
|
448
|
+
// cache at both scope boundaries; the reporter's owned measurement confirms
|
|
449
|
+
// the mechanism. The separate agent-effort surface is tracked by #3160.)
|
|
436
450
|
const context = extractFrontmatterField(frontmatter, 'context');
|
|
437
|
-
const effort = extractFrontmatterField(frontmatter, 'effort');
|
|
438
451
|
// Preserve allowed-tools as YAML multiline list (Claude native format)
|
|
439
452
|
const toolsMatch = frontmatter.match(/^allowed-tools:\s*\n((?:\s+-\s+.+\n?)*)/m);
|
|
440
453
|
let toolsBlock = '';
|
|
@@ -474,19 +487,100 @@ function convertClaudeCommandToClaudeSkill(content, skillName, runtime = null, c
|
|
|
474
487
|
fm += `argument-hint: ${yamlQuote(argumentHint)}\n`;
|
|
475
488
|
if (agent)
|
|
476
489
|
fm += `agent: ${agent}\n`;
|
|
477
|
-
// #769: emit context:
|
|
478
|
-
//
|
|
479
|
-
//
|
|
480
|
-
//
|
|
490
|
+
// #769: emit context: when present so the runtime can honour it natively
|
|
491
|
+
// (context: fork = isolated subagent window). Claude-specific; unknown
|
|
492
|
+
// frontmatter fields are silently ignored by other runtimes (backward-compatible).
|
|
493
|
+
// (#3151: effort: is intentionally NOT emitted into skill frontmatter — a
|
|
494
|
+
// static effort value changes output_config.effort on invocation and
|
|
495
|
+
// invalidates the caller's prompt cache at both scope boundaries.)
|
|
481
496
|
if (context)
|
|
482
497
|
fm += `context: ${context}\n`;
|
|
483
|
-
if (effort)
|
|
484
|
-
fm += `effort: ${normalizeClaudeSkillEffort(effort)}\n`;
|
|
485
498
|
if (toolsBlock)
|
|
486
499
|
fm += toolsBlock;
|
|
487
500
|
fm += '---';
|
|
488
501
|
return `${fm}\n${normalizedBody}`;
|
|
489
502
|
}
|
|
503
|
+
// #2873 (4b) — spec-root reachability. Matches ONLY a line that is a real
|
|
504
|
+
// `@~/.claude/gsd-core/workflows/<stem>.md` include: line-start `@`, exact
|
|
505
|
+
// spec-root shape, nothing else on the line. This is deliberately narrower
|
|
506
|
+
// than "any line mentioning gsd-core/workflows" so prose mentions and
|
|
507
|
+
// `references/`/`templates/`/`@.planning/...` includes are never touched
|
|
508
|
+
// (rows 24/25). CRLF-safe: an optional trailing `\r` is captured and
|
|
509
|
+
// preserved rather than dropped.
|
|
510
|
+
const WORKFLOW_SPEC_ROOT_INCLUDE_RE = /^@~\/\.claude\/gsd-core\/workflows\/([A-Za-z0-9._-]+)\.md[ \t]*(\r?)$/gm;
|
|
511
|
+
/**
|
|
512
|
+
* Rewrite a static global-scope Claude skill `@`-include of the command's own
|
|
513
|
+
* workflow spec into an imperative two-step resolution the agent performs at
|
|
514
|
+
* runtime: prefer the project-local spec (cwd-relative), fall back to the
|
|
515
|
+
* global spec, and treat "neither exists" as a visible failure rather than a
|
|
516
|
+
* silent no-spec proceed.
|
|
517
|
+
*
|
|
518
|
+
* WHY this can't stay a static `@`-include (even a relative one): Claude Code
|
|
519
|
+
* documents relative `@`-paths as resolving against the file *containing* the
|
|
520
|
+
* import, which for a global skill is `~/.claude/skills/gsd-<stem>/` — not
|
|
521
|
+
* the project's working directory. `@./.claude/...` would therefore always
|
|
522
|
+
* resolve inside the skill's own install directory, never the project, so
|
|
523
|
+
* there is no static include syntax that can express "prefer local, fall
|
|
524
|
+
* back to global". This function exists precisely so that resolution can be
|
|
525
|
+
* performed by the agent, not the host's pre-expansion.
|
|
526
|
+
*
|
|
527
|
+
* Scope-free by design: this function does not know or care whether it is
|
|
528
|
+
* being applied to a global or local artifact, or which runtime — that
|
|
529
|
+
* judgment belongs to the caller (`skillsKind` in
|
|
530
|
+
* `runtime-artifact-layout.cts`, the one site that knows install scope).
|
|
531
|
+
* Applying it to a body with no workflow include is a no-op (row 26); a body
|
|
532
|
+
* with two independent workflow includes has each rewritten independently
|
|
533
|
+
* (row 27); an include inside a fenced code block or wrapped in inline
|
|
534
|
+
* backticks is left untouched (the backtick case is already excluded by the
|
|
535
|
+
* line-start anchor, since a backtick-wrapped line does not begin with `@`).
|
|
536
|
+
* Idempotent: the replacement text never begins with `@` and never matches
|
|
537
|
+
* `WORKFLOW_SPEC_ROOT_INCLUDE_RE`, so re-applying this function to its own
|
|
538
|
+
* output is a no-op.
|
|
539
|
+
*
|
|
540
|
+
* Fence detection reuses `scanFencedBlocks` (markdown-sectionizer.cts) — the
|
|
541
|
+
* same CommonMark-correct state machine `stripFencedCode`/`extractFencedBlock`
|
|
542
|
+
* are built on — instead of a hand-rolled "any delimiter line toggles
|
|
543
|
+
* open/closed" tracker. A naive toggle is wrong under CommonMark: a fence
|
|
544
|
+
* opened with ``` is NOT closed by a ~~~ line (closer must share the
|
|
545
|
+
* opener's delimiter character and have run length >= the opener's), so a
|
|
546
|
+
* mismatched delimiter is fence CONTENT, not a boundary. #2873 review.
|
|
547
|
+
*/
|
|
548
|
+
function resolveSpecRootReference(body) {
|
|
549
|
+
if (typeof body !== 'string' || body.length === 0)
|
|
550
|
+
return body;
|
|
551
|
+
if (!body.includes('@~/.claude/gsd-core/workflows/'))
|
|
552
|
+
return body;
|
|
553
|
+
// Collect [start, end) character-offset ranges covered by fenced code
|
|
554
|
+
// blocks so matches inside them are skipped. An unterminated trailing
|
|
555
|
+
// fence covers to the end of the string (still "inside a fence").
|
|
556
|
+
const lines = body.split('\n');
|
|
557
|
+
const lineStartOffsets = [];
|
|
558
|
+
{
|
|
559
|
+
let offset = 0;
|
|
560
|
+
for (const line of lines) {
|
|
561
|
+
lineStartOffsets.push(offset);
|
|
562
|
+
offset += line.length + 1; // +1 for the '\n' separator
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
const fenceRanges = (0, markdown_sectionizer_cjs_1.scanFencedBlocks)(lines).map(({ openLineIdx, closeLineIdx }) => {
|
|
566
|
+
const start = lineStartOffsets[openLineIdx];
|
|
567
|
+
const end = closeLineIdx === -1
|
|
568
|
+
? body.length
|
|
569
|
+
: lineStartOffsets[closeLineIdx] + lines[closeLineIdx].length;
|
|
570
|
+
return [start, end];
|
|
571
|
+
});
|
|
572
|
+
const isInsideFence = (offset) => fenceRanges.some(([start, end]) => offset >= start && offset < end);
|
|
573
|
+
return body.replace(WORKFLOW_SPEC_ROOT_INCLUDE_RE, (match, stem, cr, offset) => {
|
|
574
|
+
if (isInsideFence(offset))
|
|
575
|
+
return match;
|
|
576
|
+
return (`To load this command's workflow spec: check for ` +
|
|
577
|
+
`\`.claude/gsd-core/workflows/${stem}.md\` relative to the current working ` +
|
|
578
|
+
`directory first (project-local); if it is not there, fall back to ` +
|
|
579
|
+
`\`~/.claude/gsd-core/workflows/${stem}.md\` (the global install). If ` +
|
|
580
|
+
`neither file exists, stop — a workflow spec is required and none was found.` +
|
|
581
|
+
cr);
|
|
582
|
+
});
|
|
583
|
+
}
|
|
490
584
|
function normalizeKimiSkillName(skillName) {
|
|
491
585
|
let text = String(skillName || '').trim().toLowerCase();
|
|
492
586
|
if (text.startsWith('/'))
|
|
@@ -502,7 +596,7 @@ function normalizeKimiSkillName(skillName) {
|
|
|
502
596
|
function convertGsdCommandReferencesToKimiSkillInvocations(content, cmdNames) {
|
|
503
597
|
if (!Array.isArray(cmdNames) || cmdNames.length === 0)
|
|
504
598
|
return content;
|
|
505
|
-
const commands = [...cmdNames].sort((a, b) => b.length - a.length).map(
|
|
599
|
+
const commands = [...cmdNames].sort((a, b) => b.length - a.length).map(pattern_cjs_1.escapeRegex);
|
|
506
600
|
const commandGroup = commands.join('|');
|
|
507
601
|
const colonPattern = new RegExp(`(?<![A-Za-z0-9_/:.-])/?gsd:(${commandGroup})(?=[^A-Za-z0-9_-]|$)`, 'g');
|
|
508
602
|
const hyphenPattern = new RegExp(`(?:/|\\$)gsd-(${commandGroup})(?=[^A-Za-z0-9_-]|$)`, 'g');
|
|
@@ -754,7 +848,8 @@ function buildKimiAgentArtifacts({ rootAgent = '', subagents = [], requestedSuba
|
|
|
754
848
|
/**
|
|
755
849
|
* Apply Antigravity-specific content conversion — path replacement + command name conversion.
|
|
756
850
|
* Path mappings depend on install mode:
|
|
757
|
-
* Global: ~/.claude/ → ~/.gemini/
|
|
851
|
+
* Global: ~/.claude/skills/ → ~/.gemini/config/skills/ (#3738),
|
|
852
|
+
* ~/.claude/ → ~/.gemini/antigravity/, ./.claude/ → ./.agents/
|
|
758
853
|
* Local: ~/.claude/ → .agents/, ./.claude/ → ./.agents/
|
|
759
854
|
* Applied to ALL Antigravity content (skills, agents, engine files).
|
|
760
855
|
* @param {string} content - Source content to convert
|
|
@@ -763,6 +858,18 @@ function buildKimiAgentArtifacts({ rootAgent = '', subagents = [], requestedSuba
|
|
|
763
858
|
function convertClaudeToAntigravityContent(content, isGlobal = false) {
|
|
764
859
|
let c = content;
|
|
765
860
|
if (isGlobal) {
|
|
861
|
+
// #3738: global skills install under ~/.gemini/config/skills (the dir AGY
|
|
862
|
+
// scans for global discovery), so skills-path references must divert there
|
|
863
|
+
// — BEFORE the configHome rewrite below, which is correct for gsd-core
|
|
864
|
+
// runtime-file references (settings, workflows, VERSION) but wrong for the
|
|
865
|
+
// skills dir itself.
|
|
866
|
+
c = c.replace(/\$HOME\/\.claude\/skills\//g, '$HOME/.gemini/config/skills/');
|
|
867
|
+
c = c.replace(/~\/\.claude\/skills\//g, '~/.gemini/config/skills/');
|
|
868
|
+
// Bare skills form (no trailing slash) — must also precede the generic
|
|
869
|
+
// slash rule, which would otherwise divert it to the retired configHome
|
|
870
|
+
// path ($HOME/.gemini/antigravity/skills).
|
|
871
|
+
c = c.replace(/\$HOME\/\.claude\/skills\b/g, '$HOME/.gemini/config/skills');
|
|
872
|
+
c = c.replace(/~\/\.claude\/skills\b/g, '~/.gemini/config/skills');
|
|
766
873
|
c = c.replace(/\$HOME\/\.claude\//g, '$HOME/.gemini/antigravity/');
|
|
767
874
|
c = c.replace(/~\/\.claude\//g, '~/.gemini/antigravity/');
|
|
768
875
|
// Bare form (no trailing slash) — must come after slash form to avoid double-replace
|
|
@@ -1533,7 +1640,7 @@ Typed mapping (agent_type-capable schema only):
|
|
|
1533
1640
|
to \`spawn_agent\` when the runtime/tool supports it. Omit missing, empty,
|
|
1534
1641
|
inherited, or unsupported values; do not invent one-off effort literals in
|
|
1535
1642
|
workflow prose.
|
|
1536
|
-
- \`fork_context: false\` by default — GSD agents load their own context via \`<
|
|
1643
|
+
- \`fork_context: false\` by default — GSD agents load their own context via \`<required_reading>\` blocks
|
|
1537
1644
|
- \`task_name\` — required by the collaboration schema; provide a descriptive name for each spawned task
|
|
1538
1645
|
- \`fork_turns\` — optional parameter controlling turn-forking depth; coexists with \`fork_context\` (not a replacement)
|
|
1539
1646
|
- \`Task(isolation="worktree")\` / \`Agent(isolation="worktree")\` → no direct \`spawn_agent\` mapping,
|
|
@@ -1609,7 +1716,26 @@ function neutralizeAgentReferences(content, instructionFile) {
|
|
|
1609
1716
|
c = c.replace(/Do NOT load full `AGENTS\.md` files[^\n]*/g, '');
|
|
1610
1717
|
return c;
|
|
1611
1718
|
}
|
|
1612
|
-
|
|
1719
|
+
/**
|
|
1720
|
+
* Render one frontmatter `key: value` line whose value came from user config.
|
|
1721
|
+
*
|
|
1722
|
+
* #3706: both `model:` and `variant:` interpolate a value read from
|
|
1723
|
+
* `.planning/config.json` / `~/.gsd/defaults.json`. Raw interpolation lets a value
|
|
1724
|
+
* containing a newline inject additional TOP-LEVEL frontmatter keys into the
|
|
1725
|
+
* generated agent file — proven by execution during the #3705 security review
|
|
1726
|
+
* (`"sonnet\ntools: [\"*\"]\npermission: bypass"` produced two extra keys).
|
|
1727
|
+
*
|
|
1728
|
+
* That sink predates #3706, but this change adds a SECOND write to it, so it is
|
|
1729
|
+
* closed here rather than doubled. Both the decision and the escaping live in
|
|
1730
|
+
* frontmatter.cts so there is exactly one set of YAML scalar rules; a value that
|
|
1731
|
+
* round-trips bare is still emitted bare, so generated files do not churn.
|
|
1732
|
+
*/
|
|
1733
|
+
function frontmatterScalar(key, value) {
|
|
1734
|
+
return frontmatterModule.agentScalarNeedsDoubleQuoting(value)
|
|
1735
|
+
? `${key} "${frontmatterModule.escapeDoubleQuotedScalar(value)}"`
|
|
1736
|
+
: `${key} ${value}`;
|
|
1737
|
+
}
|
|
1738
|
+
function convertClaudeToOpencodeFrontmatter(content, { isAgent = false, modelOverride = null, variant = null } = {}) {
|
|
1613
1739
|
// Replace tool name references in content (applies to all files)
|
|
1614
1740
|
let convertedContent = content;
|
|
1615
1741
|
convertedContent = convertedContent.replace(/\bAskUserQuestion\b/g, 'question');
|
|
@@ -1734,7 +1860,23 @@ function convertClaudeToOpencodeFrontmatter(content, { isAgent = false, modelOve
|
|
|
1734
1860
|
// respected on OpenCode (which uses static agent frontmatter, not inline
|
|
1735
1861
|
// Task() model parameters). See #2256.
|
|
1736
1862
|
if (modelOverride) {
|
|
1737
|
-
newLines.push(
|
|
1863
|
+
newLines.push(frontmatterScalar('model:', modelOverride));
|
|
1864
|
+
}
|
|
1865
|
+
// #3706: deliver the RESOLVED reasoning effort to the spawned subagent.
|
|
1866
|
+
// `query resolve-model` reported an effort that never reached OpenCode, so
|
|
1867
|
+
// every subagent ran at whatever opencode.jsonc defaults the model to — for
|
|
1868
|
+
// a custom provider commonly the most expensive setting.
|
|
1869
|
+
//
|
|
1870
|
+
// OpenCode ONLY: `EFFORT_ARGV` declares surfaces for claude/opencode/codex
|
|
1871
|
+
// and NO kilo entry, so the Kilo converter below deliberately does not emit
|
|
1872
|
+
// this. Unlike the model side — where #2794 J8 requires kilo and opencode to
|
|
1873
|
+
// resolve identically — there is no kilo effort surface to render into, and
|
|
1874
|
+
// inventing one would emit a key that runtime never documented.
|
|
1875
|
+
//
|
|
1876
|
+
// Omitted entirely when absent, per #1156's rule for `model: inherit`:
|
|
1877
|
+
// never an empty or sentinel value, let the runtime use its own default.
|
|
1878
|
+
if (variant) {
|
|
1879
|
+
newLines.push(frontmatterScalar('variant:', variant));
|
|
1738
1880
|
}
|
|
1739
1881
|
}
|
|
1740
1882
|
// For commands: add tools object if we had allowed-tools or tools
|
|
@@ -1906,7 +2048,7 @@ function convertClaudeToKiloFrontmatter(content, { isAgent = false, modelOverrid
|
|
|
1906
2048
|
// model emission exactly (#2093 UPGRADE 2 / ADR-1239; Kilo is an OpenCode
|
|
1907
2049
|
// fork with the same static-frontmatter model constraint). See #2256.
|
|
1908
2050
|
if (modelOverride) {
|
|
1909
|
-
newLines.push(
|
|
2051
|
+
newLines.push(frontmatterScalar('model:', modelOverride));
|
|
1910
2052
|
}
|
|
1911
2053
|
newLines.push(...buildKiloAgentPermissionBlock(agentTools));
|
|
1912
2054
|
}
|
|
@@ -2188,6 +2330,106 @@ function convertClaudeAgentToQwenAgent(content) {
|
|
|
2188
2330
|
fm += '---';
|
|
2189
2331
|
return `${fm}\n${body}`;
|
|
2190
2332
|
}
|
|
2333
|
+
/**
|
|
2334
|
+
* Convert a Claude Code agent .md for ZCode (#3384).
|
|
2335
|
+
*
|
|
2336
|
+
* ZCode is Claude-shaped (same frontmatter, same named-dispatch subagents), so
|
|
2337
|
+
* the file is preserved verbatim EXCEPT the `tools:` grant list: ZCode's
|
|
2338
|
+
* dispatcher treats every `mcp__<server>__*` entry as a REQUIRED MCP server and
|
|
2339
|
+
* hard-fails the subagent spawn (CONFIGURATION_ERROR: "Required MCP server is
|
|
2340
|
+
* not connected") whenever it is not connected, whereas Claude Code treats the
|
|
2341
|
+
* same entries as an optional allowlist. The `mcp__*` entries are stripped at
|
|
2342
|
+
* install time — the same exclusion Kimi's converter applies via
|
|
2343
|
+
* convertKimiToolName — so subagent spawns succeed with zero MCP servers
|
|
2344
|
+
* configured; connected servers' tools remain reachable (auto-discovered by the
|
|
2345
|
+
* host, not granted by frontmatter).
|
|
2346
|
+
*
|
|
2347
|
+
* Line-surgical by design: ONLY `tools:` lines inside the frontmatter are
|
|
2348
|
+
* touched, so every other byte (description, color, commented-out blocks, the
|
|
2349
|
+
* body) survives identically. Handles both shapes GSD emits — the inline comma
|
|
2350
|
+
* list (`tools: A, B, C`) and the YAML block list (`tools:` + `- A` items).
|
|
2351
|
+
* An agent whose filtered grant list becomes empty (every grant was `mcp__*`)
|
|
2352
|
+
* drops the `tools:` key entirely: an absent key inherits the full toolkit,
|
|
2353
|
+
* which is the degrade-gracefully outcome, never a toolless subagent.
|
|
2354
|
+
*
|
|
2355
|
+
* Byte-identical for an agent with no `mcp__*` grants (the common case) and
|
|
2356
|
+
* for an agent with no frontmatter at all.
|
|
2357
|
+
*/
|
|
2358
|
+
function convertClaudeAgentToZcodeAgent(content) {
|
|
2359
|
+
// Fast path: no MCP grant token anywhere means nothing to strip. (A body
|
|
2360
|
+
// mention alone is not a grant — the line scan below finds no tools-line
|
|
2361
|
+
// change and returns `content` unchanged anyway; this just skips the scan.)
|
|
2362
|
+
if (!content.includes('mcp__'))
|
|
2363
|
+
return content;
|
|
2364
|
+
const lines = content.split('\n');
|
|
2365
|
+
if (lines[0] !== '---')
|
|
2366
|
+
return content;
|
|
2367
|
+
let fmEnd = -1;
|
|
2368
|
+
for (let i = 1; i < lines.length; i++) {
|
|
2369
|
+
if (lines[i] === '---') {
|
|
2370
|
+
fmEnd = i;
|
|
2371
|
+
break;
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
if (fmEnd === -1)
|
|
2375
|
+
return content; // unterminated frontmatter — leave verbatim
|
|
2376
|
+
const out = [];
|
|
2377
|
+
let changed = false;
|
|
2378
|
+
let i = 1;
|
|
2379
|
+
while (i < fmEnd) {
|
|
2380
|
+
const line = lines[i];
|
|
2381
|
+
const inlineTools = /^tools:[ \t]*(.+)$/.exec(line);
|
|
2382
|
+
if (inlineTools) {
|
|
2383
|
+
const grants = inlineTools[1].split(',').map((tool) => tool.trim()).filter((tool) => tool !== '');
|
|
2384
|
+
const kept = grants.filter((tool) => !tool.startsWith('mcp__'));
|
|
2385
|
+
if (kept.length === grants.length) {
|
|
2386
|
+
out.push(line); // no mcp__* grants — keep the line byte-identical
|
|
2387
|
+
}
|
|
2388
|
+
else if (kept.length > 0) {
|
|
2389
|
+
out.push(`tools: ${kept.join(', ')}`);
|
|
2390
|
+
changed = true;
|
|
2391
|
+
}
|
|
2392
|
+
else {
|
|
2393
|
+
changed = true; // every grant was mcp__*: drop the tools key entirely
|
|
2394
|
+
}
|
|
2395
|
+
i++;
|
|
2396
|
+
continue;
|
|
2397
|
+
}
|
|
2398
|
+
if (/^tools:[ \t]*$/.test(line)) {
|
|
2399
|
+
// Block-list form: collect the following `- item` lines.
|
|
2400
|
+
const items = [];
|
|
2401
|
+
let j = i + 1;
|
|
2402
|
+
while (j < fmEnd && /^([ \t]*)-[ \t]*(\S.*)$/.test(lines[j])) {
|
|
2403
|
+
items.push(lines[j]);
|
|
2404
|
+
j++;
|
|
2405
|
+
}
|
|
2406
|
+
const kept = items.filter((item) => {
|
|
2407
|
+
const name = /^([ \t]*)-[ \t]*(\S.*)$/.exec(item)[2].trim();
|
|
2408
|
+
return !name.startsWith('mcp__');
|
|
2409
|
+
});
|
|
2410
|
+
if (kept.length !== items.length) {
|
|
2411
|
+
changed = true;
|
|
2412
|
+
if (kept.length > 0) {
|
|
2413
|
+
out.push(line);
|
|
2414
|
+
out.push(...kept);
|
|
2415
|
+
} // else: drop the tools key and all its items
|
|
2416
|
+
}
|
|
2417
|
+
else {
|
|
2418
|
+
out.push(line, ...items);
|
|
2419
|
+
}
|
|
2420
|
+
i = j;
|
|
2421
|
+
continue;
|
|
2422
|
+
}
|
|
2423
|
+
out.push(line);
|
|
2424
|
+
i++;
|
|
2425
|
+
}
|
|
2426
|
+
if (!changed)
|
|
2427
|
+
return content;
|
|
2428
|
+
// Opening delimiter + transformed frontmatter + closing delimiter + body.
|
|
2429
|
+
out.unshift(lines[0]);
|
|
2430
|
+
out.push(...lines.slice(fmEnd));
|
|
2431
|
+
return out.join('\n');
|
|
2432
|
+
}
|
|
2191
2433
|
function convertClaudeAgentToCodebuddyAgent(content) {
|
|
2192
2434
|
const converted = convertClaudeToCodebuddyMarkdown(content);
|
|
2193
2435
|
const { frontmatter, body } = extractFrontmatterAndBody(converted);
|
|
@@ -2208,6 +2450,48 @@ function convertClaudeAgentToClineAgent(content) {
|
|
|
2208
2450
|
const cleanFrontmatter = `---\nname: ${yamlIdentifier(name)}\ndescription: ${yamlQuote(toSingleLine(description))}\n---`;
|
|
2209
2451
|
return `${cleanFrontmatter}\n${body}`;
|
|
2210
2452
|
}
|
|
2453
|
+
/**
|
|
2454
|
+
* Apply a runtime's descriptor-declared `hostBehaviors.brandingRewrites` to an
|
|
2455
|
+
* agent body — the three literal-substring replaces the inline agent loop
|
|
2456
|
+
* (bin/install.js) previously hardcoded per-branding-runtime (qwen/hermes):
|
|
2457
|
+
* CLAUDE.md -> brandingRewrites['CLAUDE.md']
|
|
2458
|
+
* Claude Code -> brandingRewrites['Claude Code'] (word-boundary, \bClaude Code\b)
|
|
2459
|
+
* .claude/ -> brandingRewrites['.claude/']
|
|
2460
|
+
*
|
|
2461
|
+
* Data-driven (#2875 Part 2 / J10): reads the rewrite table from the
|
|
2462
|
+
* runtime's OWN descriptor rather than hardcoding any runtime's strings, so a
|
|
2463
|
+
* runtime declaring a different `brandingRewrites` table gets its own
|
|
2464
|
+
* rewrites applied automatically. A runtime with no `brandingRewrites`
|
|
2465
|
+
* declared returns `content` unchanged (no rewrite table to apply).
|
|
2466
|
+
*
|
|
2467
|
+
* Byte-identical to the inline loop's `else if (_hostBehaviors(runtime).brandingRewrites)`
|
|
2468
|
+
* branch, including plain (non-word-boundary) `.replace(/\bClaude Code\b/g, ...)`
|
|
2469
|
+
* semantics — J9.
|
|
2470
|
+
*/
|
|
2471
|
+
function applyAgentBrandingRewrites(content, runtime) {
|
|
2472
|
+
const _b = _hostBehaviors(runtime).brandingRewrites;
|
|
2473
|
+
if (!_b)
|
|
2474
|
+
return content;
|
|
2475
|
+
let converted = content;
|
|
2476
|
+
if (_b['CLAUDE.md'])
|
|
2477
|
+
converted = converted.replace(/CLAUDE\.md/g, _b['CLAUDE.md']);
|
|
2478
|
+
if (_b['Claude Code'])
|
|
2479
|
+
converted = converted.replace(/\bClaude Code\b/g, _b['Claude Code']);
|
|
2480
|
+
if (_b['.claude/'])
|
|
2481
|
+
converted = converted.replace(/\.claude\//g, _b['.claude/']);
|
|
2482
|
+
return converted;
|
|
2483
|
+
}
|
|
2484
|
+
/**
|
|
2485
|
+
* Named branding converter for Hermes agents (#2875 Part 2 / J9-J10).
|
|
2486
|
+
* `convertedAgentsKind` dispatches converters by exported name, so a named
|
|
2487
|
+
* export is required even though the transform itself is fully generic
|
|
2488
|
+
* (`applyAgentBrandingRewrites`) — resolved from
|
|
2489
|
+
* `capabilities/hermes/capability.json`'s `hostBehaviors.brandingRewrites`,
|
|
2490
|
+
* never hardcoded here.
|
|
2491
|
+
*/
|
|
2492
|
+
function convertClaudeAgentToHermesAgent(content) {
|
|
2493
|
+
return applyAgentBrandingRewrites(content, 'hermes');
|
|
2494
|
+
}
|
|
2211
2495
|
/**
|
|
2212
2496
|
* Convert Claude Code agent markdown to Codex agent format.
|
|
2213
2497
|
* Applies base markdown conversions, then adds a <codex_agent_role> header
|
|
@@ -2320,20 +2604,151 @@ function computePathPrefix({ isGlobal, isOpencode, isWindowsHost: _isWindowsHost
|
|
|
2320
2604
|
const NON_CLAUDE_RUNTIMES = Object.keys(capabilityRegistry.runtimes)
|
|
2321
2605
|
.filter((id) => id !== 'claude')
|
|
2322
2606
|
.sort();
|
|
2607
|
+
/**
|
|
2608
|
+
* #2652: The isolation a runtime can actually negotiate at dispatch time,
|
|
2609
|
+
* resolved from the registry exactly as `gsd_run query dispatch-isolation`
|
|
2610
|
+
* resolves it at runtime (`routeDispatchIsolation`, gsd-core/bin/gsd-tools.cjs):
|
|
2611
|
+
* the declared value must be in the closed vocabulary, a `harness-worktree`
|
|
2612
|
+
* host must also declare the flag the scheduler passes, and an
|
|
2613
|
+
* `orchestrator-worktree` host must carry a descriptor that resolves. Anything
|
|
2614
|
+
* else — unknown runtime, `undocumented`, out-of-vocabulary, a throw — is
|
|
2615
|
+
* `none` (ADR-1239, "Fail-closed").
|
|
2616
|
+
*
|
|
2617
|
+
* Install time cannot know the worktree path a future dispatch will target, so
|
|
2618
|
+
* the descriptor is probed with a placeholder; `resolveOrchestratorExec` fails
|
|
2619
|
+
* only on descriptor shape, never on a well-formed target's value.
|
|
2620
|
+
*
|
|
2621
|
+
* @private — exported as `_negotiatedDispatchIsolation` for tests.
|
|
2622
|
+
*/
|
|
2623
|
+
function _negotiatedDispatchIsolation(runtime) {
|
|
2624
|
+
try {
|
|
2625
|
+
const runtimeEntry = capabilityRegistry?.runtimes?.[runtime] ?? null;
|
|
2626
|
+
const declared = runtimeEntry?.runtime?.hostIntegration?.dispatch?.isolation ?? null;
|
|
2627
|
+
if (declared === 'harness-worktree') {
|
|
2628
|
+
const declaredFlag = runtimeEntry?.runtime?.harnessIsolationFlag ?? null;
|
|
2629
|
+
return typeof declaredFlag === 'string' && declaredFlag.length > 0
|
|
2630
|
+
? 'harness-worktree'
|
|
2631
|
+
: 'none';
|
|
2632
|
+
}
|
|
2633
|
+
if (declared === 'orchestrator-worktree') {
|
|
2634
|
+
return hostIntegration.resolveOrchestratorExec(runtimeEntry?.runtime?.orchestratorExec, '/gsd-orchestrator-worktree-probe').ok
|
|
2635
|
+
? 'orchestrator-worktree'
|
|
2636
|
+
: 'none';
|
|
2637
|
+
}
|
|
2638
|
+
return 'none';
|
|
2639
|
+
}
|
|
2640
|
+
catch {
|
|
2641
|
+
return 'none';
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2323
2644
|
/**
|
|
2324
2645
|
* #1521: Every non-Claude runtime resolves its own runtime identity from a
|
|
2325
|
-
* runtime-neutral config
|
|
2326
|
-
*
|
|
2327
|
-
*
|
|
2328
|
-
*
|
|
2646
|
+
* runtime-neutral config. Stamped into the emitted workflow runtime-resolution
|
|
2647
|
+
* blocks. (Generalizes the Codex-only #1515 fix.)
|
|
2648
|
+
*
|
|
2649
|
+
* #1521 also stamped `workflow.use_worktrees` to default false for every
|
|
2650
|
+
* non-Claude runtime, because GSD's worktree isolation was Claude Code's
|
|
2651
|
+
* `isolation="worktree"` spawn parameter and no other runtime honored it.
|
|
2652
|
+
* #2584 removed that premise: isolation is now a negotiated capability
|
|
2653
|
+
* (`dispatch.isolation`), and Cursor declares `harness-worktree` while Codex,
|
|
2654
|
+
* OpenCode, Kimi and Kimi Code declare `orchestrator-worktree`. Stamping the
|
|
2655
|
+
* false default for those hosts resolved `USE_WORKTREES=false` before
|
|
2656
|
+
* `dispatch.isolation` was ever consulted, so a runtime that declares worktree
|
|
2657
|
+
* support still got `ISOLATION=none` — judged by its name after all, which is
|
|
2658
|
+
* the defect #2652 exists to remove. The stamp is therefore scoped to the
|
|
2659
|
+
* runtimes whose negotiated isolation really is `none`, where the default it
|
|
2660
|
+
* writes is the outcome the resolver would reach anyway.
|
|
2329
2661
|
*
|
|
2330
2662
|
* @private — exported as `_stampNonClaudeRuntimeDefaults` for tests.
|
|
2331
2663
|
*/
|
|
2332
2664
|
function _stampNonClaudeRuntimeDefaults(content, runtime) {
|
|
2333
|
-
|
|
2665
|
+
if (_negotiatedDispatchIsolation(runtime) === 'none') {
|
|
2666
|
+
content = content.replace(/config-get workflow\.use_worktrees --raw 2>\/dev\/null \|\| echo "true"/g, 'config-get workflow.use_worktrees --default false --raw 2>/dev/null || echo "false"');
|
|
2667
|
+
}
|
|
2334
2668
|
content = content.replace(/config-get runtime --default claude --raw 2>\/dev\/null \|\| echo "claude"/g, `config-get runtime --default ${runtime} --raw 2>/dev/null || echo "${runtime}"`);
|
|
2335
2669
|
return content;
|
|
2336
2670
|
}
|
|
2671
|
+
/**
|
|
2672
|
+
* #3544 (extending #3133's fix): restore `@$HOME<suffix>` `@`-file-reference
|
|
2673
|
+
* lines back to their tilde equivalent (`@~<suffix>`) in Claude-emitted
|
|
2674
|
+
* content whose pathPrefix is the `$HOME` form. This is a NARROW,
|
|
2675
|
+
* context-sensitive correction layered on top of the blanket `~/.claude/` /
|
|
2676
|
+
* `$HOME/.claude/` -> pathPrefix substitution every Claude emit path
|
|
2677
|
+
* applies: that blanket substitution MUST keep emitting `$HOME` for global
|
|
2678
|
+
* installs — shell commands embedded in workflow/command bodies (e.g.
|
|
2679
|
+
* `node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs"`) need it, since `~` does
|
|
2680
|
+
* not expand inside double-quoted shell strings (#1284). But Claude Code's
|
|
2681
|
+
* own `@`-import resolver does the opposite: it documents `~` expansion and
|
|
2682
|
+
* does NOT expand `$HOME`. That is not merely undocumented — a controlled
|
|
2683
|
+
* `/context` measurement showed an `@$HOME/…` import loading nothing (see
|
|
2684
|
+
* .gsd/bug/fix-3544-home-expansion-spec-tree/10-diagnosis.md's ADDENDUM). No
|
|
2685
|
+
* automated test can verify *resolution* inside a live Claude Code session
|
|
2686
|
+
* (nothing in CI can spawn one and read `/context`); every test here — unit
|
|
2687
|
+
* and spawned-installer alike — verifies only the emitted STRING takes the
|
|
2688
|
+
* `~` form Claude Code documents as expanding. A single pathPrefix string
|
|
2689
|
+
* cannot satisfy both the shell and the `@`-import consumer, so this runs as
|
|
2690
|
+
* a second, `@`-anchored pass AFTER the blanket substitution.
|
|
2691
|
+
*
|
|
2692
|
+
* #3133 first applied this restore inline in `_applyRuntimeRewrites`'s
|
|
2693
|
+
* `case 'claude'` below (the skill/command staging pipeline). #3544 found
|
|
2694
|
+
* the identical defect in bin/install.js's `copyWithPathReplacement` — the
|
|
2695
|
+
* `gsd-core/` spec-tree emit path, which never had the restore step, so
|
|
2696
|
+
* every `@~/.claude/gsd-core/…` include in a global install's workflows/
|
|
2697
|
+
* references tree silently resolved to nothing (54 includes across 22 files
|
|
2698
|
+
* on a live install, per the diagnosis). Both call sites now share this one
|
|
2699
|
+
* implementation instead of drifting independently (DEFECT.GENERATIVE-FIX).
|
|
2700
|
+
*
|
|
2701
|
+
* No-op unless `pathPrefix` is the `$HOME` form — local installs already
|
|
2702
|
+
* bake an absolute, `@`-resolvable pathPrefix and are unaffected, as are
|
|
2703
|
+
* every non-Claude runtime (never called for them).
|
|
2704
|
+
*
|
|
2705
|
+
* #3544 review (2nd pass): the first cut of this function hardcoded the
|
|
2706
|
+
* literal `.claude/` segment, so it silently no-opped for any global install
|
|
2707
|
+
* under a non-default `--config-dir` (e.g. `~/.claude-work`) — reproducing
|
|
2708
|
+
* the exact defect #3544 fixes, just one directory name later. This ALSO
|
|
2709
|
+
* corrects the same latent gap in #3133's original path, since both call
|
|
2710
|
+
* sites share this one implementation. Fixed by deriving the rewrite from
|
|
2711
|
+
* `pathPrefix` itself rather than a hardcoded directory name: the tilde
|
|
2712
|
+
* equivalent of any `$HOME`-form prefix is `'~' + pathPrefix.slice(5)`
|
|
2713
|
+
* (`'$HOME'.length === 5`), so the transform generalizes to any config-dir
|
|
2714
|
+
* name with no runtime-specific literal.
|
|
2715
|
+
*
|
|
2716
|
+
* #3544 review (2nd pass), quote-awareness: the anchor is a negative
|
|
2717
|
+
* lookbehind for a preceding quote character, NOT a line-start anchor —
|
|
2718
|
+
* Claude Code documents `@`-references as valid "anywhere in your
|
|
2719
|
+
* CLAUDE.md" (e.g. `See @README for project overview`), so anchoring to
|
|
2720
|
+
* line-start would miss a legitimate mid-line reference. The lookbehind
|
|
2721
|
+
* instead guards the one demonstrated false-positive: a quoted shell string
|
|
2722
|
+
* like `echo "@$HOME/.claude/x"`, where rewriting `$HOME` to `~` inside
|
|
2723
|
+
* double quotes reintroduces the #1284 failure mode (`~` does not expand in
|
|
2724
|
+
* double-quoted shell). Deliberately NOT fenced-code-block aware (unlike
|
|
2725
|
+
* `resolveSpecRootReference`'s `scanFencedBlocks` use above): this pass
|
|
2726
|
+
* targets genuine `@`-import lines and inline shell references across the
|
|
2727
|
+
* whole emitted corpus, and today there are zero occurrences anywhere in the
|
|
2728
|
+
* tree of an `@$HOME<suffix>` sequence inside a fenced code block (the
|
|
2729
|
+
* quote-guard already closes the one reachable false-positive class).
|
|
2730
|
+
* Layering `scanFencedBlocks` on top would roughly double this function's
|
|
2731
|
+
* size to guard an undemonstrated case — the opposite of the brief's
|
|
2732
|
+
* "simpler, not more complex" direction. If a fenced example ever needs this
|
|
2733
|
+
* literal sequence, add fence-awareness then, with a regression test proving
|
|
2734
|
+
* the fence is real.
|
|
2735
|
+
*
|
|
2736
|
+
* @private — exported as `_restoreClaudeGlobalAtRefTilde` for tests and for
|
|
2737
|
+
* bin/install.js's `copyWithPathReplacement`.
|
|
2738
|
+
*/
|
|
2739
|
+
function restoreClaudeGlobalAtRefTilde(content, pathPrefix) {
|
|
2740
|
+
if (typeof pathPrefix !== 'string' || !pathPrefix.startsWith('$HOME'))
|
|
2741
|
+
return content;
|
|
2742
|
+
const tildeEquivalent = '~' + pathPrefix.slice('$HOME'.length);
|
|
2743
|
+
const atRefRe = new RegExp(`(?<!["'])@${(0, pattern_cjs_1.escapeRegex)(pathPrefix)}`, 'g');
|
|
2744
|
+
// Function replacement, not a string. A string replacement treats `$&` and the
|
|
2745
|
+
// backtick-dollar form in the SUBSTITUTION as special patterns, so a --config-dir
|
|
2746
|
+
// containing either corrupts output: `$HOME/.cl$&ude/` yielded
|
|
2747
|
+
// `@~/.cl@$HOME/.cl$&ude/ude/x`, and the backtick form silently DROPPED text.
|
|
2748
|
+
// Pre-existing, and #3719 adds a THIRD call site to this sink — which is how the
|
|
2749
|
+
// previous two came to share the defect in the first place.
|
|
2750
|
+
return content.replace(atRefRe, () => `@${tildeEquivalent}`);
|
|
2751
|
+
}
|
|
2337
2752
|
/**
|
|
2338
2753
|
* Apply the per-runtime rewrite table to a single content string.
|
|
2339
2754
|
* Relocated from bin/install.js `_applyRuntimeRewrites`.
|
|
@@ -2410,7 +2825,7 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
2410
2825
|
// #2097: dot-dir self-references (~/.augment/…) → resolved prefix,
|
|
2411
2826
|
// dirName-derived (no runtime literal). getDirName('augment') resolves
|
|
2412
2827
|
// to '.augment', so this is byte-identical to the prior hardcoded regexes.
|
|
2413
|
-
const _dd =
|
|
2828
|
+
const _dd = (0, pattern_cjs_1.escapeRegex)(dirName);
|
|
2414
2829
|
content = content.replace(new RegExp('~/' + _dd + '/', 'g'), pathPrefix);
|
|
2415
2830
|
content = content.replace(new RegExp('\\$HOME/' + _dd + '/', 'g'), pathPrefix);
|
|
2416
2831
|
content = content.replace(new RegExp('~/' + _dd + '(?![\\w-])', 'g'), normalizedPathPrefix);
|
|
@@ -2428,7 +2843,7 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
2428
2843
|
// #2094: descriptor-driven — dirName resolves to '.trae' via
|
|
2429
2844
|
// getDirName()/localConfigDir, so this regex is built rather than
|
|
2430
2845
|
// hardcoded as `/~\/\.trae\//g` (byte-identical output for trae).
|
|
2431
|
-
content = content.replace(new RegExp('~/' +
|
|
2846
|
+
content = content.replace(new RegExp('~/' + (0, pattern_cjs_1.escapeRegex)(dirName) + '/', 'g'), pathPrefix);
|
|
2432
2847
|
content = processAttribution(content, attribution);
|
|
2433
2848
|
break;
|
|
2434
2849
|
case 'codebuddy':
|
|
@@ -2454,6 +2869,10 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
2454
2869
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
2455
2870
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
2456
2871
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
2872
|
+
// #3133 / #3544: restore @-file-reference lines to the tilde form
|
|
2873
|
+
// Claude actually expands — see restoreClaudeGlobalAtRefTilde's doc
|
|
2874
|
+
// comment above for why this must be a separate, @-anchored pass.
|
|
2875
|
+
content = restoreClaudeGlobalAtRefTilde(content, pathPrefix);
|
|
2457
2876
|
content = processAttribution(content, attribution);
|
|
2458
2877
|
break;
|
|
2459
2878
|
// Descriptor-driven brand literals (ADR-1239 / #2092): the qwen/hermes
|
|
@@ -2541,18 +2960,18 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
2541
2960
|
* @param attribution Co-Authored-By value (string | null | undefined)
|
|
2542
2961
|
*/
|
|
2543
2962
|
function applyRuntimeContentRewritesInPlace(stagedDir, runtime, pathPrefix, isGlobal = false, attribution = undefined) {
|
|
2544
|
-
if (!
|
|
2963
|
+
if (!installFs().existsSync(stagedDir))
|
|
2545
2964
|
return;
|
|
2546
2965
|
const walkAndRewrite = (dir) => {
|
|
2547
|
-
for (const entry of
|
|
2966
|
+
for (const entry of installFs().readdirSync(dir, { withFileTypes: true })) {
|
|
2548
2967
|
const fullPath = node_path_1.default.join(dir, entry.name);
|
|
2549
2968
|
if (entry.isDirectory()) {
|
|
2550
2969
|
walkAndRewrite(fullPath);
|
|
2551
2970
|
}
|
|
2552
2971
|
else if (entry.name.endsWith('.md')) {
|
|
2553
|
-
let content =
|
|
2972
|
+
let content = installFs().readFileSync(fullPath, 'utf8');
|
|
2554
2973
|
content = _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal, attribution);
|
|
2555
|
-
|
|
2974
|
+
installFs().writeFileSync(fullPath, content);
|
|
2556
2975
|
}
|
|
2557
2976
|
}
|
|
2558
2977
|
};
|
|
@@ -2577,14 +2996,14 @@ function applyRuntimeContentRewritesInPlace(stagedDir, runtime, pathPrefix, isGl
|
|
|
2577
2996
|
* @returns {string} path to the temp dir (caller is responsible for cleanup)
|
|
2578
2997
|
*/
|
|
2579
2998
|
function applyRuntimeContentRewritesForCommandsInPlace(stagedDir, runtime, pathPrefix, isGlobal = false, attribution = undefined) {
|
|
2580
|
-
if (!
|
|
2999
|
+
if (!installFs().existsSync(stagedDir))
|
|
2581
3000
|
return stagedDir;
|
|
2582
|
-
const tempDir =
|
|
3001
|
+
const tempDir = mkInstallTempDir('gsd-cmd-rewrites-');
|
|
2583
3002
|
try {
|
|
2584
|
-
for (const entry of
|
|
3003
|
+
for (const entry of installFs().readdirSync(stagedDir, { withFileTypes: true })) {
|
|
2585
3004
|
if (!entry.isFile() || !entry.name.endsWith('.md'))
|
|
2586
3005
|
continue;
|
|
2587
|
-
let content =
|
|
3006
|
+
let content = installFs().readFileSync(node_path_1.default.join(stagedDir, entry.name), 'utf8');
|
|
2588
3007
|
content = _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal, attribution);
|
|
2589
3008
|
// #2097 (ADR-1239): descriptor-driven — commandBodyConverter name comes
|
|
2590
3009
|
// from runtime.hostBehaviors instead of a hardcoded runtime-name branch.
|
|
@@ -2592,18 +3011,52 @@ function applyRuntimeContentRewritesForCommandsInPlace(stagedDir, runtime, pathP
|
|
|
2592
3011
|
if (_cmdConv && COMMAND_BODY_CONVERTERS[_cmdConv]) {
|
|
2593
3012
|
content = COMMAND_BODY_CONVERTERS[_cmdConv](content);
|
|
2594
3013
|
}
|
|
2595
|
-
|
|
3014
|
+
installFs().writeFileSync(node_path_1.default.join(tempDir, entry.name), content);
|
|
2596
3015
|
}
|
|
2597
3016
|
}
|
|
2598
3017
|
catch (err) {
|
|
2599
3018
|
try {
|
|
2600
|
-
|
|
3019
|
+
installFs().rmSync(tempDir, { recursive: true, force: true });
|
|
2601
3020
|
}
|
|
2602
3021
|
catch { /* best-effort */ }
|
|
2603
3022
|
throw err;
|
|
2604
3023
|
}
|
|
2605
3024
|
return tempDir;
|
|
2606
3025
|
}
|
|
3026
|
+
/**
|
|
3027
|
+
* #2873 (4b) — second pass over a staged skills directory, run strictly AFTER
|
|
3028
|
+
* `applyRuntimeContentRewritesInPlace`. That pass's `case 'claude':` branch
|
|
3029
|
+
* unconditionally rewrites any bare (non-`@`-prefixed) `~/.claude/` substring
|
|
3030
|
+
* in the body to the computed pathPrefix (`$HOME/.claude/` for a global
|
|
3031
|
+
* install) and restores ONLY the `@`-prefixed form back to `~`
|
|
3032
|
+
* (`@$HOME/.claude/` → `@~/.claude/`). `resolveSpecRootReference`'s
|
|
3033
|
+
* replacement text is deliberately imperative prose containing a literal,
|
|
3034
|
+
* non-`@`-prefixed `~/.claude/gsd-core/workflows/<stem>.md` — running it
|
|
3035
|
+
* BEFORE the pass above would let that literal tilde text get silently
|
|
3036
|
+
* mangled into the undocumented `$HOME/` form the design explicitly rejects.
|
|
3037
|
+
* Running it here, after, means it only ever sees the FINAL
|
|
3038
|
+
* `@~/.claude/gsd-core/workflows/<stem>.md` include line (which survives the
|
|
3039
|
+
* pass above intact via its own `@`-guarded restore).
|
|
3040
|
+
*/
|
|
3041
|
+
function applySpecRootReferenceToStagedSkills(stagedDir) {
|
|
3042
|
+
if (!installFs().existsSync(stagedDir))
|
|
3043
|
+
return;
|
|
3044
|
+
const walk = (dir) => {
|
|
3045
|
+
for (const entry of installFs().readdirSync(dir, { withFileTypes: true })) {
|
|
3046
|
+
const fullPath = node_path_1.default.join(dir, entry.name);
|
|
3047
|
+
if (entry.isDirectory()) {
|
|
3048
|
+
walk(fullPath);
|
|
3049
|
+
}
|
|
3050
|
+
else if (entry.name === 'SKILL.md') {
|
|
3051
|
+
const content = installFs().readFileSync(fullPath, 'utf8');
|
|
3052
|
+
const rewritten = resolveSpecRootReference(content);
|
|
3053
|
+
if (rewritten !== content)
|
|
3054
|
+
installFs().writeFileSync(fullPath, rewritten);
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
};
|
|
3058
|
+
walk(stagedDir);
|
|
3059
|
+
}
|
|
2607
3060
|
/**
|
|
2608
3061
|
* HIGH-LEVEL: In-place fs walk: rewrite all .md files under stagedDir for the given runtime.
|
|
2609
3062
|
*
|
|
@@ -2620,16 +3073,30 @@ function applyRuntimeContentRewritesForCommandsInPlace(stagedDir, runtime, pathP
|
|
|
2620
3073
|
*/
|
|
2621
3074
|
function rewriteStagedSkillBodies(stagedDir, opts) {
|
|
2622
3075
|
const { runtime, configDir, scope = 'global', homedir = () => node_os_1.default.homedir(), platform = process.platform, resolveAttribution, } = opts;
|
|
2623
|
-
if (!
|
|
3076
|
+
if (!installFs().existsSync(stagedDir))
|
|
2624
3077
|
return;
|
|
2625
3078
|
const resolvedTarget = (0, shell_command_projection_cjs_1.posixNormalize)(node_path_1.default.resolve(configDir));
|
|
2626
3079
|
const homeDir = (0, shell_command_projection_cjs_1.posixNormalize)(homedir());
|
|
2627
|
-
|
|
3080
|
+
// #2870: `scope` is defaulted to 'global' above, so it is never undefined
|
|
3081
|
+
// here, and every reachable caller passes 'global' | 'local' | undefined —
|
|
3082
|
+
// isGlobalScope's throw-on-out-of-union case is unreachable at this site.
|
|
3083
|
+
const isGlobal = (0, install_scope_cjs_1.isGlobalScope)(scope);
|
|
2628
3084
|
const isOpencode = false; // #2087: opencode installs via the combined-family engine path, never through the generic rewrite
|
|
2629
3085
|
const isWindowsHost = platform === 'win32';
|
|
2630
3086
|
const pathPrefix = computePathPrefix({ isGlobal, isOpencode, isWindowsHost, resolvedTarget, homeDir });
|
|
2631
3087
|
const attribution = resolveAttribution ? resolveAttribution(runtime) : undefined;
|
|
2632
3088
|
applyRuntimeContentRewritesInPlace(stagedDir, runtime, pathPrefix, isGlobal, attribution);
|
|
3089
|
+
// #2873 (4b): claude, global scope only — see
|
|
3090
|
+
// applySpecRootReferenceToStagedSkills's doc comment for why this MUST run
|
|
3091
|
+
// after the rewrite pass above, not before. `rewriteStagedSkillBodies` is
|
|
3092
|
+
// the skills-kind seam (`kind.kind === 'skills'`), so this never touches a
|
|
3093
|
+
// 'commands' or 'agents' kind body (rows 24/25 unaffected), and claude has
|
|
3094
|
+
// no skills-kind entry at local scope, so this is already structurally
|
|
3095
|
+
// scoped to global (row 23) — the explicit isGlobal check is defense-in-depth
|
|
3096
|
+
// against that descriptor wiring ever changing.
|
|
3097
|
+
if (runtime === 'claude' && isGlobal) {
|
|
3098
|
+
applySpecRootReferenceToStagedSkills(stagedDir);
|
|
3099
|
+
}
|
|
2633
3100
|
}
|
|
2634
3101
|
/**
|
|
2635
3102
|
* HIGH-LEVEL: Copy-to-temp then rewrite all .md files for the given runtime.
|
|
@@ -2649,11 +3116,14 @@ function rewriteStagedSkillBodies(stagedDir, opts) {
|
|
|
2649
3116
|
*/
|
|
2650
3117
|
function rewriteStagedCommandBodies(stagedDir, opts) {
|
|
2651
3118
|
const { runtime, configDir, scope = 'global', homedir = () => node_os_1.default.homedir(), platform = process.platform, resolveAttribution, } = opts;
|
|
2652
|
-
if (!
|
|
3119
|
+
if (!installFs().existsSync(stagedDir))
|
|
2653
3120
|
return stagedDir;
|
|
2654
3121
|
const resolvedTarget = (0, shell_command_projection_cjs_1.posixNormalize)(node_path_1.default.resolve(configDir));
|
|
2655
3122
|
const homeDir = (0, shell_command_projection_cjs_1.posixNormalize)(homedir());
|
|
2656
|
-
|
|
3123
|
+
// #2870: `scope` is defaulted to 'global' above, so it is never undefined
|
|
3124
|
+
// here, and every reachable caller passes 'global' | 'local' | undefined —
|
|
3125
|
+
// isGlobalScope's throw-on-out-of-union case is unreachable at this site.
|
|
3126
|
+
const isGlobal = (0, install_scope_cjs_1.isGlobalScope)(scope);
|
|
2657
3127
|
const isOpencode = false; // #2087: opencode installs via the combined-family engine path, never through the generic rewrite
|
|
2658
3128
|
const isWindowsHost = platform === 'win32';
|
|
2659
3129
|
const pathPrefix = computePathPrefix({ isGlobal, isOpencode, isWindowsHost, resolvedTarget, homeDir });
|
|
@@ -2709,11 +3179,165 @@ function applyAgentPathRewrites(content, runtime, pathPrefix) {
|
|
|
2709
3179
|
const normalizedPathPrefix = pathPrefix.replace(/\/$/, '');
|
|
2710
3180
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
2711
3181
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
2712
|
-
|
|
2713
|
-
|
|
3182
|
+
// #3719 review (MAJOR): a bare `\b` is satisfied by ANY non-word character,
|
|
3183
|
+
// including '-' — for a --config-dir whose name EXTENDS '.claude' (e.g.
|
|
3184
|
+
// '.claude-work'), this re-matched the '.claude' PREFIX of the emitted
|
|
3185
|
+
// '.claude-work' path and corrupted it to '.claude-work-work'. Guard with
|
|
3186
|
+
// the SAME negative-lookahead convention already used at
|
|
3187
|
+
// copyWithPathReplacement's call site (bin/install.js:7729-7730).
|
|
3188
|
+
content = content.replace(/~\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
3189
|
+
content = content.replace(/\$HOME\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
3190
|
+
// #3719: the THIRD emit path that needed this restore. #3133 added it to the
|
|
3191
|
+
// skill/command pipeline (`_applyRuntimeRewrites` case 'claude') and #3544 to
|
|
3192
|
+
// bin/install.js's spec-tree copy; the agents pipeline never got it, so every
|
|
3193
|
+
// `@~/.claude/...` include in a global Claude install shipped as `@$HOME/...`
|
|
3194
|
+
// and resolved to NOTHING (27 of 34 emitted agents, 103 lines).
|
|
3195
|
+
//
|
|
3196
|
+
// Guarded on `claude` for the same reason the sibling call site lives inside
|
|
3197
|
+
// `case 'claude'`: the helper self-guards only on the `$HOME` PREFIX, and every
|
|
3198
|
+
// runtime's global prefix is a `$HOME` form (`$HOME/.cursor/`, ...), so an
|
|
3199
|
+
// unguarded call would rewrite `@`-refs for runtimes whose resolver has no
|
|
3200
|
+
// documented `~` expansion at all.
|
|
3201
|
+
//
|
|
3202
|
+
// Passed the NORMALIZED prefix, not `pathPrefix`. The two word-boundary
|
|
3203
|
+
// replaces above emit the trailing-slash-free form, and the helper's regex is
|
|
3204
|
+
// anchored to the exact prefix string it is handed — so `restore(pathPrefix)`
|
|
3205
|
+
// fixes `@$HOME/.claude/x` and leaves a bare `@$HOME/.claude` broken. The
|
|
3206
|
+
// normalized form is a PREFIX of both, so one call covers both.
|
|
3207
|
+
if (runtime === 'claude') {
|
|
3208
|
+
content = restoreClaudeGlobalAtRefTilde(content, normalizedPathPrefix);
|
|
3209
|
+
}
|
|
2714
3210
|
return content;
|
|
2715
3211
|
}
|
|
2716
3212
|
// ── End rewrite engine ────────────────────────────────────────────────────────
|
|
3213
|
+
/**
|
|
3214
|
+
* Derive an agent's stem name from its source `.md` filename. Byte-identical
|
|
3215
|
+
* to the inline agent loop's `entry.name.replace(/\.md$/, '')` (bin/install.js)
|
|
3216
|
+
* — single-sourced here so the descriptor pipeline's per-agent resolution
|
|
3217
|
+
* context (`agentCtx.agentName`, ADR-1235 §1 / #2875 Part 2 row I3) can never
|
|
3218
|
+
* diverge from it. A filename with no trailing `.md` is returned unchanged
|
|
3219
|
+
* (the regex has nothing to match) — I3's boundary row.
|
|
3220
|
+
*/
|
|
3221
|
+
function deriveAgentName(fileName) {
|
|
3222
|
+
return fileName.replace(/\.md$/, '');
|
|
3223
|
+
}
|
|
3224
|
+
/**
|
|
3225
|
+
* #443 — Inject `effort: <value>` into YAML frontmatter of a Claude .md agent
|
|
3226
|
+
* file in a newline-agnostic way (LF and CRLF source files are both handled).
|
|
3227
|
+
* Relocated verbatim from bin/install.js (#2875 Part 2) — see
|
|
3228
|
+
* `applyAgentFrontmatterExtensions` below for the orchestration that calls it.
|
|
3229
|
+
*
|
|
3230
|
+
* The function:
|
|
3231
|
+
* - Detects the file's EOL (CRLF if the first `---` line ends with \r\n,
|
|
3232
|
+
* otherwise LF).
|
|
3233
|
+
* - Skips injection if an `effort:` key already exists in the frontmatter
|
|
3234
|
+
* (idempotent).
|
|
3235
|
+
* - Inserts `effort: <value>` immediately before the closing `---` delimiter,
|
|
3236
|
+
* using the same EOL as the surrounding frontmatter so the output file
|
|
3237
|
+
* stays EOL-consistent.
|
|
3238
|
+
* - Returns the original content unchanged when no YAML frontmatter is found.
|
|
3239
|
+
*/
|
|
3240
|
+
function injectEffortFrontmatter(content, effortValue) {
|
|
3241
|
+
const eol = /^---\r\n/.test(content) ? '\r\n' : '\n';
|
|
3242
|
+
const fmRe = /^---\r?\n([\s\S]*?)^---\r?$/m;
|
|
3243
|
+
const match = fmRe.exec(content);
|
|
3244
|
+
if (!match)
|
|
3245
|
+
return content; // no YAML frontmatter — leave unchanged
|
|
3246
|
+
const fmBody = match[1]; // content between the two `---` lines
|
|
3247
|
+
if (/^effort:/m.test(fmBody))
|
|
3248
|
+
return content;
|
|
3249
|
+
const openLen = 3 + eol.length; // "---" + eol
|
|
3250
|
+
const closingStart = match.index + openLen + fmBody.length;
|
|
3251
|
+
const before = content.slice(0, closingStart);
|
|
3252
|
+
const after = content.slice(closingStart);
|
|
3253
|
+
return `${before}effort: ${effortValue}${eol}${after}`;
|
|
3254
|
+
}
|
|
3255
|
+
/**
|
|
3256
|
+
* #767 — Inject `disallowedTools: <value>` into the YAML frontmatter of a
|
|
3257
|
+
* Claude .md agent. Mirrors injectEffortFrontmatter: idempotent (skips if
|
|
3258
|
+
* disallowedTools: already present), inserts immediately before the closing
|
|
3259
|
+
* `---`. Claude-only — never call for other runtimes, which break on unknown
|
|
3260
|
+
* frontmatter keys. Relocated verbatim from bin/install.js (#2875 Part 2).
|
|
3261
|
+
*/
|
|
3262
|
+
function injectDisallowedToolsFrontmatter(content, disallowedValue) {
|
|
3263
|
+
const eol = /^---\r\n/.test(content) ? '\r\n' : '\n';
|
|
3264
|
+
const fmRe = /^---\r?\n([\s\S]*?)^---\r?$/m;
|
|
3265
|
+
const match = fmRe.exec(content);
|
|
3266
|
+
if (!match)
|
|
3267
|
+
return content; // no YAML frontmatter — leave unchanged
|
|
3268
|
+
const fmBody = match[1]; // content between the two `---` lines
|
|
3269
|
+
if (/^disallowedTools:/m.test(fmBody))
|
|
3270
|
+
return content;
|
|
3271
|
+
const openLen = 3 + eol.length; // "---" + eol
|
|
3272
|
+
const closingStart = match.index + openLen + fmBody.length;
|
|
3273
|
+
const before = content.slice(0, closingStart);
|
|
3274
|
+
const after = content.slice(closingStart);
|
|
3275
|
+
return `${before}disallowedTools: ${disallowedValue}${eol}${after}`;
|
|
3276
|
+
}
|
|
3277
|
+
// #767 — Read-only verifier/auditor agents get a Claude-Code disallowedTools deny-list.
|
|
3278
|
+
// Group A (pure read-only) deny Write,Edit,MultiEdit. Group B report-writers Write one
|
|
3279
|
+
// output file so they deny only Edit,MultiEdit. gsd-nyquist-auditor is intentionally
|
|
3280
|
+
// excluded (it legitimately uses Write AND Edit to create/patch test files). Relocated
|
|
3281
|
+
// verbatim from bin/install.js (#2875 Part 2) — single source of truth for both the
|
|
3282
|
+
// inline loop (which now requires this export) and the descriptor pipeline.
|
|
3283
|
+
const READONLY_AGENT_DISALLOWED_TOOLS = {
|
|
3284
|
+
'gsd-plan-checker': 'Write, Edit, MultiEdit',
|
|
3285
|
+
'gsd-integration-checker': 'Write, Edit, MultiEdit',
|
|
3286
|
+
'gsd-ui-checker': 'Write, Edit, MultiEdit',
|
|
3287
|
+
'gsd-verifier': 'Edit, MultiEdit',
|
|
3288
|
+
'gsd-doc-verifier': 'Edit, MultiEdit',
|
|
3289
|
+
'gsd-eval-auditor': 'Edit, MultiEdit',
|
|
3290
|
+
'gsd-ui-auditor': 'Edit, MultiEdit',
|
|
3291
|
+
};
|
|
3292
|
+
/**
|
|
3293
|
+
* Post-converter frontmatter-extensions step (#2875 Part 2 / ADR-1235 §1
|
|
3294
|
+
* follow-up). Driven by the runtime descriptor's
|
|
3295
|
+
* `hostBehaviors.agentFrontmatterExtensions` allow-list — Claude is its only
|
|
3296
|
+
* declared consumer today (`agentFrontmatterExtensions: ["effort"]`).
|
|
3297
|
+
* A runtime that does NOT declare the extension gets nothing injected (J3):
|
|
3298
|
+
* OpenCode/Qwen/Hermes reject unknown frontmatter keys.
|
|
3299
|
+
*
|
|
3300
|
+
* Byte-identical to the inline agent loop's
|
|
3301
|
+
* `if ((_hostBehaviors(runtime).agentFrontmatterExtensions || []).includes('effort'))`
|
|
3302
|
+
* block (bin/install.js): both the effort injection AND the disallowedTools
|
|
3303
|
+
* injection are gated behind the SAME `'effort'` extension flag — there is no
|
|
3304
|
+
* separate `'disallowedTools'` extension key, mirroring the loop exactly.
|
|
3305
|
+
*
|
|
3306
|
+
* J2 (the trap row): when the resolved effort is `'inherit'`, NO `effort:`
|
|
3307
|
+
* key is written at all — the absence of the key IS the behavior (#3533).
|
|
3308
|
+
* Writing `effort: inherit` would be a regression that looks like success.
|
|
3309
|
+
*
|
|
3310
|
+
* @param content agent .md content, already converter-transformed
|
|
3311
|
+
* @param runtime canonical runtime ID
|
|
3312
|
+
* @param agentName agent stem (from deriveAgentName), e.g. 'gsd-planner'
|
|
3313
|
+
* @param targetDir install root — resolves .planning/config.json + ~/.gsd/defaults.json
|
|
3314
|
+
*/
|
|
3315
|
+
function applyAgentFrontmatterExtensions(content, { runtime, agentName, targetDir }) {
|
|
3316
|
+
const extensions = _hostBehaviors(runtime).agentFrontmatterExtensions || [];
|
|
3317
|
+
if (!extensions.includes('effort'))
|
|
3318
|
+
return content;
|
|
3319
|
+
let result = content;
|
|
3320
|
+
const effortCfg = readGsdEffectiveEffortConfig(targetDir ?? null);
|
|
3321
|
+
const universalEffort = resolveInstallTimeEffort(effortCfg, agentName);
|
|
3322
|
+
// #3533 (10d): 'inherit' means the effort: key must NOT exist — Claude Code
|
|
3323
|
+
// then follows the session effort. The canonical source agents carry no
|
|
3324
|
+
// effort key, so skipping injection is the whole job.
|
|
3325
|
+
if (universalEffort !== 'inherit') {
|
|
3326
|
+
const renderedEffort = _getGsdEffortCatalog().renderEffortForRuntime(runtime, universalEffort).value;
|
|
3327
|
+
// #3007: `value` is `string | null` — a rejected/unrenderable level (e.g.
|
|
3328
|
+
// 'ultra', or a catalog with no advertised level at or above the request)
|
|
3329
|
+
// renders null. Same posture as the 'inherit' case above: omit the key
|
|
3330
|
+
// entirely rather than writing a literal `effort: null`, so the host
|
|
3331
|
+
// falls back to its own default instead of failing to parse.
|
|
3332
|
+
if (renderedEffort !== null) {
|
|
3333
|
+
result = injectEffortFrontmatter(result, renderedEffort);
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
const disallowedTools = READONLY_AGENT_DISALLOWED_TOOLS[agentName];
|
|
3337
|
+
if (disallowedTools)
|
|
3338
|
+
result = injectDisallowedToolsFrontmatter(result, disallowedTools);
|
|
3339
|
+
return result;
|
|
3340
|
+
}
|
|
2717
3341
|
/**
|
|
2718
3342
|
* Apply Co-Authored-By attribution policy to file content.
|
|
2719
3343
|
* - null -> remove the Co-Authored-By line and its preceding blank line
|
|
@@ -2756,6 +3380,11 @@ module.exports = {
|
|
|
2756
3380
|
convertClaudeToAntigravityContent,
|
|
2757
3381
|
convertClaudeCommandToAntigravitySkill,
|
|
2758
3382
|
convertClaudeCommandToClaudeSkill,
|
|
3383
|
+
// #2873 (4b): pure, scope-free transform — applied by the one call site
|
|
3384
|
+
// that knows install scope (skillsKind's stage() in
|
|
3385
|
+
// runtime-artifact-layout.cts), never inside convertClaudeCommandToClaudeSkill
|
|
3386
|
+
// itself.
|
|
3387
|
+
resolveSpecRootReference,
|
|
2759
3388
|
convertClaudeCommandToKimiSkill,
|
|
2760
3389
|
convertClaudeCommandToKimiCodeSkill,
|
|
2761
3390
|
buildKimiAgentArtifacts,
|
|
@@ -2810,11 +3439,20 @@ module.exports = {
|
|
|
2810
3439
|
convertClaudeAgentToCodebuddyAgent,
|
|
2811
3440
|
convertClaudeAgentToClineAgent,
|
|
2812
3441
|
convertClaudeAgentToCodexAgent,
|
|
3442
|
+
// #2875 Part 2 (J10): Hermes named branding converter, generic underlying
|
|
3443
|
+
// transform exported alongside it for direct reuse/testing.
|
|
3444
|
+
convertClaudeAgentToHermesAgent,
|
|
3445
|
+
applyAgentBrandingRewrites,
|
|
2813
3446
|
// ADR-1239 / #2092 Phase B Upgrade 1: native .qwen/agents/*.md subagent
|
|
2814
3447
|
// projection — registered by name so convertedAgentsKind's
|
|
2815
3448
|
// conversionExports[converterName] dispatch (runtime-artifact-layout.cts)
|
|
2816
3449
|
// can resolve it from capabilities/qwen/capability.json's agents kind.
|
|
2817
3450
|
convertClaudeAgentToQwenAgent,
|
|
3451
|
+
// #3384: ZCode agents are Claude-shaped but its dispatcher treats mcp__*
|
|
3452
|
+
// tools grants as required MCP servers — registered by name for the same
|
|
3453
|
+
// conversionExports[converterName] dispatch, resolved from
|
|
3454
|
+
// capabilities/zcode/capability.json's agents kind.
|
|
3455
|
+
convertClaudeAgentToZcodeAgent,
|
|
2818
3456
|
// #1511 ADR-1508 Phase 2: rewrite engine deep seam
|
|
2819
3457
|
// Low-level walkers (pathPrefix + attribution pre-resolved by caller):
|
|
2820
3458
|
applyRuntimeContentRewritesInPlace,
|
|
@@ -2825,9 +3463,21 @@ module.exports = {
|
|
|
2825
3463
|
// ADR-1235 §1: descriptor-driven agent cross-cutting
|
|
2826
3464
|
applyAgentPathRewrites,
|
|
2827
3465
|
normalizeAgentBodyForRuntime,
|
|
3466
|
+
// #2875 Part 2: descriptor-driven agent frontmatter-extensions step + its
|
|
3467
|
+
// single-sourced building blocks (also required back by bin/install.js so
|
|
3468
|
+
// the inline loop and the descriptor pipeline resolve through the SAME
|
|
3469
|
+
// code — no drift between the two byte-parity-gated pipelines).
|
|
3470
|
+
deriveAgentName,
|
|
3471
|
+
injectEffortFrontmatter,
|
|
3472
|
+
injectDisallowedToolsFrontmatter,
|
|
3473
|
+
READONLY_AGENT_DISALLOWED_TOOLS,
|
|
3474
|
+
applyAgentFrontmatterExtensions,
|
|
2828
3475
|
_computePathPrefix: computePathPrefix,
|
|
3476
|
+
_restoreClaudeGlobalAtRefTilde: restoreClaudeGlobalAtRefTilde,
|
|
2829
3477
|
_applyRuntimeRewrites,
|
|
2830
3478
|
_stampNonClaudeRuntimeDefaults,
|
|
3479
|
+
// #2652: registry-resolved dispatch isolation, mirroring routeDispatchIsolation
|
|
3480
|
+
_negotiatedDispatchIsolation,
|
|
2831
3481
|
// #1521: canonical non-Claude runtime list for test files and tooling
|
|
2832
3482
|
NON_CLAUDE_RUNTIMES,
|
|
2833
3483
|
};
|