@opengsd/gsd-core 1.11.0 → 1.12.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/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +17 -9
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +0 -1
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +3 -1
- package/agents/gsd-plan-checker.md +15 -55
- package/agents/gsd-planner.md +6 -4
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +2 -2
- package/agents/gsd-roadmapper.md +15 -11
- package/agents/gsd-ui-checker.md +63 -4
- package/agents/gsd-ui-researcher.md +41 -3
- package/agents/gsd-verifier.md +1 -1
- package/bin/install.js +609 -134
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/quick.md +8 -4
- package/gsd-core/bin/gsd-tools.cjs +567 -51
- package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
- package/gsd-core/bin/lib/api-coverage.cjs +30 -9
- package/gsd-core/bin/lib/artifacts.cjs +2 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
- package/gsd-core/bin/lib/audit.cjs +163 -41
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +336 -95
- package/gsd-core/bin/lib/capability-state.cjs +18 -3
- package/gsd-core/bin/lib/capability-validator.cjs +205 -18
- package/gsd-core/bin/lib/check-command-router.cjs +145 -5
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +543 -44
- package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
- package/gsd-core/bin/lib/config-loader.cjs +118 -29
- package/gsd-core/bin/lib/config.cjs +92 -2
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +84 -7
- package/gsd-core/bin/lib/edge-probe.cjs +9 -1
- package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
- package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
- package/gsd-core/bin/lib/frontmatter.cjs +840 -305
- package/gsd-core/bin/lib/gap-checker.cjs +27 -3
- package/gsd-core/bin/lib/git-base-branch.cjs +174 -39
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +6 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
- package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
- package/gsd-core/bin/lib/host-integration.cjs +39 -6
- package/gsd-core/bin/lib/init-command-router.cjs +118 -21
- package/gsd-core/bin/lib/init.cjs +120 -41
- package/gsd-core/bin/lib/install-engine.cjs +68 -3
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +33 -1
- package/gsd-core/bin/lib/install-profiles.cjs +78 -4
- package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
- package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +160 -15
- package/gsd-core/bin/lib/learnings.cjs +85 -14
- package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
- package/gsd-core/bin/lib/markdown-table.cjs +52 -4
- package/gsd-core/bin/lib/milestone.cjs +90 -5
- package/gsd-core/bin/lib/model-catalog.cjs +177 -19
- package/gsd-core/bin/lib/model-resolver.cjs +10 -28
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
- package/gsd-core/bin/lib/phase-id.cjs +70 -4
- package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
- package/gsd-core/bin/lib/phase-locator.cjs +138 -17
- package/gsd-core/bin/lib/phase.cjs +405 -84
- package/gsd-core/bin/lib/plan-document.cjs +263 -0
- package/gsd-core/bin/lib/plan-scan.cjs +13 -2
- package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -14
- package/gsd-core/bin/lib/planning-workspace.cjs +56 -0
- package/gsd-core/bin/lib/probe-core.cjs +4 -1
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
- package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +9 -9
- package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
- package/gsd-core/bin/lib/roadmap-parser.cjs +79 -16
- package/gsd-core/bin/lib/roadmap.cjs +74 -19
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +96 -8
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +34 -1
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +287 -55
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
- package/gsd-core/bin/lib/shell-command-projection.cjs +71 -8
- package/gsd-core/bin/lib/smart-entry.cjs +12 -22
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +47 -18
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +186 -0
- package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
- package/gsd-core/bin/lib/state-transition.cjs +517 -101
- package/gsd-core/bin/lib/state.cjs +946 -163
- package/gsd-core/bin/lib/surface.cjs +10 -2
- package/gsd-core/bin/lib/task-command-router.cjs +111 -1
- package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
- package/gsd-core/bin/lib/teams-status.cjs +4 -1
- package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
- package/gsd-core/bin/lib/uat.cjs +1376 -125
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
- package/gsd-core/bin/lib/unusable-input.cjs +13 -0
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/vendor/README.md +43 -5
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
- package/gsd-core/bin/lib/verification.cjs +14 -1
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify.cjs +95 -40
- package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +177 -21
- package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
- package/gsd-core/bin/shared/exit-codes.json +8 -0
- package/gsd-core/bin/shared/exit-codes.sh +20 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -1
- package/gsd-core/references/agent-contracts.md +3 -2
- package/gsd-core/references/api-coverage.md +24 -2
- package/gsd-core/references/autonomous-smart-discuss.md +3 -3
- package/gsd-core/references/checkpoints.md +37 -19
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/edge-probe.md +8 -0
- package/gsd-core/references/execute-mvp-tdd.md +1 -3
- package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
- package/gsd-core/references/execute-phase-wave-guard.md +11 -9
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-integration.md +5 -5
- package/gsd-core/references/git-planning-commit.md +3 -3
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +22 -0
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/nyquist-compliance.md +74 -0
- package/gsd-core/references/offer-next.md +3 -5
- package/gsd-core/references/phase-argument-parsing.md +3 -3
- package/gsd-core/references/planner-failing-direction.md +53 -0
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- package/gsd-core/references/planner-revision.md +1 -1
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +37 -8
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/tdd.md +1 -3
- package/gsd-core/references/ui-brand.md +65 -21
- package/gsd-core/references/ui-consideration-probe.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +2 -2
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +11 -11
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/SECURITY.md +3 -3
- package/gsd-core/templates/UI-SPEC.md +25 -3
- package/gsd-core/templates/VALIDATION.md +3 -3
- package/gsd-core/templates/phase-prompt.md +3 -0
- package/gsd-core/templates/state.md +7 -0
- 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 +3 -3
- package/gsd-core/workflows/add-tests.md +3 -8
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +4 -9
- package/gsd-core/workflows/audit-fix.md +12 -3
- package/gsd-core/workflows/audit-milestone.md +9 -9
- package/gsd-core/workflows/audit-uat.md +17 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +10 -26
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +2 -2
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +1 -1
- package/gsd-core/workflows/code-review-fix.md +1 -1
- package/gsd-core/workflows/code-review.md +121 -40
- package/gsd-core/workflows/complete-milestone.md +15 -10
- package/gsd-core/workflows/debug.md +5 -3
- package/gsd-core/workflows/diagnose-issues.md +12 -6
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +3 -7
- package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +2 -2
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +3 -6
- package/gsd-core/workflows/docs-update.md +5 -4
- package/gsd-core/workflows/edit-phase.md +1 -1
- package/gsd-core/workflows/eval-review.md +4 -9
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +113 -11
- 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 +22 -4
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase.md +38 -54
- package/gsd-core/workflows/execute-plan.md +17 -12
- package/gsd-core/workflows/explore.md +1 -1
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/fast.md +2 -2
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +5 -5
- package/gsd-core/workflows/health.md +3 -6
- package/gsd-core/workflows/import.md +14 -11
- package/gsd-core/workflows/inbox.md +4 -5
- package/gsd-core/workflows/ingest-docs.md +44 -11
- package/gsd-core/workflows/insert-phase.md +5 -5
- package/gsd-core/workflows/list-seeds.md +5 -3
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +12 -23
- package/gsd-core/workflows/map-codebase.md +1 -1
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +2 -2
- package/gsd-core/workflows/new-milestone.md +9 -21
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +12 -26
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +2 -2
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
- package/gsd-core/workflows/plan-phase.md +121 -42
- package/gsd-core/workflows/plan-review-convergence.md +46 -9
- package/gsd-core/workflows/plant-seed.md +2 -2
- package/gsd-core/workflows/pr-branch.md +187 -51
- package/gsd-core/workflows/profile-user.md +16 -14
- package/gsd-core/workflows/progress.md +27 -12
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -3
- package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
- package/gsd-core/workflows/quick/steps/research-phase.md +2 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick.md +20 -29
- package/gsd-core/workflows/remove-phase.md +4 -4
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +8 -12
- package/gsd-core/workflows/review.md +193 -15
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +2 -2
- package/gsd-core/workflows/settings-advanced.md +7 -9
- package/gsd-core/workflows/settings-integrations.md +64 -31
- package/gsd-core/workflows/settings.md +3 -5
- package/gsd-core/workflows/ship.md +12 -6
- package/gsd-core/workflows/sketch-wrap-up.md +11 -17
- package/gsd-core/workflows/sketch.md +12 -18
- package/gsd-core/workflows/smart-entry.md +3 -5
- package/gsd-core/workflows/spec-phase.md +23 -1
- package/gsd-core/workflows/spike-wrap-up.md +7 -11
- package/gsd-core/workflows/spike.md +20 -31
- package/gsd-core/workflows/stats.md +2 -2
- package/gsd-core/workflows/sync-skills.md +1 -1
- package/gsd-core/workflows/thread.md +11 -7
- package/gsd-core/workflows/transition.md +5 -5
- package/gsd-core/workflows/ui-phase.md +10 -16
- package/gsd-core/workflows/ui-review.md +6 -10
- package/gsd-core/workflows/ultraplan-phase.md +5 -13
- package/gsd-core/workflows/undo.md +8 -16
- package/gsd-core/workflows/update.md +6 -10
- package/gsd-core/workflows/validate-phase.md +2 -2
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +57 -18
- package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +19 -10
- package/hooks/dist/gsd-cursor-post-tool.js +3 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
- package/hooks/dist/gsd-cursor-session-start.js +2 -1
- package/hooks/dist/gsd-cursor-stop.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
- package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
- package/hooks/dist/gsd-graphify-update.sh +22 -18
- package/hooks/dist/gsd-node-runner.sh +76 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +16 -7
- package/hooks/dist/gsd-read-guard.js +16 -7
- package/hooks/dist/gsd-read-injection-scanner.js +17 -8
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +215 -26
- package/hooks/dist/gsd-validate-commit.sh +80 -6
- package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
- package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
- package/hooks/dist/gsd-workflow-guard.js +34 -16
- package/hooks/dist/gsd-worktree-path-guard.js +36 -21
- package/hooks/dist/gsd-write-guard.js +35 -25
- package/hooks/dist/lib/cli-exit.js +560 -0
- package/hooks/dist/lib/exit-code-registry.js +98 -0
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/managed-hooks-registry.cjs +3 -0
- package/hooks/gsd-agent-isolation-guard.js +77 -38
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +19 -10
- package/hooks/gsd-cursor-post-tool.js +3 -1
- package/hooks/gsd-cursor-pre-tool.js +3 -1
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +28 -23
- package/hooks/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +76 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +16 -7
- package/hooks/gsd-read-guard.js +16 -7
- package/hooks/gsd-read-injection-scanner.js +17 -8
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +215 -26
- package/hooks/gsd-validate-commit.sh +80 -6
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +34 -16
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/managed-hooks-registry.cjs +3 -0
- package/package.json +12 -7
- package/scripts/base64-scan.sh +74 -12
- package/scripts/build-hooks.js +5 -0
- package/scripts/check-glossary-refs.cjs +77 -15
- package/scripts/check-mutation-score-ratchet.cjs +156 -0
- package/scripts/ci-check-job-near-cap.cjs +49 -0
- package/scripts/ci-pr-mergeability.cjs +262 -0
- package/scripts/ci-test-scope.cjs +45 -12
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/docs-guard-registry.cjs +396 -0
- package/scripts/gen-capability-registry.cjs +8 -6
- package/scripts/gen-exit-code-docs.cjs +318 -0
- package/scripts/gen-exit-code-registry.cjs +891 -0
- package/scripts/gen-features.cjs +836 -0
- package/scripts/gen-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-loop-host-contract.cjs +134 -1
- package/scripts/gen-scripts-cli-exit.cjs +185 -0
- package/scripts/gen-state-md-docs.cjs +727 -0
- package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
- package/scripts/lib/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +32 -2
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +21 -8
- package/scripts/lint-planning-prompt-drift.cjs +38 -1
- package/scripts/lint-removed-but-needed.cjs +184 -16
- package/scripts/lint-seam-enforcement.cjs +182 -0
- package/scripts/lint-slug-derivation-drift.cjs +921 -0
- package/scripts/lint-source-test-name-collision.cjs +241 -0
- package/scripts/lint-state-write-path-drift.cjs +337 -432
- package/scripts/lint-test-file-count.allowlist.json +122 -4
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +51 -64
- package/scripts/lint-vendored-deps.cjs +208 -35
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/prompt-injection-scan.sh +75 -14
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +22 -3
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-quick/SKILL.md +8 -4
- package/vscode/package.json +1 -1
- package/bin/lib/ui-safety-gate.cjs +0 -109
- package/scripts/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/state-write-path-drift-baseline.json +0 -19
|
@@ -21,6 +21,18 @@ const fs = require('fs');
|
|
|
21
21
|
const path = require('path');
|
|
22
22
|
const { spawnSync } = require('child_process');
|
|
23
23
|
const { tokenize, skipToSubcommand } = require('./lib/git-cmd.js');
|
|
24
|
+
const { HOOK_ON_CRASH, allow, deny, crash } = require('./lib/hook-exit.js');
|
|
25
|
+
const { reportIfUndetermined } = require('./lib/git-probe.js');
|
|
26
|
+
|
|
27
|
+
// This guard is almost entirely advisory (fail open — a broken advisory must
|
|
28
|
+
// never wedge every tool call), with ONE hard block (#3504 force-add-on-
|
|
29
|
+
// agent-branch) that fails CLOSED on internal error instead. That split is
|
|
30
|
+
// re-derived dynamically inside the outer catch (failClosedBlockContext), not
|
|
31
|
+
// a fixed per-hook policy, so ON_CRASH names only the FINAL, unconditional
|
|
32
|
+
// fallback reached when the fail-closed context does not apply — i.e. the
|
|
33
|
+
// historical exit(0). Declared ONCE here so that fallback states its policy
|
|
34
|
+
// explicitly rather than inheriting a default (#3911).
|
|
35
|
+
const ON_CRASH = HOOK_ON_CRASH.ALLOW;
|
|
24
36
|
|
|
25
37
|
function forceGitAddCwds(command, defaultCwd) {
|
|
26
38
|
const tokens = tokenize(command || '');
|
|
@@ -82,6 +94,12 @@ function currentBranch(cwd) {
|
|
|
82
94
|
timeout: 2000,
|
|
83
95
|
killSignal: 'SIGTERM',
|
|
84
96
|
});
|
|
97
|
+
// #3911: a timeout/spawn-failure here previously degraded to '' exactly
|
|
98
|
+
// like a clean "not on a branch" answer — indistinguishable from the
|
|
99
|
+
// caller's point of view. reportIfUndetermined is a no-op on a genuine
|
|
100
|
+
// negative and only emits a diagnostic when the probe itself could not
|
|
101
|
+
// run; the '' fallback (and therefore this hook's exit code) is unchanged.
|
|
102
|
+
reportIfUndetermined('gsd-workflow-guard', 'git branch --show-current', result);
|
|
85
103
|
if (result.status !== 0) return '';
|
|
86
104
|
return result.stdout.trim();
|
|
87
105
|
}
|
|
@@ -114,9 +132,10 @@ function emitForceAddBlock(origin) {
|
|
|
114
132
|
origin: failClosed ? 'fail-closed' : 'force-add-detected',
|
|
115
133
|
reason,
|
|
116
134
|
};
|
|
117
|
-
|
|
118
|
-
//
|
|
119
|
-
|
|
135
|
+
// Kimi CLI's exit-2 protocol feeds stderr back to the model (#2304) — deny's
|
|
136
|
+
// stderrPayload keeps fd 2 to the plain reason string, matching the
|
|
137
|
+
// pre-migration two-write byte-for-byte.
|
|
138
|
+
deny(output, output.reason);
|
|
120
139
|
}
|
|
121
140
|
|
|
122
141
|
// #3504 fail-closed context re-derivation. Runs INSIDE the outer catch, after
|
|
@@ -260,7 +279,7 @@ function normalizeKimiPayload(data) {
|
|
|
260
279
|
}
|
|
261
280
|
|
|
262
281
|
let input = '';
|
|
263
|
-
const stdinTimeout = setTimeout(() =>
|
|
282
|
+
const stdinTimeout = setTimeout(() => allow(undefined), 3000);
|
|
264
283
|
process.stdin.setEncoding('utf8');
|
|
265
284
|
process.stdin.on('data', chunk => input += chunk);
|
|
266
285
|
process.stdin.on('end', () => {
|
|
@@ -282,29 +301,28 @@ process.stdin.on('end', () => {
|
|
|
282
301
|
|
|
283
302
|
if (toolName === 'Bash') {
|
|
284
303
|
if (!isWorkflowGuardEnabled) {
|
|
285
|
-
|
|
304
|
+
allow(undefined);
|
|
286
305
|
}
|
|
287
306
|
const command = data.tool_input?.command || '';
|
|
288
307
|
for (const gitCwd of forceGitAddCwds(command, cwd)) {
|
|
289
308
|
const branch = currentBranch(gitCwd);
|
|
290
309
|
if (/^(worktree-)?agent-/.test(branch)) {
|
|
291
310
|
emitForceAddBlock();
|
|
292
|
-
process.exit(2);
|
|
293
311
|
}
|
|
294
312
|
}
|
|
295
|
-
|
|
313
|
+
allow(undefined);
|
|
296
314
|
}
|
|
297
315
|
|
|
298
316
|
// Only guard Write, Edit, and MultiEdit tool calls
|
|
299
317
|
if (!['Write', 'Edit', 'MultiEdit'].includes(toolName)) {
|
|
300
|
-
|
|
318
|
+
allow(undefined);
|
|
301
319
|
}
|
|
302
320
|
|
|
303
321
|
// Check if we're inside a GSD workflow (Task subagent or /gsd- skill)
|
|
304
322
|
// Subagents have a session_id that differs from the parent
|
|
305
323
|
// and typically have a description field set by the orchestrator
|
|
306
324
|
if (data.tool_input?.is_subagent || data.session_type === 'task') {
|
|
307
|
-
|
|
325
|
+
allow(undefined);
|
|
308
326
|
}
|
|
309
327
|
|
|
310
328
|
// Check the file being edited
|
|
@@ -319,7 +337,7 @@ process.stdin.on('end', () => {
|
|
|
319
337
|
|
|
320
338
|
// Allow edits to .planning/ files (GSD state management)
|
|
321
339
|
if (filePath.includes('.planning/') || filePath.includes('.planning\\')) {
|
|
322
|
-
|
|
340
|
+
allow(undefined);
|
|
323
341
|
}
|
|
324
342
|
|
|
325
343
|
// Allow edits to common config/docs files that don't need GSD tracking
|
|
@@ -332,11 +350,11 @@ process.stdin.on('end', () => {
|
|
|
332
350
|
/settings\.json$/,
|
|
333
351
|
];
|
|
334
352
|
if (allowedPatterns.some(p => p.test(filePath))) {
|
|
335
|
-
|
|
353
|
+
allow(undefined);
|
|
336
354
|
}
|
|
337
355
|
|
|
338
356
|
if (!isWorkflowGuardEnabled) {
|
|
339
|
-
|
|
357
|
+
allow(undefined); // Guard disabled (default) or no GSD project
|
|
340
358
|
}
|
|
341
359
|
|
|
342
360
|
// If we get here: GSD project, guard enabled, file edit outside .planning/,
|
|
@@ -357,13 +375,13 @@ process.stdin.on('end', () => {
|
|
|
357
375
|
// #3504: split posture on internal error. The ONE hard block in this hook
|
|
358
376
|
// (force-add on agent branches) fails CLOSED — if the blocking context can
|
|
359
377
|
// be re-derived from the payload (Bash tool + guard enabled + determinably
|
|
360
|
-
// an agent branch),
|
|
378
|
+
// an agent branch), deny rather than silently allowing. Everything else
|
|
361
379
|
// keeps the historical fail-open posture: a broken advisory guard must
|
|
362
|
-
// never wedge the session's tool calls.
|
|
380
|
+
// never wedge the session's tool calls. ON_CRASH is declared ALLOW at
|
|
381
|
+
// module top for exactly that unconditional fallback (#3911).
|
|
363
382
|
if (failClosedBlockContext(input)) {
|
|
364
383
|
emitForceAddBlock('fail-closed');
|
|
365
|
-
process.exit(2);
|
|
366
384
|
}
|
|
367
|
-
|
|
385
|
+
crash(ON_CRASH, undefined);
|
|
368
386
|
}
|
|
369
387
|
});
|
|
@@ -17,6 +17,16 @@
|
|
|
17
17
|
const fs = require('fs');
|
|
18
18
|
const path = require('path');
|
|
19
19
|
const { spawnSync } = require('child_process');
|
|
20
|
+
const { HOOK_ON_CRASH, allow, deny, crash } = require('./lib/hook-exit.js');
|
|
21
|
+
const { reportIfUndetermined } = require('./lib/git-probe.js');
|
|
22
|
+
|
|
23
|
+
// This guard's outer catch has always exited 0 (fail open): a path guard that
|
|
24
|
+
// cannot resolve the worktree must not block the user's edit — its whole job
|
|
25
|
+
// is a targeted containment check, not a general-purpose file-write blocker,
|
|
26
|
+
// and an unresolved worktree root gives it nothing to check against. Declared
|
|
27
|
+
// ONCE here so the outer catch's crash() call states its policy explicitly
|
|
28
|
+
// rather than inheriting a default (#3911).
|
|
29
|
+
const ON_CRASH = HOOK_ON_CRASH.ALLOW;
|
|
20
30
|
|
|
21
31
|
const SPAWNOPT = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 2000, windowsHide: true };
|
|
22
32
|
|
|
@@ -132,7 +142,7 @@ function normalizeKimiPayload(data) {
|
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
let input = '';
|
|
135
|
-
const stdinTimeout = setTimeout(() =>
|
|
145
|
+
const stdinTimeout = setTimeout(() => allow(undefined), 3000);
|
|
136
146
|
process.stdin.setEncoding('utf8');
|
|
137
147
|
process.stdin.on('data', chunk => input += chunk);
|
|
138
148
|
process.stdin.on('end', () => {
|
|
@@ -143,7 +153,7 @@ process.stdin.on('end', () => {
|
|
|
143
153
|
|
|
144
154
|
// Only guard Edit, Write, and MultiEdit tool calls
|
|
145
155
|
if (toolName !== 'Edit' && toolName !== 'Write' && toolName !== 'MultiEdit') {
|
|
146
|
-
|
|
156
|
+
allow(undefined);
|
|
147
157
|
}
|
|
148
158
|
|
|
149
159
|
const cwd = data.cwd || process.cwd();
|
|
@@ -154,15 +164,20 @@ process.stdin.on('end', () => {
|
|
|
154
164
|
// In the main repo or a submodule it returns .git (or a path without /worktrees/).
|
|
155
165
|
// This approach works even when cwd is a subdirectory of the worktree.
|
|
156
166
|
const gitDirResult = git(['rev-parse', '--git-dir'], cwd);
|
|
167
|
+
// #3911: a timeout/spawn-failure result is indistinguishable from a clean
|
|
168
|
+
// "not a git repo" answer by status/stdout alone — reportIfUndetermined
|
|
169
|
+
// is a no-op on a genuine negative and only fires the diagnostic when the
|
|
170
|
+
// probe itself could not run. The allow() below is UNCHANGED either way.
|
|
171
|
+
reportIfUndetermined('gsd-worktree-path-guard', 'git rev-parse --git-dir', gitDirResult);
|
|
157
172
|
if (gitDirResult.status !== 0 || !gitDirResult.stdout) {
|
|
158
|
-
|
|
173
|
+
allow(undefined); // not a git repo — pass through
|
|
159
174
|
}
|
|
160
175
|
|
|
161
176
|
const gitDir = gitDirResult.stdout.trim();
|
|
162
177
|
// A linked worktree's --git-dir contains .git/worktrees/ as a path component
|
|
163
178
|
const isLinkedWorktree = /[/\\]\.git[/\\]worktrees[/\\]/.test(gitDir);
|
|
164
179
|
if (!isLinkedWorktree) {
|
|
165
|
-
|
|
180
|
+
allow(undefined); // main repo, submodule, or separate-git-dir — no-op
|
|
166
181
|
}
|
|
167
182
|
|
|
168
183
|
// #1342: Only enforce inside a GSD-managed isolated executor worktree. Those
|
|
@@ -172,18 +187,20 @@ process.stdin.on('end', () => {
|
|
|
172
187
|
// on the user's own branch, so the guard must be a no-op there. Detached HEAD
|
|
173
188
|
// / error → not GSD-managed → no-op.
|
|
174
189
|
const branchResult = git(['symbolic-ref', '--short', 'HEAD'], cwd);
|
|
190
|
+
reportIfUndetermined('gsd-worktree-path-guard', 'git symbolic-ref --short HEAD', branchResult);
|
|
175
191
|
const branch = branchResult.status === 0 && branchResult.stdout ? branchResult.stdout.trim() : '';
|
|
176
192
|
// #3021: accept worktree-wf_<runid>-<n> branches (Workflow backend's naming).
|
|
177
193
|
if (!/^((worktree-)?agent-|worktree-wf_)[A-Za-z0-9._/-]+$/.test(branch)) {
|
|
178
|
-
|
|
194
|
+
allow(undefined); // not a GSD-managed executor worktree — no-op
|
|
179
195
|
}
|
|
180
196
|
|
|
181
197
|
// Get the raw --show-toplevel output for the worktree (cwd).
|
|
182
198
|
// We keep it raw (not path.resolve'd) to compare directly with the
|
|
183
199
|
// file's toplevel — same git binary, same format, no normalization needed.
|
|
184
200
|
const wtTopResult = git(['rev-parse', '--show-toplevel'], cwd);
|
|
201
|
+
reportIfUndetermined('gsd-worktree-path-guard', 'git rev-parse --show-toplevel (worktree cwd)', wtTopResult);
|
|
185
202
|
if (wtTopResult.status !== 0 || !wtTopResult.stdout) {
|
|
186
|
-
|
|
203
|
+
allow(undefined); // can't determine root — fail open
|
|
187
204
|
}
|
|
188
205
|
const wtTopRaw = wtTopResult.stdout.trim();
|
|
189
206
|
|
|
@@ -201,7 +218,7 @@ process.stdin.on('end', () => {
|
|
|
201
218
|
? data.tool_input.file_path
|
|
202
219
|
: '';
|
|
203
220
|
if (!rawFilePath) {
|
|
204
|
-
|
|
221
|
+
allow(undefined);
|
|
205
222
|
}
|
|
206
223
|
|
|
207
224
|
// Relative paths resolve against the tool's CWD, which is inside the worktree
|
|
@@ -219,7 +236,7 @@ process.stdin.on('end', () => {
|
|
|
219
236
|
// `../`-laden path exits 0 at this line and escapes the worktree. Stating a
|
|
220
237
|
// mechanism and an unverified premise — not asserting a live bypass.
|
|
221
238
|
if (!path.isAbsolute(rawFilePath)) {
|
|
222
|
-
|
|
239
|
+
allow(undefined);
|
|
223
240
|
}
|
|
224
241
|
|
|
225
242
|
// Normalise .. traversal so /worktree/src/../../../main/file
|
|
@@ -244,7 +261,7 @@ process.stdin.on('end', () => {
|
|
|
244
261
|
// Walked to root without finding any directory — path is synthetic.
|
|
245
262
|
// A path with no existing ancestor is not the #260 main-repo vector;
|
|
246
263
|
// #260 is caught by the different-git-root branch below. Fail open. (#1342)
|
|
247
|
-
|
|
264
|
+
allow(undefined);
|
|
248
265
|
}
|
|
249
266
|
|
|
250
267
|
// Ask git for the toplevel of the file's location.
|
|
@@ -254,6 +271,7 @@ process.stdin.on('end', () => {
|
|
|
254
271
|
// back-slash inconsistencies) — both values come from the same git binary
|
|
255
272
|
// in the same format by definition.
|
|
256
273
|
const fileTopResult = git(['rev-parse', '--show-toplevel'], checkDir);
|
|
274
|
+
reportIfUndetermined('gsd-worktree-path-guard', 'git rev-parse --show-toplevel (file location)', fileTopResult);
|
|
257
275
|
|
|
258
276
|
if (fileTopResult.status !== 0 || !fileTopResult.stdout) {
|
|
259
277
|
// The target's location is not a git work tree. Two sub-cases:
|
|
@@ -263,6 +281,7 @@ process.stdin.on('end', () => {
|
|
|
263
281
|
// - Truly outside all git repositories (e.g. ~/.claude/plans/) → not the
|
|
264
282
|
// main-repo vector → fail open. (#1342)
|
|
265
283
|
const insideGitDir = git(['rev-parse', '--is-inside-git-dir'], checkDir);
|
|
284
|
+
reportIfUndetermined('gsd-worktree-path-guard', 'git rev-parse --is-inside-git-dir', insideGitDir);
|
|
266
285
|
if (insideGitDir.status === 0 && insideGitDir.stdout && insideGitDir.stdout.trim() === 'true') {
|
|
267
286
|
const output = {
|
|
268
287
|
decision: 'block',
|
|
@@ -271,20 +290,17 @@ process.stdin.on('end', () => {
|
|
|
271
290
|
`not the active worktree at '${wtTopRaw}'. Writing to repository internals via an ` +
|
|
272
291
|
`absolute path is not permitted from an isolated executor worktree. Use a relative path.`,
|
|
273
292
|
};
|
|
274
|
-
|
|
275
|
-
// Kimi feeds stderr (not stdout) back to the model on exit 2.
|
|
276
|
-
process.stderr.write(output.reason);
|
|
277
|
-
process.exit(2);
|
|
293
|
+
deny(output, output.reason);
|
|
278
294
|
}
|
|
279
295
|
// Outside all git repositories — fail open (#1342).
|
|
280
|
-
|
|
296
|
+
allow(undefined);
|
|
281
297
|
}
|
|
282
298
|
|
|
283
299
|
const fileTopRaw = fileTopResult.stdout.trim();
|
|
284
300
|
|
|
285
301
|
// Same git toplevel → file is inside the worktree → allow
|
|
286
302
|
if (fileTopRaw === wtTopRaw) {
|
|
287
|
-
|
|
303
|
+
allow(undefined);
|
|
288
304
|
}
|
|
289
305
|
|
|
290
306
|
// BLOCK: file resolves to a different git root than the active worktree
|
|
@@ -299,12 +315,11 @@ process.stdin.on('end', () => {
|
|
|
299
315
|
`(hook cwd: '${cwd}').`,
|
|
300
316
|
};
|
|
301
317
|
|
|
302
|
-
|
|
303
|
-
// Kimi feeds stderr (not stdout) back to the model on exit 2.
|
|
304
|
-
process.stderr.write(output.reason);
|
|
305
|
-
process.exit(2);
|
|
318
|
+
deny(output, output.reason);
|
|
306
319
|
} catch {
|
|
307
|
-
// Silent fail — never block valid tool calls due to hook errors
|
|
308
|
-
|
|
320
|
+
// Silent fail — never block valid tool calls due to hook errors.
|
|
321
|
+
// ON_CRASH is declared ALLOW at module top: this preserves today's
|
|
322
|
+
// exit(0) fail-open behavior exactly (#3911).
|
|
323
|
+
crash(ON_CRASH, undefined);
|
|
309
324
|
}
|
|
310
325
|
});
|
package/hooks/gsd-write-guard.js
CHANGED
|
@@ -75,6 +75,21 @@
|
|
|
75
75
|
|
|
76
76
|
const fs = require('fs');
|
|
77
77
|
const path = require('path');
|
|
78
|
+
const { HOOK_ON_CRASH, allow, deny, crash } = require('./lib/hook-exit.js');
|
|
79
|
+
|
|
80
|
+
// This guard's outer catch has always exited 0 (fail open — a hook error
|
|
81
|
+
// must never block a legitimate tool call; see the emitBlock/consumeSentinelFor
|
|
82
|
+
// header comments). Declared ONCE here so the outer catch's crash() call
|
|
83
|
+
// states its policy explicitly rather than inheriting a default (#3911).
|
|
84
|
+
const ON_CRASH = HOOK_ON_CRASH.ALLOW;
|
|
85
|
+
|
|
86
|
+
// #3911 (ADR-3889 Phase 7) NOTE: the exit(2) call site (emitBlock, below) is
|
|
87
|
+
// migrated to hooks/lib/hook-exit.js's deny(), using its `stderrPayload`
|
|
88
|
+
// param (added for exactly this site): fd 1 still gets the full JSON
|
|
89
|
+
// `output`, fd 2 gets ONLY the plain-text `output.reason` string — because
|
|
90
|
+
// Kimi's native hook bus reads stderr verbatim back to the model, and a raw
|
|
91
|
+
// JSON-stringified object on stderr is not the same reason text a model
|
|
92
|
+
// should read. Byte-identical to the pre-migration emitBlock.
|
|
78
93
|
|
|
79
94
|
// Block when the pending payload has fewer than this fraction of the on-disk
|
|
80
95
|
// line count (0.4 → a Write shrinking a file below 40% of its current size).
|
|
@@ -158,20 +173,13 @@ function consumeSentinelFor(filePath, normalized) {
|
|
|
158
173
|
|
|
159
174
|
// m2 (round 5): the block emission must itself be exception-safe. An EPIPE
|
|
160
175
|
// from writeSync inside the outer try would land in the fail-OPEN catch —
|
|
161
|
-
// the one outcome the fail-closed branches exist to prevent.
|
|
162
|
-
//
|
|
176
|
+
// the one outcome the fail-closed branches exist to prevent. terminateNow
|
|
177
|
+
// (via deny()) already guarantees this: a failed write never changes the
|
|
178
|
+
// exit code and never throws out of the call. `output.reason` is passed as
|
|
179
|
+
// the distinct stderrPayload so fd 2 gets the plain reason string — not the
|
|
180
|
+
// full JSON `output` fd 1 gets — matching the pre-migration byte-for-byte.
|
|
163
181
|
function emitBlock(output) {
|
|
164
|
-
|
|
165
|
-
// writeSync: pipe writes via process.stdout/stderr are async on Windows
|
|
166
|
-
// and process.exit() does not flush them — a truncated block payload is
|
|
167
|
-
// a guard that silently half-fired.
|
|
168
|
-
fs.writeSync(1, JSON.stringify(output));
|
|
169
|
-
// Kimi feeds stderr (not stdout) back to the model on exit 2.
|
|
170
|
-
fs.writeSync(2, output.reason);
|
|
171
|
-
} catch {
|
|
172
|
-
// Emission failed; the block still stands.
|
|
173
|
-
}
|
|
174
|
-
process.exit(2);
|
|
182
|
+
deny(output, output.reason);
|
|
175
183
|
}
|
|
176
184
|
|
|
177
185
|
// #2304: Kimi's native hook bus delivers Kimi's tool vocabulary in the payload
|
|
@@ -223,7 +231,7 @@ function normalizeKimiPayload(data) {
|
|
|
223
231
|
}
|
|
224
232
|
|
|
225
233
|
let input = '';
|
|
226
|
-
const stdinTimeout = setTimeout(() =>
|
|
234
|
+
const stdinTimeout = setTimeout(() => allow(undefined), 3000);
|
|
227
235
|
process.stdin.setEncoding('utf8');
|
|
228
236
|
process.stdin.on('data', chunk => input += chunk);
|
|
229
237
|
process.stdin.on('end', () => {
|
|
@@ -234,17 +242,17 @@ process.stdin.on('end', () => {
|
|
|
234
242
|
// A null/primitive payload has nothing to guard — exit deliberately
|
|
235
243
|
// rather than throwing into the fail-open catch below (#2595 class).
|
|
236
244
|
if (data === null || typeof data !== 'object') {
|
|
237
|
-
|
|
245
|
+
allow(undefined);
|
|
238
246
|
}
|
|
239
247
|
|
|
240
248
|
// Only whole-file Write is catastrophic-by-construction; Edit/MultiEdit
|
|
241
249
|
// replace bounded spans and are out of scope by design (#2255).
|
|
242
250
|
if (data.tool_name !== 'Write') {
|
|
243
|
-
|
|
251
|
+
allow(undefined);
|
|
244
252
|
}
|
|
245
253
|
|
|
246
254
|
if (isOverrideSet()) {
|
|
247
|
-
|
|
255
|
+
allow(undefined); // documented escape hatch — legitimate reset in progress
|
|
248
256
|
}
|
|
249
257
|
|
|
250
258
|
// Typed read (#2547 class): `[]`/`{}` are truthy, pass a `!value`
|
|
@@ -254,7 +262,7 @@ process.stdin.on('end', () => {
|
|
|
254
262
|
const rawFilePath = typeof rawInput?.file_path === 'string' ? rawInput.file_path : '';
|
|
255
263
|
const content = rawInput?.content;
|
|
256
264
|
if (!rawFilePath || typeof content !== 'string') {
|
|
257
|
-
|
|
265
|
+
allow(undefined);
|
|
258
266
|
}
|
|
259
267
|
|
|
260
268
|
// Resolve relative paths against the session cwd (the same base the
|
|
@@ -272,7 +280,7 @@ process.stdin.on('end', () => {
|
|
|
272
280
|
const normalized = filePath.replace(/\\/g, '/');
|
|
273
281
|
|
|
274
282
|
if (!CURATED_PATTERNS.some(re => re.test(normalized))) {
|
|
275
|
-
|
|
283
|
+
allow(undefined); // not a curated planning artifact
|
|
276
284
|
}
|
|
277
285
|
|
|
278
286
|
// Only guard overwrites — creating a curated file fresh is fine.
|
|
@@ -285,7 +293,7 @@ process.stdin.on('end', () => {
|
|
|
285
293
|
onDisk = fs.readFileSync(filePath, 'utf8');
|
|
286
294
|
} catch (err) {
|
|
287
295
|
if (err && err.code === 'ENOENT') {
|
|
288
|
-
|
|
296
|
+
allow(undefined); // does not exist — new-file Write, nothing to clobber
|
|
289
297
|
}
|
|
290
298
|
emitBlock({
|
|
291
299
|
decision: 'block',
|
|
@@ -306,17 +314,17 @@ process.stdin.on('end', () => {
|
|
|
306
314
|
const newLines = countLines(content);
|
|
307
315
|
|
|
308
316
|
if (oldLines < FLOOR_LINES) {
|
|
309
|
-
|
|
317
|
+
allow(undefined); // sub-floor stub — ratio checks are meaningless here
|
|
310
318
|
}
|
|
311
319
|
|
|
312
320
|
if (newLines >= oldLines * SHRINK_RATIO) {
|
|
313
|
-
|
|
321
|
+
allow(undefined); // shrink (if any) is within tolerance
|
|
314
322
|
}
|
|
315
323
|
|
|
316
324
|
// The mechanical hatch for workflow steps (see header): consulted only
|
|
317
325
|
// here, at the block point, so a within-tolerance write never burns it.
|
|
318
326
|
if (consumeSentinelFor(filePath, normalized)) {
|
|
319
|
-
|
|
327
|
+
allow(undefined); // armed for exactly this file, fresh, now consumed
|
|
320
328
|
}
|
|
321
329
|
|
|
322
330
|
const pct = Math.round((newLines / oldLines) * 100);
|
|
@@ -353,7 +361,9 @@ process.stdin.on('end', () => {
|
|
|
353
361
|
`to bypass this guard once.`,
|
|
354
362
|
});
|
|
355
363
|
} catch {
|
|
356
|
-
// Silent fail — never block valid tool calls due to hook errors
|
|
357
|
-
|
|
364
|
+
// Silent fail — never block valid tool calls due to hook errors.
|
|
365
|
+
// ON_CRASH is declared ALLOW at module top: this preserves today's
|
|
366
|
+
// exit(0) fail-open behavior exactly (#3911).
|
|
367
|
+
crash(ON_CRASH, undefined);
|
|
358
368
|
}
|
|
359
369
|
});
|