codeep 1.2.85 → 1.2.87
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/renderer/App.d.ts +7 -7
- package/dist/renderer/App.js +115 -84
- package/dist/renderer/agentExecution.js +3 -26
- package/dist/utils/smartContext.js +9 -2
- package/package.json +1 -1
package/dist/renderer/App.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ export declare class App {
|
|
|
37
37
|
private scrollOffset;
|
|
38
38
|
private notification;
|
|
39
39
|
private notificationTimeout;
|
|
40
|
+
private pendingRender;
|
|
40
41
|
private spinnerFrame;
|
|
41
42
|
private spinnerInterval;
|
|
42
43
|
private isAgentRunning;
|
|
@@ -48,6 +49,7 @@ export declare class App {
|
|
|
48
49
|
private pasteInfo;
|
|
49
50
|
private pasteInfoOpen;
|
|
50
51
|
private codeBlockCounter;
|
|
52
|
+
private messageCache;
|
|
51
53
|
private helpOpen;
|
|
52
54
|
private helpScrollIndex;
|
|
53
55
|
private statusOpen;
|
|
@@ -116,18 +118,15 @@ export declare class App {
|
|
|
116
118
|
* Add a message
|
|
117
119
|
*/
|
|
118
120
|
addMessage(message: Message): void;
|
|
119
|
-
/**
|
|
120
|
-
* Set messages (for loading session)
|
|
121
|
-
*/
|
|
122
121
|
setMessages(messages: Message[]): void;
|
|
123
|
-
/**
|
|
124
|
-
* Clear messages
|
|
125
|
-
*/
|
|
126
122
|
clearMessages(): void;
|
|
127
123
|
/**
|
|
128
124
|
* Get all messages (for API history)
|
|
129
125
|
*/
|
|
130
|
-
getMessages():
|
|
126
|
+
getMessages(): Array<{
|
|
127
|
+
role: 'user' | 'assistant' | 'system';
|
|
128
|
+
content: string;
|
|
129
|
+
}>;
|
|
131
130
|
/**
|
|
132
131
|
* Scroll to a specific message by index
|
|
133
132
|
*/
|
|
@@ -333,6 +332,7 @@ export declare class App {
|
|
|
333
332
|
/**
|
|
334
333
|
* Render current screen
|
|
335
334
|
*/
|
|
335
|
+
scheduleRender(): void;
|
|
336
336
|
render(): void;
|
|
337
337
|
/**
|
|
338
338
|
* Render chat screen
|
package/dist/renderer/App.js
CHANGED
|
@@ -93,6 +93,8 @@ export class App {
|
|
|
93
93
|
scrollOffset = 0;
|
|
94
94
|
notification = '';
|
|
95
95
|
notificationTimeout = null;
|
|
96
|
+
// Render scheduling
|
|
97
|
+
pendingRender = false;
|
|
96
98
|
// Spinner animation state
|
|
97
99
|
spinnerFrame = 0;
|
|
98
100
|
spinnerInterval = null;
|
|
@@ -107,6 +109,8 @@ export class App {
|
|
|
107
109
|
pasteInfo = null;
|
|
108
110
|
pasteInfoOpen = false;
|
|
109
111
|
codeBlockCounter = 0; // Global code block counter for /copy numbering
|
|
112
|
+
// Message render cache: index → { lines, width, startBlock, blockCount }
|
|
113
|
+
messageCache = [];
|
|
110
114
|
// Inline help state
|
|
111
115
|
helpOpen = false;
|
|
112
116
|
helpScrollIndex = 0;
|
|
@@ -214,13 +218,21 @@ export class App {
|
|
|
214
218
|
this.screen.init();
|
|
215
219
|
this.input.start();
|
|
216
220
|
this.input.onKey((event) => this.handleKey(event));
|
|
217
|
-
this.screen.onResize(() =>
|
|
218
|
-
|
|
221
|
+
this.screen.onResize(() => {
|
|
222
|
+
this.messageCache = new Array(this.messages.length).fill(null);
|
|
223
|
+
this.scheduleRender();
|
|
224
|
+
});
|
|
225
|
+
this.scheduleRender();
|
|
219
226
|
}
|
|
220
227
|
/**
|
|
221
228
|
* Stop the application
|
|
222
229
|
*/
|
|
223
230
|
stop() {
|
|
231
|
+
this.stopSpinner();
|
|
232
|
+
if (this.notificationTimeout)
|
|
233
|
+
clearTimeout(this.notificationTimeout);
|
|
234
|
+
if (this.introInterval)
|
|
235
|
+
clearInterval(this.introInterval);
|
|
224
236
|
this.input.stop();
|
|
225
237
|
this.screen.cleanup();
|
|
226
238
|
}
|
|
@@ -229,30 +241,27 @@ export class App {
|
|
|
229
241
|
*/
|
|
230
242
|
addMessage(message) {
|
|
231
243
|
this.messages.push(message);
|
|
244
|
+
this.messageCache.push(null); // slot za novu poruku
|
|
232
245
|
this.scrollOffset = 0;
|
|
233
|
-
this.
|
|
246
|
+
this.scheduleRender();
|
|
234
247
|
}
|
|
235
|
-
/**
|
|
236
|
-
* Set messages (for loading session)
|
|
237
|
-
*/
|
|
238
248
|
setMessages(messages) {
|
|
239
249
|
this.messages = messages;
|
|
250
|
+
this.messageCache = new Array(messages.length).fill(null);
|
|
240
251
|
this.scrollOffset = 0;
|
|
241
|
-
this.
|
|
252
|
+
this.scheduleRender();
|
|
242
253
|
}
|
|
243
|
-
/**
|
|
244
|
-
* Clear messages
|
|
245
|
-
*/
|
|
246
254
|
clearMessages() {
|
|
247
255
|
this.messages = [];
|
|
256
|
+
this.messageCache = [];
|
|
248
257
|
this.scrollOffset = 0;
|
|
249
|
-
this.
|
|
258
|
+
this.scheduleRender();
|
|
250
259
|
}
|
|
251
260
|
/**
|
|
252
261
|
* Get all messages (for API history)
|
|
253
262
|
*/
|
|
254
263
|
getMessages() {
|
|
255
|
-
return this.messages;
|
|
264
|
+
return this.messages.filter(m => m.role !== 'welcome');
|
|
256
265
|
}
|
|
257
266
|
/**
|
|
258
267
|
* Scroll to a specific message by index
|
|
@@ -280,7 +289,7 @@ export class App {
|
|
|
280
289
|
const visibleLines = height - 12; // Approximate visible area
|
|
281
290
|
// Set scroll offset to show the target message near the top
|
|
282
291
|
this.scrollOffset = Math.max(0, totalLines - targetStartLine - Math.floor(visibleLines / 2));
|
|
283
|
-
this.
|
|
292
|
+
this.scheduleRender();
|
|
284
293
|
this.notify(`Jumped to message #${messageIndex + 1}`);
|
|
285
294
|
}
|
|
286
295
|
/**
|
|
@@ -299,14 +308,14 @@ export class App {
|
|
|
299
308
|
this.isLoading = false;
|
|
300
309
|
this.streamingContent = '';
|
|
301
310
|
this.startSpinner();
|
|
302
|
-
this.
|
|
311
|
+
this.scheduleRender();
|
|
303
312
|
}
|
|
304
313
|
/**
|
|
305
314
|
* Add streaming chunk
|
|
306
315
|
*/
|
|
307
316
|
addStreamChunk(chunk) {
|
|
308
317
|
this.streamingContent += chunk;
|
|
309
|
-
this.
|
|
318
|
+
this.scheduleRender();
|
|
310
319
|
}
|
|
311
320
|
/**
|
|
312
321
|
* End streaming
|
|
@@ -317,11 +326,12 @@ export class App {
|
|
|
317
326
|
role: 'assistant',
|
|
318
327
|
content: this.streamingContent,
|
|
319
328
|
});
|
|
329
|
+
this.messageCache.push(null);
|
|
320
330
|
}
|
|
321
331
|
this.streamingContent = '';
|
|
322
332
|
this.isStreaming = false;
|
|
323
333
|
this.stopSpinner();
|
|
324
|
-
this.
|
|
334
|
+
this.scheduleRender();
|
|
325
335
|
}
|
|
326
336
|
/**
|
|
327
337
|
* Set loading state
|
|
@@ -334,7 +344,7 @@ export class App {
|
|
|
334
344
|
else {
|
|
335
345
|
this.stopSpinner();
|
|
336
346
|
}
|
|
337
|
-
this.
|
|
347
|
+
this.scheduleRender();
|
|
338
348
|
}
|
|
339
349
|
/**
|
|
340
350
|
* Start spinner animation
|
|
@@ -374,7 +384,7 @@ export class App {
|
|
|
374
384
|
this.isLoading = false; // Ensure loading is cleared when agent finishes
|
|
375
385
|
this.stopSpinner();
|
|
376
386
|
}
|
|
377
|
-
this.
|
|
387
|
+
this.scheduleRender();
|
|
378
388
|
}
|
|
379
389
|
/**
|
|
380
390
|
* Update agent progress
|
|
@@ -384,7 +394,7 @@ export class App {
|
|
|
384
394
|
if (action) {
|
|
385
395
|
this.agentActions.push(action);
|
|
386
396
|
}
|
|
387
|
-
this.
|
|
397
|
+
this.scheduleRender();
|
|
388
398
|
}
|
|
389
399
|
setAgentMaxIterations(max) {
|
|
390
400
|
this.agentMaxIterations = max;
|
|
@@ -394,11 +404,11 @@ export class App {
|
|
|
394
404
|
*/
|
|
395
405
|
setAgentThinking(text) {
|
|
396
406
|
this.agentThinking = text;
|
|
397
|
-
this.
|
|
407
|
+
this.scheduleRender();
|
|
398
408
|
}
|
|
399
409
|
setAgentWaitingForAI(waiting) {
|
|
400
410
|
this.agentWaitingForAI = waiting;
|
|
401
|
-
this.
|
|
411
|
+
this.scheduleRender();
|
|
402
412
|
}
|
|
403
413
|
/**
|
|
404
414
|
* Paste from system clipboard (Ctrl+V)
|
|
@@ -429,7 +439,7 @@ export class App {
|
|
|
429
439
|
// Small paste - just add to input directly
|
|
430
440
|
this.editor.insert(text);
|
|
431
441
|
this.updateAutocomplete();
|
|
432
|
-
this.
|
|
442
|
+
this.scheduleRender();
|
|
433
443
|
return;
|
|
434
444
|
}
|
|
435
445
|
// Large paste - show info box
|
|
@@ -441,7 +451,7 @@ export class App {
|
|
|
441
451
|
fullText: text,
|
|
442
452
|
};
|
|
443
453
|
this.pasteInfoOpen = true;
|
|
444
|
-
this.
|
|
454
|
+
this.scheduleRender();
|
|
445
455
|
}
|
|
446
456
|
/**
|
|
447
457
|
* Handle paste info key events
|
|
@@ -452,7 +462,7 @@ export class App {
|
|
|
452
462
|
this.pasteInfo = null;
|
|
453
463
|
this.pasteInfoOpen = false;
|
|
454
464
|
this.notify('Paste cancelled');
|
|
455
|
-
this.
|
|
465
|
+
this.scheduleRender();
|
|
456
466
|
return;
|
|
457
467
|
}
|
|
458
468
|
if (event.key === 'enter' || event.key === 'y') {
|
|
@@ -463,7 +473,7 @@ export class App {
|
|
|
463
473
|
}
|
|
464
474
|
this.pasteInfo = null;
|
|
465
475
|
this.pasteInfoOpen = false;
|
|
466
|
-
this.
|
|
476
|
+
this.scheduleRender();
|
|
467
477
|
return;
|
|
468
478
|
}
|
|
469
479
|
if (event.key === 's') {
|
|
@@ -472,7 +482,7 @@ export class App {
|
|
|
472
482
|
const text = this.pasteInfo.fullText;
|
|
473
483
|
this.pasteInfo = null;
|
|
474
484
|
this.pasteInfoOpen = false;
|
|
475
|
-
this.
|
|
485
|
+
this.scheduleRender();
|
|
476
486
|
// Submit directly
|
|
477
487
|
this.addMessage({ role: 'user', content: text });
|
|
478
488
|
this.setLoading(true);
|
|
@@ -489,13 +499,13 @@ export class App {
|
|
|
489
499
|
*/
|
|
490
500
|
notify(message, duration = 3000) {
|
|
491
501
|
this.notification = message;
|
|
492
|
-
this.
|
|
502
|
+
this.scheduleRender();
|
|
493
503
|
if (this.notificationTimeout) {
|
|
494
504
|
clearTimeout(this.notificationTimeout);
|
|
495
505
|
}
|
|
496
506
|
this.notificationTimeout = setTimeout(() => {
|
|
497
507
|
this.notification = '';
|
|
498
|
-
this.
|
|
508
|
+
this.scheduleRender();
|
|
499
509
|
}, duration);
|
|
500
510
|
}
|
|
501
511
|
/**
|
|
@@ -513,7 +523,7 @@ export class App {
|
|
|
513
523
|
this.menuIndex = 0;
|
|
514
524
|
this.menuCallback = (item) => callback(parseInt(item.key, 10));
|
|
515
525
|
this.menuOpen = true;
|
|
516
|
-
this.
|
|
526
|
+
this.scheduleRender();
|
|
517
527
|
}
|
|
518
528
|
/**
|
|
519
529
|
* Show settings (inline, below status bar)
|
|
@@ -521,7 +531,7 @@ export class App {
|
|
|
521
531
|
showSettings() {
|
|
522
532
|
this.settingsState = { selectedIndex: 0, editing: false, editValue: '' };
|
|
523
533
|
this.settingsOpen = true;
|
|
524
|
-
this.
|
|
534
|
+
this.scheduleRender();
|
|
525
535
|
}
|
|
526
536
|
/**
|
|
527
537
|
* Show confirmation dialog
|
|
@@ -530,7 +540,7 @@ export class App {
|
|
|
530
540
|
this.confirmOptions = options;
|
|
531
541
|
this.confirmSelection = 'no'; // Default to No for safety
|
|
532
542
|
this.confirmOpen = true;
|
|
533
|
-
this.
|
|
543
|
+
this.scheduleRender();
|
|
534
544
|
}
|
|
535
545
|
/**
|
|
536
546
|
* Show permission dialog (inline, below status bar)
|
|
@@ -541,7 +551,7 @@ export class App {
|
|
|
541
551
|
this.permissionIndex = 0;
|
|
542
552
|
this.permissionCallback = callback;
|
|
543
553
|
this.permissionOpen = true;
|
|
544
|
-
this.
|
|
554
|
+
this.scheduleRender();
|
|
545
555
|
}
|
|
546
556
|
/**
|
|
547
557
|
* Show session picker (inline, below status bar)
|
|
@@ -553,7 +563,7 @@ export class App {
|
|
|
553
563
|
this.sessionPickerDeleteCallback = deleteCallback || null;
|
|
554
564
|
this.sessionPickerDeleteMode = false;
|
|
555
565
|
this.sessionPickerOpen = true;
|
|
556
|
-
this.
|
|
566
|
+
this.scheduleRender();
|
|
557
567
|
}
|
|
558
568
|
/**
|
|
559
569
|
* Show search screen
|
|
@@ -564,7 +574,7 @@ export class App {
|
|
|
564
574
|
this.searchIndex = 0;
|
|
565
575
|
this.searchCallback = callback;
|
|
566
576
|
this.searchOpen = true;
|
|
567
|
-
this.
|
|
577
|
+
this.scheduleRender();
|
|
568
578
|
}
|
|
569
579
|
/**
|
|
570
580
|
* Show export screen
|
|
@@ -573,7 +583,7 @@ export class App {
|
|
|
573
583
|
this.exportIndex = 0;
|
|
574
584
|
this.exportCallback = callback;
|
|
575
585
|
this.exportOpen = true;
|
|
576
|
-
this.
|
|
586
|
+
this.scheduleRender();
|
|
577
587
|
}
|
|
578
588
|
/**
|
|
579
589
|
* Show logout picker
|
|
@@ -583,7 +593,7 @@ export class App {
|
|
|
583
593
|
this.logoutIndex = 0;
|
|
584
594
|
this.logoutCallback = callback;
|
|
585
595
|
this.logoutOpen = true;
|
|
586
|
-
this.
|
|
596
|
+
this.scheduleRender();
|
|
587
597
|
}
|
|
588
598
|
/**
|
|
589
599
|
* Start intro animation
|
|
@@ -598,7 +608,7 @@ export class App {
|
|
|
598
608
|
const noiseInterval = setInterval(() => {
|
|
599
609
|
noiseCount++;
|
|
600
610
|
this.introProgress = Math.random();
|
|
601
|
-
this.
|
|
611
|
+
this.scheduleRender();
|
|
602
612
|
if (noiseCount >= 10) {
|
|
603
613
|
clearInterval(noiseInterval);
|
|
604
614
|
// Phase 2: Decryption animation (1500ms)
|
|
@@ -608,14 +618,14 @@ export class App {
|
|
|
608
618
|
this.introInterval = setInterval(() => {
|
|
609
619
|
const elapsed = Date.now() - startTime;
|
|
610
620
|
this.introProgress = Math.min(elapsed / duration, 1);
|
|
611
|
-
this.
|
|
621
|
+
this.scheduleRender();
|
|
612
622
|
if (this.introProgress >= 1) {
|
|
613
623
|
this.finishIntro();
|
|
614
624
|
}
|
|
615
625
|
}, 16); // ~60 FPS
|
|
616
626
|
}
|
|
617
627
|
}, 50);
|
|
618
|
-
this.
|
|
628
|
+
this.scheduleRender();
|
|
619
629
|
}
|
|
620
630
|
/**
|
|
621
631
|
* Skip intro animation
|
|
@@ -637,7 +647,7 @@ export class App {
|
|
|
637
647
|
this.introCallback();
|
|
638
648
|
this.introCallback = null;
|
|
639
649
|
}
|
|
640
|
-
this.
|
|
650
|
+
this.scheduleRender();
|
|
641
651
|
}
|
|
642
652
|
/**
|
|
643
653
|
* Show inline login dialog
|
|
@@ -650,7 +660,7 @@ export class App {
|
|
|
650
660
|
this.loginError = '';
|
|
651
661
|
this.loginCallback = callback;
|
|
652
662
|
this.loginOpen = true;
|
|
653
|
-
this.
|
|
663
|
+
this.scheduleRender();
|
|
654
664
|
}
|
|
655
665
|
/**
|
|
656
666
|
* Reinitialize screen (after external screen takeover)
|
|
@@ -659,7 +669,7 @@ export class App {
|
|
|
659
669
|
this.screen.init();
|
|
660
670
|
this.input.start();
|
|
661
671
|
this.input.onKey((event) => this.handleKey(event));
|
|
662
|
-
this.
|
|
672
|
+
this.scheduleRender();
|
|
663
673
|
}
|
|
664
674
|
/**
|
|
665
675
|
* Show inline menu (renders below status bar)
|
|
@@ -673,7 +683,7 @@ export class App {
|
|
|
673
683
|
// Find current value index
|
|
674
684
|
const currentIndex = items.findIndex(item => item.key === currentValue);
|
|
675
685
|
this.menuIndex = currentIndex >= 0 ? currentIndex : 0;
|
|
676
|
-
this.
|
|
686
|
+
this.scheduleRender();
|
|
677
687
|
}
|
|
678
688
|
/**
|
|
679
689
|
* Handle keyboard input
|
|
@@ -760,7 +770,7 @@ export class App {
|
|
|
760
770
|
if (event.key === 'escape') {
|
|
761
771
|
if (this.showAutocomplete) {
|
|
762
772
|
this.showAutocomplete = false;
|
|
763
|
-
this.
|
|
773
|
+
this.scheduleRender();
|
|
764
774
|
return;
|
|
765
775
|
}
|
|
766
776
|
// In multiline mode, Escape submits the buffered input
|
|
@@ -783,12 +793,12 @@ export class App {
|
|
|
783
793
|
if (this.showAutocomplete) {
|
|
784
794
|
if (event.key === 'up') {
|
|
785
795
|
this.autocompleteIndex = Math.max(0, this.autocompleteIndex - 1);
|
|
786
|
-
this.
|
|
796
|
+
this.scheduleRender();
|
|
787
797
|
return;
|
|
788
798
|
}
|
|
789
799
|
if (event.key === 'down') {
|
|
790
800
|
this.autocompleteIndex = Math.min(this.autocompleteItems.length - 1, this.autocompleteIndex + 1);
|
|
791
|
-
this.
|
|
801
|
+
this.scheduleRender();
|
|
792
802
|
return;
|
|
793
803
|
}
|
|
794
804
|
if (event.key === 'tab' || event.key === 'enter') {
|
|
@@ -797,7 +807,7 @@ export class App {
|
|
|
797
807
|
const selected = this.autocompleteItems[this.autocompleteIndex];
|
|
798
808
|
this.editor.setValue('/' + selected + ' ');
|
|
799
809
|
this.showAutocomplete = false;
|
|
800
|
-
this.
|
|
810
|
+
this.scheduleRender();
|
|
801
811
|
return;
|
|
802
812
|
}
|
|
803
813
|
}
|
|
@@ -816,69 +826,69 @@ export class App {
|
|
|
816
826
|
// Ctrl+A - go to beginning of line
|
|
817
827
|
if (event.ctrl && event.key === 'a') {
|
|
818
828
|
this.editor.setCursorPos(0);
|
|
819
|
-
this.
|
|
829
|
+
this.scheduleRender();
|
|
820
830
|
return;
|
|
821
831
|
}
|
|
822
832
|
// Ctrl+E - go to end of line
|
|
823
833
|
if (event.ctrl && event.key === 'e') {
|
|
824
834
|
this.editor.setCursorPos(this.editor.getValue().length);
|
|
825
|
-
this.
|
|
835
|
+
this.scheduleRender();
|
|
826
836
|
return;
|
|
827
837
|
}
|
|
828
838
|
// Ctrl+U - clear line
|
|
829
839
|
if (event.ctrl && event.key === 'u') {
|
|
830
840
|
this.editor.clear();
|
|
831
841
|
this.showAutocomplete = false;
|
|
832
|
-
this.
|
|
842
|
+
this.scheduleRender();
|
|
833
843
|
return;
|
|
834
844
|
}
|
|
835
845
|
// Ctrl+W - delete word backward
|
|
836
846
|
if (event.ctrl && event.key === 'w') {
|
|
837
847
|
this.editor.deleteWordBackward();
|
|
838
848
|
this.updateAutocomplete();
|
|
839
|
-
this.
|
|
849
|
+
this.scheduleRender();
|
|
840
850
|
return;
|
|
841
851
|
}
|
|
842
852
|
// Ctrl+K - delete to end of line
|
|
843
853
|
if (event.ctrl && event.key === 'k') {
|
|
844
854
|
this.editor.deleteToEnd();
|
|
845
855
|
this.updateAutocomplete();
|
|
846
|
-
this.
|
|
856
|
+
this.scheduleRender();
|
|
847
857
|
return;
|
|
848
858
|
}
|
|
849
859
|
// Page up/down for scrolling chat history
|
|
850
860
|
if (event.key === 'pageup') {
|
|
851
861
|
// Scroll up (show older messages)
|
|
852
862
|
this.scrollOffset += 10;
|
|
853
|
-
this.
|
|
863
|
+
this.scheduleRender();
|
|
854
864
|
return;
|
|
855
865
|
}
|
|
856
866
|
if (event.key === 'pagedown') {
|
|
857
867
|
// Scroll down (show newer messages)
|
|
858
868
|
this.scrollOffset = Math.max(0, this.scrollOffset - 10);
|
|
859
|
-
this.
|
|
869
|
+
this.scheduleRender();
|
|
860
870
|
return;
|
|
861
871
|
}
|
|
862
872
|
// Arrow up/down can also scroll when input is empty
|
|
863
873
|
if (event.key === 'up' && !this.editor.getValue() && !this.showAutocomplete) {
|
|
864
874
|
this.scrollOffset += 3;
|
|
865
|
-
this.
|
|
875
|
+
this.scheduleRender();
|
|
866
876
|
return;
|
|
867
877
|
}
|
|
868
878
|
if (event.key === 'down' && !this.editor.getValue() && !this.showAutocomplete && this.scrollOffset > 0) {
|
|
869
879
|
this.scrollOffset = Math.max(0, this.scrollOffset - 3);
|
|
870
|
-
this.
|
|
880
|
+
this.scheduleRender();
|
|
871
881
|
return;
|
|
872
882
|
}
|
|
873
883
|
// Mouse scroll
|
|
874
884
|
if (event.key === 'scrollup') {
|
|
875
885
|
this.scrollOffset += 3;
|
|
876
|
-
this.
|
|
886
|
+
this.scheduleRender();
|
|
877
887
|
return;
|
|
878
888
|
}
|
|
879
889
|
if (event.key === 'scrolldown') {
|
|
880
890
|
this.scrollOffset = Math.max(0, this.scrollOffset - 3);
|
|
881
|
-
this.
|
|
891
|
+
this.scheduleRender();
|
|
882
892
|
return;
|
|
883
893
|
}
|
|
884
894
|
// Ignore other mouse events
|
|
@@ -891,13 +901,13 @@ export class App {
|
|
|
891
901
|
// Backslash continuation: if line ends with \, add newline instead of submitting
|
|
892
902
|
if (rawValue.endsWith('\\')) {
|
|
893
903
|
this.editor.setValue(rawValue.slice(0, -1) + '\n');
|
|
894
|
-
this.
|
|
904
|
+
this.scheduleRender();
|
|
895
905
|
return;
|
|
896
906
|
}
|
|
897
907
|
// Multiline mode: Enter adds newline, Ctrl+Enter submits
|
|
898
908
|
if (this.isMultilineMode && !event.ctrl) {
|
|
899
909
|
this.editor.insert('\n');
|
|
900
|
-
this.
|
|
910
|
+
this.scheduleRender();
|
|
901
911
|
return;
|
|
902
912
|
}
|
|
903
913
|
this.submitInput();
|
|
@@ -912,7 +922,7 @@ export class App {
|
|
|
912
922
|
if (this.editor.handleKey(event)) {
|
|
913
923
|
// Update autocomplete based on input
|
|
914
924
|
this.updateAutocomplete();
|
|
915
|
-
this.
|
|
925
|
+
this.scheduleRender();
|
|
916
926
|
}
|
|
917
927
|
}
|
|
918
928
|
/**
|
|
@@ -938,7 +948,7 @@ export class App {
|
|
|
938
948
|
handleInlineStatusKey(event) {
|
|
939
949
|
handleInlineStatusKey(event, {
|
|
940
950
|
close: () => { this.statusOpen = false; },
|
|
941
|
-
render: () => this.
|
|
951
|
+
render: () => this.scheduleRender(),
|
|
942
952
|
});
|
|
943
953
|
}
|
|
944
954
|
/**
|
|
@@ -949,7 +959,7 @@ export class App {
|
|
|
949
959
|
scrollIndex: this.helpScrollIndex,
|
|
950
960
|
setScrollIndex: (v) => { this.helpScrollIndex = v; },
|
|
951
961
|
close: () => { this.helpOpen = false; },
|
|
952
|
-
render: () => this.
|
|
962
|
+
render: () => this.scheduleRender(),
|
|
953
963
|
});
|
|
954
964
|
}
|
|
955
965
|
/**
|
|
@@ -964,7 +974,7 @@ export class App {
|
|
|
964
974
|
if (result.notify) {
|
|
965
975
|
this.notify(result.notify);
|
|
966
976
|
}
|
|
967
|
-
this.
|
|
977
|
+
this.scheduleRender();
|
|
968
978
|
}
|
|
969
979
|
/**
|
|
970
980
|
* Handle search screen keys
|
|
@@ -985,7 +995,7 @@ export class App {
|
|
|
985
995
|
},
|
|
986
996
|
onRender: () => {
|
|
987
997
|
this.searchIndex = state.searchIndex;
|
|
988
|
-
this.
|
|
998
|
+
this.scheduleRender();
|
|
989
999
|
},
|
|
990
1000
|
onResult: (messageIndex) => {
|
|
991
1001
|
if (callback) {
|
|
@@ -1007,7 +1017,7 @@ export class App {
|
|
|
1007
1017
|
this.exportOpen = state.exportOpen;
|
|
1008
1018
|
this.exportIndex = state.exportIndex;
|
|
1009
1019
|
this.exportCallback = state.exportCallback;
|
|
1010
|
-
this.
|
|
1020
|
+
this.scheduleRender();
|
|
1011
1021
|
};
|
|
1012
1022
|
handleExportKeyComponent(event, state, {
|
|
1013
1023
|
onClose: syncState,
|
|
@@ -1038,7 +1048,7 @@ export class App {
|
|
|
1038
1048
|
};
|
|
1039
1049
|
handleLogoutKeyComponent(event, state, {
|
|
1040
1050
|
onClose: () => { },
|
|
1041
|
-
onRender: () => { syncState(); this.
|
|
1051
|
+
onRender: () => { syncState(); this.scheduleRender(); },
|
|
1042
1052
|
onSelect: () => { },
|
|
1043
1053
|
});
|
|
1044
1054
|
syncState();
|
|
@@ -1063,7 +1073,7 @@ export class App {
|
|
|
1063
1073
|
if (callback)
|
|
1064
1074
|
callback(result);
|
|
1065
1075
|
},
|
|
1066
|
-
render: () => this.
|
|
1076
|
+
render: () => this.scheduleRender(),
|
|
1067
1077
|
});
|
|
1068
1078
|
}
|
|
1069
1079
|
/**
|
|
@@ -1081,7 +1091,7 @@ export class App {
|
|
|
1081
1091
|
if (selected && callback)
|
|
1082
1092
|
callback(selected);
|
|
1083
1093
|
},
|
|
1084
|
-
render: () => this.
|
|
1094
|
+
render: () => this.scheduleRender(),
|
|
1085
1095
|
});
|
|
1086
1096
|
}
|
|
1087
1097
|
/**
|
|
@@ -1098,7 +1108,7 @@ export class App {
|
|
|
1098
1108
|
if (callback)
|
|
1099
1109
|
callback(level);
|
|
1100
1110
|
},
|
|
1101
|
-
render: () => this.
|
|
1111
|
+
render: () => this.scheduleRender(),
|
|
1102
1112
|
});
|
|
1103
1113
|
}
|
|
1104
1114
|
handleInlineSessionPickerKey(event) {
|
|
@@ -1123,13 +1133,13 @@ export class App {
|
|
|
1123
1133
|
this.sessionPickerDeleteCallback(name);
|
|
1124
1134
|
},
|
|
1125
1135
|
notify: (msg) => this.notify(msg),
|
|
1126
|
-
render: () => this.
|
|
1136
|
+
render: () => this.scheduleRender(),
|
|
1127
1137
|
});
|
|
1128
1138
|
}
|
|
1129
1139
|
handleInlineConfirmKey(event) {
|
|
1130
1140
|
if (!this.confirmOptions) {
|
|
1131
1141
|
this.confirmOpen = false;
|
|
1132
|
-
this.
|
|
1142
|
+
this.scheduleRender();
|
|
1133
1143
|
return;
|
|
1134
1144
|
}
|
|
1135
1145
|
handleInlineConfirmKey(event, {
|
|
@@ -1145,7 +1155,7 @@ export class App {
|
|
|
1145
1155
|
else if (options.onCancel)
|
|
1146
1156
|
options.onCancel();
|
|
1147
1157
|
},
|
|
1148
|
-
render: () => this.
|
|
1158
|
+
render: () => this.scheduleRender(),
|
|
1149
1159
|
});
|
|
1150
1160
|
}
|
|
1151
1161
|
/**
|
|
@@ -1181,11 +1191,11 @@ export class App {
|
|
|
1181
1191
|
case 'help':
|
|
1182
1192
|
this.helpOpen = true;
|
|
1183
1193
|
this.helpScrollIndex = 0;
|
|
1184
|
-
this.
|
|
1194
|
+
this.scheduleRender();
|
|
1185
1195
|
break;
|
|
1186
1196
|
case 'status':
|
|
1187
1197
|
this.statusOpen = true;
|
|
1188
|
-
this.
|
|
1198
|
+
this.scheduleRender();
|
|
1189
1199
|
break;
|
|
1190
1200
|
case 'clear':
|
|
1191
1201
|
this.clearMessages();
|
|
@@ -1211,6 +1221,15 @@ export class App {
|
|
|
1211
1221
|
/**
|
|
1212
1222
|
* Render current screen
|
|
1213
1223
|
*/
|
|
1224
|
+
scheduleRender() {
|
|
1225
|
+
if (this.pendingRender)
|
|
1226
|
+
return;
|
|
1227
|
+
this.pendingRender = true;
|
|
1228
|
+
setImmediate(() => {
|
|
1229
|
+
this.pendingRender = false;
|
|
1230
|
+
this.render();
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1214
1233
|
render() {
|
|
1215
1234
|
// Intro animation takes over the whole screen
|
|
1216
1235
|
if (this.showIntro) {
|
|
@@ -1896,10 +1915,9 @@ export class App {
|
|
|
1896
1915
|
// Top border: gradient line with gradient title embedded
|
|
1897
1916
|
const titleInner = ` ${spinner} AGENT `;
|
|
1898
1917
|
const titlePadLeft = 2;
|
|
1899
|
-
const
|
|
1900
|
-
const lineLeft = lineChar.repeat(titlePadLeft);
|
|
1918
|
+
const lineLeft = PRIMARY_COLOR + '─'.repeat(titlePadLeft) + style.reset;
|
|
1901
1919
|
const titleColored = PRIMARY_COLOR + style.bold + titleInner + style.reset;
|
|
1902
|
-
const lineRight =
|
|
1920
|
+
const lineRight = PRIMARY_COLOR + '─'.repeat(Math.max(0, width - titlePadLeft - titleInner.length - 1)) + style.reset;
|
|
1903
1921
|
this.screen.write(0, y, lineLeft + titleColored + lineRight);
|
|
1904
1922
|
y++;
|
|
1905
1923
|
// Current action line
|
|
@@ -2102,11 +2120,24 @@ export class App {
|
|
|
2102
2120
|
allLines.push({ text: ' Codeep', style: PRIMARY_COLOR, raw: false });
|
|
2103
2121
|
allLines.push({ text: '', style: '' });
|
|
2104
2122
|
}
|
|
2105
|
-
for (
|
|
2106
|
-
const
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2123
|
+
for (let i = 0; i < this.messages.length; i++) {
|
|
2124
|
+
const msg = this.messages[i];
|
|
2125
|
+
const cached = this.messageCache[i];
|
|
2126
|
+
if (cached && cached.width === width && cached.startBlock === this.codeBlockCounter) {
|
|
2127
|
+
// Cache hit — preskoči formatiranje
|
|
2128
|
+
this.codeBlockCounter += cached.blockCount;
|
|
2129
|
+
allLines.push(...cached.lines);
|
|
2130
|
+
}
|
|
2131
|
+
else {
|
|
2132
|
+
// Cache miss — formatiraj i spremi
|
|
2133
|
+
const startBlock = this.codeBlockCounter;
|
|
2134
|
+
const msgLines = msg.role === 'welcome'
|
|
2135
|
+
? this.formatWelcomeMessage(msg.content)
|
|
2136
|
+
: this.formatMessage(msg.role, msg.content, width);
|
|
2137
|
+
const blockCount = this.codeBlockCounter - startBlock;
|
|
2138
|
+
this.messageCache[i] = { lines: msgLines, width, startBlock, blockCount };
|
|
2139
|
+
allLines.push(...msgLines);
|
|
2140
|
+
}
|
|
2110
2141
|
}
|
|
2111
2142
|
if (this.isStreaming && this.streamingContent) {
|
|
2112
2143
|
const streamLines = this.formatMessage('assistant', this.streamingContent + '▊', width);
|
|
@@ -2196,7 +2227,7 @@ export class App {
|
|
|
2196
2227
|
firstPrefix = ' ';
|
|
2197
2228
|
}
|
|
2198
2229
|
else {
|
|
2199
|
-
firstPrefix =
|
|
2230
|
+
firstPrefix = PRIMARY_COLOR + '\u25b8 ' + style.reset;
|
|
2200
2231
|
}
|
|
2201
2232
|
const codeBlockRegex = /```([^\n]*)\n([\s\S]*?)```/g;
|
|
2202
2233
|
let lastIndex = 0;
|
|
@@ -146,7 +146,8 @@ export async function executeAgentTask(task, dryRun, ctx) {
|
|
|
146
146
|
const fileContext = ctx.formatAddedFilesContext();
|
|
147
147
|
const enrichedTask = fileContext ? fileContext + task : task;
|
|
148
148
|
// Show N/M progress in status bar
|
|
149
|
-
|
|
149
|
+
const rawIterations = config.get('agentMaxIterations') || 100;
|
|
150
|
+
app.setAgentMaxIterations(Math.max(5, Math.min(500, rawIterations)));
|
|
150
151
|
const result = await runAgent(enrichedTask, context, {
|
|
151
152
|
dryRun,
|
|
152
153
|
chatHistory: app.getChatHistory(),
|
|
@@ -221,31 +222,7 @@ export async function executeAgentTask(task, dryRun, ctx) {
|
|
|
221
222
|
const filePath = tool.parameters.path;
|
|
222
223
|
app.addMessage({ role: 'system', content: `**Delete** \`${filePath}\`` });
|
|
223
224
|
}
|
|
224
|
-
|
|
225
|
-
const filePath = tool.parameters.path || shortTarget;
|
|
226
|
-
if (filePath)
|
|
227
|
-
app.addMessage({ role: 'system', content: `**Reading** \`${filePath}\`` });
|
|
228
|
-
}
|
|
229
|
-
else if (actionType === 'search') {
|
|
230
|
-
const pattern = tool.parameters.pattern || tool.parameters.query || shortTarget;
|
|
231
|
-
if (pattern)
|
|
232
|
-
app.addMessage({ role: 'system', content: `**Searching** for \`${pattern}\`` });
|
|
233
|
-
}
|
|
234
|
-
else if (actionType === 'list') {
|
|
235
|
-
const dirPath = tool.parameters.path || shortTarget;
|
|
236
|
-
if (dirPath)
|
|
237
|
-
app.addMessage({ role: 'system', content: `**Listing** \`${dirPath}\`` });
|
|
238
|
-
}
|
|
239
|
-
else if (actionType === 'fetch') {
|
|
240
|
-
const url = tool.parameters.url || shortTarget;
|
|
241
|
-
if (url)
|
|
242
|
-
app.addMessage({ role: 'system', content: `**Fetching** \`${url}\`` });
|
|
243
|
-
}
|
|
244
|
-
else if (actionType === 'command') {
|
|
245
|
-
const cmd = tool.parameters.command || shortTarget;
|
|
246
|
-
if (cmd)
|
|
247
|
-
app.addMessage({ role: 'system', content: `**Running** \`${cmd}\`` });
|
|
248
|
-
}
|
|
225
|
+
// read/search/list/fetch/command — setAgentThinking() above is enough, no chat message needed
|
|
249
226
|
},
|
|
250
227
|
onToolResult: (result, toolCall) => {
|
|
251
228
|
const toolName = toolCall.tool.toLowerCase();
|
|
@@ -83,8 +83,15 @@ function extractImports(content, ext) {
|
|
|
83
83
|
}
|
|
84
84
|
return imports;
|
|
85
85
|
}
|
|
86
|
-
//
|
|
86
|
+
// LRU cache for resolveImportPath — bounded to prevent memory growth in long sessions
|
|
87
|
+
const IMPORT_CACHE_MAX = 1000;
|
|
87
88
|
const importResolutionCache = new Map();
|
|
89
|
+
function importCacheSet(key, value) {
|
|
90
|
+
if (importResolutionCache.size >= IMPORT_CACHE_MAX) {
|
|
91
|
+
importResolutionCache.delete(importResolutionCache.keys().next().value);
|
|
92
|
+
}
|
|
93
|
+
importResolutionCache.set(key, value);
|
|
94
|
+
}
|
|
88
95
|
/**
|
|
89
96
|
* Resolve import path to actual file path.
|
|
90
97
|
* Results are cached by (fromFile, importPath) to avoid O(n²) disk I/O.
|
|
@@ -113,7 +120,7 @@ function resolveImportPath(importPath, fromFile, projectRoot) {
|
|
|
113
120
|
const resolved = join(fromDir, importPath);
|
|
114
121
|
result = resolveWithExtensions(resolved, ext);
|
|
115
122
|
}
|
|
116
|
-
|
|
123
|
+
importCacheSet(cacheKey, result);
|
|
117
124
|
return result;
|
|
118
125
|
}
|
|
119
126
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeep",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.87",
|
|
4
4
|
"description": "AI-powered coding assistant built for the terminal. Multiple LLM providers, project-aware context, and a seamless development workflow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|