terminal-smart-cli 0.83.0 → 0.85.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
@@ -883,12 +883,30 @@ async function agentCmd(words, { askFn, cwd: cwdIn = null, onCwd = null } = {})
883
883
  const sp = (streamJson || _noTTY) ? { text() {}, start() {}, stop() {} } : ui.spinner(T.agent_thinking).start();
884
884
  if (_noTTY && !streamJson) console.log(' ' + C.dim(T.agent_thinking));
885
885
  const t0 = Date.now();
886
+ // STATUS LINE ao vivo (estilo Claude/Verboo): tempo · modelo · tokens · passo — atualiza a cada 1s.
887
+ const _live = { steps: 0, inTok: 0, outTok: 0, model: model || 'auto' };
888
+ const _fmtTime = (ms) => { const s = Math.floor(ms / 1000), m = Math.floor(s / 60); return m > 0 ? `${m}m${String(s % 60).padStart(2, '0')}s` : `${s}s`; };
889
+ const _fmtTok = (n) => n >= 1000 ? (n / 1000).toFixed(1).replace('.0', '') + 'k' : String(n);
890
+ const _statusLine = () => {
891
+ const tok = _live.inTok + _live.outTok;
892
+ const parts = [_fmtTime(Date.now() - t0), String(_live.model).slice(0, 22)];
893
+ if (tok > 0) parts.push('↓ ' + _fmtTok(tok) + ' tok');
894
+ if (_live.steps > 0) parts.push('passo ' + _live.steps);
895
+ return T.agent_thinking + ' · ' + parts.join(' · '); // texto puro; o spinner aplica a cor
896
+ };
897
+ const _liveTimer = (!_noTTY && !streamJson) ? setInterval(() => sp.text(_statusLine()), 1000) : null;
886
898
  if (streamJson) _emit(core.AgentEvents.systemInit({ model: model || 'auto', cwd: startCwd, mode: plan ? 'plan' : readOnly ? 'ask' : 'agent' }));
887
899
  let out;
888
900
  try {
889
901
  out = await agent.run(task, {
890
902
  token, lang: cfg.lang || 'pt', yes: YES, autoAll: YOLO, model, priorMessages, cwd: startCwd, readOnly, plan, browser: useBrowser,
891
- onThinking: () => sp.text(T.agent_thinking),
903
+ onThinking: (p) => {
904
+ if (p && typeof p === 'object') {
905
+ _live.steps = p.steps ?? _live.steps; _live.inTok = p.inTok ?? _live.inTok; _live.outTok = p.outTok ?? _live.outTok;
906
+ if (p.model && p.model !== 'smart') _live.model = p.model;
907
+ }
908
+ sp.text(_statusLine());
909
+ },
892
910
  onStep: ({ name, detail, blocked, loop, retry, auto }) => {
893
911
  if (streamJson) { _emit(core.AgentEvents.tool({ subtype: retry ? 'retry' : loop ? 'loop' : blocked ? 'blocked' : auto ? 'auto_approved' : 'started', tool: name, detail: detail || '' })); return; }
894
912
  sp.stop();
@@ -919,7 +937,8 @@ async function agentCmd(words, { askFn, cwd: cwdIn = null, onCwd = null } = {})
919
937
  sp.start();
920
938
  },
921
939
  });
922
- } catch (e) { sp.stop(); throw e; }
940
+ } catch (e) { if (_liveTimer) clearInterval(_liveTimer); sp.stop(); throw e; }
941
+ if (_liveTimer) clearInterval(_liveTimer);
923
942
  sp.stop();
924
943
  // cwd EFETIVO (o agente pode ter feito cd/mudar_diretorio) → devolve ao REPL e persiste.
925
944
  const effCwd = out.cwd || startCwd;
@@ -967,7 +986,10 @@ async function agentCmd(words, { askFn, cwd: cwdIn = null, onCwd = null } = {})
967
986
  ? ' · ' + (cfg.lang === 'en' ? 'context ' : 'contexto ') + Math.round(out.context.used / 1000) + 'k/' + Math.round(out.context.window / 1000) + 'k (' + Math.round(100 * out.context.used / out.context.window) + '%)'
968
987
  : '')
969
988
  : '';
970
- console.log(' ' + C.dim(T.agent_footer(out.steps, out.credits, secs)) + C.dim(_cx) + C.dim(' · ts agente --continuar "..." pra seguir') + '\n');
989
+ // rodapé enriquecido: passos · créditos · tempo · MODELO · tokens contexto)
990
+ const _tokTot = (out.tokens && ((out.tokens.inTok || 0) + (out.tokens.outTok || 0))) || 0;
991
+ const _extra = (out.model ? ' · ' + String(out.model).slice(0, 22) : '') + (_tokTot ? ' · ↓ ' + _fmtTok(_tokTot) + ' tok' : '');
992
+ console.log(' ' + C.dim(T.agent_footer(out.steps, out.credits, secs)) + C.dim(_extra) + C.dim(_cx) + C.dim(' · ts agente --continuar "..." pra seguir') + '\n');
971
993
  _printWt();
972
994
  }
973
995
 
package/lib/agent.js CHANGED
@@ -63,6 +63,7 @@ REGRAS:
63
63
  - Windows: python -c multi-linha falha em silêncio — escreva um .py com escrever_arquivo e execute "python arquivo.py".
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
+ - 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.
66
67
  - 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.
67
68
  - 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ê.
68
69
  - 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.
@@ -87,6 +88,7 @@ RULES:
87
88
  - Windows: multi-line python -c fails silently — write a .py with escrever_arquivo and run "python file.py".
88
89
  - If a dependency is missing, install it (winget/apt/pip/npm) and continue.
89
90
  - 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
+ - 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.
90
92
  - 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.
91
93
  - 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.
92
94
  - 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.
@@ -462,7 +464,8 @@ async function run(task, opts = {}) {
462
464
  let stopped = false;
463
465
  for (let iter = 0; iter < MAX_ITER; iter++) {
464
466
  if (opts.shouldStop && opts.shouldStop()) { stopped = true; break; }
465
- onThinking(iter);
467
+ // passa o snapshot de progresso pra a status line ao vivo (tempo é contado no cliente)
468
+ onThinking({ iter, inTok: acc.inTok, outTok: acc.outTok, cachedTok: acc.cachedTok, steps, model: usedModel });
466
469
  await _compactIfNeeded(false); // proativo: compacta ao cruzar ~65% da janela
467
470
  let r;
468
471
  // gateway oscilou → mostra "reconectando" em vez de morrer calado (confiabilidade visível)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminal-smart-cli",
3
- "version": "0.83.0",
3
+ "version": "0.85.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"