@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.
Files changed (166) hide show
  1. package/CLAUDE.md +446 -1730
  2. package/README.md +515 -516
  3. package/bin/morph-spec.js +366 -294
  4. package/bin/task-manager.js +429 -368
  5. package/bin/validate.js +369 -268
  6. package/content/.claude/commands/morph-apply.md +221 -158
  7. package/content/.claude/commands/morph-deploy.md +529 -0
  8. package/content/.claude/commands/morph-preflight.md +227 -0
  9. package/content/.claude/commands/morph-proposal.md +122 -101
  10. package/content/.claude/commands/morph-status.md +86 -86
  11. package/content/.claude/commands/morph-troubleshoot.md +122 -0
  12. package/content/.claude/skills/infra/azure-deploy-specialist.md +699 -0
  13. package/content/.claude/skills/level-0-meta/README.md +7 -0
  14. package/content/.claude/skills/level-0-meta/code-review.md +226 -0
  15. package/content/.claude/skills/level-0-meta/morph-checklist.md +117 -0
  16. package/content/.claude/skills/level-0-meta/simulation-checklist.md +77 -0
  17. package/content/.claude/skills/level-1-workflows/README.md +7 -0
  18. package/content/.claude/skills/level-1-workflows/morph-replicate.md +213 -0
  19. package/content/.claude/{commands/morph-clarify.md → skills/level-1-workflows/phase-clarify.md} +131 -184
  20. package/content/.claude/{commands/morph-design.md → skills/level-1-workflows/phase-design.md} +213 -275
  21. package/content/.claude/skills/level-1-workflows/phase-setup.md +106 -0
  22. package/content/.claude/skills/level-1-workflows/phase-tasks.md +164 -0
  23. package/content/.claude/{commands/morph-uiux.md → skills/level-1-workflows/phase-uiux.md} +169 -211
  24. package/content/.claude/skills/level-2-domains/README.md +14 -0
  25. package/content/.claude/skills/level-2-domains/ai-agents/ai-system-architect.md +192 -0
  26. package/content/.claude/skills/{specialists → level-2-domains/architecture}/po-pm-advisor.md +197 -197
  27. package/content/.claude/skills/level-2-domains/architecture/standards-architect.md +156 -0
  28. package/content/.claude/skills/level-2-domains/backend/dotnet-senior.md +287 -0
  29. package/content/.claude/skills/level-2-domains/backend/ef-modeler.md +113 -0
  30. package/content/.claude/skills/level-2-domains/backend/hangfire-orchestrator.md +126 -0
  31. package/content/.claude/skills/level-2-domains/backend/ms-agent-expert.md +109 -0
  32. package/content/.claude/skills/level-2-domains/frontend/blazor-builder.md +210 -0
  33. package/content/.claude/skills/level-2-domains/frontend/nextjs-expert.md +154 -0
  34. package/content/.claude/skills/level-2-domains/frontend/ui-ux-designer.md +191 -0
  35. package/content/.claude/skills/{specialists → level-2-domains/infrastructure}/azure-architect.md +142 -142
  36. package/content/.claude/skills/level-2-domains/infrastructure/bicep-architect.md +126 -0
  37. package/content/.claude/skills/level-2-domains/infrastructure/container-specialist.md +131 -0
  38. package/content/.claude/skills/level-2-domains/infrastructure/devops-engineer.md +119 -0
  39. package/content/.claude/skills/level-2-domains/integrations/asaas-financial.md +130 -0
  40. package/content/.claude/skills/level-2-domains/integrations/azure-identity.md +142 -0
  41. package/content/.claude/skills/level-2-domains/integrations/clerk-auth.md +108 -0
  42. package/content/.claude/skills/level-2-domains/integrations/resend-email.md +119 -0
  43. package/content/.claude/skills/level-2-domains/quality/code-analyzer.md +235 -0
  44. package/content/.claude/skills/level-2-domains/quality/testing-specialist.md +126 -0
  45. package/content/.claude/skills/level-3-technologies/README.md +7 -0
  46. package/content/.claude/skills/level-4-patterns/README.md +7 -0
  47. package/content/.claude/skills/specialists/prompt-engineer.md +189 -0
  48. package/content/.claude/skills/specialists/seo-growth-hacker.md +320 -0
  49. package/content/.morph/config/agents.json +762 -242
  50. package/content/.morph/config/config.template.json +122 -108
  51. package/content/.morph/docs/workflows/design-impl.md +37 -0
  52. package/content/.morph/docs/workflows/enforcement-pipeline.md +668 -0
  53. package/content/.morph/docs/workflows/fast-track.md +29 -0
  54. package/content/.morph/docs/workflows/full-morph.md +76 -0
  55. package/content/.morph/docs/workflows/standard.md +44 -0
  56. package/content/.morph/docs/workflows/ui-refresh.md +39 -0
  57. package/content/.morph/examples/scheduled-reports/decisions.md +158 -0
  58. package/content/.morph/examples/scheduled-reports/proposal.md +95 -0
  59. package/content/.morph/examples/scheduled-reports/spec.md +267 -0
  60. package/content/.morph/hooks/README.md +348 -239
  61. package/content/.morph/hooks/pre-commit-agents.sh +24 -24
  62. package/content/.morph/hooks/task-completed.js +73 -0
  63. package/content/.morph/hooks/teammate-idle.js +68 -0
  64. package/content/.morph/schemas/tasks.schema.json +220 -0
  65. package/content/.morph/standards/agent-framework-blazor-ui.md +359 -0
  66. package/content/.morph/standards/agent-framework-production.md +410 -0
  67. package/content/.morph/standards/agent-framework-setup.md +413 -453
  68. package/content/.morph/standards/agent-framework-workflows.md +349 -0
  69. package/content/.morph/standards/agent-teams-workflow.md +474 -0
  70. package/content/.morph/standards/architecture.md +325 -325
  71. package/content/.morph/standards/azure.md +605 -379
  72. package/content/.morph/standards/dotnet10-migration.md +520 -494
  73. package/content/.morph/templates/CONTEXT-FEATURE.md +276 -0
  74. package/content/.morph/templates/CONTEXT.md +170 -0
  75. package/content/.morph/templates/agent.cs +163 -172
  76. package/content/.morph/templates/clarify-questions.md +159 -0
  77. package/content/.morph/templates/contracts/Commands.cs +74 -0
  78. package/content/.morph/templates/contracts/Entities.cs +25 -0
  79. package/content/.morph/templates/contracts/Queries.cs +74 -0
  80. package/content/.morph/templates/contracts/README.md +74 -0
  81. package/content/.morph/templates/decisions.md +123 -106
  82. package/content/.morph/templates/infra/azure-pipelines-deploy.yml +480 -0
  83. package/content/.morph/templates/infra/deploy-checklist.md +426 -0
  84. package/content/.morph/templates/proposal.md +141 -155
  85. package/content/.morph/templates/recap.md +94 -105
  86. package/content/.morph/templates/simulation.md +353 -0
  87. package/content/.morph/templates/spec.md +149 -148
  88. package/content/.morph/templates/state.template.json +222 -222
  89. package/content/.morph/templates/tasks.md +257 -235
  90. package/content/.morph/templates/ui-components.md +362 -276
  91. package/content/CLAUDE.md +150 -442
  92. package/detectors/structure-detector.js +245 -250
  93. package/docs/README.md +144 -149
  94. package/docs/getting-started.md +301 -302
  95. package/docs/installation.md +361 -361
  96. package/docs/validation-checklist.md +265 -266
  97. package/package.json +80 -80
  98. package/src/commands/advance-phase.js +266 -0
  99. package/src/commands/analyze-blazor-concurrency.js +193 -0
  100. package/src/commands/deploy.js +780 -0
  101. package/src/commands/detect-agents.js +167 -0
  102. package/src/commands/doctor.js +356 -280
  103. package/src/commands/generate-context.js +40 -0
  104. package/src/commands/init.js +258 -245
  105. package/src/commands/lint-fluent.js +352 -0
  106. package/src/commands/rollback-phase.js +185 -0
  107. package/src/commands/session-summary.js +291 -0
  108. package/src/commands/task.js +78 -75
  109. package/src/commands/troubleshoot.js +222 -0
  110. package/src/commands/update.js +192 -159
  111. package/src/commands/validate-blazor-state.js +210 -0
  112. package/src/commands/validate-blazor.js +156 -0
  113. package/src/commands/validate-css.js +84 -0
  114. package/src/commands/validate-phase.js +221 -0
  115. package/src/lib/blazor-concurrency-analyzer.js +288 -0
  116. package/src/lib/blazor-state-validator.js +291 -0
  117. package/src/lib/blazor-validator.js +374 -0
  118. package/src/lib/complexity-analyzer.js +441 -292
  119. package/src/lib/context-generator.js +513 -0
  120. package/src/lib/continuous-validator.js +421 -440
  121. package/src/lib/css-validator.js +352 -0
  122. package/src/lib/decision-constraint-loader.js +109 -0
  123. package/src/lib/design-system-detector.js +187 -0
  124. package/src/lib/design-system-scaffolder.js +299 -0
  125. package/src/lib/hook-executor.js +256 -0
  126. package/src/lib/recap-generator.js +205 -0
  127. package/src/lib/spec-validator.js +258 -0
  128. package/src/lib/standards-context-injector.js +287 -0
  129. package/src/lib/state-manager.js +397 -340
  130. package/src/lib/team-orchestrator.js +322 -0
  131. package/src/lib/troubleshoot-grep.js +194 -0
  132. package/src/lib/troubleshoot-index.js +144 -0
  133. package/src/lib/validation-runner.js +283 -0
  134. package/src/lib/validators/contract-compliance-validator.js +273 -0
  135. package/src/lib/validators/design-system-validator.js +231 -0
  136. package/src/utils/file-copier.js +187 -139
  137. package/content/.claude/commands/morph-costs.md +0 -206
  138. package/content/.claude/commands/morph-setup.md +0 -100
  139. package/content/.claude/commands/morph-tasks.md +0 -319
  140. package/content/.claude/skills/infra/bicep-architect.md +0 -419
  141. package/content/.claude/skills/infra/container-specialist.md +0 -437
  142. package/content/.claude/skills/infra/devops-engineer.md +0 -405
  143. package/content/.claude/skills/integrations/asaas-financial.md +0 -333
  144. package/content/.claude/skills/integrations/azure-identity.md +0 -309
  145. package/content/.claude/skills/integrations/clerk-auth.md +0 -290
  146. package/content/.claude/skills/specialists/ai-system-architect.md +0 -604
  147. package/content/.claude/skills/specialists/cost-guardian.md +0 -110
  148. package/content/.claude/skills/specialists/ef-modeler.md +0 -211
  149. package/content/.claude/skills/specialists/hangfire-orchestrator.md +0 -255
  150. package/content/.claude/skills/specialists/ms-agent-expert.md +0 -263
  151. package/content/.claude/skills/specialists/standards-architect.md +0 -78
  152. package/content/.claude/skills/specialists/ui-ux-designer.md +0 -1100
  153. package/content/.claude/skills/stacks/dotnet-blazor.md +0 -606
  154. package/content/.claude/skills/stacks/dotnet-nextjs.md +0 -402
  155. package/content/.claude/skills/stacks/shopify.md +0 -445
  156. package/content/.morph/config/azure-pricing.json +0 -70
  157. package/content/.morph/config/azure-pricing.schema.json +0 -50
  158. package/content/.morph/hooks/pre-commit-costs.sh +0 -91
  159. package/docs/api/cost-calculator.js.html +0 -513
  160. package/docs/api/design-system-generator.js.html +0 -382
  161. package/docs/api/global.html +0 -5263
  162. package/docs/api/index.html +0 -96
  163. package/docs/api/state-manager.js.html +0 -423
  164. package/src/commands/cost.js +0 -181
  165. package/src/commands/update-pricing.js +0 -206
  166. package/src/lib/cost-calculator.js +0 -429
