@spec-wave/cli 0.5.8 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spec-wave/cli",
3
- "version": "0.5.8",
3
+ "version": "0.5.9",
4
4
  "description": "Setup spec-driven GitHub workflow with Projects v2, labels, issue templates, and AI-powered Actions",
5
5
  "type": "module",
6
6
  "bin": {
@@ -4,7 +4,7 @@ import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
4
4
  import { execSync } from 'node:child_process';
5
5
  import path from 'node:path';
6
6
  import { resolveToken } from '../api/auth.mjs';
7
- import { CONFIG_FILE } from '../config.mjs';
7
+ import { CONFIG_FILE, STAGE_IN_PROGRESS, STAGE_DONE, STAGE_CODE_REVIEW } from '../config.mjs';
8
8
  import { getIssue } from '../api/github-rest.mjs';
9
9
  import { listSubIssues, getIssueParent } from '../api/github-graphql.mjs';
10
10
  import { detectIssueType } from '../lib/issue-type.mjs';
@@ -14,14 +14,17 @@ import { slugify } from '../lib/slugify.mjs';
14
14
  const WORK_DIR = '.spec-wave';
15
15
 
16
16
  // Sobe a cadeia de pais (Task → Story → Feature) até achar uma issue do tipo
17
- // "Feature" e devolve seu título (para resolver docs/features/<slug>). Limita a
18
- // profundidade para evitar loops em dados inconsistentes.
19
- async function resolveFeatureTitle(token, startNodeId) {
17
+ // "Feature" e devolve { number, title } — usado para resolver docs/features/<slug>
18
+ // e para as instruções de fim de Story (mover a Feature para Code Review). Limita
19
+ // a profundidade para evitar loops em dados inconsistentes.
20
+ async function resolveFeature(token, startNodeId) {
20
21
  let current = startNodeId;
21
22
  for (let depth = 0; depth < 5 && current; depth++) {
22
23
  const parent = await getIssueParent(token, current);
23
24
  if (!parent) return null;
24
- if (detectIssueType({ title: parent.title }) === 'Feature') return parent.title;
25
+ if (detectIssueType({ title: parent.title }) === 'Feature') {
26
+ return { number: parent.number, title: parent.title };
27
+ }
25
28
  current = parent.nodeId;
26
29
  }
27
30
  return null;
@@ -40,7 +43,7 @@ function readSpecPlan(featureDir) {
40
43
  }
41
44
 
42
45
  // Monta o markdown de contexto que será entregue ao spec-kit implement.
43
- function buildContext({ type, issue, tasks, spec, plan, specPath, planPath }) {
46
+ function buildContext({ type, issue, tasks, feature, spec, plan, specPath, planPath }) {
44
47
  const lines = [];
45
48
  lines.push(`# Contexto de implementação — ${type} #${issue.number}`);
46
49
  lines.push('');
@@ -50,32 +53,55 @@ function buildContext({ type, issue, tasks, spec, plan, specPath, planPath }) {
50
53
  lines.push(issue.body.trim());
51
54
  }
52
55
 
53
- // Issues cujo status deve ir para "in progress" ao iniciar: a issue alvo
54
- // (Story ou Task) e, no caso de Story, todas as suas Tasks.
55
- const inProgress = [issue.number, ...tasks.map(t => t.number)]
56
- .filter((n, i, arr) => arr.indexOf(n) === i);
56
+ // Fluxo SEQUENCIAL, uma task por vez: cada task vai para "In Progress"
57
+ // quando seu desenvolvimento começa e vai para "Done" ao concluir nunca
58
+ // todas as tasks em "In Progress" ao mesmo tempo.
57
59
  lines.push('');
58
- lines.push('## Instruções para o agente');
60
+ lines.push('## Instruções de execução (uma task por vez, sequencial)');
61
+ lines.push('');
62
+ if (type === 'Story') {
63
+ lines.push(
64
+ `Implemente as ${tasks.length} task(s) desta Story **uma de cada vez, na ordem listada ` +
65
+ 'abaixo**. É PROIBIDO mover mais de uma task para "In Progress" ao mesmo tempo: uma task ' +
66
+ 'só entra em desenvolvimento depois que a anterior estiver concluída.'
67
+ );
68
+ lines.push('');
69
+ lines.push(`1. Ao **iniciar a primeira** task, mova a Story #${issue.number} para **${STAGE_IN_PROGRESS}**.`);
70
+ lines.push('2. Para **cada task**, na ordem, execute este ciclo completo antes de passar para a próxima:');
71
+ lines.push(` 1. **Ao começar a task:** mova o status (campo "Etapa") *apenas dessa task* para **${STAGE_IN_PROGRESS}** (In Progress). Nenhuma outra task.`);
72
+ lines.push(' 2. **Implemente** essa task por completo.');
73
+ lines.push(` 3. **Ao concluir a task:** mova o status *dessa task* para **${STAGE_DONE}** (Done).`);
74
+ lines.push(' 4. Só então avance para a próxima task e repita o ciclo.');
75
+ lines.push('');
76
+ lines.push(`3. **Ao concluir a implementação de TODA a Story** (todas as tasks em **${STAGE_DONE}**):`);
77
+ lines.push(' 1. Faça o **commit** de todas as mudanças da implementação.');
78
+ lines.push(` 2. Abra o **Pull Request** da Story #${issue.number}.`);
79
+ lines.push(
80
+ ` 3. Mova ${feature ? `a Feature #${feature.number}` : 'a Feature (issue pai da Story)'} e a ` +
81
+ `Story #${issue.number} para **${STAGE_CODE_REVIEW}**. ` +
82
+ `As **Tasks permanecem em ${STAGE_DONE}** (não as mova para trás).`
83
+ );
84
+ } else {
85
+ lines.push(`Implemente a Task #${issue.number} bracketando o status no GitHub Project:`);
86
+ lines.push('');
87
+ lines.push(`1. **Ao começar:** mova o status (campo "Etapa") da Task #${issue.number} para **${STAGE_IN_PROGRESS}** (In Progress).`);
88
+ lines.push('2. **Implemente** a task por completo.');
89
+ lines.push(`3. **Ao concluir:** mova o status da Task #${issue.number} para **${STAGE_DONE}** (Done).`);
90
+ }
59
91
  lines.push('');
60
92
  lines.push(
61
- 'Antes de começar a implementação, atualize o status no GitHub Project para ' +
62
- '**🚧 Desenvolvimento** (in progress) ' +
63
- (type === 'Story'
64
- ? `da Story #${issue.number} e de cada Task: ${inProgress.filter(n => n !== issue.number).map(n => `#${n}`).join(', ')}.`
65
- : `da Task #${issue.number}.`)
66
- );
67
- lines.push(
68
- '(Atualize o campo "Etapa"/Status do item no board; mantenha o status coerente ' +
69
- 'conforme o progresso da implementação.)'
93
+ `> Mantenha o status coerente com o progresso real: nenhuma task pode ficar em "${STAGE_IN_PROGRESS}" ` +
94
+ `antes de você começá-la, nem em "${STAGE_DONE}" antes de concluí-la. Atualize o campo "Etapa"/Status ` +
95
+ 'do item no GitHub Project.'
70
96
  );
71
97
 
72
98
  lines.push('');
73
- lines.push(`## Tasks a implementar (${tasks.length})`);
74
- for (const t of tasks) {
99
+ lines.push(`## Tasks a implementar — NESTA ORDEM (${tasks.length})`);
100
+ tasks.forEach((t, i) => {
75
101
  lines.push('');
76
- lines.push(`### #${t.number} ${t.title}`);
102
+ lines.push(`### ${i + 1}. #${t.number} ${t.title}`);
77
103
  if (t.body && t.body.trim()) lines.push(t.body.trim());
78
- }
104
+ });
79
105
 
80
106
  if (spec) {
81
107
  lines.push('');
@@ -171,11 +197,12 @@ export async function implement({ issue: issueArg, featureDir: featureDirOpt, dr
171
197
  return;
172
198
  }
173
199
 
174
- // 4. Resolve spec.md/plan.md da Feature (enriquecimento opcional).
200
+ // 4. Resolve a Feature (pai na cadeia) — para spec.md/plan.md e para as
201
+ // instruções de fim de Story (mover Feature + Story para Code Review).
202
+ const feature = await resolveFeature(token, issue.node_id);
175
203
  let featureDir = featureDirOpt;
176
- if (!featureDir) {
177
- const featureTitle = await resolveFeatureTitle(token, issue.node_id);
178
- if (featureTitle) featureDir = path.join('docs', 'features', slugify(featureTitle));
204
+ if (!featureDir && feature?.title) {
205
+ featureDir = path.join('docs', 'features', slugify(feature.title));
179
206
  }
180
207
  let specPlan = { spec: null, plan: null, specPath: null, planPath: null };
181
208
  if (featureDir && existsSync(featureDir)) {
@@ -187,7 +214,7 @@ export async function implement({ issue: issueArg, featureDir: featureDirOpt, dr
187
214
  }
188
215
 
189
216
  // 5. Monta e grava o arquivo de contexto.
190
- const context = buildContext({ type, issue, tasks, ...specPlan });
217
+ const context = buildContext({ type, issue, tasks, feature, ...specPlan });
191
218
  mkdirSync(WORK_DIR, { recursive: true });
192
219
  const tasksFile = path.join(WORK_DIR, `implement-${issueNumber}.md`);
193
220
  writeFileSync(tasksFile, context);
@@ -2,7 +2,7 @@ import * as p from '@clack/prompts';
2
2
  import chalk from 'chalk';
3
3
  import { readFileSync, existsSync } from 'node:fs';
4
4
  import path from 'node:path';
5
- import { CONFIG_FILE } from '../config.mjs';
5
+ import { CONFIG_FILE, PORTAL_URL } from '../config.mjs';
6
6
 
7
7
  // Lê o marcador .spec-wave.json do repositório atual (cwd) e reporta se o
8
8
  // spec-wave já foi inicializado. Usado pela skill para decidir entre mostrar
@@ -17,6 +17,7 @@ export async function info(options = {}) {
17
17
  }
18
18
  p.intro(chalk.bold('spec-wave info'));
19
19
  p.log.warn(`Este repositório ${chalk.bold('não foi inicializado')} (sem ${CONFIG_FILE}).`);
20
+ p.log.info(`🌐 Acesse o Portal Web da ferramenta em ${chalk.cyan(PORTAL_URL)}`);
20
21
  p.outro('Execute `npx @spec-wave/cli init` para configurar.');
21
22
  return;
22
23
  }
@@ -50,5 +51,6 @@ export async function info(options = {}) {
50
51
  `${chalk.dim('Criado em:')} ${config.initializedAt ?? '?'}`,
51
52
  'Configuração'
52
53
  );
54
+ p.log.info(`🌐 Acesse o Portal Web da ferramenta em ${chalk.cyan(PORTAL_URL)}`);
53
55
  p.outro('Use `/spec-wave feature <descrição>` para criar uma Feature.');
54
56
  }
@@ -1,6 +1,6 @@
1
1
  import * as p from '@clack/prompts';
2
2
  import chalk from 'chalk';
3
- import { readFileSync } from 'node:fs';
3
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import path from 'node:path';
6
6
  import { resolveToken, verifyTokenScopes } from '../api/auth.mjs';
@@ -8,8 +8,8 @@ import { runWizard } from '../ui/wizard.mjs';
8
8
  import { setupProject } from '../setup/project.mjs';
9
9
  import { setupLabels } from '../setup/labels.mjs';
10
10
  import { setupFiles } from '../setup/files.mjs';
11
- import { upsertFile, getFileContent } from '../api/github-rest.mjs';
12
- import { CONFIG_FILE, AI_PROVIDERS, getProvider, DEFAULT_PROVIDER } from '../config.mjs';
11
+ import { getFileContent } from '../api/github-rest.mjs';
12
+ import { CONFIG_FILE, AI_PROVIDERS, getProvider, DEFAULT_PROVIDER, PORTAL_URL } from '../config.mjs';
13
13
 
14
14
  const __dir = path.dirname(fileURLToPath(import.meta.url));
15
15
  const pkg = JSON.parse(readFileSync(path.join(__dir, '..', '..', 'package.json'), 'utf-8'));
@@ -91,8 +91,13 @@ export async function init(options) {
91
91
  if (options.skipProject) {
92
92
  p.log.info('Pulando criação do GitHub Project (--skip-project).');
93
93
  // Preserva o bloco project do .spec-wave.json existente (se houver).
94
+ // Prefere o arquivo local; recorre ao remoto para repos configurados por
95
+ // versões antigas (que commitavam o config direto no repo).
94
96
  try {
95
- const raw = await getFileContent(token, owner, repo, CONFIG_FILE);
97
+ const localConfigPath = path.join(process.cwd(), CONFIG_FILE);
98
+ const raw = existsSync(localConfigPath)
99
+ ? readFileSync(localConfigPath, 'utf-8')
100
+ : await getFileContent(token, owner, repo, CONFIG_FILE);
96
101
  if (raw) {
97
102
  const existing = JSON.parse(raw);
98
103
  if (existing.project) {
@@ -165,10 +170,13 @@ export async function init(options) {
165
170
  }
166
171
 
167
172
  // --- Marcador de configuração (.spec-wave.json) ---
168
- // Commitado no repo-alvo para que a skill detecte, em sessões futuras, que o
169
- // init rodou e qual project/versão foi usado. É a fonte de estado persistente.
173
+ // Gravado LOCALMENTE no diretório atual (não commitado direto no repo): é a
174
+ // fonte de estado persistente lida por info/refresh/uninstall/skill a partir
175
+ // do cwd. O usuário revisa e commita quando quiser.
176
+ const localConfigPath = path.join(process.cwd(), CONFIG_FILE);
177
+ let configWritten = false;
170
178
  const configSpinner = p.spinner();
171
- configSpinner.start(`Gravando ${CONFIG_FILE}...`);
179
+ configSpinner.start(`Gravando ${CONFIG_FILE} local...`);
172
180
  try {
173
181
  const config = {
174
182
  version: pkg.version,
@@ -187,15 +195,9 @@ export async function init(options) {
187
195
  },
188
196
  initializedAt: new Date().toISOString(),
189
197
  };
190
- await upsertFile(
191
- token,
192
- owner,
193
- repo,
194
- CONFIG_FILE,
195
- JSON.stringify(config, null, 2) + '\n',
196
- 'chore: record spec-wave config [spec-wave]'
197
- );
198
- configSpinner.stop(`${CONFIG_FILE} gravado (spec-wave v${pkg.version})`);
198
+ writeFileSync(localConfigPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
199
+ configWritten = true;
200
+ configSpinner.stop(`${CONFIG_FILE} gravado local (spec-wave v${pkg.version})`);
199
201
  } catch (err) {
200
202
  configSpinner.stop('');
201
203
  p.log.warn(`Não foi possível gravar ${CONFIG_FILE}: ${err.message}`);
@@ -214,10 +216,14 @@ export async function init(options) {
214
216
  `\n${chalk.green('✓')} spec-wave configurado com sucesso!\n\n` +
215
217
  (projectUrl ? ` Projeto: ${chalk.cyan(projectUrl)}\n\n` : '') +
216
218
  ` Próximos passos:\n` +
217
- ` 1. Adicione ${providerMeta.secret} como secret no repositório (provider: ${providerMeta.label})\n` +
218
- ` 2. Configure o board view para agrupar por "Etapa"\n` +
219
- ` 3. Crie uma Feature com o prefixo [FEATURE] no título\n` +
220
- ` 4. Use a skill spec-wave para guiar o fluxo\n\n` +
221
- ` ${chalk.dim('Para instalar a skill no seu agente: npx @spec-wave/cli install-skill')}`
219
+ (configWritten
220
+ ? ` 1. Commite o ${CONFIG_FILE} quando quiser (git add ${CONFIG_FILE} && git commit)\n`
221
+ : '') +
222
+ ` ${configWritten ? '2' : '1'}. Adicione ${providerMeta.secret} como secret no repositório (provider: ${providerMeta.label})\n` +
223
+ ` ${configWritten ? '3' : '2'}. Configure o board view para agrupar por "Etapa"\n` +
224
+ ` ${configWritten ? '4' : '3'}. Crie uma Feature com o prefixo [FEATURE] no título\n` +
225
+ ` ${configWritten ? '5' : '4'}. Use a skill spec-wave para guiar o fluxo\n\n` +
226
+ ` ${chalk.dim('Para instalar a skill no seu agente: npx @spec-wave/cli install-skill')}\n\n` +
227
+ ` 🌐 Acesse o Portal Web da ferramenta em ${chalk.cyan(PORTAL_URL)}`
222
228
  );
223
229
  }
@@ -9,6 +9,9 @@ import path from 'node:path';
9
9
  const __dir = path.dirname(fileURLToPath(import.meta.url));
10
10
  // Fonte única da skill, publicada via "files": ["src"] no package.json.
11
11
  const SKILL_SOURCE = path.join(__dir, '..', 'templates', 'skill', 'SKILL.md');
12
+ // Versão da CLI que gerou a skill instalada — carimbada no arquivo para detecção
13
+ // de desatualização (a skill é uma cópia estática; não acompanha o `npx` sozinha).
14
+ const pkg = JSON.parse(readFileSync(path.join(__dir, '..', '..', 'package.json'), 'utf-8'));
12
15
 
13
16
  // Marcadores usados para gravar/atualizar a skill de forma idempotente em
14
17
  // arquivos compartilhados (AGENTS.md) — permite reinstalar sem duplicar.
@@ -83,39 +86,50 @@ const TARGET_BY_KEY = new Map(TARGETS.map((t) => [t.key, t]));
83
86
  // Separa o frontmatter YAML do corpo do SKILL.md. Retorna { meta, body }.
84
87
  function parseSkill(raw) {
85
88
  const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
86
- if (!match) return { meta: {}, body: raw.trim() };
89
+ if (!match) return { meta: {}, frontmatter: '', body: raw.trim() };
87
90
  let meta = {};
88
91
  try {
89
92
  meta = yaml.load(match[1]) || {};
90
93
  } catch {
91
94
  meta = {};
92
95
  }
93
- return { meta, body: match[2].trim() };
96
+ return { meta, frontmatter: match[1], body: match[2].trim() };
94
97
  }
95
98
 
96
- // Converte o SKILL.md para o formato exigido por cada agente.
97
- function renderContent(format, raw, parsed) {
98
- const { meta, body } = parsed;
99
+ // Banner de versão inserido no topo do corpo da skill instalada. O agente
100
+ // esta linha e, se `npx @spec-wave/cli --version` for maior, orienta reinstalar.
101
+ function versionBanner(version) {
102
+ return (
103
+ `> ⚙️ **spec-wave skill v${version}** — esta skill é uma cópia estática. ` +
104
+ 'Se `npx @spec-wave/cli --version` indicar uma versão maior, ela está ' +
105
+ 'desatualizada: rode `npx @spec-wave/cli install-skill --force` para atualizá-la.'
106
+ );
107
+ }
108
+
109
+ // Converte o SKILL.md para o formato exigido por cada agente, carimbando a versão.
110
+ function renderContent(format, parsed, version) {
111
+ const { meta, frontmatter, body } = parsed;
99
112
  const description = meta.description ?? 'Skill spec-wave.';
113
+ const banner = versionBanner(version);
100
114
  switch (format) {
101
115
  case 'skill':
102
- // Claude Code / opencode consomem o SKILL.md nativo. Campos extras do
103
- // frontmatter são ignorados pelo opencode sem problema.
104
- return raw.trimEnd() + '\n';
116
+ // Claude Code / opencode consomem o SKILL.md nativo. Preserva o frontmatter
117
+ // original (allowed-tools etc.) e insere o banner no topo do corpo.
118
+ return `---\n${frontmatter}\n---\n\n${banner}\n\n${body}\n`;
105
119
  case 'mdc':
106
120
  return (
107
121
  `---\n` +
108
122
  `description: ${JSON.stringify(description)}\n` +
109
123
  `alwaysApply: false\n` +
110
124
  `---\n\n` +
111
- `${body}\n`
125
+ `${banner}\n\n${body}\n`
112
126
  );
113
127
  case 'rules':
114
- return `# spec-wave\n\n${description}\n\n${body}\n`;
128
+ return `# spec-wave\n\n${banner}\n\n${description}\n\n${body}\n`;
115
129
  case 'agents':
116
- return `${BLOCK_START}\n\n# spec-wave\n\n${description}\n\n${body}\n\n${BLOCK_END}\n`;
130
+ return `${BLOCK_START}\n\n# spec-wave\n\n${banner}\n\n${description}\n\n${body}\n\n${BLOCK_END}\n`;
117
131
  default:
118
- return raw;
132
+ return body;
119
133
  }
120
134
  }
121
135
 
@@ -258,8 +272,8 @@ export async function installSkill(options = {}) {
258
272
  for (const { target, dest } of jobs) {
259
273
  const content =
260
274
  dest.format === 'agents'
261
- ? mergeAgentsFile(dest.path, renderContent('agents', raw, parsed))
262
- : renderContent(dest.format, raw, parsed);
275
+ ? mergeAgentsFile(dest.path, renderContent('agents', parsed, pkg.version))
276
+ : renderContent(dest.format, parsed, pkg.version);
263
277
 
264
278
  // Confirmar sobrescrita de arquivos "próprios" (skill/rules/mdc). Para
265
279
  // 'agents' o merge por marcadores já é seguro (não apaga conteúdo alheio).
@@ -286,7 +300,10 @@ export async function installSkill(options = {}) {
286
300
 
287
301
  p.note(
288
302
  written.map((w) => `${chalk.green('✓')} ${chalk.bold(w.target.name)}\n ${chalk.dim(w.dest.path)}`).join('\n'),
289
- `Skill instalada (escopo: ${scopeLabel})`,
303
+ `Skill v${pkg.version} instalada (escopo: ${scopeLabel})`,
304
+ );
305
+ p.outro(
306
+ 'Reinicie/recarregue o agente para que ele detecte a skill. ' +
307
+ 'Ao atualizar a CLI, rode `install-skill --force` para atualizar a skill também.',
290
308
  );
291
- p.outro('Reinicie/recarregue o agente para que ele detecte a skill.');
292
309
  }
package/src/config.mjs CHANGED
@@ -4,6 +4,9 @@
4
4
  // pelo comando `info` (e pela skill) para detectar se o spec-wave já foi configurado.
5
5
  export const CONFIG_FILE = '.spec-wave.json';
6
6
 
7
+ // Portal Web da ferramenta — exibido ao final do `init` e no `info`.
8
+ export const PORTAL_URL = 'https://spec-wave.astratech.net.br';
9
+
7
10
  // Providers de IA suportados pelos workflows (generate-plan/spec/decompose).
8
11
  // O provider e o modelo escolhidos no `init` são persistidos em .spec-wave.json
9
12
  // (bloco `ai`) e lidos em runtime por src/lib/claude.mjs. Cada provider declara
@@ -48,6 +51,14 @@ export const STATUS_OPTIONS = [
48
51
  { name: '🎉 Done', color: 'GREEN' },
49
52
  ];
50
53
 
54
+ // Etapas usadas pelo fluxo de implementação (comando `implement`): cada task vai
55
+ // para "In Progress" ao INICIAR seu desenvolvimento e para "Done" ao concluir;
56
+ // ao final da Story, Feature + Story vão para "Code Review" (após commit + PR).
57
+ // Resolvidas por nome para não quebrar se a ordem/cor das opções mudar.
58
+ export const STAGE_IN_PROGRESS = STATUS_OPTIONS.find(s => s.name.includes('Desenvolvimento')).name;
59
+ export const STAGE_DONE = STATUS_OPTIONS.find(s => s.name.includes('Done')).name;
60
+ export const STAGE_CODE_REVIEW = STATUS_OPTIONS.find(s => s.name.includes('Code Review')).name;
61
+
51
62
  export const CUSTOM_FIELDS = [
52
63
  {
53
64
  name: 'Work Item Type',
@@ -4,7 +4,7 @@ description: "Use when the user wants to set up a spec-driven GitHub workflow, c
4
4
  argument-hint: "[info|setup|issue|feature|spec|plan|ready|decompose|implement|uninstall|rfc|fix-pr] [target]"
5
5
  user-invocable: true
6
6
  allowed-tools:
7
- - Bash(npx spec-wave *)
7
+ - Bash(npx @spec-wave/cli *)
8
8
  - Bash(gh issue *)
9
9
  - Bash(gh project *)
10
10
  - Bash(gh repo view *)
@@ -27,16 +27,18 @@ Este skill guia o usuário pelo fluxo spec-driven definido no RFC-001.
27
27
 
28
28
  > **Antes de responder a qualquer sub-comando**, leia o arquivo `rfc/rfc-integrate-spec-kit-into-kanban.md` se ele existir no diretório atual, para embasar suas respostas no processo real da equipe.
29
29
 
30
+ > **Verifique se esta skill está atualizada:** logo no topo deste arquivo há um banner `spec-wave skill vX.Y.Z` (inserido na instalação). Compare com `npx @spec-wave/cli --version`. Se a CLI for **mais recente**, esta skill está desatualizada — avise o usuário e sugira rodar `npx @spec-wave/cli install-skill --force` para atualizá-la (a skill é uma cópia estática e **não** acompanha o `npx` sozinha). Se o banner estiver ausente, a skill foi instalada por uma versão antiga: sugira a mesma atualização.
31
+
30
32
  ---
31
33
 
32
34
  ## Detecção de configuração (faça isto primeiro, sempre)
33
35
 
34
- Antes de qualquer sub-comando, leia o arquivo `.spec-wave.json` na raiz do repositório atual (use o tool Read). Esse arquivo é gravado pelo `npx spec-wave init` e é a fonte de estado persistente entre sessões.
36
+ Antes de qualquer sub-comando, leia o arquivo `.spec-wave.json` na raiz do repositório atual (use o tool Read). Esse arquivo é gravado pelo `npx @spec-wave/cli init` e é a fonte de estado persistente entre sessões.
35
37
 
36
38
  - **Se existir**, o spec-wave já foi configurado. Use seus campos para contextualizar as respostas, sem perguntar de novo:
37
39
  - `owner`/`repo` → repositório alvo dos comandos `gh`
38
40
  - `project.url` / `project.title` → o GitHub Project a referenciar
39
- - `version` → versão da CLI usada no `init` (compare com `npx spec-wave --version`; se divergir, sugira `npx spec-wave refresh --config` para atualizar o arquivo, ou re-rodar o `init` para atualizar workflows/labels)
41
+ - `version` → versão da CLI usada no `init` (compare com `npx @spec-wave/cli --version`; se divergir, sugira `npx @spec-wave/cli refresh --config` para atualizar o arquivo, ou re-rodar o `init` para atualizar workflows/labels)
40
42
  - `initializedAt` → quando foi configurado
41
43
  Não rode `/spec-wave setup` de novo a menos que o usuário peça explicitamente.
42
44
  - **Se não existir**, o repositório provavelmente ainda não foi configurado. Sugira começar por `/spec-wave setup`.
@@ -80,15 +82,15 @@ Labels de gatilho:
80
82
  - `spec-wave:ready` → dispara `validate.yml` → valida ambos os arquivos
81
83
  - `spec-wave:decompose` → dispara `decompose.yml` → gera Stories e Tasks
82
84
 
83
- A etapa **🚧 Desenvolvimento** é coberta pelo comando **local** `spec-wave implement <número>` (não é uma label/Action): lê uma Story ou Task e aciona o spec-kit para implementar. Veja `/spec-wave implement`.
85
+ A etapa **🚧 Desenvolvimento** é coberta pelo comando **local** `npx @spec-wave/cli implement <número>` (não é uma label/Action): lê uma Story ou Task e aciona o spec-kit para implementar. Veja `/spec-wave implement`.
84
86
 
85
87
  ---
86
88
 
87
89
  ## Referência da CLI (conheça os parâmetros ANTES de executar)
88
90
 
89
- Esta skill é um **wrapper** da CLI `spec-wave`. Regra de ouro: **nunca rode um comando sem os parâmetros que ele aceita** esperando que ele pergunte — colete os valores com o usuário e passe via flags. Em especial, **`init` sem `--repo` abre um wizard interativo (@clack/prompts) que a skill NÃO consegue dirigir** — sempre passe `--repo`.
91
+ Esta skill é um **wrapper** da CLI `@spec-wave/cli`, sempre invocada como `npx @spec-wave/cli <comando>`. Regra de ouro: **nunca rode um comando sem os parâmetros que ele aceita** esperando que ele pergunte — colete os valores com o usuário e passe via flags. Em especial, **`init` sem `--repo` abre um wizard interativo (@clack/prompts) que a skill NÃO consegue dirigir** — sempre passe `--repo`.
90
92
 
91
- ### `spec-wave init` — configura o repositório
93
+ ### `@spec-wave/cli init` — configura o repositório
92
94
  | Flag | Tipo | Descrição |
93
95
  |------|------|-----------|
94
96
  | `--repo <owner/repo>` | string | Repositório alvo. **Passe SEMPRE** para evitar o wizard interativo. |
@@ -98,25 +100,25 @@ Esta skill é um **wrapper** da CLI `spec-wave`. Regra de ouro: **nunca rode um
98
100
  | `--skip-files` | flag | Pula a criação dos workflows + issue templates. |
99
101
  | `--dry-run` | flag | Simula a configuração sem alterar nada. |
100
102
 
101
- ### `spec-wave issue` — cria um work item tipado, opcionalmente como sub-issue, e adiciona ao board
103
+ ### `@spec-wave/cli issue` — cria um work item tipado, opcionalmente como sub-issue, e adiciona ao board
102
104
  | Flag | Tipo | Descrição |
103
105
  |------|------|-----------|
104
106
  | `--title <title>` | string (obrigatório) | Título, **sem** o prefixo de tipo (a CLI adiciona, ex.: `[STORY]`). |
105
107
  | `--type <type>` | string | `initiative`, `epic`, `feature`, `story`, `task`, `bug`, `spike` ou `rfc`. Default: `feature`. |
106
108
  | `--parent <n>` | string | Número da issue pai — cria como **sub-issue** dela (relação nativa do GitHub). |
107
109
  | `--body <text>` | string | Descrição. |
108
- | `--priority <p>` | string | `P0`, `P1`, `P2` ou `P3`. |
110
+ | `--priority <p>` | string | **Opcional.** `P0`, `P1`, `P2` ou `P3`. Omita se o usuário não pediu — a prioridade fica `null` (sem prioridade). Nunca atribua por conta própria. |
109
111
  | `--area <area>` | string | `Frontend`, `Backend`, `Mobile`, `Infra`, `DevOps` ou `Data`. |
110
112
 
111
- > Faz tudo: cria a issue (label de tipo + prioridade), vincula ao parent como sub-issue, adiciona ao Project e define os campos **Etapa = 📥 Backlog**, **Work Item Type**, **Priority** e **Area**. Grava `Parent: #N` no corpo. Lê o Project do `.spec-wave.json`. **Não use `gh issue create` direto** — ele não adiciona ao board nem vincula o parent.
113
+ > Faz tudo: cria a issue (label de tipo e de prioridade **apenas se `--priority` for informado**), vincula ao parent como sub-issue, adiciona ao Project e define os campos **Etapa = 📥 Backlog**, **Work Item Type**, **Area** e, **só se informada, Priority**. Grava `Parent: #N` no corpo. Lê o Project do `.spec-wave.json`. **Não use `gh issue create` direto** — ele não adiciona ao board nem vincula o parent.
112
114
 
113
- ### `spec-wave initiative` — atalho de `issue --type initiative`
115
+ ### `@spec-wave/cli initiative` — atalho de `issue --type initiative`
114
116
  Cria o nó raiz da hierarquia (agrupa Epics). Mesmas flags do `issue` exceto `--type` (fixo em `initiative`) e `--parent` (Initiative é raiz, não tem pai).
115
117
 
116
- ### `spec-wave feature` — atalho de `issue --type feature`
118
+ ### `@spec-wave/cli feature` — atalho de `issue --type feature`
117
119
  Mesmas flags do `issue` (exceto `--type`, fixo em `feature`). Mantido para o fluxo do RFC-001.
118
120
 
119
- ### `spec-wave uninstall` — remove a configuração (mantém o Project)
121
+ ### `@spec-wave/cli uninstall` — remove a configuração (mantém o Project)
120
122
  | Flag | Tipo | Descrição |
121
123
  |------|------|-----------|
122
124
  | `--repo <owner/repo>` | string | Repositório (default: lê do `.spec-wave.json`). |
@@ -128,33 +130,33 @@ Mesmas flags do `issue` (exceto `--type`, fixo em `feature`). Mantido para o flu
128
130
 
129
131
  > Remove labels + arquivos `.github` + `.spec-wave.json`. **NUNCA apaga o GitHub Project** (preserva o histórico do board) — o usuário deve excluí-lo manualmente se quiser.
130
132
 
131
- ### `spec-wave info` — status de configuração do repo atual
133
+ ### `@spec-wave/cli info` — status de configuração do repo atual
132
134
  | Flag | Tipo | Descrição |
133
135
  |------|------|-----------|
134
136
  | `--json` | flag | Saída JSON (`{"initialized":bool, ...}`) para parsing programático. |
135
137
 
136
- ### `spec-wave refresh` — atualiza o `.spec-wave.json` local
138
+ ### `@spec-wave/cli refresh` — atualiza o `.spec-wave.json` local
137
139
  | Flag | Tipo | Descrição |
138
140
  |------|------|-----------|
139
141
  | `--config` | flag | Re-consulta o GitHub Project e reescreve o `.spec-wave.json` (IDs do campo Etapa, opções, number, versão da CLI). |
140
142
 
141
143
  > Use quando o `.spec-wave.json` estiver desatualizado: repos inicializados por uma versão antiga (sem `etapaFieldId`/`stageOptions`), Project renomeado, ou versão da CLI divergente. Escreve no arquivo **local** — faça commit depois.
142
144
 
143
- ### `spec-wave generate-plan` · `generate-spec` · `validate` · `decompose`
145
+ ### `@spec-wave/cli generate-plan` · `generate-spec` · `validate` · `decompose`
144
146
  | Flag | Tipo | Descrição |
145
147
  |------|------|-----------|
146
148
  | `--issue-number <n>` | string (obrigatório) | Número da issue no GitHub. |
147
149
 
148
150
  > ⚠️ Esses quatro comandos são executados pelos **GitHub Actions** (disparados por labels), **não** pela skill diretamente. Veja a *Regra fundamental*: para gerar plan/spec/decompor, adicione a **label** correspondente — não rode o comando à mão (a não ser para debug local).
149
151
 
150
- ### `spec-wave implement` — aciona o spec-kit para uma Story ou Task (comando LOCAL)
152
+ ### `@spec-wave/cli implement` — aciona o spec-kit para uma Story ou Task (comando LOCAL)
151
153
  | Flag/Arg | Tipo | Descrição |
152
154
  |----------|------|-----------|
153
155
  | `<issue>` | string (obrigatório) | Número da issue (Story ou Task), ex.: `12` ou `#12`. Argumento posicional. |
154
156
  | `--feature-dir <path>` | string | Caminho `docs/features/<slug>` para anexar `spec.md`/`plan.md` como contexto (sobrescreve a resolução automática). |
155
157
  | `--dry-run` | flag | Monta o contexto e imprime o comando do spec-kit **sem executar**. |
156
158
 
157
- > Diferente dos quatro acima, `implement` roda **localmente** (lê `.spec-wave.json`, como `issue`), não por Action. Detecta o tipo da issue: **Story** → coleta todas as Tasks (sub-issues) e aciona o spec-kit uma única vez; **Task** → só aquela task. Monta o contexto em `.spec-wave/implement-<n>.md` e chama o comando configurado em `specKit.command` (no `.spec-wave.json`) ou na env `SPEC_WAVE_IMPLEMENT_CMD`. Placeholders disponíveis no template: `{tasksFile} {specFile} {planFile} {issue} {type} {title}`. Se nada estiver configurado, ele apenas monta o contexto e mostra como configurar (não executa). O contexto inclui uma instrução para o agente mover a Story e as Tasks para **🚧 Desenvolvimento** (in progress) ao iniciar.
159
+ > Diferente dos quatro acima, `implement` roda **localmente** (lê `.spec-wave.json`, como `issue`), não por Action. Detecta o tipo da issue: **Story** → coleta todas as Tasks (sub-issues) e aciona o spec-kit uma única vez; **Task** → só aquela task. Monta o contexto em `.spec-wave/implement-<n>.md` e chama o comando configurado em `specKit.command` (no `.spec-wave.json`) ou na env `SPEC_WAVE_IMPLEMENT_CMD`. Placeholders disponíveis no template: `{tasksFile} {specFile} {planFile} {issue} {type} {title}`. Se nada estiver configurado, ele apenas monta o contexto e mostra como configurar (não executa). O contexto inclui instruções para o agente implementar as Tasks **sequencialmente, uma por vez**: mover a task para **🚧 Desenvolvimento** só ao iniciá-la e para **🎉 Done** ao concluí-la, antes de passar para a próxima (nunca todas em "in progress" ao mesmo tempo). **Ao concluir toda a Story**: fazer o commit, abrir o PR e mover a **Feature** e a **Story** para **👀 Code Review** (as Tasks permanecem em 🎉 Done).
158
160
 
159
161
  ---
160
162
 
@@ -165,7 +167,7 @@ Mesmas flags do `issue` (exceto `--type`, fixo em `feature`). Mantido para o flu
165
167
  Mostra se o repositório atual já foi configurado com o spec-wave.
166
168
 
167
169
  **Passos:**
168
- 1. Execute: `npx spec-wave info`
170
+ 1. Execute: `npx @spec-wave/cli info`
169
171
  2. **Se o repositório estiver inicializado**, o comando mostra os dados do `.spec-wave.json` (owner/repo, project, versão da CLI, data). Apresente essas informações ao usuário.
170
172
  3. **Se NÃO estiver inicializado**, pergunte ao usuário: "Este repositório ainda não foi configurado com o spec-wave. Quer rodar o `init` agora?"
171
173
  - Se sim → siga o fluxo de `/spec-wave setup`.
@@ -175,17 +177,17 @@ Mostra se o repositório atual já foi configurado com o spec-wave.
175
177
 
176
178
  ### `/spec-wave setup`
177
179
 
178
- Configura o spec-wave no repositório. Você dirige o `init` com flags — **nunca rode `npx spec-wave init` sem `--repo`** (abre o wizard interativo que você não controla).
180
+ Configura o spec-wave no repositório. Você dirige o `init` com flags — **nunca rode `npx @spec-wave/cli init` sem `--repo`** (abre o wizard interativo que você não controla).
179
181
 
180
182
  **Passos:**
181
- 1. **Já configurado?** Leia `.spec-wave.json` (ou rode `npx spec-wave info`). Se existir, avise (mostre `project.url` e `version`) e confirme com o usuário antes de reconfigurar.
183
+ 1. **Já configurado?** Leia `.spec-wave.json` (ou rode `npx @spec-wave/cli info`). Se existir, avise (mostre `project.url` e `version`) e confirme com o usuário antes de reconfigurar.
182
184
  2. **Descubra o repositório alvo** (parâmetro `--repo`): rode `gh repo view --json nameWithOwner -q .nameWithOwner` para obter `owner/repo` do repo atual. Confirme com o usuário; se não houver remote, pergunte o `owner/repo`.
183
185
  3. **Pergunte o título do Project** (parâmetro `--project-title`). Ofereça o default `<repo> — Spec Wave` e aceite-o se o usuário não tiver preferência.
184
186
  4. **Cheque o auth:** `gh auth status`. Se faltarem os escopos `project,repo,workflow`, oriente o usuário a rodar ele mesmo `gh auth refresh --scopes project,repo,workflow` (comando interativo — o usuário executa, não você).
185
- 5. **(Opcional) Pré-visualize** antes de aplicar: `npx spec-wave init --repo <owner/repo> --dry-run`.
187
+ 5. **(Opcional) Pré-visualize** antes de aplicar: `npx @spec-wave/cli init --repo <owner/repo> --dry-run`.
186
188
  6. **Execute com os parâmetros coletados:**
187
189
  ```bash
188
- npx spec-wave init --repo <owner/repo> --project-title "<título>"
190
+ npx @spec-wave/cli init --repo <owner/repo> --project-title "<título>"
189
191
  ```
190
192
  Use `--skip-project` / `--skip-labels` / `--skip-files` **apenas** para re-rodar uma fase específica que falhou antes.
191
193
  7. O `init` cria o Project, as labels, os workflows, um **scaffold de `.github/config/tech_context.yml`** (só se ainda não existir) e grava `.spec-wave.json`. Oriente o usuário a fazer `git pull` para trazer os arquivos ao checkout local.
@@ -201,19 +203,19 @@ Crie um work item tipado (Initiative/Epic/Feature/Story/Task/...) já adicionado
201
203
  **Hierarquia típica:** Initiative → Epic → Feature → Story → Task. A **Initiative** é o nó raiz e agrupa Epics. Use `--parent <n>` para criar como sub-issue do nível acima (ex.: um Epic filho de uma Initiative, ou uma Story filha de uma Feature). O GitHub mostra o parent na issue filha e vice-versa; a CLI ainda grava `Parent: #N` no corpo.
202
204
 
203
205
  **Passos:**
204
- 1. Pergunte ao usuário: tipo (initiative/epic/feature/story/task/...), título (sem prefixo), descrição, área, prioridade, e se há uma issue **pai** (número).
205
- 2. Execute o comando com os parâmetros coletados:
206
+ 1. Pergunte ao usuário: tipo (initiative/epic/feature/story/task/...), título (sem prefixo), descrição e se há uma issue **pai** (número). **Prioridade e área são opcionais**: só as inclua se o usuário pedir explicitamente. **Nunca atribua uma prioridade por conta própria** — se o usuário não informou, **omita `--priority`** e a prioridade fica `null` (sem prioridade) no board.
207
+ 2. Execute o comando com os parâmetros coletados (inclua **apenas** as flags que o usuário forneceu):
206
208
  ```bash
207
- npx spec-wave issue \
209
+ npx @spec-wave/cli issue \
208
210
  --type "<tipo>" \
209
211
  --title "<título>" \
210
212
  --body "<descrição>" \
211
- --area "<área>" \
212
- --priority "<prioridade>" \
213
+ --area "<área>" \ # opcional — omita se o usuário não informou
214
+ --priority "<prioridade>" \ # opcional — só se o usuário pediu; caso contrário OMITA (prioridade fica null)
213
215
  --parent "<número-do-pai>" # opcional
214
216
  ```
215
- Para Features, pode usar o atalho `npx spec-wave feature --title ...` (equivale a `--type feature`).
216
- A CLI cria a issue (label de tipo + prioridade), vincula como sub-issue do parent, adiciona ao Project e define Etapa = 📥 Backlog + Work Item Type + Priority + Area. **Não use `gh issue create`** (não adiciona ao board nem vincula o parent).
217
+ Para Features, pode usar o atalho `npx @spec-wave/cli feature --title ...` (equivale a `--type feature`).
218
+ A CLI cria a issue (label de tipo e de prioridade **apenas se `--priority` for informado**), vincula como sub-issue do parent, adiciona ao Project e define Etapa = 📥 Backlog + Work Item Type + Area (+ Priority só se informada). **Não use `gh issue create`** (não adiciona ao board nem vincula o parent).
217
219
  3. Informe o número criado e o vínculo com o pai (se houver).
218
220
  4. Para Features: "Quando quiser iniciar, mova para **📋 Spec** e use `/spec-wave spec <número>` para gerar a especificação funcional (o plano técnico vem depois)".
219
221
 
@@ -225,8 +227,8 @@ Remove a configuração do spec-wave do repositório (labels, arquivos `.github`
225
227
 
226
228
  **Passos:**
227
229
  1. Confirme com o usuário que ele quer remover (a ação remove labels e faz commits removendo os workflows).
228
- 2. Mostre antes o que será removido com `npx spec-wave uninstall --dry-run`.
229
- 3. Execute `npx spec-wave uninstall` (a CLI pede confirmação; use `--yes` só se o usuário já confirmou).
230
+ 2. Mostre antes o que será removido com `npx @spec-wave/cli uninstall --dry-run`.
231
+ 3. Execute `npx @spec-wave/cli uninstall` (a CLI pede confirmação; use `--yes` só se o usuário já confirmou).
230
232
  4. Lembre o usuário de excluir o **GitHub Project** manualmente, se desejar — a CLI não o apaga de propósito.
231
233
 
232
234
  ---
@@ -267,7 +269,7 @@ O plano técnico segue o schema do RFC-002 §3.2: **Estratégia Técnica** (com
267
269
 
268
270
  ### Tech Context (`.github/config/tech_context.yml`)
269
271
 
270
- Fonte de verdade estática da stack do sistema (RFC-002 §4). O `generate-plan` lê este arquivo para embasar o plano técnico e usar **APENAS** as tecnologias/serviços nele declarados — sem ele, o plano fica genérico e pode inventar APIs inexistentes. O `npx spec-wave init` gera um **scaffold de exemplo** que **deve ser adaptado** à stack real. Use este fluxo quando o arquivo estiver ausente ou desatualizado.
272
+ Fonte de verdade estática da stack do sistema (RFC-002 §4). O `generate-plan` lê este arquivo para embasar o plano técnico e usar **APENAS** as tecnologias/serviços nele declarados — sem ele, o plano fica genérico e pode inventar APIs inexistentes. O `npx @spec-wave/cli init` gera um **scaffold de exemplo** que **deve ser adaptado** à stack real. Use este fluxo quando o arquivo estiver ausente ou desatualizado.
271
273
 
272
274
  **Como ajudar a criar (quando não existir):**
273
275
 
@@ -361,17 +363,18 @@ Aciona o spec-kit para implementar uma **Story** (todas as suas Tasks) ou uma **
361
363
  1. Confirme que há `.spec-wave.json` no repo (senão, oriente `/spec-wave setup`).
362
364
  2. **Sempre comece com `--dry-run`** para inspecionar o que será feito — detecção do tipo, lista de Tasks coletadas (no caso de Story) e o comando do spec-kit que seria executado:
363
365
  ```bash
364
- npx spec-wave implement <número> --dry-run
366
+ npx @spec-wave/cli implement <número> --dry-run
365
367
  ```
366
- 3. Mostre ao usuário o contexto montado em `.spec-wave/implement-<número>.md` e o comando.
367
- 4. Se o usuário aprovar e o spec-kit estiver configurado, rode sem `--dry-run`:
368
+ 3. Mostre ao usuário o contexto montado em `.spec-wave/implement-<número>.md` e o comando. Esse arquivo contém as **instruções de execução sequencial**: implemente as Tasks **uma por vez** — mova a task para **🚧 Desenvolvimento** só ao iniciá-la e para **🎉 Done** ao concluí-la, antes de passar para a próxima. **Nunca** coloque várias tasks em "in progress" ao mesmo tempo.
369
+ 4. **Se você (agente) for implementar diretamente** (sem `specKit.command`): siga o contexto task por task, na ordem listada, respeitando o ciclo In Progress → implementar → Done de cada task antes da seguinte. Atualize o campo "Etapa" do item no board via `gh`. **Ao concluir toda a Story**: faça o commit, abra o PR e mova a **Feature** e a **Story** para **👀 Code Review** (as Tasks ficam em 🎉 Done).
370
+ 5. Se o usuário aprovar e o spec-kit estiver configurado, rode sem `--dry-run`:
368
371
  ```bash
369
- npx spec-wave implement <número>
372
+ npx @spec-wave/cli implement <número>
370
373
  ```
371
374
  - Se o spec-kit **não** estiver configurado, o comando só monta o contexto e mostra como configurar (`specKit.command` / `SPEC_WAVE_IMPLEMENT_CMD`). Ajude o usuário a definir o template (placeholders: `{tasksFile} {specFile} {planFile} {issue} {type} {title}`).
372
375
  - Use `--feature-dir docs/features/<slug>` se a resolução automática da Feature falhar (a skill avisa com warning) e você quiser anexar `spec.md`/`plan.md` como contexto.
373
- 5. Se a issue **não** for Story nem Task (ex.: Feature, Bug), o comando recusa — oriente o usuário: Features se decompõem (`/spec-wave decompose`); implemente as Stories/Tasks resultantes.
374
- 6. Após implementar: oriente revisar as mudanças, abrir o PR e mover o card para **👀 Code Review**.
376
+ 6. Se a issue **não** for Story nem Task (ex.: Feature, Bug), o comando recusa — oriente o usuário: Features se decompõem (`/spec-wave decompose`); implemente as Stories/Tasks resultantes.
377
+ 7. Ao final (Story implementada, commit feito, PR aberto e Feature/Story em **👀 Code Review**): confirme o resultado com o usuário e oriente a revisão do PR.
375
378
 
376
379
  ---
377
380