@polymorphism-tech/morph-spec 8.44.0 → 8.45.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 (193) hide show
  1. package/CLAUDE.md +10 -9
  2. package/README.md +10 -10
  3. package/bin/morph-spec.js +10 -13
  4. package/bin/validate.js +379 -379
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +2 -4
  7. package/docs/QUICKSTART.md +6 -6
  8. package/framework/CLAUDE.md +6 -8
  9. package/framework/MORPH.md +39 -45
  10. package/framework/agents.json +8 -9
  11. package/framework/commands/morph-apply.md +1 -3
  12. package/framework/commands/morph-hotfix.md +1 -1
  13. package/framework/commands/morph-proposal.md +2 -2
  14. package/framework/docs/CLI.md +10 -10
  15. package/framework/evals/review-checklist.md +90 -0
  16. package/framework/evals/trace-schema.md +2 -12
  17. package/framework/hooks/README.md +2 -3
  18. package/framework/hooks/claude-code/core/heavy-gate.js +1 -1
  19. package/framework/hooks/claude-code/core/protect-spec-files.js +2 -2
  20. package/framework/hooks/claude-code/core/telemetry-log.js +2 -2
  21. package/framework/hooks/claude-code/core/trace-autogen.js +1 -22
  22. package/framework/hooks/claude-code/core/validate-completion.js +3 -56
  23. package/framework/hooks/claude-code/statusline.sh +11 -11
  24. package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
  25. package/framework/hooks/git/pre-commit/agents.sh +25 -25
  26. package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
  27. package/framework/hooks/git/pre-commit/specs.sh +50 -50
  28. package/framework/hooks/git/pre-push/run-tests.sh +44 -44
  29. package/framework/hooks/shared/build-check.js +1 -1
  30. package/framework/hooks/shared/comment-scanner.js +1 -1
  31. package/framework/hooks/shared/phase-utils.js +0 -5
  32. package/framework/hooks/shared/stdin-reader.js +26 -26
  33. package/framework/phases.json +4 -6
  34. package/framework/rubrics/backend.json +1 -1
  35. package/framework/rubrics/default.json +1 -1
  36. package/framework/rubrics/frontend.json +1 -1
  37. package/framework/rules/morph-workflow.md +6 -6
  38. package/framework/schemas/feature.schema.json +2 -28
  39. package/framework/schemas/tasks.schema.json +6 -2
  40. package/framework/skills/README.md +1 -2
  41. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  42. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  43. package/framework/skills/core/morph-apply/SKILL.md +145 -246
  44. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  45. package/framework/skills/core/morph-hotfix/SKILL.md +5 -3
  46. package/framework/skills/core/morph-implement/SKILL.md +101 -201
  47. package/framework/skills/core/morph-machine/SKILL.md +3 -3
  48. package/framework/skills/core/morph-media/SKILL.md +1 -1
  49. package/framework/skills/core/morph-plan/SKILL.md +171 -287
  50. package/framework/skills/core/morph-proposal/SKILL.md +4 -5
  51. package/framework/skills/core/morph-review/SKILL.md +109 -430
  52. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  53. package/framework/skills/core/morph-standards/SKILL.md +2 -2
  54. package/framework/skills/core/morph-uiux/SKILL.md +2 -3
  55. package/framework/standards/STANDARDS.json +10 -10
  56. package/framework/standards/ai-agents/agent-archetypes.md +1 -1
  57. package/framework/standards/ai-agents/agent-session.md +1 -1
  58. package/framework/standards/ai-agents/agent-spec.md +1 -1
  59. package/framework/standards/ai-agents/batching.md +1 -1
  60. package/framework/standards/ai-agents/context-providers.md +1 -1
  61. package/framework/standards/ai-agents/conversational-agent-with-phases.md +1 -1
  62. package/framework/standards/ai-agents/cost-and-budget.md +1 -1
  63. package/framework/standards/ai-agents/durable-workflows-hitl.md +1 -1
  64. package/framework/standards/ai-agents/guardrails.md +1 -1
  65. package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
  66. package/framework/standards/ai-agents/mcp-server.md +1 -1
  67. package/framework/standards/ai-agents/mcp-tools.md +1 -1
  68. package/framework/standards/ai-agents/media-pipeline.md +1 -1
  69. package/framework/standards/ai-agents/media-video.md +1 -1
  70. package/framework/standards/ai-agents/middleware-patterns.md +1 -1
  71. package/framework/standards/ai-agents/modalities-image-gen.md +1 -1
  72. package/framework/standards/ai-agents/modalities-speech-to-text.md +1 -1
  73. package/framework/standards/ai-agents/modalities-text-to-speech.md +1 -1
  74. package/framework/standards/ai-agents/modalities-vision.md +1 -1
  75. package/framework/standards/ai-agents/multi-agent-patterns.md +1 -1
  76. package/framework/standards/ai-agents/observability-patterns.md +3 -3
  77. package/framework/standards/ai-agents/production.md +2 -2
  78. package/framework/standards/ai-agents/prompt-sources.md +1 -1
  79. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  80. package/framework/standards/ai-agents/rag-custom-pgvector.md +1 -1
  81. package/framework/standards/ai-agents/service-tier-flex.md +1 -1
  82. package/framework/standards/ai-agents/setup.md +1 -1
  83. package/framework/standards/ai-agents/structured-output.md +1 -1
  84. package/framework/standards/ai-agents/sweet-spot.md +1 -1
  85. package/framework/standards/ai-agents/testing-ai.md +1 -1
  86. package/framework/standards/ai-agents/tool-adapters.md +1 -1
  87. package/framework/standards/ai-agents/vector-data-extensions.md +1 -1
  88. package/framework/standards/ai-agents/workflows.md +1 -1
  89. package/framework/templates/CLAUDE.md.core.template +1 -1
  90. package/framework/templates/IDE-SNIPPETS.md +262 -262
  91. package/framework/templates/README.md +1 -1
  92. package/framework/templates/REGISTRY.json +0 -81
  93. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  94. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
  95. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  96. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  97. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  98. package/framework/templates/docs/spec.md +19 -205
  99. package/framework/templates/docs/ui-components.md +52 -52
  100. package/framework/templates/docs/ui-design-system.md +76 -76
  101. package/framework/templates/docs/ui-flows.md +167 -167
  102. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  103. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  104. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  105. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  106. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  107. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  108. package/framework/templates/infrastructure/github/README.md +56 -56
  109. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  110. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  111. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  112. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  113. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  114. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  115. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
  116. package/framework/templates/state.template.json +17 -17
  117. package/framework/templates/ui/design-system.css +226 -226
  118. package/package.json +2 -2
  119. package/src/commands/e2e/e2e.js +1 -1
  120. package/src/commands/project/heavy.js +1 -1
  121. package/src/commands/project/status.js +3 -3
  122. package/src/commands/state/approve.js +7 -49
  123. package/src/commands/state/gate-check.js +16 -106
  124. package/src/commands/state/gate-decision.js +1 -1
  125. package/src/commands/state/report-check.js +42 -17
  126. package/src/commands/validation/mutate.js +1 -1
  127. package/src/commands/validation/scout-check.js +28 -0
  128. package/src/commands/validation/validate-feature.js +5 -32
  129. package/src/commands/validation/verify.js +3 -4
  130. package/src/core/paths/output-schema.js +0 -2
  131. package/src/core/state/state-manager.js +1 -2
  132. package/src/core/templates/template-renderer.js +477 -477
  133. package/src/dashboard/app/lib/format.js +5 -0
  134. package/src/dashboard/app/main.jsx +13 -13
  135. package/src/dashboard/app/pages/FeatureDetail.jsx +16 -22
  136. package/src/dashboard/app/pages/Features.jsx +7 -7
  137. package/src/dashboard/app/pages/PhasePipeline.jsx +1 -1
  138. package/src/dashboard/app/pages/Rubrics.jsx +7 -11
  139. package/src/dashboard/scanner/docs-scanner.js +1 -2
  140. package/src/dashboard/scanner/features-scanner.js +3 -4
  141. package/src/dashboard/scanner/rubrics-scanner.js +12 -12
  142. package/src/dashboard/vite.config.js +24 -24
  143. package/src/lib/agents/output-collector.js +0 -1
  144. package/src/lib/detectors/design-system-detector.js +188 -188
  145. package/src/lib/evaluator-report.js +52 -334
  146. package/src/lib/generators/design-system-generator.js +508 -508
  147. package/src/lib/generators/handoff-generator.js +4 -5
  148. package/src/lib/installers/memory-installer.js +2 -2
  149. package/src/lib/mutation/manifest.js +8 -2
  150. package/src/lib/mutation/report.js +5 -3
  151. package/src/lib/mutation/runner.js +9 -7
  152. package/src/lib/risk-detector.js +60 -122
  153. package/src/lib/scout/scout-check.js +293 -0
  154. package/src/lib/tasks/task-classifier.js +30 -30
  155. package/src/lib/tasks/task-parser.js +209 -209
  156. package/src/lib/tasks/tasks-json-parser.js +7 -5
  157. package/src/lib/tasks/test-nodes.js +1 -1
  158. package/src/lib/tasks/test-runner.js +1 -1
  159. package/src/lib/validators/css/css-validator.js +340 -340
  160. package/src/lib/validators/reuse/reuse-validator.js +6 -8
  161. package/src/lib/validators/verify-runner.js +1 -1
  162. package/src/scripts/setup-infra.js +1 -1
  163. package/src/utils/agent-id-mapper.js +11 -11
  164. package/src/utils/agents-installer.js +5 -9
  165. package/src/utils/claude-settings-manager.js +300 -300
  166. package/src/utils/hooks-installer.js +3 -10
  167. package/src/utils/skills-installer.js +1 -1
  168. package/framework/evals/proof-rubrics/README.md +0 -76
  169. package/framework/evals/proof-rubrics/eval-de-modelo.md +0 -80
  170. package/framework/evals/proof-rubrics/fiacao.md +0 -54
  171. package/framework/evals/proof-rubrics/guard-cego.md +0 -52
  172. package/framework/evals/proof-rubrics/media-claims.md +0 -109
  173. package/framework/evals/proof-rubrics/mutation-proof.md +0 -107
  174. package/framework/evals/proof-rubrics/observabilidade-genai.md +0 -119
  175. package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +0 -48
  176. package/framework/evals/rubrics/README.md +0 -45
  177. package/framework/evals/rubrics/architecture.md +0 -103
  178. package/framework/evals/rubrics/code-quality.md +0 -98
  179. package/framework/evals/rubrics/condensed.md +0 -57
  180. package/framework/evals/rubrics/contracts.md +0 -67
  181. package/framework/evals/rubrics/test-coverage.md +0 -99
  182. package/framework/evals/scenarios/dotnet-crud.yaml +0 -42
  183. package/framework/evals/scenarios/nextjs-auth.yaml +0 -38
  184. package/framework/evals/suites/standard.yaml +0 -5
  185. package/framework/hooks/claude-code/core/post-edit-typecheck.js +0 -127
  186. package/framework/skills/core/morph-eval/SKILL.md +0 -64
  187. package/framework/templates/.idea/morph-templates.xml +0 -92
  188. package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
  189. package/framework/templates/docs/clarifications.md +0 -253
  190. package/framework/templates/docs/onboarding.md +0 -122
  191. package/framework/templates/docs/schema-analysis.md +0 -119
  192. package/framework/templates/docs/user-stories.md +0 -34
  193. package/src/commands/state/score.js +0 -147
@@ -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
+ }