@polymorphism-tech/morph-spec 6.0.0 → 7.1.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 +367 -366
- package/docs/CHEATSHEET.md +3 -3
- package/docs/QUICKSTART.md +4 -4
- package/framework/CLAUDE.md +13 -0
- package/framework/MORPH.md +57 -36
- package/framework/agents.json +132 -127
- package/framework/commands/morph-apply.md +4 -21
- package/framework/commands/morph-archive.md +1 -1
- package/framework/commands/morph-proposal.md +2 -0
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +1 -1
- package/framework/hooks/claude-code/core/protect-spec-files.js +1 -1
- package/framework/hooks/claude-code/core/state-sync.js +212 -0
- package/framework/hooks/shared/phase-utils.js +5 -4
- package/framework/hooks/shared/state-reader.js +1 -1
- package/framework/phases.json +1 -1
- package/framework/rubrics/backend.json +41 -36
- package/framework/rules/ai-agents-standards.md +45 -15
- package/framework/rules/morph-workflow.md +15 -34
- package/framework/rules/neon-standards.md +25 -26
- package/framework/schemas/tasks.schema.json +35 -0
- package/framework/skills/core/morph-apply/SKILL.md +154 -0
- package/framework/skills/core/morph-brainstorming/SKILL.md +53 -30
- package/framework/skills/core/morph-implement/SKILL.md +25 -14
- package/framework/skills/core/morph-plan/SKILL.md +91 -23
- package/framework/skills/core/morph-proposal/SKILL.md +137 -0
- package/framework/skills/core/morph-review/SKILL.md +43 -7
- package/framework/skills/core/morph-stack-scan/SKILL.md +40 -1
- package/framework/standards/STANDARDS.json +1729 -1134
- package/framework/standards/ai-agents/agent-session.md +160 -0
- package/framework/standards/ai-agents/batching.md +193 -0
- package/framework/standards/ai-agents/context-providers.md +188 -0
- package/framework/standards/ai-agents/mcp-server.md +250 -0
- package/framework/standards/ai-agents/mcp-tools.md +197 -0
- package/framework/standards/ai-agents/middleware-patterns.md +207 -0
- package/framework/standards/ai-agents/modalities-image-gen.md +173 -0
- package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -0
- package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -0
- package/framework/standards/ai-agents/modalities-vision.md +201 -0
- package/framework/standards/ai-agents/multi-agent-patterns.md +155 -0
- package/framework/standards/ai-agents/production.md +114 -258
- package/framework/standards/ai-agents/prompts-in-db.md +214 -0
- package/framework/standards/ai-agents/providers/model-registry.md +158 -0
- package/framework/standards/ai-agents/rag-custom-pgvector.md +243 -0
- package/framework/standards/ai-agents/service-tier-flex.md +135 -0
- package/framework/standards/ai-agents/setup.md +142 -316
- package/framework/standards/ai-agents/structured-output.md +249 -0
- package/framework/standards/ai-agents/sweet-spot.md +109 -0
- package/framework/standards/ai-agents/workflows.md +114 -284
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +433 -429
- package/framework/standards/backend/api/validation.md +92 -88
- package/framework/standards/backend/database/postgresql/database.md +336 -332
- package/framework/standards/backend/database/vector-search-rag.md +599 -541
- package/framework/standards/backend/dotnet/fluent-validation-vsa.md +4 -0
- package/framework/standards/backend/dotnet/result-pattern.md +4 -0
- package/framework/standards/backend/dotnet/scalar-ui.md +176 -172
- package/framework/standards/backend/dotnet/scrutor.md +4 -0
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +278 -274
- package/framework/standards/backend/integrations/asaas/asaas-api.md +260 -216
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +354 -350
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +30 -19
- package/framework/standards/backend/integrations/resend/resend-email.md +400 -385
- package/framework/standards/data/nosql/cache/redis.md +106 -97
- package/framework/standards/data/nosql/cosmos-db.md +127 -118
- package/framework/standards/data/vector-search/rag-chunking.md +113 -104
- package/framework/standards/frontend/design-system/ai-image-generation.md +4 -0
- package/framework/standards/frontend/design-system/animations.md +164 -160
- package/framework/standards/frontend/design-system/naming.md +68 -64
- package/framework/standards/frontend/gsap/gsap-core.md +14 -8
- package/framework/standards/frontend/gsap/gsap-plugins.md +26 -21
- package/framework/standards/frontend/gsap/gsap-scrolltrigger.md +9 -7
- package/framework/standards/frontend/nextjs/app-router.md +127 -123
- package/framework/standards/frontend/nextjs/components.md +136 -132
- package/framework/standards/frontend/nextjs/data-fetching.md +130 -126
- package/framework/standards/frontend/nextjs/forms.md +134 -128
- package/framework/standards/frontend/nextjs/motion-patterns.md +328 -322
- package/framework/standards/frontend/nextjs/naming-conventions.md +71 -67
- package/framework/standards/frontend/nextjs/nextjs-patterns.md +229 -225
- package/framework/standards/frontend/nextjs/project-structure.md +106 -102
- package/framework/standards/frontend/nextjs/state-management.md +76 -72
- package/framework/standards/frontend/nextjs/testing.md +115 -111
- package/framework/standards/infrastructure/docker/easypanel-deploy.md +196 -192
- package/framework/standards/infrastructure/neon/neon-pgrag.md +6 -2
- package/framework/standards/infrastructure/neon/neon-pgvector.md +4 -0
- package/framework/standards/infrastructure/neon/neon-setup.md +5 -1
- package/framework/standards/integration/mcp/mcp-tools.md +471 -462
- package/framework/templates/REGISTRY.json +1745 -1239
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -0
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -0
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -0
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -0
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -0
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -0
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -0
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -0
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -0
- package/framework/templates/docs/onboarding.md +4 -4
- package/framework/templates/tasks.template.json +17 -0
- package/package.json +89 -83
- package/src/commands/project/init.js +16 -0
- package/src/commands/validation/validate-feature.js +1 -1
- package/src/core/paths/output-schema.js +6 -6
- package/src/core/state/state-manager.js +1 -1
- package/src/core/templates/template-registry.js +289 -256
- package/src/lib/detectors/design-system-detector.js +1 -1
- package/src/lib/generators/context-generator.js +516 -518
- package/src/lib/generators/scaffold-generator.js +23 -26
- package/src/lib/installers/memory-installer.js +138 -0
- package/src/lib/phase-chain/phase-validator.js +4 -4
- package/src/lib/risk-detector.js +80 -0
- package/src/lib/tasks/tasks-json-parser.js +185 -0
- package/src/scripts/setup-infra.js +25 -0
- package/src/utils/hooks-installer.js +59 -10
- package/framework/standards/infrastructure/neon/neon-semantic-kernel.md +0 -310
- package/framework/templates/integrations/semantic-kernel-neon.cs +0 -125
|
@@ -1,256 +1,289 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template Registry - Discovery and metadata API for all framework templates.
|
|
3
|
-
*
|
|
4
|
-
* Provides programmatic access to REGISTRY.json for:
|
|
5
|
-
* - Template discovery by category, phase, stack
|
|
6
|
-
* - Template metadata lookup
|
|
7
|
-
* - Path resolution with fallback logic
|
|
8
|
-
* - Validation of template existence
|
|
9
|
-
*
|
|
10
|
-
* @module template-registry
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { readFileSync, existsSync } from 'fs';
|
|
14
|
-
import { join, dirname } from 'path';
|
|
15
|
-
import { fileURLToPath } from 'url';
|
|
16
|
-
|
|
17
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
-
const __dirname = dirname(__filename);
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
*
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
*
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
*
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
*
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
*
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
*
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
*
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
*
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
*
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
*
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Template Registry - Discovery and metadata API for all framework templates.
|
|
3
|
+
*
|
|
4
|
+
* Provides programmatic access to REGISTRY.json for:
|
|
5
|
+
* - Template discovery by category, phase, stack
|
|
6
|
+
* - Template metadata lookup
|
|
7
|
+
* - Path resolution with fallback logic
|
|
8
|
+
* - Validation of template existence
|
|
9
|
+
*
|
|
10
|
+
* @module template-registry
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { readFileSync, existsSync } from 'fs';
|
|
14
|
+
import { join, dirname } from 'path';
|
|
15
|
+
import { fileURLToPath } from 'url';
|
|
16
|
+
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = dirname(__filename);
|
|
19
|
+
|
|
20
|
+
// Parsed registries cached by resolved REGISTRY.json path. Keyed on the path —
|
|
21
|
+
// NOT a single global slot — so a process that loads the framework registry
|
|
22
|
+
// and a project's registry gets each one correctly, instead of whichever
|
|
23
|
+
// loaded first.
|
|
24
|
+
const _registryCache = new Map();
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Load the template registry from REGISTRY.json.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} [projectPath] - Optional project path (defaults to framework root)
|
|
30
|
+
* @returns {Object} Parsed registry object
|
|
31
|
+
* @throws {Error} If registry file not found or invalid JSON
|
|
32
|
+
*/
|
|
33
|
+
export function loadTemplateRegistry(projectPath = null) {
|
|
34
|
+
// Resolve the path first — it is the cache key.
|
|
35
|
+
const registryPath = projectPath
|
|
36
|
+
? join(projectPath, 'framework/templates/REGISTRY.json')
|
|
37
|
+
: join(__dirname, '../../../framework/templates/REGISTRY.json');
|
|
38
|
+
|
|
39
|
+
const cached = _registryCache.get(registryPath);
|
|
40
|
+
if (cached) {
|
|
41
|
+
return cached;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!existsSync(registryPath)) {
|
|
45
|
+
throw new Error(`Template registry not found: ${registryPath}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const content = readFileSync(registryPath, 'utf-8');
|
|
50
|
+
const parsed = JSON.parse(content);
|
|
51
|
+
_registryCache.set(registryPath, parsed);
|
|
52
|
+
return parsed;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
throw new Error(`Failed to parse template registry: ${error.message}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Clear all cached registries (useful for testing).
|
|
60
|
+
*/
|
|
61
|
+
export function clearRegistryCache() {
|
|
62
|
+
_registryCache.clear();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get all templates from registry.
|
|
67
|
+
*
|
|
68
|
+
* @param {string} [projectPath] - Optional project path
|
|
69
|
+
* @returns {Array} Array of template objects
|
|
70
|
+
*/
|
|
71
|
+
export function getAllTemplates(projectPath = null) {
|
|
72
|
+
const registry = loadTemplateRegistry(projectPath);
|
|
73
|
+
return registry.templates || [];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get template by ID.
|
|
78
|
+
*
|
|
79
|
+
* @param {string} templateId - Template identifier (e.g., 'proposal', 'spec')
|
|
80
|
+
* @param {string} [projectPath] - Optional project path
|
|
81
|
+
* @returns {Object|null} Template object or null if not found
|
|
82
|
+
*/
|
|
83
|
+
export function getTemplateById(templateId, projectPath = null) {
|
|
84
|
+
const templates = getAllTemplates(projectPath);
|
|
85
|
+
return templates.find(t => t.id === templateId) || null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get templates by category.
|
|
90
|
+
*
|
|
91
|
+
* @param {string} category - Category ('documentation', 'code', 'infrastructure', 'context', 'examples')
|
|
92
|
+
* @param {string} [projectPath] - Optional project path
|
|
93
|
+
* @returns {Array} Filtered template array
|
|
94
|
+
*/
|
|
95
|
+
export function getTemplatesByCategory(category, projectPath = null) {
|
|
96
|
+
const templates = getAllTemplates(projectPath);
|
|
97
|
+
return templates.filter(t => t.category === category);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get templates by phase.
|
|
102
|
+
*
|
|
103
|
+
* @param {string} phase - Phase name (e.g., 'proposal', 'design', 'implement')
|
|
104
|
+
* @param {string} [projectPath] - Optional project path
|
|
105
|
+
* @returns {Array} Filtered template array
|
|
106
|
+
*/
|
|
107
|
+
export function getTemplatesByPhase(phase, projectPath = null) {
|
|
108
|
+
const templates = getAllTemplates(projectPath);
|
|
109
|
+
return templates.filter(t => t.phase === phase);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get templates by stack.
|
|
114
|
+
*
|
|
115
|
+
* @param {string} stackId - Stack identifier ('blazor-azure', 'nextjs-neon')
|
|
116
|
+
* @param {string} [projectPath] - Optional project path
|
|
117
|
+
* @returns {Array} Filtered template array (includes universal + stack-specific)
|
|
118
|
+
*/
|
|
119
|
+
export function getTemplatesByStack(stackId, projectPath = null) {
|
|
120
|
+
const templates = getAllTemplates(projectPath);
|
|
121
|
+
return templates.filter(t => {
|
|
122
|
+
// Include universal templates (stackSpecific: false)
|
|
123
|
+
if (!t.stackSpecific) return true;
|
|
124
|
+
|
|
125
|
+
// Include stack-specific if matches
|
|
126
|
+
return t.stacks && t.stacks.includes(stackId);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get required templates only.
|
|
132
|
+
*
|
|
133
|
+
* @param {string} [stackId] - Optional stack filter
|
|
134
|
+
* @param {string} [projectPath] - Optional project path
|
|
135
|
+
* @returns {Array} Filtered template array
|
|
136
|
+
*/
|
|
137
|
+
export function getRequiredTemplates(stackId = null, projectPath = null) {
|
|
138
|
+
let templates = getAllTemplates(projectPath);
|
|
139
|
+
|
|
140
|
+
// Filter by required flag
|
|
141
|
+
templates = templates.filter(t => t.required === true);
|
|
142
|
+
|
|
143
|
+
// Optional: filter by stack
|
|
144
|
+
if (stackId) {
|
|
145
|
+
templates = templates.filter(t => {
|
|
146
|
+
if (!t.stackSpecific) return true;
|
|
147
|
+
return t.stacks && t.stacks.includes(stackId);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return templates;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get infrastructure templates for a specific stack.
|
|
156
|
+
*
|
|
157
|
+
* @param {string} stackId - Stack identifier
|
|
158
|
+
* @param {string} [projectPath] - Optional project path
|
|
159
|
+
* @returns {Array} Infrastructure template array
|
|
160
|
+
*/
|
|
161
|
+
export function getInfrastructureTemplates(stackId, projectPath = null) {
|
|
162
|
+
const registry = loadTemplateRegistry(projectPath);
|
|
163
|
+
return registry.infrastructureTemplates?.[stackId] || [];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get contract templates for a specific stack.
|
|
168
|
+
*
|
|
169
|
+
* @param {string} stackId - Stack identifier
|
|
170
|
+
* @param {string} [projectPath] - Optional project path
|
|
171
|
+
* @returns {Array} Contract template array
|
|
172
|
+
*/
|
|
173
|
+
export function getContractTemplates(stackId, projectPath = null) {
|
|
174
|
+
const registry = loadTemplateRegistry(projectPath);
|
|
175
|
+
return registry.contractTemplates?.[stackId] || [];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get integration templates for a specific stack.
|
|
180
|
+
*
|
|
181
|
+
* @param {string} stackId - Stack identifier
|
|
182
|
+
* @param {string} [projectPath] - Optional project path
|
|
183
|
+
* @returns {Array} Integration template array
|
|
184
|
+
*/
|
|
185
|
+
export function getIntegrationTemplates(stackId, projectPath = null) {
|
|
186
|
+
const registry = loadTemplateRegistry(projectPath);
|
|
187
|
+
return registry.integrationTemplates?.[stackId] || [];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get SaaS templates for a specific stack.
|
|
192
|
+
*
|
|
193
|
+
* @param {string} stackId - Stack identifier
|
|
194
|
+
* @param {string} [projectPath] - Optional project path
|
|
195
|
+
* @returns {Array} SaaS template array
|
|
196
|
+
*/
|
|
197
|
+
export function getSaasTemplates(stackId, projectPath = null) {
|
|
198
|
+
const registry = loadTemplateRegistry(projectPath);
|
|
199
|
+
return registry.saasTemplates?.[stackId] || [];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get example templates.
|
|
204
|
+
*
|
|
205
|
+
* @param {string} [projectPath] - Optional project path
|
|
206
|
+
* @returns {Array} Example template array
|
|
207
|
+
*/
|
|
208
|
+
export function getExampleTemplates(projectPath = null) {
|
|
209
|
+
const registry = loadTemplateRegistry(projectPath);
|
|
210
|
+
return registry.exampleTemplates || [];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Search templates by keyword (searches id, name, description).
|
|
215
|
+
*
|
|
216
|
+
* @param {string} keyword - Search term
|
|
217
|
+
* @param {string} [projectPath] - Optional project path
|
|
218
|
+
* @returns {Array} Matching templates
|
|
219
|
+
*/
|
|
220
|
+
export function searchTemplates(keyword, projectPath = null) {
|
|
221
|
+
const templates = getAllTemplates(projectPath);
|
|
222
|
+
const lowerKeyword = keyword.toLowerCase();
|
|
223
|
+
|
|
224
|
+
return templates.filter(t => {
|
|
225
|
+
return (
|
|
226
|
+
t.id.toLowerCase().includes(lowerKeyword) ||
|
|
227
|
+
t.name.toLowerCase().includes(lowerKeyword) ||
|
|
228
|
+
(t.description && t.description.toLowerCase().includes(lowerKeyword))
|
|
229
|
+
);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Get template absolute path with fallback logic.
|
|
235
|
+
*
|
|
236
|
+
* Resolution order:
|
|
237
|
+
* 1. Project-local override (.morph/framework/templates/{path})
|
|
238
|
+
* 2. Framework universal template (framework/templates/{path})
|
|
239
|
+
*
|
|
240
|
+
* @param {string} templateId - Template identifier
|
|
241
|
+
* @param {string} projectPath - Project root path
|
|
242
|
+
* @returns {string|null} Absolute template path or null if not found
|
|
243
|
+
*/
|
|
244
|
+
export function resolveTemplatePathById(templateId, projectPath) {
|
|
245
|
+
const template = getTemplateById(templateId, projectPath);
|
|
246
|
+
if (!template) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// 1. Project-local override
|
|
251
|
+
const projectLocalPath = join(projectPath, '.morph/framework/templates', template.path);
|
|
252
|
+
if (existsSync(projectLocalPath)) {
|
|
253
|
+
return projectLocalPath;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// 2. Framework universal template
|
|
257
|
+
const frameworkPath = join(projectPath, 'framework/templates', template.path);
|
|
258
|
+
return existsSync(frameworkPath) ? frameworkPath : null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Absolute path of this repo's own framework/templates/ directory. */
|
|
262
|
+
const FRAMEWORK_TEMPLATES_DIR = join(__dirname, '../../../framework/templates');
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Resolve a template id to its absolute path inside THIS framework repo's
|
|
266
|
+
* `framework/templates/` directory — for generators running against the
|
|
267
|
+
* framework itself (scaffold-generator, context-generator), not against an
|
|
268
|
+
* installed `.morph/`. Use resolveTemplatePathById() when a project root and
|
|
269
|
+
* its project-local overrides are in play.
|
|
270
|
+
*
|
|
271
|
+
* @param {string} templateId - Template identifier (REGISTRY.json `id`)
|
|
272
|
+
* @returns {string} Absolute path to the template file
|
|
273
|
+
* @throws {Error} If the id is not in REGISTRY.json or the file is missing —
|
|
274
|
+
* a missing template is a framework bug, not a recoverable condition.
|
|
275
|
+
*/
|
|
276
|
+
export function resolveFrameworkTemplatePath(templateId) {
|
|
277
|
+
const template = getTemplateById(templateId);
|
|
278
|
+
if (!template) {
|
|
279
|
+
throw new Error(`Template id "${templateId}" not found in REGISTRY.json`);
|
|
280
|
+
}
|
|
281
|
+
const abs = join(FRAMEWORK_TEMPLATES_DIR, template.path);
|
|
282
|
+
if (!existsSync(abs)) {
|
|
283
|
+
throw new Error(
|
|
284
|
+
`Template "${templateId}" path "${template.path}" does not resolve to a file`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
return abs;
|
|
288
|
+
}
|
|
289
|
+
|