@pathbit/agentdevkit 0.0.27 → 0.0.28

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.
@@ -10,8 +10,9 @@ comando em macOS, Linux e Windows. Bun não faz parte do contrato validado.
10
10
  Versione scripts e testes; salve somente resultados em `.agent-tests/`.
11
11
  Prepare ferramentas locais com `preparar-ferramentas`, sem instalações globais.
12
12
  O runtime da aplicação será decidido no design doc; este requisito é dos agentes.
13
- Com Node 18+, use o [bootstrap JavaScript](preparar-ferramentas/BOOTSTRAP.md)
14
- com `--runtime-only` para preparar Node 24+ e continuar a operação.
13
+ Com Node 18+, o [bootstrap JavaScript](preparar-ferramentas/BOOTSTRAP.md)
14
+ prepara Node 24+ com `--runtime-only` ou encadeia preparo, diagnóstico e seleção
15
+ com [`--executar`](executar-script/README.md).
15
16
  Sem Node/npm, o agente instala primeiro pelo mecanismo do sistema operacional.
16
17
  Não há inicializadores `.sh` ou `.ps1` no kit.
17
18
 
@@ -7,6 +7,29 @@ description: Executor comum dos scripts Node do projeto.
7
7
  Use Node 24+ com o nome de uma pasta em `.pbai4devs/scripts/`, uma pasta do
8
8
  repositório ou o caminho de um arquivo `.mjs`. O comando funciona nos três SOs:
9
9
 
10
+ ```console
11
+ cd .pbai4devs/scripts/preparar-ferramentas
12
+ node BOOTSTRAP.mjs --executar --plan preparar-ferramentas
13
+ node BOOTSTRAP.mjs --executar preparar-ferramentas list
14
+ cd ../../..
15
+ ```
16
+
17
+ `--executar` prepara Node/npm, diagnostica e só então chama o seletor. Falha
18
+ no diagnóstico interrompe a operação, inclusive se houver configuração de
19
+ ferramenta inválida sem relação com o alvo; inspecione a falha. Diagnóstico com
20
+ sucesso indica inventário concluído, não que todas as dependências do alvo estão
21
+ prontas. Com `--no-install`, o bootstrap usa o runtime atual, o primeiro Node
22
+ do PATH ou o cache, desde que compatível, sem baixar ou instalar o runtime.
23
+ Essa opção não restringe rede nem
24
+ instalações pelo diagnóstico ou script filho. O plano também não baixa, instala
25
+ nem diagnostica, e usa as mesmas opções de runtime.
26
+ O alvo, seus argumentos e o código de saída são preservados.
27
+ Na saída padrão, `--executar` reúne o caminho do runtime, o diagnóstico, o
28
+ cabeçalho do seletor e a saída do alvo; ela não é um JSON único nem uma saída
29
+ bruta do alvo. O seletor direto também inclui seu próprio cabeçalho.
30
+
31
+ Com Node 24+ já disponível, o seletor pode ser chamado diretamente:
32
+
10
33
  ```console
11
34
  node .pbai4devs/scripts/executar-script/SCRIPT.mjs --plan preparar-ferramentas
12
35
  node .pbai4devs/scripts/executar-script/SCRIPT.mjs preparar-ferramentas list
@@ -15,7 +15,10 @@ node .pbai4devs/scripts/preparar-ferramentas/BOOTSTRAP.mjs --runtime-only
15
15
  ```
16
16
 
17
17
  O modo `--runtime-only` imprime `node=<caminho absoluto>` sem instalar
18
- ferramentas. Sem a opção, continua com `setup`; aceita grupo ou nome.
18
+ ferramentas. Sem opções, continua com `setup`; aceita grupo ou nome.
19
+ Para preparar o runtime, diagnosticar e executar um script em sequência, veja
20
+ [`--executar`](../executar-script/README.md). O plano e `--no-install` não
21
+ baixam o runtime; não restringem efeitos dos processos que forem executados.
19
22
 
20
23
  O fluxo reutiliza um runtime saudável ou o cache local. Se necessário, baixa
21
24
  Node 24 de `nodejs.org`, verifica SHA-256 e extrai usando `tar` nativo.
