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,90 @@
1
+ const { contextBridge, ipcRenderer } = require('electron');
2
+
3
+ // BLOCKER-1 FIX: Inline grid-math since sandboxed preload can't access 'path' module
4
+ // or require external files. These are small utilities needed for coordinate conversion.
5
+
6
+ const COARSE_SPACING = 100;
7
+ const FINE_SPACING = 25;
8
+ const START_OFFSET = COARSE_SPACING / 2;
9
+ const FINE_START = FINE_SPACING / 2;
10
+
11
+ function colLettersToIndex(letters) {
12
+ if (!letters || letters.length === 0) return null;
13
+ if (letters.length === 1) {
14
+ return letters.charCodeAt(0) - 65;
15
+ }
16
+ const first = letters.charCodeAt(0) - 65 + 1;
17
+ const second = letters.charCodeAt(1) - 65;
18
+ return (first * 26) + second;
19
+ }
20
+
21
+ function labelToScreenCoordinates(label) {
22
+ if (!label) return null;
23
+ const match = label.match(/^([A-Z]+)(\d+)(\.(\d)(\d))?$/);
24
+ if (!match) return null;
25
+
26
+ const [, letters, rowStr, , subColStr, subRowStr] = match;
27
+ const colIndex = colLettersToIndex(letters);
28
+ const rowIndex = parseInt(rowStr, 10);
29
+ if (colIndex === null || Number.isNaN(rowIndex)) return null;
30
+
31
+ if (subColStr && subRowStr) {
32
+ const subCol = parseInt(subColStr, 10);
33
+ const subRow = parseInt(subRowStr, 10);
34
+ if (Number.isNaN(subCol) || Number.isNaN(subRow)) return null;
35
+ const fineCol = (colIndex * 4) + subCol;
36
+ const fineRow = (rowIndex * 4) + subRow;
37
+ const x = FINE_START + fineCol * FINE_SPACING;
38
+ const y = FINE_START + fineRow * FINE_SPACING;
39
+ return { x, y, screenX: x, screenY: y, colIndex, rowIndex, fineCol, fineRow, subCol, subRow, isFine: true };
40
+ }
41
+
42
+ const x = START_OFFSET + colIndex * COARSE_SPACING;
43
+ const y = START_OFFSET + rowIndex * COARSE_SPACING;
44
+ return { x, y, screenX: x, screenY: y, colIndex, rowIndex, isFine: false };
45
+ }
46
+
47
+ const gridConstants = {
48
+ coarseSpacing: COARSE_SPACING,
49
+ fineSpacing: FINE_SPACING,
50
+ startOffset: START_OFFSET,
51
+ fineStart: FINE_START,
52
+ localFineRadius: 3
53
+ };
54
+
55
+ // Expose protected methods that allow the renderer process to use
56
+ // the ipcRenderer without exposing the entire object
57
+ contextBridge.exposeInMainWorld('electronAPI', {
58
+ // Send dot selection to main process
59
+ selectDot: (data) => ipcRenderer.send('dot-selected', data),
60
+
61
+ // Listen for mode changes
62
+ onModeChanged: (callback) => ipcRenderer.on('mode-changed', (event, mode) => callback(mode)),
63
+
64
+ // Listen for overlay commands (keyboard shortcuts routed via main process)
65
+ onOverlayCommand: (callback) => ipcRenderer.on('overlay-command', (event, data) => callback(data)),
66
+
67
+ // Get current state
68
+ getState: () => ipcRenderer.invoke('get-state'),
69
+
70
+ // Grid math helpers (inlined above)
71
+ getGridConstants: () => gridConstants,
72
+ labelToScreenCoordinates: (label) => labelToScreenCoordinates(label),
73
+
74
+ // ===== INSPECT MODE API =====
75
+
76
+ // Select an inspect region (sends targetId + bounds to main)
77
+ selectInspectRegion: (data) => ipcRenderer.send('inspect-region-selected', data),
78
+
79
+ // Listen for inspect regions updates
80
+ onInspectRegionsUpdate: (callback) => ipcRenderer.on('inspect-regions-update', (event, regions) => callback(regions)),
81
+
82
+ // Listen for inspect mode toggle
83
+ onInspectModeChanged: (callback) => ipcRenderer.on('inspect-mode-changed', (event, enabled) => callback(enabled)),
84
+
85
+ // Request inspect region detection
86
+ requestInspectRegions: () => ipcRenderer.send('request-inspect-regions'),
87
+
88
+ // Toggle inspect mode
89
+ toggleInspectMode: () => ipcRenderer.send('toggle-inspect-mode')
90
+ });
@@ -0,0 +1,82 @@
1
+ const COARSE_SPACING = 100;
2
+ const FINE_SPACING = 25;
3
+ const START_OFFSET = COARSE_SPACING / 2;
4
+ const FINE_START = FINE_SPACING / 2;
5
+
6
+ function getColLetter(colIndex) {
7
+ let letter = '';
8
+ if (colIndex >= 26) {
9
+ letter += String.fromCharCode(65 + Math.floor(colIndex / 26) - 1);
10
+ }
11
+ letter += String.fromCharCode(65 + (colIndex % 26));
12
+ return letter;
13
+ }
14
+
15
+ function colLettersToIndex(letters) {
16
+ if (!letters || letters.length === 0) return null;
17
+ if (letters.length === 1) {
18
+ return letters.charCodeAt(0) - 65;
19
+ }
20
+ const first = letters.charCodeAt(0) - 65 + 1;
21
+ const second = letters.charCodeAt(1) - 65;
22
+ return (first * 26) + second;
23
+ }
24
+
25
+ function labelToScreenCoordinates(label) {
26
+ if (!label) return null;
27
+ const match = label.match(/^([A-Z]+)(\d+)(\.(\d)(\d))?$/);
28
+ if (!match) return null;
29
+
30
+ const [, letters, rowStr, , subColStr, subRowStr] = match;
31
+ const colIndex = colLettersToIndex(letters);
32
+ const rowIndex = parseInt(rowStr, 10);
33
+ if (colIndex === null || Number.isNaN(rowIndex)) return null;
34
+
35
+ if (subColStr && subRowStr) {
36
+ const subCol = parseInt(subColStr, 10);
37
+ const subRow = parseInt(subRowStr, 10);
38
+ if (Number.isNaN(subCol) || Number.isNaN(subRow)) return null;
39
+ const fineCol = (colIndex * 4) + subCol;
40
+ const fineRow = (rowIndex * 4) + subRow;
41
+ const x = FINE_START + fineCol * FINE_SPACING;
42
+ const y = FINE_START + fineRow * FINE_SPACING;
43
+ return {
44
+ x,
45
+ y,
46
+ screenX: x,
47
+ screenY: y,
48
+ colIndex,
49
+ rowIndex,
50
+ fineCol,
51
+ fineRow,
52
+ subCol,
53
+ subRow,
54
+ isFine: true
55
+ };
56
+ }
57
+
58
+ const x = START_OFFSET + colIndex * COARSE_SPACING;
59
+ const y = START_OFFSET + rowIndex * COARSE_SPACING;
60
+ return {
61
+ x,
62
+ y,
63
+ screenX: x,
64
+ screenY: y,
65
+ colIndex,
66
+ rowIndex,
67
+ isFine: false
68
+ };
69
+ }
70
+
71
+ module.exports = {
72
+ constants: {
73
+ coarseSpacing: COARSE_SPACING,
74
+ fineSpacing: FINE_SPACING,
75
+ startOffset: START_OFFSET,
76
+ fineStart: FINE_START,
77
+ localFineRadius: 3
78
+ },
79
+ getColLetter,
80
+ colLettersToIndex,
81
+ labelToScreenCoordinates
82
+ };
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Inspect Overlay Data Contracts
3
+ * Shared type definitions for inspect regions, window context, and action traces
4
+ */
5
+
6
+ /**
7
+ * Inspect Region Data Contract
8
+ * Represents an actionable region on screen detected through various sources
9
+ * @typedef {Object} InspectRegion
10
+ * @property {string} id - Unique identifier for the region
11
+ * @property {Object} bounds - Bounding box {x, y, width, height}
12
+ * @property {string} label - Human-readable label (e.g., "Search button")
13
+ * @property {string} text - Text content if available
14
+ * @property {string} role - Accessibility role (button, textbox, etc.)
15
+ * @property {number} confidence - Detection confidence 0-1
16
+ * @property {string} source - Detection source (accessibility, ocr, heuristic)
17
+ * @property {number} timestamp - When this region was detected
18
+ */
19
+
20
+ /**
21
+ * Window Context Data Contract
22
+ * Information about the active window and process
23
+ * @typedef {Object} WindowContext
24
+ * @property {string} appName - Application name
25
+ * @property {string} windowTitle - Window title
26
+ * @property {number} pid - Process ID
27
+ * @property {Object} bounds - Window bounds {x, y, width, height}
28
+ * @property {number} zOrder - Z-order (depth) of window
29
+ * @property {number} scaleFactor - Display scale factor for DPI normalization
30
+ */
31
+
32
+ /**
33
+ * Action Trace Data Contract
34
+ * Records of actions for replay and debugging
35
+ * @typedef {Object} ActionTrace
36
+ * @property {string} actionId - Unique action identifier
37
+ * @property {string} type - Action type (click, type, key, etc.)
38
+ * @property {string} [targetId] - ID of target region if applicable
39
+ * @property {number} x - X coordinate
40
+ * @property {number} y - Y coordinate
41
+ * @property {number} timestamp - When action was executed
42
+ * @property {string} outcome - Result (success, failed, pending)
43
+ */
44
+
45
+ /**
46
+ * Create a new inspect region object
47
+ * @param {Object} params - Region parameters
48
+ * @returns {InspectRegion}
49
+ */
50
+ function createInspectRegion(params) {
51
+ return {
52
+ id: params.id || `region-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
53
+ bounds: {
54
+ x: params.x || params.bounds?.x || 0,
55
+ y: params.y || params.bounds?.y || 0,
56
+ width: params.width || params.bounds?.width || 0,
57
+ height: params.height || params.bounds?.height || 0
58
+ },
59
+ label: params.label || params.name || '',
60
+ text: params.text || '',
61
+ role: params.role || params.controlType || 'unknown',
62
+ confidence: typeof params.confidence === 'number' ? params.confidence : 0.5,
63
+ source: params.source || 'unknown',
64
+ timestamp: params.timestamp || Date.now()
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Create a new window context object
70
+ * @param {Object} params - Window parameters
71
+ * @returns {WindowContext}
72
+ */
73
+ function createWindowContext(params) {
74
+ return {
75
+ appName: params.appName || params.processName || '',
76
+ windowTitle: params.windowTitle || params.title || '',
77
+ pid: params.pid || params.processId || 0,
78
+ bounds: {
79
+ x: params.bounds?.x || params.bounds?.X || 0,
80
+ y: params.bounds?.y || params.bounds?.Y || 0,
81
+ width: params.bounds?.width || params.bounds?.Width || 0,
82
+ height: params.bounds?.height || params.bounds?.Height || 0
83
+ },
84
+ zOrder: params.zOrder || 0,
85
+ scaleFactor: params.scaleFactor || 1
86
+ };
87
+ }
88
+
89
+ /**
90
+ * Create a new action trace object
91
+ * @param {Object} params - Action parameters
92
+ * @returns {ActionTrace}
93
+ */
94
+ function createActionTrace(params) {
95
+ return {
96
+ actionId: params.actionId || `action-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
97
+ type: params.type || 'unknown',
98
+ targetId: params.targetId || null,
99
+ x: params.x || 0,
100
+ y: params.y || 0,
101
+ timestamp: params.timestamp || Date.now(),
102
+ outcome: params.outcome || 'pending'
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Normalize coordinates with scale factor
108
+ * @param {number} x - X coordinate
109
+ * @param {number} y - Y coordinate
110
+ * @param {number} scaleFactor - Display scale factor
111
+ * @returns {Object} Normalized {x, y}
112
+ */
113
+ function normalizeCoordinates(x, y, scaleFactor = 1) {
114
+ return {
115
+ x: Math.round(x * scaleFactor),
116
+ y: Math.round(y * scaleFactor)
117
+ };
118
+ }
119
+
120
+ /**
121
+ * Denormalize coordinates from scaled to logical
122
+ * @param {number} x - X coordinate (scaled)
123
+ * @param {number} y - Y coordinate (scaled)
124
+ * @param {number} scaleFactor - Display scale factor
125
+ * @returns {Object} Logical {x, y}
126
+ */
127
+ function denormalizeCoordinates(x, y, scaleFactor = 1) {
128
+ return {
129
+ x: Math.round(x / scaleFactor),
130
+ y: Math.round(y / scaleFactor)
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Check if a point is within a region's bounds
136
+ * @param {number} x - X coordinate
137
+ * @param {number} y - Y coordinate
138
+ * @param {InspectRegion} region - The region to check
139
+ * @returns {boolean}
140
+ */
141
+ function isPointInRegion(x, y, region) {
142
+ const { bounds } = region;
143
+ return x >= bounds.x &&
144
+ x < bounds.x + bounds.width &&
145
+ y >= bounds.y &&
146
+ y < bounds.y + bounds.height;
147
+ }
148
+
149
+ /**
150
+ * Find the closest region to a point
151
+ * @param {number} x - X coordinate
152
+ * @param {number} y - Y coordinate
153
+ * @param {InspectRegion[]} regions - Array of regions
154
+ * @returns {InspectRegion|null} Closest region or null
155
+ */
156
+ function findClosestRegion(x, y, regions) {
157
+ if (!regions || regions.length === 0) return null;
158
+
159
+ let closest = null;
160
+ let minDist = Infinity;
161
+
162
+ for (const region of regions) {
163
+ const centerX = region.bounds.x + region.bounds.width / 2;
164
+ const centerY = region.bounds.y + region.bounds.height / 2;
165
+ const dist = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2));
166
+
167
+ if (dist < minDist) {
168
+ minDist = dist;
169
+ closest = region;
170
+ }
171
+ }
172
+
173
+ return closest;
174
+ }
175
+
176
+ /**
177
+ * Find region containing a point
178
+ * @param {number} x - X coordinate
179
+ * @param {number} y - Y coordinate
180
+ * @param {InspectRegion[]} regions - Array of regions
181
+ * @returns {InspectRegion|null} Containing region or null
182
+ */
183
+ function findRegionAtPoint(x, y, regions) {
184
+ if (!regions || regions.length === 0) return null;
185
+
186
+ // Find all regions containing the point
187
+ const containing = regions.filter(r => isPointInRegion(x, y, r));
188
+
189
+ if (containing.length === 0) return null;
190
+ if (containing.length === 1) return containing[0];
191
+
192
+ // If multiple regions, return the smallest (most specific)
193
+ return containing.reduce((smallest, r) => {
194
+ const smallestArea = smallest.bounds.width * smallest.bounds.height;
195
+ const rArea = r.bounds.width * r.bounds.height;
196
+ return rArea < smallestArea ? r : smallest;
197
+ });
198
+ }
199
+
200
+ /**
201
+ * Format region for AI context
202
+ * @param {InspectRegion} region - The region to format
203
+ * @returns {Object} AI-friendly format
204
+ */
205
+ function formatRegionForAI(region) {
206
+ return {
207
+ id: region.id,
208
+ label: region.label,
209
+ text: region.text,
210
+ role: region.role,
211
+ confidence: region.confidence,
212
+ center: {
213
+ x: Math.round(region.bounds.x + region.bounds.width / 2),
214
+ y: Math.round(region.bounds.y + region.bounds.height / 2)
215
+ },
216
+ bounds: region.bounds
217
+ };
218
+ }
219
+
220
+ module.exports = {
221
+ createInspectRegion,
222
+ createWindowContext,
223
+ createActionTrace,
224
+ normalizeCoordinates,
225
+ denormalizeCoordinates,
226
+ isPointInRegion,
227
+ findClosestRegion,
228
+ findRegionAtPoint,
229
+ formatRegionForAI
230
+ };