@polymorphism-tech/morph-spec 10.2.0 → 10.5.3
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 +2 -2
- package/bin/morph-spec.js +9 -0
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/MORPH.md +1 -1
- package/framework/agents.json +1 -1
- package/framework/docs/CLI.md +22 -2
- package/framework/evals/review-checklist.md +5 -5
- package/framework/hooks/claude-code/core/heavy-gate.js +21 -2
- package/framework/hooks/shared/background-spawn.js +20 -0
- package/framework/hooks/shared/graph-refresh.js +2 -1
- package/framework/hooks/shared/heavy-config.js +21 -0
- package/framework/hooks/shared/host-services.js +9 -6
- package/framework/hooks/shared/next-step.js +11 -7
- package/framework/rules/plan-grammar.md +4 -1
- package/framework/schemas/config.schema.json +39 -0
- package/framework/schemas/feature.schema.json +14 -0
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +7 -4
- package/framework/skills/core/morph-apply/references/machine.md +3 -2
- package/framework/skills/core/morph-apply/references/review.md +5 -3
- package/framework/skills/core/morph-plan/SKILL.md +4 -3
- package/framework/skills/core/morph-plan/references/critique.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +1 -1
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/docs/plan.md +2 -1
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +1 -1
- package/src/commands/project/heavy.js +13 -0
- package/src/commands/validation/verify.js +51 -5
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/heavy/gate.js +34 -2
- package/src/lib/heavy/reclaim.js +107 -0
- package/src/lib/heavy/watchdog.js +46 -0
- package/src/lib/next-step.js +1 -0
- package/src/lib/plan/checks.js +1 -1
- package/src/lib/procs/prune.js +23 -1
- package/src/lib/tasks/test-nodes.js +5 -0
- package/src/lib/validators/verify-runner.js +34 -4
- package/src/lib/verify/affected.js +141 -0
- package/src/lib/verify/detached.js +156 -0
- package/src/utils/hooks-installer.js +8 -0
- package/framework/templates/.idea/morph-templates.xml +0 -92
- package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
|
@@ -14,8 +14,9 @@ Para manter a máquina utilizável enquanto você compila e testa. Aplique quand
|
|
|
14
14
|
| Sintoma | Comando | Como ler e o que fazer |
|
|
15
15
|
|---|---|---|
|
|
16
16
|
| Máquina ou editor lento, pouca RAM | `morph-spec doctor --procs` (só lê) | Cada linha diz órfão ou protegido, com a razão. Sem órfão, a pressão vem de fora (vmmem do WSL, IDE): diga isso e passe a sugestão de `.wslconfig` que o relatório traz. Com órfão, mostre a lista e ofereça `--prune`. |
|
|
17
|
-
| Comando voltou `⏳ … não rodou` ou exit 3 | `morph-spec heavy status` | Mostra quem segura cada vaga, há quanto tempo, e a RAM livre. A vaga solta sozinha (processo morto ou timeout). Vaga presa por sessão viva: diga ao usuário quem a segura. Se a causa for RAM e nenhuma vaga estiver presa, rode `doctor --procs
|
|
18
|
-
|
|
|
17
|
+
| Comando voltou `⏳ … não rodou` ou exit 3 | `morph-spec heavy status` | Mostra quem segura cada vaga, há quanto tempo, e a RAM livre. A vaga solta sozinha (processo morto ou timeout). Vaga presa por sessão viva: diga ao usuário quem a segura. Se a causa for RAM e nenhuma vaga estiver presa, rode `doctor --procs`: as stacks paradas a fila já derrubou sozinha. |
|
|
18
|
+
| O Claude Code encerrou um comando de fundo por pouca memória | o mesmo `morph-spec verify … --detach` | O `--detach` não morre com a sessão: repetir retoma a rodada. Outro comando pesado não se repete por conta própria: avise o usuário. |
|
|
19
|
+
| Várias sessões abertas, testando ao mesmo tempo | nada: a fila resolve | São 2 vagas e uma suíte de `verify` por vez. O `verify --detach` espera a vez (até `heavy.detachedWaitMinutes`), então espera longa é fila, não problema: não cancele nem peça ao usuário. Abaixo do piso, a fila derruba sozinha as stacks ociosas; `heavy reclaim` faz o mesmo à mão. |
|
|
19
20
|
| Worktree sobrando ou fora do contêiner | `morph-spec doctor --worktrees` | Lista cada árvore com a razão; remova só por `worktree remove`. |
|
|
20
21
|
| A worktree precisa de arquivo fora do git (`.env`, segredos) | `.worktreeinclude` na raiz do repo | Um padrão por linha, sintaxe do gitignore. Copia só o que casa **e** é ignorado; nunca `node_modules`, build ou `.git`. O `setup` e o `provision` aplicam. |
|
|
21
22
|
| Editor pesado por causa das worktrees | `worktree.root` em `.morph/config/config.json` | Põe as worktrees fora da árvore aberta (caminho absoluto fora do projeto). `init` e `update` já gravam as exclusões no `.vscode/settings.json`. |
|
|
@@ -10,9 +10,9 @@ Por que fresco: o que você sabe da implementação é o ponto cego que eles exi
|
|
|
10
10
|
|
|
11
11
|
## 2. Prova da suíte — uma rodada
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
A rodada afetada do §3 da `SKILL.md`, verde, é o que abre o dispatch. A completa vem depois dos relatórios (§6), uma vez só: assim uma correção do Gate 3 não paga duas rodadas cheias, e o que a completa encontrar ainda passa pela confirmação.
|
|
14
14
|
|
|
15
|
-
`flaky` no resultado do verify → a lista vai ao verificador.
|
|
15
|
+
`flaky` no resultado do verify → a lista vai ao verificador. Com `e2eChecks`, `morph-spec e2e status {feature} --json` → `baseUrl` vai ao verificador.
|
|
16
16
|
|
|
17
17
|
## 3. Dispatch — os três na mesma resposta
|
|
18
18
|
|
|
@@ -43,12 +43,14 @@ Não rode build nem teste enquanto um agente está `running`. Com todos de volta
|
|
|
43
43
|
|
|
44
44
|
## 5. Bloqueadores
|
|
45
45
|
|
|
46
|
-
O avaliador também erra: **reproduza cada bloqueador antes de editar**. Não se reproduz → é nota sua no `report.md`, e o próximo dispatch de confirmação recebe a contra-evidência. Confirmado → corrija, commite, `verify {feature}
|
|
46
|
+
O avaliador também erra: **reproduza cada bloqueador antes de editar**. Não se reproduz → é nota sua no `report.md`, e o próximo dispatch de confirmação recebe a contra-evidência. Confirmado → corrija, commite, `verify {feature} --affected`, foto `--before-eval` nova.
|
|
47
47
|
|
|
48
48
|
Confirmação = **dispatch novo** só dos papéis em `gate3.redispatch`, com o `sha` novo e o escopo da rodada: quem abriu bloqueadores confirma só esses (verificador: "confirme só C2, C5"); os outros julgam só o delta `{sha anterior}..{sha novo}`. Papel com SHIP limpo não volta se o delta ficar nos arquivos citados pelos bloqueadores ou em teste; a segurança, se o delta não tocar caminho sensível. Ninguém reavalia a feature inteira. O relatório é append-only e vale o último `**Verdict:**`; o `approve` recusa quando o código andou depois de um `**Reviewed-SHA:**`, salvo esse caso, com quem abriu bloqueador confirmando o `HEAD`.
|
|
49
49
|
|
|
50
50
|
## 6. Gate 3
|
|
51
51
|
|
|
52
|
+
**A rodada completa:** com os relatórios sem bloqueador, `morph-spec verify {feature} --ci --json --detach` em `run_in_background` (sem CI, roda local e diz por quê; repetir o comando retoma a rodada). É a única. Sem `--filter`, sempre, e sem `--affected`: verde filtrado é verde sobre código não testado, e é ela que o gate lê. `reason: "stamp-reused"` = nada andou desde o último verde: vale. Saída `CI rodando` → espere a notificação. `fail` → corrija (CI: `gh run view <id> --log-failed`) e siga o §5, porque a correção é código que ninguém revisou. Nó `e2e` em `skip` → o `gate-check` decide se dispensa (`classifyE2eSkip`, em `risk-detector.js`: docker indisponível pausa sozinho).
|
|
53
|
+
|
|
52
54
|
`morph-spec gate-check {feature} review [--json]` lê os relatórios, a cobertura dos `Cn` e os demais sinais. Forçar passagem apesar da pausa: `morph-spec gate-decision {feature} review --pause false --reason "…"`.
|
|
53
55
|
|
|
54
56
|
Pausa com `resolver: "session"` não é pergunta: faça o que a `reason` diz e rode o `gate-check` de novo. Verify vermelho ou velho → corrija e `verify {feature}`; e2e pulado a pedido → `verify {feature}` sem `--skip-e2e`; bloqueador ou `Cn` não provado → §5; relatório ausente ou sem seção → dispatch. Vira `human` sozinho depois de `FIX_ATTEMPTS` verify vermelhos seguidos ou rodadas de review: a disputa é do usuário.
|
|
@@ -4,7 +4,7 @@ description: Leva um pedido ou o link de uma issue ao plano aprovado — roteia
|
|
|
4
4
|
version: core
|
|
5
5
|
when_to_use: Use quando uma feature, bug ou chore começar ("/morph-plan {feature}", "/morph-plan <link da issue>", "vamos planejar X"), ou ao retomar uma feature cujo gate plan ainda não foi aprovado
|
|
6
6
|
user-invocable: true
|
|
7
|
-
cliVersion: "10.
|
|
7
|
+
cliVersion: "10.5.3"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-plan
|
|
@@ -50,7 +50,7 @@ Por conceito central que o plano pretende criar (entidade, DTO, componente, hook
|
|
|
50
50
|
|
|
51
51
|
O `create` escreveu o esqueleto; a gramática completa chega pela rule `plan-grammar` quando você abre o `plan.md`; o resto em `morph-spec plan-check --help`. O que importa:
|
|
52
52
|
|
|
53
|
-
- **`Resultado buscado:`** uma linha em `## Problema`: o estado depois, o que muda, para quem e como se percebe. É contra ela que a `morph-apply` decide se uma descoberta invalida o plano.
|
|
53
|
+
- **`Resultado buscado:`** uma linha em `## Problema`: o estado depois, o que muda, para quem e como se percebe; o objetivo por trás do pedido, nunca o pedido reescrito. Medível, leva um número específico e difícil: alvo pequeno se atinge ajustando, alvo duro obriga a repensar. É contra ela que a `morph-apply` decide se uma descoberta invalida o plano e que o revisor lê as notas do Gate 3.
|
|
54
54
|
- **`## Cenários` é a definição de pronto.** Cada `Cn` é um comportamento observável que o verificador do Gate 3 vai **executar** contra a app — escreva-o como um fluxo de usuário ou uma regra que dá para disparar. `prova:` `teste` (com `risco:`) | `e2e` | `eval` | `manual` | `leitura`. Sem risco, `leitura`.
|
|
55
55
|
- **`degrau:`** quando o `Cn` não se desfaz com revert: `migrar` (toda migration, aditiva ou não: drop, rename, backfill, coluna nova) ou `irreversível` (apaga dado que alguém lê, quebra contrato externo). Sem a linha, `refazer`. Esses dois degraus pausam o gate do plano para o usuário, e o Gate 3 pausa em migration sem degrau, em arquivo de contrato (`openapi`, `.proto`) sem `irreversível`, e no bloqueador `irreversivel` do revisor.
|
|
56
56
|
- **`## Padrões assumidos`**: todo valor de baixo risco que você decidiu sem perguntar, numerado `1.`…`N.`, com o valor. É o que tira a pergunta trivial da entrevista: o usuário veta por número. Auth, credencial, permissão, pagamento e apagar dado nunca entram: são pergunta.
|
|
@@ -60,7 +60,8 @@ O `create` escreveu o esqueleto; a gramática completa chega pela rule `plan-gra
|
|
|
60
60
|
- **`## Arquitetura`**: onde entra, o que conversa com o quê, edge cases técnicos. Gera imagem ou vídeo por IA → cite aqui o standard (`ai-agents-modalities-image-gen`, `ai-agents-media-video`, `ai-agents-media-pipeline`): é a citação que arma as provas de mídia do Gate 3, e o `plan-check` recusa o plano que fala em mídia gerada sem ela (falso positivo: a linha `Mídia gerada: não`).
|
|
61
61
|
|
|
62
62
|
**Contrato de implementação**: o que quem implementa precisa e o código não diz.
|
|
63
|
-
- **
|
|
63
|
+
- **Profundidade** (`feature`, antes de fechar os cenários; plano pequeno, §6, dispensa): uma linha para cada, sem aumentar o escopo: o que um profissional da área faria aqui que o pedido não pede; a matéria-prima mais crua de que ele partiria (export de produção, log, trace, nunca o resumo); o método e a ferramenta dele; o que, além de você e do verificador, mostra que está certo. Troque o substantivo por um verbo ("painel" → "fazer o dono decidir em 30 segundos") e pergunte que campo faz esse verbo melhor: importe o método dele, nunca a voz. O que sobrar vira `Cn`, instrumento ou `## Fora de escopo`.
|
|
64
|
+
- **Instrumentos:** o que falta para provar sozinho, perguntado antes do gate: onde o problema aparece, o contexto para entendê-lo e a bancada onde reproduzir e provar (ferramenta, acesso, dado, ambiente). Prova que só existe depois do deploy se diz aqui, antes do gate; "nenhuma lacuna" vale.
|
|
64
65
|
- **Verificação de runtime:** o stack sobe por `morph-spec e2e up {feature}`: app sem dependência (dados no `localStorage`, só APIs externas) sobe só por `e2e.hostServices` (build + start + health, sem compose); com dependência (banco, fila) o compose do projeto sobe ela, e criá-lo entra no plano; os fluxos que o verificador navega, nos verbos dos cenários; base URL `process.env.PLAYWRIGHT_BASE_URL ?? \`http://localhost:${process.env.PORT ?? 3000}\``, compose com `"${PORT:-3000}:3000"` e sem `container_name` (standard `infrastructure/docker/local-compose-isolation`). App de build caro no Docker também sobe como processo do host por `e2e.hostServices`, ao lado do compose das dependências. Login: `e2e.auth` (`mode: seed|storageState|bypass|none`, `seedCommand`, credenciais num `.env.e2e` gitignored) — **`mode` é obrigatório sempre que houver `seedCommand`**, sem ele o seed fica inerte. Estado limpo entre fluxos: o `e2e reset` recria os volumes de dados do stack da feature (`morph-{f}_*`); volume com `name:` fixo é compartilhado com o stack de desenvolvimento, e aí o reset recusa até existir `e2e.resetCommand`. Biblioteca pura: registre a dispensa e o porquê.
|
|
65
66
|
- **Baseline é um COMANDO, nunca um número** (`npm test`, `dotnet test`): número cravado está errado na primeira mudança. `expectedCount` (só `node-test`/`evals`) sai de `morph-spec verify {feature} --record-baseline`, nunca digitado. Sem `project.stack`/`project.tests` no `config.json`, a `morph-apply` os declara a partir destes comandos.
|
|
66
67
|
- **Pré-requisito é um PREDICADO, nunca um SHA literal:** "`git merge-base --is-ancestor origin/main HEAD` sai 0".
|
|
@@ -15,7 +15,7 @@ Nunca a conversa, as respostas da entrevista nem o seu resumo do plano ou do ped
|
|
|
15
15
|
> `premissa` (o plano afirma algo do código que o código desmente: arquivo, símbolo, contagem, flag);
|
|
16
16
|
> `contradicao` (duas partes do plano se negam);
|
|
17
17
|
> `decisao` (o plano escolhe um valor ou um caminho sem dizer, e o usuário pode querer outro);
|
|
18
|
-
> `cenario` (um `Cn` que nenhuma asserção ou passo no navegador consegue provar);
|
|
18
|
+
> `cenario` (um `Cn` que nenhuma asserção ou passo no navegador consegue provar, ou um `e2e` que confere o controle na tela em vez de disparar a ação até o efeito);
|
|
19
19
|
> `desvio` (o plano não resolve o pedido, sem ADR ou `## Fora de escopo` que diga por quê).
|
|
20
20
|
> Sem `description`, ou com um link que não abre, pule `desvio` e diga isso numa linha depois da tabela.
|
|
21
21
|
> Estilo, nome e "eu faria diferente" não entram. Sem achado, escreva "Nenhum achado." no lugar da tabela, mantendo o título e o `**Plan-Hash:**`.
|
|
@@ -4,7 +4,7 @@ description: Conduz a fase de UI/UX de uma feature com plano aprovado — render
|
|
|
4
4
|
version: core
|
|
5
5
|
when_to_use: Use quando o ramo de design for tomado (morph-spec advance {feature} --design), antes de /morph-apply
|
|
6
6
|
user-invocable: true
|
|
7
|
-
cliVersion: "10.
|
|
7
|
+
cliVersion: "10.5.3"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-uiux
|
|
@@ -1,262 +1,262 @@
|
|
|
1
|
-
# IDE Snippets for MORPH Templates
|
|
2
|
-
|
|
3
|
-
> Boost your productivity with IDE snippets for Handlebars template syntax
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 📦 Installation
|
|
8
|
-
|
|
9
|
-
### VS Code
|
|
10
|
-
|
|
11
|
-
**Option 1: Project Snippets (Recommended)**
|
|
12
|
-
|
|
13
|
-
Snippets are already configured in `framework/templates/.vscode/morph-templates.code-snippets`.
|
|
14
|
-
|
|
15
|
-
To use them:
|
|
16
|
-
1. Open your project in VS Code
|
|
17
|
-
2. Snippets auto-activate when editing template files
|
|
18
|
-
3. Type a prefix (e.g., `morph-pascal`) and press `Tab`
|
|
19
|
-
|
|
20
|
-
**Option 2: Global Snippets**
|
|
21
|
-
|
|
22
|
-
To use snippets across all projects:
|
|
23
|
-
1. Open VS Code
|
|
24
|
-
2. Go to `File > Preferences > Configure User Snippets`
|
|
25
|
-
3. Select `New Global Snippets file...`
|
|
26
|
-
4. Name it `morph-templates.code-snippets`
|
|
27
|
-
5. Copy content from `framework/templates/.vscode/morph-templates.code-snippets`
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
### JetBrains IDEs (Rider, WebStorm, IntelliJ IDEA)
|
|
32
|
-
|
|
33
|
-
**Installation:**
|
|
34
|
-
|
|
35
|
-
1. Open your IDE
|
|
36
|
-
2. Go to `File > Settings > Editor > Live Templates`
|
|
37
|
-
3. Click the `⚙️` gear icon → `Import Settings...`
|
|
38
|
-
4. Select `framework/templates/.idea/morph-templates.xml`
|
|
39
|
-
5. Click `OK` to import
|
|
40
|
-
|
|
41
|
-
**Activation:**
|
|
42
|
-
|
|
43
|
-
Snippets will be available in the "MORPH Templates" group. Type the prefix and press `Tab`.
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## 🎯 Available Snippets
|
|
48
|
-
|
|
49
|
-
### Handlebars Helpers (Case Transformations)
|
|
50
|
-
|
|
51
|
-
| Prefix | Output | Example Result |
|
|
52
|
-
|--------|--------|----------------|
|
|
53
|
-
| `morph-pascal` | `{{pascalCase FEATURE_NAME}}` | `UserAuthentication` |
|
|
54
|
-
| `morph-camel` | `{{camelCase FEATURE_NAME}}` | `userAuthentication` |
|
|
55
|
-
| `morph-snake` | `{{snakeCase FEATURE_NAME}}` | `user_authentication` |
|
|
56
|
-
| `morph-title` | `{{titleCase FEATURE_NAME}}` | `User Authentication` |
|
|
57
|
-
| `morph-kebab` | `{{kebabCase FEATURE_NAME}}` | `user-authentication` |
|
|
58
|
-
| `morph-upper-snake` | `{{upperSnakeCase FEATURE_NAME}}` | `USER_AUTHENTICATION` |
|
|
59
|
-
|
|
60
|
-
### Handlebars Control Flow
|
|
61
|
-
|
|
62
|
-
| Prefix | Description | Example |
|
|
63
|
-
|--------|-------------|---------|
|
|
64
|
-
| `morph-if` | If block | `{{#if condition}}...{{/if}}` |
|
|
65
|
-
| `morph-if-else` | If-else block | `{{#if condition}}...{{else}}...{{/if}}` |
|
|
66
|
-
| `morph-each` | Loop over array | `{{#each items}}{{this.name}}{{/each}}` |
|
|
67
|
-
| `morph-unless` | Unless block | `{{#unless condition}}...{{/unless}}` |
|
|
68
|
-
|
|
69
|
-
### .NET Templates
|
|
70
|
-
|
|
71
|
-
| Prefix | Template | File Extension |
|
|
72
|
-
|--------|----------|----------------|
|
|
73
|
-
| `morph-service` | Service class (business logic) | `.cs` |
|
|
74
|
-
| `morph-repository` | Repository class (data access) | `.cs` |
|
|
75
|
-
| `morph-migration` | EF Core migration | `.cs` |
|
|
76
|
-
| `morph-job` | Hangfire background job | `.cs` |
|
|
77
|
-
|
|
78
|
-
### TypeScript Templates
|
|
79
|
-
|
|
80
|
-
| Prefix | Template | File Extension |
|
|
81
|
-
|--------|----------|----------------|
|
|
82
|
-
| `morph-ts-contract` | TypeScript interfaces (DTOs) | `.ts` |
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
## 🚀 Usage Examples
|
|
87
|
-
|
|
88
|
-
### Example 1: Creating a Service Template
|
|
89
|
-
|
|
90
|
-
**Steps:**
|
|
91
|
-
1. Create new file: `UserService.cs`
|
|
92
|
-
2. Type `morph-service` → Press `Tab`
|
|
93
|
-
3. Result:
|
|
94
|
-
```csharp
|
|
95
|
-
namespace {{NAMESPACE}}.Application.Features.{{pascalCase FEATURE_NAME}}.Services;
|
|
96
|
-
|
|
97
|
-
public class {{pascalCase FEATURE_NAME}}Service(
|
|
98
|
-
I{{pascalCase FEATURE_NAME}}Repository repository,
|
|
99
|
-
ILogger<{{pascalCase FEATURE_NAME}}Service> logger) : I{{pascalCase FEATURE_NAME}}Service
|
|
100
|
-
{
|
|
101
|
-
public async Task<{{pascalCase FEATURE_NAME}}Dto?> GetByIdAsync(int id, CancellationToken cancellationToken = default)
|
|
102
|
-
{
|
|
103
|
-
// Cursor here
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Example 2: Using Case Helpers
|
|
109
|
-
|
|
110
|
-
**Scenario:** Convert `user-authentication` to different cases
|
|
111
|
-
|
|
112
|
-
```handlebars
|
|
113
|
-
// Type: morph-pascal → Tab
|
|
114
|
-
{{pascalCase FEATURE_NAME}} // Output: UserAuthentication
|
|
115
|
-
|
|
116
|
-
// Type: morph-camel → Tab
|
|
117
|
-
{{camelCase FEATURE_NAME}} // Output: userAuthentication
|
|
118
|
-
|
|
119
|
-
// Type: morph-snake → Tab
|
|
120
|
-
{{snakeCase FEATURE_NAME}} // Output: user_authentication
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Example 3: Conditional Rendering
|
|
124
|
-
|
|
125
|
-
**Scenario:** Show different content based on stack
|
|
126
|
-
|
|
127
|
-
```handlebars
|
|
128
|
-
// Type: morph-if-else → Tab
|
|
129
|
-
{{#if (eq STACK "Nextjs")}}
|
|
130
|
-
// Next.js-specific code
|
|
131
|
-
{{else}}
|
|
132
|
-
// Generic code
|
|
133
|
-
{{/if}}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### Example 4: Looping Over Items
|
|
137
|
-
|
|
138
|
-
**Scenario:** Generate multiple DTOs
|
|
139
|
-
|
|
140
|
-
```handlebars
|
|
141
|
-
// Type: morph-each → Tab
|
|
142
|
-
{{#each entities}}
|
|
143
|
-
public class {{pascalCase this.name}}Dto
|
|
144
|
-
{
|
|
145
|
-
// Properties
|
|
146
|
-
}
|
|
147
|
-
{{/each}}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## 📝 Customizing Snippets
|
|
153
|
-
|
|
154
|
-
### VS Code
|
|
155
|
-
|
|
156
|
-
Edit `framework/templates/.vscode/morph-templates.code-snippets`:
|
|
157
|
-
|
|
158
|
-
```json
|
|
159
|
-
{
|
|
160
|
-
"MORPH Template - Custom": {
|
|
161
|
-
"prefix": "morph-custom",
|
|
162
|
-
"body": [
|
|
163
|
-
"// Your custom template",
|
|
164
|
-
"$1"
|
|
165
|
-
],
|
|
166
|
-
"description": "Your description"
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### JetBrains IDEs
|
|
172
|
-
|
|
173
|
-
1. Go to `File > Settings > Editor > Live Templates`
|
|
174
|
-
2. Select "MORPH Templates" group
|
|
175
|
-
3. Click `+` → `Live Template`
|
|
176
|
-
4. Set abbreviation (prefix), description, and template text
|
|
177
|
-
|
|
178
|
-
---
|
|
179
|
-
|
|
180
|
-
## 🎨 Snippet Scopes
|
|
181
|
-
|
|
182
|
-
| Snippet | VS Code Scope | JetBrains Scope |
|
|
183
|
-
|---------|---------------|-----------------|
|
|
184
|
-
| Handlebars helpers | All files | All files |
|
|
185
|
-
| .NET templates | `.cs`, `.razor` | C#, HTML |
|
|
186
|
-
| TypeScript templates | `.ts`, `.tsx` | TypeScript |
|
|
187
|
-
|
|
188
|
-
---
|
|
189
|
-
|
|
190
|
-
## 💡 Best Practices
|
|
191
|
-
|
|
192
|
-
**1. Use Helpers Over Pre-computed Variables**
|
|
193
|
-
|
|
194
|
-
```handlebars
|
|
195
|
-
✅ GOOD: {{pascalCase FEATURE_NAME}}Service
|
|
196
|
-
❌ BAD: {{FEATURE_NAME_PASCAL}}Service // Deprecated
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
**2. Chain Helpers for Complex Transformations**
|
|
200
|
-
|
|
201
|
-
```handlebars
|
|
202
|
-
// Pluralize a PascalCase name
|
|
203
|
-
{{pascalCase (pluralize FEATURE_NAME)}} // Users, Orders, etc.
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
**3. Use Conditional Blocks for Stack-Specific Code**
|
|
207
|
-
|
|
208
|
-
```handlebars
|
|
209
|
-
{{#if (eq STACK "Nextjs")}}
|
|
210
|
-
import { useRouter } from 'next/navigation';
|
|
211
|
-
{{/if}}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
**4. Extract Common Patterns to Snippets**
|
|
215
|
-
|
|
216
|
-
If you find yourself typing the same template code repeatedly, create a custom snippet!
|
|
217
|
-
|
|
218
|
-
---
|
|
219
|
-
|
|
220
|
-
## 🔧 Troubleshooting
|
|
221
|
-
|
|
222
|
-
### Snippets Not Showing Up (VS Code)
|
|
223
|
-
|
|
224
|
-
**Solution:**
|
|
225
|
-
1. Check file extension matches snippet scope (e.g., `.cs` for C# snippets)
|
|
226
|
-
2. Reload VS Code: `Ctrl+Shift+P` → `Developer: Reload Window`
|
|
227
|
-
3. Verify snippet file location: `framework/templates/.vscode/morph-templates.code-snippets`
|
|
228
|
-
|
|
229
|
-
### Snippets Not Showing Up (JetBrains)
|
|
230
|
-
|
|
231
|
-
**Solution:**
|
|
232
|
-
1. Verify import: `File > Settings > Editor > Live Templates` → Check "MORPH Templates" group exists
|
|
233
|
-
2. Check snippet scope: Click snippet → Verify "Applicable in" contexts
|
|
234
|
-
3. Restart IDE
|
|
235
|
-
|
|
236
|
-
### Tab Not Expanding Snippet
|
|
237
|
-
|
|
238
|
-
**Solution:**
|
|
239
|
-
- **VS Code:** Ensure `editor.tabCompletion` is enabled in settings
|
|
240
|
-
- **JetBrains:** Ensure `Tab` is set as expansion key in `Settings > Editor > Live Templates`
|
|
241
|
-
|
|
242
|
-
---
|
|
243
|
-
|
|
244
|
-
## 📚 Related Documentation
|
|
245
|
-
|
|
246
|
-
- [Template System README](framework/templates/README.md) - Complete template system documentation
|
|
247
|
-
- [Handlebars Helpers](framework/templates/README.md#handlebars-helpers) - Full list of available helpers
|
|
248
|
-
- [CLI Template Commands](CLAUDE.md#templates) - Command-line template management
|
|
249
|
-
|
|
250
|
-
---
|
|
251
|
-
|
|
252
|
-
## 🎁 Contributing Custom Snippets
|
|
253
|
-
|
|
254
|
-
Have a useful snippet? Share it!
|
|
255
|
-
|
|
256
|
-
1. Add snippet to appropriate file (`.vscode/` or `.idea/`)
|
|
257
|
-
2. Document it in this README (add row to tables)
|
|
258
|
-
3. Submit PR with description of use case
|
|
259
|
-
|
|
260
|
-
---
|
|
261
|
-
|
|
262
|
-
*MORPH-SPEC Template Snippets by Polymorphism Tech*
|
|
1
|
+
# IDE Snippets for MORPH Templates
|
|
2
|
+
|
|
3
|
+
> Boost your productivity with IDE snippets for Handlebars template syntax
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 📦 Installation
|
|
8
|
+
|
|
9
|
+
### VS Code
|
|
10
|
+
|
|
11
|
+
**Option 1: Project Snippets (Recommended)**
|
|
12
|
+
|
|
13
|
+
Snippets are already configured in `framework/templates/.vscode/morph-templates.code-snippets`.
|
|
14
|
+
|
|
15
|
+
To use them:
|
|
16
|
+
1. Open your project in VS Code
|
|
17
|
+
2. Snippets auto-activate when editing template files
|
|
18
|
+
3. Type a prefix (e.g., `morph-pascal`) and press `Tab`
|
|
19
|
+
|
|
20
|
+
**Option 2: Global Snippets**
|
|
21
|
+
|
|
22
|
+
To use snippets across all projects:
|
|
23
|
+
1. Open VS Code
|
|
24
|
+
2. Go to `File > Preferences > Configure User Snippets`
|
|
25
|
+
3. Select `New Global Snippets file...`
|
|
26
|
+
4. Name it `morph-templates.code-snippets`
|
|
27
|
+
5. Copy content from `framework/templates/.vscode/morph-templates.code-snippets`
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
### JetBrains IDEs (Rider, WebStorm, IntelliJ IDEA)
|
|
32
|
+
|
|
33
|
+
**Installation:**
|
|
34
|
+
|
|
35
|
+
1. Open your IDE
|
|
36
|
+
2. Go to `File > Settings > Editor > Live Templates`
|
|
37
|
+
3. Click the `⚙️` gear icon → `Import Settings...`
|
|
38
|
+
4. Select `framework/templates/.idea/morph-templates.xml`
|
|
39
|
+
5. Click `OK` to import
|
|
40
|
+
|
|
41
|
+
**Activation:**
|
|
42
|
+
|
|
43
|
+
Snippets will be available in the "MORPH Templates" group. Type the prefix and press `Tab`.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 🎯 Available Snippets
|
|
48
|
+
|
|
49
|
+
### Handlebars Helpers (Case Transformations)
|
|
50
|
+
|
|
51
|
+
| Prefix | Output | Example Result |
|
|
52
|
+
|--------|--------|----------------|
|
|
53
|
+
| `morph-pascal` | `{{pascalCase FEATURE_NAME}}` | `UserAuthentication` |
|
|
54
|
+
| `morph-camel` | `{{camelCase FEATURE_NAME}}` | `userAuthentication` |
|
|
55
|
+
| `morph-snake` | `{{snakeCase FEATURE_NAME}}` | `user_authentication` |
|
|
56
|
+
| `morph-title` | `{{titleCase FEATURE_NAME}}` | `User Authentication` |
|
|
57
|
+
| `morph-kebab` | `{{kebabCase FEATURE_NAME}}` | `user-authentication` |
|
|
58
|
+
| `morph-upper-snake` | `{{upperSnakeCase FEATURE_NAME}}` | `USER_AUTHENTICATION` |
|
|
59
|
+
|
|
60
|
+
### Handlebars Control Flow
|
|
61
|
+
|
|
62
|
+
| Prefix | Description | Example |
|
|
63
|
+
|--------|-------------|---------|
|
|
64
|
+
| `morph-if` | If block | `{{#if condition}}...{{/if}}` |
|
|
65
|
+
| `morph-if-else` | If-else block | `{{#if condition}}...{{else}}...{{/if}}` |
|
|
66
|
+
| `morph-each` | Loop over array | `{{#each items}}{{this.name}}{{/each}}` |
|
|
67
|
+
| `morph-unless` | Unless block | `{{#unless condition}}...{{/unless}}` |
|
|
68
|
+
|
|
69
|
+
### .NET Templates
|
|
70
|
+
|
|
71
|
+
| Prefix | Template | File Extension |
|
|
72
|
+
|--------|----------|----------------|
|
|
73
|
+
| `morph-service` | Service class (business logic) | `.cs` |
|
|
74
|
+
| `morph-repository` | Repository class (data access) | `.cs` |
|
|
75
|
+
| `morph-migration` | EF Core migration | `.cs` |
|
|
76
|
+
| `morph-job` | Hangfire background job | `.cs` |
|
|
77
|
+
|
|
78
|
+
### TypeScript Templates
|
|
79
|
+
|
|
80
|
+
| Prefix | Template | File Extension |
|
|
81
|
+
|--------|----------|----------------|
|
|
82
|
+
| `morph-ts-contract` | TypeScript interfaces (DTOs) | `.ts` |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 🚀 Usage Examples
|
|
87
|
+
|
|
88
|
+
### Example 1: Creating a Service Template
|
|
89
|
+
|
|
90
|
+
**Steps:**
|
|
91
|
+
1. Create new file: `UserService.cs`
|
|
92
|
+
2. Type `morph-service` → Press `Tab`
|
|
93
|
+
3. Result:
|
|
94
|
+
```csharp
|
|
95
|
+
namespace {{NAMESPACE}}.Application.Features.{{pascalCase FEATURE_NAME}}.Services;
|
|
96
|
+
|
|
97
|
+
public class {{pascalCase FEATURE_NAME}}Service(
|
|
98
|
+
I{{pascalCase FEATURE_NAME}}Repository repository,
|
|
99
|
+
ILogger<{{pascalCase FEATURE_NAME}}Service> logger) : I{{pascalCase FEATURE_NAME}}Service
|
|
100
|
+
{
|
|
101
|
+
public async Task<{{pascalCase FEATURE_NAME}}Dto?> GetByIdAsync(int id, CancellationToken cancellationToken = default)
|
|
102
|
+
{
|
|
103
|
+
// Cursor here
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Example 2: Using Case Helpers
|
|
109
|
+
|
|
110
|
+
**Scenario:** Convert `user-authentication` to different cases
|
|
111
|
+
|
|
112
|
+
```handlebars
|
|
113
|
+
// Type: morph-pascal → Tab
|
|
114
|
+
{{pascalCase FEATURE_NAME}} // Output: UserAuthentication
|
|
115
|
+
|
|
116
|
+
// Type: morph-camel → Tab
|
|
117
|
+
{{camelCase FEATURE_NAME}} // Output: userAuthentication
|
|
118
|
+
|
|
119
|
+
// Type: morph-snake → Tab
|
|
120
|
+
{{snakeCase FEATURE_NAME}} // Output: user_authentication
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Example 3: Conditional Rendering
|
|
124
|
+
|
|
125
|
+
**Scenario:** Show different content based on stack
|
|
126
|
+
|
|
127
|
+
```handlebars
|
|
128
|
+
// Type: morph-if-else → Tab
|
|
129
|
+
{{#if (eq STACK "Nextjs")}}
|
|
130
|
+
// Next.js-specific code
|
|
131
|
+
{{else}}
|
|
132
|
+
// Generic code
|
|
133
|
+
{{/if}}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Example 4: Looping Over Items
|
|
137
|
+
|
|
138
|
+
**Scenario:** Generate multiple DTOs
|
|
139
|
+
|
|
140
|
+
```handlebars
|
|
141
|
+
// Type: morph-each → Tab
|
|
142
|
+
{{#each entities}}
|
|
143
|
+
public class {{pascalCase this.name}}Dto
|
|
144
|
+
{
|
|
145
|
+
// Properties
|
|
146
|
+
}
|
|
147
|
+
{{/each}}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 📝 Customizing Snippets
|
|
153
|
+
|
|
154
|
+
### VS Code
|
|
155
|
+
|
|
156
|
+
Edit `framework/templates/.vscode/morph-templates.code-snippets`:
|
|
157
|
+
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"MORPH Template - Custom": {
|
|
161
|
+
"prefix": "morph-custom",
|
|
162
|
+
"body": [
|
|
163
|
+
"// Your custom template",
|
|
164
|
+
"$1"
|
|
165
|
+
],
|
|
166
|
+
"description": "Your description"
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### JetBrains IDEs
|
|
172
|
+
|
|
173
|
+
1. Go to `File > Settings > Editor > Live Templates`
|
|
174
|
+
2. Select "MORPH Templates" group
|
|
175
|
+
3. Click `+` → `Live Template`
|
|
176
|
+
4. Set abbreviation (prefix), description, and template text
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 🎨 Snippet Scopes
|
|
181
|
+
|
|
182
|
+
| Snippet | VS Code Scope | JetBrains Scope |
|
|
183
|
+
|---------|---------------|-----------------|
|
|
184
|
+
| Handlebars helpers | All files | All files |
|
|
185
|
+
| .NET templates | `.cs`, `.razor` | C#, HTML |
|
|
186
|
+
| TypeScript templates | `.ts`, `.tsx` | TypeScript |
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## 💡 Best Practices
|
|
191
|
+
|
|
192
|
+
**1. Use Helpers Over Pre-computed Variables**
|
|
193
|
+
|
|
194
|
+
```handlebars
|
|
195
|
+
✅ GOOD: {{pascalCase FEATURE_NAME}}Service
|
|
196
|
+
❌ BAD: {{FEATURE_NAME_PASCAL}}Service // Deprecated
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**2. Chain Helpers for Complex Transformations**
|
|
200
|
+
|
|
201
|
+
```handlebars
|
|
202
|
+
// Pluralize a PascalCase name
|
|
203
|
+
{{pascalCase (pluralize FEATURE_NAME)}} // Users, Orders, etc.
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**3. Use Conditional Blocks for Stack-Specific Code**
|
|
207
|
+
|
|
208
|
+
```handlebars
|
|
209
|
+
{{#if (eq STACK "Nextjs")}}
|
|
210
|
+
import { useRouter } from 'next/navigation';
|
|
211
|
+
{{/if}}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**4. Extract Common Patterns to Snippets**
|
|
215
|
+
|
|
216
|
+
If you find yourself typing the same template code repeatedly, create a custom snippet!
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## 🔧 Troubleshooting
|
|
221
|
+
|
|
222
|
+
### Snippets Not Showing Up (VS Code)
|
|
223
|
+
|
|
224
|
+
**Solution:**
|
|
225
|
+
1. Check file extension matches snippet scope (e.g., `.cs` for C# snippets)
|
|
226
|
+
2. Reload VS Code: `Ctrl+Shift+P` → `Developer: Reload Window`
|
|
227
|
+
3. Verify snippet file location: `framework/templates/.vscode/morph-templates.code-snippets`
|
|
228
|
+
|
|
229
|
+
### Snippets Not Showing Up (JetBrains)
|
|
230
|
+
|
|
231
|
+
**Solution:**
|
|
232
|
+
1. Verify import: `File > Settings > Editor > Live Templates` → Check "MORPH Templates" group exists
|
|
233
|
+
2. Check snippet scope: Click snippet → Verify "Applicable in" contexts
|
|
234
|
+
3. Restart IDE
|
|
235
|
+
|
|
236
|
+
### Tab Not Expanding Snippet
|
|
237
|
+
|
|
238
|
+
**Solution:**
|
|
239
|
+
- **VS Code:** Ensure `editor.tabCompletion` is enabled in settings
|
|
240
|
+
- **JetBrains:** Ensure `Tab` is set as expansion key in `Settings > Editor > Live Templates`
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## 📚 Related Documentation
|
|
245
|
+
|
|
246
|
+
- [Template System README](framework/templates/README.md) - Complete template system documentation
|
|
247
|
+
- [Handlebars Helpers](framework/templates/README.md#handlebars-helpers) - Full list of available helpers
|
|
248
|
+
- [CLI Template Commands](CLAUDE.md#templates) - Command-line template management
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## 🎁 Contributing Custom Snippets
|
|
253
|
+
|
|
254
|
+
Have a useful snippet? Share it!
|
|
255
|
+
|
|
256
|
+
1. Add snippet to appropriate file (`.vscode/` or `.idea/`)
|
|
257
|
+
2. Document it in this README (add row to tables)
|
|
258
|
+
3. Submit PR with description of use case
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
*MORPH-SPEC Template Snippets by Polymorphism Tech*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
namespace {{pascalCase PROJECT_NAME}}.Abstractions
|
|
2
|
-
{
|
|
3
|
-
public interface IApiEndpoint
|
|
4
|
-
{
|
|
5
|
-
void MapEndpoint(IEndpointRouteBuilder app);
|
|
6
|
-
}
|
|
7
|
-
}
|
|
1
|
+
namespace {{pascalCase PROJECT_NAME}}.Abstractions
|
|
2
|
+
{
|
|
3
|
+
public interface IApiEndpoint
|
|
4
|
+
{
|
|
5
|
+
void MapEndpoint(IEndpointRouteBuilder app);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
namespace {{pascalCase PROJECT_NAME}}.Features.{{pascalCase FEATURE_NAME}}Feature.{{pascalCase SLICE_NAME}};
|
|
2
|
-
|
|
3
|
-
// TODO: Add properties matching the {{pascalCase SLICE_NAME}} command
|
|
4
|
-
public sealed record {{pascalCase SLICE_NAME}}Request();
|
|
1
|
+
namespace {{pascalCase PROJECT_NAME}}.Features.{{pascalCase FEATURE_NAME}}Feature.{{pascalCase SLICE_NAME}};
|
|
2
|
+
|
|
3
|
+
// TODO: Add properties matching the {{pascalCase SLICE_NAME}} command
|
|
4
|
+
public sealed record {{pascalCase SLICE_NAME}}Request();
|