@c0x12c/ai-toolkit 1.15.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 (255) hide show
  1. package/.claude-plugin/marketplace.json +16 -0
  2. package/.claude-plugin/plugin.json +12 -0
  3. package/README.md +439 -0
  4. package/VERSION +1 -0
  5. package/agents/design-critic.md +127 -0
  6. package/agents/idea-killer.md +72 -0
  7. package/agents/infrastructure-expert.md +49 -0
  8. package/agents/micronaut-backend-expert.md +45 -0
  9. package/agents/phase-reviewer.md +150 -0
  10. package/agents/research-planner.md +70 -0
  11. package/agents/solution-architect-cto.md +49 -0
  12. package/agents/sre-architect.md +49 -0
  13. package/agents/team-coordinator.md +111 -0
  14. package/bin/cli.js +780 -0
  15. package/claude-md/00-header.md +39 -0
  16. package/claude-md/01-core.md +105 -0
  17. package/claude-md/05-database.md +20 -0
  18. package/claude-md/11-backend-micronaut.md +19 -0
  19. package/claude-md/20-frontend-react.md +44 -0
  20. package/claude-md/25-ux-design.md +56 -0
  21. package/claude-md/30-infrastructure.md +24 -0
  22. package/claude-md/30-project-mgmt.md +119 -0
  23. package/claude-md/40-product.md +39 -0
  24. package/claude-md/50-ops.md +34 -0
  25. package/claude-md/60-research.md +27 -0
  26. package/claude-md/90-footer.md +21 -0
  27. package/commands/spartan/brainstorm.md +134 -0
  28. package/commands/spartan/brownfield.md +157 -0
  29. package/commands/spartan/build.md +435 -0
  30. package/commands/spartan/careful.md +94 -0
  31. package/commands/spartan/commit-message.md +112 -0
  32. package/commands/spartan/content.md +17 -0
  33. package/commands/spartan/context-save.md +161 -0
  34. package/commands/spartan/contribute.md +140 -0
  35. package/commands/spartan/daily.md +42 -0
  36. package/commands/spartan/debug.md +308 -0
  37. package/commands/spartan/deep-dive.md +55 -0
  38. package/commands/spartan/deploy.md +207 -0
  39. package/commands/spartan/e2e.md +264 -0
  40. package/commands/spartan/env-setup.md +166 -0
  41. package/commands/spartan/epic.md +199 -0
  42. package/commands/spartan/fe-review.md +181 -0
  43. package/commands/spartan/figma-to-code.md +260 -0
  44. package/commands/spartan/forensics.md +46 -0
  45. package/commands/spartan/freeze.md +84 -0
  46. package/commands/spartan/fundraise.md +53 -0
  47. package/commands/spartan/gate-review.md +229 -0
  48. package/commands/spartan/gsd-upgrade.md +376 -0
  49. package/commands/spartan/guard.md +42 -0
  50. package/commands/spartan/init-project.md +178 -0
  51. package/commands/spartan/init-rules.md +298 -0
  52. package/commands/spartan/interview.md +154 -0
  53. package/commands/spartan/kickoff.md +73 -0
  54. package/commands/spartan/kotlin-service.md +109 -0
  55. package/commands/spartan/lean-canvas.md +222 -0
  56. package/commands/spartan/lint-rules.md +122 -0
  57. package/commands/spartan/map-codebase.md +124 -0
  58. package/commands/spartan/migration.md +82 -0
  59. package/commands/spartan/next-app.md +317 -0
  60. package/commands/spartan/next-feature.md +212 -0
  61. package/commands/spartan/onboard.md +326 -0
  62. package/commands/spartan/outreach.md +16 -0
  63. package/commands/spartan/phase.md +142 -0
  64. package/commands/spartan/pitch.md +18 -0
  65. package/commands/spartan/plan.md +210 -0
  66. package/commands/spartan/pr-ready.md +202 -0
  67. package/commands/spartan/project.md +106 -0
  68. package/commands/spartan/qa.md +222 -0
  69. package/commands/spartan/research.md +254 -0
  70. package/commands/spartan/review.md +132 -0
  71. package/commands/spartan/scan-rules.md +173 -0
  72. package/commands/spartan/sessions.md +143 -0
  73. package/commands/spartan/spec.md +131 -0
  74. package/commands/spartan/startup.md +257 -0
  75. package/commands/spartan/team.md +570 -0
  76. package/commands/spartan/teardown.md +161 -0
  77. package/commands/spartan/testcontainer.md +97 -0
  78. package/commands/spartan/tf-cost.md +123 -0
  79. package/commands/spartan/tf-deploy.md +116 -0
  80. package/commands/spartan/tf-drift.md +100 -0
  81. package/commands/spartan/tf-import.md +107 -0
  82. package/commands/spartan/tf-module.md +121 -0
  83. package/commands/spartan/tf-plan.md +100 -0
  84. package/commands/spartan/tf-review.md +106 -0
  85. package/commands/spartan/tf-scaffold.md +109 -0
  86. package/commands/spartan/tf-security.md +147 -0
  87. package/commands/spartan/think.md +221 -0
  88. package/commands/spartan/unfreeze.md +13 -0
  89. package/commands/spartan/update.md +134 -0
  90. package/commands/spartan/ux.md +1233 -0
  91. package/commands/spartan/validate.md +193 -0
  92. package/commands/spartan/web-to-prd.md +706 -0
  93. package/commands/spartan/workstreams.md +109 -0
  94. package/commands/spartan/write.md +16 -0
  95. package/commands/spartan.md +386 -0
  96. package/frameworks/00-framework-comparison-guide.md +317 -0
  97. package/frameworks/01-lean-canvas.md +196 -0
  98. package/frameworks/02-design-sprint.md +304 -0
  99. package/frameworks/03-foundation-sprint.md +337 -0
  100. package/frameworks/04-business-model-canvas.md +391 -0
  101. package/frameworks/05-customer-development.md +426 -0
  102. package/frameworks/06-jobs-to-be-done.md +358 -0
  103. package/frameworks/07-mom-test.md +392 -0
  104. package/frameworks/08-value-proposition-canvas.md +488 -0
  105. package/frameworks/09-javelin-board.md +428 -0
  106. package/frameworks/10-build-measure-learn.md +467 -0
  107. package/frameworks/11-mvp-approaches.md +533 -0
  108. package/frameworks/think-before-build.md +593 -0
  109. package/lib/assembler.js +197 -0
  110. package/lib/assembler.test.js +159 -0
  111. package/lib/detector.js +166 -0
  112. package/lib/detector.test.js +221 -0
  113. package/lib/packs.js +16 -0
  114. package/lib/resolver.js +272 -0
  115. package/lib/resolver.test.js +298 -0
  116. package/lib/worktree.sh +104 -0
  117. package/package.json +50 -0
  118. package/packs/backend-micronaut.yaml +35 -0
  119. package/packs/backend-nodejs.yaml +15 -0
  120. package/packs/backend-python.yaml +15 -0
  121. package/packs/core.yaml +37 -0
  122. package/packs/database.yaml +21 -0
  123. package/packs/frontend-react.yaml +24 -0
  124. package/packs/infrastructure.yaml +40 -0
  125. package/packs/ops.yaml +16 -0
  126. package/packs/packs.compiled.json +371 -0
  127. package/packs/product.yaml +22 -0
  128. package/packs/project-mgmt.yaml +24 -0
  129. package/packs/research.yaml +39 -0
  130. package/packs/shared-backend.yaml +14 -0
  131. package/packs/ux-design.yaml +21 -0
  132. package/rules/backend-micronaut/API_DESIGN.md +313 -0
  133. package/rules/backend-micronaut/BATCH_PROCESSING.md +92 -0
  134. package/rules/backend-micronaut/CONTROLLERS.md +388 -0
  135. package/rules/backend-micronaut/KOTLIN.md +414 -0
  136. package/rules/backend-micronaut/RETROFIT_PLACEMENT.md +290 -0
  137. package/rules/backend-micronaut/SERVICES_AND_BEANS.md +325 -0
  138. package/rules/core/NAMING_CONVENTIONS.md +208 -0
  139. package/rules/core/SKILL_AUTHORING.md +174 -0
  140. package/rules/core/TIMEZONE.md +316 -0
  141. package/rules/database/ORM_AND_REPO.md +289 -0
  142. package/rules/database/SCHEMA.md +146 -0
  143. package/rules/database/TRANSACTIONS.md +311 -0
  144. package/rules/frontend-react/FRONTEND.md +344 -0
  145. package/rules/infrastructure/MODULES.md +260 -0
  146. package/rules/infrastructure/NAMING.md +196 -0
  147. package/rules/infrastructure/PROVIDERS.md +309 -0
  148. package/rules/infrastructure/SECURITY.md +310 -0
  149. package/rules/infrastructure/STATE_AND_BACKEND.md +237 -0
  150. package/rules/infrastructure/STRUCTURE.md +234 -0
  151. package/rules/infrastructure/VARIABLES.md +285 -0
  152. package/rules/shared-backend/ARCHITECTURE.md +46 -0
  153. package/rules/ux-design/DESIGN_PROCESS.md +176 -0
  154. package/skills/api-endpoint-creator/SKILL.md +455 -0
  155. package/skills/api-endpoint-creator/error-handling-guide.md +244 -0
  156. package/skills/api-endpoint-creator/examples.md +522 -0
  157. package/skills/api-endpoint-creator/testing-patterns.md +302 -0
  158. package/skills/article-writing/SKILL.md +109 -0
  159. package/skills/article-writing/examples.md +59 -0
  160. package/skills/backend-api-design/SKILL.md +84 -0
  161. package/skills/backend-api-design/code-patterns.md +138 -0
  162. package/skills/brainstorm/SKILL.md +95 -0
  163. package/skills/browser-qa/SKILL.md +87 -0
  164. package/skills/browser-qa/playwright-snippets.md +110 -0
  165. package/skills/ci-cd-patterns/SKILL.md +108 -0
  166. package/skills/ci-cd-patterns/workflows.md +149 -0
  167. package/skills/competitive-teardown/SKILL.md +93 -0
  168. package/skills/competitive-teardown/example-analysis.md +50 -0
  169. package/skills/content-engine/SKILL.md +131 -0
  170. package/skills/content-engine/examples.md +72 -0
  171. package/skills/database-patterns/SKILL.md +72 -0
  172. package/skills/database-patterns/code-templates.md +114 -0
  173. package/skills/database-table-creator/SKILL.md +141 -0
  174. package/skills/database-table-creator/examples.md +552 -0
  175. package/skills/database-table-creator/kotlin-templates.md +400 -0
  176. package/skills/database-table-creator/migration-template.sql +68 -0
  177. package/skills/database-table-creator/validation-checklist.md +337 -0
  178. package/skills/deep-research/SKILL.md +80 -0
  179. package/skills/design-intelligence/SKILL.md +268 -0
  180. package/skills/design-workflow/SKILL.md +127 -0
  181. package/skills/design-workflow/checklists.md +45 -0
  182. package/skills/idea-validation/SKILL.md +129 -0
  183. package/skills/idea-validation/example-report.md +50 -0
  184. package/skills/investor-materials/SKILL.md +122 -0
  185. package/skills/investor-materials/example-outline.md +70 -0
  186. package/skills/investor-outreach/SKILL.md +112 -0
  187. package/skills/investor-outreach/examples.md +76 -0
  188. package/skills/kotlin-best-practices/SKILL.md +58 -0
  189. package/skills/kotlin-best-practices/code-patterns.md +132 -0
  190. package/skills/market-research/SKILL.md +99 -0
  191. package/skills/security-checklist/SKILL.md +65 -0
  192. package/skills/security-checklist/audit-reference.md +95 -0
  193. package/skills/service-debugging/SKILL.md +116 -0
  194. package/skills/service-debugging/common-issues.md +65 -0
  195. package/skills/startup-pipeline/SKILL.md +152 -0
  196. package/skills/terraform-best-practices/SKILL.md +244 -0
  197. package/skills/terraform-module-creator/SKILL.md +284 -0
  198. package/skills/terraform-review/SKILL.md +222 -0
  199. package/skills/terraform-security-audit/SKILL.md +280 -0
  200. package/skills/terraform-service-scaffold/SKILL.md +574 -0
  201. package/skills/testing-strategies/SKILL.md +116 -0
  202. package/skills/testing-strategies/examples.md +103 -0
  203. package/skills/testing-strategies/integration-test-setup.md +71 -0
  204. package/skills/ui-ux-pro-max/SKILL.md +238 -0
  205. package/skills/ui-ux-pro-max/data/charts.csv +26 -0
  206. package/skills/ui-ux-pro-max/data/colors.csv +97 -0
  207. package/skills/ui-ux-pro-max/data/icons.csv +101 -0
  208. package/skills/ui-ux-pro-max/data/landing.csv +31 -0
  209. package/skills/ui-ux-pro-max/data/products.csv +97 -0
  210. package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  211. package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  212. package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  213. package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  214. package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  215. package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  216. package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  217. package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  218. package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  219. package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  220. package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  221. package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  222. package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  223. package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  224. package/skills/ui-ux-pro-max/data/styles.csv +68 -0
  225. package/skills/ui-ux-pro-max/data/typography.csv +58 -0
  226. package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  227. package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  228. package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
  229. package/skills/ui-ux-pro-max/python-setup.md +146 -0
  230. package/skills/ui-ux-pro-max/scripts/core.py +253 -0
  231. package/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
  232. package/skills/ui-ux-pro-max/scripts/search.py +114 -0
  233. package/skills/web-to-prd/SKILL.md +478 -0
  234. package/templates/build-config.yaml +44 -0
  235. package/templates/commands-config.yaml +55 -0
  236. package/templates/competitor-analysis.md +60 -0
  237. package/templates/content/AGENT_TEMPLATE.md +47 -0
  238. package/templates/content/COMMAND_TEMPLATE.md +27 -0
  239. package/templates/content/RULE_TEMPLATE.md +40 -0
  240. package/templates/content/SKILL_TEMPLATE.md +41 -0
  241. package/templates/design-config.md +105 -0
  242. package/templates/design-doc.md +207 -0
  243. package/templates/epic.md +100 -0
  244. package/templates/feature-spec.md +181 -0
  245. package/templates/idea-canvas.md +47 -0
  246. package/templates/implementation-plan.md +159 -0
  247. package/templates/prd-template.md +86 -0
  248. package/templates/preamble.md +89 -0
  249. package/templates/project-readme.md +35 -0
  250. package/templates/quality-gates.md +230 -0
  251. package/templates/spartan-config.yaml +164 -0
  252. package/templates/user-interview.md +69 -0
  253. package/templates/validation-checklist.md +108 -0
  254. package/templates/workflow-backend-micronaut.md +409 -0
  255. package/templates/workflow-frontend-react.md +233 -0
