@polymorphism-tech/morph-spec 8.28.0 → 8.31.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.md +6 -1
- package/README.md +13 -2
- package/bin/morph-spec.js +48 -0
- package/bin/validate.js +379 -379
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +2 -0
- package/framework/MORPH.md +2 -1
- package/framework/agents.json +20 -9
- package/framework/commands/create-umbraco-lp.md +1 -1
- package/framework/commands/morph-status.md +1 -1
- package/framework/docs/CLI.md +18 -0
- package/framework/evals/rubrics/code-quality.md +17 -0
- package/framework/evals/rubrics/condensed.md +3 -1
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
- package/framework/hooks/git/pre-commit/agents.sh +25 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
- package/framework/hooks/git/pre-commit/specs.sh +50 -50
- package/framework/hooks/git/pre-push/run-tests.sh +44 -44
- package/framework/hooks/shared/phase-utils.js +2 -0
- package/framework/hooks/shared/stdin-reader.js +26 -26
- package/framework/mcp/registry.json +97 -11
- package/framework/rubrics/backend.json +2 -2
- package/framework/rubrics/default.json +2 -2
- package/framework/rubrics/frontend.json +2 -2
- package/framework/schemas/tasks.schema.json +25 -0
- package/framework/skills/core/create-umbraco-lp/SKILL.md +13 -2
- package/framework/skills/core/create-umbraco-lp/references/umbraco-lp-patterns.md +598 -0
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +92 -7
- package/framework/skills/core/morph-apply/SKILL.md +12 -3
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
- package/framework/skills/core/morph-implement/SKILL.md +12 -2
- package/framework/skills/core/morph-plan/SKILL.md +45 -3
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +11 -3
- package/framework/skills/core/morph-stack-scan/SKILL.md +13 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +1 -1
- package/framework/standards/PROVENANCE.json +41 -0
- package/framework/standards/STANDARDS.json +218 -7
- package/framework/standards/ai-agents/agent-session.md +160 -160
- package/framework/standards/ai-agents/batching.md +193 -193
- package/framework/standards/ai-agents/context-providers.md +188 -188
- package/framework/standards/ai-agents/mcp-server.md +250 -250
- package/framework/standards/ai-agents/mcp-tools.md +197 -197
- package/framework/standards/ai-agents/middleware-patterns.md +207 -207
- package/framework/standards/ai-agents/modalities-image-gen.md +173 -173
- package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -171
- package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -181
- package/framework/standards/ai-agents/modalities-vision.md +201 -201
- package/framework/standards/ai-agents/multi-agent-patterns.md +155 -155
- package/framework/standards/ai-agents/production.md +231 -231
- package/framework/standards/ai-agents/prompts-in-db.md +214 -214
- package/framework/standards/ai-agents/providers/model-registry.md +158 -158
- package/framework/standards/ai-agents/service-tier-flex.md +135 -135
- package/framework/standards/ai-agents/structured-output.md +249 -249
- package/framework/standards/ai-agents/sweet-spot.md +109 -109
- package/framework/standards/frontend/design-system/ai-image-generation.md +43 -0
- package/framework/standards/frontend/design-system/ai-video-generation.md +113 -0
- package/framework/standards/frontend/design-system/premium-finish.md +160 -0
- package/framework/standards/frontend/gsap/gsap-core.md +1 -1
- package/framework/standards/frontend/gsap/gsap-plugins.md +1 -1
- package/framework/standards/frontend/gsap/gsap-scrolltrigger.md +3 -1
- package/framework/standards/frontend/nextjs/motion-patterns.md +3 -1
- package/framework/standards/frontend/scroll-driven/frame-scrub.md +287 -0
- package/framework/standards/frontend/scroll-driven/scroll-components.md +256 -0
- package/framework/standards/frontend/scroll-driven/smooth-scroll.md +148 -0
- package/framework/standards/frontend/seo/meta-schema-audit.md +134 -0
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
- package/framework/templates/code/dotnet/database/migration.cs +83 -83
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/code/typescript/contracts.ts +167 -167
- package/framework/templates/context/ARCHITECTURE.md +20 -0
- package/framework/templates/docs/clarifications.md +253 -253
- package/framework/templates/docs/schema-analysis.md +119 -119
- package/framework/templates/docs/ui-components.md +52 -52
- package/framework/templates/docs/ui-design-system.md +76 -76
- package/framework/templates/docs/ui-flows.md +167 -167
- package/framework/templates/docs/ui-mockups.md +98 -98
- package/framework/templates/feature/decisions.md +122 -122
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/integrations/asaas-client.cs +387 -387
- package/framework/templates/integrations/asaas-webhook.cs +351 -351
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
- package/framework/templates/project/validate.js +120 -120
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +1 -1
- package/src/commands/dashboard/dashboard.js +53 -53
- package/src/commands/graph/affected.js +89 -0
- package/src/commands/graph/build.js +94 -0
- package/src/commands/graph/explain.js +96 -0
- package/src/commands/graph/status.js +93 -0
- package/src/commands/index.js +13 -13
- package/src/commands/project/checkpoint.js +209 -209
- package/src/commands/project/doctor.js +11 -0
- package/src/core/paths/output-schema.js +7 -0
- package/src/core/state/phase-state-machine.js +197 -197
- package/src/core/templates/template-renderer.js +477 -477
- package/src/dashboard/app/components/DetailPanel.jsx +11 -11
- package/src/dashboard/app/components/FlowGraph.jsx +17 -17
- package/src/dashboard/app/components/SearchFilter.jsx +22 -22
- package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/AgentMap.jsx +370 -370
- package/src/dashboard/app/pages/HookInspector.jsx +418 -418
- package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
- package/src/dashboard/app/pages/RulesMap.jsx +273 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
- package/src/dashboard/app/pages/Standards.jsx +332 -332
- package/src/dashboard/app/styles/index.css +47 -47
- package/src/dashboard/index.html +12 -12
- package/src/dashboard/package.json +24 -24
- package/src/dashboard/scanner/phases-scanner.js +15 -15
- package/src/dashboard/scanner/rubrics-scanner.js +12 -12
- package/src/dashboard/scanner/rules-scanner.js +37 -37
- package/src/dashboard/scanner/skills-scanner.js +43 -43
- package/src/dashboard/scanner/squads-scanner.js +13 -13
- package/src/dashboard/vite-plugin-morph-data.js +17 -17
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/detectors/design-system-detector.js +188 -188
- package/src/lib/detectors/index.js +217 -217
- package/src/lib/generators/architecture-scanner.js +45 -4
- package/src/lib/generators/design-system-generator.js +508 -508
- package/src/lib/generators/settings-generator.js +209 -209
- package/src/lib/graph/god-nodes.js +75 -0
- package/src/lib/graph/graph-health.js +61 -0
- package/src/lib/graph/graph-loader.js +86 -0
- package/src/lib/graph/graphify-runtime.js +100 -0
- package/src/lib/graph/graphifyignore.js +86 -0
- package/src/lib/graph/sanitize.js +280 -0
- package/src/lib/hooks/hook-executor.js +167 -167
- package/src/lib/installers/mcp-installer.js +719 -719
- package/src/lib/tasks/task-classifier.js +30 -30
- package/src/lib/tasks/task-parser.js +209 -209
- package/src/lib/tasks/tasks-json-parser.js +26 -0
- package/src/lib/validators/checkpoint-persistence.js +39 -39
- package/src/lib/validators/css/css-validator.js +340 -340
- package/src/lib/validators/reuse/index.js +10 -0
- package/src/lib/validators/reuse/reuse-validator.js +324 -0
- package/src/lib/validators/reuse/symbol-scanner.js +144 -0
- package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
- package/src/lib/validators/shared/issue-counter.js +14 -14
- package/src/lib/validators/validation-runner.js +40 -1
- package/src/scripts/setup-infra.js +60 -1
- package/src/utils/agent-id-mapper.js +11 -11
- package/src/utils/claude-settings-manager.js +300 -300
- package/src/utils/file-copier.js +40 -0
- package/src/utils/skills-installer.js +187 -187
|
@@ -1,188 +1,188 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Design System Detector
|
|
3
|
-
*
|
|
4
|
-
* Detects if a design system exists for the project or feature.
|
|
5
|
-
* Checks multiple locations in priority order.
|
|
6
|
-
*
|
|
7
|
-
* @module design-system-detector
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
11
|
-
import { join } from 'path';
|
|
12
|
-
|
|
13
|
-
const SKIP_DIRS = new Set(['node_modules', '.git', '.morph', 'dist', 'build', '.next', 'obj', 'bin', '.vs']);
|
|
14
|
-
const CSS_SCAN_MAX_DEPTH = 6;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Recursively walk a directory collecting files with given extensions.
|
|
18
|
-
* @param {string} dir - Directory to walk
|
|
19
|
-
* @param {string[]} extensions - File extensions to collect (e.g. ['.css', '.scss'])
|
|
20
|
-
* @param {string[]} files - Accumulator array (mutated in-place)
|
|
21
|
-
* @param {number} depth - Current recursion depth
|
|
22
|
-
* @param {number} maxDepth - Maximum recursion depth
|
|
23
|
-
*/
|
|
24
|
-
function walkDirForCss(dir, extensions, files, depth, maxDepth) {
|
|
25
|
-
if (depth > maxDepth) return;
|
|
26
|
-
let entries;
|
|
27
|
-
try {
|
|
28
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
29
|
-
} catch {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
for (const entry of entries) {
|
|
33
|
-
if (entry.isDirectory()) {
|
|
34
|
-
if (!SKIP_DIRS.has(entry.name)) {
|
|
35
|
-
walkDirForCss(join(dir, entry.name), extensions, files, depth + 1, maxDepth);
|
|
36
|
-
}
|
|
37
|
-
} else if (entry.isFile()) {
|
|
38
|
-
const lower = entry.name.toLowerCase();
|
|
39
|
-
if (extensions.some(ext => lower.endsWith(ext))) {
|
|
40
|
-
files.push(join(dir, entry.name));
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Detect design system for project or feature
|
|
48
|
-
* @param {string} projectPath - Root path of the project
|
|
49
|
-
* @param {string|null} featureName - Feature name (optional, checks feature-level if provided)
|
|
50
|
-
* @returns {Object} { hasDesignSystem, path, isProjectLevel, isFeatureLevel, source }
|
|
51
|
-
*/
|
|
52
|
-
export function detectDesignSystem(projectPath, featureName = null) {
|
|
53
|
-
const result = {
|
|
54
|
-
hasDesignSystem: false,
|
|
55
|
-
path: null,
|
|
56
|
-
isProjectLevel: false,
|
|
57
|
-
isFeatureLevel: false,
|
|
58
|
-
source: null
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// Priority 1: Feature-level design system (if feature specified)
|
|
62
|
-
if (featureName) {
|
|
63
|
-
const featureDesignSystemPath = join(
|
|
64
|
-
projectPath,
|
|
65
|
-
'.morph/features',
|
|
66
|
-
featureName,
|
|
67
|
-
'1-design',
|
|
68
|
-
'design-system.md'
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
if (existsSync(featureDesignSystemPath)) {
|
|
72
|
-
return {
|
|
73
|
-
hasDesignSystem: true,
|
|
74
|
-
path: featureDesignSystemPath,
|
|
75
|
-
isProjectLevel: false,
|
|
76
|
-
isFeatureLevel: true,
|
|
77
|
-
source: 'feature-output'
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Priority 2: Project-level design system (markdown)
|
|
83
|
-
const projectDesignSystemPath = join(projectPath, '.morph/context/design-system.md');
|
|
84
|
-
if (existsSync(projectDesignSystemPath)) {
|
|
85
|
-
return {
|
|
86
|
-
hasDesignSystem: true,
|
|
87
|
-
path: projectDesignSystemPath,
|
|
88
|
-
isProjectLevel: true,
|
|
89
|
-
isFeatureLevel: false,
|
|
90
|
-
source: 'project-markdown'
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Priority 3: Generated CSS design system
|
|
95
|
-
const cssDesignSystemPath = join(projectPath, 'wwwroot/css/design-system.css');
|
|
96
|
-
if (existsSync(cssDesignSystemPath)) {
|
|
97
|
-
return {
|
|
98
|
-
hasDesignSystem: true,
|
|
99
|
-
path: cssDesignSystemPath,
|
|
100
|
-
isProjectLevel: true,
|
|
101
|
-
isFeatureLevel: false,
|
|
102
|
-
source: 'generated-css'
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Priority 4: Recursively scan frontend root directories for CSS/SCSS with design tokens
|
|
107
|
-
const frontendRoots = ['wwwroot', 'styles', 'src/styles', 'src', 'public'];
|
|
108
|
-
const extensions = ['.css', '.scss'];
|
|
109
|
-
const collected = [];
|
|
110
|
-
|
|
111
|
-
for (const root of frontendRoots) {
|
|
112
|
-
const rootPath = join(projectPath, root);
|
|
113
|
-
if (existsSync(rootPath)) {
|
|
114
|
-
walkDirForCss(rootPath, extensions, collected, 0, CSS_SCAN_MAX_DEPTH);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Deduplicate by resolved path
|
|
119
|
-
const seen = new Set();
|
|
120
|
-
const cssFiles = [];
|
|
121
|
-
for (const file of collected) {
|
|
122
|
-
if (!seen.has(file)) {
|
|
123
|
-
seen.add(file);
|
|
124
|
-
cssFiles.push(file);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Sort for deterministic first-match across platforms
|
|
129
|
-
cssFiles.sort();
|
|
130
|
-
|
|
131
|
-
for (const cssFile of cssFiles) {
|
|
132
|
-
try {
|
|
133
|
-
const content = readFileSync(cssFile, 'utf8');
|
|
134
|
-
const hasDesignTokens = /:root\s*\{[\s\S]*?--/.test(content);
|
|
135
|
-
if (hasDesignTokens) {
|
|
136
|
-
return {
|
|
137
|
-
hasDesignSystem: true,
|
|
138
|
-
path: cssFile,
|
|
139
|
-
isProjectLevel: true,
|
|
140
|
-
isFeatureLevel: false,
|
|
141
|
-
source: 'existing-css-variables'
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
} catch {
|
|
145
|
-
// Continue checking other files
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// No design system found
|
|
150
|
-
return result;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Get recommended design system locations based on project type
|
|
155
|
-
* @param {string} projectPath - Root path of the project
|
|
156
|
-
* @returns {Object} { markdown, css, description }
|
|
157
|
-
*/
|
|
158
|
-
export function getRecommendedLocations(projectPath) {
|
|
159
|
-
try {
|
|
160
|
-
const configPath = join(projectPath, '.morph/config/config.json');
|
|
161
|
-
|
|
162
|
-
let projectType = 'unknown';
|
|
163
|
-
if (existsSync(configPath)) {
|
|
164
|
-
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
165
|
-
projectType = config.project?.type || 'unknown';
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Default locations
|
|
169
|
-
const locations = {
|
|
170
|
-
markdown: '.morph/context/design-system.md',
|
|
171
|
-
css: 'public/design-system.css',
|
|
172
|
-
description: 'Project-level design system (shared across features)'
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// Adjust based on project type
|
|
176
|
-
if (projectType === 'nextjs' || projectType.includes('nextjs')) {
|
|
177
|
-
locations.css = 'styles/design-system.css';
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return locations;
|
|
181
|
-
} catch {
|
|
182
|
-
return {
|
|
183
|
-
markdown: '.morph/context/design-system.md',
|
|
184
|
-
css: 'wwwroot/css/design-system.css',
|
|
185
|
-
description: 'Project-level design system (shared across features)'
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Design System Detector
|
|
3
|
+
*
|
|
4
|
+
* Detects if a design system exists for the project or feature.
|
|
5
|
+
* Checks multiple locations in priority order.
|
|
6
|
+
*
|
|
7
|
+
* @module design-system-detector
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
|
|
13
|
+
const SKIP_DIRS = new Set(['node_modules', '.git', '.morph', 'dist', 'build', '.next', 'obj', 'bin', '.vs']);
|
|
14
|
+
const CSS_SCAN_MAX_DEPTH = 6;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Recursively walk a directory collecting files with given extensions.
|
|
18
|
+
* @param {string} dir - Directory to walk
|
|
19
|
+
* @param {string[]} extensions - File extensions to collect (e.g. ['.css', '.scss'])
|
|
20
|
+
* @param {string[]} files - Accumulator array (mutated in-place)
|
|
21
|
+
* @param {number} depth - Current recursion depth
|
|
22
|
+
* @param {number} maxDepth - Maximum recursion depth
|
|
23
|
+
*/
|
|
24
|
+
function walkDirForCss(dir, extensions, files, depth, maxDepth) {
|
|
25
|
+
if (depth > maxDepth) return;
|
|
26
|
+
let entries;
|
|
27
|
+
try {
|
|
28
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
29
|
+
} catch {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
for (const entry of entries) {
|
|
33
|
+
if (entry.isDirectory()) {
|
|
34
|
+
if (!SKIP_DIRS.has(entry.name)) {
|
|
35
|
+
walkDirForCss(join(dir, entry.name), extensions, files, depth + 1, maxDepth);
|
|
36
|
+
}
|
|
37
|
+
} else if (entry.isFile()) {
|
|
38
|
+
const lower = entry.name.toLowerCase();
|
|
39
|
+
if (extensions.some(ext => lower.endsWith(ext))) {
|
|
40
|
+
files.push(join(dir, entry.name));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Detect design system for project or feature
|
|
48
|
+
* @param {string} projectPath - Root path of the project
|
|
49
|
+
* @param {string|null} featureName - Feature name (optional, checks feature-level if provided)
|
|
50
|
+
* @returns {Object} { hasDesignSystem, path, isProjectLevel, isFeatureLevel, source }
|
|
51
|
+
*/
|
|
52
|
+
export function detectDesignSystem(projectPath, featureName = null) {
|
|
53
|
+
const result = {
|
|
54
|
+
hasDesignSystem: false,
|
|
55
|
+
path: null,
|
|
56
|
+
isProjectLevel: false,
|
|
57
|
+
isFeatureLevel: false,
|
|
58
|
+
source: null
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Priority 1: Feature-level design system (if feature specified)
|
|
62
|
+
if (featureName) {
|
|
63
|
+
const featureDesignSystemPath = join(
|
|
64
|
+
projectPath,
|
|
65
|
+
'.morph/features',
|
|
66
|
+
featureName,
|
|
67
|
+
'1-design',
|
|
68
|
+
'design-system.md'
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
if (existsSync(featureDesignSystemPath)) {
|
|
72
|
+
return {
|
|
73
|
+
hasDesignSystem: true,
|
|
74
|
+
path: featureDesignSystemPath,
|
|
75
|
+
isProjectLevel: false,
|
|
76
|
+
isFeatureLevel: true,
|
|
77
|
+
source: 'feature-output'
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Priority 2: Project-level design system (markdown)
|
|
83
|
+
const projectDesignSystemPath = join(projectPath, '.morph/context/design-system.md');
|
|
84
|
+
if (existsSync(projectDesignSystemPath)) {
|
|
85
|
+
return {
|
|
86
|
+
hasDesignSystem: true,
|
|
87
|
+
path: projectDesignSystemPath,
|
|
88
|
+
isProjectLevel: true,
|
|
89
|
+
isFeatureLevel: false,
|
|
90
|
+
source: 'project-markdown'
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Priority 3: Generated CSS design system
|
|
95
|
+
const cssDesignSystemPath = join(projectPath, 'wwwroot/css/design-system.css');
|
|
96
|
+
if (existsSync(cssDesignSystemPath)) {
|
|
97
|
+
return {
|
|
98
|
+
hasDesignSystem: true,
|
|
99
|
+
path: cssDesignSystemPath,
|
|
100
|
+
isProjectLevel: true,
|
|
101
|
+
isFeatureLevel: false,
|
|
102
|
+
source: 'generated-css'
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Priority 4: Recursively scan frontend root directories for CSS/SCSS with design tokens
|
|
107
|
+
const frontendRoots = ['wwwroot', 'styles', 'src/styles', 'src', 'public'];
|
|
108
|
+
const extensions = ['.css', '.scss'];
|
|
109
|
+
const collected = [];
|
|
110
|
+
|
|
111
|
+
for (const root of frontendRoots) {
|
|
112
|
+
const rootPath = join(projectPath, root);
|
|
113
|
+
if (existsSync(rootPath)) {
|
|
114
|
+
walkDirForCss(rootPath, extensions, collected, 0, CSS_SCAN_MAX_DEPTH);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Deduplicate by resolved path
|
|
119
|
+
const seen = new Set();
|
|
120
|
+
const cssFiles = [];
|
|
121
|
+
for (const file of collected) {
|
|
122
|
+
if (!seen.has(file)) {
|
|
123
|
+
seen.add(file);
|
|
124
|
+
cssFiles.push(file);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Sort for deterministic first-match across platforms
|
|
129
|
+
cssFiles.sort();
|
|
130
|
+
|
|
131
|
+
for (const cssFile of cssFiles) {
|
|
132
|
+
try {
|
|
133
|
+
const content = readFileSync(cssFile, 'utf8');
|
|
134
|
+
const hasDesignTokens = /:root\s*\{[\s\S]*?--/.test(content);
|
|
135
|
+
if (hasDesignTokens) {
|
|
136
|
+
return {
|
|
137
|
+
hasDesignSystem: true,
|
|
138
|
+
path: cssFile,
|
|
139
|
+
isProjectLevel: true,
|
|
140
|
+
isFeatureLevel: false,
|
|
141
|
+
source: 'existing-css-variables'
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
} catch {
|
|
145
|
+
// Continue checking other files
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// No design system found
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get recommended design system locations based on project type
|
|
155
|
+
* @param {string} projectPath - Root path of the project
|
|
156
|
+
* @returns {Object} { markdown, css, description }
|
|
157
|
+
*/
|
|
158
|
+
export function getRecommendedLocations(projectPath) {
|
|
159
|
+
try {
|
|
160
|
+
const configPath = join(projectPath, '.morph/config/config.json');
|
|
161
|
+
|
|
162
|
+
let projectType = 'unknown';
|
|
163
|
+
if (existsSync(configPath)) {
|
|
164
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
165
|
+
projectType = config.project?.type || 'unknown';
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Default locations
|
|
169
|
+
const locations = {
|
|
170
|
+
markdown: '.morph/context/design-system.md',
|
|
171
|
+
css: 'public/design-system.css',
|
|
172
|
+
description: 'Project-level design system (shared across features)'
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// Adjust based on project type
|
|
176
|
+
if (projectType === 'nextjs' || projectType.includes('nextjs')) {
|
|
177
|
+
locations.css = 'styles/design-system.css';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return locations;
|
|
181
|
+
} catch {
|
|
182
|
+
return {
|
|
183
|
+
markdown: '.morph/context/design-system.md',
|
|
184
|
+
css: 'wwwroot/css/design-system.css',
|
|
185
|
+
description: 'Project-level design system (shared across features)'
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|