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,533 @@
1
+ #!/usr/bin/env node
2
+ // check-unicode-safety.mjs — Scan tracked text files for dangerous / invisible
3
+ // Unicode and undeclared homoglyphs (#626).
4
+ //
5
+ // Rationale: a repo whose source, frontmatter, SKILL.md bodies, PR descriptions,
6
+ // and agent prompts are consumed by an LLM is a prime target for "ASCII /
7
+ // Tag smuggling" and bidi-override attacks — invisible code points (zero-width
8
+ // spaces, BOM, word-joiner, Unicode Tag block, bidi embed/override) that render
9
+ // as nothing to a human reviewer but carry instructions or reorder text for the
10
+ // model. This validator flags those code points everywhere, flags non-curated
11
+ // emoji in code/frontmatter (strict) while tolerating the repo's deliberate
12
+ // banner/status-symbol set in prose (lenient), and flags mixed-script homoglyphs
13
+ // inside a single identifier token in strict contexts.
14
+ //
15
+ // Usage: check-unicode-safety.mjs <plugin-root> [--write] [--json]
16
+ // Outputs lines of the form " PASS: ..." / " FAIL: ..." plus a "Results: N
17
+ // passed, M failed" line so the validate-plugin orchestrator's PASS:/FAIL: tally
18
+ // counts it.
19
+ //
20
+ // Exit codes:
21
+ // 0 — clean (no violations)
22
+ // 1 — at least one violation
23
+ // 2 — tool error (missing arg / unreadable root)
24
+ //
25
+ // Contexts:
26
+ // STRICT — code files (.mjs/.js/.ts/.sh), .json, .yml/.yaml, AND the leading
27
+ // frontmatter block (`---\n…\n---`) of .md files. Emoji + invisibles
28
+ // + mixed-script homoglyphs are all flagged here, EXCEPT the curated
29
+ // deliberate-symbol allow-list (banner/status emoji the repo ships).
30
+ // LENIENT — markdown prose body (after frontmatter). Invisibles / bidi /
31
+ // tag-block / zero-width are still flagged; the deliberate emoji set
32
+ // passes. Mixed-script homoglyph detection is NOT applied (German
33
+ // prose + intentional CJK fixtures must never be swept).
34
+
35
+ import { readFileSync, readdirSync, statSync, existsSync, writeFileSync } from 'node:fs';
36
+ import { join, extname, relative, sep } from 'node:path';
37
+ import { execFileSync } from 'node:child_process';
38
+ import { pathToFileURL } from 'node:url';
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // File enumeration — git ls-files primary, recursive fs walk fallback.
42
+ // (ported from check-owner-leakage.mjs, wrapped as pure exported helpers)
43
+ // ---------------------------------------------------------------------------
44
+
45
+ /** Text-scan extension allow-list. */
46
+ export const TEXT_EXTS = new Set(['.md', '.mjs', '.js', '.ts', '.json', '.yml', '.yaml', '.sh', '.txt']);
47
+
48
+ /** STRICT-context extensions (emoji + homoglyphs flagged, not just invisibles). */
49
+ const STRICT_EXTS = new Set(['.mjs', '.js', '.ts', '.sh', '.json', '.yml', '.yaml']);
50
+
51
+ /** Extensions whose files `--write` may rewrite (strip dangerous-invisibles). */
52
+ const WRITABLE_EXTS = new Set(['.md', '.txt']);
53
+
54
+ /**
55
+ * Return true if a path is a scannable text file by extension.
56
+ * @param {string} filePath
57
+ * @returns {boolean}
58
+ */
59
+ export function isTextFile(filePath) {
60
+ return TEXT_EXTS.has(extname(filePath));
61
+ }
62
+
63
+ function walkDir(root, dir, acc = []) {
64
+ let entries;
65
+ try {
66
+ entries = readdirSync(dir);
67
+ } catch {
68
+ return acc;
69
+ }
70
+ for (const entry of entries) {
71
+ if (entry === '.git' || entry === 'node_modules') continue;
72
+ const full = join(dir, entry);
73
+ let s;
74
+ try {
75
+ s = statSync(full);
76
+ } catch {
77
+ continue;
78
+ }
79
+ if (s.isDirectory()) {
80
+ walkDir(root, full, acc);
81
+ } else {
82
+ acc.push(full);
83
+ }
84
+ }
85
+ return acc;
86
+ }
87
+
88
+ /**
89
+ * Enumerate tracked text files under `root`. Uses `git ls-files`; falls back to
90
+ * a recursive fs walk when git is unavailable / `root` is not a repo.
91
+ * @param {string} root absolute plugin root
92
+ * @returns {string[]} absolute file paths
93
+ */
94
+ export function getTrackedFiles(root) {
95
+ let files;
96
+ try {
97
+ const output = execFileSync('git', ['ls-files'], { cwd: root, encoding: 'utf8' });
98
+ files = output.split('\n').filter(Boolean).map((f) => join(root, f));
99
+ } catch {
100
+ files = walkDir(root, root);
101
+ }
102
+ return files.filter(isTextFile);
103
+ }
104
+
105
+ // ---------------------------------------------------------------------------
106
+ // Dangerous-invisible code-point set (always flagged, every context).
107
+ // Ported from ecc check-unicode-safety.js isDangerousInvisibleCodePoint, with
108
+ // two deliberate differences:
109
+ // - the variation-selector range (U+FE00–U+FE0F) is handled SEPARATELY (see
110
+ // isOrphanVariationSelector) so legit emoji-presentation selectors don't
111
+ // false-positive;
112
+ // - U+00AD SOFT HYPHEN is ADDED (not in ecc) — an invisible discretionary
113
+ // hyphen that smuggles into prose; the repo had exactly one stray instance.
114
+ // ---------------------------------------------------------------------------
115
+
116
+ /**
117
+ * True when `codePoint` is an unconditionally dangerous invisible / format
118
+ * control. Variation selectors are NOT decided here (orphan-only — see below).
119
+ * @param {number} codePoint
120
+ * @returns {boolean}
121
+ */
122
+ export function isDangerousInvisibleCodePoint(codePoint) {
123
+ return (
124
+ (codePoint >= 0x200b && codePoint <= 0x200d) || // ZWSP / ZWNJ / ZWJ
125
+ codePoint === 0x2060 || // WORD JOINER
126
+ codePoint === 0xfeff || // BOM / ZWNBSP
127
+ (codePoint >= 0x202a && codePoint <= 0x202e) || // bidi embed / override
128
+ (codePoint >= 0x2066 && codePoint <= 0x2069) || // bidi isolates
129
+ (codePoint >= 0xe0000 && codePoint <= 0xe007f) || // Unicode Tag block (ASCII smuggling)
130
+ (codePoint >= 0xe0100 && codePoint <= 0xe01ef) || // VS supplement
131
+ codePoint === 0x180e || // MONGOLIAN VOWEL SEPARATOR
132
+ codePoint === 0x115f || // HANGUL CHOSEONG FILLER
133
+ codePoint === 0x1160 || // HANGUL JUNGSEONG FILLER
134
+ (codePoint >= 0x2061 && codePoint <= 0x2064) || // invisible math operators
135
+ codePoint === 0x3164 || // HANGUL FILLER
136
+ codePoint === 0x00ad // SOFT HYPHEN (added beyond ecc)
137
+ );
138
+ }
139
+
140
+ // Variation-selector range (U+FE00–U+FE0F). A VS-16 (U+FE0F) that immediately
141
+ // follows an Extended_Pictographic code point is the legitimate emoji-
142
+ // presentation selector and must NOT be flagged. Any other variation selector
143
+ // (orphan / standalone, or a VS not preceded by an emoji) is suspicious.
144
+ const EXTENDED_PICTOGRAPHIC = /\p{Extended_Pictographic}/u;
145
+
146
+ /**
147
+ * True when the variation selector at `prevChar`→`char` is an ORPHAN — i.e.
148
+ * not a legitimate emoji-presentation selector following a pictographic base.
149
+ * @param {number} codePoint code point of the variation selector
150
+ * @param {string} prevChar the preceding code point (as a string), or ''
151
+ * @returns {boolean}
152
+ */
153
+ function isOrphanVariationSelector(codePoint, prevChar) {
154
+ if (codePoint < 0xfe00 || codePoint > 0xfe0f) return false;
155
+ if (codePoint === 0xfe0f && prevChar && EXTENDED_PICTOGRAPHIC.test(prevChar)) {
156
+ return false; // legit emoji-presentation selector
157
+ }
158
+ return true; // orphan / standalone variation selector
159
+ }
160
+
161
+ // ---------------------------------------------------------------------------
162
+ // Emoji + deliberate-symbol allow-list.
163
+ // ---------------------------------------------------------------------------
164
+
165
+ /** Emoji / regional-indicator detection (non-global — safe for per-char .test). */
166
+ const EMOJI_RE = /(?:\p{Extended_Pictographic}|\p{Regional_Indicator})/u;
167
+
168
+ // The repo deliberately ships a curated banner/status emoji set in CLI output
169
+ // strings, hooks, docs, and SKILL bodies. Every code point in this set is an
170
+ // intentional symbol, NOT a smuggling vector — it passes in BOTH strict and
171
+ // lenient contexts. The list was built from a full survey of the tracked tree
172
+ // (18 distinct emoji code points; see #626 Wave-1/Wave-2 discovery) plus the
173
+ // task-named allow-list. A NEW emoji outside this set is still flagged.
174
+ const ALLOWED_EMOJI_CODEPOINTS = new Set([
175
+ 0x1f3af, // 🎯
176
+ 0x1f465, // 👥
177
+ 0x1f4ca, // 📊
178
+ 0x1f4cb, // 📋
179
+ 0x1f4da, // 📚
180
+ 0x1f501, // 🔁
181
+ 0x1f50d, // 🔍
182
+ 0x1f527, // 🔧
183
+ 0x1f534, // 🔴
184
+ 0x1f5a5, // 🖥
185
+ 0x1f6a6, // 🚦
186
+ 0x1f6a8, // 🚨
187
+ 0x2139, // ℹ
188
+ 0x2194, // ↔
189
+ 0x26a0, // ⚠
190
+ 0x2705, // ✅
191
+ 0x274c, // ❌
192
+ 0x2b50, // ⭐
193
+ 0x00a9, // © (copyright — defensive, ecc allow-list)
194
+ 0x00ae, // ® (registered — defensive)
195
+ 0x2122, // ™ (trademark — defensive)
196
+ ]);
197
+
198
+ /**
199
+ * True when `char` is a curated deliberate symbol that must never be flagged.
200
+ * @param {string} char a single emoji match
201
+ * @returns {boolean}
202
+ */
203
+ function isAllowedEmojiSymbol(char) {
204
+ return ALLOWED_EMOJI_CODEPOINTS.has(char.codePointAt(0));
205
+ }
206
+
207
+ // ---------------------------------------------------------------------------
208
+ // Mixed-script homoglyph detection (STRICT contexts only, conservative).
209
+ // Flags an identifier-like token that mixes Latin with Cyrillic — the classic
210
+ // homoglyph attack (e.g. Cyrillic U+0430 "а" inside an ASCII word; the Cyrillic
211
+ // glyph is visually identical to Latin "a").
212
+ //
213
+ // Greek is DELIBERATELY EXCLUDED (#626): Greek math letters (λ π Δ Σ θ µ) are
214
+ // visually distinct from Latin — they are NOT homoglyph-attack vectors — and are
215
+ // legitimately used as identifiers in code (`const λmax = 5`). Including Greek in
216
+ // the mixed-script trigger produced a CI-landmine false-positive. German umlauts
217
+ // (ä ö ü ß) are Latin script → never trigger. CJK is neither Latin/Cyrillic →
218
+ // never trigger. Prose is NOT scanned (lenient).
219
+ // ---------------------------------------------------------------------------
220
+
221
+ const IDENT_TOKEN_RE = /[\p{L}\p{M}\p{N}_]+/gu;
222
+ const SCRIPT_LATIN = /\p{Script=Latin}/u;
223
+ const SCRIPT_CYRILLIC = /\p{Script=Cyrillic}/u;
224
+
225
+ // ---------------------------------------------------------------------------
226
+ // Frontmatter extraction (a .md file's leading `---\n…\n---` block).
227
+ // ---------------------------------------------------------------------------
228
+
229
+ /**
230
+ * Return the length (in chars) of the leading YAML frontmatter block, or 0 when
231
+ * the file has none. The frontmatter region is treated as STRICT.
232
+ *
233
+ * A single leading U+FEFF BOM is tolerated before the opening `---\n` (#626):
234
+ * without this carve-out, a BOM-prefixed `.md` file fails the `startsWith` test
235
+ * and its frontmatter is silently treated as LENIENT prose. The BOM itself is
236
+ * still flagged as dangerous-invisible by the code-point walk (which scans the
237
+ * original, un-stripped text), so the carve-out only fixes detection. The
238
+ * returned length is an offset into the ORIGINAL text, so the +1 for the
239
+ * stripped BOM is added back.
240
+ * @param {string} text
241
+ * @returns {number} number of leading characters that are frontmatter
242
+ */
243
+ function frontmatterLength(text) {
244
+ // Strip a single leading BOM for the detection test only; track the offset so
245
+ // the returned length still indexes into the original `text`.
246
+ const bomOffset = text.charCodeAt(0) === 0xfeff ? 1 : 0;
247
+ const body = bomOffset ? text.slice(1) : text;
248
+ if (!body.startsWith('---\n')) return 0;
249
+ const end = body.indexOf('\n---', 4);
250
+ if (end === -1) return 0;
251
+ return bomOffset + end + 4; // include the closing `---`, offset by the BOM
252
+ }
253
+
254
+ // ---------------------------------------------------------------------------
255
+ // Per-line / column helpers.
256
+ // ---------------------------------------------------------------------------
257
+
258
+ /**
259
+ * 1-based line + column for a character index into `text`.
260
+ * @param {string} text
261
+ * @param {number} index char index of the code unit
262
+ * @returns {{line: number, column: number}}
263
+ */
264
+ function lineAndColumn(text, index) {
265
+ const before = text.slice(0, index);
266
+ const line = before.split('\n').length;
267
+ const lastNewline = before.lastIndexOf('\n');
268
+ const column = index - lastNewline; // 1-based column
269
+ return { line, column };
270
+ }
271
+
272
+ // ---------------------------------------------------------------------------
273
+ // Core collector (pure).
274
+ // ---------------------------------------------------------------------------
275
+
276
+ /**
277
+ * Collect every Unicode-safety violation across `rootOrFiles`.
278
+ *
279
+ * Pure + import-safe: no process.exit, no console. Returns the violation list so
280
+ * callers and tests can assert against it directly.
281
+ *
282
+ * @param {string} rootOrFiles absolute plugin root (files are enumerated via git)
283
+ * @returns {Array<{file: string, line: number, column: number, kind: string, codePoint: string}>}
284
+ * `file` is RELATIVE to the root. `kind` ∈
285
+ * {'dangerous-invisible','orphan-variation-selector','emoji','mixed-script'}.
286
+ */
287
+ export function collectUnicodeViolations(rootOrFiles) {
288
+ const root = rootOrFiles;
289
+ const files = getTrackedFiles(root);
290
+
291
+ // Exclude this validator's own source + test file (the dangerous-invisible
292
+ // doc-comments and the test fixtures would otherwise self-trip the scanner).
293
+ const SELF_EXCLUSIONS = new Set([
294
+ 'scripts/lib/validate/check-unicode-safety.mjs',
295
+ 'tests/lib/validate/check-unicode-safety.test.mjs',
296
+ ]);
297
+
298
+ /** @type {Array<{file: string, line: number, column: number, kind: string, codePoint: string}>} */
299
+ const violations = [];
300
+
301
+ for (const filePath of files) {
302
+ const rel = relative(root, filePath).split(sep).join('/');
303
+ if (SELF_EXCLUSIONS.has(rel)) continue;
304
+
305
+ let text;
306
+ try {
307
+ text = readFileSync(filePath, 'utf8');
308
+ } catch {
309
+ continue; // unreadable — skip
310
+ }
311
+
312
+ const ext = extname(filePath);
313
+ const fmLen = ext === '.md' ? frontmatterLength(text) : 0;
314
+ const fileIsStrict = STRICT_EXTS.has(ext);
315
+
316
+ // STRICT region of a char index: whole file when extension is strict, OR
317
+ // the frontmatter prefix of a .md file.
318
+ const indexIsStrict = (idx) => fileIsStrict || (fmLen > 0 && idx < fmLen);
319
+
320
+ // Walk code points once for invisibles / variation selectors / emoji.
321
+ let index = 0;
322
+ let prevChar = '';
323
+ for (const char of text) {
324
+ const cp = char.codePointAt(0);
325
+
326
+ if (isDangerousInvisibleCodePoint(cp)) {
327
+ const { line, column } = lineAndColumn(text, index);
328
+ violations.push({
329
+ file: rel,
330
+ line,
331
+ column,
332
+ kind: 'dangerous-invisible',
333
+ codePoint: `U+${cp.toString(16).toUpperCase()}`,
334
+ });
335
+ } else if (isOrphanVariationSelector(cp, prevChar)) {
336
+ const { line, column } = lineAndColumn(text, index);
337
+ violations.push({
338
+ file: rel,
339
+ line,
340
+ column,
341
+ kind: 'orphan-variation-selector',
342
+ codePoint: `U+${cp.toString(16).toUpperCase()}`,
343
+ });
344
+ } else if (EMOJI_RE.test(char) && !isAllowedEmojiSymbol(char) && indexIsStrict(index)) {
345
+ // Non-curated emoji in a STRICT context (code file / .json / .yml /
346
+ // .md frontmatter). In LENIENT prose, non-curated emoji are tolerated;
347
+ // the curated deliberate set (isAllowedEmojiSymbol) passes everywhere.
348
+ const { line, column } = lineAndColumn(text, index);
349
+ violations.push({
350
+ file: rel,
351
+ line,
352
+ column,
353
+ kind: 'emoji',
354
+ codePoint: `U+${cp.toString(16).toUpperCase()}`,
355
+ });
356
+ }
357
+
358
+ prevChar = char;
359
+ index += char.length;
360
+ }
361
+
362
+ // Mixed-script homoglyph detection — STRICT context only, conservative.
363
+ if (fileIsStrict || fmLen > 0) {
364
+ // Scan only the strict region. For strict-ext files that is the whole
365
+ // file; for .md it is the frontmatter prefix.
366
+ const region = fileIsStrict ? text : text.slice(0, fmLen);
367
+ IDENT_TOKEN_RE.lastIndex = 0;
368
+ let m;
369
+ while ((m = IDENT_TOKEN_RE.exec(region)) !== null) {
370
+ const token = m[0];
371
+ const hasLatin = SCRIPT_LATIN.test(token);
372
+ const hasCyrillic = SCRIPT_CYRILLIC.test(token);
373
+ // Greek is excluded by design (#626) — Latin+Greek is NOT a homoglyph
374
+ // attack and legit Greek-letter identifiers must not be swept.
375
+ if (hasLatin && hasCyrillic) {
376
+ const { line, column } = lineAndColumn(text, m.index);
377
+ // Report the code point of the first Cyrillic char in the token for
378
+ // a precise pointer.
379
+ let offending = token.codePointAt(0);
380
+ for (const c of token) {
381
+ const ccp = c.codePointAt(0);
382
+ if (SCRIPT_CYRILLIC.test(c)) {
383
+ offending = ccp;
384
+ break;
385
+ }
386
+ }
387
+ violations.push({
388
+ file: rel,
389
+ line,
390
+ column,
391
+ kind: 'mixed-script',
392
+ codePoint: `U+${offending.toString(16).toUpperCase()}`,
393
+ });
394
+ }
395
+ }
396
+ }
397
+ }
398
+
399
+ return violations;
400
+ }
401
+
402
+ // ---------------------------------------------------------------------------
403
+ // --write: strip ONLY the unambiguous dangerous-invisible chars from writable
404
+ // files. (We deliberately do NOT port ecc's markdown-whitespace-collapse — it
405
+ // is out of scope and would churn unrelated formatting.)
406
+ // ---------------------------------------------------------------------------
407
+
408
+ /**
409
+ * Strip every dangerous-invisible code point (and orphan variation selector)
410
+ * from `text`. Curated emoji and legit emoji-presentation selectors are kept.
411
+ * @param {string} text
412
+ * @returns {string}
413
+ */
414
+ export function stripDangerousInvisibles(text) {
415
+ let out = '';
416
+ let prevChar = '';
417
+ for (const char of text) {
418
+ const cp = char.codePointAt(0);
419
+ if (isDangerousInvisibleCodePoint(cp)) {
420
+ // drop
421
+ } else if (isOrphanVariationSelector(cp, prevChar)) {
422
+ // drop orphan VS
423
+ } else {
424
+ out += char;
425
+ }
426
+ prevChar = char;
427
+ }
428
+ return out;
429
+ }
430
+
431
+ // ---------------------------------------------------------------------------
432
+ // Runner.
433
+ // ---------------------------------------------------------------------------
434
+
435
+ /**
436
+ * Run the validator against a plugin root. Prints PASS/FAIL lines and a Results
437
+ * summary (unless `json` is set, in which case the violation array is emitted as
438
+ * JSON to stdout). Returns the process exit code: 0 clean, 1 violations, 2 tool
439
+ * error.
440
+ *
441
+ * @param {string} pluginRoot
442
+ * @param {{write?: boolean, json?: boolean}} [opts]
443
+ * @returns {number}
444
+ */
445
+ export function runCheckUnicodeSafety(pluginRoot, opts = {}) {
446
+ const { write = false, json = false } = opts;
447
+
448
+ if (!json) {
449
+ console.log('--- Check: Unicode safety (invisible / bidi / tag-block / homoglyph) ---');
450
+ }
451
+
452
+ // Tool-error gate: root must exist and be a directory.
453
+ try {
454
+ if (!pluginRoot || !existsSync(pluginRoot) || !statSync(pluginRoot).isDirectory()) {
455
+ console.error(` tool-error: plugin root not found or not a directory: ${pluginRoot}`);
456
+ return 2;
457
+ }
458
+ } catch (/** @type {unknown} */ e) {
459
+ console.error(` tool-error: cannot stat plugin root: ${(/** @type {Error} */ (e)).message}`);
460
+ return 2;
461
+ }
462
+
463
+ // Optional --write: strip dangerous-invisibles from writable files first, so
464
+ // the subsequent scan reflects the cleaned tree.
465
+ if (write) {
466
+ for (const filePath of getTrackedFiles(pluginRoot)) {
467
+ const ext = extname(filePath);
468
+ if (!WRITABLE_EXTS.has(ext)) continue;
469
+ const rel = relative(pluginRoot, filePath).split(sep).join('/');
470
+ if (rel === 'scripts/lib/validate/check-unicode-safety.mjs') continue;
471
+ if (rel === 'tests/lib/validate/check-unicode-safety.test.mjs') continue;
472
+ let text;
473
+ try {
474
+ text = readFileSync(filePath, 'utf8');
475
+ } catch {
476
+ continue;
477
+ }
478
+ const stripped = stripDangerousInvisibles(text);
479
+ if (stripped !== text) {
480
+ try {
481
+ writeFileSync(filePath, stripped, 'utf8');
482
+ if (!json) console.log(` stripped dangerous-invisibles from ${rel}`);
483
+ } catch {
484
+ // ignore write failure — the scan below will still report the leak
485
+ }
486
+ }
487
+ }
488
+ }
489
+
490
+ const violations = collectUnicodeViolations(pluginRoot);
491
+
492
+ if (json) {
493
+ process.stdout.write(JSON.stringify(violations) + '\n');
494
+ return violations.length > 0 ? 1 : 0;
495
+ }
496
+
497
+ let passed = 0;
498
+ let failed = 0;
499
+ const pass = (msg) => { console.log(` PASS: ${msg}`); passed++; };
500
+ const fail = (msg) => { console.log(` FAIL: ${msg}`); failed++; };
501
+
502
+ if (violations.length === 0) {
503
+ pass('no dangerous / invisible Unicode or homoglyphs found');
504
+ } else {
505
+ for (const v of violations) {
506
+ fail(`${v.file}:${v.line}:${v.column} — ${v.kind} ${v.codePoint}`);
507
+ }
508
+ }
509
+
510
+ console.log('');
511
+ console.log(`Results: ${passed} passed, ${failed} failed`);
512
+ return failed > 0 ? 1 : 0;
513
+ }
514
+
515
+ // CLI entry — only when executed directly, never on import (keeps the exports
516
+ // safe to import from tests without triggering process.exit).
517
+ const isMain = import.meta.url === pathToFileURL(process.argv[1] || '').href;
518
+ if (isMain) {
519
+ const args = process.argv.slice(2);
520
+ const flags = new Set(args.filter((a) => a.startsWith('--')));
521
+ const positional = args.filter((a) => !a.startsWith('--'));
522
+ const pluginRoot = positional[0];
523
+ if (!pluginRoot) {
524
+ console.error('Usage: check-unicode-safety.mjs <plugin-root> [--write] [--json]');
525
+ process.exit(2);
526
+ }
527
+ process.exit(
528
+ runCheckUnicodeSafety(pluginRoot, {
529
+ write: flags.has('--write'),
530
+ json: flags.has('--json'),
531
+ }),
532
+ );
533
+ }