terminal-smart-cli 0.90.0 → 0.91.0

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,6 +64,7 @@ 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.
@@ -89,6 +90,7 @@ RULES:
89
90
  - If a dependency is missing, install it (winget/apt/pip/npm) and continue.
90
91
  - 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
92
  - 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.
93
+ - 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
94
  - 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
95
  - 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
96
  - 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.
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.0",
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"