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,404 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* pre-bash-templates-first.mjs — PreToolUse hook: blocks `gh|glab pr|mr|issue
|
|
4
|
+
* create|new` invocations when no matching template was Read earlier in the
|
|
5
|
+
* session.
|
|
6
|
+
*
|
|
7
|
+
* Implements Pattern 3 of "gsd Pattern Adoption Quick-Wins" (#519; archived in
|
|
8
|
+
* the private Meta-Vault). Companion to pre-bash-destructive-guard.mjs (issue
|
|
9
|
+
* #155); both share the PreToolUse Bash matcher and run sequentially.
|
|
10
|
+
*
|
|
11
|
+
* Decision flow:
|
|
12
|
+
* G1 tool filter — only Bash is gated; other tools pass through.
|
|
13
|
+
* G2 command is a non-empty string.
|
|
14
|
+
* G3 regex match against /^\s*(gh|glab)\s+(pr|mr|issue)\s+(create|new)\b/.
|
|
15
|
+
* No match → exit 0 (the call is not a create/new operation).
|
|
16
|
+
* G4 policy load: .orchestrator/policy/templates-policy.json.
|
|
17
|
+
* Missing → exit 0 + stderr warning. Malformed → exit 0 + stderr warning.
|
|
18
|
+
* G5 bypass-pattern check: any entry in policy.bypass_patterns that the
|
|
19
|
+
* command starts with → exit 0.
|
|
20
|
+
* G6 acknowledgement-file check: if the file under the resolved acknowledgement
|
|
21
|
+
* path contains an entry for the current session_id, exit 0.
|
|
22
|
+
* G7 transcript inspection via hooks/_lib/transcript-history.mjs.
|
|
23
|
+
* If any prior Read tool call matches one of the host-specific template
|
|
24
|
+
* paths from the policy, exit 0.
|
|
25
|
+
* G8 fall-through: emit deny via stdout JSON + structured stderr listing the
|
|
26
|
+
* template paths, and exit 2.
|
|
27
|
+
*
|
|
28
|
+
* Fail-safe posture: any internal exception is swallowed in main().catch and
|
|
29
|
+
* the hook exits 0 (allow). Rationale matches pre-bash-destructive-guard.mjs:
|
|
30
|
+
* a templates-first hook that crashes should not block legitimate work; the
|
|
31
|
+
* worst case is a missed enforcement, not a wedged session.
|
|
32
|
+
*
|
|
33
|
+
* Exit codes:
|
|
34
|
+
* 0 — pass-through (G1-G3 short-circuits, bypass match, acknowledgement, Read found, error)
|
|
35
|
+
* 2 — deny + structured JSON on stdout (PRD § 3 Gherkin Pattern 3)
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import { readStdin, emitAllow } from '../scripts/lib/io.mjs';
|
|
39
|
+
import { resolveProjectDir, resolvePluginRoot } from '../scripts/lib/platform.mjs';
|
|
40
|
+
import { readJson } from '../scripts/lib/common.mjs';
|
|
41
|
+
import { hasReadInSession } from './_lib/transcript-history.mjs';
|
|
42
|
+
|
|
43
|
+
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
44
|
+
import { existsSync, readdirSync, lstatSync } from 'node:fs';
|
|
45
|
+
import path from 'node:path';
|
|
46
|
+
|
|
47
|
+
// #519: opt-in via profile/env. Default profile "full" enables this hook;
|
|
48
|
+
// minimal/off disable. SO_DISABLED_HOOKS=pre-bash-templates-first opts out
|
|
49
|
+
// per session.
|
|
50
|
+
if (!shouldRunHook('pre-bash-templates-first')) process.exit(0);
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Constants
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Matches the canonical `gh` / `glab` issue/PR/MR creation invocations.
|
|
58
|
+
* Anchored at start (^) with optional leading whitespace to catch indented
|
|
59
|
+
* shell snippets. Word-boundary at the end avoids false positives on tokens
|
|
60
|
+
* like `created` or `news`. Edit operations (`gh pr edit`, `glab mr edit`)
|
|
61
|
+
* are deliberately out of scope per PRD § 2 Out-of-Scope.
|
|
62
|
+
*/
|
|
63
|
+
const CREATE_REGEX = /^\s*(gh|glab)\s+(pr|mr|issue)\s+(create|new)\b/;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Default acknowledgement path, relative to project root. Used when the
|
|
67
|
+
* policy omits the `acknowledgement_file` key.
|
|
68
|
+
*/
|
|
69
|
+
const DEFAULT_ACK_PATH = '.orchestrator/runtime/templates-acknowledged.json';
|
|
70
|
+
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Helpers
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Block the create command: write structured deny JSON to stdout + exit 2.
|
|
77
|
+
* Mirrors the exact format from pre-bash-destructive-guard.mjs blockCommand()
|
|
78
|
+
* so downstream Claude Code rendering is consistent.
|
|
79
|
+
*
|
|
80
|
+
* @param {{ host: string, command: string, templatePaths: string[],
|
|
81
|
+
* ackFile: string }} ctx
|
|
82
|
+
* @returns {never}
|
|
83
|
+
*/
|
|
84
|
+
function blockCreate(ctx) {
|
|
85
|
+
const { host, command, templatePaths, ackFile } = ctx;
|
|
86
|
+
const pathList = templatePaths.length > 0
|
|
87
|
+
? templatePaths.map((p) => ` - ${p}`).join('\n')
|
|
88
|
+
: ' (none configured)';
|
|
89
|
+
const reason = [
|
|
90
|
+
`pre-bash-templates-first: ${host} create call detected without prior template Read.`,
|
|
91
|
+
`Command: ${command}`,
|
|
92
|
+
`Found templates:`,
|
|
93
|
+
pathList,
|
|
94
|
+
`Read one of these first, OR run \`/templates-ack\` (writes ${ackFile}) to bypass for this session.`,
|
|
95
|
+
`See: issue #519, "gsd Pattern Adoption Quick-Wins" (archived in the private Meta-Vault) (Pattern 3)`,
|
|
96
|
+
].join('\n');
|
|
97
|
+
|
|
98
|
+
// PRD § 3 Gherkin Pattern 3 spec: stderr lists template paths + ack hint.
|
|
99
|
+
// We emit BOTH stderr (human-readable per spec) AND the structured stdout
|
|
100
|
+
// JSON envelope (machine-readable for Claude Code hook protocol).
|
|
101
|
+
process.stderr.write(reason + '\n');
|
|
102
|
+
process.stdout.write(
|
|
103
|
+
JSON.stringify({ permissionDecision: 'deny', reason }) + '\n',
|
|
104
|
+
);
|
|
105
|
+
process.exit(2);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Resolve the policy file path, searching in priority order. Mirrors the
|
|
110
|
+
* resolution chain in pre-bash-destructive-guard.mjs so administrators only
|
|
111
|
+
* have to maintain a single mental model for policy locations.
|
|
112
|
+
*
|
|
113
|
+
* @param {string|null} projectDir
|
|
114
|
+
* @returns {string|null}
|
|
115
|
+
*/
|
|
116
|
+
function resolvePolicyPath(projectDir) {
|
|
117
|
+
const candidates = [
|
|
118
|
+
path.join(
|
|
119
|
+
process.cwd(),
|
|
120
|
+
'.orchestrator',
|
|
121
|
+
'policy',
|
|
122
|
+
'templates-policy.json',
|
|
123
|
+
),
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
if (projectDir && projectDir !== process.cwd()) {
|
|
127
|
+
candidates.push(
|
|
128
|
+
path.join(projectDir, '.orchestrator', 'policy', 'templates-policy.json'),
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const pluginRoot = resolvePluginRoot();
|
|
133
|
+
if (pluginRoot) {
|
|
134
|
+
candidates.push(
|
|
135
|
+
path.join(pluginRoot, '.orchestrator', 'policy', 'templates-policy.json'),
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
for (const candidate of candidates) {
|
|
140
|
+
if (existsSync(candidate)) return candidate;
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Determine which host the command targets by inspecting the CREATE_REGEX
|
|
147
|
+
* capture. `gh` → "github", `glab` → "gitlab".
|
|
148
|
+
*
|
|
149
|
+
* @param {string} command
|
|
150
|
+
* @returns {"github"|"gitlab"|null}
|
|
151
|
+
*/
|
|
152
|
+
function resolveHost(command) {
|
|
153
|
+
const m = command.match(CREATE_REGEX);
|
|
154
|
+
if (!m) return null;
|
|
155
|
+
return m[1] === 'gh' ? 'github' : 'gitlab';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* True when the command starts with any of the bypass patterns. Bypass match
|
|
160
|
+
* is a prefix check with a word/EOL boundary on the trailing edge — this
|
|
161
|
+
* prevents trivial bypass via prefix-inclusion (e.g. policy entry
|
|
162
|
+
* "gh issue create --label bot" must not match "gh issue create --label botanical").
|
|
163
|
+
*
|
|
164
|
+
* @param {string} command
|
|
165
|
+
* @param {string[]} bypassPatterns
|
|
166
|
+
* @returns {boolean}
|
|
167
|
+
*/
|
|
168
|
+
function matchesBypass(command, bypassPatterns) {
|
|
169
|
+
if (!Array.isArray(bypassPatterns) || bypassPatterns.length === 0) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
const stripped = command.replace(/^\s+/, '');
|
|
173
|
+
for (const pat of bypassPatterns) {
|
|
174
|
+
if (typeof pat !== 'string' || pat.length === 0) continue;
|
|
175
|
+
const patStripped = pat.replace(/^\s+/, '');
|
|
176
|
+
if (!stripped.startsWith(patStripped)) continue;
|
|
177
|
+
// Boundary check: next character must be whitespace, EOL, or absent.
|
|
178
|
+
// This prevents "gh foo --label bot" from matching policy "gh foo --label botanical".
|
|
179
|
+
const nextChar = stripped.charAt(patStripped.length);
|
|
180
|
+
if (nextChar === '' || /\s/.test(nextChar)) return true;
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Check the acknowledgement file for the current session_id. Best-effort:
|
|
187
|
+
* any read or parse error means "no acknowledgement" (returns false) so the
|
|
188
|
+
* subsequent transcript-history check still runs. The acknowledgement file
|
|
189
|
+
* schema is intentionally minimal:
|
|
190
|
+
*
|
|
191
|
+
* { "<session_id>": { "acknowledgedAt": "<ISO timestamp>" } }
|
|
192
|
+
*
|
|
193
|
+
* A future /templates-ack command writes this file; the hook only reads it.
|
|
194
|
+
*
|
|
195
|
+
* @param {string} ackFilePath
|
|
196
|
+
* @param {string|null} sessionId
|
|
197
|
+
* @returns {Promise<boolean>}
|
|
198
|
+
*/
|
|
199
|
+
async function isAcknowledged(ackFilePath, sessionId) {
|
|
200
|
+
if (!sessionId || typeof sessionId !== 'string') return false;
|
|
201
|
+
if (!existsSync(ackFilePath)) return false;
|
|
202
|
+
try {
|
|
203
|
+
const data = await readJson(ackFilePath);
|
|
204
|
+
if (!data || typeof data !== 'object') return false;
|
|
205
|
+
const entry = data[sessionId];
|
|
206
|
+
return Boolean(entry && typeof entry === 'object' && entry.acknowledgedAt);
|
|
207
|
+
} catch {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Pull the session_id from the hook stdin payload, with a single fallback
|
|
214
|
+
* to the persisted file written by on-session-start.mjs. Returns null when
|
|
215
|
+
* neither source yields a string.
|
|
216
|
+
*
|
|
217
|
+
* @param {object|null} input
|
|
218
|
+
* @param {string|null} projectDir
|
|
219
|
+
* @returns {Promise<string|null>}
|
|
220
|
+
*/
|
|
221
|
+
async function resolveSessionId(input, projectDir) {
|
|
222
|
+
const fromStdin = input?.session_id ?? input?.sessionId ?? null;
|
|
223
|
+
if (typeof fromStdin === 'string' && fromStdin.length > 0) return fromStdin;
|
|
224
|
+
|
|
225
|
+
if (!projectDir) return null;
|
|
226
|
+
const persisted = path.join(projectDir, '.orchestrator', 'current-session.json');
|
|
227
|
+
if (!existsSync(persisted)) return null;
|
|
228
|
+
try {
|
|
229
|
+
const data = await readJson(persisted);
|
|
230
|
+
if (data && typeof data === 'object') {
|
|
231
|
+
const sid = data.session_id ?? data.sessionId ?? null;
|
|
232
|
+
if (typeof sid === 'string' && sid.length > 0) return sid;
|
|
233
|
+
}
|
|
234
|
+
} catch {
|
|
235
|
+
// ignore — null below
|
|
236
|
+
}
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
// Main
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
|
|
244
|
+
async function main() {
|
|
245
|
+
const input = await readStdin();
|
|
246
|
+
if (!input) return emitAllow();
|
|
247
|
+
|
|
248
|
+
// G1 — only Bash is gated.
|
|
249
|
+
if (input.tool_name !== 'Bash') return emitAllow();
|
|
250
|
+
|
|
251
|
+
// G2 — command must be a non-empty string.
|
|
252
|
+
const command = input?.tool_input?.command;
|
|
253
|
+
if (typeof command !== 'string' || command.length === 0) return emitAllow();
|
|
254
|
+
|
|
255
|
+
// G3 — regex gate. Non-matching commands pass through unconditionally.
|
|
256
|
+
const host = resolveHost(command);
|
|
257
|
+
if (!host) return emitAllow();
|
|
258
|
+
|
|
259
|
+
const projectDir = resolveProjectDir();
|
|
260
|
+
|
|
261
|
+
// G4 — policy load.
|
|
262
|
+
const policyPath = resolvePolicyPath(projectDir);
|
|
263
|
+
if (!policyPath) {
|
|
264
|
+
process.stderr.write(
|
|
265
|
+
'⚠ pre-bash-templates-first: policy file not found ' +
|
|
266
|
+
'(.orchestrator/policy/templates-policy.json) — skipping guard\n',
|
|
267
|
+
);
|
|
268
|
+
return emitAllow();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
let policy;
|
|
272
|
+
try {
|
|
273
|
+
policy = await readJson(policyPath);
|
|
274
|
+
} catch {
|
|
275
|
+
process.stderr.write(
|
|
276
|
+
'⚠ pre-bash-templates-first: policy file is malformed (invalid JSON) — skipping guard\n',
|
|
277
|
+
);
|
|
278
|
+
return emitAllow();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (!policy || typeof policy !== 'object') {
|
|
282
|
+
process.stderr.write(
|
|
283
|
+
'⚠ pre-bash-templates-first: policy file empty or non-object — skipping guard\n',
|
|
284
|
+
);
|
|
285
|
+
return emitAllow();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Enforcement off → allow without inspection.
|
|
289
|
+
if (policy.enforcement === 'off') return emitAllow();
|
|
290
|
+
|
|
291
|
+
// G5 — bypass-pattern check.
|
|
292
|
+
if (matchesBypass(command, policy.bypass_patterns)) {
|
|
293
|
+
process.stderr.write(
|
|
294
|
+
`ℹ pre-bash-templates-first: bypass-pattern matched, allowing '${command.slice(0, 80)}'\n`,
|
|
295
|
+
);
|
|
296
|
+
return emitAllow();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Resolve the acknowledgement file path (policy override or default).
|
|
300
|
+
const ackRel =
|
|
301
|
+
typeof policy.acknowledgement_file === 'string' && policy.acknowledgement_file.length > 0
|
|
302
|
+
? policy.acknowledgement_file
|
|
303
|
+
: DEFAULT_ACK_PATH;
|
|
304
|
+
const ackFile = path.isAbsolute(ackRel)
|
|
305
|
+
? ackRel
|
|
306
|
+
: path.join(projectDir || process.cwd(), ackRel);
|
|
307
|
+
|
|
308
|
+
// G6 — acknowledgement-file check.
|
|
309
|
+
const sessionId = await resolveSessionId(input, projectDir);
|
|
310
|
+
if (await isAcknowledged(ackFile, sessionId)) {
|
|
311
|
+
return emitAllow();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// G7 — transcript inspection.
|
|
315
|
+
// The hook stdin payload carries `transcript_path` per Claude Code's hook
|
|
316
|
+
// contract. When absent (older harness, Codex/Cursor port, manual smoke
|
|
317
|
+
// test) we treat it as "no evidence of prior Read" and fall through to
|
|
318
|
+
// deny — that is the default-deny safety posture for this gate.
|
|
319
|
+
const hostBlock = policy.hosts?.[host] ?? {};
|
|
320
|
+
const templatePathsConfigured = Array.isArray(hostBlock.template_paths)
|
|
321
|
+
? hostBlock.template_paths.filter((p) => typeof p === 'string' && p.length > 0)
|
|
322
|
+
: [];
|
|
323
|
+
|
|
324
|
+
// Resolve configured paths to ACTUAL files on disk in the project.
|
|
325
|
+
// - File pattern (e.g. .github/PULL_REQUEST_TEMPLATE.md) → include if exists.
|
|
326
|
+
// - Directory pattern (e.g. .gitlab/merge_request_templates/) → expand to
|
|
327
|
+
// all *.md files inside (and inside immediate subdirs for ISSUE_TEMPLATE/).
|
|
328
|
+
// If NO templates resolve, nothing to enforce → allow.
|
|
329
|
+
const projectBase = projectDir || process.cwd();
|
|
330
|
+
const templatePaths = [];
|
|
331
|
+
for (const p of templatePathsConfigured) {
|
|
332
|
+
const abs = path.isAbsolute(p) ? p : path.join(projectBase, p);
|
|
333
|
+
if (!existsSync(abs)) continue;
|
|
334
|
+
let stat;
|
|
335
|
+
try {
|
|
336
|
+
const lstat = lstatSync(abs);
|
|
337
|
+
if (lstat.isSymbolicLink()) {
|
|
338
|
+
process.stderr.write(`⚠ pre-bash-templates-first: template path is a symlink (${p}) — rejected for security\n`);
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
stat = lstat;
|
|
342
|
+
} catch { continue; }
|
|
343
|
+
if (stat.isFile()) {
|
|
344
|
+
templatePaths.push(p);
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
if (stat.isDirectory()) {
|
|
348
|
+
let entries;
|
|
349
|
+
try { entries = readdirSync(abs); } catch { continue; }
|
|
350
|
+
const trimmed = p.endsWith('/') ? p.slice(0, -1) : p;
|
|
351
|
+
for (const entry of entries) {
|
|
352
|
+
if (entry.startsWith('.')) continue;
|
|
353
|
+
const entryAbs = path.join(abs, entry);
|
|
354
|
+
let estat;
|
|
355
|
+
try {
|
|
356
|
+
const elstat = lstatSync(entryAbs);
|
|
357
|
+
if (elstat.isSymbolicLink()) {
|
|
358
|
+
// Silent skip: symlinks inside template directories are not enforced templates.
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
estat = elstat;
|
|
362
|
+
} catch { continue; }
|
|
363
|
+
if (estat.isFile() && (entry.endsWith('.md') || entry.endsWith('.yml') || entry.endsWith('.yaml'))) {
|
|
364
|
+
templatePaths.push(`${trimmed}/${entry}`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (templatePaths.length === 0) {
|
|
371
|
+
process.stderr.write(
|
|
372
|
+
`ℹ pre-bash-templates-first: no ${host} template files found in repo — allowing '${command.slice(0, 80)}'\n`,
|
|
373
|
+
);
|
|
374
|
+
return emitAllow();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const transcriptPath =
|
|
378
|
+
typeof input.transcript_path === 'string' && input.transcript_path.length > 0
|
|
379
|
+
? input.transcript_path
|
|
380
|
+
: null;
|
|
381
|
+
|
|
382
|
+
if (transcriptPath && templatePaths.length > 0) {
|
|
383
|
+
const result = await hasReadInSession(templatePaths, transcriptPath);
|
|
384
|
+
if (result.matched) return emitAllow();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// G8 — deny.
|
|
388
|
+
blockCreate({
|
|
389
|
+
host,
|
|
390
|
+
command,
|
|
391
|
+
templatePaths,
|
|
392
|
+
ackFile,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Top-level error handler — never let exit 1 leak. Same posture as
|
|
397
|
+
// pre-bash-destructive-guard.mjs: fail-open on internal errors to avoid
|
|
398
|
+
// blocking legitimate work.
|
|
399
|
+
main().catch((e) => {
|
|
400
|
+
process.stderr.write(
|
|
401
|
+
`⚠ pre-bash-templates-first: internal error — ${e?.message || e}\n`,
|
|
402
|
+
);
|
|
403
|
+
process.exit(0);
|
|
404
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# hooks/run-node.sh — resolve the Node.js binary robustly, then exec a plugin hook.
|
|
3
|
+
#
|
|
4
|
+
# Why this exists (GH Kanevry/session-orchestrator#53): the Claude Code harness
|
|
5
|
+
# executes hook commands via `/bin/sh -c` with the PATH of the harness process
|
|
6
|
+
# itself. That shell does NOT source ~/.zshrc / ~/.bashrc, so Node installed via
|
|
7
|
+
# Homebrew on Apple Silicon (/opt/homebrew/bin), nvm, volta, or asdf may be
|
|
8
|
+
# invisible to hooks even though `node` works fine in a normal terminal. A bare
|
|
9
|
+
# `node ...` command then fails with "node: command not found" on EVERY hook of
|
|
10
|
+
# EVERY tool call — loud, repetitive, and useless to the operator.
|
|
11
|
+
#
|
|
12
|
+
# Contract:
|
|
13
|
+
# sh run-node.sh <hook-script.mjs> [args...]
|
|
14
|
+
# - Resolution order: $SO_NODE_BIN override > PATH > well-known install dirs
|
|
15
|
+
# ($SO_NODE_SEARCH_DIRS, colon-separated, overrides the built-in list) > nvm.
|
|
16
|
+
# - Found: exec's node — stdin/stdout/stderr and exit code pass through
|
|
17
|
+
# unchanged (PreToolUse exit-2 blocking still works).
|
|
18
|
+
# - Missing: prints ONE warning per rate-limit window (marker file in
|
|
19
|
+
# ${TMPDIR:-/tmp}, 6h TTL) and exits 0 so hooks degrade gracefully
|
|
20
|
+
# instead of spamming a shell error on every tool call.
|
|
21
|
+
|
|
22
|
+
# 1. Explicit operator override wins.
|
|
23
|
+
if [ -n "$SO_NODE_BIN" ] && [ -x "$SO_NODE_BIN" ]; then
|
|
24
|
+
exec "$SO_NODE_BIN" "$@"
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# 2. PATH as inherited from the harness.
|
|
28
|
+
if command -v node >/dev/null 2>&1; then
|
|
29
|
+
exec node "$@"
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# 3. Well-known install locations (Homebrew arm64/intel, system, volta, asdf).
|
|
33
|
+
search_dirs="${SO_NODE_SEARCH_DIRS:-/opt/homebrew/bin:/usr/local/bin:/usr/bin:${VOLTA_HOME:-$HOME/.volta}/bin:$HOME/.asdf/shims:$HOME/.local/bin}"
|
|
34
|
+
old_ifs="$IFS"
|
|
35
|
+
IFS=:
|
|
36
|
+
for dir in $search_dirs; do
|
|
37
|
+
if [ -n "$dir" ] && [ -x "$dir/node" ]; then
|
|
38
|
+
IFS="$old_ifs"
|
|
39
|
+
exec "$dir/node" "$@"
|
|
40
|
+
fi
|
|
41
|
+
done
|
|
42
|
+
IFS="$old_ifs"
|
|
43
|
+
|
|
44
|
+
# 4. nvm keeps versioned dirs; any installed version is good enough for hooks.
|
|
45
|
+
# (Glob order is lexical, not semver — acceptable for a last-resort fallback.)
|
|
46
|
+
for cand in "${NVM_DIR:-$HOME/.nvm}"/versions/node/*/bin/node; do
|
|
47
|
+
if [ -x "$cand" ]; then
|
|
48
|
+
exec "$cand" "$@"
|
|
49
|
+
fi
|
|
50
|
+
done
|
|
51
|
+
|
|
52
|
+
# 5. Not found anywhere: degrade gracefully. Warn at most once per 6 hours so
|
|
53
|
+
# the operator gets ONE actionable diagnostic instead of per-tool-call spam.
|
|
54
|
+
# Deliberately dependency-free: a PATH broken enough to lose `node` may also
|
|
55
|
+
# lack `touch`/`find`/`id`, so the marker is written via shell redirection
|
|
56
|
+
# and `find` only upgrades the check to a 6h TTL when it happens to exist.
|
|
57
|
+
marker="${TMPDIR:-/tmp}/session-orchestrator-node-missing-${USER:-uid}"
|
|
58
|
+
if [ -f "$marker" ]; then
|
|
59
|
+
expired="$(find "$marker" -mmin +360 2>/dev/null || true)"
|
|
60
|
+
else
|
|
61
|
+
expired="yes"
|
|
62
|
+
fi
|
|
63
|
+
if [ -n "$expired" ]; then
|
|
64
|
+
: > "$marker" 2>/dev/null || true
|
|
65
|
+
{
|
|
66
|
+
echo "session-orchestrator: 'node' not found on the hook PATH — plugin hooks are skipped."
|
|
67
|
+
echo " Fix: install Node.js 24+, expose it on the harness PATH, or set SO_NODE_BIN=/abs/path/to/node."
|
|
68
|
+
echo " (Hook shells do not source ~/.zshrc — Homebrew/nvm/volta installs can be invisible here."
|
|
69
|
+
echo " Rate-limited to once per 6h. See README.md § Troubleshooting.)"
|
|
70
|
+
} >&2
|
|
71
|
+
fi
|
|
72
|
+
exit 0
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* skill-invocation-telemetry.mjs — PreToolUse hook for Skill tool selection.
|
|
4
|
+
*
|
|
5
|
+
* Hook event: PreToolUse with matcher:"Skill" (issue #645, epic #643).
|
|
6
|
+
* Fires when the Skill tool is invoked (a skill is selected). Writes a
|
|
7
|
+
* selection record to `.orchestrator/metrics/skill-invocations.jsonl`.
|
|
8
|
+
*
|
|
9
|
+
* Decision flow:
|
|
10
|
+
* 1. shouldRunHook gate — exit 0 immediately when the hook is disabled.
|
|
11
|
+
* 2. Read JSON payload from stdin: { tool_name, tool_input: { skill }, session_id }.
|
|
12
|
+
* 3. Belt-and-suspenders guard: if tool_name !== "Skill", exit 0 immediately.
|
|
13
|
+
* 4. Build a 'selected' record and call appendSkillInvocation().
|
|
14
|
+
* 5. Output: nothing on stdout. Diagnostic errors to stderr only.
|
|
15
|
+
*
|
|
16
|
+
* Exit codes: 0 always (informational, never blocking).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
20
|
+
// Exit 0 immediately when disabled via SO_HOOK_PROFILE / SO_DISABLED_HOOKS.
|
|
21
|
+
if (!shouldRunHook('skill-invocation-telemetry')) process.exit(0);
|
|
22
|
+
|
|
23
|
+
import path from 'node:path';
|
|
24
|
+
import { appendSkillInvocation } from '../scripts/lib/skill-invocations-schema.mjs';
|
|
25
|
+
import { SO_PROJECT_DIR } from '../scripts/lib/platform.mjs';
|
|
26
|
+
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Constants
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
const JSONL_PATH = path.join(SO_PROJECT_DIR, '.orchestrator', 'metrics', 'skill-invocations.jsonl');
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Helpers
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Read stdin to EOF (best-effort). Returns parsed JSON or null on failure.
|
|
39
|
+
* Uses a 5 s timeout consistent with Claude Code hook contract.
|
|
40
|
+
*
|
|
41
|
+
* @returns {Promise<object|null>}
|
|
42
|
+
*/
|
|
43
|
+
function readStdinJson() {
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
if (process.stdin.readableEnded || process.stdin.closed) {
|
|
46
|
+
resolve(null);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const chunks = [];
|
|
50
|
+
const timer = setTimeout(() => { resolve(null); }, 5_000);
|
|
51
|
+
process.stdin.setEncoding('utf8');
|
|
52
|
+
process.stdin.on('data', (c) => chunks.push(c));
|
|
53
|
+
process.stdin.on('end', () => {
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
const raw = chunks.join('').trim();
|
|
56
|
+
if (!raw) { resolve(null); return; }
|
|
57
|
+
try { resolve(JSON.parse(raw)); } catch { resolve(null); }
|
|
58
|
+
});
|
|
59
|
+
process.stdin.on('error', () => { clearTimeout(timer); resolve(null); });
|
|
60
|
+
process.stdin.resume();
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// Main
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
async function main() {
|
|
69
|
+
const input = await readStdinJson();
|
|
70
|
+
if (!input) return;
|
|
71
|
+
|
|
72
|
+
// Belt-and-suspenders: the hooks.json matcher:"Skill" should already filter,
|
|
73
|
+
// but we guard defensively in case of misconfiguration or future matcher changes.
|
|
74
|
+
if (input.tool_name !== 'Skill') return;
|
|
75
|
+
|
|
76
|
+
const skillName = (typeof input.tool_input?.skill === 'string' && input.tool_input.skill.trim())
|
|
77
|
+
? input.tool_input.skill.trim()
|
|
78
|
+
: 'unknown';
|
|
79
|
+
|
|
80
|
+
const sessionId = (typeof input.session_id === 'string' && input.session_id.trim())
|
|
81
|
+
? input.session_id.trim()
|
|
82
|
+
: null;
|
|
83
|
+
|
|
84
|
+
/** @type {object} */
|
|
85
|
+
const record = {
|
|
86
|
+
timestamp: new Date().toISOString(),
|
|
87
|
+
event: 'selected',
|
|
88
|
+
skill: skillName,
|
|
89
|
+
session_id: sessionId,
|
|
90
|
+
schema_version: 1,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
await appendSkillInvocation(JSONL_PATH, record);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Exit 0 always — informational hook must never block the Skill tool.
|
|
97
|
+
main().catch((err) => {
|
|
98
|
+
process.stderr.write(`[skill-invocation-telemetry] ERROR: ${err?.message ?? err}\n`);
|
|
99
|
+
}).finally(() => process.exit(0));
|