@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,522 @@
1
+ # API Endpoint Creator - Code Examples
2
+
3
+ This file has real patterns from the codebase. **Follow these patterns exactly**.
4
+
5
+ ---
6
+
7
+ ## Complete Example: Project Management Endpoint
8
+
9
+ Shows the full flow from Controller → Manager → Repository.
10
+
11
+ ### 1. Controller Layer
12
+
13
+ **File**: `app/api-application/src/main/kotlin/com/yourcompany/controller/admin/ProjectController.kt`
14
+
15
+ ```kotlin
16
+ package com.yourcompany.controller.admin
17
+
18
+ import com.yourcompany.auth.contract.model.UserAuthentication
19
+ import com.yourcompany.client.request.{domain}.CreateProjectRequest
20
+ import com.yourcompany.client.request.{domain}.UpdateProjectRequest
21
+ import com.yourcompany.client.response.{domain}.ProjectResponse
22
+ import com.yourcompany.client.response.{domain}.ProjectListResponse
23
+ import com.yourcompany.exception.throwOrValue
24
+ import com.yourcompany.{domain}.contract.ProjectManager
25
+ import io.micronaut.http.annotation.*
26
+ import io.micronaut.scheduling.TaskExecutors
27
+ import io.micronaut.scheduling.annotation.ExecuteOn
28
+ import io.micronaut.security.annotation.Secured
29
+ import io.micronaut.validation.Validated
30
+ import io.swagger.v3.oas.annotations.tags.Tag
31
+ import jakarta.validation.Valid
32
+ import java.util.UUID
33
+
34
+ @ExecuteOn(TaskExecutors.IO)
35
+ @Validated
36
+ @Controller("/api/v1/admin")
37
+ @Tag(name = "Projects", description = "Project management API")
38
+ @Secured(OAuthSecurityRule.ADMIN)
39
+ class ProjectController(
40
+ private val projectManager: ProjectManager
41
+ ) {
42
+
43
+ @Get("/projects")
44
+ suspend fun listProjects(
45
+ @QueryValue page: Int?,
46
+ @QueryValue limit: Int?,
47
+ @QueryValue status: String?
48
+ ): ProjectListResponse {
49
+ return projectManager.list(
50
+ page = page ?: 1,
51
+ limit = limit ?: 20,
52
+ status = status
53
+ ).throwOrValue()
54
+ }
55
+
56
+ @Get("/project")
57
+ suspend fun getProject(
58
+ @QueryValue id: UUID
59
+ ): ProjectResponse {
60
+ return projectManager.byId(id).throwOrValue()
61
+ }
62
+
63
+ @Post("/project")
64
+ suspend fun createProject(
65
+ @Valid @Body request: CreateProjectRequest
66
+ ): ProjectResponse {
67
+ return projectManager.create(request).throwOrValue()
68
+ }
69
+
70
+ @Post("/project/update")
71
+ suspend fun updateProject(
72
+ @QueryValue id: UUID,
73
+ @Valid @Body request: UpdateProjectRequest
74
+ ): ProjectResponse {
75
+ return projectManager.update(id, request).throwOrValue()
76
+ }
77
+
78
+ @Post("/project/delete")
79
+ suspend fun deleteProject(
80
+ @QueryValue id: UUID
81
+ ): Boolean {
82
+ return projectManager.deleteById(id).throwOrValue()
83
+ }
84
+ }
85
+ ```
86
+
87
+ **Controller Pattern Key Points**:
88
+ - `@ExecuteOn(TaskExecutors.IO)` for suspend function support
89
+ - `@Controller` defines base path
90
+ - `@Secured(OAuthSecurityRule.ADMIN)` for admin-only access
91
+ - All IDs as `@QueryValue` (never path params)
92
+ - Methods are `suspend` for coroutines
93
+ - **Thin controllers**: Only delegate to manager
94
+ - Use `.throwOrValue()` to unwrap `Either`
95
+ - NO inline data classes
96
+
97
+ ---
98
+
99
+ ### 2. Manager Interface
100
+
101
+ **File**: `app/module-{domain}/module-api/src/main/kotlin/com/yourcompany/insight/contract/ProjectManager.kt`
102
+
103
+ ```kotlin
104
+ package com.yourcompany.{domain}.contract
105
+
106
+ import arrow.core.Either
107
+ import com.yourcompany.client.request.{domain}.CreateProjectRequest
108
+ import com.yourcompany.client.request.{domain}.UpdateProjectRequest
109
+ import com.yourcompany.client.response.{domain}.ProjectResponse
110
+ import com.yourcompany.client.response.{domain}.ProjectListResponse
111
+ import com.yourcompany.exception.ClientException
112
+ import java.util.UUID
113
+
114
+ interface ProjectManager {
115
+ suspend fun list(
116
+ page: Int,
117
+ limit: Int,
118
+ status: String?
119
+ ): Either<ClientException, ProjectListResponse>
120
+
121
+ suspend fun byId(id: UUID): Either<ClientException, ProjectResponse>
122
+
123
+ suspend fun create(
124
+ request: CreateProjectRequest
125
+ ): Either<ClientException, ProjectResponse>
126
+
127
+ suspend fun update(
128
+ id: UUID,
129
+ request: UpdateProjectRequest
130
+ ): Either<ClientException, ProjectResponse>
131
+
132
+ suspend fun deleteById(id: UUID): Either<ClientException, Boolean>
133
+ }
134
+ ```
135
+
136
+ **Manager Interface Key Points**:
137
+ - All methods return `Either<ClientException, T>`
138
+ - Use `suspend` for async operations
139
+ - No implementation details
140
+
141
+ ---
142
+
143
+ ### 3. Manager Implementation
144
+
145
+ **File**: `app/module-{domain}/module-impl/src/main/kotlin/com/yourcompany/insight/impl/DefaultProjectManager.kt`
146
+
147
+ ```kotlin
148
+ package com.yourcompany.{domain}.impl
149
+
150
+ import arrow.core.Either
151
+ import arrow.core.left
152
+ import arrow.core.right
153
+ import com.yourcompany.database.DatabaseContext
154
+ import com.yourcompany.client.request.{domain}.CreateProjectRequest
155
+ import com.yourcompany.client.request.{domain}.UpdateProjectRequest
156
+ import com.yourcompany.client.response.{domain}.ProjectResponse
157
+ import com.yourcompany.client.response.{domain}.ProjectListResponse
158
+ import com.yourcompany.exception.ClientError
159
+ import com.yourcompany.exception.ClientException
160
+ import com.yourcompany.{domain}.contract.ProjectManager
161
+ import com.yourcompany.postgresql.entity.ProjectEntity
162
+ import com.yourcompany.postgresql.repository.ProjectRepository
163
+ import com.yourcompany.postgresql.repository.EmployeeRepository
164
+ import org.jetbrains.exposed.sql.transactions.transaction
165
+ import java.util.UUID
166
+
167
+ class DefaultProjectManager(
168
+ private val db: DatabaseContext,
169
+ private val projectRepository: ProjectRepository,
170
+ private val employeeRepository: EmployeeRepository
171
+ ) : ProjectManager {
172
+
173
+ override suspend fun byId(id: UUID): Either<ClientException, ProjectResponse> {
174
+ val entity = projectRepository.byId(id)
175
+ ?: return ClientError.PROJECT_NOT_FOUND.asException().left()
176
+
177
+ val ownerName = entity.ownerEmployeeId?.let {
178
+ employeeRepository.byId(it)?.name
179
+ }
180
+
181
+ return ProjectResponse.from(entity, ownerName).right()
182
+ }
183
+
184
+ override suspend fun create(
185
+ request: CreateProjectRequest
186
+ ): Either<ClientException, ProjectResponse> {
187
+ val entity = ProjectEntity(
188
+ name = request.name,
189
+ description = request.description,
190
+ ownerEmployeeId = request.ownerEmployeeId,
191
+ githubOrg = request.githubOrg
192
+ )
193
+
194
+ val inserted = transaction(db.primary) {
195
+ projectRepository.insert(entity)
196
+ }
197
+
198
+ return ProjectResponse.from(inserted, ownerName = null).right()
199
+ }
200
+
201
+ override suspend fun update(
202
+ id: UUID,
203
+ request: UpdateProjectRequest
204
+ ): Either<ClientException, ProjectResponse> {
205
+ projectRepository.byId(id)
206
+ ?: return ClientError.PROJECT_NOT_FOUND.asException().left()
207
+
208
+ val updated = transaction(db.primary) {
209
+ projectRepository.update(
210
+ id = id,
211
+ name = request.name,
212
+ description = request.description,
213
+ ownerEmployeeId = request.ownerEmployeeId,
214
+ status = request.status,
215
+ githubOrg = request.githubOrg
216
+ )
217
+ } ?: return ClientError.PROJECT_NOT_FOUND.asException().left()
218
+
219
+ val ownerName = updated.ownerEmployeeId?.let {
220
+ employeeRepository.byId(it)?.name
221
+ }
222
+
223
+ return ProjectResponse.from(updated, ownerName).right()
224
+ }
225
+
226
+ override suspend fun deleteById(id: UUID): Either<ClientException, Boolean> {
227
+ val deleted = transaction(db.primary) {
228
+ projectRepository.deleteById(id)
229
+ }
230
+
231
+ return if (deleted != null) {
232
+ true.right()
233
+ } else {
234
+ ClientError.PROJECT_NOT_FOUND.asException().left()
235
+ }
236
+ }
237
+ }
238
+ ```
239
+
240
+ ---
241
+
242
+ ### 4. Response Model with Companion Object
243
+
244
+ **File**: `app/module-client/src/main/kotlin/com/yourcompany/client/response/insight/ProjectResponse.kt`
245
+
246
+ ```kotlin
247
+ package com.yourcompany.client.response.{domain}
248
+
249
+ import com.yourcompany.postgresql.entity.ProjectEntity
250
+ import java.time.Instant
251
+ import java.util.UUID
252
+
253
+ data class ProjectResponse(
254
+ val id: UUID,
255
+ val name: String,
256
+ val description: String?,
257
+ val ownerEmployeeId: UUID?,
258
+ val ownerName: String?,
259
+ val status: String,
260
+ val githubOrg: String?,
261
+ val createdAt: Instant,
262
+ val updatedAt: Instant?
263
+ ) {
264
+ companion object {
265
+ fun from(
266
+ entity: ProjectEntity,
267
+ ownerName: String?,
268
+ contributorCount: Int = 0
269
+ ): ProjectResponse = ProjectResponse(
270
+ id = entity.id,
271
+ name = entity.name,
272
+ description = entity.description,
273
+ ownerEmployeeId = entity.ownerEmployeeId,
274
+ ownerName = ownerName,
275
+ status = entity.status,
276
+ githubOrg = entity.githubOrg,
277
+ createdAt = entity.createdAt,
278
+ updatedAt = entity.updatedAt
279
+ )
280
+ }
281
+ }
282
+
283
+ data class ProjectListResponse(
284
+ val items: List<ProjectResponse>,
285
+ val total: Int,
286
+ val page: Int,
287
+ val limit: Int,
288
+ val hasMore: Boolean
289
+ )
290
+ ```
291
+
292
+ **Response Model Key Points**:
293
+ - `companion object { fun from() }` for entity-to-response conversion
294
+ - Extra params for data not in the entity (ownerName, counts)
295
+ - Lives in `module-client/response/` directory
296
+
297
+ ---
298
+
299
+ ### 5. Request Model
300
+
301
+ **File**: `app/module-client/src/main/kotlin/com/yourcompany/client/request/insight/ProjectRequests.kt`
302
+
303
+ ```kotlin
304
+ package com.yourcompany.client.request.{domain}
305
+
306
+ import java.util.UUID
307
+
308
+ data class CreateProjectRequest(
309
+ val name: String,
310
+ val description: String? = null,
311
+ val ownerEmployeeId: UUID? = null,
312
+ val githubOrg: String? = null
313
+ )
314
+
315
+ data class UpdateProjectRequest(
316
+ val name: String? = null,
317
+ val description: String? = null,
318
+ val ownerEmployeeId: UUID? = null,
319
+ val status: String? = null,
320
+ val githubOrg: String? = null
321
+ )
322
+ ```
323
+
324
+ ---
325
+
326
+ ### 6. Factory Bean
327
+
328
+ **File**: `app/module-{domain}/module-impl/src/main/kotlin/com/yourcompany/runtime/factory/{Domain}ManagerFactory.kt`
329
+
330
+ ```kotlin
331
+ package com.yourcompany.runtime.factory
332
+
333
+ import com.yourcompany.database.DatabaseContext
334
+ import com.yourcompany.{domain}.contract.ProjectManager
335
+ import com.yourcompany.{domain}.impl.DefaultProjectManager
336
+ import com.yourcompany.postgresql.repository.ProjectRepository
337
+ import com.yourcompany.postgresql.repository.EmployeeRepository
338
+ import io.micronaut.context.annotation.Factory
339
+ import jakarta.inject.Singleton
340
+
341
+ @Factory
342
+ class {Domain}ManagerFactory {
343
+
344
+ @Singleton
345
+ fun provideProjectManager(
346
+ db: DatabaseContext,
347
+ projectRepository: ProjectRepository,
348
+ employeeRepository: EmployeeRepository
349
+ ): ProjectManager {
350
+ return DefaultProjectManager(
351
+ db = db,
352
+ projectRepository = projectRepository,
353
+ employeeRepository = employeeRepository
354
+ )
355
+ }
356
+ }
357
+ ```
358
+
359
+ ---
360
+
361
+ ### 7. Retrofit Client
362
+
363
+ **File**: `app/module-client/src/main/kotlin/com/yourcompany/client/ProjectClient.kt`
364
+
365
+ ```kotlin
366
+ package com.yourcompany.client
367
+
368
+ import com.yourcompany.client.request.{domain}.CreateProjectRequest
369
+ import com.yourcompany.client.request.{domain}.UpdateProjectRequest
370
+ import com.yourcompany.client.response.{domain}.ProjectResponse
371
+ import com.yourcompany.client.response.{domain}.ProjectListResponse
372
+ import retrofit2.http.*
373
+ import java.util.UUID
374
+
375
+ interface ProjectClient {
376
+
377
+ @GET("/api/v1/admin/projects")
378
+ suspend fun listProjects(
379
+ @Header("Authorization") authorization: String,
380
+ @Query("page") page: Int? = null,
381
+ @Query("limit") limit: Int? = null,
382
+ @Query("status") status: String? = null
383
+ ): ProjectListResponse
384
+
385
+ @GET("/api/v1/admin/project")
386
+ suspend fun getProject(
387
+ @Header("Authorization") authorization: String,
388
+ @Query("id") id: UUID
389
+ ): ProjectResponse
390
+
391
+ @POST("/api/v1/admin/project")
392
+ suspend fun createProject(
393
+ @Header("Authorization") authorization: String,
394
+ @Body request: CreateProjectRequest
395
+ ): ProjectResponse
396
+
397
+ @POST("/api/v1/admin/project/update")
398
+ suspend fun updateProject(
399
+ @Header("Authorization") authorization: String,
400
+ @Query("id") id: UUID,
401
+ @Body request: UpdateProjectRequest
402
+ ): ProjectResponse
403
+
404
+ @POST("/api/v1/admin/project/delete")
405
+ suspend fun deleteProject(
406
+ @Header("Authorization") authorization: String,
407
+ @Query("id") id: UUID
408
+ ): Boolean
409
+ }
410
+ ```
411
+
412
+ ---
413
+
414
+ ## Error Handling Patterns
415
+
416
+ ### Pattern 1: Not Found
417
+ ```kotlin
418
+ val entity = repository.byId(id)
419
+ ?: return ClientError.PROJECT_NOT_FOUND.asException().left()
420
+ return ProjectResponse.from(entity, ownerName).right()
421
+ ```
422
+
423
+ ### Pattern 2: Already Exists
424
+ ```kotlin
425
+ val existing = repository.byEmail(email)
426
+ if (existing != null) {
427
+ return ClientError.EMAIL_ALREADY_IN_USE.asException().left()
428
+ }
429
+ ```
430
+
431
+ ### Pattern 3: Multiple Validations
432
+ ```kotlin
433
+ val existing = repository.byId(id)
434
+ ?: return ClientError.NOT_FOUND.asException().left()
435
+
436
+ if (existing.ownerId != currentUserId) {
437
+ return ClientError.UNAUTHORIZED.asException().left()
438
+ }
439
+ ```
440
+
441
+ ---
442
+
443
+ ## Anti-Patterns to AVOID
444
+
445
+ **Wrong: Business logic in controller**
446
+ ```kotlin
447
+ @Post("/project")
448
+ suspend fun create(@Valid @Body request: CreateProjectRequest): ProjectResponse {
449
+ if (request.name.isBlank()) { throw BadRequestException("Name required") }
450
+ val entity = repository.insert(...)
451
+ return ProjectResponse.from(entity)
452
+ }
453
+ ```
454
+
455
+ **Correct: Thin controller**
456
+ ```kotlin
457
+ @Post("/project")
458
+ suspend fun create(@Valid @Body request: CreateProjectRequest): ProjectResponse {
459
+ return projectManager.create(request).throwOrValue()
460
+ }
461
+ ```
462
+
463
+ ---
464
+
465
+ **Wrong: Path parameters**
466
+ ```kotlin
467
+ @Get("/project/{id}")
468
+ suspend fun getProject(@PathVariable id: UUID): ProjectResponse
469
+ ```
470
+
471
+ **Correct: Query parameters**
472
+ ```kotlin
473
+ @Get("/project")
474
+ suspend fun getProject(@QueryValue id: UUID): ProjectResponse
475
+ ```
476
+
477
+ ---
478
+
479
+ **Wrong: Manager throws exceptions**
480
+ ```kotlin
481
+ override suspend fun byId(id: UUID): ProjectResponse {
482
+ val entity = repository.byId(id)
483
+ ?: throw NotFoundException("Project not found")
484
+ return ProjectResponse.from(entity)
485
+ }
486
+ ```
487
+
488
+ **Correct: Manager returns Either**
489
+ ```kotlin
490
+ override suspend fun byId(id: UUID): Either<ClientException, ProjectResponse> {
491
+ val entity = repository.byId(id)
492
+ ?: return ClientError.PROJECT_NOT_FOUND.asException().left()
493
+ return ProjectResponse.from(entity, ownerName).right()
494
+ }
495
+ ```
496
+
497
+ ---
498
+
499
+ ## Summary Checklist
500
+
501
+ **Controller**:
502
+ - [ ] Has `@ExecuteOn(TaskExecutors.IO)`
503
+ - [ ] Has `@Secured(...)`
504
+ - [ ] Uses `@QueryValue` for IDs (no path params)
505
+ - [ ] Methods are `suspend`
506
+ - [ ] Only delegates to manager
507
+ - [ ] Uses `.throwOrValue()`
508
+ - [ ] NO inline data classes
509
+
510
+ **Manager**:
511
+ - [ ] Returns `Either<ClientException, T>`
512
+ - [ ] Uses `ClientError.{NAME}.asException().left()` for errors
513
+ - [ ] Uses `.right()` for success
514
+ - [ ] NO `!!` operators
515
+ - [ ] Converts entities using `Response.from(entity)`
516
+
517
+ **Models**:
518
+ - [ ] Response models in `module-client/response/`
519
+ - [ ] Request models in `module-client/request/`
520
+ - [ ] Response has `companion object { fun from() }`
521
+
522
+ **Follow these patterns exactly for consistent code.**