jarvis-ai-framework 3.2.0 → 5.0.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/agents/engineering/eng.docs-consolidator.agent.md +81 -0
- package/agents/engineering/qa/eng.qa.test-planner.md +7 -7
- package/agents/product/prod.discovery-interviewer.md +1 -1
- package/bin/README.md +14 -14
- package/bin/commands/docs-publish.js +61 -93
- package/bin/commands/docs-sync.js +31 -63
- package/bin/lib/cli/program.js +23 -25
- package/bin/lib/docs/fetch-file.js +29 -26
- package/bin/lib/docs/publish-file.js +98 -159
- package/bin/lib/docs/storage/build-index.js +108 -0
- package/bin/lib/docs/storage/filesystem.js +203 -0
- package/bin/lib/docs/storage/index.js +75 -0
- package/bin/lib/docs/validate-frontmatter.js +18 -4
- package/bin/lib/vcs/api.js +0 -125
- package/package.json +2 -2
- package/rules/AGENTS.md +12 -9
- package/rules/engineering/eng.docs-scraping-rules.md +4 -4
- package/rules/engineering/eng.integrations-rules.md +3 -2
- package/rules/engineering/rpa/eng.rpa-rules.md +1 -1
- package/rules/product/prod-rules.md +5 -6
- package/rules-on-demand/engineering/eng.pr-rules.md +9 -31
- package/rules-on-demand/engineering/eng.pre-pr-rules.md +12 -19
- package/rules-on-demand/product/prod.guided-rules.md +104 -0
- package/skills/eng-backend-arch-c4/SKILL.md +3 -3
- package/skills/eng-global-docs-write/SKILL.md +1 -1
- package/skills/eng-qa-planner/SKILL.md +5 -5
- package/skills/jarvis-docs-central/README.md +51 -275
- package/skills/jarvis-docs-central/SKILL.md +114 -301
- package/skills/jarvis-init/SKILL.md +81 -8
- package/templates/ENV-template.md +28 -7
- package/templates/engineering/ARD-template.md +40 -7
- package/templates/engineering/architecture-template.md +1 -0
- package/templates/engineering/central-delivery-template.md +65 -0
- package/templates/engineering/central-feature-template.md +86 -0
- package/templates/product/prod-breakdown-template.md +20 -2
- package/templates/product/prod-discovery-template.md +24 -1
- package/templates/product/prod-epic-template.md +32 -1
- package/templates/product/prod-frd-template.md +29 -2
- package/templates/product/prod-issue-template.md +36 -1
- package/templates/product/prod-prd-template.md +33 -2
- package/workflows/engineering/eng.build-tech-spec.md +4 -4
- package/workflows/engineering/eng.create-ard-from-code.md +15 -41
- package/workflows/engineering/eng.create-ard.md +22 -44
- package/workflows/engineering/eng.create-rfc.md +10 -36
- package/workflows/engineering/eng.pr.md +35 -66
- package/workflows/engineering/eng.pre-pr.md +52 -89
- package/workflows/engineering/eng.rpa.robot.md +9 -7
- package/workflows/engineering/eng.spec-consolidate.md +140 -0
- package/workflows/engineering/eng.start.md +23 -22
- package/workflows/engineering/eng.work.md +1 -1
- package/workflows/engineering/qa/eng.qa-refinement-entry.md +1 -1
- package/workflows/engineering/qa/eng.qa-release-signoff.md +3 -3
- package/workflows/engineering/qa/eng.qa-sprint-planning.md +2 -2
- package/workflows/product/prod.roadmap.preview.md +4 -1
- package/workflows/product/prod.spec.ard.md +9 -5
- package/workflows/product/prod.spec.breakdown.md +8 -4
- package/workflows/product/prod.spec.clarify.md +5 -1
- package/workflows/product/prod.spec.discovery.md +7 -3
- package/workflows/product/prod.spec.epic.md +12 -40
- package/workflows/product/prod.spec.frd.md +13 -39
- package/workflows/product/prod.spec.issue.md +5 -1
- package/workflows/product/prod.spec.md +3 -1
- package/workflows/product/prod.spec.prd.md +13 -40
- package/workflows/warm-up.md +10 -12
- package/bin/lib/docs/redis-cache.js +0 -49
- package/bin/lib/vcs/fetch-raw.js +0 -42
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: eng.docs-consolidator.agent
|
|
3
|
+
description: >
|
|
4
|
+
Especialista em organizar documentação para dois públicos, humanos e IAs. Consolida o que uma
|
|
5
|
+
entrega de fato implementou (documentos de trabalho, architecture.md, plan.md, diff e commits) em
|
|
6
|
+
documentos completos e autossuficientes para o Central docs: um por funcionalidade (estado atual)
|
|
7
|
+
e um registro por entrega. Escreve só na sessão; quem publica é o workflow, depois da confirmação.
|
|
8
|
+
Trigger: /eng.spec-consolidate (inclusive --inicial, chamado pelo /jarvis-init).
|
|
9
|
+
tools: Read, Glob, Grep, Bash, Write, Edit
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Consolidador de documentação (eng.docs-consolidator.agent)
|
|
13
|
+
|
|
14
|
+
## Papel
|
|
15
|
+
|
|
16
|
+
Você transforma o que foi **implementado** num registro que dure. Duas pessoas vão ler o que você escreve:
|
|
17
|
+
- **humanos:** outro squad, alguém novo no time, a própria pessoa daqui a seis meses;
|
|
18
|
+
- **IAs:** o `/warm-up`, o `/eng.start` e os `/prod.spec.*` consultam o Central docs como verdade sobre o sistema.
|
|
19
|
+
|
|
20
|
+
Por isso o texto tem de ser **completo, autossuficiente e sem ambiguidade**.
|
|
21
|
+
|
|
22
|
+
## Princípios
|
|
23
|
+
|
|
24
|
+
1. **Tudo, exatamente tudo.** Leia todas as fontes (abaixo) antes de escrever. Nada que esteja no PRD, no FRD, no ARD, no RFC, no `architecture.md` ou nos commits e que tenha sido implementado pode ficar de fora.
|
|
25
|
+
2. **Autossuficiente.** Se os documentos de origem se perderem, o seu documento basta. Os originais são citados só como referência (basename), nunca como "ver o PRD para detalhes".
|
|
26
|
+
3. **O implementado, não o pretendido.** Descreva o que o código faz. O que foi especificado e não entrou vai para "O que mudou em relação ao especificado" ou "Fora do escopo", com o motivo.
|
|
27
|
+
4. **Para humanos e IAs.**
|
|
28
|
+
- Frases completas e termos definidos na primeira vez.
|
|
29
|
+
- Listas "Quando X, o sistema Y".
|
|
30
|
+
- Tabelas para dados estruturados.
|
|
31
|
+
- Nada implícito, nada de "como combinado".
|
|
32
|
+
5. **Bons nomes.**
|
|
33
|
+
- Sugira o slug da funcionalidade em kebab-case, curto e pelo nome que o negócio usa (ex.: `feature-checkout-pix`, não `feature-task-123`).
|
|
34
|
+
- Os títulos dizem o que a funcionalidade é.
|
|
35
|
+
6. **Referências sempre com URL:**
|
|
36
|
+
- card pelo `TASK_MANAGER` do `ENV.md`, com a URL no padrão da `eng.integrations-rules.md` (Jira `/browse/`, Linear `/issue/`, GitHub `/issues/`, Asana); no modo freelance, o número de controle, sem URL;
|
|
37
|
+
- PR/MR pelo `VERSION_CONTROL` ou pelo `remote origin` (número + URL).
|
|
38
|
+
7. **Nunca inventar.** Sem fonte, pergunte ao agente principal (que pergunta ao usuário) ou registre como "não informado".
|
|
39
|
+
8. **Sem dados sensíveis.** Nunca e-mails de autor (só o nome), tokens, senhas, valores de variáveis de ambiente nem dados pessoais de clientes. Se o diff contiver algo parecido com um segredo, avise o agente principal.
|
|
40
|
+
|
|
41
|
+
## Fontes (nesta ordem)
|
|
42
|
+
|
|
43
|
+
1. `architecture.md` da sessão: `Branch base:`, contexto, decisões, Questões em Aberto respondidas.
|
|
44
|
+
2. `plan.md` da sessão: fases e comentários (mudanças de direção).
|
|
45
|
+
3. Documentos de trabalho em `$PROD_DOCS` e `$ENG_DOCS` ligados à tarefa (PRD, FRD, épicos, histórias, ARD, RFC, swagger, QA).
|
|
46
|
+
4. `git log {base}..HEAD --format='%h|%an|%ad|%s' --date=short` e `git diff --stat {base}...HEAD`.
|
|
47
|
+
5. O código alterado, só leitura, para confirmar o comportamento real.
|
|
48
|
+
6. O documento atual da funcionalidade no Central docs (`jarvis docs fetch`), quando for atualização.
|
|
49
|
+
7. O `gate-checklist.md` e o `pull-request.md` da sessão, se existirem.
|
|
50
|
+
|
|
51
|
+
## O que você produz
|
|
52
|
+
|
|
53
|
+
Na pasta da sessão, em `consolidation/`:
|
|
54
|
+
- **`feature-{slug}.md`**, no template `$IDE/templates/engineering/central-feature-template.md`. Na atualização, parta do documento atual do Central docs: reescreva as seções afetadas para o estado atual (não acumule "antes/depois") e acrescente a linha no Histórico de entregas.
|
|
55
|
+
- **`delivery-{AAAA-MM-DD}-{task_key}.md`**, no template `$IDE/templates/engineering/central-delivery-template.md`. No modo `--inicial` não há entrega: só o `feature-*` com a linha "Consolidação inicial a partir de {documentos}" no histórico.
|
|
56
|
+
|
|
57
|
+
Todas as seções obrigatórias do template, preenchidas ou com "Nenhum(a)" e o motivo.
|
|
58
|
+
|
|
59
|
+
## Limites
|
|
60
|
+
|
|
61
|
+
- **Escreve só em `$SESSIONS_DIR/eng/{KEY}/consolidation/`.** Não publica, não comita, não altera o código nem os documentos de trabalho.
|
|
62
|
+
- **No GitHub/GitLab, só consulta** (ex.: `gh pr view`, `gh issue view`) para obter números, URLs e títulos.
|
|
63
|
+
- **Português do Brasil;** nomes técnicos podem ficar em inglês.
|
|
64
|
+
|
|
65
|
+
## Skills Disponíveis
|
|
66
|
+
|
|
67
|
+
- **jarvis-docs-central**: contrato do Central docs (estrutura, índice, frontmatter) e leitura com `jarvis docs fetch`
|
|
68
|
+
|
|
69
|
+
## Workflows e Agentes Relacionados
|
|
70
|
+
|
|
71
|
+
- **eng.spec-consolidate**: abre este agente para escrever a consolidação (normal e `--inicial`)
|
|
72
|
+
- **jarvis-init**: oferece a carga inicial, que chama o `eng.spec-consolidate --inicial`
|
|
73
|
+
|
|
74
|
+
## Checklist antes de devolver
|
|
75
|
+
|
|
76
|
+
- [ ] Todas as fontes lidas (listar quais)
|
|
77
|
+
- [ ] As 11 seções da funcionalidade e as 7 da entrega preenchidas
|
|
78
|
+
- [ ] Card e PR/MR com URL (ou o número de controle no freelance)
|
|
79
|
+
- [ ] Commits com autor (nome, sem e-mail) e data
|
|
80
|
+
- [ ] Nada de segredos, e-mails ou valores de variáveis
|
|
81
|
+
- [ ] Nenhuma frase depende de um documento de origem para ser entendida
|
|
@@ -17,12 +17,12 @@ Antes de qualquer ação, determine qual modo aplicar:
|
|
|
17
17
|
Aplicar quando invocado via `eng.qa-refinement-entry` ou quando o argumento recebido for um ID de card ou caminho de spec e não houver código implementado na branch ainda.
|
|
18
18
|
- Fonte de dados: card no $TASK_MANAGER via integração disponível (Jira MCP)
|
|
19
19
|
- Não executar `git diff`, `git log` ou qualquer comando que dependa de código
|
|
20
|
-
- Saída: `$
|
|
20
|
+
- Saída: `$ENG_DOCS/qa/strategies/{task-id}-test-strategy.md`
|
|
21
21
|
|
|
22
22
|
**Modo B — Análise de Cobertura (pós-código)**
|
|
23
23
|
Aplicar quando invocado via `eng-qa`, `eng-qa-planner` ou quando houver código implementado na branch (git diff retorna resultados).
|
|
24
24
|
- Fonte de dados: `git diff origin/main...HEAD`
|
|
25
|
-
- Saída: `$
|
|
25
|
+
- Saída: `$ENG_DOCS/qa/{task-id}-test_coverage_branch_report.md`
|
|
26
26
|
|
|
27
27
|
Regra de decisão: se `git diff origin/main...HEAD` retornar vazio, operar em Modo A. Se retornar código, operar em Modo B.
|
|
28
28
|
|
|
@@ -225,7 +225,7 @@ Antes de finalizar cada cenário, aplicar este filtro:
|
|
|
225
225
|
|
|
226
226
|
### A.7. Salvar o Documento
|
|
227
227
|
|
|
228
|
-
Salvar em: `$
|
|
228
|
+
Salvar em: `$ENG_DOCS/qa/strategies/{task-id}-test-strategy.md`
|
|
229
229
|
|
|
230
230
|
Estrutura obrigatória:
|
|
231
231
|
```
|
|
@@ -353,7 +353,7 @@ Isso permite:
|
|
|
353
353
|
|
|
354
354
|
### B.6. Gerar Relatório de Cobertura de Testes
|
|
355
355
|
|
|
356
|
-
Crie um arquivo **`$
|
|
356
|
+
Crie um arquivo **`$ENG_DOCS/qa/{task-id}-test_coverage_branch_report.md`** contendo:
|
|
357
357
|
|
|
358
358
|
```markdown
|
|
359
359
|
# Análise de Cobertura de Testes da Branch
|
|
@@ -456,9 +456,9 @@ Esforço estimado: [estimativa aproximada]
|
|
|
456
456
|
|
|
457
457
|
## Saída
|
|
458
458
|
|
|
459
|
-
**Modo A**: `$
|
|
459
|
+
**Modo A**: `$ENG_DOCS/qa/strategies/{task-id}-test-strategy.md` (gerado em A.7)
|
|
460
460
|
|
|
461
|
-
**Modo B**: `$
|
|
461
|
+
**Modo B**: `$ENG_DOCS/qa/{task-id}-test_coverage_branch_report.md`
|
|
462
462
|
|
|
463
463
|
As recomendações devem ser **específicas, acionáveis** e, quando possível, incluir **exemplos de estrutura de teste**.
|
|
464
464
|
Foque **exclusivamente** no que foi alterado na branch atual para manter o escopo controlado.
|
|
@@ -470,7 +470,7 @@ Foque **exclusivamente** no que foi alterado na branch atual para manter o escop
|
|
|
470
470
|
Após gerar o relatório de cobertura, sugira ao usuário invocar o agente **eng.qa.testing-engineer** para escrever os testes identificados como ausentes:
|
|
471
471
|
|
|
472
472
|
```
|
|
473
|
-
@eng.qa.testing-engineer Escreva os testes identificados no relatório $
|
|
473
|
+
@eng.qa.testing-engineer Escreva os testes identificados no relatório $ENG_DOCS/qa/{task-id}-test_coverage_branch_report.md
|
|
474
474
|
```
|
|
475
475
|
|
|
476
476
|
Fluxo completo recomendado:
|
|
@@ -75,7 +75,7 @@ Não use quando já existe uma spec (use `/prod.spec.clarify`) ou quando o escop
|
|
|
75
75
|
As skills são apoio, nunca a fonte da decisão. A busca em `$PROD_DOCS` não é uma skill: é feita sempre, antes de perguntar.
|
|
76
76
|
|
|
77
77
|
### jarvis-docs-central
|
|
78
|
-
Para
|
|
78
|
+
Para conhecer o que já foi implementado no produto (funcionalidades do Central docs), quando `CENTRAL_DOCS_STORAGE` estiver configurado:
|
|
79
79
|
- Arquivo: `$IDE/skills/jarvis-docs-central/SKILL.md`
|
|
80
80
|
- Confiança: sempre
|
|
81
81
|
|
package/bin/README.md
CHANGED
|
@@ -17,17 +17,17 @@ bin/
|
|
|
17
17
|
│ ├── program.js # Único lugar que monta o commander — ver "Arquitetura"
|
|
18
18
|
│ └── shared-options.js # .option() repetidos entre comandos (--ide, --quiet, etc.)
|
|
19
19
|
├── vcs/ # Adapters de Git host (GitLab/GitHub/Bitbucket)
|
|
20
|
-
│ ├── api.js # Lógica HTTP real (createIssue, createMergeRequest
|
|
21
|
-
│ ├── create-merge.js, create-issue.js
|
|
20
|
+
│ ├── api.js # Lógica HTTP real (createIssue, createMergeRequest)
|
|
21
|
+
│ ├── create-merge.js, create-issue.js # função exportada + CLI direto (ver abaixo)
|
|
22
22
|
├── tasks/
|
|
23
23
|
│ └── comment.js # Adapter de task manager (jira/linear/github/asana) — mesmo padrão
|
|
24
24
|
├── config/ # ide-config.js (fonte de verdade de IDEs), constants.js
|
|
25
25
|
├── core/ # scanner.js, sync-engine.js, profile-filter.js, token-report.js
|
|
26
|
-
├── docs/ #
|
|
27
|
-
│ ├── validate-frontmatter.js # Valida/extrai frontmatter
|
|
28
|
-
│ ├──
|
|
29
|
-
│ ├──
|
|
30
|
-
│ └──
|
|
26
|
+
├── docs/ # Central docs: fetch/publish pelo adaptador + validação de frontmatter
|
|
27
|
+
│ ├── validate-frontmatter.js # Valida/extrai frontmatter (PRD, FRD, épico, história, tarefa, ARD, RFC)
|
|
28
|
+
│ ├── fetch-file.js # openStorageFromEnv() + fetchFile() — `docs sync`/`docs fetch`/`docs publish`
|
|
29
|
+
│ ├── publish-file.js # tipos, resolveDestPath() e publishFile() — `docs publish`
|
|
30
|
+
│ └── storage/ # adaptadores (CENTRAL_DOCS_STORAGE): index.js (fábrica), filesystem.js (pasta + git), build-index.js
|
|
31
31
|
├── utils/ # logger.js, ui.js, paths.js, frontmatter.js, git-parser.js, npmrc-parser.js
|
|
32
32
|
├── flow-map/ # `jarvis map` — grafo de chamadas entre workflows/skills/agents
|
|
33
33
|
├── auth/ # session.js (auth.json legado, hoje só `jarvis logout` o lê)
|
|
@@ -77,22 +77,22 @@ O parser manual antigo nunca validava nada: qualquer `--flag` era aceito e ignor
|
|
|
77
77
|
|
|
78
78
|
## Argumentos: `[opcional]`, não `<obrigatório>` — de propósito
|
|
79
79
|
|
|
80
|
-
Note que `info
|
|
80
|
+
Note que `info` e `tasks comment` declaram seus positionals como **opcionais** (`[target]`, `[card-key]`) mesmo sendo, na prática, obrigatórios para o comando funcionar. Isso é deliberado: se o commander validar "argumento obrigatório ausente" sozinho, ele intercepta **antes** da função rodar — e cada uma dessas funções já tem sua própria validação com mensagem de uso customizada e mais específica (`info.js`: `"❌ Uso incorreto\nUso: jarvis info <type/name>\nExemplo: ..."`). Deixar o positional opcional no commander e a validação dentro da função preserva essa mensagem.
|
|
81
81
|
|
|
82
|
-
**A mesma regra vale para `--flag` que já tem validação própria.** Nenhum comando aqui usa `.requiredOption()` ou `.choices()` do commander, mesmo quando faria sentido à primeira vista (ex: `--tipo` em `docs publish` só aceita `
|
|
82
|
+
**A mesma regra vale para `--flag` que já tem validação própria.** Nenhum comando aqui usa `.requiredOption()` ou `.choices()` do commander, mesmo quando faria sentido à primeira vista (ex: `--tipo` em `docs publish` só aceita os tipos de `VALID_TYPES`). `docsPublish.js` já valida isso e mostra um bloco de exemplos de uso bem mais rico que o que o commander mostraria. Usar `.choices()` ali faria o commander interceptar o valor errado com uma mensagem genérica, perdendo esse texto. **Ao adicionar uma opção nova**: se a função já vai validar o valor por dentro, deixe o commander permissivo (`.option()` simples) e confie na validação interna. Se não há validação nenhuma hoje, `.choices()`/`.argParser()` são bons lugares para adicionar uma — não há essa perda a proteger.
|
|
83
83
|
|
|
84
84
|
## `program.command()` vs `program.addCommand()`
|
|
85
85
|
|
|
86
86
|
Este código usa `.command(nome)` (que cria e já anexa o subcomando) em todo lugar — não `.addCommand(instanciaDeCommand)`. São equivalentes; `.addCommand()` só compensa quando o `Command` é montado em outro módulo e precisa ser importado antes de anexar. Como tudo mora em `program.js`, não há motivo para o nível extra de indireção.
|
|
87
87
|
|
|
88
|
-
## Os
|
|
88
|
+
## Os 3 "scripts standalone" (`vcs/*.js`, `tasks/comment.js`): dupla forma
|
|
89
89
|
|
|
90
|
-
`create-merge.js`, `create-issue.js
|
|
90
|
+
`create-merge.js`, `create-issue.js` e `comment.js` são chamados de duas formas:
|
|
91
91
|
|
|
92
92
|
1. **Como subcomando do CLI**: `jarvis vcs create-merge --source ... --target ...` (via `program.js`)
|
|
93
93
|
2. **Direto, por skills/workflows que ainda shell-am pra eles**: `node bin/lib/vcs/create-merge.js --source ...`
|
|
94
94
|
|
|
95
|
-
Cada um dos
|
|
95
|
+
Cada um dos 3 arquivos segue o mesmo padrão: exporta uma função (`runCreateMerge`, `runCreateIssue`, `runComment`) com a lógica, e termina com um guard que só lê `process.argv` quando o arquivo é executado diretamente:
|
|
96
96
|
|
|
97
97
|
```js
|
|
98
98
|
// Compatibilidade: continua funcionando como `node bin/lib/vcs/create-merge.js --source ...`
|
|
@@ -131,9 +131,9 @@ Os 3 arquivos mais críticos sem cobertura até pouco tempo atrás (`env-loader.
|
|
|
131
131
|
- **`env-loader.js`** (e `flow-map.test.js`, mais antigo): `mkdtempSync(join(tmpdir(), "jarvis-..."))` monta um workspace fake com `.{ide}/ENV.md`, roda a função contra ele, `rmSync` no `finally`.
|
|
132
132
|
- **`sync-engine.js`**: `getFrameworkRoot()` (`utils/paths.js`) já tem um escape hatch — `process.env.JARVIS_ROOT`, se definido e existir, vence sobre o framework real. O teste aponta `JARVIS_ROOT` para um fixture mínimo (`agents/`, `skills/`, etc. com 1 arquivo cada) em vez de sincronizar o framework inteiro.
|
|
133
133
|
- **`vcs/api.js`**: usa `mock.method(globalThis, "fetch", ...)` do `node:test` pra controlar a resposta por vendor, sem mockar o módulo nem tocar rede real. **Atenção**: toda função de rede chama `getVcsToken()` antes do fetch — fixe `GITLAB_TOKEN`/`GITHUB_TOKEN`/`BITBUCKET_TOKEN` via env var no teste (ver `withEnv` em `vcs-api.test.js`), senão o resultado depende do que a máquina que roda o teste tiver em `.npmrc`/`gh auth`.
|
|
134
|
-
- **`docs
|
|
134
|
+
- **`docs/` (Central docs)**: tudo em pasta temporária (`mkdtempSync`). A camada git do adaptador `filesystem` usa um repositório `git init --bare` local como `origin` (sem rede), e o `createMergeRequest` é injetado pelo parâmetro `deps` de `getStorage` (ver `docs-storage.test.js`).
|
|
135
135
|
|
|
136
|
-
**Armadilha com `async`**: o helper `withEnv(vars, fn)` (
|
|
136
|
+
**Armadilha com `async`**: o helper `withEnv(vars, fn)` (em `vcs-api.test.js`) precisa de `return **await** fn()`, não `return fn()`, dentro do `try`. Sem o `await`, o `finally` que restaura a env var roda assim que `fn()` retorna uma Promise pendente — ou seja, **antes** da função terminar. Isso só não quebra quando a leitura da env var acontece antes do primeiro `await` de `fn` (ex: `createIssue` lê o token antes do seu próprio `fetch`); quebra silenciosamente em qualquer função com mais de um `await` antes de ler a variável.
|
|
137
137
|
|
|
138
138
|
Ao escrever um teste novo para um arquivo que toca `fs`/rede, prefira um desses padrões a mockar o módulo inteiro ou refatorar pra injeção de dependência — nenhum dos arquivos acima precisou mudar uma linha de lógica de produção para ganhar teste.
|
|
139
139
|
|
|
@@ -1,148 +1,116 @@
|
|
|
1
|
-
import { loadEnv, resolveEnvPath } from "../lib/env-loader.js";
|
|
2
1
|
import { existsSync } from "node:fs";
|
|
3
|
-
import {
|
|
2
|
+
import { openStorageFromEnv } from "../lib/docs/fetch-file.js";
|
|
3
|
+
import { publishFile, VALID_TYPES, WORKING_DOC_TYPES } from "../lib/docs/publish-file.js";
|
|
4
4
|
import { logger, configureFromFlags } from "../lib/utils/logger.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Publica documento no
|
|
7
|
+
* Publica um documento no Central docs pelo adaptador configurado.
|
|
8
|
+
* Sem `.git` na pasta: copia e regenera o índice. Com `.git`: branch, commit, push e PR.
|
|
8
9
|
*
|
|
9
10
|
* @param {Object} flags - Flags do comando
|
|
10
11
|
* @param {string} flags.file - Caminho do arquivo local
|
|
11
|
-
* @param {string} flags.tipo -
|
|
12
|
-
* @param {string} flags.
|
|
13
|
-
* @param {string} flags.
|
|
14
|
-
* @param {string} flags.
|
|
12
|
+
* @param {string} flags.tipo - feature | delivery (ver VALID_TYPES)
|
|
13
|
+
* @param {string} [flags.produto] - Pasta do produto (padrão: WORKSPACE do ENV.md)
|
|
14
|
+
* @param {string} [flags.feature] - Slug, só se o nome do arquivo fugir do padrão {tipo}-{nome}.md
|
|
15
|
+
* @param {string} [flags.squad] - Override do SQUAD do ENV.md
|
|
16
|
+
* @param {string} [flags.workspace] - Override do WORKSPACE (padrão do --produto)
|
|
15
17
|
* @param {string} flags.envFile - Path customizado do ENV.md
|
|
16
18
|
*/
|
|
17
19
|
export async function docsPublish(flags = {}) {
|
|
18
20
|
configureFromFlags(flags);
|
|
19
21
|
|
|
20
|
-
const {
|
|
22
|
+
const {
|
|
23
|
+
file,
|
|
24
|
+
tipo,
|
|
25
|
+
feature,
|
|
26
|
+
produto,
|
|
27
|
+
squad: squadOverride,
|
|
28
|
+
workspace: workspaceOverride,
|
|
29
|
+
} = flags;
|
|
21
30
|
|
|
22
|
-
|
|
23
|
-
if (!file || !tipo || !feature) {
|
|
31
|
+
if (!file || !tipo) {
|
|
24
32
|
logger.error("❌ Parâmetros obrigatórios faltando\n");
|
|
25
33
|
logger.error(
|
|
26
|
-
|
|
34
|
+
`Uso: jarvis docs publish --file <path> --tipo <${VALID_TYPES.join("|")}> [--produto <produto>] [--feature <slug>] [--squad <squad>]\n`
|
|
35
|
+
);
|
|
36
|
+
logger.error(
|
|
37
|
+
"Normalmente chamado pelo /eng.spec-consolidate, que gera os dois arquivos na sessão:"
|
|
38
|
+
);
|
|
39
|
+
logger.error(
|
|
40
|
+
" jarvis docs publish --file .jarvis/sessions/eng/task-123/consolidation/delivery-2026-10-15-task-123.md --tipo delivery"
|
|
41
|
+
);
|
|
42
|
+
logger.error(
|
|
43
|
+
" jarvis docs publish --file .jarvis/sessions/eng/task-123/consolidation/feature-checkout.md --tipo feature"
|
|
27
44
|
);
|
|
28
|
-
logger.error("Exemplo:");
|
|
29
|
-
logger.error(" jarvis docs publish \\");
|
|
30
|
-
logger.error(" --file ./docs/engineering/ard-api-wallet.md \\");
|
|
31
|
-
logger.error(" --tipo ard \\");
|
|
32
|
-
logger.error(" --feature api-wallet-auth-jwt");
|
|
33
|
-
logger.error("");
|
|
34
|
-
logger.error(" # Publicar Swagger/OpenAPI:");
|
|
35
|
-
logger.error(" jarvis docs publish \\");
|
|
36
|
-
logger.error(" --file ./docs/engineering/swagger/api-wallet.yaml \\");
|
|
37
|
-
logger.error(" --tipo swagger \\");
|
|
38
|
-
logger.error(" --feature api-wallet");
|
|
39
|
-
logger.error("");
|
|
40
|
-
logger.error(" # Com override de squad/workspace:");
|
|
41
|
-
logger.error(" jarvis docs publish \\");
|
|
42
|
-
logger.error(" --file ./docs/engineering/ard-api-wallet.md \\");
|
|
43
|
-
logger.error(" --tipo ard \\");
|
|
44
|
-
logger.error(" --feature api-wallet-auth-jwt \\");
|
|
45
|
-
logger.error(" --squad core \\");
|
|
46
|
-
logger.error(" --workspace meu-workspace");
|
|
47
45
|
process.exit(1);
|
|
48
46
|
}
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
logger.error(
|
|
48
|
+
if (WORKING_DOC_TYPES.includes(tipo)) {
|
|
49
|
+
logger.error(
|
|
50
|
+
`❌ O Central docs guarda só o que foi implementado: "${tipo}" é um documento de trabalho.`
|
|
51
|
+
);
|
|
52
|
+
logger.error(" Ele fica em docs/jarvis/. Gere a consolidação com /eng.spec-consolidate.");
|
|
55
53
|
process.exit(1);
|
|
56
54
|
}
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
logger.error(
|
|
56
|
+
if (!VALID_TYPES.includes(tipo)) {
|
|
57
|
+
logger.error(`❌ Tipo inválido: ${tipo}`);
|
|
58
|
+
logger.error(`Tipos válidos: ${VALID_TYPES.join(", ")}`);
|
|
61
59
|
process.exit(1);
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// Resolver path do ENV.md (suporta --ide e --env-file)
|
|
67
|
-
let envPath;
|
|
68
|
-
try {
|
|
69
|
-
const resolved = resolveEnvPath(cwd, flags);
|
|
70
|
-
envPath = resolved.path;
|
|
71
|
-
} catch (error) {
|
|
72
|
-
logger.error("❌ Erro ao resolver ENV.md:", error.message);
|
|
62
|
+
if (!existsSync(file)) {
|
|
63
|
+
logger.error(`❌ Arquivo não encontrado: ${file}`);
|
|
73
64
|
process.exit(1);
|
|
74
65
|
}
|
|
75
66
|
|
|
76
|
-
// Carregar ENV.md
|
|
77
67
|
let env;
|
|
68
|
+
let storage;
|
|
78
69
|
try {
|
|
79
|
-
env =
|
|
70
|
+
({ env, storage } = openStorageFromEnv(flags));
|
|
80
71
|
} catch (error) {
|
|
81
|
-
logger.error("❌
|
|
72
|
+
logger.error("❌ Central docs:", error.message);
|
|
82
73
|
process.exit(1);
|
|
83
74
|
}
|
|
84
75
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
logger.error("
|
|
88
|
-
logger.error("\nConfigure a URL do repositório central:");
|
|
89
|
-
logger.error(" CENTRAL_DOCS_REPO=https://gitlab.com/org/central-docs.git");
|
|
76
|
+
if (!storage) {
|
|
77
|
+
logger.error("❌ Central docs desligado (CENTRAL_DOCS_STORAGE vazio no ENV.md).");
|
|
78
|
+
logger.error(" Configure-o com o /jarvis-init.");
|
|
90
79
|
process.exit(1);
|
|
91
80
|
}
|
|
92
81
|
|
|
93
|
-
// Usar override de squad/workspace se fornecido, senão usar do ENV.md
|
|
94
82
|
const squad = squadOverride || env.SQUAD;
|
|
95
|
-
const
|
|
83
|
+
const product = produto || workspaceOverride || env.WORKSPACE;
|
|
96
84
|
|
|
97
|
-
if (!squad || !
|
|
85
|
+
if (!squad || !product) {
|
|
98
86
|
logger.error("❌ SQUAD ou WORKSPACE não definidos");
|
|
99
|
-
logger.error("
|
|
100
|
-
logger.error(" 1. Definir no ENV.md:");
|
|
101
|
-
logger.error(" SQUAD=core");
|
|
102
|
-
logger.error(" WORKSPACE=meu-workspace");
|
|
103
|
-
logger.error("");
|
|
104
|
-
logger.error(" 2. Passar via flags:");
|
|
105
|
-
logger.error(" --squad core --workspace meu-workspace");
|
|
87
|
+
logger.error(" Defina no ENV.md ou passe --squad <squad> --produto <produto>");
|
|
106
88
|
process.exit(1);
|
|
107
89
|
}
|
|
108
90
|
|
|
109
|
-
logger.info(`📤 Publicando ${tipo.toUpperCase()} ${
|
|
91
|
+
logger.info(`📤 Publicando ${tipo.toUpperCase()} (${file})...\n`);
|
|
110
92
|
|
|
111
93
|
try {
|
|
112
|
-
await publishFile({
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
feature,
|
|
116
|
-
squad,
|
|
117
|
-
workspace,
|
|
118
|
-
repo: env.CENTRAL_DOCS_REPO,
|
|
119
|
-
targetBranch: env.CENTRAL_DOCS_TARGET_BRANCH || "dev",
|
|
120
|
-
cacheTtl: env.CENTRAL_DOCS_CACHE_TTL || "3600",
|
|
121
|
-
});
|
|
94
|
+
await publishFile({ storage, localFile: file, tipo, squad, produto: product, feature });
|
|
95
|
+
logger.info("");
|
|
96
|
+
logger.info("✅ Publicação concluída!");
|
|
122
97
|
} catch (error) {
|
|
123
98
|
logger.error("\n❌ Erro ao publicar documento\n");
|
|
124
99
|
logger.error(error.message);
|
|
125
100
|
|
|
126
|
-
// Dicas de troubleshooting
|
|
127
|
-
logger.error("\n💡 Troubleshooting:\n");
|
|
128
|
-
|
|
129
|
-
if (error.message.includes("Token GitLab")) {
|
|
130
|
-
logger.error("1. Configure o token GitLab no .npmrc:");
|
|
131
|
-
logger.error(" //gitlab.com/api/v4/packages/npm/:_authToken=seu-token\n");
|
|
132
|
-
}
|
|
133
|
-
|
|
134
101
|
if (error.message.includes("Campos obrigatórios")) {
|
|
135
|
-
logger.error(
|
|
136
|
-
|
|
102
|
+
logger.error(
|
|
103
|
+
`\n💡 Verifique o frontmatter do documento: o tipo ${tipo} exige campos específicos.`
|
|
104
|
+
);
|
|
137
105
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
106
|
+
if (storage.isGit) {
|
|
107
|
+
logger.error(
|
|
108
|
+
"\n💡 Central docs com git: confira se a pasta está limpa (git status), se você tem permissão de push"
|
|
109
|
+
);
|
|
110
|
+
logger.error(
|
|
111
|
+
" e se o token do vendor está configurado (GitLab .npmrc, GitHub gh auth, BITBUCKET_TOKEN)."
|
|
112
|
+
);
|
|
142
113
|
}
|
|
143
|
-
|
|
144
|
-
logger.error("Para mais detalhes, execute com --verbose");
|
|
145
|
-
|
|
146
114
|
process.exit(1);
|
|
147
115
|
}
|
|
148
116
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { fetchFile } from "../lib/docs/fetch-file.js";
|
|
1
|
+
import { openStorageFromEnv } from "../lib/docs/fetch-file.js";
|
|
3
2
|
import { logger, configureFromFlags } from "../lib/utils/logger.js";
|
|
4
3
|
|
|
4
|
+
const COUNTED_TYPES = ["feature", "delivery"];
|
|
5
|
+
const LABELS = { feature: "funcionalidade(s)", delivery: "entrega(s) recente(s)" };
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
|
-
* Sincroniza documentação do
|
|
7
|
-
* Executado automaticamente em /warm-up
|
|
8
|
+
* Sincroniza a documentação do Central docs (lê `{SQUAD}/index.md` pelo adaptador).
|
|
9
|
+
* Executado automaticamente em /warm-up.
|
|
8
10
|
*
|
|
9
11
|
* @param {Object} flags - Flags do comando
|
|
10
12
|
* @param {boolean} flags.silent - Modo silencioso (nem erros aparecem)
|
|
@@ -15,87 +17,53 @@ import { logger, configureFromFlags } from "../lib/utils/logger.js";
|
|
|
15
17
|
export async function docsSync(flags = {}) {
|
|
16
18
|
configureFromFlags(flags);
|
|
17
19
|
|
|
18
|
-
const cwd = process.cwd();
|
|
19
|
-
|
|
20
|
-
// Resolver path do ENV.md (suporta --ide e --env-file)
|
|
21
|
-
let envPath;
|
|
22
|
-
try {
|
|
23
|
-
const resolved = resolveEnvPath(cwd, flags);
|
|
24
|
-
envPath = resolved.path;
|
|
25
|
-
} catch (error) {
|
|
26
|
-
logger.error("❌ Erro ao resolver ENV.md:", error.message);
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Carregar ENV.md
|
|
31
20
|
let env;
|
|
21
|
+
let storage;
|
|
32
22
|
try {
|
|
33
|
-
env =
|
|
23
|
+
({ env, storage } = openStorageFromEnv(flags));
|
|
34
24
|
} catch (error) {
|
|
35
|
-
logger.error("❌
|
|
25
|
+
logger.error("❌ Central docs:", error.message);
|
|
36
26
|
process.exit(1);
|
|
37
27
|
}
|
|
38
28
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
logger.info("ℹ️ Central docs não configurado (CENTRAL_DOCS_REPO vazio)");
|
|
29
|
+
if (!storage) {
|
|
30
|
+
logger.info("ℹ️ Central docs desligado (CENTRAL_DOCS_STORAGE vazio)");
|
|
42
31
|
return;
|
|
43
32
|
}
|
|
44
33
|
|
|
45
|
-
const { SQUAD
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
logger.error("❌ SQUAD ou WORKSPACE não definidos no ENV.md");
|
|
34
|
+
const { SQUAD } = env;
|
|
35
|
+
if (!SQUAD) {
|
|
36
|
+
logger.error("❌ SQUAD não definido no ENV.md");
|
|
49
37
|
process.exit(1);
|
|
50
38
|
}
|
|
51
39
|
|
|
52
|
-
logger.info(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const indexPath = `${SQUAD}/index.md`;
|
|
40
|
+
logger.info(
|
|
41
|
+
`🔄 Sincronizando docs do squad ${SQUAD} (${storage.kind}${storage.isGit ? " + git" : ""})...`
|
|
42
|
+
);
|
|
56
43
|
|
|
57
44
|
try {
|
|
58
|
-
const content = await
|
|
59
|
-
repo: env.CENTRAL_DOCS_REPO,
|
|
60
|
-
cacheTtl: env.CENTRAL_DOCS_CACHE_TTL || "3600",
|
|
61
|
-
});
|
|
62
|
-
|
|
45
|
+
const content = await storage.fetch(`${SQUAD}/index.md`);
|
|
63
46
|
logger.info("✅ Docs sincronizados");
|
|
64
47
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
logger.debug(content);
|
|
68
|
-
}
|
|
48
|
+
logger.debug("\n📄 Conteúdo do index.md:\n");
|
|
49
|
+
logger.debug(content);
|
|
69
50
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const rfcCount = (content.match(/\[rfc-.*\.md\]/g) || []).length;
|
|
51
|
+
const counts = COUNTED_TYPES.map((tipo) => [
|
|
52
|
+
tipo,
|
|
53
|
+
(content.match(new RegExp(`\\[${tipo}-[^\\]]*\\]`, "g")) || []).length,
|
|
54
|
+
]).filter(([, n]) => n > 0);
|
|
55
|
+
const total = counts.reduce((sum, [, n]) => sum + n, 0);
|
|
76
56
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
logger.info(`📊 ${total} documentos disponíveis:`);
|
|
81
|
-
if (prdCount > 0) logger.info(` - ${prdCount} PRD(s)`);
|
|
82
|
-
if (frdCount > 0) logger.info(` - ${frdCount} FRD(s)`);
|
|
83
|
-
if (ardCount > 0) logger.info(` - ${ardCount} ARD(s)`);
|
|
84
|
-
if (rfcCount > 0) logger.info(` - ${rfcCount} RFC(s)`);
|
|
85
|
-
}
|
|
57
|
+
if (total > 0) {
|
|
58
|
+
logger.info(`📊 ${total} documentos disponíveis:`);
|
|
59
|
+
for (const [tipo, n] of counts) logger.info(` - ${n} ${LABELS[tipo]}`);
|
|
86
60
|
}
|
|
87
61
|
} catch (error) {
|
|
88
62
|
logger.error("❌ Erro ao sincronizar docs:", error.message);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
logger.error("\n💡 Configure o token GitLab no .npmrc:");
|
|
93
|
-
logger.error(" //gitlab.com/api/v4/packages/npm/:_authToken=seu-token");
|
|
94
|
-
} else if (error.message.includes("não encontrado")) {
|
|
95
|
-
logger.error("\n💡 Verifique se o squad existe no central-docs:");
|
|
96
|
-
logger.error(` ${SQUAD}/index.md`);
|
|
63
|
+
if (error.code === 404) {
|
|
64
|
+
logger.error("\n💡 O squad ainda não tem índice no Central docs:");
|
|
65
|
+
logger.error(` ${SQUAD}/index.md (é criado na primeira publicação: jarvis docs publish)`);
|
|
97
66
|
}
|
|
98
|
-
|
|
99
67
|
process.exit(1);
|
|
100
68
|
}
|
|
101
69
|
}
|