@polymorphism-tech/morph-spec 7.8.1 → 7.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/README.md +2 -2
  2. package/bin/morph-spec.js +30 -1
  3. package/bin/validate.js +379 -379
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +1 -1
  6. package/docs/QUICKSTART.md +1 -1
  7. package/framework/CLAUDE.md +3 -1
  8. package/framework/MORPH.md +4 -3
  9. package/framework/commands/commit.md +166 -166
  10. package/framework/commands/morph-infra.md +215 -215
  11. package/framework/commands/morph-preflight.md +205 -205
  12. package/framework/commands/morph-troubleshoot.md +124 -124
  13. package/framework/evals/trace-schema.md +22 -12
  14. package/framework/hooks/README.md +2 -0
  15. package/framework/hooks/claude-code/core/subagent-report-guard.js +95 -0
  16. package/framework/hooks/claude-code/core/trace-autogen.js +183 -0
  17. package/framework/hooks/claude-code/statusline.py +679 -679
  18. package/framework/hooks/claude-code/statusline.sh +11 -11
  19. package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
  20. package/framework/hooks/git/pre-commit/agents.sh +25 -25
  21. package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
  22. package/framework/hooks/git/pre-commit/specs.sh +50 -50
  23. package/framework/hooks/git/pre-push/run-tests.sh +44 -44
  24. package/framework/hooks/shared/activity-logger.js +160 -160
  25. package/framework/hooks/shared/hook-response.js +45 -45
  26. package/framework/hooks/shared/payload-utils.js +78 -78
  27. package/framework/hooks/shared/stdin-reader.js +26 -26
  28. package/framework/hooks/shared/tasks-validator.js +47 -4
  29. package/framework/rubrics/backend.json +41 -41
  30. package/framework/rules/ai-agents-standards.md +45 -45
  31. package/framework/rules/dotnet-templates.md +86 -86
  32. package/framework/rules/frontend-standards.md +13 -13
  33. package/framework/rules/infrastructure-standards.md +27 -27
  34. package/framework/rules/neon-standards.md +25 -25
  35. package/framework/rules/nextjs-standards.md +20 -20
  36. package/framework/rules/nextjs-templates.md +40 -40
  37. package/framework/schemas/feature.schema.json +128 -0
  38. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  39. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  40. package/framework/skills/core/morph-apply/SKILL.md +23 -10
  41. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  42. package/framework/skills/core/morph-eval/SKILL.md +3 -6
  43. package/framework/skills/core/morph-implement/SKILL.md +28 -15
  44. package/framework/skills/core/morph-plan/SKILL.md +1 -1
  45. package/framework/skills/core/morph-proposal/SKILL.md +2 -2
  46. package/framework/skills/core/morph-review/SKILL.md +2 -6
  47. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  48. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  49. package/framework/standards/ai-agents/agent-session.md +160 -160
  50. package/framework/standards/ai-agents/batching.md +193 -193
  51. package/framework/standards/ai-agents/context-providers.md +188 -188
  52. package/framework/standards/ai-agents/mcp-server.md +250 -250
  53. package/framework/standards/ai-agents/mcp-tools.md +197 -197
  54. package/framework/standards/ai-agents/middleware-patterns.md +207 -207
  55. package/framework/standards/ai-agents/modalities-image-gen.md +173 -173
  56. package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -171
  57. package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -181
  58. package/framework/standards/ai-agents/modalities-vision.md +201 -201
  59. package/framework/standards/ai-agents/multi-agent-patterns.md +155 -155
  60. package/framework/standards/ai-agents/production.md +275 -275
  61. package/framework/standards/ai-agents/prompts-in-db.md +214 -214
  62. package/framework/standards/ai-agents/providers/model-registry.md +158 -158
  63. package/framework/standards/ai-agents/service-tier-flex.md +135 -135
  64. package/framework/standards/ai-agents/structured-output.md +249 -249
  65. package/framework/standards/ai-agents/sweet-spot.md +109 -109
  66. package/framework/templates/.idea/morph-templates.xml +92 -0
  67. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  68. package/framework/templates/IDE-SNIPPETS.md +262 -262
  69. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  70. package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
  71. package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
  72. package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
  73. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
  74. package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
  75. package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
  76. package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
  77. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
  78. package/framework/templates/code/dotnet/database/migration.cs +83 -83
  79. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
  80. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  81. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  82. package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
  83. package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
  84. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  85. package/framework/templates/code/typescript/contracts.ts +167 -167
  86. package/framework/templates/docs/clarifications.md +253 -253
  87. package/framework/templates/docs/schema-analysis.md +119 -119
  88. package/framework/templates/docs/ui-components.md +52 -52
  89. package/framework/templates/docs/ui-design-system.md +76 -76
  90. package/framework/templates/docs/ui-flows.md +167 -167
  91. package/framework/templates/docs/ui-mockups.md +98 -98
  92. package/framework/templates/feature/decisions.md +122 -122
  93. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
  94. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  95. package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
  96. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  97. package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
  98. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  99. package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
  100. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  101. package/framework/templates/infrastructure/docker/docker-compose.template.yml +50 -50
  102. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  103. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  104. package/framework/templates/infrastructure/github/README.md +56 -56
  105. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  106. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  107. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  108. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  109. package/framework/templates/integrations/asaas-client.cs +387 -387
  110. package/framework/templates/integrations/asaas-webhook.cs +351 -351
  111. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  112. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  113. package/framework/templates/meta-prompts/parallel-workers/parallel-coordinator.md +113 -113
  114. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
  115. package/framework/templates/project/validate.js +120 -120
  116. package/framework/templates/state.template.json +17 -17
  117. package/framework/templates/ui/design-system.css +226 -226
  118. package/package.json +1 -1
  119. package/src/commands/dashboard/dashboard.js +53 -53
  120. package/src/commands/index.js +13 -13
  121. package/src/commands/mcp/mcp-setup.js +519 -519
  122. package/src/commands/project/checkpoint.js +209 -209
  123. package/src/commands/project/doctor.js +23 -18
  124. package/src/commands/project/update.js +16 -9
  125. package/src/commands/state/create.js +58 -0
  126. package/src/commands/state/gate-decision.js +99 -0
  127. package/src/core/state/phase-state-machine.js +197 -197
  128. package/src/core/state/state-manager.js +55 -0
  129. package/src/core/templates/template-renderer.js +477 -477
  130. package/src/dashboard/app/components/DetailPanel.jsx +11 -11
  131. package/src/dashboard/app/components/FlowGraph.jsx +17 -17
  132. package/src/dashboard/app/components/SearchFilter.jsx +22 -22
  133. package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
  134. package/src/dashboard/app/main.jsx +13 -13
  135. package/src/dashboard/app/pages/AgentMap.jsx +370 -370
  136. package/src/dashboard/app/pages/HookInspector.jsx +418 -418
  137. package/src/dashboard/app/pages/LiveState.jsx +239 -239
  138. package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
  139. package/src/dashboard/app/pages/RulesMap.jsx +273 -273
  140. package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
  141. package/src/dashboard/app/pages/Standards.jsx +332 -332
  142. package/src/dashboard/app/styles/index.css +47 -47
  143. package/src/dashboard/index.html +12 -12
  144. package/src/dashboard/package.json +24 -24
  145. package/src/dashboard/scanner/hooks-scanner.js +41 -41
  146. package/src/dashboard/scanner/phases-scanner.js +15 -15
  147. package/src/dashboard/scanner/rubrics-scanner.js +12 -12
  148. package/src/dashboard/scanner/rules-scanner.js +37 -37
  149. package/src/dashboard/scanner/skills-scanner.js +43 -43
  150. package/src/dashboard/scanner/squads-scanner.js +13 -13
  151. package/src/dashboard/scanner/standards-scanner.js +26 -26
  152. package/src/dashboard/vite-plugin-morph-data.js +17 -17
  153. package/src/dashboard/vite.config.js +24 -24
  154. package/src/lib/agents/metrics-aggregator.js +56 -56
  155. package/src/lib/agents/metrics-calculator.js +125 -125
  156. package/src/lib/agents/output-collector.js +73 -73
  157. package/src/lib/detectors/claude-config-detector.js +152 -152
  158. package/src/lib/detectors/design-system-detector.js +231 -231
  159. package/src/lib/detectors/index.js +217 -217
  160. package/src/lib/generators/architecture-scanner.js +639 -639
  161. package/src/lib/generators/design-system-generator.js +508 -508
  162. package/src/lib/generators/index.js +13 -13
  163. package/src/lib/generators/settings-generator.js +209 -209
  164. package/src/lib/hooks/hook-executor.js +167 -167
  165. package/src/lib/installers/mcp-installer.js +719 -719
  166. package/src/lib/tasks/task-classifier.js +30 -30
  167. package/src/lib/tasks/task-parser.js +209 -209
  168. package/src/lib/tasks/test-runner.js +81 -81
  169. package/src/lib/validators/architecture/architecture-validator.js +259 -259
  170. package/src/lib/validators/checkpoint-persistence.js +39 -39
  171. package/src/lib/validators/contracts/contract-compliance-validator.js +274 -274
  172. package/src/lib/validators/css/css-validator.js +340 -340
  173. package/src/lib/validators/packages/package-validator.js +347 -347
  174. package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
  175. package/src/lib/validators/shared/issue-counter.js +14 -14
  176. package/src/lib/validators/validation-runner.js +349 -349
  177. package/src/scripts/setup-infra.js +385 -383
  178. package/src/utils/agent-id-mapper.js +11 -11
  179. package/src/utils/agents-installer.js +25 -10
  180. package/src/utils/banner.js +37 -4
  181. package/src/utils/claude-settings-manager.js +300 -300
  182. package/src/utils/hooks-installer.js +32 -4
  183. package/src/utils/skills-installer.js +174 -172
