@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,28 @@
1
+ import assert from 'node:assert/strict';
2
+ import { once } from 'node:events';
3
+ import test from 'node:test';
4
+ import { createApplication } from '__SERVER_IMPORT__';
5
+
6
+ test('AC-001: HTTP health returns the expected state', async t => {
7
+ const server = createApplication();
8
+ server.listen(0, '127.0.0.1');
9
+ await once(server, 'listening');
10
+ t.after(() => new Promise(resolve => server.close(resolve)));
11
+ const response = await fetch(`http://127.0.0.1:${server.address().port}/health`);
12
+ assert.equal(response.status, 200);
13
+ assert.deepEqual(await response.json(), { status: 'ok' });
14
+ });
15
+
16
+ test('AC-002: HTTP sum computes values and rejects invalid input', async t => {
17
+ const server = createApplication();
18
+ server.listen(0, '127.0.0.1');
19
+ await once(server, 'listening');
20
+ t.after(() => new Promise(resolve => server.close(resolve)));
21
+ const url = `http://127.0.0.1:${server.address().port}/sum`;
22
+ const response = await fetch(url, { method: 'POST', body: JSON.stringify({ left: 2, right: 3 }) });
23
+ assert.equal(response.status, 200);
24
+ assert.deepEqual(await response.json(), { result: 5 });
25
+ const invalid = await fetch(url, { method: 'POST', body: JSON.stringify({ left: '2', right: 3 }) });
26
+ assert.equal(invalid.status, 400);
27
+ assert.deepEqual(await invalid.json(), { error: 'invalid input' });
28
+ });
@@ -0,0 +1,34 @@
1
+ import { createServer } from 'node:http';
2
+ import { pathToFileURL } from 'node:url';
3
+
4
+ const add = (left, right) => left + right;
5
+
6
+ export function createApplication() {
7
+ return createServer(async (request, response) => {
8
+ response.setHeader('content-type', 'application/json');
9
+ if (request.method === 'GET' && request.url === '/health') {
10
+ response.end(JSON.stringify({ status: 'ok' }));
11
+ return;
12
+ }
13
+ if (request.method === 'POST' && request.url === '/sum') {
14
+ try {
15
+ let body = '';
16
+ for await (const chunk of request) body += chunk;
17
+ const { left, right } = JSON.parse(body);
18
+ if (!Number.isFinite(left) || !Number.isFinite(right)) throw new Error('invalid numbers');
19
+ response.end(JSON.stringify({ result: add(left, right) }));
20
+ } catch {
21
+ response.statusCode = 400;
22
+ response.end(JSON.stringify({ error: 'invalid input' }));
23
+ }
24
+ return;
25
+ }
26
+ response.statusCode = 404;
27
+ response.end(JSON.stringify({ error: 'not found' }));
28
+ });
29
+ }
30
+
31
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
32
+ const server = createApplication();
33
+ server.listen(Number(process.env.PORT || 3000), '0.0.0.0');
34
+ }
@@ -0,0 +1,305 @@
1
+ import assert from 'node:assert/strict';
2
+ import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { createHash } from 'node:crypto';
6
+ import test from 'node:test';
7
+ import { projectRoot, requireNode } from '../../lib/runtime.mjs';
8
+ import { copyAgentStructure, prepareExamples as prepare, registeredRoots, resourceTargets, SCENARIOS } from '../SCRIPT.mjs';
9
+
10
+ // As fixtures pequenas isolam a cópia; o caso source-only usa o renderizador real.
11
+ const prepareExamples = options => prepare({ ...options, renderResources: () => {} });
12
+
13
+ requireNode();
14
+ function fixture(t) {
15
+ const home = join(projectRoot, '.agent-tests'); mkdirSync(home, { recursive: true });
16
+ const root = mkdtempSync(join(home, 'maturity-unit-'));
17
+ t.after(() => rmSync(root, { recursive: true, force: true }));
18
+ const put = (name, content = '') => { const file = join(root, name); mkdirSync(dirname(file), { recursive: true }); writeFileSync(file, content); return file; };
19
+ put('.pbai4devs/espelhos.conf', 'agente|.codex|plano\nagente|.github|plano\nagente|.agents|plano\nagente|.agent|plano\nagente|.gemini|plano\ngitignore|.vscode\n');
20
+ put('.pbai4devs/skills.json', '{"entries":[{"path":".pbai4devs/skills/projeto/demo"}]}');
21
+ put('.pbai4devs/skills/projeto/demo/SKILL.md', '---\nname: demo\ndescription: Fixture.\n---\n\n# Demo\n');
22
+ put('.codex/skills/demo/SKILL.md', 'ponteiro'); put('.github/skills/demo/SKILL.md', 'ponteiro');
23
+ for (const client of ['.agents', '.agent', '.gemini']) put(`${client}/skills/demo/SKILL.md`, 'ponteiro');
24
+ put('.gemini/commands/demo.toml', 'description = "Fixture"\nprompt = "Leia o recurso compartilhado."\n');
25
+ put('.vscode/settings.json', '{"fixture":true}\n');
26
+ for (const name of ['AGENTS.md', 'CLAUDE.md', 'GEMINI.md', 'README.md', '.gitattributes', '.editorconfig']) put(name, 'fixture\n');
27
+ put('.gitignore', readFileSync(join(projectRoot, '.gitignore')));
28
+ put('.pbai4devs/scripts/inspecionar-projeto/SCRIPT.mjs', readFileSync(join(projectRoot, '.pbai4devs/scripts/inspecionar-projeto/SCRIPT.mjs')));
29
+ put('.pbai4devs/scripts/lib/runtime.mjs', readFileSync(join(projectRoot, '.pbai4devs/scripts/lib/runtime.mjs')));
30
+ return { root, put, outputRoot: join(root, 'examples/generated') };
31
+ }
32
+
33
+ test('copia recursos e configurações, excluindo payloads, caches e segredos', t => {
34
+ const { root, put } = fixture(t);
35
+ put('.pbai4devs/tools/demo/TOOL.json', '{}'); put('.pbai4devs/tools/demo/SCRIPT.mjs', 'export {};'); put('.pbai4devs/tools/demo/README.md', '# Tool');
36
+ put('.pbai4devs/templates/demo/TEMPLATE.md', '# Template');
37
+ put('.pbai4devs/scripts/demo/tests/fixture.json', '{"keep":true}');
38
+ put('.pbai4devs/external-skills/README.md', '# External'); put('.pbai4devs/external-skills/CATALOG.json', '{}');
39
+ put('.env.example', 'PORT=3000\n');
40
+ const excluded = ['.pbai4devs/tools/demo/.local/package/README.md', '.pbai4devs/tools/demo/download.exe', '.pbai4devs/tools/.cache/archive', '.pbai4devs/tools/bin/demo.mjs', '.pbai4devs/external-skills/.local/upstream/SKILL.md', '.pbai4devs/scripts/demo/node_modules/pkg/index.js', '.pbai4devs/scripts/demo/.env', '.pbai4devs/scripts/demo/report.log', '.env', '.codex/auth.json', '.github/credentials.json', '.vscode/settings.local.json', '.pbai4devs/scripts/demo/private.pem', '.pbai4devs/scripts/demo/config.local.toml', '.codex/.credentials.json', '.agents/.local/token.json', '.gemini/auth.json'];
41
+ excluded.push('.pbai4devs/scripts/demo/playwright-report/index.html', '.pbai4devs/scripts/demo/TEST-RESULTS/report.json', '.pbai4devs/scripts/demo/TmP/generated.json', '.gemini/Temp/context.json', '.gemini/Sessions/state.json', '.pbai4devs/scripts/demo/history.jsonl', '.pbai4devs/skills/demo/.PLAYWRIGHT-MCP/session.json');
42
+ excluded.push('.pbai4devs/bin/agentdevkit.mjs', '.pbai4devs/src/cli.mjs', '.pbai4devs/release/pipeline.mjs', '.pbai4devs/tests/cli.test.mjs', '.pbai4devs/DEVELOPMENT.md');
43
+ excluded.push('bin/agentdevkit.mjs', 'src/cli.mjs', 'scripts/release/pipeline.mjs', 'tests/cli.test.mjs', 'examples/README.md');
44
+ const kept = ['.pbai4devs/scripts/demo/tests/fixtures/cache/input.json', '.pbai4devs/scripts/demo/tests/fixtures/playwright-response.json', '.pbai4devs/templates/demo/compose.local.yml.template'];
45
+ for (const file of kept) put(file, 'fixture versionavel');
46
+ for (const file of excluded) put(file, 'não copiar');
47
+ const destination = join(root, 'copy');
48
+ const result = copyAgentStructure(root, destination);
49
+ assert.ok(result.copiedFiles > 10);
50
+ for (const file of excluded) assert.equal(existsSync(join(destination, file)), false, file);
51
+ for (const file of excluded) assert.equal(readFileSync(join(root, file), 'utf8'), 'não copiar', `Origem alterada: ${file}`);
52
+ for (const file of ['.pbai4devs/tools/demo/TOOL.json', '.pbai4devs/tools/demo/SCRIPT.mjs', '.pbai4devs/external-skills/CATALOG.json', '.pbai4devs/templates/demo/TEMPLATE.md', '.pbai4devs/scripts/demo/tests/fixture.json', '.vscode/settings.json', '.gitattributes', '.env.example', '.agents/skills/demo/SKILL.md', '.agent/skills/demo/SKILL.md', '.gemini/skills/demo/SKILL.md', '.gemini/commands/demo.toml', 'GEMINI.md']) assert.ok(existsSync(join(destination, file)), file);
53
+ for (const file of kept) assert.equal(readFileSync(join(destination, file), 'utf8'), 'fixture versionavel');
54
+ assert.deepEqual(result.roots, ['.pbai4devs', '.codex', '.github', '.agents', '.agent', '.gemini', '.vscode']);
55
+ });
56
+
57
+ test('regeneração preserva pastas alheias e recusa sobrescrevê-las', t => {
58
+ const { root, put, outputRoot } = fixture(t);
59
+ put('examples/generated/anotacoes/keep.txt', 'preservado');
60
+ put('examples/README.md', 'Guia humano.');
61
+ const options = { sourceRoot: root, outputRoot, scenarios: [SCENARIOS[0]], initializeGit: false };
62
+ prepareExamples(options); prepareExamples(options);
63
+ assert.equal(readFileSync(join(outputRoot, 'anotacoes/keep.txt'), 'utf8'), 'preservado');
64
+ assert.equal(readFileSync(join(root, 'examples/README.md'), 'utf8'), 'Guia humano.');
65
+ put('examples/generated/descoberta/keep.txt', 'humano');
66
+ assert.throws(() => prepareExamples({ ...options, scenarios: [SCENARIOS[1]] }), /não é gerenciado/);
67
+ assert.equal(readFileSync(join(outputRoot, 'descoberta/keep.txt'), 'utf8'), 'humano');
68
+ assert.throws(() => prepareExamples({ ...options, scenarios: [{ ...SCENARIOS[0], id: '../outside' }] }), /IDs únicos e seguros/);
69
+ });
70
+
71
+ test('destino padrão redefine apenas relatório próprio e mantém resultados fora do Git', t => {
72
+ const { root, outputRoot } = fixture(t);
73
+ const git = (...args) => {
74
+ const result = spawnSync('git', ['-C', root, ...args], { encoding: 'utf8', shell: false });
75
+ assert.ifError(result.error); assert.equal(result.status, 0, result.stderr); return result.stdout;
76
+ };
77
+ git('init', '--template=', '--quiet');
78
+ const options = { sourceRoot: root, scenarios: [SCENARIOS[0]], initializeGit: false };
79
+ const examples = prepareExamples(options);
80
+ assert.equal(examples[0].root, join(outputRoot, SCENARIOS[0].id));
81
+ const reportFile = join(outputRoot, 'REPORT.json');
82
+ writeFileSync(reportFile, JSON.stringify({ owner: 'testar-maturidade', status: 'passed', results: [{ stale: true }] }));
83
+ prepareExamples(options);
84
+ assert.deepEqual(JSON.parse(readFileSync(reportFile)), { owner: 'testar-maturidade', version: 1,
85
+ status: 'prepared', scenarios: [SCENARIOS[0].id], results: [] });
86
+ assert.match(git('check-ignore', '--verbose', 'examples/generated/REPORT.json'), /examples\/generated\/\.gitignore:2:\*/);
87
+ assert.match(git('check-ignore', '--verbose', 'examples/generated/somente-agentes/README.md'), /examples\/generated\/\.gitignore:2:\*/);
88
+ const human = JSON.stringify({ owner: 'human', notes: 'preservar' });
89
+ writeFileSync(reportFile, human);
90
+ assert.throws(() => prepareExamples(options), /Relatório.*não pertence/);
91
+ assert.equal(readFileSync(reportFile, 'utf8'), human);
92
+ });
93
+
94
+ test('preserva ignore humano e continua aceitando destinos legados', t => {
95
+ const { root, put } = fixture(t);
96
+ const options = { sourceRoot: root, scenarios: [SCENARIOS[0]], initializeGit: false };
97
+ put('examples/generated/.gitignore', '!README.md\n');
98
+ assert.throws(() => prepareExamples(options), /Ignore.*não pertence/);
99
+ assert.equal(readFileSync(join(root, 'examples/generated/.gitignore'), 'utf8'), '!README.md\n');
100
+ for (const target of ['tmp/examples', '.agent-tests/cases']) {
101
+ const outputRoot = join(root, target);
102
+ const [example] = prepareExamples({ ...options, outputRoot });
103
+ assert.equal(example.root, join(outputRoot, SCENARIOS[0].id));
104
+ }
105
+ });
106
+
107
+ test('preparação interrompida não conserva resultado anterior nem declara sucesso', t => {
108
+ const { root, put, outputRoot } = fixture(t);
109
+ const options = { sourceRoot: root, scenarios: [SCENARIOS[0]], initializeGit: false };
110
+ prepareExamples(options);
111
+ const report = join(outputRoot, 'REPORT.json');
112
+ const index = join(outputRoot, 'README.md');
113
+ writeFileSync(report, JSON.stringify({ owner: 'testar-maturidade', status: 'passed', results: [{}] }));
114
+ writeFileSync(index, readFileSync(index, 'utf8').replaceAll('prepared', 'passed'));
115
+ put('.pbai4devs/estado/agentdevkit.json', JSON.stringify({ blocks: null }));
116
+ assert.throws(() => prepareExamples(options), /Estado da instalação/);
117
+ assert.equal(existsSync(report), false);
118
+ assert.match(readFileSync(index, 'utf8'), /status: preparing/);
119
+ assert.doesNotMatch(readFileSync(index, 'utf8'), /status: passed|\| passed \|/);
120
+ assert.doesNotMatch(readFileSync(index, 'utf8'), /\n{3,}/);
121
+ });
122
+
123
+ test('usa entradas distribuídas do hub e mantém fallback legado', t => {
124
+ const { root, put } = fixture(t);
125
+ const template = '---\ndescription: Entrada genérica.\n---\n\n# Agentes do projeto\n';
126
+ put('AGENTS.md', '# Produto do distribuidor\n\n[PRD](docs/prd.md)\n');
127
+ put('.pbai4devs/templates/entrypoints/AGENTS.md', template);
128
+ const destination = join(root, 'copy');
129
+ const copied = copyAgentStructure(root, destination);
130
+ assert.equal(readFileSync(join(destination, 'AGENTS.md'), 'utf8'), template);
131
+ assert.equal(readFileSync(join(destination, 'CLAUDE.md'), 'utf8'), 'fixture\n');
132
+ assert.equal(copied.entrypoints['AGENTS.md'], '.pbai4devs/templates/entrypoints/AGENTS.md');
133
+ assert.equal(copied.entrypoints['CLAUDE.md'], 'CLAUDE.md');
134
+ assert.equal(readFileSync(join(root, 'AGENTS.md'), 'utf8'), '# Produto do distribuidor\n\n[PRD](docs/prd.md)\n');
135
+ });
136
+
137
+ test('cópias respeitam clientes e entradas de instalações parciais', async t => {
138
+ for (const [client, manifest, entrypoints] of [
139
+ ['claude', 'agente|.claude|plano\n', ['AGENTS.md', 'CLAUDE.md']],
140
+ ['codex', 'agente|.codex|agrupado\nagente|.agents|agrupado\n', ['AGENTS.md']],
141
+ ]) await t.test(client, t => {
142
+ const { root, put } = fixture(t);
143
+ put('.pbai4devs/espelhos.conf', manifest);
144
+ put('.claude/skills/demo/SKILL.md', 'ponteiro');
145
+ for (const name of ['AGENTS.md', 'CLAUDE.md', 'GEMINI.md']) {
146
+ put(`.pbai4devs/templates/entrypoints/${name}`, `# Modelo ${name}\n`);
147
+ if (!entrypoints.includes(name)) rmSync(join(root, name));
148
+ }
149
+ const destination = join(root, 'copy');
150
+ const copied = copyAgentStructure(root, destination);
151
+ assert.deepEqual(Object.keys(copied.entrypoints), entrypoints);
152
+ for (const name of ['AGENTS.md', 'CLAUDE.md', 'GEMINI.md']) {
153
+ assert.equal(existsSync(join(destination, name)), entrypoints.includes(name), name);
154
+ assert.ok(existsSync(join(destination, '.pbai4devs/templates/entrypoints', name)));
155
+ }
156
+ assert.equal(existsSync(join(destination, '.vscode')), false);
157
+ assert.equal(existsSync(join(root, '.vscode/settings.json')), true);
158
+ assert.equal(existsSync(join(destination, '.github')), false);
159
+ assert.equal(existsSync(join(destination, '.gemini')), false);
160
+ if (client === 'claude') {
161
+ assert.deepEqual(copied.roots, ['.pbai4devs', '.claude']);
162
+ assert.equal(existsSync(join(destination, '.codex')), false);
163
+ assert.equal(existsSync(join(destination, '.agents')), false);
164
+ } else {
165
+ assert.deepEqual(copied.roots, ['.pbai4devs', '.codex', '.agents']);
166
+ assert.equal(existsSync(join(destination, '.claude')), false);
167
+ }
168
+ });
169
+ });
170
+
171
+ test('configurações do editor exigem registro explícito no manifesto', t => {
172
+ const { root, put } = fixture(t);
173
+ put('.pbai4devs/espelhos.conf', 'agente|.codex|plano\nagente|.vscode|plano\ngitignore|.vscode\n');
174
+ const destination = join(root, 'copy');
175
+ const copied = copyAgentStructure(root, destination);
176
+ assert.deepEqual(copied.roots, ['.pbai4devs', '.codex', '.vscode']);
177
+ assert.equal(readFileSync(join(destination, '.vscode/settings.json'), 'utf8'), '{"fixture":true}\n');
178
+ });
179
+
180
+ test('instalação parcial preserva entradas próprias e blocos editados', t => {
181
+ const { root, put } = fixture(t);
182
+ put('.pbai4devs/espelhos.conf', 'agente|.codex|agrupado\nagente|.agents|agrupado\n');
183
+ const block = '<!-- BEGIN AGENTDEVKIT -->\nEntrada gerenciada.\n<!-- END AGENTDEVKIT -->';
184
+ put('.pbai4devs/estado/agentdevkit.json', JSON.stringify({ schemaVersion: 1, version: '1.0.0', language: 'pt', clients: ['codex'], files: {}, blocks: { 'AGENTS.md': createHash('sha256').update(block).digest('hex') } }));
185
+ put('AGENTS.md', `# Agentes\n\n${block}\n`);
186
+ const human = '# Claude pessoal\r\n\r\nPreservar byte a byte: ação.\r\n';
187
+ put('CLAUDE.md', human);
188
+ rmSync(join(root, 'GEMINI.md'));
189
+ for (const name of ['AGENTS.md', 'CLAUDE.md', 'GEMINI.md']) put(`.pbai4devs/templates/entrypoints/${name}`, `# Modelo ${name}\n`);
190
+ const destination = join(root, 'copy');
191
+ const copied = copyAgentStructure(root, destination);
192
+ assert.equal(readFileSync(join(destination, 'AGENTS.md'), 'utf8'), '# Modelo AGENTS.md\n');
193
+ assert.deepEqual(readFileSync(join(destination, 'CLAUDE.md')), Buffer.from(human));
194
+ assert.doesNotMatch(readFileSync(join(destination, 'CLAUDE.md'), 'utf8'), /BEGIN AGENTDEVKIT/);
195
+ assert.equal(copied.entrypoints['AGENTS.md'], '.pbai4devs/templates/entrypoints/AGENTS.md');
196
+ assert.equal(copied.entrypoints['CLAUDE.md'], 'CLAUDE.md');
197
+ assert.equal(existsSync(join(destination, 'GEMINI.md')), false);
198
+ assert.equal(existsSync(join(destination, '.claude')), false);
199
+ assert.equal(existsSync(join(destination, '.vscode')), false);
200
+ put('AGENTS.md', `# Agentes\n\n${block.replace('gerenciada', 'editada')}\n`);
201
+ const changed = join(root, 'changed-copy');
202
+ copyAgentStructure(root, changed);
203
+ assert.deepEqual(readFileSync(join(changed, 'AGENTS.md')), readFileSync(join(root, 'AGENTS.md')));
204
+ });
205
+
206
+ test('localiza apontamentos pelo formato de cada tipo e por overrides nativos', t => {
207
+ const { root, put } = fixture(t);
208
+ put('.pbai4devs/espelhos.conf', [
209
+ 'agente|.claude|agrupado', 'formato|.claude|skills|plano',
210
+ 'agente|.codex|plano', 'formato|.codex|skills|agrupado',
211
+ 'nativo|.codex/skills/alias/SKILL.md|.pbai4devs/skills/projeto/demo/SKILL.md',
212
+ ].join('\n') + '\n');
213
+ assert.deepEqual(resourceTargets(root, '.pbai4devs/skills/projeto/demo/SKILL.md'), [
214
+ '.claude/skills/demo/SKILL.md', '.codex/skills/projeto/demo/SKILL.md', '.codex/skills/alias/SKILL.md',
215
+ ]);
216
+ });
217
+
218
+ test('checkout de fontes sem manifesto deriva clientes em memória', t => {
219
+ const { root, put } = fixture(t);
220
+ const manifest = join(root, '.pbai4devs/espelhos.conf');
221
+ rmSync(manifest);
222
+ assert.ok(registeredRoots(root).includes('.claude'));
223
+ const source = '.pbai4devs/skills/projeto/demo/SKILL.md';
224
+ assert.ok(resourceTargets(root, source).includes('.claude/skills/demo/SKILL.md'));
225
+ put('.pbai4devs/estado/agentdevkit.json', JSON.stringify({ clients: ['claude'] }));
226
+ assert.deepEqual(registeredRoots(root), ['.claude']);
227
+ assert.deepEqual(resourceTargets(root, source), ['.claude/skills/demo/SKILL.md']);
228
+ assert.equal(existsSync(manifest), false);
229
+ });
230
+
231
+ test('prepare gera apontamentos reais mesmo sem pastas de clientes na origem', t => {
232
+ const { root } = fixture(t);
233
+ const sourceRoot = join(root, 'source-only');
234
+ copyAgentStructure(projectRoot, sourceRoot);
235
+ const roots = registeredRoots(sourceRoot);
236
+ for (const client of roots) rmSync(join(sourceRoot, client), { recursive: true, force: true });
237
+ const [example] = prepare({ sourceRoot, scenarios: [SCENARIOS[0]], initializeGit: false });
238
+ const skill = '.pbai4devs/skills/projeto/criar-skill/SKILL.md';
239
+ for (const target of resourceTargets(sourceRoot, skill)) assert.ok(existsSync(join(example.root, target)), target);
240
+ for (const client of roots) assert.equal(existsSync(join(sourceRoot, client)), false, client);
241
+ for (const file of ['src/cli.mjs', 'bin/agentdevkit.mjs', 'scripts/release/pipeline.mjs', 'tests/cli.test.mjs']) assert.equal(existsSync(join(example.root, file)), false, file);
242
+ });
243
+
244
+ test('saída não pode sobrepor recursos de origem nem escapar das áreas de teste', t => {
245
+ const { root } = fixture(t);
246
+ const original = readFileSync(join(root, '.pbai4devs/espelhos.conf'), 'utf8');
247
+ for (const location of ['.pbai4devs/generated', '.github', '.codex', '.agents', '.agent', '.gemini', 'examples', 'examples/custom', 'tmp', 'output', '../outside']) {
248
+ assert.throws(() => prepareExamples({ sourceRoot: root, outputRoot: join(root, location), scenarios: [SCENARIOS[0]], initializeGit: false }), /Saída permitida/);
249
+ assert.equal(readFileSync(join(root, '.pbai4devs/espelhos.conf'), 'utf8'), original);
250
+ assert.equal(existsSync(join(root, location, '.maturity-suite.json')), false);
251
+ }
252
+ const outputRoot = join(root, '.agent-tests/permitted');
253
+ const examples = prepareExamples({ sourceRoot: root, outputRoot, scenarios: [SCENARIOS[0]], initializeGit: false });
254
+ assert.equal(examples.length, 1);
255
+ assert.equal(readFileSync(join(root, '.pbai4devs/espelhos.conf'), 'utf8'), original);
256
+ });
257
+
258
+ test('oito cópias exercitam os seis níveis e preservam layouts legado/monorepo', t => {
259
+ const { root, outputRoot } = fixture(t);
260
+ const examples = prepareExamples({ sourceRoot: root, outputRoot, initializeGit: false });
261
+ assert.equal(examples.length, 8);
262
+ for (const example of examples) {
263
+ assert.doesNotMatch(readFileSync(join(example.root, 'README.md'), 'utf8'), /\n{3,}/);
264
+ const result = spawnSync(process.execPath, ['.pbai4devs/scripts/inspecionar-projeto/SCRIPT.mjs'], { cwd: example.root, encoding: 'utf8' });
265
+ assert.equal(result.status, 0, result.stderr);
266
+ const report = JSON.parse(result.stdout);
267
+ assert.equal(report.maturity.level, example.expected, example.id);
268
+ assert.equal(report.contract.complete, example.complete, example.id);
269
+ }
270
+ const legacy = examples.find(example => example.id === 'legado');
271
+ assert.ok(existsSync(join(legacy.root, 'legacy/server.mjs')));
272
+ assert.match(readFileSync(join(legacy.root, 'package.json'), 'utf8'), /dev:old/);
273
+ const monorepo = examples.find(example => example.id === 'monorepo');
274
+ assert.ok(existsSync(join(monorepo.root, 'apps/painel com espaços/package.json')));
275
+ assert.ok(existsSync(join(monorepo.root, 'packages/cálculo/package.json')));
276
+ assert.equal(existsSync(join(monorepo.root, 'src')), false);
277
+ });
278
+
279
+ test('fixtures padrão, legado e monorepo executam HTTP real e encerram', t => {
280
+ const { root, outputRoot } = fixture(t);
281
+ const scenarios = SCENARIOS.filter(example => ['app-inicial', 'legado', 'monorepo'].includes(example.id));
282
+ const examples = prepareExamples({ sourceRoot: root, outputRoot, scenarios, initializeGit: false });
283
+ for (const example of examples) {
284
+ const environment = { ...process.env }; delete environment.NODE_TEST_CONTEXT;
285
+ const result = spawnSync(process.execPath, ['--test', ...example.application.tests], { cwd: example.root, env: environment, encoding: 'utf8', timeout: 30000 });
286
+ assert.ifError(result.error); assert.equal(result.status, 0, result.stderr || result.stdout);
287
+ assert.match(result.stdout, /AC-001/); assert.match(result.stdout, /AC-002/);
288
+ if (example.id === 'monorepo') assert.match(result.stdout, /AC-003/);
289
+ }
290
+ });
291
+
292
+ test('recusa links de origem e de saída, inclusive quebrados', t => {
293
+ const { root, put, outputRoot } = fixture(t);
294
+ const target = put('payload.txt', 'fora da cópia');
295
+ const link = join(root, '.pbai4devs/scripts/alias.mjs');
296
+ try { symlinkSync(target, link); }
297
+ catch (error) { if (process.platform === 'win32' && ['EPERM', 'EACCES'].includes(error.code)) { t.skip('Sem permissão para criar symlink de arquivo.'); return; } throw error; }
298
+ assert.throws(() => copyAgentStructure(root, join(root, 'copy')), /recusa links/);
299
+ rmSync(link); symlinkSync(join(root, 'missing.mjs'), link);
300
+ assert.throws(() => copyAgentStructure(root, join(root, 'copy')), /recusa links/);
301
+ rmSync(link);
302
+ mkdirSync(dirname(outputRoot), { recursive: true });
303
+ symlinkSync(join(root, 'missing-output'), outputRoot, process.platform === 'win32' ? 'junction' : 'dir');
304
+ assert.throws(() => prepareExamples({ sourceRoot: root, outputRoot, scenarios: [SCENARIOS[0]], initializeGit: false }), /não pode ser link/);
305
+ });
@@ -0,0 +1,49 @@
1
+ ---
2
+ description: Execução de cenários reutilizáveis no conector Playwright.
3
+ ---
4
+
5
+ # Testar no navegador
6
+
7
+ Use quando o agente tiver `browser_run_code_unsafe` com `code` e `filename`.
8
+ O script usa a página do conector, não requer instalar Playwright no projeto.
9
+ Se a ferramenta não existir ou não compartilhar a página entre chamadas,
10
+ use os testes Playwright do projeto ou informe a limitação.
11
+
12
+ 1. Abra a URL real e observe os controles pelo snapshot da ferramenta.
13
+ 2. Em `browser_run_code_unsafe`, passe uma função como esta em `code`, com os
14
+ controles e resultados observados; não coloque credenciais no cenário.
15
+
16
+ ```js
17
+ async (page) => {
18
+ page.__agentsRequest = {
19
+ url: 'http://localhost:3000',
20
+ steps: [{ action: 'visible', role: 'heading', name: 'Título real' }]
21
+ };
22
+ return { ready: true };
23
+ }
24
+ ```
25
+
26
+ 3. Execute `filename: '.pbai4devs/scripts/testar-navegador/SCRIPT.js'` na mesma
27
+ ferramenta. Confira `passed`, cada viewport e os erros; salve o resultado
28
+ em `.agent-tests/<tarefa>/resultado.json`.
29
+ 4. Capture a tela com a ferramenta em `.agent-tests/<tarefa>/tela.png` e
30
+ inspecione-a. Encerre somente a página ou sessão aberta para esta tarefa.
31
+
32
+ Por padrão, são usados 1280×800 e 390×844. O cenário aceita `viewports`,
33
+ `timeout` (máximo 30 segundos) e `title`. Cada passo usa `testId`, `label` ou
34
+ `role` e `name` exatos, com uma das ações:
35
+
36
+ | Ação | Dados |
37
+ | --- | --- |
38
+ | `visible`, `click` | Controle observado |
39
+ | `text`, `fill`, `select` | `value` |
40
+ | `press` | `key` |
41
+ | `check` | `checked` (padrão: `true`) |
42
+
43
+ É obrigatória uma asserção `visible` ou `text`. Console, página e rede com
44
+ erros reprovam o cenário; investigue a origem sem suprimir falhas para aprovar.
45
+ Fluxos destrutivos exigem dados e ambiente de teste apropriados, com limpeza.
46
+
47
+ Para provar só a infraestrutura, execute `tests/SUCCESS.js` desta pasta
48
+ e depois o executor; espere `passed: true`. Repita com `tests/FAIL.js`; espere
49
+ `passed: false` por erro de console. Essas fixtures não verificam o produto.
@@ -0,0 +1 @@
1
+ executor=playwright-mcp
@@ -0,0 +1,64 @@
1
+ async (page) => {
2
+ const request = page.__agentsRequest;
3
+ delete page.__agentsRequest;
4
+ if (!request || (!request.url && !request.fixtureHtml)) throw new Error('Defina page.__agentsRequest com URL ou fixtureHtml.');
5
+ if (!request.steps?.some(step => ['visible', 'text'].includes(step.action))) throw new Error('Informe ao menos uma verificacao observavel.');
6
+ const viewports = request.viewports || [{ width: 1280, height: 800 }, { width: 390, height: 844 }];
7
+ if (!viewports.length || viewports.some(size => !(size.width > 0 && size.height > 0))) throw new Error('Viewports invalidos.');
8
+ const errors = [];
9
+ const cleanUrl = value => { try { const url = new URL(value); return url.origin === 'null' ? url.protocol + url.pathname : url.origin + url.pathname; } catch { return value; } };
10
+ const onConsole = message => { if (message.type() === 'error') errors.push({ type: 'console', message: message.text().slice(0, 300) }); };
11
+ const onPage = error => errors.push({ type: 'page', message: error.message.slice(0, 300) });
12
+ const onRequest = req => errors.push({ type: 'network', url: cleanUrl(req.url()), message: req.failure()?.errorText });
13
+ const onResponse = response => { if (response.status() >= 400) errors.push({ type: 'http', status: response.status(), url: cleanUrl(response.url()) }); };
14
+ const locate = step => {
15
+ if (step.testId) return page.getByTestId(step.testId);
16
+ if (step.role && step.name) return page.getByRole(step.role, { name: step.name, exact: true });
17
+ if (step.label) return page.getByLabel(step.label, { exact: true });
18
+ throw new Error('Informe role/name, label ou testId observado na interface.');
19
+ };
20
+ const results = [];
21
+ page.setDefaultTimeout(Math.min(request.timeout || 10000, 30000));
22
+ page.on('console', onConsole); page.on('pageerror', onPage);
23
+ page.on('requestfailed', onRequest); page.on('response', onResponse);
24
+ try {
25
+ for (const viewport of viewports) {
26
+ errors.length = 0;
27
+ const result = { viewport, passed: false, steps: 0 };
28
+ try {
29
+ await page.setViewportSize(viewport);
30
+ if (request.fixtureHtml) await page.setContent(request.fixtureHtml, { waitUntil: 'domcontentloaded' });
31
+ else {
32
+ const response = await page.goto(request.url, { waitUntil: 'domcontentloaded' });
33
+ if (!response || !response.ok()) throw new Error(`Navegacao falhou: ${response?.status()}`);
34
+ }
35
+ if (request.title && await page.title() !== request.title) throw new Error('Titulo divergente.');
36
+ for (const step of request.steps || []) {
37
+ const locator = locate(step);
38
+ switch (step.action) {
39
+ case 'fill': await locator.fill(step.value); break;
40
+ case 'click': await locator.click(); break;
41
+ case 'press': await locator.press(step.key); break;
42
+ case 'select': await locator.selectOption(step.value); break;
43
+ case 'check': await locator.setChecked(step.checked !== false); break;
44
+ case 'visible': await locator.waitFor({ state: 'visible' }); break;
45
+ case 'text':
46
+ const escaped = step.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
47
+ await locator.filter({ hasText: new RegExp(`^\\s*${escaped}\\s*$`) }).waitFor({ state: 'visible' });
48
+ if ((await locator.innerText()).trim() !== step.value) throw new Error('Texto divergente.');
49
+ break;
50
+ default: throw new Error(`Acao desconhecida: ${step.action}`);
51
+ }
52
+ result.steps++;
53
+ }
54
+ if (errors.length) throw new Error('Erros de console, pagina ou rede.');
55
+ result.passed = true;
56
+ } catch (error) { result.failure = error.message.slice(0, 300); }
57
+ result.errors = [...errors]; results.push(result);
58
+ }
59
+ } finally {
60
+ page.off('console', onConsole); page.off('pageerror', onPage);
61
+ page.off('requestfailed', onRequest); page.off('response', onResponse);
62
+ }
63
+ return { mode: request.fixtureHtml ? 'fixture' : 'application', passed: results.every(x => x.passed), browser: page.context().browser()?.browserType().name(), version: page.context().browser()?.version(), url: cleanUrl(page.url()), results };
64
+ }
@@ -0,0 +1,8 @@
1
+ async (page) => {
2
+ page.__agentsRequest = {
3
+ fixtureHtml: '<!doctype html><html><body><h1>Falha esperada</h1><script>console.error("erro de prova")</script></body></html>',
4
+ viewports: [{ width: 1280, height: 800 }],
5
+ steps: [{ action: 'visible', role: 'heading', name: 'Falha esperada' }]
6
+ };
7
+ return { fixture: 'failure-ready', expected: 'passed=false', next: '.pbai4devs/scripts/testar-navegador/SCRIPT.js' };
8
+ }
@@ -0,0 +1,13 @@
1
+ async (page) => {
2
+ page.__agentsRequest = {
3
+ fixtureHtml: '<!doctype html><html lang="pt-BR"><head><meta name="viewport" content="width=device-width,initial-scale=1"><title>Prova dos agentes</title></head><body><main><h1>Prova dos agentes</h1><form><label>Nome<input name="nome" required></label><button type="submit">Confirmar</button></form><p role="status" data-testid="resultado"></p></main><script>document.querySelector("form").onsubmit=e=>{e.preventDefault();document.querySelector("[role=status]").textContent="Ola, "+e.target.nome.value}</script></body></html>',
4
+ title: 'Prova dos agentes',
5
+ steps: [
6
+ { action: 'visible', role: 'heading', name: 'Prova dos agentes' },
7
+ { action: 'fill', label: 'Nome', value: 'Agente' },
8
+ { action: 'click', role: 'button', name: 'Confirmar' },
9
+ { action: 'text', testId: 'resultado', value: 'Ola, Agente' }
10
+ ]
11
+ };
12
+ return { fixture: 'ready', next: '.pbai4devs/scripts/testar-navegador/SCRIPT.js' };
13
+ }
@@ -0,0 +1,25 @@
1
+ ---
2
+ description: Verificação dos recursos compartilhados e de seus apontamentos.
3
+ ---
4
+
5
+ # Verificar recursos
6
+
7
+ ```sh
8
+ node .pbai4devs/scripts/verificar-recursos/SCRIPT.mjs
9
+ node .pbai4devs/scripts/verificar-recursos/SCRIPT.mjs --source-only
10
+ node --test .pbai4devs/scripts/verificar-recursos/tests/TEST.mjs
11
+ ```
12
+
13
+ Exige Node 24+. Confere espelhos, frontmatter/H1, links locais e guias das
14
+ ferramentas. Ignora pacotes baixados e resultados temporários, inclusive
15
+ Playwright em qualquer caixa. Fixtures comuns continuam verificadas.
16
+ Inclui Markdown da raiz, de `docs/`, `src/`, `bin/`, `tests/`, `scripts/`,
17
+ `examples/` e templates de contribuição do GitHub. Exclui `examples/generated/`.
18
+ Essa coleta usa somente o diretório informado, sem buscar outro repositório Git.
19
+ Usa o Markdown lint local
20
+ quando instalado e informa sua ausência. Não instala nem sincroniza recursos.
21
+ Testes e fixtures são versionados; temporários ficam em `.agent-tests/`.
22
+
23
+ `--source-only` valida as fontes sem exigir catálogos, manifesto ou clientes
24
+ gerados. Referências aos índices derivados são conferidas pelo catálogo em
25
+ memória; links para fontes inexistentes continuam sendo erros.