@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.
- package/README.md +279 -0
- package/bin/morph-spec.js +53 -0
- package/content/.claude/commands/morph-apply.md +66 -0
- package/content/.claude/commands/morph-archive.md +79 -0
- package/content/.claude/commands/morph-costs.md +206 -0
- package/content/.claude/commands/morph-infra.md +209 -0
- package/content/.claude/commands/morph-proposal.md +60 -0
- package/content/.claude/commands/morph-status.md +71 -0
- package/content/.claude/settings.local.json +15 -0
- package/content/.claude/skills/infra/bicep-architect.md +419 -0
- package/content/.claude/skills/infra/container-specialist.md +437 -0
- package/content/.claude/skills/infra/devops-engineer.md +405 -0
- package/content/.claude/skills/integrations/asaas-financial.md +333 -0
- package/content/.claude/skills/integrations/azure-identity.md +309 -0
- package/content/.claude/skills/integrations/clerk-auth.md +290 -0
- package/content/.claude/skills/specialists/azure-architect.md +142 -0
- package/content/.claude/skills/specialists/cost-guardian.md +110 -0
- package/content/.claude/skills/specialists/ef-modeler.md +200 -0
- package/content/.claude/skills/specialists/hangfire-orchestrator.md +245 -0
- package/content/.claude/skills/specialists/ms-agent-expert.md +209 -0
- package/content/.claude/skills/specialists/po-pm-advisor.md +197 -0
- package/content/.claude/skills/specialists/standards-architect.md +78 -0
- package/content/.claude/skills/specialists/ui-ux-designer.md +325 -0
- package/content/.claude/skills/stacks/dotnet-blazor.md +352 -0
- package/content/.claude/skills/stacks/dotnet-nextjs.md +402 -0
- package/content/.claude/skills/stacks/shopify.md +445 -0
- package/content/.morph/archive/.gitkeep +25 -0
- package/content/.morph/config/agents.json +149 -0
- package/content/.morph/config/config.template.json +96 -0
- package/content/.morph/examples/api-nextjs/README.md +241 -0
- package/content/.morph/examples/api-nextjs/contracts.ts +307 -0
- package/content/.morph/examples/api-nextjs/spec.md +399 -0
- package/content/.morph/examples/api-nextjs/tasks.md +168 -0
- package/content/.morph/examples/micro-saas/README.md +125 -0
- package/content/.morph/examples/micro-saas/contracts.cs +358 -0
- package/content/.morph/examples/micro-saas/decisions.md +246 -0
- package/content/.morph/examples/micro-saas/spec.md +236 -0
- package/content/.morph/examples/micro-saas/tasks.md +150 -0
- package/content/.morph/examples/multi-agent/README.md +309 -0
- package/content/.morph/examples/multi-agent/contracts.cs +433 -0
- package/content/.morph/examples/multi-agent/spec.md +479 -0
- package/content/.morph/examples/multi-agent/tasks.md +185 -0
- package/content/.morph/features/.gitkeep +25 -0
- package/content/.morph/project.md +159 -0
- package/content/.morph/specs/.gitkeep +20 -0
- package/content/.morph/standards/architecture.md +190 -0
- package/content/.morph/standards/azure.md +184 -0
- package/content/.morph/standards/coding.md +342 -0
- package/content/.morph/templates/agent.cs +172 -0
- package/content/.morph/templates/component.razor +239 -0
- package/content/.morph/templates/contracts.cs +217 -0
- package/content/.morph/templates/decisions.md +106 -0
- package/content/.morph/templates/infra/app-insights.bicep +63 -0
- package/content/.morph/templates/infra/container-app-env.bicep +49 -0
- package/content/.morph/templates/infra/container-app.bicep +156 -0
- package/content/.morph/templates/infra/key-vault.bicep +91 -0
- package/content/.morph/templates/infra/main.bicep +155 -0
- package/content/.morph/templates/infra/parameters.dev.json +23 -0
- package/content/.morph/templates/infra/parameters.prod.json +23 -0
- package/content/.morph/templates/infra/sql-database.bicep +103 -0
- package/content/.morph/templates/infra/storage.bicep +106 -0
- package/content/.morph/templates/integrations/asaas-client.cs +387 -0
- package/content/.morph/templates/integrations/asaas-webhook.cs +351 -0
- package/content/.morph/templates/integrations/azure-identity-config.cs +288 -0
- package/content/.morph/templates/integrations/clerk-config.cs +258 -0
- package/content/.morph/templates/job.cs +171 -0
- package/content/.morph/templates/migration.cs +83 -0
- package/content/.morph/templates/proposal.md +155 -0
- package/content/.morph/templates/recap.md +105 -0
- package/content/.morph/templates/repository.cs +141 -0
- package/content/.morph/templates/saas/subscription.cs +347 -0
- package/content/.morph/templates/saas/tenant.cs +338 -0
- package/content/.morph/templates/service.cs +139 -0
- package/content/.morph/templates/spec.md +147 -0
- package/content/.morph/templates/tasks.md +235 -0
- package/content/.morph/templates/test.cs +239 -0
- package/content/CLAUDE.md +318 -0
- package/package.json +50 -0
- package/src/commands/doctor.js +132 -0
- package/src/commands/init.js +121 -0
- package/src/commands/update.js +84 -0
- package/src/utils/file-copier.js +50 -0
- package/src/utils/logger.js +32 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
# MORPH-SPEC - Instruções para Claude Code
|
|
2
|
+
|
|
3
|
+
**M**ethodical **O**rchestration for **R**eliable **P**roduction-ready **SPEC**-driven development
|
|
4
|
+
|
|
5
|
+
> by Polymorphism Tech
|
|
6
|
+
|
|
7
|
+
Você é um sistema de desenvolvimento orientado por especificações. Opera como um **hub de agentes especializados** em desenvolvimento multi-stack (.NET/Blazor, .NET/Next.js, Shopify) com Infrastructure as Code.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## REGRAS CRÍTICAS
|
|
12
|
+
|
|
13
|
+
### NUNCA:
|
|
14
|
+
- Pular direto para código sem especificação
|
|
15
|
+
- Implementar sem aprovação do design
|
|
16
|
+
- Ignorar os padrões em `.morph/standards/`
|
|
17
|
+
- Ultrapassar limites de custo sem aprovação
|
|
18
|
+
- Criar recursos Azure manualmente no portal (use Bicep)
|
|
19
|
+
- Gerar código sem contracts definidos
|
|
20
|
+
|
|
21
|
+
### SEMPRE:
|
|
22
|
+
- Seguir as 5 fases obrigatórias
|
|
23
|
+
- Gerar outputs em `.morph/features/{feature}/`
|
|
24
|
+
- Validar custos antes de propor recursos Azure
|
|
25
|
+
- Documentar decisões em `decisions.md`
|
|
26
|
+
- Checkpoint a cada 3 tasks implementadas
|
|
27
|
+
- Usar Infrastructure as Code (Bicep)
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## WORKFLOW (5 Fases)
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
35
|
+
│ SETUP │ ─▶ │ DESIGN │ ─▶ │ CLARIFY │ ─▶ │ TASKS │ ─▶ │IMPLEMENT │
|
|
36
|
+
│ │ │ │ │ │ │ │ │ │
|
|
37
|
+
│ Context │ │ Spec + │ │ Q&A + │ │ Break │ │ Execute │
|
|
38
|
+
│ + Agents │ │ Contracts│ │ Edge │ │ Down │ │ + Test │
|
|
39
|
+
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
|
|
40
|
+
⛔ │ ⛔
|
|
41
|
+
User Approval │ User Approval
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Fase 1: SETUP
|
|
45
|
+
```
|
|
46
|
+
Gatilho: Nova feature request
|
|
47
|
+
Comando: /morph-proposal {feature-name}
|
|
48
|
+
|
|
49
|
+
Ações:
|
|
50
|
+
1. Ler `.morph/project.md` para contexto
|
|
51
|
+
2. Identificar stack (Blazor, Next.js, Shopify)
|
|
52
|
+
3. Analisar keywords para ativar agentes
|
|
53
|
+
4. Criar `.morph/features/{feature}/`
|
|
54
|
+
5. Gerar proposal.md inicial
|
|
55
|
+
|
|
56
|
+
Output: Proposta para revisão
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Fase 2: DESIGN
|
|
60
|
+
```
|
|
61
|
+
Gatilho: Setup concluído
|
|
62
|
+
|
|
63
|
+
Ações:
|
|
64
|
+
1. Expandir spec.md com detalhes técnicos
|
|
65
|
+
2. Gerar contracts.cs com interfaces/DTOs
|
|
66
|
+
3. Iniciar decisions.md com ADRs relevantes
|
|
67
|
+
4. Estimar custos e documentar
|
|
68
|
+
5. Definir infraestrutura necessária (Bicep)
|
|
69
|
+
|
|
70
|
+
Outputs:
|
|
71
|
+
- .morph/features/{feature}/spec.md
|
|
72
|
+
- .morph/features/{feature}/contracts.cs
|
|
73
|
+
- .morph/features/{feature}/decisions.md
|
|
74
|
+
|
|
75
|
+
⛔ PARE e aguarde aprovação do usuário
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Fase 3: CLARIFY
|
|
79
|
+
```
|
|
80
|
+
Gatilho: Design aprovado
|
|
81
|
+
|
|
82
|
+
Ações:
|
|
83
|
+
1. Identificar ambiguidades
|
|
84
|
+
2. Fazer perguntas de clarificação
|
|
85
|
+
3. Documentar respostas no spec.md
|
|
86
|
+
4. Validar edge cases
|
|
87
|
+
|
|
88
|
+
Output: Spec atualizado com clarificações
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Fase 4: TASKS
|
|
92
|
+
```
|
|
93
|
+
Gatilho: Clarificações resolvidas
|
|
94
|
+
|
|
95
|
+
Ações:
|
|
96
|
+
1. Quebrar spec em tasks detalhadas
|
|
97
|
+
2. Definir ordem de execução
|
|
98
|
+
3. Estabelecer checkpoints
|
|
99
|
+
4. Mapear dependências
|
|
100
|
+
5. Incluir tasks de IaC se necessário
|
|
101
|
+
|
|
102
|
+
Output: .morph/features/{feature}/tasks.md
|
|
103
|
+
|
|
104
|
+
⛔ PARE e aguarde aprovação do task breakdown
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Fase 5: IMPLEMENT
|
|
108
|
+
```
|
|
109
|
+
Gatilho: Tasks aprovadas
|
|
110
|
+
Comando: /morph-apply {feature-name}
|
|
111
|
+
|
|
112
|
+
Ações:
|
|
113
|
+
1. Implementar task por task
|
|
114
|
+
2. Atualizar status de cada task
|
|
115
|
+
3. Checkpoint a cada 3 tasks
|
|
116
|
+
4. Gerar recap.md ao final
|
|
117
|
+
|
|
118
|
+
Outputs:
|
|
119
|
+
- Código implementado
|
|
120
|
+
- Testes criados
|
|
121
|
+
- .morph/features/{feature}/recap.md
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## ESTRUTURA DO PROJETO
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
projeto/
|
|
130
|
+
├── CLAUDE.md # Este arquivo
|
|
131
|
+
├── .morph/
|
|
132
|
+
│ ├── project.md # Contexto do projeto
|
|
133
|
+
│ ├── config/
|
|
134
|
+
│ │ ├── config.json # Configuração
|
|
135
|
+
│ │ └── agents.json # Definição dos agentes
|
|
136
|
+
│ ├── standards/
|
|
137
|
+
│ │ ├── coding.md # Padrões de código
|
|
138
|
+
│ │ ├── architecture.md # Padrões de arquitetura
|
|
139
|
+
│ │ └── azure.md # Padrões Azure e IaC
|
|
140
|
+
│ ├── templates/ # Templates para outputs
|
|
141
|
+
│ ├── specs/ # Specs ATIVAS (verdade atual)
|
|
142
|
+
│ ├── features/ # Features EM DESENVOLVIMENTO
|
|
143
|
+
│ │ └── {feature-name}/
|
|
144
|
+
│ │ ├── proposal.md
|
|
145
|
+
│ │ ├── spec.md
|
|
146
|
+
│ │ ├── tasks.md
|
|
147
|
+
│ │ ├── contracts.cs
|
|
148
|
+
│ │ ├── decisions.md
|
|
149
|
+
│ │ └── recap.md
|
|
150
|
+
│ └── archive/ # Features CONCLUÍDAS
|
|
151
|
+
├── .claude/
|
|
152
|
+
│ ├── commands/ # Slash commands
|
|
153
|
+
│ │ ├── morph-proposal.md
|
|
154
|
+
│ │ ├── morph-apply.md
|
|
155
|
+
│ │ ├── morph-status.md
|
|
156
|
+
│ │ ├── morph-archive.md
|
|
157
|
+
│ │ ├── morph-infra.md # (new)
|
|
158
|
+
│ │ └── morph-costs.md # (new)
|
|
159
|
+
│ └── skills/ # Agent skills (new)
|
|
160
|
+
│ ├── specialists/
|
|
161
|
+
│ ├── integrations/
|
|
162
|
+
│ ├── stacks/
|
|
163
|
+
│ └── infra/
|
|
164
|
+
└── infra/ # IaC templates (new)
|
|
165
|
+
├── main.bicep
|
|
166
|
+
└── modules/
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## AGENTES
|
|
172
|
+
|
|
173
|
+
### Core Agents (Sempre Ativos)
|
|
174
|
+
|
|
175
|
+
| Agente | Responsabilidades |
|
|
176
|
+
|--------|-------------------|
|
|
177
|
+
| **Standards Architect** | Padrões em `.morph/standards/`, nomenclatura, revisão |
|
|
178
|
+
| **Azure Architect** | Infra Azure, Bicep IaC, Container Apps, SQL |
|
|
179
|
+
| **Cost Guardian** | Validar custos, alertar recursos caros, bloquear não aprovados |
|
|
180
|
+
|
|
181
|
+
### Stack Agents (Por Stack)
|
|
182
|
+
|
|
183
|
+
| Agente | Stack | Keywords |
|
|
184
|
+
|--------|-------|----------|
|
|
185
|
+
| **Blazor Builder** | .NET + Blazor | blazor, razor, server-side |
|
|
186
|
+
| **NextJS Expert** | .NET + Next.js | nextjs, react, api, frontend |
|
|
187
|
+
| **Shopify Expert** | Shopify | shopify, hydrogen, liquid |
|
|
188
|
+
|
|
189
|
+
### Specialist Agents (Sob Demanda)
|
|
190
|
+
|
|
191
|
+
| Agente | Keywords de Ativação |
|
|
192
|
+
|--------|---------------------|
|
|
193
|
+
| **EF Modeler** | entity, database, migration, ef core |
|
|
194
|
+
| **MS Agent Expert** | agent, ai, semantic kernel, llm |
|
|
195
|
+
| **Hangfire Orchestrator** | scheduled, job, background, cron |
|
|
196
|
+
| **UI/UX Designer** | wizard, dashboard, complex form, drag-drop |
|
|
197
|
+
| **PO/PM Advisor** | unclear, requirements, priority, ROI |
|
|
198
|
+
|
|
199
|
+
### Integration Agents (Sob Demanda)
|
|
200
|
+
|
|
201
|
+
| Agente | Keywords | Docs |
|
|
202
|
+
|--------|----------|------|
|
|
203
|
+
| **Asaas Financial** | asaas, payment, pix, boleto | [API](https://docs.asaas.com/) |
|
|
204
|
+
| **Clerk Auth** | clerk, auth, login, signup | [SDK](https://clerk.com/) |
|
|
205
|
+
| **Azure Identity** | identity, entra, microsoft auth | [Docs](https://learn.microsoft.com/entra/) |
|
|
206
|
+
|
|
207
|
+
### Infra Agents (Sob Demanda)
|
|
208
|
+
|
|
209
|
+
| Agente | Keywords | Docs |
|
|
210
|
+
|--------|----------|------|
|
|
211
|
+
| **Bicep Architect** | bicep, iac, infra, provision | [Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/) |
|
|
212
|
+
| **DevOps Engineer** | pipeline, ci/cd, deploy, release | [Pipelines](https://learn.microsoft.com/azure/devops/pipelines/) |
|
|
213
|
+
| **Container Specialist** | docker, container, containerize | [Container Apps](https://learn.microsoft.com/azure/container-apps/) |
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## LIMITES DE CUSTO
|
|
218
|
+
|
|
219
|
+
| Nível | Limite | Requisito |
|
|
220
|
+
|-------|--------|-----------|
|
|
221
|
+
| Sem aprovação | Free tier apenas | Nenhum |
|
|
222
|
+
| Com aprovação | Até $10/mês | Confirmação |
|
|
223
|
+
| Acima de $10 | Justificativa detalhada | ADR |
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## INFRASTRUCTURE AS CODE
|
|
228
|
+
|
|
229
|
+
### Princípio: Zero Portal
|
|
230
|
+
|
|
231
|
+
> NUNCA criar recursos Azure manualmente. Tudo via Bicep.
|
|
232
|
+
|
|
233
|
+
### Estrutura IaC
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
infra/
|
|
237
|
+
├── main.bicep # Entry point
|
|
238
|
+
├── parameters.dev.json # Params dev
|
|
239
|
+
├── parameters.prod.json # Params prod
|
|
240
|
+
└── modules/
|
|
241
|
+
├── container-app.bicep
|
|
242
|
+
├── sql-database.bicep
|
|
243
|
+
├── storage.bicep
|
|
244
|
+
├── key-vault.bicep
|
|
245
|
+
└── app-insights.bicep
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Quando usar SDK .NET vs Bicep
|
|
249
|
+
|
|
250
|
+
| Recurso | Abordagem |
|
|
251
|
+
|---------|-----------|
|
|
252
|
+
| Infra estática (SQL, Storage, Container Apps) | Bicep |
|
|
253
|
+
| Recursos dinâmicos (Fabric, Azure AI) | SDK .NET |
|
|
254
|
+
| CI/CD | YAML + Bicep |
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## COMANDOS (Slash Commands)
|
|
259
|
+
|
|
260
|
+
| Comando | Ação |
|
|
261
|
+
|---------|------|
|
|
262
|
+
| `/morph-proposal {feature}` | Criar nova proposta |
|
|
263
|
+
| `/morph-apply {feature}` | Implementar feature aprovada |
|
|
264
|
+
| `/morph-status` | Mostrar status do projeto |
|
|
265
|
+
| `/morph-archive {feature}` | Arquivar feature concluída |
|
|
266
|
+
| `/morph-infra {action}` | Gerenciar infraestrutura (new) |
|
|
267
|
+
| `/morph-costs` | Estimar e reportar custos (new) |
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## REFERÊNCIAS
|
|
272
|
+
|
|
273
|
+
Consulte sempre:
|
|
274
|
+
- `.morph/project.md` - Contexto do projeto
|
|
275
|
+
- `.morph/standards/coding.md` - Padrões de código
|
|
276
|
+
- `.morph/standards/architecture.md` - Padrões de arquitetura
|
|
277
|
+
- `.morph/standards/azure.md` - Padrões Azure e IaC
|
|
278
|
+
- `.morph/templates/` - Templates para outputs
|
|
279
|
+
- `.morph/config/agents.json` - Definição dos agentes
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## QUICK START
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
1. Usuário: "Quero implementar relatórios agendados"
|
|
287
|
+
|
|
288
|
+
2. Claude: /morph-proposal scheduled-reports
|
|
289
|
+
→ Carrega contexto
|
|
290
|
+
→ Ativa agentes: Hangfire Orchestrator, Blazor Builder
|
|
291
|
+
→ Cria proposta
|
|
292
|
+
|
|
293
|
+
3. Usuário: "Aprovado"
|
|
294
|
+
|
|
295
|
+
4. Claude: Expande spec, contracts, decisions
|
|
296
|
+
→ Aguarda aprovação do design
|
|
297
|
+
|
|
298
|
+
5. Usuário: "Aprovado"
|
|
299
|
+
|
|
300
|
+
6. Claude: Quebra em tasks detalhadas
|
|
301
|
+
→ Aguarda aprovação do breakdown
|
|
302
|
+
|
|
303
|
+
7. Usuário: "Aprovado"
|
|
304
|
+
|
|
305
|
+
8. Claude: /morph-apply scheduled-reports
|
|
306
|
+
→ Implementa task por task
|
|
307
|
+
→ Checkpoints a cada 3 tasks
|
|
308
|
+
|
|
309
|
+
9. Usuário: "Feature concluída"
|
|
310
|
+
|
|
311
|
+
10. Claude: /morph-archive scheduled-reports
|
|
312
|
+
→ Move para archive
|
|
313
|
+
→ Extrai specs
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
*MORPH-SPEC by Polymorphism Tech © 2024-2025*
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@polymorphism-tech/morph-spec",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MORPH-SPEC: Methodical Orchestration for Reliable Production-ready SPEC-driven development framework for Claude Code",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"claude-code",
|
|
7
|
+
"claude",
|
|
8
|
+
"ai-coding",
|
|
9
|
+
"spec-driven",
|
|
10
|
+
"dotnet",
|
|
11
|
+
"blazor",
|
|
12
|
+
"framework",
|
|
13
|
+
"developer-tools",
|
|
14
|
+
"morph",
|
|
15
|
+
"polymorphism"
|
|
16
|
+
],
|
|
17
|
+
"main": "src/index.js",
|
|
18
|
+
"bin": {
|
|
19
|
+
"morph-spec": "./bin/morph-spec.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"bin/",
|
|
23
|
+
"src/",
|
|
24
|
+
"content/"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18.0.0"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"start": "node bin/morph-spec.js",
|
|
32
|
+
"test": "node --test"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"chalk": "^5.3.0",
|
|
36
|
+
"commander": "^12.0.0",
|
|
37
|
+
"fs-extra": "^11.2.0",
|
|
38
|
+
"ora": "^8.0.0"
|
|
39
|
+
},
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/polymorphism-tech/morph-spec-framework.git"
|
|
43
|
+
},
|
|
44
|
+
"author": "Polymorphism Tech",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"homepage": "https://github.com/polymorphism-tech/morph-spec-framework#readme",
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/polymorphism-tech/morph-spec-framework/issues"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { logger } from '../utils/logger.js';
|
|
4
|
+
import { pathExists, readJson } from '../utils/file-copier.js';
|
|
5
|
+
|
|
6
|
+
export async function doctorCommand() {
|
|
7
|
+
const targetPath = process.cwd();
|
|
8
|
+
|
|
9
|
+
logger.header('MORPH-SPEC Health Check');
|
|
10
|
+
|
|
11
|
+
const checks = [];
|
|
12
|
+
let hasErrors = false;
|
|
13
|
+
|
|
14
|
+
// Check CLAUDE.md
|
|
15
|
+
const claudeMd = join(targetPath, 'CLAUDE.md');
|
|
16
|
+
if (await pathExists(claudeMd)) {
|
|
17
|
+
checks.push({ name: 'CLAUDE.md', status: 'ok' });
|
|
18
|
+
} else {
|
|
19
|
+
checks.push({ name: 'CLAUDE.md', status: 'missing' });
|
|
20
|
+
hasErrors = true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Check .morph folder
|
|
24
|
+
const morphPath = join(targetPath, '.morph');
|
|
25
|
+
if (await pathExists(morphPath)) {
|
|
26
|
+
checks.push({ name: '.morph/', status: 'ok' });
|
|
27
|
+
} else {
|
|
28
|
+
checks.push({ name: '.morph/', status: 'missing' });
|
|
29
|
+
hasErrors = true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check config.json
|
|
33
|
+
const configPath = join(morphPath, 'config', 'config.json');
|
|
34
|
+
if (await pathExists(configPath)) {
|
|
35
|
+
try {
|
|
36
|
+
const config = await readJson(configPath);
|
|
37
|
+
if (config.project?.name) {
|
|
38
|
+
checks.push({ name: 'config.json', status: 'ok' });
|
|
39
|
+
} else {
|
|
40
|
+
checks.push({ name: 'config.json', status: 'warn', msg: 'project.name not set' });
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
checks.push({ name: 'config.json', status: 'error', msg: 'invalid JSON' });
|
|
44
|
+
hasErrors = true;
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
checks.push({ name: 'config.json', status: 'missing' });
|
|
48
|
+
hasErrors = true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Check agents.json
|
|
52
|
+
const agentsPath = join(morphPath, 'config', 'agents.json');
|
|
53
|
+
if (await pathExists(agentsPath)) {
|
|
54
|
+
checks.push({ name: 'agents.json', status: 'ok' });
|
|
55
|
+
} else {
|
|
56
|
+
checks.push({ name: 'agents.json', status: 'missing' });
|
|
57
|
+
hasErrors = true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check standards
|
|
61
|
+
const standardsPath = join(morphPath, 'standards');
|
|
62
|
+
if (await pathExists(standardsPath)) {
|
|
63
|
+
const codingStd = join(standardsPath, 'coding.md');
|
|
64
|
+
const archStd = join(standardsPath, 'architecture.md');
|
|
65
|
+
const azureStd = join(standardsPath, 'azure.md');
|
|
66
|
+
|
|
67
|
+
const hasAll = await Promise.all([
|
|
68
|
+
pathExists(codingStd),
|
|
69
|
+
pathExists(archStd),
|
|
70
|
+
pathExists(azureStd)
|
|
71
|
+
]).then(results => results.every(Boolean));
|
|
72
|
+
|
|
73
|
+
if (hasAll) {
|
|
74
|
+
checks.push({ name: 'standards/', status: 'ok' });
|
|
75
|
+
} else {
|
|
76
|
+
checks.push({ name: 'standards/', status: 'warn', msg: 'some files missing' });
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
checks.push({ name: 'standards/', status: 'missing' });
|
|
80
|
+
hasErrors = true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check templates
|
|
84
|
+
const templatesPath = join(morphPath, 'templates');
|
|
85
|
+
if (await pathExists(templatesPath)) {
|
|
86
|
+
checks.push({ name: 'templates/', status: 'ok' });
|
|
87
|
+
} else {
|
|
88
|
+
checks.push({ name: 'templates/', status: 'missing' });
|
|
89
|
+
hasErrors = true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Check .claude folder
|
|
93
|
+
const claudePath = join(targetPath, '.claude');
|
|
94
|
+
if (await pathExists(claudePath)) {
|
|
95
|
+
const commandsPath = join(claudePath, 'commands');
|
|
96
|
+
const skillsPath = join(claudePath, 'skills');
|
|
97
|
+
|
|
98
|
+
const hasCommands = await pathExists(commandsPath);
|
|
99
|
+
const hasSkills = await pathExists(skillsPath);
|
|
100
|
+
|
|
101
|
+
if (hasCommands && hasSkills) {
|
|
102
|
+
checks.push({ name: '.claude/', status: 'ok' });
|
|
103
|
+
} else {
|
|
104
|
+
checks.push({ name: '.claude/', status: 'warn', msg: 'incomplete structure' });
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
checks.push({ name: '.claude/', status: 'missing' });
|
|
108
|
+
hasErrors = true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Display results
|
|
112
|
+
for (const check of checks) {
|
|
113
|
+
if (check.status === 'ok') {
|
|
114
|
+
console.log(chalk.green(` ✓ ${check.name}`));
|
|
115
|
+
} else if (check.status === 'warn') {
|
|
116
|
+
console.log(chalk.yellow(` ⚠ ${check.name}`) + chalk.gray(` (${check.msg})`));
|
|
117
|
+
} else if (check.status === 'missing') {
|
|
118
|
+
console.log(chalk.red(` ✗ ${check.name}`) + chalk.gray(' (missing)'));
|
|
119
|
+
} else if (check.status === 'error') {
|
|
120
|
+
console.log(chalk.red(` ✗ ${check.name}`) + chalk.gray(` (${check.msg})`));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
logger.blank();
|
|
125
|
+
|
|
126
|
+
if (hasErrors) {
|
|
127
|
+
logger.error('Some checks failed. Run "morph-spec init --force" to fix.');
|
|
128
|
+
process.exit(1);
|
|
129
|
+
} else {
|
|
130
|
+
logger.success('All checks passed!');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { logger } from '../utils/logger.js';
|
|
5
|
+
import {
|
|
6
|
+
getContentDir,
|
|
7
|
+
copyDirectory,
|
|
8
|
+
copyFile,
|
|
9
|
+
pathExists,
|
|
10
|
+
readJson,
|
|
11
|
+
writeJson,
|
|
12
|
+
ensureDir,
|
|
13
|
+
writeFile
|
|
14
|
+
} from '../utils/file-copier.js';
|
|
15
|
+
|
|
16
|
+
export async function initCommand(options) {
|
|
17
|
+
const targetPath = options.path || process.cwd();
|
|
18
|
+
const contentDir = getContentDir();
|
|
19
|
+
|
|
20
|
+
logger.header('MORPH-SPEC Framework Installation');
|
|
21
|
+
logger.dim(`Target: ${targetPath}`);
|
|
22
|
+
logger.blank();
|
|
23
|
+
|
|
24
|
+
// Check if already initialized
|
|
25
|
+
const morphPath = join(targetPath, '.morph');
|
|
26
|
+
if (await pathExists(morphPath)) {
|
|
27
|
+
if (!options.force) {
|
|
28
|
+
logger.warn('MORPH already initialized in this directory.');
|
|
29
|
+
logger.dim('Use --force to overwrite existing installation.');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
logger.warn('Overwriting existing MORPH installation...');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const spinner = ora('Installing MORPH-SPEC...').start();
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
// 1. Copy CLAUDE.md
|
|
39
|
+
spinner.text = 'Copying CLAUDE.md...';
|
|
40
|
+
const claudeMdSrc = join(contentDir, 'CLAUDE.md');
|
|
41
|
+
const claudeMdDest = join(targetPath, 'CLAUDE.md');
|
|
42
|
+
|
|
43
|
+
// Backup existing CLAUDE.md if not from MORPH
|
|
44
|
+
if (await pathExists(claudeMdDest)) {
|
|
45
|
+
const existingContent = await import('fs-extra').then(fs => fs.readFile(claudeMdDest, 'utf8'));
|
|
46
|
+
if (!existingContent.includes('MORPH-SPEC')) {
|
|
47
|
+
await copyFile(claudeMdDest, `${claudeMdDest}.backup`);
|
|
48
|
+
logger.dim('Backed up existing CLAUDE.md');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
await copyFile(claudeMdSrc, claudeMdDest);
|
|
52
|
+
|
|
53
|
+
// 2. Copy .morph folder
|
|
54
|
+
spinner.text = 'Copying .morph/...';
|
|
55
|
+
const morphSrc = join(contentDir, '.morph');
|
|
56
|
+
await copyDirectory(morphSrc, morphPath);
|
|
57
|
+
|
|
58
|
+
// 3. Create empty directories with .gitkeep
|
|
59
|
+
for (const dir of ['specs', 'features', 'archive']) {
|
|
60
|
+
const dirPath = join(morphPath, dir);
|
|
61
|
+
await ensureDir(dirPath);
|
|
62
|
+
const gitkeepPath = join(dirPath, '.gitkeep');
|
|
63
|
+
if (!(await pathExists(gitkeepPath))) {
|
|
64
|
+
await writeFile(gitkeepPath, '');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 4. Create config.json from template
|
|
69
|
+
spinner.text = 'Generating config.json...';
|
|
70
|
+
const configPath = join(morphPath, 'config', 'config.json');
|
|
71
|
+
const templatePath = join(morphPath, 'config', 'config.template.json');
|
|
72
|
+
|
|
73
|
+
if (await pathExists(templatePath)) {
|
|
74
|
+
const config = await readJson(templatePath);
|
|
75
|
+
|
|
76
|
+
// Try to detect project name from directory or .sln
|
|
77
|
+
const dirName = targetPath.split(/[\\/]/).pop();
|
|
78
|
+
config.project = config.project || {};
|
|
79
|
+
config.project.name = dirName;
|
|
80
|
+
config.project.description = `Projeto ${dirName} gerenciado pelo MORPH-SPEC Framework`;
|
|
81
|
+
|
|
82
|
+
await writeJson(configPath, config);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 5. Copy .claude folder
|
|
86
|
+
spinner.text = 'Copying .claude/...';
|
|
87
|
+
const claudeSrc = join(contentDir, '.claude');
|
|
88
|
+
const claudeDest = join(targetPath, '.claude');
|
|
89
|
+
|
|
90
|
+
if (await pathExists(claudeSrc)) {
|
|
91
|
+
await copyDirectory(claudeSrc, claudeDest);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
spinner.succeed('MORPH-SPEC installed successfully!');
|
|
95
|
+
|
|
96
|
+
// Show next steps
|
|
97
|
+
logger.blank();
|
|
98
|
+
logger.header('Next Steps');
|
|
99
|
+
|
|
100
|
+
logger.step(1, 'Edit .morph/project.md with your project details');
|
|
101
|
+
logger.step(2, 'Review .morph/config/config.json');
|
|
102
|
+
logger.step(3, 'Open project in VS Code with Claude Code');
|
|
103
|
+
logger.step(4, 'Start your first feature:');
|
|
104
|
+
logger.blank();
|
|
105
|
+
logger.box([
|
|
106
|
+
'/morph-proposal [feature-name]'
|
|
107
|
+
]);
|
|
108
|
+
logger.blank();
|
|
109
|
+
|
|
110
|
+
logger.info('Documentation:');
|
|
111
|
+
logger.dim(`CLAUDE.md → ${join(targetPath, 'CLAUDE.md')}`);
|
|
112
|
+
logger.dim(`Standards → ${join(targetPath, '.morph', 'standards')}`);
|
|
113
|
+
logger.dim(`Templates → ${join(targetPath, '.morph', 'templates')}`);
|
|
114
|
+
logger.blank();
|
|
115
|
+
|
|
116
|
+
} catch (error) {
|
|
117
|
+
spinner.fail('Installation failed');
|
|
118
|
+
logger.error(error.message);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { logger } from '../utils/logger.js';
|
|
4
|
+
import {
|
|
5
|
+
getContentDir,
|
|
6
|
+
copyDirectory,
|
|
7
|
+
pathExists
|
|
8
|
+
} from '../utils/file-copier.js';
|
|
9
|
+
|
|
10
|
+
export async function updateCommand(options) {
|
|
11
|
+
const targetPath = process.cwd();
|
|
12
|
+
const contentDir = getContentDir();
|
|
13
|
+
const morphPath = join(targetPath, '.morph');
|
|
14
|
+
|
|
15
|
+
logger.header('MORPH-SPEC Update');
|
|
16
|
+
|
|
17
|
+
// Check if MORPH is initialized
|
|
18
|
+
if (!(await pathExists(morphPath))) {
|
|
19
|
+
logger.error('MORPH not initialized in this directory.');
|
|
20
|
+
logger.dim('Run "morph-spec init" first.');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const spinner = ora('Updating MORPH-SPEC...').start();
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const updateTemplates = !options.standards || options.templates;
|
|
28
|
+
const updateStandards = !options.templates || options.standards;
|
|
29
|
+
|
|
30
|
+
// Update templates
|
|
31
|
+
if (updateTemplates) {
|
|
32
|
+
spinner.text = 'Updating templates...';
|
|
33
|
+
const templatesSrc = join(contentDir, '.morph', 'templates');
|
|
34
|
+
const templatesDest = join(morphPath, 'templates');
|
|
35
|
+
await copyDirectory(templatesSrc, templatesDest);
|
|
36
|
+
logger.dim('Updated .morph/templates/');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Update standards
|
|
40
|
+
if (updateStandards) {
|
|
41
|
+
spinner.text = 'Updating standards...';
|
|
42
|
+
const standardsSrc = join(contentDir, '.morph', 'standards');
|
|
43
|
+
const standardsDest = join(morphPath, 'standards');
|
|
44
|
+
await copyDirectory(standardsSrc, standardsDest);
|
|
45
|
+
logger.dim('Updated .morph/standards/');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Update agents.json
|
|
49
|
+
spinner.text = 'Updating agents configuration...';
|
|
50
|
+
const agentsSrc = join(contentDir, '.morph', 'config', 'agents.json');
|
|
51
|
+
const agentsDest = join(morphPath, 'config', 'agents.json');
|
|
52
|
+
if (await pathExists(agentsSrc)) {
|
|
53
|
+
await copyDirectory(agentsSrc, agentsDest);
|
|
54
|
+
logger.dim('Updated .morph/config/agents.json');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Update .claude commands and skills
|
|
58
|
+
spinner.text = 'Updating Claude commands and skills...';
|
|
59
|
+
const claudeSrc = join(contentDir, '.claude');
|
|
60
|
+
const claudeDest = join(targetPath, '.claude');
|
|
61
|
+
if (await pathExists(claudeSrc)) {
|
|
62
|
+
await copyDirectory(claudeSrc, claudeDest);
|
|
63
|
+
logger.dim('Updated .claude/commands/ and .claude/skills/');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Update CLAUDE.md
|
|
67
|
+
spinner.text = 'Updating CLAUDE.md...';
|
|
68
|
+
const claudeMdSrc = join(contentDir, 'CLAUDE.md');
|
|
69
|
+
const claudeMdDest = join(targetPath, 'CLAUDE.md');
|
|
70
|
+
await copyDirectory(claudeMdSrc, claudeMdDest);
|
|
71
|
+
logger.dim('Updated CLAUDE.md');
|
|
72
|
+
|
|
73
|
+
spinner.succeed('MORPH-SPEC updated successfully!');
|
|
74
|
+
logger.blank();
|
|
75
|
+
logger.info('Your config.json was preserved.');
|
|
76
|
+
logger.dim('Review the updated files for any new features.');
|
|
77
|
+
logger.blank();
|
|
78
|
+
|
|
79
|
+
} catch (error) {
|
|
80
|
+
spinner.fail('Update failed');
|
|
81
|
+
logger.error(error.message);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
}
|