@opengsd/gsd-core 1.15.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -2
- package/.opencode/plugins/gsd-core.js +336 -6
- package/.opencode/plugins/gsd-statusline/tui.tsx +55 -0
- package/GEMINI.md +1 -1
- package/agents/gsd-code-fixer.compact.md +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-code-reviewer.compact.md +5 -3
- package/agents/gsd-code-reviewer.md +8 -6
- package/agents/gsd-debug-session-manager.compact.md +7 -1
- package/agents/gsd-debug-session-manager.md +7 -1
- package/agents/gsd-debugger.md +2 -2
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +4 -4
- package/agents/gsd-intel-updater.compact.md +1 -1
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +2 -2
- package/agents/gsd-phase-researcher.md +1 -1
- package/agents/gsd-plan-checker.md +7 -2
- package/agents/gsd-planner.md +3 -3
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +1 -1
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +21 -30
- package/agents/gsd-ui-auditor.md +42 -51
- package/agents/gsd-ui-researcher.compact.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +31 -9
- package/bin/gsd-mcp-server.js +3 -1
- package/bin/install.js +487 -290
- package/commands/gsd/add-tests.md +6 -1
- package/commands/gsd/ai-integration-phase.md +6 -1
- package/commands/gsd/audit-fix.md +5 -0
- package/commands/gsd/audit-milestone.md +6 -1
- package/commands/gsd/autonomous.md +5 -0
- package/commands/gsd/capture.md +8 -4
- package/commands/gsd/code-review.md +7 -2
- package/commands/gsd/complete-milestone.md +4 -0
- package/commands/gsd/config.md +7 -3
- package/commands/gsd/debug.md +11 -7
- package/commands/gsd/discuss-phase.md +7 -3
- package/commands/gsd/docs-update.md +12 -7
- package/commands/gsd/eval-review.md +6 -1
- package/commands/gsd/execute-phase.md +12 -7
- package/commands/gsd/extract-learnings.md +5 -0
- package/commands/gsd/fast.md +4 -0
- package/commands/gsd/forensics.md +5 -1
- package/commands/gsd/graphify.md +10 -6
- package/commands/gsd/health.md +5 -0
- package/commands/gsd/help.md +7 -2
- package/commands/gsd/import.md +7 -3
- package/commands/gsd/inbox.md +5 -0
- package/commands/gsd/ingest-docs.md +5 -1
- package/commands/gsd/manager.md +6 -1
- package/commands/gsd/map-codebase.md +7 -3
- package/commands/gsd/mempalace-capture.md +5 -1
- package/commands/gsd/mempalace-recall.md +5 -1
- package/commands/gsd/milestone-summary.md +5 -1
- package/commands/gsd/mvp-phase.md +8 -3
- package/commands/gsd/new-milestone.md +6 -1
- package/commands/gsd/new-project.md +5 -0
- package/commands/gsd/next.md +6 -1
- package/commands/gsd/ns-context.md +4 -0
- package/commands/gsd/ns-ideate.md +4 -0
- package/commands/gsd/ns-manage.md +4 -0
- package/commands/gsd/ns-project.md +4 -0
- package/commands/gsd/ns-review.md +4 -0
- package/commands/gsd/ns-workflow.md +4 -0
- package/commands/gsd/onboard.md +6 -1
- package/commands/gsd/pause-work.md +5 -1
- package/commands/gsd/phase.md +8 -4
- package/commands/gsd/plan-phase.md +6 -1
- package/commands/gsd/plan-review-convergence.md +5 -1
- package/commands/gsd/pr-branch.md +4 -0
- package/commands/gsd/profile-user.md +5 -1
- package/commands/gsd/progress.md +6 -1
- package/commands/gsd/quick-batch.md +20 -8
- package/commands/gsd/quick.md +12 -7
- package/commands/gsd/review.md +5 -1
- package/commands/gsd/secure-phase.md +6 -1
- package/commands/gsd/ship.md +5 -0
- package/commands/gsd/sketch.md +7 -2
- package/commands/gsd/spec-phase.md +5 -1
- package/commands/gsd/spike.md +8 -3
- package/commands/gsd/surface.md +5 -1
- package/commands/gsd/thread.md +4 -0
- package/commands/gsd/ui-phase.md +6 -1
- package/commands/gsd/ui-review.md +6 -1
- package/commands/gsd/ultraplan-phase.md +5 -1
- package/commands/gsd/undo.md +5 -1
- package/commands/gsd/update.md +6 -2
- package/commands/gsd/validate-phase.md +6 -1
- package/commands/gsd/verify-work.md +6 -1
- package/commands/gsd/workspace.md +7 -3
- package/gsd-core/bin/gsd-tools.cjs +159 -58
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- package/gsd-core/bin/lib/adr-parser.cjs +37 -5
- package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
- package/gsd-core/bin/lib/audit.cjs +78 -44
- package/gsd-core/bin/lib/broken-windows.cjs +13 -13
- package/gsd-core/bin/lib/capability-activation.cjs +9 -4
- package/gsd-core/bin/lib/capability-registry.cjs +249 -117
- package/gsd-core/bin/lib/capability-validator.cjs +18 -1
- package/gsd-core/bin/lib/capability-writer.cjs +2 -1
- package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
- package/gsd-core/bin/lib/check-command-router.cjs +164 -1712
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
- package/gsd-core/bin/lib/cli-exit.cjs +12 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +11 -8
- package/gsd-core/bin/lib/command-aliases.cjs +7 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
- package/gsd-core/bin/lib/commands.cjs +201 -178
- package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
- package/gsd-core/bin/lib/config.cjs +43 -12
- package/gsd-core/bin/lib/core-utils.cjs +6 -1
- package/gsd-core/bin/lib/coverage.cjs +4 -8
- package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
- package/gsd-core/bin/lib/drift.cjs +177 -42
- package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
- package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
- package/gsd-core/bin/lib/frontmatter.cjs +413 -234
- package/gsd-core/bin/lib/gap-checker.cjs +72 -29
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
- package/gsd-core/bin/lib/gate-args.cjs +53 -0
- package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
- package/gsd-core/bin/lib/gate-config.cjs +46 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +142 -0
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
- package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
- package/gsd-core/bin/lib/gate-exit.cjs +27 -0
- package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
- package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
- package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
- package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
- package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
- package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
- package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
- package/gsd-core/bin/lib/gate-ui-safety.cjs +81 -0
- package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
- package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
- package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
- package/gsd-core/bin/lib/graphify.cjs +3 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
- package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
- package/gsd-core/bin/lib/init.cjs +194 -108
- package/gsd-core/bin/lib/install-engine.cjs +30 -35
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- package/gsd-core/bin/lib/install-shadow-report.cjs +12 -0
- package/gsd-core/bin/lib/installer-migrations/011-opencode-mcp-no-prompts.cjs +99 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +54 -1
- package/gsd-core/bin/lib/io.cjs +122 -3
- package/gsd-core/bin/lib/jsonc-edit.cjs +418 -0
- package/gsd-core/bin/lib/loop-host-contract.cjs +2 -1
- package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
- package/gsd-core/bin/lib/mcp-server.cjs +5 -1
- package/gsd-core/bin/lib/milestone.cjs +19 -1
- package/gsd-core/bin/lib/model-resolver.cjs +18 -18
- package/gsd-core/bin/lib/observability/event.cjs +1 -1
- package/gsd-core/bin/lib/observability/logger.cjs +46 -1
- package/gsd-core/bin/lib/opencode-mcp-entry.cjs +39 -0
- package/gsd-core/bin/lib/opencode-version.cjs +486 -0
- package/gsd-core/bin/lib/pattern.cjs +10 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +11 -4
- package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
- package/gsd-core/bin/lib/phase-id.cjs +1 -1
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
- package/gsd-core/bin/lib/phase-status.cjs +360 -0
- package/gsd-core/bin/lib/phase.cjs +301 -80
- package/gsd-core/bin/lib/plan-document.cjs +93 -19
- package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
- package/gsd-core/bin/lib/planning-document.cjs +273 -40
- package/gsd-core/bin/lib/planning-inspect.cjs +42 -8
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +76 -53
- package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
- package/gsd-core/bin/lib/profile-output.cjs +6 -3
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +13 -9
- package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
- package/gsd-core/bin/lib/report-parser.cjs +269 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +13 -15
- package/gsd-core/bin/lib/roadmap-parser.cjs +82 -15
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +127 -65
- package/gsd-core/bin/lib/roadmap.cjs +169 -72
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +186 -161
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +2 -1
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
- package/gsd-core/bin/lib/runtime-homes.cjs +13 -10
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +365 -594
- package/gsd-core/bin/lib/runtime-name-policy.cjs +142 -27
- package/gsd-core/bin/lib/runtime-slash.cjs +60 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +37 -2
- package/gsd-core/bin/lib/smart-entry.cjs +111 -56
- package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
- package/gsd-core/bin/lib/state-contract.cjs +15 -18
- package/gsd-core/bin/lib/state-document.cjs +100 -22
- package/gsd-core/bin/lib/state-transition.cjs +67 -9
- package/gsd-core/bin/lib/state.cjs +668 -171
- package/gsd-core/bin/lib/surface.cjs +2 -1
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -152
- package/gsd-core/bin/lib/uat-predicate.cjs +313 -35
- package/gsd-core/bin/lib/uat.cjs +424 -7
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +94 -58
- package/gsd-core/bin/lib/update-context.cjs +18 -1
- package/gsd-core/bin/lib/vendor/README.md +31 -9
- package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
- package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +1265 -324
- package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
- package/gsd-core/bin/lib/verify.cjs +286 -567
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
- package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
- package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
- package/gsd-core/bin/lib/workstream.cjs +11 -2
- package/gsd-core/bin/shared/config-defaults.manifest.json +5 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +4 -0
- package/gsd-core/references/autonomous-smart-discuss.md +2 -1
- package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
- package/gsd-core/references/execute-mvp-tdd.md +5 -10
- package/gsd-core/references/execute-phase-between-wave-reset.md +3 -0
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +7 -1
- package/gsd-core/references/offer-next.md +1 -1
- package/gsd-core/references/planning-config.md +1 -1
- package/gsd-core/references/spidr-splitting.md +1 -1
- package/gsd-core/references/tdd.md +10 -4
- package/gsd-core/references/verifier-phase-gates.md +5 -2
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -3
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/UAT.md +17 -1
- package/gsd-core/templates/config.json +2 -11
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +8 -7
- package/gsd-core/workflows/add-tests.md +3 -2
- package/gsd-core/workflows/add-todo.md +4 -3
- package/gsd-core/workflows/ai-integration-phase.md +3 -2
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +4 -3
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous.md +28 -18
- package/gsd-core/workflows/check-todos.md +6 -5
- package/gsd-core/workflows/cleanup.md +1 -1
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
- package/gsd-core/workflows/code-review-fix.md +5 -4
- package/gsd-core/workflows/code-review.md +100 -91
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
- package/gsd-core/workflows/complete-milestone.md +12 -7
- package/gsd-core/workflows/debug.md +30 -5
- package/gsd-core/workflows/diagnose-issues.md +3 -2
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +4 -3
- package/gsd-core/workflows/do.md +1 -1
- package/gsd-core/workflows/docs-update.md +4 -3
- package/gsd-core/workflows/edit-phase.md +4 -3
- package/gsd-core/workflows/eval-review.md +5 -3
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +4 -2
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
- package/gsd-core/workflows/execute-phase.md +81 -171
- package/gsd-core/workflows/execute-plan.md +13 -19
- package/gsd-core/workflows/explore.md +2 -2
- package/gsd-core/workflows/extract-learnings.md +3 -2
- package/gsd-core/workflows/fast.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +2 -1
- package/gsd-core/workflows/import.md +3 -2
- package/gsd-core/workflows/inbox.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- package/gsd-core/workflows/insert-phase.md +4 -3
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +5 -3
- package/gsd-core/workflows/map-codebase.md +4 -3
- package/gsd-core/workflows/milestone-summary.md +3 -2
- package/gsd-core/workflows/mvp-phase.md +14 -14
- package/gsd-core/workflows/new-milestone.md +10 -10
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +1 -1
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/pause-work.md +2 -2
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +1 -1
- package/gsd-core/workflows/plan-phase.md +27 -18
- package/gsd-core/workflows/plan-review-convergence.md +1 -1
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +1 -1
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress.md +19 -49
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -2
- package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +15 -10
- package/gsd-core/workflows/quick.md +42 -31
- package/gsd-core/workflows/remove-phase.md +3 -2
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +17 -12
- package/gsd-core/workflows/review.md +3 -2
- package/gsd-core/workflows/scan.md +3 -2
- package/gsd-core/workflows/secure-phase.md +11 -12
- package/gsd-core/workflows/settings-advanced.md +1 -1
- package/gsd-core/workflows/settings-integrations.md +1 -1
- package/gsd-core/workflows/settings.md +1 -1
- package/gsd-core/workflows/ship.md +12 -12
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +1 -1
- package/gsd-core/workflows/spec-phase.md +1 -1
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/sync-skills.md +1 -1
- package/gsd-core/workflows/thread.md +2 -2
- package/gsd-core/workflows/transition.md +13 -23
- package/gsd-core/workflows/ui-phase.md +5 -4
- package/gsd-core/workflows/ui-review.md +4 -3
- package/gsd-core/workflows/ultraplan-phase.md +3 -2
- package/gsd-core/workflows/undo.md +1 -1
- package/gsd-core/workflows/validate-phase.md +10 -12
- package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
- package/gsd-core/workflows/verify-work.md +68 -59
- package/hooks/dist/gsd-agent-isolation-guard.js +8 -0
- package/hooks/dist/gsd-check-update-worker.js +8 -0
- package/hooks/dist/gsd-check-update.js +34 -20
- package/hooks/dist/gsd-context-monitor.js +45 -13
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
- package/hooks/dist/gsd-secret-read-guard.js +263 -16
- package/hooks/dist/gsd-statusline.js +336 -29
- package/hooks/dist/gsd-update-banner.js +8 -0
- package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
- package/hooks/dist/gsd-workflow-guard.js +5 -4
- package/hooks/dist/gsd-worktree-path-guard.js +6 -2
- package/hooks/dist/lib/cli-exit.js +12 -0
- package/hooks/dist/lib/git-probe.js +17 -1
- package/hooks/dist/lib/isolation-sentinel.js +2 -2
- package/hooks/gsd-agent-isolation-guard.js +8 -0
- package/hooks/gsd-check-update-worker.js +8 -0
- package/hooks/gsd-check-update.js +34 -20
- package/hooks/gsd-context-monitor.js +45 -13
- package/hooks/gsd-cursor-subagent-start.js +8 -0
- package/hooks/gsd-secret-read-guard.js +263 -16
- package/hooks/gsd-statusline.js +336 -29
- package/hooks/gsd-update-banner.js +8 -0
- package/hooks/gsd-windsurf-pre-write.js +11 -2
- package/hooks/gsd-workflow-guard.js +5 -4
- package/hooks/gsd-worktree-path-guard.js +6 -2
- package/hooks/hooks.json +5 -5
- package/hooks/lib/cli-exit.js +12 -0
- package/hooks/lib/git-probe.js +17 -1
- package/hooks/lib/isolation-sentinel.js +2 -2
- package/package.json +21 -4
- package/scripts/changeset/parse.cjs +52 -4
- package/scripts/ci-timeout-report.cjs +770 -4
- package/scripts/command-contract-helpers.cjs +12 -8
- package/scripts/docs-guard-registry.cjs +6 -0
- package/scripts/gen-capability-registry.cjs +7 -1
- package/scripts/gen-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +14 -2
- package/scripts/gen-platform-conformance-tier.cjs +187 -1
- package/scripts/gen-plugin-skills.cjs +87 -1
- package/scripts/gen-research-agents.cjs +24 -31
- package/scripts/gen-scripts-cli-exit.cjs +30 -3
- package/scripts/gen-test-timings.cjs +32 -7
- package/scripts/lib/cli-exit.cjs +12 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +24 -2
- package/scripts/lib/ndjson-reporter.cjs +28 -3
- package/scripts/lib/platform-conformance-tier.generated.cjs +44 -5
- package/scripts/lib/registration-ledger-preload.cjs +155 -0
- package/scripts/lib/vendor-bundle.cjs +59 -0
- package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-completion-predicate-drift.cjs +18 -19
- package/scripts/lint-descriptions.cjs +7 -3
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +10 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
- package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
- package/scripts/lint-phase-enumeration-drift.cjs +12 -8
- package/scripts/lint-phase-id-drift.cjs +29 -0
- package/scripts/lint-planning-document-positive-control.cjs +329 -0
- package/scripts/lint-response-language-coverage.cjs +3 -0
- package/scripts/lint-skill-deps.cjs +7 -3
- package/scripts/lint-test-file-count.allowlist.json +11 -1
- package/scripts/lint-test-file-count.cjs +34 -1
- package/scripts/lint-vendored-deps.cjs +41 -5
- package/scripts/lint-workflow-shellcheck-baseline.json +10 -10
- package/scripts/mutation-matrix.cjs +53 -6
- package/scripts/release-tarball-smoke.cjs +14 -7
- package/scripts/require-issue-link-policy.cjs +6 -2
- package/scripts/sync-runtime-launcher.cjs +184 -2
- package/scripts/verify-npm-publish.cjs +76 -20
- package/skills/gsd-add-tests/SKILL.md +6 -1
- package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
- package/skills/gsd-audit-fix/SKILL.md +5 -0
- package/skills/gsd-audit-milestone/SKILL.md +6 -1
- package/skills/gsd-autonomous/SKILL.md +5 -0
- package/skills/gsd-capture/SKILL.md +8 -4
- package/skills/gsd-code-review/SKILL.md +7 -2
- package/skills/gsd-complete-milestone/SKILL.md +4 -0
- package/skills/gsd-config/SKILL.md +8 -4
- package/skills/gsd-debug/SKILL.md +11 -7
- package/skills/gsd-discuss-phase/SKILL.md +7 -3
- package/skills/gsd-docs-update/SKILL.md +12 -7
- package/skills/gsd-eval-review/SKILL.md +6 -1
- package/skills/gsd-execute-phase/SKILL.md +12 -7
- package/skills/gsd-extract-learnings/SKILL.md +5 -0
- package/skills/gsd-fast/SKILL.md +4 -0
- package/skills/gsd-forensics/SKILL.md +5 -1
- package/skills/gsd-graphify/SKILL.md +10 -6
- package/skills/gsd-health/SKILL.md +5 -0
- package/skills/gsd-help/SKILL.md +7 -2
- package/skills/gsd-import/SKILL.md +7 -3
- package/skills/gsd-inbox/SKILL.md +5 -0
- package/skills/gsd-ingest-docs/SKILL.md +5 -1
- package/skills/gsd-manager/SKILL.md +6 -1
- package/skills/gsd-map-codebase/SKILL.md +7 -3
- package/skills/gsd-mempalace-capture/SKILL.md +5 -1
- package/skills/gsd-mempalace-recall/SKILL.md +5 -1
- package/skills/gsd-milestone-summary/SKILL.md +5 -1
- package/skills/gsd-mvp-phase/SKILL.md +8 -3
- package/skills/gsd-new-milestone/SKILL.md +6 -1
- package/skills/gsd-new-project/SKILL.md +5 -0
- package/skills/gsd-next/SKILL.md +6 -1
- package/skills/gsd-ns-context/SKILL.md +4 -0
- package/skills/gsd-ns-ideate/SKILL.md +4 -0
- package/skills/gsd-ns-manage/SKILL.md +4 -0
- package/skills/gsd-ns-project/SKILL.md +4 -0
- package/skills/gsd-ns-review/SKILL.md +4 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -0
- package/skills/gsd-onboard/SKILL.md +6 -1
- package/skills/gsd-pause-work/SKILL.md +5 -1
- package/skills/gsd-phase/SKILL.md +8 -4
- package/skills/gsd-plan-phase/SKILL.md +6 -1
- package/skills/gsd-plan-review-convergence/SKILL.md +5 -1
- package/skills/gsd-pr-branch/SKILL.md +4 -0
- package/skills/gsd-profile-user/SKILL.md +5 -1
- package/skills/gsd-progress/SKILL.md +6 -1
- package/skills/gsd-quick/SKILL.md +16 -10
- package/skills/gsd-quick-batch/SKILL.md +20 -8
- package/skills/gsd-review/SKILL.md +5 -1
- package/skills/gsd-review-backlog/SKILL.md +3 -2
- package/skills/gsd-secure-phase/SKILL.md +6 -1
- package/skills/gsd-ship/SKILL.md +5 -0
- package/skills/gsd-sketch/SKILL.md +7 -2
- package/skills/gsd-spec-phase/SKILL.md +5 -1
- package/skills/gsd-spike/SKILL.md +8 -3
- package/skills/gsd-surface/SKILL.md +5 -1
- package/skills/gsd-thread/SKILL.md +4 -0
- package/skills/gsd-ui-phase/SKILL.md +6 -1
- package/skills/gsd-ui-review/SKILL.md +6 -1
- package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
- package/skills/gsd-undo/SKILL.md +5 -1
- package/skills/gsd-update/SKILL.md +6 -2
- package/skills/gsd-validate-phase/SKILL.md +6 -1
- package/skills/gsd-verify-work/SKILL.md +6 -1
- package/skills/gsd-workspace/SKILL.md +7 -3
- package/skills/gsd-workstreams/SKILL.md +6 -6
- package/vscode/package.json +1 -1
- package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +0 -24
|
@@ -55,7 +55,7 @@ const install_scope_cjs_1 = require("./install-scope.cjs");
|
|
|
55
55
|
const { processAttribution } = runtimeArtifactConversion;
|
|
56
56
|
// resolveRuntimeArtifactLayout: accessed via module ref (not destructured) so
|
|
57
57
|
// test stubs that monkeypatch the module's exports are seen at call time.
|
|
58
|
-
const { getDirName } = runtimeNamePolicy;
|
|
58
|
+
const { getDirName, hostBehaviorsFor } = runtimeNamePolicy;
|
|
59
59
|
function withInstallerPackageSource(configDir, fn) {
|
|
60
60
|
// Reuse the existing compatibility-marker contract through the existing fs
|
|
61
61
|
// seam. The marker exists only in this synchronous call tree: no disk state,
|
|
@@ -181,7 +181,7 @@ function provisionRuntimeSurfaceCorpus(layout, configDir, scope) {
|
|
|
181
181
|
const destination = node_path_1.default.join(corpusRoot, 'agents');
|
|
182
182
|
syncRuntimeSurfaceCorpus(source, destination, configDir, 'gsd-core/agents/');
|
|
183
183
|
}
|
|
184
|
-
const markerFile =
|
|
184
|
+
const markerFile = hostBehaviorsFor(layout.runtime).sourceMarkerFile;
|
|
185
185
|
if (typeof markerFile === 'string' && markerFile !== '' && required.has('commands')) {
|
|
186
186
|
try {
|
|
187
187
|
const markerPath = runtimeArtifactInstallPlan.assertDestWithinConfigHome(configDir, markerFile);
|
|
@@ -220,25 +220,6 @@ function executingPackageRoot() { return node_path_1.default.dirname(node_path_1
|
|
|
220
220
|
*/
|
|
221
221
|
const USER_OWNED_ARTIFACTS = ['USER-PROFILE.md'];
|
|
222
222
|
// ---------------------------------------------------------------------------
|
|
223
|
-
// Host-behavior helpers
|
|
224
|
-
// ---------------------------------------------------------------------------
|
|
225
|
-
/**
|
|
226
|
-
* Host-specific install behaviors declared on the runtime descriptor
|
|
227
|
-
* (capabilities/<runtime>/capability.json -> runtime.hostBehaviors).
|
|
228
|
-
* Mirrors bin/install.js's `_hostBehaviors` (ADR-1239 / #2086/#2087). Returns
|
|
229
|
-
* {} for runtimes that declare none or if the registry fails to load, so
|
|
230
|
-
* every behavior branch degrades to the generic path by default.
|
|
231
|
-
*/
|
|
232
|
-
function _hostBehaviors(runtime) {
|
|
233
|
-
try {
|
|
234
|
-
const reg = require('./capability-registry.cjs');
|
|
235
|
-
return (reg && reg.runtimes && reg.runtimes[runtime] && reg.runtimes[runtime].runtime && reg.runtimes[runtime].runtime.hostBehaviors) || {};
|
|
236
|
-
}
|
|
237
|
-
catch {
|
|
238
|
-
return {};
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
// ---------------------------------------------------------------------------
|
|
242
223
|
// Conversion helpers
|
|
243
224
|
// ---------------------------------------------------------------------------
|
|
244
225
|
/**
|
|
@@ -737,8 +718,9 @@ function _copyStaged(stagedDir, destDir, kind, configDir, runtime) {
|
|
|
737
718
|
// files whose `name:` frontmatter is identical to their canonical
|
|
738
719
|
// sibling's, leaving the harness resolution unstated. Claude never
|
|
739
720
|
// selects compact, so claude is the one runtime whose agents kind skips
|
|
740
|
-
// them; every other runtime's emission is byte-identical.
|
|
741
|
-
|
|
721
|
+
// them; every other runtime's emission is byte-identical. #5169: the
|
|
722
|
+
// descriptor declares this (`hostBehaviors.skipCompactAgents`), not a name test.
|
|
723
|
+
if (kind.kind === 'agents' && hostBehaviorsFor(runtime).skipCompactAgents && entry.name.endsWith('.compact.md'))
|
|
742
724
|
continue;
|
|
743
725
|
const stem = entry.name.slice(0, -3); // strip .md
|
|
744
726
|
let destName;
|
|
@@ -748,7 +730,7 @@ function _copyStaged(stagedDir, destDir, kind, configDir, runtime) {
|
|
|
748
730
|
// hardcoded `runtime === 'copilot'`). copilot declares '.agent.md';
|
|
749
731
|
// every other runtime's descriptor leaves this unset, so destName falls
|
|
750
732
|
// back to entry.name unchanged (byte-parity, #1575 origin comment).
|
|
751
|
-
const _agentExt = runtime ?
|
|
733
|
+
const _agentExt = runtime ? hostBehaviorsFor(runtime).agentFileExtension : undefined;
|
|
752
734
|
destName = _agentExt
|
|
753
735
|
? entry.name.replace(/\.md$/, _agentExt)
|
|
754
736
|
: entry.name;
|
|
@@ -887,7 +869,7 @@ function _runLegacyInstallMigrations(runtime, configDir, scope = 'global') {
|
|
|
887
869
|
// that for Hermes the flat skills/gsd-*/ removal (below) does not delete the freshly
|
|
888
870
|
// created skills/gsd-dev-preferences/ skill dir.
|
|
889
871
|
let stagedLegacyArtifacts = null;
|
|
890
|
-
if (
|
|
872
|
+
if (hostBehaviorsFor(runtime).legacyCommandsGsdInstallMigration) {
|
|
891
873
|
if (installFs().existsSync(legacyCommandsGsd)) {
|
|
892
874
|
// #2875: staging root resolved lazily, only when there is actually
|
|
893
875
|
// something to stage — reused below by every other call site sharing
|
|
@@ -911,8 +893,8 @@ function _runLegacyInstallMigrations(runtime, configDir, scope = 'global') {
|
|
|
911
893
|
}
|
|
912
894
|
}
|
|
913
895
|
// Hermes: remove pre-#2841 flat skills/gsd-*/ entries that lived alongside
|
|
914
|
-
// the new skills/gsd/ nested layout.
|
|
915
|
-
if (runtime
|
|
896
|
+
// the new skills/gsd/ nested layout. (#5169: `hostBehaviors.legacyFlatSkillsCleanup`.)
|
|
897
|
+
if (hostBehaviorsFor(runtime).legacyFlatSkillsCleanup) {
|
|
916
898
|
const flatSkillsDir = node_path_1.default.join(configDir, 'skills');
|
|
917
899
|
if (installFs().existsSync(flatSkillsDir)) {
|
|
918
900
|
for (const entry of installFs().readdirSync(flatSkillsDir, { withFileTypes: true })) {
|
|
@@ -1050,7 +1032,7 @@ function _runLegacyUninstallCleanup(runtime, configDir, scope = 'global') {
|
|
|
1050
1032
|
// commands/gsd/ for claude local, preserving dev-preferences.md by restoring it
|
|
1051
1033
|
// to the same location (#1423). Using migrateLegacyDevPreferencesToSkill here
|
|
1052
1034
|
// (which would redirect to skills/) conflicts with the test contract for local installs.
|
|
1053
|
-
const _lu =
|
|
1035
|
+
const _lu = hostBehaviorsFor(runtime).legacyCommandsGsdUninstall;
|
|
1054
1036
|
// #2870: `scope` keeps its exported `string = 'global'` signature (no
|
|
1055
1037
|
// signature change), but every real caller — `uninstallRuntimeArtifacts`'s
|
|
1056
1038
|
// own required `scope` param, always fed a validated 'global' | 'local'
|
|
@@ -1075,8 +1057,8 @@ function _runLegacyUninstallCleanup(runtime, configDir, scope = 'global') {
|
|
|
1075
1057
|
}
|
|
1076
1058
|
}
|
|
1077
1059
|
}
|
|
1078
|
-
// Hermes: pre-#2841 flat skills/gsd-*/ entries
|
|
1079
|
-
if (runtime
|
|
1060
|
+
// Hermes: pre-#2841 flat skills/gsd-*/ entries (#5169: `legacyFlatSkillsCleanup`)
|
|
1061
|
+
if (hostBehaviorsFor(runtime).legacyFlatSkillsCleanup) {
|
|
1080
1062
|
const flatSkillsDir = node_path_1.default.join(configDir, 'skills');
|
|
1081
1063
|
if (node_fs_1.default.existsSync(flatSkillsDir)) {
|
|
1082
1064
|
for (const entry of node_fs_1.default.readdirSync(flatSkillsDir, { withFileTypes: true })) {
|
|
@@ -1085,6 +1067,8 @@ function _runLegacyUninstallCleanup(runtime, configDir, scope = 'global') {
|
|
|
1085
1067
|
}
|
|
1086
1068
|
}
|
|
1087
1069
|
}
|
|
1070
|
+
}
|
|
1071
|
+
if (hostBehaviorsFor(runtime).bareStemSkillsCleanup) {
|
|
1088
1072
|
// Hermes: pre-#947 bare-stem skills/gsd/<stem>/ entries (dirs that do NOT
|
|
1089
1073
|
// start with 'gsd-') — the #3664 layout used prefix='' so GSD-owned skills
|
|
1090
1074
|
// had bare names (e.g. skills/gsd/help/). These are stale on uninstall.
|
|
@@ -1142,7 +1126,7 @@ function installRuntimeArtifacts(runtime, configDir, scope, resolvedProfile, res
|
|
|
1142
1126
|
// the dedicated combined commands+skills+plugin orchestrator instead of the
|
|
1143
1127
|
// generic layout-driven loop below, mirroring the bespoke install path that
|
|
1144
1128
|
// previously lived inline in bin/install.js.
|
|
1145
|
-
const behaviors =
|
|
1129
|
+
const behaviors = hostBehaviorsFor(runtime);
|
|
1146
1130
|
const projectDir = scope === 'global' ? process.cwd() : configDir;
|
|
1147
1131
|
if (behaviors.combinedFamilyInstall) {
|
|
1148
1132
|
// #2329: combined-family runtimes (OpenCode/Kilo) bypass
|
|
@@ -1336,7 +1320,7 @@ function installRuntimeArtifacts(runtime, configDir, scope, resolvedProfile, res
|
|
|
1336
1320
|
// with the same prefix). User-owned bare dirs with no gsd-<stem> counterpart
|
|
1337
1321
|
// are untouched.
|
|
1338
1322
|
let hermesBareStemCleanup = false;
|
|
1339
|
-
if (runtime
|
|
1323
|
+
if (hostBehaviorsFor(runtime).bareStemSkillsCleanup) {
|
|
1340
1324
|
const nestedGsdDirForCleanup = node_path_1.default.join(configDir, 'skills', 'gsd');
|
|
1341
1325
|
_removeHermesBareStemDirs(nestedGsdDirForCleanup);
|
|
1342
1326
|
hermesBareStemCleanup = true;
|
|
@@ -1688,7 +1672,7 @@ function installOpencodeFamilyCommands(runtime, destDir, srcDir, pathPrefix, res
|
|
|
1688
1672
|
// already satisfies the fold-to-descriptor requirement. Only the SKILLS
|
|
1689
1673
|
// converter site above (installOpencodeFamilySkills) has a real
|
|
1690
1674
|
// `converter` string to key on via SKILLS_CONVERTER_REGISTRY.
|
|
1691
|
-
content =
|
|
1675
|
+
content = hostBehaviorsFor(runtime).frontmatterDialect === 'kilo'
|
|
1692
1676
|
? runtimeArtifactConversion.convertClaudeToKiloFrontmatter(content)
|
|
1693
1677
|
: runtimeArtifactConversion.convertClaudeToOpencodeFrontmatter(content);
|
|
1694
1678
|
installFs().writeFileSync(node_path_1.default.join(destDir, destName), content);
|
|
@@ -1757,6 +1741,17 @@ function _installNativePluginIfDeclared(runtime, configDir, behaviors, src) {
|
|
|
1757
1741
|
}
|
|
1758
1742
|
}
|
|
1759
1743
|
}
|
|
1744
|
+
// OpenCode 2.x terminal-client plugin (ADR-5331 Phase 3): a TUI entrypoint kept in its own directory under
|
|
1745
|
+
// plugins/. OpenCode 1.x scans plugins/*.js|ts only, so it never loads this directory.
|
|
1746
|
+
const cli = behaviors.nativeCliPlugin;
|
|
1747
|
+
if (cli && cli.source) {
|
|
1748
|
+
const cliSrc = node_path_1.default.join(src, cli.source);
|
|
1749
|
+
if (installFs().existsSync(cliSrc)) {
|
|
1750
|
+
const cliDest = runtimeArtifactInstallPlan.assertDestWithinConfigHome(configDir, node_path_1.default.join(cli.dir, cli.file));
|
|
1751
|
+
installFs().mkdirSync(node_path_1.default.dirname(cliDest), { recursive: true });
|
|
1752
|
+
installFs().copyFileSync(cliSrc, cliDest);
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1760
1755
|
}
|
|
1761
1756
|
// ---------------------------------------------------------------------------
|
|
1762
1757
|
// _migrateLegacyOpencodeCommandDir
|
|
@@ -1971,7 +1966,8 @@ function uninstallRuntimeArtifacts(runtime, configDir, scope, deps = {}) {
|
|
|
1971
1966
|
// the GSD-managed DESCRIPTION.md and then the category dir itself if it
|
|
1972
1967
|
// contains no user content (#947). _removeGsdEntries removed gsd-* dirs
|
|
1973
1968
|
// but left the category container and DESCRIPTION.md intact.
|
|
1974
|
-
|
|
1969
|
+
// (#5169: `hostBehaviors.categoryContainerCleanup`.)
|
|
1970
|
+
if (hostBehaviorsFor(runtime).categoryContainerCleanup) {
|
|
1975
1971
|
const nestedGsdDir = node_path_1.default.join(configDir, 'skills', 'gsd');
|
|
1976
1972
|
if (node_fs_1.default.existsSync(nestedGsdDir)) {
|
|
1977
1973
|
// Remove GSD-owned DESCRIPTION.md (written by writeHermesCategoryDescription)
|
|
@@ -2029,7 +2025,6 @@ module.exports = {
|
|
|
2029
2025
|
installAgentsKindStandalone,
|
|
2030
2026
|
installOpencodeFamilyArtifacts,
|
|
2031
2027
|
_installNativePluginIfDeclared,
|
|
2032
|
-
_hostBehaviors,
|
|
2033
2028
|
_copyStaged,
|
|
2034
2029
|
hasExistingSymlinkBetween,
|
|
2035
2030
|
isSymlinkedDestOptIn,
|
|
@@ -27,6 +27,7 @@ const node_os_1 = __importDefault(require("node:os"));
|
|
|
27
27
|
const installFsAdapter = require("./install-fs-adapter.cjs");
|
|
28
28
|
const { installFs, mkInstallTempDir } = installFsAdapter;
|
|
29
29
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
30
|
+
const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
|
|
30
31
|
// #2322: reuse the existing pure path-containment seam (ADR-1239 Phase C-2)
|
|
31
32
|
// instead of hand-rolling a new traversal check for capability skill stems.
|
|
32
33
|
const external_descriptor_trust_cjs_1 = require("./external-descriptor-trust.cjs");
|
|
@@ -95,13 +96,15 @@ const PROFILES = Object.freeze({
|
|
|
95
96
|
* Returns string[] — empty array if no requires: field.
|
|
96
97
|
*
|
|
97
98
|
* No external YAML parser dependency — hand-parse the single line
|
|
98
|
-
* since GSD enforces flow-style arrays for requires:.
|
|
99
|
+
* since GSD enforces flow-style arrays for requires:. The block is the one the
|
|
100
|
+
* one fence owner (`locateFrontmatterFence`) finds — a `---` pair later in the
|
|
101
|
+
* body (a thematic break, a YAML example) is never frontmatter.
|
|
99
102
|
*/
|
|
100
103
|
function parseRequires(content) {
|
|
101
|
-
const
|
|
102
|
-
if (!
|
|
104
|
+
const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
|
|
105
|
+
if (!fence?.closed)
|
|
103
106
|
return [];
|
|
104
|
-
const fm =
|
|
107
|
+
const fm = content.slice(fence.openEnd, fence.bodyEnd);
|
|
105
108
|
const line = fm.match(/^requires:\s*(.+)$/m);
|
|
106
109
|
if (!line)
|
|
107
110
|
return [];
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
* (design row #5 / "Not-corruption"). `kindsDiffer` on `ShadowReport` is what
|
|
24
24
|
* lets `renderShadowReport` word the two cases correctly.
|
|
25
25
|
*
|
|
26
|
+
* Same-scope pairs are not reported: a runtime whose layout installs `commands` and `skills` side by side at one
|
|
27
|
+
* scope (OpenCode, Kilo, Augment, CodeBuddy, ZCode) writes both on purpose, and `isGenuinelyShadowed` requires the
|
|
28
|
+
* winner and the loser to sit in different scopes (#5344).
|
|
29
|
+
*
|
|
26
30
|
* ── Report, don't correct (mirrors the resolver's own law) ─────────────────
|
|
27
31
|
* `mismatches` surfaces a declared runtime/scope that disagrees with the
|
|
28
32
|
* probed one (Postel's Law, design doc: liberal in what is accepted, but the
|
|
@@ -218,6 +222,14 @@ function stemFromTrigger(trigger, prefix) {
|
|
|
218
222
|
function isGenuinelyShadowed(t, scopeRecords, prefixFor) {
|
|
219
223
|
if (t.shadowedBy === null)
|
|
220
224
|
return false;
|
|
225
|
+
// This is a CROSS-SCOPE report (see the module comment). A winner and a loser in the SAME scope are the two
|
|
226
|
+
// kinds one install deliberately wrote side by side — OpenCode, Kilo, Augment, CodeBuddy and ZCode install
|
|
227
|
+
// `commands` and `skills` for every trigger at one scope. GSD wrote both, the operator cannot act on it, and
|
|
228
|
+
// the host's own dedupe (OpenCode 1.x lists the command and drops the same-named skill; 2.x keeps them on
|
|
229
|
+
// separate surfaces) is not a loss of reachability. Reporting it printed "72 triggers shadowed" on every
|
|
230
|
+
// install of those five runtimes (#5344).
|
|
231
|
+
if (t.shadowedBy.scope === t.scope)
|
|
232
|
+
return false;
|
|
221
233
|
const winnerRecord = scopeRecords.get(t.shadowedBy.scope);
|
|
222
234
|
const shadowedRecord = scopeRecords.get(t.scope);
|
|
223
235
|
const winnerStem = stemFromTrigger(t.trigger, prefixFor(t.shadowedBy.scope, t.shadowedBy.kind));
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Installer migration: stop the GSD MCP companion from duplicating every GSD command in OpenCode
|
|
4
|
+
* (#5338; epic #5331, ADR-5331 D6).
|
|
5
|
+
*
|
|
6
|
+
* OpenCode turns each MCP prompt into a `<server>:<prompt>` slash command. The GSD companion server
|
|
7
|
+
* advertised its whole prompt catalog, so with the command files the installer already writes every GSD
|
|
8
|
+
* command appeared twice (`gsd-plan-phase` and `gsd:plan-phase`; 143 entries against 72 observed on
|
|
9
|
+
* OpenCode 2.0.27). New installs register the server with `--no-prompts`. This migration converges an
|
|
10
|
+
* existing install by adding that flag to the `gsd` MCP entry GSD itself wrote — and only that entry: a
|
|
11
|
+
* `gsd` server the user edited or defined does not match the installer's shape and is never touched.
|
|
12
|
+
*
|
|
13
|
+
* It applies to OpenCode on both generations (the entry is the same shape), covers the 1.x `mcp.gsd` and
|
|
14
|
+
* the 2.x native `mcp.servers.gsd` locations, and edits the config as text so a commented `opencode.jsonc`
|
|
15
|
+
* keeps its comments and layout (#5334).
|
|
16
|
+
*
|
|
17
|
+
* Is the action safe in non-interactive install?
|
|
18
|
+
* Yes. `rewrite-json` is non-interactive and journaled; the rewrite is a one-token append to a command
|
|
19
|
+
* array the installer wrote.
|
|
20
|
+
*
|
|
21
|
+
* See docs/installer-migrations.md#shipped-migrations and the OpenCode row of
|
|
22
|
+
* docs/installer-migrations.md#runtime-configuration-contract-registry.
|
|
23
|
+
*/
|
|
24
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
25
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
26
|
+
};
|
|
27
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
28
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
29
|
+
const jsonc_edit_cjs_1 = require("../jsonc-edit.cjs");
|
|
30
|
+
const opencode_mcp_entry_cjs_1 = require("../opencode-mcp-entry.cjs");
|
|
31
|
+
/** OpenCode reads `opencode.jsonc` in preference to `opencode.json` when both exist. */
|
|
32
|
+
const CONFIG_CANDIDATES = ['opencode.jsonc', 'opencode.json'];
|
|
33
|
+
function isPlainObject(v) {
|
|
34
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
35
|
+
}
|
|
36
|
+
const migration = {
|
|
37
|
+
id: '2026-10-12-opencode-mcp-no-prompts',
|
|
38
|
+
title: 'Stop the GSD MCP companion duplicating GSD commands in OpenCode',
|
|
39
|
+
description: 'Add --no-prompts to the GSD-written `gsd` MCP server entry in opencode.json(c) (mcp.gsd, and the ' +
|
|
40
|
+
'OpenCode 2.x native mcp.servers.gsd) so OpenCode no longer lists every GSD command a second time as ' +
|
|
41
|
+
'a `gsd:<name>` MCP-prompt command. Only the entry the installer wrote is changed; comments and ' +
|
|
42
|
+
'layout in a JSONC config are preserved (#5338, #5334).',
|
|
43
|
+
introducedIn: '1.17.0',
|
|
44
|
+
runtimes: ['opencode'],
|
|
45
|
+
scopes: ['global', 'local'],
|
|
46
|
+
destructive: false,
|
|
47
|
+
runtimeContract: 'docs/installer-migrations.md#runtime-configuration-contract-registry OpenCode row',
|
|
48
|
+
plan: (ctx) => {
|
|
49
|
+
if (ctx.runtime !== 'opencode')
|
|
50
|
+
return [];
|
|
51
|
+
// The file OpenCode (and the installer's resolveOpencodeConfigPath) treats as THE config.
|
|
52
|
+
const relPath = CONFIG_CANDIDATES.find((name) => node_fs_1.default.existsSync(node_path_1.default.join(ctx.configDir, name)));
|
|
53
|
+
if (!relPath)
|
|
54
|
+
return [];
|
|
55
|
+
let config;
|
|
56
|
+
try {
|
|
57
|
+
config = (0, jsonc_edit_cjs_1.parseJsonc)(node_fs_1.default.readFileSync(node_path_1.default.join(ctx.configDir, relPath), 'utf8'));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return []; // unparseable: never touch a config we cannot read (same posture as the installer)
|
|
61
|
+
}
|
|
62
|
+
if (!isPlainObject(config) || !isPlainObject(config['mcp']))
|
|
63
|
+
return [];
|
|
64
|
+
const mcp = config['mcp'];
|
|
65
|
+
const edits = [];
|
|
66
|
+
const next = { ...config, mcp: { ...mcp } };
|
|
67
|
+
const nextMcp = next['mcp'];
|
|
68
|
+
const legacy = mcp['gsd'];
|
|
69
|
+
if ((0, opencode_mcp_entry_cjs_1.isGsdMcpEntry)(legacy) && !(0, opencode_mcp_entry_cjs_1.hasNoPromptsFlag)(legacy)) {
|
|
70
|
+
const updated = (0, opencode_mcp_entry_cjs_1.withNoPromptsFlag)(legacy);
|
|
71
|
+
nextMcp['gsd'] = updated;
|
|
72
|
+
edits.push({ path: ['mcp', 'gsd', 'command'], value: updated.command });
|
|
73
|
+
}
|
|
74
|
+
const servers = mcp['servers'];
|
|
75
|
+
if (isPlainObject(servers)) {
|
|
76
|
+
const native = servers['gsd'];
|
|
77
|
+
if ((0, opencode_mcp_entry_cjs_1.isGsdMcpEntry)(native) && !(0, opencode_mcp_entry_cjs_1.hasNoPromptsFlag)(native)) {
|
|
78
|
+
const updated = (0, opencode_mcp_entry_cjs_1.withNoPromptsFlag)(native);
|
|
79
|
+
nextMcp['servers'] = { ...servers, gsd: updated };
|
|
80
|
+
edits.push({ path: ['mcp', 'servers', 'gsd', 'command'], value: updated.command });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (edits.length === 0)
|
|
84
|
+
return [];
|
|
85
|
+
return [
|
|
86
|
+
{
|
|
87
|
+
type: 'rewrite-json',
|
|
88
|
+
relPath,
|
|
89
|
+
value: next,
|
|
90
|
+
edits,
|
|
91
|
+
reason: 'the GSD MCP companion advertised its prompt catalog, which OpenCode lists as a second set of ' +
|
|
92
|
+
'`gsd:<name>` slash commands next to the installed command files',
|
|
93
|
+
ownershipEvidence: "the entry matches the exact shape configureOpencodePermissions writes: type 'local' and a command " +
|
|
94
|
+
"array of `npx … <package> gsd-mcp-server`; a user-edited or user-defined `gsd` server does not match",
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
module.exports = migration;
|
|
@@ -18,6 +18,7 @@ const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs")
|
|
|
18
18
|
const clock_cjs_1 = require("./clock.cjs");
|
|
19
19
|
const install_scope_cjs_1 = require("./install-scope.cjs");
|
|
20
20
|
const security_cjs_1 = require("./security.cjs");
|
|
21
|
+
const jsonc_edit_cjs_1 = require("./jsonc-edit.cjs");
|
|
21
22
|
// #2874 (ADR-58 cleanup phase): this file is the ~1200-line migration
|
|
22
23
|
// plan/apply/rollback/lock/journal engine — almost none of it is on the
|
|
23
24
|
// installRuntimeArtifacts call tree. Only `readInstallManifest` and
|
|
@@ -293,15 +294,25 @@ function readInstallManifest(configDir) {
|
|
|
293
294
|
scope: (0, install_scope_cjs_1.isInstallScopeId)(m.scope) ? m.scope : null,
|
|
294
295
|
};
|
|
295
296
|
}
|
|
297
|
+
function normalizeHostGeneration(raw) {
|
|
298
|
+
if (!raw || typeof raw !== 'object')
|
|
299
|
+
return undefined;
|
|
300
|
+
const r = raw;
|
|
301
|
+
if (r.runtime !== 'opencode' || (r.generation !== 1 && r.generation !== 2))
|
|
302
|
+
return undefined;
|
|
303
|
+
return { runtime: 'opencode', generation: r.generation, version: typeof r.version === 'string' ? r.version : null };
|
|
304
|
+
}
|
|
296
305
|
function readInstallState(configDir) {
|
|
297
306
|
const state = readJsonIfPresent(node_path_1.default.join(configDir, INSTALL_STATE_NAME), STRICT_JSON);
|
|
298
307
|
if (!state || typeof state !== 'object') {
|
|
299
308
|
return { schemaVersion: 1, appliedMigrations: [] };
|
|
300
309
|
}
|
|
301
310
|
const s = state;
|
|
311
|
+
const hostGeneration = normalizeHostGeneration(s.hostGeneration);
|
|
302
312
|
return {
|
|
303
313
|
schemaVersion: typeof s.schemaVersion === 'number' ? s.schemaVersion : 1,
|
|
304
314
|
appliedMigrations: Array.isArray(s.appliedMigrations) ? s.appliedMigrations : [],
|
|
315
|
+
...(hostGeneration ? { hostGeneration } : {}),
|
|
305
316
|
};
|
|
306
317
|
}
|
|
307
318
|
// Strict atomic write for the install state: must never be left half-written.
|
|
@@ -654,6 +665,31 @@ function journalAction(action, status, extras = {}) {
|
|
|
654
665
|
const { value: _value, ...safeAction } = action;
|
|
655
666
|
return { ...safeAction, ...extras, status };
|
|
656
667
|
}
|
|
668
|
+
/**
|
|
669
|
+
* The text a `rewrite-json` action writes. A file with JSONC syntax (comments, trailing commas, or a
|
|
670
|
+
* `.jsonc` name) is edited in place from the action's `edits`, so the user's annotations survive (#5334);
|
|
671
|
+
* everything else, and any edit the text editor cannot place, is written as the action's whole `value`.
|
|
672
|
+
*/
|
|
673
|
+
function renderRewrittenJson(fullPath, action) {
|
|
674
|
+
if (Array.isArray(action.edits) && action.edits.length > 0) {
|
|
675
|
+
let original = null;
|
|
676
|
+
try {
|
|
677
|
+
original = node_fs_1.default.readFileSync(fullPath, 'utf8');
|
|
678
|
+
}
|
|
679
|
+
catch { /* fall through to the value rewrite */ }
|
|
680
|
+
if (original !== null && (fullPath.endsWith('.jsonc') || (0, jsonc_edit_cjs_1.hasJsoncFeatures)(original))) {
|
|
681
|
+
let text = original;
|
|
682
|
+
for (const edit of action.edits) {
|
|
683
|
+
text = (0, jsonc_edit_cjs_1.setJsoncValue)(text, edit.path, edit.value);
|
|
684
|
+
if (text === null)
|
|
685
|
+
break;
|
|
686
|
+
}
|
|
687
|
+
if (text !== null)
|
|
688
|
+
return text;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
return JSON.stringify(action.value, null, 2) + '\n';
|
|
692
|
+
}
|
|
657
693
|
function planInstallerMigrations({ configDir, runtime = null, scope = null, migrations, baselineScan = false, now = () => new Date().toISOString(), }) {
|
|
658
694
|
if (!configDir)
|
|
659
695
|
throw new Error('configDir is required');
|
|
@@ -725,6 +761,8 @@ function planInstallerMigrations({ configDir, runtime = null, scope = null, migr
|
|
|
725
761
|
}
|
|
726
762
|
if (action.type === 'rewrite-json') {
|
|
727
763
|
action.value = rawAction.value;
|
|
764
|
+
if (Array.isArray(rawAction.edits))
|
|
765
|
+
action.edits = rawAction.edits;
|
|
728
766
|
action.deleteIfEmpty = rawAction.deleteIfEmpty === true;
|
|
729
767
|
}
|
|
730
768
|
if (rawAction.prompt)
|
|
@@ -896,7 +934,7 @@ function applyInstallerMigrationPlan({ configDir, plan, now = () => new Date().t
|
|
|
896
934
|
}));
|
|
897
935
|
}
|
|
898
936
|
else {
|
|
899
|
-
(0, shell_command_projection_cjs_1.platformWriteSync)(fullPath,
|
|
937
|
+
(0, shell_command_projection_cjs_1.platformWriteSync)(fullPath, renderRewrittenJson(fullPath, action));
|
|
900
938
|
journal.actions.push(journalAction(action, 'rewritten', {
|
|
901
939
|
rollbackRelPath: node_path_1.default.posix.join(rollbackRootRelPath, normalized),
|
|
902
940
|
}));
|
|
@@ -945,6 +983,7 @@ function applyInstallerMigrationPlan({ configDir, plan, now = () => new Date().t
|
|
|
945
983
|
writeInstallState(configDir, {
|
|
946
984
|
schemaVersion: 1,
|
|
947
985
|
appliedMigrations: nextApplied,
|
|
986
|
+
...(state.hostGeneration ? { hostGeneration: state.hostGeneration } : {}),
|
|
948
987
|
});
|
|
949
988
|
return {
|
|
950
989
|
appliedMigrationIds: journal.appliedMigrationIds,
|
|
@@ -1017,6 +1056,7 @@ function markPendingMigrationsApplied({ configDir, plan, now = () => new Date().
|
|
|
1017
1056
|
writeInstallState(configDir, {
|
|
1018
1057
|
schemaVersion: 1,
|
|
1019
1058
|
appliedMigrations: nextApplied,
|
|
1059
|
+
...(state.hostGeneration ? { hostGeneration: state.hostGeneration } : {}),
|
|
1020
1060
|
});
|
|
1021
1061
|
}
|
|
1022
1062
|
return newlyApplied;
|
|
@@ -1073,6 +1113,18 @@ function runInstallerMigrations({ configDir, runtime = null, scope = null, migra
|
|
|
1073
1113
|
// Unused but kept to satisfy eslint — sleepSync is referenced in the original
|
|
1074
1114
|
// and may be used by test code that patches this module.
|
|
1075
1115
|
void sleepSync;
|
|
1116
|
+
/**
|
|
1117
|
+
* Record which OpenCode generation this install targets (ADR-5331 D2; #5337). Idempotent and
|
|
1118
|
+
* atomic; leaves the applied-migrations journal untouched.
|
|
1119
|
+
*/
|
|
1120
|
+
function recordHostGeneration(configDir, info) {
|
|
1121
|
+
const state = readInstallState(configDir);
|
|
1122
|
+
const next = { runtime: 'opencode', generation: info.generation, version: info.version ?? null };
|
|
1123
|
+
const prev = state.hostGeneration;
|
|
1124
|
+
if (prev && prev.generation === next.generation && prev.version === next.version)
|
|
1125
|
+
return;
|
|
1126
|
+
writeInstallState(configDir, { ...state, hostGeneration: next });
|
|
1127
|
+
}
|
|
1076
1128
|
module.exports = {
|
|
1077
1129
|
DEFAULT_MIGRATIONS_DIR,
|
|
1078
1130
|
INSTALL_MIGRATION_LOCK_NAME,
|
|
@@ -1089,6 +1141,7 @@ module.exports = {
|
|
|
1089
1141
|
planInstallerMigrations,
|
|
1090
1142
|
readInstallManifest,
|
|
1091
1143
|
readInstallState,
|
|
1144
|
+
recordHostGeneration,
|
|
1092
1145
|
runInstallerMigrations,
|
|
1093
1146
|
writeInstallState,
|
|
1094
1147
|
};
|
package/gsd-core/bin/lib/io.cjs
CHANGED
|
@@ -121,6 +121,84 @@ function writeAllSync(fd, data) {
|
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Temporarily patch `fs.writeSync` so everything written to fd 1 (stdout) is
|
|
126
|
+
* captured into a string instead of reaching the real stdout, run `run`, then
|
|
127
|
+
* restore the original `fs.writeSync` and resolve with what was captured.
|
|
128
|
+
*
|
|
129
|
+
* The ONE shared helper (#5105 S9) for "run a nested command and read its own
|
|
130
|
+
* JSON envelope back as data instead of letting it reach the real stdout" —
|
|
131
|
+
* used by gsd-tools.cjs's `--pick`/`@file:` resolution and by `uat.cts`'s
|
|
132
|
+
* `cmdUatCompleteSession` (which needs `cmdCommit`'s result object without
|
|
133
|
+
* emitting cmdCommit's own envelope as a second line of output). A single
|
|
134
|
+
* definition means both callers see the same Buffer/string/encoding handling
|
|
135
|
+
* and the same throw-mid-write flush behavior, rather than two hand-rolled
|
|
136
|
+
* monkeypatches drifting apart.
|
|
137
|
+
*
|
|
138
|
+
* On a throw from `run`, whatever was captured before the throw is flushed to
|
|
139
|
+
* the REAL stdout — the wrapped command may have already written its own
|
|
140
|
+
* JSON envelope and THEN thrown to set a non-zero exit code (e.g. a
|
|
141
|
+
* capability set/disable on an unknown id); without this flush that output
|
|
142
|
+
* would be silently discarded, since the success-path flush at the call site
|
|
143
|
+
* never runs on a throw. The error still propagates so the exit code is
|
|
144
|
+
* preserved.
|
|
145
|
+
*/
|
|
146
|
+
function captureStdoutSyncWrites(run) {
|
|
147
|
+
const originalWriteSync = node_fs_1.default.writeSync;
|
|
148
|
+
let captured = '';
|
|
149
|
+
node_fs_1.default.writeSync = ((fd, data, ...rest) => {
|
|
150
|
+
if (fd === 1) {
|
|
151
|
+
if (Buffer.isBuffer(data)) {
|
|
152
|
+
captured += data.toString('utf-8');
|
|
153
|
+
return data.length;
|
|
154
|
+
}
|
|
155
|
+
const text = String(data);
|
|
156
|
+
captured += text;
|
|
157
|
+
let encoding = 'utf-8';
|
|
158
|
+
if (typeof rest[1] === 'string')
|
|
159
|
+
encoding = rest[1];
|
|
160
|
+
return Buffer.byteLength(text, encoding);
|
|
161
|
+
}
|
|
162
|
+
return originalWriteSync.call(node_fs_1.default, fd, data, ...rest);
|
|
163
|
+
});
|
|
164
|
+
const restore = () => {
|
|
165
|
+
node_fs_1.default.writeSync = originalWriteSync;
|
|
166
|
+
};
|
|
167
|
+
return Promise.resolve()
|
|
168
|
+
.then(() => run())
|
|
169
|
+
.then(() => {
|
|
170
|
+
restore();
|
|
171
|
+
return captured;
|
|
172
|
+
}, (err) => {
|
|
173
|
+
restore();
|
|
174
|
+
if (captured) {
|
|
175
|
+
try {
|
|
176
|
+
originalWriteSync.call(node_fs_1.default, 1, captured);
|
|
177
|
+
}
|
|
178
|
+
catch { /* best-effort flush */ }
|
|
179
|
+
}
|
|
180
|
+
throw err;
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Resolve `output()`'s `@file:<path>` redirection (emitted for a >50KB JSON
|
|
185
|
+
* payload) back to the real content, or return `captured` unchanged when it
|
|
186
|
+
* is not that shape.
|
|
187
|
+
*
|
|
188
|
+
* #5105 review finding 7: the ONE definition, shared by gsd-tools.cjs's
|
|
189
|
+
* `--pick`/CLI-passthrough resolution and by `uat.cts`'s
|
|
190
|
+
* `cmdUatCompleteSession` (which reads `cmdCommit`'s captured result back
|
|
191
|
+
* without emitting cmdCommit's own envelope as a second stdout line) — same
|
|
192
|
+
* reasoning as `captureStdoutSyncWrites` just below: one definition means
|
|
193
|
+
* both callers agree on the trailing-newline-free prefix and the read
|
|
194
|
+
* encoding, rather than two hand-rolled `startsWith('@file:')` checks
|
|
195
|
+
* drifting apart.
|
|
196
|
+
*/
|
|
197
|
+
function resolveAtFileOutput(captured) {
|
|
198
|
+
if (!captured.startsWith('@file:'))
|
|
199
|
+
return captured;
|
|
200
|
+
return node_fs_1.default.readFileSync(captured.slice('@file:'.length), 'utf-8');
|
|
201
|
+
}
|
|
124
202
|
/**
|
|
125
203
|
* The wire form of a JSON result: the exact bytes `output()` emits for it.
|
|
126
204
|
*
|
|
@@ -290,6 +368,16 @@ const ERROR_REASON = Object.freeze({
|
|
|
290
368
|
// exit 0. See .gsd/phase/feat-3884-failure-is-a-value/40-design.md.
|
|
291
369
|
PICK_FIELD_ABSENT: 'pick_field_absent',
|
|
292
370
|
PICK_OUTPUT_NOT_JSON: 'pick_output_not_json',
|
|
371
|
+
// summary-extract (#5013): the SUMMARY's frontmatter fence exists but failed
|
|
372
|
+
// to parse (extractFrontmatter's FRONTMATTER_UNPARSEABLE marker) — a
|
|
373
|
+
// distinct failure from "no frontmatter"/"field absent", never a silent
|
|
374
|
+
// demotion to empty fields at exit 0.
|
|
375
|
+
SUMMARY_EXTRACT_UNPARSEABLE: 'summary_extract_unparseable',
|
|
376
|
+
// verification (#5118, ADR-5057 Phase 4): a *-VERIFICATION.md frontmatter
|
|
377
|
+
// `status` outside the closed writer set (`passed | gaps_found |
|
|
378
|
+
// human_needed`) — verification.cts's VerificationStatusError, translated
|
|
379
|
+
// once, centrally, by gsd-tools.cjs. Never read as "no result" at exit 0.
|
|
380
|
+
VERIFICATION_STATUS_INVALID: 'verification_status_invalid',
|
|
293
381
|
// generic
|
|
294
382
|
USAGE: 'usage',
|
|
295
383
|
UNKNOWN: 'unknown',
|
|
@@ -334,14 +422,35 @@ const ERROR_REASON = Object.freeze({
|
|
|
334
422
|
* more than one line or introduce an unescaped `"`. Callers embedding the
|
|
335
423
|
* result MUST NOT add their own surrounding quotes — that would
|
|
336
424
|
* double-quote it.
|
|
425
|
+
*
|
|
426
|
+
* #5118 security review: `JSON.stringify` leaves characters that render
|
|
427
|
+
* invisibly or reorder the text around them untouched — DEL and the C1
|
|
428
|
+
* controls (U+007F–U+009F), the line/paragraph separators (U+2028–U+2029),
|
|
429
|
+
* zero-width and directional marks (U+200B–U+200F), the bidi embeddings and
|
|
430
|
+
* overrides (U+202A–U+202E), the bidi isolates (U+2066–U+2069) and the BOM
|
|
431
|
+
* (U+FEFF). Each is escaped as `\uXXXX` so the token reads exactly as its
|
|
432
|
+
* code points are, whoever renders it.
|
|
337
433
|
*/
|
|
434
|
+
const INVISIBLE_OR_BIDI_RANGES = [
|
|
435
|
+
[0x7f, 0x9f],
|
|
436
|
+
[0x2028, 0x2029],
|
|
437
|
+
[0x200b, 0x200f],
|
|
438
|
+
[0x202a, 0x202e],
|
|
439
|
+
[0x2066, 0x2069],
|
|
440
|
+
[0xfeff, 0xfeff],
|
|
441
|
+
];
|
|
442
|
+
/** Code points as `\uXXXX` escape text (source stays ASCII; no literal invisible characters). */
|
|
443
|
+
function toUnicodeEscape(codePoint) {
|
|
444
|
+
return `\\u${codePoint.toString(16).padStart(4, '0')}`;
|
|
445
|
+
}
|
|
446
|
+
const INVISIBLE_OR_BIDI_RE = new RegExp(`[${INVISIBLE_OR_BIDI_RANGES.map(([lo, hi]) => `${toUnicodeEscape(lo)}-${toUnicodeEscape(hi)}`).join('')}]`, 'g');
|
|
338
447
|
function formatDiagnosticToken(value) {
|
|
339
|
-
return JSON.stringify(value);
|
|
448
|
+
return JSON.stringify(value).replace(INVISIBLE_OR_BIDI_RE, (ch) => toUnicodeEscape(ch.charCodeAt(0)));
|
|
340
449
|
}
|
|
341
450
|
/**
|
|
342
451
|
* Map an ERROR_REASON wire value onto a declared outcome name (#3912,
|
|
343
|
-
* ADR-3889 §4). Closed over the
|
|
344
|
-
* explicit entry below, so a
|
|
452
|
+
* ADR-3889 §4). Closed over the 27-member enum: every reason gets an
|
|
453
|
+
* explicit entry below, so a 28th member added without a mapping falls
|
|
345
454
|
* through to the `?? 'FAIL'` default rather than silently mis-projecting —
|
|
346
455
|
* and tests/A1 iterates `Object.values(ERROR_REASON)`, so that default is
|
|
347
456
|
* exactly what makes an unmapped addition visible instead of invisible.
|
|
@@ -389,6 +498,14 @@ const REASON_TO_OUTCOME = Object.freeze({
|
|
|
389
498
|
// supposed to be there and was not; a prerequisite of the query failed.
|
|
390
499
|
[ERROR_REASON.PICK_FIELD_ABSENT]: 'UNAVAILABLE',
|
|
391
500
|
[ERROR_REASON.PICK_OUTPUT_NOT_JSON]: 'UNAVAILABLE',
|
|
501
|
+
// #5013: the SUMMARY's frontmatter fence exists but failed to parse — the
|
|
502
|
+
// prerequisite of the query (readable frontmatter) is what's broken, same
|
|
503
|
+
// shape as PICK_FIELD_ABSENT/PICK_OUTPUT_NOT_JSON just above.
|
|
504
|
+
[ERROR_REASON.SUMMARY_EXTRACT_UNPARSEABLE]: 'UNAVAILABLE',
|
|
505
|
+
// #5118: the verification report exists but its `status` is not a verdict
|
|
506
|
+
// the reader accepts — a broken prerequisite of the query, the same shape
|
|
507
|
+
// as SUMMARY_EXTRACT_UNPARSEABLE just above.
|
|
508
|
+
[ERROR_REASON.VERIFICATION_STATUS_INVALID]: 'UNAVAILABLE',
|
|
392
509
|
// Self-failure: the run itself broke, not its inputs.
|
|
393
510
|
[ERROR_REASON.SDK_FAIL_FAST]: 'INTERNAL',
|
|
394
511
|
[ERROR_REASON.SECURITY_SCAN_FAILED]: 'INTERNAL',
|
|
@@ -433,4 +550,6 @@ module.exports = {
|
|
|
433
550
|
getJsonErrorMode,
|
|
434
551
|
error,
|
|
435
552
|
formatDiagnosticToken,
|
|
553
|
+
captureStdoutSyncWrites,
|
|
554
|
+
resolveAtFileOutput,
|
|
436
555
|
};
|