@sschepis/robodev 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/ai.mjs +8 -0
- package/package.json +48 -0
- package/src/cli/cli-interface.mjs +271 -0
- package/src/config.mjs +64 -0
- package/src/core/ai-assistant.mjs +540 -0
- package/src/core/ai-provider.mjs +579 -0
- package/src/core/history-manager.mjs +330 -0
- package/src/core/system-prompt.mjs +182 -0
- package/src/custom-tools/custom-tools-manager.mjs +310 -0
- package/src/execution/tool-executor.mjs +892 -0
- package/src/lib/README.md +114 -0
- package/src/lib/adapters/console-status-adapter.mjs +48 -0
- package/src/lib/adapters/network-llm-adapter.mjs +37 -0
- package/src/lib/index.mjs +101 -0
- package/src/lib/interfaces.d.ts +98 -0
- package/src/main.mjs +61 -0
- package/src/package/package-manager.mjs +223 -0
- package/src/quality/code-validator.mjs +126 -0
- package/src/quality/quality-evaluator.mjs +248 -0
- package/src/reasoning/reasoning-system.mjs +258 -0
- package/src/structured-dev/flow-manager.mjs +321 -0
- package/src/structured-dev/implementation-planner.mjs +223 -0
- package/src/structured-dev/manifest-manager.mjs +423 -0
- package/src/structured-dev/plan-executor.mjs +113 -0
- package/src/structured-dev/project-bootstrapper.mjs +523 -0
- package/src/tools/desktop-automation-tools.mjs +172 -0
- package/src/tools/file-tools.mjs +141 -0
- package/src/tools/tool-definitions.mjs +872 -0
- package/src/ui/console-styler.mjs +503 -0
- package/src/workspace/workspace-manager.mjs +215 -0
- package/themes.json +66 -0
package/ai.mjs
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sschepis/robodev",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Robodev: AI-powered development assistant",
|
|
6
|
+
"author": "sschepis",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/sschepis/robodev.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"ai",
|
|
14
|
+
"assistant",
|
|
15
|
+
"agent",
|
|
16
|
+
"automation",
|
|
17
|
+
"llm",
|
|
18
|
+
"dev-tool"
|
|
19
|
+
],
|
|
20
|
+
"bin": {
|
|
21
|
+
"robodev": "./ai.mjs"
|
|
22
|
+
},
|
|
23
|
+
"main": "src/lib/index.mjs",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": "./src/lib/index.mjs",
|
|
26
|
+
"./cli": "./ai.mjs",
|
|
27
|
+
"./lib": "./src/lib/index.mjs"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"start": "node ai.mjs"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@google/genai": "^1.41.0",
|
|
34
|
+
"@nut-tree-fork/nut-js": "^4.2.6",
|
|
35
|
+
"boxen": "^8.0.1",
|
|
36
|
+
"chalk": "^5.5.0",
|
|
37
|
+
"cli-spinners": "^3.2.0",
|
|
38
|
+
"dotenv": "^17.3.1",
|
|
39
|
+
"figures": "^6.1.0",
|
|
40
|
+
"gradient-string": "^3.0.0",
|
|
41
|
+
"mathjs": "^14.6.0",
|
|
42
|
+
"ora": "^8.2.0",
|
|
43
|
+
"vm2": "^3.10.4"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18.0.0"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
// CLI interface logic
|
|
2
|
+
// Handles command-line interaction and user input processing
|
|
3
|
+
|
|
4
|
+
import readline from 'readline';
|
|
5
|
+
import { consoleStyler } from '../ui/console-styler.mjs';
|
|
6
|
+
|
|
7
|
+
export class CLIInterface {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.rl = null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Start interactive mode with simple approach
|
|
13
|
+
// Returns a Promise that resolves only when the user types "exit"
|
|
14
|
+
async startInteractiveMode(assistant, workingDir) {
|
|
15
|
+
consoleStyler.log('system', 'AI Assistant (Interactive Mode). Type "exit" to quit.');
|
|
16
|
+
consoleStyler.log('system', `Working Directory: ${workingDir}`);
|
|
17
|
+
|
|
18
|
+
// Create readline with minimal config
|
|
19
|
+
this.rl = readline.createInterface({
|
|
20
|
+
input: process.stdin,
|
|
21
|
+
output: process.stdout
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Return a promise that stays pending until exit
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
// Simple recursive prompt
|
|
27
|
+
const prompt = () => {
|
|
28
|
+
this.rl.question('👤 [YOU] ', async (userInput) => {
|
|
29
|
+
if (!userInput || userInput.trim() === '') {
|
|
30
|
+
prompt();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (userInput.toLowerCase() === 'exit') {
|
|
35
|
+
consoleStyler.log('system', 'Goodbye!', { box: true });
|
|
36
|
+
// Save session on exit
|
|
37
|
+
await assistant.saveSession('.ai-session');
|
|
38
|
+
resolve(); // Resolve the promise so main() can clean up
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Handle backslash commands for history deletion
|
|
43
|
+
if (userInput.match(/^\\+$/)) {
|
|
44
|
+
const backslashCount = userInput.length;
|
|
45
|
+
const deletedCount = assistant.deleteHistoryExchanges(backslashCount);
|
|
46
|
+
consoleStyler.log('history', `Deleted ${deletedCount} exchange(s) from history.`);
|
|
47
|
+
prompt();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
// Using streaming if available
|
|
53
|
+
if (assistant.runStream) {
|
|
54
|
+
consoleStyler.log('working', 'Thinking...');
|
|
55
|
+
// No spinner for streaming to avoid clearing/redrawing issues
|
|
56
|
+
await assistant.runStream(userInput, (chunk) => {
|
|
57
|
+
process.stdout.write(chunk);
|
|
58
|
+
});
|
|
59
|
+
console.log('\n'); // Newline after stream
|
|
60
|
+
} else {
|
|
61
|
+
const spinner = consoleStyler.startSpinner('processing', 'Processing your request...');
|
|
62
|
+
const response = await assistant.run(userInput);
|
|
63
|
+
consoleStyler.succeedSpinner('processing', 'Request processed');
|
|
64
|
+
consoleStyler.log('ai', response);
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
consoleStyler.failSpinner('processing', 'Request failed');
|
|
68
|
+
consoleStyler.log('error', error.message);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
prompt(); // Continue prompting
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Handle stdin close (e.g., piped input, Ctrl+D)
|
|
76
|
+
this.rl.on('close', () => {
|
|
77
|
+
resolve();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Start prompting
|
|
81
|
+
prompt();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Run single-shot mode
|
|
86
|
+
async runSingleShot(assistant, userInput, workingDir) {
|
|
87
|
+
consoleStyler.log('user', userInput);
|
|
88
|
+
consoleStyler.log('system', `Working Directory: ${workingDir}`);
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const spinner = consoleStyler.startSpinner('processing', 'Processing your request...');
|
|
92
|
+
const response = await assistant.run(userInput);
|
|
93
|
+
consoleStyler.succeedSpinner('processing', 'Request completed');
|
|
94
|
+
consoleStyler.log('ai', response);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
consoleStyler.failSpinner('processing', 'Request failed');
|
|
97
|
+
const errorDisplay = consoleStyler.formatError(error, {
|
|
98
|
+
suggestions: [
|
|
99
|
+
'Check your AI provider is configured correctly (see .env.example)',
|
|
100
|
+
'For local: ensure LMStudio/Ollama is running on the configured port',
|
|
101
|
+
'For cloud: verify your API key (OPENAI_API_KEY or GOOGLE_API_KEY)',
|
|
102
|
+
'Try restarting the application'
|
|
103
|
+
]
|
|
104
|
+
});
|
|
105
|
+
console.log(errorDisplay);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Force exit after a short delay to ensure all output is flushed
|
|
110
|
+
setTimeout(() => {
|
|
111
|
+
process.exit(0);
|
|
112
|
+
}, 100);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Parse command line arguments
|
|
116
|
+
parseArguments() {
|
|
117
|
+
const args = process.argv.slice(2);
|
|
118
|
+
const workingDir = process.cwd();
|
|
119
|
+
|
|
120
|
+
// Check for flags
|
|
121
|
+
const resumeIndex = args.indexOf('--resume');
|
|
122
|
+
const resume = resumeIndex !== -1;
|
|
123
|
+
|
|
124
|
+
// Remove flags from args
|
|
125
|
+
if (resume) {
|
|
126
|
+
args.splice(resumeIndex, 1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
args,
|
|
131
|
+
workingDir,
|
|
132
|
+
resume,
|
|
133
|
+
isInteractive: args.length === 0,
|
|
134
|
+
userInput: args.length > 0 ? args.join(' ') : null
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Display startup information
|
|
139
|
+
displayStartupInfo(workingDir) {
|
|
140
|
+
consoleStyler.displayStartupBanner(workingDir);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Display help information
|
|
144
|
+
displayHelp() {
|
|
145
|
+
const theme = consoleStyler.getTheme();
|
|
146
|
+
const helpContent = `
|
|
147
|
+
${consoleStyler.icons.ai} AI Assistant Help:
|
|
148
|
+
|
|
149
|
+
${theme.primary('USAGE:')}
|
|
150
|
+
Interactive mode: node ai.mjs
|
|
151
|
+
Single-shot mode: node ai.mjs "your question or command"
|
|
152
|
+
|
|
153
|
+
${theme.warning('SPECIAL COMMANDS (Interactive mode only):')}
|
|
154
|
+
\\ - Delete the last exchange from conversation history
|
|
155
|
+
\\\\ - Delete the last 2 exchanges from conversation history
|
|
156
|
+
\\\\\\ - Delete the last 3 exchanges from conversation history
|
|
157
|
+
exit - Exit the program
|
|
158
|
+
|
|
159
|
+
${theme.info('EXAMPLES:')}
|
|
160
|
+
node ai.mjs "Create a simple calculator function"
|
|
161
|
+
node ai.mjs "Use axios to fetch data from jsonplaceholder.typicode.com"
|
|
162
|
+
node ai.mjs "Install and use uuid to generate 5 random IDs"
|
|
163
|
+
|
|
164
|
+
${theme.success('FEATURES:')}
|
|
165
|
+
${consoleStyler.icons.check} Execute JavaScript code with automatic npm package installation
|
|
166
|
+
${consoleStyler.icons.check} Create and manage custom reusable tools
|
|
167
|
+
${consoleStyler.icons.check} Multi-step task management with todo lists
|
|
168
|
+
${consoleStyler.icons.check} Error recovery and retry mechanisms
|
|
169
|
+
${consoleStyler.icons.check} Quality evaluation and response improvement
|
|
170
|
+
${consoleStyler.icons.check} Workspace management for complex tasks
|
|
171
|
+
${consoleStyler.icons.check} Text-to-speech support (with ElevenLabs API key)
|
|
172
|
+
${consoleStyler.icons.check} Enhanced console styling with themes
|
|
173
|
+
|
|
174
|
+
${theme.system('REQUIREMENTS:')}
|
|
175
|
+
${consoleStyler.icons.bullet} Node.js v18+ (for native fetch support)
|
|
176
|
+
${consoleStyler.icons.bullet} One of: LMStudio/Ollama (local), OpenAI API key, or Google Gemini API key
|
|
177
|
+
${consoleStyler.icons.bullet} Set AI_MODEL in .env (e.g., gpt-4o, gemini-2.0-flash, or local model name)
|
|
178
|
+
${consoleStyler.icons.bullet} Optional: ElevenLabs API key for text-to-speech
|
|
179
|
+
|
|
180
|
+
${theme.accent('THEMES:')}
|
|
181
|
+
Available: ${consoleStyler.getAvailableThemes().join(', ')}
|
|
182
|
+
Current: ${consoleStyler.currentTheme}
|
|
183
|
+
`;
|
|
184
|
+
|
|
185
|
+
console.log(helpContent);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Display error information
|
|
189
|
+
displayError(error) {
|
|
190
|
+
const suggestions = [];
|
|
191
|
+
|
|
192
|
+
if (error.code === 'ECONNREFUSED') {
|
|
193
|
+
suggestions.push(
|
|
194
|
+
'For local: start LMStudio/Ollama and load a model with function calling support',
|
|
195
|
+
'For cloud: set AI_MODEL and API key in .env (e.g., gemini-2.0-flash + GOOGLE_API_KEY)',
|
|
196
|
+
'Check your network connection'
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const errorDisplay = consoleStyler.formatError(error, { suggestions });
|
|
201
|
+
console.log(errorDisplay);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Handle process signals for graceful shutdown
|
|
205
|
+
setupSignalHandlers() {
|
|
206
|
+
process.on('SIGINT', () => {
|
|
207
|
+
consoleStyler.clearAllSpinners();
|
|
208
|
+
consoleStyler.log('system', 'Received SIGINT, shutting down gracefully...', { box: true });
|
|
209
|
+
if (this.rl) {
|
|
210
|
+
this.rl.close();
|
|
211
|
+
}
|
|
212
|
+
process.exit(0);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
process.on('SIGTERM', () => {
|
|
216
|
+
consoleStyler.clearAllSpinners();
|
|
217
|
+
consoleStyler.log('system', 'Received SIGTERM, shutting down gracefully...', { box: true });
|
|
218
|
+
if (this.rl) {
|
|
219
|
+
this.rl.close();
|
|
220
|
+
}
|
|
221
|
+
process.exit(0);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
process.on('uncaughtException', (error) => {
|
|
225
|
+
consoleStyler.clearAllSpinners();
|
|
226
|
+
consoleStyler.log('error', `UNCAUGHT EXCEPTION: ${error.message}`);
|
|
227
|
+
this.displayError(error);
|
|
228
|
+
process.exit(1);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
232
|
+
consoleStyler.clearAllSpinners();
|
|
233
|
+
consoleStyler.log('error', `UNHANDLED REJECTION: ${reason}`);
|
|
234
|
+
console.error('At promise:', promise);
|
|
235
|
+
process.exit(1);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Close the CLI interface
|
|
240
|
+
close() {
|
|
241
|
+
if (this.rl) {
|
|
242
|
+
this.rl.close();
|
|
243
|
+
this.rl = null;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Display a message with color coding
|
|
248
|
+
displayMessage(message, type = 'info') {
|
|
249
|
+
consoleStyler.log(type, message);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Display progress indicator
|
|
253
|
+
displayProgress(message) {
|
|
254
|
+
consoleStyler.startSpinner('progress', message);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Clear progress indicator
|
|
258
|
+
clearProgress() {
|
|
259
|
+
consoleStyler.succeedSpinner('progress', 'Done');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Set console theme
|
|
263
|
+
setTheme(themeName) {
|
|
264
|
+
return consoleStyler.setTheme(themeName);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Get available themes
|
|
268
|
+
getAvailableThemes() {
|
|
269
|
+
return consoleStyler.getAvailableThemes();
|
|
270
|
+
}
|
|
271
|
+
}
|
package/src/config.mjs
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
// Load environment variables
|
|
6
|
+
dotenv.config();
|
|
7
|
+
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Centralized configuration for the AI Assistant
|
|
12
|
+
*/
|
|
13
|
+
export const config = {
|
|
14
|
+
// AI Configuration
|
|
15
|
+
ai: {
|
|
16
|
+
model: process.env.AI_MODEL || 'gpt-4o', // Default model
|
|
17
|
+
provider: process.env.AI_PROVIDER || '', // Auto-detected from model name if empty. Options: 'local', 'openai', 'gemini'
|
|
18
|
+
endpoint: process.env.AI_ENDPOINT || 'http://localhost:1234/v1/chat/completions',
|
|
19
|
+
temperature: parseFloat(process.env.AI_TEMPERATURE || '0.7'),
|
|
20
|
+
maxTokens: parseInt(process.env.AI_MAX_TOKENS || '4096', 10),
|
|
21
|
+
contextWindowSize: parseInt(process.env.AI_CONTEXT_WINDOW || '128000', 10),
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
// System Configuration
|
|
25
|
+
system: {
|
|
26
|
+
environment: process.env.NODE_ENV || 'development',
|
|
27
|
+
logLevel: process.env.LOG_LEVEL || 'info',
|
|
28
|
+
workspaceRoot: process.env.WORKSPACE_ROOT || path.resolve(__dirname, '..', '..'),
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
// Tool Configuration
|
|
32
|
+
tools: {
|
|
33
|
+
enableUnsafeTools: process.env.ENABLE_UNSAFE_TOOLS === 'true',
|
|
34
|
+
allowedFileExtensions: (process.env.ALLOWED_FILE_EXTENSIONS || '.js,.mjs,.json,.md,.txt').split(','),
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
// API Keys (accessed safely)
|
|
38
|
+
keys: {
|
|
39
|
+
openai: process.env.OPENAI_API_KEY,
|
|
40
|
+
anthropic: process.env.ANTHROPIC_API_KEY,
|
|
41
|
+
google: process.env.GOOGLE_API_KEY,
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Validate required configuration
|
|
47
|
+
*/
|
|
48
|
+
export function validateConfig() {
|
|
49
|
+
const missingKeys = [];
|
|
50
|
+
|
|
51
|
+
// Example validation - uncomment if strict validation is needed
|
|
52
|
+
// if (!config.keys.openai && !config.keys.anthropic) {
|
|
53
|
+
// missingKeys.push('OPENAI_API_KEY or ANTHROPIC_API_KEY');
|
|
54
|
+
// }
|
|
55
|
+
|
|
56
|
+
if (missingKeys.length > 0) {
|
|
57
|
+
console.warn(`Warning: Missing configuration for: ${missingKeys.join(', ')}`);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default config;
|