@polymorphism-tech/morph-spec 8.1.0 → 8.1.2

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 (165) hide show
  1. package/README.md +2 -2
  2. package/bin/validate.js +379 -379
  3. package/claude-plugin.json +1 -1
  4. package/docs/CHEATSHEET.md +1 -1
  5. package/docs/QUICKSTART.md +1 -1
  6. package/framework/commands/commit.md +166 -166
  7. package/framework/commands/morph-infra.md +215 -215
  8. package/framework/commands/morph-preflight.md +205 -205
  9. package/framework/commands/morph-troubleshoot.md +124 -124
  10. package/framework/hooks/claude-code/core/validate-completion.js +2 -1
  11. package/framework/hooks/claude-code/statusline.py +679 -679
  12. package/framework/hooks/claude-code/statusline.sh +11 -11
  13. package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
  14. package/framework/hooks/git/pre-commit/agents.sh +25 -25
  15. package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
  16. package/framework/hooks/git/pre-commit/specs.sh +50 -50
  17. package/framework/hooks/git/pre-push/run-tests.sh +44 -44
  18. package/framework/hooks/shared/activity-logger.js +160 -160
  19. package/framework/hooks/shared/build-check.js +97 -24
  20. package/framework/hooks/shared/stdin-reader.js +26 -26
  21. package/framework/rubrics/backend.json +41 -41
  22. package/framework/rules/ai-agents-standards.md +45 -45
  23. package/framework/rules/dotnet-templates.md +86 -86
  24. package/framework/rules/frontend-standards.md +13 -13
  25. package/framework/rules/infrastructure-standards.md +27 -27
  26. package/framework/rules/neon-standards.md +25 -25
  27. package/framework/rules/nextjs-standards.md +20 -20
  28. package/framework/rules/nextjs-templates.md +40 -40
  29. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  30. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  31. package/framework/skills/core/morph-apply/SKILL.md +1 -1
  32. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  33. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  34. package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
  35. package/framework/skills/core/morph-implement/SKILL.md +1 -1
  36. package/framework/skills/core/morph-plan/SKILL.md +1 -1
  37. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  38. package/framework/skills/core/morph-review/SKILL.md +1 -1
  39. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  40. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  41. package/framework/standards/ai-agents/agent-session.md +160 -160
  42. package/framework/standards/ai-agents/batching.md +193 -193
  43. package/framework/standards/ai-agents/context-providers.md +188 -188
  44. package/framework/standards/ai-agents/mcp-server.md +250 -250
  45. package/framework/standards/ai-agents/mcp-tools.md +197 -197
  46. package/framework/standards/ai-agents/middleware-patterns.md +207 -207
  47. package/framework/standards/ai-agents/modalities-image-gen.md +173 -173
  48. package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -171
  49. package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -181
  50. package/framework/standards/ai-agents/modalities-vision.md +201 -201
  51. package/framework/standards/ai-agents/multi-agent-patterns.md +155 -155
  52. package/framework/standards/ai-agents/production.md +275 -275
  53. package/framework/standards/ai-agents/prompts-in-db.md +214 -214
  54. package/framework/standards/ai-agents/providers/model-registry.md +158 -158
  55. package/framework/standards/ai-agents/service-tier-flex.md +135 -135
  56. package/framework/standards/ai-agents/structured-output.md +249 -249
  57. package/framework/standards/ai-agents/sweet-spot.md +109 -109
  58. package/framework/templates/IDE-SNIPPETS.md +262 -262
  59. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  60. package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
  61. package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
  62. package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
  63. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
  64. package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
  65. package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
  66. package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
  67. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
  68. package/framework/templates/code/dotnet/database/migration.cs +83 -83
  69. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
  70. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  71. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  72. package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
  73. package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
  74. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  75. package/framework/templates/code/typescript/contracts.ts +167 -167
  76. package/framework/templates/docs/clarifications.md +253 -253
  77. package/framework/templates/docs/schema-analysis.md +119 -119
  78. package/framework/templates/docs/ui-components.md +52 -52
  79. package/framework/templates/docs/ui-design-system.md +76 -76
  80. package/framework/templates/docs/ui-flows.md +167 -167
  81. package/framework/templates/docs/ui-mockups.md +98 -98
  82. package/framework/templates/feature/decisions.md +122 -122
  83. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
  84. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  85. package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
  86. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  87. package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
  88. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  89. package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
  90. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  91. package/framework/templates/infrastructure/docker/docker-compose.template.yml +50 -50
  92. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  93. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  94. package/framework/templates/infrastructure/github/README.md +56 -56
  95. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  96. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  97. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  98. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  99. package/framework/templates/integrations/asaas-client.cs +387 -387
  100. package/framework/templates/integrations/asaas-webhook.cs +351 -351
  101. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  102. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  103. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
  104. package/framework/templates/project/validate.js +120 -120
  105. package/framework/templates/state.template.json +17 -17
  106. package/framework/templates/ui/design-system.css +226 -226
  107. package/package.json +1 -1
  108. package/src/commands/dashboard/dashboard.js +53 -53
  109. package/src/commands/index.js +13 -13
  110. package/src/commands/mcp/mcp-setup.js +519 -519
  111. package/src/commands/project/checkpoint.js +209 -209
  112. package/src/commands/project/worktree.js +83 -1
  113. package/src/core/state/phase-state-machine.js +197 -197
  114. package/src/core/templates/template-renderer.js +477 -477
  115. package/src/dashboard/app/components/DetailPanel.jsx +11 -11
  116. package/src/dashboard/app/components/FlowGraph.jsx +17 -17
  117. package/src/dashboard/app/components/SearchFilter.jsx +22 -22
  118. package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
  119. package/src/dashboard/app/main.jsx +13 -13
  120. package/src/dashboard/app/pages/AgentMap.jsx +370 -370
  121. package/src/dashboard/app/pages/HookInspector.jsx +418 -418
  122. package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
  123. package/src/dashboard/app/pages/RulesMap.jsx +273 -273
  124. package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
  125. package/src/dashboard/app/pages/Standards.jsx +332 -332
  126. package/src/dashboard/app/styles/index.css +47 -47
  127. package/src/dashboard/index.html +12 -12
  128. package/src/dashboard/package.json +24 -24
  129. package/src/dashboard/scanner/hooks-scanner.js +41 -41
  130. package/src/dashboard/scanner/phases-scanner.js +15 -15
  131. package/src/dashboard/scanner/rubrics-scanner.js +12 -12
  132. package/src/dashboard/scanner/rules-scanner.js +37 -37
  133. package/src/dashboard/scanner/skills-scanner.js +43 -43
  134. package/src/dashboard/scanner/squads-scanner.js +13 -13
  135. package/src/dashboard/scanner/standards-scanner.js +26 -26
  136. package/src/dashboard/vite-plugin-morph-data.js +17 -17
  137. package/src/dashboard/vite.config.js +24 -24
  138. package/src/lib/agents/metrics-aggregator.js +56 -56
  139. package/src/lib/agents/metrics-calculator.js +125 -125
  140. package/src/lib/agents/output-collector.js +73 -73
  141. package/src/lib/detectors/claude-config-detector.js +152 -152
  142. package/src/lib/detectors/design-system-detector.js +231 -231
  143. package/src/lib/detectors/index.js +217 -217
  144. package/src/lib/generators/architecture-scanner.js +639 -639
  145. package/src/lib/generators/design-system-generator.js +508 -508
  146. package/src/lib/generators/index.js +13 -13
  147. package/src/lib/generators/settings-generator.js +209 -209
  148. package/src/lib/hooks/hook-executor.js +167 -167
  149. package/src/lib/installers/mcp-installer.js +719 -719
  150. package/src/lib/tasks/task-classifier.js +30 -30
  151. package/src/lib/tasks/task-parser.js +209 -209
  152. package/src/lib/validators/architecture/architecture-validator.js +259 -259
  153. package/src/lib/validators/checkpoint-persistence.js +39 -39
  154. package/src/lib/validators/contracts/contract-compliance-validator.js +274 -274
  155. package/src/lib/validators/css/css-validator.js +340 -340
  156. package/src/lib/validators/packages/package-validator.js +347 -347
  157. package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
  158. package/src/lib/validators/shared/issue-counter.js +14 -14
  159. package/src/lib/validators/validation-runner.js +349 -349
  160. package/src/lib/validators/verify-runner.js +24 -7
  161. package/src/utils/agent-id-mapper.js +11 -11
  162. package/src/utils/claude-settings-manager.js +300 -300
  163. package/src/utils/skills-installer.js +174 -174
  164. package/framework/templates/.idea/morph-templates.xml +0 -92
  165. package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