@@ -0,0 +1,302 @@
1
+ # API Endpoint Testing Patterns
2
+
3
+ How to write integration tests for API endpoints in the platform.
4
+
5
+ ---
6
+
7
+ ## Test Structure
8
+
9
+ ```
10
+ Integration Test (Controller)
11
+ -> Retrofit HTTP Client
12
+ -> Actual HTTP Request
13
+ -> Controller -> Manager -> Repository -> Database
14
+ -> HTTP Response
15
+ -> Assertions
16
+ ```
17
+
18
+ Integration tests check the full stack works together.
19
+
20
+ ---
21
+
22
+ ## Base Test Class
23
+
24
+ All controller tests extend `AbstractControllerTest`:
25
+
26
+ ```kotlin
27
+ @MicronautTest(environments = ["test"], transactional = false)
28
+ @TestInstance(TestInstance.Lifecycle.PER_CLASS)
29
+ class ProjectControllerTest : AbstractControllerTest() {
30
+
31
+ private lateinit var projectClient: ProjectClient
32
+
33
+ @BeforeAll
34
+ override fun beforeAll() {
35
+ val url = embeddedServer.url.toString()
36
+ val jackson = ObjectMapper().configured()
37
+ val retrofit = Retrofits
38
+ .newBuilder(url = url.toHttpUrl(), jackson = jackson)
39
+ .build()
40
+
41
+ projectClient = retrofit.create(ProjectClient::class.java)
42
+ }
43
+
44
+ @AfterEach
45
+ fun cleanupAfterEach() {
46
+ database.primary.truncateAllTables()
47
+ }
48
+ }
49
+ ```
50
+
51
+ **Key Points**:
52
+ - `@MicronautTest(environments = ["test"])` - Use test environment
53
+ - `@TestInstance(Lifecycle.PER_CLASS)` - Reuse test instance
54
+ - Extend `AbstractControllerTest` for utilities
55
+ - Create Retrofit client in `@BeforeAll`
56
+ - Clean database in `@AfterEach`
57
+
58
+ ---
59
+
60
+ ## Retrofit Client
61
+
62
+ **File**: `app/module-client/src/main/kotlin/com/yourcompany/client/ProjectClient.kt`
63
+
64
+ ```kotlin
65
+ package com.yourcompany.client
66
+
67
+ import com.yourcompany.client.request.{domain}.CreateProjectRequest
68
+ import com.yourcompany.client.response.{domain}.ProjectResponse
69
+ import com.yourcompany.client.response.{domain}.ProjectListResponse
70
+ import retrofit2.http.*
71
+ import java.util.UUID
72
+
73
+ interface ProjectClient {
74
+
75
+ @GET("/api/v1/admin/projects")
76
+ suspend fun listProjects(
77
+ @Header("Authorization") authorization: String,
78
+ @Query("page") page: Int? = null,
79
+ @Query("limit") limit: Int? = null
80
+ ): ProjectListResponse
81
+
82
+ @GET("/api/v1/admin/project")
83
+ suspend fun getProject(
84
+ @Header("Authorization") authorization: String,
85
+ @Query("id") id: UUID
86
+ ): ProjectResponse
87
+
88
+ @POST("/api/v1/admin/project")
89
+ suspend fun createProject(
90
+ @Header("Authorization") authorization: String,
91
+ @Body request: CreateProjectRequest
92
+ ): ProjectResponse
93
+
94
+ @POST("/api/v1/admin/project/delete")
95
+ suspend fun deleteProject(
96
+ @Header("Authorization") authorization: String,
97
+ @Query("id") id: UUID
98
+ ): Boolean
99
+ }
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Test Pattern 1: Happy Path
105
+
106
+ ```kotlin
107
+ @Test
108
+ fun `getProject - returns project when exists`() = runBlocking {
109
+ val project = createTestProject()
110
+ val token = accessToken(prepareUser(), UserRole.ADMIN)
111
+
112
+ val response = projectClient.getProject(
113
+ authorization = token,
114
+ id = project.id
115
+ )
116
+
117
+ assertThat(response.id).isEqualTo(project.id)
118
+ assertThat(response.name).isEqualTo(project.name)
119
+ }
120
+ ```
121
+
122
+ ## Test Pattern 2: Not Found (404)
123
+
124
+ ```kotlin
125
+ @Test
126
+ fun `getProject - returns 404 when not found`() = runBlocking {
127
+ val token = accessToken(prepareUser(), UserRole.ADMIN)
128
+
129
+ val exception = assertThrows<HttpException> {
130
+ runBlocking {
131
+ projectClient.getProject(
132
+ authorization = token,
133
+ id = UUID.randomUUID()
134
+ )
135
+ }
136
+ }
137
+
138
+ assertThat(exception.code()).isEqualTo(404)
139
+ }
140
+ ```
141
+
142
+ ## Test Pattern 3: Authentication (401)
143
+
144
+ ```kotlin
145
+ @Test
146
+ fun `should return 401 when not authenticated`() = runBlocking {
147
+ val exception = assertThrows<HttpException> {
148
+ runBlocking {
149
+ projectClient.getProject(
150
+ authorization = "",
151
+ id = UUID.randomUUID()
152
+ )
153
+ }
154
+ }
155
+
156
+ assertThat(exception.code()).isEqualTo(401)
157
+ }
158
+ ```
159
+
160
+ ## Test Pattern 4: Create and Verify
161
+
162
+ ```kotlin
163
+ @Test
164
+ fun `createProject - creates project successfully`() = runBlocking {
165
+ val token = accessToken(prepareUser(), UserRole.ADMIN)
166
+ val request = CreateProjectRequest(
167
+ name = "Test Project",
168
+ description = "A test project"
169
+ )
170
+
171
+ val response = projectClient.createProject(
172
+ authorization = token,
173
+ request = request
174
+ )
175
+
176
+ assertThat(response.name).isEqualTo("Test Project")
177
+ assertThat(response.description).isEqualTo("A test project")
178
+
179
+ // Verify in database
180
+ val saved = projectRepository.byId(response.id)
181
+ assertThat(saved).isNotNull
182
+ assertThat(saved?.name).isEqualTo("Test Project")
183
+ }
184
+ ```
185
+
186
+ ## Test Pattern 5: List with Pagination
187
+
188
+ ```kotlin
189
+ @Test
190
+ fun `listProjects - returns paginated results`() = runBlocking {
191
+ createTestProject(name = "Project A")
192
+ createTestProject(name = "Project B")
193
+ createTestProject(name = "Project C")
194
+
195
+ val token = accessToken(prepareUser(), UserRole.ADMIN)
196
+
197
+ val response = projectClient.listProjects(
198
+ authorization = token,
199
+ page = 1,
200
+ limit = 2
201
+ )
202
+
203
+ assertThat(response.items).hasSize(2)
204
+ assertThat(response.total).isEqualTo(3)
205
+ assertThat(response.hasMore).isTrue()
206
+ }
207
+ ```
208
+
209
+ ## Test Pattern 6: Soft Delete
210
+
211
+ ```kotlin
212
+ @Test
213
+ fun `deleteProject - soft deletes project`() = runBlocking {
214
+ val project = createTestProject()
215
+ val token = accessToken(prepareUser(), UserRole.ADMIN)
216
+
217
+ val result = projectClient.deleteProject(
218
+ authorization = token,
219
+ id = project.id
220
+ )
221
+
222
+ assertThat(result).isTrue()
223
+
224
+ // Verify not returned by queries
225
+ val exception = assertThrows<HttpException> {
226
+ runBlocking {
227
+ projectClient.getProject(authorization = token, id = project.id)
228
+ }
229
+ }
230
+ assertThat(exception.code()).isEqualTo(404)
231
+ }
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Test Helper Patterns
237
+
238
+ ```kotlin
239
+ private val projectRepository: ProjectRepository by lazy {
240
+ DefaultProjectRepository(database)
241
+ }
242
+
243
+ private fun createTestProject(
244
+ name: String = "Test Project ${UUID.randomUUID()}",
245
+ description: String? = "Test description",
246
+ status: String = "active"
247
+ ): ProjectEntity {
248
+ return projectRepository.insert(
249
+ ProjectEntity(
250
+ name = name,
251
+ description = description,
252
+ status = status
253
+ )
254
+ )
255
+ }
256
+ ```
257
+
258
+ **Utility methods from `AbstractControllerTest`**:
259
+ - `prepareUser(email, displayName, role)` - Create test user
260
+ - `accessToken(user, role)` - Generate JWT token
261
+ - `database` - DatabaseContext for direct DB access
262
+
263
+ ---
264
+
265
+ ## Test Scenarios Checklist
266
+
267
+ For each endpoint, test:
268
+
269
+ ### Create
270
+ - [ ] Happy path succeeds
271
+ - [ ] Returns created resource
272
+ - [ ] Resource exists in database
273
+ - [ ] Requires authentication (401)
274
+
275
+ ### Read (Get by ID)
276
+ - [ ] Returns resource when exists (200)
277
+ - [ ] Returns 404 when not found
278
+ - [ ] Requires authentication (401)
279
+
280
+ ### List
281
+ - [ ] Returns paginated results
282
+ - [ ] Pagination works (page/limit)
283
+ - [ ] Returns empty list when no results
284
+ - [ ] Requires authentication (401)
285
+
286
+ ### Delete
287
+ - [ ] Soft deletes successfully
288
+ - [ ] Resource not returned after deletion
289
+ - [ ] Returns 404 when not found
290
+ - [ ] Requires authentication (401)
291
+
292
+ ---
293
+
294
+ ## Best Practices
295
+
296
+ 1. **Isolation**: Clean database between tests (`@AfterEach`)
297
+ 2. **Random data**: Use UUIDs in names to avoid conflicts
298
+ 3. **AssertJ**: Use `assertThat` for readable assertions
299
+ 4. **Given/When/Then**: Structure tests clearly
300
+ 5. **Verify database**: Don't just check the response
301
+ 6. **Auth tests**: Always test with and without auth
302
+ 7. **Use Retrofit clients**: Never raw HttpRequest for API tests
@@ -0,0 +1,109 @@
1
+ ---
2
+ name: article-writing
3
+ description: Write blog posts, guides, tutorials, and long-form content. Sounds like a real person, not AI. Use when the user wants polished written content.
4
+ allowed_tools:
5
+ - Read
6
+ - Write
7
+ - WebSearch
8
+ ---
9
+
10
+ # Article Writing
11
+
12
+ Write long-form content that sounds like a person wrote it.
13
+
14
+ ## When to Use
15
+
16
+ - Blog posts, essays, guides, tutorials
17
+ - Turning notes or research into an article
18
+ - Matching an existing voice from examples
19
+ - Cleaning up and tightening existing writing
20
+
21
+ > See `examples.md` for good vs bad writing examples that show what "sounds human" actually means.
22
+
23
+ ## Rules
24
+
25
+ 1. Start with something concrete: example, number, story, or code block.
26
+ 2. Explain after the example, not before.
27
+ 3. Short, direct sentences.
28
+ 4. Use real numbers when you have them.
29
+ 5. Never make up facts, company data, or quotes.
30
+
31
+ ## Banned Patterns
32
+
33
+ Delete and rewrite any of these:
34
+ - "In today's rapidly evolving landscape"
35
+ - "Moreover", "Furthermore", "Additionally"
36
+ - "game-changer", "cutting-edge", "revolutionary"
37
+ - Vague claims with no proof
38
+ - Bio claims you can't back up
39
+
40
+ ## Writing Process
41
+
42
+ 1. Know the audience and goal
43
+ 2. Outline: one purpose per section
44
+ 3. Start each section with evidence or example
45
+ 4. Only keep sentences that earn their spot
46
+ 5. Cut anything that sounds like a template
47
+
48
+ ## Structure Tips
49
+
50
+ ### Technical Guides
51
+ - Open with what the reader gets
52
+ - Code or terminal examples in every section
53
+ - End with real takeaways, not soft summary
54
+
55
+ ### Essays / Opinion
56
+ - Start with tension or a sharp point
57
+ - One argument per section
58
+ - Back opinions with examples
59
+
60
+ ### Newsletters
61
+ - First screen must be strong
62
+ - Mix insight with updates
63
+ - Clear section labels, easy to skim
64
+
65
+ ## Interaction Style
66
+
67
+ **No BS. Honest feedback only.**
68
+
69
+ This is a two-way talk:
70
+ - I ask you questions → you answer
71
+ - You ask me questions → I think hard, give you options, then answer
72
+
73
+ **When I ask you a question, I always:**
74
+ 1. Think about it first
75
+ 2. Give you 2-3 options with my honest take on each
76
+ 3. Tell you which one I'd pick and why
77
+ 4. Then ask what you think
78
+
79
+ **When you ask me something:**
80
+ - I give you a straight answer
81
+ - I tell you if a section is boring, weak, or filler
82
+ - I push for real examples over vague claims
83
+
84
+ **Never:**
85
+ - Ask a question without giving options
86
+ - Let filler paragraphs stay in the draft
87
+ - Say "it depends" without picking a side
88
+ - Skip the "this part is weak" feedback
89
+ - Write AI-sounding content and call it done
90
+
91
+ ## Gotchas
92
+
93
+ - **The intro is where most articles die.** If the first paragraph starts with "In this article, we'll explore..." — delete it and start with a story, stat, or code block.
94
+ - **AI-written articles all sound the same.** They hedge ("it's important to note"), use transition words nobody says out loud ("Moreover"), and avoid strong opinions. Cut all of that.
95
+ - **Don't explain the obvious.** If your audience is developers, don't explain what an API is. Write for the person, not the lowest common denominator.
96
+ - **Long ≠ thorough.** A 3,000-word article with 1,500 words of filler is worse than a 1,500-word article where every sentence earns its spot.
97
+ - **Every section needs evidence.** A claim without a number, example, or code block is just an opinion. Back it up or cut it.
98
+
99
+ ## Before You Deliver
100
+
101
+ - Facts match sources
102
+ - No filler or corporate talk
103
+ - Voice matches examples (if given)
104
+ - Every section adds something new
105
+ - Format fits the platform
106
+
107
+ ## Output
108
+
109
+ Save to the project's research or build folder depending on context.
@@ -0,0 +1,59 @@
1
+ # Article Writing — Good vs Bad Examples
2
+
3
+ > Read these examples to calibrate your writing style. The "bad" versions are typical AI output. The "good" versions sound like a person wrote them.
4
+
5
+ ## Intro Paragraphs
6
+
7
+ ### Bad (AI-style)
8
+ > In today's rapidly evolving technological landscape, the importance of effective error handling cannot be overstated. This comprehensive guide will walk you through the various aspects of implementing robust error handling strategies in your Kotlin applications, enabling you to build more resilient and maintainable software systems.
9
+
10
+ ### Good (human-style)
11
+ > Last Tuesday, our payment service threw 4,000 unhandled exceptions in 20 minutes. The fix was 3 lines of code. Here's what went wrong and how to make sure it doesn't happen to you.
12
+
13
+ ---
14
+
15
+ ## Explaining a Concept
16
+
17
+ ### Bad (AI-style)
18
+ > Dependency injection is a crucial software design pattern that facilitates the development of loosely coupled, maintainable, and testable code. By leveraging this paradigm, developers can effectively manage the complex dependencies between various components of their application architecture.
19
+
20
+ ### Good (human-style)
21
+ > Dependency injection means your class doesn't create its own dependencies — someone hands them in. Instead of `val db = Database()` inside your class, you write `class UserRepo(val db: Database)` and let the framework wire it up. That's it. The rest is details.
22
+
23
+ ---
24
+
25
+ ## Technical Guide Section
26
+
27
+ ### Bad (AI-style)
28
+ > To implement this feature, you'll want to start by carefully considering the architectural implications. First, create a new service class that will handle the business logic. Then, implement the necessary repository methods. Finally, wire everything together in the controller layer. This approach ensures a clean separation of concerns.
29
+
30
+ ### Good (human-style)
31
+ > Three files to create:
32
+ > 1. `PaymentManager.kt` — validates the amount, calls Stripe, saves the record
33
+ > 2. `PaymentRepository.kt` — insert and soft-delete methods
34
+ > 3. `PaymentController.kt` — one POST endpoint, delegates to the manager
35
+ >
36
+ > Start with the manager. The other two are boilerplate.
37
+
38
+ ---
39
+
40
+ ## Transitions Between Sections
41
+
42
+ ### Bad (AI-style)
43
+ > Now that we've explored the fundamentals of error handling, let's delve into the more advanced aspects of this topic. In the following section, we'll examine how to implement custom error types that can provide more granular control over your application's error handling mechanisms.
44
+
45
+ ### Good (human-style)
46
+ > The basics work for 80% of cases. But when you need different error responses for different callers (API vs webhook vs internal), you need custom error types.
47
+
48
+ ---
49
+
50
+ ## Conclusions
51
+
52
+ ### Bad (AI-style)
53
+ > In conclusion, we've covered a comprehensive overview of error handling patterns in Kotlin. By implementing these strategies, you'll be well-equipped to build robust, maintainable applications. Remember that error handling is not just about catching exceptions — it's about creating a resilient system that gracefully handles the unexpected.
54
+
55
+ ### Good (human-style)
56
+ > Three things to remember:
57
+ > 1. Return `Either`, don't throw. Your callers will thank you.
58
+ > 2. Log the actual error, not a generic message.
59
+ > 3. Test the error paths. They run more often than you think.
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: backend-api-design
3
+ description: Design RPC-style APIs with layered architecture (Controller → Manager → Repository). Use when creating new API endpoints, designing API contracts, or reviewing API patterns.
4
+ allowed_tools:
5
+ - Read
6
+ - Write
7
+ - Edit
8
+ - Glob
9
+ - Grep
10
+ ---
11
+
12
+ # Backend API Design — Quick Reference
13
+
14
+ ## URL Patterns
15
+
16
+ ```
17
+ GET /api/v1/employees # List (plural)
18
+ GET /api/v1/employee # Get one (?id=xxx)
19
+ POST /api/v1/employee # Create
20
+ POST /api/v1/employee/update # Update (?id=xxx)
21
+ POST /api/v1/employee/delete # Soft delete (?id=xxx)
22
+ POST /api/v1/employee/restore # Restore (?id=xxx)
23
+ POST /api/v1/sync/employees # Action
24
+ ```
25
+
26
+ ## Hard Rules
27
+
28
+ - **NO path params** — always `@QueryValue`, never `@PathVariable`
29
+ - **Singular for single resource** — `/employee` not `/employees/{id}`
30
+ - **Plural for collections** — `/employees`
31
+ - **Verb sub-paths for actions** — `/delete`, `/restore`, `/sync`
32
+
33
+ ## Layered Architecture
34
+
35
+ ```
36
+ Controller → thin, just delegates
37
+
38
+ Manager → business logic, transactions, Either returns
39
+
40
+ Repository → data access only, no business logic
41
+ ```
42
+
43
+ ### Controller: Thin Wrapper
44
+ - Parse query params with defaults
45
+ - Delegate to manager
46
+ - Unwrap Either with `.throwOrValue()`
47
+ - NO business logic, NO repository access
48
+
49
+ ### Manager: Business Logic
50
+ - Returns `Either<ClientException, T>`
51
+ - Wraps DB ops in `transaction(db.primary) { }`
52
+ - Orchestrates multiple repositories
53
+ - Validates business rules
54
+
55
+ ### Repository: Data Access
56
+ - Returns entities or null
57
+ - `db.replica` for reads, `db.primary` for writes
58
+ - Always checks `deletedAt.isNull()`
59
+
60
+ ## Quick Code Reference
61
+
62
+ The core controller delegation pattern:
63
+
64
+ ```kotlin
65
+ @Get("/employee")
66
+ suspend fun getEmployee(@QueryValue id: UUID): EmployeeResponse {
67
+ return employeeManager.findById(id).throwOrValue()
68
+ }
69
+ ```
70
+
71
+ - **Response models** — `companion object { fun from(entity) }` in `module-client/response/{domain}/`
72
+ - **Pagination** — offset-based, manager returns `EmployeeListResponse` with `items`, `total`, `page`, `limit`, `hasMore`
73
+ - **Errors** — return `ClientError.NOT_FOUND.asException().left()` from managers, never throw
74
+ - **Factory beans** — `@Factory` class with `@Singleton` method, wire repos + db into manager
75
+
76
+ > See code-patterns.md for complete controller, response model, pagination, error handling, and factory bean templates.
77
+
78
+ ## Gotchas
79
+
80
+ - **Multi-word `@QueryValue` params MUST have explicit snake_case names.** The frontend axios interceptor sends `project_id` but Micronaut matches the literal param name. Write `@QueryValue("project_id") projectId: UUID`, not bare `@QueryValue projectId: UUID`.
81
+ - **Don't use `@Put`, `@Delete`, or `@Patch`.** This is RPC-style — all mutations are `@Post`. The only `@Get` is for reads.
82
+ - **Controllers that inject repositories are a code smell.** If you see `private val fooRepository: FooRepository` in a controller, move it to the manager.
83
+ - **`andWhere {}` not second `.where {}`.** Calling `.where {}` twice replaces the first condition. Use `.andWhere {}` to chain.
84
+ - **Don't forget `@ExecuteOn(TaskExecutors.IO)`.** Without it, suspend functions may hang or run on the wrong thread pool. Every controller needs it.
@@ -0,0 +1,138 @@
1
+ # API Design — Code Patterns
2
+
3
+ > This file is referenced by SKILL.md. Read it when implementing actual endpoints.
4
+
5
+ ## Controller Template
6
+
7
+ ```kotlin
8
+ @ExecuteOn(TaskExecutors.IO) // REQUIRED for suspend
9
+ @Validated
10
+ @Controller("/api/v1/admin")
11
+ @Secured(OAuthSecurityRule.ADMIN)
12
+ class EmployeeController(
13
+ private val employeeManager: EmployeeManager // Managers ONLY
14
+ ) {
15
+ @Get("/employee")
16
+ suspend fun getEmployee(@QueryValue id: UUID): EmployeeResponse {
17
+ return employeeManager.findById(id).throwOrValue()
18
+ }
19
+
20
+ @Get("/employees")
21
+ suspend fun listEmployees(
22
+ @QueryValue page: Int?,
23
+ @QueryValue limit: Int?,
24
+ @QueryValue status: String?
25
+ ): EmployeeListResponse {
26
+ return employeeManager.list(
27
+ page = page ?: 1,
28
+ limit = (limit ?: 20).coerceAtMost(100),
29
+ status = status
30
+ ).throwOrValue()
31
+ }
32
+ }
33
+ ```
34
+
35
+ ## Response Models
36
+
37
+ All in `module-client/response/{domain}/`:
38
+
39
+ ```kotlin
40
+ data class EmployeeResponse(
41
+ val id: UUID,
42
+ val name: String,
43
+ val email: String,
44
+ val status: String,
45
+ val createdAt: Instant
46
+ ) {
47
+ companion object {
48
+ fun from(entity: EmployeeEntity) = EmployeeResponse(
49
+ id = entity.id,
50
+ name = entity.name,
51
+ email = entity.email,
52
+ status = entity.status,
53
+ createdAt = entity.createdAt
54
+ )
55
+ }
56
+ }
57
+
58
+ data class EmployeeListResponse(
59
+ val items: List<EmployeeResponse>,
60
+ val total: Int,
61
+ val page: Int,
62
+ val limit: Int,
63
+ val hasMore: Boolean
64
+ )
65
+ ```
66
+
67
+ ## Pagination Pattern
68
+
69
+ ```kotlin
70
+ override suspend fun list(
71
+ page: Int,
72
+ limit: Int,
73
+ status: String?
74
+ ): Either<ClientException, EmployeeListResponse> {
75
+ val offset = (page - 1) * limit
76
+
77
+ val (items, total) = transaction(db.replica) {
78
+ val query = EmployeesTable
79
+ .selectAll()
80
+ .where { EmployeesTable.deletedAt.isNull() }
81
+
82
+ if (status != null) {
83
+ query.andWhere { EmployeesTable.status eq status }
84
+ }
85
+
86
+ val total = query.count().toInt()
87
+ val items = query
88
+ .orderBy(EmployeesTable.createdAt to SortOrder.DESC)
89
+ .limit(limit)
90
+ .offset(offset.toLong())
91
+ .map { convert(it) }
92
+
93
+ items to total
94
+ }
95
+
96
+ return EmployeeListResponse(
97
+ items = items.map { EmployeeResponse.from(it) },
98
+ total = total,
99
+ page = page,
100
+ limit = limit,
101
+ hasMore = (page * limit) < total
102
+ ).right()
103
+ }
104
+ ```
105
+
106
+ ## Error Pattern
107
+
108
+ ```kotlin
109
+ // Not found
110
+ val entity = repository.byId(id)
111
+ ?: return ClientError.NOT_FOUND.asException().left()
112
+
113
+ // Already exists
114
+ val existing = repository.byEmail(email)
115
+ if (existing != null) {
116
+ return ClientError.ALREADY_EXISTS.asException().left()
117
+ }
118
+
119
+ // Validation
120
+ if (request.name.isBlank()) {
121
+ return ClientError.INVALID_INPUT.asException("Name is required").left()
122
+ }
123
+ ```
124
+
125
+ ## Factory Bean
126
+
127
+ ```kotlin
128
+ @Factory
129
+ class EmployeeManagerFactory {
130
+ @Singleton
131
+ fun provideEmployeeManager(
132
+ employeeRepository: EmployeeRepository,
133
+ db: DatabaseContext
134
+ ): EmployeeManager {
135
+ return DefaultEmployeeManager(employeeRepository, db)
136
+ }
137
+ }
138
+ ```