@polymorphism-tech/morph-spec 1.0.4 → 2.1.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.
- package/CLAUDE.md +1381 -0
- package/LICENSE +72 -0
- package/README.md +89 -6
- package/bin/detect-agents.js +225 -0
- package/bin/morph-spec.js +120 -0
- package/bin/render-template.js +302 -0
- package/bin/semantic-detect-agents.js +246 -0
- package/bin/validate-agents-skills.js +239 -0
- package/bin/validate-agents.js +69 -0
- package/bin/validate-phase.js +263 -0
- package/content/.azure/README.md +293 -0
- package/content/.azure/docs/azure-devops-setup.md +454 -0
- package/content/.azure/docs/branch-strategy.md +398 -0
- package/content/.azure/docs/local-development.md +515 -0
- package/content/.azure/pipelines/pipeline-variables.yml +34 -0
- package/content/.azure/pipelines/prod-pipeline.yml +319 -0
- package/content/.azure/pipelines/staging-pipeline.yml +234 -0
- package/content/.azure/pipelines/templates/build-dotnet.yml +75 -0
- package/content/.azure/pipelines/templates/deploy-app-service.yml +94 -0
- package/content/.azure/pipelines/templates/deploy-container-app.yml +120 -0
- package/content/.azure/pipelines/templates/infra-deploy.yml +90 -0
- package/content/.claude/commands/morph-apply.md +118 -26
- package/content/.claude/commands/morph-archive.md +9 -9
- package/content/.claude/commands/morph-clarify.md +184 -0
- package/content/.claude/commands/morph-design.md +275 -0
- package/content/.claude/commands/morph-proposal.md +56 -15
- package/content/.claude/commands/morph-setup.md +100 -0
- package/content/.claude/commands/morph-status.md +47 -32
- package/content/.claude/commands/morph-tasks.md +319 -0
- package/content/.claude/commands/morph-uiux.md +211 -0
- package/content/.claude/skills/specialists/ai-system-architect.md +604 -0
- package/content/.claude/skills/specialists/ms-agent-expert.md +143 -89
- package/content/.claude/skills/specialists/ui-ux-designer.md +744 -9
- package/content/.claude/skills/stacks/dotnet-blazor.md +244 -8
- package/content/.claude/skills/stacks/dotnet-nextjs.md +2 -2
- package/content/.morph/.morphversion +5 -0
- package/content/.morph/config/agents.json +101 -8
- package/content/.morph/config/azure-pricing.json +70 -0
- package/content/.morph/config/azure-pricing.schema.json +50 -0
- package/content/.morph/config/config.template.json +15 -3
- package/content/.morph/docs/STORY-DRIVEN-DEVELOPMENT.md +392 -0
- package/content/.morph/hooks/README.md +239 -0
- package/content/.morph/hooks/pre-commit-agents.sh +24 -0
- package/content/.morph/hooks/pre-commit-all.sh +48 -0
- package/content/.morph/hooks/pre-commit-costs.sh +91 -0
- package/content/.morph/hooks/pre-commit-specs.sh +49 -0
- package/content/.morph/hooks/pre-commit-tests.sh +60 -0
- package/content/.morph/project.md +5 -4
- package/content/.morph/schemas/agent.schema.json +296 -0
- package/content/.morph/standards/agent-framework-setup.md +453 -0
- package/content/.morph/standards/architecture.md +142 -7
- package/content/.morph/standards/azure.md +218 -23
- package/content/.morph/standards/coding.md +47 -12
- package/content/.morph/standards/dotnet10-migration.md +494 -0
- package/content/.morph/standards/fluent-ui-setup.md +590 -0
- package/content/.morph/standards/migration-guide.md +514 -0
- package/content/.morph/standards/passkeys-auth.md +423 -0
- package/content/.morph/standards/vector-search-rag.md +536 -0
- package/content/.morph/state.json +18 -0
- package/content/.morph/templates/FluentDesignTheme.cs +149 -0
- package/content/.morph/templates/MudTheme.cs +281 -0
- package/content/.morph/templates/contracts.cs +55 -55
- package/content/.morph/templates/decisions.md +4 -4
- package/content/.morph/templates/design-system.css +226 -0
- package/content/.morph/templates/infra/.dockerignore.example +89 -0
- package/content/.morph/templates/infra/Dockerfile.example +82 -0
- package/content/.morph/templates/infra/README.md +286 -0
- package/content/.morph/templates/infra/app-service.bicep +164 -0
- package/content/.morph/templates/infra/deploy.ps1 +229 -0
- package/content/.morph/templates/infra/deploy.sh +208 -0
- package/content/.morph/templates/infra/main.bicep +41 -7
- package/content/.morph/templates/infra/parameters.dev.json +6 -0
- package/content/.morph/templates/infra/parameters.prod.json +6 -0
- package/content/.morph/templates/infra/parameters.staging.json +29 -0
- package/content/.morph/templates/proposal.md +3 -3
- package/content/.morph/templates/recap.md +3 -3
- package/content/.morph/templates/spec.md +9 -8
- package/content/.morph/templates/sprint-status.yaml +68 -0
- package/content/.morph/templates/state.template.json +222 -0
- package/content/.morph/templates/story.md +143 -0
- package/content/.morph/templates/tasks.md +1 -1
- package/content/.morph/templates/ui-components.md +276 -0
- package/content/.morph/templates/ui-design-system.md +286 -0
- package/content/.morph/templates/ui-flows.md +336 -0
- package/content/.morph/templates/ui-mockups.md +133 -0
- package/content/.morph/test-infra/example.bicep +59 -0
- package/content/CLAUDE.md +124 -0
- package/content/README.md +79 -0
- package/detectors/config-detector.js +223 -0
- package/detectors/conversation-analyzer.js +163 -0
- package/detectors/index.js +84 -0
- package/detectors/standards-generator.js +275 -0
- package/detectors/structure-detector.js +221 -0
- package/docs/README.md +149 -0
- package/docs/api/cost-calculator.js.html +513 -0
- package/docs/api/design-system-generator.js.html +382 -0
- package/docs/api/fonts/Montserrat/Montserrat-Bold.eot +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Bold.woff +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Regular.eot +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Regular.woff +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
- package/docs/api/global.html +5263 -0
- package/docs/api/index.html +96 -0
- package/docs/api/scripts/collapse.js +39 -0
- package/docs/api/scripts/commonNav.js +28 -0
- package/docs/api/scripts/linenumber.js +25 -0
- package/docs/api/scripts/nav.js +12 -0
- package/docs/api/scripts/polyfill.js +4 -0
- package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/docs/api/scripts/prettify/lang-css.js +2 -0
- package/docs/api/scripts/prettify/prettify.js +28 -0
- package/docs/api/scripts/search.js +99 -0
- package/docs/api/state-manager.js.html +423 -0
- package/docs/api/styles/jsdoc.css +776 -0
- package/docs/api/styles/prettify.css +80 -0
- package/docs/examples.md +328 -0
- package/docs/getting-started.md +302 -0
- package/docs/installation.md +361 -0
- package/docs/templates.md +418 -0
- package/docs/validation-checklist.md +266 -0
- package/package.json +39 -12
- package/src/commands/cost.js +181 -0
- package/src/commands/create-story.js +283 -0
- package/src/commands/detect.js +104 -0
- package/src/commands/doctor.js +67 -0
- package/src/commands/generate.js +149 -0
- package/src/commands/init.js +69 -45
- package/src/commands/shard-spec.js +224 -0
- package/src/commands/sprint-status.js +250 -0
- package/src/commands/state.js +333 -0
- package/src/commands/sync.js +167 -0
- package/src/commands/update-pricing.js +206 -0
- package/src/commands/update.js +88 -13
- package/src/lib/complexity-analyzer.js +292 -0
- package/src/lib/cost-calculator.js +429 -0
- package/src/lib/design-system-generator.js +298 -0
- package/src/lib/state-manager.js +340 -0
- package/src/utils/file-copier.js +59 -0
- package/src/utils/version-checker.js +175 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standards Generator - Generates inferred-standards.md from detection results
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generate inferred standards
|
|
7
|
+
* @param {Object} detectionResults - Results from all detectors
|
|
8
|
+
* @returns {Promise<Object>} Generated standards
|
|
9
|
+
*/
|
|
10
|
+
export async function generateStandards(detectionResults) {
|
|
11
|
+
const { structure, config, conversation } = detectionResults;
|
|
12
|
+
|
|
13
|
+
const standards = {
|
|
14
|
+
markdown: generateMarkdown(structure, config, conversation),
|
|
15
|
+
recommendations: generateRecommendations(structure, config, conversation),
|
|
16
|
+
gaps: identifyGaps(structure, config)
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
return standards;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Generate markdown content for inferred-standards.md
|
|
24
|
+
*/
|
|
25
|
+
function generateMarkdown(structure, config, conversation) {
|
|
26
|
+
const sections = [];
|
|
27
|
+
|
|
28
|
+
// Header
|
|
29
|
+
sections.push(`# Inferred Standards (Auto-generated)`);
|
|
30
|
+
sections.push(``);
|
|
31
|
+
sections.push(`> ⚠️ This file is auto-generated by MORPH-SPEC detection system.`);
|
|
32
|
+
sections.push(`> Manual edits will be overwritten. Create custom standards in \`overrides.md\`.`);
|
|
33
|
+
sections.push(``);
|
|
34
|
+
sections.push(`**Generated:** ${new Date().toISOString()}`);
|
|
35
|
+
sections.push(``);
|
|
36
|
+
sections.push(`---`);
|
|
37
|
+
sections.push(``);
|
|
38
|
+
|
|
39
|
+
// Stack Summary
|
|
40
|
+
sections.push(`## 🎯 Project Stack`);
|
|
41
|
+
sections.push(``);
|
|
42
|
+
sections.push(`- **Type**: ${structure?.stack || 'unknown'}`);
|
|
43
|
+
sections.push(`- **Language**: ${config?.language || 'unknown'}`);
|
|
44
|
+
sections.push(`- **Version**: ${config?.version || 'unknown'}`);
|
|
45
|
+
sections.push(`- **Architecture**: ${structure?.architecture || 'unknown'}`);
|
|
46
|
+
if (structure?.uiLibrary) {
|
|
47
|
+
sections.push(`- **UI Library**: ${structure.uiLibrary}`);
|
|
48
|
+
}
|
|
49
|
+
sections.push(``);
|
|
50
|
+
|
|
51
|
+
// Technologies
|
|
52
|
+
if (config?.technologies?.length > 0) {
|
|
53
|
+
sections.push(`## 🛠️ Technologies Detected`);
|
|
54
|
+
sections.push(``);
|
|
55
|
+
config.technologies.forEach(tech => {
|
|
56
|
+
sections.push(`- ${tech}`);
|
|
57
|
+
});
|
|
58
|
+
sections.push(``);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Patterns
|
|
62
|
+
if (structure?.patterns?.length > 0) {
|
|
63
|
+
sections.push(`## 📐 Patterns in Use`);
|
|
64
|
+
sections.push(``);
|
|
65
|
+
structure.patterns.forEach(pattern => {
|
|
66
|
+
sections.push(`- ${pattern}`);
|
|
67
|
+
});
|
|
68
|
+
sections.push(``);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Coding Standards (inferred)
|
|
72
|
+
sections.push(`## 💻 Inferred Coding Standards`);
|
|
73
|
+
sections.push(``);
|
|
74
|
+
sections.push(generateCodingStandards(structure, config));
|
|
75
|
+
sections.push(``);
|
|
76
|
+
|
|
77
|
+
// Architecture Patterns
|
|
78
|
+
if (structure?.architecture !== 'unknown') {
|
|
79
|
+
sections.push(`## 🏗️ Architecture Pattern`);
|
|
80
|
+
sections.push(``);
|
|
81
|
+
sections.push(generateArchitectureSection(structure));
|
|
82
|
+
sections.push(``);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Dependencies
|
|
86
|
+
if (config?.dependencies?.length > 0) {
|
|
87
|
+
sections.push(`## 📦 Key Dependencies`);
|
|
88
|
+
sections.push(``);
|
|
89
|
+
const importantDeps = filterImportantDependencies(config.dependencies);
|
|
90
|
+
importantDeps.slice(0, 10).forEach(dep => {
|
|
91
|
+
sections.push(`- ${dep}`);
|
|
92
|
+
});
|
|
93
|
+
if (config.dependencies.length > 10) {
|
|
94
|
+
sections.push(`- ... and ${config.dependencies.length - 10} more`);
|
|
95
|
+
}
|
|
96
|
+
sections.push(``);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// User Preferences (from conversation)
|
|
100
|
+
if (conversation?.preferences && Object.keys(conversation.preferences).some(k => conversation.preferences[k])) {
|
|
101
|
+
sections.push(`## 🎨 User Preferences (from past decisions)`);
|
|
102
|
+
sections.push(``);
|
|
103
|
+
Object.entries(conversation.preferences).forEach(([key, value]) => {
|
|
104
|
+
if (value) {
|
|
105
|
+
sections.push(`- **${formatKey(key)}**: ${value}`);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
sections.push(``);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return sections.join('\n');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Generate coding standards section
|
|
116
|
+
*/
|
|
117
|
+
function generateCodingStandards(structure, config) {
|
|
118
|
+
if (config?.language === 'csharp') {
|
|
119
|
+
return `### C# / .NET Conventions
|
|
120
|
+
|
|
121
|
+
Based on detected patterns in your codebase:
|
|
122
|
+
|
|
123
|
+
- **Naming**: PascalCase for classes, camelCase for variables
|
|
124
|
+
- **Async**: Methods with \`Async\` suffix
|
|
125
|
+
- **DI**: Constructor injection (${structure.patterns.includes('Dependency Injection') ? '✅ detected' : '⚠️ not detected'})
|
|
126
|
+
- **Tests**: ${structure.patterns.includes('Unit Tests') ? '✅ Present' : '⚠️ Not detected'}
|
|
127
|
+
|
|
128
|
+
**Recommendation**: Refer to \`.morph/standards/coding.md\` for complete .NET standards.`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (config?.language === 'javascript') {
|
|
132
|
+
return `### JavaScript / TypeScript Conventions
|
|
133
|
+
|
|
134
|
+
Based on detected patterns in your codebase:
|
|
135
|
+
|
|
136
|
+
- **Package Manager**: ${config.packageManager}
|
|
137
|
+
- **Framework**: ${config.technologies.join(', ')}
|
|
138
|
+
|
|
139
|
+
**Recommendation**: Refer to framework standards for JavaScript best practices.`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return 'No specific coding standards inferred. Check framework standards for guidance.';
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Generate architecture section
|
|
147
|
+
*/
|
|
148
|
+
function generateArchitectureSection(structure) {
|
|
149
|
+
const { architecture } = structure;
|
|
150
|
+
|
|
151
|
+
const descriptions = {
|
|
152
|
+
'clean-architecture': `### Clean Architecture
|
|
153
|
+
|
|
154
|
+
Your project follows Clean Architecture pattern:
|
|
155
|
+
|
|
156
|
+
- ✅ **Domain** layer detected (core business logic)
|
|
157
|
+
- ✅ **Application** layer detected (use cases)
|
|
158
|
+
- ✅ **Infrastructure** layer detected (implementations)
|
|
159
|
+
|
|
160
|
+
**Key principles**:
|
|
161
|
+
- Domain has no dependencies
|
|
162
|
+
- Application depends on Domain
|
|
163
|
+
- Infrastructure implements Application interfaces`,
|
|
164
|
+
|
|
165
|
+
'cqrs': `### CQRS Pattern
|
|
166
|
+
|
|
167
|
+
Your project uses Command Query Responsibility Segregation:
|
|
168
|
+
|
|
169
|
+
- ✅ **Commands** detected (write operations)
|
|
170
|
+
- ✅ **Queries** detected (read operations)
|
|
171
|
+
|
|
172
|
+
**Key principles**:
|
|
173
|
+
- Separate read and write models
|
|
174
|
+
- Commands return void or Task
|
|
175
|
+
- Queries return data`,
|
|
176
|
+
|
|
177
|
+
'mvc': `### MVC Pattern
|
|
178
|
+
|
|
179
|
+
Your project uses Model-View-Controller pattern:
|
|
180
|
+
|
|
181
|
+
- ✅ **Controllers** detected
|
|
182
|
+
- ✅ **Models** detected
|
|
183
|
+
- ✅ **Views** detected`
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
return descriptions[architecture] || `Architecture: ${architecture}`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Generate recommendations
|
|
191
|
+
*/
|
|
192
|
+
function generateRecommendations(structure, config, conversation) {
|
|
193
|
+
const recommendations = [];
|
|
194
|
+
|
|
195
|
+
// Recommend based on stack
|
|
196
|
+
if (structure?.stack === 'blazor') {
|
|
197
|
+
if (!structure.uiLibrary) {
|
|
198
|
+
recommendations.push('Consider adding Fluent UI Blazor or MudBlazor for consistent UI components');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!structure.patterns.includes('AI Agents') && config?.technologies?.includes('Microsoft Agent Framework')) {
|
|
202
|
+
recommendations.push('Setup detected Agent Framework but no agents found - check .morph/standards/agent-framework-setup.md');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Recommend tests
|
|
207
|
+
if (!structure?.patterns?.includes('Unit Tests')) {
|
|
208
|
+
recommendations.push('No unit tests detected - consider adding test project');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Recommend DI
|
|
212
|
+
if (!structure?.patterns?.includes('Dependency Injection')) {
|
|
213
|
+
recommendations.push('Consider implementing Dependency Injection pattern');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Architecture recommendations
|
|
217
|
+
if (structure?.architecture === 'unknown' && structure?.folders?.hasServices) {
|
|
218
|
+
recommendations.push('Service layer detected but no clear architecture - consider Clean Architecture or CQRS');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return recommendations;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Identify gaps between framework and project
|
|
226
|
+
*/
|
|
227
|
+
function identifyGaps(structure, config) {
|
|
228
|
+
const gaps = [];
|
|
229
|
+
|
|
230
|
+
// Check for missing patterns
|
|
231
|
+
const expectedPatterns = {
|
|
232
|
+
blazor: ['Service Layer', 'Repository Pattern', 'Dependency Injection'],
|
|
233
|
+
nextjs: ['API Routes', 'Components'],
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const expected = expectedPatterns[structure?.stack];
|
|
237
|
+
if (expected) {
|
|
238
|
+
expected.forEach(pattern => {
|
|
239
|
+
if (!structure?.patterns?.includes(pattern)) {
|
|
240
|
+
gaps.push(`Missing: ${pattern}`);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return gaps;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Filter important dependencies
|
|
250
|
+
*/
|
|
251
|
+
function filterImportantDependencies(deps) {
|
|
252
|
+
const important = deps.filter(dep => {
|
|
253
|
+
// Framework packages
|
|
254
|
+
if (dep.includes('Microsoft.') || dep.includes('System.')) return true;
|
|
255
|
+
// UI libraries
|
|
256
|
+
if (dep.includes('Blazor') || dep.includes('FluentUI') || dep.includes('Mud')) return true;
|
|
257
|
+
// Common tools
|
|
258
|
+
if (dep.includes('Hangfire') || dep.includes('Serilog') || dep.includes('AutoMapper')) return true;
|
|
259
|
+
// AI/ML
|
|
260
|
+
if (dep.includes('Agents') || dep.includes('AI') || dep.includes('OpenAI')) return true;
|
|
261
|
+
return false;
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
return important.length > 0 ? important : deps;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Format key for display
|
|
269
|
+
*/
|
|
270
|
+
function formatKey(key) {
|
|
271
|
+
return key
|
|
272
|
+
.replace(/([A-Z])/g, ' $1')
|
|
273
|
+
.replace(/^./, str => str.toUpperCase())
|
|
274
|
+
.trim();
|
|
275
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
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
|
+
const patterns = {
|
|
54
|
+
blazor: [
|
|
55
|
+
'**/*.razor',
|
|
56
|
+
'**/Pages/**/*.razor',
|
|
57
|
+
'**/Components/**/*.razor'
|
|
58
|
+
],
|
|
59
|
+
nextjs: [
|
|
60
|
+
'pages/**/*.tsx',
|
|
61
|
+
'app/**/*.tsx',
|
|
62
|
+
'next.config.js',
|
|
63
|
+
'next.config.mjs'
|
|
64
|
+
],
|
|
65
|
+
shopify: [
|
|
66
|
+
'**/*.liquid',
|
|
67
|
+
'sections/**/*.liquid',
|
|
68
|
+
'templates/**/*.liquid'
|
|
69
|
+
],
|
|
70
|
+
dotnet: [
|
|
71
|
+
'**/*.csproj',
|
|
72
|
+
'**/Program.cs'
|
|
73
|
+
]
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
for (const [stack, globs] of Object.entries(patterns)) {
|
|
77
|
+
for (const pattern of globs) {
|
|
78
|
+
const files = await glob(pattern, { cwd: projectPath, nodir: true });
|
|
79
|
+
if (files.length > 0) {
|
|
80
|
+
return stack;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return 'unknown';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Detect architecture pattern
|
|
90
|
+
*/
|
|
91
|
+
async function detectArchitecture(projectPath) {
|
|
92
|
+
const checks = [
|
|
93
|
+
{
|
|
94
|
+
pattern: 'clean-architecture',
|
|
95
|
+
indicators: [
|
|
96
|
+
existsSync(join(projectPath, 'src', 'Application')),
|
|
97
|
+
existsSync(join(projectPath, 'src', 'Domain')),
|
|
98
|
+
existsSync(join(projectPath, 'src', 'Infrastructure'))
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
pattern: 'cqrs',
|
|
103
|
+
indicators: [
|
|
104
|
+
await glob('**/Commands/**/*.cs', { cwd: projectPath }).then(f => f.length > 0),
|
|
105
|
+
await glob('**/Queries/**/*.cs', { cwd: projectPath }).then(f => f.length > 0)
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
pattern: 'mvc',
|
|
110
|
+
indicators: [
|
|
111
|
+
existsSync(join(projectPath, 'Controllers')),
|
|
112
|
+
existsSync(join(projectPath, 'Models')),
|
|
113
|
+
existsSync(join(projectPath, 'Views'))
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
for (const { pattern, indicators } of checks) {
|
|
119
|
+
if (indicators.every(Boolean)) {
|
|
120
|
+
return pattern;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return 'unknown';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Detect UI library (Blazor projects)
|
|
129
|
+
*/
|
|
130
|
+
async function detectUILibrary(projectPath) {
|
|
131
|
+
const csprojFiles = await glob('**/*.csproj', { cwd: projectPath });
|
|
132
|
+
|
|
133
|
+
for (const csprojFile of csprojFiles) {
|
|
134
|
+
const content = await import('fs').then(fs =>
|
|
135
|
+
fs.promises.readFile(join(projectPath, csprojFile), 'utf8')
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
if (content.includes('FluentUI.Blazor') || content.includes('Microsoft.FluentUI.AspNetCore.Components')) {
|
|
139
|
+
return 'fluent-ui';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (content.includes('MudBlazor')) {
|
|
143
|
+
return 'mudblazor';
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Detect common patterns
|
|
152
|
+
*/
|
|
153
|
+
async function detectPatterns(projectPath) {
|
|
154
|
+
const patterns = [];
|
|
155
|
+
|
|
156
|
+
// Check for specific patterns
|
|
157
|
+
const checks = [
|
|
158
|
+
{
|
|
159
|
+
name: 'Repository Pattern',
|
|
160
|
+
glob: '**/*Repository.cs'
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: 'Service Layer',
|
|
164
|
+
glob: '**/*Service.cs'
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: 'DTOs',
|
|
168
|
+
glob: '**/*Dto.cs'
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: 'Entity Framework',
|
|
172
|
+
glob: '**/Migrations/**/*.cs'
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: 'Dependency Injection',
|
|
176
|
+
glob: '**/DependencyInjection.cs'
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: 'Hangfire Jobs',
|
|
180
|
+
glob: '**/*Job.cs'
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: 'AI Agents',
|
|
184
|
+
glob: '**/*Agent.cs'
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: 'Unit Tests',
|
|
188
|
+
glob: '**/*.Tests/**/*.cs'
|
|
189
|
+
}
|
|
190
|
+
];
|
|
191
|
+
|
|
192
|
+
for (const { name, glob: pattern } of checks) {
|
|
193
|
+
const files = await glob(pattern, { cwd: projectPath, nodir: true });
|
|
194
|
+
if (files.length > 0) {
|
|
195
|
+
patterns.push(name);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return patterns;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Analyze folder structure
|
|
204
|
+
*/
|
|
205
|
+
async function analyzeFolders(projectPath) {
|
|
206
|
+
const folders = {
|
|
207
|
+
hasPages: false,
|
|
208
|
+
hasComponents: false,
|
|
209
|
+
hasServices: false,
|
|
210
|
+
hasRepositories: false,
|
|
211
|
+
hasAgents: false
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
folders.hasPages = await glob('**/Pages/**/*', { cwd: projectPath }).then(f => f.length > 0);
|
|
215
|
+
folders.hasComponents = await glob('**/Components/**/*', { cwd: projectPath }).then(f => f.length > 0);
|
|
216
|
+
folders.hasServices = await glob('**/Services/**/*', { cwd: projectPath }).then(f => f.length > 0);
|
|
217
|
+
folders.hasRepositories = await glob('**/Repositories/**/*', { cwd: projectPath }).then(f => f.length > 0);
|
|
218
|
+
folders.hasAgents = await glob('**/Agents/**/*', { cwd: projectPath }).then(f => f.length > 0);
|
|
219
|
+
|
|
220
|
+
return folders;
|
|
221
|
+
}
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# MORPH-SPEC API Documentation
|
|
2
|
+
|
|
3
|
+
This directory contains the API documentation for MORPH-SPEC Framework libraries.
|
|
4
|
+
|
|
5
|
+
## Available Documentation
|
|
6
|
+
|
|
7
|
+
### API Reference (`/api`)
|
|
8
|
+
|
|
9
|
+
Comprehensive API documentation generated from JSDoc comments in the source code.
|
|
10
|
+
|
|
11
|
+
**Libraries Documented:**
|
|
12
|
+
- **state-manager** - Feature state management, agents, checkpoints, outputs tracking
|
|
13
|
+
- **cost-calculator** - Azure infrastructure cost calculation from Bicep files
|
|
14
|
+
- **design-system-generator** - UI design system parsing and theme generation
|
|
15
|
+
|
|
16
|
+
**View Documentation:**
|
|
17
|
+
- Open `api/index.html` in your browser
|
|
18
|
+
- Or run `npm run docs:serve` to start a local server at http://localhost:8080
|
|
19
|
+
|
|
20
|
+
## Running Tests
|
|
21
|
+
|
|
22
|
+
### Run Tests
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm test
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Runs all 85 unit tests with 100% pass rate.
|
|
29
|
+
|
|
30
|
+
### Run Tests with Coverage
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm run test:coverage
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Generates coverage report (HTML + text + lcov). **Current coverage: 98.03%**
|
|
37
|
+
|
|
38
|
+
- **Statements:** 98.03%
|
|
39
|
+
- **Branches:** 88.74%
|
|
40
|
+
- **Functions:** 100%
|
|
41
|
+
- **Lines:** 98.03%
|
|
42
|
+
|
|
43
|
+
Coverage reports are generated in `coverage/` directory.
|
|
44
|
+
|
|
45
|
+
### Coverage Summary Only
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm run test:coverage:summary
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Shows text summary without generating HTML report.
|
|
52
|
+
|
|
53
|
+
## Generating Documentation
|
|
54
|
+
|
|
55
|
+
### Build API Docs
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm run docs
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Generates HTML documentation from JSDoc comments in `src/lib/` to `docs/api/`.
|
|
62
|
+
|
|
63
|
+
### Watch Mode
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm run docs:watch
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Automatically regenerates documentation when source files change.
|
|
70
|
+
|
|
71
|
+
### Serve Locally
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npm run docs:serve
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Starts a local HTTP server at http://localhost:8080 to view documentation.
|
|
78
|
+
|
|
79
|
+
## Documentation Standards
|
|
80
|
+
|
|
81
|
+
### JSDoc Format
|
|
82
|
+
|
|
83
|
+
All library functions in `src/lib/` use JSDoc comments with:
|
|
84
|
+
|
|
85
|
+
- **@param** - Parameter descriptions with types
|
|
86
|
+
- **@returns** - Return value description with type
|
|
87
|
+
- **@throws** - Exceptions that may be thrown
|
|
88
|
+
- **@example** - Usage examples (when applicable)
|
|
89
|
+
|
|
90
|
+
**Example:**
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
/**
|
|
94
|
+
* Calculate cost of a single resource
|
|
95
|
+
* @param {Object} resource - Resource object with type, sku, minReplicas
|
|
96
|
+
* @param {string} resource.type - Azure resource type
|
|
97
|
+
* @param {string} resource.sku - SKU name
|
|
98
|
+
* @param {number} [resource.minReplicas] - Minimum replicas (Container Apps)
|
|
99
|
+
* @returns {Object} Cost result with cost, warning, estimated, details
|
|
100
|
+
* @throws {Error} If resource type is invalid
|
|
101
|
+
*/
|
|
102
|
+
export function calculateResourceCost(resource) {
|
|
103
|
+
// Implementation
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Module-Level Documentation
|
|
108
|
+
|
|
109
|
+
Each library file starts with a block comment describing:
|
|
110
|
+
|
|
111
|
+
- Purpose and responsibilities
|
|
112
|
+
- Usage context (CLI commands, internal automation)
|
|
113
|
+
- Key features
|
|
114
|
+
|
|
115
|
+
**Example:**
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
/**
|
|
119
|
+
* MORPH-SPEC State Manager Library
|
|
120
|
+
*
|
|
121
|
+
* Manages state.json for tracking features, progress, agents, and checkpoints.
|
|
122
|
+
* Used both by CLI commands and internal automation.
|
|
123
|
+
*/
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Updating Documentation
|
|
127
|
+
|
|
128
|
+
1. **Update JSDoc comments** in source files (`src/lib/*.js`)
|
|
129
|
+
2. **Regenerate docs:** `npm run docs`
|
|
130
|
+
3. **Review:** Open `docs/api/index.html`
|
|
131
|
+
4. **Commit:** Include updated `docs/api/` in your commit
|
|
132
|
+
|
|
133
|
+
## Template
|
|
134
|
+
|
|
135
|
+
Documentation is generated using the [docdash](https://github.com/clenemt/docdash) template for JSDoc, configured in `jsdoc.json`.
|
|
136
|
+
|
|
137
|
+
**Features:**
|
|
138
|
+
- Clean, modern design
|
|
139
|
+
- Full-text search
|
|
140
|
+
- Responsive layout
|
|
141
|
+
- Syntax highlighting
|
|
142
|
+
- Type definitions
|
|
143
|
+
- Source code links
|
|
144
|
+
|
|
145
|
+
## Links
|
|
146
|
+
|
|
147
|
+
- **GitHub:** https://github.com/lucasPolymorphism/morph-spec-framework
|
|
148
|
+
- **npm:** https://www.npmjs.com/package/@polymorphism-tech/morph-spec
|
|
149
|
+
- **Website:** https://polymorphism.com.br/morph-spec
|