spec-first-copilot 0.6.0-beta.9 → 0.7.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 +252 -167
- package/bin/cli.js +70 -70
- package/lib/init.js +92 -92
- package/lib/update.js +132 -132
- package/package.json +1 -1
- package/templates/.ai/memory/napkin.md +68 -68
- package/templates/.github/CHANGELOG.md +121 -0
- package/templates/.github/adapters/SETUP.md +314 -314
- package/templates/.github/adapters/confluence.md +295 -295
- package/templates/.github/adapters/errors.md +234 -234
- package/templates/.github/adapters/filesystem.md +353 -353
- package/templates/.github/adapters/interface.md +301 -301
- package/templates/.github/adapters/naming.md +241 -241
- package/templates/.github/adapters/registry.md +244 -244
- package/templates/.github/agents/backend-coder.md +14 -14
- package/templates/.github/agents/db-coder.md +165 -165
- package/templates/.github/agents/doc-writer.md +66 -53
- package/templates/.github/agents/frontend-coder.md +5 -5
- package/templates/.github/agents/infra-coder.md +341 -341
- package/templates/.github/agents/reviewer.md +6 -6
- package/templates/.github/agents/security-reviewer.md +153 -153
- package/templates/.github/copilot-instructions.md +272 -262
- package/templates/.github/instructions/docs.instructions.md +147 -145
- package/templates/.github/instructions/sensitive-files.instructions.md +32 -32
- package/templates/.github/rules.md +229 -229
- package/templates/.github/scripts/bootstrap-confluence.js +289 -223
- package/templates/.github/skills/sf-design/SKILL.md +161 -216
- package/templates/.github/skills/sf-dev/SKILL.md +204 -351
- package/templates/.github/skills/sf-discovery/SKILL.md +415 -414
- package/templates/.github/skills/sf-extract/SKILL.md +225 -249
- package/templates/.github/skills/sf-load/SKILL.md +296 -295
- package/templates/.github/skills/sf-mcp/SKILL.md +386 -385
- package/templates/.github/skills/sf-merge-docs/SKILL.md +152 -100
- package/templates/.github/skills/sf-plan/SKILL.md +152 -128
- package/templates/.github/skills/sf-publish/SKILL.md +144 -143
- package/templates/.github/skills/sf-session-finish/SKILL.md +93 -120
- package/templates/.github/skills/sf-start/SKILL.md +192 -145
- package/templates/.github/templates/estrutura/apiContracts.template.md +160 -159
- package/templates/.github/templates/estrutura/architecture.template.md +169 -168
- package/templates/.github/templates/estrutura/conventions.template.md +214 -212
- package/templates/.github/templates/estrutura/decisions.template.md +107 -107
- package/templates/.github/templates/estrutura/domain.template.md +161 -160
- package/templates/.github/templates/feature/PRD.template.md +279 -286
- package/templates/.github/templates/feature/Progresso.template.md +141 -141
- package/templates/.github/templates/feature/TRD.template.md +358 -0
- package/templates/.github/templates/feature/context.template.md +89 -48
- package/templates/.github/templates/feature/extract-log.template.md +49 -39
- package/templates/.github/templates/feature/projetos.template.yaml +79 -79
- package/templates/.github/templates/global/progresso_global.template.md +59 -57
- package/templates/.github/templates/specs/brief.template.md +66 -59
- package/templates/.github/templates/specs/contracts.template.md +147 -141
- package/templates/.github/templates/specs/scenarios.template.md +125 -117
- package/templates/.github/templates/specs/tasks.template.md +65 -63
- package/templates/_gitignore +35 -35
- package/templates/sfw.config.yml.example +147 -147
- package/templates/.github/templates/feature/backlog-extraido.template.md +0 -156
- package/templates/.github/templates/feature/sdd.template.md +0 -559
|
@@ -1,241 +1,241 @@
|
|
|
1
|
-
# Naming Templating Engine
|
|
2
|
-
|
|
3
|
-
> Motor trivial que transforma os templates em `sfw.config.yml > naming.*`
|
|
4
|
-
> em strings finais (títulos Confluence, nomes de pasta, etc.).
|
|
5
|
-
>
|
|
6
|
-
> **Função pura, zero dependência externa.** Implementável em qualquer
|
|
7
|
-
> linguagem em ~15 linhas. Este documento é a especificação que
|
|
8
|
-
> adapters/skills/bootstrap seguem.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## Por que existe
|
|
13
|
-
|
|
14
|
-
Antes da decisão do adapter layer, títulos eram hardcoded em string literal
|
|
15
|
-
no código das skills:
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
title = `out_${scope}_PRD`; // ← hardcoded em 5 lugares
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Isso forçava:
|
|
22
|
-
1. Qualquer mudança de convenção → tocar N arquivos
|
|
23
|
-
2. Times com outra convenção (ex: `[PRD] scope`) não conseguiam usar SFW
|
|
24
|
-
3. Testes precisavam mockar strings mágicas
|
|
25
|
-
|
|
26
|
-
Agora a convenção vive em **um** arquivo (`sfw.config.yml`) e é aplicada via
|
|
27
|
-
templating engine centralizado.
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## Placeholders suportados
|
|
32
|
-
|
|
33
|
-
| Placeholder | O que substitui | Fonte |
|
|
34
|
-
|-------------|-----------------|-------|
|
|
35
|
-
| `{scope}` | Nome do item no Input (ex: `app_barbearia`, `feat_login`) | Argumento da skill |
|
|
36
|
-
| `{type}` | Tipo do artefato (ex: `PRD`, `
|
|
37
|
-
|
|
38
|
-
**Nenhum outro placeholder é aceito no MVP.** Expansões futuras entram por
|
|
39
|
-
proposta explícita (ex: `{phase}`, `{repo}`, `{timestamp}`).
|
|
40
|
-
|
|
41
|
-
**O Input NÃO usa naming templating.** O usuário nomeia livremente os items
|
|
42
|
-
no backend (Confluence, filesystem, etc.). O agent lê o nome tal qual está.
|
|
43
|
-
Naming só é aplicado no Output (o que o agent GERA).
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## Templates no manifest
|
|
48
|
-
|
|
49
|
-
O `sfw.config.yml` define 2 templates de output:
|
|
50
|
-
|
|
51
|
-
| Template | Onde | Exemplo |
|
|
52
|
-
|----------|------|---------|
|
|
53
|
-
| `output_container` | Subpasta/page-mãe por scope no Output | `"out_{scope}"` → `out_app_barbearia` |
|
|
54
|
-
| `output_artifact` | Nome do artefato dentro do container | `"{scope} - {type}"` → `app_barbearia - PRD` |
|
|
55
|
-
|
|
56
|
-
**`output_container`** é o agrupador. No Confluence vira uma page-mãe com
|
|
57
|
-
children; no filesystem vira uma pasta.
|
|
58
|
-
|
|
59
|
-
**`output_artifact`** é o item individual (PRD,
|
|
60
|
-
Confluence é title de uma child page; no filesystem é nome de arquivo
|
|
61
|
-
(adapter adiciona `.md` automaticamente).
|
|
62
|
-
|
|
63
|
-
**Por que `{scope}` no `output_artifact`?** No Confluence, titles são
|
|
64
|
-
unique per SPACE — sem `{scope}`, duas features teriam `PRD` e `
|
|
65
|
-
mesmo título e colidiria. No filesystem não colide porque vive dentro de
|
|
66
|
-
pastas diferentes, então o user pode configurar `output_artifact: "{type}"`
|
|
67
|
-
se preferir limpar redundância.
|
|
68
|
-
|
|
69
|
-
---
|
|
70
|
-
|
|
71
|
-
## Contrato da função
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
/**
|
|
75
|
-
* Aplica templating de nome.
|
|
76
|
-
*
|
|
77
|
-
* @param template String com placeholders {scope}, {type}
|
|
78
|
-
* @param vars Objeto com os valores — chaves que não aparecem no template
|
|
79
|
-
* são ignoradas (não é erro)
|
|
80
|
-
* @returns String final, pronta pra uso como título/nome
|
|
81
|
-
* @throws NamingTemplateError se o template contém placeholder desconhecido
|
|
82
|
-
* OU se um placeholder usado não tem valor em vars
|
|
83
|
-
*/
|
|
84
|
-
function applyNaming(template: string, vars: {
|
|
85
|
-
scope?: string;
|
|
86
|
-
type?: string;
|
|
87
|
-
}): string
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### Regras invioláveis
|
|
91
|
-
|
|
92
|
-
1. **Placeholder desconhecido lança erro.**
|
|
93
|
-
`"{type} {module}"` → `NamingTemplateError: unknown placeholder "{module}"`.
|
|
94
|
-
Nunca retorna a string literal `{module}` — isso vazaria inconsistência pros backends.
|
|
95
|
-
|
|
96
|
-
2. **Placeholder sem valor lança erro.**
|
|
97
|
-
Template `"{scope} - {type}"` com `vars = { type: "PRD" }`
|
|
98
|
-
→ `NamingTemplateError: placeholder {scope} required but not provided`.
|
|
99
|
-
|
|
100
|
-
3. **Chaves extras em `vars` são ignoradas.**
|
|
101
|
-
Não é erro passar `vars.scope = "x"` pra um template que só usa `{type}`.
|
|
102
|
-
Isso permite que as skills passem o objeto cheio sem se preocupar com qual
|
|
103
|
-
template está sendo aplicado.
|
|
104
|
-
|
|
105
|
-
4. **Substituição é literal — sem escape.**
|
|
106
|
-
Se o `scope` tem caracteres especiais pro backend (ex: `/` no filesystem,
|
|
107
|
-
`>` no Confluence), é responsabilidade do adapter sanitizar **depois** do
|
|
108
|
-
apply. Naming engine não conhece constraints de backend.
|
|
109
|
-
|
|
110
|
-
5. **Case-sensitive.** `{Scope}` ≠ `{scope}`. Não normaliza.
|
|
111
|
-
|
|
112
|
-
6. **Não suporta lógica.** Sem `{scope|upper}`, sem condicionais. Zero magic.
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
## Pseudo-código de referência
|
|
117
|
-
|
|
118
|
-
```typescript
|
|
119
|
-
const PLACEHOLDER_RE = /\{(\w+)\}/g;
|
|
120
|
-
const KNOWN = new Set(["scope", "type"]);
|
|
121
|
-
|
|
122
|
-
function applyNaming(template: string, vars: Record<string, string | undefined>): string {
|
|
123
|
-
// 1. Detecta placeholders usados no template
|
|
124
|
-
const used = new Set<string>();
|
|
125
|
-
for (const m of template.matchAll(PLACEHOLDER_RE)) {
|
|
126
|
-
used.add(m[1]);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// 2. Valida placeholders desconhecidos
|
|
130
|
-
for (const p of used) {
|
|
131
|
-
if (!KNOWN.has(p)) {
|
|
132
|
-
throw new NamingTemplateError(
|
|
133
|
-
`unknown placeholder "{${p}}" in template "${template}" (supported: ${[...KNOWN].join(", ")})`
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// 3. Valida valores ausentes
|
|
139
|
-
for (const p of used) {
|
|
140
|
-
if (vars[p] === undefined || vars[p] === "") {
|
|
141
|
-
throw new NamingTemplateError(
|
|
142
|
-
`placeholder {${p}} required but not provided`
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// 4. Substitui
|
|
148
|
-
return template.replace(PLACEHOLDER_RE, (_, key) => vars[key]!);
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
---
|
|
153
|
-
|
|
154
|
-
## Exemplos (matriz de casos)
|
|
155
|
-
|
|
156
|
-
| Template | vars | Resultado | OK? |
|
|
157
|
-
|----------|------|-----------|-----|
|
|
158
|
-
| `out_{scope}` | `{scope:"app_barbearia"}` | `out_app_barbearia` | ✅ |
|
|
159
|
-
| `{scope} - {type}` | `{scope:"app_barbearia", type:"PRD"}` | `app_barbearia - PRD` | ✅ |
|
|
160
|
-
| `{type}` | `{type:"
|
|
161
|
-
| `[{type}] {scope}` | `{type:"PRD", scope:"feat_login"}` | `[PRD] feat_login` | ✅ |
|
|
162
|
-
| `{type}/{scope}` | `{type:"docs", scope:"arch"}` | `docs/arch` | ✅ (adapter sanitiza `/` depois) |
|
|
163
|
-
| `{scope} - {type}` | `{type:"PRD"}` | — | ❌ `NamingTemplateError: placeholder {scope} required` |
|
|
164
|
-
| `{type} {module}` | `{type:"PRD"}` | — | ❌ `NamingTemplateError: unknown placeholder "{module}"` |
|
|
165
|
-
| `hello world` | `{}` | `hello world` | ✅ (sem placeholders) |
|
|
166
|
-
| `out_{scope}` | `{scope:""}` | — | ❌ (string vazia é tratada como ausente) |
|
|
167
|
-
|
|
168
|
-
---
|
|
169
|
-
|
|
170
|
-
## Uso nas skills — matriz de quais templates usam quais placeholders
|
|
171
|
-
|
|
172
|
-
| Skill / operação | Template consumido | Placeholders usados |
|
|
173
|
-
|------------------|-------------------|---------------------|
|
|
174
|
-
| `/sf-load` busca scope no Input | Nenhum — usa o nome do item **tal qual está** no backend | — |
|
|
175
|
-
| `/sf-extract` publica PRD | `output_container` + `output_artifact` (type=PRD) | `{scope}`, `{type}` |
|
|
176
|
-
| `/sf-
|
|
177
|
-
| `/sf-plan` publica Progresso | `output_container` + `output_artifact` (type=Progresso) | `{scope}`, `{type}` |
|
|
178
|
-
| `/sf-load` grava scope folder local | `output_container` (reuso pro path local) | `{scope}` |
|
|
179
|
-
|
|
180
|
-
Skills **sempre** passam `{ scope, type }` cheio — o engine ignora o que o
|
|
181
|
-
template não usa.
|
|
182
|
-
|
|
183
|
-
**`/sf-load` não formata Input** — só lê o nome real do item via
|
|
184
|
-
`adapter.listChildren()` e busca match exato com o argumento da skill.
|
|
185
|
-
|
|
186
|
-
---
|
|
187
|
-
|
|
188
|
-
## Fluxo ponta-a-ponta (exemplo)
|
|
189
|
-
|
|
190
|
-
Config:
|
|
191
|
-
```yaml
|
|
192
|
-
naming:
|
|
193
|
-
output_container: "out_{scope}"
|
|
194
|
-
output_artifact: "{scope} - {type}"
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
Cenário: `/sf-start app_barbearia` → `/sf-extract` → `/sf-design` → `/sf-plan`
|
|
198
|
-
|
|
199
|
-
| Passo | Template usado | vars | Resultado |
|
|
200
|
-
|-------|---------------|------|-----------|
|
|
201
|
-
| `/sf-load` busca item no Input | nenhum | — | Encontra page "app_barbearia" pelo nome |
|
|
202
|
-
| `/sf-load` grava local | `output_container` | `{scope:"app_barbearia"}` | Pasta `workspace/Input/out_app_barbearia/` |
|
|
203
|
-
| `/sf-extract` cria container no Output | `output_container` | `{scope:"app_barbearia"}` | Page/pasta `out_app_barbearia` |
|
|
204
|
-
| `/sf-extract` publica PRD | `output_artifact` | `{scope:"app_barbearia", type:"PRD"}` | `app_barbearia - PRD` (dentro do container) |
|
|
205
|
-
| `/sf-
|
|
206
|
-
| `/sf-plan` publica Progresso | `output_artifact` | `{scope:"app_barbearia", type:"Progresso"}` | `app_barbearia - Progresso` |
|
|
207
|
-
|
|
208
|
-
Resultado no Confluence:
|
|
209
|
-
```
|
|
210
|
-
Output (page-mãe)
|
|
211
|
-
└── out_app_barbearia (container)
|
|
212
|
-
├── app_barbearia - PRD
|
|
213
|
-
├── app_barbearia -
|
|
214
|
-
└── app_barbearia - Progresso
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
Resultado no filesystem:
|
|
218
|
-
```
|
|
219
|
-
workspace/Output/
|
|
220
|
-
└── out_app_barbearia/
|
|
221
|
-
├── app_barbearia - PRD.md
|
|
222
|
-
├── app_barbearia -
|
|
223
|
-
└── app_barbearia - Progresso.md
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
---
|
|
227
|
-
|
|
228
|
-
## Onde vive o código da engine
|
|
229
|
-
|
|
230
|
-
Tom do MVP: implementação inline dentro do adapter layer, não arquivo próprio.
|
|
231
|
-
Cada adapter que precisa formatar título chama `applyNaming(template, vars)`.
|
|
232
|
-
Quando a implementação real for escrita (pós-P0.2), avaliar se extrai pra
|
|
233
|
-
`lib/naming.ts` ou mantém como função no registry.
|
|
234
|
-
|
|
235
|
-
---
|
|
236
|
-
|
|
237
|
-
## Referências
|
|
238
|
-
|
|
239
|
-
- Schema do manifest: `sfw.config.yml.example` (raiz do projeto)
|
|
240
|
-
- Interface do adapter: `.github/adapters/interface.md`
|
|
241
|
-
- Erros: `.github/adapters/errors.md` — `NamingTemplateError` é subclasse de `ValidationError`
|
|
1
|
+
# Naming Templating Engine
|
|
2
|
+
|
|
3
|
+
> Motor trivial que transforma os templates em `sfw.config.yml > naming.*`
|
|
4
|
+
> em strings finais (títulos Confluence, nomes de pasta, etc.).
|
|
5
|
+
>
|
|
6
|
+
> **Função pura, zero dependência externa.** Implementável em qualquer
|
|
7
|
+
> linguagem em ~15 linhas. Este documento é a especificação que
|
|
8
|
+
> adapters/skills/bootstrap seguem.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Por que existe
|
|
13
|
+
|
|
14
|
+
Antes da decisão do adapter layer, títulos eram hardcoded em string literal
|
|
15
|
+
no código das skills:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
title = `out_${scope}_PRD`; // ← hardcoded em 5 lugares
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Isso forçava:
|
|
22
|
+
1. Qualquer mudança de convenção → tocar N arquivos
|
|
23
|
+
2. Times com outra convenção (ex: `[PRD] scope`) não conseguiam usar SFW
|
|
24
|
+
3. Testes precisavam mockar strings mágicas
|
|
25
|
+
|
|
26
|
+
Agora a convenção vive em **um** arquivo (`sfw.config.yml`) e é aplicada via
|
|
27
|
+
templating engine centralizado.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Placeholders suportados
|
|
32
|
+
|
|
33
|
+
| Placeholder | O que substitui | Fonte |
|
|
34
|
+
|-------------|-----------------|-------|
|
|
35
|
+
| `{scope}` | Nome do item no Input (ex: `app_barbearia`, `feat_login`) | Argumento da skill |
|
|
36
|
+
| `{type}` | Tipo do artefato (ex: `PRD`, `TRD`, `Progresso`) | Contexto da skill |
|
|
37
|
+
|
|
38
|
+
**Nenhum outro placeholder é aceito no MVP.** Expansões futuras entram por
|
|
39
|
+
proposta explícita (ex: `{phase}`, `{repo}`, `{timestamp}`).
|
|
40
|
+
|
|
41
|
+
**O Input NÃO usa naming templating.** O usuário nomeia livremente os items
|
|
42
|
+
no backend (Confluence, filesystem, etc.). O agent lê o nome tal qual está.
|
|
43
|
+
Naming só é aplicado no Output (o que o agent GERA).
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Templates no manifest
|
|
48
|
+
|
|
49
|
+
O `sfw.config.yml` define 2 templates de output:
|
|
50
|
+
|
|
51
|
+
| Template | Onde | Exemplo |
|
|
52
|
+
|----------|------|---------|
|
|
53
|
+
| `output_container` | Subpasta/page-mãe por scope no Output | `"out_{scope}"` → `out_app_barbearia` |
|
|
54
|
+
| `output_artifact` | Nome do artefato dentro do container | `"{scope} - {type}"` → `app_barbearia - PRD` |
|
|
55
|
+
|
|
56
|
+
**`output_container`** é o agrupador. No Confluence vira uma page-mãe com
|
|
57
|
+
children; no filesystem vira uma pasta.
|
|
58
|
+
|
|
59
|
+
**`output_artifact`** é o item individual (PRD, TRD, Progresso). No
|
|
60
|
+
Confluence é title de uma child page; no filesystem é nome de arquivo
|
|
61
|
+
(adapter adiciona `.md` automaticamente).
|
|
62
|
+
|
|
63
|
+
**Por que `{scope}` no `output_artifact`?** No Confluence, titles são
|
|
64
|
+
unique per SPACE — sem `{scope}`, duas features teriam `PRD` e `TRD` com
|
|
65
|
+
mesmo título e colidiria. No filesystem não colide porque vive dentro de
|
|
66
|
+
pastas diferentes, então o user pode configurar `output_artifact: "{type}"`
|
|
67
|
+
se preferir limpar redundância.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Contrato da função
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
/**
|
|
75
|
+
* Aplica templating de nome.
|
|
76
|
+
*
|
|
77
|
+
* @param template String com placeholders {scope}, {type}
|
|
78
|
+
* @param vars Objeto com os valores — chaves que não aparecem no template
|
|
79
|
+
* são ignoradas (não é erro)
|
|
80
|
+
* @returns String final, pronta pra uso como título/nome
|
|
81
|
+
* @throws NamingTemplateError se o template contém placeholder desconhecido
|
|
82
|
+
* OU se um placeholder usado não tem valor em vars
|
|
83
|
+
*/
|
|
84
|
+
function applyNaming(template: string, vars: {
|
|
85
|
+
scope?: string;
|
|
86
|
+
type?: string;
|
|
87
|
+
}): string
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Regras invioláveis
|
|
91
|
+
|
|
92
|
+
1. **Placeholder desconhecido lança erro.**
|
|
93
|
+
`"{type} {module}"` → `NamingTemplateError: unknown placeholder "{module}"`.
|
|
94
|
+
Nunca retorna a string literal `{module}` — isso vazaria inconsistência pros backends.
|
|
95
|
+
|
|
96
|
+
2. **Placeholder sem valor lança erro.**
|
|
97
|
+
Template `"{scope} - {type}"` com `vars = { type: "PRD" }`
|
|
98
|
+
→ `NamingTemplateError: placeholder {scope} required but not provided`.
|
|
99
|
+
|
|
100
|
+
3. **Chaves extras em `vars` são ignoradas.**
|
|
101
|
+
Não é erro passar `vars.scope = "x"` pra um template que só usa `{type}`.
|
|
102
|
+
Isso permite que as skills passem o objeto cheio sem se preocupar com qual
|
|
103
|
+
template está sendo aplicado.
|
|
104
|
+
|
|
105
|
+
4. **Substituição é literal — sem escape.**
|
|
106
|
+
Se o `scope` tem caracteres especiais pro backend (ex: `/` no filesystem,
|
|
107
|
+
`>` no Confluence), é responsabilidade do adapter sanitizar **depois** do
|
|
108
|
+
apply. Naming engine não conhece constraints de backend.
|
|
109
|
+
|
|
110
|
+
5. **Case-sensitive.** `{Scope}` ≠ `{scope}`. Não normaliza.
|
|
111
|
+
|
|
112
|
+
6. **Não suporta lógica.** Sem `{scope|upper}`, sem condicionais. Zero magic.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Pseudo-código de referência
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const PLACEHOLDER_RE = /\{(\w+)\}/g;
|
|
120
|
+
const KNOWN = new Set(["scope", "type"]);
|
|
121
|
+
|
|
122
|
+
function applyNaming(template: string, vars: Record<string, string | undefined>): string {
|
|
123
|
+
// 1. Detecta placeholders usados no template
|
|
124
|
+
const used = new Set<string>();
|
|
125
|
+
for (const m of template.matchAll(PLACEHOLDER_RE)) {
|
|
126
|
+
used.add(m[1]);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 2. Valida placeholders desconhecidos
|
|
130
|
+
for (const p of used) {
|
|
131
|
+
if (!KNOWN.has(p)) {
|
|
132
|
+
throw new NamingTemplateError(
|
|
133
|
+
`unknown placeholder "{${p}}" in template "${template}" (supported: ${[...KNOWN].join(", ")})`
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 3. Valida valores ausentes
|
|
139
|
+
for (const p of used) {
|
|
140
|
+
if (vars[p] === undefined || vars[p] === "") {
|
|
141
|
+
throw new NamingTemplateError(
|
|
142
|
+
`placeholder {${p}} required but not provided`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// 4. Substitui
|
|
148
|
+
return template.replace(PLACEHOLDER_RE, (_, key) => vars[key]!);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Exemplos (matriz de casos)
|
|
155
|
+
|
|
156
|
+
| Template | vars | Resultado | OK? |
|
|
157
|
+
|----------|------|-----------|-----|
|
|
158
|
+
| `out_{scope}` | `{scope:"app_barbearia"}` | `out_app_barbearia` | ✅ |
|
|
159
|
+
| `{scope} - {type}` | `{scope:"app_barbearia", type:"PRD"}` | `app_barbearia - PRD` | ✅ |
|
|
160
|
+
| `{type}` | `{type:"TRD"}` | `TRD` | ✅ (filesystem dentro de subpasta) |
|
|
161
|
+
| `[{type}] {scope}` | `{type:"PRD", scope:"feat_login"}` | `[PRD] feat_login` | ✅ |
|
|
162
|
+
| `{type}/{scope}` | `{type:"docs", scope:"arch"}` | `docs/arch` | ✅ (adapter sanitiza `/` depois) |
|
|
163
|
+
| `{scope} - {type}` | `{type:"PRD"}` | — | ❌ `NamingTemplateError: placeholder {scope} required` |
|
|
164
|
+
| `{type} {module}` | `{type:"PRD"}` | — | ❌ `NamingTemplateError: unknown placeholder "{module}"` |
|
|
165
|
+
| `hello world` | `{}` | `hello world` | ✅ (sem placeholders) |
|
|
166
|
+
| `out_{scope}` | `{scope:""}` | — | ❌ (string vazia é tratada como ausente) |
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Uso nas skills — matriz de quais templates usam quais placeholders
|
|
171
|
+
|
|
172
|
+
| Skill / operação | Template consumido | Placeholders usados |
|
|
173
|
+
|------------------|-------------------|---------------------|
|
|
174
|
+
| `/sf-load` busca scope no Input | Nenhum — usa o nome do item **tal qual está** no backend | — |
|
|
175
|
+
| `/sf-extract` publica PRD | `output_container` + `output_artifact` (type=PRD) | `{scope}`, `{type}` |
|
|
176
|
+
| `/sf-extract` publica TRD | `output_container` + `output_artifact` (type=TRD) | `{scope}`, `{type}` |
|
|
177
|
+
| `/sf-plan` publica Progresso | `output_container` + `output_artifact` (type=Progresso) | `{scope}`, `{type}` |
|
|
178
|
+
| `/sf-load` grava scope folder local | `output_container` (reuso pro path local) | `{scope}` |
|
|
179
|
+
|
|
180
|
+
Skills **sempre** passam `{ scope, type }` cheio — o engine ignora o que o
|
|
181
|
+
template não usa.
|
|
182
|
+
|
|
183
|
+
**`/sf-load` não formata Input** — só lê o nome real do item via
|
|
184
|
+
`adapter.listChildren()` e busca match exato com o argumento da skill.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Fluxo ponta-a-ponta (exemplo)
|
|
189
|
+
|
|
190
|
+
Config:
|
|
191
|
+
```yaml
|
|
192
|
+
naming:
|
|
193
|
+
output_container: "out_{scope}"
|
|
194
|
+
output_artifact: "{scope} - {type}"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Cenário: `/sf-start app_barbearia` → `/sf-extract` → `/sf-design` → `/sf-plan`
|
|
198
|
+
|
|
199
|
+
| Passo | Template usado | vars | Resultado |
|
|
200
|
+
|-------|---------------|------|-----------|
|
|
201
|
+
| `/sf-load` busca item no Input | nenhum | — | Encontra page "app_barbearia" pelo nome |
|
|
202
|
+
| `/sf-load` grava local | `output_container` | `{scope:"app_barbearia"}` | Pasta `workspace/Input/out_app_barbearia/` |
|
|
203
|
+
| `/sf-extract` cria container no Output | `output_container` | `{scope:"app_barbearia"}` | Page/pasta `out_app_barbearia` |
|
|
204
|
+
| `/sf-extract` publica PRD | `output_artifact` | `{scope:"app_barbearia", type:"PRD"}` | `app_barbearia - PRD` (dentro do container) |
|
|
205
|
+
| `/sf-extract` publica TRD | `output_artifact` | `{scope:"app_barbearia", type:"TRD"}` | `app_barbearia - TRD` |
|
|
206
|
+
| `/sf-plan` publica Progresso | `output_artifact` | `{scope:"app_barbearia", type:"Progresso"}` | `app_barbearia - Progresso` |
|
|
207
|
+
|
|
208
|
+
Resultado no Confluence:
|
|
209
|
+
```
|
|
210
|
+
Output (page-mãe)
|
|
211
|
+
└── out_app_barbearia (container)
|
|
212
|
+
├── app_barbearia - PRD
|
|
213
|
+
├── app_barbearia - TRD
|
|
214
|
+
└── app_barbearia - Progresso
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Resultado no filesystem:
|
|
218
|
+
```
|
|
219
|
+
workspace/Output/
|
|
220
|
+
└── out_app_barbearia/
|
|
221
|
+
├── app_barbearia - PRD.md
|
|
222
|
+
├── app_barbearia - TRD.md
|
|
223
|
+
└── app_barbearia - Progresso.md
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Onde vive o código da engine
|
|
229
|
+
|
|
230
|
+
Tom do MVP: implementação inline dentro do adapter layer, não arquivo próprio.
|
|
231
|
+
Cada adapter que precisa formatar título chama `applyNaming(template, vars)`.
|
|
232
|
+
Quando a implementação real for escrita (pós-P0.2), avaliar se extrai pra
|
|
233
|
+
`lib/naming.ts` ou mantém como função no registry.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Referências
|
|
238
|
+
|
|
239
|
+
- Schema do manifest: `sfw.config.yml.example` (raiz do projeto)
|
|
240
|
+
- Interface do adapter: `.github/adapters/interface.md`
|
|
241
|
+
- Erros: `.github/adapters/errors.md` — `NamingTemplateError` é subclasse de `ValidationError`
|