terminal-smart-cli 0.90.0 → 0.91.1

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/bin/ts.js CHANGED
@@ -532,6 +532,10 @@ async function chatRepl() {
532
532
  // no REPL alteram/mostram; cada msg roteada pro agente resolve caminhos daqui.
533
533
  const _pp = require('path'), _oo = require('os'), _ff = require('fs');
534
534
  let agentCwd = process.cwd();
535
+ // Autonomia da SESSÃO pra missões grandes: null = ainda não perguntei nesta sessão;
536
+ // 'full' = faço tudo até o fim aprovando o necessário; 'step' = peço OK nas ações que
537
+ // mudam coisas. Perguntado UMA vez quando aparece a 1ª missão grande, depois reusado.
538
+ let _autonomia = null;
535
539
  console.log(' ' + C.dim((cfg.lang === 'en' ? 'working folder: ' : 'pasta de trabalho: ') + agentCwd) + C.dim(cfg.lang === 'en' ? ' · cd <folder> to change' : ' · cd <pasta> pra trocar') + '\n');
536
540
  // `ts --continuar`: avisa que a PRÓXIMA mensagem retoma a sessão anterior desta pasta
537
541
  // (o agentCmd lê --continuar do argv e carrega o histórico). REPL interativo → não cai no shell.
@@ -590,8 +594,26 @@ async function chatRepl() {
590
594
  // ROTEADOR: linguagem natural pura — o sistema decide o caminho e AVISA
591
595
  const r = await router.route(msg, token);
592
596
  if (r.dest === 'agente') {
597
+ // MISSÃO GRANDE ("coloque online e se vira"): o sistema RECONHECE sozinho e,
598
+ // em vez de rodar o modo curto (que pára cedo), roda até o fim. Na 1ª missão
599
+ // grande da sessão pergunta UMA vez COMO conduzir — em linguagem natural, com
600
+ // as opções explicadas — e reusa a escolha depois. Zero flags pro usuário.
601
+ let _agOpts = { askFn: (q) => ask1(' ' + q), cwd: agentCwd, onCwd: (c) => { agentCwd = c; } };
602
+ if (r.scope === 'big') {
603
+ const en = cfg.lang === 'en';
604
+ if (_autonomia === null) {
605
+ console.log('\n ' + C.indigo(en ? 'This looks like a big job — I can take it end to end.' : 'Isso parece uma missão grande — dá pra eu levar do início ao fim.'));
606
+ console.log(' ' + C.cyan('1') + C.dim(en ? ' I do it all, approving what I need, and report at the end (recommended)' : ' Faço tudo sozinho, aprovando o que precisar, e te aviso no fim (recomendado)'));
607
+ console.log(' ' + C.cyan('2') + C.dim(en ? ' Step by step — I ask your OK before actions that change things' : ' Passo a passo — peço seu OK antes de ações que mudam coisas'));
608
+ const _a = String(await ask1(' ' + (en ? 'choice (1/2): ' : 'escolha (1/2): ') || '') || '').trim();
609
+ _autonomia = _a.startsWith('2') ? 'step' : 'full';
610
+ console.log(' ' + C.dim(en ? (_autonomia === 'full' ? '→ autonomous mode on for this session' : '→ step-by-step for this session') : (_autonomia === 'full' ? '→ modo autônomo ligado nesta sessão' : '→ passo a passo nesta sessão')) + '\n');
611
+ }
612
+ _agOpts.maxIter = 60; // missão grande roda até o fim
613
+ _agOpts.autoAll = (_autonomia === 'full'); // "faço tudo" = aprova o necessário
614
+ }
593
615
  console.log(' ' + C.cyan('⚙') + ' ' + C.dim(T.route_agent));
594
- await agentCmd([msg], { askFn: (q) => ask1(' ' + q), cwd: agentCwd, onCwd: (c) => { agentCwd = c; } });
616
+ await agentCmd([msg], _agOpts);
595
617
  } else if (r.dest === 'run') {
596
618
  console.log(' ' + C.indigo('◆') + ' ' + C.dim(T.route_run));
597
619
  await runFlow(msg, { askFn: (q) => ask1(' ' + q) });
@@ -827,7 +849,7 @@ async function worktreesCmd(words) {
827
849
  }
828
850
 
829
851
  // ── Agente LOCAL (Fase 2): executa a tarefa NESTA máquina com ferramentas reais ──
830
- async function agentCmd(words, { askFn, cwd: cwdIn = null, onCwd = null } = {}) {
852
+ async function agentCmd(words, { askFn, cwd: cwdIn = null, onCwd = null, maxIter: maxIterIn = null, autoAll: autoAllIn = false } = {}) {
831
853
  const token = needToken();
832
854
  // flags que levam VALOR: o filtro remove só a flag (começa com "-"), deixando o VALOR nos
833
855
  // words → tira o valor daqui pra ele não virar parte do texto da tarefa.
@@ -912,9 +934,9 @@ async function agentCmd(words, { askFn, cwd: cwdIn = null, onCwd = null } = {})
912
934
  // --passos N (ou --steps): quantas iterações o agente pode fazer numa run (default 15, teto 120).
913
935
  // Pra missões grandes ("faça tudo de uma vez") sem o pára-e-continua.
914
936
  const _pi = process.argv.findIndex(a => a === '--passos' || a === '--steps' || a === '--max-passos');
915
- const _maxPassos = _pi >= 0 ? Number(process.argv[_pi + 1]) : _inlinePassos;
937
+ const _maxPassos = _pi >= 0 ? Number(process.argv[_pi + 1]) : (_inlinePassos ?? maxIterIn ?? undefined);
916
938
  out = await agent.run(task, {
917
- token, lang: cfg.lang || 'pt', yes: YES, autoAll: (YOLO || _inlineYolo), model, priorMessages, cwd: startCwd, readOnly, plan, browser: useBrowser, maxIter: _maxPassos,
939
+ token, lang: cfg.lang || 'pt', yes: YES, autoAll: (YOLO || _inlineYolo || autoAllIn), model, priorMessages, cwd: startCwd, readOnly, plan, browser: useBrowser, maxIter: _maxPassos,
918
940
  onThinking: (p) => {
919
941
  if (p && typeof p === 'object') {
920
942
  _live.steps = p.steps ?? _live.steps; _live.inTok = p.inTok ?? _live.inTok; _live.outTok = p.outTok ?? _live.outTok;
package/lib/agent.js CHANGED
@@ -64,12 +64,15 @@ REGRAS:
64
64
  - Se faltar dependência, instale (winget/apt/pip/npm) e prossiga.
65
65
  - LINUX/apt: NUNCA rode "apt-get install" cru. (a) Em máquina nova o unattended-upgrades segura o lock e a instalação PENDURA. (b) Install que baixa VÁRIOS pacotes (gcc, mariadb, tomcat…) leva mais de 60s e MORRE no timeout padrão → nada instala. Instale SEMPRE assim, com timeout LONGO: chame executar_comando com timeout_s: 300 e comando "sudo DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=600 install -y <pacotes>" (Lock::Timeout ESPERA o lock; noninteractive+-y evita prompts sem TTY; timeout_s:300 dá 5min pro download). PPA com "add-apt-repository -y" + "apt-get update". Se um comando de install voltar vazio/sem efeito, quase sempre foi timeout curto — refaça com timeout_s: 300.
66
66
  - SUBIR DAEMON/SERVIÇO em background numa VPS (via executar_remoto): NUNCA rode o binário direto nem use só "nohup cmd &" — o canal SSH fica ESPERANDO o processo (que não termina, é um serviço) e dá TIMEOUT; aí você acha que travou e REPETE (loop). Use SEMPRE este padrão, que desconecta o processo do SSH e RETORNA na hora: "setsid nohup ./daemon args >log 2>&1 </dev/null & echo iniciado" (o setsid + redirecionar TODOS os fds, INCLUSIVE stdin com </dev/null, é o que solta o canal). Depois confirme que subiu com pgrep/ss numa chamada SEPARADA. Se mesmo assim o comando de start der timeout, o daemon provavelmente está falhando na inicialização (rode-o em FOREGROUND com timeout curto pra ver o erro real e conserte) — NÃO repita o mesmo start.
67
+ - BUILD/COMPILE PESADO em host remoto (make, gcc/g++, cargo, npm build): compilar é um dos jeitos mais fáceis de DERRUBAR uma máquina apertada — o linker/compilador come RAM e, sem memória, o kernel mata processos (ou a VPS inteira trava e o ping cai). ANTES de compilar: cheque a RAM com "free -m". Se houver POUCA livre (< ~1,5 GB) ou for build grande (dezenas de .cpp, binário nativo pesado): (1) crie SWAP primeiro — "sudo fallocate -l 4G /swap && sudo chmod 600 /swap && sudo mkswap /swap && sudo swapon /swap"; (2) compile SERIAL ("make -j1", nunca "-j4" numa máquina de 2 GB); (3) rode o build DESTACADO do SSH em background ("setsid nohup make -j1 ... >/tmp/build.log 2>&1 </dev/null & echo iniciado") e acompanhe com "tail /tmp/build.log" em chamadas separadas — build de minutos preso no canal SSH dá timeout e vira falso "travou". Recompilar do zero é ÚLTIMO recurso: primeiro procure o binário/arquivo PRONTO que casa (fonte autêntica). Se a máquina cair no meio, NÃO fique só pingando — diga ao usuário pra REINICIAR a instância (reboot, que preserva o IP; parar/stop pode TROCAR o IP público).
67
68
  - FONTE AUTÊNTICA antes de improvisar: antes de GERAR do zero um arquivo estruturado (config, schema SQL, .conf, dados, manifesto), PROCURE se já existe a versão real — no projeto atual, no pack/instalação de referência que o usuário citou, ou numa instalação funcionante. Se existir, USE-A (copie/adapte o mínimo). A versão inventada de memória quase sempre sai sutilmente errada/incompleta (falta uma linha, uma procedure, um valor). Só improvise se comprovadamente não existir — e nesse caso AVISE que é uma reconstrução (pode ter erro) e peça pra validar.
68
69
  - PRIVILÉGIO MÍNIMO em infra: pra dar acesso de uma aplicação a um serviço (banco, broker), crie uma CREDENCIAL DEDICADA com o mínimo de permissão (ex: CREATE USER 'app' + GRANT só no schema necessário) e aponte a app pra ela. NUNCA altere a autenticação/config GLOBAL existente (root/admin) só pra uma app conectar — isso afeta TUDO que já usa aquele acesso (backups, outros serviços, seu próprio acesso). Se precisar MESMO mudar algo global, AVISE explicitamente o que mais será afetado e por quê.
69
70
  - NUNCA peça segredo ao usuário: jamais peça senha/chave/token/credencial no chat. Se uma operação remota (ssh/scp) falhar por autenticação, REUSE a conexão/chave que JÁ funcionou nesta sessão (mesmo host/user/chave — não reconstrua o comando do zero esquecendo o -i da chave). Se ainda assim não conectar, peça pro usuário EXECUTAR a ação — nunca entregar o segredo.
70
71
  - NÃO DESISTA sem TENTAR: é PROIBIDO responder "não tenho acesso" / "não consigo" / "preciso que você me passe X" enquanto houver uma ferramenta que você ainda não usou pra tentar. Antes de declarar que algo é impossível, AJA: conecte (conectar_vps), procure (buscar_arquivos, ou grep/find via executar_comando/executar_remoto), leia (ler_arquivo). Ex.: pediram pra ler um código-fonte que "você não tem"? Se há uma VPS/pasta onde ele pode estar, CONECTE e procure (grep -rn "<símbolo>" <dir>) ANTES de dizer que não tem. Só afirme que não conseguiu DEPOIS de ter tentado de fato e mostre o erro/saída REAL que te barrou.
71
72
  - NUNCA pergunte "onde está o arquivo X?" / "há um diretório com Y?" / "posso gerar Z?" sem ANTES ter PROCURADO de fato: rode find/buscar_arquivos por ele em TODOS os lugares plausíveis — na VPS conectada (find / -name "arquivo" 2>/dev/null, e nas pastas do projeto) E na máquina local do usuário (buscar_arquivos, incluindo o pack/instalação de origem que ele citou). Se um config/arquivo obrigatório faltar mesmo depois de procurar, tente ACHAR um exemplo/modelo (outro .conf parecido, o default no código-fonte) e GERAR a partir dele — só peça ajuda ao usuário como ÚLTIMO recurso, dizendo exatamente onde já procurou e não achou.
72
73
  - BINÁRIO NATIVO: ao subir/instalar um executável numa máquina, valide com file (arquitetura: 32 vs 64-bit) E ldd (as bibliotecas resolvem?) ANTES de declarar "pronto/instalado" — "está no lugar" NÃO é "roda". Se ldd mostrar "not found", instale a lib faltante e revalide.
74
+ - WINDOWS / SHELL BASH: o shell aqui é o bash do Git, NÃO o cmd. (a) "node -e"/"python -c" com várias linhas ou com import() FALHA — escreva um arquivo .mjs/.py e rode "node arquivo.mjs"/"python arquivo.py". (b) pra ESPERAR use "sleep N" (NUNCA "timeout /t", que é do cmd e quebra no bash). (c) comandos que só existem no cmd (dir, type, if exist) rode via "cmd /c \"...\"". (d) pra matar um servidor/processo de teste, ache o PID pela PORTA ("netstat -ano | findstr :PORTA" → "taskkill /PID <pid> /F") — NUNCA por imagem ("taskkill /IM node.exe" mata processos não relacionados, inclusive os do usuário).
75
+ - PROVE O CAMINHO REAL antes de declarar "pronto/corrigido": "testes de API passando" NÃO é "o app funciona". Se há UI ou rotas condicionais (por papel/role, por filtro), rode o FLUXO EXATO do usuário afetado — a tela/rota que quebrava — e veja o resultado; numa entrega WEB, verifique com olhos (navegador). Em fix de código com ramos (SQL com subqueries/parâmetros, condicional por role): conte placeholder-a-placeholder e rode de novo o caminho que falhava. Só diga "corrigido" COM a prova verde do caminho real — e NÃO peça pro usuário rodar a prova que você mesmo consegue rodar. Se subir um server de teste, ENCERRE-o (por PID) ao terminar — não deixe processo órfão na porta.
73
76
  - ESTILO DA RESPOSTA (importante): escreva em linguagem NATURAL e direta, como explicando pra uma pessoa — evite jargão e detalhe técnico interno que não interessa a quem só quer o resultado. NÃO use emojis decorativos (nada de 🎮📡🔬🌿✅❌ etc.); pra marcar certo/errado use SÓ os símbolos ✓ (deu certo) ou ✗ (falhou). Frases curtas; comando/código/caminho sempre em bloco de código. Não repita o que já apareceu nos passos acima.
74
77
  - Termine SEMPRE com um resumo curto: o que foi feito, resultado e caminhos de arquivos criados/alterados.
75
78
  - Responda no idioma do usuário (padrão: português do Brasil).`
@@ -89,12 +92,15 @@ RULES:
89
92
  - If a dependency is missing, install it (winget/apt/pip/npm) and continue.
90
93
  - LINUX/apt: NEVER run a bare "apt-get install" — on a freshly-created machine unattended-upgrades holds the apt lock and the install HANGS silently (no output). ALWAYS install like this: "sudo DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=600 install -y <packages>" (the -o Lock::Timeout WAITS for the lock up to 10min instead of hanging; noninteractive+-y avoids prompts that block without a TTY). Use "add-apt-repository -y" + "apt-get update" for PPAs. A command that doesn't return in ~1min is likely stuck on the lock/prompt — don't wait forever.
91
94
  - STARTING A DAEMON/SERVICE in the background on a VPS (via executar_remoto): NEVER run the binary directly or use just "nohup cmd &" — the SSH channel WAITS on the process (a service never exits) and TIMES OUT; then you think it hung and RETRY (loop). ALWAYS use this pattern, which detaches the process from SSH and RETURNS immediately: "setsid nohup ./daemon args >log 2>&1 </dev/null & echo started" (setsid + redirecting ALL fds, INCLUDING stdin with </dev/null, is what frees the channel). Then confirm it's up with pgrep/ss in a SEPARATE call. If the start still times out, the daemon is likely failing at init (run it in the FOREGROUND with a short timeout to see the real error and fix it) — do NOT repeat the same start.
95
+ - HEAVY BUILD/COMPILE on a remote host (make, gcc/g++, cargo, npm build): compiling is one of the easiest ways to TAKE DOWN a tight machine — the linker/compiler eats RAM and, with none left, the kernel kills processes (or the whole VPS freezes and ping drops). BEFORE compiling: check RAM with "free -m". If little is free (< ~1.5 GB) or it's a big build (dozens of .cpp, heavy native binary): (1) create SWAP first — "sudo fallocate -l 4G /swap && sudo chmod 600 /swap && sudo mkswap /swap && sudo swapon /swap"; (2) compile SERIAL ("make -j1", never "-j4" on a 2 GB box); (3) run the build DETACHED from SSH in the background ("setsid nohup make -j1 ... >/tmp/build.log 2>&1 </dev/null & echo started") and follow it with "tail /tmp/build.log" in separate calls — a multi-minute build stuck on the SSH channel times out and looks like a false "hang". Recompiling from scratch is a LAST resort: first look for the ready-made matching binary/file (authentic source). If the machine dies mid-build, do NOT just keep pinging — tell the user to REBOOT the instance (reboot preserves the IP; stop/start may CHANGE the public IP).
92
96
  - AUTHENTIC SOURCE before improvising: before GENERATING a structured file from scratch (config, SQL schema, .conf, data, manifest), SEARCH for whether a real version already exists — in the current project, in the reference pack/install the user mentioned, or in a working install. If it exists, USE IT (copy/adapt minimally). A version invented from memory almost always comes out subtly wrong/incomplete (a missing line, a missing procedure, a wrong value). Only improvise if it truly doesn't exist — and then WARN that it's a reconstruction (may have errors) and ask to validate.
93
97
  - LEAST PRIVILEGE in infra: to give an app access to a service (DB, broker), create a DEDICATED credential with minimal permission (e.g. CREATE USER 'app' + GRANT on the needed schema only) and point the app at it. NEVER alter the existing GLOBAL auth/config (root/admin) just to make an app connect — that affects EVERYTHING already using that access (backups, other services, your own access). If you truly must change something global, explicitly WARN what else will be affected and why.
94
98
  - NEVER ask the user for a secret: never ask for a password/key/token/credential in chat. If a remote op (ssh/scp) fails on auth, REUSE the connection/key that ALREADY worked this session (same host/user/key — don't rebuild the command dropping the -i key flag). If it still won't connect, ask the user to PERFORM the action — never to hand over the secret.
95
99
  - DON'T GIVE UP without TRYING: it is FORBIDDEN to answer "I don't have access" / "I can't" / "I need you to give me X" while there's a tool you haven't used yet to try. Before declaring something impossible, ACT: connect (conectar_vps), search (buscar_arquivos, or grep/find via executar_comando/executar_remoto), read (ler_arquivo). E.g. asked to read source code you "don't have"? If there's a VPS/folder where it might live, CONNECT and search (grep -rn "<symbol>" <dir>) BEFORE saying you don't have it. Only claim you couldn't do it AFTER actually trying, and show the REAL error/output that blocked you.
96
100
  - NEVER ask "where is file X?" / "is there a folder with Y?" / "may I generate Z?" without having actually SEARCHED first: run find/buscar_arquivos for it in EVERY plausible place — on the connected VPS (find / -name "file" 2>/dev/null, and the project folders) AND on the user's local machine (buscar_arquivos, including the source pack/install they mentioned). If a required config/file is still missing after searching, try to FIND a template/example (a similar .conf, the default in the source) and GENERATE from it — only ask the user as a LAST resort, stating exactly where you already looked and didn't find it.
97
101
  - NATIVE BINARY: when uploading/installing an executable on a machine, validate with file (architecture: 32 vs 64-bit) AND ldd (do the libraries resolve?) BEFORE declaring "done/installed" — "it's in place" is NOT "it runs". If ldd shows "not found", install the missing lib and re-validate.
102
+ - WINDOWS / BASH SHELL: the shell here is Git bash, NOT cmd. (a) "node -e"/"python -c" with multiple lines or with import() FAILS — write a .mjs/.py file and run "node file.mjs"/"python file.py". (b) to WAIT use "sleep N" (NEVER "timeout /t", which is cmd and breaks in bash). (c) cmd-only commands (dir, type, if exist) run via "cmd /c \"...\"". (d) to kill a test server/process, find the PID by PORT ("netstat -ano | findstr :PORT" → "taskkill /PID <pid> /F") — NEVER by image ("taskkill /IM node.exe" kills unrelated processes, including the user's).
103
+ - PROVE THE REAL PATH before declaring "done/fixed": "API tests passing" is NOT "the app works". If there's UI or conditional routes (by role, by filter), run the EXACT flow of the affected user — the screen/route that was breaking — and see the result; on a WEB deliverable, verify with eyes (browser). In a code fix with branches (SQL with subqueries/params, role conditionals): count placeholder-by-placeholder and re-run the path that was failing. Only say "fixed" WITH green proof of the real path — and do NOT ask the user to run a proof you can run yourself. If you start a test server, SHUT IT DOWN (by PID) when done — don't leave an orphan process on the port.
98
104
  - RESPONSE STYLE (important): write in NATURAL, plain language, like explaining to a person — avoid jargon and internal technical detail that doesn't matter to someone who just wants the result. NO decorative emojis (no 🎮📡🔬✅❌ etc.); to mark pass/fail use ONLY the symbols ✓ (worked) or ✗ (failed). Short sentences; commands/code/paths always in a code block. Don't repeat what already showed up in the steps above.
99
105
  - ALWAYS end with a short summary: what was done, the result, and paths of created/changed files.
100
106
  - Answer in the user's language.`);
package/lib/router.js CHANGED
@@ -36,6 +36,19 @@ function heuristic(msg) {
36
36
  return null; // ambíguo → classificador decide
37
37
  }
38
38
 
39
+ // ESCOPO da missão: o usuário quer que o agente "se vire" do começo ao fim, sem
40
+ // ficar mandando texto? Sinais de delegação total + objetivo amplo. Quando 'big', o
41
+ // REPL roda a missão com MUITO mais passos e oferece o modo autônomo — SEM o usuário
42
+ // precisar saber de flag (--passos/--yolo) nem do comando "meta".
43
+ const AUTONOMO_RE = /(\bse vir[ae]\b|\bse vira\b|fa[çc]a tudo|faz tudo|resolv[ae] tudo|do (começo|come[çc]o) ao fim|at[ée] o (fim|final)|sozinh[oa]\b|por conta pr[óo]pria|n[ãa]o quero (ficar )?(mandando|digitando|repetindo|falando)|tudo (pronto|prontinho)|deix[ae] (tudo |ele |isso )?pronto|coloqu?e? (isso |ele |o jogo |tudo |o site |a? ?aplica[çc][ãa]o )?(online|no ar|de p[ée]|pra (eu )?(jogar|usar))|sub[ae] (tudo|o servidor|ele)|quanto (tempo|passos?) (precisar|for preciso)|uma vez e (j[áa] era|pronto)|do zero ao fim)/i;
44
+ function scope(msg) {
45
+ const t = String(msg || '').trim();
46
+ if (AUTONOMO_RE.test(t)) return 'big';
47
+ // objetivo longo e encadeado (vários passos numa frase) também é missão grande
48
+ if (t.length > 220 && ((t.match(/[.;]\s/g) || []).length + (t.match(/\be\b/gi) || []).length) >= 4) return 'big';
49
+ return 'normal';
50
+ }
51
+
39
52
  let _keyCache = null;
40
53
  async function _aiKey(token) {
41
54
  if (_keyCache) return _keyCache;
@@ -74,9 +87,10 @@ async function classify(msg, token) {
74
87
 
75
88
  // API principal: destino + por qual camada decidiu (pra linha de transparência)
76
89
  async function route(msg, token) {
90
+ const s = scope(msg);
77
91
  const h = heuristic(msg);
78
- if (h) return { dest: h, via: 'heuristica' };
79
- return { dest: await classify(msg, token), via: 'ia' };
92
+ if (h) return { dest: h, via: 'heuristica', scope: s };
93
+ return { dest: await classify(msg, token), via: 'ia', scope: s };
80
94
  }
81
95
 
82
- module.exports = { route, heuristic };
96
+ module.exports = { route, heuristic, scope };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminal-smart-cli",
3
- "version": "0.90.0",
3
+ "version": "0.91.1",
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"