@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,317 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spartan:next-app
|
|
3
|
+
description: Scaffold a complete new Next.js application from scratch — App Router, TypeScript, Tailwind, testing setup, API client layer, auth scaffolding, and CI config.
|
|
4
|
+
argument-hint: "[app name] [brief description]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# New Next.js App: {{ args[0] }}
|
|
8
|
+
Description: {{ args[1] }}
|
|
9
|
+
|
|
10
|
+
Scaffolding a **production-ready** Next.js application. This is more complete than
|
|
11
|
+
`/spartan:next-feature` — use this for a brand-new frontend project.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Step 1: Clarify before building
|
|
16
|
+
|
|
17
|
+
Ask:
|
|
18
|
+
1. **Auth**: None / NextAuth.js / custom JWT / Clerk / Auth0?
|
|
19
|
+
2. **State management**: Server-only (preferred) / Zustand / React Query for client state?
|
|
20
|
+
3. **Connects to which BE service(s)?** (Kotlin service names/URLs)
|
|
21
|
+
4. **Deploy target**: Railway / Vercel / AWS / Docker+K8s?
|
|
22
|
+
5. **Database** (if any — for full-stack Next.js with Prisma): Yes / No, using separate BE
|
|
23
|
+
|
|
24
|
+
**Auto mode on?** → Use defaults: No auth, Server-only state, Railway deploy, no DB (separate BE). Proceed immediately.
|
|
25
|
+
**Auto mode off?** → Wait for answers.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Step 2: Bootstrap with create-next-app
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx create-next-app@latest {{ args[0] }} \
|
|
33
|
+
--typescript \
|
|
34
|
+
--tailwind \
|
|
35
|
+
--app \
|
|
36
|
+
--src-dir \
|
|
37
|
+
--import-alias "@/*" \
|
|
38
|
+
--no-eslint # we configure this ourselves
|
|
39
|
+
|
|
40
|
+
cd {{ args[0] }}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Step 3: Install core dependencies
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Testing
|
|
49
|
+
npm install -D vitest @vitejs/plugin-react jsdom
|
|
50
|
+
npm install -D @testing-library/react @testing-library/user-event @testing-library/jest-dom
|
|
51
|
+
|
|
52
|
+
# API + validation
|
|
53
|
+
npm install zod
|
|
54
|
+
|
|
55
|
+
# Auth (if NextAuth chosen)
|
|
56
|
+
npm install next-auth@beta # v5 for App Router
|
|
57
|
+
|
|
58
|
+
# Dev tooling
|
|
59
|
+
npm install -D eslint-config-next @typescript-eslint/eslint-plugin prettier
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Step 4: Configure project structure
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
src/
|
|
68
|
+
├── app/ # App Router
|
|
69
|
+
│ ├── (auth)/ # Route group — auth pages
|
|
70
|
+
│ │ ├── login/page.tsx
|
|
71
|
+
│ │ └── layout.tsx
|
|
72
|
+
│ ├── (dashboard)/ # Route group — protected pages
|
|
73
|
+
│ │ ├── layout.tsx # ← auth check here
|
|
74
|
+
│ │ └── [feature]/
|
|
75
|
+
│ ├── api/ # API routes (only when server actions aren't enough)
|
|
76
|
+
│ ├── globals.css
|
|
77
|
+
│ └── layout.tsx # Root layout
|
|
78
|
+
├── components/
|
|
79
|
+
│ ├── ui/ # Shared primitives (Button, Input, Card...)
|
|
80
|
+
│ └── [feature]/ # Feature-specific components
|
|
81
|
+
├── lib/
|
|
82
|
+
│ ├── api/ # BE API client functions
|
|
83
|
+
│ │ ├── client.ts # Base fetch wrapper
|
|
84
|
+
│ │ └── [service].api.ts # Per-service functions
|
|
85
|
+
│ ├── auth/ # Auth utilities
|
|
86
|
+
│ └── utils.ts # cn(), formatDate(), etc.
|
|
87
|
+
├── hooks/ # Custom React hooks (client-side)
|
|
88
|
+
├── types/ # Shared TypeScript types
|
|
89
|
+
│ └── api.types.ts # Mirror Kotlin DTOs here
|
|
90
|
+
└── middleware.ts # Auth + route protection
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Step 5: Create base API client
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// src/lib/api/client.ts
|
|
99
|
+
const BASE_URL = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:8080'
|
|
100
|
+
|
|
101
|
+
type ApiError = {
|
|
102
|
+
message: string
|
|
103
|
+
code: string
|
|
104
|
+
status: number
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export class ApiClient {
|
|
108
|
+
private static async request<T>(
|
|
109
|
+
path: string,
|
|
110
|
+
options?: RequestInit & { tags?: string[] }
|
|
111
|
+
): Promise<T> {
|
|
112
|
+
const res = await fetch(`${BASE_URL}${path}`, {
|
|
113
|
+
...options,
|
|
114
|
+
headers: {
|
|
115
|
+
'Content-Type': 'application/json',
|
|
116
|
+
...options?.headers,
|
|
117
|
+
},
|
|
118
|
+
next: options?.tags ? { tags: options.tags } : undefined,
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
if (!res.ok) {
|
|
122
|
+
const error: ApiError = await res.json().catch(() => ({
|
|
123
|
+
message: 'Unknown error',
|
|
124
|
+
code: 'UNKNOWN',
|
|
125
|
+
status: res.status,
|
|
126
|
+
}))
|
|
127
|
+
throw new Error(error.message)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return res.json()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
static get<T>(path: string, tags?: string[]) {
|
|
134
|
+
return this.request<T>(path, { tags })
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static post<T>(path: string, body: unknown) {
|
|
138
|
+
return this.request<T>(path, {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
body: JSON.stringify(body),
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
static put<T>(path: string, body: unknown) {
|
|
145
|
+
return this.request<T>(path, {
|
|
146
|
+
method: 'PUT',
|
|
147
|
+
body: JSON.stringify(body),
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
static delete<T>(path: string) {
|
|
152
|
+
return this.request<T>(path, { method: 'DELETE' })
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Step 6: Configure Vitest
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// vitest.config.ts
|
|
163
|
+
import { defineConfig } from 'vitest/config'
|
|
164
|
+
import react from '@vitejs/plugin-react'
|
|
165
|
+
import path from 'path'
|
|
166
|
+
|
|
167
|
+
export default defineConfig({
|
|
168
|
+
plugins: [react()],
|
|
169
|
+
test: {
|
|
170
|
+
environment: 'jsdom',
|
|
171
|
+
globals: true,
|
|
172
|
+
setupFiles: ['./src/test/setup.ts'],
|
|
173
|
+
},
|
|
174
|
+
resolve: {
|
|
175
|
+
alias: {
|
|
176
|
+
'@': path.resolve(__dirname, './src'),
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
})
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
// src/test/setup.ts
|
|
184
|
+
import '@testing-library/jest-dom'
|
|
185
|
+
import { vi } from 'vitest'
|
|
186
|
+
|
|
187
|
+
// Mock Next.js navigation
|
|
188
|
+
vi.mock('next/navigation', () => ({
|
|
189
|
+
useRouter: () => ({ push: vi.fn(), replace: vi.fn(), back: vi.fn() }),
|
|
190
|
+
usePathname: () => '/',
|
|
191
|
+
useSearchParams: () => new URLSearchParams(),
|
|
192
|
+
}))
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Add to `package.json`:
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"scripts": {
|
|
199
|
+
"test": "vitest",
|
|
200
|
+
"test:run": "vitest run",
|
|
201
|
+
"test:coverage": "vitest run --coverage"
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Step 7: Environment variables
|
|
209
|
+
|
|
210
|
+
Create `.env.local`:
|
|
211
|
+
```bash
|
|
212
|
+
NEXT_PUBLIC_API_URL=http://localhost:8080
|
|
213
|
+
# NEXTAUTH_URL=http://localhost:3000
|
|
214
|
+
# NEXTAUTH_SECRET= # generate: openssl rand -base64 32
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Create `.env.example` (commit this):
|
|
218
|
+
```bash
|
|
219
|
+
NEXT_PUBLIC_API_URL=http://localhost:8080
|
|
220
|
+
# NEXTAUTH_URL=https://your-domain.com
|
|
221
|
+
# NEXTAUTH_SECRET=your-secret-here
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Step 8: Deploy configuration
|
|
227
|
+
|
|
228
|
+
**Railway** (`railway.toml`):
|
|
229
|
+
```toml
|
|
230
|
+
[build]
|
|
231
|
+
builder = "nixpacks"
|
|
232
|
+
|
|
233
|
+
[deploy]
|
|
234
|
+
startCommand = "npm start"
|
|
235
|
+
healthcheckPath = "/api/health"
|
|
236
|
+
healthcheckTimeout = 30
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Create `src/app/api/health/route.ts`:
|
|
240
|
+
```typescript
|
|
241
|
+
export function GET() {
|
|
242
|
+
return Response.json({ status: 'ok', timestamp: new Date().toISOString() })
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**Docker** (`Dockerfile`):
|
|
247
|
+
```dockerfile
|
|
248
|
+
FROM node:20-alpine AS deps
|
|
249
|
+
WORKDIR /app
|
|
250
|
+
COPY package*.json ./
|
|
251
|
+
RUN npm ci
|
|
252
|
+
|
|
253
|
+
FROM node:20-alpine AS builder
|
|
254
|
+
WORKDIR /app
|
|
255
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
256
|
+
COPY . .
|
|
257
|
+
RUN npm run build
|
|
258
|
+
|
|
259
|
+
FROM node:20-alpine AS runner
|
|
260
|
+
WORKDIR /app
|
|
261
|
+
ENV NODE_ENV=production
|
|
262
|
+
COPY --from=builder /app/.next/standalone ./
|
|
263
|
+
COPY --from=builder /app/.next/static ./.next/static
|
|
264
|
+
COPY --from=builder /app/public ./public
|
|
265
|
+
EXPOSE 3000
|
|
266
|
+
CMD ["node", "server.js"]
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Add to `next.config.ts`: `output: 'standalone'`
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Step 9: First test — smoke test
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
// src/app/page.test.tsx
|
|
277
|
+
import { render, screen } from '@testing-library/react'
|
|
278
|
+
import { describe, it, expect } from 'vitest'
|
|
279
|
+
import Home from './page'
|
|
280
|
+
|
|
281
|
+
describe('Home page', () => {
|
|
282
|
+
it('renders without crashing', () => {
|
|
283
|
+
render(<Home />)
|
|
284
|
+
expect(document.body).toBeDefined()
|
|
285
|
+
})
|
|
286
|
+
})
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Run: `npm test` — must be green before moving on.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Step 10: GitHub Actions CI
|
|
294
|
+
|
|
295
|
+
```yaml
|
|
296
|
+
# .github/workflows/ci.yml
|
|
297
|
+
name: CI
|
|
298
|
+
|
|
299
|
+
on: [push, pull_request]
|
|
300
|
+
|
|
301
|
+
jobs:
|
|
302
|
+
test:
|
|
303
|
+
runs-on: ubuntu-latest
|
|
304
|
+
steps:
|
|
305
|
+
- uses: actions/checkout@v4
|
|
306
|
+
- uses: actions/setup-node@v4
|
|
307
|
+
with:
|
|
308
|
+
node-version: '20'
|
|
309
|
+
cache: 'npm'
|
|
310
|
+
- run: npm ci
|
|
311
|
+
- run: npm run test:run
|
|
312
|
+
- run: npm run build
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
After scaffolding complete:
|
|
316
|
+
"✅ App scaffolded. Run `npm run dev` to start. First test is green.
|
|
317
|
+
Use `/spartan:next-feature [name]` to build individual features."
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spartan:next-feature
|
|
3
|
+
description: Scaffold a new Next.js feature following App Router conventions — pages, components, server actions, types, and tests.
|
|
4
|
+
argument-hint: "[feature name] [brief description]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Next.js Feature: {{ args[0] }}
|
|
8
|
+
Description: {{ args[1] }}
|
|
9
|
+
|
|
10
|
+
You are scaffolding a new Next.js feature following **App Router** conventions for the Spartan frontend.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Step 1: Clarify before building
|
|
15
|
+
|
|
16
|
+
Ask the user:
|
|
17
|
+
1. Is this a **page** (new route), a **component** (reusable UI), or a **feature** (page + components + logic)?
|
|
18
|
+
2. Does it need **data fetching** from the Kotlin BE? If yes, which API endpoints?
|
|
19
|
+
3. Is any part **interactive** (needs `'use client'`)? What interactions?
|
|
20
|
+
4. Any **auth/access control** requirements?
|
|
21
|
+
|
|
22
|
+
**Auto mode on?** → Infer answers from the feature name and codebase context. Assume "feature" type, check existing API patterns, proceed immediately.
|
|
23
|
+
**Auto mode off?** → Wait for answers, then proceed.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Step 1.5: Check for design tokens (silent)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
ls .planning/design/system/tokens.md .planning/design-config.md 2>/dev/null
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
If design tokens exist, read them. All scaffolded components MUST use project tokens:
|
|
34
|
+
- Use project colors in default styles, not Tailwind defaults
|
|
35
|
+
- Use project font in any typography
|
|
36
|
+
- Import from the project's design token file if it exists (e.g., `@/lib/design-tokens`)
|
|
37
|
+
|
|
38
|
+
If NO tokens exist, scaffold with clean, unstyled components. Use `bg-neutral-*` placeholder styles that are obviously temporary. Don't use `bg-blue-500` or other Tailwind color defaults.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Step 2: Map the directory structure
|
|
43
|
+
|
|
44
|
+
Based on answers, propose this structure under `app/` or `components/`:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
# For a new PAGE feature:
|
|
48
|
+
app/[feature-name]/
|
|
49
|
+
page.tsx ← Server Component (data fetching here)
|
|
50
|
+
page.test.tsx ← Tests
|
|
51
|
+
layout.tsx ← Only if needs own layout
|
|
52
|
+
loading.tsx ← Loading UI (Suspense boundary)
|
|
53
|
+
error.tsx ← Error boundary
|
|
54
|
+
_components/ ← Feature-local components
|
|
55
|
+
FeatureCard.tsx
|
|
56
|
+
FeatureCard.test.tsx
|
|
57
|
+
_actions/ ← Server Actions for mutations
|
|
58
|
+
createFeature.ts
|
|
59
|
+
updateFeature.ts
|
|
60
|
+
_types/ ← TypeScript types
|
|
61
|
+
feature.types.ts
|
|
62
|
+
|
|
63
|
+
# For a reusable COMPONENT:
|
|
64
|
+
components/[ComponentName]/
|
|
65
|
+
[ComponentName].tsx
|
|
66
|
+
[ComponentName].test.tsx
|
|
67
|
+
[ComponentName].stories.tsx ← If using Storybook
|
|
68
|
+
index.ts ← Re-export
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Step 3: Create types first
|
|
74
|
+
|
|
75
|
+
In `_types/feature.types.ts`:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// Mirror the Kotlin DTO structure from BE
|
|
79
|
+
export interface FeatureDto {
|
|
80
|
+
id: string
|
|
81
|
+
// ... fields from BE response
|
|
82
|
+
createdAt: string // ISO string from BE
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// For API responses — always typed
|
|
86
|
+
export interface ApiResponse<T> {
|
|
87
|
+
data: T
|
|
88
|
+
message?: string
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// For form/mutation input
|
|
92
|
+
export interface CreateFeatureInput {
|
|
93
|
+
// ...
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Step 4: Create API client
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// lib/api/feature.api.ts
|
|
103
|
+
|
|
104
|
+
import { FeatureDto, CreateFeatureInput } from '@/app/[feature]/_types/feature.types'
|
|
105
|
+
|
|
106
|
+
export async function getFeature(id: string): Promise<FeatureDto> {
|
|
107
|
+
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/features/${id}`, {
|
|
108
|
+
next: { revalidate: 60 }, // ISR: revalidate every 60s
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
if (!res.ok) {
|
|
112
|
+
throw new Error(`Failed to fetch feature: ${res.status}`)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return res.json()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function createFeature(input: CreateFeatureInput): Promise<FeatureDto> {
|
|
119
|
+
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/features`, {
|
|
120
|
+
method: 'POST',
|
|
121
|
+
headers: { 'Content-Type': 'application/json' },
|
|
122
|
+
body: JSON.stringify(input),
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
if (!res.ok) {
|
|
126
|
+
const error = await res.json()
|
|
127
|
+
throw new Error(error.message || 'Failed to create feature')
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return res.json()
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Step 5: Create Server Component page
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// app/[feature-name]/page.tsx
|
|
140
|
+
import { getFeature } from '@/lib/api/feature.api'
|
|
141
|
+
import { FeatureCard } from './_components/FeatureCard'
|
|
142
|
+
|
|
143
|
+
// Metadata for SEO
|
|
144
|
+
export const metadata = {
|
|
145
|
+
title: '[Feature Name] | Spartan',
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
interface PageProps {
|
|
149
|
+
params: { id: string }
|
|
150
|
+
searchParams: { [key: string]: string | undefined }
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export default async function FeaturePage({ params }: PageProps) {
|
|
154
|
+
// Data fetching directly in Server Component
|
|
155
|
+
const feature = await getFeature(params.id)
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<main>
|
|
159
|
+
<FeatureCard feature={feature} />
|
|
160
|
+
</main>
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Step 6: Write tests FIRST (TDD)
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// page.test.tsx — Server Component test
|
|
171
|
+
import { render, screen } from '@testing-library/react'
|
|
172
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
173
|
+
import FeaturePage from './page'
|
|
174
|
+
|
|
175
|
+
// Mock the API call
|
|
176
|
+
vi.mock('@/lib/api/feature.api', () => ({
|
|
177
|
+
getFeature: vi.fn()
|
|
178
|
+
}))
|
|
179
|
+
|
|
180
|
+
describe('FeaturePage', () => {
|
|
181
|
+
it('renders feature data when loaded', async () => {
|
|
182
|
+
// given
|
|
183
|
+
const mockFeature = { id: '1', name: 'Test Feature' }
|
|
184
|
+
vi.mocked(getFeature).mockResolvedValue(mockFeature)
|
|
185
|
+
|
|
186
|
+
// when
|
|
187
|
+
const page = await FeaturePage({ params: { id: '1' }, searchParams: {} })
|
|
188
|
+
render(page)
|
|
189
|
+
|
|
190
|
+
// then
|
|
191
|
+
expect(screen.getByText('Test Feature')).toBeInTheDocument()
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Step 7: Environment variables check
|
|
199
|
+
|
|
200
|
+
Ensure these are set in `.env.local` (and in Railway env vars):
|
|
201
|
+
```
|
|
202
|
+
NEXT_PUBLIC_API_URL=http://localhost:8080 # local
|
|
203
|
+
# Railway: set to production BE service URL
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
After scaffolding, run:
|
|
207
|
+
```bash
|
|
208
|
+
npm run dev
|
|
209
|
+
npm test -- --watch [feature-name]
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Say: "Feature scaffolded. First failing test written. Say **'go'** to implement."
|