@@ -109,7 +109,7 @@ export function validateExtracted(root) {
109
109
  visit(base);
110
110
  }
111
111
 
112
- export async function ensureRuntime({ root = projectRoot, minimum = 24, node = process.execPath, platform = process.platform, arch = process.arch, libc, run = spawnSync, download = downloadOfficial, environment = process.env } = {}) {
112
+ export async function ensureRuntime({ root = projectRoot, minimum = 24, node = process.execPath, platform = process.platform, arch = process.arch, libc, run = spawnSync, download = downloadOfficial, environment = process.env, allowDownload = true, searchPath = false } = {}) {
113
113
  if (Number(process.versions.node.split('.')[0]) < 18) failure('O inicializador requer Node.js 18+; instale Node.js 24+ pelo gerenciador do SO.');
114
114
  root = realpathSync(root);
115
115
  const current = probe(node, minimum, run, environment);
@@ -117,6 +117,12 @@ export async function ensureRuntime({ root = projectRoot, minimum = 24, node = p
117
117
  const npm = npmFor(current.node, run, environment, platform);
118
118
  if (npm) return { node: current.node, npm, version: current.version, source: 'current' };
119
119
  }
120
+ if (searchPath) {
121
+ const executable = findExecutable('node', environment, platform);
122
+ const found = executable && probe(executable, minimum, run, environment);
123
+ const npm = found && npmFor(found.node, run, environment, platform);
124
+ if (found && npm) return { node: found.node, npm, version: found.version, source: 'path' };
125
+ }
120
126
  const targetPlatform = platformName(platform, arch, run, environment, libc);
121
127
  const cache = safePath(root, ['.pbai4devs', 'tools', '.cache', 'runtime', 'node', targetPlatform]);
122
128
  if (stat(cache) && !stat(cache).isDirectory()) failure('Cache de runtime nao e diretorio.');
@@ -126,6 +132,7 @@ export async function ensureRuntime({ root = projectRoot, minimum = 24, node = p
126
132
  const found = probe(executable, minimum, run, environment);
127
133
  if (found) { const npm = npmFor(executable, run, environment, platform, true); if (npm) return { node: executable, npm, version: found.version, source: 'cache' }; }
128
134
  }
135
+ if (!allowDownload) failure('Node.js 24+ e npm nao encontrados; prepare o runtime antes de continuar.');
129
136
  const tar = findExecutable('tar', environment, platform);
130
137
  if (!tar) failure('Instale tar pelo gerenciador do SO para extrair o Node local.');
131
138
  mkdirSync(cache, { recursive: true });
@@ -170,11 +177,42 @@ export async function ensureRuntime({ root = projectRoot, minimum = 24, node = p
170
177
 
171
178
  export async function main(args = process.argv.slice(2), options = {}) {
172
179
  const runtimeOnly = args[0] === '--runtime-only';
180
+ const execute = args[0] === '--executar';
181
+ const selected = execute ? args.slice(1) : [];
182
+ let plan = false, noInstall = false;
183
+ while (execute && ['--plan', '--no-install'].includes(selected[0])) {
184
+ if (selected[0] === '--plan') { if (plan) failure('--plan duplicado.'); plan = true; }
185
+ else { if (noInstall) failure('--no-install duplicado.'); noInstall = true; }
186
+ selected.shift();
187
+ }
173
188
  if (runtimeOnly && args.length !== 1) failure('--runtime-only nao aceita selecao ou outros argumentos.');
174
- if (!runtimeOnly && args.some(arg => arg.startsWith('-'))) failure('Use --runtime-only ou uma selecao de ferramentas.');
175
- const runtime = await ensureRuntime(options);
189
+ if (execute && !selected[0]) failure('Use --executar [--no-install] [--plan] <nome-ou-caminho> [argumentos].');
190
+ if (!runtimeOnly && !execute && args.some(arg => arg.startsWith('-'))) failure('Use --runtime-only ou uma selecao de ferramentas.');
191
+ if (execute && ['--help', '-h'].includes(selected[0])) {
192
+ process.stdout.write('Uso: --executar [--no-install] [--plan] <nome-ou-caminho> [argumentos]\n');
193
+ return 0;
194
+ }
195
+ if (execute) {
196
+ const { resolveScript } = await import('../executar-script/SCRIPT.mjs');
197
+ resolveScript(selected[0], { root: options.root || projectRoot });
198
+ }
199
+ const runtime = await ensureRuntime({ ...options, allowDownload: !plan && !noInstall, searchPath: execute });
176
200
  process.stdout.write(`node=${runtime.node}\n`);
177
201
  if (runtimeOnly) return 0;
202
+ if (execute) {
203
+ const run = options.run || spawnSync;
204
+ const root = options.root || projectRoot;
205
+ const env = environmentWithNode(options.environment || process.env, runtime.node);
206
+ const processOptions = { stdio: 'inherit', env, cwd: root, windowsHide: true };
207
+ if (!plan) {
208
+ const diagnostic = run(runtime.node, [join(root, '.pbai4devs/scripts/diagnosticar-ambiente/SCRIPT.mjs')], processOptions);
209
+ if (diagnostic.error) throw diagnostic.error;
210
+ if (diagnostic.status !== 0) return diagnostic.status ?? 1;
211
+ }
212
+ const result = run(runtime.node, [join(root, '.pbai4devs/scripts/executar-script/SCRIPT.mjs'), ...(plan ? ['--plan'] : []), ...selected], processOptions);
213
+ if (result.error) throw result.error;
214
+ return result.status ?? 1;
215
+ }
178
216
  const result = (options.run || spawnSync)(runtime.node, [join(options.root || projectRoot, '.pbai4devs/scripts/preparar-ferramentas/SCRIPT.mjs'), 'setup', ...args], { stdio: 'inherit', env: environmentWithNode(options.environment || process.env, runtime.node) });
179
217
  if (result.error) throw result.error;
180
218
  return result.status ?? 1;
@@ -185,5 +223,8 @@ function isEntrypoint() {
185
223
  catch { return false; }
186
224
  }
187
225
  if (isEntrypoint()) {
188
- try { process.exitCode = await main(); } catch (error) { console.error(`bootstrap: ${error.message}`); process.exitCode = 1; }
226
+ try { process.exitCode = await main(); } catch (error) {
227
+ console.error(`bootstrap: ${error.message}`);
228
+ process.exitCode = Number.isInteger(error.exitCode) && error.exitCode >= 1 && error.exitCode <= 255 ? error.exitCode : 1;
229
+ }
189
230
  }
@@ -1,8 +1,9 @@
1
1
  import test from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
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';
4
+ import { chmodSync, copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
5
+ import { dirname, join, resolve, sep } from 'node:path';
6
+ import { tmpdir } from 'node:os';
6
7
  import { fileURLToPath } from 'node:url';
7
8
  import { spawnSync } from 'node:child_process';
8
9
  import { ensureRuntime, main, validateArchive, validateExtracted } from '../BOOTSTRAP.mjs';
@@ -29,7 +30,7 @@ function fixture(t) {
29
30
  let manifest = `${checksum} ${top}.${extension}\n`, calls = 0;
30
31
  const run = (file, args, options) => {
31
32
  if (args[0] === '-p') {
32
- if (!existsSync(file) || readFileSync(file, 'utf8') !== 'fixture-node') return { status: 1, stdout: '' };
33
+ if (!file.startsWith(root + sep) || !existsSync(file) || readFileSync(file, 'utf8') !== 'fixture-node') return { status: 1, stdout: '' };
33
34
  return { status: 0, stdout: JSON.stringify({ version: '24.1.2', node: file, bun: false }) };
34
35
  }
35
36
  if (args[1] === '--version') return { status: existsSync(args[0]) && readFileSync(args[0], 'utf8') === 'fixture-npm' ? 0 : 1, stdout: '11.0.0\n' };
@@ -37,7 +38,7 @@ function fixture(t) {
37
38
  };
38
39
  const options = { root, node: join(root, 'missing-node'), platform, arch, libc: 'glibc', run,
39
40
  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
+ return { root, options, archive, key, cache: join(root, '.pbai4devs/tools/.cache/runtime/node', key), script: name => write(join(root, '.pbai4devs/scripts', name, 'SCRIPT.mjs'), ''), setManifest: value => { manifest = value; }, calls: () => calls };
41
42
  }
42
43
 
43
44
  test('bootstrap Node extrai arquivo real, verifica npm e reutiliza cache sem rede', async t => {
@@ -92,3 +93,156 @@ test('runtime atual saudável não altera PATH nem grava cache', async t => {
92
93
  const result = await ensureRuntime({ root: f.root, minimum: Number(process.versions.node.split('.')[0]) });
93
94
  assert.equal(result.source, 'current'); assert.equal(process.env.PATH, original); assert.equal(existsSync(f.cache), false);
94
95
  });
96
+
97
+ test('entrada única prepara runtime, diagnostica e encaminha argumentos e status sem shell', async t => {
98
+ const f = fixture(t), calls = [];
99
+ f.script('script com espaços');
100
+ const args = ['script com espaços', '--flag', 'valor com espaços', '$(não executar)', '', '--', 'final'];
101
+ const options = { ...f.options, run: (file, argv, settings) => {
102
+ if (argv[0] === '-p' || argv[1] === '--version' || file === tar) return f.options.run(file, argv, settings);
103
+ calls.push({ file, argv, settings });
104
+ return { status: calls.length === 1 ? 0 : 69 };
105
+ } };
106
+ assert.equal(await main(['--executar', ...args], options), 69);
107
+ assert.equal(f.calls(), 2);
108
+ assert.equal(calls.length, 2);
109
+ assert.equal(calls[0].file, calls[1].file);
110
+ assert.deepEqual(calls[0].argv, [join(f.root, '.pbai4devs/scripts/diagnosticar-ambiente/SCRIPT.mjs')]);
111
+ assert.deepEqual(calls[1].argv, [join(f.root, '.pbai4devs/scripts/executar-script/SCRIPT.mjs'), ...args]);
112
+ assert.equal(calls[0].settings.cwd, f.root);
113
+ assert.equal(calls[1].settings.cwd, f.root);
114
+ assert.equal(calls[1].settings.shell, undefined);
115
+ assert.equal(calls[1].settings.stdio, 'inherit');
116
+ });
117
+
118
+ test('falha de diagnóstico impede execução e propaga status', async t => {
119
+ const f = fixture(t), calls = [];
120
+ f.script('qualquer');
121
+ const options = { ...f.options, run: (file, argv, settings) => {
122
+ if (argv[0] === '-p' || argv[1] === '--version' || file === tar) return f.options.run(file, argv, settings);
123
+ calls.push(argv); return { status: 23 };
124
+ } };
125
+ assert.equal(await main(['--executar', 'qualquer'], options), 23);
126
+ assert.equal(calls.length, 1);
127
+ assert.equal(calls[0][0], join(f.root, '.pbai4devs/scripts/diagnosticar-ambiente/SCRIPT.mjs'));
128
+ });
129
+
130
+ test('plano não instala nem diagnostica; usa apenas runtime disponível', async t => {
131
+ const f = fixture(t), calls = [];
132
+ f.script('qualquer');
133
+ await assert.rejects(main(['--executar', '--plan', 'qualquer'], f.options), /prepare o runtime/);
134
+ assert.equal(f.calls(), 0);
135
+ assert.equal(existsSync(f.cache), false);
136
+ await ensureRuntime(f.options);
137
+ const callsBefore = f.calls();
138
+ const options = { ...f.options, download: async () => { throw new Error('rede indevida'); }, run: (file, argv, settings) => {
139
+ if (argv[0] === '-p' || argv[1] === '--version' || file === tar) return f.options.run(file, argv, settings);
140
+ calls.push(argv); return { status: 0 };
141
+ } };
142
+ assert.equal(await main(['--executar', '--plan', 'qualquer', 'argumento'], options), 0);
143
+ assert.equal(f.calls(), callsBefore);
144
+ assert.deepEqual(calls, [[join(f.root, '.pbai4devs/scripts/executar-script/SCRIPT.mjs'), '--plan', 'qualquer', 'argumento']]);
145
+ });
146
+
147
+ test('--no-install falha sem runtime e usa cache sem rede; preserva flag do alvo', async t => {
148
+ const f = fixture(t), calls = [];
149
+ f.script('alvo');
150
+ await assert.rejects(main(['--executar', '--no-install', 'alvo'], f.options), /prepare o runtime/);
151
+ assert.equal(f.calls(), 0);
152
+ assert.equal(existsSync(f.cache), false);
153
+ await ensureRuntime(f.options);
154
+ const options = { ...f.options, download: async () => { throw new Error('rede indevida'); }, run: (file, argv, settings) => {
155
+ if (argv[0] === '-p' || argv[1] === '--version' || file === tar) return f.options.run(file, argv, settings);
156
+ calls.push(argv); return { status: 0 };
157
+ } };
158
+ assert.equal(await main(['--executar', '--no-install', 'alvo', '--no-install'], options), 0);
159
+ assert.equal(calls.length, 2);
160
+ assert.deepEqual(calls[1], [join(f.root, '.pbai4devs/scripts/executar-script/SCRIPT.mjs'), 'alvo', '--no-install']);
161
+ calls.length = 0;
162
+ assert.equal(await main(['--executar', '--plan', '--no-install', 'alvo'], options), 0);
163
+ assert.deepEqual(calls, [[join(f.root, '.pbai4devs/scripts/executar-script/SCRIPT.mjs'), '--plan', 'alvo']]);
164
+ });
165
+
166
+ test('modo novo encontra Node 24 no PATH com espaços sem baixar runtime', async t => {
167
+ const f = fixture(t), calls = [], bin = join(f.root, 'bin com espaços');
168
+ f.script('alvo');
169
+ mkdirSync(join(bin, 'node_modules/npm/bin'), { recursive: true });
170
+ const executable = join(bin, process.platform === 'win32' ? 'node.exe' : 'node');
171
+ writeFileSync(executable, 'fixture-node');
172
+ if (process.platform !== 'win32') chmodSync(executable, 0o755);
173
+ writeFileSync(join(bin, 'node_modules/npm/bin/npm-cli.js'), 'fixture-npm');
174
+ const options = { ...f.options, environment: { PATH: bin }, download: async () => { throw new Error('rede indevida'); }, run: (file, argv, settings) => {
175
+ if (argv[0] === '-p' || argv[1] === '--version') return f.options.run(file, argv, settings);
176
+ calls.push({ file, argv }); return { status: 0 };
177
+ } };
178
+ const runtime = await ensureRuntime({ ...options, searchPath: true, allowDownload: false });
179
+ assert.equal(runtime.source, 'path');
180
+ assert.equal(runtime.node, executable);
181
+ assert.equal(await main(['--executar', '--no-install', 'alvo', 'argumento com espaços'], options), 0);
182
+ assert.equal(calls.length, 2);
183
+ assert.equal(calls[1].file, executable);
184
+ assert.deepEqual(calls[1].argv.slice(-2), ['alvo', 'argumento com espaços']);
185
+ assert.equal(existsSync(f.cache), false);
186
+ assert.equal(f.calls(), 0);
187
+ });
188
+
189
+ test('entrada única sem alvo falha antes de preparar runtime', async t => {
190
+ const f = fixture(t);
191
+ for (const args of [['--executar'], ['--executar', '--plan'], ['--executar', '--no-install'], ['--executar', '--plan', '--no-install']]) await assert.rejects(main(args, f.options), /nome-ou-caminho/);
192
+ assert.equal(f.calls(), 0);
193
+ assert.equal(existsSync(f.cache), false);
194
+ });
195
+
196
+ test('ajuda não procura runtime, acessa rede ou inicia diagnóstico', async t => {
197
+ const f = fixture(t), unexpected = [];
198
+ const options = { ...f.options, run: (...args) => { unexpected.push(args); return f.options.run(...args); } };
199
+ for (const args of [['--executar', '--help'], ['--executar', '-h'], ['--executar', '--no-install', '--plan', '--help']]) assert.equal(await main(args, options), 0);
200
+ assert.equal(unexpected.length, 0);
201
+ assert.equal(f.calls(), 0);
202
+ assert.equal(existsSync(f.cache), false);
203
+ });
204
+
205
+ test('alvo ou configuração inválida falha antes de runtime, rede e diagnóstico', async t => {
206
+ const f = fixture(t), scripts = join(f.root, '.pbai4devs/scripts');
207
+ const invalid = join(scripts, 'invalido'); mkdirSync(invalid, { recursive: true });
208
+ writeFileSync(join(invalid, 'SCRIPT.mjs'), '');
209
+ writeFileSync(join(invalid, 'SCRIPT.conf'), 'executor=desconhecido\n');
210
+ writeFileSync(join(f.root, 'shell.sh'), '');
211
+ const unexpected = [];
212
+ const options = { ...f.options, run: (...args) => { unexpected.push(args); return f.options.run(...args); } };
213
+ for (const target of ['ausente', 'invalido', 'shell.sh', '../fora.mjs']) {
214
+ await assert.rejects(main(['--executar', target], options), /ausente|SCRIPT.conf|JavaScript|fora/);
215
+ }
216
+ assert.equal(unexpected.length, 0);
217
+ assert.equal(f.calls(), 0);
218
+ assert.equal(existsSync(f.cache), false);
219
+ });
220
+
221
+ test('CLI preserva código do seletor quando alvo não existe', t => {
222
+ const f = fixture(t), target = join(f.root, 'ausente.mjs');
223
+ const selector = join(project, '.pbai4devs/scripts/executar-script/SCRIPT.mjs');
224
+ const bootstrap = join(project, '.pbai4devs/scripts/preparar-ferramentas/BOOTSTRAP.mjs');
225
+ const direct = spawnSync(process.execPath, [selector, target], { encoding: 'utf8', timeout: 30000 });
226
+ const viaBootstrap = spawnSync(process.execPath, [bootstrap, '--executar', target], { encoding: 'utf8', timeout: 30000 });
227
+ assert.equal(direct.status, 2, direct.stderr);
228
+ assert.equal(viaBootstrap.status, direct.status, viaBootstrap.stderr);
229
+ assert.match(viaBootstrap.stderr, /ausente/);
230
+ assert.doesNotMatch(viaBootstrap.stdout, /node=/);
231
+ assert.equal(existsSync(f.cache), false);
232
+ });
233
+
234
+ test('diagnóstico real bem-sucedido não garante dependências do alvo', { timeout: 180000 }, t => {
235
+ const root = mkdtempSync(join(tmpdir(), 'adk-bootstrap-integration-'));
236
+ t.after(() => rmSync(root, { recursive: true, force: true }));
237
+ const cli = spawnSync(process.execPath, [join(project, 'bin/agentdevkit.mjs'), 'init', '--agents', 'codex', '--target', root, '--json'], { encoding: 'utf8', timeout: 120000 });
238
+ assert.equal(cli.status, 0, `${cli.error?.message || ''}\n${cli.stderr}\n${cli.stdout}`);
239
+ const target = join(root, 'probe.cjs');
240
+ writeFileSync(target, `const {spawnSync}=require('node:child_process'); const p=spawnSync('adk-tool-nao-existe-197834',[]); console.log('DEPENDENCIA_AUSENTE='+(p.error?.code==='ENOENT')); process.exit(29);\n`);
241
+ const environment = { ...process.env, PATH: dirname(process.execPath), Path: dirname(process.execPath) };
242
+ const bootstrap = join(root, '.pbai4devs/scripts/preparar-ferramentas/BOOTSTRAP.mjs');
243
+ const result = spawnSync(process.execPath, [bootstrap, '--executar', '--no-install', 'probe.cjs'], { cwd: root, env: environment, encoding: 'utf8', timeout: 30000, maxBuffer: 4 * 1024 * 1024 });
244
+ assert.equal(result.status, 29, `${result.error?.message || ''}\n${result.stderr}\n${result.stdout}`);
245
+ assert.match(result.stdout, /"tools"\s*:/);
246
+ assert.match(result.stdout, /runtime=node/);
247
+ assert.match(result.stdout, /DEPENDENCIA_AUSENTE=true/);
248
+ });
@@ -5,20 +5,30 @@ description: Identifica ferramentas instaladas e executa scripts pelo interpreta
5
5
 
6
6
  # Executar script
7
7
 
8
- No início, confira Node.js 24+ e npm. Com Node 18+, execute o
9
- [bootstrap JavaScript](../../../scripts/preparar-ferramentas/BOOTSTRAP.md)
10
- com `--runtime-only` para preparar o núcleo. Sem Node, instale-o primeiro
11
- pelo mecanismo nativo do SO. Preserve a operação originalmente solicitada.
12
- Faça o preparo pelo usuário e reutilize o caminho absoluto informado, sem
13
- depender de PATH global. Execute
14
- `diagnosticar-ambiente` pelo seletor de [scripts](../../../scripts/README.md).
15
- Reutilize a verificação enquanto o ambiente não mudar.
8
+ Com Node 18+ e npm, use o bootstrap para preparar Node 24+, diagnosticar e
9
+ executar o script solicitado, nessa ordem:
16
10
 
17
- Prefira scripts existentes a reconstruir automações na conversa. Use
18
- `--plan <nome-ou-caminho>` para consultar a execução e confira pré-requisitos,
19
- efeitos e autorização da tarefa. Depois execute com os argumentos originais.
20
- Após o preparo, automações usam `.mjs`, iguais em todos os sistemas. O seletor usa
21
- o Node atual, preserva argumentos e códigos de saída e não instala dependências.
11
+ ```sh
12
+ node .pbai4devs/scripts/preparar-ferramentas/BOOTSTRAP.mjs --executar <script> [argumentos...]
13
+ ```
14
+
15
+ Se o diagnóstico falhar, inspecione a causa; o script não é executado. Seu
16
+ sucesso indica inventário concluído, não dependências necessariamente prontas.
17
+ `--executar --no-install`
18
+ evita apenas o download e instalação do runtime; usa o atual, o primeiro Node
19
+ do PATH ou o cache, se compatível. Não restringe o diagnóstico ou script filho.
20
+ Quando rede ou
21
+ instalações forem proibidas, só execute etapas comprovadamente compatíveis com
22
+ essas restrições ou use isolamento adequado. Sem Node/npm, instale-os pelo
23
+ mecanismo do SO quando autorizado. Preserve a operação e os argumentos pedidos.
24
+
25
+ Prefira scripts existentes. Use `--executar --plan <script>` para consultar a
26
+ execução sem instalar nem diagnosticar; o plano usa as mesmas opções de runtime.
27
+ Confira pré-requisitos, efeitos e autorização, e execute sem `--plan`.
28
+ O seletor direto continua disponível e não faz preparo nem diagnóstico.
29
+ O bootstrap reúne na saída informações do runtime, diagnóstico e execução;
30
+ identifique cada trecho antes de relatar o resultado do script.
31
+ Consulte a [referência](../../../scripts/executar-script/README.md).
22
32
 
23
33
  Se indicar `playwright-mcp`, use o conector conforme a
24
34
  [referência](../../../scripts/testar-navegador/REFERENCE.md). Não execute esse
package/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@ description: Alterações verificáveis por versão do AgentDevKit.
4
4
 
5
5
  # Changelog
6
6
 
7
+ ## 0.0.28 — 2026-11-14
8
+
9
+ - Encadeia preparo do runtime, diagnóstico e execução por `--executar`, com
10
+ plano somente de leitura e opção de não instalar o runtime.
11
+ - Rejeita alvos inválidos antes de preparar o ambiente e reconhece o primeiro
12
+ Node compatível no PATH; documenta os limites de saída e isolamento.
13
+
7
14
  ## 0.0.27 — 2026-11-14
8
15
 
9
16
  - Esclarece que desativar locks opcionais não garante a preservação dos bytes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pathbit/agentdevkit",
3
- "version": "0.0.27",
3
+ "version": "0.0.28",
4
4
  "description": "Shared skills, specifications and workflows for AI coding agents.",
5
5
  "type": "module",
6
6
  "license": "MIT",