session-orchestrator 5.1.0 → 5.3.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/.agents/skills/architecture/SKILL.md +3 -1
- package/.agents/skills/autopilot/SKILL.md +6 -1
- package/.agents/skills/autopilot/agents/openai.yaml +5 -0
- package/.agents/skills/bootstrap/SKILL.md +7 -1
- package/.agents/skills/bootstrap/agents/openai.yaml +5 -0
- package/.agents/skills/brainstorm/SKILL.md +8 -1
- package/.agents/skills/brainstorm/agents/openai.yaml +5 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +3 -1
- package/.agents/skills/close/SKILL.md +21 -0
- package/.agents/skills/close/agents/openai.yaml +5 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +4 -2
- package/.agents/skills/debug/SKILL.md +7 -1
- package/.agents/skills/debug/agents/openai.yaml +5 -0
- package/.agents/skills/discovery/SKILL.md +7 -2
- package/.agents/skills/discovery/agents/openai.yaml +5 -0
- package/.agents/skills/dispatcher/SKILL.md +7 -1
- package/.agents/skills/dispatcher/agents/openai.yaml +5 -0
- package/.agents/skills/docs-orchestrator/SKILL.md +3 -1
- package/.agents/skills/ecosystem-health/SKILL.md +3 -1
- package/.agents/skills/eli5/SKILL.md +7 -1
- package/.agents/skills/eli5/agents/openai.yaml +5 -0
- package/.agents/skills/eval/SKILL.md +7 -2
- package/.agents/skills/eval/agents/openai.yaml +5 -0
- package/.agents/skills/evolve/SKILL.md +8 -3
- package/.agents/skills/evolve/agents/openai.yaml +5 -0
- package/.agents/skills/frontmatter-guard/SKILL.md +3 -1
- package/.agents/skills/gitlab-ops/SKILL.md +3 -1
- package/.agents/skills/gitlab-portfolio/SKILL.md +3 -1
- package/.agents/skills/go/SKILL.md +22 -0
- package/.agents/skills/go/agents/openai.yaml +5 -0
- package/.agents/skills/grill/SKILL.md +7 -1
- package/.agents/skills/grill/agents/openai.yaml +5 -0
- package/.agents/skills/harness-audit/SKILL.md +20 -0
- package/.agents/skills/harness-audit/agents/openai.yaml +5 -0
- package/.agents/skills/hook-development/SKILL.md +3 -1
- package/.agents/skills/mcp-builder/SKILL.md +3 -1
- package/.agents/skills/memory-cleanup/SKILL.md +6 -1
- package/.agents/skills/memory-cleanup/agents/openai.yaml +5 -0
- package/.agents/skills/mode-selector/SKILL.md +3 -1
- package/.agents/skills/npm-publish/SKILL.md +4 -2
- package/.agents/skills/peekaboo-driver/SKILL.md +3 -1
- package/.agents/skills/persona-panel/SKILL.md +6 -1
- package/.agents/skills/persona-panel/agents/openai.yaml +5 -0
- package/.agents/skills/plan/SKILL.md +8 -2
- package/.agents/skills/plan/agents/openai.yaml +5 -0
- package/.agents/skills/playwright-driver/SKILL.md +3 -1
- package/.agents/skills/portfolio/SKILL.md +21 -0
- package/.agents/skills/portfolio/agents/openai.yaml +5 -0
- package/.agents/skills/quality-gates/SKILL.md +3 -1
- package/.agents/skills/reconcile/SKILL.md +6 -1
- package/.agents/skills/reconcile/agents/openai.yaml +5 -0
- package/.agents/skills/release/SKILL.md +22 -0
- package/.agents/skills/release/agents/openai.yaml +5 -0
- package/.agents/skills/remote-offload/SKILL.md +3 -1
- package/.agents/skills/repo-audit/SKILL.md +6 -1
- package/.agents/skills/repo-audit/agents/openai.yaml +5 -0
- package/.agents/skills/session/SKILL.md +21 -0
- package/.agents/skills/session/agents/openai.yaml +5 -0
- package/.agents/skills/session-end/SKILL.md +3 -1
- package/.agents/skills/session-plan/SKILL.md +3 -1
- package/.agents/skills/session-start/SKILL.md +3 -1
- package/.agents/skills/spinout/SKILL.md +6 -1
- package/.agents/skills/spinout/agents/openai.yaml +5 -0
- package/.agents/skills/sunset-review/SKILL.md +7 -1
- package/.agents/skills/sunset-review/agents/openai.yaml +5 -0
- package/.agents/skills/templates-ack/SKILL.md +21 -0
- package/.agents/skills/templates-ack/agents/openai.yaml +5 -0
- package/.agents/skills/test/SKILL.md +21 -0
- package/.agents/skills/test/agents/openai.yaml +5 -0
- package/.agents/skills/test-runner/SKILL.md +3 -1
- package/.agents/skills/tmux-layout/SKILL.md +3 -1
- package/.agents/skills/using-orchestrator/SKILL.md +3 -1
- package/.agents/skills/ux-grill/SKILL.md +7 -1
- package/.agents/skills/ux-grill/agents/openai.yaml +5 -0
- package/.agents/skills/vault-mirror/SKILL.md +3 -1
- package/.agents/skills/vault-sync/SKILL.md +3 -1
- package/.agents/skills/wave-executor/SKILL.md +3 -1
- package/.agents/skills/write-executable-plan/SKILL.md +3 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +4 -4
- package/.codex-plugin/skills/autopilot/SKILL.md +5 -4
- package/.codex-plugin/skills/bootstrap/SKILL.md +8 -4
- package/.codex-plugin/skills/brainstorm/SKILL.md +11 -4
- package/.codex-plugin/skills/close/SKILL.md +3 -3
- package/.codex-plugin/skills/convergence-monitoring/SKILL.md +1 -1
- package/.codex-plugin/skills/debug/SKILL.md +11 -4
- package/.codex-plugin/skills/discovery/SKILL.md +8 -4
- package/.codex-plugin/skills/dispatcher/SKILL.md +4 -4
- package/.codex-plugin/skills/eli5/SKILL.md +9 -4
- package/.codex-plugin/skills/eval/SKILL.md +9 -4
- package/.codex-plugin/skills/evolve/SKILL.md +9 -4
- package/.codex-plugin/skills/go/SKILL.md +3 -3
- package/.codex-plugin/skills/grill/SKILL.md +11 -4
- package/.codex-plugin/skills/harness-audit/SKILL.md +4 -3
- package/.codex-plugin/skills/memory-cleanup/SKILL.md +9 -4
- package/.codex-plugin/skills/npm-publish/SKILL.md +1 -1
- package/.codex-plugin/skills/persona-panel/SKILL.md +5 -5
- package/.codex-plugin/skills/plan/SKILL.md +8 -4
- package/.codex-plugin/skills/portfolio/SKILL.md +3 -3
- package/.codex-plugin/skills/reconcile/SKILL.md +9 -4
- package/.codex-plugin/skills/release/SKILL.md +3 -3
- package/.codex-plugin/skills/repo-audit/SKILL.md +6 -4
- package/.codex-plugin/skills/session/SKILL.md +1 -1
- package/.codex-plugin/skills/spinout/SKILL.md +4 -4
- package/.codex-plugin/skills/sunset-review/SKILL.md +5 -4
- package/.codex-plugin/skills/test/SKILL.md +3 -3
- package/.codex-plugin/skills/ux-grill/SKILL.md +11 -4
- package/.cursor/commands/autopilot.md +4 -4
- package/.cursor/commands/bootstrap.md +5 -4
- package/.cursor/commands/brainstorm.md +5 -4
- package/.cursor/commands/close.md +4 -3
- package/.cursor/commands/debug.md +4 -4
- package/.cursor/commands/discovery.md +4 -4
- package/.cursor/commands/dispatcher.md +4 -4
- package/.cursor/commands/eli5.md +4 -4
- package/.cursor/commands/eval.md +4 -4
- package/.cursor/commands/evolve.md +4 -4
- package/.cursor/commands/go.md +4 -3
- package/.cursor/commands/grill.md +4 -4
- package/.cursor/commands/harness-audit.md +3 -3
- package/.cursor/commands/memory-cleanup.md +4 -4
- package/.cursor/commands/persona-panel.md +4 -4
- package/.cursor/commands/plan.md +5 -4
- package/.cursor/commands/portfolio.md +3 -3
- package/.cursor/commands/reconcile.md +4 -4
- package/.cursor/commands/release.md +4 -3
- package/.cursor/commands/repo-audit.md +4 -4
- package/.cursor/commands/session.md +1 -1
- package/.cursor/commands/spinout.md +4 -4
- package/.cursor/commands/sunset-review.md +4 -4
- package/.cursor/commands/test.md +3 -3
- package/.cursor/commands/ux-grill.md +4 -4
- package/.cursor/rules/000-session-orchestrator.mdc +0 -2
- package/.cursor/rules/010-session-workflow.mdc +2 -2
- package/.cursor/rules/050-plan.mdc +1 -1
- package/.cursor/skills/bootstrap/SKILL.md +1 -0
- package/.cursor/skills/close/SKILL.md +13 -0
- package/.cursor/skills/convergence-monitoring/SKILL.md +1 -0
- package/.cursor/skills/debug/SKILL.md +0 -1
- package/.cursor/skills/discovery/SKILL.md +0 -1
- package/.cursor/skills/dispatcher/SKILL.md +0 -1
- package/.cursor/skills/eli5/SKILL.md +0 -1
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/evolve/SKILL.md +0 -1
- package/.cursor/skills/go/SKILL.md +13 -0
- package/.cursor/skills/grill/SKILL.md +0 -1
- package/.cursor/skills/harness-audit/SKILL.md +12 -0
- package/.cursor/skills/npm-publish/SKILL.md +1 -0
- package/.cursor/skills/portfolio/SKILL.md +12 -0
- package/.cursor/skills/release/SKILL.md +13 -0
- package/.cursor/skills/repo-audit/SKILL.md +0 -1
- package/.cursor/skills/sunset-review/SKILL.md +0 -1
- package/.cursor/skills/test/SKILL.md +12 -0
- package/.cursor/skills/ux-grill/SKILL.md +0 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.orchestrator/policy/blocked-commands.json +13 -4
- package/AGENTS.md +3 -2
- package/CHANGELOG.md +197 -0
- package/README.md +11 -9
- package/SECURITY.md +12 -0
- package/agents/dialectic-deriver.md +13 -10
- package/agents/eval-judge.md +67 -45
- package/agents/skill-applied-judge.md +34 -19
- package/commands/session.md +17 -3
- package/docs/baseline.md +12 -6
- package/docs/ci-setup.md +53 -0
- package/docs/codex-setup.md +15 -3
- package/docs/components.md +13 -6
- package/docs/events-schema.md +59 -9
- package/docs/install.md +16 -0
- package/docs/persona-panel.md +1 -1
- package/docs/pi-setup.md +1 -1
- package/docs/rule-authoring.md +135 -14
- package/docs/scope-collision-guard.md +2 -0
- package/docs/session-config-reference.md +106 -11
- package/docs/session-config-template.md +31 -2
- package/docs/telemetry.md +2 -0
- package/hooks/_lib/hook-import-set.json +125 -8
- package/hooks/_lib/subagent-paths.mjs +15 -0
- package/hooks/_lib/subagent-transcript.mjs +582 -31
- package/hooks/_lib/vcs-create-matcher.mjs +217 -62
- package/hooks/config-protection.mjs +11 -3
- package/hooks/cwd-change-restore.mjs +11 -3
- package/hooks/enforce-commands.mjs +70 -23
- package/hooks/enforce-scope.mjs +143 -33
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +1 -1
- package/hooks/loop-guard.mjs +11 -3
- package/hooks/on-session-end.mjs +72 -25
- package/hooks/on-session-start.mjs +48 -11
- package/hooks/on-stop.mjs +211 -23
- package/hooks/operator-steer.mjs +11 -3
- package/hooks/post-bash-issue-budget-refund.mjs +18 -8
- package/hooks/post-bash-write-verify.mjs +6 -2
- package/hooks/post-edit-import-probe.mjs +17 -9
- package/hooks/post-edit-validate.mjs +13 -5
- package/hooks/post-subagent-discovery-validator.mjs +98 -13
- package/hooks/post-tool-batch-wave-signal.mjs +200 -38
- package/hooks/post-tool-failure-corrective-context.mjs +11 -5
- package/hooks/post-tooluse-frontend-slop.mjs +10 -4
- package/hooks/pre-auq-clarity.mjs +18 -2
- package/hooks/pre-bash-destructive-guard.mjs +80 -9
- package/hooks/pre-bash-issue-budget.mjs +119 -28
- package/hooks/pre-bash-memory-propose-audit.mjs +86 -54
- package/hooks/pre-bash-sessions-ledger-guard.mjs +391 -20
- package/hooks/pre-bash-staging-fence.mjs +335 -31
- package/hooks/pre-bash-templates-first.mjs +19 -14
- package/hooks/pre-task-scope-disjoint.mjs +385 -5
- package/hooks/skill-invocation-telemetry.mjs +2 -1
- package/hooks/subagent-telemetry.mjs +15 -19
- package/hooks/wave-scope-commit-guard.mjs +197 -100
- package/monitors/monitors.json +1 -1
- package/output-styles/wave-summary.md +1 -1
- package/package.json +2 -1
- package/pi/prompts/autopilot.md +3 -3
- package/pi/prompts/bootstrap.md +3 -3
- package/pi/prompts/brainstorm.md +3 -3
- package/pi/prompts/close.md +2 -2
- package/pi/prompts/debug.md +3 -3
- package/pi/prompts/discovery.md +3 -3
- package/pi/prompts/dispatcher.md +3 -3
- package/pi/prompts/eli5.md +3 -3
- package/pi/prompts/eval.md +3 -3
- package/pi/prompts/evolve.md +3 -3
- package/pi/prompts/go.md +2 -2
- package/pi/prompts/grill.md +3 -3
- package/pi/prompts/harness-audit.md +2 -3
- package/pi/prompts/memory-cleanup.md +3 -3
- package/pi/prompts/persona-panel.md +3 -3
- package/pi/prompts/plan.md +3 -3
- package/pi/prompts/portfolio.md +2 -2
- package/pi/prompts/reconcile.md +3 -3
- package/pi/prompts/release.md +3 -3
- package/pi/prompts/repo-audit.md +3 -4
- package/pi/prompts/session.md +2 -2
- package/pi/prompts/spinout.md +3 -3
- package/pi/prompts/sunset-review.md +3 -3
- package/pi/prompts/templates-ack.md +1 -1
- package/pi/prompts/test.md +3 -3
- package/pi/prompts/ux-grill.md +3 -3
- package/rules/README.md +1 -1
- package/rules/opt-in-domain/prompt-caching.md +1 -1
- package/rules/opt-in-stack/backend-data.md +1 -1
- package/rules/opt-in-stack/backend.md +3 -3
- package/rules/opt-in-stack/frontend.md +1 -1
- package/rules/opt-in-stack/security-web.md +3 -3
- package/rules/opt-in-stack/swift.md +1 -1
- package/scripts/archive-closed-prds.mjs +2 -2
- package/scripts/auq-audit.mjs +2 -3
- package/scripts/autopilot.mjs +23 -2
- package/scripts/backfill-abandoned-sessions.mjs +171 -15
- package/scripts/backfill-evidence-digest.mjs +2 -1
- package/scripts/backfill-learnings-from-vault.mjs +2 -2
- package/scripts/check-package-manager.mjs +2 -2
- package/scripts/check-sessions-integrity.mjs +300 -0
- package/scripts/ci/assert-vitest-green.mjs +2 -1
- package/scripts/dialectic-deriver.mjs +50 -13
- package/scripts/emit-session.mjs +77 -32
- package/scripts/eval-session.mjs +65 -3
- package/scripts/export-hw-learnings.mjs +2 -1
- package/scripts/express-path.mjs +1 -1
- package/scripts/gc-stale-worktrees.mjs +2 -1
- package/scripts/generate-agents-skills.mjs +102 -29
- package/scripts/generate-codex-skills.mjs +48 -4
- package/scripts/generate-cursor-adapter.mjs +220 -11
- package/scripts/generate-hook-import-set.mjs +12 -27
- package/scripts/generate-pi-prompts.mjs +183 -13
- package/scripts/github-protection-audit.mjs +2 -3
- package/scripts/lib/agent-frontmatter.mjs +23 -1
- package/scripts/lib/agent-status.mjs +2 -31
- package/scripts/lib/auq/clarity.mjs +10 -2
- package/scripts/lib/auq/parse.mjs +12 -31
- package/scripts/lib/auq/schema.mjs +56 -41
- package/scripts/lib/auto-dialectic.mjs +304 -15
- package/scripts/lib/autopilot/flags.mjs +12 -1
- package/scripts/lib/autopilot/kill-switches.mjs +6 -3
- package/scripts/lib/autopilot/loop.mjs +14 -1
- package/scripts/lib/autopilot/stall-sampler.mjs +80 -23
- package/scripts/lib/ci-status-banner.mjs +376 -16
- package/scripts/lib/claude-md-budget-lint.mjs +2 -5
- package/scripts/lib/command-blocker.mjs +408 -33
- package/scripts/lib/config/dialectic.mjs +12 -3
- package/scripts/lib/config/drift-check.mjs +19 -0
- package/scripts/lib/config/gate.mjs +74 -0
- package/scripts/lib/config/reaper.mjs +162 -0
- package/scripts/lib/config.mjs +14 -0
- package/scripts/lib/convergence-monitor.mjs +76 -13
- package/scripts/lib/cursor-hook-bridge.mjs +2 -2
- package/scripts/lib/description-surface.mjs +2 -5
- package/scripts/lib/dispatcher/cli.mjs +2 -1
- package/scripts/lib/ecosystem-health.mjs +11 -0
- package/scripts/lib/ecosystem-wizard.mjs +2 -1
- package/scripts/lib/eval/engine.mjs +421 -53
- package/scripts/lib/eval/judge.mjs +463 -40
- package/scripts/lib/eval/schema.mjs +10 -1
- package/scripts/lib/events-rotation.mjs +221 -25
- package/scripts/lib/events-schema.mjs +114 -0
- package/scripts/lib/events.mjs +524 -5
- package/scripts/lib/fetch-baseline.mjs +3 -8
- package/scripts/lib/frontmatter-guard.mjs +21 -10
- package/scripts/lib/gates/gate-baseline.mjs +27 -2
- package/scripts/lib/gates/gate-full.mjs +28 -3
- package/scripts/lib/gates/gate-helpers.mjs +243 -21
- package/scripts/lib/gates/gate-incremental.mjs +28 -3
- package/scripts/lib/gates/gate-per-file.mjs +27 -2
- package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +2 -1
- package/scripts/lib/gitlab-portfolio/cli.mjs +2 -1
- package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +6 -1
- package/scripts/lib/instruction-budget-guard.mjs +332 -50
- package/scripts/lib/io.mjs +42 -8
- package/scripts/lib/is-main-module.mjs +82 -0
- package/scripts/lib/issue-close-strip-labels.mjs +207 -49
- package/scripts/lib/js-mask.mjs +197 -0
- package/scripts/lib/learnings/evolve-telemetry.mjs +11 -7
- package/scripts/lib/locks/index.mjs +32 -25
- package/scripts/lib/maintenance-due-banner.mjs +122 -91
- package/scripts/lib/orphan-reaper.mjs +1588 -0
- package/scripts/lib/peer-cards/merger.mjs +48 -10
- package/scripts/lib/peer-cards/reader.mjs +78 -2
- package/scripts/lib/peer-discovery.mjs +2 -5
- package/scripts/lib/playwright-driver/runner.mjs +2 -1
- package/scripts/lib/process-group.mjs +899 -0
- package/scripts/lib/quality-gate.mjs +107 -28
- package/scripts/lib/reconcile/backlog.mjs +368 -0
- package/scripts/lib/reconcile/engine.mjs +55 -188
- package/scripts/lib/reconcile/rule-expiry-sweep.mjs +884 -0
- package/scripts/lib/reconcile/sanitize.mjs +69 -3
- package/scripts/lib/reconcile-nudge-banner.mjs +138 -45
- package/scripts/lib/resource-probe/parsers.mjs +31 -0
- package/scripts/lib/rule-loader.mjs +41 -12
- package/scripts/lib/rules-sync.mjs +2 -5
- package/scripts/lib/scope-echo.mjs +429 -7
- package/scripts/lib/scope-gate.mjs +605 -1
- package/scripts/lib/session-close-backfill.mjs +91 -12
- package/scripts/lib/session-id.mjs +9 -20
- package/scripts/lib/session-invocation.mjs +20 -0
- package/scripts/lib/session-schema/constants.mjs +30 -2
- package/scripts/lib/session-schema/normalizer.mjs +56 -4
- package/scripts/lib/session-schema.mjs +8 -3
- package/scripts/lib/session-start-probes.mjs +95 -10
- package/scripts/lib/sessions-canonical.mjs +23 -0
- package/scripts/lib/sessions-integrity-banner.mjs +7 -1
- package/scripts/lib/sessions-staleness-banner.mjs +193 -51
- package/scripts/lib/skill-evidence-window.mjs +891 -0
- package/scripts/lib/skill-evolution/candidate-intake.mjs +133 -12
- package/scripts/lib/skill-evolution/engine.mjs +18 -9
- package/scripts/lib/skill-judge.mjs +45 -3
- package/scripts/lib/state-md.mjs +84 -3
- package/scripts/lib/sunset/walker.mjs +31 -4
- package/scripts/lib/tail-window.mjs +56 -0
- package/scripts/lib/telemetry/schema.mjs +30 -0
- package/scripts/lib/telemetry/sync.mjs +61 -6
- package/scripts/lib/telemetry-flush-health-banner.mjs +4 -22
- package/scripts/lib/test-runner/issue-reconcile.mjs +48 -16
- package/scripts/lib/tests-src-ratio.mjs +2 -6
- package/scripts/lib/tmux-layout/telemetry-stats.mjs +74 -14
- package/scripts/lib/user-invocable-skills.mjs +205 -0
- package/scripts/lib/ux-grill/reconcile.mjs +48 -22
- package/scripts/lib/validate/check-agents-skills.mjs +26 -15
- package/scripts/lib/validate/check-banner-parity.mjs +2 -2
- package/scripts/lib/validate/check-cursor-adapter.mjs +3 -2
- package/scripts/lib/validate/check-dead-bridge.mjs +2 -2
- package/scripts/lib/validate/check-doc-cli-commands.mjs +2 -2
- package/scripts/lib/validate/check-entry-guard.mjs +329 -0
- package/scripts/lib/validate/check-guard-requires-parity.mjs +2 -2
- package/scripts/lib/validate/check-hook-entry-guards.mjs +636 -0
- package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +2 -2
- package/scripts/lib/validate/check-learning-provenance.mjs +2 -2
- package/scripts/lib/validate/check-pi-prompts.mjs +1 -0
- package/scripts/lib/validate/check-rules.mjs +7 -5
- package/scripts/lib/validate/check-skill-links.mjs +35 -6
- package/scripts/lib/validate/check-skill-script-paths.mjs +241 -29
- package/scripts/lib/validate/check-test-git-config-target.mjs +26 -36
- package/scripts/lib/validate/check-unicode-safety.mjs +2 -2
- package/scripts/lib/validate/check-untracked-test-deps.mjs +9 -104
- package/scripts/lib/validate/check-unwired-features.mjs +220 -33
- package/scripts/lib/validate/check-validator-registration.mjs +36 -12
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +2 -2
- package/scripts/lib/validate/confidential-names.mjs +10 -0
- package/scripts/lib/validate-vendored-rules.mjs +39 -12
- package/scripts/lib/vault-mirror/namespace.mjs +46 -8
- package/scripts/lib/vault-mirror/process.mjs +10 -3
- package/scripts/lib/vault-mirror/render-sessions.mjs +12 -2
- package/scripts/lib/vault-status/narrative-mirror.mjs +31 -7
- package/scripts/lib/vault-yaml.mjs +118 -0
- package/scripts/lib/wave-transcript-tail.mjs +2 -2
- package/scripts/lib/worktree/lifecycle.mjs +153 -1
- package/scripts/lock-reaper.mjs +2 -1
- package/scripts/materialize-wave-scope.mjs +87 -4
- package/scripts/migrate-sessions-jsonl.mjs +2 -1
- package/scripts/migrate-vault-paths.mjs +2 -3
- package/scripts/release-session-lock.mjs +305 -0
- package/scripts/release.mjs +109 -39
- package/scripts/relocate-vault-corpus.mjs +2 -3
- package/scripts/repair-invalid-sessions.mjs +2 -2
- package/scripts/resolve-session-invocation.mjs +59 -0
- package/scripts/run-quality-gate.mjs +156 -17
- package/scripts/session-shape.mjs +2 -2
- package/scripts/site-numbers.mjs +35 -11
- package/scripts/sweep-expired-rules.mjs +227 -0
- package/scripts/validate-plugin.mjs +21 -0
- package/scripts/validate-wave-scope.mjs +32 -105
- package/scripts/vault-consolidate.mjs +2 -2
- package/scripts/vault-mirror.mjs +11 -4
- package/scripts/wave-scope-binding.mjs +2 -3
- package/skills/_shared/bootstrap-gate.md +1 -1
- package/skills/_shared/monitor-patterns.md +1 -1
- package/skills/_shared/platform-tools.md +23 -11
- package/skills/_shared/research-evidence.md +53 -0
- package/skills/_shared/state-ownership.md +3 -0
- package/skills/autopilot/SKILL.md +80 -11
- package/skills/bootstrap/SKILL.md +51 -1
- package/skills/brainstorm/SKILL.md +16 -0
- package/skills/claude-md-drift-check/SKILL.md +1 -1
- package/skills/claude-md-drift-check/checker.mjs +49 -11
- package/{commands/close.md → skills/close/SKILL.md} +9 -3
- package/skills/convergence-monitoring/README.md +8 -1
- package/skills/convergence-monitoring/SIGNALS.md +50 -6
- package/skills/convergence-monitoring/SKILL.md +15 -6
- package/skills/debug/SKILL.md +10 -0
- package/skills/discovery/SKILL.md +24 -1
- package/skills/discovery/probes-session.md +2 -2
- package/skills/dispatcher/SKILL.md +38 -7
- package/skills/eli5/SKILL.md +11 -0
- package/skills/eval/SKILL.md +52 -23
- package/skills/eval/rubric-v1.md +1 -0
- package/skills/eval/rubric-v2.md +457 -0
- package/skills/evolve/SKILL.md +9 -2
- package/skills/evolve/references/evolve-dialectic-mode.md +46 -25
- package/skills/gitlab-ops/SKILL.md +3 -2
- package/{commands/go.md → skills/go/SKILL.md} +9 -1
- package/skills/grill/SKILL.md +19 -0
- package/{commands/harness-audit.md → skills/harness-audit/SKILL.md} +7 -2
- package/skills/hook-development/SKILL.md +46 -41
- package/skills/memory-cleanup/SKILL.md +7 -0
- package/skills/npm-publish/SKILL.md +2 -2
- package/skills/persona-panel/SKILL.md +56 -1
- package/skills/persona-panel/persona-format.md +1 -1
- package/skills/plan/SKILL.md +28 -1
- package/{commands/portfolio.md → skills/portfolio/SKILL.md} +8 -2
- package/skills/reconcile/SKILL.md +21 -0
- package/{commands/release.md → skills/release/SKILL.md} +16 -2
- package/skills/repo-audit/SKILL.md +7 -0
- package/skills/session-end/SKILL.md +13 -16
- package/skills/session-end/discovery-scan.md +1 -1
- package/skills/session-end/phase-3-6-tail.md +55 -9
- package/skills/session-end/plan-verification.md +2 -2
- package/skills/session-end/references/phase-5-issue-cleanup.md +9 -14
- package/skills/session-end/session-metrics-write.md +10 -0
- package/skills/session-plan/SKILL.md +18 -6
- package/skills/session-plan/references/session-plan-task-classification.md +2 -2
- package/skills/session-start/SKILL.md +5 -4
- package/skills/session-start/phase-8-5-express-path.md +6 -6
- package/skills/session-start/references/phase-1-5-session-continuity.md +1 -1
- package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +1 -1
- package/skills/session-start/references/phase-4-ssot-environment-check.md +6 -4
- package/skills/spinout/SKILL.md +12 -1
- package/skills/sunset-review/SKILL.md +13 -0
- package/{commands/test.md → skills/test/SKILL.md} +10 -4
- package/skills/ux-grill/SKILL.md +20 -2
- package/skills/wave-executor/SKILL.md +14 -7
- package/skills/wave-executor/circuit-breaker.md +2 -0
- package/skills/wave-executor/references/wave-executor-state-init.md +18 -4
- package/skills/wave-executor/references/wave-loop-dispatch.md +5 -2
- package/skills/wave-executor/references/wave-loop-review.md +17 -1
- package/commands/autopilot.md +0 -80
- package/commands/bootstrap.md +0 -56
- package/commands/brainstorm.md +0 -48
- package/commands/debug.md +0 -36
- package/commands/discovery.md +0 -32
- package/commands/dispatcher.md +0 -59
- package/commands/eli5.md +0 -33
- package/commands/eval.md +0 -28
- package/commands/evolve.md +0 -10
- package/commands/grill.md +0 -45
- package/commands/memory-cleanup.md +0 -26
- package/commands/persona-panel.md +0 -121
- package/commands/plan.md +0 -15
- package/commands/reconcile.md +0 -23
- package/commands/repo-audit.md +0 -24
- package/commands/spinout.md +0 -15
- package/commands/sunset-review.md +0 -27
- package/commands/ux-grill.md +0 -51
|
@@ -96,28 +96,25 @@
|
|
|
96
96
|
* triage of a per-session counter file
|
|
97
97
|
* (`.orchestrator/runtime/issue-budget/<hash>.json`, #1141) or in a
|
|
98
98
|
* transcript census of real create calls.
|
|
99
|
-
* - An `xargs`-driven create
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* xargs -
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* `hooks/pre-bash-
|
|
116
|
-
* `
|
|
117
|
-
*
|
|
118
|
-
* Pinned by `tests/hooks/vcs-create-matcher.test.mjs`. Revisit when an
|
|
119
|
-
* `xargs`-driven create shows up in a transcript census or in the overflow
|
|
120
|
-
* triage of a per-session counter file.
|
|
99
|
+
* - An `xargs`-driven create IS matched since #1289 — but LOCALLY, in
|
|
100
|
+
* {@link xargsDrivenRemainder}, never by changing what `resolveSegmentVerb`
|
|
101
|
+
* reports for `xargs`. That resolver still answers `verb: 'xargs'`, because
|
|
102
|
+
* `command-blocker.mjs` classes `xargs` as an INTERPRETER
|
|
103
|
+
* (`SHELL_EXEC_INTERPRETERS`) and unwrapping it there would LOOSEN the
|
|
104
|
+
* destructive-command guard the five positional consumers of that resolver
|
|
105
|
+
* share. Measured 2026-09-16 against this file — the four shapes that
|
|
106
|
+
* yielded 0 statements before now yield 1 each, flagged `bulk: true`:
|
|
107
|
+
*
|
|
108
|
+
* xargs glab issue create --title X → 1
|
|
109
|
+
* echo X | xargs -I% glab issue create --title % → 1
|
|
110
|
+
* seq 1 50 | xargs -I% gh api -X POST …/issues -f title=% → 1
|
|
111
|
+
* xargs -n1 glab issue create → 1
|
|
112
|
+
*
|
|
113
|
+
* A `bulk` statement is never CHARGED: the multiplicity comes from the
|
|
114
|
+
* stdin word list and is unknowable before the shell runs, so
|
|
115
|
+
* `hooks/pre-bash-issue-budget.mjs` treats it exactly like a loop body —
|
|
116
|
+
* deny in `strict`, report the undercount in `warn`. The remaining ceiling
|
|
117
|
+
* is the option grammar, named at {@link xargsDrivenRemainder}.
|
|
121
118
|
* - A paren glued to the verb (`(glab issue create …)`) is not reached: it
|
|
122
119
|
* lexes as the single word `(glab`. This is `command-blocker.mjs`'s own
|
|
123
120
|
* named ceiling on `COMMAND_POSITION_KEYWORDS` (#1145), inherited here
|
|
@@ -164,7 +161,14 @@ export const CREATE_REGEX = /^\s*(gh|glab)\s+(pr|mr|issue)\s+(create|new)\b/;
|
|
|
164
161
|
function statementsOf(command) {
|
|
165
162
|
if (typeof command !== 'string' || command.length === 0) return [];
|
|
166
163
|
try {
|
|
167
|
-
|
|
164
|
+
const segments = splitChainSegments(tokenizeCommand(command));
|
|
165
|
+
const out = [];
|
|
166
|
+
for (const segment of segments) {
|
|
167
|
+
out.push(segment);
|
|
168
|
+
const rest = xargsDrivenRemainder(segment);
|
|
169
|
+
if (rest !== null) out.push(rest);
|
|
170
|
+
}
|
|
171
|
+
return out;
|
|
168
172
|
} catch {
|
|
169
173
|
// Fail OPEN, matching every other error path in the two consuming hooks:
|
|
170
174
|
// a lexer that throws must not wedge every Bash call. Worst case is a
|
|
@@ -173,6 +177,64 @@ function statementsOf(command) {
|
|
|
173
177
|
}
|
|
174
178
|
}
|
|
175
179
|
|
|
180
|
+
/**
|
|
181
|
+
* xargs' own options, split by whether they consume a SEPARATE next token.
|
|
182
|
+
* Both the separated (`-n 1`) and the attached (`-n1`, `-I%`) spellings are
|
|
183
|
+
* handled — the attached form is what agents actually write.
|
|
184
|
+
*/
|
|
185
|
+
const XARGS_VALUE_FLAGS = new Set(['-I', '-i', '-n', '-P', '-L', '-s', '-E', '-d', '-a']);
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* The UTILITY xargs drives, as an additional statement — or `null` when the
|
|
189
|
+
* segment is not an `xargs` call, or nothing follows its options (#1289).
|
|
190
|
+
*
|
|
191
|
+
* ## Why here and not in the shared lexer
|
|
192
|
+
*
|
|
193
|
+
* `command-blocker.mjs` classes `xargs` as an INTERPRETER, deliberately NOT a
|
|
194
|
+
* transparent wrapper: unwrapping it there would resolve past it for the FIVE
|
|
195
|
+
* positional consumers of `resolveSegmentVerb` and LOOSEN the destructive-command
|
|
196
|
+
* guard. So the widening stays local to this matcher, where the only question is
|
|
197
|
+
* "does this command file issues?" — the shared verb resolution is untouched,
|
|
198
|
+
* and `resolveSegmentVerb` still reports `verb: 'xargs'`.
|
|
199
|
+
*
|
|
200
|
+
* The returned array carries a non-index `xargsDriven` marker so
|
|
201
|
+
* {@link findIssueCreateStatements} can flag the statement `bulk: true`: the
|
|
202
|
+
* multiplicity is set by the STDIN word list, which does not exist at hook time,
|
|
203
|
+
* so such a create can never be charged — only denied or reported.
|
|
204
|
+
*
|
|
205
|
+
* NAMED CEILING (BV-004): only the option spellings in `XARGS_VALUE_FLAGS` plus
|
|
206
|
+
* bare boolean/unknown short flags are skipped. A SEPARATED long form
|
|
207
|
+
* (`--max-args 5`) leaves its operand in verb position, so the remainder does not
|
|
208
|
+
* match a create shape and the call is missed — the pre-#1289 behaviour, i.e. an
|
|
209
|
+
* under-report, never a false deny. REVISIT TRIGGER: a separated-long-form xargs
|
|
210
|
+
* create in a transcript census.
|
|
211
|
+
*
|
|
212
|
+
* @param {Array<{ text: string, quoted: boolean }>} segment
|
|
213
|
+
* @returns {Array<{ text: string, quoted: boolean }>|null}
|
|
214
|
+
*/
|
|
215
|
+
function xargsDrivenRemainder(segment) {
|
|
216
|
+
if (segment.length === 0) return null;
|
|
217
|
+
const head = segment[0];
|
|
218
|
+
if (head.quoted || head.text.replace(/^.*\//, '') !== 'xargs') return null;
|
|
219
|
+
|
|
220
|
+
let i = 1;
|
|
221
|
+
while (i < segment.length) {
|
|
222
|
+
const tok = segment[i];
|
|
223
|
+
if (tok.quoted) break; // a quoted word is the utility, never an option
|
|
224
|
+
const text = tok.text;
|
|
225
|
+
if (text === '--') { i++; break; }
|
|
226
|
+
if (!text.startsWith('-') || text === '-') break;
|
|
227
|
+
if (XARGS_VALUE_FLAGS.has(text)) { i += 2; continue; } // separated: `-n 1`
|
|
228
|
+
if (XARGS_VALUE_FLAGS.has(text.slice(0, 2))) { i += 1; continue; } // attached: `-I%`
|
|
229
|
+
i += 1; // boolean (`-0`, `-r`, `-t`) or an unknown flag
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const rest = segment.slice(i);
|
|
233
|
+
if (rest.length === 0) return null;
|
|
234
|
+
rest.xargsDriven = true;
|
|
235
|
+
return rest;
|
|
236
|
+
}
|
|
237
|
+
|
|
176
238
|
/**
|
|
177
239
|
* `--help` short-circuits a cobra command (both `gh` and `glab` are cobra
|
|
178
240
|
* CLIs): the help text is printed and the command body never runs, so
|
|
@@ -609,13 +671,19 @@ export function findIssueCreateStatements(command) {
|
|
|
609
671
|
continue;
|
|
610
672
|
}
|
|
611
673
|
literalCatPaths ??= singleQuotedCatPaths(command);
|
|
612
|
-
|
|
674
|
+
const statement = {
|
|
613
675
|
shape,
|
|
614
676
|
tokens,
|
|
615
677
|
text: tokens.map((t) => t.text).join(' '),
|
|
616
678
|
...fieldsFromTokens(tokens, shape, literalCatPaths),
|
|
617
679
|
cwdChanged,
|
|
618
|
-
}
|
|
680
|
+
};
|
|
681
|
+
// `bulk` — the create files an UNKNOWN number of issues (#1289). Today the
|
|
682
|
+
// only source is an `xargs`-driven statement, whose multiplicity is the
|
|
683
|
+
// stdin word list; the key is present ONLY when true, so every existing
|
|
684
|
+
// consumer of this shape is unaffected.
|
|
685
|
+
if (tokens.xargsDriven === true) statement.bulk = true;
|
|
686
|
+
out.push(statement);
|
|
619
687
|
}
|
|
620
688
|
return out;
|
|
621
689
|
}
|
|
@@ -689,6 +757,124 @@ export function isIssueCreate(command) {
|
|
|
689
757
|
return matchVcsCreate(command)?.kind === 'issue';
|
|
690
758
|
}
|
|
691
759
|
|
|
760
|
+
/**
|
|
761
|
+
* EVERY issue-create STATEMENT that sits inside a LOOP BODY — one token array
|
|
762
|
+
* per implicated loop, `[]` when none (#1145, #1379).
|
|
763
|
+
*
|
|
764
|
+
* Returning the statements rather than a boolean is what lets the consuming hook
|
|
765
|
+
* bind the CAP EXEMPTION to the statement that CAUSED the bulk classification.
|
|
766
|
+
* Measured 2026-09-16 against `hooks/pre-bash-issue-budget.mjs` before this
|
|
767
|
+
* change, which classified the exemption on `statements[0]`:
|
|
768
|
+
*
|
|
769
|
+
* for i in 1 2 3; do glab issue create --title junk$i; done
|
|
770
|
+
* → DENY
|
|
771
|
+
* glab issue create --title "[Carryover] real"; for i in 1 2 3; do \
|
|
772
|
+
* glab issue create --title junk$i; done
|
|
773
|
+
* → ALLOW
|
|
774
|
+
*
|
|
775
|
+
* The second command files an unknowable number of UNTEMPLATED issues, lifted
|
|
776
|
+
* by an unrelated neighbour's exemption. That is `.claude/rules/guard-design.md`
|
|
777
|
+
* § "Widening a matcher without narrowing its bypass" (#1106 class) in the
|
|
778
|
+
* exemption lane instead of the bypass lane — same shape, same fix: judge the
|
|
779
|
+
* exemption on the statement the gate fired on, never on a sibling.
|
|
780
|
+
*
|
|
781
|
+
* Each element is the segment the loop-depth scan implicated, so the caller can
|
|
782
|
+
* rebuild its classification text exactly as {@link findIssueCreateStatements}
|
|
783
|
+
* does (`tokens.map((t) => t.text).join(' ')`).
|
|
784
|
+
*
|
|
785
|
+
* ## Why ALL of them, not the first (#1379)
|
|
786
|
+
*
|
|
787
|
+
* The 2026-09-16 scan below collected every candidate head but `return`ed on
|
|
788
|
+
* the first one found at `depth > 0`. Since the consuming hook classifies the
|
|
789
|
+
* cap exemption PER bulk statement, reporting one loop for a two-loop command
|
|
790
|
+
* means an EXEMPT first loop is the only loop judged and every later loop goes
|
|
791
|
+
* unexamined. Reproduced 2026-09-17 @ `9e8146b4` against the live hook in a
|
|
792
|
+
* strict-mode throwaway repo:
|
|
793
|
+
*
|
|
794
|
+
* for i in 1 2 3; do glab issue create --label carryover --title x$i; done;
|
|
795
|
+
* for j in 1 2 3; do glab issue create --title junk$j; done
|
|
796
|
+
* → ALLOW, ledger count=1 exempt=1 (must DENY)
|
|
797
|
+
*
|
|
798
|
+
* Same class as the exemption-binding fix this docblock already describes, one
|
|
799
|
+
* level up: judging the exemption on ONE of several bulk statements is the same
|
|
800
|
+
* defect as judging it on a sibling.
|
|
801
|
+
*
|
|
802
|
+
* @param {string} command
|
|
803
|
+
* @returns {Array<Array<{ text: string, quoted: boolean }>>} one entry per loop
|
|
804
|
+
*/
|
|
805
|
+
export function findLoopedIssueCreates(command) {
|
|
806
|
+
if (typeof command !== 'string' || command.length === 0) return [];
|
|
807
|
+
|
|
808
|
+
let tokens;
|
|
809
|
+
let segments;
|
|
810
|
+
try {
|
|
811
|
+
tokens = tokenizeCommand(command);
|
|
812
|
+
segments = splitChainSegments(tokens);
|
|
813
|
+
} catch {
|
|
814
|
+
return []; // fail OPEN, same posture as statementsOf
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// EVERY issue-create statement is a candidate, keyed by its HEAD token's
|
|
818
|
+
// object identity — the position in the raw stream at which the loop depth
|
|
819
|
+
// must be read.
|
|
820
|
+
//
|
|
821
|
+
// Until 2026-09-16 this considered only the FIRST create statement of ANY
|
|
822
|
+
// kind and then checked its `kind`, mirroring the pre-#1163 hook whose G3 was
|
|
823
|
+
// a single `isIssueCreate(command)` boolean. That mirror is obsolete and was
|
|
824
|
+
// a MISS: since #1163 the hook judges EVERY statement via
|
|
825
|
+
// {@link findIssueCreateStatements}, and the old first-only scan reported
|
|
826
|
+
// `false` for any command whose first create sits outside the loop. Measured
|
|
827
|
+
// 2026-09-16 against HEAD:
|
|
828
|
+
//
|
|
829
|
+
// glab issue create --title "[Carryover] real"; for i in 1 2 3; do \
|
|
830
|
+
// glab issue create --title junk$i; done → false (should be true)
|
|
831
|
+
// glab mr create --title m; for i in 1 2; do \
|
|
832
|
+
// glab issue create --title j; done → false (should be true)
|
|
833
|
+
// for i in 1 2 3; do glab issue create --title j; done → true
|
|
834
|
+
//
|
|
835
|
+
// The first shape files an unknowable number of issues and was ALLOWED by the
|
|
836
|
+
// budget gate. Scanning all candidates is the fail-CLOSED direction and no
|
|
837
|
+
// longer disagrees with the hook about which statements exist.
|
|
838
|
+
const createHeads = new Map();
|
|
839
|
+
for (const seg of segments) {
|
|
840
|
+
const shape = matchStatement(seg);
|
|
841
|
+
if (shape && shape.kind === 'issue') createHeads.set(seg[0], seg);
|
|
842
|
+
}
|
|
843
|
+
if (createHeads.size === 0) return [];
|
|
844
|
+
|
|
845
|
+
const kept = new Set(segments.flat());
|
|
846
|
+
const found = [];
|
|
847
|
+
let depth = 0;
|
|
848
|
+
for (const tok of tokens) {
|
|
849
|
+
// A create head at depth 0 is a create AFTER (or BEFORE) a loop, not inside
|
|
850
|
+
// one: `while x; do echo a; done; glab issue create` stays unimplicated.
|
|
851
|
+
if (createHeads.has(tok)) {
|
|
852
|
+
if (depth > 0) found.push(createHeads.get(tok));
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
855
|
+
if (tok.quoted || kept.has(tok)) continue; // an argument, or a separator
|
|
856
|
+
if (tok.text === 'do') depth += 1;
|
|
857
|
+
else if (tok.text === 'done' && depth > 0) depth -= 1;
|
|
858
|
+
}
|
|
859
|
+
return found;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* The FIRST looped issue-create statement, or `null`.
|
|
864
|
+
*
|
|
865
|
+
* @deprecated since #1379 — use {@link findLoopedIssueCreates}, which reports
|
|
866
|
+
* EVERY implicated loop. A caller that classifies anything per loop (a cap
|
|
867
|
+
* exemption, a per-loop deny reason) is WRONG on this API for a command with
|
|
868
|
+
* two loops; kept only because this module has no `exports` map, so every
|
|
869
|
+
* export is a public entrypoint a consumer may already import.
|
|
870
|
+
*
|
|
871
|
+
* @param {string} command
|
|
872
|
+
* @returns {Array<{ text: string, quoted: boolean }>|null}
|
|
873
|
+
*/
|
|
874
|
+
export function findLoopedIssueCreate(command) {
|
|
875
|
+
return findLoopedIssueCreates(command)[0] ?? null;
|
|
876
|
+
}
|
|
877
|
+
|
|
692
878
|
/**
|
|
693
879
|
* True when the matched issue-create statement sits inside a LOOP BODY, i.e.
|
|
694
880
|
* the command creates an unknown number of issues (#1145).
|
|
@@ -730,47 +916,16 @@ export function isIssueCreate(command) {
|
|
|
730
916
|
* to charging 1 for N. The unit test `tests/hooks/vcs-create-matcher.test.mjs`
|
|
731
917
|
* pins the coupling directly. Revisit if that set is ever narrowed.
|
|
732
918
|
*
|
|
919
|
+
* Boolean wrapper over {@link findLoopedIssueCreates} — kept as the named
|
|
920
|
+
* question for callers that only need the FACT (and for the sibling suites that
|
|
921
|
+
* pin it). A caller that must classify the cap exemption asks for the STATEMENT
|
|
922
|
+
* instead; see that function's docblock for why the distinction is load-bearing.
|
|
923
|
+
*
|
|
733
924
|
* @param {string} command
|
|
734
925
|
* @returns {boolean} false when the command has no issue-create statement at all
|
|
735
926
|
*/
|
|
736
927
|
export function isLoopedIssueCreate(command) {
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
let tokens;
|
|
740
|
-
let segments;
|
|
741
|
-
try {
|
|
742
|
-
tokens = tokenizeCommand(command);
|
|
743
|
-
segments = splitChainSegments(tokens);
|
|
744
|
-
} catch {
|
|
745
|
-
return false; // fail OPEN, same posture as statementsOf
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
// The create statement's HEAD token, by object identity — the position in the
|
|
749
|
-
// raw stream at which the loop depth must be read.
|
|
750
|
-
//
|
|
751
|
-
// Deliberately the FIRST create statement of any kind, then a kind check —
|
|
752
|
-
// exactly what isIssueCreate() does via matchVcsCreate(). Searching for the
|
|
753
|
-
// first ISSUE create instead would diverge on `glab mr create … ; glab issue
|
|
754
|
-
// create …`: the hook's G3 would look at the mr, this at the issue, and the
|
|
755
|
-
// two would disagree about which statement they are judging.
|
|
756
|
-
let headTok = null;
|
|
757
|
-
for (const seg of segments) {
|
|
758
|
-
const shape = matchStatement(seg);
|
|
759
|
-
if (!shape) continue;
|
|
760
|
-
if (shape.kind === 'issue') headTok = seg[0];
|
|
761
|
-
break;
|
|
762
|
-
}
|
|
763
|
-
if (!headTok) return false;
|
|
764
|
-
|
|
765
|
-
const kept = new Set(segments.flat());
|
|
766
|
-
let depth = 0;
|
|
767
|
-
for (const tok of tokens) {
|
|
768
|
-
if (tok === headTok) return depth > 0;
|
|
769
|
-
if (tok.quoted || kept.has(tok)) continue; // an argument, or a separator
|
|
770
|
-
if (tok.text === 'do') depth += 1;
|
|
771
|
-
else if (tok.text === 'done' && depth > 0) depth -= 1;
|
|
772
|
-
}
|
|
773
|
-
return false;
|
|
928
|
+
return findLoopedIssueCreates(command).length > 0;
|
|
774
929
|
}
|
|
775
930
|
|
|
776
931
|
/**
|
|
@@ -42,8 +42,7 @@
|
|
|
42
42
|
*/
|
|
43
43
|
|
|
44
44
|
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
45
|
-
|
|
46
|
-
if (!shouldRunHook('config-protection')) process.exit(0);
|
|
45
|
+
import { isMainModule } from '../scripts/lib/is-main-module.mjs';
|
|
47
46
|
|
|
48
47
|
import { promises as fs } from 'node:fs';
|
|
49
48
|
import path from 'node:path';
|
|
@@ -512,4 +511,13 @@ async function main() {
|
|
|
512
511
|
}
|
|
513
512
|
|
|
514
513
|
// Fail-open on ANY uncaught error — a guard bug must never block a legit edit.
|
|
515
|
-
|
|
514
|
+
// Entry guard (#1393): run only when this file IS the script node was invoked
|
|
515
|
+
// with — every harness path execs it (`sh run-node.sh <this file>`). A bare
|
|
516
|
+
// `import()` (a probe, a test, a curious agent) must neither run main() nor
|
|
517
|
+
// tear the importing process down. The profile gate sits INSIDE the guard for
|
|
518
|
+
// that second reason: at module top level its `process.exit(0)` exited every
|
|
519
|
+
// process that merely imported this hook.
|
|
520
|
+
if (isMainModule(import.meta.url)) {
|
|
521
|
+
if (!shouldRunHook('config-protection')) process.exit(0);
|
|
522
|
+
main().catch(() => { try { emitAllow(); } catch { process.exit(0); } });
|
|
523
|
+
}
|
|
@@ -27,8 +27,7 @@
|
|
|
27
27
|
import path from 'node:path';
|
|
28
28
|
|
|
29
29
|
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
30
|
-
|
|
31
|
-
if (!shouldRunHook('cwd-change-restore')) process.exit(0);
|
|
30
|
+
import { isMainModule } from '../scripts/lib/is-main-module.mjs';
|
|
32
31
|
|
|
33
32
|
import { getProjectDir } from '../scripts/lib/platform.mjs';
|
|
34
33
|
import { atomicMutateJson } from './_lib/atomic-json.mjs';
|
|
@@ -108,4 +107,13 @@ async function main() {
|
|
|
108
107
|
}
|
|
109
108
|
|
|
110
109
|
// Exit 0 always — informational hook must never block Claude.
|
|
111
|
-
|
|
110
|
+
// Entry guard (#1393): run only when this file IS the script node was invoked
|
|
111
|
+
// with — every harness path execs it (`sh run-node.sh <this file>`). A bare
|
|
112
|
+
// `import()` (a probe, a test, a curious agent) must neither run main() nor
|
|
113
|
+
// tear the importing process down. The profile gate sits INSIDE the guard for
|
|
114
|
+
// that second reason: at module top level its `process.exit(0)` exited every
|
|
115
|
+
// process that merely imported this hook.
|
|
116
|
+
if (isMainModule(import.meta.url)) {
|
|
117
|
+
if (!shouldRunHook('cwd-change-restore')) process.exit(0);
|
|
118
|
+
main().catch(() => {}).finally(() => process.exit(0));
|
|
119
|
+
}
|
|
@@ -31,9 +31,44 @@
|
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
33
|
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
34
|
-
//
|
|
35
|
-
|
|
34
|
+
// Static for the SAME reason profile-gate.mjs is (#993, see the late-binding
|
|
35
|
+
// block below): a leaf predicate with ZERO repo imports (node:fs + node:url
|
|
36
|
+
// only) that decides whether this hook runs at all. Everything carrying a
|
|
37
|
+
// transitive repo graph stays late-bound inside bootstrap().
|
|
38
|
+
import { isMainModule } from '../scripts/lib/is-main-module.mjs';
|
|
36
39
|
|
|
40
|
+
/**
|
|
41
|
+
* sha256(command), auf 16 Hex-Zeichen gekuerzt.
|
|
42
|
+
*
|
|
43
|
+
* WORTGLEICH zu `hashCommand()` in `hooks/pre-bash-destructive-guard.mjs:245`
|
|
44
|
+
* (das seinerseits `loop-guard.mjs` hashArgs() spiegelt). Bewusst dupliziert
|
|
45
|
+
* statt geteilt: ein Hook darf beim Start nicht an einem weiteren Modul
|
|
46
|
+
* haengen, das fehlen kann — die drei Zeilen sind billiger als ein
|
|
47
|
+
* Ladefehler auf dem PreToolUse-Pfad.
|
|
48
|
+
*
|
|
49
|
+
* WARUM ES DEN HELFER HIER BRAUCHT (2026-09-19, EventDrop #1140):
|
|
50
|
+
* Die `foreign_session_ignored`-Nutzlast dieses Hooks trug bis heute das
|
|
51
|
+
* ROHE Kommando. Gemessen in EventDrop.at: 3.816 Zeilen in der getrackten
|
|
52
|
+
* `.orchestrator/metrics/events.jsonl` mit rohem `command`, darin 24
|
|
53
|
+
* distinkte ECHTE Produktions-Share-Codes aus 17 fremden Kundenkonten und
|
|
54
|
+
* ein protokollierter `select access_pin_hash`. Bei 23 dieser Events ist
|
|
55
|
+
* der Share-Code die vollstaendige Capability — `/event/<code>` oeffnet das
|
|
56
|
+
* Album ohne Anmeldung. Das Journal ist getrackt und geht bei jedem Klon mit.
|
|
57
|
+
* Der Geschwisterhook `pre-bash-destructive-guard.mjs` machte es von Anfang
|
|
58
|
+
* an richtig und sagt es im eigenen Kopf: „Payload never includes the raw
|
|
59
|
+
* command — only a truncated sha256 command_hash."
|
|
60
|
+
*
|
|
61
|
+
* Der Hash haelt das Ereignis ZAEHLBAR und GRUPPIERBAR — genau die
|
|
62
|
+
* Eigenschaft, fuer die es laut `docs/scope-collision-guard.md` existiert.
|
|
63
|
+
* Das rohe Kommando wurde von keinem Konsumenten gelesen.
|
|
64
|
+
*
|
|
65
|
+
* @param {string} command
|
|
66
|
+
* @returns {string}
|
|
67
|
+
*/
|
|
68
|
+
function hashCommand(command) {
|
|
69
|
+
return crypto.createHash('sha256').update(command).digest('hex').slice(0, 16);
|
|
70
|
+
}
|
|
71
|
+
import crypto from 'node:crypto';
|
|
37
72
|
import path from 'node:path';
|
|
38
73
|
import { pathToFileURL } from 'node:url';
|
|
39
74
|
|
|
@@ -298,7 +333,7 @@ async function main() {
|
|
|
298
333
|
manifest_session: manifestIds,
|
|
299
334
|
own_session: [...ownIds],
|
|
300
335
|
wave: scope.wave,
|
|
301
|
-
command,
|
|
336
|
+
command_hash: hashCommand(command),
|
|
302
337
|
},
|
|
303
338
|
{ repoRoot: projectRoot },
|
|
304
339
|
);
|
|
@@ -430,26 +465,38 @@ function targetInWaveScope(target, allowedPaths, projectRoot) {
|
|
|
430
465
|
// guard armed and then tripped over a specific command; that fails CLOSED
|
|
431
466
|
// via emitDeny (SECURITY-REQ-01). The two paths MUST stay separate.
|
|
432
467
|
// ---------------------------------------------------------------------------
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
468
|
+
// Entry guard (#1393): run only when this file IS the script node was invoked
|
|
469
|
+
// with — every harness path execs it (`sh run-node.sh <this file>`). A bare
|
|
470
|
+
// `import()` (a probe, a test, a curious agent) must neither run main() nor
|
|
471
|
+
// tear the importing process down. The profile gate sits INSIDE the guard for
|
|
472
|
+
// that second reason: at module top level its `process.exit(0)` exited every
|
|
473
|
+
// process that merely imported this hook. bootstrap() is inside too — loading
|
|
474
|
+
// the guard sources is work a disabled hook and a bare importer must not do.
|
|
475
|
+
if (isMainModule(import.meta.url)) {
|
|
476
|
+
// #211: exit 0 immediately (silent allow) when this hook is disabled via profile/env
|
|
477
|
+
if (!shouldRunHook('enforce-commands')) process.exit(0);
|
|
478
|
+
|
|
436
479
|
try {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
480
|
+
await bootstrap();
|
|
481
|
+
} catch (loadError) {
|
|
482
|
+
try {
|
|
483
|
+
const { emitGuardInactiveBanner } = await import('./_lib/guard-source-loader.mjs');
|
|
484
|
+
// hookName is threaded explicitly (#993 — no hard-wired literal in the loader).
|
|
485
|
+
emitGuardInactiveBanner({ hookName: HOOK_NAME, error: loadError, consequence: GUARD_CONSEQUENCE });
|
|
486
|
+
} catch {
|
|
487
|
+
// Last resort: even the banner helper failed to load. Emit unconditionally —
|
|
488
|
+
// repeated noise beats a silent disarm.
|
|
489
|
+
process.stderr.write(
|
|
490
|
+
'🚨 enforce-commands: GUARD INACTIVE — module load failed ' +
|
|
491
|
+
`(${String(loadError?.message || loadError).split('\n')[0]}). ` +
|
|
492
|
+
'Blocked Bash commands are NOT being screened. See issue #993.\n'
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
process.exit(0); // fail-open, but no longer fail-silent
|
|
448
496
|
}
|
|
449
|
-
process.exit(0); // fail-open, but no longer fail-silent
|
|
450
|
-
}
|
|
451
497
|
|
|
452
|
-
// SECURITY-REQ-01 (F-03): top-level try/catch — never let exit 1 leak.
|
|
453
|
-
main().catch((e) => {
|
|
454
|
-
|
|
455
|
-
});
|
|
498
|
+
// SECURITY-REQ-01 (F-03): top-level try/catch — never let exit 1 leak.
|
|
499
|
+
main().catch((e) => {
|
|
500
|
+
emitDeny('Internal hook error — request blocked for safety', `${e?.message || e}`);
|
|
501
|
+
});
|
|
502
|
+
}
|