@opengsd/gsd-core 1.9.1 → 1.11.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 +2 -3
- package/.opencode/plugins/gsd-core.js +8 -1
- package/agents/gsd-code-fixer.md +27 -3
- package/agents/gsd-debug-session-manager.md +11 -0
- package/agents/gsd-debugger.md +12 -246
- package/agents/gsd-doc-synthesizer.md +2 -4
- package/agents/gsd-executor.md +12 -10
- package/agents/gsd-integration-checker.md +3 -0
- package/agents/gsd-mempalace-curator.md +5 -2
- package/agents/gsd-phase-researcher.md +20 -1
- package/agents/gsd-plan-checker.md +46 -0
- package/agents/gsd-planner.md +49 -54
- package/agents/gsd-roadmapper.md +21 -3
- package/agents/gsd-user-profiler.md +3 -0
- package/agents/gsd-verifier.md +26 -73
- package/bin/install.js +1272 -1238
- package/bin/lib/ui-safety-gate.cjs +2 -0
- package/commands/gsd/code-review.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/map-codebase.md +1 -1
- package/commands/gsd/mempalace-capture.md +2 -2
- package/commands/gsd/mempalace-recall.md +1 -1
- package/commands/gsd/new-milestone.md +2 -2
- package/commands/gsd/plan-phase.md +1 -1
- package/commands/gsd/quick.md +1 -1
- package/commands/gsd/review-backlog.md +2 -1
- package/commands/gsd/verify-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +1009 -115
- package/gsd-core/bin/lib/active-workstream-store.cjs +153 -12
- package/gsd-core/bin/lib/agent-install-check.cjs +268 -38
- package/gsd-core/bin/lib/api-coverage.cjs +123 -5
- package/gsd-core/bin/lib/artifacts.cjs +3 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +2 -4
- package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
- package/gsd-core/bin/lib/audit.cjs +926 -202
- package/gsd-core/bin/lib/broken-windows.cjs +36 -6
- 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-registry.cjs +608 -148
- package/gsd-core/bin/lib/capability-source.cjs +92 -0
- package/gsd-core/bin/lib/capability-trust.cjs +444 -25
- package/gsd-core/bin/lib/capability-validator.cjs +507 -24
- package/gsd-core/bin/lib/capability-writer.cjs +3 -2
- package/gsd-core/bin/lib/check-command-router.cjs +114 -38
- package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
- package/gsd-core/bin/lib/codex-agent-toml.cjs +329 -0
- package/gsd-core/bin/lib/command-aliases.cjs +94 -0
- package/gsd-core/bin/lib/command-roster.cjs +44 -1
- package/gsd-core/bin/lib/commands.cjs +665 -99
- package/gsd-core/bin/lib/commonjs-marker.cjs +142 -0
- package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
- package/gsd-core/bin/lib/config-loader.cjs +76 -0
- package/gsd-core/bin/lib/config.cjs +22 -2
- package/gsd-core/bin/lib/context-composer.cjs +278 -0
- package/gsd-core/bin/lib/context-predicates.cjs +506 -0
- package/gsd-core/bin/lib/core-utils.cjs +217 -40
- package/gsd-core/bin/lib/decisions.cjs +23 -0
- package/gsd-core/bin/lib/docs.cjs +3 -2
- package/gsd-core/bin/lib/external-job.cjs +19 -4
- package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
- package/gsd-core/bin/lib/frontmatter.cjs +239 -32
- package/gsd-core/bin/lib/gap-checker.cjs +68 -7
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +57 -6
- package/gsd-core/bin/lib/git-base-branch.cjs +160 -15
- package/gsd-core/bin/lib/graphify.cjs +142 -27
- package/gsd-core/bin/lib/gsd2-import.cjs +37 -5
- 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 +145 -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 +265 -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 +173 -0
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +431 -0
- package/gsd-core/bin/lib/host-integration.cjs +13 -1
- package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
- package/gsd-core/bin/lib/init-command-router.cjs +83 -8
- package/gsd-core/bin/lib/init.cjs +1325 -169
- package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
- package/gsd-core/bin/lib/install-engine.cjs +805 -264
- package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +203 -0
- package/gsd-core/bin/lib/install-profiles.cjs +160 -57
- 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-authoring.cjs +3 -1
- package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
- package/gsd-core/bin/lib/installer-migrations/007-retire-config-root-commonjs-marker.cjs +149 -0
- package/gsd-core/bin/lib/installer-migrations/008-cursor-retire-commands-surface.cjs +55 -0
- package/gsd-core/bin/lib/installer-migrations/009-pi-retire-reserved-hooks-dir.cjs +199 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +206 -13
- package/gsd-core/bin/lib/io.cjs +38 -3
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +8 -1
- package/gsd-core/bin/lib/markdown-table.cjs +133 -20
- package/gsd-core/bin/lib/mcp-catalog.cjs +518 -0
- package/gsd-core/bin/lib/mcp-server.cjs +135 -3
- package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
- package/gsd-core/bin/lib/milestone.cjs +821 -109
- package/gsd-core/bin/lib/model-catalog.cjs +59 -1
- package/gsd-core/bin/lib/model-resolver.cjs +183 -40
- package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
- package/gsd-core/bin/lib/pattern.cjs +122 -0
- package/gsd-core/bin/lib/phase-estimation.cjs +1 -1
- package/gsd-core/bin/lib/phase-id.cjs +507 -36
- package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
- package/gsd-core/bin/lib/phase-locator.cjs +258 -58
- package/gsd-core/bin/lib/phase.cjs +891 -156
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +303 -0
- package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
- package/gsd-core/bin/lib/plan-scan.cjs +86 -2
- package/gsd-core/bin/lib/planning-scope.cjs +31 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +890 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +60 -6
- package/gsd-core/bin/lib/probe-core.cjs +1 -1
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/prompt-budget.cjs +128 -165
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +740 -0
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +85 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +108 -0
- package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +447 -68
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
- package/gsd-core/bin/lib/roadmap-command-router.cjs +76 -9
- package/gsd-core/bin/lib/roadmap-parser.cjs +1035 -194
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
- package/gsd-core/bin/lib/roadmap.cjs +405 -84
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +795 -100
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +440 -57
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
- package/gsd-core/bin/lib/runtime-homes.cjs +220 -41
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +220 -44
- package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
- package/gsd-core/bin/lib/runtime-slash.cjs +27 -9
- package/gsd-core/bin/lib/section-manifest.cjs +209 -0
- package/gsd-core/bin/lib/security.cjs +104 -5
- package/gsd-core/bin/lib/shell-command-projection.cjs +388 -30
- package/gsd-core/bin/lib/smart-entry.cjs +154 -22
- package/gsd-core/bin/lib/state-command-router.cjs +5 -1
- package/gsd-core/bin/lib/state-document.cjs +152 -8
- package/gsd-core/bin/lib/state-transition.cjs +424 -105
- package/gsd-core/bin/lib/state.cjs +1927 -401
- package/gsd-core/bin/lib/surface.cjs +35 -10
- 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 +20 -4
- package/gsd-core/bin/lib/uat.cjs +706 -64
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +14 -5
- package/gsd-core/bin/lib/unusable-input.cjs +33 -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.cjs +20 -6
- package/gsd-core/bin/lib/vendor/README.md +37 -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 +287 -20
- package/gsd-core/bin/lib/verify.cjs +368 -880
- package/gsd-core/bin/lib/workflow-fragments.cjs +557 -0
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +203 -19
- package/gsd-core/bin/lib/workstream-inventory.cjs +576 -31
- package/gsd-core/bin/lib/workstream.cjs +8 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +50 -6
- package/gsd-core/bin/lib/worktree-safety.cjs +450 -125
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +9 -1
- package/gsd-core/references/agent-contracts.md +43 -26
- package/gsd-core/references/artifact-types.md +10 -3
- package/gsd-core/references/autonomous-ui-design-contract.md +42 -0
- package/gsd-core/references/checkpoints.md +2 -2
- package/gsd-core/references/context-budget.md +1 -1
- package/gsd-core/references/debugger-techniques.md +255 -0
- package/gsd-core/references/dispatch-isolation-gate.md +138 -0
- package/gsd-core/references/doc-conflict-engine.md +1 -1
- package/gsd-core/references/execute-mvp-tdd.md +3 -3
- package/gsd-core/references/execute-phase-between-wave-reset.md +6 -2
- 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 +6 -2
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-planning-commit.md +2 -1
- package/gsd-core/references/loop-hook-dispatch.md +39 -2
- package/gsd-core/references/model-profiles.md +12 -4
- package/gsd-core/references/mvp-concepts.md +9 -9
- package/gsd-core/references/planner-guidance.md +3 -9
- package/gsd-core/references/planner-preconditions.md +1 -1
- package/gsd-core/references/planner-reviews.md +1 -1
- package/gsd-core/references/planning-config.md +8 -6
- package/gsd-core/references/research-documentation-lookup.md +5 -3
- package/gsd-core/references/revision-loop.md +1 -1
- package/gsd-core/references/specless-probe-fallback.md +8 -7
- package/gsd-core/references/universal-anti-patterns.md +3 -3
- package/gsd-core/references/verifier-phase-gates.md +192 -0
- package/gsd-core/references/verifier-wiring-patterns.md +100 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +22 -6
- package/gsd-core/references/worktree-branch-check.md +2 -2
- package/gsd-core/templates/discussion-log.md +1 -1
- package/gsd-core/templates/phase-prompt.md +2 -4
- package/gsd-core/templates/state.md +4 -4
- package/gsd-core/templates/summary-complex.md +2 -0
- package/gsd-core/templates/summary-minimal.md +2 -0
- package/gsd-core/templates/summary-standard.md +2 -0
- package/gsd-core/templates/summary.md +2 -0
- package/gsd-core/templates/verification-report.md +9 -1
- package/gsd-core/workflows/ai-integration-phase.md +9 -11
- package/gsd-core/workflows/audit-milestone.md +3 -0
- package/gsd-core/workflows/autonomous/steps/converge-banner.md +1 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md +11 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md +7 -0
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +21 -0
- package/gsd-core/workflows/autonomous/steps/converge-loop.md +7 -0
- package/gsd-core/workflows/autonomous.md +33 -70
- package/gsd-core/workflows/cleanup.md +62 -3
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +39 -0
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +93 -0
- package/gsd-core/workflows/code-review-fix.md +37 -10
- package/gsd-core/workflows/code-review.md +74 -166
- package/gsd-core/workflows/complete-milestone/steps/git-tag.md +29 -0
- package/gsd-core/workflows/complete-milestone.md +160 -95
- package/gsd-core/workflows/debug.md +16 -17
- package/gsd-core/workflows/diagnose-issues.md +56 -8
- package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -1
- package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +15 -0
- package/gsd-core/workflows/discuss-phase-assumptions.md +7 -17
- package/gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md +51 -0
- package/gsd-core/workflows/docs-update.md +8 -51
- package/gsd-core/workflows/edit-phase.md +26 -1
- package/gsd-core/workflows/eval-review.md +3 -5
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +64 -7
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +50 -0
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +31 -0
- 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 +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +42 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +43 -37
- package/gsd-core/workflows/execute-phase.md +103 -187
- package/gsd-core/workflows/execute-plan.md +36 -4
- package/gsd-core/workflows/explore.md +131 -4
- package/gsd-core/workflows/fast.md +10 -2
- package/gsd-core/workflows/health.md +73 -4
- package/gsd-core/workflows/help/modes/full.md +6 -1
- package/gsd-core/workflows/import.md +4 -4
- package/gsd-core/workflows/ingest-docs.md +7 -6
- package/gsd-core/workflows/mvp-phase.md +6 -3
- package/gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md +16 -0
- package/gsd-core/workflows/new-milestone/steps/reset-phase-safety.md +19 -0
- package/gsd-core/workflows/new-milestone.md +35 -47
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +176 -0
- package/gsd-core/workflows/new-project/steps/auto-mode-detection.md +32 -0
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +18 -0
- package/gsd-core/workflows/new-project.md +27 -240
- package/gsd-core/workflows/next.md +12 -0
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +15 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +110 -0
- package/gsd-core/workflows/plan-phase/steps/prd-express-gate.md +8 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-early-exit.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-modifiers.md +16 -0
- package/gsd-core/workflows/plan-phase/steps/reviews-prerequisite.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +149 -0
- package/gsd-core/workflows/plan-phase.md +89 -209
- package/gsd-core/workflows/plan-review-convergence.md +50 -2
- package/gsd-core/workflows/progress/steps/forensic-audit.md +125 -0
- package/gsd-core/workflows/progress/steps/mvp-display.md +18 -0
- package/gsd-core/workflows/progress.md +45 -159
- package/gsd-core/workflows/quick/steps/discussion-phase.md +124 -0
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +111 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +67 -0
- package/gsd-core/workflows/quick/steps/research-phase.md +72 -0
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +37 -0
- package/gsd-core/workflows/quick.md +55 -405
- package/gsd-core/workflows/resume-project.md +3 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-1.md +4 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-2.md +3 -0
- package/gsd-core/workflows/review.md +41 -13
- package/gsd-core/workflows/section-manifest.json +219 -0
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/session-report.md +2 -1
- package/gsd-core/workflows/settings.md +66 -2
- package/gsd-core/workflows/ship.md +104 -44
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/spec-phase.md +41 -20
- package/gsd-core/workflows/spike-wrap-up.md +20 -5
- package/gsd-core/workflows/spike.md +50 -16
- package/gsd-core/workflows/sync-skills.md +106 -13
- package/gsd-core/workflows/transition/steps/workstream-collision-check.md +17 -0
- package/gsd-core/workflows/transition.md +53 -31
- package/gsd-core/workflows/ui-phase.md +13 -12
- package/gsd-core/workflows/ui-review.md +2 -2
- package/gsd-core/workflows/update/steps/channel-banner.md +7 -0
- package/gsd-core/workflows/update.md +19 -8
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +36 -0
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +21 -0
- package/gsd-core/workflows/verify-work.md +17 -65
- package/hooks/dist/gsd-agent-isolation-guard.js +517 -0
- package/hooks/dist/gsd-check-update-worker.js +64 -12
- package/hooks/dist/gsd-check-update.js +19 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +0 -3
- package/hooks/dist/gsd-cursor-subagent-start.js +607 -26
- package/hooks/dist/gsd-cursor-subagent-stop.js +3 -2
- package/hooks/dist/gsd-prompt-guard.js +21 -20
- package/hooks/dist/gsd-read-injection-scanner.js +45 -24
- package/hooks/dist/gsd-statusline.js +90 -6
- package/hooks/dist/gsd-update-banner.js +22 -1
- package/hooks/dist/gsd-workflow-guard.js +134 -36
- package/hooks/dist/gsd-worktree-path-guard.js +2 -1
- package/hooks/dist/gsd-write-guard.js +359 -0
- package/hooks/dist/lib/git-cmd.js +92 -59
- 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 +277 -0
- package/hooks/dist/managed-hooks-registry.cjs +2 -0
- package/hooks/gsd-agent-isolation-guard.js +517 -0
- package/hooks/gsd-check-update-worker.js +64 -12
- package/hooks/gsd-check-update.js +19 -1
- package/hooks/gsd-cursor-pre-tool.js +0 -3
- package/hooks/gsd-cursor-subagent-start.js +607 -26
- package/hooks/gsd-cursor-subagent-stop.js +3 -2
- package/hooks/gsd-prompt-guard.js +21 -20
- package/hooks/gsd-read-injection-scanner.js +45 -24
- package/hooks/gsd-statusline.js +90 -6
- package/hooks/gsd-update-banner.js +22 -1
- package/hooks/gsd-workflow-guard.js +134 -36
- package/hooks/gsd-worktree-path-guard.js +2 -1
- package/hooks/gsd-write-guard.js +359 -0
- package/hooks/hooks.json +12 -0
- package/hooks/lib/git-cmd.js +92 -59
- package/hooks/lib/injection-patterns.js +45 -0
- package/hooks/lib/isolation-deny-reason.js +39 -0
- package/hooks/lib/isolation-sentinel.js +277 -0
- package/hooks/managed-hooks-registry.cjs +2 -0
- package/package.json +31 -10
- package/pi/gsd.cjs +71 -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 +9 -0
- package/scripts/changeset/lint.cjs +68 -6
- package/scripts/changeset/serialize.cjs +5 -1
- package/scripts/check-alias-drift.cjs +7 -43
- package/scripts/check-contract-drift.cjs +297 -0
- package/scripts/ci-test-scope.cjs +19 -2
- package/scripts/command-contract-helpers.cjs +903 -1
- package/scripts/gen-adr-index.cjs +728 -38
- package/scripts/gen-capability-matrix.cjs +1 -1
- package/scripts/gen-capability-registry.cjs +3 -15
- package/scripts/gen-context-index.cjs +439 -0
- package/scripts/gen-health-docs.cjs +390 -0
- package/scripts/gen-inventory-manifest.cjs +150 -4
- package/scripts/gen-loop-host-contract.cjs +4 -24
- package/scripts/gen-prompt-budget-parity-corpus.cjs +645 -0
- package/scripts/gen-registry.cjs +3 -14
- package/scripts/gen-section-manifest.cjs +638 -0
- package/scripts/generate-package-identity.cjs +4 -2
- package/scripts/lib/alias-drift-families.cjs +46 -0
- package/scripts/lib/drift-scan.cjs +278 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +15 -54
- 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-compiled-artifact-sync.cjs +6 -1
- 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-command-form.cjs +195 -0
- package/scripts/lint-docs-required.cjs +9 -1
- package/scripts/lint-emitted-drift-ack.cjs +215 -20
- package/scripts/lint-eslint-glob-coverage.allowlist.json +34 -0
- package/scripts/lint-eslint-glob-coverage.cjs +340 -0
- package/scripts/lint-example-parser-parity.cjs +395 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
- package/scripts/lint-health-diagnostic-rule-table.cjs +404 -0
- package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
- package/scripts/lint-milestone-window-drift.cjs +468 -0
- package/scripts/lint-phase-enumeration-drift.cjs +479 -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 +434 -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 +320 -0
- package/scripts/lint-state-field-drift.cjs +805 -0
- package/scripts/lint-state-write-path-drift.cjs +1045 -0
- package/scripts/lint-test-file-count.allowlist.json +40 -3
- package/scripts/lint-unreachable-guard-drift.cjs +843 -0
- package/scripts/lint-vendored-deps.cjs +124 -0
- package/scripts/mutation-matrix.cjs +13 -0
- package/scripts/pr-changed-files.cjs +63 -0
- package/scripts/pr-template-policy.cjs +14 -4
- package/scripts/prompt-injection-scan.sh +52 -6
- package/scripts/require-issue-link-policy.cjs +192 -0
- package/scripts/state-write-path-drift-baseline.json +19 -0
- package/scripts/sync-runtime-launcher.cjs +2 -4
- package/skills/gsd-autonomous/SKILL.md +0 -1
- package/skills/gsd-code-review/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -2
- package/skills/gsd-map-codebase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +2 -2
- package/skills/gsd-mempalace-recall/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +2 -2
- package/skills/gsd-next/SKILL.md +0 -1
- package/skills/gsd-plan-phase/SKILL.md +1 -2
- package/skills/gsd-progress/SKILL.md +0 -1
- package/skills/gsd-quick/SKILL.md +1 -1
- 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/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 -577
- package/scripts/affected-tests-lib.cjs +0 -554
- package/scripts/gen-emitted-baseline.cjs +0 -145
- package/scripts/lint-allow-test-rule-refs.cjs +0 -162
- package/scripts/run-affected-tests.cjs +0 -7
- package/scripts/run-tests.cjs +0 -1050
|
@@ -22,6 +22,15 @@ const WORKSTREAM_SESSION_ENV_KEYS = [
|
|
|
22
22
|
'GSD_SESSION_KEY',
|
|
23
23
|
'CODEX_THREAD_ID',
|
|
24
24
|
'CLAUDE_SESSION_ID',
|
|
25
|
+
// #3557 — Claude Code (≥ 2.1.132) exports its session id to Bash-tool
|
|
26
|
+
// subprocesses as CLAUDE_CODE_SESSION_ID. Without it the probe returned
|
|
27
|
+
// null on Claude Code, so every concurrent session in a working tree
|
|
28
|
+
// shared the single .planning/active-workstream pointer and cross-
|
|
29
|
+
// workstream STATE.md writes landed silently in the wrong file. Inserted
|
|
30
|
+
// beside the other runtime keys without reordering any existing entry;
|
|
31
|
+
// ahead of CLAUDE_CODE_SSE_PORT so the canonical id wins when both are
|
|
32
|
+
// present (runtime identity outranks terminal identity in this list).
|
|
33
|
+
'CLAUDE_CODE_SESSION_ID',
|
|
25
34
|
'CLAUDE_CODE_SSE_PORT',
|
|
26
35
|
'OPENCODE_SESSION_ID',
|
|
27
36
|
'GEMINI_SESSION_ID',
|
|
@@ -183,21 +192,150 @@ function pickActiveWorkstreamAdapter(cwd, opts = {}) {
|
|
|
183
192
|
}
|
|
184
193
|
return createSharedPointerAdapter(cwd);
|
|
185
194
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Read-resolution chain for getActiveWorkstream/peekActiveWorkstream (#3579).
|
|
197
|
+
*
|
|
198
|
+
* pickActiveWorkstreamAdapter (above) picks exactly one adapter and remains
|
|
199
|
+
* the seam for WRITE paths (set/clear), where "which pointer do I mutate" has
|
|
200
|
+
* only one right answer: the session pointer when a session key exists,
|
|
201
|
+
* otherwise the shared marker. Reads are different — a session that has
|
|
202
|
+
* never called `workstream use` has no opinion of its own, so it should
|
|
203
|
+
* inherit the repo-wide `.planning/active-workstream` marker rather than
|
|
204
|
+
* resolve to nothing. This returns an ORDERED chain: [owned, ...fallbacks].
|
|
205
|
+
* `chain[0]` ("owned") is exactly what pickActiveWorkstreamAdapter would have
|
|
206
|
+
* returned — resolveFromChain() self-heals only chain[0], never a fallback,
|
|
207
|
+
* so one session's read can never delete another scope's marker. Fallbacks
|
|
208
|
+
* are consulted ONLY when chain[0].read() comes back absent/empty; a session
|
|
209
|
+
* with its own (even stale/invalid) pointer never falls through — that is
|
|
210
|
+
* the isolation guarantee and it must not be weakened by inheritance.
|
|
211
|
+
*/
|
|
212
|
+
function pickActiveWorkstreamAdapterChain(cwd, opts = {}) {
|
|
213
|
+
if (opts.activeWorkstreamAdapter) {
|
|
214
|
+
return [opts.activeWorkstreamAdapter];
|
|
194
215
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
216
|
+
// #3579 item 3: when a caller supplies `opts.activeWorkstreamAdapters` at
|
|
217
|
+
// all, honor ONLY what it provides. The prior `|| createXPointerAdapter(...)`
|
|
218
|
+
// fallback synthesized a REAL filesystem adapter for whichever half a test
|
|
219
|
+
// double omitted — so a test injecting only `{ session }` silently touched
|
|
220
|
+
// the real shared marker file, and one injecting only `{ shared }` silently
|
|
221
|
+
// touched the real session-scoped tmp file. A missing half now gets a
|
|
222
|
+
// no-op in-memory adapter (always reads null) instead — this preserves the
|
|
223
|
+
// chain[0]-is-owned / rest-are-fallback shape resolveFromChain relies on
|
|
224
|
+
// without ever reaching disk. A caller that wants a real adapter for one
|
|
225
|
+
// half can still construct and pass it explicitly.
|
|
226
|
+
const injected = opts.activeWorkstreamAdapters;
|
|
227
|
+
const sessionKey = getWorkstreamSessionKey();
|
|
228
|
+
if (!sessionKey) {
|
|
229
|
+
const shared = injected
|
|
230
|
+
? (injected.shared ?? createMemoryPointerAdapter(null))
|
|
231
|
+
: createSharedPointerAdapter(cwd);
|
|
232
|
+
return [shared];
|
|
233
|
+
}
|
|
234
|
+
const session = injected
|
|
235
|
+
? (injected.session ?? createMemoryPointerAdapter(null))
|
|
236
|
+
: createSessionScopedPointerAdapter(cwd, sessionKey);
|
|
237
|
+
const shared = injected
|
|
238
|
+
? (injected.shared ?? createMemoryPointerAdapter(null))
|
|
239
|
+
: createSharedPointerAdapter(cwd);
|
|
240
|
+
return session ? [session, shared] : [shared];
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Shared "does this stored name resolve" predicate — format-valid AND its
|
|
244
|
+
* workstream directory exists. Factored out so resolveFromChain's owned/
|
|
245
|
+
* fallback arms (and diagnoseUnresolvedActiveWorkstream, #3579 item 1) share
|
|
246
|
+
* one definition of "resolvable" instead of re-deriving the same two checks.
|
|
247
|
+
*/
|
|
248
|
+
function resolvesToExistingWorkstream(cwd, name) {
|
|
249
|
+
if (!name || !validateWorkstreamName(name))
|
|
250
|
+
return false;
|
|
251
|
+
return node_fs_1.default.existsSync(node_path_1.default.join(planningRoot(cwd), 'workstreams', name));
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Resolves a stored workstream name by walking an adapter chain.
|
|
255
|
+
*
|
|
256
|
+
* chain[0] is "owned" by this resolution: an absent/empty read falls through
|
|
257
|
+
* to the next adapter, but a present-and-bad read (invalid name, or a name
|
|
258
|
+
* whose workstream dir no longer exists) is resolved right there — self-
|
|
259
|
+
* healed via adapter.clear() when `selfHeal` is true, and never consulted
|
|
260
|
+
* further. Anything after chain[0] is a read-only fallback (the inherited
|
|
261
|
+
* marker): a bad value there resolves to null WITHOUT ever calling clear(),
|
|
262
|
+
* so a pointer-less session's read can never delete the shared marker that
|
|
263
|
+
* other sessions/scopes still depend on.
|
|
264
|
+
*/
|
|
265
|
+
function resolveFromChain(cwd, chain, selfHeal) {
|
|
266
|
+
if (chain.length === 0)
|
|
198
267
|
return null;
|
|
268
|
+
const [owned, ...fallbacks] = chain;
|
|
269
|
+
const ownedName = owned.read();
|
|
270
|
+
if (ownedName) {
|
|
271
|
+
if (!resolvesToExistingWorkstream(cwd, ownedName)) {
|
|
272
|
+
if (selfHeal)
|
|
273
|
+
owned.clear();
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
return ownedName;
|
|
277
|
+
}
|
|
278
|
+
for (const adapter of fallbacks) {
|
|
279
|
+
const name = adapter.read();
|
|
280
|
+
if (resolvesToExistingWorkstream(cwd, name))
|
|
281
|
+
return name;
|
|
282
|
+
}
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Diagnostic sibling of resolveFromChain (#3579 item 1). getActiveWorkstream/
|
|
287
|
+
* peekActiveWorkstream collapse EVERY unresolvable case to `null`, which is
|
|
288
|
+
* exactly right for routing — but a fail-safe guard reporting "no active
|
|
289
|
+
* workstream is set" to an operator needs to distinguish two very different
|
|
290
|
+
* situations that both produce that same `null`:
|
|
291
|
+
*
|
|
292
|
+
* (a) no marker/pointer exists anywhere in the chain at all, vs.
|
|
293
|
+
* (b) a marker/pointer EXISTS (names a value) but that value didn't
|
|
294
|
+
* resolve — either the name fails validateWorkstreamName, or it's a
|
|
295
|
+
* well-formed name whose `workstreams/<name>` directory is missing.
|
|
296
|
+
*
|
|
297
|
+
* Walks the same chain resolveFromChain uses and, for the first adapter that
|
|
298
|
+
* held a non-empty raw value, reports why it didn't resolve. Read-only: never
|
|
299
|
+
* calls adapter.clear() (mirrors peekActiveWorkstream, not getActiveWorkstream
|
|
300
|
+
* — a diagnostic read must not have side effects). Reuses
|
|
301
|
+
* resolvesToExistingWorkstream so this can never disagree with the actual
|
|
302
|
+
* resolution predicate above.
|
|
303
|
+
*/
|
|
304
|
+
function diagnoseUnresolvedActiveWorkstream(cwd, opts = {}) {
|
|
305
|
+
const chain = pickActiveWorkstreamAdapterChain(cwd, opts);
|
|
306
|
+
for (const adapter of chain) {
|
|
307
|
+
const raw = adapter.read();
|
|
308
|
+
if (!raw)
|
|
309
|
+
continue;
|
|
310
|
+
if (resolvesToExistingWorkstream(cwd, raw))
|
|
311
|
+
continue;
|
|
312
|
+
return {
|
|
313
|
+
present: true,
|
|
314
|
+
value: raw,
|
|
315
|
+
reason: validateWorkstreamName(raw) ? 'missing_workstream_dir' : 'invalid_name',
|
|
316
|
+
};
|
|
199
317
|
}
|
|
200
|
-
return
|
|
318
|
+
return { present: false, value: null, reason: null };
|
|
319
|
+
}
|
|
320
|
+
function getActiveWorkstream(cwd, opts = {}) {
|
|
321
|
+
const chain = pickActiveWorkstreamAdapterChain(cwd, opts);
|
|
322
|
+
return resolveFromChain(cwd, chain, true);
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Read-only sibling of getActiveWorkstream (#2850): identical resolution —
|
|
326
|
+
* adapter -> stored name -> validate format -> workstream dir exists — but
|
|
327
|
+
* NEVER calls adapter.clear(). getActiveWorkstream's self-heal (deleting a
|
|
328
|
+
* stale/invalid pointer) is correct for a command that is actively acting on
|
|
329
|
+
* the active workstream; it is wrong for a read-only consumer invoked on
|
|
330
|
+
* every render (e.g. the statusline hook), which must never mutate
|
|
331
|
+
* persistent, possibly cross-session state as a side effect of drawing a
|
|
332
|
+
* screen. A stale or invalid pointer simply resolves to null here — the
|
|
333
|
+
* caller decides what "unresolvable" means for its own render, and the
|
|
334
|
+
* pointer file is left exactly as it was for whatever created it to fix.
|
|
335
|
+
*/
|
|
336
|
+
function peekActiveWorkstream(cwd, opts = {}) {
|
|
337
|
+
const chain = pickActiveWorkstreamAdapterChain(cwd, opts);
|
|
338
|
+
return resolveFromChain(cwd, chain, false);
|
|
201
339
|
}
|
|
202
340
|
function setActiveWorkstream(cwd, name, opts = {}) {
|
|
203
341
|
const adapter = pickActiveWorkstreamAdapter(cwd, opts);
|
|
@@ -287,7 +425,10 @@ module.exports = {
|
|
|
287
425
|
createSessionScopedPointerAdapter,
|
|
288
426
|
createMemoryPointerAdapter,
|
|
289
427
|
pickActiveWorkstreamAdapter,
|
|
428
|
+
pickActiveWorkstreamAdapterChain,
|
|
290
429
|
getActiveWorkstream,
|
|
430
|
+
peekActiveWorkstream,
|
|
431
|
+
diagnoseUnresolvedActiveWorkstream,
|
|
291
432
|
setActiveWorkstream,
|
|
292
433
|
clearActiveWorkstream,
|
|
293
434
|
parseCliWorkstream,
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Agent Install Check — moved from core.cts (ADR-857 T0 #1268 phase rehome-core-squatters).
|
|
4
4
|
*
|
|
5
5
|
* Owns:
|
|
6
|
-
* - getAgentsDir(runtime?): string
|
|
7
|
-
* - checkAgentsInstalled(runtime?): AgentsInstalledResult
|
|
6
|
+
* - getAgentsDir(runtime?, projectRoot?): string
|
|
7
|
+
* - checkAgentsInstalled(runtime?, projectRoot?): AgentsInstalledResult
|
|
8
8
|
*
|
|
9
9
|
* The core.cjs re-export spine was retired in epic #1267; callers import
|
|
10
10
|
* these symbols from agent-install-check.cjs directly.
|
|
@@ -18,25 +18,103 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
18
18
|
const modelProfiles = require("./model-profiles.cjs");
|
|
19
19
|
const { MODEL_PROFILES } = modelProfiles;
|
|
20
20
|
const runtime_homes_cjs_1 = require("./runtime-homes.cjs");
|
|
21
|
+
const runtime_name_policy_cjs_1 = require("./runtime-name-policy.cjs");
|
|
22
|
+
// #3242 — model-catalog is a genuine leaf (only node:path + its own JSON), which is
|
|
23
|
+
// exactly why Phase 1 (#3241) moved isAnthropicFlavoredModel there: this module can
|
|
24
|
+
// consume it without dragging model-resolver's config-loader chain into a
|
|
25
|
+
// pure read/verify surface.
|
|
26
|
+
const model_catalog_cjs_1 = require("./model-catalog.cjs");
|
|
27
|
+
// #3243 — the Codex `.toml` block-range/BOM/scan primitives moved into the typed
|
|
28
|
+
// IR module (Phase 3), which this reader now imports rather than defining
|
|
29
|
+
// locally. Behavior is unchanged: scanTomlLines/stripBOM here are the exact
|
|
30
|
+
// same lenient functions that used to live in this file — see
|
|
31
|
+
// codex-agent-toml.cts's module header for the reader/writer reconciliation.
|
|
32
|
+
const codex_agent_toml_cjs_1 = require("./codex-agent-toml.cjs");
|
|
33
|
+
/**
|
|
34
|
+
* Frozen reason enum for {@link checkCodexModelPosture}. Per CONTRIBUTING's
|
|
35
|
+
* typed-IR rule ("Error / status / reason → a frozen enum"): callers and tests
|
|
36
|
+
* assert on these wire values, never on prose. Adding a member is a deliberate
|
|
37
|
+
* three-way coordinated change — enum, emitting site, and the enum-lock test.
|
|
38
|
+
*/
|
|
39
|
+
const POSTURE_REASON = Object.freeze({
|
|
40
|
+
ANTHROPIC_FLAVORED_MODEL: 'anthropic_flavored_model',
|
|
41
|
+
ORPHANED_REASONING_EFFORT: 'orphaned_reasoning_effort',
|
|
42
|
+
UNREADABLE: 'unreadable',
|
|
43
|
+
NOT_CODEX: 'not_codex',
|
|
44
|
+
AGENTS_DIR_MISSING: 'agents_dir_missing',
|
|
45
|
+
});
|
|
46
|
+
// Matches the value-truncation convention in bin/install.js's
|
|
47
|
+
// _warnCodexModelOverrideDropped: values over 64 chars are capped so an
|
|
48
|
+
// oversized or secret-shaped config value can never reach a report in full.
|
|
49
|
+
function truncatePostureValue(value) {
|
|
50
|
+
return value.length > 64 ? `${value.slice(0, 64)}…` : value;
|
|
51
|
+
}
|
|
21
52
|
/**
|
|
22
53
|
* Resolve the agents directory for the given runtime.
|
|
23
54
|
*
|
|
24
55
|
* Priority:
|
|
25
56
|
* 1. GSD_AGENTS_DIR env var (explicit override, any runtime)
|
|
26
|
-
* 2. For claude runtime: __dirname-relative path (agents/ sibling of
|
|
27
|
-
*
|
|
28
|
-
* agents/
|
|
29
|
-
*
|
|
57
|
+
* 2. For claude runtime: __dirname-relative path (agents/ sibling of
|
|
58
|
+
* gsd-core/) — correct for repo runs and runtime-config-dir installs,
|
|
59
|
+
* where the sibling agents/ IS the user's agents dir — UNLESS that path
|
|
60
|
+
* carries an exact node_modules segment. gsd-tools.cjs lives inside
|
|
61
|
+
* gsd-core/bin/ in every install shape, but on an npm-global install
|
|
62
|
+
* gsd-core/ sits inside the package (not the runtime config dir) and the
|
|
63
|
+
* package ships its own agents/, so the install-relative path resolves
|
|
64
|
+
* to the bundled copy and the check validates the package against
|
|
65
|
+
* itself — agents_installed can never be false. In that case resolve
|
|
66
|
+
* getGlobalConfigDir('claude')/agents (honours CLAUDE_CONFIG_DIR) like
|
|
67
|
+
* every other runtime (#3203).
|
|
68
|
+
* 3. For non-claude runtimes with a manifest-backed project-local install:
|
|
69
|
+
* <projectRoot>/<localConfigDir>/agents (or <projectRoot>/agents when
|
|
70
|
+
* the runtime's local install targets the project root). Requiring the
|
|
71
|
+
* GSD manifest prevents runtime-native project agents from shadowing a
|
|
72
|
+
* working global GSD install. Symlinked local agent directories are ignored.
|
|
73
|
+
* 4. For non-claude runtimes: getGlobalConfigDir(runtime)/agents
|
|
30
74
|
*
|
|
31
75
|
* @param runtime - the active runtime name; defaults to GSD_RUNTIME env, then 'claude'
|
|
76
|
+
* @param projectRoot - canonical project root for local-install discovery
|
|
32
77
|
*/
|
|
33
|
-
function getAgentsDir(runtime) {
|
|
78
|
+
function getAgentsDir(runtime, projectRoot) {
|
|
34
79
|
if (process.env['GSD_AGENTS_DIR']) {
|
|
35
80
|
return process.env['GSD_AGENTS_DIR'];
|
|
36
81
|
}
|
|
37
82
|
const resolved = runtime ?? (process.env['GSD_RUNTIME'] || 'claude');
|
|
38
83
|
if (resolved === 'claude') {
|
|
39
|
-
|
|
84
|
+
const installRelative = node_path_1.default.join(__dirname, '..', '..', '..', 'agents');
|
|
85
|
+
// #3203: a lexical guard, not an install-shape test. It targets the
|
|
86
|
+
// layouts where the sibling agents/ is the package's own bundled copy and
|
|
87
|
+
// the check would otherwise validate the package against itself; a path
|
|
88
|
+
// merely carrying a directory of that name resolves the same way, and a
|
|
89
|
+
// non-empty GSD_AGENTS_DIR overrides both.
|
|
90
|
+
if (installRelative.split(node_path_1.default.sep).includes('node_modules')) {
|
|
91
|
+
return node_path_1.default.join((0, runtime_homes_cjs_1.getGlobalConfigDir)('claude'), 'agents');
|
|
92
|
+
}
|
|
93
|
+
return installRelative;
|
|
94
|
+
}
|
|
95
|
+
if (projectRoot) {
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
97
|
+
const { runtimes } = require('./capability-registry.cjs');
|
|
98
|
+
const runtimeConfig = runtimes[resolved]?.runtime;
|
|
99
|
+
const localConfigDirName = (0, runtime_name_policy_cjs_1.getDirName)(resolved);
|
|
100
|
+
const localConfigDir = localConfigDirName === runtime_name_policy_cjs_1.NO_LOCAL_CONFIG_DIR_SENTINEL
|
|
101
|
+
? undefined
|
|
102
|
+
: runtimeConfig?.hostBehaviors?.localTargetIsProjectRoot
|
|
103
|
+
? projectRoot
|
|
104
|
+
: node_path_1.default.join(projectRoot, localConfigDirName);
|
|
105
|
+
if (!localConfigDir) {
|
|
106
|
+
return node_path_1.default.join((0, runtime_homes_cjs_1.getGlobalConfigDir)(resolved), 'agents');
|
|
107
|
+
}
|
|
108
|
+
const localAgentsDir = node_path_1.default.join(localConfigDir, 'agents');
|
|
109
|
+
const manifestPath = node_path_1.default.join(localConfigDir, 'gsd-file-manifest.json');
|
|
110
|
+
try {
|
|
111
|
+
if (node_fs_1.default.lstatSync(localAgentsDir).isDirectory() && node_fs_1.default.lstatSync(manifestPath).isFile()) {
|
|
112
|
+
return localAgentsDir;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Local discovery is best-effort; any probe failure preserves global fallback.
|
|
117
|
+
}
|
|
40
118
|
}
|
|
41
119
|
return node_path_1.default.join((0, runtime_homes_cjs_1.getGlobalConfigDir)(resolved), 'agents');
|
|
42
120
|
}
|
|
@@ -44,10 +122,11 @@ function getAgentsDir(runtime) {
|
|
|
44
122
|
* Check which GSD agents are installed on disk.
|
|
45
123
|
*
|
|
46
124
|
* @param runtime - the active runtime name; defaults to GSD_RUNTIME env, then 'claude'
|
|
125
|
+
* @param projectRoot - canonical project root for local-install discovery
|
|
47
126
|
*/
|
|
48
|
-
function checkAgentsInstalled(runtime) {
|
|
127
|
+
function checkAgentsInstalled(runtime, projectRoot) {
|
|
49
128
|
const resolvedRuntime = runtime ?? (process.env['GSD_RUNTIME'] || 'claude');
|
|
50
|
-
const agentsDir = getAgentsDir(resolvedRuntime);
|
|
129
|
+
const agentsDir = getAgentsDir(resolvedRuntime, projectRoot);
|
|
51
130
|
const expectedAgents = Object.keys(MODEL_PROFILES);
|
|
52
131
|
const installed = [];
|
|
53
132
|
const missing = [];
|
|
@@ -62,18 +141,7 @@ function checkAgentsInstalled(runtime) {
|
|
|
62
141
|
};
|
|
63
142
|
}
|
|
64
143
|
for (const agent of expectedAgents) {
|
|
65
|
-
|
|
66
|
-
const agentFileCopilot = node_path_1.default.join(agentsDir, `${agent}.agent.md`);
|
|
67
|
-
const agentFileCodex = node_path_1.default.join(agentsDir, `${agent}.toml`);
|
|
68
|
-
const agentFileKimiYaml = node_path_1.default.join(agentsDir, 'subagents', `${agent}.yaml`);
|
|
69
|
-
const agentFileKimiPrompt = node_path_1.default.join(agentsDir, 'subagents', `${agent}.md`);
|
|
70
|
-
const kimiAgentInstalled = resolvedRuntime === 'kimi' &&
|
|
71
|
-
node_fs_1.default.existsSync(agentFileKimiYaml) &&
|
|
72
|
-
node_fs_1.default.existsSync(agentFileKimiPrompt);
|
|
73
|
-
if (node_fs_1.default.existsSync(agentFile) ||
|
|
74
|
-
node_fs_1.default.existsSync(agentFileCopilot) ||
|
|
75
|
-
node_fs_1.default.existsSync(agentFileCodex) ||
|
|
76
|
-
kimiAgentInstalled) {
|
|
144
|
+
if (agentFileExists(agentsDir, agent, resolvedRuntime)) {
|
|
77
145
|
installed.push(agent);
|
|
78
146
|
}
|
|
79
147
|
else {
|
|
@@ -87,22 +155,13 @@ function checkAgentsInstalled(runtime) {
|
|
|
87
155
|
// when the plain presence check above passed (e.g. .md present, .toml absent).
|
|
88
156
|
// If no manifest is found the check is a no-op (graceful for claude/bundled).
|
|
89
157
|
const incomplete = [];
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
'files' in parsed &&
|
|
98
|
-
typeof parsed['files'] === 'object' &&
|
|
99
|
-
parsed['files'] !== null) {
|
|
100
|
-
manifestFiles = parsed['files'];
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
catch {
|
|
104
|
-
// No manifest or unreadable — completeness check is skipped
|
|
105
|
-
}
|
|
158
|
+
// #2872: the manifest read is the Installer Migration Module's, not a
|
|
159
|
+
// fourth private copy of it. Lazily required — matching this file's own
|
|
160
|
+
// capability-registry idiom — so a pure read/verify surface on the
|
|
161
|
+
// init/verify hot path takes no new static dependency.
|
|
162
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
163
|
+
const { readInstallManifest } = require('./installer-migrations.cjs');
|
|
164
|
+
const manifestFiles = readInstallManifest(node_path_1.default.dirname(agentsDir)).files;
|
|
106
165
|
if (Object.keys(manifestFiles).length > 0) {
|
|
107
166
|
for (const agent of expectedAgents) {
|
|
108
167
|
// Find all manifest keys that belong to this agent:
|
|
@@ -137,7 +196,178 @@ function checkAgentsInstalled(runtime) {
|
|
|
137
196
|
agent_runtime: resolvedRuntime,
|
|
138
197
|
};
|
|
139
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Validate Codex `.toml` agent files for Anthropic-flavored `model` pins and
|
|
201
|
+
* orphaned `model_reasoning_effort` values (ADR-2313 D6, #3242).
|
|
202
|
+
*
|
|
203
|
+
* A new sibling export to {@link checkAgentsInstalled}, deliberately — that
|
|
204
|
+
* function carries 33 upstream dependents and cyclomatic complexity 25, so this
|
|
205
|
+
* posture check gets zero new branches there (see 40-design.md "Rejected" #1).
|
|
206
|
+
* Presence is `checkAgentsInstalled`'s job; this function's job starts only once
|
|
207
|
+
* the runtime is confirmed `codex` and only inspects posture, never presence.
|
|
208
|
+
*
|
|
209
|
+
* Read-only: detects, never repairs (repair is Phase 3, #3243).
|
|
210
|
+
*
|
|
211
|
+
* @param runtime - the active runtime name; defaults to GSD_RUNTIME env, then 'claude'
|
|
212
|
+
* @param projectRoot - canonical project root for local-install discovery
|
|
213
|
+
*/
|
|
214
|
+
function checkCodexModelPosture(runtime, projectRoot) {
|
|
215
|
+
// Short-circuit BEFORE any filesystem access: a non-codex runtime must never
|
|
216
|
+
// have its agents directory resolved or a stray .toml inspected, however
|
|
217
|
+
// violating that file's contents would be if it were ever read (#3242 row 25).
|
|
218
|
+
const resolvedRuntime = runtime ?? (process.env['GSD_RUNTIME'] || 'claude');
|
|
219
|
+
if (resolvedRuntime !== 'codex') {
|
|
220
|
+
return {
|
|
221
|
+
ok: true,
|
|
222
|
+
violations: [],
|
|
223
|
+
checked: [],
|
|
224
|
+
agents_dir: '',
|
|
225
|
+
agent_runtime: resolvedRuntime,
|
|
226
|
+
reason: POSTURE_REASON.NOT_CODEX,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const agentsDir = getAgentsDir(resolvedRuntime, projectRoot);
|
|
230
|
+
if (!node_fs_1.default.existsSync(agentsDir)) {
|
|
231
|
+
// Presence is checkAgentsInstalled's job — an absent agents dir here is a
|
|
232
|
+
// distinct, non-violating outcome, not a failure of this check.
|
|
233
|
+
return {
|
|
234
|
+
ok: true,
|
|
235
|
+
violations: [],
|
|
236
|
+
checked: [],
|
|
237
|
+
agents_dir: agentsDir,
|
|
238
|
+
agent_runtime: resolvedRuntime,
|
|
239
|
+
reason: POSTURE_REASON.AGENTS_DIR_MISSING,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
// Skip symlinks — matches cmdEffortSync's existing idiom in commands.cts
|
|
243
|
+
// ("Skip symlinks — only write regular files..."). Here the risk is reading
|
|
244
|
+
// (not writing) through a symlink: readFileSync follows symlinks, so an
|
|
245
|
+
// agents-dir symlink pointing at an arbitrary file would let that target's
|
|
246
|
+
// contents be echoed into this function's `value` field. A symlinked agent
|
|
247
|
+
// file is a structural install choice (checkAgentsInstalled's territory),
|
|
248
|
+
// not a model-content posture defect, so it is silently excluded from
|
|
249
|
+
// `checked` rather than reported as a distinct violation — same shape as
|
|
250
|
+
// cmdEffortSync, which silently drops symlinks from its file list rather
|
|
251
|
+
// than inventing a new skip/violation reason.
|
|
252
|
+
const tomlFiles = node_fs_1.default
|
|
253
|
+
.readdirSync(agentsDir)
|
|
254
|
+
.filter((entry) => {
|
|
255
|
+
if (!entry.endsWith('.toml'))
|
|
256
|
+
return false;
|
|
257
|
+
try {
|
|
258
|
+
return node_fs_1.default.lstatSync(node_path_1.default.join(agentsDir, entry)).isFile();
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
.sort();
|
|
265
|
+
const checked = [];
|
|
266
|
+
const violations = [];
|
|
267
|
+
for (const entry of tomlFiles) {
|
|
268
|
+
const agentName = entry.slice(0, -'.toml'.length);
|
|
269
|
+
const filePath = node_path_1.default.join(agentsDir, entry);
|
|
270
|
+
checked.push(agentName);
|
|
271
|
+
let raw;
|
|
272
|
+
try {
|
|
273
|
+
raw = node_fs_1.default.readFileSync(filePath, 'utf8');
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
// Never throws, never silently skips — an unreadable file is reported and
|
|
277
|
+
// the loop continues checking the rest (40-design.md "Rejected" #5).
|
|
278
|
+
violations.push({ agent: agentName, file: filePath, reason: POSTURE_REASON.UNREADABLE });
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
const { model, hasReasoningEffort } = (0, codex_agent_toml_cjs_1.scanTomlLines)((0, codex_agent_toml_cjs_1.stripBOM)(raw));
|
|
282
|
+
if (model !== null && (0, model_catalog_cjs_1.isAnthropicFlavoredModel)(model)) {
|
|
283
|
+
violations.push({
|
|
284
|
+
agent: agentName,
|
|
285
|
+
file: filePath,
|
|
286
|
+
reason: POSTURE_REASON.ANTHROPIC_FLAVORED_MODEL,
|
|
287
|
+
value: truncatePostureValue(model),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
else if (model === null && hasReasoningEffort) {
|
|
291
|
+
// #838 coupling: a reasoning-effort pin with no model pin means Codex
|
|
292
|
+
// inherits the session model while the effort pin silently disagrees.
|
|
293
|
+
violations.push({
|
|
294
|
+
agent: agentName,
|
|
295
|
+
file: filePath,
|
|
296
|
+
reason: POSTURE_REASON.ORPHANED_REASONING_EFFORT,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
ok: violations.length === 0,
|
|
302
|
+
violations,
|
|
303
|
+
checked,
|
|
304
|
+
agents_dir: agentsDir,
|
|
305
|
+
agent_runtime: resolvedRuntime,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Probe a single agents dir for `<name>` across runtime filename variants.
|
|
310
|
+
* Mirrors {@link checkAgentsInstalled}'s probe (`.md`, `.agent.md`, `.toml`,
|
|
311
|
+
* and the kimi `subagents/<name>.{yaml,md}` pair) so the two can never disagree
|
|
312
|
+
* about which on-disk shapes count as "installed". Not exported — internal to
|
|
313
|
+
* {@link resolveAgentHint}.
|
|
314
|
+
*/
|
|
315
|
+
function agentFileExists(agentsDir, name, runtime) {
|
|
316
|
+
const base = node_path_1.default.join(agentsDir, `${name}.md`);
|
|
317
|
+
const copilot = node_path_1.default.join(agentsDir, `${name}.agent.md`);
|
|
318
|
+
const codex = node_path_1.default.join(agentsDir, `${name}.toml`);
|
|
319
|
+
if (node_fs_1.default.existsSync(base) || node_fs_1.default.existsSync(copilot) || node_fs_1.default.existsSync(codex)) {
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
// kimi requires BOTH the persona yaml and the prompt md (same as checkAgentsInstalled).
|
|
323
|
+
const kimiYaml = node_path_1.default.join(agentsDir, 'subagents', `${name}.yaml`);
|
|
324
|
+
const kimiPrompt = node_path_1.default.join(agentsDir, 'subagents', `${name}.md`);
|
|
325
|
+
return runtime === 'kimi' && node_fs_1.default.existsSync(kimiYaml) && node_fs_1.default.existsSync(kimiPrompt);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Resolve a per-plan `agent_hint` specialist name to a dispatchable subagent
|
|
329
|
+
* type on the active runtime (#1689). Unlike {@link checkAgentsInstalled},
|
|
330
|
+
* which validates the fixed GSD roster, this answers "does an agent file for
|
|
331
|
+
* this ARBITRARY name exist in the active runtime's agent dir(s)?" — so a plan
|
|
332
|
+
* can opt into a domain specialist (e.g. a Flutter engineer) that shares the
|
|
333
|
+
* gsd-executor contract without being part of the built-in roster.
|
|
334
|
+
*
|
|
335
|
+
* Probes BOTH the runtime-canonical agents dir ({@link getAgentsDir}, which
|
|
336
|
+
* honors `GSD_AGENTS_DIR`, project-local manifest-backed installs, and the
|
|
337
|
+
* claude install-relative path) AND the runtime's global config agents dir, so
|
|
338
|
+
* a specialist installed at either level is recognized. The decision in #1689
|
|
339
|
+
* explicitly requires consulting the active runtime's agent dir rather than
|
|
340
|
+
* only the Claude runtime's user-global and project-local agent dirs.
|
|
341
|
+
*
|
|
342
|
+
* @returns the name when a matching agent file exists; `null` when it does not
|
|
343
|
+
* (the caller falls back to `gsd-executor`). An empty/whitespace name always
|
|
344
|
+
* returns `null`.
|
|
345
|
+
*/
|
|
346
|
+
function resolveAgentHint(name, runtime, projectRoot) {
|
|
347
|
+
const trimmed = String(name ?? '').trim();
|
|
348
|
+
if (trimmed === '')
|
|
349
|
+
return null;
|
|
350
|
+
// A hint is a bare agent name. Reject path separators and `..` so a value
|
|
351
|
+
// like `../../README` cannot path-traverse out of the agents dir via
|
|
352
|
+
// path.join and match an unrelated file — that would echo an invalid
|
|
353
|
+
// subagent_type and block the wave, defeating fail-closed resolution.
|
|
354
|
+
if (trimmed.includes('/') || trimmed.includes('\\') || trimmed.includes('..'))
|
|
355
|
+
return null;
|
|
356
|
+
const resolvedRuntime = runtime ?? (process.env['GSD_RUNTIME'] || 'claude');
|
|
357
|
+
const candidateDirs = new Set();
|
|
358
|
+
candidateDirs.add(getAgentsDir(resolvedRuntime, projectRoot));
|
|
359
|
+
candidateDirs.add(node_path_1.default.join((0, runtime_homes_cjs_1.getGlobalConfigDir)(resolvedRuntime), 'agents'));
|
|
360
|
+
for (const dir of candidateDirs) {
|
|
361
|
+
if (agentFileExists(dir, trimmed, resolvedRuntime)) {
|
|
362
|
+
return trimmed;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
140
367
|
module.exports = {
|
|
141
368
|
getAgentsDir,
|
|
142
369
|
checkAgentsInstalled,
|
|
370
|
+
checkCodexModelPosture,
|
|
371
|
+
POSTURE_REASON,
|
|
372
|
+
resolveAgentHint,
|
|
143
373
|
};
|