@guilhermefsousa/open-spec-kit 0.0.9 → 0.0.11

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.
Files changed (28) hide show
  1. package/README.md +1 -1
  2. package/bin/open-spec-kit.js +7 -0
  3. package/package.json +1 -1
  4. package/src/commands/doctor.js +107 -197
  5. package/src/commands/init.js +112 -347
  6. package/src/commands/install.js +393 -0
  7. package/src/commands/update.js +117 -165
  8. package/src/schemas/spec.schema.js +3 -3
  9. package/src/utils/global-path.js +73 -0
  10. package/templates/agents/agents/spec-hub.agent.md +13 -13
  11. package/templates/agents/rules/hub_structure.instructions.md +1 -1
  12. package/templates/agents/rules/ownership.instructions.md +39 -39
  13. package/templates/agents/skills/dev-orchestrator/SKILL.md +17 -17
  14. package/templates/agents/skills/discovery/SKILL.md +17 -17
  15. package/templates/agents/skills/setup-project/SKILL.md +15 -15
  16. package/templates/agents/skills/specifying-features/SKILL.md +28 -28
  17. package/templates/github/agents/spec-hub.agent.md +5 -5
  18. package/templates/github/copilot-instructions.md +9 -9
  19. package/templates/github/instructions/hub_structure.instructions.md +1 -1
  20. package/templates/github/instructions/ownership.instructions.md +9 -9
  21. package/templates/github/skills/dev-orchestrator/SKILL.md +619 -5
  22. package/templates/github/skills/discovery/SKILL.md +419 -5
  23. package/templates/github/skills/setup-project/SKILL.md +496 -5
  24. package/templates/github/skills/specifying-features/SKILL.md +417 -5
  25. /package/templates/github/prompts/{dev.prompt.md → osk-build.prompt.md} +0 -0
  26. /package/templates/github/prompts/{discovery.prompt.md → osk-discover.prompt.md} +0 -0
  27. /package/templates/github/prompts/{setup.prompt.md → osk-init.prompt.md} +0 -0
  28. /package/templates/github/prompts/{nova-feature.prompt.md → osk-spec.prompt.md} +0 -0
package/README.md CHANGED
@@ -23,7 +23,7 @@ npx open-spec-kit update
23
23
  O workflow completo roda com 4 skills no VS Code:
24
24
 
25
25
  ```
26
- /setup → /discovery → /spec → /dev
26
+ /osk-init → /osk-discover → /osk-spec → /osk-build
27
27
  ```
28
28
 
29
29
  Cada skill lê o que a anterior produziu, gera artefatos e atualiza o Confluence automaticamente.
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { Command } from 'commander';
4
4
  import { initCommand } from '../src/commands/init.js';
5
+ import { installCommand } from '../src/commands/install.js';
5
6
  import { updateCommand } from '../src/commands/update.js';
6
7
  import { validateCommand } from '../src/commands/validate.js';
7
8
  import { doctorCommand } from '../src/commands/doctor.js';
@@ -18,6 +19,12 @@ program
18
19
  .description('Inicializar estrutura de spec-driven development no projeto')
19
20
  .action(initCommand);
20
21
 
22
+ program
23
+ .command('install')
24
+ .description('Instalar skills e agents globalmente em ~/.open-spec-kit/')
25
+ .option('--force', 'Sobrescrever tudo, incluindo credenciais')
26
+ .action(installCommand);
27
+
21
28
  program
22
29
  .command('update')
23
30
  .description('Re-gerar arquivos de adapter quando skills ou agents mudarem')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guilhermefsousa/open-spec-kit",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "description": "CLI para spec-driven development com suporte a Claude Code e GitHub Copilot",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5,6 +5,7 @@ import { execSync } from 'child_process';
5
5
  import yaml from 'yaml';
6
6
  import { detectMcpRunner, getInstallInstructions } from '../utils/mcp-detect.js';
7
7
  import { confluenceRequest, figmaRequest, isSslError } from '../utils/http.js';
