codebuff 1.0.149 → 1.0.151
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/dist/__tests__/browser-runner.test.d.ts +1 -0
- package/dist/__tests__/browser-runner.test.js +15 -0
- package/dist/__tests__/browser-runner.test.js.map +1 -0
- package/dist/browser-runner.d.ts +34 -0
- package/dist/browser-runner.js +622 -0
- package/dist/browser-runner.js.map +1 -0
- package/dist/chat-storage.d.ts +2 -4
- package/dist/chat-storage.js +79 -46
- package/dist/chat-storage.js.map +1 -1
- package/dist/cli.d.ts +27 -10
- package/dist/cli.js +321 -308
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +5 -4
- package/dist/client.js +24 -18
- package/dist/client.js.map +1 -1
- package/dist/code-map/languages.js +7 -17
- package/dist/code-map/languages.js.map +1 -1
- package/dist/code-map/parse.js +7 -17
- package/dist/code-map/parse.js.map +1 -1
- package/dist/code-map/tsconfig.tsbuildinfo +1 -1
- package/dist/common/actions.d.ts +301 -106
- package/dist/common/actions.js +17 -0
- package/dist/common/actions.js.map +1 -1
- package/dist/common/advanced-analyzer.d.ts +19 -0
- package/dist/common/advanced-analyzer.js +140 -0
- package/dist/common/advanced-analyzer.js.map +1 -0
- package/dist/common/browser-actions.d.ts +4354 -0
- package/dist/common/browser-actions.js +336 -0
- package/dist/common/browser-actions.js.map +1 -0
- package/dist/common/constants.d.ts +9 -2
- package/dist/common/constants.js +3 -2
- package/dist/common/constants.js.map +1 -1
- package/dist/common/message-image-handling.d.ts +41 -0
- package/dist/common/message-image-handling.js +57 -0
- package/dist/common/message-image-handling.js.map +1 -0
- package/dist/common/project-file-tree.js +7 -7
- package/dist/common/project-file-tree.js.map +1 -1
- package/dist/common/types/usage.d.ts +2 -2
- package/dist/common/util/credentials.d.ts +2 -2
- package/dist/common/util/file.js +5 -3
- package/dist/common/util/file.js.map +1 -1
- package/dist/common/util/min-heap.d.ts +15 -0
- package/dist/common/util/min-heap.js +73 -0
- package/dist/common/util/min-heap.js.map +1 -0
- package/dist/common/util/string.d.ts +10 -0
- package/dist/common/util/string.js +29 -1
- package/dist/common/util/string.js.map +1 -1
- package/dist/common/websockets/websocket-schema.d.ts +478 -244
- package/dist/create-template-project.d.ts +1 -1
- package/dist/create-template-project.js +27 -29
- package/dist/create-template-project.js.map +1 -1
- package/dist/credentials.d.ts +1 -0
- package/dist/credentials.js +7 -3
- package/dist/credentials.js.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/menu.js +7 -17
- package/dist/menu.js.map +1 -1
- package/dist/project-files.d.ts +3 -0
- package/dist/project-files.js +41 -19
- package/dist/project-files.js.map +1 -1
- package/dist/tool-handlers.d.ts +3 -1
- package/dist/tool-handlers.js +59 -6
- package/dist/tool-handlers.js.map +1 -1
- package/dist/utils/terminal.js +10 -22
- package/dist/utils/terminal.js.map +1 -1
- package/dist/web-scraper.d.ts +1 -1
- package/dist/web-scraper.js +9 -7
- package/dist/web-scraper.js.map +1 -1
- package/package.json +4 -4
- package/dist/common/logger.d.ts +0 -1
- package/dist/common/logger.js +0 -7
- package/dist/common/logger.js.map +0 -1
- package/dist/common/util/constants.d.ts +0 -1
- package/dist/common/util/constants.js +0 -7
- package/dist/common/util/constants.js.map +0 -1
- package/dist/common/util/helpers.d.ts +0 -1
- package/dist/common/util/helpers.js +0 -6
- package/dist/common/util/helpers.js.map +0 -1
- package/dist/common/util/messages.d.ts +0 -1
- package/dist/common/util/messages.js +0 -7
- package/dist/common/util/messages.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -15,23 +15,13 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) ||
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
35
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
26
|
exports.CLI = void 0;
|
|
37
27
|
const lodash_1 = require("lodash");
|
|
@@ -40,17 +30,6 @@ const changes_1 = require("./common/util/changes");
|
|
|
40
30
|
const readline = __importStar(require("readline"));
|
|
41
31
|
const picocolors_1 = require("picocolors");
|
|
42
32
|
const path_1 = require("path");
|
|
43
|
-
function rewriteLine(line) {
|
|
44
|
-
// Only do line rewriting if we have an interactive TTY
|
|
45
|
-
if (process.stdout.isTTY) {
|
|
46
|
-
readline.clearLine(process.stdout, 0);
|
|
47
|
-
readline.cursorTo(process.stdout, 0);
|
|
48
|
-
process.stdout.write(line);
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
process.stdout.write(line + '\n');
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
33
|
const config_1 = require("./config");
|
|
55
34
|
const chat_storage_1 = require("./chat-storage");
|
|
56
35
|
const client_1 = require("./client");
|
|
@@ -79,52 +58,13 @@ class CLI {
|
|
|
79
58
|
consecutiveFastInputs = 0;
|
|
80
59
|
pastedContent = '';
|
|
81
60
|
isPasting = false;
|
|
61
|
+
// ==================== Initialization Methods ====================
|
|
82
62
|
constructor(readyPromise, { git, costMode }) {
|
|
83
63
|
this.git = git;
|
|
84
64
|
this.costMode = costMode;
|
|
85
65
|
this.chatStorage = new chat_storage_1.ChatStorage();
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
this.restoreCursor();
|
|
89
|
-
process.exit(0);
|
|
90
|
-
});
|
|
91
|
-
this.rl = readline.createInterface({
|
|
92
|
-
input: process.stdin,
|
|
93
|
-
output: process.stdout,
|
|
94
|
-
historySize: 1000,
|
|
95
|
-
terminal: true,
|
|
96
|
-
completer: (line) => {
|
|
97
|
-
if (!this.client.fileContext?.fileTree)
|
|
98
|
-
return [[], line];
|
|
99
|
-
const tokenNames = Object.values(this.client.fileContext.fileTokenScores).flatMap((o) => Object.keys(o));
|
|
100
|
-
const paths = (0, project_file_tree_1.getAllFilePaths)(this.client.fileContext.fileTree);
|
|
101
|
-
const lastWord = line.split(' ').pop() || '';
|
|
102
|
-
const matchingTokens = [...tokenNames, ...paths].filter((token) => token.startsWith(lastWord) || token.includes('/' + lastWord));
|
|
103
|
-
if (matchingTokens.length > 1) {
|
|
104
|
-
// Find common characters after lastWord
|
|
105
|
-
const suffixes = matchingTokens.map((token) => {
|
|
106
|
-
const index = token.indexOf(lastWord);
|
|
107
|
-
return token.slice(index + lastWord.length);
|
|
108
|
-
});
|
|
109
|
-
let commonPrefix = '';
|
|
110
|
-
const firstSuffix = suffixes[0];
|
|
111
|
-
for (let i = 0; i < firstSuffix.length; i++) {
|
|
112
|
-
const char = firstSuffix[i];
|
|
113
|
-
if (suffixes.every((suffix) => suffix[i] === char)) {
|
|
114
|
-
commonPrefix += char;
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
if (commonPrefix) {
|
|
121
|
-
// Match the common prefix
|
|
122
|
-
return [[lastWord + commonPrefix], lastWord];
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return [matchingTokens, lastWord];
|
|
126
|
-
},
|
|
127
|
-
});
|
|
66
|
+
this.setupSignalHandlers();
|
|
67
|
+
this.initReadlineInterface();
|
|
128
68
|
this.client = new client_1.Client(config_1.websocketUrl, this.chatStorage, this.onWebSocketError.bind(this), this.onWebSocketReconnect.bind(this), this.returnControlToUser.bind(this), this.costMode, this.git, this.rl);
|
|
129
69
|
this.readyPromise = Promise.all([
|
|
130
70
|
readyPromise.then((results) => {
|
|
@@ -135,116 +75,63 @@ class CLI {
|
|
|
135
75
|
this.client.connect(),
|
|
136
76
|
]);
|
|
137
77
|
this.setPrompt();
|
|
138
|
-
|
|
139
|
-
|
|
78
|
+
}
|
|
79
|
+
setupSignalHandlers() {
|
|
80
|
+
process.on('exit', () => this.restoreCursor());
|
|
81
|
+
process.on('SIGTERM', () => {
|
|
82
|
+
this.restoreCursor();
|
|
83
|
+
process.exit(0);
|
|
140
84
|
});
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
85
|
+
process.on('SIGTSTP', () => this.handleExit());
|
|
86
|
+
}
|
|
87
|
+
initReadlineInterface() {
|
|
88
|
+
this.rl = readline.createInterface({
|
|
89
|
+
input: process.stdin,
|
|
90
|
+
output: process.stdout,
|
|
91
|
+
historySize: 1000,
|
|
92
|
+
terminal: true,
|
|
93
|
+
completer: this.completer.bind(this),
|
|
94
|
+
});
|
|
95
|
+
this.rl.on('line', (line) => this.handleLine(line));
|
|
96
|
+
this.rl.on('SIGINT', () => this.handleSigint());
|
|
97
|
+
this.rl.on('close', () => this.handleExit());
|
|
98
|
+
process.stdin.on('keypress', (str, key) => this.handleKeyPress(str, key));
|
|
99
|
+
}
|
|
100
|
+
completer(line) {
|
|
101
|
+
if (!this.client.fileContext?.fileTree)
|
|
102
|
+
return [[], line];
|
|
103
|
+
const tokenNames = Object.values(this.client.fileContext.fileTokenScores).flatMap((o) => Object.keys(o));
|
|
104
|
+
const paths = (0, project_file_tree_1.getAllFilePaths)(this.client.fileContext.fileTree);
|
|
105
|
+
const lastWord = line.split(' ').pop() || '';
|
|
106
|
+
const lastWordLower = lastWord.toLowerCase();
|
|
107
|
+
const matchingTokens = [...tokenNames, ...paths].filter((token) => token.toLowerCase().startsWith(lastWordLower) ||
|
|
108
|
+
token.toLowerCase().includes('/' + lastWordLower));
|
|
109
|
+
if (matchingTokens.length > 1) {
|
|
110
|
+
const suffixes = matchingTokens.map((token) => {
|
|
111
|
+
const index = token.toLowerCase().indexOf(lastWordLower);
|
|
112
|
+
return token.slice(index + lastWord.length);
|
|
113
|
+
});
|
|
114
|
+
let commonPrefix = '';
|
|
115
|
+
const firstSuffix = suffixes[0];
|
|
116
|
+
for (let i = 0; i < firstSuffix.length; i++) {
|
|
117
|
+
const char = firstSuffix[i];
|
|
118
|
+
if (suffixes.every((suffix) => suffix[i] === char)) {
|
|
119
|
+
commonPrefix += char;
|
|
157
120
|
}
|
|
158
121
|
else {
|
|
159
|
-
|
|
160
|
-
console.log('\nPress Ctrl-C again to exit');
|
|
161
|
-
this.rl.prompt();
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
this.rl.on('close', () => {
|
|
166
|
-
this.handleExit();
|
|
167
|
-
});
|
|
168
|
-
process.on('SIGTSTP', () => {
|
|
169
|
-
// Exit on Ctrl+Z
|
|
170
|
-
this.handleExit();
|
|
171
|
-
});
|
|
172
|
-
process.stdin.on('keypress', (str, key) => {
|
|
173
|
-
if (key.name === 'escape') {
|
|
174
|
-
this.handleEscKey();
|
|
175
|
-
}
|
|
176
|
-
// Make double spaces into newlines
|
|
177
|
-
if (str === ' ' &&
|
|
178
|
-
'_refreshLine' in this.rl &&
|
|
179
|
-
'line' in this.rl &&
|
|
180
|
-
'cursor' in this.rl) {
|
|
181
|
-
const rl = this.rl;
|
|
182
|
-
const { cursor, line } = rl;
|
|
183
|
-
const prevTwoChars = cursor > 1 ? line.slice(cursor - 2, cursor) : '';
|
|
184
|
-
if (prevTwoChars === ' ') {
|
|
185
|
-
rl.line = line.slice(0, cursor - 2) + '\n\n' + line.slice(cursor);
|
|
186
|
-
rl._refreshLine();
|
|
122
|
+
break;
|
|
187
123
|
}
|
|
188
124
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
returnControlToUser() {
|
|
193
|
-
this.rl.prompt();
|
|
194
|
-
this.isReceivingResponse = false;
|
|
195
|
-
if (this.stopResponse) {
|
|
196
|
-
this.stopResponse();
|
|
197
|
-
}
|
|
198
|
-
this.stopLoadingAnimation();
|
|
199
|
-
}
|
|
200
|
-
onWebSocketError() {
|
|
201
|
-
this.stopLoadingAnimation();
|
|
202
|
-
this.isReceivingResponse = false;
|
|
203
|
-
if (this.stopResponse) {
|
|
204
|
-
this.stopResponse();
|
|
205
|
-
this.stopResponse = null;
|
|
206
|
-
}
|
|
207
|
-
console.error((0, picocolors_1.yellow)('\nCould not connect. Retrying...'));
|
|
208
|
-
}
|
|
209
|
-
onWebSocketReconnect() {
|
|
210
|
-
console.log((0, picocolors_1.green)('\nReconnected!'));
|
|
211
|
-
this.returnControlToUser();
|
|
212
|
-
}
|
|
213
|
-
detectPasting() {
|
|
214
|
-
const currentTime = Date.now();
|
|
215
|
-
const timeDiff = currentTime - this.lastInputTime;
|
|
216
|
-
if (timeDiff < 10) {
|
|
217
|
-
this.consecutiveFastInputs++;
|
|
218
|
-
if (this.consecutiveFastInputs >= 2) {
|
|
219
|
-
this.isPasting = true;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
this.consecutiveFastInputs = 0;
|
|
224
|
-
if (this.isPasting) {
|
|
225
|
-
this.isPasting = false;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
this.lastInputTime = currentTime;
|
|
229
|
-
}
|
|
230
|
-
handleInput(line) {
|
|
231
|
-
this.detectPasting();
|
|
232
|
-
if (this.isPasting) {
|
|
233
|
-
this.pastedContent += line + '\n';
|
|
234
|
-
}
|
|
235
|
-
else if (!this.isReceivingResponse) {
|
|
236
|
-
if (this.pastedContent) {
|
|
237
|
-
this.handleUserInput((this.pastedContent + line).trim());
|
|
238
|
-
this.pastedContent = '';
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
this.handleUserInput(line.trim());
|
|
125
|
+
if (commonPrefix) {
|
|
126
|
+
return [[lastWord + commonPrefix], lastWord];
|
|
242
127
|
}
|
|
243
128
|
}
|
|
129
|
+
return [matchingTokens, lastWord];
|
|
244
130
|
}
|
|
245
131
|
setPrompt() {
|
|
246
132
|
this.rl.setPrompt((0, picocolors_1.green)(`${(0, path_1.parse)((0, project_files_1.getProjectRoot)()).base} > `));
|
|
247
133
|
}
|
|
134
|
+
// ==================== Public Methods ====================
|
|
248
135
|
async printInitialPrompt(initialInput) {
|
|
249
136
|
if (this.client.user) {
|
|
250
137
|
(0, menu_1.displayGreeting)(this.costMode, this.client.user.name);
|
|
@@ -260,177 +147,83 @@ class CLI {
|
|
|
260
147
|
this.handleUserInput(initialInput);
|
|
261
148
|
}
|
|
262
149
|
}
|
|
263
|
-
|
|
264
|
-
this.
|
|
265
|
-
this.rl.prompt();
|
|
266
|
-
}
|
|
267
|
-
handleRedo() {
|
|
268
|
-
this.navigateFileVersion('redo');
|
|
150
|
+
async printDiff() {
|
|
151
|
+
this.handleDiff();
|
|
269
152
|
this.rl.prompt();
|
|
270
153
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const navigated = this.chatStorage.navigateVersion(direction);
|
|
277
|
-
if (navigated) {
|
|
278
|
-
console.log(direction === 'undo'
|
|
279
|
-
? (0, picocolors_1.green)('Undo last change')
|
|
280
|
-
: (0, picocolors_1.green)('Redo last change'));
|
|
281
|
-
const files = this.applyAndDisplayCurrentFileVersion();
|
|
282
|
-
console.log((0, picocolors_1.green)('Loaded files:'), (0, picocolors_1.green)(Object.keys(files).join(', ')));
|
|
283
|
-
}
|
|
284
|
-
else {
|
|
285
|
-
console.log((0, picocolors_1.green)(`No more ${direction === 'undo' ? 'undo' : 'redo'}s`));
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
handleStopResponse() {
|
|
289
|
-
console.log((0, picocolors_1.yellow)('\n[Response stopped by user]'));
|
|
290
|
-
this.isReceivingResponse = false;
|
|
291
|
-
if (this.stopResponse) {
|
|
292
|
-
this.stopResponse();
|
|
293
|
-
}
|
|
294
|
-
this.stopLoadingAnimation();
|
|
295
|
-
this.restoreCursor();
|
|
296
|
-
}
|
|
297
|
-
restoreCursor() {
|
|
298
|
-
// Show cursor ANSI escape code
|
|
299
|
-
process.stdout.write('\u001B[?25h');
|
|
300
|
-
}
|
|
301
|
-
handleExit() {
|
|
302
|
-
this.restoreCursor();
|
|
303
|
-
console.log('\n\n');
|
|
304
|
-
console.log(`${(0, string_1.pluralize)(this.client.sessionCreditsUsed, 'credit')} used this session.`);
|
|
305
|
-
if (!!this.client.limit &&
|
|
306
|
-
!!this.client.usage &&
|
|
307
|
-
!!this.client.nextQuotaReset) {
|
|
308
|
-
const daysUntilReset = Math.max(0, Math.floor((this.client.nextQuotaReset.getTime() - Date.now()) /
|
|
309
|
-
(1000 * 60 * 60 * 24)));
|
|
310
|
-
console.log(`${Math.max(0, this.client.limit - this.client.usage)} / ${this.client.limit} credits remaining. Renews in ${(0, string_1.pluralize)(daysUntilReset, 'day')}.`);
|
|
311
|
-
}
|
|
312
|
-
console.log((0, picocolors_1.green)('Codebuff out!'));
|
|
313
|
-
process.exit(0);
|
|
314
|
-
}
|
|
315
|
-
handleEscKey() {
|
|
316
|
-
if (this.isReceivingResponse) {
|
|
317
|
-
this.handleStopResponse();
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
applyAndDisplayCurrentFileVersion() {
|
|
321
|
-
const currentVersion = this.chatStorage.getCurrentVersion();
|
|
322
|
-
if (currentVersion) {
|
|
323
|
-
(0, project_files_1.setFiles)(currentVersion.files);
|
|
324
|
-
return currentVersion.files;
|
|
325
|
-
}
|
|
326
|
-
return {};
|
|
327
|
-
}
|
|
328
|
-
startLoadingAnimation() {
|
|
329
|
-
const chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
330
|
-
let i = 0;
|
|
331
|
-
// Hide cursor while spinner is active
|
|
332
|
-
process.stdout.write('\u001B[?25l');
|
|
333
|
-
this.loadingInterval = setInterval(() => {
|
|
334
|
-
rewriteLine((0, picocolors_1.green)(`${chars[i]} Thinking...`));
|
|
335
|
-
i = (i + 1) % chars.length;
|
|
336
|
-
}, 100);
|
|
337
|
-
}
|
|
338
|
-
stopLoadingAnimation() {
|
|
339
|
-
if (this.loadingInterval) {
|
|
340
|
-
clearInterval(this.loadingInterval);
|
|
341
|
-
this.loadingInterval = null;
|
|
342
|
-
rewriteLine(''); // Clear the spinner line
|
|
343
|
-
this.restoreCursor(); // Show cursor after spinner stops
|
|
154
|
+
// ==================== Input Handling Methods ====================
|
|
155
|
+
async handleLine(line) {
|
|
156
|
+
this.detectPasting();
|
|
157
|
+
if (this.isPasting) {
|
|
158
|
+
this.pastedContent += line + '\n';
|
|
344
159
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
return commitMessage;
|
|
160
|
+
else if (!this.isReceivingResponse) {
|
|
161
|
+
if (this.pastedContent) {
|
|
162
|
+
await this.handleUserInput((this.pastedContent + line).trim());
|
|
163
|
+
this.pastedContent = '';
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
await this.handleUserInput(line.trim());
|
|
167
|
+
}
|
|
354
168
|
}
|
|
355
|
-
return undefined;
|
|
356
|
-
}
|
|
357
|
-
handleDiff() {
|
|
358
|
-
if (this.lastChanges.length === 0) {
|
|
359
|
-
console.log((0, picocolors_1.yellow)('No changes found in the last assistant response.'));
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
this.lastChanges.forEach((change) => {
|
|
363
|
-
console.log('-', change.filePath);
|
|
364
|
-
const lines = change.content
|
|
365
|
-
.split('\n')
|
|
366
|
-
.map((line) => (change.type === 'file' ? '+' + line : line));
|
|
367
|
-
lines.forEach((line) => {
|
|
368
|
-
if (line.startsWith('+')) {
|
|
369
|
-
console.log((0, picocolors_1.green)(line));
|
|
370
|
-
}
|
|
371
|
-
else if (line.startsWith('-')) {
|
|
372
|
-
console.log((0, picocolors_1.red)(line));
|
|
373
|
-
}
|
|
374
|
-
else {
|
|
375
|
-
console.log(line);
|
|
376
|
-
}
|
|
377
|
-
});
|
|
378
|
-
});
|
|
379
169
|
}
|
|
380
170
|
async handleUserInput(userInput) {
|
|
381
171
|
if (!userInput)
|
|
382
172
|
return;
|
|
383
173
|
userInput = userInput.trim();
|
|
384
|
-
|
|
174
|
+
if (await this.processCommand(userInput)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
await this.forwardUserInput(userInput);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Checks if the input matches a built-in command.
|
|
181
|
+
* Returns true if the command has been handled.
|
|
182
|
+
*/
|
|
183
|
+
async processCommand(userInput) {
|
|
385
184
|
if (userInput === 'help' || userInput === 'h') {
|
|
386
185
|
(0, menu_1.displayMenu)();
|
|
387
186
|
this.rl.prompt();
|
|
388
|
-
return;
|
|
187
|
+
return true;
|
|
389
188
|
}
|
|
390
189
|
if (userInput === 'login' || userInput === 'signin') {
|
|
391
190
|
await this.client.login();
|
|
392
|
-
return;
|
|
191
|
+
return true;
|
|
393
192
|
}
|
|
394
|
-
|
|
193
|
+
if (userInput === 'logout' || userInput === 'signout') {
|
|
395
194
|
await this.client.logout();
|
|
396
195
|
this.rl.prompt();
|
|
397
|
-
return;
|
|
196
|
+
return true;
|
|
398
197
|
}
|
|
399
|
-
|
|
198
|
+
if (userInput.startsWith('ref-')) {
|
|
400
199
|
await this.client.handleReferralCode(userInput.trim());
|
|
401
|
-
return;
|
|
200
|
+
return true;
|
|
402
201
|
}
|
|
403
|
-
|
|
202
|
+
if (userInput === 'usage' || userInput === 'credits') {
|
|
404
203
|
this.client.getUsage();
|
|
405
|
-
return;
|
|
204
|
+
return true;
|
|
406
205
|
}
|
|
407
|
-
|
|
206
|
+
if (userInput === 'undo' || userInput === 'u') {
|
|
408
207
|
this.handleUndo();
|
|
409
|
-
return;
|
|
208
|
+
return true;
|
|
410
209
|
}
|
|
411
|
-
|
|
210
|
+
if (userInput === 'redo' || userInput === 'r') {
|
|
412
211
|
this.handleRedo();
|
|
413
|
-
return;
|
|
212
|
+
return true;
|
|
414
213
|
}
|
|
415
|
-
|
|
416
|
-
userInput === 'exit' ||
|
|
417
|
-
userInput === 'q') {
|
|
214
|
+
if (userInput === 'quit' || userInput === 'exit' || userInput === 'q') {
|
|
418
215
|
this.handleExit();
|
|
419
|
-
return;
|
|
216
|
+
return true;
|
|
420
217
|
}
|
|
421
|
-
|
|
422
|
-
userInput === 'doff' ||
|
|
423
|
-
userInput === 'dif' ||
|
|
424
|
-
userInput === 'iff' ||
|
|
425
|
-
userInput === 'd') {
|
|
218
|
+
if (['diff', 'doff', 'dif', 'iff', 'd'].includes(userInput)) {
|
|
426
219
|
this.handleDiff();
|
|
427
220
|
this.rl.prompt();
|
|
428
|
-
return;
|
|
221
|
+
return true;
|
|
429
222
|
}
|
|
430
223
|
const runPrefix = '/run ';
|
|
431
224
|
const hasRunPrefix = userInput.startsWith(runPrefix);
|
|
432
225
|
if (hasRunPrefix ||
|
|
433
|
-
(!constants_1.SKIPPED_TERMINAL_COMMANDS.some((
|
|
226
|
+
(!constants_1.SKIPPED_TERMINAL_COMMANDS.some((cmd) => userInput.toLowerCase().startsWith(cmd)) &&
|
|
434
227
|
!userInput.includes('error ') &&
|
|
435
228
|
!userInput.includes("'") &&
|
|
436
229
|
userInput.split(' ').length <= 5)) {
|
|
@@ -439,15 +232,22 @@ class CLI {
|
|
|
439
232
|
if (result !== 'command not found') {
|
|
440
233
|
this.setPrompt();
|
|
441
234
|
this.rl.prompt();
|
|
442
|
-
return;
|
|
235
|
+
return true;
|
|
443
236
|
}
|
|
444
237
|
else if (hasRunPrefix) {
|
|
445
238
|
process.stdout.write(stdout);
|
|
446
239
|
this.setPrompt();
|
|
447
240
|
this.rl.prompt();
|
|
448
|
-
return;
|
|
241
|
+
return true;
|
|
449
242
|
}
|
|
450
243
|
}
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Processes regular user input (non-command) by gathering file changes and scraped content,
|
|
248
|
+
* sending the consolidated user message, and then applying assistant response changes.
|
|
249
|
+
*/
|
|
250
|
+
async forwardUserInput(userInput) {
|
|
451
251
|
this.startLoadingAnimation();
|
|
452
252
|
await this.readyPromise;
|
|
453
253
|
const currentChat = this.chatStorage.getCurrentChat();
|
|
@@ -501,7 +301,7 @@ class CLI {
|
|
|
501
301
|
if (this.client.lastRequestCredits > constants_1.REQUEST_CREDIT_SHOW_THRESHOLD) {
|
|
502
302
|
console.log(`\n${(0, string_1.pluralize)(this.client.lastRequestCredits, 'credit')} used for this request.`);
|
|
503
303
|
}
|
|
504
|
-
console.log('Complete! Type "diff" to
|
|
304
|
+
console.log('Complete! Type "diff" to review changes or "undo" to revert.');
|
|
505
305
|
this.client.showUsageWarning();
|
|
506
306
|
}
|
|
507
307
|
console.log();
|
|
@@ -515,6 +315,28 @@ class CLI {
|
|
|
515
315
|
this.chatStorage.addNewFileState(updatedFiles);
|
|
516
316
|
this.rl.prompt();
|
|
517
317
|
}
|
|
318
|
+
// ==================== WebSocket and Response Handling ====================
|
|
319
|
+
returnControlToUser() {
|
|
320
|
+
this.rl.prompt();
|
|
321
|
+
this.isReceivingResponse = false;
|
|
322
|
+
if (this.stopResponse) {
|
|
323
|
+
this.stopResponse();
|
|
324
|
+
}
|
|
325
|
+
this.stopLoadingAnimation();
|
|
326
|
+
}
|
|
327
|
+
onWebSocketError() {
|
|
328
|
+
this.stopLoadingAnimation();
|
|
329
|
+
this.isReceivingResponse = false;
|
|
330
|
+
if (this.stopResponse) {
|
|
331
|
+
this.stopResponse();
|
|
332
|
+
this.stopResponse = null;
|
|
333
|
+
}
|
|
334
|
+
console.error((0, picocolors_1.yellow)('\nCould not connect. Retrying...'));
|
|
335
|
+
}
|
|
336
|
+
onWebSocketReconnect() {
|
|
337
|
+
console.log((0, picocolors_1.green)('\nReconnected!'));
|
|
338
|
+
this.returnControlToUser();
|
|
339
|
+
}
|
|
518
340
|
async sendUserInputAndAwaitResponse() {
|
|
519
341
|
const userInputId = `mc-input-` + Math.random().toString(36).substring(2, 15);
|
|
520
342
|
const { responsePromise, stopResponse } = this.client.subscribeToResponse((chunk) => {
|
|
@@ -529,6 +351,197 @@ class CLI {
|
|
|
529
351
|
this.stopResponse = null;
|
|
530
352
|
return result;
|
|
531
353
|
}
|
|
354
|
+
// ==================== File Version Navigation ====================
|
|
355
|
+
handleUndo() {
|
|
356
|
+
this.navigateFileVersion('undo');
|
|
357
|
+
this.rl.prompt();
|
|
358
|
+
}
|
|
359
|
+
handleRedo() {
|
|
360
|
+
this.navigateFileVersion('redo');
|
|
361
|
+
this.rl.prompt();
|
|
362
|
+
}
|
|
363
|
+
navigateFileVersion(direction) {
|
|
364
|
+
const currentVersion = this.chatStorage.getCurrentVersion();
|
|
365
|
+
const filePaths = Object.keys(currentVersion ? currentVersion.files : {});
|
|
366
|
+
const currentFiles = (0, project_files_1.getExistingFiles)(filePaths);
|
|
367
|
+
this.chatStorage.saveCurrentFileState(currentFiles);
|
|
368
|
+
const navigated = this.chatStorage.navigateVersion(direction);
|
|
369
|
+
if (navigated) {
|
|
370
|
+
console.log(direction === 'undo'
|
|
371
|
+
? (0, picocolors_1.green)('Undo last change')
|
|
372
|
+
: (0, picocolors_1.green)('Redo last change'));
|
|
373
|
+
const files = this.applyAndDisplayCurrentFileVersion();
|
|
374
|
+
console.log((0, picocolors_1.green)('Loaded files:'), (0, picocolors_1.green)(Object.keys(files).join(', ')));
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
console.log((0, picocolors_1.green)(`No more ${direction === 'undo' ? 'undo' : 'redo'}s`));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
applyAndDisplayCurrentFileVersion() {
|
|
381
|
+
const currentVersion = this.chatStorage.getCurrentVersion();
|
|
382
|
+
if (currentVersion) {
|
|
383
|
+
(0, project_files_1.setFiles)(currentVersion.files);
|
|
384
|
+
return currentVersion.files;
|
|
385
|
+
}
|
|
386
|
+
return {};
|
|
387
|
+
}
|
|
388
|
+
// ==================== Terminal Animation and Cursor Management ====================
|
|
389
|
+
startLoadingAnimation() {
|
|
390
|
+
const chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
391
|
+
let i = 0;
|
|
392
|
+
// Hide cursor while spinner is active
|
|
393
|
+
process.stdout.write('\u001B[?25l');
|
|
394
|
+
this.loadingInterval = setInterval(() => {
|
|
395
|
+
this.rewriteLine((0, picocolors_1.green)(`${chars[i]} Thinking...`));
|
|
396
|
+
i = (i + 1) % chars.length;
|
|
397
|
+
}, 100);
|
|
398
|
+
}
|
|
399
|
+
stopLoadingAnimation() {
|
|
400
|
+
if (this.loadingInterval) {
|
|
401
|
+
clearInterval(this.loadingInterval);
|
|
402
|
+
this.loadingInterval = null;
|
|
403
|
+
this.rewriteLine(''); // Clear the spinner line
|
|
404
|
+
this.restoreCursor(); // Show cursor after spinner stops
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
rewriteLine(line) {
|
|
408
|
+
if (process.stdout.isTTY) {
|
|
409
|
+
readline.clearLine(process.stdout, 0);
|
|
410
|
+
readline.cursorTo(process.stdout, 0);
|
|
411
|
+
process.stdout.write(line);
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
process.stdout.write(line + '\n');
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
restoreCursor() {
|
|
418
|
+
process.stdout.write('\u001B[?25h');
|
|
419
|
+
}
|
|
420
|
+
// ==================== Keyboard and SIGINT Handling ====================
|
|
421
|
+
handleKeyPress(str, key) {
|
|
422
|
+
if (key.name === 'escape') {
|
|
423
|
+
this.handleEscKey();
|
|
424
|
+
}
|
|
425
|
+
// Convert double spaces into newlines
|
|
426
|
+
if (!this.isPasting &&
|
|
427
|
+
str === ' ' &&
|
|
428
|
+
'_refreshLine' in this.rl &&
|
|
429
|
+
'line' in this.rl &&
|
|
430
|
+
'cursor' in this.rl) {
|
|
431
|
+
const rlAny = this.rl;
|
|
432
|
+
const { cursor, line } = rlAny;
|
|
433
|
+
const prevTwoChars = cursor > 1 ? line.slice(cursor - 2, cursor) : '';
|
|
434
|
+
if (prevTwoChars === ' ') {
|
|
435
|
+
rlAny.line = line.slice(0, cursor - 2) + '\n\n' + line.slice(cursor);
|
|
436
|
+
rlAny._refreshLine();
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
this.detectPasting();
|
|
440
|
+
}
|
|
441
|
+
handleSigint() {
|
|
442
|
+
if ((0, terminal_1.isCommandRunning)()) {
|
|
443
|
+
(0, terminal_1.resetShell)();
|
|
444
|
+
}
|
|
445
|
+
if ('line' in this.rl) {
|
|
446
|
+
;
|
|
447
|
+
this.rl.line = '';
|
|
448
|
+
}
|
|
449
|
+
if (this.isReceivingResponse) {
|
|
450
|
+
this.handleStopResponse();
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
const now = Date.now();
|
|
454
|
+
if (now - this.lastSigintTime < 5000) {
|
|
455
|
+
this.handleExit();
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
this.lastSigintTime = now;
|
|
459
|
+
console.log('\nPress Ctrl-C again to exit');
|
|
460
|
+
this.rl.prompt();
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
handleEscKey() {
|
|
465
|
+
if (this.isReceivingResponse) {
|
|
466
|
+
this.handleStopResponse();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
handleStopResponse() {
|
|
470
|
+
console.log((0, picocolors_1.yellow)('\n[Response stopped by user]'));
|
|
471
|
+
this.isReceivingResponse = false;
|
|
472
|
+
if (this.stopResponse) {
|
|
473
|
+
this.stopResponse();
|
|
474
|
+
}
|
|
475
|
+
this.stopLoadingAnimation();
|
|
476
|
+
this.restoreCursor();
|
|
477
|
+
}
|
|
478
|
+
// ==================== Exit Handling ====================
|
|
479
|
+
handleExit() {
|
|
480
|
+
this.restoreCursor();
|
|
481
|
+
console.log('\n\n');
|
|
482
|
+
console.log(`${(0, string_1.pluralize)(this.client.sessionCreditsUsed, 'credit')} used this session.`);
|
|
483
|
+
if (this.client.limit && this.client.usage && this.client.nextQuotaReset) {
|
|
484
|
+
const daysUntilReset = Math.max(0, Math.floor((this.client.nextQuotaReset.getTime() - Date.now()) /
|
|
485
|
+
(1000 * 60 * 60 * 24)));
|
|
486
|
+
console.log(`${Math.max(0, this.client.limit - this.client.usage)} / ${this.client.limit} credits remaining. Renews in ${(0, string_1.pluralize)(daysUntilReset, 'day')}.`);
|
|
487
|
+
}
|
|
488
|
+
console.log((0, picocolors_1.green)('Codebuff out!'));
|
|
489
|
+
process.exit(0);
|
|
490
|
+
}
|
|
491
|
+
// ==================== Pasting Detection ====================
|
|
492
|
+
detectPasting() {
|
|
493
|
+
const currentTime = Date.now();
|
|
494
|
+
const timeDiff = currentTime - this.lastInputTime;
|
|
495
|
+
if (timeDiff < 10) {
|
|
496
|
+
this.consecutiveFastInputs++;
|
|
497
|
+
if (this.consecutiveFastInputs >= 2) {
|
|
498
|
+
this.isPasting = true;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
this.consecutiveFastInputs = 0;
|
|
503
|
+
if (this.isPasting) {
|
|
504
|
+
this.isPasting = false;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
this.lastInputTime = currentTime;
|
|
508
|
+
}
|
|
509
|
+
// ==================== Auto Commit Handling ====================
|
|
510
|
+
async autoCommitChanges() {
|
|
511
|
+
if ((0, git_1.hasStagedChanges)()) {
|
|
512
|
+
const stagedChanges = (0, git_1.getStagedChanges)();
|
|
513
|
+
if (!stagedChanges)
|
|
514
|
+
return;
|
|
515
|
+
const commitMessage = await this.client.generateCommitMessage(stagedChanges);
|
|
516
|
+
(0, git_1.commitChanges)(commitMessage);
|
|
517
|
+
return commitMessage;
|
|
518
|
+
}
|
|
519
|
+
return undefined;
|
|
520
|
+
}
|
|
521
|
+
// ==================== Diff Handling ====================
|
|
522
|
+
handleDiff() {
|
|
523
|
+
if (this.lastChanges.length === 0) {
|
|
524
|
+
console.log((0, picocolors_1.yellow)('No changes found in the last assistant response.'));
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
this.lastChanges.forEach((change) => {
|
|
528
|
+
console.log('-', change.filePath);
|
|
529
|
+
const lines = change.content
|
|
530
|
+
.split('\n')
|
|
531
|
+
.map((line) => (change.type === 'file' ? '+' + line : line));
|
|
532
|
+
lines.forEach((line) => {
|
|
533
|
+
if (line.startsWith('+')) {
|
|
534
|
+
console.log((0, picocolors_1.green)(line));
|
|
535
|
+
}
|
|
536
|
+
else if (line.startsWith('-')) {
|
|
537
|
+
console.log((0, picocolors_1.red)(line));
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
console.log(line);
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
}
|
|
532
545
|
}
|
|
533
546
|
exports.CLI = CLI;
|
|
534
547
|
//# sourceMappingURL=cli.js.map
|