@polymorphism-tech/morph-spec 4.7.1 → 4.8.1
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/.morph/analytics/threads-log.jsonl +54 -0
- package/.morph/state.json +198 -0
- package/LICENSE +1 -2
- package/README.md +379 -414
- package/bin/morph-spec.js +57 -403
- package/bin/validate.js +2 -26
- package/claude-plugin.json +2 -2
- package/docs/ARCHITECTURE.md +43 -46
- package/docs/CHEATSHEET.md +203 -221
- package/docs/COMMAND-FLOWS.md +319 -289
- package/docs/QUICKSTART.md +2 -8
- package/docs/plans/2026-02-22-claude-docs-morph-alignment-analysis.md +2 -0
- package/docs/plans/2026-02-22-claude-settings.md +2 -0
- package/docs/plans/2026-02-22-morph-cc-alignment-impl.md +2 -0
- package/docs/plans/2026-02-22-morph-spec-next.md +2 -0
- package/docs/plans/2026-02-22-native-alignment-design.md +2 -0
- package/docs/plans/2026-02-22-native-alignment-impl.md +2 -0
- package/docs/plans/2026-02-22-native-enrichment-design.md +2 -0
- package/docs/plans/2026-02-22-native-enrichment.md +2 -0
- package/docs/plans/2026-02-23-ddd-architecture-refactor.md +2 -0
- package/docs/plans/2026-02-23-ddd-nextsteps.md +2 -0
- package/docs/plans/2026-02-23-infra-architect-refactor.md +2 -0
- package/docs/plans/2026-02-23-nextjs-code-review-design.md +2 -1
- package/docs/plans/2026-02-23-nextjs-code-review-impl.md +2 -0
- package/docs/plans/2026-02-23-nextjs-standards-design.md +2 -1
- package/docs/plans/2026-02-23-nextjs-standards-impl.md +2 -0
- package/docs/plans/2026-02-24-cli-radical-simplification.md +592 -0
- package/docs/plans/2026-02-24-framework-failure-points.md +125 -0
- package/docs/plans/2026-02-24-morph-init-design.md +337 -0
- package/docs/plans/2026-02-24-morph-init-impl.md +1269 -0
- package/docs/plans/2026-02-24-tutorial-command-design.md +71 -0
- package/docs/plans/2026-02-24-tutorial-command.md +298 -0
- package/framework/CLAUDE.md +2 -2
- package/framework/commands/morph-proposal.md +3 -3
- package/framework/hooks/README.md +11 -10
- package/framework/hooks/claude-code/notification/approval-reminder.js +2 -0
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +1 -1
- package/framework/hooks/claude-code/pre-tool-use/protect-readonly-files.js +4 -55
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +20 -5
- package/framework/hooks/claude-code/statusline.py +6 -1
- package/framework/hooks/claude-code/stop/validate-completion.js +1 -1
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +1 -1
- package/framework/hooks/dev/check-sync-health.js +117 -0
- package/framework/hooks/dev/guard-version-numbers.js +57 -0
- package/framework/hooks/dev/sync-standards-registry.js +60 -0
- package/framework/hooks/dev/sync-template-registry.js +60 -0
- package/framework/hooks/dev/validate-skill-format.js +70 -0
- package/framework/hooks/dev/validate-standard-format.js +73 -0
- package/framework/hooks/shared/payload-utils.js +39 -0
- package/framework/hooks/shared/state-reader.js +25 -1
- package/framework/rules/morph-workflow.md +1 -1
- package/framework/skills/level-0-meta/morph-init/SKILL.md +216 -0
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +4 -4
- package/framework/skills/level-0-meta/tool-usage-guide/SKILL.md +4 -4
- package/framework/skills/level-0-meta/verification-before-completion/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +192 -191
- package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +181 -180
- package/framework/skills/level-1-workflows/phase-design/SKILL.md +339 -338
- package/framework/skills/level-1-workflows/phase-implement/SKILL.md +254 -253
- package/framework/skills/level-1-workflows/phase-setup/SKILL.md +168 -170
- package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +284 -283
- package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +246 -245
- package/framework/templates/examples/design-system-examples.md +1 -1
- package/framework/templates/ui/FluentDesignTheme.cs +1 -1
- package/framework/templates/ui/MudTheme.cs +1 -1
- package/framework/templates/ui/design-system.css +1 -1
- package/package.json +4 -2
- package/scripts/bump-version.js +248 -0
- package/scripts/install-dev-hooks.js +138 -0
- package/src/commands/agents/index.js +1 -2
- package/src/commands/index.js +13 -16
- package/src/commands/project/doctor.js +100 -14
- package/src/commands/project/index.js +7 -10
- package/src/commands/project/init.js +398 -555
- package/src/commands/project/install-plugin-cmd.js +28 -0
- package/src/commands/project/setup-infra-cmd.js +12 -0
- package/src/commands/project/tutorial.js +115 -0
- package/src/commands/project/update.js +22 -37
- package/src/commands/state/approve.js +213 -221
- package/src/commands/state/index.js +0 -1
- package/src/commands/state/state.js +337 -365
- package/src/commands/templates/index.js +0 -4
- package/src/commands/trust/trust.js +1 -93
- package/src/commands/utils/index.js +1 -5
- package/src/commands/validation/index.js +1 -5
- package/src/core/registry/command-registry.js +11 -285
- package/src/core/state/state-manager.js +5 -2
- package/src/lib/detectors/index.js +81 -87
- package/src/lib/detectors/structure-detector.js +275 -273
- package/src/lib/generators/recap-generator.js +232 -225
- package/src/lib/installers/mcp-installer.js +18 -3
- package/src/scripts/global-install.js +34 -0
- package/src/scripts/install-plugin.js +126 -0
- package/src/scripts/setup-infra.js +203 -0
- package/src/utils/agents-installer.js +10 -1
- package/src/utils/hooks-installer.js +70 -17
- package/CLAUDE.md +0 -77
- package/docs/claude-alignment-report.md +0 -137
- package/docs/examples/order-management/contracts.cs +0 -84
- package/docs/examples/order-management/proposal.md +0 -24
- package/docs/examples/order-management/spec.md +0 -162
- package/src/commands/feature/create-story.js +0 -362
- package/src/commands/feature/index.js +0 -6
- package/src/commands/feature/shard-spec.js +0 -225
- package/src/commands/feature/sprint-status.js +0 -250
- package/src/commands/generation/generate-onboarding.js +0 -169
- package/src/commands/generation/generate.js +0 -276
- package/src/commands/generation/index.js +0 -5
- package/src/commands/learning/capture-pattern.js +0 -121
- package/src/commands/learning/index.js +0 -5
- package/src/commands/learning/search-patterns.js +0 -126
- package/src/commands/mcp/mcp.js +0 -102
- package/src/commands/project/changes.js +0 -66
- package/src/commands/project/cost.js +0 -179
- package/src/commands/project/detect.js +0 -114
- package/src/commands/project/diff.js +0 -278
- package/src/commands/project/revert.js +0 -173
- package/src/commands/project/standards.js +0 -80
- package/src/commands/project/sync.js +0 -167
- package/src/commands/project/update-agents.js +0 -23
- package/src/commands/state/rollback-phase.js +0 -185
- package/src/commands/templates/template-customize.js +0 -87
- package/src/commands/templates/template-list.js +0 -114
- package/src/commands/templates/template-show.js +0 -129
- package/src/commands/templates/template-validate.js +0 -91
- package/src/commands/utils/troubleshoot.js +0 -222
- package/src/commands/validation/analyze-blazor-concurrency.js +0 -193
- package/src/commands/validation/lint-fluent.js +0 -352
- package/src/commands/validation/validate-blazor-state.js +0 -210
- package/src/commands/validation/validate-blazor.js +0 -156
- package/src/commands/validation/validate-css.js +0 -84
- package/src/lib/detectors/conversation-analyzer.js +0 -163
- package/src/lib/learning/index.js +0 -7
- package/src/lib/learning/learning-system.js +0 -520
- package/src/lib/troubleshooting/index.js +0 -8
- package/src/lib/troubleshooting/troubleshoot-grep.js +0 -198
- package/src/lib/troubleshooting/troubleshoot-index.js +0 -144
- package/src/llm/environment-detector.js +0 -43
|
@@ -1,273 +1,275 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Structure Detector - Analyzes folder structure to infer stack and architecture
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { glob } from 'glob';
|
|
6
|
-
import { join } from 'path';
|
|
7
|
-
import { existsSync } from 'fs';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Detect project structure
|
|
11
|
-
* @param {string} projectPath - Project root path
|
|
12
|
-
* @returns {Promise<Object>} Structure detection results
|
|
13
|
-
*/
|
|
14
|
-
export async function detectStructure(projectPath) {
|
|
15
|
-
const result = {
|
|
16
|
-
stack: 'unknown',
|
|
17
|
-
architecture: 'unknown',
|
|
18
|
-
uiLibrary: null,
|
|
19
|
-
patterns: [],
|
|
20
|
-
folders: {
|
|
21
|
-
hasPages: false,
|
|
22
|
-
hasComponents: false,
|
|
23
|
-
hasServices: false,
|
|
24
|
-
hasRepositories: false,
|
|
25
|
-
hasAgents: false
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
// Detect stack
|
|
30
|
-
result.stack = await detectStack(projectPath);
|
|
31
|
-
|
|
32
|
-
// Detect architecture pattern
|
|
33
|
-
result.architecture = await detectArchitecture(projectPath);
|
|
34
|
-
|
|
35
|
-
// Detect UI library
|
|
36
|
-
if (result.stack === 'blazor') {
|
|
37
|
-
result.uiLibrary = await detectUILibrary(projectPath);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Detect patterns
|
|
41
|
-
result.patterns = await detectPatterns(projectPath);
|
|
42
|
-
|
|
43
|
-
// Analyze folders
|
|
44
|
-
result.folders = await analyzeFolders(projectPath);
|
|
45
|
-
|
|
46
|
-
return result;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Detect stack type
|
|
51
|
-
*/
|
|
52
|
-
async function detectStack(projectPath) {
|
|
53
|
-
// Order matters: more specific patterns first
|
|
54
|
-
const patterns = {
|
|
55
|
-
blazor: [
|
|
56
|
-
'**/*.razor',
|
|
57
|
-
'**/Pages/**/*.razor',
|
|
58
|
-
'**/Components/**/*.razor'
|
|
59
|
-
],
|
|
60
|
-
nextjs: [
|
|
61
|
-
'next.config.js',
|
|
62
|
-
'next.config.mjs',
|
|
63
|
-
'next.config.ts',
|
|
64
|
-
'pages/**/*.tsx',
|
|
65
|
-
'app/**/*.tsx'
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
'
|
|
71
|
-
'
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
'
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
'
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
existsSync(join(projectPath, 'src', '
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
existsSync(join(projectPath, '
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
existsSync(join(projectPath, '
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
{ name: '
|
|
219
|
-
{ name: '
|
|
220
|
-
{ name: '
|
|
221
|
-
{ name: '
|
|
222
|
-
{ name: '
|
|
223
|
-
{ name: '
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
{ name: '
|
|
231
|
-
{ name: '
|
|
232
|
-
{ name: '
|
|
233
|
-
{ name: '
|
|
234
|
-
{ name: '
|
|
235
|
-
{ name: '
|
|
236
|
-
{ name: 'Unit Tests', glob: '
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
folders.
|
|
269
|
-
folders.
|
|
270
|
-
folders.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Structure Detector - Analyzes folder structure to infer stack and architecture
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { glob } from 'glob';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { existsSync } from 'fs';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Detect project structure
|
|
11
|
+
* @param {string} projectPath - Project root path
|
|
12
|
+
* @returns {Promise<Object>} Structure detection results
|
|
13
|
+
*/
|
|
14
|
+
export async function detectStructure(projectPath) {
|
|
15
|
+
const result = {
|
|
16
|
+
stack: 'unknown',
|
|
17
|
+
architecture: 'unknown',
|
|
18
|
+
uiLibrary: null,
|
|
19
|
+
patterns: [],
|
|
20
|
+
folders: {
|
|
21
|
+
hasPages: false,
|
|
22
|
+
hasComponents: false,
|
|
23
|
+
hasServices: false,
|
|
24
|
+
hasRepositories: false,
|
|
25
|
+
hasAgents: false
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Detect stack
|
|
30
|
+
result.stack = await detectStack(projectPath);
|
|
31
|
+
|
|
32
|
+
// Detect architecture pattern
|
|
33
|
+
result.architecture = await detectArchitecture(projectPath);
|
|
34
|
+
|
|
35
|
+
// Detect UI library
|
|
36
|
+
if (result.stack === 'blazor') {
|
|
37
|
+
result.uiLibrary = await detectUILibrary(projectPath);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Detect patterns
|
|
41
|
+
result.patterns = await detectPatterns(projectPath);
|
|
42
|
+
|
|
43
|
+
// Analyze folders
|
|
44
|
+
result.folders = await analyzeFolders(projectPath);
|
|
45
|
+
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Detect stack type
|
|
51
|
+
*/
|
|
52
|
+
async function detectStack(projectPath) {
|
|
53
|
+
// Order matters: more specific patterns first
|
|
54
|
+
const patterns = {
|
|
55
|
+
blazor: [
|
|
56
|
+
'**/*.razor',
|
|
57
|
+
'**/Pages/**/*.razor',
|
|
58
|
+
'**/Components/**/*.razor'
|
|
59
|
+
],
|
|
60
|
+
nextjs: [
|
|
61
|
+
'**/next.config.js',
|
|
62
|
+
'**/next.config.mjs',
|
|
63
|
+
'**/next.config.ts',
|
|
64
|
+
'pages/**/*.tsx',
|
|
65
|
+
'app/**/*.tsx',
|
|
66
|
+
'**/pages/**/*.tsx',
|
|
67
|
+
'**/app/**/*.tsx'
|
|
68
|
+
],
|
|
69
|
+
react: [
|
|
70
|
+
'src/App.jsx',
|
|
71
|
+
'src/App.tsx',
|
|
72
|
+
'vite.config.js',
|
|
73
|
+
'craco.config.js'
|
|
74
|
+
],
|
|
75
|
+
vue: [
|
|
76
|
+
'vite.config.js', // Vue 3 + Vite
|
|
77
|
+
'vue.config.js', // Vue 2
|
|
78
|
+
'src/App.vue'
|
|
79
|
+
],
|
|
80
|
+
dotnet: [
|
|
81
|
+
'**/*.csproj',
|
|
82
|
+
'**/Program.cs'
|
|
83
|
+
],
|
|
84
|
+
typescript: [
|
|
85
|
+
'tsconfig.json',
|
|
86
|
+
'src/**/*.ts'
|
|
87
|
+
],
|
|
88
|
+
nodejs: [
|
|
89
|
+
'package.json' // Fallback: generic Node.js
|
|
90
|
+
],
|
|
91
|
+
python: [
|
|
92
|
+
'requirements.txt',
|
|
93
|
+
'pyproject.toml',
|
|
94
|
+
'setup.py'
|
|
95
|
+
],
|
|
96
|
+
go: [
|
|
97
|
+
'go.mod',
|
|
98
|
+
'main.go'
|
|
99
|
+
]
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const globIgnore = ['framework/**', 'test/**', 'node_modules/**', '.morph/**'];
|
|
103
|
+
|
|
104
|
+
for (const [stack, globs] of Object.entries(patterns)) {
|
|
105
|
+
for (const pattern of globs) {
|
|
106
|
+
const files = await glob(pattern, { cwd: projectPath, nodir: true, ignore: globIgnore });
|
|
107
|
+
if (files.length > 0) {
|
|
108
|
+
return stack;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return 'unknown';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Detect architecture pattern
|
|
118
|
+
*/
|
|
119
|
+
async function detectArchitecture(projectPath) {
|
|
120
|
+
const globIgnore = ['framework/**', 'test/**', 'node_modules/**', '.morph/**'];
|
|
121
|
+
|
|
122
|
+
const checks = [
|
|
123
|
+
// .NET: Clean Architecture
|
|
124
|
+
{
|
|
125
|
+
pattern: 'clean-architecture',
|
|
126
|
+
indicators: [
|
|
127
|
+
existsSync(join(projectPath, 'src', 'Application')),
|
|
128
|
+
existsSync(join(projectPath, 'src', 'Domain')),
|
|
129
|
+
existsSync(join(projectPath, 'src', 'Infrastructure'))
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
// .NET: CQRS
|
|
133
|
+
{
|
|
134
|
+
pattern: 'cqrs',
|
|
135
|
+
indicators: [
|
|
136
|
+
await glob('**/Commands/**/*.cs', { cwd: projectPath, ignore: globIgnore }).then(f => f.length > 0),
|
|
137
|
+
await glob('**/Queries/**/*.cs', { cwd: projectPath, ignore: globIgnore }).then(f => f.length > 0)
|
|
138
|
+
]
|
|
139
|
+
},
|
|
140
|
+
// .NET: MVC
|
|
141
|
+
{
|
|
142
|
+
pattern: 'mvc',
|
|
143
|
+
indicators: [
|
|
144
|
+
existsSync(join(projectPath, 'Controllers')),
|
|
145
|
+
existsSync(join(projectPath, 'Models')),
|
|
146
|
+
existsSync(join(projectPath, 'Views'))
|
|
147
|
+
]
|
|
148
|
+
},
|
|
149
|
+
// JS: CLI / Library (bin + src + lib)
|
|
150
|
+
{
|
|
151
|
+
pattern: 'cli-library',
|
|
152
|
+
indicators: [
|
|
153
|
+
existsSync(join(projectPath, 'bin')),
|
|
154
|
+
existsSync(join(projectPath, 'src')),
|
|
155
|
+
existsSync(join(projectPath, 'package.json'))
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
// JS: Next.js App Router
|
|
159
|
+
{
|
|
160
|
+
pattern: 'nextjs-app-router',
|
|
161
|
+
indicators: [
|
|
162
|
+
existsSync(join(projectPath, 'app')),
|
|
163
|
+
await glob('app/**/*.{js,ts,jsx,tsx}', { cwd: projectPath, ignore: globIgnore }).then(f => f.length > 0)
|
|
164
|
+
]
|
|
165
|
+
},
|
|
166
|
+
// JS: Express MVC
|
|
167
|
+
{
|
|
168
|
+
pattern: 'express-mvc',
|
|
169
|
+
indicators: [
|
|
170
|
+
existsSync(join(projectPath, 'src', 'routes')) || existsSync(join(projectPath, 'routes')),
|
|
171
|
+
existsSync(join(projectPath, 'src', 'controllers')) || existsSync(join(projectPath, 'controllers'))
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
for (const { pattern, indicators } of checks) {
|
|
177
|
+
if (indicators.every(Boolean)) {
|
|
178
|
+
return pattern;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return 'unknown';
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Detect UI library (Blazor projects)
|
|
187
|
+
*/
|
|
188
|
+
async function detectUILibrary(projectPath) {
|
|
189
|
+
const csprojFiles = await glob('**/*.csproj', { cwd: projectPath });
|
|
190
|
+
|
|
191
|
+
for (const csprojFile of csprojFiles) {
|
|
192
|
+
const content = await import('fs').then(fs =>
|
|
193
|
+
fs.promises.readFile(join(projectPath, csprojFile), 'utf8')
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
if (content.includes('FluentUI.Blazor') || content.includes('Microsoft.FluentUI.AspNetCore.Components')) {
|
|
197
|
+
return 'fluent-ui';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (content.includes('MudBlazor')) {
|
|
201
|
+
return 'mudblazor';
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Detect common patterns
|
|
210
|
+
*/
|
|
211
|
+
async function detectPatterns(projectPath) {
|
|
212
|
+
const patterns = [];
|
|
213
|
+
// Ignore template/vendor directories; keep test/ accessible for JS test detection
|
|
214
|
+
const globIgnore = ['framework/**', 'test/fixtures/**', 'node_modules/**', '.morph/**'];
|
|
215
|
+
|
|
216
|
+
// C# / .NET patterns
|
|
217
|
+
const dotnetChecks = [
|
|
218
|
+
{ name: 'Repository Pattern', glob: '**/*Repository.cs' },
|
|
219
|
+
{ name: 'Service Layer', glob: '**/*Service.cs' },
|
|
220
|
+
{ name: 'DTOs', glob: '**/*Dto.cs' },
|
|
221
|
+
{ name: 'Entity Framework', glob: '**/Migrations/**/*.cs' },
|
|
222
|
+
{ name: 'Dependency Injection', glob: '**/DependencyInjection.cs' },
|
|
223
|
+
{ name: 'Hangfire Jobs', glob: '**/*Job.cs' },
|
|
224
|
+
{ name: 'AI Agents', glob: '**/*Agent.cs' },
|
|
225
|
+
{ name: 'Unit Tests', glob: '**/*.Tests/**/*.cs' }
|
|
226
|
+
];
|
|
227
|
+
|
|
228
|
+
// JavaScript / TypeScript patterns
|
|
229
|
+
const jsChecks = [
|
|
230
|
+
{ name: 'Service Layer', glob: 'src/**/*Service.js' },
|
|
231
|
+
{ name: 'Service Layer', glob: 'src/**/*Service.ts' },
|
|
232
|
+
{ name: 'Repository Pattern', glob: 'src/**/*Repository.js' },
|
|
233
|
+
{ name: 'Repository Pattern', glob: 'src/**/*Repository.ts' },
|
|
234
|
+
{ name: 'API Routes', glob: 'src/**/routes/**/*.{js,ts}' },
|
|
235
|
+
{ name: 'API Routes', glob: 'src/commands/**/*.{js,ts}' },
|
|
236
|
+
{ name: 'Unit Tests', glob: 'test/**/*.test.{js,ts}' },
|
|
237
|
+
{ name: 'Unit Tests', glob: 'src/**/*.test.{js,ts}' },
|
|
238
|
+
{ name: 'Unit Tests', glob: 'src/**/*.spec.{js,ts}' }
|
|
239
|
+
];
|
|
240
|
+
|
|
241
|
+
const allChecks = [...dotnetChecks, ...jsChecks];
|
|
242
|
+
const seen = new Set();
|
|
243
|
+
|
|
244
|
+
for (const { name, glob: pattern } of allChecks) {
|
|
245
|
+
if (seen.has(name)) continue;
|
|
246
|
+
const files = await glob(pattern, { cwd: projectPath, nodir: true, ignore: globIgnore });
|
|
247
|
+
if (files.length > 0) {
|
|
248
|
+
patterns.push(name);
|
|
249
|
+
seen.add(name);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return patterns;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Analyze folder structure
|
|
258
|
+
*/
|
|
259
|
+
async function analyzeFolders(projectPath) {
|
|
260
|
+
const folders = {
|
|
261
|
+
hasPages: false,
|
|
262
|
+
hasComponents: false,
|
|
263
|
+
hasServices: false,
|
|
264
|
+
hasRepositories: false,
|
|
265
|
+
hasAgents: false
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
folders.hasPages = await glob('**/Pages/**/*', { cwd: projectPath }).then(f => f.length > 0);
|
|
269
|
+
folders.hasComponents = await glob('**/Components/**/*', { cwd: projectPath }).then(f => f.length > 0);
|
|
270
|
+
folders.hasServices = await glob('**/Services/**/*', { cwd: projectPath }).then(f => f.length > 0);
|
|
271
|
+
folders.hasRepositories = await glob('**/Repositories/**/*', { cwd: projectPath }).then(f => f.length > 0);
|
|
272
|
+
folders.hasAgents = await glob('**/Agents/**/*', { cwd: projectPath }).then(f => f.length > 0);
|
|
273
|
+
|
|
274
|
+
return folders;
|
|
275
|
+
}
|