copilot-liku-cli 0.0.1
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/ARCHITECTURE.md +411 -0
- package/CONFIGURATION.md +302 -0
- package/CONTRIBUTING.md +225 -0
- package/ELECTRON_README.md +121 -0
- package/INSTALLATION.md +350 -0
- package/LICENSE.md +1 -0
- package/PROJECT_STATUS.md +229 -0
- package/QUICKSTART.md +255 -0
- package/README.md +167 -0
- package/TESTING.md +274 -0
- package/package.json +61 -0
- package/scripts/start.js +30 -0
- package/src/assets/tray-icon.png +0 -0
- package/src/cli/commands/agent.js +327 -0
- package/src/cli/commands/click.js +108 -0
- package/src/cli/commands/drag.js +85 -0
- package/src/cli/commands/find.js +109 -0
- package/src/cli/commands/keys.js +132 -0
- package/src/cli/commands/mouse.js +79 -0
- package/src/cli/commands/repl.js +290 -0
- package/src/cli/commands/screenshot.js +72 -0
- package/src/cli/commands/scroll.js +74 -0
- package/src/cli/commands/start.js +67 -0
- package/src/cli/commands/type.js +57 -0
- package/src/cli/commands/wait.js +84 -0
- package/src/cli/commands/window.js +104 -0
- package/src/cli/liku.js +249 -0
- package/src/cli/util/output.js +174 -0
- package/src/main/agents/base-agent.js +410 -0
- package/src/main/agents/builder.js +484 -0
- package/src/main/agents/index.js +62 -0
- package/src/main/agents/orchestrator.js +362 -0
- package/src/main/agents/researcher.js +511 -0
- package/src/main/agents/state-manager.js +344 -0
- package/src/main/agents/supervisor.js +365 -0
- package/src/main/agents/verifier.js +452 -0
- package/src/main/ai-service.js +1633 -0
- package/src/main/index.js +2208 -0
- package/src/main/inspect-service.js +467 -0
- package/src/main/system-automation.js +1186 -0
- package/src/main/ui-automation/config.js +76 -0
- package/src/main/ui-automation/core/helpers.js +41 -0
- package/src/main/ui-automation/core/index.js +15 -0
- package/src/main/ui-automation/core/powershell.js +82 -0
- package/src/main/ui-automation/elements/finder.js +274 -0
- package/src/main/ui-automation/elements/index.js +14 -0
- package/src/main/ui-automation/elements/wait.js +66 -0
- package/src/main/ui-automation/index.js +164 -0
- package/src/main/ui-automation/interactions/element-click.js +211 -0
- package/src/main/ui-automation/interactions/high-level.js +230 -0
- package/src/main/ui-automation/interactions/index.js +47 -0
- package/src/main/ui-automation/keyboard/index.js +15 -0
- package/src/main/ui-automation/keyboard/input.js +179 -0
- package/src/main/ui-automation/mouse/click.js +186 -0
- package/src/main/ui-automation/mouse/drag.js +88 -0
- package/src/main/ui-automation/mouse/index.js +30 -0
- package/src/main/ui-automation/mouse/movement.js +51 -0
- package/src/main/ui-automation/mouse/scroll.js +116 -0
- package/src/main/ui-automation/screenshot.js +183 -0
- package/src/main/ui-automation/window/index.js +23 -0
- package/src/main/ui-automation/window/manager.js +305 -0
- package/src/main/utils/time.js +62 -0
- package/src/main/visual-awareness.js +597 -0
- package/src/renderer/chat/chat.js +671 -0
- package/src/renderer/chat/index.html +725 -0
- package/src/renderer/chat/preload.js +112 -0
- package/src/renderer/overlay/index.html +648 -0
- package/src/renderer/overlay/overlay.js +782 -0
- package/src/renderer/overlay/preload.js +90 -0
- package/src/shared/grid-math.js +82 -0
- package/src/shared/inspect-types.js +230 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builder Agent
|
|
3
|
+
*
|
|
4
|
+
* Implements decomposed plans from Supervisor with minimal diffs and local proofs.
|
|
5
|
+
* Focuses on code changes without full verification (Verifier handles that).
|
|
6
|
+
*
|
|
7
|
+
* Operating Rules:
|
|
8
|
+
* - Implement only the assigned scope from Supervisor
|
|
9
|
+
* - Prefer minimal, localized diffs
|
|
10
|
+
* - Provide local proofs (lint/unit/build if available)
|
|
11
|
+
* - If blocked after 3 attempts, hand back with blocker and evidence
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { BaseAgent, AgentRole, AgentCapabilities } = require('./base-agent');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
class BuilderAgent extends BaseAgent {
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
super({
|
|
21
|
+
...options,
|
|
22
|
+
role: AgentRole.BUILDER,
|
|
23
|
+
name: options.name || 'builder',
|
|
24
|
+
description: 'Implements code changes with minimal diffs and local proofs',
|
|
25
|
+
capabilities: [
|
|
26
|
+
AgentCapabilities.SEARCH,
|
|
27
|
+
AgentCapabilities.READ,
|
|
28
|
+
AgentCapabilities.EDIT,
|
|
29
|
+
AgentCapabilities.EXECUTE,
|
|
30
|
+
AgentCapabilities.TODO,
|
|
31
|
+
AgentCapabilities.HANDOFF
|
|
32
|
+
]
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Builder-specific state
|
|
36
|
+
this.diffs = [];
|
|
37
|
+
this.localProofs = [];
|
|
38
|
+
this.blockers = [];
|
|
39
|
+
this.attemptCount = 0;
|
|
40
|
+
this.maxAttempts = 3;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getSystemPrompt() {
|
|
44
|
+
return `You are the BUILDER agent in a multi-agent coding system.
|
|
45
|
+
|
|
46
|
+
# OPERATING CONTRACT (NON-NEGOTIABLE)
|
|
47
|
+
- **No guessing**: Probe or ground with tools (search, read, execute).
|
|
48
|
+
- **Preserve functionalities**: Build additively; never disable core features.
|
|
49
|
+
- **Modularity & robustness**: Decompose into sub-modules; use todo for state.
|
|
50
|
+
- **Least privilege**: Prefer read/search; use edit only for assigned scope.
|
|
51
|
+
- **Recursion limits**: Depth ≤3; avoid >10 sub-calls without progress.
|
|
52
|
+
- **Security**: Isolate changes; audit proofs/logs.
|
|
53
|
+
- **Background hygiene**: Track long-running processes (PID/terminal id).
|
|
54
|
+
|
|
55
|
+
# YOUR RESPONSIBILITIES
|
|
56
|
+
1. Receive plan from Supervisor
|
|
57
|
+
2. Probe assigned module (read/search)
|
|
58
|
+
3. Implement via minimal diffs (edit)
|
|
59
|
+
4. Local verify: Lint + unit tests
|
|
60
|
+
5. Return: Diffs, rationale, local proofs
|
|
61
|
+
6. Suggest handoff: "Verify with Verifier" or "Back to Supervisor"
|
|
62
|
+
|
|
63
|
+
# WORKFLOW
|
|
64
|
+
For each assigned task:
|
|
65
|
+
1. Read and understand the target files
|
|
66
|
+
2. Plan the minimal changes needed
|
|
67
|
+
3. Implement changes with clear rationale
|
|
68
|
+
4. Run local verification (lint, type check, unit tests)
|
|
69
|
+
5. Document changes as diffs
|
|
70
|
+
|
|
71
|
+
# OUTPUT FORMAT
|
|
72
|
+
Always structure your response as:
|
|
73
|
+
1. Files Modified: [list of files]
|
|
74
|
+
2. Diffs: [minimal diffs with context]
|
|
75
|
+
3. Rationale: [why these changes]
|
|
76
|
+
4. Local Proofs: [lint/test output]
|
|
77
|
+
5. Status: [success/blocked]
|
|
78
|
+
6. Next: [verify/back to supervisor]
|
|
79
|
+
|
|
80
|
+
# BLOCKED HANDLING
|
|
81
|
+
If blocked after 3 attempts:
|
|
82
|
+
- Document the blocker clearly
|
|
83
|
+
- Include all evidence and attempts
|
|
84
|
+
- Hand back to Supervisor with suggestions`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async process(task, context = {}) {
|
|
88
|
+
this.log('info', 'Builder processing task', { task: task.description || task });
|
|
89
|
+
this.attemptCount++;
|
|
90
|
+
|
|
91
|
+
// Check if we've exceeded max attempts
|
|
92
|
+
if (this.attemptCount > this.maxAttempts) {
|
|
93
|
+
return this.reportBlocker('Exceeded maximum attempts', context);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Check recursion limits
|
|
97
|
+
const limits = this.checkRecursionLimits();
|
|
98
|
+
if (!limits.allowed) {
|
|
99
|
+
return this.reportBlocker(limits.reason, context);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
this.enterRecursion();
|
|
104
|
+
|
|
105
|
+
// Step 1: Probe and understand
|
|
106
|
+
const understanding = await this.probeTarget(task, context);
|
|
107
|
+
|
|
108
|
+
// Step 2: Plan changes
|
|
109
|
+
const changePlan = await this.planChanges(understanding, task);
|
|
110
|
+
|
|
111
|
+
// Step 3: Implement changes
|
|
112
|
+
const implementation = await this.implementChanges(changePlan, context);
|
|
113
|
+
|
|
114
|
+
// Step 4: Local verification
|
|
115
|
+
const proofs = await this.runLocalVerification(implementation);
|
|
116
|
+
this.localProofs.push(...proofs);
|
|
117
|
+
|
|
118
|
+
// Step 5: Compile results
|
|
119
|
+
const result = {
|
|
120
|
+
success: proofs.every(p => p.passed),
|
|
121
|
+
diffs: this.diffs,
|
|
122
|
+
proofs: this.localProofs,
|
|
123
|
+
rationale: changePlan.rationale,
|
|
124
|
+
filesModified: implementation.filesModified,
|
|
125
|
+
suggestedNext: proofs.every(p => p.passed) ? 'verify' : 'supervisor'
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
this.exitRecursion();
|
|
129
|
+
|
|
130
|
+
// Reset attempt count on success
|
|
131
|
+
if (result.success) {
|
|
132
|
+
this.attemptCount = 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return result;
|
|
136
|
+
|
|
137
|
+
} catch (error) {
|
|
138
|
+
this.exitRecursion();
|
|
139
|
+
this.blockers.push({
|
|
140
|
+
error: error.message,
|
|
141
|
+
attempt: this.attemptCount,
|
|
142
|
+
timestamp: new Date().toISOString()
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
if (this.attemptCount >= this.maxAttempts) {
|
|
146
|
+
return this.reportBlocker(error.message, context);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
error: error.message,
|
|
152
|
+
canRetry: this.attemptCount < this.maxAttempts
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async probeTarget(task, context) {
|
|
158
|
+
const taskDesc = typeof task === 'string' ? task : task.description;
|
|
159
|
+
|
|
160
|
+
// Extract file paths from task
|
|
161
|
+
const filePattern = /[a-zA-Z0-9_\-./]+\.(js|ts|jsx|tsx|json|md|py|rs|go)/g;
|
|
162
|
+
const mentionedFiles = taskDesc.match(filePattern) || [];
|
|
163
|
+
|
|
164
|
+
// Read mentioned files
|
|
165
|
+
const fileContents = {};
|
|
166
|
+
for (const file of mentionedFiles) {
|
|
167
|
+
const fullPath = path.isAbsolute(file) ? file : path.join(process.cwd(), file);
|
|
168
|
+
if (fs.existsSync(fullPath)) {
|
|
169
|
+
const result = await this.read(fullPath);
|
|
170
|
+
if (!result.error) {
|
|
171
|
+
fileContents[file] = result.content;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Ask LLM to understand the context
|
|
177
|
+
const prompt = `Analyze this task and the relevant files to understand what needs to be changed.
|
|
178
|
+
|
|
179
|
+
Task: ${taskDesc}
|
|
180
|
+
|
|
181
|
+
Files:
|
|
182
|
+
${Object.entries(fileContents).map(([f, c]) => `--- ${f} ---\n${c.slice(0, 1500)}`).join('\n\n')}
|
|
183
|
+
|
|
184
|
+
Provide:
|
|
185
|
+
1. What needs to change?
|
|
186
|
+
2. What are the dependencies?
|
|
187
|
+
3. What could break?`;
|
|
188
|
+
|
|
189
|
+
const response = await this.chat(prompt);
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
task: taskDesc,
|
|
193
|
+
files: mentionedFiles,
|
|
194
|
+
fileContents,
|
|
195
|
+
analysis: response.text
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async planChanges(understanding, task) {
|
|
200
|
+
const prompt = `Based on this analysis, plan the minimal changes needed.
|
|
201
|
+
|
|
202
|
+
Analysis: ${understanding.analysis}
|
|
203
|
+
Task: ${typeof task === 'string' ? task : task.description}
|
|
204
|
+
|
|
205
|
+
Provide:
|
|
206
|
+
1. Exact changes (old code → new code)
|
|
207
|
+
2. Files to modify
|
|
208
|
+
3. Order of changes
|
|
209
|
+
4. Rationale for each change`;
|
|
210
|
+
|
|
211
|
+
const response = await this.chat(prompt);
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
changes: this.parseChangePlan(response.text),
|
|
215
|
+
rationale: response.text,
|
|
216
|
+
understanding
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
parseChangePlan(planText) {
|
|
221
|
+
// Parse changes from the plan
|
|
222
|
+
const changes = [];
|
|
223
|
+
const blocks = planText.split(/(?=---\s*\w)/);
|
|
224
|
+
|
|
225
|
+
for (const block of blocks) {
|
|
226
|
+
const fileMatch = block.match(/(?:file|modify|change):\s*([^\n]+)/i);
|
|
227
|
+
if (fileMatch) {
|
|
228
|
+
changes.push({
|
|
229
|
+
file: fileMatch[1].trim(),
|
|
230
|
+
description: block
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return changes;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async implementChanges(changePlan, context) {
|
|
239
|
+
const filesModified = [];
|
|
240
|
+
const errors = [];
|
|
241
|
+
const rollbackData = [];
|
|
242
|
+
|
|
243
|
+
for (const change of changePlan.changes) {
|
|
244
|
+
try {
|
|
245
|
+
const originalContent = changePlan.understanding.fileContents[change.file];
|
|
246
|
+
if (originalContent) {
|
|
247
|
+
rollbackData.push({
|
|
248
|
+
file: change.file,
|
|
249
|
+
originalContent,
|
|
250
|
+
timestamp: new Date().toISOString()
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Generate the actual edit
|
|
255
|
+
const prompt = `Generate the exact code change for this modification:
|
|
256
|
+
|
|
257
|
+
File: ${change.file}
|
|
258
|
+
Change description: ${change.description}
|
|
259
|
+
Current content: ${changePlan.understanding.fileContents[change.file]?.slice(0, 2000) || 'Not loaded'}
|
|
260
|
+
|
|
261
|
+
Provide the change in unified diff format:
|
|
262
|
+
\`\`\`diff
|
|
263
|
+
--- a/${change.file}
|
|
264
|
+
+++ b/${change.file}
|
|
265
|
+
@@ -X,Y +X,Y @@
|
|
266
|
+
context
|
|
267
|
+
-old line
|
|
268
|
+
+new line
|
|
269
|
+
context
|
|
270
|
+
\`\`\``;
|
|
271
|
+
|
|
272
|
+
const response = await this.chat(prompt);
|
|
273
|
+
|
|
274
|
+
// Extract and store diff
|
|
275
|
+
const diffMatch = response.text.match(/```diff\n([\s\S]*?)```/);
|
|
276
|
+
if (diffMatch) {
|
|
277
|
+
this.diffs.push({
|
|
278
|
+
file: change.file,
|
|
279
|
+
diff: diffMatch[1],
|
|
280
|
+
timestamp: new Date().toISOString(),
|
|
281
|
+
modelMetadata: this.modelMetadata,
|
|
282
|
+
planId: changePlan.planId,
|
|
283
|
+
rationale: change.description,
|
|
284
|
+
rollbackAvailable: !!originalContent
|
|
285
|
+
});
|
|
286
|
+
filesModified.push(change.file);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// In a real implementation, we would apply the diff here
|
|
290
|
+
// For now, we just record it
|
|
291
|
+
this.addProof('diff', diffMatch?.[1] || response.text, change.file);
|
|
292
|
+
|
|
293
|
+
} catch (error) {
|
|
294
|
+
errors.push({
|
|
295
|
+
file: change.file,
|
|
296
|
+
error: error.message
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
filesModified,
|
|
303
|
+
errors,
|
|
304
|
+
diffs: this.diffs,
|
|
305
|
+
rollbackData
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async runLocalVerification(implementation) {
|
|
310
|
+
const proofs = [];
|
|
311
|
+
|
|
312
|
+
// Run linter if available
|
|
313
|
+
try {
|
|
314
|
+
const lintResult = await this.execute('npm run lint --if-present 2>&1 || echo "No lint script"', { timeout: 30000 });
|
|
315
|
+
proofs.push({
|
|
316
|
+
type: 'lint',
|
|
317
|
+
passed: !lintResult.error && !lintResult.stderr?.includes('error'),
|
|
318
|
+
output: lintResult.stdout || lintResult.stderr,
|
|
319
|
+
timestamp: new Date().toISOString()
|
|
320
|
+
});
|
|
321
|
+
} catch (error) {
|
|
322
|
+
proofs.push({
|
|
323
|
+
type: 'lint',
|
|
324
|
+
passed: false,
|
|
325
|
+
error: error.message,
|
|
326
|
+
timestamp: new Date().toISOString()
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Run type check if TypeScript
|
|
331
|
+
try {
|
|
332
|
+
const tscResult = await this.execute('npx tsc --noEmit 2>&1 || echo "No TypeScript"', { timeout: 60000 });
|
|
333
|
+
proofs.push({
|
|
334
|
+
type: 'typecheck',
|
|
335
|
+
passed: !tscResult.error && !tscResult.stdout?.includes('error'),
|
|
336
|
+
output: tscResult.stdout || tscResult.stderr,
|
|
337
|
+
timestamp: new Date().toISOString()
|
|
338
|
+
});
|
|
339
|
+
} catch (error) {
|
|
340
|
+
proofs.push({
|
|
341
|
+
type: 'typecheck',
|
|
342
|
+
passed: true, // Skip on error
|
|
343
|
+
skipped: true,
|
|
344
|
+
timestamp: new Date().toISOString()
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Run unit tests for modified files
|
|
349
|
+
for (const file of implementation.filesModified) {
|
|
350
|
+
const testFile = file.replace(/\.(js|ts)$/, '.test.$1');
|
|
351
|
+
if (fs.existsSync(testFile)) {
|
|
352
|
+
try {
|
|
353
|
+
const testResult = await this.execute(`npm test -- --testPathPattern="${path.basename(testFile)}" 2>&1`, { timeout: 60000 });
|
|
354
|
+
proofs.push({
|
|
355
|
+
type: 'unit-test',
|
|
356
|
+
file: testFile,
|
|
357
|
+
passed: !testResult.error && testResult.stdout?.includes('passed'),
|
|
358
|
+
output: testResult.stdout,
|
|
359
|
+
timestamp: new Date().toISOString()
|
|
360
|
+
});
|
|
361
|
+
} catch (error) {
|
|
362
|
+
proofs.push({
|
|
363
|
+
type: 'unit-test',
|
|
364
|
+
file: testFile,
|
|
365
|
+
passed: false,
|
|
366
|
+
error: error.message,
|
|
367
|
+
timestamp: new Date().toISOString()
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return proofs;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
reportBlocker(reason, context) {
|
|
377
|
+
const blockerReport = {
|
|
378
|
+
success: false,
|
|
379
|
+
blocked: true,
|
|
380
|
+
reason,
|
|
381
|
+
attempts: this.attemptCount,
|
|
382
|
+
blockers: this.blockers,
|
|
383
|
+
evidence: {
|
|
384
|
+
diffs: this.diffs,
|
|
385
|
+
proofs: this.localProofs
|
|
386
|
+
},
|
|
387
|
+
suggestedNext: 'supervisor',
|
|
388
|
+
timestamp: new Date().toISOString()
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
this.log('warn', 'Builder blocked', blockerReport);
|
|
392
|
+
|
|
393
|
+
return blockerReport;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async rollback(rollbackData) {
|
|
397
|
+
const results = [];
|
|
398
|
+
|
|
399
|
+
for (const item of rollbackData) {
|
|
400
|
+
try {
|
|
401
|
+
fs.writeFileSync(item.file, item.originalContent);
|
|
402
|
+
results.push({
|
|
403
|
+
file: item.file,
|
|
404
|
+
success: true,
|
|
405
|
+
timestamp: new Date().toISOString()
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
this.addStructuredProof({
|
|
409
|
+
type: 'rollback',
|
|
410
|
+
file: item.file,
|
|
411
|
+
reason: 'Rollback requested'
|
|
412
|
+
});
|
|
413
|
+
} catch (error) {
|
|
414
|
+
results.push({
|
|
415
|
+
file: item.file,
|
|
416
|
+
success: false,
|
|
417
|
+
error: error.message
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return results;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// ===== Builder-specific Methods =====
|
|
426
|
+
|
|
427
|
+
async createFile(filePath, content, rationale) {
|
|
428
|
+
if (!this.capabilities.includes(AgentCapabilities.EDIT)) {
|
|
429
|
+
return { error: 'No edit capability' };
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const dir = path.dirname(filePath);
|
|
433
|
+
if (!fs.existsSync(dir)) {
|
|
434
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
fs.writeFileSync(filePath, content);
|
|
438
|
+
|
|
439
|
+
this.diffs.push({
|
|
440
|
+
file: filePath,
|
|
441
|
+
type: 'create',
|
|
442
|
+
content: content.slice(0, 500) + '...',
|
|
443
|
+
rationale,
|
|
444
|
+
timestamp: new Date().toISOString()
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
this.addProof('file-created', filePath);
|
|
448
|
+
|
|
449
|
+
return { success: true, filePath };
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
async modifyFile(filePath, oldContent, newContent, rationale) {
|
|
453
|
+
if (!this.capabilities.includes(AgentCapabilities.EDIT)) {
|
|
454
|
+
return { error: 'No edit capability' };
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (!fs.existsSync(filePath)) {
|
|
458
|
+
return { error: `File not found: ${filePath}` };
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
fs.writeFileSync(filePath, newContent);
|
|
462
|
+
|
|
463
|
+
this.diffs.push({
|
|
464
|
+
file: filePath,
|
|
465
|
+
type: 'modify',
|
|
466
|
+
rationale,
|
|
467
|
+
timestamp: new Date().toISOString()
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
this.addProof('file-modified', filePath);
|
|
471
|
+
|
|
472
|
+
return { success: true, filePath };
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
reset() {
|
|
476
|
+
super.reset();
|
|
477
|
+
this.diffs = [];
|
|
478
|
+
this.localProofs = [];
|
|
479
|
+
this.blockers = [];
|
|
480
|
+
this.attemptCount = 0;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
module.exports = { BuilderAgent };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Agent System for Copilot-Liku CLI
|
|
3
|
+
*
|
|
4
|
+
* Architecture: Supervisor-Builder-Verifier with Recursive Long-Context support
|
|
5
|
+
* Based on RLM-inspired agent patterns for comprehensive task handling.
|
|
6
|
+
*
|
|
7
|
+
* Agents:
|
|
8
|
+
* - Supervisor: Orchestrates and decomposes tasks
|
|
9
|
+
* - Builder: Implements code changes with minimal diffs
|
|
10
|
+
* - Verifier: Validates changes with phased verification
|
|
11
|
+
* - Researcher: Gathers context and information (optional)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { AgentOrchestrator } = require('./orchestrator');
|
|
15
|
+
const { SupervisorAgent } = require('./supervisor');
|
|
16
|
+
const { BuilderAgent } = require('./builder');
|
|
17
|
+
const { VerifierAgent } = require('./verifier');
|
|
18
|
+
const { ResearcherAgent } = require('./researcher');
|
|
19
|
+
const { AgentStateManager } = require('./state-manager');
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
AgentOrchestrator,
|
|
23
|
+
SupervisorAgent,
|
|
24
|
+
BuilderAgent,
|
|
25
|
+
VerifierAgent,
|
|
26
|
+
ResearcherAgent,
|
|
27
|
+
AgentStateManager,
|
|
28
|
+
|
|
29
|
+
// Factory function for creating configured orchestrator
|
|
30
|
+
createAgentSystem: (options = {}) => {
|
|
31
|
+
const stateManager = new AgentStateManager(options.statePath);
|
|
32
|
+
|
|
33
|
+
const modelMetadata = options.aiService?.getModelMetadata?.() || null;
|
|
34
|
+
|
|
35
|
+
if (modelMetadata) {
|
|
36
|
+
stateManager.setModelMetadata(modelMetadata);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const orchestrator = new AgentOrchestrator({
|
|
40
|
+
stateManager,
|
|
41
|
+
aiService: options.aiService,
|
|
42
|
+
maxRecursionDepth: options.maxRecursionDepth || 3,
|
|
43
|
+
maxSubCalls: options.maxSubCalls || 10,
|
|
44
|
+
enableLongContext: options.enableLongContext !== false,
|
|
45
|
+
modelMetadata
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return orchestrator;
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
// Recovery function for checkpoint restoration
|
|
52
|
+
recoverFromCheckpoint: (checkpointId, options = {}) => {
|
|
53
|
+
const stateManager = new AgentStateManager(options.statePath);
|
|
54
|
+
const checkpoint = stateManager.getCheckpoint(checkpointId);
|
|
55
|
+
|
|
56
|
+
if (!checkpoint) {
|
|
57
|
+
throw new Error(`Checkpoint not found: ${checkpointId}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return checkpoint;
|
|
61
|
+
}
|
|
62
|
+
};
|