jarvis-ai-framework 3.2.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/agents/engineering/eng.docs-consolidator.agent.md +81 -0
  2. package/agents/engineering/qa/eng.qa.test-planner.md +7 -7
  3. package/agents/product/prod.discovery-interviewer.md +1 -1
  4. package/bin/README.md +14 -14
  5. package/bin/commands/docs-publish.js +61 -93
  6. package/bin/commands/docs-sync.js +31 -63
  7. package/bin/lib/cli/program.js +23 -25
  8. package/bin/lib/docs/fetch-file.js +29 -26
  9. package/bin/lib/docs/publish-file.js +98 -159
  10. package/bin/lib/docs/storage/build-index.js +108 -0
  11. package/bin/lib/docs/storage/filesystem.js +203 -0
  12. package/bin/lib/docs/storage/index.js +75 -0
  13. package/bin/lib/docs/validate-frontmatter.js +18 -4
  14. package/bin/lib/vcs/api.js +0 -125
  15. package/package.json +2 -2
  16. package/rules/AGENTS.md +12 -9
  17. package/rules/engineering/eng.docs-scraping-rules.md +4 -4
  18. package/rules/engineering/eng.integrations-rules.md +3 -2
  19. package/rules/engineering/rpa/eng.rpa-rules.md +1 -1
  20. package/rules/product/prod-rules.md +5 -6
  21. package/rules-on-demand/engineering/eng.pr-rules.md +9 -31
  22. package/rules-on-demand/engineering/eng.pre-pr-rules.md +12 -19
  23. package/rules-on-demand/product/prod.guided-rules.md +104 -0
  24. package/skills/eng-backend-arch-c4/SKILL.md +3 -3
  25. package/skills/eng-global-docs-write/SKILL.md +1 -1
  26. package/skills/eng-qa-planner/SKILL.md +5 -5
  27. package/skills/jarvis-docs-central/README.md +51 -275
  28. package/skills/jarvis-docs-central/SKILL.md +114 -301
  29. package/skills/jarvis-init/SKILL.md +81 -8
  30. package/templates/ENV-template.md +28 -7
  31. package/templates/engineering/ARD-template.md +40 -7
  32. package/templates/engineering/architecture-template.md +1 -0
  33. package/templates/engineering/central-delivery-template.md +65 -0
  34. package/templates/engineering/central-feature-template.md +86 -0
  35. package/templates/product/prod-breakdown-template.md +20 -2
  36. package/templates/product/prod-discovery-template.md +24 -1
  37. package/templates/product/prod-epic-template.md +32 -1
  38. package/templates/product/prod-frd-template.md +29 -2
  39. package/templates/product/prod-issue-template.md +36 -1
  40. package/templates/product/prod-prd-template.md +33 -2
  41. package/workflows/engineering/eng.build-tech-spec.md +4 -4
  42. package/workflows/engineering/eng.create-ard-from-code.md +15 -41
  43. package/workflows/engineering/eng.create-ard.md +22 -44
  44. package/workflows/engineering/eng.create-rfc.md +10 -36
  45. package/workflows/engineering/eng.pr.md +35 -66
  46. package/workflows/engineering/eng.pre-pr.md +52 -89
  47. package/workflows/engineering/eng.rpa.robot.md +9 -7
  48. package/workflows/engineering/eng.spec-consolidate.md +140 -0
  49. package/workflows/engineering/eng.start.md +23 -22
  50. package/workflows/engineering/eng.work.md +1 -1
  51. package/workflows/engineering/qa/eng.qa-refinement-entry.md +1 -1
  52. package/workflows/engineering/qa/eng.qa-release-signoff.md +3 -3
  53. package/workflows/engineering/qa/eng.qa-sprint-planning.md +2 -2
  54. package/workflows/product/prod.roadmap.preview.md +4 -1
  55. package/workflows/product/prod.spec.ard.md +9 -5
  56. package/workflows/product/prod.spec.breakdown.md +8 -4
  57. package/workflows/product/prod.spec.clarify.md +5 -1
  58. package/workflows/product/prod.spec.discovery.md +7 -3
  59. package/workflows/product/prod.spec.epic.md +12 -40
  60. package/workflows/product/prod.spec.frd.md +13 -39
  61. package/workflows/product/prod.spec.issue.md +5 -1
  62. package/workflows/product/prod.spec.md +3 -1
  63. package/workflows/product/prod.spec.prd.md +13 -40
  64. package/workflows/warm-up.md +10 -12
  65. package/bin/lib/docs/redis-cache.js +0 -49
  66. package/bin/lib/vcs/fetch-raw.js +0 -42
