@spec-wave/cli 0.1.0
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/bin/spec-wave.mjs +125 -0
- package/package.json +27 -0
- package/src/api/auth.mjs +36 -0
- package/src/api/github-graphql.mjs +238 -0
- package/src/api/github-rest.mjs +157 -0
- package/src/commands/decompose.mjs +113 -0
- package/src/commands/feature.mjs +7 -0
- package/src/commands/generate-plan.mjs +71 -0
- package/src/commands/generate-spec.mjs +77 -0
- package/src/commands/info.mjs +53 -0
- package/src/commands/init.mjs +191 -0
- package/src/commands/issue.mjs +186 -0
- package/src/commands/refresh.mjs +116 -0
- package/src/commands/uninstall.mjs +132 -0
- package/src/commands/validate.mjs +77 -0
- package/src/config.mjs +131 -0
- package/src/lib/claude.mjs +25 -0
- package/src/lib/slugify.mjs +13 -0
- package/src/setup/files.mjs +63 -0
- package/src/setup/labels.mjs +17 -0
- package/src/setup/project.mjs +62 -0
- package/src/templates/issue/plan-template.md +35 -0
- package/src/templates/issue/spec-template.md +32 -0
- package/src/templates/workflows/decompose.yml +29 -0
- package/src/templates/workflows/generate-plan.yml +31 -0
- package/src/templates/workflows/generate-spec.yml +31 -0
- package/src/templates/workflows/validate.yml +27 -0
- package/src/ui/wizard.mjs +83 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Especificação Funcional (spec.md)"
|
|
3
|
+
about: "Especificação funcional de uma Feature. Gerado automaticamente ao mover para a coluna 📋 Spec."
|
|
4
|
+
title: "[FEATURE] "
|
|
5
|
+
labels: "[FEATURE]"
|
|
6
|
+
assignees: ""
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Objetivo
|
|
10
|
+
|
|
11
|
+
<!-- O que esta Feature entrega e qual problema resolve? -->
|
|
12
|
+
|
|
13
|
+
# Regras de Negócio
|
|
14
|
+
|
|
15
|
+
<!-- Liste as regras que governam o comportamento desta Feature -->
|
|
16
|
+
|
|
17
|
+
# Fluxos
|
|
18
|
+
|
|
19
|
+
<!-- Descreva o fluxo principal e os fluxos alternativos -->
|
|
20
|
+
|
|
21
|
+
# Critérios de Aceite
|
|
22
|
+
|
|
23
|
+
- [ ]
|
|
24
|
+
- [ ]
|
|
25
|
+
|
|
26
|
+
# Casos de Erro
|
|
27
|
+
|
|
28
|
+
<!-- O que acontece quando algo dá errado? Como o sistema se comporta? -->
|
|
29
|
+
|
|
30
|
+
# Dependências
|
|
31
|
+
|
|
32
|
+
<!-- Outras Features, integrações, APIs externas ou pré-requisitos -->
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Decompose Feature
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issues:
|
|
5
|
+
types: [labeled]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
decompose:
|
|
9
|
+
if: >
|
|
10
|
+
github.event.label.name == 'spec-wave:decompose' &&
|
|
11
|
+
contains(github.event.issue.title, '[FEATURE]')
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
permissions:
|
|
14
|
+
issues: write
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- uses: actions/setup-node@v4
|
|
21
|
+
with:
|
|
22
|
+
node-version: '20'
|
|
23
|
+
|
|
24
|
+
- name: Decompose into Stories and Tasks
|
|
25
|
+
run: npx @spec-wave/cli decompose --issue-number ${{ github.event.issue.number }}
|
|
26
|
+
env:
|
|
27
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
28
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
29
|
+
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Generate Plan
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issues:
|
|
5
|
+
types: [labeled]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
generate-plan:
|
|
9
|
+
if: >
|
|
10
|
+
github.event.label.name == 'spec-wave:plan' &&
|
|
11
|
+
contains(github.event.issue.title, '[FEATURE]')
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
permissions:
|
|
14
|
+
issues: write
|
|
15
|
+
contents: write
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
with:
|
|
20
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
21
|
+
|
|
22
|
+
- uses: actions/setup-node@v4
|
|
23
|
+
with:
|
|
24
|
+
node-version: '20'
|
|
25
|
+
|
|
26
|
+
- name: Generate plan.md
|
|
27
|
+
run: npx @spec-wave/cli generate-plan --issue-number ${{ github.event.issue.number }}
|
|
28
|
+
env:
|
|
29
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
30
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
31
|
+
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Generate Spec
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issues:
|
|
5
|
+
types: [labeled]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
generate-spec:
|
|
9
|
+
if: >
|
|
10
|
+
github.event.label.name == 'spec-wave:spec' &&
|
|
11
|
+
contains(github.event.issue.title, '[FEATURE]')
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
permissions:
|
|
14
|
+
issues: write
|
|
15
|
+
contents: write
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
with:
|
|
20
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
21
|
+
|
|
22
|
+
- uses: actions/setup-node@v4
|
|
23
|
+
with:
|
|
24
|
+
node-version: '20'
|
|
25
|
+
|
|
26
|
+
- name: Generate spec.md
|
|
27
|
+
run: npx @spec-wave/cli generate-spec --issue-number ${{ github.event.issue.number }}
|
|
28
|
+
env:
|
|
29
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
30
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
31
|
+
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Validate Spec + Plan
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issues:
|
|
5
|
+
types: [labeled]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
validate:
|
|
9
|
+
if: >
|
|
10
|
+
github.event.label.name == 'spec-wave:ready' &&
|
|
11
|
+
contains(github.event.issue.title, '[FEATURE]')
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
permissions:
|
|
14
|
+
issues: write
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-node@v4
|
|
20
|
+
with:
|
|
21
|
+
node-version: '20'
|
|
22
|
+
|
|
23
|
+
- name: Validate spec and plan
|
|
24
|
+
run: npx @spec-wave/cli validate --issue-number ${{ github.event.issue.number }}
|
|
25
|
+
env:
|
|
26
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
27
|
+
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
|
|
4
|
+
export async function runWizard() {
|
|
5
|
+
p.intro('spec-wave — configuração do fluxo spec-driven');
|
|
6
|
+
|
|
7
|
+
const answers = await p.group(
|
|
8
|
+
{
|
|
9
|
+
repo: () =>
|
|
10
|
+
p.text({
|
|
11
|
+
message: 'Repositório GitHub (owner/repo):',
|
|
12
|
+
placeholder: detectRepo(),
|
|
13
|
+
defaultValue: detectRepo(),
|
|
14
|
+
validate: v => {
|
|
15
|
+
if (!v || !v.includes('/')) return 'Formato esperado: owner/repo';
|
|
16
|
+
},
|
|
17
|
+
}),
|
|
18
|
+
|
|
19
|
+
projectTitle: ({ results }) =>
|
|
20
|
+
p.text({
|
|
21
|
+
message: 'Nome do GitHub Project:',
|
|
22
|
+
defaultValue: `${results.repo?.split('/')[1] ?? 'projeto'} — Spec Wave`,
|
|
23
|
+
placeholder: 'Meu Projeto — Spec Wave',
|
|
24
|
+
}),
|
|
25
|
+
|
|
26
|
+
triggerStrategy: () =>
|
|
27
|
+
p.select({
|
|
28
|
+
message: 'Como prefere acionar os workflows automáticos?',
|
|
29
|
+
options: [
|
|
30
|
+
{
|
|
31
|
+
value: 'labels',
|
|
32
|
+
label: 'Labels (recomendado)',
|
|
33
|
+
hint: 'A skill adiciona labels automaticamente ao mover cards',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
value: 'webhook',
|
|
37
|
+
label: 'Webhook (em breve)',
|
|
38
|
+
hint: 'Acionamento automático ao mover card no board — requer endpoint externo',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
}),
|
|
42
|
+
|
|
43
|
+
confirm: ({ results }) =>
|
|
44
|
+
p.confirm({
|
|
45
|
+
message: `Configurar ${results.repo} com GitHub Project "${results.projectTitle}"?\n - 12 colunas kanban\n - 5 campos customizados\n - 15 labels\n - 4 workflows + 2 issue templates`,
|
|
46
|
+
initialValue: true,
|
|
47
|
+
}),
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
onCancel: () => {
|
|
51
|
+
p.cancel('Operação cancelada.');
|
|
52
|
+
process.exit(0);
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
if (!answers.confirm) {
|
|
58
|
+
p.cancel('Operação cancelada.');
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (answers.triggerStrategy === 'webhook') {
|
|
63
|
+
p.note(
|
|
64
|
+
'Webhook support está planejado para uma versão futura.\nUsando labels por enquanto.',
|
|
65
|
+
'Aviso'
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const [owner, repo] = answers.repo.split('/');
|
|
70
|
+
return { owner, repo, projectTitle: answers.projectTitle };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function detectRepo() {
|
|
74
|
+
try {
|
|
75
|
+
const remote = execSync('git remote get-url origin', { stdio: ['pipe', 'pipe', 'pipe'] })
|
|
76
|
+
.toString()
|
|
77
|
+
.trim();
|
|
78
|
+
const match = remote.match(/github\.com[:/]([^/]+\/[^/.]+)/);
|
|
79
|
+
return match ? match[1] : '';
|
|
80
|
+
} catch {
|
|
81
|
+
return '';
|
|
82
|
+
}
|
|
83
|
+
}
|