jarvis-ai-framework 6.1.0 → 7.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.md +4 -4
- package/README.md +3 -3
- package/agents/engineering/eng.docs-consolidator.agent.md +1 -1
- package/agents/engineering/qa/eng.qa.test-planner.md +2 -2
- package/bin/README.md +11 -8
- package/bin/commands/docs-publish.js +1 -1
- package/bin/commands/init.js +85 -12
- package/bin/lib/cli/program.js +22 -7
- package/bin/lib/config/ide-config.js +56 -0
- package/bin/lib/core/docs-migrate.js +17 -5
- package/bin/lib/core/env-upgrade.js +56 -10
- package/bin/lib/core/mcp-config.js +197 -0
- package/bin/lib/core/workspace-mode.js +13 -2
- package/bin/lib/docs/fetch-file.js +4 -1
- package/bin/lib/docs/storage/filesystem.js +31 -8
- package/bin/lib/integrations/cli-runner.js +412 -0
- package/bin/lib/integrations/vendors.json +243 -0
- package/bin/lib/tasks/comment.js +133 -109
- package/bin/lib/utils/git-parser.js +36 -56
- package/bin/lib/vcs/api.js +102 -158
- package/bin/lib/vcs/create-issue.js +33 -26
- package/bin/lib/vcs/create-merge.js +64 -15
- package/package.json +4 -3
- package/rules/AGENTS.md +2 -1
- package/rules/engineering/eng-rules.md +2 -1
- package/rules/engineering/eng.integrations-rules.md +21 -22
- package/rules-on-demand/doc-links-rules.md +103 -0
- package/rules-on-demand/engineering/eng.start-rules.md +1 -1
- package/skills/AGENTS.md +4 -4
- package/skills/SKILLS-ROADMAP.md +1 -1
- package/skills/eng-backend-microservices-trace/SKILL.md +7 -8
- package/skills/eng-global-investigation/references/caso-investigar.md +1 -1
- package/skills/eng-global-pr/SKILL.md +3 -3
- package/skills/eng-global-task-comment/SKILL.md +65 -26
- package/skills/eng-global-tech-spec/references/delivery-spec.md +1 -1
- package/skills/jarvis-docs-central/README.md +3 -6
- package/skills/jarvis-docs-central/SKILL.md +3 -2
- package/skills/jarvis-init/SKILL.md +114 -96
- package/skills/jarvis-init/assets/mcp-configs.md +57 -75
- package/skills/jarvis-init/assets/onboarding-checklist.md +6 -6
- package/skills/jarvis-init/assets/setup-guide.md +45 -22
- package/skills/jarvis-report-issue/SKILL.md +46 -10
- package/skills/product-roadmap-report/commands/status.roadmap.preview.md +1 -1
- package/templates/ENV-template.md +32 -18
- package/templates/cdd-mind-map-template.md +1 -1
- package/templates/engineering/ARD-template.md +2 -2
- package/templates/engineering/PR-template.md +6 -2
- package/templates/engineering/architecture-template.md +14 -8
- package/templates/engineering/plan-template.md +4 -2
- package/templates/engineering/tech-spec-template.md +9 -9
- package/templates/product/prod-frd-template.md +21 -19
- package/templates/product/prod-prd-template.md +32 -30
- package/workflows/engineering/eng.breakdown-subtasks.md +2 -0
- package/workflows/engineering/eng.build-tech-spec.md +3 -1
- package/workflows/engineering/eng.create-ard-from-code.md +2 -0
- package/workflows/engineering/eng.create-ard.md +2 -0
- package/workflows/engineering/eng.create-rfc.md +2 -0
- package/workflows/engineering/eng.debug.md +3 -1
- package/workflows/engineering/eng.docs.md +3 -1
- package/workflows/engineering/eng.light-arch.md +2 -0
- package/workflows/engineering/eng.plan.md +2 -0
- package/workflows/engineering/eng.pr.md +21 -19
- package/workflows/engineering/eng.pre-pr.md +5 -1
- package/workflows/engineering/eng.review.md +2 -0
- package/workflows/engineering/eng.rpa.robot.md +3 -0
- package/workflows/engineering/eng.spec-consolidate.md +2 -0
- package/workflows/engineering/eng.start.md +5 -1
- package/workflows/engineering/eng.work.md +2 -0
- package/workflows/engineering/qa/eng.qa-refinement-entry.md +3 -3
- package/workflows/product/prod.roadmap.preview.md +1 -1
- package/workflows/product/prod.spec.ard.md +2 -0
- package/workflows/product/prod.spec.breakdown.md +2 -0
- package/workflows/product/prod.spec.clarify.md +2 -0
- package/workflows/product/prod.spec.discovery.md +2 -0
- package/workflows/product/prod.spec.epic.md +2 -0
- package/workflows/product/prod.spec.frd.md +2 -0
- package/workflows/product/prod.spec.issue.md +5 -3
- package/workflows/product/prod.spec.prd.md +2 -0
- package/workflows/warm-up.md +1 -1
- package/bin/lib/utils/npmrc-parser.js +0 -99
package/AGENTS.md
CHANGED
|
@@ -62,8 +62,8 @@ prompts/
|
|
|
62
62
|
│ │ ├── product-specs-update/
|
|
63
63
|
│ │ ├── eng-qa/ # Base neutra de QA (+ especializações em QA_SPECIALIZATIONS)
|
|
64
64
|
│ │ ├── eng-qa-planner/ # Planejamento de testes e análise de gaps de cobertura
|
|
65
|
-
│ │ ├── jarvis-report-issue/ # Auto-report de bugs no repo do Jarvis (
|
|
66
|
-
│ │ ├── eng-global-task-comment/ # Comentário no card (Jira/
|
|
65
|
+
│ │ ├── jarvis-report-issue/ # Auto-report de bugs no repo do Jarvis (CLI do host: gh)
|
|
66
|
+
│ │ ├── eng-global-task-comment/ # Comentário no card (Jira/GitHub/GitLab, pela CLI do vendor)
|
|
67
67
|
│ │ ├── jarvis-list-specializations/ # Lista especializações de stack instaladas e registradas
|
|
68
68
|
│ │ ├── jarvis-create-specialization/ # Cria uma especialização de stack a partir do código do projeto e a registra
|
|
69
69
|
│ │ ├── jarvis-evolution-architect/ # Mentor de arquitetura para a evolução do Jarvis
|
|
@@ -78,7 +78,7 @@ prompts/
|
|
|
78
78
|
│ │ │ └── frontend/ # eng.frontend-rules.md (HUB: FRONTEND)
|
|
79
79
|
│ │ └── product/ # prod-rules.md (AREA=PRODUCT)
|
|
80
80
|
│ │
|
|
81
|
-
│ ├── rules-on-demand/ # Rules de uma etapa
|
|
81
|
+
│ ├── rules-on-demand/ # Rules lidas pelo workflow que as usa (as de uma etapa, como start, plan e pr, e as transversais, como doc-links e warm-up)
|
|
82
82
|
│ │
|
|
83
83
|
│ ├── scripts/ # Scripts utilitarios
|
|
84
84
|
│ └── docs/ # Documentacao adicional
|
|
@@ -219,7 +219,7 @@ Quando atuar em um projeto alvo, detectar comandos em:
|
|
|
219
219
|
2. `$IDE/ENV.md` - Contexto do ambiente
|
|
220
220
|
3. `$IDE/JARVIS.md` - Guia principal de uso
|
|
221
221
|
4. `$IDE/rules/` - Regras especificas por dominio (carregadas no inicio)
|
|
222
|
-
`$IDE/rules-on-demand/` - Regras de uma etapa
|
|
222
|
+
`$IDE/rules-on-demand/` - Regras de uma etapa ou transversais, lidas pelo workflow que as usa
|
|
223
223
|
5. `$IDE/agents/` - Definicoes de agentes
|
|
224
224
|
6. `$IDE/skills/` - Playbooks operacionais
|
|
225
225
|
7. `$IDE/workflows/` - Templates de execucao
|
package/README.md
CHANGED
|
@@ -184,10 +184,10 @@ Escolha **Upgrade (C)** — adiciona chaves novas do template sem apagar os valo
|
|
|
184
184
|
|-------|------------|
|
|
185
185
|
| `git pull` | Código do framework no clone |
|
|
186
186
|
| `npm install -g` / `--save-dev` | Binário `jarvis` + pacote que o `init` lê |
|
|
187
|
-
| `jarvis init` (ou postinstall com lock) | Arquivos em `.$IDE/` (skills, agents, workflows…) |
|
|
187
|
+
| `jarvis init` (ou postinstall com lock) | Arquivos em `.$IDE/` (skills, agents, workflows…) e o Context7 no arquivo de MCP da IDE (ex.: `.mcp.json` no Claude Code; só acrescenta, sem apagar outros servidores) |
|
|
188
188
|
| `/jarvis-init` Upgrade | Só `$IDE/ENV.md` (variáveis faltantes) e, se você aceitar o passo opcional "adicionar as stacks", as listas de especializações |
|
|
189
189
|
|
|
190
|
-
`ENV.md`, `.jarvis/sessions/` e o `.gitignore` do workspace **não** são sobrescritos pelo sync de assets.
|
|
190
|
+
`ENV.md`, `.jarvis/sessions/` e o `.gitignore` do workspace **não** são sobrescritos pelo sync de assets. O `.gitignore` só é criado ou alterado quando a raiz tem `.git`; sem `.git` (como no modo workspace) a raiz não é versionada e o init não o toca.
|
|
191
191
|
|
|
192
192
|
| Papel | Descricao | Link |
|
|
193
193
|
|-------|-----------|------|
|
|
@@ -253,6 +253,6 @@ Precisa de ajuda?
|
|
|
253
253
|
- **Engenharia (um guia por comando `eng.*`)** — [`docs/engenharia/`](docs/engenharia/README.md)
|
|
254
254
|
- **Indice de docs/** — [`docs/README.md`](docs/README.md)
|
|
255
255
|
- **Instrucoes para agentes / mapa de skills** — `AGENTS.md`
|
|
256
|
-
- **Issues/Feedback** — skill `jarvis-report-issue`
|
|
256
|
+
- **Issues/Feedback** — skill `jarvis-report-issue` (sem a CLI do host (`gh`), sem login ou sem rede, o issue fica em `.jarvis/issues/issue-{desc}.md` para abrir à mão)
|
|
257
257
|
- **Duvidas sobre comandos** — Pergunte diretamente ao agent:
|
|
258
258
|
`"Como funciona /eng.start?"` | `"Qual a diferenca entre /eng.work e /pr?"` | `"O que e CDD?"`
|
|
@@ -33,7 +33,7 @@ Por isso o texto tem de ser **completo, autossuficiente e sem ambiguidade**.
|
|
|
33
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
34
|
- Os títulos dizem o que a funcionalidade é.
|
|
35
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/`,
|
|
36
|
+
- card pelo `TASK_MANAGER` do `ENV.md`, com a URL no padrão da `eng.integrations-rules.md` (Jira `/browse/`, GitHub `/issues/`, GitLab `/-/issues/`); no modo freelance, o número de controle, sem URL;
|
|
37
37
|
- PR/MR pelo `VERSION_CONTROL` ou pelo `remote origin` (número + URL).
|
|
38
38
|
7. **Nunca inventar.** Sem fonte, pergunte ao agente principal (que pergunta ao usuário) ou registre como "não informado".
|
|
39
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.
|
|
@@ -15,7 +15,7 @@ Antes de qualquer ação, determine qual modo aplicar:
|
|
|
15
15
|
|
|
16
16
|
**Modo A — Estratégia a partir da Tech Spec (pré-código)**
|
|
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
|
-
- Fonte de dados: card no $TASK_MANAGER
|
|
18
|
+
- Fonte de dados: card no $TASK_MANAGER, lido pela CLI do vendor (ação `view` do catálogo `bin/lib/integrations/vendors.json`: `gh issue view`, `glab issue view` ou `acli jira workitem view`) ou pelo conteúdo colado pelo usuário
|
|
19
19
|
- Não executar `git diff`, `git log` ou qualquer comando que dependa de código
|
|
20
20
|
- Saída: `$ENG_DOCS/qa/strategies/{task-id}-test-strategy.md`
|
|
21
21
|
|
|
@@ -125,7 +125,7 @@ Usar quando: invocado via `eng.qa-refinement-entry`, ou quando git diff retornar
|
|
|
125
125
|
|
|
126
126
|
### A.1. Buscar o Card
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
Buscar o card pelo ID recebido como argumento, pela CLI do vendor (ação `view` do catálogo `bin/lib/integrations/vendors.json`: `gh issue view`, `glab issue view` ou `acli jira workitem view`) ou pelo conteúdo colado pelo usuário.
|
|
129
129
|
Extrair: título, descrição, critérios de aceite, análise técnica, dependências e qualquer anexo de spec.
|
|
130
130
|
|
|
131
131
|
Se a integração não estiver disponível, solicitar ao usuário que cole o conteúdo da spec diretamente.
|
package/bin/README.md
CHANGED
|
@@ -16,11 +16,14 @@ bin/
|
|
|
16
16
|
├── cli/
|
|
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
|
-
├──
|
|
20
|
-
│ ├──
|
|
19
|
+
├── integrations/ # Board e git pela CLI oficial do vendor (FRD-006)
|
|
20
|
+
│ ├── vendors.json # Catálogo: vendors (jira/acli, github/gh, gitlab/glab), comandos por ação e variáveis
|
|
21
|
+
│ └── cli-runner.js # checkCli, buildArgs, runAction (execFile sem shell), códigos de saída 0/1/3/4
|
|
22
|
+
├── vcs/ # GitHub/GitLab pela CLI (gh pr create, glab mr create, issue create)
|
|
23
|
+
│ ├── api.js # createIssue, createMergeRequest, cardUrl (sobre o cli-runner)
|
|
21
24
|
│ ├── create-merge.js, create-issue.js # função exportada + CLI direto (ver abaixo)
|
|
22
25
|
├── tasks/
|
|
23
|
-
│ └── comment.js #
|
|
26
|
+
│ └── comment.js # Comentário no card (jira/github/gitlab) pelo cli-runner — mesmo padrão
|
|
24
27
|
├── config/ # ide-config.js (fonte de verdade de IDEs), constants.js
|
|
25
28
|
├── core/ # scanner.js, sync-engine.js, profile-filter.js, token-report.js
|
|
26
29
|
├── docs/ # Central docs: fetch/publish pelo adaptador + validação de frontmatter
|
|
@@ -28,7 +31,7 @@ bin/
|
|
|
28
31
|
│ ├── fetch-file.js # openStorageFromEnv() + fetchFile() — `docs sync`/`docs fetch`/`docs publish`
|
|
29
32
|
│ ├── publish-file.js # tipos, resolveDestPath() e publishFile() — `docs publish`
|
|
30
33
|
│ └── storage/ # adaptadores (CENTRAL_DOCS_STORAGE): index.js (fábrica), filesystem.js (pasta + git), build-index.js
|
|
31
|
-
├── utils/ # logger.js, ui.js, paths.js, frontmatter.js, git-parser.js
|
|
34
|
+
├── utils/ # logger.js, ui.js, paths.js, frontmatter.js, git-parser.js
|
|
32
35
|
├── flow-map/ # `jarvis map` — grafo de chamadas entre workflows/skills/agents
|
|
33
36
|
├── auth/ # session.js (auth.json legado, hoje só `jarvis logout` o lê)
|
|
34
37
|
└── env-loader.js # Resolve e lê o ENV.md ($IDE/ENV.md)
|
|
@@ -116,12 +119,12 @@ if (fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
|
116
119
|
|
|
117
120
|
## Variáveis de ambiente lidas diretamente (fora do ENV.md)
|
|
118
121
|
|
|
119
|
-
`bin/lib/tasks/comment.js` e `bin/lib/vcs/*.js` leem `process.env` direto (`TASK_MANAGER`, `
|
|
122
|
+
`bin/lib/tasks/comment.js` e `bin/lib/vcs/*.js` leem `process.env` direto (`TASK_MANAGER`, `TASK_MANAGER_ACCESS`, `TASK_MANAGER_URL_BASE`, `VERSION_CONTROL`, etc.; nenhum token: o login é o da CLI do vendor) — não passam pelo `env-loader.js`/ENV.md. O `jarvis tasks comment` e o `jarvis vcs create-merge` completam o que faltar no ambiente lendo o `$IDE/ENV.md` do workspace (`readFileEnv`/`resolveFileEnv` em `integrations/cli-runner.js`, com `--ide`/`--env-file`; o ambiente do processo vence; `setting` tira aspas e comentário de fim de linha), então os skills não precisam exportar nada nem usar `eval`. O `accessGate` decide antes de qualquer envio: só `cli` (ou vazio, com aviso) segue; `manual`, valor inválido e ENV.md ilegível saem com 3. O ENV.md lido é repassado ao `createMergeRequest` (`fileEnvResult`), inclusive pelo Central docs. Se um comando novo precisar ler o `ENV.md` diretamente, use `env-loader.js#resolveEnvPath`/`loadEnv` (ver `whoami.js`, `tokens.js`, `qa-signoff.js` como exemplo) em vez de `process.env`.
|
|
120
123
|
|
|
121
124
|
## Testes
|
|
122
125
|
|
|
123
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).
|
|
124
|
-
- `npm run test:filtro` — testes unitários de `profile-filter.js`, `token-report.js`, `flow-map`, `env-loader.js`, `sync-engine.js
|
|
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.
|
|
125
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.
|
|
126
129
|
|
|
127
130
|
### Como testar código que lê `fs`/rede sem mockar módulo
|
|
@@ -130,10 +133,10 @@ Os 3 arquivos mais críticos sem cobertura até pouco tempo atrás (`env-loader.
|
|
|
130
133
|
|
|
131
134
|
- **`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
135
|
- **`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
|
-
- **`vcs/api.js
|
|
136
|
+
- **`integrations/cli-runner.js`, `vcs/api.js`, `tasks/comment.js`**: o `exec` (que roda a CLI do vendor) é injetável pelo último parâmetro (`deps`); o teste passa uma CLI falsa que responde por subcomando (ver `fakeExec` em `cli-runner.test.js`), sem chamar `gh`/`glab`/`acli` de verdade nem depender do login da máquina. No `jarvis init --upgrade` (teste por subprocesso), `JARVIS_SKIP_CLI_CHECK=1` desliga a descoberta, e o teste dela põe uma CLI falsa no `PATH`.
|
|
134
137
|
- **`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
138
|
|
|
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:
|
|
139
|
+
**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: uma função que lê `process.env` antes do primeiro `await`); quebra silenciosamente em qualquer função com mais de um `await` antes de ler a variável.
|
|
137
140
|
|
|
138
141
|
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
142
|
|
|
@@ -108,7 +108,7 @@ export async function docsPublish(flags = {}) {
|
|
|
108
108
|
"\n💡 Central docs com git: confira se a pasta está limpa (git status), se você tem permissão de push"
|
|
109
109
|
);
|
|
110
110
|
logger.error(
|
|
111
|
-
" e se
|
|
111
|
+
" e se a CLI do host está logada (gh auth login, glab auth login)."
|
|
112
112
|
);
|
|
113
113
|
}
|
|
114
114
|
process.exit(1);
|
package/bin/commands/init.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, dirname, basename } from "node:path";
|
|
3
3
|
import { createInterface } from "node:readline";
|
|
4
4
|
import { syncAssets } from "../lib/core/sync-engine.js";
|
|
@@ -22,18 +22,44 @@ import {
|
|
|
22
22
|
removeStaleSkills,
|
|
23
23
|
} from "../lib/core/stale-skills.js";
|
|
24
24
|
import { parseEnvEntries, planEnvUpgrade, applyEnvUpgrade } from "../lib/core/env-upgrade.js";
|
|
25
|
+
import { checkCli, getVendor } from "../lib/integrations/cli-runner.js";
|
|
25
26
|
import {
|
|
26
27
|
expandEnv,
|
|
27
28
|
planDocsMigration,
|
|
28
29
|
applyDocsMigration,
|
|
29
30
|
rewriteRelatedDiscovery,
|
|
30
31
|
declineEnvValues,
|
|
32
|
+
ensureGitignore,
|
|
31
33
|
} from "../lib/core/docs-migrate.js";
|
|
34
|
+
import { rootIsRepo } from "../lib/core/workspace-mode.js";
|
|
35
|
+
import { ensureContext7 } from "../lib/core/mcp-config.js";
|
|
32
36
|
|
|
33
37
|
function ask(rl, question) {
|
|
34
38
|
return new Promise((resolve) => rl.question(question, resolve));
|
|
35
39
|
}
|
|
36
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Grava o Context7 (MCP obrigatório) no arquivo de MCP da IDE e mostra o que falta.
|
|
43
|
+
* Só avisa: problema no arquivo de MCP não muda o código de saída (instalação e --upgrade).
|
|
44
|
+
*/
|
|
45
|
+
function setupMcp(workspaceDir, ide) {
|
|
46
|
+
const mcp = ensureContext7(workspaceDir, ide);
|
|
47
|
+
logger.info(`${BLUE}🔌 MCP (Context7):${NC}`);
|
|
48
|
+
for (const p of mcp.written) logger.info(` ${GREEN}✓ ${p}${NC} (Context7 adicionado)`);
|
|
49
|
+
for (const p of mcp.present) logger.info(`${DIM} ↳ ${p} (Context7 já configurado)${NC}`);
|
|
50
|
+
for (const m of mcp.manual) {
|
|
51
|
+
const why = m.reason
|
|
52
|
+
? `${m.path}: ${m.reason}. Nada alterado; inclua à mão:`
|
|
53
|
+
: `Config só global: inclua em ${m.path}:`;
|
|
54
|
+
logger.info(`${YELLOW} ⚠️ ${why}${NC}`);
|
|
55
|
+
for (const line of m.snippet.trimEnd().split("\n")) logger.info(`${DIM} ${line}${NC}`);
|
|
56
|
+
}
|
|
57
|
+
for (const e of mcp.errors) logger.info(`${YELLOW} ⚠️ ${e} (nada alterado)${NC}`);
|
|
58
|
+
const note = getIDEConfig(ide)?.mcpNote;
|
|
59
|
+
if (note && (mcp.written.length || mcp.manual.length)) logger.info(`${DIM} ${note}${NC}`);
|
|
60
|
+
logger.info("");
|
|
61
|
+
}
|
|
62
|
+
|
|
37
63
|
export async function init(flags) {
|
|
38
64
|
configureFromFlags(flags);
|
|
39
65
|
|
|
@@ -154,20 +180,16 @@ export async function init(flags) {
|
|
|
154
180
|
for (const area of ["eng", "prod", "qa"]) {
|
|
155
181
|
mkdirSync(join(jarvisRoot, "sessions", area), { recursive: true });
|
|
156
182
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const text = readFileSync(gitignore, "utf8");
|
|
161
|
-
if (!text.split("\n").some((l) => l.trim() === ignoreLine || l.trim() === ".jarvis")) {
|
|
162
|
-
appendFileSync(gitignore, `${text.endsWith("\n") ? "" : "\n"}${ignoreLine}\n`);
|
|
163
|
-
}
|
|
164
|
-
} else {
|
|
165
|
-
writeFileSync(gitignore, `${ignoreLine}\n`);
|
|
183
|
+
// .gitignore só numa raiz versionada (com .git): no modo workspace a raiz não é um repositório.
|
|
184
|
+
if (rootIsRepo(cwd)) {
|
|
185
|
+
ensureGitignore(cwd, ".jarvis/");
|
|
166
186
|
}
|
|
167
187
|
|
|
188
|
+
logger.info("");
|
|
189
|
+
setupMcp(cwd, ide);
|
|
190
|
+
|
|
168
191
|
const pkg = getPackageInfo();
|
|
169
192
|
|
|
170
|
-
logger.info("");
|
|
171
193
|
logger.info(`${GREEN}✓ Configuração concluída!${NC}`);
|
|
172
194
|
logger.info(`${BOLD}${CYAN} versão: ${pkg.name} v${pkg.version}${NC}`);
|
|
173
195
|
logger.info(`${DIM} origem: ${pkg.root}${NC}`);
|
|
@@ -245,6 +267,31 @@ function readLockVersion(workspaceDir) {
|
|
|
245
267
|
}
|
|
246
268
|
}
|
|
247
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Descoberta da CLI de cada eixo (só leitura: binário no PATH + `auth status`).
|
|
272
|
+
* Não pergunta nada: a confirmação do acesso e as variáveis ficam no `/jarvis-init` (Q2 da #181).
|
|
273
|
+
* `JARVIS_SKIP_CLI_CHECK=1` pula a descoberta (CI, sem rede).
|
|
274
|
+
*/
|
|
275
|
+
async function reportCliDiscovery(entries) {
|
|
276
|
+
const axes = [
|
|
277
|
+
["TASK_MANAGER", "Board de cards"],
|
|
278
|
+
["VERSION_CONTROL", "Controle de versão"],
|
|
279
|
+
];
|
|
280
|
+
const targets = axes
|
|
281
|
+
.map(([key, label]) => ({ key, label, vendor: (entries.get(key) || "").toLowerCase() }))
|
|
282
|
+
.filter((t) => getVendor(t.vendor));
|
|
283
|
+
if (!targets.length) return;
|
|
284
|
+
if (process.env.JARVIS_SKIP_CLI_CHECK) {
|
|
285
|
+
logger.info(`${DIM} • Descoberta da CLI pulada (JARVIS_SKIP_CLI_CHECK)${NC}`);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
for (const t of targets) {
|
|
289
|
+
const r = await checkCli(t.vendor);
|
|
290
|
+
const mark = r.installed && r.loggedIn ? GREEN : YELLOW;
|
|
291
|
+
logger.info(`${mark} • ${t.label} (${t.key}=${t.vendor}): ${r.reason}${NC}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
248
295
|
/**
|
|
249
296
|
* `jarvis init --upgrade`: atualiza um projeto já configurado em uma passada.
|
|
250
297
|
* 1. arquivos do framework (como o --force, sem perguntar); 2. só as chaves novas do ENV;
|
|
@@ -281,6 +328,7 @@ async function runUpgrade(cwd, flags) {
|
|
|
281
328
|
keepEnvValues: true,
|
|
282
329
|
});
|
|
283
330
|
for (const item of sync.errors) logger.error(`${RED} ✗ ${item}${NC}`);
|
|
331
|
+
setupMcp(workspaceDir, ide);
|
|
284
332
|
|
|
285
333
|
// 2. Variáveis novas (só acrescenta)
|
|
286
334
|
const envText = readFileSync(envPath, "utf-8");
|
|
@@ -390,7 +438,32 @@ async function runUpgrade(cwd, flags) {
|
|
|
390
438
|
`${YELLOW} ⚠️ WORKSPACE_MODE: a raiz é um repositório e há subpastas com .git (submódulos?): gravado como project; confira no ENV.md${NC}`
|
|
391
439
|
);
|
|
392
440
|
}
|
|
441
|
+
for (const key of envPlan.filledApplied || []) {
|
|
442
|
+
logger.info(` • ${key} estava vazio: preenchido com o repositório oficial do Jarvis`);
|
|
443
|
+
}
|
|
444
|
+
if (envPlan.warnings.includes("TOKEN_TASK_MANAGER")) {
|
|
445
|
+
logger.info(
|
|
446
|
+
`${YELLOW} ⚠️ TOKEN_TASK_MANAGER não é mais usado: o Jarvis usa o login da CLI do vendor. Faça o login da CLI e remova a linha pelo /jarvis-init upgrade (o valor não foi lido nem copiado)${NC}`
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
for (const w of envPlan.warnings.filter((x) => x.startsWith("UNSUPPORTED:"))) {
|
|
450
|
+
const [, key, vendor] = w.split(":");
|
|
451
|
+
logger.info(
|
|
452
|
+
`${YELLOW} ⚠️ ${key}=${vendor} não é suportado (jira, github, gitlab): o eixo fica em freelance e as ações viram .md${NC}`
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
await reportCliDiscovery(futureEntries);
|
|
456
|
+
|
|
457
|
+
// Eixos com vendor do catálogo e sem forma de acesso definida: a confirmação é no /jarvis-init.
|
|
458
|
+
const pendingAxes = ["TASK_MANAGER", "VERSION_CONTROL"].filter((key) => {
|
|
459
|
+
const access = (futureEntries.get(`${key}_ACCESS`) || "").trim();
|
|
460
|
+
return getVendor(futureEntries.get(key)) && (!access || /^\[.*\]$/.test(access));
|
|
461
|
+
});
|
|
393
462
|
logger.info("");
|
|
394
|
-
logger.info(
|
|
463
|
+
logger.info(
|
|
464
|
+
pendingAxes.length
|
|
465
|
+
? `${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`
|
|
466
|
+
: `${CYAN}➡️ Agora rode ${BOLD}/jarvis-init upgrade${NC}${CYAN} na IDE.${NC}\n`
|
|
467
|
+
);
|
|
395
468
|
return sync.errors.length || moveErrors.length || staleErrors.length ? 1 : 0;
|
|
396
469
|
}
|
package/bin/lib/cli/program.js
CHANGED
|
@@ -196,15 +196,17 @@ export function buildProgram() {
|
|
|
196
196
|
.option("--env-file <path>")
|
|
197
197
|
.action((options) => dailyReport(toLegacyFlags(options)));
|
|
198
198
|
|
|
199
|
-
const vcs = cmd(program, "vcs").description("
|
|
199
|
+
const vcs = cmd(program, "vcs").description("Git host pela CLI oficial (gh, glab)");
|
|
200
200
|
|
|
201
201
|
cmd(vcs, "create-merge")
|
|
202
|
-
.description("Abrir um MR/PR")
|
|
202
|
+
.description("Abrir um MR/PR (gh pr create / glab mr create)")
|
|
203
203
|
.option("--source <branch>")
|
|
204
204
|
.option("--target <branch>")
|
|
205
205
|
.option("--title <titulo>")
|
|
206
206
|
.option("--body-file <path>")
|
|
207
207
|
.option("--repo <url>", "Padrão: git remote get-url origin")
|
|
208
|
+
.option("--ide <ide>", "IDE do ENV.md (quando há mais de um no workspace)")
|
|
209
|
+
.option("--env-file <path>", "Path explícito do ENV.md")
|
|
208
210
|
.action((options) =>
|
|
209
211
|
runCreateMerge({
|
|
210
212
|
source: options.source,
|
|
@@ -212,12 +214,14 @@ export function buildProgram() {
|
|
|
212
214
|
title: options.title,
|
|
213
215
|
bodyFile: options.bodyFile,
|
|
214
216
|
repo: options.repo,
|
|
217
|
+
ide: options.ide,
|
|
218
|
+
envFile: options.envFile,
|
|
215
219
|
})
|
|
216
220
|
);
|
|
217
221
|
|
|
218
222
|
cmd(vcs, "create-issue")
|
|
219
|
-
.description("Abrir uma issue")
|
|
220
|
-
.option("--repo <url
|
|
223
|
+
.description("Abrir uma issue (gh issue create / glab issue create)")
|
|
224
|
+
.option("--repo <url>", "URL completa do repositório")
|
|
221
225
|
.option("--title <titulo>")
|
|
222
226
|
.option("--body-file <path>")
|
|
223
227
|
.option("--label <labels>", "Labels separadas por vírgula (padrão: bug)")
|
|
@@ -230,13 +234,24 @@ export function buildProgram() {
|
|
|
230
234
|
})
|
|
231
235
|
);
|
|
232
236
|
|
|
233
|
-
const tasks = cmd(program, "tasks").description(
|
|
237
|
+
const tasks = cmd(program, "tasks").description(
|
|
238
|
+
"Board de cards pela CLI oficial (gh, glab, acli)"
|
|
239
|
+
);
|
|
234
240
|
|
|
235
241
|
cmd(tasks, "comment")
|
|
236
|
-
.description("Comentar num card (Jira
|
|
242
|
+
.description("Comentar num card (Jira, GitHub, GitLab — lê TASK_MANAGER do ambiente)")
|
|
237
243
|
.argument("[card-key]")
|
|
238
244
|
.argument("[message...]", "Texto do comentário (pode ter espaços)")
|
|
239
|
-
.
|
|
245
|
+
.option("--body-file <path>", "Lê o texto do comentário de um arquivo")
|
|
246
|
+
.option("--ide <ide>", "IDE do ENV.md (quando há mais de um no workspace)")
|
|
247
|
+
.option("--env-file <path>", "Path explícito do ENV.md")
|
|
248
|
+
.action((cardKey, messageParts, options) =>
|
|
249
|
+
runComment(cardKey, messageParts.join(" "), {
|
|
250
|
+
bodyFile: options.bodyFile,
|
|
251
|
+
ide: options.ide,
|
|
252
|
+
envFile: options.envFile,
|
|
253
|
+
})
|
|
254
|
+
);
|
|
240
255
|
|
|
241
256
|
return program;
|
|
242
257
|
}
|
|
@@ -22,6 +22,18 @@ export const STEERING_FOLDER = "steering";
|
|
|
22
22
|
* @property {string} workflowsFolder - Nome da pasta para workflows/commands
|
|
23
23
|
* @property {string} [folderName] - Nome real da pasta no filesystem (sem o ponto). Defaults para value.
|
|
24
24
|
* @property {string} [icon] - Emoji/ícone da IDE (opcional)
|
|
25
|
+
* @property {ReadonlyArray<MCPTarget>} mcp - Onde a IDE lê a configuração de MCP
|
|
26
|
+
* @property {string} mcpNote - O que falta para o servidor ficar ativo depois de gravado
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Arquivo de MCP que a IDE lê. Fonte: documentação oficial de cada IDE (conferida em 2026-10).
|
|
31
|
+
* @typedef {Object} MCPTarget
|
|
32
|
+
* @property {string} path - Relativo ao workspace; com `~/`, é da pasta do usuário (global)
|
|
33
|
+
* @property {"json"|"toml"} format
|
|
34
|
+
* @property {"mcpServers"|"mcp"|"mcp_servers"} key - Chave raiz dos servidores
|
|
35
|
+
* @property {"stdio"|"opencode"|"plain"} style - Formato do item do servidor
|
|
36
|
+
* @property {boolean} [global] - Só existe config global: o init não grava, só orienta
|
|
25
37
|
*/
|
|
26
38
|
|
|
27
39
|
/**
|
|
@@ -35,6 +47,24 @@ export const IDES = Object.freeze([
|
|
|
35
47
|
supported: true,
|
|
36
48
|
workflowsFolder: DEFAULT_WORKFLOWS_FOLDER,
|
|
37
49
|
icon: "🤖",
|
|
50
|
+
mcp: [
|
|
51
|
+
{
|
|
52
|
+
path: "~/.config/devin/mcp_config.json",
|
|
53
|
+
format: "json",
|
|
54
|
+
key: "mcpServers",
|
|
55
|
+
style: "plain",
|
|
56
|
+
global: true,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
path: "~/.codeium/windsurf/mcp_config.json",
|
|
60
|
+
format: "json",
|
|
61
|
+
key: "mcpServers",
|
|
62
|
+
style: "plain",
|
|
63
|
+
global: true,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
mcpNote:
|
|
67
|
+
"A Windsurf só tem config global de MCP: inclua o servidor no arquivo indicado e ative-o no painel MCP do Cascade.",
|
|
38
68
|
},
|
|
39
69
|
{
|
|
40
70
|
name: "Claude Code",
|
|
@@ -42,6 +72,8 @@ export const IDES = Object.freeze([
|
|
|
42
72
|
supported: true,
|
|
43
73
|
workflowsFolder: COMMANDS_FOLDER,
|
|
44
74
|
icon: "🔌",
|
|
75
|
+
mcp: [{ path: ".mcp.json", format: "json", key: "mcpServers", style: "stdio" }],
|
|
76
|
+
mcpNote: "Reinicie o Claude Code e aprove o servidor do .mcp.json quando ele perguntar.",
|
|
45
77
|
},
|
|
46
78
|
{
|
|
47
79
|
name: "Cursor",
|
|
@@ -49,6 +81,8 @@ export const IDES = Object.freeze([
|
|
|
49
81
|
supported: true,
|
|
50
82
|
workflowsFolder: COMMANDS_FOLDER,
|
|
51
83
|
icon: "💻",
|
|
84
|
+
mcp: [{ path: ".cursor/mcp.json", format: "json", key: "mcpServers", style: "plain" }],
|
|
85
|
+
mcpNote: "Recarregue o Cursor; o servidor aparece em Settings → MCP.",
|
|
52
86
|
},
|
|
53
87
|
{
|
|
54
88
|
name: "Codex (OpenAI)",
|
|
@@ -56,6 +90,9 @@ export const IDES = Object.freeze([
|
|
|
56
90
|
supported: true,
|
|
57
91
|
workflowsFolder: DEFAULT_WORKFLOWS_FOLDER,
|
|
58
92
|
icon: "🔮",
|
|
93
|
+
mcp: [{ path: ".codex/config.toml", format: "toml", key: "mcp_servers", style: "plain" }],
|
|
94
|
+
mcpNote:
|
|
95
|
+
"O Codex só lê o .codex/config.toml de projetos confiáveis (trusted): confie no projeto e reinicie o Codex.",
|
|
59
96
|
},
|
|
60
97
|
{
|
|
61
98
|
name: "OpenCode",
|
|
@@ -63,6 +100,8 @@ export const IDES = Object.freeze([
|
|
|
63
100
|
supported: true,
|
|
64
101
|
workflowsFolder: COMMANDS_FOLDER,
|
|
65
102
|
icon: "⚡",
|
|
103
|
+
mcp: [{ path: "opencode.json", format: "json", key: "mcp", style: "opencode" }],
|
|
104
|
+
mcpNote: "Reinicie o OpenCode para carregar o servidor do opencode.json.",
|
|
66
105
|
},
|
|
67
106
|
{
|
|
68
107
|
name: "Gemini CLI / Antigravity (Google)",
|
|
@@ -71,6 +110,12 @@ export const IDES = Object.freeze([
|
|
|
71
110
|
supported: true,
|
|
72
111
|
workflowsFolder: DEFAULT_WORKFLOWS_FOLDER,
|
|
73
112
|
icon: "🌐",
|
|
113
|
+
mcp: [
|
|
114
|
+
{ path: ".gemini/settings.json", format: "json", key: "mcpServers", style: "plain" },
|
|
115
|
+
{ path: ".agents/mcp_config.json", format: "json", key: "mcpServers", style: "plain" },
|
|
116
|
+
],
|
|
117
|
+
mcpNote:
|
|
118
|
+
"Gemini CLI: confie na pasta (gemini trust) e reinicie. Antigravity: use Refresh em Customizations (ou /mcp no CLI).",
|
|
74
119
|
},
|
|
75
120
|
{
|
|
76
121
|
name: "Kiro (AWS)",
|
|
@@ -78,6 +123,8 @@ export const IDES = Object.freeze([
|
|
|
78
123
|
supported: true,
|
|
79
124
|
workflowsFolder: STEERING_FOLDER,
|
|
80
125
|
icon: "☁️",
|
|
126
|
+
mcp: [{ path: ".kiro/settings/mcp.json", format: "json", key: "mcpServers", style: "plain" }],
|
|
127
|
+
mcpNote: "O Kiro reconecta os servidores ao salvar o arquivo; confira no painel MCP.",
|
|
81
128
|
},
|
|
82
129
|
]);
|
|
83
130
|
|
|
@@ -220,6 +267,15 @@ export function getWorkflowsFolder(ideValue) {
|
|
|
220
267
|
return config?.workflowsFolder || DEFAULT_WORKFLOWS_FOLDER;
|
|
221
268
|
}
|
|
222
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Arquivos de MCP da IDE (lista vazia para IDE desconhecida)
|
|
272
|
+
* @param {string} ideValue - Slug da IDE
|
|
273
|
+
* @returns {ReadonlyArray<MCPTarget>}
|
|
274
|
+
*/
|
|
275
|
+
export function getMCPTargets(ideValue) {
|
|
276
|
+
return getIDEConfig(ideValue)?.mcp || [];
|
|
277
|
+
}
|
|
278
|
+
|
|
223
279
|
/**
|
|
224
280
|
* Limpa o cache (útil para testes)
|
|
225
281
|
* @private
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from "node:fs";
|
|
16
16
|
import { join, resolve, relative, dirname, basename, isAbsolute, sep } from "node:path";
|
|
17
17
|
import { execFileSync } from "node:child_process";
|
|
18
|
+
import { rootIsRepo } from "./workspace-mode.js";
|
|
18
19
|
|
|
19
20
|
const ENG_SUBDIRS = ["ARD", "RFC", "qa", "swagger", "openapi", "robots", "c4"];
|
|
20
21
|
const LEGACY_ENG_SUBDIRS = ["robots", "swagger", "openapi", "c4"];
|
|
@@ -164,7 +165,8 @@ function emptyDirsUnder(dirAbs) {
|
|
|
164
165
|
|
|
165
166
|
/**
|
|
166
167
|
* Aplica o plano: `git mv` se o arquivo é versionado, senão `rename`. Remove as pastas
|
|
167
|
-
* antigas só se ficarem vazias e garante `{JARVIS_DOCS}/` no
|
|
168
|
+
* antigas só se ficarem vazias e, se a raiz for um repositório, garante `{JARVIS_DOCS}/` no
|
|
169
|
+
* `.gitignore` (raiz não versionada, como no modo workspace, não tem o `.gitignore` tocado).
|
|
168
170
|
* @returns {{ moved: {from: string, to: string, discovery: boolean}[], errors: string[] }}
|
|
169
171
|
*/
|
|
170
172
|
export function applyDocsMigration(workspaceDir, plan, env) {
|
|
@@ -194,16 +196,26 @@ export function applyDocsMigration(workspaceDir, plan, env) {
|
|
|
194
196
|
removeEmptyDirs(dirname(srcAbs), docsAbs);
|
|
195
197
|
}
|
|
196
198
|
|
|
197
|
-
if (moved.length > 0
|
|
199
|
+
if (moved.length > 0 && rootIsRepo(workspaceDir)) {
|
|
200
|
+
ensureGitignore(workspaceDir, `${docsFolders(env).jarvis}/`);
|
|
201
|
+
}
|
|
198
202
|
return { moved, errors };
|
|
199
203
|
}
|
|
200
204
|
|
|
201
|
-
|
|
205
|
+
/**
|
|
206
|
+
* Garante `line` no `.gitignore` de `workspaceDir` (cria o arquivo se faltar). Reconhece a
|
|
207
|
+
* linha com e sem a barra final, com a barra inicial e com `/*` ou `/**` (`docs/jarvis/`,
|
|
208
|
+
* `docs/jarvis`, `/docs/jarvis/`, `docs/jarvis/**`). Acrescenta com a quebra de linha do arquivo.
|
|
209
|
+
* Quem chama decide se a raiz é versionada (`rootIsRepo`).
|
|
210
|
+
*/
|
|
211
|
+
export function ensureGitignore(workspaceDir, line) {
|
|
202
212
|
const p = join(workspaceDir, ".gitignore");
|
|
203
213
|
const text = existsSync(p) ? readFileSync(p, "utf-8") : "";
|
|
204
214
|
const bare = line.replace(/\/$/, "");
|
|
205
|
-
|
|
206
|
-
|
|
215
|
+
const forms = [bare, `${bare}/`, `${bare}/*`, `${bare}/**`].flatMap((f) => [f, `/${f}`]);
|
|
216
|
+
if (text.split(/\r?\n/).some((l) => forms.includes(l.trim()))) return;
|
|
217
|
+
const eol = text.includes("\r\n") ? "\r\n" : "\n";
|
|
218
|
+
appendFileSync(p, `${text && !text.endsWith("\n") ? eol : ""}${line}${eol}`);
|
|
207
219
|
}
|
|
208
220
|
|
|
209
221
|
/**
|