@pathbit/agentdevkit 0.0.0-stage → 0.0.6

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 (242) hide show
  1. package/.pbai4devs/COMPATIBILIDADE.md +109 -0
  2. package/.pbai4devs/INSTRUCTIONS.md +87 -0
  3. package/.pbai4devs/README.md +53 -0
  4. package/.pbai4devs/agents/README.md +18 -0
  5. package/.pbai4devs/agents/revisor/AGENT.md +16 -0
  6. package/.pbai4devs/agents/revisor/antigravity.yaml +8 -0
  7. package/.pbai4devs/agents/revisor/claude.yaml +5 -0
  8. package/.pbai4devs/agents/revisor/cursor.yaml +5 -0
  9. package/.pbai4devs/agents/revisor/gemini.yaml +6 -0
  10. package/.pbai4devs/agents/revisor/github.yaml +5 -0
  11. package/.pbai4devs/agents/revisor/kimi-code.yaml +5 -0
  12. package/.pbai4devs/agents/revisor/mimocode.yaml +10 -0
  13. package/.pbai4devs/agents/revisor/opencode.yaml +7 -0
  14. package/.pbai4devs/agents/revisor/zcode.yaml +5 -0
  15. package/.pbai4devs/commands/README.md +30 -0
  16. package/.pbai4devs/commands/atualizar-ferramentas/COMMAND.md +17 -0
  17. package/.pbai4devs/commands/diagnosticar/COMMAND.md +18 -0
  18. package/.pbai4devs/commands/instalar-ferramentas/COMMAND.md +21 -0
  19. package/.pbai4devs/commands/preparar-dev/COMMAND.md +14 -0
  20. package/.pbai4devs/commands/proximos-passos/COMMAND.md +9 -0
  21. package/.pbai4devs/commands/revisar-entrega/COMMAND.md +29 -0
  22. package/.pbai4devs/commands/sincronizar/COMMAND.md +20 -0
  23. package/.pbai4devs/commands/status-roadmap/COMMAND.md +12 -0
  24. package/.pbai4devs/commands/testar-navegador/COMMAND.md +16 -0
  25. package/.pbai4devs/commands/verificar-agentes/COMMAND.md +19 -0
  26. package/.pbai4devs/estado/README.md +12 -0
  27. package/.pbai4devs/external-skills/CATALOG.json +121 -0
  28. package/.pbai4devs/external-skills/README.md +48 -0
  29. package/.pbai4devs/rules/README.md +18 -0
  30. package/.pbai4devs/rules/ambiente-local/RULE.md +33 -0
  31. package/.pbai4devs/rules/ambiente-local/cursor.yaml +4 -0
  32. package/.pbai4devs/rules/documentacao/RULE.md +11 -0
  33. package/.pbai4devs/rules/documentacao/cursor.yaml +4 -0
  34. package/.pbai4devs/rules/estrutura/RULE.md +33 -0
  35. package/.pbai4devs/rules/estrutura/cursor.yaml +4 -0
  36. package/.pbai4devs/rules/git/RULE.md +18 -0
  37. package/.pbai4devs/rules/git/cursor.yaml +4 -0
  38. package/.pbai4devs/rules/proximos-passos/RULE.md +14 -0
  39. package/.pbai4devs/rules/qualidade/RULE.md +32 -0
  40. package/.pbai4devs/rules/qualidade/cursor.yaml +4 -0
  41. package/.pbai4devs/scripts/README.md +69 -0
  42. package/.pbai4devs/scripts/diagnosticar-ambiente/SCRIPT.mjs +41 -0
  43. package/.pbai4devs/scripts/diagnosticar-ambiente/tests/TEST.mjs +30 -0
  44. package/.pbai4devs/scripts/executar-script/README.md +28 -0
  45. package/.pbai4devs/scripts/executar-script/SCRIPT.mjs +78 -0
  46. package/.pbai4devs/scripts/executar-script/tests/TEST.mjs +122 -0
  47. package/.pbai4devs/scripts/gerenciar-skills-externas/README.md +31 -0
  48. package/.pbai4devs/scripts/gerenciar-skills-externas/SCRIPT.mjs +182 -0
  49. package/.pbai4devs/scripts/gerenciar-skills-externas/tests/TEST.mjs +134 -0
  50. package/.pbai4devs/scripts/inspecionar-projeto/SCRIPT.mjs +190 -0
  51. package/.pbai4devs/scripts/inspecionar-projeto/tests/TEST.mjs +161 -0
  52. package/.pbai4devs/scripts/lib/README.md +9 -0
  53. package/.pbai4devs/scripts/lib/adapters.mjs +135 -0
  54. package/.pbai4devs/scripts/lib/catalog.mjs +170 -0
  55. package/.pbai4devs/scripts/lib/gitignore.mjs +348 -0
  56. package/.pbai4devs/scripts/lib/npm.mjs +17 -0
  57. package/.pbai4devs/scripts/lib/resource-path.mjs +20 -0
  58. package/.pbai4devs/scripts/lib/runtime.mjs +28 -0
  59. package/.pbai4devs/scripts/lib/script-config.mjs +8 -0
  60. package/.pbai4devs/scripts/preparar-ferramentas/BOOTSTRAP.md +34 -0
  61. package/.pbai4devs/scripts/preparar-ferramentas/BOOTSTRAP.mjs +189 -0
  62. package/.pbai4devs/scripts/preparar-ferramentas/README.md +71 -0
  63. package/.pbai4devs/scripts/preparar-ferramentas/SCRIPT.mjs +455 -0
  64. package/.pbai4devs/scripts/preparar-ferramentas/tests/FIXTURE.html +20 -0
  65. package/.pbai4devs/scripts/preparar-ferramentas/tests/SMOKE_BROWSER.mjs +55 -0
  66. package/.pbai4devs/scripts/preparar-ferramentas/tests/SMOKE_DESKTOP.mjs +61 -0
  67. package/.pbai4devs/scripts/preparar-ferramentas/tests/SMOKE_VERCEL.mjs +192 -0
  68. package/.pbai4devs/scripts/preparar-ferramentas/tests/TEST.mjs +552 -0
  69. package/.pbai4devs/scripts/preparar-ferramentas/tests/bootstrap.test.mjs +94 -0
  70. package/.pbai4devs/scripts/preparar-ferramentas/tests/fixtures-vercel/just-bash/input.txt +2 -0
  71. package/.pbai4devs/scripts/preparar-ferramentas/tests/fixtures-vercel/just-bash-overlay/input.txt +1 -0
  72. package/.pbai4devs/scripts/preparar-ferramentas/tests/fixtures-vercel/skills/agentdevkit-smoke/SKILL.md +8 -0
  73. package/.pbai4devs/scripts/preparar-ferramentas/tests/fixtures-vercel/webreel/index.html +6 -0
  74. package/.pbai4devs/scripts/preparar-ferramentas/tests/fixtures-vercel/webreel/webreel.config.json +10 -0
  75. package/.pbai4devs/scripts/revisar-entrega/README.md +36 -0
  76. package/.pbai4devs/scripts/revisar-entrega/SCRIPT.mjs +249 -0
  77. package/.pbai4devs/scripts/revisar-entrega/tests/TEST.mjs +249 -0
  78. package/.pbai4devs/scripts/sincronizar-agentes/README.md +41 -0
  79. package/.pbai4devs/scripts/sincronizar-agentes/SCRIPT.mjs +473 -0
  80. package/.pbai4devs/scripts/sincronizar-agentes/tests/LOCAL_IGNORE.mjs +290 -0
  81. package/.pbai4devs/scripts/sincronizar-agentes/tests/TEST.mjs +869 -0
  82. package/.pbai4devs/scripts/testar-infraestrutura/SCRIPT.mjs +39 -0
  83. package/.pbai4devs/scripts/testar-infraestrutura/tests/TEST.mjs +29 -0
  84. package/.pbai4devs/scripts/testar-maturidade/README.md +54 -0
  85. package/.pbai4devs/scripts/testar-maturidade/SCRIPT.mjs +477 -0
  86. package/.pbai4devs/scripts/testar-maturidade/assets/calculator.mjs +1 -0
  87. package/.pbai4devs/scripts/testar-maturidade/assets/calculator.test.mjs.template +8 -0
  88. package/.pbai4devs/scripts/testar-maturidade/assets/http-api.test.mjs.template +28 -0
  89. package/.pbai4devs/scripts/testar-maturidade/assets/server.mjs +34 -0
  90. package/.pbai4devs/scripts/testar-maturidade/tests/TEST.mjs +305 -0
  91. package/.pbai4devs/scripts/testar-navegador/REFERENCE.md +49 -0
  92. package/.pbai4devs/scripts/testar-navegador/SCRIPT.conf +1 -0
  93. package/.pbai4devs/scripts/testar-navegador/SCRIPT.js +64 -0
  94. package/.pbai4devs/scripts/testar-navegador/tests/FAIL.js +8 -0
  95. package/.pbai4devs/scripts/testar-navegador/tests/SUCCESS.js +13 -0
  96. package/.pbai4devs/scripts/verificar-recursos/README.md +25 -0
  97. package/.pbai4devs/scripts/verificar-recursos/SCRIPT.mjs +209 -0
  98. package/.pbai4devs/scripts/verificar-recursos/tests/TEST.mjs +310 -0
  99. package/.pbai4devs/scripts/verificar-recursos/tests/ambiente-local.mjs +80 -0
  100. package/.pbai4devs/skills/README.md +24 -0
  101. package/.pbai4devs/skills/git/git-branch/SKILL.md +11 -0
  102. package/.pbai4devs/skills/git/git-commit/SKILL.md +16 -0
  103. package/.pbai4devs/skills/git/git-diff/SKILL.md +9 -0
  104. package/.pbai4devs/skills/git/git-fetch/SKILL.md +10 -0
  105. package/.pbai4devs/skills/git/git-log/SKILL.md +9 -0
  106. package/.pbai4devs/skills/git/git-merge/SKILL.md +13 -0
  107. package/.pbai4devs/skills/git/git-pull/SKILL.md +12 -0
  108. package/.pbai4devs/skills/git/git-push/SKILL.md +12 -0
  109. package/.pbai4devs/skills/git/git-status/SKILL.md +9 -0
  110. package/.pbai4devs/skills/git/git-worktree/SKILL.md +10 -0
  111. package/.pbai4devs/skills/produto/criar-design-doc/SKILL.md +37 -0
  112. package/.pbai4devs/skills/produto/criar-prd/SKILL.md +35 -0
  113. package/.pbai4devs/skills/produto/criar-ux-ui/SKILL.md +34 -0
  114. package/.pbai4devs/skills/projeto/atualizar-documentacao/SKILL.md +26 -0
  115. package/.pbai4devs/skills/projeto/configurar-projeto/SKILL.md +62 -0
  116. package/.pbai4devs/skills/projeto/criar-agente/SKILL.md +20 -0
  117. package/.pbai4devs/skills/projeto/criar-comando/SKILL.md +25 -0
  118. package/.pbai4devs/skills/projeto/criar-recurso/SKILL.md +41 -0
  119. package/.pbai4devs/skills/projeto/criar-regra/SKILL.md +19 -0
  120. package/.pbai4devs/skills/projeto/criar-skill/SKILL.md +22 -0
  121. package/.pbai4devs/skills/projeto/executar-script/SKILL.md +29 -0
  122. package/.pbai4devs/skills/projeto/preparar-dev/SKILL.md +45 -0
  123. package/.pbai4devs/skills/projeto/preparar-ferramentas/SKILL.md +56 -0
  124. package/.pbai4devs/skills/projeto/proximos-passos/SKILL.md +23 -0
  125. package/.pbai4devs/skills/projeto/sincronizar-agentes/SKILL.md +42 -0
  126. package/.pbai4devs/skills/projeto/usar-skills-externas/SKILL.md +28 -0
  127. package/.pbai4devs/skills/projeto/verificar-trabalho/SKILL.md +45 -0
  128. package/.pbai4devs/skills/specs/criar-spec/SKILL.md +33 -0
  129. package/.pbai4devs/skills/specs/gerenciar-roadmap/SKILL.md +26 -0
  130. package/.pbai4devs/skills/specs/implementar-spec/SKILL.md +48 -0
  131. package/.pbai4devs/templates/README.md +65 -0
  132. package/.pbai4devs/templates/adr/TEMPLATE.md +21 -0
  133. package/.pbai4devs/templates/ambiente-local/Makefile.template +48 -0
  134. package/.pbai4devs/templates/ambiente-local/TEMPLATE.md +48 -0
  135. package/.pbai4devs/templates/ambiente-local/compose.local.yml.template +16 -0
  136. package/.pbai4devs/templates/ambiente-local/dockerignore.template +28 -0
  137. package/.pbai4devs/templates/changelog/TEMPLATE.md +17 -0
  138. package/.pbai4devs/templates/claude/TEMPLATE.md +8 -0
  139. package/.pbai4devs/templates/clients/ADAPTERS.json +306 -0
  140. package/.pbai4devs/templates/clients/TEMPLATE.md +12 -0
  141. package/.pbai4devs/templates/comando/TEMPLATE.md +15 -0
  142. package/.pbai4devs/templates/configuracao/TEMPLATE.md +46 -0
  143. package/.pbai4devs/templates/configuracao-raiz/TEMPLATE.md +22 -0
  144. package/.pbai4devs/templates/configuracao-raiz/editorconfig.template +21 -0
  145. package/.pbai4devs/templates/configuracao-raiz/env.example.template +3 -0
  146. package/.pbai4devs/templates/configuracao-raiz/gitattributes.template +6 -0
  147. package/.pbai4devs/templates/contribuicao/TEMPLATE.md +23 -0
  148. package/.pbai4devs/templates/design/TEMPLATE.md +30 -0
  149. package/.pbai4devs/templates/entrypoints/AGENTS.md +19 -0
  150. package/.pbai4devs/templates/entrypoints/CLAUDE.md +7 -0
  151. package/.pbai4devs/templates/entrypoints/GEMINI.md +7 -0
  152. package/.pbai4devs/templates/entrypoints/TEMPLATE.md +12 -0
  153. package/.pbai4devs/templates/gitignore/TEMPLATE.md +42 -0
  154. package/.pbai4devs/templates/gitignore/agents.ignore +45 -0
  155. package/.pbai4devs/templates/gitignore/clients.json +20 -0
  156. package/.pbai4devs/templates/gitignore/local.ignore +10 -0
  157. package/.pbai4devs/templates/gitignore/shared.ignore +60 -0
  158. package/.pbai4devs/templates/instrucoes-agentes/TEMPLATE.md +25 -0
  159. package/.pbai4devs/templates/plano-implementacao/TEMPLATE.md +27 -0
  160. package/.pbai4devs/templates/prd/TEMPLATE.md +30 -0
  161. package/.pbai4devs/templates/readme-pasta/TEMPLATE.md +16 -0
  162. package/.pbai4devs/templates/readme-projeto/TEMPLATE.md +34 -0
  163. package/.pbai4devs/templates/release/TEMPLATE.md +23 -0
  164. package/.pbai4devs/templates/revisao-entrega/TEMPLATE.md +41 -0
  165. package/.pbai4devs/templates/roadmap/TEMPLATE.md +18 -0
  166. package/.pbai4devs/templates/seguranca/TEMPLATE.md +22 -0
  167. package/.pbai4devs/templates/spec/TEMPLATE.md +61 -0
  168. package/.pbai4devs/templates/tasks/TEMPLATE.md +20 -0
  169. package/.pbai4devs/templates/ux-ui/TEMPLATE.md +26 -0
  170. package/.pbai4devs/tools/README.md +87 -0
  171. package/.pbai4devs/tools/agent-browser/README.md +26 -0
  172. package/.pbai4devs/tools/agent-browser/SCRIPT.mjs +5 -0
  173. package/.pbai4devs/tools/agent-browser/TOOL.json +33 -0
  174. package/.pbai4devs/tools/agent-desktop/README.md +26 -0
  175. package/.pbai4devs/tools/agent-desktop/SCRIPT.mjs +5 -0
  176. package/.pbai4devs/tools/agent-desktop/TOOL.json +30 -0
  177. package/.pbai4devs/tools/just-bash/README.md +23 -0
  178. package/.pbai4devs/tools/just-bash/SCRIPT.mjs +5 -0
  179. package/.pbai4devs/tools/just-bash/TOOL.json +27 -0
  180. package/.pbai4devs/tools/markdownlint-cli2/README.md +18 -0
  181. package/.pbai4devs/tools/markdownlint-cli2/SCRIPT.mjs +5 -0
  182. package/.pbai4devs/tools/markdownlint-cli2/TOOL.json +28 -0
  183. package/.pbai4devs/tools/opensrc/README.md +25 -0
  184. package/.pbai4devs/tools/opensrc/SCRIPT.mjs +5 -0
  185. package/.pbai4devs/tools/opensrc/TOOL.json +29 -0
  186. package/.pbai4devs/tools/portless/README.md +21 -0
  187. package/.pbai4devs/tools/portless/SCRIPT.mjs +5 -0
  188. package/.pbai4devs/tools/portless/TOOL.json +30 -0
  189. package/.pbai4devs/tools/skills/README.md +24 -0
  190. package/.pbai4devs/tools/skills/SCRIPT.mjs +5 -0
  191. package/.pbai4devs/tools/skills/TOOL.json +33 -0
  192. package/.pbai4devs/tools/webreel/README.md +23 -0
  193. package/.pbai4devs/tools/webreel/SCRIPT.mjs +5 -0
  194. package/.pbai4devs/tools/webreel/TOOL.json +25 -0
  195. package/CHANGELOG.md +65 -0
  196. package/CONTRIBUTING.md +73 -0
  197. package/LICENSE +21 -0
  198. package/README.en.md +89 -0
  199. package/README.es.md +91 -0
  200. package/README.md +94 -2
  201. package/bin/README.md +8 -0
  202. package/bin/agentdevkit.mjs +35 -0
  203. package/docs/README.md +81 -0
  204. package/docs/guides/README.md +14 -0
  205. package/docs/guides/cli.md +81 -0
  206. package/docs/guides/installation.md +86 -0
  207. package/docs/guides/resources.md +121 -0
  208. package/docs/guides/tools-and-testing.md +85 -0
  209. package/docs/guides/troubleshooting.md +151 -0
  210. package/docs/guides/workflow.md +130 -0
  211. package/docs/maintainers/README.md +12 -0
  212. package/docs/maintainers/community.md +49 -0
  213. package/docs/maintainers/development.md +54 -0
  214. package/docs/maintainers/maintenance.md +104 -0
  215. package/docs/maintainers/packages.md +87 -0
  216. package/docs/product/README.md +12 -0
  217. package/docs/product/design.md +104 -0
  218. package/docs/product/prd.md +70 -0
  219. package/docs/product/roadmap.md +38 -0
  220. package/docs/product/ux-ui.md +42 -0
  221. package/docs/specs/README.md +21 -0
  222. package/docs/specs/github-packages/SPEC.md +45 -0
  223. package/docs/specs/v0.0.1/README.md +10 -0
  224. package/docs/specs/v0.0.1/SPEC.md +57 -0
  225. package/docs/specs/v0.0.2/README.md +10 -0
  226. package/docs/specs/v0.0.2/SPEC.md +67 -0
  227. package/docs/specs/v0.0.3/README.md +10 -0
  228. package/docs/specs/v0.0.3/SPEC.md +64 -0
  229. package/docs/specs/v0.0.4/README.md +10 -0
  230. package/docs/specs/v0.0.4/SPEC.md +53 -0
  231. package/docs/specs/v0.0.5/README.md +9 -0
  232. package/docs/specs/v0.0.5/SPEC.md +42 -0
  233. package/docs/specs/v0.0.6/README.md +10 -0
  234. package/docs/specs/v0.0.6/SPEC.md +77 -0
  235. package/package.json +67 -4
  236. package/src/README.md +16 -0
  237. package/src/adapters/index.mjs +1 -0
  238. package/src/cli.mjs +162 -0
  239. package/src/config.mjs +14 -0
  240. package/src/i18n.mjs +42 -0
  241. package/src/install.mjs +373 -0
  242. package/src/next.mjs +79 -0
