@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,247 @@
1
+ ---
2
+ name: linear-fetch
3
+ description: "This skill should be used when a user input contains a Linear issue reference (e.g., SOL-39 or linear.app/.../issue/<ID>) and the downstream agent needs the screenshots embedded in the issue as visual context."
4
+ allowed-tools:
5
+ - Bash
6
+ - mcp__linear-server__get_issue
7
+ - mcp__linear-server__list_comments
8
+ - mcp__linear-server__extract_images
9
+ preconditions:
10
+ - Linear MCP server is authenticated (mcp__linear-server__authenticate has been run for this session)
11
+ ---
12
+
13
+ <!-- grok-harness-invoke:start -->
14
+ **Grok Build (`plugins/soleur/lib/harness.ts` `invokeSkill()`):** Read this SKILL.md in this process and run it to completion. A one-segment `soleur:<name>` in this document names a SKILL — on Grok Build, Read `plugins/soleur/skills/<name>/SKILL.md` in this process; it is not a nested tool_use. A multi-segment id such as `soleur:<domain>:<name>` names an AGENT: spawn it, never Read it, and on Grok Build spawn_subagent takes the id with its colons replaced by hyphens (`agentIdToGrokSubagentType`). **Claude Code:** Skill tool for a skill (`soleur:<name>`), Task tool with `subagent_type` for an agent. Forbidden is executing a subset, not the Read.
15
+ <!-- grok-harness-invoke:end -->
16
+
17
+ # Linear Issue Image Context
18
+
19
+ This skill detects Linear issue references in caller input, fetches the issue body and recent comments, and streams the embedded screenshots directly into the active model conversation. The caller (typically `soleur:one-shot` Step 0a or `soleur:brainstorm` Phase 0.4) receives two return artifacts and is responsible for choosing the right one for each downstream consumer.
20
+
21
+ ## Caller Contract
22
+
23
+ This skill returns **two artifacts**. Mixing them up is the load-bearing failure mode the skill exists to prevent.
24
+
25
+ 1. **Agent context** — the full markdown blob (description + comment bodies) with Linear image content blocks streamed into the active conversation by `mcp__linear-server__extract_images`. **Callers MUST NOT write this artifact to disk, a PR body, a commit message, or any file under `knowledge-base/`.** The Linear image URLs inside it (`uploads.linear.app/*`) are signed bearer credentials that grant public-internet read access for their validity window.
26
+ 2. **Persist-safe summary** — the same text with every `uploads.linear.app/*` URL redacted to the literal token `[linear-image: REDACTED]`. **Callers MUST use this variant for any persistence**: writes to `knowledge-base/`, PR descriptions, commit messages, learning files, downstream subagent prompts.
27
+
28
+ The redaction primitive lives at [scripts/redact-linear-urls.sh](./scripts/redact-linear-urls.sh). It uses a **case-insensitive hostname match** (`[Hh][Tt][Tt][Pp][Ss]?://[Uu][Pp][Ll][Oo][Aa][Dd][Ss]\.[Ll][Ii][Nn][Ee][Aa][Rr]\.[Aa][Pp][Pp]/`) — DNS is case-insensitive and `https://Uploads.Linear.App/...` serves the same signed bearer credential as the lowercase form. The URL path uses a positive ASCII-only character class (RFC 3986 unreserved + reserved set), so Unicode separators (U+2028, U+2029, NBSP) and markdown/HTML terminators (`< > " ' ) ]`) all terminate the match cleanly. The full justification per character is inline in the script. The companion CI `pii-grep` job in `.github/workflows/pr-quality-guards.yml` mirrors the case-insensitive matching with `grep -iE`.
29
+
30
+ The repository is public on GitHub. A single leaked `uploads.linear.app` URL is a `single-user incident` brand-survival event per `knowledge-base/project/specs/archive/20261013-014038-feat-linear-issue-image-context/spec.md`.
31
+
32
+ ## Phases
33
+
34
+ ### Phase A — Reference detection
35
+
36
+ Scan the input string for substrings matching either:
37
+
38
+ - The identifier shape `[A-Z]{2,}-[0-9]+` (matches `SOL-39`, `LIN-12`, `ENG-100`, but also `PR-123`, `HTTP-200`, `CVE-2024` — false positives are handled in Phase B, not at the regex level), OR
39
+ - The URL shape `linear\.app/[^/]+/issue/([A-Z]+-[0-9]+)` — extract the captured identifier.
40
+
41
+ Uppercase each captured identifier and deduplicate (case-sensitive after uppercasing, exact-match on the number — `SOL-39` and `sol-39` collapse to one entry).
42
+
43
+ If more than 5 distinct identifiers match, use the `AskUserQuestion` tool to ask the user how to proceed before any MCP call:
44
+
45
+ - **Header:** "Linear cap"
46
+ - **Question:** "Found N Linear references; the default cap is 5 to bound prompt size and MCP cost. Process the first 5, all N, or abort?"
47
+ - **Options:** "Process first 5 (default)", "Process all (may bloat context)", "Abort". The "Other" escape is appended automatically by the runtime.
48
+
49
+ Take the user's choice as the working set for Phase B.
50
+
51
+ If 5 or fewer matches, proceed directly.
52
+
53
+ If zero matches, return immediately with no disclosure line — the skill is a no-op for inputs that do not reference Linear.
54
+
55
+ ### Phase B — Fetch + comment scan
56
+
57
+ For each identifier in the working set:
58
+
59
+ 1. Invoke `mcp__linear-server__get_issue(id=<identifier>)`. On a `404 Not Found` response, silently drop the identifier — this converts false-positive matches like `PR-123` or `CVE-2024` into no-ops with no user-visible failure. On any other failure (`403`, network error, token expired), emit the warning from Phase E and skip to the next identifier; do not abort.
60
+ 2. Invoke `mcp__linear-server__list_comments(issueId=<identifier>, orderBy="createdAt", limit=250)`. The MCP schema (verified at Phase 0 of the plan) supports `orderBy: createdAt | updatedAt` and `limit` up to 250. The schema does NOT expose an order-direction parameter, so the response order is treated as ambiguous: parse each comment's `createdAt` field and sort client-side **descending**, then take the first 10 entries as "10 most-recent by creation time." If the response contains 10 or fewer comments, skip the sort.
61
+ 3. Concatenate the issue description and the 10 most-recent comment bodies into a single markdown blob, delimited as `\n\n--- comment by <author.displayName> on <createdAt> ---\n\n` between each block.
62
+
63
+ ### Phase B.0 — Pre-fetch preflight (blob-independent; runs BEFORE Phase C)
64
+
65
+ **Run this fence before invoking `extract_images`, and abort the whole skill on a non-zero exit.**
66
+
67
+ Both checks below are **blob-independent** — they interrogate the plugin installation, not the
68
+ issue. Until #7450 they sat in Phase D, *after* Phase C had already streamed the issue's image
69
+ content blocks into the conversation. So on every halt path the signed `uploads.linear.app`
70
+ bearer URLs were already in the parent transcript, and the "fail closed" refusal protected only
71
+ the artifact nobody had produced yet. A guard that cannot run before the exposure is not a guard
72
+ against it. Nothing here depends on the blob, so nothing forced that ordering.
73
+
74
+ ```bash
75
+ [ -f "${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json" ] \
76
+ && grep -q '"name"[[:space:]]*:[[:space:]]*"soleur"' "${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json" \
77
+ || { echo "SOLEUR_LINEAR_FETCH_HALT reason=plugin-root-unverified root=[${CLAUDE_PLUGIN_ROOT}]"
78
+ echo "linear-fetch: cannot verify the Soleur plugin installation — stopping before anything is fetched." >&2
79
+ echo " Resolved plugin root: [${CLAUDE_PLUGIN_ROOT}]" >&2
80
+ echo " If that is EMPTY: no Soleur plugin is loaded in this session. Install it and start a NEW session — re-running here resolves the same empty root." >&2
81
+ echo " If it names a path: that path is not a Soleur install (a repo checkout is not an install). Run 'claude plugin update soleur@soleur-marketplace' (or the id 'claude plugin list' prints, if you added the repository directly), then RESTART Claude Code — plugin changes apply only on restart. If you installed with --scope project or --scope local, pass the same scope. Reinstall only if that does not clear it." >&2
82
+ echo " Nothing has been fetched yet, so nothing has leaked." >&2
83
+ exit 2; }
84
+ SCRUBBER="${CLAUDE_PLUGIN_ROOT}/skills/linear-fetch/scripts/redact-linear-urls.sh"
85
+ [[ -r "$SCRUBBER" ]] || { echo "SOLEUR_LINEAR_FETCH_HALT reason=scrubber-unreadable scrubber=[$SCRUBBER]"
86
+ echo "linear-fetch: the redaction primitive is missing from an otherwise valid Soleur install — stopping." >&2
87
+ echo " Expected at: [$SCRUBBER]" >&2
88
+ echo " The install is partial or out of date. Run 'claude plugin update soleur@soleur-marketplace' (or the id 'claude plugin list' prints, if you added the repository directly), then RESTART Claude Code — plugin changes apply only on restart. If you installed with --scope project or --scope local, pass the same scope. Reinstall only if that does not clear it." >&2
89
+ echo " Nothing has been fetched yet, so nothing has leaked." >&2
90
+ exit 2; }
91
+ echo "SOLEUR_LINEAR_FETCH_PREFLIGHT_OK scrubber=present"
92
+ ```
93
+
94
+ If this fence exits non-zero, **stop**: do not invoke `extract_images`, do not emit
95
+ `agent_context`, and do not emit a disclosure line. The correct operator-visible outcome is the
96
+ halt message above, not a partially-fetched issue.
97
+
98
+ ### Phase C — Image passthrough
99
+
100
+ For each issue's combined markdown blob from Phase B, invoke `mcp__linear-server__extract_images(markdown=<blob>)`. The MCP server resolves Linear-authenticated CDN URLs server-side and streams the underlying image bytes back into the active conversation as image content blocks. The skill never sees the bytes, never writes them to disk, never re-fetches them.
101
+
102
+ Count the image content blocks returned per issue (call this `N_images_total`), and count how many were extracted from comment bodies specifically (call this `M_comments_with_images`). The MCP response shape should make this distinguishable; if not, infer from the per-source split by passing description and comments through `extract_images` separately.
103
+
104
+ ### Phase D — Disclosure & dual-artifact return
105
+
106
+ For each issue, emit exactly one disclosure line to stdout based on the Phase C counts:
107
+
108
+ <!-- markdownlint-disable MD038 -->
109
+ <!-- #7927: MD038 on this table is cosmetic. Verified with the repo's own markdown-it:
110
+ every span here renders with no edge space (CommonMark strips a single leading and
111
+ trailing space), except one where the leading space is the point -- the cell
112
+ documents a string that is APPENDED, so its space is content. A disable-line comment
113
+ cannot be used: GFM discards anything past a row's closing pipe. -->
114
+ | Condition | Disclosure line |
115
+ |---|---|
116
+ | Images present | `Detected <ID> — fetched issue + N_images_total images from description and M_comments_with_images comments.` |
117
+ | Text-only (description and comments had zero `uploads.linear.app` references and `extract_images` returned zero images) | `Detected <ID> — text-only issue, no images.` |
118
+ | Soft anomaly (description or comments contained `uploads.linear.app` URLs but `extract_images` returned zero images) | `Detected <ID> — N image URLs found but extraction returned 0 images (soft anomaly).` |
119
+ | Comment cap hit (`list_comments` returned exactly 250 — Linear MCP's per-call limit, may have more comments past the cap) | append ` (comment cap hit — may be missing newer comments)` to the appropriate line above. The skill takes the 10 most-recent from the 250-window, so very high-volume issues may miss comments past position 250. Cursor pagination is a v1.1 follow-up (issue filed at plan finalization). |
120
+ <!-- markdownlint-enable MD038 -->
121
+
122
+ Construct the two return artifacts:
123
+
124
+ - **agent_context** — emit the full markdown blob inline in the conversation, so the parent's message history holds both the text and the image content blocks from `extract_images`. The parent retains these for downstream phases (one-shot Steps 3+, brainstorm Phase 2 Synthesis, brainstorm Phase 3 Capture). Per `2026-05-12-task-subagent-prompt-text-only.md`, image content blocks do NOT propagate to Task subagents — design accordingly.
125
+ - **persist_safe_summary** — produce by piping the full markdown blob through the redaction primitive, resolved from the **deployed plugin root** (ADR-179's canonical bare anchor, no fallback arm). The redacted text is the only artifact callers may persist. Return it as a fenced text block clearly labeled `PERSIST-SAFE SUMMARY (use this for any write to disk):` so the parent skill cannot confuse it with the agent_context.
126
+
127
+ **The exit-code dispatch below is load-bearing, and this site had none before #7450.** With no
128
+ guard, an unresolved root yields exit `127` and **empty stdout** — and empty stdout is exactly
129
+ the shape an agent can mistake for "the redacted text" and persist. Empty output is a failure,
130
+ never a clean summary. The default arm was removed, not re-pointed: `review/SKILL.md` instructs
131
+ `gh pr checkout`, so that arm resolved this scrubber from the reviewed party's tree.
132
+
133
+ The identity and readability checks are NOT repeated here — they ran in **Phase B.0**, before
134
+ anything was fetched. What remains is the blob-dependent half.
135
+
136
+ Substitute the full markdown blob verbatim between the heredoc delimiters. **The delimiter is
137
+ quoted (`<<'LINEAR_BLOB'`), which is load-bearing twice over**: it stops the shell expanding
138
+ issue text that Linear users control, and it keeps the blob off disk. The pre-#7450 form
139
+ (`bash "$SCRUBBER" <blob-tmpfile>`) wrote the **un-redacted, bearer-URL-bearing** blob to a file
140
+ that was never created, named, or removed by any step in this skill, on all four halt paths —
141
+ while the prose two paragraphs up still described the operation as piping.
142
+
143
+ ```bash
144
+ # RE-DERIVED, not inherited. Phase B.0 is a SEPARATE Bash call, and shell state does not
145
+ # persist across calls — so `$SCRUBBER` is EMPTY here unless it is defined in this fence.
146
+ # Assuming otherwise is not a theoretical risk: it shipped, and it bricked this skill on
147
+ # every invocation (`bash: : No such file or directory` -> reason=scrubber-nonzero-exit ->
148
+ # rc 2, so `persist_safe_summary` could never be produced and every caller hard-stopped).
149
+ # It failed CLOSED only because the anchor is quoted; unquoted, `bash $SCRUBBER <<'…'`
150
+ # executes the heredoc, i.e. runs Linear issue text as a shell script.
151
+ # B.0 still owns the identity preflight and the readability check — those are the
152
+ # blob-independent halts that must precede the fetch. This line is the same bare anchor,
153
+ # re-stated where it is used.
154
+ SCRUBBER="${CLAUDE_PLUGIN_ROOT}/skills/linear-fetch/scripts/redact-linear-urls.sh"
155
+
156
+ # The scrubber reads stdin and writes redacted text to stdout.
157
+ PERSIST_SAFE="$(bash "$SCRUBBER" <<'LINEAR_BLOB'
158
+ <the full markdown blob from Phase B, verbatim>
159
+ LINEAR_BLOB
160
+ )" || { echo "SOLEUR_LINEAR_FETCH_HALT reason=scrubber-nonzero-exit"
161
+ echo "linear-fetch: the redaction primitive exited non-zero — stopping." >&2
162
+ echo " Nothing is persisted. Do NOT fall back to agent_context; it carries the bearer URLs." >&2
163
+ echo " What to do: re-run once. If it repeats, this is a Soleur defect — file it with the exit status; do not hand-copy the issue text." >&2
164
+ exit 2; }
165
+
166
+ [ -n "$PERSIST_SAFE" ] \
167
+ || { echo "SOLEUR_LINEAR_FETCH_HALT reason=redaction-empty-output"
168
+ echo "linear-fetch: redaction produced EMPTY output — stopping." >&2
169
+ echo " Empty output is a failure, not a clean summary. Do NOT persist it." >&2
170
+ echo " What to do: re-run once. If it repeats, this is a Soleur defect — file it; do not paste the issue text by hand." >&2
171
+ exit 2; }
172
+
173
+ # Redaction must have HAPPENED, not merely have run. A scrubber that returns its input
174
+ # unchanged is non-empty and exits 0, so every other guard here stays green while the
175
+ # artifact labelled PERSIST-SAFE still carries the signed bearer URLs. This is also the
176
+ # only check on this path that does not depend on the anchor being trustworthy.
177
+ # Herestring, not a pipe: under `set -o pipefail` a `grep -q` that matches early closes
178
+ # the pipe, the producer takes SIGPIPE, and the pipeline reports failure on a match.
179
+ if grep -qiE 'uploads\.linear\.app' <<<"$PERSIST_SAFE"; then
180
+ echo "SOLEUR_LINEAR_FETCH_HALT reason=redaction-ineffective"
181
+ echo "linear-fetch: redaction did NOT remove the signed uploads.linear.app URLs — stopping." >&2
182
+ echo " The scrubber ran and returned output, but the output still carries bearer credentials." >&2
183
+ echo " Do NOT persist this text." >&2
184
+ echo " This is a SOLEUR DEFECT, not a problem with your issue: the scrubber ran and returned bearer URLs anyway." >&2
185
+ echo " What to do: do NOT retry — a retry will produce the same result. File this with the issue identifier (not its contents)." >&2
186
+ exit 2
187
+ fi
188
+
189
+ # Emit it. Shell state does not persist across Bash calls, so a block that assigns
190
+ # PERSIST_SAFE and never prints it cannot produce the artifact it exists to produce.
191
+ printf '%s\n' "$PERSIST_SAFE"
192
+ ```
193
+
194
+ On any halt above, `persist_safe_summary` **does not exist**. Callers must treat its absence as a hard stop and MUST NOT substitute `agent_context` — see the absent-artifact contract in [one-shot](../one-shot/SKILL.md) Step 0a and [brainstorm](../brainstorm/SKILL.md) Phase 0.4. `agent_context` carries the signed `uploads.linear.app` bearer URLs this primitive exists to remove, so falling back to it converts a refusal into the exact leak.
195
+
196
+ **Telemetry redaction (TR7).** This skill MUST NOT include the matched Linear identifier, the issue title, any image URL, or any signed-URL fragment in incident telemetry (`.claude/hooks/lib/incidents.sh emit_incident`). If telemetry is ever emitted from inside this skill, use generic strings only (e.g., `linear-fetch applied`). The current implementation has zero `emit_incident` call sites; a future maintainer adding one must pass the [assert-no-linear-telemetry.sh](./scripts/assert-no-linear-telemetry.sh) assertion or extend the assertion to cover the new emission shape.
197
+
198
+ ### Phase E — Failure handling
199
+
200
+ For any MCP failure that is NOT a benign 404 false-positive:
201
+
202
+ - Emit a one-line warning to stdout: `Linear fetch failed for <ID>: <reason>. Continuing without image context.`
203
+ - Do NOT abort the parent skill. The original identifier text remains in `$ARGUMENTS`; the downstream agent can still reason about the issue from prose alone.
204
+ - Do NOT include the matched identifier in any telemetry emission per TR7. The warning above is operator-facing stdout, not telemetry — the identifier is permitted in the warning so the operator can take corrective action.
205
+
206
+ The most common reasons (and what they mean):
207
+
208
+ | Reason | What it usually means |
209
+ |---|---|
210
+ | `MCP tool not found` | Linear MCP server is not registered for this session. Run `mcp__linear-server__authenticate`. |
211
+ | `403 Forbidden` | Token is valid but lacks access to the issue's workspace. Confirm Linear workspace membership. |
212
+ | `Network timeout` | Linear API unreachable. Try again, or work offline from prose only. |
213
+ | `Token expired` | OAuth token rotation needed. Re-run `mcp__linear-server__authenticate`. |
214
+
215
+ ## Caller invocation contract
216
+
217
+ This skill is invoked via the `Skill` tool from inside another skill (not directly by the operator). Two known callers as of v1:
218
+
219
+ - `soleur:one-shot` Step 0a — parent fetches once, retains images for Steps 3+, substitutes `persist_safe_summary` into the Steps 1-2 Task subagent's prompt template.
220
+ - `soleur:brainstorm` Phase 0.4 — parent fetches once, retains images for Phase 2 Synthesis and Phase 3 Capture, embeds `persist_safe_summary` in Phase 0.5 domain-leader prompts.
221
+
222
+ The skill does NOT modify `soleur:plan`, `soleur:fix-issue`, or `soleur:work` in v1. Those are tracked as a v2 follow-up issue at plan-finalization time.
223
+
224
+ ## Manual Test Runbook
225
+
226
+ The 10 spec acceptance-test scenarios. Operators run these against a real Linear workspace before marking the post-merge acceptance criteria done. Each step lists: input to type into Claude Code, expected disclosure, and post-condition to verify.
227
+
228
+ 1. **Happy path — single-issue, description-only image.** Pick a real `SOL-*` issue whose description contains one Linear-CDN-hosted image (markdown shape `![](URL)`). Type `fix <ID>` into a `soleur:one-shot`-routed prompt. Expected: disclosure `Detected <ID> — fetched issue + 1 images from description and 0 comments.` Visual check: the image is rendered inline in the current conversation. Persist check: the plan subagent's plan document at `knowledge-base/project/plans/...` contains zero `uploads.linear.app` matches (`grep -c` returns 0).
229
+ 2. **Happy path — comments-only image.** Pick an issue with a text-only description and one image attached in a comment. Same invocation. Expected: `fetched issue + 1 images from description and 1 comments.`
230
+ 3. **Text-only issue.** Pick an issue with no images anywhere. Expected: `text-only issue, no images.`
231
+ 4. **Multi-issue input.** Type `compare <ID-1> and <ID-2>`. Expected: two disclosure lines, one per issue.
232
+ 5. **404 issue.** Type `fix SOL-999999` (or any non-existent identifier). Expected: silent no-op, no warning printed (false-positive path).
233
+ 6. **False-positive identifier.** Type `the PR-123 we shipped`. Expected: silent no-op for `PR-123` (404 → silently dropped). No warning, no MCP error surfacing to the operator.
234
+ 7. **Comments cap.** Pick an issue with 15+ comments, several of which contain images. Expected: only 10 most-recent processed; the disclosure's `M_comments_with_images` count is at most 10.
235
+ 8. **Persist-safe round-trip.** After a `soleur:brainstorm` invocation that triggered the skill, run `grep -c 'uploads.linear.app' knowledge-base/project/brainstorms/<latest>.md`. Expected: `0`.
236
+ 9. **CI grep gate fires.** Create a fixture branch outside Claude Code, manually `git add` and `git commit` a file containing `https://uploads.linear.app/TEST-FIXTURE-NOT-REAL.png`, push, and open a PR. Expected: the `pii-grep` CI job fails the PR with a clear `::error::` annotation pointing at `knowledge-base/project/specs/archive/20261013-014038-feat-linear-issue-image-context/spec.md`.
237
+ 10. **Telemetry redaction.** Run any skill that emits incident telemetry while the conversation has linear-fetch context. Inspect `.claude/incidents/*.jsonl` after the test run. Expected: zero lines contain a Linear identifier (`SOL-\d+` or similar) or `uploads.linear.app`.
238
+
239
+ If any scenario fails post-merge, file a P1 follow-up issue, leave `#3635` open, and do not mark the skill production-ready until the failure is reproduced and fixed.
240
+
241
+ ## Sharp Edges
242
+
243
+ - The `LINEAR_CDN_PATTERNS` array in [scripts/redact-linear-urls.sh](./scripts/redact-linear-urls.sh) is intentionally extensible (currently one pattern: `uploads.linear.app`). If Linear introduces a new CDN hostname (`cdn.linear.app`, `linear-assets.com`), add a new pattern to the array AND update the CI `pii-grep` workflow regex in the same PR. The redaction primitive's unit-test fixtures must also gain a matching fixture per new pattern.
244
+ - The character class excludes single quote via the bash `$'\x27'` ANSI-C escape (so the regex literal contains `'`). This works on bash 5.x (Linux) and bash 3.2 (macOS) but the test runbook explicitly tests on both before declaring the script production-ready.
245
+ - `list_comments` `orderBy` direction is not exposed in the MCP schema; client-side `createdAt desc` sort + take-10 is the hard fallback to remain order-independent. Do not assume server-side direction.
246
+ - The skill must be invoked via the `Skill` tool, not run as a CLI. The `Skill` invocation places the image content blocks from `extract_images` into the caller's conversation; a standalone CLI run would lose the images entirely.
247
+ - `extract_images` may return zero images for an issue whose markdown contains `uploads.linear.app` URLs (e.g., an upstream Linear bug, transient CDN failure, expired URL inside the issue). The Phase D soft-anomaly disclosure exists precisely so this case is operator-visible and not silent.
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env bash
2
+ # Assert that a telemetry payload (or any text passed via stdin or as
3
+ # the first argument file path) contains no Linear identifiers, no Linear
4
+ # CDN URLs, and no UUID-style Linear IDs. Used to enforce spec TR7.
5
+ #
6
+ # Usage:
7
+ # echo "$payload" | bash assert-no-linear-telemetry.sh
8
+ # bash assert-no-linear-telemetry.sh /path/to/file.jsonl
9
+ #
10
+ # Exit 0 if clean. Exit 1 if any forbidden pattern matches. The first
11
+ # offending pattern is printed to stderr with the matched substring.
12
+ #
13
+ # Forbidden patterns:
14
+ # 1. Linear issue identifier: [A-Z]{2,}-[0-9]+ (matches SOL-39, LIN-12, etc.)
15
+ # 2. Linear CDN URL: uploads\.linear\.app
16
+ # 3. Linear UUID-style ID: 8-4-4-4-12 hex (Linear's internal issue IDs)
17
+ #
18
+ # Note: this helper is INTENTIONALLY strict. Operator-facing stdout (the
19
+ # Phase E warning line that names the failed identifier) is NOT routed
20
+ # through this assertion — only telemetry payloads emitted to
21
+ # .claude/hooks/lib/incidents.sh are gated. The skill currently has zero
22
+ # emit_incident call sites; this helper exists so a future maintainer
23
+ # adding one cannot regress TR7 silently.
24
+ #
25
+ # Known limitations (security-sentinel P2-1, P2-2):
26
+ # 1. Base64-encoded payloads are NOT decoded before pattern check. A
27
+ # caller that base64-encodes Linear identifiers or URLs in telemetry
28
+ # will silently pass this gate. If telemetry shapes start including
29
+ # encoded fields, add a base64-decode pre-pass (detect strings of
30
+ # shape `^[A-Za-z0-9+/=]{16,}$`, decode, recurse).
31
+ # 2. URL-fragment-only leaks (e.g., `?sig=<long_base64>&expires=<ts>`
32
+ # without the hostname) are NOT caught. A future maintainer pasting
33
+ # signed-URL query params separately from the host would bypass.
34
+ # Consider adding a fourth pattern matching `\?[a-zA-Z]+=[A-Za-z0-9_-]{32,}`.
35
+ # These limitations are acceptable for the current zero-emit_incident
36
+ # baseline; revisit if the skill grows real telemetry emissions.
37
+
38
+ set -eu
39
+
40
+ if [[ $# -ge 1 && -r "$1" ]]; then
41
+ input=$(cat "$1")
42
+ else
43
+ input=$(cat -)
44
+ fi
45
+
46
+ # Pattern 1: Linear issue identifier shape.
47
+ if grep -qE '\b[A-Z]{2,}-[0-9]+\b' <<<"$input"; then
48
+ match=$(printf '%s' "$input" | grep -oE '\b[A-Z]{2,}-[0-9]+\b' | head -n 1)
49
+ echo "FAIL: telemetry payload contains Linear identifier shape: $match" >&2
50
+ exit 1
51
+ fi
52
+
53
+ # Pattern 2: Linear CDN hostname (any form, signed-URL fragments included).
54
+ if grep -qiE 'uploads\.linear\.app' <<<"$input"; then
55
+ echo "FAIL: telemetry payload contains uploads.linear.app reference" >&2
56
+ exit 1
57
+ fi
58
+
59
+ # Pattern 3: UUID-style ID (Linear's internal issue UUIDs).
60
+ if grep -qE '\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b' <<<"$input"; then
61
+ match=$(printf '%s' "$input" | grep -oE '\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b' | head -n 1)
62
+ echo "FAIL: telemetry payload contains UUID-style identifier: $match" >&2
63
+ exit 1
64
+ fi
65
+
66
+ exit 0
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+ # Redact uploads.linear.app URLs from stdin. Writes redacted text to
3
+ # stdout; writes redaction count to stderr.
4
+ #
5
+ # Brand-survival: a regression here means a uploads.linear.app signed URL
6
+ # can leak into a committed artifact on a public-GitHub repo. See
7
+ # knowledge-base/project/specs/archive/20261013-014038-feat-linear-issue-image-context/spec.md FR7
8
+ # and the plan's User-Brand Impact section.
9
+ #
10
+ # Hostname matching is case-insensitive (DNS is case-insensitive — a URL like
11
+ # https://Uploads.Linear.App/x.png resolves to the same CDN host as the
12
+ # lowercase form and still serves the signed bearer credential). The hostname
13
+ # is encoded as bracketed character classes ([Hh][Tt]...) because BSD sed
14
+ # (macOS) does not portably support the `s///gI` case-insensitive flag, so
15
+ # we cannot rely on a flag in the substitution path.
16
+ #
17
+ # URL-path character class is a POSITIVE match against the RFC 3986 unreserved
18
+ # + reserved set (ASCII only). This is more conservative than the prior
19
+ # negated class:
20
+ # - Multi-byte UTF-8 sequences (U+2028, U+2029, NBSP, etc.) automatically
21
+ # terminate the match at the first non-ASCII byte. POSIX [:space:] is
22
+ # ASCII-only and would let the redactor consume past Unicode separators
23
+ # (security-sentinel P2-3 + cq-regex-unicode-separators-escape-only).
24
+ # - Markdown/HTML terminators (< > " ' ) ]) are not in the set, so URLs
25
+ # end correctly inside autolinks, HTML attributes, and markdown links.
26
+ #
27
+ # Adding a new CDN hostname (e.g., cdn.linear.app) requires:
28
+ # 1. A new entry in LINEAR_CDN_PATTERNS below.
29
+ # 2. An updated regex in .github/workflows/pr-quality-guards.yml pii-grep.
30
+ # 3. A matching fixture in redact-linear-urls.test.sh.
31
+ # The parity test (parity.test.sh, when added) enforces 1 and 2 stay in sync.
32
+ #
33
+ # set -e is on; pipefail is intentionally off because `grep -oE` returns
34
+ # exit 1 on zero matches — a benign condition we count and continue past.
35
+
36
+ set -eu
37
+
38
+ LINEAR_CDN_PATTERNS=(
39
+ $'[Hh][Tt][Tt][Pp][Ss]?://[Uu][Pp][Ll][Oo][Aa][Dd][Ss]\\.[Ll][Ii][Nn][Ee][Aa][Rr]\\.[Aa][Pp][Pp]/[A-Za-z0-9._~:/?#@!$&*+,;=%-]+'
40
+ )
41
+
42
+ input=$(cat -)
43
+ count=0
44
+ output="$input"
45
+ for pattern in "${LINEAR_CDN_PATTERNS[@]}"; do
46
+ matches=$(printf '%s' "$output" | grep -oE "$pattern" | wc -l | tr -d '[:space:]')
47
+ count=$((count + matches))
48
+ output=$(printf '%s' "$output" | sed -E "s#$pattern#[linear-image: REDACTED]#g")
49
+ done
50
+ printf '%s' "$output"
51
+ printf '%s' "$count" >&2
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env bash
2
+ # Substitute a placeholder in a template (stdin) with a value (arg 1).
3
+ # Used by persist-safe-integration.test.sh to simulate the runtime
4
+ # substitution that one-shot Step 0a and brainstorm Phase 0.4 perform
5
+ # when threading persist_safe_summary into downstream prompts.
6
+ #
7
+ # Usage:
8
+ # echo "$template" | bash render-caller-template.sh "$summary"
9
+ #
10
+ # The placeholder is the literal string __PERSIST_SAFE_SUMMARY__ — chosen
11
+ # to be visually distinct from $ARGUMENTS (one-shot template) and {desc}
12
+ # (brainstorm template) so test fixtures can mirror either real template
13
+ # by writing the placeholder in the right spot.
14
+ #
15
+ # Implementation note: awk is used (not sed) so the substitution value
16
+ # can contain arbitrary characters including /, &, \, and newlines
17
+ # without escaping. The placeholder itself is 24 chars of fixed-shape
18
+ # alphanumeric + underscore — no regex metacharacters.
19
+
20
+ set -eu
21
+
22
+ VALUE="${1:?usage: render-caller-template.sh <value>}"
23
+ TEMPLATE=$(cat -)
24
+ PLACEHOLDER='__PERSIST_SAFE_SUMMARY__'
25
+
26
+ # Use awk for arbitrary-content substitution — no sed escaping needed.
27
+ printf '%s' "$TEMPLATE" | awk -v val="$VALUE" -v ph="$PLACEHOLDER" '
28
+ {
29
+ line = $0
30
+ while ((p = index(line, ph)) > 0) {
31
+ printf "%s%s", substr(line, 1, p-1), val
32
+ line = substr(line, p + length(ph))
33
+ }
34
+ print line
35
+ }
36
+ '
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env bash
2
+ # Tests for assert-no-linear-telemetry.sh — TR7 telemetry-redaction gate.
3
+ #
4
+ # Run via: bash plugins/soleur/skills/linear-fetch/scripts/assert-no-linear-telemetry.test.sh
5
+
6
+ set -eu
7
+
8
+ # Owning trap (ADR-129). These files allocated tempfiles with no cleanup for as long as they
9
+ # sat under scripts/, where no runner reached them; relocating them into test/ put them in
10
+ # the lint's scope and surfaced it.
11
+ _LF_TMPS=()
12
+ _lf_cleanup() { [[ ${#_LF_TMPS[@]} -gt 0 ]] && rm -f "${_LF_TMPS[@]}"; return 0; }
13
+ trap _lf_cleanup EXIT INT TERM HUP
14
+ _lf_mktemp() { local t; t="$(mktemp)" || return 1; _LF_TMPS+=("$t"); printf '%s' "$t"; }
15
+
16
+
17
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../scripts" && pwd)"
18
+ SCRIPT="$SCRIPT_DIR/assert-no-linear-telemetry.sh"
19
+
20
+ PASS=0
21
+ FAIL=0
22
+ fail() { echo " FAIL: $1"; FAIL=$((FAIL+1)); }
23
+ pass() { echo " pass: $1"; PASS=$((PASS+1)); }
24
+
25
+ # Helper: pipe $1 through script, capture exit code.
26
+ run_assert() {
27
+ set +e
28
+ printf '%s' "$1" | bash "$SCRIPT" >/dev/null 2>/dev/null
29
+ RC=$?
30
+ set -e
31
+ }
32
+
33
+ # ------------------------------------------------------------------------
34
+ # Clean cases — must exit 0.
35
+ # ------------------------------------------------------------------------
36
+ echo "Test 1: empty payload"
37
+ run_assert ""
38
+ [[ "$RC" == "0" ]] && pass "exit 0 on empty" || fail "rc=$RC"
39
+
40
+ echo "Test 2: generic telemetry text (no Linear refs)"
41
+ run_assert "linear-fetch applied"
42
+ [[ "$RC" == "0" ]] && pass "exit 0 on generic" || fail "rc=$RC"
43
+
44
+ echo "Test 3: JSON-like telemetry without Linear refs"
45
+ run_assert '{"rule": "hr-gdpr-gate", "action": "applied", "duration_ms": 12}'
46
+ [[ "$RC" == "0" ]] && pass "exit 0" || fail "rc=$RC"
47
+
48
+ # ------------------------------------------------------------------------
49
+ # Forbidden patterns — must exit 1.
50
+ # ------------------------------------------------------------------------
51
+ echo "Test 4: Linear identifier SOL-39"
52
+ run_assert "telemetry for SOL-39 processed"
53
+ [[ "$RC" == "1" ]] && pass "exit 1 on SOL-39" || fail "rc=$RC"
54
+
55
+ echo "Test 5: Linear identifier ENG-1234"
56
+ run_assert "ENG-1234 in payload"
57
+ [[ "$RC" == "1" ]] && pass "exit 1 on ENG-1234" || fail "rc=$RC"
58
+
59
+ echo "Test 6: uploads.linear.app URL"
60
+ run_assert "see https://uploads.linear.app/TEST-FIXTURE-NOT-REAL.png"
61
+ [[ "$RC" == "1" ]] && pass "exit 1 on CDN URL" || fail "rc=$RC"
62
+
63
+ echo "Test 7: uploads.linear.app case-insensitive"
64
+ run_assert "Uploads.Linear.App/X.PNG"
65
+ [[ "$RC" == "1" ]] && pass "exit 1 on case-insensitive CDN" || fail "rc=$RC"
66
+
67
+ echo "Test 8: UUID-style ID"
68
+ run_assert "issue 9e0a3888-fd38-49cd-87cb-83cdcecca199 done"
69
+ [[ "$RC" == "1" ]] && pass "exit 1 on UUID" || fail "rc=$RC"
70
+
71
+ # ------------------------------------------------------------------------
72
+ # Edge cases — should NOT trigger false positives.
73
+ # ------------------------------------------------------------------------
74
+ echo "Test 9: PR-123 alone should NOT trigger (matches identifier shape but no Linear ref)"
75
+ # Actually PR-123 DOES match [A-Z]{2,}-[0-9]+ — this is the spec's R2
76
+ # false-positive class. The assertion is intentionally conservative:
77
+ # any [A-Z]{2,}-[0-9]+ in telemetry is forbidden, even if it's a GitHub
78
+ # PR reference. Telemetry should use generic strings or specific
79
+ # whitelisted IDs (hashed/anonymized) instead.
80
+ run_assert "see PR-123 for context"
81
+ [[ "$RC" == "1" ]] && pass "exit 1 on PR-123 (conservative)" || fail "rc=$RC"
82
+
83
+ echo "Test 10: lowercase identifier sol-39 should NOT trigger (case-sensitive on prefix)"
84
+ run_assert "sol-39 is lowercase"
85
+ [[ "$RC" == "0" ]] && pass "exit 0 on lowercase" || fail "rc=$RC"
86
+
87
+ # ------------------------------------------------------------------------
88
+ # File-path arg mode.
89
+ # ------------------------------------------------------------------------
90
+ echo "Test 11: file-path argument with clean content"
91
+ tmp=$(_lf_mktemp)
92
+ printf 'linear-fetch applied\n' > "$tmp"
93
+ set +e
94
+ bash "$SCRIPT" "$tmp" >/dev/null 2>/dev/null
95
+ RC=$?
96
+ set -e
97
+ rm -f "$tmp"
98
+ [[ "$RC" == "0" ]] && pass "exit 0 on clean file" || fail "rc=$RC"
99
+
100
+ echo "Test 12: file-path argument with forbidden content"
101
+ tmp=$(_lf_mktemp)
102
+ printf 'SOL-39 in file\n' > "$tmp"
103
+ set +e
104
+ bash "$SCRIPT" "$tmp" >/dev/null 2>/dev/null
105
+ RC=$?
106
+ set -e
107
+ rm -f "$tmp"
108
+ [[ "$RC" == "1" ]] && pass "exit 1 on dirty file" || fail "rc=$RC"
109
+
110
+ # ------------------------------------------------------------------------
111
+ echo
112
+ echo "Results: $PASS passed, $FAIL failed"
113
+ exit $((FAIL > 0 ? 1 : 0))
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env bash
2
+ # Cross-artifact parity test for the load-bearing redaction rail.
3
+ #
4
+ # The plan's User-Brand Impact section names a 2-layer defense (in-skill
5
+ # redaction + CI grep) "with no shared bypass." That guarantee depends on
6
+ # both files covering the SAME set of CDN hostnames. This test asserts
7
+ # the property: for each canonical hostname, both the redaction script
8
+ # and the CI workflow must contain a reference to it.
9
+ #
10
+ # Without this test, the only enforcement is the "Sharp Edges" SKILL.md
11
+ # note that says "update both sites." Reviewer agents flagged the bare-
12
+ # convention approach as schema-drift-prone (learning
13
+ # 2026-05-10-handshake-schema-drift-and-stale-precondition-budgets.md).
14
+ #
15
+ # Update CANONICAL_HOSTS below when LINEAR_CDN_PATTERNS gains a new
16
+ # hostname — the test will then enforce that the workflow grew the
17
+ # matching pattern in the same PR.
18
+ #
19
+ # Run via: bash plugins/soleur/skills/linear-fetch/scripts/parity.test.sh
20
+
21
+ set -eu
22
+
23
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../scripts" && pwd)"
24
+ REPO_ROOT="$(cd "$SCRIPT_DIR/../../../../.." && pwd)"
25
+ REDACTOR="$SCRIPT_DIR/redact-linear-urls.sh"
26
+ WORKFLOW="$REPO_ROOT/.github/workflows/pr-quality-guards.yml"
27
+
28
+ # Canonical hostname set the redactor and CI grep both must cover.
29
+ # When a new hostname (e.g., cdn.linear.app) is added to LINEAR_CDN_PATTERNS,
30
+ # add it here AND ensure the workflow's pii-grep regex covers it.
31
+ CANONICAL_HOSTS=("uploads.linear.app")
32
+
33
+ PASS=0
34
+ FAIL=0
35
+ fail() { echo " FAIL: $1"; FAIL=$((FAIL+1)); }
36
+ pass() { echo " pass: $1"; PASS=$((PASS+1)); }
37
+
38
+ # Build a case-insensitive bracket pattern for a lowercase hostname.
39
+ # Example: "uploads.linear.app" -> "[Uu][Pp][Ll][Oo][Aa][Dd][Ss]\.[Ll][Ii][Nn][Ee][Aa][Rr]\.[Aa][Pp][Pp]"
40
+ case_bracket_pattern() {
41
+ local host="$1"
42
+ local out=""
43
+ local i c upper
44
+ for ((i = 0; i < ${#host}; i++)); do
45
+ c="${host:$i:1}"
46
+ if [[ "$c" == "." ]]; then
47
+ # The redactor file stores hostnames inside a bash $'...' ANSI-C
48
+ # string, so a literal `.` in the regex is escaped as `\\.` (two
49
+ # backslashes followed by a dot — bash's ANSI-C parser collapses
50
+ # this to one backslash + dot at runtime). We grep the on-disk
51
+ # representation, so we need the double-backslash form here.
52
+ out+='\\.'
53
+ else
54
+ upper="${c^^}"
55
+ out+="[${upper}${c}]"
56
+ fi
57
+ done
58
+ printf '%s' "$out"
59
+ }
60
+
61
+ # Files exist sanity check.
62
+ [[ -r "$REDACTOR" ]] || { echo "ERROR: redactor not readable at $REDACTOR" >&2; exit 2; }
63
+ [[ -r "$WORKFLOW" ]] || { echo "ERROR: workflow not readable at $WORKFLOW" >&2; exit 2; }
64
+
65
+ for host in "${CANONICAL_HOSTS[@]}"; do
66
+ echo "Host: $host"
67
+
68
+ # Redactor encodes hostnames as [Cc][Hh]... bracketed case-insensitive
69
+ # sequences. Build the expected pattern and grep for it.
70
+ case_pat=$(case_bracket_pattern "$host")
71
+ if grep -qF "$case_pat" "$REDACTOR"; then
72
+ pass "redactor LINEAR_CDN_PATTERNS covers $host (matched: $case_pat)"
73
+ else
74
+ fail "redactor missing $host — expected to find literal '$case_pat'"
75
+ fi
76
+
77
+ # Workflow uses `grep -iE` so the literal lowercase form is what appears
78
+ # in the regex. (Other prose mentions in comments don't affect the gate's
79
+ # behavior but do satisfy this presence-check, which is fine — the test's
80
+ # job is to assert co-presence, not exhaustive line-by-line equivalence.)
81
+ if grep -qF "$host" "$WORKFLOW"; then
82
+ pass "workflow references $host"
83
+ else
84
+ fail "workflow does NOT reference $host — pii-grep would not catch leaks of this host"
85
+ fi
86
+ done
87
+
88
+ # Test 2: no orphan host in the workflow (a hostname appearing in the
89
+ # workflow's actual regex but NOT in CANONICAL_HOSTS would indicate the
90
+ # parity test itself is stale). Heuristic: extract `[a-z0-9-]+\.linear\.app`
91
+ # literals from the workflow's gate steps and assert each is in
92
+ # CANONICAL_HOSTS.
93
+ echo
94
+ echo "Workflow orphan-host check"
95
+ workflow_hosts=$(grep -oE '[a-z0-9-]+\\\.linear\\\.app' "$WORKFLOW" | sed -E 's/\\\././g' | sort -u || true)
96
+ canonical_set=$(printf '%s\n' "${CANONICAL_HOSTS[@]}" | sort -u)
97
+ orphan=""
98
+ while IFS= read -r wh; do
99
+ [[ -z "$wh" ]] && continue
100
+ if ! printf '%s\n' "$canonical_set" | grep -cFx >/dev/null "$wh"; then
101
+ orphan+="$wh "
102
+ fi
103
+ done <<< "$workflow_hosts"
104
+ if [[ -z "${orphan// /}" ]]; then
105
+ pass "no orphan hosts in workflow"
106
+ else
107
+ fail "workflow references hosts not in CANONICAL_HOSTS: $orphan"
108
+ fi
109
+
110
+ echo
111
+ echo "Results: $PASS passed, $FAIL failed"
112
+ exit $((FAIL > 0 ? 1 : 0))