@spec-wave/cli 0.5.1 → 0.5.3

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.1",
3
+ "version": "0.5.3",
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": {
@@ -75,6 +75,7 @@ export async function generatePlan({ issueNumber }) {
75
75
  git(`git config user.name "spec-wave[bot]"`);
76
76
  git(`git add "${filePath}"`);
77
77
  git(`git commit -m "docs: generate plan.md for ${slug} [spec-wave]"`);
78
+ git('git pull --rebase');
78
79
  git('git push');
79
80
 
80
81
  // Remove trigger label
@@ -68,6 +68,7 @@ export async function generateSpec({ issueNumber }) {
68
68
  git(`git config user.name "spec-wave[bot]"`);
69
69
  git(`git add "${filePath}"`);
70
70
  git(`git commit -m "docs: generate spec.md for ${slug} [spec-wave]"`);
71
+ git('git pull --rebase');
71
72
  git('git push');
72
73
 
73
74
  // Remove trigger label
@@ -2,54 +2,9 @@ import { existsSync, readFileSync } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { resolveToken } from '../api/auth.mjs';
4
4
  import { getIssue, removeLabel, addLabel, commentOnIssue } from '../api/github-rest.mjs';
5
- import { addProjectItem, setItemSingleSelect, getSingleSelectField } from '../api/github-graphql.mjs';
6
5
  import { slugify } from '../lib/slugify.mjs';
7
6
  import { CONFIG_FILE, REQUIRED_PLAN_SECTIONS, REQUIRED_SPEC_SECTIONS } from '../config.mjs';
8
7
 
9
- // Opção nativa do campo Status do GitHub Projects (Todo / In Progress / Done).
10
- const DONE_STAGE = 'Done';
11
-
12
- // Resolve um campo SINGLE_SELECT pelo nome: usa o .spec-wave.json, cai para o
13
- // formato legado (etapaFieldId/stageOptions) e, por fim, consulta o Project.
14
- async function resolveField(token, project, name) {
15
- if (project.fields && project.fields[name]) return project.fields[name];
16
- if (name === 'Etapa' && project.etapaFieldId) {
17
- return { id: project.etapaFieldId, options: project.stageOptions || {} };
18
- }
19
- return await getSingleSelectField(token, project.id, name);
20
- }
21
-
22
- // Move o item da issue para a Etapa "🎉 Done" no board. Best-effort: loga e
23
- // segue se o .spec-wave.json não tiver o Project ou o campo não for encontrado.
24
- async function moveToDone(token, issue) {
25
- const configPath = path.join(process.cwd(), CONFIG_FILE);
26
- if (!existsSync(configPath)) {
27
- console.warn(`${CONFIG_FILE} não encontrado — status do board não atualizado.`);
28
- return;
29
- }
30
- let project;
31
- try {
32
- project = (JSON.parse(readFileSync(configPath, 'utf-8')).project) || {};
33
- } catch (err) {
34
- console.warn(`${CONFIG_FILE} corrompido (${err.message}) — status do board não atualizado.`);
35
- return;
36
- }
37
- if (!project.id) {
38
- console.warn(`Project não configurado no ${CONFIG_FILE} — status do board não atualizado.`);
39
- return;
40
- }
41
- // addProjectItem é idempotente: retorna o item existente se a issue já está no board.
42
- const itemId = await addProjectItem(token, project.id, issue.node_id);
43
- const field = await resolveField(token, project, 'Status');
44
- const optionId = field?.options?.[DONE_STAGE];
45
- if (field?.id && optionId) {
46
- await setItemSingleSelect(token, project.id, itemId, field.id, optionId);
47
- console.log(`Status do board atualizado para "${DONE_STAGE}".`);
48
- } else {
49
- console.warn(`Etapa "${DONE_STAGE}" não encontrada no Project — status do board não atualizado.`);
50
- }
51
- }
52
-
53
8
  export async function validate({ issueNumber }) {
54
9
  const token = await resolveToken();
55
10
  const [envOwner, envRepo] = (process.env.GITHUB_REPOSITORY || '').split('/');
@@ -114,21 +69,14 @@ export async function validate({ issueNumber }) {
114
69
  process.exit(1);
115
70
  }
116
71
 
117
- // Validação passou: move a issue para "🎉 Done" no board (best-effort).
118
- let doneOk = false;
119
- try {
120
- await moveToDone(token, issue);
121
- doneOk = !!DONE_STAGE;
122
- } catch (err) {
123
- console.warn(`Falha ao atualizar status do board: ${err.message}`);
124
- }
72
+ // Validação passou: adiciona label plan-approved.
73
+ await addLabel(token, owner, repo, parseInt(issueNumber, 10), 'spec-wave:plan-approved');
125
74
 
126
75
  await commentOnIssue(
127
76
  token, owner, repo, parseInt(issueNumber, 10),
128
77
  `✅ **Validação concluída com sucesso!**\n\n` +
129
78
  `- [\`${specPath}\`](${specPath}) ✓\n` +
130
79
  `- [\`${planPath}\`](${planPath}) ✓\n\n` +
131
- (doneOk ? `Status movido para **${DONE_STAGE}**. ` : '') +
132
80
  `A Feature está pronta para decomposição. Use:\n` +
133
81
  `\`\`\`\ngh issue edit ${issueNumber} --add-label "spec-wave:decompose"\n\`\`\``
134
82
  );
package/src/config.mjs CHANGED
@@ -129,10 +129,11 @@ export const PRIORITY_LABELS = [
129
129
  ];
130
130
 
131
131
  export const TRIGGER_LABELS = [
132
- { name: 'spec-wave:spec', color: 'BFD4F2', description: 'Gerar spec.md via GitHub Action' },
133
- { name: 'spec-wave:plan', color: 'BFD4F2', description: 'Gerar plan.md via GitHub Action' },
134
- { name: 'spec-wave:ready', color: '0E8A16', description: 'Validar spec+plan e mover para Ready' },
135
- { name: 'spec-wave:decompose', color: 'BFD4F2', description: 'Decompor em Stories e Tasks' },
132
+ { name: 'spec-wave:spec', color: 'BFD4F2', description: 'Gerar spec.md via GitHub Action' },
133
+ { name: 'spec-wave:plan', color: 'BFD4F2', description: 'Gerar plan.md via GitHub Action' },
134
+ { name: 'spec-wave:ready', color: '0E8A16', description: 'Validar spec+plan e mover para Ready' },
135
+ { name: 'spec-wave:plan-approved', color: '0E8A16', description: 'Spec+plan validados com sucesso' },
136
+ { name: 'spec-wave:decompose', color: 'BFD4F2', description: 'Decompor em Stories e Tasks' },
136
137
  ];
137
138
 
138
139
  export const ALL_LABELS = [...TYPE_LABELS, ...PRIORITY_LABELS, ...TRIGGER_LABELS];