@opengsd/gsd-core 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.opencode/plugins/gsd-core.js +12 -0
- package/agents/gsd-advisor-researcher.compact.md +85 -0
- package/agents/gsd-ai-researcher.compact.md +96 -0
- package/agents/gsd-assumptions-analyzer.compact.md +81 -0
- package/agents/gsd-code-fixer.compact.md +458 -0
- package/agents/gsd-code-fixer.md +5 -5
- package/agents/gsd-code-reviewer.compact.md +269 -0
- package/agents/gsd-code-reviewer.md +15 -3
- package/agents/gsd-codebase-mapper.compact.md +760 -0
- package/agents/gsd-debug-session-manager.compact.md +345 -0
- package/agents/gsd-doc-classifier.compact.md +192 -0
- package/agents/gsd-doc-synthesizer.compact.md +200 -0
- package/agents/gsd-doc-verifier.compact.md +143 -0
- package/agents/gsd-doc-writer.compact.md +440 -0
- package/agents/gsd-dom-verifier.compact.md +138 -0
- package/agents/gsd-domain-researcher.compact.md +141 -0
- package/agents/gsd-eval-auditor.compact.md +160 -0
- package/agents/gsd-eval-planner.compact.md +137 -0
- package/agents/gsd-executor.md +63 -35
- package/agents/gsd-framework-selector.compact.md +82 -0
- package/agents/gsd-integration-checker.compact.md +245 -0
- package/agents/gsd-intel-updater.compact.md +226 -0
- package/agents/gsd-mempalace-curator.compact.md +45 -0
- package/agents/gsd-nyquist-auditor.compact.md +179 -0
- package/agents/gsd-pattern-mapper.compact.md +275 -0
- package/agents/gsd-plan-checker.md +76 -57
- package/agents/gsd-planner.md +14 -0
- package/agents/gsd-project-researcher.compact.md +587 -0
- package/agents/gsd-research-synthesizer.compact.md +212 -0
- package/agents/gsd-roadmapper.compact.md +454 -0
- package/agents/gsd-roadmapper.md +13 -0
- package/agents/gsd-security-auditor.compact.md +162 -0
- package/agents/gsd-ui-auditor.compact.md +404 -0
- package/agents/gsd-ui-checker.compact.md +277 -0
- package/agents/gsd-ui-checker.md +19 -3
- package/agents/gsd-ui-researcher.compact.md +282 -0
- package/agents/gsd-ui-researcher.md +29 -0
- package/agents/gsd-user-profiler.compact.md +108 -0
- package/agents/gsd-verifier.md +23 -1
- package/bin/install.js +444 -134
- package/commands/gsd/cleanup.md +1 -0
- package/commands/gsd/code-review.md +2 -1
- package/commands/gsd/complete-milestone.md +1 -0
- package/commands/gsd/config.md +1 -0
- package/commands/gsd/debug.md +1 -0
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/graphify.md +1 -0
- package/commands/gsd/health.md +1 -0
- package/commands/gsd/mempalace-capture.md +1 -0
- package/commands/gsd/mempalace-recall.md +1 -0
- package/commands/gsd/new-milestone.md +1 -0
- package/commands/gsd/new-project.md +1 -0
- package/commands/gsd/next.md +1 -0
- package/commands/gsd/ns-workflow.md +2 -1
- package/commands/gsd/pause-work.md +1 -0
- package/commands/gsd/phase.md +2 -1
- package/commands/gsd/pr-branch.md +1 -0
- package/commands/gsd/quick-batch.md +105 -0
- package/commands/gsd/resume-work.md +1 -0
- package/commands/gsd/review-backlog.md +1 -0
- package/commands/gsd/settings.md +2 -1
- package/commands/gsd/stats.md +1 -0
- package/commands/gsd/surface.md +18 -8
- package/commands/gsd/thread.md +1 -0
- package/commands/gsd/workspace.md +1 -0
- package/commands/gsd/workstreams.md +1 -0
- package/gsd-core/bin/check-latest-version.cjs +8 -3
- package/gsd-core/bin/gsd-tools.cjs +532 -174
- package/gsd-core/bin/lib/adr-parser.cjs +1 -1
- package/gsd-core/bin/lib/artifacts.cjs +2 -1
- package/gsd-core/bin/lib/audit.cjs +39 -22
- package/gsd-core/bin/lib/broken-windows.cjs +168 -49
- package/gsd-core/bin/lib/capability-activation.cjs +27 -0
- package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
- package/gsd-core/bin/lib/capability-loader.cjs +135 -1
- package/gsd-core/bin/lib/capability-registry.cjs +528 -116
- package/gsd-core/bin/lib/capability-source.cjs +19 -2
- package/gsd-core/bin/lib/capability-state.cjs +7 -1
- package/gsd-core/bin/lib/capability-validator.cjs +134 -5
- package/gsd-core/bin/lib/capability-writer.cjs +14 -4
- package/gsd-core/bin/lib/check-command-router.cjs +198 -38
- package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
- package/gsd-core/bin/lib/clusters.cjs +1 -0
- package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
- package/gsd-core/bin/lib/command-aliases.cjs +16 -0
- package/gsd-core/bin/lib/commands.cjs +981 -79
- package/gsd-core/bin/lib/config-loader.cjs +4 -0
- package/gsd-core/bin/lib/config.cjs +153 -38
- package/gsd-core/bin/lib/core-utils.cjs +34 -7
- package/gsd-core/bin/lib/coverage.cjs +1 -1
- package/gsd-core/bin/lib/decisions.cjs +343 -28
- package/gsd-core/bin/lib/edge-probe.cjs +14 -1
- package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
- package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
- package/gsd-core/bin/lib/frontmatter.cjs +137 -23
- package/gsd-core/bin/lib/gap-checker.cjs +22 -13
- package/gsd-core/bin/lib/git-base-branch.cjs +10 -2
- package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +54 -11
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +87 -23
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
- package/gsd-core/bin/lib/host-integration.cjs +57 -5
- package/gsd-core/bin/lib/init-command-router.cjs +14 -0
- package/gsd-core/bin/lib/init.cjs +539 -60
- package/gsd-core/bin/lib/install-engine.cjs +199 -14
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +45 -0
- package/gsd-core/bin/lib/install-profiles.cjs +36 -14
- package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +33 -4
- package/gsd-core/bin/lib/io.cjs +35 -0
- package/gsd-core/bin/lib/loop-resolver.cjs +64 -39
- package/gsd-core/bin/lib/markdown-table.cjs +123 -0
- package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
- package/gsd-core/bin/lib/milestone.cjs +41 -10
- package/gsd-core/bin/lib/model-resolver.cjs +101 -10
- package/gsd-core/bin/lib/phase-command-router.cjs +20 -7
- package/gsd-core/bin/lib/phase-id.cjs +412 -31
- package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
- package/gsd-core/bin/lib/phase.cjs +941 -98
- package/gsd-core/bin/lib/plan-document.cjs +10 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +34 -18
- package/gsd-core/bin/lib/planning-snapshot.cjs +206 -30
- package/gsd-core/bin/lib/planning-workspace.cjs +153 -29
- package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
- package/gsd-core/bin/lib/quick-batch.cjs +840 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
- package/gsd-core/bin/lib/research-store.cjs +11 -12
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +53 -5
- package/gsd-core/bin/lib/review-lane-invocation.cjs +96 -1
- package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
- package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
- package/gsd-core/bin/lib/roadmap-parser.cjs +555 -41
- package/gsd-core/bin/lib/roadmap.cjs +292 -69
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +260 -43
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +28 -20
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +294 -108
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -47
- package/gsd-core/bin/lib/security.cjs +126 -7
- package/gsd-core/bin/lib/shell-command-projection.cjs +4 -0
- package/gsd-core/bin/lib/smart-entry.cjs +7 -9
- package/gsd-core/bin/lib/state-document.cjs +159 -32
- package/gsd-core/bin/lib/state-md-schema.cjs +44 -27
- package/gsd-core/bin/lib/state-transition.cjs +465 -62
- package/gsd-core/bin/lib/state.cjs +906 -151
- package/gsd-core/bin/lib/surface.cjs +83 -10
- package/gsd-core/bin/lib/task-command-router.cjs +12 -6
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
- package/gsd-core/bin/lib/uat.cjs +1420 -516
- package/gsd-core/bin/lib/update-context.cjs +36 -26
- package/gsd-core/bin/lib/validate.cjs +230 -12
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +316 -23
- package/gsd-core/bin/lib/verify-command-grounding.cjs +1 -1
- package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
- package/gsd-core/bin/lib/verify.cjs +531 -36
- package/gsd-core/bin/lib/workstream-inventory.cjs +21 -2
- package/gsd-core/bin/lib/worktree-safety.cjs +21 -7
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +507 -81
- package/gsd-core/references/agent-contracts.md +3 -3
- package/gsd-core/references/compact-content-gate.md +66 -0
- package/gsd-core/references/edge-probe.md +17 -13
- package/gsd-core/references/execute-mvp-tdd.md +18 -16
- package/gsd-core/references/execute-phase-response-language.md +6 -0
- package/gsd-core/references/executor-examples.md +42 -0
- package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
- package/gsd-core/references/loop-hook-dispatch.md +18 -0
- package/gsd-core/references/model-profiles.md +12 -3
- package/gsd-core/references/mvp-concepts.md +2 -2
- package/gsd-core/references/plan-checker-examples.md +41 -0
- package/gsd-core/references/planner-antipatterns.md +25 -0
- package/gsd-core/references/planner-chunked.md +5 -1
- package/gsd-core/references/planner-coupling.md +42 -0
- package/gsd-core/references/planner-quick-batch.md +71 -0
- package/gsd-core/references/planner-reviews.md +47 -0
- package/gsd-core/references/planner-revision.md +75 -2
- package/gsd-core/references/planning-config.md +5 -1
- package/gsd-core/references/response-language-directive.md +9 -0
- package/gsd-core/references/revision-loop.md +118 -11
- package/gsd-core/references/tdd.md +17 -9
- package/gsd-core/references/thinking-models-planning.md +18 -2
- package/gsd-core/references/verification-patterns.md +17 -4
- package/gsd-core/references/verifier-evidence-gate.md +160 -0
- package/gsd-core/references/worktree-path-safety.md +112 -2
- package/gsd-core/templates/README.md +7 -1
- package/gsd-core/templates/phase-prompt.md +4 -0
- package/gsd-core/templates/state.md +6 -3
- package/gsd-core/templates/summary.compact.md +212 -0
- package/gsd-core/templates/user-setup.compact.md +199 -0
- package/gsd-core/templates/user-setup.md +0 -9
- package/gsd-core/templates/verification-report.md +5 -0
- package/gsd-core/workflows/add-backlog.md +2 -0
- package/gsd-core/workflows/add-phase.md +2 -0
- package/gsd-core/workflows/add-tests.md +1 -1
- package/gsd-core/workflows/add-todo.md +4 -3
- package/gsd-core/workflows/ai-integration-phase.md +1 -1
- package/gsd-core/workflows/analyze-dependencies.md +2 -0
- package/gsd-core/workflows/audit-fix.md +2 -0
- package/gsd-core/workflows/audit-milestone.md +2 -0
- package/gsd-core/workflows/audit-uat.md +2 -0
- package/gsd-core/workflows/autonomous.md +15 -10
- package/gsd-core/workflows/check-todos.md +5 -3
- package/gsd-core/workflows/cleanup.md +4 -2
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +22 -13
- package/gsd-core/workflows/code-review-fix.md +5 -3
- package/gsd-core/workflows/code-review.md +211 -43
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
- package/gsd-core/workflows/complete-milestone.md +40 -254
- package/gsd-core/workflows/debug.md +1 -1
- package/gsd-core/workflows/diagnose-issues.md +5 -1
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/text.md +2 -0
- package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
- package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
- package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
- package/gsd-core/workflows/discuss-phase-power.md +2 -0
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +43 -13
- package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
- package/gsd-core/workflows/docs-update.md +15 -156
- package/gsd-core/workflows/edit-phase.md +2 -0
- package/gsd-core/workflows/eval-review.md +1 -1
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +20 -3
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +24 -3
- package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +8 -2
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
- package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
- package/gsd-core/workflows/execute-phase.md +78 -159
- package/gsd-core/workflows/execute-plan.md +28 -15
- package/gsd-core/workflows/explore.md +2 -0
- package/gsd-core/workflows/extract-learnings.md +2 -0
- package/gsd-core/workflows/fast.md +6 -0
- package/gsd-core/workflows/forensics.md +2 -0
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +1 -1
- package/gsd-core/workflows/help/modes/brief.md +2 -0
- package/gsd-core/workflows/help/modes/default.md +2 -0
- package/gsd-core/workflows/help/modes/full.compact.md +398 -0
- package/gsd-core/workflows/help/modes/full.md +12 -0
- package/gsd-core/workflows/help/modes/topic.md +2 -0
- package/gsd-core/workflows/help.md +3 -1
- package/gsd-core/workflows/import.md +3 -3
- package/gsd-core/workflows/inbox.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- package/gsd-core/workflows/insert-phase.md +2 -0
- package/gsd-core/workflows/list-phase-assumptions.md +2 -0
- package/gsd-core/workflows/list-seeds.md +2 -0
- package/gsd-core/workflows/list-workspaces.md +2 -0
- package/gsd-core/workflows/manager.md +3 -3
- package/gsd-core/workflows/map-codebase.md +52 -3
- package/gsd-core/workflows/milestone-summary.md +2 -0
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +55 -13
- package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
- package/gsd-core/workflows/new-project.md +37 -205
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +2 -0
- package/gsd-core/workflows/node-repair.md +2 -0
- package/gsd-core/workflows/note.md +2 -0
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +19 -4
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -0
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +9 -0
- package/gsd-core/workflows/plan-phase.md +144 -185
- package/gsd-core/workflows/plan-review-convergence.md +102 -10
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +30 -10
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
- package/gsd-core/workflows/progress.md +25 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +37 -2
- package/gsd-core/workflows/quick/steps/research-phase.md +3 -3
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
- package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
- package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
- package/gsd-core/workflows/quick-batch.md +203 -0
- package/gsd-core/workflows/quick.md +21 -4
- package/gsd-core/workflows/reapply-patches.md +79 -3
- package/gsd-core/workflows/remove-phase.md +2 -0
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +6 -2
- package/gsd-core/workflows/review.md +215 -10
- package/gsd-core/workflows/scan.md +2 -0
- package/gsd-core/workflows/section-manifest.json +12 -0
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/session-report.md +2 -0
- package/gsd-core/workflows/settings-advanced.md +2 -0
- package/gsd-core/workflows/settings-integrations.md +9 -8
- package/gsd-core/workflows/settings.md +19 -6
- package/gsd-core/workflows/ship.md +10 -10
- package/gsd-core/workflows/sketch-wrap-up.md +2 -0
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +1 -1
- package/gsd-core/workflows/spec-phase.md +24 -19
- package/gsd-core/workflows/spike-wrap-up.md +2 -0
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +2 -0
- package/gsd-core/workflows/sync-skills.md +12 -4
- package/gsd-core/workflows/thread.md +2 -0
- package/gsd-core/workflows/transition.md +2 -0
- package/gsd-core/workflows/ui-phase.md +26 -5
- package/gsd-core/workflows/ui-review.md +1 -1
- package/gsd-core/workflows/ultraplan-phase.md +2 -0
- package/gsd-core/workflows/undo.md +1 -1
- package/gsd-core/workflows/update.md +48 -43
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
- package/gsd-core/workflows/verify-work.md +68 -182
- package/hooks/dist/gsd-agent-isolation-guard.js +42 -16
- package/hooks/dist/gsd-check-update-worker.js +19 -2
- package/hooks/dist/gsd-context-monitor.js +371 -27
- package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
- package/hooks/dist/gsd-node-runner.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +30 -5
- package/hooks/dist/gsd-read-guard.js +2 -0
- package/hooks/dist/gsd-read-injection-scanner.js +5 -5
- package/hooks/dist/gsd-secret-read-guard.js +1105 -0
- package/hooks/dist/gsd-statusline.js +18 -10
- package/hooks/dist/gsd-validate-commit.sh +474 -7
- package/hooks/dist/gsd-workflow-guard.js +2 -1
- package/hooks/dist/gsd-worktree-path-guard.js +25 -14
- package/hooks/dist/gsd-write-guard.js +46 -1
- package/hooks/dist/lib/dispatch-identity.js +187 -0
- package/hooks/dist/lib/filename-classification.js +64 -0
- package/hooks/dist/lib/git-cmd.js +210 -1
- package/hooks/dist/lib/injection-patterns.js +36 -6
- package/hooks/dist/lib/isolation-deny-reason.js +53 -1
- package/hooks/dist/lib/isolation-sentinel.js +58 -19
- package/hooks/dist/managed-hooks-registry.cjs +1 -0
- package/hooks/gsd-agent-isolation-guard.js +42 -16
- package/hooks/gsd-check-update-worker.js +19 -2
- package/hooks/gsd-context-monitor.js +371 -27
- package/hooks/gsd-cursor-subagent-start.js +34 -14
- package/hooks/gsd-node-runner.sh +1 -0
- package/hooks/gsd-prompt-guard.js +30 -5
- package/hooks/gsd-read-guard.js +2 -0
- package/hooks/gsd-read-injection-scanner.js +5 -5
- package/hooks/gsd-secret-read-guard.js +1105 -0
- package/hooks/gsd-statusline.js +18 -10
- package/hooks/gsd-validate-commit.sh +474 -7
- package/hooks/gsd-workflow-guard.js +2 -1
- package/hooks/gsd-worktree-path-guard.js +25 -14
- package/hooks/gsd-write-guard.js +46 -1
- package/hooks/hooks.json +6 -0
- package/hooks/lib/dispatch-identity.js +187 -0
- package/hooks/lib/filename-classification.js +64 -0
- package/hooks/lib/git-cmd.js +210 -1
- package/hooks/lib/injection-patterns.js +36 -6
- package/hooks/lib/isolation-deny-reason.js +53 -1
- package/hooks/lib/isolation-sentinel.js +58 -19
- package/hooks/managed-hooks-registry.cjs +1 -0
- package/package.json +13 -9
- package/scripts/benchmark-compact-content-variants.cjs +298 -0
- package/scripts/benchmark-compact-content.cjs +368 -0
- package/scripts/build-hooks.js +11 -4
- package/scripts/check-contract-drift.cjs +4 -1
- package/scripts/check-env.cjs +36 -8
- package/scripts/check-glossary-refs.cjs +25 -21
- package/scripts/ci-next-health.cjs +271 -0
- package/scripts/ci-prepare-test-scope.cjs +7 -7
- package/scripts/ci-test-scope.cjs +133 -20
- package/scripts/ci-timeout-report.cjs +1 -1
- package/scripts/diff-touches-shipped-paths.cjs +1 -1
- package/scripts/docs-guard-registry.cjs +17 -2
- package/scripts/gen-adr-index.cjs +8 -2
- package/scripts/gen-inventory-manifest.cjs +12 -0
- package/scripts/gen-loop-host-contract.cjs +67 -15
- package/scripts/gen-platform-conformance-tier.cjs +557 -0
- package/scripts/lib/drift-scan.cjs +1 -1
- package/scripts/lib/macos-conformance-tier.generated.cjs +210 -0
- package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +276 -0
- package/scripts/lib/shellcheck-fetch.cjs +247 -0
- package/scripts/lib/suite-detection.cjs +32 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
- 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-allowed-tools-parity.cjs +221 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +24 -2
- package/scripts/lint-phase-enumeration-drift.cjs +24 -6
- package/scripts/lint-phase-id-drift.cjs +465 -15
- package/scripts/lint-portable-grep.cjs +176 -0
- package/scripts/lint-response-language-coverage.cjs +530 -0
- package/scripts/lint-source-test-name-collision.cjs +1 -1
- package/scripts/lint-test-file-count.allowlist.json +4 -1
- package/scripts/lint-vendored-deps.cjs +128 -17
- package/scripts/lint-workflow-shellcheck-baseline.json +1112 -0
- package/scripts/lint-workflow-shellcheck.cjs +614 -0
- package/scripts/npm-audit-baseline.cjs +376 -0
- package/scripts/prompt-injection-scan.sh +22 -0
- package/scripts/require-issue-link-policy.cjs +16 -1
- package/scripts/workflow-size.cjs +139 -0
- package/skills/gsd-cleanup/SKILL.md +1 -0
- package/skills/gsd-code-review/SKILL.md +2 -1
- package/skills/gsd-complete-milestone/SKILL.md +1 -0
- package/skills/gsd-config/SKILL.md +1 -0
- package/skills/gsd-debug/SKILL.md +1 -0
- package/skills/gsd-execute-phase/SKILL.md +1 -1
- package/skills/gsd-graphify/SKILL.md +1 -0
- package/skills/gsd-health/SKILL.md +1 -0
- package/skills/gsd-mempalace-capture/SKILL.md +1 -0
- package/skills/gsd-mempalace-recall/SKILL.md +1 -0
- package/skills/gsd-new-milestone/SKILL.md +1 -0
- package/skills/gsd-new-project/SKILL.md +1 -0
- package/skills/gsd-next/SKILL.md +1 -0
- package/skills/gsd-ns-workflow/SKILL.md +1 -0
- package/skills/gsd-pause-work/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +2 -1
- package/skills/gsd-pr-branch/SKILL.md +1 -0
- package/skills/gsd-quick-batch/SKILL.md +105 -0
- package/skills/gsd-resume-work/SKILL.md +1 -0
- package/skills/gsd-review-backlog/SKILL.md +1 -0
- package/skills/gsd-settings/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +1 -0
- package/skills/gsd-surface/SKILL.md +18 -8
- package/skills/gsd-thread/SKILL.md +1 -0
- package/skills/gsd-workspace/SKILL.md +1 -0
- package/skills/gsd-workstreams/SKILL.md +1 -0
- package/vscode/package.json +1 -1
- package/gsd-core/templates/claude-md.md +0 -145
- package/gsd-core/templates/codebase/concerns.md +0 -310
- package/gsd-core/templates/codebase/conventions.md +0 -307
- package/gsd-core/templates/codebase/integrations.md +0 -280
- package/gsd-core/templates/codebase/structure.md +0 -285
- package/gsd-core/templates/codebase/testing.md +0 -480
- package/gsd-core/templates/debug-subagent-prompt.md +0 -91
- package/gsd-core/templates/discovery.md +0 -146
|
@@ -40,7 +40,7 @@ const { resolveCapabilityRuntimeState } = capabilityStateModule;
|
|
|
40
40
|
// ─── Capability-activation engine (single owner for config-key precedence) ────
|
|
41
41
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
42
42
|
const capabilityActivationModule = require("./capability-activation.cjs");
|
|
43
|
-
const { _getNestedConfigValue, _readRawConfigKey, _resolveActivationValue, resolveConfigKey } = capabilityActivationModule;
|
|
43
|
+
const { _getNestedConfigValue, _readRawConfigKey, _resolveActivationValue, _resolvePointGate, resolveConfigKey } = capabilityActivationModule;
|
|
44
44
|
// ─── Canonical points (derived from LOOP_HOST_CONTRACT — authoritative 12) ───
|
|
45
45
|
// FIX 2: Derive the authoritative canonical set from LOOP_HOST_CONTRACT so it
|
|
46
46
|
// cannot drift from the host contract. CANONICAL_POINTS_FALLBACK is kept as an
|
|
@@ -125,13 +125,15 @@ function resolveLoopHooks(input) {
|
|
|
125
125
|
// Helper: check activation using single-key precedence resolver (FIX 1 + FIX 3)
|
|
126
126
|
function isActive(hook) {
|
|
127
127
|
const when = hook['when'];
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
128
|
+
if (when !== undefined && when !== null) {
|
|
129
|
+
// FIX 3: `when` present but not a non-empty string → malformed registry data → INACTIVE
|
|
130
|
+
if (typeof when !== 'string' || when.length === 0)
|
|
131
|
+
return false;
|
|
132
|
+
if (!_resolveActivationValue(when, config, cwd, registry))
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
// #3661: optional point-selection gate — see capability-activation.cts.
|
|
136
|
+
return _resolvePointGate(hook['pointFrom'], point, config, cwd, registry);
|
|
135
137
|
}
|
|
136
138
|
function isCapabilityActive(capId) {
|
|
137
139
|
if (!capabilityStatesById)
|
|
@@ -222,6 +224,9 @@ function resolveLoopHooks(input) {
|
|
|
222
224
|
active.consumes = consumes;
|
|
223
225
|
if (onError !== undefined)
|
|
224
226
|
active.onError = onError;
|
|
227
|
+
// #4209 DISP-02: only a literal `true` projects; absent/false stay inert.
|
|
228
|
+
if (hook['supportsReviewerLanes'] === true)
|
|
229
|
+
active.supportsReviewerLanes = true;
|
|
225
230
|
activeHooks.push(active);
|
|
226
231
|
}
|
|
227
232
|
// Process contributions
|
|
@@ -420,17 +425,20 @@ function sanitizeLoadFailReason(reason) {
|
|
|
420
425
|
.slice(0, 300);
|
|
421
426
|
return cleaned || '(no reason given)';
|
|
422
427
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
428
|
+
/**
|
|
429
|
+
* The full config/registry/capability-state resolution `cmdLoopRenderHooks` performs, minus its
|
|
430
|
+
* CLI-only output formatting — extracted so an in-process caller (e.g. `review-lane dispatch-step`
|
|
431
|
+
* self-verifying a `supportsReviewerLanes` trait) can reach the SAME resolution `gsd_run loop
|
|
432
|
+
* render-hooks <point> --raw` would give it, without spawning a subprocess and re-parsing its
|
|
433
|
+
* stdout (which was subject to `io.cjs`'s `@file:` overflow protocol on the rendered-string
|
|
434
|
+
* envelope — a bug class this in-process call cannot hit, since it never touches that envelope
|
|
435
|
+
* or its rendering at all).
|
|
436
|
+
*
|
|
437
|
+
* Throws on an invalid `point` (mirrors `resolveLoopHooks`); callers convert to their own error
|
|
438
|
+
* channel. Emits the same loud stderr load-failure warnings `cmdLoopRenderHooks` always has,
|
|
439
|
+
* regardless of caller — a skipped gate must never be silently invisible.
|
|
440
|
+
*/
|
|
441
|
+
function resolveActiveHooksForPoint(cwd, point, options = {}) {
|
|
434
442
|
const runtimeConfigDir = typeof options['configDir'] === 'string'
|
|
435
443
|
? options['configDir']
|
|
436
444
|
: undefined;
|
|
@@ -466,15 +474,7 @@ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
|
|
|
466
474
|
for (const cap of state.capabilities || []) {
|
|
467
475
|
capabilityStatesById.set(cap.id, cap);
|
|
468
476
|
}
|
|
469
|
-
|
|
470
|
-
try {
|
|
471
|
-
resolved = resolveLoopHooks({ point, registry, config, cwd, capabilityStatesById });
|
|
472
|
-
}
|
|
473
|
-
catch (err) {
|
|
474
|
-
const msg = (err instanceof Error) ? err.message : String(err);
|
|
475
|
-
coreError(msg);
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
477
|
+
const resolved = resolveLoopHooks({ point, registry, config, cwd, capabilityStatesById });
|
|
478
478
|
// ── ADR-1244 D2: load-failed capability gates FAIL OPEN with a loud warning ────
|
|
479
479
|
// Decision (#2009): a capability that failed to LOAD must not block the loop.
|
|
480
480
|
// The prior behavior injected a BLOCKING synthetic gate (blocking:true,
|
|
@@ -523,24 +523,44 @@ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
|
|
|
523
523
|
for (const w of loadFailWarnings) {
|
|
524
524
|
process.stderr.write(`gsd: warning — ${w}\n`);
|
|
525
525
|
}
|
|
526
|
-
//
|
|
526
|
+
// Surface capability-state warnings and the #2009 load-failure fail-open warnings together
|
|
527
|
+
// (in addition to the stderr emission above, which is the channel host workflows actually see).
|
|
528
|
+
const combinedWarnings = [...(state.warnings || []), ...loadFailWarnings];
|
|
529
|
+
return { point: resolved.point, activeHooks: resolved.activeHooks, warnings: combinedWarnings };
|
|
530
|
+
}
|
|
531
|
+
function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
|
|
532
|
+
if (!point) {
|
|
533
|
+
coreError('loop render-hooks requires a <point> argument. Valid points: ' + CANONICAL_POINTS.join(', '));
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
// --active-cap <capId> mode: emit 'true' or 'false' only (scanner-safe, no JSON envelope)
|
|
537
|
+
const activeCapId = typeof options['activeCap'] === 'string' ? options['activeCap'] : undefined;
|
|
538
|
+
if (activeCapId !== undefined && activeCapId === '') {
|
|
539
|
+
coreError('--active-cap requires a <capId> value (e.g. --active-cap tdd)');
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
let result;
|
|
543
|
+
try {
|
|
544
|
+
result = resolveActiveHooksForPoint(cwd, point, options);
|
|
545
|
+
}
|
|
546
|
+
catch (err) {
|
|
547
|
+
const msg = (err instanceof Error) ? err.message : String(err);
|
|
548
|
+
coreError(msg);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
527
551
|
if (activeCapId !== undefined) {
|
|
528
|
-
const isActive =
|
|
552
|
+
const isActive = result.activeHooks.some((h) => h.capId === activeCapId);
|
|
529
553
|
process.stdout.write(isActive ? 'true\n' : 'false\n');
|
|
530
554
|
return;
|
|
531
555
|
}
|
|
532
|
-
const rendered = renderLoopHooks(
|
|
556
|
+
const rendered = renderLoopHooks({ point: result.point, activeHooks: result.activeHooks });
|
|
533
557
|
const envelope = {
|
|
534
|
-
point:
|
|
535
|
-
activeHooks:
|
|
558
|
+
point: result.point,
|
|
559
|
+
activeHooks: result.activeHooks,
|
|
536
560
|
rendered,
|
|
537
561
|
};
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
// stderr emission above, which is the channel host workflows actually see).
|
|
541
|
-
const combinedWarnings = [...(state.warnings || []), ...loadFailWarnings];
|
|
542
|
-
if (combinedWarnings.length > 0) {
|
|
543
|
-
envelope.warnings = combinedWarnings;
|
|
562
|
+
if (result.warnings.length > 0) {
|
|
563
|
+
envelope.warnings = result.warnings;
|
|
544
564
|
}
|
|
545
565
|
coreOutput(envelope, raw);
|
|
546
566
|
}
|
|
@@ -548,6 +568,7 @@ module.exports = {
|
|
|
548
568
|
resolveLoopHooks,
|
|
549
569
|
renderLoopHooks,
|
|
550
570
|
cmdLoopRenderHooks,
|
|
571
|
+
resolveActiveHooksForPoint,
|
|
551
572
|
// Exported for tests
|
|
552
573
|
_getNestedConfigValue,
|
|
553
574
|
_resolveActivationValue,
|
|
@@ -555,6 +576,10 @@ module.exports = {
|
|
|
555
576
|
// Re-exported for identity parity guard (FIX 2: resolveConfigValues in this module
|
|
556
577
|
// calls resolveConfigKey; exporting it here makes the single-owner contract testable).
|
|
557
578
|
resolveConfigKey,
|
|
579
|
+
// #3661: re-exported for the same identity parity guard — isActive calls
|
|
580
|
+
// _resolvePointGate; exporting it here makes the single-owner contract testable
|
|
581
|
+
// (see tests/capability-precedence-parity.test.cjs's identity guard describe block).
|
|
582
|
+
_resolvePointGate,
|
|
558
583
|
CANONICAL_POINTS_FALLBACK,
|
|
559
584
|
CANONICAL_POINTS,
|
|
560
585
|
};
|
|
@@ -23,6 +23,7 @@ exports.findTableBySchema = findTableBySchema;
|
|
|
23
23
|
exports.findTableWithColumns = findTableWithColumns;
|
|
24
24
|
exports.escapeCell = escapeCell;
|
|
25
25
|
exports.appendQuickTaskRow = appendQuickTaskRow;
|
|
26
|
+
exports.migrateQuickTasksTable = migrateQuickTasksTable;
|
|
26
27
|
exports.resetQuickTaskRows = resetQuickTaskRows;
|
|
27
28
|
const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
|
|
28
29
|
// ─── Schema registry ──────────────────────────────────────────────────────────
|
|
@@ -776,6 +777,128 @@ function appendQuickTaskRow(stateContent, fields) {
|
|
|
776
777
|
const content = (0, markdown_sectionizer_cjs_1.replaceSection)(stateContent, section, newBody);
|
|
777
778
|
return { ok: true, value: { content, row, variant: match.label } };
|
|
778
779
|
}
|
|
780
|
+
// ─── migrateQuickTasksTable (#3730 option b) ───────────────────────────────
|
|
781
|
+
/**
|
|
782
|
+
* Column-name → canonical-cell mapping for migrating a legacy Quick Tasks table
|
|
783
|
+
* (#3730). Name-based, case-insensitive; every column NOT in this map lands in
|
|
784
|
+
* the Description bucket (joined with ' · ' in original column order, unknown
|
|
785
|
+
* names kept as `name: value`) so no historical datum is dropped.
|
|
786
|
+
*/
|
|
787
|
+
const QUICK_TASKS_COLUMN_ALIASES = {
|
|
788
|
+
'#': '#', id: '#',
|
|
789
|
+
date: 'Date', when: 'Date',
|
|
790
|
+
commit: 'Commit', sha: 'Commit',
|
|
791
|
+
status: 'Status',
|
|
792
|
+
directory: 'Directory', artifacts: 'Directory', path: 'Directory', dir: 'Directory',
|
|
793
|
+
description: 'Description', task: 'Description', summary: 'Description',
|
|
794
|
+
slug: 'Description', scope: 'Description', title: 'Description',
|
|
795
|
+
};
|
|
796
|
+
/**
|
|
797
|
+
* Migrate STATE.md's "Quick Tasks Completed" table onto the canonical
|
|
798
|
+
* `with-status` schema (#3730, maintainer decision 2026-09-02: option b).
|
|
799
|
+
*
|
|
800
|
+
* The pre-registry prose template licensed arbitrary column shapes GSD itself
|
|
801
|
+
* emitted, and `appendQuickTaskRow` fails loud on every one of them — leaving a
|
|
802
|
+
* project permanently unappendable. This is the supported repair path: the
|
|
803
|
+
* `gsd-tools quick-tasks-migrate` subcommand, plus an automatic check on the
|
|
804
|
+
* first `quick`/`fast` run (the workflow calls this before appending), which is
|
|
805
|
+
* a silent no-op when the section is absent, the table is already canonical, or
|
|
806
|
+
* the user never runs quick at all.
|
|
807
|
+
*
|
|
808
|
+
* Same section-selection, parse, and fail-loud posture as `appendQuickTaskRow`
|
|
809
|
+
* (ADR-2143 §7 upheld — append still rejects; this REPAIRS). Returns
|
|
810
|
+
* `{ok:true, value:{content, migrated:false}}` for the no-op cases so callers
|
|
811
|
+
* can stay silent, and `{migrated:true, from, rows}` when a rewrite happened so
|
|
812
|
+
* the CLI can report exactly what moved.
|
|
813
|
+
*/
|
|
814
|
+
function migrateQuickTasksTable(stateContent) {
|
|
815
|
+
const section = selectQuickTasksSection(stateContent);
|
|
816
|
+
if (!section) {
|
|
817
|
+
return { ok: true, value: { content: stateContent, migrated: false } };
|
|
818
|
+
}
|
|
819
|
+
const parsed = parseMarkdownTable(section.body);
|
|
820
|
+
if (!parsed.ok) {
|
|
821
|
+
return { ok: false, reason: `quick-tasks table: ${parsed.reason}` };
|
|
822
|
+
}
|
|
823
|
+
const match = matchTableSchema(parsed.value.columns);
|
|
824
|
+
if (match && match.id === 'QuickTasks') {
|
|
825
|
+
return { ok: true, value: { content: stateContent, migrated: false } };
|
|
826
|
+
}
|
|
827
|
+
const canonical = exports.TABLE_SCHEMAS.QuickTasks.find((v) => v.label === 'with-status').columns;
|
|
828
|
+
// Map each legacy column to its canonical target; unmapped names keep their
|
|
829
|
+
// identity so the Description bucket can render them losslessly.
|
|
830
|
+
const targets = parsed.value.columns.map((c) => QUICK_TASKS_COLUMN_ALIASES[c.trim().toLowerCase()] ?? null);
|
|
831
|
+
const rows = parsed.value.rows.map((row, rowIdx) => {
|
|
832
|
+
const bucket = {
|
|
833
|
+
'#': '', Description: '', Date: '', Commit: '', Status: '', Directory: '',
|
|
834
|
+
};
|
|
835
|
+
const descriptionParts = [];
|
|
836
|
+
// parseMarkdownTable hands back name-keyed records (rows[i][columnName]).
|
|
837
|
+
parsed.value.columns.forEach((col, i) => {
|
|
838
|
+
const raw = (row[col] ?? '').trim();
|
|
839
|
+
const target = targets[i];
|
|
840
|
+
if (target === 'Description') {
|
|
841
|
+
if (raw)
|
|
842
|
+
descriptionParts.push(raw);
|
|
843
|
+
}
|
|
844
|
+
else if (target) {
|
|
845
|
+
// Collision-safe (#3730 review): two legacy columns aliasing the same
|
|
846
|
+
// canonical target must not silently overwrite — the displaced value
|
|
847
|
+
// joins the Description bucket under its own name, same convention as
|
|
848
|
+
// an unknown column, so no historical datum is dropped.
|
|
849
|
+
if (raw && bucket[target]) {
|
|
850
|
+
descriptionParts.push(`${col.trim()}: ${raw}`);
|
|
851
|
+
}
|
|
852
|
+
else if (raw) {
|
|
853
|
+
bucket[target] = raw;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
else if (raw) {
|
|
857
|
+
descriptionParts.push(`${col.trim()}: ${raw}`);
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
bucket['#'] = bucket['#'] || String(rowIdx + 1);
|
|
861
|
+
bucket.Description = descriptionParts.join(' · ');
|
|
862
|
+
bucket.Date = bucket.Date || '—';
|
|
863
|
+
bucket.Commit = bucket.Commit || '—';
|
|
864
|
+
bucket.Status = bucket.Status || '—';
|
|
865
|
+
bucket.Directory = bucket.Directory || '—';
|
|
866
|
+
return `| ${canonical.map((c) => escapeCell(bucket[c])).join(' | ')} |`;
|
|
867
|
+
});
|
|
868
|
+
const eol = /\r\n/.test(section.body) ? '\r\n' : '\n';
|
|
869
|
+
const lines = section.body.split(/\r?\n/);
|
|
870
|
+
// CONTIGUOUS-run bound (#3730 review), mirroring resetQuickTaskRows: the
|
|
871
|
+
// section body may carry a SECOND table (a `#### Notes` subsection or a
|
|
872
|
+
// trailing table after a blank line) that parseMarkdownTable never read —
|
|
873
|
+
// scanning for the last pipe line anywhere in the body would splice the
|
|
874
|
+
// rewrite across it and silently destroy it. Only the first CONTIGUOUS run
|
|
875
|
+
// of pipe lines — the table that was parsed — is replaced.
|
|
876
|
+
const firstTableLineIdx = lines.findIndex((l) => l.trim().startsWith('|'));
|
|
877
|
+
let lastTableLineIdx = firstTableLineIdx;
|
|
878
|
+
while (lastTableLineIdx + 1 < lines.length && lines[lastTableLineIdx + 1].trim().startsWith('|')) {
|
|
879
|
+
lastTableLineIdx++;
|
|
880
|
+
}
|
|
881
|
+
const header = `| ${canonical.join(' | ')} |`;
|
|
882
|
+
// Widths match workflows/quick.md's canonical template byte-for-byte
|
|
883
|
+
// (#3730 review): its delimiter is max(3, len+2) per column.
|
|
884
|
+
const delimiter = `| ${canonical.map((c) => '-'.repeat(Math.max(3, c.length + 2))).join(' | ')} |`;
|
|
885
|
+
const newBody = [
|
|
886
|
+
...lines.slice(0, firstTableLineIdx),
|
|
887
|
+
header,
|
|
888
|
+
delimiter,
|
|
889
|
+
...rows,
|
|
890
|
+
...lines.slice(lastTableLineIdx + 1),
|
|
891
|
+
].join(eol);
|
|
892
|
+
return {
|
|
893
|
+
ok: true,
|
|
894
|
+
value: {
|
|
895
|
+
content: (0, markdown_sectionizer_cjs_1.replaceSection)(stateContent, section, newBody),
|
|
896
|
+
migrated: true,
|
|
897
|
+
from: parsed.value.columns,
|
|
898
|
+
rows: rows.length,
|
|
899
|
+
},
|
|
900
|
+
};
|
|
901
|
+
}
|
|
779
902
|
// ─── resetQuickTaskRows (#2142) ────────────────────────────────────────────
|
|
780
903
|
/**
|
|
781
904
|
* Clear every DATA row from STATE.md's "Quick Tasks Completed" table, leaving
|
|
@@ -402,8 +402,8 @@ function classifyUnindexedUri(uri) {
|
|
|
402
402
|
}
|
|
403
403
|
/** Second, independent gate (design "Hostile inputs" gate 2): re-validate an INDEXED entry's relPath against the catalog root, catching a symlink planted after the index was built. */
|
|
404
404
|
function readIndexedResource(catalog, entry) {
|
|
405
|
-
const
|
|
406
|
-
if (
|
|
405
|
+
const contained = (0, security_cjs_1.tryWithinRoot)(entry.relPath, catalog.root);
|
|
406
|
+
if (contained === null) {
|
|
407
407
|
fail(exports.REASON.TRAVERSAL_REFUSED, `indexed resource escapes catalog root: ${entry.relPath}`);
|
|
408
408
|
}
|
|
409
409
|
let raw;
|
|
@@ -55,7 +55,7 @@ const { scanPhasePlans } = planScanMod;
|
|
|
55
55
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-locator.cjs is an export= CommonJS module
|
|
56
56
|
const phaseLocatorMod = require("./phase-locator.cjs");
|
|
57
57
|
const { listMilestonePhaseDirs } = phaseLocatorMod;
|
|
58
|
-
const { planningPaths } = planningWorkspace;
|
|
58
|
+
const { planningPaths, resolvePhaseIdConvention } = planningWorkspace;
|
|
59
59
|
const { extractFrontmatter } = frontmatterMod;
|
|
60
60
|
// ADR-3408 §8.3 / #3469: `writeStateMd` gets sync and NO preservation — the
|
|
61
61
|
// same #3374-shaped exposure the milestone-complete write used to carry (a
|
|
@@ -702,6 +702,22 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
702
702
|
let totalPlans = 0;
|
|
703
703
|
let totalTasks = 0;
|
|
704
704
|
const accomplishments = [];
|
|
705
|
+
// #2761 (round-11 BLOCKER): resolved ONCE, ambiently (no explicit `ws` —
|
|
706
|
+
// this function has none of its own and already resolves everything else
|
|
707
|
+
// off `cwd` via planningPaths(cwd), matching the ambient-workstream
|
|
708
|
+
// contract `resolvePhaseIdConvention`/`planningDir` share), and threaded
|
|
709
|
+
// into the single `listMilestonePhaseDirs` call directly below. Left
|
|
710
|
+
// unthreaded, `phaseIdConvention` stays `undefined` and
|
|
711
|
+
// `getMilestonePhaseFilter` silently resolves it FROM CONFIG itself —
|
|
712
|
+
// which is exactly the behavior the changeset originally (and wrongly)
|
|
713
|
+
// claimed this PR does not reach: on a bracket-convention project that
|
|
714
|
+
// lazily-resolved convention changes `milestonePhaseDirs`/
|
|
715
|
+
// `milestonePhaseScope` below, and this function goes on to ARCHIVE
|
|
716
|
+
// (rename/move) whatever that set names. Resolving explicitly here removes
|
|
717
|
+
// the silent-inherit path without changing behavior for `null` /
|
|
718
|
+
// `milestone-prefixed` projects, whose resolved convention is the same
|
|
719
|
+
// value `undefined` would have lazily produced anyway.
|
|
720
|
+
const phaseConvention = resolvePhaseIdConvention(cwd);
|
|
705
721
|
// #3597 (ADR-3180 Decision 2): SINGLE resolution of "which phase
|
|
706
722
|
// directories belong to the current milestone" AND the SCOPE discriminator
|
|
707
723
|
// that resolution came from — shared verbatim by the read-only stats loop
|
|
@@ -715,7 +731,11 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
715
731
|
// disk, same as before #3597. Only the destructive archive pass (and its
|
|
716
732
|
// --dry-run preview) refuses to act on a non-COMPLETE (non-answer) scope;
|
|
717
733
|
// see the guard built from `milestonePhaseScope` further down.
|
|
718
|
-
const { value: milestonePhaseDirs, scope: milestonePhaseScope } = listMilestonePhaseDirs(phasesDir, {
|
|
734
|
+
const { value: milestonePhaseDirs, scope: milestonePhaseScope } = listMilestonePhaseDirs(phasesDir, {
|
|
735
|
+
cwd,
|
|
736
|
+
versionOverride: version,
|
|
737
|
+
phaseIdConvention: phaseConvention,
|
|
738
|
+
});
|
|
719
739
|
try {
|
|
720
740
|
for (const dir of milestonePhaseDirs) {
|
|
721
741
|
phaseCount++;
|
|
@@ -806,7 +826,7 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
806
826
|
// never disagree with what a real run actually archives. Absent
|
|
807
827
|
// --archive-quick this stays `[]` and nothing on disk is touched either
|
|
808
828
|
// way (dry-run always returns before any mutation below).
|
|
809
|
-
const quickDirsToArchive = options.archiveQuick ? listQuickTaskDirsForArchive(cwd) : [];
|
|
829
|
+
const quickDirsToArchive = options.archiveQuick ? listQuickTaskDirsForArchive(cwd).map((d) => d.name) : [];
|
|
810
830
|
const dryRunResult = {
|
|
811
831
|
dry_run: true,
|
|
812
832
|
version,
|
|
@@ -1462,6 +1482,16 @@ function writeQuickArchiveReadme(archiveQuickDir) {
|
|
|
1462
1482
|
* written three times, and only the real-run copy applied `requireSafePath`,
|
|
1463
1483
|
* so a dry-run preview could list a directory the real run would silently
|
|
1464
1484
|
* skip).
|
|
1485
|
+
*
|
|
1486
|
+
* Returns the proven `ContainedPath` alongside each entry's bare `name`
|
|
1487
|
+
* (`no-unconfined-path-join`'s `discardedContainmentResult` arm — a bare
|
|
1488
|
+
* statement call to `requireSafePath` throws away the exact answer it just
|
|
1489
|
+
* computed). The two dry-run previews only need `name` for display;
|
|
1490
|
+
* `archiveQuickTaskDirectories` deliberately does NOT reuse `abs` for its
|
|
1491
|
+
* rename — it re-derives and re-validates independently as TOCTOU
|
|
1492
|
+
* defense-in-depth (see its own comment), so `abs` exists here only to make
|
|
1493
|
+
* this function's own discard explicit, not to be trusted downstream as a
|
|
1494
|
+
* stale-safe proof.
|
|
1465
1495
|
*/
|
|
1466
1496
|
function listQuickTaskDirsForArchive(cwd) {
|
|
1467
1497
|
const planningBase = planningPaths(cwd).planning;
|
|
@@ -1474,19 +1504,20 @@ function listQuickTaskDirsForArchive(cwd) {
|
|
|
1474
1504
|
// .planning/quick absent or unreadable — nothing to select.
|
|
1475
1505
|
return [];
|
|
1476
1506
|
}
|
|
1477
|
-
const
|
|
1507
|
+
const results = [];
|
|
1478
1508
|
for (const entry of sourceEntries) {
|
|
1479
1509
|
if (!entry.isDirectory())
|
|
1480
1510
|
continue; // excludes symlinks too — see MAJOR 3 note above
|
|
1511
|
+
let abs;
|
|
1481
1512
|
try {
|
|
1482
|
-
(0, security_cjs_1.requireSafePath)(node_path_1.default.join(quickDir, entry.name), planningBase, 'quick task dir',
|
|
1513
|
+
abs = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(quickDir, entry.name), planningBase, 'quick task dir', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
|
|
1483
1514
|
}
|
|
1484
1515
|
catch {
|
|
1485
1516
|
continue; // symlink/escape attempt — never a candidate, in preview OR real run
|
|
1486
1517
|
}
|
|
1487
|
-
|
|
1518
|
+
results.push({ name: entry.name, abs });
|
|
1488
1519
|
}
|
|
1489
|
-
return
|
|
1520
|
+
return results.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
1490
1521
|
}
|
|
1491
1522
|
/**
|
|
1492
1523
|
* #2142: move each DIRECTORY entry under `.planning/quick/` into
|
|
@@ -1535,7 +1566,7 @@ function archiveQuickTaskDirectories(cwd, version) {
|
|
|
1535
1566
|
// #2142 MAJOR 5 (review): dirNames is the SAME selection
|
|
1536
1567
|
// `listQuickTaskDirsForArchive` hands to both dry-run previews — this is
|
|
1537
1568
|
// the real run, so it cannot disagree with what a preview reported.
|
|
1538
|
-
const dirNames = listQuickTaskDirsForArchive(cwd);
|
|
1569
|
+
const dirNames = listQuickTaskDirsForArchive(cwd).map((d) => d.name);
|
|
1539
1570
|
if (dirNames.length === 0) {
|
|
1540
1571
|
// Boundary 0 (#2142): zero (safe) directory entries (empty dir, only
|
|
1541
1572
|
// stray files, or every entry excluded by the selection rule) must not
|
|
@@ -1556,7 +1587,7 @@ function archiveQuickTaskDirectories(cwd, version) {
|
|
|
1556
1587
|
// rename are two separate filesystem observations, and an entry
|
|
1557
1588
|
// that was a safe real directory at selection time could in theory
|
|
1558
1589
|
// be swapped for a symlink before this loop reaches it.
|
|
1559
|
-
safeSrc = (0, security_cjs_1.requireSafePath)(src, planningBase, 'quick task dir',
|
|
1590
|
+
safeSrc = (0, security_cjs_1.requireSafePath)(src, planningBase, 'quick task dir', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
|
|
1560
1591
|
}
|
|
1561
1592
|
catch {
|
|
1562
1593
|
continue; // symlink/escape attempt — skip, not archived
|
|
@@ -1649,7 +1680,7 @@ function cmdQuickArchive(cwd, version, options, raw) {
|
|
|
1649
1680
|
// `cmdMilestoneComplete`'s own dry-run preview and the real
|
|
1650
1681
|
// `archiveQuickTaskDirectories` both use, so all three can never disagree.
|
|
1651
1682
|
if (options.dryRun) {
|
|
1652
|
-
const quickDirsToArchive = listQuickTaskDirsForArchive(cwd);
|
|
1683
|
+
const quickDirsToArchive = listQuickTaskDirsForArchive(cwd).map((d) => d.name);
|
|
1653
1684
|
output({
|
|
1654
1685
|
dry_run: true,
|
|
1655
1686
|
version,
|
|
@@ -128,6 +128,67 @@ function _resolveRuntimeTier(config, tier) {
|
|
|
128
128
|
overrides: config['model_profile_overrides'],
|
|
129
129
|
});
|
|
130
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* #4192 — Resolve the claude-runtime TIER OVERRIDE model for (config, tier).
|
|
133
|
+
*
|
|
134
|
+
* Step 3's runtime-aware resolution deliberately skips the claude runtime to
|
|
135
|
+
* preserve the alias-native posture (#1156/#2297): with no user override, the
|
|
136
|
+
* resolver must keep returning bare tier aliases, and the builtin claude tier
|
|
137
|
+
* map (`opus → claude-opus-4-8`, …) must never force full-ID emission on every
|
|
138
|
+
* default install. But `model_profile_overrides.<runtime>.<tier>` is a
|
|
139
|
+
* documented override point (docs/CONFIGURATION.md § Runtime-Aware Profiles)
|
|
140
|
+
* that `workflows/settings-advanced.md` actively writes for claude-runtime
|
|
141
|
+
* users — and #4192 Finding 1 measured the key inert on this runtime.
|
|
142
|
+
*
|
|
143
|
+
* This helper reads ONLY the user's override entry for the effective claude
|
|
144
|
+
* runtime and tier — never the builtin claude tier map — so an install with no
|
|
145
|
+
* override is byte-identical to before the fix. The runtime is resolved the
|
|
146
|
+
* same way steps 1-3 resolve it (config['runtime'], defaulting to 'claude'),
|
|
147
|
+
* NOT via resolveActiveRuntime (GSD_RUNTIME/marker): the value policy must
|
|
148
|
+
* key off the config the operator wrote, matching mapClaudeOverrideForRuntime.
|
|
149
|
+
*
|
|
150
|
+
* Value policy mirrors the model_overrides path (#2041/#4192): an override
|
|
151
|
+
* value that maps to a current tier alias collapses to that alias
|
|
152
|
+
* (byte-equivalent resolution, alias-form emission); anything else — a pinned
|
|
153
|
+
* older generation (`claude-opus-4-7`), a bare alias/tier repoint (`sonnet`),
|
|
154
|
+
* or a non-Claude vendor id (`openai/o3`) — is emitted verbatim as pinned.
|
|
155
|
+
* Malformed entries (no usable `model` string) return null so the caller falls
|
|
156
|
+
* through to normal alias resolution (ADR-443 D1: invalid values fall through).
|
|
157
|
+
*/
|
|
158
|
+
function resolveClaudeTierOverrideModel(configRuntime, tier, overrides) {
|
|
159
|
+
if (!tier || tier === 'inherit')
|
|
160
|
+
return null;
|
|
161
|
+
const effectiveRuntime = configRuntime || 'claude';
|
|
162
|
+
if (effectiveRuntime !== 'claude')
|
|
163
|
+
return null; // non-claude runtimes resolve at step 3
|
|
164
|
+
const overridesMap = overrides;
|
|
165
|
+
if (!overridesMap || typeof overridesMap !== 'object')
|
|
166
|
+
return null;
|
|
167
|
+
// Own-property guards throughout: both levels are config-supplied plain
|
|
168
|
+
// objects, so a prototype-chain key ("constructor", "toString") must not
|
|
169
|
+
// resolve an inherited member instead of falling through (same hardening as
|
|
170
|
+
// every other config-keyed lookup in this module).
|
|
171
|
+
const runtimeEntry = Object.hasOwn(overridesMap, effectiveRuntime)
|
|
172
|
+
? overridesMap[effectiveRuntime]
|
|
173
|
+
: undefined;
|
|
174
|
+
if (!runtimeEntry || typeof runtimeEntry !== 'object')
|
|
175
|
+
return null;
|
|
176
|
+
const userRaw = Object.hasOwn(runtimeEntry, tier) ? runtimeEntry[tier] : undefined;
|
|
177
|
+
if (userRaw === undefined || userRaw === null)
|
|
178
|
+
return null;
|
|
179
|
+
const entry = typeof userRaw === 'string'
|
|
180
|
+
? { model: userRaw }
|
|
181
|
+
: userRaw;
|
|
182
|
+
if (!entry || typeof entry !== 'object')
|
|
183
|
+
return null;
|
|
184
|
+
const model = entry['model'];
|
|
185
|
+
if (typeof model !== 'string' || model.length === 0)
|
|
186
|
+
return null;
|
|
187
|
+
if (Object.hasOwn(CLAUDE_POLICY_ID_TO_ALIAS, model)) {
|
|
188
|
+
return CLAUDE_POLICY_ID_TO_ALIAS[model];
|
|
189
|
+
}
|
|
190
|
+
return model;
|
|
191
|
+
}
|
|
131
192
|
// Reverse of the Claude tier-default IDs, plus the Fable alias which Claude
|
|
132
193
|
// Code's Agent tool accepts but which is not a GSD model-profile tier (#1133).
|
|
133
194
|
const CLAUDE_POLICY_ID_TO_ALIAS = {
|
|
@@ -154,7 +215,10 @@ function warnModelPolicyUnmappable(agentType, policyModel, tier) {
|
|
|
154
215
|
function _resetModelPolicyWarningCacheForTests() {
|
|
155
216
|
_modelPolicyUnmappableWarned.clear();
|
|
156
217
|
}
|
|
157
|
-
// Dedupe stderr warnings for unmappable model_overrides Claude IDs (#2041
|
|
218
|
+
// Dedupe stderr warnings for unmappable model_overrides Claude IDs (#2041 /
|
|
219
|
+
// #4192). #2041 originally warned that such a value was being DROPPED to tier
|
|
220
|
+
// resolution; #4192 keeps the warn-once breadcrumb but changes the behavior to
|
|
221
|
+
// a verbatim pass-through, so the text now describes the pass-through.
|
|
158
222
|
const _modelOverrideUnmappableWarned = new Set();
|
|
159
223
|
function warnModelOverrideUnmappable(agentType, overrideValue) {
|
|
160
224
|
const key = `${agentType}::${overrideValue}`;
|
|
@@ -165,8 +229,9 @@ function warnModelOverrideUnmappable(agentType, overrideValue) {
|
|
|
165
229
|
// full to stderr/logs (#2041 security review). MUST go to stderr — resolve-
|
|
166
230
|
// model's JSON result is parsed from stdout.
|
|
167
231
|
const safe = overrideValue.length > 64 ? overrideValue.slice(0, 64) + '…' : overrideValue;
|
|
168
|
-
process.stderr.write(`gsd: warning — model_overrides value "${safe}" for ${agentType} ` +
|
|
169
|
-
`
|
|
232
|
+
process.stderr.write(`gsd: warning — model_overrides value "${safe}" for ${agentType} is a fully-qualified ` +
|
|
233
|
+
`Claude model ID with no tier alias; passing it through verbatim. Claude Code setups ` +
|
|
234
|
+
`whose Agent tool accepts only tier aliases will not honor it. (#4192)\n`);
|
|
170
235
|
}
|
|
171
236
|
// Test-only: reset the model_overrides warn-dedupe cache between cases (#2041).
|
|
172
237
|
function _resetModelOverrideWarningCacheForTests() {
|
|
@@ -175,12 +240,24 @@ function _resetModelOverrideWarningCacheForTests() {
|
|
|
175
240
|
/**
|
|
176
241
|
* #2041 — Map a `model_overrides` value to its Claude Agent-tool alias on the
|
|
177
242
|
* claude runtime, mirroring the `model_policy` path (#1144). Claude Code's
|
|
178
|
-
* Agent tool `model` parameter documents
|
|
179
|
-
* fable)
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
243
|
+
* Agent tool `model` parameter documents tier aliases (opus/sonnet/haiku/
|
|
244
|
+
* fable) as the always-accepted form. Returns the value to emit verbatim, or
|
|
245
|
+
* null to signal "fall through to normal tier/dynamic-routing resolution".
|
|
246
|
+
* Non-Claude runtimes and non-Claude values always pass through verbatim.
|
|
247
|
+
*
|
|
248
|
+
* #4192 — an unmappable `claude-*` value (a pinned generation that is not the
|
|
249
|
+
* current catalog default, e.g. `claude-opus-4-7`) is now PASSED THROUGH
|
|
250
|
+
* VERBATIM with a warn-once stderr breadcrumb, instead of being dropped to
|
|
251
|
+
* tier resolution. #2041's drop was correct when the value was plausibly a
|
|
252
|
+
* mis-typed current default, but for an explicit pin it silently UNPINNED the
|
|
253
|
+
* operator's choice — the resolver would report a tier the config never asked
|
|
254
|
+
* for, the exact "profile can misrepresent what actually runs" defect #4192
|
|
255
|
+
* files. The documented contract ("any fully-qualified model ID",
|
|
256
|
+
* docs/CONFIGURATION.md § Per-Agent Overrides,
|
|
257
|
+
* gsd-core/references/model-profiles.md § Per-Agent Overrides) is restored:
|
|
258
|
+
* the pin is resolved as configured. Values that DO map to a current tier
|
|
259
|
+
* alias still collapse to that alias — byte-equivalent resolution, the #2041
|
|
260
|
+
* protection preserved — and a mappable pin never warns.
|
|
184
261
|
*
|
|
185
262
|
* Hardening (code+security review): a `typeof` guard preserves the pre-fix
|
|
186
263
|
* no-crash behavior if a malformed config surfaces a non-string value, and an
|
|
@@ -204,8 +281,9 @@ function mapClaudeOverrideForRuntime(override, configRuntime, agentType) {
|
|
|
204
281
|
if (model_catalog_cjs_1.CLAUDE_AGENT_ALIASES.has(override))
|
|
205
282
|
return override;
|
|
206
283
|
if (override.startsWith('claude-')) {
|
|
284
|
+
// #4192: explicit generation pin — resolve as configured (see docblock).
|
|
207
285
|
warnModelOverrideUnmappable(agentType, override);
|
|
208
|
-
return
|
|
286
|
+
return override;
|
|
209
287
|
}
|
|
210
288
|
return override;
|
|
211
289
|
}
|
|
@@ -467,6 +545,19 @@ function resolveModelInternal(cwd, agentType) {
|
|
|
467
545
|
&& (projectExplicitlySetsOmit(cwd) || !RUNTIMES_WITH_NATIVE_ALIASES.has(resolveActiveRuntime(config)))) {
|
|
468
546
|
return '';
|
|
469
547
|
}
|
|
548
|
+
// 4.5. Claude-runtime tier override (#4192 Finding 1). Sits AFTER the omit
|
|
549
|
+
// gate so an explicit project `resolve_model_ids:"omit"` still wins (#2297:
|
|
550
|
+
// explicit project omit is honored regardless of runtime), and BEFORE the
|
|
551
|
+
// alias return so a pinned generation is not re-collapsed to a tier alias or
|
|
552
|
+
// re-materialized to the LATEST catalog id by step 5's
|
|
553
|
+
// `resolve_model_ids:true` path. Fires ONLY when the user wrote a
|
|
554
|
+
// `model_profile_overrides.claude.<tier>` entry for this tier — see
|
|
555
|
+
// resolveClaudeTierOverrideModel for why the builtin map stays out.
|
|
556
|
+
if (tier && tier !== 'inherit') {
|
|
557
|
+
const claudeOverrideModel = resolveClaudeTierOverrideModel(configRuntime, tier, config['model_profile_overrides']);
|
|
558
|
+
if (claudeOverrideModel !== null)
|
|
559
|
+
return claudeOverrideModel;
|
|
560
|
+
}
|
|
470
561
|
// 5. Profile lookup (Claude-native default).
|
|
471
562
|
if (!agentModels) {
|
|
472
563
|
return profile === 'quality' ? 'opus'
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Unsupported in this router:
|
|
7
7
|
* - scaffold: routed through top-level scaffold command.
|
|
8
8
|
*
|
|
9
|
-
* CJS-only subcommands: mvp-mode (dispatched directly, before hub).
|
|
9
|
+
* CJS-only subcommands: mvp-mode, tdd-applicable (dispatched directly, before hub).
|
|
10
10
|
*
|
|
11
11
|
* #3788: dispatch is mediated by CommandRoutingHub. The public entry point
|
|
12
12
|
* and observable CLI behaviour are unchanged.
|
|
@@ -57,6 +57,13 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
|
|
|
57
57
|
phase.cmdPhaseMvpMode(cwd, args.slice(2), raw);
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
60
|
+
// `tdd-applicable` (#4273): same CJS-native dispatch shape as `mvp-mode`
|
|
61
|
+
// immediately above — a precedence cascade over plan/task/config sources
|
|
62
|
+
// with its own typed JSON result, not routed through the SDK query layer.
|
|
63
|
+
if (subcommand === 'tdd-applicable') {
|
|
64
|
+
phase.cmdPhaseTddApplicable(cwd, args.slice(2), raw);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
60
67
|
// ── Build the CJS registry ──────────────────────────────────────────────────
|
|
61
68
|
// Each handler receives a ctx object from the hub and must return a HubResult.
|
|
62
69
|
const cjsRegistry = {
|
|
@@ -119,7 +126,13 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
|
|
|
119
126
|
if (args.includes('--dry-run')) {
|
|
120
127
|
return makeInvalidArgs('--dry-run', 'phase insert does not support --dry-run');
|
|
121
128
|
}
|
|
122
|
-
|
|
129
|
+
// #4569: --sibling opts into joining afterPhase's parent decimal level
|
|
130
|
+
// instead of nesting one level deeper. Filtered out like other
|
|
131
|
+
// boolean flags (see `remove`'s --force handling above) so it never
|
|
132
|
+
// leaks into the free-text description.
|
|
133
|
+
const sibling = args.includes('--sibling');
|
|
134
|
+
const insertArgs = args.slice(2).filter(token => token !== '--sibling');
|
|
135
|
+
phase.cmdPhaseInsert(cwd, insertArgs[0], insertArgs.slice(1).join(' '), raw, sibling ? 'sibling' : 'nested');
|
|
123
136
|
return { ok: true, data: null };
|
|
124
137
|
},
|
|
125
138
|
remove: (_ctx) => {
|
|
@@ -224,14 +237,14 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
|
|
|
224
237
|
// ── Build manifest (available subcommands for UnknownCommand detection) ─────
|
|
225
238
|
// `availableSubcommands` is what the error message shows. It excludes
|
|
226
239
|
// unsupported commands (already handled above) but does NOT include 'mvp-mode'
|
|
227
|
-
// because
|
|
228
|
-
// shown in the "Available:" list there either.
|
|
240
|
+
// or 'tdd-applicable' (#4273) because both are absent from PHASE_SUBCOMMANDS
|
|
241
|
+
// and were not shown in the "Available:" list there either.
|
|
229
242
|
//
|
|
230
243
|
// `manifestSubcommands` is the full routing set for the hub — it includes
|
|
231
|
-
// 'mvp-mode'
|
|
232
|
-
// manifest entry) so the hub's UnknownCommand check passes for
|
|
244
|
+
// 'mvp-mode' and 'tdd-applicable' (both routed via a handler even without a
|
|
245
|
+
// manifest entry) so the hub's UnknownCommand check passes for them.
|
|
233
246
|
const availableSubcommands = command_aliases_cjs_1.PHASE_SUBCOMMANDS.filter(s => !UNSUPPORTED[s]);
|
|
234
|
-
const manifestSubcommands = ['mvp-mode', ...availableSubcommands];
|
|
247
|
+
const manifestSubcommands = ['mvp-mode', 'tdd-applicable', ...availableSubcommands];
|
|
235
248
|
const manifest = { phase: manifestSubcommands };
|
|
236
249
|
// ── Construct hub ──────────────────────────────────────────────────────────
|
|
237
250
|
// #175: Hub is CJS-only — no mode param, no sdkLoader.
|