oh-my-agent-commandcode 0.0.0-stage → 20.38.4

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 (334) hide show
  1. package/README.md +35 -2
  2. package/filter-test-output.sh +52 -0
  3. package/mods/oma.ts +347 -0
  4. package/package.json +30 -4
  5. package/skills/_shared/conditional/experiment-ledger.md +28 -0
  6. package/skills/_shared/conditional/exploration-loop.md +22 -0
  7. package/skills/_shared/conditional/quality-score.md +33 -0
  8. package/skills/_shared/core/anti-ai-prose.md +191 -0
  9. package/skills/_shared/core/api-contracts/README.md +28 -0
  10. package/skills/_shared/core/api-contracts/template.md +90 -0
  11. package/skills/_shared/core/clarification-protocol.md +23 -0
  12. package/skills/_shared/core/code-intelligence.md +46 -0
  13. package/skills/_shared/core/collaboration-policy.md +28 -0
  14. package/skills/_shared/core/common-checklist.md +28 -0
  15. package/skills/_shared/core/context-budget.md +22 -0
  16. package/skills/_shared/core/context-loading.md +34 -0
  17. package/skills/_shared/core/difficulty-guide.md +26 -0
  18. package/skills/_shared/core/execution-policy.md +31 -0
  19. package/skills/_shared/core/lessons-learned.md +33 -0
  20. package/skills/_shared/core/prompt-structure.md +18 -0
  21. package/skills/_shared/core/quality-principles.md +11 -0
  22. package/skills/_shared/core/session-metrics.md +35 -0
  23. package/skills/_shared/core/skill-routing.md +39 -0
  24. package/skills/_shared/core/test-approach.md +54 -0
  25. package/skills/_shared/core/vendor-detection.md +52 -0
  26. package/skills/_shared/runtime/memory-protocol.md +118 -0
  27. package/skills/_shared/runtime/result-contract.md +64 -0
  28. package/skills/_shared/ui-interaction-quality.md +133 -0
  29. package/skills/oma-academic-writing/SKILL.md +181 -0
  30. package/skills/oma-academic-writing/resources/abstract-guide.md +51 -0
  31. package/skills/oma-academic-writing/resources/academic-verb-tiers.md +42 -0
  32. package/skills/oma-academic-writing/resources/anti-ai-checklist.md +52 -0
  33. package/skills/oma-academic-writing/resources/hedging-guide.md +132 -0
  34. package/skills/oma-academic-writing/resources/sentence-structure-reference.md +151 -0
  35. package/skills/oma-architecture/SKILL.md +203 -0
  36. package/skills/oma-architecture/resources/api-evolution.md +41 -0
  37. package/skills/oma-architecture/resources/checklist.md +42 -0
  38. package/skills/oma-architecture/resources/execution-protocol.md +115 -0
  39. package/skills/oma-architecture/resources/methodology-selection.md +92 -0
  40. package/skills/oma-architecture/resources/migration-patterns.md +28 -0
  41. package/skills/oma-architecture/resources/output-templates.md +111 -0
  42. package/skills/oma-architecture/resources/stakeholder-synthesis.md +80 -0
  43. package/skills/oma-backend/SKILL.md +202 -0
  44. package/skills/oma-backend/resources/checklist.md +45 -0
  45. package/skills/oma-backend/resources/error-playbook.md +97 -0
  46. package/skills/oma-backend/resources/execution-protocol.md +39 -0
  47. package/skills/oma-backend/resources/orm-reference.md +61 -0
  48. package/skills/oma-backend/variants/node/api-template.ts +315 -0
  49. package/skills/oma-backend/variants/node/snippets.md +475 -0
  50. package/skills/oma-backend/variants/node/stack.yaml +21 -0
  51. package/skills/oma-backend/variants/node/tech-stack.md +33 -0
  52. package/skills/oma-backend/variants/python/api-template.py +378 -0
  53. package/skills/oma-backend/variants/python/snippets.md +199 -0
  54. package/skills/oma-backend/variants/python/stack.yaml +18 -0
  55. package/skills/oma-backend/variants/python/tech-stack.md +45 -0
  56. package/skills/oma-backend/variants/rust/api-template.rs +635 -0
  57. package/skills/oma-backend/variants/rust/snippets.md +471 -0
  58. package/skills/oma-backend/variants/rust/stack.yaml +20 -0
  59. package/skills/oma-backend/variants/rust/tech-stack.md +36 -0
  60. package/skills/oma-backend/variants/stack.schema.json +73 -0
  61. package/skills/oma-brainstorm/SKILL.md +166 -0
  62. package/skills/oma-brainstorm/resources/triz-lite.md +117 -0
  63. package/skills/oma-coordination/SKILL.md +171 -0
  64. package/skills/oma-db/SKILL.md +201 -0
  65. package/skills/oma-db/resources/anti-patterns.md +177 -0
  66. package/skills/oma-db/resources/checklist.md +67 -0
  67. package/skills/oma-db/resources/document-templates.md +107 -0
  68. package/skills/oma-db/resources/error-playbook.md +50 -0
  69. package/skills/oma-db/resources/examples.md +170 -0
  70. package/skills/oma-db/resources/execution-protocol.md +127 -0
  71. package/skills/oma-db/resources/iso-controls.md +98 -0
  72. package/skills/oma-db/resources/migration-playbook.md +93 -0
  73. package/skills/oma-db/resources/query-tuning.md +57 -0
  74. package/skills/oma-db/resources/vector-db.md +159 -0
  75. package/skills/oma-debug/SKILL.md +146 -0
  76. package/skills/oma-debug/resources/bug-report-template.md +327 -0
  77. package/skills/oma-debug/resources/checklist.md +31 -0
  78. package/skills/oma-debug/resources/common-patterns.md +734 -0
  79. package/skills/oma-debug/resources/debugging-checklist.md +364 -0
  80. package/skills/oma-debug/resources/error-playbook.md +94 -0
  81. package/skills/oma-debug/resources/execution-protocol.md +40 -0
  82. package/skills/oma-design/SKILL.md +135 -0
  83. package/skills/oma-design/resources/asset-strategy.md +34 -0
  84. package/skills/oma-design/resources/checklist.md +63 -0
  85. package/skills/oma-design/resources/data-grid.md +48 -0
  86. package/skills/oma-design/resources/design-md-spec.md +107 -0
  87. package/skills/oma-design/resources/design-tokens.md +53 -0
  88. package/skills/oma-design/resources/error-playbook.md +18 -0
  89. package/skills/oma-design/resources/execution-protocol.md +95 -0
  90. package/skills/oma-design/resources/external-design-skills.md +130 -0
  91. package/skills/oma-design/resources/getdesign-fetcher.md +104 -0
  92. package/skills/oma-design/resources/product-interface-review.md +59 -0
  93. package/skills/oma-design/resources/redesign-protocol.md +52 -0
  94. package/skills/oma-design/resources/stitch-integration.md +244 -0
  95. package/skills/oma-dev-workflow/SKILL.md +375 -0
  96. package/skills/oma-dev-workflow/resources/affected-checks.py +58 -0
  97. package/skills/oma-dev-workflow/resources/api-workflows.md +58 -0
  98. package/skills/oma-dev-workflow/resources/database-patterns.md +107 -0
  99. package/skills/oma-dev-workflow/resources/i18n-patterns.md +63 -0
  100. package/skills/oma-dev-workflow/resources/release-coordination.md +240 -0
  101. package/skills/oma-dev-workflow/resources/tooling-profiles.md +217 -0
  102. package/skills/oma-dev-workflow/resources/troubleshooting.md +78 -0
  103. package/skills/oma-dev-workflow/resources/validation-pipeline.md +246 -0
  104. package/skills/oma-docs/SKILL.md +77 -0
  105. package/skills/oma-docs/resources/commands.md +76 -0
  106. package/skills/oma-explanation/SKILL.md +212 -0
  107. package/skills/oma-explanation/resources/document-structure.md +115 -0
  108. package/skills/oma-explanation/resources/draft-format.md +199 -0
  109. package/skills/oma-explanation/resources/html-contract.md +102 -0
  110. package/skills/oma-frontend/SKILL.md +227 -0
  111. package/skills/oma-frontend/resources/angular-rules.md +110 -0
  112. package/skills/oma-frontend/resources/checklist.md +76 -0
  113. package/skills/oma-frontend/resources/error-playbook.md +125 -0
  114. package/skills/oma-frontend/resources/execution-protocol.md +46 -0
  115. package/skills/oma-frontend/resources/snippets.md +354 -0
  116. package/skills/oma-frontend/resources/specialized-visual-implementation.md +193 -0
  117. package/skills/oma-frontend/resources/tailwind-rules.md +83 -0
  118. package/skills/oma-frontend/resources/tech-stack.md +156 -0
  119. package/skills/oma-hwp/SKILL.md +166 -0
  120. package/skills/oma-hwp/config/hwp-config.yaml +35 -0
  121. package/skills/oma-hwp/resources/balanced-tables.ts +39 -0
  122. package/skills/oma-hwp/resources/execution-protocol.md +145 -0
  123. package/skills/oma-hwp/resources/flatten-tables.ts +101 -0
  124. package/skills/oma-hwp/resources/package.json +13 -0
  125. package/skills/oma-hwp/resources/troubleshooting.md +70 -0
  126. package/skills/oma-hwp/resources/tsconfig.json +10 -0
  127. package/skills/oma-hwp/resources/turndown-plugin-gfm.d.ts +9 -0
  128. package/skills/oma-image/SKILL.md +142 -0
  129. package/skills/oma-image/config/image-config.yaml +70 -0
  130. package/skills/oma-image/resources/checklist.md +18 -0
  131. package/skills/oma-image/resources/execution-protocol.md +117 -0
  132. package/skills/oma-image/resources/invocation.md +105 -0
  133. package/skills/oma-image/resources/prompt-tips.md +71 -0
  134. package/skills/oma-image/resources/vendor-matrix.md +90 -0
  135. package/skills/oma-market/SKILL.md +142 -0
  136. package/skills/oma-market/resources/checklist.md +12 -0
  137. package/skills/oma-market/resources/error-playbook.md +12 -0
  138. package/skills/oma-market/resources/execution-protocol.md +60 -0
  139. package/skills/oma-market/resources/frameworks/pestel.md +101 -0
  140. package/skills/oma-market/resources/frameworks/porters-5f.md +90 -0
  141. package/skills/oma-market/resources/frameworks/swot.md +61 -0
  142. package/skills/oma-market/resources/intent-rules.md +35 -0
  143. package/skills/oma-market/resources/output-laws.md +53 -0
  144. package/skills/oma-mobile/SKILL.md +168 -0
  145. package/skills/oma-mobile/resources/checklist.md +77 -0
  146. package/skills/oma-mobile/resources/error-playbook.md +198 -0
  147. package/skills/oma-mobile/resources/execution-protocol.md +47 -0
  148. package/skills/oma-mobile/resources/screen-template.dart +331 -0
  149. package/skills/oma-mobile/resources/screen-template.swift +239 -0
  150. package/skills/oma-mobile/resources/screen-template.tsx +179 -0
  151. package/skills/oma-mobile/resources/tech-stack.md +69 -0
  152. package/skills/oma-mobile/variants/README.md +45 -0
  153. package/skills/oma-mobile/variants/flutter/api-template.dart +461 -0
  154. package/skills/oma-mobile/variants/flutter/snippets.md +1353 -0
  155. package/skills/oma-mobile/variants/flutter/stack.yaml +21 -0
  156. package/skills/oma-mobile/variants/flutter/tech-stack.md +201 -0
  157. package/skills/oma-mobile/variants/kotlin-android/api-template.kt +182 -0
  158. package/skills/oma-mobile/variants/kotlin-android/snippets.md +871 -0
  159. package/skills/oma-mobile/variants/kotlin-android/stack.yaml +32 -0
  160. package/skills/oma-mobile/variants/kotlin-android/tech-stack.md +159 -0
  161. package/skills/oma-mobile/variants/react-native/api-template.ts +531 -0
  162. package/skills/oma-mobile/variants/react-native/snippets.md +1014 -0
  163. package/skills/oma-mobile/variants/react-native/stack.yaml +22 -0
  164. package/skills/oma-mobile/variants/react-native/tech-stack.md +192 -0
  165. package/skills/oma-mobile/variants/stack.schema.json +68 -0
  166. package/skills/oma-mobile/variants/swift-ios/api-template.swift +165 -0
  167. package/skills/oma-mobile/variants/swift-ios/snippets.md +1028 -0
  168. package/skills/oma-mobile/variants/swift-ios/stack.yaml +25 -0
  169. package/skills/oma-mobile/variants/swift-ios/tech-stack.md +174 -0
  170. package/skills/oma-observability/SKILL.md +225 -0
  171. package/skills/oma-observability/resources/anti-patterns.md +555 -0
  172. package/skills/oma-observability/resources/boundaries/cross-application.md +348 -0
  173. package/skills/oma-observability/resources/boundaries/multi-tenant.md +290 -0
  174. package/skills/oma-observability/resources/boundaries/release.md +372 -0
  175. package/skills/oma-observability/resources/boundaries/slo.md +225 -0
  176. package/skills/oma-observability/resources/checklist.md +123 -0
  177. package/skills/oma-observability/resources/examples.md +448 -0
  178. package/skills/oma-observability/resources/execution-protocol.md +166 -0
  179. package/skills/oma-observability/resources/incident-forensics.md +348 -0
  180. package/skills/oma-observability/resources/intent-rules.md +307 -0
  181. package/skills/oma-observability/resources/layers/L3-network.md +281 -0
  182. package/skills/oma-observability/resources/layers/L4-transport.md +299 -0
  183. package/skills/oma-observability/resources/layers/L7-application/crash-analytics.md +359 -0
  184. package/skills/oma-observability/resources/layers/L7-application/mobile-rum.md +278 -0
  185. package/skills/oma-observability/resources/layers/L7-application/waf.md +246 -0
  186. package/skills/oma-observability/resources/layers/L7-application/web-rum.md +350 -0
  187. package/skills/oma-observability/resources/layers/mesh.md +368 -0
  188. package/skills/oma-observability/resources/matrix.md +177 -0
  189. package/skills/oma-observability/resources/meta-observability.md +605 -0
  190. package/skills/oma-observability/resources/observability-as-code.md +666 -0
  191. package/skills/oma-observability/resources/signals/audit.md +310 -0
  192. package/skills/oma-observability/resources/signals/cost.md +303 -0
  193. package/skills/oma-observability/resources/signals/logs.md +263 -0
  194. package/skills/oma-observability/resources/signals/metrics.md +478 -0
  195. package/skills/oma-observability/resources/signals/privacy.md +351 -0
  196. package/skills/oma-observability/resources/signals/profiles.md +245 -0
  197. package/skills/oma-observability/resources/signals/traces.md +360 -0
  198. package/skills/oma-observability/resources/standards.md +214 -0
  199. package/skills/oma-observability/resources/transport/collector-topology.md +308 -0
  200. package/skills/oma-observability/resources/transport/otlp-grpc-vs-http.md +256 -0
  201. package/skills/oma-observability/resources/transport/sampling-recipes.md +297 -0
  202. package/skills/oma-observability/resources/transport/udp-statsd-mtu.md +167 -0
  203. package/skills/oma-observability/resources/vendor-categories.md +352 -0
  204. package/skills/oma-orchestration/SKILL.md +302 -0
  205. package/skills/oma-orchestration/config/cli-config.yaml +92 -0
  206. package/skills/oma-orchestration/resources/memory-schema.md +232 -0
  207. package/skills/oma-orchestration/resources/subagent-prompt-template.md +30 -0
  208. package/skills/oma-orchestration/scripts/parallel-run.sh +6 -0
  209. package/skills/oma-orchestration/scripts/spawn-agent.sh +5 -0
  210. package/skills/oma-orchestration/scripts/verify.sh +20 -0
  211. package/skills/oma-orchestration/templates/backend-task.md +19 -0
  212. package/skills/oma-orchestration/templates/debug-task.md +16 -0
  213. package/skills/oma-orchestration/templates/frontend-task.md +18 -0
  214. package/skills/oma-orchestration/templates/mobile-task.md +18 -0
  215. package/skills/oma-orchestration/templates/qa-task.md +16 -0
  216. package/skills/oma-orchestration/templates/tasks-example.yaml +15 -0
  217. package/skills/oma-pdf/SKILL.md +162 -0
  218. package/skills/oma-pdf/config/pdf-config.yaml +27 -0
  219. package/skills/oma-pdf/resources/execution-protocol.md +127 -0
  220. package/skills/oma-pm/SKILL.md +157 -0
  221. package/skills/oma-pm/resources/error-playbook.md +75 -0
  222. package/skills/oma-pm/resources/examples.md +439 -0
  223. package/skills/oma-pm/resources/execution-protocol.md +62 -0
  224. package/skills/oma-pm/resources/iso-planning.md +110 -0
  225. package/skills/oma-pm/resources/plan-phase-protocol.md +66 -0
  226. package/skills/oma-pm/resources/task-template.json +86 -0
  227. package/skills/oma-qa/SKILL.md +141 -0
  228. package/skills/oma-qa/resources/checklist.md +339 -0
  229. package/skills/oma-qa/resources/error-playbook.md +98 -0
  230. package/skills/oma-qa/resources/execution-protocol.md +169 -0
  231. package/skills/oma-qa/resources/iso-quality.md +82 -0
  232. package/skills/oma-qa/resources/verify-ship-protocol.md +116 -0
  233. package/skills/oma-recap/SKILL.md +245 -0
  234. package/skills/oma-recap/resources/output-formats.md +83 -0
  235. package/skills/oma-refactor/SKILL.md +184 -0
  236. package/skills/oma-refactor/resources/definition.md +108 -0
  237. package/skills/oma-refactor/resources/governance.md +33 -0
  238. package/skills/oma-refactor/resources/hotspots.py +44 -0
  239. package/skills/oma-refactor/resources/measurement.md +93 -0
  240. package/skills/oma-scholar/SKILL.md +276 -0
  241. package/skills/oma-scholar/config/scholar-config.yaml +79 -0
  242. package/skills/oma-scholar/resources/api-endpoints.md +174 -0
  243. package/skills/oma-scholar/resources/checklist.md +92 -0
  244. package/skills/oma-scholar/resources/execution-protocol.md +224 -0
  245. package/skills/oma-scholar/resources/fallback-providers.md +143 -0
  246. package/skills/oma-scholar/resources/setup-openalex.md +85 -0
  247. package/skills/oma-scholar/resources/sidecar-spec.md +341 -0
  248. package/skills/oma-scholar/resources/upstream-spec-cache.md +972 -0
  249. package/skills/oma-scm/SKILL.md +103 -0
  250. package/skills/oma-scm/resources/cm-operations.md +56 -0
  251. package/skills/oma-scm/resources/codeowners-playbook.md +45 -0
  252. package/skills/oma-scm/resources/conventional-commits.md +108 -0
  253. package/skills/oma-scm/resources/merge-risk.md +76 -0
  254. package/skills/oma-scm/resources/onboarding-risk-signals.md +71 -0
  255. package/skills/oma-search/SKILL.md +125 -0
  256. package/skills/oma-search/resources/checklist.md +36 -0
  257. package/skills/oma-search/resources/error-playbook.md +66 -0
  258. package/skills/oma-search/resources/examples.md +80 -0
  259. package/skills/oma-search/resources/execution-protocol.md +86 -0
  260. package/skills/oma-search/resources/intent-rules.md +47 -0
  261. package/skills/oma-search/resources/trust-registry.md +53 -0
  262. package/skills/oma-security/SKILL.md +172 -0
  263. package/skills/oma-security/resources/artex.md +108 -0
  264. package/skills/oma-security/resources/ci.md +134 -0
  265. package/skills/oma-security/resources/cisco-source.md +108 -0
  266. package/skills/oma-security/resources/deepsec-config.md +45 -0
  267. package/skills/oma-security/resources/deepsec-matchers.md +35 -0
  268. package/skills/oma-security/resources/deepsec-scanning.md +45 -0
  269. package/skills/oma-security/resources/deepsec-setup.md +41 -0
  270. package/skills/oma-security/resources/findings-contract.md +408 -0
  271. package/skills/oma-security/resources/mcp-scanning.md +122 -0
  272. package/skills/oma-security/resources/skill-scanning.md +106 -0
  273. package/skills/oma-security/resources/validation.md +71 -0
  274. package/skills/oma-skill-creation/SKILL.md +84 -0
  275. package/skills/oma-skill-creation/resources/prompt-evaluation.md +36 -0
  276. package/skills/oma-skill-creation/resources/ssl-lite-template.md +108 -0
  277. package/skills/oma-skill-creation/resources/validation-checklist.md +101 -0
  278. package/skills/oma-slide/SKILL.md +277 -0
  279. package/skills/oma-slide/resources/animation-patterns.md +245 -0
  280. package/skills/oma-slide/resources/assets/deck-stage.js +514 -0
  281. package/skills/oma-slide/resources/assets/viewport-base.css +257 -0
  282. package/skills/oma-slide/resources/canva-integration.md +203 -0
  283. package/skills/oma-slide/resources/checklist.md +46 -0
  284. package/skills/oma-slide/resources/design-doctrine.md +260 -0
  285. package/skills/oma-slide/resources/fixed-stage.md +232 -0
  286. package/skills/oma-slide/resources/generation-protocol.md +393 -0
  287. package/skills/oma-slide/resources/native-pptx.md +84 -0
  288. package/skills/oma-slide/resources/selection-index.json +461 -0
  289. package/skills/oma-slide/resources/style-presets.LICENSE +10 -0
  290. package/skills/oma-slide/resources/style-presets.md +520 -0
  291. package/skills/oma-tf-infra/SKILL.md +201 -0
  292. package/skills/oma-tf-infra/resources/checklist.md +53 -0
  293. package/skills/oma-tf-infra/resources/cost-optimization.md +574 -0
  294. package/skills/oma-tf-infra/resources/error-playbook.md +68 -0
  295. package/skills/oma-tf-infra/resources/examples.md +38 -0
  296. package/skills/oma-tf-infra/resources/execution-protocol.md +129 -0
  297. package/skills/oma-tf-infra/resources/iso-42001-infra.md +229 -0
  298. package/skills/oma-tf-infra/resources/multi-cloud-examples.md +241 -0
  299. package/skills/oma-tf-infra/resources/policy-testing-examples.md +332 -0
  300. package/skills/oma-translation/SKILL.md +229 -0
  301. package/skills/oma-translation/resources/anti-ai-patterns.md +104 -0
  302. package/skills/oma-translation/resources/lang/_template.md +73 -0
  303. package/skills/oma-translation/resources/lang/en.md +123 -0
  304. package/skills/oma-translation/resources/lang/ja.md +274 -0
  305. package/skills/oma-translation/resources/lang/ko.md +228 -0
  306. package/skills/oma-translation/resources/lang/zh.md +285 -0
  307. package/skills/oma-translation/resources/style-skills.md +104 -0
  308. package/skills/oma-translation/resources/translation-rubric.md +126 -0
  309. package/skills/oma-video/SKILL.md +229 -0
  310. package/skills/oma-video/config/video-config.yaml +60 -0
  311. package/skills/oma-video/resources/checklist.md +26 -0
  312. package/skills/oma-video/resources/execution-protocol.md +122 -0
  313. package/skills/oma-video/resources/existing-media.md +185 -0
  314. package/skills/oma-video/resources/hyperframes-authoring/README.md +44 -0
  315. package/skills/oma-video/resources/hyperframes-authoring/demo.md +31 -0
  316. package/skills/oma-video/resources/hyperframes-authoring/explainer.md +10 -0
  317. package/skills/oma-video/resources/hyperframes-authoring/shorts.md +10 -0
  318. package/skills/oma-video/resources/motion-principles.md +81 -0
  319. package/skills/oma-video/resources/mpt/driver.py +301 -0
  320. package/skills/oma-video/resources/prompt-tips.md +75 -0
  321. package/skills/oma-video/resources/script-schema.md +81 -0
  322. package/skills/oma-video/resources/sound-design.md +127 -0
  323. package/skills/oma-video/resources/strudel/package.json +11 -0
  324. package/skills/oma-video/resources/strudel/page.html +87 -0
  325. package/skills/oma-video/resources/strudel/render.mjs +295 -0
  326. package/skills/oma-video/resources/vendor-matrix.md +124 -0
  327. package/skills/oma-voice/SKILL.md +249 -0
  328. package/skills/oma-voice/config/voice-config.yaml +30 -0
  329. package/skills/oma-voice/resources/audio-processing.md +119 -0
  330. package/skills/oma-voice/resources/checklist.md +47 -0
  331. package/skills/oma-voice/resources/execution-protocol.md +215 -0
  332. package/skills/oma-voice/resources/prompt-tips.md +43 -0
  333. package/skills/oma-voice/resources/voice-matrix.md +51 -0
  334. package/skills/oma-voice/resources/voicestudio.md +87 -0
