@soleur/opencode 0.0.0-stage → 1.0.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 (812) hide show
  1. package/.claude-plugin/agents.manifest.json +476 -0
  2. package/.claude-plugin/plugin.json +42 -0
  3. package/README.md +44 -2
  4. package/agents/community/.gitkeep +0 -0
  5. package/agents/engineering/cto.md +87 -0
  6. package/agents/engineering/design/ddd-architect.md +95 -0
  7. package/agents/engineering/discovery/agent-finder.md +214 -0
  8. package/agents/engineering/discovery/functional-discovery.md +182 -0
  9. package/agents/engineering/infra/infra-security.md +151 -0
  10. package/agents/engineering/infra/platform-strategist.md +109 -0
  11. package/agents/engineering/infra/terraform-architect.md +171 -0
  12. package/agents/engineering/prompt-engineer.md +37 -0
  13. package/agents/engineering/research/best-practices-researcher.md +115 -0
  14. package/agents/engineering/research/framework-docs-researcher.md +93 -0
  15. package/agents/engineering/research/git-history-analyzer.md +53 -0
  16. package/agents/engineering/research/learnings-researcher.md +285 -0
  17. package/agents/engineering/research/repo-research-analyst.md +118 -0
  18. package/agents/engineering/review/agent-native-reviewer.md +279 -0
  19. package/agents/engineering/review/architecture-strategist.md +61 -0
  20. package/agents/engineering/review/code-quality-analyst.md +92 -0
  21. package/agents/engineering/review/code-simplicity-reviewer.md +109 -0
  22. package/agents/engineering/review/data-integrity-guardian.md +82 -0
  23. package/agents/engineering/review/data-migration-expert.md +98 -0
  24. package/agents/engineering/review/deployment-verification-agent.md +183 -0
  25. package/agents/engineering/review/dhh-rails-reviewer.md +46 -0
  26. package/agents/engineering/review/kieran-rails-reviewer.md +87 -0
  27. package/agents/engineering/review/legacy-code-expert.md +55 -0
  28. package/agents/engineering/review/observability-coverage-reviewer.md +147 -0
  29. package/agents/engineering/review/pattern-recognition-specialist.md +59 -0
  30. package/agents/engineering/review/performance-oracle.md +130 -0
  31. package/agents/engineering/review/security-sentinel.md +167 -0
  32. package/agents/engineering/review/semgrep-sast.md +36 -0
  33. package/agents/engineering/review/test-design-reviewer.md +150 -0
  34. package/agents/engineering/review/user-impact-reviewer.md +70 -0
  35. package/agents/engineering/workflow/pr-comment-resolver.md +69 -0
  36. package/agents/finance/budget-analyst.md +29 -0
  37. package/agents/finance/cfo.md +44 -0
  38. package/agents/finance/financial-reporter.md +30 -0
  39. package/agents/finance/revenue-analyst.md +30 -0
  40. package/agents/legal/clo.md +115 -0
  41. package/agents/legal/legal-compliance-auditor.md +110 -0
  42. package/agents/legal/legal-document-generator.md +134 -0
  43. package/agents/marketing/analytics-analyst.md +25 -0
  44. package/agents/marketing/brand-architect.md +181 -0
  45. package/agents/marketing/cmo.md +76 -0
  46. package/agents/marketing/conversion-optimizer.md +19 -0
  47. package/agents/marketing/copywriter.md +20 -0
  48. package/agents/marketing/fact-checker.md +70 -0
  49. package/agents/marketing/growth-strategist.md +145 -0
  50. package/agents/marketing/paid-media-strategist.md +28 -0
  51. package/agents/marketing/pricing-strategist.md +17 -0
  52. package/agents/marketing/programmatic-seo-specialist.md +25 -0
  53. package/agents/marketing/retention-strategist.md +23 -0
  54. package/agents/marketing/seo-aeo-analyst.md +164 -0
  55. package/agents/operations/coo.md +50 -0
  56. package/agents/operations/ops-advisor.md +78 -0
  57. package/agents/operations/ops-provisioner.md +101 -0
  58. package/agents/operations/ops-research.md +52 -0
  59. package/agents/operations/service-automator.md +278 -0
  60. package/agents/product/business-validator.md +206 -0
  61. package/agents/product/competitive-intelligence.md +89 -0
  62. package/agents/product/cpo.md +63 -0
  63. package/agents/product/design/ux-design-lead.md +209 -0
  64. package/agents/product/spec-flow-analyzer.md +122 -0
  65. package/agents/sales/cro.md +44 -0
  66. package/agents/sales/deal-architect.md +29 -0
  67. package/agents/sales/outbound-strategist.md +29 -0
  68. package/agents/sales/pipeline-analyst.md +29 -0
  69. package/agents/support/cco.md +48 -0
  70. package/agents/support/community-manager.md +570 -0
  71. package/agents/support/ticket-triage.md +70 -0
  72. package/bin/soleur-opencode.js +29 -0
  73. package/commands/go.md +659 -0
  74. package/commands/help.md +273 -0
  75. package/commands/sync.md +977 -0
  76. package/commands/version.md +103 -0
  77. package/hooks/antigravity-hooks.json +84 -0
  78. package/hooks/antigravity-session-start.sh +15 -0
  79. package/hooks/bare-root-cwd-guard.sh +131 -0
  80. package/hooks/browser-snapshot-credential-guard.sh +200 -0
  81. package/hooks/codex-session-start.sh +9 -0
  82. package/hooks/compaction-state.sh +525 -0
  83. package/hooks/destructive-command-guard.sh +1387 -0
  84. package/hooks/devin-session-start.sh +90 -0
  85. package/hooks/ext-filter-detect.sh +171 -0
  86. package/hooks/hooks.json +133 -0
  87. package/hooks/lib/hook-tool-kind.sh +24 -0
  88. package/hooks/lib/log-rotation.sh +176 -0
  89. package/hooks/lib/shell-argv.pl +576 -0
  90. package/hooks/memory-backstop.sh +1114 -0
  91. package/hooks/operator-stage-approval.sh +391 -0
  92. package/hooks/stop-hook.sh +367 -0
  93. package/hooks/unkept-promise-hook.sh +383 -0
  94. package/hooks/version-banner.sh +127 -0
  95. package/hooks/welcome-hook.sh +50 -0
  96. package/install.js +108 -0
  97. package/lib/agent-registry.ts +154 -0
  98. package/lib/c4-canonical-cli.mjs +34 -0
  99. package/lib/c4-canonical.mjs +46 -0
  100. package/lib/c4-from-components.ts +554 -0
  101. package/lib/go-routing.ts +81 -0
  102. package/lib/grok-inspect-contract.ts +311 -0
  103. package/lib/harness-model-map.ts +101 -0
  104. package/lib/harness-parity.ts +726 -0
  105. package/lib/harness-tool-map.ts +180 -0
  106. package/lib/harness.ts +716 -0
  107. package/lib/heartbeat-live-reconcile.ts +1153 -0
  108. package/lib/heartbeat-manifest.ts +342 -0
  109. package/lib/kb-coverage.ts +252 -0
  110. package/lib/pr-merge-poll.ts +103 -0
  111. package/lib/workflow-fidelity.ts +388 -0
  112. package/opencode/INSTRUCTIONS.md +152 -0
  113. package/opencode/agents/engineering/cto.md +10 -0
  114. package/opencode/agents/engineering/design/ddd-architect.md +10 -0
  115. package/opencode/agents/engineering/discovery/agent-finder.md +10 -0
  116. package/opencode/agents/engineering/discovery/functional-discovery.md +10 -0
  117. package/opencode/agents/engineering/infra/infra-security.md +10 -0
  118. package/opencode/agents/engineering/infra/platform-strategist.md +10 -0
  119. package/opencode/agents/engineering/infra/terraform-architect.md +10 -0
  120. package/opencode/agents/engineering/prompt-engineer.md +10 -0
  121. package/opencode/agents/engineering/research/best-practices-researcher.md +10 -0
  122. package/opencode/agents/engineering/research/framework-docs-researcher.md +10 -0
  123. package/opencode/agents/engineering/research/git-history-analyzer.md +10 -0
  124. package/opencode/agents/engineering/research/learnings-researcher.md +10 -0
  125. package/opencode/agents/engineering/research/repo-research-analyst.md +10 -0
  126. package/opencode/agents/engineering/review/agent-native-reviewer.md +10 -0
  127. package/opencode/agents/engineering/review/architecture-strategist.md +10 -0
  128. package/opencode/agents/engineering/review/code-quality-analyst.md +10 -0
  129. package/opencode/agents/engineering/review/code-simplicity-reviewer.md +10 -0
  130. package/opencode/agents/engineering/review/data-integrity-guardian.md +10 -0
  131. package/opencode/agents/engineering/review/data-migration-expert.md +10 -0
  132. package/opencode/agents/engineering/review/deployment-verification-agent.md +10 -0
  133. package/opencode/agents/engineering/review/dhh-rails-reviewer.md +10 -0
  134. package/opencode/agents/engineering/review/kieran-rails-reviewer.md +10 -0
  135. package/opencode/agents/engineering/review/legacy-code-expert.md +10 -0
  136. package/opencode/agents/engineering/review/observability-coverage-reviewer.md +10 -0
  137. package/opencode/agents/engineering/review/pattern-recognition-specialist.md +10 -0
  138. package/opencode/agents/engineering/review/performance-oracle.md +10 -0
  139. package/opencode/agents/engineering/review/security-sentinel.md +10 -0
  140. package/opencode/agents/engineering/review/semgrep-sast.md +10 -0
  141. package/opencode/agents/engineering/review/test-design-reviewer.md +10 -0
  142. package/opencode/agents/engineering/review/user-impact-reviewer.md +10 -0
  143. package/opencode/agents/engineering/workflow/pr-comment-resolver.md +10 -0
  144. package/opencode/agents/finance/budget-analyst.md +10 -0
  145. package/opencode/agents/finance/cfo.md +10 -0
  146. package/opencode/agents/finance/financial-reporter.md +10 -0
  147. package/opencode/agents/finance/revenue-analyst.md +10 -0
  148. package/opencode/agents/legal/clo.md +10 -0
  149. package/opencode/agents/legal/legal-compliance-auditor.md +10 -0
  150. package/opencode/agents/legal/legal-document-generator.md +10 -0
  151. package/opencode/agents/marketing/analytics-analyst.md +10 -0
  152. package/opencode/agents/marketing/brand-architect.md +10 -0
  153. package/opencode/agents/marketing/cmo.md +10 -0
  154. package/opencode/agents/marketing/conversion-optimizer.md +10 -0
  155. package/opencode/agents/marketing/copywriter.md +10 -0
  156. package/opencode/agents/marketing/fact-checker.md +10 -0
  157. package/opencode/agents/marketing/growth-strategist.md +10 -0
  158. package/opencode/agents/marketing/paid-media-strategist.md +10 -0
  159. package/opencode/agents/marketing/pricing-strategist.md +10 -0
  160. package/opencode/agents/marketing/programmatic-seo-specialist.md +10 -0
  161. package/opencode/agents/marketing/retention-strategist.md +10 -0
  162. package/opencode/agents/marketing/seo-aeo-analyst.md +10 -0
  163. package/opencode/agents/operations/coo.md +10 -0
  164. package/opencode/agents/operations/ops-advisor.md +10 -0
  165. package/opencode/agents/operations/ops-provisioner.md +10 -0
  166. package/opencode/agents/operations/ops-research.md +10 -0
  167. package/opencode/agents/operations/service-automator.md +10 -0
  168. package/opencode/agents/product/business-validator.md +10 -0
  169. package/opencode/agents/product/competitive-intelligence.md +10 -0
  170. package/opencode/agents/product/cpo.md +10 -0
  171. package/opencode/agents/product/design/ux-design-lead.md +10 -0
  172. package/opencode/agents/product/spec-flow-analyzer.md +10 -0
  173. package/opencode/agents/sales/cro.md +10 -0
  174. package/opencode/agents/sales/deal-architect.md +10 -0
  175. package/opencode/agents/sales/outbound-strategist.md +10 -0
  176. package/opencode/agents/sales/pipeline-analyst.md +10 -0
  177. package/opencode/agents/support/cco.md +10 -0
  178. package/opencode/agents/support/community-manager.md +10 -0
  179. package/opencode/agents/support/ticket-triage.md +10 -0
  180. package/opencode/commands/go.md +10 -0
  181. package/opencode/commands/help.md +10 -0
  182. package/opencode/commands/sync.md +10 -0
  183. package/opencode/commands/version.md +11 -0
  184. package/opencode/opencode.json +8 -0
  185. package/opencode/plugin.ts +493 -0
  186. package/package.json +36 -4
  187. package/scripts/admin-merge-ready.sh +545 -0
  188. package/scripts/admin-merge-ready.test.sh +753 -0
  189. package/scripts/affected-tests.sh +908 -0
  190. package/scripts/affected-tests.test.sh +1797 -0
  191. package/scripts/alpha-metrics.sh +78 -0
  192. package/scripts/alpha-metrics.test.sh +115 -0
  193. package/scripts/ancestor-signal-helpers.baseline.txt +16 -0
  194. package/scripts/audit-flag-flip.sh +107 -0
  195. package/scripts/check-red-on-main.sh +316 -0
  196. package/scripts/ci-head-verdict.sh +363 -0
  197. package/scripts/cloud-detect.sh +193 -0
  198. package/scripts/delegation-verify.sh +657 -0
  199. package/scripts/delegation-verify.test.sh +1424 -0
  200. package/scripts/deploy-arm.sh +475 -0
  201. package/scripts/domain-model-drift.sh +419 -0
  202. package/scripts/emit-decision.sh +144 -0
  203. package/scripts/emit-decision.test.sh +144 -0
  204. package/scripts/ext-filter-divergence-corpus.txt +15 -0
  205. package/scripts/ext-filter-divergence.sh +189 -0
  206. package/scripts/generate-c4-from-components.ts +442 -0
  207. package/scripts/grok-fidelity-gate.sh +68 -0
  208. package/scripts/grok-pre-push-gate.sh +232 -0
  209. package/scripts/harness-discovery-smoke.ts +906 -0
  210. package/scripts/harness-parity-census.ts +92 -0
  211. package/scripts/inngest-luks-alert-armed-probe.ts +29 -0
  212. package/scripts/install-opencode-adapter.ts +520 -0
  213. package/scripts/lib/affected-tests/common.sh +365 -0
  214. package/scripts/lib/affected-tests/go.sh +157 -0
  215. package/scripts/lib/affected-tests/js.sh +188 -0
  216. package/scripts/lib/affected-tests/nx-turbo.sh +98 -0
  217. package/scripts/lib/affected-tests/python.sh +185 -0
  218. package/scripts/lib/context-sanitize.sh +49 -0
  219. package/scripts/lib/domain-model-lib.sh +180 -0
  220. package/scripts/lib/ext-filter-grammar.tsv +34 -0
  221. package/scripts/lib/operator-script.sh +1236 -0
  222. package/scripts/lib/proc.sh +580 -0
  223. package/scripts/lib/session-state.sh +748 -0
  224. package/scripts/lib/tmp-classify.sh +843 -0
  225. package/scripts/lib/wrapper-boundary.sh +47 -0
  226. package/scripts/lint-ratchet.py +1547 -0
  227. package/scripts/monitor-pr-checks.sh +441 -0
  228. package/scripts/pipeline-tally.sh +756 -0
  229. package/scripts/pipeline-tally.test.sh +740 -0
  230. package/scripts/plugin-version.sh +528 -0
  231. package/scripts/precommit-guard.sh +113 -0
  232. package/scripts/precommit-guard.test.sh +254 -0
  233. package/scripts/reconcile-live-heartbeats.ts +705 -0
  234. package/scripts/render-c4-model.sh +217 -0
  235. package/scripts/resolve-git-root.sh +55 -0
  236. package/scripts/resolve-regenerable-conflicts.sh +363 -0
  237. package/scripts/resolve-regenerable-conflicts.test.sh +780 -0
  238. package/scripts/ruff-requirements.txt +22 -0
  239. package/scripts/run-in-pid-namespace.sh +98 -0
  240. package/scripts/run-in-pid-namespace.test.sh +290 -0
  241. package/scripts/scan-ancestor-signal-helpers.py +556 -0
  242. package/scripts/scan-ancestor-signal-helpers.test.sh +670 -0
  243. package/scripts/setup-ralph-loop.sh +238 -0
  244. package/scripts/sync-cursor-name-map.ts +316 -0
  245. package/scripts/sync-grok-agent-compat.ts +188 -0
  246. package/scripts/sync-npm-package.ts +178 -0
  247. package/scripts/sync-opencode-adapter.ts +479 -0
  248. package/scripts/sync-pr-behind.sh +534 -0
  249. package/scripts/sync-pr-behind.test.sh +252 -0
  250. package/scripts/taste-profile-update.sh +180 -0
  251. package/scripts/taste-profile-update.test.sh +167 -0
  252. package/scripts/write-budget-marker.sh +23 -0
  253. package/scripts/write-kb-coverage.ts +113 -0
  254. package/skills/admin-ip-refresh/SKILL.md +81 -0
  255. package/skills/admin-ip-refresh/references/admin-ip-refresh-procedure.md +201 -0
  256. package/skills/agent-browser/SKILL.md +698 -0
  257. package/skills/agent-browser/scripts/playwright-mcp-profile-slot.sh +304 -0
  258. package/skills/agent-browser/scripts/playwright-mcp-redact-proxy.py +964 -0
  259. package/skills/agent-browser/scripts/redact-a11y-snapshot.py +443 -0
  260. package/skills/agent-browser/test/fixtures/capture-playwright-mcp-fixtures.py +217 -0
  261. package/skills/agent-browser/test/fixtures/fake-passthrough-proxy.py +24 -0
  262. package/skills/agent-browser/test/fixtures/fake-playwright-mcp.py +131 -0
  263. package/skills/agent-browser/test/fixtures/make-proxy-mutant.py +48 -0
  264. package/skills/agent-browser/test/fixtures/odd-shapes/dialog-link-injection.json +10 -0
  265. package/skills/agent-browser/test/fixtures/odd-shapes/empty-content.json +5 -0
  266. package/skills/agent-browser/test/fixtures/odd-shapes/error-and-result.json +4 -0
  267. package/skills/agent-browser/test/fixtures/odd-shapes/error-code-bool.json +6 -0
  268. package/skills/agent-browser/test/fixtures/odd-shapes/error-code-string.json +6 -0
  269. package/skills/agent-browser/test/fixtures/odd-shapes/error-message-tree.json +6 -0
  270. package/skills/agent-browser/test/fixtures/odd-shapes/error-no-data.json +6 -0
  271. package/skills/agent-browser/test/fixtures/odd-shapes/error-string.json +4 -0
  272. package/skills/agent-browser/test/fixtures/odd-shapes/error-with-data.json +9 -0
  273. package/skills/agent-browser/test/fixtures/odd-shapes/evaluate-escaped-object.json +10 -0
  274. package/skills/agent-browser/test/fixtures/odd-shapes/invalid-utf8.json +4 -0
  275. package/skills/agent-browser/test/fixtures/odd-shapes/link-result.json +10 -0
  276. package/skills/agent-browser/test/fixtures/odd-shapes/list-two-results.json +6 -0
  277. package/skills/agent-browser/test/fixtures/odd-shapes/meta-in-result.json +13 -0
  278. package/skills/agent-browser/test/fixtures/odd-shapes/no-content.json +5 -0
  279. package/skills/agent-browser/test/fixtures/odd-shapes/quoted-key.json +10 -0
  280. package/skills/agent-browser/test/fixtures/odd-shapes/resource-block.json +18 -0
  281. package/skills/agent-browser/test/fixtures/odd-shapes/result-and-method.json +11 -0
  282. package/skills/agent-browser/test/fixtures/odd-shapes/result-string.json +3 -0
  283. package/skills/agent-browser/test/fixtures/odd-shapes/run-code-escaped-one-row.json +10 -0
  284. package/skills/agent-browser/test/fixtures/odd-shapes/run-code-escaped.json +10 -0
  285. package/skills/agent-browser/test/fixtures/odd-shapes/structured-content.json +13 -0
  286. package/skills/agent-browser/test/fixtures/odd-shapes/tools-list-no-tools.json +5 -0
  287. package/skills/agent-browser/test/fixtures/odd-shapes/tree-no-credential.json +10 -0
  288. package/skills/agent-browser/test/fixtures/odd-shapes/two-text-blocks.json +14 -0
  289. package/skills/agent-browser/test/fixtures/odd-shapes/unparsable-line.json +3 -0
  290. package/skills/agent-browser/test/fixtures/odd-shapes/value-contains-brace.json +10 -0
  291. package/skills/agent-browser/test/fixtures/odd-shapes/value-contains-link-substring.json +10 -0
  292. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/close.json +12 -0
  293. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/evaluate.json +12 -0
  294. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/find.json +12 -0
  295. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/framing.json +6 -0
  296. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/initialize.json +14 -0
  297. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/navigate-link.json +12 -0
  298. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/navigate.json +12 -0
  299. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/roots-list-request.json +5 -0
  300. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/screenshot.json +17 -0
  301. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot-filename.json +12 -0
  302. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot-meta-json.json +12 -0
  303. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot-meta-raw.json +12 -0
  304. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot.json +12 -0
  305. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/tools-list.json +827 -0
  306. package/skills/agent-browser/test/fixtures/proxy-session-driver.py +138 -0
  307. package/skills/agent-browser/test/fixtures/session-facts.py +102 -0
  308. package/skills/agent-browser/test/playwright-mcp-lifetime.test.sh +1620 -0
  309. package/skills/agent-browser/test/playwright-mcp-redact-proxy.test.sh +1599 -0
  310. package/skills/agent-browser/test/redact-a11y-snapshot.test.sh +527 -0
  311. package/skills/agent-native-architecture/SKILL.md +479 -0
  312. package/skills/agent-native-architecture/references/action-parity-discipline.md +430 -0
  313. package/skills/agent-native-architecture/references/agent-execution-patterns.md +483 -0
  314. package/skills/agent-native-architecture/references/agent-native-testing.md +605 -0
  315. package/skills/agent-native-architecture/references/architecture-patterns.md +493 -0
  316. package/skills/agent-native-architecture/references/dynamic-context-injection.md +358 -0
  317. package/skills/agent-native-architecture/references/files-universal-interface.md +314 -0
  318. package/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +381 -0
  319. package/skills/agent-native-architecture/references/mcp-tool-design.md +524 -0
  320. package/skills/agent-native-architecture/references/mobile-patterns.md +897 -0
  321. package/skills/agent-native-architecture/references/product-implications.md +478 -0
  322. package/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +342 -0
  323. package/skills/agent-native-architecture/references/self-modification.md +294 -0
  324. package/skills/agent-native-architecture/references/shared-workspace-architecture.md +700 -0
  325. package/skills/agent-native-architecture/references/system-prompt-design.md +263 -0
  326. package/skills/agent-native-audit/SKILL.md +300 -0
  327. package/skills/agent-native-audit/workflows/agent-native-audit.workflow.js +423 -0
  328. package/skills/andrew-kane-gem-writer/SKILL.md +189 -0
  329. package/skills/andrew-kane-gem-writer/references/database-adapters.md +231 -0
  330. package/skills/andrew-kane-gem-writer/references/module-organization.md +121 -0
  331. package/skills/andrew-kane-gem-writer/references/rails-integration.md +183 -0
  332. package/skills/andrew-kane-gem-writer/references/resources.md +126 -0
  333. package/skills/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
  334. package/skills/architecture/SKILL.md +436 -0
  335. package/skills/architecture/references/adr-template.md +154 -0
  336. package/skills/architecture/references/likec4-reference.md +136 -0
  337. package/skills/architecture/references/nfr-reference.md +126 -0
  338. package/skills/archive-kb/SKILL.md +86 -0
  339. package/skills/archive-kb/scripts/archive-kb.sh +300 -0
  340. package/skills/archive-kb/test/archive-kb-partial-run.test.sh +276 -0
  341. package/skills/atdd-developer/SKILL.md +65 -0
  342. package/skills/brainstorm/SKILL.md +701 -0
  343. package/skills/brainstorm/references/brainstorm-brand-workshop.md +153 -0
  344. package/skills/brainstorm/references/brainstorm-domain-config.md +63 -0
  345. package/skills/brainstorm/references/brainstorm-validation-workshop.md +65 -0
  346. package/skills/brainstorm/references/ui-surface-terms.md +42 -0
  347. package/skills/brainstorm-techniques/SKILL.md +231 -0
  348. package/skills/brainstorm-techniques/references/decision-principles.md +150 -0
  349. package/skills/brainstorm-techniques/references/phase-boundaries.md +103 -0
  350. package/skills/brainstorm-techniques/references/standing-delegation.md +407 -0
  351. package/skills/brainstorm-techniques/references/technical-ruling-delegation.md +174 -0
  352. package/skills/campaign-calendar/SKILL.md +145 -0
  353. package/skills/cf-token-scope/SKILL.md +164 -0
  354. package/skills/cf-token-scope/references/widen-playbook.md +114 -0
  355. package/skills/cf-token-scope/scripts/cf-token-scope.sh +253 -0
  356. package/skills/changelog/SKILL.md +142 -0
  357. package/skills/code-to-prd/SKILL.md +66 -0
  358. package/skills/code-to-prd/references/banner-template.md +12 -0
  359. package/skills/code-to-prd/references/prd-template.md +41 -0
  360. package/skills/code-to-prd/scripts/code-to-prd.sh +634 -0
  361. package/skills/code-to-prd/test/code-to-prd.test.sh +447 -0
  362. package/skills/code-to-prd/test/fixture/.env.example +7 -0
  363. package/skills/code-to-prd/test/fixture/app/about/page.tsx +11 -0
  364. package/skills/code-to-prd/test/fixture/app/api/health/route.ts +10 -0
  365. package/skills/code-to-prd/test/fixture/app/page.tsx +14 -0
  366. package/skills/code-to-prd/test/fixture/next.config.mjs +3 -0
  367. package/skills/code-to-prd/test/fixture/package.json +6 -0
  368. package/skills/cohort-status/SKILL.md +62 -0
  369. package/skills/community/SKILL.md +210 -0
  370. package/skills/community/scripts/bsky-community.sh +644 -0
  371. package/skills/community/scripts/bsky-setup.sh +631 -0
  372. package/skills/community/scripts/community-router.sh +93 -0
  373. package/skills/community/scripts/discord-community.sh +487 -0
  374. package/skills/community/scripts/discord-setup.sh +784 -0
  375. package/skills/community/scripts/github-community.sh +706 -0
  376. package/skills/community/scripts/hn-community.sh +233 -0
  377. package/skills/community/scripts/lib/hmac-sha1-b64.sh +126 -0
  378. package/skills/community/scripts/linkedin-community.sh +750 -0
  379. package/skills/community/scripts/linkedin-setup.sh +758 -0
  380. package/skills/community/scripts/x-community.sh +890 -0
  381. package/skills/community/scripts/x-setup.sh +713 -0
  382. package/skills/community/test/community-argv.test.sh +2922 -0
  383. package/skills/community/test/github-community.test.sh +981 -0
  384. package/skills/competitive-analysis/SKILL.md +69 -0
  385. package/skills/competitive-analysis/references/peer-plugin-audit.md +218 -0
  386. package/skills/compound/SKILL.md +673 -0
  387. package/skills/compound/scripts/token-efficiency-report.sh +334 -0
  388. package/skills/compound/test/phase-16.test.sh +379 -0
  389. package/skills/compound-capture/SKILL.md +791 -0
  390. package/skills/compound-capture/assets/critical-pattern-template.md +37 -0
  391. package/skills/compound-capture/assets/resolution-template.md +116 -0
  392. package/skills/compound-capture/references/yaml-schema.md +67 -0
  393. package/skills/compound-capture/schema.yaml +181 -0
  394. package/skills/constraint-scaffold/SKILL.md +175 -0
  395. package/skills/constraint-scaffold/references/boundary-readme.template +35 -0
  396. package/skills/constraint-scaffold/references/constraint-gates-workflow.template +69 -0
  397. package/skills/constraint-scaffold/references/depcruise-config.template +230 -0
  398. package/skills/constraint-scaffold/references/fix-constraints-stage-a.template +240 -0
  399. package/skills/constraint-scaffold/references/fix-constraints-stage-b.template +313 -0
  400. package/skills/constraint-scaffold/references/shared-runner.template +135 -0
  401. package/skills/constraint-scaffold/scripts/constraint-scaffold.sh +589 -0
  402. package/skills/constraint-scaffold/test/bite-proof.test.sh +1338 -0
  403. package/skills/constraint-scaffold/test/boundary.test.sh +734 -0
  404. package/skills/constraint-scaffold/test/emit-fix-constraints.test.sh +193 -0
  405. package/skills/constraint-scaffold/test/generator.test.sh +93 -0
  406. package/skills/constraint-scaffold/test/parity.test.sh +285 -0
  407. package/skills/content-writer/SKILL.md +242 -0
  408. package/skills/content-writer/scripts/blog-jargon-scan.sh +42 -0
  409. package/skills/cron-delete/SKILL.md +56 -0
  410. package/skills/cron-list/SKILL.md +54 -0
  411. package/skills/deepen-plan/SKILL.md +937 -0
  412. package/skills/deepen-plan/workflows/deepen-plan.workflow.js +488 -0
  413. package/skills/deploy/SKILL.md +115 -0
  414. package/skills/deploy/references/hetzner-setup.md +65 -0
  415. package/skills/deploy/scripts/deploy.sh +37 -0
  416. package/skills/deploy-docs/SKILL.md +95 -0
  417. package/skills/dhh-rails-style/SKILL.md +203 -0
  418. package/skills/dhh-rails-style/references/architecture.md +717 -0
  419. package/skills/dhh-rails-style/references/controllers.md +323 -0
  420. package/skills/dhh-rails-style/references/frontend.md +556 -0
  421. package/skills/dhh-rails-style/references/gems.md +316 -0
  422. package/skills/dhh-rails-style/references/models.md +388 -0
  423. package/skills/dhh-rails-style/references/testing.md +354 -0
  424. package/skills/discord-content/SKILL.md +126 -0
  425. package/skills/docs-site/SKILL.md +214 -0
  426. package/skills/drain-labeled-backlog/SKILL.md +203 -0
  427. package/skills/drain-labeled-backlog/scripts/group-by-area.sh +208 -0
  428. package/skills/drain-labeled-backlog/workflows/drain-labeled-backlog.workflow.js +433 -0
  429. package/skills/drain-prs/SKILL.md +142 -0
  430. package/skills/drain-prs/scripts/triage-prs.sh +192 -0
  431. package/skills/drain-prs/test/argv-ceiling.test.sh +183 -0
  432. package/skills/dspy-ruby/SKILL.md +634 -0
  433. package/skills/dspy-ruby/assets/config-template.rb +359 -0
  434. package/skills/dspy-ruby/assets/module-template.rb +326 -0
  435. package/skills/dspy-ruby/assets/signature-template.rb +143 -0
  436. package/skills/dspy-ruby/references/core-concepts.md +268 -0
  437. package/skills/dspy-ruby/references/optimization.md +623 -0
  438. package/skills/dspy-ruby/references/providers.md +335 -0
  439. package/skills/eval-harness/README.md +194 -0
  440. package/skills/eval-harness/SKILL.md +157 -0
  441. package/skills/eval-harness/enums/go-routes.json +13 -0
  442. package/skills/eval-harness/enums/incident-threshold.json +5 -0
  443. package/skills/eval-harness/enums/lane.json +5 -0
  444. package/skills/eval-harness/enums/tool-selection.json +20 -0
  445. package/skills/eval-harness/enums/triage-levels.json +5 -0
  446. package/skills/eval-harness/gated-skills.json +34 -0
  447. package/skills/eval-harness/models.generated.json +5 -0
  448. package/skills/eval-harness/promptfooconfig-go-routing.yaml +35 -0
  449. package/skills/eval-harness/promptfooconfig-incident-threshold.yaml +32 -0
  450. package/skills/eval-harness/promptfooconfig-lane-inference.yaml +33 -0
  451. package/skills/eval-harness/promptfooconfig-ticket-triage.yaml +34 -0
  452. package/skills/eval-harness/promptfooconfig-tool-selection.yaml +33 -0
  453. package/skills/eval-harness/prompts/go-baseline.txt +6 -0
  454. package/skills/eval-harness/prompts/go-skill.txt +21 -0
  455. package/skills/eval-harness/prompts/incident-threshold-baseline.txt +6 -0
  456. package/skills/eval-harness/prompts/incident-threshold-skill.txt +12 -0
  457. package/skills/eval-harness/prompts/lane-inference-baseline.txt +6 -0
  458. package/skills/eval-harness/prompts/lane-inference-skill.txt +15 -0
  459. package/skills/eval-harness/prompts/tool-selection-baseline.txt +19 -0
  460. package/skills/eval-harness/prompts/tool-selection-skill.txt +22 -0
  461. package/skills/eval-harness/prompts/triage-baseline.txt +7 -0
  462. package/skills/eval-harness/prompts/triage-skill.txt +10 -0
  463. package/skills/eval-harness/scripts/eval-gate.cjs +318 -0
  464. package/skills/eval-harness/scripts/extract-block.cjs +87 -0
  465. package/skills/eval-harness/scripts/gate-classification.cjs +28 -0
  466. package/skills/eval-harness/scripts/gen-models.sh +41 -0
  467. package/skills/eval-harness/scripts/gen-skill-prompt.cjs +177 -0
  468. package/skills/eval-harness/scripts/measure-classification.cjs +30 -0
  469. package/skills/eval-harness/scripts/parse-label.cjs +93 -0
  470. package/skills/eval-harness/scripts/verdict.cjs +132 -0
  471. package/skills/eval-harness/tasks/go-routing.jsonl +8 -0
  472. package/skills/eval-harness/tasks/incident-threshold.jsonl +7 -0
  473. package/skills/eval-harness/tasks/lane-inference.jsonl +7 -0
  474. package/skills/eval-harness/tasks/ticket-triage.jsonl +6 -0
  475. package/skills/eval-harness/tasks/tool-selection.jsonl +15 -0
  476. package/skills/eval-harness/test/eval-gate.test.sh +132 -0
  477. package/skills/eval-harness/test/extract-block.test.sh +87 -0
  478. package/skills/eval-harness/test/gate-classification.test.sh +77 -0
  479. package/skills/eval-harness/test/gen-models.test.sh +75 -0
  480. package/skills/eval-harness/test/measure-classification.test.sh +74 -0
  481. package/skills/eval-harness/test/parse-label.test.sh +129 -0
  482. package/skills/eval-harness/test/registry-completeness.test.sh +109 -0
  483. package/skills/eval-harness/test/verdict.test.sh +123 -0
  484. package/skills/every-style-editor/SKILL.md +142 -0
  485. package/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
  486. package/skills/feature-tweet/SKILL.md +199 -0
  487. package/skills/feature-video/SKILL.md +412 -0
  488. package/skills/feature-video/scripts/check_deps.sh +281 -0
  489. package/skills/file-todos/SKILL.md +267 -0
  490. package/skills/file-todos/assets/todo-template.md +166 -0
  491. package/skills/fix-issue/SKILL.md +278 -0
  492. package/skills/fix-issue/references/agent-authored-exclusion.md +118 -0
  493. package/skills/fix-issue/references/exclude-label-jq-snippet.md +96 -0
  494. package/skills/flag-bootstrap/SETUP.md +142 -0
  495. package/skills/flag-create/SKILL.md +145 -0
  496. package/skills/flag-create/scripts/create.sh +273 -0
  497. package/skills/flag-delete/SKILL.md +167 -0
  498. package/skills/flag-delete/scripts/delete.sh +288 -0
  499. package/skills/flag-list/SKILL.md +99 -0
  500. package/skills/flag-list/scripts/list.sh +275 -0
  501. package/skills/flag-set-role/SKILL.md +229 -0
  502. package/skills/flag-set-role/scripts/flip.sh +774 -0
  503. package/skills/frontend-anti-slop/SKILL.md +106 -0
  504. package/skills/frontend-anti-slop/references/anti-patterns.md +199 -0
  505. package/skills/frontend-anti-slop/references/slop-rules.md +65 -0
  506. package/skills/frontend-anti-slop/scripts/tier1-scan.ts +525 -0
  507. package/skills/frontend-design/SKILL.md +77 -0
  508. package/skills/gdpr-gate/NOTICE +136 -0
  509. package/skills/gdpr-gate/SKILL.md +341 -0
  510. package/skills/gdpr-gate/references/fields.md +160 -0
  511. package/skills/gdpr-gate/references/layers/api-layer.md +355 -0
  512. package/skills/gdpr-gate/references/layers/auth-sessions.md +290 -0
  513. package/skills/gdpr-gate/references/layers/data-in-transit.md +331 -0
  514. package/skills/gdpr-gate/references/layers/data-lifecycle.md +341 -0
  515. package/skills/gdpr-gate/references/layers/frontend.md +249 -0
  516. package/skills/gdpr-gate/references/layers/testing-seeding.md +256 -0
  517. package/skills/gdpr-gate/references/leakage-vectors.md +248 -0
  518. package/skills/gdpr-gate/references/legacy/legal-consent-v1-prose.md +48 -0
  519. package/skills/gdpr-gate/references/legal-consent.md +140 -0
  520. package/skills/gdpr-gate/references/non-negotiables.md +60 -0
  521. package/skills/gdpr-gate/scripts/gdpr-gate.sh +271 -0
  522. package/skills/gdpr-gate/scripts/notice-frontmatter.sh +337 -0
  523. package/skills/gdpr-gate/scripts/path-denylist.txt +39 -0
  524. package/skills/gdpr-gate/scripts/repo-scan.sh +158 -0
  525. package/skills/gdpr-gate/scripts/vendor-drift-classify.sh +120 -0
  526. package/skills/gdpr-gate/scripts/vendor-pin-integrity.sh +237 -0
  527. package/skills/gemini-imagegen/SKILL.md +283 -0
  528. package/skills/gemini-imagegen/requirements.txt +5 -0
  529. package/skills/gemini-imagegen/scripts/_error_handling.py +146 -0
  530. package/skills/gemini-imagegen/scripts/compose_images.py +150 -0
  531. package/skills/gemini-imagegen/scripts/edit_image.py +137 -0
  532. package/skills/gemini-imagegen/scripts/gemini_images.py +256 -0
  533. package/skills/gemini-imagegen/scripts/generate_image.py +147 -0
  534. package/skills/gemini-imagegen/scripts/multi_turn_chat.py +223 -0
  535. package/skills/gemini-imagegen/scripts/test_error_handling.py +179 -0
  536. package/skills/git-worktree/SKILL.md +534 -0
  537. package/skills/git-worktree/scripts/git-repo-readiness-diag.sh +98 -0
  538. package/skills/git-worktree/scripts/worktree-manager.sh +4773 -0
  539. package/skills/git-worktree/test/create-from-origin-main.test.sh +116 -0
  540. package/skills/git-worktree/test/lease-protects-active.test.sh +1056 -0
  541. package/skills/git-worktree/test/no-repo-fail-loud.test.sh +88 -0
  542. package/skills/git-worktree/test/orphan-reaper-honest-count.test.sh +379 -0
  543. package/skills/git-worktree/test/reap-archive-persistence.test.sh +725 -0
  544. package/skills/git-worktree/test/stale-lock-sweep.test.sh +176 -0
  545. package/skills/go/SKILL.md +54 -0
  546. package/skills/growth/SKILL.md +164 -0
  547. package/skills/harvest-debt/SKILL.md +78 -0
  548. package/skills/harvest-debt/scripts/harvest-debt.sh +114 -0
  549. package/skills/heal-skill/SKILL.md +173 -0
  550. package/skills/help/SKILL.md +46 -0
  551. package/skills/incident/NOTICE +39 -0
  552. package/skills/incident/SKILL.md +395 -0
  553. package/skills/incident/scripts/dry-run.sh +448 -0
  554. package/skills/incident/scripts/redact-engine.py +412 -0
  555. package/skills/incident/scripts/redact-sentinel.sh +44 -0
  556. package/skills/incident/templates/pir.md +139 -0
  557. package/skills/incident/test/fixtures/dry-run-incident.json +26 -0
  558. package/skills/incident/test/fixtures/dry-run-secret-leak.json +26 -0
  559. package/skills/incident/test/fixtures/legacy-bash-scanner.sh +82 -0
  560. package/skills/incident/test/fixtures/positive-corpus.md +87 -0
  561. package/skills/incident/test/redact-sentinel.test.sh +1420 -0
  562. package/skills/invoice/SKILL.md +206 -0
  563. package/skills/kb-glossary/SKILL.md +71 -0
  564. package/skills/kb-glossary/references/glossary-format.md +111 -0
  565. package/skills/kb-glossary/references/rejected-request-register.md +115 -0
  566. package/skills/kb-search/SKILL.md +276 -0
  567. package/skills/kb-search/scripts/kb-search-cache.sh +123 -0
  568. package/skills/legal-audit/SKILL.md +161 -0
  569. package/skills/legal-generate/NOTICE +242 -0
  570. package/skills/legal-generate/SKILL.md +244 -0
  571. package/skills/legal-generate/references/templates/advisor-agreement/template.md +12 -0
  572. package/skills/legal-generate/references/templates/business-associate-agreement/template.md +112 -0
  573. package/skills/legal-generate/references/templates/cookie-notice/template.md +82 -0
  574. package/skills/legal-generate/references/templates/dpa-global/template.md +439 -0
  575. package/skills/legal-generate/references/templates/dpa-us/template.md +222 -0
  576. package/skills/legal-generate/references/templates/employee-offer-letter/template.md +75 -0
  577. package/skills/legal-generate/references/templates/master-services-agreement/template.md +252 -0
  578. package/skills/legal-generate/references/templates/mutual-nda/template.md +66 -0
  579. package/skills/legal-generate/references/templates/one-way-nda/template.md +38 -0
  580. package/skills/legal-generate/references/templates/privacy-policy-gdpr/template.md +427 -0
  581. package/skills/legal-generate/references/templates/privacy-policy-us/template.md +313 -0
  582. package/skills/legal-generate/references/templates/terms-of-use/template.md +75 -0
  583. package/skills/legal-generate/scripts/strip-vendor-credit.sh +96 -0
  584. package/skills/linear-fetch/SKILL.md +247 -0
  585. package/skills/linear-fetch/scripts/assert-no-linear-telemetry.sh +66 -0
  586. package/skills/linear-fetch/scripts/redact-linear-urls.sh +51 -0
  587. package/skills/linear-fetch/scripts/render-caller-template.sh +36 -0
  588. package/skills/linear-fetch/test/assert-no-linear-telemetry.test.sh +113 -0
  589. package/skills/linear-fetch/test/parity.test.sh +112 -0
  590. package/skills/linear-fetch/test/persist-safe-integration.test.sh +191 -0
  591. package/skills/linear-fetch/test/redact-linear-urls.test.sh +226 -0
  592. package/skills/merge-pr/SKILL.md +668 -0
  593. package/skills/model-launch-review/SKILL.md +255 -0
  594. package/skills/model-launch-review/scripts/audit-models.sh +501 -0
  595. package/skills/one-shot/SKILL.md +316 -0
  596. package/skills/one-shot/references/token-discipline.md +74 -0
  597. package/skills/operator-bootstrap/SKILL.md +412 -0
  598. package/skills/operator-bootstrap/template.sh +439 -0
  599. package/skills/operator-digest/SKILL.md +287 -0
  600. package/skills/operator-digest/assets/operator-digest.workflow.yml +175 -0
  601. package/skills/operator-digest/scripts/digest-scrub.sh +119 -0
  602. package/skills/operator-digest/scripts/provision-operator-digest-repo.sh +147 -0
  603. package/skills/operator-rephrase/SKILL.md +98 -0
  604. package/skills/pencil-setup/SKILL.md +201 -0
  605. package/skills/pencil-setup/scripts/check_deps.sh +556 -0
  606. package/skills/pencil-setup/scripts/copy_adapter.sh +80 -0
  607. package/skills/pencil-setup/scripts/package-lock.json +1172 -0
  608. package/skills/pencil-setup/scripts/package.json +14 -0
  609. package/skills/pencil-setup/scripts/pencil-error-enrichment.mjs +21 -0
  610. package/skills/pencil-setup/scripts/pencil-mcp-adapter.mjs +733 -0
  611. package/skills/pencil-setup/scripts/pencil-response-classify.mjs +38 -0
  612. package/skills/pencil-setup/scripts/pencil-save-gate.mjs +26 -0
  613. package/skills/pencil-setup/scripts/sanitize-filename.mjs +25 -0
  614. package/skills/plan/SKILL.md +1140 -0
  615. package/skills/plan/references/differential-migration-testing.md +185 -0
  616. package/skills/plan/references/plan-community-discovery.md +42 -0
  617. package/skills/plan/references/plan-founder-check.md +137 -0
  618. package/skills/plan/references/plan-functional-overlap.md +25 -0
  619. package/skills/plan/references/plan-host-fanout.md +51 -0
  620. package/skills/plan/references/plan-issue-templates.md +779 -0
  621. package/skills/plan/references/plan-network-outage-checklist.md +131 -0
  622. package/skills/plan/references/plan-scope-check.md +126 -0
  623. package/skills/plan/references/plan-sharp-edges.md +325 -0
  624. package/skills/plan/references/plan-signoff-lifecycle.md +12 -0
  625. package/skills/plan/references/webapp-cwv-observability.md +143 -0
  626. package/skills/plan-review/SKILL.md +91 -0
  627. package/skills/plan-review/lib/named-panel.mjs +58 -0
  628. package/skills/plan-review/workflows/plan-review.workflow.js +477 -0
  629. package/skills/postmerge/SKILL.md +642 -0
  630. package/skills/postmerge/references/deploy-status-debugging.md +138 -0
  631. package/skills/postmerge/references/graceful-degradation.md +20 -0
  632. package/skills/postmerge/references/sentry-error-count-delta.md +137 -0
  633. package/skills/preflight/SKILL.md +1553 -0
  634. package/skills/preflight/references/check-13-founder-check.md +286 -0
  635. package/skills/preflight/scripts/founder-check.py +1640 -0
  636. package/skills/preflight/scripts/parse-form-a.awk +107 -0
  637. package/skills/preflight/scripts/probe-verb-gate.sh +108 -0
  638. package/skills/product-roadmap/SKILL.md +357 -0
  639. package/skills/product-roadmap/scripts/roadmap-reconcile.sh +327 -0
  640. package/skills/provision-cloudflare/SKILL.md +67 -0
  641. package/skills/provision-cloudflare/scripts/provision-cloudflare.sh +271 -0
  642. package/skills/provision-doppler/SKILL.md +58 -0
  643. package/skills/provision-doppler/scripts/provision-doppler.sh +358 -0
  644. package/skills/provision-github/SKILL.md +59 -0
  645. package/skills/provision-github/scripts/provision-github.sh +300 -0
  646. package/skills/provision-hetzner/SKILL.md +76 -0
  647. package/skills/provision-hetzner/scripts/provision-hetzner.sh +262 -0
  648. package/skills/provision-hetzner/test/fixture/knowledge-base/legal/tenant-dpa-register.md +26 -0
  649. package/skills/provision-hetzner/test/fixture/provision-hetzner-dry-run.golden +34 -0
  650. package/skills/provision-hetzner/test/provision-hetzner-characterization.test.sh +227 -0
  651. package/skills/qa/SKILL.md +263 -0
  652. package/skills/qa/references/browser-preflight.md +22 -0
  653. package/skills/questionnaire-generate/SKILL.md +328 -0
  654. package/skills/questionnaire-generate/references/questionnaire.template +60 -0
  655. package/skills/rclone/SKILL.md +162 -0
  656. package/skills/rclone/scripts/check_setup.sh +63 -0
  657. package/skills/release-announce/SKILL.md +65 -0
  658. package/skills/release-docs/SKILL.md +92 -0
  659. package/skills/reproduce-bug/SKILL.md +358 -0
  660. package/skills/resolve-debt/SKILL.md +85 -0
  661. package/skills/resolve-debt/scripts/resolve-debt.py +584 -0
  662. package/skills/resolve-parallel/SKILL.md +45 -0
  663. package/skills/resolve-parallel/workflows/resolve-parallel.workflow.js +415 -0
  664. package/skills/resolve-pr-parallel/SKILL.md +68 -0
  665. package/skills/resolve-pr-parallel/workflows/resolve-pr-parallel.workflow.js +419 -0
  666. package/skills/resolve-todo-parallel/SKILL.md +57 -0
  667. package/skills/resolve-todo-parallel/workflows/resolve-todo-parallel.workflow.js +398 -0
  668. package/skills/review/SKILL.md +1621 -0
  669. package/skills/review/references/anti-slop-scanner-hook.md +40 -0
  670. package/skills/review/references/defect-classes-migrations.md +9 -0
  671. package/skills/review/references/generated-input-oracle-surfaces.md +15 -0
  672. package/skills/review/references/review-e2e-testing.md +116 -0
  673. package/skills/review/references/review-todo-structure.md +165 -0
  674. package/skills/review/references/risk-tier-and-fix-rounds.md +95 -0
  675. package/skills/review/references/semgrep-custom-rules.yaml +174 -0
  676. package/skills/review/references/ultrathink-phases.md +98 -0
  677. package/skills/review/references/wfs.md +6 -0
  678. package/skills/review/scripts/emit-review-trailer.sh +499 -0
  679. package/skills/review/scripts/ensure-semgrep.sh +99 -0
  680. package/skills/review/scripts/fix-round-seats.sh +197 -0
  681. package/skills/review/test/emit-review-trailer.test.sh +387 -0
  682. package/skills/review/test/ensure-semgrep.test.sh +111 -0
  683. package/skills/review/test/fix-round-seats.test.sh +237 -0
  684. package/skills/review/workflows/README.md +147 -0
  685. package/skills/review/workflows/review.workflow.js +667 -0
  686. package/skills/schedule/SKILL.md +796 -0
  687. package/skills/seo-aeo/SKILL.md +139 -0
  688. package/skills/seo-aeo/scripts/validate-csp.sh +233 -0
  689. package/skills/seo-aeo/scripts/validate-seo.sh +262 -0
  690. package/skills/ship/SKILL.md +3022 -0
  691. package/skills/ship/references/adr-ordinal-collision.md +47 -0
  692. package/skills/ship/references/affected-gate-contract.md +115 -0
  693. package/skills/ship/references/ci-run-diagnosis.md +29 -0
  694. package/skills/ship/references/ci-workflow-authoring.md +68 -0
  695. package/skills/ship/references/counsel-review-clo-attestation.md +38 -0
  696. package/skills/ship/references/cto-ruling-armed-push.md +5 -0
  697. package/skills/ship/references/decision-challenges-render.md +71 -0
  698. package/skills/ship/references/detached-battery-verdict.md +13 -0
  699. package/skills/ship/references/feature-tweet-draft.md +42 -0
  700. package/skills/ship/references/followthrough-issue-body-template.md +47 -0
  701. package/skills/ship/references/followthrough-stub-template.sh +92 -0
  702. package/skills/ship/references/merge-queue-dequeue.md +39 -0
  703. package/skills/ship/references/operator-holds.md +149 -0
  704. package/skills/ship/references/pipeline-tally-render.md +43 -0
  705. package/skills/ship/references/pr-overlap.md +153 -0
  706. package/skills/ship/references/queue-mode.md +7 -0
  707. package/skills/ship/references/ready-run-wait.md +36 -0
  708. package/skills/ship/references/red-on-main-quarantine.md +110 -0
  709. package/skills/ship/references/register-update-pr-pattern.md +88 -0
  710. package/skills/ship/references/settle-then-admin-merge.md +130 -0
  711. package/skills/ship/references/supabase-column-probe.md +13 -0
  712. package/skills/ship/scripts/auto-close-scan.sh +76 -0
  713. package/skills/ship/scripts/battery-owed.sh +403 -0
  714. package/skills/ship/scripts/hold-gate-eval.sh +410 -0
  715. package/skills/ship/scripts/net-issue-flow.sh +665 -0
  716. package/skills/ship/scripts/pr-overlap-preflight.sh +483 -0
  717. package/skills/ship/scripts/ship-pir-action-items-gate.sh +246 -0
  718. package/skills/ship/scripts/technical-ruling-gate.sh +444 -0
  719. package/skills/skill-creator/SKILL.md +299 -0
  720. package/skills/skill-creator/references/api-security.md +248 -0
  721. package/skills/skill-creator/references/authoring-levers.md +110 -0
  722. package/skills/skill-creator/references/be-clear-and-direct.md +587 -0
  723. package/skills/skill-creator/references/best-practices.md +431 -0
  724. package/skills/skill-creator/references/common-patterns.md +639 -0
  725. package/skills/skill-creator/references/core-principles.md +193 -0
  726. package/skills/skill-creator/references/executable-code.md +188 -0
  727. package/skills/skill-creator/references/iteration-and-testing.md +509 -0
  728. package/skills/skill-creator/references/official-spec.md +193 -0
  729. package/skills/skill-creator/references/recommended-structure.md +180 -0
  730. package/skills/skill-creator/references/skill-structure.md +396 -0
  731. package/skills/skill-creator/references/using-scripts.md +122 -0
  732. package/skills/skill-creator/references/using-templates.md +120 -0
  733. package/skills/skill-creator/references/workflows-and-validation.md +541 -0
  734. package/skills/skill-creator/scripts/init_skill.py +322 -0
  735. package/skills/skill-creator/scripts/package_skill.py +110 -0
  736. package/skills/skill-creator/scripts/quick_validate.py +64 -0
  737. package/skills/skill-creator/templates/router-skill.md +73 -0
  738. package/skills/skill-creator/templates/simple-skill.md +34 -0
  739. package/skills/skill-creator/workflows/add-reference.md +96 -0
  740. package/skills/skill-creator/workflows/add-script.md +104 -0
  741. package/skills/skill-creator/workflows/add-template.md +83 -0
  742. package/skills/skill-creator/workflows/add-workflow.md +128 -0
  743. package/skills/skill-creator/workflows/audit-skill.md +159 -0
  744. package/skills/skill-creator/workflows/create-domain-expertise-skill.md +623 -0
  745. package/skills/skill-creator/workflows/create-new-skill.md +207 -0
  746. package/skills/skill-creator/workflows/get-guidance.md +134 -0
  747. package/skills/skill-creator/workflows/upgrade-to-router.md +170 -0
  748. package/skills/skill-creator/workflows/verify-skill.md +226 -0
  749. package/skills/skill-security-scan/SKILL.md +145 -0
  750. package/skills/skill-security-scan/references/disclaimer.md +21 -0
  751. package/skills/skill-security-scan/references/first-party-allowlist.yaml +38 -0
  752. package/skills/skill-security-scan/references/override-artifact-schema.json +68 -0
  753. package/skills/skill-security-scan/references/override-mechanism.md +171 -0
  754. package/skills/skill-security-scan/references/redirect-domains.yaml +27 -0
  755. package/skills/skill-security-scan/references/regex-patterns.md +136 -0
  756. package/skills/skill-security-scan/references/rules/code-exec.yaml +70 -0
  757. package/skills/skill-security-scan/references/rules/filesystem-boundary.yaml +29 -0
  758. package/skills/skill-security-scan/references/rules/manifest.yaml +44 -0
  759. package/skills/skill-security-scan/references/rules/supply-chain.yaml +36 -0
  760. package/skills/skill-security-scan/references/scan-meta-schema.json +63 -0
  761. package/skills/skill-security-scan/references/test-fixtures/clean-soleur-style.skill.md +23 -0
  762. package/skills/skill-security-scan/references/test-fixtures/clean-third-party.skill.md +26 -0
  763. package/skills/skill-security-scan/references/test-fixtures/malicious-codeexec.skill.md +23 -0
  764. package/skills/skill-security-scan/references/test-fixtures/malicious-curl-pipe-bash.skill.md +69 -0
  765. package/skills/skill-security-scan/references/test-fixtures/malicious-prompt-injection.skill.md +17 -0
  766. package/skills/skill-security-scan/references/test-fixtures/malicious-telemetry-beacon.skill.md +23 -0
  767. package/skills/skill-security-scan/references/typosquat-targets.yaml +65 -0
  768. package/skills/skill-security-scan/scripts/check-codeexec.sh +41 -0
  769. package/skills/skill-security-scan/scripts/check-filesystem-boundary.sh +19 -0
  770. package/skills/skill-security-scan/scripts/check-prompt-injection.sh +95 -0
  771. package/skills/skill-security-scan/scripts/check-supply-chain.sh +219 -0
  772. package/skills/skill-security-scan/scripts/check-telemetry-surface.sh +126 -0
  773. package/skills/skill-security-scan/scripts/lib.sh +197 -0
  774. package/skills/skill-security-scan/scripts/parse-override.sh +231 -0
  775. package/skills/skill-security-scan/scripts/run-scan.sh +342 -0
  776. package/skills/skill-security-scan/scripts/run-self-test.sh +95 -0
  777. package/skills/skill-security-scan/test/argv-ceiling.test.sh +180 -0
  778. package/skills/skill-security-scan/test/meta-dir-reap.test.sh +411 -0
  779. package/skills/skill-security-scan/test/parse-override.test.sh +607 -0
  780. package/skills/skill-security-scan/test/redact-pii-ghs-shape.test.sh +251 -0
  781. package/skills/social-distribute/SKILL.md +485 -0
  782. package/skills/spec-templates/SKILL.md +274 -0
  783. package/skills/sync/SKILL.md +49 -0
  784. package/skills/test-browser/SKILL.md +464 -0
  785. package/skills/test-browser/references/feature-map.md +161 -0
  786. package/skills/test-fix-loop/SKILL.md +159 -0
  787. package/skills/test-fix-loop/references/determinism-checklist.md +154 -0
  788. package/skills/triage/SKILL.md +402 -0
  789. package/skills/trigger-cron/SKILL.md +136 -0
  790. package/skills/trigger-cron/scripts/trigger.sh +198 -0
  791. package/skills/user-set-role/SKILL.md +147 -0
  792. package/skills/user-set-role/scripts/set-role.sh +218 -0
  793. package/skills/user-story-writer/SKILL.md +77 -0
  794. package/skills/ux-audit/SKILL.md +251 -0
  795. package/skills/ux-audit/references/finding.schema.json +62 -0
  796. package/skills/ux-audit/references/route-list.yaml +95 -0
  797. package/skills/ux-audit/scripts/bot-fixture.ts +270 -0
  798. package/skills/ux-audit/scripts/bot-signin.ts +135 -0
  799. package/skills/ux-audit/scripts/dedup-hash.ts +46 -0
  800. package/skills/work/SKILL.md +1479 -0
  801. package/skills/work/references/end-of-work-resume.md +7 -0
  802. package/skills/work/references/hook-test-main-cwd.md +24 -0
  803. package/skills/work/references/key-principles.md +83 -0
  804. package/skills/work/references/property-testing.md +122 -0
  805. package/skills/work/references/sql-security-definer-rpc-scaffold.sql +78 -0
  806. package/skills/work/references/work-agent-teams.md +97 -0
  807. package/skills/work/references/work-lifecycle-parallel.md +147 -0
  808. package/skills/work/references/work-lockfile-bumps.md +97 -0
  809. package/skills/work/references/work-scratch-sandboxes.md +212 -0
  810. package/skills/work/references/work-subagent-fanout.md +61 -0
  811. package/skills/work/references/work-test-harness-sharp-edges.md +73 -0
  812. package/skills/xcode-test/SKILL.md +360 -0
