@rabts/cli 1.0.0 → 2.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 +3 -1
- package/src/agent.js +72 -12
- package/src/config.js +3 -2
- package/src/llm.js +2 -2
- package/src/memory.js +46 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rabts/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Rabts Studio CLI",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"commander": "^12.1.0",
|
|
19
19
|
"dotenv": "^16.4.5",
|
|
20
20
|
"inquirer": "^10.0.0",
|
|
21
|
+
"marked": "^15.0.12",
|
|
22
|
+
"marked-terminal": "^7.3.0",
|
|
21
23
|
"openai": "^4.52.0"
|
|
22
24
|
}
|
|
23
25
|
}
|
package/src/agent.js
CHANGED
|
@@ -4,7 +4,14 @@ 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 { Marked } from 'marked';
|
|
8
|
+
import { markedTerminal } from 'marked-terminal';
|
|
7
9
|
import { sendMessage } from './llm.js';
|
|
10
|
+
import { loadSessionMemory, saveSessionMemory, clearSessionMemory } from './memory.js';
|
|
11
|
+
|
|
12
|
+
// Setup Markdown Renderer
|
|
13
|
+
const marked = new Marked();
|
|
14
|
+
marked.use(markedTerminal());
|
|
8
15
|
|
|
9
16
|
const execPromise = util.promisify(exec);
|
|
10
17
|
|
|
@@ -101,16 +108,47 @@ async function executeToolCall(toolCall) {
|
|
|
101
108
|
return "Função desconhecida.";
|
|
102
109
|
}
|
|
103
110
|
|
|
111
|
+
function getProjectContext() {
|
|
112
|
+
const cwd = process.cwd();
|
|
113
|
+
let contextStr = `\nContexto do Diretório Atual:\n- Caminho: ${cwd}\n`;
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const files = fs.readdirSync(cwd).slice(0, 50); // Limita para não estourar tokens
|
|
117
|
+
contextStr += `- Arquivos na raiz: ${files.join(', ')}\n`;
|
|
118
|
+
|
|
119
|
+
if (fs.existsSync(path.join(cwd, 'package.json'))) {
|
|
120
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
|
121
|
+
contextStr += `- Projeto Node.js: ${pkg.name || 'Desconhecido'}\n`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (fs.existsSync(path.join(cwd, 'composer.json'))) {
|
|
125
|
+
const comp = JSON.parse(fs.readFileSync(path.join(cwd, 'composer.json'), 'utf8'));
|
|
126
|
+
contextStr += `- Projeto PHP (Composer): ${comp.name || 'Desconhecido'}\n`;
|
|
127
|
+
}
|
|
128
|
+
} catch (e) {
|
|
129
|
+
// Ignora erros de permissão ou parse
|
|
130
|
+
}
|
|
131
|
+
return contextStr;
|
|
132
|
+
}
|
|
133
|
+
|
|
104
134
|
export async function startInteractiveLoop(config) {
|
|
105
135
|
console.log(infoColor(`\nConectado via ${config.provider} (Modelo: ${config.model})`));
|
|
106
|
-
console.log(infoColor('
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
136
|
+
console.log(infoColor('Dicas: Digite "sair" para encerrar. Digite "/clear" para limpar a memória desta pasta.\n'));
|
|
137
|
+
|
|
138
|
+
// Carrega memória anterior se existir
|
|
139
|
+
let messages = loadSessionMemory();
|
|
140
|
+
const isNewSession = messages.length === 0;
|
|
141
|
+
|
|
142
|
+
if (isNewSession) {
|
|
143
|
+
let systemContent = 'Você é o Rabts AI, um assistente de terminal inteligente. Responda em Markdown limpo.';
|
|
144
|
+
systemContent += getProjectContext();
|
|
145
|
+
|
|
146
|
+
messages = [
|
|
147
|
+
{ role: 'system', content: systemContent }
|
|
148
|
+
];
|
|
149
|
+
} else {
|
|
150
|
+
console.log(chalk.green('✓ Sessão anterior restaurada para este projeto.\n'));
|
|
151
|
+
}
|
|
114
152
|
|
|
115
153
|
const supportsTools = (config.provider === 'rabts' || config.provider === 'openai');
|
|
116
154
|
|
|
@@ -123,14 +161,31 @@ export async function startInteractiveLoop(config) {
|
|
|
123
161
|
}
|
|
124
162
|
]);
|
|
125
163
|
|
|
126
|
-
|
|
164
|
+
const input = userPrompt.trim();
|
|
165
|
+
|
|
166
|
+
if (input.toLowerCase() === 'sair' || input.toLowerCase() === 'exit') {
|
|
127
167
|
console.log(infoColor('Até logo!'));
|
|
128
168
|
break;
|
|
129
169
|
}
|
|
170
|
+
|
|
171
|
+
if (input.toLowerCase() === '/clear') {
|
|
172
|
+
clearSessionMemory();
|
|
173
|
+
console.log(chalk.yellow('Memória limpa! O contexto do projeto será reiniciado na próxima mensagem.\n'));
|
|
174
|
+
messages = [];
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!input) continue;
|
|
130
179
|
|
|
131
|
-
|
|
180
|
+
// Se a memória foi limpa, recarrega o context inicial
|
|
181
|
+
if (messages.length === 0) {
|
|
182
|
+
messages.push({
|
|
183
|
+
role: 'system',
|
|
184
|
+
content: 'Você é o Rabts AI, um assistente de terminal inteligente.' + getProjectContext()
|
|
185
|
+
});
|
|
186
|
+
}
|
|
132
187
|
|
|
133
|
-
messages.push({ role: 'user', content:
|
|
188
|
+
messages.push({ role: 'user', content: input });
|
|
134
189
|
|
|
135
190
|
let isProcessingTools = true;
|
|
136
191
|
let totalPromptTokens = 0;
|
|
@@ -172,10 +227,15 @@ export async function startInteractiveLoop(config) {
|
|
|
172
227
|
|
|
173
228
|
if (responseMessage.content) {
|
|
174
229
|
console.log(themeColor('\nRabts:'));
|
|
175
|
-
|
|
230
|
+
// Renderiza a resposta visualmente com syntax highlighting
|
|
231
|
+
console.log(marked.parse(responseMessage.content).trim());
|
|
176
232
|
}
|
|
177
233
|
|
|
178
234
|
console.log(infoColor(`\n ↳ Finalizado em ${duration}s • ${totalPromptTokens + totalCompletionTokens} tokens (${totalPromptTokens} in, ${totalCompletionTokens} out)\n`));
|
|
235
|
+
|
|
236
|
+
// Salva memória a cada interação
|
|
237
|
+
saveSessionMemory(messages);
|
|
238
|
+
|
|
179
239
|
isProcessingTools = false;
|
|
180
240
|
}
|
|
181
241
|
}
|
package/src/config.js
CHANGED
|
@@ -6,7 +6,7 @@ import chalk from 'chalk';
|
|
|
6
6
|
|
|
7
7
|
const CONFIG_FILE_PATH = path.join(os.homedir(), '.rabts-config.json');
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
// A chave padrão não fica mais no código-fonte por segurança
|
|
10
10
|
|
|
11
11
|
export function getConfig() {
|
|
12
12
|
if (fs.existsSync(CONFIG_FILE_PATH)) {
|
|
@@ -47,7 +47,8 @@ export async function configureApiKey() {
|
|
|
47
47
|
let config = { provider };
|
|
48
48
|
|
|
49
49
|
if (provider === 'rabts') {
|
|
50
|
-
|
|
50
|
+
// A chave é injetada via proxy no servidor backend
|
|
51
|
+
config.apiKey = 'proxy_mode';
|
|
51
52
|
const { model } = await inquirer.prompt([
|
|
52
53
|
{
|
|
53
54
|
type: 'list',
|
package/src/llm.js
CHANGED
|
@@ -10,8 +10,8 @@ export function getClient(config) {
|
|
|
10
10
|
|
|
11
11
|
if (config.provider === 'rabts') {
|
|
12
12
|
clients[config.provider] = new OpenAI({
|
|
13
|
-
baseURL: 'https://
|
|
14
|
-
apiKey:
|
|
13
|
+
baseURL: 'https://dev.rabts.com/api',
|
|
14
|
+
apiKey: 'proxy_mode',
|
|
15
15
|
});
|
|
16
16
|
} else if (config.provider === 'openai') {
|
|
17
17
|
clients[config.provider] = new OpenAI({
|
package/src/memory.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
export function getMemoryPath() {
|
|
5
|
+
const cwd = process.cwd();
|
|
6
|
+
return path.join(cwd, '.rabts', 'session.json');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function loadSessionMemory() {
|
|
10
|
+
const memPath = getMemoryPath();
|
|
11
|
+
if (fs.existsSync(memPath)) {
|
|
12
|
+
try {
|
|
13
|
+
const data = fs.readFileSync(memPath, 'utf8');
|
|
14
|
+
return JSON.parse(data);
|
|
15
|
+
} catch (e) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function saveSessionMemory(messages) {
|
|
23
|
+
const memPath = getMemoryPath();
|
|
24
|
+
const dirPath = path.dirname(memPath);
|
|
25
|
+
|
|
26
|
+
if (!fs.existsSync(dirPath)) {
|
|
27
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Ocultar a pasta no Windows/Linux de forma simples
|
|
31
|
+
// Adicionar um gitignore para que o usuário não commite acidentalmente o histórico
|
|
32
|
+
const gitignorePath = path.join(dirPath, '.gitignore');
|
|
33
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
34
|
+
fs.writeFileSync(gitignorePath, "*\n");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Filtrar o que salvar (não precisamos salvar todos os resultados longos de tools se não quisermos)
|
|
38
|
+
fs.writeFileSync(memPath, JSON.stringify(messages, null, 2), 'utf8');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function clearSessionMemory() {
|
|
42
|
+
const memPath = getMemoryPath();
|
|
43
|
+
if (fs.existsSync(memPath)) {
|
|
44
|
+
fs.unlinkSync(memPath);
|
|
45
|
+
}
|
|
46
|
+
}
|