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,501 @@
1
+ /**
2
+ * slopcheck.mjs — Package-legitimacy classifier (PRD 2026-05-22 § 2/§ 4 — Pattern 2, issue #520).
3
+ *
4
+ * Defends against LLM-hallucinated package names ("slopsquatting") by classifying
5
+ * package mentions against their respective registry. Returns one of four labels:
6
+ *
7
+ * - LEGITIMATE — package exists in the registry (versions[] non-empty).
8
+ * - ASSUMED — registry probe could not classify definitively (timeout, malformed
9
+ * input, unsupported registry, ENOENT, etc.). Fail-soft default.
10
+ * - SUS — package exists but carries a known concern (deprecation,
11
+ * audit advisory, typosquat heuristic). RESERVED FOR FUTURE WAVES —
12
+ * the MVP NEVER emits SUS. The npm-deprecated-flag and audit-warning
13
+ * detection paths are not wired up. Tests must not expect SUS emission
14
+ * until that detection logic ships. See PRD § 5 —
15
+ * "gsd Pattern Adoption Quick-Wins" (#520; archived in the private Meta-Vault) — for intent.
16
+ * - SLOP — package does NOT exist in the registry (404 / non-zero exit /
17
+ * empty version list). Highest-confidence "hallucinated name" signal.
18
+ *
19
+ * Design principles (matches scripts/lib/session-lock.mjs conventions):
20
+ * - Never throws. Every error path returns a structured result entry. Top-level
21
+ * `classifyPackages()` wraps every per-package classifier in try/catch so a
22
+ * single bad input cannot tear down the whole batch.
23
+ * - Bounded execution. 5 s per package via execFile timeout option. Timeouts
24
+ * fall back to ASSUMED + WARN — no hangs, no hard-block.
25
+ * - Order-preserving. Output[i] corresponds to input[i] for any well-formed input.
26
+ * - Fail-soft on cache I/O. Cache read/write failures degrade silently (WARN to
27
+ * stderr); classification still returns to the caller.
28
+ * - Cache: in-memory map mirrors the on-disk JSON file. 24 h TTL.
29
+ *
30
+ * Registry support matrix (MVP):
31
+ * - npm — full classification via `npm view <pkg> versions --json`.
32
+ * - pip — skeleton: always returns ASSUMED + evidence 'pip-registry-unsupported-mvp'.
33
+ * - cargo — skeleton: always returns ASSUMED + evidence 'cargo-registry-unsupported-mvp'.
34
+ * - other — returns ASSUMED + evidence 'unknown-registry'.
35
+ *
36
+ * No external dependencies — Node 20+ stdlib only.
37
+ *
38
+ * Consumed by:
39
+ * - skills/plan/SKILL.md Phase 3.5 (Package-Audit) — Agent B (parallel).
40
+ * - skills/discovery/probes/supply-chain-slopcheck.mjs — Agent C (parallel).
41
+ */
42
+
43
+ import { execFile } from 'node:child_process';
44
+ import fs from 'node:fs';
45
+ import path from 'node:path';
46
+
47
+ // ---------------------------------------------------------------------------
48
+ // Constants
49
+ // ---------------------------------------------------------------------------
50
+
51
+ /** Cache file path, relative to repoRoot (or process.cwd() when no repoRoot given). */
52
+ export const CACHE_PATH = '.orchestrator/runtime/slopcheck-cache.json';
53
+
54
+ /** Per-package registry-probe timeout. */
55
+ export const DEFAULT_TIMEOUT_MS = 5_000;
56
+
57
+ /** Cache entry TTL — 24 hours. */
58
+ export const CACHE_TTL_MS = 24 * 60 * 60 * 1_000;
59
+
60
+ /**
61
+ * Closed enum of classification labels.
62
+ * NOTE: 'SUS' is reserved for future waves — the MVP never emits it.
63
+ * The npm-deprecated-flag and audit-warning detection paths are not implemented.
64
+ * Tests should not expect SUS emission until that detection is wired up.
65
+ */
66
+ export const CLASSIFICATIONS = Object.freeze(['LEGITIMATE', 'ASSUMED', 'SUS', 'SLOP']);
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // Internal cache (in-memory mirror of disk file)
70
+ // ---------------------------------------------------------------------------
71
+
72
+ /**
73
+ * In-memory cache, keyed by `<registry>:<name>`. Loaded lazily from disk on
74
+ * first access. clearCache() resets this and removes the persisted file.
75
+ *
76
+ * Entry shape: { classification, fetchedAt, evidence? }
77
+ * - classification: one of CLASSIFICATIONS
78
+ * - fetchedAt: epoch ms (Number) at write time
79
+ * - evidence: optional string for diagnostic context
80
+ *
81
+ * @type {Map<string, { classification: string, fetchedAt: number, evidence?: string }>|null}
82
+ */
83
+ let _cache = null;
84
+ let _cacheLoadedFromPath = null;
85
+
86
+ /**
87
+ * Resolve the absolute path to the cache file.
88
+ * @param {string|undefined} repoRoot
89
+ * @returns {string}
90
+ */
91
+ function cachePathFor(repoRoot) {
92
+ return path.join(repoRoot ?? process.cwd(), CACHE_PATH);
93
+ }
94
+
95
+ /**
96
+ * Build the cache key.
97
+ * @param {string} name
98
+ * @param {string} registry
99
+ * @returns {string}
100
+ */
101
+ function cacheKey(name, registry) {
102
+ return `${registry}:${name}`;
103
+ }
104
+
105
+ /**
106
+ * Lazily load the cache from disk into memory. Safe to call repeatedly —
107
+ * subsequent calls are no-ops once the cache is loaded for the given repoRoot.
108
+ * Cache contents are validated; malformed entries are dropped silently.
109
+ *
110
+ * @param {string|undefined} repoRoot
111
+ */
112
+ function ensureCacheLoaded(repoRoot) {
113
+ const file = cachePathFor(repoRoot);
114
+ // If cache is already loaded for this path, reuse it.
115
+ if (_cache !== null && _cacheLoadedFromPath === file) return;
116
+
117
+ _cache = new Map();
118
+ _cacheLoadedFromPath = file;
119
+
120
+ let raw;
121
+ try {
122
+ raw = fs.readFileSync(file, 'utf8');
123
+ } catch (err) {
124
+ // ENOENT is the expected first-run case; any other I/O error is also non-fatal.
125
+ if (err.code !== 'ENOENT') {
126
+ console.warn(`slopcheck: cache read failed (${err.code ?? 'unknown'}): ${err.message}`);
127
+ }
128
+ return;
129
+ }
130
+
131
+ let parsed;
132
+ try {
133
+ parsed = JSON.parse(raw);
134
+ } catch {
135
+ // Corrupt cache — treat as empty. Next write will overwrite atomically.
136
+ console.warn('slopcheck: cache file is corrupt — starting with empty cache');
137
+ return;
138
+ }
139
+
140
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return;
141
+
142
+ for (const [key, entry] of Object.entries(parsed)) {
143
+ if (
144
+ typeof entry === 'object' &&
145
+ entry !== null &&
146
+ typeof entry.classification === 'string' &&
147
+ CLASSIFICATIONS.includes(entry.classification) &&
148
+ typeof entry.fetchedAt === 'number' &&
149
+ Number.isFinite(entry.fetchedAt)
150
+ ) {
151
+ const e = { classification: entry.classification, fetchedAt: entry.fetchedAt };
152
+ if (typeof entry.evidence === 'string') e.evidence = entry.evidence;
153
+ _cache.set(key, e);
154
+ }
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Persist the in-memory cache to disk via atomic tmp+rename. Failures degrade
160
+ * to a WARN; the classification result is still returned to the caller.
161
+ *
162
+ * @param {string|undefined} repoRoot
163
+ */
164
+ function persistCache(repoRoot) {
165
+ if (_cache === null) return;
166
+ const file = cachePathFor(repoRoot);
167
+ const dir = path.dirname(file);
168
+ try {
169
+ fs.mkdirSync(dir, { recursive: true });
170
+ const obj = Object.fromEntries(_cache.entries());
171
+ const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
172
+ fs.writeFileSync(tmp, JSON.stringify(obj, null, 2) + '\n', 'utf8');
173
+ fs.renameSync(tmp, file);
174
+ } catch (err) {
175
+ console.warn(`slopcheck: cache write failed (${err.code ?? 'unknown'}): ${err.message}`);
176
+ }
177
+ }
178
+
179
+ // ---------------------------------------------------------------------------
180
+ // Registry probes
181
+ // ---------------------------------------------------------------------------
182
+
183
+ /**
184
+ * Wrap execFile() callback-style in a Promise. We do NOT use util.promisify
185
+ * here because tests mock the named `execFile` export from 'node:child_process'
186
+ * — promisify would capture the function at import time, which still works,
187
+ * but a manual wrapper is simpler and matches the test mock contract exactly.
188
+ *
189
+ * @param {string} cmd
190
+ * @param {string[]} args
191
+ * @param {object} opts
192
+ * @returns {Promise<{ stdout: string, stderr: string }>}
193
+ */
194
+ function execFileAsync(cmd, args, opts) {
195
+ return new Promise((resolve, reject) => {
196
+ execFile(cmd, args, opts, (err, stdout, stderr) => {
197
+ if (err) {
198
+ // Attach stdout/stderr so the caller can inspect them without re-running.
199
+ err.stdout = stdout ?? '';
200
+ err.stderr = stderr ?? '';
201
+ reject(err);
202
+ return;
203
+ }
204
+ resolve({ stdout: stdout ?? '', stderr: stderr ?? '' });
205
+ });
206
+ });
207
+ }
208
+
209
+ /**
210
+ * Classify a single npm package via `npm view <pkg> versions --json`.
211
+ *
212
+ * Classification rules (MVP):
213
+ * - execFile error with err.killed || err.signal → ASSUMED (timeout/SIGTERM).
214
+ * - execFile error with err.code === 'ENOENT' (npm not on PATH) → ASSUMED.
215
+ * - execFile error otherwise (exit code 1, stderr 404) → SLOP.
216
+ * - stdout parses to non-empty array → LEGITIMATE.
217
+ * - stdout parses to empty array or non-array → ASSUMED (unusual response shape).
218
+ *
219
+ * The PRD § 5 also documents a `SUS` path for known audit warnings; in the
220
+ * MVP we do not fetch audit data (one more network call per package).
221
+ * Discovery probe can call `npm audit` separately and reclassify.
222
+ *
223
+ * @param {string} pkgName Validated non-empty string.
224
+ * @param {number} timeoutMs
225
+ * @returns {Promise<{ classification: string, evidence?: string }>}
226
+ */
227
+ async function classifyNpmPackage(pkgName, timeoutMs) {
228
+ // SEC: validate package name against npm grammar before execFile.
229
+ // Prevents argv injection where a malicious package.json key like
230
+ // "--registry=http://attacker/" would be parsed by npm as a flag.
231
+ // npm package name grammar (RFC): optional @scope/ + lowercase alnum/dash/dot/underscore.
232
+ const NPM_NAME_RE = /^(?:@[a-z0-9~][a-z0-9-._~]*\/)?[a-z0-9~][a-z0-9-._~]*$/i;
233
+ if (!NPM_NAME_RE.test(pkgName)) {
234
+ return { classification: 'SLOP', evidence: 'invalid-npm-package-name' };
235
+ }
236
+
237
+ let stdout;
238
+ try {
239
+ const result = await execFileAsync(
240
+ 'npm',
241
+ // `--` separator: even if NPM_NAME_RE somehow lets a flag-shaped name slip
242
+ // through, `--` halts npm's option parsing. Belt + braces.
243
+ ['view', '--', pkgName, 'versions', '--json'],
244
+ { timeout: timeoutMs, env: process.env },
245
+ );
246
+ stdout = result.stdout;
247
+ } catch (err) {
248
+ // Timeout or SIGTERM — fail-soft.
249
+ if (err.killed === true || err.signal === 'SIGTERM' || err.signal === 'SIGKILL') {
250
+ console.warn(`slopcheck: npm view timed out for ${pkgName} after ${timeoutMs}ms — classifying as ASSUMED`);
251
+ return { classification: 'ASSUMED', evidence: 'registry-timeout' };
252
+ }
253
+ // npm CLI missing — environmental, fail-soft.
254
+ if (err.code === 'ENOENT') {
255
+ console.warn(`slopcheck: npm CLI not found on PATH — classifying ${pkgName} as ASSUMED`);
256
+ return { classification: 'ASSUMED', evidence: 'npm-cli-missing' };
257
+ }
258
+ // Numeric exit code (e.g. 1 from npm 404) → registry confirmed absence.
259
+ // This matches the test contract: mockNpmNotFound sets err.code = 1.
260
+ if (typeof err.code === 'number') {
261
+ return { classification: 'SLOP', evidence: 'registry-404-or-non-zero-exit' };
262
+ }
263
+ // Any other unexpected error — fail-soft.
264
+ console.warn(`slopcheck: npm view unexpected error for ${pkgName}: ${err.message}`);
265
+ return { classification: 'ASSUMED', evidence: `registry-error:${err.code ?? 'unknown'}` };
266
+ }
267
+
268
+ // Parse versions list. `npm view <pkg> versions --json` returns either
269
+ // a JSON array (multi-version packages) or a JSON string (single-version).
270
+ let parsed;
271
+ try {
272
+ parsed = JSON.parse(stdout);
273
+ } catch {
274
+ // Unparseable response — treat as ASSUMED rather than guessing.
275
+ console.warn(`slopcheck: unparseable npm response for ${pkgName} — classifying as ASSUMED`);
276
+ return { classification: 'ASSUMED', evidence: 'unparseable-registry-response' };
277
+ }
278
+
279
+ if (Array.isArray(parsed)) {
280
+ if (parsed.length === 0) {
281
+ // Package record exists but no versions published — extremely rare; ASSUMED.
282
+ return { classification: 'ASSUMED', evidence: 'no-published-versions' };
283
+ }
284
+ return { classification: 'LEGITIMATE', evidence: `versions-count:${parsed.length}` };
285
+ }
286
+ if (typeof parsed === 'string' && parsed.length > 0) {
287
+ // Single-version package — npm collapses the array to a scalar.
288
+ return { classification: 'LEGITIMATE', evidence: 'single-version' };
289
+ }
290
+
291
+ // Anything else (object, null, number) — unexpected shape.
292
+ return { classification: 'ASSUMED', evidence: 'unexpected-response-shape' };
293
+ }
294
+
295
+ /**
296
+ * Classify a single package by registry. Skeleton implementations for pip
297
+ * and cargo per PRD § 4 — full registry probes are out-of-scope for the MVP.
298
+ *
299
+ * @param {string} name
300
+ * @param {string} registry
301
+ * @param {number} timeoutMs
302
+ * @returns {Promise<{ classification: string, evidence?: string }>}
303
+ */
304
+ async function classifyByRegistry(name, registry, timeoutMs) {
305
+ if (registry === 'npm') {
306
+ return classifyNpmPackage(name, timeoutMs);
307
+ }
308
+ if (registry === 'pip') {
309
+ return { classification: 'ASSUMED', evidence: 'pip-registry-unsupported-mvp' };
310
+ }
311
+ if (registry === 'cargo') {
312
+ return { classification: 'ASSUMED', evidence: 'cargo-registry-unsupported-mvp' };
313
+ }
314
+ return { classification: 'ASSUMED', evidence: 'unknown-registry' };
315
+ }
316
+
317
+ // ---------------------------------------------------------------------------
318
+ // Input validation helpers
319
+ // ---------------------------------------------------------------------------
320
+
321
+ /**
322
+ * Validate a single input entry. Returns a normalised { name, registry } when
323
+ * usable, or { invalid: true, name, registry } otherwise. We preserve the
324
+ * input fields (even when malformed) so the output entry can echo them back.
325
+ *
326
+ * @param {*} entry
327
+ * @returns {{ name: string|null, registry: string|null, invalid: boolean }}
328
+ */
329
+ function normaliseEntry(entry) {
330
+ if (typeof entry !== 'object' || entry === null) {
331
+ return { name: null, registry: null, invalid: true };
332
+ }
333
+ const name = typeof entry.name === 'string' && entry.name.length > 0 ? entry.name : null;
334
+ const registry = typeof entry.registry === 'string' && entry.registry.length > 0
335
+ ? entry.registry
336
+ : null;
337
+ return { name, registry, invalid: name === null || registry === null };
338
+ }
339
+
340
+ // ---------------------------------------------------------------------------
341
+ // Public API
342
+ // ---------------------------------------------------------------------------
343
+
344
+ /**
345
+ * Classify a list of packages against their respective registries.
346
+ *
347
+ * Each input entry is `{ name: string, registry: 'npm'|'pip'|'cargo'|... }`.
348
+ * Each output entry is `{ name, registry, classification, evidence? }` —
349
+ * positionally aligned with the input. Always returns an Array; never throws.
350
+ *
351
+ * Side-effects:
352
+ * - Reads/writes `.orchestrator/runtime/slopcheck-cache.json` (24 h TTL).
353
+ * - Spawns `npm view ...` per uncached npm package (5 s timeout each).
354
+ * - Writes WARN to stderr on timeouts, cache failures, malformed responses.
355
+ *
356
+ * Callers must NOT rely on classifyPackages running probes in parallel
357
+ * (today it is sequential — keeps the cache-write semantics simple).
358
+ *
359
+ * @param {Array<{ name: string, registry: string }>} pkgs
360
+ * @param {object} [opts]
361
+ * @param {string} [opts.repoRoot] — defaults to process.cwd().
362
+ * @param {number} [opts.timeoutMs=5000] — per-package registry timeout.
363
+ * @returns {Promise<Array<{ name: any, registry: any, classification: string, evidence?: string }>>}
364
+ */
365
+ export async function classifyPackages(pkgs, opts = {}) {
366
+ // Top-level never-throw guard: if input is not iterable, return [].
367
+ if (!Array.isArray(pkgs)) return [];
368
+ if (pkgs.length === 0) return [];
369
+
370
+ const { repoRoot, timeoutMs = DEFAULT_TIMEOUT_MS } = opts;
371
+
372
+ try {
373
+ ensureCacheLoaded(repoRoot);
374
+ } catch (err) {
375
+ // ensureCacheLoaded already catches its own I/O errors, but belt+suspenders:
376
+ console.warn(`slopcheck: cache load failed unexpectedly: ${err.message}`);
377
+ }
378
+
379
+ const results = [];
380
+ let cacheDirty = false;
381
+
382
+ for (const rawEntry of pkgs) {
383
+ const normalised = normaliseEntry(rawEntry);
384
+
385
+ // Surface the original fields on the output so callers can correlate.
386
+ const echoName = (rawEntry && typeof rawEntry === 'object') ? rawEntry.name ?? null : null;
387
+ const echoRegistry = (rawEntry && typeof rawEntry === 'object') ? rawEntry.registry ?? null : null;
388
+
389
+ if (normalised.invalid) {
390
+ // Malformed input — ASSUMED with diagnostic evidence. Never SLOP, because
391
+ // we cannot prove non-existence without a name to probe.
392
+ results.push({
393
+ name: echoName,
394
+ registry: echoRegistry,
395
+ classification: 'ASSUMED',
396
+ evidence: 'invalid-input-entry',
397
+ });
398
+ continue;
399
+ }
400
+
401
+ const key = cacheKey(normalised.name, normalised.registry);
402
+ const cached = _cache?.get(key) ?? null;
403
+ if (cached !== null && (Date.now() - cached.fetchedAt) < CACHE_TTL_MS) {
404
+ // Cache hit — return the cached classification without invoking the registry.
405
+ const entry = {
406
+ name: normalised.name,
407
+ registry: normalised.registry,
408
+ classification: cached.classification,
409
+ };
410
+ if (typeof cached.evidence === 'string') entry.evidence = cached.evidence;
411
+ results.push(entry);
412
+ continue;
413
+ }
414
+
415
+ // Cache miss (or expired). Probe the registry; never let an unexpected
416
+ // error escape this loop.
417
+ let probe;
418
+ try {
419
+ probe = await classifyByRegistry(normalised.name, normalised.registry, timeoutMs);
420
+ } catch (err) {
421
+ console.warn(`slopcheck: classifier threw unexpectedly for ${normalised.name}@${normalised.registry}: ${err.message}`);
422
+ probe = { classification: 'ASSUMED', evidence: 'classifier-exception' };
423
+ }
424
+
425
+ // Defensive: ensure the classifier returned a valid enum value.
426
+ const classification = CLASSIFICATIONS.includes(probe.classification)
427
+ ? probe.classification
428
+ : 'ASSUMED';
429
+ const evidence = typeof probe.evidence === 'string' ? probe.evidence : undefined;
430
+
431
+ const outEntry = { name: normalised.name, registry: normalised.registry, classification };
432
+ if (evidence !== undefined) outEntry.evidence = evidence;
433
+ results.push(outEntry);
434
+
435
+ // Update cache (in-memory now, flushed to disk once after the loop).
436
+ if (_cache !== null) {
437
+ const cacheEntry = { classification, fetchedAt: Date.now() };
438
+ if (evidence !== undefined) cacheEntry.evidence = evidence;
439
+ _cache.set(key, cacheEntry);
440
+ cacheDirty = true;
441
+ }
442
+ }
443
+
444
+ // Flush cache once per call to avoid N fsync syscalls in a large batch.
445
+ if (cacheDirty) {
446
+ persistCache(repoRoot);
447
+ }
448
+
449
+ return results;
450
+ }
451
+
452
+ /**
453
+ * Look up a cached classification entry without contacting the registry.
454
+ * Returns null when the entry is absent, expired, or the cache was never loaded.
455
+ *
456
+ * Note: this helper triggers a one-shot cache load from disk so it can be
457
+ * called BEFORE any classifyPackages() invocation in the same process.
458
+ *
459
+ * @param {string} name
460
+ * @param {string} registry
461
+ * @param {object} [opts]
462
+ * @param {string} [opts.repoRoot] — defaults to process.cwd().
463
+ * @returns {{ classification: string, fetchedAt: number, evidence?: string }|null}
464
+ */
465
+ export function getCachedClassification(name, registry, opts = {}) {
466
+ if (typeof name !== 'string' || name.length === 0) return null;
467
+ if (typeof registry !== 'string' || registry.length === 0) return null;
468
+ ensureCacheLoaded(opts.repoRoot);
469
+ const entry = _cache?.get(cacheKey(name, registry)) ?? null;
470
+ if (entry === null) return null;
471
+ if ((Date.now() - entry.fetchedAt) >= CACHE_TTL_MS) return null;
472
+ // Return a shallow copy so callers cannot mutate the in-memory cache.
473
+ const copy = { classification: entry.classification, fetchedAt: entry.fetchedAt };
474
+ if (typeof entry.evidence === 'string') copy.evidence = entry.evidence;
475
+ return copy;
476
+ }
477
+
478
+ /**
479
+ * Wipe the in-memory cache AND remove the persisted cache file. Fail-soft:
480
+ * unlink errors are swallowed (the in-memory clear still succeeds).
481
+ *
482
+ * @param {object} [opts]
483
+ * @param {string} [opts.repoRoot] — defaults to process.cwd().
484
+ */
485
+ export function clearCache(opts = {}) {
486
+ _cache = new Map();
487
+ // Reset the loaded-path marker so the next ensureCacheLoaded() will rerun
488
+ // the disk load (now finding either no file or a freshly-written one).
489
+ _cacheLoadedFromPath = null;
490
+
491
+ const file = cachePathFor(opts.repoRoot);
492
+ try {
493
+ fs.unlinkSync(file);
494
+ } catch (err) {
495
+ // ENOENT is the expected case after first-run clearCache; any other
496
+ // failure is non-fatal — we already cleared the in-memory cache.
497
+ if (err.code !== 'ENOENT') {
498
+ console.warn(`slopcheck: cache file unlink failed (${err.code ?? 'unknown'}): ${err.message}`);
499
+ }
500
+ }
501
+ }
@@ -0,0 +1,118 @@
1
+ /**
2
+ * soul-resolve.mjs — Template resolver for soul.md files (Issue #176, D3).
3
+ *
4
+ * Resolves `{{slot}}` placeholders in soul.md templates using owner persona
5
+ * config loaded via `owner-yaml.mjs` (D1). Pure at the `resolveSoul` level;
6
+ * `loadAndResolveSoul` performs disk I/O.
7
+ *
8
+ * ── Slot syntax ──────────────────────────────────────────────────────────────
9
+ *
10
+ * {{owner.language}} → 'de' | 'en'
11
+ * {{tone.style}} → 'direct' | 'neutral' | 'friendly'
12
+ * {{efficiency.output-level}} → 'lite' | 'full' | 'ultra'
13
+ * {{efficiency.preamble}} → 'minimal' | 'verbose'
14
+ *
15
+ * ── Resolution rules ─────────────────────────────────────────────────────────
16
+ *
17
+ * - Known slot path present in ownerConfig → replaced with the value
18
+ * - Known slot path missing in ownerConfig → replaced with default (silent)
19
+ * - Unknown slot path → left as-is; warning added to result
20
+ *
21
+ * ── Exports ───────────────────────────────────────────────────────────────────
22
+ *
23
+ * resolveSoul(templateContent, ownerConfig) → { resolved: string, warnings: string[] }
24
+ * loadAndResolveSoul(soulPath, opts?) → { resolved: string, warnings: string[], source: string }
25
+ */
26
+
27
+ import { readFileSync } from 'node:fs';
28
+ import { loadOwnerConfig, getDefaults } from './owner-yaml.mjs';
29
+
30
+ // ---------------------------------------------------------------------------
31
+ // Helpers
32
+ // ---------------------------------------------------------------------------
33
+
34
+ /**
35
+ * Look up a dot-notation path such as "efficiency.output-level" in a nested
36
+ * object. Returns `undefined` if any segment is missing.
37
+ *
38
+ * @param {object} obj
39
+ * @param {string} dotPath
40
+ * @returns {unknown}
41
+ */
42
+ function getByPath(obj, dotPath) {
43
+ const segments = dotPath.split('.');
44
+ let cursor = obj;
45
+ for (const seg of segments) {
46
+ if (cursor === null || cursor === undefined || typeof cursor !== 'object') {
47
+ return undefined;
48
+ }
49
+ cursor = cursor[seg];
50
+ }
51
+ return cursor;
52
+ }
53
+
54
+ /**
55
+ * Set of slot paths explicitly documented / known by this module.
56
+ * Any slot NOT in this set is considered unknown and left in place with a warning.
57
+ */
58
+ const KNOWN_SLOTS = new Set([
59
+ 'owner.language',
60
+ 'tone.style',
61
+ 'efficiency.output-level',
62
+ 'efficiency.preamble',
63
+ ]);
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Public API
67
+ // ---------------------------------------------------------------------------
68
+
69
+ /**
70
+ * Resolve `{{slot}}` placeholders in `templateContent` using `ownerConfig`.
71
+ *
72
+ * Pure function — no I/O, no side-effects.
73
+ *
74
+ * @param {string} templateContent Raw soul.md template text.
75
+ * @param {object} ownerConfig Owner persona config (from loadOwnerConfig or getDefaults).
76
+ * @returns {{ resolved: string, warnings: string[] }}
77
+ */
78
+ export function resolveSoul(templateContent, ownerConfig) {
79
+ const defaults = getDefaults();
80
+ const warnings = [];
81
+
82
+ const resolved = templateContent.replace(/\{\{\s*([\w.-]+)\s*\}\}/g, (_match, path) => {
83
+ if (!KNOWN_SLOTS.has(path)) {
84
+ warnings.push(`Unknown slot path: {{${path}}} — left in place`);
85
+ return `{{${path}}}`;
86
+ }
87
+
88
+ // Try ownerConfig first, then fall back to defaults
89
+ let value = getByPath(ownerConfig, path);
90
+ if (value === undefined || value === null || value === '') {
91
+ value = getByPath(defaults, path);
92
+ }
93
+
94
+ return String(value ?? '');
95
+ });
96
+
97
+ return { resolved, warnings };
98
+ }
99
+
100
+ /**
101
+ * Read a soul.md template from `soulPath`, load owner config (from
102
+ * `ownerConfigPath` or the default location), and return the resolved content.
103
+ *
104
+ * @param {string} soulPath Absolute path to the soul.md template.
105
+ * @param {{ ownerConfigPath?: string }} [opts]
106
+ * @returns {{ resolved: string, warnings: string[], source: 'file'|'defaults'|'partial' }}
107
+ */
108
+ export function loadAndResolveSoul(soulPath, opts = {}) {
109
+ const templateContent = readFileSync(soulPath, 'utf8');
110
+
111
+ const { config, source } = loadOwnerConfig(
112
+ opts.ownerConfigPath ? { path: opts.ownerConfigPath } : {},
113
+ );
114
+
115
+ const { resolved, warnings } = resolveSoul(templateContent, config);
116
+
117
+ return { resolved, warnings, source };
118
+ }