@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,414 @@
1
+ # Kotlin Rules
2
+
3
+ > Full guide: use `/kotlin-best-practices` skill
4
+
5
+ ## Null Safety (CRITICAL)
6
+
7
+ **The force unwrap operator `!!` is FORBIDDEN in this codebase.**
8
+
9
+ It causes runtime crashes, makes code unpredictable, and goes against Kotlin's null safety. There are no exceptions.
10
+
11
+ ### What to Use Instead
12
+
13
+ ```kotlin
14
+ // Safe call + elvis
15
+ val email = decodedToken.email ?: return AuthError.INVALID_CREDENTIALS.asException().left()
16
+
17
+ // Null check (enables smart cast)
18
+ if (user == null) {
19
+ return AuthError.AUTHENTICATION_FAILED.asException().left()
20
+ }
21
+ generateTokensAndResponse(user, provider.value) // user is non-null here
22
+
23
+ // requireNotNull (only when null = programming error)
24
+ val validUser = requireNotNull(user) { "User must not be null here" }
25
+ ```
26
+
27
+ ---
28
+
29
+ ## No Workarounds (CRITICAL)
30
+
31
+ **NEVER use workarounds. Always fix the root cause. This rule has no exceptions.**
32
+
33
+ **Forbidden patterns:**
34
+
35
+ | Pattern | Instead |
36
+ |---------|---------|
37
+ | `@Suppress("UNUSED_PARAMETER")` | Remove the parameter, update call sites |
38
+ | `@Suppress("DEPRECATION")` | Use the non-deprecated replacement |
39
+ | `// TODO: Fix this later` + `!!` | Fix it now with proper null handling |
40
+ | `// HACK:` comments | Fix the root cause using proper layers |
41
+ | Placeholder parameters `reserved: String = ""` | Only include parameters you use |
42
+
43
+ ```kotlin
44
+ // WRONG
45
+ @Suppress("UNUSED_PARAMETER")
46
+ private fun createCommit(employeeId: UUID, authorUsername: String)
47
+
48
+ // CORRECT — remove unused param, update all call sites
49
+ private fun createCommit(authorUsername: String)
50
+ ```
51
+
52
+ **Always**: understand root cause, fix actual problem, update all related code (call sites, tests, docs).
53
+
54
+ ---
55
+
56
+ ## Error Handling
57
+
58
+ ### Use Either for All Fallible Operations
59
+
60
+ Return `Either<ClientException, T>` from managers. Never throw exceptions in business logic.
61
+
62
+ ```kotlin
63
+ suspend fun loginUser(email: String): Either<ClientException, User> {
64
+ val user = userRepository.byEmail(email)
65
+ ?: return ClientError.USER_NOT_FOUND.asException().left()
66
+ return user.right()
67
+ }
68
+ ```
69
+
70
+ ```kotlin
71
+ // Good - return error type
72
+ fun validateEmail(email: String): Either<ValidationError, String> {
73
+ return if (email.contains("@")) {
74
+ email.right()
75
+ } else {
76
+ ValidationError.INVALID_EMAIL.left()
77
+ }
78
+ }
79
+
80
+ // Bad - throwing exceptions
81
+ fun validateEmail(email: String): String {
82
+ if (!email.contains("@")) {
83
+ throw IllegalArgumentException("Invalid email")
84
+ }
85
+ return email
86
+ }
87
+ ```
88
+
89
+ ### Exhaustive When Expressions
90
+
91
+ Use exhaustive `when` on enums (no `else` needed if all cases covered):
92
+ ```kotlin
93
+ when (provider) {
94
+ OAuthProvider.GOOGLE -> handleGoogle()
95
+ OAuthProvider.GITHUB -> handleGithub()
96
+ OAuthProvider.TWITTER -> handleTwitter()
97
+ }
98
+ ```
99
+
100
+ ### Transaction Safety
101
+
102
+ Always return from transaction blocks and handle nullable results:
103
+ ```kotlin
104
+ transaction(db.primary) {
105
+ val user = userRepository.byId(userId)
106
+ if (user == null) {
107
+ return@transaction AuthError.USER_NOT_FOUND.asException().left()
108
+ }
109
+ return@transaction Success(user).right()
110
+ }
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Enum Usage
116
+
117
+ **NEVER hardcode strings when an enum exists.** Use `EnumName.VALUE.value` everywhere: return values, fallback values, comparisons, default values.
118
+
119
+ ```kotlin
120
+ // WRONG - hardcoded string
121
+ ifLeft = { "critical" }
122
+ val status = "healthy"
123
+ if (value == "at_risk") { ... }
124
+
125
+ // CORRECT - use the enum
126
+ ifLeft = { HealthStatus.CRITICAL.value }
127
+ val status = HealthStatus.HEALTHY.value
128
+ if (value == HealthStatus.AT_RISK.value) { ... }
129
+ ```
130
+
131
+ **Why**: Hardcoded strings break silently when enum values change. The compiler can't catch typos in strings.
132
+
133
+ **Known enums**: `HealthStatus`, `ReportType`, `EvaluationMode`, `ProjectRole`, `ProjectStatus`.
134
+
135
+ Convert String -> enum at boundaries (controllers). Use enum references everywhere else.
136
+
137
+ ---
138
+
139
+ ## Conversions
140
+
141
+ Put `companion object { fun from(entity) }` inside Response DTOs. **Never** create separate mapper files or private extension functions.
142
+
143
+ ```kotlin
144
+ data class UserResponse(val id: UUID, val email: String, val displayName: String) {
145
+ companion object {
146
+ fun from(entity: UserEntity) = UserResponse(
147
+ id = entity.id,
148
+ email = entity.email,
149
+ displayName = entity.displayName ?: entity.email
150
+ )
151
+ }
152
+ }
153
+ ```
154
+
155
+ When extra data needed, pass as additional params: `fun from(entity, extraData, count)`.
156
+ When extra data needs fetching, use a private manager helper that calls `Response.from()`.
157
+
158
+ **Never do:** separate mapper files, private extension functions in managers, scattered inline mapping.
159
+
160
+ **Location:** Response DTOs go in `module-client/response/`. `module-client` depends on `module-repository`.
161
+
162
+ ---
163
+
164
+ ## Style
165
+
166
+ ### No Magic Numbers
167
+
168
+ Never hardcode durations, timeouts, limits. Put them in `application.yml` and inject via config class.
169
+
170
+ ```kotlin
171
+ // WRONG
172
+ val expiresAt = Instant.now().plus(7, ChronoUnit.DAYS)
173
+ val maxRetries = 3
174
+
175
+ // CORRECT
176
+ val expiresAt = Instant.now().plusSeconds(tokenConfig.refreshTokenExpirationSeconds)
177
+ val maxRetries = appConfig.maxRetries
178
+ ```
179
+
180
+ ### No Inline Fully-Qualified Imports
181
+
182
+ Always use `import` at the top. Never write fully-qualified class names inline.
183
+
184
+ ```kotlin
185
+ // WRONG
186
+ val token = java.util.UUID.randomUUID().toString()
187
+
188
+ // CORRECT
189
+ import java.util.UUID
190
+ val token = UUID.randomUUID().toString()
191
+ ```
192
+
193
+ ### Config Objects Over Individual Fields
194
+
195
+ Inject the config object. Don't unpack fields into separate constructor params.
196
+
197
+ ```kotlin
198
+ // WRONG — too many params, fragile
199
+ class AuthManager(
200
+ private val refreshTokenExpirationSeconds: Long,
201
+ private val passwordResetExpirationSeconds: Long,
202
+ private val emailVerificationExpirationSeconds: Long
203
+ )
204
+
205
+ // CORRECT — one config object
206
+ class AuthManager(
207
+ private val tokenExpirationConfig: TokenExpirationConfig
208
+ )
209
+ ```
210
+
211
+ ### JSON Naming
212
+
213
+ Backend uses `snake_case` for all JSON. Jackson does this automatically.
214
+ Kotlin code stays `camelCase` — Jackson converts at serialization time.
215
+
216
+ ### Named Arguments
217
+ Use named arguments for 2+ parameters:
218
+ ```kotlin
219
+ fn(a = x, b = y)
220
+ ```
221
+
222
+ ### Comments
223
+ Comments explain **WHY**, not **WHAT**. No KDoc that restates the function name.
224
+
225
+ ```kotlin
226
+ // WRONG - useless comments
227
+ /**
228
+ * Default implementation of [UserManager].
229
+ * Provides CRUD operations for users with validation.
230
+ */
231
+ class DefaultUserManager(...) : UserManager
232
+
233
+ /**
234
+ * Validates a configuration value.
235
+ * @param configKey The configuration key
236
+ * @param value The value to validate
237
+ * @return ValidationResult
238
+ */
239
+ fun validate(configKey: String, value: JsonNode): ValidationResult
240
+ ```
241
+
242
+ ```kotlin
243
+ // CORRECT - no comments needed, code is self-explanatory
244
+ class DefaultUserManager(...) : UserManager
245
+
246
+ fun validate(configKey: String, value: JsonNode): ValidationResult
247
+ ```
248
+
249
+ ```kotlin
250
+ // CORRECT - comments that explain WHY
251
+ // Twilio requires E.164 format, strip any formatting
252
+ val normalized = phoneNumber.replace(Regex("[^+\\d]"), "")
253
+
254
+ // Cache for 5 minutes to reduce DB load during peak hours
255
+ val CACHE_TTL = 5.minutes
256
+ ```
257
+
258
+ ### Configuration URLs
259
+ Wrap default URL values in backticks in YAML:
260
+ ```yaml
261
+ # Good
262
+ base-url: ${VENDOR_BASE_URL:`https://api.example.com`}
263
+
264
+ # Bad - may cause build errors
265
+ base-url: ${VENDOR_BASE_URL:https://api.example.com}
266
+ ```
267
+
268
+ ### Formatting
269
+ - **2-space indentation**, no tabs
270
+ - **LF line endings** (Unix style)
271
+ - **120 char** line length guide, 1000 hard limit
272
+ - UTF-8 encoding, final newline always
273
+ - No star imports (wildcard imports disabled)
274
+ - Import order: `*,java.*,javax.*,kotlin.*`
275
+
276
+ ```kotlin
277
+ // Braces on same line
278
+ class MyClass {
279
+ fun myFunction() {
280
+ // code
281
+ }
282
+ }
283
+
284
+ // Multiline parameters - aligned
285
+ fun longFunctionName(
286
+ firstParameter: String,
287
+ secondParameter: Int,
288
+ thirdParameter: Boolean
289
+ ) {
290
+ // body
291
+ }
292
+ ```
293
+
294
+ ### Enforcement
295
+ ```bash
296
+ ./gradlew ktlintCheck # Check style
297
+ ./gradlew ktlintFormat # Auto-fix style
298
+ ```
299
+
300
+ ---
301
+
302
+ ## Constants Registry
303
+
304
+ Use the right pattern for the right kind of constant:
305
+
306
+ | Use Case | Pattern |
307
+ |----------|---------|
308
+ | Finite, known values (status, role) | `enum class` |
309
+ | Open-ended, growing list (event names, metrics) | `object` with `const val` |
310
+ | Structured paths (S3 keys, URLs) | Type-safe builder class |
311
+
312
+ ```kotlin
313
+ // Enums for BOUNDED sets
314
+ enum class ProjectStatus(val value: String) {
315
+ ACTIVE("active"), ARCHIVED("archived")
316
+ }
317
+
318
+ // Object registries for OPEN-ENDED sets
319
+ object ActivityNames {
320
+ const val AGENT_CONVERSATION = "AgentConversation"
321
+ const val PROCESS_DOCUMENT = "ProcessDocument"
322
+ const val SYNC_USER_PROFILE = "SyncUserProfile"
323
+ }
324
+
325
+ // Type-safe builders for STRUCTURED paths
326
+ object StorageKey {
327
+ fun user(userId: UUID) = UserStorageKey(userId)
328
+
329
+ class UserStorageKey(private val userId: UUID) {
330
+ fun avatar() = "user/$userId/avatar"
331
+ fun document(docId: UUID) = "user/$userId/documents/$docId"
332
+ }
333
+ }
334
+ ```
335
+
336
+ Once a registry passes ~50 constants, split by domain with nested objects:
337
+ ```kotlin
338
+ object ActivityNames {
339
+ object Agent {
340
+ const val CONVERSATION = "AgentConversation"
341
+ }
342
+ object Document {
343
+ const val PROCESS = "ProcessDocument"
344
+ }
345
+ }
346
+ // Usage: ActivityNames.Agent.CONVERSATION
347
+ ```
348
+
349
+ ---
350
+
351
+ ## Nested Input/Output Data Classes
352
+
353
+ For internal service contracts, nest `Input`/`Output` inside the class that uses them:
354
+
355
+ ```kotlin
356
+ class ProcessDocumentService {
357
+ data class Input(
358
+ val documentId: UUID,
359
+ val userId: UUID,
360
+ val options: ProcessOptions = ProcessOptions()
361
+ )
362
+ data class Output(
363
+ val resultId: UUID,
364
+ val pageCount: Int
365
+ )
366
+
367
+ suspend fun run(input: Input): Either<ClientException, Output> { ... }
368
+ }
369
+
370
+ // Usage is self-documenting:
371
+ val result = service.run(ProcessDocumentService.Input(documentId = id, userId = userId))
372
+ ```
373
+
374
+ **When to use:** Internal service-to-service contracts, background jobs, batch operations.
375
+ **When NOT to use:** API request/response models — those go in `module-client`.
376
+
377
+ ---
378
+
379
+ ## No Silent Try-Catch
380
+
381
+ Never catch and swallow exceptions. If you need a fallback, log the failure first.
382
+
383
+ ```kotlin
384
+ // WRONG — silent swallow, hides bugs
385
+ val result = try {
386
+ expensiveOperation()
387
+ } catch (e: Exception) {
388
+ fallbackValue // No logging — bug disappears silently
389
+ }
390
+
391
+ // CORRECT for non-suspend — log then fallback
392
+ val result = runCatching { blockingOperation() }
393
+ .onFailure { logger.warn("Operation failed, using fallback", it) }
394
+ .getOrDefault(fallbackValue)
395
+
396
+ // CAREFUL — runCatching catches ALL Throwables including CancellationException.
397
+ // In suspend code, this breaks cancellation silently:
398
+ val result = runCatching { suspendFunction() } // WRONG — swallows CancellationException
399
+ .getOrDefault(fallback)
400
+
401
+ // CORRECT for suspend — rethrow CancellationException
402
+ val result = runCatching { suspendFunction() }
403
+ .onFailure { if (it is CancellationException) throw it }
404
+ .onFailure { logger.warn("Operation failed", it) }
405
+ .getOrDefault(fallback)
406
+
407
+ // BEST — use Either, no exceptions at all
408
+ suspend fun doWork(): Either<ClientException, Result> { ... }
409
+ ```
410
+
411
+ **Rules:**
412
+ - Never catch-and-swallow. Log the failure if you need a fallback.
413
+ - `runCatching` is fine for non-suspend code. For suspend functions, always rethrow `CancellationException` or use Either.
414
+ - If you don't need a fallback, use Either and let the caller decide.
@@ -0,0 +1,290 @@
1
+ # Retrofit Client Placement Rules
2
+
3
+ > Full guide: use `/kotlin-best-practices` skill
4
+
5
+ ---
6
+
7
+ ## Core Rule
8
+
9
+ **NEVER place Retrofit client interfaces in modules that have kapt enabled for Micronaut.**
10
+
11
+ ### Correct Pattern
12
+
13
+ ```
14
+ module-client/ # NO kapt
15
+ ├── src/main/kotlin/
16
+ │ └── com/yourcompany/client/
17
+ │ ├── TemplateServiceClient.kt ✅ Retrofit @GET/@POST
18
+ │ ├── TerraformServiceClient.kt ✅ Retrofit interfaces
19
+ │ └── dto/
20
+ │ ├── template/
21
+ │ │ └── TemplateDtos.kt ✅ Plain data classes
22
+ │ └── terraform/
23
+ │ └── TerraformDtos.kt ✅ No Micronaut annotations
24
+ └── build.gradle
25
+ # NO kotlin-kapt plugin ✅
26
+ dependencies {
27
+ implementation(libs.networking.retrofit)
28
+ implementation(libs.networking.okhttp)
29
+ implementation(libs.arrow.core)
30
+ implementation(libs.jackson.*)
31
+ }
32
+ ```
33
+
34
+ ### Incorrect Pattern
35
+
36
+ ```
37
+ module-auth/ # HAS kapt enabled
38
+ ├── src/main/kotlin/
39
+ │ └── com/yourcompany/auth/
40
+ │ ├── client/
41
+ │ │ ├── TemplateServiceClient.kt ❌ WILL FAIL
42
+ │ │ └── TerraformServiceClient.kt ❌ kapt can't process
43
+ │ └── dto/
44
+ │ └── TemplateDtos.kt ❌ @Serdeable conflicts
45
+ └── build.gradle
46
+ plugins {
47
+ id("kotlin-kapt") ❌ PROBLEM
48
+ }
49
+ ```
50
+
51
+ ---
52
+
53
+ ## Why This Fails
54
+
55
+ ### Kapt Limitations
56
+
57
+ 1. **Type Alias Issues**
58
+ ```kotlin
59
+ // Kapt can't handle this in stub generation:
60
+ typealias EitherCall<R> = Call<Either<ErrorResponse, R>>
61
+ ```
62
+
63
+ 2. **Complex Generic Types**
64
+ ```kotlin
65
+ // Kapt fails to resolve nested generics:
66
+ fun getStatus(): Call<Either<ErrorResponse, TerraformStatusDto>>
67
+ ```
68
+
69
+ 3. **Annotation Processor Conflicts**
70
+ - Micronaut processors try to process Retrofit interfaces
71
+ - Results in `@error.NonExistentClass()` in generated stubs
72
+ - Build fails with "incompatible types" errors
73
+
74
+ ---
75
+
76
+ ## Implementation Checklist
77
+
78
+ When adding new external service clients:
79
+
80
+ ### 1. Create Client Interface in module-client
81
+
82
+ ```kotlin
83
+ package com.yourcompany.client
84
+
85
+ import arrow.core.Either
86
+ import com.yourcompany.retrofit.ErrorResponse
87
+ import retrofit2.Call
88
+ import retrofit2.http.*
89
+
90
+ interface MyServiceClient {
91
+ @GET("/api/resource")
92
+ fun getResource(): Call<Either<ErrorResponse, ResourceDto>>
93
+
94
+ @POST("/api/resource")
95
+ fun createResource(@Body request: CreateRequestDto): Call<Either<ErrorResponse, ResourceDto>>
96
+ }
97
+ ```
98
+
99
+ **Key Points:**
100
+ - ✅ Package: `com.yourcompany.client` (NOT `com.yourcompany.auth.client`)
101
+ - ✅ Use full type: `Call<Either<ErrorResponse, T>>` (NOT `EitherCall<T>`)
102
+ - ✅ Explicit imports (NOT wildcard `import com.yourcompany.client.dto.*`)
103
+
104
+ ### 2. Create DTOs in module-client
105
+
106
+ ```kotlin
107
+ package com.yourcompany.client.dto.myservice
108
+
109
+ // NO Micronaut annotations! ❌ @Serdeable
110
+ data class ResourceDto(
111
+ val id: String,
112
+ val name: String,
113
+ val createdAt: Instant
114
+ )
115
+
116
+ data class CreateRequestDto(
117
+ val name: String,
118
+ val options: Map<String, Any>
119
+ )
120
+ ```
121
+
122
+ **Key Points:**
123
+ - ✅ Plain Kotlin data classes
124
+ - ✅ NO `@Serdeable` annotation
125
+ - ✅ Jackson handles serialization automatically
126
+ - ✅ Package: `com.yourcompany.client.dto.*`
127
+
128
+ ### 3. Create Client Factory in module-auth
129
+
130
+ ```kotlin
131
+ package com.yourcompany.auth.config
132
+
133
+ import com.yourcompany.client.MyServiceClient
134
+ import io.micronaut.context.annotation.Factory
135
+ import jakarta.inject.Singleton
136
+
137
+ @Factory
138
+ class MyServiceConfig(
139
+ @Value("\${app.myservice.url}")
140
+ private val serviceUrl: String,
141
+ private val objectMapper: ObjectMapper
142
+ ) {
143
+
144
+ @Singleton
145
+ fun myServiceClient(): MyServiceClient {
146
+ val retrofit = Retrofits.createRetrofit(
147
+ baseUrl = serviceUrl.toHttpUrl(),
148
+ mapper = objectMapper
149
+ )
150
+ return retrofit.create(MyServiceClient::class.java)
151
+ }
152
+ }
153
+ ```
154
+
155
+ **Key Points:**
156
+ - ✅ Factory is in module-auth (has kapt for `@Factory`)
157
+ - ✅ Client interface is in module-client (no kapt)
158
+ - ✅ Clean separation of concerns
159
+
160
+ ### 4. Update module-auth Dependencies
161
+
162
+ ```groovy
163
+ // module-auth/build.gradle
164
+
165
+ dependencies {
166
+ // Client module (contains Retrofit interfaces)
167
+ implementation(project(":module-client"))
168
+
169
+ // Retrofit runtime (needed for creating clients in factories)
170
+ implementation(libs.networking.retrofit)
171
+ implementation(libs.networking.retrofit.jackson)
172
+ implementation(libs.networking.okhttp)
173
+ implementation(libs.networking.okhttp.logging)
174
+
175
+ // Arrow (for Either type)
176
+ implementation(libs.arrow.core)
177
+ }
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Troubleshooting
183
+
184
+ ### Error: `@error.NonExistentClass()`
185
+
186
+ **Cause:** Retrofit client interface in a module with kapt enabled
187
+
188
+ **Solution:** Move client to `module-client`
189
+
190
+ ### Error: `Unresolved reference 'Serdeable'`
191
+
192
+ **Cause:** DTOs use Micronaut annotation but module lacks dependency
193
+
194
+ **Solution:** Remove `@Serdeable` - Jackson handles it automatically
195
+
196
+ ### Error: `when expression must be exhaustive`
197
+
198
+ **Cause:** Nullable enum in when expression
199
+
200
+ **Solution:**
201
+ ```kotlin
202
+ // ❌ Before:
203
+ when (task.automationType) { ... }
204
+
205
+ // ✅ After:
206
+ val type = task.automationType ?: return ...
207
+ when (type) { ... }
208
+ ```
209
+
210
+ ---
211
+
212
+ ## Module Architecture
213
+
214
+ ```
215
+ backend/
216
+ ├── app/
217
+ │ ├── module-auth/ # Micronaut controllers/services
218
+ │ │ ├── build.gradle # HAS kapt
219
+ │ │ ├── controller/ # @Controller, @Get, @Post
220
+ │ │ ├── service/ # @Singleton services
221
+ │ │ └── config/ # @Factory for clients
222
+ │ │
223
+ │ └── module-client/ # Retrofit clients ONLY
224
+ │ ├── build.gradle # NO kapt ✅
225
+ │ └── src/.../client/ # Retrofit interfaces
226
+
227
+ └── core/
228
+ └── module-retrofit/ # Shared Retrofit utilities
229
+ └── EitherCall.kt # Type aliases, adapters
230
+ ```
231
+
232
+ ---
233
+
234
+ ## Benefits
235
+
236
+ 1. **Clean Compilation**
237
+ - Kapt only processes Micronaut beans
238
+ - Retrofit clients compile with standard Kotlin compiler
239
+
240
+ 2. **Clear Separation**
241
+ - Micronaut infrastructure ↔ External API clients
242
+ - Easy to test and mock
243
+
244
+ 3. **Reusability**
245
+ - Clients can be used in other modules
246
+ - No Micronaut dependency pollution
247
+
248
+ ---
249
+
250
+ ## Related Rules
251
+
252
+ - `KOTLIN.md` — Null safety, no force unwraps
253
+ - `NAMING_CONVENTIONS.md` — Package naming
254
+ - `SCHEMA.md` — Repository patterns
255
+
256
+ ---
257
+
258
+ ---
259
+
260
+ ## Always Retrofit Over Raw OkHttp
261
+
262
+ Always prefer Retrofit interfaces over raw OkHttp for HTTP clients. ~80% less boilerplate.
263
+
264
+ ```kotlin
265
+ // WRONG — 198 lines of OkHttp boilerplate per client
266
+ class DefaultSearchClient(private val okHttpClient: OkHttpClient) {
267
+ fun search(query: String): SearchResult {
268
+ val request = Request.Builder()
269
+ .url("$baseUrl/search")
270
+ .post(objectMapper.writeValueAsString(body).toRequestBody())
271
+ .addHeader("Authorization", "Bearer $apiKey")
272
+ .build()
273
+ val response = okHttpClient.newCall(request).execute()
274
+ // 40+ lines of response parsing, error handling...
275
+ }
276
+ }
277
+
278
+ // CORRECT — 25 lines, Retrofit handles the plumbing
279
+ interface SearchClient {
280
+ @POST("/search")
281
+ suspend fun search(
282
+ @Header("Authorization") auth: String,
283
+ @Body request: SearchRequest
284
+ ): Response<SearchResult>
285
+ }
286
+ ```
287
+
288
+ ---
289
+
290
+ **TL;DR:** Retrofit clients → `module-client` (no kapt). Micronaut beans → `module-auth` (has kapt). Never mix. Always Retrofit, never raw OkHttp.