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,577 @@
1
+ /**
2
+ * catalog-loader.mjs — Load + validate persona specs for /persona-panel (#457).
3
+ *
4
+ * Discovers `.claude/personas/*.md` files, parses their YAML frontmatter under
5
+ * the explicit js-yaml CORE_SCHEMA (W1-D4 L1 — disables YAML 1.1 type
6
+ * extensions such as `!!js/function` / `!!js/regexp`), validates them against
7
+ * the persona-spec rules, and returns a Map keyed by persona `name`.
8
+ *
9
+ * Module-level pure functions; mirrors triage-state.mjs shape. Filesystem I/O
10
+ * is concentrated inside `loadCatalog` / `loadPersona` — every other exported
11
+ * function is pure and side-effect-free.
12
+ *
13
+ * ── Security guards baked in ─────────────────────────────────────────────────
14
+ *
15
+ * H1 (path traversal):
16
+ * - Personas root is resolved relative to the project root and pinned via
17
+ * `validatePathInsideProject` before any read.
18
+ * - File names are matched against a strict glob `<safe-name>.md` derived
19
+ * from `SAFE_PERSONA_NAME_RE`.
20
+ * - Symlinks are rejected at read time via `lstat` (L3).
21
+ *
22
+ * H2 (model allowlist):
23
+ * - `model` field validated via ALLOWED_MODEL_ALIASES ∪ MODEL_ID_RE imported
24
+ * directly from `agent-frontmatter.mjs` — single source of truth.
25
+ *
26
+ * H3 (output_contract structural pre-check):
27
+ * - `preCheckOutputContract` rejects `$ref`, `$defs`, `definitions`, `allOf`,
28
+ * `anyOf`, `oneOf`, `not`, and unknown root keys. Operator-authored
29
+ * schemas must be plain object schemas; combinators and refs are
30
+ * attack-surface for untrusted YAML.
31
+ *
32
+ * L1: explicit CORE_SCHEMA.
33
+ * L3: symlink rejection.
34
+ * L4: additionalProperties:false-equivalent — unknown frontmatter keys fail
35
+ * loading.
36
+ *
37
+ * Duplicate `name` across two persona files → DuplicateNameError with both
38
+ * source paths (last-writer-wins would silently shadow whichever file the
39
+ * filesystem enumeration returned second).
40
+ */
41
+
42
+ import { readFile, readdir, lstat } from 'node:fs/promises';
43
+ import path from 'node:path';
44
+ import yaml from 'js-yaml';
45
+
46
+ import { findProjectRoot } from '../common.mjs';
47
+ import { validatePathInsideProject } from '../path-utils.mjs';
48
+ import { ALLOWED_MODEL_ALIASES, MODEL_ID_RE } from '../agent-frontmatter.mjs';
49
+
50
+ // ---------------------------------------------------------------------------
51
+ // Persona-name and frontmatter constants
52
+ // ---------------------------------------------------------------------------
53
+
54
+ /**
55
+ * Strict persona-name allowlist. Mirrors the file-name regex used to
56
+ * enumerate persona files — every persona MUST satisfy both, eliminating
57
+ * any path-traversal vector via `--personas` arguments. (W1-D4 H1.)
58
+ */
59
+ export const SAFE_PERSONA_NAME_RE = /^[a-z0-9-]{1,64}$/;
60
+
61
+ /** Allowed tier values. */
62
+ const ALLOWED_TIERS = new Set(['domain-expert', 'buyer-persona', 'auditor', 'compliance', 'reviewer', 'custom']);
63
+
64
+ /** Required frontmatter keys (W1-D4 L4 — `additionalProperties:false` semantics). */
65
+ const REQUIRED_KEYS = new Set([
66
+ 'name',
67
+ 'schema_version',
68
+ 'version',
69
+ 'role',
70
+ 'model',
71
+ 'output_contract',
72
+ 'evaluation_criteria',
73
+ 'tier',
74
+ ]);
75
+
76
+ /**
77
+ * Optional frontmatter keys — keys NOT in REQUIRED_KEYS and NOT in
78
+ * OPTIONAL_KEYS cause the persona to be rejected.
79
+ */
80
+ const OPTIONAL_KEYS = new Set([
81
+ 'description',
82
+ 'color',
83
+ 'tags',
84
+ 'enabled',
85
+ 'sandbox-tier',
86
+ ]);
87
+
88
+ /** Forbidden top-level keys in `output_contract`. (W1-D4 H3.) */
89
+ const FORBIDDEN_SCHEMA_KEYS = new Set([
90
+ '$ref',
91
+ '$defs',
92
+ 'definitions',
93
+ 'allOf',
94
+ 'anyOf',
95
+ 'oneOf',
96
+ 'not',
97
+ ]);
98
+
99
+ /** Per-criterion length cap (operator-authored prompt-injection guard). */
100
+ const MAX_CRITERION_CHARS = 512;
101
+
102
+ /**
103
+ * Default repo-relative directory containing persona spec files.
104
+ * Override via `opts.personasDir` for tests or alternative layouts.
105
+ */
106
+ export const DEFAULT_PERSONAS_DIR = '.claude/personas';
107
+
108
+ /**
109
+ * Thrown when two persona files declare the same `name`. The error message
110
+ * lists both source paths so the operator can resolve the conflict directly.
111
+ */
112
+ export class DuplicateNameError extends Error {
113
+ constructor(name, firstPath, secondPath) {
114
+ super(
115
+ `Duplicate persona name "${name}" found in both:\n - ${firstPath}\n - ${secondPath}`,
116
+ );
117
+ this.name = 'DuplicateNameError';
118
+ this.duplicateName = name;
119
+ this.paths = [firstPath, secondPath];
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Thrown when `loadPersona(name)` is called with a name not present in the
125
+ * catalog. Lists the available names so callers can correct the spelling.
126
+ */
127
+ export class PersonaNotFoundError extends Error {
128
+ constructor(name, available) {
129
+ super(
130
+ `Persona "${name}" not found. Available: ${available.length === 0 ? '(none)' : available.join(', ')}`,
131
+ );
132
+ this.name = 'PersonaNotFoundError';
133
+ this.requestedName = name;
134
+ this.available = available;
135
+ }
136
+ }
137
+
138
+ // ---------------------------------------------------------------------------
139
+ // preCheckOutputContract
140
+ // ---------------------------------------------------------------------------
141
+
142
+ /**
143
+ * Structural pre-check for the operator-authored `output_contract` schema
144
+ * (W1-D4 H3). AJV is still run by persona-runner.mjs::validatePersonaOutput
145
+ * for the actual payload check; this pre-check fails fast on dangerous
146
+ * shapes BEFORE AJV is exposed to them.
147
+ *
148
+ * Rejected shapes:
149
+ * - Anything that is not a plain object (string, array, null, function, …)
150
+ * - Top-level `$ref`, `$defs`, `definitions`, `allOf`, `anyOf`, `oneOf`, `not`
151
+ * (these enable indirection that compounds the schema-DoS attack surface)
152
+ *
153
+ * Allowed: simple object schemas with `type`, `properties`, `required`,
154
+ * `additionalProperties`, `enum`, `items`, `minimum`, `maximum`,
155
+ * `minLength`, `maxLength`, `pattern`, `format`, etc.
156
+ *
157
+ * @param {unknown} schema
158
+ * @returns {{ok: true} | {ok: false, errors: string[]}}
159
+ */
160
+ export function preCheckOutputContract(schema) {
161
+ if (schema === null || typeof schema !== 'object' || Array.isArray(schema)) {
162
+ return {
163
+ ok: false,
164
+ errors: ['output_contract must be a JSON object'],
165
+ };
166
+ }
167
+
168
+ const errors = [];
169
+ for (const key of Object.keys(schema)) {
170
+ if (FORBIDDEN_SCHEMA_KEYS.has(key)) {
171
+ errors.push(
172
+ `output_contract.${key} is not permitted (schema combinators and refs are blocked for safety)`,
173
+ );
174
+ }
175
+ }
176
+ if (errors.length > 0) return { ok: false, errors };
177
+ return { ok: true };
178
+ }
179
+
180
+ // ---------------------------------------------------------------------------
181
+ // validatePersonaSpec
182
+ // ---------------------------------------------------------------------------
183
+
184
+ /**
185
+ * Validate one parsed persona-spec object.
186
+ *
187
+ * Pure function: no I/O. Use after `yaml.load` to validate the frontmatter
188
+ * shape, model allowlist, evaluation-criteria array, and output_contract
189
+ * structural shape.
190
+ *
191
+ * @param {unknown} rawSpec — result of yaml.load on the frontmatter block
192
+ * @param {string} sourcePath — absolute path of the persona file (for errors)
193
+ * @returns {{ok: true, persona: object} | {ok: false, errors: Array<{path:string, rule:string, message:string}>}}
194
+ */
195
+ export function validatePersonaSpec(rawSpec, sourcePath) {
196
+ const errors = [];
197
+ const pushError = (p, rule, message) => {
198
+ errors.push({ path: p, rule, message: `${message} (in ${sourcePath})` });
199
+ };
200
+
201
+ if (rawSpec === null || typeof rawSpec !== 'object' || Array.isArray(rawSpec)) {
202
+ return {
203
+ ok: false,
204
+ errors: [{
205
+ path: '$',
206
+ rule: 'shape',
207
+ message: `persona frontmatter must be a YAML mapping at the top level (in ${sourcePath})`,
208
+ }],
209
+ };
210
+ }
211
+
212
+ // Required-keys check (W1-D4 L4).
213
+ for (const req of REQUIRED_KEYS) {
214
+ if (!(req in rawSpec)) {
215
+ pushError(req, 'required', `${req} is required`);
216
+ }
217
+ }
218
+
219
+ // Unknown-keys check (additionalProperties:false equivalent).
220
+ for (const key of Object.keys(rawSpec)) {
221
+ if (!REQUIRED_KEYS.has(key) && !OPTIONAL_KEYS.has(key)) {
222
+ pushError(key, 'unknown-key', `unknown frontmatter key "${key}" (additionalProperties is closed)`);
223
+ }
224
+ }
225
+
226
+ // name
227
+ const name = rawSpec.name;
228
+ if (name !== undefined) {
229
+ if (typeof name !== 'string') {
230
+ pushError('name', 'type', 'name must be a string');
231
+ } else if (!SAFE_PERSONA_NAME_RE.test(name)) {
232
+ pushError('name', 'format', `name must match ${SAFE_PERSONA_NAME_RE} (got ${JSON.stringify(name)})`);
233
+ }
234
+ }
235
+
236
+ // schema_version (must equal 1)
237
+ if ('schema_version' in rawSpec && rawSpec.schema_version !== 1) {
238
+ pushError('schema_version', 'enum', `schema_version must equal 1 (got ${JSON.stringify(rawSpec.schema_version)})`);
239
+ }
240
+
241
+ // version (string, non-empty)
242
+ if ('version' in rawSpec) {
243
+ if (typeof rawSpec.version !== 'string' || rawSpec.version.trim() === '') {
244
+ pushError('version', 'type', 'version must be a non-empty string');
245
+ }
246
+ }
247
+
248
+ // role (string, non-empty)
249
+ if ('role' in rawSpec) {
250
+ if (typeof rawSpec.role !== 'string' || rawSpec.role.trim() === '') {
251
+ pushError('role', 'type', 'role must be a non-empty string');
252
+ }
253
+ }
254
+
255
+ // model (allowlist — H2)
256
+ if ('model' in rawSpec) {
257
+ const m = rawSpec.model;
258
+ if (typeof m !== 'string' || m === '') {
259
+ pushError('model', 'type', 'model must be a non-empty string');
260
+ } else if (!ALLOWED_MODEL_ALIASES.has(m) && !MODEL_ID_RE.test(m)) {
261
+ pushError(
262
+ 'model',
263
+ 'enum',
264
+ `model must be one of ${[...ALLOWED_MODEL_ALIASES].join('|')} or a full model ID like "claude-opus-4-7" (got ${JSON.stringify(m)})`,
265
+ );
266
+ }
267
+ }
268
+
269
+ // tier (enum)
270
+ if ('tier' in rawSpec) {
271
+ if (typeof rawSpec.tier !== 'string' || !ALLOWED_TIERS.has(rawSpec.tier)) {
272
+ pushError(
273
+ 'tier',
274
+ 'enum',
275
+ `tier must be one of ${[...ALLOWED_TIERS].join('|')} (got ${JSON.stringify(rawSpec.tier)})`,
276
+ );
277
+ }
278
+ }
279
+
280
+ // evaluation_criteria — non-empty array of short strings
281
+ if ('evaluation_criteria' in rawSpec) {
282
+ const crit = rawSpec.evaluation_criteria;
283
+ if (!Array.isArray(crit) || crit.length === 0) {
284
+ pushError('evaluation_criteria', 'type', 'evaluation_criteria must be a non-empty array');
285
+ } else {
286
+ crit.forEach((c, i) => {
287
+ if (typeof c !== 'string' || c.length === 0) {
288
+ pushError(`evaluation_criteria[${i}]`, 'type', 'each criterion must be a non-empty string');
289
+ } else if (c.length > MAX_CRITERION_CHARS) {
290
+ pushError(
291
+ `evaluation_criteria[${i}]`,
292
+ 'maxLength',
293
+ `criterion exceeds ${MAX_CRITERION_CHARS} chars`,
294
+ );
295
+ }
296
+ });
297
+ }
298
+ }
299
+
300
+ // output_contract — structural pre-check (H3)
301
+ if ('output_contract' in rawSpec) {
302
+ const pre = preCheckOutputContract(rawSpec.output_contract);
303
+ if (!pre.ok) {
304
+ for (const e of pre.errors) {
305
+ pushError('output_contract', 'shape', e);
306
+ }
307
+ }
308
+ }
309
+
310
+ if (errors.length > 0) return { ok: false, errors };
311
+ return { ok: true, persona: /** @type {object} */ (rawSpec) };
312
+ }
313
+
314
+ // ---------------------------------------------------------------------------
315
+ // Internal helpers
316
+ // ---------------------------------------------------------------------------
317
+
318
+ /** Match the YAML frontmatter block at the head of a markdown file. */
319
+ const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
320
+
321
+ /**
322
+ * Strip and parse the YAML frontmatter block from a markdown file's contents.
323
+ *
324
+ * Uses `yaml.CORE_SCHEMA` explicitly (W1-D4 L1) — disables YAML 1.1 type
325
+ * extensions like `!!js/function` and `!!js/regexp`.
326
+ *
327
+ * @param {string} contents
328
+ * @param {string} sourcePath
329
+ * @returns {{ok: true, frontmatter: object, body: string} | {ok: false, error: string}}
330
+ */
331
+ function parseFrontmatterBlock(contents, sourcePath) {
332
+ const match = FRONTMATTER_RE.exec(contents);
333
+ if (match === null) {
334
+ return {
335
+ ok: false,
336
+ error: `${sourcePath}: missing YAML frontmatter (file must begin with --- … ---)`,
337
+ };
338
+ }
339
+
340
+ const [, fmText, body] = match;
341
+ let parsed;
342
+ try {
343
+ parsed = yaml.load(fmText, { schema: yaml.CORE_SCHEMA });
344
+ } catch (err) {
345
+ // yaml.load throws YAMLException with `.mark.line` / `.mark.column`.
346
+ const where = err?.mark
347
+ ? `line ${err.mark.line + 1}, column ${err.mark.column + 1}`
348
+ : 'unknown location';
349
+ return {
350
+ ok: false,
351
+ error: `${sourcePath}: YAML parse error at ${where}: ${err.message ?? err}`,
352
+ };
353
+ }
354
+
355
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
356
+ return {
357
+ ok: false,
358
+ error: `${sourcePath}: frontmatter must be a YAML mapping (got ${Array.isArray(parsed) ? 'array' : typeof parsed})`,
359
+ };
360
+ }
361
+
362
+ return { ok: true, frontmatter: parsed, body: body.startsWith('\n') ? body.slice(1) : body };
363
+ }
364
+
365
+ /**
366
+ * Read one persona file, rejecting symlinks (L3) and enforcing the safe-name
367
+ * pattern at the file level. Returns the parsed-and-validated persona OR a
368
+ * structured error.
369
+ *
370
+ * @param {string} absPath — already-validated absolute path under personasRoot
371
+ * @returns {Promise<{ok: true, persona: object, frontmatter: object, body: string, sourcePath: string}
372
+ * | {ok: false, errors: Array<{path:string, rule:string, message:string}>, sourcePath: string}>}
373
+ */
374
+ async function loadOnePersonaFile(absPath) {
375
+ // L3 — symlink rejection.
376
+ let stat;
377
+ try {
378
+ stat = await lstat(absPath);
379
+ } catch (err) {
380
+ return {
381
+ ok: false,
382
+ sourcePath: absPath,
383
+ errors: [{ path: '$', rule: 'read-error', message: `lstat failed: ${err.message ?? err}` }],
384
+ };
385
+ }
386
+ if (stat.isSymbolicLink()) {
387
+ return {
388
+ ok: false,
389
+ sourcePath: absPath,
390
+ errors: [{ path: '$', rule: 'symlink', message: 'persona files must not be symbolic links' }],
391
+ };
392
+ }
393
+ if (!stat.isFile()) {
394
+ return {
395
+ ok: false,
396
+ sourcePath: absPath,
397
+ errors: [{ path: '$', rule: 'type', message: 'persona path must be a regular file' }],
398
+ };
399
+ }
400
+
401
+ let raw;
402
+ try {
403
+ raw = await readFile(absPath, 'utf8');
404
+ } catch (err) {
405
+ return {
406
+ ok: false,
407
+ sourcePath: absPath,
408
+ errors: [{ path: '$', rule: 'read-error', message: `read failed: ${err.message ?? err}` }],
409
+ };
410
+ }
411
+
412
+ const fm = parseFrontmatterBlock(raw, absPath);
413
+ if (!fm.ok) {
414
+ return {
415
+ ok: false,
416
+ sourcePath: absPath,
417
+ errors: [{ path: '$', rule: 'yaml-parse', message: fm.error }],
418
+ };
419
+ }
420
+
421
+ const validation = validatePersonaSpec(fm.frontmatter, absPath);
422
+ if (!validation.ok) {
423
+ return { ok: false, sourcePath: absPath, errors: validation.errors };
424
+ }
425
+
426
+ return {
427
+ ok: true,
428
+ persona: validation.persona,
429
+ frontmatter: fm.frontmatter,
430
+ body: fm.body,
431
+ sourcePath: absPath,
432
+ };
433
+ }
434
+
435
+ /**
436
+ * Resolve + validate the personas directory.
437
+ *
438
+ * @param {{personasDir?: string, projectRoot?: string}} opts
439
+ * @returns {{personasRoot: string, projectRoot: string}}
440
+ * @throws {Error} when the resolved path escapes the project root
441
+ */
442
+ function resolvePersonasRoot(opts) {
443
+ const projectRoot = opts.projectRoot ?? findProjectRoot();
444
+ const rel = opts.personasDir ?? DEFAULT_PERSONAS_DIR;
445
+ const guard = validatePathInsideProject(rel, projectRoot);
446
+ if (!guard.ok) {
447
+ throw new Error(
448
+ `personas directory ${JSON.stringify(rel)} is not inside the project root (reason=${guard.reason}${guard.error ? `: ${guard.error}` : ''})`,
449
+ );
450
+ }
451
+ return { personasRoot: guard.lexicalPath, projectRoot };
452
+ }
453
+
454
+ // ---------------------------------------------------------------------------
455
+ // loadCatalog
456
+ // ---------------------------------------------------------------------------
457
+
458
+ /**
459
+ * Load every persona under `<projectRoot>/<personasDir>/*.md`.
460
+ *
461
+ * Failure modes (in priority order):
462
+ * (a) Personas directory does not exist → throw with helpful message
463
+ * (b) Directory exists but contains no `*.md` → returns empty Map (NOT an error;
464
+ * caller decides whether empty is allowed)
465
+ * (c) A persona has unrecoverable validation errors → throw with all errors aggregated
466
+ * (d) Two personas share `name` → throw DuplicateNameError
467
+ *
468
+ * @param {{personasDir?: string, projectRoot?: string}} [opts]
469
+ * @returns {Promise<Map<string, {persona: object, frontmatter: object, body: string, sourcePath: string}>>}
470
+ */
471
+ export async function loadCatalog(opts = {}) {
472
+ const { personasRoot } = resolvePersonasRoot(opts);
473
+
474
+ let entries;
475
+ try {
476
+ entries = await readdir(personasRoot, { withFileTypes: true });
477
+ } catch (err) {
478
+ if (err && err.code === 'ENOENT') {
479
+ throw new Error(
480
+ `personas directory not found: ${personasRoot} (create .claude/personas/ or pass --personas-dir)`,
481
+ { cause: err },
482
+ );
483
+ }
484
+ throw err;
485
+ }
486
+
487
+ // Filter to `<safe-name>.md` files only — any other entry (dotfile,
488
+ // subdirectory, weird extension) is silently skipped to keep the directory
489
+ // forward-compatible with future tooling (README.md, fixtures/, etc.).
490
+ const candidates = entries
491
+ .filter((e) => e.isFile())
492
+ .map((e) => e.name)
493
+ .filter((n) => n.endsWith('.md'))
494
+ .filter((n) => {
495
+ const base = n.slice(0, -'.md'.length);
496
+ return SAFE_PERSONA_NAME_RE.test(base);
497
+ })
498
+ .sort(); // deterministic enumeration order
499
+
500
+ const map = new Map();
501
+ const validationErrors = [];
502
+
503
+ for (const fname of candidates) {
504
+ // Re-validate the per-file path inside the personas root — defence-in-depth
505
+ // against a personasRoot that somehow contains a traversal seam.
506
+ const guard = validatePathInsideProject(fname, personasRoot);
507
+ if (!guard.ok) {
508
+ validationErrors.push({
509
+ sourcePath: path.join(personasRoot, fname),
510
+ errors: [{ path: '$', rule: 'path-traversal', message: `entry rejected (reason=${guard.reason})` }],
511
+ });
512
+ continue;
513
+ }
514
+
515
+ const result = await loadOnePersonaFile(guard.realPath ?? guard.lexicalPath);
516
+ if (!result.ok) {
517
+ validationErrors.push({ sourcePath: result.sourcePath, errors: result.errors });
518
+ continue;
519
+ }
520
+
521
+ const name = result.persona.name;
522
+ if (map.has(name)) {
523
+ const existing = map.get(name);
524
+ throw new DuplicateNameError(name, existing.sourcePath, result.sourcePath);
525
+ }
526
+ map.set(name, {
527
+ persona: result.persona,
528
+ frontmatter: result.frontmatter,
529
+ body: result.body,
530
+ sourcePath: result.sourcePath,
531
+ });
532
+ }
533
+
534
+ if (validationErrors.length > 0) {
535
+ const lines = validationErrors.flatMap(({ sourcePath, errors }) =>
536
+ errors.map((e) => ` ${sourcePath} :: ${e.path}: ${e.message}`),
537
+ );
538
+ throw new Error(`persona validation failed:\n${lines.join('\n')}`);
539
+ }
540
+
541
+ return map;
542
+ }
543
+
544
+ // ---------------------------------------------------------------------------
545
+ // loadPersona
546
+ // ---------------------------------------------------------------------------
547
+
548
+ /**
549
+ * Load one persona by `name`. Used by the `--personas <comma,list>` CLI arg.
550
+ *
551
+ * Validates `name` against `SAFE_PERSONA_NAME_RE` before any filesystem
552
+ * lookup, so a malicious operator can't smuggle traversal sequences through
553
+ * the argument.
554
+ *
555
+ * @param {string} name
556
+ * @param {{personasDir?: string, projectRoot?: string}} [opts]
557
+ * @returns {Promise<{persona: object, frontmatter: object, body: string, sourcePath: string}>}
558
+ * @throws {TypeError} if name is not a string
559
+ * @throws {Error} if name fails SAFE_PERSONA_NAME_RE
560
+ * @throws {PersonaNotFoundError} if name is not in the catalog
561
+ */
562
+ export async function loadPersona(name, opts = {}) {
563
+ if (typeof name !== 'string') {
564
+ throw new TypeError(`loadPersona: name must be a string (got ${typeof name})`);
565
+ }
566
+ if (!SAFE_PERSONA_NAME_RE.test(name)) {
567
+ throw new Error(
568
+ `loadPersona: name ${JSON.stringify(name)} fails ${SAFE_PERSONA_NAME_RE} — refusing to read`,
569
+ );
570
+ }
571
+ const catalog = await loadCatalog(opts);
572
+ const hit = catalog.get(name);
573
+ if (hit === undefined) {
574
+ throw new PersonaNotFoundError(name, [...catalog.keys()].sort());
575
+ }
576
+ return hit;
577
+ }