@ketecode/loom 0.0.0-stage → 0.1.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 (656) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +516 -2
  3. package/agents/accessibility-reviewer/AGENT.md +70 -0
  4. package/agents/accessibility-reviewer/evals/triggers.json +15 -0
  5. package/agents/agent-evaluator/AGENT.md +74 -0
  6. package/agents/agent-evaluator/evals/triggers.json +15 -0
  7. package/agents/architect/AGENT.md +82 -0
  8. package/agents/architect/evals/triggers.json +17 -0
  9. package/agents/billing-reviewer/AGENT.md +68 -0
  10. package/agents/billing-reviewer/evals/triggers.json +16 -0
  11. package/agents/code-reviewer/AGENT.md +51 -0
  12. package/agents/code-reviewer/evals/triggers.json +14 -0
  13. package/agents/code-simplifier/AGENT.md +74 -0
  14. package/agents/code-simplifier/evals/triggers.json +15 -0
  15. package/agents/cpp-build-resolver/AGENT.md +73 -0
  16. package/agents/cpp-build-resolver/evals/triggers.json +16 -0
  17. package/agents/cpp-reviewer/AGENT.md +59 -0
  18. package/agents/cpp-reviewer/evals/triggers.json +16 -0
  19. package/agents/csharp-build-resolver/AGENT.md +84 -0
  20. package/agents/csharp-build-resolver/evals/triggers.json +17 -0
  21. package/agents/csharp-reviewer/AGENT.md +88 -0
  22. package/agents/csharp-reviewer/evals/triggers.json +17 -0
  23. package/agents/dart-build-resolver/AGENT.md +80 -0
  24. package/agents/dart-build-resolver/evals/triggers.json +17 -0
  25. package/agents/dart-reviewer/AGENT.md +66 -0
  26. package/agents/dart-reviewer/evals/triggers.json +17 -0
  27. package/agents/database-reviewer/AGENT.md +72 -0
  28. package/agents/database-reviewer/evals/triggers.json +16 -0
  29. package/agents/django-reviewer/AGENT.md +61 -0
  30. package/agents/django-reviewer/evals/triggers.json +15 -0
  31. package/agents/doc-updater/AGENT.md +92 -0
  32. package/agents/doc-updater/evals/triggers.json +14 -0
  33. package/agents/e2e-runner/AGENT.md +88 -0
  34. package/agents/e2e-runner/evals/triggers.json +16 -0
  35. package/agents/fastapi-reviewer/AGENT.md +63 -0
  36. package/agents/fastapi-reviewer/evals/triggers.json +15 -0
  37. package/agents/flutter-reviewer/AGENT.md +70 -0
  38. package/agents/flutter-reviewer/evals/triggers.json +15 -0
  39. package/agents/fsharp-build-resolver/AGENT.md +81 -0
  40. package/agents/fsharp-build-resolver/evals/triggers.json +15 -0
  41. package/agents/fsharp-reviewer/AGENT.md +77 -0
  42. package/agents/fsharp-reviewer/evals/triggers.json +15 -0
  43. package/agents/go-build-resolver/AGENT.md +85 -0
  44. package/agents/go-build-resolver/evals/triggers.json +16 -0
  45. package/agents/go-reviewer/AGENT.md +66 -0
  46. package/agents/go-reviewer/evals/triggers.json +16 -0
  47. package/agents/healthcare-reviewer/AGENT.md +65 -0
  48. package/agents/healthcare-reviewer/evals/triggers.json +16 -0
  49. package/agents/i18n-reviewer/AGENT.md +64 -0
  50. package/agents/i18n-reviewer/evals/triggers.json +16 -0
  51. package/agents/infra-reviewer/AGENT.md +93 -0
  52. package/agents/infra-reviewer/evals/triggers.json +15 -0
  53. package/agents/java-build-resolver/AGENT.md +82 -0
  54. package/agents/java-build-resolver/evals/triggers.json +16 -0
  55. package/agents/java-reviewer/AGENT.md +64 -0
  56. package/agents/java-reviewer/evals/triggers.json +16 -0
  57. package/agents/kotlin-build-resolver/AGENT.md +89 -0
  58. package/agents/kotlin-build-resolver/evals/triggers.json +16 -0
  59. package/agents/kotlin-reviewer/AGENT.md +75 -0
  60. package/agents/kotlin-reviewer/evals/triggers.json +16 -0
  61. package/agents/mle-reviewer/AGENT.md +68 -0
  62. package/agents/mle-reviewer/evals/triggers.json +16 -0
  63. package/agents/network-config-reviewer/AGENT.md +68 -0
  64. package/agents/network-config-reviewer/evals/triggers.json +17 -0
  65. package/agents/payments-reviewer/AGENT.md +64 -0
  66. package/agents/payments-reviewer/evals/triggers.json +16 -0
  67. package/agents/performance-optimizer/AGENT.md +86 -0
  68. package/agents/performance-optimizer/evals/triggers.json +16 -0
  69. package/agents/perl-build-resolver/AGENT.md +84 -0
  70. package/agents/perl-build-resolver/evals/triggers.json +15 -0
  71. package/agents/perl-reviewer/AGENT.md +77 -0
  72. package/agents/perl-reviewer/evals/triggers.json +15 -0
  73. package/agents/php-build-resolver/AGENT.md +88 -0
  74. package/agents/php-build-resolver/evals/triggers.json +16 -0
  75. package/agents/php-reviewer/AGENT.md +61 -0
  76. package/agents/php-reviewer/evals/triggers.json +15 -0
  77. package/agents/planner/AGENT.md +75 -0
  78. package/agents/planner/evals/triggers.json +17 -0
  79. package/agents/pr-test-analyzer/AGENT.md +82 -0
  80. package/agents/pr-test-analyzer/evals/triggers.json +16 -0
  81. package/agents/python-build-resolver/AGENT.md +87 -0
  82. package/agents/python-build-resolver/evals/triggers.json +16 -0
  83. package/agents/python-reviewer/AGENT.md +63 -0
  84. package/agents/python-reviewer/evals/triggers.json +15 -0
  85. package/agents/rag-reviewer/AGENT.md +71 -0
  86. package/agents/rag-reviewer/evals/triggers.json +16 -0
  87. package/agents/react-reviewer/AGENT.md +70 -0
  88. package/agents/react-reviewer/evals/triggers.json +16 -0
  89. package/agents/refactor-cleaner/AGENT.md +82 -0
  90. package/agents/refactor-cleaner/evals/triggers.json +16 -0
  91. package/agents/ruby-build-resolver/AGENT.md +93 -0
  92. package/agents/ruby-build-resolver/evals/triggers.json +16 -0
  93. package/agents/ruby-reviewer/AGENT.md +62 -0
  94. package/agents/ruby-reviewer/evals/triggers.json +16 -0
  95. package/agents/rust-build-resolver/AGENT.md +98 -0
  96. package/agents/rust-build-resolver/evals/triggers.json +16 -0
  97. package/agents/rust-reviewer/AGENT.md +78 -0
  98. package/agents/rust-reviewer/evals/triggers.json +15 -0
  99. package/agents/security-reviewer/AGENT.md +84 -0
  100. package/agents/security-reviewer/evals/triggers.json +16 -0
  101. package/agents/silent-failure-hunter/AGENT.md +66 -0
  102. package/agents/silent-failure-hunter/evals/triggers.json +16 -0
  103. package/agents/solidity-reviewer/AGENT.md +70 -0
  104. package/agents/solidity-reviewer/evals/triggers.json +16 -0
  105. package/agents/swift-build-resolver/AGENT.md +85 -0
  106. package/agents/swift-build-resolver/evals/triggers.json +15 -0
  107. package/agents/swift-reviewer/AGENT.md +75 -0
  108. package/agents/swift-reviewer/evals/triggers.json +15 -0
  109. package/agents/typescript-build-resolver/AGENT.md +76 -0
  110. package/agents/typescript-build-resolver/evals/triggers.json +15 -0
  111. package/agents/typescript-reviewer/AGENT.md +76 -0
  112. package/agents/typescript-reviewer/evals/triggers.json +15 -0
  113. package/agents/vue-reviewer/AGENT.md +75 -0
  114. package/agents/vue-reviewer/evals/triggers.json +15 -0
  115. package/bin/kete-loom.js +683 -0
  116. package/commands/aside.md +19 -0
  117. package/commands/build-fix.md +26 -0
  118. package/commands/campaign.md +19 -0
  119. package/commands/changelog.md +36 -0
  120. package/commands/checkpoint.md +26 -0
  121. package/commands/codemap.md +25 -0
  122. package/commands/commit.md +26 -0
  123. package/commands/deps-audit.md +32 -0
  124. package/commands/epic-decompose.md +21 -0
  125. package/commands/explain.md +37 -0
  126. package/commands/feature-dev.md +22 -0
  127. package/commands/hookify.md +23 -0
  128. package/commands/i18n-check.md +39 -0
  129. package/commands/issue-sync.md +22 -0
  130. package/commands/jira.md +19 -0
  131. package/commands/learn.md +23 -0
  132. package/commands/loom.md +21 -0
  133. package/commands/loop.md +20 -0
  134. package/commands/model-route.md +27 -0
  135. package/commands/onboard.md +35 -0
  136. package/commands/orchestrate.md +21 -0
  137. package/commands/plan-feature.md +36 -0
  138. package/commands/postmortem.md +42 -0
  139. package/commands/pr-describe.md +39 -0
  140. package/commands/pr.md +22 -0
  141. package/commands/prd.md +41 -0
  142. package/commands/project-init.md +21 -0
  143. package/commands/refactor-clean.md +24 -0
  144. package/commands/resume-session.md +22 -0
  145. package/commands/review.md +35 -0
  146. package/commands/save-session.md +37 -0
  147. package/commands/security-scan.md +32 -0
  148. package/commands/sessions.md +21 -0
  149. package/commands/skill-create.md +21 -0
  150. package/commands/tdd.md +36 -0
  151. package/commands/test-coverage.md +24 -0
  152. package/commands/threat-model.md +36 -0
  153. package/commands/update-docs.md +25 -0
  154. package/commands/verify.md +21 -0
  155. package/hooks/block-dangerous-commands/HOOK.md +30 -0
  156. package/hooks/block-dangerous-commands/cases.json +50 -0
  157. package/hooks/block-dangerous-commands/hook.js +75 -0
  158. package/hooks/block-no-verify/HOOK.md +25 -0
  159. package/hooks/block-no-verify/cases.json +30 -0
  160. package/hooks/block-no-verify/hook.js +43 -0
  161. package/hooks/format-on-edit/HOOK.md +24 -0
  162. package/hooks/format-on-edit/hook.js +89 -0
  163. package/hooks/guard-secrets/HOOK.md +19 -0
  164. package/hooks/guard-secrets/cases.json +110 -0
  165. package/hooks/guard-secrets/hook.js +44 -0
  166. package/hooks/load-session-context/HOOK.md +20 -0
  167. package/hooks/load-session-context/hook.js +56 -0
  168. package/hooks/protect-files/HOOK.md +24 -0
  169. package/hooks/protect-files/cases.json +27 -0
  170. package/hooks/protect-files/hook.js +39 -0
  171. package/hooks/run.js +132 -0
  172. package/mcp/atlassian.json +11 -0
  173. package/mcp/chrome-devtools.json +18 -0
  174. package/mcp/cloudflare.json +11 -0
  175. package/mcp/context7.json +11 -0
  176. package/mcp/fetch.json +17 -0
  177. package/mcp/figma.json +11 -0
  178. package/mcp/git.json +17 -0
  179. package/mcp/github.json +11 -0
  180. package/mcp/linear.json +11 -0
  181. package/mcp/memory.json +18 -0
  182. package/mcp/notion.json +11 -0
  183. package/mcp/playwright.json +18 -0
  184. package/mcp/sentry.json +11 -0
  185. package/mcp/stripe.json +11 -0
  186. package/mcp/supabase.json +11 -0
  187. package/mcp/time.json +17 -0
  188. package/package.json +59 -4
  189. package/profiles.json +165 -0
  190. package/rules/common.md +21 -0
  191. package/rules/cpp.md +29 -0
  192. package/rules/csharp.md +25 -0
  193. package/rules/dart.md +29 -0
  194. package/rules/fsharp.md +28 -0
  195. package/rules/git.md +26 -0
  196. package/rules/go.md +29 -0
  197. package/rules/java.md +29 -0
  198. package/rules/kotlin.md +27 -0
  199. package/rules/perl.md +26 -0
  200. package/rules/php.md +28 -0
  201. package/rules/python.md +30 -0
  202. package/rules/ruby.md +27 -0
  203. package/rules/rust.md +26 -0
  204. package/rules/security.md +29 -0
  205. package/rules/swift.md +28 -0
  206. package/rules/testing.md +25 -0
  207. package/rules/typescript.md +29 -0
  208. package/schemas/skill-frontmatter.schema.json +52 -0
  209. package/scripts/lib/audit.js +96 -0
  210. package/scripts/lib/frontmatter.js +65 -0
  211. package/scripts/lib/hooks-install.js +292 -0
  212. package/scripts/lib/i18n.js +51 -0
  213. package/scripts/lib/inventory.js +85 -0
  214. package/scripts/lib/lint.js +158 -0
  215. package/scripts/lib/mcp-install.js +284 -0
  216. package/scripts/lib/profiles.js +128 -0
  217. package/scripts/lib/render.js +86 -0
  218. package/scripts/lib/rules-install.js +118 -0
  219. package/scripts/lib/skills.js +86 -0
  220. package/scripts/lib/targets.js +113 -0
  221. package/skills/accounting-api-integration/SKILL.md +81 -0
  222. package/skills/accounting-api-integration/evals/triggers.json +17 -0
  223. package/skills/accounting-api-integration/references/quickbooks.md +48 -0
  224. package/skills/accounting-api-integration/references/xero.md +45 -0
  225. package/skills/agent-harness-design/SKILL.md +125 -0
  226. package/skills/agent-harness-design/evals/triggers.json +21 -0
  227. package/skills/ai-media-generation/SKILL.md +89 -0
  228. package/skills/ai-media-generation/evals/triggers.json +16 -0
  229. package/skills/ai-media-generation/references/job-pipeline.md +76 -0
  230. package/skills/angular-patterns/SKILL.md +122 -0
  231. package/skills/angular-patterns/evals/triggers.json +16 -0
  232. package/skills/api-design/SKILL.md +96 -0
  233. package/skills/api-design/evals/triggers.json +18 -0
  234. package/skills/api-design/references/conventions.md +133 -0
  235. package/skills/architecture-decision-records/SKILL.md +98 -0
  236. package/skills/architecture-decision-records/evals/triggers.json +17 -0
  237. package/skills/article-writing/SKILL.md +108 -0
  238. package/skills/article-writing/evals/triggers.json +16 -0
  239. package/skills/aspnet-core-patterns/SKILL.md +149 -0
  240. package/skills/aspnet-core-patterns/evals/triggers.json +16 -0
  241. package/skills/auth-patterns/SKILL.md +104 -0
  242. package/skills/auth-patterns/evals/triggers.json +17 -0
  243. package/skills/auth-patterns/references/oauth-oidc.md +64 -0
  244. package/skills/autonomous-loops/SKILL.md +99 -0
  245. package/skills/autonomous-loops/evals/triggers.json +14 -0
  246. package/skills/aws-patterns/SKILL.md +91 -0
  247. package/skills/aws-patterns/evals/triggers.json +16 -0
  248. package/skills/azure-patterns/SKILL.md +86 -0
  249. package/skills/azure-patterns/evals/triggers.json +16 -0
  250. package/skills/benchmark-methodology/SKILL.md +88 -0
  251. package/skills/benchmark-methodology/evals/triggers.json +16 -0
  252. package/skills/benchmark-methodology/references/language-harnesses.md +130 -0
  253. package/skills/bgp-diagnostics/SKILL.md +84 -0
  254. package/skills/bgp-diagnostics/evals/triggers.json +18 -0
  255. package/skills/bgp-diagnostics/references/bgp-reference.md +81 -0
  256. package/skills/bioinformatics-workflows/SKILL.md +108 -0
  257. package/skills/bioinformatics-workflows/evals/triggers.json +16 -0
  258. package/skills/bioinformatics-workflows/references/commands.md +71 -0
  259. package/skills/bioinformatics-workflows/references/formats.md +24 -0
  260. package/skills/bioinformatics-workflows/references/workflow-managers.md +107 -0
  261. package/skills/brand-voice/SKILL.md +112 -0
  262. package/skills/brand-voice/evals/triggers.json +16 -0
  263. package/skills/browser-qa/SKILL.md +99 -0
  264. package/skills/browser-qa/evals/triggers.json +16 -0
  265. package/skills/bun-runtime/SKILL.md +93 -0
  266. package/skills/bun-runtime/evals/triggers.json +16 -0
  267. package/skills/cisco-ios-config/SKILL.md +101 -0
  268. package/skills/cisco-ios-config/evals/triggers.json +18 -0
  269. package/skills/cisco-ios-config/references/verification.md +63 -0
  270. package/skills/clickhouse-patterns/SKILL.md +101 -0
  271. package/skills/clickhouse-patterns/evals/triggers.json +18 -0
  272. package/skills/clickhouse-patterns/references/engines-and-views.md +138 -0
  273. package/skills/clinical-decision-support/SKILL.md +90 -0
  274. package/skills/clinical-decision-support/evals/triggers.json +16 -0
  275. package/skills/clinical-decision-support/references/cds-hooks.md +76 -0
  276. package/skills/clinical-decision-support/references/regulatory-boundaries.md +37 -0
  277. package/skills/code-tour/SKILL.md +97 -0
  278. package/skills/code-tour/evals/triggers.json +14 -0
  279. package/skills/codebase-onboarding/SKILL.md +112 -0
  280. package/skills/codebase-onboarding/evals/triggers.json +14 -0
  281. package/skills/cold-outreach-sequences/SKILL.md +90 -0
  282. package/skills/cold-outreach-sequences/evals/triggers.json +16 -0
  283. package/skills/content-repurposing/SKILL.md +82 -0
  284. package/skills/content-repurposing/evals/triggers.json +16 -0
  285. package/skills/context-budget/SKILL.md +84 -0
  286. package/skills/context-budget/evals/triggers.json +14 -0
  287. package/skills/conventional-commits/SKILL.md +144 -0
  288. package/skills/conventional-commits/evals/triggers.json +16 -0
  289. package/skills/cpp-patterns/SKILL.md +129 -0
  290. package/skills/cpp-patterns/evals/triggers.json +16 -0
  291. package/skills/cpp-patterns/references/cmake-and-tooling.md +88 -0
  292. package/skills/cpp-patterns/references/undefined-behaviour.md +75 -0
  293. package/skills/cpp-testing/SKILL.md +131 -0
  294. package/skills/cpp-testing/evals/triggers.json +16 -0
  295. package/skills/csharp-patterns/SKILL.md +110 -0
  296. package/skills/csharp-patterns/evals/triggers.json +17 -0
  297. package/skills/csharp-patterns/references/analyzers.md +59 -0
  298. package/skills/csharp-testing/SKILL.md +85 -0
  299. package/skills/csharp-testing/evals/triggers.json +17 -0
  300. package/skills/csharp-testing/references/integration-tests.md +77 -0
  301. package/skills/css-architecture/SKILL.md +143 -0
  302. package/skills/css-architecture/evals/triggers.json +16 -0
  303. package/skills/customs-trade-compliance/SKILL.md +95 -0
  304. package/skills/customs-trade-compliance/evals/triggers.json +16 -0
  305. package/skills/customs-trade-compliance/references/incoterms-2020.md +42 -0
  306. package/skills/dart-patterns/SKILL.md +157 -0
  307. package/skills/dart-patterns/evals/triggers.json +18 -0
  308. package/skills/dart-testing/SKILL.md +164 -0
  309. package/skills/dart-testing/evals/triggers.json +18 -0
  310. package/skills/data-analysis-notebooks/SKILL.md +113 -0
  311. package/skills/data-analysis-notebooks/evals/triggers.json +16 -0
  312. package/skills/data-analysis-notebooks/references/dataframe-pitfalls.md +49 -0
  313. package/skills/data-analysis-notebooks/references/statistics-pitfalls.md +24 -0
  314. package/skills/data-pipelines/SKILL.md +90 -0
  315. package/skills/data-pipelines/evals/triggers.json +16 -0
  316. package/skills/data-pipelines/references/orchestrators.md +64 -0
  317. package/skills/database-migrations/SKILL.md +102 -0
  318. package/skills/database-migrations/evals/triggers.json +16 -0
  319. package/skills/database-migrations/references/recipes.md +98 -0
  320. package/skills/database-migrations/references/tools.md +21 -0
  321. package/skills/dbt-patterns/SKILL.md +96 -0
  322. package/skills/dbt-patterns/evals/triggers.json +16 -0
  323. package/skills/dbt-patterns/references/yaml-examples.md +114 -0
  324. package/skills/deep-research/SKILL.md +107 -0
  325. package/skills/deep-research/evals/triggers.json +14 -0
  326. package/skills/dependency-audit/SKILL.md +103 -0
  327. package/skills/dependency-audit/evals/triggers.json +16 -0
  328. package/skills/dependency-audit/references/tools.md +124 -0
  329. package/skills/deployment-strategies/SKILL.md +108 -0
  330. package/skills/deployment-strategies/evals/triggers.json +16 -0
  331. package/skills/django-patterns/SKILL.md +123 -0
  332. package/skills/django-patterns/evals/triggers.json +16 -0
  333. package/skills/docker-patterns/SKILL.md +132 -0
  334. package/skills/docker-patterns/evals/triggers.json +16 -0
  335. package/skills/docker-patterns/references/dockerfiles.md +105 -0
  336. package/skills/e2e-testing-playwright/SKILL.md +94 -0
  337. package/skills/e2e-testing-playwright/evals/triggers.json +16 -0
  338. package/skills/e2e-testing-playwright/references/config-and-ci.md +129 -0
  339. package/skills/ef-core-patterns/SKILL.md +117 -0
  340. package/skills/ef-core-patterns/evals/triggers.json +16 -0
  341. package/skills/email-deliverability/SKILL.md +99 -0
  342. package/skills/email-deliverability/evals/triggers.json +17 -0
  343. package/skills/email-deliverability/references/dns-records.md +85 -0
  344. package/skills/error-handling-patterns/SKILL.md +112 -0
  345. package/skills/error-handling-patterns/evals/triggers.json +18 -0
  346. package/skills/error-handling-patterns/references/language-idioms.md +49 -0
  347. package/skills/estimation-and-scoping/SKILL.md +97 -0
  348. package/skills/estimation-and-scoping/evals/triggers.json +14 -0
  349. package/skills/evm-dapp-integration/SKILL.md +73 -0
  350. package/skills/evm-dapp-integration/evals/triggers.json +16 -0
  351. package/skills/fastapi-patterns/SKILL.md +127 -0
  352. package/skills/fastapi-patterns/evals/triggers.json +16 -0
  353. package/skills/ffmpeg-media-processing/SKILL.md +94 -0
  354. package/skills/ffmpeg-media-processing/evals/triggers.json +16 -0
  355. package/skills/ffmpeg-media-processing/references/recipes.md +112 -0
  356. package/skills/fhir-integration/SKILL.md +81 -0
  357. package/skills/fhir-integration/evals/triggers.json +16 -0
  358. package/skills/fhir-integration/references/search-and-bundles.md +73 -0
  359. package/skills/fhir-integration/references/smart-on-fhir.md +50 -0
  360. package/skills/flask-patterns/SKILL.md +132 -0
  361. package/skills/flask-patterns/evals/triggers.json +16 -0
  362. package/skills/flutter-patterns/SKILL.md +99 -0
  363. package/skills/flutter-patterns/evals/triggers.json +16 -0
  364. package/skills/flutterwave-integration/SKILL.md +75 -0
  365. package/skills/flutterwave-integration/evals/triggers.json +16 -0
  366. package/skills/flutterwave-integration/references/api.md +73 -0
  367. package/skills/flutterwave-integration/references/webhooks.md +49 -0
  368. package/skills/fsharp-patterns/SKILL.md +133 -0
  369. package/skills/fsharp-patterns/evals/triggers.json +16 -0
  370. package/skills/fsharp-testing/SKILL.md +136 -0
  371. package/skills/fsharp-testing/evals/triggers.json +16 -0
  372. package/skills/gcp-patterns/SKILL.md +86 -0
  373. package/skills/gcp-patterns/evals/triggers.json +16 -0
  374. package/skills/git-workflow/SKILL.md +114 -0
  375. package/skills/git-workflow/evals/triggers.json +16 -0
  376. package/skills/github-actions-ci/SKILL.md +117 -0
  377. package/skills/github-actions-ci/evals/triggers.json +16 -0
  378. package/skills/github-api-automation/SKILL.md +101 -0
  379. package/skills/github-api-automation/evals/triggers.json +17 -0
  380. package/skills/go-patterns/SKILL.md +118 -0
  381. package/skills/go-patterns/evals/triggers.json +17 -0
  382. package/skills/go-patterns/references/concurrency.md +162 -0
  383. package/skills/go-testing/SKILL.md +154 -0
  384. package/skills/go-testing/evals/triggers.json +17 -0
  385. package/skills/hexagonal-architecture/SKILL.md +89 -0
  386. package/skills/hexagonal-architecture/evals/triggers.json +17 -0
  387. package/skills/hipaa-compliance/SKILL.md +91 -0
  388. package/skills/hipaa-compliance/evals/triggers.json +18 -0
  389. package/skills/hipaa-compliance/references/safeguards.md +68 -0
  390. package/skills/hl7v2-integration/SKILL.md +69 -0
  391. package/skills/hl7v2-integration/evals/triggers.json +16 -0
  392. package/skills/hl7v2-integration/references/messages-and-ack.md +78 -0
  393. package/skills/homelab-network-setup/SKILL.md +125 -0
  394. package/skills/homelab-network-setup/evals/triggers.json +18 -0
  395. package/skills/homelab-network-setup/references/firewall-and-dns.md +45 -0
  396. package/skills/homelab-network-setup/references/wireguard.md +72 -0
  397. package/skills/hubtel-integration/SKILL.md +77 -0
  398. package/skills/hubtel-integration/evals/triggers.json +16 -0
  399. package/skills/hubtel-integration/references/api.md +92 -0
  400. package/skills/i18n-localization/SKILL.md +114 -0
  401. package/skills/i18n-localization/evals/triggers.json +16 -0
  402. package/skills/i18n-localization/references/libraries.md +52 -0
  403. package/skills/incident-postmortem/SKILL.md +118 -0
  404. package/skills/incident-postmortem/evals/triggers.json +14 -0
  405. package/skills/inventory-demand-planning/SKILL.md +89 -0
  406. package/skills/inventory-demand-planning/evals/triggers.json +16 -0
  407. package/skills/inventory-demand-planning/references/formulas.md +62 -0
  408. package/skills/investor-materials/SKILL.md +127 -0
  409. package/skills/investor-materials/evals/triggers.json +16 -0
  410. package/skills/investor-materials/references/templates.md +74 -0
  411. package/skills/invoicing-and-tax/SKILL.md +84 -0
  412. package/skills/invoicing-and-tax/evals/triggers.json +17 -0
  413. package/skills/invoicing-and-tax/references/e-invoicing.md +24 -0
  414. package/skills/invoicing-and-tax/references/invoice-fields.md +38 -0
  415. package/skills/invoicing-and-tax/references/rounding.md +26 -0
  416. package/skills/java-patterns/SKILL.md +141 -0
  417. package/skills/java-patterns/evals/triggers.json +17 -0
  418. package/skills/java-testing/SKILL.md +142 -0
  419. package/skills/java-testing/evals/triggers.json +16 -0
  420. package/skills/jetpack-compose-patterns/SKILL.md +88 -0
  421. package/skills/jetpack-compose-patterns/evals/triggers.json +16 -0
  422. package/skills/jira-integration/SKILL.md +92 -0
  423. package/skills/jira-integration/evals/triggers.json +17 -0
  424. package/skills/jpa-hibernate/SKILL.md +92 -0
  425. package/skills/jpa-hibernate/evals/triggers.json +18 -0
  426. package/skills/jpa-hibernate/references/mapping.md +99 -0
  427. package/skills/kotlin-multiplatform/SKILL.md +97 -0
  428. package/skills/kotlin-multiplatform/evals/triggers.json +16 -0
  429. package/skills/kotlin-patterns/SKILL.md +128 -0
  430. package/skills/kotlin-patterns/evals/triggers.json +16 -0
  431. package/skills/kotlin-testing/SKILL.md +117 -0
  432. package/skills/kotlin-testing/evals/triggers.json +16 -0
  433. package/skills/ktor-patterns/SKILL.md +135 -0
  434. package/skills/ktor-patterns/evals/triggers.json +18 -0
  435. package/skills/kubernetes-patterns/SKILL.md +106 -0
  436. package/skills/kubernetes-patterns/evals/triggers.json +16 -0
  437. package/skills/kubernetes-patterns/references/manifests.md +115 -0
  438. package/skills/laravel-patterns/SKILL.md +136 -0
  439. package/skills/laravel-patterns/evals/triggers.json +16 -0
  440. package/skills/linear-integration/SKILL.md +98 -0
  441. package/skills/linear-integration/evals/triggers.json +17 -0
  442. package/skills/literature-review/SKILL.md +109 -0
  443. package/skills/literature-review/evals/triggers.json +16 -0
  444. package/skills/literature-review/references/appraisal.md +41 -0
  445. package/skills/literature-review/references/search-sources.md +39 -0
  446. package/skills/llm-app-patterns/SKILL.md +97 -0
  447. package/skills/llm-app-patterns/evals/triggers.json +17 -0
  448. package/skills/llm-evals/SKILL.md +98 -0
  449. package/skills/llm-evals/evals/triggers.json +16 -0
  450. package/skills/llm-evals/references/graders.md +66 -0
  451. package/skills/logistics-exception-management/SKILL.md +90 -0
  452. package/skills/logistics-exception-management/evals/triggers.json +16 -0
  453. package/skills/manim-animations/SKILL.md +106 -0
  454. package/skills/manim-animations/evals/triggers.json +16 -0
  455. package/skills/market-research/SKILL.md +117 -0
  456. package/skills/market-research/evals/triggers.json +16 -0
  457. package/skills/marketing-campaign/SKILL.md +111 -0
  458. package/skills/marketing-campaign/evals/triggers.json +16 -0
  459. package/skills/mcp-server-patterns/SKILL.md +127 -0
  460. package/skills/mcp-server-patterns/evals/triggers.json +18 -0
  461. package/skills/mcp-server-patterns/references/protocol-notes.md +68 -0
  462. package/skills/metrics-dashboards/SKILL.md +113 -0
  463. package/skills/metrics-dashboards/evals/triggers.json +16 -0
  464. package/skills/ml-experiment-workflow/SKILL.md +125 -0
  465. package/skills/ml-experiment-workflow/evals/triggers.json +16 -0
  466. package/skills/mongodb-patterns/SKILL.md +115 -0
  467. package/skills/mongodb-patterns/evals/triggers.json +18 -0
  468. package/skills/mongodb-patterns/references/indexes-and-aggregation.md +94 -0
  469. package/skills/mpesa-daraja-integration/SKILL.md +70 -0
  470. package/skills/mpesa-daraja-integration/evals/triggers.json +16 -0
  471. package/skills/mpesa-daraja-integration/references/api.md +103 -0
  472. package/skills/mpesa-daraja-integration/references/callbacks.md +74 -0
  473. package/skills/mtn-momo-integration/SKILL.md +66 -0
  474. package/skills/mtn-momo-integration/evals/triggers.json +16 -0
  475. package/skills/mtn-momo-integration/references/api.md +153 -0
  476. package/skills/mtn-momo-integration/references/callbacks.md +92 -0
  477. package/skills/mtn-momo-integration/references/sandbox-and-go-live.md +51 -0
  478. package/skills/mtn-momo-integration/scripts/sandbox-setup.js +79 -0
  479. package/skills/mysql-patterns/SKILL.md +96 -0
  480. package/skills/mysql-patterns/evals/triggers.json +16 -0
  481. package/skills/mysql-patterns/references/explain.md +34 -0
  482. package/skills/mysql-patterns/references/online-ddl.md +41 -0
  483. package/skills/nestjs-patterns/SKILL.md +114 -0
  484. package/skills/nestjs-patterns/evals/triggers.json +16 -0
  485. package/skills/nestjs-patterns/references/testing.md +68 -0
  486. package/skills/network-automation/SKILL.md +107 -0
  487. package/skills/network-automation/evals/triggers.json +18 -0
  488. package/skills/network-automation/references/tool-snippets.md +153 -0
  489. package/skills/network-troubleshooting/SKILL.md +152 -0
  490. package/skills/network-troubleshooting/evals/triggers.json +18 -0
  491. package/skills/network-troubleshooting/references/commands.md +33 -0
  492. package/skills/nextjs-patterns/SKILL.md +150 -0
  493. package/skills/nextjs-patterns/evals/triggers.json +18 -0
  494. package/skills/nextjs-patterns/references/caching.md +87 -0
  495. package/skills/node-backend-patterns/SKILL.md +103 -0
  496. package/skills/node-backend-patterns/evals/triggers.json +16 -0
  497. package/skills/node-backend-patterns/references/templates.md +122 -0
  498. package/skills/nuxt-patterns/SKILL.md +103 -0
  499. package/skills/nuxt-patterns/evals/triggers.json +16 -0
  500. package/skills/parallel-agent-orchestration/SKILL.md +106 -0
  501. package/skills/parallel-agent-orchestration/evals/triggers.json +14 -0
  502. package/skills/paystack-integration/SKILL.md +59 -0
  503. package/skills/paystack-integration/evals/triggers.json +14 -0
  504. package/skills/paystack-integration/references/api.md +73 -0
  505. package/skills/paystack-integration/references/mobile-money.md +50 -0
  506. package/skills/paystack-integration/references/webhooks.md +80 -0
  507. package/skills/pci-dss-scope/SKILL.md +103 -0
  508. package/skills/pci-dss-scope/evals/triggers.json +18 -0
  509. package/skills/pci-dss-scope/references/saq-and-data.md +36 -0
  510. package/skills/perl-patterns/SKILL.md +135 -0
  511. package/skills/perl-patterns/evals/triggers.json +18 -0
  512. package/skills/perl-testing/SKILL.md +126 -0
  513. package/skills/perl-testing/evals/triggers.json +17 -0
  514. package/skills/php-patterns/SKILL.md +116 -0
  515. package/skills/php-patterns/evals/triggers.json +16 -0
  516. package/skills/php-testing/SKILL.md +123 -0
  517. package/skills/php-testing/evals/triggers.json +16 -0
  518. package/skills/postgres-patterns/SKILL.md +103 -0
  519. package/skills/postgres-patterns/evals/triggers.json +17 -0
  520. package/skills/postgres-patterns/references/explain.md +59 -0
  521. package/skills/postgres-patterns/references/locks.md +53 -0
  522. package/skills/prd-writing/SKILL.md +124 -0
  523. package/skills/prd-writing/evals/triggers.json +15 -0
  524. package/skills/prisma-patterns/SKILL.md +83 -0
  525. package/skills/prisma-patterns/evals/triggers.json +18 -0
  526. package/skills/prisma-patterns/references/prisma-8.md +46 -0
  527. package/skills/prisma-patterns/references/workflow.md +50 -0
  528. package/skills/privacy-compliance/SKILL.md +121 -0
  529. package/skills/privacy-compliance/evals/triggers.json +18 -0
  530. package/skills/privacy-compliance/references/jurisdictions.md +51 -0
  531. package/skills/production-scheduling/SKILL.md +98 -0
  532. package/skills/production-scheduling/evals/triggers.json +16 -0
  533. package/skills/prompt-engineering/SKILL.md +92 -0
  534. package/skills/prompt-engineering/evals/triggers.json +16 -0
  535. package/skills/python-patterns/SKILL.md +107 -0
  536. package/skills/python-patterns/evals/triggers.json +16 -0
  537. package/skills/python-patterns/references/tooling.md +101 -0
  538. package/skills/python-testing/SKILL.md +126 -0
  539. package/skills/python-testing/evals/triggers.json +16 -0
  540. package/skills/python-testing/references/mocking.md +71 -0
  541. package/skills/pytorch-patterns/SKILL.md +139 -0
  542. package/skills/pytorch-patterns/evals/triggers.json +16 -0
  543. package/skills/pytorch-patterns/references/debugging.md +39 -0
  544. package/skills/quarkus-patterns/SKILL.md +93 -0
  545. package/skills/quarkus-patterns/evals/triggers.json +18 -0
  546. package/skills/rag-pipeline-patterns/SKILL.md +117 -0
  547. package/skills/rag-pipeline-patterns/evals/triggers.json +18 -0
  548. package/skills/rag-pipeline-patterns/references/evaluation.md +37 -0
  549. package/skills/rag-pipeline-patterns/references/retrieval-recipes.md +78 -0
  550. package/skills/rails-patterns/SKILL.md +132 -0
  551. package/skills/rails-patterns/evals/triggers.json +16 -0
  552. package/skills/react-native-patterns/SKILL.md +101 -0
  553. package/skills/react-native-patterns/evals/triggers.json +16 -0
  554. package/skills/react-patterns/SKILL.md +140 -0
  555. package/skills/react-patterns/evals/triggers.json +18 -0
  556. package/skills/react-performance/SKILL.md +107 -0
  557. package/skills/react-performance/evals/triggers.json +18 -0
  558. package/skills/redis-patterns/SKILL.md +109 -0
  559. package/skills/redis-patterns/evals/triggers.json +18 -0
  560. package/skills/redis-patterns/references/operations.md +61 -0
  561. package/skills/refactoring-safely/SKILL.md +107 -0
  562. package/skills/refactoring-safely/evals/triggers.json +17 -0
  563. package/skills/release-management/SKILL.md +141 -0
  564. package/skills/release-management/evals/triggers.json +16 -0
  565. package/skills/remotion-video/SKILL.md +91 -0
  566. package/skills/remotion-video/evals/triggers.json +16 -0
  567. package/skills/remotion-video/references/lambda.md +66 -0
  568. package/skills/returns-reverse-logistics/SKILL.md +97 -0
  569. package/skills/returns-reverse-logistics/evals/triggers.json +16 -0
  570. package/skills/ruby-patterns/SKILL.md +127 -0
  571. package/skills/ruby-patterns/evals/triggers.json +18 -0
  572. package/skills/ruby-testing/SKILL.md +113 -0
  573. package/skills/ruby-testing/evals/triggers.json +18 -0
  574. package/skills/runbook-writing/SKILL.md +101 -0
  575. package/skills/runbook-writing/evals/triggers.json +14 -0
  576. package/skills/rust-patterns/SKILL.md +118 -0
  577. package/skills/rust-patterns/evals/triggers.json +16 -0
  578. package/skills/rust-patterns/references/cargo-and-clippy.md +105 -0
  579. package/skills/rust-patterns/references/errors-and-traits.md +97 -0
  580. package/skills/rust-testing/SKILL.md +148 -0
  581. package/skills/rust-testing/evals/triggers.json +16 -0
  582. package/skills/rust-testing/references/fakes-proptest-async.md +113 -0
  583. package/skills/search-first/SKILL.md +73 -0
  584. package/skills/search-first/evals/triggers.json +14 -0
  585. package/skills/security-review/SKILL.md +58 -0
  586. package/skills/security-review/evals/triggers.json +14 -0
  587. package/skills/security-review/references/checklist.md +54 -0
  588. package/skills/security-review/scripts/scan-secrets.js +79 -0
  589. package/skills/seo-audit/SKILL.md +111 -0
  590. package/skills/seo-audit/evals/triggers.json +16 -0
  591. package/skills/seo-audit/references/checks.md +65 -0
  592. package/skills/session-handoff/SKILL.md +101 -0
  593. package/skills/session-handoff/evals/triggers.json +14 -0
  594. package/skills/skill-authoring/SKILL.md +87 -0
  595. package/skills/skill-authoring/evals/triggers.json +14 -0
  596. package/skills/skill-authoring/references/frontmatter.md +40 -0
  597. package/skills/slack-app-integration/SKILL.md +108 -0
  598. package/skills/slack-app-integration/evals/triggers.json +17 -0
  599. package/skills/smart-contract-security/SKILL.md +110 -0
  600. package/skills/smart-contract-security/evals/triggers.json +16 -0
  601. package/skills/smart-contract-security/references/vulnerability-checklist.md +79 -0
  602. package/skills/social-media-publishing/SKILL.md +84 -0
  603. package/skills/social-media-publishing/evals/triggers.json +17 -0
  604. package/skills/social-media-publishing/references/platform-apis.md +116 -0
  605. package/skills/solidity-patterns/SKILL.md +95 -0
  606. package/skills/solidity-patterns/evals/triggers.json +16 -0
  607. package/skills/spring-boot-patterns/SKILL.md +128 -0
  608. package/skills/spring-boot-patterns/evals/triggers.json +18 -0
  609. package/skills/spring-boot-patterns/references/testing.md +72 -0
  610. package/skills/stripe-integration/SKILL.md +69 -0
  611. package/skills/stripe-integration/evals/triggers.json +16 -0
  612. package/skills/stripe-integration/references/api.md +71 -0
  613. package/skills/stripe-integration/references/billing.md +58 -0
  614. package/skills/stripe-integration/references/webhooks.md +76 -0
  615. package/skills/subscription-billing-ops/SKILL.md +118 -0
  616. package/skills/subscription-billing-ops/evals/triggers.json +17 -0
  617. package/skills/subscription-billing-ops/references/proration.md +47 -0
  618. package/skills/subscription-billing-ops/references/revenue-metrics.md +38 -0
  619. package/skills/svelte-patterns/SKILL.md +122 -0
  620. package/skills/svelte-patterns/evals/triggers.json +16 -0
  621. package/skills/swift-patterns/SKILL.md +113 -0
  622. package/skills/swift-patterns/evals/triggers.json +16 -0
  623. package/skills/swift-patterns/references/concurrency-migration.md +40 -0
  624. package/skills/swift-testing/SKILL.md +121 -0
  625. package/skills/swift-testing/evals/triggers.json +16 -0
  626. package/skills/swift-testing/references/swift-testing-cheatsheet.md +81 -0
  627. package/skills/swiftui-patterns/SKILL.md +102 -0
  628. package/skills/swiftui-patterns/evals/triggers.json +16 -0
  629. package/skills/tdd-workflow/SKILL.md +67 -0
  630. package/skills/tdd-workflow/evals/triggers.json +14 -0
  631. package/skills/technical-writing/SKILL.md +95 -0
  632. package/skills/technical-writing/evals/triggers.json +14 -0
  633. package/skills/terraform-patterns/SKILL.md +122 -0
  634. package/skills/terraform-patterns/evals/triggers.json +16 -0
  635. package/skills/terraform-patterns/references/backends.md +67 -0
  636. package/skills/test-coverage-improvement/SKILL.md +107 -0
  637. package/skills/test-coverage-improvement/evals/triggers.json +16 -0
  638. package/skills/threat-modeling/SKILL.md +113 -0
  639. package/skills/threat-modeling/evals/triggers.json +16 -0
  640. package/skills/typescript-patterns/SKILL.md +122 -0
  641. package/skills/typescript-patterns/evals/triggers.json +16 -0
  642. package/skills/typescript-testing/SKILL.md +101 -0
  643. package/skills/typescript-testing/evals/triggers.json +16 -0
  644. package/skills/verification-loop/SKILL.md +70 -0
  645. package/skills/verification-loop/evals/triggers.json +14 -0
  646. package/skills/verification-loop/scripts/detect-checks.js +102 -0
  647. package/skills/vite-patterns/SKILL.md +117 -0
  648. package/skills/vite-patterns/evals/triggers.json +16 -0
  649. package/skills/vue-patterns/SKILL.md +104 -0
  650. package/skills/vue-patterns/evals/triggers.json +16 -0
  651. package/skills/web-accessibility/SKILL.md +119 -0
  652. package/skills/web-accessibility/evals/triggers.json +16 -0
  653. package/skills/web-performance/SKILL.md +107 -0
  654. package/skills/web-performance/evals/triggers.json +16 -0
  655. package/skills/web-scraping/SKILL.md +80 -0
  656. package/skills/web-scraping/evals/triggers.json +16 -0
