session-orchestrator 3.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +29 -0
- package/.claude-plugin/plugin.json +18 -0
- package/.codex-plugin/agents/explorer.toml +14 -0
- package/.codex-plugin/agents/session-reviewer.toml +23 -0
- package/.codex-plugin/agents/wave-worker.toml +15 -0
- package/.codex-plugin/config.toml +20 -0
- package/.codex-plugin/plugin.json +37 -0
- package/.cursor/rules/000-session-orchestrator.mdc +73 -0
- package/.cursor/rules/010-session-workflow.mdc +170 -0
- package/.cursor/rules/020-quality-gates.mdc +128 -0
- package/.cursor/rules/030-wave-execution.mdc +216 -0
- package/.cursor/rules/040-discovery.mdc +242 -0
- package/.cursor/rules/050-plan.mdc +235 -0
- package/.cursor/rules/060-evolve.mdc +232 -0
- package/.cursor/rules/070-gitlab-ops.mdc +246 -0
- package/.cursor/rules/080-ecosystem-health.mdc +145 -0
- package/.mcp.json +8 -0
- package/CHANGELOG.md +1544 -0
- package/LICENSE +21 -0
- package/NOTICE +64 -0
- package/README.md +242 -0
- package/SECURITY.md +90 -0
- package/agents/AGENTS.md +136 -0
- package/agents/analyst.md +99 -0
- package/agents/architect-reviewer.md +93 -0
- package/agents/code-implementer.md +106 -0
- package/agents/db-specialist.md +104 -0
- package/agents/dialectic-deriver.md +139 -0
- package/agents/docs-writer.md +113 -0
- package/agents/eval-judge.md +146 -0
- package/agents/memory-proposal-collector.md +297 -0
- package/agents/qa-strategist.md +102 -0
- package/agents/schemas/analyst.schema.json +46 -0
- package/agents/schemas/architect-reviewer.schema.json +50 -0
- package/agents/schemas/code-implementer.schema.json +61 -0
- package/agents/schemas/db-specialist.schema.json +80 -0
- package/agents/schemas/docs-writer.schema.json +56 -0
- package/agents/schemas/persona-panel-sidecar.schema.json +245 -0
- package/agents/schemas/qa-strategist.schema.json +46 -0
- package/agents/schemas/security-reviewer.schema.json +86 -0
- package/agents/schemas/session-reviewer.schema.json +69 -0
- package/agents/schemas/test-writer.schema.json +69 -0
- package/agents/schemas/ui-developer.schema.json +90 -0
- package/agents/schemas/ux-evaluator.schema.json +51 -0
- package/agents/security-reviewer.md +236 -0
- package/agents/session-reviewer.md +201 -0
- package/agents/skill-applied-judge.md +122 -0
- package/agents/test-writer.md +123 -0
- package/agents/ui-developer.md +109 -0
- package/agents/ux-evaluator.md +161 -0
- package/assets/icon.svg +11 -0
- package/assets/og-card.png +0 -0
- package/assets/og-card.svg +47 -0
- package/commands/autopilot-multi.md +74 -0
- package/commands/autopilot.md +80 -0
- package/commands/bootstrap.md +56 -0
- package/commands/brainstorm.md +48 -0
- package/commands/close.md +24 -0
- package/commands/debug.md +36 -0
- package/commands/discovery.md +32 -0
- package/commands/dispatcher.md +59 -0
- package/commands/eval.md +28 -0
- package/commands/evolve.md +10 -0
- package/commands/go.md +41 -0
- package/commands/grill.md +45 -0
- package/commands/harness-audit.md +26 -0
- package/commands/memory-cleanup.md +25 -0
- package/commands/persona-panel.md +121 -0
- package/commands/plan.md +15 -0
- package/commands/portfolio.md +97 -0
- package/commands/reconcile.md +23 -0
- package/commands/repo-audit.md +24 -0
- package/commands/session.md +30 -0
- package/commands/spinout.md +15 -0
- package/commands/sunset-review.md +27 -0
- package/commands/templates-ack.md +96 -0
- package/commands/test.md +97 -0
- package/docs/README.md +105 -0
- package/docs/USER-GUIDE.md +1403 -0
- package/docs/ci-setup.md +81 -0
- package/docs/codex-setup.md +142 -0
- package/docs/components.md +74 -0
- package/docs/cursor-setup.md +104 -0
- package/docs/events-schema.md +81 -0
- package/docs/migration-v3.md +148 -0
- package/docs/owner-config-schema.md +154 -0
- package/docs/persona-panel.md +433 -0
- package/docs/pi-setup.md +115 -0
- package/docs/plugin-architecture-v3.md +296 -0
- package/docs/pm-skills-marketplace.md +114 -0
- package/docs/policy-cache-validation-2026-04-28.md +118 -0
- package/docs/rule-authoring.md +316 -0
- package/docs/session-config-reference.md +1439 -0
- package/docs/session-config-template.md +961 -0
- package/docs/vault-docs-architecture.md +297 -0
- package/hooks/_lib/lock-bootstrap.mjs +272 -0
- package/hooks/_lib/lock-reconcile.mjs +93 -0
- package/hooks/_lib/profile-gate.mjs +95 -0
- package/hooks/_lib/transcript-history.mjs +211 -0
- package/hooks/agent-teams-h3-test.sh +362 -0
- package/hooks/config-protection.mjs +0 -0
- package/hooks/cwd-change-restore.mjs +131 -0
- package/hooks/enforce-commands.mjs +179 -0
- package/hooks/enforce-scope.mjs +273 -0
- package/hooks/hooks-codex.json +60 -0
- package/hooks/hooks-cursor.json +15 -0
- package/hooks/hooks-pi.json +115 -0
- package/hooks/hooks.json +215 -0
- package/hooks/loop-guard.mjs +260 -0
- package/hooks/on-session-end.mjs +217 -0
- package/hooks/on-session-start.mjs +660 -0
- package/hooks/on-stop.mjs +294 -0
- package/hooks/operator-steer.mjs +64 -0
- package/hooks/post-edit-validate.mjs +225 -0
- package/hooks/post-subagent-discovery-validator.mjs +398 -0
- package/hooks/post-tool-batch-wave-signal.mjs +328 -0
- package/hooks/post-tool-failure-corrective-context.mjs +248 -0
- package/hooks/post-tooluse-frontend-slop.mjs +184 -0
- package/hooks/pre-bash-destructive-guard.mjs +515 -0
- package/hooks/pre-bash-memory-propose-audit.mjs +206 -0
- package/hooks/pre-bash-staging-fence.mjs +223 -0
- package/hooks/pre-bash-templates-first.mjs +404 -0
- package/hooks/run-node.sh +72 -0
- package/hooks/skill-invocation-telemetry.mjs +99 -0
- package/hooks/subagent-telemetry.mjs +249 -0
- package/hooks/wave-scope-commit-guard.mjs +191 -0
- package/monitors/monitors.json +14 -0
- package/output-styles/finding-report.md +48 -0
- package/output-styles/session-report.md +53 -0
- package/output-styles/wave-summary.md +38 -0
- package/package.json +94 -0
- package/pi/extensions/session-orchestrator.ts +25 -0
- package/pi/prompts/autopilot-multi.md +12 -0
- package/pi/prompts/autopilot.md +12 -0
- package/pi/prompts/bootstrap.md +12 -0
- package/pi/prompts/brainstorm.md +12 -0
- package/pi/prompts/close.md +11 -0
- package/pi/prompts/debug.md +12 -0
- package/pi/prompts/discovery.md +12 -0
- package/pi/prompts/dispatcher.md +12 -0
- package/pi/prompts/eval.md +12 -0
- package/pi/prompts/evolve.md +12 -0
- package/pi/prompts/go.md +12 -0
- package/pi/prompts/grill.md +12 -0
- package/pi/prompts/harness-audit.md +12 -0
- package/pi/prompts/memory-cleanup.md +12 -0
- package/pi/prompts/persona-panel.md +12 -0
- package/pi/prompts/plan.md +12 -0
- package/pi/prompts/portfolio.md +12 -0
- package/pi/prompts/reconcile.md +12 -0
- package/pi/prompts/repo-audit.md +12 -0
- package/pi/prompts/session.md +12 -0
- package/pi/prompts/spinout.md +12 -0
- package/pi/prompts/sunset-review.md +12 -0
- package/pi/prompts/templates-ack.md +12 -0
- package/pi/prompts/test.md +12 -0
- package/rules/_index.md +51 -0
- package/rules/always-on/commit-discipline.md +26 -0
- package/rules/always-on/npm-quality-gates.md +26 -0
- package/rules/always-on/parallel-sessions.md +43 -0
- package/rules/opt-in-domain/prompt-caching.md +270 -0
- package/rules/opt-in-stack/backend-data.md +188 -0
- package/rules/opt-in-stack/backend.md +390 -0
- package/rules/opt-in-stack/frontend.md +98 -0
- package/rules/opt-in-stack/security-web.md +194 -0
- package/rules/opt-in-stack/swift.md +65 -0
- package/scripts/archive-closed-prds.mjs +416 -0
- package/scripts/autopilot-multi.mjs +802 -0
- package/scripts/autopilot.mjs +383 -0
- package/scripts/backfill-abandoned-sessions.mjs +265 -0
- package/scripts/backfill-learnings-expires.mjs +196 -0
- package/scripts/backfill-learnings.mjs +203 -0
- package/scripts/backfill-sessions.mjs +282 -0
- package/scripts/check-doc-consistency.sh +279 -0
- package/scripts/check-package-manager.mjs +445 -0
- package/scripts/ci/assert-vitest-green.mjs +267 -0
- package/scripts/codex-install.mjs +435 -0
- package/scripts/compute-grounding-injection.sh +186 -0
- package/scripts/cursor-install.mjs +113 -0
- package/scripts/dialectic-deriver.mjs +573 -0
- package/scripts/emit-event.mjs +160 -0
- package/scripts/emit-session.mjs +212 -0
- package/scripts/eval-session.mjs +262 -0
- package/scripts/export-hw-learnings.mjs +437 -0
- package/scripts/gc-stale-worktrees.mjs +666 -0
- package/scripts/generate-pi-prompts.mjs +127 -0
- package/scripts/harness-audit.mjs +287 -0
- package/scripts/lib/agent-frontmatter.mjs +266 -0
- package/scripts/lib/agent-output-schema.mjs +166 -0
- package/scripts/lib/agent-status.mjs +303 -0
- package/scripts/lib/ajv-loader.mjs +34 -0
- package/scripts/lib/auto-dialectic.mjs +382 -0
- package/scripts/lib/auto-dream.mjs +471 -0
- package/scripts/lib/autonomy/suitability.mjs +212 -0
- package/scripts/lib/autopilot/dep-graph.mjs +417 -0
- package/scripts/lib/autopilot/durable-telemetry.mjs +121 -0
- package/scripts/lib/autopilot/flags.mjs +104 -0
- package/scripts/lib/autopilot/kill-switches.mjs +174 -0
- package/scripts/lib/autopilot/loop.mjs +320 -0
- package/scripts/lib/autopilot/mr-draft.mjs +520 -0
- package/scripts/lib/autopilot/multi-killswitch.mjs +184 -0
- package/scripts/lib/autopilot/recent-runs.mjs +106 -0
- package/scripts/lib/autopilot/stall-sampler.mjs +97 -0
- package/scripts/lib/autopilot/telemetry.mjs +224 -0
- package/scripts/lib/autopilot/worktree-pipeline.mjs +605 -0
- package/scripts/lib/autopilot-telemetry.mjs +11 -0
- package/scripts/lib/autopilot.mjs +39 -0
- package/scripts/lib/backlog-scan.mjs +179 -0
- package/scripts/lib/bootstrap-lock-freshness.mjs +260 -0
- package/scripts/lib/bootstrap-lock-refresh.mjs +186 -0
- package/scripts/lib/build-live-signals.mjs +150 -0
- package/scripts/lib/ci-status-banner.mjs +425 -0
- package/scripts/lib/claude-md-budget-lint.mjs +246 -0
- package/scripts/lib/cli-flags.mjs +158 -0
- package/scripts/lib/codex/plugin-contract.mjs +610 -0
- package/scripts/lib/cold-start-detector.mjs +240 -0
- package/scripts/lib/command-blocker.mjs +458 -0
- package/scripts/lib/common.mjs +333 -0
- package/scripts/lib/config/auto-dream.mjs +77 -0
- package/scripts/lib/config/block-header.mjs +94 -0
- package/scripts/lib/config/broken-window.mjs +114 -0
- package/scripts/lib/config/coercers.mjs +248 -0
- package/scripts/lib/config/cold-start.mjs +92 -0
- package/scripts/lib/config/config-protection.mjs +120 -0
- package/scripts/lib/config/cross-repo.mjs +104 -0
- package/scripts/lib/config/custom-phases.mjs +213 -0
- package/scripts/lib/config/dialectic.mjs +92 -0
- package/scripts/lib/config/discovery-validator.mjs +75 -0
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +240 -0
- package/scripts/lib/config/dispatcher-autonomy.mjs +152 -0
- package/scripts/lib/config/docs-orchestrator.mjs +90 -0
- package/scripts/lib/config/docs-staleness.mjs +96 -0
- package/scripts/lib/config/drift-check.mjs +155 -0
- package/scripts/lib/config/eval.mjs +130 -0
- package/scripts/lib/config/events-rotation.mjs +74 -0
- package/scripts/lib/config/evolve.mjs +308 -0
- package/scripts/lib/config/frontend-slop-hook.mjs +104 -0
- package/scripts/lib/config/gitlab-portfolio.mjs +150 -0
- package/scripts/lib/config/handover-gate.mjs +106 -0
- package/scripts/lib/config/host-paths.mjs +76 -0
- package/scripts/lib/config/io.mjs +54 -0
- package/scripts/lib/config/loop-guard.mjs +117 -0
- package/scripts/lib/config/memory.mjs +150 -0
- package/scripts/lib/config/persona-gate-wave.mjs +258 -0
- package/scripts/lib/config/reconcile.mjs +205 -0
- package/scripts/lib/config/section-extractor.mjs +100 -0
- package/scripts/lib/config/skill-evolution.mjs +112 -0
- package/scripts/lib/config/slopcheck.mjs +99 -0
- package/scripts/lib/config/state-md-lock.mjs +83 -0
- package/scripts/lib/config/templates-first.mjs +94 -0
- package/scripts/lib/config/test.mjs +113 -0
- package/scripts/lib/config/vault-integration.mjs +201 -0
- package/scripts/lib/config/vault-mirror-quality.mjs +99 -0
- package/scripts/lib/config/vault-staleness.mjs +84 -0
- package/scripts/lib/config/vault-sync.mjs +96 -0
- package/scripts/lib/config/verification-auto-fix.mjs +84 -0
- package/scripts/lib/config/wave-reviewers.mjs +133 -0
- package/scripts/lib/config-schema.mjs +345 -0
- package/scripts/lib/config.mjs +474 -0
- package/scripts/lib/convergence-monitor.mjs +389 -0
- package/scripts/lib/coordinator-snapshot.mjs +371 -0
- package/scripts/lib/crypto-digest-utils.mjs +91 -0
- package/scripts/lib/discovery/helpers.mjs +127 -0
- package/scripts/lib/discovery/triage-state.mjs +279 -0
- package/scripts/lib/dispatcher/cli.mjs +257 -0
- package/scripts/lib/dispatcher/enumerate.mjs +243 -0
- package/scripts/lib/dispatcher/rank.mjs +363 -0
- package/scripts/lib/ecosystem-health.mjs +224 -0
- package/scripts/lib/ecosystem-wizard/ci-detector.mjs +18 -0
- package/scripts/lib/ecosystem-wizard/config-parser.mjs +54 -0
- package/scripts/lib/ecosystem-wizard/config-writer.mjs +287 -0
- package/scripts/lib/ecosystem-wizard/package-manager-detector.mjs +42 -0
- package/scripts/lib/ecosystem-wizard/wizard-prompt.mjs +246 -0
- package/scripts/lib/ecosystem-wizard.mjs +48 -0
- package/scripts/lib/env-check.mjs +89 -0
- package/scripts/lib/eval/engine.mjs +605 -0
- package/scripts/lib/eval/judge.mjs +433 -0
- package/scripts/lib/eval/report.mjs +367 -0
- package/scripts/lib/eval/schema.mjs +618 -0
- package/scripts/lib/eval/session-resolve.mjs +137 -0
- package/scripts/lib/eval/sink.mjs +77 -0
- package/scripts/lib/events-rotation.mjs +86 -0
- package/scripts/lib/events-schema.mjs +81 -0
- package/scripts/lib/events.mjs +80 -0
- package/scripts/lib/evolve/autonomy-verdict.mjs +461 -0
- package/scripts/lib/evolve/autopilot-effectiveness.mjs +293 -0
- package/scripts/lib/exclusivity-matrix.mjs +68 -0
- package/scripts/lib/fetch-baseline.mjs +311 -0
- package/scripts/lib/file-lock.mjs +512 -0
- package/scripts/lib/frontend-detect/detect.mjs +138 -0
- package/scripts/lib/frontend-detect/rules.mjs +295 -0
- package/scripts/lib/frontmatter-guard.mjs +241 -0
- package/scripts/lib/gates/echo-stub-detect.mjs +39 -0
- package/scripts/lib/gates/gate-baseline.mjs +42 -0
- package/scripts/lib/gates/gate-full.mjs +85 -0
- package/scripts/lib/gates/gate-helpers.mjs +231 -0
- package/scripts/lib/gates/gate-incremental.mjs +76 -0
- package/scripts/lib/gates/gate-per-file.mjs +55 -0
- package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +447 -0
- package/scripts/lib/gitlab-portfolio/aggregator.mjs +383 -0
- package/scripts/lib/gitlab-portfolio/cli.mjs +428 -0
- package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +289 -0
- package/scripts/lib/gitlab-portfolio/vcs-detect.mjs +182 -0
- package/scripts/lib/handover-gate.mjs +222 -0
- package/scripts/lib/hardening.mjs +43 -0
- package/scripts/lib/hardware-pattern-detector.mjs +238 -0
- package/scripts/lib/harness-audit/categories/category1.mjs +123 -0
- package/scripts/lib/harness-audit/categories/category2.mjs +145 -0
- package/scripts/lib/harness-audit/categories/category3.mjs +143 -0
- package/scripts/lib/harness-audit/categories/category4.mjs +202 -0
- package/scripts/lib/harness-audit/categories/category5.mjs +152 -0
- package/scripts/lib/harness-audit/categories/category6.mjs +211 -0
- package/scripts/lib/harness-audit/categories/category7.mjs +125 -0
- package/scripts/lib/harness-audit/categories/category8.mjs +328 -0
- package/scripts/lib/harness-audit/categories/category9.mjs +294 -0
- package/scripts/lib/harness-audit/categories/helpers.mjs +165 -0
- package/scripts/lib/harness-audit/categories.mjs +19 -0
- package/scripts/lib/historical-guard.mjs +15 -0
- package/scripts/lib/host-identity.mjs +262 -0
- package/scripts/lib/instruction-budget-guard.mjs +332 -0
- package/scripts/lib/io.mjs +304 -0
- package/scripts/lib/issue-close-strip-labels.mjs +161 -0
- package/scripts/lib/language-mappers/README.md +57 -0
- package/scripts/lib/language-mappers/index.mjs +165 -0
- package/scripts/lib/language-mappers/markdown.mjs +149 -0
- package/scripts/lib/language-mappers/python.mjs +249 -0
- package/scripts/lib/language-mappers/swift.mjs +201 -0
- package/scripts/lib/language-mappers/typescript.mjs +433 -0
- package/scripts/lib/learnings/expiry-sweep.mjs +164 -0
- package/scripts/lib/learnings/filters.mjs +43 -0
- package/scripts/lib/learnings/io.mjs +255 -0
- package/scripts/lib/learnings/schema.mjs +518 -0
- package/scripts/lib/learnings/surface.mjs +207 -0
- package/scripts/lib/learnings.mjs +42 -0
- package/scripts/lib/lock-reaper.mjs +648 -0
- package/scripts/lib/locks/index.mjs +31 -0
- package/scripts/lib/locks/lock-body.mjs +62 -0
- package/scripts/lib/locks/staging-fence-lock.mjs +267 -0
- package/scripts/lib/locks/state-md-lock.mjs +351 -0
- package/scripts/lib/loop-readiness-banner.mjs +144 -0
- package/scripts/lib/memory-banner.mjs +478 -0
- package/scripts/lib/memory-cleanup/worktree-sweep.mjs +108 -0
- package/scripts/lib/memory-cleanup-stamp.mjs +56 -0
- package/scripts/lib/memory-paths.mjs +31 -0
- package/scripts/lib/memory-proposals/collector.mjs +334 -0
- package/scripts/lib/memory-proposals/schema.mjs +289 -0
- package/scripts/lib/memory-proposals/sink.mjs +507 -0
- package/scripts/lib/memory-proposals/store.mjs +441 -0
- package/scripts/lib/mission-status-schema.mjs +114 -0
- package/scripts/lib/mode-selector/alternatives.mjs +64 -0
- package/scripts/lib/mode-selector/constants.mjs +29 -0
- package/scripts/lib/mode-selector/context-pressure.mjs +157 -0
- package/scripts/lib/mode-selector/rationale.mjs +55 -0
- package/scripts/lib/mode-selector/scoring.mjs +221 -0
- package/scripts/lib/mode-selector-accuracy.mjs +121 -0
- package/scripts/lib/mode-selector.mjs +160 -0
- package/scripts/lib/multi-provider-build/providers.mjs +64 -0
- package/scripts/lib/multi-provider-build/templating.mjs +130 -0
- package/scripts/lib/named-baseline-resolver.mjs +233 -0
- package/scripts/lib/named-vault-resolver.mjs +433 -0
- package/scripts/lib/owner-config/coerce.mjs +29 -0
- package/scripts/lib/owner-config/constants.mjs +21 -0
- package/scripts/lib/owner-config/defaults.mjs +50 -0
- package/scripts/lib/owner-config/error.mjs +19 -0
- package/scripts/lib/owner-config/index.mjs +13 -0
- package/scripts/lib/owner-config/merge.mjs +52 -0
- package/scripts/lib/owner-config/validate.mjs +259 -0
- package/scripts/lib/owner-config-banner.mjs +126 -0
- package/scripts/lib/owner-config-loader.mjs +159 -0
- package/scripts/lib/owner-config.example.yaml +72 -0
- package/scripts/lib/owner-config.mjs +28 -0
- package/scripts/lib/owner-interview.mjs +243 -0
- package/scripts/lib/owner-yaml.mjs +571 -0
- package/scripts/lib/package-manager.mjs +160 -0
- package/scripts/lib/path-utils.mjs +217 -0
- package/scripts/lib/peer-cards/merger.mjs +310 -0
- package/scripts/lib/peer-cards/reader.mjs +125 -0
- package/scripts/lib/peer-cards/schema.mjs +230 -0
- package/scripts/lib/peer-cards/staleness-banner.mjs +86 -0
- package/scripts/lib/peer-cards/writer.mjs +138 -0
- package/scripts/lib/peer-discovery.mjs +200 -0
- package/scripts/lib/persona-panel/catalog-loader.mjs +577 -0
- package/scripts/lib/persona-panel/consolidator.mjs +370 -0
- package/scripts/lib/persona-panel/persona-runner.mjs +375 -0
- package/scripts/lib/persona-panel/threshold.mjs +130 -0
- package/scripts/lib/pi-hook-bridge.mjs +328 -0
- package/scripts/lib/platform.mjs +266 -0
- package/scripts/lib/playwright-driver/runner.mjs +297 -0
- package/scripts/lib/plugin-root.mjs +210 -0
- package/scripts/lib/pre-dispatch-check.mjs +126 -0
- package/scripts/lib/product-repo-detect.mjs +121 -0
- package/scripts/lib/profiles/registry.mjs +176 -0
- package/scripts/lib/profiles/schema.mjs +209 -0
- package/scripts/lib/qg-command-drift-banner.mjs +88 -0
- package/scripts/lib/quality-gate/diagnostics.mjs +92 -0
- package/scripts/lib/quality-gate.mjs +536 -0
- package/scripts/lib/quality-gates-cache.mjs +228 -0
- package/scripts/lib/quality-gates-policy.mjs +95 -0
- package/scripts/lib/recommendations-v0.mjs +156 -0
- package/scripts/lib/reconcile/eligibility.mjs +203 -0
- package/scripts/lib/reconcile/emitter.mjs +244 -0
- package/scripts/lib/reconcile/engine.mjs +412 -0
- package/scripts/lib/reconcile/idempotency.mjs +239 -0
- package/scripts/lib/reconcile/renderer.mjs +211 -0
- package/scripts/lib/reconcile/writer.mjs +293 -0
- package/scripts/lib/reconcile-nudge-banner.mjs +284 -0
- package/scripts/lib/resource-probe/evaluate.mjs +190 -0
- package/scripts/lib/resource-probe/parsers.mjs +181 -0
- package/scripts/lib/resource-probe/probe-platform.mjs +300 -0
- package/scripts/lib/resource-probe.mjs +95 -0
- package/scripts/lib/rule-loader.mjs +552 -0
- package/scripts/lib/rules-sync.mjs +439 -0
- package/scripts/lib/scope-gate.mjs +496 -0
- package/scripts/lib/session-close-backfill.mjs +539 -0
- package/scripts/lib/session-discovery.mjs +256 -0
- package/scripts/lib/session-end/phase-skip.mjs +357 -0
- package/scripts/lib/session-end/worktree-cleanup.mjs +112 -0
- package/scripts/lib/session-id.mjs +362 -0
- package/scripts/lib/session-lock.mjs +703 -0
- package/scripts/lib/session-registry.mjs +355 -0
- package/scripts/lib/session-schema/aliases.mjs +71 -0
- package/scripts/lib/session-schema/constants.mjs +115 -0
- package/scripts/lib/session-schema/normalizer.mjs +66 -0
- package/scripts/lib/session-schema/timestamps.mjs +64 -0
- package/scripts/lib/session-schema/validator.mjs +453 -0
- package/scripts/lib/session-schema.mjs +71 -0
- package/scripts/lib/session-token-rollup.mjs +137 -0
- package/scripts/lib/sessions-staleness-banner.mjs +247 -0
- package/scripts/lib/skill-evolution/blast-radius-classifier.mjs +114 -0
- package/scripts/lib/skill-evolution/candidate-intake.mjs +270 -0
- package/scripts/lib/skill-evolution/config-validation-gate.mjs +279 -0
- package/scripts/lib/skill-evolution/engine.mjs +719 -0
- package/scripts/lib/skill-evolution/idempotency.mjs +279 -0
- package/scripts/lib/skill-evolution/mr-opener.mjs +507 -0
- package/scripts/lib/skill-health/join.mjs +181 -0
- package/scripts/lib/skill-health/score.mjs +123 -0
- package/scripts/lib/skill-invocations-schema.mjs +214 -0
- package/scripts/lib/skill-judge.mjs +348 -0
- package/scripts/lib/skill-judgments-schema.mjs +264 -0
- package/scripts/lib/slopcheck.mjs +501 -0
- package/scripts/lib/soul-resolve.mjs +118 -0
- package/scripts/lib/spiral-carryover.mjs +495 -0
- package/scripts/lib/state-md/body-sections.mjs +851 -0
- package/scripts/lib/state-md/frontmatter-mutators.mjs +453 -0
- package/scripts/lib/state-md/mission-status.mjs +247 -0
- package/scripts/lib/state-md/recommendations.mjs +57 -0
- package/scripts/lib/state-md/yaml-parser.mjs +234 -0
- package/scripts/lib/state-md-peer-guard.mjs +232 -0
- package/scripts/lib/state-md.mjs +53 -0
- package/scripts/lib/subagents-schema.mjs +309 -0
- package/scripts/lib/sunset/walker.mjs +1192 -0
- package/scripts/lib/test-runner/artifact-paths.mjs +94 -0
- package/scripts/lib/test-runner/fingerprint.mjs +33 -0
- package/scripts/lib/test-runner/issue-reconcile.mjs +770 -0
- package/scripts/lib/tmux-layout/layouts.mjs +224 -0
- package/scripts/lib/tmux-layout/telemetry-stats.mjs +100 -0
- package/scripts/lib/tmux-layout/telemetry.mjs +88 -0
- package/scripts/lib/tmux-layout/tmux-shell.mjs +82 -0
- package/scripts/lib/tmux-layout/vcs-detector.mjs +88 -0
- package/scripts/lib/validate/check-agents.mjs +457 -0
- package/scripts/lib/validate/check-codex-plugin.mjs +37 -0
- package/scripts/lib/validate/check-commands.mjs +148 -0
- package/scripts/lib/validate/check-component-paths.mjs +112 -0
- package/scripts/lib/validate/check-dead-bridge.mjs +180 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +258 -0
- package/scripts/lib/validate/check-json-files.mjs +116 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +1011 -0
- package/scripts/lib/validate/check-path-utils-canary.mjs +175 -0
- package/scripts/lib/validate/check-peekaboo-driver-canary.mjs +201 -0
- package/scripts/lib/validate/check-pi-package.mjs +110 -0
- package/scripts/lib/validate/check-pi-prompts.mjs +43 -0
- package/scripts/lib/validate/check-playwright-mcp-canary.mjs +154 -0
- package/scripts/lib/validate/check-plugin-json.mjs +96 -0
- package/scripts/lib/validate/check-plugin-monitors.mjs +206 -0
- package/scripts/lib/validate/check-plugin-schema.mjs +137 -0
- package/scripts/lib/validate/check-rules.mjs +143 -0
- package/scripts/lib/validate/check-session-plan-routing.mjs +154 -0
- package/scripts/lib/validate/check-test-fixture-shapes.mjs +280 -0
- package/scripts/lib/validate/check-unicode-safety.mjs +533 -0
- package/scripts/lib/validate/confidential-names.mjs +169 -0
- package/scripts/lib/validate/dead-bridge-corpus.mjs +141 -0
- package/scripts/lib/validate/dead-bridge-detectors.mjs +568 -0
- package/scripts/lib/validate/tier-inference.mjs +100 -0
- package/scripts/lib/validate-vendored-rules.mjs +519 -0
- package/scripts/lib/vault-archive.mjs +404 -0
- package/scripts/lib/vault-backfill/glab.mjs +164 -0
- package/scripts/lib/vault-backfill/manifest.mjs +75 -0
- package/scripts/lib/vault-backfill/template.mjs +130 -0
- package/scripts/lib/vault-consolidate-fs.mjs +331 -0
- package/scripts/lib/vault-migration-rules.mjs +155 -0
- package/scripts/lib/vault-mirror/auto-commit.mjs +203 -0
- package/scripts/lib/vault-mirror/namespace.mjs +152 -0
- package/scripts/lib/vault-mirror/process.mjs +567 -0
- package/scripts/lib/vault-mirror/pseudonym-map.mjs +164 -0
- package/scripts/lib/vault-mirror/render-learnings.mjs +201 -0
- package/scripts/lib/vault-mirror/render-sessions.mjs +367 -0
- package/scripts/lib/vault-mirror/render.mjs +8 -0
- package/scripts/lib/vault-mirror/utils.mjs +217 -0
- package/scripts/lib/vault-relocation-rules.mjs +555 -0
- package/scripts/lib/vault-repo-backfill.mjs +235 -0
- package/scripts/lib/vault-staleness-banner.mjs +142 -0
- package/scripts/lib/vault-status/board-writer.mjs +769 -0
- package/scripts/lib/vault-status/narrative-mirror.mjs +544 -0
- package/scripts/lib/vault-sync-baseline.mjs +152 -0
- package/scripts/lib/wave-context.mjs +29 -0
- package/scripts/lib/wave-executor/pool.mjs +248 -0
- package/scripts/lib/wave-resource-gate.mjs +204 -0
- package/scripts/lib/wave-sizing.mjs +75 -0
- package/scripts/lib/webhook-url.mjs +105 -0
- package/scripts/lib/workspace.mjs +198 -0
- package/scripts/lib/worktree/constants.mjs +35 -0
- package/scripts/lib/worktree/index.mjs +17 -0
- package/scripts/lib/worktree/lifecycle.mjs +287 -0
- package/scripts/lib/worktree/listing.mjs +118 -0
- package/scripts/lib/worktree/meta.mjs +64 -0
- package/scripts/lib/worktree-freshness.mjs +313 -0
- package/scripts/lib/worktree.mjs +15 -0
- package/scripts/lifecycle-sim-v6.mjs +347 -0
- package/scripts/lock-reaper.mjs +185 -0
- package/scripts/mcp-server.sh +241 -0
- package/scripts/measure-policy-cache-effectiveness.mjs +427 -0
- package/scripts/memory-propose.mjs +464 -0
- package/scripts/migrate-cold-start-seed.mjs +404 -0
- package/scripts/migrate-learnings-jsonl.mjs +189 -0
- package/scripts/migrate-legacy-learnings.sh +61 -0
- package/scripts/migrate-sessions-jsonl.mjs +448 -0
- package/scripts/migrate-subagents-jsonl.mjs +196 -0
- package/scripts/migrate-vault-paths.mjs +796 -0
- package/scripts/parse-config.mjs +149 -0
- package/scripts/pi-install.mjs +117 -0
- package/scripts/print-applicable-rules.mjs +247 -0
- package/scripts/promote-vault-strict.mjs +496 -0
- package/scripts/relocate-vault-corpus.mjs +1178 -0
- package/scripts/run-migrate-v2-cross-repo.mjs +385 -0
- package/scripts/run-quality-gate.mjs +216 -0
- package/scripts/spikes/h3-agent-teams/preflight.sh +53 -0
- package/scripts/spikes/h3-agent-teams/run-h3.sh +112 -0
- package/scripts/spikes/h3-agent-teams/setup.sh +137 -0
- package/scripts/spikes/h3-agent-teams/toggle.sh +38 -0
- package/scripts/sweep-expired-learnings.mjs +135 -0
- package/scripts/sync-vault-schema.mjs +376 -0
- package/scripts/tests/fixtures/fetch-baseline/sample-rule.md +8 -0
- package/scripts/tmux-layout.mjs +245 -0
- package/scripts/token-audit.sh +191 -0
- package/scripts/typecheck.mjs +42 -0
- package/scripts/upload-social-preview.mjs +316 -0
- package/scripts/validate-config.mjs +46 -0
- package/scripts/validate-plugin-manifests.mjs +163 -0
- package/scripts/validate-plugin.mjs +264 -0
- package/scripts/validate-wave-scope.mjs +289 -0
- package/scripts/vault-backfill.mjs +404 -0
- package/scripts/vault-consolidate.mjs +596 -0
- package/scripts/vault-integration-watcher.mjs +394 -0
- package/scripts/vault-mirror.mjs +430 -0
- package/skills/_shared/bootstrap-gate.md +111 -0
- package/skills/_shared/config-reading.md +226 -0
- package/skills/_shared/instruction-file-resolution.md +79 -0
- package/skills/_shared/model-selection.md +64 -0
- package/skills/_shared/monitor-patterns.md +300 -0
- package/skills/_shared/parallel-aware-auq.md +121 -0
- package/skills/_shared/parallel-aware-preamble.md +185 -0
- package/skills/_shared/platform-tools.md +96 -0
- package/skills/_shared/state-ownership.md +221 -0
- package/skills/architecture/DEEPENING.md +37 -0
- package/skills/architecture/INTERFACE-DESIGN.md +44 -0
- package/skills/architecture/LANGUAGE.md +53 -0
- package/skills/architecture/SKILL.md +92 -0
- package/skills/autopilot/SKILL.md +419 -0
- package/skills/bootstrap/SKILL.md +592 -0
- package/skills/bootstrap/STATE.md.template +24 -0
- package/skills/bootstrap/_shared-template.md +243 -0
- package/skills/bootstrap/deep-template.md +659 -0
- package/skills/bootstrap/fast-template.md +251 -0
- package/skills/bootstrap/intensity-heuristic.md +80 -0
- package/skills/bootstrap/public-fallback.md +342 -0
- package/skills/bootstrap/standard-template.md +736 -0
- package/skills/bootstrap/templates/agents/project-code-review.md +18 -0
- package/skills/bootstrap/templates/agents/project-discovery.md +18 -0
- package/skills/bootstrap/templates/agents/project-quality-gate.md +18 -0
- package/skills/brainstorm/SKILL.md +268 -0
- package/skills/brainstorm/soul.md +49 -0
- package/skills/claude-md-drift-check/SKILL.md +186 -0
- package/skills/claude-md-drift-check/checker.mjs +1380 -0
- package/skills/claude-md-drift-check/checker.sh +37 -0
- package/skills/claude-md-drift-check/package.json +16 -0
- package/skills/convergence-monitoring/README.md +39 -0
- package/skills/convergence-monitoring/SIGNALS.md +246 -0
- package/skills/convergence-monitoring/SKILL.md +285 -0
- package/skills/daily/SKILL.md +222 -0
- package/skills/daily/generate.sh +92 -0
- package/skills/daily/templates/daily.md.tpl +36 -0
- package/skills/debug/SKILL.md +188 -0
- package/skills/debug/soul.md +35 -0
- package/skills/discovery/SKILL.md +567 -0
- package/skills/discovery/issue-templates.md +237 -0
- package/skills/discovery/probes/docs-staleness.mjs +195 -0
- package/skills/discovery/probes/frontend-slop.mjs +186 -0
- package/skills/discovery/probes/ssot-code-diff.mjs +310 -0
- package/skills/discovery/probes/supply-chain-slopcheck.mjs +440 -0
- package/skills/discovery/probes/vault-narrative-staleness.mjs +355 -0
- package/skills/discovery/probes/vault-staleness.mjs +272 -0
- package/skills/discovery/probes-arch.md +252 -0
- package/skills/discovery/probes-audit.md +95 -0
- package/skills/discovery/probes-code.md +329 -0
- package/skills/discovery/probes-docs.md +76 -0
- package/skills/discovery/probes-feature.md +150 -0
- package/skills/discovery/probes-infra.md +138 -0
- package/skills/discovery/probes-intro.md +25 -0
- package/skills/discovery/probes-session.md +495 -0
- package/skills/discovery/probes-supply-chain.md +94 -0
- package/skills/discovery/probes-ui.md +147 -0
- package/skills/discovery/probes-vault.md +64 -0
- package/skills/discovery/slop-patterns.md +115 -0
- package/skills/dispatcher/SKILL.md +173 -0
- package/skills/docs-orchestrator/SKILL.md +362 -0
- package/skills/docs-orchestrator/audience-mapping.md +140 -0
- package/skills/domain-model/ADR-FORMAT.md +47 -0
- package/skills/domain-model/CONTEXT-FORMAT.md +77 -0
- package/skills/domain-model/SKILL.md +85 -0
- package/skills/ecosystem-health/SKILL.md +119 -0
- package/skills/ecosystem-health/wizard.md +193 -0
- package/skills/eval/SKILL.md +293 -0
- package/skills/eval/rubric-v1.md +218 -0
- package/skills/evolve/SKILL.md +546 -0
- package/skills/frontmatter-guard/SKILL.md +126 -0
- package/skills/gitlab-ops/SKILL.md +368 -0
- package/skills/gitlab-portfolio/SKILL.md +196 -0
- package/skills/grill/SKILL.md +185 -0
- package/skills/grill/soul.md +55 -0
- package/skills/hook-development/SKILL.md +413 -0
- package/skills/mcp-builder/SKILL.md +260 -0
- package/skills/memory-cleanup/SKILL.md +310 -0
- package/skills/mode-selector/SKILL.md +226 -0
- package/skills/peekaboo-driver/SKILL.md +237 -0
- package/skills/peekaboo-driver/soul.md +32 -0
- package/skills/persona-panel/SKILL.md +365 -0
- package/skills/persona-panel/persona-format.md +205 -0
- package/skills/persona-panel/presets/designer-lens.md +87 -0
- package/skills/persona-panel/presets/engineer-lens.md +88 -0
- package/skills/persona-panel/presets/pm-lens.md +86 -0
- package/skills/plan/SKILL.md +496 -0
- package/skills/plan/mode-feature.md +141 -0
- package/skills/plan/mode-new.md +297 -0
- package/skills/plan/mode-retro.md +271 -0
- package/skills/plan/prd-feature-template.md +132 -0
- package/skills/plan/prd-full-template.md +151 -0
- package/skills/plan/prd-reviewer-prompt.md +103 -0
- package/skills/plan/retro-template.md +75 -0
- package/skills/plan/soul.md +62 -0
- package/skills/playwright-driver/SKILL.md +226 -0
- package/skills/playwright-driver/soul.md +30 -0
- package/skills/quality-gates/SKILL.md +212 -0
- package/skills/reconcile/SKILL.md +324 -0
- package/skills/repo-audit/SKILL.md +272 -0
- package/skills/session-end/SKILL.md +1044 -0
- package/skills/session-end/discovery-scan.md +37 -0
- package/skills/session-end/drift-operations.md +97 -0
- package/skills/session-end/learning-patterns.md +78 -0
- package/skills/session-end/metrics-collection.md +175 -0
- package/skills/session-end/phase-3-2-docs-verification.md +148 -0
- package/skills/session-end/phase-3-6-tail.md +344 -0
- package/skills/session-end/phase-3-7a-recommendations.md +86 -0
- package/skills/session-end/plan-verification.md +288 -0
- package/skills/session-end/session-metrics-write.md +223 -0
- package/skills/session-end/vault-operations.md +50 -0
- package/skills/session-end/verification-checklist.md +20 -0
- package/skills/session-plan/SKILL.md +554 -0
- package/skills/session-plan/wave-template.md +37 -0
- package/skills/session-start/SKILL.md +1043 -0
- package/skills/session-start/phase-2-5-docs-planning.md +119 -0
- package/skills/session-start/phase-4-5-resource-health.md +49 -0
- package/skills/session-start/phase-7-1-premise-check.md +47 -0
- package/skills/session-start/phase-7-5-mode-selector.md +237 -0
- package/skills/session-start/phase-8-5-express-path.md +61 -0
- package/skills/session-start/presentation-format.md +81 -0
- package/skills/session-start/soul.md +57 -0
- package/skills/skill-creator/SKILL.md +168 -0
- package/skills/spinout/SKILL.md +76 -0
- package/skills/sunset-review/SKILL.md +96 -0
- package/skills/test-runner/SKILL.md +362 -0
- package/skills/test-runner/rubric-v1.md +388 -0
- package/skills/test-runner/soul.md +46 -0
- package/skills/tmux-layout/SKILL.md +104 -0
- package/skills/ubiquitous-language/SKILL.md +97 -0
- package/skills/using-orchestrator/SKILL.md +144 -0
- package/skills/vault-mirror/SKILL.md +234 -0
- package/skills/vault-sync/SKILL.md +319 -0
- package/skills/vault-sync/package-lock.json +40 -0
- package/skills/vault-sync/package.json +11 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/90-archive/bad-archived.md +8 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/live-note.md +8 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/bad-type.md +8 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/good-note.md +8 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/.obsidian/config.md +8 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/01-projects/foo/projects-baseline.md +10 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/03-daily/daily-2026-04-13.md +8 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/README.md +3 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/hello-world.md +11 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/has-dangling.md +9 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/real-target.md +8 -0
- package/skills/vault-sync/tests/fixtures/empty-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/missing-id.md +7 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/03-daily/daily-2026-04-13.md +9 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/nested-tags-note.md +11 -0
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/README.md +3 -0
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_MOC.md +3 -0
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_MOC.md +11 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/hello-world.md +11 -0
- package/skills/vault-sync/tests/schema-drift.test.mjs +133 -0
- package/skills/vault-sync/validator.mjs +658 -0
- package/skills/vault-sync/validator.sh +55 -0
- package/skills/wave-executor/SKILL.md +496 -0
- package/skills/wave-executor/circuit-breaker.md +169 -0
- package/skills/wave-executor/wave-loop.md +1043 -0
- package/skills/write-executable-plan/SKILL.md +237 -0
- package/skills/write-executable-plan/plan-template.md +154 -0
- package/templates/_minimal/CLAUDE.md.tmpl +41 -0
- package/templates/_minimal/README.md.tmpl +15 -0
- package/templates/_minimal/gitignore.tmpl +47 -0
- package/templates/_shared/harte-regeln.md +16 -0
- package/templates/_shared/loop.md +90 -0
- package/templates/_shared/rules/parallel-sessions.md +77 -0
- package/templates/nextjs-minimal/README.md +30 -0
- package/templates/nextjs-minimal/app/layout.tsx +18 -0
- package/templates/nextjs-minimal/app/page.tsx +7 -0
- package/templates/nextjs-minimal/eslint.config.mjs +16 -0
- package/templates/nextjs-minimal/next.config.mjs +4 -0
- package/templates/nextjs-minimal/package.json +27 -0
- package/templates/nextjs-minimal/tsconfig.json +23 -0
- package/templates/node-minimal/README.md +33 -0
- package/templates/node-minimal/eslint.config.mjs +10 -0
- package/templates/node-minimal/package.json +21 -0
- package/templates/node-minimal/src/index.ts +1 -0
- package/templates/node-minimal/tests/sanity.test.ts +5 -0
- package/templates/node-minimal/tsconfig.json +17 -0
- package/templates/personas/README.md +150 -0
- package/templates/personas/accounting-compliance.v1.md +120 -0
- package/templates/personas/accounting-tax-advisor.v1.md +116 -0
- package/templates/personas/buyer-p1-cto.v1.md +125 -0
- package/templates/personas/buyer-p2-kanzlei.v1.md +134 -0
- package/templates/personas/buyer-p3-build.v1.md +130 -0
- package/templates/personas/buyer-p4-tech-veto.v1.md +130 -0
- package/templates/personas/buyer-p5-solo.v1.md +132 -0
- package/templates/personas/buyer-p6-ld.v1.md +130 -0
- package/templates/personas/klima-ai-expert.v1.md +114 -0
- package/templates/personas/klima-physicist.v1.md +117 -0
- package/templates/python-uv/README.md +28 -0
- package/templates/python-uv/pyproject.toml +32 -0
- package/templates/python-uv/src/__PROJECT_NAME__/__init__.py +0 -0
- package/templates/python-uv/src/__PROJECT_NAME__/main.py +6 -0
- package/templates/python-uv/tests/test_sanity.py +2 -0
- package/templates/static-html/README.md +19 -0
- package/templates/static-html/index.html +15 -0
- package/templates/static-html/script.js +1 -0
- package/templates/static-html/styles.css +26 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* cwd-change-restore.mjs — CwdChanged hook handler.
|
|
4
|
+
*
|
|
5
|
+
* Hook event: CwdChanged (issue #342).
|
|
6
|
+
* Fires when the coordinator's working directory changes unexpectedly.
|
|
7
|
+
* Records the event in `.orchestrator/current-session.json` under the
|
|
8
|
+
* `cwd_changes` array so the coordinator and downstream skills can
|
|
9
|
+
* inspect recent directory changes without re-reading the full event log.
|
|
10
|
+
*
|
|
11
|
+
* Note: the actual CWD restoration is a harness-level concern; this
|
|
12
|
+
* handler is informational only. It mirrors the pattern used by
|
|
13
|
+
* post-tool-failure-corrective-context.mjs.
|
|
14
|
+
*
|
|
15
|
+
* Decision flow:
|
|
16
|
+
* 1. shouldRunHook gate — exit 0 immediately when the hook is disabled.
|
|
17
|
+
* 2. Read JSON payload from stdin: { previous_cwd, new_cwd }.
|
|
18
|
+
* 3. Build a compact record: { timestamp, previous_cwd, new_cwd }.
|
|
19
|
+
* 4. Atomic read-modify-write of .orchestrator/current-session.json:
|
|
20
|
+
* append to `cwd_changes` array (create if absent), keep last 20
|
|
21
|
+
* entries to bound file growth.
|
|
22
|
+
* 5. Output: nothing on stdout. Diagnostic errors to stderr only.
|
|
23
|
+
*
|
|
24
|
+
* Exit codes: 0 always (informational, never blocking).
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { readFile, writeFile, rename, mkdir } from 'node:fs/promises';
|
|
28
|
+
import path from 'node:path';
|
|
29
|
+
|
|
30
|
+
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
31
|
+
// Exit 0 immediately when disabled via SO_HOOK_PROFILE / SO_DISABLED_HOOKS.
|
|
32
|
+
if (!shouldRunHook('cwd-change-restore')) process.exit(0);
|
|
33
|
+
|
|
34
|
+
import { SO_PROJECT_DIR } from '../scripts/lib/platform.mjs';
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Constants
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
/** Maximum number of cwd_change entries retained per session. */
|
|
41
|
+
const MAX_ENTRIES = 20;
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Helpers
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Read stdin to EOF (best-effort). Returns parsed JSON or null on failure.
|
|
49
|
+
* Uses a 5 s timeout consistent with Claude Code hook contract.
|
|
50
|
+
*
|
|
51
|
+
* @returns {Promise<object|null>}
|
|
52
|
+
*/
|
|
53
|
+
function readStdinJson() {
|
|
54
|
+
return new Promise((resolve) => {
|
|
55
|
+
if (process.stdin.readableEnded || process.stdin.closed) {
|
|
56
|
+
resolve(null);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const chunks = [];
|
|
60
|
+
const timer = setTimeout(() => { resolve(null); }, 5_000);
|
|
61
|
+
process.stdin.setEncoding('utf8');
|
|
62
|
+
process.stdin.on('data', (c) => chunks.push(c));
|
|
63
|
+
process.stdin.on('end', () => {
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
const raw = chunks.join('').trim();
|
|
66
|
+
if (!raw) { resolve(null); return; }
|
|
67
|
+
try { resolve(JSON.parse(raw)); } catch { resolve(null); }
|
|
68
|
+
});
|
|
69
|
+
process.stdin.on('error', () => { clearTimeout(timer); resolve(null); });
|
|
70
|
+
process.stdin.resume();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Atomic read-modify-write of a JSON file via temp + rename.
|
|
76
|
+
* Reads the existing file (or starts with `defaultValue` when absent),
|
|
77
|
+
* applies `mutate`, writes to a tmp file, then renames over the original.
|
|
78
|
+
* Atomic on POSIX (same-filesystem rename). Best-effort on Windows.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} filePath
|
|
81
|
+
* @param {object} defaultValue — used when the file does not exist or is unparseable
|
|
82
|
+
* @param {function(object): object} mutate — synchronous pure transformer
|
|
83
|
+
*/
|
|
84
|
+
async function atomicMutateJson(filePath, defaultValue, mutate) {
|
|
85
|
+
let current = defaultValue;
|
|
86
|
+
try {
|
|
87
|
+
const raw = await readFile(filePath, 'utf8');
|
|
88
|
+
current = JSON.parse(raw);
|
|
89
|
+
} catch {
|
|
90
|
+
// File absent or unparseable — start from defaultValue.
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const updated = mutate(current);
|
|
94
|
+
const tmp = `${filePath}.tmp-cwd-${process.pid}-${Date.now()}`;
|
|
95
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
96
|
+
await writeFile(tmp, JSON.stringify(updated, null, 2) + '\n', 'utf8');
|
|
97
|
+
await rename(tmp, filePath);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// Main
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
async function main() {
|
|
105
|
+
const input = await readStdinJson();
|
|
106
|
+
|
|
107
|
+
const previousCwd =
|
|
108
|
+
typeof input?.previous_cwd === 'string' ? input.previous_cwd : null;
|
|
109
|
+
const newCwd =
|
|
110
|
+
typeof input?.new_cwd === 'string' ? input.new_cwd : null;
|
|
111
|
+
|
|
112
|
+
const record = {
|
|
113
|
+
timestamp: new Date().toISOString(),
|
|
114
|
+
previous_cwd: previousCwd,
|
|
115
|
+
new_cwd: newCwd,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const sessionFile = path.join(SO_PROJECT_DIR, '.orchestrator', 'current-session.json');
|
|
119
|
+
|
|
120
|
+
await atomicMutateJson(sessionFile, {}, (current) => {
|
|
121
|
+
const existing = Array.isArray(current.cwd_changes)
|
|
122
|
+
? current.cwd_changes
|
|
123
|
+
: [];
|
|
124
|
+
// Append the new record and cap at MAX_ENTRIES (keep most-recent).
|
|
125
|
+
const updated = [...existing, record].slice(-MAX_ENTRIES);
|
|
126
|
+
return { ...current, cwd_changes: updated };
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Exit 0 always — informational hook must never block Claude.
|
|
131
|
+
main().catch(() => {}).finally(() => process.exit(0));
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* enforce-commands.mjs — PreToolUse hook: blocks dangerous Bash commands.
|
|
4
|
+
*
|
|
5
|
+
* Node.js port of hooks/enforce-commands.sh. Part of v3.0.0 migration
|
|
6
|
+
* (Epic #124, issue #138). ESM, Node 20+, no external dependencies beyond stdlib.
|
|
7
|
+
*
|
|
8
|
+
* Decision flow (8 gates, early-exit):
|
|
9
|
+
* G1 tool filter — only Bash tool is gated
|
|
10
|
+
* G2 command present + string
|
|
11
|
+
* G3 wave-scope.json exists
|
|
12
|
+
* G4 command-guard gate enabled
|
|
13
|
+
* G5 enforcement != "off"
|
|
14
|
+
* G6 blocked pattern match against .blockedCommands[], or
|
|
15
|
+
* fallback safety list when .blockedCommands is empty
|
|
16
|
+
* G7 strict → deny (exit 2); warn → stderr + allow (exit 0); otherwise allow
|
|
17
|
+
*
|
|
18
|
+
* SECURITY-REQ-01: try/catch on main(). emitDeny on any unhandled error —
|
|
19
|
+
* exit 2, never exit 1. Null-guard readStdin() return.
|
|
20
|
+
* SECURITY-REQ-07: FALLBACK_BLOCKED includes 'git push -f' and 'drop table'
|
|
21
|
+
* (short form + case variant gaps in the original Bash fallback list).
|
|
22
|
+
* SECURITY-REQ-08: scope file read exactly once per invocation.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
26
|
+
// #211: exit 0 immediately (silent allow) when this hook is disabled via profile/env
|
|
27
|
+
if (!shouldRunHook('enforce-commands')) process.exit(0);
|
|
28
|
+
|
|
29
|
+
import path from 'node:path';
|
|
30
|
+
|
|
31
|
+
import { readStdin, emitAllow, emitDeny, emitWarn } from '../scripts/lib/io.mjs';
|
|
32
|
+
import { resolveProjectDir } from '../scripts/lib/platform.mjs';
|
|
33
|
+
import {
|
|
34
|
+
findScopeFile,
|
|
35
|
+
commandMatchesBlocked,
|
|
36
|
+
suggestForCommandBlock,
|
|
37
|
+
extractBashWriteTargets,
|
|
38
|
+
pathMatchesPattern,
|
|
39
|
+
} from '../scripts/lib/hardening.mjs';
|
|
40
|
+
import { readJson } from '../scripts/lib/common.mjs';
|
|
41
|
+
|
|
42
|
+
// Fallback safety list — applied when scope.blockedCommands is empty.
|
|
43
|
+
// Keep in sync with hooks/enforce-commands.sh; v3 additions (#138, SECURITY-REQ-07)
|
|
44
|
+
// cover the short-form git push and the lowercase SQL variant the Bash version missed.
|
|
45
|
+
const FALLBACK_BLOCKED = [
|
|
46
|
+
'rm -rf',
|
|
47
|
+
'git push --force',
|
|
48
|
+
'git push -f',
|
|
49
|
+
'git reset --hard',
|
|
50
|
+
'DROP TABLE',
|
|
51
|
+
'drop table',
|
|
52
|
+
'git checkout -- .',
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
async function main() {
|
|
56
|
+
const input = await readStdin();
|
|
57
|
+
if (!input) return emitAllow();
|
|
58
|
+
|
|
59
|
+
// G1 — only Bash is gated
|
|
60
|
+
if (input.tool_name !== 'Bash') return emitAllow();
|
|
61
|
+
|
|
62
|
+
// G2 — command must be a non-empty string
|
|
63
|
+
const command = input?.tool_input?.command;
|
|
64
|
+
if (typeof command !== 'string' || command.length === 0) return emitAllow();
|
|
65
|
+
|
|
66
|
+
const projectRoot = resolveProjectDir();
|
|
67
|
+
|
|
68
|
+
// G3 — no scope file → allow
|
|
69
|
+
const scopePath = findScopeFile(projectRoot);
|
|
70
|
+
if (!scopePath) return emitAllow();
|
|
71
|
+
|
|
72
|
+
// SECURITY-REQ-08: read scope file exactly once; use the parsed object
|
|
73
|
+
// for all subsequent gate checks.
|
|
74
|
+
let scope;
|
|
75
|
+
try {
|
|
76
|
+
scope = await readJson(scopePath);
|
|
77
|
+
} catch {
|
|
78
|
+
scope = {};
|
|
79
|
+
}
|
|
80
|
+
const enforcement = scope.enforcement || 'strict';
|
|
81
|
+
const blockedCommands = Array.isArray(scope.blockedCommands)
|
|
82
|
+
? scope.blockedCommands
|
|
83
|
+
: [];
|
|
84
|
+
const gateOn = scope?.gates?.['command-guard'] !== false;
|
|
85
|
+
|
|
86
|
+
// bash-write-guard (#800) — OPT-IN, WARN-ONLY, default OFF.
|
|
87
|
+
//
|
|
88
|
+
// INVERTED DEFAULT (deliberate divergence from the command-guard convention
|
|
89
|
+
// above, where a MISSING gates entry means ENABLED): this gate runs ONLY when
|
|
90
|
+
// `gates['bash-write-guard'] === true` is EXPLICITLY set. Conservative shell-
|
|
91
|
+
// write parsing carries a real false-positive risk (quoting, `>$VAR`, process
|
|
92
|
+
// substitution, pipes), so it stays off unless a wave opts in. It never denies
|
|
93
|
+
// and never changes the exit path — it only writes advisory stderr lines — so
|
|
94
|
+
// it is safe to run before the command-guard gate/enforcement early-returns.
|
|
95
|
+
// Skipped under enforcement:off (nothing is enforced there). See #800.
|
|
96
|
+
if (enforcement !== 'off' && scope?.gates?.['bash-write-guard'] === true) {
|
|
97
|
+
runBashWriteGuard(command, scope, projectRoot);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// G4 — gate disabled → allow
|
|
101
|
+
if (!gateOn) return emitAllow();
|
|
102
|
+
// G5 — enforcement "off" → allow
|
|
103
|
+
if (enforcement === 'off') return emitAllow();
|
|
104
|
+
|
|
105
|
+
// G6 — determine which list to check
|
|
106
|
+
const useFallback = blockedCommands.length === 0;
|
|
107
|
+
const patternsToCheck = useFallback ? FALLBACK_BLOCKED : blockedCommands;
|
|
108
|
+
|
|
109
|
+
for (const pattern of patternsToCheck) {
|
|
110
|
+
if (commandMatchesBlocked(command, pattern)) {
|
|
111
|
+
const prefix = useFallback
|
|
112
|
+
? 'Blocked by fallback safety list'
|
|
113
|
+
: 'Blocked command';
|
|
114
|
+
const reason = `${prefix}: '${pattern}' found in command`;
|
|
115
|
+
const suggestion = suggestForCommandBlock(pattern);
|
|
116
|
+
if (enforcement === 'strict') {
|
|
117
|
+
return emitDeny(reason, suggestion);
|
|
118
|
+
}
|
|
119
|
+
return emitWarn(`${reason} — ${suggestion}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// G7 — no match → allow
|
|
124
|
+
return emitAllow();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* bash-write-guard (#800) — advisory, side-effecting stderr warner.
|
|
129
|
+
*
|
|
130
|
+
* Extracts likely Bash write targets from `command`, relativises each against the
|
|
131
|
+
* project root where possible, and WARNS (stderr only) for every target that is
|
|
132
|
+
* NOT covered by the wave's allowedPaths. NEVER denies, NEVER changes the exit
|
|
133
|
+
* code — v1 is warn-only by design (#800). No event infra is pulled in: this hook
|
|
134
|
+
* has no emitEvent import, so warnings are plain stderr lines per the #800 contract.
|
|
135
|
+
*
|
|
136
|
+
* @param {string} command — raw Bash command string
|
|
137
|
+
* @param {object} scope — parsed wave-scope.json
|
|
138
|
+
* @param {string} projectRoot — absolute project root
|
|
139
|
+
*/
|
|
140
|
+
function runBashWriteGuard(command, scope, projectRoot) {
|
|
141
|
+
const allowedPaths = Array.isArray(scope.allowedPaths) ? scope.allowedPaths : [];
|
|
142
|
+
const targets = extractBashWriteTargets(command);
|
|
143
|
+
for (const target of targets) {
|
|
144
|
+
if (!targetInWaveScope(target, allowedPaths, projectRoot)) {
|
|
145
|
+
process.stderr.write(`bash-write-guard: ${target} outside wave scope (warn-only, #800)\n`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Is a write target covered by the wave's allowedPaths? Reuses the same
|
|
152
|
+
* `pathMatchesPattern` matcher the enforce-scope path gate uses (no bespoke
|
|
153
|
+
* matching). Absolute targets inside the project root are relativised first;
|
|
154
|
+
* both the relative and raw forms are tried so an in-scope target never warns.
|
|
155
|
+
*
|
|
156
|
+
* @param {string} target — verbatim write target from extractBashWriteTargets
|
|
157
|
+
* @param {string[]} allowedPaths — wave allowedPaths union
|
|
158
|
+
* @param {string} projectRoot — absolute project root
|
|
159
|
+
* @returns {boolean}
|
|
160
|
+
*/
|
|
161
|
+
function targetInWaveScope(target, allowedPaths, projectRoot) {
|
|
162
|
+
if (!Array.isArray(allowedPaths) || allowedPaths.length === 0) return false;
|
|
163
|
+
let rel = target;
|
|
164
|
+
if (path.isAbsolute(target)) {
|
|
165
|
+
const fromRoot = path.relative(projectRoot, target);
|
|
166
|
+
if (fromRoot && !fromRoot.startsWith('..') && !path.isAbsolute(fromRoot)) {
|
|
167
|
+
rel = fromRoot;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const norm = rel.split(path.sep).join('/').replace(/^\.\//, '');
|
|
171
|
+
return allowedPaths.some(
|
|
172
|
+
(p) => pathMatchesPattern(norm, p) || pathMatchesPattern(target, p),
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// SECURITY-REQ-01 (F-03): top-level try/catch — never let exit 1 leak.
|
|
177
|
+
main().catch((e) => {
|
|
178
|
+
emitDeny('Internal hook error — request blocked for safety', `${e?.message || e}`);
|
|
179
|
+
});
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* enforce-scope.mjs — PreToolUse hook: block Edit/Write/MultiEdit outside allowed wave paths.
|
|
4
|
+
*
|
|
5
|
+
* Replaces enforce-scope.sh (87-line Bash). Part of v3.0.0 Windows-native migration.
|
|
6
|
+
* Issue: github.com/Kanevry/session-orchestrator/issues/137
|
|
7
|
+
*
|
|
8
|
+
* Decision flow (8 gates + one pre-gate, early-exit):
|
|
9
|
+
* G1 tool filter — only Edit/Write/MultiEdit are gated
|
|
10
|
+
* G2 file_path present + string
|
|
11
|
+
* G3 wave-scope.json exists
|
|
12
|
+
* G4 path-guard gate enabled
|
|
13
|
+
* G5 enforcement != "off"
|
|
14
|
+
* G5b (#792) allowlist-first: an EXPLICIT absolute allowedPaths entry that
|
|
15
|
+
* matches the fully realpath-resolved candidate → allow, BEFORE G6.
|
|
16
|
+
* Runs before G6 so a deliberate out-of-repo grant (e.g. a vault path)
|
|
17
|
+
* is reachable at all — G6 would otherwise deny every out-of-repo path
|
|
18
|
+
* without ever consulting allowedPaths. See matchesAbsoluteAllowlist.
|
|
19
|
+
* G6 resolved path inside project root
|
|
20
|
+
* G7 relative path matches an allowedPaths pattern
|
|
21
|
+
* G8 (all passed) → allow
|
|
22
|
+
*
|
|
23
|
+
* Exit codes: 0 = allow 2 = deny
|
|
24
|
+
*
|
|
25
|
+
* SECURITY notes (inline refs):
|
|
26
|
+
* REQ-01 top-level try/catch → emitDeny on unexpected error (fail-closed)
|
|
27
|
+
* REQ-03 realpath(dirname) to resolve symlinks; ENOENT → fall back to path.resolve
|
|
28
|
+
* REQ-04 relativeFromRoot() === null → deny as outside-root
|
|
29
|
+
* REQ-05 normalize path separators to "/" before pathMatchesPattern (Windows compat)
|
|
30
|
+
* REQ-06 relative file_path resolved against projectRoot, not process.cwd()
|
|
31
|
+
* REQ-08 wave-scope.json read once; parsed object passed to all gate checks
|
|
32
|
+
* REQ-09 (#792) G5b out-of-repo carveout is structurally safe: RELATIVE
|
|
33
|
+
* allowedPaths entries can NEVER match an out-of-repo path (the
|
|
34
|
+
* isAbsolute filter drops them), so `**` / `../**` cannot be used to
|
|
35
|
+
* escape the repo; only entries that are themselves absolute match,
|
|
36
|
+
* and only against their own literal (canonical/realpath) subtree.
|
|
37
|
+
* Empty absolute set → the pre-gate is inert (byte-identical to the
|
|
38
|
+
* pre-#792 behaviour).
|
|
39
|
+
*
|
|
40
|
+
* Coordinator carveout (#245): a short, explicit list of harness-owned files
|
|
41
|
+
* bypasses Gate 7 (allowedPaths glob) — specifically STATE.md across all platform
|
|
42
|
+
* state dirs and the wave-scope.json manifest itself. Coordinators write these
|
|
43
|
+
* between waves as part of the harness protocol; subjecting them to per-wave
|
|
44
|
+
* allowedPaths would force every wave plan to re-list harness infrastructure.
|
|
45
|
+
* Project-root containment (Gate 6) and enforcement-off (Gate 5) still apply.
|
|
46
|
+
* No wildcards — exact string match only.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
import path from 'node:path';
|
|
50
|
+
import { promises as fs } from 'node:fs';
|
|
51
|
+
|
|
52
|
+
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
53
|
+
// #211: exit 0 immediately (silent allow) when this hook is disabled via profile/env
|
|
54
|
+
if (!shouldRunHook('enforce-scope')) process.exit(0);
|
|
55
|
+
|
|
56
|
+
import { readStdin, emitAllow, emitDeny, emitWarn } from '../scripts/lib/io.mjs';
|
|
57
|
+
import { isPathInside, relativeFromRoot } from '../scripts/lib/path-utils.mjs';
|
|
58
|
+
import { resolveProjectDir } from '../scripts/lib/platform.mjs';
|
|
59
|
+
import {
|
|
60
|
+
findScopeFile,
|
|
61
|
+
pathMatchesPattern,
|
|
62
|
+
suggestForScopeViolation,
|
|
63
|
+
} from '../scripts/lib/hardening.mjs';
|
|
64
|
+
import { readJson } from '../scripts/lib/common.mjs';
|
|
65
|
+
|
|
66
|
+
async function main() {
|
|
67
|
+
// SECURITY-REQ-01: null-guard empty stdin — treat as allow (no input = not a real hook call)
|
|
68
|
+
const input = await readStdin();
|
|
69
|
+
if (!input) return emitAllow();
|
|
70
|
+
|
|
71
|
+
const toolName = input.tool_name;
|
|
72
|
+
const filePath = input?.tool_input?.file_path;
|
|
73
|
+
|
|
74
|
+
// Gate 1: only Edit, Write, and MultiEdit are path-gated
|
|
75
|
+
if (toolName !== 'Edit' && toolName !== 'Write' && toolName !== 'MultiEdit') return emitAllow();
|
|
76
|
+
|
|
77
|
+
// Gate 2: file_path must be a non-empty string
|
|
78
|
+
if (!filePath || typeof filePath !== 'string') return emitAllow();
|
|
79
|
+
|
|
80
|
+
const projectRootRaw = resolveProjectDir();
|
|
81
|
+
|
|
82
|
+
// Resolve symlinks in the project root itself so that realpath(file) comparisons
|
|
83
|
+
// are consistent. On macOS /tmp → /private/tmp; mismatches would cause false denials.
|
|
84
|
+
let projectRoot;
|
|
85
|
+
try {
|
|
86
|
+
projectRoot = await fs.realpath(projectRootRaw);
|
|
87
|
+
} catch {
|
|
88
|
+
projectRoot = projectRootRaw;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Gate 3: no wave-scope.json → nothing to enforce
|
|
92
|
+
const scopePath = findScopeFile(projectRoot);
|
|
93
|
+
if (!scopePath) return emitAllow();
|
|
94
|
+
|
|
95
|
+
// SECURITY-REQ-08: read scope file once; pass parsed object to all subsequent checks
|
|
96
|
+
let scope;
|
|
97
|
+
try {
|
|
98
|
+
scope = await readJson(scopePath);
|
|
99
|
+
} catch {
|
|
100
|
+
scope = {};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const enforcement = scope.enforcement ?? 'strict';
|
|
104
|
+
const allowedPaths = Array.isArray(scope.allowedPaths) ? scope.allowedPaths : [];
|
|
105
|
+
const gatesEnabled = scope.gates?.['path-guard'] !== false;
|
|
106
|
+
|
|
107
|
+
// Gate 4: path-guard gate explicitly disabled
|
|
108
|
+
if (!gatesEnabled) return emitAllow();
|
|
109
|
+
|
|
110
|
+
// Gate 5: enforcement is turned off
|
|
111
|
+
if (enforcement === 'off') return emitAllow();
|
|
112
|
+
|
|
113
|
+
// SECURITY-REQ-06: resolve relative file_path against projectRoot, not process.cwd()
|
|
114
|
+
const absPathInput = path.isAbsolute(filePath)
|
|
115
|
+
? filePath
|
|
116
|
+
: path.resolve(projectRoot, filePath);
|
|
117
|
+
|
|
118
|
+
// SECURITY-REQ-03: resolve symlinks to prevent symlink-escape.
|
|
119
|
+
// Strategy:
|
|
120
|
+
// 1. Try fs.realpath on the file itself — follows symlinks (blocks `src/evil → /etc/passwd`).
|
|
121
|
+
// 2. On ENOENT (file doesn't exist yet — common for Write), walk up to the
|
|
122
|
+
// nearest existing ancestor, realpath it, then re-attach the non-existent
|
|
123
|
+
// suffix. Keeps projectRoot and resolvedPath in the same canonical namespace
|
|
124
|
+
// (e.g. both under /private/tmp on macOS).
|
|
125
|
+
// 3. Any non-ENOENT fs error → fail-closed.
|
|
126
|
+
let resolvedPath;
|
|
127
|
+
try {
|
|
128
|
+
resolvedPath = await fs.realpath(absPathInput);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
if (err?.code !== 'ENOENT') {
|
|
131
|
+
return emitDeny(
|
|
132
|
+
'Scope check failed — unable to resolve file path',
|
|
133
|
+
`Internal: ${err?.code ?? 'unknown'}`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
// File doesn't exist yet — walk up to nearest existing ancestor.
|
|
137
|
+
try {
|
|
138
|
+
const segments = [path.basename(absPathInput)];
|
|
139
|
+
let ancestor = path.dirname(absPathInput);
|
|
140
|
+
while (true) {
|
|
141
|
+
try {
|
|
142
|
+
const realAncestor = await fs.realpath(ancestor);
|
|
143
|
+
segments.reverse();
|
|
144
|
+
resolvedPath = path.join(realAncestor, ...segments);
|
|
145
|
+
break;
|
|
146
|
+
} catch (e) {
|
|
147
|
+
if (e?.code !== 'ENOENT') throw e;
|
|
148
|
+
const parent = path.dirname(ancestor);
|
|
149
|
+
if (parent === ancestor) {
|
|
150
|
+
// Hit filesystem root (should not occur for paths under projectRoot).
|
|
151
|
+
resolvedPath = absPathInput;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
segments.push(path.basename(ancestor));
|
|
155
|
+
ancestor = parent;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
} catch (e) {
|
|
159
|
+
return emitDeny(
|
|
160
|
+
'Scope check failed — unable to resolve file path',
|
|
161
|
+
`Internal: ${e?.code ?? 'unknown'}`,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Gate 5b (#792): allowlist-first for explicit absolute (out-of-repo) grants.
|
|
167
|
+
// An EXPLICIT absolute allowedPaths entry (e.g. a coordinator-granted vault
|
|
168
|
+
// path) is structurally unreachable under Gate 6, which denies every
|
|
169
|
+
// out-of-repo path before allowedPaths is ever consulted. Honour such grants
|
|
170
|
+
// here — matching ONLY absolute entries against the fully realpath-resolved
|
|
171
|
+
// candidate, so relative entries can never be used to escape the repo (REQ-09).
|
|
172
|
+
if (matchesAbsoluteAllowlist(resolvedPath, allowedPaths)) return emitAllow();
|
|
173
|
+
|
|
174
|
+
// Gate 6: path must be inside the project root
|
|
175
|
+
if (!isPathInside(resolvedPath, projectRoot)) {
|
|
176
|
+
const reason = `Scope violation: path outside project root`;
|
|
177
|
+
const suggestion = suggestForScopeViolation(filePath, allowedPaths.join(', '));
|
|
178
|
+
return enforcement === 'strict'
|
|
179
|
+
? emitDeny(reason, suggestion)
|
|
180
|
+
: emitWarn(`${reason} — ${suggestion}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Compute the relative path from project root to the resolved file path
|
|
184
|
+
const relPath = relativeFromRoot(projectRoot, resolvedPath);
|
|
185
|
+
|
|
186
|
+
// SECURITY-REQ-04: null return means outside root — deny rather than pass null to pathMatchesPattern
|
|
187
|
+
if (relPath === null) {
|
|
188
|
+
const reason = `Scope violation: '${filePath}' outside project root`;
|
|
189
|
+
const suggestion = suggestForScopeViolation(filePath, allowedPaths.join(', '));
|
|
190
|
+
return enforcement === 'strict'
|
|
191
|
+
? emitDeny(reason, suggestion)
|
|
192
|
+
: emitWarn(`${reason} — ${suggestion}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// SECURITY-REQ-05: normalize Windows path separators to '/' before glob matching
|
|
196
|
+
const normalizedRel = relPath.split(path.sep).join('/');
|
|
197
|
+
|
|
198
|
+
// Coordinator carveout (#245): exact-path allowlist for harness-owned files.
|
|
199
|
+
// STATE.md and wave-scope.json are written by the coordinator between waves;
|
|
200
|
+
// per-wave allowedPaths lists should not need to enumerate harness infrastructure.
|
|
201
|
+
if (isCoordinatorCarveout(normalizedRel, projectRoot, scopePath)) {
|
|
202
|
+
return emitAllow();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Gate 7: check normalised relative path against each allowedPaths pattern
|
|
206
|
+
const matched = allowedPaths.some((pattern) => pathMatchesPattern(normalizedRel, pattern));
|
|
207
|
+
|
|
208
|
+
if (!matched) {
|
|
209
|
+
const reason = `Scope violation: '${normalizedRel}' not in allowed paths [${allowedPaths.join(', ')}]`;
|
|
210
|
+
const suggestion = suggestForScopeViolation(normalizedRel, allowedPaths.join(', '));
|
|
211
|
+
return enforcement === 'strict'
|
|
212
|
+
? emitDeny(reason, suggestion)
|
|
213
|
+
: emitWarn(`${reason} — ${suggestion}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Gate 8 / all gates passed → allow
|
|
217
|
+
return emitAllow();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const COORDINATOR_CARVEOUT_PATHS = Object.freeze([
|
|
221
|
+
'.claude/STATE.md',
|
|
222
|
+
'.codex/STATE.md',
|
|
223
|
+
'.cursor/STATE.md',
|
|
224
|
+
'.pi/STATE.md',
|
|
225
|
+
]);
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Returns true if the given project-relative, forward-slash-normalized path is
|
|
229
|
+
* one of the harness-owned files the coordinator writes between waves.
|
|
230
|
+
*
|
|
231
|
+
* Matches STATE.md across all platform state dirs and the wave-scope.json the
|
|
232
|
+
* hook just read (its exact, resolved relative path — no wildcard). Exact
|
|
233
|
+
* string comparison only; any glob semantics belong in Gate 7.
|
|
234
|
+
*/
|
|
235
|
+
function isCoordinatorCarveout(normalizedRel, projectRoot, scopePath) {
|
|
236
|
+
if (COORDINATOR_CARVEOUT_PATHS.includes(normalizedRel)) return true;
|
|
237
|
+
const scopeRel = relativeFromRoot(projectRoot, scopePath);
|
|
238
|
+
if (scopeRel === null) return false;
|
|
239
|
+
return scopeRel.split(path.sep).join('/') === normalizedRel;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* #792: Honour EXPLICIT absolute allowedPaths entries that point OUTSIDE the repo
|
|
244
|
+
* (a deliberate coordinator grant, e.g. "/Users/x/Projects/vault/**").
|
|
245
|
+
* Such entries are structurally unreachable under the default relative-only
|
|
246
|
+
* matcher because Gate 6 denies out-of-repo paths first, and Gate 7 only ever
|
|
247
|
+
* matches the ROOT-RELATIVE candidate (an absolute pattern can never match it).
|
|
248
|
+
*
|
|
249
|
+
* SECURITY (REQ-09): matches ONLY entries that are themselves absolute, against
|
|
250
|
+
* the fully realpath-resolved candidate — so RELATIVE entries (`**`, `../**`)
|
|
251
|
+
* can NEVER be used to escape the repo. When no absolute entry exists the helper
|
|
252
|
+
* returns false and the caller falls through to Gate 6 unchanged (inert pre-gate).
|
|
253
|
+
* An absolute entry matches only its own literal (canonical/realpath) subtree;
|
|
254
|
+
* the operator is responsible for supplying a canonical absolute path.
|
|
255
|
+
*
|
|
256
|
+
* @param {string} resolvedPath — fully realpath-resolved candidate (absolute)
|
|
257
|
+
* @param {string[]} allowedPaths — raw allowedPaths array from wave-scope.json
|
|
258
|
+
* @returns {boolean}
|
|
259
|
+
*/
|
|
260
|
+
function matchesAbsoluteAllowlist(resolvedPath, allowedPaths) {
|
|
261
|
+
const abs = allowedPaths.filter((p) => typeof p === 'string' && path.isAbsolute(p));
|
|
262
|
+
if (abs.length === 0) return false;
|
|
263
|
+
const normalizedAbs = resolvedPath.split(path.sep).join('/');
|
|
264
|
+
return abs.some((pat) => pathMatchesPattern(normalizedAbs, pat.split(path.sep).join('/')));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// SECURITY-REQ-01 (fail-closed): any unhandled rejection → structured deny, never bare exit 1
|
|
268
|
+
main().catch((e) => {
|
|
269
|
+
emitDeny(
|
|
270
|
+
'Internal hook error — request blocked for safety',
|
|
271
|
+
`${e?.message ?? String(e)}`,
|
|
272
|
+
);
|
|
273
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Synchronous Codex hooks for payload-compatible Session Orchestrator handlers.",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"SessionStart": [
|
|
5
|
+
{
|
|
6
|
+
"matcher": "startup|resume|clear|compact",
|
|
7
|
+
"hooks": [
|
|
8
|
+
{
|
|
9
|
+
"type": "command",
|
|
10
|
+
"command": "echo '🎯 Session Orchestrator v3.16.0 — /session [housekeeping|feature|deep] | /plan [new|feature|retro] | /discovery [scope] | /evolve [analyze|review|list]'",
|
|
11
|
+
"async": false
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"type": "command",
|
|
15
|
+
"command": "SO_PLATFORM=codex CODEX_PLUGIN_ROOT=\"${PLUGIN_ROOT}\" sh \"${PLUGIN_ROOT}/hooks/run-node.sh\" \"${PLUGIN_ROOT}/hooks/on-session-start.mjs\"",
|
|
16
|
+
"timeout": 5
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"PreToolUse": [],
|
|
22
|
+
"PostToolUse": [
|
|
23
|
+
{
|
|
24
|
+
"matcher": "*",
|
|
25
|
+
"hooks": [
|
|
26
|
+
{
|
|
27
|
+
"type": "command",
|
|
28
|
+
"command": "SO_PLATFORM=codex CODEX_PLUGIN_ROOT=\"${PLUGIN_ROOT}\" sh \"${PLUGIN_ROOT}/hooks/run-node.sh\" \"${PLUGIN_ROOT}/hooks/loop-guard.mjs\"",
|
|
29
|
+
"timeout": 5
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"SubagentStart": [],
|
|
35
|
+
"SubagentStop": [
|
|
36
|
+
{
|
|
37
|
+
"matcher": "",
|
|
38
|
+
"hooks": [
|
|
39
|
+
{
|
|
40
|
+
"type": "command",
|
|
41
|
+
"command": "SO_PLATFORM=codex CODEX_PLUGIN_ROOT=\"${PLUGIN_ROOT}\" sh \"${PLUGIN_ROOT}/hooks/run-node.sh\" \"${PLUGIN_ROOT}/hooks/on-stop.mjs\"",
|
|
42
|
+
"timeout": 5
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"Stop": [
|
|
48
|
+
{
|
|
49
|
+
"matcher": "",
|
|
50
|
+
"hooks": [
|
|
51
|
+
{
|
|
52
|
+
"type": "command",
|
|
53
|
+
"command": "SO_PLATFORM=codex CODEX_PLUGIN_ROOT=\"${PLUGIN_ROOT}\" sh \"${PLUGIN_ROOT}/hooks/run-node.sh\" \"${PLUGIN_ROOT}/hooks/on-stop.mjs\"",
|
|
54
|
+
"timeout": 5
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Cursor IDE hook mapping reference. Configure hooks in Cursor Settings > Hooks.",
|
|
3
|
+
"_note": "Cursor hooks fire at different lifecycle points than Claude Code. See docs/cursor-setup.md for details.",
|
|
4
|
+
"_limitation": "SessionStart greeting (session-start skill) is NOT available on Cursor. Cursor has no conversation-start lifecycle event equivalent to Claude Code's UserPromptSubmit hook. Session initialisation must be triggered manually.",
|
|
5
|
+
"hooks": {
|
|
6
|
+
"afterFileEdit": {
|
|
7
|
+
"script": "hooks/enforce-scope.mjs",
|
|
8
|
+
"note": "Post-hoc scope warning — Cursor fires AFTER the edit (not before). Cannot prevent out-of-scope edits, only warn."
|
|
9
|
+
},
|
|
10
|
+
"beforeShellExecution": {
|
|
11
|
+
"script": "hooks/enforce-commands.mjs",
|
|
12
|
+
"note": "Blocks dangerous commands before shell execution. Direct equivalent of Claude Code's PreToolUse Bash hook."
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|