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,104 @@
1
+ /**
2
+ * window command - Window management
3
+ * @module cli/commands/window
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 window command
21
+ *
22
+ * Usage:
23
+ * liku window # List all windows
24
+ * liku window "Visual Studio" # Focus window by title
25
+ * liku window --active # Show active window info
26
+ */
27
+ async function run(args, options) {
28
+ loadUI();
29
+
30
+ // Show active window info
31
+ if (options.active) {
32
+ const win = await ui.getActiveWindow();
33
+ if (!win) {
34
+ error('Could not get active window');
35
+ return { success: false };
36
+ }
37
+
38
+ if (!options.quiet && !options.json) {
39
+ const bounds = win.bounds || { x: '?', y: '?', width: '?', height: '?' };
40
+ console.log(`
41
+ ${highlight('Active Window:')}
42
+ Title: ${win.title || '(unknown)'}
43
+ Process: ${win.processName || '(unknown)'}
44
+ Class: ${win.className || '(unknown)'}
45
+ Handle: ${win.hwnd}
46
+ Position: ${bounds.x}, ${bounds.y}
47
+ Size: ${bounds.width} x ${bounds.height}
48
+ `);
49
+ }
50
+ return { success: true, window: win };
51
+ }
52
+
53
+ // Focus window by title
54
+ if (args.length > 0) {
55
+ const title = args.join(' ');
56
+
57
+ if (!options.quiet) {
58
+ info(`Focusing window: "${title}"`);
59
+ }
60
+
61
+ const result = await ui.focusWindow({ title });
62
+
63
+ if (result.success) {
64
+ if (!options.quiet) {
65
+ success(`Focused: ${result.window?.title || title}`);
66
+ }
67
+ return { success: true, window: result.window };
68
+ } else {
69
+ error(`Window not found: "${title}"`);
70
+ return { success: false, error: 'Window not found' };
71
+ }
72
+ }
73
+
74
+ // List all windows
75
+ if (!options.quiet) {
76
+ info('Listing windows...');
77
+ }
78
+
79
+ const windows = await ui.findWindows({});
80
+
81
+ if (windows.length === 0) {
82
+ if (!options.quiet) {
83
+ info('No windows found');
84
+ }
85
+ return { success: true, windows: [], count: 0 };
86
+ }
87
+
88
+ if (!options.quiet && !options.json) {
89
+ console.log(`\n${highlight(`Found ${windows.length} windows:`)}\n`);
90
+
91
+ const rows = windows.map((w, i) => [
92
+ i + 1,
93
+ w.title?.substring(0, 50) || dim('(untitled)'),
94
+ w.processName || '-',
95
+ `${w.bounds.width}x${w.bounds.height}`,
96
+ ]);
97
+
98
+ table(rows, ['#', 'Title', 'Process', 'Size']);
99
+ }
100
+
101
+ return { success: true, windows, count: windows.length };
102
+ }
103
+
104
+ module.exports = { run };
@@ -0,0 +1,249 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * liku - Copilot-Liku CLI
4
+ *
5
+ * A powerful command-line interface for UI automation and the Copilot-Liku agent.
6
+ *
7
+ * Usage:
8
+ * liku Start the Electron agent (visual mode)
9
+ * liku start Same as above
10
+ * liku click <text> Click element by text
11
+ * liku find <text> Find UI elements matching text
12
+ * liku type <text> Type text at cursor
13
+ * liku keys <combo> Send key combination (e.g., "ctrl+c")
14
+ * liku screenshot [path] Take screenshot
15
+ * liku window <title> Focus window by title
16
+ * liku mouse <x> <y> Move mouse to coordinates
17
+ * liku repl Interactive automation shell
18
+ * liku --help Show help
19
+ * liku --version Show version
20
+ */
21
+
22
+ const path = require('path');
23
+ const fs = require('fs');
24
+
25
+ // Resolve paths relative to CLI location
26
+ const CLI_DIR = __dirname;
27
+ const PROJECT_ROOT = path.resolve(CLI_DIR, '../..');
28
+ const COMMANDS_DIR = path.join(CLI_DIR, 'commands');
29
+
30
+ // Import output utilities
31
+ const { log, success, error, warn, info, dim, highlight } = require('./util/output');
32
+
33
+ // Package info
34
+ const pkg = require(path.join(PROJECT_ROOT, 'package.json'));
35
+
36
+ // Command registry
37
+ const COMMANDS = {
38
+ start: { desc: 'Start the Electron agent with overlay', file: 'start' },
39
+ click: { desc: 'Click element by text or coordinates', file: 'click', args: '<text|x,y>' },
40
+ find: { desc: 'Find UI elements matching criteria', file: 'find', args: '<text>' },
41
+ type: { desc: 'Type text at current cursor position', file: 'type', args: '<text>' },
42
+ keys: { desc: 'Send keyboard shortcut', file: 'keys', args: '<combo>' },
43
+ screenshot: { desc: 'Capture screenshot', file: 'screenshot', args: '[path]' },
44
+ window: { desc: 'Focus or list windows', file: 'window', args: '[title]' },
45
+ mouse: { desc: 'Move mouse to coordinates', file: 'mouse', args: '<x> <y>' },
46
+ drag: { desc: 'Drag from one point to another', file: 'drag', args: '<x1> <y1> <x2> <y2>' },
47
+ scroll: { desc: 'Scroll up or down', file: 'scroll', args: '<up|down> [amount]' },
48
+ wait: { desc: 'Wait for element to appear', file: 'wait', args: '<text> [timeout]' },
49
+ repl: { desc: 'Interactive automation shell', file: 'repl' },
50
+ };
51
+
52
+ /**
53
+ * Show help message
54
+ */
55
+ function showHelp() {
56
+ console.log(`
57
+ ${highlight('liku')} - Copilot-Liku CLI v${pkg.version}
58
+ ${dim('A powerful command-line interface for UI automation')}
59
+
60
+ ${highlight('USAGE:')}
61
+ liku [command] [options]
62
+
63
+ ${highlight('COMMANDS:')}
64
+ `);
65
+
66
+ // Calculate padding for alignment
67
+ const maxLen = Math.max(...Object.keys(COMMANDS).map(k => k.length + (COMMANDS[k].args?.length || 0)));
68
+
69
+ for (const [name, cmd] of Object.entries(COMMANDS)) {
70
+ const cmdStr = cmd.args ? `${name} ${cmd.args}` : name;
71
+ const padding = ' '.repeat(maxLen - cmdStr.length + 4);
72
+ console.log(` ${highlight(cmdStr)}${padding}${dim(cmd.desc)}`);
73
+ }
74
+
75
+ console.log(`
76
+ ${highlight('OPTIONS:')}
77
+ --help, -h Show this help message
78
+ --version, -v Show version
79
+ --json Output results as JSON (for scripting)
80
+ --quiet, -q Suppress non-essential output
81
+
82
+ ${highlight('EXAMPLES:')}
83
+ ${dim('# Start the visual agent')}
84
+ liku start
85
+
86
+ ${dim('# Click a button by text')}
87
+ liku click "Submit"
88
+
89
+ ${dim('# Find all buttons with "Save" in their text')}
90
+ liku find "Save" --type Button
91
+
92
+ ${dim('# Type text')}
93
+ liku type "Hello, World!"
94
+
95
+ ${dim('# Send keyboard shortcut')}
96
+ liku keys ctrl+shift+s
97
+
98
+ ${dim('# Take a screenshot')}
99
+ liku screenshot ./capture.png
100
+
101
+ ${dim('# Focus VS Code window')}
102
+ liku window "Visual Studio Code"
103
+
104
+ ${dim('# Interactive mode')}
105
+ liku repl
106
+
107
+ ${highlight('ENVIRONMENT:')}
108
+ LIKU_DEBUG=1 Enable debug output
109
+ LIKU_JSON=1 Default to JSON output
110
+
111
+ ${dim('Documentation: https://github.com/TayDa64/copilot-Liku-cli')}
112
+ `);
113
+ }
114
+
115
+ /**
116
+ * Show version
117
+ */
118
+ function showVersion() {
119
+ console.log(`liku v${pkg.version}`);
120
+ }
121
+
122
+ /**
123
+ * Parse command-line arguments
124
+ */
125
+ function parseArgs(argv) {
126
+ const args = argv.slice(2);
127
+ const result = {
128
+ command: null,
129
+ args: [],
130
+ flags: {
131
+ help: false,
132
+ version: false,
133
+ json: false,
134
+ quiet: false,
135
+ debug: process.env.LIKU_DEBUG === '1',
136
+ },
137
+ options: {},
138
+ };
139
+
140
+ let i = 0;
141
+ while (i < args.length) {
142
+ const arg = args[i];
143
+
144
+ if (arg === '--help' || arg === '-h') {
145
+ result.flags.help = true;
146
+ } else if (arg === '--version' || arg === '-v') {
147
+ result.flags.version = true;
148
+ } else if (arg === '--json') {
149
+ result.flags.json = true;
150
+ } else if (arg === '--quiet' || arg === '-q') {
151
+ result.flags.quiet = true;
152
+ } else if (arg === '--debug') {
153
+ result.flags.debug = true;
154
+ } else if (arg.startsWith('--')) {
155
+ // Named option (--key=value or --key value)
156
+ const [key, val] = arg.slice(2).split('=');
157
+ if (val !== undefined) {
158
+ result.options[key] = val;
159
+ } else if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
160
+ result.options[key] = args[++i];
161
+ } else {
162
+ result.options[key] = true;
163
+ }
164
+ } else if (!result.command) {
165
+ result.command = arg;
166
+ } else {
167
+ result.args.push(arg);
168
+ }
169
+ i++;
170
+ }
171
+
172
+ // Default JSON from env
173
+ if (process.env.LIKU_JSON === '1') {
174
+ result.flags.json = true;
175
+ }
176
+
177
+ return result;
178
+ }
179
+
180
+ /**
181
+ * Load and execute a command module
182
+ */
183
+ async function executeCommand(name, cmdArgs, flags, options) {
184
+ const cmdInfo = COMMANDS[name];
185
+ if (!cmdInfo) {
186
+ error(`Unknown command: ${name}`);
187
+ console.log(`\nRun ${highlight('liku --help')} for available commands.`);
188
+ process.exit(1);
189
+ }
190
+
191
+ const cmdPath = path.join(COMMANDS_DIR, `${cmdInfo.file}.js`);
192
+
193
+ if (!fs.existsSync(cmdPath)) {
194
+ error(`Command module not found: ${cmdPath}`);
195
+ process.exit(1);
196
+ }
197
+
198
+ try {
199
+ const command = require(cmdPath);
200
+ const result = await command.run(cmdArgs, { ...flags, ...options });
201
+
202
+ // Output result
203
+ if (flags.json && result !== undefined) {
204
+ console.log(JSON.stringify(result, null, 2));
205
+ }
206
+
207
+ // Exit with appropriate code
208
+ if (result && result.success === false) {
209
+ process.exit(1);
210
+ }
211
+ } catch (err) {
212
+ if (flags.debug) {
213
+ console.error(err);
214
+ } else {
215
+ error(err.message);
216
+ }
217
+ process.exit(1);
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Main entry point
223
+ */
224
+ async function main() {
225
+ const { command, args, flags, options } = parseArgs(process.argv);
226
+
227
+ // Handle global flags
228
+ if (flags.version) {
229
+ showVersion();
230
+ return;
231
+ }
232
+
233
+ if (flags.help || (!command && args.length === 0)) {
234
+ showHelp();
235
+ return;
236
+ }
237
+
238
+ // Default command is 'start' (launch Electron)
239
+ const cmd = command || 'start';
240
+
241
+ // Execute the command
242
+ await executeCommand(cmd, args, flags, options);
243
+ }
244
+
245
+ // Run
246
+ main().catch(err => {
247
+ error(err.message);
248
+ process.exit(1);
249
+ });
@@ -0,0 +1,174 @@
1
+ /**
2
+ * CLI Output Utilities
3
+ *
4
+ * Colored console output helpers for the liku CLI.
5
+ * @module cli/util/output
6
+ */
7
+
8
+ // ANSI color codes
9
+ const COLORS = {
10
+ reset: '\x1b[0m',
11
+ bright: '\x1b[1m',
12
+ dim: '\x1b[2m',
13
+ red: '\x1b[31m',
14
+ green: '\x1b[32m',
15
+ yellow: '\x1b[33m',
16
+ blue: '\x1b[34m',
17
+ magenta: '\x1b[35m',
18
+ cyan: '\x1b[36m',
19
+ white: '\x1b[37m',
20
+ };
21
+
22
+ // Check if colors are supported
23
+ const supportsColor = process.stdout.isTTY && !process.env.NO_COLOR;
24
+
25
+ function colorize(color, text) {
26
+ if (!supportsColor) return text;
27
+ return `${color}${text}${COLORS.reset}`;
28
+ }
29
+
30
+ /**
31
+ * Log a message (no prefix)
32
+ */
33
+ function log(message) {
34
+ console.log(message);
35
+ }
36
+
37
+ /**
38
+ * Success message (green checkmark)
39
+ */
40
+ function success(message) {
41
+ console.log(colorize(COLORS.green, '✓ ') + message);
42
+ }
43
+
44
+ /**
45
+ * Error message (red X)
46
+ */
47
+ function error(message) {
48
+ console.error(colorize(COLORS.red, '✗ ') + message);
49
+ }
50
+
51
+ /**
52
+ * Warning message (yellow)
53
+ */
54
+ function warn(message) {
55
+ console.log(colorize(COLORS.yellow, '⚠ ') + message);
56
+ }
57
+
58
+ /**
59
+ * Info message (blue)
60
+ */
61
+ function info(message) {
62
+ console.log(colorize(COLORS.blue, 'ℹ ') + message);
63
+ }
64
+
65
+ /**
66
+ * Dim text (muted)
67
+ */
68
+ function dim(text) {
69
+ return colorize(COLORS.dim, text);
70
+ }
71
+
72
+ /**
73
+ * Highlight text (cyan/bright)
74
+ */
75
+ function highlight(text) {
76
+ return colorize(COLORS.cyan, text);
77
+ }
78
+
79
+ /**
80
+ * Bold text
81
+ */
82
+ function bold(text) {
83
+ return colorize(COLORS.bright, text);
84
+ }
85
+
86
+ /**
87
+ * Format a table of data
88
+ */
89
+ function table(rows, headers = null) {
90
+ if (rows.length === 0) return;
91
+
92
+ // Calculate column widths
93
+ const allRows = headers ? [headers, ...rows] : rows;
94
+ const colCount = Math.max(...allRows.map(r => r.length));
95
+ const colWidths = [];
96
+
97
+ for (let i = 0; i < colCount; i++) {
98
+ colWidths[i] = Math.max(...allRows.map(r => String(r[i] || '').length));
99
+ }
100
+
101
+ // Print headers
102
+ if (headers) {
103
+ const headerLine = headers.map((h, i) => String(h).padEnd(colWidths[i])).join(' ');
104
+ console.log(bold(headerLine));
105
+ console.log(dim('-'.repeat(headerLine.length)));
106
+ }
107
+
108
+ // Print rows
109
+ for (const row of rows) {
110
+ const line = row.map((cell, i) => String(cell || '').padEnd(colWidths[i])).join(' ');
111
+ console.log(line);
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Spinner for long-running operations
117
+ */
118
+ class Spinner {
119
+ constructor(message) {
120
+ this.message = message;
121
+ this.frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
122
+ this.frameIndex = 0;
123
+ this.interval = null;
124
+ }
125
+
126
+ start() {
127
+ if (!supportsColor) {
128
+ console.log(this.message + '...');
129
+ return;
130
+ }
131
+
132
+ process.stdout.write(this.message + ' ');
133
+ this.interval = setInterval(() => {
134
+ process.stdout.write(`\r${this.message} ${this.frames[this.frameIndex]}`);
135
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
136
+ }, 80);
137
+ }
138
+
139
+ stop(finalMessage = null) {
140
+ if (this.interval) {
141
+ clearInterval(this.interval);
142
+ this.interval = null;
143
+ }
144
+ if (supportsColor) {
145
+ process.stdout.write('\r' + ' '.repeat(this.message.length + 10) + '\r');
146
+ }
147
+ if (finalMessage) {
148
+ console.log(finalMessage);
149
+ }
150
+ }
151
+
152
+ succeed(message) {
153
+ this.stop(colorize(COLORS.green, '✓ ') + (message || this.message));
154
+ }
155
+
156
+ fail(message) {
157
+ this.stop(colorize(COLORS.red, '✗ ') + (message || this.message));
158
+ }
159
+ }
160
+
161
+ module.exports = {
162
+ log,
163
+ success,
164
+ error,
165
+ warn,
166
+ info,
167
+ dim,
168
+ highlight,
169
+ bold,
170
+ table,
171
+ Spinner,
172
+ COLORS,
173
+ colorize,
174
+ };