@soleur/opencode 0.0.0-stage → 3.354.2

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 (832) 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 +660 -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 +29 -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 +361 -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 +171 -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 +818 -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 +411 -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 +509 -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 +115 -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 +165 -0
  247. package/scripts/sync-opencode-adapter.ts +498 -0
  248. package/scripts/sync-pr-behind.sh +647 -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 +67 -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 +143 -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 +120 -0
  493. package/skills/fix-issue/references/exclude-label-jq-snippet.md +97 -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 +670 -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 +331 -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 +141 -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 +1620 -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-evidence-comment.md +36 -0
  674. package/skills/review/references/review-todo-structure.md +165 -0
  675. package/skills/review/references/risk-tier-and-fix-rounds.md +97 -0
  676. package/skills/review/references/semgrep-custom-rules.yaml +174 -0
  677. package/skills/review/references/ultrathink-phases.md +98 -0
  678. package/skills/review/references/wfs.md +6 -0
  679. package/skills/review/scripts/emit-review-trailer.sh +499 -0
  680. package/skills/review/scripts/ensure-semgrep.sh +99 -0
  681. package/skills/review/scripts/fix-round-seats.sh +197 -0
  682. package/skills/review/test/emit-review-trailer.test.sh +387 -0
  683. package/skills/review/test/ensure-semgrep.test.sh +111 -0
  684. package/skills/review/test/fix-round-seats.test.sh +237 -0
  685. package/skills/review/workflows/README.md +147 -0
  686. package/skills/review/workflows/review.workflow.js +667 -0
  687. package/skills/schedule/SKILL.md +796 -0
  688. package/skills/seo-aeo/SKILL.md +139 -0
  689. package/skills/seo-aeo/scripts/validate-csp.sh +233 -0
  690. package/skills/seo-aeo/scripts/validate-seo.sh +262 -0
  691. package/skills/ship/SKILL.md +3023 -0
  692. package/skills/ship/references/adr-ordinal-collision.md +47 -0
  693. package/skills/ship/references/affected-gate-contract.md +115 -0
  694. package/skills/ship/references/ci-run-diagnosis.md +29 -0
  695. package/skills/ship/references/ci-workflow-authoring.md +68 -0
  696. package/skills/ship/references/cleanup-merged-archival.md +39 -0
  697. package/skills/ship/references/counsel-review-clo-attestation.md +38 -0
  698. package/skills/ship/references/cto-ruling-armed-push.md +5 -0
  699. package/skills/ship/references/decision-challenges-render.md +71 -0
  700. package/skills/ship/references/delegated-act-authoring-form.md +7 -0
  701. package/skills/ship/references/detached-battery-verdict.md +13 -0
  702. package/skills/ship/references/epic-close-sweep.md +62 -0
  703. package/skills/ship/references/feature-tweet-draft.md +42 -0
  704. package/skills/ship/references/followthrough-issue-body-template.md +47 -0
  705. package/skills/ship/references/followthrough-stub-template.sh +92 -0
  706. package/skills/ship/references/merge-queue-dequeue.md +39 -0
  707. package/skills/ship/references/operator-holds.md +179 -0
  708. package/skills/ship/references/pipeline-tally-render.md +43 -0
  709. package/skills/ship/references/pr-overlap.md +153 -0
  710. package/skills/ship/references/queue-mode.md +7 -0
  711. package/skills/ship/references/ready-run-wait.md +36 -0
  712. package/skills/ship/references/red-on-main-quarantine.md +110 -0
  713. package/skills/ship/references/register-update-pr-pattern.md +88 -0
  714. package/skills/ship/references/settle-then-admin-merge.md +130 -0
  715. package/skills/ship/references/supabase-column-probe.md +13 -0
  716. package/skills/ship/scripts/auto-close-scan.sh +76 -0
  717. package/skills/ship/scripts/battery-owed.sh +403 -0
  718. package/skills/ship/scripts/hold-gate-eval.sh +410 -0
  719. package/skills/ship/scripts/net-issue-flow.sh +665 -0
  720. package/skills/ship/scripts/pr-overlap-preflight.sh +483 -0
  721. package/skills/ship/scripts/ship-pir-action-items-gate.sh +246 -0
  722. package/skills/ship/scripts/technical-ruling-gate.sh +444 -0
  723. package/skills/skill-creator/SKILL.md +299 -0
  724. package/skills/skill-creator/references/api-security.md +248 -0
  725. package/skills/skill-creator/references/authoring-levers.md +110 -0
  726. package/skills/skill-creator/references/be-clear-and-direct.md +587 -0
  727. package/skills/skill-creator/references/best-practices.md +431 -0
  728. package/skills/skill-creator/references/common-patterns.md +639 -0
  729. package/skills/skill-creator/references/core-principles.md +193 -0
  730. package/skills/skill-creator/references/executable-code.md +188 -0
  731. package/skills/skill-creator/references/iteration-and-testing.md +509 -0
  732. package/skills/skill-creator/references/official-spec.md +193 -0
  733. package/skills/skill-creator/references/recommended-structure.md +180 -0
  734. package/skills/skill-creator/references/skill-structure.md +396 -0
  735. package/skills/skill-creator/references/using-scripts.md +122 -0
  736. package/skills/skill-creator/references/using-templates.md +120 -0
  737. package/skills/skill-creator/references/workflows-and-validation.md +541 -0
  738. package/skills/skill-creator/scripts/init_skill.py +322 -0
  739. package/skills/skill-creator/scripts/package_skill.py +110 -0
  740. package/skills/skill-creator/scripts/quick_validate.py +64 -0
  741. package/skills/skill-creator/templates/router-skill.md +73 -0
  742. package/skills/skill-creator/templates/simple-skill.md +34 -0
  743. package/skills/skill-creator/workflows/add-reference.md +96 -0
  744. package/skills/skill-creator/workflows/add-script.md +104 -0
  745. package/skills/skill-creator/workflows/add-template.md +83 -0
  746. package/skills/skill-creator/workflows/add-workflow.md +128 -0
  747. package/skills/skill-creator/workflows/audit-skill.md +159 -0
  748. package/skills/skill-creator/workflows/create-domain-expertise-skill.md +623 -0
  749. package/skills/skill-creator/workflows/create-new-skill.md +207 -0
  750. package/skills/skill-creator/workflows/get-guidance.md +134 -0
  751. package/skills/skill-creator/workflows/upgrade-to-router.md +170 -0
  752. package/skills/skill-creator/workflows/verify-skill.md +226 -0
  753. package/skills/skill-security-scan/SKILL.md +145 -0
  754. package/skills/skill-security-scan/references/disclaimer.md +21 -0
  755. package/skills/skill-security-scan/references/first-party-allowlist.yaml +38 -0
  756. package/skills/skill-security-scan/references/override-artifact-schema.json +68 -0
  757. package/skills/skill-security-scan/references/override-mechanism.md +171 -0
  758. package/skills/skill-security-scan/references/redirect-domains.yaml +27 -0
  759. package/skills/skill-security-scan/references/regex-patterns.md +136 -0
  760. package/skills/skill-security-scan/references/rules/code-exec.yaml +70 -0
  761. package/skills/skill-security-scan/references/rules/filesystem-boundary.yaml +29 -0
  762. package/skills/skill-security-scan/references/rules/manifest.yaml +44 -0
  763. package/skills/skill-security-scan/references/rules/supply-chain.yaml +36 -0
  764. package/skills/skill-security-scan/references/scan-meta-schema.json +63 -0
  765. package/skills/skill-security-scan/references/test-fixtures/clean-soleur-style.skill.md +23 -0
  766. package/skills/skill-security-scan/references/test-fixtures/clean-third-party.skill.md +26 -0
  767. package/skills/skill-security-scan/references/test-fixtures/malicious-codeexec.skill.md +23 -0
  768. package/skills/skill-security-scan/references/test-fixtures/malicious-curl-pipe-bash.skill.md +69 -0
  769. package/skills/skill-security-scan/references/test-fixtures/malicious-prompt-injection.skill.md +17 -0
  770. package/skills/skill-security-scan/references/test-fixtures/malicious-telemetry-beacon.skill.md +23 -0
  771. package/skills/skill-security-scan/references/typosquat-targets.yaml +65 -0
  772. package/skills/skill-security-scan/scripts/check-codeexec.sh +41 -0
  773. package/skills/skill-security-scan/scripts/check-filesystem-boundary.sh +19 -0
  774. package/skills/skill-security-scan/scripts/check-prompt-injection.sh +95 -0
  775. package/skills/skill-security-scan/scripts/check-supply-chain.sh +219 -0
  776. package/skills/skill-security-scan/scripts/check-telemetry-surface.sh +126 -0
  777. package/skills/skill-security-scan/scripts/lib.sh +197 -0
  778. package/skills/skill-security-scan/scripts/parse-override.sh +231 -0
  779. package/skills/skill-security-scan/scripts/run-scan.sh +342 -0
  780. package/skills/skill-security-scan/scripts/run-self-test.sh +95 -0
  781. package/skills/skill-security-scan/test/argv-ceiling.test.sh +180 -0
  782. package/skills/skill-security-scan/test/meta-dir-reap.test.sh +411 -0
  783. package/skills/skill-security-scan/test/parse-override.test.sh +607 -0
  784. package/skills/skill-security-scan/test/redact-pii-ghs-shape.test.sh +251 -0
  785. package/skills/social-distribute/SKILL.md +485 -0
  786. package/skills/spec-templates/SKILL.md +274 -0
  787. package/skills/sync/SKILL.md +49 -0
  788. package/skills/test-browser/SKILL.md +464 -0
  789. package/skills/test-browser/references/feature-map.md +161 -0
  790. package/skills/test-fix-loop/SKILL.md +159 -0
  791. package/skills/test-fix-loop/references/determinism-checklist.md +154 -0
  792. package/skills/triage/SKILL.md +402 -0
  793. package/skills/trigger-cron/SKILL.md +136 -0
  794. package/skills/trigger-cron/scripts/trigger.sh +198 -0
  795. package/skills/user-set-role/SKILL.md +147 -0
  796. package/skills/user-set-role/scripts/set-role.sh +218 -0
  797. package/skills/user-story-writer/SKILL.md +77 -0
  798. package/skills/ux-audit/SKILL.md +251 -0
  799. package/skills/ux-audit/references/finding.schema.json +62 -0
  800. package/skills/ux-audit/references/route-list.yaml +95 -0
  801. package/skills/ux-audit/scripts/bot-fixture.ts +270 -0
  802. package/skills/ux-audit/scripts/bot-signin.ts +135 -0
  803. package/skills/ux-audit/scripts/dedup-hash.ts +46 -0
  804. package/skills/verification-scaffold/SKILL.md +73 -0
  805. package/skills/verification-scaffold/references/gitleaks.toml.template +13 -0
  806. package/skills/verification-scaffold/references/guardrail-audit.md +156 -0
  807. package/skills/verification-scaffold/references/guards/block-stash-hook.sh.template +72 -0
  808. package/skills/verification-scaffold/references/guards/commit-on-main-hook.sh.template +82 -0
  809. package/skills/verification-scaffold/references/guards/orphan-test-check.sh.template +102 -0
  810. package/skills/verification-scaffold/references/guards/precommit-guard.sh.template +115 -0
  811. package/skills/verification-scaffold/references/guards/secret-scan-hook.sh.template +176 -0
  812. package/skills/verification-scaffold/references/guards/verification-gates.yml.template +49 -0
  813. package/skills/verification-scaffold/references/guards/wrapper-boundary.sh.template +47 -0
  814. package/skills/verification-scaffold/references/guards.tsv +21 -0
  815. package/skills/verification-scaffold/references/verification-readme.template +55 -0
  816. package/skills/verification-scaffold/scripts/verification-scaffold.sh +785 -0
  817. package/skills/verification-scaffold/test/bite-proof.test.sh +159 -0
  818. package/skills/verification-scaffold/test/generator.test.sh +319 -0
  819. package/skills/verification-scaffold/test/manifest.test.sh +144 -0
  820. package/skills/work/SKILL.md +1480 -0
  821. package/skills/work/references/end-of-work-resume.md +7 -0
  822. package/skills/work/references/hook-test-main-cwd.md +24 -0
  823. package/skills/work/references/key-principles.md +83 -0
  824. package/skills/work/references/property-testing.md +122 -0
  825. package/skills/work/references/sql-security-definer-rpc-scaffold.sql +78 -0
  826. package/skills/work/references/work-agent-teams.md +97 -0
  827. package/skills/work/references/work-lifecycle-parallel.md +147 -0
  828. package/skills/work/references/work-lockfile-bumps.md +97 -0
  829. package/skills/work/references/work-scratch-sandboxes.md +212 -0
  830. package/skills/work/references/work-subagent-fanout.md +61 -0
  831. package/skills/work/references/work-test-harness-sharp-edges.md +73 -0
  832. package/skills/xcode-test/SKILL.md +360 -0
