codeep 1.2.17 → 1.2.19
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/README.md +20 -7
- package/dist/api/index.d.ts +7 -0
- package/dist/api/index.js +21 -17
- package/dist/config/providers.d.ts +6 -0
- package/dist/config/providers.js +11 -0
- package/dist/renderer/App.d.ts +1 -5
- package/dist/renderer/App.js +106 -486
- package/dist/renderer/agentExecution.d.ts +36 -0
- package/dist/renderer/agentExecution.js +394 -0
- package/dist/renderer/commands.d.ts +16 -0
- package/dist/renderer/commands.js +838 -0
- package/dist/renderer/handlers.d.ts +87 -0
- package/dist/renderer/handlers.js +260 -0
- package/dist/renderer/highlight.d.ts +18 -0
- package/dist/renderer/highlight.js +130 -0
- package/dist/renderer/main.d.ts +4 -2
- package/dist/renderer/main.js +103 -1550
- package/dist/utils/agent.d.ts +5 -15
- package/dist/utils/agent.js +9 -693
- package/dist/utils/agentChat.d.ts +46 -0
- package/dist/utils/agentChat.js +343 -0
- package/dist/utils/agentStream.d.ts +23 -0
- package/dist/utils/agentStream.js +216 -0
- package/dist/utils/keychain.js +3 -2
- package/dist/utils/learning.js +9 -3
- package/dist/utils/mcpIntegration.d.ts +61 -0
- package/dist/utils/mcpIntegration.js +154 -0
- package/dist/utils/project.js +8 -3
- package/dist/utils/skills.js +21 -11
- package/dist/utils/smartContext.d.ts +4 -0
- package/dist/utils/smartContext.js +51 -14
- package/dist/utils/toolExecution.d.ts +27 -0
- package/dist/utils/toolExecution.js +525 -0
- package/dist/utils/toolParsing.d.ts +18 -0
- package/dist/utils/toolParsing.js +302 -0
- package/dist/utils/tools.d.ts +11 -24
- package/dist/utils/tools.js +22 -1187
- package/package.json +3 -1
- package/dist/config/config.test.d.ts +0 -1
- package/dist/config/config.test.js +0 -157
- package/dist/config/providers.test.d.ts +0 -1
- package/dist/config/providers.test.js +0 -187
- package/dist/hooks/index.d.ts +0 -4
- package/dist/hooks/index.js +0 -4
- package/dist/hooks/useAgent.d.ts +0 -29
- package/dist/hooks/useAgent.js +0 -148
- package/dist/utils/agent.test.d.ts +0 -1
- package/dist/utils/agent.test.js +0 -315
- package/dist/utils/git.test.d.ts +0 -1
- package/dist/utils/git.test.js +0 -193
- package/dist/utils/gitignore.test.d.ts +0 -1
- package/dist/utils/gitignore.test.js +0 -167
- package/dist/utils/project.test.d.ts +0 -1
- package/dist/utils/project.test.js +0 -212
- package/dist/utils/ratelimit.test.d.ts +0 -1
- package/dist/utils/ratelimit.test.js +0 -131
- package/dist/utils/retry.test.d.ts +0 -1
- package/dist/utils/retry.test.js +0 -163
- package/dist/utils/smartContext.test.d.ts +0 -1
- package/dist/utils/smartContext.test.js +0 -382
- package/dist/utils/tools.test.d.ts +0 -1
- package/dist/utils/tools.test.js +0 -681
- package/dist/utils/validation.test.d.ts +0 -1
- package/dist/utils/validation.test.js +0 -164
package/dist/renderer/App.js
CHANGED
|
@@ -5,157 +5,11 @@
|
|
|
5
5
|
import { Screen } from './Screen.js';
|
|
6
6
|
import { Input, LineEditor } from './Input.js';
|
|
7
7
|
import { fg, style, stringWidth } from './ansi.js';
|
|
8
|
+
import { SYNTAX, highlightCode } from './highlight.js';
|
|
9
|
+
import { handleInlineStatusKey, handleInlineHelpKey, handleMenuKey, handleInlinePermissionKey, handleInlineSessionPickerKey, handleInlineConfirmKey, handleLoginKey, } from './handlers.js';
|
|
8
10
|
import clipboardy from 'clipboardy';
|
|
9
|
-
import { spawn } from 'child_process';
|
|
10
11
|
// Primary color: #f02a30 (Codeep red)
|
|
11
12
|
const PRIMARY_COLOR = fg.rgb(240, 42, 48);
|
|
12
|
-
// Syntax highlighting colors (One Dark theme inspired)
|
|
13
|
-
const SYNTAX = {
|
|
14
|
-
keyword: fg.rgb(198, 120, 221), // Purple - keywords
|
|
15
|
-
string: fg.rgb(152, 195, 121), // Green - strings
|
|
16
|
-
number: fg.rgb(209, 154, 102), // Orange - numbers
|
|
17
|
-
comment: fg.rgb(92, 99, 112), // Gray - comments
|
|
18
|
-
function: fg.rgb(97, 175, 239), // Blue - functions
|
|
19
|
-
type: fg.rgb(229, 192, 123), // Yellow - types
|
|
20
|
-
operator: fg.rgb(86, 182, 194), // Cyan - operators
|
|
21
|
-
variable: fg.white, // White - variables
|
|
22
|
-
punctuation: fg.gray, // Gray - punctuation
|
|
23
|
-
codeFrame: fg.rgb(100, 105, 115), // Frame color
|
|
24
|
-
codeLang: fg.rgb(150, 155, 165), // Language label
|
|
25
|
-
};
|
|
26
|
-
// Keywords for different languages
|
|
27
|
-
const KEYWORDS = {
|
|
28
|
-
js: ['const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while', 'do', 'switch', 'case', 'break', 'continue', 'try', 'catch', 'throw', 'finally', 'new', 'class', 'extends', 'import', 'export', 'from', 'default', 'async', 'await', 'yield', 'typeof', 'instanceof', 'in', 'of', 'delete', 'void', 'this', 'super', 'null', 'undefined', 'true', 'false', 'NaN', 'Infinity'],
|
|
29
|
-
ts: ['const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while', 'do', 'switch', 'case', 'break', 'continue', 'try', 'catch', 'throw', 'finally', 'new', 'class', 'extends', 'import', 'export', 'from', 'default', 'async', 'await', 'yield', 'typeof', 'instanceof', 'in', 'of', 'delete', 'void', 'this', 'super', 'null', 'undefined', 'true', 'false', 'type', 'interface', 'enum', 'namespace', 'module', 'declare', 'abstract', 'implements', 'private', 'public', 'protected', 'readonly', 'static', 'as', 'is', 'keyof', 'infer', 'never', 'unknown', 'any'],
|
|
30
|
-
py: ['def', 'class', 'return', 'if', 'elif', 'else', 'for', 'while', 'try', 'except', 'finally', 'raise', 'import', 'from', 'as', 'with', 'yield', 'lambda', 'pass', 'break', 'continue', 'and', 'or', 'not', 'in', 'is', 'None', 'True', 'False', 'global', 'nonlocal', 'assert', 'del', 'async', 'await'],
|
|
31
|
-
go: ['func', 'return', 'if', 'else', 'for', 'range', 'switch', 'case', 'break', 'continue', 'fallthrough', 'default', 'go', 'select', 'chan', 'defer', 'panic', 'recover', 'type', 'struct', 'interface', 'map', 'package', 'import', 'const', 'var', 'nil', 'true', 'false', 'iota', 'make', 'new', 'append', 'len', 'cap', 'copy', 'delete'],
|
|
32
|
-
rust: ['fn', 'let', 'mut', 'const', 'static', 'return', 'if', 'else', 'match', 'for', 'while', 'loop', 'break', 'continue', 'struct', 'enum', 'trait', 'impl', 'type', 'where', 'use', 'mod', 'pub', 'crate', 'self', 'super', 'async', 'await', 'move', 'ref', 'true', 'false', 'Some', 'None', 'Ok', 'Err', 'Self', 'dyn', 'unsafe', 'extern'],
|
|
33
|
-
sh: ['if', 'then', 'else', 'elif', 'fi', 'case', 'esac', 'for', 'while', 'until', 'do', 'done', 'in', 'function', 'return', 'local', 'export', 'readonly', 'declare', 'typeset', 'unset', 'shift', 'exit', 'break', 'continue', 'source', 'alias', 'echo', 'printf', 'read', 'test', 'true', 'false'],
|
|
34
|
-
html: ['html', 'head', 'body', 'div', 'span', 'p', 'a', 'img', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'table', 'tr', 'td', 'th', 'form', 'input', 'button', 'select', 'option', 'textarea', 'label', 'section', 'article', 'nav', 'header', 'footer', 'main', 'aside', 'meta', 'link', 'script', 'style', 'title', 'DOCTYPE'],
|
|
35
|
-
css: ['import', 'media', 'keyframes', 'font-face', 'supports', 'charset', 'namespace', 'page', 'inherit', 'initial', 'unset', 'none', 'auto', 'block', 'inline', 'flex', 'grid', 'absolute', 'relative', 'fixed', 'sticky', 'static', 'hidden', 'visible', 'solid', 'dashed', 'dotted', 'transparent', 'important'],
|
|
36
|
-
};
|
|
37
|
-
// Map language aliases
|
|
38
|
-
const LANG_ALIASES = {
|
|
39
|
-
javascript: 'js', typescript: 'ts', python: 'py', golang: 'go',
|
|
40
|
-
bash: 'sh', shell: 'sh', zsh: 'sh', tsx: 'ts', jsx: 'js',
|
|
41
|
-
htm: 'html', scss: 'css', sass: 'css', less: 'css',
|
|
42
|
-
};
|
|
43
|
-
/**
|
|
44
|
-
* Syntax highlighter for code with better token handling
|
|
45
|
-
*/
|
|
46
|
-
function highlightCode(code, lang) {
|
|
47
|
-
const normalizedLang = LANG_ALIASES[lang.toLowerCase()] || lang.toLowerCase();
|
|
48
|
-
const keywords = KEYWORDS[normalizedLang] || KEYWORDS['js'] || [];
|
|
49
|
-
// HTML: highlight tags, attributes, and values
|
|
50
|
-
if (normalizedLang === 'html' || normalizedLang === 'xml' || normalizedLang === 'svg') {
|
|
51
|
-
return code.replace(/(<\/?)(\w[\w-]*)((?:\s+[\w-]+(?:=(?:"[^"]*"|'[^']*'|\S+))?)*)(\s*\/?>)/g, (_match, open, tag, attrs, close) => {
|
|
52
|
-
const highlightedAttrs = attrs.replace(/([\w-]+)(=)("[^"]*"|'[^']*')/g, (_m, attr, eq, val) => SYNTAX.function + attr + '\x1b[0m' + SYNTAX.operator + eq + '\x1b[0m' + SYNTAX.string + val + '\x1b[0m');
|
|
53
|
-
return SYNTAX.punctuation + open + '\x1b[0m' + SYNTAX.keyword + tag + '\x1b[0m' + highlightedAttrs + SYNTAX.punctuation + close + '\x1b[0m';
|
|
54
|
-
}).replace(/<!--[\s\S]*?-->/g, (comment) => SYNTAX.comment + comment + '\x1b[0m');
|
|
55
|
-
}
|
|
56
|
-
// CSS: highlight selectors, properties, and values
|
|
57
|
-
if (normalizedLang === 'css') {
|
|
58
|
-
return code
|
|
59
|
-
.replace(/\/\*[\s\S]*?\*\//g, (comment) => SYNTAX.comment + comment + '\x1b[0m')
|
|
60
|
-
.replace(/([\w-]+)(\s*:\s*)([^;{}]+)/g, (_m, prop, colon, val) => SYNTAX.function + prop + '\x1b[0m' + colon + SYNTAX.string + val + '\x1b[0m')
|
|
61
|
-
.replace(/([.#]?[\w-]+(?:\s*[,>+~]\s*[.#]?[\w-]+)*)\s*\{/g, (match, selector) => SYNTAX.keyword + selector + '\x1b[0m' + ' {');
|
|
62
|
-
}
|
|
63
|
-
// Tokenize and highlight
|
|
64
|
-
let result = '';
|
|
65
|
-
let i = 0;
|
|
66
|
-
while (i < code.length) {
|
|
67
|
-
// Check for comments first
|
|
68
|
-
if (code.slice(i, i + 2) === '//' || (normalizedLang === 'py' && code[i] === '#') ||
|
|
69
|
-
(normalizedLang === 'sh' && code[i] === '#')) {
|
|
70
|
-
// Line comment - highlight rest of line
|
|
71
|
-
let end = code.indexOf('\n', i);
|
|
72
|
-
if (end === -1)
|
|
73
|
-
end = code.length;
|
|
74
|
-
result += SYNTAX.comment + code.slice(i, end) + '\x1b[0m';
|
|
75
|
-
i = end;
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
// Multi-line comment /*
|
|
79
|
-
if (code.slice(i, i + 2) === '/*') {
|
|
80
|
-
let end = code.indexOf('*/', i + 2);
|
|
81
|
-
if (end === -1)
|
|
82
|
-
end = code.length;
|
|
83
|
-
else
|
|
84
|
-
end += 2;
|
|
85
|
-
result += SYNTAX.comment + code.slice(i, end) + '\x1b[0m';
|
|
86
|
-
i = end;
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
// Strings
|
|
90
|
-
if (code[i] === '"' || code[i] === "'" || code[i] === '`') {
|
|
91
|
-
const quote = code[i];
|
|
92
|
-
let end = i + 1;
|
|
93
|
-
while (end < code.length) {
|
|
94
|
-
if (code[end] === '\\') {
|
|
95
|
-
end += 2; // Skip escaped char
|
|
96
|
-
}
|
|
97
|
-
else if (code[end] === quote) {
|
|
98
|
-
end++;
|
|
99
|
-
break;
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
end++;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
result += SYNTAX.string + code.slice(i, end) + '\x1b[0m';
|
|
106
|
-
i = end;
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
// Numbers (including hex, binary, floats)
|
|
110
|
-
const numMatch = code.slice(i).match(/^(0x[0-9a-fA-F]+|0b[01]+|0o[0-7]+|\d+\.?\d*(?:e[+-]?\d+)?)/);
|
|
111
|
-
if (numMatch && (i === 0 || !/[a-zA-Z_]/.test(code[i - 1]))) {
|
|
112
|
-
result += SYNTAX.number + numMatch[1] + '\x1b[0m';
|
|
113
|
-
i += numMatch[1].length;
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
// Identifiers (keywords, functions, variables)
|
|
117
|
-
const identMatch = code.slice(i).match(/^[a-zA-Z_][a-zA-Z0-9_]*/);
|
|
118
|
-
if (identMatch) {
|
|
119
|
-
const ident = identMatch[0];
|
|
120
|
-
const nextChar = code[i + ident.length];
|
|
121
|
-
if (keywords.includes(ident)) {
|
|
122
|
-
// Keyword
|
|
123
|
-
result += SYNTAX.keyword + ident + '\x1b[0m';
|
|
124
|
-
}
|
|
125
|
-
else if (nextChar === '(') {
|
|
126
|
-
// Function call
|
|
127
|
-
result += SYNTAX.function + ident + '\x1b[0m';
|
|
128
|
-
}
|
|
129
|
-
else if (ident[0] === ident[0].toUpperCase() && /^[A-Z]/.test(ident)) {
|
|
130
|
-
// Type/Class (PascalCase)
|
|
131
|
-
result += SYNTAX.type + ident + '\x1b[0m';
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
// Regular identifier
|
|
135
|
-
result += ident;
|
|
136
|
-
}
|
|
137
|
-
i += ident.length;
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
// Operators
|
|
141
|
-
const opMatch = code.slice(i).match(/^(===|!==|==|!=|<=|>=|=>|->|\+\+|--|&&|\|\||<<|>>|\+=|-=|\*=|\/=|[+\-*/%=<>!&|^~?:])/);
|
|
142
|
-
if (opMatch) {
|
|
143
|
-
result += SYNTAX.operator + opMatch[1] + '\x1b[0m';
|
|
144
|
-
i += opMatch[1].length;
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
// Punctuation
|
|
148
|
-
if ('{}[]();,.'.includes(code[i])) {
|
|
149
|
-
result += SYNTAX.punctuation + code[i] + '\x1b[0m';
|
|
150
|
-
i++;
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
// Default - just add the character
|
|
154
|
-
result += code[i];
|
|
155
|
-
i++;
|
|
156
|
-
}
|
|
157
|
-
return result;
|
|
158
|
-
}
|
|
159
13
|
// Spinner frames for animation
|
|
160
14
|
const SPINNER_FRAMES = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];
|
|
161
15
|
// ASCII Logo
|
|
@@ -1071,46 +925,21 @@ export class App {
|
|
|
1071
925
|
* Handle inline status keys
|
|
1072
926
|
*/
|
|
1073
927
|
handleInlineStatusKey(event) {
|
|
1074
|
-
|
|
1075
|
-
this.statusOpen = false;
|
|
1076
|
-
this.render()
|
|
1077
|
-
}
|
|
928
|
+
handleInlineStatusKey(event, {
|
|
929
|
+
close: () => { this.statusOpen = false; },
|
|
930
|
+
render: () => this.render(),
|
|
931
|
+
});
|
|
1078
932
|
}
|
|
1079
933
|
/**
|
|
1080
934
|
* Handle help screen keys
|
|
1081
935
|
*/
|
|
1082
936
|
handleInlineHelpKey(event) {
|
|
1083
|
-
|
|
1084
|
-
this.
|
|
1085
|
-
this.
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
let totalItems = 0;
|
|
1090
|
-
for (const cat of helpCategories) {
|
|
1091
|
-
totalItems += 1 + cat.items.length; // category header + items
|
|
1092
|
-
}
|
|
1093
|
-
totalItems += 1 + keyboardShortcuts.length; // shortcuts header + items
|
|
1094
|
-
if (event.key === 'down') {
|
|
1095
|
-
this.helpScrollIndex = Math.min(this.helpScrollIndex + 1, Math.max(0, totalItems - 5));
|
|
1096
|
-
this.render();
|
|
1097
|
-
return;
|
|
1098
|
-
}
|
|
1099
|
-
if (event.key === 'up') {
|
|
1100
|
-
this.helpScrollIndex = Math.max(0, this.helpScrollIndex - 1);
|
|
1101
|
-
this.render();
|
|
1102
|
-
return;
|
|
1103
|
-
}
|
|
1104
|
-
if (event.key === 'pagedown') {
|
|
1105
|
-
this.helpScrollIndex = Math.min(this.helpScrollIndex + 5, Math.max(0, totalItems - 5));
|
|
1106
|
-
this.render();
|
|
1107
|
-
return;
|
|
1108
|
-
}
|
|
1109
|
-
if (event.key === 'pageup') {
|
|
1110
|
-
this.helpScrollIndex = Math.max(0, this.helpScrollIndex - 5);
|
|
1111
|
-
this.render();
|
|
1112
|
-
return;
|
|
1113
|
-
}
|
|
937
|
+
handleInlineHelpKey(event, {
|
|
938
|
+
scrollIndex: this.helpScrollIndex,
|
|
939
|
+
setScrollIndex: (v) => { this.helpScrollIndex = v; },
|
|
940
|
+
close: () => { this.helpOpen = false; },
|
|
941
|
+
render: () => this.render(),
|
|
942
|
+
});
|
|
1114
943
|
}
|
|
1115
944
|
/**
|
|
1116
945
|
* Handle inline settings keys
|
|
@@ -1207,279 +1036,84 @@ export class App {
|
|
|
1207
1036
|
* Handle login keys
|
|
1208
1037
|
*/
|
|
1209
1038
|
handleLoginKey(event) {
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1039
|
+
handleLoginKey(event, {
|
|
1040
|
+
step: this.loginStep,
|
|
1041
|
+
providerIndex: this.loginProviderIndex,
|
|
1042
|
+
providers: this.loginProviders,
|
|
1043
|
+
apiKey: this.loginApiKey,
|
|
1044
|
+
setStep: (v) => { this.loginStep = v; },
|
|
1045
|
+
setProviderIndex: (v) => { this.loginProviderIndex = v; },
|
|
1046
|
+
setApiKey: (v) => { this.loginApiKey = v; },
|
|
1047
|
+
setError: (msg) => { this.loginError = msg; },
|
|
1048
|
+
close: (result) => {
|
|
1214
1049
|
const callback = this.loginCallback;
|
|
1215
|
-
this.loginCallback = null;
|
|
1216
|
-
this.render();
|
|
1217
|
-
if (callback)
|
|
1218
|
-
callback(null);
|
|
1219
|
-
return;
|
|
1220
|
-
}
|
|
1221
|
-
if (event.key === 'up') {
|
|
1222
|
-
this.loginProviderIndex = Math.max(0, this.loginProviderIndex - 1);
|
|
1223
|
-
this.render();
|
|
1224
|
-
return;
|
|
1225
|
-
}
|
|
1226
|
-
if (event.key === 'down') {
|
|
1227
|
-
this.loginProviderIndex = Math.min(this.loginProviders.length - 1, this.loginProviderIndex + 1);
|
|
1228
|
-
this.render();
|
|
1229
|
-
return;
|
|
1230
|
-
}
|
|
1231
|
-
if (event.key === 'enter') {
|
|
1232
|
-
// Move to API key entry
|
|
1233
|
-
this.loginStep = 'apikey';
|
|
1234
|
-
this.loginApiKey = '';
|
|
1235
|
-
this.loginError = '';
|
|
1236
|
-
this.render();
|
|
1237
|
-
return;
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
|
-
else {
|
|
1241
|
-
// API key entry step
|
|
1242
|
-
if (event.key === 'escape') {
|
|
1243
|
-
// Go back to provider selection
|
|
1244
|
-
this.loginStep = 'provider';
|
|
1245
|
-
this.loginApiKey = '';
|
|
1246
|
-
this.loginError = '';
|
|
1247
|
-
this.render();
|
|
1248
|
-
return;
|
|
1249
|
-
}
|
|
1250
|
-
if (event.key === 'enter') {
|
|
1251
|
-
// Validate and submit
|
|
1252
|
-
if (this.loginApiKey.length < 10) {
|
|
1253
|
-
this.loginError = 'API key too short (min 10 characters)';
|
|
1254
|
-
this.render();
|
|
1255
|
-
return;
|
|
1256
|
-
}
|
|
1257
|
-
const callback = this.loginCallback;
|
|
1258
|
-
const result = {
|
|
1259
|
-
providerId: this.loginProviders[this.loginProviderIndex].id,
|
|
1260
|
-
apiKey: this.loginApiKey,
|
|
1261
|
-
};
|
|
1262
1050
|
this.loginOpen = false;
|
|
1263
1051
|
this.loginCallback = null;
|
|
1264
|
-
this.render();
|
|
1265
1052
|
if (callback)
|
|
1266
1053
|
callback(result);
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
this.loginApiKey = this.loginApiKey.slice(0, -1);
|
|
1271
|
-
this.loginError = '';
|
|
1272
|
-
this.render();
|
|
1273
|
-
return;
|
|
1274
|
-
}
|
|
1275
|
-
// Ctrl+V to paste
|
|
1276
|
-
if (event.ctrl && event.key === 'v') {
|
|
1277
|
-
this.pasteApiKey();
|
|
1278
|
-
return;
|
|
1279
|
-
}
|
|
1280
|
-
// Ctrl+B to open subscribe URL
|
|
1281
|
-
if (event.ctrl && event.key === 'b') {
|
|
1282
|
-
const provider = this.loginProviders[this.loginProviderIndex];
|
|
1283
|
-
if (provider.subscribeUrl) {
|
|
1284
|
-
try {
|
|
1285
|
-
const cmd = process.platform === 'darwin' ? 'open'
|
|
1286
|
-
: process.platform === 'win32' ? 'start'
|
|
1287
|
-
: 'xdg-open';
|
|
1288
|
-
const child = spawn(cmd, [provider.subscribeUrl], { detached: true, stdio: 'ignore' });
|
|
1289
|
-
child.unref();
|
|
1290
|
-
}
|
|
1291
|
-
catch { /* ignore */ }
|
|
1292
|
-
}
|
|
1293
|
-
return;
|
|
1294
|
-
}
|
|
1295
|
-
// Handle paste detection (fast input)
|
|
1296
|
-
if (event.isPaste && event.key.length > 1) {
|
|
1297
|
-
this.loginApiKey += event.key.trim();
|
|
1298
|
-
this.loginError = '';
|
|
1299
|
-
this.render();
|
|
1300
|
-
return;
|
|
1301
|
-
}
|
|
1302
|
-
// Regular character input
|
|
1303
|
-
if (event.key.length === 1 && !event.ctrl) {
|
|
1304
|
-
this.loginApiKey += event.key;
|
|
1305
|
-
this.loginError = '';
|
|
1306
|
-
this.render();
|
|
1307
|
-
return;
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
/**
|
|
1312
|
-
* Paste API key from clipboard
|
|
1313
|
-
*/
|
|
1314
|
-
async pasteApiKey() {
|
|
1315
|
-
try {
|
|
1316
|
-
const text = await clipboardy.read();
|
|
1317
|
-
if (text) {
|
|
1318
|
-
this.loginApiKey = text.trim();
|
|
1319
|
-
this.loginError = '';
|
|
1320
|
-
this.render();
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
catch {
|
|
1324
|
-
this.loginError = 'Could not read clipboard';
|
|
1325
|
-
this.render();
|
|
1326
|
-
}
|
|
1054
|
+
},
|
|
1055
|
+
render: () => this.render(),
|
|
1056
|
+
});
|
|
1327
1057
|
}
|
|
1328
1058
|
/**
|
|
1329
1059
|
* Handle inline menu keys
|
|
1330
1060
|
*/
|
|
1331
1061
|
handleMenuKey(event) {
|
|
1332
|
-
|
|
1333
|
-
this.
|
|
1334
|
-
this.
|
|
1335
|
-
this.
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
this.render();
|
|
1346
|
-
return;
|
|
1347
|
-
}
|
|
1348
|
-
if (event.key === 'enter') {
|
|
1349
|
-
const selectedItem = this.menuItems[this.menuIndex];
|
|
1350
|
-
const callback = this.menuCallback;
|
|
1351
|
-
this.menuOpen = false;
|
|
1352
|
-
this.menuCallback = null;
|
|
1353
|
-
this.render();
|
|
1354
|
-
if (callback) {
|
|
1355
|
-
callback(selectedItem);
|
|
1356
|
-
}
|
|
1357
|
-
return;
|
|
1358
|
-
}
|
|
1359
|
-
if (event.key === 'pageup') {
|
|
1360
|
-
this.menuIndex = Math.max(0, this.menuIndex - 5);
|
|
1361
|
-
this.render();
|
|
1362
|
-
return;
|
|
1363
|
-
}
|
|
1364
|
-
if (event.key === 'pagedown') {
|
|
1365
|
-
this.menuIndex = Math.min(this.menuItems.length - 1, this.menuIndex + 5);
|
|
1366
|
-
this.render();
|
|
1367
|
-
return;
|
|
1368
|
-
}
|
|
1369
|
-
// Ignore other keys when menu is open
|
|
1062
|
+
handleMenuKey(event, {
|
|
1063
|
+
index: this.menuIndex,
|
|
1064
|
+
items: this.menuItems,
|
|
1065
|
+
setIndex: (v) => { this.menuIndex = v; },
|
|
1066
|
+
close: (_cb, selected) => {
|
|
1067
|
+
const callback = this.menuCallback;
|
|
1068
|
+
this.menuOpen = false;
|
|
1069
|
+
this.menuCallback = null;
|
|
1070
|
+
if (selected && callback)
|
|
1071
|
+
callback(selected);
|
|
1072
|
+
},
|
|
1073
|
+
render: () => this.render(),
|
|
1074
|
+
});
|
|
1370
1075
|
}
|
|
1371
1076
|
/**
|
|
1372
|
-
* Handle
|
|
1077
|
+
* Handle permission dialog keys
|
|
1373
1078
|
*/
|
|
1374
1079
|
handleInlinePermissionKey(event) {
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
callback
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
this.render();
|
|
1388
|
-
return;
|
|
1389
|
-
}
|
|
1390
|
-
if (event.key === 'down') {
|
|
1391
|
-
this.permissionIndex = Math.min(options.length - 1, this.permissionIndex + 1);
|
|
1392
|
-
this.render();
|
|
1393
|
-
return;
|
|
1394
|
-
}
|
|
1395
|
-
if (event.key === 'enter') {
|
|
1396
|
-
const selected = options[this.permissionIndex];
|
|
1397
|
-
const callback = this.permissionCallback;
|
|
1398
|
-
this.permissionOpen = false;
|
|
1399
|
-
this.permissionCallback = null;
|
|
1400
|
-
this.render();
|
|
1401
|
-
if (callback)
|
|
1402
|
-
callback(selected);
|
|
1403
|
-
return;
|
|
1404
|
-
}
|
|
1080
|
+
handleInlinePermissionKey(event, {
|
|
1081
|
+
index: this.permissionIndex,
|
|
1082
|
+
setIndex: (v) => { this.permissionIndex = v; },
|
|
1083
|
+
close: (level) => {
|
|
1084
|
+
const callback = this.permissionCallback;
|
|
1085
|
+
this.permissionOpen = false;
|
|
1086
|
+
this.permissionCallback = null;
|
|
1087
|
+
if (callback)
|
|
1088
|
+
callback(level);
|
|
1089
|
+
},
|
|
1090
|
+
render: () => this.render(),
|
|
1091
|
+
});
|
|
1405
1092
|
}
|
|
1406
1093
|
handleInlineSessionPickerKey(event) {
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
this.
|
|
1411
|
-
this.
|
|
1412
|
-
this.
|
|
1413
|
-
this.
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
if (event.key === 'd' && this.sessionPickerDeleteCallback && this.sessionPickerItems.length > 0) {
|
|
1420
|
-
this.sessionPickerDeleteMode = !this.sessionPickerDeleteMode;
|
|
1421
|
-
this.render();
|
|
1422
|
-
return;
|
|
1423
|
-
}
|
|
1424
|
-
if (event.key === 'escape') {
|
|
1425
|
-
if (this.sessionPickerDeleteMode) {
|
|
1426
|
-
// Exit delete mode
|
|
1094
|
+
handleInlineSessionPickerKey(event, {
|
|
1095
|
+
index: this.sessionPickerIndex,
|
|
1096
|
+
items: this.sessionPickerItems,
|
|
1097
|
+
deleteMode: this.sessionPickerDeleteMode,
|
|
1098
|
+
hasDeleteCallback: !!this.sessionPickerDeleteCallback,
|
|
1099
|
+
setIndex: (v) => { this.sessionPickerIndex = v; },
|
|
1100
|
+
setItems: (items) => { this.sessionPickerItems = items; },
|
|
1101
|
+
setDeleteMode: (v) => { this.sessionPickerDeleteMode = v; },
|
|
1102
|
+
close: (sessionName) => {
|
|
1103
|
+
const callback = this.sessionPickerCallback;
|
|
1104
|
+
this.sessionPickerOpen = false;
|
|
1105
|
+
this.sessionPickerCallback = null;
|
|
1427
1106
|
this.sessionPickerDeleteMode = false;
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
}
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
this.
|
|
1436
|
-
this.render()
|
|
1437
|
-
|
|
1438
|
-
callback(null);
|
|
1439
|
-
return;
|
|
1440
|
-
}
|
|
1441
|
-
if (event.key === 'up') {
|
|
1442
|
-
this.sessionPickerIndex = Math.max(0, this.sessionPickerIndex - 1);
|
|
1443
|
-
this.render();
|
|
1444
|
-
return;
|
|
1445
|
-
}
|
|
1446
|
-
if (event.key === 'down') {
|
|
1447
|
-
this.sessionPickerIndex = Math.min(this.sessionPickerItems.length - 1, this.sessionPickerIndex + 1);
|
|
1448
|
-
this.render();
|
|
1449
|
-
return;
|
|
1450
|
-
}
|
|
1451
|
-
if (event.key === 'enter' && this.sessionPickerItems.length > 0) {
|
|
1452
|
-
const selected = this.sessionPickerItems[this.sessionPickerIndex];
|
|
1453
|
-
if (this.sessionPickerDeleteMode) {
|
|
1454
|
-
// Delete the selected session
|
|
1455
|
-
const deleteCallback = this.sessionPickerDeleteCallback;
|
|
1456
|
-
if (deleteCallback) {
|
|
1457
|
-
deleteCallback(selected.name);
|
|
1458
|
-
// Remove from list
|
|
1459
|
-
this.sessionPickerItems = this.sessionPickerItems.filter(s => s.name !== selected.name);
|
|
1460
|
-
// Adjust index if needed
|
|
1461
|
-
if (this.sessionPickerIndex >= this.sessionPickerItems.length) {
|
|
1462
|
-
this.sessionPickerIndex = Math.max(0, this.sessionPickerItems.length - 1);
|
|
1463
|
-
}
|
|
1464
|
-
// Exit delete mode if no more items
|
|
1465
|
-
if (this.sessionPickerItems.length === 0) {
|
|
1466
|
-
this.sessionPickerDeleteMode = false;
|
|
1467
|
-
}
|
|
1468
|
-
this.notify(`Deleted: ${selected.name}`);
|
|
1469
|
-
this.render();
|
|
1470
|
-
}
|
|
1471
|
-
return;
|
|
1472
|
-
}
|
|
1473
|
-
// Load selected session
|
|
1474
|
-
const callback = this.sessionPickerCallback;
|
|
1475
|
-
this.sessionPickerOpen = false;
|
|
1476
|
-
this.sessionPickerCallback = null;
|
|
1477
|
-
this.sessionPickerDeleteMode = false;
|
|
1478
|
-
this.render();
|
|
1479
|
-
if (callback)
|
|
1480
|
-
callback(selected.name);
|
|
1481
|
-
return;
|
|
1482
|
-
}
|
|
1107
|
+
if (callback)
|
|
1108
|
+
callback(sessionName);
|
|
1109
|
+
},
|
|
1110
|
+
onDelete: (name) => {
|
|
1111
|
+
if (this.sessionPickerDeleteCallback)
|
|
1112
|
+
this.sessionPickerDeleteCallback(name);
|
|
1113
|
+
},
|
|
1114
|
+
notify: (msg) => this.notify(msg),
|
|
1115
|
+
render: () => this.render(),
|
|
1116
|
+
});
|
|
1483
1117
|
}
|
|
1484
1118
|
handleInlineConfirmKey(event) {
|
|
1485
1119
|
if (!this.confirmOptions) {
|
|
@@ -1487,43 +1121,21 @@ export class App {
|
|
|
1487
1121
|
this.render();
|
|
1488
1122
|
return;
|
|
1489
1123
|
}
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
this.
|
|
1493
|
-
this.
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
this.confirmSelection = 'yes';
|
|
1506
|
-
this.render();
|
|
1507
|
-
return;
|
|
1508
|
-
}
|
|
1509
|
-
if (event.key === 'n') {
|
|
1510
|
-
this.confirmSelection = 'no';
|
|
1511
|
-
this.render();
|
|
1512
|
-
return;
|
|
1513
|
-
}
|
|
1514
|
-
if (event.key === 'enter') {
|
|
1515
|
-
const options = this.confirmOptions;
|
|
1516
|
-
this.confirmOptions = null;
|
|
1517
|
-
this.confirmOpen = false;
|
|
1518
|
-
this.render();
|
|
1519
|
-
if (this.confirmSelection === 'yes') {
|
|
1520
|
-
options.onConfirm();
|
|
1521
|
-
}
|
|
1522
|
-
else if (options.onCancel) {
|
|
1523
|
-
options.onCancel();
|
|
1524
|
-
}
|
|
1525
|
-
return;
|
|
1526
|
-
}
|
|
1124
|
+
handleInlineConfirmKey(event, {
|
|
1125
|
+
options: this.confirmOptions,
|
|
1126
|
+
selection: this.confirmSelection,
|
|
1127
|
+
setSelection: (v) => { this.confirmSelection = v; },
|
|
1128
|
+
close: (confirmed) => {
|
|
1129
|
+
const options = this.confirmOptions;
|
|
1130
|
+
this.confirmOptions = null;
|
|
1131
|
+
this.confirmOpen = false;
|
|
1132
|
+
if (confirmed)
|
|
1133
|
+
options.onConfirm();
|
|
1134
|
+
else if (options.onCancel)
|
|
1135
|
+
options.onCancel();
|
|
1136
|
+
},
|
|
1137
|
+
render: () => this.render(),
|
|
1138
|
+
});
|
|
1527
1139
|
}
|
|
1528
1140
|
/**
|
|
1529
1141
|
* Submit the current input buffer (used by Enter and Escape-in-multiline)
|
|
@@ -2246,16 +1858,24 @@ export class App {
|
|
|
2246
1858
|
renderInlineAgentProgress(startY, width) {
|
|
2247
1859
|
let y = startY;
|
|
2248
1860
|
const spinner = SPINNER_FRAMES[this.spinnerFrame];
|
|
2249
|
-
// Calculate stats
|
|
2250
|
-
const stats = {
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
1861
|
+
// Calculate stats in a single pass
|
|
1862
|
+
const stats = this.agentActions.reduce((acc, a) => {
|
|
1863
|
+
if (a.type === 'read')
|
|
1864
|
+
acc.reads++;
|
|
1865
|
+
else if (a.type === 'write')
|
|
1866
|
+
acc.writes++;
|
|
1867
|
+
else if (a.type === 'edit')
|
|
1868
|
+
acc.edits++;
|
|
1869
|
+
else if (a.type === 'delete')
|
|
1870
|
+
acc.deletes++;
|
|
1871
|
+
else if (a.type === 'command')
|
|
1872
|
+
acc.commands++;
|
|
1873
|
+
else if (a.type === 'search')
|
|
1874
|
+
acc.searches++;
|
|
1875
|
+
if (a.result === 'error')
|
|
1876
|
+
acc.errors++;
|
|
1877
|
+
return acc;
|
|
1878
|
+
}, { reads: 0, writes: 0, edits: 0, deletes: 0, commands: 0, searches: 0, errors: 0 });
|
|
2259
1879
|
// Top border with title
|
|
2260
1880
|
const title = ` ${spinner} AGENT `;
|
|
2261
1881
|
const titlePadLeft = 2;
|