prab-cli 1.0.0 → 1.2.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/dist/index.js +249 -163
- package/dist/lib/chat-handler.js +128 -23
- package/dist/lib/config.js +12 -12
- package/dist/lib/context.js +7 -12
- package/dist/lib/groq-models.js +3 -3
- package/dist/lib/groq.js +6 -6
- package/dist/lib/safety.js +29 -34
- package/dist/lib/slash-commands.js +94 -0
- package/dist/lib/tracker.js +74 -72
- package/dist/lib/ui.js +219 -101
- package/package.json +27 -3
package/dist/lib/chat-handler.js
CHANGED
|
@@ -11,7 +11,13 @@ const tracker_1 = require("./tracker");
|
|
|
11
11
|
class ChatHandler {
|
|
12
12
|
constructor(toolRegistry, toolExecutor, modelProvider, initialContext) {
|
|
13
13
|
this.messages = [];
|
|
14
|
-
this.contextMessage =
|
|
14
|
+
this.contextMessage = "";
|
|
15
|
+
this.usage = {
|
|
16
|
+
promptTokens: 0,
|
|
17
|
+
completionTokens: 0,
|
|
18
|
+
totalTokens: 0,
|
|
19
|
+
requestCount: 0,
|
|
20
|
+
};
|
|
15
21
|
this.toolRegistry = toolRegistry;
|
|
16
22
|
this.toolExecutor = toolExecutor;
|
|
17
23
|
this.modelProvider = modelProvider;
|
|
@@ -52,9 +58,9 @@ When you need to perform file operations, use the appropriate tools rather than
|
|
|
52
58
|
try {
|
|
53
59
|
// Auto-attach file context if files are mentioned
|
|
54
60
|
const attachedFiles = await this.attachMentionedFiles(input);
|
|
55
|
-
|
|
61
|
+
const finalInput = input;
|
|
56
62
|
if (attachedFiles.length > 0) {
|
|
57
|
-
ui_1.log.info(`Attached context for: ${attachedFiles.join(
|
|
63
|
+
ui_1.log.info(`Attached context for: ${attachedFiles.join(", ")}`);
|
|
58
64
|
tracker_1.tracker.contextAttached(attachedFiles);
|
|
59
65
|
}
|
|
60
66
|
// Add user message
|
|
@@ -67,20 +73,20 @@ When you need to perform file operations, use the appropriate tools rather than
|
|
|
67
73
|
const maxIterations = 10; // Prevent infinite loops
|
|
68
74
|
while (continueLoop && iterationCount < maxIterations) {
|
|
69
75
|
iterationCount++;
|
|
70
|
-
tracker_1.tracker.iteration(iterationCount,
|
|
76
|
+
tracker_1.tracker.iteration(iterationCount, "Starting API call");
|
|
71
77
|
// Log API request
|
|
72
78
|
tracker_1.tracker.apiRequest(this.modelProvider.modelId, this.messages.length, tools.length);
|
|
73
79
|
const apiStartTime = Date.now();
|
|
74
80
|
// Stream response from model
|
|
75
81
|
const stream = this.modelProvider.streamChat(this.messages, tools);
|
|
76
|
-
let fullResponse =
|
|
82
|
+
let fullResponse = "";
|
|
77
83
|
let toolCalls = [];
|
|
78
84
|
const formatter = new ui_1.StreamFormatter();
|
|
79
|
-
process.stdout.write(
|
|
85
|
+
process.stdout.write("\n");
|
|
80
86
|
try {
|
|
81
87
|
for await (const chunk of stream) {
|
|
82
88
|
// Handle text content
|
|
83
|
-
if (chunk.content && typeof chunk.content ===
|
|
89
|
+
if (chunk.content && typeof chunk.content === "string") {
|
|
84
90
|
fullResponse += chunk.content;
|
|
85
91
|
// Format and output the chunk with syntax highlighting
|
|
86
92
|
const formatted = formatter.processChunk(chunk.content);
|
|
@@ -92,7 +98,15 @@ When you need to perform file operations, use the appropriate tools rather than
|
|
|
92
98
|
if (chunk.tool_calls && chunk.tool_calls.length > 0) {
|
|
93
99
|
toolCalls = chunk.tool_calls;
|
|
94
100
|
}
|
|
101
|
+
// Capture usage metadata from the chunk
|
|
102
|
+
if (chunk.usage_metadata) {
|
|
103
|
+
this.usage.promptTokens += chunk.usage_metadata.input_tokens || 0;
|
|
104
|
+
this.usage.completionTokens += chunk.usage_metadata.output_tokens || 0;
|
|
105
|
+
this.usage.totalTokens += chunk.usage_metadata.total_tokens || 0;
|
|
106
|
+
}
|
|
95
107
|
}
|
|
108
|
+
// Increment request count
|
|
109
|
+
this.usage.requestCount++;
|
|
96
110
|
// Flush any remaining content in the formatter
|
|
97
111
|
const remaining = formatter.flush();
|
|
98
112
|
if (remaining) {
|
|
@@ -106,7 +120,7 @@ When you need to perform file operations, use the appropriate tools rather than
|
|
|
106
120
|
tracker_1.tracker.apiError(apiError.message, { stack: apiError.stack });
|
|
107
121
|
throw apiError;
|
|
108
122
|
}
|
|
109
|
-
process.stdout.write(
|
|
123
|
+
process.stdout.write("\n\n");
|
|
110
124
|
// Log AI response if there's content
|
|
111
125
|
if (fullResponse.length > 0) {
|
|
112
126
|
tracker_1.tracker.aiResponse(fullResponse);
|
|
@@ -114,14 +128,14 @@ When you need to perform file operations, use the appropriate tools rather than
|
|
|
114
128
|
// If we have tool calls, execute them
|
|
115
129
|
if (toolCalls.length > 0) {
|
|
116
130
|
// Log AI's tool decision
|
|
117
|
-
tracker_1.tracker.aiToolDecision(toolCalls.map(tc => ({
|
|
131
|
+
tracker_1.tracker.aiToolDecision(toolCalls.map((tc) => ({
|
|
118
132
|
name: tc.name,
|
|
119
|
-
args: tc.args || {}
|
|
133
|
+
args: tc.args || {},
|
|
120
134
|
})));
|
|
121
135
|
// Add AI message with tool calls
|
|
122
136
|
this.messages.push(new messages_1.AIMessage({
|
|
123
137
|
content: fullResponse,
|
|
124
|
-
tool_calls: toolCalls
|
|
138
|
+
tool_calls: toolCalls,
|
|
125
139
|
}));
|
|
126
140
|
// Execute tools
|
|
127
141
|
const results = await this.executeToolCalls(toolCalls);
|
|
@@ -132,10 +146,10 @@ When you need to perform file operations, use the appropriate tools rather than
|
|
|
132
146
|
this.messages.push(new messages_1.ToolMessage({
|
|
133
147
|
content: result.success ? result.output : `Error: ${result.error}`,
|
|
134
148
|
tool_call_id: toolCall.id,
|
|
135
|
-
name: toolCall.name
|
|
149
|
+
name: toolCall.name,
|
|
136
150
|
}));
|
|
137
151
|
}
|
|
138
|
-
tracker_1.tracker.iteration(iterationCount,
|
|
152
|
+
tracker_1.tracker.iteration(iterationCount, "Tool execution complete, continuing loop");
|
|
139
153
|
// Continue loop to get AI's response after tool execution
|
|
140
154
|
continue;
|
|
141
155
|
}
|
|
@@ -143,31 +157,98 @@ When you need to perform file operations, use the appropriate tools rather than
|
|
|
143
157
|
// No tool calls, add final AI message and end loop
|
|
144
158
|
this.messages.push(new messages_1.AIMessage(fullResponse));
|
|
145
159
|
continueLoop = false;
|
|
146
|
-
tracker_1.tracker.iteration(iterationCount,
|
|
160
|
+
tracker_1.tracker.iteration(iterationCount, "No tool calls, ending loop");
|
|
147
161
|
}
|
|
148
162
|
}
|
|
149
163
|
if (iterationCount >= maxIterations) {
|
|
150
|
-
ui_1.log.warning(
|
|
151
|
-
tracker_1.tracker.warn(
|
|
164
|
+
ui_1.log.warning("Maximum iteration limit reached. Ending conversation turn.");
|
|
165
|
+
tracker_1.tracker.warn("Max iterations reached", { iterations: iterationCount });
|
|
152
166
|
}
|
|
153
167
|
// Log success
|
|
154
168
|
const duration = Date.now() - startTime;
|
|
155
169
|
tracker_1.tracker.promptComplete(input, duration, iterationCount);
|
|
170
|
+
return { success: true };
|
|
156
171
|
}
|
|
157
172
|
catch (error) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
tracker_1.tracker.
|
|
173
|
+
const errorMessage = error.message || "Unknown error";
|
|
174
|
+
ui_1.log.error(`Error: ${errorMessage}`);
|
|
175
|
+
tracker_1.tracker.promptFailed(input, errorMessage);
|
|
176
|
+
tracker_1.tracker.error("Chat processing failed", error);
|
|
177
|
+
// Detect model-specific errors
|
|
178
|
+
const isModelError = this.isModelError(errorMessage);
|
|
179
|
+
const errorType = this.classifyError(errorMessage);
|
|
180
|
+
if (isModelError) {
|
|
181
|
+
// Remove the user message so they can retry with a different model
|
|
182
|
+
this.messages.pop();
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
success: false,
|
|
186
|
+
error: errorMessage,
|
|
187
|
+
isModelError,
|
|
188
|
+
errorType,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Check if error is related to the model (rate limit, unavailable, etc.)
|
|
194
|
+
*/
|
|
195
|
+
isModelError(errorMessage) {
|
|
196
|
+
const modelErrorPatterns = [
|
|
197
|
+
"rate limit",
|
|
198
|
+
"rate_limit",
|
|
199
|
+
"quota exceeded",
|
|
200
|
+
"model not found",
|
|
201
|
+
"model is not available",
|
|
202
|
+
"model_not_available",
|
|
203
|
+
"overloaded",
|
|
204
|
+
"capacity",
|
|
205
|
+
"too many requests",
|
|
206
|
+
"429",
|
|
207
|
+
"503",
|
|
208
|
+
"502",
|
|
209
|
+
"service unavailable",
|
|
210
|
+
"timeout",
|
|
211
|
+
"context length",
|
|
212
|
+
"maximum context",
|
|
213
|
+
"token limit",
|
|
214
|
+
];
|
|
215
|
+
const lowerMessage = errorMessage.toLowerCase();
|
|
216
|
+
return modelErrorPatterns.some((pattern) => lowerMessage.includes(pattern));
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Classify the type of error
|
|
220
|
+
*/
|
|
221
|
+
classifyError(errorMessage) {
|
|
222
|
+
const lowerMessage = errorMessage.toLowerCase();
|
|
223
|
+
if (lowerMessage.includes("rate limit") ||
|
|
224
|
+
lowerMessage.includes("rate_limit") ||
|
|
225
|
+
lowerMessage.includes("too many requests") ||
|
|
226
|
+
lowerMessage.includes("429")) {
|
|
227
|
+
return "rate_limit";
|
|
161
228
|
}
|
|
229
|
+
if (lowerMessage.includes("model not found") ||
|
|
230
|
+
lowerMessage.includes("not available") ||
|
|
231
|
+
lowerMessage.includes("overloaded") ||
|
|
232
|
+
lowerMessage.includes("503") ||
|
|
233
|
+
lowerMessage.includes("502")) {
|
|
234
|
+
return "model_unavailable";
|
|
235
|
+
}
|
|
236
|
+
if (lowerMessage.includes("auth") ||
|
|
237
|
+
lowerMessage.includes("api key") ||
|
|
238
|
+
lowerMessage.includes("unauthorized") ||
|
|
239
|
+
lowerMessage.includes("401")) {
|
|
240
|
+
return "auth_error";
|
|
241
|
+
}
|
|
242
|
+
return "unknown";
|
|
162
243
|
}
|
|
163
244
|
/**
|
|
164
245
|
* Execute tool calls from AI
|
|
165
246
|
*/
|
|
166
247
|
async executeToolCalls(toolCalls) {
|
|
167
|
-
const formattedCalls = toolCalls.map(tc => ({
|
|
248
|
+
const formattedCalls = toolCalls.map((tc) => ({
|
|
168
249
|
id: tc.id || `call-${Date.now()}`,
|
|
169
250
|
name: tc.name,
|
|
170
|
-
args: tc.args || {}
|
|
251
|
+
args: tc.args || {},
|
|
171
252
|
}));
|
|
172
253
|
return await this.toolExecutor.executeMultiple(formattedCalls);
|
|
173
254
|
}
|
|
@@ -178,7 +259,7 @@ When you need to perform file operations, use the appropriate tools rather than
|
|
|
178
259
|
const allFiles = await (0, context_1.getFileTree)();
|
|
179
260
|
const attached = [];
|
|
180
261
|
for (const file of allFiles) {
|
|
181
|
-
const base = file.split(
|
|
262
|
+
const base = file.split("/").pop() || "";
|
|
182
263
|
const isBaseMatch = base.length > 2 && input.includes(base);
|
|
183
264
|
const isPathMatch = input.includes(file);
|
|
184
265
|
if (isPathMatch || isBaseMatch) {
|
|
@@ -197,7 +278,7 @@ When you need to perform file operations, use the appropriate tools rather than
|
|
|
197
278
|
clearHistory() {
|
|
198
279
|
const systemMsg = this.messages[0];
|
|
199
280
|
this.messages = [systemMsg];
|
|
200
|
-
tracker_1.tracker.debug(
|
|
281
|
+
tracker_1.tracker.debug("Chat history cleared");
|
|
201
282
|
}
|
|
202
283
|
/**
|
|
203
284
|
* Get message count
|
|
@@ -205,6 +286,12 @@ When you need to perform file operations, use the appropriate tools rather than
|
|
|
205
286
|
getMessageCount() {
|
|
206
287
|
return this.messages.length;
|
|
207
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* Get usage statistics
|
|
291
|
+
*/
|
|
292
|
+
getUsageStats() {
|
|
293
|
+
return { ...this.usage };
|
|
294
|
+
}
|
|
208
295
|
/**
|
|
209
296
|
* Update context (e.g., if file tree changes)
|
|
210
297
|
*/
|
|
@@ -215,5 +302,23 @@ When you need to perform file operations, use the appropriate tools rather than
|
|
|
215
302
|
this.messages[0] = systemMsg; // Keep the same message for now
|
|
216
303
|
// In a more sophisticated implementation, we'd rebuild the system message
|
|
217
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* Update the model provider (for switching models)
|
|
307
|
+
*/
|
|
308
|
+
updateModelProvider(modelProvider) {
|
|
309
|
+
this.modelProvider = modelProvider;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Get the last user input (for retry after model switch)
|
|
313
|
+
*/
|
|
314
|
+
getLastUserInput() {
|
|
315
|
+
for (let i = this.messages.length - 1; i >= 0; i--) {
|
|
316
|
+
const msg = this.messages[i];
|
|
317
|
+
if (msg instanceof messages_1.HumanMessage) {
|
|
318
|
+
return msg.content;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
218
323
|
}
|
|
219
324
|
exports.ChatHandler = ChatHandler;
|
package/dist/lib/config.js
CHANGED
|
@@ -8,8 +8,8 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const os_1 = __importDefault(require("os"));
|
|
10
10
|
const registry_1 = require("./models/registry");
|
|
11
|
-
const CONFIG_DIR = path_1.default.join(os_1.default.homedir(),
|
|
12
|
-
const CONFIG_FILE = path_1.default.join(CONFIG_DIR,
|
|
11
|
+
const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), ".config", "groq-cli-tool");
|
|
12
|
+
const CONFIG_FILE = path_1.default.join(CONFIG_DIR, "config.json");
|
|
13
13
|
const ensureConfigDir = () => {
|
|
14
14
|
if (!fs_1.default.existsSync(CONFIG_DIR)) {
|
|
15
15
|
fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
@@ -19,7 +19,7 @@ const readConfig = () => {
|
|
|
19
19
|
try {
|
|
20
20
|
if (!fs_1.default.existsSync(CONFIG_FILE))
|
|
21
21
|
return {};
|
|
22
|
-
return JSON.parse(fs_1.default.readFileSync(CONFIG_FILE,
|
|
22
|
+
return JSON.parse(fs_1.default.readFileSync(CONFIG_FILE, "utf-8"));
|
|
23
23
|
}
|
|
24
24
|
catch {
|
|
25
25
|
return {};
|
|
@@ -35,22 +35,22 @@ const writeConfig = (data) => {
|
|
|
35
35
|
const getConfig = () => {
|
|
36
36
|
const config = readConfig();
|
|
37
37
|
// Handle legacy config format (old apiKey -> new apiKeys.groq)
|
|
38
|
-
if (
|
|
38
|
+
if ("apiKey" in config && typeof config.apiKey === "string") {
|
|
39
39
|
const legacyKey = config.apiKey;
|
|
40
40
|
delete config.apiKey;
|
|
41
41
|
config.apiKeys = { groq: legacyKey };
|
|
42
42
|
writeConfig(config);
|
|
43
43
|
}
|
|
44
44
|
return {
|
|
45
|
-
apiKeys: config.apiKeys || { groq:
|
|
45
|
+
apiKeys: config.apiKeys || { groq: "" },
|
|
46
46
|
activeModel: config.activeModel || (0, registry_1.getDefaultModel)(),
|
|
47
47
|
preferences: {
|
|
48
48
|
temperature: config.preferences?.temperature ?? 0.7,
|
|
49
49
|
autoConfirm: config.preferences?.autoConfirm ?? false,
|
|
50
50
|
safeMode: config.preferences?.safeMode ?? true,
|
|
51
|
-
maxTokens: config.preferences?.maxTokens
|
|
51
|
+
maxTokens: config.preferences?.maxTokens,
|
|
52
52
|
},
|
|
53
|
-
session: config.session || { todos: [] }
|
|
53
|
+
session: config.session || { todos: [] },
|
|
54
54
|
};
|
|
55
55
|
};
|
|
56
56
|
exports.getConfig = getConfig;
|
|
@@ -59,7 +59,7 @@ exports.getConfig = getConfig;
|
|
|
59
59
|
*/
|
|
60
60
|
const getApiKey = () => {
|
|
61
61
|
const config = (0, exports.getConfig)();
|
|
62
|
-
return config.apiKeys.groq ||
|
|
62
|
+
return config.apiKeys.groq || "";
|
|
63
63
|
};
|
|
64
64
|
exports.getApiKey = getApiKey;
|
|
65
65
|
/**
|
|
@@ -68,7 +68,7 @@ exports.getApiKey = getApiKey;
|
|
|
68
68
|
const setApiKey = (key) => {
|
|
69
69
|
const config = readConfig();
|
|
70
70
|
if (!config.apiKeys) {
|
|
71
|
-
config.apiKeys = { groq:
|
|
71
|
+
config.apiKeys = { groq: "" };
|
|
72
72
|
}
|
|
73
73
|
config.apiKeys.groq = key;
|
|
74
74
|
writeConfig(config);
|
|
@@ -80,7 +80,7 @@ exports.setApiKey = setApiKey;
|
|
|
80
80
|
const clearApiKey = () => {
|
|
81
81
|
const config = readConfig();
|
|
82
82
|
if (config.apiKeys) {
|
|
83
|
-
config.apiKeys.groq =
|
|
83
|
+
config.apiKeys.groq = "";
|
|
84
84
|
}
|
|
85
85
|
writeConfig(config);
|
|
86
86
|
};
|
|
@@ -92,7 +92,7 @@ const getModelConfig = () => {
|
|
|
92
92
|
const config = (0, exports.getConfig)();
|
|
93
93
|
return {
|
|
94
94
|
modelId: config.activeModel,
|
|
95
|
-
temperature: config.preferences.temperature
|
|
95
|
+
temperature: config.preferences.temperature,
|
|
96
96
|
};
|
|
97
97
|
};
|
|
98
98
|
exports.getModelConfig = getModelConfig;
|
|
@@ -121,7 +121,7 @@ const setPreference = (key, value) => {
|
|
|
121
121
|
config.preferences = {
|
|
122
122
|
temperature: 0.7,
|
|
123
123
|
autoConfirm: false,
|
|
124
|
-
safeMode: true
|
|
124
|
+
safeMode: true,
|
|
125
125
|
};
|
|
126
126
|
}
|
|
127
127
|
config.preferences[key] = value;
|
package/dist/lib/context.js
CHANGED
|
@@ -12,33 +12,28 @@ const isGitRepo = async () => {
|
|
|
12
12
|
try {
|
|
13
13
|
return await git.checkIsRepo();
|
|
14
14
|
}
|
|
15
|
-
catch
|
|
15
|
+
catch {
|
|
16
16
|
return false;
|
|
17
17
|
}
|
|
18
18
|
};
|
|
19
19
|
exports.isGitRepo = isGitRepo;
|
|
20
20
|
const getFileTree = async (cwd = process.cwd()) => {
|
|
21
21
|
// Ignore node_modules, .git, dist, etc.
|
|
22
|
-
const ignore = [
|
|
23
|
-
const files = await (0, glob_1.glob)(
|
|
22
|
+
const ignore = ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/.env", "**/*.lock"];
|
|
23
|
+
const files = await (0, glob_1.glob)("**/*", { cwd, ignore, nodir: true });
|
|
24
24
|
return files;
|
|
25
25
|
};
|
|
26
26
|
exports.getFileTree = getFileTree;
|
|
27
27
|
const getFileContent = (filePath) => {
|
|
28
28
|
try {
|
|
29
|
-
return fs_1.default.readFileSync(filePath,
|
|
29
|
+
return fs_1.default.readFileSync(filePath, "utf-8");
|
|
30
30
|
}
|
|
31
|
-
catch
|
|
32
|
-
return
|
|
31
|
+
catch {
|
|
32
|
+
return "";
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
35
|
exports.getFileContent = getFileContent;
|
|
36
36
|
const writeFile = (filePath, content) => {
|
|
37
|
-
|
|
38
|
-
fs_1.default.writeFileSync(filePath, content, 'utf-8');
|
|
39
|
-
}
|
|
40
|
-
catch (e) {
|
|
41
|
-
throw e;
|
|
42
|
-
}
|
|
37
|
+
fs_1.default.writeFileSync(filePath, content, "utf-8");
|
|
43
38
|
};
|
|
44
39
|
exports.writeFile = writeFile;
|
package/dist/lib/groq-models.js
CHANGED
|
@@ -16,12 +16,12 @@ async function fetchGroqModels(apiKey) {
|
|
|
16
16
|
const response = await groq.models.list();
|
|
17
17
|
// Filter and sort models
|
|
18
18
|
const models = response.data
|
|
19
|
-
.filter(m => m.active !== false)
|
|
19
|
+
.filter((m) => m.active !== false)
|
|
20
20
|
.sort((a, b) => a.id.localeCompare(b.id));
|
|
21
21
|
return models;
|
|
22
22
|
}
|
|
23
23
|
catch (error) {
|
|
24
|
-
console.error(
|
|
24
|
+
console.error("Failed to fetch models:", error.message);
|
|
25
25
|
return [];
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -31,7 +31,7 @@ async function fetchGroqModels(apiKey) {
|
|
|
31
31
|
function groupModelsByOwner(models) {
|
|
32
32
|
const groups = new Map();
|
|
33
33
|
for (const model of models) {
|
|
34
|
-
const owner = model.owned_by ||
|
|
34
|
+
const owner = model.owned_by || "Other";
|
|
35
35
|
if (!groups.has(owner)) {
|
|
36
36
|
groups.set(owner, []);
|
|
37
37
|
}
|
package/dist/lib/groq.js
CHANGED
|
@@ -8,22 +8,22 @@ let model = null;
|
|
|
8
8
|
const initGroq = () => {
|
|
9
9
|
const apiKey = (0, config_1.getApiKey)();
|
|
10
10
|
if (!apiKey) {
|
|
11
|
-
throw new Error(
|
|
11
|
+
throw new Error("API Key not found");
|
|
12
12
|
}
|
|
13
13
|
model = new groq_1.ChatGroq({
|
|
14
14
|
apiKey,
|
|
15
|
-
model:
|
|
16
|
-
temperature: 0.7
|
|
15
|
+
model: "llama-3.3-70b-versatile",
|
|
16
|
+
temperature: 0.7,
|
|
17
17
|
});
|
|
18
18
|
};
|
|
19
19
|
exports.initGroq = initGroq;
|
|
20
20
|
const streamChat = async (messages) => {
|
|
21
21
|
if (!model)
|
|
22
22
|
(0, exports.initGroq)();
|
|
23
|
-
const langChainMessages = messages.map(m => {
|
|
24
|
-
if (m.role ===
|
|
23
|
+
const langChainMessages = messages.map((m) => {
|
|
24
|
+
if (m.role === "system")
|
|
25
25
|
return new messages_1.SystemMessage(m.content);
|
|
26
|
-
if (m.role ===
|
|
26
|
+
if (m.role === "user")
|
|
27
27
|
return new messages_1.HumanMessage(m.content);
|
|
28
28
|
return new messages_1.AIMessage(m.content);
|
|
29
29
|
});
|
package/dist/lib/safety.js
CHANGED
|
@@ -33,23 +33,16 @@ class SafetyChecker {
|
|
|
33
33
|
*/
|
|
34
34
|
isExtremelyDangerous(tool, params) {
|
|
35
35
|
// Git operations that can't be easily undone
|
|
36
|
-
if (tool.name ===
|
|
36
|
+
if (tool.name === "git_push" && params.force) {
|
|
37
37
|
return true;
|
|
38
38
|
}
|
|
39
|
-
if (tool.name ===
|
|
39
|
+
if (tool.name === "git_branch" && params.action === "delete") {
|
|
40
40
|
return true;
|
|
41
41
|
}
|
|
42
42
|
// Shell commands with dangerous patterns
|
|
43
|
-
if (tool.name ===
|
|
44
|
-
const dangerous = [
|
|
45
|
-
|
|
46
|
-
/rm\s+.*\/\*/,
|
|
47
|
-
/>\s*\/dev/,
|
|
48
|
-
/dd\s+if=/,
|
|
49
|
-
/mkfs/,
|
|
50
|
-
/format/i
|
|
51
|
-
];
|
|
52
|
-
return dangerous.some(pattern => pattern.test(params.command));
|
|
43
|
+
if (tool.name === "bash") {
|
|
44
|
+
const dangerous = [/rm\s+-rf/, /rm\s+.*\/\*/, />\s*\/dev/, /dd\s+if=/, /mkfs/, /format/i];
|
|
45
|
+
return dangerous.some((pattern) => pattern.test(params.command));
|
|
53
46
|
}
|
|
54
47
|
return false;
|
|
55
48
|
}
|
|
@@ -58,28 +51,28 @@ class SafetyChecker {
|
|
|
58
51
|
*/
|
|
59
52
|
async promptConfirmation(tool, params) {
|
|
60
53
|
const description = this.getOperationDescription(tool, params);
|
|
61
|
-
console.log(
|
|
54
|
+
console.log("\n⚠️ Confirmation required:");
|
|
62
55
|
console.log(`Tool: ${tool.name}`);
|
|
63
56
|
console.log(`Operation: ${description}`);
|
|
64
|
-
console.log(
|
|
57
|
+
console.log("");
|
|
65
58
|
const { proceed, remember } = await inquirer_1.default.prompt([
|
|
66
59
|
{
|
|
67
|
-
type:
|
|
68
|
-
name:
|
|
69
|
-
message:
|
|
70
|
-
default: false
|
|
60
|
+
type: "confirm",
|
|
61
|
+
name: "proceed",
|
|
62
|
+
message: "Do you want to proceed with this operation?",
|
|
63
|
+
default: false,
|
|
71
64
|
},
|
|
72
65
|
{
|
|
73
|
-
type:
|
|
74
|
-
name:
|
|
75
|
-
message:
|
|
66
|
+
type: "confirm",
|
|
67
|
+
name: "remember",
|
|
68
|
+
message: "Remember this choice for similar operations in this session?",
|
|
76
69
|
default: false,
|
|
77
|
-
when: (answers) => answers.proceed
|
|
78
|
-
}
|
|
70
|
+
when: (answers) => answers.proceed,
|
|
71
|
+
},
|
|
79
72
|
]);
|
|
80
73
|
return {
|
|
81
74
|
confirmed: proceed,
|
|
82
|
-
rememberChoice: remember || false
|
|
75
|
+
rememberChoice: remember || false,
|
|
83
76
|
};
|
|
84
77
|
}
|
|
85
78
|
/**
|
|
@@ -87,20 +80,22 @@ class SafetyChecker {
|
|
|
87
80
|
*/
|
|
88
81
|
getOperationDescription(tool, params) {
|
|
89
82
|
switch (tool.name) {
|
|
90
|
-
case
|
|
83
|
+
case "write_file":
|
|
91
84
|
return `Write to file: ${params.file_path}`;
|
|
92
|
-
case
|
|
85
|
+
case "edit_file":
|
|
93
86
|
return `Edit file: ${params.file_path} (replace "${params.search.substring(0, 50)}...")`;
|
|
94
|
-
case
|
|
87
|
+
case "bash":
|
|
95
88
|
return `Execute command: ${params.command}`;
|
|
96
|
-
case
|
|
97
|
-
const files = params.files ? ` (${params.files.length} files)` :
|
|
89
|
+
case "git_commit": {
|
|
90
|
+
const files = params.files ? ` (${params.files.length} files)` : "";
|
|
98
91
|
return `Create git commit${files}: "${params.message}"`;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
92
|
+
}
|
|
93
|
+
case "git_push": {
|
|
94
|
+
const force = params.force ? " (FORCE)" : "";
|
|
95
|
+
return `Push to remote${force}: ${params.branch || "current branch"}`;
|
|
96
|
+
}
|
|
97
|
+
case "git_branch":
|
|
98
|
+
return `${params.action} branch: ${params.name || "N/A"}`;
|
|
104
99
|
default:
|
|
105
100
|
return JSON.stringify(params, null, 2);
|
|
106
101
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SLASH_COMMANDS = void 0;
|
|
7
|
+
exports.showSlashCommandMenu = showSlashCommandMenu;
|
|
8
|
+
const search_1 = __importDefault(require("@inquirer/search"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
exports.SLASH_COMMANDS = [
|
|
11
|
+
{
|
|
12
|
+
name: "/model",
|
|
13
|
+
description: "Switch between available AI models",
|
|
14
|
+
shortcut: "m",
|
|
15
|
+
action: "model",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "/usage",
|
|
19
|
+
description: "Show model info and session statistics",
|
|
20
|
+
shortcut: "u",
|
|
21
|
+
action: "usage",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "/tools",
|
|
25
|
+
description: "List all available tools",
|
|
26
|
+
shortcut: "t",
|
|
27
|
+
action: "tools",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "/todos",
|
|
31
|
+
description: "Display current todo list",
|
|
32
|
+
action: "todos",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "/clear-todos",
|
|
36
|
+
description: "Clear all todos",
|
|
37
|
+
action: "clear-todos",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "/context",
|
|
41
|
+
description: "Show file context from current directory",
|
|
42
|
+
shortcut: "c",
|
|
43
|
+
action: "context",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "/clear",
|
|
47
|
+
description: "Clear chat history",
|
|
48
|
+
action: "clear",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "/api-key",
|
|
52
|
+
description: "Update your Groq API key",
|
|
53
|
+
action: "api-key",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "/exit",
|
|
57
|
+
description: "Exit the application",
|
|
58
|
+
shortcut: "q",
|
|
59
|
+
action: "exit",
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
function formatCommandChoice(cmd) {
|
|
63
|
+
const shortcut = cmd.shortcut ? chalk_1.default.dim(` (${cmd.shortcut})`) : "";
|
|
64
|
+
return `${chalk_1.default.cyan(cmd.name)}${shortcut} ${chalk_1.default.dim("─")} ${cmd.description}`;
|
|
65
|
+
}
|
|
66
|
+
async function showSlashCommandMenu() {
|
|
67
|
+
try {
|
|
68
|
+
const answer = await (0, search_1.default)({
|
|
69
|
+
message: "Command",
|
|
70
|
+
source: async (input) => {
|
|
71
|
+
const searchTerm = (input || "").toLowerCase().replace(/^\//, "");
|
|
72
|
+
return exports.SLASH_COMMANDS.filter((cmd) => {
|
|
73
|
+
if (!searchTerm)
|
|
74
|
+
return true;
|
|
75
|
+
return (cmd.name.toLowerCase().includes(searchTerm) ||
|
|
76
|
+
cmd.description.toLowerCase().includes(searchTerm) ||
|
|
77
|
+
cmd.shortcut?.toLowerCase() === searchTerm);
|
|
78
|
+
}).map((cmd) => ({
|
|
79
|
+
name: formatCommandChoice(cmd),
|
|
80
|
+
value: cmd.action,
|
|
81
|
+
description: cmd.description,
|
|
82
|
+
}));
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
return answer;
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
// User cancelled (Ctrl+C)
|
|
89
|
+
if (error.message?.includes("cancelled") || error.name === "ExitPromptError") {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|