terminal-smart-cli 0.45.0 → 0.47.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 +115 -0
  2. package/lib/i18n.js +2 -0
  3. package/package.json +1 -1
package/bin/ts.js CHANGED
@@ -1333,6 +1333,86 @@ async function metaCmd() {
1333
1333
  }
1334
1334
 
1335
1335
  // ── Uso / conta / idioma ─────────────────────────────────────────────────────
1336
+ // ── ts cloud — caixa de dev na nuvem (Fase 1) ────────────────────────────────
1337
+ async function cloudCmd(args) {
1338
+ const token = needToken();
1339
+ const en = (cfg.lang === 'en');
1340
+ const sub = String(args[0] || 'status').toLowerCase();
1341
+ const rest = args.slice(1).filter(a => !a.startsWith('-'));
1342
+ const call = (path, opts = {}) => api('/api/cloud/' + path, { token, ...opts });
1343
+
1344
+ if (sub === 'up' || sub === 'start' || sub === 'ligar') {
1345
+ console.log(C.dim(en ? 'provisioning your cloud box…' : 'provisionando sua caixa na nuvem…'));
1346
+ try {
1347
+ const r = await call('up', { method: 'POST', body: {}, timeoutMs: 120000 });
1348
+ if (!r.ok) { console.log(ui.errLine(r.error || 'falha')); if (r.code === 'plan_required') console.log(C.dim(en ? 'Get Ultra at terminalsmart.com.br/planos' : 'Assine o Ultra em terminalsmart.com.br/planos')); return; }
1349
+ console.log('\n' + ui.box([
1350
+ C.ok(en ? 'Cloud box is up!' : 'Caixa na nuvem no ar!'), '',
1351
+ C.dim('URL: ') + C.bold(r.url),
1352
+ C.dim(en ? 'box: ' : 'caixa: ') + r.box,
1353
+ ], { title: 'ts cloud' }) + '\n');
1354
+ console.log(C.dim(en ? 'Build in the cloud:' : 'Construa na nuvem:') + ' ts cloud meta "' + (en ? 'your goal' : 'seu objetivo') + '"');
1355
+ } catch (e) { console.log(ui.errLine(e.code === 'auth' ? T.need_login : (e.message || 'falha'))); }
1356
+ return;
1357
+ }
1358
+
1359
+ if (sub === 'status' || sub === 'url') {
1360
+ const r = await call('status');
1361
+ if (!r.exists) { console.log(ui.infoLine(en ? 'No cloud box yet. Run: ts cloud up' : 'Nenhuma caixa ainda. Rode: ts cloud up')); return; }
1362
+ if (sub === 'url') { console.log(r.url); return; }
1363
+ console.log('\n' + ui.box([
1364
+ C.dim('status: ') + (r.running ? C.ok(en ? 'running' : 'no ar') : C.err(en ? 'stopped' : 'parada')),
1365
+ C.dim('URL: ') + C.bold(r.url),
1366
+ C.dim(en ? 'port: ' : 'porta: ') + r.port,
1367
+ ], { title: 'ts cloud' }) + '\n');
1368
+ return;
1369
+ }
1370
+
1371
+ if (sub === 'exec' || sub === 'sh' || sub === 'run') {
1372
+ const cmd = rest.join(' ').trim();
1373
+ if (!cmd) { console.log(ui.errLine(en ? 'usage: ts cloud exec "<command>"' : 'uso: ts cloud exec "<comando>"')); return; }
1374
+ const r = await call('exec', { method: 'POST', body: { cmd }, timeoutMs: 300000 });
1375
+ if (r.out) process.stdout.write(r.out);
1376
+ if (!r.ok && r.error) console.log(ui.errLine(r.error));
1377
+ return;
1378
+ }
1379
+
1380
+ if (sub === 'meta' || sub === 'missao' || sub === 'missão') {
1381
+ const goal = rest.join(' ').trim();
1382
+ if (!goal) { console.log(ui.errLine(en ? 'usage: ts cloud meta "<goal>"' : 'uso: ts cloud meta "<objetivo>"')); return; }
1383
+ let st = await call('status');
1384
+ if (!st.exists) { console.log(C.dim(en ? 'creating box…' : 'criando caixa…')); const up = await call('up', { method: 'POST', body: {}, timeoutMs: 120000 }); if (!up.ok) { console.log(ui.errLine(up.error || 'falha')); if (up.code === 'plan_required') console.log(C.dim('terminalsmart.com.br/planos')); return; } }
1385
+ console.log(C.dim(en ? 'running ts meta inside the cloud box (may take a while)…' : 'rodando ts meta dentro da caixa (pode demorar)…'));
1386
+ const safe = goal.replace(/(["\\$`])/g, '\\$1');
1387
+ // build com ts meta e, ao terminar, EXPÕE o app na porta 3000 (mapeada pro subdomínio):
1388
+ // npm start se houver, senão serve estático. Assim a caixa fica no ar de verdade.
1389
+ const expose = 'pkill -f "http.server 3000" 2>/dev/null; pkill -f "PORT=3000" 2>/dev/null; ' +
1390
+ 'if [ -f package.json ] && grep -q \'"start"\' package.json; then (PORT=3000 nohup npm start >/tmp/app.log 2>&1 &); ' +
1391
+ 'elif [ -f index.html ] || ls *.html >/dev/null 2>&1; then (nohup python3 -m http.server 3000 >/tmp/app.log 2>&1 &); fi; sleep 2; echo "[ts cloud] app exposto na porta 3000"';
1392
+ const r = await call('exec', { method: 'POST', body: { cmd: 'cd /workspace && ts meta "' + safe + '" --yes 2>&1 | tail -50; ' + expose }, timeoutMs: 1800000 });
1393
+ if (r.out) process.stdout.write(r.out);
1394
+ const u = await call('status'); if (u && u.url) console.log('\n' + ui.okLine((en ? 'Live at: ' : 'No ar em: ') + u.url));
1395
+ return;
1396
+ }
1397
+
1398
+ if (sub === 'down' || sub === 'stop' || sub === 'parar' || sub === 'destroy' || sub === 'apagar') {
1399
+ const destroy = (sub === 'destroy' || sub === 'apagar' || FLAGS.has('--destroy'));
1400
+ const r = await call('down', { method: 'POST', body: { destroy } });
1401
+ console.log(r.ok ? ui.okLine(destroy ? (en ? 'box destroyed' : 'caixa apagada') : (en ? 'box stopped' : 'caixa parada')) : ui.errLine(r.error || 'falha'));
1402
+ return;
1403
+ }
1404
+
1405
+ console.log('\n' + ui.box([
1406
+ C.bold('ts cloud') + C.dim(en ? ' — your dev box in the cloud' : ' — sua caixa de dev na nuvem'), '',
1407
+ C.dim('up') + ' ' + (en ? 'create/start your box + public URL' : 'cria/liga sua caixa + URL pública'),
1408
+ C.dim('meta "…"') + ' ' + (en ? 'the agent builds your app in the cloud' : 'o agente constrói seu app na nuvem'),
1409
+ C.dim('exec "…"') + ' ' + (en ? 'run a shell command in the box' : 'roda um comando na caixa'),
1410
+ C.dim('url') + ' ' + (en ? 'print the public URL' : 'mostra a URL pública'),
1411
+ C.dim('status') + ' ' + (en ? 'box status' : 'status da caixa'),
1412
+ C.dim('down') + ' ' + (en ? 'stop the box (--destroy to remove)' : 'para a caixa (--destroy pra remover)'),
1413
+ ], { title: 'ts cloud' }) + '\n');
1414
+ }
1415
+
1336
1416
  async function uso() {
1337
1417
  const token = needToken();
1338
1418
  const r = await api('/api/credits', { token });
@@ -1380,6 +1460,39 @@ async function nova() {
1380
1460
  console.log(ui.okLine(T.new_conv));
1381
1461
  }
1382
1462
 
1463
+ // RECALL cross-sessão (estilo deja-vu): busca um termo em TODAS as sessões passadas do
1464
+ // agente (~/.ts/agente/*.json, uma por pasta de projeto) — "já lidei com isso antes, e onde?".
1465
+ // 100% LOCAL, sem rede: usa as sessões que o `ts agente` já salva por pasta.
1466
+ function recallCmd(args) {
1467
+ const _fs = require('fs'), _p = require('path'), _os = require('os');
1468
+ const termo = (args || []).join(' ').trim();
1469
+ if (!termo) { console.error(ui.infoLine(cfg.lang === 'en' ? 'Usage: ts recall <term> — searches your past agent sessions' : 'Uso: ts recall <termo> — busca nas suas sessões passadas do agente')); process.exit(2); }
1470
+ const dir = _p.join(_os.homedir(), '.ts', 'agente');
1471
+ let files = []; try { files = _fs.readdirSync(dir).filter(f => f.endsWith('.json')); } catch (_) {}
1472
+ const rx = new RegExp(termo.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i');
1473
+ const hits = [];
1474
+ for (const f of files) {
1475
+ let s; try { s = JSON.parse(_fs.readFileSync(_p.join(dir, f), 'utf8')); } catch (_) { continue; }
1476
+ const folder = s.cwd ? _p.basename(s.cwd) : '?';
1477
+ for (const m of (s.messages || [])) {
1478
+ if (m.role === 'system') continue; // pula o prompt injetado (ruído, não é conversa)
1479
+ const txt = typeof m.content === 'string' ? m.content : JSON.stringify(m.content || '');
1480
+ const i = txt.search(rx);
1481
+ if (i >= 0) { const snip = txt.slice(Math.max(0, i - 60), i + 140).replace(/\s+/g, ' ').trim(); hits.push({ folder, at: s.at || '', role: m.role || '?', snip }); }
1482
+ }
1483
+ }
1484
+ if (JSON_OUT) { console.log(JSON.stringify({ termo, hits })); return; }
1485
+ if (!hits.length) { console.log(ui.infoLine((cfg.lang === 'en' ? 'No past session mentions ' : 'Nenhuma sessão passada menciona ') + C.cyan('"' + termo + '"'))); return; }
1486
+ hits.sort((a, b) => String(b.at).localeCompare(String(a.at)));
1487
+ const lines = [];
1488
+ for (const h of hits.slice(0, 12)) {
1489
+ lines.push(C.cyan(h.folder) + C.dim(' · ' + String(h.at).slice(0, 10) + ' · ' + h.role));
1490
+ lines.push(' ' + C.dim(h.snip.slice(0, 180)));
1491
+ }
1492
+ const title = 'Recall: "' + termo + '" (' + hits.length + (cfg.lang === 'en' ? ' hits' : ' resultados') + ')';
1493
+ console.log('\n' + ui.box(lines, { title }) + '\n');
1494
+ }
1495
+
1383
1496
  // ── Dispatcher ───────────────────────────────────────────────────────────────
1384
1497
  (async () => {
1385
1498
  const cmd = (POS[0] || '').toLowerCase();
@@ -1407,7 +1520,9 @@ async function nova() {
1407
1520
  case 'acp': return acpCmd();
1408
1521
  case 'skills': case 'skill': return skillsCmd(POS.slice(1));
1409
1522
  case 'memoria': case 'memória': case 'memory': return memoriaCmd(POS.slice(1));
1523
+ case 'recall': case 'lembrei': case 'sessoes': case 'sessões': return recallCmd(POS.slice(1));
1410
1524
  case 'vps': case 'servidor': return vpsCmd(POS.slice(1));
1525
+ case 'cloud': case 'nuvem': return cloudCmd(POS.slice(1));
1411
1526
  case 'meta': case 'missao': case 'mission': return metaCmd();
1412
1527
  case 'runs': return runsCmd();
1413
1528
  case 'status': return statusCmd(POS[1]);
package/lib/i18n.js CHANGED
@@ -26,6 +26,7 @@ const STR = {
26
26
  ['ts memoria', 'mostra a memória do projeto + global'],
27
27
  ['ts memoria add "fato"', 'grava um fato (o agente sempre lê depois)'],
28
28
  ['ts memoria add "x" -g', 'grava na memória global (todo projeto)'],
29
+ ['ts recall "termo"', 'busca "já vi isso?" nas suas sessões passadas'],
29
30
  ] },
30
31
  { title: 'Agente LOCAL (mãos nesta máquina)', items: [
31
32
  ['ts agente "tarefa"', 'executa DE VERDADE aqui: comandos, arquivos, diagnóstico'],
@@ -174,6 +175,7 @@ const STR = {
174
175
  ['ts memoria', 'show project + global memory'],
175
176
  ['ts memoria add "fact"', 'save a fact (the agent always reads it later)'],
176
177
  ['ts memoria add "x" -g', 'save to global memory (every project)'],
178
+ ['ts recall "term"', 'search your past sessions ("seen this before?")'],
177
179
  ] },
178
180
  { title: 'LOCAL agent (hands on this machine)', items: [
179
181
  ['ts agente "task"', 'actually does it here: commands, files, diagnosis'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminal-smart-cli",
3
- "version": "0.45.0",
3
+ "version": "0.47.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"