@@ -1,231 +1,231 @@
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
- * Check if UI agents are active for a feature
155
- * @param {string} projectPath - Root path of the project
156
- * @param {string} featureName - Feature name
157
- * @returns {boolean} True if UI agents are active
158
- */
159
- export function hasUIAgentsActive(projectPath, featureName) {
160
- try {
161
- const statePath = join(projectPath, '.morph/state.json');
162
-
163
- if (!existsSync(statePath)) {
164
- return false;
165
- }
166
-
167
- const state = JSON.parse(readFileSync(statePath, 'utf8'));
168
-
169
- // Handle both array and object format
170
- let feature;
171
- if (Array.isArray(state.features)) {
172
- feature = state.features.find(f => f.name === featureName);
173
- } else if (state.features && typeof state.features === 'object') {
174
- feature = state.features[featureName];
175
- }
176
-
177
- if (!feature || !feature.activeAgents) {
178
- return false;
179
- }
180
-
181
- // UI design agents that require a design system before implement phase.
182
- // Implementation-only agents (nextjs-expert, css-specialist)
183
- // build from existing designs and do NOT trigger this gate.
184
- const uiAgents = [
185
- 'ui-designer',
186
- 'uiux-designer',
187
- 'ui-ux-designer',
188
- ];
189
-
190
- return feature.activeAgents.some(agentId => uiAgents.includes(agentId));
191
- } catch {
192
- return false;
193
- }
194
- }
195
-
196
- /**
197
- * Get recommended design system locations based on project type
198
- * @param {string} projectPath - Root path of the project
199
- * @returns {Object} { markdown, css, description }
200
- */
201
- export function getRecommendedLocations(projectPath) {
202
- try {
203
- const configPath = join(projectPath, '.morph/config/config.json');
204
-
205
- let projectType = 'unknown';
206
- if (existsSync(configPath)) {
207
- const config = JSON.parse(readFileSync(configPath, 'utf8'));
208
- projectType = config.project?.type || 'unknown';
209
- }
210
-
211
- // Default locations
212
- const locations = {
213
- markdown: '.morph/context/design-system.md',
214
- css: 'public/design-system.css',
215
- description: 'Project-level design system (shared across features)'
216
- };
217
-
218
- // Adjust based on project type
219
- if (projectType === 'nextjs' || projectType.includes('nextjs')) {
220
- locations.css = 'styles/design-system.css';
221
- }
222
-
223
- return locations;
224
- } catch {
225
- return {
226
- markdown: '.morph/context/design-system.md',
227
- css: 'wwwroot/css/design-system.css',
228
- description: 'Project-level design system (shared across features)'
229
- };
230
- }
231
- }
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
+ * Check if UI agents are active for a feature
155
+ * @param {string} projectPath - Root path of the project
156
+ * @param {string} featureName - Feature name
157
+ * @returns {boolean} True if UI agents are active
158
+ */
159
+ export function hasUIAgentsActive(projectPath, featureName) {
160
+ try {
161
+ const statePath = join(projectPath, '.morph/state.json');
162
+
163
+ if (!existsSync(statePath)) {
164
+ return false;
165
+ }
166
+
167
+ const state = JSON.parse(readFileSync(statePath, 'utf8'));
168
+
169
+ // Handle both array and object format
170
+ let feature;
171
+ if (Array.isArray(state.features)) {
172
+ feature = state.features.find(f => f.name === featureName);
173
+ } else if (state.features && typeof state.features === 'object') {
174
+ feature = state.features[featureName];
175
+ }
176
+
177
+ if (!feature || !feature.activeAgents) {
178
+ return false;
179
+ }
180
+
181
+ // UI design agents that require a design system before implement phase.
182
+ // Implementation-only agents (nextjs-expert, css-specialist)
183
+ // build from existing designs and do NOT trigger this gate.
184
+ const uiAgents = [
185
+ 'ui-designer',
186
+ 'uiux-designer',
187
+ 'ui-ux-designer',
188
+ ];
189
+
190
+ return feature.activeAgents.some(agentId => uiAgents.includes(agentId));
191
+ } catch {
192
+ return false;
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Get recommended design system locations based on project type
198
+ * @param {string} projectPath - Root path of the project
199
+ * @returns {Object} { markdown, css, description }
200
+ */
201
+ export function getRecommendedLocations(projectPath) {
202
+ try {
203
+ const configPath = join(projectPath, '.morph/config/config.json');
204
+
205
+ let projectType = 'unknown';
206
+ if (existsSync(configPath)) {
207
+ const config = JSON.parse(readFileSync(configPath, 'utf8'));
208
+ projectType = config.project?.type || 'unknown';
209
+ }
210
+
211
+ // Default locations
212
+ const locations = {
213
+ markdown: '.morph/context/design-system.md',
214
+ css: 'public/design-system.css',
215
+ description: 'Project-level design system (shared across features)'
216
+ };
217
+
218
+ // Adjust based on project type
219
+ if (projectType === 'nextjs' || projectType.includes('nextjs')) {
220
+ locations.css = 'styles/design-system.css';
221
+ }
222
+
223
+ return locations;
224
+ } catch {
225
+ return {
226
+ markdown: '.morph/context/design-system.md',
227
+ css: 'wwwroot/css/design-system.css',
228
+ description: 'Project-level design system (shared across features)'
229
+ };
230
+ }
231
+ }