@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,713 @@
1
+ #!/usr/bin/env bash
2
+ # x-setup.sh -- X/Twitter API credential setup and validation
3
+ #
4
+ # SECURITY: Credentials MUST be in environment variables.
5
+ # Never pass tokens as CLI arguments (visible in ps/history).
6
+ #
7
+ # Usage: x-setup.sh <command> [args]
8
+ # Commands:
9
+ # validate-credentials - Verify all 4 env vars via GET /2/users/me
10
+ # write-env - Write credentials to .env with chmod 600
11
+ # verify - Source .env and run round-trip API check
12
+ #
13
+ # Environment variables (required for API commands):
14
+ # X_API_KEY - API key (consumer key)
15
+ # X_API_SECRET - API secret (consumer secret)
16
+ # X_ACCESS_TOKEN - Access token
17
+ # X_ACCESS_TOKEN_SECRET - Access token secret
18
+ #
19
+ # Exit codes:
20
+ # 0 - Success
21
+ # 1 - General error, including missing credentials and a write-env value outside the .env
22
+ # allow-list, or an operational write-env fault (a held lock, an unreadable or dangling .env).
23
+ # Told apart by the value-free stderr marker `SOLEUR_CREDENTIAL_REFUSED`: `reason=token_shape` or
24
+ # `control_char` with `var=<NAME>` is the allow-list (fix the value, do not retry); `reason=env_*`
25
+ # with `var=ENV_FILE phase=write-env` is a write fault (diagnose, retry once). Exit 1 with no marker
26
+ # is a missing credential.
27
+ #
28
+ # Output: JSON or plain text to stdout
29
+ # Errors: Messages to stderr, exit 1. The exception is `write-env`, which has no stdout payload and
30
+ # whose human text is therefore all on STDOUT (agent runtimes surface stdout and swallow
31
+ # stderr): the missing-credentials diagnostic, the allow-list line, and the "Wrote N
32
+ # variables" confirmation. Only the allow-list marker stays on stderr.
33
+
34
+ set -euo pipefail
35
+
36
+ # (#7797) Xtrace refusal -- the FIRST thing after `set …`, so nothing above it is
37
+ # traced. This script ships to installed Soleur CLIs: the terminal it runs in
38
+ # belongs to the founder, and `bash -x` would print their live X
39
+ # credentials. UNCONDITIONAL, and that is measured rather than inherited:
40
+ # `cmd_verify` does `set -a; source "$env_file"; set +a`, loading the founder's
41
+ # .env at RUNTIME, AFTER this prologue. A conditional hatch keyed on whether the
42
+ # credential is already set would therefore be empty at guard time and open, and
43
+ # `source` under xtrace echoes EVERY assignment in the file -- so the leak would
44
+ # be the whole .env, not one token. (Written without the literal parameter
45
+ # expansion on purpose: a bare-token grep for it cannot tell this comment from
46
+ # real code, and the 4-vs-11 arm split is asserted by exactly such a grep.)
47
+ # Refusal goes to STDOUT, not stderr: agent runtimes surface stdout and swallow
48
+ # stderr (constitution.md > Code Style > Always), and a swallowed security refusal
49
+ # leaves the user with a bare `exit 78` and no text at all.
50
+ case "$-" in
51
+ *x*)
52
+ printf 'Refusing to run under `bash -x`: this script loads X_API_KEY, X_API_SECRET, X_ACCESS_TOKEN, X_ACCESS_TOKEN_SECRET from your .env at runtime, and tracing would print them to your terminal. Unsetting them first does not help -- the .env is the source. Re-run without `bash -x`.\n'
53
+ exit 78
54
+ ;;
55
+ esac
56
+
57
+ # (#7873) `--disable` closes ~/.curlrc and `--noproxy '*'` closes the proxy
58
+ # variables, but NEITHER touches the trust store or the TLS session-key log. On an
59
+ # installed CLI the environment belongs to someone who is not us, so
60
+ # a substituted `CURL_CA_BUNDLE` is a clean MITM of the founder's platform
61
+ # token with every other guard fully intact, and `SSLKEYLOGFILE` is passive
62
+ # decryption with no MITM at all. Matches the line in scripts/betterstack-query.sh.
63
+ unset SSLKEYLOGFILE CURL_CA_BUNDLE SSL_CERT_FILE SSL_CERT_DIR CURL_HOME \
64
+ HOSTALIASES LOCALDOMAIN RES_OPTIONS
65
+
66
+ # --- Transport-confinement diagnostics (#7873) --------------------------------
67
+ # Every credentialed `curl` below carries `--disable --noproxy '*'` and discards
68
+ # curl's own stderr (which can carry the URL). A failed request then has four
69
+ # competing causes the founder cannot tell apart: the xtrace refusal, the proxy
70
+ # this script deliberately bypassed, a ~/.curlrc that `--disable` dropped, or an
71
+ # ordinary network failure. So each failure emits ONE structured marker carrying
72
+ # all four discriminators, beside a human line that names the bypass instead of
73
+ # blaming the founder's connectivity.
74
+ SOLEUR_TRANSPORT_SCRIPT="x-setup.sh"
75
+ SOLEUR_TRANSPORT_PLATFORM="X"
76
+
77
+ # The request helpers below run inside `$(...)`, where fd 1 is the capture pipe.
78
+ # Save the script's real stdout so the marker reaches the founder's terminal (and
79
+ # the always-on PostToolUse Bash extractor) instead of being swallowed into a
80
+ # variable. The marker is on stdout BY DESIGN -- the call sites' `2>/dev/null` is
81
+ # what keeps curl's URL-carrying stderr out of the transcript, and it must not be
82
+ # able to suppress the diagnostic too.
83
+ exec 3>&1
84
+
85
+ # Which proxy variables are non-empty, or `none`.
86
+ proxy_env_names() {
87
+ local names="" n
88
+ for n in HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY http_proxy https_proxy all_proxy no_proxy; do
89
+ if [[ -n "${!n-}" ]]; then
90
+ names="${names},${n}"
91
+ fi
92
+ done
93
+ if [[ -z "$names" ]]; then
94
+ printf 'none'
95
+ else
96
+ printf '%s' "${names#,}"
97
+ fi
98
+ }
99
+
100
+ # True when a proxy this script deliberately bypasses is configured. NO_PROXY
101
+ # alone is not one -- it only ever narrows proxying, so it cannot be the cause.
102
+ proxy_bypassed() {
103
+ [[ -n "${HTTP_PROXY:-}${HTTPS_PROXY:-}${ALL_PROXY:-}${http_proxy:-}${https_proxy:-}${all_proxy:-}" ]]
104
+ }
105
+
106
+ # One line, eight fields, on the saved stdout.
107
+ #
108
+ # Two fields were literals in the first cut and are now MEASURED (#7898 review):
109
+ # `noproxy_applied` was the constant "true", which ASSERTS the property instead of
110
+ # observing it -- dropping --noproxy from a call site left the diagnostic still
111
+ # claiming it was applied. It now reports whether a proxy was actually configured
112
+ # for this request to bypass, which is the fact a reader needs. `refusal` was the
113
+ # constant "none" at every call site, so the cause it exists to discriminate could
114
+ # never appear; it now carries `env-rebind-refused` and `xtrace-credential-bound`.
115
+ #
116
+ # `tls_env_cleared` is new. The prologue unsets CURL_CA_BUNDLE/SSL_CERT_FILE et al,
117
+ # which is correct against an attacker and BREAKS a founder whose corporate CA
118
+ # arrives that way -- curl then exits 60 and, without this field, the event is
119
+ # indistinguishable from an ordinary network failure.
120
+ emit_transport_diag() {
121
+ local curl_exit="$1" refusal="$2" surface="installed-cli" curlrc="false" noproxy="false"
122
+ # The hosted sandbox always sets this (agent-env.ts > AGENT_ENV_OVERRIDES); an
123
+ # installed CLI does not. A label on the event, not a gate on behaviour.
124
+ if [[ -n "${CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:-}" ]]; then
125
+ surface="in-sandbox"
126
+ fi
127
+ if [[ -f "${HOME:-}/.curlrc" ]]; then
128
+ curlrc="true"
129
+ fi
130
+ if proxy_bypassed; then
131
+ noproxy="true"
132
+ fi
133
+ printf 'SOLEUR_TRANSPORT_DIAG surface=%s script=%s curl_exit=%s refusal=%s proxy_env=%s curlrc_present=%s noproxy_applied=%s tls_env_cleared=%s\n' \
134
+ "$surface" "$SOLEUR_TRANSPORT_SCRIPT" "$curl_exit" "$refusal" \
135
+ "$(proxy_env_names)" "$curlrc" "$noproxy" "true" >&3
136
+ }
137
+
138
+ # Replaces the bare "Check your network connection and try again." on a
139
+ # credentialed-curl failure. With a proxy set, that line blames the founder for a
140
+ # bypass this script chose.
141
+ report_transport_failure() {
142
+ local curl_exit="${1:-unknown}"
143
+ local detail="${2:-Failed to connect to the ${SOLEUR_TRANSPORT_PLATFORM} API.}"
144
+ emit_transport_diag "$curl_exit" "none"
145
+ echo "Error: ${detail}" >&2
146
+ # Ordered by how specific the evidence is. Each arm names something THIS script
147
+ # did, so the founder is never told to go debug their own network for a choice
148
+ # made here. The bare "check your connection" line is the LAST resort, not the
149
+ # default -- it was the default in the first cut, which meant a founder whose
150
+ # corporate CA or curlrc we had just discarded was blamed for it (#7898 review).
151
+ if [[ "$curl_exit" == "60" || "$curl_exit" == "35" || "$curl_exit" == "77" ]]; then
152
+ # 60 peer-certificate, 35 TLS handshake, 77 CA-bundle unreadable.
153
+ echo "This looks like a TLS trust failure. This script deliberately clears CURL_CA_BUNDLE, SSL_CERT_FILE, SSL_CERT_DIR and SSLKEYLOGFILE before the request, because those variables can redirect or expose a request carrying your ${SOLEUR_TRANSPORT_PLATFORM} credential." >&2
154
+ echo "If your machine needs a corporate CA to reach ${SOLEUR_TRANSPORT_PLATFORM}, that is not currently supported -- please open an issue at https://github.com/jikig-ai/soleur/issues." >&2
155
+ elif proxy_bypassed; then
156
+ echo "This request deliberately bypasses your proxy ($(proxy_env_names)), because a proxy can redirect a request carrying your ${SOLEUR_TRANSPORT_PLATFORM} token." >&2
157
+ echo "If you need Soleur to reach ${SOLEUR_TRANSPORT_PLATFORM} through your proxy, that is not currently supported -- please open an issue at https://github.com/jikig-ai/soleur/issues." >&2
158
+ elif [[ -f "${HOME:-}/.curlrc" ]]; then
159
+ echo "You have a ~/.curlrc, and this request deliberately ignores it (--disable), because a curlrc can redirect a request carrying your ${SOLEUR_TRANSPORT_PLATFORM} token. If it configures a proxy or a CA bundle you need, that is why this failed." >&2
160
+ echo "That is not currently supported -- please open an issue at https://github.com/jikig-ai/soleur/issues." >&2
161
+ else
162
+ echo "Check your network connection and try again." >&2
163
+ fi
164
+ }
165
+
166
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
167
+ source "$SCRIPT_DIR/../../../scripts/resolve-git-root.sh"
168
+ # (#9597) OAuth 1.0a request signing (HMAC-SHA1) without the key on any process's argument list.
169
+ # A function of this name inherited from the environment (an exported bash function) is dropped
170
+ # first, so it cannot stand in for the library's when the library loads without defining it
171
+ # (truncated or emptied); the check after the load refuses to run at all in that case. Only an
172
+ # inherited function NAMED hmac_sha1_b64 is closed here: the key stays readable by any other
173
+ # exported function, the same exposure as the exported secret variables.
174
+ unset -f hmac_sha1_b64 2>/dev/null || true
175
+ source "$SCRIPT_DIR/lib/hmac-sha1-b64.sh"
176
+ if ! declare -F hmac_sha1_b64 >/dev/null; then
177
+ echo "Error: the OAuth signing helper did not define hmac_sha1_b64; refusing to run." >&2
178
+ exit 1
179
+ fi
180
+
181
+ # readonly (#7898 review): `cmd_verify` and friends run `set -a; source "$env_file"; set +a`
182
+ # BELOW this line, so a plain assignment here is rebindable by the repo's .env --
183
+ # which retargets the credentialed request while every transport flag stays intact.
184
+ # That is the "--disable and --noproxy are intact and irrelevant" shape this change
185
+ # exists to close, one layer up. readonly makes the destination non-rebindable.
186
+ readonly X_API="https://api.x.com"
187
+
188
+ # (#9597) write-env value allow-list. `.env` values are written UNQUOTED and later SOURCED
189
+ # (`verify`, the community scripts), so a value holding a command substitution, a backtick, a
190
+ # quote, a semicolon, a space or a newline would execute or split on the next source, and a leading
191
+ # tilde would silently change a stored credential (tilde expansion on source). Validation is an
192
+ # allow-list, fail-closed, and runs for EVERY value BEFORE the first write: a refused value prints
193
+ # the value-free marker plus ONE human line naming only the VARIABLE, writes nothing, exits 1 and
194
+ # leaves any existing .env untouched. A value outside the list that is legitimate is added to .env
195
+ # by hand. The glob runs under LC_ALL=C and is not grep, which is line-oriented and lets a
196
+ # multi-line value through.
197
+ _wenv_class() { # <value>: 0 allowed, 1 outside the allow-list or empty, 2 holds a control character
198
+ local LC_ALL=C
199
+ case "${1-}" in
200
+ '') return 1 ;;
201
+ *[[:cntrl:]]*) return 2 ;;
202
+ *[!A-Za-z0-9._:/@%+=,-]*) return 1 ;;
203
+ esac
204
+ return 0
205
+ }
206
+ _wenv_refuse() { # <reason> <VARIABLE>
207
+ printf 'SOLEUR_CREDENTIAL_REFUSED script=%s reason=%s var=%s phase=write-env\n' "$SOLEUR_TRANSPORT_SCRIPT" "$1" "$2" >&2
208
+ echo "Error: ${2} holds a character this script does not write to .env (allowed: letters, digits and . _ : / @ % + = , -), so nothing was written and your existing .env is unchanged. The value is not shown. If the value is legitimate, add it to .env by hand."
209
+ exit 1
210
+ }
211
+ # _wenv_validate <VARIABLE>...: every NON-EMPTY named variable passes the allow-list. The required
212
+ # variables are checked non-empty by the caller before this runs; an empty optional one is skipped.
213
+ _wenv_validate() {
214
+ local _wn _wrc
215
+ for _wn in "$@"; do
216
+ [[ -n "${!_wn:-}" ]] || continue
217
+ _wrc=0
218
+ _wenv_class "${!_wn}" || _wrc=$?
219
+ case "$_wrc" in
220
+ 0) ;;
221
+ 2) _wenv_refuse control_char "$_wn" ;;
222
+ *) _wenv_refuse token_shape "$_wn" ;;
223
+ esac
224
+ done
225
+ }
226
+
227
+ # (#10091) write-env commit: ONE helper, byte-identical in the four setup scripts (a drift row pins
228
+ # it, from this comment line through the closing brace of `_wenv_write`). `_wenv_write <env_file>
229
+ # <remove-ERE> <KEY=VALUE>...` replaces the old remove-then-append, which raced (two writers each read
230
+ # the file, each rewrote it, the later mv lost the earlier's keys), was not atomic (the append hit the
231
+ # live file after the mv) and widened a new file before the chmod.
232
+ # The edit is built in a temp file `<.env>.wenv.XXXXXX` beside the RESOLVED .env (same filesystem, so the
233
+ # final `mv -f` is one atomic rename; a namespace of its own, so the sweep below touches only that
234
+ # namespace (a file there that is not a regular file this user owns is left alone), never the operator
235
+ # library's `.env.tmp.*` temps) under `umask 077` (0600 from birth), serialized by
236
+ # ONE mutual-exclusion mechanism on every platform: an owner-pid `mkdir` lock directory
237
+ # (`<.env>.lock.d`) that is held until AFTER that rename. No flock (a flock run and a mkdir run exclude
238
+ # nothing against each other, flock without a timeout blocks forever, and `exec {fd}<` needs bash 4.1
239
+ # while macOS ships 3.2).
240
+ # Stale-lock recovery is serialized as well, by a second `mkdir` mutex `<.env>.lock.d.recover`. A waiter
241
+ # judges a lock stale when its owner pid is dead (or its owner file stayed empty for _WENV_EMPTY_POLLS
242
+ # passes), then tries to take the mutex; a waiter that loses it keeps waiting. The winner RE-READS the
243
+ # owner of the lock UNDER the mutex (the snapshot it judged on is never trusted; the re-read narrows the
244
+ # window to the few instructions between the read and the removal, it does not close it), removes the lock
245
+ # only if the owner is still a dead pid, or if the snapshot AND the re-read (after a short pause) both found
246
+ # no pid, ALWAYS releases the mutex, and goes round the loop again. A lock whose owner turned from a dead pid
247
+ # into nothing has changed hands and is left alone. A recovering waiter never writes while it recovers,
248
+ # nothing is renamed away and put back, and the lock path is never empty while a live holder exists, so two
249
+ # waiters cannot both hold the lock. A SOLEUR_WENV_LOCK_TICKS below 2 never attempts recovery.
250
+ # The wait is ONE bounded loop. Every failed `mkdir` pass either sleeps one tick (0.1 s; 1 s where
251
+ # `sleep` takes no fraction) or fails at once, and every pass counts against SOLEUR_WENV_LOCK_TICKS
252
+ # (digits only, at most _WENV_TICKS_MAX, else _WENV_TICKS_DEFAULT): about 10 s by default with a
253
+ # fractional `sleep`, about 100 s without one. It FAILS CLOSED: no write, a value-free marker, exit 1,
254
+ # .env untouched. A lock path that exists but is not a directory fails at once (env_lock_not_dir). grep
255
+ # exit 1 (nothing left) is normal; exit 2 (an unreadable .env) aborts, because swallowing it would
256
+ # install an empty file over the credentials. A symlinked .env is resolved first (the real target is
257
+ # rewritten, the link survives); a dangling link or a non-regular target is refused.
258
+ # Signals: this helper owns the EXIT, INT, TERM and HUP traps for its duration and restores nothing (no
259
+ # caller installs one today; a caller that needs its own must chain them). On those four it removes its
260
+ # temp and its recovery mutex and releases the lock, and it removes the lock ONLY when the owner file
261
+ # reads exactly this process. Residual, stated: SIGKILL leaves its lock (recovered by the next run
262
+ # through the dead pid), its temp (swept by the next run, under the lock) and, if it died mid-recovery,
263
+ # `<.env>.lock.d.recover`, which the next runs report after the bounded wait ("remove that directory");
264
+ # a recycled pid can keep a dead owner looking live until the wait times out; the owner check uses
265
+ # `kill -0`, so a lock held by another user reads as dead; a holder stalled between its `mkdir` and its
266
+ # owner write for longer than the empty-owner window can be treated as ownerless and recovered.
267
+ _WENV_TICK=0.1 # seconds per wait tick (a `sleep` without fractions falls back to 1 s)
268
+ _WENV_TICKS_DEFAULT=100 # the default bound: about 10 s
269
+ _WENV_TICKS_MAX=1000 # the largest SOLEUR_WENV_LOCK_TICKS accepted: about 100 s
270
+ _WENV_EMPTY_POLLS=30 # passes an ownerless lock directory is waited on before recovery is tried: about 3 s
271
+ _WENV_EMPTY_PAUSE=0.3 # pause between the two owner reads under the recovery mutex when the owner is not a pid
272
+ _WENV_LOCK="" _WENV_TMP="" _WENV_REC=""
273
+ _wenv_unlock() { # idempotent: remove our temp and recovery mutex; release the lock ONLY if its owner file reads exactly this process
274
+ local _wo=""
275
+ if [[ -n "$_WENV_TMP" ]]; then rm -f -- "$_WENV_TMP"; _WENV_TMP=""; fi
276
+ if [[ -n "$_WENV_REC" ]]; then rmdir -- "$_WENV_REC" 2>/dev/null || true; _WENV_REC=""; fi
277
+ if [[ -n "$_WENV_LOCK" ]]; then
278
+ { read -r _wo < "$_WENV_LOCK/owner"; } 2>/dev/null || _wo=""
279
+ if [[ "$_wo" == "$$" ]]; then rm -rf -- "$_WENV_LOCK"; fi
280
+ _WENV_LOCK=""
281
+ fi
282
+ }
283
+ _wenv_fail() { # <reason> <human text>: value-free marker on stderr, one human line on stdout, exit 1
284
+ printf 'SOLEUR_CREDENTIAL_REFUSED script=%s reason=%s var=ENV_FILE phase=write-env\n' "$SOLEUR_TRANSPORT_SCRIPT" "$1" >&2
285
+ echo "Error: $2 Nothing was written and your existing .env is unchanged."
286
+ exit 1
287
+ }
288
+ _wenv_write() {
289
+ local _wf="$1" _wre="$2" _wr _wl _wm _wd _wi=0 _ws=0 _wt _wown _wo2 _wsnap="" _wstale _wgone _wwhy="" _wn _wrc _wum
290
+ shift 2
291
+ if _wr="$(readlink -f -- "$_wf" 2>/dev/null)" && [[ -n "$_wr" ]]; then
292
+ :
293
+ elif [[ -L "$_wf" ]]; then
294
+ _wenv_fail env_unresolvable "The .env link at ${_wf} could not be resolved."
295
+ else
296
+ _wr="$_wf"
297
+ fi
298
+ if [[ -L "$_wf" && ! -e "$_wr" ]]; then
299
+ _wenv_fail env_dangling_link "The .env at ${_wf} is a link to a file that does not exist."
300
+ fi
301
+ if [[ -e "$_wr" && ! -f "$_wr" ]]; then
302
+ _wenv_fail env_not_regular "The .env at ${_wf} is not a regular file."
303
+ fi
304
+ _wl="${_wr}.lock.d"
305
+ _wm="${_wl}.recover"
306
+ _wt="${SOLEUR_WENV_LOCK_TICKS:-$_WENV_TICKS_DEFAULT}"
307
+ if [[ "$_wt" =~ ^[0-9]{1,4}$ ]] && (( 10#$_wt <= _WENV_TICKS_MAX )); then _wt=$((10#$_wt)); else _wt=$_WENV_TICKS_DEFAULT; fi
308
+ _wum="$(umask)"
309
+ umask 077
310
+ trap '_wenv_unlock' EXIT
311
+ trap '_wenv_unlock; exit 130' INT
312
+ trap '_wenv_unlock; exit 143' TERM
313
+ trap '_wenv_unlock; exit 129' HUP
314
+ while :; do
315
+ if mkdir -- "$_wl" 2>/dev/null; then
316
+ _WENV_LOCK="$_wl"
317
+ if ! printf '%s\n' "$$" > "$_wl/owner"; then
318
+ rm -rf -- "$_wl"; _WENV_LOCK=""
319
+ _wenv_fail env_write_failed "The lock at ${_wl} could not be recorded."
320
+ fi
321
+ break
322
+ fi
323
+ if [[ -L "$_wl" || -f "$_wl" ]]; then
324
+ _wenv_fail env_lock_not_dir "The lock path ${_wl} exists but is not a directory; remove it."
325
+ fi
326
+ _wi=$((_wi + 1))
327
+ _wstale=0
328
+ if [[ ! -d "$_wl" ]]; then
329
+ # mkdir failed with the path absent: the lock was released in between, or mkdir fails for another
330
+ # reason (a name too long, no space): a pass that made no progress, so it sleeps below like any other.
331
+ _ws=0; _wwhy=""
332
+ _wd="${_wr%/*}"; [[ "$_wd" != "$_wr" ]] || _wd=.
333
+ [[ -d "$_wd" && -w "$_wd" ]] || _wenv_fail env_dir_unwritable "The directory of ${_wf} does not allow a lock to be created."
334
+ else
335
+ _wown=""
336
+ { read -r _wown < "$_wl/owner"; } 2>/dev/null || _wown=""
337
+ if [[ "$_wown" =~ ^[0-9]+$ ]]; then
338
+ _ws=0; _wsnap=pid
339
+ if [[ "$_wown" == "$$" ]] || ! kill -0 "$_wown" 2>/dev/null; then _wstale=1; _wwhy=stale; else _wwhy=live; fi
340
+ else
341
+ _ws=$((_ws + 1)); _wwhy=""; _wsnap=empty
342
+ if (( _ws > _WENV_EMPTY_POLLS )); then _wstale=1; fi
343
+ fi
344
+ fi
345
+ if (( _wstale == 1 && _wi < _wt )); then
346
+ if mkdir -- "$_wm" 2>/dev/null; then
347
+ _WENV_REC="$_wm"
348
+ _wo2=""
349
+ { read -r _wo2 < "$_wl/owner"; } 2>/dev/null || _wo2=""
350
+ if [[ "$_wsnap" == empty && ! "$_wo2" =~ ^[0-9]+$ ]]; then
351
+ sleep "$_WENV_EMPTY_PAUSE" 2>/dev/null || sleep 1
352
+ { read -r _wo2 < "$_wl/owner"; } 2>/dev/null || _wo2=""
353
+ fi
354
+ _wgone=0
355
+ if [[ "$_wo2" =~ ^[0-9]+$ ]]; then
356
+ if [[ "$_wo2" == "$$" ]] || ! kill -0 "$_wo2" 2>/dev/null; then rm -rf -- "$_wl"; _ws=0; _wgone=1; fi
357
+ elif [[ "$_wsnap" == empty ]]; then
358
+ rm -rf -- "$_wl"; _ws=0; _wgone=1
359
+ fi
360
+ _wenv_unlock
361
+ if (( _wgone == 1 )); then continue; fi
362
+ fi
363
+ fi
364
+ if (( _wi >= _wt )); then
365
+ if [[ "$_wwhy" != live && -d "$_wm" ]]; then _wwhy=recover; fi
366
+ case "$_wwhy" in
367
+ live) _wenv_fail env_lock_timeout "Another write-env run is still holding the lock at ${_wl}; wait for it to finish and run this again. If no other run is active, remove that directory." ;;
368
+ recover) _wenv_fail env_lock_timeout "A lock recovery is in progress or was interrupted (${_wm} exists); waited and gave up. If no other run is active, remove that directory." ;;
369
+ *) _wenv_fail env_lock_timeout "The lock at ${_wl} could not be taken or recovered; waited and gave up. If no other run is active, remove that directory." ;;
370
+ esac
371
+ fi
372
+ sleep "$_WENV_TICK" 2>/dev/null || sleep 1
373
+ done
374
+ for _wn in "${_wr}".wenv.??????; do
375
+ if [[ -f "$_wn" && ! -L "$_wn" && -O "$_wn" ]]; then rm -f -- "$_wn"; fi
376
+ done
377
+ _WENV_TMP="$(mktemp "${_wr}.wenv.XXXXXX")" || _wenv_fail env_write_failed "A temp file could not be created beside ${_wf}."
378
+ if [[ -f "$_wr" ]]; then
379
+ _wrc=0
380
+ LC_ALL=C grep -a -v -E "$_wre" "$_wr" > "$_WENV_TMP" || _wrc=$?
381
+ if (( _wrc > 1 )); then
382
+ _wenv_fail env_unreadable "The existing .env at ${_wf} could not be read."
383
+ fi
384
+ fi
385
+ printf '%s\n' "$@" >> "$_WENV_TMP" || _wenv_fail env_write_failed "The new values could not be written beside ${_wf}."
386
+ chmod 600 "$_WENV_TMP" || _wenv_fail env_write_failed "The temp file beside ${_wf} could not be restricted to mode 600."
387
+ mv -f -- "$_WENV_TMP" "$_wr" || _wenv_fail env_write_failed "The new .env could not be moved into place at ${_wf}."
388
+ _WENV_TMP=""
389
+ _wenv_unlock
390
+ trap - EXIT INT TERM HUP
391
+ umask "$_wum"
392
+ }
393
+
394
+ # --- Dependency checks ---
395
+
396
+ require_openssl() {
397
+ if ! command -v openssl &>/dev/null; then
398
+ echo "Error: openssl is required for OAuth 1.0a signing but not found." >&2
399
+ echo "Install it via your package manager (e.g., apt install openssl)." >&2
400
+ exit 1
401
+ fi
402
+ }
403
+
404
+ require_jq() {
405
+ if ! command -v jq &>/dev/null; then
406
+ echo "Error: jq is required but not installed." >&2
407
+ echo "Install it: https://jqlang.github.io/jq/download/" >&2
408
+ exit 1
409
+ fi
410
+ }
411
+
412
+ # --- Credential validation ---
413
+
414
+ require_credentials() {
415
+ local missing=()
416
+
417
+ if [[ -z "${X_API_KEY:-}" ]]; then
418
+ missing+=("X_API_KEY")
419
+ fi
420
+ if [[ -z "${X_API_SECRET:-}" ]]; then
421
+ missing+=("X_API_SECRET")
422
+ fi
423
+ if [[ -z "${X_ACCESS_TOKEN:-}" ]]; then
424
+ missing+=("X_ACCESS_TOKEN")
425
+ fi
426
+ if [[ -z "${X_ACCESS_TOKEN_SECRET:-}" ]]; then
427
+ missing+=("X_ACCESS_TOKEN_SECRET")
428
+ fi
429
+
430
+ if [[ ${#missing[@]} -gt 0 ]]; then
431
+ echo "Error: Missing X API credentials: ${missing[*]}" >&2
432
+ echo "" >&2
433
+ echo "To configure:" >&2
434
+ echo " 1. Go to https://developer.x.com/en/portal/dashboard" >&2
435
+ echo " 2. Create or select a project and app" >&2
436
+ echo " 3. Generate API Key, API Secret, Access Token, and Access Token Secret" >&2
437
+ echo " 4. Export them as environment variables, without typing a value into the command (its text is kept in the transcript and the shell history): in a terminal, read -rs X_API_KEY; export X_API_KEY, and the same for each of the other three" >&2
438
+ exit 1
439
+ fi
440
+ }
441
+
442
+ # (#9597) The OAuth 1.0a Authorization header carries the signature and the access
443
+ # token, so it rides curl's stdin config channel (`--config -`), never its argument
444
+ # list (readable by every local user in /proc/<pid>/cmdline). The channel is
445
+ # line-oriented: a CR/LF or other control character in the header would end the
446
+ # `header = "..."` directive and let the following bytes act as a new one, so a header
447
+ # holding one is refused BEFORE it is formatted into the stream. The header's own
448
+ # quotes (`oauth_token="..."`) are legitimate and are escaped by `_cfg_q`. The consumer
449
+ # secret and token secret never reach curl at all: they only key the HMAC in oauth_sign.
450
+ _hdr_ok() { local LC_ALL=C; case "${1:-}" in ''|*[[:cntrl:]]*) return 1 ;; esac; }
451
+ _cfg_q() { local v="${1-}"; v=${v//\\/\\\\}; v=${v//\"/\\\"}; printf '%s' "$v"; }
452
+
453
+ # --- OAuth 1.0a signing ---
454
+
455
+ # URL-encode a string per RFC 3986 (RFC 5849 3.6 for OAuth). Byte-wise: under LC_ALL=C each byte
456
+ # is one character, so a multi-byte UTF-8 character encodes as its bytes (%C3%A9), not as a
457
+ # code point (the `printf '%02X' "'é"` form gives E9 in a UTF-8 locale, which is not the encoding).
458
+ urlencode() {
459
+ local LC_ALL=C
460
+ local string="$1"
461
+ local encoded=""
462
+ local i c o
463
+ for (( i = 0; i < ${#string}; i++ )); do
464
+ c="${string:i:1}"
465
+ case "$c" in
466
+ [A-Za-z0-9._~-]) encoded+="$c" ;;
467
+ *)
468
+ o=$(printf '%02X' "'$c")
469
+ encoded+="%${o}"
470
+ ;;
471
+ esac
472
+ done
473
+ echo "$encoded"
474
+ }
475
+
476
+ # Generate OAuth 1.0a signature for a request
477
+ # Arguments: method url [param_key=param_value ...]
478
+ oauth_sign() {
479
+ local method="$1"
480
+ local url="$2"
481
+ shift 2
482
+
483
+ local oauth_nonce
484
+ oauth_nonce=$(openssl rand -hex 16)
485
+ local oauth_timestamp
486
+ oauth_timestamp=$(date +%s)
487
+
488
+ # Collect all parameters (OAuth + request params)
489
+ local -a params=()
490
+ params+=("oauth_consumer_key=$(urlencode "${X_API_KEY}")")
491
+ params+=("oauth_nonce=$(urlencode "${oauth_nonce}")")
492
+ params+=("oauth_signature_method=HMAC-SHA1")
493
+ params+=("oauth_timestamp=${oauth_timestamp}")
494
+ params+=("oauth_token=$(urlencode "${X_ACCESS_TOKEN}")")
495
+ params+=("oauth_version=1.0")
496
+
497
+ # Add request parameters
498
+ local param
499
+ for param in "$@"; do
500
+ local key="${param%%=*}"
501
+ local val="${param#*=}"
502
+ params+=("$(urlencode "$key")=$(urlencode "$val")")
503
+ done
504
+
505
+ # Sort parameters lexicographically
506
+ local sorted_params
507
+ sorted_params=$(printf '%s\n' "${params[@]}" | sort)
508
+
509
+ # Build parameter string
510
+ local param_string=""
511
+ while IFS= read -r line; do
512
+ if [[ -n "$param_string" ]]; then
513
+ param_string+="&"
514
+ fi
515
+ param_string+="$line"
516
+ done <<< "$sorted_params"
517
+
518
+ # Build signature base string
519
+ local base_string="${method}&$(urlencode "$url")&$(urlencode "$param_string")"
520
+
521
+ # Build signing key (read BY NAME inside the HMAC function below)
522
+ # shellcheck disable=SC2034
523
+ local signing_key="$(urlencode "${X_API_SECRET}")&$(urlencode "${X_ACCESS_TOKEN_SECRET}")"
524
+
525
+ # Generate HMAC-SHA1 signature (#9597). The signing key is handed to the function by NAME and
526
+ # lives only in bash builtins and pipes: it is on no process's argument list. A failed
527
+ # computation yields no signature and the request is never sent unsigned.
528
+ local signature
529
+ signature=$(printf '%s' "$base_string" | hmac_sha1_b64 signing_key) || signature=""
530
+ if [[ -z "$signature" ]]; then
531
+ echo "Error: could not compute the OAuth signature; refusing to send an unsigned request." >&2
532
+ return 1
533
+ fi
534
+
535
+ # Build Authorization header
536
+ local auth_header="OAuth "
537
+ auth_header+="oauth_consumer_key=\"$(urlencode "${X_API_KEY}")\", "
538
+ auth_header+="oauth_nonce=\"$(urlencode "${oauth_nonce}")\", "
539
+ auth_header+="oauth_signature=\"$(urlencode "$signature")\", "
540
+ auth_header+="oauth_signature_method=\"HMAC-SHA1\", "
541
+ auth_header+="oauth_timestamp=\"${oauth_timestamp}\", "
542
+ auth_header+="oauth_token=\"$(urlencode "${X_ACCESS_TOKEN}")\", "
543
+ auth_header+="oauth_version=\"1.0\""
544
+
545
+ echo "$auth_header"
546
+ }
547
+
548
+ # --- Commands ---
549
+
550
+ cmd_validate_credentials() {
551
+ require_credentials
552
+
553
+ local url="${X_API}/2/users/me"
554
+ local auth_header
555
+ auth_header=$(oauth_sign "GET" "$url")
556
+
557
+ local response http_code body
558
+ # Suppress stderr to prevent credential leakage
559
+ if ! _hdr_ok "$auth_header"; then
560
+ echo "Error: the OAuth Authorization header has an unexpected shape; refusing to send it." >&2
561
+ exit 1
562
+ fi
563
+ local __curl_rc=0
564
+ # The signed header rides curl's stdin config channel, never its argument list.
565
+ response=$(curl --disable --noproxy '*' -s -w "\n%{http_code}" --config - \
566
+ "${url}" 2>/dev/null \
567
+ < <(printf 'header = "Authorization: %s"\n' "$(_cfg_q "$auth_header")")) || __curl_rc=$?
568
+ if (( __curl_rc != 0 )); then
569
+ report_transport_failure "$__curl_rc" "Failed to connect to X API."
570
+ exit 1
571
+ fi
572
+
573
+ http_code=$(echo "$response" | tail -1)
574
+ body=$(echo "$response" | sed '$d')
575
+
576
+ case "$http_code" in
577
+ 2[0-9][0-9])
578
+ local user_id username name
579
+ user_id=$(echo "$body" | jq -r '.data.id // empty')
580
+ username=$(echo "$body" | jq -r '.data.username // empty')
581
+ name=$(echo "$body" | jq -r '.data.name // empty')
582
+
583
+ if [[ -z "$user_id" ]]; then
584
+ echo "Error: Could not extract user info from X API response." >&2
585
+ echo "Response: ${body}" >&2
586
+ exit 1
587
+ fi
588
+
589
+ # Output user info as JSON on stdout
590
+ echo "$body" | jq '.data'
591
+ echo "Credentials valid. Account: @${username} (${name})" >&2
592
+ ;;
593
+ 401)
594
+ echo "Error: X API returned 401 Unauthorized." >&2
595
+ echo "Your credentials may be expired or invalid." >&2
596
+ echo "" >&2
597
+ echo "To fix:" >&2
598
+ echo " 1. Go to https://developer.x.com/en/portal/dashboard" >&2
599
+ echo " 2. Regenerate your Access Token and Secret" >&2
600
+ echo " 3. Update environment variables" >&2
601
+ exit 1
602
+ ;;
603
+ 403)
604
+ echo "Error: X API returned 403 Forbidden." >&2
605
+ echo "Your app may lack the required permissions or your account may be suspended." >&2
606
+ exit 1
607
+ ;;
608
+ 429)
609
+ echo "Error: X API rate limit exceeded." >&2
610
+ echo "Wait and try again later." >&2
611
+ exit 1
612
+ ;;
613
+ *)
614
+ local message
615
+ message=$(echo "$body" | jq -r '.detail // .title // "Unknown error"' 2>/dev/null || echo "Unknown error")
616
+ echo "Error: X API returned HTTP ${http_code}: ${message}" >&2
617
+ exit 1
618
+ ;;
619
+ esac
620
+ }
621
+
622
+ cmd_write_env() {
623
+ # write-env has no stdout payload, so its missing-credentials diagnostic goes to STDOUT too
624
+ # (require_credentials writes it to stderr, shared with the other commands; exit 1 either way).
625
+ require_credentials 2>&1
626
+ # Every value is checked BEFORE the first write (see the allow-list above).
627
+ _wenv_validate X_API_KEY X_API_SECRET X_ACCESS_TOKEN X_ACCESS_TOKEN_SECRET
628
+
629
+ local repo_root="$GIT_ROOT"
630
+ local env_file="${repo_root}/.env"
631
+
632
+ # One atomic, serialized, mode-600-from-birth write (see _wenv_write above).
633
+ _wenv_write "$env_file" '^(X_API_KEY|X_API_SECRET|X_ACCESS_TOKEN|X_ACCESS_TOKEN_SECRET)=' \
634
+ "X_API_KEY=${X_API_KEY}" \
635
+ "X_API_SECRET=${X_API_SECRET}" \
636
+ "X_ACCESS_TOKEN=${X_ACCESS_TOKEN}" \
637
+ "X_ACCESS_TOKEN_SECRET=${X_ACCESS_TOKEN_SECRET}"
638
+
639
+ echo "Wrote 4 variables to ${env_file} (permissions: 600)"
640
+ }
641
+
642
+ cmd_verify() {
643
+ # Verify .env configuration by running validate-credentials
644
+ local repo_root="$GIT_ROOT"
645
+ local env_file="${repo_root}/.env"
646
+
647
+ if [[ ! -f "$env_file" ]]; then
648
+ echo "Error: .env file not found at ${env_file}" >&2
649
+ exit 1
650
+ fi
651
+
652
+ # shellcheck disable=SC1090
653
+ set -a
654
+ # The API base above is `readonly` (#7898 review), so if this .env tries to
655
+ # rebind it `source` returns non-zero. Do NOT let `set -e` abort here: that
656
+ # would be a silent death on the credential-loading path. Capture the status,
657
+ # report it, and continue -- the credentials in the same file still load, and
658
+ # the pinned destination keeps its legitimate value.
659
+ _env_rc=0
660
+ source "$env_file" || _env_rc=$?
661
+ set +a
662
+ if (( _env_rc != 0 )); then
663
+ printf 'Note: at least one assignment in %s was refused. A pinned request destination cannot be overridden from .env -- your credentials loaded normally and the request still goes to the real API.\n' "$env_file"
664
+ emit_transport_diag "none" "env-rebind-refused"
665
+ fi
666
+
667
+ local missing=()
668
+ [[ -z "${X_API_KEY:-}" ]] && missing+=("X_API_KEY")
669
+ [[ -z "${X_API_SECRET:-}" ]] && missing+=("X_API_SECRET")
670
+ [[ -z "${X_ACCESS_TOKEN:-}" ]] && missing+=("X_ACCESS_TOKEN")
671
+ [[ -z "${X_ACCESS_TOKEN_SECRET:-}" ]] && missing+=("X_ACCESS_TOKEN_SECRET")
672
+
673
+ if [[ ${#missing[@]} -gt 0 ]]; then
674
+ echo "Error: .env is missing: ${missing[*]}" >&2
675
+ exit 1
676
+ fi
677
+
678
+ # Validate via API
679
+ cmd_validate_credentials
680
+ }
681
+
682
+ # --- Main ---
683
+
684
+ main() {
685
+ local command="${1:-}"
686
+ shift || true
687
+
688
+ if [[ -z "$command" ]]; then
689
+ echo "Usage: x-setup.sh <command>" >&2
690
+ echo "" >&2
691
+ echo "Commands:" >&2
692
+ echo " validate-credentials - Verify credentials via GET /2/users/me" >&2
693
+ echo " write-env - Write credentials to .env with chmod 600" >&2
694
+ echo " verify - Source .env and run round-trip check" >&2
695
+ exit 1
696
+ fi
697
+
698
+ require_jq
699
+ require_openssl
700
+
701
+ case "$command" in
702
+ validate-credentials) cmd_validate_credentials ;;
703
+ write-env) cmd_write_env ;;
704
+ verify) cmd_verify ;;
705
+ *)
706
+ echo "Error: Unknown command '${command}'" >&2
707
+ echo "Run 'x-setup.sh' without arguments for usage." >&2
708
+ exit 1
709
+ ;;
710
+ esac
711
+ }
712
+
713
+ main "$@"