prab-cli 1.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/LICENSE +15 -0
- package/README.md +272 -0
- package/dist/index.js +374 -0
- package/dist/lib/chat-handler.js +219 -0
- package/dist/lib/config.js +156 -0
- package/dist/lib/context.js +44 -0
- package/dist/lib/groq-models.js +53 -0
- package/dist/lib/groq.js +33 -0
- package/dist/lib/models/groq-provider.js +82 -0
- package/dist/lib/models/provider.js +10 -0
- package/dist/lib/models/registry.js +101 -0
- package/dist/lib/safety.js +109 -0
- package/dist/lib/tools/base.js +115 -0
- package/dist/lib/tools/executor.js +127 -0
- package/dist/lib/tools/file-tools.js +283 -0
- package/dist/lib/tools/git-tools.js +280 -0
- package/dist/lib/tools/shell-tools.js +73 -0
- package/dist/lib/tools/todo-tool.js +105 -0
- package/dist/lib/tracker.js +314 -0
- package/dist/lib/ui.js +578 -0
- package/dist/log-viewer.js +374 -0
- package/dist/types/index.js +5 -0
- package/package.json +75 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TodoTool = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const base_1 = require("./base");
|
|
6
|
+
const config_1 = require("../config");
|
|
7
|
+
const ui_1 = require("../ui");
|
|
8
|
+
/**
|
|
9
|
+
* Manage todos for multi-step tasks
|
|
10
|
+
*/
|
|
11
|
+
class TodoTool extends base_1.Tool {
|
|
12
|
+
constructor() {
|
|
13
|
+
super(...arguments);
|
|
14
|
+
this.name = 'manage_todos';
|
|
15
|
+
this.description = 'Create, update, or complete todo items to track progress on multi-step tasks. Use this proactively for complex operations.';
|
|
16
|
+
this.requiresConfirmation = false;
|
|
17
|
+
this.destructive = false;
|
|
18
|
+
this.schema = zod_1.z.object({
|
|
19
|
+
action: zod_1.z.enum(['create', 'update', 'complete', 'list', 'clear']).describe('Action to perform'),
|
|
20
|
+
todos: zod_1.z.array(zod_1.z.object({
|
|
21
|
+
id: zod_1.z.string().optional(),
|
|
22
|
+
content: zod_1.z.string(),
|
|
23
|
+
activeForm: zod_1.z.string(),
|
|
24
|
+
status: zod_1.z.enum(['pending', 'in_progress', 'completed'])
|
|
25
|
+
})).optional().describe('Todo items for create/update actions')
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async execute(params) {
|
|
29
|
+
try {
|
|
30
|
+
const session = (0, config_1.getSessionData)();
|
|
31
|
+
let todos = session.todos || [];
|
|
32
|
+
switch (params.action) {
|
|
33
|
+
case 'create': {
|
|
34
|
+
if (!params.todos || params.todos.length === 0) {
|
|
35
|
+
return this.error('No todos provided for create action');
|
|
36
|
+
}
|
|
37
|
+
// Add new todos
|
|
38
|
+
const newTodos = params.todos.map(todo => ({
|
|
39
|
+
id: todo.id || `todo-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
40
|
+
content: todo.content,
|
|
41
|
+
activeForm: todo.activeForm,
|
|
42
|
+
status: todo.status,
|
|
43
|
+
createdAt: Date.now()
|
|
44
|
+
}));
|
|
45
|
+
todos = [...todos, ...newTodos];
|
|
46
|
+
(0, config_1.setSessionData)({ todos });
|
|
47
|
+
(0, ui_1.showTodoList)(todos);
|
|
48
|
+
return this.success(`Created ${newTodos.length} todo(s)`, { count: newTodos.length, todos });
|
|
49
|
+
}
|
|
50
|
+
case 'update': {
|
|
51
|
+
if (!params.todos || params.todos.length === 0) {
|
|
52
|
+
return this.error('No todos provided for update action');
|
|
53
|
+
}
|
|
54
|
+
// Update existing todos
|
|
55
|
+
for (const update of params.todos) {
|
|
56
|
+
const index = todos.findIndex(t => t.id === update.id || t.content === update.content);
|
|
57
|
+
if (index !== -1) {
|
|
58
|
+
todos[index] = {
|
|
59
|
+
...todos[index],
|
|
60
|
+
content: update.content,
|
|
61
|
+
activeForm: update.activeForm,
|
|
62
|
+
status: update.status
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
(0, config_1.setSessionData)({ todos });
|
|
67
|
+
(0, ui_1.showTodoList)(todos);
|
|
68
|
+
return this.success(`Updated ${params.todos.length} todo(s)`, { todos });
|
|
69
|
+
}
|
|
70
|
+
case 'complete': {
|
|
71
|
+
if (!params.todos || params.todos.length === 0) {
|
|
72
|
+
return this.error('No todos provided for complete action');
|
|
73
|
+
}
|
|
74
|
+
// Mark todos as completed
|
|
75
|
+
for (const completedTodo of params.todos) {
|
|
76
|
+
const index = todos.findIndex(t => t.id === completedTodo.id || t.content === completedTodo.content);
|
|
77
|
+
if (index !== -1) {
|
|
78
|
+
todos[index].status = 'completed';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
(0, config_1.setSessionData)({ todos });
|
|
82
|
+
(0, ui_1.showTodoList)(todos);
|
|
83
|
+
return this.success(`Completed ${params.todos.length} todo(s)`, { todos });
|
|
84
|
+
}
|
|
85
|
+
case 'list': {
|
|
86
|
+
(0, ui_1.showTodoList)(todos);
|
|
87
|
+
const pending = todos.filter(t => t.status === 'pending').length;
|
|
88
|
+
const inProgress = todos.filter(t => t.status === 'in_progress').length;
|
|
89
|
+
const completed = todos.filter(t => t.status === 'completed').length;
|
|
90
|
+
return this.success(`Total todos: ${todos.length} (${pending} pending, ${inProgress} in progress, ${completed} completed)`, { todos, counts: { pending, inProgress, completed } });
|
|
91
|
+
}
|
|
92
|
+
case 'clear': {
|
|
93
|
+
(0, config_1.setSessionData)({ todos: [] });
|
|
94
|
+
return this.success('Cleared all todos');
|
|
95
|
+
}
|
|
96
|
+
default:
|
|
97
|
+
return this.error(`Unknown action: ${params.action}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
return this.error(`Todo management failed: ${error.message}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.TodoTool = TodoTool;
|
|
@@ -0,0 +1,314 @@
|
|
|
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.tracker = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
/**
|
|
11
|
+
* Super Logger - Tracks everything happening in the CLI
|
|
12
|
+
*/
|
|
13
|
+
class SuperTracker {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.logDir = path_1.default.join(os_1.default.homedir(), '.config', 'groq-cli-tool', 'logs');
|
|
16
|
+
this.sessionId = this.generateSessionId();
|
|
17
|
+
this.logFile = path_1.default.join(this.logDir, `session-${this.sessionId}.jsonl`);
|
|
18
|
+
this.ensureLogDir();
|
|
19
|
+
}
|
|
20
|
+
generateSessionId() {
|
|
21
|
+
const now = new Date();
|
|
22
|
+
return `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}-${String(now.getHours()).padStart(2, '0')}${String(now.getMinutes()).padStart(2, '0')}${String(now.getSeconds()).padStart(2, '0')}`;
|
|
23
|
+
}
|
|
24
|
+
ensureLogDir() {
|
|
25
|
+
if (!fs_1.default.existsSync(this.logDir)) {
|
|
26
|
+
fs_1.default.mkdirSync(this.logDir, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Write a log entry to file (with immediate flush)
|
|
31
|
+
*/
|
|
32
|
+
write(entry) {
|
|
33
|
+
const line = JSON.stringify(entry) + '\n';
|
|
34
|
+
const fd = fs_1.default.openSync(this.logFile, 'a');
|
|
35
|
+
fs_1.default.writeSync(fd, line);
|
|
36
|
+
fs_1.default.fsyncSync(fd);
|
|
37
|
+
fs_1.default.closeSync(fd);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Log session start
|
|
41
|
+
*/
|
|
42
|
+
sessionStart(model, toolCount) {
|
|
43
|
+
this.write({
|
|
44
|
+
timestamp: new Date().toISOString(),
|
|
45
|
+
level: 'info',
|
|
46
|
+
event: 'SESSION_START',
|
|
47
|
+
message: `Session started with model: ${model}`,
|
|
48
|
+
data: { model, toolCount, sessionId: this.sessionId }
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Log user prompt received
|
|
53
|
+
*/
|
|
54
|
+
promptReceived(prompt) {
|
|
55
|
+
this.write({
|
|
56
|
+
timestamp: new Date().toISOString(),
|
|
57
|
+
level: 'info',
|
|
58
|
+
event: 'PROMPT_RECEIVED',
|
|
59
|
+
message: `User: "${prompt.length > 100 ? prompt.substring(0, 100) + '...' : prompt}"`,
|
|
60
|
+
data: { prompt, length: prompt.length }
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Log API request to Groq
|
|
65
|
+
*/
|
|
66
|
+
apiRequest(model, messageCount, toolCount) {
|
|
67
|
+
this.write({
|
|
68
|
+
timestamp: new Date().toISOString(),
|
|
69
|
+
level: 'api',
|
|
70
|
+
event: 'API_REQUEST',
|
|
71
|
+
message: `Sending request to Groq API`,
|
|
72
|
+
data: { model, messageCount, toolCount }
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Log API response from Groq
|
|
77
|
+
*/
|
|
78
|
+
apiResponse(hasContent, hasToolCalls, toolCallCount, duration) {
|
|
79
|
+
this.write({
|
|
80
|
+
timestamp: new Date().toISOString(),
|
|
81
|
+
level: 'api',
|
|
82
|
+
event: 'API_RESPONSE',
|
|
83
|
+
message: `Received response from Groq API`,
|
|
84
|
+
data: { hasContent, hasToolCalls, toolCallCount },
|
|
85
|
+
duration
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Log API error
|
|
90
|
+
*/
|
|
91
|
+
apiError(error, details) {
|
|
92
|
+
this.write({
|
|
93
|
+
timestamp: new Date().toISOString(),
|
|
94
|
+
level: 'error',
|
|
95
|
+
event: 'API_ERROR',
|
|
96
|
+
message: `API Error: ${error}`,
|
|
97
|
+
data: { error, details }
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Log AI response text
|
|
102
|
+
*/
|
|
103
|
+
aiResponse(content) {
|
|
104
|
+
this.write({
|
|
105
|
+
timestamp: new Date().toISOString(),
|
|
106
|
+
level: 'ai',
|
|
107
|
+
event: 'AI_RESPONSE',
|
|
108
|
+
message: `AI: "${content.length > 150 ? content.substring(0, 150) + '...' : content}"`,
|
|
109
|
+
data: { content: content.substring(0, 500), length: content.length }
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Log AI decided to call tools
|
|
114
|
+
*/
|
|
115
|
+
aiToolDecision(toolCalls) {
|
|
116
|
+
const toolNames = toolCalls.map(t => t.name).join(', ');
|
|
117
|
+
this.write({
|
|
118
|
+
timestamp: new Date().toISOString(),
|
|
119
|
+
level: 'ai',
|
|
120
|
+
event: 'AI_TOOL_DECISION',
|
|
121
|
+
message: `AI decided to call: [${toolNames}]`,
|
|
122
|
+
data: { toolCalls }
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Log tool execution start
|
|
127
|
+
*/
|
|
128
|
+
toolStart(toolName, args) {
|
|
129
|
+
this.write({
|
|
130
|
+
timestamp: new Date().toISOString(),
|
|
131
|
+
level: 'info',
|
|
132
|
+
event: 'TOOL_START',
|
|
133
|
+
message: `Executing tool: ${toolName}`,
|
|
134
|
+
data: { toolName, args }
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Log tool execution success
|
|
139
|
+
*/
|
|
140
|
+
toolSuccess(toolName, output, duration) {
|
|
141
|
+
this.write({
|
|
142
|
+
timestamp: new Date().toISOString(),
|
|
143
|
+
level: 'success',
|
|
144
|
+
event: 'TOOL_SUCCESS',
|
|
145
|
+
message: `Tool completed: ${toolName}`,
|
|
146
|
+
data: { toolName, outputPreview: output.substring(0, 200), outputLength: output.length },
|
|
147
|
+
duration
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Log tool execution failure
|
|
152
|
+
*/
|
|
153
|
+
toolError(toolName, error, duration, args) {
|
|
154
|
+
this.write({
|
|
155
|
+
timestamp: new Date().toISOString(),
|
|
156
|
+
level: 'error',
|
|
157
|
+
event: 'TOOL_ERROR',
|
|
158
|
+
message: `Tool failed: ${toolName}`,
|
|
159
|
+
data: { toolName, error, args, errorMessage: error },
|
|
160
|
+
duration
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Log tool cancelled by user
|
|
165
|
+
*/
|
|
166
|
+
toolCancelled(toolName) {
|
|
167
|
+
this.write({
|
|
168
|
+
timestamp: new Date().toISOString(),
|
|
169
|
+
level: 'warn',
|
|
170
|
+
event: 'TOOL_CANCELLED',
|
|
171
|
+
message: `Tool cancelled by user: ${toolName}`,
|
|
172
|
+
data: { toolName }
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Log model initialization
|
|
177
|
+
*/
|
|
178
|
+
modelInit(modelId, provider, success, error) {
|
|
179
|
+
this.write({
|
|
180
|
+
timestamp: new Date().toISOString(),
|
|
181
|
+
level: success ? 'success' : 'error',
|
|
182
|
+
event: 'MODEL_INIT',
|
|
183
|
+
message: success ? `Model initialized: ${modelId}` : `Model init failed: ${error}`,
|
|
184
|
+
data: { modelId, provider, success, error }
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Log model switch
|
|
189
|
+
*/
|
|
190
|
+
modelSwitch(fromModel, toModel, success) {
|
|
191
|
+
this.write({
|
|
192
|
+
timestamp: new Date().toISOString(),
|
|
193
|
+
level: success ? 'success' : 'error',
|
|
194
|
+
event: 'MODEL_SWITCH',
|
|
195
|
+
message: success ? `Switched model: ${fromModel} -> ${toModel}` : `Model switch failed: ${fromModel} -> ${toModel}`,
|
|
196
|
+
data: { fromModel, toModel, success }
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Log prompt processing complete
|
|
201
|
+
*/
|
|
202
|
+
promptComplete(prompt, duration, iterations) {
|
|
203
|
+
this.write({
|
|
204
|
+
timestamp: new Date().toISOString(),
|
|
205
|
+
level: 'success',
|
|
206
|
+
event: 'PROMPT_COMPLETE',
|
|
207
|
+
message: `Prompt processed successfully`,
|
|
208
|
+
data: { promptPreview: prompt.substring(0, 50), iterations },
|
|
209
|
+
duration
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Log prompt processing failed
|
|
214
|
+
*/
|
|
215
|
+
promptFailed(prompt, error) {
|
|
216
|
+
this.write({
|
|
217
|
+
timestamp: new Date().toISOString(),
|
|
218
|
+
level: 'error',
|
|
219
|
+
event: 'PROMPT_FAILED',
|
|
220
|
+
message: `Prompt processing failed: ${error}`,
|
|
221
|
+
data: { promptPreview: prompt.substring(0, 50), error }
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Log streaming chunk received
|
|
226
|
+
*/
|
|
227
|
+
streamChunk(hasContent, hasToolCalls) {
|
|
228
|
+
this.write({
|
|
229
|
+
timestamp: new Date().toISOString(),
|
|
230
|
+
level: 'debug',
|
|
231
|
+
event: 'STREAM_CHUNK',
|
|
232
|
+
message: `Stream chunk: content=${hasContent}, tools=${hasToolCalls}`,
|
|
233
|
+
data: { hasContent, hasToolCalls }
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Log iteration in the response loop
|
|
238
|
+
*/
|
|
239
|
+
iteration(count, reason) {
|
|
240
|
+
this.write({
|
|
241
|
+
timestamp: new Date().toISOString(),
|
|
242
|
+
level: 'debug',
|
|
243
|
+
event: 'ITERATION',
|
|
244
|
+
message: `Loop iteration ${count}: ${reason}`,
|
|
245
|
+
data: { count, reason }
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Log context attachment
|
|
250
|
+
*/
|
|
251
|
+
contextAttached(files) {
|
|
252
|
+
this.write({
|
|
253
|
+
timestamp: new Date().toISOString(),
|
|
254
|
+
level: 'info',
|
|
255
|
+
event: 'CONTEXT_ATTACHED',
|
|
256
|
+
message: `Attached ${files.length} file(s) for context`,
|
|
257
|
+
data: { files }
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Generic debug log
|
|
262
|
+
*/
|
|
263
|
+
debug(message, data) {
|
|
264
|
+
this.write({
|
|
265
|
+
timestamp: new Date().toISOString(),
|
|
266
|
+
level: 'debug',
|
|
267
|
+
event: 'DEBUG',
|
|
268
|
+
message,
|
|
269
|
+
data
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Generic warning log
|
|
274
|
+
*/
|
|
275
|
+
warn(message, data) {
|
|
276
|
+
this.write({
|
|
277
|
+
timestamp: new Date().toISOString(),
|
|
278
|
+
level: 'warn',
|
|
279
|
+
event: 'WARNING',
|
|
280
|
+
message,
|
|
281
|
+
data
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Generic error log
|
|
286
|
+
*/
|
|
287
|
+
error(message, error, data) {
|
|
288
|
+
this.write({
|
|
289
|
+
timestamp: new Date().toISOString(),
|
|
290
|
+
level: 'error',
|
|
291
|
+
event: 'ERROR',
|
|
292
|
+
message,
|
|
293
|
+
data: {
|
|
294
|
+
error: error instanceof Error ? error.message : error,
|
|
295
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
296
|
+
...data
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Get log file path
|
|
302
|
+
*/
|
|
303
|
+
getLogFile() {
|
|
304
|
+
return this.logFile;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Get session ID
|
|
308
|
+
*/
|
|
309
|
+
getSessionId() {
|
|
310
|
+
return this.sessionId;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// Singleton instance
|
|
314
|
+
exports.tracker = new SuperTracker();
|