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,243 @@
1
+ /**
2
+ * enumerate.mjs — Candidate-repo enumeration + free/busy resolution.
3
+ *
4
+ * Epic #673 Phase 2 (issue #676, PRD §2 P2.1+P2.2, §4). Enumerates candidate
5
+ * repos one level below a confinement root and resolves each as free or busy
6
+ * via its per-repo `session.lock` v2 lease (heartbeat-based liveness).
7
+ *
8
+ * Source of truth for free/busy: the same lease semantics as
9
+ * `scripts/lib/vault-status/board-writer.mjs` collectRows —
10
+ * - live lock → in-progress (busy)
11
+ * - dead lock → force-closed (busy)
12
+ * - no lock → frei (free)
13
+ * Busy repos are LISTED, never dropped (PRD: "busy repos listed as such, not
14
+ * selected"). Downstream selection happens in rank.mjs (#677), which consumes
15
+ * the {@link Candidate} contract defined here.
16
+ *
17
+ * Exports:
18
+ * enumerateCandidates — scan immediate children of a startDir, resolve each
19
+ * repo's free/busy status from its lease.
20
+ * freeCandidates — filter helper: keep only `free === true` candidates.
21
+ *
22
+ * No top-level side effects. All filesystem + lock access is dependency-injected
23
+ * (the `deps` arg) so Wave-4 tests can stub a tmpdir fs + fake locks.
24
+ *
25
+ * Plain Node ESM. Named exports. No external deps.
26
+ */
27
+
28
+ import { readdirSync, existsSync } from 'node:fs';
29
+ import os from 'node:os';
30
+ import path from 'node:path';
31
+
32
+ import { getConfinementRoot, getCrossRepoProjects } from '../config/cross-repo.mjs';
33
+ import { validatePathInsideProject } from '../path-utils.mjs';
34
+ import { readLock, isLockLive } from '../session-lock.mjs';
35
+
36
+ /**
37
+ * @typedef {Object} Candidate
38
+ * @property {string} repoRoot absolute path
39
+ * @property {string} repoName path.basename(repoRoot)
40
+ * @property {boolean} free true iff no live lease
41
+ * @property {'frei'|'in-progress'|'force-closed'} status
42
+ * @property {string|null} heartbeat lock.last_heartbeat ?? null
43
+ * @property {string|null} sessionId lock.semantic_session_id ?? lock.session_id ?? null
44
+ */
45
+
46
+ const STATUS_FREI = 'frei';
47
+ const STATUS_IN_PROGRESS = 'in-progress';
48
+ const STATUS_FORCE_CLOSED = 'force-closed';
49
+
50
+ /**
51
+ * Expand a leading `~` to the current user's home directory. Mirrors the helper
52
+ * in board-writer.mjs (a shared extraction is deferred to a later epic). Used to
53
+ * normalise config-declared cross-repo paths that may begin with `~/`.
54
+ *
55
+ * @param {string} p
56
+ * @returns {string}
57
+ */
58
+ function expandHome(p) {
59
+ if (typeof p !== 'string' || p.length === 0) return p;
60
+ if (p === '~') return os.homedir();
61
+ if (p.startsWith('~/')) return path.join(os.homedir(), p.slice(2));
62
+ return p;
63
+ }
64
+
65
+ /**
66
+ * Derive free/busy status for a single repo from its session.lock lease.
67
+ * Mirrors board-writer.mjs collectRows semantics exactly:
68
+ * - live lock → in-progress (busy)
69
+ * - dead lock → force-closed (busy)
70
+ * - no lock → frei (free)
71
+ *
72
+ * @param {string} repoRoot — absolute repo path.
73
+ * @param {number} nowMs — clock seam (ms).
74
+ * @param {object} resolveDeps
75
+ * @param {Function} resolveDeps.readLock — readLock({repoRoot}) → lock|null.
76
+ * @param {Function} resolveDeps.isLockLive — isLockLive(lock, nowMs) → boolean.
77
+ * @returns {Candidate}
78
+ */
79
+ function resolveCandidate(repoRoot, nowMs, { readLock: readLockFn, isLockLive: isLockLiveFn }) {
80
+ const lock = readLockFn({ repoRoot });
81
+ const live = !!(lock && isLockLiveFn(lock, nowMs));
82
+
83
+ let status;
84
+ let free;
85
+ if (live) {
86
+ status = STATUS_IN_PROGRESS;
87
+ free = false;
88
+ } else if (lock) {
89
+ // Lock present but dead lease (heartbeat older than ttl) — busy, not free.
90
+ status = STATUS_FORCE_CLOSED;
91
+ free = false;
92
+ } else {
93
+ status = STATUS_FREI;
94
+ free = true;
95
+ }
96
+
97
+ // Fields read straight off the raw lock (null when there is no lock).
98
+ const heartbeat = lock ? (lock.last_heartbeat ?? null) : null;
99
+ const sessionId = lock ? (lock.semantic_session_id ?? lock.session_id ?? null) : null;
100
+
101
+ return {
102
+ repoRoot,
103
+ repoName: path.basename(repoRoot),
104
+ free,
105
+ status,
106
+ heartbeat,
107
+ sessionId,
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Test whether a child path is a git repo. A child is a repo iff `<child>/.git`
113
+ * exists as a directory OR a file (the file form covers worktrees + submodules,
114
+ * whose `.git` is a `gitdir:` pointer file rather than a directory).
115
+ *
116
+ * @param {string} childAbs — absolute child path.
117
+ * @param {Function} existsSyncFn — injected existsSync.
118
+ * @returns {boolean}
119
+ */
120
+ function isGitRepo(childAbs, existsSyncFn) {
121
+ return existsSyncFn(path.join(childAbs, '.git'));
122
+ }
123
+
124
+ /**
125
+ * Enumerate candidate repos from a starting directory and resolve each as
126
+ * free or busy via its local lease.
127
+ *
128
+ * Algorithm:
129
+ * 1. Scan the IMMEDIATE children (one level deep) of `startDir` that are git
130
+ * repos (a child is a repo iff `<child>/.git` exists — dir or file).
131
+ * 2. Drop any child failing the confinement guard
132
+ * (`validatePathInsideProject(childAbs, startDir)`).
133
+ * 3. OPTIONAL secondary source: union with `getCrossRepoProjects()`
134
+ * config-declared paths (leading `~/` expanded, then confinement-filtered),
135
+ * deduped by `path.resolve()`. Additive, applied AFTER the FS scan.
136
+ * 4. Resolve free/busy per repo from its `session.lock` lease.
137
+ *
138
+ * ALL repos are returned (busy ones LISTED, not dropped — downstream rank.mjs
139
+ * filters). Returns a plain serialisable {@link Candidate}[].
140
+ *
141
+ * @param {object} [opts]
142
+ * @param {string} [opts.startDir] — scan root; defaults to {@link getConfinementRoot}().
143
+ * @param {number} [opts.now] — clock seam in ms; defaults to Date.now().
144
+ * @param {object} [opts.deps] — dependency-injection seam (Wave-4 testability).
145
+ * @param {Function} [opts.deps.readdirSync] — node:fs readdirSync.
146
+ * @param {Function} [opts.deps.existsSync] — node:fs existsSync.
147
+ * @param {Function} [opts.deps.readLock] — session-lock readLock.
148
+ * @param {Function} [opts.deps.isLockLive] — session-lock isLockLive.
149
+ * @param {Function} [opts.deps.getCrossRepoProjects] — config-declared paths accessor.
150
+ * @param {Function} [opts.deps.validatePathInsideProject] — confinement guard.
151
+ * @param {Function} [opts.deps.now] — () => ms (overridden by opts.now when set).
152
+ * @returns {Promise<Candidate[]>}
153
+ */
154
+ export async function enumerateCandidates({ startDir, now, deps } = {}) {
155
+ const d = deps ?? {};
156
+ const readdirSyncFn = d.readdirSync ?? readdirSync;
157
+ const existsSyncFn = d.existsSync ?? existsSync;
158
+ const readLockFn = d.readLock ?? readLock;
159
+ const isLockLiveFn = d.isLockLive ?? isLockLive;
160
+ const getCrossRepoProjectsFn = d.getCrossRepoProjects ?? getCrossRepoProjects;
161
+ const validatePathInsideProjectFn = d.validatePathInsideProject ?? validatePathInsideProject;
162
+ const nowFn = d.now ?? Date.now;
163
+
164
+ const root = (typeof startDir === 'string' && startDir.length > 0)
165
+ ? startDir
166
+ : getConfinementRoot();
167
+ const nowMs = typeof now === 'number' ? now : nowFn();
168
+
169
+ // Dedup set keyed by resolved absolute path; preserves first-seen ordering
170
+ // (FS-scan repos first, config-declared additions after).
171
+ const seen = new Set();
172
+ /** @type {string[]} */
173
+ const repoPaths = [];
174
+
175
+ const addRepo = (absPath) => {
176
+ const resolved = path.resolve(absPath);
177
+ if (seen.has(resolved)) return;
178
+ seen.add(resolved);
179
+ repoPaths.push(resolved);
180
+ };
181
+
182
+ // ── 1+2. FS scan of immediate children, confinement-guarded. ──
183
+ let entries;
184
+ try {
185
+ entries = readdirSyncFn(root, { withFileTypes: true });
186
+ } catch {
187
+ // Unreadable/absent startDir → no FS-scanned repos. The config-declared
188
+ // secondary source below may still contribute.
189
+ entries = [];
190
+ }
191
+
192
+ for (const entry of entries) {
193
+ // Only directories can be repos. Dirent.isDirectory() guards against files,
194
+ // sockets, etc. A stubbed entry may not implement isDirectory — fall back
195
+ // to treating it as a directory candidate (existsSync('.git') gates anyway).
196
+ const isDir = typeof entry?.isDirectory === 'function' ? entry.isDirectory() : true;
197
+ if (!isDir) continue;
198
+
199
+ const childAbs = path.join(root, entry.name);
200
+ if (!isGitRepo(childAbs, existsSyncFn)) continue;
201
+
202
+ // Confinement guard: drop anything not strictly inside startDir.
203
+ const guard = validatePathInsideProjectFn(childAbs, root);
204
+ if (!guard || guard.ok !== true) continue;
205
+
206
+ addRepo(childAbs);
207
+ }
208
+
209
+ // ── 3. Optional secondary source: config-declared cross-repo projects. ──
210
+ let declared;
211
+ try {
212
+ declared = await getCrossRepoProjectsFn();
213
+ } catch {
214
+ declared = [];
215
+ }
216
+ if (Array.isArray(declared)) {
217
+ for (const raw of declared) {
218
+ if (typeof raw !== 'string' || raw.length === 0) continue;
219
+ const expanded = expandHome(raw);
220
+ const abs = path.resolve(expanded);
221
+ // Confinement-filter against the same root as the FS scan.
222
+ const guard = validatePathInsideProjectFn(abs, root);
223
+ if (!guard || guard.ok !== true) continue;
224
+ addRepo(abs);
225
+ }
226
+ }
227
+
228
+ // ── 4. Resolve free/busy per repo. ──
229
+ return repoPaths.map((repoRoot) =>
230
+ resolveCandidate(repoRoot, nowMs, { readLock: readLockFn, isLockLive: isLockLiveFn }),
231
+ );
232
+ }
233
+
234
+ /**
235
+ * Filter a candidate list down to the free ones (no live lease).
236
+ *
237
+ * @param {Candidate[]} candidates
238
+ * @returns {Candidate[]}
239
+ */
240
+ export function freeCandidates(candidates) {
241
+ if (!Array.isArray(candidates)) return [];
242
+ return candidates.filter((c) => c && c.free === true);
243
+ }
@@ -0,0 +1,363 @@
1
+ /**
2
+ * rank.mjs — autopilot dispatcher ranking scorer (Epic #673 P2, issue #677).
3
+ *
4
+ * Ranks FREE candidate repos so the cross-repo dispatcher can recommend the
5
+ * single most worthwhile one to work on next. Combines three signals into a
6
+ * multiplicative score:
7
+ *
8
+ * priority × staleness × readiness
9
+ *
10
+ * - PRIORITY — open-backlog severity (critical/high counts) from
11
+ * `scanBacklog` (scripts/lib/backlog-scan.mjs). null (glab/gh missing or
12
+ * timeout) ⇒ neutral fallback (1) + a warning. NEVER blocks.
13
+ * - STALENESS — days since the candidate's last completed session. Older =
14
+ * more worthwhile (you have not touched it in a while). No prior sessions ⇒
15
+ * maximally stale.
16
+ * - READINESS — CI status (red repos are worth less right now) × host
17
+ * resource verdict (a degraded/critical host de-prioritises everything
18
+ * equally; it is a host-level signal, fetched once and shared).
19
+ *
20
+ * Design contract (PRD §2 P2.3, §3 FA-2, §4):
21
+ * - `scoreCandidate` is a PURE, deterministic, documented formula — no I/O.
22
+ * - `rankCandidates` does all I/O through injectable `deps` so Wave-4 tests
23
+ * are deterministic. Default `deps` wires the real signal sources.
24
+ * - GLAB/GH FALLBACK is mandatory: a null priority is ranked on
25
+ * staleness × readiness only, with a human-readable warning. Never throws,
26
+ * never blocks.
27
+ * - All returns are plain serialisable objects (CLI wrapper lands in #678).
28
+ *
29
+ * @typedef {{ repoRoot: string, repoName: string, free: boolean, status: 'frei'|'in-progress'|'force-closed', heartbeat: string|null, sessionId: string|null }} Candidate
30
+ */
31
+
32
+ import { readFileSync } from 'node:fs';
33
+ import path from 'node:path';
34
+
35
+ import { scanBacklog } from '../backlog-scan.mjs';
36
+ import { checkCiStatus as realCheckCiStatus } from '../ci-status-banner.mjs';
37
+ import { probe as realProbe, evaluate as realEvaluate } from '../resource-probe.mjs';
38
+
39
+ /** Staleness cap (days). Beyond this, additional age does not raise the score. */
40
+ export const STALENESS_CAP_DAYS = 90;
41
+
42
+ /** Day length in ms — shared age-in-days idiom (mirrors backlog-scan ageDays). */
43
+ const MS_PER_DAY = 86_400_000;
44
+
45
+ /**
46
+ * Canonical resource-threshold defaults (mirrors
47
+ * scripts/lib/config/vault-integration.mjs `_parseResourceThresholds`).
48
+ * Used by the default `resourceVerdict` dep so ranking works standalone
49
+ * without a parsed Session Config in hand.
50
+ */
51
+ const DEFAULT_RESOURCE_THRESHOLDS = {
52
+ 'ram-free-min-gb': 4,
53
+ 'ram-free-critical-gb': 2,
54
+ 'cpu-load-max-pct': 80,
55
+ 'concurrent-sessions-warn': 5,
56
+ };
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // PURE core
60
+ // ---------------------------------------------------------------------------
61
+
62
+ /**
63
+ * Score a single candidate from its gathered signals. Pure + deterministic.
64
+ *
65
+ * FORMULA (multiplicative — higher = more worth working on):
66
+ *
67
+ * priorityScore = priority
68
+ * ? 1 + 2·criticalCount + 1·highCount
69
+ * : 1 // null ⇒ neutral fallback
70
+ * stalenessScore = 1 + min(staleDays, 90) / 30 // older = higher, capped
71
+ * readinessScore = ciFactor · resourceFactor
72
+ * ciFactor = ciStatus === 'red' ? 0.25 : 1
73
+ * // null / 'unknown' / 'green' are all non-blocking (1)
74
+ * resourceFactor = critical ? 0.25 : degraded ? 0.6 : 1
75
+ * // 'green' / 'warn' ⇒ 1
76
+ *
77
+ * score = priorityScore · stalenessScore · readinessScore
78
+ *
79
+ * Rationale:
80
+ * - A candidate with no backlog signal is neither rewarded nor punished — it
81
+ * competes on staleness × readiness alone (the glab/gh fallback).
82
+ * - Staleness is bounded so an ancient repo cannot dominate purely on age.
83
+ * - Readiness only ever DAMPENS (factors ≤ 1): a red CI or a strained host
84
+ * makes a repo less attractive right now, but never negative.
85
+ *
86
+ * @param {{
87
+ * priority: { criticalCount: number, highCount: number } | null,
88
+ * staleDays: number,
89
+ * readiness: {
90
+ * ciStatus: 'green'|'red'|'unknown'|null,
91
+ * resourceVerdict: 'green'|'warn'|'degraded'|'critical',
92
+ * },
93
+ * }} signals
94
+ * @returns {number} score (≥ 0; higher = more worthwhile)
95
+ */
96
+ export function scoreCandidate(signals) {
97
+ const { priority, staleDays, readiness } = signals;
98
+
99
+ const priorityScore = priority
100
+ ? 1 + 2 * (priority.criticalCount || 0) + 1 * (priority.highCount || 0)
101
+ : 1;
102
+
103
+ // Guard against NaN/negative staleDays — clamp to [0, cap].
104
+ const safeStaleDays =
105
+ typeof staleDays === 'number' && Number.isFinite(staleDays) && staleDays > 0
106
+ ? staleDays
107
+ : 0;
108
+ const stalenessScore = 1 + Math.min(safeStaleDays, STALENESS_CAP_DAYS) / 30;
109
+
110
+ const ciStatus = readiness?.ciStatus ?? null;
111
+ const resourceVerdict = readiness?.resourceVerdict ?? 'green';
112
+
113
+ const ciFactor = ciStatus === 'red' ? 0.25 : 1;
114
+ const resourceFactor =
115
+ resourceVerdict === 'critical'
116
+ ? 0.25
117
+ : resourceVerdict === 'degraded'
118
+ ? 0.6
119
+ : 1;
120
+ const readinessScore = ciFactor * resourceFactor;
121
+
122
+ return priorityScore * stalenessScore * readinessScore;
123
+ }
124
+
125
+ // ---------------------------------------------------------------------------
126
+ // Default signal-source implementations (the real wiring)
127
+ // ---------------------------------------------------------------------------
128
+
129
+ /**
130
+ * Default PRIORITY source: run `scanBacklog` against the candidate repo.
131
+ *
132
+ * `scanBacklog` keys off the CURRENT process git remote, so to score a
133
+ * DIFFERENT repo we run it with that repo as the working directory and restore
134
+ * cwd afterwards. Returns the scanBacklog summary or null (CLI missing /
135
+ * timeout / parse-fail). Never throws.
136
+ *
137
+ * @param {string} repoRoot
138
+ * @param {number} nowMs
139
+ * @returns {Promise<null | {criticalCount: number, highCount: number}>}
140
+ */
141
+ async function defaultFetchPriority(repoRoot, nowMs) {
142
+ const prevCwd = process.cwd();
143
+ try {
144
+ process.chdir(repoRoot);
145
+ return await scanBacklog({ nowMs });
146
+ } catch {
147
+ return null;
148
+ } finally {
149
+ try {
150
+ process.chdir(prevCwd);
151
+ } catch {
152
+ /* best-effort cwd restore */
153
+ }
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Default STALENESS source: read `<repoRoot>/.orchestrator/metrics/sessions.jsonl`,
159
+ * take the LAST record, and compute days since `completed_at` (fallback
160
+ * `started_at`). No file / no parsable record / no timestamp ⇒
161
+ * `STALENESS_CAP_DAYS` (treat as maximally stale = most worthwhile).
162
+ *
163
+ * @param {string} repoRoot
164
+ * @param {number} nowMs
165
+ * @returns {Promise<number>} days since last session (≥ 0)
166
+ */
167
+ async function defaultStaleDaysFor(repoRoot, nowMs) {
168
+ try {
169
+ const file = path.join(repoRoot, '.orchestrator', 'metrics', 'sessions.jsonl');
170
+ const raw = readFileSync(file, 'utf8');
171
+ const lines = raw.split('\n').map((l) => l.trim()).filter(Boolean);
172
+ if (lines.length === 0) return STALENESS_CAP_DAYS;
173
+
174
+ // Last non-empty line = most recent session record.
175
+ let last = null;
176
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
177
+ try {
178
+ last = JSON.parse(lines[i]);
179
+ break;
180
+ } catch {
181
+ // Skip a corrupt trailing line and try the previous one.
182
+ }
183
+ }
184
+ if (!last || typeof last !== 'object') return STALENESS_CAP_DAYS;
185
+
186
+ const iso = last.completed_at || last.started_at || null;
187
+ if (!iso || typeof iso !== 'string') return STALENESS_CAP_DAYS;
188
+ const t = Date.parse(iso);
189
+ if (Number.isNaN(t)) return STALENESS_CAP_DAYS;
190
+
191
+ const days = (nowMs - t) / MS_PER_DAY;
192
+ return days > 0 ? days : 0;
193
+ } catch {
194
+ // No sessions file (or unreadable) ⇒ never worked on ⇒ maximally stale.
195
+ return STALENESS_CAP_DAYS;
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Default READINESS (CI) source: thin wrapper over `checkCiStatus`.
201
+ * Returns the 'green'|'red'|'unknown' status string, or null (no-op).
202
+ * null / 'unknown' are treated as non-blocking by `scoreCandidate`.
203
+ *
204
+ * @param {{ repoRoot: string }} args
205
+ * @returns {Promise<'green'|'red'|'unknown'|null>}
206
+ */
207
+ async function defaultCheckCiStatus({ repoRoot }) {
208
+ try {
209
+ const result = await realCheckCiStatus({ repoRoot });
210
+ return result && typeof result.status === 'string' ? result.status : null;
211
+ } catch {
212
+ return null;
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Default READINESS (resource) source: probe the host ONCE and evaluate against
218
+ * the canonical default thresholds. This is a HOST-level signal (identical for
219
+ * every candidate), so `rankCandidates` calls it exactly once per ranking run.
220
+ * Falls back to 'green' (non-dampening) on any failure.
221
+ *
222
+ * @returns {Promise<'green'|'warn'|'degraded'|'critical'>}
223
+ */
224
+ async function defaultResourceVerdict() {
225
+ try {
226
+ const snapshot = await realProbe();
227
+ const { verdict } = realEvaluate(snapshot, DEFAULT_RESOURCE_THRESHOLDS);
228
+ return verdict || 'green';
229
+ } catch {
230
+ return 'green';
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Build the default `deps` object. Exposed for tests that want to override a
236
+ * single dep while keeping the real ones for the rest.
237
+ *
238
+ * @returns {{
239
+ * fetchPriority: (repoRoot: string, nowMs: number) => Promise<null | {criticalCount: number, highCount: number}>,
240
+ * staleDaysFor: (repoRoot: string, nowMs: number) => Promise<number>,
241
+ * checkCiStatus: (args: { repoRoot: string }) => Promise<'green'|'red'|'unknown'|null>,
242
+ * resourceVerdict: () => Promise<'green'|'warn'|'degraded'|'critical'>,
243
+ * }}
244
+ */
245
+ export function defaultDeps() {
246
+ return {
247
+ fetchPriority: defaultFetchPriority,
248
+ staleDaysFor: defaultStaleDaysFor,
249
+ checkCiStatus: defaultCheckCiStatus,
250
+ resourceVerdict: defaultResourceVerdict,
251
+ };
252
+ }
253
+
254
+ // ---------------------------------------------------------------------------
255
+ // Orchestration
256
+ // ---------------------------------------------------------------------------
257
+
258
+ /**
259
+ * Rank a list of FREE candidate repos (the dispatcher pre-filters
260
+ * `free === true`). Gathers signals via injectable `deps`, scores each
261
+ * candidate with the pure `scoreCandidate` formula, and sorts DESC by score.
262
+ *
263
+ * Sort is deterministic: score DESC, then staleDays DESC, then repoName ASC.
264
+ * `ranked[0]` is the top recommendation.
265
+ *
266
+ * GLAB/GH FALLBACK: when `deps.fetchPriority` returns null (CLI missing /
267
+ * timeout), the candidate is scored with `priority: null` (neutral 1) AND a
268
+ * warning is pushed. Never throws, never blocks.
269
+ *
270
+ * The host resource verdict is fetched ONCE (host-level signal) and shared
271
+ * across all candidates.
272
+ *
273
+ * @param {Candidate[]} freeCandidates
274
+ * @param {{
275
+ * now?: number,
276
+ * deps?: Partial<ReturnType<typeof defaultDeps>>,
277
+ * }} [opts]
278
+ * @returns {Promise<{
279
+ * ranked: Array<{ candidate: Candidate, score: number, signals: {
280
+ * priority: { criticalCount: number, highCount: number } | null,
281
+ * staleDays: number,
282
+ * readiness: { ciStatus: 'green'|'red'|'unknown'|null, resourceVerdict: 'green'|'warn'|'degraded'|'critical' },
283
+ * } }>,
284
+ * warnings: string[],
285
+ * }>}
286
+ */
287
+ export async function rankCandidates(freeCandidates, opts = {}) {
288
+ const candidates = Array.isArray(freeCandidates) ? freeCandidates : [];
289
+ const nowMs = typeof opts.now === 'number' ? opts.now : Date.now();
290
+ const deps = { ...defaultDeps(), ...(opts.deps || {}) };
291
+ const warnings = [];
292
+
293
+ // Host-level resource verdict — fetched ONCE, shared across candidates.
294
+ let resourceVerdict;
295
+ try {
296
+ resourceVerdict = (await deps.resourceVerdict()) || 'green';
297
+ } catch {
298
+ // Non-blocking: a failed host probe defaults to non-dampening 'green'.
299
+ resourceVerdict = 'green';
300
+ warnings.push('resource verdict unavailable (host probe failed) — readiness scored without resource dampening');
301
+ }
302
+
303
+ const rows = [];
304
+ for (const candidate of candidates) {
305
+ const repoRoot = candidate?.repoRoot;
306
+ const repoName = candidate?.repoName ?? repoRoot ?? '<unknown>';
307
+
308
+ // PRIORITY (with glab/gh fallback).
309
+ let priority;
310
+ try {
311
+ const raw = await deps.fetchPriority(repoRoot, nowMs);
312
+ priority =
313
+ raw && typeof raw === 'object'
314
+ ? { criticalCount: raw.criticalCount || 0, highCount: raw.highCount || 0 }
315
+ : null;
316
+ } catch {
317
+ priority = null;
318
+ }
319
+ if (priority === null) {
320
+ warnings.push(
321
+ `priority unavailable for ${repoName} (glab/gh missing or timeout) — ranked on staleness×readiness only`,
322
+ );
323
+ }
324
+
325
+ // STALENESS.
326
+ let staleDays;
327
+ try {
328
+ const d = await deps.staleDaysFor(repoRoot, nowMs);
329
+ staleDays = typeof d === 'number' && Number.isFinite(d) && d >= 0 ? d : STALENESS_CAP_DAYS;
330
+ } catch {
331
+ staleDays = STALENESS_CAP_DAYS;
332
+ }
333
+
334
+ // READINESS — CI status.
335
+ let ciStatus;
336
+ try {
337
+ ciStatus = await deps.checkCiStatus({ repoRoot });
338
+ } catch {
339
+ ciStatus = null;
340
+ }
341
+
342
+ const signals = {
343
+ priority,
344
+ staleDays,
345
+ readiness: { ciStatus, resourceVerdict },
346
+ };
347
+ const score = scoreCandidate(signals);
348
+
349
+ rows.push({ candidate, score, signals });
350
+ }
351
+
352
+ rows.sort((a, b) => {
353
+ if (b.score !== a.score) return b.score - a.score;
354
+ if (b.signals.staleDays !== a.signals.staleDays) {
355
+ return b.signals.staleDays - a.signals.staleDays;
356
+ }
357
+ const an = a.candidate?.repoName ?? '';
358
+ const bn = b.candidate?.repoName ?? '';
359
+ return an < bn ? -1 : an > bn ? 1 : 0;
360
+ });
361
+
362
+ return { ranked: rows, warnings };
363
+ }