jarvis-ai-framework 8.1.0 → 8.2.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/README.md CHANGED
@@ -100,7 +100,9 @@ npm install -g jarvis-ai-framework@latest
100
100
  jarvis init --upgrade --ide {ide} # assets + variáveis novas do ENV.md + documentos para as pastas novas
101
101
  ```
102
102
 
103
- Depois, na IDE, rode `/jarvis-init upgrade`: ele vai direto ao Upgrade (C) e cuida do que precisa de conversa (Central docs antigo e o passo das stacks). O `--upgrade` só **acrescenta** chaves ao `ENV.md`: nenhuma chave existente muda. Ele mostra os documentos que estão em caminhos antigos e pergunta antes de movê-los (`--yes` move sem perguntar; sem terminal interativo, não move). Também lista os skills instalados que saíram do framework e pergunta antes de removê-los (`--remove-stale` remove sem perguntar só os listados; skills registrados em `*_SPECIALIZATIONS` nunca são listados).
103
+ `jarvis init --ide {ide}` numa pasta já instalada mostra o menu **Atualizar** (Enter, o mesmo que o `--upgrade`) / **Reinstalar** (o mesmo que o `--force`) / **Cancelar**, com a versão instalada e a do pacote. No fim, ele só manda rodar o `/jarvis-init upgrade` quando há pendência.
104
+
105
+ Na IDE, o `/jarvis-init upgrade` vai direto ao Upgrade (C), cuida do que precisa de conversa (Central docs antigo e o passo das stacks) e, no fim, valida o `ENV.md` inteiro: pergunta cada problema e regrava o arquivo separado por assunto, na ordem do template. O `--upgrade` só **acrescenta** chaves ao `ENV.md`, no fim do arquivo: nenhuma chave existente muda. Ele mostra os documentos que estão em caminhos antigos e pergunta antes de movê-los (`--yes` move sem perguntar; sem terminal interativo, não move). Também lista os skills instalados que saíram do framework e pergunta antes de removê-los (`--remove-stale` remove sem perguntar só os listados; skills registrados em `*_SPECIALIZATIONS` nunca são listados).
104
106
 
105
107
  **Instalou via clone local** (fluxo de [Desenvolvendo o Jarvis localmente](#desenvolvendo-o-jarvis-localmente)) — quando o repo no GitHub ganhar commits novos, sincronize assim:
106
108
 
@@ -175,20 +175,26 @@ Para detecção automática de contexto de tarefas:
175
175
  - Uso: `/jarvis-context-detect [jira-key]` ou `/jarvis-context-detect --override tipo=hotfix`
176
176
  - Chamado automaticamente pelo workflow `eng.start`
177
177
 
178
- ### eng-platform (performance)
179
- Quando a tarefa envolver análise ou otimização de performance, observabilidade, gargalos, latência, load testing, chaos engineering ou cache multi-camada — temas 7, 14, 15 e 16 da base de platform:
178
+ ### eng-platform
179
+ Quando a tarefa envolver infraestrutura, performance ou segurança de infraestrutura — base de platform, complementada pela especialização registrada em `PLATFORM_SPECIALIZATIONS`:
180
180
  - Arquivo: `$IDE/skills/eng-platform/SKILL.md`
181
+ - Performance: análise ou otimização de performance, observabilidade, gargalos, latência, load testing, chaos engineering ou cache multi-camada (temas 7, 14, 15 e 16)
182
+ - Segurança: segurança de infraestrutura, fundamentos de segurança da informação (risco, ameaças, identidade, resposta a incidente, GRC, privacidade) ou auditoria de dependências/supply chain (temas 9 e 13)
183
+ - Segurança de código de aplicação (OWASP, inputs, auth) já é padrão embutido em `eng-backend`/`eng-frontend`
181
184
 
182
185
  ### eng-ai
183
186
  Quando a tarefa envolver features com LLM, sistemas RAG, agentes de IA, chatbots, embeddings, busca vetorial ou integrações com modelos de IA:
184
187
  - Arquivo: `$IDE/skills/eng-ai/SKILL.md`
185
188
 
186
- ### Backend e frontend (skill base + especializações)
187
- Quando a tarefa envolver **backend** (APIs, autenticação, workers e jobs assíncronos, filas e mensageria, integrações externas, cache, banco de dados) ou **frontend** (componentes, estado, estilos, performance de UI, acessibilidade, testes de interface), não escolha o skill por nome: aplique a regra `$IDE/rules/engineering/eng.specializations-rules.md` para a área envolvida. Ela carrega o skill base e as especializações que o projeto registrou no `ENV.md`:
188
- - Backend: `$IDE/skills/eng-backend/SKILL.md` + itens de `BACKEND_SPECIALIZATIONS`
189
- - Frontend: `$IDE/skills/eng-frontend/SKILL.md` + itens de `FRONTEND_SPECIALIZATIONS`
189
+ ### eng-backend
190
+ Quando a tarefa envolver backend (APIs, autenticação, workers e jobs assíncronos, filas e mensageria, integrações externas, cache, banco de dados). Não escolha o skill por nome: aplique a regra `$IDE/rules/engineering/eng.specializations-rules.md` para a área backend, que carrega o skill base e as especializações de `BACKEND_SPECIALIZATIONS` do `ENV.md`:
191
+ - Arquivo: `$IDE/skills/eng-backend/SKILL.md`
190
192
  - Workflows relacionados: `eng.start` (arquitetura), `eng.work` (implementação), `eng.debug` (troubleshooting)
191
- - Para ver o que está instalado e registrado: `/jarvis-list-specializations`
193
+
194
+ ### eng-frontend
195
+ Quando a tarefa envolver frontend (componentes, estado, estilos, performance de UI, acessibilidade, testes de interface). Não escolha o skill por nome: aplique a regra `$IDE/rules/engineering/eng.specializations-rules.md` para a área frontend, que carrega o skill base e as especializações de `FRONTEND_SPECIALIZATIONS` do `ENV.md`:
196
+ - Arquivo: `$IDE/skills/eng-frontend/SKILL.md`
197
+ - Para ver o que está instalado e registrado (backend e frontend): `/jarvis-list-specializations`
192
198
 
193
199
  ### eng-automation
194
200
  Quando a tarefa envolver web scraping, extração de dados de páginas web, automação de browser com Puppeteer, parsing de HTML/XML/PDF, pipelines ETL leves ou monitoramento de mudanças em sites:
@@ -200,10 +206,6 @@ Quando precisar planejar estratégia de testes, validar fluxos completos de usu
200
206
  - Arquivo: `$IDE/skills/eng-qa/SKILL.md`
201
207
  - Workflows relacionados: `eng.pr` (validação pré-PR), `eng.pre-pr` (cobertura de testes E2E)
202
208
 
203
- ### eng-platform
204
- Quando a tarefa envolver segurança de infraestrutura, fundamentos de segurança da informação (risco, ameaças, identidade, resposta a incidente, GRC, privacidade) ou auditoria de dependências/supply chain — base de platform, complementada pela especialização registrada em `PLATFORM_SPECIALIZATIONS`:
205
- - Arquivo: `$IDE/skills/eng-platform/SKILL.md` (temas 9 e 13)
206
- - Segurança de código de aplicação (OWASP, inputs, auth) já é padrão embutido em `eng-backend`/`eng-frontend`
207
209
 
208
210
  ---
209
211
 
@@ -6,18 +6,18 @@ model: opus
6
6
  color: green
7
7
  ---
8
8
 
9
- # code-reviewe
9
+ # code-reviewer
10
10
  Você é um especialista em revisão de código responsável por analisar mudanças de código em preparação para a abertura de um Pull Request.
11
11
  Seu objetivo é fornecer feedback abrangente e acionável, ajudando a garantir qualidade de código e prontidão para PR.
12
12
 
13
13
  Processo de Revisão
14
14
  1. Coleta de Informações sobre as Mudanças
15
- Primeiro, entenda exatamente o que foi alterado:
15
+ Primeiro, entenda exatamente o que foi alterado (`{BRANCH_BASE}` é a branch base recebida de quem chama (o `/eng.pre-pr` passa a `Branch base:` do `architecture.md`); sem ela, `origin/main`.):
16
16
  - Execute git status para verificar mudanças não commitadas
17
17
  - Execute git diff para visualizar mudanças não staged
18
18
  - Execute git diff --staged para visualizar mudanças staged
19
- - Execute git log origin/main..HEAD --oneline para ver os commits da branch atual
20
- - Execute git diff origin/main...HEAD para ver todas as mudanças comparadas com a branch principal
19
+ - Execute git log {BRANCH_BASE}..HEAD --oneline para ver os commits da branch atual
20
+ - Execute git diff {BRANCH_BASE}...HEAD para ver todas as mudanças comparadas com a branch base
21
21
 
22
22
  2. Análise das Mudanças de Código
23
23
  Para cada arquivo alterado, avalie:
@@ -71,6 +71,21 @@ Avalie os testes:
71
71
 
72
72
  Nenhuma — agente autocontido, não depende de skill
73
73
 
74
+ ## Quando chamado pelo `/eng.pre-pr`
75
+
76
+ O `/eng.pre-pr` passa a branch base (a `Branch base:` do `architecture.md`) e, da rodada 2 em diante, o diff incremental desde a rodada anterior. Use essa base no lugar de `origin/main` nos comandos de diff e log. Só aponte: não edite nem grave arquivos.
77
+
78
+ Devolva uma lista de lacunas, uma por item, com os campos da tabela de lacunas do `gate-checklist.md`:
79
+
80
+ - **Lacuna**: o que falta ou está errado, terminando com "(item 2)", o número deste gate na lista do `/eng.pre-pr`.
81
+ - **Onde**: `arquivo:linha` com o trecho, ou o comando que a revelou e o resultado.
82
+ - **Por que importa**: o impacto, em linguagem simples.
83
+ - **Refutação**: o que derrubaria esta lacuna, com o lugar a conferir.
84
+ - **Sugestão**: como resolver.
85
+ - **Severidade**: 🔴 Bloqueador, 🟡 Importante ou ⚪ Menor (redação é ⚪).
86
+
87
+ Lista vazia é uma resposta válida. Fora do `/eng.pre-pr`, vale o formato de resposta deste arquivo.
88
+
74
89
  ## Formato da Resposta
75
90
  Forneça uma revisão estruturada no seguinte formato:
76
91
 
@@ -17,6 +17,21 @@ Fonte de verdade do processo e do formato de entrega da documentação:
17
17
 
18
18
  ---
19
19
 
20
+ ## Quando chamado pelo `/eng.pre-pr`
21
+
22
+ O `/eng.pre-pr` passa a branch base (a `Branch base:` do `architecture.md`) e, da rodada 2 em diante, o diff incremental desde a rodada anterior. Use essa base no lugar de `origin/main` nos comandos de diff e log. Só aponte: não edite nem grave arquivos. Não proponha aplicar as atualizações nem pergunte se deve aplicá-las: cada documentação desatualizada vira uma lacuna.
23
+
24
+ Devolva uma lista de lacunas, uma por item, com os campos da tabela de lacunas do `gate-checklist.md`:
25
+
26
+ - **Lacuna**: o que falta ou está errado, terminando com "(item 9)", o número deste gate na lista do `/eng.pre-pr`.
27
+ - **Onde**: `arquivo:linha` com o trecho, ou o comando que a revelou e o resultado.
28
+ - **Por que importa**: o impacto, em linguagem simples.
29
+ - **Refutação**: o que derrubaria esta lacuna, com o lugar a conferir.
30
+ - **Sugestão**: como resolver.
31
+ - **Severidade**: 🔴 Bloqueador, 🟡 Importante ou ⚪ Menor (redação é ⚪).
32
+
33
+ Lista vazia é uma resposta válida. Fora do `/eng.pre-pr`, vale o formato de resposta deste arquivo.
34
+
20
35
  ## Fluxo de Trabalho
21
36
 
22
37
  ### 1. Analisar Mudanças de Código
@@ -26,8 +41,8 @@ Comece entendendo exatamente o que mudou:
26
41
  - Execute `git status` para verificar mudanças não commitadas
27
42
  - Execute `git diff` para visualizar mudanças não staged
28
43
  - Execute `git diff --staged` para visualizar mudanças staged
29
- - Execute `git log origin/main..HEAD --oneline` para ver os commits da branch
30
- - Execute `git diff origin/main...HEAD` para ver todas as mudanças da branch
44
+ - Execute `git log {BRANCH_BASE}..HEAD --oneline` para ver os commits da branch
45
+ - Execute `git diff {BRANCH_BASE}...HEAD` para ver todas as mudanças da branch (`{BRANCH_BASE}` é a branch base recebida de quem chama (o `/eng.pre-pr` passa a `Branch base:` do `architecture.md`); sem ela, `origin/main`.)
31
46
 
32
47
  Foque especialmente em:
33
48
  - Novas funcionalidades
@@ -21,10 +21,25 @@ Aplicar quando invocado via `eng.qa-refinement-entry` ou quando o argumento rece
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
- - Fonte de dados: `git diff origin/main...HEAD`
24
+ - Fonte de dados: `git diff {BRANCH_BASE}...HEAD` (`{BRANCH_BASE}` é a branch base recebida de quem chama (o `/eng.pre-pr` passa a `Branch base:` do `architecture.md`); sem ela, `origin/main`.)
25
25
  - Saída: `$ENG_DOCS/qa/{task-id}-test_coverage_branch_report.md`
26
26
 
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.
27
+ Regra de decisão: se `git diff {BRANCH_BASE}...HEAD` retornar vazio, operar em Modo A. Se retornar código, operar em Modo B.
28
+
29
+ ## Quando chamado pelo `/eng.pre-pr`
30
+
31
+ O `/eng.pre-pr` passa a branch base (a `Branch base:` do `architecture.md`) e, da rodada 2 em diante, o diff incremental desde a rodada anterior. Use essa base no lugar de `origin/main` nos comandos de diff e log. Só aponte: não edite nem grave arquivos. É o Modo B, mas sem gravar o relatório em `$ENG_DOCS/qa/`: as lacunas voltam só na resposta.
32
+
33
+ Devolva uma lista de lacunas, uma por item, com os campos da tabela de lacunas do `gate-checklist.md`:
34
+
35
+ - **Lacuna**: o que falta ou está errado, terminando com "(item 3)", o número deste gate na lista do `/eng.pre-pr`.
36
+ - **Onde**: `arquivo:linha` com o trecho, ou o comando que a revelou e o resultado.
37
+ - **Por que importa**: o impacto, em linguagem simples.
38
+ - **Refutação**: o que derrubaria esta lacuna, com o lugar a conferir.
39
+ - **Sugestão**: como resolver.
40
+ - **Severidade**: 🔴 Bloqueador, 🟡 Importante ou ⚪ Menor (redação é ⚪).
41
+
42
+ Lista vazia é uma resposta válida. Fora do `/eng.pre-pr`, vale o formato de resposta deste arquivo.
28
43
 
29
44
  ## Skills Disponíveis
30
45
 
@@ -290,9 +305,9 @@ Usar quando: invocado via `eng-qa` ou `eng-qa-planner`, ou quando git diff retor
290
305
 
291
306
  Comece entendendo o que mudou na branch atual:
292
307
 
293
- - Execute `git diff origin/main...HEAD --name-only` para listar todos os arquivos alterados
294
- - Execute `git diff origin/main...HEAD` para ver as mudanças em detalhe
295
- - Execute `git log origin/main..HEAD --oneline` para entender o histórico de commits
308
+ - Execute `git diff {BRANCH_BASE}...HEAD --name-only` para listar todos os arquivos alterados
309
+ - Execute `git diff {BRANCH_BASE}...HEAD` para ver as mudanças em detalhe
310
+ - Execute `git log {BRANCH_BASE}..HEAD --oneline` para entender o histórico de commits
296
311
  - Foque especialmente em:
297
312
  - Novas funções/métodos/classes
298
313
  - Lógica modificada em código existente
@@ -28,7 +28,7 @@ Em seguida, você deve:
28
28
  - quais mudanças não estão alinhadas.
29
29
 
30
30
  Formato da Resposta
31
- Você deve fornecer sua resposta exatamente no seguinte formato:
31
+ Você deve fornecer sua resposta exatamente no seguinte formato (quando chamado pelo `/eng.pre-pr`, use o formato da seção "Quando chamado pelo `/eng.pre-pr`"):
32
32
 
33
33
  ```
