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,108 @@
1
+ /**
2
+ * click command - Click element by text or coordinates
3
+ * @module cli/commands/click
4
+ */
5
+
6
+ const path = require('path');
7
+ const { success, error, info, Spinner } = require('../util/output');
8
+
9
+ // Load UI automation module
10
+ const UI_MODULE = path.resolve(__dirname, '../../main/ui-automation');
11
+ let ui;
12
+
13
+ function loadUI() {
14
+ if (!ui) {
15
+ ui = require(UI_MODULE);
16
+ }
17
+ return ui;
18
+ }
19
+
20
+ /**
21
+ * Run the click command
22
+ *
23
+ * Usage:
24
+ * liku click "Button Text"
25
+ * liku click 500,300
26
+ * liku click "Submit" --double
27
+ * liku click "Menu" --right
28
+ */
29
+ async function run(args, options) {
30
+ if (args.length === 0) {
31
+ error('Usage: liku click <text|x,y> [--double] [--right] [--wait <ms>]');
32
+ return { success: false };
33
+ }
34
+
35
+ loadUI();
36
+ const target = args.join(' ');
37
+
38
+ // Check if target is coordinates (e.g., "500,300" or "500 300")
39
+ const coordMatch = target.match(/^(\d+)[,\s]+(\d+)$/);
40
+
41
+ if (coordMatch) {
42
+ // Click at coordinates
43
+ const x = parseInt(coordMatch[1], 10);
44
+ const y = parseInt(coordMatch[2], 10);
45
+
46
+ if (!options.quiet) {
47
+ info(`Clicking at (${x}, ${y})...`);
48
+ }
49
+
50
+ const button = options.right ? 'right' : 'left';
51
+ const clickFn = options.double ? ui.doubleClickAt : ui.clickAt;
52
+
53
+ const result = await clickFn(x, y, { button });
54
+
55
+ if (result.success) {
56
+ if (!options.quiet) {
57
+ success(`Clicked at (${x}, ${y})`);
58
+ }
59
+ return { success: true, x, y, method: 'coordinates' };
60
+ } else {
61
+ error(`Click failed: ${result.error || 'Unknown error'}`);
62
+ return { success: false, error: result.error };
63
+ }
64
+ } else {
65
+ // Click by text
66
+ const spinner = !options.quiet ? new Spinner(`Searching for "${target}"`) : null;
67
+ spinner?.start();
68
+
69
+ const criteria = { text: target };
70
+
71
+ // Add control type filter if specified
72
+ if (options.type) {
73
+ criteria.controlType = options.type;
74
+ }
75
+
76
+ // Add window filter if specified
77
+ if (options.window) {
78
+ criteria.windowTitle = options.window;
79
+ }
80
+
81
+ const clickOptions = {
82
+ button: options.right ? 'right' : 'left',
83
+ doubleClick: options.double || false,
84
+ waitTimeout: options.wait ? parseInt(options.wait, 10) : 0,
85
+ };
86
+
87
+ const result = await ui.click(criteria, clickOptions);
88
+
89
+ spinner?.stop();
90
+
91
+ if (result.success) {
92
+ if (!options.quiet) {
93
+ const element = result.element;
94
+ success(`Clicked "${element?.name || target}" (${element?.controlType || 'unknown'})`);
95
+ }
96
+ return {
97
+ success: true,
98
+ element: result.element,
99
+ method: 'text',
100
+ };
101
+ } else {
102
+ error(`Element not found: "${target}"`);
103
+ return { success: false, error: result.error || 'Element not found' };
104
+ }
105
+ }
106
+ }
107
+
108
+ module.exports = { run };
@@ -0,0 +1,85 @@
1
+ /**
2
+ * drag command - Drag from one point to another
3
+ * @module cli/commands/drag
4
+ */
5
+
6
+ const path = require('path');
7
+ const { success, error, info } = require('../util/output');
8
+
9
+ const UI_MODULE = path.resolve(__dirname, '../../main/ui-automation');
10
+ let ui;
11
+
12
+ function loadUI() {
13
+ if (!ui) {
14
+ ui = require(UI_MODULE);
15
+ }
16
+ return ui;
17
+ }
18
+
19
+ /**
20
+ * Run the drag command
21
+ *
22
+ * Usage:
23
+ * liku drag 100 100 500 300
24
+ * liku drag 100,100 to 500,300
25
+ */
26
+ async function run(args, options) {
27
+ loadUI();
28
+
29
+ // Parse coordinates
30
+ let fromX, fromY, toX, toY;
31
+
32
+ // Remove "to" keyword if present
33
+ const cleanArgs = args.filter(a => a.toLowerCase() !== 'to');
34
+
35
+ if (cleanArgs.length === 2) {
36
+ // Format: "100,100" "500,300"
37
+ const from = cleanArgs[0].split(',');
38
+ const to = cleanArgs[1].split(',');
39
+ fromX = parseInt(from[0], 10);
40
+ fromY = parseInt(from[1], 10);
41
+ toX = parseInt(to[0], 10);
42
+ toY = parseInt(to[1], 10);
43
+ } else if (cleanArgs.length >= 4) {
44
+ // Format: "100 100 500 300"
45
+ fromX = parseInt(cleanArgs[0], 10);
46
+ fromY = parseInt(cleanArgs[1], 10);
47
+ toX = parseInt(cleanArgs[2], 10);
48
+ toY = parseInt(cleanArgs[3], 10);
49
+ } else {
50
+ error('Usage: liku drag <fromX> <fromY> <toX> <toY>');
51
+ info('Example: liku drag 100 100 500 300');
52
+ return { success: false };
53
+ }
54
+
55
+ if ([fromX, fromY, toX, toY].some(isNaN)) {
56
+ error('Invalid coordinates. Use numbers.');
57
+ return { success: false };
58
+ }
59
+
60
+ if (!options.quiet) {
61
+ info(`Dragging from (${fromX}, ${fromY}) to (${toX}, ${toY})...`);
62
+ }
63
+
64
+ const dragOptions = {};
65
+ if (options.steps) {
66
+ dragOptions.steps = parseInt(options.steps, 10);
67
+ }
68
+ if (options.delay) {
69
+ dragOptions.stepDelay = parseInt(options.delay, 10);
70
+ }
71
+
72
+ const result = await ui.drag(fromX, fromY, toX, toY, dragOptions);
73
+
74
+ if (result.success) {
75
+ if (!options.quiet) {
76
+ success(`Dragged from (${fromX}, ${fromY}) to (${toX}, ${toY})`);
77
+ }
78
+ return { success: true, from: { x: fromX, y: fromY }, to: { x: toX, y: toY } };
79
+ } else {
80
+ error(`Drag failed: ${result.error || 'Unknown error'}`);
81
+ return { success: false, error: result.error };
82
+ }
83
+ }
84
+
85
+ module.exports = { run };
@@ -0,0 +1,109 @@
1
+ /**
2
+ * find command - Find UI elements matching criteria
3
+ * @module cli/commands/find
4
+ */
5
+
6
+ const path = require('path');
7
+ const { success, error, info, table, dim, highlight } = require('../util/output');
8
+
9
+ const UI_MODULE = path.resolve(__dirname, '../../main/ui-automation');
10
+ let ui;
11
+
12
+ function loadUI() {
13
+ if (!ui) {
14
+ ui = require(UI_MODULE);
15
+ }
16
+ return ui;
17
+ }
18
+
19
+ /**
20
+ * Run the find command
21
+ *
22
+ * Usage:
23
+ * liku find "Button Text"
24
+ * liku find "Save" --type Button
25
+ * liku find "*" --type Edit --window "Notepad"
26
+ */
27
+ async function run(args, options) {
28
+ if (args.length === 0) {
29
+ error('Usage: liku find <text> [--type <ControlType>] [--window <title>]');
30
+ return { success: false };
31
+ }
32
+
33
+ loadUI();
34
+ const searchText = args.join(' ');
35
+
36
+ if (!options.quiet) {
37
+ info(`Searching for elements matching "${searchText}"...`);
38
+ }
39
+
40
+ const criteria = {};
41
+
42
+ // Handle wildcard search (find all of a type)
43
+ if (searchText !== '*') {
44
+ criteria.text = searchText;
45
+ }
46
+
47
+ if (options.type) {
48
+ criteria.controlType = options.type;
49
+ }
50
+
51
+ if (options.window) {
52
+ criteria.windowTitle = options.window;
53
+ }
54
+
55
+ if (options.id) {
56
+ criteria.automationId = options.id;
57
+ }
58
+
59
+ if (options.class) {
60
+ criteria.className = options.class;
61
+ }
62
+
63
+ const result = await ui.findElements(criteria);
64
+
65
+ if (!result.success) {
66
+ error(`Search failed: ${result.error}`);
67
+ return { success: false, error: result.error };
68
+ }
69
+
70
+ if (result.count === 0) {
71
+ if (!options.quiet) {
72
+ info('No elements found matching criteria');
73
+ }
74
+ return { success: true, elements: [], count: 0 };
75
+ }
76
+
77
+ if (!options.quiet && !options.json) {
78
+ success(`Found ${result.count} element(s):\n`);
79
+
80
+ // Display as table
81
+ const rows = result.elements.map((el, i) => [
82
+ i + 1,
83
+ el.name?.substring(0, 40) || dim('(unnamed)'),
84
+ el.controlType || '-',
85
+ el.bounds ? `${el.bounds.x},${el.bounds.y}` : '-',
86
+ el.bounds ? `${el.bounds.width}x${el.bounds.height}` : '-',
87
+ ]);
88
+
89
+ table(rows, ['#', 'Name', 'Type', 'Position', 'Size']);
90
+
91
+ // Show automation IDs if verbose
92
+ if (options.verbose) {
93
+ console.log('\n' + dim('Automation IDs:'));
94
+ result.elements.forEach((el, i) => {
95
+ if (el.automationId) {
96
+ console.log(` ${i + 1}. ${highlight(el.automationId)}`);
97
+ }
98
+ });
99
+ }
100
+ }
101
+
102
+ return {
103
+ success: true,
104
+ elements: result.elements,
105
+ count: result.count,
106
+ };
107
+ }
108
+
109
+ module.exports = { run };
@@ -0,0 +1,132 @@
1
+ /**
2
+ * keys command - Send keyboard shortcuts
3
+ * @module cli/commands/keys
4
+ */
5
+
6
+ const path = require('path');
7
+ const { success, error, info } = require('../util/output');
8
+
9
+ const UI_MODULE = path.resolve(__dirname, '../../main/ui-automation');
10
+ let ui;
11
+
12
+ function loadUI() {
13
+ if (!ui) {
14
+ ui = require(UI_MODULE);
15
+ }
16
+ return ui;
17
+ }
18
+
19
+ /**
20
+ * Convert human-readable key combo to SendKeys format
21
+ *
22
+ * Examples:
23
+ * ctrl+c → ^c
24
+ * ctrl+shift+s → ^+s
25
+ * alt+f4 → %{F4}
26
+ * enter → {ENTER}
27
+ */
28
+ function toSendKeys(combo) {
29
+ // Already in SendKeys format
30
+ if (combo.includes('{') || combo.includes('^') || combo.includes('%') || combo.includes('+')) {
31
+ return combo;
32
+ }
33
+
34
+ const parts = combo.toLowerCase().split(/[+\-]/);
35
+ let modifiers = '';
36
+ let key = '';
37
+
38
+ for (const part of parts) {
39
+ const trimmed = part.trim();
40
+ switch (trimmed) {
41
+ case 'ctrl':
42
+ case 'control':
43
+ modifiers += '^';
44
+ break;
45
+ case 'alt':
46
+ modifiers += '%';
47
+ break;
48
+ case 'shift':
49
+ modifiers += '+';
50
+ break;
51
+ case 'win':
52
+ case 'windows':
53
+ case 'meta':
54
+ // Windows key - use PowerShell workaround
55
+ modifiers += '#';
56
+ break;
57
+ default:
58
+ key = trimmed;
59
+ }
60
+ }
61
+
62
+ // Special keys
63
+ const specialKeys = {
64
+ 'enter': '{ENTER}',
65
+ 'return': '{ENTER}',
66
+ 'tab': '{TAB}',
67
+ 'esc': '{ESC}',
68
+ 'escape': '{ESC}',
69
+ 'space': ' ',
70
+ 'backspace': '{BACKSPACE}',
71
+ 'delete': '{DELETE}',
72
+ 'del': '{DELETE}',
73
+ 'insert': '{INSERT}',
74
+ 'ins': '{INSERT}',
75
+ 'home': '{HOME}',
76
+ 'end': '{END}',
77
+ 'pageup': '{PGUP}',
78
+ 'pgup': '{PGUP}',
79
+ 'pagedown': '{PGDN}',
80
+ 'pgdn': '{PGDN}',
81
+ 'up': '{UP}',
82
+ 'down': '{DOWN}',
83
+ 'left': '{LEFT}',
84
+ 'right': '{RIGHT}',
85
+ 'f1': '{F1}', 'f2': '{F2}', 'f3': '{F3}', 'f4': '{F4}',
86
+ 'f5': '{F5}', 'f6': '{F6}', 'f7': '{F7}', 'f8': '{F8}',
87
+ 'f9': '{F9}', 'f10': '{F10}', 'f11': '{F11}', 'f12': '{F12}',
88
+ };
89
+
90
+ const finalKey = specialKeys[key] || key;
91
+
92
+ return modifiers + finalKey;
93
+ }
94
+
95
+ /**
96
+ * Run the keys command
97
+ *
98
+ * Usage:
99
+ * liku keys ctrl+c
100
+ * liku keys ctrl+shift+s
101
+ * liku keys enter
102
+ * liku keys "^c" (raw SendKeys format)
103
+ */
104
+ async function run(args, options) {
105
+ if (args.length === 0) {
106
+ error('Usage: liku keys <combo>');
107
+ info('Examples: ctrl+c, ctrl+shift+s, alt+f4, enter, tab');
108
+ return { success: false };
109
+ }
110
+
111
+ loadUI();
112
+ const combo = args.join(' ');
113
+ const sendKeysFormat = toSendKeys(combo);
114
+
115
+ if (!options.quiet) {
116
+ info(`Sending keys: ${combo} (${sendKeysFormat})`);
117
+ }
118
+
119
+ const result = await ui.sendKeys(sendKeysFormat);
120
+
121
+ if (result.success) {
122
+ if (!options.quiet) {
123
+ success(`Sent: ${combo}`);
124
+ }
125
+ return { success: true, keys: sendKeysFormat };
126
+ } else {
127
+ error(`Keys failed: ${result.error || 'Unknown error'}`);
128
+ return { success: false, error: result.error };
129
+ }
130
+ }
131
+
132
+ module.exports = { run };
@@ -0,0 +1,79 @@
1
+ /**
2
+ * mouse command - Move mouse to coordinates
3
+ * @module cli/commands/mouse
4
+ */
5
+
6
+ const path = require('path');
7
+ const { success, error, info } = require('../util/output');
8
+
9
+ const UI_MODULE = path.resolve(__dirname, '../../main/ui-automation');
10
+ let ui;
11
+
12
+ function loadUI() {
13
+ if (!ui) {
14
+ ui = require(UI_MODULE);
15
+ }
16
+ return ui;
17
+ }
18
+
19
+ /**
20
+ * Run the mouse command
21
+ *
22
+ * Usage:
23
+ * liku mouse 500 300 # Move to coordinates
24
+ * liku mouse --pos # Show current position
25
+ */
26
+ async function run(args, options) {
27
+ loadUI();
28
+
29
+ // Show current position
30
+ if (options.pos || options.position || args.length === 0) {
31
+ const pos = await ui.getMousePosition();
32
+
33
+ if (!options.quiet && !options.json) {
34
+ console.log(`Mouse position: (${pos.x}, ${pos.y})`);
35
+ }
36
+
37
+ return { success: true, x: pos.x, y: pos.y };
38
+ }
39
+
40
+ // Parse coordinates
41
+ let x, y;
42
+
43
+ if (args.length === 1 && args[0].includes(',')) {
44
+ // Format: "500,300"
45
+ const parts = args[0].split(',');
46
+ x = parseInt(parts[0], 10);
47
+ y = parseInt(parts[1], 10);
48
+ } else if (args.length >= 2) {
49
+ // Format: "500 300"
50
+ x = parseInt(args[0], 10);
51
+ y = parseInt(args[1], 10);
52
+ } else {
53
+ error('Usage: liku mouse <x> <y>');
54
+ return { success: false };
55
+ }
56
+
57
+ if (isNaN(x) || isNaN(y)) {
58
+ error('Invalid coordinates. Use numbers like: liku mouse 500 300');
59
+ return { success: false };
60
+ }
61
+
62
+ if (!options.quiet) {
63
+ info(`Moving mouse to (${x}, ${y})...`);
64
+ }
65
+
66
+ const result = await ui.moveMouse(x, y);
67
+
68
+ if (result.success) {
69
+ if (!options.quiet) {
70
+ success(`Mouse moved to (${x}, ${y})`);
71
+ }
72
+ return { success: true, x, y };
73
+ } else {
74
+ error(`Move failed: ${result.error || 'Unknown error'}`);
75
+ return { success: false, error: result.error };
76
+ }
77
+ }
78
+
79
+ module.exports = { run };