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,332 @@
1
+ /**
2
+ * instruction-budget-guard.mjs — #687
3
+ *
4
+ * Lightweight directive-budget guard for always-on `.claude/rules/*.md`.
5
+ *
6
+ * Sums the always-on directive count across the rule files that the
7
+ * rule-loader classifies as always-on (no `globs:` frontmatter, and no
8
+ * `paths:` alias either — issue #795: `paths:`-scoped rules are NOT
9
+ * always-on and must not inflate this count) and warns when the total
10
+ * exceeds a ceiling. "Mechanism over discipline" — the #668 instruction-
11
+ * budget audit recommends this as a silent-now growth ratchet that only
12
+ * fires when NEW always-on directives are added.
13
+ *
14
+ * Plain-JS — no Zod dependency. Never throws.
15
+ * - `computeInstructionBudget` always returns the full shape (never null).
16
+ * - `checkInstructionBudget` returns a banner object or null (session-start
17
+ * Phase 4 convention, mirroring checkQgCommandDrift / checkCiStatus).
18
+ *
19
+ * Always-on membership is delegated to `loadApplicableRules` from
20
+ * `./rule-loader.mjs` (single SSOT) — we do NOT hard-code the file list.
21
+ *
22
+ * Cross-references:
23
+ * - "2026-06-20 instruction-budget audit" (#668 / #687; archived in the private Meta-Vault)
24
+ * - scripts/lib/rule-loader.mjs (always-on classification SSOT)
25
+ * - scripts/lib/qg-command-drift-banner.mjs (banner-shape convention)
26
+ * - scripts/lib/ci-status-banner.mjs (never-throws convention)
27
+ */
28
+
29
+ import { existsSync, readFileSync } from 'node:fs';
30
+ import { basename, join } from 'node:path';
31
+ import { loadApplicableRules } from './rule-loader.mjs';
32
+
33
+ /** Default directive ceiling (operator-chosen growth ratchet just above the ~457 baseline). */
34
+ export const DEFAULT_CEILING = 480;
35
+
36
+ /**
37
+ * Read the `instruction-budget:` nested block from the `## Session Config`
38
+ * section of CLAUDE.md (or AGENTS.md) at `repoRoot`. Synchronous + never throws.
39
+ *
40
+ * The block lives inside `## Session Config`, e.g.:
41
+ *
42
+ * instruction-budget:
43
+ * enabled: true
44
+ * ceiling: 480
45
+ * mode: warn
46
+ *
47
+ * Behaviour:
48
+ * - Config-load failure (no instruction file / unreadable) → returns the
49
+ * graceful fallback `{ enabled: true, ceiling: DEFAULT_CEILING, mode: 'warn' }`
50
+ * so the probe still computes (mirrors the other session-start probes).
51
+ * - Absent block → same fallback (the feature is on-by-default, growth-ratchet).
52
+ * - Malformed individual values silently fall back to the per-key default.
53
+ *
54
+ * @param {string} repoRoot
55
+ * @returns {{ enabled: boolean, ceiling: number, mode: 'warn' | 'off' }}
56
+ */
57
+ export function loadInstructionBudgetConfig(repoRoot) {
58
+ const fallback = { enabled: true, ceiling: DEFAULT_CEILING, mode: 'warn' };
59
+
60
+ let content = null;
61
+ for (const name of ['CLAUDE.md', 'AGENTS.md']) {
62
+ const file = join(repoRoot ?? process.cwd(), name);
63
+ try {
64
+ if (existsSync(file)) {
65
+ content = readFileSync(file, 'utf8');
66
+ break;
67
+ }
68
+ } catch {
69
+ // unreadable — try the next candidate
70
+ }
71
+ }
72
+ if (typeof content !== 'string') return fallback;
73
+
74
+ try {
75
+ return _parseInstructionBudget(content, fallback);
76
+ } catch {
77
+ return fallback;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Parse the `instruction-budget:` block out of raw markdown content.
83
+ * Independent helper (testable without disk IO).
84
+ *
85
+ * @param {string} content - full file contents
86
+ * @param {{ enabled: boolean, ceiling: number, mode: 'warn' | 'off' }} [defaults]
87
+ * @returns {{ enabled: boolean, ceiling: number, mode: 'warn' | 'off' }}
88
+ */
89
+ export function _parseInstructionBudget(content, defaults) {
90
+ const base = defaults ?? { enabled: true, ceiling: DEFAULT_CEILING, mode: 'warn' };
91
+ if (typeof content !== 'string' || content === '') return { ...base };
92
+
93
+ const lines = content.split(/\r?\n/);
94
+ let inBlock = false;
95
+ let keyIndent = 0;
96
+ const blockLines = [];
97
+
98
+ for (const rawLine of lines) {
99
+ const line = rawLine.replace(/\r$/, '');
100
+ if (!inBlock) {
101
+ // The block key normally sits at column 0 inside `## Session Config`,
102
+ // but tolerate a leading indent (e.g. nested under another mapping).
103
+ const m = line.match(/^(\s*)instruction-budget:\s*$/);
104
+ if (m) {
105
+ inBlock = true;
106
+ keyIndent = m[1].length;
107
+ }
108
+ continue;
109
+ }
110
+ // Blank lines stay inside the block (mid-block spacing is tolerated).
111
+ if (line.trim() === '') {
112
+ blockLines.push(line);
113
+ continue;
114
+ }
115
+ // A child line must be indented STRICTLY DEEPER than the block key.
116
+ // Any line at or below the key's indent (incl. column 0) closes the block.
117
+ const indent = line.length - line.replace(/^\s+/, '').length;
118
+ if (indent <= keyIndent) break;
119
+ blockLines.push(line);
120
+ }
121
+
122
+ if (blockLines.length === 0) return { ...base };
123
+
124
+ let enabled = base.enabled;
125
+ let ceiling = base.ceiling;
126
+ let mode = base.mode;
127
+
128
+ for (const rawLine of blockLines) {
129
+ const clean = rawLine.replace(/\s*#.*$/, '').replace(/\s+$/, '');
130
+ if (!clean.trim()) continue;
131
+
132
+ const kvMatch = clean.match(/^\s+([a-zA-Z_-]+):\s*(.*)/);
133
+ if (!kvMatch) continue;
134
+
135
+ const k = kvMatch[1];
136
+ let v = kvMatch[2].trim();
137
+ if (v.startsWith('"') && v.endsWith('"') && v.length >= 2) v = v.slice(1, -1);
138
+ else if (v.startsWith("'") && v.endsWith("'") && v.length >= 2) v = v.slice(1, -1);
139
+
140
+ switch (k) {
141
+ case 'enabled':
142
+ // Default on → only flip to false on explicit "false".
143
+ enabled = v.toLowerCase() !== 'false';
144
+ break;
145
+ case 'ceiling': {
146
+ if (/^-?\d+$/.test(v)) {
147
+ const n = Number.parseInt(v, 10);
148
+ if (Number.isFinite(n) && n > 0) ceiling = n;
149
+ }
150
+ break;
151
+ }
152
+ case 'mode':
153
+ // Only `off` silences; any other value (incl. `warn`) surfaces the banner.
154
+ mode = v.toLowerCase() === 'off' ? 'off' : 'warn';
155
+ break;
156
+ }
157
+ }
158
+
159
+ return { enabled, ceiling, mode };
160
+ }
161
+
162
+ /**
163
+ * Count always-on directives in a single rule file's content.
164
+ *
165
+ * Deterministic heuristic — counts lines that represent a directive:
166
+ * - bullets: /^\s*[-*+]\s/
167
+ * - ordered items: /^\s*\d+[.)]\s/
168
+ * - headings ≥2: /^#{2,}\s/
169
+ *
170
+ * Fenced code blocks (``` … ```) are excluded entirely, and a leading
171
+ * `---` … `---` YAML frontmatter block is skipped before counting.
172
+ *
173
+ * @param {string} content - raw file contents
174
+ * @returns {number}
175
+ */
176
+ function countDirectives(content) {
177
+ if (typeof content !== 'string' || content === '') return 0;
178
+
179
+ const lines = content.split(/\r?\n/);
180
+ let i = 0;
181
+
182
+ // Skip a leading YAML frontmatter block: `---` … `---`.
183
+ if (lines[0] !== undefined && lines[0].trim() === '---') {
184
+ let j = 1;
185
+ while (j < lines.length && lines[j].trim() !== '---') j++;
186
+ // Only skip if a closing fence was found; otherwise leave i at 0.
187
+ if (j < lines.length) i = j + 1;
188
+ }
189
+
190
+ let count = 0;
191
+ let inFence = false;
192
+
193
+ for (; i < lines.length; i++) {
194
+ const line = lines[i];
195
+
196
+ // Toggle code-fence state on any line that opens/closes a fence.
197
+ if (/^\s*```/.test(line)) {
198
+ inFence = !inFence;
199
+ continue;
200
+ }
201
+ if (inFence) continue;
202
+
203
+ if (
204
+ /^\s*[-*+]\s/.test(line) ||
205
+ /^\s*\d+[.)]\s/.test(line) ||
206
+ /^#{2,}\s/.test(line)
207
+ ) {
208
+ count++;
209
+ }
210
+ }
211
+
212
+ return count;
213
+ }
214
+
215
+ /**
216
+ * Pure computation — always returns the full shape (never null, never throws).
217
+ *
218
+ * @param {object} [opts]
219
+ * @param {string} [opts.repoRoot] project root (defaults to process.cwd()).
220
+ * @param {string} [opts.rulesDir] rules directory (defaults to <repoRoot>/.claude/rules).
221
+ * @param {number} [opts.ceiling] directive ceiling (defaults to DEFAULT_CEILING).
222
+ * @returns {{
223
+ * totalDirectives: number,
224
+ * perFile: Array<{ file: string, count: number }>,
225
+ * ceiling: number,
226
+ * overBudget: boolean,
227
+ * severity: 'ok' | 'warn',
228
+ * }}
229
+ * perFile is sorted DESC by count. On missing/unreadable dir →
230
+ * { totalDirectives: 0, perFile: [], ceiling, overBudget: false, severity: 'ok' }.
231
+ */
232
+ export function computeInstructionBudget(opts = {}) {
233
+ const repoRoot = opts.repoRoot ?? process.cwd();
234
+ const rulesDir = opts.rulesDir ?? join(repoRoot, '.claude/rules');
235
+ const ceiling = typeof opts.ceiling === 'number' ? opts.ceiling : DEFAULT_CEILING;
236
+
237
+ const empty = {
238
+ totalDirectives: 0,
239
+ perFile: [],
240
+ ceiling,
241
+ overBudget: false,
242
+ severity: 'ok',
243
+ };
244
+
245
+ let entries;
246
+ try {
247
+ // Empty scopePaths → only always-on rules (no glob matches) are returned.
248
+ entries = loadApplicableRules({ rulesDir, scopePaths: [] });
249
+ } catch {
250
+ return empty;
251
+ }
252
+
253
+ if (!Array.isArray(entries)) return empty;
254
+
255
+ const perFile = [];
256
+ let totalDirectives = 0;
257
+
258
+ for (const entry of entries) {
259
+ if (!entry || entry.alwaysOn !== true) continue;
260
+ const count = countDirectives(entry.content);
261
+ totalDirectives += count;
262
+ perFile.push({ file: basename(entry.path), count });
263
+ }
264
+
265
+ // Sort DESC by count; tie-break by filename for deterministic output.
266
+ perFile.sort((a, b) => b.count - a.count || a.file.localeCompare(b.file));
267
+
268
+ const overBudget = totalDirectives > ceiling;
269
+
270
+ return {
271
+ totalDirectives,
272
+ perFile,
273
+ ceiling,
274
+ overBudget,
275
+ severity: overBudget ? 'warn' : 'ok',
276
+ };
277
+ }
278
+
279
+ /**
280
+ * Banner wrapper — session-start Phase 4 convention.
281
+ *
282
+ * Reads `instruction-budget.{enabled,ceiling,mode}` from Session Config
283
+ * (CLAUDE.md / AGENTS.md at `opts.repoRoot`, default process.cwd()):
284
+ * - `enabled: false` OR `mode: off` → returns null (silent no-op).
285
+ * - The config `ceiling` is used unless `opts.ceiling` is explicitly supplied
286
+ * (an explicit opt wins, keeping callers that pin a ceiling deterministic).
287
+ * - Config-load failure → graceful fallback `{enabled:true, ceiling:480,
288
+ * mode:warn}` so the probe still computes (mirrors the other probes).
289
+ * Never throws.
290
+ *
291
+ * @param {object} [opts] forwarded to computeInstructionBudget.
292
+ * @param {string} [opts.repoRoot] project root for the config read.
293
+ * @param {number} [opts.ceiling] explicit ceiling override (wins over config).
294
+ * @returns {{ severity: 'warn', message: string } | null}
295
+ * null when disabled / off / at-or-under ceiling OR on any read failure.
296
+ */
297
+ export function checkInstructionBudget(opts = {}) {
298
+ let cfg;
299
+ try {
300
+ cfg = loadInstructionBudgetConfig(opts.repoRoot);
301
+ } catch {
302
+ cfg = { enabled: true, ceiling: DEFAULT_CEILING, mode: 'warn' };
303
+ }
304
+
305
+ // Opt-out gates — return null without computing.
306
+ if (!cfg.enabled || cfg.mode === 'off') return null;
307
+
308
+ // An explicit ceiling opt wins over the config ceiling; otherwise use config.
309
+ const ceiling = typeof opts.ceiling === 'number' ? opts.ceiling : cfg.ceiling;
310
+
311
+ let budget;
312
+ try {
313
+ budget = computeInstructionBudget({ ...opts, ceiling });
314
+ } catch {
315
+ return null; // never throw out of the banner wrapper
316
+ }
317
+
318
+ if (!budget || !budget.overBudget) return null;
319
+
320
+ const top = budget.perFile
321
+ .slice(0, 3)
322
+ .map((f) => `${f.file} (${f.count})`)
323
+ .join(', ');
324
+
325
+ const message = [
326
+ `⚠ Instruction budget: ${budget.totalDirectives} always-on directives across ${budget.perFile.length} rules — over ceiling ${budget.ceiling}.`,
327
+ ` Top files: ${top}`,
328
+ ' See the instruction-budget audit (#687; archived in the private Meta-Vault) for the prune/demote list.',
329
+ ].join('\n');
330
+
331
+ return { severity: 'warn', message };
332
+ }
@@ -0,0 +1,304 @@
1
+ /**
2
+ * io.mjs — stdin/stdout helpers implementing the Claude Code hook I/O contract,
3
+ * plus shared atomic file-write helpers used by skills that need crash-safe
4
+ * sidecar JSON output (e.g. persona-panel, issue #457).
5
+ *
6
+ * Provides promise-based stdin JSON reading and structured exit helpers used by
7
+ * PreToolUse / PostToolUse hooks in the session-orchestrator v3 migration, and
8
+ * `writeJsonAtomic` (async) + `writeJsonAtomicSync` (sync) for tmp+rename JSON
9
+ * writes. The async variant supports optional pre-write validation; the sync
10
+ * variant is hot-path-friendly for hooks and session-lock writers that cannot
11
+ * await.
12
+ *
13
+ * No external dependencies — Node 20+ stdlib only.
14
+ *
15
+ * Part of v3.0.0 migration (Epic #124, issue #131); writeJsonAtomic added for
16
+ * #457; writeJsonAtomicSync extracted for #558 M1.
17
+ */
18
+
19
+ import { writeFile, rename, mkdir } from 'node:fs/promises';
20
+ import { mkdirSync, writeFileSync, renameSync, readFileSync, existsSync } from 'node:fs';
21
+ import path, { dirname } from 'node:path';
22
+ import { randomBytes, randomUUID } from 'node:crypto';
23
+
24
+ // ---------------------------------------------------------------------------
25
+ // Internal helpers
26
+ // ---------------------------------------------------------------------------
27
+
28
+ /**
29
+ * Build the deny reason string, optionally appending a suggestion.
30
+ * @param {string} reason
31
+ * @param {string|undefined} suggestion
32
+ * @returns {string}
33
+ */
34
+ function _formatReason(reason, suggestion) {
35
+ return suggestion ? `${reason} — ${suggestion}` : reason;
36
+ }
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // Exports
40
+ // ---------------------------------------------------------------------------
41
+
42
+ /**
43
+ * Read process.stdin to EOF and parse as JSON.
44
+ * @returns {Promise<object|null>} Parsed JSON object, or null on empty stream.
45
+ * @throws {SyntaxError} If stdin contains non-empty, non-JSON data.
46
+ * @throws {Error} If the 1 MB size limit or 5 s timeout is exceeded.
47
+ */
48
+ export async function readStdin() {
49
+ const MAX_BYTES = 1_048_576; // 1 MB guard
50
+ const TIMEOUT_MS = 5_000; // 5 s guard
51
+
52
+ return new Promise((resolve, reject) => {
53
+ const controller = new AbortController();
54
+ const timer = setTimeout(() => {
55
+ controller.abort();
56
+ reject(new Error('io.mjs: readStdin timed out after 5 s'));
57
+ }, TIMEOUT_MS);
58
+
59
+ const chunks = [];
60
+ let totalBytes = 0;
61
+
62
+ // If stdin is already closed (e.g. not a TTY with no data), handle cleanly.
63
+ if (process.stdin.readableEnded) {
64
+ clearTimeout(timer);
65
+ resolve(null);
66
+ return;
67
+ }
68
+
69
+ process.stdin.setEncoding('utf8');
70
+
71
+ const onData = (chunk) => {
72
+ if (controller.signal.aborted) return;
73
+ totalBytes += Buffer.byteLength(chunk, 'utf8');
74
+ if (totalBytes > MAX_BYTES) {
75
+ cleanup();
76
+ reject(new Error(`io.mjs: stdin payload exceeds 1 MB limit (${totalBytes} bytes read)`));
77
+ return;
78
+ }
79
+ chunks.push(chunk);
80
+ };
81
+
82
+ const onEnd = () => {
83
+ if (controller.signal.aborted) return;
84
+ cleanup();
85
+ const raw = chunks.join('').trim();
86
+ if (raw === '') {
87
+ resolve(null);
88
+ return;
89
+ }
90
+ try {
91
+ resolve(JSON.parse(raw));
92
+ } catch {
93
+ reject(new SyntaxError(`io.mjs: stdin is not valid JSON — got: ${raw.slice(0, 120)}`));
94
+ }
95
+ };
96
+
97
+ const onError = (err) => {
98
+ cleanup();
99
+ reject(err);
100
+ };
101
+
102
+ const cleanup = () => {
103
+ clearTimeout(timer);
104
+ process.stdin.off('data', onData);
105
+ process.stdin.off('end', onEnd);
106
+ process.stdin.off('error', onError);
107
+ };
108
+
109
+ process.stdin.on('data', onData);
110
+ process.stdin.on('end', onEnd);
111
+ process.stdin.on('error', onError);
112
+
113
+ // Resume in case the stream is paused (e.g. in flowing mode was never started).
114
+ process.stdin.resume();
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Allow the current hook invocation — exits 0 silently.
120
+ * @returns {never}
121
+ */
122
+ export function emitAllow() {
123
+ process.exit(0);
124
+ }
125
+
126
+ /**
127
+ * Deny the current hook invocation with a structured JSON reason on stdout, then exit 2.
128
+ * @param {string} reason Non-empty human-readable denial reason.
129
+ * @param {string} [suggestion] Optional remediation hint appended after " — ".
130
+ * @returns {never}
131
+ */
132
+ export function emitDeny(reason, suggestion) {
133
+ if (!reason) throw new TypeError('io.mjs: emitDeny requires a non-empty reason string');
134
+ console.log(JSON.stringify({ permissionDecision: 'deny', reason: _formatReason(reason, suggestion) }));
135
+ process.exit(2);
136
+ }
137
+
138
+ /**
139
+ * Emit a warning message to stderr and exit 0 (allow with notice).
140
+ * @param {string} message Warning text written to stderr prefixed with "⚠ ".
141
+ * @returns {never}
142
+ */
143
+ export function emitWarn(message) {
144
+ console.error(`⚠ ${message}`);
145
+ process.exit(0);
146
+ }
147
+
148
+ /**
149
+ * Inject a system message into the hook response without exiting.
150
+ * @param {string} msg Message text wrapped in a systemMessage JSON envelope on stdout.
151
+ */
152
+ export function emitSystemMessage(msg) {
153
+ console.log(JSON.stringify({ systemMessage: msg }));
154
+ }
155
+
156
+ /**
157
+ * Atomically write a JSON value to filePath. Creates parent directories as needed.
158
+ *
159
+ * Crash-safe pattern: write to `<filePath>.<rand>.tmp`, then `rename()` over the
160
+ * target. Same-filesystem rename is atomic on POSIX, so partial-write states are
161
+ * impossible — observers see either the previous contents or the new ones, never
162
+ * a half-written file.
163
+ *
164
+ * When `validatorFn` is provided, the value is validated BEFORE any disk write.
165
+ * Validation failure throws an Error with `.validationErrors` attached and leaves
166
+ * the target file untouched.
167
+ *
168
+ * Caller is responsible for path-confinement (see scripts/lib/path-utils.mjs#validatePathInsideProject)
169
+ * — this helper does NOT validate that filePath lives inside the project.
170
+ *
171
+ * Added for persona-panel sidecar writes (issue #457).
172
+ *
173
+ * @param {string} filePath Target path; parent dirs created with mkdir -p semantics.
174
+ * @param {*} value JSON-serializable value.
175
+ * @param {object} [opts]
176
+ * @param {(value: *) => {ok: boolean, errors?: Array<object>}} [opts.validatorFn]
177
+ * Pre-write validator. Throws (without writing) on `ok=false`.
178
+ * @param {number} [opts.indent=2] JSON.stringify indent.
179
+ * @returns {Promise<{ path: string, bytes: number }>}
180
+ * @throws {Error} If validatorFn rejects the value (with `.validationErrors` array attached).
181
+ */
182
+ export async function writeJsonAtomic(filePath, value, opts = {}) {
183
+ const { validatorFn, indent = 2 } = opts;
184
+
185
+ if (typeof validatorFn === 'function') {
186
+ const result = validatorFn(value);
187
+ if (!result || result.ok !== true) {
188
+ const err = new Error('writeJsonAtomic: validation failed before write');
189
+ err.validationErrors = (result && result.errors) || [];
190
+ throw err;
191
+ }
192
+ }
193
+
194
+ await mkdir(dirname(filePath), { recursive: true });
195
+
196
+ const tmp = `${filePath}.${randomUUID().slice(0, 8)}.tmp`;
197
+ const content = JSON.stringify(value, null, indent);
198
+ await writeFile(tmp, content, 'utf8');
199
+ await rename(tmp, filePath);
200
+
201
+ return { path: filePath, bytes: Buffer.byteLength(content, 'utf8') };
202
+ }
203
+
204
+ /**
205
+ * Atomically replace a file via tmp + renameSync. Synchronous companion to
206
+ * {@link writeJsonAtomic} for hot-path code that cannot await — session-lock
207
+ * acquire/replace, staging-fence intent logs, and other crash-safe sidecar
208
+ * writes invoked from PreToolUse hooks where async would add a microtask hop.
209
+ *
210
+ * Crash-safe pattern: write to `<dir>/<tmpPrefix>.<rand>` then renameSync over
211
+ * the target. Same-filesystem rename is atomic on POSIX, so partial-write
212
+ * states are impossible — observers see either the previous contents or the
213
+ * new ones, never a half-written file.
214
+ *
215
+ * Caller is responsible for path-confinement — this helper does NOT validate
216
+ * that filePath lives inside the project (mirrors the async {@link writeJsonAtomic}
217
+ * contract).
218
+ *
219
+ * Hook-safety: io.mjs MUST NOT reverse-import from `hooks/` — this helper is a
220
+ * pure Node-stdlib utility (`node:fs`, `node:path`, `node:crypto`) so it can be
221
+ * used from any layer without violating the layering rule in
222
+ * `scripts/lib/hardening.mjs`.
223
+ *
224
+ * @param {string} filePath Target path; parent dirs created with mkdir -p semantics.
225
+ * @param {*} data JSON-serializable value.
226
+ * @param {object} [opts]
227
+ * @param {number} [opts.indent=2] JSON.stringify indent.
228
+ * @param {string} [opts.tmpPrefix='.tmp'] Tmp-file prefix (callers pick their domain prefix).
229
+ * @returns {{ ok: true } | { ok: false, reason: 'fs-error', error: string }}
230
+ */
231
+ export function writeJsonAtomicSync(filePath, data, opts = {}) {
232
+ const { indent = 2, tmpPrefix = '.tmp' } = opts;
233
+ try {
234
+ const dir = dirname(filePath);
235
+ mkdirSync(dir, { recursive: true });
236
+ const tmpSuffix = randomBytes(6).toString('hex');
237
+ const tmpFile = path.join(dir, `${tmpPrefix}.${tmpSuffix}`);
238
+ writeFileSync(tmpFile, JSON.stringify(data, null, indent) + '\n', { encoding: 'utf8' });
239
+ renameSync(tmpFile, filePath);
240
+ return { ok: true };
241
+ } catch (err) {
242
+ return { ok: false, reason: 'fs-error', error: err?.message ?? String(err) };
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Parse newline-delimited JSON (JSONL) into an array of parsed objects.
248
+ *
249
+ * Splits `raw` on '\n', drops blank / whitespace-only lines, and JSON.parses
250
+ * each remaining line. This is the shared, tested replacement for the inline
251
+ * `raw.split('\n').filter(l => l.trim().length > 0).map(JSON.parse)` idiom that
252
+ * is re-implemented across ~30 metrics / event readers.
253
+ *
254
+ * Empty or whitespace-only input returns `[]` and never throws.
255
+ *
256
+ * @param {string} raw Raw JSONL text.
257
+ * @param {object} [opts]
258
+ * @param {boolean} [opts.skipInvalid=false] When `true`, silently skip lines
259
+ * that fail JSON.parse. When `false` (default), throw an Error naming the
260
+ * 1-based source line number plus a short snippet of the offending line,
261
+ * so callers get actionable diagnostics.
262
+ * @returns {object[]} Parsed objects, in source order.
263
+ * @throws {Error} When `skipInvalid` is false and a non-blank line is not valid JSON.
264
+ */
265
+ export function readJsonlLines(raw, opts = {}) {
266
+ const { skipInvalid = false } = opts;
267
+ if (typeof raw !== 'string' || raw.trim().length === 0) return [];
268
+
269
+ const out = [];
270
+ const lines = raw.split('\n');
271
+ for (let i = 0; i < lines.length; i++) {
272
+ const line = lines[i];
273
+ if (line.trim().length === 0) continue;
274
+ try {
275
+ out.push(JSON.parse(line));
276
+ } catch (err) {
277
+ if (skipInvalid) continue;
278
+ const snippet = line.length > 80 ? `${line.slice(0, 80)}…` : line;
279
+ throw new Error(
280
+ `io.mjs: readJsonlLines failed to parse JSON on line ${i + 1}: ${snippet} (${err?.message ?? String(err)})`,
281
+ { cause: err },
282
+ );
283
+ }
284
+ }
285
+ return out;
286
+ }
287
+
288
+ /**
289
+ * Read a UTF-8 JSONL file and parse it via {@link readJsonlLines}.
290
+ *
291
+ * A missing file returns `[]` rather than throwing — JSONL metrics / events
292
+ * sidecars (e.g. `.orchestrator/metrics/*.jsonl`) are routinely absent on first
293
+ * run, and callers should treat "no file" identically to "empty file".
294
+ *
295
+ * @param {string} filePath Path to a UTF-8 JSONL file.
296
+ * @param {object} [opts] Forwarded to {@link readJsonlLines} (e.g. `{ skipInvalid: true }`).
297
+ * @returns {object[]} Parsed objects, or `[]` when the file does not exist.
298
+ * @throws {Error} When the file exists but a non-blank line is invalid and `skipInvalid` is false.
299
+ */
300
+ export function readJsonlFile(filePath, opts = {}) {
301
+ if (!existsSync(filePath)) return [];
302
+ const raw = readFileSync(filePath, 'utf8');
303
+ return readJsonlLines(raw, opts);
304
+ }