@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
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* installed-surface-resolver.cts — Installed Surface Resolver Module (#2872,
|
|
4
|
+
* ADR-2866, Phase 3 — governed by
|
|
5
|
+
* `.gsd/phase/feat-2872-manifest-scope-runtime/40-design.md`).
|
|
6
|
+
*
|
|
7
|
+
* `resolveInstalledSurfaces(runtime?, opts?)` answers a question no existing
|
|
8
|
+
* module answers: "which surfaces actually exist on THIS machine, across
|
|
9
|
+
* BOTH install scopes, right now?" `capability-state.cts` answers "which
|
|
10
|
+
* capabilities are enabled in THIS config dir" — a different question at a
|
|
11
|
+
* different altitude. This module is a new leaf: it reads two scopes at
|
|
12
|
+
* once, composing three already-shipped, already-tested pieces
|
|
13
|
+
* (`resolveScope`, Phase 1; `resolveTriggerSurface`, Phase 2; and
|
|
14
|
+
* `readInstallManifest`, widened in this same phase) rather than
|
|
15
|
+
* re-implementing any of their rules.
|
|
16
|
+
*
|
|
17
|
+
* ── Probe-not-declared keying ───────────────────────────────────────────────
|
|
18
|
+
* A scope RECORD is keyed by the PROBED scope (`resolveScope`'s own `id`),
|
|
19
|
+
* never by what the manifest itself claims. `manifestVersion`/`runtime`/
|
|
20
|
+
* `scope` recorded inside a v2 manifest are corroboration, not identity: a
|
|
21
|
+
* manifest copied between config dirs, or a `--config-dir` install, must
|
|
22
|
+
* still be reported at the scope this machine actually resolves to.
|
|
23
|
+
*
|
|
24
|
+
* ── Report, don't correct ───────────────────────────────────────────────────
|
|
25
|
+
* When a declared `runtime`/`scope` disagrees with the probe, this module
|
|
26
|
+
* reports the mismatch (`declaredScopeMatchesProbe: false` /
|
|
27
|
+
* `declaredRuntimeMatchesProbe: false`) and otherwise proceeds exactly as it
|
|
28
|
+
* would for an undeclared (v1) manifest. It never silently substitutes the
|
|
29
|
+
* declared value for the probed one, and never throws on a mismatch — see
|
|
30
|
+
* B-row "Postel's Law" discussion in the design doc.
|
|
31
|
+
*
|
|
32
|
+
* ── One trigger call over installed scopes only ─────────────────────────────
|
|
33
|
+
* `resolveTriggerSurface` is called AT MOST once per runtime, with the
|
|
34
|
+
* scopes that are actually installed on this machine (per manifest
|
|
35
|
+
* presence — never per the new corroboration fields, which is what keeps a
|
|
36
|
+
* v1-only install fully functional with no reinstall required). A
|
|
37
|
+
* hypothetical "what if both scopes were installed" answer is deliberately
|
|
38
|
+
* not offered; #2218 is a fact about THIS machine, not a simulation.
|
|
39
|
+
*
|
|
40
|
+
* ── Installed-ness is manifest PRESENCE, never the new fields ───────────────
|
|
41
|
+
* `installed` is `manifestVersion !== null`. A v1 manifest (no
|
|
42
|
+
* `manifestVersion` key at all, `manifestVersion: 1` after normalization) is
|
|
43
|
+
* a correct manifest written by an older GSD — never a broken one, never
|
|
44
|
+
* grounds for `installed: false`, a warning, or a reinstall prompt.
|
|
45
|
+
*
|
|
46
|
+
* ── Stems come from the manifest, not the source tree ───────────────────────
|
|
47
|
+
* `resolveTriggerSurface` needs `stems`. They are derived from the
|
|
48
|
+
* INSTALLED manifest's own `files` keys (see the private stem-derivation
|
|
49
|
+
* helpers below), never from a roster read of the source tree — a global
|
|
50
|
+
* `claude` install ships no `commands/gsd` source, so a roster read would
|
|
51
|
+
* return `[]` in exactly the configuration #2218 is about. The derivation
|
|
52
|
+
* consumes `isNamespacedByDir` and `composeCommandFilename`
|
|
53
|
+
* (`runtime-artifact-layout.cjs`, #2871 Phase 2's exported helpers) rather
|
|
54
|
+
* than re-deriving either rule as a fourth independent copy.
|
|
55
|
+
*/
|
|
56
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
57
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
58
|
+
};
|
|
59
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
60
|
+
exports.resolveInstalledSurfaces = resolveInstalledSurfaces;
|
|
61
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
62
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
63
|
+
const install_scope_cjs_1 = require("./install-scope.cjs");
|
|
64
|
+
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
66
|
+
const runtimeArtifactLayoutMod = require("./runtime-artifact-layout.cjs");
|
|
67
|
+
const { resolveRuntimeArtifactLayout, resolveRuntimeArtifactLayoutFromRegistry, resolveTriggerSurface, isNamespacedByDir, composeCommandFilename, } = runtimeArtifactLayoutMod;
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
69
|
+
const installerMigrationsMod = require("./installer-migrations.cjs");
|
|
70
|
+
const { readInstallManifest, MANIFEST_NAME } = installerMigrationsMod;
|
|
71
|
+
// In .cts (CommonJS output) files, `require` is available as a global.
|
|
72
|
+
const _require = require;
|
|
73
|
+
// ── Internals ───────────────────────────────────────────────────────────
|
|
74
|
+
/**
|
|
75
|
+
* A trigger stem is a kebab-case token and nothing else. Verified against the
|
|
76
|
+
* real roster: all 71 `commands/gsd/*.md` stems match this, so it has no false
|
|
77
|
+
* negatives today.
|
|
78
|
+
*
|
|
79
|
+
* This is a SECURITY boundary, not cosmetics. A stem is derived from a manifest
|
|
80
|
+
* key — attacker-influenceable text, since a project-local
|
|
81
|
+
* `gsd-file-manifest.json` lives inside a repository a user may merely have
|
|
82
|
+
* cloned — and Phase 4 (#2873) renders it back to the user as `/gsd-<stem>`.
|
|
83
|
+
* Without this, `skills/gsd-../../../x` yields the stem `..`, and control
|
|
84
|
+
* characters, newlines, ANSI escapes and RTL-override codepoints all survive
|
|
85
|
+
* into that rendered trigger. The `commands` branch happened to be protected by
|
|
86
|
+
* its `composeCommandFilename` round-trip; the `skills` branch had no
|
|
87
|
+
* equivalent, so the rule is stated once here and applied to both.
|
|
88
|
+
*/
|
|
89
|
+
const SAFE_STEM = /^[a-z0-9][a-z0-9-]*$/;
|
|
90
|
+
function getDefaultRuntimeRegistry() {
|
|
91
|
+
return _require('./capability-registry.cjs');
|
|
92
|
+
}
|
|
93
|
+
/** C7: every registered runtime id, sorted for determinism. Reads
|
|
94
|
+
* `opts.registry` when provided (so a sweep is assertable without the real
|
|
95
|
+
* capability registry), else the real one. */
|
|
96
|
+
function listRegisteredRuntimeIds(opts) {
|
|
97
|
+
const registry = opts.registry ?? getDefaultRuntimeRegistry();
|
|
98
|
+
return Object.keys(registry.runtimes).sort();
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* D — the stem inverse for one `commands`/`skills` layout kind entry.
|
|
102
|
+
* Consumes `isNamespacedByDir` (to decide the commands filename shape) and
|
|
103
|
+
* `composeCommandFilename` (to VERIFY a candidate stem round-trips to the
|
|
104
|
+
* exact filename seen) rather than re-deriving either rule locally (design
|
|
105
|
+
* "Rejected" #6). Manifest keys are normalized with an unconditional
|
|
106
|
+
* `.replace(/\\/g, '/')` (D7) — never gated on `path.sep` — matching this
|
|
107
|
+
* repo's recorded path-separator-normalization defect class.
|
|
108
|
+
*/
|
|
109
|
+
function deriveStemsForKindEntry(kind, destSubpath, prefix, fileKeys) {
|
|
110
|
+
const destSubpathNorm = (0, shell_command_projection_cjs_1.posixNormalize)(destSubpath);
|
|
111
|
+
const boundary = `${destSubpathNorm}/`;
|
|
112
|
+
const namespacedByDir = isNamespacedByDir(kind, destSubpath, prefix);
|
|
113
|
+
const stems = new Set();
|
|
114
|
+
for (const rawKey of fileKeys) {
|
|
115
|
+
const key = rawKey.replace(/\\/g, '/');
|
|
116
|
+
if (!key.startsWith(boundary))
|
|
117
|
+
continue; // D4: key not under this declared subpath
|
|
118
|
+
const rest = key.slice(boundary.length);
|
|
119
|
+
if (rest === '')
|
|
120
|
+
continue;
|
|
121
|
+
const segments = rest.split('/');
|
|
122
|
+
if (kind === 'skills') {
|
|
123
|
+
const dirSegment = segments[0];
|
|
124
|
+
if (!dirSegment.startsWith(prefix))
|
|
125
|
+
continue; // D5: subpath matches, prefix does not
|
|
126
|
+
const stem = dirSegment.slice(prefix.length);
|
|
127
|
+
if (stem === '')
|
|
128
|
+
continue; // D7/B7: empty stem never emitted
|
|
129
|
+
if (!SAFE_STEM.test(stem))
|
|
130
|
+
continue; // security: reject anything but a kebab-case token
|
|
131
|
+
stems.add(stem); // D6: several files under one dir -> one stem
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
// commands: exactly one path segment — the composed filename itself.
|
|
135
|
+
if (segments.length !== 1)
|
|
136
|
+
continue;
|
|
137
|
+
const filename = segments[0];
|
|
138
|
+
if (!filename.endsWith('.md'))
|
|
139
|
+
continue;
|
|
140
|
+
const base = filename.slice(0, -3);
|
|
141
|
+
const candidateStem = namespacedByDir
|
|
142
|
+
? base
|
|
143
|
+
: (base.startsWith(prefix) ? base.slice(prefix.length) : '');
|
|
144
|
+
if (candidateStem === '')
|
|
145
|
+
continue;
|
|
146
|
+
if (composeCommandFilename(namespacedByDir, prefix, candidateStem) !== filename)
|
|
147
|
+
continue;
|
|
148
|
+
if (!SAFE_STEM.test(candidateStem))
|
|
149
|
+
continue; // security: reject anything but a kebab-case token
|
|
150
|
+
stems.add(candidateStem);
|
|
151
|
+
}
|
|
152
|
+
return [...stems];
|
|
153
|
+
}
|
|
154
|
+
/** D — full stem set for one scope: every `commands`/`skills` layout kind
|
|
155
|
+
* entry, unioned. Empty `files` (C13) short-circuits to `[]` without
|
|
156
|
+
* resolving a layout at all. */
|
|
157
|
+
function deriveStemsFromManifest(runtime, scopeId, configHome, files, opts) {
|
|
158
|
+
const fileKeys = Object.keys(files);
|
|
159
|
+
if (fileKeys.length === 0)
|
|
160
|
+
return [];
|
|
161
|
+
const layout = opts.registry !== undefined
|
|
162
|
+
? resolveRuntimeArtifactLayoutFromRegistry(opts.registry, runtime, configHome, scopeId)
|
|
163
|
+
: resolveRuntimeArtifactLayout(runtime, configHome, scopeId);
|
|
164
|
+
const stems = new Set();
|
|
165
|
+
for (const kindEntry of layout.kinds) {
|
|
166
|
+
if (kindEntry.kind !== 'commands' && kindEntry.kind !== 'skills')
|
|
167
|
+
continue; // excludes agents/kimi-agents (D4)
|
|
168
|
+
for (const stem of deriveStemsForKindEntry(kindEntry.kind, kindEntry.destSubpath, kindEntry.prefix, fileKeys)) {
|
|
169
|
+
stems.add(stem);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return [...stems].sort();
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* True when `p` is a symlink. An `lstatSync` throw (ENOENT — nothing at this
|
|
176
|
+
* path) is NOT evidence of a symlink; it is treated as "not a symlink" here
|
|
177
|
+
* and left for `readManifest` to classify (it already owns the absent-file
|
|
178
|
+
* case, per C14 above).
|
|
179
|
+
*/
|
|
180
|
+
function isSymlinkPath(p, lstatSync) {
|
|
181
|
+
try {
|
|
182
|
+
return lstatSync(p).isSymbolicLink();
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* The shared "not installed" degraded shape (C14's EACCES path, and this
|
|
190
|
+
* phase's new symlink-guard path). A FACTORY, not a module-level constant
|
|
191
|
+
* object: a `const` object spread at each return site would still share the
|
|
192
|
+
* same `stems` ARRAY reference across every call (`...` shallow-copies the
|
|
193
|
+
* object but not the array a property points at), which would violate this
|
|
194
|
+
* module's own "builds fresh arrays/objects on every call" contract (C15) —
|
|
195
|
+
* a caller mutating one degraded record's `stems` must never be visible on
|
|
196
|
+
* another's.
|
|
197
|
+
*/
|
|
198
|
+
function notInstalledScopeRecordFields() {
|
|
199
|
+
return {
|
|
200
|
+
installed: false,
|
|
201
|
+
manifestVersion: null,
|
|
202
|
+
declaredRuntime: null,
|
|
203
|
+
declaredScope: null,
|
|
204
|
+
declaredScopeMatchesProbe: null,
|
|
205
|
+
declaredRuntimeMatchesProbe: null,
|
|
206
|
+
stems: [],
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Build one scope's record. `resolvedConfigHome` has already been probed
|
|
211
|
+
* successfully by the time this is called (a `resolveScope` `TypeError` is
|
|
212
|
+
* handled by the caller, per C8 — it is never this function's concern).
|
|
213
|
+
*
|
|
214
|
+
* Two distinct failure modes are handled with two distinct, narrowly-scoped
|
|
215
|
+
* try/catches — NOT one catch-all around the whole function:
|
|
216
|
+
*
|
|
217
|
+
* - The manifest READ (`readManifest`) is the only thing that can justify
|
|
218
|
+
* `installed: false` (C14, e.g. EACCES). A failure here means "we could
|
|
219
|
+
* not even tell whether this scope is installed".
|
|
220
|
+
* - The stem DERIVATION (`deriveStemsFromManifest`, which resolves a
|
|
221
|
+
* runtime artifact layout) is a separate concern. A `TypeError` thrown
|
|
222
|
+
* while deriving stems is a layout-lookup failure, not evidence the
|
|
223
|
+
* manifest is absent — the manifest was already read successfully, so
|
|
224
|
+
* `installed` and every declared/*MatchesProbe field stay exactly as the
|
|
225
|
+
* manifest reported. Conflating the two would report a genuinely
|
|
226
|
+
* installed runtime as `installed: false`, hiding a real install from
|
|
227
|
+
* #2218 shadow detection precisely when this module exists to surface it.
|
|
228
|
+
*/
|
|
229
|
+
function buildScopeRecord(runtime, scopeId, resolvedConfigHome, opts) {
|
|
230
|
+
// Hardening requirement 2 (#2873 design doc, "Hardening requirements
|
|
231
|
+
// claimed from #2873's comment"): `readInstallManifest` -> `readJsonIfPresent`
|
|
232
|
+
// uses `existsSync` + `readFileSync` and therefore FOLLOWS symlinks, and
|
|
233
|
+
// this module resolves `local` against `process.cwd()` — a directory that,
|
|
234
|
+
// as of this phase, becomes reachable from an arbitrary cloned repository
|
|
235
|
+
// (this is the same phase that makes the local scope's manifest a first
|
|
236
|
+
// read target, not merely a write target). The in-tree precedent is
|
|
237
|
+
// `getAgentsDir` (`agent-install-check.cts`), which probes with
|
|
238
|
+
// `fs.lstatSync(...).isDirectory()`/`.isFile()` and deliberately does not
|
|
239
|
+
// follow. #2872 left this resolver's read un-guarded only because the path
|
|
240
|
+
// had zero callers at the time; refusing to follow a symlinked config dir
|
|
241
|
+
// or manifest here closes that asymmetry rather than carrying it forward.
|
|
242
|
+
// Degrading to `installed: false` reuses the SAME shape the EACCES catch
|
|
243
|
+
// below already returns — no new failure shape is introduced.
|
|
244
|
+
const lstatSync = opts.lstatSync ?? node_fs_1.default.lstatSync;
|
|
245
|
+
const manifestPath = node_path_1.default.join(resolvedConfigHome, MANIFEST_NAME);
|
|
246
|
+
if (isSymlinkPath(resolvedConfigHome, lstatSync) || isSymlinkPath(manifestPath, lstatSync)) {
|
|
247
|
+
return { scope: scopeId, configHome: resolvedConfigHome, ...notInstalledScopeRecordFields() };
|
|
248
|
+
}
|
|
249
|
+
let manifest;
|
|
250
|
+
try {
|
|
251
|
+
const readManifest = opts.readManifest ?? readInstallManifest;
|
|
252
|
+
manifest = readManifest(resolvedConfigHome);
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// C14: manifest read/probe failure degrades to not-installed, never throws.
|
|
256
|
+
// Deliberately a BARE catch, unlike the stem-derivation catch below: any
|
|
257
|
+
// read failure at all (EACCES, ENOENT-after-race, a corrupt filesystem)
|
|
258
|
+
// legitimately means "cannot tell whether installed" (design row C14), so
|
|
259
|
+
// there is no error TYPE here that should instead propagate.
|
|
260
|
+
return { scope: scopeId, configHome: resolvedConfigHome, ...notInstalledScopeRecordFields() };
|
|
261
|
+
}
|
|
262
|
+
const installed = manifest.manifestVersion !== null; // C9: presence, never the new fields
|
|
263
|
+
const declaredRuntime = manifest.runtime;
|
|
264
|
+
const declaredScope = manifest.scope;
|
|
265
|
+
const declaredRuntimeMatchesProbe = declaredRuntime === null ? null : declaredRuntime === runtime;
|
|
266
|
+
const declaredScopeMatchesProbe = declaredScope === null ? null : declaredScope === scopeId;
|
|
267
|
+
let stems = [];
|
|
268
|
+
if (installed) {
|
|
269
|
+
try {
|
|
270
|
+
stems = deriveStemsFromManifest(runtime, scopeId, resolvedConfigHome, manifest.files, opts);
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
if (!(error instanceof TypeError))
|
|
274
|
+
throw error;
|
|
275
|
+
// A layout-lookup failure means "we could not enumerate this scope's
|
|
276
|
+
// triggers", NOT "this scope is not installed" — the manifest read
|
|
277
|
+
// already succeeded above, so `installed` and the declared fields
|
|
278
|
+
// stay as reported. Conflating the two would hide a real install.
|
|
279
|
+
// Anything that is not the expected `TypeError` (a genuine bug in
|
|
280
|
+
// `deriveStemsFromManifest`/`resolveRuntimeArtifactLayout`) is
|
|
281
|
+
// rethrown rather than silently degrading to `stems: []` — this
|
|
282
|
+
// matches `resolveInstalledSurfaces`'s own `TypeError` narrowing
|
|
283
|
+
// below, so the two catches cannot drift apart.
|
|
284
|
+
stems = [];
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
scope: scopeId,
|
|
289
|
+
configHome: resolvedConfigHome,
|
|
290
|
+
installed,
|
|
291
|
+
manifestVersion: manifest.manifestVersion,
|
|
292
|
+
declaredRuntime,
|
|
293
|
+
declaredScope,
|
|
294
|
+
declaredScopeMatchesProbe,
|
|
295
|
+
declaredRuntimeMatchesProbe,
|
|
296
|
+
stems,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Resolve one runtime's full installed surface. A `resolveScope` `TypeError`
|
|
301
|
+
* (unknown runtime, or `configHome.kind === 'none'`, e.g. vscode) propagates
|
|
302
|
+
* from here uncaught — `resolveInstalledSurfaces` decides whether that
|
|
303
|
+
* means "skip" (C7 sweep) or "propagate" (explicit single-runtime ask, C8).
|
|
304
|
+
*/
|
|
305
|
+
function resolveOneRuntime(runtime, opts) {
|
|
306
|
+
const scopes = install_scope_cjs_1.SCOPE_ORDER.map((scopeId) => {
|
|
307
|
+
const resolved = (0, install_scope_cjs_1.resolveScope)({
|
|
308
|
+
id: scopeId,
|
|
309
|
+
runtime,
|
|
310
|
+
env: opts.env,
|
|
311
|
+
home: opts.home,
|
|
312
|
+
existsSync: opts.existsSync,
|
|
313
|
+
cwd: opts.cwd,
|
|
314
|
+
});
|
|
315
|
+
return buildScopeRecord(runtime, scopeId, resolved.configHome, opts);
|
|
316
|
+
});
|
|
317
|
+
// C12: dedupe by resolved configHome BEFORE the trigger call, keeping the
|
|
318
|
+
// higher-ranked (global, first in SCOPE_ORDER) scope. Both scope RECORDS
|
|
319
|
+
// above are unaffected — only the scope list handed to resolveTriggerSurface
|
|
320
|
+
// is deduped.
|
|
321
|
+
const seenConfigHomes = new Set();
|
|
322
|
+
const triggerScopeIds = [];
|
|
323
|
+
for (const record of scopes) {
|
|
324
|
+
if (!record.installed)
|
|
325
|
+
continue;
|
|
326
|
+
if (seenConfigHomes.has(record.configHome))
|
|
327
|
+
continue;
|
|
328
|
+
seenConfigHomes.add(record.configHome);
|
|
329
|
+
triggerScopeIds.push(record.scope);
|
|
330
|
+
}
|
|
331
|
+
const stemUnion = new Set();
|
|
332
|
+
for (const scopeId of triggerScopeIds) {
|
|
333
|
+
const record = scopes.find((s) => s.scope === scopeId);
|
|
334
|
+
for (const stem of record?.stems ?? [])
|
|
335
|
+
stemUnion.add(stem);
|
|
336
|
+
}
|
|
337
|
+
const triggers = triggerScopeIds.length > 0
|
|
338
|
+
? resolveTriggerSurface(runtime, triggerScopeIds, {
|
|
339
|
+
stems: [...stemUnion].sort(),
|
|
340
|
+
registry: opts.registry,
|
|
341
|
+
})
|
|
342
|
+
: [];
|
|
343
|
+
return { runtime, scopes, triggers };
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Read-only. Probes both install scopes for a runtime (or every registered
|
|
347
|
+
* runtime, sorted by id, when `runtime` is omitted), reads each scope's
|
|
348
|
+
* manifest, and resolves the trigger surface across the scopes that are
|
|
349
|
+
* actually installed on this machine. See the module-level comment for the
|
|
350
|
+
* non-obvious choices (probe-not-declared keying, report-don't-correct, one
|
|
351
|
+
* trigger call over installed scopes only).
|
|
352
|
+
*
|
|
353
|
+
* Pure with respect to caller-visible state: builds fresh arrays/objects on
|
|
354
|
+
* every call (C15) and performs no writes. Filesystem reads happen only via
|
|
355
|
+
* `resolveScope`'s injected `existsSync`/`env`/`home`/`cwd` and via
|
|
356
|
+
* `readManifest` (default: `readInstallManifest`).
|
|
357
|
+
*
|
|
358
|
+
* @throws {TypeError} when `runtime` is given explicitly and it is unknown,
|
|
359
|
+
* or has no installable config directory (`configHome.kind === 'none'`,
|
|
360
|
+
* e.g. vscode) — same contract `resolveScope` throws. In the all-runtimes
|
|
361
|
+
* sweep (`runtime` omitted), a runtime that would throw this same
|
|
362
|
+
* `TypeError` is skipped instead, so one non-installable runtime cannot
|
|
363
|
+
* kill the sweep (C7/C8). Any other error type is never swallowed here.
|
|
364
|
+
*/
|
|
365
|
+
function resolveInstalledSurfaces(runtime, opts = {}) {
|
|
366
|
+
if (typeof runtime === 'string') {
|
|
367
|
+
return [resolveOneRuntime(runtime, opts)];
|
|
368
|
+
}
|
|
369
|
+
const results = [];
|
|
370
|
+
for (const runtimeId of listRegisteredRuntimeIds(opts)) {
|
|
371
|
+
try {
|
|
372
|
+
results.push(resolveOneRuntime(runtimeId, opts));
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
if (error instanceof TypeError)
|
|
376
|
+
continue; // C8: sweep skips, never dies
|
|
377
|
+
throw error;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return results;
|
|
381
|
+
}
|
|
@@ -109,7 +109,9 @@ function validateInstallerMigrationActions(actions, migration) {
|
|
|
109
109
|
// Ownership and runtime-contract evidence are required by
|
|
110
110
|
// docs/installer-migrations.md#action-types and
|
|
111
111
|
// docs/adr/0008-installer-migration-module.md#runtime-contract-decision.
|
|
112
|
-
|
|
112
|
+
// `remove-empty-dir` carries the same evidence bar as `remove-managed`: it is
|
|
113
|
+
// still a destructive removal, just of a directory node instead of a file.
|
|
114
|
+
if (actType === 'remove-managed' || actType === 'rewrite-json' || actType === 'remove-empty-dir') {
|
|
113
115
|
requireActionEvidence(act, 'ownershipEvidence', migration);
|
|
114
116
|
}
|
|
115
117
|
if (actType === 'rewrite-json') {
|
|
@@ -32,6 +32,7 @@ const VALID_CHOICES = ['keep', 'remove'];
|
|
|
32
32
|
// on-disk `hooks/` directory in both directions: whitelist-but-missing
|
|
33
33
|
// AND shipped-but-not-whitelisted both fail CI.
|
|
34
34
|
exports.BUNDLED_GSD_HOOK_FILES = Object.freeze(new Set([
|
|
35
|
+
'hooks/gsd-agent-isolation-guard.js',
|
|
35
36
|
'hooks/gsd-check-update-worker.js',
|
|
36
37
|
'hooks/gsd-check-update.js',
|
|
37
38
|
'hooks/gsd-config-reload.js',
|
|
@@ -57,6 +58,7 @@ exports.BUNDLED_GSD_HOOK_FILES = Object.freeze(new Set([
|
|
|
57
58
|
'hooks/gsd-validate-commit.sh',
|
|
58
59
|
'hooks/gsd-workflow-guard.js',
|
|
59
60
|
'hooks/gsd-worktree-path-guard.js',
|
|
61
|
+
'hooks/gsd-write-guard.js',
|
|
60
62
|
]));
|
|
61
63
|
// ── Internal helpers ──────────────────────────────────────────────────────────
|
|
62
64
|
function installerMigrationActionLabel(action) {
|
|
@@ -74,6 +76,8 @@ function installerMigrationActionLabel(action) {
|
|
|
74
76
|
return 'preserved';
|
|
75
77
|
if (action.type === 'preserve-user')
|
|
76
78
|
return 'preserved';
|
|
79
|
+
if (action.type === 'remove-empty-dir')
|
|
80
|
+
return 'removed';
|
|
77
81
|
if (action.type === 'prompt-user')
|
|
78
82
|
return 'blocked';
|
|
79
83
|
return 'skipped';
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Installer migration: retire the config-root `{"type":"commonjs"}` marker that
|
|
4
|
+
* pre-#2544 installs wrote over `<configRoot>/package.json`.
|
|
5
|
+
*
|
|
6
|
+
* What old artifact is being retired?
|
|
7
|
+
* `package.json` at the runtime config root. Before #2544,
|
|
8
|
+
* `installSharedHooksBundle(destRootDir)` wrote `{"type":"commonjs"}` there
|
|
9
|
+
* unconditionally on every install and every re-install, to pin GSD's staged
|
|
10
|
+
* `.js` hook scripts to CommonJS via Node's ancestor walk. #2544 moved that
|
|
11
|
+
* marker into the directories GSD actually fills (`hooks/`, and the
|
|
12
|
+
* `nativePlugin.dir` for runtimes declaring one) and stopped writing the
|
|
13
|
+
* config root at all. Without this migration an upgrader keeps BOTH markers:
|
|
14
|
+
* the new one under `hooks/` and the stale one at the root, so the config
|
|
15
|
+
* root stays pinned to CommonJS and the PR's own claim — that GSD no longer
|
|
16
|
+
* writes the shared config root — is false for every install made since the
|
|
17
|
+
* marker was introduced, until the user uninstalls.
|
|
18
|
+
*
|
|
19
|
+
* How do we prove it is GSD-owned?
|
|
20
|
+
* By exact content match, NOT by the manifest. The config-root marker was
|
|
21
|
+
* never recorded in `gsd-file-manifest.json` — `writeManifest()` records
|
|
22
|
+
* `hooks/`, `agents/`, `commands/`, `scripts/`, and the native plugin, and
|
|
23
|
+
* has never had a `manifest.files['package.json']` entry — so
|
|
24
|
+
* `classifyArtifact('package.json')` answers `unknown` and the planner's
|
|
25
|
+
* own guard would downgrade a `remove-managed` to `preserve-user`.
|
|
26
|
+
* This migration therefore supplies the "purpose-built detector for an old
|
|
27
|
+
* GSD-owned shape" that `docs/installer-migrations.md#remove-managed`
|
|
28
|
+
* sanctions, and declares the resulting classification on the action: the
|
|
29
|
+
* file is removed only when its bytes are exactly the marker GSD writes
|
|
30
|
+
* (`{"type":"commonjs"}`, trailing whitespace tolerated). That is the same
|
|
31
|
+
* predicate `removeCommonJsMarker` has always used on the uninstall side, so
|
|
32
|
+
* install, uninstall, and migration cannot drift apart.
|
|
33
|
+
*
|
|
34
|
+
* What happens if the user modified it?
|
|
35
|
+
* Then it is not the marker, and this migration does not touch it. Any
|
|
36
|
+
* `package.json` carrying a `name`, `dependencies`, `scripts`, or any key
|
|
37
|
+
* beyond the single `type` — i.e. every file the #2544 defect destroyed —
|
|
38
|
+
* fails the exact-content test and is left exactly as found. There is
|
|
39
|
+
* deliberately no `backup-and-remove` branch: a modified file here is not a
|
|
40
|
+
* patched GSD artifact, it is somebody else's file.
|
|
41
|
+
*
|
|
42
|
+
* What happens if it is missing?
|
|
43
|
+
* No actions. Fresh post-#2544 installs never wrote it, already-migrated
|
|
44
|
+
* installs no longer have it, and the executor additionally journals a
|
|
45
|
+
* `missing` outcome if it disappears between plan and apply. Idempotent.
|
|
46
|
+
*
|
|
47
|
+
* What runtime and scope does it affect?
|
|
48
|
+
* Every runtime whose config root received the marker — i.e. every runtime
|
|
49
|
+
* not excluded from `installSharedHooksBundle(targetDir)` by
|
|
50
|
+
* `hostBehaviors.skipSharedHooksInstall` and not Codex: antigravity,
|
|
51
|
+
* augment, claude, claude-local, codebuddy, hermes, qwen, kilo, opencode,
|
|
52
|
+
* and pi. The `runtimes` field is OMITTED — the framework's "all runtimes" —
|
|
53
|
+
* rather than carrying that hand-list: a runtime that never received the
|
|
54
|
+
* marker simply has no file to match, so enumerating them would add a second
|
|
55
|
+
* place for the set to drift out of date without changing behavior. Note it
|
|
56
|
+
* must be omitted and not `[]`; see the field's own comment below.
|
|
57
|
+
*
|
|
58
|
+
* ONE DELIBERATE CARVE-OUT — kimi. Kimi's marker was written to its native
|
|
59
|
+
* hook root (`~/.kimi`, `resolveKimiHooksTomlDir`), which is NOT under
|
|
60
|
+
* kimi's `configDir` (its generic Agent-Skills root). Migration relPaths are
|
|
61
|
+
* structurally confined to `configDir` (`validateSafeRelPath` /
|
|
62
|
+
* `ensureInsideConfig`), so this framework cannot address that path at all.
|
|
63
|
+
* Kimi's stale root marker is retired by the installer instead, at the same
|
|
64
|
+
* call site that writes its replacement — see the `kimi-hooks-toml` branch
|
|
65
|
+
* in `bin/install.js`. Named here so the gap is not mistaken for an
|
|
66
|
+
* oversight.
|
|
67
|
+
*
|
|
68
|
+
* Is the action safe in non-interactive install?
|
|
69
|
+
* Yes. `remove-managed` is non-interactive and journaled, the executor takes
|
|
70
|
+
* a rollback snapshot before unlinking, and no branch of this migration can
|
|
71
|
+
* emit `prompt-user`. A file that is not byte-identical to GSD's marker
|
|
72
|
+
* produces no action at all.
|
|
73
|
+
*
|
|
74
|
+
* See docs/installer-migrations.md#shipped-migrations and #action-types.
|
|
75
|
+
*/
|
|
76
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
77
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
78
|
+
};
|
|
79
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
80
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
81
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
82
|
+
/** The config-root path the pre-#2544 installer wrote, relative to configDir. */
|
|
83
|
+
const STALE_ROOT_MARKER = 'package.json';
|
|
84
|
+
/**
|
|
85
|
+
* The exact marker content GSD wrote. Duplicated as a literal rather than
|
|
86
|
+
* imported from `src/commonjs-marker.cts` on purpose: a migration record is a
|
|
87
|
+
* frozen historical statement about what a PAST version installed, and it must
|
|
88
|
+
* keep matching those bytes even if the live module's constant is ever changed.
|
|
89
|
+
* Importing would silently re-point this detector at a future value.
|
|
90
|
+
*/
|
|
91
|
+
const LEGACY_MARKER_CONTENT = '{"type":"commonjs"}';
|
|
92
|
+
const OWNERSHIP_EVIDENCE = 'file content is byte-identical to the {"type":"commonjs"} marker pre-#2544 installs '
|
|
93
|
+
+ 'wrote at the config root (installSharedHooksBundle); same exact-content predicate '
|
|
94
|
+
+ 'removeCommonJsMarker uses on uninstall. Never manifest-recorded, so this is the '
|
|
95
|
+
+ 'purpose-built detector permitted by docs/installer-migrations.md#remove-managed';
|
|
96
|
+
const REASON = 'superseded by the hooks/ and plugin-dir markers (#2544); leaving it pins the shared '
|
|
97
|
+
+ 'config root to CommonJS and keeps GSD occupying a file it no longer writes';
|
|
98
|
+
const migration = {
|
|
99
|
+
id: '2026-07-28-retire-config-root-commonjs-marker',
|
|
100
|
+
title: 'Retire the pre-#2544 config-root CommonJS marker',
|
|
101
|
+
description: 'Remove <configRoot>/package.json when it is exactly the {"type":"commonjs"} marker '
|
|
102
|
+
+ 'pre-#2544 installs wrote there, now superseded by markers scoped to the directories '
|
|
103
|
+
+ 'GSD owns. A package.json with any other content is left untouched.',
|
|
104
|
+
introducedIn: '1.8.0',
|
|
105
|
+
scopes: ['global', 'local'],
|
|
106
|
+
destructive: true,
|
|
107
|
+
plan: (ctx) => {
|
|
108
|
+
const markerPath = node_path_1.default.join(ctx.configDir, STALE_ROOT_MARKER);
|
|
109
|
+
let stat;
|
|
110
|
+
try {
|
|
111
|
+
// lstat, not existsSync: existsSync follows symlinks and reports false for
|
|
112
|
+
// a dangling one. A symlink here is not something GSD wrote, and removing
|
|
113
|
+
// it is never ours to do — mirrors classifyMarker's fail-closed posture.
|
|
114
|
+
stat = node_fs_1.default.lstatSync(markerPath);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
if (!stat.isFile())
|
|
120
|
+
return [];
|
|
121
|
+
let content;
|
|
122
|
+
try {
|
|
123
|
+
content = node_fs_1.default.readFileSync(markerPath, 'utf8');
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Present but unreadable never downgrades to the permissive answer.
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
if (content.trim() !== LEGACY_MARKER_CONTENT)
|
|
130
|
+
return [];
|
|
131
|
+
const hash = node_crypto_1.default.createHash('sha256').update(content).digest('hex');
|
|
132
|
+
return [
|
|
133
|
+
{
|
|
134
|
+
type: 'remove-managed',
|
|
135
|
+
relPath: STALE_ROOT_MARKER,
|
|
136
|
+
reason: REASON,
|
|
137
|
+
ownershipEvidence: OWNERSHIP_EVIDENCE,
|
|
138
|
+
// Declared, not derived: classifyArtifact answers 'unknown' for this
|
|
139
|
+
// never-manifested path, and the planner downgrades a remove-managed on
|
|
140
|
+
// an 'unknown' classification to preserve-user. The exact-content match
|
|
141
|
+
// above IS the ownership proof, so the classification is stated here.
|
|
142
|
+
classification: 'managed-pristine',
|
|
143
|
+
originalHash: hash,
|
|
144
|
+
currentHash: hash,
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
module.exports = migration;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Installer migration: retire Cursor's duplicate commands/ surface (#2644).
|
|
4
|
+
*
|
|
5
|
+
* Cursor discovers GSD skills as slash-menu entries while also keeping them
|
|
6
|
+
* model-invocable. Older GSD releases installed the same workflows again as
|
|
7
|
+
* commands/gsd-*.md, so every action appeared twice. The skills remain the
|
|
8
|
+
* sole workflow surface; this migration removes only old command files proven
|
|
9
|
+
* managed by gsd-file-manifest.json. Modified files are backed up first and
|
|
10
|
+
* unmanifested files are preserved.
|
|
11
|
+
*/
|
|
12
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
+
};
|
|
15
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
16
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
17
|
+
const COMMANDS_DIR = 'commands';
|
|
18
|
+
const REASON = 'Cursor exposes skills directly in the slash menu; the parallel command file duplicated the same GSD action (#2644)';
|
|
19
|
+
const OWNERSHIP_EVIDENCE = 'pre-#2644 Cursor installs record commands/gsd-*.md in gsd-file-manifest.json';
|
|
20
|
+
const migration = {
|
|
21
|
+
id: '2026-07-29-cursor-retire-commands-surface',
|
|
22
|
+
title: 'Retire Cursor duplicate commands surface',
|
|
23
|
+
description: 'Remove manifest-managed Cursor commands/gsd-*.md files now that skills are the single slash-menu and model-invocation surface.',
|
|
24
|
+
introducedIn: '1.8.1',
|
|
25
|
+
runtimes: ['cursor'],
|
|
26
|
+
scopes: ['global', 'local'],
|
|
27
|
+
destructive: true,
|
|
28
|
+
plan: (ctx) => {
|
|
29
|
+
const commandsDir = node_path_1.default.join(ctx.configDir, COMMANDS_DIR);
|
|
30
|
+
let entries;
|
|
31
|
+
try {
|
|
32
|
+
if (!node_fs_1.default.existsSync(commandsDir) || node_fs_1.default.lstatSync(commandsDir).isSymbolicLink())
|
|
33
|
+
return [];
|
|
34
|
+
entries = node_fs_1.default.readdirSync(commandsDir, { withFileTypes: true });
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
const actions = [];
|
|
40
|
+
for (const entry of entries) {
|
|
41
|
+
if (!entry.isFile() || !entry.name.startsWith('gsd-') || !entry.name.endsWith('.md'))
|
|
42
|
+
continue;
|
|
43
|
+
const relPath = node_path_1.default.posix.join(COMMANDS_DIR, entry.name);
|
|
44
|
+
const artifact = ctx.classifyArtifact(relPath);
|
|
45
|
+
if (artifact.classification === 'managed-pristine') {
|
|
46
|
+
actions.push({ type: 'remove-managed', relPath, reason: REASON, ownershipEvidence: OWNERSHIP_EVIDENCE });
|
|
47
|
+
}
|
|
48
|
+
else if (artifact.classification === 'managed-modified') {
|
|
49
|
+
actions.push({ type: 'backup-and-remove', relPath, reason: REASON, ownershipEvidence: OWNERSHIP_EVIDENCE });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return actions;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
module.exports = migration;
|