@spec-wave/cli 0.34.0 → 0.34.2

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.34.0",
3
+ "version": "0.34.2",
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": {
@@ -36,7 +36,7 @@ import { resolveDocDir } from '../lib/doc-paths.mjs';
36
36
  import { docBlobUrl } from '../lib/repo-links.mjs';
37
37
  import { detectIssueType } from '../lib/issue-type.mjs';
38
38
  import { loadConfig } from '../lib/project-root.mjs';
39
- import { resolveFlowContext } from '../lib/flow-run.mjs';
39
+ import { executionMode, missingRootMessage, resolveFlowContext } from '../lib/flow-run.mjs';
40
40
  import { publishArtifact } from '../lib/artifact-publish.mjs';
41
41
  import { buildDependencyMap, storiesFromAppliedDoc, DEPENDENCY_MAP_FILE } from '../lib/dependency-map.mjs';
42
42
  import { invalidateCache } from '../lib/net-cache.mjs';
@@ -1136,6 +1136,7 @@ export async function decompose({ issueNumber, apply }) {
1136
1136
 
1137
1137
  // Config do repo: raiz para os caminhos de documento e escalada da crítica.
1138
1138
  const { config, root } = loadConfig();
1139
+ if (!root) throw new Error(missingRootMessage('decompose', executionMode()));
1139
1140
  const { rel: docRel } = resolveDocDir(root, issue, type);
1140
1141
 
1141
1142
  // Branch base: é dela que os documentos são lidos quando não estão no clone, e
@@ -52,7 +52,7 @@ export async function generateBug({ issueNumber }) {
52
52
  // Mesmo contexto dos outros geradores: GITHUB_REPOSITORY quando existe, o
53
53
  // `.spec-wave.json` quando não. Este comando exigia a env crua e morria fora
54
54
  // do runner mandando exportá-la — dentro de um repo que já sabe seu owner/repo.
55
- const { owner, repo, root, config, mode } = resolveFlowContext({ command: 'generate-bug' });
55
+ const { owner, repo, root, config, mode } = resolveFlowContext({ command: 'generate-bug', requireRoot: true });
56
56
  const n = parseInt(issueNumber, 10);
57
57
 
58
58
  console.log(`Buscando issue #${n}...`);
@@ -132,7 +132,7 @@ async function critiquePlan({
132
132
  export async function generatePlan({ issueNumber }) {
133
133
  const token = await resolveToken();
134
134
  // Roda nos dois modos: no Action (disparado por label) e na sessão local.
135
- const { owner, repo, root, config, mode } = resolveFlowContext({ command: 'generate-plan' });
135
+ const { owner, repo, root, config, mode } = resolveFlowContext({ command: 'generate-plan', requireRoot: true });
136
136
  console.log(`Modo de execução: ${mode}`);
137
137
 
138
138
  console.log(`Buscando issue #${issueNumber}...`);
@@ -384,7 +384,7 @@ export async function critique({ issueNumber, file, kind, failOnGrave = false })
384
384
  }
385
385
 
386
386
  const token = await resolveToken();
387
- const { owner, repo, root, config, mode } = resolveFlowContext({ command: 'critique' });
387
+ const { owner, repo, root, config, mode } = resolveFlowContext({ command: 'critique', requireRoot: true });
388
388
  console.log(`Modo de execução: ${mode}`);
389
389
 
390
390
  const n = parseInt(issueNumber, 10);
@@ -27,7 +27,7 @@ import { recordUsage } from '../lib/usage-report.mjs';
27
27
  import { resolveDocDir } from '../lib/doc-paths.mjs';
28
28
  import { docBlobUrl } from '../lib/repo-links.mjs';
29
29
  import { loadConfig } from '../lib/project-root.mjs';
30
- import { resolveFlowContext } from '../lib/flow-run.mjs';
30
+ import { executionMode, missingRootMessage, resolveFlowContext } from '../lib/flow-run.mjs';
31
31
  import { publishArtifact } from '../lib/artifact-publish.mjs';
32
32
  import { loadArtifact, isAwaitingMerge } from '../lib/doc-source.mjs';
33
33
  import { awaitingMergeBlock, renderPrLine } from '../lib/artifact-pr.mjs';
@@ -121,6 +121,7 @@ export async function generateQaPlan({ issueNumber }) {
121
121
  }
122
122
 
123
123
  const { config, root } = loadConfig();
124
+ if (!root) throw new Error(missingRootMessage('generate-qa-plan', executionMode()));
124
125
  const { rel: dirRel } = resolveDocDir(root, issue, type);
125
126
  const docRel = `${dirRel}/${QA_PLAN_FILE}`;
126
127
  const base = await getRepoDefaultBranch(token, owner, repo).catch(() => null);
@@ -32,7 +32,7 @@ function formatLintWarning(lintFindings) {
32
32
  export async function generateSpec({ issueNumber }) {
33
33
  const token = await resolveToken();
34
34
  // Roda nos dois modos: no Action (disparado por label) e na sessão local.
35
- const { owner, repo, root, mode } = resolveFlowContext({ command: 'generate-spec' });
35
+ const { owner, repo, root, mode } = resolveFlowContext({ command: 'generate-spec', requireRoot: true });
36
36
  console.log(`Modo de execução: ${mode}`);
37
37
 
38
38
  console.log(`Buscando issue #${issueNumber}...`);
@@ -43,9 +43,14 @@ export function executionMode(env = process.env) {
43
43
  * @param {string} [opts.cwd]
44
44
  * @param {string} [opts.command] nome do comando, para a mensagem de erro
45
45
  * @param {object} [opts.env]
46
+ * @param {boolean} [opts.requireRoot] comandos que ancoram caminhos/prompts na
47
+ * raiz devem exigi-la: sem isto um repo nunca inicializado (checkout
48
+ * dos Actions sem .spec-wave.json) seguia com root=null e morria três
49
+ * chamadas depois com `path.join(null, …)` — "The path argument must
50
+ * be of type string. Received null" (caso real, 2026-09-06).
46
51
  * @returns {{owner: string, repo: string, root: string|null, config: object|null, mode: 'actions'|'local'}}
47
52
  */
48
- export function resolveFlowContext({ cwd = process.cwd(), command = 'este comando', env = process.env } = {}) {
53
+ export function resolveFlowContext({ cwd = process.cwd(), command = 'este comando', env = process.env, requireRoot = false } = {}) {
49
54
  // Repassa o `env` recebido: sem isso o modo local fica intestável dentro do
50
55
  // GitHub Actions, que define GITHUB_REPOSITORY em toda execução.
51
56
  const { owner, repo, root, config } = resolveRepoContext(cwd, env);
@@ -61,5 +66,26 @@ export function resolveFlowContext({ cwd = process.cwd(), command = 'este comand
61
66
  ` GITHUB_REPOSITORY=owner/repo spec-wave ${command} --issue-number 1`)
62
67
  );
63
68
  }
69
+ if (requireRoot && !root) {
70
+ throw new Error(missingRootMessage(command, mode));
71
+ }
64
72
  return { owner, repo, root, config, mode };
65
73
  }
74
+
75
+ /**
76
+ * Mensagem de "repositório não inicializado" — compartilhada com os comandos
77
+ * que resolvem a raiz via loadConfig() direto (decompose, generate-qa-plan).
78
+ *
79
+ * @param {string} command
80
+ * @param {'actions'|'local'} mode
81
+ * @returns {string}
82
+ */
83
+ export function missingRootMessage(command, mode) {
84
+ return (
85
+ `${CONFIG_FILE} não encontrado — spec-wave ${command} precisa da raiz do projeto.\n` +
86
+ (mode === 'actions'
87
+ ? `Este repositório ainda não foi inicializado para o spec-wave: rode \`spec-wave init\` ` +
88
+ `no repositório e commite o ${CONFIG_FILE}.`
89
+ : `Rode \`spec-wave init\` na raiz do repositório primeiro.`)
90
+ );
91
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "spec-wave",
3
3
  "displayName": "Spec Wave",
4
- "version": "0.34.0",
4
+ "version": "0.34.2",
5
5
  "description": "Fluxo spec-driven no GitHub (RFC-001): Projects v2, labels de gatilho, spec/plan gerados por Action, decomposição em duas etapas e implementação orientada a Stories/Tasks.",
6
6
  "author": {
7
7
  "name": "Astratech",
@@ -35,8 +35,8 @@ jobs:
35
35
  outputs:
36
36
  feature: ${{ steps.resolve.outputs.feature }}
37
37
  steps:
38
- - uses: actions/checkout@v4
39
- - uses: actions/setup-node@v4
38
+ - uses: actions/checkout@v5
39
+ - uses: actions/setup-node@v5
40
40
  with:
41
41
  node-version: '24'
42
42
 
@@ -69,9 +69,9 @@ jobs:
69
69
  contents: read
70
70
 
71
71
  steps:
72
- - uses: actions/checkout@v4
72
+ - uses: actions/checkout@v5
73
73
 
74
- - uses: actions/setup-node@v4
74
+ - uses: actions/setup-node@v5
75
75
  with:
76
76
  node-version: '24'
77
77
 
@@ -39,11 +39,11 @@ jobs:
39
39
  contents: read
40
40
 
41
41
  steps:
42
- - uses: actions/checkout@v4
42
+ - uses: actions/checkout@v5
43
43
  with:
44
44
  token: ${{ secrets.GITHUB_TOKEN }}
45
45
 
46
- - uses: actions/setup-node@v4
46
+ - uses: actions/setup-node@v5
47
47
  with:
48
48
  node-version: '24'
49
49
 
@@ -70,11 +70,11 @@ jobs:
70
70
  pull-requests: write
71
71
 
72
72
  steps:
73
- - uses: actions/checkout@v4
73
+ - uses: actions/checkout@v5
74
74
  with:
75
75
  token: ${{ secrets.GITHUB_TOKEN }}
76
76
 
77
- - uses: actions/setup-node@v4
77
+ - uses: actions/setup-node@v5
78
78
  with:
79
79
  node-version: '24'
80
80
 
@@ -54,11 +54,11 @@ jobs:
54
54
  pull-requests: write
55
55
 
56
56
  steps:
57
- - uses: actions/checkout@v4
57
+ - uses: actions/checkout@v5
58
58
  with:
59
59
  token: ${{ secrets.GITHUB_TOKEN }}
60
60
 
61
- - uses: actions/setup-node@v4
61
+ - uses: actions/setup-node@v5
62
62
  with:
63
63
  node-version: '24'
64
64
 
@@ -64,11 +64,11 @@ jobs:
64
64
  pull-requests: write
65
65
 
66
66
  steps:
67
- - uses: actions/checkout@v4
67
+ - uses: actions/checkout@v5
68
68
  with:
69
69
  token: ${{ secrets.GITHUB_TOKEN }}
70
70
 
71
- - uses: actions/setup-node@v4
71
+ - uses: actions/setup-node@v5
72
72
  with:
73
73
  node-version: '24'
74
74
 
@@ -41,11 +41,11 @@ jobs:
41
41
  pull-requests: write
42
42
 
43
43
  steps:
44
- - uses: actions/checkout@v4
44
+ - uses: actions/checkout@v5
45
45
  with:
46
46
  token: ${{ secrets.GITHUB_TOKEN }}
47
47
 
48
- - uses: actions/setup-node@v4
48
+ - uses: actions/setup-node@v5
49
49
  with:
50
50
  node-version: '24'
51
51
 
@@ -64,11 +64,11 @@ jobs:
64
64
  pull-requests: write
65
65
 
66
66
  steps:
67
- - uses: actions/checkout@v4
67
+ - uses: actions/checkout@v5
68
68
  with:
69
69
  token: ${{ secrets.GITHUB_TOKEN }}
70
70
 
71
- - uses: actions/setup-node@v4
71
+ - uses: actions/setup-node@v5
72
72
  with:
73
73
  node-version: '24'
74
74
 
@@ -44,9 +44,9 @@ jobs:
44
44
  contents: read
45
45
 
46
46
  steps:
47
- - uses: actions/checkout@v4
47
+ - uses: actions/checkout@v5
48
48
 
49
- - uses: actions/setup-node@v4
49
+ - uses: actions/setup-node@v5
50
50
  with:
51
51
  node-version: '24'
52
52
 
@@ -38,9 +38,9 @@ jobs:
38
38
  contents: read
39
39
 
40
40
  steps:
41
- - uses: actions/checkout@v4
41
+ - uses: actions/checkout@v5
42
42
 
43
- - uses: actions/setup-node@v4
43
+ - uses: actions/setup-node@v5
44
44
  with:
45
45
  node-version: '24'
46
46