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