@polymorphism-tech/morph-spec 8.24.3 → 8.26.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/README.md +2 -2
- package/bin/morph-spec.js +2 -1
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +2 -2
- package/framework/MORPH.md +22 -21
- package/framework/agents.json +1 -1
- package/framework/commands/create-umbraco-lp.md +1 -1
- package/framework/commands/migrate-lp-to-umbraco.md +1 -1
- package/framework/commands/morph-apply.md +4 -4
- package/framework/commands/morph-hotfix.md +3 -3
- package/framework/commands/morph-proposal.md +5 -5
- package/framework/docs/CLI.md +4 -4
- package/framework/evals/rubrics/condensed.md +2 -2
- package/framework/evals/rubrics/test-coverage.md +7 -0
- package/framework/hooks/claude-code/core/protect-spec-files.js +115 -4
- package/framework/hooks/shared/payload-utils.js +19 -4
- package/framework/rules/dotnet-templates.md +2 -1
- package/framework/rules/morph-workflow.md +42 -29
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +6 -6
- package/framework/skills/core/morph-brainstorming/SKILL.md +2 -2
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +7 -7
- package/framework/skills/core/morph-implement/SKILL.md +21 -6
- package/framework/skills/core/morph-plan/SKILL.md +15 -3
- package/framework/skills/core/morph-proposal/SKILL.md +7 -7
- package/framework/skills/core/morph-review/SKILL.md +10 -3
- package/framework/skills/core/morph-stack-scan/SKILL.md +3 -3
- package/framework/skills/core/morph-standards/SKILL.md +4 -4
- package/framework/skills/core/morph-uiux/SKILL.md +3 -3
- package/framework/templates/README.md +72 -643
- package/framework/templates/examples/spec-examples.md +2 -1
- package/package.json +1 -1
- package/src/commands/project/claim.js +19 -3
- package/src/commands/state/approve.js +17 -3
- package/src/commands/state/finish.js +202 -15
- package/src/commands/state/gate-check.js +9 -20
- package/src/commands/state/report-check.js +157 -5
- package/src/lib/evaluator-report.js +72 -0
- package/src/lib/risk-detector.js +35 -0
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
# MORPH-SPEC Template System
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Handlebars templates organized by **technology**, indexed by `REGISTRY.json`.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**89 templates**, grouped by the technology they target (dotnet, typescript, sql, docker,
|
|
8
|
+
nextjs) rather than by stack. A template for a .NET slice serves every stack with a .NET
|
|
9
|
+
backend, so a new stack reuses what exists instead of copying it.
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
`REGISTRY.json` is the source of truth: it maps each template `id` to its `path`, category,
|
|
12
|
+
technology and applicable stacks. Nothing resolves a template by hardcoded path — code goes
|
|
13
|
+
through the registry, and this README never repeats a path the registry already owns.
|
|
10
14
|
|
|
11
15
|
---
|
|
12
16
|
|
|
@@ -14,135 +18,57 @@ The MORPH-SPEC template system provides **57 production-ready templates** organi
|
|
|
14
18
|
|
|
15
19
|
```
|
|
16
20
|
framework/templates/
|
|
17
|
-
├── docs/ #
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
├── docs/ # Phase documents (proposal, spec, user-stories, ui-*, onboarding…)
|
|
22
|
+
├── feature/ # decisions.md, recap.md
|
|
23
|
+
├── context/ # ARCHITECTURE.md, design-system.md
|
|
20
24
|
│
|
|
21
|
-
├──
|
|
22
|
-
│ ├──
|
|
23
|
-
│
|
|
25
|
+
├── code/ # Code templates, by technology
|
|
26
|
+
│ ├── dotnet/ # abstractions, ai-agents, constants, contracts, database,
|
|
27
|
+
│ │ # exceptions, extensions, feature, jobs, pipelines
|
|
28
|
+
│ ├── typescript/ # contracts.ts
|
|
29
|
+
│ └── sql/ # postgresql migrations, RLS policy, ai-agents
|
|
24
30
|
│
|
|
25
|
-
├──
|
|
26
|
-
│
|
|
27
|
-
│ └── CONTEXT-FEATURE.md # Feature-level context
|
|
28
|
-
│
|
|
29
|
-
├── code/ # Code templates organized by technology
|
|
30
|
-
│ ├── dotnet/
|
|
31
|
-
│ │ ├── backend/
|
|
32
|
-
│ │ │ ├── service.cs # Service layer
|
|
33
|
-
│ │ │ └── test.cs # Unit tests
|
|
34
|
-
│ │ ├── database/
|
|
35
|
-
│ │ │ └── migration.cs # EF Core migrations
|
|
36
|
-
│ │ ├── jobs/
|
|
37
|
-
│ │ │ ├── job.cs # Hangfire background jobs
|
|
38
|
-
│ │ │ └── agent.cs # MS Agent Framework agents
|
|
39
|
-
│ │ └── contracts/
|
|
40
|
-
│ │ ├── contracts.cs # Base contracts
|
|
41
|
-
│ │ ├── Commands.cs # CQRS commands
|
|
42
|
-
│ │ ├── Queries.cs # CQRS queries
|
|
43
|
-
│ │ ├── Entities.cs # Domain entities
|
|
44
|
-
│ │ ├── api-contracts.cs # API contracts
|
|
45
|
-
│ │ └── README.md # Contracts documentation
|
|
46
|
-
│ │
|
|
47
|
-
│ ├── typescript/
|
|
48
|
-
│ │ └── contracts.ts # TypeScript type definitions
|
|
49
|
-
│ │
|
|
50
|
-
│ └── sql/
|
|
51
|
-
│ ├── postgresql-migration.sql # Basic migration
|
|
52
|
-
│ ├── postgresql-migration.template.sql # Enhanced migration
|
|
53
|
-
│ └── postgresql-rls-policy.sql # Row-level security
|
|
31
|
+
├── frontend/
|
|
32
|
+
│ └── nextjs/ # page, client-component, feature-form, use-feature, env, tsconfig…
|
|
54
33
|
│
|
|
55
34
|
├── infrastructure/
|
|
56
|
-
│ ├── docker/ #
|
|
57
|
-
│
|
|
58
|
-
│ │ ├── dockerfile-web.dockerfile # Next.js web
|
|
59
|
-
│ │ ├── Dockerfile.template # Generic
|
|
60
|
-
│ │ ├── docker-compose.template.yml # Compose
|
|
61
|
-
│ │ └── coolify.template.json # Coolify config
|
|
62
|
-
│ │
|
|
63
|
-
│ └── github/ # GitHub Actions CI/CD
|
|
64
|
-
│ ├── workflows/ # Reusable workflow templates
|
|
65
|
-
│ │ ├── dotnet-build.yml.hbs # .NET restore, build, test
|
|
66
|
-
│ │ ├── docker-build-push.yml.hbs # Docker build & push
|
|
67
|
-
│ │ └── deploy-coolify.yml.hbs # Coolify deployment (via SSH)
|
|
68
|
-
│ │
|
|
69
|
-
│ ├── actions/ # Composite action templates
|
|
70
|
-
│ │ ├── docker-build-push/action.yml.hbs
|
|
71
|
-
│ │ └── health-check/action.yml.hbs
|
|
72
|
-
│ │
|
|
73
|
-
│ └── README.md # GitHub Actions docs
|
|
74
|
-
│
|
|
75
|
-
├── integrations/ # Platform-agnostic integrations
|
|
76
|
-
│ ├── asaas-client.cs # Asaas financial API
|
|
77
|
-
│ ├── asaas-webhook.cs # Asaas webhooks
|
|
78
|
-
│ └── neon-auth-config.cs # Neon Auth (Better Auth)
|
|
79
|
-
│
|
|
80
|
-
├── ui/ # Design systems
|
|
81
|
-
│ └── design-system.css # CSS tokens
|
|
35
|
+
│ ├── docker/ # Dockerfiles, compose, Coolify config
|
|
36
|
+
│ └── github/ # Actions workflows + composite actions (.hbs)
|
|
82
37
|
│
|
|
83
|
-
├──
|
|
84
|
-
|
|
85
|
-
├──
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
│
|
|
89
|
-
└── REGISTRY.json # Template metadata registry
|
|
38
|
+
├── integrations/ # asaas-client, asaas-webhook, neon-auth-config
|
|
39
|
+
├── ui/ # design-system.css
|
|
40
|
+
├── meta-prompts/ # fusion, squad-leaders, validators
|
|
41
|
+
├── examples/ # spec-examples.md, design-system-examples.md
|
|
42
|
+
└── REGISTRY.json # template metadata — the index every consumer reads
|
|
90
43
|
```
|
|
91
44
|
|
|
92
45
|
---
|
|
93
46
|
|
|
94
|
-
## 🏗️ Technology-Based Architecture
|
|
95
|
-
|
|
96
|
-
**Why organize by technology instead of by stack?**
|
|
97
|
-
|
|
98
|
-
| Organization | Pros | Cons | Best For |
|
|
99
|
-
|--------------|------|------|----------|
|
|
100
|
-
| **By Stack** (old v1) | Stack-specific optimizations | Duplicate templates, hard to reuse | Single-stack projects |
|
|
101
|
-
| **By Technology** (new v2) | Reusable, DRY, future-proof | Requires stack override mechanism | Multi-stack frameworks |
|
|
102
|
-
|
|
103
|
-
**Example:** A `.NET service.cs` template works for both `dotnet-nextjs` and `nextjs-neon` stacks (both use .NET backend). No need to duplicate!
|
|
104
|
-
|
|
105
|
-
**Future-proof:** When we create `vue-firebase` stack, we can reuse:
|
|
106
|
-
- `code/typescript/` templates
|
|
107
|
-
- `infrastructure/docker/` templates
|
|
108
|
-
- `integrations/` templates (Neon Auth, etc.)
|
|
109
|
-
|
|
110
|
-
**Only create NEW templates for Vue-specific code.**
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
47
|
## ⚙️ Template Engine: Handlebars
|
|
115
48
|
|
|
116
|
-
All templates
|
|
117
|
-
|
|
118
|
-
### Basic Syntax
|
|
49
|
+
All templates render through Handlebars with custom helpers registered in
|
|
50
|
+
`src/core/templates/template-renderer.js`.
|
|
119
51
|
|
|
120
52
|
```handlebars
|
|
121
53
|
# Feature: {{titleCase FEATURE_NAME}}
|
|
122
54
|
|
|
123
|
-
|
|
55
|
+
namespace {{pascalCase FEATURE_NAME}};
|
|
124
56
|
|
|
125
|
-
Created: {{
|
|
57
|
+
Created: {{now}}
|
|
126
58
|
```
|
|
127
59
|
|
|
128
|
-
### Available
|
|
129
|
-
|
|
130
|
-
|
|
|
131
|
-
|
|
132
|
-
| `
|
|
133
|
-
|
|
|
134
|
-
|
|
|
135
|
-
|
|
|
136
|
-
| `
|
|
137
|
-
|
|
|
138
|
-
|
|
|
139
|
-
|
|
|
140
|
-
| `{{eq a b}}` | `"nextjs" "nextjs"` | `true` | Conditionals |
|
|
141
|
-
| `{{or a b}}` | `false true` | `true` | Logical OR |
|
|
142
|
-
| `{{and a b}}` | `true false` | `false` | Logical AND |
|
|
143
|
-
| `{{default val fallback}}` | `null "default"` | `"default"` | Defaults |
|
|
144
|
-
|
|
145
|
-
### Conditional Logic
|
|
60
|
+
### Available helpers
|
|
61
|
+
|
|
62
|
+
| Group | Helpers |
|
|
63
|
+
|-------|---------|
|
|
64
|
+
| **Case** | `pascalCase` `camelCase` `snakeCase` `upperSnakeCase` `kebabCase` `titleCase` `uppercase` `lowercase` `capitalize` |
|
|
65
|
+
| **Words** | `pluralize` `singularize` |
|
|
66
|
+
| **Comparison** | `eq` `ne` `lt` `gt` `lte` `gte` |
|
|
67
|
+
| **Logic** | `and` `or` `not` `default` |
|
|
68
|
+
| **String** | `trim` `replace` `concat` `substr` `startsWith` `endsWith` `contains` `length` |
|
|
69
|
+
| **Math** | `add` `subtract` `multiply` `divide` `mod` `round` |
|
|
70
|
+
| **Array** | `join` `first` `last` `slice` |
|
|
71
|
+
| **Misc** | `json` `now` `year` `formatDate` |
|
|
146
72
|
|
|
147
73
|
```handlebars
|
|
148
74
|
{{#if (eq STACK "nextjs-neon")}}
|
|
@@ -152,556 +78,59 @@ Created: {{DATE}}
|
|
|
152
78
|
{{/if}}
|
|
153
79
|
```
|
|
154
80
|
|
|
155
|
-
### Deprecated: Pre-computed Variables
|
|
156
|
-
|
|
157
|
-
**❌ Old (v1):**
|
|
158
|
-
```handlebars
|
|
159
|
-
{{pascalCase FEATURE_NAME}} <!-- Pre-computed in bin/render-template.js -->
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
**✅ New (v2):**
|
|
163
|
-
```handlebars
|
|
164
|
-
{{pascalCase FEATURE_NAME}} <!-- Computed via Handlebars helper -->
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
---
|
|
168
|
-
|
|
169
|
-
## 🛠️ CLI Commands
|
|
170
|
-
|
|
171
|
-
### List All Templates
|
|
172
|
-
|
|
173
|
-
```bash
|
|
174
|
-
# List all templates
|
|
175
|
-
morph-spec template list
|
|
176
|
-
|
|
177
|
-
# Filter by category
|
|
178
|
-
morph-spec template list --category code
|
|
179
|
-
morph-spec template list --category infrastructure
|
|
180
|
-
|
|
181
|
-
# Filter by phase
|
|
182
|
-
morph-spec template list --phase implement
|
|
183
|
-
|
|
184
|
-
# Filter by stack applicability
|
|
185
|
-
morph-spec template list --stack dotnet-nextjs
|
|
186
|
-
|
|
187
|
-
# Show only required templates
|
|
188
|
-
morph-spec template list --required
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
**Output:**
|
|
192
|
-
```
|
|
193
|
-
📦 Universal Templates (9)
|
|
194
|
-
proposal Feature Proposal [REQUIRED] [UNIVERSAL] [HBS]
|
|
195
|
-
spec Technical Specification [REQUIRED] [UNIVERSAL] [HBS]
|
|
196
|
-
tasks Implementation Tasks [REQUIRED] [UNIVERSAL] [HBS]
|
|
197
|
-
...
|
|
198
|
-
|
|
199
|
-
📦 Technology-Based Templates (48)
|
|
200
|
-
dotnet-backend-service .NET Service Class [UNIVERSAL] [HBS] (dotnet)
|
|
201
|
-
typescript-contracts TypeScript Contracts [UNIVERSAL] [HBS] (typescript)
|
|
202
|
-
...
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
### Show Template Details
|
|
206
|
-
|
|
207
|
-
```bash
|
|
208
|
-
# Show metadata
|
|
209
|
-
morph-spec template show dotnet-backend-service
|
|
210
|
-
|
|
211
|
-
# Show with file preview
|
|
212
|
-
morph-spec template show dotnet-backend-service --preview
|
|
213
|
-
|
|
214
|
-
# Show with resolved path
|
|
215
|
-
morph-spec template show dotnet-backend-service --show-path
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
**Output:**
|
|
219
|
-
```
|
|
220
|
-
Template: dotnet-backend-service
|
|
221
|
-
|
|
222
|
-
📋 Metadata
|
|
223
|
-
ID: dotnet-backend-service
|
|
224
|
-
Name: .NET Service Class
|
|
225
|
-
Path: code/dotnet/backend/service.cs
|
|
226
|
-
Location: framework
|
|
227
|
-
Category: code
|
|
228
|
-
Phase: implement
|
|
229
|
-
Required: No
|
|
230
|
-
Technology: dotnet
|
|
231
|
-
Applicable Stacks: dotnet-nextjs, nextjs-neon
|
|
232
|
-
|
|
233
|
-
📝 Description
|
|
234
|
-
C# service class template with dependency injection and interface pattern
|
|
235
|
-
|
|
236
|
-
🔑 Variables
|
|
237
|
-
{{FEATURE_NAME}}
|
|
238
|
-
{{NAMESPACE}}
|
|
239
|
-
|
|
240
|
-
📄 Output Name
|
|
241
|
-
{FeatureName}Service.cs
|
|
242
|
-
|
|
243
|
-
💡 Usage Hint
|
|
244
|
-
morph-spec template render dotnet-backend-service output.cs '{"FEATURE_NAME":"user-auth","NAMESPACE":"MyApp"}'
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
### Render Template
|
|
248
|
-
|
|
249
|
-
```bash
|
|
250
|
-
# Render to file
|
|
251
|
-
morph-spec template render dotnet-backend-service UserAuthService.cs '{
|
|
252
|
-
"FEATURE_NAME": "user-auth",
|
|
253
|
-
"NAMESPACE": "MyApp.Services"
|
|
254
|
-
}'
|
|
255
|
-
|
|
256
|
-
# Dry run (preview only)
|
|
257
|
-
morph-spec template render dotnet-backend-service output.cs '{"FEATURE_NAME":"test"}' --dry-run
|
|
258
|
-
|
|
259
|
-
# Auto-resolve path (searches framework → stack override)
|
|
260
|
-
morph-spec template render proposal proposal.md '{"FEATURE_NAME":"auth"}' --resolve-path
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
**Default Variables (auto-injected):**
|
|
264
|
-
- `DATE`: `2026-02-17`
|
|
265
|
-
- `YEAR`: `2026`
|
|
266
|
-
- `TIMESTAMP`: `2026-02-17T19:38:00.000Z`
|
|
267
|
-
|
|
268
|
-
### Customize Template (Stack Override)
|
|
269
|
-
|
|
270
|
-
```bash
|
|
271
|
-
# Copy to stack-specific directory (override framework version)
|
|
272
|
-
morph-spec template customize dotnet-backend-service --stack dotnet-nextjs
|
|
273
|
-
|
|
274
|
-
# Copy to project-local (cross-stack override)
|
|
275
|
-
morph-spec template customize dotnet-backend-service --local
|
|
276
|
-
|
|
277
|
-
# Force overwrite if exists
|
|
278
|
-
morph-spec template customize dotnet-backend-service --stack dotnet-nextjs --force
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
**Output:**
|
|
282
|
-
```
|
|
283
|
-
Source: framework/templates/code/dotnet/backend/service.cs
|
|
284
|
-
Destination: stacks/dotnet-nextjs/.morph/framework/templates/code/dotnet/backend/service.cs
|
|
285
|
-
|
|
286
|
-
✅ Template copied for customization
|
|
287
|
-
|
|
288
|
-
📝 Next Steps
|
|
289
|
-
1. Edit the template file:
|
|
290
|
-
stacks/dotnet-nextjs/.morph/framework/templates/code/dotnet/backend/service.cs
|
|
291
|
-
|
|
292
|
-
2. Template will now be used instead of framework version
|
|
293
|
-
(Stack-specific templates take precedence)
|
|
294
|
-
|
|
295
|
-
3. Test rendering:
|
|
296
|
-
morph-spec template render dotnet-backend-service output.cs '{"FEATURE_NAME":"test"}'
|
|
297
|
-
|
|
298
|
-
⚠️ Customized templates won't receive framework updates
|
|
299
|
-
To get updates: delete customized version and re-run this command
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
---
|
|
303
|
-
|
|
304
|
-
## 🔄 GitHub Actions Templates
|
|
305
|
-
|
|
306
|
-
The framework provides reusable GitHub Actions workflows and composite actions for CI/CD automation.
|
|
307
|
-
|
|
308
|
-
### Structure
|
|
309
|
-
|
|
310
|
-
```
|
|
311
|
-
infrastructure/github/
|
|
312
|
-
├── workflows/ # Reusable workflow templates
|
|
313
|
-
│ ├── dotnet-build.yml.hbs # .NET restore, build, test
|
|
314
|
-
│ ├── docker-build-push.yml.hbs # Docker build & push
|
|
315
|
-
│ └── deploy-coolify.yml.hbs # Coolify deployment via SSH
|
|
316
|
-
│
|
|
317
|
-
└── actions/ # Composite action templates
|
|
318
|
-
├── docker-build-push/action.yml.hbs
|
|
319
|
-
└── health-check/action.yml.hbs
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
### Reusable Workflows vs Composite Actions
|
|
323
|
-
|
|
324
|
-
| Type | Purpose | Location | Usage |
|
|
325
|
-
|------|---------|----------|-------|
|
|
326
|
-
| **Reusable Workflows** | Complete CI/CD pipelines (multi-job) | `workflows/*.yml.hbs` | Called via `uses:` from stack workflows |
|
|
327
|
-
| **Composite Actions** | Reusable step logic (single responsibility) | `actions/*/action.yml.hbs` | Called via `uses:` in workflow steps |
|
|
328
|
-
|
|
329
|
-
### Deployment Strategy Difference
|
|
330
|
-
|
|
331
|
-
**NextJS-Neon:** Docker containers to Coolify
|
|
332
|
-
- `docker build` → `docker push` → Coolify deploy triggered via SSH (Coolify's API is not publicly exposed — the runner SSHes into the VPS and calls `localhost:8000/api/v1/deploy` there)
|
|
333
|
-
- Multi-service deployment (API + Web)
|
|
334
|
-
- Registry: GitHub Container Registry (ghcr.io)
|
|
335
|
-
|
|
336
|
-
### Template Rendering
|
|
337
|
-
|
|
338
|
-
**GitHub Actions Constraint:**
|
|
339
|
-
- ❌ GitHub Actions CANNOT reference parent directories (`../../../../framework/`)
|
|
340
|
-
- ✅ Solution: Framework templates are **rendered** into each stack's `.github/` directory
|
|
341
|
-
- Rendered `.yml` files are **committed** to repos (not `.yml.hbs` templates)
|
|
342
|
-
|
|
343
|
-
**Rendering Process:**
|
|
344
|
-
```bash
|
|
345
|
-
# Render all GitHub Actions templates for a stack (manual for now)
|
|
346
|
-
# Future: morph-spec template render github-workflows --stack dotnet-nextjs
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
### Handlebars Variables
|
|
350
|
-
|
|
351
|
-
- `{{APP_NAME}}` - Application name from config.json
|
|
352
|
-
- `{{DOTNET_VERSION}}` - .NET SDK version
|
|
353
|
-
- `{{GITHUB_OWNER}}` - GitHub organization/user
|
|
354
|
-
- `{{kebabCase APP_NAME}}` - Docker image name format
|
|
355
|
-
- `${{{{ inputs.* }}}}` - GitHub Actions runtime syntax (escaped with `{{{{raw}}}}`)
|
|
356
|
-
|
|
357
|
-
### Authentication & Secrets
|
|
358
|
-
|
|
359
|
-
**Docker Registry (NextJS-Neon):**
|
|
360
|
-
```yaml
|
|
361
|
-
# GitHub Container Registry (ghcr.io)
|
|
362
|
-
- uses: docker/login-action@v3
|
|
363
|
-
with:
|
|
364
|
-
registry: ghcr.io
|
|
365
|
-
username: ${{{{ github.actor }}}}
|
|
366
|
-
password: ${{{{ secrets.GITHUB_TOKEN }}}}
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
**GitHub Secrets to Configure:**
|
|
370
|
-
- NextJS-Neon: `GITHUB_TOKEN` (auto), `COOLIFY_SSH_HOST`, `COOLIFY_SSH_USER`, `COOLIFY_SSH_KEY`, `COOLIFY_APPLICATION_UUID`
|
|
371
|
-
|
|
372
|
-
### Stack-Specific Overrides
|
|
373
|
-
|
|
374
|
-
Stack-specific workflow templates in:
|
|
375
|
-
- `stacks/nextjs-neon/.morph/framework/templates/infrastructure/github/workflows/`
|
|
376
|
-
|
|
377
|
-
**Resolution Order:**
|
|
378
|
-
1. Project-local: `.morph/framework/templates/infrastructure/github/workflows/ci-build.yml.hbs`
|
|
379
|
-
2. Stack default: `stacks/{stack}/.morph/framework/templates/infrastructure/github/workflows/ci-build.yml.hbs`
|
|
380
|
-
3. Framework fallback: `framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs`
|
|
381
|
-
|
|
382
|
-
---
|
|
383
|
-
|
|
384
|
-
## 🔀 Stack Override Mechanism
|
|
385
|
-
|
|
386
|
-
**Resolution Order:**
|
|
387
|
-
|
|
388
|
-
1. **Stack-specific override** (highest priority)
|
|
389
|
-
- `stacks/{stack}/.morph/framework/templates/code/dotnet/backend/service.cs`
|
|
390
|
-
|
|
391
|
-
2. **Project-local override** (medium priority)
|
|
392
|
-
- `.morph/framework/templates/code/dotnet/backend/service.cs`
|
|
393
|
-
|
|
394
|
-
3. **Framework template** (fallback)
|
|
395
|
-
- `framework/templates/code/dotnet/backend/service.cs`
|
|
396
|
-
|
|
397
|
-
**Use Cases for Overrides:**
|
|
398
|
-
|
|
399
|
-
| Scenario | Override Location | Example |
|
|
400
|
-
|----------|-------------------|---------|
|
|
401
|
-
| Stack-specific tweak | `stacks/{stack}/.morph/framework/templates/` | Stack uses different DI pattern |
|
|
402
|
-
| Project-wide customization | `.morph/framework/templates/` | Company-specific code style |
|
|
403
|
-
| Use framework default | None (no override) | Standard templates work fine |
|
|
404
|
-
|
|
405
|
-
**Example:**
|
|
406
|
-
|
|
407
|
-
```bash
|
|
408
|
-
# Create stack override
|
|
409
|
-
morph-spec template customize dotnet-backend-service --stack dotnet-nextjs
|
|
410
|
-
|
|
411
|
-
# Edit the override
|
|
412
|
-
code stacks/dotnet-nextjs/.morph/framework/templates/code/dotnet/backend/service.cs
|
|
413
|
-
|
|
414
|
-
# Now all renders for dotnet-nextjs will use the override
|
|
415
|
-
morph-spec template render dotnet-backend-service UserService.cs '...' --stack dotnet-nextjs
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
---
|
|
419
|
-
|
|
420
|
-
## 📊 Template Registry (REGISTRY.json)
|
|
421
|
-
|
|
422
|
-
The `REGISTRY.json` file is the **single source of truth** for all template metadata.
|
|
423
|
-
|
|
424
|
-
### Schema
|
|
425
|
-
|
|
426
|
-
```json
|
|
427
|
-
{
|
|
428
|
-
"version": "2.0.0",
|
|
429
|
-
"templates": [
|
|
430
|
-
{
|
|
431
|
-
"id": "dotnet-backend-service",
|
|
432
|
-
"name": ".NET Service Class",
|
|
433
|
-
"path": "code/dotnet/backend/service.cs",
|
|
434
|
-
"location": "framework",
|
|
435
|
-
"category": "code",
|
|
436
|
-
"phase": "implement",
|
|
437
|
-
"required": false,
|
|
438
|
-
"technology": "dotnet",
|
|
439
|
-
"applicableStacks": ["dotnet-nextjs", "nextjs-neon"],
|
|
440
|
-
"engine": "handlebars",
|
|
441
|
-
"placeholders": ["FEATURE_NAME", "NAMESPACE"],
|
|
442
|
-
"outputName": "{FeatureName}Service.cs",
|
|
443
|
-
"deprecated": false
|
|
444
|
-
}
|
|
445
|
-
]
|
|
446
|
-
}
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
### Discovery API
|
|
450
|
-
|
|
451
|
-
```javascript
|
|
452
|
-
import {
|
|
453
|
-
getAllTemplates,
|
|
454
|
-
getTemplateById,
|
|
455
|
-
getTemplatesByCategory,
|
|
456
|
-
getTemplatesByTechnology,
|
|
457
|
-
resolveTemplatePathById
|
|
458
|
-
} from '../src/lib/template-registry.js';
|
|
459
|
-
|
|
460
|
-
// Get all templates
|
|
461
|
-
const all = getAllTemplates();
|
|
462
|
-
|
|
463
|
-
// Get specific template
|
|
464
|
-
const service = getTemplateById('dotnet-backend-service');
|
|
465
|
-
|
|
466
|
-
// Filter by category
|
|
467
|
-
const codeTemplates = getTemplatesByCategory('code');
|
|
468
|
-
|
|
469
|
-
// Filter by technology
|
|
470
|
-
const dotnetTemplates = getTemplatesByTechnology('dotnet');
|
|
471
|
-
|
|
472
|
-
// Resolve path with fallback
|
|
473
|
-
const path = resolveTemplatePathById('dotnet-backend-service', '/project', 'dotnet-nextjs');
|
|
474
|
-
// Returns: framework/templates/code/dotnet/backend/service.cs
|
|
475
|
-
// Or: stacks/dotnet-nextjs/.morph/framework/templates/code/dotnet/backend/service.cs (if override exists)
|
|
476
|
-
```
|
|
477
|
-
|
|
478
81
|
---
|
|
479
82
|
|
|
480
|
-
##
|
|
83
|
+
## 🔗 How templates are consumed
|
|
481
84
|
|
|
482
|
-
|
|
85
|
+
Templates are **resolved from the installed npm package**, not copied into projects.
|
|
86
|
+
`morph-spec setup-infra` deliberately skips copying `framework/templates/` — a target project
|
|
87
|
+
gets the package's templates for free and stays current on every upgrade.
|
|
483
88
|
|
|
484
|
-
|
|
485
|
-
# For .NET templates
|
|
486
|
-
touch framework/templates/code/dotnet/backend/my-template.cs
|
|
487
|
-
|
|
488
|
-
# For infrastructure templates
|
|
489
|
-
touch framework/templates/infrastructure/docker/my-resource.dockerfile
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
### 2. Add Handlebars Placeholders
|
|
493
|
-
|
|
494
|
-
```handlebars
|
|
495
|
-
namespace {{pascalCase NAMESPACE}};
|
|
496
|
-
|
|
497
|
-
public class {{pascalCase FEATURE_NAME}}Service
|
|
498
|
-
{
|
|
499
|
-
// TODO: Implement {{titleCase FEATURE_NAME}} logic
|
|
500
|
-
}
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
### 3. Register in REGISTRY.json
|
|
504
|
-
|
|
505
|
-
```json
|
|
506
|
-
{
|
|
507
|
-
"id": "dotnet-backend-my-template",
|
|
508
|
-
"name": "My Custom Template",
|
|
509
|
-
"path": "code/dotnet/backend/my-template.cs",
|
|
510
|
-
"location": "framework",
|
|
511
|
-
"category": "code",
|
|
512
|
-
"phase": "implement",
|
|
513
|
-
"required": false,
|
|
514
|
-
"technology": "dotnet",
|
|
515
|
-
"applicableStacks": ["dotnet-nextjs"],
|
|
516
|
-
"engine": "handlebars",
|
|
517
|
-
"placeholders": ["FEATURE_NAME", "NAMESPACE"],
|
|
518
|
-
"outputName": "{FeatureName}MyTemplate.cs",
|
|
519
|
-
"deprecated": false
|
|
520
|
-
}
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
### 4. Test Rendering
|
|
89
|
+
**The consumer is the scaffolder**, which pairs a REGISTRY `id` with an output shape:
|
|
524
90
|
|
|
525
91
|
```bash
|
|
526
|
-
morph-spec
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
}'
|
|
530
|
-
```
|
|
531
|
-
|
|
532
|
-
---
|
|
533
|
-
|
|
534
|
-
## 📦 Migration Guide: v1 → v2
|
|
535
|
-
|
|
536
|
-
### What Changed
|
|
537
|
-
|
|
538
|
-
| Aspect | v1 (Stack-Based) | v2 (Technology-Based) |
|
|
539
|
-
|--------|------------------|----------------------|
|
|
540
|
-
| **Location** | `stacks/{stack}/.morph/framework/templates/` | `framework/templates/{technology}/` |
|
|
541
|
-
| **Organization** | By stack (dotnet-nextjs, nextjs-neon) | By technology (dotnet, docker) |
|
|
542
|
-
| **Duplication** | High (proposal.md × 2, decisions.md × 2) | Low (shared templates in framework) |
|
|
543
|
-
| **Template Engine** | 3 engines (custom, Handlebars, mixed) | 1 engine (Handlebars with helpers) |
|
|
544
|
-
| **Pre-computed Variables** | `{{pascalCase FEATURE_NAME}}` | `{{pascalCase FEATURE_NAME}}` (helpers) |
|
|
545
|
-
| **Registry** | None (hardcoded paths) | `REGISTRY.json` (centralized metadata) |
|
|
546
|
-
| **CLI** | None | `morph-spec template list/show/render/customize` |
|
|
547
|
-
| **Future Stacks** | Must copy templates | Reuse existing technology templates |
|
|
548
|
-
|
|
549
|
-
### Breaking Changes
|
|
550
|
-
|
|
551
|
-
**1. Template Paths**
|
|
552
|
-
|
|
553
|
-
```diff
|
|
554
|
-
- stacks/dotnet-nextjs/.morph/framework/templates/service.cs
|
|
555
|
-
+ framework/templates/code/dotnet/backend/service.cs
|
|
556
|
-
|
|
557
|
-
- stacks/nextjs-neon/.morph/framework/templates/dockerfile-web.dockerfile
|
|
558
|
-
+ framework/templates/infrastructure/docker/dockerfile-web.dockerfile
|
|
559
|
-
```
|
|
560
|
-
|
|
561
|
-
**2. Pre-computed Variables**
|
|
562
|
-
|
|
563
|
-
```diff
|
|
564
|
-
- {{pascalCase FEATURE_NAME}}
|
|
565
|
-
+ {{pascalCase FEATURE_NAME}}
|
|
566
|
-
|
|
567
|
-
- {{camelCase FEATURE_NAME}}
|
|
568
|
-
+ {{camelCase FEATURE_NAME}}
|
|
569
|
-
|
|
570
|
-
- {{snakeCase FEATURE_NAME}}
|
|
571
|
-
+ {{snakeCase FEATURE_NAME}}
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
**3. Template Resolution**
|
|
575
|
-
|
|
576
|
-
```javascript
|
|
577
|
-
// Old (v1)
|
|
578
|
-
const templatePath = join(resolveTemplatesDir(projectPath), 'service.cs');
|
|
579
|
-
|
|
580
|
-
// New (v2)
|
|
581
|
-
const templatePath = resolveTemplatePath(projectPath, 'code/dotnet/backend/service.cs', stackId);
|
|
582
|
-
// Automatically falls back: stack override → framework
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
### Migration Steps
|
|
586
|
-
|
|
587
|
-
**Step 1:** Update code to use `resolveTemplatePath()`
|
|
588
|
-
|
|
589
|
-
```javascript
|
|
590
|
-
import { resolveTemplatePath } from '../lib/stack-resolver.js';
|
|
591
|
-
|
|
592
|
-
// Before
|
|
593
|
-
const path = join(resolveTemplatesDir(process.cwd()), 'service.cs');
|
|
594
|
-
|
|
595
|
-
// After
|
|
596
|
-
const path = resolveTemplatePath(process.cwd(), 'code/dotnet/backend/service.cs', 'dotnet-nextjs');
|
|
92
|
+
morph-spec scaffold dotnet <feature> <slice-name> # VSA slice (endpoint, handler, request,
|
|
93
|
+
# response, validator, errors, entity)
|
|
94
|
+
morph-spec scaffold nextjs <feature> # page, client component, form, hook
|
|
597
95
|
```
|
|
598
96
|
|
|
599
|
-
**
|
|
97
|
+
There is **no `morph-spec template` command** — the v4 CLI surface (`list`/`show`/`render`/
|
|
98
|
+
`customize`) was removed in the v4→v5 migration. To use a template by hand, read the file and
|
|
99
|
+
follow its pattern, or call `renderTemplate()` from the renderer module.
|
|
600
100
|
|
|
601
|
-
|
|
602
|
-
<!-- Before -->
|
|
603
|
-
namespace {{NAMESPACE}};
|
|
604
|
-
public class {{pascalCase FEATURE_NAME}}Service { }
|
|
605
|
-
|
|
606
|
-
<!-- After -->
|
|
607
|
-
namespace {{pascalCase NAMESPACE}};
|
|
608
|
-
public class {{pascalCase FEATURE_NAME}}Service { }
|
|
609
|
-
```
|
|
610
|
-
|
|
611
|
-
**Step 3:** Use CLI commands instead of hardcoded paths
|
|
612
|
-
|
|
613
|
-
```bash
|
|
614
|
-
# Before (hardcoded)
|
|
615
|
-
cp framework/templates/proposal.md .morph/features/user-auth/proposal.md
|
|
101
|
+
### Local override
|
|
616
102
|
|
|
617
|
-
|
|
618
|
-
morph
|
|
619
|
-
|
|
620
|
-
}'
|
|
621
|
-
```
|
|
103
|
+
A project may shadow any packaged template by placing a file at the same relative path under
|
|
104
|
+
`.morph/framework/templates/{path}`. The local copy wins, and **stops receiving framework
|
|
105
|
+
updates** — only override when the packaged template genuinely does not fit.
|
|
622
106
|
|
|
623
107
|
---
|
|
624
108
|
|
|
625
|
-
##
|
|
626
|
-
|
|
627
|
-
### 1. **Use Helpers, Not Pre-computed Variables**
|
|
628
|
-
|
|
629
|
-
```handlebars
|
|
630
|
-
✅ GOOD:
|
|
631
|
-
{{pascalCase FEATURE_NAME}}Service
|
|
632
|
-
|
|
633
|
-
❌ BAD:
|
|
634
|
-
{{pascalCase FEATURE_NAME}}Service // Deprecated, won't work
|
|
635
|
-
```
|
|
636
|
-
|
|
637
|
-
### 2. **Keep Templates Technology-Agnostic**
|
|
638
|
-
|
|
639
|
-
```handlebars
|
|
640
|
-
✅ GOOD: code/dotnet/backend/service.cs
|
|
641
|
-
Works for: dotnet-nextjs, nextjs-neon, future-dotnet-stack
|
|
642
|
-
|
|
643
|
-
❌ BAD: code/stack-specific/service.cs
|
|
644
|
-
Locked to one stack only
|
|
645
|
-
```
|
|
646
|
-
|
|
647
|
-
### 3. **Use Stack Conditionals for Differences**
|
|
648
|
-
|
|
649
|
-
```handlebars
|
|
650
|
-
{{#if (eq STACK "nextjs-neon")}}
|
|
651
|
-
// Neon-specific implementation
|
|
652
|
-
{{else}}
|
|
653
|
-
// Generic implementation
|
|
654
|
-
{{/if}}
|
|
655
|
-
```
|
|
656
|
-
|
|
657
|
-
### 4. **Document Applicability in Registry**
|
|
658
|
-
|
|
659
|
-
```json
|
|
660
|
-
{
|
|
661
|
-
"technology": "dotnet",
|
|
662
|
-
"applicableStacks": ["dotnet-nextjs", "nextjs-neon"],
|
|
663
|
-
"description": "Works with any .NET backend stack"
|
|
664
|
-
}
|
|
665
|
-
```
|
|
109
|
+
## ➕ Adding a template
|
|
666
110
|
|
|
667
|
-
|
|
111
|
+
1. Create the file under the right technology directory:
|
|
112
|
+
`framework/templates/code/{technology}/...` (Handlebars syntax, `.hbs` when the extension
|
|
113
|
+
would otherwise confuse tooling).
|
|
114
|
+
2. Register it in `REGISTRY.json` with a unique `id`, its `path`, `category`, `technology`
|
|
115
|
+
and applicable stacks.
|
|
116
|
+
3. If a generator should emit it, add the `id` to the relevant list in
|
|
117
|
+
`src/lib/generators/scaffold-generator.js`.
|
|
668
118
|
|
|
669
|
-
|
|
670
|
-
# Only customize if framework template doesn't meet needs
|
|
671
|
-
morph-spec template customize dotnet-backend-service --stack dotnet-nextjs
|
|
119
|
+
### Deprecating a template
|
|
672
120
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
```
|
|
121
|
+
Add `"deprecated": true` plus `"deprecationReason"` to its `REGISTRY.json` entry. Keep the
|
|
122
|
+
file until a major version bump.
|
|
676
123
|
|
|
677
124
|
---
|
|
678
125
|
|
|
679
126
|
## 📚 References
|
|
680
127
|
|
|
681
|
-
- **
|
|
682
|
-
- **
|
|
683
|
-
- **
|
|
684
|
-
- **
|
|
685
|
-
- **Handlebars
|
|
686
|
-
|
|
687
|
-
---
|
|
688
|
-
|
|
689
|
-
## 🤝 Contributing
|
|
690
|
-
|
|
691
|
-
### Adding New Technology
|
|
692
|
-
|
|
693
|
-
1. Create directory: `framework/templates/code/{technology}/`
|
|
694
|
-
2. Add templates with Handlebars syntax
|
|
695
|
-
3. Register in `REGISTRY.json` with `"technology": "{technology}"`
|
|
696
|
-
4. Test with `morph-spec template render`
|
|
697
|
-
5. Document in this README
|
|
698
|
-
|
|
699
|
-
### Deprecating Templates
|
|
700
|
-
|
|
701
|
-
1. Add `"deprecated": true` to `REGISTRY.json`
|
|
702
|
-
2. Add `"deprecationReason": "Reason here"`
|
|
703
|
-
3. Keep file for backward compatibility or remove after major version bump
|
|
128
|
+
- **Registry API:** [`src/core/templates/template-registry.js`](../../src/core/templates/template-registry.js)
|
|
129
|
+
- **Renderer + helpers:** [`src/core/templates/template-renderer.js`](../../src/core/templates/template-renderer.js)
|
|
130
|
+
- **Scaffold generator:** [`src/lib/generators/scaffold-generator.js`](../../src/lib/generators/scaffold-generator.js)
|
|
131
|
+
- **Stack filtering:** [`src/lib/stack-filter.js`](../../src/lib/stack-filter.js)
|
|
132
|
+
- **Handlebars docs:** https://handlebarsjs.com/
|
|
704
133
|
|
|
705
134
|
---
|
|
706
135
|
|
|
707
|
-
**MORPH-SPEC Template System
|
|
136
|
+
**MORPH-SPEC Template System** — technology-based, registry-indexed, resolved from the package.
|