spec-first-copilot 0.5.0-beta.4 → 0.5.0-beta.6
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/package.json
CHANGED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# Adapter Setup Guide
|
|
2
|
+
|
|
3
|
+
> Guia passo a passo para configurar os adapters do SFW.
|
|
4
|
+
> Se o usuário pedir ajuda pra configurar, siga este guia.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Escolha seu modo de trabalho
|
|
9
|
+
|
|
10
|
+
O SFW funciona em **3 modos**, configurados no `sfw.config.yml`:
|
|
11
|
+
|
|
12
|
+
| Modo | Input | Output | Quando usar |
|
|
13
|
+
|------|-------|--------|-------------|
|
|
14
|
+
| **Full Confluence** | `adapter: confluence` | `mode: auto` | Time usa Confluence, PM publica lá, quer artefatos lá |
|
|
15
|
+
| **Confluence só leitura** | `adapter: confluence` | `mode: off` | PM publica no Confluence, mas artefatos ficam só local |
|
|
16
|
+
| **100% local** | `adapter: filesystem` | `mode: off` | Time sem Confluence, tudo no disco |
|
|
17
|
+
|
|
18
|
+
Para mudar: edite `sfw.config.yml` e ajuste `input.adapter` e `output.targets[].mode`.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Setup 1: Confluence (Full ou só leitura)
|
|
23
|
+
|
|
24
|
+
### Pré-requisitos
|
|
25
|
+
|
|
26
|
+
- Python 3.10+ instalado
|
|
27
|
+
- Conta Atlassian com acesso ao Confluence Cloud
|
|
28
|
+
- Claude Code instalado
|
|
29
|
+
|
|
30
|
+
### Passo 1 — Instalar uvx
|
|
31
|
+
|
|
32
|
+
`uvx` é o runtime que executa o MCP server do Confluence.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Windows (PowerShell)
|
|
36
|
+
pip install uv
|
|
37
|
+
|
|
38
|
+
# Mac/Linux
|
|
39
|
+
pip install uv
|
|
40
|
+
# ou
|
|
41
|
+
brew install uv
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Verificar:
|
|
45
|
+
```bash
|
|
46
|
+
uvx --version
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Passo 2 — Gerar API Token Atlassian
|
|
50
|
+
|
|
51
|
+
1. Acesse: https://id.atlassian.com/manage-profile/security/api-tokens
|
|
52
|
+
2. Clique **Create API token**
|
|
53
|
+
3. Nome: `sfw-mcp` (ou qualquer nome descritivo)
|
|
54
|
+
4. Copie o token gerado — **ele só aparece uma vez**
|
|
55
|
+
|
|
56
|
+
Você vai precisar de:
|
|
57
|
+
- **Email**: seu email da conta Atlassian (ex: `dev@empresa.com`)
|
|
58
|
+
- **Token**: o token que acabou de copiar
|
|
59
|
+
- **URL do Confluence**: geralmente `https://sua-empresa.atlassian.net/wiki`
|
|
60
|
+
|
|
61
|
+
### Passo 3 — Criar `.mcp.json`
|
|
62
|
+
|
|
63
|
+
Crie o arquivo `.mcp.json` na **raiz do projeto** (onde está o `sfw.config.yml`).
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"mcpServers": {
|
|
68
|
+
"atlassian": {
|
|
69
|
+
"command": "uvx",
|
|
70
|
+
"args": ["mcp-atlassian"],
|
|
71
|
+
"env": {
|
|
72
|
+
"CONFLUENCE_URL": "https://sua-empresa.atlassian.net/wiki",
|
|
73
|
+
"CONFLUENCE_USERNAME": "seu-email@empresa.com",
|
|
74
|
+
"CONFLUENCE_API_TOKEN": "seu-token-aqui"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**IMPORTANTE — Gotchas aprendidos na prática:**
|
|
82
|
+
|
|
83
|
+
1. **Credenciais HARDCODED no arquivo** — `${VAR}` com variáveis de ambiente **NÃO funciona** no startup do Claude Code. Coloque os valores direto.
|
|
84
|
+
|
|
85
|
+
2. **`.mcp.json` DEVE estar no `.gitignore`** — tem credenciais. O template do kit já inclui isso, mas verifique:
|
|
86
|
+
```
|
|
87
|
+
# .gitignore
|
|
88
|
+
.mcp.json
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
3. **Após criar/editar `.mcp.json`, REINICIE o Claude Code** — servers MCP só são carregados no startup. Mudança no arquivo não tem efeito em sessão ativa.
|
|
92
|
+
|
|
93
|
+
4. **Primeira execução baixa ~118 dependências** — o pacote `mcp-atlassian` é pesado. A primeira vez pode demorar 1-2 minutos. As seguintes são instantâneas.
|
|
94
|
+
|
|
95
|
+
### Passo 4 — Descobrir seu Space Key
|
|
96
|
+
|
|
97
|
+
O Space Key é o identificador curto do espaço Confluence (ex: `ST`, `DEV`, `PROJ`).
|
|
98
|
+
|
|
99
|
+
Para encontrar:
|
|
100
|
+
- Abra qualquer página do seu espaço Confluence
|
|
101
|
+
- Olhe a URL: `https://empresa.atlassian.net/wiki/spaces/ST/pages/...`
|
|
102
|
+
- O `ST` é o Space Key
|
|
103
|
+
|
|
104
|
+
### Passo 5 — Criar estrutura no Confluence
|
|
105
|
+
|
|
106
|
+
Crie manualmente no Confluence (ou peça pro PM criar):
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
{Seu Space}
|
|
110
|
+
└── {Nome do Projeto} ← page raiz do projeto
|
|
111
|
+
├── Input ← PM coloca insumos aqui
|
|
112
|
+
│ └── (scopes como pages filhas)
|
|
113
|
+
└── Output ← agent publica aqui (não editar manualmente)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Anote os **Page IDs** de `Input` e `Output`:
|
|
117
|
+
- Abra a page → ... (menu) → Page information → o ID está na URL
|
|
118
|
+
- Ou: URL da page contém `/pages/123456/...` — o número é o Page ID
|
|
119
|
+
|
|
120
|
+
### Passo 6 — Configurar `sfw.config.yml`
|
|
121
|
+
|
|
122
|
+
Copie o `sfw.config.yml.example` pra `sfw.config.yml` e preencha:
|
|
123
|
+
|
|
124
|
+
```yaml
|
|
125
|
+
project:
|
|
126
|
+
name: "Meu Projeto"
|
|
127
|
+
|
|
128
|
+
naming:
|
|
129
|
+
output_container: "out_{scope}"
|
|
130
|
+
output_artifact: "{scope} - {type}"
|
|
131
|
+
|
|
132
|
+
input:
|
|
133
|
+
adapter: confluence
|
|
134
|
+
config:
|
|
135
|
+
space_key: "ST" # ← seu Space Key
|
|
136
|
+
parent_page_id: "360668" # ← Page ID da page Input
|
|
137
|
+
recursive: true
|
|
138
|
+
include_attachments: true
|
|
139
|
+
cache:
|
|
140
|
+
local_dir: "workspace/Input/"
|
|
141
|
+
log: ".ai/load-log.md"
|
|
142
|
+
incremental: true
|
|
143
|
+
|
|
144
|
+
output:
|
|
145
|
+
targets:
|
|
146
|
+
- name: confluence-mirror
|
|
147
|
+
adapter: confluence
|
|
148
|
+
config:
|
|
149
|
+
space_key: "ST" # ← mesmo Space Key
|
|
150
|
+
parent_page_id: "294931" # ← Page ID da page Output
|
|
151
|
+
publishes: [PRD, TRD, SDD, Progresso]
|
|
152
|
+
mode: auto # ← auto | manual | off
|
|
153
|
+
conflict_detection: version
|
|
154
|
+
approval_mechanism: label
|
|
155
|
+
approval_label: "sfw-approved"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Passo 7 — Testar a conexão
|
|
159
|
+
|
|
160
|
+
Após reiniciar o Claude Code com o `.mcp.json` configurado, peça ao agent:
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
Teste a conexão com o Confluence: liste as páginas filhas da page {Page ID do Input}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**IMPORTANTE — Como o Confluence funciona no Claude Code:**
|
|
167
|
+
|
|
168
|
+
O Confluence **NÃO é um comando de terminal**. Não existe `confluence` como CLI.
|
|
169
|
+
O acesso é feito via **MCP tools** — funções que o Claude Code expõe automaticamente
|
|
170
|
+
quando o server MCP está configurado no `.mcp.json`.
|
|
171
|
+
|
|
172
|
+
As tools disponíveis são prefixadas com `mcp__atlassian__confluence_`:
|
|
173
|
+
|
|
174
|
+
| Tool MCP | O que faz |
|
|
175
|
+
|----------|-----------|
|
|
176
|
+
| `mcp__atlassian__confluence_get_page` | Lê conteúdo de uma page por ID |
|
|
177
|
+
| `mcp__atlassian__confluence_get_page_children` | Lista filhos diretos de uma page |
|
|
178
|
+
| `mcp__atlassian__confluence_create_page` | Cria page nova |
|
|
179
|
+
| `mcp__atlassian__confluence_update_page` | Atualiza conteúdo de page existente |
|
|
180
|
+
| `mcp__atlassian__confluence_search` | Busca por texto no space |
|
|
181
|
+
| `mcp__atlassian__confluence_get_labels` | Lê labels de uma page |
|
|
182
|
+
| `mcp__atlassian__confluence_get_attachments` | Lista attachments de uma page |
|
|
183
|
+
| `mcp__atlassian__confluence_download_attachment` | Baixa attachment |
|
|
184
|
+
|
|
185
|
+
O agent chama essas tools diretamente — **nunca via bash/terminal**.
|
|
186
|
+
Se o agent tentar rodar `confluence ...` no terminal, está errado.
|
|
187
|
+
|
|
188
|
+
**Como verificar que o MCP está funcionando:**
|
|
189
|
+
1. Reinicie o Claude Code após criar/editar `.mcp.json`
|
|
190
|
+
2. Peça ao agent: "Use a tool `mcp__atlassian__confluence_get_page` com page_id {ID}"
|
|
191
|
+
3. Se funcionar, está conectado. Se der "tool not found", o MCP não subiu — verifique `.mcp.json`
|
|
192
|
+
|
|
193
|
+
**Nota**: `get_page_children` retorna apenas filhos **diretos** (não recursivo).
|
|
194
|
+
Para trazer toda a árvore, o agent faz loop: chama `get_page_children` em cada
|
|
195
|
+
filho que tem `hasChildren`, empilha e desce. O `/load` já faz isso automaticamente.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Setup 2: 100% Local (sem Confluence)
|
|
200
|
+
|
|
201
|
+
Nenhuma instalação extra necessária. Não precisa de `.mcp.json`.
|
|
202
|
+
|
|
203
|
+
### Passo 1 — Configurar `sfw.config.yml`
|
|
204
|
+
|
|
205
|
+
```yaml
|
|
206
|
+
project:
|
|
207
|
+
name: "Meu Projeto"
|
|
208
|
+
|
|
209
|
+
naming:
|
|
210
|
+
output_container: "out_{scope}"
|
|
211
|
+
output_artifact: "{type}" # sem {scope} no nome — sem risco de colisão local
|
|
212
|
+
|
|
213
|
+
input:
|
|
214
|
+
adapter: filesystem
|
|
215
|
+
config:
|
|
216
|
+
root_path: "workspace/Input"
|
|
217
|
+
cache:
|
|
218
|
+
local_dir: "workspace/Input/"
|
|
219
|
+
log: ".ai/load-log.md"
|
|
220
|
+
incremental: true
|
|
221
|
+
|
|
222
|
+
output:
|
|
223
|
+
targets:
|
|
224
|
+
- name: local
|
|
225
|
+
adapter: filesystem
|
|
226
|
+
config:
|
|
227
|
+
root_path: "workspace/Output"
|
|
228
|
+
publishes: [PRD, TRD, SDD, Progresso]
|
|
229
|
+
mode: auto
|
|
230
|
+
conflict_detection: hash
|
|
231
|
+
approval_mechanism: none
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Passo 2 — Criar pastas de Input
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
mkdir -p workspace/Input/meu_escopo
|
|
238
|
+
# Adicione seus arquivos de insumo na pasta
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Pronto. `/new-project meu_escopo` funciona direto.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Ligar e desligar Confluence (sem mudar nada estrutural)
|
|
246
|
+
|
|
247
|
+
### Desligar output pro Confluence (manter input)
|
|
248
|
+
|
|
249
|
+
Mude `mode` no target:
|
|
250
|
+
|
|
251
|
+
```yaml
|
|
252
|
+
output:
|
|
253
|
+
targets:
|
|
254
|
+
- name: confluence-mirror
|
|
255
|
+
mode: off # ← era "auto", agora "off"
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Pipeline roda normal, artefatos ficam locais. Quando quiser religar, volte pra `auto`.
|
|
259
|
+
|
|
260
|
+
### Desligar tudo (input + output) — virar 100% local
|
|
261
|
+
|
|
262
|
+
Mude o adapter do input:
|
|
263
|
+
|
|
264
|
+
```yaml
|
|
265
|
+
input:
|
|
266
|
+
adapter: filesystem # ← era "confluence"
|
|
267
|
+
config:
|
|
268
|
+
root_path: "workspace/Input"
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
E coloque `mode: off` nos targets de Confluence.
|
|
272
|
+
|
|
273
|
+
### Adicionar Confluence depois (começou local, quer publicar)
|
|
274
|
+
|
|
275
|
+
1. Siga os passos 1-5 do Setup Confluence
|
|
276
|
+
2. Adicione o target no `sfw.config.yml`:
|
|
277
|
+
```yaml
|
|
278
|
+
output:
|
|
279
|
+
targets:
|
|
280
|
+
- name: confluence-mirror
|
|
281
|
+
adapter: confluence
|
|
282
|
+
config:
|
|
283
|
+
space_key: "ST"
|
|
284
|
+
parent_page_id: "294931"
|
|
285
|
+
publishes: [PRD, TRD, SDD, Progresso]
|
|
286
|
+
mode: auto
|
|
287
|
+
```
|
|
288
|
+
3. Re-rode `/extract`, `/design`, `/plan` — os artefatos locais serão publicados
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Troubleshooting
|
|
293
|
+
|
|
294
|
+
| Problema | Solução |
|
|
295
|
+
|----------|---------|
|
|
296
|
+
| MCP server não aparece no Claude Code | Reiniciar Claude Code. MCP só carrega no startup |
|
|
297
|
+
| `${VAR}` não resolve no `.mcp.json` | Hardcode as credenciais direto. Variáveis de ambiente não funcionam |
|
|
298
|
+
| "401 Unauthorized" | Verifique email + token no `.mcp.json`. Token pode ter expirado |
|
|
299
|
+
| "403 Forbidden" | Usuário sem permissão no space. Pedir acesso ao admin |
|
|
300
|
+
| "Page not found" (404) | Page ID errado. Confirme o ID via URL da page no Confluence |
|
|
301
|
+
| Primeira execução demora muito | Normal — uvx baixa ~118 deps na primeira vez |
|
|
302
|
+
| `BadRequestException: title already exists` | Title duplicado no space (unique per space no Confluence). Renomear o scope no Input |
|
|
303
|
+
| Edição no `.mcp.json` não faz efeito | Reiniciar Claude Code obrigatório |
|
|
304
|
+
| Content no Confluence parece diferente do local | Markdown roundtrip normaliza (`#`→`===`, `-`→`*`). Normal — version number é fonte de verdade, não bytes |
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Referências
|
|
309
|
+
|
|
310
|
+
- Interface do adapter: `.github/adapters/interface.md`
|
|
311
|
+
- ConfluenceAdapter: `.github/adapters/confluence.md`
|
|
312
|
+
- FilesystemAdapter: `.github/adapters/filesystem.md`
|
|
313
|
+
- Erros: `.github/adapters/errors.md`
|
|
314
|
+
- Naming: `.github/adapters/naming.md`
|
|
@@ -54,16 +54,38 @@ Não valida que `space_key` existe no Confluence — isso acontece no primeiro
|
|
|
54
54
|
|
|
55
55
|
**MCP**: `mcp__atlassian__confluence_get_page_children`
|
|
56
56
|
|
|
57
|
+
> **ATENÇÃO**: `get_page_children` retorna apenas filhos **diretos** — NÃO é recursivo.
|
|
58
|
+
> Para trazer a árvore inteira, o caller (ex: `/load`) deve implementar recursão manual.
|
|
59
|
+
|
|
57
60
|
**Passos**:
|
|
58
|
-
1. Chamar tool com `
|
|
61
|
+
1. Chamar tool com `page_id: containerId`
|
|
59
62
|
2. Para cada filho retornado, montar `Item`:
|
|
60
63
|
- `id` = `page.id` (string)
|
|
61
64
|
- `title` = `page.title`
|
|
62
|
-
- `version` = `String(page.version.number)`
|
|
65
|
+
- `version` = `String(page.version.number)` (se disponível, senão chamar `get_page` pra obter)
|
|
63
66
|
- `type` = `"page"` (Confluence só tem páginas — folders são páginas vazias convencionadas)
|
|
64
|
-
- `hasChildren` =
|
|
65
|
-
3. Se retorno paginado
|
|
66
|
-
4. Retornar array
|
|
67
|
+
- `hasChildren` = verificar via `get_page_children` no filho ou assumir `true` por default
|
|
68
|
+
3. Se retorno paginado, iterar até esgotar
|
|
69
|
+
4. Retornar array (apenas filhos diretos — 1 nível)
|
|
70
|
+
|
|
71
|
+
**Recursão (obrigatória pro `/load` com `recursive: true`)**:
|
|
72
|
+
|
|
73
|
+
O `/load` precisa de toda a árvore. Padrão:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
function listChildrenRecursive(containerId):
|
|
77
|
+
filhos = listChildren(containerId) // 1 nível
|
|
78
|
+
resultado = [...filhos]
|
|
79
|
+
para cada filho em filhos:
|
|
80
|
+
se filho.type == "page": // tudo no Confluence é page
|
|
81
|
+
netos = listChildrenRecursive(filho.id)
|
|
82
|
+
resultado = [...resultado, ...netos]
|
|
83
|
+
retornar resultado
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Custo: 1 chamada MCP por nó da árvore. Uma árvore com 5 scopes × 3 filhos = ~20 chamadas.
|
|
87
|
+
É aceitável — MCP é local (uvx), não HTTP. Se ficar lento com árvores grandes,
|
|
88
|
+
considerar cache em `.ai/load-log.md` e re-scan incremental.
|
|
67
89
|
|
|
68
90
|
**Erros**:
|
|
69
91
|
- `404` → `NotFoundError { kind: "container", itemId: containerId }`
|
|
@@ -20,10 +20,11 @@
|
|
|
20
20
|
|
|
21
21
|
### 2. Validar acesso a todas skills
|
|
22
22
|
|
|
23
|
-
Verificar que TODAS as
|
|
23
|
+
Verificar que TODAS as 10 skills estão acessíveis:
|
|
24
24
|
|
|
25
25
|
| Skill | Caminho |
|
|
26
26
|
|-------|---------|
|
|
27
|
+
| `/sf-load` | `.github/skills/sf-load/SKILL.md` |
|
|
27
28
|
| `/sf-new-project` | `.github/skills/sf-new-project/SKILL.md` |
|
|
28
29
|
| `/sf-feature` | `.github/skills/sf-feature/SKILL.md` |
|
|
29
30
|
| `/sf-extract` | `.github/skills/sf-extract/SKILL.md` |
|
|
@@ -76,8 +77,9 @@ Nenhum código é escrito sem especificação aprovada (SDD).
|
|
|
76
77
|
## Pipeline do Workflow
|
|
77
78
|
|
|
78
79
|
```
|
|
79
|
-
workspace/Input/
|
|
80
|
-
↓
|
|
80
|
+
/sf-load <nome> → puxa insumos do backend (Confluence/filesystem) → workspace/Input/{nome}/
|
|
81
|
+
↓
|
|
82
|
+
/sf-new-project <nome> (TRD — bootstrap técnico) ou /sf-feature <nome> (PRD — feature)
|
|
81
83
|
/sf-discovery → análise profunda dos insumos (RECOMENDADO, opcional)
|
|
82
84
|
↓
|
|
83
85
|
/sf-extract → PRD ou TRD em workspace/Output/{nome}/ (checkpoint — usuário revisa e aprova)
|
|
@@ -94,6 +96,7 @@ workspace/Input/ (qualquer arquivo)
|
|
|
94
96
|
|
|
95
97
|
| Skill | Tipo | O que faz |
|
|
96
98
|
|-------|------|-----------|
|
|
99
|
+
| `/sf-load <nome>` | Utilitária | Puxa insumos do backend (Confluence/filesystem) para workspace/Input/{nome}/. Incremental |
|
|
97
100
|
| `/sf-new-project <nome>` | Orquestrador | Bootstrap técnico: cria .context.md tipo TRD, chama /sf-extract, para no checkpoint |
|
|
98
101
|
| `/sf-feature <nome>` | Orquestrador | Feature: cria .context.md tipo PRD, chama /sf-extract, para no checkpoint |
|
|
99
102
|
| `/sf-extract <nome>` | Atômica | Lê workspace/Input/{nome}/ → gera PRD ou TRD. Re-executável para novos insumos |
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# /sf-load <nome>
|
|
2
|
+
|
|
3
|
+
Puxa os insumos de um scope do backend configurado (Confluence, filesystem, etc.)
|
|
4
|
+
e materializa em `workspace/Input/{nome}/` no projeto local.
|
|
5
|
+
|
|
6
|
+
## Argumento
|
|
7
|
+
|
|
8
|
+
`<nome>` = nome do item no backend de input (título da page no Confluence,
|
|
9
|
+
nome da pasta no filesystem). Match exato — se não encontrar, para com erro.
|
|
10
|
+
|
|
11
|
+
## Quando usar
|
|
12
|
+
|
|
13
|
+
- **Antes de `/sf-new-project <nome>` ou `/sf-feature <nome>`** — para trazer
|
|
14
|
+
insumos que o PM publicou no Confluence (ou outro backend)
|
|
15
|
+
- **Após o PM adicionar novos insumos** (ex: `ajustes_v1` como page filha) —
|
|
16
|
+
para re-sincronizar. O log incremental detecta NOVO/MODIFICADO/INALTERADO
|
|
17
|
+
- **Não obrigatório** se o usuário já colocou insumos direto em `workspace/Input/{nome}/`
|
|
18
|
+
|
|
19
|
+
## Pré-condições
|
|
20
|
+
|
|
21
|
+
| # | Validação | Se falhar |
|
|
22
|
+
|---|-----------|-----------|
|
|
23
|
+
| 1 | `sfw.config.yml` existe na raiz do projeto | Parar → "Copie sfw.config.yml.example para sfw.config.yml e configure. Veja .github/adapters/SETUP.md" |
|
|
24
|
+
| 2 | `sfw.config.yml` tem seção `input` com `adapter` e `config` | Parar → "Seção input não encontrada no sfw.config.yml" |
|
|
25
|
+
| 3 | Adapter do input está acessível (MCP rodando pra Confluence, pasta existe pra filesystem) | Parar → orientar setup conforme `.github/adapters/SETUP.md` |
|
|
26
|
+
|
|
27
|
+
## Execução
|
|
28
|
+
|
|
29
|
+
### 1. Carregar configuração
|
|
30
|
+
|
|
31
|
+
Ler `sfw.config.yml` e extrair:
|
|
32
|
+
- `input.adapter` — tipo do adapter (`confluence` ou `filesystem`)
|
|
33
|
+
- `input.config` — config específica do adapter
|
|
34
|
+
- `input.cache.local_dir` — onde materializar (default: `workspace/Input/`)
|
|
35
|
+
- `input.cache.log` — arquivo de log (default: `.ai/sf-load-log.md`)
|
|
36
|
+
- `input.cache.incremental` — se compara hashes antes de sobrescrever
|
|
37
|
+
- `naming.output_container` — template pra nomear a pasta local (usa `{scope}`)
|
|
38
|
+
|
|
39
|
+
### 2. Buscar o scope no backend
|
|
40
|
+
|
|
41
|
+
**Para Confluence** (`adapter: confluence`):
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
1. Chamar mcp__atlassian__confluence_get_page_children
|
|
45
|
+
com page_id = config.parent_page_id
|
|
46
|
+
2. Na lista de filhos, buscar o que tem title == {nome} (match exato)
|
|
47
|
+
3. Se não encontrou → ERRO: "Scope '{nome}' não encontrado no Confluence
|
|
48
|
+
(parent_page_id: {id}). Verifique se a page existe como filha de Input."
|
|
49
|
+
4. Anotar scope_id = page.id
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Para Filesystem** (`adapter: filesystem`):
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
1. Listar conteúdo de config.root_path
|
|
56
|
+
2. Buscar pasta ou arquivo com nome == {nome}
|
|
57
|
+
3. Se não encontrou → ERRO: "Scope '{nome}' não encontrado em {root_path}"
|
|
58
|
+
4. Anotar scope_path
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 3. Trazer conteúdo recursivamente
|
|
62
|
+
|
|
63
|
+
**Para Confluence**:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
function loadScope(pageId, localDir):
|
|
67
|
+
// Baixar conteúdo da page-mãe
|
|
68
|
+
page = mcp__atlassian__confluence_get_page(page_id=pageId, convert_to_markdown=true)
|
|
69
|
+
salvar page.content em {localDir}/_parent.md (ou {titulo_sanitizado}.md)
|
|
70
|
+
registrar no load-log: pageId, title, version, sha256(content)
|
|
71
|
+
|
|
72
|
+
// Baixar attachments (se include_attachments=true)
|
|
73
|
+
attachments = mcp__atlassian__confluence_get_attachments(page_id=pageId)
|
|
74
|
+
para cada attachment:
|
|
75
|
+
bytes = mcp__atlassian__confluence_download_attachment(page_id=pageId, filename=attachment.title)
|
|
76
|
+
salvar em {localDir}/attachments/{filename}
|
|
77
|
+
registrar no load-log
|
|
78
|
+
|
|
79
|
+
// Recursão nos filhos
|
|
80
|
+
children = mcp__atlassian__confluence_get_page_children(page_id=pageId)
|
|
81
|
+
para cada child:
|
|
82
|
+
childDir = {localDir}/{sanitize(child.title)}
|
|
83
|
+
mkdir childDir
|
|
84
|
+
loadScope(child.id, childDir) // recursão
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Para Filesystem**:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
function loadScope(sourcePath, localDir):
|
|
91
|
+
// Se source == localDir → no-op (input já é local)
|
|
92
|
+
// Senão: copiar recursivamente sourcePath → localDir
|
|
93
|
+
// Calcular sha256 de cada arquivo pra load-log
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 4. Log incremental (`.ai/sf-load-log.md`)
|
|
97
|
+
|
|
98
|
+
Formato append-only:
|
|
99
|
+
|
|
100
|
+
```markdown
|
|
101
|
+
## Load: {nome} — {ISO_DATETIME}
|
|
102
|
+
|
|
103
|
+
| Item ID | Title | Version | SHA256 | Local Path | Status |
|
|
104
|
+
|---------|-------|---------|--------|------------|--------|
|
|
105
|
+
| 294950 | setup_projeto | 3 | a3f5... | workspace/Input/app_barbearia/_parent.md | NOVO |
|
|
106
|
+
| 557057 | brief fullstack | 1 | 9fc3... | workspace/Input/app_barbearia/brief_fullstack.md | NOVO |
|
|
107
|
+
| att:diagram.png | diagram.png | — | 7d2e... | workspace/Input/app_barbearia/attachments/diagram.png | NOVO |
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Se `incremental: true`** (default):
|
|
111
|
+
- Antes de baixar, comparar hash atual com hash no log anterior
|
|
112
|
+
- Se hash idêntico → status `INALTERADO`, não sobrescrever
|
|
113
|
+
- Se hash diferente → status `MODIFICADO`, sobrescrever
|
|
114
|
+
- Se não existia no log → status `NOVO`
|
|
115
|
+
- Arquivo que existia no log mas sumiu do backend → status `REMOVIDO` (log only, não deleta local)
|
|
116
|
+
|
|
117
|
+
### 5. Informar o usuário
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
Insumos carregados em workspace/Input/{nome}/
|
|
121
|
+
|
|
122
|
+
{N} arquivos ({X novos, Y modificados, Z inalterados})
|
|
123
|
+
{M} attachments
|
|
124
|
+
|
|
125
|
+
Próximo passo:
|
|
126
|
+
/sf-new-project {nome} ← se é bootstrap técnico (gera TRD)
|
|
127
|
+
/sf-feature {nome} ← se é feature (gera PRD)
|
|
128
|
+
|
|
129
|
+
Log salvo em .ai/sf-load-log.md
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Notas
|
|
133
|
+
|
|
134
|
+
- `/sf-load` é **idempotente** — rodar N vezes com mesmos insumos no backend
|
|
135
|
+
não muda nada (hashes batem, status = INALTERADO)
|
|
136
|
+
- Se o PM adicionar page `ajustes_v1` como filha do scope no Confluence,
|
|
137
|
+
re-rodar `/sf-load {nome}` traz como NOVO automaticamente
|
|
138
|
+
- O `/sf-load` **nunca modifica o backend** — só lê
|
|
139
|
+
- O `/sf-load` **nunca deleta arquivos locais** — se algo sumiu do backend,
|
|
140
|
+
marca como REMOVIDO no log mas mantém o arquivo local
|
|
141
|
+
- Attachments são salvos em subpasta `attachments/` dentro do scope
|
|
142
|
+
- Nomes de arquivo são sanitizados: espaços → `_`, caracteres especiais removidos
|
|
143
|
+
|
|
144
|
+
## Erros
|
|
145
|
+
|
|
146
|
+
| Erro | Ação |
|
|
147
|
+
|------|------|
|
|
148
|
+
| `sfw.config.yml` não existe | Parar, orientar setup |
|
|
149
|
+
| Adapter não configurado | Parar, apontar pra SETUP.md |
|
|
150
|
+
| MCP não disponível (Confluence) | Parar → "Reinicie o Claude Code. MCP carrega no startup" |
|
|
151
|
+
| Scope não encontrado no backend | Parar, listar scopes disponíveis se possível |
|
|
152
|
+
| Auth error (401/403) | Parar → "Verifique credenciais no .mcp.json" |
|
|
153
|
+
| Page sem conteúdo | OK — salvar arquivo vazio, registrar no log |
|
|
154
|
+
|
|
155
|
+
## Referências
|
|
156
|
+
|
|
157
|
+
- Adapter interface: `.github/adapters/interface.md`
|
|
158
|
+
- Confluence adapter: `.github/adapters/confluence.md`
|
|
159
|
+
- Filesystem adapter: `.github/adapters/filesystem.md`
|
|
160
|
+
- Setup guide: `.github/adapters/SETUP.md`
|
|
161
|
+
- Naming: `.github/adapters/naming.md`
|