spec-first-copilot 0.5.0-beta.5 → 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-first-copilot",
3
- "version": "0.5.0-beta.5",
3
+ "version": "0.5.0-beta.6",
4
4
  "description": "Spec-first workflow kit for GitHub Copilot — AI-driven development with specs, not guesswork",
5
5
  "bin": {
6
6
  "spec-first-copilot": "bin/cli.js"
@@ -160,12 +160,39 @@ output:
160
160
  Após reiniciar o Claude Code com o `.mcp.json` configurado, peça ao agent:
161
161
 
162
162
  ```
163
- Teste a conexão com o Confluence: liste as páginas filhas de {Page ID do Input}
163
+ Teste a conexão com o Confluence: liste as páginas filhas da page {Page ID do Input}
164
164
  ```
165
165
 
166
- O agent deve conseguir usar `confluence_get_page_children` e listar o conteúdo.
167
- Se der erro de auth → verifique email, token e URL no `.mcp.json`.
168
- Se o MCP não estiver disponível reinicie o Claude Code.
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.
169
196
 
170
197
  ---
171
198
 
@@ -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 `parent_id: containerId`, `limit: 100`, `expand: "version"`
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` = `page.children_count > 0` (se vier na resposta; senão `true` por default — custo de 1 chamada extra é baixo)
65
- 3. Se retorno paginado (cursor), iterar até esgotar
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 9 skills estão acessíveis:
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/ (qualquer arquivo)
80
- /sf-new-project <nome> (TRD — bootstrap técnico) ou /sf-feature <nome> (PRD — feature)
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`