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,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MODEL_REGISTRY = void 0;
|
|
4
|
+
exports.getModelInfo = getModelInfo;
|
|
5
|
+
exports.isValidModel = isValidModel;
|
|
6
|
+
exports.getAllModelIds = getAllModelIds;
|
|
7
|
+
exports.getDefaultModel = getDefaultModel;
|
|
8
|
+
exports.getModelList = getModelList;
|
|
9
|
+
exports.validateModelId = validateModelId;
|
|
10
|
+
/**
|
|
11
|
+
* Registry of available models with their metadata
|
|
12
|
+
* Note: Whisper models are excluded as they are audio transcription models, not chat models
|
|
13
|
+
*/
|
|
14
|
+
exports.MODEL_REGISTRY = {
|
|
15
|
+
// OpenAI-compatible models on Groq - Better function calling
|
|
16
|
+
'openai/gpt-oss-20b': {
|
|
17
|
+
id: 'openai/gpt-oss-20b',
|
|
18
|
+
provider: 'groq',
|
|
19
|
+
capabilities: ['chat', 'streaming', 'function_calling'],
|
|
20
|
+
description: 'GPT-OSS 20B - OpenAI-compatible model with better function calling'
|
|
21
|
+
},
|
|
22
|
+
// Groq Models
|
|
23
|
+
'llama-3.3-70b-versatile': {
|
|
24
|
+
id: 'llama-3.3-70b-versatile',
|
|
25
|
+
provider: 'groq',
|
|
26
|
+
capabilities: ['chat', 'streaming', 'function_calling'],
|
|
27
|
+
description: 'Llama 3.3 70B - Fast and versatile, great for coding tasks'
|
|
28
|
+
},
|
|
29
|
+
'llama-3.1-70b-versatile': {
|
|
30
|
+
id: 'llama-3.1-70b-versatile',
|
|
31
|
+
provider: 'groq',
|
|
32
|
+
capabilities: ['chat', 'streaming', 'function_calling'],
|
|
33
|
+
description: 'Llama 3.1 70B - Versatile model for various tasks'
|
|
34
|
+
},
|
|
35
|
+
'llama-3.1-8b-instant': {
|
|
36
|
+
id: 'llama-3.1-8b-instant',
|
|
37
|
+
provider: 'groq',
|
|
38
|
+
capabilities: ['chat', 'streaming', 'function_calling'],
|
|
39
|
+
description: 'Llama 3.1 8B - Fast and efficient for quick responses'
|
|
40
|
+
},
|
|
41
|
+
'mixtral-8x7b-32768': {
|
|
42
|
+
id: 'mixtral-8x7b-32768',
|
|
43
|
+
provider: 'groq',
|
|
44
|
+
capabilities: ['chat', 'streaming', 'function_calling'],
|
|
45
|
+
description: 'Mixtral 8x7B - Excellent for complex reasoning'
|
|
46
|
+
},
|
|
47
|
+
'gemma2-9b-it': {
|
|
48
|
+
id: 'gemma2-9b-it',
|
|
49
|
+
provider: 'groq',
|
|
50
|
+
capabilities: ['chat', 'streaming', 'function_calling'],
|
|
51
|
+
description: 'Gemma 2 9B - Google\'s efficient instruction-tuned model'
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Get model information by ID
|
|
56
|
+
*/
|
|
57
|
+
function getModelInfo(modelId) {
|
|
58
|
+
return exports.MODEL_REGISTRY[modelId];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if a model ID is valid
|
|
62
|
+
*/
|
|
63
|
+
function isValidModel(modelId) {
|
|
64
|
+
return modelId in exports.MODEL_REGISTRY;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get all available model IDs
|
|
68
|
+
*/
|
|
69
|
+
function getAllModelIds() {
|
|
70
|
+
return Object.keys(exports.MODEL_REGISTRY);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get default model ID
|
|
74
|
+
*/
|
|
75
|
+
function getDefaultModel() {
|
|
76
|
+
return 'openai/gpt-oss-20b';
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get formatted list of models for display
|
|
80
|
+
*/
|
|
81
|
+
function getModelList() {
|
|
82
|
+
return Object.values(exports.MODEL_REGISTRY)
|
|
83
|
+
.map(model => ` ${model.id}\n ${model.description}`)
|
|
84
|
+
.join('\n\n');
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Validate and sanitize model ID
|
|
88
|
+
*/
|
|
89
|
+
function validateModelId(modelId) {
|
|
90
|
+
if (isValidModel(modelId)) {
|
|
91
|
+
return { valid: true };
|
|
92
|
+
}
|
|
93
|
+
// Try to find similar models
|
|
94
|
+
const allIds = getAllModelIds();
|
|
95
|
+
const similar = allIds.find(id => id.toLowerCase().includes(modelId.toLowerCase()));
|
|
96
|
+
return {
|
|
97
|
+
valid: false,
|
|
98
|
+
error: `Invalid model ID: ${modelId}`,
|
|
99
|
+
suggested: similar
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
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.SafetyChecker = void 0;
|
|
7
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
8
|
+
const config_1 = require("./config");
|
|
9
|
+
/**
|
|
10
|
+
* Safety checker for tool operations
|
|
11
|
+
* Determines when to ask for user confirmation
|
|
12
|
+
*/
|
|
13
|
+
class SafetyChecker {
|
|
14
|
+
/**
|
|
15
|
+
* Check if a tool operation should require confirmation
|
|
16
|
+
*/
|
|
17
|
+
async shouldConfirm(tool, params) {
|
|
18
|
+
const prefs = (0, config_1.getPreferences)();
|
|
19
|
+
// If autoConfirm is true and safeMode is false, skip most confirmations
|
|
20
|
+
if (prefs.autoConfirm && !prefs.safeMode) {
|
|
21
|
+
// Still confirm for extremely dangerous operations
|
|
22
|
+
return this.isExtremelyDangerous(tool, params);
|
|
23
|
+
}
|
|
24
|
+
// Safe mode: always confirm destructive operations
|
|
25
|
+
if (prefs.safeMode && tool.destructive) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
// Regular mode: confirm based on tool requirements
|
|
29
|
+
return tool.requiresConfirmation;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Check if an operation is extremely dangerous (always confirm)
|
|
33
|
+
*/
|
|
34
|
+
isExtremelyDangerous(tool, params) {
|
|
35
|
+
// Git operations that can't be easily undone
|
|
36
|
+
if (tool.name === 'git_push' && params.force) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
if (tool.name === 'git_branch' && params.action === 'delete') {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
// Shell commands with dangerous patterns
|
|
43
|
+
if (tool.name === 'bash') {
|
|
44
|
+
const dangerous = [
|
|
45
|
+
/rm\s+-rf/,
|
|
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));
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Prompt user for confirmation
|
|
58
|
+
*/
|
|
59
|
+
async promptConfirmation(tool, params) {
|
|
60
|
+
const description = this.getOperationDescription(tool, params);
|
|
61
|
+
console.log('\n⚠️ Confirmation required:');
|
|
62
|
+
console.log(`Tool: ${tool.name}`);
|
|
63
|
+
console.log(`Operation: ${description}`);
|
|
64
|
+
console.log('');
|
|
65
|
+
const { proceed, remember } = await inquirer_1.default.prompt([
|
|
66
|
+
{
|
|
67
|
+
type: 'confirm',
|
|
68
|
+
name: 'proceed',
|
|
69
|
+
message: 'Do you want to proceed with this operation?',
|
|
70
|
+
default: false
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
type: 'confirm',
|
|
74
|
+
name: 'remember',
|
|
75
|
+
message: 'Remember this choice for similar operations in this session?',
|
|
76
|
+
default: false,
|
|
77
|
+
when: (answers) => answers.proceed
|
|
78
|
+
}
|
|
79
|
+
]);
|
|
80
|
+
return {
|
|
81
|
+
confirmed: proceed,
|
|
82
|
+
rememberChoice: remember || false
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get a human-readable description of the operation
|
|
87
|
+
*/
|
|
88
|
+
getOperationDescription(tool, params) {
|
|
89
|
+
switch (tool.name) {
|
|
90
|
+
case 'write_file':
|
|
91
|
+
return `Write to file: ${params.file_path}`;
|
|
92
|
+
case 'edit_file':
|
|
93
|
+
return `Edit file: ${params.file_path} (replace "${params.search.substring(0, 50)}...")`;
|
|
94
|
+
case 'bash':
|
|
95
|
+
return `Execute command: ${params.command}`;
|
|
96
|
+
case 'git_commit':
|
|
97
|
+
const files = params.files ? ` (${params.files.length} files)` : '';
|
|
98
|
+
return `Create git commit${files}: "${params.message}"`;
|
|
99
|
+
case 'git_push':
|
|
100
|
+
const force = params.force ? ' (FORCE)' : '';
|
|
101
|
+
return `Push to remote${force}: ${params.branch || 'current branch'}`;
|
|
102
|
+
case 'git_branch':
|
|
103
|
+
return `${params.action} branch: ${params.name || 'N/A'}`;
|
|
104
|
+
default:
|
|
105
|
+
return JSON.stringify(params, null, 2);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.SafetyChecker = SafetyChecker;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ToolRegistry = exports.Tool = void 0;
|
|
4
|
+
const tools_1 = require("@langchain/core/tools");
|
|
5
|
+
/**
|
|
6
|
+
* Base interface for all tools
|
|
7
|
+
*/
|
|
8
|
+
class Tool {
|
|
9
|
+
/**
|
|
10
|
+
* Get a short summary of what this tool does for the AI
|
|
11
|
+
*/
|
|
12
|
+
getSummary() {
|
|
13
|
+
return `${this.name}: ${this.description}`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Convert this tool to a LangChain tool for binding to models
|
|
17
|
+
*/
|
|
18
|
+
toLangChainTool() {
|
|
19
|
+
return new tools_1.DynamicStructuredTool({
|
|
20
|
+
name: this.name,
|
|
21
|
+
description: this.description,
|
|
22
|
+
schema: this.schema,
|
|
23
|
+
func: async (input) => {
|
|
24
|
+
const result = await this.execute(input);
|
|
25
|
+
if (!result.success) {
|
|
26
|
+
throw new Error(result.error || 'Tool execution failed');
|
|
27
|
+
}
|
|
28
|
+
return result.output;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Format a successful result
|
|
34
|
+
*/
|
|
35
|
+
success(output, metadata) {
|
|
36
|
+
return {
|
|
37
|
+
success: true,
|
|
38
|
+
output,
|
|
39
|
+
metadata
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Format an error result
|
|
44
|
+
*/
|
|
45
|
+
error(error, metadata) {
|
|
46
|
+
return {
|
|
47
|
+
success: false,
|
|
48
|
+
output: '',
|
|
49
|
+
error,
|
|
50
|
+
metadata
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.Tool = Tool;
|
|
55
|
+
/**
|
|
56
|
+
* Registry to manage all available tools
|
|
57
|
+
*/
|
|
58
|
+
class ToolRegistry {
|
|
59
|
+
constructor() {
|
|
60
|
+
this.tools = new Map();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Register a tool
|
|
64
|
+
*/
|
|
65
|
+
register(tool) {
|
|
66
|
+
this.tools.set(tool.name, tool);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get a tool by name
|
|
70
|
+
*/
|
|
71
|
+
get(name) {
|
|
72
|
+
return this.tools.get(name);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get all registered tools
|
|
76
|
+
*/
|
|
77
|
+
getAll() {
|
|
78
|
+
return Array.from(this.tools.values());
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get all tools as LangChain tools
|
|
82
|
+
*/
|
|
83
|
+
getAllAsLangChainTools() {
|
|
84
|
+
return this.getAll().map(tool => tool.toLangChainTool());
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get a description of all tools for the AI system prompt
|
|
88
|
+
*/
|
|
89
|
+
getToolDescriptions() {
|
|
90
|
+
const descriptions = this.getAll().map(tool => {
|
|
91
|
+
const params = Object.keys(tool.schema.shape).join(', ');
|
|
92
|
+
return `- ${tool.name}(${params}): ${tool.description}`;
|
|
93
|
+
});
|
|
94
|
+
return descriptions.join('\n');
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get list of tool names
|
|
98
|
+
*/
|
|
99
|
+
getToolNames() {
|
|
100
|
+
return Array.from(this.tools.keys());
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Check if a tool exists
|
|
104
|
+
*/
|
|
105
|
+
has(name) {
|
|
106
|
+
return this.tools.has(name);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get count of registered tools
|
|
110
|
+
*/
|
|
111
|
+
count() {
|
|
112
|
+
return this.tools.size;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
exports.ToolRegistry = ToolRegistry;
|
|
@@ -0,0 +1,127 @@
|
|
|
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.ToolExecutor = void 0;
|
|
7
|
+
const ui_1 = require("../ui");
|
|
8
|
+
const tracker_1 = require("../tracker");
|
|
9
|
+
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
/**
|
|
11
|
+
* Executes tools called by the AI
|
|
12
|
+
*/
|
|
13
|
+
class ToolExecutor {
|
|
14
|
+
constructor(registry, safetyChecker) {
|
|
15
|
+
this.sessionOverrides = new Map();
|
|
16
|
+
this.registry = registry;
|
|
17
|
+
this.safetyChecker = safetyChecker;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Execute a single tool call
|
|
21
|
+
*/
|
|
22
|
+
async executeSingle(toolCall) {
|
|
23
|
+
const tool = this.registry.get(toolCall.name);
|
|
24
|
+
const startTime = Date.now();
|
|
25
|
+
if (!tool) {
|
|
26
|
+
tracker_1.tracker.toolError(toolCall.name, 'Tool not found', 0);
|
|
27
|
+
return {
|
|
28
|
+
success: false,
|
|
29
|
+
output: '',
|
|
30
|
+
error: `Tool '${toolCall.name}' not found`
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// Log tool start
|
|
34
|
+
tracker_1.tracker.toolStart(tool.name, toolCall.args);
|
|
35
|
+
try {
|
|
36
|
+
// Check if confirmation is needed
|
|
37
|
+
const needsConfirm = await this.safetyChecker.shouldConfirm(tool, toolCall.args);
|
|
38
|
+
if (needsConfirm) {
|
|
39
|
+
// Check session override
|
|
40
|
+
const overrideKey = `${tool.name}:${JSON.stringify(toolCall.args)}`;
|
|
41
|
+
if (!this.sessionOverrides.get(overrideKey)) {
|
|
42
|
+
const { confirmed, rememberChoice } = await this.safetyChecker.promptConfirmation(tool, toolCall.args);
|
|
43
|
+
if (!confirmed) {
|
|
44
|
+
const duration = Date.now() - startTime;
|
|
45
|
+
tracker_1.tracker.toolCancelled(tool.name);
|
|
46
|
+
return {
|
|
47
|
+
success: false,
|
|
48
|
+
output: '',
|
|
49
|
+
error: 'Operation cancelled by user'
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (rememberChoice) {
|
|
53
|
+
this.sessionOverrides.set(overrideKey, true);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Execute the tool with spinner feedback
|
|
58
|
+
const spinner = (0, ora_1.default)(`Executing ${tool.name}...`).start();
|
|
59
|
+
ui_1.log.tool(tool.name, 'executing');
|
|
60
|
+
const result = await tool.execute(toolCall.args);
|
|
61
|
+
const duration = Date.now() - startTime;
|
|
62
|
+
if (result.success) {
|
|
63
|
+
spinner.succeed(`${tool.name} completed`);
|
|
64
|
+
ui_1.log.toolResult(true, result.output);
|
|
65
|
+
// Show formatted output with colors
|
|
66
|
+
if (result.output && result.output.trim()) {
|
|
67
|
+
ui_1.log.toolOutput(tool.name, result.output);
|
|
68
|
+
}
|
|
69
|
+
tracker_1.tracker.toolSuccess(tool.name, result.output, duration);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
spinner.fail(`${tool.name} failed`);
|
|
73
|
+
ui_1.log.toolResult(false, result.error || 'Unknown error');
|
|
74
|
+
// Log error with args for debugging
|
|
75
|
+
tracker_1.tracker.toolError(tool.name, result.error || 'Unknown error', duration, toolCall.args);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
const duration = Date.now() - startTime;
|
|
81
|
+
ui_1.log.error(`Tool execution error: ${error.message}`);
|
|
82
|
+
tracker_1.tracker.toolError(tool.name, error.message, duration, toolCall.args);
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
output: '',
|
|
86
|
+
error: error.message || 'Unknown error occurred'
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Execute multiple tool calls in sequence
|
|
92
|
+
*/
|
|
93
|
+
async executeMultiple(toolCalls) {
|
|
94
|
+
const results = [];
|
|
95
|
+
for (const toolCall of toolCalls) {
|
|
96
|
+
const result = await this.executeSingle(toolCall);
|
|
97
|
+
results.push(result);
|
|
98
|
+
// If a tool fails, we might want to continue or stop
|
|
99
|
+
// For now, we continue executing remaining tools
|
|
100
|
+
}
|
|
101
|
+
return results;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Format tool results as messages for the AI
|
|
105
|
+
*/
|
|
106
|
+
formatResultsAsMessages(toolCalls, results) {
|
|
107
|
+
return toolCalls.map((call, index) => {
|
|
108
|
+
const result = results[index];
|
|
109
|
+
const content = result.success
|
|
110
|
+
? result.output
|
|
111
|
+
: `Error: ${result.error}`;
|
|
112
|
+
return {
|
|
113
|
+
role: 'tool',
|
|
114
|
+
content,
|
|
115
|
+
tool_call_id: call.id,
|
|
116
|
+
name: call.name
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Clear session overrides (confirmations)
|
|
122
|
+
*/
|
|
123
|
+
clearSessionOverrides() {
|
|
124
|
+
this.sessionOverrides.clear();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
exports.ToolExecutor = ToolExecutor;
|