jarvis-ai-framework 7.0.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/AGENTS.md +2 -2
- package/README.md +11 -34
- package/agents/engineering/data/eng.data-engineer.agent.md +3 -3
- package/agents/engineering/eng.agent.md +13 -11
- package/agents/engineering/eng.dev-code-reviewer.md +19 -4
- package/agents/engineering/eng.docs-consolidator.agent.md +2 -2
- package/agents/engineering/eng.docs-writer.md +17 -2
- package/agents/engineering/qa/eng.qa.quality-champion-task-agent.md +2 -2
- package/agents/engineering/qa/eng.qa.test-planner.md +20 -5
- package/agents/product/prod.pm-checker.md +16 -1
- package/bin/README.md +4 -4
- package/bin/commands/init.js +237 -29
- package/bin/lib/cli/program.js +7 -11
- package/bin/lib/core/sync-engine.js +8 -4
- package/bin/lib/utils/frontmatter.js +1 -1
- package/bin/lib/utils/paths.js +2 -1
- package/bin/lib/utils/version.js +65 -0
- package/bin/postinstall.js +7 -31
- package/package.json +2 -2
- package/rules/AGENTS.md +1 -1
- package/rules/engineering/data/data-rules.md +2 -2
- package/rules/engineering/eng-rules.md +1 -1
- package/rules/engineering/eng-security-rules.md +1 -1
- package/rules/engineering/eng.integrations-rules.md +6 -2
- package/rules/rtk-rules.md +1 -1
- package/rules-on-demand/doc-links-rules.md +2 -2
- package/rules-on-demand/engineering/eng.breakdown-subtasks-rules.md +1 -1
- package/rules-on-demand/engineering/eng.plan-rules.md +2 -0
- package/rules-on-demand/engineering/eng.pr-rules.md +8 -6
- package/rules-on-demand/engineering/eng.pre-pr-rules.md +10 -3
- package/rules-on-demand/engineering/eng.start-rules.md +7 -5
- package/rules-on-demand/engineering/eng.tech-spec-rules.md +9 -7
- package/rules-on-demand/engineering/eng.transitions-rules.md +7 -6
- package/skills/AGENTS.md +2 -2
- package/skills/SKILLS-ROADMAP.md +3 -3
- package/skills/eng-backend-arch-c4/assets/example-container.md +4 -4
- package/skills/eng-backend-arch-c4/assets/example-context.md +6 -6
- package/skills/eng-backend-microservices-trace/SKILL.md +1 -1
- package/skills/eng-global-investigation/SKILL.md +4 -0
- package/skills/eng-global-pr/SKILL.md +15 -12
- package/skills/eng-global-task-comment/SKILL.md +11 -3
- package/skills/eng-global-tech-spec/references/delivery-spec.md +28 -21
- package/skills/jarvis-docs-central/README.md +4 -4
- package/skills/jarvis-docs-central/SKILL.md +5 -3
- package/skills/jarvis-evolution-architect/references/exemplos.md +3 -3
- package/skills/jarvis-init/SKILL.md +89 -24
- package/skills/jarvis-init/assets/setup-guide.md +1 -1
- package/skills/jarvis-report-issue/SKILL.md +37 -24
- package/skills/product-roadmap-report/SKILL.md +2 -2
- package/skills/product-roadmap-report/commands/status.memory.md +1 -1
- package/skills/product-roadmap-report/commands/status.roadmap.preview.md +2 -2
- package/skills/product-specs/SKILL.md +1 -1
- package/skills/product-specs-update/SKILL.md +138 -121
- package/templates/ENV-template.md +57 -27
- package/templates/cdd-aplicado-a-prompts.md +1 -1
- package/templates/engineering/AGENTS-template.md +2 -2
- package/templates/engineering/CONTACTS-template.md +1 -1
- package/templates/engineering/advisor-template.md +2 -2
- package/templates/engineering/architecture-template.md +14 -1
- package/templates/engineering/data-contract-template.md +1 -1
- package/templates/engineering/plan-template.md +2 -0
- package/templates/engineering/tech-spec-delivery-template.md +8 -8
- package/templates/engineering/tech-spec-template.md +3 -3
- package/templates/product/prod-frd-template.md +1 -1
- package/templates/product/prod-prd-template.md +1 -1
- package/workflows/AGENTS.md +1 -1
- package/workflows/README.md +1 -1
- package/workflows/engineering/data/data.contract.md +12 -8
- package/workflows/engineering/eng.breakdown-subtasks.md +2 -2
- package/workflows/engineering/eng.build-tech-spec.md +1 -1
- package/workflows/engineering/eng.create-ard-from-code.md +3 -5
- package/workflows/engineering/eng.create-ard.md +3 -5
- package/workflows/engineering/eng.create-rfc.md +5 -5
- package/workflows/engineering/eng.light-arch.md +1 -1
- package/workflows/engineering/eng.plan.md +10 -7
- package/workflows/engineering/eng.pr.md +26 -19
- package/workflows/engineering/eng.pre-pr.md +126 -27
- package/workflows/engineering/eng.spec-consolidate.md +5 -5
- package/workflows/engineering/eng.start.md +23 -9
- package/workflows/engineering/eng.work.md +4 -4
- package/workflows/engineering/qa/eng.qa-release-signoff.md +1 -66
- package/workflows/product/prod.roadmap.preview.md +10 -14
- package/workflows/product/prod.spec.ard.md +2 -2
- package/workflows/product/prod.spec.breakdown.md +2 -2
- package/workflows/product/prod.spec.epic.md +4 -4
- package/workflows/product/prod.spec.frd.md +3 -3
- package/workflows/product/prod.spec.issue.md +3 -3
- package/workflows/product/prod.spec.prd.md +5 -5
- package/bin/commands/qa-signoff.js +0 -115
package/bin/commands/init.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
|
-
import { join, dirname, basename } from "node:path";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync } from "node:fs";
|
|
2
|
+
import { join, dirname, basename, resolve, sep } from "node:path";
|
|
3
3
|
import { createInterface } from "node:readline";
|
|
4
4
|
import { syncAssets } from "../lib/core/sync-engine.js";
|
|
5
5
|
import { discoverAll } from "../lib/core/scanner.js";
|
|
@@ -16,6 +16,7 @@ import { getPackageInfo, getFrameworkRoot } from "../lib/utils/paths.js";
|
|
|
16
16
|
import { logger, configureFromFlags } from "../lib/utils/logger.js";
|
|
17
17
|
import { resolveEnvPath } from "../lib/env-loader.js";
|
|
18
18
|
import { LOCK_FILE } from "../lib/config/constants.js";
|
|
19
|
+
import { compareVersions, localInstall } from "../lib/utils/version.js";
|
|
19
20
|
import {
|
|
20
21
|
findStaleSkills,
|
|
21
22
|
registeredSpecializations,
|
|
@@ -38,6 +39,115 @@ function ask(rl, question) {
|
|
|
38
39
|
return new Promise((resolve) => rl.question(question, resolve));
|
|
39
40
|
}
|
|
40
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
|
+
|
|
41
151
|
/**
|
|
42
152
|
* Grava o Context7 (MCP obrigatório) no arquivo de MCP da IDE e mostra o que falta.
|
|
43
153
|
* Só avisa: problema no arquivo de MCP não muda o código de saída (instalação e --upgrade).
|
|
@@ -72,6 +182,21 @@ export async function init(flags) {
|
|
|
72
182
|
return;
|
|
73
183
|
}
|
|
74
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
|
+
|
|
75
200
|
let ide;
|
|
76
201
|
if (flags.ide) {
|
|
77
202
|
if (!isIDESupported(flags.ide)) {
|
|
@@ -99,14 +224,15 @@ export async function init(flags) {
|
|
|
99
224
|
});
|
|
100
225
|
logger.info("");
|
|
101
226
|
|
|
102
|
-
const rl = createInterface({
|
|
103
|
-
input: process.stdin,
|
|
104
|
-
output: process.stdout,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
227
|
let choice;
|
|
108
228
|
while (true) {
|
|
109
|
-
choice = await
|
|
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
|
+
}
|
|
110
236
|
const choiceNum = parseInt(choice, 10);
|
|
111
237
|
|
|
112
238
|
if (choiceNum >= 1 && choiceNum <= IDES.length) {
|
|
@@ -127,36 +253,41 @@ export async function init(flags) {
|
|
|
127
253
|
);
|
|
128
254
|
}
|
|
129
255
|
}
|
|
130
|
-
|
|
131
|
-
rl.close();
|
|
132
256
|
}
|
|
133
257
|
|
|
134
258
|
const ideDir = join(cwd, `.${getIDEFolder(ide)}`);
|
|
135
259
|
|
|
136
|
-
//
|
|
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);
|
|
264
|
+
|
|
265
|
+
// Reinstalação: --force ou a opção 2 do menu sobrescrevem os assets (cpSync force:true).
|
|
137
266
|
let forceSync = !!flags.force;
|
|
138
267
|
|
|
139
268
|
if (existsSync(ideDir) && !forceSync) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
269
|
+
const choice = await askInstalledMenu({
|
|
270
|
+
folder: getIDEFolder(ide),
|
|
271
|
+
lockVersion,
|
|
272
|
+
packageVersion: versionCheck.running,
|
|
273
|
+
hasEnv: existsSync(join(ideDir, "ENV.md")),
|
|
274
|
+
askLine,
|
|
146
275
|
});
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (
|
|
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") {
|
|
154
284
|
logger.info(`${BLUE}ℹ️ Instalação cancelada.${NC}\n`);
|
|
155
285
|
return;
|
|
156
286
|
}
|
|
157
287
|
forceSync = true;
|
|
158
288
|
logger.info("");
|
|
159
289
|
}
|
|
290
|
+
closeReader();
|
|
160
291
|
|
|
161
292
|
const ideConfig = getIDEConfig(ide);
|
|
162
293
|
logger.info(`${BLUE}Configurando ambiente para ${ideConfig?.name}...${NC}`);
|
|
@@ -164,13 +295,15 @@ export async function init(flags) {
|
|
|
164
295
|
const result = syncAssets(cwd, ide, {
|
|
165
296
|
force: forceSync,
|
|
166
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,
|
|
167
300
|
});
|
|
168
301
|
|
|
169
302
|
for (const item of result.copied) {
|
|
170
303
|
logger.info(`${BLUE} ↳ ${item}${NC}`);
|
|
171
304
|
}
|
|
172
305
|
for (const item of result.skipped) {
|
|
173
|
-
logger.info(`${DIM} ↳ ${item}
|
|
306
|
+
logger.info(`${DIM} ↳ ${item}${NC}`); // o motivo já vem no item (sync-engine)
|
|
174
307
|
}
|
|
175
308
|
for (const item of result.errors) {
|
|
176
309
|
logger.error(`${RED} ✗ ${item}${NC}`);
|
|
@@ -257,6 +390,53 @@ function ideFromEnvPath(envPath) {
|
|
|
257
390
|
return getIDEValues().find((v) => `.${getIDEFolder(v)}` === folder);
|
|
258
391
|
}
|
|
259
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Avisos de versão antes do sync. O Jarvis é instalado só global:
|
|
395
|
+
* - cópia local no projeto (`node_modules` ou dependência no `package.json`) é ignorada;
|
|
396
|
+
* se é ela que está rodando (`npx jarvis`, `./node_modules/.bin/jarvis`), o aviso diz isso;
|
|
397
|
+
* - lock do projeto mais novo que o Jarvis em uso: o sync rebaixaria os assets.
|
|
398
|
+
* Recebe o lock lido antes do `syncAssets`, que o reescreve.
|
|
399
|
+
* @param {string} workspaceDir
|
|
400
|
+
* @param {string} [lock] - versão do `jarvis-lock.json` do projeto
|
|
401
|
+
* @returns {{ downgrade: boolean, running: string }} `downgrade` = lock mais novo que o Jarvis em uso
|
|
402
|
+
*/
|
|
403
|
+
export function warnVersionMismatch(workspaceDir, lock = readLockVersion(workspaceDir)) {
|
|
404
|
+
const running = getPackageInfo().version;
|
|
405
|
+
// Caminhos reais dos dois lados: o Node já resolve o link do script (`import.meta.url`),
|
|
406
|
+
// então uma cópia por `npm link` em node_modules só bate com o realpath.
|
|
407
|
+
const real = (p) => {
|
|
408
|
+
try {
|
|
409
|
+
return realpathSync(p);
|
|
410
|
+
} catch {
|
|
411
|
+
return resolve(p);
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
const root = real(getFrameworkRoot());
|
|
415
|
+
const localDir = join(workspaceDir, "node_modules", "jarvis-ai-framework");
|
|
416
|
+
const runningLocal =
|
|
417
|
+
(root + sep).startsWith(real(join(workspaceDir, "node_modules")) + sep) ||
|
|
418
|
+
(existsSync(localDir) && root === real(localDir));
|
|
419
|
+
const local = localInstall(workspaceDir);
|
|
420
|
+
if (runningLocal) {
|
|
421
|
+
logger.info(
|
|
422
|
+
`${BLUE}ℹ️ O Jarvis em uso (${running}) é o mesmo de node_modules (cópia local do projeto). O Jarvis é instalado global: npm install -g jarvis-ai-framework, rode \`jarvis\` (sem npx) e remova a cópia local.${NC}`
|
|
423
|
+
);
|
|
424
|
+
} else if (local) {
|
|
425
|
+
logger.info(
|
|
426
|
+
`${BLUE}ℹ️ O Jarvis é instalado global (npm install -g jarvis-ai-framework). A cópia local deste projeto (${local.version} em ${local.source}) é ignorada: pode removê-la.${NC}`
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
const downgrade = compareVersions(lock, running) === 1;
|
|
430
|
+
if (downgrade) {
|
|
431
|
+
const who = runningLocal ? "o Jarvis em uso (cópia local)" : "o seu jarvis global";
|
|
432
|
+
logger.info(
|
|
433
|
+
`${YELLOW}⚠️ Os assets deste projeto são da ${lock}, mas ${who} é a ${running}. Atualize o global: npm install -g jarvis-ai-framework${NC}`
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
if (runningLocal || local || downgrade) logger.info("");
|
|
437
|
+
return { downgrade, running };
|
|
438
|
+
}
|
|
439
|
+
|
|
260
440
|
function readLockVersion(workspaceDir) {
|
|
261
441
|
try {
|
|
262
442
|
return (
|
|
@@ -319,6 +499,29 @@ async function runUpgrade(cwd, flags) {
|
|
|
319
499
|
const workspaceDir = resolved.root;
|
|
320
500
|
const envPath = resolved.path;
|
|
321
501
|
const previousVersion = readLockVersion(workspaceDir);
|
|
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);
|
|
505
|
+
if (downgrade && !flags.yes) {
|
|
506
|
+
let go = false;
|
|
507
|
+
if (process.stdin.isTTY) {
|
|
508
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
509
|
+
const answer = await ask(
|
|
510
|
+
rl,
|
|
511
|
+
`Seguir e rebaixar os assets de ${previousVersion} para ${running}? (s/N): `
|
|
512
|
+
);
|
|
513
|
+
rl.close();
|
|
514
|
+
go = answer.trim().toLowerCase() === "s";
|
|
515
|
+
} else {
|
|
516
|
+
logger.info(`${DIM} Sem terminal interativo e sem --yes: nada será rebaixado.${NC}`);
|
|
517
|
+
}
|
|
518
|
+
if (!go) {
|
|
519
|
+
logger.info(
|
|
520
|
+
`${YELLOW}Upgrade interrompido: atualize o global (npm install -g jarvis-ai-framework) e rode de novo.${NC}\n`
|
|
521
|
+
);
|
|
522
|
+
return 1;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
322
525
|
|
|
323
526
|
// 1. Arquivos do framework (o ENV.md não está entre os sincronizados)
|
|
324
527
|
logger.info(`${BLUE}⬆️ Upgrade do Jarvis em .${getIDEFolder(ide)}/${NC}\n`);
|
|
@@ -407,7 +610,11 @@ async function runUpgrade(cwd, flags) {
|
|
|
407
610
|
// 4. Resumo (só nomes de chave e caminhos)
|
|
408
611
|
const pkg = getPackageInfo();
|
|
409
612
|
logger.info("");
|
|
410
|
-
logger.info(
|
|
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
|
+
);
|
|
411
618
|
logger.info(` • Arquivos do framework: ${sync.copied.length} item(ns) sincronizado(s)`);
|
|
412
619
|
logger.info(
|
|
413
620
|
` • ENV.md: ${addedKeys.length ? `+ ${addedKeys.join(", ")}` : "nenhuma variável nova"}`
|
|
@@ -459,11 +666,12 @@ async function runUpgrade(cwd, flags) {
|
|
|
459
666
|
const access = (futureEntries.get(`${key}_ACCESS`) || "").trim();
|
|
460
667
|
return getVendor(futureEntries.get(key)) && (!access || /^\[.*\]$/.test(access));
|
|
461
668
|
});
|
|
669
|
+
const pending = upgradePendencies({ pendingAxes, envPlan });
|
|
462
670
|
logger.info("");
|
|
463
671
|
logger.info(
|
|
464
|
-
|
|
465
|
-
? `${CYAN}➡️ Agora rode ${BOLD}/jarvis-init upgrade${NC}${CYAN} na IDE
|
|
466
|
-
: `${
|
|
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`
|
|
467
675
|
);
|
|
468
676
|
return sync.errors.length || moveErrors.length || staleErrors.length ? 1 : 0;
|
|
469
677
|
}
|
package/bin/lib/cli/program.js
CHANGED
|
@@ -22,7 +22,6 @@ import { info } from "../../commands/info.js";
|
|
|
22
22
|
import { whoami } from "../../commands/whoami.js";
|
|
23
23
|
import { docsSync } from "../../commands/docs-sync.js";
|
|
24
24
|
import { docsPublish } from "../../commands/docs-publish.js";
|
|
25
|
-
import { qaSignoff } from "../../commands/qa-signoff.js";
|
|
26
25
|
import { installRtk } from "../../commands/install-rtk.js";
|
|
27
26
|
import { tokens } from "../../commands/tokens.js";
|
|
28
27
|
import { map } from "../../commands/map.js";
|
|
@@ -88,15 +87,18 @@ export function buildProgram() {
|
|
|
88
87
|
const initCmd = cmd(program, "init")
|
|
89
88
|
.description("Bootstrap do framework no workspace")
|
|
90
89
|
.option("--ide <ide>", "IDE a configurar (pula o menu interativo)")
|
|
91
|
-
.option("--force", "
|
|
90
|
+
.option("--force", "Reinstala sem mostrar o menu: sobrescreve os arquivos do framework")
|
|
92
91
|
.option(
|
|
93
92
|
"--upgrade",
|
|
94
|
-
"Atualiza
|
|
93
|
+
"Atualiza sem mostrar o menu: arquivos do framework, variáveis novas do ENV e pastas de documentos"
|
|
94
|
+
)
|
|
95
|
+
.option(
|
|
96
|
+
"--yes",
|
|
97
|
+
"Com --upgrade (ou a opção Atualizar do menu), move os documentos para as pastas novas e aceita rebaixar os assets sem perguntar"
|
|
95
98
|
)
|
|
96
|
-
.option("--yes", "Com --upgrade, move os documentos para as pastas novas sem perguntar")
|
|
97
99
|
.option(
|
|
98
100
|
"--remove-stale",
|
|
99
|
-
"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)"
|
|
100
102
|
);
|
|
101
103
|
withLogOptions(initCmd).action((options) => init(toLegacyFlags(options)));
|
|
102
104
|
|
|
@@ -162,12 +164,6 @@ export function buildProgram() {
|
|
|
162
164
|
);
|
|
163
165
|
withLogOptions(installRtkCmd).action((options) => installRtk(toLegacyFlags(options)));
|
|
164
166
|
|
|
165
|
-
const qaSignoffCmd = cmd(program, "qa-signoff")
|
|
166
|
-
.description("Verificar sign-off QA válido para a branch atual (usado pelo CI)")
|
|
167
|
-
.option("--branch <branch>", "Branch a verificar (padrão: lido do git)")
|
|
168
|
-
.option("--max-age <horas>", "Validade máxima em horas (padrão: 24)");
|
|
169
|
-
withEnvOptions(qaSignoffCmd).action((options) => qaSignoff(toLegacyFlags(options)));
|
|
170
|
-
|
|
171
167
|
cmd(program, "tokens")
|
|
172
168
|
.description("Estimar o que carrega no início da sessão")
|
|
173
169
|
.option("--hub <hub>")
|
|
@@ -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}/ (
|
|
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} (
|
|
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
|
-
|
|
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
|
}
|
|
@@ -575,7 +579,7 @@ function writeLockFile(targetDir, ideDir, ide, frameworkRoot) {
|
|
|
575
579
|
|
|
576
580
|
const lockContent = JSON.stringify(lock, null, 2) + "\n";
|
|
577
581
|
|
|
578
|
-
// Write lock file to project root (
|
|
582
|
+
// Write lock file to project root (lido pelo jarvis init --upgrade e pelo Passo 0 do /jarvis-init)
|
|
579
583
|
writeFileSync(join(targetDir, LOCK_FILE), lockContent);
|
|
580
584
|
|
|
581
585
|
// Also write lock file to IDE folder
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Leitura de frontmatter YAML (`---\n...\n---`) compartilhada
|
|
3
|
-
* entre scanner.js, validate-frontmatter.js
|
|
3
|
+
* entre scanner.js, validate-frontmatter.js e sync-engine.js —
|
|
4
4
|
* cada um tinha sua própria cópia da mesma regex de bloco.
|
|
5
5
|
* @module utils/frontmatter
|
|
6
6
|
*/
|
package/bin/lib/utils/paths.js
CHANGED
|
@@ -18,7 +18,8 @@ export function getFrameworkRoot() {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
throw new Error(
|
|
21
|
-
"
|
|
21
|
+
"Não foi possível localizar os assets do framework.\n" +
|
|
22
|
+
"Instale o Jarvis global: npm install -g jarvis-ai-framework"
|
|
22
23
|
);
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { lstatSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
const PACKAGE = "jarvis-ai-framework";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Compara duas versões `x.y.z` (aceita `v` na frente e ignora o sufixo, como `-beta.1`).
|
|
8
|
+
* @param {string} a
|
|
9
|
+
* @param {string} b
|
|
10
|
+
* @returns {-1 | 0 | 1 | null} `null` se uma das duas não for uma versão legível
|
|
11
|
+
*/
|
|
12
|
+
export function compareVersions(a, b) {
|
|
13
|
+
const parse = (v) => {
|
|
14
|
+
const m = String(v ?? "")
|
|
15
|
+
.trim()
|
|
16
|
+
.match(/^v?(\d+)\.(\d+)\.(\d+)/);
|
|
17
|
+
return m ? m.slice(1).map(Number) : null;
|
|
18
|
+
};
|
|
19
|
+
const pa = parse(a);
|
|
20
|
+
const pb = parse(b);
|
|
21
|
+
if (!pa || !pb) return null;
|
|
22
|
+
for (let i = 0; i < 3; i++) {
|
|
23
|
+
if (pa[i] !== pb[i]) return pa[i] > pb[i] ? 1 : -1;
|
|
24
|
+
}
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// lstat não segue o link: um link simbólico quebrado também conta como presente.
|
|
29
|
+
function exists(path) {
|
|
30
|
+
try {
|
|
31
|
+
lstatSync(path);
|
|
32
|
+
return true;
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function readJson(path) {
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Cópia local do Jarvis no projeto, que é ignorada (o Jarvis é instalado só global).
|
|
48
|
+
* @param {string} workspaceDir
|
|
49
|
+
* @returns {{ version: string, source: string } | null} a versão instalada em `node_modules`
|
|
50
|
+
* ("desconhecida" se ilegível), ou a faixa declarada no `package.json`; `null` sem nenhuma das duas
|
|
51
|
+
*/
|
|
52
|
+
export function localInstall(workspaceDir) {
|
|
53
|
+
const installed = join(workspaceDir, "node_modules", PACKAGE, "package.json");
|
|
54
|
+
if (exists(join(workspaceDir, "node_modules", PACKAGE))) {
|
|
55
|
+
// Pasta (ou link, mesmo quebrado) presente, sem package.json legível: ainda é uma cópia local.
|
|
56
|
+
const version = readJson(installed)?.version || "desconhecida";
|
|
57
|
+
return { version, source: `node_modules/${PACKAGE}` };
|
|
58
|
+
}
|
|
59
|
+
const pkg = readJson(join(workspaceDir, "package.json"));
|
|
60
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
61
|
+
const range = pkg?.[field]?.[PACKAGE];
|
|
62
|
+
if (range) return { version: range, source: `package.json (${field})` };
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
package/bin/postinstall.js
CHANGED
|
@@ -1,41 +1,17 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
1
|
import { execSync } from "node:child_process";
|
|
3
|
-
import { join, dirname } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// ── Sincronizar assets para projetos com jarvis-lock.json ───────────────────
|
|
13
|
-
if (existsSync(lockPath)) {
|
|
14
|
-
try {
|
|
15
|
-
const lock = JSON.parse(readFileSync(lockPath, "utf-8"));
|
|
16
|
-
const { syncAssets } = await import("./lib/core/sync-engine.js");
|
|
17
|
-
const { getWorkflowsFolder } = await import("./lib/config/ide-config.js");
|
|
18
|
-
const { logger } = await import("./lib/utils/logger.js");
|
|
19
|
-
logger.info(`\n jarvis-framework — syncing to .${lock.ide}/\n`);
|
|
20
|
-
const result = syncAssets(cwd, lock.ide, {
|
|
21
|
-
force: true,
|
|
22
|
-
workflowsFolder: getWorkflowsFolder(lock.ide),
|
|
23
|
-
});
|
|
24
|
-
logger.info(` ${result.copied.length} items synced.\n`);
|
|
25
|
-
} catch {}
|
|
26
|
-
} else {
|
|
27
|
-
const { logger } = await import("./lib/utils/logger.js");
|
|
28
|
-
logger.info(`
|
|
29
|
-
jarvis-framework installed.
|
|
30
|
-
Run: npx jarvis init --ide windsurf
|
|
3
|
+
// O Jarvis é instalado só global (npm install -g); o postinstall só dá as dicas.
|
|
4
|
+
// A atualização dos assets de um projeto tem um caminho só: jarvis init --upgrade, dentro dele.
|
|
5
|
+
const { logger } = await import("./lib/utils/logger.js");
|
|
6
|
+
logger.info(`
|
|
7
|
+
jarvis-ai-framework instalado.
|
|
8
|
+
No projeto: jarvis init --ide {ide} (ou jarvis init --upgrade para atualizar os assets)
|
|
31
9
|
`);
|
|
32
|
-
}
|
|
33
10
|
|
|
34
11
|
// ── Sugestão RTK (sempre, se rtk não estiver instalado) ─────────────────────
|
|
35
12
|
try {
|
|
36
13
|
execSync("rtk gain --help 2>/dev/null", { stdio: "pipe" });
|
|
37
14
|
} catch {
|
|
38
|
-
const { logger } = await import("./lib/utils/logger.js");
|
|
39
15
|
logger.info(` 💡 Dica: instale o RTK para economizar 60-90% dos tokens de shell.`);
|
|
40
|
-
logger.info(`
|
|
16
|
+
logger.info(` jarvis install-rtk\n`);
|
|
41
17
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jarvis-ai-framework",
|
|
3
|
-
"version": "
|
|
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",
|
|
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/",
|
package/rules/AGENTS.md
CHANGED
|
@@ -169,7 +169,7 @@ Links para documentacao relacionada.
|
|
|
169
169
|
|------|----------|-----------|
|
|
170
170
|
| Planejamento | `eng.start`, `eng.plan` | Somente analise, SEM codigo |
|
|
171
171
|
| Implementacao | `eng.work` | Codigo e testes, commit por fase, SEM push nem PR |
|
|
172
|
-
| Entrega | `eng.pr` |
|
|
172
|
+
| Entrega | `eng.pr` | Push e PR, depois da pergunta (regra de ouro) |
|
|
173
173
|
|
|
174
174
|
### Seguranca
|
|
175
175
|
|
|
@@ -67,7 +67,7 @@ expect_table_row_count_to_be_between(min_value=last_run_count * 0.5)
|
|
|
67
67
|
### Falha de qualidade
|
|
68
68
|
|
|
69
69
|
- **Bronze → Silver**: falha bloqueia a promoção. Dados permanecem em bronze.
|
|
70
|
-
- **Silver → Gold**: falha bloqueia publicação. Alerta no Slack canal do time de Data.
|
|
70
|
+
- **Silver → Gold**: falha bloqueia publicação. Alerta automático do pipeline no Slack canal do time de Data (runtime; um aviso enviado pelo Jarvis só sai depois da pergunta, regra de ouro).
|
|
71
71
|
- Nunca silenciar falhas — registrar no log com contexto da execução.
|
|
72
72
|
|
|
73
73
|
---
|
|
@@ -116,7 +116,7 @@ logger.info({
|
|
|
116
116
|
|
|
117
117
|
- **AWS Glue**: logs automáticos no CloudWatch (`/aws-glue/jobs/output`)
|
|
118
118
|
- **Airflow**: logs nas tasks do DAG + CloudWatch quando em produção
|
|
119
|
-
- **Erros críticos**:
|
|
119
|
+
- **Erros críticos**: alerta automático do pipeline no canal Slack do time de Data (`#data-alerts` ou equivalente; runtime. Um aviso enviado pelo Jarvis só sai depois da pergunta, regra de ouro)
|
|
120
120
|
|
|
121
121
|
### Retenção
|
|
122
122
|
|
|
@@ -65,7 +65,7 @@ env_file: "@/ENV.md"
|
|
|
65
65
|
- No modo workspace a raiz (a pasta que contém `$IDE/`) **não é um repositório** e guarda itens fora do git (`.claude/`, `.jarvis/`, `.vscode/`, `node_modules/`). `grep`, `ls` e `Glob` na raiz leem tudo isso como se fosse código do projeto e confundem a análise.
|
|
66
66
|
- **Código do projeto** = só as subpastas com `.git/` (ou as de `WORKSPACE_REPOS`, quando preenchido). Busque e leia código **dentro delas**.
|
|
67
67
|
- **Ignore nas buscas de código**: a pasta da IDE ativa (derivada do `IDE`, tabela acima), `.jarvis/`, `.vscode/`, `node_modules/` e o `CDD-MIND-MAP.md`. Leia esses itens **só quando o workflow mandar** (ENV.md, rules, skills, templates, sessão).
|
|
68
|
-
- **`docs/jarvis/` não é ignorada**: guarda os documentos de trabalho gerados (PRD, FRD, ARD, RFC, SPEC, discoveries, Central docs), fora do versionamento. Leia-a sob demanda, como documento de trabalho, e não como código.
|
|
68
|
+
- **`docs/jarvis/` não é ignorada**: guarda os documentos de trabalho gerados (PRD, FRD, ARD, RFC, SPEC, discoveries) e, com storage `filesystem`, a cópia local do Central docs (`central-docs/`, no local padrão; o registro do implementado), fora do versionamento. Leia-a sob demanda, como documento de trabalho ou registro, e não como código.
|
|
69
69
|
- **Pastas de outras IDEs** nunca são lidas (vale o "Isolamento de IDE" acima).
|
|
70
70
|
- Com `WORKSPACE_MODE=project`, o git já delimita o código e esta regra não age.
|
|
71
71
|
|
|
@@ -105,7 +105,7 @@ O agente DEVE incluir no checklist de pre-PR as seguintes verificacoes de segura
|
|
|
105
105
|
- [ ] **Outputs**: erros nao vazam detalhes internos
|
|
106
106
|
|
|
107
107
|
Se o PR toca em areas sensiveis (auth, sessions, RBAC, CORS, CSP, permissoes), o agente DEVE:
|
|
108
|
-
1.
|
|
108
|
+
1. Registrar no PR Brief do `gate-checklist.md` que o PR leva o label `security` e orientar a pessoa a adiciona-lo ao PR (o Jarvis nao altera o PR no vendor sem perguntar, regra de ouro; o `jarvis vcs create-merge` nao poe label)
|
|
109
109
|
2. Seguir o gate de seguranca inline do `eng.pre-pr` (sanitizacao, auth guards, headers, secrets scan, `npm audit`) antes do merge
|
|
110
110
|
|
|
111
111
|
---
|