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,796 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * migrate-vault-paths.mjs — one-shot cross-repo migration for username-drift in vault/project paths.
4
+ *
5
+ * Rewrites a literal "from" username path-segment to a "to" segment across markdown
6
+ * files under ~/Projects/. Only the username path segment changes; the trailing
7
+ * path is preserved.
8
+ *
9
+ * Typical use: a host username change (`/Users/oldname/` → `/Users/newname/`) leaves
10
+ * hardcoded paths in CLAUDE.md / AGENTS.md / STATE.md across every checked-out repo.
11
+ * This script sweeps them in one pass.
12
+ *
13
+ * Usage:
14
+ * node scripts/migrate-vault-paths.mjs --from <old/> --to <new/> [--dry-run|--apply] [--repos <comma,list>] [--json] [--help]
15
+ *
16
+ * Rule resolution (in priority order):
17
+ * 1. --from / --to flags (explicit CLI override)
18
+ * 2. username-rewrites: [{from,to}] in
19
+ * ~/.config/session-orchestrator/vault-migration-rules.yaml
20
+ * (first entry is used; multiple rewrites in one run require multiple invocations)
21
+ * 3. <none> → fail with non-zero exit and a hint to the config file.
22
+ *
23
+ * Target repo resolution (in priority order):
24
+ * 1. --repos <comma,list> (explicit CLI override)
25
+ * 2. audited-repos: [...] in the same vault-migration-rules.yaml
26
+ * 3. <none, no --repos> → scan only the auto-discover sweep under ~/Projects/**.
27
+ *
28
+ * Flags:
29
+ * --from SEG Literal source segment (e.g. '/Users/oldname/'). Required if no config entry.
30
+ * --to SEG Literal target segment (e.g. '/Users/newname/'). Required if no config entry.
31
+ * --dry-run Preview changes without writing (DEFAULT)
32
+ * --apply Write fixes in-place (atomic: tmp + rename)
33
+ * --repos LIST Comma-separated repo names or absolute paths to scan
34
+ * --json Emit machine-readable JSONL on stdout (one record per hit)
35
+ * --help, -h Show this help
36
+ *
37
+ * Exit codes:
38
+ * 0 Success (including no-op)
39
+ * 1 Input/arg error
40
+ * 2 I/O error
41
+ *
42
+ * Idempotent: re-running --apply after a successful run is a no-op.
43
+ *
44
+ * Output (stdout — human or --json):
45
+ * migrate-vault-paths: <abbrev-path>:<line>: <classification> → would-fix|fixed|skipped
46
+ *
47
+ * Summary line (stderr):
48
+ * migrate-vault-paths: N files scanned, M lines fixed, K historical skipped [dry-run|applied]
49
+ */
50
+
51
+ import { promises as fs, existsSync } from 'node:fs';
52
+ import { spawnSync } from 'node:child_process';
53
+ import { pathToFileURL } from 'node:url';
54
+ import path from 'node:path';
55
+ import os from 'node:os';
56
+ import {
57
+ loadVaultMigrationRules,
58
+ VAULT_MIGRATION_RULES_PATH,
59
+ } from './lib/vault-migration-rules.mjs';
60
+ import { parseColumnFlags, CliFlagError } from './lib/cli-flags.mjs';
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // Constants
64
+ // ---------------------------------------------------------------------------
65
+
66
+ const HOME = os.homedir();
67
+ const PROJECTS_ROOT = path.join(HOME, 'Projects');
68
+
69
+ // Set at startup from CLI args or config. Helpers read these; main() resolves them.
70
+ let OLD_SEGMENT = null;
71
+ let NEW_SEGMENT = null;
72
+
73
+ /**
74
+ * Test-only seam (architect MED #607 D3): set the module-level OLD/NEW segments
75
+ * that the exported pure helpers (rewriteContent, findMissingSegmentHits,
76
+ * isOwnedByUsernamePath, classifyHit) read. The CLI path sets these inside
77
+ * main() from --from/--to or config; importing tests have no other way to drive
78
+ * the segment-dependent helpers. Underscore prefix = internal-but-test-visible,
79
+ * mirroring the _parseVaultIntegration / _parseMemoryProposals convention.
80
+ *
81
+ * Returns the prior values so a test can save/restore around an assertion.
82
+ */
83
+ function _setSegmentsForTest(oldSeg, newSeg) {
84
+ const prev = { OLD_SEGMENT, NEW_SEGMENT };
85
+ OLD_SEGMENT = oldSeg;
86
+ NEW_SEGMENT = newSeg;
87
+ return prev;
88
+ }
89
+
90
+ // ── Missing-segment class (GitLab #600 D3) ────────────────────────────────────
91
+ // A second, username-independent drift class: a `vault-dir:` value that points at
92
+ // `~/Projects/vault` (or `/Users/<user>/Projects/vault`) — MISSING the canonical
93
+ // `/Bernhard/` owner segment — must become `~/Projects/Bernhard/vault`.
94
+ //
95
+ // Scoping discipline (do NOT blunt-rewrite every `~/Projects/` path):
96
+ // - Only lines in a `vault-dir:` context (vault-integration.vault-dir /
97
+ // vault-sync.vault-dir) are touched. `cache: ~/Projects/vault-backups` and
98
+ // other unrelated `~/Projects/...` paths are left untouched.
99
+ // - The match anchors the value to `~` or `/Users/<user>` immediately before
100
+ // `/Projects/vault`, and requires a path boundary AFTER `vault` (negative
101
+ // lookahead `(?![\w-])`) so `vault-backups` / `vaultfoo` never match.
102
+ // - Matching `Projects/vault` directly (not `Projects/Bernhard/vault`) yields
103
+ // idempotency for free: a canonical line has `Projects/Bernhard/vault`, so
104
+ // the pattern does not match it → no `.../Bernhard/Bernhard/vault`.
105
+ //
106
+ // The owner segment is the canonical `Bernhard`; the rewrite inserts it between
107
+ // `Projects/` and `vault`.
108
+ const MISSING_SEGMENT_OWNER = 'Bernhard';
109
+
110
+ // Classification label for the missing-/Bernhard/-segment drift class. Referenced
111
+ // in findMissingSegmentHits() (hit record) and the emit() action strings — hoisted
112
+ // here so the literal lives in exactly one place (architect MED #607 D3).
113
+ const MISSING_SEGMENT_CLASS = 'vault-dir-missing-segment';
114
+
115
+ const MISSING_SEGMENT_RE = new RegExp(
116
+ // (1) vault-dir: prefix (with optional surrounding whitespace)
117
+ // (2) value root: ~ or /Users/<user>
118
+ // (3) /Projects/vault, NOT followed by a word char or hyphen (path boundary)
119
+ String.raw`(vault-dir:\s*['"]?)(~|/Users/[^/\s'"]+)/Projects/vault(?![\w-])`,
120
+ 'g',
121
+ );
122
+
123
+ const EXCLUDE_GLOBS = [
124
+ '/tests/',
125
+ '/fixtures/',
126
+ '/.git/',
127
+ '/.vault-backup-',
128
+ '/node_modules/',
129
+ // Mutation-testing & build-temp sandboxes — these are file copies, not source
130
+ '/.stryker-tmp/',
131
+ '/.next/',
132
+ '/dist/',
133
+ '/build/',
134
+ '/coverage/',
135
+ ];
136
+
137
+ /**
138
+ * Historical heuristic: skip rewriting these hits (still report them in dry-run
139
+ * so reviewers see the scope). Matches:
140
+ * - decisions.md (session decision log)
141
+ * - /history/ in path (any history directory)
142
+ * - /archive/ or /XX-archive/ in path (vault-style archives like 90-archive/)
143
+ * - basename containing 'archive' (ARCHIVE-INSTRUCTIONS.md, …)
144
+ * - completed/snapshot session-evidence under .research/, .task-evidence/
145
+ * when filename includes a date stamp or "session-" prefix
146
+ */
147
+ function isHistorical(filePath) {
148
+ const lower = filePath.toLowerCase();
149
+ const base = path.basename(lower);
150
+ if (base === 'decisions.md') return true;
151
+ if (lower.includes('/history/')) return true;
152
+ if (lower.includes('-history/')) return true; // e.g. pricing-history/
153
+ // Vault-style archive directories: /90-archive/, /archive/, /archived/
154
+ if (/\/(?:\d{2}-)?archived?\//.test(lower)) return true;
155
+ if (base.includes('archive')) return true;
156
+ return false;
157
+ }
158
+
159
+ // ---------------------------------------------------------------------------
160
+ // Arg parsing
161
+ // ---------------------------------------------------------------------------
162
+
163
+ function parseArgs(argv) {
164
+ // Migrated to scripts/lib/cli-flags.mjs (#510). Per-script semantics
165
+ // (mutex check, --repos comma-split, "next arg starts with --" guards)
166
+ // remain here; the helper owns argv tokenisation + unknown-flag rejection.
167
+ //
168
+ // Note: `node:util` parseArgs strict mode already rejects "--from --to"
169
+ // patterns by treating `--to` as the missing-value for `--from` (the prior
170
+ // hand-rolled check covered the same case, with a different error message).
171
+ let parsedFlags;
172
+ try {
173
+ parsedFlags = parseColumnFlags({
174
+ argv: argv.slice(2),
175
+ knownBool: {
176
+ help: { short: 'h', default: false },
177
+ apply: false,
178
+ 'dry-run': false,
179
+ json: false,
180
+ },
181
+ knownString: {
182
+ repos: null, // raw comma-separated string; split below
183
+ from: null,
184
+ to: null,
185
+ },
186
+ });
187
+ } catch (err) {
188
+ if (err instanceof CliFlagError) {
189
+ // Preserve legacy prose so the existing test contracts at
190
+ // tests/scripts/migrate-vault-paths.test.mjs ("unknown argument",
191
+ // "--repos requires") keep passing — same exit-1 behaviour, same
192
+ // user-visible message shape, helper owns the actual parsing.
193
+ const legacy = err.message
194
+ .replace(/^Unknown option/, 'unknown argument')
195
+ .replace(/^Option '--repos <value>' argument missing$/, '--repos requires a comma-separated list')
196
+ .replace(/^Option '--from <value>' argument missing$/, '--from requires a literal source segment')
197
+ .replace(/^Option '--to <value>' argument missing$/, '--to requires a literal target segment');
198
+ process.stderr.write(`migrate-vault-paths: ${legacy}\n`);
199
+ process.exit(1);
200
+ }
201
+ throw err;
202
+ }
203
+
204
+ const values = parsedFlags.values;
205
+
206
+ // Mutex check: --dry-run and --apply on the same invocation is the
207
+ // canonical footgun (issue #509). Preserved verbatim.
208
+ const applyFlag = values.apply === true;
209
+ const dryRunFlag = values['dry-run'] === true;
210
+ if (applyFlag && dryRunFlag) {
211
+ process.stderr.write('migrate-vault-paths: --dry-run and --apply are mutually exclusive\n');
212
+ process.exit(1);
213
+ }
214
+
215
+ // Split --repos into a string[] at the use site, preserving the prior shape
216
+ // contract (the downstream code at main() iterates opts.repos as an array).
217
+ let repos = null;
218
+ if (values.repos !== undefined && values.repos !== null) {
219
+ repos = values.repos
220
+ .split(',')
221
+ .map((s) => s.trim())
222
+ .filter(Boolean);
223
+ }
224
+
225
+ return {
226
+ help: values.help === true,
227
+ apply: applyFlag, // --dry-run leaves this false (matches the prior shape)
228
+ json: values.json === true,
229
+ repos,
230
+ from: values.from ?? null,
231
+ to: values.to ?? null,
232
+ };
233
+ }
234
+
235
+ function printHelp() {
236
+ process.stdout.write(
237
+ `Usage: migrate-vault-paths.mjs --from <old/> --to <new/> [--dry-run|--apply] [--repos <comma,list>] [--json] [--help]
238
+
239
+ Rewrites a literal "from" path segment to a "to" segment across markdown files
240
+ under ~/Projects/, skipping tests/, fixtures/, .git/, vault-backups, and
241
+ historical contexts (decisions.md, history/, archive*).
242
+
243
+ Both --from and --to are required unless username-rewrites[0] is set in
244
+ ${VAULT_MIGRATION_RULES_PATH}.
245
+
246
+ Options:
247
+ --from SEG Literal source segment (e.g. '/Users/oldname/')
248
+ --to SEG Literal target segment (e.g. '/Users/newname/')
249
+ --dry-run Preview changes without writing (DEFAULT)
250
+ --apply Write fixes in-place (atomic: tmp + rename)
251
+ --repos LIST Comma-separated repo names or paths to scan; falls back
252
+ to audited-repos: [...] in vault-migration-rules.yaml,
253
+ then to ~/Projects/** auto-discovery sweep
254
+ --json Emit JSONL records on stdout (one per hit)
255
+ --help, -h Show this help
256
+
257
+ Exit codes: 0 success 1 input error 2 I/O error
258
+ `,
259
+ );
260
+ }
261
+
262
+ // ---------------------------------------------------------------------------
263
+ // Repo discovery
264
+ // ---------------------------------------------------------------------------
265
+
266
+ /**
267
+ * Resolve a repo name to an absolute path by searching ~/Projects/**.
268
+ * If the input is already an absolute path that exists, returns it as-is.
269
+ * Returns null when no match is found.
270
+ */
271
+ function resolveRepoPath(nameOrPath) {
272
+ if (path.isAbsolute(nameOrPath)) {
273
+ return existsSync(nameOrPath) ? nameOrPath : null;
274
+ }
275
+ // Search up to 4 levels deep under ~/Projects/
276
+ const result = spawnSync(
277
+ 'find',
278
+ [PROJECTS_ROOT, '-maxdepth', '4', '-type', 'd', '-name', nameOrPath],
279
+ { encoding: 'utf8' },
280
+ );
281
+ if (result.status !== 0) return null;
282
+ const lines = result.stdout.split('\n').map((l) => l.trim()).filter(Boolean);
283
+ // Prefer non-vault paths; the vault mirror under 01-projects/<name>/ is the wrong target.
284
+ const nonVault = lines.find((l) => !l.includes('/vault/'));
285
+ return nonVault ?? lines[0] ?? null;
286
+ }
287
+
288
+ /**
289
+ * Use grep -r -l -I to find every text file under any root that contains the
290
+ * literal old-username segment. Returns absolute file paths.
291
+ * The -I flag excludes binary files.
292
+ */
293
+ function findCandidateFiles(roots) {
294
+ if (roots.length === 0) return [];
295
+ const result = spawnSync(
296
+ 'grep',
297
+ [
298
+ '-r', // recursive
299
+ '-l', // list-files-with-matches
300
+ '-I', // skip binary
301
+ '--include=*.md', // markdown only (CLI runbooks live in .md; STATE.md and STATUS.md included)
302
+ OLD_SEGMENT,
303
+ ...roots,
304
+ ],
305
+ { encoding: 'utf8' },
306
+ );
307
+ // ENOENT guard (GitLab #600 F2): when the `grep` binary is missing from PATH,
308
+ // spawnSync sets `result.error` (ENOENT) and `result.status` is null. The
309
+ // status check below treats null as a failure but reports an empty stderr
310
+ // ("grep failed: "), masking the real cause. Surface the spawn error first.
311
+ if (result.error) {
312
+ process.stderr.write(
313
+ `migrate-vault-paths: grep/find not found on PATH (${result.error.code ?? result.error.message})\n`,
314
+ );
315
+ process.exit(2);
316
+ }
317
+ // grep exits 1 when no matches; both 0 and 1 are valid responses
318
+ if (result.status !== 0 && result.status !== 1) {
319
+ process.stderr.write(`migrate-vault-paths: grep failed: ${result.stderr}\n`);
320
+ process.exit(2);
321
+ }
322
+ return result.stdout
323
+ .split('\n')
324
+ .map((l) => l.trim())
325
+ .filter(Boolean)
326
+ .filter((p) => !EXCLUDE_GLOBS.some((g) => p.includes(g)));
327
+ }
328
+
329
+ /**
330
+ * Discover files containing a missing-segment `vault-dir:` drift (GitLab #600
331
+ * D3) — i.e. a `vault-dir:` line pointing at `~/Projects/vault` (or
332
+ * `/Users/<user>/Projects/vault`) that lacks the canonical `/Bernhard/` owner
333
+ * segment. These files do NOT contain OLD_SEGMENT, so findCandidateFiles() (which
334
+ * greps OLD_SEGMENT) cannot find them.
335
+ *
336
+ * grep matches the literal `Projects/vault`; per-file classification then applies
337
+ * the precise MISSING_SEGMENT_RE (vault-dir context + path-boundary + not-canonical)
338
+ * to weed out false positives such as `cache: ~/Projects/vault-backups`. Test-dir
339
+ * and other EXCLUDE_GLOBS are filtered out so tests/** fixtures are never rewritten.
340
+ */
341
+ function findMissingSegmentFiles(roots) {
342
+ if (roots.length === 0) return [];
343
+ const result = spawnSync(
344
+ 'grep',
345
+ [
346
+ '-r',
347
+ '-l',
348
+ '-I',
349
+ '--include=*.md',
350
+ // Coarse literal pre-filter; precise vault-dir/idempotency check happens
351
+ // per-line in findMissingSegmentHits(). 'Projects/vault' also matches
352
+ // canonical 'Projects/Bernhard/vault' lines, but those are dropped later.
353
+ 'Projects/vault',
354
+ ...roots,
355
+ ],
356
+ { encoding: 'utf8' },
357
+ );
358
+ if (result.error) {
359
+ process.stderr.write(
360
+ `migrate-vault-paths: grep/find not found on PATH (${result.error.code ?? result.error.message})\n`,
361
+ );
362
+ process.exit(2);
363
+ }
364
+ if (result.status !== 0 && result.status !== 1) {
365
+ process.stderr.write(`migrate-vault-paths: grep failed: ${result.stderr}\n`);
366
+ process.exit(2);
367
+ }
368
+ return result.stdout
369
+ .split('\n')
370
+ .map((l) => l.trim())
371
+ .filter(Boolean)
372
+ .filter((p) => !EXCLUDE_GLOBS.some((g) => p.includes(g)));
373
+ }
374
+
375
+ /**
376
+ * Auto-discover ALL repos under ~/Projects/** that contain the OLD_SEGMENT
377
+ * literal OR a missing-segment `vault-dir:` drift. Used in tandem with the
378
+ * default-target list to find drift we don't already know about.
379
+ */
380
+ function discoverAdditionalFiles() {
381
+ if (!existsSync(PROJECTS_ROOT)) return [];
382
+ const set = new Set(findCandidateFiles([PROJECTS_ROOT]));
383
+ for (const f of findMissingSegmentFiles([PROJECTS_ROOT])) set.add(f);
384
+ return [...set];
385
+ }
386
+
387
+ // ---------------------------------------------------------------------------
388
+ // Classification
389
+ // ---------------------------------------------------------------------------
390
+
391
+ /**
392
+ * Classify a single drift hit on a line.
393
+ * - vault-dir-drift: contains `vault-dir:` AND vault-path literal
394
+ * - path-drift: any other use of the old username
395
+ * - historical: file is in a decisions.md / history/ / archive context
396
+ */
397
+ function classifyHit(filePath, line) {
398
+ if (isHistorical(filePath)) return 'historical';
399
+ const lower = line.toLowerCase();
400
+ if (lower.includes('vault-dir:') && line.includes(OLD_SEGMENT + 'Projects/vault')) {
401
+ return 'vault-dir-drift';
402
+ }
403
+ return 'path-drift';
404
+ }
405
+
406
+ /**
407
+ * Does this line carry a missing-segment `vault-dir:` drift (GitLab #600 D3)?
408
+ * Returns true only for a `vault-dir:` value at `~/Projects/vault` or
409
+ * `/Users/<user>/Projects/vault` that is NOT already canonical
410
+ * (`~/Projects/Bernhard/vault`) — see MISSING_SEGMENT_RE for the precise shape.
411
+ * `RegExp.test` advances lastIndex on a /g regex, so reset it before each probe.
412
+ */
413
+ function lineHasMissingSegment(line) {
414
+ MISSING_SEGMENT_RE.lastIndex = 0;
415
+ return MISSING_SEGMENT_RE.test(line);
416
+ }
417
+
418
+ /**
419
+ * Is this line owned by the username-rewrite path (i.e. does it carry OLD_SEGMENT)?
420
+ *
421
+ * The missing-segment pass must never touch a line the username rewrite owns — a
422
+ * `vault-dir: /Users/oldname/Projects/vault` line is a username drift, not a
423
+ * missing-segment drift. This single predicate is the shared invariant used by
424
+ * BOTH findMissingSegmentHits() (skip the hit) and rewriteMissingSegment() (skip
425
+ * the rewrite, gated on the ORIGINAL line text). Extracted to one place so the
426
+ * two sites cannot drift apart (architect MED #607 D3).
427
+ *
428
+ * `line` may be undefined when an original-content array is shorter than the
429
+ * working-content array (chained transforms can change line count); an undefined
430
+ * line is, by definition, not owned by the username path → returns false.
431
+ */
432
+ function isOwnedByUsernamePath(line) {
433
+ return line !== undefined && line.includes(OLD_SEGMENT);
434
+ }
435
+
436
+ /**
437
+ * Find missing-segment hits in a file's content, returning
438
+ * [{line, text, classification: MISSING_SEGMENT_CLASS}].
439
+ * Historical files (decisions.md, archive/, …) are reported but not rewritten,
440
+ * mirroring the OLD_SEGMENT path — classifyHit's isHistorical() guard.
441
+ *
442
+ * Lines that also contain OLD_SEGMENT are owned by the username-rewrite path and
443
+ * deliberately skipped here: a `vault-dir: /Users/oldname/Projects/vault` line is
444
+ * a username drift (→ /Users/newname/Projects/vault), NOT a missing-segment drift.
445
+ * Without this guard the two classes collide and the username output would gain a
446
+ * spurious /Bernhard/ segment.
447
+ */
448
+ function findMissingSegmentHits(filePath, content) {
449
+ const historical = isHistorical(filePath);
450
+ const lines = content.split('\n');
451
+ const hits = [];
452
+ for (let i = 0; i < lines.length; i++) {
453
+ if (isOwnedByUsernamePath(lines[i])) continue; // owned by username-rewrite path
454
+ if (lineHasMissingSegment(lines[i])) {
455
+ hits.push({
456
+ line: i + 1, // 1-indexed
457
+ text: lines[i],
458
+ classification: historical ? 'historical' : MISSING_SEGMENT_CLASS,
459
+ });
460
+ }
461
+ }
462
+ return hits;
463
+ }
464
+
465
+ /**
466
+ * Rewrite missing-segment `vault-dir:` values in `content`: insert the canonical
467
+ * `/Bernhard/` owner segment between `Projects/` and `vault`. Only `vault-dir:`
468
+ * lines matching MISSING_SEGMENT_RE are touched — unrelated `~/Projects/...`
469
+ * paths (e.g. `cache: ~/Projects/vault-backups`) are preserved verbatim.
470
+ *
471
+ * `originalContent` (the file BEFORE rewriteContent ran) gates which lines are
472
+ * eligible: any line whose original carried OLD_SEGMENT is owned by the username
473
+ * path and is skipped, even though the username rewrite may have produced a
474
+ * `/Users/newname/Projects/vault` form that would otherwise match the regex.
475
+ * This keeps the two classes from colliding when both transforms chain.
476
+ *
477
+ * `originalContent` is REQUIRED (no default): the collision-gate is meaningless
478
+ * without the pre-rewrite text, and a `= content` default silently fails open if
479
+ * a future caller forgets it — passing the already-rewritten content as its own
480
+ * "original" would let a chained username rewrite leak a spurious /Bernhard/
481
+ * segment. Callers MUST pass the original content explicitly (architect MED #607 D3).
482
+ */
483
+ function rewriteMissingSegment(content, originalContent) {
484
+ const origLines = originalContent.split('\n');
485
+ return content
486
+ .split('\n')
487
+ .map((line, i) => {
488
+ // Skip lines the username path owns (matched on the ORIGINAL line text).
489
+ if (isOwnedByUsernamePath(origLines[i])) return line;
490
+ MISSING_SEGMENT_RE.lastIndex = 0;
491
+ return line.replace(
492
+ MISSING_SEGMENT_RE,
493
+ (_m, prefix, root) => `${prefix}${root}/Projects/${MISSING_SEGMENT_OWNER}/vault`,
494
+ );
495
+ })
496
+ .join('\n');
497
+ }
498
+
499
+ // ---------------------------------------------------------------------------
500
+ // File processing
501
+ // ---------------------------------------------------------------------------
502
+
503
+ /**
504
+ * Replace OLD_SEGMENT with NEW_SEGMENT across the full text. Only that literal
505
+ * segment changes; the trailing path is preserved verbatim (e.g. '/Users/x/Foo'
506
+ * → '/Users/y/Foo' when the rewrite rule is '/Users/x/' → '/Users/y/').
507
+ */
508
+ function rewriteContent(content) {
509
+ // Use split+join (no regex) so we never accidentally match a substring outside the literal.
510
+ return content.split(OLD_SEGMENT).join(NEW_SEGMENT);
511
+ }
512
+
513
+ /**
514
+ * Determine which lines in a file contain the literal, returning [{line: n, text, classification}].
515
+ */
516
+ function findHits(filePath, content) {
517
+ const lines = content.split('\n');
518
+ const hits = [];
519
+ for (let i = 0; i < lines.length; i++) {
520
+ if (lines[i].includes(OLD_SEGMENT)) {
521
+ hits.push({
522
+ line: i + 1, // 1-indexed
523
+ text: lines[i],
524
+ classification: classifyHit(filePath, lines[i]),
525
+ });
526
+ }
527
+ }
528
+ return hits;
529
+ }
530
+
531
+ /**
532
+ * Atomic write: write to <path>.migrate-tmp, then rename.
533
+ */
534
+ async function atomicWrite(filePath, content) {
535
+ const tmp = `${filePath}.migrate-tmp-${process.pid}-${Date.now()}`;
536
+ await fs.writeFile(tmp, content, 'utf8');
537
+ await fs.rename(tmp, filePath);
538
+ }
539
+
540
+ /**
541
+ * Shorten a path for display: ~/Projects/<short> from absolute.
542
+ */
543
+ function abbrev(filePath) {
544
+ if (filePath.startsWith(HOME)) return '~' + filePath.slice(HOME.length);
545
+ return filePath;
546
+ }
547
+
548
+ // ---------------------------------------------------------------------------
549
+ // Main
550
+ // ---------------------------------------------------------------------------
551
+
552
+ async function main() {
553
+ const opts = parseArgs(process.argv);
554
+
555
+ if (opts.help) {
556
+ printHelp();
557
+ process.exit(0);
558
+ }
559
+
560
+ // ── Resolve --from / --to rewrite segments ────────────────────────────────
561
+ // Priority: CLI args > config username-rewrites[0] > fail.
562
+ const ruleset = loadVaultMigrationRules();
563
+ for (const e of ruleset.errors) {
564
+ process.stderr.write(`migrate-vault-paths: config: ${e}\n`);
565
+ }
566
+ if (opts.from && opts.to) {
567
+ OLD_SEGMENT = opts.from;
568
+ NEW_SEGMENT = opts.to;
569
+ } else if (opts.from || opts.to) {
570
+ process.stderr.write('migrate-vault-paths: --from and --to must be given together\n');
571
+ process.exit(1);
572
+ } else if (ruleset.config.usernameRewrites.length > 0) {
573
+ OLD_SEGMENT = ruleset.config.usernameRewrites[0].from;
574
+ NEW_SEGMENT = ruleset.config.usernameRewrites[0].to;
575
+ } else {
576
+ process.stderr.write(
577
+ 'migrate-vault-paths: no rewrite rule found.\n' +
578
+ ' Either pass --from <old/> --to <new/>, or set username-rewrites: in\n' +
579
+ ` ${VAULT_MIGRATION_RULES_PATH}\n`
580
+ );
581
+ process.exit(1);
582
+ }
583
+
584
+ // Resolve the scan set: explicit --repos, or config audited-repos, or empty (auto-discover only)
585
+ const repoRoots = [];
586
+ const explicitRepos = opts.repos ?? (ruleset.config.auditedRepos.length > 0 ? ruleset.config.auditedRepos : null);
587
+ if (explicitRepos) {
588
+ for (const r of explicitRepos) {
589
+ const resolved = resolveRepoPath(r);
590
+ if (!resolved) {
591
+ process.stderr.write(`migrate-vault-paths: WARN repo not found: ${r}\n`);
592
+ continue;
593
+ }
594
+ repoRoots.push(resolved);
595
+ }
596
+ if (repoRoots.length === 0 && opts.repos) {
597
+ process.stderr.write('migrate-vault-paths: no valid repos to scan\n');
598
+ process.exit(1);
599
+ }
600
+ }
601
+
602
+ // Initial file discovery within named repos: OLD_SEGMENT files AND
603
+ // missing-segment vault-dir drift files (GitLab #600 D3 — the latter do not
604
+ // contain OLD_SEGMENT, so findCandidateFiles alone would miss them).
605
+ const candidateSet = new Set(findCandidateFiles(repoRoots));
606
+ for (const f of findMissingSegmentFiles(repoRoots)) candidateSet.add(f);
607
+ let candidateFiles = [...candidateSet];
608
+
609
+ // If NO explicit repo list was given (neither --repos nor config.audited-repos),
610
+ // expand scan to discover anything else under ~/Projects/**.
611
+ if (!explicitRepos) {
612
+ const additional = discoverAdditionalFiles();
613
+ const set = new Set(candidateFiles);
614
+ for (const f of additional) set.add(f);
615
+ candidateFiles = [...set];
616
+ }
617
+
618
+ candidateFiles.sort();
619
+
620
+ let totalScanned = 0;
621
+ let totalLinesFixed = 0;
622
+ let totalHistoricalSkipped = 0;
623
+ let totalPermissionErrors = 0;
624
+
625
+ for (const filePath of candidateFiles) {
626
+ // Skip if path matches an exclude glob (defense-in-depth; grep filter already applied)
627
+ if (EXCLUDE_GLOBS.some((g) => filePath.includes(g))) continue;
628
+
629
+ let content;
630
+ try {
631
+ // Skip symlinks (safer than dereferencing during a rewrite)
632
+ const lst = await fs.lstat(filePath);
633
+ if (lst.isSymbolicLink()) {
634
+ emit(opts, {
635
+ action: 'skipped',
636
+ reason: 'symlink',
637
+ file: filePath,
638
+ line: null,
639
+ classification: null,
640
+ });
641
+ continue;
642
+ }
643
+ content = await fs.readFile(filePath, 'utf8');
644
+ } catch (err) {
645
+ totalPermissionErrors++;
646
+ emit(opts, {
647
+ action: 'skipped',
648
+ reason: `read-error: ${err.message}`,
649
+ file: filePath,
650
+ line: null,
651
+ classification: null,
652
+ });
653
+ continue;
654
+ }
655
+
656
+ totalScanned++;
657
+ // Two independent drift classes, each discovered per-line:
658
+ // - OLD_SEGMENT username drift → rewriteContent (split+join)
659
+ // - missing /Bernhard/ segment → rewriteMissingSegment (#600 D3)
660
+ const hits = findHits(filePath, content);
661
+ const missingHits = findMissingSegmentHits(filePath, content);
662
+ if (hits.length === 0 && missingHits.length === 0) continue;
663
+
664
+ // Decide per-file: if every hit is historical → skip the whole file.
665
+ // Otherwise, rewrite the whole file (transforms are global) — historical hits
666
+ // in mixed files are rare; if any exist, classify each line and skip-count.
667
+ const fixableHits = hits.filter((h) => h.classification !== 'historical');
668
+ const historicalHits = hits.filter((h) => h.classification === 'historical');
669
+ const fixableMissing = missingHits.filter((h) => h.classification !== 'historical');
670
+ const historicalMissing = missingHits.filter((h) => h.classification === 'historical');
671
+
672
+ for (const h of [...historicalHits, ...historicalMissing]) {
673
+ totalHistoricalSkipped++;
674
+ emit(opts, {
675
+ action: 'skipped',
676
+ reason: 'historical',
677
+ file: filePath,
678
+ line: h.line,
679
+ classification: h.classification,
680
+ text: truncate(h.text),
681
+ });
682
+ }
683
+
684
+ if (fixableHits.length === 0 && fixableMissing.length === 0) continue;
685
+
686
+ // Defense-in-depth: if the entire file is in a historical context, skip
687
+ // rewriting even if any hit slipped past per-line classification.
688
+ if (isHistorical(filePath)) continue;
689
+
690
+ // Chain both transforms. rewriteContent (username) runs first; the
691
+ // missing-segment pass is then gated on the ORIGINAL content so it never
692
+ // touches a line the username rewrite already owns (see rewriteMissingSegment).
693
+ let newContent = content;
694
+ if (fixableHits.length > 0) newContent = rewriteContent(newContent);
695
+ if (fixableMissing.length > 0) newContent = rewriteMissingSegment(newContent, content);
696
+
697
+ for (const h of fixableHits) {
698
+ totalLinesFixed++;
699
+ emit(opts, {
700
+ action: opts.apply ? 'fixed' : 'would-fix',
701
+ reason: null,
702
+ file: filePath,
703
+ line: h.line,
704
+ classification: h.classification,
705
+ text: truncate(h.text),
706
+ });
707
+ }
708
+ for (const h of fixableMissing) {
709
+ totalLinesFixed++;
710
+ emit(opts, {
711
+ action: opts.apply ? `${MISSING_SEGMENT_CLASS}-fixed` : `${MISSING_SEGMENT_CLASS}-would-fix`,
712
+ reason: null,
713
+ file: filePath,
714
+ line: h.line,
715
+ classification: h.classification,
716
+ text: truncate(h.text),
717
+ });
718
+ }
719
+
720
+ if (opts.apply) {
721
+ try {
722
+ await atomicWrite(filePath, newContent);
723
+ } catch (err) {
724
+ totalPermissionErrors++;
725
+ process.stderr.write(
726
+ `migrate-vault-paths: ERROR failed to write ${filePath}: ${err.message}\n`,
727
+ );
728
+ // Continue with next file rather than aborting the whole run.
729
+ }
730
+ }
731
+ }
732
+
733
+ const modeLabel = opts.apply ? 'applied' : 'dry-run';
734
+ process.stderr.write(
735
+ `migrate-vault-paths: ${totalScanned} files scanned, ${totalLinesFixed} lines fixed, ` +
736
+ `${totalHistoricalSkipped} historical skipped` +
737
+ (totalPermissionErrors > 0 ? `, ${totalPermissionErrors} I/O errors` : '') +
738
+ ` [${modeLabel}]\n`,
739
+ );
740
+
741
+ process.exit(totalPermissionErrors > 0 ? 2 : 0);
742
+ }
743
+
744
+ function truncate(s) {
745
+ if (typeof s !== 'string') return s;
746
+ return s.length > 200 ? s.slice(0, 197) + '...' : s;
747
+ }
748
+
749
+ /**
750
+ * Emit a single output record. JSON mode prints one JSONL object; text mode
751
+ * prints the canonical human line: "<abbrev>:<line>: <class> → <action>".
752
+ */
753
+ function emit(opts, rec) {
754
+ if (opts.json) {
755
+ process.stdout.write(JSON.stringify(rec) + '\n');
756
+ return;
757
+ }
758
+ const where = `${abbrev(rec.file)}${rec.line ? ':' + rec.line : ''}`;
759
+ const cls = rec.classification ?? rec.reason ?? '?';
760
+ process.stdout.write(`migrate-vault-paths: ${where}: ${cls} → ${rec.action}\n`);
761
+ }
762
+
763
+ // ---------------------------------------------------------------------------
764
+ // Entry guard + exports (architect MED #607 D3)
765
+ //
766
+ // Run main() only when invoked directly as a CLI (node scripts/migrate-vault-paths.mjs).
767
+ // When imported by a test (or another module), the pure helpers below are
768
+ // available without firing the one-shot migration as a side effect.
769
+ //
770
+ // `process.argv[1]` is the invoked script path under `node <script>`, but it is
771
+ // `undefined` under `node -e "import(...)"` and some dynamic-import contexts —
772
+ // pathToFileURL(undefined) throws ERR_INVALID_ARG_TYPE. Guard the falsy case so
773
+ // importing the module is never a hard error regardless of how the importer runs.
774
+ // ---------------------------------------------------------------------------
775
+
776
+ const invokedDirectly =
777
+ process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href;
778
+
779
+ if (invokedDirectly) {
780
+ main().catch((err) => {
781
+ process.stderr.write(`migrate-vault-paths: FATAL: ${err.message}\n`);
782
+ process.exit(2);
783
+ });
784
+ }
785
+
786
+ export {
787
+ rewriteMissingSegment,
788
+ rewriteContent,
789
+ lineHasMissingSegment,
790
+ findMissingSegmentHits,
791
+ isHistorical,
792
+ classifyHit,
793
+ isOwnedByUsernamePath,
794
+ MISSING_SEGMENT_CLASS,
795
+ _setSegmentsForTest,
796
+ };