terminal-smart-cli 0.97.30 → 0.97.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/tools.js CHANGED
@@ -39,6 +39,18 @@ function _snapshot(absPath) {
39
39
  // pra compatibilidade (tools.isDestructive segue funcionando pra quem já importava).
40
40
  const { isDestructive } = require('./core');
41
41
 
42
+ // `findstr`, `rg` e `grep` usam o código 1 para informar que a busca foi
43
+ // executada corretamente, mas não encontrou ocorrências. Para o harness isso
44
+ // é evidência de inspeção (por exemplo: confirmar que um texto antigo já não
45
+ // existe), e não uma falha operacional que justifique repetir a mesma ação.
46
+ function _isReadOnlySearchNoMatch(command, code, stdout, stderr) {
47
+ if (Number(code) !== 1 || String(stdout || '').trim() || String(stderr || '').trim()) return false;
48
+ const executable = String(command || '')
49
+ .trim()
50
+ .replace(/^\s*cd\s+(?:\/d\s+)?(?:"[^"]+"|'[^']+'|[^&|]+)\s*(?:&&|&)\s*/i, '');
51
+ return /^(?:findstr|rg|grep)\b/i.test(executable);
52
+ }
53
+
42
54
  // Conteúdo de e-mail/site/arquivo pode tentar convencer o modelo a mandar
43
55
  // segredos para fora. O prompt ajuda, mas a defesa precisa existir também no
44
56
  // executor: se um comando combina transporte de rede com fonte sensível, ele
@@ -523,6 +535,15 @@ function _commandScopeViolation(cmd, confineDir) {
523
535
  if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(m[1])) candidates.push(m[1]);
524
536
  }
525
537
  for (const raw of candidates) {
538
+ // No CMD do Windows, `cd /d C:\\projeto` usa /d como um modificador
539
+ // (troca também a unidade atual), não como caminho Unix absoluto. Sem
540
+ // esta exceção o confinamento recusava uma mudança de pasta totalmente
541
+ // interna antes de o comando chegar ao shell.
542
+ // Opções do CMD também podem ter dois-pontos, por exemplo
543
+ // `findstr /c:"texto"`. Não são caminhos Unix nem tentativas de escapar da
544
+ // raiz. Sem esta exceção uma busca contendo `</script>` era lida como
545
+ // redirecionamento e o `/c:` seguinte virava um falso caminho externo.
546
+ if (process.platform === 'win32' && /^\/[a-z](?::|$)/i.test(raw)) continue;
526
547
  const abs = path.resolve(root, raw);
527
548
  if (!_scopeContains(root, abs)) {
528
549
  return `CAPABILITY_DENIED: comando mutante referencia "${raw}", fora da raiz autorizada "${root}".`;
@@ -653,6 +674,11 @@ async function execute(name, input, opts = {}) {
653
674
  }
654
675
  const _outsideScope = (p) => {
655
676
  if (!opts.confineDir) return false;
677
+ // Uma skill só pode ser lida fora do projeto se o chamador a tiver
678
+ // explicitamente anunciado nesta rodada. Isso permite seguir uma skill
679
+ // instalada sem transformar ~/.ts, ~/.codex etc. em acesso geral.
680
+ if (name === 'ler_arquivo' && Array.isArray(opts.trustedReadPaths)
681
+ && opts.trustedReadPaths.some(allowed => path.resolve(String(allowed)) === path.resolve(String(p)))) return false;
656
682
  return !_scopeContains(opts.confineDir, p);
657
683
  };
658
684
  const _scopeError = (p) => ({ erro: `CAPABILITY_DENIED: "${path.resolve(p)}" está fora da raiz autorizada "${path.resolve(opts.confineDir)}".` });
@@ -685,6 +711,11 @@ async function execute(name, input, opts = {}) {
685
711
  // B18: mascara chave/token ANTES de virar contexto do modelo e linha de log.
686
712
  const _rd = require('./core').redactSecrets;
687
713
  const r = { stdout: _rd(c.out), stderr: _rd(compactor.stripAnsi(err || '').slice(0, 1500)), codigo: e?.code ?? 0 };
714
+ if (_isReadOnlySearchNoMatch(comandoExecutado, r.codigo, r.stdout, r.stderr)) {
715
+ r.codigo = 0;
716
+ r.no_match = true;
717
+ r.stdout = 'Nenhuma ocorrência encontrada.';
718
+ }
688
719
  if (c.note) r.aviso = c.note;
689
720
  if (normalized) r.normalizado = normalized.note;
690
721
  if (!r.stdout.trim() && !r.stderr.trim() && r.codigo === 0 && /python3? -c/.test(comando) && comando.includes('\n'))
@@ -1165,4 +1196,4 @@ async function execute(name, input, opts = {}) {
1165
1196
  }
1166
1197
 
1167
1198
  module.exports = { DEFS, execute, isDestructive, selfDestructiveReason, secretExfiltrationReason, buildProjectMap,
1168
- _test: { shellGotcha: _shellGotcha, normalizeWindowsReadOnlyCommand: _normalizeWindowsReadOnlyCommand } };
1199
+ _test: { shellGotcha: _shellGotcha, normalizeWindowsReadOnlyCommand: _normalizeWindowsReadOnlyCommand, commandScopeViolation: _commandScopeViolation, isReadOnlySearchNoMatch: _isReadOnlySearchNoMatch } };
package/lib/verify.js CHANGED
@@ -17,8 +17,21 @@ function vCommand(c, cwd) {
17
17
  const want = c.exit != null ? Number(c.exit) : 0;
18
18
  try {
19
19
  const out = execSync(c.cmd, { cwd, encoding: 'utf8', timeout: (c.timeout_s || 120) * 1000, stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
20
+ const output = String(out || '');
21
+ // Um script como `echo "No tests yet" && exit 0` não é uma prova de saúde.
22
+ // Para critérios que afirmam rodar testes, trate explicitamente essa saída
23
+ // como falha, mesmo que o processo tenha saído com código zero.
24
+ const isTestCommand = /(^|\s)(npm\s+test|pnpm\s+test|yarn\s+test|node\s+.*test)/i.test(String(c.cmd || ''));
25
+ const noTests = /\b(no tests? yet|no tests? (?:found|run)|0 tests? (?:found|run))\b/i.test(output);
26
+ if (want === 0 && isTestCommand && noTests) {
27
+ return { ok: false, detail: 'teste sem casos executados (falso positivo)', errorClass: 'verification_failed', evidence: output.slice(-200).replace(/\s+/g, ' ') };
28
+ }
29
+ const reportedFailure = /(^|\n)\s*(?:✗|FAIL(?:\s|:|$)|AssertionError\b)/im.test(output);
30
+ if (want === 0 && isTestCommand && reportedFailure) {
31
+ return { ok: false, detail: 'teste reportou falha apesar de exit 0 (falso positivo)', errorClass: 'verification_failed', evidence: output.slice(-300).replace(/\s+/g, ' ') };
32
+ }
20
33
  const ok = want === 0;
21
- return { ok, detail: ok ? 'exit 0' : `exit 0 mas esperava ${want}`, evidence: String(out).slice(-200).replace(/\s+/g, ' ') };
34
+ return { ok, detail: ok ? 'exit 0' : `exit 0 mas esperava ${want}`, evidence: output.slice(-200).replace(/\s+/g, ' ') };
22
35
  } catch (e) {
23
36
  const code = typeof e.status === 'number' ? e.status : 1;
24
37
  const ok = code === want;
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "terminal-smart-cli",
3
- "version": "0.97.30",
3
+ "version": "0.97.41",
4
4
  "description": "Terminal Smart no seu terminal — pergunte, analise logs por pipe e orquestre agentes de IA. Comando: ts",
5
5
  "bin": {
6
6
  "ts": "bin/ts.js"
7
7
  },
8
8
  "scripts": {
9
- "test": "node test/core.test.js && node test/windows-shell-normalization.test.js && node test/gateways.test.js && node test/agent-recovery-guard.test.js && node test/agent-plan-model-contract.test.js && node test/agent-mission-limits.test.js && node test/agent-external-approval.test.js && node test/agent-prompt-injection.test.js && node test/file-concurrency.test.js && node test/mission-observability.test.js && node test/audit-packs.test.js && node test/intelligence-core.test.js && node test/cloud-slug.test.js && node test/eval-model.test.js && node test/project-cache.test.js && node test/memory-bus.test.js && node test/capabilities.test.js && node test/mcp-e2e.test.js && node test/erros.test.js && node test/evolution-telemetry.test.js && node test/owner-audit.test.js && node test/capability-pack.test.js && node test/video-generation.test.js && node test/image-job.test.js && node test/byok.test.js && node test/conhecimento.test.js && node test/policy.test.js && node test/temas.test.js && node test/skill-index.test.js && node test/doctor.test.js && node test/google-workspace-tools.test.js && node test/office-editors.test.js"
9
+ "test": "node test/core.test.js && node test/conversation-scope.test.js && node test/windows-shell-normalization.test.js && node test/gateways.test.js && node test/agent-recovery-guard.test.js && node test/agent-plan-model-contract.test.js && node test/agent-mission-limits.test.js && node test/agent-external-approval.test.js && node test/agent-prompt-injection.test.js && node test/file-concurrency.test.js && node test/mission-observability.test.js && node test/audit-packs.test.js && node test/intelligence-core.test.js && node test/cloud-slug.test.js && node test/eval-model.test.js && node test/project-cache.test.js && node test/memory-bus.test.js && node test/capabilities.test.js && node test/mcp-e2e.test.js && node test/erros.test.js && node test/evolution-telemetry.test.js && node test/owner-audit.test.js && node test/capability-pack.test.js && node test/video-generation.test.js && node test/image-job.test.js && node test/byok.test.js && node test/conhecimento.test.js && node test/policy.test.js && node test/temas.test.js && node test/skill-index.test.js && node test/doctor.test.js && node test/google-workspace-tools.test.js && node test/office-editors.test.js"
10
10
  },
11
11
  "files": [
12
12
  "bin",