@@ -0,0 +1,635 @@
1
+ // API Template for Backend Agent (Rust / Axum)
2
+ //
3
+ // Demonstrates the Handler -> Service -> Repository pattern with SQLx.
4
+ // Each layer has a single responsibility:
5
+ // - handlers/ : HTTP concerns only (extract, validate, respond)
6
+ // - services/ : Business logic, authorization, orchestration
7
+ // - repositories/ : Raw DB access, no business logic
8
+ //
9
+ // Cargo.toml dependencies required:
10
+ // axum = "0.8"
11
+ // sqlx = { version = "0.8", features = ["postgres", "uuid", "chrono", "runtime-tokio-native-tls", "macros"] }
12
+ // tokio = { version = "1", features = ["full"] }
13
+ // serde = { version = "1", features = ["derive"] }
14
+ // serde_json = "1"
15
+ // uuid = { version = "1", features = ["v4", "serde"] }
16
+ // chrono = { version = "0.4", features = ["serde"] }
17
+ // thiserror = "1"
18
+ // validator = { version = "0.18", features = ["derive"] }
19
+ // jsonwebtoken = "9"
20
+ // anyhow = "1"
21
+ // tracing = "0.1"
22
+ // tracing-subscriber = "0.3"
23
+ // serde_with = "3"
24
+ // tower = { version = "0.5", features = ["util"] } # dev-dependency
25
+
26
+ // ============================================================
27
+ // error.rs: Centralized error type
28
+ // ============================================================
29
+
30
+ use axum::{
31
+ http::StatusCode,
32
+ response::{IntoResponse, Response},
33
+ Json,
34
+ };
35
+ use serde_json::json;
36
+ use thiserror::Error;
37
+
38
+ #[derive(Debug, Error)]
39
+ pub enum AppError {
40
+ #[error("not found")]
41
+ NotFound,
42
+
43
+ #[error("forbidden")]
44
+ Forbidden,
45
+
46
+ #[error("unauthorized")]
47
+ Unauthorized,
48
+
49
+ #[error("validation error: {0}")]
50
+ Validation(String),
51
+
52
+ #[error("conflict: {0}")]
53
+ Conflict(String),
54
+
55
+ #[error("database error: {0}")]
56
+ Database(#[from] sqlx::Error),
57
+
58
+ #[error("internal error: {0}")]
59
+ Internal(#[from] anyhow::Error),
60
+ }
61
+
62
+ impl IntoResponse for AppError {
63
+ fn into_response(self) -> Response {
64
+ let (status, message) = match &self {
65
+ AppError::NotFound => (StatusCode::NOT_FOUND, self.to_string()),
66
+ AppError::Forbidden => (StatusCode::FORBIDDEN, self.to_string()),
67
+ AppError::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()),
68
+ AppError::Validation(msg) => (StatusCode::UNPROCESSABLE_ENTITY, msg.clone()),
69
+ AppError::Conflict(msg) => (StatusCode::CONFLICT, msg.clone()),
70
+ AppError::Database(_) | AppError::Internal(_) => {
71
+ tracing::error!(error = %self, "unhandled error");
72
+ (StatusCode::INTERNAL_SERVER_ERROR, "internal server error".to_string())
73
+ }
74
+ };
75
+
76
+ (status, Json(json!({ "error": message }))).into_response()
77
+ }
78
+ }
79
+
80
+ // ============================================================
81
+ // models/resource.rs: Domain model + request/response types
82
+ // ============================================================
83
+
84
+ use chrono::{DateTime, Utc};
85
+ use serde::{Deserialize, Serialize};
86
+ use uuid::Uuid;
87
+ use validator::Validate;
88
+
89
+ /// Represents a row in the `resources` table.
90
+ #[derive(Debug, Clone, Serialize, sqlx::FromRow)]
91
+ pub struct Resource {
92
+ pub id: Uuid,
93
+ pub title: String,
94
+ pub description: Option<String>,
95
+ pub user_id: Uuid,
96
+ pub created_at: DateTime<Utc>,
97
+ pub updated_at: DateTime<Utc>,
98
+ }
99
+
100
+ /// Request body for POST /api/resources
101
+ #[derive(Debug, Deserialize, Validate)]
102
+ pub struct ResourceCreate {
103
+ #[validate(length(min = 1, max = 200, message = "title must be 1-200 characters"))]
104
+ pub title: String,
105
+
106
+ #[validate(length(max = 2000, message = "description must not exceed 2000 characters"))]
107
+ pub description: Option<String>,
108
+ }
109
+
110
+ /// Request body for PATCH /api/resources/{id}
111
+ #[derive(Debug, Deserialize, Validate)]
112
+ pub struct ResourceUpdate {
113
+ #[validate(length(min = 1, max = 200, message = "title must be 1-200 characters"))]
114
+ pub title: Option<String>,
115
+
116
+ /// `None` means "do not change"; `Some(None)` means "set to null"
117
+ #[serde(default, with = "serde_with::rust::double_option")]
118
+ pub description: Option<Option<String>>,
119
+ }
120
+
121
+ /// Paginated list query params
122
+ #[derive(Debug, Deserialize)]
123
+ pub struct PaginationParams {
124
+ #[serde(default = "default_page")]
125
+ pub page: i64,
126
+ #[serde(default = "default_size")]
127
+ pub size: i64,
128
+ }
129
+
130
+ fn default_page() -> i64 { 1 }
131
+ fn default_size() -> i64 { 20 }
132
+
133
+ /// Generic paginated response envelope
134
+ #[derive(Debug, Serialize)]
135
+ pub struct PaginatedResponse<T: Serialize> {
136
+ pub items: Vec<T>,
137
+ pub total: i64,
138
+ pub page: i64,
139
+ pub size: i64,
140
+ }
141
+
142
+ // ============================================================
143
+ // extractors/auth.rs: JWT extractor as Axum FromRequestParts
144
+ // ============================================================
145
+
146
+ use axum::{
147
+ extract::FromRequestParts,
148
+ http::{request::Parts, HeaderMap},
149
+ };
150
+ use jsonwebtoken::{decode, DecodingKey, Validation};
151
+ use serde::Deserialize;
152
+
153
+ #[derive(Debug, Deserialize)]
154
+ struct Claims {
155
+ sub: Uuid,
156
+ exp: i64,
157
+ }
158
+
159
+ /// Authenticated user injected by the JWT extractor.
160
+ #[derive(Debug, Clone)]
161
+ pub struct AuthUser {
162
+ pub id: Uuid,
163
+ }
164
+
165
+ impl FromRequestParts<AppState> for AuthUser {
166
+ type Rejection = AppError;
167
+
168
+ async fn from_request_parts(parts: &mut Parts, state: &AppState) -> Result<Self, Self::Rejection> {
169
+ let headers: &HeaderMap = &parts.headers;
170
+
171
+ let token = headers
172
+ .get("Authorization")
173
+ .and_then(|v| v.to_str().ok())
174
+ .and_then(|v| v.strip_prefix("Bearer "))
175
+ .ok_or(AppError::Unauthorized)?;
176
+
177
+ let token_data = decode::<Claims>(
178
+ token,
179
+ &state.jwt_decoding_key,
180
+ &Validation::default(),
181
+ )
182
+ .map_err(|_| AppError::Unauthorized)?;
183
+
184
+ Ok(AuthUser {
185
+ id: token_data.claims.sub,
186
+ })
187
+ }
188
+ }
189
+
190
+ // ============================================================
191
+ // repositories/resource_repository.rs: DB access layer
192
+ // ============================================================
193
+
194
+ use sqlx::PgPool;
195
+
196
+ pub struct ResourceRepository;
197
+
198
+ impl ResourceRepository {
199
+ pub async fn find_by_id(pool: &PgPool, id: Uuid) -> Result<Option<Resource>, AppError> {
200
+ let row = sqlx::query_as!(
201
+ Resource,
202
+ r#"
203
+ SELECT id, title, description, user_id, created_at, updated_at
204
+ FROM resources
205
+ WHERE id = $1
206
+ AND deleted_at IS NULL
207
+ "#,
208
+ id
209
+ )
210
+ .fetch_optional(pool)
211
+ .await?;
212
+
213
+ Ok(row)
214
+ }
215
+
216
+ pub async fn find_all_by_user(
217
+ pool: &PgPool,
218
+ user_id: Uuid,
219
+ limit: i64,
220
+ offset: i64,
221
+ ) -> Result<(Vec<Resource>, i64), AppError> {
222
+ let (rows, total) = tokio::try_join!(
223
+ sqlx::query_as!(
224
+ Resource,
225
+ r#"
226
+ SELECT id, title, description, user_id, created_at, updated_at
227
+ FROM resources
228
+ WHERE user_id = $1
229
+ AND deleted_at IS NULL
230
+ ORDER BY created_at DESC
231
+ LIMIT $2 OFFSET $3
232
+ "#,
233
+ user_id,
234
+ limit,
235
+ offset,
236
+ )
237
+ .fetch_all(pool),
238
+ sqlx::query_scalar!(
239
+ "SELECT COUNT(*) FROM resources WHERE user_id = $1 AND deleted_at IS NULL",
240
+ user_id
241
+ )
242
+ .fetch_one(pool),
243
+ )?;
244
+
245
+ Ok((rows, total.unwrap_or(0)))
246
+ }
247
+
248
+ pub async fn insert(
249
+ pool: &PgPool,
250
+ user_id: Uuid,
251
+ title: &str,
252
+ description: Option<&str>,
253
+ ) -> Result<Resource, AppError> {
254
+ let row = sqlx::query_as!(
255
+ Resource,
256
+ r#"
257
+ INSERT INTO resources (id, title, description, user_id, created_at, updated_at)
258
+ VALUES (gen_random_uuid(), $1, $2, $3, NOW(), NOW())
259
+ RETURNING id, title, description, user_id, created_at, updated_at
260
+ "#,
261
+ title,
262
+ description,
263
+ user_id,
264
+ )
265
+ .fetch_one(pool)
266
+ .await?;
267
+
268
+ Ok(row)
269
+ }
270
+
271
+ pub async fn update(
272
+ pool: &PgPool,
273
+ id: Uuid,
274
+ title: Option<&str>,
275
+ description: Option<Option<&str>>,
276
+ ) -> Result<Resource, AppError> {
277
+ // Build a targeted update; only changed fields are written
278
+ let row = sqlx::query_as!(
279
+ Resource,
280
+ r#"
281
+ UPDATE resources
282
+ SET title = COALESCE($2, title),
283
+ description = CASE WHEN $3 THEN $4 ELSE description END,
284
+ updated_at = NOW()
285
+ WHERE id = $1
286
+ AND deleted_at IS NULL
287
+ RETURNING id, title, description, user_id, created_at, updated_at
288
+ "#,
289
+ id,
290
+ title,
291
+ description.is_some(), // $3: whether to touch the column
292
+ description.flatten() as _, // $4: the new value (may be NULL)
293
+ )
294
+ .fetch_optional(pool)
295
+ .await?
296
+ .ok_or(AppError::NotFound)?;
297
+
298
+ Ok(row)
299
+ }
300
+
301
+ pub async fn soft_delete(pool: &PgPool, id: Uuid) -> Result<(), AppError> {
302
+ let result = sqlx::query!(
303
+ "UPDATE resources SET deleted_at = NOW() WHERE id = $1 AND deleted_at IS NULL",
304
+ id
305
+ )
306
+ .execute(pool)
307
+ .await?;
308
+
309
+ if result.rows_affected() == 0 {
310
+ return Err(AppError::NotFound);
311
+ }
312
+
313
+ Ok(())
314
+ }
315
+ }
316
+
317
+ // ============================================================
318
+ // services/resource_service.rs: Business logic layer
319
+ // ============================================================
320
+
321
+ use validator::Validate;
322
+
323
+ pub struct ResourceService;
324
+
325
+ impl ResourceService {
326
+ pub async fn list(
327
+ &self,
328
+ pool: &PgPool,
329
+ user_id: Uuid,
330
+ params: &PaginationParams,
331
+ ) -> Result<PaginatedResponse<Resource>, AppError> {
332
+ let size = params.size.clamp(1, 100);
333
+ let page = params.page.max(1);
334
+ let offset = (page - 1) * size;
335
+
336
+ let (items, total) =
337
+ ResourceRepository::find_all_by_user(pool, user_id, size, offset).await?;
338
+
339
+ Ok(PaginatedResponse { items, total, page, size })
340
+ }
341
+
342
+ pub async fn get_for_user(
343
+ &self,
344
+ pool: &PgPool,
345
+ resource_id: Uuid,
346
+ user_id: Uuid,
347
+ ) -> Result<Resource, AppError> {
348
+ let resource = ResourceRepository::find_by_id(pool, resource_id)
349
+ .await?
350
+ .ok_or(AppError::NotFound)?;
351
+
352
+ if resource.user_id != user_id {
353
+ return Err(AppError::Forbidden);
354
+ }
355
+
356
+ Ok(resource)
357
+ }
358
+
359
+ pub async fn create(
360
+ &self,
361
+ pool: &PgPool,
362
+ user_id: Uuid,
363
+ payload: ResourceCreate,
364
+ ) -> Result<Resource, AppError> {
365
+ payload.validate().map_err(|e| AppError::Validation(e.to_string()))?;
366
+
367
+ ResourceRepository::insert(pool, user_id, &payload.title, payload.description.as_deref())
368
+ .await
369
+ }
370
+
371
+ pub async fn update(
372
+ &self,
373
+ pool: &PgPool,
374
+ resource_id: Uuid,
375
+ user_id: Uuid,
376
+ payload: ResourceUpdate,
377
+ ) -> Result<Resource, AppError> {
378
+ payload.validate().map_err(|e| AppError::Validation(e.to_string()))?;
379
+
380
+ // Confirm ownership before mutating
381
+ let _ = self.get_for_user(pool, resource_id, user_id).await?;
382
+
383
+ ResourceRepository::update(
384
+ pool,
385
+ resource_id,
386
+ payload.title.as_deref(),
387
+ payload.description.as_ref().map(|opt| opt.as_deref()),
388
+ )
389
+ .await
390
+ }
391
+
392
+ pub async fn delete(
393
+ &self,
394
+ pool: &PgPool,
395
+ resource_id: Uuid,
396
+ user_id: Uuid,
397
+ ) -> Result<(), AppError> {
398
+ let _ = self.get_for_user(pool, resource_id, user_id).await?;
399
+ ResourceRepository::soft_delete(pool, resource_id).await
400
+ }
401
+ }
402
+
403
+ // ============================================================
404
+ // handlers/resource.rs: HTTP layer (thin wrappers)
405
+ // ============================================================
406
+
407
+ use axum::{
408
+ extract::{Path, Query, State},
409
+ http::StatusCode,
410
+ Json,
411
+ };
412
+ use std::sync::Arc;
413
+
414
+ #[derive(Clone)]
415
+ pub struct AppState {
416
+ pub db: PgPool,
417
+ pub resource_service: Arc<ResourceService>,
418
+ pub jwt_decoding_key: Arc<DecodingKey>,
419
+ }
420
+
421
+ /// GET /api/resources?page=1&size=20
422
+ pub async fn list_resources(
423
+ State(state): State<AppState>,
424
+ user: AuthUser,
425
+ Query(params): Query<PaginationParams>,
426
+ ) -> Result<Json<PaginatedResponse<Resource>>, AppError> {
427
+ let result = state.resource_service.list(&state.db, user.id, &params).await?;
428
+ Ok(Json(result))
429
+ }
430
+
431
+ /// GET /api/resources/{id}
432
+ pub async fn get_resource(
433
+ State(state): State<AppState>,
434
+ user: AuthUser,
435
+ Path(resource_id): Path<Uuid>,
436
+ ) -> Result<Json<Resource>, AppError> {
437
+ let resource = state
438
+ .resource_service
439
+ .get_for_user(&state.db, resource_id, user.id)
440
+ .await?;
441
+ Ok(Json(resource))
442
+ }
443
+
444
+ /// POST /api/resources
445
+ pub async fn create_resource(
446
+ State(state): State<AppState>,
447
+ user: AuthUser,
448
+ Json(payload): Json<ResourceCreate>,
449
+ ) -> Result<(StatusCode, Json<Resource>), AppError> {
450
+ let resource = state
451
+ .resource_service
452
+ .create(&state.db, user.id, payload)
453
+ .await?;
454
+ Ok((StatusCode::CREATED, Json(resource)))
455
+ }
456
+
457
+ /// PATCH /api/resources/{id}
458
+ pub async fn update_resource(
459
+ State(state): State<AppState>,
460
+ user: AuthUser,
461
+ Path(resource_id): Path<Uuid>,
462
+ Json(payload): Json<ResourceUpdate>,
463
+ ) -> Result<Json<Resource>, AppError> {
464
+ let resource = state
465
+ .resource_service
466
+ .update(&state.db, resource_id, user.id, payload)
467
+ .await?;
468
+ Ok(Json(resource))
469
+ }
470
+
471
+ /// DELETE /api/resources/{id}
472
+ pub async fn delete_resource(
473
+ State(state): State<AppState>,
474
+ user: AuthUser,
475
+ Path(resource_id): Path<Uuid>,
476
+ ) -> Result<StatusCode, AppError> {
477
+ state
478
+ .resource_service
479
+ .delete(&state.db, resource_id, user.id)
480
+ .await?;
481
+ Ok(StatusCode::NO_CONTENT)
482
+ }
483
+
484
+ // ============================================================
485
+ // main.rs: Router wiring and server startup
486
+ // ============================================================
487
+
488
+ use axum::{routing, Router};
489
+
490
+ pub fn resource_router(state: AppState) -> Router {
491
+ Router::new()
492
+ .route(
493
+ "/api/resources",
494
+ routing::get(list_resources).post(create_resource),
495
+ )
496
+ .route(
497
+ "/api/resources/{id}",
498
+ routing::get(get_resource)
499
+ .patch(update_resource)
500
+ .delete(delete_resource),
501
+ )
502
+ .with_state(state)
503
+ }
504
+
505
+ #[tokio::main]
506
+ async fn main() -> anyhow::Result<()> {
507
+ tracing_subscriber::fmt::init();
508
+
509
+ let jwt_secret = std::env::var("JWT_SECRET")?;
510
+ anyhow::ensure!(jwt_secret.len() >= 32, "JWT_SECRET must contain at least 32 bytes");
511
+ let jwt_decoding_key = Arc::new(DecodingKey::from_secret(jwt_secret.as_bytes()));
512
+ let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
513
+ let pool = sqlx::PgPool::connect(&database_url).await?;
514
+
515
+ sqlx::migrate!("./migrations").run(&pool).await?;
516
+
517
+ let state = AppState {
518
+ db: pool,
519
+ resource_service: Arc::new(ResourceService),
520
+ jwt_decoding_key,
521
+ };
522
+
523
+ let app = resource_router(state);
524
+ let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await?;
525
+
526
+ tracing::info!("listening on {}", listener.local_addr()?);
527
+ axum::serve(listener, app).await?;
528
+
529
+ Ok(())
530
+ }
531
+
532
+ // ============================================================
533
+ // tests/resource_integration.rs: Integration tests
534
+ // ============================================================
535
+
536
+ #[cfg(test)]
537
+ mod tests {
538
+ use super::*;
539
+ use axum::{
540
+ body::Body,
541
+ http::{header, Method, Request, StatusCode},
542
+ };
543
+ use tower::ServiceExt;
544
+ const TEST_JWT_SECRET: &[u8] = b"test-only-jwt-key-with-at-least-32-bytes";
545
+
546
+ async fn build_test_app() -> (Router, PgPool) {
547
+ let pool = sqlx::PgPool::connect(&std::env::var("DATABASE_URL_TEST").unwrap())
548
+ .await
549
+ .unwrap();
550
+ sqlx::migrate!("./migrations").run(&pool).await.unwrap();
551
+ let state = AppState {
552
+ db: pool.clone(),
553
+ resource_service: Arc::new(ResourceService),
554
+ jwt_decoding_key: Arc::new(DecodingKey::from_secret(TEST_JWT_SECRET)),
555
+ };
556
+ (resource_router(state), pool)
557
+ }
558
+
559
+ fn mint_jwt(user_id: Uuid) -> String {
560
+ use jsonwebtoken::{encode, EncodingKey, Header};
561
+ let claims = serde_json::json!({
562
+ "sub": user_id.to_string(),
563
+ "exp": (chrono::Utc::now() + chrono::Duration::hours(1)).timestamp(),
564
+ });
565
+ encode(&Header::default(), &claims, &EncodingKey::from_secret(TEST_JWT_SECRET)).unwrap()
566
+ }
567
+
568
+ #[test]
569
+ fn patch_description_distinguishes_missing_null_and_value() {
570
+ let missing: ResourceUpdate = serde_json::from_str(r#"{}"#).unwrap();
571
+ let cleared: ResourceUpdate = serde_json::from_str(r#"{"description":null}"#).unwrap();
572
+ let value: ResourceUpdate = serde_json::from_str(r#"{"description":"new"}"#).unwrap();
573
+ assert_eq!(missing.description, None);
574
+ assert_eq!(cleared.description, Some(None));
575
+ assert_eq!(value.description, Some(Some("new".to_owned())));
576
+ }
577
+
578
+ #[tokio::test]
579
+ async fn create_and_fetch_resource() {
580
+ let (app, _pool) = build_test_app().await;
581
+ let user_id = Uuid::new_v4();
582
+ let token = mint_jwt(user_id);
583
+
584
+ // Create
585
+ let create_req = Request::builder()
586
+ .method(Method::POST)
587
+ .uri("/api/resources")
588
+ .header(header::CONTENT_TYPE, "application/json")
589
+ .header(header::AUTHORIZATION, format!("Bearer {token}"))
590
+ .body(Body::from(r#"{"title":"hello","description":"world"}"#))
591
+ .unwrap();
592
+
593
+ let create_resp = app.clone().oneshot(create_req).await.unwrap();
594
+ assert_eq!(create_resp.status(), StatusCode::CREATED);
595
+
596
+ let bytes = axum::body::to_bytes(create_resp.into_body(), usize::MAX)
597
+ .await
598
+ .unwrap();
599
+ let body: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
600
+ let resource_id = body["id"].as_str().unwrap().to_owned();
601
+
602
+ // Fetch
603
+ let get_req = Request::builder()
604
+ .method(Method::GET)
605
+ .uri(format!("/api/resources/{resource_id}"))
606
+ .header(header::AUTHORIZATION, format!("Bearer {token}"))
607
+ .body(Body::empty())
608
+ .unwrap();
609
+
610
+ let get_resp = app.oneshot(get_req).await.unwrap();
611
+ assert_eq!(get_resp.status(), StatusCode::OK);
612
+ }
613
+
614
+ #[tokio::test]
615
+ async fn delete_resource_returns_no_content() {
616
+ let (app, pool) = build_test_app().await;
617
+ let user_id = Uuid::new_v4();
618
+ let token = mint_jwt(user_id);
619
+
620
+ // Seed directly via repository
621
+ let resource = ResourceRepository::insert(&pool, user_id, "temp", None)
622
+ .await
623
+ .unwrap();
624
+
625
+ let req = Request::builder()
626
+ .method(Method::DELETE)
627
+ .uri(format!("/api/resources/{}", resource.id))
628
+ .header(header::AUTHORIZATION, format!("Bearer {token}"))
629
+ .body(Body::empty())
630
+ .unwrap();
631
+
632
+ let resp = app.oneshot(req).await.unwrap();
633
+ assert_eq!(resp.status(), StatusCode::NO_CONTENT);
634
+ }
635
+ }