@@ -0,0 +1,203 @@
1
+ /**
2
+ * @fileoverview Adaptador `filesystem` do Central docs: uma pasta do disco
3
+ * (dentro do workspace ou em qualquer caminho). Se a pasta tiver `.git` (clone
4
+ * de um repositório de documentação), liga a camada git: `pull` antes de ler e
5
+ * branch + commit + push + PR ao publicar.
6
+ *
7
+ * Todo comando externo usa `execFileSync` com argumentos (sem shell), e todo
8
+ * caminho recebido é resolvido dentro da pasta (sem `..`).
9
+ * @module docs/storage/filesystem
10
+ */
11
+
12
+ import { execFileSync } from "node:child_process";
13
+ import { copyFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
14
+ import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
15
+ import { createMergeRequest as defaultCreateMergeRequest } from "../../vcs/api.js";
16
+ import { logger } from "../../utils/logger.js";
17
+ import { buildIndex, walkFiles } from "./build-index.js";
18
+
19
+ /**
20
+ * Executa `git` na pasta informada, sem shell.
21
+ * @param {string[]} args
22
+ * @param {string} cwd
23
+ * @returns {string} stdout sem espaços nas pontas
24
+ */
25
+ export function runGit(args, cwd) {
26
+ return execFileSync("git", args, {
27
+ cwd,
28
+ encoding: "utf-8",
29
+ stdio: ["ignore", "pipe", "pipe"],
30
+ }).trim();
31
+ }
32
+
33
+ /**
34
+ * Resolve um caminho relativo dentro de `root`, rejeitando o que sair dela.
35
+ * @param {string} root
36
+ * @param {string} rel
37
+ * @returns {string} caminho absoluto
38
+ * @throws {Error} caminho absoluto, com `..` que escape da pasta, ou vazio
39
+ */
40
+ export function safePath(root, rel) {
41
+ if (!rel || isAbsolute(rel)) {
42
+ throw new Error(`Caminho inválido para o Central docs: "${rel}"`);
43
+ }
44
+ const full = resolve(root, rel);
45
+ const fromRoot = relative(root, full);
46
+ if (fromRoot === "" || fromRoot.startsWith("..") || isAbsolute(fromRoot)) {
47
+ throw new Error(`Caminho fora da pasta do Central docs: "${rel}"`);
48
+ }
49
+ return full;
50
+ }
51
+
52
+ /**
53
+ * @param {Object} cfg
54
+ * @param {string} cfg.location - Pasta do Central docs (absoluta)
55
+ * @param {string} [cfg.ref] - Branch lida no `pull` (CENTRAL_DOCS_REF)
56
+ * @param {string} [cfg.targetBranch] - Branch base do PR (CENTRAL_DOCS_TARGET_BRANCH)
57
+ * @param {string} [cfg.reviewers] - Revisores (vão na descrição do PR)
58
+ * @param {Object} [cfg.deps] - `runGit` e `createMergeRequest` injetáveis (testes)
59
+ */
60
+ export function createFilesystemStorage({
61
+ location,
62
+ ref = "main",
63
+ targetBranch = "main",
64
+ reviewers = "",
65
+ deps = {},
66
+ }) {
67
+ const git = deps.runGit ?? runGit;
68
+ const createMergeRequest = deps.createMergeRequest ?? defaultCreateMergeRequest;
69
+ const isGit = existsSync(join(location, ".git"));
70
+ let pulled = false;
71
+
72
+ /** `git pull --ff-only` uma vez por instância; falha vira aviso e a leitura segue local. */
73
+ function pullOnce() {
74
+ if (!isGit || pulled) return;
75
+ pulled = true;
76
+ try {
77
+ git(["pull", "--ff-only", "origin", ref], location);
78
+ } catch (error) {
79
+ logger.warn(`⚠️ Não foi possível atualizar o Central docs (git pull): ${error.message.split("\n")[0]}`);
80
+ logger.warn(" Usando a cópia local.");
81
+ }
82
+ }
83
+
84
+ function ensureLocation() {
85
+ if (!existsSync(location)) {
86
+ throw new Error(
87
+ `Pasta do Central docs não encontrada: ${location}\nCrie a pasta ou ajuste CENTRAL_DOCS_LOCATION no ENV.md (/jarvis-init).`
88
+ );
89
+ }
90
+ }
91
+
92
+ return {
93
+ kind: "filesystem",
94
+ location,
95
+ isGit,
96
+
97
+ /**
98
+ * Lê um documento.
99
+ * @param {string} path - Caminho dentro do Central docs (ex: `CORE/index.md`)
100
+ * @returns {Promise<string>}
101
+ * @throws {Error} com `code = 404` se o arquivo não existir
102
+ */
103
+ async fetch(path) {
104
+ ensureLocation();
105
+ pullOnce();
106
+ const full = safePath(location, path);
107
+ if (!existsSync(full)) {
108
+ const err = new Error(`Arquivo não encontrado no Central docs: ${path}`);
109
+ err.code = 404;
110
+ throw err;
111
+ }
112
+ return readFileSync(full, "utf-8");
113
+ },
114
+
115
+ /**
116
+ * Lista os `.md` sob um prefixo.
117
+ * @param {string} prefix
118
+ * @returns {Promise<string[]>} caminhos relativos à pasta, com `/`
119
+ */
120
+ async list(prefix) {
121
+ ensureLocation();
122
+ pullOnce();
123
+ return walkFiles(safePath(location, prefix))
124
+ .filter((f) => f.endsWith(".md"))
125
+ .map((f) => relative(location, f).split(sep).join("/"));
126
+ },
127
+
128
+ /**
129
+ * Regenera `{squad}/index.md`. Com `.git` e `scripts/build-index.sh` no repositório,
130
+ * usa o script dele (o CI do repositório confere o índice com ele).
131
+ * @param {string} squad
132
+ * @returns {string} caminho relativo do índice
133
+ */
134
+ buildIndex(squad) {
135
+ const script = join(location, "scripts", "build-index.sh");
136
+ if (isGit && existsSync(script)) {
137
+ execFileSync("bash", [script], { cwd: location, stdio: ["ignore", "pipe", "pipe"] });
138
+ } else {
139
+ buildIndex(location, squad);
140
+ }
141
+ return `${squad}/index.md`;
142
+ },
143
+
144
+ /**
145
+ * Grava um documento e regenera o índice do squad.
146
+ * Sem `.git`: copia e pronto. Com `.git`: branch → cópia → índice → commit → push → PR.
147
+ * @param {Object} p
148
+ * @param {string} p.localFile - Arquivo de origem
149
+ * @param {string} p.destPath - Destino dentro do Central docs (`{SQUAD}/{área}/{produto}/{arquivo}`)
150
+ * @param {string} p.squad
151
+ * @param {string} p.tipo
152
+ * @returns {Promise<{ destPath: string, indexPath: string, sha?: string, branch?: string, mr?: Object }>}
153
+ */
154
+ async publish({ localFile, destPath, squad, tipo }) {
155
+ ensureLocation();
156
+ const dest = safePath(location, destPath);
157
+
158
+ if (!isGit) {
159
+ mkdirSync(dirname(dest), { recursive: true });
160
+ copyFileSync(localFile, dest);
161
+ const indexPath = this.buildIndex(squad);
162
+ return { destPath, indexPath };
163
+ }
164
+
165
+ const branch = `docs/${destPath.replace(extname(destPath), "")}`;
166
+ git(["checkout", targetBranch], location);
167
+ try {
168
+ git(["pull", "--ff-only", "origin", targetBranch], location);
169
+ } catch (error) {
170
+ logger.warn(`⚠️ git pull falhou (${error.message.split("\n")[0]}); seguindo com a cópia local.`);
171
+ }
172
+ git(["checkout", "-b", branch], location);
173
+
174
+ try {
175
+ mkdirSync(dirname(dest), { recursive: true });
176
+ copyFileSync(localFile, dest);
177
+ const indexPath = this.buildIndex(squad);
178
+ git(["add", "--", destPath, indexPath], location);
179
+ git(["commit", "-m", `docs(${squad}): add ${tipo} ${basename(destPath)}`], location);
180
+ const sha = git(["rev-parse", "HEAD"], location);
181
+
182
+ git(["push", "-u", "origin", branch], location);
183
+ const remoteUrl = git(["remote", "get-url", "origin"], location);
184
+ let description = `## Documento\n\n- **Tipo**: ${tipo}\n- **Arquivo**: \`${destPath}\``;
185
+ if (reviewers) description += `\n- **Revisores**: ${reviewers}`;
186
+ const mr = await createMergeRequest(remoteUrl, {
187
+ source: branch,
188
+ target: targetBranch,
189
+ title: `docs(${squad}): ${tipo} ${basename(destPath)}`,
190
+ description,
191
+ });
192
+
193
+ return { destPath, indexPath, sha, branch, mr };
194
+ } finally {
195
+ try {
196
+ git(["checkout", targetBranch], location);
197
+ } catch {
198
+ // a branch de trabalho fica; o erro original (se houver) é o que importa
199
+ }
200
+ }
201
+ },
202
+ };
203
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @fileoverview Fábrica dos adaptadores de armazenamento do Central docs.
3
+ * O valor de `CENTRAL_DOCS_STORAGE` escolhe o adaptador; `CENTRAL_DOCS_LOCATION`
4
+ * diz onde ele guarda. Hoje só existe o `filesystem` (com camada git opcional);
5
+ * S3 e Google Drive são as issues #144 e #145.
6
+ * @module docs/storage
7
+ */
8
+
9
+ import { isAbsolute, resolve } from "node:path";
10
+ import { createFilesystemStorage } from "./filesystem.js";
11
+
12
+ /** @constant {ReadonlyArray<string>} Adaptadores disponíveis */
13
+ export const SUPPORTED_STORAGES = Object.freeze(["filesystem"]);
14
+
15
+ /**
16
+ * Erro de configuração do Central docs (variável ausente, valor não suportado).
17
+ */
18
+ export class StorageConfigError extends Error {}
19
+
20
+ /**
21
+ * Resolve `CENTRAL_DOCS_LOCATION` a partir da pasta do workspace (a que contém `$IDE/`).
22
+ * @param {string} location
23
+ * @param {string} root - Pasta do workspace
24
+ * @returns {string} caminho absoluto
25
+ */
26
+ export function resolveLocation(location, root) {
27
+ return isAbsolute(location) ? location : resolve(root, location);
28
+ }
29
+
30
+ /**
31
+ * Devolve o adaptador configurado no ENV.md, ou `null` se o Central docs estiver desligado.
32
+ *
33
+ * @param {Record<string, string>} env - Variáveis do ENV.md (`loadEnv`)
34
+ * @param {Object} opts
35
+ * @param {string} opts.root - Pasta do workspace (`resolveEnvPath(...).root`)
36
+ * @param {Object} [opts.deps] - Dependências injetáveis (testes): `runGit`, `createMergeRequest`
37
+ * @returns {ReturnType<typeof createFilesystemStorage> | null}
38
+ * @throws {StorageConfigError} valor de STORAGE não suportado, LOCATION ausente ou variável antiga
39
+ */
40
+ export function getStorage(env, { root, deps } = {}) {
41
+ const storage = (env.CENTRAL_DOCS_STORAGE || "").trim().toLowerCase();
42
+
43
+ if (!storage) {
44
+ if (env.CENTRAL_DOCS_REPO) {
45
+ throw new StorageConfigError(
46
+ "CENTRAL_DOCS_REPO não é mais usado. Rode o /jarvis-init e escolha Upgrade (C) para migrar " +
47
+ "para CENTRAL_DOCS_STORAGE + CENTRAL_DOCS_LOCATION."
48
+ );
49
+ }
50
+ return null;
51
+ }
52
+
53
+ if (!SUPPORTED_STORAGES.includes(storage)) {
54
+ throw new StorageConfigError(
55
+ `Armazenamento "${storage}" ainda não suportado. Use: ${SUPPORTED_STORAGES.join(", ")}.`
56
+ );
57
+ }
58
+
59
+ if (!env.CENTRAL_DOCS_LOCATION) {
60
+ throw new StorageConfigError(
61
+ "CENTRAL_DOCS_LOCATION não definido no ENV.md (pasta do Central docs)."
62
+ );
63
+ }
64
+ if (!root) {
65
+ throw new StorageConfigError("getStorage: pasta do workspace (root) não informada.");
66
+ }
67
+
68
+ return createFilesystemStorage({
69
+ location: resolveLocation(env.CENTRAL_DOCS_LOCATION, root),
70
+ ref: env.CENTRAL_DOCS_REF || "main",
71
+ targetBranch: env.CENTRAL_DOCS_TARGET_BRANCH || "main",
72
+ reviewers: env.CENTRAL_DOCS_REVIEWERS || "",
73
+ deps,
74
+ });
75
+ }
@@ -2,10 +2,10 @@ import { readFileSync } from 'node:fs'
2
2
  import { parseFrontmatter, extractFrontmatterBlock } from '../utils/frontmatter.js'
3
3
 
4
4
  /**
5
- * Valida frontmatter YAML de documentos (PRD, FRD, ARD, RFC)
5
+ * Valida frontmatter YAML de documentos (PRD, FRD, épico, história, tarefa, ARD, RFC)
6
6
  *
7
7
  * @param {string} filePath - Caminho do arquivo
8
- * @param {string} tipo - Tipo do documento (prd|frd|ard|rfc)
8
+ * @param {string} tipo - Tipo do documento (ver VALID_TYPES em publish-file.js)
9
9
  * @returns {Object} Metadados extraídos
10
10
  * @throws {Error} Se frontmatter for inválido
11
11
  */
@@ -54,7 +54,13 @@ export function validateFrontmatter(filePath, tipo) {
54
54
  }
55
55
 
56
56
  // Validar status
57
- const validStatuses = ['icebox', 'in_review', 'in_progress', 'in_production', 'deprecated', 'Proposta', 'Em desenvolvimento', 'Em validação', 'Finalizada', 'Draft', 'In Review', 'Ready for Decision', 'Accepted', 'Rejected', 'Superseded']
57
+ // Produto (rules/product/prod-rules.md), ARD (templates/engineering/ARD-template.md),
58
+ // discovery (prod-discovery-template.md) e RFC (RFC-template.md)
59
+ const validStatuses = [
60
+ 'icebox', 'in_review', 'backlog', 'in_progress', 'in_production', 'cancelled', 'deprecated',
61
+ 'proposed', 'approved',
62
+ 'Draft', 'In Review', 'Ready for Decision', 'Accepted', 'Rejected', 'Superseded'
63
+ ]
58
64
 
59
65
  if (metadata.status && !validStatuses.includes(metadata.status)) {
60
66
  console.warn(
@@ -74,9 +80,17 @@ export function validateFrontmatter(filePath, tipo) {
74
80
  */
75
81
  function getRequiredFields(tipo) {
76
82
  const fields = {
83
+ // Consolidação do Central docs (/eng.spec-consolidate)
84
+ feature: ['id', 'name', 'status', 'product', 'updated_at'],
85
+ delivery: ['id', 'feature', 'task_key', 'pr', 'date'],
86
+ // Documentos de trabalho (validação usada fora do publish)
77
87
  prd: ['id', 'name', 'version', 'status'],
78
88
  frd: ['id', 'name', 'version', 'status', 'related_prd'],
79
- ard: ['Status', 'Data', 'Autor', 'Versão'],
89
+ feat: ['id', 'name', 'version', 'status', 'related_prd'],
90
+ epic: ['id', 'name', 'version', 'status', 'related_prd'],
91
+ story: ['id', 'name', 'status', 'related_prd'],
92
+ task: ['id', 'name', 'status'],
93
+ ard: ['id', 'name', 'version', 'status'],
80
94
  rfc: ['Status', 'Criado em', 'Proponente'],
81
95
  'qa-report': ['period', 'squad', 'version']
82
96
  }
@@ -37,39 +37,6 @@ async function request(url, { method = "GET", headers = {}, body } = {}) {
37
37
  return { ok: res.ok, status: res.status, json, text };
38
38
  }
39
39
 
40
- export async function fetchRawFile(repoUrl, filePath, ref = "main") {
41
- const repo = parseRepoUrl(repoUrl);
42
- const token = getVcsToken(repo.vendor);
43
- const headers = authHeaders(repo.vendor, token);
44
- const encodedPath = encodeURIComponent(filePath);
45
-
46
- let url;
47
- if (repo.vendor === "gitlab") {
48
- url = `${repo.apiBase}/projects/${repo.projectPathEncoded}/repository/files/${encodedPath}/raw?ref=${encodeURIComponent(ref)}`;
49
- } else if (repo.vendor === "github") {
50
- url = `${repo.apiBase}/repos/${repo.owner}/${repo.repo}/contents/${filePath}?ref=${encodeURIComponent(ref)}`;
51
- headers.Accept = "application/vnd.github.raw";
52
- } else {
53
- url = `${repo.apiBase}/repositories/${repo.owner}/${repo.repo}/src/${encodeURIComponent(ref)}/${filePath}`;
54
- }
55
-
56
- const res = await fetch(url, { headers });
57
- if (res.status === 404) {
58
- const err = new Error(`not_found:${filePath}`);
59
- err.code = 404;
60
- throw err;
61
- }
62
- if (res.status === 401 || res.status === 403) {
63
- const err = new Error(`unauthorized:${repo.vendor}`);
64
- err.code = res.status;
65
- throw err;
66
- }
67
- if (!res.ok) {
68
- throw new Error(`fetch failed ${res.status}: ${await res.text()}`);
69
- }
70
- return await res.text();
71
- }
72
-
73
40
  export async function createMergeRequest(repoUrl, opts) {
74
41
  const { source, target, title, description = "" } = opts;
75
42
  const repo = parseRepoUrl(repoUrl);
@@ -141,98 +108,6 @@ export async function createMergeRequest(repoUrl, opts) {
141
108
  };
142
109
  }
143
110
 
144
- /**
145
- * Commit de um ou mais arquivos numa branch nova (docs publish).
146
- * files: [{ path, contentBase64, action: 'create'|'update' }]
147
- */
148
- export async function commitFiles(repoUrl, opts) {
149
- const { branch, startBranch, message, files } = opts;
150
- const repo = parseRepoUrl(repoUrl);
151
- const token = getVcsToken(repo.vendor);
152
- const headers = authHeaders(repo.vendor, token);
153
-
154
- if (repo.vendor === "gitlab") {
155
- const { ok, status, json } = await request(
156
- `${repo.apiBase}/projects/${repo.projectPathEncoded}/repository/commits`,
157
- {
158
- method: "POST",
159
- headers,
160
- body: {
161
- branch,
162
- start_branch: startBranch,
163
- commit_message: message,
164
- actions: files.map((f) => ({
165
- action: f.action || "create",
166
- file_path: f.path,
167
- content: f.contentBase64,
168
- encoding: "base64",
169
- })),
170
- },
171
- }
172
- );
173
- if (!ok || !json?.id) {
174
- throw new Error(`Falha no commit GitLab (${status}): ${JSON.stringify(json)}`);
175
- }
176
- return { sha: json.id };
177
- }
178
-
179
- if (repo.vendor === "github") {
180
- const refRes = await request(
181
- `${repo.apiBase}/repos/${repo.owner}/${repo.repo}/git/ref/heads/${startBranch}`,
182
- { headers }
183
- );
184
- if (!refRes.ok) {
185
- throw new Error(`Branch ${startBranch} não encontrada no GitHub (${refRes.status})`);
186
- }
187
- const sha = refRes.json.object.sha;
188
- await request(`${repo.apiBase}/repos/${repo.owner}/${repo.repo}/git/refs`, {
189
- method: "POST",
190
- headers,
191
- body: { ref: `refs/heads/${branch}`, sha },
192
- });
193
- let lastSha = sha;
194
- for (const file of files) {
195
- const put = await request(
196
- `${repo.apiBase}/repos/${repo.owner}/${repo.repo}/contents/${file.path}`,
197
- {
198
- method: "PUT",
199
- headers,
200
- body: {
201
- message,
202
- content: file.contentBase64,
203
- branch,
204
- },
205
- }
206
- );
207
- if (!put.ok) {
208
- throw new Error(
209
- `Falha ao escrever ${file.path} no GitHub (${put.status}): ${JSON.stringify(put.json)}`
210
- );
211
- }
212
- lastSha = put.json.commit?.sha || lastSha;
213
- }
214
- return { sha: lastSha };
215
- }
216
-
217
- const form = new FormData();
218
- form.set("branch", branch);
219
- form.set("parents", startBranch);
220
- form.set("message", message);
221
- for (const file of files) {
222
- const buf = Buffer.from(file.contentBase64, "base64");
223
- form.set(file.path, new Blob([buf]));
224
- }
225
- const res = await fetch(`${repo.apiBase}/repositories/${repo.owner}/${repo.repo}/src`, {
226
- method: "POST",
227
- headers: { Authorization: `Bearer ${token}` },
228
- body: form,
229
- });
230
- if (!res.ok) {
231
- throw new Error(`Falha no commit Bitbucket (${res.status}): ${await res.text()}`);
232
- }
233
- return { sha: "ok" };
234
- }
235
-
236
111
  export async function createIssue(repoUrl, opts) {
237
112
  const { title, body, labels = ["bug"] } = opts;
238
113
  const repo = parseRepoUrl(repoUrl);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jarvis-ai-framework",
3
- "version": "3.2.0",
3
+ "version": "5.0.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",
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",
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
@@ -24,14 +24,16 @@ rules/ # Sempre carregadas no início da sessão
24
24
  └── prod-rules.md # Regras de especificacao
25
25
 
26
26
  rules-on-demand/ # Lidas pelo workflow que as usa (não carregadas no início)
27
- └── engineering/
28
- ├── eng.start-rules.md # Regras para /eng.start
29
- ├── eng.plan-rules.md # Regras para /eng.plan
30
- ├── eng.work-rules.md # Regras para /eng.work
31
- ├── eng.pre-pr-rules.md # Regras para /eng.pre-pr
32
- ├── eng.pr-rules.md # Regras para /eng.pr
33
- ├── eng.tech-spec-rules.md # Regras para tech specs
34
- └── eng.breakdown-subtasks-rules.md # Regras para o breakdown de subtarefas
27
+ ├── engineering/
28
+ │ ├── eng.start-rules.md # Regras para /eng.start
29
+ │ ├── eng.plan-rules.md # Regras para /eng.plan
30
+ │ ├── eng.work-rules.md # Regras para /eng.work
31
+ │ ├── eng.pre-pr-rules.md # Regras para /eng.pre-pr
32
+ │ ├── eng.pr-rules.md # Regras para /eng.pr
33
+ │ ├── eng.tech-spec-rules.md # Regras para tech specs
34
+ │ └── eng.breakdown-subtasks-rules.md # Regras para o breakdown de subtarefas
35
+ └── product/
36
+ └── prod.guided-rules.md # Modo Developer-Guided (lida só com PROD_MODE=developer-guided)
35
37
  ```
36
38
 
37
39
  ### `rules/` x `rules-on-demand/`
@@ -46,7 +48,8 @@ O Claude Code carrega tudo de `.claude/rules/` no início da sessão: `rules/` f
46
48
  1. .windsurf/rules / .cursor/rules (mais alta)
47
49
  2. rules/engineering/eng-rules.md
48
50
  3. rules-on-demand/engineering/eng.{comando}-rules.md
49
- 4. rules/product/prod-rules.md (mais baixa)
51
+ 4. rules-on-demand/product/prod.guided-rules.md (só com PROD_MODE=developer-guided)
52
+ 5. rules/product/prod-rules.md (mais baixa)
50
53
  ```
51
54
 
52
55
  **Regra**: Regras mais especificas tem precedencia sobre regras gerais.
@@ -18,8 +18,8 @@ Aplicável sempre que um robô novo for implementado, um bug estrutural for reso
18
18
 
19
19
  #### Localização
20
20
 
21
- - Sempre criar em `docs/engineering/robots/{robot-tag}-robot.md`
22
- - Exemplos: `docs/engineering/robots/consulta-orgao-robot.md`, `docs/engineering/robots/extrator-dados-robot.md`
21
+ - Sempre criar em `$ENG_DOCS/robots/{robot-tag}-robot.md`
22
+ - Exemplos: `$ENG_DOCS/robots/consulta-orgao-robot.md`, `$ENG_DOCS/robots/extrator-dados-robot.md`
23
23
 
24
24
  #### Conteúdo obrigatório
25
25
 
@@ -44,7 +44,7 @@ Todo arquivo `{robot-tag}-robot.md` deve conter:
44
44
 
45
45
  ### Proibido
46
46
 
47
- - ❌ Criar em `docs/robots/` — pasta incorreta, usar `docs/engineering/robots/`
47
+ - ❌ Criar em `docs/robots/` — pasta incorreta, usar `$ENG_DOCS/robots/`
48
48
  - ❌ Omitir a seção de **Limitações Conhecidas** — é fundamental para evitar que outros devs tentem implementar algo impossível
49
49
  - ❌ Omitir o **Histórico de Bugs** — descobertas custam tempo; documentar evita retrabalho
50
50
 
@@ -61,4 +61,4 @@ Nenhuma — todo robô deve ter sua documentação, independente do tamanho ou c
61
61
 
62
62
  ## Referências
63
63
 
64
- - `docs/engineering/robots/{robot-tag}-robot.md` — referência de implementação completa
64
+ - `$ENG_DOCS/robots/{robot-tag}-robot.md` — referência de implementação completa
@@ -9,7 +9,8 @@ O Jarvis não assume Jira, GitLab ou Slack. Leia `$IDE/ENV.md` e despache pelo a
9
9
  | Variável | Vendors | Adapter |
10
10
  |----------|---------|---------|
11
11
  | `TASK_MANAGER` | jira, linear, github, asana | `jarvis tasks comment` |
12
- | `VERSION_CONTROL` | gitlab, github, bitbucket | `jarvis vcs create-merge` / `create-issue` / `fetch-raw` |
12
+ | `VERSION_CONTROL` | gitlab, github, bitbucket | `jarvis vcs create-merge` / `create-issue` |
13
+ | `CENTRAL_DOCS_STORAGE` | filesystem (s3 e gdrive no futuro) | `jarvis docs fetch` / `sync` / `publish` |
13
14
  | `MESSAGE_COMUNICATOR` | slack, discord, teams | runtime `communicator/` |
14
15
 
15
16
  `{TASK_MANAGER_KEY}` = id da tarefa. Com board, é o id do card. Sem board, é o número de controle do usuário.
@@ -36,7 +37,7 @@ Com board definido, se o key não veio nos argumentos, perguntar o id do card na
36
37
 
37
38
  Só se `TASK_MANAGER` estiver preenchido com um vendor. Freelance → pular. Falha no comentário não bloqueia o workflow.
38
39
 
39
- Comentar **somente** em eventos relevantes: (a) **desvio do plano** (mudança de direção, fase bloqueada), (b) **auditoria do Advisor** (as questões do `architecture.md` com as opções junto da escolha), (c) **bloqueio** com valor de rastreabilidade (ex.: pré-PR reprovado), (d) **link do MR**, uma única vez. **Nunca** comentar início ou fim de etapa ("Iniciando…", "concluído…"): o acompanhamento fica no fluxo e no PR.
40
+ Comentar **somente** em eventos relevantes: (a) **desvio do plano** (mudança de direção, fase bloqueada), (b) **auditoria do Advisor** (as questões do `architecture.md` com as opções junto da escolha), (c) **bloqueio** com valor de rastreabilidade, (d) **link do MR**, uma única vez, (e) o **`gate-checklist.md` do `eng.pre-pr`**, só se o usuário responder Sim. **Nunca** comentar início ou fim de etapa ("Iniciando…", "concluído…"): o acompanhamento fica no fluxo e no PR.
40
41
 
41
42
  ```
42
43
  /eng-global-task-comment {TASK_MANAGER_KEY} {mensagem}
@@ -188,7 +188,7 @@ Quando o robô interage com interface visual (browser headless), usar a especial
188
188
 
189
189
  Seguir `$IDE/rules/engineering/eng.docs-scraping-rules.md` sem exceções.
190
190
 
191
- Criar ou atualizar `docs/engineering/robots/{robot-tag}-robot.md` com:
191
+ Criar ou atualizar `$ENG_DOCS/robots/{robot-tag}-robot.md` com:
192
192
  - Visão Geral (o que coleta e o que **não** coleta)
193
193
  - Fluxo de Execução (sequência de passos com URLs)
194
194
  - Fontes de Dados e Campos Extraídos
@@ -22,7 +22,6 @@ IMPORTANTE: Para que você funcione como o previsto e tenha todas as informaçõ
22
22
  - `PROD_FLOWS`
23
23
  - `PROD_TEMPLATES`
24
24
  - `PROD_DOCS`
25
- - `CENTRAL_DOCS_REPO`
26
25
  - **Se o ENV.md não existir ou estiver incompleto**, o agente deve:
27
26
  1. Interromper a execução do comando solicitado
28
27
  2. Informar ao usuário que não podemos avançar sem o ENV.md. Mensagem abaixo.
@@ -40,21 +39,20 @@ PROD_FOLDER_NAME=product
40
39
  PROD_RULES=$RULES_FOLDER/$PROD_FOLDER_NAME
41
40
  PROD_FLOWS=.$IDE/$FLOWS_FOLDER
42
41
  PROD_TEMPLATES=.$IDE/$TEMPLATES_FOLDER/$PROD_FOLDER_NAME
43
- PROD_DOCS=$DOCS_FOLDER/$PROD_FOLDER_NAME
44
- CENTRAL_DOCS_REPO=[USER:LOCAL_CENTRAL_DOCS_REPO]
42
+ PROD_DOCS=$DOCS_FOLDER/jarvis/$PROD_FOLDER_NAME
45
43
  ```
46
44
 
47
45
  Abaixo, segue uma descrição de algumas das variáveis mais importantes para as rotinas de produto:
48
46
 
49
47
  - $DOCS_FOLDER é o nome da pasta principal de documentações do projeto. Ela fica localizada na raiz do projeto.
50
- - $PROD_DOCS é o nome da pasta principal de documentações de produto do projeto.
48
+ - $PROD_DOCS é a pasta dos documentos de produto gerados pelos comandos (PRD, FRD, épicos, histórias, discovery): `$JARVIS_DOCS/product` (`docs/jarvis/product`), nos dois modos de trabalho e fora do versionamento do código. Os de engenharia ficam em `$ENG_DOCS` (`docs/jarvis/engineering`).
51
49
  - $PROD_FOLDER_NAME é o nome padrão da pasta de produto que será usada em diversos contextos do projeto.
52
50
  - $TEMPLATES_FOLDER é o nome da pasta principal de templates do projeto.
53
51
  - $PROD_TEMPLATES é o caminho completo para os templates de produto.
54
52
  - $FLOWS_FOLDER é o nome da pasta de workflows e comandos utilizados pelos IDEs. (tenha em mente que o nome da pasta pode variar de acordo com a IDE utilizada)
55
53
  - $RULES_FOLDER é o nome da pasta principal de regras invioláveis que os agentes devem seguir.
56
54
  - $PROD_RULES é o caminho para a pasta que contém as regras de produto. Leia todos os arquivos dentro dessa pasta para entender as regras de produto.
57
- - $CENTRAL_DOCS_REPO é o caminho para a pasta local que contém as especificações de produto centralizadas. Essa pasta é usada para manter um índice global de todas as especificações de produto.
55
+ - $CENTRAL_DOCS_STORAGE e $CENTRAL_DOCS_LOCATION configuram o Central docs: o registro do que **foi implementado** (um documento por funcionalidade e um por entrega, gerados pelo `/eng.spec-consolidate`), com um índice global. Ligado por padrão (`filesystem`); vazio = desligado. Os documentos de trabalho (PRD, FRD, ARD, RFC) não são publicados lá. Leitura e publicação sempre por `jarvis docs fetch | sync | publish`.
58
56
 
59
57
 
60
58
  ## VARIÁVEL `$IDE` - DETECÇÃO AUTOMÁTICA DA PASTA DA IDE
@@ -116,7 +114,8 @@ Em itens e especificações, utilizamos status para identificar quais etapas do
116
114
  - backlog: Item já foi estudado, sabemos o que fazer e está aguardando priorização de implementação.
117
115
  - in_progress: Item que está em desenvolvimento.
118
116
  - in_production: Item que está em produção.
119
- - cancelled: Item que foi descontinuado ou cancelado.
117
+ - cancelled: Item cancelado antes de ser entregue.
118
+ - deprecated: Item que esteve em produção e foi descontinuado.
120
119
 
121
120
 
122
121
  ## Padrão de nomes e pastas