@polymorphism-tech/morph-spec 1.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 (83) hide show
  1. package/README.md +279 -0
  2. package/bin/morph-spec.js +53 -0
  3. package/content/.claude/commands/morph-apply.md +66 -0
  4. package/content/.claude/commands/morph-archive.md +79 -0
  5. package/content/.claude/commands/morph-costs.md +206 -0
  6. package/content/.claude/commands/morph-infra.md +209 -0
  7. package/content/.claude/commands/morph-proposal.md +60 -0
  8. package/content/.claude/commands/morph-status.md +71 -0
  9. package/content/.claude/settings.local.json +15 -0
  10. package/content/.claude/skills/infra/bicep-architect.md +419 -0
  11. package/content/.claude/skills/infra/container-specialist.md +437 -0
  12. package/content/.claude/skills/infra/devops-engineer.md +405 -0
  13. package/content/.claude/skills/integrations/asaas-financial.md +333 -0
  14. package/content/.claude/skills/integrations/azure-identity.md +309 -0
  15. package/content/.claude/skills/integrations/clerk-auth.md +290 -0
  16. package/content/.claude/skills/specialists/azure-architect.md +142 -0
  17. package/content/.claude/skills/specialists/cost-guardian.md +110 -0
  18. package/content/.claude/skills/specialists/ef-modeler.md +200 -0
  19. package/content/.claude/skills/specialists/hangfire-orchestrator.md +245 -0
  20. package/content/.claude/skills/specialists/ms-agent-expert.md +209 -0
  21. package/content/.claude/skills/specialists/po-pm-advisor.md +197 -0
  22. package/content/.claude/skills/specialists/standards-architect.md +78 -0
  23. package/content/.claude/skills/specialists/ui-ux-designer.md +325 -0
  24. package/content/.claude/skills/stacks/dotnet-blazor.md +352 -0
  25. package/content/.claude/skills/stacks/dotnet-nextjs.md +402 -0
  26. package/content/.claude/skills/stacks/shopify.md +445 -0
  27. package/content/.morph/archive/.gitkeep +25 -0
  28. package/content/.morph/config/agents.json +149 -0
  29. package/content/.morph/config/config.template.json +96 -0
  30. package/content/.morph/examples/api-nextjs/README.md +241 -0
  31. package/content/.morph/examples/api-nextjs/contracts.ts +307 -0
  32. package/content/.morph/examples/api-nextjs/spec.md +399 -0
  33. package/content/.morph/examples/api-nextjs/tasks.md +168 -0
  34. package/content/.morph/examples/micro-saas/README.md +125 -0
  35. package/content/.morph/examples/micro-saas/contracts.cs +358 -0
  36. package/content/.morph/examples/micro-saas/decisions.md +246 -0
  37. package/content/.morph/examples/micro-saas/spec.md +236 -0
  38. package/content/.morph/examples/micro-saas/tasks.md +150 -0
  39. package/content/.morph/examples/multi-agent/README.md +309 -0
  40. package/content/.morph/examples/multi-agent/contracts.cs +433 -0
  41. package/content/.morph/examples/multi-agent/spec.md +479 -0
  42. package/content/.morph/examples/multi-agent/tasks.md +185 -0
  43. package/content/.morph/features/.gitkeep +25 -0
  44. package/content/.morph/project.md +159 -0
  45. package/content/.morph/specs/.gitkeep +20 -0
  46. package/content/.morph/standards/architecture.md +190 -0
  47. package/content/.morph/standards/azure.md +184 -0
  48. package/content/.morph/standards/coding.md +342 -0
  49. package/content/.morph/templates/agent.cs +172 -0
  50. package/content/.morph/templates/component.razor +239 -0
  51. package/content/.morph/templates/contracts.cs +217 -0
  52. package/content/.morph/templates/decisions.md +106 -0
  53. package/content/.morph/templates/infra/app-insights.bicep +63 -0
  54. package/content/.morph/templates/infra/container-app-env.bicep +49 -0
  55. package/content/.morph/templates/infra/container-app.bicep +156 -0
  56. package/content/.morph/templates/infra/key-vault.bicep +91 -0
  57. package/content/.morph/templates/infra/main.bicep +155 -0
  58. package/content/.morph/templates/infra/parameters.dev.json +23 -0
  59. package/content/.morph/templates/infra/parameters.prod.json +23 -0
  60. package/content/.morph/templates/infra/sql-database.bicep +103 -0
  61. package/content/.morph/templates/infra/storage.bicep +106 -0
  62. package/content/.morph/templates/integrations/asaas-client.cs +387 -0
  63. package/content/.morph/templates/integrations/asaas-webhook.cs +351 -0
  64. package/content/.morph/templates/integrations/azure-identity-config.cs +288 -0
  65. package/content/.morph/templates/integrations/clerk-config.cs +258 -0
  66. package/content/.morph/templates/job.cs +171 -0
  67. package/content/.morph/templates/migration.cs +83 -0
  68. package/content/.morph/templates/proposal.md +155 -0
  69. package/content/.morph/templates/recap.md +105 -0
  70. package/content/.morph/templates/repository.cs +141 -0
  71. package/content/.morph/templates/saas/subscription.cs +347 -0
  72. package/content/.morph/templates/saas/tenant.cs +338 -0
  73. package/content/.morph/templates/service.cs +139 -0
  74. package/content/.morph/templates/spec.md +147 -0
  75. package/content/.morph/templates/tasks.md +235 -0
  76. package/content/.morph/templates/test.cs +239 -0
  77. package/content/CLAUDE.md +318 -0
  78. package/package.json +50 -0
  79. package/src/commands/doctor.js +132 -0
  80. package/src/commands/init.js +121 -0
  81. package/src/commands/update.js +84 -0
  82. package/src/utils/file-copier.js +50 -0
  83. package/src/utils/logger.js +32 -0
