@rabts/cli 2.0.0 → 3.0.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/package.json +4 -2
- package/src/agent.js +82 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rabts/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Rabts Studio CLI",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -15,11 +15,13 @@
|
|
|
15
15
|
"@google/genai": "^2.9.0",
|
|
16
16
|
"axios": "^1.7.2",
|
|
17
17
|
"chalk": "^5.3.0",
|
|
18
|
+
"cheerio": "^1.2.0",
|
|
18
19
|
"commander": "^12.1.0",
|
|
19
20
|
"dotenv": "^16.4.5",
|
|
20
21
|
"inquirer": "^10.0.0",
|
|
21
22
|
"marked": "^15.0.12",
|
|
22
23
|
"marked-terminal": "^7.3.0",
|
|
23
|
-
"openai": "^4.52.0"
|
|
24
|
+
"openai": "^4.52.0",
|
|
25
|
+
"ora": "^9.4.1"
|
|
24
26
|
}
|
|
25
27
|
}
|
package/src/agent.js
CHANGED
|
@@ -4,6 +4,9 @@ import { exec } from 'child_process';
|
|
|
4
4
|
import util from 'util';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import inquirer from 'inquirer';
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
import * as cheerio from 'cheerio';
|
|
9
|
+
import axios from 'axios';
|
|
7
10
|
import { Marked } from 'marked';
|
|
8
11
|
import { markedTerminal } from 'marked-terminal';
|
|
9
12
|
import { sendMessage } from './llm.js';
|
|
@@ -16,7 +19,7 @@ marked.use(markedTerminal());
|
|
|
16
19
|
const execPromise = util.promisify(exec);
|
|
17
20
|
|
|
18
21
|
// Tema Rabts
|
|
19
|
-
const themeColor = chalk.cyan;
|
|
22
|
+
const themeColor = chalk.cyan;
|
|
20
23
|
const infoColor = chalk.gray;
|
|
21
24
|
|
|
22
25
|
const tools = [
|
|
@@ -58,14 +61,39 @@ const tools = [
|
|
|
58
61
|
required: ["command"]
|
|
59
62
|
}
|
|
60
63
|
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
type: "function",
|
|
67
|
+
function: {
|
|
68
|
+
name: "search_code",
|
|
69
|
+
description: "Faz uma busca global por texto (Grep) nos arquivos do projeto.",
|
|
70
|
+
parameters: {
|
|
71
|
+
type: "object",
|
|
72
|
+
properties: { query: { type: "string", description: "O que pesquisar" } },
|
|
73
|
+
required: ["query"]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
type: "function",
|
|
79
|
+
function: {
|
|
80
|
+
name: "read_url",
|
|
81
|
+
description: "Lê e extrai texto limpo de uma página web ou documentação.",
|
|
82
|
+
parameters: {
|
|
83
|
+
type: "object",
|
|
84
|
+
properties: { url: { type: "string" } },
|
|
85
|
+
required: ["url"]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
61
88
|
}
|
|
62
89
|
];
|
|
63
90
|
|
|
64
|
-
async function executeToolCall(toolCall) {
|
|
91
|
+
async function executeToolCall(toolCall, spinner) {
|
|
65
92
|
const args = JSON.parse(toolCall.function.arguments);
|
|
66
93
|
const cwd = process.cwd();
|
|
67
94
|
|
|
68
95
|
if (toolCall.function.name === 'read_file') {
|
|
96
|
+
spinner.text = `Lendo arquivo: ${args.filepath}`;
|
|
69
97
|
try {
|
|
70
98
|
const fullPath = path.resolve(cwd, args.filepath);
|
|
71
99
|
return fs.readFileSync(fullPath, 'utf8');
|
|
@@ -75,11 +103,11 @@ async function executeToolCall(toolCall) {
|
|
|
75
103
|
}
|
|
76
104
|
|
|
77
105
|
if (toolCall.function.name === 'write_file') {
|
|
106
|
+
spinner.text = `Salvando arquivo: ${args.filepath}`;
|
|
78
107
|
try {
|
|
79
108
|
const fullPath = path.resolve(cwd, args.filepath);
|
|
80
109
|
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
81
110
|
fs.writeFileSync(fullPath, args.content, 'utf8');
|
|
82
|
-
console.log(themeColor(` ↳ Arquivo salvo: `) + chalk.white(args.filepath));
|
|
83
111
|
return `Arquivo salvo em ${fullPath}`;
|
|
84
112
|
} catch (e) {
|
|
85
113
|
return `Erro ao salvar arquivo: ${e.message}`;
|
|
@@ -87,6 +115,8 @@ async function executeToolCall(toolCall) {
|
|
|
87
115
|
}
|
|
88
116
|
|
|
89
117
|
if (toolCall.function.name === 'run_command') {
|
|
118
|
+
// Para perguntar ao usuário, precisamos pausar o spinner
|
|
119
|
+
spinner.stop();
|
|
90
120
|
const { confirm } = await inquirer.prompt([{
|
|
91
121
|
type: 'confirm',
|
|
92
122
|
name: 'confirm',
|
|
@@ -94,10 +124,13 @@ async function executeToolCall(toolCall) {
|
|
|
94
124
|
default: false
|
|
95
125
|
}]);
|
|
96
126
|
|
|
97
|
-
if (!confirm)
|
|
127
|
+
if (!confirm) {
|
|
128
|
+
spinner.start('Retomando...');
|
|
129
|
+
return `Usuário negou o comando: ${args.command}`;
|
|
130
|
+
}
|
|
98
131
|
|
|
132
|
+
spinner.start(`Executando comando: ${args.command}`);
|
|
99
133
|
try {
|
|
100
|
-
console.log(themeColor(` ↳ Executando: `) + chalk.white(args.command));
|
|
101
134
|
const { stdout, stderr } = await execPromise(args.command, { cwd });
|
|
102
135
|
return stdout || stderr || "Sucesso sem output.";
|
|
103
136
|
} catch (e) {
|
|
@@ -105,6 +138,34 @@ async function executeToolCall(toolCall) {
|
|
|
105
138
|
}
|
|
106
139
|
}
|
|
107
140
|
|
|
141
|
+
if (toolCall.function.name === 'search_code') {
|
|
142
|
+
spinner.text = `Buscando globalmente por: ${args.query}`;
|
|
143
|
+
try {
|
|
144
|
+
// Uso do grep recursivo ignorando node_modules
|
|
145
|
+
const cmd = `grep -rn --exclude-dir=node_modules --exclude-dir=.git "${args.query}" . || true`;
|
|
146
|
+
const { stdout } = await execPromise(cmd, { cwd });
|
|
147
|
+
if (!stdout.trim()) return "Nenhum resultado encontrado.";
|
|
148
|
+
// Limita resultados para não estourar tokens
|
|
149
|
+
return stdout.substring(0, 10000);
|
|
150
|
+
} catch (e) {
|
|
151
|
+
return `Erro ao buscar: ${e.message}`;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (toolCall.function.name === 'read_url') {
|
|
156
|
+
spinner.text = `Acessando URL: ${args.url}`;
|
|
157
|
+
try {
|
|
158
|
+
const response = await axios.get(args.url);
|
|
159
|
+
const $ = cheerio.load(response.data);
|
|
160
|
+
// Remove scripts, styles
|
|
161
|
+
$('script, style, noscript, svg, img, nav, footer').remove();
|
|
162
|
+
const text = $('body').text().replace(/\s+/g, ' ').trim();
|
|
163
|
+
return text.substring(0, 15000); // Retorna até 15k chars
|
|
164
|
+
} catch (e) {
|
|
165
|
+
return `Erro ao acessar URL: ${e.message}`;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
108
169
|
return "Função desconhecida.";
|
|
109
170
|
}
|
|
110
171
|
|
|
@@ -113,7 +174,7 @@ function getProjectContext() {
|
|
|
113
174
|
let contextStr = `\nContexto do Diretório Atual:\n- Caminho: ${cwd}\n`;
|
|
114
175
|
|
|
115
176
|
try {
|
|
116
|
-
const files = fs.readdirSync(cwd).slice(0, 50);
|
|
177
|
+
const files = fs.readdirSync(cwd).slice(0, 50);
|
|
117
178
|
contextStr += `- Arquivos na raiz: ${files.join(', ')}\n`;
|
|
118
179
|
|
|
119
180
|
if (fs.existsSync(path.join(cwd, 'package.json'))) {
|
|
@@ -125,9 +186,7 @@ function getProjectContext() {
|
|
|
125
186
|
const comp = JSON.parse(fs.readFileSync(path.join(cwd, 'composer.json'), 'utf8'));
|
|
126
187
|
contextStr += `- Projeto PHP (Composer): ${comp.name || 'Desconhecido'}\n`;
|
|
127
188
|
}
|
|
128
|
-
} catch (e) {
|
|
129
|
-
// Ignora erros de permissão ou parse
|
|
130
|
-
}
|
|
189
|
+
} catch (e) {}
|
|
131
190
|
return contextStr;
|
|
132
191
|
}
|
|
133
192
|
|
|
@@ -135,12 +194,11 @@ export async function startInteractiveLoop(config) {
|
|
|
135
194
|
console.log(infoColor(`\nConectado via ${config.provider} (Modelo: ${config.model})`));
|
|
136
195
|
console.log(infoColor('Dicas: Digite "sair" para encerrar. Digite "/clear" para limpar a memória desta pasta.\n'));
|
|
137
196
|
|
|
138
|
-
// Carrega memória anterior se existir
|
|
139
197
|
let messages = loadSessionMemory();
|
|
140
198
|
const isNewSession = messages.length === 0;
|
|
141
199
|
|
|
142
200
|
if (isNewSession) {
|
|
143
|
-
let systemContent = 'Você é o Rabts AI, um assistente de terminal inteligente. Responda
|
|
201
|
+
let systemContent = 'Você é o Rabts AI, um desenvolvedor e assistente de terminal altamente inteligente capaz de navegar pela internet e gerenciar código autonomamente. Responda usando Markdown limpo.';
|
|
144
202
|
systemContent += getProjectContext();
|
|
145
203
|
|
|
146
204
|
messages = [
|
|
@@ -177,11 +235,10 @@ export async function startInteractiveLoop(config) {
|
|
|
177
235
|
|
|
178
236
|
if (!input) continue;
|
|
179
237
|
|
|
180
|
-
// Se a memória foi limpa, recarrega o context inicial
|
|
181
238
|
if (messages.length === 0) {
|
|
182
239
|
messages.push({
|
|
183
240
|
role: 'system',
|
|
184
|
-
content: 'Você é o Rabts AI, um
|
|
241
|
+
content: 'Você é o Rabts AI, um desenvolvedor autônomo. ' + getProjectContext()
|
|
185
242
|
});
|
|
186
243
|
}
|
|
187
244
|
|
|
@@ -192,14 +249,15 @@ export async function startInteractiveLoop(config) {
|
|
|
192
249
|
let totalCompletionTokens = 0;
|
|
193
250
|
let startTime = Date.now();
|
|
194
251
|
|
|
252
|
+
const spinner = ora({
|
|
253
|
+
text: 'Pensando...',
|
|
254
|
+
color: 'cyan',
|
|
255
|
+
spinner: 'dots'
|
|
256
|
+
}).start();
|
|
257
|
+
|
|
195
258
|
while (isProcessingTools) {
|
|
196
|
-
process.stdout.write(infoColor(' Pensando...'));
|
|
197
|
-
|
|
198
259
|
const responseMessage = await sendMessage(config, messages, supportsTools ? tools : null);
|
|
199
260
|
|
|
200
|
-
process.stdout.clearLine(0);
|
|
201
|
-
process.stdout.cursorTo(0);
|
|
202
|
-
|
|
203
261
|
if (responseMessage.usage) {
|
|
204
262
|
totalPromptTokens += responseMessage.usage.prompt_tokens || 0;
|
|
205
263
|
totalCompletionTokens += responseMessage.usage.completion_tokens || 0;
|
|
@@ -213,8 +271,9 @@ export async function startInteractiveLoop(config) {
|
|
|
213
271
|
|
|
214
272
|
if (responseMessage.tool_calls && responseMessage.tool_calls.length > 0) {
|
|
215
273
|
for (const toolCall of responseMessage.tool_calls) {
|
|
216
|
-
|
|
217
|
-
const result = await executeToolCall(toolCall);
|
|
274
|
+
spinner.text = `Analisando ferramenta: ${toolCall.function.name}`;
|
|
275
|
+
const result = await executeToolCall(toolCall, spinner);
|
|
276
|
+
|
|
218
277
|
messages.push({
|
|
219
278
|
tool_call_id: toolCall.id,
|
|
220
279
|
role: "tool",
|
|
@@ -222,20 +281,19 @@ export async function startInteractiveLoop(config) {
|
|
|
222
281
|
content: result,
|
|
223
282
|
});
|
|
224
283
|
}
|
|
284
|
+
spinner.text = 'Processando resultados...';
|
|
225
285
|
} else {
|
|
286
|
+
spinner.stop();
|
|
226
287
|
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
227
288
|
|
|
228
289
|
if (responseMessage.content) {
|
|
229
290
|
console.log(themeColor('\nRabts:'));
|
|
230
|
-
// Renderiza a resposta visualmente com syntax highlighting
|
|
231
291
|
console.log(marked.parse(responseMessage.content).trim());
|
|
232
292
|
}
|
|
233
293
|
|
|
234
|
-
console.log(infoColor(`\n ↳ Finalizado em ${duration}s • ${totalPromptTokens + totalCompletionTokens} tokens
|
|
294
|
+
console.log(infoColor(`\n ↳ Finalizado em ${duration}s • ${totalPromptTokens + totalCompletionTokens} tokens\n`));
|
|
235
295
|
|
|
236
|
-
// Salva memória a cada interação
|
|
237
296
|
saveSessionMemory(messages);
|
|
238
|
-
|
|
239
297
|
isProcessingTools = false;
|
|
240
298
|
}
|
|
241
299
|
}
|