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,367 @@
1
+ /**
2
+ * render-sessions.mjs — Session markdown generators for vault-mirror (Issue #283 split).
3
+ *
4
+ * Exports: detectSessionSchema, normalizeSessionEntry, generateSessionNote, generateSessionNoteV2, generateSessionNoteV3
5
+ *
6
+ * #732: all three generators accept `options.repoNs` (the leak-guarded /
7
+ * pseudonym-mapped namespace segment from resolveRepoNamespace(), threaded
8
+ * through by process.mjs::processSession) and emit it as a `source-repo:`
9
+ * frontmatter line — mirroring render-learnings.mjs's `source-repo` field.
10
+ * Prior to #732, session notes emitted a raw `repo:` field sourced directly
11
+ * from deriveRepo() (bypassing the leak-guard entirely, even though the write
12
+ * PATH already used resolveRepoNamespace()). Historical notes on disk may still
13
+ * carry the legacy `repo:` key — the vault frontmatter Zod schema tolerates it
14
+ * via `.passthrough()`, and it is NOT rewritten retroactively by this change.
15
+ */
16
+
17
+ import { toDate, buildTag, slugifyIdSafe } from './utils.mjs';
18
+
19
+ const GENERATOR_MARKER = 'session-orchestrator-vault-mirror@1';
20
+
21
+ /**
22
+ * Frontmatter line emitter — skips emission when value is null/undefined/empty
23
+ * to avoid template-literal coercion bugs (e.g. `platform: undefined` → "undefined").
24
+ * Returns the formatted line with trailing newline, or empty string to skip.
25
+ */
26
+ function fmLine(key, value) {
27
+ if (value === null || value === undefined || value === '') return '';
28
+ return `${key}: ${value}\n`;
29
+ }
30
+
31
+ /**
32
+ * Session JSONL has three producer schemas in production:
33
+ * v1 (legacy): total_waves, total_agents, total_files_changed, agent_summary, waves[{agent_count, files_changed, quality}]
34
+ * v2 (S69+): files_changed (top-level), waves[{agents, agents_done, agents_partial, agents_failed, dispatch, duration_s}]
35
+ * v3 (2026-05+): coordinator-direct records — `waves` is a SCALAR count and
36
+ * `agents_dispatched` is a scalar; no per-wave array breakdown.
37
+ * This is the shape session-end actually emits (#491), which
38
+ * previously fell through to v1 validation and was rejected as
39
+ * `skipped-invalid` (no vault session-note was ever written).
40
+ * v1 and v2 both carry `waves` as an ARRAY, so a numeric `waves` is the
41
+ * unambiguous v3 discriminator.
42
+ */
43
+ export function detectSessionSchema(entry) {
44
+ if (!entry) return 'v1';
45
+ if (typeof entry.waves === 'number') return 'v3';
46
+ return entry.total_agents === undefined && entry.files_changed !== undefined ? 'v2' : 'v1';
47
+ }
48
+
49
+ /**
50
+ * Map known producer alias fields onto the canonical session shapes (#635).
51
+ *
52
+ * Several session-end variants emitted alias fields the validators reject:
53
+ * `ended_at` (for completed_at), `mode` (for session_type), and wave counts
54
+ * only as `total_waves`/`waves_completed` with no `waves` field at all. Such
55
+ * records fell through to v1 validation and were skipped-invalid. This pure
56
+ * function fills MISSING canonical fields from their aliases so the entry
57
+ * routes to the v3 (scalar-waves) renderer. Canonical v1/v2/v3 entries pass
58
+ * through untouched (`waves` is only filled when absent — an existing array
59
+ * or number is never modified).
60
+ *
61
+ * Returns a new object; never mutates the input.
62
+ */
63
+ export function normalizeSessionEntry(entry) {
64
+ if (!entry || typeof entry !== 'object') return entry;
65
+ const e = { ...entry };
66
+
67
+ if (e.completed_at === null || e.completed_at === undefined) {
68
+ if (e.ended_at !== null && e.ended_at !== undefined) e.completed_at = e.ended_at;
69
+ }
70
+ if (e.session_type === null || e.session_type === undefined) {
71
+ if (typeof e.mode === 'string' && e.mode.length > 0) e.session_type = e.mode;
72
+ }
73
+ if (e.waves === null || e.waves === undefined) {
74
+ if (typeof e.total_waves === 'number') e.waves = e.total_waves;
75
+ else if (typeof e.waves_completed === 'number') e.waves = e.waves_completed;
76
+ else e.waves = 0; // coordinator-direct record with no wave breakdown
77
+ }
78
+
79
+ return e;
80
+ }
81
+
82
+ export function generateSessionNote(entry, options = {}) {
83
+ const REQUIRED_SESSION_FIELDS = ['session_id', 'session_type', 'started_at', 'completed_at', 'total_waves', 'total_agents', 'total_files_changed', 'agent_summary', 'waves', 'effectiveness'];
84
+ for (const field of REQUIRED_SESSION_FIELDS) {
85
+ if (entry[field] === null || entry[field] === undefined) {
86
+ throw new Error(`vault-mirror: session entry missing required field '${field}' (session_id=${entry.session_id ?? '<no session_id>'})`);
87
+ }
88
+ }
89
+
90
+ const {
91
+ session_id,
92
+ session_type,
93
+ platform,
94
+ started_at,
95
+ completed_at,
96
+ duration_seconds,
97
+ total_waves,
98
+ total_agents,
99
+ total_files_changed,
100
+ agent_summary,
101
+ waves,
102
+ effectiveness,
103
+ } = entry;
104
+
105
+ if (typeof effectiveness !== 'object' || effectiveness === null) {
106
+ throw new Error(`vault-mirror: session entry missing nested field 'effectiveness' (session_id=${session_id})`);
107
+ }
108
+ if (typeof agent_summary !== 'object' || agent_summary === null) {
109
+ throw new Error(`vault-mirror: session entry missing nested field 'agent_summary' (session_id=${session_id})`);
110
+ }
111
+ if (!Array.isArray(waves)) {
112
+ throw new Error(`vault-mirror: session entry missing nested field 'waves' (session_id=${session_id})`);
113
+ }
114
+
115
+ const created = toDate(started_at);
116
+ const updated = toDate(completed_at);
117
+ const durationMin = Math.round((duration_seconds ?? 0) / 60);
118
+ const { planned_issues, completed, carryover, emergent, completion_rate } = effectiveness;
119
+ const ratePercent = Math.round(completion_rate * 100) + '%';
120
+ const { complete, partial, failed, spiral } = agent_summary;
121
+
122
+ const titleValue = `Session ${created} — ${session_type}`;
123
+ // Always quote session title — it contains em-dash
124
+ const title = `"${titleValue}"`;
125
+
126
+ // #602: session_type is interpolated raw upstream; slugify each tag segment
127
+ // and cap the combined tag at 64 chars so the frontmatter passes the vault
128
+ // tagPathRegex. The `id` is likewise slugified to a kebab slug below —
129
+ // entry.session_id may carry an ISO-timestamp uppercase `T`/`:`/`.`/`Z`.
130
+ const noteId = slugifyIdSafe(session_id) ?? session_id;
131
+ const tags = `[${buildTag(['session', session_type])}, ${buildTag(['status', 'verified'])}]`;
132
+
133
+ // Build wave table rows
134
+ const waveRows = waves
135
+ .map(
136
+ (w) =>
137
+ `| ${w.wave} | ${w.role} | ${w.agent_count} | ${w.files_changed} | ${w.quality} |`,
138
+ )
139
+ .join('\n');
140
+
141
+ // Skip-emit guard: avoid `platform: undefined` literal coercion (issue #343).
142
+ const platformBullet = (platform === null || platform === undefined || platform === '')
143
+ ? ''
144
+ : ` · **Platform:** ${platform}`;
145
+
146
+ // #732: emit `source-repo` (the leak-guarded/pseudonym-mapped namespace from
147
+ // resolveRepoNamespace(), threaded through by process.mjs) instead of the
148
+ // legacy raw `repo` field — mirrors render-learnings.mjs's source-repo line.
149
+ const { repoNs } = options;
150
+
151
+ return `---
152
+ id: ${noteId}
153
+ type: session
154
+ title: ${title}
155
+ status: verified
156
+ created: ${created}
157
+ updated: ${updated}
158
+ tags: ${tags}
159
+ ${fmLine('source-repo', repoNs)}_generator: ${GENERATOR_MARKER}
160
+ ---
161
+
162
+ # Session ${session_id}
163
+
164
+ - **Type:** ${session_type}${platformBullet}
165
+ - **Duration:** ${durationMin}m (${started_at} → ${completed_at})
166
+ - **Waves:** ${total_waves} · **Agents:** ${total_agents} · **Files changed:** ${total_files_changed}
167
+ - **Effectiveness:** planned=${planned_issues}, completed=${completed}, carryover=${carryover}, emergent=${emergent}, rate=${ratePercent}
168
+
169
+ ## Wave breakdown
170
+
171
+ | Wave | Role | Agents | Files | Quality |
172
+ |------|------|--------|-------|---------|
173
+ ${waveRows}
174
+
175
+ ## Agent summary
176
+
177
+ - Complete: ${complete} · Partial: ${partial} · Failed: ${failed} · Spiral: ${spiral}
178
+ `;
179
+ }
180
+
181
+ export function generateSessionNoteV2(entry, options = {}) {
182
+ const REQUIRED_SESSION_V2_FIELDS = ['session_id', 'session_type', 'started_at', 'completed_at', 'waves', 'files_changed', 'effectiveness'];
183
+ for (const field of REQUIRED_SESSION_V2_FIELDS) {
184
+ if (entry[field] === null || entry[field] === undefined) {
185
+ throw new Error(`vault-mirror: session entry missing required field '${field}' (session_id=${entry.session_id ?? '<no session_id>'})`);
186
+ }
187
+ }
188
+ if (!Array.isArray(entry.waves)) {
189
+ throw new Error(`vault-mirror: session entry 'waves' must be an array (session_id=${entry.session_id})`);
190
+ }
191
+ if (typeof entry.effectiveness !== 'object' || entry.effectiveness === null) {
192
+ throw new Error(`vault-mirror: session entry missing nested field 'effectiveness' (session_id=${entry.session_id})`);
193
+ }
194
+
195
+ const { session_id, session_type, started_at, completed_at, duration_seconds, branch, planned_issues, waves, files_changed, issues_closed, issues_created, effectiveness, notes } = entry;
196
+
197
+ const created = toDate(started_at);
198
+ const updated = toDate(completed_at);
199
+ const durationMin = Math.round((duration_seconds ?? 0) / 60);
200
+
201
+ // Derive v1-equivalent aggregates from v2 wave structure
202
+ const totalWaves = waves.length;
203
+ const totalAgents = waves.reduce((acc, w) => acc + (w.agents ?? 0), 0);
204
+ const complete = waves.reduce((acc, w) => acc + (w.agents_done ?? 0), 0);
205
+ const partial = waves.reduce((acc, w) => acc + (w.agents_partial ?? 0), 0);
206
+ const failed = waves.reduce((acc, w) => acc + (w.agents_failed ?? 0), 0);
207
+
208
+ const completionRate = effectiveness.completion_rate;
209
+ const ratePercent = typeof completionRate === 'number' ? Math.round(completionRate * 100) + '%' : 'n/a';
210
+ const carryover = effectiveness.carryover ?? 'n/a';
211
+
212
+ const titleValue = `Session ${created} — ${session_type}`;
213
+ const title = `"${titleValue}"`;
214
+
215
+ // #602: session_type is interpolated raw upstream; slugify each tag segment
216
+ // and cap the combined tag at 64 chars so the frontmatter passes the vault
217
+ // tagPathRegex. The `id` is likewise slugified to a kebab slug below —
218
+ // entry.session_id may carry an ISO-timestamp uppercase `T`/`:`/`.`/`Z`.
219
+ const noteId = slugifyIdSafe(session_id) ?? session_id;
220
+ const tags = `[${buildTag(['session', session_type])}, ${buildTag(['status', 'verified'])}]`;
221
+
222
+ const waveRows = waves
223
+ .map((w) => `| ${w.wave} | ${w.role} | ${w.agents ?? '?'} | ${w.dispatch ?? '?'} | ${w.duration_s ?? '?'}s | ${w.agents_done ?? 0}/${w.agents_partial ?? 0}/${w.agents_failed ?? 0} |`)
224
+ .join('\n');
225
+
226
+ const closedList = Array.isArray(issues_closed) && issues_closed.length ? issues_closed.join(', ') : '—';
227
+ const createdList = Array.isArray(issues_created) && issues_created.length ? issues_created.join(', ') : '—';
228
+ const branchLine = branch ? ` · **Branch:** ${branch}` : '';
229
+ const notesBlock = notes ? `\n## Notes\n\n${notes}\n` : '';
230
+
231
+ // #732: emit `source-repo` (leak-guarded/pseudonym-mapped) instead of the
232
+ // legacy raw `repo` field — see generateSessionNote for the full rationale.
233
+ const { repoNs } = options;
234
+
235
+ return `---
236
+ id: ${noteId}
237
+ type: session
238
+ title: ${title}
239
+ status: verified
240
+ created: ${created}
241
+ updated: ${updated}
242
+ tags: ${tags}
243
+ ${fmLine('source-repo', repoNs)}_generator: ${GENERATOR_MARKER}
244
+ ---
245
+
246
+ # Session ${session_id}
247
+
248
+ - **Type:** ${session_type}${branchLine}
249
+ - **Duration:** ${durationMin}m (${started_at} → ${completed_at})
250
+ - **Waves:** ${totalWaves} · **Agents:** ${totalAgents} · **Files changed:** ${files_changed}
251
+ - **Effectiveness:** planned=${planned_issues ?? 'n/a'}, carryover=${carryover}, rate=${ratePercent}
252
+ - **Issues closed:** ${closedList}
253
+ - **Issues created:** ${createdList}
254
+
255
+ ## Wave breakdown
256
+
257
+ | Wave | Role | Agents | Dispatch | Duration | done/partial/failed |
258
+ |------|------|--------|----------|----------|---------------------|
259
+ ${waveRows}
260
+
261
+ ## Agent summary
262
+
263
+ - Complete: ${complete} · Partial: ${partial} · Failed: ${failed}
264
+ ${notesBlock}`;
265
+ }
266
+
267
+ /**
268
+ * v3 generator — coordinator-direct session records (#491).
269
+ *
270
+ * These records carry `waves` as a scalar count and `agents_dispatched` as a
271
+ * scalar (no per-wave array), plus rich top-level metadata (issues_closed,
272
+ * follow_ups_filed, commits, tests_total_*). They are what session-end actually
273
+ * writes to sessions.jsonl. The v1/v2 generators require `waves` to be an array
274
+ * and reject this shape, so it gets its own renderer.
275
+ */
276
+ export function generateSessionNoteV3(entry, options = {}) {
277
+ const REQUIRED_SESSION_V3_FIELDS = ['session_id', 'session_type', 'started_at', 'completed_at', 'waves', 'effectiveness'];
278
+ for (const field of REQUIRED_SESSION_V3_FIELDS) {
279
+ if (entry[field] === null || entry[field] === undefined) {
280
+ throw new Error(`vault-mirror: session entry missing required field '${field}' (session_id=${entry.session_id ?? '<no session_id>'})`);
281
+ }
282
+ }
283
+ if (typeof entry.waves !== 'number') {
284
+ throw new Error(`vault-mirror: session entry 'waves' must be a number (session_id=${entry.session_id})`);
285
+ }
286
+ if (typeof entry.effectiveness !== 'object' || entry.effectiveness === null) {
287
+ throw new Error(`vault-mirror: session entry missing nested field 'effectiveness' (session_id=${entry.session_id})`);
288
+ }
289
+
290
+ const {
291
+ session_id, session_type, platform, branch, started_at, completed_at,
292
+ duration_minutes, duration_seconds, waves, agents_dispatched, agent_summary,
293
+ planned_issues, effectiveness, commits, issues_closed, issues_created,
294
+ follow_ups_filed, tests_total_pre, tests_total_post, tests_added, notes,
295
+ } = entry;
296
+
297
+ const created = toDate(started_at);
298
+ const updated = toDate(completed_at);
299
+ const durationMin = duration_minutes ?? Math.round((duration_seconds ?? 0) / 60);
300
+
301
+ const completionRate = effectiveness.completion_rate;
302
+ const ratePercent = typeof completionRate === 'number' ? Math.round(completionRate * 100) + '%' : 'n/a';
303
+ const completed = effectiveness.completed_issues ?? effectiveness.completed ?? 'n/a';
304
+ const carryover = effectiveness.carryover ?? 'n/a';
305
+ const emergent = effectiveness.unplanned_finds ?? effectiveness.emergent ?? 0;
306
+
307
+ const agentsValue = typeof agents_dispatched === 'number' ? agents_dispatched : 'n/a';
308
+ const as = agent_summary && typeof agent_summary === 'object' ? agent_summary : {};
309
+ const { complete = 0, partial = 0, failed = 0, spiral = 0 } = as;
310
+
311
+ const fmtIssues = (list) =>
312
+ Array.isArray(list) && list.length ? list.map((i) => `#${i}`).join(', ') : '—';
313
+ const closedList = fmtIssues(issues_closed);
314
+ const createdList = fmtIssues(issues_created);
315
+ const followList = fmtIssues(follow_ups_filed);
316
+ const commitCount = Array.isArray(commits) ? commits.length : 0;
317
+ const testsDelta =
318
+ typeof tests_total_pre === 'number' && typeof tests_total_post === 'number'
319
+ ? `${tests_total_pre} → ${tests_total_post}`
320
+ : typeof tests_added === 'number'
321
+ ? `+${tests_added}`
322
+ : '—';
323
+
324
+ const titleValue = `Session ${created} — ${session_type}`;
325
+ const title = `"${titleValue}"`;
326
+ // #602: session_type is interpolated raw upstream; slugify each tag segment
327
+ // and cap the combined tag at 64 chars so the frontmatter passes the vault
328
+ // tagPathRegex. The `id` is likewise slugified to a kebab slug below —
329
+ // entry.session_id may carry an ISO-timestamp uppercase `T`/`:`/`.`/`Z`.
330
+ const noteId = slugifyIdSafe(session_id) ?? session_id;
331
+ const tags = `[${buildTag(['session', session_type])}, ${buildTag(['status', 'verified'])}]`;
332
+
333
+ const platformBullet = platform === null || platform === undefined || platform === '' ? '' : ` · **Platform:** ${platform}`;
334
+ const branchLine = branch ? ` · **Branch:** ${branch}` : '';
335
+ const notesBlock = notes ? `\n## Notes\n\n${notes}\n` : '';
336
+
337
+ // #732: emit `source-repo` (leak-guarded/pseudonym-mapped) instead of the
338
+ // legacy raw `repo` field — see generateSessionNote for the full rationale.
339
+ const { repoNs } = options;
340
+
341
+ return `---
342
+ id: ${noteId}
343
+ type: session
344
+ title: ${title}
345
+ status: verified
346
+ created: ${created}
347
+ updated: ${updated}
348
+ tags: ${tags}
349
+ ${fmLine('source-repo', repoNs)}_generator: ${GENERATOR_MARKER}
350
+ ---
351
+
352
+ # Session ${session_id}
353
+
354
+ - **Type:** ${session_type}${branchLine}${platformBullet}
355
+ - **Duration:** ${durationMin}m (${started_at} → ${completed_at})
356
+ - **Waves:** ${waves} · **Agents:** ${agentsValue} · **Commits:** ${commitCount}
357
+ - **Effectiveness:** planned=${planned_issues ?? 'n/a'}, completed=${completed}, carryover=${carryover}, emergent=${emergent}, rate=${ratePercent}
358
+ - **Tests:** ${testsDelta}
359
+ - **Issues closed:** ${closedList}
360
+ - **Issues created:** ${createdList}
361
+ - **Follow-ups filed:** ${followList}
362
+
363
+ ## Agent summary
364
+
365
+ - Complete: ${complete} · Partial: ${partial} · Failed: ${failed} · Spiral: ${spiral}
366
+ ${notesBlock}`;
367
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * render.mjs — Re-export barrel for vault-mirror render modules (Issue #283 split).
3
+ *
4
+ * Consumers can import from this file or directly from render-learnings.mjs / render-sessions.mjs.
5
+ */
6
+
7
+ export { detectLearningSchema, generateLearningNote, generateLearningNoteV2 } from './render-learnings.mjs';
8
+ export { detectSessionSchema, generateSessionNote, generateSessionNoteV2, generateSessionNoteV3 } from './render-sessions.mjs';
@@ -0,0 +1,217 @@
1
+ /**
2
+ * utils.mjs — Shared utilities for vault-mirror (Issue #283 split).
3
+ *
4
+ * Exports: subjectToSlug, isValidSlug, uuidPrefix8, toDate,
5
+ * truncateAtWord, yamlQuoteIfNeeded, parseFrontmatter,
6
+ * slugifyTagSegment, buildTag, slugifyIdSafe, TAG_MAX_LENGTH,
7
+ * resolveSourceSessionLink
8
+ */
9
+
10
+ import { parseSessionId } from '../session-id.mjs';
11
+
12
+ /**
13
+ * Convert a subject string to a kebab slug.
14
+ * - If subject contains slashes, collapse to last path segment.
15
+ * - Replace dots and underscores with hyphens.
16
+ * - Strip all non-[a-z0-9-] chars.
17
+ * - Collapse consecutive hyphens.
18
+ * - Trim leading/trailing hyphens.
19
+ */
20
+ export function subjectToSlug(subject) {
21
+ let s = subject;
22
+
23
+ // Collapse slash paths to last segment
24
+ if (s.includes('/')) {
25
+ const parts = s.split('/').filter(Boolean);
26
+ s = parts[parts.length - 1];
27
+ }
28
+
29
+ // Normalise: lowercase, dots/underscores → hyphens, strip invalid chars
30
+ s = s
31
+ .toLowerCase()
32
+ .replace(/[._]/g, '-')
33
+ .replace(/[^a-z0-9-]/g, '')
34
+ .replace(/-+/g, '-')
35
+ .replace(/^-+|-+$/g, '');
36
+
37
+ return s;
38
+ }
39
+
40
+ const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
41
+
42
+ /**
43
+ * Validate that `s` is a filesystem-safe kebab slug.
44
+ *
45
+ * Guards against RegExp's implicit ToString coercion (issue #718): without
46
+ * the `typeof` check, `slugRegex.test(undefined)` coerces to the literal
47
+ * string "undefined" — which matches `^[a-z0-9]+(?:-[a-z0-9]+)*$` — and
48
+ * `isValidSlug(undefined)` returns `true`. Callers (e.g. `processSession` in
49
+ * process.mjs) rely on `isValidSlug(rawSessionId) === false` to route
50
+ * missing/non-string ids to the uuid-based synthesizer; the coercion trap
51
+ * let `undefined` through as a "valid" id and crashed downstream on
52
+ * `session_id.length`.
53
+ *
54
+ * @param {unknown} s
55
+ * @returns {boolean}
56
+ */
57
+ export function isValidSlug(s) {
58
+ return typeof s === 'string' && slugRegex.test(s);
59
+ }
60
+
61
+ /**
62
+ * Max length of a single vault tag string. Mirrors the authoritative
63
+ * vaultFrontmatterSchema (`tags[].max(64)` in skills/vault-sync/validator.mjs).
64
+ */
65
+ export const TAG_MAX_LENGTH = 64;
66
+
67
+ /**
68
+ * Slugify ONE tag segment to a kebab slug matching the schema's per-segment
69
+ * regex (`/^[a-z0-9]+(?:-[a-z0-9]+)*$/`).
70
+ *
71
+ * Unlike {@link subjectToSlug}, this does NOT collapse slashes — callers pass a
72
+ * single segment at a time so {@link buildTag} can preserve the `/`-separated
73
+ * tag hierarchy. Whitespace, dots and underscores become hyphens (so
74
+ * "Deep Mode" → "deep-mode"); all other non-`[a-z0-9-]` chars are stripped. An
75
+ * empty result falls back to "unknown" so the enclosing tag never contains an
76
+ * empty `/`-segment (which would violate tagPathRegex).
77
+ *
78
+ * @param {string} input
79
+ * @returns {string}
80
+ */
81
+ export function slugifyTagSegment(input) {
82
+ const slug = String(input ?? '')
83
+ .toLowerCase()
84
+ .replace(/[\s._]+/g, '-')
85
+ .replace(/[^a-z0-9-]/g, '')
86
+ .replace(/-+/g, '-')
87
+ .replace(/^-+|-+$/g, '');
88
+ return slug || 'unknown';
89
+ }
90
+
91
+ /**
92
+ * Build a schema-valid vault tag from path segments (#602). Each segment is
93
+ * kebab-slugified (preserving the `/`-separated hierarchy), and the joined tag
94
+ * is capped at {@link TAG_MAX_LENGTH} characters. When truncation lands on a
95
+ * separator or hyphen, the trailing char is removed so the result still matches
96
+ * the schema's tagPathRegex.
97
+ *
98
+ * @param {Array<string>|string} segments
99
+ * @returns {string}
100
+ */
101
+ export function buildTag(segments) {
102
+ const slugged = (Array.isArray(segments) ? segments : [segments]).map((s) =>
103
+ slugifyTagSegment(s),
104
+ );
105
+ if (slugged.length === 0) return 'unknown';
106
+ let tag = slugged.join('/');
107
+ if (tag.length > TAG_MAX_LENGTH) {
108
+ // Trim to the cap, then strip any trailing separator/hyphen exposed by the
109
+ // cut so the tail segment stays a valid kebab slug.
110
+ tag = tag.slice(0, TAG_MAX_LENGTH).replace(/[/-]+$/, '');
111
+ }
112
+ return tag;
113
+ }
114
+
115
+ /**
116
+ * Slugify a value for use as a vault note `id` (#602), guaranteeing the
117
+ * schema's kebab + min-length-2 contract. Returns null when no usable slug
118
+ * remains (caller treats this as an invalid entry).
119
+ *
120
+ * @param {string} input
121
+ * @returns {string|null}
122
+ */
123
+ export function slugifyIdSafe(input) {
124
+ // Normalise whitespace to hyphens first (so "My ID" → "my-id" rather than
125
+ // "myid"), then delegate to subjectToSlug for the rest of the kebab contract.
126
+ const normalised = String(input ?? '').replace(/\s+/g, '-');
127
+ const slug = subjectToSlug(normalised);
128
+ if (!slug) return null;
129
+ // Schema requires min length 2; pad a single-char slug deterministically.
130
+ return slug.length >= 2 ? slug : `${slug}-x`;
131
+ }
132
+
133
+ /** Derive the first 8 chars of a UUID (strip hyphens, take first 8 hex chars). */
134
+ export function uuidPrefix8(id) {
135
+ return id.replace(/-/g, '').slice(0, 8);
136
+ }
137
+
138
+ /** Format a UTC ISO date string as YYYY-MM-DD. */
139
+ export function toDate(isoString) {
140
+ if (!isoString) return '';
141
+ return isoString.slice(0, 10);
142
+ }
143
+
144
+ /** Truncate a string to maxLen chars, ending at a word boundary. */
145
+ export function truncateAtWord(str, maxLen) {
146
+ if (str.length <= maxLen) return str;
147
+ const truncated = str.slice(0, maxLen);
148
+ const lastSpace = truncated.lastIndexOf(' ');
149
+ return lastSpace > 0 ? truncated.slice(0, lastSpace) : truncated;
150
+ }
151
+
152
+ /** Determine if a YAML title value needs quoting (contains : # \ or starts with -). */
153
+ export function yamlQuoteIfNeeded(value) {
154
+ if (/[:#{}[\],&*?|<>=!%@`\\]/.test(value) || value.startsWith('-') || value.startsWith('"')) {
155
+ return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
156
+ }
157
+ return value;
158
+ }
159
+
160
+ // ── Source-session link resolver (Issue #704) ─────────────────────────────────
161
+
162
+ /**
163
+ * Decide whether a learning's source_session should be emitted as an Obsidian
164
+ * wiki-link or as plain text.
165
+ *
166
+ * When `opts.noteExists` is provided (a function `(slug: string) => boolean`),
167
+ * the decision is EXISTENCE-based: the target slug is looked up in the caller's
168
+ * known-sessions Set built from the real vault's 50-sessions directory. This is
169
+ * the authoritative path used by process.mjs and eliminates false-negatives for
170
+ * legacy/HHmm session IDs whose vault notes exist but don't match the strict
171
+ * semantic regex (e.g. `main-2026-04-23-1255`, `main-2026-05-25-deep`).
172
+ *
173
+ * Without the predicate (unit-test / vault-less run), the function falls back to
174
+ * strict format-validity (semantic `<branch>-<YYYY-MM-DD>-<mode>-<N>` or UUID-v4)
175
+ * — identical to the Wave-2 behaviour, never worse.
176
+ *
177
+ * @param {unknown} source_session
178
+ * @param {{ noteExists?: (slug: string) => boolean }} [opts]
179
+ * @returns {{ isLink: boolean, target: string }}
180
+ */
181
+ export function resolveSourceSessionLink(source_session, opts = {}) {
182
+ const raw = source_session === null || source_session === undefined ? '' : String(source_session).trim();
183
+ if (!raw || raw === 'unknown') return { isLink: false, target: raw || 'unknown' };
184
+ // Provenance tags like "agent-proposed@wave-1" are never session notes.
185
+ if (/@/.test(raw)) return { isLink: false, target: raw };
186
+ const slug = subjectToSlug(raw) || raw;
187
+ if (typeof opts.noteExists === 'function') {
188
+ // EXISTENCE-based (authoritative when a predicate is supplied).
189
+ return opts.noteExists(slug) ? { isLink: true, target: slug } : { isLink: false, target: raw };
190
+ }
191
+ // Fallback (no predicate, e.g. unit tests / no vault): strict format validity.
192
+ const parsed = parseSessionId(raw);
193
+ const ok = parsed !== null && (parsed.format === 'semantic' || parsed.format === 'uuid');
194
+ return ok ? { isLink: true, target: slug } : { isLink: false, target: raw };
195
+ }
196
+
197
+ // ── Frontmatter parser (minimal — only reads the opening --- block) ───────────
198
+
199
+ export function parseFrontmatter(content) {
200
+ if (!content.startsWith('---')) return null;
201
+ const end = content.indexOf('\n---', 3);
202
+ if (end === -1) return null;
203
+ const block = content.slice(3, end).trim();
204
+ const result = {};
205
+ for (const line of block.split('\n')) {
206
+ const colonIdx = line.indexOf(':');
207
+ if (colonIdx === -1) continue;
208
+ const key = line.slice(0, colonIdx).trim();
209
+ let value = line.slice(colonIdx + 1).trim();
210
+ // Strip surrounding quotes
211
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
212
+ value = value.slice(1, -1);
213
+ }
214
+ result[key] = value;
215
+ }
216
+ return result;
217
+ }