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.
Files changed (762) hide show
  1. package/.claude-plugin/marketplace.json +29 -0
  2. package/.claude-plugin/plugin.json +18 -0
  3. package/.codex-plugin/agents/explorer.toml +14 -0
  4. package/.codex-plugin/agents/session-reviewer.toml +23 -0
  5. package/.codex-plugin/agents/wave-worker.toml +15 -0
  6. package/.codex-plugin/config.toml +20 -0
  7. package/.codex-plugin/plugin.json +37 -0
  8. package/.cursor/rules/000-session-orchestrator.mdc +73 -0
  9. package/.cursor/rules/010-session-workflow.mdc +170 -0
  10. package/.cursor/rules/020-quality-gates.mdc +128 -0
  11. package/.cursor/rules/030-wave-execution.mdc +216 -0
  12. package/.cursor/rules/040-discovery.mdc +242 -0
  13. package/.cursor/rules/050-plan.mdc +235 -0
  14. package/.cursor/rules/060-evolve.mdc +232 -0
  15. package/.cursor/rules/070-gitlab-ops.mdc +246 -0
  16. package/.cursor/rules/080-ecosystem-health.mdc +145 -0
  17. package/.mcp.json +8 -0
  18. package/CHANGELOG.md +1544 -0
  19. package/LICENSE +21 -0
  20. package/NOTICE +64 -0
  21. package/README.md +242 -0
  22. package/SECURITY.md +90 -0
  23. package/agents/AGENTS.md +136 -0
  24. package/agents/analyst.md +99 -0
  25. package/agents/architect-reviewer.md +93 -0
  26. package/agents/code-implementer.md +106 -0
  27. package/agents/db-specialist.md +104 -0
  28. package/agents/dialectic-deriver.md +139 -0
  29. package/agents/docs-writer.md +113 -0
  30. package/agents/eval-judge.md +146 -0
  31. package/agents/memory-proposal-collector.md +297 -0
  32. package/agents/qa-strategist.md +102 -0
  33. package/agents/schemas/analyst.schema.json +46 -0
  34. package/agents/schemas/architect-reviewer.schema.json +50 -0
  35. package/agents/schemas/code-implementer.schema.json +61 -0
  36. package/agents/schemas/db-specialist.schema.json +80 -0
  37. package/agents/schemas/docs-writer.schema.json +56 -0
  38. package/agents/schemas/persona-panel-sidecar.schema.json +245 -0
  39. package/agents/schemas/qa-strategist.schema.json +46 -0
  40. package/agents/schemas/security-reviewer.schema.json +86 -0
  41. package/agents/schemas/session-reviewer.schema.json +69 -0
  42. package/agents/schemas/test-writer.schema.json +69 -0
  43. package/agents/schemas/ui-developer.schema.json +90 -0
  44. package/agents/schemas/ux-evaluator.schema.json +51 -0
  45. package/agents/security-reviewer.md +236 -0
  46. package/agents/session-reviewer.md +201 -0
  47. package/agents/skill-applied-judge.md +122 -0
  48. package/agents/test-writer.md +123 -0
  49. package/agents/ui-developer.md +109 -0
  50. package/agents/ux-evaluator.md +161 -0
  51. package/assets/icon.svg +11 -0
  52. package/assets/og-card.png +0 -0
  53. package/assets/og-card.svg +47 -0
  54. package/commands/autopilot-multi.md +74 -0
  55. package/commands/autopilot.md +80 -0
  56. package/commands/bootstrap.md +56 -0
  57. package/commands/brainstorm.md +48 -0
  58. package/commands/close.md +24 -0
  59. package/commands/debug.md +36 -0
  60. package/commands/discovery.md +32 -0
  61. package/commands/dispatcher.md +59 -0
  62. package/commands/eval.md +28 -0
  63. package/commands/evolve.md +10 -0
  64. package/commands/go.md +41 -0
  65. package/commands/grill.md +45 -0
  66. package/commands/harness-audit.md +26 -0
  67. package/commands/memory-cleanup.md +25 -0
  68. package/commands/persona-panel.md +121 -0
  69. package/commands/plan.md +15 -0
  70. package/commands/portfolio.md +97 -0
  71. package/commands/reconcile.md +23 -0
  72. package/commands/repo-audit.md +24 -0
  73. package/commands/session.md +30 -0
  74. package/commands/spinout.md +15 -0
  75. package/commands/sunset-review.md +27 -0
  76. package/commands/templates-ack.md +96 -0
  77. package/commands/test.md +97 -0
  78. package/docs/README.md +105 -0
  79. package/docs/USER-GUIDE.md +1403 -0
  80. package/docs/ci-setup.md +81 -0
  81. package/docs/codex-setup.md +142 -0
  82. package/docs/components.md +74 -0
  83. package/docs/cursor-setup.md +104 -0
  84. package/docs/events-schema.md +81 -0
  85. package/docs/migration-v3.md +148 -0
  86. package/docs/owner-config-schema.md +154 -0
  87. package/docs/persona-panel.md +433 -0
  88. package/docs/pi-setup.md +115 -0
  89. package/docs/plugin-architecture-v3.md +296 -0
  90. package/docs/pm-skills-marketplace.md +114 -0
  91. package/docs/policy-cache-validation-2026-04-28.md +118 -0
  92. package/docs/rule-authoring.md +316 -0
  93. package/docs/session-config-reference.md +1439 -0
  94. package/docs/session-config-template.md +961 -0
  95. package/docs/vault-docs-architecture.md +297 -0
  96. package/hooks/_lib/lock-bootstrap.mjs +272 -0
  97. package/hooks/_lib/lock-reconcile.mjs +93 -0
  98. package/hooks/_lib/profile-gate.mjs +95 -0
  99. package/hooks/_lib/transcript-history.mjs +211 -0
  100. package/hooks/agent-teams-h3-test.sh +362 -0
  101. package/hooks/config-protection.mjs +0 -0
  102. package/hooks/cwd-change-restore.mjs +131 -0
  103. package/hooks/enforce-commands.mjs +179 -0
  104. package/hooks/enforce-scope.mjs +273 -0
  105. package/hooks/hooks-codex.json +60 -0
  106. package/hooks/hooks-cursor.json +15 -0
  107. package/hooks/hooks-pi.json +115 -0
  108. package/hooks/hooks.json +215 -0
  109. package/hooks/loop-guard.mjs +260 -0
  110. package/hooks/on-session-end.mjs +217 -0
  111. package/hooks/on-session-start.mjs +660 -0
  112. package/hooks/on-stop.mjs +294 -0
  113. package/hooks/operator-steer.mjs +64 -0
  114. package/hooks/post-edit-validate.mjs +225 -0
  115. package/hooks/post-subagent-discovery-validator.mjs +398 -0
  116. package/hooks/post-tool-batch-wave-signal.mjs +328 -0
  117. package/hooks/post-tool-failure-corrective-context.mjs +248 -0
  118. package/hooks/post-tooluse-frontend-slop.mjs +184 -0
  119. package/hooks/pre-bash-destructive-guard.mjs +515 -0
  120. package/hooks/pre-bash-memory-propose-audit.mjs +206 -0
  121. package/hooks/pre-bash-staging-fence.mjs +223 -0
  122. package/hooks/pre-bash-templates-first.mjs +404 -0
  123. package/hooks/run-node.sh +72 -0
  124. package/hooks/skill-invocation-telemetry.mjs +99 -0
  125. package/hooks/subagent-telemetry.mjs +249 -0
  126. package/hooks/wave-scope-commit-guard.mjs +191 -0
  127. package/monitors/monitors.json +14 -0
  128. package/output-styles/finding-report.md +48 -0
  129. package/output-styles/session-report.md +53 -0
  130. package/output-styles/wave-summary.md +38 -0
  131. package/package.json +94 -0
  132. package/pi/extensions/session-orchestrator.ts +25 -0
  133. package/pi/prompts/autopilot-multi.md +12 -0
  134. package/pi/prompts/autopilot.md +12 -0
  135. package/pi/prompts/bootstrap.md +12 -0
  136. package/pi/prompts/brainstorm.md +12 -0
  137. package/pi/prompts/close.md +11 -0
  138. package/pi/prompts/debug.md +12 -0
  139. package/pi/prompts/discovery.md +12 -0
  140. package/pi/prompts/dispatcher.md +12 -0
  141. package/pi/prompts/eval.md +12 -0
  142. package/pi/prompts/evolve.md +12 -0
  143. package/pi/prompts/go.md +12 -0
  144. package/pi/prompts/grill.md +12 -0
  145. package/pi/prompts/harness-audit.md +12 -0
  146. package/pi/prompts/memory-cleanup.md +12 -0
  147. package/pi/prompts/persona-panel.md +12 -0
  148. package/pi/prompts/plan.md +12 -0
  149. package/pi/prompts/portfolio.md +12 -0
  150. package/pi/prompts/reconcile.md +12 -0
  151. package/pi/prompts/repo-audit.md +12 -0
  152. package/pi/prompts/session.md +12 -0
  153. package/pi/prompts/spinout.md +12 -0
  154. package/pi/prompts/sunset-review.md +12 -0
  155. package/pi/prompts/templates-ack.md +12 -0
  156. package/pi/prompts/test.md +12 -0
  157. package/rules/_index.md +51 -0
  158. package/rules/always-on/commit-discipline.md +26 -0
  159. package/rules/always-on/npm-quality-gates.md +26 -0
  160. package/rules/always-on/parallel-sessions.md +43 -0
  161. package/rules/opt-in-domain/prompt-caching.md +270 -0
  162. package/rules/opt-in-stack/backend-data.md +188 -0
  163. package/rules/opt-in-stack/backend.md +390 -0
  164. package/rules/opt-in-stack/frontend.md +98 -0
  165. package/rules/opt-in-stack/security-web.md +194 -0
  166. package/rules/opt-in-stack/swift.md +65 -0
  167. package/scripts/archive-closed-prds.mjs +416 -0
  168. package/scripts/autopilot-multi.mjs +802 -0
  169. package/scripts/autopilot.mjs +383 -0
  170. package/scripts/backfill-abandoned-sessions.mjs +265 -0
  171. package/scripts/backfill-learnings-expires.mjs +196 -0
  172. package/scripts/backfill-learnings.mjs +203 -0
  173. package/scripts/backfill-sessions.mjs +282 -0
  174. package/scripts/check-doc-consistency.sh +279 -0
  175. package/scripts/check-package-manager.mjs +445 -0
  176. package/scripts/ci/assert-vitest-green.mjs +267 -0
  177. package/scripts/codex-install.mjs +435 -0
  178. package/scripts/compute-grounding-injection.sh +186 -0
  179. package/scripts/cursor-install.mjs +113 -0
  180. package/scripts/dialectic-deriver.mjs +573 -0
  181. package/scripts/emit-event.mjs +160 -0
  182. package/scripts/emit-session.mjs +212 -0
  183. package/scripts/eval-session.mjs +262 -0
  184. package/scripts/export-hw-learnings.mjs +437 -0
  185. package/scripts/gc-stale-worktrees.mjs +666 -0
  186. package/scripts/generate-pi-prompts.mjs +127 -0
  187. package/scripts/harness-audit.mjs +287 -0
  188. package/scripts/lib/agent-frontmatter.mjs +266 -0
  189. package/scripts/lib/agent-output-schema.mjs +166 -0
  190. package/scripts/lib/agent-status.mjs +303 -0
  191. package/scripts/lib/ajv-loader.mjs +34 -0
  192. package/scripts/lib/auto-dialectic.mjs +382 -0
  193. package/scripts/lib/auto-dream.mjs +471 -0
  194. package/scripts/lib/autonomy/suitability.mjs +212 -0
  195. package/scripts/lib/autopilot/dep-graph.mjs +417 -0
  196. package/scripts/lib/autopilot/durable-telemetry.mjs +121 -0
  197. package/scripts/lib/autopilot/flags.mjs +104 -0
  198. package/scripts/lib/autopilot/kill-switches.mjs +174 -0
  199. package/scripts/lib/autopilot/loop.mjs +320 -0
  200. package/scripts/lib/autopilot/mr-draft.mjs +520 -0
  201. package/scripts/lib/autopilot/multi-killswitch.mjs +184 -0
  202. package/scripts/lib/autopilot/recent-runs.mjs +106 -0
  203. package/scripts/lib/autopilot/stall-sampler.mjs +97 -0
  204. package/scripts/lib/autopilot/telemetry.mjs +224 -0
  205. package/scripts/lib/autopilot/worktree-pipeline.mjs +605 -0
  206. package/scripts/lib/autopilot-telemetry.mjs +11 -0
  207. package/scripts/lib/autopilot.mjs +39 -0
  208. package/scripts/lib/backlog-scan.mjs +179 -0
  209. package/scripts/lib/bootstrap-lock-freshness.mjs +260 -0
  210. package/scripts/lib/bootstrap-lock-refresh.mjs +186 -0
  211. package/scripts/lib/build-live-signals.mjs +150 -0
  212. package/scripts/lib/ci-status-banner.mjs +425 -0
  213. package/scripts/lib/claude-md-budget-lint.mjs +246 -0
  214. package/scripts/lib/cli-flags.mjs +158 -0
  215. package/scripts/lib/codex/plugin-contract.mjs +610 -0
  216. package/scripts/lib/cold-start-detector.mjs +240 -0
  217. package/scripts/lib/command-blocker.mjs +458 -0
  218. package/scripts/lib/common.mjs +333 -0
  219. package/scripts/lib/config/auto-dream.mjs +77 -0
  220. package/scripts/lib/config/block-header.mjs +94 -0
  221. package/scripts/lib/config/broken-window.mjs +114 -0
  222. package/scripts/lib/config/coercers.mjs +248 -0
  223. package/scripts/lib/config/cold-start.mjs +92 -0
  224. package/scripts/lib/config/config-protection.mjs +120 -0
  225. package/scripts/lib/config/cross-repo.mjs +104 -0
  226. package/scripts/lib/config/custom-phases.mjs +213 -0
  227. package/scripts/lib/config/dialectic.mjs +92 -0
  228. package/scripts/lib/config/discovery-validator.mjs +75 -0
  229. package/scripts/lib/config/dispatcher-autonomy-capture.mjs +240 -0
  230. package/scripts/lib/config/dispatcher-autonomy.mjs +152 -0
  231. package/scripts/lib/config/docs-orchestrator.mjs +90 -0
  232. package/scripts/lib/config/docs-staleness.mjs +96 -0
  233. package/scripts/lib/config/drift-check.mjs +155 -0
  234. package/scripts/lib/config/eval.mjs +130 -0
  235. package/scripts/lib/config/events-rotation.mjs +74 -0
  236. package/scripts/lib/config/evolve.mjs +308 -0
  237. package/scripts/lib/config/frontend-slop-hook.mjs +104 -0
  238. package/scripts/lib/config/gitlab-portfolio.mjs +150 -0
  239. package/scripts/lib/config/handover-gate.mjs +106 -0
  240. package/scripts/lib/config/host-paths.mjs +76 -0
  241. package/scripts/lib/config/io.mjs +54 -0
  242. package/scripts/lib/config/loop-guard.mjs +117 -0
  243. package/scripts/lib/config/memory.mjs +150 -0
  244. package/scripts/lib/config/persona-gate-wave.mjs +258 -0
  245. package/scripts/lib/config/reconcile.mjs +205 -0
  246. package/scripts/lib/config/section-extractor.mjs +100 -0
  247. package/scripts/lib/config/skill-evolution.mjs +112 -0
  248. package/scripts/lib/config/slopcheck.mjs +99 -0
  249. package/scripts/lib/config/state-md-lock.mjs +83 -0
  250. package/scripts/lib/config/templates-first.mjs +94 -0
  251. package/scripts/lib/config/test.mjs +113 -0
  252. package/scripts/lib/config/vault-integration.mjs +201 -0
  253. package/scripts/lib/config/vault-mirror-quality.mjs +99 -0
  254. package/scripts/lib/config/vault-staleness.mjs +84 -0
  255. package/scripts/lib/config/vault-sync.mjs +96 -0
  256. package/scripts/lib/config/verification-auto-fix.mjs +84 -0
  257. package/scripts/lib/config/wave-reviewers.mjs +133 -0
  258. package/scripts/lib/config-schema.mjs +345 -0
  259. package/scripts/lib/config.mjs +474 -0
  260. package/scripts/lib/convergence-monitor.mjs +389 -0
  261. package/scripts/lib/coordinator-snapshot.mjs +371 -0
  262. package/scripts/lib/crypto-digest-utils.mjs +91 -0
  263. package/scripts/lib/discovery/helpers.mjs +127 -0
  264. package/scripts/lib/discovery/triage-state.mjs +279 -0
  265. package/scripts/lib/dispatcher/cli.mjs +257 -0
  266. package/scripts/lib/dispatcher/enumerate.mjs +243 -0
  267. package/scripts/lib/dispatcher/rank.mjs +363 -0
  268. package/scripts/lib/ecosystem-health.mjs +224 -0
  269. package/scripts/lib/ecosystem-wizard/ci-detector.mjs +18 -0
  270. package/scripts/lib/ecosystem-wizard/config-parser.mjs +54 -0
  271. package/scripts/lib/ecosystem-wizard/config-writer.mjs +287 -0
  272. package/scripts/lib/ecosystem-wizard/package-manager-detector.mjs +42 -0
  273. package/scripts/lib/ecosystem-wizard/wizard-prompt.mjs +246 -0
  274. package/scripts/lib/ecosystem-wizard.mjs +48 -0
  275. package/scripts/lib/env-check.mjs +89 -0
  276. package/scripts/lib/eval/engine.mjs +605 -0
  277. package/scripts/lib/eval/judge.mjs +433 -0
  278. package/scripts/lib/eval/report.mjs +367 -0
  279. package/scripts/lib/eval/schema.mjs +618 -0
  280. package/scripts/lib/eval/session-resolve.mjs +137 -0
  281. package/scripts/lib/eval/sink.mjs +77 -0
  282. package/scripts/lib/events-rotation.mjs +86 -0
  283. package/scripts/lib/events-schema.mjs +81 -0
  284. package/scripts/lib/events.mjs +80 -0
  285. package/scripts/lib/evolve/autonomy-verdict.mjs +461 -0
  286. package/scripts/lib/evolve/autopilot-effectiveness.mjs +293 -0
  287. package/scripts/lib/exclusivity-matrix.mjs +68 -0
  288. package/scripts/lib/fetch-baseline.mjs +311 -0
  289. package/scripts/lib/file-lock.mjs +512 -0
  290. package/scripts/lib/frontend-detect/detect.mjs +138 -0
  291. package/scripts/lib/frontend-detect/rules.mjs +295 -0
  292. package/scripts/lib/frontmatter-guard.mjs +241 -0
  293. package/scripts/lib/gates/echo-stub-detect.mjs +39 -0
  294. package/scripts/lib/gates/gate-baseline.mjs +42 -0
  295. package/scripts/lib/gates/gate-full.mjs +85 -0
  296. package/scripts/lib/gates/gate-helpers.mjs +231 -0
  297. package/scripts/lib/gates/gate-incremental.mjs +76 -0
  298. package/scripts/lib/gates/gate-per-file.mjs +55 -0
  299. package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +447 -0
  300. package/scripts/lib/gitlab-portfolio/aggregator.mjs +383 -0
  301. package/scripts/lib/gitlab-portfolio/cli.mjs +428 -0
  302. package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +289 -0
  303. package/scripts/lib/gitlab-portfolio/vcs-detect.mjs +182 -0
  304. package/scripts/lib/handover-gate.mjs +222 -0
  305. package/scripts/lib/hardening.mjs +43 -0
  306. package/scripts/lib/hardware-pattern-detector.mjs +238 -0
  307. package/scripts/lib/harness-audit/categories/category1.mjs +123 -0
  308. package/scripts/lib/harness-audit/categories/category2.mjs +145 -0
  309. package/scripts/lib/harness-audit/categories/category3.mjs +143 -0
  310. package/scripts/lib/harness-audit/categories/category4.mjs +202 -0
  311. package/scripts/lib/harness-audit/categories/category5.mjs +152 -0
  312. package/scripts/lib/harness-audit/categories/category6.mjs +211 -0
  313. package/scripts/lib/harness-audit/categories/category7.mjs +125 -0
  314. package/scripts/lib/harness-audit/categories/category8.mjs +328 -0
  315. package/scripts/lib/harness-audit/categories/category9.mjs +294 -0
  316. package/scripts/lib/harness-audit/categories/helpers.mjs +165 -0
  317. package/scripts/lib/harness-audit/categories.mjs +19 -0
  318. package/scripts/lib/historical-guard.mjs +15 -0
  319. package/scripts/lib/host-identity.mjs +262 -0
  320. package/scripts/lib/instruction-budget-guard.mjs +332 -0
  321. package/scripts/lib/io.mjs +304 -0
  322. package/scripts/lib/issue-close-strip-labels.mjs +161 -0
  323. package/scripts/lib/language-mappers/README.md +57 -0
  324. package/scripts/lib/language-mappers/index.mjs +165 -0
  325. package/scripts/lib/language-mappers/markdown.mjs +149 -0
  326. package/scripts/lib/language-mappers/python.mjs +249 -0
  327. package/scripts/lib/language-mappers/swift.mjs +201 -0
  328. package/scripts/lib/language-mappers/typescript.mjs +433 -0
  329. package/scripts/lib/learnings/expiry-sweep.mjs +164 -0
  330. package/scripts/lib/learnings/filters.mjs +43 -0
  331. package/scripts/lib/learnings/io.mjs +255 -0
  332. package/scripts/lib/learnings/schema.mjs +518 -0
  333. package/scripts/lib/learnings/surface.mjs +207 -0
  334. package/scripts/lib/learnings.mjs +42 -0
  335. package/scripts/lib/lock-reaper.mjs +648 -0
  336. package/scripts/lib/locks/index.mjs +31 -0
  337. package/scripts/lib/locks/lock-body.mjs +62 -0
  338. package/scripts/lib/locks/staging-fence-lock.mjs +267 -0
  339. package/scripts/lib/locks/state-md-lock.mjs +351 -0
  340. package/scripts/lib/loop-readiness-banner.mjs +144 -0
  341. package/scripts/lib/memory-banner.mjs +478 -0
  342. package/scripts/lib/memory-cleanup/worktree-sweep.mjs +108 -0
  343. package/scripts/lib/memory-cleanup-stamp.mjs +56 -0
  344. package/scripts/lib/memory-paths.mjs +31 -0
  345. package/scripts/lib/memory-proposals/collector.mjs +334 -0
  346. package/scripts/lib/memory-proposals/schema.mjs +289 -0
  347. package/scripts/lib/memory-proposals/sink.mjs +507 -0
  348. package/scripts/lib/memory-proposals/store.mjs +441 -0
  349. package/scripts/lib/mission-status-schema.mjs +114 -0
  350. package/scripts/lib/mode-selector/alternatives.mjs +64 -0
  351. package/scripts/lib/mode-selector/constants.mjs +29 -0
  352. package/scripts/lib/mode-selector/context-pressure.mjs +157 -0
  353. package/scripts/lib/mode-selector/rationale.mjs +55 -0
  354. package/scripts/lib/mode-selector/scoring.mjs +221 -0
  355. package/scripts/lib/mode-selector-accuracy.mjs +121 -0
  356. package/scripts/lib/mode-selector.mjs +160 -0
  357. package/scripts/lib/multi-provider-build/providers.mjs +64 -0
  358. package/scripts/lib/multi-provider-build/templating.mjs +130 -0
  359. package/scripts/lib/named-baseline-resolver.mjs +233 -0
  360. package/scripts/lib/named-vault-resolver.mjs +433 -0
  361. package/scripts/lib/owner-config/coerce.mjs +29 -0
  362. package/scripts/lib/owner-config/constants.mjs +21 -0
  363. package/scripts/lib/owner-config/defaults.mjs +50 -0
  364. package/scripts/lib/owner-config/error.mjs +19 -0
  365. package/scripts/lib/owner-config/index.mjs +13 -0
  366. package/scripts/lib/owner-config/merge.mjs +52 -0
  367. package/scripts/lib/owner-config/validate.mjs +259 -0
  368. package/scripts/lib/owner-config-banner.mjs +126 -0
  369. package/scripts/lib/owner-config-loader.mjs +159 -0
  370. package/scripts/lib/owner-config.example.yaml +72 -0
  371. package/scripts/lib/owner-config.mjs +28 -0
  372. package/scripts/lib/owner-interview.mjs +243 -0
  373. package/scripts/lib/owner-yaml.mjs +571 -0
  374. package/scripts/lib/package-manager.mjs +160 -0
  375. package/scripts/lib/path-utils.mjs +217 -0
  376. package/scripts/lib/peer-cards/merger.mjs +310 -0
  377. package/scripts/lib/peer-cards/reader.mjs +125 -0
  378. package/scripts/lib/peer-cards/schema.mjs +230 -0
  379. package/scripts/lib/peer-cards/staleness-banner.mjs +86 -0
  380. package/scripts/lib/peer-cards/writer.mjs +138 -0
  381. package/scripts/lib/peer-discovery.mjs +200 -0
  382. package/scripts/lib/persona-panel/catalog-loader.mjs +577 -0
  383. package/scripts/lib/persona-panel/consolidator.mjs +370 -0
  384. package/scripts/lib/persona-panel/persona-runner.mjs +375 -0
  385. package/scripts/lib/persona-panel/threshold.mjs +130 -0
  386. package/scripts/lib/pi-hook-bridge.mjs +328 -0
  387. package/scripts/lib/platform.mjs +266 -0
  388. package/scripts/lib/playwright-driver/runner.mjs +297 -0
  389. package/scripts/lib/plugin-root.mjs +210 -0
  390. package/scripts/lib/pre-dispatch-check.mjs +126 -0
  391. package/scripts/lib/product-repo-detect.mjs +121 -0
  392. package/scripts/lib/profiles/registry.mjs +176 -0
  393. package/scripts/lib/profiles/schema.mjs +209 -0
  394. package/scripts/lib/qg-command-drift-banner.mjs +88 -0
  395. package/scripts/lib/quality-gate/diagnostics.mjs +92 -0
  396. package/scripts/lib/quality-gate.mjs +536 -0
  397. package/scripts/lib/quality-gates-cache.mjs +228 -0
  398. package/scripts/lib/quality-gates-policy.mjs +95 -0
  399. package/scripts/lib/recommendations-v0.mjs +156 -0
  400. package/scripts/lib/reconcile/eligibility.mjs +203 -0
  401. package/scripts/lib/reconcile/emitter.mjs +244 -0
  402. package/scripts/lib/reconcile/engine.mjs +412 -0
  403. package/scripts/lib/reconcile/idempotency.mjs +239 -0
  404. package/scripts/lib/reconcile/renderer.mjs +211 -0
  405. package/scripts/lib/reconcile/writer.mjs +293 -0
  406. package/scripts/lib/reconcile-nudge-banner.mjs +284 -0
  407. package/scripts/lib/resource-probe/evaluate.mjs +190 -0
  408. package/scripts/lib/resource-probe/parsers.mjs +181 -0
  409. package/scripts/lib/resource-probe/probe-platform.mjs +300 -0
  410. package/scripts/lib/resource-probe.mjs +95 -0
  411. package/scripts/lib/rule-loader.mjs +552 -0
  412. package/scripts/lib/rules-sync.mjs +439 -0
  413. package/scripts/lib/scope-gate.mjs +496 -0
  414. package/scripts/lib/session-close-backfill.mjs +539 -0
  415. package/scripts/lib/session-discovery.mjs +256 -0
  416. package/scripts/lib/session-end/phase-skip.mjs +357 -0
  417. package/scripts/lib/session-end/worktree-cleanup.mjs +112 -0
  418. package/scripts/lib/session-id.mjs +362 -0
  419. package/scripts/lib/session-lock.mjs +703 -0
  420. package/scripts/lib/session-registry.mjs +355 -0
  421. package/scripts/lib/session-schema/aliases.mjs +71 -0
  422. package/scripts/lib/session-schema/constants.mjs +115 -0
  423. package/scripts/lib/session-schema/normalizer.mjs +66 -0
  424. package/scripts/lib/session-schema/timestamps.mjs +64 -0
  425. package/scripts/lib/session-schema/validator.mjs +453 -0
  426. package/scripts/lib/session-schema.mjs +71 -0
  427. package/scripts/lib/session-token-rollup.mjs +137 -0
  428. package/scripts/lib/sessions-staleness-banner.mjs +247 -0
  429. package/scripts/lib/skill-evolution/blast-radius-classifier.mjs +114 -0
  430. package/scripts/lib/skill-evolution/candidate-intake.mjs +270 -0
  431. package/scripts/lib/skill-evolution/config-validation-gate.mjs +279 -0
  432. package/scripts/lib/skill-evolution/engine.mjs +719 -0
  433. package/scripts/lib/skill-evolution/idempotency.mjs +279 -0
  434. package/scripts/lib/skill-evolution/mr-opener.mjs +507 -0
  435. package/scripts/lib/skill-health/join.mjs +181 -0
  436. package/scripts/lib/skill-health/score.mjs +123 -0
  437. package/scripts/lib/skill-invocations-schema.mjs +214 -0
  438. package/scripts/lib/skill-judge.mjs +348 -0
  439. package/scripts/lib/skill-judgments-schema.mjs +264 -0
  440. package/scripts/lib/slopcheck.mjs +501 -0
  441. package/scripts/lib/soul-resolve.mjs +118 -0
  442. package/scripts/lib/spiral-carryover.mjs +495 -0
  443. package/scripts/lib/state-md/body-sections.mjs +851 -0
  444. package/scripts/lib/state-md/frontmatter-mutators.mjs +453 -0
  445. package/scripts/lib/state-md/mission-status.mjs +247 -0
  446. package/scripts/lib/state-md/recommendations.mjs +57 -0
  447. package/scripts/lib/state-md/yaml-parser.mjs +234 -0
  448. package/scripts/lib/state-md-peer-guard.mjs +232 -0
  449. package/scripts/lib/state-md.mjs +53 -0
  450. package/scripts/lib/subagents-schema.mjs +309 -0
  451. package/scripts/lib/sunset/walker.mjs +1192 -0
  452. package/scripts/lib/test-runner/artifact-paths.mjs +94 -0
  453. package/scripts/lib/test-runner/fingerprint.mjs +33 -0
  454. package/scripts/lib/test-runner/issue-reconcile.mjs +770 -0
  455. package/scripts/lib/tmux-layout/layouts.mjs +224 -0
  456. package/scripts/lib/tmux-layout/telemetry-stats.mjs +100 -0
  457. package/scripts/lib/tmux-layout/telemetry.mjs +88 -0
  458. package/scripts/lib/tmux-layout/tmux-shell.mjs +82 -0
  459. package/scripts/lib/tmux-layout/vcs-detector.mjs +88 -0
  460. package/scripts/lib/validate/check-agents.mjs +457 -0
  461. package/scripts/lib/validate/check-codex-plugin.mjs +37 -0
  462. package/scripts/lib/validate/check-commands.mjs +148 -0
  463. package/scripts/lib/validate/check-component-paths.mjs +112 -0
  464. package/scripts/lib/validate/check-dead-bridge.mjs +180 -0
  465. package/scripts/lib/validate/check-hooks-symmetry.mjs +258 -0
  466. package/scripts/lib/validate/check-json-files.mjs +116 -0
  467. package/scripts/lib/validate/check-owner-leakage.mjs +1011 -0
  468. package/scripts/lib/validate/check-path-utils-canary.mjs +175 -0
  469. package/scripts/lib/validate/check-peekaboo-driver-canary.mjs +201 -0
  470. package/scripts/lib/validate/check-pi-package.mjs +110 -0
  471. package/scripts/lib/validate/check-pi-prompts.mjs +43 -0
  472. package/scripts/lib/validate/check-playwright-mcp-canary.mjs +154 -0
  473. package/scripts/lib/validate/check-plugin-json.mjs +96 -0
  474. package/scripts/lib/validate/check-plugin-monitors.mjs +206 -0
  475. package/scripts/lib/validate/check-plugin-schema.mjs +137 -0
  476. package/scripts/lib/validate/check-rules.mjs +143 -0
  477. package/scripts/lib/validate/check-session-plan-routing.mjs +154 -0
  478. package/scripts/lib/validate/check-test-fixture-shapes.mjs +280 -0
  479. package/scripts/lib/validate/check-unicode-safety.mjs +533 -0
  480. package/scripts/lib/validate/confidential-names.mjs +169 -0
  481. package/scripts/lib/validate/dead-bridge-corpus.mjs +141 -0
  482. package/scripts/lib/validate/dead-bridge-detectors.mjs +568 -0
  483. package/scripts/lib/validate/tier-inference.mjs +100 -0
  484. package/scripts/lib/validate-vendored-rules.mjs +519 -0
  485. package/scripts/lib/vault-archive.mjs +404 -0
  486. package/scripts/lib/vault-backfill/glab.mjs +164 -0
  487. package/scripts/lib/vault-backfill/manifest.mjs +75 -0
  488. package/scripts/lib/vault-backfill/template.mjs +130 -0
  489. package/scripts/lib/vault-consolidate-fs.mjs +331 -0
  490. package/scripts/lib/vault-migration-rules.mjs +155 -0
  491. package/scripts/lib/vault-mirror/auto-commit.mjs +203 -0
  492. package/scripts/lib/vault-mirror/namespace.mjs +152 -0
  493. package/scripts/lib/vault-mirror/process.mjs +567 -0
  494. package/scripts/lib/vault-mirror/pseudonym-map.mjs +164 -0
  495. package/scripts/lib/vault-mirror/render-learnings.mjs +201 -0
  496. package/scripts/lib/vault-mirror/render-sessions.mjs +367 -0
  497. package/scripts/lib/vault-mirror/render.mjs +8 -0
  498. package/scripts/lib/vault-mirror/utils.mjs +217 -0
  499. package/scripts/lib/vault-relocation-rules.mjs +555 -0
  500. package/scripts/lib/vault-repo-backfill.mjs +235 -0
  501. package/scripts/lib/vault-staleness-banner.mjs +142 -0
  502. package/scripts/lib/vault-status/board-writer.mjs +769 -0
  503. package/scripts/lib/vault-status/narrative-mirror.mjs +544 -0
  504. package/scripts/lib/vault-sync-baseline.mjs +152 -0
  505. package/scripts/lib/wave-context.mjs +29 -0
  506. package/scripts/lib/wave-executor/pool.mjs +248 -0
  507. package/scripts/lib/wave-resource-gate.mjs +204 -0
  508. package/scripts/lib/wave-sizing.mjs +75 -0
  509. package/scripts/lib/webhook-url.mjs +105 -0
  510. package/scripts/lib/workspace.mjs +198 -0
  511. package/scripts/lib/worktree/constants.mjs +35 -0
  512. package/scripts/lib/worktree/index.mjs +17 -0
  513. package/scripts/lib/worktree/lifecycle.mjs +287 -0
  514. package/scripts/lib/worktree/listing.mjs +118 -0
  515. package/scripts/lib/worktree/meta.mjs +64 -0
  516. package/scripts/lib/worktree-freshness.mjs +313 -0
  517. package/scripts/lib/worktree.mjs +15 -0
  518. package/scripts/lifecycle-sim-v6.mjs +347 -0
  519. package/scripts/lock-reaper.mjs +185 -0
  520. package/scripts/mcp-server.sh +241 -0
  521. package/scripts/measure-policy-cache-effectiveness.mjs +427 -0
  522. package/scripts/memory-propose.mjs +464 -0
  523. package/scripts/migrate-cold-start-seed.mjs +404 -0
  524. package/scripts/migrate-learnings-jsonl.mjs +189 -0
  525. package/scripts/migrate-legacy-learnings.sh +61 -0
  526. package/scripts/migrate-sessions-jsonl.mjs +448 -0
  527. package/scripts/migrate-subagents-jsonl.mjs +196 -0
  528. package/scripts/migrate-vault-paths.mjs +796 -0
  529. package/scripts/parse-config.mjs +149 -0
  530. package/scripts/pi-install.mjs +117 -0
  531. package/scripts/print-applicable-rules.mjs +247 -0
  532. package/scripts/promote-vault-strict.mjs +496 -0
  533. package/scripts/relocate-vault-corpus.mjs +1178 -0
  534. package/scripts/run-migrate-v2-cross-repo.mjs +385 -0
  535. package/scripts/run-quality-gate.mjs +216 -0
  536. package/scripts/spikes/h3-agent-teams/preflight.sh +53 -0
  537. package/scripts/spikes/h3-agent-teams/run-h3.sh +112 -0
  538. package/scripts/spikes/h3-agent-teams/setup.sh +137 -0
  539. package/scripts/spikes/h3-agent-teams/toggle.sh +38 -0
  540. package/scripts/sweep-expired-learnings.mjs +135 -0
  541. package/scripts/sync-vault-schema.mjs +376 -0
  542. package/scripts/tests/fixtures/fetch-baseline/sample-rule.md +8 -0
  543. package/scripts/tmux-layout.mjs +245 -0
  544. package/scripts/token-audit.sh +191 -0
  545. package/scripts/typecheck.mjs +42 -0
  546. package/scripts/upload-social-preview.mjs +316 -0
  547. package/scripts/validate-config.mjs +46 -0
  548. package/scripts/validate-plugin-manifests.mjs +163 -0
  549. package/scripts/validate-plugin.mjs +264 -0
  550. package/scripts/validate-wave-scope.mjs +289 -0
  551. package/scripts/vault-backfill.mjs +404 -0
  552. package/scripts/vault-consolidate.mjs +596 -0
  553. package/scripts/vault-integration-watcher.mjs +394 -0
  554. package/scripts/vault-mirror.mjs +430 -0
  555. package/skills/_shared/bootstrap-gate.md +111 -0
  556. package/skills/_shared/config-reading.md +226 -0
  557. package/skills/_shared/instruction-file-resolution.md +79 -0
  558. package/skills/_shared/model-selection.md +64 -0
  559. package/skills/_shared/monitor-patterns.md +300 -0
  560. package/skills/_shared/parallel-aware-auq.md +121 -0
  561. package/skills/_shared/parallel-aware-preamble.md +185 -0
  562. package/skills/_shared/platform-tools.md +96 -0
  563. package/skills/_shared/state-ownership.md +221 -0
  564. package/skills/architecture/DEEPENING.md +37 -0
  565. package/skills/architecture/INTERFACE-DESIGN.md +44 -0
  566. package/skills/architecture/LANGUAGE.md +53 -0
  567. package/skills/architecture/SKILL.md +92 -0
  568. package/skills/autopilot/SKILL.md +419 -0
  569. package/skills/bootstrap/SKILL.md +592 -0
  570. package/skills/bootstrap/STATE.md.template +24 -0
  571. package/skills/bootstrap/_shared-template.md +243 -0
  572. package/skills/bootstrap/deep-template.md +659 -0
  573. package/skills/bootstrap/fast-template.md +251 -0
  574. package/skills/bootstrap/intensity-heuristic.md +80 -0
  575. package/skills/bootstrap/public-fallback.md +342 -0
  576. package/skills/bootstrap/standard-template.md +736 -0
  577. package/skills/bootstrap/templates/agents/project-code-review.md +18 -0
  578. package/skills/bootstrap/templates/agents/project-discovery.md +18 -0
  579. package/skills/bootstrap/templates/agents/project-quality-gate.md +18 -0
  580. package/skills/brainstorm/SKILL.md +268 -0
  581. package/skills/brainstorm/soul.md +49 -0
  582. package/skills/claude-md-drift-check/SKILL.md +186 -0
  583. package/skills/claude-md-drift-check/checker.mjs +1380 -0
  584. package/skills/claude-md-drift-check/checker.sh +37 -0
  585. package/skills/claude-md-drift-check/package.json +16 -0
  586. package/skills/convergence-monitoring/README.md +39 -0
  587. package/skills/convergence-monitoring/SIGNALS.md +246 -0
  588. package/skills/convergence-monitoring/SKILL.md +285 -0
  589. package/skills/daily/SKILL.md +222 -0
  590. package/skills/daily/generate.sh +92 -0
  591. package/skills/daily/templates/daily.md.tpl +36 -0
  592. package/skills/debug/SKILL.md +188 -0
  593. package/skills/debug/soul.md +35 -0
  594. package/skills/discovery/SKILL.md +567 -0
  595. package/skills/discovery/issue-templates.md +237 -0
  596. package/skills/discovery/probes/docs-staleness.mjs +195 -0
  597. package/skills/discovery/probes/frontend-slop.mjs +186 -0
  598. package/skills/discovery/probes/ssot-code-diff.mjs +310 -0
  599. package/skills/discovery/probes/supply-chain-slopcheck.mjs +440 -0
  600. package/skills/discovery/probes/vault-narrative-staleness.mjs +355 -0
  601. package/skills/discovery/probes/vault-staleness.mjs +272 -0
  602. package/skills/discovery/probes-arch.md +252 -0
  603. package/skills/discovery/probes-audit.md +95 -0
  604. package/skills/discovery/probes-code.md +329 -0
  605. package/skills/discovery/probes-docs.md +76 -0
  606. package/skills/discovery/probes-feature.md +150 -0
  607. package/skills/discovery/probes-infra.md +138 -0
  608. package/skills/discovery/probes-intro.md +25 -0
  609. package/skills/discovery/probes-session.md +495 -0
  610. package/skills/discovery/probes-supply-chain.md +94 -0
  611. package/skills/discovery/probes-ui.md +147 -0
  612. package/skills/discovery/probes-vault.md +64 -0
  613. package/skills/discovery/slop-patterns.md +115 -0
  614. package/skills/dispatcher/SKILL.md +173 -0
  615. package/skills/docs-orchestrator/SKILL.md +362 -0
  616. package/skills/docs-orchestrator/audience-mapping.md +140 -0
  617. package/skills/domain-model/ADR-FORMAT.md +47 -0
  618. package/skills/domain-model/CONTEXT-FORMAT.md +77 -0
  619. package/skills/domain-model/SKILL.md +85 -0
  620. package/skills/ecosystem-health/SKILL.md +119 -0
  621. package/skills/ecosystem-health/wizard.md +193 -0
  622. package/skills/eval/SKILL.md +293 -0
  623. package/skills/eval/rubric-v1.md +218 -0
  624. package/skills/evolve/SKILL.md +546 -0
  625. package/skills/frontmatter-guard/SKILL.md +126 -0
  626. package/skills/gitlab-ops/SKILL.md +368 -0
  627. package/skills/gitlab-portfolio/SKILL.md +196 -0
  628. package/skills/grill/SKILL.md +185 -0
  629. package/skills/grill/soul.md +55 -0
  630. package/skills/hook-development/SKILL.md +413 -0
  631. package/skills/mcp-builder/SKILL.md +260 -0
  632. package/skills/memory-cleanup/SKILL.md +310 -0
  633. package/skills/mode-selector/SKILL.md +226 -0
  634. package/skills/peekaboo-driver/SKILL.md +237 -0
  635. package/skills/peekaboo-driver/soul.md +32 -0
  636. package/skills/persona-panel/SKILL.md +365 -0
  637. package/skills/persona-panel/persona-format.md +205 -0
  638. package/skills/persona-panel/presets/designer-lens.md +87 -0
  639. package/skills/persona-panel/presets/engineer-lens.md +88 -0
  640. package/skills/persona-panel/presets/pm-lens.md +86 -0
  641. package/skills/plan/SKILL.md +496 -0
  642. package/skills/plan/mode-feature.md +141 -0
  643. package/skills/plan/mode-new.md +297 -0
  644. package/skills/plan/mode-retro.md +271 -0
  645. package/skills/plan/prd-feature-template.md +132 -0
  646. package/skills/plan/prd-full-template.md +151 -0
  647. package/skills/plan/prd-reviewer-prompt.md +103 -0
  648. package/skills/plan/retro-template.md +75 -0
  649. package/skills/plan/soul.md +62 -0
  650. package/skills/playwright-driver/SKILL.md +226 -0
  651. package/skills/playwright-driver/soul.md +30 -0
  652. package/skills/quality-gates/SKILL.md +212 -0
  653. package/skills/reconcile/SKILL.md +324 -0
  654. package/skills/repo-audit/SKILL.md +272 -0
  655. package/skills/session-end/SKILL.md +1044 -0
  656. package/skills/session-end/discovery-scan.md +37 -0
  657. package/skills/session-end/drift-operations.md +97 -0
  658. package/skills/session-end/learning-patterns.md +78 -0
  659. package/skills/session-end/metrics-collection.md +175 -0
  660. package/skills/session-end/phase-3-2-docs-verification.md +148 -0
  661. package/skills/session-end/phase-3-6-tail.md +344 -0
  662. package/skills/session-end/phase-3-7a-recommendations.md +86 -0
  663. package/skills/session-end/plan-verification.md +288 -0
  664. package/skills/session-end/session-metrics-write.md +223 -0
  665. package/skills/session-end/vault-operations.md +50 -0
  666. package/skills/session-end/verification-checklist.md +20 -0
  667. package/skills/session-plan/SKILL.md +554 -0
  668. package/skills/session-plan/wave-template.md +37 -0
  669. package/skills/session-start/SKILL.md +1043 -0
  670. package/skills/session-start/phase-2-5-docs-planning.md +119 -0
  671. package/skills/session-start/phase-4-5-resource-health.md +49 -0
  672. package/skills/session-start/phase-7-1-premise-check.md +47 -0
  673. package/skills/session-start/phase-7-5-mode-selector.md +237 -0
  674. package/skills/session-start/phase-8-5-express-path.md +61 -0
  675. package/skills/session-start/presentation-format.md +81 -0
  676. package/skills/session-start/soul.md +57 -0
  677. package/skills/skill-creator/SKILL.md +168 -0
  678. package/skills/spinout/SKILL.md +76 -0
  679. package/skills/sunset-review/SKILL.md +96 -0
  680. package/skills/test-runner/SKILL.md +362 -0
  681. package/skills/test-runner/rubric-v1.md +388 -0
  682. package/skills/test-runner/soul.md +46 -0
  683. package/skills/tmux-layout/SKILL.md +104 -0
  684. package/skills/ubiquitous-language/SKILL.md +97 -0
  685. package/skills/using-orchestrator/SKILL.md +144 -0
  686. package/skills/vault-mirror/SKILL.md +234 -0
  687. package/skills/vault-sync/SKILL.md +319 -0
  688. package/skills/vault-sync/package-lock.json +40 -0
  689. package/skills/vault-sync/package.json +11 -0
  690. package/skills/vault-sync/tests/fixtures/archive-test-vault/90-archive/bad-archived.md +8 -0
  691. package/skills/vault-sync/tests/fixtures/archive-test-vault/_meta/.gitkeep +0 -0
  692. package/skills/vault-sync/tests/fixtures/archive-test-vault/live-note.md +8 -0
  693. package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/_meta/.gitkeep +0 -0
  694. package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/bad-type.md +8 -0
  695. package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/good-note.md +8 -0
  696. package/skills/vault-sync/tests/fixtures/clean-vault/.obsidian/config.md +8 -0
  697. package/skills/vault-sync/tests/fixtures/clean-vault/01-projects/foo/projects-baseline.md +10 -0
  698. package/skills/vault-sync/tests/fixtures/clean-vault/03-daily/daily-2026-04-13.md +8 -0
  699. package/skills/vault-sync/tests/fixtures/clean-vault/README.md +3 -0
  700. package/skills/vault-sync/tests/fixtures/clean-vault/hello-world.md +11 -0
  701. package/skills/vault-sync/tests/fixtures/dangling-link-vault/_meta/.gitkeep +0 -0
  702. package/skills/vault-sync/tests/fixtures/dangling-link-vault/has-dangling.md +9 -0
  703. package/skills/vault-sync/tests/fixtures/dangling-link-vault/real-target.md +8 -0
  704. package/skills/vault-sync/tests/fixtures/empty-vault/_meta/.gitkeep +0 -0
  705. package/skills/vault-sync/tests/fixtures/missing-field-vault/_meta/.gitkeep +0 -0
  706. package/skills/vault-sync/tests/fixtures/missing-field-vault/missing-id.md +7 -0
  707. package/skills/vault-sync/tests/fixtures/nested-tag-vault/03-daily/daily-2026-04-13.md +9 -0
  708. package/skills/vault-sync/tests/fixtures/nested-tag-vault/_meta/.gitkeep +0 -0
  709. package/skills/vault-sync/tests/fixtures/nested-tag-vault/nested-tags-note.md +11 -0
  710. package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/README.md +3 -0
  711. package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_MOC.md +3 -0
  712. package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_meta/.gitkeep +0 -0
  713. package/skills/vault-sync/tests/fixtures/with-moc-vault/_MOC.md +11 -0
  714. package/skills/vault-sync/tests/fixtures/with-moc-vault/_meta/.gitkeep +0 -0
  715. package/skills/vault-sync/tests/fixtures/with-moc-vault/hello-world.md +11 -0
  716. package/skills/vault-sync/tests/schema-drift.test.mjs +133 -0
  717. package/skills/vault-sync/validator.mjs +658 -0
  718. package/skills/vault-sync/validator.sh +55 -0
  719. package/skills/wave-executor/SKILL.md +496 -0
  720. package/skills/wave-executor/circuit-breaker.md +169 -0
  721. package/skills/wave-executor/wave-loop.md +1043 -0
  722. package/skills/write-executable-plan/SKILL.md +237 -0
  723. package/skills/write-executable-plan/plan-template.md +154 -0
  724. package/templates/_minimal/CLAUDE.md.tmpl +41 -0
  725. package/templates/_minimal/README.md.tmpl +15 -0
  726. package/templates/_minimal/gitignore.tmpl +47 -0
  727. package/templates/_shared/harte-regeln.md +16 -0
  728. package/templates/_shared/loop.md +90 -0
  729. package/templates/_shared/rules/parallel-sessions.md +77 -0
  730. package/templates/nextjs-minimal/README.md +30 -0
  731. package/templates/nextjs-minimal/app/layout.tsx +18 -0
  732. package/templates/nextjs-minimal/app/page.tsx +7 -0
  733. package/templates/nextjs-minimal/eslint.config.mjs +16 -0
  734. package/templates/nextjs-minimal/next.config.mjs +4 -0
  735. package/templates/nextjs-minimal/package.json +27 -0
  736. package/templates/nextjs-minimal/tsconfig.json +23 -0
  737. package/templates/node-minimal/README.md +33 -0
  738. package/templates/node-minimal/eslint.config.mjs +10 -0
  739. package/templates/node-minimal/package.json +21 -0
  740. package/templates/node-minimal/src/index.ts +1 -0
  741. package/templates/node-minimal/tests/sanity.test.ts +5 -0
  742. package/templates/node-minimal/tsconfig.json +17 -0
  743. package/templates/personas/README.md +150 -0
  744. package/templates/personas/accounting-compliance.v1.md +120 -0
  745. package/templates/personas/accounting-tax-advisor.v1.md +116 -0
  746. package/templates/personas/buyer-p1-cto.v1.md +125 -0
  747. package/templates/personas/buyer-p2-kanzlei.v1.md +134 -0
  748. package/templates/personas/buyer-p3-build.v1.md +130 -0
  749. package/templates/personas/buyer-p4-tech-veto.v1.md +130 -0
  750. package/templates/personas/buyer-p5-solo.v1.md +132 -0
  751. package/templates/personas/buyer-p6-ld.v1.md +130 -0
  752. package/templates/personas/klima-ai-expert.v1.md +114 -0
  753. package/templates/personas/klima-physicist.v1.md +117 -0
  754. package/templates/python-uv/README.md +28 -0
  755. package/templates/python-uv/pyproject.toml +32 -0
  756. package/templates/python-uv/src/__PROJECT_NAME__/__init__.py +0 -0
  757. package/templates/python-uv/src/__PROJECT_NAME__/main.py +6 -0
  758. package/templates/python-uv/tests/test_sanity.py +2 -0
  759. package/templates/static-html/README.md +19 -0
  760. package/templates/static-html/index.html +15 -0
  761. package/templates/static-html/script.js +1 -0
  762. package/templates/static-html/styles.css +26 -0
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * post-tooluse-frontend-slop.mjs — PostToolUse hook that runs the deterministic
4
+ * frontend-slop detector on a UI file right after it is edited.
5
+ *
6
+ * Issue #684 (Track 2 / item B). OPT-IN, warn-only, NON-BLOCKING. PostToolUse
7
+ * fires AFTER the edit is already applied, so this hook can never block — it
8
+ * only surfaces an `additionalContext` roll-up of detector findings so the
9
+ * agent sees the slop signal on its next turn. Mirrors hooks/loop-guard.mjs.
10
+ *
11
+ * Decision flow:
12
+ * 1. shouldRunHook('frontend-slop-hook') gate — exit 0 when disabled via profile.
13
+ * 2. Read JSON payload from stdin (null-on-failure; never throws).
14
+ * 3. tool_name gate: proceed only for Edit | Write | MultiEdit, else exit 0.
15
+ * 4. file_path gate: exit 0 if absent / non-string.
16
+ * 5. Extension gate: exit 0 unless path.extname(file) ∈ SCANNABLE_EXTS (SSOT).
17
+ * 6. Session Config gate: exit 0 unless `frontend-slop-hook.enabled === true`
18
+ * (OPT-IN — default off).
19
+ * 7. detectFiles([file]) (fail-soft). No findings → exit 0 silently.
20
+ * 8. Output: additionalContext roll-up on stdout (NEVER blocks). Best-effort
21
+ * orchestrator.frontend_slop.warning event.
22
+ *
23
+ * Exit codes: 0 always (informational, never blocking).
24
+ */
25
+
26
+ import { shouldRunHook } from './_lib/profile-gate.mjs';
27
+ // Exit 0 immediately when disabled via SO_HOOK_PROFILE / SO_DISABLED_HOOKS.
28
+ if (!shouldRunHook('frontend-slop-hook')) process.exit(0);
29
+
30
+ import path from 'node:path';
31
+
32
+ import { SO_PROJECT_DIR } from '../scripts/lib/platform.mjs';
33
+ import { emitEvent } from '../scripts/lib/events.mjs';
34
+ import { detectFiles, SCANNABLE_EXTS } from '../scripts/lib/frontend-detect/detect.mjs';
35
+ import { loadFrontendSlopHookConfig } from '../scripts/lib/config/frontend-slop-hook.mjs';
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // stdin reading (inline null-on-failure — PostToolUse hooks never throw)
39
+ // ---------------------------------------------------------------------------
40
+
41
+ /**
42
+ * Read stdin to EOF (best-effort). Returns parsed JSON or null on failure.
43
+ * Uses a 5 s timeout consistent with the Claude Code hook contract.
44
+ *
45
+ * @returns {Promise<object|null>}
46
+ */
47
+ function readStdinJson() {
48
+ return new Promise((resolve) => {
49
+ if (process.stdin.readableEnded || process.stdin.closed) {
50
+ resolve(null);
51
+ return;
52
+ }
53
+ const chunks = [];
54
+ const timer = setTimeout(() => { resolve(null); }, 5_000);
55
+ process.stdin.setEncoding('utf8');
56
+ process.stdin.on('data', (c) => chunks.push(c));
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
+ // helpers
70
+ // ---------------------------------------------------------------------------
71
+
72
+ /** Tool names whose payloads carry an editable file path. */
73
+ const EDIT_TOOLS = new Set(['Edit', 'Write', 'MultiEdit']);
74
+
75
+ /**
76
+ * Resolve the session id from the hook payload. Precedence:
77
+ * session_id → parent_session_id → null.
78
+ *
79
+ * @param {object} input
80
+ * @returns {string|null}
81
+ */
82
+ function resolveSessionId(input) {
83
+ if (typeof input.session_id === 'string' && input.session_id.length > 0) {
84
+ return input.session_id;
85
+ }
86
+ if (typeof input.parent_session_id === 'string' && input.parent_session_id.length > 0) {
87
+ return input.parent_session_id;
88
+ }
89
+ return null;
90
+ }
91
+
92
+ /**
93
+ * Render the file path relative to the project root for display, falling back
94
+ * to the basename when it lives outside the root (e.g. a temp sandbox).
95
+ *
96
+ * @param {string} filePath — absolute or cwd-relative file path
97
+ * @returns {string}
98
+ */
99
+ function toRelPath(filePath) {
100
+ try {
101
+ const rel = path.relative(SO_PROJECT_DIR, filePath);
102
+ if (rel && !rel.startsWith('..') && !path.isAbsolute(rel)) return rel;
103
+ } catch { /* fall through */ }
104
+ return path.basename(filePath);
105
+ }
106
+
107
+ /**
108
+ * Build a compact one-line warning roll-up from the detector findings.
109
+ *
110
+ * @param {import('../scripts/lib/frontend-detect/detect.mjs').Finding[]} findings
111
+ * @param {string} relPath
112
+ * @returns {string}
113
+ */
114
+ function buildWarning(findings, relPath) {
115
+ const total = findings.length;
116
+ // Show up to the first 5 findings as "<rule-id> L<line>"; aggregate fpRisk.
117
+ const shown = findings.slice(0, 5).map((f) => `${f.rule} L${f.line}`);
118
+ const more = total > shown.length ? `, +${total - shown.length} more` : '';
119
+ const fpRisks = [...new Set(findings.map((f) => f.fpRisk))];
120
+ return (
121
+ `⚠ frontend-slop: ${total} finding(s) in ${relPath} — ` +
122
+ `${shown.join(', ')}${more} (fpRisk: ${fpRisks.join('/')}). ` +
123
+ `See rules/opt-in-stack/frontend.md`
124
+ );
125
+ }
126
+
127
+ // ---------------------------------------------------------------------------
128
+ // main
129
+ // ---------------------------------------------------------------------------
130
+
131
+ async function main() {
132
+ const input = await readStdinJson();
133
+ // No payload at all → nothing to scan. Exit 0 (handled in finally).
134
+ if (!input) return;
135
+
136
+ const toolName = typeof input.tool_name === 'string' ? input.tool_name : null;
137
+ // Only edit-shaped tools carry a file path worth scanning.
138
+ if (!toolName || !EDIT_TOOLS.has(toolName)) return;
139
+
140
+ const toolInput = input.tool_input;
141
+ const filePath =
142
+ toolInput && typeof toolInput.file_path === 'string' ? toolInput.file_path : null;
143
+ if (!filePath) return;
144
+
145
+ // Extension gate — reuse the detector's SSOT set, never re-list extensions.
146
+ const ext = path.extname(filePath).toLowerCase();
147
+ if (!SCANNABLE_EXTS.has(ext)) return;
148
+
149
+ // Session Config gate — OPT-IN (default off).
150
+ const config = await loadFrontendSlopHookConfig({ repoRoot: SO_PROJECT_DIR });
151
+ if (config.enabled !== true) return;
152
+
153
+ // Run the deterministic detector (fail-soft: unreadable files → []).
154
+ const findings = detectFiles([filePath]);
155
+ if (findings.length === 0) return;
156
+
157
+ const relPath = toRelPath(filePath);
158
+ const sessionId = resolveSessionId(input);
159
+
160
+ // Best-effort telemetry — must never block the hook.
161
+ const high = findings.filter((f) => f.severity === 'high').length;
162
+ const byRule = {};
163
+ for (const f of findings) byRule[f.rule] = (byRule[f.rule] ?? 0) + 1;
164
+ try {
165
+ await emitEvent('orchestrator.frontend_slop.warning', {
166
+ ...(sessionId ? { session_id: sessionId } : {}),
167
+ file: relPath,
168
+ total: findings.length,
169
+ high,
170
+ by_rule: byRule,
171
+ });
172
+ } catch { /* best-effort — hook must remain non-blocking */ }
173
+
174
+ // Output: PostToolUse cannot block an applied edit — surface the roll-up only.
175
+ process.stdout.write(JSON.stringify({
176
+ hookSpecificOutput: {
177
+ hookEventName: 'PostToolUse',
178
+ additionalContext: buildWarning(findings, relPath),
179
+ },
180
+ }));
181
+ }
182
+
183
+ // Exit 0 always — informational hook must never block Claude (#684).
184
+ main().catch(() => {}).finally(() => process.exit(0));
@@ -0,0 +1,515 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * pre-bash-destructive-guard.mjs — PreToolUse hook: blocks destructive Bash commands
4
+ * at the MAIN SESSION level.
5
+ *
6
+ * Motivation: on 2026-04-19 the main session ran `git reset --hard HEAD~1` and
7
+ * destroyed concurrent WIP. The rule (.claude/rules/parallel-sessions.md, PSA-003)
8
+ * existed but had no executable guard. This hook is that guard.
9
+ *
10
+ * Part of issue #155 (deliverable 2). Node 20+, ESM, no bash-isms.
11
+ *
12
+ * Decision flow:
13
+ * G1 tool filter — only Bash is gated
14
+ * G2 command present + string
15
+ * G3 bypass: allow-destructive-ops: true in Session Config → exit 0
16
+ * G4 policy load: .orchestrator/policy/blocked-commands.json
17
+ * Missing → exit 0 (warn). Malformed → exit 0 (warn).
18
+ * G5 rule evaluation per rule in policy.rules
19
+ * severity:"block" → exit 2 with deny message
20
+ * severity:"warn" → emit warning, exit 0 (allow)
21
+ * Special cases:
22
+ * git-stash-any: only warn when stash is non-empty
23
+ * rm-rf-destructive: path exception for .orchestrator/tmp and node_modules
24
+ * G6 no match → exit 0
25
+ *
26
+ * Telemetry (Epic #803 process-safety dimension): best-effort
27
+ * `orchestrator.destructive_guard.blocked` / `orchestrator.destructive_guard.warned`
28
+ * events are appended to events.jsonl on the block/warn paths. Payload never
29
+ * includes the raw command — only a truncated sha256 `command_hash`. Emission
30
+ * is wrapped in try/catch and happens BEFORE the block/warn outcome is
31
+ * finalized; a telemetry failure never changes the guard's decision.
32
+ */
33
+
34
+ import { readStdin, emitAllow } from '../scripts/lib/io.mjs';
35
+ import { resolveProjectDir, resolvePluginRoot } from '../scripts/lib/platform.mjs';
36
+ import { commandMatchesBlocked, tokenizeCommand } from '../scripts/lib/hardening.mjs';
37
+ import { readConfigFile } from '../scripts/lib/config.mjs';
38
+ import { readJson } from '../scripts/lib/common.mjs';
39
+ import { emitEvent } from '../scripts/lib/events.mjs';
40
+ import fs, { existsSync } from 'node:fs';
41
+ import os from 'node:os';
42
+ import path from 'node:path';
43
+ import crypto from 'node:crypto';
44
+
45
+ import { shouldRunHook } from './_lib/profile-gate.mjs';
46
+ // #211: exit 0 immediately (silent allow) when this hook is disabled via profile/env
47
+ if (!shouldRunHook('pre-bash-destructive-guard')) process.exit(0);
48
+
49
+ // Module-level policy cache (issue #250). Safe because each hook invocation runs as
50
+ // an isolated Node subprocess — state is fresh per process, never shared across calls.
51
+ // Cache is invalidated on:
52
+ // (a) resolved policy path changes (different projectDir/CWD)
53
+ // (b) file mtime advances (user edited the policy)
54
+ // Any stat/read error → skip cache + fall back to uncached read (fail-safe).
55
+ let _cachedPolicy = null;
56
+ let _cachedPolicyPath = null;
57
+ let _cachedPolicyMtimeMs = null;
58
+
59
+ async function loadPolicyCached(policyPath) {
60
+ try {
61
+ const stat = await fs.promises.stat(policyPath);
62
+ const mtimeMs = stat.mtimeMs;
63
+ if (
64
+ _cachedPolicy !== null &&
65
+ _cachedPolicyPath === policyPath &&
66
+ _cachedPolicyMtimeMs === mtimeMs
67
+ ) {
68
+ return _cachedPolicy;
69
+ }
70
+ const fresh = await readJson(policyPath);
71
+ _cachedPolicy = fresh;
72
+ _cachedPolicyPath = policyPath;
73
+ _cachedPolicyMtimeMs = mtimeMs;
74
+ return fresh;
75
+ } catch {
76
+ // On any error (stat failure, read failure), re-throw to let caller's existing
77
+ // try/catch handle the "malformed policy" branch. Do NOT poison the cache.
78
+ return readJson(policyPath);
79
+ }
80
+ }
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // Internal helpers
84
+ // ---------------------------------------------------------------------------
85
+
86
+ /**
87
+ * sha256(command) truncated to 16 hex chars — mirrors loop-guard.mjs
88
+ * hashArgs(). Never emit the raw command into telemetry (privacy).
89
+ *
90
+ * @param {string} command
91
+ * @returns {string}
92
+ */
93
+ function hashCommand(command) {
94
+ return crypto.createHash('sha256').update(command).digest('hex').slice(0, 16);
95
+ }
96
+
97
+ /**
98
+ * Resolve a session id for event payloads. Precedence mirrors
99
+ * loop-guard.mjs resolveSessionKey(): session_id → parent_session_id →
100
+ * null (field omitted from the payload when unavailable).
101
+ *
102
+ * @param {object|null} input
103
+ * @returns {string|null}
104
+ */
105
+ function resolveSessionId(input) {
106
+ if (input) {
107
+ if (typeof input.session_id === 'string' && input.session_id.length > 0) {
108
+ return input.session_id;
109
+ }
110
+ if (typeof input.parent_session_id === 'string' && input.parent_session_id.length > 0) {
111
+ return input.parent_session_id;
112
+ }
113
+ }
114
+ return null;
115
+ }
116
+
117
+ /**
118
+ * Block a command: write structured deny JSON to stdout + exit 2.
119
+ * Uses raw process.exit(2) rather than emitDeny to emit the exact
120
+ * multi-line message format required by the spec.
121
+ *
122
+ * Emits a best-effort `orchestrator.destructive_guard.blocked` telemetry
123
+ * event BEFORE exiting. Emission failure (e.g. unwritable events.jsonl path)
124
+ * must NEVER change the block outcome — the guard's block-decision is
125
+ * strictly independent of telemetry success.
126
+ */
127
+ async function blockCommand(pattern, ruleId, rationale, command, sessionId) {
128
+ const reason = [
129
+ `Destructive command blocked: '${pattern}' (rule: ${ruleId})`,
130
+ `Reason: ${rationale}`,
131
+ `Override: Set \`allow-destructive-ops: true\` in Session Config if intentional.`,
132
+ `See: issue #155, .claude/rules/parallel-sessions.md (PSA-003)`,
133
+ ].join('\n');
134
+
135
+ try {
136
+ await emitEvent('orchestrator.destructive_guard.blocked', {
137
+ ...(sessionId ? { session_id: sessionId } : {}),
138
+ rule: ruleId,
139
+ command_hash: hashCommand(command),
140
+ });
141
+ } catch {
142
+ // Best-effort — telemetry must never block or alter the guard decision.
143
+ }
144
+
145
+ // Structured deny for Claude Code hook protocol
146
+ process.stdout.write(JSON.stringify({ permissionDecision: 'deny', reason }) + '\n');
147
+ process.exit(2);
148
+ }
149
+
150
+ /**
151
+ * Resolve the policy file path, searching in priority order:
152
+ * 1. <CWD>/.orchestrator/policy/blocked-commands.json
153
+ * 2. <CLAUDE_PROJECT_DIR>/.orchestrator/policy/blocked-commands.json
154
+ * 3. <CLAUDE_PLUGIN_ROOT>/.orchestrator/policy/blocked-commands.json
155
+ * Returns the first existing path, or null if none found.
156
+ */
157
+ function resolvePolicyPath(projectDir) {
158
+ const candidates = [
159
+ path.join(process.cwd(), '.orchestrator', 'policy', 'blocked-commands.json'),
160
+ ];
161
+
162
+ if (projectDir && projectDir !== process.cwd()) {
163
+ candidates.push(path.join(projectDir, '.orchestrator', 'policy', 'blocked-commands.json'));
164
+ }
165
+
166
+ const pluginRoot = resolvePluginRoot();
167
+ if (pluginRoot) {
168
+ candidates.push(path.join(pluginRoot, '.orchestrator', 'policy', 'blocked-commands.json'));
169
+ }
170
+
171
+ for (const candidate of candidates) {
172
+ if (existsSync(candidate)) return candidate;
173
+ }
174
+ return null;
175
+ }
176
+
177
+ /**
178
+ * Check whether git stash is non-empty by running `git stash list`.
179
+ * Returns true if non-empty (should warn), false if empty (silent allow).
180
+ * On any error returns true (conservative).
181
+ */
182
+ async function isGitStashNonEmpty(projectDir) {
183
+ try {
184
+ const { $: zx$ } = await import('zx');
185
+ zx$.verbose = false;
186
+ zx$.quiet = true;
187
+ const cwd = projectDir || process.cwd();
188
+ const result = await zx$`git -C ${cwd} stash list`;
189
+ return result.stdout.trim().length > 0;
190
+ } catch {
191
+ // On git failure → conservative: warn
192
+ return true;
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Parse ALL non-flag path arguments from every `rm` invocation in a command.
198
+ *
199
+ * Hardened over the previous single-target parser (#641): handles `-r -f`,
200
+ * `-fr`, `-rf`, `--` end-of-options ordering, multiple targets per `rm`, and
201
+ * chained commands (`rm -rf /tmp/x; rm -rf src/`). Quote-aware via
202
+ * tokenizeCommand so a path with spaces inside quotes is one target.
203
+ *
204
+ * Returns an array of target path strings (possibly empty). The caller treats
205
+ * the rm-rf rule as "allowed" only when EVERY returned target is allowlisted.
206
+ *
207
+ * @param {string} command
208
+ * @returns {string[]}
209
+ */
210
+ function parseRmTargets(command) {
211
+ const tokens = tokenizeCommand(command);
212
+ const targets = [];
213
+ let i = 0;
214
+
215
+ while (i < tokens.length) {
216
+ const verb = tokens[i].text.replace(/^.*\//, ''); // basename
217
+ const isOperator = !tokens[i].quoted && /^(;|&&|\|\||\||&)$/.test(tokens[i].text);
218
+ if (isOperator) { i++; continue; }
219
+ if (verb !== 'rm') { i++; continue; }
220
+
221
+ // Consume this `rm` invocation's args until the next chain operator.
222
+ i++; // skip `rm`
223
+ let seenDashDash = false;
224
+ while (i < tokens.length) {
225
+ const tok = tokens[i];
226
+ // Stop at unquoted chain operators — they delimit the next command.
227
+ if (!tok.quoted && /^(;|&&|\|\||\||&)$/.test(tok.text)) break;
228
+ if (!seenDashDash && tok.text === '--') { seenDashDash = true; i++; continue; }
229
+ // A flag is unquoted and starts with '-' (and is not the bare '-' stdin marker).
230
+ if (!seenDashDash && !tok.quoted && tok.text.startsWith('-') && tok.text !== '-') {
231
+ i++;
232
+ continue;
233
+ }
234
+ targets.push(tok.text);
235
+ i++;
236
+ }
237
+ }
238
+
239
+ return targets;
240
+ }
241
+
242
+ /**
243
+ * Detect whether the command contains an UNQUOTED `rm` invocation carrying BOTH
244
+ * recursive (`-r`/`-R`/`--recursive`) AND force (`-f`/`--force`) semantics,
245
+ * including combined/short forms (`-rf`, `-fr`, `-r -f`). This catches flag-form
246
+ * variants the literal "rm -rf" pattern misses (#641 gap closure) while staying
247
+ * consistent with the quoted-payload guard: an `rm` that appears only inside a
248
+ * quoted token is NOT treated as an invocation here.
249
+ *
250
+ * @param {string} command
251
+ * @returns {boolean}
252
+ */
253
+ function commandHasRecursiveForceRm(command) {
254
+ const tokens = tokenizeCommand(command);
255
+ let i = 0;
256
+ while (i < tokens.length) {
257
+ const tok = tokens[i];
258
+ const verb = tok.text.replace(/^.*\//, ''); // basename
259
+ if (tok.quoted || verb !== 'rm') { i++; continue; }
260
+
261
+ // Scan this rm invocation's flags until the next unquoted chain operator.
262
+ i++; // skip `rm`
263
+ let recursive = false;
264
+ let force = false;
265
+ let seenDashDash = false;
266
+ while (i < tokens.length) {
267
+ const t = tokens[i];
268
+ if (!t.quoted && /^(;|&&|\|\||\||&)$/.test(t.text)) break;
269
+ if (!seenDashDash && t.text === '--') { seenDashDash = true; i++; continue; }
270
+ if (!seenDashDash && !t.quoted && t.text.startsWith('-') && t.text !== '-') {
271
+ if (t.text === '--recursive') recursive = true;
272
+ else if (t.text === '--force') force = true;
273
+ else if (/^-[a-zA-Z]+$/.test(t.text)) {
274
+ // Bundled short flags: -rf, -fr, -Rf, etc.
275
+ if (/[rR]/.test(t.text)) recursive = true;
276
+ if (/f/.test(t.text)) force = true;
277
+ }
278
+ i++;
279
+ continue;
280
+ }
281
+ i++; // non-flag arg (a target) — skip
282
+ }
283
+ if (recursive && force) return true;
284
+ }
285
+ return false;
286
+ }
287
+
288
+ /**
289
+ * Return true when a single path is a safe `rm -rf` target.
290
+ *
291
+ * Safe targets:
292
+ * - <projectRoot>/.orchestrator/tmp (any depth)
293
+ * - <projectRoot>/node_modules (any depth)
294
+ * - /tmp/ (any depth) — agent-owned scratch
295
+ * - /private/tmp/ (any depth) — macOS canonical /tmp
296
+ * - resolved os.tmpdir() / $TMPDIR (any depth)
297
+ *
298
+ * The /tmp-class prefixes come from the rule's optional `path-allowlist` and are
299
+ * resolved at runtime here.
300
+ *
301
+ * @param {string} targetPath
302
+ * @param {string} projectDir
303
+ * @param {string[]} ruleAllowlist — `path-allowlist` entries (e.g. "/tmp/", "$TMPDIR")
304
+ * @returns {boolean}
305
+ */
306
+ function isRmPathAllowed(targetPath, projectDir, ruleAllowlist = []) {
307
+ if (!targetPath) return false;
308
+
309
+ const base = projectDir || process.cwd();
310
+ const wasAbsolute = path.isAbsolute(targetPath);
311
+
312
+ // Project-relative safe dirs (always allowed, independent of the rule allowlist).
313
+ const abs = wasAbsolute
314
+ ? path.normalize(targetPath)
315
+ : path.resolve(base, targetPath);
316
+
317
+ const safeProjectDirs = [
318
+ path.join(base, '.orchestrator', 'tmp'),
319
+ path.join(base, 'node_modules'),
320
+ ];
321
+ for (const safe of safeProjectDirs) {
322
+ if (abs === safe || abs.startsWith(safe + path.sep)) return true;
323
+ }
324
+
325
+ // Rule-driven absolute prefixes (/tmp/, /private/tmp/, $TMPDIR).
326
+ // Apply ONLY to targets that were ABSOLUTE in the command. A bare relative
327
+ // target (e.g. "src/") is project-relative and must NEVER be allowlisted by a
328
+ // /tmp prefix just because the project dir itself happens to live under /tmp
329
+ // (the case on CI runners where os.tmpdir() === /tmp). #641.
330
+ if (wasAbsolute) {
331
+ for (const prefix of resolveAllowlistPrefixes(ruleAllowlist)) {
332
+ // The target must be the prefix dir itself or a descendant of it.
333
+ if (abs === prefix || abs.startsWith(prefix + path.sep)) return true;
334
+ }
335
+ }
336
+
337
+ return false;
338
+ }
339
+
340
+ /**
341
+ * Resolve the rule's `path-allowlist` entries into concrete absolute directory
342
+ * prefixes. `$TMPDIR` expands to env.TMPDIR (if set) and os.tmpdir(); literal
343
+ * paths are normalised. Trailing slashes are stripped for prefix comparison.
344
+ *
345
+ * @param {string[]} ruleAllowlist
346
+ * @returns {string[]} normalised absolute prefixes (no trailing slash)
347
+ */
348
+ function resolveAllowlistPrefixes(ruleAllowlist) {
349
+ const out = new Set();
350
+ const add = (p) => {
351
+ if (!p) return;
352
+ const norm = path.normalize(p).replace(/[/\\]+$/, '');
353
+ if (norm) out.add(norm);
354
+ };
355
+ const tempRoots = ['/tmp', '/private/tmp', '/var/folders'].map((p) => path.normalize(p));
356
+ const addTemp = (p) => {
357
+ if (!p || !path.isAbsolute(p)) return;
358
+ const norm = path.normalize(p).replace(/[/\\]+$/, '');
359
+ if (tempRoots.some((root) => norm === root || norm.startsWith(root + path.sep))) {
360
+ out.add(norm);
361
+ }
362
+ };
363
+
364
+ for (const entry of Array.isArray(ruleAllowlist) ? ruleAllowlist : []) {
365
+ if (typeof entry !== 'string' || entry.length === 0) continue;
366
+ if (entry === '$TMPDIR' || entry === '${TMPDIR}') {
367
+ if (process.env.TMPDIR) addTemp(process.env.TMPDIR);
368
+ addTemp(os.tmpdir());
369
+ continue;
370
+ }
371
+ if (path.isAbsolute(entry)) add(entry);
372
+ }
373
+
374
+ return [...out];
375
+ }
376
+
377
+ // ---------------------------------------------------------------------------
378
+ // Main
379
+ // ---------------------------------------------------------------------------
380
+
381
+ async function main() {
382
+ const input = await readStdin();
383
+ if (!input) return emitAllow();
384
+
385
+ // G1 — only Bash is gated
386
+ if (input.tool_name !== 'Bash') return emitAllow();
387
+
388
+ // G2 — command must be a non-empty string
389
+ const command = input?.tool_input?.command;
390
+ if (typeof command !== 'string' || command.length === 0) return emitAllow();
391
+
392
+ const projectDir = resolveProjectDir();
393
+ const sessionId = resolveSessionId(input);
394
+
395
+ // G3 — bypass: allow-destructive-ops: true in Session Config
396
+ // Note: parseSessionConfig only returns known fields; allow-destructive-ops is
397
+ // a new field, so we parse the raw markdown for it directly.
398
+ try {
399
+ const mdContent = await readConfigFile(projectDir);
400
+ // Look for "allow-destructive-ops: true" in the Session Config block.
401
+ // Simple line-based scan: find lines under ## Session Config, stop at next ##.
402
+ const lines = mdContent.split(/\r?\n/);
403
+ let inConfig = false;
404
+ for (const line of lines) {
405
+ if (line === '## Session Config') { inConfig = true; continue; }
406
+ if (inConfig && /^## /.test(line)) break;
407
+ if (inConfig) {
408
+ const m = line.match(/^\s*(?:-\s+\*\*)?allow-destructive-ops(?::\*\*)?\s*:\s*(\S+)/);
409
+ if (m && m[1].toLowerCase() === 'true') {
410
+ process.stderr.write('ℹ destructive-guard bypassed\n');
411
+ return emitAllow();
412
+ }
413
+ }
414
+ }
415
+ } catch {
416
+ // No config file or parse error — proceed to policy check
417
+ }
418
+
419
+ // G4 — policy load
420
+ const policyPath = resolvePolicyPath(projectDir);
421
+ if (!policyPath) {
422
+ process.stderr.write(
423
+ '⚠ pre-bash-destructive-guard: policy file not found ' +
424
+ '(.orchestrator/policy/blocked-commands.json) — skipping guard\n'
425
+ );
426
+ return emitAllow();
427
+ }
428
+
429
+ let policy;
430
+ try {
431
+ policy = await loadPolicyCached(policyPath);
432
+ } catch {
433
+ process.stderr.write(
434
+ '⚠ pre-bash-destructive-guard: policy file is malformed (invalid JSON) — skipping guard\n'
435
+ );
436
+ return emitAllow();
437
+ }
438
+
439
+ if (!policy || !Array.isArray(policy.rules)) {
440
+ process.stderr.write(
441
+ '⚠ pre-bash-destructive-guard: policy file missing .rules array — skipping guard\n'
442
+ );
443
+ return emitAllow();
444
+ }
445
+
446
+ // G5 — rule evaluation
447
+ for (const rule of policy.rules) {
448
+ const { id, pattern, severity, rationale = '' } = rule;
449
+
450
+ // The rm-rf-destructive rule also fires for recursive+force rm flag variants
451
+ // the literal "rm -rf" pattern misses (`rm -r -f`, `rm -fr`) — #641 gap closure.
452
+ const matched = id === 'rm-rf-destructive'
453
+ ? (commandMatchesBlocked(command, pattern) || commandHasRecursiveForceRm(command))
454
+ : commandMatchesBlocked(command, pattern);
455
+ if (!matched) continue;
456
+
457
+ if (severity === 'warn') {
458
+ // Special: git-stash-any — only warn when stash is non-empty
459
+ if (id === 'git-stash-any') {
460
+ const nonEmpty = await isGitStashNonEmpty(projectDir);
461
+ if (!nonEmpty) {
462
+ // Empty stash — silent allow
463
+ continue;
464
+ }
465
+ }
466
+ process.stderr.write(
467
+ `⚠ pre-bash-destructive-guard: '${pattern}' (rule: ${id}) — ${rationale}\n`
468
+ );
469
+ // Best-effort telemetry — must never affect the warn/allow outcome.
470
+ try {
471
+ await emitEvent('orchestrator.destructive_guard.warned', {
472
+ ...(sessionId ? { session_id: sessionId } : {}),
473
+ rule: id,
474
+ command_hash: hashCommand(command),
475
+ });
476
+ } catch {
477
+ // Best-effort — telemetry must never block or alter the guard decision.
478
+ }
479
+ // warn → allow (exit 0), continue checking remaining rules
480
+ continue;
481
+ }
482
+
483
+ if (severity === 'block') {
484
+ // Special: rm-rf-destructive — path exception
485
+ if (id === 'rm-rf-destructive') {
486
+ const ruleAllowlist = Array.isArray(rule['path-allowlist']) ? rule['path-allowlist'] : [];
487
+ const targets = parseRmTargets(command);
488
+ // Allow ONLY when there is at least one target AND every target is
489
+ // allowlisted. An unparseable command (no targets) or any non-allowlisted
490
+ // target → block (conservative). This makes mixed chains like
491
+ // `rm -rf /tmp/x; rm -rf src/` block on the src/ target.
492
+ const allAllowed =
493
+ targets.length > 0 &&
494
+ targets.every((t) => isRmPathAllowed(t, projectDir, ruleAllowlist));
495
+ if (allAllowed) {
496
+ // Safe paths only (.orchestrator/tmp, node_modules, /tmp, $TMPDIR) — allow
497
+ continue;
498
+ }
499
+ }
500
+ await blockCommand(pattern, id, rationale, command, sessionId);
501
+ }
502
+ // Unknown severity → skip (conservative allow for unknown future severities)
503
+ }
504
+
505
+ // G6 — no blocking match
506
+ return emitAllow();
507
+ }
508
+
509
+ // Top-level error handler — never let exit 1 leak
510
+ main().catch((e) => {
511
+ process.stderr.write(
512
+ `⚠ pre-bash-destructive-guard: internal error — ${e?.message || e}\n`
513
+ );
514
+ process.exit(0); // fail-open on internal errors to avoid blocking legitimate work
515
+ });