34
34
  [nome da branch]
@@ -51,6 +51,21 @@ Você deve fornecer sua resposta exatamente no seguinte formato:
51
51
  Regras Importantes
52
52
  Não faça nenhuma alteração no código ou nos requisitos, a menos que o usuário solicite explicitamente. Seu papel é avaliar e reportar, não corrigir.
53
53
 
54
+ ## Quando chamado pelo `/eng.pre-pr`
55
+
56
+ O `/eng.pre-pr` passa a branch base (a `Branch base:` do `architecture.md`) e, da rodada 2 em diante, o diff incremental desde a rodada anterior. Use essa base no lugar de `origin/main` nos comandos de diff e log. Só aponte: não edite nem grave arquivos. Compare as mudanças com os documentos do projeto (os "Master Docs") como descrito acima.
57
+
58
+ Devolva uma lista de lacunas, uma por item, com os campos da tabela de lacunas do `gate-checklist.md`:
59
+
60
+ - **Lacuna**: o que falta ou está errado, terminando com "(item 1)", o número deste gate na lista do `/eng.pre-pr`.
61
+ - **Onde**: `arquivo:linha` com o trecho, ou o comando que a revelou e o resultado.
62
+ - **Por que importa**: o impacto, em linguagem simples.
63
+ - **Refutação**: o que derrubaria esta lacuna, com o lugar a conferir.
64
+ - **Sugestão**: como resolver.
65
+ - **Severidade**: 🔴 Bloqueador, 🟡 Importante ou ⚪ Menor (redação é ⚪).
66
+
67
+ Lista vazia é uma resposta válida. Fora do `/eng.pre-pr`, vale o formato de resposta deste arquivo.
68
+
54
69
  ## Skills Disponíveis