@@ -0,0 +1,122 @@
1
+ # Templates
2
+
3
+ Adapt to the project's module system and libraries; these use ESM and zod.
4
+
5
+ ## Express: validation middleware and error handler
6
+
7
+ ```ts
8
+ import type { Request, Response, NextFunction } from 'express';
9
+ import { ZodError, type ZodType } from 'zod';
10
+
11
+ export class AppError extends Error {
12
+ constructor(public status: number, public code: string, message: string) { super(message); }
13
+ }
14
+
15
+ export const validateBody = (schema: ZodType) => (req: Request, res: Response, next: NextFunction) => {
16
+ const result = schema.safeParse(req.body);
17
+ if (!result.success) {
18
+ return res.status(400).json({ error: { code: 'invalid_request', issues: result.error.issues.map((i) => ({ path: i.path.join('.'), message: i.message })) } });
19
+ }
20
+ req.body = result.data;
21
+ next();
22
+ };
23
+
24
+ // Register after all routes.
25
+ export function errorHandler(err: unknown, req: Request, res: Response, next: NextFunction) {
26
+ if (res.headersSent) return next(err); // let Express close a half-sent response
27
+ if (err instanceof AppError) {
28
+ return res.status(err.status).json({ error: { code: err.code, message: err.message } });
29
+ }
30
+ if (err instanceof ZodError) {
31
+ return res.status(400).json({ error: { code: 'invalid_request' } });
32
+ }
33
+ req.log?.error({ err }, 'unhandled error');
34
+ res.status(500).json({ error: { code: 'internal', message: 'Something went wrong' } });
35
+ }
36
+ ```
37
+
38
+ ## Express: server.ts with graceful shutdown
39
+
40
+ ```ts
41
+ import { createApp } from './app.js';
42
+ import { config } from './config.js';
43
+ import { logger } from './logger.js';
44
+ import { db } from './db.js';
45
+
46
+ const app = createApp();
47
+ const server = app.listen(config.port, () => logger.info({ port: config.port }, 'listening'));
48
+ server.headersTimeout = 65_000;
49
+ server.requestTimeout = 30_000;
50
+
51
+ let shuttingDown = false;
52
+ export const isReady = () => !shuttingDown; // the readiness route reads this
53
+
54
+ async function shutdown(signal: string) {
55
+ if (shuttingDown) return;
56
+ shuttingDown = true;
57
+ logger.info({ signal }, 'shutting down');
58
+ const force = setTimeout(() => { logger.error('forced exit'); process.exit(1); }, 25_000);
59
+ force.unref();
60
+ server.close(async (err) => {
61
+ await db.end().catch((e) => logger.error({ err: e }, 'db close failed'));
62
+ process.exit(err ? 1 : 0);
63
+ });
64
+ server.closeIdleConnections();
65
+ }
66
+
67
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
68
+ process.on('SIGINT', () => shutdown('SIGINT'));
69
+ ```
70
+
71
+ ## Fastify: schema-validated route and shutdown
72
+
73
+ ```ts
74
+ import Fastify from 'fastify';
75
+
76
+ const app = Fastify({ logger: { redact: ['req.headers.authorization'] }, bodyLimit: 100_000 });
77
+
78
+ app.post('/orders', {
79
+ schema: {
80
+ body: {
81
+ type: 'object',
82
+ required: ['items'],
83
+ additionalProperties: false, // stripped, not rejected, under Fastify's default ajv options
84
+ properties: { items: { type: 'array', minItems: 1, items: { type: 'string' } } },
85
+ },
86
+ response: { 201: { type: 'object', properties: { id: { type: 'string' } } } },
87
+ },
88
+ }, async (req, reply) => {
89
+ const order = await placeOrder(req.body);
90
+ return reply.code(201).send(order);
91
+ });
92
+
93
+ app.addHook('onClose', async () => { await db.end(); });
94
+
95
+ await app.listen({ port: Number(process.env.PORT ?? 3000), host: '0.0.0.0' });
96
+
97
+ for (const signal of ['SIGTERM', 'SIGINT'] as const) {
98
+ process.once(signal, async () => {
99
+ setTimeout(() => process.exit(1), 25_000).unref();
100
+ await app.close();
101
+ process.exit(0);
102
+ });
103
+ }
104
+ ```
105
+
106
+ ## Streaming a file download
107
+
108
+ ```ts
109
+ import { createReadStream } from 'node:fs';
110
+ import { pipeline } from 'node:stream/promises';
111
+
112
+ app.get('/exports/:id', async (req, res, next) => {
113
+ try {
114
+ const path = await exportPathFor(req.params.id); // validates ownership, throws AppError(404)
115
+ res.setHeader('Content-Type', 'text/csv');
116
+ res.setHeader('Content-Disposition', `attachment; filename="export-${req.params.id}.csv"`);
117
+ await pipeline(createReadStream(path), res);
118
+ } catch (err) {
119
+ if (!res.headersSent) next(err); else res.destroy(err as Error);
120
+ }
121
+ });
122
+ ```
@@ -0,0 +1,103 @@
1
+ ---
2
+ name: nuxt-patterns
3
+ description: Nuxt 3 and 4 full-stack app guidance for choosing SSR, SPA, prerender or hybrid route rules, fetching data once with useFetch and useAsyncData instead of double-fetching with $fetch, writing Nitro server routes, wiring runtimeConfig secrets, setting SEO metadata, and tracking down hydration mismatches. Use when working in a Nuxt project on pages, server/api endpoints, nuxt.config, data loading, meta tags, or a server-versus-client rendering bug.
4
+ license: MIT
5
+ compatibility: Any Agent Skills host. Targets Nuxt 3.x and 4.x; Nuxt 4 differences are called out. Needs the project's package manager to run the dev server or build.
6
+ metadata:
7
+ kete-loom/version: "0.1.0"
8
+ kete-loom/category: coding
9
+ kete-loom/risk: runs-commands
10
+ kete-loom/owner: "Kete-org"
11
+ ---
12
+
13
+ # Nuxt patterns
14
+
15
+ Build Nuxt pages that render the same on server and client, fetch each piece of data once, and keep secrets on the server. For plain Vue component reactivity, props and Pinia, also follow `vue-patterns`.
16
+
17
+ ## 0. Orient first
18
+
19
+ 1. Read `package.json` for the Nuxt major version and modules (`@pinia/nuxt`, `@nuxtjs/i18n`, `@nuxt/content`, `@nuxt/image`).
20
+ 2. Check the layout. Nuxt 4 uses `app/` as `srcDir` (so `~/components` is `app/components`), keeps `server/`, `public/` and `shared/` at the root. A Nuxt 4 project may keep the old layout with `srcDir: '.'`. Follow what is there.
21
+ 3. Read `nuxt.config.ts` for `ssr`, `routeRules`, `runtimeConfig`, `nitro.preset` and `app.head`.
22
+
23
+ ## 1. Pick a rendering mode per route
24
+
25
+ | Need | Setting |
26
+ |---|---|
27
+ | Personalised or SEO-critical dynamic page | default SSR |
28
+ | Marketing page, docs, blog post | `routeRules: { '/blog/**': { prerender: true } }` |
29
+ | Mostly static but changes hourly | `{ swr: 3600 }` or `{ isr: 3600 }` on hosts that support ISR |
30
+ | Dashboard behind login, no SEO | `{ ssr: false }` for that route |
31
+ | Whole app is a client-only SPA | `ssr: false` at the top level |
32
+
33
+ Prefer per-route `routeRules` over flipping the global `ssr` flag. Confirm the deploy target supports the rule (`isr` is host-specific).
34
+
35
+ ## 2. Fetch data once
36
+
37
+ 1. In page or component setup, use `useFetch('/api/x')` for a URL, or `useAsyncData('key', () => fn())` for any async function. Both run on the server, serialise the result into the payload, and reuse it on hydration.
38
+ 2. Never call bare `$fetch` at the top of setup in an SSR page: it runs on the server and again on the client. Use `$fetch` only in event handlers, actions, server routes, or inside `useAsyncData`.
39
+ 3. Give `useAsyncData` a stable, unique key. Calls sharing a key share state; in Nuxt 4 they must not pass conflicting `transform`, `pick`, `default` or `getCachedData` options.
40
+ 4. Make reactive inputs explicit: `useFetch('/api/items', { query: { page } })` with `page` a ref refetches automatically; for `useAsyncData` pass `watch: [page]`.
41
+ 5. Handle all states in the template with `status` (`'idle' | 'pending' | 'success' | 'error'`) and `error`, not only `data`.
42
+ 6. Nuxt 4 changes: `data` and `error` default to `undefined` (Nuxt 3: `null`), and `data` is a `shallowRef`, so mutating a nested field does not re-render. Replace the object, or pass `deep: true`.
43
+ 7. Use `lazy: true` (or `useLazyFetch`) when the page should render before the data arrives, and show a pending state.
44
+ 8. Use `server: false` only for data that must never be fetched on the server (browser-only APIs, per-user data on a cached page).
45
+ 9. During SSR, `useFetch` with a relative URL to your own API forwards the incoming request's cookies and headers (except hop-by-hop ones such as `host`). A bare `$fetch` in a composable does not; use `useRequestFetch()` when you need the incoming request's headers.
46
+ 10. For one-time app init (load the current user), use `callOnce` or a plugin, not a `useFetch` in `app.vue` that runs on every navigation.
47
+
48
+ ## 3. Server routes (Nitro)
49
+
50
+ - Files in `server/api/` map to `/api/...`; method suffixes (`users.post.ts`) restrict the verb. `server/routes/` maps without the `/api` prefix. `server/middleware/` runs on every request, so keep it cheap.
51
+ - Use `defineEventHandler`, read input with `getQuery`, `readBody`, `getRouterParam`, and validate it (for example `readValidatedBody(event, schema.parse)`) before use.
52
+ - Throw `createError({ statusCode: 404, statusMessage: 'Not found' })` instead of returning error objects with a 200.
53
+ - Check authentication and authorization in every handler that touches private data. A route in `server/api` is public unless you guard it.
54
+ - Put code shared by server and client in `shared/` (Nuxt 4) and never import server-only modules from `app/`.
55
+
56
+ ## 4. Runtime config and secrets
57
+
58
+ ```ts
59
+ export default defineNuxtConfig({
60
+ runtimeConfig: {
61
+ apiSecret: '', // server only, override with NUXT_API_SECRET
62
+ public: { apiBase: '/api' }, // shipped to the browser, override with NUXT_PUBLIC_API_BASE
63
+ },
64
+ })
65
+ ```
66
+
67
+ - Read with `useRuntimeConfig()` (in server handlers, `useRuntimeConfig(event)`).
68
+ - Anything under `public` is visible to every visitor. Secrets go at the top level only.
69
+ - Set values in production through `NUXT_`-prefixed environment variables; `process.env` reads in `nuxt.config` are baked in at build time.
70
+
71
+ ## 5. SEO and head
72
+
73
+ - Use `useSeoMeta({ title, description, ogTitle, ogImage })` per page, and `useHead` for links, scripts and `htmlAttrs: { lang }`.
74
+ - Set defaults in `app.head` in `nuxt.config.ts`; let pages override.
75
+ - Generate canonical URLs and `og:url` from a configured site URL, not from `window.location`.
76
+ - SEO tags only reach crawlers on SSR or prerendered routes; do not rely on them for `ssr: false` routes.
77
+
78
+ ## 6. Hydration mismatches
79
+
80
+ When the console shows "Hydration mismatch" or "Hydration completed but contains mismatches":
81
+
82
+ 1. Reproduce with a production build (`nuxt build` then `nuxt preview`) and read the warning for the node that differs.
83
+ 2. Look for values that differ between server and client: `Date.now()`, `new Date().toLocaleString()`, `Math.random()`, time zones, `window`/`localStorage` reads during render, and user-agent checks.
84
+ 3. Fix by computing the value once on the server and passing it through `useState` or fetched data, by moving browser-only reads into `onMounted`, or by wrapping the subtree in `<ClientOnly>` with a fallback.
85
+ 4. Guard browser-only code with `import.meta.client` (or `.server`), not `process.client`.
86
+ 5. Check for invalid HTML nesting (a `<div>` inside a `<p>`, nested `<a>`, a `<tr>` without `<tbody>`); browsers rewrite it and the DOM no longer matches.
87
+ 6. For cross-request state, use `useState('key', () => init)` instead of a module-level `ref`, which leaks between users on the server.
88
+
89
+ ## Stop and ask the user
90
+
91
+ - Changing the rendering mode or `nitro.preset` of a route already in production.
92
+ - A secret is currently under `runtimeConfig.public` or committed in `nuxt.config`: flag it and ask before rotating anything.
93
+ - The fix needs a Nuxt major upgrade.
94
+
95
+ ## Never
96
+
97
+ - Never put API keys in `runtimeConfig.public` or client code.
98
+ - Never hide a hydration warning with `<ClientOnly>` around a whole page without finding the cause.
99
+ - Never leave a `server/api` handler without input validation or an auth check when it touches user data.
100
+
101
+ ## Report back
102
+
103
+ State the files changed, the rendering mode of each touched route, how data is fetched there, the command you ran (`nuxt build`, tests, typecheck) and its result, and anything not checked in a production build.
@@ -0,0 +1,16 @@
1
+ {
2
+ "should_trigger": [
3
+ "My Nuxt page calls the API twice, once on the server and again in the browser",
4
+ "Prerender the blog section of our Nuxt app but keep the dashboard client-only",
5
+ "How do I keep the Stripe secret out of the browser using Nuxt runtimeConfig?",
6
+ "Add a server/api endpoint in Nuxt that validates the POST body",
7
+ "Fix the hydration mismatch on my Nuxt product page caused by the date"
8
+ ],
9
+ "should_not_trigger": [
10
+ "Why does my Vue 3 composable lose reactivity when I destructure it?",
11
+ "Review my Nuxt pull request for missing watcher cleanup",
12
+ "Write a SvelteKit form action that validates the signup form",
13
+ "Set up getServerSideProps in my Next.js page",
14
+ "Write a Pinia store for user preferences in a plain Vite Vue app"
15
+ ]
16
+ }
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: parallel-agent-orchestration
3
+ description: Fans a large coding job out to several subagents at once and merges the results. Covers cutting the job into independent units with disjoint file ownership, writing a self-contained brief and return contract for each worker, isolating writers in separate git worktrees or branches, and checking every result before integrating it. Use when work splits naturally into pieces that can run side by side (many modules, many packages, a survey plus several fixes), or when the user asks to run agents in parallel or orchestrate a team of subagents.
4
+ license: MIT
5
+ compatibility: Any Agent Skills host that can start subagents. Worktree isolation needs git 2.17 or later (for git worktree remove).
6
+ metadata:
7
+ kete-loom/version: "0.1.0"
8
+ kete-loom/category: meta
9
+ kete-loom/risk: runs-commands
10
+ kete-loom/requires: "git"
11
+ kete-loom/owner: "Kete-org"
12
+ ---
13
+
14
+ # Parallel agent orchestration
15
+
16
+ You are the orchestrator. Workers do the units; you own the plan, the contracts, the merge and the final verification. Parallelism only pays when units are truly independent. Two agents editing the same file is a merge conflict you chose.
17
+
18
+ ## 1. Decide whether to parallelise
19
+
20
+ Parallelise only if all are true:
21
+
22
+ - The work splits into 2 or more units that don't need each other's output to start.
23
+ - Each unit can be checked on its own (a test, a lint, a build, a clear review question).
24
+ - The units touch different files, or are read-only.
25
+
26
+ Otherwise do it sequentially, or run a read-only survey in parallel and the edits in sequence. If the split is unclear, **stop and propose the split to the user** before starting workers.
27
+
28
+ ## 2. Cut the units
29
+
30
+ 1. Search the codebase to map what each unit touches. Write the plan as a table:
31
+
32
+ | Unit | Goal | Owns (may edit) | May read | Check command |
33
+ |---|---|---|---|---|
34
+
35
+ 2. Give every file at most one owner. Shared files (lockfiles, `package.json`, route tables, barrel `index` files, generated catalogs, changelogs) belong to **you**: workers report what they need there and you apply it at merge time.
36
+ 3. Do shared groundwork first and yourself: interfaces, types, config that several units depend on. Commit it, then start workers from that commit.
37
+ 4. Keep the count small. Three to six workers is easier to verify than twenty. More units than that: run in waves.
38
+
39
+ ## 3. Isolate writers
40
+
41
+ - Read-only workers (search, review, research) can share the working tree.
42
+ - Writing workers each get their own worktree and branch, so they can't clobber each other:
43
+
44
+ ```
45
+ git worktree add -b work/<unit> ../<repo>-<unit> <base-sha>
46
+ ```
47
+
48
+ Start every worktree from the groundwork commit of step 2. If the host creates worktrees for its subagents itself, use those instead. Remove them after merging with `git worktree remove ../<repo>-<unit>`.
49
+ - If the host runs workers in the same tree, ownership from step 2 is the only protection: state it in every brief.
50
+
51
+ ## 4. Write each brief
52
+
53
+ A worker starts with none of your context. Every brief contains:
54
+
55
+ ```
56
+ Goal: <one sentence, the outcome>
57
+ Context: <the 3-6 facts it needs: paths, conventions, decisions already made>
58
+ You own: <files/folders you may edit>. Do not edit anything else.
59
+ Do not: <push / commit anywhere but work/<unit> / install dependencies / touch shared files / ask the user>
60
+ Done when: <check command> passes and <acceptance criteria>
61
+ Return exactly:
62
+ status: done | blocked | partial
63
+ changed: list of paths
64
+ check: <command> -> <result>
65
+ needs_from_orchestrator: shared-file changes or decisions, or "none"
66
+ notes: max 5 lines
67
+ ```
68
+
69
+ Ask for a fixed, short return format. Free-form essays are hard to merge and eat your context.
70
+
71
+ ## 5. Run and watch
72
+
73
+ - Start the independent workers together. Don't start a worker whose inputs aren't ready.
74
+ - If a worker returns `blocked`, decide yourself or **ask the user**; don't let it guess.
75
+ - Don't re-run a failed worker with the same brief. Fix the brief (missing context, wrong ownership) first.
76
+
77
+ ## 6. Verify every result
78
+
79
+ Treat worker output as a claim, not a fact:
80
+
81
+ 1. Check `changed` against ownership: `git diff --name-only <base-sha>..work/<unit>`, plus `git -C ../<repo>-<unit> status --short` for anything left uncommitted. Any file outside its ownership: reject or redo that part.
82
+ 2. Re-run the unit's check command yourself.
83
+ 3. Read the diff. Look for skipped or deleted tests, stubs, TODOs and silent scope creep.
84
+
85
+ ## 7. Merge
86
+
87
+ 1. Merge units one at a time onto the integration branch, in dependency order.
88
+ 2. Apply the `needs_from_orchestrator` changes to shared files yourself.
89
+ 3. After each merge run the affected checks; after the last, run the project's full check (tests, lint, build).
90
+ 4. On a conflict, resolve it yourself with both briefs in mind, or redo the smaller unit on top of the merged result. Never pick a side blindly.
91
+
92
+ ## Report
93
+
94
+ ```
95
+ Units: <n> (<n> done, <n> redone, <n> dropped)
96
+ Merged: <branch> at <sha>
97
+ Full check: <command> -> <result>
98
+ Left over: <blocked items and open questions>
99
+ ```
100
+
101
+ ## Never
102
+
103
+ - Never give two workers write access to the same file.
104
+ - Never merge a unit you haven't re-checked yourself.
105
+ - Never let workers commit to the integration branch or push.
106
+ - Never parallelise a task whose steps depend on each other's results just to go faster.
@@ -0,0 +1,14 @@
1
+ {
2
+ "should_trigger": [
3
+ "Spin up several subagents to migrate each of our 8 packages to ESM at the same time",
4
+ "Split this big refactor across parallel workers without them stepping on each other's files",
5
+ "Run one agent per service to add health checks, then merge everything into one branch",
6
+ "How should I divide this job between a team of agents and combine what they produce"
7
+ ],
8
+ "should_not_trigger": [
9
+ "Keep retrying the flaky integration test fix overnight until CI is green",
10
+ "Design the message queue so our worker processes handle jobs concurrently",
11
+ "The context keeps overflowing when I read this monorepo, help me budget it",
12
+ "Plan the implementation steps for the new invoicing feature"
13
+ ]
14
+ }
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: paystack-integration
3
+ description: Builds and reviews Paystack payment integrations, including transaction initialisation, server-side verification, webhooks with HMAC-SHA512 signature checks, idempotent fulfilment, and Ghana mobile money (MTN, Telecel, AT) charges. Use when adding Paystack checkout, card or mobile money payments, handling Paystack webhooks, or debugging a Paystack payment flow.
4
+ license: MIT
5
+ compatibility: Any Agent Skills host. Needs network access to api.paystack.co for live calls. Examples are TypeScript (Node 18+); the rules apply to any backend language.
6
+ metadata:
7
+ kete-loom/version: "0.1.0"
8
+ kete-loom/category: integrations
9
+ kete-loom/risk: network
10
+ kete-loom/owner: "Kete-org"
11
+ ---
12
+
13
+ # Paystack integration
14
+
15
+ Build a payment flow that can't be faked from the browser and never gives value twice. These rules cover cards, bank and mobile money.
16
+
17
+ ## Ground rules
18
+
19
+ 1. **The secret key stays on the server.** Read it from the environment (`PAYSTACK_SECRET_KEY`). Only the public key (`pk_...`) may reach the browser or app.
20
+ 2. **Amounts are in the currency's subunit**: pesewas for GHS, kobo for NGN, cents for USD, KES and ZAR. GHS 25.50 is `2550`. Use integers, never floats.
21
+ 3. **The server decides the price.** Compute the amount from your own database (order total, plan price). Never trust an amount sent by the client.
22
+ 4. **Value is given only after verification.** Either a webhook with a valid signature, or a call to the Verify Transaction endpoint, must confirm `status: "success"` **and** that the amount and currency match what you expected.
23
+ 5. **Fulfilment is idempotent.** Store the `reference` with a unique constraint and process each reference once, because Paystack retries webhooks and users refresh pages.
24
+ 6. **Test keys first.** Use `sk_test_` / `pk_test_` until the full flow, including failures, works end to end.
25
+
26
+ ## Flow
27
+
28
+ ```
29
+ client ──(order id)──▶ server: create order, compute amount, POST /transaction/initialize
30
+ server ──(authorization_url or access_code)──▶ client: redirect or open Popup
31
+ customer pays on Paystack
32
+ Paystack ──(charge.success webhook)──▶ server: verify signature, verify amount, fulfil once
33
+ client returns to callback_url ──▶ server: GET /transaction/verify/:reference, show result
34
+ ```
35
+
36
+ The callback page only *shows* status. Fulfilment happens in the webhook (or a verify call), guarded by the idempotency check, so it is safe whichever arrives first.
37
+
38
+ ## Implementation steps
39
+
40
+ 1. **Initialise** with `POST https://api.paystack.co/transaction/initialize`, `Authorization: Bearer <secret key>`, and a body containing `email`, `amount` (subunit), `currency`, a `reference` you generate and store, `callback_url`, and `metadata` (for example the order id). See `references/api.md` for request and response shapes.
41
+ 2. **Webhook endpoint**: follow `references/webhooks.md` exactly. Verify the signature on the **raw** body, return `200` quickly, and do the work asynchronously.
42
+ 3. **Verify** with `GET https://api.paystack.co/transaction/verify/:reference` on the callback page and as a fallback when no webhook arrives.
43
+ 4. **Mobile money (Ghana)**: use the Charge API or the standard checkout, and follow `references/mobile-money.md` for the `pay_offline` pending flow and the 180-second approval window.
44
+ 5. **Persist** an order and payment record holding `reference`, expected amount and currency, status, the Paystack transaction id, and timestamps.
45
+
46
+ ## Reviewing an existing integration
47
+
48
+ Check each ground rule above, then look for these common bugs:
49
+
50
+ - Signature computed over re-serialised JSON instead of the raw bytes.
51
+ - Webhook does heavy work before responding, which causes timeouts and duplicate retries.
52
+ - Fulfilment keyed on `id` from the client, or done in the callback page without verification.
53
+ - Float arithmetic on amounts (`25.5 * 100`), or amounts in cedis instead of pesewas.
54
+ - Secret key committed in code or exposed in a `NEXT_PUBLIC_`/`VITE_` variable.
55
+ - No handling for `failed`, `abandoned` or `reversed` transactions.
56
+
57
+ ## Before you finish
58
+
59
+ Confirm field names and endpoints against Paystack's current docs at paystack.com/docs when generating new code; this skill gives the rules and the shape, the docs are the final word on the API. Then test: successful payment, declined payment, a duplicate webhook delivery, a webhook with a bad signature (must be rejected), and mobile money approval timing out.
@@ -0,0 +1,14 @@
1
+ {
2
+ "should_trigger": [
3
+ "Add Paystack checkout to our Next.js shop so Ghanaian customers can pay",
4
+ "My Paystack webhook says invalid signature every time, can you fix it?",
5
+ "Let users pay with MTN MoMo through Paystack in the mobile app",
6
+ "Customers are getting their orders twice when Paystack retries the webhook"
7
+ ],
8
+ "should_not_trigger": [
9
+ "Integrate Stripe subscriptions into the billing page",
10
+ "Review my last commit for security vulnerabilities",
11
+ "Write a blog post about mobile money adoption in West Africa",
12
+ "Build an invoice PDF generator for the accounting module"
13
+ ]
14
+ }
@@ -0,0 +1,73 @@
1
+ # Paystack API shapes used by this skill
2
+
3
+ Base URL: `https://api.paystack.co`. All server calls send `Authorization: Bearer <PAYSTACK_SECRET_KEY>` and `Content-Type: application/json`. Responses look like `{ "status": true, "message": "...", "data": { ... } }`; `status` there means the API call succeeded, not that a payment did.
4
+
5
+ Confirm fields against paystack.com/docs/api before shipping; this is a working summary.
6
+
7
+ ## Initialise a transaction
8
+
9
+ `POST /transaction/initialize`
10
+
11
+ ```json
12
+ {
13
+ "email": "customer@example.com",
14
+ "amount": 2550,
15
+ "currency": "GHS",
16
+ "reference": "ord_8f2c1a",
17
+ "callback_url": "https://app.example.com/pay/callback",
18
+ "channels": ["card", "mobile_money"],
19
+ "metadata": { "order_id": "8f2c1a" }
20
+ }
21
+ ```
22
+
23
+ Response `data`: `authorization_url` (redirect the customer here), `access_code` (for the inline Popup), `reference`.
24
+
25
+ ## Verify a transaction
26
+
27
+ `GET /transaction/verify/:reference`
28
+
29
+ Check in `data`:
30
+
31
+ | Field | Expect |
32
+ |---|---|
33
+ | `status` | `"success"` (others: `failed`, `abandoned`, `reversed`, `pending`) |
34
+ | `amount` | equals the amount you stored, in subunits |
35
+ | `currency` | equals the currency you stored |
36
+ | `reference` | the reference you generated |
37
+ | `id` | Paystack transaction id; store it |
38
+ | `channel` | `card`, `mobile_money`, `bank`, ... |
39
+
40
+ ## Minimal server client (TypeScript)
41
+
42
+ ```ts
43
+ const BASE = 'https://api.paystack.co';
44
+
45
+ async function paystack<T>(path: string, init: RequestInit = {}): Promise<T> {
46
+ const res = await fetch(`${BASE}${path}`, {
47
+ ...init,
48
+ headers: {
49
+ authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
50
+ 'content-type': 'application/json',
51
+ ...init.headers,
52
+ },
53
+ });
54
+ const body = await res.json();
55
+ if (!res.ok || body.status !== true) throw new Error(`Paystack ${path} failed: ${body.message ?? res.status}`);
56
+ return body.data as T;
57
+ }
58
+
59
+ export const initialize = (input: object) =>
60
+ paystack<{ authorization_url: string; access_code: string; reference: string }>('/transaction/initialize', {
61
+ method: 'POST',
62
+ body: JSON.stringify(input),
63
+ });
64
+
65
+ export const verify = (reference: string) =>
66
+ paystack<{ status: string; amount: number; currency: string; reference: string; id: number }>(
67
+ `/transaction/verify/${encodeURIComponent(reference)}`
68
+ );
69
+ ```
70
+
71
+ ## References
72
+
73
+ Generate references on the server, unique per payment attempt (for example `ord_<orderId>_<random>`). A retry after a failure gets a new reference; the order keeps a list of attempts.
@@ -0,0 +1,50 @@
1
+ # Ghana mobile money with Paystack
2
+
3
+ Two ways to accept mobile money:
4
+
5
+ 1. **Standard checkout** (simplest): initialise a transaction with `currency: "GHS"`, and optionally `channels: ["mobile_money"]`. The customer picks the network and approves on their phone. Fulfil from the webhook as usual.
6
+ 2. **Charge API** (your own UI): `POST https://api.paystack.co/charge` with your secret key.
7
+
8
+ ## Charge API request
9
+
10
+ ```json
11
+ {
12
+ "email": "customer@example.com",
13
+ "amount": 2550,
14
+ "currency": "GHS",
15
+ "reference": "ord_8f2c1a",
16
+ "mobile_money": {
17
+ "phone": "0551234987",
18
+ "provider": "mtn"
19
+ }
20
+ }
21
+ ```
22
+
23
+ Provider codes for Ghana:
24
+
25
+ | Code | Network |
26
+ |---|---|
27
+ | `mtn` | MTN Mobile Money |
28
+ | `vod` | Telecel Cash |
29
+ | `atl` | AT Money / Airtel Money |
30
+
31
+ `amount` is in pesewas (`2550` = GHS 25.50).
32
+
33
+ ## Pending flow
34
+
35
+ 1. The response usually has `data.status: "pay_offline"` and a `data.display_text`. Show that text to the customer ("Approve the prompt on your phone...").
36
+ 2. The customer has about **180 seconds** to approve on their handset; the networks set this limit.
37
+ 3. Wait for the `charge.success` webhook. Do not fulfil on the initial response.
38
+ 4. If no webhook arrives within ~180 seconds, call `GET /transaction/verify/:reference`. A failure reason is in `data.message`. Let the customer retry with a **new** reference.
39
+
40
+ Other statuses you may see on the charge response include `send_otp` and `pending`; follow the `display_text` and the Charge API docs for the submit endpoint each one requires.
41
+
42
+ ## UX notes
43
+
44
+ - Validate the phone number format before calling the API, and make sure the chosen provider matches the number's network.
45
+ - Show a countdown and a "Check status" button rather than polling Paystack aggressively.
46
+ - Make clear that the customer must approve on their phone, and that no PIN is ever entered in your app.
47
+
48
+ ## Testing
49
+
50
+ Use test keys and the mobile money test numbers on Paystack's Test Payments page. Cover approved, declined and timed-out approvals.
@@ -0,0 +1,80 @@
1
+ # Paystack webhooks
2
+
3
+ ## What Paystack sends
4
+
5
+ - `POST` with a JSON body, for example `{ "event": "charge.success", "data": { ... } }`.
6
+ - Header `x-paystack-signature`: HMAC SHA512 of the request body, keyed with your **secret key**, hex encoded.
7
+ - Paystack treats anything other than `200 OK` as a failed delivery and retries. In live mode it retries every 3 minutes for the first 4 tries, then hourly for 72 hours.
8
+ - Paystack documents a fixed set of source IPs you can additionally allow-list; check the webhooks page for the current list.
9
+
10
+ ## Handler rules
11
+
12
+ 1. Read the **raw body bytes** before any JSON parsing. Re-serialising parsed JSON changes whitespace and key order and breaks the signature.
13
+ 2. Compute `HMAC_SHA512(secretKey, rawBody)` as hex and compare with the header using a **constant-time** comparison. Mismatch → respond `401` and stop.
14
+ 3. Respond `200` immediately after the signature check, then process the event asynchronously (a queue or background job).
15
+ 4. In the processor: look up your order by `data.reference`, check `data.status === "success"`, and check `data.amount` and `data.currency` equal what you stored. Then mark the order paid **in the same transaction** as an idempotency check (unique `reference`, or `UPDATE ... WHERE status = 'pending'`).
16
+ 5. Log the event id and reference. Never log card data or the secret key.
17
+
18
+ ## Express (TypeScript)
19
+
20
+ ```ts
21
+ import crypto from 'node:crypto';
22
+ import express from 'express';
23
+
24
+ const app = express();
25
+ const SECRET = process.env.PAYSTACK_SECRET_KEY!;
26
+
27
+ app.post('/webhooks/paystack', express.raw({ type: 'application/json' }), (req, res) => {
28
+ const signature = req.header('x-paystack-signature') ?? '';
29
+ const expected = crypto.createHmac('sha512', SECRET).update(req.body).digest('hex');
30
+ const valid =
31
+ signature.length === expected.length &&
32
+ crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
33
+ if (!valid) return res.sendStatus(401);
34
+
35
+ res.sendStatus(200); // acknowledge first
36
+ const event = JSON.parse(req.body.toString('utf8'));
37
+ queue.add('paystack-event', event); // process asynchronously and idempotently
38
+ });
39
+ ```
40
+
41
+ ## Next.js route handler
42
+
43
+ ```ts
44
+ import crypto from 'node:crypto';
45
+
46
+ export async function POST(req: Request) {
47
+ const raw = Buffer.from(await req.arrayBuffer());
48
+ const signature = req.headers.get('x-paystack-signature') ?? '';
49
+ const expected = crypto.createHmac('sha512', process.env.PAYSTACK_SECRET_KEY!).update(raw).digest('hex');
50
+ if (signature.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
51
+ return new Response('invalid signature', { status: 401 });
52
+ }
53
+ const event = JSON.parse(raw.toString('utf8'));
54
+ await enqueue(event); // keep this fast
55
+ return new Response('ok', { status: 200 });
56
+ }
57
+ ```
58
+
59
+ ## Python (FastAPI)
60
+
61
+ ```python
62
+ import hashlib, hmac, os
63
+ from fastapi import FastAPI, Request, Response
64
+
65
+ app = FastAPI()
66
+ SECRET = os.environ["PAYSTACK_SECRET_KEY"].encode()
67
+
68
+ @app.post("/webhooks/paystack")
69
+ async def paystack_webhook(request: Request):
70
+ raw = await request.body()
71
+ expected = hmac.new(SECRET, raw, hashlib.sha512).hexdigest()
72
+ if not hmac.compare_digest(expected, request.headers.get("x-paystack-signature", "")):
73
+ return Response(status_code=401)
74
+ enqueue(raw) # process asynchronously and idempotently
75
+ return Response(status_code=200)
76
+ ```
77
+
78
+ ## Local testing
79
+
80
+ Webhooks can't reach `localhost`. Use a tunnel (for example `cloudflared` or `ngrok`) and set the tunnel URL in the Paystack dashboard for test mode. Use the dashboard's resend option, or the Webhook Events API, to replay an event and confirm your handler is idempotent.