@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,890 @@
1
+ #!/usr/bin/env bash
2
+ # x-community.sh -- X/Twitter API v2 wrapper for community operations
3
+ #
4
+ # Usage: x-community.sh <command> [args]
5
+ # Commands:
6
+ # fetch-metrics - Fetch account metrics (followers, following, tweets)
7
+ # fetch-mentions [--max-results N] [--since-id ID] - Fetch recent mentions of the authenticated user
8
+ # fetch-timeline [--max N] - Fetch recent tweets (paid API)
9
+ # fetch-user-timeline <user_id> [--max N] - Fetch another user's recent tweets
10
+ # post-tweet <text> [--reply-to ID] - Post a tweet (optionally as a reply)
11
+ #
12
+ # Environment variables (required):
13
+ # X_API_KEY - API key (consumer key)
14
+ # X_API_SECRET - API secret (consumer secret)
15
+ # X_ACCESS_TOKEN - Access token
16
+ # X_ACCESS_TOKEN_SECRET - Access token secret
17
+ # X_ALLOW_POST - Set to "true" to enable posting (defense-in-depth guard)
18
+ #
19
+ # Exit codes:
20
+ # 0 - Success
21
+ # 1 - General error
22
+ # 2 - Retryable error (rate limit)
23
+ #
24
+ # Output: JSON to stdout
25
+ # Errors: Messages to stderr, exit 1
26
+
27
+ set -euo pipefail
28
+
29
+ # (#7797) Xtrace refusal -- the FIRST thing after `set …`, so nothing above it is
30
+ # traced. This script ships to installed Soleur CLIs: the terminal it runs in
31
+ # belongs to the founder, and `bash -x` would print their live X
32
+ # credentials. The `:+x` form tests non-emptiness WITHOUT expanding the value, so
33
+ # the guard cannot leak the thing it is refusing over. CONDITIONAL, and that is
34
+ # measured rather than inherited: every credential this file handles is bound
35
+ # BEFORE the prologue runs (this script's own credential check reads them from
36
+ # the environment, it never acquires one at runtime), so the guard can never be
37
+ # empty while a credential is live -- and a founder tracing with none set keeps
38
+ # full tracing.
39
+ # Refusal goes to STDOUT, not stderr: agent runtimes surface stdout and swallow
40
+ # stderr (constitution.md > Code Style > Always), and a swallowed security refusal
41
+ # leaves the user with a bare `exit 78` and no text at all.
42
+ case "$-" in
43
+ *x*)
44
+ if [ -n "${X_ACCESS_TOKEN:+x}${X_ACCESS_TOKEN_SECRET:+x}${X_API_KEY:+x}${X_API_SECRET:+x}" ]; then
45
+ printf 'Refusing to run under `bash -x`: X_ACCESS_TOKEN, X_ACCESS_TOKEN_SECRET, X_API_KEY, X_API_SECRET are set, and tracing would print them to your terminal. To trace safely, unset all of them and re-run.\n'
46
+ exit 78
47
+ fi
48
+ ;;
49
+ esac
50
+
51
+ # (#7873) `--disable` closes ~/.curlrc and `--noproxy '*'` closes the proxy
52
+ # variables, but NEITHER touches the trust store or the TLS session-key log. On an
53
+ # installed CLI the environment belongs to someone who is not us, so
54
+ # a substituted `CURL_CA_BUNDLE` is a clean MITM of the founder's platform
55
+ # token with every other guard fully intact, and `SSLKEYLOGFILE` is passive
56
+ # decryption with no MITM at all. Matches the line in scripts/betterstack-query.sh.
57
+ unset SSLKEYLOGFILE CURL_CA_BUNDLE SSL_CERT_FILE SSL_CERT_DIR CURL_HOME \
58
+ HOSTALIASES LOCALDOMAIN RES_OPTIONS
59
+
60
+ # --- Transport-confinement diagnostics (#7873) --------------------------------
61
+ # Every credentialed `curl` below carries `--disable --noproxy '*'` and discards
62
+ # curl's own stderr (which can carry the URL). A failed request then has four
63
+ # competing causes the founder cannot tell apart: the xtrace refusal, the proxy
64
+ # this script deliberately bypassed, a ~/.curlrc that `--disable` dropped, or an
65
+ # ordinary network failure. So each failure emits ONE structured marker carrying
66
+ # all four discriminators, beside a human line that names the bypass instead of
67
+ # blaming the founder's connectivity.
68
+ SOLEUR_TRANSPORT_SCRIPT="x-community.sh"
69
+ SOLEUR_TRANSPORT_PLATFORM="X"
70
+
71
+ # The request helpers below run inside `$(...)`, where fd 1 is the capture pipe.
72
+ # Save the script's real stdout so the marker reaches the founder's terminal (and
73
+ # the always-on PostToolUse Bash extractor) instead of being swallowed into a
74
+ # variable. The marker is on stdout BY DESIGN -- the call sites' `2>/dev/null` is
75
+ # what keeps curl's URL-carrying stderr out of the transcript, and it must not be
76
+ # able to suppress the diagnostic too.
77
+ exec 3>&1
78
+
79
+ # Which proxy variables are non-empty, or `none`.
80
+ proxy_env_names() {
81
+ local names="" n
82
+ for n in HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY http_proxy https_proxy all_proxy no_proxy; do
83
+ if [[ -n "${!n-}" ]]; then
84
+ names="${names},${n}"
85
+ fi
86
+ done
87
+ if [[ -z "$names" ]]; then
88
+ printf 'none'
89
+ else
90
+ printf '%s' "${names#,}"
91
+ fi
92
+ }
93
+
94
+ # True when a proxy this script deliberately bypasses is configured. NO_PROXY
95
+ # alone is not one -- it only ever narrows proxying, so it cannot be the cause.
96
+ proxy_bypassed() {
97
+ [[ -n "${HTTP_PROXY:-}${HTTPS_PROXY:-}${ALL_PROXY:-}${http_proxy:-}${https_proxy:-}${all_proxy:-}" ]]
98
+ }
99
+
100
+ # One line, eight fields, on the saved stdout.
101
+ #
102
+ # Two fields were literals in the first cut and are now MEASURED (#7898 review):
103
+ # `noproxy_applied` was the constant "true", which ASSERTS the property instead of
104
+ # observing it -- dropping --noproxy from a call site left the diagnostic still
105
+ # claiming it was applied. It now reports whether a proxy was actually configured
106
+ # for this request to bypass, which is the fact a reader needs. `refusal` was the
107
+ # constant "none" at every call site, so the cause it exists to discriminate could
108
+ # never appear; it now carries `env-rebind-refused` and `xtrace-credential-bound`.
109
+ #
110
+ # `tls_env_cleared` is new. The prologue unsets CURL_CA_BUNDLE/SSL_CERT_FILE et al,
111
+ # which is correct against an attacker and BREAKS a founder whose corporate CA
112
+ # arrives that way -- curl then exits 60 and, without this field, the event is
113
+ # indistinguishable from an ordinary network failure.
114
+ emit_transport_diag() {
115
+ local curl_exit="$1" refusal="$2" surface="installed-cli" curlrc="false" noproxy="false"
116
+ # The hosted sandbox always sets this (agent-env.ts > AGENT_ENV_OVERRIDES); an
117
+ # installed CLI does not. A label on the event, not a gate on behaviour.
118
+ if [[ -n "${CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:-}" ]]; then
119
+ surface="in-sandbox"
120
+ fi
121
+ if [[ -f "${HOME:-}/.curlrc" ]]; then
122
+ curlrc="true"
123
+ fi
124
+ if proxy_bypassed; then
125
+ noproxy="true"
126
+ fi
127
+ 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' \
128
+ "$surface" "$SOLEUR_TRANSPORT_SCRIPT" "$curl_exit" "$refusal" \
129
+ "$(proxy_env_names)" "$curlrc" "$noproxy" "true" >&3
130
+ }
131
+
132
+ # Replaces the bare "Check your network connection and try again." on a
133
+ # credentialed-curl failure. With a proxy set, that line blames the founder for a
134
+ # bypass this script chose.
135
+ report_transport_failure() {
136
+ local curl_exit="${1:-unknown}"
137
+ local detail="${2:-Failed to connect to the ${SOLEUR_TRANSPORT_PLATFORM} API.}"
138
+ emit_transport_diag "$curl_exit" "none"
139
+ echo "Error: ${detail}" >&2
140
+ # Ordered by how specific the evidence is. Each arm names something THIS script
141
+ # did, so the founder is never told to go debug their own network for a choice
142
+ # made here. The bare "check your connection" line is the LAST resort, not the
143
+ # default -- it was the default in the first cut, which meant a founder whose
144
+ # corporate CA or curlrc we had just discarded was blamed for it (#7898 review).
145
+ if [[ "$curl_exit" == "60" || "$curl_exit" == "35" || "$curl_exit" == "77" ]]; then
146
+ # 60 peer-certificate, 35 TLS handshake, 77 CA-bundle unreadable.
147
+ 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
148
+ 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
149
+ elif proxy_bypassed; then
150
+ echo "This request deliberately bypasses your proxy ($(proxy_env_names)), because a proxy can redirect a request carrying your ${SOLEUR_TRANSPORT_PLATFORM} token." >&2
151
+ 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
152
+ elif [[ -f "${HOME:-}/.curlrc" ]]; then
153
+ 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
154
+ echo "That is not currently supported -- please open an issue at https://github.com/jikig-ai/soleur/issues." >&2
155
+ else
156
+ echo "Check your network connection and try again." >&2
157
+ fi
158
+ }
159
+
160
+ # readonly (#7898 review): `cmd_verify` and friends run `set -a; source "$env_file"; set +a`
161
+ # BELOW this line, so a plain assignment here is rebindable by the repo's .env --
162
+ # which retargets the credentialed request while every transport flag stays intact.
163
+ # That is the "--disable and --noproxy are intact and irrelevant" shape this change
164
+ # exists to close, one layer up. readonly makes the destination non-rebindable.
165
+ readonly X_API="https://api.x.com"
166
+
167
+ # (#9597) OAuth 1.0a request signing (HMAC-SHA1) without the key on any process's argument list.
168
+ # The helper library is sourced LAZILY, inside oauth_sign at each signature, not here: it
169
+ # refuses `bash -x` unconditionally at load time (it names its key indirectly, so it has no
170
+ # literal credential name to test), and loading it up here would make `bash -x x-community.sh`
171
+ # with no credentials set exit 78 instead of printing usage -- the case this file's conditional
172
+ # prologue above promises to keep working.
173
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
174
+
175
+ # --- Dependency checks ---
176
+
177
+ require_openssl() {
178
+ if ! command -v openssl &>/dev/null; then
179
+ echo "Error: openssl is required for OAuth 1.0a signing but not found." >&2
180
+ echo "Install it via your package manager (e.g., apt install openssl)." >&2
181
+ exit 1
182
+ fi
183
+ }
184
+
185
+ require_jq() {
186
+ if ! command -v jq &>/dev/null; then
187
+ echo "Error: jq is required but not installed." >&2
188
+ echo "Install it: https://jqlang.github.io/jq/download/" >&2
189
+ exit 1
190
+ fi
191
+ }
192
+
193
+ # --- Credential validation ---
194
+
195
+ require_credentials() {
196
+ local missing=()
197
+
198
+ if [[ -z "${X_API_KEY:-}" ]]; then
199
+ missing+=("X_API_KEY")
200
+ fi
201
+ if [[ -z "${X_API_SECRET:-}" ]]; then
202
+ missing+=("X_API_SECRET")
203
+ fi
204
+ if [[ -z "${X_ACCESS_TOKEN:-}" ]]; then
205
+ missing+=("X_ACCESS_TOKEN")
206
+ fi
207
+ if [[ -z "${X_ACCESS_TOKEN_SECRET:-}" ]]; then
208
+ missing+=("X_ACCESS_TOKEN_SECRET")
209
+ fi
210
+
211
+ if [[ ${#missing[@]} -gt 0 ]]; then
212
+ echo "Error: Missing X API credentials: ${missing[*]}" >&2
213
+ echo "" >&2
214
+ echo "To configure:" >&2
215
+ echo " 1. Go to https://developer.x.com/en/portal/dashboard" >&2
216
+ echo " 2. Create or select a project and app" >&2
217
+ echo " 3. Generate API Key, API Secret, Access Token, and Access Token Secret" >&2
218
+ 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
219
+ exit 1
220
+ fi
221
+ }
222
+
223
+ # (#9597) The OAuth 1.0a Authorization header carries the signature and the access
224
+ # token, so it rides curl's stdin config channel (`--config -`), never its argument
225
+ # list (readable by every local user in /proc/<pid>/cmdline). The channel is
226
+ # line-oriented: a CR/LF or other control character in the header would end the
227
+ # `header = "..."` directive and let the following bytes act as a new one, so a header
228
+ # holding one is refused BEFORE it is formatted into the stream. The header's own
229
+ # quotes (`oauth_token="..."`) are legitimate and are escaped by `_cfg_q`. The consumer
230
+ # secret and token secret never reach curl at all: they only key the HMAC in oauth_sign.
231
+ _hdr_ok() { local LC_ALL=C; case "${1:-}" in ''|*[[:cntrl:]]*) return 1 ;; esac; }
232
+ _cfg_q() { local v="${1-}"; v=${v//\\/\\\\}; v=${v//\"/\\\"}; printf '%s' "$v"; }
233
+
234
+ # --- OAuth 1.0a signing ---
235
+
236
+ # URL-encode a string per RFC 3986 (RFC 5849 3.6 for OAuth). Byte-wise: under LC_ALL=C each byte
237
+ # is one character, so a multi-byte UTF-8 character encodes as its bytes (%C3%A9), not as a
238
+ # code point (the `printf '%02X' "'é"` form gives E9 in a UTF-8 locale, which is not the encoding).
239
+ urlencode() {
240
+ local LC_ALL=C
241
+ local string="$1"
242
+ local encoded=""
243
+ local i c o
244
+ for (( i = 0; i < ${#string}; i++ )); do
245
+ c="${string:i:1}"
246
+ case "$c" in
247
+ [A-Za-z0-9._~-]) encoded+="$c" ;;
248
+ *)
249
+ o=$(printf '%02X' "'$c")
250
+ encoded+="%${o}"
251
+ ;;
252
+ esac
253
+ done
254
+ echo "$encoded"
255
+ }
256
+
257
+ # Generate OAuth 1.0a signature for a request
258
+ # Arguments: method url [param_key=param_value ...]
259
+ oauth_sign() {
260
+ local method="$1"
261
+ local url="$2"
262
+ shift 2
263
+
264
+ local oauth_nonce
265
+ oauth_nonce=$(openssl rand -hex 16)
266
+ local oauth_timestamp
267
+ oauth_timestamp=$(date +%s)
268
+
269
+ # Collect all parameters (OAuth + request params)
270
+ local -a params=()
271
+ params+=("oauth_consumer_key=$(urlencode "${X_API_KEY}")")
272
+ params+=("oauth_nonce=$(urlencode "${oauth_nonce}")")
273
+ params+=("oauth_signature_method=HMAC-SHA1")
274
+ params+=("oauth_timestamp=${oauth_timestamp}")
275
+ params+=("oauth_token=$(urlencode "${X_ACCESS_TOKEN}")")
276
+ params+=("oauth_version=1.0")
277
+
278
+ # Add request parameters
279
+ local param
280
+ for param in "$@"; do
281
+ local key="${param%%=*}"
282
+ local val="${param#*=}"
283
+ params+=("$(urlencode "$key")=$(urlencode "$val")")
284
+ done
285
+
286
+ # Sort parameters lexicographically
287
+ local sorted_params
288
+ sorted_params=$(printf '%s\n' "${params[@]}" | sort)
289
+
290
+ # Build parameter string
291
+ local param_string=""
292
+ while IFS= read -r line; do
293
+ if [[ -n "$param_string" ]]; then
294
+ param_string+="&"
295
+ fi
296
+ param_string+="$line"
297
+ done <<< "$sorted_params"
298
+
299
+ # Build signature base string
300
+ local base_string="${method}&$(urlencode "$url")&$(urlencode "$param_string")"
301
+
302
+ # Build signing key (read BY NAME inside the HMAC function below)
303
+ # shellcheck disable=SC2034
304
+ local signing_key="$(urlencode "${X_API_SECRET}")&$(urlencode "${X_ACCESS_TOKEN_SECRET}")"
305
+
306
+ # Generate HMAC-SHA1 signature (#9597). The signing key is handed to the function by NAME and
307
+ # lives only in bash builtins and pipes: it is on no process's argument list. A failed
308
+ # computation yields no signature and the request is never sent unsigned.
309
+ # oauth_sign runs inside `$(...)`, so the load's stdout (the library's xtrace refusal text) goes
310
+ # to the saved real stdout (fd 3), where the founder sees it, not into the captured header.
311
+ # The load happens at EACH signature, not once: this function body is a command-substitution
312
+ # subshell, so a function defined here dies with it and nothing is cached for the next call (the
313
+ # library is one small file). Any function of this name inherited from the environment (an
314
+ # exported bash function) is dropped first, so an inherited function named hmac_sha1_b64 cannot
315
+ # stand in for the library's. That is all this closes: the key stays readable by any other
316
+ # exported function (one named od, tr, openssl, base64 or cat still receives it on its stdin),
317
+ # which is the same exposure as the exported secret variables themselves.
318
+ unset -f hmac_sha1_b64 2>/dev/null || true
319
+ source "$SCRIPT_DIR/lib/hmac-sha1-b64.sh" >&3 || {
320
+ echo "Error: could not load the OAuth signing helper; refusing to send an unsigned request." >&2
321
+ return 1
322
+ }
323
+ local signature
324
+ signature=$(printf '%s' "$base_string" | hmac_sha1_b64 signing_key) || signature=""
325
+ if [[ -z "$signature" ]]; then
326
+ echo "Error: could not compute the OAuth signature; refusing to send an unsigned request." >&2
327
+ return 1
328
+ fi
329
+
330
+ # Build Authorization header
331
+ local auth_header="OAuth "
332
+ auth_header+="oauth_consumer_key=\"$(urlencode "${X_API_KEY}")\", "
333
+ auth_header+="oauth_nonce=\"$(urlencode "${oauth_nonce}")\", "
334
+ auth_header+="oauth_signature=\"$(urlencode "$signature")\", "
335
+ auth_header+="oauth_signature_method=\"HMAC-SHA1\", "
336
+ auth_header+="oauth_timestamp=\"${oauth_timestamp}\", "
337
+ auth_header+="oauth_token=\"$(urlencode "${X_ACCESS_TOKEN}")\", "
338
+ auth_header+="oauth_version=\"1.0\""
339
+
340
+ echo "$auth_header"
341
+ }
342
+
343
+ # --- Response handler ---
344
+
345
+ # Handle HTTP response status codes from X API
346
+ # Arguments: http_code body endpoint depth retry_cmd...
347
+ # On 2xx: validates JSON, echoes body to stdout
348
+ # On 429: sleeps and invokes retry_cmd (caller with incremented depth)
349
+ # On error: prints diagnostic to stderr, exits 1 (or 2 for rate limit exhaustion)
350
+ handle_response() {
351
+ local http_code="$1"
352
+ local body="$2"
353
+ local endpoint="$3"
354
+ local depth="$4"
355
+ shift 4
356
+ local -a retry_cmd=("$@")
357
+
358
+ case "$http_code" in
359
+ 2[0-9][0-9])
360
+ if ! echo "$body" | jq . >/dev/null 2>&1; then
361
+ echo "Error: X API returned malformed JSON for ${endpoint}" >&2
362
+ exit 1
363
+ fi
364
+ echo "$body"
365
+ ;;
366
+ 401)
367
+ echo "Error: X API returned 401 Unauthorized for ${endpoint}." >&2
368
+ echo "Your credentials may be expired or invalid." >&2
369
+ echo "" >&2
370
+ echo "To fix:" >&2
371
+ echo " 1. Go to https://developer.x.com/en/portal/dashboard" >&2
372
+ echo " 2. Regenerate your Access Token and Secret" >&2
373
+ echo " 3. Update environment variables" >&2
374
+ exit 1
375
+ ;;
376
+ 403)
377
+ local reason
378
+ reason=$(echo "$body" | jq -r '.reason // "unknown"' 2>/dev/null || echo "unknown")
379
+ echo "Error: X API returned 403 Forbidden for ${endpoint}." >&2
380
+ if [[ "$reason" == "client-not-enrolled" ]]; then
381
+ # CONTRACT: community-manager agent matches on this string for 403 fallback detection.
382
+ # Changing this message requires updating agents/support/community-manager.md Capability 4 Step 1b.
383
+ echo "This endpoint requires paid API access." >&2
384
+ echo "Visit https://developer.x.com to purchase credits." >&2
385
+ elif [[ "$reason" == "official-client-forbidden" ]]; then
386
+ echo "Your app may lack the required permissions." >&2
387
+ else
388
+ echo "Your app may lack the required permissions or your account may be suspended." >&2
389
+ fi
390
+ exit 1
391
+ ;;
392
+ 429)
393
+ local retry_after
394
+ retry_after=$(echo "$body" | jq -r '.retry_after // 5' 2>/dev/null || echo "5")
395
+ # Clamp retry_after to sane range [1, 60]
396
+ # Use printf to truncate float to integer for arithmetic comparison
397
+ # (sleep accepts floats natively, but bash (( )) does not)
398
+ local retry_int
399
+ retry_int=$(printf '%.0f' "$retry_after" 2>/dev/null || echo "5")
400
+ if (( retry_int > 60 )); then
401
+ retry_after=60
402
+ elif (( retry_int < 1 )); then
403
+ retry_after=1
404
+ fi
405
+ echo "Rate limited. Retrying after ${retry_after}s (attempt $((depth + 1))/3)..." >&2
406
+ sleep "$retry_after"
407
+ "${retry_cmd[@]}"
408
+ ;;
409
+ *)
410
+ local message
411
+ message=$(echo "$body" | jq -r '.detail // .title // "Unknown error"' 2>/dev/null || echo "Unknown error")
412
+ echo "Error: X API returned HTTP ${http_code} for ${endpoint}: ${message}" >&2
413
+ exit 1
414
+ ;;
415
+ esac
416
+ }
417
+
418
+ # --- POST request helper ---
419
+
420
+ # Make an authenticated POST request to the X API
421
+ # Arguments: endpoint [json_body] [depth]
422
+ # Retries on 429 up to 3 times
423
+ post_request() {
424
+ local endpoint="$1"
425
+ local json_body="${2:-}"
426
+ local depth="${3:-0}"
427
+
428
+ if (( depth >= 3 )); then
429
+ echo "Error: X API rate limit exceeded after 3 retries for ${endpoint}." >&2
430
+ exit 2
431
+ fi
432
+
433
+ local url="${X_API}${endpoint}"
434
+ local auth_header
435
+ # `|| exit 1`: a failed signature (it has already said why) stops here. These helpers run inside
436
+ # `$(...)`, where errexit does not apply, so without it the empty header would fall through to the
437
+ # shape check below and print a second, misleading "unexpected shape" line.
438
+ auth_header=$(oauth_sign "POST" "$url") || exit 1
439
+
440
+ local -a curl_args=(
441
+ -s -w "\n%{http_code}"
442
+ --config -
443
+ -H "Content-Type: application/json"
444
+ )
445
+
446
+ if [[ -n "$json_body" ]]; then
447
+ curl_args+=(-X POST -d "$json_body")
448
+ fi
449
+
450
+ if ! _hdr_ok "$auth_header"; then
451
+ echo "Error: the OAuth Authorization header has an unexpected shape; refusing to send it." >&2
452
+ exit 1
453
+ fi
454
+
455
+ local response http_code body
456
+ # Suppress stderr to prevent credential leakage. The signed header rides curl's stdin
457
+ # config channel, never its argument list.
458
+ local __curl_rc=0
459
+ response=$(curl --disable --noproxy '*' "${curl_args[@]}" "$url" 2>/dev/null \
460
+ < <(printf 'header = "Authorization: %s"\n' "$(_cfg_q "$auth_header")")) || __curl_rc=$?
461
+ if (( __curl_rc != 0 )); then
462
+ report_transport_failure "$__curl_rc" "Failed to connect to X API."
463
+ exit 1
464
+ fi
465
+
466
+ http_code=$(echo "$response" | tail -1)
467
+ body=$(echo "$response" | sed '$d')
468
+
469
+ handle_response "$http_code" "$body" "$endpoint" "$depth" \
470
+ post_request "$endpoint" "$json_body" "$((depth + 1))"
471
+ }
472
+
473
+ # --- GET request helper ---
474
+
475
+ # Make an authenticated GET request to the X API with query params
476
+ # Arguments: endpoint query_params [depth]
477
+ # Query params are included in the OAuth signature
478
+ # Retries on 429 up to 3 times
479
+ get_request() {
480
+ local endpoint="$1"
481
+ local query_params="${2:-}"
482
+ local depth="${3:-0}"
483
+
484
+ if (( depth >= 3 )); then
485
+ echo "Error: X API rate limit exceeded after 3 retries for ${endpoint}." >&2
486
+ exit 2
487
+ fi
488
+
489
+ local url="${X_API}${endpoint}"
490
+
491
+ # Build OAuth signature with query params included
492
+ local auth_header
493
+ if [[ -n "$query_params" ]]; then
494
+ # Split query_params on & and pass as varargs to oauth_sign
495
+ local -a param_args=()
496
+ local param
497
+ while IFS= read -r param; do
498
+ [[ -n "$param" ]] && param_args+=("$param")
499
+ done <<< "${query_params//&/$'\n'}"
500
+ auth_header=$(oauth_sign "GET" "$url" "${param_args[@]}") || exit 1
501
+ else
502
+ auth_header=$(oauth_sign "GET" "$url") || exit 1
503
+ fi
504
+
505
+ # Build request URL with query string
506
+ local request_url="$url"
507
+ if [[ -n "$query_params" ]]; then
508
+ request_url="${url}?${query_params}"
509
+ fi
510
+
511
+ local response http_code body
512
+ if ! _hdr_ok "$auth_header"; then
513
+ echo "Error: the OAuth Authorization header has an unexpected shape; refusing to send it." >&2
514
+ exit 1
515
+ fi
516
+ local __curl_rc=0
517
+ # The signed header rides curl's stdin config channel, never its argument list.
518
+ response=$(curl --disable --noproxy '*' -s -w "\n%{http_code}" --config - \
519
+ "$request_url" 2>/dev/null \
520
+ < <(printf 'header = "Authorization: %s"\n' "$(_cfg_q "$auth_header")")) || __curl_rc=$?
521
+ if (( __curl_rc != 0 )); then
522
+ report_transport_failure "$__curl_rc" "Failed to connect to X API."
523
+ exit 1
524
+ fi
525
+
526
+ http_code=$(echo "$response" | tail -1)
527
+ body=$(echo "$response" | sed '$d')
528
+
529
+ handle_response "$http_code" "$body" "$endpoint" "$depth" \
530
+ get_request "$endpoint" "$query_params" "$((depth + 1))"
531
+ }
532
+
533
+ # Resolve the authenticated user's numeric ID
534
+ resolve_user_id() {
535
+ local response
536
+ response=$(get_request "/2/users/me" "")
537
+
538
+ local user_id
539
+ user_id=$(echo "$response" | jq -r '.data.id' 2>/dev/null || echo "")
540
+
541
+ if [[ ! "$user_id" =~ ^[0-9]+$ ]]; then
542
+ echo "Error: Failed to resolve user ID from /2/users/me." >&2
543
+ echo "Response did not contain a valid numeric ID." >&2
544
+ exit 1
545
+ fi
546
+
547
+ echo "$user_id"
548
+ }
549
+
550
+ # --- Shared helpers ---
551
+
552
+ # Fetch tweets for a given user ID and return the data array.
553
+ # Arguments: user_id max_results
554
+ _fetch_tweets_for_user() {
555
+ local user_id="$1"
556
+ local max_results="$2"
557
+ local query_params="tweet.fields=created_at,public_metrics,text&max_results=${max_results}"
558
+ local body
559
+ body=$(get_request "/2/users/${user_id}/tweets" "$query_params")
560
+ echo "$body" | jq '.data // []'
561
+ }
562
+
563
+ # --- Anomaly detection ---
564
+
565
+ # Detect anomalous public_metrics patterns and emit warnings to stderr.
566
+ # Shell boolean convention: returns 0 (true) if anomaly exists, 1 (false) if normal.
567
+ # Arguments: metrics_json (JSON object with followers_count, following_count, tweet_count)
568
+ _has_metrics_anomaly() {
569
+ local metrics_json="$1"
570
+
571
+ local followers following tweets
572
+ read -r followers following tweets < <(
573
+ echo "$metrics_json" | jq -r '[.followers_count // 0, .following_count // 0, .tweet_count // 0] | @tsv'
574
+ )
575
+
576
+ # Fail safe on non-numeric jq output (malformed API response)
577
+ if [[ ! "$followers" =~ ^[0-9]+$ ]] || [[ ! "$following" =~ ^[0-9]+$ ]] || [[ ! "$tweets" =~ ^[0-9]+$ ]]; then
578
+ return 1
579
+ fi
580
+
581
+ # Genuinely new/empty account: all zeros is not anomalous
582
+ if (( followers == 0 && following == 0 && tweets == 0 )); then
583
+ return 1
584
+ fi
585
+
586
+ # All-zeros degradation: followers=0, following=0, but tweets>0
587
+ if (( followers == 0 && following == 0 && tweets > 0 )); then
588
+ echo "Warning: X API returned all-zero social metrics for account with ${tweets} tweets. Possible API degradation." | head -c 200 >&2
589
+ return 0
590
+ fi
591
+
592
+ # Active account with zero followers: followers=0 but tweets>0 and following>0
593
+ if (( followers == 0 && tweets > 0 && following > 0 )); then
594
+ echo "Warning: X API returned 0 followers for active account (${tweets} tweets, ${following} following). This may indicate unfollows, spam cleanup, or API degradation." | head -c 200 >&2
595
+ return 0
596
+ fi
597
+
598
+ return 1
599
+ }
600
+
601
+ # --- Commands ---
602
+
603
+ cmd_fetch_metrics() {
604
+ local body
605
+ body=$(get_request "/2/users/me" "user.fields=public_metrics,description,created_at")
606
+
607
+ local output metrics_json
608
+ output=$(echo "$body" | jq '{
609
+ username: .data.username,
610
+ name: .data.name,
611
+ description: .data.description,
612
+ created_at: .data.created_at,
613
+ metrics: .data.public_metrics
614
+ }')
615
+ metrics_json=$(echo "$output" | jq '.metrics // {}')
616
+
617
+ # Run anomaly check (warnings go to stderr, stdout unchanged)
618
+ _has_metrics_anomaly "$metrics_json" || true
619
+
620
+ echo "$output"
621
+ }
622
+
623
+ cmd_fetch_mentions() {
624
+ local max_results=10
625
+ local since_id=""
626
+
627
+ # Parse optional arguments
628
+ while [[ $# -gt 0 ]]; do
629
+ case "${1:-}" in
630
+ --max-results)
631
+ local mr_val="${2:-}"
632
+ if [[ -z "$mr_val" ]]; then
633
+ echo "Error: --max-results requires a numeric value." >&2
634
+ exit 1
635
+ fi
636
+ if ! [[ "$mr_val" =~ ^[0-9]+$ ]]; then
637
+ echo "Error: --max-results must be a numeric value." >&2
638
+ exit 1
639
+ fi
640
+ if (( mr_val < 5 || mr_val > 100 )); then
641
+ echo "Error: --max-results must be between 5 and 100." >&2
642
+ exit 1
643
+ fi
644
+ max_results="$mr_val"
645
+ shift 2
646
+ ;;
647
+ --since-id)
648
+ local si_val="${2:-}"
649
+ if [[ -z "$si_val" ]]; then
650
+ echo "Error: --since-id requires a numeric value." >&2
651
+ exit 1
652
+ fi
653
+ if ! [[ "$si_val" =~ ^[0-9]+$ ]]; then
654
+ echo "Error: --since-id must be a numeric value." >&2
655
+ exit 1
656
+ fi
657
+ since_id="$si_val"
658
+ shift 2
659
+ ;;
660
+ *)
661
+ echo "Error: Unknown option." >&2
662
+ echo "Usage: x-community.sh fetch-mentions [--max-results N] [--since-id ID]" >&2
663
+ exit 1
664
+ ;;
665
+ esac
666
+ done
667
+
668
+ # Resolve authenticated user ID
669
+ local user_id
670
+ user_id=$(resolve_user_id)
671
+
672
+ # Build query parameters
673
+ local query_params="max_results=${max_results}&tweet.fields=author_id,created_at,conversation_id,referenced_tweets&expansions=author_id&user.fields=username,name,profile_image_url,public_metrics"
674
+ if [[ -n "$since_id" ]]; then
675
+ query_params="${query_params}&since_id=${since_id}"
676
+ fi
677
+
678
+ # Use shared GET helper with retry and enhanced error handling
679
+ local body
680
+ body=$(get_request "/2/users/${user_id}/mentions" "$query_params")
681
+
682
+ # Handle empty data (no mentions)
683
+ local data_count
684
+ data_count=$(echo "$body" | jq '.data | length // 0' 2>/dev/null || echo "0")
685
+ if [[ "$data_count" == "0" ]] || [[ "$data_count" == "null" ]]; then
686
+ echo '{"mentions":[],"meta":{"newest_id":null,"result_count":0}}'
687
+ return 0
688
+ fi
689
+
690
+ # Transform: join includes.users to data by author_id
691
+ # Use INDEX to build a lookup map so tweets without a matching user
692
+ # are preserved with "unknown" fallbacks (not silently dropped)
693
+ echo "$body" | jq '
694
+ ((.includes.users // []) | INDEX(.id)) as $users |
695
+ {
696
+ mentions: [
697
+ .data[] |
698
+ ($users[.author_id] // {}) as $user |
699
+ {
700
+ id: .id,
701
+ text: .text,
702
+ author_id: .author_id,
703
+ author_username: ($user.username // "unknown"),
704
+ author_name: ($user.name // "unknown"),
705
+ author_profile_image_url: ($user.profile_image_url // null),
706
+ author_followers_count: ($user.public_metrics.followers_count // 0),
707
+ created_at: .created_at,
708
+ conversation_id: .conversation_id,
709
+ referenced_tweets: (.referenced_tweets // null)
710
+ }
711
+ ],
712
+ meta: {
713
+ newest_id: (.meta.newest_id // null),
714
+ result_count: (.meta.result_count // 0)
715
+ }
716
+ }'
717
+ }
718
+
719
+ cmd_fetch_timeline() {
720
+ local max_results=10
721
+
722
+ while [[ $# -gt 0 ]]; do
723
+ case "$1" in
724
+ --max)
725
+ max_results="${2:?--max requires a number}"
726
+ shift 2
727
+ ;;
728
+ *)
729
+ echo "Error: Unknown option." >&2
730
+ exit 1
731
+ ;;
732
+ esac
733
+ done
734
+
735
+ # Validate --max is a positive integer (prevents query param injection)
736
+ if [[ ! "$max_results" =~ ^[0-9]+$ ]]; then
737
+ echo "Error: --max must be a positive integer." >&2
738
+ exit 1
739
+ fi
740
+
741
+ # Clamp max_results to API range 5-100
742
+ if (( max_results < 5 )); then
743
+ echo "Warning: --max ${max_results} is below API minimum of 5, clamping to 5." >&2
744
+ max_results=5
745
+ elif (( max_results > 100 )); then
746
+ echo "Warning: --max ${max_results} is above API maximum of 100, clamping to 100." >&2
747
+ max_results=100
748
+ fi
749
+
750
+ local user_id
751
+ user_id=$(resolve_user_id)
752
+
753
+ _fetch_tweets_for_user "$user_id" "$max_results"
754
+ }
755
+
756
+ cmd_fetch_user_timeline() {
757
+ local user_id="${1:-}"
758
+ shift || true
759
+
760
+ if [[ -z "$user_id" ]]; then
761
+ echo "Error: fetch-user-timeline requires a user_id argument." >&2
762
+ echo "Usage: x-community.sh fetch-user-timeline <user_id> [--max N]" >&2
763
+ exit 1
764
+ fi
765
+
766
+ # Validate user_id is a positive integer (prevents path traversal)
767
+ if [[ ! "$user_id" =~ ^[0-9]+$ ]]; then
768
+ echo "Error: user_id must be a positive integer." >&2
769
+ exit 1
770
+ fi
771
+
772
+ local max_results=5
773
+
774
+ while [[ $# -gt 0 ]]; do
775
+ case "$1" in
776
+ --max)
777
+ max_results="${2:?--max requires a number}"
778
+ shift 2
779
+ ;;
780
+ *)
781
+ echo "Error: Unknown option." >&2
782
+ exit 1
783
+ ;;
784
+ esac
785
+ done
786
+
787
+ # Validate --max is a positive integer
788
+ if [[ ! "$max_results" =~ ^[0-9]+$ ]]; then
789
+ echo "Error: --max must be a positive integer." >&2
790
+ exit 1
791
+ fi
792
+
793
+ # Clamp max_results to API range 5-100
794
+ if (( max_results < 5 )); then
795
+ echo "Warning: --max ${max_results} is below API minimum of 5, clamping to 5." >&2
796
+ max_results=5
797
+ elif (( max_results > 100 )); then
798
+ echo "Warning: --max ${max_results} is above API maximum of 100, clamping to 100." >&2
799
+ max_results=100
800
+ fi
801
+
802
+ _fetch_tweets_for_user "$user_id" "$max_results"
803
+ }
804
+
805
+ cmd_post_tweet() {
806
+ # Guard: require explicit opt-in to post.
807
+ if [[ "${X_ALLOW_POST:-}" != "true" ]]; then
808
+ echo "Error: X_ALLOW_POST is not set to 'true'." >&2
809
+ echo "Set X_ALLOW_POST=true to enable posting." >&2
810
+ return 1
811
+ fi
812
+
813
+ local text="${1:?Usage: x-community.sh post-tweet <text> [--reply-to TWEET_ID]}"
814
+ shift
815
+
816
+ local reply_to=""
817
+ while [[ $# -gt 0 ]]; do
818
+ case "$1" in
819
+ --reply-to)
820
+ reply_to="${2:?--reply-to requires a tweet ID}"
821
+ shift 2
822
+ ;;
823
+ *)
824
+ echo "Error: Unknown option." >&2
825
+ exit 1
826
+ ;;
827
+ esac
828
+ done
829
+
830
+ # Build JSON body
831
+ local json_body
832
+ if [[ -n "$reply_to" ]]; then
833
+ json_body=$(jq -n --arg text "$text" --arg reply "$reply_to" \
834
+ '{text: $text, reply: {in_reply_to_tweet_id: $reply}}')
835
+ else
836
+ json_body=$(jq -n --arg text "$text" '{text: $text}')
837
+ fi
838
+
839
+ local result
840
+ result=$(post_request "/2/tweets" "$json_body")
841
+
842
+ # Output the created tweet
843
+ echo "$result" | jq '{
844
+ id: .data.id,
845
+ text: .data.text
846
+ }'
847
+
848
+ echo "Tweet posted successfully." >&2
849
+ }
850
+
851
+ # --- Main ---
852
+
853
+ main() {
854
+ local command="${1:-}"
855
+ shift || true
856
+
857
+ if [[ -z "$command" ]]; then
858
+ echo "Usage: x-community.sh <command> [args]" >&2
859
+ echo "" >&2
860
+ echo "Commands:" >&2
861
+ echo " fetch-metrics - Fetch account metrics" >&2
862
+ echo " fetch-mentions [--max-results N] [--since-id ID] - Fetch recent mentions" >&2
863
+ echo " fetch-timeline [--max N] - Fetch recent tweets (paid API)" >&2
864
+ echo " fetch-user-timeline <user_id> [--max N] - Fetch another user's tweets" >&2
865
+ echo " post-tweet <text> [--reply-to ID] - Post a tweet" >&2
866
+ exit 1
867
+ fi
868
+
869
+ require_jq
870
+ require_openssl
871
+ require_credentials
872
+
873
+ case "$command" in
874
+ fetch-metrics) cmd_fetch_metrics ;;
875
+ fetch-mentions) cmd_fetch_mentions "$@" ;;
876
+ fetch-timeline) cmd_fetch_timeline "$@" ;;
877
+ fetch-user-timeline) cmd_fetch_user_timeline "$@" ;;
878
+ post-tweet) cmd_post_tweet "$@" ;;
879
+ *)
880
+ echo "Error: Unknown command." >&2
881
+ echo "Run 'x-community.sh' without arguments for usage." >&2
882
+ exit 1
883
+ ;;
884
+ esac
885
+ }
886
+
887
+ # Guard: allow sourcing without executing main (for test harness)
888
+ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
889
+ main "$@"
890
+ fi