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,179 @@
1
+ /**
2
+ * backlog-scan.mjs — VCS backlog signal source for Mode-Selector (Phase B-3, issue #293).
3
+ *
4
+ * Provides `scanBacklog({limit, vcs})` returning structured counts derived from the
5
+ * project's open-issue list via `glab` (GitLab) or `gh` (GitHub). The result is fed
6
+ * into `signals.backlog` consumed by `selectMode` in `mode-selector.mjs`.
7
+ *
8
+ * Design contract:
9
+ * - Pure structural counts, never raw issue objects (keeps the selector pure).
10
+ * - Module-level cache keyed on (vcs, limit) — one VCS round-trip per session.
11
+ * - Graceful degradation: missing CLI / non-zero exit / parse failure → null.
12
+ * Never throws to the caller.
13
+ *
14
+ * Stale threshold: 30 days since `updated_at`.
15
+ *
16
+ * Dependencies:
17
+ * - VCS detection follows `skills/gitlab-ops/SKILL.md` (origin URL contains "github.com" → gh, else glab).
18
+ */
19
+
20
+ import { spawnSync } from 'node:child_process';
21
+
22
+ export const STALE_THRESHOLD_DAYS = 30;
23
+
24
+ /** Module-level cache. Keyed by JSON.stringify({vcs, limit}). */
25
+ const _cache = new Map();
26
+
27
+ /**
28
+ * Detect the VCS for the current working directory by inspecting the origin URL.
29
+ * Returns 'github' | 'gitlab' | null. Never throws.
30
+ *
31
+ * @returns {'github'|'gitlab'|null}
32
+ */
33
+ export function detectVcs() {
34
+ try {
35
+ const r = spawnSync('git', ['remote', 'get-url', 'origin'], { encoding: 'utf8' });
36
+ if (r.status !== 0) return null;
37
+ const url = String(r.stdout || '').trim();
38
+ if (!url) return null;
39
+ return url.includes('github.com') ? 'github' : 'gitlab';
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Run a CLI command and parse stdout as JSON. Returns null on any failure
47
+ * (binary missing, non-zero exit, empty stdout, unparsable JSON, non-array).
48
+ * spawnSync sets `r.status = null` and `r.error.code = 'ENOENT'` when the
49
+ * binary is absent — the `r.status !== 0` check covers that path.
50
+ *
51
+ * @param {string} bin
52
+ * @param {string[]} args
53
+ * @returns {Array|null}
54
+ */
55
+ function runJson(bin, args) {
56
+ try {
57
+ const r = spawnSync(bin, args, { encoding: 'utf8' });
58
+ if (r.status !== 0) return null;
59
+ const out = String(r.stdout || '').trim();
60
+ if (!out) return null;
61
+ const parsed = JSON.parse(out);
62
+ return Array.isArray(parsed) ? parsed : null;
63
+ } catch {
64
+ return null;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Compute days elapsed between an ISO-8601 timestamp and `now`.
70
+ * Returns Infinity if the input is unparsable so unparsable dates never count
71
+ * as "fresh".
72
+ *
73
+ * @param {string|undefined|null} iso
74
+ * @param {number} nowMs
75
+ * @returns {number}
76
+ */
77
+ function ageDays(iso, nowMs) {
78
+ if (!iso || typeof iso !== 'string') return Infinity;
79
+ const t = Date.parse(iso);
80
+ if (Number.isNaN(t)) return Infinity;
81
+ return (nowMs - t) / 86_400_000;
82
+ }
83
+
84
+ /**
85
+ * Aggregate a list of issue records into the structural backlog summary.
86
+ *
87
+ * Issue shape requirements (tolerant — missing fields are skipped):
88
+ * - `labels`: array of strings OR array of {name: string} objects
89
+ * - `updated_at` (glab) or `updatedAt` (gh): ISO-8601 timestamp
90
+ *
91
+ * @param {Array<object>} issues
92
+ * @param {number} nowMs — injected for tests
93
+ * @returns {{criticalCount: number, highCount: number, staleCount: number, byLabel: Record<string, number>, total: number}}
94
+ */
95
+ export function summarizeIssues(issues, nowMs = Date.now()) {
96
+ let criticalCount = 0;
97
+ let highCount = 0;
98
+ let staleCount = 0;
99
+ const byLabel = {};
100
+
101
+ for (const issue of issues) {
102
+ if (!issue || typeof issue !== 'object') continue;
103
+
104
+ const rawLabels = Array.isArray(issue.labels) ? issue.labels : [];
105
+ const labelNames = rawLabels
106
+ .map((l) => (typeof l === 'string' ? l : l && typeof l === 'object' ? l.name : null))
107
+ .filter((n) => typeof n === 'string' && n.length > 0);
108
+
109
+ for (const name of labelNames) {
110
+ byLabel[name] = (byLabel[name] || 0) + 1;
111
+ if (name === 'priority:critical') criticalCount += 1;
112
+ else if (name === 'priority:high') highCount += 1;
113
+ }
114
+
115
+ const updated = issue.updated_at || issue.updatedAt || null;
116
+ if (ageDays(updated, nowMs) > STALE_THRESHOLD_DAYS) staleCount += 1;
117
+ }
118
+
119
+ return {
120
+ criticalCount,
121
+ highCount,
122
+ staleCount,
123
+ byLabel,
124
+ total: issues.length,
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Scan the project's open backlog and return a structural summary suitable for
130
+ * `signals.backlog`. Caches the result per (vcs, limit) within the running process.
131
+ *
132
+ * Returns null on any of:
133
+ * - VCS cannot be detected (no git origin)
134
+ * - CLI binary missing (`glab` for gitlab, `gh` for github)
135
+ * - CLI exits non-zero or produces unparsable output
136
+ *
137
+ * Never throws.
138
+ *
139
+ * @param {{limit?: number, vcs?: 'github'|'gitlab'|null, nowMs?: number}} [opts]
140
+ * @returns {Promise<null | {criticalCount: number, highCount: number, staleCount: number, byLabel: Record<string, number>, total: number, vcs: string, limit: number}>}
141
+ */
142
+ export async function scanBacklog(opts = {}) {
143
+ const limit = Number.isInteger(opts.limit) && opts.limit > 0 ? opts.limit : 50;
144
+ // Distinguish "user did not pass vcs" (auto-detect) from "user explicitly passed
145
+ // null" (degrade). 'vcs' in opts catches the explicit-null path so callers can
146
+ // force the no-vcs branch in tests without monkey-patching detectVcs.
147
+ const vcs = 'vcs' in opts ? opts.vcs : detectVcs();
148
+ const nowMs = typeof opts.nowMs === 'number' ? opts.nowMs : Date.now();
149
+
150
+ if (vcs !== 'github' && vcs !== 'gitlab') return null;
151
+
152
+ const cacheKey = JSON.stringify({ vcs, limit });
153
+ if (_cache.has(cacheKey)) return _cache.get(cacheKey);
154
+
155
+ const bin = vcs === 'github' ? 'gh' : 'glab';
156
+ const args =
157
+ vcs === 'github'
158
+ ? ['issue', 'list', '--limit', String(limit), '--json', 'number,labels,updatedAt,state']
159
+ : ['issue', 'list', '--per-page', String(limit), '--output', 'json'];
160
+
161
+ const issues = runJson(bin, args);
162
+ if (issues === null) {
163
+ _cache.set(cacheKey, null);
164
+ return null;
165
+ }
166
+
167
+ const summary = summarizeIssues(issues, nowMs);
168
+ const result = { ...summary, vcs, limit };
169
+ _cache.set(cacheKey, result);
170
+ return result;
171
+ }
172
+
173
+ /**
174
+ * Test/coordinator helper: clear the in-process cache. Not part of the
175
+ * production surface but exported so tests don't need to reload the module.
176
+ */
177
+ export function clearBacklogCache() {
178
+ _cache.clear();
179
+ }
@@ -0,0 +1,260 @@
1
+ /**
2
+ * bootstrap-lock-freshness.mjs — #186 / #290 / #203
3
+ * Validates .orchestrator/bootstrap.lock freshness + plugin-version drift.
4
+ *
5
+ * Plain-JS validation — no Zod dependency.
6
+ * Never throws. Never mutates input.
7
+ */
8
+
9
+ import { existsSync, readFileSync } from 'node:fs';
10
+ import { join } from 'node:path';
11
+
12
+ /** Milliseconds in one day — extracted for readability + testability (#203). */
13
+ export const MS_PER_DAY = 24 * 60 * 60 * 1000;
14
+
15
+ /**
16
+ * Minimal single-level YAML parser for .orchestrator/bootstrap.lock.
17
+ * Returns an object with all key:value pairs found. Unknown fields are
18
+ * preserved. Comment lines and blank lines are ignored. Values are trimmed.
19
+ *
20
+ * @returns {Record<string, string>}
21
+ */
22
+ export function parseBootstrapLock(contents) {
23
+ if (typeof contents !== 'string') return {};
24
+
25
+ const result = {};
26
+ for (const raw of contents.split('\n')) {
27
+ const line = raw.trim();
28
+ if (!line || line.startsWith('#')) continue;
29
+ const colonIdx = line.indexOf(':');
30
+ if (colonIdx === -1) continue;
31
+ const key = line.slice(0, colonIdx).trim();
32
+ const value = line.slice(colonIdx + 1).trim();
33
+ if (key) result[key] = value;
34
+ }
35
+ return result;
36
+ }
37
+
38
+ /**
39
+ * Reads the plugin version from package.json at the given plugin root.
40
+ * Returns null if the file is absent or unparseable — never throws.
41
+ *
42
+ * @param {string} pluginRoot Absolute path to the session-orchestrator plugin root.
43
+ * @returns {string|null}
44
+ */
45
+ export function readPluginVersionFromPackageJson(pluginRoot) {
46
+ try {
47
+ const pkgPath = join(pluginRoot, 'package.json');
48
+ const raw = readFileSync(pkgPath, 'utf8');
49
+ const pkg = JSON.parse(raw);
50
+ return typeof pkg.version === 'string' ? pkg.version : null;
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Classifies a version mismatch severity between two semver strings.
58
+ *
59
+ * Rules (#290):
60
+ * - major component differs → 'alert'
61
+ * - minor or patch only differs → 'info'
62
+ * - either string is not parseable semver → 'warn' (safe fallback)
63
+ *
64
+ * @param {string} lockVer Version string from bootstrap.lock.
65
+ * @param {string} currentVer Current plugin version.
66
+ * @returns {'info'|'warn'|'alert'}
67
+ */
68
+ export function classifyVersionMismatch(lockVer, currentVer) {
69
+ // Simple semver: split on '.' and compare the major component.
70
+ const parse = (v) => v.match(/^(\d+)\.(\d+)\.(\d+)/);
71
+ const lm = parse(lockVer);
72
+ const cm = parse(currentVer);
73
+ if (!lm || !cm) return 'warn'; // not parseable — safe fallback
74
+ if (lm[1] !== cm[1]) return 'alert'; // major mismatch
75
+ return 'info'; // minor or patch only
76
+ }
77
+
78
+ /**
79
+ * Checks .orchestrator/bootstrap.lock for freshness and plugin-version drift.
80
+ *
81
+ * Tiered severity:
82
+ * info — ageDays < 30 AND (no version mismatch OR patch/minor-only mismatch)
83
+ * warn — ageDays 30..89 OR non-parseable version mismatch
84
+ * alert — ageDays >= 90 OR bootstrappedAt unparseable OR lock missing
85
+ * OR major plugin-version mismatch
86
+ *
87
+ * Legacy lock support: if `bootstrapped-at` is absent, falls back to
88
+ * `timestamp` (present in all pre-#186 locks). If both absent, severity=alert.
89
+ *
90
+ * Backward compat (#290): if the lock file predates the `plugin-version` field,
91
+ * a soft informational message is added to details but severity is NOT raised.
92
+ *
93
+ * Refresh awareness (#57): `refreshed-at` / `refreshed-plugin-version`
94
+ * (written by `scripts/lib/bootstrap-lock-refresh.mjs`) take precedence over
95
+ * `bootstrapped-at`/`timestamp` and `plugin-version` respectively when present
96
+ * — a refresh resets the freshness clock and acknowledges the current plugin
97
+ * version without disturbing the lock's original bootstrap provenance
98
+ * (`details.bootstrappedAt`/`details.pluginVersion` always report the
99
+ * ORIGINAL values, unchanged by a refresh).
100
+ *
101
+ * `details.reason` names the single highest-severity contributor driving the
102
+ * returned severity: `'missing-repoRoot' | 'missing' | 'read-error' |
103
+ * 'stale-age' | 'unparseable-timestamp' | 'version-mismatch-major' |
104
+ * 'version-mismatch-unparseable' | null` (null when severity is `'info'`).
105
+ * When both age and version-drift would independently raise severity, the
106
+ * higher-ranked contributor wins (alert > warn > info); a tie is broken in
107
+ * favor of the age reason.
108
+ *
109
+ * @param {{repoRoot: string, currentPluginVersion?: string, now?: number}} opts
110
+ * @returns {{ok: boolean, severity: 'info'|'warn'|'alert', message: string, details: object}}
111
+ */
112
+ export function checkBootstrapLockFreshness({
113
+ repoRoot,
114
+ currentPluginVersion,
115
+ now = Date.now(),
116
+ } = {}) {
117
+ if (!repoRoot) {
118
+ return {
119
+ ok: false,
120
+ severity: 'alert',
121
+ message: 'bootstrap.lock: repoRoot not provided',
122
+ details: { reason: 'missing-repoRoot' },
123
+ };
124
+ }
125
+
126
+ const lockPath = join(repoRoot, '.orchestrator', 'bootstrap.lock');
127
+
128
+ if (!existsSync(lockPath)) {
129
+ return {
130
+ ok: false,
131
+ severity: 'alert',
132
+ message: 'bootstrap.lock missing',
133
+ details: { reason: 'missing' },
134
+ };
135
+ }
136
+
137
+ let parsed;
138
+ try {
139
+ parsed = parseBootstrapLock(readFileSync(lockPath, 'utf8'));
140
+ } catch {
141
+ return {
142
+ ok: false,
143
+ severity: 'alert',
144
+ message: 'bootstrap.lock: failed to read file',
145
+ details: { reason: 'read-error' },
146
+ };
147
+ }
148
+
149
+ // Fall back to legacy `timestamp` field for pre-#186 locks. `bootstrappedAt`
150
+ // and `pluginVersion` report the ORIGINAL bootstrap provenance and are never
151
+ // overwritten by a later refresh (#57 provenance-honesty guarantee).
152
+ const bootstrappedAt = parsed['bootstrapped-at'] || parsed['timestamp'] || null;
153
+ const pluginVersion = parsed['plugin-version'] || null;
154
+
155
+ // #57 — refresh-writer fields (scripts/lib/bootstrap-lock-refresh.mjs).
156
+ // Raw, possibly null; absent on every pre-#57 lock and on any lock that has
157
+ // never been refreshed.
158
+ const refreshedAt = parsed['refreshed-at'] || null;
159
+ const refreshedPluginVersion = parsed['refreshed-plugin-version'] || null;
160
+
161
+ // Age-source precedence (#57): refreshed-at → bootstrapped-at → timestamp.
162
+ // A refresh resets the freshness clock without touching bootstrappedAt.
163
+ const ageSourceAt = refreshedAt || bootstrappedAt;
164
+
165
+ let ageDays = null;
166
+ if (ageSourceAt) {
167
+ const ts = Date.parse(ageSourceAt);
168
+ if (!Number.isNaN(ts)) {
169
+ ageDays = Math.floor((now - ts) / MS_PER_DAY);
170
+ }
171
+ }
172
+
173
+ // Version-drift source precedence (#57): refreshed-plugin-version → plugin-version.
174
+ // A refresh acknowledges the current plugin version without touching pluginVersion.
175
+ const effectivePluginVersion = refreshedPluginVersion || pluginVersion;
176
+
177
+ // Backward compat (#290): lock predates plugin-version field — soft signal only.
178
+ // #57: a refresh that stamps refreshed-plugin-version resolves this (the
179
+ // effective version is now comparable), so this check uses the same
180
+ // refreshed-plugin-version → plugin-version precedence as the mismatch check.
181
+ const legacyLockNoPluinVersion =
182
+ typeof currentPluginVersion === 'string' && effectivePluginVersion === null;
183
+
184
+ let versionMismatch = false;
185
+ let versionMismatchSeverity = null; // 'info' | 'warn' | 'alert' — null when no mismatch
186
+
187
+ if (
188
+ typeof currentPluginVersion === 'string' &&
189
+ typeof effectivePluginVersion === 'string' &&
190
+ effectivePluginVersion !== currentPluginVersion
191
+ ) {
192
+ versionMismatch = true;
193
+ versionMismatchSeverity = classifyVersionMismatch(effectivePluginVersion, currentPluginVersion);
194
+ }
195
+
196
+ let severity = 'info';
197
+
198
+ // #57 — reason-aware tiering. `reason` names the single HIGHEST-severity
199
+ // contributor (age vs. version-drift) so downstream prose can recommend the
200
+ // right remediation instead of always pointing at --retroactive. Rank:
201
+ // alert=2, warn=1, info=0, none=-1. `considerReason` only replaces the
202
+ // current pick on a STRICT rank increase, so calling the age contributor
203
+ // before the version contributor implements "tie → age reason".
204
+ let reason = null;
205
+ let reasonRank = -1;
206
+ const rank = (s) => (s === 'alert' ? 2 : s === 'warn' ? 1 : s === 'info' ? 0 : -1);
207
+ const considerReason = (candidateSeverity, candidateReason) => {
208
+ const r = rank(candidateSeverity);
209
+ if (r > reasonRank) {
210
+ reasonRank = r;
211
+ reason = candidateReason;
212
+ }
213
+ };
214
+
215
+ // Age-based escalation.
216
+ if (ageDays !== null && ageDays >= 30) {
217
+ severity = 'warn';
218
+ considerReason('warn', 'stale-age');
219
+ }
220
+ if (ageDays !== null && ageDays >= 90) {
221
+ severity = 'alert';
222
+ considerReason('alert', 'stale-age');
223
+ }
224
+ if (ageDays === null) {
225
+ severity = 'alert';
226
+ considerReason('alert', 'unparseable-timestamp');
227
+ }
228
+
229
+ // Version-mismatch escalation (only when mismatch detected).
230
+ if (versionMismatch && versionMismatchSeverity === 'warn') {
231
+ if (severity === 'info') severity = 'warn';
232
+ considerReason('warn', 'version-mismatch-unparseable');
233
+ }
234
+ if (versionMismatch && versionMismatchSeverity === 'alert') {
235
+ severity = 'alert';
236
+ considerReason('alert', 'version-mismatch-major');
237
+ }
238
+ // 'info'-classified mismatches (minor/patch) do NOT raise severity beyond
239
+ // whatever age already determined, and do not contribute a reason.
240
+
241
+ return {
242
+ ok: severity === 'info',
243
+ severity,
244
+ message: `bootstrap.lock: age=${ageDays ?? 'unknown'}d, plugin-version=${pluginVersion ?? 'unknown'} (current=${currentPluginVersion ?? 'unknown'})`,
245
+ details: {
246
+ ageDays,
247
+ pluginVersion,
248
+ currentPluginVersion: currentPluginVersion ?? null,
249
+ bootstrappedAt,
250
+ refreshedAt,
251
+ refreshedPluginVersion,
252
+ versionMismatch,
253
+ versionMismatchSeverity,
254
+ legacyLock: legacyLockNoPluinVersion
255
+ ? 'lock predates plugin-version field; consider /bootstrap --refresh-lock to stamp a current plugin-version reference'
256
+ : null,
257
+ reason,
258
+ },
259
+ };
260
+ }
@@ -0,0 +1,186 @@
1
+ /**
2
+ * bootstrap-lock-refresh.mjs — #57
3
+ * Provenance-honest `.orchestrator/bootstrap.lock` refresh writer.
4
+ *
5
+ * `checkBootstrapLockFreshness()` (bootstrap-lock-freshness.mjs) can only ever
6
+ * *diagnose* a stale/drifted lock — it is read-only by design. Before #57 the
7
+ * only remediation the surrounding prose offered was `/bootstrap --retroactive`,
8
+ * which is a no-op once the lock already has valid `version`/`tier` fields
9
+ * (see the Retroactive Flow's idempotency guard in `skills/bootstrap/SKILL.md`)
10
+ * — the operator would re-run the recommended command and see nothing change.
11
+ *
12
+ * `refreshBootstrapLock()` closes that gap: it acknowledges the current
13
+ * plugin version and resets the freshness clock by appending/updating exactly
14
+ * two fields — `refreshed-at` and `refreshed-plugin-version` — while leaving
15
+ * every other line of the lock file byte-identical. The ORIGINAL bootstrap
16
+ * provenance (`bootstrapped-at`/`timestamp`, `plugin-version`, `tier`,
17
+ * `archetype`, `source`, …) is never rewritten. This is the load-bearing
18
+ * guarantee: a refresh is an acknowledgement, not a re-bootstrap.
19
+ *
20
+ * Plain-JS — no Zod dependency. Never throws; every failure path returns a
21
+ * structured `{ ok: false, reason, message }` object. Never fabricates a lock
22
+ * — a missing or structurally invalid lock is refused, not synthesized.
23
+ */
24
+
25
+ import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from 'node:fs';
26
+ import { join, dirname } from 'node:path';
27
+ import { randomBytes } from 'node:crypto';
28
+ import { parseBootstrapLock } from './bootstrap-lock-freshness.mjs';
29
+
30
+ const PRECONDITION_MESSAGE_SUFFIX =
31
+ 'run /bootstrap or /bootstrap --retroactive first.';
32
+
33
+ /**
34
+ * Locate the (trimmed, comment-skipping) line index for a given top-level
35
+ * lock key, mirroring {@link parseBootstrapLock}'s own key-extraction rules
36
+ * so upsert targets the same line parseBootstrapLock would report for `key`.
37
+ *
38
+ * @param {string[]} lines
39
+ * @param {string} key
40
+ * @returns {number} 0-based index, or -1 when not found.
41
+ */
42
+ function findKeyLineIndex(lines, key) {
43
+ for (let i = 0; i < lines.length; i++) {
44
+ const trimmed = lines[i].trim();
45
+ if (!trimmed || trimmed.startsWith('#')) continue;
46
+ const colonIdx = trimmed.indexOf(':');
47
+ if (colonIdx === -1) continue;
48
+ if (trimmed.slice(0, colonIdx).trim() === key) return i;
49
+ }
50
+ return -1;
51
+ }
52
+
53
+ /**
54
+ * Replace the line for `key` in place, or append a new `key: value` line
55
+ * when absent. Mutates `lines` in place; every other line is left untouched.
56
+ *
57
+ * @param {string[]} lines
58
+ * @param {string} key
59
+ * @param {string} value
60
+ */
61
+ function upsertKeyLine(lines, key, value) {
62
+ const formatted = `${key}: ${value}`;
63
+ const idx = findKeyLineIndex(lines, key);
64
+ if (idx === -1) {
65
+ lines.push(formatted);
66
+ } else {
67
+ lines[idx] = formatted;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Strip CR/LF from a value before it is composed into an upserted `key:
73
+ * value` lock line — defense-in-depth: a malformed `currentPluginVersion`
74
+ * (or, symmetrically, the generated ISO timestamp) containing an embedded
75
+ * newline could otherwise inject an arbitrary forged `key: value` line into
76
+ * the lock file via {@link upsertKeyLine}. Never throws; non-string input is
77
+ * returned unchanged (callers already guard the string check).
78
+ *
79
+ * @param {string} value
80
+ * @returns {string}
81
+ */
82
+ function sanitizeLockValue(value) {
83
+ return typeof value === 'string' ? value.replace(/[\r\n]/g, '') : value;
84
+ }
85
+
86
+ /**
87
+ * Atomically write `content` to `filePath` via tmp-file + renameSync — the
88
+ * same crash-safe pattern used by `scripts/lib/io.mjs#writeJsonAtomicSync`
89
+ * and `scripts/lib/session-lock.mjs`, adapted for raw (non-JSON) text.
90
+ *
91
+ * @param {string} filePath
92
+ * @param {string} content
93
+ */
94
+ function writeTextAtomicSync(filePath, content) {
95
+ const dir = dirname(filePath);
96
+ mkdirSync(dir, { recursive: true });
97
+ const tmpSuffix = randomBytes(6).toString('hex');
98
+ const tmpFile = join(dir, `.bootstrap-lock-refresh.${tmpSuffix}.tmp`);
99
+ writeFileSync(tmpFile, content, { encoding: 'utf8' });
100
+ renameSync(tmpFile, filePath);
101
+ }
102
+
103
+ /**
104
+ * Refreshes `.orchestrator/bootstrap.lock` in place: acknowledges the current
105
+ * plugin version and resets the freshness clock without disturbing the
106
+ * lock's original bootstrap provenance.
107
+ *
108
+ * Precondition: the lock must exist and parse with non-empty `version` and
109
+ * `tier` fields. A missing or structurally invalid lock is refused — this
110
+ * function never fabricates a lock (that is `/bootstrap` or
111
+ * `/bootstrap --retroactive`'s job, not this one's).
112
+ *
113
+ * Write contract (provenance honesty, #57): every line of the existing lock
114
+ * is preserved byte-for-byte EXCEPT the `refreshed-at` line (always written)
115
+ * and the `refreshed-plugin-version` line (written only when
116
+ * `currentPluginVersion` is a non-empty string). Re-running twice replaces
117
+ * those two lines in place — it never duplicates them (idempotent).
118
+ *
119
+ * @param {{repoRoot: string, currentPluginVersion?: string, now?: number}} opts
120
+ * @returns {{ok: true, path: string, refreshedAt: string, refreshedPluginVersion: string|null}
121
+ * | {ok: false, reason: 'missing-or-invalid', message: string}}
122
+ */
123
+ export function refreshBootstrapLock({ repoRoot, currentPluginVersion, now = Date.now() } = {}) {
124
+ if (!repoRoot) {
125
+ return {
126
+ ok: false,
127
+ reason: 'missing-or-invalid',
128
+ message: `bootstrap.lock: repoRoot not provided — ${PRECONDITION_MESSAGE_SUFFIX}`,
129
+ };
130
+ }
131
+
132
+ const lockPath = join(repoRoot, '.orchestrator', 'bootstrap.lock');
133
+
134
+ if (!existsSync(lockPath)) {
135
+ return {
136
+ ok: false,
137
+ reason: 'missing-or-invalid',
138
+ message: `bootstrap.lock missing — ${PRECONDITION_MESSAGE_SUFFIX}`,
139
+ };
140
+ }
141
+
142
+ let raw;
143
+ try {
144
+ raw = readFileSync(lockPath, 'utf8');
145
+ } catch {
146
+ return {
147
+ ok: false,
148
+ reason: 'missing-or-invalid',
149
+ message: `bootstrap.lock: failed to read file — ${PRECONDITION_MESSAGE_SUFFIX}`,
150
+ };
151
+ }
152
+
153
+ const parsed = parseBootstrapLock(raw);
154
+ if (!parsed['version'] || !parsed['tier']) {
155
+ return {
156
+ ok: false,
157
+ reason: 'missing-or-invalid',
158
+ message: `bootstrap.lock: missing required version/tier fields — ${PRECONDITION_MESSAGE_SUFFIX}`,
159
+ };
160
+ }
161
+
162
+ const hadTrailingNewline = raw.endsWith('\n');
163
+ const lines = raw.split('\n');
164
+ if (hadTrailingNewline) lines.pop(); // drop the implicit empty tail element from split()
165
+
166
+ const refreshedAt = sanitizeLockValue(new Date(now).toISOString());
167
+ const refreshedPluginVersion =
168
+ typeof currentPluginVersion === 'string' && currentPluginVersion.length > 0
169
+ ? sanitizeLockValue(currentPluginVersion)
170
+ : null;
171
+
172
+ upsertKeyLine(lines, 'refreshed-at', refreshedAt);
173
+ if (refreshedPluginVersion !== null) {
174
+ upsertKeyLine(lines, 'refreshed-plugin-version', refreshedPluginVersion);
175
+ }
176
+
177
+ const newContent = lines.join('\n') + (hadTrailingNewline ? '\n' : '');
178
+ writeTextAtomicSync(lockPath, newContent);
179
+
180
+ return {
181
+ ok: true,
182
+ path: lockPath,
183
+ refreshedAt,
184
+ refreshedPluginVersion,
185
+ };
186
+ }