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,125 @@
1
+ /**
2
+ * reader.mjs — Read peer cards (USER.md / AGENT.md) from `.orchestrator/peers/`
3
+ * for issue #503 (Wave 2 I6).
4
+ *
5
+ * Consumers:
6
+ * - session-start Phase 4 staleness banner (Wave 3)
7
+ * - /evolve --dialectic (future #506 — out of scope here)
8
+ *
9
+ * Design notes
10
+ * ────────────
11
+ * • Reuses the canonical YAML-subset parser at `scripts/lib/state-md/yaml-parser.mjs`
12
+ * (`parseStateMd`). It returns `{ frontmatter, body }`, handles flow arrays
13
+ * (`[a, b, c]`), strings, booleans, integers, nulls — the same superset peer-cards
14
+ * need. "STATE.md" in the function name is incidental; the grammar is just
15
+ * "markdown with `---` YAML frontmatter".
16
+ * • Schema validation is delegated to `./schema.mjs` (I5). This module never
17
+ * decides what a valid peer card looks like; it just hands the parsed
18
+ * frontmatter to the validator and reports the verdict.
19
+ * • Read-only by contract. Never writes, never throws on missing files /
20
+ * malformed frontmatter — graceful degradation lets the banner caller
21
+ * decide UX (warn vs. silent).
22
+ * • Clock is injectable (`opts.now`) so staleness tests are deterministic.
23
+ */
24
+
25
+ import { readFile } from 'node:fs/promises';
26
+ import { existsSync } from 'node:fs';
27
+ import { join } from 'node:path';
28
+
29
+ import { parseStateMd } from '../state-md/yaml-parser.mjs';
30
+ import {
31
+ validatePeerCardFrontmatter,
32
+ computeStalenessDays,
33
+ STALENESS_THRESHOLD_DAYS,
34
+ } from './schema.mjs';
35
+
36
+ /**
37
+ * @typedef {Object} PeerCard
38
+ * @property {Record<string, unknown> | null} frontmatter — parsed YAML frontmatter, or null when missing/malformed
39
+ * @property {string} body — markdown body after the closing `---` (empty string if none)
40
+ * @property {number} stalenessDays — whole days since `frontmatter.updated`; `Infinity` if no `updated` field
41
+ * @property {boolean} isStale — `stalenessDays > STALENESS_THRESHOLD_DAYS`
42
+ * @property {{ ok: boolean, errors: string[] }} validation — schema verdict from `validatePeerCardFrontmatter`
43
+ */
44
+
45
+ /**
46
+ * @typedef {Object} PeerCardsResult
47
+ * @property {PeerCard | null} user — USER.md card; null if file missing
48
+ * @property {PeerCard | null} agent — AGENT.md card; null if file missing
49
+ * @property {string} peersDir — absolute path to `.orchestrator/peers/`
50
+ * @property {boolean} exists — whether peersDir exists on disk
51
+ */
52
+
53
+ /**
54
+ * Read peer cards from `<repoRoot>/.orchestrator/peers/`.
55
+ *
56
+ * @param {string} repoRoot — REQUIRED, absolute path to repo root
57
+ * @param {{ now?: Date }} [opts]
58
+ * @returns {Promise<PeerCardsResult>}
59
+ */
60
+ export async function readPeerCards(repoRoot, opts = {}) {
61
+ if (!repoRoot || typeof repoRoot !== 'string') {
62
+ throw new Error(`readPeerCards: repoRoot is required (got ${typeof repoRoot}).`);
63
+ }
64
+
65
+ const peersDir = join(repoRoot, '.orchestrator', 'peers');
66
+
67
+ if (!existsSync(peersDir)) {
68
+ return { user: null, agent: null, peersDir, exists: false };
69
+ }
70
+
71
+ const now = opts.now ?? new Date();
72
+ const userPath = join(peersDir, 'USER.md');
73
+ const agentPath = join(peersDir, 'AGENT.md');
74
+
75
+ const [user, agent] = await Promise.all([
76
+ readOneCard(userPath, now),
77
+ readOneCard(agentPath, now),
78
+ ]);
79
+
80
+ return { user, agent, peersDir, exists: true };
81
+ }
82
+
83
+ /**
84
+ * Read and parse a single peer-card file. Returns null if the file is missing.
85
+ * Never throws — degrades to a card with `validation.ok === false`.
86
+ *
87
+ * @param {string} absPath
88
+ * @param {Date} now
89
+ * @returns {Promise<PeerCard | null>}
90
+ */
91
+ async function readOneCard(absPath, now) {
92
+ if (!existsSync(absPath)) return null;
93
+
94
+ let content;
95
+ try {
96
+ content = await readFile(absPath, 'utf8');
97
+ } catch (err) {
98
+ return {
99
+ frontmatter: null,
100
+ body: '',
101
+ stalenessDays: Infinity,
102
+ isStale: true,
103
+ validation: { ok: false, errors: [`read failed: ${err.message}`] },
104
+ };
105
+ }
106
+
107
+ const parsed = parseStateMd(content);
108
+ if (parsed === null) {
109
+ return {
110
+ frontmatter: null,
111
+ body: content,
112
+ stalenessDays: Infinity,
113
+ isStale: true,
114
+ validation: { ok: false, errors: ['no frontmatter or malformed YAML'] },
115
+ };
116
+ }
117
+
118
+ const { frontmatter, body } = parsed;
119
+ const validation = validatePeerCardFrontmatter(frontmatter);
120
+ const updatedIso = typeof frontmatter.updated === 'string' ? frontmatter.updated : null;
121
+ const stalenessDays = updatedIso ? computeStalenessDays(updatedIso, now) : Infinity;
122
+ const isStale = stalenessDays > STALENESS_THRESHOLD_DAYS;
123
+
124
+ return { frontmatter, body, stalenessDays, isStale, validation };
125
+ }
@@ -0,0 +1,230 @@
1
+ /**
2
+ * scripts/lib/peer-cards/schema.mjs — Peer-card frontmatter validator (issue #503).
3
+ *
4
+ * Pure ESM, no I/O, no external dependencies. Used by reader.mjs / writer.mjs /
5
+ * merger.mjs (siblings under scripts/lib/peer-cards/) to validate peer-card
6
+ * YAML frontmatter on read and before write.
7
+ *
8
+ * Design notes:
9
+ * - Follows the `mission-status-schema.mjs` convention (pure regex/checks)
10
+ * rather than `skills/vault-sync/validator.mjs` (zod). The repo root has
11
+ * no `zod` dependency — only `skills/vault-sync/` does, scoped to that
12
+ * skill's own `node_modules`. Keeping this validator dependency-free lets
13
+ * callers from anywhere under `scripts/lib/` use it without extra setup.
14
+ * - The shape mirrors the canonical `vaultFrontmatterSchema` in
15
+ * `skills/vault-sync/validator.mjs` (slug regex, ISO date regex, optional
16
+ * title/tags) and extends it with the peer-card specific `target` enum and
17
+ * `source_sessions` array.
18
+ * - The `'peer-card'` discriminator is added to the vault-sync enum via the
19
+ * vendored-edit + sync-script-baseline path (see scripts/sync-vault-schema.mjs
20
+ * and the BEGIN/END GENERATED SCHEMA sentinel block in validator.mjs).
21
+ *
22
+ * Required frontmatter fields (per #503 AC1):
23
+ * id : kebab-case slug, 2..128 chars
24
+ * type : literal 'peer-card'
25
+ * target : 'user' | 'agent'
26
+ * created : ISO-8601 timestamp
27
+ * updated : ISO-8601 timestamp
28
+ * source_sessions : string[] (may be empty; defaults to [] when absent)
29
+ *
30
+ * Optional fields:
31
+ * title : 1..200 chars
32
+ * tags : string[]
33
+ *
34
+ * Unknown fields pass through (mirrors the .passthrough() behaviour of the
35
+ * canonical vault schema).
36
+ */
37
+
38
+ // ── Constants ───────────────────────────────────────────────────────────────
39
+
40
+ /** Canonical kebab-case slug regex (mirrors vault-sync slugRegex). */
41
+ const SLUG_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
42
+
43
+ /**
44
+ * Strict ISO 8601 datetime regex with UTC Z suffix, matching the AC1 example
45
+ * `2026-05-23T12:30:00Z`. Optional millisecond precision is accepted.
46
+ * Note: the canonical vault schema accepts date-only and offsets too; peer-cards
47
+ * deliberately require the full timestamp form for reproducible ordering.
48
+ */
49
+ const ISO_DATETIME_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?Z$/;
50
+
51
+ /** Permitted peer-card target values. */
52
+ export const PEER_CARD_TARGETS = Object.freeze(['user', 'agent']);
53
+
54
+ /** Staleness threshold in days — used by merger / discovery probes. */
55
+ export const STALENESS_THRESHOLD_DAYS = 30;
56
+
57
+ /** Inclusive bounds for the `id` field. */
58
+ const ID_MIN_LEN = 2;
59
+ const ID_MAX_LEN = 128;
60
+
61
+ /** Inclusive bounds for the optional `title` field. */
62
+ const TITLE_MIN_LEN = 1;
63
+ const TITLE_MAX_LEN = 200;
64
+
65
+ // ── Type guards ─────────────────────────────────────────────────────────────
66
+
67
+ function _isString(v) {
68
+ return typeof v === 'string';
69
+ }
70
+
71
+ function _isStringArray(v) {
72
+ return Array.isArray(v) && v.every(_isString);
73
+ }
74
+
75
+ // ── Public predicates ──────────────────────────────────────────────────────
76
+
77
+ /**
78
+ * Returns true when `value` is one of the canonical peer-card target strings.
79
+ *
80
+ * @param {unknown} value
81
+ * @returns {boolean}
82
+ */
83
+ function isValidPeerCardTarget(value) {
84
+ return typeof value === 'string' && PEER_CARD_TARGETS.includes(value);
85
+ }
86
+
87
+ /**
88
+ * Returns true when `value` is a valid kebab-case slug within the id length
89
+ * bounds (2..128).
90
+ *
91
+ * @param {unknown} value
92
+ * @returns {boolean}
93
+ */
94
+ function isValidPeerCardId(value) {
95
+ if (typeof value !== 'string') return false;
96
+ if (value.length < ID_MIN_LEN || value.length > ID_MAX_LEN) return false;
97
+ return SLUG_REGEX.test(value);
98
+ }
99
+
100
+ /**
101
+ * Returns true when `value` is a strict ISO 8601 datetime ending in `Z`.
102
+ *
103
+ * @param {unknown} value
104
+ * @returns {boolean}
105
+ */
106
+ function isValidIsoTimestamp(value) {
107
+ return typeof value === 'string' && ISO_DATETIME_REGEX.test(value);
108
+ }
109
+
110
+ // ── Validator ──────────────────────────────────────────────────────────────
111
+
112
+ /**
113
+ * Validate a parsed peer-card frontmatter object against the #503 AC1 shape.
114
+ *
115
+ * Returns a discriminated result object:
116
+ * - { ok: true, data: object } — input is valid; `data` is the input with
117
+ * `source_sessions` defaulted to `[]` when
118
+ * absent (mirrors AC1 default). Unknown
119
+ * fields are preserved (passthrough).
120
+ * - { ok: false, errors: string[] } — one entry per failed field, formatted
121
+ * as `"<path>: <message>"`.
122
+ *
123
+ * Never throws.
124
+ *
125
+ * @param {unknown} input - parsed YAML frontmatter object
126
+ * @returns {{ ok: true, data: object } | { ok: false, errors: string[] }}
127
+ */
128
+ export function validatePeerCardFrontmatter(input) {
129
+ const errors = [];
130
+
131
+ if (input === null || typeof input !== 'object' || Array.isArray(input)) {
132
+ return { ok: false, errors: ['<root>: frontmatter must be a plain object'] };
133
+ }
134
+
135
+ // ── id ──────────────────────────────────────────────────────────────────
136
+ if (!('id' in input)) {
137
+ errors.push('id: required field missing');
138
+ } else if (!isValidPeerCardId(input.id)) {
139
+ errors.push(
140
+ `id: must be a kebab-case slug of ${ID_MIN_LEN}..${ID_MAX_LEN} chars (matching ${SLUG_REGEX.source})`,
141
+ );
142
+ }
143
+
144
+ // ── type ────────────────────────────────────────────────────────────────
145
+ if (!('type' in input)) {
146
+ errors.push('type: required field missing');
147
+ } else if (input.type !== 'peer-card') {
148
+ errors.push(`type: must be literal "peer-card" (got ${JSON.stringify(input.type)})`);
149
+ }
150
+
151
+ // ── target ──────────────────────────────────────────────────────────────
152
+ if (!('target' in input)) {
153
+ errors.push('target: required field missing');
154
+ } else if (!isValidPeerCardTarget(input.target)) {
155
+ errors.push(
156
+ `target: must be one of ${JSON.stringify(PEER_CARD_TARGETS)} (got ${JSON.stringify(input.target)})`,
157
+ );
158
+ }
159
+
160
+ // ── created ─────────────────────────────────────────────────────────────
161
+ if (!('created' in input)) {
162
+ errors.push('created: required field missing');
163
+ } else if (!isValidIsoTimestamp(input.created)) {
164
+ errors.push('created: must be an ISO 8601 timestamp ending in Z (e.g. 2026-05-23T12:30:00Z)');
165
+ }
166
+
167
+ // ── updated ─────────────────────────────────────────────────────────────
168
+ if (!('updated' in input)) {
169
+ errors.push('updated: required field missing');
170
+ } else if (!isValidIsoTimestamp(input.updated)) {
171
+ errors.push('updated: must be an ISO 8601 timestamp ending in Z (e.g. 2026-05-23T12:30:00Z)');
172
+ }
173
+
174
+ // ── source_sessions (default: []) ───────────────────────────────────────
175
+ let sourceSessions;
176
+ if (!('source_sessions' in input) || input.source_sessions === undefined) {
177
+ sourceSessions = [];
178
+ } else if (!_isStringArray(input.source_sessions)) {
179
+ errors.push('source_sessions: must be an array of strings');
180
+ sourceSessions = undefined;
181
+ } else {
182
+ sourceSessions = input.source_sessions;
183
+ }
184
+
185
+ // ── Optional: title ────────────────────────────────────────────────────
186
+ if ('title' in input && input.title !== undefined) {
187
+ if (!_isString(input.title)) {
188
+ errors.push('title: must be a string when provided');
189
+ } else if (input.title.length < TITLE_MIN_LEN || input.title.length > TITLE_MAX_LEN) {
190
+ errors.push(`title: length must be ${TITLE_MIN_LEN}..${TITLE_MAX_LEN} chars`);
191
+ }
192
+ }
193
+
194
+ // ── Optional: tags ─────────────────────────────────────────────────────
195
+ if ('tags' in input && input.tags !== undefined) {
196
+ if (!_isStringArray(input.tags)) {
197
+ errors.push('tags: must be an array of strings when provided');
198
+ }
199
+ }
200
+
201
+ if (errors.length > 0) {
202
+ return { ok: false, errors };
203
+ }
204
+
205
+ // Spread input first so passthrough fields are preserved, then overwrite
206
+ // source_sessions with the defaulted value.
207
+ return { ok: true, data: { ...input, source_sessions: sourceSessions } };
208
+ }
209
+
210
+ // ── Staleness helpers ──────────────────────────────────────────────────────
211
+
212
+ /**
213
+ * Compute staleness in whole days between `updatedIso` and `now`.
214
+ *
215
+ * Returns `Infinity` when `updatedIso` cannot be parsed by `Date`. The result
216
+ * floors toward zero so a card updated 29 hours ago reads as `1` day stale.
217
+ *
218
+ * @param {string} updatedIso - ISO 8601 timestamp from peer-card frontmatter
219
+ * @param {Date} [now=new Date()] - injectable clock for tests
220
+ * @returns {number} whole days since `updatedIso`; `Infinity` on parse failure
221
+ */
222
+ export function computeStalenessDays(updatedIso, now = new Date()) {
223
+ if (typeof updatedIso !== 'string') return Infinity;
224
+ const updated = new Date(updatedIso);
225
+ const t = updated.getTime();
226
+ if (Number.isNaN(t)) return Infinity;
227
+ const nowMs = now instanceof Date ? now.getTime() : Number(now);
228
+ if (!Number.isFinite(nowMs)) return Infinity;
229
+ return Math.floor((nowMs - t) / 86_400_000);
230
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * staleness-banner.mjs — Session-start banner for #503 peer-card staleness.
3
+ *
4
+ * Surfaces a `warn` banner during session-start Phase 4 when peer cards
5
+ * (USER.md / AGENT.md under `.orchestrator/peers/`) have an `updated:`
6
+ * frontmatter older than `STALENESS_THRESHOLD_DAYS` (30) days.
7
+ *
8
+ * Design notes:
9
+ * - Mirrors the contract used by other Phase 4 banners
10
+ * (`scripts/lib/vault-staleness-banner.mjs`, `scripts/lib/ci-status-banner.mjs`,
11
+ * `scripts/lib/qg-command-drift-banner.mjs`): a single `checkXxx()` entry
12
+ * point that returns `null` (silent no-op) or `{ severity, message, ... }`.
13
+ * - Never throws. The banner is informational — the session-start renderer
14
+ * must not be derailed by reader errors, missing files, or schema mismatches.
15
+ * Defensive `try/catch` wraps every potentially-throwing call.
16
+ * - Clock is injectable (`opts.now`) to keep tests deterministic. Propagates
17
+ * through to `readPeerCards()` so staleness computations agree across the
18
+ * reader and the banner.
19
+ *
20
+ * Cross-references:
21
+ * - `scripts/lib/peer-cards/reader.mjs` — produces the `PeerCardsResult`
22
+ * consumed here.
23
+ * - `scripts/lib/peer-cards/schema.mjs` — `STALENESS_THRESHOLD_DAYS = 30`.
24
+ * - `.claude/rules/owner-persona.md` — host-wide `owner.yaml`; peer cards
25
+ * are the per-repo behavioural complement.
26
+ * - `skills/session-start/SKILL.md` Phase 4 — banner render site.
27
+ */
28
+
29
+ import { readPeerCards } from './reader.mjs';
30
+
31
+ /**
32
+ * Check peer-card staleness and produce a session-start banner.
33
+ *
34
+ * @param {object} opts
35
+ * @param {string} opts.repoRoot - REQUIRED absolute path to repo root
36
+ * @param {Date} [opts.now] - injectable clock for tests
37
+ * @returns {Promise<null | { severity: 'warn', message: string, stale: Array<{target: string, days: number}> }>}
38
+ */
39
+ export async function checkPeerCardsStaleness({ repoRoot, now } = {}) {
40
+ // Silent no-op on bad input (consistent with other Phase 4 banners).
41
+ if (!repoRoot || typeof repoRoot !== 'string') return null;
42
+
43
+ let result;
44
+ try {
45
+ result = await readPeerCards(repoRoot, { now });
46
+ } catch {
47
+ // Never throw — banner is informational only.
48
+ return null;
49
+ }
50
+
51
+ if (!result || !result.exists) return null;
52
+
53
+ const stale = [];
54
+
55
+ // Only count cards that are (a) present, (b) have valid frontmatter so the
56
+ // `updated:` date could be parsed, and (c) flagged stale by the reader.
57
+ // A card without parseable frontmatter still has `isStale: true` because
58
+ // `stalenessDays === Infinity`, but in that case the corrective action is
59
+ // "fix the frontmatter", not "run /evolve --dialectic" — so we exclude it
60
+ // from this banner.
61
+ if (
62
+ result.user &&
63
+ result.user.isStale &&
64
+ result.user.frontmatter &&
65
+ Number.isFinite(result.user.stalenessDays)
66
+ ) {
67
+ stale.push({ target: 'USER.md', days: result.user.stalenessDays });
68
+ }
69
+ if (
70
+ result.agent &&
71
+ result.agent.isStale &&
72
+ result.agent.frontmatter &&
73
+ Number.isFinite(result.agent.stalenessDays)
74
+ ) {
75
+ stale.push({ target: 'AGENT.md', days: result.agent.stalenessDays });
76
+ }
77
+
78
+ if (stale.length === 0) return null;
79
+
80
+ const parts = stale.map((s) => `${s.target} (${s.days}d)`).join(', ');
81
+ const message =
82
+ `⚠ peer-cards: ${parts} stale (>30 days) — ` +
83
+ `consider running /evolve --dialectic to refresh.`;
84
+
85
+ return { severity: 'warn', message, stale };
86
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * writer.mjs — Atomic writer for peer-card markdown files.
3
+ *
4
+ * Peer cards live at `.orchestrator/peers/{USER,AGENT}.md` and carry a frontmatter
5
+ * block followed by markdown body. Writes are crash-safe via tmp + rename (POSIX
6
+ * atomic same-filesystem rename), and the writer ENFORCES the EARS unwanted-behaviour
7
+ * rule from #503: a peer-card missing the required `id` field fails with
8
+ * `peer-card missing required field: id` and writes nothing.
9
+ *
10
+ * Full frontmatter validation is delegated to schema.mjs (`validatePeerCardFrontmatter`);
11
+ * if validation fails for any reason (missing fields, wrong types, etc.) the writer
12
+ * returns `{ ok: false, errors }` and leaves the target file untouched.
13
+ *
14
+ * Auto-fill semantics (only when caller omits the field):
15
+ * - type → 'peer-card'
16
+ * - target → from the function argument
17
+ * - updated → current ISO timestamp
18
+ * - created → mirrors `updated` if absent
19
+ * - source_sessions → []
20
+ * - `id` is NEVER auto-filled — callers must provide it (EARS gate).
21
+ *
22
+ * Part of #503 (Wave 2; sibling to schema.mjs / reader.mjs / merger.mjs).
23
+ */
24
+
25
+ import { writeFile, rename, mkdir, unlink } from 'node:fs/promises';
26
+ import { join } from 'node:path';
27
+ import { randomUUID } from 'node:crypto';
28
+
29
+ import { validatePeerCardFrontmatter } from './schema.mjs';
30
+
31
+ // Frontmatter keys serialized in this order. Anything else is appended.
32
+ const ORDERED_KEYS = ['id', 'type', 'target', 'created', 'updated', 'source_sessions', 'title', 'tags'];
33
+
34
+ // YAML special chars that force quoting of scalar string values.
35
+ const YAML_SPECIAL = /[:#&*!|>'"%@`,[\]{}]/;
36
+
37
+ /**
38
+ * Serialize a flat frontmatter object to a YAML `---` block.
39
+ *
40
+ * Peer-card frontmatter is intentionally flat (no nested objects per schema), so this
41
+ * helper handles scalars, string arrays, and string-keyed booleans/numbers. Strings
42
+ * containing YAML-special characters are quoted via JSON.stringify (safe superset of
43
+ * YAML double-quoted scalar).
44
+ *
45
+ * @param {Record<string, unknown>} fm
46
+ * @returns {string} YAML block including the opening and closing `---` markers.
47
+ */
48
+ function serializeFrontmatter(fm) {
49
+ const lines = ['---'];
50
+
51
+ const writeKey = (key, value) => {
52
+ if (Array.isArray(value)) {
53
+ lines.push(`${key}: [${value.map((v) => JSON.stringify(v)).join(', ')}]`);
54
+ } else if (typeof value === 'string') {
55
+ const needsQuoting = YAML_SPECIAL.test(value);
56
+ lines.push(`${key}: ${needsQuoting ? JSON.stringify(value) : value}`);
57
+ } else {
58
+ lines.push(`${key}: ${JSON.stringify(value)}`);
59
+ }
60
+ };
61
+
62
+ for (const key of ORDERED_KEYS) {
63
+ if (key in fm) writeKey(key, fm[key]);
64
+ }
65
+ for (const key of Object.keys(fm)) {
66
+ if (!ORDERED_KEYS.includes(key)) writeKey(key, fm[key]);
67
+ }
68
+
69
+ lines.push('---');
70
+ return lines.join('\n');
71
+ }
72
+
73
+ /**
74
+ * Atomically write a peer card to `.orchestrator/peers/{USER,AGENT}.md`.
75
+ *
76
+ * @param {string} repoRoot Absolute path to the repo root (required).
77
+ * @param {'user'|'agent'} target Which peer card to write.
78
+ * @param {{ frontmatter: object, body: string }} card Card content.
79
+ * @returns {Promise<{ ok: true, path: string } | { ok: false, errors: string[] }>}
80
+ * @throws {Error} On invalid input (bad repoRoot, bad target, bad card).
81
+ */
82
+ export async function writePeerCard(repoRoot, target, card) {
83
+ if (!repoRoot || typeof repoRoot !== 'string') {
84
+ throw new Error(`writePeerCard: repoRoot is required (got ${typeof repoRoot}).`);
85
+ }
86
+ if (target !== 'user' && target !== 'agent') {
87
+ throw new Error(`writePeerCard: target must be 'user' or 'agent' (got ${JSON.stringify(target)}).`);
88
+ }
89
+ if (!card || typeof card !== 'object') {
90
+ throw new Error('writePeerCard: card must be an object.');
91
+ }
92
+
93
+ // Build the effective frontmatter with auto-filled derived fields.
94
+ // Note: `id` is INTENTIONALLY not auto-filled — the EARS gate below depends on this.
95
+ const fm = { ...card.frontmatter };
96
+ fm.type = 'peer-card';
97
+ fm.target = target;
98
+ fm.updated = fm.updated || new Date().toISOString();
99
+ fm.created = fm.created || fm.updated;
100
+ fm.source_sessions = fm.source_sessions || [];
101
+
102
+ // EARS unwanted-behaviour gate (#503): refuse to write without id.
103
+ if (!fm.id) {
104
+ return { ok: false, errors: ['peer-card missing required field: id'] };
105
+ }
106
+
107
+ // Full schema validation — schema.mjs is the source of truth for valid shapes.
108
+ const validation = validatePeerCardFrontmatter(fm);
109
+ if (!validation.ok) {
110
+ return { ok: false, errors: validation.errors };
111
+ }
112
+
113
+ const filename = target === 'user' ? 'USER.md' : 'AGENT.md';
114
+ const peersDir = join(repoRoot, '.orchestrator', 'peers');
115
+ const finalPath = join(peersDir, filename);
116
+
117
+ await mkdir(peersDir, { recursive: true });
118
+
119
+ const bodyStr = typeof card.body === 'string' ? card.body : '';
120
+ const content = `${serializeFrontmatter(fm)}\n\n${bodyStr.trimStart()}`;
121
+ const tmpPath = `${finalPath}.${randomUUID().slice(0, 8)}.tmp`;
122
+
123
+ try {
124
+ await writeFile(tmpPath, content, 'utf8');
125
+ await rename(tmpPath, finalPath);
126
+ return { ok: true, path: finalPath };
127
+ } catch (err) {
128
+ // Best-effort cleanup of orphaned tmp file. Swallow secondary failures —
129
+ // the primary error is what the caller needs to see.
130
+ try {
131
+ await unlink(tmpPath);
132
+ } catch {
133
+ /* tmp file may not exist if writeFile failed before creating it */
134
+ }
135
+ throw err;
136
+ }
137
+ }
138
+