@@ -0,0 +1,726 @@
1
+ /**
2
+ * Harness parity — every component reference in plugin prose is the canonical `soleur:<name>`.
3
+ *
4
+ * ADR-226 (#8299). A skill, command or agent named in a SKILL.md, command or agent doc is read by an
5
+ * agent on FIVE harnesses (Claude, Grok Build, Codex, Devin, OpenCode), and each has its own
6
+ * invocation syntax (`/plan`, `/soleur:plan`, `$soleur:plan`, `soleur:plan`). The doc must name
7
+ * the component in the one shape every adapter resolves — `soleur:<name>` for skills and
8
+ * commands, the registry id for agents — and leave rendering to `formatSkillInvocation` /
9
+ * `spawnAgent` in `harness.ts`.
10
+ *
11
+ * This module is an ALLOWLIST over a closed index (AP-025 / ADR-202: a state predicate is
12
+ * complete by construction; a list of forbidden forms cannot be proven complete). The index is
13
+ * derived from the tree, never listed: skill directories, command files, and the agent registry
14
+ * (`discoverAgentPaths()` — the same function Grok's compat stubs are generated from). Anything
15
+ * that names a known component in a context the allowlist does not admit is NONCANONICAL.
16
+ *
17
+ * Four chokepoints, none a member list (plan v3 §Guard Contract):
18
+ * 1. POPULATION_GLOBS — which docs are examined, each carrying its region policy;
19
+ * 2. INDEX_GLOBS + the registry — which names are known;
20
+ * 3. classifyDoc — the single verdict site: BOUNDARY, the path class, the token class, the
21
+ * trailing-glue strip, rules R1–R9 and the marker grammar;
22
+ * 4. the tree test's own literal pathspecs for the index invariant (harness-parity-tree.test.ts).
23
+ * `census` folds and `fixDoc` inverts; neither decides.
24
+ *
25
+ * The permanent fixtures in test/harness-parity.test.ts pin every constant below. Weakening the
26
+ * gate means editing a constant AND the fixture that pins it, in one reviewable diff.
27
+ */
28
+
29
+ import { execFileSync } from "child_process";
30
+ import { readFileSync } from "fs";
31
+ import { basename, resolve } from "path";
32
+ import {
33
+ PLUGIN_ROOT,
34
+ discoverAgentPaths,
35
+ pathToAgentId,
36
+ agentIdToGrokSubagentType,
37
+ } from "./agent-registry";
38
+
39
+ export type RegionPolicy = "skill" | "command" | "agent";
40
+
41
+ export type Verdict = "CANONICAL" | "BARE" | "PATH" | "NONCANONICAL" | "UNKNOWN-NS" | "EXEMPT" | "SELF-NAME";
42
+
43
+ /** Which rule produced a NONCANONICAL verdict — the census's shape split. */
44
+ export type Shape =
45
+ | "ns-sigil" // R1: `/soleur:x`, `$soleur:x`, `/soleur:<metavar>`
46
+ | "agent-mention" // R4: `@agent-soleur:product:cpo`
47
+ | "grok-stem" // R5: `soleur-product-cpo` in prose
48
+ | "sigil-skill" // R8: `/plan`, `$plan`, `%plan`
49
+ | "bare-agent-leaf" // R6b: `Task cpo(…)`, "the `cpo`"
50
+ | "sigil-agent-leaf" // R6b with a sigil: `/cpo`
51
+ | "hyphen-absorbed"; // R9: `@agent-cpo`, `@agent-soleur-product-cpo`
52
+
53
+ /**
54
+ * Harness attribution decorates the message and the report split. It is a LOCAL lookup over
55
+ * (shape, preceding character) and never participates in the verdict, so an incomplete table
56
+ * cannot blind the gate — `unrecognised` is the catch-all.
57
+ */
58
+ export type Attribution =
59
+ | "grok" // `/name` (grok slash) or a hyphen stem
60
+ | "claude-devin" // `/soleur:name`
61
+ | "codex" // `$soleur:name`
62
+ | "claude-agent" // `@agent-soleur:…`
63
+ | "claude-leaf" // `@agent-<leaf>`
64
+ | "grok-stem" // `soleur-a-b` in prose
65
+ | "bare-leaf" // an agent leaf with no namespace — dead on grok
66
+ | "unrecognised"; // any sigil nobody has thought of
67
+
68
+ export interface PopulationGlob {
69
+ readonly pathspec: string;
70
+ readonly regionPolicy: RegionPolicy;
71
+ }
72
+
73
+ /**
74
+ * The names the index is built from. Separate from POPULATION_GLOBS on purpose (spec-flow #7b):
75
+ * emptying the population fires "0 docs examined", never the index invariant, and vice versa.
76
+ * `:(glob)` magic is load-bearing — a plain `skills/*\/SKILL.md` pathspec crosses `/` in git and
77
+ * would enumerate `skills/x/references/SKILL.md` as a skill named `references` (arch F5).
78
+ */
79
+ export const INDEX_GLOBS = {
80
+ skills: ":(glob)plugins/soleur/skills/*/SKILL.md",
81
+ commands: ":(glob)plugins/soleur/commands/*.md",
82
+ } as const;
83
+
84
+ /** The references glob, named so the `SKILL.md` carve-out below can be scoped to it. */
85
+ const REFERENCES_PATHSPEC = ":(glob)plugins/soleur/skills/*/references/**/*.md";
86
+
87
+ /**
88
+ * The docs examined. `regionPolicy` is derived from WHICH glob matched, so one constant carries
89
+ * both membership and policy.
90
+ *
91
+ * NG-P widening (#8317) is COMPLETE as of 2026-09-24. The references half (2026-09-23) needed
92
+ * `**` support in `globToRegex`, the nested-`SKILL.md` exclusion below, and the tree test's
93
+ * "no nested SKILL.md" assertion. The agent-body half needed the self-name carve-out: every
94
+ * agent opens with its own leaf as a frontmatter `name:` value, which R6b would classify
95
+ * NONCANONICAL and which cannot be rewritten (see `findSelfNameLine`). The `agent` policy
96
+ * scopes that carve-out; it does NOT honour harness-forms regions — only `command` does.
97
+ */
98
+ export const POPULATION_GLOBS: readonly PopulationGlob[] = [
99
+ { pathspec: ":(glob)plugins/soleur/skills/*/SKILL.md", regionPolicy: "skill" },
100
+ { pathspec: ":(glob)plugins/soleur/commands/*.md", regionPolicy: "command" },
101
+ // The Codex and Devin front doors: the same routing prose one harness over (arch F6).
102
+ { pathspec: ":(glob)plugins/soleur/codex/skills/*/SKILL.md", regionPolicy: "skill" },
103
+ { pathspec: ":(glob)plugins/soleur/devin/skills/*/SKILL.md", regionPolicy: "skill" },
104
+ // The OpenCode front doors. They are the ONLY harness whose shims are prose rather
105
+ // than a copy of this skill tree: OpenCode registers the canonical `skills/` directory
106
+ // through a config key (ADR-291), so the component references an OpenCode session
107
+ // meets are the ones in the GENERATED agent stubs and commands. Both were outside the
108
+ // census before this row, which is the whole reason a wrong id could ship from the
109
+ // generator and only show up as a failed spawn in a live session.
110
+ { pathspec: ":(glob)plugins/soleur/opencode/agents/**/*.md", regionPolicy: "agent" },
111
+ { pathspec: ":(glob)plugins/soleur/opencode/commands/*.md", regionPolicy: "command" },
112
+ // The references half of NG-P (#8317). These are agent-read on every harness —
113
+ // `plan-sharp-edges.md` alone is loaded by plan Phase 6.5 — so their component
114
+ // references carry the same obligation as the entry files', and they were outside
115
+ // the census entirely until now. A nested `SKILL.md` here is excluded in
116
+ // `regionPolicyForPath`, scoped to THIS glob.
117
+ { pathspec: REFERENCES_PATHSPEC, regionPolicy: "skill" },
118
+ // The agent-body half of NG-P (#8317). Every registry agent is read by every spawned
119
+ // subagent on every harness. `agents/` holds only agent definitions (Claude loads every
120
+ // `.md` under it as a subagent), so this glob IS the registry; the tree test pins that.
121
+ { pathspec: ":(glob)plugins/soleur/agents/**/*.md", regionPolicy: "agent" },
122
+ ];
123
+
124
+ /** Docs excluded by path, each with the reason it is not wrapped in a region instead. */
125
+ export const EXCLUDED_BY_PATH: ReadonlyMap<string, string> = new Map([
126
+ // The Devin CLI entry-point shims. Same category as commands/help.md and by the same clause of
127
+ // ADR-226 §4: a doc whose SUBJECT is the per-harness typed form is a human-typed entry point,
128
+ // enumerated per harness by design. Each opens "You are the `/soleur:<name>` slash command for
129
+ // the Soleur plugin on Devin CLI" — canonicalising that makes the sentence FALSE, because
130
+ // `/soleur:<name>` is exactly what the Devin adapter emits (harness.ts) and what the operator
131
+ // types. The earlier revision rewrote all three; measured, it turned each shim's own
132
+ // self-description into a form its harness does not have (#8299).
133
+ ...(["go", "help", "sync"] as const).map(
134
+ (n) =>
135
+ [
136
+ `plugins/soleur/skills/${n}/SKILL.md`,
137
+ "Devin CLI entry-point shim: its subject IS the typed slash form, so the canonical shape would make its own self-description false (ADR-226 §4, human-typed entry point)",
138
+ ] as [string, string],
139
+ ),
140
+ [
141
+ "plugins/soleur/commands/help.md",
142
+ "its subject is the per-harness typed forms an operator types; measured, exempting it by region instead needs 11 marker pairs (22 lines) around 19 content lines carrying 34 sites, which is a whole-file exemption wearing markers (DHH #5)",
143
+ ],
144
+ ]);
145
+
146
+ /**
147
+ * BOUNDARY — the allowlist of characters permitted immediately before a reference. No harness
148
+ * uses any of them as an invocation sigil (every
149
+ * `formatSkillInvocation` branch and `formatAgentSpawn` read). Anything NOT in the set — `/`,
150
+ * `$`, `@`, `!`, `%`, `~`, `\`, and any character nobody has thought of — is a sigil. Authors
151
+ * who collide with the set (a shell `$work`, a glob `"$d"/rclone-*`) brace or rename the
152
+ * identifier; BOUNDARY is never widened to admit a sigil.
153
+ *
154
+ * Eighteen members are load-bearing on the tree today — deleting one produces false positives,
155
+ * from 3778 for a space down to 1 for `#`. Five (`\t`, `;`, `&`, em dash, en dash) currently
156
+ * prevent zero, and are kept as ANTICIPATED rather than measured: each is ordinary prose
157
+ * punctuation, and a missing member costs a false RED on an unrelated PR while a spurious one
158
+ * costs nothing unless a harness adopts it as a sigil (§Consequences of ADR-226 makes re-reading
159
+ * this set an obligation when a fifth harness lands). Do not read the whole set as measured.
160
+ */
161
+ export const BOUNDARY: ReadonlySet<string> = new Set([
162
+ " ", // whitespace — prose
163
+ "\t",
164
+ "`", // inline code: `soleur:plan`
165
+ '"', // quoted string
166
+ "'",
167
+ "(", // parenthetical / call: Skill(soleur:go)
168
+ "[", // markdown link text
169
+ "{", // brace: `${work}` — the sanctioned way to keep a shell variable out of the gate
170
+ "*", // emphasis: **work**
171
+ "<", // angle: <plan>
172
+ ">", // blockquote / angle close
173
+ "|", // table cell
174
+ ",", // list separator
175
+ ";",
176
+ ".", // dotfile / extension: `.go`
177
+ "=", // KEY=value
178
+ "+", // plan+work
179
+ "&", // URL query and shell `&&`
180
+ "?", // URL query
181
+ "#", // markdown anchor `#plan-heading` and issue refs `#NNNN` (simplicity F6)
182
+ "→", // plan→work
183
+ "—", // em dash
184
+ "–", // en dash
185
+ ]);
186
+
187
+ /**
188
+ * The path class — the SECOND verdict-deciding allowlist. `pathctx` holds when the character
189
+ * before the token is `/` and the character before THAT is a path character, so `skills/plan`,
190
+ * `./plan`, `https://a/plan`, `../plan` and `~/plan` are path components while `` `/plan` `` and
191
+ * `(/plan)` are grok slashes. Its own fixture (path.md) and mutation row (N5b — add a backtick
192
+ * here and grok-slash.md goes PATH).
193
+ *
194
+ * `~` is in the class because a home-relative path is an ordinary doc idiom and excluding it was
195
+ * not merely a false positive: `fixDoc` rewrote `~/plan` to `~soleur:plan`, destroying the path
196
+ * AND converging on a state the classifier calls NONCANONICAL forever (the `~` is not a boundary,
197
+ * so the result is R1). `fixDoc`'s post-condition below is the general guard for that class.
198
+ */
199
+ export const PATH_PREV = /[A-Za-z0-9_./~]/;
200
+
201
+ /**
202
+ * The other half of the path class: a slash after a delimiter that CLOSES a span is a path
203
+ * separator, not a sigil. A glob segment, a command substitution's closing paren, or a closing
204
+ * backtick or quote all put a real path separator next to a component name — while the same
205
+ * name inside an OPEN delimiter (a backticked, parenthesised or quoted slash form) is the grok
206
+ * sigil. The two are lexically identical apart from what precedes the delimiter, so parity over
207
+ * the line prefix is the only available discriminator: an odd count means the delimiter closes.
208
+ *
209
+ * This lives with the path class rather than inside `fixDoc` so the classifier and the fixer
210
+ * share one predicate — a site that is a PATH is not reported, so there is nothing for the fixer
211
+ * to rewrite, and the two cannot disagree. Measured: without it, the fixer rewrote a working
212
+ * `cp` glob in the rclone install one-liner into a dead one, and the result classified CANONICAL
213
+ * — so the census called that doc clean forever and the gate could never find it again (#8299).
214
+ *
215
+ * (This comment names no glob literally: a star-slash inside a block comment closes it.)
216
+ */
217
+ export function closesSpan(prev2: string, linePrefix: string): boolean {
218
+ if (prev2 === "*" || prev2 === ")" || prev2 === "]" || prev2 === "}") return true;
219
+ if (prev2 !== "`" && prev2 !== '"' && prev2 !== "'") return false;
220
+ return (linePrefix.split(prev2).length - 1) % 2 === 1;
221
+ }
222
+
223
+ /**
224
+ * The token class: a maximal run of `[A-Za-z0-9_:-]`. `:` so `soleur:product:cpo` is one token;
225
+ * `-` so `deepen-plan` and `re-plan` are whole tokens that name nothing rather than a bare `plan`
226
+ * (N5c). `_` for shell identifiers.
227
+ */
228
+ export const TOKEN = /[A-Za-z0-9_:-]+/g;
229
+
230
+ /**
231
+ * Trailing glue stripped for index lookup only: `/ship:` and `/work:` glued the colon and escaped
232
+ * the index in v2 (Kieran #1). The RAW token is kept for R1/R4 so `/soleur:<metavar>` still trips
233
+ * the namespace sentinel, and for the printed site.
234
+ */
235
+ export const TRAILING_GLUE = /[:_-]+$/;
236
+
237
+ /** The one region kind, honoured only where regionPolicy is `command`. */
238
+ export const HONOURED_REGION = "harness-forms";
239
+
240
+ /**
241
+ * Marker grammar (arch F4, spec-flow #7a, Kieran #9). A marker is a whole line that, after
242
+ * trimming spaces and tabs ONLY, equals the strict form byte-for-byte. A line the loose form
243
+ * matches but the strict form does not is RED "malformed marker" — case, CRLF (the `\r`
244
+ * survives the trim on purpose), inline text. Markers with any OTHER name are transparent:
245
+ * ordinary content lines that never open or close anything.
246
+ */
247
+ export const MARKER_STRICT = /^<!-- harness-forms:(start|end) -->$/;
248
+ export const MARKER_LOOSE = /<!--\s*harness-forms\s*:\s*(start|end)\s*-->/i;
249
+
250
+ export interface Index {
251
+ /** Skill directory names ∪ command basenames (`plan`, `go`, …). */
252
+ readonly skillNames: ReadonlySet<string>;
253
+ /** `soleur:<skill|command>` ∪ registry agent ids — 165 today. */
254
+ readonly canonicalIds: ReadonlySet<string>;
255
+ /** Registry agent ids, in discovery order. */
256
+ readonly agentIds: readonly string[];
257
+ /** Last `:` segment of each agent id → id. Leaves are unique (asserted by the tree test). */
258
+ readonly agentLeaves: ReadonlyMap<string, string>;
259
+ /** `agentIdToGrokSubagentType(id)` → id. */
260
+ readonly grokStems: ReadonlyMap<string, string>;
261
+ }
262
+
263
+ export interface Doc {
264
+ readonly path: string;
265
+ readonly text: string;
266
+ readonly regionPolicy: RegionPolicy;
267
+ }
268
+
269
+ export interface Site {
270
+ readonly path: string;
271
+ readonly line: number;
272
+ readonly col: number;
273
+ /** The token as matched, trailing glue included. */
274
+ readonly raw: string;
275
+ /** `raw` with trailing glue stripped — the index key. */
276
+ readonly token: string;
277
+ /** The character before the token; `"\n"` at line start. */
278
+ readonly before: string;
279
+ readonly verdict: Verdict;
280
+ readonly shape?: Shape;
281
+ readonly attribution?: Attribution;
282
+ /** The canonical id to write instead. */
283
+ readonly fix?: string;
284
+ /** `path:line: <site> — <attribution>; write <fix>[ — tail]`; empty for permitted verdicts. */
285
+ readonly message: string;
286
+ }
287
+
288
+ export interface DocResult {
289
+ readonly path: string;
290
+ readonly regionPolicy: RegionPolicy;
291
+ readonly sites: readonly Site[];
292
+ /** Marker-grammar errors; any entry is RED for the doc. */
293
+ readonly errors: readonly string[];
294
+ readonly counts: Readonly<Record<Verdict, number>>;
295
+ }
296
+
297
+ export interface CensusResult {
298
+ readonly docs: readonly DocResult[];
299
+ readonly docsExamined: number;
300
+ readonly docsWithNoncanonical: number;
301
+ readonly totals: Readonly<Record<Verdict, number>>;
302
+ readonly noncanonical: readonly Site[];
303
+ readonly unknownNs: readonly Site[];
304
+ readonly errors: readonly string[];
305
+ readonly attribution: Readonly<Record<Attribution, number>>;
306
+ }
307
+
308
+ const VERDICTS: readonly Verdict[] = ["CANONICAL", "BARE", "PATH", "NONCANONICAL", "UNKNOWN-NS", "EXEMPT", "SELF-NAME"];
309
+ const ATTRIBUTIONS: readonly Attribution[] = [
310
+ "grok",
311
+ "claude-devin",
312
+ "codex",
313
+ "claude-agent",
314
+ "claude-leaf",
315
+ "grok-stem",
316
+ "bare-leaf",
317
+ "unrecognised",
318
+ ];
319
+
320
+ function zeroCounts<K extends string>(keys: readonly K[]): Record<K, number> {
321
+ return Object.fromEntries(keys.map((k) => [k, 0])) as Record<K, number>;
322
+ }
323
+
324
+ /** Repo root derived from the module's own location, so `bun test` from `plugins/soleur/` resolves (spec-flow #7d). */
325
+ export const REPO_ROOT = resolve(PLUGIN_ROOT, "../..");
326
+
327
+ /** `git ls-files --full-name <pathspecs>` from REPO_ROOT — the pathspecs are repo-rooted, never cwd-relative. */
328
+ function gitLsFiles(pathspecs: readonly string[]): string[] {
329
+ const out = execFileSync("git", ["ls-files", "--full-name", "--", ...pathspecs], {
330
+ cwd: REPO_ROOT,
331
+ encoding: "utf-8",
332
+ });
333
+ return out.split("\n").filter((l) => l.length > 0);
334
+ }
335
+
336
+ /** Read the index from the tree: never a listed set. */
337
+ export function readIndex(): Index {
338
+ const skills = gitLsFiles([INDEX_GLOBS.skills]).map((p) => {
339
+ const parts = p.split("/");
340
+ return parts[parts.length - 2];
341
+ });
342
+ const commands = gitLsFiles([INDEX_GLOBS.commands]).map((p) => {
343
+ const parts = p.split("/");
344
+ return parts[parts.length - 1].replace(/\.md$/, "");
345
+ });
346
+ const agentIds = discoverAgentPaths().map(pathToAgentId);
347
+ const skillNames = new Set([...skills, ...commands]);
348
+ const canonicalIds = new Set([...[...skillNames].map((n) => `soleur:${n}`), ...agentIds]);
349
+ const agentLeaves = new Map<string, string>();
350
+ const grokStems = new Map<string, string>();
351
+ for (const id of agentIds) {
352
+ const leaf = id.split(":").pop() as string;
353
+ agentLeaves.set(leaf, id);
354
+ grokStems.set(agentIdToGrokSubagentType(id), id);
355
+ }
356
+ return { skillNames, canonicalIds, agentIds, agentLeaves, grokStems };
357
+ }
358
+
359
+ /** Turn a `:(glob)` pathspec into a regex over the full repo-relative path. */
360
+ function globToRegex(pathspec: string): RegExp {
361
+ const body = pathspec.replace(/^:\(glob\)/, "");
362
+ // `**/` is recognised BEFORE the single-`*` pass (#8317) — taken in the other order the
363
+ // two stars are rewritten independently and `**/` degrades to `[^/]+[^/]+/`, which
364
+ // cannot cross a `/`, so a nested references path resolves to no policy while
365
+ // `git ls-files` still enumerates it and `readPopulation`'s `?? g.regionPolicy`
366
+ // fallback hides the difference in production.
367
+ //
368
+ // It is recognised via a SENTINEL rather than substituted in place, because its
369
+ // replacement `(?:[^/]+/)*` itself ends in a `*`: substituting directly leaves that
370
+ // star in the string for the single-`*` pass to rewrite into `(?:[^/]+/)[^/]+`, which
371
+ // matches EXACTLY ONE intermediate directory. Measured: 102 of 115 references docs
372
+ // resolved to no policy under the in-place form — the 13 survivors being the ones at
373
+ // the one depth it happened to admit, which is precisely the partial loss that reads
374
+ // as a working regex.
375
+ const DOUBLESTAR = "\u0000";
376
+ const escaped = body
377
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
378
+ .replace(/\*\*\//g, DOUBLESTAR)
379
+ .replace(/\*/g, "[^/]+")
380
+ .replace(new RegExp(DOUBLESTAR, "g"), "(?:[^/]+/)*");
381
+ return new RegExp(`^${escaped}$`);
382
+ }
383
+
384
+ /** Which policy a repo-relative path carries, derived from the matching population glob. */
385
+ export function regionPolicyForPath(
386
+ path: string,
387
+ globs: readonly PopulationGlob[] = POPULATION_GLOBS,
388
+ ): RegionPolicy | undefined {
389
+ for (const g of globs) {
390
+ if (!globToRegex(g.pathspec).test(path)) continue;
391
+ // A `SKILL.md` NESTED under references/ is not a member — `skills/*/SKILL.md` is the
392
+ // entry file, and a reference doc that happens to be named SKILL.md is a sample, not
393
+ // a second entry point. The carve-out is scoped to the REFERENCES glob deliberately:
394
+ // applied globally it would return `undefined` for all 99 real skill entry files plus
395
+ // the 6 Codex/Devin shims, and production would NOT notice, because `readPopulation`
396
+ // falls back to `?? g.regionPolicy` and would restore the right answer. Only the
397
+ // fixtures would red — which invites "fix the fixture" as the obvious repair.
398
+ if (g.pathspec === REFERENCES_PATHSPEC && path.endsWith("/SKILL.md")) return undefined;
399
+ return g.regionPolicy;
400
+ }
401
+ return undefined;
402
+ }
403
+
404
+ /** The only I/O in the module besides readIndex: enumerate the population and read each doc. */
405
+ export function readPopulation(globs: readonly PopulationGlob[] = POPULATION_GLOBS): Doc[] {
406
+ const docs: Doc[] = [];
407
+ const seen = new Set<string>();
408
+ for (const g of globs) {
409
+ for (const path of gitLsFiles([g.pathspec])) {
410
+ if (seen.has(path) || EXCLUDED_BY_PATH.has(path)) continue;
411
+ seen.add(path);
412
+ // Resolve the policy through the same exported function the fixtures use, rather than
413
+ // reading `g.regionPolicy` directly: otherwise the fixture suite exercises a parallel
414
+ // implementation and production never runs the one under test.
415
+ const regionPolicy = regionPolicyForPath(path, globs) ?? g.regionPolicy;
416
+ docs.push({ path, text: readFileSync(resolve(REPO_ROOT, path), "utf-8"), regionPolicy });
417
+ }
418
+ }
419
+ return docs;
420
+ }
421
+
422
+ interface Classified {
423
+ verdict: Verdict;
424
+ shape?: Shape;
425
+ fix?: string;
426
+ }
427
+
428
+ /**
429
+ * Classify one token. `raw` as matched, `before` the preceding character (`"\n"` at line start),
430
+ * `prev2` the character before that (`""` when absent).
431
+ */
432
+ function classifyToken(
433
+ raw: string,
434
+ before: string,
435
+ prev2: string,
436
+ index: Index,
437
+ linePrefix = "",
438
+ ): Classified | undefined {
439
+ const t = raw.replace(TRAILING_GLUE, "");
440
+ const atb = before === "\n" || BOUNDARY.has(before);
441
+ const pathctx = before === "/" && (PATH_PREV.test(prev2) || closesSpan(prev2, linePrefix));
442
+
443
+ // R1–R3: the namespace sentinel. `raw`, not `t`, so `/soleur:<metavar>` (raw `soleur:`) trips R1.
444
+ if (raw.startsWith("soleur:")) {
445
+ if (!atb) {
446
+ // `raw` of a bare `soleur:` strips to the truthy "soleur", so a `|| raw` fallback never
447
+ // fires and the message told the author to write a bare word naming nothing. Keep the
448
+ // namespace punctuation whenever stripping would leave no name after the colon.
449
+ const stripped = raw.replace(TRAILING_GLUE, "");
450
+ return { verdict: "NONCANONICAL", shape: "ns-sigil", fix: stripped.includes(":") ? stripped : raw };
451
+ }
452
+ return index.canonicalIds.has(t) ? { verdict: "CANONICAL" } : { verdict: "UNKNOWN-NS" };
453
+ }
454
+ // R4: `@agent-soleur:product:cpo` — the namespace appears inside the token.
455
+ if (raw.includes("soleur:")) {
456
+ const at = raw.indexOf("soleur:");
457
+ return { verdict: "NONCANONICAL", shape: "agent-mention", fix: raw.slice(at).replace(TRAILING_GLUE, "") };
458
+ }
459
+ // R5 / R7: a grok hyphen stem, derived by the adapter's own function.
460
+ const stemId = index.grokStems.get(t);
461
+ if (stemId !== undefined) {
462
+ return pathctx ? { verdict: "PATH" } : { verdict: "NONCANONICAL", shape: "grok-stem", fix: stemId };
463
+ }
464
+ // R6 / R7 / R8: a skill or command name.
465
+ if (index.skillNames.has(t)) {
466
+ if (atb) return { verdict: "BARE" };
467
+ if (pathctx) return { verdict: "PATH" };
468
+ return { verdict: "NONCANONICAL", shape: "sigil-skill", fix: `soleur:${t}` };
469
+ }
470
+ // R6b / R7: an agent leaf. Never a prose word; a bare leaf maps to no Grok stub.
471
+ const leafId = index.agentLeaves.get(t);
472
+ if (leafId !== undefined) {
473
+ if (pathctx) return { verdict: "PATH" };
474
+ return { verdict: "NONCANONICAL", shape: atb ? "bare-agent-leaf" : "sigil-agent-leaf", fix: leafId };
475
+ }
476
+ // R9: a sigil absorbed into a hyphenated token (`@agent-cpo`, `$x-plan`). Path components
477
+ // (`…-feature-plan.md`) are excluded by the pathctx clause; prose compounds after whitespace
478
+ // (`deepen-plan`, `re-plan`) never reach here because they are atb.
479
+ if (!atb && !pathctx) {
480
+ const parts = t.split("-");
481
+ for (let i = 1; i < parts.length; i++) {
482
+ const suffix = parts.slice(i).join("-");
483
+ if (index.skillNames.has(suffix)) return { verdict: "NONCANONICAL", shape: "hyphen-absorbed", fix: `soleur:${suffix}` };
484
+ const leaf = index.agentLeaves.get(suffix);
485
+ if (leaf !== undefined) return { verdict: "NONCANONICAL", shape: "hyphen-absorbed", fix: leaf };
486
+ const stem = index.grokStems.get(suffix);
487
+ if (stem !== undefined) return { verdict: "NONCANONICAL", shape: "hyphen-absorbed", fix: stem };
488
+ }
489
+ }
490
+ return undefined;
491
+ }
492
+
493
+ /** Local decoration: which harness's form this is. Never a verdict input. */
494
+ function attribute(shape: Shape, before: string): Attribution {
495
+ switch (shape) {
496
+ case "ns-sigil":
497
+ return before === "/" ? "claude-devin" : before === "$" ? "codex" : "unrecognised";
498
+ case "sigil-skill":
499
+ return before === "/" ? "grok" : "unrecognised";
500
+ case "agent-mention":
501
+ return before === "@" ? "claude-agent" : "unrecognised";
502
+ case "hyphen-absorbed":
503
+ return before === "@" ? "claude-leaf" : "unrecognised";
504
+ case "grok-stem":
505
+ return "grok-stem";
506
+ case "bare-agent-leaf":
507
+ return "bare-leaf";
508
+ case "sigil-agent-leaf":
509
+ return "unrecognised";
510
+ }
511
+ }
512
+
513
+ const ATTRIBUTION_LABEL: Readonly<Record<Attribution, string>> = {
514
+ grok: "grok",
515
+ "claude-devin": "claude/devin",
516
+ codex: "codex",
517
+ "claude-agent": "claude",
518
+ "claude-leaf": "claude",
519
+ "grok-stem": "grok",
520
+ "bare-leaf": "bare agent leaf, dead on grok",
521
+ unrecognised: "unrecognised sigil",
522
+ };
523
+
524
+ const BRACE_TAIL = " — or, if this is not a component reference, brace/rename the identifier";
525
+
526
+ /**
527
+ * The agent self-name carve-out (#8317 agent half; ADR-226 amendment 2026-09-24). An agent's own
528
+ * frontmatter `name:` is its bare leaf by necessity: `discoverAgentEntries` reads it into the
529
+ * committed manifest and Claude's loader keys on it, so it cannot become the registry id.
530
+ *
531
+ * The candidate is the FIRST line of the doc matching `^name:` — first, not first-exact, so a later
532
+ * exact line cannot certify a doc whose real `name:` is quoted (the registry's YAML reader rejects
533
+ * duplicate keys outright). It is accepted only when it sits inside a closed leading frontmatter
534
+ * (line 1 is exactly `---`; it ends at the first later line starting with `---`) and reads
535
+ * byte-exactly `name: <stem>`,
536
+ * where the stem comes from the PATH, so a doc cannot certify itself. Accepted: the one own-stem
537
+ * token on it is SELF-NAME. Rejected: every non-canonical site on it carries a message that says
538
+ * how to fix the line, because any `write <id>` hint there would rewrite the manifest's key. Every
539
+ * other token, in frontmatter or body, keeps its ordinary verdict.
540
+ */
541
+ function findSelfNameLine(lines: readonly string[], path: string): { line: number; accepted: boolean; stem: string } | undefined {
542
+ const candidate = lines.findIndex((l) => l.startsWith("name:"));
543
+ if (candidate === -1) return undefined;
544
+ const stem = basename(path, ".md");
545
+ // The frontmatter ends where the registry's own loader ends it: at the first later line that
546
+ // STARTS with `---` (agent-registry.ts parseFrontmatter). A stricter close would certify a
547
+ // name: line every loader reads as body text.
548
+ const close = lines[0] === "---" ? lines.findIndex((l, i) => i > 0 && l.startsWith("---")) : -1;
549
+ const inFrontmatter = close !== -1 && candidate > 0 && candidate < close;
550
+ return { line: candidate + 1, accepted: inFrontmatter && lines[candidate] === `name: ${stem}`, stem };
551
+ }
552
+
553
+ /** Classify one doc. Pure: `text` in, sites and marker errors out. */
554
+ export function classifyDoc(text: string, index: Index, regionPolicy: RegionPolicy, path = "<doc>"): DocResult {
555
+ const sites: Site[] = [];
556
+ const errors: string[] = [];
557
+ const counts = zeroCounts(VERDICTS);
558
+ const lines = text.split("\n");
559
+ let regionOpenAt: number | undefined;
560
+ const selfName = regionPolicy === "agent" ? findSelfNameLine(lines, path) : undefined;
561
+
562
+ lines.forEach((line, i) => {
563
+ const lineNo = i + 1;
564
+ const trimmed = line.replace(/^[ \t]+|[ \t]+$/g, "");
565
+ const strict = MARKER_STRICT.exec(trimmed);
566
+ if (strict) {
567
+ if (strict[1] === "start") {
568
+ if (regionOpenAt !== undefined) errors.push(`${path}:${lineNo}: ${HONOURED_REGION}:start while a region is open`);
569
+ else regionOpenAt = lineNo;
570
+ } else if (regionOpenAt === undefined) {
571
+ errors.push(`${path}:${lineNo}: stray ${HONOURED_REGION}:end with no open region`);
572
+ } else {
573
+ regionOpenAt = undefined;
574
+ }
575
+ return;
576
+ }
577
+ if (MARKER_LOOSE.test(line)) {
578
+ errors.push(`${path}:${lineNo}: malformed marker ${JSON.stringify(line)} — expected exactly <!-- ${HONOURED_REGION}:start --> or <!-- ${HONOURED_REGION}:end --> on its own line`);
579
+ return;
580
+ }
581
+ const exempt = regionOpenAt !== undefined && regionPolicy === "command";
582
+
583
+ for (const m of line.matchAll(TOKEN)) {
584
+ const raw = m[0];
585
+ const s = m.index ?? 0;
586
+ const before = s > 0 ? line[s - 1] : "\n";
587
+ const prev2 = s > 1 ? line[s - 2] : "";
588
+ const c = classifyToken(raw, before, prev2, index, s > 1 ? line.slice(0, s - 2) : "");
589
+ if (c === undefined) continue;
590
+ const token = raw.replace(TRAILING_GLUE, "");
591
+ let verdict = c.verdict;
592
+ if (exempt && verdict === "NONCANONICAL") verdict = "EXEMPT";
593
+ const onSelfNameLine = selfName !== undefined && selfName.line === lineNo;
594
+ if (onSelfNameLine && selfName.accepted && verdict === "NONCANONICAL" && token === selfName.stem) verdict = "SELF-NAME";
595
+ counts[verdict] += 1;
596
+ if (verdict !== "NONCANONICAL") {
597
+ sites.push({ path, line: lineNo, col: s + 1, raw, token, before, verdict, message: "" });
598
+ continue;
599
+ }
600
+ const shape = c.shape as Shape;
601
+ const attribution = attribute(shape, before);
602
+ // Printed as `before + raw` when the sigil is a character, so found and fix never read
603
+ // identically (spec-flow #6); bare leaves print the token alone.
604
+ const atb = before === "\n" || BOUNDARY.has(before);
605
+ const site = atb ? raw : `${before}${raw}`;
606
+ const tail = attribution === "unrecognised" || attribution === "bare-leaf" ? BRACE_TAIL : "";
607
+ const rejectedSelfName = onSelfNameLine && !selfName.accepted;
608
+ const message = rejectedSelfName
609
+ ? `${path}:${lineNo}: ${site} — agent self-name must be exactly "name: ${selfName.stem}" ` +
610
+ `(unquoted, equal to the filename, inside the frontmatter: line 1 exactly ---, ending at the next line that starts with ---); do not write a registry id here`
611
+ : `${path}:${lineNo}: ${site} — ${ATTRIBUTION_LABEL[attribution]}; write ${c.fix}${tail}`;
612
+ sites.push({ path, line: lineNo, col: s + 1, raw, token, before, verdict, shape, attribution, fix: c.fix, message });
613
+ }
614
+ });
615
+
616
+ if (regionOpenAt !== undefined) errors.push(`${path}:${regionOpenAt}: ${HONOURED_REGION}:start never closed`);
617
+ return { path, regionPolicy, sites, errors, counts };
618
+ }
619
+
620
+ /** Fold per-doc results. Throws on an empty population (N4) — a clean census over nothing is not clean. */
621
+ export function census(docs: readonly Doc[], index: Index): CensusResult {
622
+ if (docs.length === 0) throw new Error("harness-parity: 0 docs examined");
623
+ const results = docs.map((d) => classifyDoc(d.text, index, d.regionPolicy, d.path));
624
+ const totals = zeroCounts(VERDICTS);
625
+ const attribution = zeroCounts(ATTRIBUTIONS);
626
+ const noncanonical: Site[] = [];
627
+ const unknownNs: Site[] = [];
628
+ const errors: string[] = [];
629
+ let docsWithNoncanonical = 0;
630
+ for (const r of results) {
631
+ for (const v of VERDICTS) totals[v] += r.counts[v];
632
+ errors.push(...r.errors);
633
+ let any = false;
634
+ for (const s of r.sites) {
635
+ if (s.verdict === "NONCANONICAL") {
636
+ any = true;
637
+ noncanonical.push(s);
638
+ attribution[s.attribution as Attribution] += 1;
639
+ } else if (s.verdict === "UNKNOWN-NS") {
640
+ unknownNs.push(s);
641
+ }
642
+ }
643
+ if (any) docsWithNoncanonical += 1;
644
+ }
645
+ return { docs: results, docsExamined: results.length, docsWithNoncanonical, totals, noncanonical, unknownNs, errors, attribution };
646
+ }
647
+
648
+ /**
649
+ * The mechanical inverse for the sigil-plus-known-name shapes: `/soleur:x` and `$soleur:x` (R1),
650
+ * `@agent-soleur:x` (R4), and grok `/x` for a known skill (R8). Never a bare leaf or a hyphen
651
+ * mention (the sentence around them changes — hand edits), never an honoured region, never `$x` (a shell
652
+ * variable that happens to be a skill name is braced by the author, not rewritten). Idempotent:
653
+ * every rewrite yields a canonical token that classifies R2 on the next pass.
654
+ */
655
+ export function fixDoc(text: string, index: Index, regionPolicy: RegionPolicy = "skill"): string {
656
+ let inRegion = false;
657
+ return text
658
+ .split("\n")
659
+ .map((line) => {
660
+ // Mirror the classifier's region state so a `command` doc's honoured region is left alone;
661
+ // malformed markers are content here (the classifier reports them, the fixer never edits them).
662
+ const strict = MARKER_STRICT.exec(line.replace(/^[ \t]+|[ \t]+$/g, ""));
663
+ if (strict) {
664
+ inRegion = strict[1] === "start";
665
+ return line;
666
+ }
667
+ if (inRegion && regionPolicy === "command") return line;
668
+ let out = "";
669
+ let last = 0;
670
+ for (const m of line.matchAll(TOKEN)) {
671
+ const raw = m[0];
672
+ const s = m.index ?? 0;
673
+ const before = s > 0 ? line[s - 1] : "\n";
674
+ const prev2 = s > 1 ? line[s - 2] : "";
675
+ const c = classifyToken(raw, before, prev2, index, s > 1 ? line.slice(0, s - 2) : "");
676
+ if (c === undefined || c.verdict !== "NONCANONICAL") continue;
677
+ let replacement: string | undefined;
678
+ if (c.shape === "ns-sigil" && (before === "/" || before === "$")) replacement = raw;
679
+ else if (c.shape === "agent-mention" && before === "@" && raw.startsWith("agent-soleur:")) replacement = raw.slice("agent-".length);
680
+ else if (c.shape === "sigil-skill" && before === "/") replacement = `soleur:${raw}`;
681
+ if (replacement === undefined) continue;
682
+ // The consumed byte is guaranteed to be the sigil: a `/` that is a path separator
683
+ // classifies PATH via `closesSpan`/`PATH_PREV` above, so it is never a NONCANONICAL
684
+ // site and never reaches here. One predicate, in the classifier, shared by both.
685
+ //
686
+ // Post-condition: never emit a rewrite that does not itself classify clean — the
687
+ // residual guard for shapes where the consumed byte was not a boundary character
688
+ // (`~/plan`, `!/plan`), whose rewrite stays visibly non-canonical.
689
+ // The splice consumes the character at `s - 1`, so after it the token's predecessor is
690
+ // `prev2` and ITS predecessor is `line[s - 3]` — classify against those, not against the
691
+ // sigil that is about to disappear.
692
+ const newBefore = s > 1 ? prev2 : "\n";
693
+ const newPrev2 = s > 2 ? line[s - 3] : "";
694
+ const after = classifyToken(replacement, newBefore, newPrev2, index, "");
695
+ if (after !== undefined && after.verdict !== "CANONICAL") continue;
696
+ out += line.slice(last, s - 1) + replacement;
697
+ last = s + raw.length;
698
+ }
699
+ return out + line.slice(last);
700
+ })
701
+ .join("\n");
702
+ }
703
+
704
+ /** Human report for the CLI and the tree test's failure output. */
705
+ export function formatReport(result: CensusResult): string {
706
+ const lines: string[] = [];
707
+ lines.push(
708
+ `harness-parity: ${result.docsExamined} docs examined, ${result.noncanonical.length} non-canonical sites in ${result.docsWithNoncanonical} docs, ` +
709
+ `${result.totals.CANONICAL} canonical, ${result.unknownNs.length} unknown-ns, ${result.totals.EXEMPT} exempt, ` +
710
+ `${result.totals["SELF-NAME"]} self-name, ${result.errors.length} marker errors`,
711
+ );
712
+ lines.push(
713
+ "attribution: " + ATTRIBUTIONS.map((a) => `${a} ${result.attribution[a]}`).join(" / "),
714
+ );
715
+ const perDoc = result.docs
716
+ .filter((d) => d.counts.NONCANONICAL > 0)
717
+ .sort((a, b) => b.counts.NONCANONICAL - a.counts.NONCANONICAL)
718
+ .map((d) => ` ${d.counts.NONCANONICAL}\t${d.path}`);
719
+ if (perDoc.length > 0) lines.push("docs:", ...perDoc);
720
+ if (result.unknownNs.length > 0) {
721
+ lines.push("unknown-ns:", ...result.unknownNs.map((s) => ` ${s.path}:${s.line}: ${s.raw}`));
722
+ }
723
+ if (result.errors.length > 0) lines.push("marker errors:", ...result.errors.map((e) => ` ${e}`));
724
+ if (result.noncanonical.length > 0) lines.push("sites:", ...result.noncanonical.map((s) => ` ${s.message}`));
725
+ return lines.join("\n");
726
+ }