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,240 @@
1
+ /**
2
+ * cold-start-detector.mjs — F1.3 Cold-Start Abandonment Fix
3
+ *
4
+ * Detects whether a repo is in a "bootstrapped but never used" state and
5
+ * produces a banner-emit decision for the SessionStart hook. The headline
6
+ * adoption finding (PRD 2026-05-21, §Background) is that 58% of audited
7
+ * repos never get past bootstrap — the `SessionStart` hook stays silent and
8
+ * the operator never returns. This detector adds one nudge.
9
+ *
10
+ * Behaviour (PRD §F1.3):
11
+ * 1. Skip if `bootstrap.lock` is missing (repo not yet bootstrapped —
12
+ * the bootstrap-gate handles that path).
13
+ * 2. Skip if `sessions.jsonl` line count ≥ `silence-after-sessions`
14
+ * (operator has already engaged at least once).
15
+ * 3. Skip if bootstrap age < `nudge-after-hours` (give the operator a
16
+ * reasonable window after bootstrap before nudging).
17
+ * 4. Emit otherwise, with a `markerPath` when the migration marker
18
+ * `.orchestrator/welcome-banner-pending` is present (the hook then
19
+ * calls `consumeMarker()` after emission).
20
+ *
21
+ * Hard contract:
22
+ * - Never throws. Any error → return {shouldEmit: false, reason: '...'}.
23
+ * - Pure async fs.promises — must not block the SessionStart hook's 5s
24
+ * budget. Internally uses stat + readFile only on small files.
25
+ * - No external deps. ESM only. Node 20+ `node:fs/promises`.
26
+ *
27
+ * Wired from `hooks/on-session-start.mjs` immediately after the host-banner
28
+ * block (~line 240) and before the registry/peer block.
29
+ *
30
+ * @module cold-start-detector
31
+ */
32
+
33
+ import { stat, readFile, unlink } from 'node:fs/promises';
34
+ import path from 'node:path';
35
+
36
+ import { parseBootstrapLock } from './bootstrap-lock-freshness.mjs';
37
+
38
+ /** Milliseconds in one hour — exported for testability. */
39
+ export const MS_PER_HOUR = 60 * 60 * 1000;
40
+
41
+ /** Relative path of the migration-seed marker file. */
42
+ export const WELCOME_MARKER_REL = path.join('.orchestrator', 'welcome-banner-pending');
43
+
44
+ /** Relative path of the bootstrap lock. */
45
+ const BOOTSTRAP_LOCK_REL = path.join('.orchestrator', 'bootstrap.lock');
46
+
47
+ /** Relative path of the sessions roll-up (NDJSON). */
48
+ const SESSIONS_JSONL_REL = path.join('.orchestrator', 'metrics', 'sessions.jsonl');
49
+
50
+ /**
51
+ * Cold-start banner copy. English first (Anthropic Claude Code surface).
52
+ * Returned as `bannerLines` so the caller can join('\n') for stdout emission
53
+ * or splice in additional context (e.g. bootstrap timestamp) without
54
+ * re-parsing the message.
55
+ *
56
+ * Returned array, not a constant — keeps the function pure (no shared
57
+ * mutable state).
58
+ *
59
+ * @param {{bootstrappedAt?: string|null}} [opts]
60
+ * @returns {string[]}
61
+ */
62
+ function buildBannerLines({ bootstrappedAt = null } = {}) {
63
+ const tail = bootstrappedAt
64
+ ? [`Bootstrap completed: ${bootstrappedAt}`]
65
+ : [];
66
+ return [
67
+ '📚 First session not yet — your repo is set up but the orchestrator hasn\'t run anything yet.',
68
+ '',
69
+ 'What this gives you:',
70
+ ' • Auto-detects open issues, branch state, and CI status at session-start',
71
+ ' • Distributes work across parallel subagents with file-lane isolation',
72
+ ' • Captures learnings and decisions to vault automatically',
73
+ '',
74
+ 'Try: `/session housekeeping` ← 3-5 minutes, low-risk warm-up',
75
+ ...tail,
76
+ ];
77
+ }
78
+
79
+ /**
80
+ * Best-effort line counter for NDJSON files. Returns 0 when the file is
81
+ * missing OR empty OR unreadable — all three are equivalent for the
82
+ * cold-start decision ("no sessions yet").
83
+ *
84
+ * Reads the full file because sessions.jsonl is small (1 line/session, ~2 KB
85
+ * each — even 100 sessions = ~200 KB).
86
+ *
87
+ * @param {string} filePath
88
+ * @returns {Promise<number>}
89
+ */
90
+ async function countLines(filePath) {
91
+ try {
92
+ const raw = await readFile(filePath, 'utf8');
93
+ if (!raw) return 0;
94
+ // Trailing newline must not inflate the count. split-then-filter handles
95
+ // both Unix (\n) and Windows (\r\n) line endings.
96
+ const lines = raw.split('\n').filter((l) => l.length > 0);
97
+ return lines.length;
98
+ } catch {
99
+ return 0;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Existence check via fs.promises.stat. Returns true only when the path
105
+ * resolves to a regular file or directory. Any error → false.
106
+ *
107
+ * @param {string} p
108
+ * @returns {Promise<boolean>}
109
+ */
110
+ async function exists(p) {
111
+ try {
112
+ await stat(p);
113
+ return true;
114
+ } catch {
115
+ return false;
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Detect cold-start state and produce a banner-emit decision.
121
+ *
122
+ * @param {object} opts
123
+ * @param {string} opts.repoRoot - resolved repo root (caller passes
124
+ * `resolveProjectDir()` from platform.mjs)
125
+ * @param {number} [opts.nudgeAfterHours=1] - PRD default 1 — emit only if
126
+ * bootstrap age >= this. Override via Session Config
127
+ * `cold-start.nudge-after-hours`.
128
+ * @param {number} [opts.silenceAfterSessions=1] - PRD default 1 — silence
129
+ * after >= this many sessions exist. Override via Session Config
130
+ * `cold-start.silence-after-sessions`.
131
+ * @param {boolean} [opts.enabled=true] - master switch (Session Config
132
+ * `cold-start.enabled`). When false, returns shouldEmit=false with
133
+ * reason='disabled'.
134
+ * @param {number} [opts.now] - injected clock for tests (defaults to
135
+ * Date.now()).
136
+ * @returns {Promise<{shouldEmit: boolean, reason: string, markerPath?: string, bannerLines?: string[]}>}
137
+ */
138
+ export async function detectColdStart(opts = {}) {
139
+ const {
140
+ repoRoot,
141
+ nudgeAfterHours = 1,
142
+ silenceAfterSessions = 1,
143
+ enabled = true,
144
+ now = Date.now(),
145
+ } = opts;
146
+
147
+ // Defensive: missing repoRoot is a programming error, but the hook
148
+ // contract is "never break" — fall through cleanly.
149
+ if (!repoRoot || typeof repoRoot !== 'string') {
150
+ return { shouldEmit: false, reason: 'no-repo-root' };
151
+ }
152
+
153
+ if (enabled === false) {
154
+ return { shouldEmit: false, reason: 'disabled' };
155
+ }
156
+
157
+ const lockPath = path.join(repoRoot, BOOTSTRAP_LOCK_REL);
158
+ const sessionsPath = path.join(repoRoot, SESSIONS_JSONL_REL);
159
+ const markerPath = path.join(repoRoot, WELCOME_MARKER_REL);
160
+
161
+ // PRD §F1.3 Unwanted behaviour: if bootstrap.lock missing, do NOT emit.
162
+ // Bootstrap-gate handles that path — emitting here would race the gate.
163
+ if (!(await exists(lockPath))) {
164
+ return { shouldEmit: false, reason: 'no-bootstrap-lock' };
165
+ }
166
+
167
+ // PRD §F1.3 Ubiquitous: silence once the repo has ≥ N sessions. This is
168
+ // the auto-silence path — banner is one-shot per repo (default N=1).
169
+ const sessionCount = await countLines(sessionsPath);
170
+ if (sessionCount >= silenceAfterSessions) {
171
+ return {
172
+ shouldEmit: false,
173
+ reason: `sessions-floor-met (${sessionCount} >= ${silenceAfterSessions})`,
174
+ };
175
+ }
176
+
177
+ // PRD §F1.3 Event-driven: bootstrap age >= nudge-after-hours.
178
+ let bootstrappedAt;
179
+ try {
180
+ const lockRaw = await readFile(lockPath, 'utf8');
181
+ const parsed = parseBootstrapLock(lockRaw);
182
+ // Prefer modern `bootstrapped-at` (post-#186); fall back to legacy
183
+ // `timestamp` for older locks.
184
+ bootstrappedAt = parsed['bootstrapped-at'] || parsed['timestamp'] || null;
185
+ } catch {
186
+ return { shouldEmit: false, reason: 'lock-read-failed' };
187
+ }
188
+
189
+ if (!bootstrappedAt) {
190
+ return { shouldEmit: false, reason: 'lock-no-timestamp' };
191
+ }
192
+
193
+ const ts = Date.parse(bootstrappedAt);
194
+ if (Number.isNaN(ts)) {
195
+ return { shouldEmit: false, reason: 'lock-unparseable-timestamp' };
196
+ }
197
+
198
+ const ageMs = now - ts;
199
+ const thresholdMs = nudgeAfterHours * MS_PER_HOUR;
200
+ if (ageMs < thresholdMs) {
201
+ const ageHours = Math.floor(ageMs / MS_PER_HOUR);
202
+ return {
203
+ shouldEmit: false,
204
+ reason: `bootstrap-too-fresh (${ageHours}h < ${nudgeAfterHours}h)`,
205
+ };
206
+ }
207
+
208
+ // All conditions met — emit. Marker presence is informational: the hook
209
+ // calls `consumeMarker(markerPath)` only when this field is non-null.
210
+ const markerPresent = await exists(markerPath);
211
+
212
+ return {
213
+ shouldEmit: true,
214
+ reason: markerPresent
215
+ ? 'migration-marker-present'
216
+ : `bootstrap-age-met (${Math.floor(ageMs / MS_PER_HOUR)}h >= ${nudgeAfterHours}h, sessions=${sessionCount})`,
217
+ markerPath: markerPresent ? markerPath : undefined,
218
+ bannerLines: buildBannerLines({ bootstrappedAt }),
219
+ };
220
+ }
221
+
222
+ /**
223
+ * Delete the migration-seed marker after the banner has been emitted.
224
+ *
225
+ * Best-effort: any failure (already deleted by another session, permission
226
+ * issue, missing parent dir) is swallowed. The next session-start will
227
+ * re-evaluate via the standard sessions-jsonl path.
228
+ *
229
+ * @param {string} markerPath - absolute path returned by `detectColdStart()`
230
+ * @returns {Promise<boolean>} true on successful delete, false otherwise
231
+ */
232
+ export async function consumeMarker(markerPath) {
233
+ if (!markerPath || typeof markerPath !== 'string') return false;
234
+ try {
235
+ await unlink(markerPath);
236
+ return true;
237
+ } catch {
238
+ return false;
239
+ }
240
+ }
@@ -0,0 +1,458 @@
1
+ /**
2
+ * command-blocker.mjs — command-blocking tokenizer + matcher.
3
+ *
4
+ * Split out of scripts/lib/hardening.mjs (concern C). This is the
5
+ * destructive-command guard powering pre-bash-destructive-guard /
6
+ * enforce-commands — security-sensitive; behaviour is preserved EXACTLY.
7
+ * Re-exported by hardening.mjs as a barrel so existing importers keep working
8
+ * unchanged.
9
+ *
10
+ * Layering: hook-safe — pure functions only; no I/O at import time;
11
+ * ESM-pure for fast hook hot-paths. Hooks (under `hooks/`) import from
12
+ * this lib; this lib MUST NOT reverse-import from `hooks/`. Cross-cutting
13
+ * invariant for all exports below — see #554 A2.
14
+ */
15
+
16
+ /**
17
+ * Shell interpreters whose QUOTED argument text is still executed as a command.
18
+ * When a command segment's argv[0] (or `command <verb>` / `env … <verb>`) is one
19
+ * of these, a blocked pattern found inside a quoted token is NOT inert — it is the
20
+ * payload the interpreter will run. Includes SQL executors (`psql -c "DROP TABLE …"`)
21
+ * and `find` (`find . -exec rm -rf {} \;`).
22
+ *
23
+ * Used by the quoted-payload guard in commandMatchesBlocked (#641).
24
+ */
25
+ const SHELL_EXEC_INTERPRETERS = new Set([
26
+ 'bash', 'sh', 'zsh', 'dash', 'ksh',
27
+ 'eval', 'xargs', 'env', 'command',
28
+ 'psql', 'mysql', 'sqlite3',
29
+ 'find',
30
+ ]);
31
+
32
+ function matchIfsWhitespaceExpansion(command, index) {
33
+ if (command.startsWith('${IFS', index)) {
34
+ const end = command.indexOf('}', index + 2);
35
+ if (end !== -1) {
36
+ const body = command.slice(index + 2, end);
37
+ if (body === 'IFS' || /^IFS:-\s*$/.test(body)) return end + 1;
38
+ }
39
+ }
40
+
41
+ if (command.startsWith('$IFS', index)) {
42
+ const next = command[index + 4] ?? '';
43
+ if (!/[A-Za-z0-9_]/.test(next)) return index + 4;
44
+ }
45
+
46
+ return -1;
47
+ }
48
+
49
+ function isWhitespaceCode(code) {
50
+ return Number.isFinite(code) && /\s/.test(String.fromCharCode(code));
51
+ }
52
+
53
+ function matchAnsiCWhitespaceQuote(command, index) {
54
+ if (!command.startsWith("$'", index)) return -1;
55
+
56
+ let i = index + 2;
57
+ let sawWhitespace = false;
58
+ while (i < command.length) {
59
+ const ch = command[i];
60
+ if (ch === "'") return sawWhitespace ? i + 1 : -1;
61
+
62
+ if (ch === '\\') {
63
+ if (i + 1 >= command.length) return -1;
64
+ const next = command[i + 1];
65
+
66
+ if (next === 't' || next === 'n' || next === 'r' || next === 'v' || next === 'f') {
67
+ sawWhitespace = true;
68
+ i += 2;
69
+ continue;
70
+ }
71
+
72
+ if (next === 'x') {
73
+ const hex = command.slice(i + 2).match(/^[0-9A-Fa-f]{1,2}/)?.[0];
74
+ if (!hex) return -1;
75
+ if (!isWhitespaceCode(Number.parseInt(hex, 16))) return -1;
76
+ sawWhitespace = true;
77
+ i += 2 + hex.length;
78
+ continue;
79
+ }
80
+
81
+ if (/^[0-7]$/.test(next)) {
82
+ const octal = command.slice(i + 1).match(/^[0-7]{1,3}/)?.[0];
83
+ if (!octal) return -1;
84
+ if (!isWhitespaceCode(Number.parseInt(octal, 8))) return -1;
85
+ sawWhitespace = true;
86
+ i += 1 + octal.length;
87
+ continue;
88
+ }
89
+
90
+ if (/\s/.test(next)) {
91
+ sawWhitespace = true;
92
+ i += 2;
93
+ continue;
94
+ }
95
+
96
+ return -1;
97
+ }
98
+
99
+ if (/\s/.test(ch)) {
100
+ sawWhitespace = true;
101
+ i++;
102
+ continue;
103
+ }
104
+
105
+ return -1;
106
+ }
107
+
108
+ return -1;
109
+ }
110
+
111
+ function matchShellWhitespaceExpansion(command, index) {
112
+ const ifsEnd = matchIfsWhitespaceExpansion(command, index);
113
+ if (ifsEnd !== -1) return ifsEnd;
114
+ return matchAnsiCWhitespaceQuote(command, index);
115
+ }
116
+
117
+ /**
118
+ * Normalize common shell whitespace obfuscations into literal spaces before
119
+ * guard parsing. This is intentionally narrow: it recognizes IFS expansions and
120
+ * ANSI-C quotes that decode entirely to whitespace, not arbitrary shell syntax.
121
+ *
122
+ * By default single-quoted text is preserved because the outer shell treats it
123
+ * literally. Callers that inspect shell-interpreter payload strings can opt in
124
+ * to `expandSingleQuoted` because those strings are parsed by a later shell.
125
+ *
126
+ * @param {string} command
127
+ * @param {{ expandSingleQuoted?: boolean, expandDoubleQuoted?: boolean }} [options]
128
+ * @returns {string}
129
+ */
130
+ function normalizeShellWhitespaceExpansions(command, options = {}) {
131
+ const expandSingleQuoted = options.expandSingleQuoted === true;
132
+ const expandDoubleQuoted = options.expandDoubleQuoted !== false;
133
+
134
+ let out = '';
135
+ let state = 'normal';
136
+
137
+ for (let i = 0; i < command.length;) {
138
+ const ch = command[i];
139
+
140
+ if (state === 'single') {
141
+ if (ch === "'") { state = 'normal'; out += ch; i++; continue; }
142
+ if (expandSingleQuoted) {
143
+ const end = matchShellWhitespaceExpansion(command, i);
144
+ if (end !== -1) { out += ' '; i = end; continue; }
145
+ }
146
+ out += ch;
147
+ i++;
148
+ continue;
149
+ }
150
+
151
+ if (state === 'double') {
152
+ if (ch === '"') { state = 'normal'; out += ch; i++; continue; }
153
+ if (expandDoubleQuoted) {
154
+ const end = matchShellWhitespaceExpansion(command, i);
155
+ if (end !== -1) { out += ' '; i = end; continue; }
156
+ }
157
+ if (ch === '\\' && i + 1 < command.length) {
158
+ out += ch + command[i + 1];
159
+ i += 2;
160
+ continue;
161
+ }
162
+ out += ch;
163
+ i++;
164
+ continue;
165
+ }
166
+
167
+ const end = matchShellWhitespaceExpansion(command, i);
168
+ if (end !== -1) { out += ' '; i = end; continue; }
169
+ if (ch === '\\' && i + 1 < command.length) {
170
+ out += ch + command[i + 1];
171
+ i += 2;
172
+ continue;
173
+ }
174
+ if (ch === "'") { state = 'single'; out += ch; i++; continue; }
175
+ if (ch === '"') { state = 'double'; out += ch; i++; continue; }
176
+
177
+ out += ch;
178
+ i++;
179
+ }
180
+
181
+ return out;
182
+ }
183
+
184
+ /**
185
+ * Hand-rolled quote-aware command lexer.
186
+ *
187
+ * Splits a command string into tokens on UNQUOTED whitespace, tracking single- and
188
+ * double-quote state and backslash escapes. Each token records whether ANY of its
189
+ * characters originated inside quotes (`quoted: true`). Quote characters and the
190
+ * escaping backslash are consumed (not part of the token text), so the returned
191
+ * token text is the logical argument value a shell would pass.
192
+ *
193
+ * This is deliberately NOT node:util.parseArgs — parseArgs operates on an already-
194
+ * tokenized argv array and does not lex raw shell strings with quote semantics.
195
+ * No new npm dependency is introduced (hook hot-path constraint).
196
+ *
197
+ * Notes / scope (sufficient for the guard, not a full POSIX shell parser):
198
+ * - Single quotes: literal, no escapes inside (POSIX).
199
+ * - Double quotes: backslash escapes the next char.
200
+ * - Outside quotes: backslash escapes the next char (incl. whitespace → same token).
201
+ * - A token that mixes quoted + unquoted runs (e.g. foo"bar") is `quoted: true`
202
+ * because part of it came from a quoted run — conservative for the guard.
203
+ *
204
+ * @param {string} command
205
+ * @returns {Array<{ text: string, quoted: boolean }>}
206
+ */
207
+ export function tokenizeCommand(command) {
208
+ const tokens = [];
209
+ if (typeof command !== 'string' || command.length === 0) return tokens;
210
+ command = normalizeShellWhitespaceExpansions(command);
211
+
212
+ let text = '';
213
+ let started = false; // a token is in progress
214
+ let sawQuote = false; // any char of the current token came from inside quotes
215
+ let state = 'normal'; // 'normal' | 'single' | 'double'
216
+
217
+ const flush = () => {
218
+ if (started) {
219
+ tokens.push({ text, quoted: sawQuote });
220
+ text = '';
221
+ started = false;
222
+ sawQuote = false;
223
+ }
224
+ };
225
+
226
+ for (let i = 0; i < command.length; i++) {
227
+ const ch = command[i];
228
+
229
+ if (state === 'single') {
230
+ if (ch === "'") { state = 'normal'; continue; }
231
+ text += ch; started = true; sawQuote = true;
232
+ continue;
233
+ }
234
+
235
+ if (state === 'double') {
236
+ if (ch === '"') { state = 'normal'; continue; }
237
+ if (ch === '\\' && i + 1 < command.length) {
238
+ const next = command[i + 1];
239
+ // In double quotes, backslash only escapes a small set; keep it simple:
240
+ // consume the backslash and take the next char literally.
241
+ text += next; started = true; sawQuote = true; i++;
242
+ continue;
243
+ }
244
+ text += ch; started = true; sawQuote = true;
245
+ continue;
246
+ }
247
+
248
+ // state === 'normal'
249
+ if (ch === "'") { state = 'single'; started = true; continue; }
250
+ if (ch === '"') { state = 'double'; started = true; continue; }
251
+ if (ch === '\\' && i + 1 < command.length) {
252
+ text += command[i + 1]; started = true; i++;
253
+ continue;
254
+ }
255
+ if (/\s/.test(ch)) { flush(); continue; }
256
+
257
+ // Unquoted shell control operators become standalone tokens so chain-splitting
258
+ // and per-segment verb detection work even without surrounding whitespace
259
+ // (e.g. `/tmp/x;rm -rf src/`). Recognised: ; && || | & — longest match first.
260
+ if (ch === ';' || ch === '|' || ch === '&') {
261
+ flush();
262
+ let op = ch;
263
+ if ((ch === '&' || ch === '|') && command[i + 1] === ch) { op = ch + ch; i++; }
264
+ tokens.push({ text: op, quoted: false });
265
+ continue;
266
+ }
267
+
268
+ text += ch; started = true;
269
+ }
270
+
271
+ // Unterminated quote → flush whatever accumulated (mark quoted so the guard treats
272
+ // the dangling text conservatively).
273
+ if (state === 'single' || state === 'double') sawQuote = true;
274
+ flush();
275
+
276
+ return tokens;
277
+ }
278
+
279
+ /**
280
+ * Split a tokenized command into chained segments on shell control operators
281
+ * (`;`, `&&`, `||`, `|`, `&`). Only UNQUOTED single-token operators split; an
282
+ * operator that arrived inside quotes stays part of its segment.
283
+ *
284
+ * @param {Array<{ text: string, quoted: boolean }>} tokens
285
+ * @returns {Array<Array<{ text: string, quoted: boolean }>>}
286
+ */
287
+ function splitSegments(tokens) {
288
+ const segments = [];
289
+ let current = [];
290
+ const operators = new Set([';', '&&', '||', '|', '&']);
291
+ for (const tok of tokens) {
292
+ if (!tok.quoted && operators.has(tok.text)) {
293
+ segments.push(current);
294
+ current = [];
295
+ continue;
296
+ }
297
+ current.push(tok);
298
+ }
299
+ segments.push(current);
300
+ return segments.filter((s) => s.length > 0);
301
+ }
302
+
303
+ /**
304
+ * Resolve the effective argv[0] (the command verb) for a chain segment, skipping
305
+ * leading `VAR=value` env assignments and unwrapping `env …`/`command …` prefixes.
306
+ * Returns the bare program name (basename, no path) or null.
307
+ *
308
+ * @param {Array<{ text: string, quoted: boolean }>} segment
309
+ * @returns {string|null}
310
+ */
311
+ function segmentVerb(segment) {
312
+ let i = 0;
313
+ // Skip leading FOO=bar env assignments (unquoted).
314
+ while (i < segment.length && !segment[i].quoted && /^[A-Za-z_][A-Za-z0-9_]*=/.test(segment[i].text)) {
315
+ i++;
316
+ }
317
+ // Unwrap `env [VAR=val …]` and `command` prefixes that delegate to a real verb.
318
+ while (i < segment.length) {
319
+ const raw = segment[i].text;
320
+ const verb = raw.replace(/^.*\//, ''); // basename
321
+ if (verb === 'env') {
322
+ i++;
323
+ // env may carry its own VAR=val assignments before the real command
324
+ while (i < segment.length && !segment[i].quoted && /^[A-Za-z_][A-Za-z0-9_]*=/.test(segment[i].text)) {
325
+ i++;
326
+ }
327
+ continue;
328
+ }
329
+ if (verb === 'command') { i++; continue; }
330
+ break;
331
+ }
332
+ if (i >= segment.length) return null;
333
+ return segment[i].text.replace(/^.*\//, '');
334
+ }
335
+
336
+ /**
337
+ * Build the case-sensitive boundary regex for a blocked pattern. Boundary chars:
338
+ * whitespace + shell operators + quotes. Matches at start/end too.
339
+ *
340
+ * @param {string} pattern
341
+ * @returns {RegExp}
342
+ */
343
+ function boundaryRegex(pattern) {
344
+ const escaped = pattern.replace(/[.*+?|[\](){}\\^$]/g, '\\$&');
345
+ const boundary = '[\\s;|&(){}`\'"]';
346
+ return new RegExp(`(^|${boundary})${escaped}(${boundary}|$)`);
347
+ }
348
+
349
+ /**
350
+ * Test whether a blocked pattern occurs in the raw concatenation of a segment's
351
+ * QUOTED token payloads (with the boundary rule applied per token). Used to decide
352
+ * whether an interpreter segment carries the pattern inside its quoted argument.
353
+ *
354
+ * @param {Array<{ text: string, quoted: boolean }>} segment
355
+ * @param {RegExp} re
356
+ * @returns {boolean}
357
+ */
358
+ function quotedTokensMatch(segment, re) {
359
+ for (const tok of segment) {
360
+ if (tok.quoted && re.test(normalizeShellWhitespaceExpansions(tok.text, { expandSingleQuoted: true }))) {
361
+ return true;
362
+ }
363
+ }
364
+ return false;
365
+ }
366
+
367
+ /**
368
+ * Test whether a blocked pattern occurs OUTSIDE quoted tokens within a segment.
369
+ * Reconstructs the unquoted skeleton (quoted tokens replaced by a single space
370
+ * placeholder so they cannot bridge an adjacent-token match) and applies the
371
+ * boundary regex.
372
+ *
373
+ * @param {Array<{ text: string, quoted: boolean }>} segment
374
+ * @param {RegExp} re
375
+ * @returns {boolean}
376
+ */
377
+ function unquotedSegmentMatch(segment, re) {
378
+ const skeleton = segment.map((t) => (t.quoted ? ' ' : t.text)).join(' ');
379
+ return re.test(skeleton);
380
+ }
381
+
382
+ /**
383
+ * Test whether a command string contains a blocked pattern with shell-aware
384
+ * boundaries AND a quoted-payload guard (#641).
385
+ *
386
+ * Verb detection stays boundary-tolerant: a pattern that appears UNQUOTED — including
387
+ * across shell operators (`ls;rm -rf /`, `ls&&rm -rf /`, `(rm -rf /)`, piped into
388
+ * `xargs rm -rf`, prefixed by `FOO=1 …`) — still matches.
389
+ *
390
+ * Quoted-payload guard: a pattern whose ONLY occurrences are wholly inside quoted
391
+ * tokens is treated as inert literal text (no match) UNLESS the enclosing chain
392
+ * segment's verb (argv[0], after skipping env-assignments and unwrapping
393
+ * `env`/`command`) is a shell-exec interpreter (`bash -c "rm -rf /"`,
394
+ * `eval "…"`, `psql -c "DROP TABLE …"`, `find … -exec …`). The guard is applied
395
+ * PER chain segment: a quoted pattern in segment N is judged against segment N's verb.
396
+ *
397
+ * Boundary characters: whitespace, shell operators (`;`, `|`, `&`, `(`, `)`,
398
+ * `{`, `}`, backtick), or string quotes (`'`, `"`). Case-sensitive.
399
+ *
400
+ * @param {string} command — full command string
401
+ * @param {string} pattern — blocked pattern to search for
402
+ * @returns {boolean}
403
+ */
404
+ export function commandMatchesBlocked(command, pattern) {
405
+ if (!pattern) return false;
406
+ if (typeof command !== 'string' || command.length === 0) return false;
407
+
408
+ const re = boundaryRegex(pattern);
409
+ const normalizedCommand = normalizeShellWhitespaceExpansions(command);
410
+ const payloadNormalizedCommand = normalizeShellWhitespaceExpansions(command, {
411
+ expandSingleQuoted: true,
412
+ });
413
+
414
+ // Fast path: if the boundary regex does not match the raw string at all, no
415
+ // tokenization can produce a match. Shell whitespace expansions can create
416
+ // matches, so test the normalized forms rather than the original command.
417
+ if (!re.test(normalizedCommand) && !re.test(payloadNormalizedCommand)) return false;
418
+
419
+ const segments = splitSegments(tokenizeCommand(normalizedCommand));
420
+
421
+ for (const segment of segments) {
422
+ // 1) Unquoted occurrence anywhere in the segment → always a match.
423
+ if (unquotedSegmentMatch(segment, re)) return true;
424
+
425
+ // 2) Quoted occurrence → only a match when the segment verb is an interpreter
426
+ // that executes its quoted payload.
427
+ if (quotedTokensMatch(segment, re)) {
428
+ const verb = segmentVerb(segment);
429
+ if (verb && SHELL_EXEC_INTERPRETERS.has(verb)) return true;
430
+ // else: inert literal inside quotes for a non-interpreter verb → no match
431
+ // for THIS segment; keep scanning other segments.
432
+ }
433
+ }
434
+
435
+ return false;
436
+ }
437
+
438
+ /**
439
+ * Build an actionable suggestion string for a blocked command pattern.
440
+ *
441
+ * @param {string} pattern — the blocked command pattern
442
+ * @returns {string}
443
+ */
444
+ export function suggestForCommandBlock(pattern) {
445
+ switch (pattern) {
446
+ case 'rm -rf':
447
+ return 'Destructive deletion is blocked. Move specific files instead or use trash-cli.';
448
+ case 'git push --force':
449
+ case 'git push -f':
450
+ return "Force-push is blocked. Use 'git push --force-with-lease' after coordinator approval.";
451
+ case 'git reset --hard':
452
+ return "Hard reset is blocked. Use 'git reset --soft' or 'git stash' to preserve work.";
453
+ case 'git checkout -- .':
454
+ return 'Whole-tree discard is blocked. Target specific files instead.';
455
+ default:
456
+ return `Blocked command pattern '${pattern}' is not permitted during wave execution.`;
457
+ }
458
+ }