spec-first-copilot 0.6.0-beta.7 → 0.6.0-beta.9

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/bin/cli.js CHANGED
@@ -8,19 +8,19 @@ const args = process.argv.slice(2);
8
8
  const command = args[0];
9
9
 
10
10
  function printUsage() {
11
- console.log('Usage:');
12
- console.log(' spec-first-copilot init --name=<project-name> [--target=<path>]');
13
- console.log(' spec-first-copilot update [--target=<path>] [--force]');
11
+ console.log('Uso:');
12
+ console.log(' spec-first-copilot init --name=<nome-do-projeto> [--target=<caminho>]');
13
+ console.log(' spec-first-copilot update [--target=<caminho>] [--force]');
14
14
  console.log('');
15
- console.log('Commands:');
16
- console.log(' init Create a new spec-first project');
17
- console.log(' update Update an existing project with new framework files');
18
- console.log(' (adds missing files, updates framework files like skills/adapters/agents)');
15
+ console.log('Comandos:');
16
+ console.log(' init Cria um novo projeto spec-first');
17
+ console.log(' update Atualiza projeto existente com arquivos novos do framework');
18
+ console.log(' (adiciona faltantes, atualiza skills/adapters/agents/templates)');
19
19
  console.log('');
20
- console.log('Options:');
21
- console.log(' --name=<name> Project name (required for init)');
22
- console.log(' --target=<path> Target directory (defaults to ./<name> for init, cwd for update)');
23
- console.log(' --force Force update all files, not just framework files (use with caution)');
20
+ console.log('Opções:');
21
+ console.log(' --name=<nome> Nome do projeto (obrigatório no init)');
22
+ console.log(' --target=<path> Diretório destino (default: ./<nome> no init, cwd no update)');
23
+ console.log(' --force Força atualizar TODOS arquivos, não os do framework (cuidado)');
24
24
  }
25
25
 
