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,130 @@
1
+ /**
2
+ * template.mjs — Canonical .vault.yaml template renderer for vault-backfill.
3
+ *
4
+ * Reads the template once from projects-baseline; subsequent calls use cache.
5
+ * Part of scripts/vault-backfill.mjs (Issue #241).
6
+ */
7
+
8
+ import { readFileSync, existsSync } from 'node:fs';
9
+ import { homedir } from 'node:os';
10
+ import { resolve } from 'node:path';
11
+
12
+ export const TEMPLATE_PATH = process.env.PROJECTS_BASELINE_DIR
13
+ ? resolve(process.env.PROJECTS_BASELINE_DIR, 'templates/shared/.vault.yaml.template')
14
+ : resolve(homedir(), 'Projects/projects-baseline/templates/shared/.vault.yaml.template');
15
+
16
+ const TODAY = new Date().toISOString().slice(0, 10);
17
+
18
+ let _templateContent = null;
19
+
20
+ /**
21
+ * Load and cache the canonical template. Calls dieFn(2, ...) if missing.
22
+ */
23
+ export function loadTemplate(dieFn) {
24
+ if (_templateContent !== null) return _templateContent;
25
+
26
+ if (!existsSync(TEMPLATE_PATH)) {
27
+ dieFn(
28
+ 2,
29
+ `canonical template not found at ${TEMPLATE_PATH} — ` +
30
+ `set PROJECTS_BASELINE_DIR env var or check projects-baseline checkout at $HOME/Projects/projects-baseline`,
31
+ );
32
+ }
33
+
34
+ try {
35
+ _templateContent = readFileSync(TEMPLATE_PATH, 'utf8');
36
+ } catch (err) {
37
+ dieFn(2, `cannot read template at ${TEMPLATE_PATH}: ${err.message}`);
38
+ }
39
+
40
+ return _templateContent;
41
+ }
42
+
43
+ /**
44
+ * Convert a kebab-case slug to a human-readable Title Case name.
45
+ * e.g. "auth-service" → "Auth Service"
46
+ */
47
+ export function slugToHumanName(slug) {
48
+ return slug
49
+ .split('-')
50
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
51
+ .join(' ');
52
+ }
53
+
54
+ /**
55
+ * Derive a kebab-case slug from a repo path (last segment).
56
+ */
57
+ export function pathToSlug(repoPath) {
58
+ const last = repoPath.split('/').filter(Boolean).pop() ?? repoPath;
59
+ return last
60
+ .toLowerCase()
61
+ .replace(/[._]/g, '-')
62
+ .replace(/[^a-z0-9-]/g, '')
63
+ .replace(/-+/g, '-')
64
+ .replace(/^-+|-+$/g, '');
65
+ }
66
+
67
+ /**
68
+ * Produce a safe YAML 1.2 scalar for a user-supplied string value.
69
+ *
70
+ * Uses JSON.stringify (YAML 1.2 is a superset of JSON) which:
71
+ * - wraps the value in double quotes
72
+ * - escapes newlines (\n), carriage returns (\r), tabs (\t), backslashes, and double quotes
73
+ *
74
+ * This prevents YAML injection via embedded newlines or special characters
75
+ * in externally-sourced fields such as `owner` (GitLab API) and `gitlabPath`
76
+ * (manifest file). CWE-1336 / Issue #247.
77
+ *
78
+ * @param {string} value
79
+ * @returns {string} — a JSON-encoded double-quoted YAML scalar, e.g. "alice"
80
+ */
81
+ export function yamlScalar(value) {
82
+ return JSON.stringify(String(value));
83
+ }
84
+
85
+ /**
86
+ * Render .vault.yaml content from the canonical template.
87
+ *
88
+ * Substitutions:
89
+ * {{PROJECT_NAME}} → humanName (metadata.name, summary placeholder)
90
+ * metadata.slug → slug (overwrite the {{PROJECT_NAME}} echo on slug field)
91
+ * tier → manifest tier
92
+ * owner → fetched owner [sanitised via yamlScalar]
93
+ * {{GITLAB_GROUP}}/{{PROJECT_NAME}} → full gitlab path_with_namespace [sanitised via yamlScalar]
94
+ *
95
+ * @param {{ humanName: string, slug: string, tier: string, gitlabPath: string, owner: string }} opts
96
+ * @param {string} templateContent - loaded template string
97
+ * @returns {string}
98
+ */
99
+ export function renderTemplate({ humanName, slug, tier, gitlabPath, owner }, templateContent) {
100
+ let out =
101
+ `# Generated by scripts/vault-backfill.mjs on ${TODAY} — review and update before committing.\n` +
102
+ templateContent;
103
+
104
+ // {{PROJECT_NAME}} → humanName (appears in metadata.name, summary)
105
+ // humanName is derived from a manifest-validated slug ([a-z0-9-]) so it only
106
+ // contains word characters and spaces — no YAML-special characters.
107
+ out = out.replace(/\{\{PROJECT_NAME\}\}/g, humanName);
108
+
109
+ // metadata.slug line: after PROJECT_NAME substitution it reads slug: "humanName" — fix to actual slug
110
+ // slug is validated to /^[a-z0-9]+(?:-[a-z0-9]+)*$/ by validateManifest — safe to embed bare.
111
+ out = out.replace(/^(\s*slug:\s*)"[^"]*"(.*)$/m, `$1"${slug}"$2`);
112
+
113
+ // tier: active → tier: <manifest tier>
114
+ // tier is a closed enum (top|active|archived) — safe to embed bare.
115
+ out = out.replace(/^(\s*tier:\s*)active(.*)$/m, `$1${tier}$2`);
116
+
117
+ // owner field — value comes from GitLab API (externally controlled).
118
+ // Use yamlScalar() to produce a quoted scalar, preventing newline injection (CWE-1336, #247).
119
+ // The template has a bare unquoted owner value (e.g. `owner: bernhard`);
120
+ // the regex replaces only the bare value token, preserving any trailing comment.
121
+ out = out.replace(/^(\s*owner:\s*)\S+(.*)$/m, `$1${yamlScalar(owner)}$2`);
122
+
123
+ // spec.links.gitlab — template uses {{GITLAB_GROUP}}/{{PROJECT_NAME}};
124
+ // after PROJECT_NAME substitution the token reads {{GITLAB_GROUP}}/humanName, still inside "...".
125
+ // gitlabPath comes from the manifest `path` field which is not validated for YAML-special chars.
126
+ // Replace the whole "..." quoted token with yamlScalar(gitlabPath) to prevent injection (#247).
127
+ out = out.replace(/"?\{\{GITLAB_GROUP\}\}\/[^\n"]*"?/g, yamlScalar(gitlabPath));
128
+
129
+ return out;
130
+ }
@@ -0,0 +1,331 @@
1
+ /**
2
+ * vault-consolidate-fs.mjs — filesystem + classification helpers for
3
+ * scripts/vault-consolidate.mjs (Issue #514 / #607).
4
+ *
5
+ * Extracted from the previously-monolithic vault-consolidate.mjs so the
6
+ * walk / hash / classify / backup logic is unit-importable in isolation. The
7
+ * script itself (vault-consolidate.mjs) wires these helpers behind an
8
+ * `import.meta.url === pathToFileURL(process.argv[1])` entry-guard so importing
9
+ * the script no longer executes the migration as a top-level side effect.
10
+ *
11
+ * Every function here is behaviour-identical to its former in-script form;
12
+ * the only change is that previously-closed-over module constants/state
13
+ * (SCRIPT_NAME, BACKUP_PREFIX, DECISIONS_SIDECAR_REL, canonicalRoot) are now
14
+ * exported constants / explicit parameters so the helpers are pure with
15
+ * respect to their inputs.
16
+ */
17
+
18
+ import { promises as fs } from 'node:fs';
19
+ import { isUtf8 } from 'node:buffer';
20
+ import path from 'node:path';
21
+ import crypto from 'node:crypto';
22
+ import { spawnSync } from 'node:child_process';
23
+
24
+ export const SCRIPT_NAME = 'vault-consolidate';
25
+ export const BACKUP_PREFIX = '.vault-backup-';
26
+ export const DECISIONS_SIDECAR_REL = '.vault-consolidate-decisions.json';
27
+
28
+ /**
29
+ * Recursively walk `root`, returning absolute paths of regular files.
30
+ * Skips: hidden directories (start with `.`), and any descendant of an
31
+ * existing `.vault-backup-*` directory.
32
+ */
33
+ export async function walkFiles(root) {
34
+ /** @type {string[]} */
35
+ const out = [];
36
+ /** @type {string[]} */
37
+ const stack = [root];
38
+
39
+ while (stack.length > 0) {
40
+ const dir = stack.pop();
41
+ let entries;
42
+ try {
43
+ entries = await fs.readdir(dir, { withFileTypes: true });
44
+ } catch (err) {
45
+ process.stderr.write(`${SCRIPT_NAME}: WARN readdir failed for ${dir}: ${err.message}\n`);
46
+ continue;
47
+ }
48
+
49
+ for (const entry of entries) {
50
+ const fullPath = path.join(dir, entry.name);
51
+
52
+ // F5 (issue #514 fold-in) — symlink guard, evaluated FIRST.
53
+ //
54
+ // `fs.readdir(dir, { withFileTypes: true })` populates each Dirent from
55
+ // an `lstat` of the entry, so a symlink-to-directory already reports
56
+ // `isDirectory() === false` (and `isSymbolicLink() === true`) and would
57
+ // fall through both branches below untouched. This explicit guard makes
58
+ // the skip OBSERVABLE (a WARN line) and hardens against any future
59
+ // refactor that switches to `fs.stat`-based classification — which DOES
60
+ // follow symlinks and could recurse into an out-of-tree directory or
61
+ // copy a dereferenced target into the backup. We never follow a symlink:
62
+ // neither symlinked directories (recursion risk) nor symlinked files
63
+ // (the dereference-into-backup risk #514 also guards at stageBackup).
64
+ if (entry.isSymbolicLink()) {
65
+ process.stderr.write(
66
+ `${SCRIPT_NAME}: WARN skipping symlink (not dereferenced): ${fullPath}\n`
67
+ );
68
+ continue;
69
+ }
70
+
71
+ if (entry.isDirectory()) {
72
+ // Skip hidden dirs and prior backup staging dirs
73
+ if (entry.name === '.git') continue;
74
+ if (entry.name.startsWith(BACKUP_PREFIX)) continue;
75
+ if (entry.name === '.obsidian' || entry.name === '.trash') continue;
76
+ if (entry.name === 'node_modules') continue;
77
+ // Allow other dotfiles (vault-mirror writes none, but be conservative)
78
+ stack.push(fullPath);
79
+ continue;
80
+ }
81
+
82
+ if (entry.isFile()) {
83
+ // Skip macOS metadata, the decisions sidecar, and .DS_Store
84
+ if (entry.name === '.DS_Store') continue;
85
+ if (entry.name === DECISIONS_SIDECAR_REL) continue;
86
+ if (entry.name.startsWith('._')) continue;
87
+ // Skip backup archives left by prior --apply runs (idempotency).
88
+ // Matches both `.vault-backup-<ts>.tar.gz` and `.vault-backup-<ts>` dirs
89
+ // (the dir form is also filtered by the directory branch above).
90
+ if (entry.name.startsWith(BACKUP_PREFIX)) continue;
91
+ out.push(fullPath);
92
+ }
93
+ // (Symlinks already handled by the isSymbolicLink() guard above.)
94
+ }
95
+ }
96
+
97
+ out.sort();
98
+ return out;
99
+ }
100
+
101
+ export async function sha256OfFile(absPath) {
102
+ const buf = await fs.readFile(absPath);
103
+ return crypto.createHash('sha256').update(buf).digest('hex');
104
+ }
105
+
106
+ /**
107
+ * Check whether `B` starts with `A` modulo trailing whitespace.
108
+ * Both are utf-8 strings.
109
+ */
110
+ export function isPrefix(a, b) {
111
+ const aTrim = a.trimEnd();
112
+ if (aTrim.length === 0) return false;
113
+ if (aTrim.length > b.length) return false;
114
+ return b.startsWith(aTrim);
115
+ }
116
+
117
+ /**
118
+ * Classify one source file.
119
+ *
120
+ * @param {string} srcAbs absolute path inside source vault
121
+ * @param {string} rel relative path under source root
122
+ * @param {string} canonicalRoot absolute path of the canonical vault root
123
+ * @returns {Promise<object>} action record
124
+ */
125
+ export async function classifyFile(srcAbs, rel, canonicalRoot) {
126
+ const dstAbs = path.join(canonicalRoot, rel);
127
+
128
+ let srcStat;
129
+ try {
130
+ srcStat = await fs.stat(srcAbs);
131
+ } catch (err) {
132
+ return {
133
+ kind: 'action',
134
+ action: 'error',
135
+ rel,
136
+ source: srcAbs,
137
+ canonical: dstAbs,
138
+ error: `stat source failed: ${err.message}`,
139
+ };
140
+ }
141
+
142
+ let dstStat;
143
+ try {
144
+ dstStat = await fs.stat(dstAbs);
145
+ } catch {
146
+ dstStat = null;
147
+ }
148
+
149
+ if (dstStat === null) {
150
+ return {
151
+ kind: 'action',
152
+ action: 'copy',
153
+ rel,
154
+ source: srcAbs,
155
+ canonical: dstAbs,
156
+ src_size: srcStat.size,
157
+ src_mtime: srcStat.mtime.toISOString(),
158
+ };
159
+ }
160
+
161
+ // Both sides exist — hash both
162
+ let srcSha;
163
+ let dstSha;
164
+ try {
165
+ [srcSha, dstSha] = await Promise.all([sha256OfFile(srcAbs), sha256OfFile(dstAbs)]);
166
+ } catch (err) {
167
+ return {
168
+ kind: 'action',
169
+ action: 'error',
170
+ rel,
171
+ source: srcAbs,
172
+ canonical: dstAbs,
173
+ error: `hash failed: ${err.message}`,
174
+ };
175
+ }
176
+
177
+ if (srcSha === dstSha) {
178
+ return {
179
+ kind: 'action',
180
+ action: 'skip-already-present',
181
+ rel,
182
+ source: srcAbs,
183
+ canonical: dstAbs,
184
+ src_size: srcStat.size,
185
+ dst_size: dstStat.size,
186
+ src_sha: srcSha,
187
+ dst_sha: dstSha,
188
+ };
189
+ }
190
+
191
+ // Content differs — check subset relationship to pre-suggest a winner.
192
+ // We use explicit `isUtf8()` binary detection (not throw-based) to classify
193
+ // as `merge` vs `conflict-needs-review`. Node 20+ silently substitutes the
194
+ // U+FFFD replacement char for invalid UTF-8 sequences instead of throwing
195
+ // from `readFile('utf8')`, which previously left the conflict-needs-review
196
+ // branch unreachable for binary files (issue #508).
197
+ let subsetHint = null;
198
+ let textReadable = false;
199
+ try {
200
+ const [srcBuf, dstBuf] = await Promise.all([fs.readFile(srcAbs), fs.readFile(dstAbs)]);
201
+ // Both sides must be valid UTF-8 to be classified as `merge`. If either
202
+ // is binary (or non-UTF-8 encoded), fall through to conflict-needs-review
203
+ // so the operator inspects the pair manually with the right tool.
204
+ if (isUtf8(srcBuf) && isUtf8(dstBuf)) {
205
+ textReadable = true;
206
+ const srcContent = srcBuf.toString('utf8');
207
+ const dstContent = dstBuf.toString('utf8');
208
+ if (isPrefix(srcContent, dstContent)) {
209
+ // canonical is a superset of source → prefer canonical (dst)
210
+ subsetHint = 'dst-is-superset';
211
+ } else if (isPrefix(dstContent, srcContent)) {
212
+ // source is a superset of canonical → prefer source (src)
213
+ subsetHint = 'src-is-superset';
214
+ }
215
+ }
216
+ } catch {
217
+ // Read error (permissions, disappeared mid-walk, etc.) — leave
218
+ // textReadable=false; treat as conflict-needs-review so the operator
219
+ // inspects manually.
220
+ }
221
+
222
+ // Classification:
223
+ // - both sides valid UTF-8 → merge (AUQ-resolvable;
224
+ // subset_hint may suggest winner)
225
+ // - unreadable / binary / non-UTF-8 → conflict-needs-review
226
+ //
227
+ // Both classes still require an operator decision via the two-phase
228
+ // coordinator flow, but the label tells the caller whether the diff can be
229
+ // shown inline (merge) or requires opening the file in a binary tool
230
+ // (conflict-needs-review).
231
+ const action = textReadable ? 'merge' : 'conflict-needs-review';
232
+
233
+ return {
234
+ kind: 'action',
235
+ action,
236
+ rel,
237
+ source: srcAbs,
238
+ canonical: dstAbs,
239
+ src_size: srcStat.size,
240
+ dst_size: dstStat.size,
241
+ src_mtime: srcStat.mtime.toISOString(),
242
+ dst_mtime: dstStat.mtime.toISOString(),
243
+ src_sha: srcSha,
244
+ dst_sha: dstSha,
245
+ subset_hint: subsetHint,
246
+ };
247
+ }
248
+
249
+ /**
250
+ * Stage one file into the backup directory, preserving its relative path.
251
+ *
252
+ * #514 — symlink defense (defense-in-depth alongside the walk-level guard).
253
+ * `fs.copyFile()` and `fs.stat()` both FOLLOW symlinks, so a symlinked source
254
+ * entry (e.g. `evil.md → /etc/passwd`) would silently copy the TARGET's
255
+ * contents into the backup. We `fs.lstat()` first (which does NOT follow the
256
+ * link) and skip the copy entirely if the source is a symlink, so a symlink is
257
+ * never dereferenced into the backup.
258
+ *
259
+ * @returns {Promise<{staged: boolean}>} staged=false when skipped (symlink)
260
+ */
261
+ export async function stageBackup(backupRoot, srcAbs, rel) {
262
+ const linkStat = await fs.lstat(srcAbs);
263
+ if (linkStat.isSymbolicLink()) {
264
+ process.stderr.write(
265
+ `${SCRIPT_NAME}: WARN refusing to back up symlink (not dereferenced): ${srcAbs}\n`
266
+ );
267
+ return { staged: false };
268
+ }
269
+
270
+ const stagedAbs = path.join(backupRoot, rel);
271
+ await fs.mkdir(path.dirname(stagedAbs), { recursive: true });
272
+ await fs.copyFile(srcAbs, stagedAbs);
273
+ try {
274
+ const st = await fs.stat(srcAbs);
275
+ await fs.utimes(stagedAbs, st.atime, st.mtime);
276
+ } catch {
277
+ // non-fatal
278
+ }
279
+ return { staged: true };
280
+ }
281
+
282
+ /**
283
+ * Run `tar -czf <archive> -C <parent> <basename>` then remove the staging dir.
284
+ * Falls back to leaving the staging directory in place if tar is unavailable.
285
+ *
286
+ * @param {string} backupRoot absolute path of the staging directory
287
+ * @returns {Promise<{archive: string|null, removed: boolean}>}
288
+ */
289
+ export async function compressAndCleanupBackup(backupRoot) {
290
+ const parent = path.dirname(backupRoot);
291
+ const basename = path.basename(backupRoot);
292
+ const archive = `${backupRoot}.tar.gz`;
293
+
294
+ const res = spawnSync('tar', ['-czf', archive, '-C', parent, basename], {
295
+ stdio: ['ignore', 'pipe', 'pipe'],
296
+ encoding: 'utf8',
297
+ });
298
+
299
+ // F1 (issue #514 fold-in) — distinguish "tar not on PATH" (ENOENT) from a
300
+ // non-zero tar exit. On ENOENT `spawnSync` sets `res.error` and leaves
301
+ // `res.status === null`; the generic branch below would otherwise print a
302
+ // confusing "tar failed (status null)". Surface the real cause and keep the
303
+ // uncompressed staging dir as the documented fallback.
304
+ if (res.error) {
305
+ const reason = res.error.code === 'ENOENT' ? 'tar not found on PATH' : res.error.message;
306
+ process.stderr.write(
307
+ `${SCRIPT_NAME}: WARN ${reason} — cannot compress backup. ` +
308
+ `Leaving staging directory at ${backupRoot} for manual archival.\n`
309
+ );
310
+ return { archive: null, removed: false };
311
+ }
312
+
313
+ if (res.status === 0) {
314
+ // Remove the now-redundant staging directory
315
+ try {
316
+ await fs.rm(backupRoot, { recursive: true, force: true });
317
+ return { archive, removed: true };
318
+ } catch (err) {
319
+ process.stderr.write(
320
+ `${SCRIPT_NAME}: WARN backup compressed but staging dir cleanup failed: ${err.message}\n`
321
+ );
322
+ return { archive, removed: false };
323
+ }
324
+ }
325
+
326
+ process.stderr.write(
327
+ `${SCRIPT_NAME}: WARN tar failed (status ${res.status}): ${res.stderr || res.stdout || 'no detail'}. ` +
328
+ `Leaving staging directory at ${backupRoot} for manual archival.\n`
329
+ );
330
+ return { archive: null, removed: false };
331
+ }
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * vault-migration-rules.mjs — Per-user config loader for the vault migration scripts.
4
+ *
5
+ * Reads ~/.config/session-orchestrator/vault-migration-rules.yaml and returns
6
+ * structured defaults for the operator-private slug / username-rewrite data
7
+ * that the migration scripts (migrate-vault-paths.mjs, migrate-cold-start-seed.mjs)
8
+ * used to carry as hardcoded literals.
9
+ *
10
+ * Out of scope for this loader: argument-parsing, YAML schema versioning beyond
11
+ * v1, runtime mutation. The migration scripts retain `--repos` / `--from` / `--to`
12
+ * CLI args as the override path; this loader only supplies sensible defaults.
13
+ *
14
+ * Schema (schema-version: 1):
15
+ *
16
+ * schema-version: 1
17
+ * username-rewrites:
18
+ * - from: '/Users/oldname/'
19
+ * to: '/Users/newname/'
20
+ * audited-repos: # default --repos for migrate-vault-paths.mjs
21
+ * - repo-name-or-absolute-path
22
+ * dormant-repos: # default --repos for migrate-cold-start-seed.mjs
23
+ * - '~/Projects/path/to/dormant-repo'
24
+ *
25
+ * All three sections are optional. Missing file → defaults are empty arrays;
26
+ * the migration scripts then fall back to their CLI-args path or print a
27
+ * helpful error if no override is given.
28
+ *
29
+ * Path: ~/.config/session-orchestrator/vault-migration-rules.yaml
30
+ * (same per-user location convention as owner.yaml — outside every repo,
31
+ * appended to ~/.gitignore by the migration-script bootstrap on first
32
+ * write).
33
+ */
34
+
35
+ import { readFileSync, existsSync } from 'node:fs';
36
+ import { join } from 'node:path';
37
+ import { homedir } from 'node:os';
38
+ import yaml from 'js-yaml';
39
+
40
+ export const VAULT_MIGRATION_RULES_PATH = join(
41
+ homedir(),
42
+ '.config',
43
+ 'session-orchestrator',
44
+ 'vault-migration-rules.yaml',
45
+ );
46
+
47
+ /**
48
+ * Return the empty-defaults shape — used when the file does not exist or
49
+ * fails to parse. Callers decide whether to error or fall back to CLI args.
50
+ *
51
+ * @returns {{ usernameRewrites: Array<{from:string,to:string}>, auditedRepos: string[], dormantRepos: string[] }}
52
+ */
53
+ export function getDefaults() {
54
+ return {
55
+ usernameRewrites: [],
56
+ auditedRepos: [],
57
+ dormantRepos: [],
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Load and validate the vault-migration-rules config. Pure, never throws.
63
+ *
64
+ * @param {string} [filePath] - override for the default path (testing only)
65
+ * @returns {{ config: ReturnType<typeof getDefaults>, source: 'file'|'defaults', errors: string[] }}
66
+ */
67
+ export function loadVaultMigrationRules(filePath = VAULT_MIGRATION_RULES_PATH) {
68
+ if (!existsSync(filePath)) {
69
+ return { config: getDefaults(), source: 'defaults', errors: [] };
70
+ }
71
+
72
+ let raw;
73
+ try {
74
+ raw = readFileSync(filePath, 'utf8');
75
+ } catch (err) {
76
+ return {
77
+ config: getDefaults(),
78
+ source: 'defaults',
79
+ errors: [`failed to read vault-migration-rules.yaml: ${err.message}`],
80
+ };
81
+ }
82
+
83
+ let parsed;
84
+ try {
85
+ parsed = yaml.load(raw);
86
+ } catch (err) {
87
+ return {
88
+ config: getDefaults(),
89
+ source: 'defaults',
90
+ errors: [`failed to parse vault-migration-rules.yaml: ${err.message}`],
91
+ };
92
+ }
93
+
94
+ const { config, errors } = normalize(parsed);
95
+ return { config, source: errors.length === 0 ? 'file' : 'defaults', errors };
96
+ }
97
+
98
+ /**
99
+ * Normalize a parsed YAML object into the canonical shape, returning errors
100
+ * for malformed entries. Unknown sections are ignored (forward-compatible).
101
+ *
102
+ * @param {unknown} obj
103
+ * @returns {{ config: ReturnType<typeof getDefaults>, errors: string[] }}
104
+ */
105
+ function normalize(obj) {
106
+ const errors = [];
107
+ const config = getDefaults();
108
+
109
+ if (!isPlainObject(obj)) {
110
+ return { config, errors: ['config must be a YAML mapping'] };
111
+ }
112
+
113
+ // username-rewrites: [{ from: string, to: string }]
114
+ const rewrites = obj['username-rewrites'];
115
+ if (rewrites !== undefined) {
116
+ if (!Array.isArray(rewrites)) {
117
+ errors.push('username-rewrites must be an array');
118
+ } else {
119
+ for (let i = 0; i < rewrites.length; i++) {
120
+ const r = rewrites[i];
121
+ if (!isPlainObject(r) || typeof r.from !== 'string' || typeof r.to !== 'string') {
122
+ errors.push(`username-rewrites[${i}]: must have string 'from' and 'to' fields`);
123
+ continue;
124
+ }
125
+ config.usernameRewrites.push({ from: r.from, to: r.to });
126
+ }
127
+ }
128
+ }
129
+
130
+ // audited-repos: string[]
131
+ const audited = obj['audited-repos'];
132
+ if (audited !== undefined) {
133
+ if (!Array.isArray(audited) || !audited.every((s) => typeof s === 'string')) {
134
+ errors.push('audited-repos must be an array of strings');
135
+ } else {
136
+ config.auditedRepos = audited.slice();
137
+ }
138
+ }
139
+
140
+ // dormant-repos: string[]
141
+ const dormant = obj['dormant-repos'];
142
+ if (dormant !== undefined) {
143
+ if (!Array.isArray(dormant) || !dormant.every((s) => typeof s === 'string')) {
144
+ errors.push('dormant-repos must be an array of strings');
145
+ } else {
146
+ config.dormantRepos = dormant.slice();
147
+ }
148
+ }
149
+
150
+ return { config, errors };
151
+ }
152
+
153
+ function isPlainObject(v) {
154
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
155
+ }