@hanzo/dev 1.2.0 ā 2.1.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/.eslintrc.json +24 -0
- package/README.md +359 -0
- package/dist/cli/dev.js +21724 -602
- package/package.json +19 -4
- package/src/cli/dev.ts +623 -106
- package/src/lib/agent-loop.ts +552 -0
- package/src/lib/benchmark-runner.ts +431 -0
- package/src/lib/code-act-agent.ts +378 -0
- package/src/lib/config.ts +163 -0
- package/src/lib/editor.ts +395 -0
- package/src/lib/function-calling.ts +318 -0
- package/src/lib/mcp-client.ts +259 -0
- package/src/lib/peer-agent-network.ts +584 -0
- package/src/lib/swarm-runner.ts +379 -0
- package/src/lib/unified-workspace.ts +435 -0
- package/test-swarm/file1.js +6 -0
- package/test-swarm/file2.ts +12 -0
- package/test-swarm/file3.py +15 -0
- package/test-swarm/file4.md +13 -0
- package/test-swarm/file5.json +12 -0
- package/test-swarm-demo.sh +22 -0
- package/tests/browser-integration.test.ts +242 -0
- package/tests/code-act-agent.test.ts +305 -0
- package/tests/editor.test.ts +223 -0
- package/tests/fixtures/sample-code.js +13 -0
- package/tests/fixtures/sample-code.py +28 -0
- package/tests/fixtures/sample-code.ts +22 -0
- package/tests/mcp-client.test.ts +238 -0
- package/tests/peer-agent-network.test.ts +340 -0
- package/tests/swarm-runner.test.ts +301 -0
- package/tests/swe-bench.test.ts +357 -0
- package/tsconfig.json +13 -15
- package/vitest.config.ts +37 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import { FileEditor, ChunkLocalizer } from './editor';
|
|
2
|
+
import { FunctionCallingSystem, FunctionCall } from './function-calling';
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
|
|
8
|
+
export interface AgentTask {
|
|
9
|
+
id: string;
|
|
10
|
+
description: string;
|
|
11
|
+
status: 'pending' | 'running' | 'completed' | 'failed';
|
|
12
|
+
result?: any;
|
|
13
|
+
error?: string;
|
|
14
|
+
retries: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CodeActPlan {
|
|
18
|
+
steps: string[];
|
|
19
|
+
parallelizable: boolean[];
|
|
20
|
+
currentStep: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class CodeActAgent {
|
|
24
|
+
private editor: FileEditor;
|
|
25
|
+
private functionCalling: FunctionCallingSystem;
|
|
26
|
+
private tasks: Map<string, AgentTask> = new Map();
|
|
27
|
+
private maxRetries: number = 3;
|
|
28
|
+
private parallelExecutor: ParallelExecutor;
|
|
29
|
+
|
|
30
|
+
constructor() {
|
|
31
|
+
this.editor = new FileEditor();
|
|
32
|
+
this.functionCalling = new FunctionCallingSystem();
|
|
33
|
+
this.parallelExecutor = new ParallelExecutor();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Plan and execute a complex task
|
|
37
|
+
async executeTask(description: string): Promise<void> {
|
|
38
|
+
console.log(chalk.cyan(`\nš¤ CodeAct Agent: ${description}\n`));
|
|
39
|
+
|
|
40
|
+
// Generate plan
|
|
41
|
+
const plan = await this.generatePlan(description);
|
|
42
|
+
console.log(chalk.yellow('š Execution Plan:'));
|
|
43
|
+
plan.steps.forEach((step, i) => {
|
|
44
|
+
const parallel = plan.parallelizable[i] ? ' [parallel]' : '';
|
|
45
|
+
console.log(` ${i + 1}. ${step}${parallel}`);
|
|
46
|
+
});
|
|
47
|
+
console.log();
|
|
48
|
+
|
|
49
|
+
// Execute plan
|
|
50
|
+
await this.executePlan(plan);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private async generatePlan(description: string): Promise<CodeActPlan> {
|
|
54
|
+
// In a real implementation, this would use an LLM to generate the plan
|
|
55
|
+
// For now, we'll use pattern matching
|
|
56
|
+
const steps: string[] = [];
|
|
57
|
+
const parallelizable: boolean[] = [];
|
|
58
|
+
|
|
59
|
+
if (description.includes('refactor')) {
|
|
60
|
+
steps.push('Analyze current code structure');
|
|
61
|
+
parallelizable.push(false);
|
|
62
|
+
steps.push('Identify refactoring opportunities');
|
|
63
|
+
parallelizable.push(false);
|
|
64
|
+
steps.push('Apply refactoring changes');
|
|
65
|
+
parallelizable.push(true);
|
|
66
|
+
steps.push('Run tests');
|
|
67
|
+
parallelizable.push(false);
|
|
68
|
+
steps.push('Fix any issues');
|
|
69
|
+
parallelizable.push(false);
|
|
70
|
+
} else if (description.includes('test')) {
|
|
71
|
+
steps.push('Discover test files');
|
|
72
|
+
parallelizable.push(true);
|
|
73
|
+
steps.push('Run tests');
|
|
74
|
+
parallelizable.push(true);
|
|
75
|
+
steps.push('Analyze failures');
|
|
76
|
+
parallelizable.push(false);
|
|
77
|
+
steps.push('Fix failing tests');
|
|
78
|
+
parallelizable.push(true);
|
|
79
|
+
steps.push('Re-run tests');
|
|
80
|
+
parallelizable.push(false);
|
|
81
|
+
} else {
|
|
82
|
+
// Default plan
|
|
83
|
+
steps.push('Analyze requirements');
|
|
84
|
+
parallelizable.push(false);
|
|
85
|
+
steps.push('Implement changes');
|
|
86
|
+
parallelizable.push(false);
|
|
87
|
+
steps.push('Validate changes');
|
|
88
|
+
parallelizable.push(false);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { steps, parallelizable, currentStep: 0 };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private async executePlan(plan: CodeActPlan): Promise<void> {
|
|
95
|
+
for (let i = 0; i < plan.steps.length; i++) {
|
|
96
|
+
plan.currentStep = i;
|
|
97
|
+
const step = plan.steps[i];
|
|
98
|
+
|
|
99
|
+
console.log(chalk.blue(`\nā¶ Step ${i + 1}: ${step}`));
|
|
100
|
+
|
|
101
|
+
if (plan.parallelizable[i] && i + 1 < plan.steps.length && plan.parallelizable[i + 1]) {
|
|
102
|
+
// Collect parallel tasks
|
|
103
|
+
const parallelTasks: string[] = [step];
|
|
104
|
+
while (i + 1 < plan.steps.length && plan.parallelizable[i + 1]) {
|
|
105
|
+
i++;
|
|
106
|
+
parallelTasks.push(plan.steps[i]);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Execute in parallel
|
|
110
|
+
await this.executeParallelSteps(parallelTasks);
|
|
111
|
+
} else {
|
|
112
|
+
// Execute single step
|
|
113
|
+
await this.executeStep(step);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log(chalk.green('\nā
Task completed successfully!\n'));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private async executeStep(step: string): Promise<void> {
|
|
121
|
+
const taskId = this.generateTaskId();
|
|
122
|
+
const task: AgentTask = {
|
|
123
|
+
id: taskId,
|
|
124
|
+
description: step,
|
|
125
|
+
status: 'running',
|
|
126
|
+
retries: 0
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
this.tasks.set(taskId, task);
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
// Execute with retry logic
|
|
133
|
+
await this.executeWithRetry(task);
|
|
134
|
+
task.status = 'completed';
|
|
135
|
+
console.log(chalk.green(` ā ${step}`));
|
|
136
|
+
} catch (error) {
|
|
137
|
+
task.status = 'failed';
|
|
138
|
+
task.error = error instanceof Error ? error.message : String(error);
|
|
139
|
+
console.log(chalk.red(` ā ${step}: ${task.error}`));
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private async executeParallelSteps(steps: string[]): Promise<void> {
|
|
145
|
+
console.log(chalk.yellow(`\nā” Executing ${steps.length} steps in parallel...`));
|
|
146
|
+
|
|
147
|
+
const tasks = steps.map(step => {
|
|
148
|
+
const taskId = this.generateTaskId();
|
|
149
|
+
const task: AgentTask = {
|
|
150
|
+
id: taskId,
|
|
151
|
+
description: step,
|
|
152
|
+
status: 'pending',
|
|
153
|
+
retries: 0
|
|
154
|
+
};
|
|
155
|
+
this.tasks.set(taskId, task);
|
|
156
|
+
return task;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const results = await this.parallelExecutor.executeTasks(tasks, async (task) => {
|
|
160
|
+
task.status = 'running';
|
|
161
|
+
await this.executeWithRetry(task);
|
|
162
|
+
task.status = 'completed';
|
|
163
|
+
console.log(chalk.green(` ā ${task.description}`));
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Check for failures
|
|
167
|
+
const failures = results.filter(r => r.status === 'failed');
|
|
168
|
+
if (failures.length > 0) {
|
|
169
|
+
throw new Error(`${failures.length} parallel tasks failed`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private async executeWithRetry(task: AgentTask): Promise<void> {
|
|
174
|
+
while (task.retries < this.maxRetries) {
|
|
175
|
+
try {
|
|
176
|
+
// Map step description to actual actions
|
|
177
|
+
await this.mapStepToActions(task.description);
|
|
178
|
+
return;
|
|
179
|
+
} catch (error) {
|
|
180
|
+
task.retries++;
|
|
181
|
+
if (task.retries >= this.maxRetries) {
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
console.log(chalk.yellow(` ā Retry ${task.retries}/${this.maxRetries}: ${error}`));
|
|
186
|
+
|
|
187
|
+
// Attempt to fix the error
|
|
188
|
+
await this.attemptErrorCorrection(error);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private async mapStepToActions(step: string): Promise<void> {
|
|
194
|
+
// This would use LLM in real implementation
|
|
195
|
+
// For now, use pattern matching
|
|
196
|
+
|
|
197
|
+
if (step.includes('test')) {
|
|
198
|
+
await this.runTests();
|
|
199
|
+
} else if (step.includes('analyze') || step.includes('identify')) {
|
|
200
|
+
await this.analyzeCode();
|
|
201
|
+
} else if (step.includes('refactor') || step.includes('implement')) {
|
|
202
|
+
await this.implementChanges();
|
|
203
|
+
} else if (step.includes('fix')) {
|
|
204
|
+
await this.fixIssues();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Simulate some work
|
|
208
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private async runTests(): Promise<void> {
|
|
212
|
+
const result = await this.functionCalling.callFunction({
|
|
213
|
+
id: Date.now().toString(),
|
|
214
|
+
name: 'run_command',
|
|
215
|
+
arguments: { command: 'npm test' }
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
if (!result.result?.success) {
|
|
219
|
+
throw new Error('Tests failed');
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private async analyzeCode(): Promise<void> {
|
|
224
|
+
// Use search and analysis tools
|
|
225
|
+
const result = await this.functionCalling.callFunction({
|
|
226
|
+
id: Date.now().toString(),
|
|
227
|
+
name: 'search_files',
|
|
228
|
+
arguments: { pattern: '*.ts', path: '.' }
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Analyze results...
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private async implementChanges(): Promise<void> {
|
|
235
|
+
// Simulate making changes
|
|
236
|
+
console.log(chalk.gray(' Making code changes...'));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private async fixIssues(): Promise<void> {
|
|
240
|
+
// Simulate fixing issues
|
|
241
|
+
console.log(chalk.gray(' Applying fixes...'));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private async attemptErrorCorrection(error: any): Promise<void> {
|
|
245
|
+
console.log(chalk.yellow(' š§ Attempting automatic error correction...'));
|
|
246
|
+
|
|
247
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
248
|
+
|
|
249
|
+
// Pattern-based error correction
|
|
250
|
+
if (errorMessage.includes('Tests failed')) {
|
|
251
|
+
console.log(chalk.gray(' Analyzing test failures...'));
|
|
252
|
+
// Would use LLM to analyze and fix test failures
|
|
253
|
+
} else if (errorMessage.includes('compile')) {
|
|
254
|
+
console.log(chalk.gray(' Fixing compilation errors...'));
|
|
255
|
+
// Would use LLM to fix compilation errors
|
|
256
|
+
} else if (errorMessage.includes('lint')) {
|
|
257
|
+
console.log(chalk.gray(' Fixing linting errors...'));
|
|
258
|
+
await this.functionCalling.callFunction({
|
|
259
|
+
id: Date.now().toString(),
|
|
260
|
+
name: 'run_command',
|
|
261
|
+
arguments: { command: 'npm run lint -- --fix' }
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private generateTaskId(): string {
|
|
267
|
+
return `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Parallel task executor
|
|
272
|
+
class ParallelExecutor {
|
|
273
|
+
async executeTasks<T extends AgentTask>(
|
|
274
|
+
tasks: T[],
|
|
275
|
+
executor: (task: T) => Promise<void>
|
|
276
|
+
): Promise<T[]> {
|
|
277
|
+
const promises = tasks.map(task =>
|
|
278
|
+
executor(task).catch(error => {
|
|
279
|
+
task.status = 'failed';
|
|
280
|
+
task.error = error instanceof Error ? error.message : String(error);
|
|
281
|
+
})
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
await Promise.all(promises);
|
|
285
|
+
return tasks;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Advanced file editing with AST understanding
|
|
290
|
+
export class SmartFileEditor extends FileEditor {
|
|
291
|
+
private lspClient?: LSPClient;
|
|
292
|
+
|
|
293
|
+
async editWithUnderstanding(
|
|
294
|
+
filePath: string,
|
|
295
|
+
intent: string
|
|
296
|
+
): Promise<void> {
|
|
297
|
+
console.log(chalk.cyan(`\nš§ Smart Edit: ${intent}\n`));
|
|
298
|
+
|
|
299
|
+
// Read file
|
|
300
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
301
|
+
|
|
302
|
+
// Find relevant chunk
|
|
303
|
+
const chunk = ChunkLocalizer.findRelevantChunk(content, intent);
|
|
304
|
+
if (!chunk) {
|
|
305
|
+
throw new Error('Could not locate relevant code section');
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
console.log(chalk.gray(`Found relevant code at lines ${chunk.startLine}-${chunk.endLine}`));
|
|
309
|
+
|
|
310
|
+
// In real implementation, would use LLM to generate the edit
|
|
311
|
+
// For now, just show what would happen
|
|
312
|
+
console.log(chalk.yellow('Would apply intelligent edit based on intent'));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async applyBulkRefactoring(
|
|
316
|
+
pattern: string,
|
|
317
|
+
transformation: string
|
|
318
|
+
): Promise<void> {
|
|
319
|
+
console.log(chalk.cyan(`\nš Bulk Refactoring: ${pattern} ā ${transformation}\n`));
|
|
320
|
+
|
|
321
|
+
// Find all files matching pattern
|
|
322
|
+
const files = await this.findFilesWithPattern(pattern);
|
|
323
|
+
console.log(chalk.gray(`Found ${files.length} files to refactor`));
|
|
324
|
+
|
|
325
|
+
// Apply transformation to each file
|
|
326
|
+
for (const file of files) {
|
|
327
|
+
console.log(chalk.gray(` Refactoring ${file}...`));
|
|
328
|
+
// Would apply transformation
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
private async findFilesWithPattern(pattern: string): Promise<string[]> {
|
|
333
|
+
// Simplified implementation
|
|
334
|
+
const results: string[] = [];
|
|
335
|
+
|
|
336
|
+
const walkDir = (dir: string) => {
|
|
337
|
+
const files = fs.readdirSync(dir);
|
|
338
|
+
for (const file of files) {
|
|
339
|
+
const fullPath = path.join(dir, file);
|
|
340
|
+
const stats = fs.statSync(fullPath);
|
|
341
|
+
|
|
342
|
+
if (stats.isDirectory() && !file.startsWith('.') && file !== 'node_modules') {
|
|
343
|
+
walkDir(fullPath);
|
|
344
|
+
} else if (stats.isFile() && fullPath.endsWith('.ts')) {
|
|
345
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
346
|
+
if (content.includes(pattern)) {
|
|
347
|
+
results.push(fullPath);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
walkDir(process.cwd());
|
|
354
|
+
return results;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Placeholder for LSP client (would integrate with real LSP)
|
|
359
|
+
class LSPClient {
|
|
360
|
+
async initialize(rootPath: string): Promise<void> {
|
|
361
|
+
console.log(chalk.gray(`Initializing LSP for ${rootPath}`));
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async getDefinition(file: string, position: number): Promise<any> {
|
|
365
|
+
// Would return actual definition location
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async getReferences(file: string, position: number): Promise<any[]> {
|
|
370
|
+
// Would return all references
|
|
371
|
+
return [];
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async rename(file: string, position: number, newName: string): Promise<any> {
|
|
375
|
+
// Would perform project-wide rename
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import * as toml from '@iarna/toml';
|
|
5
|
+
|
|
6
|
+
export interface LLMConfig {
|
|
7
|
+
model: string;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
temperature?: number;
|
|
11
|
+
maxTokens?: number;
|
|
12
|
+
provider?: 'openai' | 'anthropic' | 'google' | 'azure' | 'local';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface AgentConfig {
|
|
16
|
+
name: string;
|
|
17
|
+
llmConfig?: LLMConfig;
|
|
18
|
+
memoryEnabled?: boolean;
|
|
19
|
+
microagentsEnabled?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface SecurityConfig {
|
|
23
|
+
confirmationMode: boolean;
|
|
24
|
+
sandboxMode?: boolean;
|
|
25
|
+
allowedCommands?: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SandboxConfig {
|
|
29
|
+
workspaceBase: string;
|
|
30
|
+
selectedRepo?: string;
|
|
31
|
+
useHost?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface HanzoDevConfig {
|
|
35
|
+
// Core settings
|
|
36
|
+
defaultAgent: string;
|
|
37
|
+
agents: AgentConfig[];
|
|
38
|
+
llm: LLMConfig;
|
|
39
|
+
security: SecurityConfig;
|
|
40
|
+
sandbox: SandboxConfig;
|
|
41
|
+
|
|
42
|
+
// CLI specific
|
|
43
|
+
cliMultilineInput?: boolean;
|
|
44
|
+
runtime?: 'cli' | 'docker' | 'local';
|
|
45
|
+
|
|
46
|
+
// Session
|
|
47
|
+
sessionName?: string;
|
|
48
|
+
resumeSession?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class ConfigManager {
|
|
52
|
+
private configPath: string;
|
|
53
|
+
private config: HanzoDevConfig;
|
|
54
|
+
|
|
55
|
+
constructor() {
|
|
56
|
+
this.configPath = this.getConfigPath();
|
|
57
|
+
this.config = this.loadConfig();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private getConfigPath(): string {
|
|
61
|
+
// Check for config in order of precedence
|
|
62
|
+
const locations = [
|
|
63
|
+
path.join(process.cwd(), 'hanzo-dev.toml'),
|
|
64
|
+
path.join(process.cwd(), '.hanzo-dev', 'config.toml'),
|
|
65
|
+
path.join(os.homedir(), '.config', 'hanzo-dev', 'config.toml'),
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
for (const loc of locations) {
|
|
69
|
+
if (fs.existsSync(loc)) {
|
|
70
|
+
return loc;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Default location
|
|
75
|
+
return path.join(os.homedir(), '.config', 'hanzo-dev', 'config.toml');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private loadConfig(): HanzoDevConfig {
|
|
79
|
+
// Default configuration
|
|
80
|
+
const defaultConfig: HanzoDevConfig = {
|
|
81
|
+
defaultAgent: 'CodeActAgent',
|
|
82
|
+
agents: [{
|
|
83
|
+
name: 'CodeActAgent',
|
|
84
|
+
memoryEnabled: true,
|
|
85
|
+
microagentsEnabled: true,
|
|
86
|
+
}],
|
|
87
|
+
llm: {
|
|
88
|
+
model: 'gpt-4',
|
|
89
|
+
provider: 'openai',
|
|
90
|
+
temperature: 0.7,
|
|
91
|
+
},
|
|
92
|
+
security: {
|
|
93
|
+
confirmationMode: true,
|
|
94
|
+
sandboxMode: true,
|
|
95
|
+
},
|
|
96
|
+
sandbox: {
|
|
97
|
+
workspaceBase: process.cwd(),
|
|
98
|
+
useHost: false,
|
|
99
|
+
},
|
|
100
|
+
runtime: 'cli',
|
|
101
|
+
cliMultilineInput: false,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// Load from file if exists
|
|
105
|
+
if (fs.existsSync(this.configPath)) {
|
|
106
|
+
try {
|
|
107
|
+
const content = fs.readFileSync(this.configPath, 'utf-8');
|
|
108
|
+
const parsed = toml.parse(content) as Partial<HanzoDevConfig>;
|
|
109
|
+
return { ...defaultConfig, ...parsed };
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error('Error loading config:', error);
|
|
112
|
+
return defaultConfig;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return defaultConfig;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public getConfig(): HanzoDevConfig {
|
|
120
|
+
return this.config;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public updateConfig(updates: Partial<HanzoDevConfig>): void {
|
|
124
|
+
this.config = { ...this.config, ...updates };
|
|
125
|
+
this.saveConfig();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private saveConfig(): void {
|
|
129
|
+
const configDir = path.dirname(this.configPath);
|
|
130
|
+
if (!fs.existsSync(configDir)) {
|
|
131
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const content = toml.stringify(this.config as any);
|
|
135
|
+
fs.writeFileSync(this.configPath, content);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Load LLM config from environment
|
|
139
|
+
public loadLLMConfigFromEnv(): LLMConfig {
|
|
140
|
+
const config = this.config.llm;
|
|
141
|
+
|
|
142
|
+
// Check for API keys in environment
|
|
143
|
+
if (process.env.OPENAI_API_KEY) {
|
|
144
|
+
config.apiKey = process.env.OPENAI_API_KEY;
|
|
145
|
+
config.provider = 'openai';
|
|
146
|
+
} else if (process.env.ANTHROPIC_API_KEY) {
|
|
147
|
+
config.apiKey = process.env.ANTHROPIC_API_KEY;
|
|
148
|
+
config.provider = 'anthropic';
|
|
149
|
+
config.model = 'claude-3-sonnet-20240229';
|
|
150
|
+
} else if (process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY) {
|
|
151
|
+
config.apiKey = process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY;
|
|
152
|
+
config.provider = 'google';
|
|
153
|
+
config.model = 'gemini-pro';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Check for base URL
|
|
157
|
+
if (process.env.LLM_BASE_URL) {
|
|
158
|
+
config.baseUrl = process.env.LLM_BASE_URL;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return config;
|
|
162
|
+
}
|
|
163
|
+
}
|