@@ -0,0 +1,283 @@
1
+ /**
2
+ * MORPH-SPEC Validation Runner
3
+ *
4
+ * Unified orchestrator that runs the right validators based on the feature's
5
+ * active agents and decisions. Called by task-done before marking tasks complete.
6
+ *
7
+ * MORPH-SPEC 3.0 - Phase A: Active Validation Pipeline
8
+ */
9
+
10
+ import { existsSync, readFileSync } from 'fs';
11
+ import { join } from 'path';
12
+ import chalk from 'chalk';
13
+ import { loadState } from './state-manager.js';
14
+ import { loadConstraints } from './decision-constraint-loader.js';
15
+
16
+ /**
17
+ * Load agent → validators map from agents.json (data-driven)
18
+ * @param {string} projectPath - Root path of the project
19
+ * @returns {Object} Map of agent IDs to validator arrays
20
+ */
21
+ function loadAgentValidatorMap(projectPath) {
22
+ try {
23
+ const agentsPath = join(projectPath, 'content/.morph/config/agents.json');
24
+ const agentsConfig = JSON.parse(readFileSync(agentsPath, 'utf8'));
25
+
26
+ const map = {};
27
+ Object.entries(agentsConfig.agents || {}).forEach(([agentId, agent]) => {
28
+ if (!agentId.startsWith('_comment') && agent.validators && agent.validators.length > 0) {
29
+ map[agentId] = agent.validators;
30
+ }
31
+ });
32
+
33
+ return map;
34
+ } catch (err) {
35
+ console.warn(chalk.yellow('⚠️ Failed to load agents.json - no validators will run'));
36
+ console.warn(chalk.gray(` Run 'morph-spec init' to set up MORPH-SPEC configuration`));
37
+ return {}; // Empty map = honest failure
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Run all relevant validators for a feature
43
+ *
44
+ * @param {string} projectPath - Project root path
45
+ * @param {string} featureName - Feature name from state.json
46
+ * @param {Object} options - Options
47
+ * @param {boolean} options.verbose - Show detailed output
48
+ * @param {string[]} options.validators - Specific validators to run (overrides agent-based detection)
49
+ * @returns {Object} { passed, errors, warnings, issues, summary }
50
+ */
51
+ export async function runValidation(projectPath, featureName, options = {}) {
52
+ const result = {
53
+ passed: true,
54
+ errors: 0,
55
+ warnings: 0,
56
+ issues: [],
57
+ summary: '',
58
+ results: {} // Per-validator results for hooks integration
59
+ };
60
+
61
+ // Load decision constraints from decisions.md
62
+ const constraints = loadConstraints(projectPath, featureName);
63
+
64
+ // Determine which validators to run
65
+ const validatorIds = options.validators || detectValidators(featureName, projectPath);
66
+
67
+ // Merge constraint-enabled validators
68
+ validatorIds.push(...constraints.enabledValidators);
69
+
70
+ if (validatorIds.length === 0) {
71
+ result.summary = 'No validators applicable for this feature';
72
+ return result;
73
+ }
74
+
75
+ if (options.verbose) {
76
+ console.log(chalk.gray(`\n Running validators: ${validatorIds.join(', ')}`));
77
+ if (constraints.enabledValidators.length > 0) {
78
+ console.log(chalk.gray(` (+ ${constraints.enabledValidators.length} from decisions)`));
79
+ }
80
+ }
81
+
82
+ // Always run contract compliance if contracts.cs exists
83
+ const contractsPath = join(projectPath, '.morph/project/outputs', featureName, 'contracts.cs');
84
+ if (existsSync(contractsPath)) {
85
+ validatorIds.push('contract-compliance');
86
+ }
87
+
88
+ // Deduplicate
89
+ const uniqueValidators = [...new Set(validatorIds)];
90
+
91
+ // Run each validator
92
+ for (const validatorId of uniqueValidators) {
93
+ try {
94
+ // Pass validator-specific constraints from decisions.md
95
+ const validatorOptions = {
96
+ ...options,
97
+ constraints: constraints.validators[validatorId] || {}
98
+ };
99
+
100
+ const validatorResult = await runSingleValidator(validatorId, projectPath, featureName, validatorOptions);
101
+
102
+ if (validatorResult) {
103
+ result.errors += validatorResult.errors || 0;
104
+ result.warnings += validatorResult.warnings || 0;
105
+
106
+ if (validatorResult.issues) {
107
+ result.issues.push(...validatorResult.issues);
108
+ }
109
+
110
+ // Store per-validator results for hooks
111
+ result.results[validatorId] = {
112
+ passed: (validatorResult.errors || 0) === 0,
113
+ issues: validatorResult.issues || []
114
+ };
115
+ }
116
+ } catch (err) {
117
+ if (options.verbose) {
118
+ console.log(chalk.yellow(` ⚠ Validator '${validatorId}' failed: ${err.message}`));
119
+ }
120
+ result.results[validatorId] = {
121
+ passed: false,
122
+ error: err.message
123
+ };
124
+ }
125
+ }
126
+
127
+ result.passed = result.errors === 0;
128
+ result.summary = result.passed
129
+ ? `✅ All validations passed (${result.warnings} warnings)`
130
+ : `❌ ${result.errors} error(s), ${result.warnings} warning(s)`;
131
+
132
+ return result;
133
+ }
134
+
135
+ /**
136
+ * Detect which validators to run based on feature's active agents
137
+ * @param {string} featureName - Feature name
138
+ * @param {string} projectPath - Project root path (default: current directory)
139
+ * @returns {string[]} Array of validator IDs to run
140
+ */
141
+ function detectValidators(featureName, projectPath = '.') {
142
+ const state = loadState(false);
143
+ if (!state || !state.features[featureName]) {
144
+ return ['architecture', 'packages']; // Default validators
145
+ }
146
+
147
+ const feature = state.features[featureName];
148
+ const agents = feature.activeAgents || [];
149
+ const validatorSet = new Set();
150
+
151
+ // Load agent → validators map from agents.json (data-driven)
152
+ const agentValidatorMap = loadAgentValidatorMap(projectPath);
153
+
154
+ for (const agentId of agents) {
155
+ const validators = agentValidatorMap[agentId] || [];
156
+ validators.forEach(v => validatorSet.add(v));
157
+ }
158
+
159
+ // Always include architecture and packages as baseline
160
+ validatorSet.add('architecture');
161
+ validatorSet.add('packages');
162
+
163
+ return Array.from(validatorSet);
164
+ }
165
+
166
+ /**
167
+ * Run a single validator by ID
168
+ */
169
+ async function runSingleValidator(validatorId, projectPath, featureName, options) {
170
+ switch (validatorId) {
171
+ case 'architecture': {
172
+ const { validateArchitecture } = await import('./validators/architecture-validator.js');
173
+ return await validateArchitecture(projectPath, options);
174
+ }
175
+
176
+ case 'packages': {
177
+ const { validatePackages } = await import('./validators/package-validator.js');
178
+ return await validatePackages(projectPath, options);
179
+ }
180
+
181
+ case 'contrast': {
182
+ const { validateContrast } = await import('./validators/ui-contrast-validator.js');
183
+ return await validateContrast(projectPath, options);
184
+ }
185
+
186
+ case 'blazor': {
187
+ try {
188
+ const { validateBlazorPatterns } = await import('./blazor-validator.js');
189
+ return await validateBlazorPatterns(projectPath, options);
190
+ } catch {
191
+ return null; // Validator may not exist or have different export
192
+ }
193
+ }
194
+
195
+ case 'blazor-concurrency': {
196
+ try {
197
+ const { analyzeConcurrency } = await import('./blazor-concurrency-analyzer.js');
198
+ return await analyzeConcurrency(projectPath, options);
199
+ } catch {
200
+ return null;
201
+ }
202
+ }
203
+
204
+ case 'blazor-state': {
205
+ try {
206
+ const { validateState } = await import('./blazor-state-validator.js');
207
+ return await validateState(projectPath, options);
208
+ } catch {
209
+ return null;
210
+ }
211
+ }
212
+
213
+ case 'css': {
214
+ try {
215
+ const { validateCss } = await import('./css-validator.js');
216
+ return await validateCss(projectPath, options);
217
+ } catch {
218
+ return null;
219
+ }
220
+ }
221
+
222
+ case 'contract-compliance': {
223
+ const { validateContracts } = await import('./validators/contract-compliance-validator.js');
224
+ return await validateContracts(projectPath, featureName, options);
225
+ }
226
+
227
+ case 'design-system': {
228
+ const { validateDesignSystem } = await import('./validators/design-system-validator.js');
229
+ return await validateDesignSystem(projectPath, featureName, options);
230
+ }
231
+
232
+ default:
233
+ if (options.verbose) {
234
+ console.log(chalk.gray(` Unknown validator: ${validatorId}`));
235
+ }
236
+ return null;
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Format validation results for console output
242
+ */
243
+ export function formatValidationResults(result) {
244
+ if (result.passed && result.warnings === 0) {
245
+ console.log(chalk.green('\n ✅ All validations passed!\n'));
246
+ return;
247
+ }
248
+
249
+ if (result.passed && result.warnings > 0) {
250
+ console.log(chalk.yellow(`\n ⚠️ Passed with ${result.warnings} warning(s)\n`));
251
+ } else {
252
+ console.log(chalk.red(`\n ❌ Validation FAILED: ${result.errors} error(s), ${result.warnings} warning(s)\n`));
253
+ }
254
+
255
+ // Group issues by level
256
+ const errors = result.issues.filter(i => i.level === 'error');
257
+ const warnings = result.issues.filter(i => i.level === 'warning');
258
+
259
+ if (errors.length > 0) {
260
+ console.log(chalk.red.bold(' Errors:'));
261
+ for (const issue of errors.slice(0, 10)) {
262
+ console.log(chalk.red(` ✗ ${issue.message}`));
263
+ if (issue.file) console.log(chalk.gray(` ${issue.file}${issue.line ? ':' + issue.line : ''}`));
264
+ if (issue.solution) console.log(chalk.cyan(` Fix: ${issue.solution}`));
265
+ }
266
+ if (errors.length > 10) {
267
+ console.log(chalk.gray(` ... and ${errors.length - 10} more errors`));
268
+ }
269
+ }
270
+
271
+ if (warnings.length > 0) {
272
+ console.log(chalk.yellow('\n Warnings:'));
273
+ for (const issue of warnings.slice(0, 5)) {
274
+ console.log(chalk.yellow(` ⚠ ${issue.message}`));
275
+ if (issue.file) console.log(chalk.gray(` ${issue.file}`));
276
+ }
277
+ if (warnings.length > 5) {
278
+ console.log(chalk.gray(` ... and ${warnings.length - 5} more warnings`));
279
+ }
280
+ }
281
+
282
+ console.log('');
283
+ }
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Contract Compliance Validator
3
+ *
4
+ * Parses contracts.cs from feature outputs and verifies that the project code
5
+ * actually implements all defined interfaces, methods, DTOs, and enums.
6
+ *
7
+ * MORPH-SPEC 3.0 - Phase A: Active Validation Pipeline
8
+ */
9
+
10
+ import { readFileSync, existsSync } from 'fs';
11
+ import { join } from 'path';
12
+ import { glob } from 'glob';
13
+ import chalk from 'chalk';
14
+
15
+ /**
16
+ * Validate that contracts.cs interfaces are implemented in project code
17
+ *
18
+ * @param {string} projectPath - Project root path
19
+ * @param {string} featureName - Feature name
20
+ * @param {Object} options - Options
21
+ * @returns {Object} { status, errors, warnings, issues, coverage }
22
+ */
23
+ export async function validateContracts(projectPath, featureName, options = {}) {
24
+ const contractsPath = join(projectPath, '.morph/project/outputs', featureName, 'contracts.cs');
25
+
26
+ if (!existsSync(contractsPath)) {
27
+ return {
28
+ status: 'ok',
29
+ errors: 0,
30
+ warnings: 0,
31
+ issues: [],
32
+ coverage: null,
33
+ message: 'No contracts.cs found — skipping'
34
+ };
35
+ }
36
+
37
+ const contractsContent = readFileSync(contractsPath, 'utf-8');
38
+ const contracts = parseContracts(contractsContent);
39
+
40
+ if (contracts.interfaces.length === 0 && contracts.records.length === 0 && contracts.enums.length === 0) {
41
+ return {
42
+ status: 'ok',
43
+ errors: 0,
44
+ warnings: 1,
45
+ issues: [{ level: 'warning', message: 'contracts.cs exists but no interfaces/records/enums found' }],
46
+ coverage: null
47
+ };
48
+ }
49
+
50
+ // Find all .cs files in the project (excluding .morph outputs, bin, obj)
51
+ const csFiles = await glob('**/*.cs', {
52
+ cwd: projectPath,
53
+ ignore: ['.morph/**', '**/bin/**', '**/obj/**', '**/node_modules/**']
54
+ });
55
+
56
+ const projectCode = {};
57
+ for (const file of csFiles) {
58
+ const fullPath = join(projectPath, file);
59
+ try {
60
+ projectCode[file] = readFileSync(fullPath, 'utf-8');
61
+ } catch {
62
+ // Skip unreadable files
63
+ }
64
+ }
65
+
66
+ const issues = [];
67
+ let implemented = 0;
68
+ let total = 0;
69
+ const interfaceResults = [];
70
+
71
+ // Validate interfaces
72
+ for (const iface of contracts.interfaces) {
73
+ total++;
74
+ const result = validateInterface(iface, projectCode, options);
75
+ interfaceResults.push(result);
76
+
77
+ if (result.implemented) {
78
+ implemented++;
79
+ if (options.verbose) {
80
+ console.log(chalk.green(` ✓ ${iface.name} → ${result.implementingClass} (${result.implementedMethods}/${result.totalMethods} methods)`));
81
+ }
82
+ } else {
83
+ issues.push({
84
+ level: 'error',
85
+ message: `Interface ${iface.name} not implemented in project code`,
86
+ solution: `Create a class that implements ${iface.name} with all ${iface.methods.length} method(s)`
87
+ });
88
+ }
89
+
90
+ // Check for missing methods
91
+ for (const method of result.missingMethods || []) {
92
+ issues.push({
93
+ level: 'error',
94
+ message: `Method ${method} missing from ${result.implementingClass || iface.name} implementation`,
95
+ file: result.implementingFile,
96
+ solution: `Add method ${method} to the class implementing ${iface.name}`
97
+ });
98
+ }
99
+ }
100
+
101
+ // Validate records/DTOs
102
+ for (const record of contracts.records) {
103
+ total++;
104
+ const found = Object.entries(projectCode).some(([, content]) =>
105
+ new RegExp(`(class|record|struct)\\s+${escapeRegex(record.name)}`, 'g').test(content)
106
+ );
107
+
108
+ if (found) {
109
+ implemented++;
110
+ if (options.verbose) {
111
+ console.log(chalk.green(` ✓ ${record.name} (${record.type})`));
112
+ }
113
+ } else {
114
+ issues.push({
115
+ level: 'warning',
116
+ message: `${record.type} ${record.name} defined in contracts but not found in project code`,
117
+ solution: `Create ${record.type} ${record.name} matching the contract definition`
118
+ });
119
+ }
120
+ }
121
+
122
+ // Validate enums
123
+ for (const enumDef of contracts.enums) {
124
+ total++;
125
+ const found = Object.entries(projectCode).some(([, content]) =>
126
+ new RegExp(`enum\\s+${escapeRegex(enumDef.name)}`, 'g').test(content)
127
+ );
128
+
129
+ if (found) {
130
+ implemented++;
131
+ if (options.verbose) {
132
+ console.log(chalk.green(` ✓ ${enumDef.name} (enum)`));
133
+ }
134
+ } else {
135
+ issues.push({
136
+ level: 'warning',
137
+ message: `Enum ${enumDef.name} defined in contracts but not found in project code`,
138
+ solution: `Create enum ${enumDef.name} matching the contract definition`
139
+ });
140
+ }
141
+ }
142
+
143
+ const coveragePercent = total > 0 ? Math.round((implemented / total) * 100) : 100;
144
+ const errors = issues.filter(i => i.level === 'error').length;
145
+ const warnings = issues.filter(i => i.level === 'warning').length;
146
+
147
+ if (options.verbose) {
148
+ console.log(chalk.cyan(`\n Contract coverage: ${coveragePercent}% (${implemented}/${total})`));
149
+ }
150
+
151
+ return {
152
+ status: errors > 0 ? 'error' : 'ok',
153
+ errors,
154
+ warnings,
155
+ issues,
156
+ coverage: {
157
+ percentage: coveragePercent,
158
+ implemented,
159
+ total,
160
+ interfaces: interfaceResults
161
+ }
162
+ };
163
+ }
164
+
165
+ /**
166
+ * Parse contracts.cs content and extract interfaces, records, and enums
167
+ */
168
+ function parseContracts(content) {
169
+ const interfaces = [];
170
+ const records = [];
171
+ const enums = [];
172
+
173
+ // Extract interfaces with their methods
174
+ const interfaceRegex = /public\s+interface\s+(I\w+)(?:\s*:\s*[\w<>,\s]+)?\s*\{([^}]*)\}/gs;
175
+ let match;
176
+
177
+ while ((match = interfaceRegex.exec(content)) !== null) {
178
+ const name = match[1];
179
+ const body = match[2];
180
+ const methods = extractMethods(body);
181
+
182
+ interfaces.push({ name, methods });
183
+ }
184
+
185
+ // Extract records/classes (DTOs)
186
+ const recordRegex = /public\s+(record|class|struct)\s+(\w+)/g;
187
+ while ((match = recordRegex.exec(content)) !== null) {
188
+ const type = match[1];
189
+ const name = match[2];
190
+ // Skip interface names that were already captured
191
+ if (!name.startsWith('I') || !interfaces.some(i => i.name === name)) {
192
+ records.push({ type, name });
193
+ }
194
+ }
195
+
196
+ // Extract enums
197
+ const enumRegex = /public\s+enum\s+(\w+)/g;
198
+ while ((match = enumRegex.exec(content)) !== null) {
199
+ enums.push({ name: match[1] });
200
+ }
201
+
202
+ return { interfaces, records, enums };
203
+ }
204
+
205
+ /**
206
+ * Extract method signatures from an interface body
207
+ */
208
+ function extractMethods(body) {
209
+ const methods = [];
210
+
211
+ // Match method signatures like: Task<Result> DoSomethingAsync(params);
212
+ const methodRegex = /(?:Task<[^>]+>|Task|void|string|int|bool|decimal|[\w<>[\],\s?]+)\s+(\w+)\s*\(/g;
213
+ let match;
214
+
215
+ while ((match = methodRegex.exec(body)) !== null) {
216
+ const methodName = match[1];
217
+ // Skip property getters/setters
218
+ if (!['get', 'set', 'init'].includes(methodName)) {
219
+ methods.push(methodName);
220
+ }
221
+ }
222
+
223
+ return methods;
224
+ }
225
+
226
+ /**
227
+ * Validate that an interface is implemented in project code
228
+ */
229
+ function validateInterface(iface, projectCode, options) {
230
+ const result = {
231
+ name: iface.name,
232
+ implemented: false,
233
+ implementingClass: null,
234
+ implementingFile: null,
235
+ totalMethods: iface.methods.length,
236
+ implementedMethods: 0,
237
+ missingMethods: []
238
+ };
239
+
240
+ // Search for classes implementing this interface
241
+ const implRegex = new RegExp(`class\\s+(\\w+)\\s*(?:<[^>]*>)?\\s*:\\s*[^{]*${escapeRegex(iface.name)}`, 'g');
242
+
243
+ for (const [file, content] of Object.entries(projectCode)) {
244
+ const match = implRegex.exec(content);
245
+ if (match) {
246
+ result.implemented = true;
247
+ result.implementingClass = match[1];
248
+ result.implementingFile = file;
249
+
250
+ // Check which methods are implemented
251
+ for (const method of iface.methods) {
252
+ const methodExists = new RegExp(`(public|protected|private|internal)?\\s*(async\\s+)?\\S+\\s+${escapeRegex(method)}\\s*\\(`, 'g').test(content);
253
+ if (methodExists) {
254
+ result.implementedMethods++;
255
+ } else {
256
+ result.missingMethods.push(method);
257
+ }
258
+ }
259
+
260
+ break; // Found the implementation
261
+ }
262
+ implRegex.lastIndex = 0; // Reset for next file
263
+ }
264
+
265
+ return result;
266
+ }
267
+
268
+ /**
269
+ * Escape special regex characters in a string
270
+ */
271
+ function escapeRegex(str) {
272
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
273
+ }