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,95 @@
1
+ /**
2
+ * resource-probe.mjs — live RAM/CPU/process snapshot for adaptive session planning.
3
+ *
4
+ * Probes current host load at session-start (and optionally inside wave planning)
5
+ * so `agents-per-wave` and Docker-usage decisions can adapt to real resource
6
+ * pressure rather than static config defaults.
7
+ *
8
+ * Part of v3.1.0 Epic #157, Sub-Epic #158 (A+B). Issue #163.
9
+ * Extended in v3.2 Phase C-2 (#296): swap_used_mb + memory_pressure_pct_free signals.
10
+ *
11
+ * Snapshot shape:
12
+ * {
13
+ * timestamp: '2026-04-19T11:00:00Z',
14
+ * ram_free_gb: 3.2,
15
+ * ram_used_pct: 78,
16
+ * cpu_load_1m: 2.4,
17
+ * cpu_load_pct: 65,
18
+ * claude_processes_count: 3 | null,
19
+ * codex_processes_count: 0 | null,
20
+ * other_node_processes: 12 | null,
21
+ * zombie_processes_count: 1 | null,
22
+ * swap_used_mb: 512 | null,
23
+ * memory_pressure_pct_free: 42 | null,
24
+ * ram_available_gb: 82.5 | null, // macOS: free + reclaimable (vm_stat); null elsewhere (#667)
25
+ * probe_duration_ms: 45,
26
+ * }
27
+ *
28
+ * process-count fields fall back to `null` when the process-list command fails
29
+ * (e.g. sandboxed environments). Consumers treat `null` as "unknown".
30
+ *
31
+ * swap_used_mb: null on Windows/unknown or on spawn/parse failure.
32
+ * memory_pressure_pct_free: null on Linux/Windows/unknown or on failure.
33
+ *
34
+ * Implementation split in #287 (hotspot 2/2):
35
+ * - scripts/lib/resource-probe/parsers.mjs — pure output parsers
36
+ * - scripts/lib/resource-probe/probe-platform.mjs — platform sampling I/O
37
+ * - scripts/lib/resource-probe/evaluate.mjs — verdict + threshold logic
38
+ */
39
+
40
+ import { ramSnapshot, cpuSnapshot, processCounts, swapUsedMb, memoryPressurePctFree, ramAvailableGb } from './resource-probe/probe-platform.mjs';
41
+
42
+ // ---------------------------------------------------------------------------
43
+ // Re-exports — preserve public API for all existing callers
44
+ // ---------------------------------------------------------------------------
45
+
46
+ export { evaluate } from './resource-probe/evaluate.mjs';
47
+ export { parseEtimeToMinutes, countZombieProcesses, countProcessMatches, parseSwapUsageOutput, parseMemoryPressureOutput, parseVmStatAvailableGb } from './resource-probe/parsers.mjs';
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // Public API — probe()
51
+ // ---------------------------------------------------------------------------
52
+
53
+ /**
54
+ * Capture a live resource snapshot of the current host.
55
+ * @param {object} [opts]
56
+ * @param {boolean} [opts.skipProcessCounts] — skip process listing (faster in tests)
57
+ * @param {boolean} [opts.skipExtendedSignals] — skip swap + memory_pressure calls (faster in tests)
58
+ * @param {number|null} [opts.zombieThresholdMin] — when non-null, detect zombie Claude/Node
59
+ * processes older than this many minutes with low CPU. Default null (feature disabled).
60
+ * @returns {Promise<object>}
61
+ */
62
+ export async function probe(opts = {}) {
63
+ const start = Date.now();
64
+ const ram = ramSnapshot();
65
+ const cpu = cpuSnapshot();
66
+ const zombieThresholdMin = opts.zombieThresholdMin ?? null;
67
+ const procs = opts.skipProcessCounts
68
+ ? { claude_processes_count: null, codex_processes_count: null, other_node_processes: null, zombie_processes_count: null }
69
+ : await processCounts(zombieThresholdMin);
70
+
71
+ let swap_used_mb = null;
72
+ let memory_pressure_pct_free = null;
73
+ let ram_available_gb = null;
74
+ if (!opts.skipExtendedSignals) {
75
+ [swap_used_mb, memory_pressure_pct_free, ram_available_gb] = await Promise.all([
76
+ swapUsedMb(),
77
+ memoryPressurePctFree(),
78
+ ramAvailableGb(),
79
+ ]);
80
+ }
81
+
82
+ const duration = Date.now() - start;
83
+ return {
84
+ timestamp: new Date().toISOString(),
85
+ ...ram,
86
+ ...cpu,
87
+ ...procs,
88
+ swap_used_mb,
89
+ memory_pressure_pct_free,
90
+ // macOS-only numeric available-RAM (free + reclaimable) from vm_stat; null
91
+ // on Linux/Windows/unknown where os.freemem() is already accurate (#667).
92
+ ram_available_gb,
93
+ probe_duration_ms: duration,
94
+ };
95
+ }
@@ -0,0 +1,552 @@
1
+ /**
2
+ * Rule Loader — issue #336 glob-scoped rules + #694 rule-activation foundation
3
+ * + #795 `paths:` alias.
4
+ *
5
+ * Reads `.claude/rules/*.md` files, parses optional YAML frontmatter, and
6
+ * returns the subset of rules applicable to a given set of file paths and
7
+ * activation axes.
8
+ *
9
+ * Rules with no `globs:`/`paths:` frontmatter are always-on (loaded for every
10
+ * wave). Rules with `globs:` (or the `paths:` alias — issue #795) load only
11
+ * when at least one `scopePath` matches at least one glob pattern.
12
+ *
13
+ * `paths:` is a same-shape alias for `globs:` (issue #795) — some repos use a
14
+ * `paths:` frontmatter convention instead of `globs:` (e.g. projects-baseline:
15
+ * 26 rule files, all `paths:`, 0 `globs:`). Before #795 these repos'
16
+ * path-scoped rules were silently misclassified as always-on, inflating the
17
+ * `instruction-budget-guard.mjs` (#687) always-on count with a false positive.
18
+ * `paths:` supports the identical inline-array and block-list forms as
19
+ * `globs:`. Precedence when BOTH keys are present on the same rule: `globs:`
20
+ * wins (silently — no merge, no warning) and `paths:` is ignored entirely.
21
+ *
22
+ * A rule file MAY carry a leading single-line provenance header before its
23
+ * frontmatter block — the vendoring pipeline (`scripts/rules-sync.mjs`)
24
+ * requires a first-line `<!-- source: session-orchestrator plugin
25
+ * (canonical: ...) -->` comment on every vendored rule. The frontmatter
26
+ * parser tolerates any leading run of blank lines and/or single-line HTML
27
+ * comments before the opening `---`, so a vendored rule keeps its `globs:`
28
+ * scoping and scalar meta instead of silently falling back to always-on.
29
+ * Only single-line comments are tolerated (no multi-line comment blocks);
30
+ * files with no header, or starting directly with `---`, parse exactly as
31
+ * before.
32
+ *
33
+ * Beyond the `globs:` key (issue #336), the frontmatter parser also captures
34
+ * these scalar activation keys (issue #694), surfaced on each RuleEntry:
35
+ * - `description` (string)
36
+ * - `mode` (string) — see mode-gating below
37
+ * - `host-class` (string) — see host-class-gating below; surfaced as `hostClass`
38
+ * - `alwaysApply` (boolean) — DISTINCT from the `alwaysOn` "no globs" flag
39
+ * - `expires-at` (string) — ISO date; see expiry gating below; surfaced as `expiresAt`
40
+ * - `learning-key` (string) — surfaced as `learningKey`
41
+ * - `auto-generated`(boolean) — surfaced as `autoGenerated`
42
+ * - `confidence` (number)
43
+ * Unknown keys are still ignored without error, and malformed frontmatter still
44
+ * falls back to always-on (a rule is never silently dropped).
45
+ *
46
+ * Deterministic gating (issue #694) runs after a successful frontmatter parse
47
+ * and applies to BOTH always-on and glob-matched candidate rules — a rule must
48
+ * pass ALL active gates to be included:
49
+ * - Expiry: a rule with a parseable `expires-at` strictly before `now` is
50
+ * EXCLUDED (with a mandatory stderr WARN). A malformed `expires-at` never
51
+ * excludes (fail-open).
52
+ * - Mode-gating: when the `mode` param is non-null and the rule declares a
53
+ * `mode` that differs, the rule is EXCLUDED. A null `mode` param disables
54
+ * mode filtering; a rule without a `mode` key always passes.
55
+ * - Host-class-gating: identical logic against the `hostClass` param vs the
56
+ * rule's `host-class` value.
57
+ *
58
+ * Parse errors fall back to always-on — a rule is never silently dropped.
59
+ *
60
+ * @module rule-loader
61
+ */
62
+
63
+ import { readFileSync, readdirSync } from 'node:fs';
64
+ import { join, extname } from 'node:path';
65
+ import { createRequire } from 'node:module';
66
+
67
+ // ---------------------------------------------------------------------------
68
+ // Picomatch integration (available in the project's node_modules).
69
+ // We resolve it dynamically so the module stays loadable in environments where
70
+ // picomatch is absent — the inline fallback glob-to-RegExp takes over then.
71
+ // ---------------------------------------------------------------------------
72
+
73
+ let _picomatch = null;
74
+
75
+ function getPicomatch() {
76
+ if (_picomatch !== null) return _picomatch;
77
+ try {
78
+ const require = createRequire(import.meta.url);
79
+ _picomatch = require('picomatch');
80
+ } catch {
81
+ // picomatch not available — will use inline fallback
82
+ _picomatch = false;
83
+ }
84
+ return _picomatch;
85
+ }
86
+
87
+ /**
88
+ * Minimal glob-to-RegExp fallback used only when picomatch is absent.
89
+ * Handles `**`, `*`, and literal character matching.
90
+ *
91
+ * @param {string} pattern
92
+ * @returns {RegExp}
93
+ */
94
+ function globToRegExp(pattern) {
95
+ // Normalize Windows separators
96
+ const p = pattern.replace(/\\/g, '/');
97
+ let re = '';
98
+ let i = 0;
99
+ while (i < p.length) {
100
+ const c = p[i];
101
+ if (c === '*' && p[i + 1] === '*') {
102
+ // `**` — match zero or more path segments
103
+ re += '.*';
104
+ i += 2;
105
+ // Consume optional trailing separator
106
+ if (p[i] === '/') i++;
107
+ } else if (c === '*') {
108
+ // `*` — match any characters except separator
109
+ re += '[^/]*';
110
+ i++;
111
+ } else if (c === '?') {
112
+ re += '[^/]';
113
+ i++;
114
+ } else if (c === '.') {
115
+ re += '\\.';
116
+ i++;
117
+ } else {
118
+ // Escape regex-special characters
119
+ re += c.replace(/[$()+[\]^{|}]/g, '\\$&');
120
+ i++;
121
+ }
122
+ }
123
+ return new RegExp(`^${re}$`);
124
+ }
125
+
126
+ /**
127
+ * Returns true when `filePath` matches `globPattern` using picomatch (or the
128
+ * inline fallback when picomatch is absent).
129
+ *
130
+ * @param {string} filePath - path relative to repo root, forward-slash separated
131
+ * @param {string} globPattern
132
+ * @returns {boolean}
133
+ */
134
+ function matchGlob(filePath, globPattern) {
135
+ const pm = getPicomatch();
136
+ if (pm) {
137
+ return pm.isMatch(filePath, globPattern, { dot: true });
138
+ }
139
+ return globToRegExp(globPattern).test(filePath);
140
+ }
141
+
142
+ // ---------------------------------------------------------------------------
143
+ // Minimal YAML frontmatter parser (matches state-md.mjs style)
144
+ // ---------------------------------------------------------------------------
145
+
146
+ const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
147
+
148
+ // A single header line is either blank or a complete single-line HTML
149
+ // comment (`<!-- ... -->` on one line). Multi-line comment blocks are
150
+ // deliberately NOT supported — the vendoring convention is a single first
151
+ // line, and tolerating a few stacked comment lines (each matching this RE)
152
+ // is sufficient.
153
+ const HEADER_LINE_RE = /^[ \t]*(?:<!--.*-->)?[ \t]*$/;
154
+
155
+ /**
156
+ * Skips a leading run of blank lines and/or single-line HTML comments
157
+ * (`<!-- ... -->`) at the very start of a rule file's contents, so the
158
+ * frontmatter opener (`---`) can be found even when the vendoring pipeline's
159
+ * mandatory provenance header precedes it. Stops at the first line that is
160
+ * neither blank nor a complete single-line comment.
161
+ *
162
+ * @param {string} contents - raw file contents
163
+ * @returns {string} contents starting at the first non-header line (empty
164
+ * string when the entire content is header lines)
165
+ */
166
+ function stripLeadingProvenanceHeader(contents) {
167
+ const lines = contents.split(/\r?\n/);
168
+ let idx = 0;
169
+ while (idx < lines.length && HEADER_LINE_RE.test(lines[idx])) {
170
+ idx++;
171
+ }
172
+ return lines.slice(idx).join('\n');
173
+ }
174
+
175
+ // Scalar activation keys captured (issue #694 + #692). The `globs` key keeps
176
+ // its dedicated sequence/flow handling and is NOT routed through this table.
177
+ const SCALAR_META_KEYS = new Set([
178
+ 'description',
179
+ 'mode',
180
+ 'host-class',
181
+ 'alwaysApply',
182
+ 'expires-at',
183
+ 'learning-key',
184
+ 'auto-generated',
185
+ 'confidence',
186
+ 'tier',
187
+ ]);
188
+
189
+ /**
190
+ * Parses the YAML frontmatter block for the `globs:` field (issue #336),
191
+ * its `paths:` alias (issue #795), and the scalar activation keys (issue #694).
192
+ *
193
+ * Returns:
194
+ * - `{ globs: string[] | null, meta: object }` on success
195
+ * - throws `Error` on malformed frontmatter so the caller can fall back
196
+ *
197
+ * `globs` is `null` when no frontmatter, and neither `globs:` nor `paths:` is
198
+ * present (always-on). `paths:` is parsed with the identical inline-array and
199
+ * block-list forms as `globs:`; when BOTH keys are present on the same rule,
200
+ * `globs:` wins silently and `paths:` is discarded (no merge, no warning —
201
+ * see module doc). The returned shape is unchanged either way — callers never
202
+ * see which key produced the value. `meta` carries only the recognised scalar
203
+ * keys that were present, with type coercion applied:
204
+ * - `alwaysApply`, `auto-generated` → boolean ('true'/'false')
205
+ * - `confidence` → number (undefined when NaN)
206
+ * - all other recognised keys → quote-stripped strings
207
+ * Unknown keys are ignored without error.
208
+ *
209
+ * A leading run of blank lines and/or single-line HTML comments (e.g. the
210
+ * vendoring pipeline's provenance header) is tolerated before the opening
211
+ * `---` — see `stripLeadingProvenanceHeader`. Files with no such header parse
212
+ * identically to before this tolerance was added.
213
+ *
214
+ * @param {string} contents - raw file contents
215
+ * @returns {{ globs: string[] | null, meta: Record<string, unknown> }}
216
+ */
217
+ export function parseGlobsFrontmatter(contents) {
218
+ const match = FRONTMATTER_RE.exec(stripLeadingProvenanceHeader(contents));
219
+ if (!match) return { globs: null, meta: {} };
220
+
221
+ const fmText = match[1];
222
+ const lines = fmText.split(/\r?\n/);
223
+
224
+ let globsValue = null;
225
+ let pathsValue = null;
226
+ // Which sequence-value key ('globs' | 'paths') is currently accumulating
227
+ // block-style ` - value` lines, or null when not inside either block.
228
+ let activeSeqKey = null;
229
+ /** @type {Record<string, unknown>} */
230
+ const meta = {};
231
+
232
+ for (const line of lines) {
233
+ const rstripped = line.replace(/\s+$/, '');
234
+
235
+ // Skip blank lines and comments
236
+ if (rstripped === '' || /^\s*#/.test(rstripped)) {
237
+ // If inside a `globs:`/`paths:` block, a blank line ends the sequence
238
+ // only if the next non-blank line is at col 0 (a new key). We just keep
239
+ // activeSeqKey set until we hit a non-indented non-blank line.
240
+ continue;
241
+ }
242
+
243
+ if (activeSeqKey) {
244
+ // Inside a `globs:`/`paths:` block — expect ` - value` indented lines
245
+ const seqMatch = rstripped.match(/^(\s+)-\s+(.*)/);
246
+ if (seqMatch) {
247
+ const raw = seqMatch[2].trim().replace(/^["']|["']$/g, '');
248
+ if (activeSeqKey === 'globs') {
249
+ if (!Array.isArray(globsValue)) globsValue = [];
250
+ globsValue.push(raw);
251
+ } else {
252
+ if (!Array.isArray(pathsValue)) pathsValue = [];
253
+ pathsValue.push(raw);
254
+ }
255
+ continue;
256
+ }
257
+ // Non-indented line — end of the sequence block
258
+ activeSeqKey = null;
259
+ }
260
+
261
+ // Top-level key detection
262
+ if (/^\s/.test(rstripped)) {
263
+ // Indented but not in a known block — skip (another block's continuation)
264
+ continue;
265
+ }
266
+
267
+ const colonIdx = rstripped.indexOf(':');
268
+ if (colonIdx === -1) {
269
+ throw new Error(`Malformed frontmatter line: ${JSON.stringify(rstripped)}`);
270
+ }
271
+
272
+ const key = rstripped.slice(0, colonIdx).trim();
273
+ const valuePart = rstripped.slice(colonIdx + 1).trim();
274
+
275
+ if (key === 'globs' || key === 'paths') {
276
+ let parsedValue;
277
+ if (valuePart === '') {
278
+ // Block-style list follows
279
+ activeSeqKey = key;
280
+ parsedValue = [];
281
+ } else if (valuePart.startsWith('[') && valuePart.endsWith(']')) {
282
+ // Flow-style: globs: ["src/**", "lib/**"] (or paths: [...])
283
+ const inner = valuePart.slice(1, -1).trim();
284
+ parsedValue = inner === ''
285
+ ? []
286
+ : inner.split(',').map((s) => s.trim().replace(/^["']|["']$/g, ''));
287
+ } else {
288
+ // Single inline value (unusual but handle gracefully)
289
+ parsedValue = [valuePart.replace(/^["']|["']$/g, '')];
290
+ }
291
+ if (key === 'globs') globsValue = parsedValue;
292
+ else pathsValue = parsedValue;
293
+ } else if (SCALAR_META_KEYS.has(key)) {
294
+ // Scalar activation key (issue #694). Strip surrounding quotes, then
295
+ // apply per-key coercion. Empty values are skipped (key not present).
296
+ const stripped = valuePart.replace(/^["']|["']$/g, '');
297
+ if (stripped === '') continue;
298
+ if (key === 'alwaysApply' || key === 'auto-generated') {
299
+ if (stripped === 'true') meta[key] = true;
300
+ else if (stripped === 'false') meta[key] = false;
301
+ // Any other value: leave undefined (not a recognised boolean)
302
+ } else if (key === 'confidence') {
303
+ const n = Number(stripped);
304
+ if (!Number.isNaN(n)) meta[key] = n;
305
+ } else {
306
+ meta[key] = stripped;
307
+ }
308
+ }
309
+ // Ignore all other keys
310
+ }
311
+
312
+ // Precedence (issue #795): `globs:` wins silently when both keys are present.
313
+ const globs = globsValue !== null ? globsValue : pathsValue;
314
+
315
+ return { globs, meta };
316
+ }
317
+
318
+ /**
319
+ * Maps the raw (hyphenated) frontmatter meta keys onto the camelCase RuleEntry
320
+ * field names, including only keys that were actually present.
321
+ *
322
+ * @param {Record<string, unknown>} meta
323
+ * @returns {Record<string, unknown>}
324
+ */
325
+ function metaToEntryFields(meta) {
326
+ /** @type {Record<string, unknown>} */
327
+ const out = {};
328
+ if (Object.prototype.hasOwnProperty.call(meta, 'description')) out.description = meta.description;
329
+ if (Object.prototype.hasOwnProperty.call(meta, 'mode')) out.mode = meta.mode;
330
+ if (Object.prototype.hasOwnProperty.call(meta, 'host-class')) out.hostClass = meta['host-class'];
331
+ if (Object.prototype.hasOwnProperty.call(meta, 'alwaysApply')) out.alwaysApply = meta.alwaysApply;
332
+ if (Object.prototype.hasOwnProperty.call(meta, 'expires-at')) out.expiresAt = meta['expires-at'];
333
+ if (Object.prototype.hasOwnProperty.call(meta, 'learning-key')) out.learningKey = meta['learning-key'];
334
+ if (Object.prototype.hasOwnProperty.call(meta, 'auto-generated')) out.autoGenerated = meta['auto-generated'];
335
+ if (Object.prototype.hasOwnProperty.call(meta, 'confidence')) out.confidence = meta.confidence;
336
+ if (Object.prototype.hasOwnProperty.call(meta, 'tier')) out.tier = meta.tier;
337
+ return out;
338
+ }
339
+
340
+ /**
341
+ * Deterministic gating check (issue #694 + #692). Returns `{ excluded: boolean,
342
+ * reason?: string }`. Applied to BOTH always-on and glob-matched rules.
343
+ *
344
+ * @param {Record<string, unknown>} meta - raw parsed meta (hyphenated keys)
345
+ * @param {string} filePath - absolute path (for WARN messages)
346
+ * @param {string|null} mode
347
+ * @param {string|null} hostClass
348
+ * @param {number} now - epoch ms
349
+ * @param {string|null} [context] - 'wave' | 'coordinator' | null (null = no tier gating)
350
+ * @returns {{ excluded: boolean }}
351
+ */
352
+ function applyGates(meta, filePath, mode, hostClass, now, context = null) {
353
+ // Expiry gate — fail-open on a malformed `expires-at`.
354
+ if (Object.prototype.hasOwnProperty.call(meta, 'expires-at')) {
355
+ const rawExpiry = meta['expires-at'];
356
+ const ts = Date.parse(String(rawExpiry));
357
+ if (Number.isNaN(ts)) {
358
+ process.stderr.write(
359
+ `[rule-loader] Rule ${filePath} has unparseable expires-at ${JSON.stringify(rawExpiry)} — ignoring expiry\n`,
360
+ );
361
+ } else if (ts < now) {
362
+ process.stderr.write(
363
+ `[rule-loader] Rule ${filePath} expired at ${rawExpiry} — excluded\n`,
364
+ );
365
+ return { excluded: true };
366
+ }
367
+ }
368
+
369
+ // Mode gate — only active when a non-null `mode` param is supplied AND the
370
+ // rule declares a differing `mode`.
371
+ if (mode !== null && Object.prototype.hasOwnProperty.call(meta, 'mode') && meta.mode !== mode) {
372
+ return { excluded: true };
373
+ }
374
+
375
+ // Host-class gate — symmetric to the mode gate.
376
+ if (
377
+ hostClass !== null &&
378
+ Object.prototype.hasOwnProperty.call(meta, 'host-class') &&
379
+ meta['host-class'] !== hostClass
380
+ ) {
381
+ return { excluded: true };
382
+ }
383
+
384
+ // Tier gate (issue #692) — only active when context is non-null.
385
+ // BACKWARD-COMPAT: context === null → no tier gating whatsoever.
386
+ if (context !== null && Object.prototype.hasOwnProperty.call(meta, 'tier')) {
387
+ const tier = meta.tier;
388
+ if (context === 'wave' && tier === 'coordinator-only') {
389
+ return { excluded: true };
390
+ }
391
+ if (context === 'coordinator' && tier === 'wave-only') {
392
+ return { excluded: true };
393
+ }
394
+ }
395
+
396
+ return { excluded: false };
397
+ }
398
+
399
+ // ---------------------------------------------------------------------------
400
+ // Public API
401
+ // ---------------------------------------------------------------------------
402
+
403
+ /**
404
+ * @typedef {object} RuleEntry
405
+ * @property {string} path - absolute path to the rule file
406
+ * @property {string} content - raw file contents (byte-identical to disk)
407
+ * @property {boolean} alwaysOn - true when neither `globs:` nor its `paths:`
408
+ * alias (issue #795) frontmatter is present (UNCHANGED semantics; DISTINCT
409
+ * from the `alwaysApply` frontmatter key)
410
+ * @property {string[]} matchedGlobs - globs that matched; empty when alwaysOn
411
+ * @property {true} [_parseError] - present only on frontmatter parse-error entries
412
+ * @property {string} [description] - frontmatter `description` (issue #694), when present
413
+ * @property {string} [mode] - frontmatter `mode` (issue #694), when present
414
+ * @property {string} [hostClass] - frontmatter `host-class` (issue #694), when present
415
+ * @property {boolean} [alwaysApply] - frontmatter `alwaysApply` (issue #694), when present
416
+ * @property {string} [expiresAt] - frontmatter `expires-at` ISO date (issue #694), when present
417
+ * @property {string} [learningKey] - frontmatter `learning-key` (issue #694), when present
418
+ * @property {boolean} [autoGenerated] - frontmatter `auto-generated` (issue #694), when present
419
+ * @property {number} [confidence] - frontmatter `confidence` (issue #694), when present
420
+ * @property {string} [tier] - frontmatter `tier` (issue #692): 'coordinator-only' | 'wave-only' | 'always', when present
421
+ */
422
+
423
+ /**
424
+ * Loads rule files from `rulesDir` and returns those applicable to the given
425
+ * `scopePaths` and activation axes.
426
+ *
427
+ * Rules without `globs:` (or its `paths:` alias — issue #795) frontmatter are
428
+ * always included (alwaysOn: true). Rules with `globs:`/`paths:` are included
429
+ * only when at least one scopePath matches at least one glob pattern.
430
+ *
431
+ * After a successful frontmatter parse, deterministic gates (issue #694) are
432
+ * applied to BOTH always-on and glob-matched rules — a rule must pass ALL
433
+ * active gates (expiry + mode + host-class) to be included. The new params are
434
+ * strictly optional and default to "no gating", so the issue #336 two-key call
435
+ * shape stays 100% backward-compatible.
436
+ *
437
+ * On frontmatter parse error, the rule is treated as always-on and a warning
438
+ * is emitted to stderr — rules are never silently dropped. (Parse-error rules
439
+ * carry no meta, so they pass every gate.)
440
+ *
441
+ * @param {object} opts
442
+ * @param {string} opts.rulesDir - absolute path to the directory containing rule *.md files
443
+ * @param {string[]} [opts.scopePaths] - paths (relative to repo root) to match against globs
444
+ * @param {string|null} [opts.mode] - when non-null, exclude rules whose `mode` differs
445
+ * @param {string|null} [opts.hostClass] - when non-null, exclude rules whose `host-class` differs
446
+ * @param {number} [opts.now] - epoch ms used for expiry gating (injectable for tests)
447
+ * @param {string|null} [opts.context] - 'wave' | 'coordinator' | null; when non-null, applies
448
+ * tier gating: 'wave' excludes coordinator-only rules, 'coordinator' excludes wave-only rules.
449
+ * null (default) disables tier gating entirely — backward-compatible with all existing callers.
450
+ * @returns {RuleEntry[]}
451
+ */
452
+ export function loadApplicableRules({
453
+ rulesDir,
454
+ scopePaths = [],
455
+ mode = null,
456
+ hostClass = null,
457
+ now = Date.now(),
458
+ context = null,
459
+ }) {
460
+ /** @type {string[]} */
461
+ let entries;
462
+ try {
463
+ entries = readdirSync(rulesDir);
464
+ } catch (err) {
465
+ process.stderr.write(`[rule-loader] Cannot read rulesDir ${rulesDir}: ${err.message}\n`);
466
+ return [];
467
+ }
468
+
469
+ // Normalize scopePaths to forward slashes for consistent matching
470
+ const normalizedScope = scopePaths.map((p) => p.replace(/\\/g, '/'));
471
+
472
+ /** @type {RuleEntry[]} */
473
+ const results = [];
474
+
475
+ for (const entry of entries) {
476
+ if (extname(entry) !== '.md') continue;
477
+
478
+ const filePath = join(rulesDir, entry);
479
+ let content;
480
+
481
+ try {
482
+ content = readFileSync(filePath, 'utf8');
483
+ } catch (err) {
484
+ process.stderr.write(`[rule-loader] Cannot read ${filePath}: ${err.message}\n`);
485
+ continue;
486
+ }
487
+
488
+ let globs;
489
+ let meta;
490
+ let parseError = false;
491
+
492
+ try {
493
+ ({ globs, meta } = parseGlobsFrontmatter(content));
494
+ } catch (err) {
495
+ process.stderr.write(
496
+ `[rule-loader] Frontmatter parse error in ${filePath}: ${err.message} — treating as always-on\n`,
497
+ );
498
+ globs = null;
499
+ meta = {};
500
+ parseError = true;
501
+ }
502
+
503
+ // Deterministic gating (issue #694 + #692) — applies to BOTH always-on and
504
+ // glob-matched candidates. Parse-error rules carry empty meta, so they pass
505
+ // every gate (preserving fail-open behavior).
506
+ if (!parseError && applyGates(meta, filePath, mode, hostClass, now, context).excluded) {
507
+ continue;
508
+ }
509
+
510
+ // Camel-cased meta fields surfaced on the entry (only present keys).
511
+ const metaFields = metaToEntryFields(meta);
512
+
513
+ if (globs === null) {
514
+ // No globs frontmatter (or parse error) → always-on
515
+ results.push({
516
+ path: filePath,
517
+ content,
518
+ alwaysOn: true,
519
+ matchedGlobs: [],
520
+ ...metaFields,
521
+ ...(parseError ? { _parseError: true } : {}),
522
+ });
523
+ continue;
524
+ }
525
+
526
+ if (globs.length === 0) {
527
+ // Empty globs array: matches nothing (intentionally scoped out)
528
+ continue;
529
+ }
530
+
531
+ // Check whether any scopePath matches any glob
532
+ const matchedGlobs = [];
533
+ for (const glob of globs) {
534
+ const matched = normalizedScope.some((sp) => matchGlob(sp, glob));
535
+ if (matched) {
536
+ matchedGlobs.push(glob);
537
+ }
538
+ }
539
+
540
+ if (matchedGlobs.length > 0) {
541
+ results.push({
542
+ path: filePath,
543
+ content,
544
+ alwaysOn: false,
545
+ matchedGlobs,
546
+ ...metaFields,
547
+ });
548
+ }
549
+ }
550
+
551
+ return results;
552
+ }