@opengsd/gsd-core 1.13.0 → 1.15.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/README.ja-JP.md +3 -3
- package/README.ko-KR.md +3 -3
- package/README.pt-BR.md +3 -3
- package/README.zh-CN.md +3 -3
- 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 +459 -0
- package/agents/gsd-code-fixer.md +9 -8
- 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 +360 -0
- package/agents/gsd-debug-session-manager.md +17 -2
- package/agents/gsd-debugger.md +2 -2
- 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-auditor.md +1 -1
- package/agents/gsd-eval-planner.compact.md +137 -0
- package/agents/gsd-executor.md +13 -8
- 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-intel-updater.md +1 -1
- 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-phase-researcher.md +19 -11
- package/agents/gsd-plan-checker.md +8 -7
- package/agents/gsd-planner.md +12 -8
- package/agents/gsd-project-researcher.compact.md +587 -0
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +212 -0
- package/agents/gsd-research-synthesizer.md +1 -1
- 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-auditor.md +155 -17
- package/agents/gsd-ui-checker.compact.md +277 -0
- package/agents/gsd-ui-researcher.compact.md +282 -0
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-user-profiler.compact.md +108 -0
- package/agents/gsd-verifier.md +10 -9
- package/bin/install.js +848 -163
- package/commands/gsd/autonomous.md +2 -2
- package/commands/gsd/capture.md +1 -1
- 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/graphify.md +1 -0
- package/commands/gsd/health.md +1 -0
- package/commands/gsd/mempalace-capture.md +8 -3
- 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/pause-work.md +1 -0
- package/commands/gsd/phase.md +1 -0
- package/commands/gsd/plan-review-convergence.md +6 -6
- package/commands/gsd/pr-branch.md +1 -0
- package/commands/gsd/progress.md +1 -1
- package/commands/gsd/quick-batch.md +1 -1
- package/commands/gsd/resume-work.md +1 -0
- package/commands/gsd/review-backlog.md +1 -0
- package/commands/gsd/review.md +2 -3
- package/commands/gsd/settings.md +2 -1
- package/commands/gsd/stats.md +1 -0
- 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 +672 -146
- package/gsd-core/bin/lib/adr-parser.cjs +4 -2
- package/gsd-core/bin/lib/artifacts.cjs +2 -1
- package/gsd-core/bin/lib/audit.cjs +119 -34
- package/gsd-core/bin/lib/broken-windows.cjs +168 -49
- 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 +96 -189
- package/gsd-core/bin/lib/capability-source.cjs +19 -2
- package/gsd-core/bin/lib/capability-validator.cjs +14 -2
- package/gsd-core/bin/lib/check-command-router.cjs +213 -49
- package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
- package/gsd-core/bin/lib/codex-agent-toml.cjs +21 -25
- package/gsd-core/bin/lib/commands.cjs +823 -112
- package/gsd-core/bin/lib/config-loader.cjs +66 -4
- package/gsd-core/bin/lib/config.cjs +186 -45
- package/gsd-core/bin/lib/coverage.cjs +1 -1
- package/gsd-core/bin/lib/decisions.cjs +164 -45
- package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
- package/gsd-core/bin/lib/frontmatter.cjs +13 -0
- package/gsd-core/bin/lib/graphify.cjs +10 -2
- package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +12 -1
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
- package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
- package/gsd-core/bin/lib/init.cjs +614 -86
- package/gsd-core/bin/lib/install-engine.cjs +29 -3
- package/gsd-core/bin/lib/install-profiles.cjs +14 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +41 -5
- package/gsd-core/bin/lib/loop-resolver.cjs +50 -31
- package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
- package/gsd-core/bin/lib/milestone.cjs +37 -13
- package/gsd-core/bin/lib/model-resolver.cjs +253 -53
- package/gsd-core/bin/lib/phase-command-router.cjs +16 -2
- package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
- package/gsd-core/bin/lib/phase-id.cjs +268 -27
- package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
- package/gsd-core/bin/lib/phase-locator.cjs +29 -10
- package/gsd-core/bin/lib/phase.cjs +393 -88
- package/gsd-core/bin/lib/plan-document.cjs +49 -1
- package/gsd-core/bin/lib/planning-document.cjs +459 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +52 -19
- package/gsd-core/bin/lib/planning-snapshot.cjs +61 -12
- package/gsd-core/bin/lib/planning-workspace.cjs +57 -3
- package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
- package/gsd-core/bin/lib/probe-core.cjs +7 -1
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
- package/gsd-core/bin/lib/project-root.cjs +41 -2
- package/gsd-core/bin/lib/quick-batch.cjs +1 -1
- 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 +10 -30
- package/gsd-core/bin/lib/review-lane-invocation.cjs +23 -0
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
- package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +12 -4
- package/gsd-core/bin/lib/roadmap-parser.cjs +219 -18
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +1539 -13
- package/gsd-core/bin/lib/roadmap.cjs +356 -42
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +310 -41
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +15 -4
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +13 -5
- package/gsd-core/bin/lib/runtime-homes.cjs +4 -0
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -37
- package/gsd-core/bin/lib/runtime-name-policy.cjs +111 -1
- package/gsd-core/bin/lib/security.cjs +126 -7
- package/gsd-core/bin/lib/shell-command-projection.cjs +10 -6
- package/gsd-core/bin/lib/state-document.cjs +130 -28
- package/gsd-core/bin/lib/state-md-schema.cjs +21 -14
- package/gsd-core/bin/lib/state-transition.cjs +181 -30
- package/gsd-core/bin/lib/state.cjs +265 -27
- package/gsd-core/bin/lib/surface.cjs +77 -3
- package/gsd-core/bin/lib/task-command-router.cjs +12 -6
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +78 -5
- package/gsd-core/bin/lib/uat-predicate.cjs +47 -4
- package/gsd-core/bin/lib/uat.cjs +9 -1
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +100 -9
- package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
- package/gsd-core/bin/lib/update-context.cjs +30 -24
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
- package/gsd-core/bin/lib/verification.cjs +315 -30
- package/gsd-core/bin/lib/verify-command-grounding.cjs +47 -3
- package/gsd-core/bin/lib/verify.cjs +320 -48
- package/gsd-core/bin/lib/workstream-inventory.cjs +1 -0
- package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
- package/gsd-core/bin/lib/worktree-safety.cjs +797 -58
- package/gsd-core/bin/shared/config-defaults.manifest.json +4 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +6 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +439 -80
- package/gsd-core/references/checkpoints.md +5 -3
- package/gsd-core/references/compact-content-gate.md +66 -0
- package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe.md +195 -21
- package/gsd-core/references/execute-phase-between-wave-reset.md +7 -6
- package/gsd-core/references/execute-phase-wave-guard.md +22 -11
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +18 -0
- package/gsd-core/references/model-profiles.md +13 -4
- package/gsd-core/references/phase-argument-parsing.md +9 -7
- package/gsd-core/references/phase-id-convention.md +28 -0
- package/gsd-core/references/planner-gap-closure.md +2 -0
- package/gsd-core/references/planner-load-graph-context.md +24 -13
- package/gsd-core/references/planner-verify-command-grounding.md +14 -0
- package/gsd-core/references/planning-config.md +14 -2
- package/gsd-core/references/tdd.md +30 -4
- package/gsd-core/references/thinking-models-planning.md +18 -2
- package/gsd-core/references/ui-consideration-probe.md +10 -5
- package/gsd-core/references/verification-patterns.md +17 -4
- package/gsd-core/references/verify-command-path-resolvability.md +10 -2
- package/gsd-core/references/worktree-path-safety.md +433 -2
- package/gsd-core/templates/README.md +7 -1
- 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 +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 +1 -1
- package/gsd-core/workflows/add-tests.md +2 -2
- package/gsd-core/workflows/add-todo.md +6 -5
- package/gsd-core/workflows/ai-integration-phase.md +11 -3
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +1 -1
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
- package/gsd-core/workflows/autonomous.md +29 -16
- package/gsd-core/workflows/check-todos.md +6 -4
- package/gsd-core/workflows/cleanup.md +5 -3
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +8 -1
- package/gsd-core/workflows/code-review-fix.md +108 -22
- package/gsd-core/workflows/code-review.md +216 -73
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
- package/gsd-core/workflows/complete-milestone.md +41 -264
- package/gsd-core/workflows/debug.md +3 -3
- package/gsd-core/workflows/diagnose-issues.md +1 -1
- 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 +1 -1
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +2 -2
- package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
- package/gsd-core/workflows/docs-update.md +17 -158
- package/gsd-core/workflows/edit-phase.md +1 -1
- package/gsd-core/workflows/eval-review.md +10 -3
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1017 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +19 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +43 -4
- package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +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 +46 -9
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
- package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +24 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
- package/gsd-core/workflows/execute-phase.md +83 -172
- package/gsd-core/workflows/execute-plan.md +24 -10
- package/gsd-core/workflows/explore.md +3 -3
- package/gsd-core/workflows/extract-learnings.md +2 -1
- 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 -2
- package/gsd-core/workflows/help/modes/full.compact.md +398 -0
- package/gsd-core/workflows/help/modes/full.md +5 -5
- package/gsd-core/workflows/help/modes/topic.md +15 -5
- package/gsd-core/workflows/help.md +1 -1
- package/gsd-core/workflows/import.md +2 -2
- package/gsd-core/workflows/inbox.md +2 -2
- package/gsd-core/workflows/ingest-docs.md +3 -3
- package/gsd-core/workflows/insert-phase.md +1 -1
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +2 -2
- package/gsd-core/workflows/map-codebase.md +52 -5
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +56 -14
- package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
- package/gsd-core/workflows/new-project.md +39 -209
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/note.md +1 -1
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
- package/gsd-core/workflows/plan-phase.md +45 -187
- package/gsd-core/workflows/plan-review-convergence.md +21 -5
- package/gsd-core/workflows/plant-seed.md +62 -20
- package/gsd-core/workflows/pr-branch.md +132 -20
- package/gsd-core/workflows/profile-user.md +2 -2
- package/gsd-core/workflows/progress.md +1 -1
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +25 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +1 -1
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +1 -1
- 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 +1 -1
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +1 -1
- package/gsd-core/workflows/quick.md +29 -10
- package/gsd-core/workflows/reapply-patches.md +86 -6
- package/gsd-core/workflows/remove-phase.md +1 -1
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +1 -1
- package/gsd-core/workflows/review.md +31 -16
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +3 -2
- package/gsd-core/workflows/settings-advanced.md +30 -10
- package/gsd-core/workflows/settings-integrations.md +2 -3
- package/gsd-core/workflows/settings.md +22 -9
- package/gsd-core/workflows/ship.md +3 -2
- 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 +2 -2
- package/gsd-core/workflows/spec-phase.md +15 -5
- 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 +5 -5
- package/gsd-core/workflows/thread.md +1 -1
- package/gsd-core/workflows/transition.md +1 -1
- package/gsd-core/workflows/ui-phase.md +44 -8
- package/gsd-core/workflows/ui-review.md +18 -4
- package/gsd-core/workflows/ultraplan-phase.md +1 -1
- package/gsd-core/workflows/undo.md +339 -20
- package/gsd-core/workflows/update.md +14 -12
- package/gsd-core/workflows/validate-phase.md +3 -2
- package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +101 -196
- package/hooks/dist/gsd-agent-isolation-guard.js +66 -16
- package/hooks/dist/gsd-context-monitor.js +88 -15
- package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
- package/hooks/dist/gsd-secret-read-guard.js +71 -19
- package/hooks/dist/gsd-statusline.js +81 -20
- package/hooks/dist/gsd-validate-commit.sh +97 -8
- 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/isolation-deny-reason.js +53 -1
- package/hooks/dist/lib/isolation-sentinel.js +58 -19
- package/hooks/gsd-agent-isolation-guard.js +66 -16
- package/hooks/gsd-context-monitor.js +88 -15
- package/hooks/gsd-cursor-subagent-start.js +34 -14
- package/hooks/gsd-secret-read-guard.js +71 -19
- package/hooks/gsd-statusline.js +81 -20
- package/hooks/gsd-validate-commit.sh +97 -8
- package/hooks/gsd-worktree-path-guard.js +25 -14
- package/hooks/gsd-write-guard.js +46 -1
- package/hooks/lib/dispatch-identity.js +187 -0
- package/hooks/lib/filename-classification.js +64 -0
- package/hooks/lib/isolation-deny-reason.js +53 -1
- package/hooks/lib/isolation-sentinel.js +58 -19
- package/package.json +11 -6
- package/scripts/benchmark-compact-content-variants.cjs +298 -0
- package/scripts/benchmark-compact-content.cjs +368 -0
- package/scripts/build-hooks.js +15 -6
- package/scripts/check-contract-drift.cjs +131 -12
- 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 +126 -20
- package/scripts/ci-timeout-report.cjs +1 -1
- package/scripts/command-contract-helpers.cjs +3 -0
- package/scripts/diff-touches-shipped-paths.cjs +1 -1
- package/scripts/docs-guard-registry.cjs +35 -2
- package/scripts/gen-adr-index.cjs +8 -2
- package/scripts/gen-inventory-manifest.cjs +12 -0
- package/scripts/gen-loop-host-contract.cjs +69 -0
- 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 +224 -0
- package/scripts/lib/ndjson-reporter.cjs +3 -2
- package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +287 -0
- package/scripts/lib/suite-detection.cjs +32 -0
- package/scripts/lint-allowed-tools-parity.cjs +221 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +47 -3
- package/scripts/lint-phase-arg-assignment.cjs +257 -0
- package/scripts/lint-phase-id-drift.cjs +623 -13
- package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
- package/scripts/lint-response-language-coverage.cjs +9 -3
- package/scripts/lint-retired-runtime-name.cjs +619 -0
- package/scripts/lint-source-test-name-collision.cjs +1 -1
- package/scripts/lint-state-write-path-drift.cjs +93 -0
- package/scripts/lint-test-file-count.allowlist.json +29 -9
- package/scripts/lint-vendored-deps.cjs +128 -17
- package/scripts/lint-workflow-shellcheck-baseline.json +100 -0
- package/scripts/prompt-injection-scan.sh +18 -0
- package/scripts/release-tarball-smoke.cjs +194 -1
- package/scripts/workflow-size.cjs +139 -0
- package/skills/gsd-autonomous/SKILL.md +2 -2
- package/skills/gsd-capture/SKILL.md +1 -1
- 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-graphify/SKILL.md +1 -0
- package/skills/gsd-health/SKILL.md +1 -0
- package/skills/gsd-mempalace-capture/SKILL.md +8 -3
- 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-pause-work/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +1 -0
- package/skills/gsd-plan-review-convergence/SKILL.md +5 -5
- package/skills/gsd-pr-branch/SKILL.md +1 -0
- package/skills/gsd-progress/SKILL.md +1 -1
- package/skills/gsd-quick-batch/SKILL.md +1 -1
- package/skills/gsd-resume-work/SKILL.md +1 -0
- package/skills/gsd-review/SKILL.md +2 -3
- 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-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
|
@@ -23,6 +23,7 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
23
23
|
const node_os_1 = __importDefault(require("node:os"));
|
|
24
24
|
const node_path_1 = __importDefault(require("node:path"));
|
|
25
25
|
const runtimeArtifactConversion = require("./runtime-artifact-conversion.cjs");
|
|
26
|
+
const security_cjs_1 = require("./security.cjs");
|
|
26
27
|
const runtimeArtifactLayout = require("./runtime-artifact-layout.cjs");
|
|
27
28
|
const runtimeArtifactInstallPlan = require("./runtime-artifact-install-plan.cjs");
|
|
28
29
|
const runtimeNamePolicy = require("./runtime-name-policy.cjs");
|
|
@@ -111,7 +112,7 @@ function previousOwnedCorpusFiles(configDir, prefix) {
|
|
|
111
112
|
}
|
|
112
113
|
function pruneEmptyCorpusParents(start, stop) {
|
|
113
114
|
let current = node_path_1.default.dirname(start);
|
|
114
|
-
while (current !== stop && current.startsWith(stop + node_path_1.default.sep)) {
|
|
115
|
+
while (current !== stop && current.startsWith(stop + node_path_1.default.sep)) { // allow-handrolled-containment: ancestor-walk loop condition, not a containment gate
|
|
115
116
|
if (installFs().readdirSync(current).length > 0)
|
|
116
117
|
return;
|
|
117
118
|
installFs().rmdirSync(current);
|
|
@@ -347,8 +348,11 @@ function hasExistingSymlinkBetween(root, fullPath, options = {}) {
|
|
|
347
348
|
const resolvedFullPath = node_path_1.default.resolve(fullPath);
|
|
348
349
|
// (a) Path-traversal refusal — ALWAYS enforced, even with opt-in. An untrusted
|
|
349
350
|
// destSubpath string that escapes the install root via '..' is rejected
|
|
350
|
-
// regardless of user opt-in state (ADR-1239 Phase B threat (a)).
|
|
351
|
-
|
|
351
|
+
// regardless of user opt-in state (ADR-1239 Phase B threat (a)). Lexical
|
|
352
|
+
// (ADR-4650 decision 6): this function's whole purpose is to DETECT
|
|
353
|
+
// symlinks between root and target, so resolving them here would erase what
|
|
354
|
+
// it measures.
|
|
355
|
+
if ((0, security_cjs_1.tryWithinRootLexical)(resolvedFullPath, resolvedRoot) === null) {
|
|
352
356
|
return true;
|
|
353
357
|
}
|
|
354
358
|
// #2393 (security-review finding): realpathSync fully resolves all symlink
|
|
@@ -726,6 +730,16 @@ function _copyStaged(stagedDir, destDir, kind, configDir, runtime) {
|
|
|
726
730
|
continue;
|
|
727
731
|
if (!entry.name.endsWith('.md'))
|
|
728
732
|
continue;
|
|
733
|
+
// #4782: compact agent variants are consumed ONLY through the non-claude
|
|
734
|
+
// gate in init.cts's agent-skills persona fallback (`runtime !== 'claude'`
|
|
735
|
+
// — claude's contract is a skills-injection path, never a persona
|
|
736
|
+
// fallback). Staging them into Claude's agents directory shipped 29 dead
|
|
737
|
+
// files whose `name:` frontmatter is identical to their canonical
|
|
738
|
+
// sibling's, leaving the harness resolution unstated. Claude never
|
|
739
|
+
// selects compact, so claude is the one runtime whose agents kind skips
|
|
740
|
+
// them; every other runtime's emission is byte-identical.
|
|
741
|
+
if (kind.kind === 'agents' && runtime === 'claude' && entry.name.endsWith('.compact.md'))
|
|
742
|
+
continue;
|
|
729
743
|
const stem = entry.name.slice(0, -3); // strip .md
|
|
730
744
|
let destName;
|
|
731
745
|
if (kind.kind === 'agents') {
|
|
@@ -1492,6 +1506,15 @@ function installOpencodeFamilySkills(runtime, targetDir, rawCommandsDir, pathPre
|
|
|
1492
1506
|
content = applyOpencodeFamilyPathPrefix(content, runtime, pathPrefix);
|
|
1493
1507
|
content = processAttribution(content, resolveAttribution(runtime));
|
|
1494
1508
|
const skillDir = node_path_1.default.join(dest, skillName);
|
|
1509
|
+
// isPathConfined is lexical and cannot see a symlink. mkdirSync({recursive:true})
|
|
1510
|
+
// does NOT throw when skillDir already exists as a symlink to a directory, so a
|
|
1511
|
+
// pre-planted link would redirect the SKILL.md write outside `dest`. Refuse to
|
|
1512
|
+
// write through a link (epic #4636; mirrors retired-artifact-cleanup.cts:77).
|
|
1513
|
+
try {
|
|
1514
|
+
if (installFs().lstatSync(skillDir).isSymbolicLink())
|
|
1515
|
+
continue;
|
|
1516
|
+
}
|
|
1517
|
+
catch { /* ENOENT: not created yet — the normal case */ }
|
|
1495
1518
|
installFs().mkdirSync(skillDir, { recursive: true });
|
|
1496
1519
|
installFs().writeFileSync(node_path_1.default.join(skillDir, 'SKILL.md'), content);
|
|
1497
1520
|
// #2322 HIGH-3 parity: persist the capability-owned marker so a later
|
|
@@ -1868,6 +1891,9 @@ function installOpencodeFamilyArtifacts(runtime, configDir, scope, resolvedProfi
|
|
|
1868
1891
|
isWindowsHost: process.platform === 'win32',
|
|
1869
1892
|
resolvedTarget: (0, shell_command_projection_cjs_1.posixNormalize)(node_path_1.default.resolve(configDir)),
|
|
1870
1893
|
homeDir: (0, shell_command_projection_cjs_1.posixNormalize)(node_os_1.default.homedir()),
|
|
1894
|
+
// #4377: the runtime's own localConfigDir, so an opted-in local install
|
|
1895
|
+
// emits `<dir>/...` instead of this checkout's absolute path.
|
|
1896
|
+
localDirName: runtimeArtifactConversion._localIncludeDirName(runtime),
|
|
1871
1897
|
});
|
|
1872
1898
|
// #2329: destDir is derived from the SAME hostBehaviors.flatCommandDir
|
|
1873
1899
|
// descriptor value read by writeManifest's manifest-key prefix and by
|
|
@@ -740,6 +740,11 @@ function readInstalledCapabilitySkill(stem, registry) {
|
|
|
740
740
|
return null;
|
|
741
741
|
const skillPath = node_path_1.default.join(capDir, relSkillPath);
|
|
742
742
|
try {
|
|
743
|
+
// statSync follows symlinks; isPathConfined is lexical and cannot see one.
|
|
744
|
+
// Refuse to read through a link so an outside file's content cannot be
|
|
745
|
+
// installed as a capability skill (epic #4636).
|
|
746
|
+
if (node_fs_1.default.lstatSync(skillPath).isSymbolicLink())
|
|
747
|
+
return null;
|
|
743
748
|
if (!node_fs_1.default.statSync(skillPath).isFile())
|
|
744
749
|
return null;
|
|
745
750
|
return { capId, content: node_fs_1.default.readFileSync(skillPath, 'utf8') };
|
|
@@ -858,6 +863,15 @@ function stageSkillsForRuntimeAsSkills(srcCommandsDir, resolvedProfile, converte
|
|
|
858
863
|
if (!(0, external_descriptor_trust_cjs_1.isPathConfined)(skillName, stageDir))
|
|
859
864
|
continue; // defense-in-depth
|
|
860
865
|
const destDir = node_path_1.default.join(stageDir, skillName);
|
|
866
|
+
// isPathConfined is lexical and cannot see a symlink. mkdirSync({recursive:true})
|
|
867
|
+
// does NOT throw when destDir already exists as a symlink to a directory, so a
|
|
868
|
+
// pre-planted link would redirect the SKILL.md write outside `stageDir`. Refuse
|
|
869
|
+
// to write through a link (epic #4636; mirrors retired-artifact-cleanup.cts:77).
|
|
870
|
+
try {
|
|
871
|
+
if (installFs().lstatSync(destDir).isSymbolicLink())
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
catch { /* ENOENT: not created yet — the normal case */ }
|
|
861
875
|
installFs().mkdirSync(destDir, { recursive: true });
|
|
862
876
|
installFs().writeFileSync(node_path_1.default.join(destDir, 'SKILL.md'), found.content);
|
|
863
877
|
// #2322 HIGH-3: persist the capability-owned marker so a later prune
|
|
@@ -17,6 +17,7 @@ const installer_migration_authoring_cjs_1 = require("./installer-migration-autho
|
|
|
17
17
|
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
|
+
const security_cjs_1 = require("./security.cjs");
|
|
20
21
|
// #2874 (ADR-58 cleanup phase): this file is the ~1200-line migration
|
|
21
22
|
// plan/apply/rollback/lock/journal engine — almost none of it is on the
|
|
22
23
|
// installRuntimeArtifacts call tree. Only `readInstallManifest` and
|
|
@@ -122,8 +123,17 @@ function evaluateRemoveEmptyDir(configDir, fullPath) {
|
|
|
122
123
|
catch {
|
|
123
124
|
return 'left-in-place';
|
|
124
125
|
}
|
|
125
|
-
|
|
126
|
-
|
|
126
|
+
// `resolvedTarget === resolvedRoot` is a DELIBERATE ADDITIONAL rejection,
|
|
127
|
+
// separate from the containment decision: `tryWithinRootLexical` treats
|
|
128
|
+
// target === root as CONTAINED, but removing the config root itself is
|
|
129
|
+
// never in scope for this action (see the doc comment above) — this arm
|
|
130
|
+
// prevents `rmdirSync` from ever being asked to remove `configDir` itself.
|
|
131
|
+
// Kept as its own check per ADR-4650 decision 6 (a wrapper may add its own
|
|
132
|
+
// conditions on top of the canonical predicate, never invert it).
|
|
133
|
+
if (resolvedTarget === resolvedRoot)
|
|
134
|
+
return 'left-in-place';
|
|
135
|
+
if ((0, security_cjs_1.tryWithinRootLexical)(resolvedTarget, resolvedRoot) === null) {
|
|
136
|
+
// Refuses "target escaped configDir".
|
|
127
137
|
return 'left-in-place';
|
|
128
138
|
}
|
|
129
139
|
let entries;
|
|
@@ -270,7 +280,14 @@ function readInstallManifest(configDir) {
|
|
|
270
280
|
version: typeof m.version === 'string' ? m.version : null,
|
|
271
281
|
timestamp: typeof m.timestamp === 'string' ? m.timestamp : null,
|
|
272
282
|
mode: typeof m.mode === 'string' ? m.mode : null,
|
|
273
|
-
|
|
283
|
+
// #4544 (review): `typeof [] === 'object'` — a manifest whose `files` is a
|
|
284
|
+
// JSON array passed the object-shape guard, and Object.keys() then yielded
|
|
285
|
+
// "0","1",... as install-relative file paths. Consumers iterate these keys,
|
|
286
|
+
// so an array shape must degrade to the empty set exactly like a
|
|
287
|
+
// non-object shape does.
|
|
288
|
+
files: m.files && typeof m.files === 'object' && !Array.isArray(m.files)
|
|
289
|
+
? m.files
|
|
290
|
+
: {},
|
|
274
291
|
manifestVersion: normalizeManifestVersion(m.manifestVersion),
|
|
275
292
|
runtime: normalizeReportedRuntime(rawRuntime),
|
|
276
293
|
scope: (0, install_scope_cjs_1.isInstallScopeId)(m.scope) ? m.scope : null,
|
|
@@ -598,11 +615,30 @@ function acquireInstallMigrationLock(configDir, { timeoutMs = DEFAULT_LOCK_TIMEO
|
|
|
598
615
|
}
|
|
599
616
|
}
|
|
600
617
|
}
|
|
618
|
+
// DELIBERATELY LEXICAL — the RESOLUTION policy stays lexical, never realpath
|
|
619
|
+
// (`assertWithinRoot` / `tryWithinRoot`, src/security.cts).
|
|
620
|
+
//
|
|
621
|
+
// Reviewed under epic #4636 Phase 3 and reverted after the remote matrix proved
|
|
622
|
+
// the realpath collapse wrong. This module's contract is that a symlinked
|
|
623
|
+
// managed path is treated AS A LINK and never dereferenced — it is snapshotted
|
|
624
|
+
// as a link, restored as a link, and backed up as a link. The realpath-based
|
|
625
|
+
// predicate dereferences exactly the symlinks this module exists to preserve
|
|
626
|
+
// and then rejects them for escaping configDir
|
|
627
|
+
// ("migration path escapes configDir: extensions/gsd.cjs"). Four tests in
|
|
628
|
+
// tests/installer-migrations.test.cjs pin that behavior.
|
|
629
|
+
//
|
|
630
|
+
// The containment DECISION now routes through the canonical LEXICAL predicate
|
|
631
|
+
// (`tryWithinRootLexical`, ADR-4650 decision 6) — only the comparison moved;
|
|
632
|
+
// the lexical policy itself remains this module's own required choice, and
|
|
633
|
+
// the thrown message / returned `fullPath` are unchanged.
|
|
634
|
+
//
|
|
635
|
+
// `normalizeRelPath` is the pre-gate: it throws on absolute paths and on any
|
|
636
|
+
// '..' segment BEFORE this runs, so the check below is defense-in-depth over
|
|
637
|
+
// already-traversal-free input rather than the primary boundary.
|
|
601
638
|
function ensureInsideConfig(configDir, relPath) {
|
|
602
639
|
const normalized = normalizeRelPath(relPath);
|
|
603
640
|
const fullPath = node_path_1.default.resolve(configDir, normalized);
|
|
604
|
-
|
|
605
|
-
if (fullPath !== root && !fullPath.startsWith(root + node_path_1.default.sep)) {
|
|
641
|
+
if ((0, security_cjs_1.tryWithinRootLexical)(fullPath, configDir) === null) {
|
|
606
642
|
throw new Error(`migration path escapes configDir: ${relPath}`);
|
|
607
643
|
}
|
|
608
644
|
return { normalized, fullPath };
|
|
@@ -224,6 +224,9 @@ function resolveLoopHooks(input) {
|
|
|
224
224
|
active.consumes = consumes;
|
|
225
225
|
if (onError !== undefined)
|
|
226
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;
|
|
227
230
|
activeHooks.push(active);
|
|
228
231
|
}
|
|
229
232
|
// Process contributions
|
|
@@ -422,17 +425,20 @@ function sanitizeLoadFailReason(reason) {
|
|
|
422
425
|
.slice(0, 300);
|
|
423
426
|
return cleaned || '(no reason given)';
|
|
424
427
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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 = {}) {
|
|
436
442
|
const runtimeConfigDir = typeof options['configDir'] === 'string'
|
|
437
443
|
? options['configDir']
|
|
438
444
|
: undefined;
|
|
@@ -468,15 +474,7 @@ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
|
|
|
468
474
|
for (const cap of state.capabilities || []) {
|
|
469
475
|
capabilityStatesById.set(cap.id, cap);
|
|
470
476
|
}
|
|
471
|
-
|
|
472
|
-
try {
|
|
473
|
-
resolved = resolveLoopHooks({ point, registry, config, cwd, capabilityStatesById });
|
|
474
|
-
}
|
|
475
|
-
catch (err) {
|
|
476
|
-
const msg = (err instanceof Error) ? err.message : String(err);
|
|
477
|
-
coreError(msg);
|
|
478
|
-
return;
|
|
479
|
-
}
|
|
477
|
+
const resolved = resolveLoopHooks({ point, registry, config, cwd, capabilityStatesById });
|
|
480
478
|
// ── ADR-1244 D2: load-failed capability gates FAIL OPEN with a loud warning ────
|
|
481
479
|
// Decision (#2009): a capability that failed to LOAD must not block the loop.
|
|
482
480
|
// The prior behavior injected a BLOCKING synthetic gate (blocking:true,
|
|
@@ -525,24 +523,44 @@ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
|
|
|
525
523
|
for (const w of loadFailWarnings) {
|
|
526
524
|
process.stderr.write(`gsd: warning — ${w}\n`);
|
|
527
525
|
}
|
|
528
|
-
//
|
|
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
|
+
}
|
|
529
551
|
if (activeCapId !== undefined) {
|
|
530
|
-
const isActive =
|
|
552
|
+
const isActive = result.activeHooks.some((h) => h.capId === activeCapId);
|
|
531
553
|
process.stdout.write(isActive ? 'true\n' : 'false\n');
|
|
532
554
|
return;
|
|
533
555
|
}
|
|
534
|
-
const rendered = renderLoopHooks(
|
|
556
|
+
const rendered = renderLoopHooks({ point: result.point, activeHooks: result.activeHooks });
|
|
535
557
|
const envelope = {
|
|
536
|
-
point:
|
|
537
|
-
activeHooks:
|
|
558
|
+
point: result.point,
|
|
559
|
+
activeHooks: result.activeHooks,
|
|
538
560
|
rendered,
|
|
539
561
|
};
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
// stderr emission above, which is the channel host workflows actually see).
|
|
543
|
-
const combinedWarnings = [...(state.warnings || []), ...loadFailWarnings];
|
|
544
|
-
if (combinedWarnings.length > 0) {
|
|
545
|
-
envelope.warnings = combinedWarnings;
|
|
562
|
+
if (result.warnings.length > 0) {
|
|
563
|
+
envelope.warnings = result.warnings;
|
|
546
564
|
}
|
|
547
565
|
coreOutput(envelope, raw);
|
|
548
566
|
}
|
|
@@ -550,6 +568,7 @@ module.exports = {
|
|
|
550
568
|
resolveLoopHooks,
|
|
551
569
|
renderLoopHooks,
|
|
552
570
|
cmdLoopRenderHooks,
|
|
571
|
+
resolveActiveHooksForPoint,
|
|
553
572
|
// Exported for tests
|
|
554
573
|
_getNestedConfigValue,
|
|
555
574
|
_resolveActivationValue,
|
|
@@ -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;
|
|
@@ -38,7 +38,7 @@ const stateContract = require("./state-contract.cjs");
|
|
|
38
38
|
const { publishStateContract } = stateContract;
|
|
39
39
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
40
40
|
const phaseIdMod = require("./phase-id.cjs");
|
|
41
|
-
const { normalizePhaseName, matchPhaseDirs,
|
|
41
|
+
const { normalizePhaseName, matchPhaseDirs, isSentinelPhaseId, isSentinelPhaseDir, buildPhaseHeadingScanRegex, PHASE_HEADING_BASELINE } = phaseIdMod;
|
|
42
42
|
const pattern_cjs_1 = require("./pattern.cjs");
|
|
43
43
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
44
44
|
const roadmapParserMod = require("./roadmap-parser.cjs");
|
|
@@ -642,8 +642,21 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
642
642
|
// windows converge to the same value regardless of which version drove
|
|
643
643
|
// the lookup.
|
|
644
644
|
const scopedContent = sliceMilestoneWindow(roadmapContent, version) ?? extractCurrentMilestone(roadmapContent, cwd);
|
|
645
|
-
// #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag
|
|
646
|
-
|
|
645
|
+
// #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag, owned
|
|
646
|
+
// by buildPhaseHeadingScanRegex (phase-id.cts) so this guard also
|
|
647
|
+
// recognizes bracket-convention headings instead of hand-rolling a
|
|
648
|
+
// literal `Phase\s+`.
|
|
649
|
+
// #4984 fix: resolved ONCE and threaded into BOTH the heading scan and
|
|
650
|
+
// the disk-side matchPhaseDirs check below — the heading scan alone
|
|
651
|
+
// recognizing `[GSD.02] 01:` and extracting the bare phase-number token
|
|
652
|
+
// "01" is only half the fix. Without also passing this convention to
|
|
653
|
+
// matchPhaseDirs, its bracket-qualified match arm never fires, "01"
|
|
654
|
+
// matches no bracket directory name ("GSD.02-01-setup"), and every
|
|
655
|
+
// properly-scaffolded bracket phase reads as disk_status: 'no_directory'
|
|
656
|
+
// — turning a real bracket project's own phases into false "ROADMAP
|
|
657
|
+
// lists N unstarted phase(s)" failures on `milestone complete`.
|
|
658
|
+
const convention = resolvePhaseIdConvention(cwd);
|
|
659
|
+
const { regex: phasePattern, phaseNumGroup } = buildPhaseHeadingScanRegex(PHASE_HEADING_BASELINE.ANY_BRACKET, convention);
|
|
647
660
|
const noDirectoryPhases = [];
|
|
648
661
|
let pm;
|
|
649
662
|
const phaseDirEntries = (() => {
|
|
@@ -658,7 +671,7 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
658
671
|
}
|
|
659
672
|
})();
|
|
660
673
|
while ((pm = phasePattern.exec(scopedContent)) !== null) {
|
|
661
|
-
const phaseNum = pm[
|
|
674
|
+
const phaseNum = pm[phaseNumGroup];
|
|
662
675
|
// Phase 0 (pre-milestone) and Phase 999 (backlog) are sentinels, not
|
|
663
676
|
// real phases — they legitimately have no directory and must not block
|
|
664
677
|
// milestone completion. Mirrors the engine-wide sentinel convention
|
|
@@ -672,7 +685,7 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
672
685
|
// with a matching token exists on disk. Use the same matchPhaseDirs
|
|
673
686
|
// owner that roadmap.analyze uses to avoid false positives on decimal
|
|
674
687
|
// (2.1) and letter-suffix (12A) phase IDs. (#2528)
|
|
675
|
-
const hasDirectory = matchPhaseDirs(phaseDirEntries, normalized).matches.length > 0;
|
|
688
|
+
const hasDirectory = matchPhaseDirs(phaseDirEntries, normalized, convention).matches.length > 0;
|
|
676
689
|
if (!hasDirectory) {
|
|
677
690
|
noDirectoryPhases.push(phaseNum);
|
|
678
691
|
}
|
|
@@ -826,7 +839,7 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
826
839
|
// never disagree with what a real run actually archives. Absent
|
|
827
840
|
// --archive-quick this stays `[]` and nothing on disk is touched either
|
|
828
841
|
// way (dry-run always returns before any mutation below).
|
|
829
|
-
const quickDirsToArchive = options.archiveQuick ? listQuickTaskDirsForArchive(cwd) : [];
|
|
842
|
+
const quickDirsToArchive = options.archiveQuick ? listQuickTaskDirsForArchive(cwd).map((d) => d.name) : [];
|
|
830
843
|
const dryRunResult = {
|
|
831
844
|
dry_run: true,
|
|
832
845
|
version,
|
|
@@ -1482,6 +1495,16 @@ function writeQuickArchiveReadme(archiveQuickDir) {
|
|
|
1482
1495
|
* written three times, and only the real-run copy applied `requireSafePath`,
|
|
1483
1496
|
* so a dry-run preview could list a directory the real run would silently
|
|
1484
1497
|
* skip).
|
|
1498
|
+
*
|
|
1499
|
+
* Returns the proven `ContainedPath` alongside each entry's bare `name`
|
|
1500
|
+
* (`no-unconfined-path-join`'s `discardedContainmentResult` arm — a bare
|
|
1501
|
+
* statement call to `requireSafePath` throws away the exact answer it just
|
|
1502
|
+
* computed). The two dry-run previews only need `name` for display;
|
|
1503
|
+
* `archiveQuickTaskDirectories` deliberately does NOT reuse `abs` for its
|
|
1504
|
+
* rename — it re-derives and re-validates independently as TOCTOU
|
|
1505
|
+
* defense-in-depth (see its own comment), so `abs` exists here only to make
|
|
1506
|
+
* this function's own discard explicit, not to be trusted downstream as a
|
|
1507
|
+
* stale-safe proof.
|
|
1485
1508
|
*/
|
|
1486
1509
|
function listQuickTaskDirsForArchive(cwd) {
|
|
1487
1510
|
const planningBase = planningPaths(cwd).planning;
|
|
@@ -1494,19 +1517,20 @@ function listQuickTaskDirsForArchive(cwd) {
|
|
|
1494
1517
|
// .planning/quick absent or unreadable — nothing to select.
|
|
1495
1518
|
return [];
|
|
1496
1519
|
}
|
|
1497
|
-
const
|
|
1520
|
+
const results = [];
|
|
1498
1521
|
for (const entry of sourceEntries) {
|
|
1499
1522
|
if (!entry.isDirectory())
|
|
1500
1523
|
continue; // excludes symlinks too — see MAJOR 3 note above
|
|
1524
|
+
let abs;
|
|
1501
1525
|
try {
|
|
1502
|
-
(0, security_cjs_1.requireSafePath)(node_path_1.default.join(quickDir, entry.name), planningBase, 'quick task dir',
|
|
1526
|
+
abs = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(quickDir, entry.name), planningBase, 'quick task dir', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
|
|
1503
1527
|
}
|
|
1504
1528
|
catch {
|
|
1505
1529
|
continue; // symlink/escape attempt — never a candidate, in preview OR real run
|
|
1506
1530
|
}
|
|
1507
|
-
|
|
1531
|
+
results.push({ name: entry.name, abs });
|
|
1508
1532
|
}
|
|
1509
|
-
return
|
|
1533
|
+
return results.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
1510
1534
|
}
|
|
1511
1535
|
/**
|
|
1512
1536
|
* #2142: move each DIRECTORY entry under `.planning/quick/` into
|
|
@@ -1555,7 +1579,7 @@ function archiveQuickTaskDirectories(cwd, version) {
|
|
|
1555
1579
|
// #2142 MAJOR 5 (review): dirNames is the SAME selection
|
|
1556
1580
|
// `listQuickTaskDirsForArchive` hands to both dry-run previews — this is
|
|
1557
1581
|
// the real run, so it cannot disagree with what a preview reported.
|
|
1558
|
-
const dirNames = listQuickTaskDirsForArchive(cwd);
|
|
1582
|
+
const dirNames = listQuickTaskDirsForArchive(cwd).map((d) => d.name);
|
|
1559
1583
|
if (dirNames.length === 0) {
|
|
1560
1584
|
// Boundary 0 (#2142): zero (safe) directory entries (empty dir, only
|
|
1561
1585
|
// stray files, or every entry excluded by the selection rule) must not
|
|
@@ -1576,7 +1600,7 @@ function archiveQuickTaskDirectories(cwd, version) {
|
|
|
1576
1600
|
// rename are two separate filesystem observations, and an entry
|
|
1577
1601
|
// that was a safe real directory at selection time could in theory
|
|
1578
1602
|
// be swapped for a symlink before this loop reaches it.
|
|
1579
|
-
safeSrc = (0, security_cjs_1.requireSafePath)(src, planningBase, 'quick task dir',
|
|
1603
|
+
safeSrc = (0, security_cjs_1.requireSafePath)(src, planningBase, 'quick task dir', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
|
|
1580
1604
|
}
|
|
1581
1605
|
catch {
|
|
1582
1606
|
continue; // symlink/escape attempt — skip, not archived
|
|
@@ -1669,7 +1693,7 @@ function cmdQuickArchive(cwd, version, options, raw) {
|
|
|
1669
1693
|
// `cmdMilestoneComplete`'s own dry-run preview and the real
|
|
1670
1694
|
// `archiveQuickTaskDirectories` both use, so all three can never disagree.
|
|
1671
1695
|
if (options.dryRun) {
|
|
1672
|
-
const quickDirsToArchive = listQuickTaskDirsForArchive(cwd);
|
|
1696
|
+
const quickDirsToArchive = listQuickTaskDirsForArchive(cwd).map((d) => d.name);
|
|
1673
1697
|
output({
|
|
1674
1698
|
dry_run: true,
|
|
1675
1699
|
version,
|