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.
@@ -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;