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,293 @@
1
+ /**
2
+ * writer.mjs — FA3 writer seam for the Reconciliation Engine (Epic #693, issue #696).
3
+ *
4
+ * Persists APPROVED reconciliation rule proposals to `.claude/rules/` AFTER
5
+ * operator approval. This is the one and only module that writes `.claude/rules/`
6
+ * on behalf of the engine — the FA2 engine/renderer NEVER touch the filesystem
7
+ * for rule files.
8
+ *
9
+ * Responsibilities:
10
+ * - Acquire a per-write file lock (`.orchestrator/rules.lock`) to serialise
11
+ * concurrent writers — mirrors PSA-005 (withStateMdLock) pattern.
12
+ * - For each approved proposal: path-safety guard → mkdirSync → atomic tmp+rename write.
13
+ * - For each rejected proposal: JSONL-append to `.orchestrator/reconcile.rejected.log`.
14
+ * - Never throws — all failures are collected into errors[] and returned.
15
+ *
16
+ * Path-safety:
17
+ * - `validatePathInsideProject(item.path, repoRoot, {canonicalizeRoot:true})` is
18
+ * the primary guard (two-phase lexical + realpath, CWE-22 defence).
19
+ * - Additional assertion: resolved path must be inside `<repoRoot>/.claude/rules/`.
20
+ * - Both guards must pass; failure skips the record and pushes an error string.
21
+ *
22
+ * Atomic write strategy (rule files):
23
+ * - Write content to `<target>.XXXXXXXX.tmp` via `writeFileSync`, then
24
+ * `renameSync` over the final path. Same-filesystem rename is atomic on POSIX,
25
+ * so the rule file is never partially visible. Mirrors idempotency.mjs pattern.
26
+ *
27
+ * JSONL append strategy (rejected log):
28
+ * - `appendFileSync` for the rejected log — each record is a self-contained line,
29
+ * and POSIX append (O_APPEND) is atomic for writes < PIPE_BUF (4096 bytes),
30
+ * which every JSONL record satisfies. Mirrors memory-proposals/sink.mjs pattern.
31
+ *
32
+ * DI-friendly: accepts repoRoot as a parameter; no global cwd assumptions.
33
+ *
34
+ * Plain Node ESM, no external deps — Node 20+ stdlib + sibling scripts/lib only.
35
+ *
36
+ * Part of Epic #693 → issue #696 (FA3 Advisory Delivery).
37
+ *
38
+ * @module reconcile/writer
39
+ */
40
+
41
+ import { mkdirSync, writeFileSync, renameSync, appendFileSync, realpathSync } from 'node:fs';
42
+ import { randomBytes } from 'node:crypto';
43
+ import path from 'node:path';
44
+
45
+ import { withFileLock } from '../file-lock.mjs';
46
+ import { validatePathInsideProject } from '../path-utils.mjs';
47
+
48
+ // ---------------------------------------------------------------------------
49
+ // Path constants (relative to repoRoot)
50
+ // ---------------------------------------------------------------------------
51
+
52
+ /** Lock file that serialises concurrent rule-write operations. */
53
+ const RULES_LOCK_REL = path.join('.orchestrator', 'rules.lock');
54
+
55
+ /** Directory where auto-generated rule files are written (repo-relative). */
56
+ const RULES_DIR_REL = path.join('.claude', 'rules');
57
+
58
+ /** Rejected-proposals log for rules declined by the operator (repo-relative). */
59
+ const REJECTED_LOG_REL = path.join('.orchestrator', 'reconcile.rejected.log');
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // Internal helpers
63
+ // ---------------------------------------------------------------------------
64
+
65
+ /**
66
+ * Resolve the absolute rules directory for the given repoRoot. Used both for
67
+ * the per-proposal assertion and for mkdirSync.
68
+ *
69
+ * @param {string} repoRoot
70
+ * @returns {string}
71
+ */
72
+ function rulesAbsDir(repoRoot) {
73
+ return path.resolve(repoRoot, RULES_DIR_REL);
74
+ }
75
+
76
+ /**
77
+ * Write `content` to `destPath` atomically via tmp+rename.
78
+ *
79
+ * Uses a random 8-hex-char suffix for the tmp file to avoid collisions when
80
+ * multiple proposals write to the same directory concurrently (defensive;
81
+ * under the lock this should not happen, but the pattern is cheap).
82
+ *
83
+ * Throws on filesystem errors — callers must catch.
84
+ *
85
+ * @param {string} destPath - absolute path of the target rule file.
86
+ * @param {string} content - UTF-8 text content to write.
87
+ */
88
+ function writeTextAtomic(destPath, content) {
89
+ const dir = path.dirname(destPath);
90
+ mkdirSync(dir, { recursive: true });
91
+ const suffix = randomBytes(4).toString('hex');
92
+ const tmpPath = `${destPath}.${suffix}.tmp`;
93
+ writeFileSync(tmpPath, content, 'utf8');
94
+ renameSync(tmpPath, destPath);
95
+ }
96
+
97
+ // ---------------------------------------------------------------------------
98
+ // Public API
99
+ // ---------------------------------------------------------------------------
100
+
101
+ /**
102
+ * @typedef {Object} WriterApprovedItem
103
+ * @property {string} slug - kebab-case slug (from renderer.mjs / engine.mjs).
104
+ * @property {string} path - repo-relative rule path (`.claude/rules/<slug>.md`).
105
+ * @property {string} content - full rendered markdown content.
106
+ * @property {string} [learningKey]
107
+ * @property {number} [confidence]
108
+ * @property {string} [candidateId]
109
+ * @property {string} [status]
110
+ */
111
+
112
+ /**
113
+ * @typedef {Object} WriterRejectedItem
114
+ * @property {string} [learningKey]
115
+ * @property {string} [type]
116
+ * @property {string} [reason]
117
+ * @property {string} [status]
118
+ */
119
+
120
+ /**
121
+ * @typedef {Object} WriteApprovedRulesResult
122
+ * @property {number} written - number of rule files successfully written.
123
+ * @property {number} archived - number of rejected records appended to the log.
124
+ * @property {string[]} errors - per-item error strings (never fatal).
125
+ */
126
+
127
+ /**
128
+ * Persist approved reconciliation rule proposals to `.claude/rules/` and
129
+ * archive rejected proposals to the rejected log.
130
+ *
131
+ * NEVER throws — all per-item failures are collected into `errors[]`.
132
+ *
133
+ * @param {Object} opts
134
+ * @param {WriterApprovedItem[]} opts.approved - proposals approved by the operator.
135
+ * @param {WriterRejectedItem[]} [opts.rejected] - proposals declined by the operator.
136
+ * @param {string} opts.repoRoot - absolute repo root path.
137
+ * @param {string} [opts.sessionId] - current session id (informational; unused in v1).
138
+ * @returns {Promise<WriteApprovedRulesResult>}
139
+ */
140
+ export async function writeApprovedRules({ approved, rejected = [], repoRoot, sessionId: _sessionId }) {
141
+ // Defensive: coerce inputs
142
+ const approvedItems = Array.isArray(approved) ? approved : [];
143
+ const rejectedItems = Array.isArray(rejected) ? rejected : [];
144
+
145
+ if (approvedItems.length === 0 && rejectedItems.length === 0) {
146
+ return { written: 0, archived: 0, errors: [] };
147
+ }
148
+
149
+ const lockPath = path.join(repoRoot, RULES_LOCK_REL);
150
+
151
+ // Ensure .orchestrator dir exists so lock acquisition can create the lock file.
152
+ try {
153
+ mkdirSync(path.dirname(lockPath), { recursive: true });
154
+ } catch {
155
+ // Non-fatal: withFileLock will surface the fs-error if the lock cannot be created.
156
+ }
157
+
158
+ // Acquire the rules lock — serialises concurrent rule writes.
159
+ const lockResult = await withFileLock(
160
+ lockPath,
161
+ async () => {
162
+ /** @type {string[]} */
163
+ const errors = [];
164
+ let written = 0;
165
+ let archived = 0;
166
+
167
+ const rulesDir = rulesAbsDir(repoRoot);
168
+
169
+ // Parent-directory symlink hardening (#697 security follow-up): if
170
+ // `.claude/rules/` is itself a pre-planted symlink to a directory outside
171
+ // the repo, a lexically-safe leaf path would still be written through it.
172
+ // Resolve the directory's realpath once and refuse all writes if it
173
+ // escapes the canonical repo root. Requires local FS write access to
174
+ // exploit (below the VCS trust boundary) but the guard is one cheap call.
175
+ let rulesDirSafe = true;
176
+ try {
177
+ mkdirSync(rulesDir, { recursive: true });
178
+ let canonRoot = repoRoot;
179
+ try { canonRoot = realpathSync(repoRoot); } catch { /* fall back to lexical */ }
180
+ const expectedRulesDir = path.resolve(canonRoot, RULES_DIR_REL);
181
+ const realRulesDir = realpathSync(rulesDir);
182
+ if (realRulesDir !== expectedRulesDir && !realRulesDir.startsWith(expectedRulesDir + path.sep)) {
183
+ errors.push('path-confinement: .claude/rules/ resolves outside the repo (symlinked dir) — all approved writes skipped');
184
+ rulesDirSafe = false;
185
+ }
186
+ } catch { /* mkdir/realpath failure — per-item writes will surface errors normally */ }
187
+
188
+ // ── Step 1: write approved rule files ──────────────────────────────────
189
+ for (const item of approvedItems) {
190
+ if (!rulesDirSafe) break;
191
+ // Guard: item must have a path string
192
+ if (!item || typeof item.path !== 'string' || item.path.length === 0) {
193
+ errors.push(`approved item missing path: ${JSON.stringify(item)}`);
194
+ continue;
195
+ }
196
+
197
+ // Primary path-safety guard (two-phase lexical + realpath, CWE-22 defence)
198
+ const pathResult = validatePathInsideProject(item.path, repoRoot, { canonicalizeRoot: true });
199
+ if (!pathResult.ok) {
200
+ errors.push(`path-safety (${pathResult.reason}): "${item.path}" — skipped`);
201
+ continue;
202
+ }
203
+
204
+ // Resolve the absolute write target from the validated lexical path
205
+ const absPath = pathResult.realPath ?? pathResult.lexicalPath;
206
+
207
+ // Defense-in-depth: assert the resolved path is inside .claude/rules/.
208
+ // Canonicalize repoRoot the same way validatePathInsideProject does
209
+ // (opts.canonicalizeRoot:true) so the prefix check is consistent on
210
+ // platforms where os.tmpdir() has a symlink (e.g. macOS /var → /private/var).
211
+ let canonRoot = repoRoot;
212
+ try { canonRoot = realpathSync(repoRoot); } catch { /* ENOENT/EACCES: fall back to lexical */ }
213
+ const resolvedRulesDir = path.resolve(canonRoot, RULES_DIR_REL);
214
+ if (!absPath.startsWith(resolvedRulesDir + path.sep) && absPath !== resolvedRulesDir) {
215
+ errors.push(`path-confinement: "${item.path}" resolves outside .claude/rules/ — skipped`);
216
+ continue;
217
+ }
218
+
219
+ // Guard: content must be a string
220
+ if (typeof item.content !== 'string') {
221
+ errors.push(`approved item "${item.path}" has non-string content — skipped`);
222
+ continue;
223
+ }
224
+
225
+ // Ensure .claude/rules/ exists and write atomically
226
+ try {
227
+ mkdirSync(rulesDir, { recursive: true });
228
+ writeTextAtomic(absPath, item.content);
229
+ written++;
230
+ } catch (err) {
231
+ const msg = err && err.message ? err.message : String(err);
232
+ errors.push(`write failed "${item.path}": ${msg}`);
233
+ }
234
+ }
235
+
236
+ // ── Step 2: archive rejected proposals to the rejected log ─────────────
237
+ if (rejectedItems.length > 0) {
238
+ const rejectedLogRelPath = REJECTED_LOG_REL;
239
+ const logResult = validatePathInsideProject(rejectedLogRelPath, repoRoot, { canonicalizeRoot: true });
240
+
241
+ if (!logResult.ok) {
242
+ errors.push(`path-safety (rejected log): ${logResult.reason} (${rejectedLogRelPath})`);
243
+ } else {
244
+ const rejectedLogPath = logResult.realPath ?? logResult.lexicalPath;
245
+
246
+ // Ensure parent directory exists
247
+ try {
248
+ mkdirSync(path.dirname(rejectedLogPath), { recursive: true });
249
+ } catch (err) {
250
+ const msg = err && err.message ? err.message : String(err);
251
+ errors.push(`mkdir failed (rejected log): ${msg}`);
252
+ }
253
+
254
+ const rejectedAt = new Date().toISOString();
255
+
256
+ for (const item of rejectedItems) {
257
+ try {
258
+ const archiveRecord = {
259
+ ...item,
260
+ _rejected_reason: (item && typeof item.reason === 'string' && item.reason !== '')
261
+ ? item.reason
262
+ : 'user-declined',
263
+ _rejected_at: rejectedAt,
264
+ };
265
+ const line = JSON.stringify(archiveRecord) + '\n';
266
+ appendFileSync(rejectedLogPath, line, 'utf8');
267
+ archived++;
268
+ } catch (err) {
269
+ const msg = err && err.message ? err.message : String(err);
270
+ const key = (item && item.learningKey) ? item.learningKey : '<unknown>';
271
+ errors.push(`archive failed "${key}": ${msg}`);
272
+ }
273
+ }
274
+ }
275
+ }
276
+
277
+ return { written, archived, errors };
278
+ },
279
+ { timeoutMs: 10000 },
280
+ );
281
+
282
+ // If lock acquisition failed, return a zeroed result with the lock error.
283
+ if (lockResult.ok === false) {
284
+ return {
285
+ written: 0,
286
+ archived: 0,
287
+ errors: [`lock-${lockResult.reason ?? 'unknown'}`],
288
+ };
289
+ }
290
+
291
+ // Unwrap the result returned from inside the lock body.
292
+ return lockResult.value;
293
+ }
@@ -0,0 +1,284 @@
1
+ /**
2
+ * reconcile-nudge-banner.mjs — #723 (Epic #723 B1, Flaggschiff-Konvergenz 11).
3
+ *
4
+ * Deterministic session-start nudge for the Learning→Rule reconciliation
5
+ * pipeline. Fleet finding: `reconcile.enabled` defaults to `false` (silent
6
+ * no-op) and `/reconcile` is on-demand-only — repos with 131/123/101
7
+ * learnings on disk had ZERO reconcile runs, because nothing ever told the
8
+ * operator the corpus had accumulated enough evidence to be worth converting.
9
+ * This probe closes that gap the same way `loop-readiness-banner.mjs` (#633)
10
+ * closes the "no .claude/loop.md" gap: a single `checkXxx({repoRoot})` entry
11
+ * point that returns `null` (silent no-op) or `{severity:'warn', message}`.
12
+ *
13
+ * Deliberately introduces NO new Session Config key — per the #723 scope note,
14
+ * an advisory-only banner needs no on/off switch of its own (mirrors
15
+ * `ci-status-banner.mjs` / `qg-command-drift-banner.mjs` / `loop-readiness-banner.mjs`,
16
+ * none of which carry a dedicated enable flag). The EXISTING `reconcile.enabled`
17
+ * key is read only to decide whether to append an informational parenthetical —
18
+ * never to gate the probe itself.
19
+ *
20
+ * "Last reconcile run" provenance (researched, not guessed): the reconciliation
21
+ * engine (`scripts/lib/reconcile/engine.mjs` `runReconcile`) writes exactly ONE
22
+ * disk artifact per run — the idempotency sidecar at
23
+ * `.orchestrator/runtime/reconcile-candidates.jsonl` (owned by
24
+ * `scripts/lib/reconcile/idempotency.mjs`). Every learning present at a given
25
+ * run (eligible OR rejected) gets a candidate row, so:
26
+ * - the MAX `created_at` across `loadCandidates({repoRoot})` is the most
27
+ * recent reconcile run's timestamp (or `null` when the store is empty —
28
+ * i.e. no run on record, matching the fleet finding above).
29
+ * - the candidate COUNT is a reasonable proxy for "how many learnings had
30
+ * been seen as of the last run" (dedup is by `learning_key`, so it is a
31
+ * high-water mark across all runs to date), used for the "new learnings
32
+ * since last run" delta heuristic.
33
+ * There is no separate `reconcile`-scoped event in `.orchestrator/metrics/events.jsonl`
34
+ * — the idempotency sidecar IS the durable run record, so this module reads
35
+ * that directly rather than re-deriving a signal from the generic event log.
36
+ *
37
+ * Reuses the existing learnings-corpus helpers rather than re-implementing
38
+ * filter logic:
39
+ * - `readLearnings` (`./learnings/io.mjs`) — normalized entries (files→file_paths
40
+ * dialect rename applied), used for the eligibility count.
41
+ * - `surfaceTopN` (`./learnings/surface.mjs`) — the canonical active-learning
42
+ * filter (confidence > floor, not expired); called with a large cap to get
43
+ * the full active set rather than a top-N slice.
44
+ * - `filterEligible` (`./reconcile/eligibility.mjs`) — the SAME type/file_paths
45
+ * allow-list gate the reconcile engine itself uses (deliberately NOT
46
+ * confidence-gated — mirrors `runReconcile`'s own posture, see
47
+ * `skills/reconcile/SKILL.md` Phase 2.2).
48
+ *
49
+ * Plain-JS — no Zod dependency. Never throws. `computeReconcileNudge` always
50
+ * returns the full shape (never null); `checkReconcileNudge` returns the
51
+ * session-start banner object or null.
52
+ *
53
+ * Cross-references:
54
+ * - `.claude/rules/verification-before-completion.md` — evidence-before-claims.
55
+ * - `scripts/lib/loop-readiness-banner.mjs` — the banner-shape template.
56
+ * - `scripts/lib/reconcile/engine.mjs` / `idempotency.mjs` / `eligibility.mjs`.
57
+ * - `scripts/lib/learnings/io.mjs` / `surface.mjs`.
58
+ * - `scripts/lib/config/reconcile.mjs` (`_parseReconcile`) — existing config key.
59
+ * - `skills/session-start/SKILL.md` Phase 4 — banner render site.
60
+ * - Issue #723.
61
+ */
62
+
63
+ import { join } from 'node:path';
64
+
65
+ import { readLearnings } from './learnings/io.mjs';
66
+ import { surfaceTopN } from './learnings/surface.mjs';
67
+ import { filterEligible } from './reconcile/eligibility.mjs';
68
+ import { loadCandidates } from './reconcile/idempotency.mjs';
69
+ import { readConfigFile } from './config/io.mjs';
70
+ import { _parseReconcile } from './config/reconcile.mjs';
71
+
72
+ /** Repo-relative location of the learnings corpus (mirrors engine.mjs's own constant — not exported there). */
73
+ const LEARNINGS_PATH = '.orchestrator/metrics/learnings.jsonl';
74
+
75
+ /** Nudge threshold (a): active-learning count with no reconcile run on record. */
76
+ export const NUDGE_MIN_LEARNINGS = 20;
77
+
78
+ /** Nudge threshold (b): new learnings accrued since the last determinable reconcile run. */
79
+ export const NUDGE_MIN_DELTA = 15;
80
+
81
+ /** Nudge threshold (c): rule-eligible learnings (type + file_paths allow-list), regardless of confidence. */
82
+ export const NUDGE_MIN_ELIGIBLE = 3;
83
+
84
+ /**
85
+ * Resolve the max `created_at` across reconcile-candidate sidecar records —
86
+ * the most recent reconcile run's timestamp, or `null` when no run is on
87
+ * record (empty store, missing file, or no parseable timestamp).
88
+ *
89
+ * @param {Array<{created_at?: unknown}>} candidates
90
+ * @returns {string|null} ISO 8601 timestamp, or null
91
+ */
92
+ function _lastRunAt(candidates) {
93
+ if (!Array.isArray(candidates) || candidates.length === 0) return null;
94
+ let maxMs = -Infinity;
95
+ for (const c of candidates) {
96
+ const t = c && typeof c.created_at === 'string' ? Date.parse(c.created_at) : NaN;
97
+ if (Number.isFinite(t) && t > maxMs) maxMs = t;
98
+ }
99
+ return Number.isFinite(maxMs) && maxMs > -Infinity ? new Date(maxMs).toISOString() : null;
100
+ }
101
+
102
+ /**
103
+ * Pure computation — always returns the full shape (never null, never throws).
104
+ * Touches disk only via the injected/default learnings + candidates readers,
105
+ * all of which are themselves never-throwing.
106
+ *
107
+ * @param {object} [opts]
108
+ * @param {string} [opts.repoRoot] — project root (defaults to process.cwd()).
109
+ * @param {Date|number} [opts.now] — injectable clock, forwarded to the active-learning filter.
110
+ * @returns {Promise<{
111
+ * totalLearnings: number,
112
+ * activeLearnings: number,
113
+ * eligibleCount: number,
114
+ * lastRunAt: string|null,
115
+ * lastRunCandidateCount: number,
116
+ * delta: number,
117
+ * nudge: boolean,
118
+ * reasons: string[],
119
+ * }>}
120
+ */
121
+ export async function computeReconcileNudge(opts = {}) {
122
+ const empty = {
123
+ totalLearnings: 0,
124
+ activeLearnings: 0,
125
+ eligibleCount: 0,
126
+ lastRunAt: null,
127
+ lastRunCandidateCount: 0,
128
+ delta: 0,
129
+ nudge: false,
130
+ reasons: [],
131
+ };
132
+
133
+ const repoRoot = typeof opts.repoRoot === 'string' && opts.repoRoot.length > 0 ? opts.repoRoot : process.cwd();
134
+ const learningsPath = join(repoRoot, LEARNINGS_PATH);
135
+
136
+ /** @type {object[]} */
137
+ let entries;
138
+ try {
139
+ const result = await readLearnings(learningsPath);
140
+ entries = Array.isArray(result?.entries) ? result.entries : [];
141
+ } catch {
142
+ return empty; // fail-open — never throw out of the probe
143
+ }
144
+
145
+ // Silent no-op: missing file, empty file, or all-malformed lines all
146
+ // collapse to entries.length === 0 here.
147
+ if (entries.length === 0) return empty;
148
+
149
+ /** @type {object[]} */
150
+ let active;
151
+ try {
152
+ // Reuse surfaceTopN's active-filter (confidence > floor, not expired)
153
+ // uncapped — pass a large `n` to get the FULL active set, not a top slice.
154
+ active = await surfaceTopN(learningsPath, Number.MAX_SAFE_INTEGER, { now: opts.now });
155
+ } catch {
156
+ active = [];
157
+ }
158
+
159
+ if (active.length === 0) return empty;
160
+
161
+ let eligibleCount;
162
+ try {
163
+ eligibleCount = filterEligible(entries).eligible.length;
164
+ } catch {
165
+ eligibleCount = 0;
166
+ }
167
+
168
+ /** @type {Array<Record<string, unknown>>} */
169
+ let candidates;
170
+ try {
171
+ candidates = loadCandidates({ repoRoot });
172
+ } catch {
173
+ candidates = [];
174
+ }
175
+
176
+ const lastRunAt = _lastRunAt(candidates);
177
+ const lastRunCandidateCount = Array.isArray(candidates) ? candidates.length : 0;
178
+ const delta = entries.length - lastRunCandidateCount;
179
+
180
+ const reasons = [];
181
+ // (a) — plenty of active learnings, but no reconcile run has ever recorded them.
182
+ if (active.length >= NUDGE_MIN_LEARNINGS && lastRunAt === null) {
183
+ reasons.push(`${active.length} active learnings with no reconcile run on record`);
184
+ }
185
+ // (b) — a determinable prior run exists, and the corpus has grown meaningfully since.
186
+ if (lastRunAt !== null && delta > NUDGE_MIN_DELTA) {
187
+ reasons.push(`${delta} new learnings since the last reconcile run`);
188
+ }
189
+ // (c) — enough rule-eligible learnings to be worth a batch, independent of confidence.
190
+ if (eligibleCount >= NUDGE_MIN_ELIGIBLE) {
191
+ reasons.push(`${eligibleCount} rule-eligible learnings`);
192
+ }
193
+
194
+ return {
195
+ totalLearnings: entries.length,
196
+ activeLearnings: active.length,
197
+ eligibleCount,
198
+ lastRunAt,
199
+ lastRunCandidateCount,
200
+ delta,
201
+ nudge: reasons.length > 0,
202
+ reasons,
203
+ };
204
+ }
205
+
206
+ /**
207
+ * Best-effort extraction of `reconcile.enabled` from an already-parsed Session
208
+ * Config object (DI/test seam — avoids re-reading CLAUDE.md when the caller
209
+ * already has it). Returns `null` when unresolvable from `config` alone, in
210
+ * which case `checkReconcileNudge` falls back to reading CLAUDE.md/AGENTS.md.
211
+ *
212
+ * @param {unknown} config
213
+ * @returns {boolean|null}
214
+ */
215
+ function _reconcileEnabledFromConfig(config) {
216
+ if (config && typeof config === 'object') {
217
+ const rc = /** @type {Record<string, unknown>} */ (config).reconcile;
218
+ if (rc && typeof rc === 'object' && typeof (/** @type {any} */ (rc).enabled) === 'boolean') {
219
+ return /** @type {any} */ (rc).enabled;
220
+ }
221
+ }
222
+ return null;
223
+ }
224
+
225
+ /**
226
+ * Check reconcile-nudge readiness and produce a session-start banner.
227
+ *
228
+ * Silent (`null`) when: no learnings corpus (missing/empty/all-malformed),
229
+ * zero active learnings, or none of the three nudge thresholds are met.
230
+ * Never throws.
231
+ *
232
+ * @param {{repoRoot: string, config?: object, now?: Date|number}} opts
233
+ * - `repoRoot`: REQUIRED absolute path to the repo root.
234
+ * - `config`: optional already-parsed Session Config (DI seam; avoids a
235
+ * second CLAUDE.md read when the caller already parsed it).
236
+ * - `now`: optional injectable clock, forwarded to the active-learning filter.
237
+ * @returns {Promise<null | {severity:'warn', message:string}>}
238
+ */
239
+ export async function checkReconcileNudge(opts = {}) {
240
+ try {
241
+ const { repoRoot, config, now } = opts;
242
+ if (!repoRoot || typeof repoRoot !== 'string') return null;
243
+
244
+ let computed;
245
+ try {
246
+ computed = await computeReconcileNudge({ repoRoot, now });
247
+ } catch {
248
+ return null;
249
+ }
250
+ if (!computed || computed.nudge !== true) return null;
251
+
252
+ let reconcileEnabled = _reconcileEnabledFromConfig(config);
253
+ if (reconcileEnabled === null) {
254
+ try {
255
+ const content = await readConfigFile(repoRoot);
256
+ reconcileEnabled = _parseReconcile(content).enabled;
257
+ } catch {
258
+ // Config unreadable — fall back to the documented config default (false)
259
+ // so the informational parenthetical still renders conservatively.
260
+ reconcileEnabled = false;
261
+ }
262
+ }
263
+
264
+ const lastRunLabel =
265
+ typeof computed.lastRunAt === 'string' && computed.lastRunAt.length >= 10
266
+ ? computed.lastRunAt.slice(0, 10)
267
+ : 'never';
268
+
269
+ const lines = [
270
+ `⚠ reconcile-nudge: ${computed.activeLearnings} active learnings, ${computed.eligibleCount} rule-eligible, ` +
271
+ `last reconcile run: ${lastRunLabel} — run /reconcile to convert learnings into rules.`,
272
+ ];
273
+ if (reconcileEnabled === false) {
274
+ lines.push(
275
+ ' (reconcile.enabled: false — banner is advisory only; /reconcile still runs on-demand.)'
276
+ );
277
+ }
278
+
279
+ return { severity: 'warn', message: lines.join('\n') };
280
+ } catch {
281
+ // Defensive catch-all — banner must never throw.
282
+ return null;
283
+ }
284
+ }