8
+ import { GLOBAL_DIR, getGlobalPath, getGlobalEnvPath, parseEnvContent } from '../utils/global-path.js';
8
9
 
9
10
  export async function doctorCommand() {
10
11
  console.log(chalk.bold('\n open-spec-kit doctor\n'));
@@ -16,6 +17,28 @@ export async function doctorCommand() {
16
17
  let claudeMcpContent = '';
17
18
  let copilotMcpContent = '';
18
19
 
20
+ // Check 0: Global installation exists
21
+ try {
22
+ await access(GLOBAL_DIR);
23
+ await access(getGlobalEnvPath());
24
+ console.log(chalk.green(` ✓ Instalação global encontrada (${GLOBAL_DIR})`));
25
+ pass++;
26
+ } catch {
27
+ console.log(chalk.red(` ✗ Instalação global não encontrada (${GLOBAL_DIR})`));
28
+ console.log(chalk.dim(' Execute "open-spec-kit install" para instalar skills e credenciais.'));
29
+ fail++;
30
+ }
31
+
32
+ // Check 0b: Version check
33
+ try {
34
+ const versionJson = JSON.parse(await readFile(getGlobalPath('version.json'), 'utf-8'));
35
+ console.log(chalk.green(` ✓ Versão instalada: ${versionJson.version}`));
36
+ pass++;
37
+ } catch {
38
+ console.log(chalk.yellow(' ⚠ version.json ausente — execute "open-spec-kit install" para atualizar'));
39
+ fail++;
40
+ }
41
+
19
42
  // Check 1: projects.yml exists and is filled
20
43
  try {
21
44
  projectsContent = await readFile(join(cwd, 'projects.yml'), 'utf-8');
@@ -32,25 +55,12 @@ export async function doctorCommand() {
32
55
  fail++;
33
56
  }
34
57
 
35
- // Check 2: Agents configured
36
- let agents = [];
58
+ // Check 2: Repos configured
37
59
  let projectsData = null;
38
60
  try {
39
61
  projectsData = yaml.parse(projectsContent);
40
- agents = projectsData?.project?.agents || [];
41
- if (!Array.isArray(agents)) agents = [];
42
- } catch { /* fallback: ignore parse error, agents = [] */ }
43
-
44
- if (agents.length > 0) {
45
- console.log(chalk.green(` ✓ AI tools configurados: ${agents.join(', ')}`));
46
- pass++;
47
- } else {
48
- console.log(chalk.yellow(' ⚠ Nenhum AI tool configurado em projects.yml (campo agents:)'));
49
-
50
- fail++;
51
- }
62
+ } catch { /* ignore */ }
52
63
 
53
- // Check 2b: Repos configured
54
64
  try {
55
65
  const repos = projectsData?.repos;
56
66
  const hasRepos = Array.isArray(repos) && repos.some(r => r && typeof r === 'object' && r.name);
@@ -63,131 +73,101 @@ export async function doctorCommand() {
63
73
  }
64
74
  } catch { /* ignore */ }
65
75
 
66
- // Check 3: Claude Code files
67
- if (agents.includes('claude')) {
68
- try {
69
- claudeMcpContent = await readFile(join(cwd, '.mcp.json'), 'utf-8');
70
- if (claudeMcpContent.includes('SEU-TOKEN') || claudeMcpContent.includes('SEU-DOMINIO')
71
- || claudeMcpContent.includes('seu-token-aqui') || claudeMcpContent.includes('seu-dominio')
72
- || claudeMcpContent.includes('seu-email') || claudeMcpContent.includes('${CONFLUENCE_URL}')
73
- || claudeMcpContent.includes('${CONFLUENCE_API_TOKEN}')) {
74
- console.log(chalk.yellow(' ⚠ .mcp.json (Claude) existe mas tem placeholders'));
75
- fail++;
76
- } else {
77
- console.log(chalk.green(' ✓ .mcp.json (Claude) configurado'));
78
- pass++;
79
- }
80
- } catch {
81
- console.log(chalk.red(' ✗ .mcp.json (Claude) nao encontrado'));
76
+ // Check 3: MCP configs (still per-project)
77
+ try {
78
+ claudeMcpContent = await readFile(join(cwd, '.mcp.json'), 'utf-8');
79
+ if (claudeMcpContent.includes('SEU-TOKEN') || claudeMcpContent.includes('seu-token-aqui')
80
+ || claudeMcpContent.includes('seu-dominio') || claudeMcpContent.includes('${CONFLUENCE_URL}')) {
81
+ console.log(chalk.yellow(' ⚠ .mcp.json (Claude) existe mas tem placeholders'));
82
82
  fail++;
83
- }
84
-
85
- try {
86
- await access(join(cwd, '.agents/agents/spec-hub.agent.md'));
87
- console.log(chalk.green(' ✓ .agents/agents/spec-hub.agent.md existe'));
83
+ } else {
84
+ console.log(chalk.green(' ✓ .mcp.json (Claude) configurado'));
88
85
  pass++;
89
- } catch {
90
- console.log(chalk.red(' ✗ .agents/agents/spec-hub.agent.md ausente'));
91
- fail++;
92
86
  }
87
+ } catch {
88
+ console.log(chalk.yellow(' ⚠ .mcp.json (Claude) não encontrado'));
89
+ fail++;
93
90
  }
94
91
 
95
- // Check 4: Copilot files
96
- if (agents.includes('copilot')) {
97
- try {
98
- copilotMcpContent = await readFile(join(cwd, '.vscode/mcp.json'), 'utf-8');
99
- if (copilotMcpContent.includes('SEU-TOKEN') || copilotMcpContent.includes('SEU-DOMINIO')
100
- || copilotMcpContent.includes('seu-token-aqui') || copilotMcpContent.includes('seu-dominio')
101
- || copilotMcpContent.includes('seu-email') || copilotMcpContent.includes('${CONFLUENCE_URL}')
102
- || copilotMcpContent.includes('${CONFLUENCE_API_TOKEN}')) {
103
- console.log(chalk.yellow(' ⚠ .vscode/mcp.json (Copilot) existe mas tem placeholders'));
104
- fail++;
105
- } else {
106
- console.log(chalk.green(' ✓ .vscode/mcp.json (Copilot) configurado'));
107
- pass++;
108
- }
109
- } catch {
110
- console.log(chalk.red(' ✗ .vscode/mcp.json (Copilot) ausente'));
92
+ try {
93
+ copilotMcpContent = await readFile(join(cwd, '.vscode/mcp.json'), 'utf-8');
94
+ if (copilotMcpContent.includes('SEU-TOKEN') || copilotMcpContent.includes('seu-token-aqui')
95
+ || copilotMcpContent.includes('seu-dominio') || copilotMcpContent.includes('${CONFLUENCE_URL}')) {
96
+ console.log(chalk.yellow(' ⚠ .vscode/mcp.json (Copilot) existe mas tem placeholders'));
111
97
  fail++;
112
- }
113
-
114
- try {
115
- await access(join(cwd, '.github/agents/spec-hub.agent.md'));
116
- console.log(chalk.green(' ✓ .github/agents/spec-hub.agent.md existe'));
98
+ } else {
99
+ console.log(chalk.green(' ✓ .vscode/mcp.json (Copilot) configurado'));
117
100
  pass++;
118
- } catch {
119
- console.log(chalk.red(' ✗ .github/agents/spec-hub.agent.md ausente'));
120
- fail++;
121
- }
122
-
123
- try {
124
- await access(join(cwd, '.github/copilot-instructions.md'));
125
- console.log(chalk.green(' ✓ .github/copilot-instructions.md existe'));
126
- pass++;
127
- } catch {
128
- console.log(chalk.red(' ✗ .github/copilot-instructions.md ausente'));
129
- fail++;
130
101
  }
102
+ } catch {
103
+ console.log(chalk.yellow(' ⚠ .vscode/mcp.json (Copilot) não encontrado'));
104
+ fail++;
131
105
  }
132
106
 
133
- // Check 5: Skills exist
107
+ // Check 4: Global skills exist
134
108
  const skills = [
135
- '.agents/skills/setup-project/SKILL.md',
136
- '.agents/skills/discovery/SKILL.md',
137
- '.agents/skills/specifying-features/SKILL.md',
138
- '.agents/skills/dev-orchestrator/SKILL.md',
109
+ 'skills/setup-project/SKILL.md',
110
+ 'skills/discovery/SKILL.md',
111
+ 'skills/specifying-features/SKILL.md',
112
+ 'skills/dev-orchestrator/SKILL.md',
139
113
  ];
140
- let skillCount = 0;
141
- for (const skill of skills) {
142
- try {
143
- await access(join(cwd, skill));
144
- skillCount++;
145
- } catch { /* missing */ }
146
- }
114
+ const skillResults = await Promise.all(skills.map(s => access(getGlobalPath(s)).then(() => true, () => false)));
115
+ const skillCount = skillResults.filter(Boolean).length;
147
116
  if (skillCount === skills.length) {
148
- console.log(chalk.green(` ✓ ${skillCount}/4 skills encontrados`));
117
+ console.log(chalk.green(` ✓ ${skillCount}/4 skills globais encontrados`));
149
118
  pass++;
150
119
  } else {
151
- console.log(chalk.yellow(` ⚠ ${skillCount}/4 skills encontrados`));
120
+ console.log(chalk.yellow(` ⚠ ${skillCount}/4 skills globais encontrados`));
152
121
  fail++;
153
122
  }
154
123
 
155
- // Check 6: Rules exist
156
- const rules = [
157
- '.agents/rules/ownership.instructions.md',
158
- '.agents/rules/hub_structure.instructions.md',
159
- ];
160
- let ruleCount = 0;
161
- for (const rule of rules) {
162
- try {
163
- await access(join(cwd, rule));
164
- ruleCount++;
165
- } catch { /* missing */ }
166
- }
124
+ // Check 5: Global rules exist
125
+ const rules = ['rules/ownership.instructions.md', 'rules/hub_structure.instructions.md'];
126
+ const ruleResults = await Promise.all(rules.map(r => access(getGlobalPath(r)).then(() => true, () => false)));
127
+ const ruleCount = ruleResults.filter(Boolean).length;
167
128
  if (ruleCount === rules.length) {
168
- console.log(chalk.green(` ✓ ${ruleCount}/2 rules encontradas`));
129
+ console.log(chalk.green(` ✓ ${ruleCount}/2 rules globais encontradas`));
169
130
  pass++;
170
131
  } else {
171
- console.log(chalk.yellow(` ⚠ ${ruleCount}/2 rules encontradas`));
132
+ console.log(chalk.yellow(` ⚠ ${ruleCount}/2 rules globais encontradas`));
172
133
  fail++;
173
134
  }
174
135
 
175
- // Check 7: MCP CLI available — detect best runner (direct, uvx, pipx)
136
+ // Check 6: Global agent hub
137
+ try {
138
+ await access(getGlobalPath('agents', 'spec-hub.agent.md'));
139
+ console.log(chalk.green(' ✓ spec-hub.agent.md global encontrado'));
140
+ pass++;
141
+ } catch {
142
+ console.log(chalk.yellow(' ⚠ spec-hub.agent.md global ausente'));
143
+ fail++;
144
+ }
145
+
146
+ // Check 7: Copilot global files
147
+ try {
148
+ await access(getGlobalPath('github', 'agents', 'spec-hub.agent.md'));
149
+ console.log(chalk.green(' ✓ Copilot agents globais encontrados'));
150
+ pass++;
151
+ } catch {
152
+ console.log(chalk.yellow(' ⚠ Copilot agents globais ausentes'));
153
+ fail++;
154
+ }
155
+
156
+ // Check 8: MCP CLI available
176
157
  const mcpRunner = detectMcpRunner();
177
158
  if (mcpRunner.method !== 'pip-fallback') {
178
159
  const via = mcpRunner.method === 'direct' ? 'PATH' : mcpRunner.method;
179
- console.log(chalk.green(` ✓ mcp-atlassian disponível via ${via} (${mcpRunner.command}${mcpRunner.prefix.length > 0 ? ' ' + mcpRunner.prefix.join(' ') : ''})`));
160
+ console.log(chalk.green(` ✓ mcp-atlassian disponível via ${via}`));
180
161
  pass++;
181
162
  } else {
182
- console.log(chalk.yellow(' ⚠ mcp-atlassian não encontrado (nem direto, nem via uvx/pipx)'));
163
+ console.log(chalk.yellow(' ⚠ mcp-atlassian não encontrado'));
183
164
  for (const line of getInstallInstructions()) {
184
165
  console.log(chalk.dim(` ${line}`));
185
166
  }
186
167
  fail++;
187
168
  }
188
169
 
189
- // Check 7b: Python version >= 3.10 (required by mcp-atlassian)
190
- // Try python3 first (Linux/macOS default), fall back to python (Windows default)
170
+ // Check 9: Python version >= 3.10
191
171
  try {
192
172
  let pyVersion;
193
173
  try {
@@ -200,21 +180,19 @@ export async function doctorCommand() {
200
180
  const major = Number(match[1]);
201
181
  const minor = Number(match[2]);
202
182
  if (major > 3 || (major === 3 && minor >= 10)) {
203
- console.log(chalk.green(` ✓ Python ${major}.${minor} (>= 3.10 exigido pelo mcp-atlassian)`));
183
+ console.log(chalk.green(` ✓ Python ${major}.${minor}`));
204
184
  pass++;
205
185
  } else {
206
- console.log(chalk.yellow(` ⚠ Python ${major}.${minor} detectado — mcp-atlassian requer >= 3.10`));
207
- console.log(chalk.dim(' Instale Python 3.10+: https://python.org/downloads'));
186
+ console.log(chalk.yellow(` ⚠ Python ${major}.${minor} — mcp-atlassian requer >= 3.10`));
208
187
  fail++;
209
188
  }
210
189
  }
211
190
  } catch {
212
191
  console.log(chalk.yellow(' ⚠ Python não encontrado no PATH'));
213
- console.log(chalk.dim(' Instale Python 3.10+: https://python.org/downloads'));
214
192
  fail++;
215
193
  }
216
194
 
217
- // Check 8: Git repo
195
+ // Check 10: Git repo
218
196
  try {
219
197
  execSync('git rev-parse --is-inside-work-tree', { cwd, stdio: 'pipe', timeout: 10000 });
220
198
  console.log(chalk.green(' ✓ Repositório Git inicializado'));
@@ -224,127 +202,58 @@ export async function doctorCommand() {
224
202
  fail++;
225
203
  }
226
204
 
227
- // Check 9: Dual-platform sync
228
- if (agents.includes('claude') && agents.includes('copilot')) {
229
- try {
230
- const claudeAgent = await readFile(join(cwd, '.agents/agents/spec-hub.agent.md'), 'utf-8');
231
- const copilotAgent = await readFile(join(cwd, '.github/agents/spec-hub.agent.md'), 'utf-8');
232
-
233
- // Simple check: both should reference spec-agent
234
- if (claudeAgent.includes('spec-agent') && copilotAgent.includes('spec-agent')) {
235
- console.log(chalk.green(' ✓ Agents sincronizados entre Claude e Copilot'));
236
- pass++;
237
- } else {
238
- console.log(chalk.yellow(' ⚠ Agents podem estar dessincronizados'));
239
- fail++;
240
- }
241
- } catch {
242
- console.log(chalk.yellow(' ⚠ Nao foi possivel verificar sincronizacao'));
243
- fail++;
244
- }
245
- }
246
-
247
- // Check 10: Figma MCP (optional — only if projects.yml declares figma)
248
- const hasFigma = /figma:\s*\n\s+file_url:\s*\S+/.test(projectsContent);
249
- if (hasFigma) {
250
- if (claudeMcpContent) {
251
- if (claudeMcpContent.includes('"figma"')) {
252
- console.log(chalk.green(' ✓ .mcp.json (Claude) tem servidor Figma configurado'));
253
- pass++;
254
- } else {
255
- console.log(chalk.yellow(' ⚠ projects.yml tem figma: mas .mcp.json não tem servidor figma'));
256
- fail++;
257
- }
258
- }
259
- if (copilotMcpContent) {
260
- if (copilotMcpContent.includes('"figma"')) {
261
- console.log(chalk.green(' ✓ .vscode/mcp.json (Copilot) tem servidor Figma configurado'));
262
- pass++;
263
- } else {
264
- console.log(chalk.yellow(' ⚠ projects.yml tem figma: mas .vscode/mcp.json não tem servidor figma'));
265
- fail++;
266
- }
267
- }
268
- }
269
-
270
- // Parse .env once for credential checks (11 + 12)
205
+ // Check 11: Confluence credentials (from global .env)
271
206
  let envVars = {};
272
207
  try {
273
- const envContent = await readFile(join(cwd, '.env'), 'utf-8');
274
- envVars = Object.fromEntries(
275
- envContent.split('\n')
276
- .filter(l => l.includes('=') && !l.startsWith('#'))
277
- .map(l => { const idx = l.indexOf('='); return [l.slice(0, idx).trim(), l.slice(idx + 1).trim()]; }),
278
- );
279
- } catch {
280
- // .env not found — already warned by MCP config check
281
- }
208
+ const envContent = await readFile(getGlobalEnvPath(), 'utf-8');
209
+ envVars = parseEnvContent(envContent);
210
+ } catch { /* global .env not found */ }
282
211
 
283
- const isPlaceholder = (v) => !v || v.includes('seu-') || v.includes('SEU-') || v === 'seu-api-token-aqui' || v === 'seu-figma-api-key';
212
+ const isPlaceholder = (v) => !v || v.includes('seu-') || v.includes('SEU-');
284
213
 
285
- // Check 11: Confluence credentials — make real HTTP call to validate token
286
214
  {
287
215
  const confUrl = envVars.CONFLUENCE_URL;
288
216
  const confUser = envVars.CONFLUENCE_USERNAME;
289
217
  const confToken = envVars.CONFLUENCE_API_TOKEN;
290
218
 
291
219
  if (isPlaceholder(confUrl) || isPlaceholder(confUser) || isPlaceholder(confToken)) {
292
- console.log(chalk.yellow(' ⚠ .env não configurado pular validação de credenciais Confluence'));
293
- console.log(chalk.dim(' Configure CONFLUENCE_URL, CONFLUENCE_USERNAME e CONFLUENCE_API_TOKEN no .env'));
220
+ console.log(chalk.yellow(' ⚠ Credenciais Confluence não configuradas no .env global'));
221
+ console.log(chalk.dim(` Configure em: ${getGlobalEnvPath()}`));
294
222
  fail++;
295
223
  } else {
296
224
  try {
297
225
  await confluenceRequest(confUrl, '/rest/api/space?limit=1', confUser, confToken, 10000);
298
- console.log(chalk.green(' ✓ Credenciais Confluence válidas (token ativo)'));
226
+ console.log(chalk.green(' ✓ Credenciais Confluence válidas'));
299
227
  pass++;
300
228
  } catch (err) {
301
229
  if (err.status === 401 || err.status === 403) {
302
- console.log(chalk.red(` ✗ Token Confluence inválido ou expirado (HTTP ${err.status})`));
303
- console.log(chalk.dim(' Renove em: https://id.atlassian.com/manage-profile/security/api-tokens'));
304
- console.log(chalk.dim(' Atualize CONFLUENCE_API_TOKEN no .env'));
230
+ console.log(chalk.red(` ✗ Token Confluence inválido (HTTP ${err.status})`));
231
+ console.log(chalk.dim(` Atualize em: ${getGlobalEnvPath()}`));
305
232
  fail++;
306
233
  } else {
307
- console.log(chalk.yellow(` ⚠ Não foi possível verificar credenciais Confluence: ${err.message}`));
234
+ console.log(chalk.yellow(` ⚠ Não foi possível verificar credenciais: ${err.message}`));
308
235
  fail++;
309
236
  }
310
237
  }
311
238
  }
312
239
  }
313
240
 
314
- // Check 12: Figma credentials — make real HTTP call to validate token
241
+ // Check 12: Figma credentials (optional)
242
+ const hasFigma = /figma:\s*\n\s+file_url:\s*\S+/.test(projectsContent);
315
243
  if (hasFigma) {
316
244
  const figmaKey = envVars.FIGMA_API_KEY;
317
-
318
245
  if (isPlaceholder(figmaKey)) {
319
- console.log(chalk.yellow(' ⚠ FIGMA_API_KEY não configurado no .env'));
320
- console.log(chalk.dim(' Gere em: Figma > Account Settings > Personal Access Tokens'));
246
+ console.log(chalk.yellow(' ⚠ FIGMA_API_KEY não configurado no .env global'));
321
247
  fail++;
322
248
  } else {
323
249
  try {
324
250
  const result = await figmaRequest(figmaKey, 10000);
325
- console.log(chalk.green(` ✓ Token Figma válido (usuário: ${result.data.handle || result.data.email || 'OK'})`));
251
+ console.log(chalk.green(` ✓ Token Figma válido (${result.data.handle || result.data.email || 'OK'})`));
326
252
  pass++;
327
253
  } catch (err) {
328
254
  if (err.status === 401 || err.status === 403) {
329
- console.log(chalk.red(` ✗ Token Figma inválido ou expirado (HTTP ${err.status})`));
330
- console.log(chalk.dim(' Renove em: Figma > Account Settings > Personal Access Tokens'));
331
- console.log(chalk.dim(' Atualize FIGMA_API_KEY no .env'));
255
+ console.log(chalk.red(` ✗ Token Figma inválido (HTTP ${err.status})`));
332
256
  fail++;
333
- } else if (isSslError(err)) {
334
- try {
335
- const result = await figmaRequest(figmaKey, 10000, true);
336
- console.log(chalk.green(` ✓ Token Figma válido — SSL corporativo ignorado (usuário: ${result.data.handle || result.data.email || 'OK'})`));
337
- pass++;
338
- } catch (retryErr) {
339
- if (retryErr.status === 401 || retryErr.status === 403) {
340
- console.log(chalk.red(` ✗ Token Figma inválido ou expirado (HTTP ${retryErr.status})`));
341
- console.log(chalk.dim(' Renove em: Figma > Account Settings > Personal Access Tokens'));
342
- console.log(chalk.dim(' Atualize FIGMA_API_KEY no .env'));
343
- } else {
344
- console.log(chalk.yellow(` ⚠ Não foi possível verificar token Figma: ${retryErr.message}`));
345
- }
346
- fail++;
347
- }
348
257
  } else {
349
258
  console.log(chalk.yellow(` ⚠ Não foi possível verificar token Figma: ${err.message}`));
350
259
  fail++;
@@ -360,8 +269,9 @@ export async function doctorCommand() {
360
269
  console.log('');
361
270
 
362
271
  if (fail > 0) {
363
- console.log(chalk.dim(' Execute "open-spec-kit init" para corrigir a estrutura.\n'));
272
+ console.log(chalk.dim(' Execute "open-spec-kit install" para corrigir a instalação global.'));
273
+ console.log(chalk.dim(' Execute "open-spec-kit init" para corrigir o projeto.\n'));
364
274
  } else {
365
- console.log(chalk.green(' Ambiente pronto! Execute /setup para comecar.\n'));
275
+ console.log(chalk.green(' Ambiente pronto! Execute /osk-init para começar.\n'));
366
276
  }
367
277
  }