terminal-smart-cli 0.85.0 → 0.87.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.
Files changed (3) hide show
  1. package/bin/ts.js +12 -8
  2. package/lib/i18n.js +2 -2
  3. package/package.json +1 -1
package/bin/ts.js CHANGED
@@ -37,11 +37,13 @@ function fmtK(n) { return n >= 1000 ? (n / 1000).toFixed(1) + 'k' : String(n); }
37
37
  // heredoc SQL a 1ª linha é o comando externo, ex "mysql -u root pw <<'SQL'"). Aceita:
38
38
  // s/N = sim/não neste passo · a = aprovar TUDO na sessão (full-auto) · v = ver o comando inteiro
39
39
  // Retorna true | false | 'all'.
40
- async function askDestructive(cmd, { en = false, warning = null } = {}) {
40
+ async function askDestructive(cmd, { en = false, warning = null, askFn = null } = {}) {
41
41
  const raw = String(cmd || '');
42
42
  const { shown, hidden } = ui.cmdSummary(raw);
43
43
  const more = hidden > 0 ? C.dim(` … +${hidden} ${en ? 'lines' : 'linhas'}`) : '';
44
44
  const desc = require('../lib/core').describeDestructive(raw, { en }); // o que a ação FAZ, em linguagem natural
45
+ // no REPL, reusa o readline do REPL (askFn) — criar um novo com ui.ask ESTRAGA o do REPL (teclado morre).
46
+ const _ask = askFn || ui.ask;
45
47
  let first = true;
46
48
  for (;;) {
47
49
  if (first) {
@@ -53,24 +55,26 @@ async function askDestructive(cmd, { en = false, warning = null } = {}) {
53
55
  first = false;
54
56
  }
55
57
  const q = C.dim(en ? ' (S) Yes · (N) No · (A) Always · (V) View command › ' : ' (S) Sim · (N) Não · (A) Sim sempre · (V) Ver comando › ');
56
- const a = String(await ui.ask(q)).trim().toLowerCase();
58
+ const a = String(await _ask(q)).trim().toLowerCase();
57
59
  if (['v', 'ver', 'view'].includes(a)) { console.log('\n' + raw.split('\n').map(l => ' ' + C.dim(l)).join('\n') + '\n'); continue; }
58
60
  if (['a', 'all', 'tudo', 'sempre', 'always'].includes(a)) return 'all';
59
61
  return ['s', 'sim', 'y', 'yes'].includes(a);
60
62
  }
61
63
  }
62
64
  // askLabelApprove: aprovação de um LABEL já formatado (skill/MCP, não é comando cru). s/N + a=tudo.
63
- async function askLabelApprove(label, { en = false } = {}) {
64
- const a = String(await ui.ask(C.warn('▲ ') + T.agent_approve(C.bold(label)) + C.dim(en ? '(a=all) ' : '(a=tudo) '))).trim().toLowerCase();
65
+ async function askLabelApprove(label, { en = false, askFn = null } = {}) {
66
+ const _ask = askFn || ui.ask;
67
+ const a = String(await _ask(C.warn('▲ ') + T.agent_approve(C.bold(label)) + C.dim(en ? '(a=all) ' : '(a=tudo) '))).trim().toLowerCase();
65
68
  if (['a', 'all', 'tudo'].includes(a)) return 'all';
66
69
  return ['s', 'sim', 'y', 'yes'].includes(a);
67
70
  }
68
71
  // Roteia o payload do askApprove do agente (obj destrutivo → resumo; string label → skill/MCP).
69
- async function askApproveRoute(payload) {
72
+ // askFn: quando vem do REPL, é o readline DELE — usar ui.ask novo aqui estragaria o teclado do REPL.
73
+ async function askApproveRoute(payload, askFn = null) {
70
74
  const en = cfg.lang === 'en';
71
75
  const isObj = payload && typeof payload === 'object';
72
76
  const cmd = isObj ? payload.cmd : String(payload);
73
- return (isObj && payload.kind === 'destructive') ? askDestructive(cmd, { en, warning: payload.warning }) : askLabelApprove(cmd, { en });
77
+ return (isObj && payload.kind === 'destructive') ? askDestructive(cmd, { en, warning: payload.warning, askFn }) : askLabelApprove(cmd, { en, askFn });
74
78
  }
75
79
  // Linha de STATUS por etapa (✓ verde / ✗ vermelho) sob a linha ⚙, com recuo `pad`.
76
80
  function stepDoneLine({ ok, evidence }, pad = ' ') {
@@ -926,7 +930,7 @@ async function agentCmd(words, { askFn, cwd: cwdIn = null, onCwd = null } = {})
926
930
  // headless (stream-json): não dá pra perguntar → NEGA o destrutivo e sinaliza o evento.
927
931
  if (streamJson) { const cmd = payload && typeof payload === 'object' ? payload.cmd : payload; _emit(core.AgentEvents.tool({ subtype: 'approval_denied', reason: 'headless (--stream-json): destructive command not auto-approved', command: cmd })); return false; }
928
932
  sp.stop();
929
- const r = await askApproveRoute(payload);
933
+ const r = await askApproveRoute(payload, askFn); // reusa o readline do REPL (não estraga o teclado)
930
934
  sp.start();
931
935
  return r;
932
936
  },
@@ -947,7 +951,7 @@ async function agentCmd(words, { askFn, cwd: cwdIn = null, onCwd = null } = {})
947
951
  if (onCwd && !_wt && effCwd !== startCwd) onCwd(effCwd);
948
952
  const persistCwd = _wt ? _wt.base : effCwd;
949
953
  try { _fs.mkdirSync(_p.dirname(sessFile), { recursive: true }); _fs.writeFileSync(sessFile, JSON.stringify({ cwd: persistCwd, at: new Date().toISOString(), messages: (out.messages || []).slice(-60) })); } catch (_) {}
950
- const secs = ((Date.now() - t0) / 1000).toFixed(1).replace('.', ',');
954
+ const secs = _fmtTime(Date.now() - t0); // tempo total já em min+seg (ex: 12m44s), não segundos crus
951
955
  // WORKTREE: commita o que mudou e prepara o resumo (merge/descarte).
952
956
  const _wtInfo = _wt ? _worktreeFinish(_wt) : null;
953
957
  const _printWt = () => {
package/lib/i18n.js CHANGED
@@ -145,7 +145,7 @@ const STR = {
145
145
  agent_thinking: 'agente pensando',
146
146
  agent_blocked: 'bloqueado (destrutivo)',
147
147
  agent_approve: (cmd) => `O agente quer rodar um comando DESTRUTIVO:\n\n ${cmd}\n\n Permitir? (s/N) `,
148
- agent_footer: (st, cr, s) => `${st} passo(s) · ${cr} créditos · ${s}s`,
148
+ agent_footer: (st, cr, s) => `${st} passo(s) · ${cr} créditos · ${s}`,
149
149
  agent_remote_wait: (ttl) => `comando destrutivo — pedido de aprovação enviado no seu Telegram (responda em até ${Math.round(ttl / 60)} min; sem resposta = negado)`,
150
150
  meta_need: 'Descreva o objetivo. Ex.: ts meta "monte um site de portfólio completo nesta pasta" --budget 300',
151
151
  meta_title: 'MISSÃO',
@@ -297,7 +297,7 @@ const STR = {
297
297
  agent_thinking: 'agent thinking',
298
298
  agent_blocked: 'blocked (destructive)',
299
299
  agent_approve: (cmd) => `The agent wants to run a DESTRUCTIVE command:\n\n ${cmd}\n\n Allow? (y/N) `,
300
- agent_footer: (st, cr, s) => `${st} step(s) · ${cr} credits · ${s}s`,
300
+ agent_footer: (st, cr, s) => `${st} step(s) · ${cr} credits · ${s}`,
301
301
  agent_remote_wait: (ttl) => `destructive command — approval request sent to your Telegram (answer within ${Math.round(ttl / 60)} min; no answer = denied)`,
302
302
  meta_need: 'Describe the goal. E.g.: ts meta "build a complete portfolio site in this folder" --budget 300',
303
303
  meta_title: 'MISSION',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminal-smart-cli",
3
- "version": "0.85.0",
3
+ "version": "0.87.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"