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,512 @@
1
+ /**
2
+ * file-lock.mjs — shared POSIX file-lock primitive (issue #630).
3
+ *
4
+ * Consolidates the five self-contained tmp + linkSync lock implementations that
5
+ * had accreted across the codebase (agent-status.mjs, memory-proposals/store.mjs,
6
+ * and the state-lock / staging-fence / session-lock blocks of session-lock.mjs).
7
+ * Each copy independently re-implemented the same skeleton:
8
+ *
9
+ * 1. atomic create-or-fail via `linkSync(tmp, lock)` (POSIX mutex);
10
+ * 2. on EEXIST → read + parse the existing body;
11
+ * 3. same-host + dead-PID (or unparseable) → atomic override + WARN;
12
+ * 4. live holder OR cross-host → poll until a deadline;
13
+ * 5. owner-guarded release.
14
+ *
15
+ * This module is the single home for that skeleton. It is a PURE primitive:
16
+ * it imports ONLY scripts/lib/io.mjs (for writeJsonAtomicSync) and Node stdlib.
17
+ * It does NOT import session-lock.mjs — instead `isPidAliveOnHost` is MOVED here
18
+ * and re-exported from session-lock.mjs, so the dependency edge points
19
+ * file-lock → io, never the reverse (no import cycle).
20
+ *
21
+ * Behavior-preservation contract (issue #630 is a PURE dedup refactor):
22
+ * - Lock file paths, body shapes, timeouts, poll cadences, override channels
23
+ * (console.warn vs process.stderr.write), and result-object reasons are
24
+ * reproduced EXACTLY by the call-site options. The primitive carries no
25
+ * opinion of its own — every divergence between the five copies is a knob.
26
+ * - Cross-host locks are NEVER auto-overridden (PSA-003 hard invariant).
27
+ * - Overrides always go through writeJsonAtomicSync (tmp + renameSync).
28
+ *
29
+ * No external dependencies — Node 20+ stdlib + io.mjs only.
30
+ */
31
+
32
+ import fs from 'node:fs';
33
+ import os from 'node:os';
34
+ import path from 'node:path';
35
+ import crypto from 'node:crypto';
36
+
37
+ import { writeJsonAtomicSync } from './io.mjs';
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // PID liveness (moved here from session-lock.mjs in #630)
41
+ // ---------------------------------------------------------------------------
42
+
43
+ /**
44
+ * Check whether a PID corresponds to a live process on this host.
45
+ * Returns true when the process exists (even if we lack kill permission).
46
+ * Returns false when the process does not exist (ESRCH).
47
+ *
48
+ * @forensic
49
+ * This is a SAME-HOST PID liveness probe (POSIX `process.kill(pid, 0)`,
50
+ * signal-0). It is NOT the discovery-path liveness check. Since Epic #583
51
+ * (W1-D1/W1-D4) the discovery decision tree uses heartbeat-age via
52
+ * `isLockLive` (session-lock.mjs) instead, because the `pid` recorded on a
53
+ * session.lock is the *ephemeral hook subprocess* PID — that process exits
54
+ * ~500ms after the SessionStart hook returns, so a signal-0 probe of it reports
55
+ * "dead" while the semantic owner (the Claude harness) is still alive (the D2
56
+ * production defect). Cross-host callers MUST pass `null` for the liveness slot
57
+ * and never call this function. Remaining same-host callers use it only for the
58
+ * short-lived state-lock / staging-fence-lock / agent-status / memory-proposals
59
+ * stale-override path, where the recorded PID IS the live writer's PID.
60
+ *
61
+ * @param {number} pid Process ID to probe via the POSIX signal-0 trick.
62
+ * @returns {boolean} true when a process with the given PID exists; false
63
+ * when no such process exists or the probe failed.
64
+ *
65
+ * @remarks
66
+ * PID-recycle trade-off (#560 Q3 L2 — deep-2115 session-reviewer):
67
+ *
68
+ * - On Unix, `process.kill(pid, 0)` returns true if ANY process exists with
69
+ * that PID, including a recycled one. The kernel does not distinguish the
70
+ * original lock-holder from a fresh process that happens to have inherited
71
+ * the same numeric PID after the lock-holder's death.
72
+ * - On a long session where the lock-holder died abnormally AND the OS reused
73
+ * its PID before TTL expiry, this function returns `true` even though the
74
+ * original lock-holder is gone. The lock is then perceived as held by a
75
+ * "live" process that is actually unrelated to the original writer.
76
+ * - Impact: a stale lock waits the full TTL before being reclaimed by the
77
+ * timeout path. Fail-open posture means correctness is preserved — only
78
+ * operational latency is affected, and the missed race-detection is
79
+ * bounded to one incident per stale-lock event.
80
+ * - Trade-off accepted: the alternative would require recording a UUID or
81
+ * boot-nonce alongside the PID and comparing both fields on stale-detection.
82
+ * That adds complexity to the lock body, parse path, and every
83
+ * acquire/release branch for a single-missed-race-per-incident impact.
84
+ * Current trade-off is operationally sound at our scale.
85
+ */
86
+ export function isPidAliveOnHost(pid) {
87
+ try {
88
+ process.kill(pid, 0);
89
+ return true;
90
+ } catch (err) {
91
+ if (err.code === 'ESRCH') return false;
92
+ if (err.code === 'EPERM') return true; // process exists, we just lack permission
93
+ return false;
94
+ }
95
+ }
96
+
97
+ // ---------------------------------------------------------------------------
98
+ // Internal helpers
99
+ // ---------------------------------------------------------------------------
100
+
101
+ /**
102
+ * Parse a lock-file body. Returns null on any malformed input. The required
103
+ * fields are configurable so a single parser serves both the 3-field shape
104
+ * ({pid, host, acquiredAt}) used by agent-status / memory-proposals and the
105
+ * 4-field shape ({pid, host, acquiredAt, holder}) used by the state-lock /
106
+ * staging-fence locks. The primitive only ever inspects `pid` and `host`, so
107
+ * the minimal contract enforced here is `{ pid: number, host: string }`.
108
+ *
109
+ * @param {string} raw
110
+ * @returns {object|null}
111
+ */
112
+ function parseBody(raw) {
113
+ try {
114
+ const obj = JSON.parse(raw);
115
+ if (
116
+ obj &&
117
+ typeof obj === 'object' &&
118
+ !Array.isArray(obj) &&
119
+ typeof obj.pid === 'number' &&
120
+ typeof obj.host === 'string'
121
+ ) {
122
+ return obj;
123
+ }
124
+ return null;
125
+ } catch {
126
+ return null;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Resolve whether an existing lock body is stale relative to the requested
132
+ * stale-check policy. Cross-host bodies are NEVER stale here (PSA-003 — we
133
+ * cannot signal a remote process). Returns a `{ stale, reason }` pair.
134
+ *
135
+ * @param {object} existing — parsed lock body (non-null).
136
+ * @param {'pid'|'heartbeat'|'mtime'|'none'} staleCheck
137
+ * @param {number|undefined} staleMs — TTL for heartbeat/mtime checks.
138
+ * @param {string|undefined} lockPath — needed for mtime stat.
139
+ * @returns {{ stale: boolean, reason: string }}
140
+ */
141
+ function isExistingStale(existing, staleCheck, staleMs, lockPath) {
142
+ const sameHost = existing.host === os.hostname();
143
+ if (!sameHost) return { stale: false, reason: 'cross-host' };
144
+
145
+ if (staleCheck === 'none') return { stale: false, reason: 'live' };
146
+
147
+ if (staleCheck === 'pid') {
148
+ const pidIsNumber = typeof existing.pid === 'number';
149
+ const dead = pidIsNumber && !isPidAliveOnHost(existing.pid);
150
+ return dead
151
+ ? { stale: true, reason: `dead pid ${existing.pid}` }
152
+ : { stale: false, reason: 'live' };
153
+ }
154
+
155
+ if (staleCheck === 'heartbeat') {
156
+ const ts = Date.parse(existing.acquiredAt ?? existing.lastHeartbeat ?? '');
157
+ if (!Number.isFinite(ts) || typeof staleMs !== 'number') {
158
+ return { stale: false, reason: 'live' };
159
+ }
160
+ const age = Date.now() - ts;
161
+ return age > staleMs
162
+ ? { stale: true, reason: `heartbeat age ${age}ms > ${staleMs}ms` }
163
+ : { stale: false, reason: 'live' };
164
+ }
165
+
166
+ if (staleCheck === 'mtime') {
167
+ if (typeof staleMs !== 'number' || !lockPath) {
168
+ return { stale: false, reason: 'live' };
169
+ }
170
+ let mtimeMs;
171
+ try {
172
+ mtimeMs = fs.statSync(lockPath).mtimeMs;
173
+ } catch {
174
+ return { stale: false, reason: 'live' };
175
+ }
176
+ const age = Date.now() - mtimeMs;
177
+ return age > staleMs
178
+ ? { stale: true, reason: `mtime age ${Math.round(age)}ms > ${staleMs}ms` }
179
+ : { stale: false, reason: 'live' };
180
+ }
181
+
182
+ return { stale: false, reason: 'live' };
183
+ }
184
+
185
+ /**
186
+ * Atomically create the lock file via tmp + linkSync (POSIX create-or-fail).
187
+ * The winner creates the hard link; every loser gets EEXIST.
188
+ *
189
+ * @param {string} lockPath
190
+ * @param {object} body
191
+ * @param {{ indent?: number|null, tmpPrefix: string }} fmt
192
+ * @returns {{ ok: true } | { ok: false, reason: 'exists' } | { ok: false, reason: 'fs-error', error: string }}
193
+ */
194
+ function createExclusive(lockPath, body, fmt) {
195
+ const dir = path.dirname(lockPath);
196
+ let tmpFile;
197
+ try {
198
+ fs.mkdirSync(dir, { recursive: true });
199
+ const suffix = crypto.randomBytes(8).toString('hex');
200
+ tmpFile = path.join(dir, `${fmt.tmpPrefix}.create.tmp.${suffix}`);
201
+ fs.writeFileSync(tmpFile, serializeBody(body, fmt.indent), 'utf8');
202
+ } catch (err) {
203
+ if (tmpFile) {
204
+ try { fs.unlinkSync(tmpFile); } catch { /* best-effort */ }
205
+ }
206
+ return { ok: false, reason: 'fs-error', error: err?.message ?? String(err) };
207
+ }
208
+
209
+ try {
210
+ fs.linkSync(tmpFile, lockPath);
211
+ return { ok: true };
212
+ } catch (err) {
213
+ if (err.code === 'EEXIST') return { ok: false, reason: 'exists' };
214
+ return { ok: false, reason: 'fs-error', error: err?.message ?? String(err) };
215
+ } finally {
216
+ try { fs.unlinkSync(tmpFile); } catch { /* best-effort */ }
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Serialize a lock body. `indent === null` → compact (no whitespace, no
222
+ * trailing newline) to reproduce agent-status / memory-proposals exactly.
223
+ * Numeric indent → pretty-printed with a trailing newline (state / staging).
224
+ *
225
+ * @param {object} body
226
+ * @param {number|null} indent
227
+ * @returns {string}
228
+ */
229
+ function serializeBody(body, indent) {
230
+ if (indent === null || indent === undefined) {
231
+ return JSON.stringify(body);
232
+ }
233
+ return JSON.stringify(body, null, indent) + '\n';
234
+ }
235
+
236
+ // ---------------------------------------------------------------------------
237
+ // Exported primitive
238
+ // ---------------------------------------------------------------------------
239
+
240
+ /**
241
+ * Attempt ONE exclusive lock acquisition pass.
242
+ *
243
+ * On EEXIST the existing body is read and parsed; if it is stale per the
244
+ * configured `staleCheck` policy (or unparseable), the lock is atomically
245
+ * overridden via writeJsonAtomicSync and a WARN is emitted. A live holder or a
246
+ * cross-host body returns `{ acquired: false, reason: 'held' }`.
247
+ *
248
+ * The `signalVanished` knob reproduces memory-proposals/store.mjs's distinct
249
+ * third state: when the lock file disappears between the EEXIST and the read
250
+ * (concurrent release race), `{ acquired: false, reason: 'vanished' }` is
251
+ * returned so the caller defers WITHOUT any override side-effect (preserves the
252
+ * C9 8-worker double-holder invariant). When `signalVanished` is false the
253
+ * ENOENT-on-read race collapses into `{ acquired: false, reason: 'held' }`
254
+ * (state-lock / staging-fence / agent-status behavior).
255
+ *
256
+ * @param {string} lockPath Absolute path to the lock file.
257
+ * @param {object} [opts]
258
+ * @param {'pid'|'heartbeat'|'mtime'|'none'} [opts.staleCheck='pid']
259
+ * @param {number} [opts.staleMs] — TTL for heartbeat/mtime staleCheck.
260
+ * @param {object} [opts.meta] — extra fields merged into the body.
261
+ * @param {string} [opts.holder] — holder label (added to body when set).
262
+ * @param {boolean} [opts.signalVanished=false]
263
+ * @param {number|null} [opts.indent] — body serialization: null=compact, number=pretty.
264
+ * @param {string} [opts.tmpPrefix] — tmp-file prefix for create + override.
265
+ * @param {(msg: string) => void} [opts.warn] — override-WARN sink (default console.warn).
266
+ * @param {(reason: string, lockPath: string, existing: object|null) => string} [opts.warnMessage]
267
+ * — build the WARN string from the stale reason, path, and the parsed
268
+ * existing body (null when the body was unparseable). The reason token is
269
+ * 'unparseable body' | 'dead pid <n>' | 'heartbeat age ...' | 'mtime age ...'.
270
+ * @returns {{ acquired: true, body: object }
271
+ * | { acquired: false, reason: 'held'|'vanished'|'fs-error', existing?: object|null, error?: string }}
272
+ */
273
+ export function tryAcquireFileLock(lockPath, opts = {}) {
274
+ const {
275
+ staleCheck = 'pid',
276
+ staleMs,
277
+ meta,
278
+ holder,
279
+ signalVanished = false,
280
+ indent = null,
281
+ tmpPrefix = '.file.lock',
282
+ warn = (msg) => console.warn(msg),
283
+ warnMessage = (reason, lp) => `⚠ file-lock: overriding stale lock (${reason}) at ${lp}`,
284
+ } = opts;
285
+
286
+ // The stale-override path writes via writeJsonAtomicSync. The original copies
287
+ // diverged on override formatting: agent-status / state-lock / staging-fence
288
+ // override pretty-printed (indent 2) regardless of create format, while
289
+ // memory-proposals overrode compact. writeJsonAtomicSync always appends a
290
+ // trailing newline; with overrideIndent:null it emits `JSON.stringify(body)\n`
291
+ // (compact + newline). The trailing newline is parse-transparent (JSON.parse
292
+ // ignores it), so this is behavior-identical for every consumer.
293
+ const overrideIndent = opts.overrideIndent !== undefined ? opts.overrideIndent : (indent ?? 2);
294
+
295
+ const body = {
296
+ pid: process.pid,
297
+ host: os.hostname(),
298
+ acquiredAt: new Date().toISOString(),
299
+ ...(meta && typeof meta === 'object' ? meta : {}),
300
+ };
301
+ if (typeof holder === 'string' && holder.length > 0) {
302
+ body.holder = holder;
303
+ }
304
+
305
+ const fmt = { indent, tmpPrefix };
306
+
307
+ let created;
308
+ try {
309
+ created = createExclusive(lockPath, body, fmt);
310
+ } catch (err) {
311
+ return { acquired: false, reason: 'fs-error', error: err?.message ?? String(err) };
312
+ }
313
+ if (created.ok) return { acquired: true, body };
314
+ if (created.reason === 'fs-error') {
315
+ return { acquired: false, reason: 'fs-error', error: created.error };
316
+ }
317
+
318
+ // reason === 'exists' — inspect the holder.
319
+ let raw;
320
+ try {
321
+ raw = fs.readFileSync(lockPath, 'utf8');
322
+ } catch (err) {
323
+ if (err.code === 'ENOENT') {
324
+ // Lock vanished between linkSync-EEXIST and read — a concurrent
325
+ // release-then-reacquire race. We lost; never override.
326
+ return signalVanished
327
+ ? { acquired: false, reason: 'vanished' }
328
+ : { acquired: false, reason: 'held', existing: null };
329
+ }
330
+ return { acquired: false, reason: 'fs-error', error: err?.message ?? String(err) };
331
+ }
332
+
333
+ const existing = parseBody(raw);
334
+
335
+ // Unparseable contents → treat as stale and override (only safe move).
336
+ if (existing === null) {
337
+ warn(warnMessage('unparseable body', lockPath, null));
338
+ const replaced = writeJsonAtomicSync(lockPath, body, { tmpPrefix, indent: overrideIndent });
339
+ if (replaced.ok) return { acquired: true, body };
340
+ return { acquired: false, reason: 'fs-error', error: replaced.error };
341
+ }
342
+
343
+ const { stale, reason } = isExistingStale(existing, staleCheck, staleMs, lockPath);
344
+ if (stale) {
345
+ warn(warnMessage(reason, lockPath, existing));
346
+ const replaced = writeJsonAtomicSync(lockPath, body, { tmpPrefix, indent: overrideIndent });
347
+ if (replaced.ok) return { acquired: true, body };
348
+ return { acquired: false, reason: 'fs-error', error: replaced.error };
349
+ }
350
+
351
+ // Live holder OR cross-host → caller polls.
352
+ return { acquired: false, reason: 'held', existing };
353
+ }
354
+
355
+ /**
356
+ * Release a lock file.
357
+ *
358
+ * With `ownerGuard: true` (default) the file is unlinked ONLY when we own it:
359
+ * the recorded `holder` matches `opts.holder` (when provided), else PID + host
360
+ * match. This reproduces the agent-status / state-lock / staging-fence owner
361
+ * guard (PSA-003: never delete a lock another holder owns).
362
+ *
363
+ * With `ownerGuard: false` the file is unlinked unconditionally, ENOENT
364
+ * ignored — reproducing memory-proposals/store.mjs's `releaseProposalsLock`.
365
+ *
366
+ * @param {string} lockPath
367
+ * @param {object} [opts]
368
+ * @param {string} [opts.holder] — expected holder for the owner guard.
369
+ * @param {boolean} [opts.ownerGuard=true]
370
+ * @param {(errToken: string) => void} [opts.warn] — sink for unexpected fs
371
+ * errors on the ownerGuard:false path. Receives the raw
372
+ * `err.code ?? err.message` token; the call-site formats the message.
373
+ * @returns {{ ok: true }
374
+ * | { ok: false, reason: 'not-found'|'not-owner'|'fs-error', error?: string }}
375
+ */
376
+ export function releaseFileLock(lockPath, opts = {}) {
377
+ const { holder, ownerGuard = true, warn } = opts;
378
+
379
+ if (ownerGuard === false) {
380
+ // Unconditional unlink; ENOENT ignored. Other fs errors surfaced via warn.
381
+ // The warn callback receives the raw `err.code ?? err.message` token so the
382
+ // call-site controls the surrounding message format.
383
+ try {
384
+ fs.unlinkSync(lockPath);
385
+ return { ok: true };
386
+ } catch (err) {
387
+ if (err.code === 'ENOENT') return { ok: true };
388
+ if (typeof warn === 'function') warn(err.code ?? err.message);
389
+ return { ok: false, reason: 'fs-error', error: err?.message ?? String(err) };
390
+ }
391
+ }
392
+
393
+ let raw;
394
+ try {
395
+ raw = fs.readFileSync(lockPath, 'utf8');
396
+ } catch (err) {
397
+ if (err.code === 'ENOENT') return { ok: false, reason: 'not-found' };
398
+ return { ok: false, reason: 'fs-error', error: err?.message ?? String(err) };
399
+ }
400
+
401
+ const lock = parseBody(raw);
402
+ if (lock === null) {
403
+ // Unparseable — refuse to delete; some other process may be writing now.
404
+ return { ok: false, reason: 'not-owner' };
405
+ }
406
+
407
+ const expectedHolder = typeof holder === 'string' && holder.length > 0 ? holder : null;
408
+ const ownerMatch = expectedHolder !== null
409
+ ? lock.holder === expectedHolder
410
+ : lock.pid === process.pid && lock.host === os.hostname();
411
+
412
+ if (!ownerMatch) return { ok: false, reason: 'not-owner' };
413
+
414
+ try {
415
+ fs.unlinkSync(lockPath);
416
+ return { ok: true };
417
+ } catch (err) {
418
+ if (err.code === 'ENOENT') return { ok: false, reason: 'not-found' };
419
+ return { ok: false, reason: 'fs-error', error: err?.message ?? String(err) };
420
+ }
421
+ }
422
+
423
+ /**
424
+ * Synchronous busy-wait sleep via Atomics.wait on a throwaway buffer — a
425
+ * portable, CPU-cheap sleep used by the `sync` variant of withFileLock (the
426
+ * agent-status no-throw hot path that must not leak async timers). Falls back
427
+ * to a tight spin if SharedArrayBuffer is unavailable.
428
+ * @param {number} ms
429
+ */
430
+ function sleepSync(ms) {
431
+ const end = Date.now() + ms;
432
+ try {
433
+ const sab = new Int32Array(new SharedArrayBuffer(4));
434
+ Atomics.wait(sab, 0, 0, ms);
435
+ } catch {
436
+ while (Date.now() < end) { /* spin */ }
437
+ }
438
+ }
439
+
440
+ /**
441
+ * Async poll-sleep. Promise-returning so the loop does not block the event loop.
442
+ * @param {number} ms
443
+ */
444
+ function delay(ms) {
445
+ return new Promise((resolve) => setTimeout(resolve, ms));
446
+ }
447
+
448
+ /**
449
+ * High-level wrapper: acquire `lockPath` (polling until a deadline), run `fn`,
450
+ * and release in a finally. The acquire pass delegates to tryAcquireFileLock,
451
+ * so every stale-override / cross-host / vanished knob is honored here too.
452
+ *
453
+ * Returns the value of `fn()`. On acquire timeout or fs-error returns a
454
+ * structured failure (callers that want a throw should branch on it — the
455
+ * existing session-lock wrappers keep their own throw-translation). `fn` errors
456
+ * propagate after the lock is released.
457
+ *
458
+ * @param {string} lockPath
459
+ * @param {(body: object) => (T | Promise<T>)} fn — receives the acquired body.
460
+ * @param {object} [opts]
461
+ * @param {number} [opts.timeoutMs=10000]
462
+ * @param {number} [opts.pollMs=100]
463
+ * @param {boolean} [opts.sync=false] — true → synchronous busy-wait poll +
464
+ * synchronous fn (agent-status variant). false → async poll.
465
+ * @param {boolean} [opts.ownerGuard=true] — passed to releaseFileLock.
466
+ * @param {...*} [opts.acquireOpts] — remaining keys forwarded to tryAcquireFileLock.
467
+ * @returns {Promise<{ ok: true, value: T }
468
+ * | { ok: false, reason: 'timeout'|'fs-error', error?: string, existing?: object|null }>}
469
+ * @template T
470
+ */
471
+ export async function withFileLock(lockPath, fn, opts = {}) {
472
+ const {
473
+ timeoutMs = 10000,
474
+ pollMs = 100,
475
+ sync = false,
476
+ ownerGuard = true,
477
+ ...acquireOpts
478
+ } = opts;
479
+
480
+ const effectiveTimeout = typeof timeoutMs === 'number' && timeoutMs >= 0 ? timeoutMs : 10000;
481
+ const effectivePoll = typeof pollMs === 'number' && pollMs > 0 ? pollMs : 100;
482
+ const deadline = Date.now() + effectiveTimeout;
483
+
484
+ let acquired;
485
+ // First iteration runs unconditionally so timeoutMs:0 still attempts once.
486
+ for (;;) {
487
+ const attempt = tryAcquireFileLock(lockPath, acquireOpts);
488
+ if (attempt.acquired) {
489
+ acquired = attempt;
490
+ break;
491
+ }
492
+ if (attempt.reason === 'fs-error') {
493
+ return { ok: false, reason: 'fs-error', error: attempt.error };
494
+ }
495
+ if (Date.now() >= deadline) {
496
+ return { ok: false, reason: 'timeout', existing: attempt.existing ?? null };
497
+ }
498
+ if (sync) {
499
+ sleepSync(effectivePoll);
500
+ } else {
501
+ await delay(effectivePoll);
502
+ }
503
+ }
504
+
505
+ const releaseHolder = acquired.body.holder;
506
+ try {
507
+ const value = await fn(acquired.body);
508
+ return { ok: true, value };
509
+ } finally {
510
+ releaseFileLock(lockPath, { holder: releaseHolder, ownerGuard });
511
+ }
512
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * scripts/lib/frontend-detect/detect.mjs
3
+ *
4
+ * Deterministic frontend-slop detector — orchestration layer over rules.mjs.
5
+ *
6
+ * Provides:
7
+ * - detectContent(content, filePath, opts) → Finding[] (pure, no I/O)
8
+ * - detectFiles(paths, opts) → Finding[] (reads files)
9
+ * - SCANNABLE_EXTS → Set<string>
10
+ *
11
+ * No LLM, no network, no browser. Pure regex tier (see rules.mjs provenance).
12
+ *
13
+ * @typedef {Object} Rule
14
+ * @property {string} id
15
+ * @property {string} ruleRef
16
+ * @property {'high'|'medium'|'low'} severity
17
+ * @property {'ai-slop'|'quality'} category
18
+ * @property {string} title
19
+ * @property {string} recommendation
20
+ * @property {'low'|'medium'|'high'} fpRisk
21
+ * @property {(content: string) => Array<{line:number, snippet:string, value?:string}>} scan
22
+ *
23
+ * @typedef {Object} Finding
24
+ * @property {string} rule
25
+ * @property {string} ruleRef
26
+ * @property {'high'|'medium'|'low'} severity
27
+ * @property {'ai-slop'|'quality'} category
28
+ * @property {string} title
29
+ * @property {string} file
30
+ * @property {number} line
31
+ * @property {string} snippet
32
+ * @property {string} recommendation
33
+ * @property {'low'|'medium'|'high'} fpRisk
34
+ */
35
+
36
+ import { readFileSync } from 'node:fs';
37
+ import { RULES } from './rules.mjs';
38
+
39
+ /** File extensions worth scanning for frontend slop. */
40
+ export const SCANNABLE_EXTS = new Set([
41
+ '.css',
42
+ '.scss',
43
+ '.sass',
44
+ '.less',
45
+ '.html',
46
+ '.htm',
47
+ '.astro',
48
+ '.vue',
49
+ '.svelte',
50
+ '.jsx',
51
+ '.tsx',
52
+ ]);
53
+
54
+ /**
55
+ * Run the rule set over a single in-memory string.
56
+ *
57
+ * @param {string} content
58
+ * @param {string} [filePath] — for finding provenance (default '<inline>')
59
+ * @param {{ ignoreRules?: string[] }} [opts]
60
+ * @returns {Finding[]}
61
+ */
62
+ export function detectContent(content, filePath = '<inline>', opts = {}) {
63
+ const ignore = new Set(opts.ignoreRules ?? []);
64
+ const findings = [];
65
+ for (const rule of RULES) {
66
+ if (ignore.has(rule.id)) continue;
67
+ let matches;
68
+ try {
69
+ matches = rule.scan(content) ?? [];
70
+ } catch {
71
+ // A rule must never crash the scan — fail-soft, skip this rule.
72
+ continue;
73
+ }
74
+ for (const mt of matches) {
75
+ findings.push({
76
+ rule: rule.id,
77
+ ruleRef: rule.ruleRef,
78
+ severity: rule.severity,
79
+ category: rule.category,
80
+ title: rule.title,
81
+ file: filePath,
82
+ line: mt.line,
83
+ snippet: mt.snippet,
84
+ recommendation: rule.recommendation,
85
+ fpRisk: rule.fpRisk,
86
+ });
87
+ }
88
+ }
89
+ return findings.sort((a, b) => a.line - b.line || a.rule.localeCompare(b.rule));
90
+ }
91
+
92
+ /**
93
+ * Run the rule set over a list of file paths. Unreadable / non-scannable files
94
+ * are skipped silently (fail-soft).
95
+ *
96
+ * @param {string[]} paths — absolute or cwd-relative file paths
97
+ * @param {{ ignoreRules?: string[] }} [opts]
98
+ * @returns {Finding[]}
99
+ */
100
+ export function detectFiles(paths, opts = {}) {
101
+ const findings = [];
102
+ for (const p of paths) {
103
+ const dot = p.lastIndexOf('.');
104
+ const ext = dot === -1 ? '' : p.slice(dot).toLowerCase();
105
+ if (!SCANNABLE_EXTS.has(ext)) continue;
106
+ let content;
107
+ try {
108
+ content = readFileSync(p, 'utf8');
109
+ } catch {
110
+ continue;
111
+ }
112
+ findings.push(...detectContent(content, p, opts));
113
+ }
114
+ return findings;
115
+ }
116
+
117
+ /**
118
+ * Aggregate findings into a summary roll-up by severity + category.
119
+ * @param {Finding[]} findings
120
+ */
121
+ export function summarize(findings) {
122
+ const summary = {
123
+ total: findings.length,
124
+ high: 0,
125
+ medium: 0,
126
+ low: 0,
127
+ aiSlop: 0,
128
+ quality: 0,
129
+ byRule: {},
130
+ };
131
+ for (const f of findings) {
132
+ summary[f.severity]++;
133
+ if (f.category === 'ai-slop') summary.aiSlop++;
134
+ else summary.quality++;
135
+ summary.byRule[f.rule] = (summary.byRule[f.rule] ?? 0) + 1;
136
+ }
137
+ return summary;
138
+ }