@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,205 @@
1
+ /**
2
+ * Recap Generator
3
+ *
4
+ * Auto-generates recap.md from real project data: state.json, contract compliance,
5
+ * validation results, and decisions. Replaces manual template filling.
6
+ *
7
+ * MORPH-SPEC 3.0 - Phase B: Spec-Driven Pipeline
8
+ */
9
+
10
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
11
+ import { join, dirname } from 'path';
12
+ import chalk from 'chalk';
13
+ import { loadState } from './state-manager.js';
14
+ import { runValidation } from './validation-runner.js';
15
+ import { loadConstraints } from './decision-constraint-loader.js';
16
+
17
+ /**
18
+ * Generate recap.md for a feature
19
+ *
20
+ * @param {string} projectPath - Project root path
21
+ * @param {string} featureName - Feature name
22
+ * @param {Object} options - Options
23
+ * @returns {string} Generated recap content
24
+ */
25
+ export async function generateRecap(projectPath, featureName, options = {}) {
26
+ const state = loadState(false);
27
+ if (!state || !state.features[featureName]) {
28
+ throw new Error(`Feature '${featureName}' not found in state.json`);
29
+ }
30
+
31
+ const feature = state.features[featureName];
32
+ const outputsPath = join(projectPath, '.morph/project/outputs', featureName);
33
+
34
+ // Gather data
35
+ const tasksSummary = getTasksSummary(feature);
36
+ const agentsSummary = getAgentsSummary(feature);
37
+ const checkpointsSummary = getCheckpointsSummary(feature);
38
+ const decisions = loadConstraints(projectPath, featureName);
39
+ const validationResult = await runValidation(projectPath, featureName, { verbose: false });
40
+
41
+ // Contract coverage
42
+ let contractCoverage = null;
43
+ if (validationResult.issues) {
44
+ const contractIssues = validationResult.issues.filter(i => i.message?.includes('Interface') || i.message?.includes('contracts'));
45
+ if (contractIssues.length > 0 || existsSync(join(outputsPath, 'contracts.cs'))) {
46
+ // Extract coverage from validation result
47
+ contractCoverage = extractContractCoverage(validationResult);
48
+ }
49
+ }
50
+
51
+ // Generate markdown
52
+ const recap = buildRecapMarkdown({
53
+ featureName,
54
+ feature,
55
+ tasksSummary,
56
+ agentsSummary,
57
+ checkpointsSummary,
58
+ decisions: decisions.decisions,
59
+ validationResult,
60
+ contractCoverage
61
+ });
62
+
63
+ // Write to file
64
+ const recapPath = join(outputsPath, 'recap.md');
65
+ if (!existsSync(dirname(recapPath))) {
66
+ mkdirSync(dirname(recapPath), { recursive: true });
67
+ }
68
+ writeFileSync(recapPath, recap, 'utf-8');
69
+
70
+ if (!options.quiet) {
71
+ console.log(chalk.green(`\n✅ Recap generated: ${recapPath}`));
72
+ }
73
+
74
+ return recap;
75
+ }
76
+
77
+ function getTasksSummary(feature) {
78
+ const tasks = Array.isArray(feature.tasks) ? feature.tasks : [];
79
+ const completed = tasks.filter(t => t.status === 'completed');
80
+ const pending = tasks.filter(t => t.status === 'pending');
81
+ const inProgress = tasks.filter(t => t.status === 'in_progress');
82
+
83
+ return {
84
+ total: tasks.length,
85
+ completed: completed.length,
86
+ pending: pending.length,
87
+ inProgress: inProgress.length,
88
+ percentage: tasks.length > 0 ? Math.round((completed.length / tasks.length) * 100) : 0,
89
+ items: tasks
90
+ };
91
+ }
92
+
93
+ function getAgentsSummary(feature) {
94
+ return feature.activeAgents || [];
95
+ }
96
+
97
+ function getCheckpointsSummary(feature) {
98
+ return (feature.checkpoints || []).map(cp => ({
99
+ timestamp: cp.timestamp,
100
+ note: cp.note,
101
+ tasks: cp.tasksCompleted || []
102
+ }));
103
+ }
104
+
105
+ function extractContractCoverage(validationResult) {
106
+ // Look for contract compliance data in validation issues
107
+ const interfaceErrors = validationResult.issues.filter(i =>
108
+ i.message?.includes('Interface') && i.level === 'error'
109
+ );
110
+ const methodErrors = validationResult.issues.filter(i =>
111
+ i.message?.includes('Method') && i.message?.includes('missing') && i.level === 'error'
112
+ );
113
+
114
+ return {
115
+ missingInterfaces: interfaceErrors.length,
116
+ missingMethods: methodErrors.length,
117
+ hasIssues: interfaceErrors.length > 0 || methodErrors.length > 0
118
+ };
119
+ }
120
+
121
+ function buildRecapMarkdown(data) {
122
+ const { featureName, feature, tasksSummary, agentsSummary, checkpointsSummary, decisions, validationResult, contractCoverage } = data;
123
+ const now = new Date().toISOString().split('T')[0];
124
+
125
+ let md = `# Recap: ${featureName}\n\n`;
126
+ md += `> Generated: ${now} | Phase: ${feature.phase} | Status: ${feature.status}\n\n`;
127
+ md += `---\n\n`;
128
+
129
+ // Progress
130
+ md += `## Progress\n\n`;
131
+ md += `| Metric | Value |\n|--------|-------|\n`;
132
+ md += `| Tasks completed | ${tasksSummary.completed}/${tasksSummary.total} (${tasksSummary.percentage}%) |\n`;
133
+ md += `| Tasks pending | ${tasksSummary.pending} |\n`;
134
+ md += `| Tasks in progress | ${tasksSummary.inProgress} |\n`;
135
+ md += `| Active agents | ${agentsSummary.length} |\n`;
136
+ md += `| Checkpoints | ${checkpointsSummary.length} |\n\n`;
137
+
138
+ // Validation Results
139
+ md += `## Validation Results\n\n`;
140
+ if (validationResult.passed) {
141
+ md += `**Status: PASSED** (${validationResult.warnings} warnings)\n\n`;
142
+ } else {
143
+ md += `**Status: FAILED** (${validationResult.errors} errors, ${validationResult.warnings} warnings)\n\n`;
144
+ if (validationResult.issues.length > 0) {
145
+ md += `| Level | Issue |\n|-------|-------|\n`;
146
+ for (const issue of validationResult.issues.slice(0, 15)) {
147
+ md += `| ${issue.level} | ${issue.message} |\n`;
148
+ }
149
+ md += `\n`;
150
+ }
151
+ }
152
+
153
+ // Contract Coverage
154
+ if (contractCoverage) {
155
+ md += `## Contract Compliance\n\n`;
156
+ if (contractCoverage.hasIssues) {
157
+ md += `- Missing interfaces: ${contractCoverage.missingInterfaces}\n`;
158
+ md += `- Missing methods: ${contractCoverage.missingMethods}\n\n`;
159
+ } else {
160
+ md += `All contracts implemented.\n\n`;
161
+ }
162
+ }
163
+
164
+ // Decisions
165
+ if (decisions.length > 0) {
166
+ md += `## Decisions Applied\n\n`;
167
+ for (const d of decisions) {
168
+ if (d.title) {
169
+ md += `- **${d.title}**: ${d.decision.slice(0, 100)}${d.decision.length > 100 ? '...' : ''}\n`;
170
+ }
171
+ }
172
+ md += `\n`;
173
+ }
174
+
175
+ // Agents
176
+ if (agentsSummary.length > 0) {
177
+ md += `## Active Agents\n\n`;
178
+ md += agentsSummary.map(a => `- ${a}`).join('\n');
179
+ md += `\n\n`;
180
+ }
181
+
182
+ // Checkpoints
183
+ if (checkpointsSummary.length > 0) {
184
+ md += `## Checkpoints\n\n`;
185
+ for (const cp of checkpointsSummary) {
186
+ md += `- ${cp.timestamp}: ${cp.note}\n`;
187
+ }
188
+ md += `\n`;
189
+ }
190
+
191
+ // Tasks Detail
192
+ if (tasksSummary.items.length > 0) {
193
+ md += `## Tasks\n\n`;
194
+ md += `| ID | Title | Status |\n|----|-------|--------|\n`;
195
+ for (const task of tasksSummary.items) {
196
+ const statusIcon = task.status === 'completed' ? '✅' : task.status === 'in_progress' ? '🔄' : '⏳';
197
+ md += `| ${task.id} | ${task.title || '-'} | ${statusIcon} ${task.status} |\n`;
198
+ }
199
+ md += `\n`;
200
+ }
201
+
202
+ md += `---\n\n*Generated by MORPH-SPEC Recap Generator*\n`;
203
+
204
+ return md;
205
+ }
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Spec Validator
3
+ *
4
+ * Validates spec.md and contracts.cs at design time (Phase 2) to catch
5
+ * architectural and coding standard violations early.
6
+ *
7
+ * MORPH-SPEC 3.0 - Phase 4: Spec-Time Validation
8
+ */
9
+
10
+ import { readFileSync, existsSync } from 'fs';
11
+ import { join } from 'path';
12
+
13
+ /**
14
+ * Validate spec.md and contracts.cs at design time
15
+ *
16
+ * @param {string} projectPath - Project root path
17
+ * @param {string} featureName - Feature name
18
+ * @param {Object} options - Options
19
+ * @returns {Object} { status, errors, warnings, issues }
20
+ */
21
+ export async function validateSpec(projectPath, featureName, options = {}) {
22
+ const outputsPath = join(projectPath, '.morph/project/outputs', featureName);
23
+ const specPath = join(outputsPath, 'spec.md');
24
+ const contractsPath = join(outputsPath, 'contracts.cs');
25
+
26
+ const issues = [];
27
+
28
+ // Validate spec.md
29
+ if (existsSync(specPath)) {
30
+ const specContent = readFileSync(specPath, 'utf8');
31
+ const specIssues = validateSpecMd(specContent);
32
+ issues.push(...specIssues);
33
+ }
34
+
35
+ // Validate contracts.cs
36
+ if (existsSync(contractsPath)) {
37
+ const contractsContent = readFileSync(contractsPath, 'utf8');
38
+ const contractsIssues = validateContractsCs(contractsContent);
39
+ issues.push(...contractsIssues);
40
+ }
41
+
42
+ const errors = issues.filter(i => i.level === 'error').length;
43
+ const warnings = issues.filter(i => i.level === 'warning').length;
44
+
45
+ return {
46
+ status: errors > 0 ? 'error' : (warnings > 0 ? 'warning' : 'ok'),
47
+ errors,
48
+ warnings,
49
+ issues,
50
+ passed: errors === 0
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Validate spec.md structure and content
56
+ */
57
+ function validateSpecMd(content) {
58
+ const issues = [];
59
+
60
+ // Required sections
61
+ const requiredSections = [
62
+ { name: 'Overview', pattern: /^##\s+(?:1\.\s+)?Overview/mi },
63
+ { name: 'Functional Requirements', pattern: /^##\s+(?:\d+\.\s+)?Functional Requirements/mi },
64
+ { name: 'Non-Functional Requirements', pattern: /^##\s+(?:\d+\.\s+)?Non-Functional Requirements/mi },
65
+ { name: 'Technical Architecture', pattern: /^##\s+(?:\d+\.\s+)?Technical Architecture/mi },
66
+ { name: 'Data Model', pattern: /^##\s+(?:\d+\.\s+)?Data Model/mi }
67
+ ];
68
+
69
+ for (const section of requiredSections) {
70
+ if (!section.pattern.test(content)) {
71
+ issues.push({
72
+ level: 'error',
73
+ type: 'spec',
74
+ message: `Missing required section: "${section.name}"`,
75
+ solution: `Add ## ${section.name} section to spec.md`
76
+ });
77
+ }
78
+ }
79
+
80
+ // Check for manual infrastructure creation (anti-pattern)
81
+ const manualInfraPatterns = [
82
+ /create.*manually/i,
83
+ /portal.*azure/i,
84
+ /click.*portal/i,
85
+ /via.*portal/i,
86
+ /manual.*setup/i
87
+ ];
88
+
89
+ for (const pattern of manualInfraPatterns) {
90
+ if (pattern.test(content)) {
91
+ issues.push({
92
+ level: 'error',
93
+ type: 'spec',
94
+ message: 'Spec references manual Azure portal creation (anti-pattern)',
95
+ solution: 'Use Infrastructure as Code (Bicep) instead of manual portal operations'
96
+ });
97
+ break; // One error is enough
98
+ }
99
+ }
100
+
101
+ // Check for Bicep reference (best practice)
102
+ const hasBicep = /bicep/i.test(content) || /infrastructure\s+as\s+code/i.test(content) || /\.bicep/i.test(content);
103
+ if (!hasBicep && /azure|sql|storage|container/i.test(content)) {
104
+ issues.push({
105
+ level: 'warning',
106
+ type: 'spec',
107
+ message: 'Spec mentions Azure resources but no Bicep/IaC reference found',
108
+ solution: 'Add Infrastructure section with Bicep module references'
109
+ });
110
+ }
111
+
112
+ return issues;
113
+ }
114
+
115
+ /**
116
+ * Validate contracts.cs coding standards
117
+ */
118
+ function validateContractsCs(content) {
119
+ const issues = [];
120
+
121
+ // 1. Interface naming: Must start with 'I' and be PascalCase
122
+ const interfaceRegex = /public\s+interface\s+(\w+)/g;
123
+ let match;
124
+
125
+ while ((match = interfaceRegex.exec(content)) !== null) {
126
+ const name = match[1];
127
+
128
+ if (!name.startsWith('I')) {
129
+ issues.push({
130
+ level: 'error',
131
+ type: 'contracts',
132
+ message: `Interface '${name}' must start with 'I' (e.g., I${name})`,
133
+ solution: `Rename to I${name}`
134
+ });
135
+ } else if (!isPascalCase(name)) {
136
+ issues.push({
137
+ level: 'error',
138
+ type: 'contracts',
139
+ message: `Interface '${name}' must use PascalCase`,
140
+ solution: `Rename to ${toPascalCase(name)}`
141
+ });
142
+ }
143
+ }
144
+
145
+ // 2. Async methods must have CancellationToken parameter
146
+ const asyncMethodRegex = /Task(?:<[^>]+>)?\s+(\w+Async)\s*\(([^)]*)\)/g;
147
+ while ((match = asyncMethodRegex.exec(content)) !== null) {
148
+ const methodName = match[1];
149
+ const params = match[2];
150
+
151
+ if (!params.includes('CancellationToken')) {
152
+ issues.push({
153
+ level: 'error',
154
+ type: 'contracts',
155
+ message: `Async method '${methodName}' missing CancellationToken parameter`,
156
+ solution: `Add 'CancellationToken ct = default' parameter to ${methodName}`
157
+ });
158
+ }
159
+ }
160
+
161
+ // 3. DTOs should use records (not classes)
162
+ const classRegex = /public\s+class\s+(\w+(?:Dto|Response|Request|Command|Query))/g;
163
+ while ((match = classRegex.exec(content)) !== null) {
164
+ const className = match[1];
165
+ issues.push({
166
+ level: 'warning',
167
+ type: 'contracts',
168
+ message: `DTO '${className}' defined as class instead of record`,
169
+ solution: `Use 'public record ${className}' for immutable DTOs`
170
+ });
171
+ }
172
+
173
+ // 4. Classes should be sealed (unless abstract/base)
174
+ const unsealedClassRegex = /public\s+class\s+(\w+)(?!\s*:\s*\w+Base)/g;
175
+ while ((match = unsealedClassRegex.exec(content)) !== null) {
176
+ const className = match[1];
177
+ // Skip if already sealed
178
+ const lineStart = content.lastIndexOf('\n', match.index) + 1;
179
+ const lineEnd = content.indexOf('\n', match.index);
180
+ const line = content.substring(lineStart, lineEnd);
181
+
182
+ if (!line.includes('sealed') && !line.includes('abstract')) {
183
+ issues.push({
184
+ level: 'warning',
185
+ type: 'contracts',
186
+ message: `Class '${className}' should be sealed (unless designed for inheritance)`,
187
+ solution: `Change to 'public sealed class ${className}'`
188
+ });
189
+ }
190
+ }
191
+
192
+ // 5. PascalCase for constants (NOT UPPER_SNAKE_CASE)
193
+ const upperSnakeRegex = /const\s+\w+\s+([A-Z_][A-Z0-9_]+)\s*=/g;
194
+ while ((match = upperSnakeRegex.exec(content)) !== null) {
195
+ const constName = match[1];
196
+ if (constName.includes('_') && constName === constName.toUpperCase()) {
197
+ issues.push({
198
+ level: 'error',
199
+ type: 'contracts',
200
+ message: `Constant '${constName}' uses UPPER_SNAKE_CASE (anti-pattern in C#)`,
201
+ solution: `Use PascalCase: '${toPascalCase(constName)}'`
202
+ });
203
+ }
204
+ }
205
+
206
+ // 6. Methods returning Task must end with Async
207
+ const taskMethodRegex = /Task(?:<[^>]+>)?\s+(\w+)\s*\(/g;
208
+ while ((match = taskMethodRegex.exec(content)) !== null) {
209
+ const methodName = match[1];
210
+ if (!methodName.endsWith('Async') && !['get', 'set', 'init'].includes(methodName)) {
211
+ issues.push({
212
+ level: 'error',
213
+ type: 'contracts',
214
+ message: `Method '${methodName}' returns Task but doesn't end with 'Async'`,
215
+ solution: `Rename to '${methodName}Async'`
216
+ });
217
+ }
218
+ }
219
+
220
+ // 7. Enum members should be PascalCase (NOT UPPER_SNAKE_CASE)
221
+ const enumRegex = /enum\s+\w+\s*\{([^}]+)\}/gs;
222
+ while ((match = enumRegex.exec(content)) !== null) {
223
+ const enumBody = match[1];
224
+ const memberRegex = /\n\s*([A-Z_][A-Z0-9_]+)/g;
225
+ let memberMatch;
226
+
227
+ while ((memberMatch = memberRegex.exec(enumBody)) !== null) {
228
+ const memberName = memberMatch[1];
229
+ if (memberName.includes('_') && memberName === memberName.toUpperCase()) {
230
+ issues.push({
231
+ level: 'error',
232
+ type: 'contracts',
233
+ message: `Enum member '${memberName}' uses UPPER_SNAKE_CASE (must be PascalCase)`,
234
+ solution: `Use PascalCase: '${toPascalCase(memberName)}'`
235
+ });
236
+ }
237
+ }
238
+ }
239
+
240
+ return issues;
241
+ }
242
+
243
+ /**
244
+ * Check if a string is PascalCase
245
+ */
246
+ function isPascalCase(str) {
247
+ return /^[A-Z][a-z0-9]*([A-Z][a-z0-9]*)*$/.test(str);
248
+ }
249
+
250
+ /**
251
+ * Convert string to PascalCase
252
+ */
253
+ function toPascalCase(str) {
254
+ return str
255
+ .split('_')
256
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
257
+ .join('');
258
+ }