@@ -0,0 +1,528 @@
1
+ #!/usr/bin/env bash
2
+ # plugin-version.sh — resolve the installed and latest Soleur plugin version.
3
+ #
4
+ # Shared by the SessionStart banner (hooks/version-banner.sh), /soleur:version and
5
+ # /soleur:help, so all three agree. Output is key=value lines, one shape for callers
6
+ # and tests. Never reports a version it did not read; always exits 0 (fail open).
7
+ #
8
+ # Modes (one required):
9
+ # --installed source=manifest|commit|checkout|unknown|none, version=, commit=, marketplace=
10
+ # --cached-latest --installed keys + cache_dir= + the cached latest release; NO network
11
+ # --latest [--refresh] latest release tag; uses a fresh cache unless --refresh
12
+ # --compare [--refresh] --installed + --latest + relation=older|current|unknown
13
+ # --cmp A B semver order of two X.Y.Z strings: older|same|newer
14
+ #
15
+ # Installed identity precedence: manifest top-level `.version` (X.Y.Z) -> basename of the
16
+ # plugin root when it is a commit-hash cache dir -> the HEAD commit of the local git checkout
17
+ # the plugin is loaded from in place (source=checkout) -> unknown. A manifest with no `version`
18
+ # key is the normal published shape (ADR-182 D1), so the commit hash is the usual answer.
19
+ #
20
+ # source=checkout: a local-Folder marketplace (or --plugin-dir) loads the plugin from
21
+ # <top>/plugins/soleur, which has neither a manifest version nor a commit-hash directory name.
22
+ # The identity is then <top>'s HEAD commit, read with PLAIN FILE READS ONLY (HEAD -> a loose
23
+ # ref or packed-refs; a detached HEAD holds the sha). Git is never run against that repository:
24
+ # its .git/config, hooks and attributes are untrusted project input. The lookup is anchored on
25
+ # the layout (the root ends in /plugins/soleur, <top>/.git exists, plugin.json exists), never a
26
+ # walk up to "the first .git", which would adopt a dotfiles repository above the project. Every
27
+ # file is size-gated by stat BEFORE it is read, HEAD and loose refs must be exactly one line,
28
+ # only a 40-hex commit is accepted, and only its first 12 hex are ever printed.
29
+ # The plugin root is CLAUDE_PLUGIN_ROOT, or, when that is unset (the Bash tool does not
30
+ # always export it), the directory one level above this script's directory.
31
+ #
32
+ # Latest release: highest tag matching ^v[0-9]+.[0-9]+.[0-9]+$ from `git ls-remote`
33
+ # against a fixed URL. The repo also publishes web-v*, vinngest-v*, telegram-v* and
34
+ # zot-image-* tags; the strict regex excludes them and prereleases.
35
+ #
36
+ # Relation for a commit-hash or local-checkout install uses one unauthenticated GitHub compare API call
37
+ # (ls-remote maps tags to commits but cannot order an installed commit against them).
38
+ # `diverged` or any failure is `unknown`, never "current". A relation answered from a
39
+ # stale cache or after a failed lookup is reported as unknown, except `older`, which
40
+ # stays true (releases only move forward).
41
+ #
42
+ # Network isolation: the lookups run from a neutral directory with the user's and the
43
+ # project's git configuration disabled and https as the only allowed protocol, so a
44
+ # `url.<base>.insteadOf` rewrite, `http.extraHeader`, `core.sshCommand` or a GIT_CONFIG_*
45
+ # variable cannot redirect the request, add credentials or run a command. Proxy
46
+ # environment variables still apply.
47
+ #
48
+ # Cache: one file per install, ${XDG_CACHE_HOME:-~/.cache}/soleur/latest-release.<id>
49
+ # (a relative XDG_CACHE_HOME is ignored, per the XDG spec). Treated as fresh for 24 h;
50
+ # the file stays on disk until replaced or deleted. A local checkout moves with every commit,
51
+ # so all checkouts share the one file latest-release.checkout (it records which commit it
52
+ # answers; another commit's entry reads as cache=missing and is simply replaced).
53
+ #
54
+ # Env: SOLEUR_FETCH_TIMEOUT (seconds per request, default 8, must be >= 1).
55
+ set -uo pipefail
56
+
57
+ REPO_URL="https://github.com/jikig-ai/soleur.git"
58
+ COMPARE_API="https://api.github.com/repos/jikig-ai/soleur/compare"
59
+ TAG_RE='^v[0-9]+\.[0-9]+\.[0-9]+$'
60
+ SEMVER_RE='^[0-9]+\.[0-9]+\.[0-9]+$'
61
+ DIR_RE='^[0-9a-f]{12}(-[0-9a-f]{8})?$'
62
+ SHA40_RE='^[0-9a-f]{40}$'
63
+ SHA12_RE='^[0-9a-f]{12}$'
64
+ SMALL_MAX=512 # bytes: HEAD, loose refs, .git pointer files, commondir
65
+ PACKED_MAX=4194304 # bytes: packed-refs (this repository's own is ~0.5 MB)
66
+ MANIFEST_MAX=65536 # bytes: plugin.json (the real one is ~1 KB; the no-jq awk scanner is superlinear, 100 KB cost seconds on mawk)
67
+ TTL_S=86400
68
+ # A failed compare is cached as fresh for only this long (the fetched stamp is backdated by
69
+ # TTL_S - FAIL_FRESH_S), so one rate-limit response does not silence the notice for a day.
70
+ FAIL_FRESH_S=3600
71
+ FETCH_TIMEOUT="${SOLEUR_FETCH_TIMEOUT:-8}"
72
+ if [[ "$FETCH_TIMEOUT" =~ ^[0-9]+$ ]] && (( 10#$FETCH_TIMEOUT >= 1 )); then
73
+ FETCH_TIMEOUT=$((10#$FETCH_TIMEOUT))
74
+ else
75
+ FETCH_TIMEOUT=8
76
+ fi
77
+
78
+ TIMEOUT_CMD=""
79
+ if command -v timeout >/dev/null 2>&1; then TIMEOUT_CMD="timeout"
80
+ elif command -v gtimeout >/dev/null 2>&1; then TIMEOUT_CMD="gtimeout"; fi
81
+
82
+ # With timeout/gtimeout the command is bounded by it. Without them (stock macOS) a bash
83
+ # watchdog kills it after FETCH_TIMEOUT: http.lowSpeedLimit alone does not bound a stalled
84
+ # connect or TLS handshake. A killed command is reported as rc 124 (timeout).
85
+ run_bounded() {
86
+ if [[ -n "$TIMEOUT_CMD" ]]; then "$TIMEOUT_CMD" "$FETCH_TIMEOUT" "$@"; return; fi
87
+ local pid wd rc
88
+ "$@" &
89
+ pid=$!
90
+ ( sleep "$FETCH_TIMEOUT"; pkill -P "$pid" 2>/dev/null; kill "$pid" 2>/dev/null ) </dev/null >/dev/null 2>&1 &
91
+ wd=$!
92
+ wait "$pid" 2>/dev/null
93
+ rc=$?
94
+ kill "$wd" 2>/dev/null
95
+ wait "$wd" 2>/dev/null
96
+ (( rc >= 128 )) && rc=124
97
+ return "$rc"
98
+ }
99
+
100
+ # ---- semver ------------------------------------------------------------------
101
+ semver_cmp() { # A B (both X.Y.Z) -> older|same|newer; numeric, leading zeros are not octal
102
+ local IFS=. i p q
103
+ local -a x y
104
+ IFS=. read -r -a x <<< "$1"
105
+ IFS=. read -r -a y <<< "$2"
106
+ for i in 0 1 2; do
107
+ p=$((10#${x[i]:-0}))
108
+ q=$((10#${y[i]:-0}))
109
+ (( p < q )) && { echo older; return 0; }
110
+ (( p > q )) && { echo newer; return 0; }
111
+ done
112
+ echo same
113
+ }
114
+
115
+ # ---- cache location ----------------------------------------------------------
116
+ CACHE_DIR=""
117
+ resolve_cache_dir() {
118
+ local base="${XDG_CACHE_HOME:-}"
119
+ case "$base" in /*) ;; *) base="" ;; esac # relative values must be ignored (XDG spec)
120
+ [[ "$base" == *[[:cntrl:]]* ]] && base="" # a newline would inject key=value lines downstream
121
+ if [[ -z "$base" ]]; then
122
+ case "${HOME:-}" in /*) ;; *) return 0 ;; esac
123
+ [[ "$HOME" == *[[:cntrl:]]* ]] && return 0
124
+ base="$HOME/.cache"
125
+ fi
126
+ CACHE_DIR="$base/soleur"
127
+ }
128
+
129
+ # ---- installed ---------------------------------------------------------------
130
+ INST_SOURCE=none INST_VERSION="" INST_COMMIT="" INST_ID="" INST_MARKETPLACE="" CACHE_FILE=""
131
+
132
+ # Top-level "version" string of a JSON file without jq: a small depth-aware scanner, so a
133
+ # nested {"mcpServers":{"x":{"version":"1.0.0"}}} is never mistaken for the plugin version.
134
+ manifest_version_nojq() {
135
+ awk '
136
+ { buf = buf $0 "\n" }
137
+ END {
138
+ n = length(buf); depth = 0; i = 1; want = 0
139
+ while (i <= n) {
140
+ c = substr(buf, i, 1)
141
+ if (c == "\"") {
142
+ j = i + 1; s = ""
143
+ while (j <= n) {
144
+ d = substr(buf, j, 1)
145
+ if (d == "\\") { s = s substr(buf, j, 2); j += 2; continue }
146
+ if (d == "\"") break
147
+ s = s d; j++
148
+ }
149
+ i = j + 1
150
+ if (want) { if (depth == 1) { print s; exit } want = 0; continue }
151
+ k = i
152
+ while (k <= n && substr(buf, k, 1) ~ /[ \t\r\n]/) k++
153
+ if (substr(buf, k, 1) == ":" && depth == 1 && s == "version") want = 1
154
+ continue
155
+ }
156
+ if (c == "{" || c == "[") depth++
157
+ else if (c == "}" || c == "]") depth--
158
+ else if (want && c !~ /[ \t\r\n:]/) want = 0
159
+ i++
160
+ }
161
+ }' "$1" 2>/dev/null
162
+ }
163
+
164
+ read_manifest_version() {
165
+ local f="$1" v="" n
166
+ # A FIFO or a /dev/zero symlink here would stall jq/awk for the whole hook budget.
167
+ [[ -f "$f" && -r "$f" ]] || return 0
168
+ n="$(file_size "$f")" || return 0
169
+ (( n <= MANIFEST_MAX )) || return 0
170
+ if command -v jq >/dev/null 2>&1; then
171
+ v="$(jq -r '.version // empty' "$f" 2>/dev/null)" || v=""
172
+ else
173
+ v="$(manifest_version_nojq "$f")"
174
+ fi
175
+ printf '%s' "$v"
176
+ }
177
+
178
+ # ---- local checkout (source=checkout): plain file reads, never git ---------------------
179
+ # These and the manifest read are the only readers of repository bytes. Each checks regular-file-ness
180
+ # AND a stat size before it reads (a FIFO or device would hang an open; a sparse multi-GiB HEAD of
181
+ # NULs took 38 s through `read -n 512` alone), and nothing is executed, sourced or evaluated.
182
+ CK_VAL=""
183
+
184
+ file_size() { # path -> byte count on stdout; nothing when it cannot be measured
185
+ local n
186
+ n="$(stat -L -c %s -- "$1" 2>/dev/null || stat -L -f %z -- "$1" 2>/dev/null)" || return 1
187
+ [[ "$n" =~ ^[0-9]+$ ]] || return 1
188
+ printf '%s' "$n"
189
+ }
190
+
191
+ read_small() { # path -> CK_VAL = the file's single line (no newline); 1 when absent, irregular, oversize or not exactly one line
192
+ local f="$1" n line="" rc=0 got LC_ALL=C # bytes, not characters: a non-ASCII name must not change the verdict
193
+ CK_VAL=""
194
+ [[ -f "$f" && -r "$f" ]] || return 1
195
+ n="$(file_size "$f")" || return 1
196
+ (( n >= 1 && n <= SMALL_MAX )) || return 1
197
+ { IFS= read -r -n "$SMALL_MAX" line < "$f"; } 2>/dev/null || rc=$?
198
+ # What was consumed must be exactly what the file holds. `read` silently drops NUL bytes and
199
+ # stops at the first newline, so a NUL-spliced or multi-line file consumes fewer bytes than
200
+ # its size and is refused instead of printing an identity from a string that is not its
201
+ # bytes. LC_ALL=C makes ${#line} count bytes in every locale, so a non-ASCII branch or path
202
+ # resolves the same under C and UTF-8 (a character count would silently drop the banner).
203
+ got=${#line}
204
+ if (( rc == 0 && got < SMALL_MAX )); then got=$((got + 1)); fi # the newline `read` consumed
205
+ (( got == n )) || return 1
206
+ CK_VAL="$line"
207
+ }
208
+
209
+ valid_ref_name() { # refs/heads/<name> -> 0 when it is a plausible git branch ref, else 1 (deny-list, git check-ref-format style)
210
+ local r="$1" rest LC_ALL=C # the bracket classes below must mean the same thing in every locale
211
+ case "$r" in refs/heads/?*) ;; *) return 1 ;; esac
212
+ rest="${r#refs/heads/}"
213
+ case "$rest" in
214
+ *[[:cntrl:][:space:]~^:?*\[\\]*|*..*|*//*|*@\{*|/*|*/|.*|*/.*|*.|*.lock|*.lock/*|@) return 1 ;;
215
+ esac
216
+ return 0
217
+ }
218
+
219
+ packed_ref_sha() { # commondir ref -> CK_VAL = sha for the exact ref in packed-refs
220
+ local f="$1/packed-refs" n
221
+ CK_VAL=""
222
+ [[ -f "$f" && -r "$f" ]] || return 1
223
+ n="$(file_size "$f")" || return 1
224
+ (( n >= 1 && n <= PACKED_MAX )) || return 1
225
+ # A peel line (`^sha`) has one field, so it never matches; a first field starting with `#` is skipped;
226
+ # a record over 1 KiB (a real entry is ~40 + a ref name <= ~500) is skipped before awk splits it, and
227
+ # whatever the first match holds must still pass the 40-hex test below, so a corrupt line yields
228
+ # `unknown` or a sha the repository's own bytes name, never invented bytes. LC_ALL=C makes length()
229
+ # count bytes on every awk.
230
+ CK_VAL="$(SOLEUR_REF="$2" LC_ALL=C awk '
231
+ length($0) < 1024 && $1 !~ /^#/ && $2 == ENVIRON["SOLEUR_REF"] { print $1; exit }' "$f" 2>/dev/null)"
232
+ [[ "$CK_VAL" =~ $SHA40_RE ]]
233
+ }
234
+
235
+ resolve_checkout_commit() { # absolute plugin root -> CK_VAL = 40-hex HEAD commit, else 1
236
+ local root="${1%/}" top gd="" common head ref
237
+ CK_VAL=""
238
+ case "$root" in /*) ;; *) return 1 ;; esac
239
+ # Physical path first: a plugin root that is a symlink into another checkout must read THAT
240
+ # checkout's HEAD, not the HEAD of the repository that merely holds the link.
241
+ root="$(cd -P -- "$root" 2>/dev/null && pwd -P)" || return 1
242
+ case "$root" in */plugins/soleur) ;; *) return 1 ;; esac
243
+ top="${root%/plugins/soleur}"
244
+ [[ -n "$top" ]] || return 1
245
+ [[ -f "$root/.claude-plugin/plugin.json" ]] || return 1
246
+ # <top>/.git is a directory (clone or bare layout) or a file `gitdir: <path>` (linked worktree).
247
+ if [[ -d "$top/.git" ]]; then
248
+ gd="$top/.git"
249
+ elif [[ -f "$top/.git" ]]; then
250
+ read_small "$top/.git" || return 1
251
+ case "$CK_VAL" in gitdir:\ ?*) gd="${CK_VAL#gitdir: }" ;; *) return 1 ;; esac
252
+ case "$gd" in /*) ;; *) gd="$top/$gd" ;; esac
253
+ [[ -d "$gd" ]] || return 1
254
+ else
255
+ return 1
256
+ fi
257
+ common="$gd"
258
+ if [[ -f "$gd/commondir" ]]; then
259
+ read_small "$gd/commondir" || return 1
260
+ case "$CK_VAL" in /*) common="$CK_VAL" ;; *) common="$gd/$CK_VAL" ;; esac
261
+ [[ -d "$common" ]] || return 1
262
+ fi
263
+ read_small "$gd/HEAD" || return 1
264
+ head="$CK_VAL"
265
+ if [[ "$head" =~ $SHA40_RE ]]; then CK_VAL="$head"; return 0; fi # detached HEAD
266
+ case "$head" in "ref: "?*) ref="${head#ref: }" ;; *) return 1 ;; esac
267
+ valid_ref_name "$ref" || return 1
268
+ # A loose ref that exists must be valid; it never falls through to a stale packed entry.
269
+ if [[ -e "$common/$ref" || -L "$common/$ref" ]]; then
270
+ read_small "$common/$ref" || return 1
271
+ [[ "$CK_VAL" =~ $SHA40_RE ]] || return 1
272
+ return 0
273
+ fi
274
+ packed_ref_sha "$common" "$ref" || return 1
275
+ return 0
276
+ }
277
+
278
+ resolve_installed() {
279
+ local root="${CLAUDE_PLUGIN_ROOT:-}" v base parent
280
+ if [[ -z "$root" ]]; then
281
+ root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." 2>/dev/null && pwd)" || root=""
282
+ fi
283
+ resolve_cache_dir
284
+ if [[ -z "$root" || ! -d "$root" ]]; then INST_SOURCE=none; return 0; fi
285
+ # .../plugins/cache/<marketplace>/soleur/<id> -> marketplace (selects the right update command)
286
+ root="${root%/}"
287
+ parent="$(dirname "$root")"
288
+ if [[ "$(basename "$parent")" == soleur && "$(basename "$(dirname "$(dirname "$parent")")")" == cache ]]; then
289
+ base="$(basename "$(dirname "$parent")")"
290
+ [[ "$base" =~ ^[A-Za-z0-9._-]+$ ]] && INST_MARKETPLACE="$base"
291
+ fi
292
+ v="$(read_manifest_version "$root/.claude-plugin/plugin.json")"
293
+ if [[ "$v" =~ $SEMVER_RE ]]; then
294
+ INST_SOURCE=manifest INST_VERSION="$v" INST_ID="$v"
295
+ else
296
+ base="$(basename "$root")"
297
+ if [[ "$base" =~ $DIR_RE ]]; then
298
+ INST_SOURCE=commit INST_COMMIT="${base:0:12}" INST_ID="${base:0:12}"
299
+ elif resolve_checkout_commit "$root"; then
300
+ INST_SOURCE=checkout INST_COMMIT="${CK_VAL:0:12}" INST_ID="${CK_VAL:0:12}"
301
+ else
302
+ INST_SOURCE=unknown
303
+ fi
304
+ fi
305
+ if [[ -n "$INST_ID" && -n "$CACHE_DIR" ]]; then
306
+ if [[ "$INST_SOURCE" == checkout ]]; then
307
+ CACHE_FILE="$CACHE_DIR/latest-release.checkout" # one file for every commit of a moving checkout
308
+ else
309
+ CACHE_FILE="$CACHE_DIR/latest-release.$INST_ID"
310
+ fi
311
+ fi
312
+ return 0
313
+ }
314
+
315
+ print_installed() {
316
+ printf 'source=%s\n' "$INST_SOURCE"
317
+ [[ -n "$INST_VERSION" ]] && printf 'version=%s\n' "$INST_VERSION"
318
+ [[ -n "$INST_COMMIT" ]] && printf 'commit=%s\n' "$INST_COMMIT"
319
+ [[ -n "$INST_MARKETPLACE" ]] && printf 'marketplace=%s\n' "$INST_MARKETPLACE"
320
+ return 0
321
+ }
322
+
323
+ # ---- cache -------------------------------------------------------------------
324
+ C_TAG="" C_SHA="" C_FETCHED="" C_REL="" C_REASON="" CACHE_STATE=missing
325
+
326
+ reset_cache_vars() { C_TAG="" C_SHA="" C_FETCHED="" C_REL="" C_REASON="" CACHE_STATE=missing; }
327
+
328
+ load_cache() {
329
+ reset_cache_vars
330
+ [[ -n "$CACHE_FILE" && -f "$CACHE_FILE" ]] || return 0
331
+ # Same trust rule as write_cache: never read a cache from a directory we do not own.
332
+ [[ -O "$CACHE_DIR" && ! -L "$CACHE_DIR" ]] || return 0
333
+ local line key val rel_for="" complete="" now age mtime
334
+ while IFS= read -r line; do
335
+ key="${line%%=*}" val="${line#*=}"
336
+ case "$key" in
337
+ tag) C_TAG="$val" ;; sha) C_SHA="$val" ;; fetched) C_FETCHED="$val" ;;
338
+ relation) C_REL="$val" ;; relation_for) rel_for="$val" ;; reason) C_REASON="$val" ;;
339
+ complete) complete="$val" ;;
340
+ esac
341
+ done < "$CACHE_FILE" 2>/dev/null
342
+ # A truncated write loses the trailing `complete=1`, so it reads as corrupt, never as a
343
+ # fresh "unknown" that would silence the notice for a day.
344
+ # The one latest-release.checkout file answers a single commit at a time: an entry written for
345
+ # another commit is not corruption (and must not be reported as such), it is just not this
346
+ # commit's answer, so it reads as missing and the opt-in refresh replaces it.
347
+ if [[ "$INST_SOURCE" == checkout && "$complete" == 1 && "$rel_for" != "$INST_ID" && "$rel_for" =~ $SHA12_RE ]]; then
348
+ reset_cache_vars; CACHE_STATE=missing
349
+ return 0
350
+ fi
351
+ if [[ "$complete" != 1 || "$rel_for" != "$INST_ID" || ! "$C_TAG" =~ $TAG_RE || ! "$C_FETCHED" =~ ^[1-9][0-9]{0,11}$ ]]; then
352
+ reset_cache_vars; CACHE_STATE=corrupt
353
+ return 0
354
+ fi
355
+ [[ "$C_SHA" =~ $SHA40_RE ]] || C_SHA=""
356
+ case "$C_REL" in older|current|unknown) ;; *) C_REL="" ;; esac
357
+ [[ "$C_REASON" =~ ^[a-z-]{0,40}$ ]] || C_REASON=""
358
+ now="$(date +%s)"
359
+ age=$(( now - C_FETCHED ))
360
+ # A future-dated cache (clock change, hand edit) counts as stale, not fresh forever.
361
+ if (( age < 0 || age >= TTL_S )); then CACHE_STATE=stale; else CACHE_STATE=fresh; fi
362
+ mtime="$(stat -c %Y "$CACHE_FILE" 2>/dev/null || stat -f %m "$CACHE_FILE" 2>/dev/null || echo 0)"
363
+ if [[ "$mtime" =~ ^[0-9]+$ ]] && (( mtime > now + 60 )); then CACHE_STATE=stale; fi
364
+ return 0
365
+ }
366
+
367
+ write_cache() { # tag sha relation reason fetched
368
+ [[ -n "$CACHE_FILE" && -n "$CACHE_DIR" ]] || return 0
369
+ ( umask 077; mkdir -p "$CACHE_DIR" ) 2>/dev/null || return 0
370
+ # Never write into a directory we do not own or that is a symlink (shared XDG dirs).
371
+ [[ -d "$CACHE_DIR" && -O "$CACHE_DIR" && ! -L "$CACHE_DIR" ]] || return 0
372
+ chmod 700 "$CACHE_DIR" 2>/dev/null || true
373
+ local tmp="$CACHE_FILE.$$"
374
+ rm -f "$tmp" 2>/dev/null
375
+ # noclobber makes `>` fail on an existing path (including a planted symlink).
376
+ if ( set -C; umask 077
377
+ { printf 'tag=%s\nsha=%s\nfetched=%s\n' "$1" "$2" "$5"
378
+ printf 'relation=%s\nrelation_for=%s\nreason=%s\ncomplete=1\n' "$3" "$INST_ID" "$4"; } > "$tmp" ) 2>/dev/null; then
379
+ mv -f "$tmp" "$CACHE_FILE" 2>/dev/null || rm -f "$tmp" 2>/dev/null
380
+ else
381
+ rm -f "$tmp" 2>/dev/null
382
+ fi
383
+ return 0
384
+ }
385
+
386
+ # ---- latest release ----------------------------------------------------------
387
+ FETCH_STATUS="" F_TAG="" F_SHA=""
388
+
389
+ fetch_latest() {
390
+ FETCH_STATUS="" F_TAG="" F_SHA=""
391
+ if ! command -v git >/dev/null 2>&1; then FETCH_STATUS=no-git; return 0; fi
392
+ local out rc
393
+ out="$(
394
+ cd / 2>/dev/null
395
+ unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE GIT_CONFIG_COUNT GIT_CONFIG_PARAMETERS \
396
+ GIT_SSH GIT_SSH_COMMAND GIT_PROXY_COMMAND GIT_ASKPASS SSH_ASKPASS GIT_EXEC_PATH
397
+ export GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null GIT_CONFIG_NOSYSTEM=1 \
398
+ GIT_TERMINAL_PROMPT=0 HOME=/nonexistent XDG_CONFIG_HOME=/nonexistent
399
+ run_bounded git -c protocol.allow=never -c protocol.https.allow=always \
400
+ -c http.lowSpeedLimit=1 -c "http.lowSpeedTime=$FETCH_TIMEOUT" \
401
+ ls-remote --tags "$REPO_URL" 'refs/tags/v[0-9]*' 2>/dev/null
402
+ )"
403
+ rc=$?
404
+ if (( rc == 124 )); then FETCH_STATUS=timeout; return 0; fi
405
+ if (( rc != 0 )); then FETCH_STATUS=offline; return 0; fi
406
+ # Highest strict vX.Y.Z by numeric key (no `sort -V`: not portable). Components wider than
407
+ # four digits are skipped so the key stays exact in awk's double arithmetic.
408
+ F_TAG="$(printf '%s\n' "$out" | awk -F'\t' '
409
+ NF >= 2 {
410
+ t = $2; sub(/^refs\/tags\//, "", t); sub(/\^\{\}$/, "", t)
411
+ if (t ~ /^v[0-9]+\.[0-9]+\.[0-9]+$/) {
412
+ split(substr(t, 2), a, ".")
413
+ if (length(a[1]) <= 4 && length(a[2]) <= 4 && length(a[3]) <= 4)
414
+ printf "%.0f %s\n", (a[1] * 10000 + a[2]) * 10000 + a[3], t
415
+ }
416
+ }' | sort -n | tail -1 | cut -d' ' -f2)"
417
+ if [[ -z "$F_TAG" ]]; then FETCH_STATUS=no-tags; return 0; fi
418
+ # The peeled line (^{}) carries the commit an annotated tag points at.
419
+ F_SHA="$(printf '%s\n' "$out" | awk -F'\t' -v t="refs/tags/$F_TAG" \
420
+ '$2==t"^{}"{p=$1} $2==t{s=$1} END{print (p!="" ? p : s)}')"
421
+ [[ "$F_SHA" =~ $SHA40_RE ]] || F_SHA=""
422
+ FETCH_STATUS=ok
423
+ }
424
+
425
+ REL="" REL_REASON=""
426
+
427
+ compute_relation() { # needs INST_*, F_TAG, F_SHA
428
+ REL=unknown REL_REASON=""
429
+ case "$INST_SOURCE" in
430
+ manifest)
431
+ REL="$(semver_cmp "$INST_VERSION" "${F_TAG#v}")"
432
+ [[ "$REL" == older ]] || REL=current
433
+ ;;
434
+ commit|checkout)
435
+ # The tag's own commit is the installed one: nothing to compare.
436
+ if [[ -n "$F_SHA" && "${F_SHA:0:12}" == "$INST_COMMIT" ]]; then REL=current; return 0; fi
437
+ # Re-validated at the sink: only 12 hex ever reaches the URL, whatever the source said.
438
+ [[ "$INST_COMMIT" =~ $SHA12_RE ]] || { REL_REASON=installed-unknown; return 0; }
439
+ if ! command -v curl >/dev/null 2>&1; then REL_REASON=no-curl; return 0; fi
440
+ local body status
441
+ # page=2000 keeps the body ~20 KB instead of ~1.3 MB (the file list is paged); the
442
+ # top-level status is unaffected. -q ignores any ~/.curlrc.
443
+ body="$(cd / 2>/dev/null; run_bounded curl -q -fsS --proto =https --compressed -m "$FETCH_TIMEOUT" \
444
+ -H 'Accept: application/vnd.github+json' \
445
+ "$COMPARE_API/$INST_COMMIT...$F_TAG?per_page=1&page=2000" 2>/dev/null)" || { REL_REASON=compare-failed; return 0; }
446
+ # First "status" is the top-level one; later ones belong to the per-file entries.
447
+ status="$(printf '%s' "$body" | grep -o '"status"[[:space:]]*:[[:space:]]*"[a-z]*"' | head -1 | sed 's/.*"\([a-z]*\)"$/\1/')"
448
+ case "$status" in
449
+ ahead) REL=older ;;
450
+ identical|behind) REL=current ;;
451
+ diverged) REL_REASON=diverged ;;
452
+ *) REL_REASON=compare-unparseable ;;
453
+ esac
454
+ ;;
455
+ *) REL_REASON=installed-unknown ;;
456
+ esac
457
+ return 0
458
+ }
459
+
460
+ # refresh_latest: fetch, compare, cache. Honors a fresh cache unless $1 == force.
461
+ refresh_latest() {
462
+ local fetched
463
+ load_cache
464
+ if [[ "${1:-}" != force && "$CACHE_STATE" == fresh ]]; then FETCH_STATUS=cache-fresh; return 0; fi
465
+ fetch_latest
466
+ if [[ "$FETCH_STATUS" == ok ]]; then
467
+ compute_relation
468
+ fetched="$(date +%s)"
469
+ case "$REL_REASON" in
470
+ compare-failed|no-curl|compare-unparseable) fetched=$(( fetched - TTL_S + FAIL_FRESH_S )) ;;
471
+ esac
472
+ # Nothing to key a cache on without a recognisable install: answer, but do not persist.
473
+ [[ -n "$INST_ID" ]] && write_cache "$F_TAG" "$F_SHA" "$REL" "$REL_REASON" "$fetched"
474
+ C_TAG="$F_TAG" C_SHA="$F_SHA" C_REL="$REL" C_REASON="$REL_REASON" C_FETCHED="$fetched" CACHE_STATE=fresh
475
+ fi
476
+ return 0
477
+ }
478
+
479
+ print_latest() {
480
+ local rel="$C_REL" reason="$C_REASON" unreliable=0
481
+ printf 'cache=%s\n' "$CACHE_STATE"
482
+ [[ -n "$CACHE_DIR" ]] && printf 'cache_dir=%s\n' "$CACHE_DIR"
483
+ [[ -n "$FETCH_STATUS" ]] && printf 'latest_status=%s\n' "$FETCH_STATUS"
484
+ [[ -n "$C_TAG" ]] || return 0
485
+ printf 'latest=%s\n' "$C_TAG"
486
+ [[ -n "$C_SHA" ]] && printf 'latest_commit=%s\n' "$C_SHA"
487
+ if [[ -z "$INST_ID" ]]; then
488
+ rel=unknown reason=installed-unknown
489
+ else
490
+ [[ "$CACHE_STATE" != fresh ]] && unreliable=1
491
+ case "$FETCH_STATUS" in offline|timeout|no-git|no-tags) unreliable=1 ;; esac
492
+ # `older` stays true when old (releases only move forward); `current` does not. An
493
+ # unknown keeps the reason it was first answered with (diverged, compare-failed, ...).
494
+ if (( unreliable )) && [[ "$rel" == current ]]; then rel=unknown reason=stale-cache; fi
495
+ [[ -n "$rel" ]] || { rel=unknown; reason=cache-invalid; }
496
+ fi
497
+ printf 'relation=%s\n' "$rel"
498
+ [[ -n "$reason" ]] && printf 'reason=%s\n' "$reason"
499
+ return 0
500
+ }
501
+
502
+ main() {
503
+ local mode="" force="" a1="" a2=""
504
+ while (( $# )); do
505
+ case "$1" in
506
+ --installed|--cached-latest|--latest|--compare) mode="$1" ;;
507
+ --cmp) mode="$1"; a1="${2:-}"; a2="${3:-}"; shift 2 2>/dev/null || shift $# ;;
508
+ --refresh) force=force ;;
509
+ esac
510
+ shift 2>/dev/null || break
511
+ done
512
+ if [[ "$mode" == --cmp ]]; then
513
+ if [[ "$a1" =~ $SEMVER_RE && "$a2" =~ $SEMVER_RE ]]; then semver_cmp "$a1" "$a2"; else echo unknown; fi
514
+ return 0
515
+ fi
516
+ resolve_installed
517
+ case "$mode" in
518
+ --installed) print_installed ;;
519
+ --cached-latest) print_installed; load_cache; print_latest ;;
520
+ --latest) refresh_latest "$force"; print_latest ;;
521
+ --compare) print_installed; refresh_latest "$force"; print_latest ;;
522
+ *) echo "usage: plugin-version.sh --installed|--cached-latest|--latest [--refresh]|--compare [--refresh]|--cmp A B" >&2 ;;
523
+ esac
524
+ return 0
525
+ }
526
+
527
+ main "$@"
528
+ exit 0
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env bash
2
+ # precommit-guard.sh — refuse `git commit` on main/master, without hook execution.
3
+ #
4
+ # Canonical source for the commit-on-main check (Soleur Cloud Mode, FR5).
5
+ # Plugin command hooks are documented cloud-capable for most events, but a
6
+ # skill cannot rely on hook execution in a not-local session — and user repos
7
+ # consuming via requiredPlugins have no .claude/hooks at all. work/ship/one-shot
8
+ # invoke this script directly so the refusal is structural in both worlds.
9
+ #
10
+ # Scope: commit-on-main ONLY. The DONE-marker stop-gate is not extractable —
11
+ # it reads hook-stdin transcript data a standalone script cannot see; it stays
12
+ # prose + the plugin Stop hook.
13
+ #
14
+ # Self-contained by design: no jq, no .claude/hooks/lib vendoring (those paths
15
+ # resolve relative to the repo, which is wrong when this script runs from the
16
+ # plugin install cache), no emit_incident — callers log their own incidents.
17
+ # ONE sibling dependency: scripts/lib/wrapper-boundary.sh ships beside this
18
+ # script in the plugin cache, so $(dirname)/lib/… resolves in both contexts
19
+ # (#10248 — the canonical wrapper-prefix grammar the COMMIT_RE below consumes).
20
+ #
21
+ # Usage:
22
+ # precommit-guard.sh "COMMAND" check a shell command string
23
+ # precommit-guard.sh --cwd DIR also check DIR's branch (a `cd DIR &&`
24
+ # or `git -C DIR` inside COMMAND wins)
25
+ #
26
+ # Exit 0 = allowed (not a commit, or not on main/master).
27
+ # Exit 1 = refused — reason on stderr.
28
+ # Exit 2 = usage error.
29
+
30
+ set -euo pipefail
31
+
32
+ COMMAND=""
33
+ CWD=""
34
+ while [[ $# -gt 0 ]]; do
35
+ case "$1" in
36
+ --cwd) CWD="${2:-}"; shift 2 ;;
37
+ -*) echo "precommit-guard.sh: unknown flag: $1" >&2; exit 2 ;;
38
+ *) COMMAND="$1"; shift ;;
39
+ esac
40
+ done
41
+
42
+ # Detection is per pipeline segment: a `git commit` can hide anywhere in a
43
+ # chain — `git add && git commit`, `cd repo; git commit`, `printf msg |
44
+ # git commit -F -`, `LEFTHOOK=0 git commit`. Split COMMAND on the chain
45
+ # operators (&&, ||, ;, |, newline), then judge each segment.
46
+ #
47
+ # Known lexical limits (documented, not silently missed): subshell/eval forms
48
+ # like `bash -c 'git commit'`, `$(git commit)`, `( git commit )`, quoted
49
+ # `-C`/`cd` paths containing spaces, `git -c <cfg> commit` side effects beyond
50
+ # detection, and commits created without `git commit` (merge/pull/cherry-pick/
51
+ # rebase/continue) are out of this guard's declared scope — commit-on-main ONLY.
52
+ # Opaque string payloads (`rtk run -c '…'`, `rtk summary --shell '…'`) join that
53
+ # same declared class — the boundary fragment deliberately excludes the
54
+ # -c/--command/--shell atoms so they cannot match here.
55
+ _PC_GUARD_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
56
+ unset SOLEUR_ENV_ASN_RE SOLEUR_LAUNCHER_RE SOLEUR_RTK_RE SOLEUR_BOUNDARY_PFX SOLEUR_BOUNDARY_PFX_LOADED
57
+ if [[ -f "$_PC_GUARD_DIR/lib/wrapper-boundary.sh" ]]; then
58
+ # shellcheck source=/dev/null
59
+ source "$_PC_GUARD_DIR/lib/wrapper-boundary.sh" 2>/dev/null || true
60
+ fi
61
+ if [[ "${SOLEUR_BOUNDARY_PFX_LOADED:-0}" != 1 ]]; then
62
+ unset SOLEUR_ENV_ASN_RE SOLEUR_LAUNCHER_RE SOLEUR_RTK_RE SOLEUR_BOUNDARY_PFX
63
+ echo "precommit-guard.sh: wrapper-boundary.sh unavailable at $_PC_GUARD_DIR/lib — commit gate runs pre-#10248 matching" >&2
64
+ fi
65
+ unset _PC_GUARD_DIR
66
+ # #10248: the shared fragment replaces the inline env-asn/launcher alternation —
67
+ # one canonical atom set for every boundary-anchored gate. The :- fallback is
68
+ # the PRE-FIX atom set (env + launcher) — a superset of this file's prior
69
+ # env*/launcher?/env* sandwich, which is the safe direction for a deny gate.
70
+ COMMIT_RE='^[[:space:]]*('"${SOLEUR_BOUNDARY_PFX:-([A-Za-z_][A-Za-z_0-9]*=[^[:space:]]+|sudo|command|nice|env|xargs)}"'[[:space:]]+)*git([[:space:]]+(-C[[:space:]]+[^[:space:]]+|-c[[:space:]]+[^[:space:]]+|--git-dir=[^[:space:]]+|--git-dir[[:space:]]+[^[:space:]]+|-[A-Za-z]))*[[:space:]]+commit([[:space:]]|$)'
71
+
72
+ last_cd=""
73
+ while IFS= read -r seg; do
74
+ # Track the dir the chain has cd'd into — it applies to later segments.
75
+ cd_hit="$(grep -oE '(^|[[:space:]])cd[[:space:]]+[^[:space:]]+' <<<"$seg" | tail -n 1 | sed -E 's/.*cd[[:space:]]+//' || true)"
76
+ [[ -n "$cd_hit" ]] && last_cd="$cd_hit"
77
+
78
+ grep -qE "$COMMIT_RE" <<<"$seg" || continue
79
+
80
+ # Resolve the repo THIS segment commits into. Priority: the -C / --git-dir
81
+ # attached to the commit's own git invocation, then a GIT_DIR env-assignment
82
+ # prefix (LEFTHOOK=0-style prefixes are the reason env-assignments are scanned
83
+ # at all — dropping GIT_DIR would resolve the wrong repo); then the chain's
84
+ # most recent cd; then --cwd; then ambient $PWD.
85
+ target="" git_dir=""
86
+ if grep -qoE -- '--git-dir[=[:space:]][^[:space:]]+' <<<"$seg"; then
87
+ git_dir="$(grep -oE -- '--git-dir[=[:space:]][^[:space:]]+' <<<"$seg" | tail -n 1 | sed -E 's/^--git-dir[=[:space:]]+//' || true)"
88
+ elif grep -qoE -- '(^|[[:space:]])GIT_DIR=[^[:space:]]+' <<<"$seg"; then
89
+ git_dir="$(grep -oE -- '(^|[[:space:]])GIT_DIR=[^[:space:]]+' <<<"$seg" | tail -n 1 | sed -E 's/.*GIT_DIR=//' || true)"
90
+ else
91
+ git_c="$(grep -oE -- '-C[[:space:]]+[^[:space:]]+' <<<"$seg" | tail -n 1 | sed -E 's/^-C[[:space:]]+//' || true)"
92
+ [[ -n "$git_c" ]] && target="$git_c"
93
+ fi
94
+ if [[ -z "$target" && -z "$git_dir" ]]; then
95
+ for cand in "$last_cd" "$CWD" "$PWD"; do
96
+ [[ -n "$cand" && -d "$cand" ]] && target="$cand" && break
97
+ done
98
+ fi
99
+
100
+ if [[ -n "$git_dir" ]]; then
101
+ BRANCH="$(git --git-dir="$git_dir" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
102
+ elif [[ -n "$target" && -d "$target" ]]; then
103
+ BRANCH="$(git -C "$target" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
104
+ else
105
+ continue # nowhere to resolve — nothing to refuse on
106
+ fi
107
+ # The alternation is the stamped default-branch set — soleur:verification-scaffold
108
+ # emits this file with `main|master` substituted for the target's resolved default.
109
+ if [[ "$BRANCH" =~ ^(main|master)$ ]]; then
110
+ printf 'BLOCKED: Committing directly to %s is not allowed. Create a feature branch first.\n' "$BRANCH" >&2
111
+ exit 1
112
+ fi
113
+ done < <(printf '%s\n' "$COMMAND" | sed -E 's/&&|\|\||[;|]/\n/g')
114
+
115
+ exit 0