@opengsd/gsd-core 1.14.0 → 1.16.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 +85 -5
- 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-code-fixer.compact.md +7 -6
- package/agents/gsd-code-fixer.md +9 -8
- package/agents/gsd-code-reviewer.compact.md +5 -3
- package/agents/gsd-code-reviewer.md +8 -6
- package/agents/gsd-debug-session-manager.compact.md +17 -2
- package/agents/gsd-debug-session-manager.md +17 -2
- package/agents/gsd-debugger.md +3 -3
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +17 -12
- package/agents/gsd-intel-updater.compact.md +1 -1
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +2 -2
- package/agents/gsd-phase-researcher.md +19 -11
- package/agents/gsd-plan-checker.md +15 -9
- package/agents/gsd-planner.md +15 -11
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +1 -1
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +21 -30
- package/agents/gsd-ui-auditor.md +166 -37
- package/agents/gsd-ui-researcher.compact.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +37 -14
- package/bin/install.js +764 -287
- package/commands/gsd/add-tests.md +6 -1
- package/commands/gsd/ai-integration-phase.md +6 -1
- package/commands/gsd/audit-fix.md +5 -0
- package/commands/gsd/audit-milestone.md +6 -1
- package/commands/gsd/autonomous.md +7 -2
- package/commands/gsd/capture.md +9 -5
- package/commands/gsd/code-review.md +7 -2
- package/commands/gsd/complete-milestone.md +4 -0
- package/commands/gsd/config.md +7 -3
- package/commands/gsd/debug.md +11 -7
- package/commands/gsd/discuss-phase.md +7 -3
- package/commands/gsd/docs-update.md +12 -7
- package/commands/gsd/eval-review.md +6 -1
- package/commands/gsd/execute-phase.md +12 -7
- package/commands/gsd/extract-learnings.md +5 -0
- package/commands/gsd/fast.md +4 -0
- package/commands/gsd/forensics.md +5 -1
- package/commands/gsd/graphify.md +10 -6
- package/commands/gsd/health.md +5 -0
- package/commands/gsd/help.md +7 -2
- package/commands/gsd/import.md +7 -3
- package/commands/gsd/inbox.md +5 -0
- package/commands/gsd/ingest-docs.md +5 -1
- package/commands/gsd/manager.md +6 -1
- package/commands/gsd/map-codebase.md +7 -3
- package/commands/gsd/mempalace-capture.md +12 -4
- package/commands/gsd/mempalace-recall.md +5 -1
- package/commands/gsd/milestone-summary.md +5 -1
- package/commands/gsd/mvp-phase.md +8 -3
- package/commands/gsd/new-milestone.md +6 -1
- package/commands/gsd/new-project.md +5 -0
- package/commands/gsd/next.md +6 -1
- package/commands/gsd/ns-context.md +4 -0
- package/commands/gsd/ns-ideate.md +4 -0
- package/commands/gsd/ns-manage.md +4 -0
- package/commands/gsd/ns-project.md +4 -0
- package/commands/gsd/ns-review.md +4 -0
- package/commands/gsd/ns-workflow.md +4 -0
- package/commands/gsd/onboard.md +6 -1
- package/commands/gsd/pause-work.md +5 -1
- package/commands/gsd/phase.md +8 -4
- package/commands/gsd/plan-phase.md +6 -1
- package/commands/gsd/plan-review-convergence.md +11 -7
- package/commands/gsd/pr-branch.md +4 -0
- package/commands/gsd/profile-user.md +5 -1
- package/commands/gsd/progress.md +7 -2
- package/commands/gsd/quick-batch.md +21 -9
- package/commands/gsd/quick.md +12 -7
- package/commands/gsd/review.md +7 -4
- package/commands/gsd/secure-phase.md +6 -1
- package/commands/gsd/ship.md +5 -0
- package/commands/gsd/sketch.md +7 -2
- package/commands/gsd/spec-phase.md +5 -1
- package/commands/gsd/spike.md +8 -3
- package/commands/gsd/surface.md +5 -1
- package/commands/gsd/thread.md +4 -0
- package/commands/gsd/ui-phase.md +6 -1
- package/commands/gsd/ui-review.md +6 -1
- package/commands/gsd/ultraplan-phase.md +5 -1
- package/commands/gsd/undo.md +5 -1
- package/commands/gsd/update.md +6 -2
- package/commands/gsd/validate-phase.md +6 -1
- package/commands/gsd/verify-work.md +6 -1
- package/commands/gsd/workspace.md +7 -3
- package/gsd-core/bin/gsd-tools.cjs +477 -78
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- package/gsd-core/bin/lib/adr-parser.cjs +3 -1
- package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
- package/gsd-core/bin/lib/audit.cjs +144 -42
- package/gsd-core/bin/lib/broken-windows.cjs +13 -13
- package/gsd-core/bin/lib/capability-activation.cjs +9 -4
- package/gsd-core/bin/lib/capability-registry.cjs +197 -222
- package/gsd-core/bin/lib/capability-validator.cjs +16 -1
- package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
- package/gsd-core/bin/lib/check-command-router.cjs +164 -1625
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
- package/gsd-core/bin/lib/cli-exit.cjs +12 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +32 -33
- package/gsd-core/bin/lib/command-aliases.cjs +7 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
- package/gsd-core/bin/lib/commands.cjs +343 -207
- package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
- package/gsd-core/bin/lib/config-loader.cjs +65 -4
- package/gsd-core/bin/lib/config.cjs +76 -19
- package/gsd-core/bin/lib/core-utils.cjs +6 -1
- package/gsd-core/bin/lib/coverage.cjs +4 -8
- package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
- package/gsd-core/bin/lib/decisions.cjs +30 -14
- package/gsd-core/bin/lib/drift.cjs +177 -42
- package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
- package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
- package/gsd-core/bin/lib/frontmatter.cjs +426 -234
- package/gsd-core/bin/lib/gap-checker.cjs +72 -29
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
- package/gsd-core/bin/lib/gate-args.cjs +53 -0
- package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
- package/gsd-core/bin/lib/gate-config.cjs +46 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
- package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
- package/gsd-core/bin/lib/gate-exit.cjs +27 -0
- package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
- package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
- package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
- package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
- package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
- package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
- package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
- package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
- package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
- package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
- package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
- package/gsd-core/bin/lib/graphify.cjs +10 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
- package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
- package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
- package/gsd-core/bin/lib/init.cjs +364 -132
- package/gsd-core/bin/lib/install-engine.cjs +30 -33
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- package/gsd-core/bin/lib/installer-migrations.cjs +8 -1
- package/gsd-core/bin/lib/io.cjs +122 -3
- package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
- package/gsd-core/bin/lib/milestone.cjs +37 -6
- package/gsd-core/bin/lib/model-resolver.cjs +171 -62
- package/gsd-core/bin/lib/observability/event.cjs +1 -1
- package/gsd-core/bin/lib/observability/logger.cjs +46 -1
- package/gsd-core/bin/lib/pattern.cjs +10 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +20 -5
- package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
- 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 +110 -8
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
- package/gsd-core/bin/lib/phase-locator.cjs +29 -10
- package/gsd-core/bin/lib/phase-status.cjs +360 -0
- package/gsd-core/bin/lib/phase.cjs +489 -88
- package/gsd-core/bin/lib/plan-document.cjs +142 -20
- package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
- package/gsd-core/bin/lib/planning-document.cjs +692 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +60 -9
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +83 -55
- package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
- package/gsd-core/bin/lib/probe-core.cjs +7 -1
- package/gsd-core/bin/lib/profile-output.cjs +6 -3
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
- package/gsd-core/bin/lib/project-root.cjs +41 -2
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
- package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
- package/gsd-core/bin/lib/report-parser.cjs +269 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
- package/gsd-core/bin/lib/roadmap-command-router.cjs +25 -19
- package/gsd-core/bin/lib/roadmap-parser.cjs +242 -15
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +1653 -65
- package/gsd-core/bin/lib/roadmap.cjs +405 -88
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +373 -187
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +5 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
- package/gsd-core/bin/lib/runtime-homes.cjs +14 -7
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +522 -624
- package/gsd-core/bin/lib/runtime-name-policy.cjs +246 -21
- package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +47 -8
- package/gsd-core/bin/lib/smart-entry.cjs +19 -3
- package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
- package/gsd-core/bin/lib/state-contract.cjs +15 -18
- package/gsd-core/bin/lib/state-document.cjs +100 -22
- package/gsd-core/bin/lib/state-transition.cjs +39 -2
- package/gsd-core/bin/lib/state.cjs +256 -105
- package/gsd-core/bin/lib/surface.cjs +19 -2
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -79
- package/gsd-core/bin/lib/uat-predicate.cjs +359 -38
- package/gsd-core/bin/lib/uat.cjs +432 -7
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +167 -40
- package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
- package/gsd-core/bin/lib/vendor/README.md +31 -9
- package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
- package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +1378 -274
- package/gsd-core/bin/lib/verify-command-grounding.cjs +46 -2
- package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
- package/gsd-core/bin/lib/verify.cjs +357 -531
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
- package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
- package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
- package/gsd-core/bin/lib/workstream.cjs +11 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
- package/gsd-core/bin/lib/worktree-safety.cjs +784 -51
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
- package/gsd-core/references/autonomous-smart-discuss.md +2 -1
- package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
- package/gsd-core/references/checkpoints.md +5 -3
- 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-mvp-tdd.md +5 -10
- package/gsd-core/references/execute-phase-between-wave-reset.md +10 -6
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- 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 +7 -1
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/offer-next.md +1 -1
- 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 +12 -3
- package/gsd-core/references/spidr-splitting.md +1 -1
- package/gsd-core/references/tdd.md +37 -8
- package/gsd-core/references/ui-consideration-probe.md +10 -5
- package/gsd-core/references/verifier-phase-gates.md +5 -2
- package/gsd-core/references/verify-command-path-resolvability.md +10 -2
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -3
- package/gsd-core/references/worktree-path-safety.md +321 -0
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/UAT.md +17 -1
- package/gsd-core/templates/config.json +2 -11
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +8 -7
- package/gsd-core/workflows/add-tests.md +4 -3
- package/gsd-core/workflows/add-todo.md +6 -5
- package/gsd-core/workflows/ai-integration-phase.md +13 -4
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +4 -3
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
- package/gsd-core/workflows/autonomous.md +43 -23
- package/gsd-core/workflows/check-todos.md +7 -6
- package/gsd-core/workflows/cleanup.md +2 -2
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
- package/gsd-core/workflows/code-review-fix.md +112 -25
- package/gsd-core/workflows/code-review.md +146 -135
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
- package/gsd-core/workflows/complete-milestone.md +13 -8
- package/gsd-core/workflows/debug.md +32 -7
- package/gsd-core/workflows/diagnose-issues.md +3 -2
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +4 -3
- package/gsd-core/workflows/do.md +2 -2
- package/gsd-core/workflows/docs-update.md +6 -5
- package/gsd-core/workflows/edit-phase.md +4 -3
- package/gsd-core/workflows/eval-review.md +14 -5
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1019 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +37 -3
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +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/regression-gate.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
- package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
- package/gsd-core/workflows/execute-phase.md +96 -178
- package/gsd-core/workflows/execute-plan.md +18 -23
- package/gsd-core/workflows/explore.md +4 -4
- package/gsd-core/workflows/extract-learnings.md +4 -2
- package/gsd-core/workflows/fast.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +3 -2
- package/gsd-core/workflows/help/modes/full.compact.md +3 -3
- package/gsd-core/workflows/help/modes/full.md +5 -5
- package/gsd-core/workflows/help/modes/topic.md +15 -5
- package/gsd-core/workflows/import.md +4 -3
- package/gsd-core/workflows/inbox.md +2 -2
- package/gsd-core/workflows/ingest-docs.md +3 -3
- package/gsd-core/workflows/insert-phase.md +4 -3
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +6 -4
- package/gsd-core/workflows/map-codebase.md +5 -4
- package/gsd-core/workflows/milestone-summary.md +3 -2
- package/gsd-core/workflows/mvp-phase.md +14 -14
- package/gsd-core/workflows/new-milestone.md +11 -11
- 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 +7 -7
- 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 +2 -2
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
- package/gsd-core/workflows/plan-phase.md +50 -24
- 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 +113 -13
- package/gsd-core/workflows/profile-user.md +2 -2
- package/gsd-core/workflows/progress.md +19 -49
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +27 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +15 -10
- package/gsd-core/workflows/quick.md +62 -39
- package/gsd-core/workflows/reapply-patches.md +9 -3
- package/gsd-core/workflows/remove-phase.md +3 -2
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +3 -2
- package/gsd-core/workflows/review.md +33 -17
- package/gsd-core/workflows/scan.md +3 -2
- package/gsd-core/workflows/secure-phase.md +13 -13
- package/gsd-core/workflows/settings-advanced.md +30 -10
- package/gsd-core/workflows/settings-integrations.md +2 -3
- package/gsd-core/workflows/settings.md +4 -4
- package/gsd-core/workflows/ship.md +14 -13
- 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 +2 -2
- package/gsd-core/workflows/transition.md +13 -23
- package/gsd-core/workflows/ui-phase.md +48 -11
- package/gsd-core/workflows/ui-review.md +21 -6
- package/gsd-core/workflows/ultraplan-phase.md +3 -2
- package/gsd-core/workflows/undo.md +339 -20
- package/gsd-core/workflows/update.md +7 -7
- package/gsd-core/workflows/validate-phase.md +12 -13
- package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
- package/gsd-core/workflows/verify-work.md +129 -55
- package/hooks/dist/gsd-agent-isolation-guard.js +32 -0
- package/hooks/dist/gsd-check-update-worker.js +8 -0
- package/hooks/dist/gsd-check-update.js +8 -0
- package/hooks/dist/gsd-context-monitor.js +31 -8
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
- package/hooks/dist/gsd-secret-read-guard.js +161 -4
- package/hooks/dist/gsd-statusline.js +85 -20
- package/hooks/dist/gsd-update-banner.js +8 -0
- package/hooks/dist/gsd-validate-commit.sh +63 -4
- package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
- package/hooks/dist/gsd-workflow-guard.js +5 -4
- package/hooks/dist/gsd-worktree-path-guard.js +6 -2
- package/hooks/dist/lib/cli-exit.js +12 -0
- package/hooks/dist/lib/git-probe.js +17 -1
- package/hooks/dist/lib/isolation-sentinel.js +2 -2
- package/hooks/gsd-agent-isolation-guard.js +32 -0
- package/hooks/gsd-check-update-worker.js +8 -0
- package/hooks/gsd-check-update.js +8 -0
- package/hooks/gsd-context-monitor.js +31 -8
- package/hooks/gsd-cursor-subagent-start.js +8 -0
- package/hooks/gsd-secret-read-guard.js +161 -4
- package/hooks/gsd-statusline.js +85 -20
- package/hooks/gsd-update-banner.js +8 -0
- package/hooks/gsd-validate-commit.sh +63 -4
- package/hooks/gsd-windsurf-pre-write.js +11 -2
- package/hooks/gsd-workflow-guard.js +5 -4
- package/hooks/gsd-worktree-path-guard.js +6 -2
- package/hooks/hooks.json +5 -5
- package/hooks/lib/cli-exit.js +12 -0
- package/hooks/lib/git-probe.js +17 -1
- package/hooks/lib/isolation-sentinel.js +2 -2
- package/package.json +22 -4
- package/scripts/build-hooks.js +15 -6
- package/scripts/changeset/parse.cjs +52 -4
- package/scripts/check-contract-drift.cjs +127 -11
- package/scripts/ci-timeout-report.cjs +770 -4
- package/scripts/command-contract-helpers.cjs +15 -8
- package/scripts/docs-guard-registry.cjs +34 -0
- package/scripts/gen-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +79 -1
- package/scripts/gen-platform-conformance-tier.cjs +187 -1
- package/scripts/gen-plugin-skills.cjs +87 -1
- package/scripts/gen-research-agents.cjs +24 -31
- package/scripts/gen-scripts-cli-exit.cjs +30 -3
- package/scripts/gen-test-timings.cjs +32 -7
- package/scripts/lib/cli-exit.cjs +12 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +34 -2
- package/scripts/lib/ndjson-reporter.cjs +31 -5
- package/scripts/lib/platform-conformance-tier.generated.cjs +45 -5
- package/scripts/lib/registration-ledger-preload.cjs +155 -0
- package/scripts/lib/vendor-bundle.cjs +59 -0
- package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-completion-predicate-drift.cjs +18 -19
- package/scripts/lint-descriptions.cjs +7 -3
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +38 -1
- package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
- package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
- package/scripts/lint-phase-arg-assignment.cjs +257 -0
- package/scripts/lint-phase-enumeration-drift.cjs +12 -8
- package/scripts/lint-phase-id-drift.cjs +319 -5
- package/scripts/lint-planning-document-positive-control.cjs +329 -0
- package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
- package/scripts/lint-response-language-coverage.cjs +3 -0
- package/scripts/lint-retired-runtime-name.cjs +619 -0
- package/scripts/lint-skill-deps.cjs +7 -3
- package/scripts/lint-state-write-path-drift.cjs +93 -0
- package/scripts/lint-test-file-count.allowlist.json +38 -9
- package/scripts/lint-test-file-count.cjs +34 -1
- package/scripts/lint-vendored-deps.cjs +41 -5
- package/scripts/lint-workflow-shellcheck-baseline.json +25 -10
- package/scripts/mutation-matrix.cjs +50 -5
- package/scripts/prompt-injection-scan.sh +4 -0
- package/scripts/release-tarball-smoke.cjs +194 -1
- package/scripts/require-issue-link-policy.cjs +6 -2
- package/scripts/sync-runtime-launcher.cjs +184 -2
- package/scripts/verify-npm-publish.cjs +76 -20
- package/skills/gsd-add-tests/SKILL.md +6 -1
- package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
- package/skills/gsd-audit-fix/SKILL.md +5 -0
- package/skills/gsd-audit-milestone/SKILL.md +6 -1
- package/skills/gsd-autonomous/SKILL.md +7 -2
- package/skills/gsd-capture/SKILL.md +9 -5
- package/skills/gsd-code-review/SKILL.md +7 -2
- package/skills/gsd-complete-milestone/SKILL.md +4 -0
- package/skills/gsd-config/SKILL.md +8 -4
- package/skills/gsd-debug/SKILL.md +11 -7
- package/skills/gsd-discuss-phase/SKILL.md +7 -3
- package/skills/gsd-docs-update/SKILL.md +12 -7
- package/skills/gsd-eval-review/SKILL.md +6 -1
- package/skills/gsd-execute-phase/SKILL.md +12 -7
- package/skills/gsd-extract-learnings/SKILL.md +5 -0
- package/skills/gsd-fast/SKILL.md +4 -0
- package/skills/gsd-forensics/SKILL.md +5 -1
- package/skills/gsd-graphify/SKILL.md +10 -6
- package/skills/gsd-health/SKILL.md +5 -0
- package/skills/gsd-help/SKILL.md +7 -2
- package/skills/gsd-import/SKILL.md +7 -3
- package/skills/gsd-inbox/SKILL.md +5 -0
- package/skills/gsd-ingest-docs/SKILL.md +5 -1
- package/skills/gsd-manager/SKILL.md +6 -1
- package/skills/gsd-map-codebase/SKILL.md +7 -3
- package/skills/gsd-mempalace-capture/SKILL.md +12 -4
- package/skills/gsd-mempalace-recall/SKILL.md +5 -1
- package/skills/gsd-milestone-summary/SKILL.md +5 -1
- package/skills/gsd-mvp-phase/SKILL.md +8 -3
- package/skills/gsd-new-milestone/SKILL.md +6 -1
- package/skills/gsd-new-project/SKILL.md +5 -0
- package/skills/gsd-next/SKILL.md +6 -1
- package/skills/gsd-ns-context/SKILL.md +4 -0
- package/skills/gsd-ns-ideate/SKILL.md +4 -0
- package/skills/gsd-ns-manage/SKILL.md +4 -0
- package/skills/gsd-ns-project/SKILL.md +4 -0
- package/skills/gsd-ns-review/SKILL.md +4 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -0
- package/skills/gsd-onboard/SKILL.md +6 -1
- package/skills/gsd-pause-work/SKILL.md +5 -1
- package/skills/gsd-phase/SKILL.md +8 -4
- package/skills/gsd-plan-phase/SKILL.md +6 -1
- package/skills/gsd-plan-review-convergence/SKILL.md +10 -6
- package/skills/gsd-pr-branch/SKILL.md +4 -0
- package/skills/gsd-profile-user/SKILL.md +5 -1
- package/skills/gsd-progress/SKILL.md +7 -2
- package/skills/gsd-quick/SKILL.md +16 -10
- package/skills/gsd-quick-batch/SKILL.md +21 -9
- package/skills/gsd-review/SKILL.md +7 -4
- package/skills/gsd-review-backlog/SKILL.md +3 -2
- package/skills/gsd-secure-phase/SKILL.md +6 -1
- package/skills/gsd-ship/SKILL.md +5 -0
- package/skills/gsd-sketch/SKILL.md +7 -2
- package/skills/gsd-spec-phase/SKILL.md +5 -1
- package/skills/gsd-spike/SKILL.md +8 -3
- package/skills/gsd-surface/SKILL.md +5 -1
- package/skills/gsd-thread/SKILL.md +4 -0
- package/skills/gsd-ui-phase/SKILL.md +6 -1
- package/skills/gsd-ui-review/SKILL.md +6 -1
- package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
- package/skills/gsd-undo/SKILL.md +5 -1
- package/skills/gsd-update/SKILL.md +6 -2
- package/skills/gsd-validate-phase/SKILL.md +6 -1
- package/skills/gsd-verify-work/SKILL.md +6 -1
- package/skills/gsd-workspace/SKILL.md +7 -3
- package/skills/gsd-workstreams/SKILL.md +6 -6
- package/vscode/package.json +1 -1
|
@@ -61,7 +61,13 @@
|
|
|
61
61
|
// segment's operands — `[ -f .env ]` and `ls .env*` are existence checks
|
|
62
62
|
// GSD's own agents run — while `cp`/`mv`/`ln`/`git` are deliberately NOT
|
|
63
63
|
// exempt (`cp .env x && cat x` launders the name; `git show HEAD:.env`
|
|
64
|
-
// reads).
|
|
64
|
+
// reads). Narrower carve-outs exempt single operand POSITIONS that are
|
|
65
|
+
// names, never contents, and keep checking the rest of the segment: the
|
|
66
|
+
// `--env-file` value under a container runtime (#4639), the pathspecs of
|
|
67
|
+
// `git check-ignore` / `git ls-files` / `git rm --cached`, and the
|
|
68
|
+
// destination of a one-source `cp`/`mv` (#4856). The #4856 carve-outs
|
|
69
|
+
// fail closed: an option they do not list withdraws them from the segment.
|
|
70
|
+
// A shell interpreter (bash/sh/zsh/dash/ksh/su) has its script scanned
|
|
65
71
|
// whether it arrives via `-c '…'`, a `<( )` file operand, a heredoc /
|
|
66
72
|
// here-string, or a pipe from a knowable `echo`/`printf` source
|
|
67
73
|
// (`echo cat .env | bash`); `eval` scans its joined operands; `source`/`.`
|
|
@@ -86,7 +92,16 @@
|
|
|
86
92
|
// on plain commands, without arming the compound-`cd` prompt". Writes to
|
|
87
93
|
// secret files are out of scope (Write/Edit were never gated). Commands
|
|
88
94
|
// over 1 MiB are denied outright (`command-too-large`) rather than
|
|
89
|
-
// scanned partially or waved through.
|
|
95
|
+
// scanned partially or waved through. (#4639 adds one more, by design: the
|
|
96
|
+
// value of `--env-file` under a container runtime is exempt, so the
|
|
97
|
+
// container's own command can print the interpolated environment
|
|
98
|
+
// (`alpine printenv`, `docker compose config`) — the same exposure class
|
|
99
|
+
// as the pre-existing volume-mount gap (`-v .env:/s`); the flag's value
|
|
100
|
+
// itself is a name, never contents. #4856 adds another: the `cp`/`mv`
|
|
101
|
+
// destination exemption can move a link prepared under a non-secret name
|
|
102
|
+
// onto a secret name (`ln -s .env.example l && mv l .env`), so a later
|
|
103
|
+
// edit of `.env` lands in the link target — a write redirection, the
|
|
104
|
+
// out-of-scope write class, not a read.)
|
|
90
105
|
//
|
|
91
106
|
// Triggers on: Read, Grep, Bash tool calls (Kimi: ReadFile, Grep, Shell)
|
|
92
107
|
// Action: BLOCK (decision: 'block', exit 2) — codes secret-read |
|
|
@@ -127,6 +142,66 @@ const NON_READING_COMMANDS = new Set([
|
|
|
127
142
|
'basename', 'dirname', 'realpath', 'file', 'echo', 'printf',
|
|
128
143
|
]);
|
|
129
144
|
|
|
145
|
+
// #4639: container runtimes take `--env-file <file>` — the runtime opens the
|
|
146
|
+
// file itself, interpolates it into the container environment, and returns
|
|
147
|
+
// nothing to the agent, so the flag's VALUE is a name, never contents. The
|
|
148
|
+
// same category NON_READING_COMMANDS encodes, expressed as a flag value. Only
|
|
149
|
+
// the flag's value under these runtimes is exempt; every other operand in the
|
|
150
|
+
// segment is still checked, so the carve-out cannot launder a read.
|
|
151
|
+
const CONTAINER_RUNTIMES = new Set(['docker', 'docker-compose', 'podman', 'nerdctl']);
|
|
152
|
+
const ENV_FILE_FLAG_RE = /^--env-file(=|$)/;
|
|
153
|
+
|
|
154
|
+
// #4856: git subcommands that report or drop a path's ignore / tracking
|
|
155
|
+
// status and never print the file, so their pathspec operands are names —
|
|
156
|
+
// the category NON_READING_COMMANDS encodes. Each carries the closed set of
|
|
157
|
+
// its NO-VALUE options (from `git <subcommand> -h`, git 2.49). Any other
|
|
158
|
+
// option withdraws the exemption from the whole segment, so an option value
|
|
159
|
+
// is never mistaken for a pathspec: `-X <file>` reads the file, and
|
|
160
|
+
// `--pathspec-from-file=<file>` echoes its lines in the "did not match"
|
|
161
|
+
// error. `requires` names the option without which the subcommand stays
|
|
162
|
+
// checked (#4856 scopes `git rm` to `--cached`).
|
|
163
|
+
const GIT_PATHSPEC_SUBCOMMANDS = new Map([
|
|
164
|
+
['check-ignore', {
|
|
165
|
+
flags: new Set(['-q', '--quiet', '-v', '--verbose', '--stdin', '-z', '-n', '--non-matching', '--no-index', '--index']),
|
|
166
|
+
}],
|
|
167
|
+
['ls-files', {
|
|
168
|
+
flags: new Set([
|
|
169
|
+
'-z', '-t', '-v', '-f', '-c', '--cached', '-d', '--deleted', '-m', '--modified', '-o', '--others',
|
|
170
|
+
'-i', '--ignored', '-s', '--stage', '-k', '--killed', '-u', '--unmerged', '--directory', '--eol',
|
|
171
|
+
'--no-empty-directory', '--resolve-undo', '--exclude-standard', '--full-name',
|
|
172
|
+
'--recurse-submodules', '--error-unmatch', '--abbrev', '--debug', '--deduplicate', '--sparse',
|
|
173
|
+
]),
|
|
174
|
+
}],
|
|
175
|
+
['rm', {
|
|
176
|
+
flags: new Set(['--cached', '-f', '--force', '-n', '--dry-run', '-q', '--quiet', '-r', '--ignore-unmatch', '--sparse']),
|
|
177
|
+
requires: '--cached',
|
|
178
|
+
}],
|
|
179
|
+
]);
|
|
180
|
+
|
|
181
|
+
// git's global options (its own usage line, git 2.49), needed to locate the
|
|
182
|
+
// subcommand. A value option consumes the next word unless written
|
|
183
|
+
// `--opt=value`; an option in neither set fails closed, so a value is never
|
|
184
|
+
// read as the subcommand (`git -C ls-files show HEAD:.env` runs `show`).
|
|
185
|
+
const GIT_GLOBAL_VALUE_OPTIONS = new Set(['-C', '-c', '--git-dir', '--work-tree', '--namespace', '--config-env']);
|
|
186
|
+
const GIT_GLOBAL_FLAGS = new Set([
|
|
187
|
+
'-p', '--paginate', '-P', '--no-pager', '--no-replace-objects', '--no-lazy-fetch',
|
|
188
|
+
'--no-optional-locks', '--no-advice', '--bare',
|
|
189
|
+
]);
|
|
190
|
+
|
|
191
|
+
// #4856: `cp`/`mv` write their destination and never print it, so a secret
|
|
192
|
+
// name that is ONLY the destination of a one-source copy or move is a name
|
|
193
|
+
// (`cp .env.example .env`). The exemption holds for exactly two operands
|
|
194
|
+
// behind options from this closed set; anything else keeps the whole segment
|
|
195
|
+
// checked — `-t`/`--target-directory` (every operand becomes a source), a
|
|
196
|
+
// backup (`-b`, `--backup`, `-S`: the old secret survives as `.env~`, a name
|
|
197
|
+
// the predicate does not classify), a link (`-l`, `-s`) or `--exchange`
|
|
198
|
+
// (the destination shares or swaps the secret). Options must precede the
|
|
199
|
+
// operands: a trailing one is an option under GNU getopt's permutation but
|
|
200
|
+
// an operand under POSIXLY_CORRECT (`cp a .env -f` then copies `.env` INTO
|
|
201
|
+
// `-f/`), so which word is the destination would hang on an environment
|
|
202
|
+
// the hook cannot see.
|
|
203
|
+
const COPY_NAME_ONLY_FLAGS = new Set(['-f', '--force', '-i', '--interactive', '-n', '--no-clobber', '-v', '--verbose']);
|
|
204
|
+
|
|
130
205
|
// Shell interpreters that run a script from `-c`, a file operand, or stdin
|
|
131
206
|
// (heredoc / here-string / piped `echo`|`printf`). `su` is here for its `-c`
|
|
132
207
|
// form (`su [user] -c 'cmd'`); a bare `su user` resolves to file mode, which
|
|
@@ -700,6 +775,86 @@ function resolveCommand(words) {
|
|
|
700
775
|
return { base: lastSegment(words[idx].text).toLowerCase(), operands: words.slice(idx + 1) };
|
|
701
776
|
}
|
|
702
777
|
|
|
778
|
+
// Operand indices the segment's command consumes as a NAME, never as
|
|
779
|
+
// CONTENTS (#4639, #4856). Only these positions skip the operand check;
|
|
780
|
+
// every other operand is still checked, so a carve-out cannot launder a read
|
|
781
|
+
// elsewhere in the segment.
|
|
782
|
+
function nameOnlyOperandIndices(base, operands) {
|
|
783
|
+
if (CONTAINER_RUNTIMES.has(base)) return envFileValueIndices(operands);
|
|
784
|
+
if (base === 'git') return gitPathspecIndices(operands);
|
|
785
|
+
if (base === 'cp' || base === 'mv') return copyDestinationIndices(operands);
|
|
786
|
+
return new Set();
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// #4639: the `--env-file` word, plus the next operand for the bare flag
|
|
790
|
+
// (`--env-file=<value>` is a single word).
|
|
791
|
+
function envFileValueIndices(operands) {
|
|
792
|
+
const exempt = new Set();
|
|
793
|
+
for (let k = 0; k < operands.length; k++) {
|
|
794
|
+
if (!ENV_FILE_FLAG_RE.test(operands[k].text)) continue;
|
|
795
|
+
exempt.add(k);
|
|
796
|
+
if (!operands[k].text.includes('=')) exempt.add(++k);
|
|
797
|
+
}
|
|
798
|
+
return exempt;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// True when `text` is a listed long option, or a short cluster (`-co`) whose
|
|
802
|
+
// every letter is listed. `--opt=value`, an abbreviation, or a cluster
|
|
803
|
+
// holding a value option (`-ciX`) is not.
|
|
804
|
+
function isListedFlag(text, flags) {
|
|
805
|
+
if (text.startsWith('--')) return flags.has(text);
|
|
806
|
+
return /^-[A-Za-z]+$/.test(text) && [...text.slice(1)].every((ch) => flags.has(`-${ch}`));
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// Index of git's subcommand past its global options, or -1 when an option
|
|
810
|
+
// outside GIT_GLOBAL_VALUE_OPTIONS / GIT_GLOBAL_FLAGS precedes it.
|
|
811
|
+
function gitSubcommandIndex(operands) {
|
|
812
|
+
for (let k = 0; k < operands.length; k++) {
|
|
813
|
+
const t = operands[k].text;
|
|
814
|
+
if (!t.startsWith('-')) return k;
|
|
815
|
+
if (GIT_GLOBAL_VALUE_OPTIONS.has(t)) { k++; continue; }
|
|
816
|
+
if (GIT_GLOBAL_FLAGS.has(t)) continue;
|
|
817
|
+
const eq = t.indexOf('=');
|
|
818
|
+
const isLongValueForm = t.startsWith('--') && eq !== -1 && GIT_GLOBAL_VALUE_OPTIONS.has(t.slice(0, eq));
|
|
819
|
+
if (!isLongValueForm) return -1;
|
|
820
|
+
}
|
|
821
|
+
return -1;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// #4856: the pathspec operands of a GIT_PATHSPEC_SUBCOMMANDS invocation. git
|
|
825
|
+
// permutes options, so one may follow a pathspec; after `--` every word is a
|
|
826
|
+
// pathspec. The subcommand is matched case-sensitively, as git looks it up.
|
|
827
|
+
function gitPathspecIndices(operands) {
|
|
828
|
+
const sub = gitSubcommandIndex(operands);
|
|
829
|
+
const spec = sub === -1 ? undefined : GIT_PATHSPEC_SUBCOMMANDS.get(operands[sub].text);
|
|
830
|
+
if (!spec) return new Set();
|
|
831
|
+
const pathspecs = new Set();
|
|
832
|
+
let required = spec.requires === undefined;
|
|
833
|
+
let endOfOptions = false;
|
|
834
|
+
for (let k = sub + 1; k < operands.length; k++) {
|
|
835
|
+
const t = operands[k].text;
|
|
836
|
+
if (endOfOptions || !t.startsWith('-') || t === '-') pathspecs.add(k);
|
|
837
|
+
else if (t === '--') endOfOptions = true;
|
|
838
|
+
else if (!isListedFlag(t, spec.flags)) return new Set();
|
|
839
|
+
else if (t === spec.requires) required = true;
|
|
840
|
+
}
|
|
841
|
+
return required ? pathspecs : new Set();
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// #4856: the destination of a one-source `cp`/`mv` (see COPY_NAME_ONLY_FLAGS).
|
|
845
|
+
function copyDestinationIndices(operands) {
|
|
846
|
+
const positional = [];
|
|
847
|
+
let endOfOptions = false;
|
|
848
|
+
for (let k = 0; k < operands.length; k++) {
|
|
849
|
+
const t = operands[k].text;
|
|
850
|
+
if (endOfOptions || !t.startsWith('-') || t === '-') positional.push(k);
|
|
851
|
+
else if (positional.length) return new Set(); // an option or `--` after an operand
|
|
852
|
+
else if (t === '--') endOfOptions = true;
|
|
853
|
+
else if (!isListedFlag(t, COPY_NAME_ONLY_FLAGS)) return new Set();
|
|
854
|
+
}
|
|
855
|
+
return positional.length === 2 ? new Set([positional[1]]) : new Set();
|
|
856
|
+
}
|
|
857
|
+
|
|
703
858
|
// The statically-knowable stdin a segment writes: `echo`/`printf` operands
|
|
704
859
|
// joined by a space (for `echo`, leading `-neE` flags dropped). Any other
|
|
705
860
|
// source (`cat gen.sh | bash`, `curl … | sh`) is not knowable → null.
|
|
@@ -840,8 +995,10 @@ function findSecretRead(command, depth) {
|
|
|
840
995
|
|
|
841
996
|
if (NON_READING_COMMANDS.has(base)) continue;
|
|
842
997
|
|
|
843
|
-
|
|
844
|
-
|
|
998
|
+
const nameOnly = nameOnlyOperandIndices(base, operands);
|
|
999
|
+
for (let k = 0; k < operands.length; k++) {
|
|
1000
|
+
if (nameOnly.has(k)) continue;
|
|
1001
|
+
if (namesSecret(normalizeOperand(operands[k].text))) return operands[k].text;
|
|
845
1002
|
}
|
|
846
1003
|
}
|
|
847
1004
|
return null;
|
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
// Claude Code Statusline - GSD Edition
|
|
4
4
|
// Shows: model | current task (or GSD state) | directory | context usage
|
|
5
5
|
|
|
6
|
+
// #5183: Node's on-disk compile cache, so later runs reuse V8's compiled code
|
|
7
|
+
// for unchanged modules. Only as the entry (a test require() is unaffected),
|
|
8
|
+
// skipped under V8 coverage, optional (Bun stubs it), and never fatal.
|
|
9
|
+
// NODE_DISABLE_COMPILE_CACHE (any value) turns it off.
|
|
10
|
+
if (require.main === module && !process.env.NODE_V8_COVERAGE) {
|
|
11
|
+
try { require('node:module').enableCompileCache?.(); } catch { /* optimization only */ }
|
|
12
|
+
}
|
|
13
|
+
|
|
6
14
|
const fs = require('fs');
|
|
7
15
|
const path = require('path');
|
|
8
16
|
const os = require('os');
|
|
@@ -42,6 +50,11 @@ if (require.main === module) {
|
|
|
42
50
|
const { isSemverNewer } = require('../gsd-core/bin/lib/semver-compare.cjs');
|
|
43
51
|
const { PACKAGE_NAME, updateCacheFileName } = require('../gsd-core/bin/lib/package-identity.cjs');
|
|
44
52
|
const { normalizeStateStatus } = require('../gsd-core/bin/lib/state-document.cjs');
|
|
53
|
+
const { locateFrontmatterFence } = require('../gsd-core/bin/lib/frontmatter-fence.cjs');
|
|
54
|
+
const {
|
|
55
|
+
renderBracketPhaseDisplay,
|
|
56
|
+
renderBracketMilestoneDisplay,
|
|
57
|
+
} = require('../gsd-core/bin/lib/phase-id-display.cjs');
|
|
45
58
|
// #2850: reuse the existing workstream resolution seams rather than
|
|
46
59
|
// re-implementing CLI>env>store precedence or path construction inline.
|
|
47
60
|
// peekActiveWorkstream is the read-only sibling of the store-tier lookup
|
|
@@ -246,13 +259,12 @@ function readGsdState(dir, opts = {}) {
|
|
|
246
259
|
function parseStateMd(content) {
|
|
247
260
|
const state = {};
|
|
248
261
|
|
|
249
|
-
// YAML frontmatter
|
|
250
|
-
// #2754
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const fm = fmMatch[1];
|
|
262
|
+
// YAML frontmatter: the block the one fence owner finds (`locateFrontmatterFence`), so a
|
|
263
|
+
// CRLF STATE.md (#2754), a BOM, and a `---` inside a value read exactly as every other
|
|
264
|
+
// STATE.md reader reads them (found while implementing #5105).
|
|
265
|
+
const fence = locateFrontmatterFence(content);
|
|
266
|
+
if (fence && fence.closed) {
|
|
267
|
+
const fm = content.slice(fence.openEnd, fence.bodyEnd);
|
|
256
268
|
// Top-level scalar key: value
|
|
257
269
|
for (const line of fm.split(/\r?\n/)) {
|
|
258
270
|
const m = line.match(/^(\w+):\s*(.+)/);
|
|
@@ -359,16 +371,26 @@ function renderProgressBar(percent) {
|
|
|
359
371
|
* Progress bar is opt-in: appended to the milestone segment only when
|
|
360
372
|
* progress.percent is present in frontmatter; absent → empty string.
|
|
361
373
|
*/
|
|
362
|
-
function formatGsdState(s) {
|
|
374
|
+
function formatGsdState(s, opts = {}) {
|
|
363
375
|
// #2850: workstream mode with nothing resolvable — an observable signal,
|
|
364
376
|
// never silent emptiness (distinguishes from "GSD isn't installed here").
|
|
365
377
|
if (s.noActiveWorkstream) return NO_ACTIVE_WORKSTREAM_LABEL;
|
|
366
378
|
|
|
367
379
|
const parts = [];
|
|
380
|
+
const bracket = opts.convention === 'bracket';
|
|
381
|
+
const bracketMilestone = bracket
|
|
382
|
+
? renderBracketMilestoneDisplay(s.milestone, opts.projectCode)
|
|
383
|
+
: null;
|
|
384
|
+
const phaseLabel = (phase) => {
|
|
385
|
+
const display = bracket
|
|
386
|
+
? renderBracketPhaseDisplay(s.milestone, phase, opts.projectCode)
|
|
387
|
+
: null;
|
|
388
|
+
return display ?? `Phase ${phase}`;
|
|
389
|
+
};
|
|
368
390
|
|
|
369
391
|
// Milestone segment: version + name + (opt-in) progress bar
|
|
370
392
|
if (s.milestone || s.milestoneName) {
|
|
371
|
-
const ver = s.milestone
|
|
393
|
+
const ver = bracketMilestone ?? s.milestone ?? '';
|
|
372
394
|
const name = (s.milestoneName && s.milestoneName !== 'milestone') ? s.milestoneName : '';
|
|
373
395
|
const bar = renderProgressBar(s.percent);
|
|
374
396
|
const pieces = [ver, name, bar].filter(Boolean);
|
|
@@ -384,7 +406,8 @@ function formatGsdState(s) {
|
|
|
384
406
|
// stage = whichever lifecycle status was written by the orchestrator
|
|
385
407
|
// (discussing / planning / executing / verifying)
|
|
386
408
|
const stage = s.status || '';
|
|
387
|
-
|
|
409
|
+
const phase = phaseLabel(s.activePhase);
|
|
410
|
+
parts.push(stage ? `${phase} ${stage}` : phase);
|
|
388
411
|
} else if (s.nextAction && phasesStr) {
|
|
389
412
|
// Scene 2: idle + a recommended next command is visible to the user.
|
|
390
413
|
// Surfaces "what to run next" without the user opening STATE.md.
|
|
@@ -402,9 +425,16 @@ function formatGsdState(s) {
|
|
|
402
425
|
// earlier so no existing project's status-line changes shape.
|
|
403
426
|
if (s.status) parts.push(s.status);
|
|
404
427
|
if (s.phaseNum && s.phaseTotal) {
|
|
428
|
+
const bracketPhase = bracket
|
|
429
|
+
? renderBracketPhaseDisplay(s.milestone, s.phaseNum, opts.projectCode)
|
|
430
|
+
: null;
|
|
405
431
|
const phase = s.phaseName
|
|
406
|
-
?
|
|
407
|
-
|
|
432
|
+
? bracketPhase
|
|
433
|
+
? `${bracketPhase} ${s.phaseName} (${s.phaseNum}/${s.phaseTotal})`
|
|
434
|
+
: `${s.phaseName} (${s.phaseNum}/${s.phaseTotal})`
|
|
435
|
+
: bracketPhase
|
|
436
|
+
? `${bracketPhase} (${s.phaseNum}/${s.phaseTotal})`
|
|
437
|
+
: `ph ${s.phaseNum}/${s.phaseTotal}`;
|
|
408
438
|
parts.push(phase);
|
|
409
439
|
}
|
|
410
440
|
}
|
|
@@ -490,17 +520,24 @@ function shortGsdStatus(status) {
|
|
|
490
520
|
* default format — and collapses narrative statuses via shortGsdStatus().
|
|
491
521
|
* The default "full" format is untouched.
|
|
492
522
|
*/
|
|
493
|
-
function formatGsdStateCompact(s) {
|
|
523
|
+
function formatGsdStateCompact(s, opts = {}) {
|
|
494
524
|
// #2850: mirrors formatGsdState's observable "nothing resolvable" signal.
|
|
495
525
|
if (s.noActiveWorkstream) return NO_ACTIVE_WORKSTREAM_LABEL;
|
|
496
526
|
|
|
497
527
|
const parts = [];
|
|
528
|
+
const bracket = opts.convention === 'bracket';
|
|
529
|
+
const bracketMilestone = bracket
|
|
530
|
+
? renderBracketMilestoneDisplay(s.milestone, opts.projectCode)
|
|
531
|
+
: null;
|
|
498
532
|
|
|
499
|
-
if (s.milestone) parts.push(s.milestone);
|
|
533
|
+
if (s.milestone) parts.push(bracketMilestone ?? s.milestone);
|
|
500
534
|
|
|
501
535
|
const phaseId = s.activePhase || s.phaseNum;
|
|
502
536
|
if (phaseId) {
|
|
503
|
-
|
|
537
|
+
const bracketPhase = bracket
|
|
538
|
+
? renderBracketPhaseDisplay(s.milestone, phaseId, opts.projectCode)
|
|
539
|
+
: null;
|
|
540
|
+
parts.push(bracketPhase ?? (s.phaseTotal ? `P${phaseId}/${s.phaseTotal}` : `P${phaseId}`));
|
|
504
541
|
}
|
|
505
542
|
|
|
506
543
|
// Scene exclusivity mirrors formatGsdState's if/else chain: an in-flight
|
|
@@ -747,7 +784,7 @@ function formatStateFreshness(fresh) {
|
|
|
747
784
|
* entry points — this collapses both onto one resolver.
|
|
748
785
|
*
|
|
749
786
|
* @param {object} cfg — parsed .planning/config.json (readGsdConfig())
|
|
750
|
-
* @returns {{ showLastCommand: boolean, position: 'end'|'front', stateFormat: 'full'|'compact', showGit: boolean, showStateFreshness: boolean }}
|
|
787
|
+
* @returns {{ showLastCommand: boolean, position: 'end'|'front', stateFormat: 'full'|'compact', showGit: boolean, showStateFreshness: boolean, convention: string|null, projectCode: string|null }}
|
|
751
788
|
*/
|
|
752
789
|
function resolveStatuslineOptions(cfg) {
|
|
753
790
|
const showLastCommand = getConfigValue(cfg, 'statusline.show_last_command') === true;
|
|
@@ -759,7 +796,22 @@ function resolveStatuslineOptions(cfg) {
|
|
|
759
796
|
const stateFormat = getConfigValue(cfg, 'statusline.state_format') === 'compact' ? 'compact' : 'full';
|
|
760
797
|
const showGit = getConfigValue(cfg, 'statusline.show_git') === true;
|
|
761
798
|
const showStateFreshness = getConfigValue(cfg, 'statusline.show_state_freshness') === true;
|
|
762
|
-
|
|
799
|
+
const convention = getConfigValue(cfg, 'phase_id_convention') === 'bracket'
|
|
800
|
+
? 'bracket'
|
|
801
|
+
: null;
|
|
802
|
+
const projectCodeValue = getConfigValue(cfg, 'project_code');
|
|
803
|
+
const projectCode = typeof projectCodeValue === 'string' && projectCodeValue !== ''
|
|
804
|
+
? projectCodeValue
|
|
805
|
+
: null;
|
|
806
|
+
return {
|
|
807
|
+
showLastCommand,
|
|
808
|
+
position,
|
|
809
|
+
stateFormat,
|
|
810
|
+
showGit,
|
|
811
|
+
showStateFreshness,
|
|
812
|
+
convention,
|
|
813
|
+
projectCode,
|
|
814
|
+
};
|
|
763
815
|
}
|
|
764
816
|
|
|
765
817
|
// --- stdin ------------------------------------------------------------------
|
|
@@ -934,7 +986,9 @@ function runStatusline() {
|
|
|
934
986
|
// freshness git spawn here would spend a subprocess on discarded output.
|
|
935
987
|
if (!task) {
|
|
936
988
|
const state = readGsdState(dir, { stateFreshness: options.showStateFreshness }) || {};
|
|
937
|
-
gsdStateStr = options.stateFormat === 'compact'
|
|
989
|
+
gsdStateStr = options.stateFormat === 'compact'
|
|
990
|
+
? formatGsdStateCompact(state, options)
|
|
991
|
+
: formatGsdState(state, options);
|
|
938
992
|
}
|
|
939
993
|
|
|
940
994
|
// Output
|
|
@@ -1042,6 +1096,7 @@ module.exports = {
|
|
|
1042
1096
|
STATE_HEAD_ADVISORY_COMMITS, isValidStateHeadStamp,
|
|
1043
1097
|
readStateHeadCommits, parseRevListCounts, deriveStateFreshness,
|
|
1044
1098
|
formatStateFreshness, resolveStatuslineOptions,
|
|
1099
|
+
renderBracketPhaseDisplay, renderBracketMilestoneDisplay,
|
|
1045
1100
|
};
|
|
1046
1101
|
|
|
1047
1102
|
/**
|
|
@@ -1059,7 +1114,15 @@ function renderStatusline(data) {
|
|
|
1059
1114
|
// key from reaching only one of them.
|
|
1060
1115
|
let lastCmdSuffix = '';
|
|
1061
1116
|
let gitSuffix = '';
|
|
1062
|
-
let options = {
|
|
1117
|
+
let options = {
|
|
1118
|
+
showLastCommand: false,
|
|
1119
|
+
position: 'end',
|
|
1120
|
+
stateFormat: 'full',
|
|
1121
|
+
showGit: false,
|
|
1122
|
+
showStateFreshness: false,
|
|
1123
|
+
convention: null,
|
|
1124
|
+
projectCode: null,
|
|
1125
|
+
};
|
|
1063
1126
|
try {
|
|
1064
1127
|
const cfg = readGsdConfig(dir);
|
|
1065
1128
|
options = resolveStatuslineOptions(cfg);
|
|
@@ -1075,7 +1138,9 @@ function renderStatusline(data) {
|
|
|
1075
1138
|
} catch (e) { /* swallow */ }
|
|
1076
1139
|
|
|
1077
1140
|
const state = readGsdState(dir, { stateFreshness: options.showStateFreshness }) || {};
|
|
1078
|
-
const gsdStateStr = options.stateFormat === 'compact'
|
|
1141
|
+
const gsdStateStr = options.stateFormat === 'compact'
|
|
1142
|
+
? formatGsdStateCompact(state, options)
|
|
1143
|
+
: formatGsdState(state, options);
|
|
1079
1144
|
const middle = gsdStateStr ? `\x1b[2m${gsdStateStr}\x1b[0m` : null;
|
|
1080
1145
|
return composeStatusline({ model, ctx: '', middle, dirname, lastCmdSuffix, gitSuffix, position: options.position });
|
|
1081
1146
|
}
|
|
@@ -12,6 +12,14 @@
|
|
|
12
12
|
|
|
13
13
|
'use strict';
|
|
14
14
|
|
|
15
|
+
// #5183: Node's on-disk compile cache, so later runs reuse V8's compiled code
|
|
16
|
+
// for unchanged modules. Only as the entry (a test require() is unaffected),
|
|
17
|
+
// skipped under V8 coverage, optional (Bun stubs it), and never fatal.
|
|
18
|
+
// NODE_DISABLE_COMPILE_CACHE (any value) turns it off.
|
|
19
|
+
if (require.main === module && !process.env.NODE_V8_COVERAGE) {
|
|
20
|
+
try { require('node:module').enableCompileCache?.(); } catch { /* optimization only */ }
|
|
21
|
+
}
|
|
22
|
+
|
|
15
23
|
const fs = require('fs');
|
|
16
24
|
const path = require('path');
|
|
17
25
|
const os = require('os');
|
|
@@ -16,6 +16,21 @@ set -euo pipefail
|
|
|
16
16
|
# Idempotent and failure-proof by construction: unset vars expand to "" (a
|
|
17
17
|
# no-op rm -f target), and `|| true` guarantees the trap itself never changes
|
|
18
18
|
# the script's exit status.
|
|
19
|
+
# Subprocess exit statuses, pre-initialised so they can never be inherited from
|
|
20
|
+
# the ambient environment. Each is captured as `... || VAR=$?`, which assigns
|
|
21
|
+
# ONLY on the failure branch; on success the variable keeps whatever it already
|
|
22
|
+
# held, and `${VAR:-0}` defaults only when unset or empty. So an EXPORTED
|
|
23
|
+
# CONFIG_STATUS / CMD_STATUS / CLASSIFY_STATUS — from a CI wrapper, a .envrc, or
|
|
24
|
+
# another hook — survived into the success path and was read as "the subprocess
|
|
25
|
+
# failed". Measured: `CLASSIFY_STATUS=3 git commit -m "nope: bad"` printed
|
|
26
|
+
# "validator disabled for this call" and exited 0, silently accepting a
|
|
27
|
+
# non-conforming commit. Same for CONFIG_STATUS and CMD_STATUS. Found by the
|
|
28
|
+
# security review of #4429; the gate is fail-open by design on a genuine
|
|
29
|
+
# subprocess failure (#3838), which is exactly what made this bypass quiet.
|
|
30
|
+
CONFIG_STATUS=0
|
|
31
|
+
CMD_STATUS=0
|
|
32
|
+
CLASSIFY_STATUS=0
|
|
33
|
+
|
|
19
34
|
ENABLED_ERR=""
|
|
20
35
|
CMD_ERR=""
|
|
21
36
|
CLASSIFY_ERR=""
|
|
@@ -61,6 +76,12 @@ if [ -f .planning/config.json ]; then
|
|
|
61
76
|
process.exit(3);
|
|
62
77
|
}
|
|
63
78
|
" 2>"$ENABLED_ERR") || CONFIG_STATUS=$?
|
|
79
|
+
# Pre-initialised, NOT left to `${...:-0}` alone: the capture below only
|
|
80
|
+
# assigns on the `||` branch, so on SUCCESS the variable keeps whatever it
|
|
81
|
+
# already held — and an EXPORTED variable of this name is inherited from the
|
|
82
|
+
# ambient environment. `${VAR:-0}` defaults only when unset/empty, so
|
|
83
|
+
# `CONFIG_STATUS=3 git commit …` made this hook print "validator disabled" and exit 0,
|
|
84
|
+
# silently accepting a non-conforming commit. Found by review of #4429.
|
|
64
85
|
CONFIG_STATUS=${CONFIG_STATUS:-0}
|
|
65
86
|
if [ "$CONFIG_STATUS" != "0" ]; then
|
|
66
87
|
# Could not determine the opt-in flag at all (node missing, JSON parse
|
|
@@ -103,6 +124,12 @@ CMD=$(echo "$INPUT" | node -e "
|
|
|
103
124
|
}
|
|
104
125
|
});
|
|
105
126
|
" 2>"$CMD_ERR") || CMD_STATUS=$?
|
|
127
|
+
# Pre-initialised, NOT left to `${...:-0}` alone: the capture below only
|
|
128
|
+
# assigns on the `||` branch, so on SUCCESS the variable keeps whatever it
|
|
129
|
+
# already held — and an EXPORTED variable of this name is inherited from the
|
|
130
|
+
# ambient environment. `${VAR:-0}` defaults only when unset/empty, so
|
|
131
|
+
# `CMD_STATUS=3 git commit …` made this hook print "validator disabled" and exit 0,
|
|
132
|
+
# silently accepting a non-conforming commit. Found by review of #4429.
|
|
106
133
|
CMD_STATUS=${CMD_STATUS:-0}
|
|
107
134
|
if [ "$CMD_STATUS" != "0" ]; then
|
|
108
135
|
# Could not extract tool_input.command at all (node missing, malformed
|
|
@@ -127,6 +154,12 @@ GIT_CMD_LIB="$HOOK_DIR/lib/git-cmd.js" node -e "
|
|
|
127
154
|
process.exit(3);
|
|
128
155
|
}
|
|
129
156
|
" "$CMD" 2>"$CLASSIFY_ERR" || CLASSIFY_STATUS=$?
|
|
157
|
+
# Pre-initialised, NOT left to `${...:-0}` alone: the capture below only
|
|
158
|
+
# assigns on the `||` branch, so on SUCCESS the variable keeps whatever it
|
|
159
|
+
# already held — and an EXPORTED variable of this name is inherited from the
|
|
160
|
+
# ambient environment. `${VAR:-0}` defaults only when unset/empty, so
|
|
161
|
+
# `CLASSIFY_STATUS=3 git commit …` made this hook print "validator disabled" and exit 0,
|
|
162
|
+
# silently accepting a non-conforming commit. Found by review of #4429.
|
|
130
163
|
CLASSIFY_STATUS=${CLASSIFY_STATUS:-0}
|
|
131
164
|
if [ "$CLASSIFY_STATUS" != "0" ] && [ "$CLASSIFY_STATUS" != "1" ]; then
|
|
132
165
|
# 0 = is a git commit (validate below); 1 = genuinely not a git commit
|
|
@@ -557,7 +590,7 @@ if [ "$CLASSIFY_STATUS" = "0" ]; then
|
|
|
557
590
|
fi
|
|
558
591
|
# Single source of truth for the accepted commit-type list (#3811): the
|
|
559
592
|
# 10 built-ins plus whatever passed the safe-token filter above. Both the
|
|
560
|
-
#
|
|
593
|
+
# membership test and the human-readable error text below are derived
|
|
561
594
|
# from this ONE array — no hand-synced second copy.
|
|
562
595
|
#
|
|
563
596
|
# The `"${EXTRA_COMMIT_TYPES[@]+"${EXTRA_COMMIT_TYPES[@]}"}"` form (not
|
|
@@ -568,7 +601,6 @@ if [ "$CLASSIFY_STATUS" = "0" ]; then
|
|
|
568
601
|
# /bin/bash 3.2.57 on macOS. The `${arr[@]+word}` form is the
|
|
569
602
|
# nounset-safe idiom for "expand if set, empty otherwise" on empty arrays.
|
|
570
603
|
COMMIT_TYPES=("${BUILTIN_COMMIT_TYPES[@]}" "${EXTRA_COMMIT_TYPES[@]+"${EXTRA_COMMIT_TYPES[@]}"}")
|
|
571
|
-
COMMIT_TYPE_ALT=$(IFS='|'; echo "${COMMIT_TYPES[*]}")
|
|
572
604
|
COMMIT_TYPE_LIST=$(printf '%s, ' "${COMMIT_TYPES[@]}")
|
|
573
605
|
COMMIT_TYPE_LIST="${COMMIT_TYPE_LIST%, }"
|
|
574
606
|
# Typed `valid_types` array (#3811 review finding): CONTRIBUTING.md bans
|
|
@@ -580,8 +612,35 @@ if [ "$CLASSIFY_STATUS" = "0" ]; then
|
|
|
580
612
|
# or `\`.
|
|
581
613
|
COMMIT_TYPES_JSON=$(printf '"%s",' "${COMMIT_TYPES[@]}")
|
|
582
614
|
COMMIT_TYPES_JSON="[${COMMIT_TYPES_JSON%,}]"
|
|
583
|
-
# Validate Conventional Commits format
|
|
584
|
-
|
|
615
|
+
# Validate Conventional Commits format.
|
|
616
|
+
#
|
|
617
|
+
# #4429: do NOT build `^(type1|type2|...)` out of COMMIT_TYPES. That
|
|
618
|
+
# alternation grows with the CONFIGURED list, and how large a pattern can be
|
|
619
|
+
# compiled is a property of the platform's regex engine. bash 3.2.57 / BSD
|
|
620
|
+
# libc (macOS, this file's stated target) caps it at 64 KiB - bisected: a
|
|
621
|
+
# 65504-byte alternation compiles, 65515 fails. bash 5.2 / glibc has no
|
|
622
|
+
# reachable cap, so this half never bit Linux. Past a cap `[[ =~ ]]`
|
|
623
|
+
# returns 2, and `if !` cannot tell a COMPILE ERROR from "the subject does
|
|
624
|
+
# not conform" - so a valid `feat(auth): ...` was blocked with
|
|
625
|
+
# CONVENTIONAL_COMMITS_VIOLATION while `feat` sat in its own valid_types.
|
|
626
|
+
#
|
|
627
|
+
# Match the SHAPE with a fixed-size pattern, then test membership against
|
|
628
|
+
# the array. The regex no longer depends on how many types are configured,
|
|
629
|
+
# and the loop adds no subprocess or pipe (the #4429 hazard this file
|
|
630
|
+
# already avoids elsewhere). The character class is exactly the safe-token
|
|
631
|
+
# filter `^[a-z][a-z0-9-]*$` applied above, so it captures every type that
|
|
632
|
+
# can legally reach COMMIT_TYPES and no token that cannot.
|
|
633
|
+
SUBJECT_TYPE=''
|
|
634
|
+
if [[ "$SUBJECT" =~ ^([a-z][a-z0-9-]*)(\(.+\))?:[[:space:]].+ ]]; then
|
|
635
|
+
SUBJECT_TYPE="${BASH_REMATCH[1]}"
|
|
636
|
+
fi
|
|
637
|
+
COMMIT_TYPE_OK=0
|
|
638
|
+
if [ -n "$SUBJECT_TYPE" ]; then
|
|
639
|
+
for _known_type in "${COMMIT_TYPES[@]}"; do
|
|
640
|
+
if [ "$_known_type" = "$SUBJECT_TYPE" ]; then COMMIT_TYPE_OK=1; break; fi
|
|
641
|
+
done
|
|
642
|
+
fi
|
|
643
|
+
if [ "$COMMIT_TYPE_OK" -ne 1 ]; then
|
|
585
644
|
# Emit typed `code` and `valid_types` fields alongside `reason` (#2974,
|
|
586
645
|
# #3811). Tests assert on the stable code string and the typed array;
|
|
587
646
|
# the reason is the human-readable copy, never grepped by tests.
|
|
@@ -31,14 +31,23 @@ const fs = require('fs');
|
|
|
31
31
|
const path = require('path');
|
|
32
32
|
const { spawnSync } = require('child_process');
|
|
33
33
|
const { allow, deny } = require('./lib/hook-exit.js');
|
|
34
|
-
const { reportIfUndetermined } = require('./lib/git-probe.js');
|
|
34
|
+
const { reportIfUndetermined, BLOCKING_GUARD_PROBE_TIMEOUT_MS } = require('./lib/git-probe.js');
|
|
35
35
|
|
|
36
36
|
// #3911 (ADR-3889 Phase 7): the exit(2) call site (block(), below) is
|
|
37
37
|
// migrated to hook-exit.js's deny(undefined, reason) — see
|
|
38
38
|
// gsd-windsurf-pre-command.js's identical note for the fixed defect
|
|
39
39
|
// (terminateNow's fd 1/fd 2 writes now run in independent try/catch blocks).
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
// Shared blocking-guard probe budget (hooks/lib/git-probe.js, #5180): a timed-out
|
|
42
|
+
// probe fails open, so it is sized for a starved host. Windsurf documents NO
|
|
43
|
+
// hook timeout (docs.devin.ai/desktop/cascade/hooks lists only an aspirational
|
|
44
|
+
// "sub-100ms" target) and this surface's registration entry
|
|
45
|
+
// (buildWindsurfHookEntry in src/runtime-hooks-surface.cts) carries none, so the
|
|
46
|
+
// host budget is UNKNOWN. This guard runs at most 3 sequential probes (15 s
|
|
47
|
+
// worst case); that worst case is NOT claimed to fit any host budget — if
|
|
48
|
+
// Windsurf kills the hook sooner, the host kill fails open exactly as a probe
|
|
49
|
+
// timeout does. The guard keeps its fail-open posture either way.
|
|
50
|
+
const SPAWNOPT = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: BLOCKING_GUARD_PROBE_TIMEOUT_MS, windowsHide: true };
|
|
42
51
|
|
|
43
52
|
function git(args, cwd) {
|
|
44
53
|
return spawnSync('git', args, { ...SPAWNOPT, cwd });
|
|
@@ -22,7 +22,7 @@ const path = require('path');
|
|
|
22
22
|
const { spawnSync } = require('child_process');
|
|
23
23
|
const { tokenize, skipToSubcommand } = require('./lib/git-cmd.js');
|
|
24
24
|
const { HOOK_ON_CRASH, allow, deny, crash } = require('./lib/hook-exit.js');
|
|
25
|
-
const { reportIfUndetermined } = require('./lib/git-probe.js');
|
|
25
|
+
const { reportIfUndetermined, BLOCKING_GUARD_PROBE_TIMEOUT_MS } = require('./lib/git-probe.js');
|
|
26
26
|
|
|
27
27
|
// This guard is almost entirely advisory (fail open — a broken advisory must
|
|
28
28
|
// never wedge every tool call), with ONE hard block (#3504 force-add-on-
|
|
@@ -87,11 +87,12 @@ function currentBranch(cwd) {
|
|
|
87
87
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
88
88
|
windowsHide: true,
|
|
89
89
|
// #3504: bounded — this ran unbounded before, an indefinite hang under a
|
|
90
|
-
// wedged git would hang every PreToolUse call.
|
|
91
|
-
// budget
|
|
90
|
+
// wedged git would hang every PreToolUse call. The probe gets the shared
|
|
91
|
+
// blocking-guard budget (hooks/lib/git-probe.js, #5180), well inside the
|
|
92
|
+
// 120s host budget this guard is registered with (#3981); a timeout
|
|
92
93
|
// returns '' (branch unknown), which both the block decision and the
|
|
93
94
|
// fail-closed re-check treat as "cannot establish agent branch".
|
|
94
|
-
timeout:
|
|
95
|
+
timeout: BLOCKING_GUARD_PROBE_TIMEOUT_MS,
|
|
95
96
|
killSignal: 'SIGTERM',
|
|
96
97
|
});
|
|
97
98
|
// #3911: a timeout/spawn-failure here previously degraded to '' exactly
|
|
@@ -18,7 +18,7 @@ const fs = require('fs');
|
|
|
18
18
|
const path = require('path');
|
|
19
19
|
const { spawnSync } = require('child_process');
|
|
20
20
|
const { HOOK_ON_CRASH, allow, deny, crash } = require('./lib/hook-exit.js');
|
|
21
|
-
const { reportIfUndetermined } = require('./lib/git-probe.js');
|
|
21
|
+
const { reportIfUndetermined, BLOCKING_GUARD_PROBE_TIMEOUT_MS } = require('./lib/git-probe.js');
|
|
22
22
|
|
|
23
23
|
// This guard's outer catch has always exited 0 (fail open): a path guard that
|
|
24
24
|
// cannot resolve the worktree must not block the user's edit — its whole job
|
|
@@ -28,7 +28,11 @@ const { reportIfUndetermined } = require('./lib/git-probe.js');
|
|
|
28
28
|
// rather than inheriting a default (#3911).
|
|
29
29
|
const ON_CRASH = HOOK_ON_CRASH.ALLOW;
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
// A timed-out git probe fails OPEN (see ON_CRASH above), so the per-probe
|
|
32
|
+
// budget is the shared blocking-guard value, sized for a starved host rather
|
|
33
|
+
// than git's ~0.1s typical run (rationale and arithmetic in
|
|
34
|
+
// hooks/lib/git-probe.js, #5180). This guard runs at most 3 sequential probes.
|
|
35
|
+
const SPAWNOPT = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: BLOCKING_GUARD_PROBE_TIMEOUT_MS, windowsHide: true };
|
|
32
36
|
|
|
33
37
|
function git(args, cwd) {
|
|
34
38
|
return spawnSync('git', args, { ...SPAWNOPT, cwd });
|
|
@@ -185,6 +185,17 @@ function setPendingOutcome(v) {
|
|
|
185
185
|
function getPendingOutcome() {
|
|
186
186
|
return globalThis[PENDING_OUTCOME_KEY];
|
|
187
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Declare the outcome a void-returning `main()` ends with (#5170, ADR-5057 §4): writes the
|
|
190
|
+
* pending-outcome cell that `runMain` projects. Validates `name` is an outcome
|
|
191
|
+
* `projectOutcome` accepts (PASS, FAIL or a registered name) and THROWS otherwise, so a typo'd
|
|
192
|
+
* name fails at the declaration rather than at process exit. Call it AFTER `output()`, which
|
|
193
|
+
* rewrites the cell on every call. `output()` and this are the only writers of the cell.
|
|
194
|
+
*/
|
|
195
|
+
function declareOutcome(name) {
|
|
196
|
+
projectOutcome(name, 'v1');
|
|
197
|
+
setPendingOutcome(name);
|
|
198
|
+
}
|
|
188
199
|
const EXIT_CONTRACT_FLAG_PREFIX = '--exit-contract=';
|
|
189
200
|
/** Scan argv for the FIRST `--exit-contract=<value>` token; undefined if absent. */
|
|
190
201
|
function findExitContractFlag(argv) {
|
|
@@ -557,4 +568,5 @@ module.exports = {
|
|
|
557
568
|
terminateNow,
|
|
558
569
|
setPendingOutcome,
|
|
559
570
|
getPendingOutcome,
|
|
571
|
+
declareOutcome,
|
|
560
572
|
};
|