@polymorphism-tech/morph-spec 4.16.0 → 4.18.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 +36 -0
- package/README.md +2 -2
- package/bin/morph-spec.js +3 -0
- package/bin/task-manager.js +999 -962
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/agents/ai-agents/vector-search-expert.md +247 -233
- package/framework/agents/backend/ms-agent-expert.md +1 -1
- package/framework/agents/backend/neon-expert.md +379 -0
- package/framework/agents/frontend/gsap-animator.md +131 -0
- package/framework/agents/integrations/neon-auth.md +151 -0
- package/framework/agents.json +1183 -1085
- package/framework/commands/morph-preflight.md +200 -192
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +1 -1
- package/framework/rules/gsap-standards.md +13 -0
- package/framework/rules/neon-auth-standards.md +13 -0
- package/framework/rules/neon-standards.md +26 -0
- package/framework/skills/level-0-meta/mcp-registry.json +1 -1
- package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +472 -472
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-init/SKILL.md +12 -12
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +5 -5
- package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +5 -5
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +7 -7
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +24 -24
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +12 -12
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +9 -9
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +143 -143
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
- package/framework/standards/STANDARDS.json +197 -116
- package/framework/standards/ai-agents/production.md +1 -0
- package/framework/standards/ai-agents/setup.md +3 -2
- package/framework/standards/ai-agents/workflows.md +1 -0
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +4 -4
- package/framework/standards/backend/database/postgresql/database.md +63 -83
- package/framework/standards/backend/dotnet/scalar-ui.md +172 -172
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +274 -274
- package/framework/standards/backend/integrations/asaas/asaas-api.md +2 -2
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +364 -0
- package/framework/standards/frontend/design-system/animations.md +9 -0
- package/framework/standards/frontend/gsap/gsap-core.md +463 -0
- package/framework/standards/frontend/gsap/gsap-plugins.md +294 -0
- package/framework/standards/frontend/gsap/gsap-scrolltrigger.md +318 -0
- package/framework/standards/frontend/nextjs/motion-patterns.md +1 -0
- package/framework/standards/frontend/nextjs/nextjs-patterns.md +47 -37
- package/framework/standards/infrastructure/docker/easypanel-deploy.md +4 -8
- package/framework/standards/infrastructure/neon/neon-pgrag.md +239 -0
- package/framework/standards/infrastructure/neon/neon-pgvector.md +236 -0
- package/framework/standards/infrastructure/neon/neon-semantic-kernel.md +310 -0
- package/framework/standards/infrastructure/neon/neon-setup.md +186 -0
- package/framework/standards/integration/mcp/mcp-tools.md +32 -34
- package/framework/templates/README.md +20 -20
- package/framework/templates/REGISTRY.json +54 -101
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/backend/repository.cs +148 -148
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
- package/framework/templates/code/dotnet/database/vector-entity.cs +93 -0
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +1 -1
- package/framework/templates/code/sql/{supabase-migration.sql → postgresql-migration.sql} +12 -21
- package/framework/templates/code/sql/{supabase-migration.template.sql → postgresql-migration.template.sql} +13 -18
- package/framework/templates/code/sql/{rls-policy.sql → postgresql-rls-policy.sql} +6 -9
- package/framework/templates/code/typescript/contracts.ts +167 -168
- package/framework/templates/context/CONTEXT.md +3 -3
- package/framework/templates/docs/proposal.md +6 -6
- package/framework/templates/docs/ui-components.md +1 -1
- package/framework/templates/feature/decisions.md +122 -121
- package/framework/templates/feature/recap.md +145 -145
- package/framework/templates/feature/tasks.md +199 -199
- package/framework/templates/frontend/nextjs/gsap-scroll-section.tsx.hbs +44 -0
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +50 -67
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/docker/easypanel.template.json +46 -54
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/integrations/neon-auth-config.cs +133 -0
- package/framework/templates/integrations/semantic-kernel-neon.cs +125 -0
- package/framework/workflows/README.md +1 -1
- package/framework/workflows/docs/full-morph.md +69 -69
- package/package.json +1 -1
- package/src/commands/agents/dispatch-agents.js +19 -15
- package/src/commands/state/advance-phase.js +5 -0
- package/src/commands/tasks/task.js +3 -0
- package/src/core/state/state-manager.js +45 -1
- package/src/core/templates/template-registry.js +1 -66
- package/src/lib/detectors/index.js +4 -4
- package/src/lib/generators/architecture-scanner.js +639 -640
- package/src/lib/installers/mcp-installer.js +1 -1
- package/src/lib/tasks/task-classifier.js +15 -1
- package/src/lib/tasks/test-runner.js +5 -1
- package/src/lib/validators/content/content-validator.js +9 -10
- package/src/lib/validators/validation-runner.js +7 -5
- package/framework/agents/backend/supabase-expert.md +0 -256
- package/framework/agents/integrations/clerk-auth.md +0 -109
- package/framework/rules/clerk-standards.md +0 -13
- package/framework/rules/supabase-standards.md +0 -29
- package/framework/standards/backend/integrations/clerk/clerk-auth.md +0 -290
- package/framework/standards/infrastructure/supabase/mcp-setup.md +0 -142
- package/framework/standards/infrastructure/supabase/supabase-auth.md +0 -176
- package/framework/standards/infrastructure/supabase/supabase-custom-jwt.md +0 -206
- package/framework/standards/infrastructure/supabase/supabase-pgvector.md +0 -169
- package/framework/standards/infrastructure/supabase/supabase-rls.md +0 -184
- package/framework/standards/infrastructure/supabase/supabase-storage.md +0 -153
- package/framework/templates/integrations/clerk-config.cs +0 -217
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// ==============================================================================
|
|
2
|
+
// MORPH-SPEC - Neon Auth Configuration Template
|
|
3
|
+
// Configuracao de autenticacao com Neon Auth (Better Auth) para .NET
|
|
4
|
+
// ==============================================================================
|
|
5
|
+
|
|
6
|
+
using Microsoft.AspNetCore.Authentication;
|
|
7
|
+
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
8
|
+
using Microsoft.IdentityModel.Tokens;
|
|
9
|
+
using System.Security.Claims;
|
|
10
|
+
|
|
11
|
+
namespace {{Namespace}}.Infrastructure.Auth;
|
|
12
|
+
|
|
13
|
+
// ==============================================================================
|
|
14
|
+
// OPTIONS
|
|
15
|
+
// ==============================================================================
|
|
16
|
+
|
|
17
|
+
public class NeonAuthOptions
|
|
18
|
+
{
|
|
19
|
+
public const string SectionName = "NeonAuth";
|
|
20
|
+
|
|
21
|
+
/// <summary>
|
|
22
|
+
/// Neon Auth Base URL (e.g., https://ep-xxx.neonauth.us-east-2.aws.neon.build/neondb/auth)
|
|
23
|
+
/// </summary>
|
|
24
|
+
public string BaseUrl { get; set; } = string.Empty;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ==============================================================================
|
|
28
|
+
// SERVICE EXTENSIONS
|
|
29
|
+
// ==============================================================================
|
|
30
|
+
|
|
31
|
+
public static class NeonAuthServiceExtensions
|
|
32
|
+
{
|
|
33
|
+
/// <summary>
|
|
34
|
+
/// Adds Neon Auth JWT Bearer authentication to the project.
|
|
35
|
+
/// Validates JWTs issued by Neon Auth service.
|
|
36
|
+
/// </summary>
|
|
37
|
+
public static IServiceCollection AddNeonAuthentication(
|
|
38
|
+
this IServiceCollection services,
|
|
39
|
+
IConfiguration configuration)
|
|
40
|
+
{
|
|
41
|
+
services.Configure<NeonAuthOptions>(configuration.GetSection(NeonAuthOptions.SectionName));
|
|
42
|
+
|
|
43
|
+
var neonAuthOptions = configuration.GetSection(NeonAuthOptions.SectionName).Get<NeonAuthOptions>()!;
|
|
44
|
+
|
|
45
|
+
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
46
|
+
.AddJwtBearer(options =>
|
|
47
|
+
{
|
|
48
|
+
options.Authority = neonAuthOptions.BaseUrl;
|
|
49
|
+
options.TokenValidationParameters = new TokenValidationParameters
|
|
50
|
+
{
|
|
51
|
+
ValidateIssuer = true,
|
|
52
|
+
ValidateAudience = false,
|
|
53
|
+
ValidateLifetime = true,
|
|
54
|
+
ValidateIssuerSigningKey = true,
|
|
55
|
+
NameClaimType = "sub"
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
services.AddAuthorization();
|
|
60
|
+
|
|
61
|
+
return services;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ==============================================================================
|
|
66
|
+
// USER CONTEXT SERVICE
|
|
67
|
+
// ==============================================================================
|
|
68
|
+
|
|
69
|
+
public interface ICurrentUserService
|
|
70
|
+
{
|
|
71
|
+
string? UserId { get; }
|
|
72
|
+
string? Email { get; }
|
|
73
|
+
bool IsAuthenticated { get; }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public class CurrentUserService(IHttpContextAccessor httpContextAccessor) : ICurrentUserService
|
|
77
|
+
{
|
|
78
|
+
public string? UserId =>
|
|
79
|
+
httpContextAccessor.HttpContext?.User.FindFirstValue("sub");
|
|
80
|
+
|
|
81
|
+
public string? Email =>
|
|
82
|
+
httpContextAccessor.HttpContext?.User.FindFirstValue("email");
|
|
83
|
+
|
|
84
|
+
public bool IsAuthenticated =>
|
|
85
|
+
httpContextAccessor.HttpContext?.User.Identity?.IsAuthenticated ?? false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ==============================================================================
|
|
89
|
+
// MIDDLEWARE PIPELINE
|
|
90
|
+
// ==============================================================================
|
|
91
|
+
|
|
92
|
+
public static class NeonAuthMiddlewareExtensions
|
|
93
|
+
{
|
|
94
|
+
public static IApplicationBuilder UseNeonAuthentication(this IApplicationBuilder app)
|
|
95
|
+
{
|
|
96
|
+
app.UseAuthentication();
|
|
97
|
+
app.UseAuthorization();
|
|
98
|
+
return app;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ==============================================================================
|
|
103
|
+
// APPSETTINGS EXAMPLE
|
|
104
|
+
// ==============================================================================
|
|
105
|
+
|
|
106
|
+
/*
|
|
107
|
+
{
|
|
108
|
+
"NeonAuth": {
|
|
109
|
+
"BaseUrl": "https://ep-xxx.neonauth.us-east-2.aws.neon.build/neondb/auth"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
// ==============================================================================
|
|
115
|
+
// PROGRAM.CS EXAMPLE
|
|
116
|
+
// ==============================================================================
|
|
117
|
+
|
|
118
|
+
/*
|
|
119
|
+
var builder = WebApplication.CreateBuilder(args);
|
|
120
|
+
|
|
121
|
+
// Add Neon Auth authentication
|
|
122
|
+
builder.Services.AddNeonAuthentication(builder.Configuration);
|
|
123
|
+
builder.Services.AddHttpContextAccessor();
|
|
124
|
+
builder.Services.AddScoped<ICurrentUserService, CurrentUserService>();
|
|
125
|
+
|
|
126
|
+
var app = builder.Build();
|
|
127
|
+
|
|
128
|
+
app.UseNeonAuthentication();
|
|
129
|
+
|
|
130
|
+
app.MapControllers().RequireAuthorization();
|
|
131
|
+
|
|
132
|
+
app.Run();
|
|
133
|
+
*/
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// SEMANTIC KERNEL + NEON PGVECTOR SETUP TEMPLATE
|
|
3
|
+
// Generated by MORPH Framework
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
using Microsoft.Extensions.DependencyInjection;
|
|
7
|
+
using Microsoft.Extensions.VectorData;
|
|
8
|
+
using Microsoft.SemanticKernel;
|
|
9
|
+
using Microsoft.SemanticKernel.Connectors.PgVector;
|
|
10
|
+
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
|
11
|
+
using Microsoft.SemanticKernel.Embeddings;
|
|
12
|
+
using Npgsql;
|
|
13
|
+
|
|
14
|
+
#nullable disable
|
|
15
|
+
|
|
16
|
+
namespace {{NAMESPACE}}.Infrastructure.AI;
|
|
17
|
+
|
|
18
|
+
// ============================================================
|
|
19
|
+
// 1. DATA MODEL — annotated for Semantic Kernel vector store
|
|
20
|
+
// ============================================================
|
|
21
|
+
|
|
22
|
+
public class KnowledgeEntry
|
|
23
|
+
{
|
|
24
|
+
[VectorStoreKey(StorageName = "id")]
|
|
25
|
+
public Guid Id { get; set; }
|
|
26
|
+
|
|
27
|
+
[VectorStoreData(StorageName = "title")]
|
|
28
|
+
public string Title { get; set; } = string.Empty;
|
|
29
|
+
|
|
30
|
+
[VectorStoreData(StorageName = "content", IsFullTextSearchable = true)]
|
|
31
|
+
public string Content { get; set; } = string.Empty;
|
|
32
|
+
|
|
33
|
+
[VectorStoreData(StorageName = "source")]
|
|
34
|
+
public string? Source { get; set; }
|
|
35
|
+
|
|
36
|
+
[VectorStoreVector(Dimensions: 1536, DistanceFunction.CosineSimilarity, StorageName = "embedding")]
|
|
37
|
+
public ReadOnlyMemory<float> Embedding { get; set; }
|
|
38
|
+
|
|
39
|
+
[VectorStoreData(StorageName = "created_at")]
|
|
40
|
+
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ============================================================
|
|
44
|
+
// 2. SERVICE REGISTRATION — add to Program.cs
|
|
45
|
+
// ============================================================
|
|
46
|
+
|
|
47
|
+
public static class SemanticKernelExtensions
|
|
48
|
+
{
|
|
49
|
+
public static IServiceCollection AddSemanticKernelWithNeon(
|
|
50
|
+
this IServiceCollection services,
|
|
51
|
+
string neonConnectionString,
|
|
52
|
+
string openAiApiKey,
|
|
53
|
+
string embeddingModel = "text-embedding-3-small")
|
|
54
|
+
{
|
|
55
|
+
// Neon PostgreSQL data source with pgvector
|
|
56
|
+
services.AddSingleton<NpgsqlDataSource>(sp =>
|
|
57
|
+
{
|
|
58
|
+
var builder = new NpgsqlDataSourceBuilder(neonConnectionString);
|
|
59
|
+
builder.UseVector();
|
|
60
|
+
return builder.Build();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Semantic Kernel with OpenAI + Postgres vector store
|
|
64
|
+
services.AddKernel();
|
|
65
|
+
|
|
66
|
+
services.AddOpenAITextEmbeddingGeneration(embeddingModel, openAiApiKey);
|
|
67
|
+
|
|
68
|
+
services.AddPostgresVectorStore();
|
|
69
|
+
|
|
70
|
+
// VectorStoreTextSearch for natural language queries
|
|
71
|
+
services.AddSingleton<VectorStoreTextSearch<KnowledgeEntry>>(sp =>
|
|
72
|
+
{
|
|
73
|
+
var vectorStore = sp.GetRequiredService<IVectorStore>();
|
|
74
|
+
var collection = vectorStore.GetCollection<Guid, KnowledgeEntry>("knowledge_entries");
|
|
75
|
+
var embeddingGenerator = sp.GetRequiredService<ITextEmbeddingGenerationService>();
|
|
76
|
+
|
|
77
|
+
return new VectorStoreTextSearch<KnowledgeEntry>(
|
|
78
|
+
collection, embeddingGenerator);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return services;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ============================================================
|
|
86
|
+
// SETUP INSTRUCTIONS
|
|
87
|
+
// ============================================================
|
|
88
|
+
//
|
|
89
|
+
// 1. Install NuGet packages:
|
|
90
|
+
//
|
|
91
|
+
// dotnet add package Microsoft.SemanticKernel.Connectors.PgVector --prerelease
|
|
92
|
+
// dotnet add package Microsoft.SemanticKernel.Connectors.OpenAI
|
|
93
|
+
// dotnet add package Npgsql
|
|
94
|
+
//
|
|
95
|
+
// 2. Add to Program.cs:
|
|
96
|
+
//
|
|
97
|
+
// builder.Services.AddSemanticKernelWithNeon(
|
|
98
|
+
// builder.Configuration.GetConnectionString("DefaultConnection")!,
|
|
99
|
+
// builder.Configuration["OpenAI:ApiKey"]!);
|
|
100
|
+
//
|
|
101
|
+
// 3. Create the database table (Neon SQL):
|
|
102
|
+
//
|
|
103
|
+
// CREATE EXTENSION IF NOT EXISTS vector;
|
|
104
|
+
// CREATE TABLE knowledge_entries (
|
|
105
|
+
// id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
106
|
+
// title TEXT NOT NULL,
|
|
107
|
+
// content TEXT NOT NULL,
|
|
108
|
+
// source TEXT,
|
|
109
|
+
// embedding vector(1536),
|
|
110
|
+
// created_at TIMESTAMPTZ DEFAULT now()
|
|
111
|
+
// );
|
|
112
|
+
// CREATE INDEX ON knowledge_entries USING hnsw (embedding vector_cosine_ops);
|
|
113
|
+
//
|
|
114
|
+
// 4. Use in a service or MAF agent:
|
|
115
|
+
//
|
|
116
|
+
// public class SearchService(VectorStoreTextSearch<KnowledgeEntry> search)
|
|
117
|
+
// {
|
|
118
|
+
// public async Task<IReadOnlyList<KnowledgeEntry>> SearchAsync(string query)
|
|
119
|
+
// {
|
|
120
|
+
// var results = await search.SearchAsync(query, new() { Top = 5 });
|
|
121
|
+
// return await results.Results.Select(r => r.Record).ToListAsync();
|
|
122
|
+
// }
|
|
123
|
+
// }
|
|
124
|
+
//
|
|
125
|
+
// ============================================================
|
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
# Workflow: FULL (Alta Complexidade)
|
|
2
|
-
|
|
3
|
-
> **Layer:** 1 | **Load:** on-keyword | **Keywords:** sistema, integração, arquitetura, migração, infraestrutura, plataforma
|
|
4
|
-
|
|
5
|
-
## Critérios
|
|
6
|
-
|
|
7
|
-
- > 10 arquivos, > 500 linhas, múltiplos componentes novos
|
|
8
|
-
- Infraestrutura, decisões arquiteturais estratégicas, multi-domínio
|
|
9
|
-
|
|
10
|
-
## Fases (TODAS obrigatórias)
|
|
11
|
-
|
|
12
|
-
### FASE 0: PROPOSAL
|
|
13
|
-
1. Analisar solicitação, identificar stack, estimar complexidade
|
|
14
|
-
2. Gerar proposal.md, registrar agentes no state.json
|
|
15
|
-
3. Avaliar complexidade → recomendar workflow full
|
|
16
|
-
- **⛔ PAUSA:** Aprovar proposta?
|
|
17
|
-
|
|
18
|
-
### FASE 1: UI/UX (condicional — se keywords UI detectadas)
|
|
19
|
-
1. **Perguntar ao usuário:** layout, referências visuais, fluxo, imagens
|
|
20
|
-
2. Analisar referências fornecidas
|
|
21
|
-
3. Gerar deliverables (design-system, mockups, components, flows)
|
|
22
|
-
4. Validar acessibilidade (WCAG 2.1) e responsividade
|
|
23
|
-
- **⛔ PAUSA:** Aprovar UI/UX?
|
|
24
|
-
|
|
25
|
-
### FASE 2: DESIGN (com clarificação integrada)
|
|
26
|
-
1. Scan real do schema (
|
|
27
|
-
2. Validar schema com usuário (CHECKPOINT 1)
|
|
28
|
-
3. Gerar blueprint arquitetural (CHECKPOINT 2)
|
|
29
|
-
4. Gerar spec.md, contracts.cs/.ts, schema-analysis.md, decisions.md
|
|
30
|
-
5. **Quality + Satisfaction scoring** (0-100):
|
|
31
|
-
- Quality: schema fidelity, functional coverage, blueprint, integrations, infra
|
|
32
|
-
- Satisfaction: functional completeness, failure coverage, edge cases, clarity, contracts
|
|
33
|
-
6. Loop de refinamento: perguntas → atualizar spec.md direto → re-score
|
|
34
|
-
7. Sai quando score ≥ 85 E usuário aprova
|
|
35
|
-
- **⛔ PAUSA:** Aprovar design?
|
|
36
|
-
|
|
37
|
-
### FASE 3: PLAN
|
|
38
|
-
1. Ler spec + contracts + decisions
|
|
39
|
-
2. Analisar codebase existente (Glob + Grep)
|
|
40
|
-
3. Determinar estratégia de execução (single/subagents/agent-teams)
|
|
41
|
-
4. Gerar plan.md com tasks cross-referenciando FRs
|
|
42
|
-
- **⛔ PAUSA:** Aprovar plano?
|
|
43
|
-
|
|
44
|
-
### FASE 4: TASKS
|
|
45
|
-
1. Quebrar spec em tasks T001-TXXX com [FR refs]
|
|
46
|
-
2. Definir ordem, checkpoints, dependências
|
|
47
|
-
- **⛔ PAUSA:** Aprovar tasks?
|
|
48
|
-
|
|
49
|
-
### FASE 5: IMPLEMENT
|
|
50
|
-
1. Para cada task: ler spec FRs → ler contracts → implementar → verificar
|
|
51
|
-
2. `morph-spec task done {feature} {task-id}` após cada task
|
|
52
|
-
3. Checkpoints automáticos a cada 3 tasks
|
|
53
|
-
4. Gerar recap.md ao final
|
|
54
|
-
|
|
55
|
-
### FASE 6: SYNC
|
|
56
|
-
1. Revisar decisions.md, categorizar
|
|
57
|
-
2. Promover para `.morph/context/{category}.md`
|
|
58
|
-
3. Gerar commit dedicado de sync
|
|
59
|
-
- **⛔ PAUSA:** Aprovar promoção de decisões?
|
|
60
|
-
|
|
61
|
-
## Outputs Completos
|
|
62
|
-
proposal.md, design-system.md, mockups.md, components.md, flows.md, spec.md, contracts.cs/.ts, schema-analysis.md, decisions.md, plan.md, tasks.md, recap.md
|
|
63
|
-
|
|
64
|
-
## Exemplos
|
|
65
|
-
- "Sistema de relatórios com jobs agendados Hangfire"
|
|
66
|
-
- "Autenticação completa com
|
|
67
|
-
- "Dashboard com gráficos, tabelas e filtros complexos"
|
|
68
|
-
- "RAG pipeline com AI Search e embeddings"
|
|
69
|
-
- "Infraestrutura completa com Docker/Easypanel"
|
|
1
|
+
# Workflow: FULL (Alta Complexidade)
|
|
2
|
+
|
|
3
|
+
> **Layer:** 1 | **Load:** on-keyword | **Keywords:** sistema, integração, arquitetura, migração, infraestrutura, plataforma
|
|
4
|
+
|
|
5
|
+
## Critérios
|
|
6
|
+
|
|
7
|
+
- > 10 arquivos, > 500 linhas, múltiplos componentes novos
|
|
8
|
+
- Infraestrutura, decisões arquiteturais estratégicas, multi-domínio
|
|
9
|
+
|
|
10
|
+
## Fases (TODAS obrigatórias)
|
|
11
|
+
|
|
12
|
+
### FASE 0: PROPOSAL
|
|
13
|
+
1. Analisar solicitação, identificar stack, estimar complexidade
|
|
14
|
+
2. Gerar proposal.md, registrar agentes no state.json
|
|
15
|
+
3. Avaliar complexidade → recomendar workflow full
|
|
16
|
+
- **⛔ PAUSA:** Aprovar proposta?
|
|
17
|
+
|
|
18
|
+
### FASE 1: UI/UX (condicional — se keywords UI detectadas)
|
|
19
|
+
1. **Perguntar ao usuário:** layout, referências visuais, fluxo, imagens
|
|
20
|
+
2. Analisar referências fornecidas
|
|
21
|
+
3. Gerar deliverables (design-system, mockups, components, flows)
|
|
22
|
+
4. Validar acessibilidade (WCAG 2.1) e responsividade
|
|
23
|
+
- **⛔ PAUSA:** Aprovar UI/UX?
|
|
24
|
+
|
|
25
|
+
### FASE 2: DESIGN (com clarificação integrada)
|
|
26
|
+
1. Scan real do schema (Neon/psql, EF Core, TypeScript)
|
|
27
|
+
2. Validar schema com usuário (CHECKPOINT 1)
|
|
28
|
+
3. Gerar blueprint arquitetural (CHECKPOINT 2)
|
|
29
|
+
4. Gerar spec.md, contracts.cs/.ts, schema-analysis.md, decisions.md
|
|
30
|
+
5. **Quality + Satisfaction scoring** (0-100):
|
|
31
|
+
- Quality: schema fidelity, functional coverage, blueprint, integrations, infra
|
|
32
|
+
- Satisfaction: functional completeness, failure coverage, edge cases, clarity, contracts
|
|
33
|
+
6. Loop de refinamento: perguntas → atualizar spec.md direto → re-score
|
|
34
|
+
7. Sai quando score ≥ 85 E usuário aprova
|
|
35
|
+
- **⛔ PAUSA:** Aprovar design?
|
|
36
|
+
|
|
37
|
+
### FASE 3: PLAN
|
|
38
|
+
1. Ler spec + contracts + decisions
|
|
39
|
+
2. Analisar codebase existente (Glob + Grep)
|
|
40
|
+
3. Determinar estratégia de execução (single/subagents/agent-teams)
|
|
41
|
+
4. Gerar plan.md com tasks cross-referenciando FRs
|
|
42
|
+
- **⛔ PAUSA:** Aprovar plano?
|
|
43
|
+
|
|
44
|
+
### FASE 4: TASKS
|
|
45
|
+
1. Quebrar spec em tasks T001-TXXX com [FR refs]
|
|
46
|
+
2. Definir ordem, checkpoints, dependências
|
|
47
|
+
- **⛔ PAUSA:** Aprovar tasks?
|
|
48
|
+
|
|
49
|
+
### FASE 5: IMPLEMENT
|
|
50
|
+
1. Para cada task: ler spec FRs → ler contracts → implementar → verificar
|
|
51
|
+
2. `morph-spec task done {feature} {task-id}` após cada task
|
|
52
|
+
3. Checkpoints automáticos a cada 3 tasks
|
|
53
|
+
4. Gerar recap.md ao final
|
|
54
|
+
|
|
55
|
+
### FASE 6: SYNC
|
|
56
|
+
1. Revisar decisions.md, categorizar
|
|
57
|
+
2. Promover para `.morph/context/{category}.md`
|
|
58
|
+
3. Gerar commit dedicado de sync
|
|
59
|
+
- **⛔ PAUSA:** Aprovar promoção de decisões?
|
|
60
|
+
|
|
61
|
+
## Outputs Completos
|
|
62
|
+
proposal.md, design-system.md, mockups.md, components.md, flows.md, spec.md, contracts.cs/.ts, schema-analysis.md, decisions.md, plan.md, tasks.md, recap.md
|
|
63
|
+
|
|
64
|
+
## Exemplos
|
|
65
|
+
- "Sistema de relatórios com jobs agendados Hangfire"
|
|
66
|
+
- "Autenticação completa com Neon Auth (login, signup, OAuth)"
|
|
67
|
+
- "Dashboard com gráficos, tabelas e filtros complexos"
|
|
68
|
+
- "RAG pipeline com AI Search e embeddings"
|
|
69
|
+
- "Infraestrutura completa com Docker/Easypanel"
|
package/package.json
CHANGED
|
@@ -40,7 +40,7 @@ const AGENTS_JSON_PATH = join(__dirname, '../../../framework/agents.json');
|
|
|
40
40
|
const PHASES_JSON_PATH = join(__dirname, '../../../framework/phases.json');
|
|
41
41
|
|
|
42
42
|
/** Phases where agent dispatch is meaningful */
|
|
43
|
-
const DISPATCHABLE_PHASES = ['design', 'tasks', 'implement'];
|
|
43
|
+
const DISPATCHABLE_PHASES = ['design', 'plan', 'tasks', 'implement'];
|
|
44
44
|
|
|
45
45
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
46
46
|
// Required Skills Injection
|
|
@@ -238,7 +238,7 @@ export async function buildDispatchConfig(projectPath, featureName, phase, opts
|
|
|
238
238
|
// Collect dispatchable agents
|
|
239
239
|
const isValidateMode = opts.mode === 'validate';
|
|
240
240
|
const processed = new Set();
|
|
241
|
-
|
|
241
|
+
let dispatchableAgents = [];
|
|
242
242
|
|
|
243
243
|
const candidateIds = [
|
|
244
244
|
...activeAgentIds,
|
|
@@ -304,6 +304,23 @@ export async function buildDispatchConfig(projectPath, featureName, phase, opts
|
|
|
304
304
|
return a.id.localeCompare(b.id);
|
|
305
305
|
});
|
|
306
306
|
|
|
307
|
+
// Pre-flight: check agent files exist, skip missing (BUG-001)
|
|
308
|
+
const warnings = [];
|
|
309
|
+
const verifiedAgents = [];
|
|
310
|
+
for (const agent of dispatchableAgents) {
|
|
311
|
+
const agentFile = join(projectPath, '.claude', 'agents', `${agent.subagentType}.md`);
|
|
312
|
+
if (!existsSync(agentFile)) {
|
|
313
|
+
warnings.push({
|
|
314
|
+
agentId: agent.id,
|
|
315
|
+
subagentType: agent.subagentType,
|
|
316
|
+
message: `File .claude/agents/${agent.subagentType}.md not found — agent skipped from dispatch`,
|
|
317
|
+
});
|
|
318
|
+
} else {
|
|
319
|
+
verifiedAgents.push(agent);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
dispatchableAgents = verifiedAgents;
|
|
323
|
+
|
|
307
324
|
const shouldDispatch = dispatchableAgents.length >= 2;
|
|
308
325
|
|
|
309
326
|
// ── Recommended agents by group ───────────────────────────────────────────
|
|
@@ -320,19 +337,6 @@ export async function buildDispatchConfig(projectPath, featureName, phase, opts
|
|
|
320
337
|
tasks = parseAndGroupTasks(tasksPath);
|
|
321
338
|
}
|
|
322
339
|
|
|
323
|
-
// ── Pre-flight: check agent files exist ──────────────────────────────────
|
|
324
|
-
const warnings = [];
|
|
325
|
-
for (const agent of dispatchableAgents) {
|
|
326
|
-
const agentFile = join(projectPath, '.claude', 'agents', `${agent.subagentType}.md`);
|
|
327
|
-
if (!existsSync(agentFile)) {
|
|
328
|
-
warnings.push({
|
|
329
|
-
agentId: agent.id,
|
|
330
|
-
subagentType: agent.subagentType,
|
|
331
|
-
message: `File .claude/agents/${agent.subagentType}.md not found`,
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
340
|
return {
|
|
337
341
|
feature: featureName,
|
|
338
342
|
phase,
|
|
@@ -128,6 +128,11 @@ function getNextPhase(currentPhase, feature, skipOptional, featureName) {
|
|
|
128
128
|
|
|
129
129
|
// Skip uiux if no UI design agents are active
|
|
130
130
|
if (candidate === 'uiux') {
|
|
131
|
+
if (feature.workflow === 'migration' || feature.workflow === 'refactor') {
|
|
132
|
+
console.log(chalk.gray(` ⏩ Skipping ${candidate}: ${feature.workflow} workflow — no visual changes`));
|
|
133
|
+
skippedPhases.push(candidate);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
131
136
|
const hasUiDesignAgent = feature.activeAgents?.some(a => uiDesignAgents.includes(a));
|
|
132
137
|
if (!hasUiDesignAgent && skipOptional !== false) {
|
|
133
138
|
console.log(chalk.gray(` ⏩ Skipping ${candidate}: no UI design agents active`));
|
|
@@ -42,6 +42,7 @@ export async function taskDoneCommand(featureName, taskIds, options) {
|
|
|
42
42
|
try {
|
|
43
43
|
const args = ['done', featureName, ...taskIds];
|
|
44
44
|
if (options.skipValidation) args.push('--skip-validation');
|
|
45
|
+
if (options.skipBuild) args.push('--skip-build');
|
|
45
46
|
if (options.reason) args.push('--reason', options.reason);
|
|
46
47
|
if (options.note) args.push('--note', options.note);
|
|
47
48
|
if (options.dryRun) args.push('--dry-run');
|
|
@@ -76,6 +77,7 @@ export async function taskBulkDoneCommand(featureName, range, options) {
|
|
|
76
77
|
if (options.from) args.push('--from', options.from);
|
|
77
78
|
if (options.to) args.push('--to', options.to);
|
|
78
79
|
if (options.skipValidation) args.push('--skip-validation');
|
|
80
|
+
if (options.skipBuild) args.push('--skip-build');
|
|
79
81
|
if (options.reason) args.push('--reason', options.reason);
|
|
80
82
|
if (options.note) args.push('--note', options.note);
|
|
81
83
|
if (options.dryRun) args.push('--dry-run');
|
|
@@ -93,6 +95,7 @@ export async function taskAnnotateCommand(featureName, taskId, text, options) {
|
|
|
93
95
|
try {
|
|
94
96
|
const args = ['annotate', featureName, taskId, text];
|
|
95
97
|
if (options.type) args.push('--type', options.type);
|
|
98
|
+
if (options.markDone) args.push('--mark-done');
|
|
96
99
|
await executeTaskManager(args);
|
|
97
100
|
} catch (error) {
|
|
98
101
|
console.error(chalk.red(`Error: ${error.message}`));
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Used both by CLI commands and internal automation.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { readFileSync, writeFileSync, renameSync, unlinkSync, existsSync, mkdirSync } from 'fs';
|
|
8
|
+
import { readFileSync, writeFileSync, renameSync, unlinkSync, existsSync, mkdirSync, rmdirSync, statSync } from 'fs';
|
|
9
9
|
import { join, dirname } from 'path';
|
|
10
10
|
import { detectWorkflow } from '../workflows/workflow-detector.js';
|
|
11
11
|
import { getAllOutputPaths, getOutputPath } from '../paths/output-schema.js';
|
|
@@ -163,6 +163,50 @@ export function saveState(state) {
|
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
// ============================================================================
|
|
167
|
+
// File Locking (BUG-002)
|
|
168
|
+
// ============================================================================
|
|
169
|
+
|
|
170
|
+
const LOCK_DIR_NAME = '.morph/state.json.lock';
|
|
171
|
+
const LOCK_MAX_RETRIES = 5;
|
|
172
|
+
const LOCK_BASE_MS = 100;
|
|
173
|
+
const LOCK_STALE_MS = 30_000;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Wrap a read-modify-write cycle on state.json with a mkdir-based mutex.
|
|
177
|
+
* @param {Function} fn - Async function to execute while holding the lock
|
|
178
|
+
* @returns {Promise<*>} Return value of fn
|
|
179
|
+
*/
|
|
180
|
+
export async function withStateLock(fn) {
|
|
181
|
+
const lockPath = join(process.cwd(), LOCK_DIR_NAME);
|
|
182
|
+
|
|
183
|
+
for (let attempt = 0; attempt <= LOCK_MAX_RETRIES; attempt++) {
|
|
184
|
+
try {
|
|
185
|
+
mkdirSync(lockPath);
|
|
186
|
+
break;
|
|
187
|
+
} catch (err) {
|
|
188
|
+
if (err.code !== 'EEXIST') throw err;
|
|
189
|
+
try {
|
|
190
|
+
const lockStat = statSync(lockPath);
|
|
191
|
+
if (Date.now() - lockStat.mtimeMs > LOCK_STALE_MS) {
|
|
192
|
+
try { rmdirSync(lockPath); } catch { }
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
} catch { }
|
|
196
|
+
if (attempt === LOCK_MAX_RETRIES) {
|
|
197
|
+
throw new Error('Could not acquire state lock after retries');
|
|
198
|
+
}
|
|
199
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, LOCK_BASE_MS * (2 ** attempt));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
return await fn();
|
|
205
|
+
} finally {
|
|
206
|
+
try { rmdirSync(lockPath); } catch { }
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
166
210
|
/**
|
|
167
211
|
* Initialize new state file
|
|
168
212
|
* @param {Object} options - Options
|
|
@@ -108,7 +108,7 @@ export function getTemplatesByPhase(phase, projectPath = null) {
|
|
|
108
108
|
/**
|
|
109
109
|
* Get templates by stack.
|
|
110
110
|
*
|
|
111
|
-
* @param {string} stackId - Stack identifier ('blazor-azure', 'nextjs-
|
|
111
|
+
* @param {string} stackId - Stack identifier ('blazor-azure', 'nextjs-neon')
|
|
112
112
|
* @param {string} [projectPath] - Optional project path
|
|
113
113
|
* @returns {Array} Filtered template array (includes universal + stack-specific)
|
|
114
114
|
*/
|
|
@@ -226,28 +226,6 @@ export function searchTemplates(keyword, projectPath = null) {
|
|
|
226
226
|
});
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
/**
|
|
230
|
-
* Get registry statistics.
|
|
231
|
-
*
|
|
232
|
-
* @param {string} [projectPath] - Optional project path
|
|
233
|
-
* @returns {Object} Statistics object
|
|
234
|
-
*/
|
|
235
|
-
export function getRegistryStats(projectPath = null) {
|
|
236
|
-
const registry = loadTemplateRegistry(projectPath);
|
|
237
|
-
return registry.stats || {};
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Validate that a template exists in the registry.
|
|
242
|
-
*
|
|
243
|
-
* @param {string} templateId - Template identifier
|
|
244
|
-
* @param {string} [projectPath] - Optional project path
|
|
245
|
-
* @returns {boolean} True if template exists
|
|
246
|
-
*/
|
|
247
|
-
export function templateExists(templateId, projectPath = null) {
|
|
248
|
-
return getTemplateById(templateId, projectPath) !== null;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
229
|
/**
|
|
252
230
|
* Get template absolute path with fallback logic.
|
|
253
231
|
*
|
|
@@ -276,46 +254,3 @@ export function resolveTemplatePathById(templateId, projectPath) {
|
|
|
276
254
|
return existsSync(frameworkPath) ? frameworkPath : null;
|
|
277
255
|
}
|
|
278
256
|
|
|
279
|
-
/**
|
|
280
|
-
* List all template IDs.
|
|
281
|
-
*
|
|
282
|
-
* @param {string} [projectPath] - Optional project path
|
|
283
|
-
* @returns {string[]} Array of template IDs
|
|
284
|
-
*/
|
|
285
|
-
export function listTemplateIds(projectPath = null) {
|
|
286
|
-
const templates = getAllTemplates(projectPath);
|
|
287
|
-
return templates.map(t => t.id);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Get templates that use Handlebars engine.
|
|
292
|
-
*
|
|
293
|
-
* @param {string} [projectPath] - Optional project path
|
|
294
|
-
* @returns {Array} Templates using Handlebars
|
|
295
|
-
*/
|
|
296
|
-
export function getHandlebarsTemplates(projectPath = null) {
|
|
297
|
-
const templates = getAllTemplates(projectPath);
|
|
298
|
-
return templates.filter(t => t.engine === 'handlebars');
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Get templates that use custom engine (need migration).
|
|
303
|
-
*
|
|
304
|
-
* @param {string} [projectPath] - Optional project path
|
|
305
|
-
* @returns {Array} Templates using custom engine
|
|
306
|
-
*/
|
|
307
|
-
export function getCustomEngineTemplates(projectPath = null) {
|
|
308
|
-
const templates = getAllTemplates(projectPath);
|
|
309
|
-
return templates.filter(t => t.engine === 'custom');
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Get deprecated templates.
|
|
314
|
-
*
|
|
315
|
-
* @param {string} [projectPath] - Optional project path
|
|
316
|
-
* @returns {Array} Deprecated templates
|
|
317
|
-
*/
|
|
318
|
-
export function getDeprecatedTemplates(projectPath = null) {
|
|
319
|
-
const templates = getAllTemplates(projectPath);
|
|
320
|
-
return templates.filter(t => t.deprecated === true);
|
|
321
|
-
}
|
|
@@ -71,11 +71,11 @@ export function detectProject(targetPath, opts = {}) {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// Integrations
|
|
74
|
-
if (depNames.includes('@
|
|
75
|
-
structure.integrations.push('
|
|
74
|
+
if (depNames.includes('@neondatabase/serverless') || depNames.includes('postgres') || depNames.includes('pg')) {
|
|
75
|
+
structure.integrations.push('neon');
|
|
76
76
|
}
|
|
77
|
-
if (depNames.includes('@
|
|
78
|
-
structure.integrations.push('
|
|
77
|
+
if (depNames.includes('@neondatabase/auth')) {
|
|
78
|
+
structure.integrations.push('neon-auth');
|
|
79
79
|
}
|
|
80
80
|
if (depNames.includes('stripe')) {
|
|
81
81
|
structure.integrations.push('stripe');
|