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.
Files changed (71) hide show
  1. package/ARCHITECTURE.md +411 -0
  2. package/CONFIGURATION.md +302 -0
  3. package/CONTRIBUTING.md +225 -0
  4. package/ELECTRON_README.md +121 -0
  5. package/INSTALLATION.md +350 -0
  6. package/LICENSE.md +1 -0
  7. package/PROJECT_STATUS.md +229 -0
  8. package/QUICKSTART.md +255 -0
  9. package/README.md +167 -0
  10. package/TESTING.md +274 -0
  11. package/package.json +61 -0
  12. package/scripts/start.js +30 -0
  13. package/src/assets/tray-icon.png +0 -0
  14. package/src/cli/commands/agent.js +327 -0
  15. package/src/cli/commands/click.js +108 -0
  16. package/src/cli/commands/drag.js +85 -0
  17. package/src/cli/commands/find.js +109 -0
  18. package/src/cli/commands/keys.js +132 -0
  19. package/src/cli/commands/mouse.js +79 -0
  20. package/src/cli/commands/repl.js +290 -0
  21. package/src/cli/commands/screenshot.js +72 -0
  22. package/src/cli/commands/scroll.js +74 -0
  23. package/src/cli/commands/start.js +67 -0
  24. package/src/cli/commands/type.js +57 -0
  25. package/src/cli/commands/wait.js +84 -0
  26. package/src/cli/commands/window.js +104 -0
  27. package/src/cli/liku.js +249 -0
  28. package/src/cli/util/output.js +174 -0
  29. package/src/main/agents/base-agent.js +410 -0
  30. package/src/main/agents/builder.js +484 -0
  31. package/src/main/agents/index.js +62 -0
  32. package/src/main/agents/orchestrator.js +362 -0
  33. package/src/main/agents/researcher.js +511 -0
  34. package/src/main/agents/state-manager.js +344 -0
  35. package/src/main/agents/supervisor.js +365 -0
  36. package/src/main/agents/verifier.js +452 -0
  37. package/src/main/ai-service.js +1633 -0
  38. package/src/main/index.js +2208 -0
  39. package/src/main/inspect-service.js +467 -0
  40. package/src/main/system-automation.js +1186 -0
  41. package/src/main/ui-automation/config.js +76 -0
  42. package/src/main/ui-automation/core/helpers.js +41 -0
  43. package/src/main/ui-automation/core/index.js +15 -0
  44. package/src/main/ui-automation/core/powershell.js +82 -0
  45. package/src/main/ui-automation/elements/finder.js +274 -0
  46. package/src/main/ui-automation/elements/index.js +14 -0
  47. package/src/main/ui-automation/elements/wait.js +66 -0
  48. package/src/main/ui-automation/index.js +164 -0
  49. package/src/main/ui-automation/interactions/element-click.js +211 -0
  50. package/src/main/ui-automation/interactions/high-level.js +230 -0
  51. package/src/main/ui-automation/interactions/index.js +47 -0
  52. package/src/main/ui-automation/keyboard/index.js +15 -0
  53. package/src/main/ui-automation/keyboard/input.js +179 -0
  54. package/src/main/ui-automation/mouse/click.js +186 -0
  55. package/src/main/ui-automation/mouse/drag.js +88 -0
  56. package/src/main/ui-automation/mouse/index.js +30 -0
  57. package/src/main/ui-automation/mouse/movement.js +51 -0
  58. package/src/main/ui-automation/mouse/scroll.js +116 -0
  59. package/src/main/ui-automation/screenshot.js +183 -0
  60. package/src/main/ui-automation/window/index.js +23 -0
  61. package/src/main/ui-automation/window/manager.js +305 -0
  62. package/src/main/utils/time.js +62 -0
  63. package/src/main/visual-awareness.js +597 -0
  64. package/src/renderer/chat/chat.js +671 -0
  65. package/src/renderer/chat/index.html +725 -0
  66. package/src/renderer/chat/preload.js +112 -0
  67. package/src/renderer/overlay/index.html +648 -0
  68. package/src/renderer/overlay/overlay.js +782 -0
  69. package/src/renderer/overlay/preload.js +90 -0
  70. package/src/shared/grid-math.js +82 -0
  71. 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
+ };