@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.
- package/.claude-plugin/marketplace.json +16 -0
- package/.claude-plugin/plugin.json +12 -0
- package/README.md +439 -0
- package/VERSION +1 -0
- package/agents/design-critic.md +127 -0
- package/agents/idea-killer.md +72 -0
- package/agents/infrastructure-expert.md +49 -0
- package/agents/micronaut-backend-expert.md +45 -0
- package/agents/phase-reviewer.md +150 -0
- package/agents/research-planner.md +70 -0
- package/agents/solution-architect-cto.md +49 -0
- package/agents/sre-architect.md +49 -0
- package/agents/team-coordinator.md +111 -0
- package/bin/cli.js +780 -0
- package/claude-md/00-header.md +39 -0
- package/claude-md/01-core.md +105 -0
- package/claude-md/05-database.md +20 -0
- package/claude-md/11-backend-micronaut.md +19 -0
- package/claude-md/20-frontend-react.md +44 -0
- package/claude-md/25-ux-design.md +56 -0
- package/claude-md/30-infrastructure.md +24 -0
- package/claude-md/30-project-mgmt.md +119 -0
- package/claude-md/40-product.md +39 -0
- package/claude-md/50-ops.md +34 -0
- package/claude-md/60-research.md +27 -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/build.md +435 -0
- package/commands/spartan/careful.md +94 -0
- package/commands/spartan/commit-message.md +112 -0
- package/commands/spartan/content.md +17 -0
- package/commands/spartan/context-save.md +161 -0
- package/commands/spartan/contribute.md +140 -0
- package/commands/spartan/daily.md +42 -0
- package/commands/spartan/debug.md +308 -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/epic.md +199 -0
- package/commands/spartan/fe-review.md +181 -0
- package/commands/spartan/figma-to-code.md +260 -0
- package/commands/spartan/forensics.md +46 -0
- package/commands/spartan/freeze.md +84 -0
- package/commands/spartan/fundraise.md +53 -0
- package/commands/spartan/gate-review.md +229 -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/init-rules.md +298 -0
- package/commands/spartan/interview.md +154 -0
- package/commands/spartan/kickoff.md +73 -0
- package/commands/spartan/kotlin-service.md +109 -0
- package/commands/spartan/lean-canvas.md +222 -0
- package/commands/spartan/lint-rules.md +122 -0
- package/commands/spartan/map-codebase.md +124 -0
- package/commands/spartan/migration.md +82 -0
- package/commands/spartan/next-app.md +317 -0
- package/commands/spartan/next-feature.md +212 -0
- package/commands/spartan/onboard.md +326 -0
- package/commands/spartan/outreach.md +16 -0
- package/commands/spartan/phase.md +142 -0
- package/commands/spartan/pitch.md +18 -0
- package/commands/spartan/plan.md +210 -0
- package/commands/spartan/pr-ready.md +202 -0
- package/commands/spartan/project.md +106 -0
- package/commands/spartan/qa.md +222 -0
- package/commands/spartan/research.md +254 -0
- package/commands/spartan/review.md +132 -0
- package/commands/spartan/scan-rules.md +173 -0
- package/commands/spartan/sessions.md +143 -0
- package/commands/spartan/spec.md +131 -0
- package/commands/spartan/startup.md +257 -0
- package/commands/spartan/team.md +570 -0
- package/commands/spartan/teardown.md +161 -0
- package/commands/spartan/testcontainer.md +97 -0
- package/commands/spartan/tf-cost.md +123 -0
- package/commands/spartan/tf-deploy.md +116 -0
- package/commands/spartan/tf-drift.md +100 -0
- package/commands/spartan/tf-import.md +107 -0
- package/commands/spartan/tf-module.md +121 -0
- package/commands/spartan/tf-plan.md +100 -0
- package/commands/spartan/tf-review.md +106 -0
- package/commands/spartan/tf-scaffold.md +109 -0
- package/commands/spartan/tf-security.md +147 -0
- package/commands/spartan/think.md +221 -0
- package/commands/spartan/unfreeze.md +13 -0
- package/commands/spartan/update.md +134 -0
- package/commands/spartan/ux.md +1233 -0
- package/commands/spartan/validate.md +193 -0
- package/commands/spartan/web-to-prd.md +706 -0
- package/commands/spartan/workstreams.md +109 -0
- package/commands/spartan/write.md +16 -0
- package/commands/spartan.md +386 -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 +197 -0
- package/lib/assembler.test.js +159 -0
- package/lib/detector.js +166 -0
- package/lib/detector.test.js +221 -0
- package/lib/packs.js +16 -0
- package/lib/resolver.js +272 -0
- package/lib/resolver.test.js +298 -0
- package/lib/worktree.sh +104 -0
- package/package.json +50 -0
- package/packs/backend-micronaut.yaml +35 -0
- package/packs/backend-nodejs.yaml +15 -0
- package/packs/backend-python.yaml +15 -0
- package/packs/core.yaml +37 -0
- package/packs/database.yaml +21 -0
- package/packs/frontend-react.yaml +24 -0
- package/packs/infrastructure.yaml +40 -0
- package/packs/ops.yaml +16 -0
- package/packs/packs.compiled.json +371 -0
- package/packs/product.yaml +22 -0
- package/packs/project-mgmt.yaml +24 -0
- package/packs/research.yaml +39 -0
- package/packs/shared-backend.yaml +14 -0
- package/packs/ux-design.yaml +21 -0
- package/rules/backend-micronaut/API_DESIGN.md +313 -0
- package/rules/backend-micronaut/BATCH_PROCESSING.md +92 -0
- package/rules/backend-micronaut/CONTROLLERS.md +388 -0
- package/rules/backend-micronaut/KOTLIN.md +414 -0
- package/rules/backend-micronaut/RETROFIT_PLACEMENT.md +290 -0
- package/rules/backend-micronaut/SERVICES_AND_BEANS.md +325 -0
- package/rules/core/NAMING_CONVENTIONS.md +208 -0
- package/rules/core/SKILL_AUTHORING.md +174 -0
- package/rules/core/TIMEZONE.md +316 -0
- package/rules/database/ORM_AND_REPO.md +289 -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/infrastructure/MODULES.md +260 -0
- package/rules/infrastructure/NAMING.md +196 -0
- package/rules/infrastructure/PROVIDERS.md +309 -0
- package/rules/infrastructure/SECURITY.md +310 -0
- package/rules/infrastructure/STATE_AND_BACKEND.md +237 -0
- package/rules/infrastructure/STRUCTURE.md +234 -0
- package/rules/infrastructure/VARIABLES.md +285 -0
- package/rules/shared-backend/ARCHITECTURE.md +46 -0
- package/rules/ux-design/DESIGN_PROCESS.md +176 -0
- package/skills/api-endpoint-creator/SKILL.md +455 -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 +109 -0
- package/skills/article-writing/examples.md +59 -0
- package/skills/backend-api-design/SKILL.md +84 -0
- package/skills/backend-api-design/code-patterns.md +138 -0
- package/skills/brainstorm/SKILL.md +95 -0
- package/skills/browser-qa/SKILL.md +87 -0
- package/skills/browser-qa/playwright-snippets.md +110 -0
- package/skills/ci-cd-patterns/SKILL.md +108 -0
- package/skills/ci-cd-patterns/workflows.md +149 -0
- package/skills/competitive-teardown/SKILL.md +93 -0
- package/skills/competitive-teardown/example-analysis.md +50 -0
- package/skills/content-engine/SKILL.md +131 -0
- package/skills/content-engine/examples.md +72 -0
- package/skills/database-patterns/SKILL.md +72 -0
- package/skills/database-patterns/code-templates.md +114 -0
- package/skills/database-table-creator/SKILL.md +141 -0
- package/skills/database-table-creator/examples.md +552 -0
- package/skills/database-table-creator/kotlin-templates.md +400 -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 +80 -0
- package/skills/design-intelligence/SKILL.md +268 -0
- package/skills/design-workflow/SKILL.md +127 -0
- package/skills/design-workflow/checklists.md +45 -0
- package/skills/idea-validation/SKILL.md +129 -0
- package/skills/idea-validation/example-report.md +50 -0
- package/skills/investor-materials/SKILL.md +122 -0
- package/skills/investor-materials/example-outline.md +70 -0
- package/skills/investor-outreach/SKILL.md +112 -0
- package/skills/investor-outreach/examples.md +76 -0
- package/skills/kotlin-best-practices/SKILL.md +58 -0
- package/skills/kotlin-best-practices/code-patterns.md +132 -0
- package/skills/market-research/SKILL.md +99 -0
- package/skills/security-checklist/SKILL.md +65 -0
- package/skills/security-checklist/audit-reference.md +95 -0
- package/skills/service-debugging/SKILL.md +116 -0
- package/skills/service-debugging/common-issues.md +65 -0
- package/skills/startup-pipeline/SKILL.md +152 -0
- package/skills/terraform-best-practices/SKILL.md +244 -0
- package/skills/terraform-module-creator/SKILL.md +284 -0
- package/skills/terraform-review/SKILL.md +222 -0
- package/skills/terraform-security-audit/SKILL.md +280 -0
- package/skills/terraform-service-scaffold/SKILL.md +574 -0
- package/skills/testing-strategies/SKILL.md +116 -0
- package/skills/testing-strategies/examples.md +103 -0
- package/skills/testing-strategies/integration-test-setup.md +71 -0
- package/skills/ui-ux-pro-max/SKILL.md +238 -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/python-setup.md +146 -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/skills/web-to-prd/SKILL.md +478 -0
- package/templates/build-config.yaml +44 -0
- package/templates/commands-config.yaml +55 -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/design-config.md +105 -0
- package/templates/design-doc.md +207 -0
- package/templates/epic.md +100 -0
- package/templates/feature-spec.md +181 -0
- package/templates/idea-canvas.md +47 -0
- package/templates/implementation-plan.md +159 -0
- package/templates/prd-template.md +86 -0
- package/templates/preamble.md +89 -0
- package/templates/project-readme.md +35 -0
- package/templates/quality-gates.md +230 -0
- package/templates/spartan-config.yaml +164 -0
- package/templates/user-interview.md +69 -0
- package/templates/validation-checklist.md +108 -0
- package/templates/workflow-backend-micronaut.md +409 -0
- package/templates/workflow-frontend-react.md +233 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Design Process Rules
|
|
2
|
+
|
|
3
|
+
## The 7-Phase Workflow
|
|
4
|
+
|
|
5
|
+
World-class design follows this order. Don't skip to pixels before understanding the problem.
|
|
6
|
+
|
|
7
|
+
| Phase | What | Output |
|
|
8
|
+
|-------|------|--------|
|
|
9
|
+
| 1. Research | User interviews, competitor audit, data analysis | `design/research/insights.md` |
|
|
10
|
+
| 2. Define | Problem statement, personas, journey map, scope | `design/definition/brief.md` |
|
|
11
|
+
| 3. Ideate | Brainstorm solutions, user flows, information architecture | `design/ideation/flows.md` |
|
|
12
|
+
| 4. Design System | Tokens (color, type, spacing), component inventory | `design/system/tokens.md` |
|
|
13
|
+
| 5. Prototype | Screen design, all states, responsive, motion, accessibility | `design/screens/{feature}.md` |
|
|
14
|
+
| 6. Test | Usability testing plan, findings, severity ratings | `design/test-results.md` |
|
|
15
|
+
| 7. Handoff + QA | Dev specs, implementation checklist, design QA | Updated screen files |
|
|
16
|
+
|
|
17
|
+
## Time Allocation Rule
|
|
18
|
+
|
|
19
|
+
| Phase | Average team | World-class team |
|
|
20
|
+
|-------|-------------|-----------------|
|
|
21
|
+
| Research + Define | 10% | 40% |
|
|
22
|
+
| Ideate | 10% | 10% |
|
|
23
|
+
| Design + Prototype | 50% | 25% |
|
|
24
|
+
| Test | 10% | 15% |
|
|
25
|
+
| Handoff | 15% | 5% (design system handles it) |
|
|
26
|
+
| Iterate | 5% | ongoing |
|
|
27
|
+
|
|
28
|
+
**Rule:** Spend at least 30% of design time BEFORE opening any design tool. Understand the problem first.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Design Token Rules
|
|
33
|
+
|
|
34
|
+
### Tokens Are the Source of Truth
|
|
35
|
+
|
|
36
|
+
Once design tokens exist in `.planning/design/system/tokens.md` or `.planning/design-config.md`, they are BINDING.
|
|
37
|
+
|
|
38
|
+
**FORBIDDEN:**
|
|
39
|
+
- Using Tailwind color defaults (`bg-blue-500`, `bg-purple-600`) when project tokens exist
|
|
40
|
+
- Using generic fonts (`Inter`, `Roboto`, `Arial`, `system-ui`) when project font is defined
|
|
41
|
+
- Using arbitrary spacing (`p-[13px]`, `mt-[7px]`) when spacing scale exists
|
|
42
|
+
- Inventing new colors or fonts not in the token list
|
|
43
|
+
|
|
44
|
+
**REQUIRED:**
|
|
45
|
+
- Read tokens BEFORE generating any UI code
|
|
46
|
+
- Use token names or CSS variables, not raw hex values
|
|
47
|
+
- Follow the spacing grid (4px or 8px base)
|
|
48
|
+
- Use project border radius, not generic `rounded-lg`
|
|
49
|
+
|
|
50
|
+
### Token Format
|
|
51
|
+
|
|
52
|
+
Tokens file must include these sections:
|
|
53
|
+
|
|
54
|
+
```markdown
|
|
55
|
+
## Colors
|
|
56
|
+
| Role | Token | Value | Usage |
|
|
57
|
+
|------|-------|-------|-------|
|
|
58
|
+
| Primary | --color-primary | #hex | Buttons, links, active states |
|
|
59
|
+
| ... | ... | ... | ... |
|
|
60
|
+
|
|
61
|
+
## Typography
|
|
62
|
+
- Font family: [name]
|
|
63
|
+
- Scale: h1 (32px/700) → h6 (14px/600) → body (16px/400) → caption (12px/400)
|
|
64
|
+
|
|
65
|
+
## Spacing
|
|
66
|
+
- Base: [4px or 8px]
|
|
67
|
+
- Scale: xs (4) / sm (8) / md (16) / lg (24) / xl (32) / 2xl (48)
|
|
68
|
+
|
|
69
|
+
## Radius
|
|
70
|
+
- Card: [value]
|
|
71
|
+
- Button: [value]
|
|
72
|
+
- Badge: [value]
|
|
73
|
+
|
|
74
|
+
## Shadows
|
|
75
|
+
- sm: [value]
|
|
76
|
+
- md: [value]
|
|
77
|
+
- lg: [value]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Design All States
|
|
83
|
+
|
|
84
|
+
Every screen and component MUST design these states:
|
|
85
|
+
|
|
86
|
+
| State | What to show |
|
|
87
|
+
|-------|-------------|
|
|
88
|
+
| Default | Normal, populated view |
|
|
89
|
+
| Loading | Skeleton screen or spinner |
|
|
90
|
+
| Empty | Helpful message + action prompt |
|
|
91
|
+
| Error | Friendly message + recovery action |
|
|
92
|
+
| Success | Confirmation feedback |
|
|
93
|
+
| Edge case | Long text, missing data, single item, 100+ items |
|
|
94
|
+
|
|
95
|
+
**FORBIDDEN:** Designing only the happy path. If you don't design the empty state, the developer will show a blank screen.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Responsive Breakpoints
|
|
100
|
+
|
|
101
|
+
Every screen must work at these widths:
|
|
102
|
+
|
|
103
|
+
| Breakpoint | Width | Device |
|
|
104
|
+
|-----------|-------|--------|
|
|
105
|
+
| Mobile | 375px | iPhone SE / small Android |
|
|
106
|
+
| Tablet | 768px | iPad / medium screens |
|
|
107
|
+
| Desktop | 1440px | Standard laptop/desktop |
|
|
108
|
+
|
|
109
|
+
**FORBIDDEN:** Designing only for desktop. Mobile-first or at least mobile-aware.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Accessibility Checklist
|
|
114
|
+
|
|
115
|
+
Every design must pass:
|
|
116
|
+
|
|
117
|
+
- [ ] Color contrast: 4.5:1 minimum for normal text, 3:1 for large text
|
|
118
|
+
- [ ] Touch targets: 44x44px minimum on mobile
|
|
119
|
+
- [ ] Keyboard navigation: tab order matches visual order
|
|
120
|
+
- [ ] Focus states: visible focus ring on all interactive elements
|
|
121
|
+
- [ ] Screen reader: all images have alt text, icon buttons have aria-label
|
|
122
|
+
- [ ] Color not sole indicator: don't use color alone to show status
|
|
123
|
+
- [ ] Motion: respect `prefers-reduced-motion`
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Anti-AI-Generic Rules
|
|
128
|
+
|
|
129
|
+
Designs MUST avoid these generic AI patterns:
|
|
130
|
+
|
|
131
|
+
**Colors:**
|
|
132
|
+
- No purple/violet gradients on white backgrounds
|
|
133
|
+
- No neon accent colors on dark backgrounds
|
|
134
|
+
- No gray-on-gray low-contrast layouts
|
|
135
|
+
- Use the PROJECT'S accent color, not a random one
|
|
136
|
+
|
|
137
|
+
**Layout:**
|
|
138
|
+
- Break the grid sometimes — asymmetry over centered-everything
|
|
139
|
+
- No blob/wave backgrounds unless that's the brand
|
|
140
|
+
- Cards should have visual variety, not all identical rectangles
|
|
141
|
+
|
|
142
|
+
**Typography:**
|
|
143
|
+
- Clear hierarchy: big headlines, medium subheads, small body
|
|
144
|
+
- Font weight contrast matters — don't use 400 for everything
|
|
145
|
+
- Real copy, not "Lorem ipsum" or "Unlock your potential"
|
|
146
|
+
|
|
147
|
+
**Components:**
|
|
148
|
+
- Button hierarchy: one primary, rest secondary/ghost
|
|
149
|
+
- Not every feature needs an icon
|
|
150
|
+
- Empty states should feel designed, not like a bug
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Folder Structure
|
|
155
|
+
|
|
156
|
+
All design artifacts live in `.planning/design/`:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
.planning/design/
|
|
160
|
+
├── research/
|
|
161
|
+
│ ├── interviews.md ← User interview notes + synthesis
|
|
162
|
+
│ ├── competitors.md ← Competitor audit
|
|
163
|
+
│ └── insights.md ← Key findings, "How Might We" questions
|
|
164
|
+
├── definition/
|
|
165
|
+
│ ├── personas.md ← 2-3 behavior-based personas
|
|
166
|
+
│ ├── journey-map.md ← Current user journey with pain points
|
|
167
|
+
│ └── brief.md ← Problem statement, success metrics, scope
|
|
168
|
+
├── ideation/
|
|
169
|
+
│ ├── ideas.md ← Brainstorm output, ranked ideas
|
|
170
|
+
│ └── flows.md ← User flows, information architecture
|
|
171
|
+
├── system/
|
|
172
|
+
│ ├── tokens.md ← Color, typography, spacing tokens
|
|
173
|
+
│ └── components.md ← Component inventory with specs
|
|
174
|
+
└── screens/
|
|
175
|
+
└── {feature-name}.md ← Per-feature screen designs
|
|
176
|
+
```
|
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: api-endpoint-creator
|
|
3
|
+
description: Creates RPC-style endpoint following layered architecture (Controller → Manager → Repository). Use when creating new API endpoints or CRUD operations.
|
|
4
|
+
allowed_tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Write
|
|
7
|
+
- Edit
|
|
8
|
+
- Glob
|
|
9
|
+
- Grep
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# API Endpoint Creator Skill
|
|
13
|
+
|
|
14
|
+
Creates complete RPC-style API endpoints following strict layered architecture patterns.
|
|
15
|
+
|
|
16
|
+
## When to Use
|
|
17
|
+
|
|
18
|
+
- Creating a new REST API endpoint from scratch
|
|
19
|
+
- Adding CRUD operations for a new domain entity
|
|
20
|
+
- Setting up the full stack: Controller → Manager → Repository → Tests
|
|
21
|
+
- Need a Retrofit client for integration testing
|
|
22
|
+
|
|
23
|
+
## Process
|
|
24
|
+
|
|
25
|
+
### 1. Create Response/Request Models
|
|
26
|
+
|
|
27
|
+
Location: `app/module-client/src/main/kotlin/com/yourcompany/client/response/{domain}/`
|
|
28
|
+
|
|
29
|
+
```kotlin
|
|
30
|
+
package com.yourcompany.client.response.{domain}
|
|
31
|
+
|
|
32
|
+
import com.yourcompany.postgresql.entity.{Domain}Entity
|
|
33
|
+
import java.time.Instant
|
|
34
|
+
import java.util.UUID
|
|
35
|
+
|
|
36
|
+
data class {Domain}Response(
|
|
37
|
+
val id: UUID,
|
|
38
|
+
val name: String,
|
|
39
|
+
val status: String,
|
|
40
|
+
val createdAt: Instant,
|
|
41
|
+
val updatedAt: Instant?
|
|
42
|
+
) {
|
|
43
|
+
companion object {
|
|
44
|
+
fun from(entity: {Domain}Entity): {Domain}Response = {Domain}Response(
|
|
45
|
+
id = entity.id,
|
|
46
|
+
name = entity.name,
|
|
47
|
+
status = entity.status,
|
|
48
|
+
createdAt = entity.createdAt,
|
|
49
|
+
updatedAt = entity.updatedAt
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
data class {Domain}ListResponse(
|
|
55
|
+
val items: List<{Domain}Response>,
|
|
56
|
+
val total: Int,
|
|
57
|
+
val page: Int,
|
|
58
|
+
val limit: Int,
|
|
59
|
+
val hasMore: Boolean
|
|
60
|
+
)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Location: `app/module-client/src/main/kotlin/com/yourcompany/client/request/{domain}/`
|
|
64
|
+
|
|
65
|
+
```kotlin
|
|
66
|
+
package com.yourcompany.client.request.{domain}
|
|
67
|
+
|
|
68
|
+
data class Create{Domain}Request(
|
|
69
|
+
val name: String,
|
|
70
|
+
val description: String? = null
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
data class Update{Domain}Request(
|
|
74
|
+
val name: String? = null,
|
|
75
|
+
val description: String? = null,
|
|
76
|
+
val status: String? = null
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Key**: All models in module-client, never in controllers or managers.
|
|
81
|
+
|
|
82
|
+
### 2. Create Controller
|
|
83
|
+
|
|
84
|
+
Location: `app/api-application/src/main/kotlin/com/yourcompany/controller/{Domain}Controller.kt`
|
|
85
|
+
|
|
86
|
+
```kotlin
|
|
87
|
+
package com.yourcompany.controller
|
|
88
|
+
|
|
89
|
+
import com.yourcompany.{domain}.contract.{Domain}Manager
|
|
90
|
+
import com.yourcompany.client.request.{domain}.Create{Domain}Request
|
|
91
|
+
import com.yourcompany.client.request.{domain}.Update{Domain}Request
|
|
92
|
+
import com.yourcompany.client.response.{domain}.{Domain}Response
|
|
93
|
+
import com.yourcompany.client.response.{domain}.{Domain}ListResponse
|
|
94
|
+
import com.yourcompany.exception.throwOrValue
|
|
95
|
+
import io.micronaut.http.annotation.*
|
|
96
|
+
import io.micronaut.scheduling.TaskExecutors
|
|
97
|
+
import io.micronaut.scheduling.annotation.ExecuteOn
|
|
98
|
+
import io.micronaut.security.annotation.Secured
|
|
99
|
+
import io.micronaut.validation.Validated
|
|
100
|
+
import io.swagger.v3.oas.annotations.tags.Tag
|
|
101
|
+
import jakarta.validation.Valid
|
|
102
|
+
import java.util.UUID
|
|
103
|
+
|
|
104
|
+
@ExecuteOn(TaskExecutors.IO)
|
|
105
|
+
@Validated
|
|
106
|
+
@Controller("/api/v1/{domain}")
|
|
107
|
+
@Tag(name = "{Domain}", description = "{Domain} API")
|
|
108
|
+
@Secured(SecurityRule.IS_AUTHENTICATED)
|
|
109
|
+
class {Domain}Controller(
|
|
110
|
+
private val {domain}Manager: {Domain}Manager
|
|
111
|
+
) {
|
|
112
|
+
|
|
113
|
+
@Get("/{domain}s")
|
|
114
|
+
suspend fun list(
|
|
115
|
+
@QueryValue page: Int?,
|
|
116
|
+
@QueryValue limit: Int?,
|
|
117
|
+
@QueryValue status: String?
|
|
118
|
+
): {Domain}ListResponse {
|
|
119
|
+
return {domain}Manager.list(
|
|
120
|
+
page = page ?: 1,
|
|
121
|
+
limit = limit ?: 20,
|
|
122
|
+
status = status
|
|
123
|
+
).throwOrValue()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@Get("/{domain}")
|
|
127
|
+
suspend fun getById(
|
|
128
|
+
@QueryValue id: UUID
|
|
129
|
+
): {Domain}Response {
|
|
130
|
+
return {domain}Manager.byId(id).throwOrValue()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@Post("/{domain}")
|
|
134
|
+
suspend fun create(
|
|
135
|
+
@Valid @Body request: Create{Domain}Request
|
|
136
|
+
): {Domain}Response {
|
|
137
|
+
return {domain}Manager.create(request).throwOrValue()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@Post("/{domain}/update")
|
|
141
|
+
suspend fun update(
|
|
142
|
+
@QueryValue id: UUID,
|
|
143
|
+
@Valid @Body request: Update{Domain}Request
|
|
144
|
+
): {Domain}Response {
|
|
145
|
+
return {domain}Manager.update(id, request).throwOrValue()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@Post("/{domain}/delete")
|
|
149
|
+
suspend fun delete(
|
|
150
|
+
@QueryValue id: UUID
|
|
151
|
+
): Boolean {
|
|
152
|
+
return {domain}Manager.deleteById(id).throwOrValue()
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Key Points**:
|
|
158
|
+
- `@ExecuteOn(TaskExecutors.IO)` required for suspend functions
|
|
159
|
+
- Query params for ALL identifiers (`@QueryValue id: UUID`)
|
|
160
|
+
- Thin methods - just delegate to manager
|
|
161
|
+
- `.throwOrValue()` to unwrap Either
|
|
162
|
+
- Inject Manager only (never Repository)
|
|
163
|
+
- NO inline data classes
|
|
164
|
+
|
|
165
|
+
### 3. Create Manager Interface
|
|
166
|
+
|
|
167
|
+
Location: `app/module-{domain}/module-api/src/main/kotlin/com/yourcompany/{domain}/contract/{Domain}Manager.kt`
|
|
168
|
+
|
|
169
|
+
```kotlin
|
|
170
|
+
package com.yourcompany.{domain}.contract
|
|
171
|
+
|
|
172
|
+
import arrow.core.Either
|
|
173
|
+
import com.yourcompany.client.request.{domain}.Create{Domain}Request
|
|
174
|
+
import com.yourcompany.client.request.{domain}.Update{Domain}Request
|
|
175
|
+
import com.yourcompany.client.response.{domain}.{Domain}Response
|
|
176
|
+
import com.yourcompany.client.response.{domain}.{Domain}ListResponse
|
|
177
|
+
import com.yourcompany.exception.ClientException
|
|
178
|
+
import java.util.UUID
|
|
179
|
+
|
|
180
|
+
interface {Domain}Manager {
|
|
181
|
+
suspend fun list(
|
|
182
|
+
page: Int,
|
|
183
|
+
limit: Int,
|
|
184
|
+
status: String?
|
|
185
|
+
): Either<ClientException, {Domain}ListResponse>
|
|
186
|
+
|
|
187
|
+
suspend fun byId(id: UUID): Either<ClientException, {Domain}Response>
|
|
188
|
+
|
|
189
|
+
suspend fun create(
|
|
190
|
+
request: Create{Domain}Request
|
|
191
|
+
): Either<ClientException, {Domain}Response>
|
|
192
|
+
|
|
193
|
+
suspend fun update(
|
|
194
|
+
id: UUID,
|
|
195
|
+
request: Update{Domain}Request
|
|
196
|
+
): Either<ClientException, {Domain}Response>
|
|
197
|
+
|
|
198
|
+
suspend fun deleteById(id: UUID): Either<ClientException, Boolean>
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 4. Create Manager Implementation
|
|
203
|
+
|
|
204
|
+
Location: `app/module-{domain}/module-impl/src/main/kotlin/com/yourcompany/{domain}/impl/Default{Domain}Manager.kt`
|
|
205
|
+
|
|
206
|
+
```kotlin
|
|
207
|
+
package com.yourcompany.{domain}.impl
|
|
208
|
+
|
|
209
|
+
import arrow.core.Either
|
|
210
|
+
import arrow.core.left
|
|
211
|
+
import arrow.core.right
|
|
212
|
+
import com.yourcompany.database.DatabaseContext
|
|
213
|
+
import com.yourcompany.{domain}.contract.{Domain}Manager
|
|
214
|
+
import com.yourcompany.client.request.{domain}.Create{Domain}Request
|
|
215
|
+
import com.yourcompany.client.request.{domain}.Update{Domain}Request
|
|
216
|
+
import com.yourcompany.client.response.{domain}.{Domain}Response
|
|
217
|
+
import com.yourcompany.client.response.{domain}.{Domain}ListResponse
|
|
218
|
+
import com.yourcompany.exception.ClientError
|
|
219
|
+
import com.yourcompany.exception.ClientException
|
|
220
|
+
import com.yourcompany.postgresql.entity.{Domain}Entity
|
|
221
|
+
import com.yourcompany.postgresql.repository.{Domain}Repository
|
|
222
|
+
import org.jetbrains.exposed.sql.transactions.transaction
|
|
223
|
+
import java.util.UUID
|
|
224
|
+
|
|
225
|
+
class Default{Domain}Manager(
|
|
226
|
+
private val {domain}Repository: {Domain}Repository,
|
|
227
|
+
private val db: DatabaseContext
|
|
228
|
+
) : {Domain}Manager {
|
|
229
|
+
|
|
230
|
+
override suspend fun byId(id: UUID): Either<ClientException, {Domain}Response> {
|
|
231
|
+
val entity = {domain}Repository.byId(id)
|
|
232
|
+
?: return ClientError.{DOMAIN}_NOT_FOUND.asException().left()
|
|
233
|
+
|
|
234
|
+
return {Domain}Response.from(entity).right()
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
override suspend fun create(
|
|
238
|
+
request: Create{Domain}Request
|
|
239
|
+
): Either<ClientException, {Domain}Response> {
|
|
240
|
+
val entity = {Domain}Entity(
|
|
241
|
+
name = request.name,
|
|
242
|
+
description = request.description
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
val inserted = transaction(db.primary) {
|
|
246
|
+
{domain}Repository.insert(entity)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return {Domain}Response.from(inserted).right()
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
override suspend fun update(
|
|
253
|
+
id: UUID,
|
|
254
|
+
request: Update{Domain}Request
|
|
255
|
+
): Either<ClientException, {Domain}Response> {
|
|
256
|
+
val existing = {domain}Repository.byId(id)
|
|
257
|
+
?: return ClientError.{DOMAIN}_NOT_FOUND.asException().left()
|
|
258
|
+
|
|
259
|
+
val updated = transaction(db.primary) {
|
|
260
|
+
{domain}Repository.update(
|
|
261
|
+
id = id,
|
|
262
|
+
name = request.name,
|
|
263
|
+
description = request.description,
|
|
264
|
+
status = request.status
|
|
265
|
+
)
|
|
266
|
+
} ?: return ClientError.{DOMAIN}_NOT_FOUND.asException().left()
|
|
267
|
+
|
|
268
|
+
return {Domain}Response.from(updated).right()
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
override suspend fun deleteById(id: UUID): Either<ClientException, Boolean> {
|
|
272
|
+
val deleted = transaction(db.primary) {
|
|
273
|
+
{domain}Repository.deleteById(id)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return if (deleted != null) {
|
|
277
|
+
true.right()
|
|
278
|
+
} else {
|
|
279
|
+
ClientError.{DOMAIN}_NOT_FOUND.asException().left()
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Key Points**:
|
|
286
|
+
- Use `{Domain}Response.from(entity)` for conversions (companion object pattern)
|
|
287
|
+
- `transaction(db.primary)` for writes
|
|
288
|
+
- Return `Either.left()` for errors, `Either.right()` for success
|
|
289
|
+
- NO `!!` operators
|
|
290
|
+
|
|
291
|
+
### 5. Register Factory Bean
|
|
292
|
+
|
|
293
|
+
Location: `app/module-{domain}/module-impl/src/main/kotlin/com/yourcompany/runtime/factory/{Domain}ManagerFactory.kt`
|
|
294
|
+
|
|
295
|
+
```kotlin
|
|
296
|
+
package com.yourcompany.runtime.factory
|
|
297
|
+
|
|
298
|
+
import com.yourcompany.database.DatabaseContext
|
|
299
|
+
import com.yourcompany.{domain}.impl.Default{Domain}Manager
|
|
300
|
+
import com.yourcompany.{domain}.contract.{Domain}Manager
|
|
301
|
+
import com.yourcompany.postgresql.repository.{Domain}Repository
|
|
302
|
+
import io.micronaut.context.annotation.Factory
|
|
303
|
+
import jakarta.inject.Singleton
|
|
304
|
+
|
|
305
|
+
@Factory
|
|
306
|
+
class {Domain}ManagerFactory {
|
|
307
|
+
|
|
308
|
+
@Singleton
|
|
309
|
+
fun provide{Domain}Manager(
|
|
310
|
+
{domain}Repository: {Domain}Repository,
|
|
311
|
+
db: DatabaseContext
|
|
312
|
+
): {Domain}Manager {
|
|
313
|
+
return Default{Domain}Manager({domain}Repository, db)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### 6. Create Retrofit Client
|
|
319
|
+
|
|
320
|
+
Location: `app/module-client/src/main/kotlin/com/yourcompany/client/{Domain}Client.kt`
|
|
321
|
+
|
|
322
|
+
```kotlin
|
|
323
|
+
package com.yourcompany.client
|
|
324
|
+
|
|
325
|
+
import com.yourcompany.client.request.{domain}.Create{Domain}Request
|
|
326
|
+
import com.yourcompany.client.request.{domain}.Update{Domain}Request
|
|
327
|
+
import com.yourcompany.client.response.{domain}.{Domain}Response
|
|
328
|
+
import com.yourcompany.client.response.{domain}.{Domain}ListResponse
|
|
329
|
+
import retrofit2.http.*
|
|
330
|
+
import java.util.UUID
|
|
331
|
+
|
|
332
|
+
interface {Domain}Client {
|
|
333
|
+
|
|
334
|
+
@GET("/api/v1/{domain}s")
|
|
335
|
+
suspend fun list(
|
|
336
|
+
@Header("Authorization") authorization: String,
|
|
337
|
+
@Query("page") page: Int? = null,
|
|
338
|
+
@Query("limit") limit: Int? = null,
|
|
339
|
+
@Query("status") status: String? = null
|
|
340
|
+
): {Domain}ListResponse
|
|
341
|
+
|
|
342
|
+
@GET("/api/v1/{domain}")
|
|
343
|
+
suspend fun getById(
|
|
344
|
+
@Header("Authorization") authorization: String,
|
|
345
|
+
@Query("id") id: UUID
|
|
346
|
+
): {Domain}Response
|
|
347
|
+
|
|
348
|
+
@POST("/api/v1/{domain}")
|
|
349
|
+
suspend fun create(
|
|
350
|
+
@Header("Authorization") authorization: String,
|
|
351
|
+
@Body request: Create{Domain}Request
|
|
352
|
+
): {Domain}Response
|
|
353
|
+
|
|
354
|
+
@POST("/api/v1/{domain}/update")
|
|
355
|
+
suspend fun update(
|
|
356
|
+
@Header("Authorization") authorization: String,
|
|
357
|
+
@Query("id") id: UUID,
|
|
358
|
+
@Body request: Update{Domain}Request
|
|
359
|
+
): {Domain}Response
|
|
360
|
+
|
|
361
|
+
@POST("/api/v1/{domain}/delete")
|
|
362
|
+
suspend fun delete(
|
|
363
|
+
@Header("Authorization") authorization: String,
|
|
364
|
+
@Query("id") id: UUID
|
|
365
|
+
): Boolean
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### 7. Create Integration Test
|
|
370
|
+
|
|
371
|
+
Location: `app/api-application/src/test/kotlin/com/yourcompany/{Domain}ControllerTest.kt`
|
|
372
|
+
|
|
373
|
+
> See `testing-patterns.md` for complete test examples.
|
|
374
|
+
|
|
375
|
+
### 8. Run Tests
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
./gradlew :app:api-application:test --tests "{Domain}ControllerTest"
|
|
379
|
+
./gradlew test
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Interaction Style
|
|
383
|
+
|
|
384
|
+
- Always generates the full stack: models, controller, manager, factory, client, tests
|
|
385
|
+
- Follows the project's exact patterns — no shortcuts or creative alternatives
|
|
386
|
+
- Uses query parameters for all IDs, never path parameters
|
|
387
|
+
- Asks which domain entity before starting if not clear from context
|
|
388
|
+
|
|
389
|
+
## Rules
|
|
390
|
+
|
|
391
|
+
### RPC-Style API (POST for Mutations, Query Params Only)
|
|
392
|
+
|
|
393
|
+
This project uses **RPC-style endpoints: @Get for reads, @Post for all mutations, query parameters for all IDs**:
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
GET /api/v1/employees # List employees (plural)
|
|
397
|
+
GET /api/v1/employee # Get one employee (?id=xxx)
|
|
398
|
+
POST /api/v1/employee # Create employee
|
|
399
|
+
POST /api/v1/employee/update # Update employee
|
|
400
|
+
POST /api/v1/employee/delete # Delete employee (soft)
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Rules from API_RULES.md:**
|
|
404
|
+
- NEVER use path parameters (`/{id}`)
|
|
405
|
+
- Use query parameters: `@QueryValue id: UUID`
|
|
406
|
+
- Singular nouns for single resource, plural for collections
|
|
407
|
+
- Use verb sub-paths for actions (`/delete`, `/restore`)
|
|
408
|
+
|
|
409
|
+
### Layered Architecture
|
|
410
|
+
|
|
411
|
+
```
|
|
412
|
+
HTTP Request → Controller → Manager → Repository → Database
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
**Controller**: Thin (just delegation), HTTP annotations, @ExecuteOn(TaskExecutors.IO), @Secured, unwrap Either with `.throwOrValue()`
|
|
416
|
+
|
|
417
|
+
**Manager**: All business logic, returns `Either<ClientException, T>`, wraps DB operations in transactions, never throws exceptions
|
|
418
|
+
|
|
419
|
+
**Repository**: Data access only (already exists)
|
|
420
|
+
|
|
421
|
+
### NO !! Operator
|
|
422
|
+
|
|
423
|
+
```kotlin
|
|
424
|
+
val employee = employeeRepository.byId(id)
|
|
425
|
+
?: return ClientError.EMPLOYEE_NOT_FOUND.asException().left()
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Error Handling Patterns
|
|
429
|
+
|
|
430
|
+
#### Not Found
|
|
431
|
+
```kotlin
|
|
432
|
+
val entity = repository.byId(id)
|
|
433
|
+
?: return ClientError.{DOMAIN}_NOT_FOUND.asException().left()
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
#### Already Exists
|
|
437
|
+
```kotlin
|
|
438
|
+
val existing = repository.byEmail(email)
|
|
439
|
+
if (existing != null) {
|
|
440
|
+
return ClientError.EMAIL_ALREADY_IN_USE.asException().left()
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
## Output
|
|
445
|
+
|
|
446
|
+
- Controller is thin (just delegation)
|
|
447
|
+
- Controller has `@ExecuteOn(TaskExecutors.IO)`
|
|
448
|
+
- NO path parameters (use `@QueryValue` for all IDs)
|
|
449
|
+
- Manager returns Either (never throws)
|
|
450
|
+
- Transactions wrap DB operations
|
|
451
|
+
- No `!!` operator
|
|
452
|
+
- Response models in module-client with `companion object { fun from() }`
|
|
453
|
+
- NO inline data classes in controllers
|
|
454
|
+
- Integration tests use Retrofit client
|
|
455
|
+
- All tests pass
|