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,294 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* on-stop.mjs — consolidated Stop + SubagentStop hook.
|
|
4
|
+
*
|
|
5
|
+
* Replaces hooks/on-stop.sh and hooks/on-subagent-stop.sh. Handles both Claude Code
|
|
6
|
+
* hook events in a single file, discriminating by the `hook_event_name` field first,
|
|
7
|
+
* then falling back to presence of `agent_type` (SubagentStop) vs absence (Stop).
|
|
8
|
+
*
|
|
9
|
+
* Part of v3.0.0 Windows-native migration. Issue #141.
|
|
10
|
+
*
|
|
11
|
+
* Exit codes: 0 always (informational hooks must never block).
|
|
12
|
+
*
|
|
13
|
+
* JSONL format (`.orchestrator/metrics/events.jsonl`) — emitted via the canonical
|
|
14
|
+
* `emitEvent()` so the JSONL record and the optional Clank webhook always carry the
|
|
15
|
+
* SAME dotted event name (was: bare `stop`/`subagent_stop` in JSONL vs dotted in webhook):
|
|
16
|
+
* Stop: {"timestamp":<ISO>,"event":"orchestrator.session.stopped","session_id":"...","wave":<int>,"branch":"...","commit":"...","duration_ms":<int>}
|
|
17
|
+
* SubagentStop: {"timestamp":<ISO>,"event":"orchestrator.agent.stopped","agent":"<name>"}
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import { promises as fs } from 'node:fs';
|
|
22
|
+
import { $ } from 'zx';
|
|
23
|
+
|
|
24
|
+
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
25
|
+
// #211: exit 0 immediately (silent allow) when this hook is disabled via profile/env
|
|
26
|
+
if (!shouldRunHook('on-stop')) process.exit(0);
|
|
27
|
+
|
|
28
|
+
import { emitEvent } from '../scripts/lib/events.mjs';
|
|
29
|
+
import { SO_PROJECT_DIR } from '../scripts/lib/platform.mjs';
|
|
30
|
+
import { deregisterSelf, logSweepEvent } from '../scripts/lib/session-registry.mjs';
|
|
31
|
+
import { updateHeartbeat } from '../scripts/lib/session-lock.mjs';
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// stdin reading (inline — no io.mjs because Stop hooks exit 0 always, never deny)
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Read stdin to EOF and parse as JSON. Returns null on empty or parse failure.
|
|
39
|
+
* @returns {Promise<object|null>}
|
|
40
|
+
*/
|
|
41
|
+
async function readStdinJson() {
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
const chunks = [];
|
|
44
|
+
const timer = setTimeout(() => {
|
|
45
|
+
process.stdin.destroy();
|
|
46
|
+
resolve(null);
|
|
47
|
+
}, 8000); // generous guard; contract says 5s (Stop) / 3s (SubagentStop)
|
|
48
|
+
|
|
49
|
+
if (process.stdin.readableEnded) {
|
|
50
|
+
clearTimeout(timer);
|
|
51
|
+
resolve(null);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
process.stdin.setEncoding('utf8');
|
|
56
|
+
process.stdin.on('data', (chunk) => chunks.push(chunk));
|
|
57
|
+
process.stdin.on('end', () => {
|
|
58
|
+
clearTimeout(timer);
|
|
59
|
+
const raw = chunks.join('').trim();
|
|
60
|
+
if (!raw) { resolve(null); return; }
|
|
61
|
+
try { resolve(JSON.parse(raw)); } catch { resolve(null); }
|
|
62
|
+
});
|
|
63
|
+
process.stdin.on('error', () => { clearTimeout(timer); resolve(null); });
|
|
64
|
+
process.stdin.resume();
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// discriminate hook event type
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Determine whether the parsed stdin represents a Stop or SubagentStop event.
|
|
74
|
+
* Precedence: hook_event_name field → presence of agent_type field → default Stop.
|
|
75
|
+
* @param {object|null} input
|
|
76
|
+
* @returns {"stop"|"subagent_stop"}
|
|
77
|
+
*/
|
|
78
|
+
function discriminate(input) {
|
|
79
|
+
if (!input) return 'stop';
|
|
80
|
+
const name = input.hook_event_name;
|
|
81
|
+
if (typeof name === 'string') {
|
|
82
|
+
if (name === 'SubagentStop') return 'subagent_stop';
|
|
83
|
+
return 'stop';
|
|
84
|
+
}
|
|
85
|
+
// Fallback: SubagentStop always provides agent_type; Stop does not.
|
|
86
|
+
if (typeof input.agent_type === 'string') return 'subagent_stop';
|
|
87
|
+
return 'stop';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// git helpers
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Returns { commit, branch } from the git repo at projectRoot, or null values
|
|
96
|
+
* if git is unavailable or the directory is not a git repo.
|
|
97
|
+
* @param {string} projectRoot — working directory for git commands
|
|
98
|
+
* @returns {Promise<{commit:string|null, branch:string|null}>}
|
|
99
|
+
*/
|
|
100
|
+
async function gitInfo(projectRoot) {
|
|
101
|
+
$.verbose = false;
|
|
102
|
+
$.quiet = true;
|
|
103
|
+
const opts = projectRoot ? { cwd: projectRoot } : {};
|
|
104
|
+
try {
|
|
105
|
+
const commitResult = await $({ ...opts })`git rev-parse HEAD`;
|
|
106
|
+
const branchResult = await $({ ...opts })`git rev-parse --abbrev-ref HEAD`;
|
|
107
|
+
return {
|
|
108
|
+
commit: commitResult.stdout.trim() || null,
|
|
109
|
+
branch: branchResult.stdout.trim() || null,
|
|
110
|
+
};
|
|
111
|
+
} catch {
|
|
112
|
+
return { commit: null, branch: null };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
// wave-scope.json helpers
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Try to read the wave number from .claude/wave-scope.json (or .codex / .cursor / .pi).
|
|
122
|
+
* Returns 0 if no scope file is found or the file cannot be parsed.
|
|
123
|
+
* @param {string} projectRoot
|
|
124
|
+
* @returns {Promise<number>}
|
|
125
|
+
*/
|
|
126
|
+
async function readWaveNumber(projectRoot) {
|
|
127
|
+
const dirs = ['.pi', '.claude', '.codex', '.cursor'];
|
|
128
|
+
for (const dir of dirs) {
|
|
129
|
+
const scopePath = path.join(projectRoot, dir, 'wave-scope.json');
|
|
130
|
+
try {
|
|
131
|
+
const raw = await fs.readFile(scopePath, 'utf8');
|
|
132
|
+
const obj = JSON.parse(raw);
|
|
133
|
+
const wave = typeof obj.wave === 'number' ? obj.wave : 0;
|
|
134
|
+
return wave;
|
|
135
|
+
} catch {
|
|
136
|
+
// file missing or unparseable — try next
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// event handlers
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Resolve this session's id. Stdin payload wins. If Claude Code did not pass
|
|
148
|
+
* one (Codex / Cursor paths, or older harnesses), fall back to
|
|
149
|
+
* `.orchestrator/current-session.json` which on-session-start.mjs writes.
|
|
150
|
+
*
|
|
151
|
+
* @param {object|null} input
|
|
152
|
+
* @param {string} projectRoot
|
|
153
|
+
* @returns {Promise<string|null>}
|
|
154
|
+
*/
|
|
155
|
+
async function resolveSessionId(input, projectRoot) {
|
|
156
|
+
const fromStdin = input?.session_id ?? input?.sessionId ?? null;
|
|
157
|
+
if (typeof fromStdin === 'string' && fromStdin.length > 0) return fromStdin;
|
|
158
|
+
try {
|
|
159
|
+
const raw = await fs.readFile(
|
|
160
|
+
path.join(projectRoot, '.orchestrator', 'current-session.json'),
|
|
161
|
+
'utf8',
|
|
162
|
+
);
|
|
163
|
+
const parsed = JSON.parse(raw);
|
|
164
|
+
if (typeof parsed.session_id === 'string' && parsed.session_id.length > 0) {
|
|
165
|
+
return parsed.session_id;
|
|
166
|
+
}
|
|
167
|
+
} catch { /* missing or unparseable is fine */ }
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Handle a Stop event. Reads wave from scope file + git info, appends JSONL.
|
|
173
|
+
* @param {object|null} input
|
|
174
|
+
*/
|
|
175
|
+
async function handleStop(input) {
|
|
176
|
+
const projectRoot = SO_PROJECT_DIR;
|
|
177
|
+
|
|
178
|
+
const wave = await readWaveNumber(projectRoot);
|
|
179
|
+
const { commit, branch } = await gitInfo(projectRoot);
|
|
180
|
+
|
|
181
|
+
const sessionId = await resolveSessionId(input, projectRoot);
|
|
182
|
+
|
|
183
|
+
// v3.1.0 multi-session registry (#169) — best-effort deregister. Missing
|
|
184
|
+
// entry is fine (zombie sweep handles crashed sessions). Failures are logged
|
|
185
|
+
// to sweep.log for observability but never re-thrown (hook must remain silent).
|
|
186
|
+
if (sessionId) {
|
|
187
|
+
try {
|
|
188
|
+
await deregisterSelf(sessionId);
|
|
189
|
+
} catch (err) {
|
|
190
|
+
// Deregistration failed — emit an observability breadcrumb to sweep.log.
|
|
191
|
+
// Do NOT throw, do NOT write to stderr: the hook is informational-only.
|
|
192
|
+
logSweepEvent({ event: 'deregister-failed', session_id: sessionId, error: err?.message ?? String(err) });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Epic #583 W5-F1c — refresh session.lock heartbeat on every turn-end.
|
|
196
|
+
// Augments PostToolBatch's heartbeat (closes W4-Q3 H2 cadence finding:
|
|
197
|
+
// a session with no PostToolBatch activity would otherwise go heartbeat-stale).
|
|
198
|
+
// Best-effort: never throws, never blocks the Stop hook.
|
|
199
|
+
try {
|
|
200
|
+
updateHeartbeat({ sessionId, repoRoot: projectRoot });
|
|
201
|
+
} catch { /* best-effort */ }
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// duration_ms: if input provides a start time we compute from it, else 0
|
|
205
|
+
const durationMs =
|
|
206
|
+
typeof input?.start_ms === 'number' ? Date.now() - input.start_ms : 0;
|
|
207
|
+
|
|
208
|
+
// Single emission path: emitEvent writes the canonical {timestamp, event, ...payload}
|
|
209
|
+
// JSONL record AND fires the optional Clank webhook with the SAME event name — no
|
|
210
|
+
// more bare-`stop` (JSONL) vs dotted-`stopped` (webhook) divergence.
|
|
211
|
+
await emitEvent('orchestrator.session.stopped', {
|
|
212
|
+
...(sessionId !== null ? { session_id: sessionId } : {}),
|
|
213
|
+
wave,
|
|
214
|
+
...(branch !== null ? { branch } : {}),
|
|
215
|
+
...(commit !== null ? { commit } : {}),
|
|
216
|
+
duration_ms: durationMs,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Handle a SubagentStop event. Extracts agent name, emits orchestrator.agent.stopped.
|
|
222
|
+
* Returns an optional additionalContext string (v2.1.163+) to feed back to the
|
|
223
|
+
* coordinator turn. Currently always returns null (no inline warning from this
|
|
224
|
+
* handler) — the slot is reserved for future SubagentStop feedback.
|
|
225
|
+
* @param {object|null} input
|
|
226
|
+
* @returns {Promise<string|null>} additionalContext or null
|
|
227
|
+
*/
|
|
228
|
+
async function handleSubagentStop(input) {
|
|
229
|
+
const agent = input?.agent_type ?? 'unknown';
|
|
230
|
+
await emitEvent('orchestrator.agent.stopped', { agent });
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
// main
|
|
236
|
+
// ---------------------------------------------------------------------------
|
|
237
|
+
|
|
238
|
+
async function main() {
|
|
239
|
+
const input = await readStdinJson();
|
|
240
|
+
const eventType = discriminate(input);
|
|
241
|
+
|
|
242
|
+
if (eventType === 'subagent_stop') {
|
|
243
|
+
const additionalContext = await handleSubagentStop(input);
|
|
244
|
+
// v2.1.163+: emit hookSpecificOutput for SubagentStop path.
|
|
245
|
+
// If handleSubagentStop returns a non-empty context string, feed it back
|
|
246
|
+
// to the coordinator turn. Currently returns null (no inline warning from
|
|
247
|
+
// this handler), but the slot is live for future SubagentStop feedback.
|
|
248
|
+
// terminalSequence is a Stop-only field — not emitted for SubagentStop.
|
|
249
|
+
if (typeof additionalContext === 'string' && additionalContext.length > 0) {
|
|
250
|
+
process.stdout.write(JSON.stringify({
|
|
251
|
+
hookSpecificOutput: {
|
|
252
|
+
hookEventName: 'SubagentStop',
|
|
253
|
+
additionalContext,
|
|
254
|
+
},
|
|
255
|
+
}));
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
// terminalSequence must be emitted even when handleStop() throws (e.g.
|
|
259
|
+
// emitEvent's fs.appendFile fails on a full / read-only disk). Wrap the
|
|
260
|
+
// Stop branch in try/finally so the desktop notification is always sent.
|
|
261
|
+
// SubagentStop is intentionally outside this block — it must never emit
|
|
262
|
+
// terminalSequence.
|
|
263
|
+
try {
|
|
264
|
+
await handleStop(input);
|
|
265
|
+
} finally {
|
|
266
|
+
// terminalSequence is only meaningful for Stop (session-level) events.
|
|
267
|
+
process.stdout.write(buildTerminalSequenceJson());
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
// terminal notification
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Emit a cross-platform desktop notification via the CC 2.1.141+ terminalSequence
|
|
278
|
+
* output field. Supports OSC 9 (iTerm2, Windows Terminal, WezTerm, ConEmu) and
|
|
279
|
+
* OSC 777 (Ghostty, urxvt, Warp). Both sequences are emitted together; unsupported
|
|
280
|
+
* terminals silently ignore. Returns the JSON string to write to stdout.
|
|
281
|
+
* @returns {string}
|
|
282
|
+
*/
|
|
283
|
+
function buildTerminalSequenceJson() {
|
|
284
|
+
const title = 'Claude Code';
|
|
285
|
+
const body = 'Session stopped — your turn';
|
|
286
|
+
const osc9 = `\x1b]9;${title}: ${body}\x07`;
|
|
287
|
+
const osc777 = `\x1b]777;notify;${title};${body}\x07`;
|
|
288
|
+
return JSON.stringify({ terminalSequence: osc9 + osc777 });
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Exit 0 always — informational hook must never block Claude.
|
|
292
|
+
main()
|
|
293
|
+
.catch(() => {})
|
|
294
|
+
.finally(() => process.exit(0));
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* operator-steer.mjs — PostToolBatch hook for operator in-band steering.
|
|
4
|
+
*
|
|
5
|
+
* Hook event: PostToolBatch (issue #409).
|
|
6
|
+
* Fires after a batch of tool invocations completes within a single wave turn.
|
|
7
|
+
* Reads `.orchestrator/STEER.md` and, if non-empty, emits its contents as a
|
|
8
|
+
* `systemMessage` payload to stdout so Claude Code injects the guidance into
|
|
9
|
+
* the active session context. The file is then truncated (set to empty) so the
|
|
10
|
+
* message fires exactly once per write — a one-shot handshake.
|
|
11
|
+
*
|
|
12
|
+
* Operator workflow:
|
|
13
|
+
* echo "Focus on error handling in wave 3" > .orchestrator/STEER.md
|
|
14
|
+
* # Claude picks it up at the next PostToolBatch boundary, clears the file.
|
|
15
|
+
*
|
|
16
|
+
* Decision flow:
|
|
17
|
+
* 1. shouldRunHook gate — exit 0 immediately when the hook is disabled.
|
|
18
|
+
* 2. Resolve steerPath: $SO_PROJECT_DIR/.orchestrator/STEER.md
|
|
19
|
+
* 3. If the file is absent or whitespace-only → exit 0 silently.
|
|
20
|
+
* 4. If non-empty: emit { systemMessage: <contents> } to stdout, then
|
|
21
|
+
* truncate the file to '' so it fires only once.
|
|
22
|
+
*
|
|
23
|
+
* Output contract (PostToolBatch):
|
|
24
|
+
* stdout: JSON — { systemMessage: string }
|
|
25
|
+
* stderr: nothing (diagnostic errors swallowed)
|
|
26
|
+
* exit code: 0 always (informational — must never block Claude)
|
|
27
|
+
*
|
|
28
|
+
* TODO(#409): Cursor support — hooks-cursor.json uses a different event model
|
|
29
|
+
* (no PostToolBatch equivalent) and is not wired here. Register this hook
|
|
30
|
+
* in hooks-cursor.json when Cursor gains a suitable between-batch event.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
34
|
+
import path from 'node:path';
|
|
35
|
+
|
|
36
|
+
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
37
|
+
// Exit 0 immediately when disabled via SO_HOOK_PROFILE / SO_DISABLED_HOOKS.
|
|
38
|
+
if (!shouldRunHook('operator-steer')) process.exit(0);
|
|
39
|
+
|
|
40
|
+
import { SO_PROJECT_DIR } from '../scripts/lib/platform.mjs';
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Main
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
async function main() {
|
|
47
|
+
const steerPath = path.join(SO_PROJECT_DIR, '.orchestrator', 'STEER.md');
|
|
48
|
+
|
|
49
|
+
// File absent — nothing to do.
|
|
50
|
+
if (!existsSync(steerPath)) return;
|
|
51
|
+
|
|
52
|
+
// Read contents and check for meaningful (non-whitespace) content.
|
|
53
|
+
const contents = readFileSync(steerPath, 'utf8');
|
|
54
|
+
if (!contents.trim()) return;
|
|
55
|
+
|
|
56
|
+
// Emit the systemMessage payload so Claude Code injects it into the session.
|
|
57
|
+
console.log(JSON.stringify({ systemMessage: contents }));
|
|
58
|
+
|
|
59
|
+
// Truncate the file — one-shot handshake, fires exactly once per write.
|
|
60
|
+
writeFileSync(steerPath, '', 'utf8');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Exit 0 always — informational hook must never block Claude.
|
|
64
|
+
main().catch(() => {}).finally(() => process.exit(0));
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* post-edit-validate.mjs — PostToolUse hook for incremental validation after Edit/Write/MultiEdit.
|
|
4
|
+
*
|
|
5
|
+
* Node.js port of hooks/post-edit-validate.sh. Part of v3.0.0 Windows-native migration.
|
|
6
|
+
* Issue: github.com/Kanevry/session-orchestrator/issues/139
|
|
7
|
+
*
|
|
8
|
+
* Runs incremental typecheck on the file that was just edited.
|
|
9
|
+
* This is informational only — it NEVER blocks (always exits 0).
|
|
10
|
+
*
|
|
11
|
+
* Decision flow:
|
|
12
|
+
* G1 stdin null → silent exit 0
|
|
13
|
+
* G2 tool_name filter — only Edit/Write/MultiEdit trigger typecheck
|
|
14
|
+
* G3 file_path present
|
|
15
|
+
* G4 file extension filter — only .ts|.tsx|.js|.jsx|.mjs|.cjs
|
|
16
|
+
* G5 gate check — wave-scope.json.gates['post-edit-validate'] === false → exit 0
|
|
17
|
+
* G6 resolve typecheck command: Session Config → tsgo → tsc → npx tsc → skip
|
|
18
|
+
* G7 run typecheck with 2s AbortController timeout
|
|
19
|
+
* G8 emit JSONL result to stderr
|
|
20
|
+
*
|
|
21
|
+
* Output (stderr): {"check":"typecheck","status":"pass|fail|skip","file":"<rel>","duration_ms":<int>}
|
|
22
|
+
* Exit codes: 0 — always (PostToolUse hooks are informational)
|
|
23
|
+
*
|
|
24
|
+
* SECURITY notes:
|
|
25
|
+
* REQ-01 top-level try/catch → silent exit 0 on unexpected error (never blocks)
|
|
26
|
+
* REQ-02 typecheck process uses AbortController (not shell `timeout` — cross-platform)
|
|
27
|
+
* REQ-03 relative path computed via path.relative(projectRoot, absFilePath)
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import path from 'node:path';
|
|
31
|
+
import { spawnSync } from 'node:child_process';
|
|
32
|
+
import { performance } from 'node:perf_hooks';
|
|
33
|
+
|
|
34
|
+
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
35
|
+
// #211: exit 0 immediately (silent allow) when this hook is disabled via profile/env
|
|
36
|
+
if (!shouldRunHook('post-edit-validate')) process.exit(0);
|
|
37
|
+
|
|
38
|
+
import { readStdin } from '../scripts/lib/io.mjs';
|
|
39
|
+
import { resolveProjectDir } from '../scripts/lib/platform.mjs';
|
|
40
|
+
import { findScopeFile, gateEnabled } from '../scripts/lib/hardening.mjs';
|
|
41
|
+
import { readConfigFile, parseSessionConfig } from '../scripts/lib/config.mjs';
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Internal helpers
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
/** File extensions that trigger typecheck. */
|
|
48
|
+
const TS_EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Emit the JSONL result line to stderr.
|
|
52
|
+
* @param {string} file - relative file path
|
|
53
|
+
* @param {'pass'|'fail'|'skip'} status
|
|
54
|
+
* @param {number} [duration_ms] - omit for skip
|
|
55
|
+
* @param {string} [reason] - human-readable reason for skip or fail
|
|
56
|
+
* @param {string} [remediation] - actionable fix hint for fail status only
|
|
57
|
+
*/
|
|
58
|
+
function emitResult(file, status, duration_ms, reason, remediation) {
|
|
59
|
+
const obj = { check: 'typecheck', status, file };
|
|
60
|
+
if (typeof duration_ms === 'number') obj.duration_ms = duration_ms;
|
|
61
|
+
if (reason !== undefined) obj.reason = reason;
|
|
62
|
+
if (remediation !== undefined) obj.remediation = remediation;
|
|
63
|
+
process.stderr.write(JSON.stringify(obj) + '\n');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Try to resolve the typecheck command.
|
|
68
|
+
* Order: Session Config typecheck-command → tsgo → tsc → npx tsc → null
|
|
69
|
+
*
|
|
70
|
+
* @param {string} projectRoot
|
|
71
|
+
* @returns {Promise<{cmd: string, args: string[]}|null>}
|
|
72
|
+
*/
|
|
73
|
+
async function resolveTypecheckCommand(projectRoot) {
|
|
74
|
+
// 1. Session Config typecheck-command
|
|
75
|
+
try {
|
|
76
|
+
const md = await readConfigFile(projectRoot);
|
|
77
|
+
const config = parseSessionConfig(md);
|
|
78
|
+
const cmd = config['typecheck-command'];
|
|
79
|
+
if (cmd && typeof cmd === 'string') {
|
|
80
|
+
const parts = cmd.trim().split(/\s+/);
|
|
81
|
+
return { cmd: parts[0], args: parts.slice(1) };
|
|
82
|
+
}
|
|
83
|
+
} catch {
|
|
84
|
+
// config unavailable — fall through
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 2. tsgo
|
|
88
|
+
const tsgoParts = _tryWhich('tsgo');
|
|
89
|
+
if (tsgoParts) return { cmd: tsgoParts, args: ['--noEmit'] };
|
|
90
|
+
|
|
91
|
+
// 3. tsc
|
|
92
|
+
const tscParts = _tryWhich('tsc');
|
|
93
|
+
if (tscParts) return { cmd: tscParts, args: ['--noEmit'] };
|
|
94
|
+
|
|
95
|
+
// 4. npx tsc
|
|
96
|
+
const npxParts = _tryWhich('npx');
|
|
97
|
+
if (npxParts) return { cmd: npxParts, args: ['tsc', '--noEmit'] };
|
|
98
|
+
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Check whether an executable exists on PATH using `which` / `where` (sync).
|
|
104
|
+
* Returns the executable name if found, null otherwise. Never throws.
|
|
105
|
+
*
|
|
106
|
+
* @param {string} name
|
|
107
|
+
* @returns {string|null}
|
|
108
|
+
*/
|
|
109
|
+
function _tryWhich(name) {
|
|
110
|
+
const whichCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
111
|
+
try {
|
|
112
|
+
const result = spawnSync(whichCmd, [name], { encoding: 'utf8', timeout: 2000 });
|
|
113
|
+
if (result.status === 0 && result.stdout.trim()) return name;
|
|
114
|
+
} catch {
|
|
115
|
+
// ignore
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Run the typecheck command with a 2s AbortController timeout.
|
|
122
|
+
*
|
|
123
|
+
* @param {string} cmd
|
|
124
|
+
* @param {string[]} args
|
|
125
|
+
* @returns {{status: 'pass'|'fail', duration_ms: number, reason?: string}}
|
|
126
|
+
*/
|
|
127
|
+
function runTypecheck(cmd, args) {
|
|
128
|
+
const controller = new AbortController();
|
|
129
|
+
const timer = setTimeout(() => controller.abort(), 2000);
|
|
130
|
+
|
|
131
|
+
const start = performance.now();
|
|
132
|
+
let spawnResult;
|
|
133
|
+
try {
|
|
134
|
+
spawnResult = spawnSync(cmd, args, {
|
|
135
|
+
signal: controller.signal,
|
|
136
|
+
encoding: 'utf8',
|
|
137
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
138
|
+
timeout: 2000,
|
|
139
|
+
});
|
|
140
|
+
} finally {
|
|
141
|
+
clearTimeout(timer);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const duration_ms = Math.max(0, Math.round(performance.now() - start));
|
|
145
|
+
|
|
146
|
+
// AbortError or SIGTERM from timeout
|
|
147
|
+
if (spawnResult.error?.name === 'AbortError' || spawnResult.signal === 'SIGTERM') {
|
|
148
|
+
return { status: 'fail', duration_ms, reason: 'typecheck timed out after 2s' };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (spawnResult.status !== 0) {
|
|
152
|
+
// Capture the first non-empty line of stderr/stdout as a compact reason.
|
|
153
|
+
const combined = ((spawnResult.stderr ?? '') + (spawnResult.stdout ?? '')).trim();
|
|
154
|
+
const firstLine = combined.split('\n').find((l) => l.trim()) ?? 'typecheck exited non-zero';
|
|
155
|
+
return { status: 'fail', duration_ms, reason: firstLine.slice(0, 256) };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return { status: 'pass', duration_ms };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
// Main
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
|
|
165
|
+
async function main() {
|
|
166
|
+
// G1: null stdin → silent exit 0
|
|
167
|
+
let input;
|
|
168
|
+
try {
|
|
169
|
+
input = await readStdin();
|
|
170
|
+
} catch {
|
|
171
|
+
process.exit(0);
|
|
172
|
+
}
|
|
173
|
+
if (!input) process.exit(0);
|
|
174
|
+
|
|
175
|
+
const toolName = input.tool_name;
|
|
176
|
+
const filePath = input?.tool_input?.file_path;
|
|
177
|
+
|
|
178
|
+
// G2: only Edit, Write, and MultiEdit trigger typecheck
|
|
179
|
+
if (toolName !== 'Edit' && toolName !== 'Write' && toolName !== 'MultiEdit') process.exit(0);
|
|
180
|
+
|
|
181
|
+
// G3: file_path must be present
|
|
182
|
+
if (!filePath || typeof filePath !== 'string') process.exit(0);
|
|
183
|
+
|
|
184
|
+
// G4: extension filter — only TS/JS variants
|
|
185
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
186
|
+
if (!TS_EXTS.has(ext)) process.exit(0);
|
|
187
|
+
|
|
188
|
+
const projectRoot = resolveProjectDir();
|
|
189
|
+
|
|
190
|
+
// Compute relative path (REQ-03)
|
|
191
|
+
const absFilePath = path.isAbsolute(filePath)
|
|
192
|
+
? filePath
|
|
193
|
+
: path.resolve(projectRoot, filePath);
|
|
194
|
+
const relPath = path.relative(projectRoot, absFilePath);
|
|
195
|
+
|
|
196
|
+
// G5: per-gate toggle — skip if post-edit-validate gate is disabled
|
|
197
|
+
const scopeFile = findScopeFile(projectRoot);
|
|
198
|
+
if (scopeFile && !gateEnabled(scopeFile, 'post-edit-validate')) {
|
|
199
|
+
process.exit(0);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// G6: resolve typecheck command
|
|
203
|
+
const tc = await resolveTypecheckCommand(projectRoot);
|
|
204
|
+
if (!tc) {
|
|
205
|
+
emitResult(relPath, 'skip', undefined, 'no typecheck command found');
|
|
206
|
+
process.exit(0);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// G7: run typecheck with 2s timeout (REQ-02)
|
|
210
|
+
const { status, duration_ms, reason } = runTypecheck(tc.cmd, tc.args);
|
|
211
|
+
|
|
212
|
+
// G8: emit JSONL result to stderr
|
|
213
|
+
const remediation =
|
|
214
|
+
status === 'fail'
|
|
215
|
+
? 'Run `npm run typecheck` to see full error context, then fix the type at the cited location.'
|
|
216
|
+
: undefined;
|
|
217
|
+
emitResult(relPath, status, duration_ms, reason, remediation);
|
|
218
|
+
|
|
219
|
+
process.exit(0);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// REQ-01: top-level catch — PostToolUse must never block; always exit 0
|
|
223
|
+
main().catch(() => {
|
|
224
|
+
process.exit(0);
|
|
225
|
+
});
|