terminal-smart-cli 0.94.2 → 0.97.1
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/README.md +9 -0
- package/bin/ts.js +501 -8
- package/lib/agent.js +142 -9
- package/lib/checkpoint.js +155 -0
- package/lib/conhecimento.js +224 -0
- package/lib/core.js +4 -1
- package/lib/device-tools.js +226 -0
- package/lib/diagnose.js +8 -4
- package/lib/doctor.js +17 -0
- package/lib/eval.js +4 -2
- package/lib/i18n.js +28 -2
- package/lib/keyring.js +235 -0
- package/lib/meta.js +11 -5
- package/lib/office-readers.js +106 -0
- package/lib/policy.js +162 -0
- package/lib/providers.js +118 -0
- package/lib/router.js +3 -2
- package/lib/skill-index.js +414 -0
- package/lib/temas.js +101 -0
- package/lib/tools.js +100 -3
- package/lib/ui.js +19 -23
- package/package.json +3 -2
package/lib/tools.js
CHANGED
|
@@ -101,6 +101,11 @@ const DEFS = [
|
|
|
101
101
|
consulta: { type: 'string', description: 'os termos de busca (seja específico)' },
|
|
102
102
|
max: { type: 'number', description: 'quantos resultados (padrão 8, máx 20)' },
|
|
103
103
|
}, required: ['consulta'] } } },
|
|
104
|
+
{ type: 'function', function: { name: 'buscar_skill',
|
|
105
|
+
description: 'Procura uma SKILL (procedimento pronto, escrito por humano) que resolva a tarefa — entre as instaladas nesta máquina e as disponíveis na galeria/catálogos. Use quando a tarefa parece um procedimento que alguém já escreveu (revisar PR, montar planilha, auditar MCP, publicar release). Skill INSTALADA: leia o arquivo dela e siga. Skill NÃO instalada: avise o usuário que existe e que `ts skills add <slug>` instala — NUNCA instale por conta própria.',
|
|
106
|
+
parameters: { type: 'object', properties: {
|
|
107
|
+
consulta: { type: 'string', description: 'o que você precisa fazer, em poucas palavras' },
|
|
108
|
+
}, required: ['consulta'] } } },
|
|
104
109
|
{ type: 'function', function: { name: 'info_sistema',
|
|
105
110
|
description: 'Informações desta máquina: SO, memória, CPU, uptime, usuário, diretório atual.',
|
|
106
111
|
parameters: { type: 'object', properties: {}, required: [] } } },
|
|
@@ -150,6 +155,52 @@ const DEFS = [
|
|
|
150
155
|
parameters: { type: 'object', properties: {
|
|
151
156
|
tarefa: { type: 'string', description: 'a pergunta/investigação a delegar (clara e específica)' },
|
|
152
157
|
}, required: ['tarefa'] } } },
|
|
158
|
+
{ type: 'function', function: { name: 'ler_documento',
|
|
159
|
+
description: 'Lê Word .docx com paginação ou busca, retornando texto, quantidade de parágrafos, tabelas e imagens.',
|
|
160
|
+
parameters: { type: 'object', properties: {
|
|
161
|
+
caminho: { type: 'string' }, buscar: { type: 'string' },
|
|
162
|
+
inicio_char: { type: 'number' }, tamanho: { type: 'number' },
|
|
163
|
+
}, required: ['caminho'] } } },
|
|
164
|
+
{ type: 'function', function: { name: 'ler_apresentacao',
|
|
165
|
+
description: 'Lê PowerPoint .pptx por slides, retornando o texto estruturado e a quantidade de imagens.',
|
|
166
|
+
parameters: { type: 'object', properties: {
|
|
167
|
+
caminho: { type: 'string' }, inicio_slide: { type: 'number' }, slides: { type: 'number' },
|
|
168
|
+
}, required: ['caminho'] } } },
|
|
169
|
+
{ type: 'function', function: { name: 'android_dispositivos',
|
|
170
|
+
description: 'Lista dispositivos Android/Android TV via ADB, com serial, estado e modelo. Somente leitura.',
|
|
171
|
+
parameters: { type: 'object', properties: {}, required: [] } } },
|
|
172
|
+
{ type: 'function', function: { name: 'android_parear',
|
|
173
|
+
description: 'Pareia Android/Android TV por depuração sem fio. Use a porta DE PAREAMENTO e o código de 6 dígitos exibido no aparelho.',
|
|
174
|
+
parameters: { type: 'object', properties: {
|
|
175
|
+
host: { type: 'string' }, porta: { type: 'number' }, codigo: { type: 'string' },
|
|
176
|
+
}, required: ['host', 'porta', 'codigo'] } } },
|
|
177
|
+
{ type: 'function', function: { name: 'android_conectar',
|
|
178
|
+
description: 'Conecta ao Android/Android TV já pareado. Use a porta DE CONEXÃO ADB, que pode ser diferente da porta de pareamento.',
|
|
179
|
+
parameters: { type: 'object', properties: {
|
|
180
|
+
host: { type: 'string' }, porta: { type: 'number' },
|
|
181
|
+
}, required: ['host', 'porta'] } } },
|
|
182
|
+
{ type: 'function', function: { name: 'android_instalar',
|
|
183
|
+
description: 'Instala ou atualiza um APK real via ADB e só retorna sucesso se o ADB responder Success.',
|
|
184
|
+
parameters: { type: 'object', properties: {
|
|
185
|
+
apk: { type: 'string' }, serial: { type: 'string' },
|
|
186
|
+
substituir: { type: 'boolean' }, conceder_permissoes: { type: 'boolean' },
|
|
187
|
+
}, required: ['apk'] } } },
|
|
188
|
+
{ type: 'function', function: { name: 'android_iniciar',
|
|
189
|
+
description: 'Inicia um aplicativo Android instalado pelo package name.',
|
|
190
|
+
parameters: { type: 'object', properties: {
|
|
191
|
+
pacote: { type: 'string' }, serial: { type: 'string' },
|
|
192
|
+
}, required: ['pacote'] } } },
|
|
193
|
+
{ type: 'function', function: { name: 'android_logs',
|
|
194
|
+
description: 'Lê logcat, opcionalmente filtrado pelo package name, e destaca crashes/FATAL EXCEPTION/ANR. Somente leitura.',
|
|
195
|
+
parameters: { type: 'object', properties: {
|
|
196
|
+
serial: { type: 'string' }, pacote: { type: 'string' },
|
|
197
|
+
linhas: { type: 'number' }, nivel: { type: 'string', enum: ['V', 'D', 'I', 'W', 'E', 'F'] },
|
|
198
|
+
}, required: [] } } },
|
|
199
|
+
{ type: 'function', function: { name: 'android_capturar_tela',
|
|
200
|
+
description: 'Captura a tela real do Android/Android TV em PNG e retorna caminho, tamanho e SHA-256.',
|
|
201
|
+
parameters: { type: 'object', properties: {
|
|
202
|
+
caminho: { type: 'string' }, serial: { type: 'string' },
|
|
203
|
+
}, required: ['caminho'] } } },
|
|
153
204
|
];
|
|
154
205
|
|
|
155
206
|
// GRAFO DE IMPORTS (ideia do Graphfy/Knowledge Graph): parser DETERMINÍSTICO — lê só as
|
|
@@ -511,9 +562,12 @@ async function execute(name, input, opts = {}) {
|
|
|
511
562
|
}
|
|
512
563
|
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
513
564
|
const existed = fs.existsSync(p);
|
|
514
|
-
|
|
565
|
+
// O caminho do backup era DESCARTADO aqui. Agora volta no resultado (_backup) pra
|
|
566
|
+
// o loop registrar no checkpoint da sessão e permitir o "desfaz tudo".
|
|
567
|
+
const _bk = existed ? _snapshot(p) : null; // backup antes de sobrescrever
|
|
515
568
|
fs.writeFileSync(p, String(input.conteudo ?? ''), 'utf8');
|
|
516
569
|
const r = { ok: true, caminho: p, bytes: Buffer.byteLength(String(input.conteudo ?? '')), sobrescreveu: existed };
|
|
570
|
+
r._backup = _bk || ''; r._acao = existed ? 'alterado' : 'criado';
|
|
517
571
|
if (_rebased) r.aviso = 'Caminho fora do diretório de trabalho foi RE-BASEADO para dentro dele. Use ESTE caminho a partir de agora: ' + p;
|
|
518
572
|
return r;
|
|
519
573
|
}
|
|
@@ -553,9 +607,9 @@ async function execute(name, input, opts = {}) {
|
|
|
553
607
|
}
|
|
554
608
|
if (count > 1 && !input.todas) return { erro: `Trecho AMBÍGUO: ${count} ocorrências. Inclua mais linhas de contexto em "buscar" pra ficar único, ou passe todas:true.` };
|
|
555
609
|
const novo = input.todas ? orig.split(buscar).join(substituir) : orig.replace(buscar, substituir);
|
|
556
|
-
_snapshot(p); // backup antes de editar (reversível via restaurar_arquivo)
|
|
610
|
+
const _bk2 = _snapshot(p); // backup antes de editar (reversível via restaurar_arquivo)
|
|
557
611
|
fs.writeFileSync(p, novo, 'utf8');
|
|
558
|
-
return { ok: true, caminho: p, ocorrencias: input.todas ? count : 1, bytes_antes: Buffer.byteLength(orig), bytes_depois: Buffer.byteLength(novo) };
|
|
612
|
+
return { ok: true, caminho: p, ocorrencias: input.todas ? count : 1, bytes_antes: Buffer.byteLength(orig), bytes_depois: Buffer.byteLength(novo), _backup: _bk2 || '', _acao: 'alterado' };
|
|
559
613
|
}
|
|
560
614
|
case 'restaurar_arquivo': {
|
|
561
615
|
const p = _abs(input.caminho, baseDir);
|
|
@@ -619,6 +673,22 @@ async function execute(name, input, opts = {}) {
|
|
|
619
673
|
if (!hits.length) return { resultados: [], aviso: 'nada relevante no índice — tente outros termos, ou rode "ts indexar" se o projeto mudou muito.' };
|
|
620
674
|
return { resultados: hits.map(h => ({ arquivo: h.file, linhas: h.l0 + '-' + h.l1, score: h.score, ...(h.sig && h.sig.length ? { define: h.sig } : {}), previa: h.snippet })), ...(construiu ? { nota: 'índice construído agora (' + idx.N + ' trechos)' } : {}) };
|
|
621
675
|
}
|
|
676
|
+
case 'buscar_skill': {
|
|
677
|
+
const q = String(input.consulta || '').trim();
|
|
678
|
+
if (!q) return { erro: 'consulta vazia.' };
|
|
679
|
+
try {
|
|
680
|
+
const si = require('./skill-index');
|
|
681
|
+
const achadas = si.buscar(si.todas(), q, { k: 5 });
|
|
682
|
+
if (!achadas.length) return { consulta: q, achadas: [], aviso: 'Nenhuma skill conhecida cobre isso — siga com suas ferramentas normais.' };
|
|
683
|
+
return { consulta: q, achadas: achadas.map(a => ({
|
|
684
|
+
slug: a.slug, descricao: a.descricao, origem: a.origem,
|
|
685
|
+
instalada: !!a.instalada,
|
|
686
|
+
// instalada → o caminho pra LER; não instalada → só o comando (a instalação é do humano)
|
|
687
|
+
arquivo: a.instalada ? a.caminho : undefined,
|
|
688
|
+
instalar_com: a.instalada ? undefined : ('ts skills add ' + a.slug),
|
|
689
|
+
})) };
|
|
690
|
+
} catch (e) { return { erro: 'não deu pra consultar o índice de skills: ' + (e.message || e) }; }
|
|
691
|
+
}
|
|
622
692
|
case 'buscar_web': {
|
|
623
693
|
const q = String(input.consulta || '').trim();
|
|
624
694
|
if (!q) return { erro: 'consulta vazia.' };
|
|
@@ -703,6 +773,33 @@ async function execute(name, input, opts = {}) {
|
|
|
703
773
|
return { diretorio: base, arquivos: m.arquivos, dependencias: m.dependencias,
|
|
704
774
|
hubs: m.hubs, mapa: m.edges.map(([a, b]) => a + ' -> ' + b) };
|
|
705
775
|
}
|
|
776
|
+
case 'android_dispositivos':
|
|
777
|
+
return await require('./device-tools').listarDispositivos();
|
|
778
|
+
case 'ler_documento': {
|
|
779
|
+
const target = _abs(input.caminho, baseDir);
|
|
780
|
+
if (_outsideScope(target)) return _scopeError(target);
|
|
781
|
+
return await require('./office-readers').lerDocumento({ ...input, caminho: target });
|
|
782
|
+
}
|
|
783
|
+
case 'ler_apresentacao': {
|
|
784
|
+
const target = _abs(input.caminho, baseDir);
|
|
785
|
+
if (_outsideScope(target)) return _scopeError(target);
|
|
786
|
+
return await require('./office-readers').lerApresentacao({ ...input, caminho: target });
|
|
787
|
+
}
|
|
788
|
+
case 'android_parear':
|
|
789
|
+
return await require('./device-tools').parear(input);
|
|
790
|
+
case 'android_conectar':
|
|
791
|
+
return await require('./device-tools').conectar(input);
|
|
792
|
+
case 'android_instalar':
|
|
793
|
+
return await require('./device-tools').instalar(input);
|
|
794
|
+
case 'android_iniciar':
|
|
795
|
+
return await require('./device-tools').iniciar(input);
|
|
796
|
+
case 'android_logs':
|
|
797
|
+
return await require('./device-tools').logs(input);
|
|
798
|
+
case 'android_capturar_tela': {
|
|
799
|
+
const target = _abs(input.caminho, baseDir);
|
|
800
|
+
if (_outsideScope(target)) return _scopeError(target);
|
|
801
|
+
return await require('./device-tools').capturarTela({ ...input, caminho: target });
|
|
802
|
+
}
|
|
706
803
|
default: {
|
|
707
804
|
// sugere o nome REAL mais parecido (o modelo às vezes inventa uma variante):
|
|
708
805
|
// 1) substring óbvia, 2) menor distância de edição, 3) mesmo radical antes do "_".
|
package/lib/ui.js
CHANGED
|
@@ -3,30 +3,26 @@
|
|
|
3
3
|
const TTY = !!process.stdout.isTTY && !process.env.NO_COLOR;
|
|
4
4
|
|
|
5
5
|
const esc = (s) => TTY ? s : '';
|
|
6
|
-
const rgb = (r, g, b) => (s) => TTY ? `\x1b[38;2;${r};${g};${b}m${s}\x1b[39m` : String(s);
|
|
7
|
-
|
|
8
|
-
const C = {
|
|
9
|
-
cyan: rgb(34, 211, 238),
|
|
10
|
-
indigo: rgb(129, 140, 248),
|
|
11
|
-
ok: rgb(52, 211, 153),
|
|
12
|
-
warn: rgb(251, 191, 36),
|
|
13
|
-
err: rgb(248, 113, 113),
|
|
14
|
-
bold: (s) => TTY ? `\x1b[1m${s}\x1b[22m` : String(s),
|
|
15
|
-
dim: (s) => TTY ? `\x1b[2m${s}\x1b[22m` : String(s),
|
|
16
|
-
};
|
|
17
6
|
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
7
|
+
// TEMA: a paleta vem de lib/temas.js e é escolhida em ~/.ts/config.json (`ts tema`).
|
|
8
|
+
// `C` mantém os mesmos nomes de sempre (cyan/indigo/ok/warn/err) — só os VALORES mudam,
|
|
9
|
+
// então nenhum dos ~centenas de usos no CLI precisou ser tocado.
|
|
10
|
+
// Leitura direta do config (sem require de ./config) pra não criar ciclo de módulos.
|
|
11
|
+
const _temas = require('./temas');
|
|
12
|
+
function _temaAtivo() {
|
|
13
|
+
if (process.env.TS_TEMA && _temas.existe(process.env.TS_TEMA)) return process.env.TS_TEMA.toLowerCase();
|
|
14
|
+
try {
|
|
15
|
+
const os = require('os'), path = require('path'), fs = require('fs');
|
|
16
|
+
const cfg = JSON.parse(fs.readFileSync(path.join(os.homedir(), '.ts', 'config.json'), 'utf8'));
|
|
17
|
+
if (cfg && cfg.tema && _temas.existe(cfg.tema)) return String(cfg.tema).toLowerCase();
|
|
18
|
+
} catch (_) {}
|
|
19
|
+
return _temas.PADRAO;
|
|
29
20
|
}
|
|
21
|
+
const TEMA = _temaAtivo();
|
|
22
|
+
const C = _temas.montar(TEMA, TTY);
|
|
23
|
+
|
|
24
|
+
// Gradiente primária→secundária caractere a caractere (a assinatura visual do ts)
|
|
25
|
+
function gradient(text) { return _temas.gradienteDe(TEMA, text, TTY); }
|
|
30
26
|
|
|
31
27
|
function banner(version, tagline) {
|
|
32
28
|
const title = 'T E R M I N A L S M A R T';
|
|
@@ -182,4 +178,4 @@ const okLine = (s) => ' ' + C.ok('✔') + ' ' + s;
|
|
|
182
178
|
const errLine = (s) => ' ' + C.err('✖') + ' ' + s;
|
|
183
179
|
const infoLine = (s) => ' ' + C.cyan('⌁') + ' ' + s;
|
|
184
180
|
|
|
185
|
-
module.exports = { C, TTY, gradient, banner, box, spinner, md, bar, ask, cmdSummary, okLine, errLine, infoLine, wordTrunc, friendlyTool, _wrapLine };
|
|
181
|
+
module.exports = { C, TTY, TEMA, gradient, banner, box, spinner, md, bar, ask, cmdSummary, okLine, errLine, infoLine, wordTrunc, friendlyTool, _wrapLine };
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "terminal-smart-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.97.1",
|
|
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"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"test": "node test/core.test.js && node test/intelligence-core.test.js && node test/eval-model.test.js && node test/project-cache.test.js && node test/memory-bus.test.js && node test/capabilities.test.js && node test/mcp-e2e.test.js && node test/erros.test.js"
|
|
9
|
+
"test": "node test/core.test.js && node test/intelligence-core.test.js && node test/eval-model.test.js && node test/project-cache.test.js && node test/memory-bus.test.js && node test/capabilities.test.js && node test/mcp-e2e.test.js && node test/erros.test.js && node test/capability-pack.test.js && node test/byok.test.js && node test/conhecimento.test.js && node test/policy.test.js && node test/temas.test.js && node test/skill-index.test.js"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"bin",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
"license": "UNLICENSED",
|
|
33
33
|
"dependencies": {
|
|
34
|
+
"jszip": "^3.10.1",
|
|
34
35
|
"puppeteer-core": "^23.11.1",
|
|
35
36
|
"qrcode-terminal": "^0.12.0",
|
|
36
37
|
"ssh2": "^1.17.0"
|