@c0x12c/spartan-ai-toolkit 1.0.1
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.
- package/.claude-plugin/marketplace.json +16 -0
- package/.claude-plugin/plugin.json +12 -0
- package/README.md +300 -0
- package/VERSION +1 -0
- package/agents/idea-killer.md +72 -0
- package/agents/micronaut-backend-expert.md +45 -0
- package/agents/research-planner.md +70 -0
- package/agents/solution-architect-cto.md +49 -0
- package/bin/cli.js +589 -0
- package/claude-md/00-header.md +39 -0
- package/claude-md/01-core.md +94 -0
- package/claude-md/05-database.md +20 -0
- package/claude-md/11-backend-micronaut.md +36 -0
- package/claude-md/20-frontend-react.md +23 -0
- package/claude-md/30-project-mgmt.md +91 -0
- package/claude-md/40-product.md +36 -0
- package/claude-md/50-ops.md +34 -0
- package/claude-md/60-research.md +75 -0
- package/claude-md/90-footer.md +21 -0
- package/commands/spartan/brainstorm.md +134 -0
- package/commands/spartan/brownfield.md +157 -0
- package/commands/spartan/careful.md +94 -0
- package/commands/spartan/content.md +17 -0
- package/commands/spartan/context-save.md +161 -0
- package/commands/spartan/daily.md +42 -0
- package/commands/spartan/debug.md +156 -0
- package/commands/spartan/deep-dive.md +55 -0
- package/commands/spartan/deploy.md +207 -0
- package/commands/spartan/e2e.md +264 -0
- package/commands/spartan/env-setup.md +166 -0
- package/commands/spartan/fe-review.md +134 -0
- package/commands/spartan/figma-to-code.md +244 -0
- package/commands/spartan/forensics.md +46 -0
- package/commands/spartan/freeze.md +84 -0
- package/commands/spartan/full-run.md +78 -0
- package/commands/spartan/fundraise.md +53 -0
- package/commands/spartan/gsd-upgrade.md +376 -0
- package/commands/spartan/guard.md +42 -0
- package/commands/spartan/init-project.md +178 -0
- package/commands/spartan/interview.md +154 -0
- package/commands/spartan/kickoff.md +52 -0
- package/commands/spartan/kotlin-service.md +109 -0
- package/commands/spartan/lean-canvas.md +222 -0
- package/commands/spartan/map-codebase.md +72 -0
- package/commands/spartan/migration.md +82 -0
- package/commands/spartan/next-app.md +317 -0
- package/commands/spartan/next-feature.md +197 -0
- package/commands/spartan/outreach.md +16 -0
- package/commands/spartan/phase.md +119 -0
- package/commands/spartan/pitch.md +18 -0
- package/commands/spartan/pr-ready.md +200 -0
- package/commands/spartan/project.md +106 -0
- package/commands/spartan/quickplan.md +122 -0
- package/commands/spartan/research.md +19 -0
- package/commands/spartan/review.md +102 -0
- package/commands/spartan/teardown.md +161 -0
- package/commands/spartan/testcontainer.md +97 -0
- package/commands/spartan/think.md +221 -0
- package/commands/spartan/unfreeze.md +13 -0
- package/commands/spartan/update.md +81 -0
- package/commands/spartan/validate.md +193 -0
- package/commands/spartan/workstreams.md +109 -0
- package/commands/spartan/write.md +16 -0
- package/commands/spartan.md +222 -0
- package/frameworks/00-framework-comparison-guide.md +317 -0
- package/frameworks/01-lean-canvas.md +196 -0
- package/frameworks/02-design-sprint.md +304 -0
- package/frameworks/03-foundation-sprint.md +337 -0
- package/frameworks/04-business-model-canvas.md +391 -0
- package/frameworks/05-customer-development.md +426 -0
- package/frameworks/06-jobs-to-be-done.md +358 -0
- package/frameworks/07-mom-test.md +392 -0
- package/frameworks/08-value-proposition-canvas.md +488 -0
- package/frameworks/09-javelin-board.md +428 -0
- package/frameworks/10-build-measure-learn.md +467 -0
- package/frameworks/11-mvp-approaches.md +533 -0
- package/frameworks/think-before-build.md +593 -0
- package/lib/assembler.js +52 -0
- package/lib/packs.js +16 -0
- package/lib/resolver.js +144 -0
- package/lib/resolver.test.js +140 -0
- package/package.json +48 -0
- package/packs/backend-micronaut.yaml +34 -0
- package/packs/backend-nodejs.yaml +15 -0
- package/packs/backend-python.yaml +15 -0
- package/packs/core.yaml +25 -0
- package/packs/database.yaml +21 -0
- package/packs/frontend-react.yaml +23 -0
- package/packs/ops.yaml +16 -0
- package/packs/packs.compiled.json +281 -0
- package/packs/product.yaml +20 -0
- package/packs/project-mgmt.yaml +21 -0
- package/packs/research.yaml +39 -0
- package/packs/shared-backend.yaml +14 -0
- package/rules/backend-micronaut/API_DESIGN.md +250 -0
- package/rules/backend-micronaut/CONTROLLERS.md +755 -0
- package/rules/backend-micronaut/KOTLIN.md +483 -0
- package/rules/backend-micronaut/RETROFIT_PLACEMENT.md +258 -0
- package/rules/backend-micronaut/SERVICES_AND_BEANS.md +673 -0
- package/rules/core/NAMING_CONVENTIONS.md +208 -0
- package/rules/database/ORM_AND_REPO.md +393 -0
- package/rules/database/SCHEMA.md +146 -0
- package/rules/database/TRANSACTIONS.md +311 -0
- package/rules/frontend-react/FRONTEND.md +344 -0
- package/rules/shared-backend/ARCHITECTURE.md +46 -0
- package/skills/api-endpoint-creator/SKILL.md +560 -0
- package/skills/api-endpoint-creator/error-handling-guide.md +244 -0
- package/skills/api-endpoint-creator/examples.md +522 -0
- package/skills/api-endpoint-creator/testing-patterns.md +302 -0
- package/skills/article-writing/SKILL.md +95 -0
- package/skills/backend-api-design/SKILL.md +187 -0
- package/skills/brainstorm/SKILL.md +85 -0
- package/skills/competitive-teardown/SKILL.md +105 -0
- package/skills/content-engine/SKILL.md +101 -0
- package/skills/database-patterns/SKILL.md +145 -0
- package/skills/database-table-creator/SKILL.md +588 -0
- package/skills/database-table-creator/examples.md +552 -0
- package/skills/database-table-creator/migration-template.sql +68 -0
- package/skills/database-table-creator/validation-checklist.md +337 -0
- package/skills/deep-research/SKILL.md +94 -0
- package/skills/idea-validation/SKILL.md +115 -0
- package/skills/investor-materials/SKILL.md +115 -0
- package/skills/investor-outreach/SKILL.md +98 -0
- package/skills/kotlin-best-practices/SKILL.md +145 -0
- package/skills/market-research/SKILL.md +113 -0
- package/skills/security-checklist/SKILL.md +150 -0
- package/skills/startup-pipeline/SKILL.md +125 -0
- package/skills/testing-strategies/SKILL.md +156 -0
- package/skills/ui-ux-pro-max/SKILL.md +377 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/templates/competitor-analysis.md +60 -0
- package/templates/content/AGENT_TEMPLATE.md +47 -0
- package/templates/content/COMMAND_TEMPLATE.md +27 -0
- package/templates/content/RULE_TEMPLATE.md +40 -0
- package/templates/content/SKILL_TEMPLATE.md +41 -0
- package/templates/idea-canvas.md +47 -0
- package/templates/prd-template.md +84 -0
- package/templates/project-readme.md +35 -0
- package/templates/user-interview.md +69 -0
- package/templates/validation-checklist.md +108 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# API Endpoint Testing Patterns
|
|
2
|
+
|
|
3
|
+
How to write integration tests for API endpoints in the Insight 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/insight/c0x12c/client/ProjectClient.kt`
|
|
63
|
+
|
|
64
|
+
```kotlin
|
|
65
|
+
package insight.c0x12c.client
|
|
66
|
+
|
|
67
|
+
import insight.c0x12c.client.request.insight.CreateProjectRequest
|
|
68
|
+
import insight.c0x12c.client.response.insight.ProjectResponse
|
|
69
|
+
import insight.c0x12c.client.response.insight.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,95 @@
|
|
|
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
|
+
---
|
|
5
|
+
|
|
6
|
+
# Article Writing
|
|
7
|
+
|
|
8
|
+
Write long-form content that sounds like a person wrote it.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
- Blog posts, essays, guides, tutorials
|
|
13
|
+
- Turning notes or research into an article
|
|
14
|
+
- Matching an existing voice from examples
|
|
15
|
+
- Cleaning up and tightening existing writing
|
|
16
|
+
|
|
17
|
+
## Rules
|
|
18
|
+
|
|
19
|
+
1. Start with something concrete: example, number, story, or code block.
|
|
20
|
+
2. Explain after the example, not before.
|
|
21
|
+
3. Short, direct sentences.
|
|
22
|
+
4. Use real numbers when you have them.
|
|
23
|
+
5. Never make up facts, company data, or quotes.
|
|
24
|
+
|
|
25
|
+
## Banned Patterns
|
|
26
|
+
|
|
27
|
+
Delete and rewrite any of these:
|
|
28
|
+
- "In today's rapidly evolving landscape"
|
|
29
|
+
- "Moreover", "Furthermore", "Additionally"
|
|
30
|
+
- "game-changer", "cutting-edge", "revolutionary"
|
|
31
|
+
- Vague claims with no proof
|
|
32
|
+
- Bio claims you can't back up
|
|
33
|
+
|
|
34
|
+
## Writing Process
|
|
35
|
+
|
|
36
|
+
1. Know the audience and goal
|
|
37
|
+
2. Outline: one purpose per section
|
|
38
|
+
3. Start each section with evidence or example
|
|
39
|
+
4. Only keep sentences that earn their spot
|
|
40
|
+
5. Cut anything that sounds like a template
|
|
41
|
+
|
|
42
|
+
## Structure Tips
|
|
43
|
+
|
|
44
|
+
### Technical Guides
|
|
45
|
+
- Open with what the reader gets
|
|
46
|
+
- Code or terminal examples in every section
|
|
47
|
+
- End with real takeaways, not soft summary
|
|
48
|
+
|
|
49
|
+
### Essays / Opinion
|
|
50
|
+
- Start with tension or a sharp point
|
|
51
|
+
- One argument per section
|
|
52
|
+
- Back opinions with examples
|
|
53
|
+
|
|
54
|
+
### Newsletters
|
|
55
|
+
- First screen must be strong
|
|
56
|
+
- Mix insight with updates
|
|
57
|
+
- Clear section labels, easy to skim
|
|
58
|
+
|
|
59
|
+
## Interaction Style
|
|
60
|
+
|
|
61
|
+
**No BS. Honest feedback only.**
|
|
62
|
+
|
|
63
|
+
This is a two-way talk:
|
|
64
|
+
- I ask you questions → you answer
|
|
65
|
+
- You ask me questions → I think hard, give you options, then answer
|
|
66
|
+
|
|
67
|
+
**When I ask you a question, I always:**
|
|
68
|
+
1. Think about it first
|
|
69
|
+
2. Give you 2-3 options with my honest take on each
|
|
70
|
+
3. Tell you which one I'd pick and why
|
|
71
|
+
4. Then ask what you think
|
|
72
|
+
|
|
73
|
+
**When you ask me something:**
|
|
74
|
+
- I give you a straight answer
|
|
75
|
+
- I tell you if a section is boring, weak, or filler
|
|
76
|
+
- I push for real examples over vague claims
|
|
77
|
+
|
|
78
|
+
**Never:**
|
|
79
|
+
- Ask a question without giving options
|
|
80
|
+
- Let filler paragraphs stay in the draft
|
|
81
|
+
- Say "it depends" without picking a side
|
|
82
|
+
- Skip the "this part is weak" feedback
|
|
83
|
+
- Write AI-sounding content and call it done
|
|
84
|
+
|
|
85
|
+
## Before You Deliver
|
|
86
|
+
|
|
87
|
+
- Facts match sources
|
|
88
|
+
- No filler or corporate talk
|
|
89
|
+
- Voice matches examples (if given)
|
|
90
|
+
- Every section adds something new
|
|
91
|
+
- Format fits the platform
|
|
92
|
+
|
|
93
|
+
## Output
|
|
94
|
+
|
|
95
|
+
Save to the project's research or build folder depending on context.
|
|
@@ -0,0 +1,187 @@
|
|
|
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
|
+
---
|
|
5
|
+
|
|
6
|
+
# Backend API Design — Quick Reference
|
|
7
|
+
|
|
8
|
+
## URL Patterns
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
GET /api/v1/employees # List (plural)
|
|
12
|
+
GET /api/v1/employee # Get one (?id=xxx)
|
|
13
|
+
POST /api/v1/employee # Create
|
|
14
|
+
POST /api/v1/employee/update # Update (?id=xxx)
|
|
15
|
+
POST /api/v1/employee/delete # Soft delete (?id=xxx)
|
|
16
|
+
POST /api/v1/employee/restore # Restore (?id=xxx)
|
|
17
|
+
POST /api/v1/sync/employees # Action
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Hard Rules
|
|
21
|
+
|
|
22
|
+
- **NO path params** — always `@QueryValue`, never `@PathVariable`
|
|
23
|
+
- **Singular for single resource** — `/employee` not `/employees/{id}`
|
|
24
|
+
- **Plural for collections** — `/employees`
|
|
25
|
+
- **Verb sub-paths for actions** — `/delete`, `/restore`, `/sync`
|
|
26
|
+
|
|
27
|
+
## Controller Template
|
|
28
|
+
|
|
29
|
+
```kotlin
|
|
30
|
+
@ExecuteOn(TaskExecutors.IO) // REQUIRED for suspend
|
|
31
|
+
@Validated
|
|
32
|
+
@Controller("/api/v1/admin")
|
|
33
|
+
@Secured(OAuthSecurityRule.ADMIN)
|
|
34
|
+
class EmployeeController(
|
|
35
|
+
private val employeeManager: EmployeeManager // Managers ONLY
|
|
36
|
+
) {
|
|
37
|
+
@Get("/employee")
|
|
38
|
+
suspend fun getEmployee(@QueryValue id: UUID): EmployeeResponse {
|
|
39
|
+
return employeeManager.findById(id).throwOrValue()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Get("/employees")
|
|
43
|
+
suspend fun listEmployees(
|
|
44
|
+
@QueryValue page: Int?,
|
|
45
|
+
@QueryValue limit: Int?,
|
|
46
|
+
@QueryValue status: String?
|
|
47
|
+
): EmployeeListResponse {
|
|
48
|
+
return employeeManager.list(
|
|
49
|
+
page = page ?: 1,
|
|
50
|
+
limit = (limit ?: 20).coerceAtMost(100),
|
|
51
|
+
status = status
|
|
52
|
+
).throwOrValue()
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Layered Architecture
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
Controller → thin, just delegates
|
|
61
|
+
↓
|
|
62
|
+
Manager → business logic, transactions, Either returns
|
|
63
|
+
↓
|
|
64
|
+
Repository → data access only, no business logic
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Controller: Thin Wrapper
|
|
68
|
+
- Parse query params with defaults
|
|
69
|
+
- Delegate to manager
|
|
70
|
+
- Unwrap Either with `.throwOrValue()`
|
|
71
|
+
- NO business logic, NO repository access
|
|
72
|
+
|
|
73
|
+
### Manager: Business Logic
|
|
74
|
+
- Returns `Either<ClientException, T>`
|
|
75
|
+
- Wraps DB ops in `transaction(db.primary) { }`
|
|
76
|
+
- Orchestrates multiple repositories
|
|
77
|
+
- Validates business rules
|
|
78
|
+
|
|
79
|
+
### Repository: Data Access
|
|
80
|
+
- Returns entities or null
|
|
81
|
+
- `db.replica` for reads, `db.primary` for writes
|
|
82
|
+
- Always checks `deletedAt.isNull()`
|
|
83
|
+
|
|
84
|
+
## Response Models
|
|
85
|
+
|
|
86
|
+
All in `module-client/response/{domain}/`:
|
|
87
|
+
|
|
88
|
+
```kotlin
|
|
89
|
+
data class EmployeeResponse(
|
|
90
|
+
val id: UUID,
|
|
91
|
+
val name: String,
|
|
92
|
+
val email: String,
|
|
93
|
+
val status: String,
|
|
94
|
+
val createdAt: Instant
|
|
95
|
+
) {
|
|
96
|
+
companion object {
|
|
97
|
+
fun from(entity: EmployeeEntity) = EmployeeResponse(
|
|
98
|
+
id = entity.id,
|
|
99
|
+
name = entity.name,
|
|
100
|
+
email = entity.email,
|
|
101
|
+
status = entity.status,
|
|
102
|
+
createdAt = entity.createdAt
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
data class EmployeeListResponse(
|
|
108
|
+
val items: List<EmployeeResponse>,
|
|
109
|
+
val total: Int,
|
|
110
|
+
val page: Int,
|
|
111
|
+
val limit: Int,
|
|
112
|
+
val hasMore: Boolean
|
|
113
|
+
)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Pagination Pattern
|
|
117
|
+
|
|
118
|
+
```kotlin
|
|
119
|
+
override suspend fun list(
|
|
120
|
+
page: Int,
|
|
121
|
+
limit: Int,
|
|
122
|
+
status: String?
|
|
123
|
+
): Either<ClientException, EmployeeListResponse> {
|
|
124
|
+
val offset = (page - 1) * limit
|
|
125
|
+
|
|
126
|
+
val (items, total) = transaction(db.replica) {
|
|
127
|
+
val query = EmployeesTable
|
|
128
|
+
.selectAll()
|
|
129
|
+
.where { EmployeesTable.deletedAt.isNull() }
|
|
130
|
+
|
|
131
|
+
if (status != null) {
|
|
132
|
+
query.andWhere { EmployeesTable.status eq status }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
val total = query.count().toInt()
|
|
136
|
+
val items = query
|
|
137
|
+
.orderBy(EmployeesTable.createdAt to SortOrder.DESC)
|
|
138
|
+
.limit(limit)
|
|
139
|
+
.offset(offset.toLong())
|
|
140
|
+
.map { convert(it) }
|
|
141
|
+
|
|
142
|
+
items to total
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return EmployeeListResponse(
|
|
146
|
+
items = items.map { EmployeeResponse.from(it) },
|
|
147
|
+
total = total,
|
|
148
|
+
page = page,
|
|
149
|
+
limit = limit,
|
|
150
|
+
hasMore = (page * limit) < total
|
|
151
|
+
).right()
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Error Pattern
|
|
156
|
+
|
|
157
|
+
```kotlin
|
|
158
|
+
// Not found
|
|
159
|
+
val entity = repository.byId(id)
|
|
160
|
+
?: return ClientError.NOT_FOUND.asException().left()
|
|
161
|
+
|
|
162
|
+
// Already exists
|
|
163
|
+
val existing = repository.byEmail(email)
|
|
164
|
+
if (existing != null) {
|
|
165
|
+
return ClientError.ALREADY_EXISTS.asException().left()
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Validation
|
|
169
|
+
if (request.name.isBlank()) {
|
|
170
|
+
return ClientError.INVALID_INPUT.asException("Name is required").left()
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Factory Bean
|
|
175
|
+
|
|
176
|
+
```kotlin
|
|
177
|
+
@Factory
|
|
178
|
+
class EmployeeManagerFactory {
|
|
179
|
+
@Singleton
|
|
180
|
+
fun provideEmployeeManager(
|
|
181
|
+
employeeRepository: EmployeeRepository,
|
|
182
|
+
db: DatabaseContext
|
|
183
|
+
): EmployeeManager {
|
|
184
|
+
return DefaultEmployeeManager(employeeRepository, db)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: brainstorm
|
|
3
|
+
description: Run a structured brainstorm session for startup ideas. Takes a theme or problem and generates ideas with quick gut-checks. Use when the user wants to explore a space or generate new ideas.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Brainstorm
|
|
7
|
+
|
|
8
|
+
Turn a vague direction into a list of concrete ideas worth testing.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
- User has a theme, problem, or space they want to explore
|
|
13
|
+
- Generating new project ideas
|
|
14
|
+
- Expanding on a half-baked idea
|
|
15
|
+
- Looking for adjacent opportunities
|
|
16
|
+
|
|
17
|
+
## Process
|
|
18
|
+
|
|
19
|
+
### 1. Set the Frame
|
|
20
|
+
Ask or confirm:
|
|
21
|
+
- What space? (health, finance, dev tools, etc.)
|
|
22
|
+
- Who's the user? (founders, students, parents, etc.)
|
|
23
|
+
- Any limits? (no hardware, must be B2B, etc.)
|
|
24
|
+
- What's the goal? (side project, VC-backed, bootstrap)
|
|
25
|
+
|
|
26
|
+
### 2. Generate Ideas (Go Wide)
|
|
27
|
+
Produce 8-15 ideas. For each:
|
|
28
|
+
- **Name** - Working title
|
|
29
|
+
- **One-liner** - What it does in 10 words
|
|
30
|
+
- **Who** - Target user
|
|
31
|
+
- **Problem** - What pain it fixes
|
|
32
|
+
- **Why now** - Why this didn't work before
|
|
33
|
+
- **Quick risk** - Biggest thing that could kill it
|
|
34
|
+
|
|
35
|
+
### 3. Gut-Check Filter
|
|
36
|
+
Rate each idea on:
|
|
37
|
+
- Demand signal (0-5): Are people searching for this? Paying for alternatives?
|
|
38
|
+
- Buildability (0-5): Can you build an MVP in 2 weeks?
|
|
39
|
+
- Moat potential (0-5): Can you defend this?
|
|
40
|
+
|
|
41
|
+
### 4. Pick Top 3
|
|
42
|
+
Pick the 3 best ideas. For each, write:
|
|
43
|
+
- The key bet (what must be true for this to work)
|
|
44
|
+
- First validation step (cheapest way to test)
|
|
45
|
+
- Existing competitors (who's doing something close)
|
|
46
|
+
|
|
47
|
+
## Interaction Style
|
|
48
|
+
|
|
49
|
+
**No BS. Honest feedback only.**
|
|
50
|
+
|
|
51
|
+
This is a two-way talk:
|
|
52
|
+
- I ask you questions → you answer
|
|
53
|
+
- You ask me questions → I think hard, give you options, then answer
|
|
54
|
+
|
|
55
|
+
**When I ask you a question, I always:**
|
|
56
|
+
1. Think about it first
|
|
57
|
+
2. Give you 2-3 options with my honest take on each
|
|
58
|
+
3. Tell you which one I'd pick and why
|
|
59
|
+
4. Then ask what you think
|
|
60
|
+
|
|
61
|
+
**When you ask me something:**
|
|
62
|
+
- I give you a straight answer
|
|
63
|
+
- I tell you what's wrong with your thinking if I see it
|
|
64
|
+
- I push back if your idea is weak
|
|
65
|
+
|
|
66
|
+
**Never:**
|
|
67
|
+
- Ask a question without giving options
|
|
68
|
+
- Sugarcoat bad ideas
|
|
69
|
+
- Say "it depends" without picking a side
|
|
70
|
+
- Give soft answers to hard questions
|
|
71
|
+
- Skip the tough feedback to be nice
|
|
72
|
+
|
|
73
|
+
## Rules
|
|
74
|
+
|
|
75
|
+
- No idea is too dumb during generation
|
|
76
|
+
- But be brutal during filtering
|
|
77
|
+
- "Interesting" is not enough. Need a real pain point.
|
|
78
|
+
- If the user already has a direction, skip to expanding that
|
|
79
|
+
- Don't fall in love with clever solutions to fake problems
|
|
80
|
+
|
|
81
|
+
## Output
|
|
82
|
+
|
|
83
|
+
Save to the project's `01-brainstorm/` folder.
|
|
84
|
+
|
|
85
|
+
After delivering, suggest: "Want me to run /validate on any of these?"
|