spec-first-claude 0.6.0-beta.7 → 0.6.0-beta.8
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
|
@@ -8,6 +8,37 @@
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
## 0.6.0-beta.8 (2026-04-13)
|
|
12
|
+
|
|
13
|
+
### Novo: `bootstrap-confluence.js` (opcional, pré-Claude)
|
|
14
|
+
|
|
15
|
+
Script Node cross-platform em `.claude/scripts/bootstrap-confluence.js` que prepara
|
|
16
|
+
o ambiente para o backend Confluence **antes** de abrir o Claude Code. Evita o ciclo
|
|
17
|
+
"abrir Claude → descobrir que falta `uvx` ou credenciais → sair, ajustar, reiniciar".
|
|
18
|
+
|
|
19
|
+
O que faz:
|
|
20
|
+
1. Valida `uvx` instalado (orienta install se faltar — não instala automaticamente)
|
|
21
|
+
2. Coleta URL do Confluence, email Atlassian e API Token (com link pra gerar o token)
|
|
22
|
+
3. Gera/mescla `.mcp.json` preservando outras entries de MCP existentes
|
|
23
|
+
4. Garante `.mcp.json` no `.gitignore`
|
|
24
|
+
5. Pré-cacheia o pacote `mcp-atlassian` via `uvx` (100+ deps no primeiro uso)
|
|
25
|
+
|
|
26
|
+
Depois é só abrir o Claude Code e rodar `/mcp confluence` — vai direto pra descoberta
|
|
27
|
+
da árvore no Confluence, sem reiniciar.
|
|
28
|
+
|
|
29
|
+
Uso: `node .claude/scripts/bootstrap-confluence.js`
|
|
30
|
+
Opcional — `/mcp confluence` sozinho também cobre esses passos, mas exige reiniciar
|
|
31
|
+
Claude Code após criar `.mcp.json`.
|
|
32
|
+
|
|
33
|
+
Skill `/mcp` ganhou nota referenciando o script como atalho recomendado.
|
|
34
|
+
|
|
35
|
+
### Primeira release sincronizada pelo `sync-kits`
|
|
36
|
+
|
|
37
|
+
Esta é a primeira release onde **toda a edição aconteceu só no kit Claude** e o script
|
|
38
|
+
`npm run sync-kits` propagou para o kit Copilot automaticamente. Fluxo validado.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
11
42
|
## 0.6.0-beta.7 (2026-04-13)
|
|
12
43
|
|
|
13
44
|
### Script de sync entre kits (interno, não afeta o user)
|
|
@@ -15,6 +15,18 @@ Setup interativo de backend externo. Idempotente — pode rodar múltiplas vezes
|
|
|
15
15
|
/mcp confluence test ← só testa conexão
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
> **Dica**: para evitar o ciclo "abrir Claude → descobrir que falta uvx ou credenciais →
|
|
19
|
+
> sair, ajustar, reiniciar", há um script opcional que prepara o ambiente antes:
|
|
20
|
+
>
|
|
21
|
+
> ```bash
|
|
22
|
+
> node .claude/scripts/bootstrap-confluence.js
|
|
23
|
+
> ```
|
|
24
|
+
>
|
|
25
|
+
> Ele valida `uvx`, coleta credenciais, gera `.mcp.json` e pré-cacheia o MCP. Depois
|
|
26
|
+
> você abre o Claude Code e `/mcp confluence` só descobre a árvore do projeto. Uso
|
|
27
|
+
> opcional — `/mcp confluence` sozinho também cobre esses passos, mas demanda reiniciar
|
|
28
|
+
> o Claude após gerar `.mcp.json` (MCPs só sobem no startup).
|
|
29
|
+
|
|
18
30
|
---
|
|
19
31
|
|
|
20
32
|
## Provider: Confluence
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bootstrap-confluence.js — prepara o ambiente para o backend Confluence do SFW.
|
|
3
|
+
//
|
|
4
|
+
// Propósito: tirar fricção ANTES de abrir Claude Code. Depois de rodar este script,
|
|
5
|
+
// o /mcp confluence vai só descobrir a árvore do projeto no Confluence — credenciais
|
|
6
|
+
// e dependências já estarão prontas.
|
|
7
|
+
//
|
|
8
|
+
// Uso:
|
|
9
|
+
// node .claude/scripts/bootstrap-confluence.js
|
|
10
|
+
//
|
|
11
|
+
// O que faz:
|
|
12
|
+
// 1. Verifica se `uvx` está instalado (roda o MCP Atlassian)
|
|
13
|
+
// 2. Coleta credenciais Atlassian interativamente (email + token + URL)
|
|
14
|
+
// 3. Gera/mescla `.mcp.json` com entry `atlassian` (preserva outras entries)
|
|
15
|
+
// 4. Garante que `.mcp.json` está no `.gitignore`
|
|
16
|
+
// 5. Pré-cacheia `uvx mcp-atlassian` (evita delay no primeiro boot do Claude)
|
|
17
|
+
//
|
|
18
|
+
// O que NÃO faz (fica pro /mcp confluence dentro do Claude):
|
|
19
|
+
// - Validar credenciais contra o servidor
|
|
20
|
+
// - Descobrir páginas root do projeto
|
|
21
|
+
// - Gerar `sfw.config.yml`
|
|
22
|
+
|
|
23
|
+
const fs = require("fs");
|
|
24
|
+
const path = require("path");
|
|
25
|
+
const readline = require("readline");
|
|
26
|
+
const { execSync, spawn } = require("child_process");
|
|
27
|
+
|
|
28
|
+
const CWD = process.cwd();
|
|
29
|
+
|
|
30
|
+
function log(msg) {
|
|
31
|
+
console.log(msg);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function err(msg) {
|
|
35
|
+
console.error(`✗ ${msg}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function ok(msg) {
|
|
39
|
+
console.log(`✓ ${msg}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function hr() {
|
|
43
|
+
console.log("─".repeat(60));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function prompt(question) {
|
|
47
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
rl.question(question, (answer) => {
|
|
50
|
+
rl.close();
|
|
51
|
+
resolve(answer.trim());
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function hasCommand(cmd) {
|
|
57
|
+
try {
|
|
58
|
+
execSync(`${cmd} --version`, { stdio: "ignore" });
|
|
59
|
+
return true;
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function loadJson(filePath) {
|
|
66
|
+
if (!fs.existsSync(filePath)) return null;
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
69
|
+
} catch (e) {
|
|
70
|
+
err(`JSON inválido em ${filePath}: ${e.message}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function saveJson(filePath, data) {
|
|
76
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf8");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function ensureGitignore(entry) {
|
|
80
|
+
const giPath = path.join(CWD, ".gitignore");
|
|
81
|
+
let content = "";
|
|
82
|
+
if (fs.existsSync(giPath)) {
|
|
83
|
+
content = fs.readFileSync(giPath, "utf8");
|
|
84
|
+
const lines = content.split(/\r?\n/).map((l) => l.trim());
|
|
85
|
+
if (lines.includes(entry)) return false; // já tem
|
|
86
|
+
}
|
|
87
|
+
const sep = content && !content.endsWith("\n") ? "\n" : "";
|
|
88
|
+
fs.appendFileSync(giPath, `${sep}${entry}\n`, "utf8");
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function validateUrl(url) {
|
|
93
|
+
return /^https:\/\/[\w.-]+\.atlassian\.net\/wiki\/?$/.test(url);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function validateEmail(email) {
|
|
97
|
+
return /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ---- Main flow ----
|
|
101
|
+
|
|
102
|
+
(async function main() {
|
|
103
|
+
log("");
|
|
104
|
+
log("SFW — Bootstrap Confluence");
|
|
105
|
+
hr();
|
|
106
|
+
|
|
107
|
+
// 1. Verificar uvx
|
|
108
|
+
log("1. Verificando dependências...");
|
|
109
|
+
if (!hasCommand("uvx")) {
|
|
110
|
+
err("`uvx` não encontrado no PATH.");
|
|
111
|
+
log("");
|
|
112
|
+
log(" Instale com:");
|
|
113
|
+
log(" pip install uv (Python 3.10+ necessário)");
|
|
114
|
+
log("");
|
|
115
|
+
log(" Ou via Homebrew (macOS):");
|
|
116
|
+
log(" brew install uv");
|
|
117
|
+
log("");
|
|
118
|
+
log(" Depois rode este script novamente.");
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
ok("uvx disponível");
|
|
122
|
+
|
|
123
|
+
// 2. Coletar credenciais
|
|
124
|
+
log("");
|
|
125
|
+
log("2. Credenciais Atlassian");
|
|
126
|
+
log("");
|
|
127
|
+
log(" → URL do Confluence: abra seu Confluence no browser e copie a URL base.");
|
|
128
|
+
log(" Ex: https://empresa.atlassian.net/wiki");
|
|
129
|
+
log("");
|
|
130
|
+
let url = "";
|
|
131
|
+
while (!validateUrl(url)) {
|
|
132
|
+
url = await prompt(" URL: ");
|
|
133
|
+
if (!validateUrl(url)) {
|
|
134
|
+
err(`URL inválida. Formato esperado: https://{empresa}.atlassian.net/wiki`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (url.endsWith("/")) url = url.slice(0, -1);
|
|
138
|
+
|
|
139
|
+
log("");
|
|
140
|
+
log(" → Email da conta Atlassian (o que você usa pra logar).");
|
|
141
|
+
log("");
|
|
142
|
+
let email = "";
|
|
143
|
+
while (!validateEmail(email)) {
|
|
144
|
+
email = await prompt(" Email: ");
|
|
145
|
+
if (!validateEmail(email)) err("Email inválido.");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
log("");
|
|
149
|
+
log(" → API Token.");
|
|
150
|
+
log(" Gere em: https://id.atlassian.com/manage-profile/security/api-tokens");
|
|
151
|
+
log(" Clique em 'Create API token' → dê um nome (ex: 'sfw-local') → copie agora");
|
|
152
|
+
log(" (o token só aparece uma vez, depois some).");
|
|
153
|
+
log("");
|
|
154
|
+
let token = "";
|
|
155
|
+
while (!token) {
|
|
156
|
+
token = await prompt(" Token: ");
|
|
157
|
+
if (!token) err("Token não pode estar vazio.");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// 3. Gerar/mesclar .mcp.json
|
|
161
|
+
log("");
|
|
162
|
+
log("3. Gerando .mcp.json...");
|
|
163
|
+
const mcpPath = path.join(CWD, ".mcp.json");
|
|
164
|
+
const existing = loadJson(mcpPath) || {};
|
|
165
|
+
if (!existing.mcpServers) existing.mcpServers = {};
|
|
166
|
+
|
|
167
|
+
const hadEntry = !!existing.mcpServers.atlassian;
|
|
168
|
+
existing.mcpServers.atlassian = {
|
|
169
|
+
command: "uvx",
|
|
170
|
+
args: ["mcp-atlassian"],
|
|
171
|
+
env: {
|
|
172
|
+
CONFLUENCE_URL: url,
|
|
173
|
+
CONFLUENCE_USERNAME: email,
|
|
174
|
+
CONFLUENCE_API_TOKEN: token,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
saveJson(mcpPath, existing);
|
|
178
|
+
ok(`.mcp.json ${hadEntry ? "atualizado" : "criado"} (entry: atlassian)`);
|
|
179
|
+
|
|
180
|
+
// 4. Garantir .gitignore
|
|
181
|
+
const added = ensureGitignore(".mcp.json");
|
|
182
|
+
if (added) ok(".mcp.json adicionado ao .gitignore");
|
|
183
|
+
else ok(".mcp.json já estava no .gitignore");
|
|
184
|
+
|
|
185
|
+
// 5. Pré-cachear MCP
|
|
186
|
+
log("");
|
|
187
|
+
log("4. Pré-cacheando mcp-atlassian (baixa 100+ deps na primeira vez, pode demorar 1-3min)...");
|
|
188
|
+
await new Promise((resolve) => {
|
|
189
|
+
const child = spawn("uvx", ["mcp-atlassian", "--help"], { stdio: "ignore", shell: true });
|
|
190
|
+
const timeout = setTimeout(() => {
|
|
191
|
+
child.kill();
|
|
192
|
+
log(" (timeout após 5min — segue adiante, Claude pode retomar o download)");
|
|
193
|
+
resolve();
|
|
194
|
+
}, 5 * 60 * 1000);
|
|
195
|
+
child.on("close", (code) => {
|
|
196
|
+
clearTimeout(timeout);
|
|
197
|
+
if (code === 0) ok("mcp-atlassian em cache local");
|
|
198
|
+
else log(` (uvx retornou código ${code} — pode ser normal, --help às vezes sai != 0)`);
|
|
199
|
+
resolve();
|
|
200
|
+
});
|
|
201
|
+
child.on("error", (e) => {
|
|
202
|
+
clearTimeout(timeout);
|
|
203
|
+
err(`Falha ao rodar uvx: ${e.message}`);
|
|
204
|
+
resolve();
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Final
|
|
209
|
+
log("");
|
|
210
|
+
hr();
|
|
211
|
+
ok("Bootstrap concluído.");
|
|
212
|
+
log("");
|
|
213
|
+
log("Próximos passos:");
|
|
214
|
+
log(" 1. Abra o Claude Code neste diretório");
|
|
215
|
+
log(" 2. Rode: /mcp confluence");
|
|
216
|
+
log(" → vai descobrir a árvore do seu projeto no Confluence e gerar sfw.config.yml");
|
|
217
|
+
log("");
|
|
218
|
+
log("Se reiniciar o Claude Code for necessário (após este setup), ele vai pedir.");
|
|
219
|
+
log("");
|
|
220
|
+
})().catch((e) => {
|
|
221
|
+
err(`Erro inesperado: ${e.message}`);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
});
|