@polymorphism-tech/morph-spec 4.9.0 → 4.10.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 (124) hide show
  1. package/README.md +2 -2
  2. package/bin/morph-spec.js +30 -0
  3. package/bin/task-manager.js +34 -22
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +1 -1
  6. package/docs/QUICKSTART.md +1 -1
  7. package/framework/CLAUDE.md +99 -98
  8. package/framework/agents.json +37 -7
  9. package/framework/commands/commit.md +166 -0
  10. package/framework/commands/morph-apply.md +13 -2
  11. package/framework/commands/morph-archive.md +8 -2
  12. package/framework/commands/morph-infra.md +6 -0
  13. package/framework/commands/morph-preflight.md +6 -0
  14. package/framework/commands/morph-proposal.md +56 -7
  15. package/framework/commands/morph-status.md +6 -0
  16. package/framework/commands/morph-troubleshoot.md +6 -0
  17. package/framework/hooks/claude-code/notification/approval-reminder.js +3 -2
  18. package/framework/hooks/claude-code/post-tool-use/dispatch.js +154 -31
  19. package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +7 -84
  20. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +8 -17
  21. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +16 -3
  22. package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +4 -3
  23. package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +3 -2
  24. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +60 -0
  25. package/framework/hooks/claude-code/session-start/inject-morph-context.js +55 -2
  26. package/framework/hooks/claude-code/session-start/post-compact-restore.js +41 -0
  27. package/framework/hooks/claude-code/stop/validate-completion.js +2 -15
  28. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +23 -5
  29. package/framework/hooks/shared/compact-restore.js +100 -0
  30. package/framework/hooks/shared/dispatch-helpers.js +116 -0
  31. package/framework/hooks/shared/phase-utils.js +9 -5
  32. package/framework/hooks/shared/state-reader.js +27 -3
  33. package/framework/phases.json +30 -7
  34. package/framework/rules/morph-workflow.md +88 -86
  35. package/framework/skills/level-0-meta/mcp-registry.json +86 -51
  36. package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/SKILL.md +13 -16
  37. package/framework/skills/level-0-meta/{code-review → morph-code-review}/SKILL.md +1 -1
  38. package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/SKILL.md +2 -2
  39. package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/SKILL.md +5 -5
  40. package/framework/skills/level-0-meta/morph-init/SKILL.md +72 -7
  41. package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/SKILL.md +9 -9
  42. package/framework/skills/level-0-meta/morph-replicate/SKILL.md +1 -1
  43. package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/SKILL.md +1 -1
  44. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/SKILL.md +2 -3
  45. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/references/tools-per-phase.md +1 -2
  46. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/SKILL.md +1 -1
  47. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/scripts/check-phase-outputs.mjs +2 -2
  48. package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +238 -0
  49. package/framework/skills/level-1-workflows/{phase-codebase-analysis → morph-phase-codebase-analysis}/SKILL.md +251 -251
  50. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +507 -0
  51. package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/SKILL.md +590 -491
  52. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +50 -0
  53. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +45 -0
  54. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +47 -0
  55. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +254 -0
  56. package/framework/skills/level-1-workflows/{phase-setup → morph-phase-setup}/SKILL.md +237 -194
  57. package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/SKILL.md +307 -270
  58. package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/scripts/validate-tasks.mjs +3 -3
  59. package/framework/skills/level-1-workflows/{phase-uiux → morph-phase-uiux}/SKILL.md +320 -285
  60. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +97 -0
  61. package/framework/standards/integration/mcp/mcp-tools.md +25 -7
  62. package/framework/templates/docs/onboarding.md +2 -2
  63. package/package.json +1 -2
  64. package/src/commands/agents/dispatch-agents.js +50 -3
  65. package/src/commands/mcp/mcp-setup.js +39 -2
  66. package/src/commands/phase/phase-reset.js +74 -0
  67. package/src/commands/project/doctor.js +19 -5
  68. package/src/commands/scope/escalate.js +215 -0
  69. package/src/commands/state/advance-phase.js +27 -53
  70. package/src/commands/state/state.js +1 -1
  71. package/src/commands/task/expand.js +100 -0
  72. package/src/core/paths/output-schema.js +4 -3
  73. package/src/core/state/phase-state-machine.js +7 -4
  74. package/src/core/state/state-manager.js +4 -3
  75. package/src/lib/detectors/claude-config-detector.js +93 -347
  76. package/src/lib/detectors/design-system-detector.js +189 -189
  77. package/src/lib/detectors/index.js +155 -57
  78. package/src/lib/generators/context-generator.js +2 -2
  79. package/src/lib/installers/mcp-installer.js +37 -5
  80. package/src/lib/phase-chain/phase-validator.js +22 -16
  81. package/src/lib/scope/impact-analyzer.js +106 -0
  82. package/src/lib/tasks/task-parser.js +1 -1
  83. package/src/lib/validators/shared/emit-validator-dispatch.js +64 -0
  84. package/src/scripts/setup-infra.js +15 -0
  85. package/src/utils/agents-installer.js +32 -12
  86. package/src/utils/file-copier.js +0 -1
  87. package/src/utils/hooks-installer.js +15 -1
  88. package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +0 -216
  89. package/framework/skills/level-1-workflows/phase-design/SKILL.md +0 -383
  90. package/src/commands/project/index.js +0 -8
  91. package/src/core/index.js +0 -10
  92. package/src/core/state/index.js +0 -8
  93. package/src/core/templates/index.js +0 -9
  94. package/src/core/templates/template-data-sources.js +0 -325
  95. package/src/core/workflows/index.js +0 -7
  96. package/src/lib/detectors/config-detector.js +0 -223
  97. package/src/lib/detectors/standards-generator.js +0 -335
  98. package/src/lib/detectors/structure-detector.js +0 -275
  99. package/src/lib/monitor/agent-resolver.js +0 -144
  100. package/src/lib/monitor/renderer.js +0 -230
  101. package/src/lib/orchestration/index.js +0 -7
  102. package/src/lib/orchestration/team-orchestrator.js +0 -404
  103. package/src/sanitizer/context-sanitizer.js +0 -221
  104. package/src/sanitizer/patterns.js +0 -163
  105. package/src/writer/file-writer.js +0 -86
  106. /package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/references/proposal-example.md +0 -0
  107. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-example.md +0 -0
  108. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-guidelines.md +0 -0
  109. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/scripts/scan-csharp.mjs +0 -0
  110. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/references/review-example-nextjs.md +0 -0
  111. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/scripts/scan-nextjs.mjs +0 -0
  112. /package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/scripts/scan-accessibility.mjs +0 -0
  113. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-dev-server.mjs +0 -0
  114. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-stack.mjs +0 -0
  115. /package/framework/skills/level-0-meta/{simulation-checklist → morph-simulation-checklist}/SKILL.md +0 -0
  116. /package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/scripts/set_title.sh +0 -0
  117. /package/framework/skills/level-1-workflows/{phase-clarify → morph-phase-clarify}/references/clarifications-example.md +0 -0
  118. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/architecture-analysis-guide.md +0 -0
  119. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-authoring-guide.md +0 -0
  120. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-example.md +0 -0
  121. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/recap-example.md +0 -0
  122. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/vsa-implementation-guide.md +0 -0
  123. /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/task-planning-patterns.md +0 -0
  124. /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/tasks-example.md +0 -0