@@ -0,0 +1,94 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { createHash } from 'node:crypto';
4
+ import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
5
+ import { dirname, join, resolve } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { spawnSync } from 'node:child_process';
8
+ import { ensureRuntime, main, validateArchive, validateExtracted } from '../BOOTSTRAP.mjs';
9
+ import { findExecutable } from '../../lib/runtime.mjs';
10
+
11
+ const project = resolve(dirname(fileURLToPath(import.meta.url)), '../../../..');
12
+ const tar = findExecutable('tar');
13
+ function fixture(t) {
14
+ assert.ok(tar, 'Native tar is required for archive integration tests.');
15
+ mkdirSync(join(project, '.agent-tests'), { recursive: true });
16
+ const root = mkdtempSync(join(project, '.agent-tests/bootstrap-node-'));
17
+ t.after(() => rmSync(root, { recursive: true, force: true }));
18
+ const platform = process.platform, arch = process.arch;
19
+ const key = platform === 'win32' ? `win-${arch}` : `${platform}-${arch}`;
20
+ const top = `node-v24.1.2-${key}`, extension = platform === 'win32' ? 'zip' : 'tar.gz';
21
+ const bundle = join(root, top); mkdirSync(bundle);
22
+ const write = (path, content) => { mkdirSync(dirname(path), { recursive: true }); writeFileSync(path, content); };
23
+ write(join(bundle, platform === 'win32' ? 'node.exe' : 'bin/node'), 'fixture-node');
24
+ write(join(bundle, platform === 'win32' ? 'node_modules/npm/bin/npm-cli.js' : 'lib/node_modules/npm/bin/npm-cli.js'), 'fixture-npm');
25
+ const archive = join(root, `${top}.${extension}`);
26
+ const packed = spawnSync(tar, [platform === 'win32' ? '-acf' : '-czf', archive, '-C', root, top], { encoding: 'utf8' });
27
+ assert.equal(packed.status, 0, packed.stderr);
28
+ const checksum = createHash('sha256').update(readFileSync(archive)).digest('hex');
29
+ let manifest = `${checksum} ${top}.${extension}\n`, calls = 0;
30
+ const run = (file, args, options) => {
31
+ if (args[0] === '-p') {
32
+ if (!existsSync(file) || readFileSync(file, 'utf8') !== 'fixture-node') return { status: 1, stdout: '' };
33
+ return { status: 0, stdout: JSON.stringify({ version: '24.1.2', node: file, bun: false }) };
34
+ }
35
+ if (args[1] === '--version') return { status: existsSync(args[0]) && readFileSync(args[0], 'utf8') === 'fixture-npm' ? 0 : 1, stdout: '11.0.0\n' };
36
+ return spawnSync(file, args, options);
37
+ };
38
+ const options = { root, node: join(root, 'missing-node'), platform, arch, libc: 'glibc', run,
39
+ download: async (url, destination) => { calls++; if (url.endsWith('SHASUMS256.txt')) writeFileSync(destination, manifest); else copyFileSync(archive, destination); } };
40
+ return { root, options, archive, key, cache: join(root, '.pbai4devs/tools/.cache/runtime/node', key), setManifest: value => { manifest = value; }, calls: () => calls };
41
+ }
42
+
43
+ test('bootstrap Node extrai arquivo real, verifica npm e reutiliza cache sem rede', async t => {
44
+ const f = fixture(t), runtime = await ensureRuntime(f.options);
45
+ assert.equal(runtime.source, 'download'); assert.equal(runtime.version, '24.1.2'); assert.equal(f.calls(), 2);
46
+ assert.ok(existsSync(runtime.node)); assert.ok(existsSync(runtime.npm));
47
+ const cached = await ensureRuntime({ ...f.options, download: async () => { throw new Error('Network must not run'); } });
48
+ assert.equal(cached.source, 'cache'); assert.equal(cached.node, runtime.node);
49
+ assert.ok(readdirSync(f.cache).every(name => !name.startsWith('.stage.')));
50
+ });
51
+
52
+ test('checksum, manifesto ambíguo e download falho não promovem runtime', async t => {
53
+ for (const kind of ['checksum', 'manifest', 'download']) await t.test(kind, async subtest => {
54
+ const f = fixture(subtest), previous = join(f.cache, 'v24.1.2'); mkdirSync(previous, { recursive: true }); writeFileSync(join(previous, 'preserved'), 'original');
55
+ if (kind === 'checksum') f.setManifest(`${'0'.repeat(64)} node-v24.1.2-${f.key}.${process.platform === 'win32' ? 'zip' : 'tar.gz'}\n`);
56
+ if (kind === 'manifest') f.setManifest('no matching official archive\n');
57
+ const options = kind === 'download' ? { ...f.options, download: async () => { throw new Error('offline'); } } : f.options;
58
+ await assert.rejects(ensureRuntime(options), /SHA-256|indisponivel|offline/);
59
+ assert.equal(readFileSync(join(previous, 'preserved'), 'utf8'), 'original');
60
+ assert.ok(readdirSync(f.cache).every(name => !name.startsWith('.stage.')));
61
+ });
62
+ });
63
+
64
+ test('validação recusa nomes fora da distribuição antes da extração', () => {
65
+ const top = 'node-v24.1.2-linux-x64';
66
+ assert.doesNotThrow(() => validateArchive(`${top}/bin/npm\n`, top));
67
+ for (const name of ['../../outside', `${top}/../outside`, '/absolute/file', `${top}/C:file`]) assert.throws(() => validateArchive(`${name}\n`, top), /fora/);
68
+ });
69
+
70
+ test('links internos extraídos são aceitos e links externos são recusados', t => {
71
+ const f = fixture(t), inside = join(f.root, 'inside'), outside = join(f.root, 'outside'); mkdirSync(inside); mkdirSync(outside); writeFileSync(join(inside, 'file'), 'safe');
72
+ try { symlinkSync('file', join(inside, 'link')); } catch (error) { if (['EPERM', 'EACCES'].includes(error.code)) { t.skip('No symlink privilege'); return; } throw error; }
73
+ assert.doesNotThrow(() => validateExtracted(inside));
74
+ symlinkSync(outside, join(inside, 'escape'), 'dir');
75
+ assert.throws(() => validateExtracted(inside), /fora/);
76
+ });
77
+
78
+ test('cache simbólico é recusado sem tocar alvo externo', async t => {
79
+ const f = fixture(t), outside = join(f.root, 'outside'); mkdirSync(outside);
80
+ const cacheRoot = join(f.root, '.pbai4devs/tools/.cache'); mkdirSync(dirname(cacheRoot), { recursive: true });
81
+ try { symlinkSync(outside, cacheRoot, 'dir'); } catch (error) { if (['EPERM', 'EACCES'].includes(error.code)) { t.skip('No symlink privilege'); return; } throw error; }
82
+ await assert.rejects(ensureRuntime(f.options), /link simbolico/); assert.deepEqual(readdirSync(outside), []);
83
+ });
84
+
85
+ test('opções inválidas falham antes de rede ou criação de cache', async () => {
86
+ await assert.rejects(main(['--runtime-only', 'all']), /nao aceita/);
87
+ await assert.rejects(main(['--unknown']), /runtime-only/);
88
+ });
89
+
90
+ test('runtime atual saudável não altera PATH nem grava cache', async t => {
91
+ const f = fixture(t), original = process.env.PATH;
92
+ const result = await ensureRuntime({ root: f.root, minimum: Number(process.versions.node.split('.')[0]) });
93
+ assert.equal(result.source, 'current'); assert.equal(process.env.PATH, original); assert.equal(existsSync(f.cache), false);
94
+ });
@@ -0,0 +1,2 @@
1
+ agentdevkit example
2
+ outra linha
@@ -0,0 +1 @@
1
+ Fixture para escrita somente em memória.
@@ -0,0 +1,8 @@
1
+ ---
2
+ name: agentdevkit-smoke
3
+ description: Fixture local para verificar descoberta sem instalar skills.
4
+ ---
5
+
6
+ # AgentDevKit smoke local
7
+
8
+ Use apenas para testar a descoberta local da CLI skills.
@@ -0,0 +1,6 @@
1
+ <!doctype html>
2
+ <html lang="pt-BR">
3
+ <meta charset="utf-8">
4
+ <title>AgentDevKit smoke local</title>
5
+ <h1>Fixture local de roteiro</h1>
6
+ </html>
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "https://webreel.dev/schema/v1.json",
3
+ "videos": {
4
+ "agentdevkit-smoke": {
5
+ "url": "./index.html",
6
+ "viewport": { "width": 640, "height": 480 },
7
+ "steps": [{ "action": "pause", "ms": 10 }]
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,36 @@
1
+ ---
2
+ description: Verificação dos documentos, da revisão e das evidências da entrega.
3
+ ---
4
+
5
+ # Revisar entrega
6
+
7
+ Após implementar, atualizar a documentação afetada e testar, gere o registro:
8
+
9
+ ```sh
10
+ node .pbai4devs/scripts/revisar-entrega/SCRIPT.mjs prepare --id minha-mudanca
11
+ ```
12
+
13
+ Use `--spec caminho/SPEC.md` para extrair os critérios `AC-001` da spec.
14
+ `--scope arquivo` limita uma revisão; sem ele, o snapshot reúne alterações Git.
15
+ Use `--document arquivo` para incluir PRD, design e outros documentos.
16
+ README, AGENTS, CLAUDE e GEMINI versionáveis na raiz entram automaticamente na
17
+ revisão, mesmo sem alterações. Saídas locais ignoradas fora do índice não são
18
+ documentos obrigatórios; documentos rastreados continuam cobertos mesmo quando
19
+ uma regra de ignore corresponde ao nome.
20
+ Preencha decisões, resultados e evidências no JSON retornado; peça revisão
21
+ independente, execute `seal <caminho/revisao.json>` e depois
22
+ `check <caminho/revisao.json>` antes de concluir.
23
+
24
+ O check recusa resultados pendentes, evidências ausentes/versionadas e arquivos
25
+ alterados depois da revisão. Relatórios ficam em
26
+ `.agent-tests/<id>/<execução>/`.
27
+ O preparo preserva o escopo em `baseline.json`; o selo registra hashes do
28
+ relatório e das provas em `seal.json`. Preserve esses metadados no fluxo.
29
+ Mudanças posteriores exigem nova execução, preservando as evidências anteriores.
30
+ Uma deleção staged com arquivo ainda presente e ignorado é registrada como
31
+ `indexAction: untrack`. O snapshot preserva seu hash físico; alterações no
32
+ arquivo, no ignore ou nessa ação do índice invalidam a revisão. Diferenças
33
+ entre índice e conteúdo testado continuam bloqueadas, inclusive com `--scope`.
34
+ O script confere o registro; a revisão semântica continua sendo responsabilidade
35
+ do agente revisor. Veja a skill
36
+ [atualizar-documentacao](../../skills/projeto/atualizar-documentacao/SKILL.md).
@@ -0,0 +1,249 @@
1
+ #!/usr/bin/env node
2
+ import { createHash } from 'node:crypto';
3
+ import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
4
+ import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
5
+ import { spawnSync } from 'node:child_process';
6
+ import { isMain, projectRoot, requireNode } from '../lib/runtime.mjs';
7
+
8
+ const fail = message => { throw new Error(message); };
9
+ const slug = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
10
+ const portable = value => value.split(sep).join('/');
11
+ const nonempty = value => typeof value === 'string' && value.trim().length > 0;
12
+ const completed = value => nonempty(value) && !/^(?:a definir|pendente|pending|todo|tbd|\.\.\.)[.!]?$/i.test(value.trim());
13
+ const digest = file => createHash('sha256').update(readFileSync(file)).digest('hex');
14
+
15
+ function pathInRoot(root, input) {
16
+ if (!nonempty(input) || /[\0\r\n]/.test(input)) fail('Caminho invalido.');
17
+ const name = portable(relative(root, resolve(root, input)));
18
+ if (!name || name === '..' || name.startsWith('../') || isAbsolute(name) || name.split('/').some(part => part.toLowerCase() === '.git')) fail(`Caminho fora do escopo: ${input}`);
19
+ let current = root;
20
+ for (const part of name.split('/')) {
21
+ current = join(current, part);
22
+ try { if (lstatSync(current).isSymbolicLink()) fail(`Link simbolico nao permitido: ${name}`); }
23
+ catch (error) { if (error.code !== 'ENOENT') throw error; }
24
+ }
25
+ return name;
26
+ }
27
+
28
+ function git(root, args, allowFailure = false) {
29
+ const result = spawnSync('git', args, { cwd: root, encoding: 'utf8', timeout: 15000, windowsHide: true });
30
+ if ((result.error || result.status !== 0) && !allowFailure) fail(`Git: ${result.error?.message || result.stderr?.trim() || 'operacao falhou'}`);
31
+ return result;
32
+ }
33
+
34
+ function repository(root) {
35
+ const canonical = realpathSync(root);
36
+ const top = git(canonical, ['rev-parse', '--show-toplevel']).stdout.trim();
37
+ if (realpathSync(top) !== canonical) fail('Use a raiz do repositorio.');
38
+ return canonical;
39
+ }
40
+
41
+ function head(root) {
42
+ const result = git(root, ['rev-parse', '--verify', 'HEAD'], true);
43
+ return result.status === 0 ? result.stdout.trim() : null;
44
+ }
45
+
46
+ function ignored(root, file) {
47
+ return git(root, ['check-ignore', '--no-index', '-q', '--', file], true).status === 0;
48
+ }
49
+
50
+ function tracked(root, file) {
51
+ return git(root, ['--literal-pathspecs', 'ls-files', '--error-unmatch', '--', file], true).status === 0;
52
+ }
53
+
54
+ function ignoredPaths(root, files) {
55
+ if (!files.length) return new Set();
56
+ const result = spawnSync('git', ['check-ignore', '--no-index', '--stdin', '-z'], { cwd: root, input: files.join('\0') + '\0', encoding: 'utf8', timeout: 15000, windowsHide: true });
57
+ if (result.error || ![0, 1].includes(result.status)) fail(`Git: ${result.error?.message || result.stderr?.trim() || 'falha ao conferir ignores'}`);
58
+ return new Set(result.stdout.split('\0').filter(Boolean));
59
+ }
60
+
61
+ function stagedDeletions(root) {
62
+ return new Set(git(root, ['diff', '--cached', '--diff-filter=D', '--no-renames', '--name-only', '-z', '--']).stdout.split('\0').filter(Boolean));
63
+ }
64
+
65
+ function changedFiles(root) {
66
+ const baseline = head(root);
67
+ const versioned = git(root, baseline ? ['diff', '--name-only', '-z', 'HEAD', '--'] : ['ls-files', '-z']).stdout;
68
+ const untracked = git(root, ['ls-files', '--others', '--exclude-standard', '-z']).stdout;
69
+ return [...new Set((versioned + '\0' + untracked).split('\0').filter(Boolean))].sort();
70
+ }
71
+
72
+ function snapshot(root, mode, paths, documents = [], spec = null) {
73
+ const names = mode === 'git' ? [...changedFiles(root), ...paths] : paths;
74
+ const selected = [...new Set(names.map(file => pathInRoot(root, file)))].sort();
75
+ const deleted = stagedDeletions(root), local = ignoredPaths(root, selected);
76
+ const files = selected.map(path => {
77
+ if (path.toLowerCase().startsWith('.agent-tests/')) fail('Artefatos nao fazem parte do escopo versionado.');
78
+ const absolute = join(root, path);
79
+ if (!existsSync(absolute)) return { path, sha256: null };
80
+ if (!lstatSync(absolute).isFile()) fail(`Escopo exige arquivos: ${path}`);
81
+ return { path, sha256: createHash('sha256').update(readFileSync(absolute)).digest('hex'), ...(deleted.has(path) && local.has(path) ? { indexAction: 'untrack' } : {}) };
82
+ });
83
+ if (!files.length) fail('Nenhuma alteracao para revisar; indique --scope para uma revisao especifica.');
84
+ return { mode, head: head(root), files, documents, spec };
85
+ }
86
+
87
+ export function specCriteria(text) {
88
+ const section = text.match(/^## (?:Crit[eé]rios de aceite|Acceptance criteria)\s*\r?\n([\s\S]*?)(?=^## |$(?![\s\S]))/im)?.[1];
89
+ const ids = [...new Set((section || '').match(/\bAC-\d{3,}\b/g) || [])];
90
+ if (!ids.length) fail('A spec deve definir criterios AC-001, AC-002... na secao Criterios de aceite.');
91
+ return ids;
92
+ }
93
+
94
+ function expectedDocuments(root, state, extra = []) {
95
+ const existing = ['README.md', 'AGENTS.md', 'CLAUDE.md', 'GEMINI.md', 'CONTRIBUTING.md', 'CHANGELOG.md', 'SECURITY.md'].filter(file => existsSync(join(root, file)));
96
+ const changed = state.files.filter(file => /\.mdc?$/i.test(file.path)).map(file => file.path);
97
+ const candidates = [...new Set([...existing, ...changed].map(file => pathInRoot(root, file)))];
98
+ const local = ignoredPaths(root, candidates);
99
+ const versioned = new Set(git(root, ['ls-files', '-z']).stdout.split('\0').filter(Boolean));
100
+ return [...new Set([...candidates.filter(file => !local.has(file) || versioned.has(file)), ...extra.map(file => pathInRoot(root, file))])].sort();
101
+ }
102
+
103
+ function checkIndex(root, state) {
104
+ const staged = new Set(git(root, ['diff', '--cached', '--name-only', '-z', '--']).stdout.split('\0').filter(Boolean));
105
+ const deleted = stagedDeletions(root);
106
+ const indexed = new Set(git(root, ['ls-files', '-z']).stdout.split('\0').filter(Boolean));
107
+ const local = ignoredPaths(root, state.files.filter(file => file.indexAction === 'untrack').map(file => file.path));
108
+ for (const file of state.files) {
109
+ if (file.indexAction === 'untrack') {
110
+ if (!staged.has(file.path) || !deleted.has(file.path) || indexed.has(file.path) || file.sha256 === null || !existsSync(join(root, file.path)) || !local.has(file.path)) fail(`Acao de desindexacao mudou: ${file.path}`);
111
+ continue;
112
+ }
113
+ if (file.indexAction !== undefined) fail(`Acao de indice invalida: ${file.path}`);
114
+ if (!staged.has(file.path)) continue;
115
+ const deletedButPresent = !indexed.has(file.path) && file.sha256 !== null;
116
+ const differs = git(root, ['--literal-pathspecs', 'diff', '--quiet', '--no-ext-diff', '--', file.path], true);
117
+ if (deletedButPresent || differs.error || differs.status !== 0) fail(`Indice Git difere do arquivo testado: ${file.path}`);
118
+ }
119
+ }
120
+
121
+ function artifact(root, file, prefix, mustExist) {
122
+ const path = pathInRoot(root, file);
123
+ if (!path.startsWith(prefix + '/')) fail(`Evidencia deve ficar em ${prefix}/: ${path}`);
124
+ if (!ignored(root, path) || tracked(root, path)) fail(`Artefato deve estar ignorado e fora do indice Git: ${path}`);
125
+ if (mustExist && (!existsSync(join(root, path)) || !lstatSync(join(root, path)).isFile() || lstatSync(join(root, path)).size === 0)) fail(`Evidencia ausente ou vazia: ${path}`);
126
+ return path;
127
+ }
128
+
129
+ export function prepareReview({ root = projectRoot, id, execution = new Date().toISOString().replace(/[^a-z0-9]/gi, '').toLowerCase(), spec = null, scope = [], documents = [], output } = {}) {
130
+ requireNode(); root = repository(root);
131
+ if (!slug.test(id || '') || !slug.test(execution)) fail('Use referencia e execucao como slugs em minusculas.');
132
+ const reference = spec ? pathInRoot(root, spec) : null;
133
+ if (reference && ignored(root, reference) && !tracked(root, reference)) fail('A spec deve ser um documento versionavel, fora dos artefatos ignorados.');
134
+ const criteria = reference ? specCriteria(readFileSync(join(root, reference), 'utf8')) : ['AC-001'];
135
+ let state = snapshot(root, scope.length ? 'scope' : 'git', [...scope, ...(reference ? [reference] : [])]);
136
+ const requiredDocs = expectedDocuments(root, state, documents);
137
+ const localDocuments = ignoredPaths(root, requiredDocs);
138
+ for (const file of requiredDocs) if (localDocuments.has(file) && !tracked(root, file)) fail(`Documento deve ser versionavel: ${file}`);
139
+ state = snapshot(root, scope.length ? 'scope' : 'git', [...state.files.map(file => file.path), ...requiredDocs], requiredDocs, reference);
140
+ const prefix = `.agent-tests/${id}/${execution}`;
141
+ const file = artifact(root, output || `${prefix}/revisao.json`, prefix, false);
142
+ const baselineFile = artifact(root, `${prefix}/baseline.json`, prefix, false);
143
+ if (existsSync(join(root, file)) || existsSync(join(root, baselineFile))) fail(`Relatorio ja existe; preserve-o e use outra execucao: ${file}`);
144
+ const baseline = { version: 1, reference: id, execution, spec: reference, snapshot: state, criteria };
145
+ mkdirSync(dirname(join(root, file)), { recursive: true });
146
+ writeFileSync(join(root, baselineFile), JSON.stringify(baseline, null, 2) + '\n', { flag: 'wx' });
147
+ const report = {
148
+ version: 1, reference: id, execution, spec: reference, snapshot: state,
149
+ baseline: { file: baselineFile, sha256: digest(join(root, baselineFile)) },
150
+ documentation: expectedDocuments(root, state, documents).map(path => ({ path, decision: 'pending', reason: '' })),
151
+ validation: criteria.map(criterion => ({ criterion, method: '', prerequisite: '', assertion: '', evidence: [], status: 'pending' })),
152
+ review: { status: 'pending', reviewer: '', findings: [] },
153
+ };
154
+ mkdirSync(dirname(join(root, file)), { recursive: true });
155
+ writeFileSync(join(root, file), JSON.stringify(report, null, 2) + '\n', { flag: 'wx' });
156
+ return { file, report };
157
+ }
158
+
159
+ function validateReview(file, { root = projectRoot, requireSeal = true } = {}) {
160
+ requireNode(); root = repository(root);
161
+ const reportPath = pathInRoot(root, file);
162
+ const report = JSON.parse(readFileSync(join(root, reportPath), 'utf8'));
163
+ if (report.version !== 1 || !slug.test(report.reference || '') || !slug.test(report.execution || '')) fail('Cabecalho de revisao invalido.');
164
+ const prefix = `.agent-tests/${report.reference}/${report.execution}`;
165
+ artifact(root, reportPath, prefix, true);
166
+ const baselineFile = artifact(root, `${prefix}/baseline.json`, prefix, true);
167
+ const baseline = JSON.parse(readFileSync(join(root, baselineFile), 'utf8'));
168
+ if (report.baseline?.file !== baselineFile || report.baseline.sha256 !== digest(join(root, baselineFile)) ||
169
+ baseline.reference !== report.reference || baseline.execution !== report.execution || baseline.spec !== report.spec ||
170
+ JSON.stringify(baseline.snapshot) !== JSON.stringify(report.snapshot)) fail('Baseline gerada diverge do relatorio; preserve os metadados e refaca o preparo.');
171
+ const state = report.snapshot;
172
+ if (!state || !['git', 'scope'].includes(state.mode) || !Array.isArray(state.files) || !Array.isArray(state.documents) || report.spec !== state.spec) fail('Snapshot ausente ou referencias alteradas.');
173
+ const requiredDocs = state.documents.map(file => pathInRoot(root, file));
174
+ const currentDocuments = expectedDocuments(root, state, requiredDocs);
175
+ if (currentDocuments.some(path => !requiredDocs.includes(path) || !state.files.some(file => file.path === path))) {
176
+ fail('Documentos obrigatorios mudaram depois do snapshot; refaca a revisao.');
177
+ }
178
+ const current = snapshot(root, state.mode, state.files.map(file => file.path), requiredDocs, state.spec);
179
+ if (JSON.stringify(current) !== JSON.stringify(state)) fail('Arquivos ou HEAD mudaram depois do snapshot; refaca a revisao.');
180
+ checkIndex(root, state);
181
+ if (!Array.isArray(report.documentation) || !Array.isArray(report.validation)) fail('Documentacao e validacoes sao obrigatorias.');
182
+ const docs = new Map();
183
+ for (const item of report.documentation) {
184
+ const path = pathInRoot(root, item.path);
185
+ if (docs.has(path) || !['updated', 'no-impact', 'not-applicable'].includes(item.decision) || !completed(item.reason)) fail(`Decisao de documentacao pendente/invalida: ${path}`);
186
+ if (item.decision !== 'not-applicable' && !existsSync(join(root, path))) fail(`Documento ausente: ${path}`);
187
+ if (item.decision === 'not-applicable' && existsSync(join(root, path))) fail(`Documento existente exige decisao de impacto: ${path}`);
188
+ docs.set(path, item);
189
+ }
190
+ for (const path of currentDocuments) if (!docs.has(path)) fail(`Documento sem revisao: ${path}`);
191
+ const ids = new Set();
192
+ const proofs = new Set();
193
+ for (const item of report.validation) {
194
+ if (!/^AC-\d{3,}$/.test(item.criterion || '') || ids.has(item.criterion)) fail('Criterio invalido ou duplicado.');
195
+ ids.add(item.criterion);
196
+ if (![item.method, item.prerequisite, item.assertion].every(completed)) fail(`Plano de validacao incompleto: ${item.criterion}`);
197
+ if (item.status !== 'passed' || !Array.isArray(item.evidence) || !item.evidence.length) fail(`Criterio sem prova aprovada: ${item.criterion}`);
198
+ for (const proof of item.evidence) proofs.add(artifact(root, proof, prefix, true));
199
+ }
200
+ if (!ids.size) fail('Nenhum criterio de aceite.');
201
+ for (const id of baseline.criteria) if (!ids.has(id)) fail(`Criterio da spec sem resultado: ${id}`);
202
+ if (report.review?.status !== 'approved' || !nonempty(report.review.reviewer) || !Array.isArray(report.review.findings)) fail('Revisao independente pendente.');
203
+ if (report.review.findings.some(item => !nonempty(item.id) || !nonempty(item.description) || item.status !== 'resolved')) fail('Achados de revisao ainda abertos.');
204
+ const seal = {
205
+ reportHash: digest(join(root, reportPath)), baselineHash: digest(join(root, baselineFile)),
206
+ evidence: [...proofs].sort().map(path => ({ path, sha256: digest(join(root, path)) })),
207
+ };
208
+ if (requireSeal) {
209
+ if (!existsSync(join(root, prefix, 'seal.json'))) fail('Revisao nao selada; conclua a revisao independente e execute seal.');
210
+ const sealPath = artifact(root, `${prefix}/seal.json`, prefix, true);
211
+ const recorded = JSON.parse(readFileSync(join(root, sealPath), 'utf8'));
212
+ if (JSON.stringify(recorded) !== JSON.stringify(seal)) fail('Relatorio, baseline ou evidencia alterados apos selar a revisao.');
213
+ }
214
+ return { root, prefix, seal, result: { status: 'passed', report: reportPath, criteria: ids.size, documents: docs.size } };
215
+ }
216
+
217
+ export function checkReview(file, { root = projectRoot } = {}) { return validateReview(file, { root, requireSeal: true }).result; }
218
+
219
+ export function sealReview(file, options) {
220
+ const checked = validateReview(file, { ...options, requireSeal: false });
221
+ const path = artifact(checked.root, `${checked.prefix}/seal.json`, checked.prefix, false);
222
+ if (existsSync(join(checked.root, path))) return checkReview(file, options);
223
+ writeFileSync(join(checked.root, path), JSON.stringify(checked.seal, null, 2) + '\n', { flag: 'wx' });
224
+ return checkReview(file, options);
225
+ }
226
+
227
+ const usage = 'Uso: revisar-entrega prepare --id <slug> [--execution <slug>] [--spec <arquivo>] [--scope <arquivo>] [--document <arquivo>] [--output <artefato>]; seal|check <revisao.json>';
228
+ export function main(args = process.argv.slice(2)) {
229
+ requireNode(); const [mode, ...rest] = args;
230
+ if (!mode || ['--help', '-h'].includes(mode)) { console.log(usage); return 0; }
231
+ if (mode === 'check' && rest.length === 1) { console.log(JSON.stringify(checkReview(rest[0]))); return 0; }
232
+ if (mode === 'seal' && rest.length === 1) { console.log(JSON.stringify(sealReview(rest[0]))); return 0; }
233
+ if (mode !== 'prepare') fail(usage);
234
+ const options = { scope: [], documents: [] };
235
+ const fields = { '--id': 'id', '--execution': 'execution', '--spec': 'spec', '--output': 'output' };
236
+ for (let i = 0; i < rest.length; i += 2) {
237
+ const value = rest[i + 1]; if (!value || value.startsWith('--')) fail(usage);
238
+ if (rest[i] === '--scope') options.scope.push(value);
239
+ else if (rest[i] === '--document') options.documents.push(value);
240
+ else if (fields[rest[i]]) { if (options[fields[rest[i]]] !== undefined) fail('Opcao duplicada.'); options[fields[rest[i]]] = value; }
241
+ else fail(usage);
242
+ }
243
+ console.log(JSON.stringify({ report: prepareReview(options).file, status: 'pending' })); return 0;
244
+ }
245
+
246
+ if (isMain(import.meta.url)) {
247
+ try { process.exitCode = main(); }
248
+ catch (error) { console.error(error.message); process.exitCode = 1; }
249
+ }