@spec-wave/cli 0.5.3 → 0.5.7

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.3",
3
+ "version": "0.5.7",
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": {
@@ -69,6 +69,7 @@ async function setCodeReview(token, project, etapaField, statusField, nodeId) {
69
69
 
70
70
  export async function codeReview({ prNumber }) {
71
71
  const token = await resolveToken();
72
+ const projectToken = process.env.PROJECT_TOKEN || token;
72
73
  const [envOwner, envRepo] = (process.env.GITHUB_REPOSITORY || '').split('/');
73
74
  const cfgPath = path.join(process.cwd(), CONFIG_FILE);
74
75
  let cfg = {};
@@ -110,8 +111,8 @@ export async function codeReview({ prNumber }) {
110
111
  }
111
112
 
112
113
  // Resolve campos uma vez, reutiliza em todas as Features.
113
- const etapaField = await resolveField(token, project, 'Etapa').catch(() => null);
114
- const statusField = await resolveField(token, project, 'Status').catch(() => null);
114
+ const etapaField = await resolveField(projectToken, project, 'Etapa').catch(() => null);
115
+ const statusField = await resolveField(projectToken, project, 'Status').catch(() => null);
115
116
 
116
117
  const seen = new Set();
117
118
  const updated = [];
@@ -121,7 +122,7 @@ export async function codeReview({ prNumber }) {
121
122
  if (!feature || seen.has(feature.number)) continue;
122
123
  seen.add(feature.number);
123
124
  try {
124
- await setCodeReview(token, project, etapaField, statusField, feature.node_id);
125
+ await setCodeReview(projectToken, project, etapaField, statusField, feature.node_id);
125
126
  updated.push(`#${feature.number} ${feature.title}`);
126
127
  console.log(`Feature #${feature.number} → "${CODE_REVIEW_STAGE}" / Status "${TODO_STATUS}".`);
127
128
  } catch (err) {
@@ -65,6 +65,9 @@ Regras:
65
65
 
66
66
  export async function decompose({ issueNumber }) {
67
67
  const token = await resolveToken();
68
+ // PROJECT_TOKEN deve ter scope "project" para atualizar GitHub Projects v2.
69
+ // Fallback para GITHUB_TOKEN (só funciona em repos pessoais sem org restrictions).
70
+ const projectToken = process.env.PROJECT_TOKEN || token;
68
71
  const [owner, repo] = (process.env.GITHUB_REPOSITORY || '').split('/');
69
72
 
70
73
  if (!owner || !repo) {
@@ -83,7 +86,7 @@ export async function decompose({ issueNumber }) {
83
86
  let statusField = null;
84
87
  if (project?.id && READY_STAGE) {
85
88
  try {
86
- statusField = await resolveStatusField(token, project);
89
+ statusField = await resolveStatusField(projectToken, project);
87
90
  } catch (err) {
88
91
  console.warn(`Não foi possível resolver campo Status do board: ${err.message}`);
89
92
  }
@@ -144,7 +147,7 @@ export async function decompose({ issueNumber }) {
144
147
 
145
148
  // Move story para Ready no board.
146
149
  try {
147
- await moveToStage(token, project, statusField, createdStory.nodeId, READY_STAGE);
150
+ await moveToStage(projectToken, project, statusField, createdStory.nodeId, READY_STAGE);
148
151
  } catch (err) {
149
152
  console.warn(` Falha ao mover story #${createdStory.number} para "${READY_STAGE}": ${err.message}`);
150
153
  }
@@ -164,7 +167,7 @@ export async function decompose({ issueNumber }) {
164
167
 
165
168
  // Move task para Ready no board.
166
169
  try {
167
- await moveToStage(token, project, statusField, createdTask.nodeId, READY_STAGE);
170
+ await moveToStage(projectToken, project, statusField, createdTask.nodeId, READY_STAGE);
168
171
  } catch (err) {
169
172
  console.warn(` Falha ao mover task #${createdTask.number} para "${READY_STAGE}": ${err.message}`);
170
173
  }
@@ -173,7 +176,7 @@ export async function decompose({ issueNumber }) {
173
176
 
174
177
  // Move a própria Feature para Ready no board.
175
178
  try {
176
- await moveToStage(token, project, statusField, featureNodeId, READY_STAGE);
179
+ await moveToStage(projectToken, project, statusField, featureNodeId, READY_STAGE);
177
180
  if (project?.id && statusField) console.log(`Feature movida para "${READY_STAGE}" no board.`);
178
181
  } catch (err) {
179
182
  console.warn(`Falha ao mover Feature para "${READY_STAGE}": ${err.message}`);
@@ -85,7 +85,7 @@ export async function generatePlan({ issueNumber }) {
85
85
  await commentOnIssue(
86
86
  token, owner, repo, parseInt(issueNumber, 10),
87
87
  `📋 **plan.md gerado automaticamente!**\n\n` +
88
- `📄 Arquivo: [\`${filePath}\`](${filePath})\n\n` +
88
+ `📄 Arquivo: [\`${filePath}\`](https://github.com/${owner}/${repo}/blob/main/${filePath})\n\n` +
89
89
  `Revise o plano e, quando estiver pronto, valide a Feature: mova o card para **✅ Ready** ou use:\n` +
90
90
  `\`\`\`\ngh issue edit ${issueNumber} --add-label "spec-wave:ready"\n\`\`\``
91
91
  );
@@ -78,7 +78,7 @@ export async function generateSpec({ issueNumber }) {
78
78
  await commentOnIssue(
79
79
  token, owner, repo, parseInt(issueNumber, 10),
80
80
  `📋 **spec.md gerado automaticamente!**\n\n` +
81
- `📄 Arquivo: [\`${filePath}\`](${filePath})\n\n` +
81
+ `📄 Arquivo: [\`${filePath}\`](https://github.com/${owner}/${repo}/blob/main/${filePath})\n\n` +
82
82
  `Revise a especificação e, quando estiver pronto, gere o plano técnico: mova o card para **📋 Plan** ou use:\n` +
83
83
  `\`\`\`\ngh issue edit ${issueNumber} --add-label "spec-wave:plan"\n\`\`\``
84
84
  );
@@ -64,6 +64,7 @@ async function setQA(token, project, etapaField, statusField, nodeId) {
64
64
 
65
65
  export async function qa({ prNumber }) {
66
66
  const token = await resolveToken();
67
+ const projectToken = process.env.PROJECT_TOKEN || token;
67
68
  const [envOwner, envRepo] = (process.env.GITHUB_REPOSITORY || '').split('/');
68
69
  const cfgPath = path.join(process.cwd(), CONFIG_FILE);
69
70
  let cfg = {};
@@ -103,8 +104,8 @@ export async function qa({ prNumber }) {
103
104
  return;
104
105
  }
105
106
 
106
- const etapaField = await resolveField(token, project, 'Etapa').catch(() => null);
107
- const statusField = await resolveField(token, project, 'Status').catch(() => null);
107
+ const etapaField = await resolveField(projectToken, project, 'Etapa').catch(() => null);
108
+ const statusField = await resolveField(projectToken, project, 'Status').catch(() => null);
108
109
 
109
110
  const seen = new Set();
110
111
  const updated = [];
@@ -114,7 +115,7 @@ export async function qa({ prNumber }) {
114
115
  if (!feature || seen.has(feature.number)) continue;
115
116
  seen.add(feature.number);
116
117
  try {
117
- await setQA(token, project, etapaField, statusField, feature.node_id);
118
+ await setQA(projectToken, project, etapaField, statusField, feature.node_id);
118
119
  updated.push(`#${feature.number} ${feature.title}`);
119
120
  console.log(`Feature #${feature.number} → "${QA_STAGE}" / Status "${TODO_STATUS}".`);
120
121
  } catch (err) {
@@ -17,10 +17,11 @@ jobs:
17
17
 
18
18
  - uses: actions/setup-node@v4
19
19
  with:
20
- node-version: '20'
20
+ node-version: '24'
21
21
 
22
22
  - name: Move Feature to Code Review
23
23
  run: npx @spec-wave/cli code-review --pr-number ${{ github.event.pull_request.number }}
24
24
  env:
25
25
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
26
+ PROJECT_TOKEN: ${{ secrets.GH_PROJECT_TOKEN }}
26
27
  GITHUB_REPOSITORY: ${{ github.repository }}
@@ -19,12 +19,13 @@ jobs:
19
19
 
20
20
  - uses: actions/setup-node@v4
21
21
  with:
22
- node-version: '20'
22
+ node-version: '24'
23
23
 
24
24
  - name: Decompose into Stories and Tasks
25
25
  run: npx @spec-wave/cli decompose --issue-number ${{ github.event.issue.number }}
26
26
  env:
27
27
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28
+ PROJECT_TOKEN: ${{ secrets.GH_PROJECT_TOKEN }}
28
29
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
29
30
  OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
30
31
  GITHUB_REPOSITORY: ${{ github.repository }}
@@ -21,7 +21,7 @@ jobs:
21
21
 
22
22
  - uses: actions/setup-node@v4
23
23
  with:
24
- node-version: '20'
24
+ node-version: '24'
25
25
 
26
26
  - name: Generate plan.md
27
27
  run: npx @spec-wave/cli generate-plan --issue-number ${{ github.event.issue.number }}
@@ -21,7 +21,7 @@ jobs:
21
21
 
22
22
  - uses: actions/setup-node@v4
23
23
  with:
24
- node-version: '20'
24
+ node-version: '24'
25
25
 
26
26
  - name: Generate spec.md
27
27
  run: npx @spec-wave/cli generate-spec --issue-number ${{ github.event.issue.number }}
@@ -18,10 +18,11 @@ jobs:
18
18
 
19
19
  - uses: actions/setup-node@v4
20
20
  with:
21
- node-version: '20'
21
+ node-version: '24'
22
22
 
23
23
  - name: Move Feature to QA
24
24
  run: npx @spec-wave/cli qa --pr-number ${{ github.event.pull_request.number }}
25
25
  env:
26
26
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27
+ PROJECT_TOKEN: ${{ secrets.GH_PROJECT_TOKEN }}
27
28
  GITHUB_REPOSITORY: ${{ github.repository }}
@@ -12,13 +12,14 @@ jobs:
12
12
  runs-on: ubuntu-latest
13
13
  permissions:
14
14
  issues: write
15
+ contents: read
15
16
 
16
17
  steps:
17
18
  - uses: actions/checkout@v4
18
19
 
19
20
  - uses: actions/setup-node@v4
20
21
  with:
21
- node-version: '20'
22
+ node-version: '24'
22
23
 
23
24
  - name: Validate spec and plan
24
25
  run: npx @spec-wave/cli validate --issue-number ${{ github.event.issue.number }}