26
26
  function parseArgs(args) {
@@ -40,7 +40,7 @@ if (command === 'init') {
40
40
  const opts = parseArgs(args.slice(1));
41
41
 
42
42
  if (!opts.name) {
43
- console.error('Error: --name is required.\n');
43
+ console.error('Erro: --name é obrigatório.\n');
44
44
  printUsage();
45
45
  process.exit(1);
46
46
  }
@@ -64,7 +64,7 @@ if (command === 'init') {
64
64
  } else {
65
65
  printUsage();
66
66
  if (command) {
67
- console.error(`\nUnknown command: "${command}"`);
67
+ console.error(`\nComando desconhecido: "${command}"`);
68
68
  }
69
69
  process.exit(command ? 1 : 0);
70
70
  }
package/lib/init.js CHANGED
@@ -42,19 +42,31 @@ function init({ name, templatesDir, targetDir }) {
42
42
  const dest = targetDir || path.resolve(process.cwd(), name);
43
43
 
44
44
  const existed = fs.existsSync(dest);
45
- console.log(`\n${existed ? 'Initializing' : 'Creating'} project "${name}" in ${dest}\n`);
45
+ console.log(`\n${existed ? 'Inicializando' : 'Criando'} projeto "${name}" em ${dest}\n`);
46
46
 
47
47
  const replacements = { [PLACEHOLDER]: name };
48
48
  copyDir(templatesDir, dest, replacements);
49
49
  patchProjectName(dest, name);
50
50
 
51
- console.log(`Done! Project "${name}" is ready.`);
52
- console.log('\nNext steps:');
51
+ console.log(`Pronto! Projeto "${name}" criado.`);
52
+ console.log('\nPróximos passos:');
53
53
  console.log(` 1. cd ${name}`);
54
- console.log(' 2. Create a folder in workspace/Input/ with your project files (e.g. workspace/Input/my_app/)');
55
- console.log(' 3. (Optional) Copy sfw.config.yml.example to sfw.config.yml and configure your backend');
56
- console.log(' 4. Open VS Code with Copilot Chat and run: /sf-start <folder-name>');
57
- console.log(' Framework detects first-run automatically (creates docs/) vs feature (updates docs/).');
54
+ console.log(' 2. Crie uma pasta em workspace/Input/ com os insumos do seu projeto');
55
+ console.log(' (ex: workspace/Input/meu_app/requisitos.md)');
56
+ console.log('');
57
+ console.log(' 3. (Opcional) Configure o backend de input/output:');
58
+ console.log(' cp sfw.config.yml.example sfw.config.yml');
59
+ console.log(' Sem isso, o framework roda 100% local (sem Confluence/etc.)');
60
+ console.log('');
61
+ console.log(' 4. (Opcional, se vai usar Confluence) Prepare o ambiente ANTES de abrir o VS Code:');
62
+ console.log(' node .github/scripts/bootstrap-confluence.js');
63
+ console.log(' Valida uvx, coleta credenciais, gera .mcp.json e pré-cacheia o MCP.');
64
+ console.log(' Evita o ciclo "abrir Copilot → faltar dep → reiniciar VS Code".');
65
+ console.log('');
66
+ console.log(' 5. Abra o VS Code com Copilot Chat e rode:');
67
+ console.log(' /sf-start <nome-da-pasta-em-Input>');
68
+ console.log(' O framework detecta first-run automaticamente (cria docs/) vs feature');
69
+ console.log(' (atualiza docs/ via merge).');
58
70
  console.log('');
59
71
  }
60
72
 
package/lib/update.js CHANGED
@@ -20,36 +20,36 @@ function update({ templatesDir, targetDir, force }) {
20
20
  const dest = targetDir || process.cwd();
21
21
 
22
22
  if (!fs.existsSync(dest)) {
23
- console.error(`Error: directory "${dest}" does not exist.`);
24
- console.error('Run "spec-first-copilot init --name=<name>" first to create a project.');
23
+ console.error(`Erro: diretório "${dest}" não existe.`);
24
+ console.error('Rode "spec-first-copilot init --name=<nome>" primeiro pra criar o projeto.');
25
25
  process.exit(1);
26
26
  }
27
27
 
28
28
  const githubDir = path.join(dest, '.github');
29
29
  if (!fs.existsSync(githubDir)) {
30
- console.error('Error: not a spec-first project (missing .github/ directory).');
31
- console.error('Run "spec-first-copilot init --name=<name>" first.');
30
+ console.error('Erro: não é um projeto spec-first (pasta .github/ não encontrada).');
31
+ console.error('Rode "spec-first-copilot init --name=<nome>" primeiro.');
32
32
  process.exit(1);
33
33
  }
34
34
 
35
- console.log(`\nUpdating project in ${dest}\n`);
35
+ console.log(`\nAtualizando projeto em ${dest}\n`);
36
36
 
37
37
  const stats = { added: [], updated: [], skipped: 0 };
38
38
  syncDir(templatesDir, dest, force, stats, dest);
39
39
 
40
40
  console.log('');
41
41
  if (stats.added.length > 0) {
42
- console.log(`Added (${stats.added.length}):`);
42
+ console.log(`Adicionados (${stats.added.length}):`);
43
43
  for (const f of stats.added) console.log(` + ${f}`);
44
44
  }
45
45
  if (stats.updated.length > 0) {
46
- console.log(`Updated (${stats.updated.length}):`);
46
+ console.log(`Atualizados (${stats.updated.length}):`);
47
47
  for (const f of stats.updated) console.log(` ~ ${f}`);
48
48
  }
49
49
  if (stats.added.length === 0 && stats.updated.length === 0) {
50
- console.log('Already up to date no new files to add.');
50
+ console.log(' está atualizadonenhum arquivo novo pra adicionar.');
51
51
  } else {
52
- console.log(`\n${stats.added.length} added, ${stats.updated.length} updated, ${stats.skipped} unchanged`);
52
+ console.log(`\n${stats.added.length} adicionado(s), ${stats.updated.length} atualizado(s), ${stats.skipped} inalterado(s)`);
53
53
  }
54
54
 
55
55
  console.log('');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-first-copilot",
3
- "version": "0.6.0-beta.7",
3
+ "version": "0.6.0-beta.9",
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"
@@ -8,6 +8,67 @@
8
8
 
9
9
  ---
10
10
 
11
+ ## 0.6.0-beta.9 (2026-04-13)
12
+
13
+ ### CLI em pt-BR + dica de bootstrap-confluence no init
14
+
15
+ Mensagens do CLI (`init` e `update`) traduzidas para pt-BR — usuário final é o público.
16
+
17
+ Mensagem do `init` reescrita para deixar **explícito o passo opcional de pré-setup
18
+ do Confluence**:
19
+
20
+ ```
21
+ Pronto! Projeto "X" criado.
22
+
23
+ Próximos passos:
24
+ 1. cd X
25
+ 2. Crie uma pasta em workspace/Input/ com os insumos do seu projeto
26
+ 3. (Opcional) Configure o backend de input/output:
27
+ cp sfw.config.yml.example sfw.config.yml
28
+ 4. (Opcional, se vai usar Confluence) Prepare o ambiente ANTES de abrir o VS Code:
29
+ node .github/scripts/bootstrap-confluence.js
30
+ 5. Abra o VS Code com Copilot Chat e rode: /sf-start <nome-da-pasta>
31
+ ```
32
+
33
+ O passo 4 era invisível antes — só aparecia inline na "Dica" do skill `/sf-mcp`.
34
+ Agora aparece logo no fim do `init`, no momento exato em que o usuário vai decidir
35
+ se prepara o Confluence ou não.
36
+
37
+ ---
38
+
39
+ ## 0.6.0-beta.8 (2026-04-13)
40
+
41
+ ### Novo: `bootstrap-confluence.js` (opcional, pré-Copilot)
42
+
43
+ Script Node cross-platform em `.github/scripts/bootstrap-confluence.js` que prepara
44
+ o ambiente para o backend Confluence **antes** de abrir o VS Code com Copilot. Evita
45
+ o ciclo "abrir Copilot → descobrir que falta `uvx` ou credenciais → sair, ajustar,
46
+ reiniciar".
47
+
48
+ O que faz:
49
+ 1. Valida `uvx` instalado (orienta install se faltar — não instala automaticamente)
50
+ 2. Coleta URL do Confluence, email Atlassian e API Token (com link pra gerar o token)
51
+ 3. Gera/mescla `.mcp.json` preservando outras entries de MCP existentes
52
+ 4. Garante `.mcp.json` no `.gitignore`
53
+ 5. Pré-cacheia o pacote `mcp-atlassian` via `uvx` (100+ deps no primeiro uso)
54
+
55
+ Depois é só abrir o VS Code e rodar `/sf-mcp confluence` — vai direto pra descoberta
56
+ da árvore no Confluence, sem reiniciar.
57
+
58
+ Uso: `node .github/scripts/bootstrap-confluence.js`
59
+ Opcional — `/sf-mcp confluence` sozinho também cobre esses passos, mas exige reiniciar
60
+ VS Code após criar `.mcp.json`.
61
+
62
+ Skill `/sf-mcp` ganhou nota referenciando o script como atalho recomendado.
63
+
64
+ ### Primeira release sincronizada automaticamente
65
+
66
+ Primeira release onde o conteúdo compartilhado entre kits veio direto do script
67
+ `npm run sync-kits` no repo SFW — sem cópia manual. Zero drift entre Claude e Copilot
68
+ validado antes do publish.
69
+
70
+ ---
71
+
11
72
  ## 0.6.0-beta.7 (2026-04-13)
12
73
 
13
74
  ### Fix: 27 referências a slash-commands faltando prefix `sf-`
@@ -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 /sf-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 .github/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 /sf-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: /sf-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
+ });
@@ -15,6 +15,18 @@ Setup interativo de backend externo. Idempotente — pode rodar múltiplas vezes
15
15
  /sf-mcp confluence test ← só testa conexão
16
16
  ```
17
17
 
18
+ > **Dica**: para evitar o ciclo "abrir VS Code → descobrir que falta uvx ou credenciais →
19
+ > sair, ajustar, reiniciar", há um script opcional que prepara o ambiente antes:
20
+ >
21
+ > ```bash
22
+ > node .github/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 Copilot Chat e `/sf-mcp confluence` só descobre a árvore do projeto. Uso
27
+ > opcional — `/sf-mcp confluence` sozinho também cobre esses passos, mas demanda reiniciar
28
+ > o VS Code após gerar `.mcp.json` (MCPs só sobem no startup).
29
+
18
30
  ---
19
31
 
20
32
  ## Provider: Confluence