@@ -1,325 +0,0 @@
1
- import { glob } from 'glob';
2
- import { readFileSync, existsSync } from 'fs';
3
- import { join } from 'path';
4
- import { execSync } from 'child_process';
5
-
6
- /**
7
- * Template Data Sources - Inject dynamic MCP data into templates
8
- *
9
- * Provides project context, compliance status, and recent activity
10
- * for enriching template placeholders.
11
- */
12
-
13
- /**
14
- * Get project structure statistics
15
- * @param {string} projectPath - Project root path
16
- * @returns {Promise<Object>} Project structure data
17
- */
18
- export async function getProjectStructure(projectPath = process.cwd()) {
19
- try {
20
- // Find all files (excluding common ignore patterns)
21
- const files = await glob('**/*', {
22
- cwd: projectPath,
23
- ignore: [
24
- 'node_modules/**',
25
- 'bin/**',
26
- 'obj/**',
27
- '.git/**',
28
- '.morph/**',
29
- 'dist/**',
30
- 'build/**',
31
- '*.log'
32
- ],
33
- nodir: true
34
- });
35
-
36
- // Calculate language stats
37
- const languageStats = {};
38
- files.forEach(file => {
39
- const ext = file.split('.').pop();
40
- languageStats[ext] = (languageStats[ext] || 0) + 1;
41
- });
42
-
43
- // Get test coverage if available
44
- let testCoverage = null;
45
- const coveragePath = join(projectPath, 'coverage/coverage-summary.json');
46
- if (existsSync(coveragePath)) {
47
- const coverageData = JSON.parse(readFileSync(coveragePath, 'utf8'));
48
- testCoverage = coverageData.total?.lines?.pct || null;
49
- }
50
-
51
- // Get last commit info
52
- let lastCommit = null;
53
- try {
54
- const gitLog = execSync('git log -1 --format="%H|%an|%ar|%s"', {
55
- cwd: projectPath,
56
- encoding: 'utf8',
57
- stdio: 'pipe'
58
- });
59
-
60
- const [hash, author, time, message] = gitLog.trim().split('|');
61
- lastCommit = { hash: hash.substring(0, 7), author, time, message };
62
- } catch {
63
- // Git not available or not a repo
64
- }
65
-
66
- return {
67
- totalFiles: files.length,
68
- languageStats,
69
- testCoverage: testCoverage ? Math.round(testCoverage) : null,
70
- lastCommit,
71
- filesByExtension: {
72
- cs: languageStats.cs || 0,
73
- js: languageStats.js || 0,
74
- ts: languageStats.ts || 0,
75
- tsx: languageStats.tsx || 0,
76
- razor: languageStats.razor || 0,
77
- css: languageStats.css || 0,
78
- md: languageStats.md || 0
79
- }
80
- };
81
- } catch (error) {
82
- return {
83
- error: error.message,
84
- totalFiles: 0,
85
- testCoverage: null
86
- };
87
- }
88
- }
89
-
90
- /**
91
- * Get dependency information
92
- * @param {string} projectPath - Project root path
93
- * @returns {Promise<Object>} Dependency data
94
- */
95
- export async function getDependencyInfo(projectPath = process.cwd()) {
96
- const dependencies = {
97
- nuget: [],
98
- npm: [],
99
- outdated: []
100
- };
101
-
102
- try {
103
- // Check for NuGet packages (C# projects)
104
- const csprojFiles = await glob('**/*.csproj', {
105
- cwd: projectPath,
106
- ignore: ['**/bin/**', '**/obj/**']
107
- });
108
-
109
- if (csprojFiles.length > 0) {
110
- const csprojPath = join(projectPath, csprojFiles[0]);
111
- const csprojContent = readFileSync(csprojPath, 'utf8');
112
-
113
- // Extract PackageReference elements
114
- const packageRegex = /<PackageReference\s+Include="([^"]+)"\s+Version="([^"]+)"/g;
115
- let match;
116
-
117
- while ((match = packageRegex.exec(csprojContent)) !== null) {
118
- dependencies.nuget.push({
119
- name: match[1],
120
- version: match[2]
121
- });
122
- }
123
- }
124
-
125
- // Check for npm packages
126
- const packageJsonPath = join(projectPath, 'package.json');
127
- if (existsSync(packageJsonPath)) {
128
- const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
129
-
130
- const allDeps = {
131
- ...packageJson.dependencies,
132
- ...packageJson.devDependencies
133
- };
134
-
135
- Object.entries(allDeps).forEach(([name, version]) => {
136
- dependencies.npm.push({ name, version });
137
- });
138
- }
139
-
140
- } catch (error) {
141
- dependencies.error = error.message;
142
- }
143
-
144
- return dependencies;
145
- }
146
-
147
- /**
148
- * Get compliance status from validators
149
- * @param {string} projectPath - Project root path
150
- * @returns {Promise<Object>} Compliance data
151
- */
152
- export async function getComplianceStatus(projectPath = process.cwd()) {
153
- const compliance = {
154
- architectureViolations: 0,
155
- packageConflicts: 0,
156
- designSystemCompliance: 100,
157
- securityIssues: 0,
158
- overall: 100
159
- };
160
-
161
- try {
162
- // Run validators if available
163
- const validators = ['architecture', 'packages', 'design-system', 'security'];
164
-
165
- for (const validator of validators) {
166
- try {
167
- const result = execSync(
168
- `node bin/validate.js ${validator} --json`,
169
- {
170
- cwd: projectPath,
171
- encoding: 'utf8',
172
- stdio: 'pipe'
173
- }
174
- );
175
-
176
- const parsed = JSON.parse(result);
177
-
178
- if (validator === 'architecture') {
179
- compliance.architectureViolations = parsed.errors || 0;
180
- } else if (validator === 'packages') {
181
- compliance.packageConflicts = parsed.errors || 0;
182
- } else if (validator === 'design-system') {
183
- const total = parsed.total || 100;
184
- const errors = parsed.errors || 0;
185
- compliance.designSystemCompliance = Math.round(((total - errors) / total) * 100);
186
- } else if (validator === 'security') {
187
- compliance.securityIssues = parsed.errors || 0;
188
- }
189
- } catch {
190
- // Validator not available or failed - skip
191
- }
192
- }
193
-
194
- // Calculate overall compliance score
195
- const violations = compliance.architectureViolations +
196
- compliance.packageConflicts +
197
- compliance.securityIssues;
198
-
199
- compliance.overall = Math.max(0, 100 - (violations * 5));
200
-
201
- } catch (error) {
202
- compliance.error = error.message;
203
- }
204
-
205
- return compliance;
206
- }
207
-
208
- /**
209
- * Get recent activity and history
210
- * @param {string} projectPath - Project root path
211
- * @returns {Promise<Object>} Activity data
212
- */
213
- export async function getRecentActivity(projectPath = process.cwd()) {
214
- const activity = {
215
- lastFeature: null,
216
- recentCommits: [],
217
- activeBranches: [],
218
- lastDeployment: null
219
- };
220
-
221
- try {
222
- // Check state.json for last feature
223
- const statePath = join(projectPath, '.morph/state.json');
224
- if (existsSync(statePath)) {
225
- const state = JSON.parse(readFileSync(statePath, 'utf8'));
226
-
227
- // Find most recently updated feature
228
- let lastFeature = null;
229
- let lastTimestamp = null;
230
-
231
- Object.entries(state.features || {}).forEach(([name, feature]) => {
232
- const updatedAt = feature.updatedAt || feature.createdAt;
233
- if (updatedAt && (!lastTimestamp || updatedAt > lastTimestamp)) {
234
- lastTimestamp = updatedAt;
235
- lastFeature = name;
236
- }
237
- });
238
-
239
- activity.lastFeature = lastFeature;
240
- }
241
-
242
- // Get recent commits
243
- try {
244
- const gitLog = execSync('git log -5 --format="%h|%an|%ar|%s"', {
245
- cwd: projectPath,
246
- encoding: 'utf8',
247
- stdio: 'pipe'
248
- });
249
-
250
- activity.recentCommits = gitLog.trim().split('\n').map(line => {
251
- const [hash, author, time, message] = line.split('|');
252
- return { hash, author, time, message };
253
- });
254
- } catch {
255
- // Git not available
256
- }
257
-
258
- // Get active branches
259
- try {
260
- const branches = execSync('git branch -a', {
261
- cwd: projectPath,
262
- encoding: 'utf8',
263
- stdio: 'pipe'
264
- });
265
-
266
- activity.activeBranches = branches
267
- .split('\n')
268
- .map(b => b.trim().replace(/^\*\s+/, ''))
269
- .filter(b => b && !b.includes('->'));
270
- } catch {
271
- // Git not available
272
- }
273
-
274
- } catch (error) {
275
- activity.error = error.message;
276
- }
277
-
278
- return activity;
279
- }
280
-
281
- /**
282
- * Get all template data sources combined
283
- * @param {string} projectPath - Project root path
284
- * @returns {Promise<Object>} All data sources
285
- */
286
- export async function getAllTemplatePlaceholders(projectPath = process.cwd()) {
287
- const [structure, dependencies, compliance, activity] = await Promise.all([
288
- getProjectStructure(projectPath),
289
- getDependencyInfo(projectPath),
290
- getComplianceStatus(projectPath),
291
- getRecentActivity(projectPath)
292
- ]);
293
-
294
- return {
295
- MCP_PROJECT_FILES: structure.totalFiles,
296
- MCP_TEST_COVERAGE: structure.testCoverage || 'N/A',
297
- MCP_COMPLIANCE_SCORE: compliance.overall,
298
- MCP_LAST_FEATURE: activity.lastFeature || 'None',
299
- MCP_LAST_COMMIT: structure.lastCommit?.message || 'Unknown',
300
- MCP_LAST_COMMIT_AUTHOR: structure.lastCommit?.author || 'Unknown',
301
- MCP_LAST_COMMIT_TIME: structure.lastCommit?.time || 'Unknown',
302
-
303
- MCP_CS_FILES: structure.filesByExtension?.cs || 0,
304
- MCP_RAZOR_FILES: structure.filesByExtension?.razor || 0,
305
- MCP_TS_FILES: structure.filesByExtension?.ts || 0,
306
- MCP_JS_FILES: structure.filesByExtension?.js || 0,
307
-
308
- MCP_NUGET_PACKAGES: dependencies.nuget.length,
309
- MCP_NPM_PACKAGES: dependencies.npm.length,
310
-
311
- MCP_ARCHITECTURE_VIOLATIONS: compliance.architectureViolations,
312
- MCP_PACKAGE_CONFLICTS: compliance.packageConflicts,
313
- MCP_SECURITY_ISSUES: compliance.securityIssues,
314
-
315
- MCP_RECENT_BRANCHES: activity.activeBranches.slice(0, 3).join(', '),
316
-
317
- // Full objects for advanced usage
318
- _raw: {
319
- structure,
320
- dependencies,
321
- compliance,
322
- activity
323
- }
324
- };
325
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * Core Workflow System
3
- *
4
- * Workflow detection and classification (fast-track, standard, full-morph, etc.)
5
- */
6
-
7
- export { WorkflowDetector, detectWorkflow } from './workflow-detector.js';
@@ -1,223 +0,0 @@
1
- /**
2
- * Config Detector - Reads configuration files to extract technologies, versions, and dependencies
3
- */
4
-
5
- import { readFileSync, existsSync } from 'fs';
6
- import { join } from 'path';
7
- import { glob } from 'glob';
8
-
9
- /**
10
- * Detect project configuration
11
- * @param {string} projectPath - Project root path
12
- * @returns {Promise<Object>} Configuration detection results
13
- */
14
- export async function detectConfig(projectPath) {
15
- const result = {
16
- language: 'unknown',
17
- version: null,
18
- packageManager: 'unknown',
19
- dependencies: [],
20
- technologies: [],
21
- auth: null,
22
- database: null,
23
- hosting: null
24
- };
25
-
26
- // Detect .NET projects
27
- const dotnetConfig = await detectDotNet(projectPath);
28
- if (dotnetConfig) {
29
- Object.assign(result, dotnetConfig);
30
- return result;
31
- }
32
-
33
- // Detect Node.js projects
34
- const nodeConfig = await detectNode(projectPath);
35
- if (nodeConfig) {
36
- Object.assign(result, nodeConfig);
37
- return result;
38
- }
39
-
40
- return result;
41
- }
42
-
43
- /**
44
- * Detect .NET configuration
45
- */
46
- async function detectDotNet(projectPath) {
47
- const programCs = join(projectPath, 'Program.cs');
48
- const csprojFiles = await glob('**/*.csproj', { cwd: projectPath });
49
-
50
- if (!existsSync(programCs) && csprojFiles.length === 0) {
51
- return null;
52
- }
53
-
54
- const config = {
55
- language: 'csharp',
56
- version: null,
57
- packageManager: 'dotnet',
58
- dependencies: [],
59
- technologies: [],
60
- auth: null,
61
- database: null,
62
- hosting: null
63
- };
64
-
65
- // Read Program.cs
66
- if (existsSync(programCs)) {
67
- const content = readFileSync(programCs, 'utf8');
68
-
69
- // Detect .NET version
70
- if (content.includes('WebApplication.CreateBuilder') || content.includes('.NET')) {
71
- config.version = detectDotNetVersion(content);
72
- }
73
-
74
- // Detect technologies from Program.cs
75
- config.technologies = detectTechnologiesFromProgramCs(content);
76
- config.auth = detectAuthMethod(content);
77
- config.database = detectDatabase(content);
78
- config.hosting = detectHosting(content);
79
- }
80
-
81
- // Read .csproj files
82
- for (const csprojFile of csprojFiles.slice(0, 3)) { // Read first 3 csproj files
83
- const csprojPath = join(projectPath, csprojFile);
84
- const content = readFileSync(csprojPath, 'utf8');
85
-
86
- config.dependencies.push(...extractNuGetPackages(content));
87
- }
88
-
89
- // Remove duplicates
90
- config.dependencies = [...new Set(config.dependencies)];
91
- config.technologies = [...new Set(config.technologies)];
92
-
93
- return config;
94
- }
95
-
96
- /**
97
- * Detect Node.js configuration
98
- */
99
- async function detectNode(projectPath) {
100
- const packageJsonPath = join(projectPath, 'package.json');
101
-
102
- if (!existsSync(packageJsonPath)) {
103
- return null;
104
- }
105
-
106
- const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
107
-
108
- const config = {
109
- language: 'javascript',
110
- version: packageJson.engines?.node || null,
111
- packageManager: existsSync(join(projectPath, 'yarn.lock')) ? 'yarn' :
112
- existsSync(join(projectPath, 'pnpm-lock.yaml')) ? 'pnpm' : 'npm',
113
- dependencies: [],
114
- technologies: [],
115
- auth: null,
116
- database: null,
117
- hosting: null
118
- };
119
-
120
- // Extract dependencies
121
- const allDeps = {
122
- ...packageJson.dependencies,
123
- ...packageJson.devDependencies
124
- };
125
-
126
- config.dependencies = Object.keys(allDeps);
127
-
128
- // Detect technologies
129
- if (allDeps['next']) config.technologies.push('Next.js');
130
- if (allDeps['react']) config.technologies.push('React');
131
- if (allDeps['@clerk/nextjs']) config.auth = 'Clerk';
132
- if (allDeps['prisma']) config.database = 'Prisma';
133
-
134
- return config;
135
- }
136
-
137
- /**
138
- * Detect .NET version from Program.cs
139
- */
140
- function detectDotNetVersion(content) {
141
- if (content.includes('.NET 10') || content.includes('net10.0')) return '.NET 10';
142
- if (content.includes('.NET 9') || content.includes('net9.0')) return '.NET 9';
143
- if (content.includes('.NET 8') || content.includes('net8.0')) return '.NET 8';
144
- return 'unknown';
145
- }
146
-
147
- /**
148
- * Detect technologies from Program.cs
149
- */
150
- function detectTechnologiesFromProgramCs(content) {
151
- const techs = [];
152
-
153
- if (content.includes('AddBlazor') || content.includes('MapBlazorHub')) {
154
- techs.push('Blazor Server');
155
- }
156
-
157
- if (content.includes('AddHangfire')) {
158
- techs.push('Hangfire');
159
- }
160
-
161
- if (content.includes('IChatClient') || content.includes('Microsoft.Agents')) {
162
- techs.push('Microsoft Agent Framework');
163
- }
164
-
165
- if (content.includes('AddDbContext') || content.includes('UseSqlServer')) {
166
- techs.push('Entity Framework Core');
167
- }
168
-
169
- if (content.includes('AddFluentUIComponents') || content.includes('FluentUI')) {
170
- techs.push('Fluent UI Blazor');
171
- }
172
-
173
- if (content.includes('AddMudServices') || content.includes('MudBlazor')) {
174
- techs.push('MudBlazor');
175
- }
176
-
177
- return techs;
178
- }
179
-
180
- /**
181
- * Detect authentication method
182
- */
183
- function detectAuthMethod(content) {
184
- if (content.includes('AddClerk') || content.includes('Clerk')) return 'Clerk';
185
- if (content.includes('AddMicrosoftIdentity') || content.includes('Entra')) return 'Microsoft Entra';
186
- if (content.includes('AddAuthentication')) return 'ASP.NET Identity';
187
- return null;
188
- }
189
-
190
- /**
191
- * Detect database
192
- */
193
- function detectDatabase(content) {
194
- if (content.includes('UseSqlServer')) return 'SQL Server';
195
- if (content.includes('UsePostgres')) return 'PostgreSQL';
196
- if (content.includes('UseSqlite')) return 'SQLite';
197
- if (content.includes('UseCosmos')) return 'Cosmos DB';
198
- return null;
199
- }
200
-
201
- /**
202
- * Detect hosting platform
203
- */
204
- function detectHosting(content) {
205
- if (content.includes('Azure') || content.includes('AddAzure')) return 'Azure';
206
- if (content.includes('AWS')) return 'AWS';
207
- return null;
208
- }
209
-
210
- /**
211
- * Extract NuGet packages from .csproj
212
- */
213
- function extractNuGetPackages(csprojContent) {
214
- const packages = [];
215
- const regex = /<PackageReference\s+Include="([^"]+)"/g;
216
- let match;
217
-
218
- while ((match = regex.exec(csprojContent)) !== null) {
219
- packages.push(match[1]);
220
- }
221
-
222
- return packages;
223
- }