@@ -1,122 +1,122 @@
1
- # Architecture Decision Records - {{titleCase FEATURE_NAME}}
2
-
3
- ## ADR-001: {Decision Title}
4
-
5
- **Date:** {{DATE}}
6
- **Status:** Proposed | Accepted | Deprecated | Superseded
7
- **Deciders:** MORPH Agents, Developer
8
-
9
- ### Context
10
-
11
- {Describe the context and problem that led to this decision}
12
-
13
- ### Decision
14
-
15
- {Describe the decision made}
16
-
17
- ### Consequences
18
-
19
- **Pros:**
20
- - {Positive consequence 1}
21
- - {Positive consequence 2}
22
-
23
- **Cons:**
24
- - {Negative consequence 1}
25
-
26
- **Risks:**
27
- - {Identified risk}
28
-
29
- ### Alternatives Considered
30
-
31
- #### Option A: {Name}
32
- - Pros: {advantages}
33
- - Cons: {disadvantages}
34
- - **Rejected because:** {reason}
35
-
36
- #### Option B: {Name}
37
- - Pros: {advantages}
38
- - Cons: {disadvantages}
39
- - **Rejected because:** {reason}
40
-
41
- ---
42
-
43
- {{#if (eq STACK "nextjs-neon")}}
44
- ## ADR-002: Database Access Strategy
45
-
46
- **Date:** {{DATE}}
47
- **Status:** Accepted
48
-
49
- ### Context
50
-
51
- Need to decide how the .NET API accesses Neon PostgreSQL.
52
-
53
- ### Decision
54
-
55
- Use Dapper with Npgsql for direct SQL queries. No EF Core.
56
-
57
- ### Consequences
58
-
59
- **Pros:**
60
- - Full control over SQL queries
61
- - Better performance (no ORM overhead)
62
- - Direct access to PostgreSQL features (jsonb, pgvector, functions)
63
- - RLS policies apply naturally through connection
64
-
65
- **Cons:**
66
- - No automatic migrations (use Neon CLI / dotnet ef instead)
67
- - Manual SQL query writing
68
- - No change tracking
69
-
70
- ---
71
-
72
- ## ADR-003: Auth Strategy
73
-
74
- **Date:** {{DATE}}
75
- **Status:** Accepted
76
-
77
- ### Context
78
-
79
- Need authentication for both Next.js frontend and .NET API.
80
-
81
- ### Decision
82
-
83
- Use Neon Auth (Better Auth) for authentication. Auth data lives in the Neon database. JWT validated by .NET API middleware.
84
-
85
- ### Consequences
86
-
87
- **Pros:**
88
- - Auth data in same database — no external provider, no sync delays
89
- - RLS policies work with Neon Auth JWT via auth.user_id() / pg_session_jwt
90
- - Built-in social login, email/password via @neondatabase/auth SDK
91
- - Branch isolation — each Neon branch gets isolated auth data
92
- - No webhooks needed — query neon_auth schema directly
93
-
94
- **Cons:**
95
- - Neon Auth is currently in Beta
96
- - JWT must be forwarded to .NET API for backend auth
97
- {{/if}}
98
-
99
- ---
100
-
101
- ## Template for New ADRs
102
-
103
- ```markdown
104
- ## ADR-XXX: {Title}
105
-
106
- **Date:**
107
- **Status:** Proposed | Accepted | Deprecated | Superseded
108
-
109
- ### Context
110
- {Why is this decision needed?}
111
-
112
- ### Decision
113
- {What was decided?}
114
-
115
- ### Consequences
116
- **Pros:** {list}
117
- **Cons:** {list}
118
- ```
119
-
120
- ---
121
-
122
- *Generated by MORPH Framework*
1
+ # Architecture Decision Records - {{titleCase FEATURE_NAME}}
2
+
3
+ ## ADR-001: {Decision Title}
4
+
5
+ **Date:** {{DATE}}
6
+ **Status:** Proposed | Accepted | Deprecated | Superseded
7
+ **Deciders:** MORPH Agents, Developer
8
+
9
+ ### Context
10
+
11
+ {Describe the context and problem that led to this decision}
12
+
13
+ ### Decision
14
+
15
+ {Describe the decision made}
16
+
17
+ ### Consequences
18
+
19
+ **Pros:**
20
+ - {Positive consequence 1}
21
+ - {Positive consequence 2}
22
+
23
+ **Cons:**
24
+ - {Negative consequence 1}
25
+
26
+ **Risks:**
27
+ - {Identified risk}
28
+
29
+ ### Alternatives Considered
30
+
31
+ #### Option A: {Name}
32
+ - Pros: {advantages}
33
+ - Cons: {disadvantages}
34
+ - **Rejected because:** {reason}
35
+
36
+ #### Option B: {Name}
37
+ - Pros: {advantages}
38
+ - Cons: {disadvantages}
39
+ - **Rejected because:** {reason}
40
+
41
+ ---
42
+
43
+ {{#if (eq STACK "nextjs-neon")}}
44
+ ## ADR-002: Database Access Strategy
45
+
46
+ **Date:** {{DATE}}
47
+ **Status:** Accepted
48
+
49
+ ### Context
50
+
51
+ Need to decide how the .NET API accesses Neon PostgreSQL.
52
+
53
+ ### Decision
54
+
55
+ Use Dapper with Npgsql for direct SQL queries. No EF Core.
56
+
57
+ ### Consequences
58
+
59
+ **Pros:**
60
+ - Full control over SQL queries
61
+ - Better performance (no ORM overhead)
62
+ - Direct access to PostgreSQL features (jsonb, pgvector, functions)
63
+ - RLS policies apply naturally through connection
64
+
65
+ **Cons:**
66
+ - No automatic migrations (use Neon CLI / dotnet ef instead)
67
+ - Manual SQL query writing
68
+ - No change tracking
69
+
70
+ ---
71
+
72
+ ## ADR-003: Auth Strategy
73
+
74
+ **Date:** {{DATE}}
75
+ **Status:** Accepted
76
+
77
+ ### Context
78
+
79
+ Need authentication for both Next.js frontend and .NET API.
80
+
81
+ ### Decision
82
+
83
+ Use Neon Auth (Better Auth) for authentication. Auth data lives in the Neon database. JWT validated by .NET API middleware.
84
+
85
+ ### Consequences
86
+
87
+ **Pros:**
88
+ - Auth data in same database — no external provider, no sync delays
89
+ - RLS policies work with Neon Auth JWT via auth.user_id() / pg_session_jwt
90
+ - Built-in social login, email/password via @neondatabase/auth SDK
91
+ - Branch isolation — each Neon branch gets isolated auth data
92
+ - No webhooks needed — query neon_auth schema directly
93
+
94
+ **Cons:**
95
+ - Neon Auth is currently in Beta
96
+ - JWT must be forwarded to .NET API for backend auth
97
+ {{/if}}
98
+
99
+ ---
100
+
101
+ ## Template for New ADRs
102
+
103
+ ```markdown
104
+ ## ADR-XXX: {Title}
105
+
106
+ **Date:**
107
+ **Status:** Proposed | Accepted | Deprecated | Superseded
108
+
109
+ ### Context
110
+ {Why is this decision needed?}
111
+
112
+ ### Decision
113
+ {What was decided?}
114
+
115
+ ### Consequences
116
+ **Pros:** {list}
117
+ **Cons:** {list}
118
+ ```
119
+
120
+ ---
121
+
122
+ *Generated by MORPH Framework*
@@ -1,43 +1,43 @@
1
- # Multi-stage Next.js production build for Coolify
2
- # Stage 1: Dependencies
3
- FROM node:20-alpine AS deps
4
- RUN apk add --no-cache libc6-compat
5
- WORKDIR /app
6
-
7
- COPY package.json package-lock.json* ./
8
- RUN npm ci --only=production && npm cache clean --force
9
-
10
- # Stage 2: Build
11
- FROM node:20-alpine AS builder
12
- WORKDIR /app
13
-
14
- COPY --from=deps /app/node_modules ./node_modules
15
- COPY . .
16
-
17
- # Build args for NEXT_PUBLIC_ variables (must be available at build time)
18
- ARG NEXT_PUBLIC_APP_URL
19
- ENV NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL
20
-
21
- RUN npm run build
22
-
23
- # Stage 3: Production runner
24
- FROM node:20-alpine AS runner
25
- WORKDIR /app
26
-
27
- ENV NODE_ENV=production
28
- ENV NEXT_TELEMETRY_DISABLED=1
29
-
30
- RUN addgroup --system --gid 1001 nodejs
31
- RUN adduser --system --uid 1001 nextjs
32
-
33
- COPY --from=builder /app/public ./public
34
- COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
35
- COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
36
-
37
- USER nextjs
38
-
39
- EXPOSE 3000
40
- ENV PORT=3000
41
- ENV HOSTNAME="0.0.0.0"
42
-
43
- CMD ["node", "server.js"]
1
+ # Multi-stage Next.js production build for Coolify
2
+ # Stage 1: Dependencies
3
+ FROM node:20-alpine AS deps
4
+ RUN apk add --no-cache libc6-compat
5
+ WORKDIR /app
6
+
7
+ COPY package.json package-lock.json* ./
8
+ RUN npm ci --only=production && npm cache clean --force
9
+
10
+ # Stage 2: Build
11
+ FROM node:20-alpine AS builder
12
+ WORKDIR /app
13
+
14
+ COPY --from=deps /app/node_modules ./node_modules
15
+ COPY . .
16
+
17
+ # Build args for NEXT_PUBLIC_ variables (must be available at build time)
18
+ ARG NEXT_PUBLIC_APP_URL
19
+ ENV NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL
20
+
21
+ RUN npm run build
22
+
23
+ # Stage 3: Production runner
24
+ FROM node:20-alpine AS runner
25
+ WORKDIR /app
26
+
27
+ ENV NODE_ENV=production
28
+ ENV NEXT_TELEMETRY_DISABLED=1
29
+
30
+ RUN addgroup --system --gid 1001 nodejs
31
+ RUN adduser --system --uid 1001 nextjs
32
+
33
+ COPY --from=builder /app/public ./public
34
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
35
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
36
+
37
+ USER nextjs
38
+
39
+ EXPOSE 3000
40
+ ENV PORT=3000
41
+ ENV HOSTNAME="0.0.0.0"
42
+
43
+ CMD ["node", "server.js"]
@@ -1,26 +1,26 @@
1
- // features/{{kebabCase featureName}}/components/{{kebabCase featureName}}-list.tsx
2
- 'use client';
3
-
4
- import { use{{pascalCase featureName}}s } from '@/features/{{kebabCase featureName}}/hooks/use-{{kebabCase featureName}}s';
5
- import { DataTable } from '@/components/data-table';
6
- import type { {{pascalCase featureName}} } from '@/features/{{kebabCase featureName}}/types/{{kebabCase featureName}}.types';
7
- import type { ColumnDef } from '@tanstack/react-table';
8
-
9
- const columns: ColumnDef<{{pascalCase featureName}}>[] = [
10
- { accessorKey: 'id', header: 'ID' },
11
- { accessorKey: 'name', header: 'Name' },
12
- ];
13
-
14
- interface {{pascalCase featureName}}ListProps {
15
- initialData?: {{pascalCase featureName}}[];
16
- }
17
-
18
- export function {{pascalCase featureName}}List({ initialData }: {{pascalCase featureName}}ListProps) {
19
- const { data = initialData ?? [], isLoading } = use{{pascalCase featureName}}s();
20
-
21
- if (isLoading) {
22
- return <div className="flex items-center justify-center h-32">Loading...</div>;
23
- }
24
-
25
- return <DataTable columns={columns} data={data} />;
26
- }
1
+ // features/{{kebabCase featureName}}/components/{{kebabCase featureName}}-list.tsx
2
+ 'use client';
3
+
4
+ import { use{{pascalCase featureName}}s } from '@/features/{{kebabCase featureName}}/hooks/use-{{kebabCase featureName}}s';
5
+ import { DataTable } from '@/components/data-table';
6
+ import type { {{pascalCase featureName}} } from '@/features/{{kebabCase featureName}}/types/{{kebabCase featureName}}.types';
7
+ import type { ColumnDef } from '@tanstack/react-table';
8
+
9
+ const columns: ColumnDef<{{pascalCase featureName}}>[] = [
10
+ { accessorKey: 'id', header: 'ID' },
11
+ { accessorKey: 'name', header: 'Name' },
12
+ ];
13
+
14
+ interface {{pascalCase featureName}}ListProps {
15
+ initialData?: {{pascalCase featureName}}[];
16
+ }
17
+
18
+ export function {{pascalCase featureName}}List({ initialData }: {{pascalCase featureName}}ListProps) {
19
+ const { data = initialData ?? [], isLoading } = use{{pascalCase featureName}}s();
20
+
21
+ if (isLoading) {
22
+ return <div className="flex items-center justify-center h-32">Loading...</div>;
23
+ }
24
+
25
+ return <DataTable columns={columns} data={data} />;
26
+ }
@@ -1,32 +1,32 @@
1
- // env.mjs — Zod-validated environment variables
2
- // Crashes at startup if required vars are missing — no silent undefined
3
- import { z } from 'zod';
4
-
5
- const serverSchema = z.object({
6
- API_URL: z.string().url('API_URL must be a valid URL'),
7
- NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
8
- });
9
-
10
- const clientSchema = z.object({
11
- NEXT_PUBLIC_APP_URL: z.string().url().optional(),
12
- });
13
-
14
- // Validate server-side env (only available on server)
15
- const serverEnv = serverSchema.safeParse(process.env);
16
- if (!serverEnv.success) {
17
- console.error('❌ Invalid server environment variables:');
18
- console.error(serverEnv.error.flatten().fieldErrors);
19
- throw new Error('Invalid environment variables');
20
- }
21
-
22
- // Validate client-side env
23
- const clientEnv = clientSchema.safeParse({
24
- NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
25
- });
26
- if (!clientEnv.success) {
27
- console.error('❌ Invalid client environment variables:');
28
- console.error(clientEnv.error.flatten().fieldErrors);
29
- throw new Error('Invalid client environment variables');
30
- }
31
-
32
- export const env = { ...serverEnv.data, ...clientEnv.data };
1
+ // env.mjs — Zod-validated environment variables
2
+ // Crashes at startup if required vars are missing — no silent undefined
3
+ import { z } from 'zod';
4
+
5
+ const serverSchema = z.object({
6
+ API_URL: z.string().url('API_URL must be a valid URL'),
7
+ NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
8
+ });
9
+
10
+ const clientSchema = z.object({
11
+ NEXT_PUBLIC_APP_URL: z.string().url().optional(),
12
+ });
13
+
14
+ // Validate server-side env (only available on server)
15
+ const serverEnv = serverSchema.safeParse(process.env);
16
+ if (!serverEnv.success) {
17
+ console.error('❌ Invalid server environment variables:');
18
+ console.error(serverEnv.error.flatten().fieldErrors);
19
+ throw new Error('Invalid environment variables');
20
+ }
21
+
22
+ // Validate client-side env
23
+ const clientEnv = clientSchema.safeParse({
24
+ NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
25
+ });
26
+ if (!clientEnv.success) {
27
+ console.error('❌ Invalid client environment variables:');
28
+ console.error(clientEnv.error.flatten().fieldErrors);
29
+ throw new Error('Invalid client environment variables');
30
+ }
31
+
32
+ export const env = { ...serverEnv.data, ...clientEnv.data };
@@ -1,56 +1,56 @@
1
- // features/{{kebabCase featureName}}/components/create-{{kebabCase featureName}}-form.tsx
2
- 'use client';
3
-
4
- import { useForm } from 'react-hook-form';
5
- import { zodResolver } from '@hookform/resolvers/zod';
6
- import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
7
- import { Input } from '@/components/ui/input';
8
- import { Button } from '@/components/ui/button';
9
- import { useCreate{{pascalCase featureName}} } from '@/features/{{kebabCase featureName}}/hooks/use-create-{{kebabCase featureName}}';
10
- import { create{{pascalCase featureName}}Schema, type Create{{pascalCase featureName}}Input } from '@/features/{{kebabCase featureName}}/types/{{kebabCase featureName}}.schemas';
11
-
12
- interface Create{{pascalCase featureName}}FormProps {
13
- onSuccess?: () => void;
14
- }
15
-
16
- export function Create{{pascalCase featureName}}Form({ onSuccess }: Create{{pascalCase featureName}}FormProps) {
17
- const form = useForm<Create{{pascalCase featureName}}Input>({
18
- resolver: zodResolver(create{{pascalCase featureName}}Schema),
19
- defaultValues: { name: '' },
20
- });
21
-
22
- const { mutate: create{{pascalCase featureName}}, isPending } = useCreate{{pascalCase featureName}}();
23
-
24
- function onSubmit(values: Create{{pascalCase featureName}}Input) {
25
- create{{pascalCase featureName}}(values, {
26
- onSuccess: () => { form.reset(); onSuccess?.(); },
27
- onError: (error) => { form.setError('root', { message: error.message }); },
28
- });
29
- }
30
-
31
- return (
32
- <Form {...form}>
33
- <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
34
- <FormField
35
- control={form.control}
36
- name="name"
37
- render={({ field }) => (
38
- <FormItem>
39
- <FormLabel>Name</FormLabel>
40
- <FormControl>
41
- <Input placeholder="Enter name" {...field} />
42
- </FormControl>
43
- <FormMessage />
44
- </FormItem>
45
- )}
46
- />
47
- {form.formState.errors.root && (
48
- <p className="text-sm text-destructive">{form.formState.errors.root.message}</p>
49
- )}
50
- <Button type="submit" disabled={isPending}>
51
- {isPending ? 'Creating...' : 'Create {{pascalCase featureName}}'}
52
- </Button>
53
- </form>
54
- </Form>
55
- );
56
- }
1
+ // features/{{kebabCase featureName}}/components/create-{{kebabCase featureName}}-form.tsx
2
+ 'use client';
3
+
4
+ import { useForm } from 'react-hook-form';
5
+ import { zodResolver } from '@hookform/resolvers/zod';
6
+ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
7
+ import { Input } from '@/components/ui/input';
8
+ import { Button } from '@/components/ui/button';
9
+ import { useCreate{{pascalCase featureName}} } from '@/features/{{kebabCase featureName}}/hooks/use-create-{{kebabCase featureName}}';
10
+ import { create{{pascalCase featureName}}Schema, type Create{{pascalCase featureName}}Input } from '@/features/{{kebabCase featureName}}/types/{{kebabCase featureName}}.schemas';
11
+
12
+ interface Create{{pascalCase featureName}}FormProps {
13
+ onSuccess?: () => void;
14
+ }
15
+
16
+ export function Create{{pascalCase featureName}}Form({ onSuccess }: Create{{pascalCase featureName}}FormProps) {
17
+ const form = useForm<Create{{pascalCase featureName}}Input>({
18
+ resolver: zodResolver(create{{pascalCase featureName}}Schema),
19
+ defaultValues: { name: '' },
20
+ });
21
+
22
+ const { mutate: create{{pascalCase featureName}}, isPending } = useCreate{{pascalCase featureName}}();
23
+
24
+ function onSubmit(values: Create{{pascalCase featureName}}Input) {
25
+ create{{pascalCase featureName}}(values, {
26
+ onSuccess: () => { form.reset(); onSuccess?.(); },
27
+ onError: (error) => { form.setError('root', { message: error.message }); },
28
+ });
29
+ }
30
+
31
+ return (
32
+ <Form {...form}>
33
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
34
+ <FormField
35
+ control={form.control}
36
+ name="name"
37
+ render={({ field }) => (
38
+ <FormItem>
39
+ <FormLabel>Name</FormLabel>
40
+ <FormControl>
41
+ <Input placeholder="Enter name" {...field} />
42
+ </FormControl>
43
+ <FormMessage />
44
+ </FormItem>
45
+ )}
46
+ />
47
+ {form.formState.errors.root && (
48
+ <p className="text-sm text-destructive">{form.formState.errors.root.message}</p>
49
+ )}
50
+ <Button type="submit" disabled={isPending}>
51
+ {isPending ? 'Creating...' : 'Create {{pascalCase featureName}}'}
52
+ </Button>
53
+ </form>
54
+ </Form>
55
+ );
56
+ }
@@ -1,22 +1,22 @@
1
- // app/(dashboard)/{{kebabCase featureName}}/page.tsx
2
- // Server Component — fetches initial data, renders feature component
3
- import { {{pascalCase featureName}}List } from '@/features/{{kebabCase featureName}}';
4
-
5
- async function get{{pascalCase featureName}}List() {
6
- const res = await fetch(`${process.env.API_URL}/api/{{kebabCase featureName}}`, {
7
- next: { revalidate: 30 },
8
- headers: { 'Content-Type': 'application/json' },
9
- });
10
- if (!res.ok) throw new Error('Failed to fetch {{featureName}}');
11
- return res.json();
12
- }
13
-
14
- export default async function {{pascalCase featureName}}Page() {
15
- const data = await get{{pascalCase featureName}}List();
16
- return (
17
- <div className="container py-6">
18
- <h1 className="text-2xl font-bold mb-6">{{pascalCase featureName}}</h1>
19
- <{{pascalCase featureName}}List initialData={data} />
20
- </div>
21
- );
22
- }
1
+ // app/(dashboard)/{{kebabCase featureName}}/page.tsx
2
+ // Server Component — fetches initial data, renders feature component
3
+ import { {{pascalCase featureName}}List } from '@/features/{{kebabCase featureName}}';
4
+
5
+ async function get{{pascalCase featureName}}List() {
6
+ const res = await fetch(`${process.env.API_URL}/api/{{kebabCase featureName}}`, {
7
+ next: { revalidate: 30 },
8
+ headers: { 'Content-Type': 'application/json' },
9
+ });
10
+ if (!res.ok) throw new Error('Failed to fetch {{featureName}}');
11
+ return res.json();
12
+ }
13
+
14
+ export default async function {{pascalCase featureName}}Page() {
15
+ const data = await get{{pascalCase featureName}}List();
16
+ return (
17
+ <div className="container py-6">
18
+ <h1 className="text-2xl font-bold mb-6">{{pascalCase featureName}}</h1>
19
+ <{{pascalCase featureName}}List initialData={data} />
20
+ </div>
21
+ );
22
+ }
@@ -1,26 +1,26 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "lib": ["dom", "dom.iterable", "esnext"],
5
- "allowJs": false,
6
- "skipLibCheck": true,
7
- "strict": true,
8
- "noUncheckedIndexedAccess": true,
9
- "noImplicitOverride": true,
10
- "forceConsistentCasingInFileNames": true,
11
- "noEmit": true,
12
- "esModuleInterop": true,
13
- "module": "esnext",
14
- "moduleResolution": "bundler",
15
- "resolveJsonModule": true,
16
- "isolatedModules": true,
17
- "jsx": "preserve",
18
- "incremental": true,
19
- "plugins": [{ "name": "next" }],
20
- "paths": {
21
- "@/*": ["./src/*"]
22
- }
23
- },
24
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25
- "exclude": ["node_modules"]
26
- }
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": false,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noUncheckedIndexedAccess": true,
9
+ "noImplicitOverride": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "noEmit": true,
12
+ "esModuleInterop": true,
13
+ "module": "esnext",
14
+ "moduleResolution": "bundler",
15
+ "resolveJsonModule": true,
16
+ "isolatedModules": true,
17
+ "jsx": "preserve",
18
+ "incremental": true,
19
+ "plugins": [{ "name": "next" }],
20
+ "paths": {
21
+ "@/*": ["./src/*"]
22
+ }
23
+ },
24
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25
+ "exclude": ["node_modules"]
26
+ }