@@ -0,0 +1,342 @@
1
+ # Padrões de Código - MORPH Framework
2
+
3
+ ## 📝 Nomenclatura
4
+
5
+ ### Classes e Interfaces
6
+ ```csharp
7
+ // Interfaces: sempre com prefixo I
8
+ public interface IReportService { }
9
+ public interface IReportRepository { }
10
+
11
+ // Classes: PascalCase, sufixo indica tipo
12
+ public class ReportService : IReportService { }
13
+ public class ReportRepository : IReportRepository { }
14
+ public class ReportController : ControllerBase { }
15
+ public class ReportGeneratorJob { } // Hangfire job
16
+ public class ReportAnalyzerAgent { } // AI Agent
17
+ ```
18
+
19
+ ### Métodos
20
+ ```csharp
21
+ // Async sempre com sufixo Async
22
+ public async Task<Report> GetByIdAsync(int id) { }
23
+ public async Task CreateAsync(Report report) { }
24
+
25
+ // Métodos síncronos sem sufixo
26
+ public Report GetById(int id) { }
27
+ public void Validate(Report report) { }
28
+ ```
29
+
30
+ ### Variáveis e Parâmetros
31
+ ```csharp
32
+ // camelCase para variáveis locais e parâmetros
33
+ public async Task ProcessAsync(string reportId, CancellationToken cancellationToken)
34
+ {
35
+ var report = await _repository.GetByIdAsync(reportId);
36
+ var analysisResult = await _analyzer.AnalyzeAsync(report);
37
+ }
38
+
39
+ // _camelCase para campos privados
40
+ private readonly IReportRepository _repository;
41
+ private readonly ILogger<ReportService> _logger;
42
+ ```
43
+
44
+ ---
45
+
46
+ ## 📁 Estrutura de Projeto
47
+
48
+ ```
49
+ src/
50
+ ├── MyProject.Web/ # Blazor Server
51
+ │ ├── Components/
52
+ │ │ ├── Pages/ # Páginas (rotas)
53
+ │ │ ├── Shared/ # Componentes compartilhados
54
+ │ │ └── Layout/ # Layouts
55
+ │ └── Program.cs
56
+
57
+ ├── MyProject.Application/ # Casos de uso
58
+ │ ├── Features/
59
+ │ │ └── Reports/
60
+ │ │ ├── Commands/
61
+ │ │ ├── Queries/
62
+ │ │ └── Services/
63
+ │ └── Common/
64
+
65
+ ├── MyProject.Domain/ # Entidades e regras
66
+ │ ├── Entities/
67
+ │ ├── ValueObjects/
68
+ │ └── Enums/
69
+
70
+ ├── MyProject.Infrastructure/ # Implementações
71
+ │ ├── Data/
72
+ │ │ ├── Configurations/
73
+ │ │ ├── Migrations/
74
+ │ │ └── AppDbContext.cs
75
+ │ └── Services/
76
+
77
+ └── MyProject.Agents/ # MS Agent Framework
78
+ └── ReportAnalyzer/
79
+ ```
80
+
81
+ ---
82
+
83
+ ## 💉 Dependency Injection
84
+
85
+ ```csharp
86
+ // Usar primary constructor (C# 12+)
87
+ public class ReportService(
88
+ IReportRepository repository,
89
+ ILogger<ReportService> logger) : IReportService
90
+ {
91
+ public async Task<Report> GetByIdAsync(int id)
92
+ {
93
+ logger.LogInformation("Getting report {ReportId}", id);
94
+ return await repository.GetByIdAsync(id);
95
+ }
96
+ }
97
+ ```
98
+
99
+ ---
100
+
101
+ ## 🗄️ Entity Framework Core
102
+
103
+ ### Entidades
104
+ ```csharp
105
+ public class ReportSchedule
106
+ {
107
+ public int Id { get; private set; }
108
+ public string Name { get; private set; } = string.Empty;
109
+ public ReportType ReportType { get; private set; }
110
+ public string CronExpression { get; private set; } = string.Empty;
111
+ public List<string> Recipients { get; private set; } = [];
112
+ public bool IsActive { get; private set; }
113
+ public DateTime CreatedAt { get; private set; }
114
+
115
+ // Factory method para criação
116
+ public static ReportSchedule Create(string name, ReportType reportType,
117
+ string cronExpression, List<string> recipients)
118
+ {
119
+ return new ReportSchedule
120
+ {
121
+ Name = name,
122
+ ReportType = reportType,
123
+ CronExpression = cronExpression,
124
+ Recipients = recipients,
125
+ IsActive = true,
126
+ CreatedAt = DateTime.UtcNow
127
+ };
128
+ }
129
+
130
+ // Métodos de domínio
131
+ public void Activate() => IsActive = true;
132
+ public void Deactivate() => IsActive = false;
133
+ }
134
+ ```
135
+
136
+ ### Configuração Fluent API
137
+ ```csharp
138
+ public class ReportScheduleConfiguration : IEntityTypeConfiguration<ReportSchedule>
139
+ {
140
+ public void Configure(EntityTypeBuilder<ReportSchedule> builder)
141
+ {
142
+ builder.ToTable("ReportSchedules");
143
+ builder.HasKey(x => x.Id);
144
+
145
+ builder.Property(x => x.Name)
146
+ .IsRequired()
147
+ .HasMaxLength(200);
148
+
149
+ // JSON column para lista
150
+ builder.Property(x => x.Recipients)
151
+ .HasConversion(
152
+ v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
153
+ v => JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions?)null) ?? [])
154
+ .HasColumnType("nvarchar(max)");
155
+ }
156
+ }
157
+ ```
158
+
159
+ ---
160
+
161
+ ## 🔥 Componentes Blazor
162
+
163
+ ```razor
164
+ @page "/reports/schedules"
165
+ @attribute [Authorize(Policy = "CanManageReports")]
166
+ @inject IReportScheduleService ReportService
167
+ @inject ILogger<ReportScheduleList> Logger
168
+
169
+ <PageTitle>Report Schedules</PageTitle>
170
+
171
+ @if (_isLoading)
172
+ {
173
+ <LoadingSpinner />
174
+ }
175
+ else if (_schedules is null || !_schedules.Any())
176
+ {
177
+ <EmptyState Message="No schedules configured" />
178
+ }
179
+ else
180
+ {
181
+ <div class="schedule-list">
182
+ @foreach (var schedule in _schedules)
183
+ {
184
+ <ScheduleCard Schedule="schedule" OnToggle="HandleToggleAsync" />
185
+ }
186
+ </div>
187
+ }
188
+
189
+ @code {
190
+ private List<ReportScheduleDto>? _schedules;
191
+ private bool _isLoading = true;
192
+
193
+ protected override async Task OnInitializedAsync()
194
+ {
195
+ try
196
+ {
197
+ _schedules = await ReportService.GetAllAsync();
198
+ }
199
+ catch (Exception ex)
200
+ {
201
+ Logger.LogError(ex, "Failed to load schedules");
202
+ }
203
+ finally
204
+ {
205
+ _isLoading = false;
206
+ }
207
+ }
208
+ }
209
+ ```
210
+
211
+ ### Padrões Obrigatórios
212
+ - ✅ Loading state em toda página
213
+ - ✅ Empty state quando lista vazia
214
+ - ✅ Error handling com try/catch
215
+ - ✅ `@inject` para DI (não construtor)
216
+ - ✅ `@attribute [Authorize]` quando necessário
217
+
218
+ ---
219
+
220
+ ## 🤖 MS Agent Framework
221
+
222
+ ```csharp
223
+ public class ReportAnalyzerAgent(
224
+ Kernel kernel,
225
+ ILogger<ReportAnalyzerAgent> logger) : IReportAnalyzerAgent
226
+ {
227
+ public async Task<AnalysisResult> AnalyzeAsync(
228
+ ReportData reportData,
229
+ CancellationToken cancellationToken = default)
230
+ {
231
+ var prompt = $"""
232
+ Analyze the following report data:
233
+ {reportData.ToJson()}
234
+
235
+ Provide: summary, top 3 insights, recommendations.
236
+ Respond in JSON format.
237
+ """;
238
+
239
+ try
240
+ {
241
+ var response = await kernel.InvokePromptAsync(prompt, cancellationToken: cancellationToken);
242
+ return ParseResponse(response.ToString());
243
+ }
244
+ catch (Exception ex)
245
+ {
246
+ logger.LogError(ex, "Analysis failed");
247
+ throw new ReportAnalysisException("Failed to analyze", ex);
248
+ }
249
+ }
250
+ }
251
+ ```
252
+
253
+ ---
254
+
255
+ ## ⏰ Hangfire Jobs
256
+
257
+ ```csharp
258
+ public class ReportGeneratorJob(
259
+ IReportScheduleService scheduleService,
260
+ IReportAnalyzerAgent analyzer,
261
+ ILogger<ReportGeneratorJob> logger) : IReportGeneratorJob
262
+ {
263
+ [AutomaticRetry(Attempts = 3, DelaysInSeconds = new[] { 60, 300, 900 })]
264
+ [Queue("reports")]
265
+ public async Task ExecuteAsync(int scheduleId, CancellationToken cancellationToken)
266
+ {
267
+ logger.LogInformation("Starting report generation for {ScheduleId}", scheduleId);
268
+
269
+ var schedule = await scheduleService.GetByIdAsync(scheduleId);
270
+ if (schedule is null || !schedule.IsActive)
271
+ {
272
+ logger.LogWarning("Schedule {ScheduleId} not found or inactive", scheduleId);
273
+ return;
274
+ }
275
+
276
+ // ... implementação
277
+ }
278
+ }
279
+ ```
280
+
281
+ ---
282
+
283
+ ## 🧪 Testes
284
+
285
+ ### Nomenclatura
286
+ ```csharp
287
+ // Classe: {ClasseTestada}Tests
288
+ public class ReportServiceTests
289
+ {
290
+ // Método: {Método}_{Cenário}_{ResultadoEsperado}
291
+ [Fact]
292
+ public async Task GetByIdAsync_WithValidId_ReturnsReport() { }
293
+
294
+ [Fact]
295
+ public async Task GetByIdAsync_WithInvalidId_ThrowsNotFoundException() { }
296
+ }
297
+ ```
298
+
299
+ ### Estrutura AAA
300
+ ```csharp
301
+ [Fact]
302
+ public async Task CreateAsync_WithValidData_CreatesSchedule()
303
+ {
304
+ // Arrange
305
+ var request = new CreateReportScheduleRequest("Daily Sales", ReportType.Sales);
306
+
307
+ // Act
308
+ var result = await _service.CreateAsync(request);
309
+
310
+ // Assert
311
+ Assert.NotNull(result);
312
+ Assert.Equal(request.Name, result.Name);
313
+ }
314
+ ```
315
+
316
+ ---
317
+
318
+ ## 🚫 Anti-Patterns a Evitar
319
+
320
+ ```csharp
321
+ // ❌ Service Locator
322
+ var service = serviceProvider.GetService<IReportService>();
323
+
324
+ // ✅ Constructor Injection
325
+ public class MyClass(IReportService reportService) { }
326
+
327
+ // ❌ Async void
328
+ public async void ProcessReport() { }
329
+
330
+ // ✅ Async Task
331
+ public async Task ProcessReportAsync() { }
332
+
333
+ // ❌ Catching generic Exception sem log
334
+ catch (Exception) { }
335
+
336
+ // ✅ Log and handle
337
+ catch (Exception ex)
338
+ {
339
+ _logger.LogError(ex, "Operation failed");
340
+ throw;
341
+ }
342
+ ```
@@ -0,0 +1,172 @@
1
+ // ============================================================
2
+ // MS AGENT FRAMEWORK TEMPLATE
3
+ // Generated by MORPH Framework
4
+ // ============================================================
5
+
6
+ using Microsoft.Extensions.Logging;
7
+ using Microsoft.SemanticKernel;
8
+ using System.Text.Json;
9
+
10
+ namespace MyProject.Agents.{Feature}Analyzer;
11
+
12
+ /// <summary>
13
+ /// AI Agent for analyzing {Feature} data.
14
+ /// Uses Semantic Kernel with Azure OpenAI (gpt-4o-mini by default).
15
+ /// </summary>
16
+ public class {Feature}AnalyzerAgent(
17
+ Kernel kernel,
18
+ ILogger<{Feature}AnalyzerAgent> logger) : I{Feature}AnalyzerAgent
19
+ {
20
+ private const int MaxRetries = 3;
21
+ private static readonly TimeSpan[] RetryDelays =
22
+ {
23
+ TimeSpan.FromSeconds(1),
24
+ TimeSpan.FromSeconds(2),
25
+ TimeSpan.FromSeconds(4)
26
+ };
27
+
28
+ /// <inheritdoc />
29
+ public async Task<{Feature}AnalysisResult> AnalyzeAsync(
30
+ {Feature}Data data,
31
+ CancellationToken cancellationToken = default)
32
+ {
33
+ logger.LogInformation("Starting {Feature} analysis for: {Content}",
34
+ data.Content.Length > 100 ? data.Content[..100] + "..." : data.Content);
35
+
36
+ var prompt = BuildPrompt(data);
37
+
38
+ for (int attempt = 0; attempt < MaxRetries; attempt++)
39
+ {
40
+ try
41
+ {
42
+ var response = await kernel.InvokePromptAsync(
43
+ prompt,
44
+ cancellationToken: cancellationToken);
45
+
46
+ var result = ParseResponse(response.ToString());
47
+
48
+ logger.LogInformation(
49
+ "Analysis completed. Confidence: {Confidence:P0}",
50
+ result.ConfidenceScore);
51
+
52
+ return result;
53
+ }
54
+ catch (Exception ex) when (attempt < MaxRetries - 1)
55
+ {
56
+ logger.LogWarning(ex,
57
+ "Analysis attempt {Attempt} failed, retrying...",
58
+ attempt + 1);
59
+
60
+ await Task.Delay(RetryDelays[attempt], cancellationToken);
61
+ }
62
+ }
63
+
64
+ throw new {Feature}ProcessingException("Analysis failed after all retries");
65
+ }
66
+
67
+ #region Private Methods
68
+
69
+ private static string BuildPrompt({Feature}Data data)
70
+ {
71
+ return $"""
72
+ You are an expert analyst for {Feature} data.
73
+
74
+ Analyze the following data and provide insights:
75
+
76
+ <data>
77
+ {data.Content}
78
+ </data>
79
+
80
+ Provide your analysis in the following JSON format:
81
+ {{
82
+ "summary": "Brief summary of the analysis (1-2 sentences)",
83
+ "insights": ["insight 1", "insight 2", "insight 3"],
84
+ "recommendations": ["recommendation 1", "recommendation 2"],
85
+ "confidenceScore": 0.85
86
+ }}
87
+
88
+ Guidelines:
89
+ - Be concise and actionable
90
+ - Confidence score should be between 0 and 1
91
+ - Provide at least 2 insights
92
+ - Provide at least 1 recommendation
93
+ - Focus on practical, business-relevant observations
94
+
95
+ Respond ONLY with the JSON object, no additional text.
96
+ """;
97
+ }
98
+
99
+ private {Feature}AnalysisResult ParseResponse(string response)
100
+ {
101
+ try
102
+ {
103
+ // Clean up response (remove markdown code blocks if present)
104
+ var json = response
105
+ .Replace("```json", "")
106
+ .Replace("```", "")
107
+ .Trim();
108
+
109
+ var parsed = JsonSerializer.Deserialize<AnalysisResponse>(json,
110
+ new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
111
+
112
+ if (parsed is null)
113
+ {
114
+ throw new {Feature}ProcessingException("Failed to parse analysis response");
115
+ }
116
+
117
+ return new {Feature}AnalysisResult(
118
+ parsed.Summary ?? "Analysis completed",
119
+ parsed.Insights ?? new List<string>(),
120
+ parsed.Recommendations ?? new List<string>(),
121
+ parsed.ConfidenceScore
122
+ );
123
+ }
124
+ catch (JsonException ex)
125
+ {
126
+ logger.LogError(ex, "Failed to parse AI response: {Response}", response);
127
+ throw new {Feature}ProcessingException("Failed to parse analysis response", ex);
128
+ }
129
+ }
130
+
131
+ private class AnalysisResponse
132
+ {
133
+ public string? Summary { get; set; }
134
+ public List<string>? Insights { get; set; }
135
+ public List<string>? Recommendations { get; set; }
136
+ public double ConfidenceScore { get; set; }
137
+ }
138
+
139
+ #endregion
140
+ }
141
+
142
+ // ============================================================
143
+ // DEPENDENCY INJECTION SETUP
144
+ // ============================================================
145
+ //
146
+ // In Program.cs or a DI extension:
147
+ //
148
+ // public static IServiceCollection AddAgents(
149
+ // this IServiceCollection services,
150
+ // IConfiguration configuration)
151
+ // {
152
+ // // Configure Semantic Kernel with Azure OpenAI
153
+ // var builder = Kernel.CreateBuilder();
154
+ //
155
+ // builder.AddAzureOpenAIChatCompletion(
156
+ // deploymentName: "gpt-4o-mini",
157
+ // endpoint: configuration["AzureOpenAI:Endpoint"]!,
158
+ // apiKey: configuration["AzureOpenAI:ApiKey"]!);
159
+ //
160
+ // // Or with Managed Identity (preferred):
161
+ // // builder.AddAzureOpenAIChatCompletion(
162
+ // // deploymentName: "gpt-4o-mini",
163
+ // // endpoint: configuration["AzureOpenAI:Endpoint"]!,
164
+ // // credentials: new DefaultAzureCredential());
165
+ //
166
+ // services.AddSingleton(builder.Build());
167
+ // services.AddScoped<I{Feature}AnalyzerAgent, {Feature}AnalyzerAgent>();
168
+ //
169
+ // return services;
170
+ // }
171
+ //
172
+ // ============================================================