@polymorphism-tech/morph-spec 2.3.0 → 3.0.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 +446 -1730
- package/README.md +515 -516
- package/bin/morph-spec.js +366 -294
- package/bin/task-manager.js +429 -368
- package/bin/validate.js +369 -268
- package/content/.claude/commands/morph-apply.md +221 -158
- package/content/.claude/commands/morph-deploy.md +529 -0
- package/content/.claude/commands/morph-preflight.md +227 -0
- package/content/.claude/commands/morph-proposal.md +122 -101
- package/content/.claude/commands/morph-status.md +86 -86
- package/content/.claude/commands/morph-troubleshoot.md +122 -0
- package/content/.claude/skills/infra/azure-deploy-specialist.md +699 -0
- package/content/.claude/skills/level-0-meta/README.md +7 -0
- package/content/.claude/skills/level-0-meta/code-review.md +226 -0
- package/content/.claude/skills/level-0-meta/morph-checklist.md +117 -0
- package/content/.claude/skills/level-0-meta/simulation-checklist.md +77 -0
- package/content/.claude/skills/level-1-workflows/README.md +7 -0
- package/content/.claude/skills/level-1-workflows/morph-replicate.md +213 -0
- package/content/.claude/{commands/morph-clarify.md → skills/level-1-workflows/phase-clarify.md} +131 -184
- package/content/.claude/{commands/morph-design.md → skills/level-1-workflows/phase-design.md} +213 -275
- package/content/.claude/skills/level-1-workflows/phase-setup.md +106 -0
- package/content/.claude/skills/level-1-workflows/phase-tasks.md +164 -0
- package/content/.claude/{commands/morph-uiux.md → skills/level-1-workflows/phase-uiux.md} +169 -211
- package/content/.claude/skills/level-2-domains/README.md +14 -0
- package/content/.claude/skills/level-2-domains/ai-agents/ai-system-architect.md +192 -0
- package/content/.claude/skills/{specialists → level-2-domains/architecture}/po-pm-advisor.md +197 -197
- package/content/.claude/skills/level-2-domains/architecture/standards-architect.md +156 -0
- package/content/.claude/skills/level-2-domains/backend/dotnet-senior.md +287 -0
- package/content/.claude/skills/level-2-domains/backend/ef-modeler.md +113 -0
- package/content/.claude/skills/level-2-domains/backend/hangfire-orchestrator.md +126 -0
- package/content/.claude/skills/level-2-domains/backend/ms-agent-expert.md +109 -0
- package/content/.claude/skills/level-2-domains/frontend/blazor-builder.md +210 -0
- package/content/.claude/skills/level-2-domains/frontend/nextjs-expert.md +154 -0
- package/content/.claude/skills/level-2-domains/frontend/ui-ux-designer.md +191 -0
- package/content/.claude/skills/{specialists → level-2-domains/infrastructure}/azure-architect.md +142 -142
- package/content/.claude/skills/level-2-domains/infrastructure/bicep-architect.md +126 -0
- package/content/.claude/skills/level-2-domains/infrastructure/container-specialist.md +131 -0
- package/content/.claude/skills/level-2-domains/infrastructure/devops-engineer.md +119 -0
- package/content/.claude/skills/level-2-domains/integrations/asaas-financial.md +130 -0
- package/content/.claude/skills/level-2-domains/integrations/azure-identity.md +142 -0
- package/content/.claude/skills/level-2-domains/integrations/clerk-auth.md +108 -0
- package/content/.claude/skills/level-2-domains/integrations/resend-email.md +119 -0
- package/content/.claude/skills/level-2-domains/quality/code-analyzer.md +235 -0
- package/content/.claude/skills/level-2-domains/quality/testing-specialist.md +126 -0
- package/content/.claude/skills/level-3-technologies/README.md +7 -0
- package/content/.claude/skills/level-4-patterns/README.md +7 -0
- package/content/.claude/skills/specialists/prompt-engineer.md +189 -0
- package/content/.claude/skills/specialists/seo-growth-hacker.md +320 -0
- package/content/.morph/config/agents.json +762 -242
- package/content/.morph/config/config.template.json +122 -108
- package/content/.morph/docs/workflows/design-impl.md +37 -0
- package/content/.morph/docs/workflows/enforcement-pipeline.md +668 -0
- package/content/.morph/docs/workflows/fast-track.md +29 -0
- package/content/.morph/docs/workflows/full-morph.md +76 -0
- package/content/.morph/docs/workflows/standard.md +44 -0
- package/content/.morph/docs/workflows/ui-refresh.md +39 -0
- package/content/.morph/examples/scheduled-reports/decisions.md +158 -0
- package/content/.morph/examples/scheduled-reports/proposal.md +95 -0
- package/content/.morph/examples/scheduled-reports/spec.md +267 -0
- package/content/.morph/hooks/README.md +348 -239
- package/content/.morph/hooks/pre-commit-agents.sh +24 -24
- package/content/.morph/hooks/task-completed.js +73 -0
- package/content/.morph/hooks/teammate-idle.js +68 -0
- package/content/.morph/schemas/tasks.schema.json +220 -0
- package/content/.morph/standards/agent-framework-blazor-ui.md +359 -0
- package/content/.morph/standards/agent-framework-production.md +410 -0
- package/content/.morph/standards/agent-framework-setup.md +413 -453
- package/content/.morph/standards/agent-framework-workflows.md +349 -0
- package/content/.morph/standards/agent-teams-workflow.md +474 -0
- package/content/.morph/standards/architecture.md +325 -325
- package/content/.morph/standards/azure.md +605 -379
- package/content/.morph/standards/dotnet10-migration.md +520 -494
- package/content/.morph/templates/CONTEXT-FEATURE.md +276 -0
- package/content/.morph/templates/CONTEXT.md +170 -0
- package/content/.morph/templates/agent.cs +163 -172
- package/content/.morph/templates/clarify-questions.md +159 -0
- package/content/.morph/templates/contracts/Commands.cs +74 -0
- package/content/.morph/templates/contracts/Entities.cs +25 -0
- package/content/.morph/templates/contracts/Queries.cs +74 -0
- package/content/.morph/templates/contracts/README.md +74 -0
- package/content/.morph/templates/decisions.md +123 -106
- package/content/.morph/templates/infra/azure-pipelines-deploy.yml +480 -0
- package/content/.morph/templates/infra/deploy-checklist.md +426 -0
- package/content/.morph/templates/proposal.md +141 -155
- package/content/.morph/templates/recap.md +94 -105
- package/content/.morph/templates/simulation.md +353 -0
- package/content/.morph/templates/spec.md +149 -148
- package/content/.morph/templates/state.template.json +222 -222
- package/content/.morph/templates/tasks.md +257 -235
- package/content/.morph/templates/ui-components.md +362 -276
- package/content/CLAUDE.md +150 -442
- package/detectors/structure-detector.js +245 -250
- package/docs/README.md +144 -149
- package/docs/getting-started.md +301 -302
- package/docs/installation.md +361 -361
- package/docs/validation-checklist.md +265 -266
- package/package.json +80 -80
- package/src/commands/advance-phase.js +266 -0
- package/src/commands/analyze-blazor-concurrency.js +193 -0
- package/src/commands/deploy.js +780 -0
- package/src/commands/detect-agents.js +167 -0
- package/src/commands/doctor.js +356 -280
- package/src/commands/generate-context.js +40 -0
- package/src/commands/init.js +258 -245
- package/src/commands/lint-fluent.js +352 -0
- package/src/commands/rollback-phase.js +185 -0
- package/src/commands/session-summary.js +291 -0
- package/src/commands/task.js +78 -75
- package/src/commands/troubleshoot.js +222 -0
- package/src/commands/update.js +192 -159
- package/src/commands/validate-blazor-state.js +210 -0
- package/src/commands/validate-blazor.js +156 -0
- package/src/commands/validate-css.js +84 -0
- package/src/commands/validate-phase.js +221 -0
- package/src/lib/blazor-concurrency-analyzer.js +288 -0
- package/src/lib/blazor-state-validator.js +291 -0
- package/src/lib/blazor-validator.js +374 -0
- package/src/lib/complexity-analyzer.js +441 -292
- package/src/lib/context-generator.js +513 -0
- package/src/lib/continuous-validator.js +421 -440
- package/src/lib/css-validator.js +352 -0
- package/src/lib/decision-constraint-loader.js +109 -0
- package/src/lib/design-system-detector.js +187 -0
- package/src/lib/design-system-scaffolder.js +299 -0
- package/src/lib/hook-executor.js +256 -0
- package/src/lib/recap-generator.js +205 -0
- package/src/lib/spec-validator.js +258 -0
- package/src/lib/standards-context-injector.js +287 -0
- package/src/lib/state-manager.js +397 -340
- package/src/lib/team-orchestrator.js +322 -0
- package/src/lib/troubleshoot-grep.js +194 -0
- package/src/lib/troubleshoot-index.js +144 -0
- package/src/lib/validation-runner.js +283 -0
- package/src/lib/validators/contract-compliance-validator.js +273 -0
- package/src/lib/validators/design-system-validator.js +231 -0
- package/src/utils/file-copier.js +187 -139
- package/content/.claude/commands/morph-costs.md +0 -206
- package/content/.claude/commands/morph-setup.md +0 -100
- package/content/.claude/commands/morph-tasks.md +0 -319
- package/content/.claude/skills/infra/bicep-architect.md +0 -419
- package/content/.claude/skills/infra/container-specialist.md +0 -437
- package/content/.claude/skills/infra/devops-engineer.md +0 -405
- package/content/.claude/skills/integrations/asaas-financial.md +0 -333
- package/content/.claude/skills/integrations/azure-identity.md +0 -309
- package/content/.claude/skills/integrations/clerk-auth.md +0 -290
- package/content/.claude/skills/specialists/ai-system-architect.md +0 -604
- package/content/.claude/skills/specialists/cost-guardian.md +0 -110
- package/content/.claude/skills/specialists/ef-modeler.md +0 -211
- package/content/.claude/skills/specialists/hangfire-orchestrator.md +0 -255
- package/content/.claude/skills/specialists/ms-agent-expert.md +0 -263
- package/content/.claude/skills/specialists/standards-architect.md +0 -78
- package/content/.claude/skills/specialists/ui-ux-designer.md +0 -1100
- package/content/.claude/skills/stacks/dotnet-blazor.md +0 -606
- package/content/.claude/skills/stacks/dotnet-nextjs.md +0 -402
- package/content/.claude/skills/stacks/shopify.md +0 -445
- package/content/.morph/config/azure-pricing.json +0 -70
- package/content/.morph/config/azure-pricing.schema.json +0 -50
- package/content/.morph/hooks/pre-commit-costs.sh +0 -91
- package/docs/api/cost-calculator.js.html +0 -513
- package/docs/api/design-system-generator.js.html +0 -382
- package/docs/api/global.html +0 -5263
- package/docs/api/index.html +0 -96
- package/docs/api/state-manager.js.html +0 -423
- package/src/commands/cost.js +0 -181
- package/src/commands/update-pricing.js +0 -206
- package/src/lib/cost-calculator.js +0 -429
package/package.json
CHANGED
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@polymorphism-tech/morph-spec",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "MORPH-SPEC v2.0: AI-First development framework with .NET 10, Microsoft Agent Framework, and Fluent UI Blazor",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"claude-code",
|
|
7
|
-
"claude",
|
|
8
|
-
"ai-coding",
|
|
9
|
-
"ai-first",
|
|
10
|
-
"spec-driven",
|
|
11
|
-
"dotnet",
|
|
12
|
-
"dotnet10",
|
|
13
|
-
"blazor",
|
|
14
|
-
"agent-framework",
|
|
15
|
-
"fluent-ui",
|
|
16
|
-
"framework",
|
|
17
|
-
"developer-tools",
|
|
18
|
-
"morph",
|
|
19
|
-
"polymorphism",
|
|
20
|
-
"micro-saas"
|
|
21
|
-
],
|
|
22
|
-
"main": "src/index.js",
|
|
23
|
-
"bin": {
|
|
24
|
-
"morph-spec": "bin/morph-spec.js"
|
|
25
|
-
},
|
|
26
|
-
"files": [
|
|
27
|
-
"bin/",
|
|
28
|
-
"src/",
|
|
29
|
-
"scripts/",
|
|
30
|
-
"detectors/",
|
|
31
|
-
"content/",
|
|
32
|
-
"docs/",
|
|
33
|
-
"README.md",
|
|
34
|
-
"LICENSE",
|
|
35
|
-
"CLAUDE.md"
|
|
36
|
-
],
|
|
37
|
-
"type": "module",
|
|
38
|
-
"engines": {
|
|
39
|
-
"node": ">=18.0.0"
|
|
40
|
-
},
|
|
41
|
-
"scripts": {
|
|
42
|
-
"postinstall": "node scripts/postinstall.js",
|
|
43
|
-
"start": "node bin/morph-spec.js",
|
|
44
|
-
"test": "node --test",
|
|
45
|
-
"test:coverage": "c8 --reporter=text --reporter=html --reporter=lcov node --test",
|
|
46
|
-
"test:coverage:summary": "c8 --reporter=text-summary node --test",
|
|
47
|
-
"docs": "jsdoc -c jsdoc.json",
|
|
48
|
-
"docs:watch": "jsdoc -c jsdoc.json --watch",
|
|
49
|
-
"docs:serve": "npx http-server docs/api -p 8080 -o"
|
|
50
|
-
},
|
|
51
|
-
"dependencies": {
|
|
52
|
-
"ajv": "^8.12.0",
|
|
53
|
-
"ajv-formats": "^3.0.1",
|
|
54
|
-
"chalk": "^5.3.0",
|
|
55
|
-
"commander": "^12.0.0",
|
|
56
|
-
"fs-extra": "^11.2.0",
|
|
57
|
-
"glob": "^10.3.0",
|
|
58
|
-
"ora": "^8.0.0",
|
|
59
|
-
"yaml": "^2.3.4"
|
|
60
|
-
},
|
|
61
|
-
"repository": {
|
|
62
|
-
"type": "git",
|
|
63
|
-
"url": "git+https://github.com/lucasPolymorphism/morph-spec-framework.git"
|
|
64
|
-
},
|
|
65
|
-
"author": "Polymorphism Tech <contact@polymorphism.com.br>",
|
|
66
|
-
"license": "SEE LICENSE IN LICENSE",
|
|
67
|
-
"homepage": "https://polymorphism.com.br/morph-spec",
|
|
68
|
-
"bugs": {
|
|
69
|
-
"email": "support@polymorphism.com.br"
|
|
70
|
-
},
|
|
71
|
-
"private": false,
|
|
72
|
-
"publishConfig": {
|
|
73
|
-
"access": "public"
|
|
74
|
-
},
|
|
75
|
-
"devDependencies": {
|
|
76
|
-
"c8": "^10.1.3",
|
|
77
|
-
"docdash": "^2.0.2",
|
|
78
|
-
"jsdoc": "^4.0.5"
|
|
79
|
-
}
|
|
80
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@polymorphism-tech/morph-spec",
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "MORPH-SPEC v2.0: AI-First development framework with .NET 10, Microsoft Agent Framework, and Fluent UI Blazor",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"claude-code",
|
|
7
|
+
"claude",
|
|
8
|
+
"ai-coding",
|
|
9
|
+
"ai-first",
|
|
10
|
+
"spec-driven",
|
|
11
|
+
"dotnet",
|
|
12
|
+
"dotnet10",
|
|
13
|
+
"blazor",
|
|
14
|
+
"agent-framework",
|
|
15
|
+
"fluent-ui",
|
|
16
|
+
"framework",
|
|
17
|
+
"developer-tools",
|
|
18
|
+
"morph",
|
|
19
|
+
"polymorphism",
|
|
20
|
+
"micro-saas"
|
|
21
|
+
],
|
|
22
|
+
"main": "src/index.js",
|
|
23
|
+
"bin": {
|
|
24
|
+
"morph-spec": "bin/morph-spec.js"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"bin/",
|
|
28
|
+
"src/",
|
|
29
|
+
"scripts/",
|
|
30
|
+
"detectors/",
|
|
31
|
+
"content/",
|
|
32
|
+
"docs/",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE",
|
|
35
|
+
"CLAUDE.md"
|
|
36
|
+
],
|
|
37
|
+
"type": "module",
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=18.0.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"postinstall": "node scripts/postinstall.js",
|
|
43
|
+
"start": "node bin/morph-spec.js",
|
|
44
|
+
"test": "node --test",
|
|
45
|
+
"test:coverage": "c8 --reporter=text --reporter=html --reporter=lcov node --test",
|
|
46
|
+
"test:coverage:summary": "c8 --reporter=text-summary node --test",
|
|
47
|
+
"docs": "jsdoc -c jsdoc.json",
|
|
48
|
+
"docs:watch": "jsdoc -c jsdoc.json --watch",
|
|
49
|
+
"docs:serve": "npx http-server docs/api -p 8080 -o"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"ajv": "^8.12.0",
|
|
53
|
+
"ajv-formats": "^3.0.1",
|
|
54
|
+
"chalk": "^5.3.0",
|
|
55
|
+
"commander": "^12.0.0",
|
|
56
|
+
"fs-extra": "^11.2.0",
|
|
57
|
+
"glob": "^10.3.0",
|
|
58
|
+
"ora": "^8.0.0",
|
|
59
|
+
"yaml": "^2.3.4"
|
|
60
|
+
},
|
|
61
|
+
"repository": {
|
|
62
|
+
"type": "git",
|
|
63
|
+
"url": "git+https://github.com/lucasPolymorphism/morph-spec-framework.git"
|
|
64
|
+
},
|
|
65
|
+
"author": "Polymorphism Tech <contact@polymorphism.com.br>",
|
|
66
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
67
|
+
"homepage": "https://polymorphism.com.br/morph-spec",
|
|
68
|
+
"bugs": {
|
|
69
|
+
"email": "support@polymorphism.com.br"
|
|
70
|
+
},
|
|
71
|
+
"private": false,
|
|
72
|
+
"publishConfig": {
|
|
73
|
+
"access": "public"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"c8": "^10.1.3",
|
|
77
|
+
"docdash": "^2.0.2",
|
|
78
|
+
"jsdoc": "^4.0.5"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MORPH-SPEC Phase Advance Command
|
|
3
|
+
*
|
|
4
|
+
* Validates current phase → advances to next → shows requirements.
|
|
5
|
+
* Replaces the two-step `state set` + `validate-phase` dance.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* morph-spec phase advance <feature>
|
|
9
|
+
* morph-spec phase advance <feature> --skip-optional
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import chalk from 'chalk';
|
|
13
|
+
import { loadState, saveState, getFeature } from '../lib/state-manager.js';
|
|
14
|
+
import { PHASES, validatePhase } from './validate-phase.js';
|
|
15
|
+
import { detectDesignSystem, hasUIAgentsActive } from '../lib/design-system-detector.js';
|
|
16
|
+
import { validateSpec } from '../lib/spec-validator.js';
|
|
17
|
+
|
|
18
|
+
// Phase order for advancing (skips optional phases unless active)
|
|
19
|
+
const PHASE_ORDER = ['proposal', 'setup', 'uiux', 'design', 'clarify', 'tasks', 'implement', 'sync'];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get the next phase after the current one
|
|
23
|
+
*/
|
|
24
|
+
function getNextPhase(currentPhase, feature, skipOptional) {
|
|
25
|
+
const currentIndex = PHASE_ORDER.indexOf(currentPhase);
|
|
26
|
+
if (currentIndex === -1 || currentIndex >= PHASE_ORDER.length - 1) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for (let i = currentIndex + 1; i < PHASE_ORDER.length; i++) {
|
|
31
|
+
const candidate = PHASE_ORDER[i];
|
|
32
|
+
const phaseDef = PHASES[candidate];
|
|
33
|
+
|
|
34
|
+
// Skip optional phases if flag set or no relevant agents/outputs
|
|
35
|
+
if (phaseDef?.optional && skipOptional) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Skip uiux if no UI agents are active
|
|
40
|
+
if (candidate === 'uiux') {
|
|
41
|
+
const uiAgents = ['blazor-builder', 'uiux-designer'];
|
|
42
|
+
const hasUiAgent = feature.activeAgents?.some(a => uiAgents.includes(a));
|
|
43
|
+
if (!hasUiAgent && skipOptional !== false) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Skip sync for simple workflows
|
|
49
|
+
if (candidate === 'sync') {
|
|
50
|
+
const isSimple = feature.workflow === 'fast-track';
|
|
51
|
+
if (isSimple && skipOptional !== false) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return candidate;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Main command handler
|
|
64
|
+
*/
|
|
65
|
+
export async function advancePhaseCommand(feature, options = {}) {
|
|
66
|
+
console.log(chalk.cyan('\n╔════════════════════════════════════════════════╗'));
|
|
67
|
+
console.log(chalk.cyan('║ MORPH-SPEC PHASE ADVANCE ║'));
|
|
68
|
+
console.log(chalk.cyan('╚════════════════════════════════════════════════╝\n'));
|
|
69
|
+
|
|
70
|
+
// Get current feature state
|
|
71
|
+
const featureData = getFeature(feature);
|
|
72
|
+
if (!featureData) {
|
|
73
|
+
console.log(chalk.red(`✗ Feature not found: ${feature}`));
|
|
74
|
+
console.log(chalk.yellow(` Run: morph-spec state set ${feature} phase proposal`));
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const currentPhase = featureData.phase;
|
|
79
|
+
const currentPhaseDef = PHASES[currentPhase];
|
|
80
|
+
|
|
81
|
+
console.log(chalk.gray('Feature:'), feature);
|
|
82
|
+
console.log(chalk.gray('Current Phase:'), currentPhaseDef?.name || currentPhase);
|
|
83
|
+
console.log(chalk.gray('Workflow:'), featureData.workflow || 'auto');
|
|
84
|
+
|
|
85
|
+
// Determine next phase
|
|
86
|
+
const nextPhase = getNextPhase(currentPhase, featureData, options.skipOptional);
|
|
87
|
+
|
|
88
|
+
if (!nextPhase) {
|
|
89
|
+
console.log(chalk.green('\n✓ Feature is at the final phase!'));
|
|
90
|
+
if (currentPhase === 'implement' || currentPhase === 'sync') {
|
|
91
|
+
console.log(chalk.green(' Consider running: morph-spec generate recap ' + feature));
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const nextPhaseDef = PHASES[nextPhase];
|
|
97
|
+
console.log(chalk.gray('Next Phase:'), nextPhaseDef.name);
|
|
98
|
+
|
|
99
|
+
// Validate that current phase requirements are met before advancing
|
|
100
|
+
const validation = validatePhase(feature, nextPhase);
|
|
101
|
+
|
|
102
|
+
if (!validation.valid) {
|
|
103
|
+
console.log(chalk.red('\n✗ Cannot advance — missing requirements:'));
|
|
104
|
+
validation.missingOutputs.forEach(output => {
|
|
105
|
+
console.log(chalk.red(` - ${output}`));
|
|
106
|
+
});
|
|
107
|
+
console.log(chalk.yellow(`\n Complete these before advancing to ${nextPhaseDef.name}`));
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (validation.stateWarning) {
|
|
112
|
+
console.log(chalk.yellow(`\n⚠️ ${validation.stateWarning}`));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Gate: Validate spec.md and contracts.cs when advancing from design phase
|
|
116
|
+
if (currentPhase === 'design' && (nextPhase === 'clarify' || nextPhase === 'tasks')) {
|
|
117
|
+
const specValidation = await validateSpec('.', feature);
|
|
118
|
+
|
|
119
|
+
if (specValidation.errors > 0) {
|
|
120
|
+
console.log(chalk.red('\n✗ Spec validation failed — fix errors before advancing:'));
|
|
121
|
+
specValidation.issues.filter(i => i.level === 'error').forEach(issue => {
|
|
122
|
+
console.log(chalk.red(` - ${issue.message}`));
|
|
123
|
+
console.log(chalk.yellow(` → ${issue.solution}`));
|
|
124
|
+
});
|
|
125
|
+
console.log('');
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (specValidation.warnings > 0 && !options.skipWarnings) {
|
|
130
|
+
console.log(chalk.yellow('\n⚠️ Spec validation warnings:'));
|
|
131
|
+
specValidation.issues.filter(i => i.level === 'warning').forEach(issue => {
|
|
132
|
+
console.log(chalk.yellow(` - ${issue.message}`));
|
|
133
|
+
});
|
|
134
|
+
console.log(chalk.gray('\n (Use --skip-warnings to ignore warnings)\n'));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Gate: Check design system when advancing to implement with UI agents
|
|
139
|
+
if (nextPhase === 'implement') {
|
|
140
|
+
const gateResult = designSystemGate(feature);
|
|
141
|
+
|
|
142
|
+
if (gateResult.blocked) {
|
|
143
|
+
console.log(chalk.red(`\n✗ ${gateResult.message}`));
|
|
144
|
+
console.log('');
|
|
145
|
+
gateResult.solution.forEach(line => {
|
|
146
|
+
if (line === '') {
|
|
147
|
+
console.log('');
|
|
148
|
+
} else {
|
|
149
|
+
console.log(chalk.yellow(` ${line}`));
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
console.log('');
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Advance the phase
|
|
158
|
+
const state = loadState();
|
|
159
|
+
state.features[feature].phase = nextPhase;
|
|
160
|
+
state.features[feature].updatedAt = new Date().toISOString();
|
|
161
|
+
saveState(state);
|
|
162
|
+
|
|
163
|
+
console.log(chalk.green(`\n✓ Advanced to ${nextPhaseDef.name}`));
|
|
164
|
+
|
|
165
|
+
// Show what's needed in the new phase
|
|
166
|
+
console.log(chalk.cyan('\n📋 Phase requirements:'));
|
|
167
|
+
console.log(chalk.gray(` ${nextPhaseDef.description}`));
|
|
168
|
+
|
|
169
|
+
if (nextPhaseDef.requiredOutputs?.length > 0) {
|
|
170
|
+
console.log(chalk.cyan('\n Required outputs:'));
|
|
171
|
+
nextPhaseDef.requiredOutputs.forEach(output => {
|
|
172
|
+
const exists = validation.phase ? true : false; // Already validated
|
|
173
|
+
console.log(chalk.gray(` ✓ ${output} (exists)`));
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Show phase-specific guidance
|
|
178
|
+
const guidance = getPhaseGuidance(nextPhase, feature);
|
|
179
|
+
if (guidance) {
|
|
180
|
+
console.log(chalk.cyan('\n Next steps:'));
|
|
181
|
+
guidance.forEach(step => console.log(chalk.white(` → ${step}`)));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
console.log('');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Get guidance for what to do in a phase
|
|
189
|
+
*/
|
|
190
|
+
function getPhaseGuidance(phase, feature) {
|
|
191
|
+
const guides = {
|
|
192
|
+
'setup': [
|
|
193
|
+
`Resume spec pipeline: /morph-proposal ${feature}`,
|
|
194
|
+
'Auto-continues from setup phase'
|
|
195
|
+
],
|
|
196
|
+
'uiux': [
|
|
197
|
+
`Resume spec pipeline: /morph-proposal ${feature}`,
|
|
198
|
+
'Provide layout references and preferences',
|
|
199
|
+
'Review wireframes before proceeding'
|
|
200
|
+
],
|
|
201
|
+
'design': [
|
|
202
|
+
`Resume spec pipeline: /morph-proposal ${feature}`,
|
|
203
|
+
'Review DECISION POINTS carefully',
|
|
204
|
+
'Approve spec.md and contracts.cs'
|
|
205
|
+
],
|
|
206
|
+
'clarify': [
|
|
207
|
+
`Resume spec pipeline: /morph-proposal ${feature}`,
|
|
208
|
+
'Resolve edge cases and unknowns'
|
|
209
|
+
],
|
|
210
|
+
'tasks': [
|
|
211
|
+
`Resume spec pipeline: /morph-proposal ${feature}`,
|
|
212
|
+
'Review task order and dependencies',
|
|
213
|
+
'Approve task list before implementing'
|
|
214
|
+
],
|
|
215
|
+
'implement': [
|
|
216
|
+
`Start implementing: /morph-apply ${feature}`,
|
|
217
|
+
'Complete tasks one by one with: morph-spec task done',
|
|
218
|
+
'Validators run automatically on task completion'
|
|
219
|
+
],
|
|
220
|
+
'sync': [
|
|
221
|
+
'Review decisions.md for standards to promote',
|
|
222
|
+
`Sync: morph-spec sync --path .morph/project/outputs/${feature}/decisions.md`
|
|
223
|
+
]
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
return guides[phase] || null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Gate: Check design system exists when advancing to implement with UI agents
|
|
231
|
+
* @param {string} feature - Feature name
|
|
232
|
+
* @param {string} projectPath - Project root path
|
|
233
|
+
* @returns {Object} { blocked: boolean, message?: string, solution?: string[] }
|
|
234
|
+
*/
|
|
235
|
+
function designSystemGate(feature, projectPath = '.') {
|
|
236
|
+
// Check if UI agents are active
|
|
237
|
+
const hasUIAgents = hasUIAgentsActive(projectPath, feature);
|
|
238
|
+
|
|
239
|
+
if (!hasUIAgents) {
|
|
240
|
+
return { blocked: false }; // No UI agents, gate doesn't apply
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Check if design system exists
|
|
244
|
+
const dsDetection = detectDesignSystem(projectPath, feature);
|
|
245
|
+
|
|
246
|
+
if (dsDetection.hasDesignSystem) {
|
|
247
|
+
return { blocked: false }; // Design system exists, gate passed
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Block advancement - no design system found with UI agents active
|
|
251
|
+
return {
|
|
252
|
+
blocked: true,
|
|
253
|
+
message: 'Cannot advance to implementation — UI agents are active but no design system found',
|
|
254
|
+
solution: [
|
|
255
|
+
'Design system is required when UI agents (blazor-builder, ui-designer, css-specialist) are active',
|
|
256
|
+
'',
|
|
257
|
+
'Create a design system with one of these options:',
|
|
258
|
+
' 1. Project-level: .morph/project/design-system.md (shared across features)',
|
|
259
|
+
` 2. Feature-level: .morph/project/outputs/${feature}/ui-design-system.md (feature-specific)`,
|
|
260
|
+
' 3. Auto-generate: morph-spec generate design-system (scans existing CSS)',
|
|
261
|
+
'',
|
|
262
|
+
'Or remove UI agents if they are not needed:',
|
|
263
|
+
` morph-spec state remove-agent ${feature} blazor-builder`
|
|
264
|
+
]
|
|
265
|
+
};
|
|
266
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* analyze-blazor-concurrency command
|
|
3
|
+
*
|
|
4
|
+
* Analyzes Blazor Server code for DbContext concurrency issues.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* morph-spec analyze-blazor-concurrency [path]
|
|
8
|
+
* morph-spec analyze-blazor-concurrency src/Services --verbose
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { glob } from 'glob';
|
|
12
|
+
import { readFileSync, existsSync, statSync } from 'fs';
|
|
13
|
+
import { join, resolve } from 'path';
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
import ora from 'ora';
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
analyzeConcurrency,
|
|
19
|
+
countIssues,
|
|
20
|
+
} from '../lib/blazor-concurrency-analyzer.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Main command handler for analyze-blazor-concurrency.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} [targetPath] - Path to analyze (file or directory)
|
|
26
|
+
* @param {Object} options - Command options
|
|
27
|
+
* @param {boolean} [options.verbose] - Show detailed output
|
|
28
|
+
*/
|
|
29
|
+
export async function analyzeBlazorConcurrencyCommand(targetPath, options = {}) {
|
|
30
|
+
const { verbose = false } = options;
|
|
31
|
+
|
|
32
|
+
console.log('');
|
|
33
|
+
console.log(
|
|
34
|
+
chalk.cyan('\uD83D\uDD0D Analisando concorrencia DbContext em Blazor Server...')
|
|
35
|
+
);
|
|
36
|
+
console.log('');
|
|
37
|
+
|
|
38
|
+
// Determine target path
|
|
39
|
+
const basePath = targetPath ? resolve(targetPath) : process.cwd();
|
|
40
|
+
|
|
41
|
+
if (!existsSync(basePath)) {
|
|
42
|
+
console.log(chalk.red(`\u274C Path not found: ${basePath}`));
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const spinner = ora('Scanning for C# files...').start();
|
|
47
|
+
|
|
48
|
+
let files = [];
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const stats = statSync(basePath);
|
|
52
|
+
|
|
53
|
+
if (stats.isFile()) {
|
|
54
|
+
if (basePath.endsWith('.cs')) {
|
|
55
|
+
files = [basePath];
|
|
56
|
+
} else {
|
|
57
|
+
spinner.fail('File must be .cs');
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
// Find all C# files (services, repositories, handlers)
|
|
62
|
+
const pattern = join(basePath, '**/*.cs');
|
|
63
|
+
files = await glob(pattern, {
|
|
64
|
+
ignore: [
|
|
65
|
+
'**/node_modules/**',
|
|
66
|
+
'**/bin/**',
|
|
67
|
+
'**/obj/**',
|
|
68
|
+
'**/Migrations/**',
|
|
69
|
+
],
|
|
70
|
+
windowsPathsNoEscape: true,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
spinner.succeed(`Found ${files.length} C# file(s)`);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
spinner.fail(`Error scanning files: ${error.message}`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (files.length === 0) {
|
|
81
|
+
console.log(chalk.yellow('\n\u26A0\uFE0F No C# files found to analyze'));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Analyze each file
|
|
86
|
+
const allIssues = [];
|
|
87
|
+
const fileResults = [];
|
|
88
|
+
|
|
89
|
+
for (const file of files) {
|
|
90
|
+
try {
|
|
91
|
+
const content = readFileSync(file, 'utf-8');
|
|
92
|
+
const issues = analyzeConcurrency(content, file);
|
|
93
|
+
|
|
94
|
+
if (issues.length > 0) {
|
|
95
|
+
allIssues.push(...issues);
|
|
96
|
+
fileResults.push({ file, issues });
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
if (verbose) {
|
|
100
|
+
console.log(chalk.red(`\u274C Error reading ${file}: ${error.message}`));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Output results
|
|
106
|
+
console.log('');
|
|
107
|
+
|
|
108
|
+
if (allIssues.length === 0) {
|
|
109
|
+
console.log(
|
|
110
|
+
chalk.green(
|
|
111
|
+
'\u2705 Nenhum problema de concorrencia detectado!'
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
console.log(
|
|
115
|
+
chalk.gray(
|
|
116
|
+
' Dica: Certifique-se de usar IDbContextFactory para operacoes background.'
|
|
117
|
+
)
|
|
118
|
+
);
|
|
119
|
+
console.log('');
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Display issues
|
|
124
|
+
if (verbose) {
|
|
125
|
+
fileResults.forEach(({ file, issues }) => {
|
|
126
|
+
console.log(chalk.white.bold(`\n${file}`));
|
|
127
|
+
console.log(chalk.gray('-'.repeat(60)));
|
|
128
|
+
|
|
129
|
+
issues.forEach((issue) => {
|
|
130
|
+
const icon =
|
|
131
|
+
issue.type === 'error'
|
|
132
|
+
? '\u274C'
|
|
133
|
+
: issue.type === 'warning'
|
|
134
|
+
? '\u26A0\uFE0F'
|
|
135
|
+
: '\u2139\uFE0F';
|
|
136
|
+
|
|
137
|
+
console.log(`${icon} ${issue.message}`);
|
|
138
|
+
if (issue.suggestion) {
|
|
139
|
+
console.log(chalk.gray(` \u2192 ${issue.suggestion}`));
|
|
140
|
+
}
|
|
141
|
+
if (issue.line) {
|
|
142
|
+
console.log(chalk.dim(` Linha: ${issue.line}`));
|
|
143
|
+
}
|
|
144
|
+
if (issue.code) {
|
|
145
|
+
console.log(chalk.dim(` Codigo: ${issue.code}`));
|
|
146
|
+
}
|
|
147
|
+
console.log('');
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
} else {
|
|
151
|
+
allIssues.forEach((issue) => {
|
|
152
|
+
const icon =
|
|
153
|
+
issue.type === 'error'
|
|
154
|
+
? '\u274C'
|
|
155
|
+
: issue.type === 'warning'
|
|
156
|
+
? '\u26A0\uFE0F'
|
|
157
|
+
: '\u2139\uFE0F';
|
|
158
|
+
|
|
159
|
+
const location = issue.line ? `${issue.file}:${issue.line}` : issue.file;
|
|
160
|
+
|
|
161
|
+
console.log(`${icon} ${issue.message}`);
|
|
162
|
+
if (issue.suggestion) {
|
|
163
|
+
console.log(chalk.gray(` \u2192 ${issue.suggestion}`));
|
|
164
|
+
}
|
|
165
|
+
console.log(chalk.dim(` ${location}`));
|
|
166
|
+
console.log('');
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Summary
|
|
171
|
+
const { errors, warnings, infos } = countIssues(allIssues);
|
|
172
|
+
const summaryColor = errors > 0 ? chalk.red : chalk.yellow;
|
|
173
|
+
|
|
174
|
+
console.log(chalk.gray('-'.repeat(60)));
|
|
175
|
+
console.log(
|
|
176
|
+
summaryColor(
|
|
177
|
+
`\n${files.length} arquivo(s) analisado(s) | ${errors} erro(s) | ${warnings} warning(s) | ${infos} info(s)\n`
|
|
178
|
+
)
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
// Recommendation
|
|
182
|
+
if (errors > 0) {
|
|
183
|
+
console.log(
|
|
184
|
+
chalk.cyan(
|
|
185
|
+
'\uD83D\uDCD6 Consulte: framework/standards/blazor-efcore.md para padroes corretos'
|
|
186
|
+
)
|
|
187
|
+
);
|
|
188
|
+
console.log('');
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export default analyzeBlazorConcurrencyCommand;
|