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.
- package/bin/ts.js +12 -8
- package/lib/i18n.js +2 -2
- 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
|
|
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
|
|
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
|
-
|
|
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 = (
|
|
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}
|
|
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}
|
|
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',
|