55
70
 
56
71
  Nenhuma — agente autocontido, não depende de skill
package/bin/README.md CHANGED
@@ -124,7 +124,7 @@ if (fileURLToPath(import.meta.url) === process.argv[1]) {
124
124
  ## Testes
125
125
 
126
126
  - `npm run test:comandos` — valida que toda referência a comando/skill nos workflows e skills existe de fato (não testa o CLI em runtime).
127
- - `npm run test:filtro` — testes unitários de `profile-filter.js`, `token-report.js`, `flow-map`, `env-loader.js`, `sync-engine.js`, `vcs/api.js`, `tasks/comment.js`, `integrations/` (catálogo e runner) e do upgrade.
127
+ - `npm run test:filtro` — testes unitários de `profile-filter.js`, `token-report.js`, `flow-map`, `env-loader.js`, `sync-engine.js`, `vcs/api.js`, `tasks/comment.js`, `integrations/` (catálogo e runner) e do upgrade, além dos testes que guardam o texto de workflows e rules (por exemplo, `gate-checklist-rodadas`, `publish-comments-rules` e `pre-pr-criterio`). A lista completa é o script `test:filtro` do `package.json`.
128
128
  - Não há teste automatizado de ponta a ponta do CLI (`jarvis <comando>` real) — a camada de dispatch (`program.js`, `bin/commands/*.js`) não é cobrida pelos testes unitários. Qualquer mudança ali precisa de validação manual dos comandos afetados.
129
129
 
130
130
  ### Como testar código que lê `fs`/rede sem mockar módulo
@@ -39,6 +39,115 @@ function ask(rl, question) {
39
39
  return new Promise((resolve) => rl.question(question, resolve));
40
40
  }
41
41
 
42
+ /**
43
+ * Escolha do menu de pasta já instalada (#189). Função pura, para testar sem subprocesso.
44
+ * Com ENV.md: Enter ou 1 = atualizar (a opção recomendada), 2 = reinstalar, 3 = cancelar.
45
+ * Sem ENV.md não há o que atualizar (o upgrade precisa dele): o menu só tem 2 e 3, e Enter
46
+ * cancela, para o padrão nunca ser destrutivo (Q3). Qualquer outra resposta é inválida (Q9).
47
+ * @param {string} answer - linha digitada
48
+ * @param {{ hasEnv: boolean }} opts
49
+ * @returns {"upgrade" | "reinstall" | "cancel" | null} `null` = resposta inválida
50
+ */
51
+ export function parseMenuChoice(answer, { hasEnv }) {
52
+ const choice = String(answer).trim();
53
+ if (choice === "2") return "reinstall";
54
+ if (choice === "3") return "cancel";
55
+ if (hasEnv && (choice === "" || choice === "1")) return "upgrade";
56
+ if (!hasEnv && choice === "") return "cancel";
57
+ return null;
58
+ }
59
+
60
+ /**
61
+ * O que ainda pede o `/jarvis-init upgrade` na IDE depois do `jarvis init --upgrade` (#189, Q4).
62
+ * Só as pendências que o terminal não resolve: eixo sem forma de acesso, chave vazia, Central docs
63
+ * no formato antigo e TOKEN_TASK_MANAGER com valor. Não contam o aviso de WORKSPACE_MODE (conferir
64
+ * no ENV.md), vendor não suportado (vira freelance) nem as chaves novas acrescentadas (Q15: o
65
+ * `/jarvis-init` as põe no assunto delas na próxima vez que rodar). Nunca inclui valores do ENV.
66
+ * @param {{ pendingAxes: string[], envPlan: { pending?: string[], warnings?: string[] } }} input
67
+ * @returns {string[]} um motivo por pendência; vazio = nada pendente
68
+ */
69
+ export function upgradePendencies({ pendingAxes, envPlan }) {
70
+ const reasons = [];
71
+ if (pendingAxes.length) reasons.push(`confirmar o acesso de ${pendingAxes.join(" e ")}`);
72
+ if (envPlan.pending?.length) reasons.push(`preencher ${envPlan.pending.join(", ")}`);
73
+ const warnings = envPlan.warnings || [];
74
+ if (warnings.includes("CENTRAL_DOCS_REPO")) reasons.push("migrar o Central docs");
75
+ if (warnings.includes("TOKEN_TASK_MANAGER")) reasons.push("remover o TOKEN_TASK_MANAGER");
76
+ return reasons;
77
+ }
78
+
79
+ /**
80
+ * Leitor de linhas que não perde resposta: guarda as linhas que chegam antes da pergunta
81
+ * (pipe com várias linhas, como `printf 's\n3\n'`) e devolve `null` quando a entrada fecha
82
+ * sem linha (CI com `</dev/null`), em vez de esperar para sempre como o `rl.question` (Q2).
83
+ * @param {import("node:readline").Interface} rl
84
+ * @returns {(question: string) => Promise<string | null>}
85
+ */
86
+ function lineReader(rl) {
87
+ const lines = [];
88
+ const waiting = [];
89
+ let closed = false;
90
+ rl.on("line", (line) => (waiting.length ? waiting.shift()(line) : lines.push(line)));
91
+ rl.on("close", () => {
92
+ closed = true;
93
+ while (waiting.length) waiting.shift()(null);
94
+ });
95
+ // Sem terminal, a resposta não aparece na tela: a quebra de linha separa a pergunta do que vem depois.
96
+ const echo = (line) => {
97
+ if (line !== null && !process.stdin.isTTY) process.stdout.write("\n");
98
+ return line;
99
+ };
100
+ return (question) => {
101
+ process.stdout.write(question);
102
+ if (lines.length) return Promise.resolve(echo(lines.shift()));
103
+ if (closed) return Promise.resolve(null);
104
+ return new Promise((resolve) => waiting.push(resolve)).then(echo);
105
+ };
106
+ }
107
+
108
+ /**
109
+ * Menu de `jarvis init` numa pasta que já tem a pasta da IDE (#189).
110
+ * Decide pela resposta lida, não por `process.stdin.isTTY`: `printf '\n' |` também atualiza.
111
+ * `askLine` é o leitor compartilhado com o menu de IDE: um leitor só para a entrada inteira,
112
+ * para a resposta de um menu não consumir a do outro (`printf '1\n3\n' | jarvis init`).
113
+ * @returns {Promise<"upgrade" | "reinstall" | "cancel" | "no-answer">} `no-answer` = entrada fechada (a mensagem já saiu)
114
+ */
115
+ async function askInstalledMenu({ folder, lockVersion, packageVersion, hasEnv, askLine }) {
116
+ const installed = lockVersion === "desconhecida" ? "versão desconhecida" : `v${lockVersion}`;
117
+ logger.info(
118
+ `${YELLOW}⚠️ Jarvis já instalado em .${folder} (${installed} · pacote v${packageVersion})${NC}`
119
+ );
120
+ logger.info("");
121
+ if (!hasEnv) {
122
+ logger.info(`${DIM}Atualizar precisa do ENV.md: reinstale e rode /jarvis-init na IDE.${NC}`);
123
+ logger.info("");
124
+ }
125
+ logger.info("O que deseja fazer?");
126
+ if (hasEnv) {
127
+ logger.info(
128
+ ` 1) Atualizar (recomendado) — sincroniza com a versão do pacote, preserva o ENV.md`
129
+ );
130
+ }
131
+ logger.info(` 2) Reinstalar — sobrescreve os arquivos do framework`);
132
+ logger.info(` 3) Cancelar`);
133
+ logger.info("");
134
+
135
+ const valid = hasEnv ? "1, 2 ou 3" : "2 ou 3";
136
+ for (;;) {
137
+ const answer = await askLine(`Escolha [${hasEnv ? "1" : "3"}]: `);
138
+ if (answer === null) {
139
+ logger.info("");
140
+ logger.info(
141
+ `${BLUE}ℹ️ Sem resposta: nada alterado. Use --upgrade (atualizar) ou --force (reinstalar).${NC}\n`
142
+ );
143
+ return "no-answer";
144
+ }
145
+ const choice = parseMenuChoice(answer, { hasEnv });
146
+ if (choice) return choice;
147
+ logger.info(`${RED}Opção inválida. Escolha ${valid}.${NC}`);
148
+ }
149
+ }
150
+
42
151
  /**
43
152
  * Grava o Context7 (MCP obrigatório) no arquivo de MCP da IDE e mostra o que falta.
44
153
  * Só avisa: problema no arquivo de MCP não muda o código de saída (instalação e --upgrade).
@@ -73,6 +182,21 @@ export async function init(flags) {
73
182
  return;
74
183
  }
75
184
 
185
+ // Um leitor de linhas só para os dois menus (IDE e pasta já instalada), criado na primeira
186
+ // pergunta e fechado antes do sync ou do upgrade (que abre o seu, só com terminal).
187
+ let reader = null;
188
+ const askLine = (question) => {
189
+ if (!reader) {
190
+ const rl = createInterface({ input: process.stdin, terminal: false });
191
+ reader = { rl, next: lineReader(rl) };
192
+ }
193
+ return reader.next(question);
194
+ };
195
+ const closeReader = () => {
196
+ reader?.rl.close();
197
+ reader = null;
198
+ };
199
+
76
200
  let ide;
77
201
  if (flags.ide) {
78
202
  if (!isIDESupported(flags.ide)) {
@@ -100,14 +224,15 @@ export async function init(flags) {
100
224
  });
101
225
  logger.info("");
102
226
 
103
- const rl = createInterface({
104
- input: process.stdin,
105
- output: process.stdout,
106
- });
107
-
108
227
  let choice;
109
228
  while (true) {
110
- choice = await ask(rl, `${BLUE}Digite o número da sua escolha (1-${IDES.length}): ${NC}`);
229
+ choice = await askLine(`${BLUE}Digite o número da sua escolha (1-${IDES.length}): ${NC}`);
230
+ if (choice === null) {
231
+ closeReader();
232
+ logger.info("");
233
+ logger.info(`${BLUE}ℹ️ Sem resposta: nada alterado. Use --ide {ide}.${NC}\n`);
234
+ return;
235
+ }
111
236
  const choiceNum = parseInt(choice, 10);
112
237
 
113
238
  if (choiceNum >= 1 && choiceNum <= IDES.length) {
@@ -128,40 +253,41 @@ export async function init(flags) {
128
253
  );
129
254
  }
130
255
  }
131
-
132
- rl.close();
133
256
  }
134
257
 
135
258
  const ideDir = join(cwd, `.${getIDEFolder(ide)}`);
136
259
 
137
- // Avisos de versão antes da pergunta de reinstalação: quem responde "s" (ou usa --force)
138
- // já sabe se vai rebaixar os assets.
139
- warnVersionMismatch(cwd);
260
+ // Avisos de versão antes do menu: quem escolhe reinstalar (ou usa --force) já sabe se vai
261
+ // rebaixar os assets. O resultado vai para o runUpgrade, que não repete o aviso (Q8).
262
+ const lockVersion = readLockVersion(cwd);
263
+ const versionCheck = warnVersionMismatch(cwd, lockVersion);
140
264
 
141
- // Reinstall: --force ou confirmação "s" devem sobrescrever assets (cpSync force:true).
265
+ // Reinstalação: --force ou a opção 2 do menu sobrescrevem os assets (cpSync force:true).
142
266
  let forceSync = !!flags.force;
143
267
 
144
268
  if (existsSync(ideDir) && !forceSync) {
145
- logger.info(`${YELLOW}⚠️ Framework já instalado em .${getIDEFolder(ide)}${NC}`);
146
- logger.info("");
147
-
148
- const rl = createInterface({
149
- input: process.stdin,
150
- output: process.stdout,
269
+ const choice = await askInstalledMenu({
270
+ folder: getIDEFolder(ide),
271
+ lockVersion,
272
+ packageVersion: versionCheck.running,
273
+ hasEnv: existsSync(join(ideDir, "ENV.md")),
274
+ askLine,
151
275
  });
152
- const confirm = await ask(
153
- rl,
154
- `Deseja reinstalar? Isso irá sobrescrever a instalação existente. (s/N): `
155
- );
156
- rl.close();
157
-
158
- if (confirm.toLowerCase() !== "s") {
276
+ closeReader();
277
+ if (choice === "upgrade") {
278
+ logger.info("");
279
+ process.exitCode = await runUpgrade(cwd, { ...flags, ide, versionCheck });
280
+ return;
281
+ }
282
+ if (choice === "no-answer") return;
283
+ if (choice === "cancel") {
159
284
  logger.info(`${BLUE}ℹ️ Instalação cancelada.${NC}\n`);
160
285
  return;
161
286
  }
162
287
  forceSync = true;
163
288
  logger.info("");
164
289
  }
290
+ closeReader();
165
291
 
166
292
  const ideConfig = getIDEConfig(ide);
167
293
  logger.info(`${BLUE}Configurando ambiente para ${ideConfig?.name}...${NC}`);
@@ -169,13 +295,15 @@ export async function init(flags) {
169
295
  const result = syncAssets(cwd, ide, {
170
296
  force: forceSync,
171
297
  workflowsFolder: getWorkflowsFolder(ide),
298
+ // Reinstalação não troca valor do ENV.md (RTK_ENABLED=false fica), como o upgrade (Q1).
299
+ keepEnvValues: forceSync,
172
300
  });
173
301
 
174
302
  for (const item of result.copied) {
175
303
  logger.info(`${BLUE} ↳ ${item}${NC}`);
176
304
  }
177
305
  for (const item of result.skipped) {
178
- logger.info(`${DIM} ↳ ${item} (já existe)${NC}`);
306
+ logger.info(`${DIM} ↳ ${item}${NC}`); // o motivo já vem no item (sync-engine)
179
307
  }
180
308
  for (const item of result.errors) {
181
309
  logger.error(`${RED} ✗ ${item}${NC}`);
@@ -371,7 +499,9 @@ async function runUpgrade(cwd, flags) {
371
499
  const workspaceDir = resolved.root;
372
500
  const envPath = resolved.path;
373
501
  const previousVersion = readLockVersion(workspaceDir);
374
- const { downgrade, running } = warnVersionMismatch(workspaceDir, previousVersion);
502
+ // Vindo do menu do `init`, o aviso já saiu antes da escolha (Q8): só reaproveita o resultado.
503
+ const { downgrade, running } =
504
+ flags.versionCheck ?? warnVersionMismatch(workspaceDir, previousVersion);
375
505
  if (downgrade && !flags.yes) {
376
506
  let go = false;
377
507
  if (process.stdin.isTTY) {
@@ -480,7 +610,11 @@ async function runUpgrade(cwd, flags) {
480
610
  // 4. Resumo (só nomes de chave e caminhos)
481
611
  const pkg = getPackageInfo();
482
612
  logger.info("");
483
- logger.info(`${GREEN}✅ Upgrade concluído: ${previousVersion} → ${pkg.version}${NC}`);
613
+ logger.info(
614
+ previousVersion === pkg.version
615
+ ? `${GREEN}✅ Upgrade concluído: já na v${pkg.version} (arquivos ressincronizados)${NC}`
616
+ : `${GREEN}✅ Upgrade concluído: ${previousVersion} → ${pkg.version}${NC}`
617
+ );
484
618
  logger.info(` • Arquivos do framework: ${sync.copied.length} item(ns) sincronizado(s)`);
485
619
  logger.info(
486
620
  ` • ENV.md: ${addedKeys.length ? `+ ${addedKeys.join(", ")}` : "nenhuma variável nova"}`
@@ -532,11 +666,12 @@ async function runUpgrade(cwd, flags) {
532
666
  const access = (futureEntries.get(`${key}_ACCESS`) || "").trim();
533
667
  return getVendor(futureEntries.get(key)) && (!access || /^\[.*\]$/.test(access));
534
668
  });
669
+ const pending = upgradePendencies({ pendingAxes, envPlan });
535
670
  logger.info("");
536
671
  logger.info(
537
- pendingAxes.length
538
- ? `${CYAN}➡️ Agora rode ${BOLD}/jarvis-init upgrade${NC}${CYAN} na IDE para confirmar o acesso de ${pendingAxes.join(" e ")} (e as variáveis).${NC}\n`
539
- : `${CYAN}➡️ Agora rode ${BOLD}/jarvis-init upgrade${NC}${CYAN} na IDE.${NC}\n`
672
+ pending.length
673
+ ? `${CYAN}➡️ Agora rode ${BOLD}/jarvis-init upgrade${NC}${CYAN} na IDE: ${pending.join("; ")}.${NC}\n`
674
+ : `${GREEN}✓ Nada pendente na IDE.${NC}\n`
540
675
  );
541
676
  return sync.errors.length || moveErrors.length || staleErrors.length ? 1 : 0;
542
677
  }
@@ -87,18 +87,18 @@ export function buildProgram() {
87
87
  const initCmd = cmd(program, "init")
88
88
  .description("Bootstrap do framework no workspace")
89
89
  .option("--ide <ide>", "IDE a configurar (pula o menu interativo)")
90
- .option("--force", "Sobrescreve a instalação existente sem perguntar")
90
+ .option("--force", "Reinstala sem mostrar o menu: sobrescreve os arquivos do framework")
91
91
  .option(
92
92
  "--upgrade",
93
- "Atualiza um projeto já configurado: arquivos do framework, variáveis novas do ENV e pastas de documentos"
93
+ "Atualiza sem mostrar o menu: arquivos do framework, variáveis novas do ENV e pastas de documentos"
94
94
  )
95
95
  .option(
96
96
  "--yes",
97
- "Com --upgrade, move os documentos para as pastas novas e aceita rebaixar os assets sem perguntar"
97
+ "Com --upgrade (ou a opção Atualizar do menu), move os documentos para as pastas novas e aceita rebaixar os assets sem perguntar"
98
98
  )
99
99
  .option(
100
100
  "--remove-stale",
101
- "Com --upgrade, remove sem perguntar os skills instalados que saíram do framework (só os listados)"
101
+ "Com --upgrade (ou a opção Atualizar do menu), remove sem perguntar os skills instalados que saíram do framework (só os listados)"
102
102
  );
103
103
  withLogOptions(initCmd).action((options) => init(toLegacyFlags(options)));
104
104
 
@@ -80,7 +80,7 @@ export function syncAssets(targetDir, ide, opts = {}) {
80
80
  const dest = join(ideDir, destDirName);
81
81
 
82
82
  if (!existsSync(src)) {
83
- result.skipped.push(`${dir}/ (not found in framework)`);
83
+ result.skipped.push(`${dir}/ (não existe no framework)`);
84
84
  continue;
85
85
  }
86
86
 
@@ -102,7 +102,7 @@ export function syncAssets(targetDir, ide, opts = {}) {
102
102
  if (!existsSync(src)) continue;
103
103
 
104
104
  if (existsSync(dest) && !opts.force) {
105
- result.skipped.push(`${file} (already exists)`);
105
+ result.skipped.push(`${file} (já existe)`);
106
106
  continue;
107
107
  }
108
108
 
@@ -228,7 +228,11 @@ function reconcileRtk(ideDir, targetDir, ide, { keepEnvValues = false } = {}) {
228
228
  return "(rtk) detectado — RTK_ENABLED=true no ENV.md";
229
229
  }
230
230
  if (!env.includes("RTK_ENABLED")) {
231
- env = env.trimEnd() + "\n\n# --- RTK — Token Killer ---\nRTK_ENABLED=true\n";
231
+ // Moldura do ENV-template (#189): o /jarvis-init não aponta o bloco como separador antigo.
232
+ const bar =
233
+ "# =============================================================================";
234
+ env =
235
+ env.trimEnd() + `\n\n${bar}\n# RTK — Token Killer (opcional)\n${bar}\nRTK_ENABLED=true\n`;
232
236
  writeFileSync(envPath, env, "utf-8");
233
237
  return "(rtk) detectado — RTK_ENABLED=true adicionado ao ENV.md";
234
238
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jarvis-ai-framework",
3
- "version": "8.1.0",
3
+ "version": "8.2.0",
4
4
  "description": "Jarvis framework",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,7 +11,7 @@
11
11
  "prepublishOnly": "echo 'Publishing jarvis-framework...'",
12
12
  "publish:gitlab": "npm publish",
13
13
  "test:comandos": "node test/comandos-docs.test.js",
14
- "test:filtro": "node --test test/profile-filter.test.js test/token-report.test.js test/flow-map.test.js test/env-loader.test.js test/sync-engine.test.js test/vcs-api.test.js test/fetch-file.test.js test/publish-file.test.js test/docs-storage.test.js test/env-upgrade.test.js test/docs-migrate.test.js test/init-upgrade.test.js test/workspace-mode.test.js test/stale-skills.test.js test/session-scan.test.js test/framework-refs.test.js test/validate-frontmatter.test.js test/mcp-config.test.js test/integrations-catalog.test.js test/cli-runner.test.js test/tasks-comment.test.js test/version.test.js test/gate-checklist-rodadas.test.js test/publish-comments-rules.test.js",
14
+ "test:filtro": "node --test test/profile-filter.test.js test/token-report.test.js test/flow-map.test.js test/env-loader.test.js test/sync-engine.test.js test/vcs-api.test.js test/fetch-file.test.js test/publish-file.test.js test/docs-storage.test.js test/env-upgrade.test.js test/docs-migrate.test.js test/init-upgrade.test.js test/workspace-mode.test.js test/stale-skills.test.js test/session-scan.test.js test/framework-refs.test.js test/validate-frontmatter.test.js test/mcp-config.test.js test/integrations-catalog.test.js test/cli-runner.test.js test/tasks-comment.test.js test/version.test.js test/gate-checklist-rodadas.test.js test/publish-comments-rules.test.js test/pre-pr-criterio.test.js test/nomes-soltos.test.js",
15
15
  "lint": "eslint bin/ test/",
16
16
  "lint:fix": "eslint bin/ test/ --fix",
17
17
  "format": "prettier --write bin/ test/",
@@ -211,6 +211,7 @@ Para cada tarefa, incluir:
211
211
 
212
212
  ### Fase 5: Validação
213
213
 
214
+ 0. **Executar** o Checklist de Conclusão (abaixo) item a item contra o `plan.md` e corrigir o plano antes de apresentar (passe crítico, Fase 4 do workflow)
214
215
  1. **Apresentar** o plano ao humano
215
216
  2. **Discutir** ajustes necessários
216
217
  3. **Atualizar** `architecture.md` se houver mudanças arquiteturais
@@ -300,6 +301,7 @@ Antes de considerar o plano completo:
300
301
  - [ ] Dependências estão claras (sequencial/paralelo)
301
302
  - [ ] Status iniciais definidos (todos ⏳)
302
303
  - [ ] Conteúdo em português (pt-BR)
304
+ - [ ] Passe crítico executado (este checklist conferido item a item) e "Ajustes antes da aprovação" atualizado no cabeçalho a cada "Ajustar o plano"
303
305
  - [ ] Validado com o humano
304
306
  - [ ] `plan.md` criado em `$SESSIONS_DIR/eng/{TASK_MANAGER_KEY}/`
305
307
 
@@ -75,9 +75,11 @@ Avaliar **apenas** as mudanças da branch atual em relação à branch base (`Br
75
75
  git diff {BRANCH_BASE}...HEAD
76
76
  ```
77
77
 
78
+ Da rodada 2 em diante, cada gate roda só se o diff desde o `Commit:` da rodada anterior tocar o que ele lê (seção "Rodada incremental" do `eng.pre-pr.md`). O gate que roda continua revisando a branch inteira.
79
+
78
80
  ### Evidências Obrigatórias
79
81
 
80
- Cada lacuna cita onde está (`arquivo:linha`) ou o comando que a revelou e o resultado.
82
+ Cada lacuna cita onde está (`arquivo:linha` com o trecho) ou o comando que a revelou e o resultado, **e o que a derrubaria** (coluna Refutação). Antes de gravar, o passe crítico (seção "Passe crítico" do `eng.pre-pr.md`) remove a lacuna cuja refutação se sustenta e rebaixa a que pesa menos do que a severidade dada.
81
83
 
82
84
  ---
83
85
 
@@ -86,9 +88,12 @@ Cada lacuna cita onde está (`arquivo:linha`) ou o comando que a revelou e o res
86
88
  O workflow **deve sempre** terminar com:
87
89
 
88
90
  1. **`gate-checklist.md`** em `$SESSIONS_DIR/eng/{TASK_MANAGER_KEY}/`:
89
- - semáforo (🟢 / 🟡 / 🔴), resumo, lacunas por área (o quê, onde, por que importa, sugestão, severidade, **Decisão**), evidências e PR Brief.
91
+ - semáforo (🟢 / 🟡 / 🔴), resumo (com a linha `Critério de parada:`), lacunas por área (o quê, com o "(item N)" do gate que a achou no fim, onde, por que importa, refutação, sugestão, severidade, **Decisão**), evidências e PR Brief (com as rodadas por severidade, a rodada em que o critério de parada agiu, os ajustes do `architecture.md` e do `plan.md` antes da aprovação e as lições desta tarefa; guardar lições entre tarefas não é deste comando, é da #169).
90
92
  - `Rodada` e `Commit` no cabeçalho, as rodadas anteriores e, da rodada 2 em diante, a resolução das lacunas da rodada anterior.
93
+ - Da rodada 2 em diante, as áreas cujos gates não rodaram na rodada incremental, marcadas como "Não reavaliada nesta rodada".
91
94
  - É sempre a rodada mais recente: ao rodar de novo na mesma sessão, o relatório existente é preservado como `gate-checklist-rodada-{N}.md` (seção "Rodadas" do `eng.pre-pr.md`). **Nunca** sobrescrever nem apagar uma rodada; renomear imediatamente antes de gravar.
92
- 2. **Decisões do usuário** gravadas na coluna Decisão (🔧 Corrigir / 👍 Aceitar + motivo; vazia se o usuário escolheu "Só gerar o relatório", exceto as decisões herdadas da rodada anterior).
95
+ 2. **Decisões do usuário** gravadas na coluna Decisão (🔧 Corrigir / 👍 Aceitar + motivo; vazia se o usuário escolheu "Só gerar o relatório", exceto as decisões herdadas da rodada anterior). Na variante B do fechamento, as ⚪ aceitas levam `👍 Aceitar: aceito no fechamento da rodada {N}` e as 🟡 aceitas em bloco (só com o teto atingido) levam `👍 Aceitar: teto de rodadas atingido na rodada {N}`, e as decididas uma a uma levam o motivo dado pela pessoa; decisão herdada nunca é trocada.
96
+ - **Critério de parada** (seção "Fechamento" do `eng.pre-pr.md`): teto de rodadas pela urgência do `context.md` (`alta` 3 · `normal` 5 · `baixa` 8) ou rodada sem lacuna 🔴 ou 🟡 nova (com os itens 2 e 3 tendo rodado). Sem 🔴 pendente nem 🟡 herdada com `🔧 Corrigir`, e com só ⚪ sem decisão (ou também 🟡, com o critério de parada atingido), o fechamento usa a variante B. Na variante B, "Aceitar as ⚪ e seguir para o PR" é a primeira opção (a recomendada) e "Só gerar o relatório" mantém o fluxo de hoje. Só o teto aceita 🟡 em bloco; com o gatilho "sem lacuna nova", as 🟡 são decididas uma a uma. O critério nunca pula a revisão nem a gravação. Fora desses casos (por exemplo, com 🔴 pendente), o ciclo segue na variante A. Sempre que o teto for atingido e houver pendência, o fechamento avisa.
97
+ - **Pendentes não reavaliadas** (rodada incremental): a lacuna pendente da rodada anterior cujo gate de origem (o "(item N)" da coluna Lacuna) não rodou é copiada para a rodada nova, com a decisão herdada.
93
98
  3. **Pergunta** (`AskUserQuestion`) sobre registrar o resultado como comentário no card (`/eng-global-task-comment`): Não, manter só na sessão (como hoje) / Sim, só o resumo / Sim, o relatório completo. Nunca publicar sem a resposta. Freelance: não pergunta, só na sessão.
94
99
  4. **Aguardar permissão explícita** do usuário para prosseguir para `/eng.pr`.