erosolar-cli 1.7.192 → 1.7.194

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/bin/selfTest.d.ts.map +1 -1
  2. package/dist/bin/selfTest.js +3 -105
  3. package/dist/bin/selfTest.js.map +1 -1
  4. package/dist/contracts/module-schema.json +1 -1
  5. package/dist/core/performanceMonitor.d.ts +18 -0
  6. package/dist/core/performanceMonitor.d.ts.map +1 -1
  7. package/dist/core/performanceMonitor.js.map +1 -1
  8. package/dist/core/schemaValidator.d.ts +38 -0
  9. package/dist/core/schemaValidator.d.ts.map +1 -1
  10. package/dist/core/schemaValidator.js +62 -0
  11. package/dist/core/schemaValidator.js.map +1 -1
  12. package/dist/core/testUtils.d.ts +121 -0
  13. package/dist/core/testUtils.d.ts.map +1 -0
  14. package/dist/core/testUtils.js +235 -0
  15. package/dist/core/testUtils.js.map +1 -0
  16. package/dist/core/toolPreconditions.d.ts.map +1 -1
  17. package/dist/core/toolPreconditions.js +55 -3
  18. package/dist/core/toolPreconditions.js.map +1 -1
  19. package/dist/core/toolRuntime.d.ts +46 -0
  20. package/dist/core/toolRuntime.d.ts.map +1 -1
  21. package/dist/core/toolRuntime.js +37 -12
  22. package/dist/core/toolRuntime.js.map +1 -1
  23. package/dist/core/types.d.ts +233 -68
  24. package/dist/core/types.d.ts.map +1 -1
  25. package/dist/core/types.js +75 -1
  26. package/dist/core/types.js.map +1 -1
  27. package/dist/core/unified/tools.js +1 -1
  28. package/dist/core/unified/tools.js.map +1 -1
  29. package/dist/providers/anthropicProvider.js +2 -2
  30. package/dist/providers/anthropicProvider.js.map +1 -1
  31. package/dist/providers/openaiChatCompletionsProvider.js +4 -5
  32. package/dist/providers/openaiChatCompletionsProvider.js.map +1 -1
  33. package/dist/shell/interactiveShell.d.ts +15 -3
  34. package/dist/shell/interactiveShell.d.ts.map +1 -1
  35. package/dist/shell/interactiveShell.js +95 -11
  36. package/dist/shell/interactiveShell.js.map +1 -1
  37. package/dist/shell/shellApp.d.ts.map +1 -1
  38. package/dist/shell/shellApp.js +16 -0
  39. package/dist/shell/shellApp.js.map +1 -1
  40. package/dist/shell/terminalInput.d.ts +36 -1
  41. package/dist/shell/terminalInput.d.ts.map +1 -1
  42. package/dist/shell/terminalInput.js +248 -21
  43. package/dist/shell/terminalInput.js.map +1 -1
  44. package/dist/shell/terminalInputAdapter.d.ts +27 -4
  45. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  46. package/dist/shell/terminalInputAdapter.js +28 -3
  47. package/dist/shell/terminalInputAdapter.js.map +1 -1
  48. package/dist/tools/editTools.js +2 -2
  49. package/dist/tools/editTools.js.map +1 -1
  50. package/dist/ui/display.d.ts +6 -21
  51. package/dist/ui/display.d.ts.map +1 -1
  52. package/dist/ui/display.js +88 -177
  53. package/dist/ui/display.js.map +1 -1
  54. package/package.json +1 -1
@@ -13,6 +13,7 @@ import { EventEmitter } from 'node:events';
13
13
  * Input modes - determines how input is handled
14
14
  */
15
15
  export type InputMode = 'idle' | 'streaming' | 'paste';
16
+ export type EditGuardMode = 'display-edits' | 'ask-permission';
16
17
  /**
17
18
  * Events emitted by TerminalInput
18
19
  */
@@ -51,6 +52,8 @@ export declare class TerminalInput extends EventEmitter {
51
52
  private mode;
52
53
  private pasteBuffer;
53
54
  private isPasting;
55
+ private pastePlaceholders;
56
+ private pasteCounter;
54
57
  private history;
55
58
  private historyIndex;
56
59
  private tempInput;
@@ -64,6 +67,9 @@ export declare class TerminalInput extends EventEmitter {
64
67
  private isRendering;
65
68
  private disposed;
66
69
  private enabled;
70
+ private contextUsage;
71
+ private editMode;
72
+ private outputInterceptorCleanup?;
67
73
  constructor(writeStream?: NodeJS.WriteStream, config?: TerminalInputConfig);
68
74
  /**
69
75
  * Enable bracketed paste mode in terminal
@@ -94,9 +100,13 @@ export declare class TerminalInput extends EventEmitter {
94
100
  */
95
101
  getMode(): InputMode;
96
102
  /**
97
- * Get the current buffer content
103
+ * Get the current buffer content (may contain placeholders for pasted text)
98
104
  */
99
105
  getBuffer(): string;
106
+ /**
107
+ * Get the actual text with paste placeholders expanded
108
+ */
109
+ getText(): string;
100
110
  /**
101
111
  * Set buffer content
102
112
  */
@@ -133,6 +143,17 @@ export declare class TerminalInput extends EventEmitter {
133
143
  * Handle terminal resize
134
144
  */
135
145
  handleResize(): void;
146
+ /**
147
+ * Register with display's output interceptor to position cursor correctly.
148
+ * When scroll region is active, output needs to go to the scroll region,
149
+ * not the protected bottom area where the input is rendered.
150
+ */
151
+ registerOutputInterceptor(display: {
152
+ registerOutputInterceptor: (i: {
153
+ beforeWrite?: () => void;
154
+ afterWrite?: () => void;
155
+ }) => () => void;
156
+ }): void;
136
157
  /**
137
158
  * Dispose and clean up
138
159
  */
@@ -164,6 +185,20 @@ export declare class TerminalInput extends EventEmitter {
164
185
  private disableScrollRegion;
165
186
  private updateReservedLines;
166
187
  private wrapBuffer;
188
+ private getComposedLength;
189
+ private assembleText;
190
+ private shiftPlaceholders;
191
+ private removeRange;
192
+ private insertPlainText;
193
+ private findPlaceholderAt;
194
+ private buildPlaceholder;
195
+ private insertPastePlaceholder;
196
+ private deletePlaceholder;
197
+ updateContextUsage(value: number | null): void;
198
+ getEditMode(): EditGuardMode;
199
+ applyEditMode(mode: EditGuardMode): void;
200
+ private setEditMode;
201
+ toggleEditMode(): void;
167
202
  private scheduleRender;
168
203
  private canRender;
169
204
  private isTTY;
@@ -1 +1 @@
1
- {"version":3,"file":"terminalInput.d.ts","sourceRoot":"","sources":["../../src/shell/terminalInput.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAkC3C;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,YAAY;IAC7C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;IAGvD,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,IAAI,CAAqB;IAGjC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,SAAS,CAAkB;IAGnC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAe;IAG1C,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,cAAc,CAAa;IAGnC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,kBAAkB,CAAkB;IAC5C,OAAO,CAAC,iBAAiB,CAAc;IACvC,OAAO,CAAC,gBAAgB,CAAc;IACtC,OAAO,CAAC,WAAW,CAAkB;IAGrC,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,OAAO,CAAiB;gBAG9B,WAAW,GAAE,MAAM,CAAC,WAA4B,EAChD,MAAM,GAAE,mBAAwB;IAiBlC;;OAEG;IACH,oBAAoB,IAAI,IAAI;IAM5B;;OAEG;IACH,qBAAqB,IAAI,IAAI;IAM7B;;;OAGG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE;IAsCxE;;OAEG;IACH,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI;IA2B3D;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAW9B;;OAEG;IACH,OAAO,IAAI,SAAS;IAIpB;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAOjD;;OAEG;IACH,KAAK,IAAI,IAAI;IAQb;;OAEG;IACH,QAAQ,IAAI,WAAW,EAAE;IAIzB;;OAEG;IACH,OAAO,IAAI,WAAW,GAAG,SAAS;IAMlC;;OAEG;IACH,UAAU,IAAI,IAAI;IAKlB;;OAEG;IACH,MAAM,IAAI,IAAI;IAqHd;;OAEG;IACH,WAAW,IAAI,IAAI;IAMnB;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIlC;;OAEG;IACH,YAAY,IAAI,IAAI;IAYpB;;OAEG;IACH,OAAO,IAAI,IAAI;IAgBf,OAAO,CAAC,aAAa;IAyCrB,OAAO,CAAC,aAAa;IAsBrB,OAAO,CAAC,gBAAgB;IAkDxB,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,qBAAqB;IAQ7B,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,QAAQ;IAuBhB,OAAO,CAAC,UAAU;IAsBlB,OAAO,CAAC,YAAY;IA2BpB,OAAO,CAAC,cAAc;IAwBtB,OAAO,CAAC,iBAAiB;IAuBzB,OAAO,CAAC,MAAM;IAkCd,OAAO,CAAC,WAAW;IAsBnB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,UAAU;IAkElB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,KAAK;IAUb,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,WAAW;CAGpB;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB"}
1
+ {"version":3,"file":"terminalInput.d.ts","sourceRoot":"","sources":["../../src/shell/terminalInput.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,7 @@
9
9
  * - No readline dependency for display
10
10
  */
11
11
  import { EventEmitter } from 'node:events';
12
+ import { isMultilinePaste } from '../core/multilinePasteHandler.js';
12
13
  // ANSI escape codes
13
14
  const ESC = {
14
15
  // Cursor control
@@ -49,6 +50,8 @@ export class TerminalInput extends EventEmitter {
49
50
  // Paste accumulation
50
51
  pasteBuffer = '';
51
52
  isPasting = false;
53
+ pastePlaceholders = [];
54
+ pasteCounter = 0;
52
55
  // History
53
56
  history = [];
54
57
  historyIndex = -1;
@@ -66,11 +69,15 @@ export class TerminalInput extends EventEmitter {
66
69
  // Lifecycle
67
70
  disposed = false;
68
71
  enabled = true;
72
+ contextUsage = null;
73
+ editMode = 'display-edits';
74
+ // Output interceptor cleanup
75
+ outputInterceptorCleanup;
69
76
  constructor(writeStream = process.stdout, config = {}) {
70
77
  super();
71
78
  this.out = writeStream;
72
79
  this.config = {
73
- maxLines: config.maxLines ?? 15,
80
+ maxLines: config.maxLines ?? 1000,
74
81
  maxLength: config.maxLength ?? 10000,
75
82
  maxQueueSize: config.maxQueueSize ?? 100,
76
83
  promptChar: config.promptChar ?? '> ',
@@ -178,16 +185,24 @@ export class TerminalInput extends EventEmitter {
178
185
  return this.mode;
179
186
  }
180
187
  /**
181
- * Get the current buffer content
188
+ * Get the current buffer content (may contain placeholders for pasted text)
182
189
  */
183
190
  getBuffer() {
184
191
  return this.buffer;
185
192
  }
193
+ /**
194
+ * Get the actual text with paste placeholders expanded
195
+ */
196
+ getText() {
197
+ return this.assembleText();
198
+ }
186
199
  /**
187
200
  * Set buffer content
188
201
  */
189
202
  setBuffer(text, cursorPos) {
190
- this.buffer = this.sanitize(text).slice(0, this.config.maxLength);
203
+ this.pastePlaceholders = [];
204
+ const clean = this.sanitize(text).slice(0, this.config.maxLength);
205
+ this.buffer = clean;
191
206
  this.cursor = cursorPos ?? this.buffer.length;
192
207
  this.clampCursor();
193
208
  this.scheduleRender();
@@ -200,6 +215,7 @@ export class TerminalInput extends EventEmitter {
200
215
  this.cursor = 0;
201
216
  this.historyIndex = -1;
202
217
  this.tempInput = '';
218
+ this.pastePlaceholders = [];
203
219
  this.scheduleRender();
204
220
  }
205
221
  /**
@@ -241,7 +257,8 @@ export class TerminalInput extends EventEmitter {
241
257
  const maxWidth = Math.max(8, cols - 4);
242
258
  // Wrap buffer into display lines
243
259
  const { lines, cursorLine, cursorCol } = this.wrapBuffer(maxWidth);
244
- const displayLines = Math.min(lines.length, this.config.maxLines);
260
+ const maxVisible = Math.max(1, Math.min(this.config.maxLines, rows - 1));
261
+ const displayLines = Math.min(lines.length, maxVisible);
245
262
  // Update reserved lines if needed
246
263
  this.updateReservedLines(displayLines);
247
264
  // Calculate display window (keep cursor visible)
@@ -264,6 +281,15 @@ export class TerminalInput extends EventEmitter {
264
281
  this.write('─'.repeat(sepWidth));
265
282
  // Status hints
266
283
  const hints = [];
284
+ if (this.contextUsage !== null) {
285
+ const remaining = Math.max(0, 100 - this.contextUsage);
286
+ hints.push(`context ${remaining}% left`);
287
+ }
288
+ hints.push(this.editMode === 'display-edits' ? 'mode: display edits' : 'mode: ask permission');
289
+ if (this.pastePlaceholders.length > 0) {
290
+ const count = this.pastePlaceholders.length;
291
+ hints.push(`${count} paste block${count === 1 ? '' : 's'}`);
292
+ }
267
293
  if (lines.length > 1)
268
294
  hints.push(`${lines.length} lines`);
269
295
  if (this.mode === 'streaming' && this.queue.length > 0) {
@@ -351,12 +377,44 @@ export class TerminalInput extends EventEmitter {
351
377
  }
352
378
  this.scheduleRender();
353
379
  }
380
+ /**
381
+ * Register with display's output interceptor to position cursor correctly.
382
+ * When scroll region is active, output needs to go to the scroll region,
383
+ * not the protected bottom area where the input is rendered.
384
+ */
385
+ registerOutputInterceptor(display) {
386
+ if (this.outputInterceptorCleanup) {
387
+ this.outputInterceptorCleanup();
388
+ }
389
+ this.outputInterceptorCleanup = display.registerOutputInterceptor({
390
+ beforeWrite: () => {
391
+ // When scroll region is active, move cursor to scroll area before output
392
+ if (this.scrollRegionActive) {
393
+ const { rows } = this.getSize();
394
+ const scrollBottom = Math.max(1, rows - this.reservedLines);
395
+ this.write(ESC.SAVE);
396
+ this.write(ESC.TO(scrollBottom, 1));
397
+ }
398
+ },
399
+ afterWrite: () => {
400
+ // Restore cursor position after output
401
+ if (this.scrollRegionActive) {
402
+ this.write(ESC.RESTORE);
403
+ }
404
+ },
405
+ });
406
+ }
354
407
  /**
355
408
  * Dispose and clean up
356
409
  */
357
410
  dispose() {
358
411
  if (this.disposed)
359
412
  return;
413
+ // Clean up output interceptor
414
+ if (this.outputInterceptorCleanup) {
415
+ this.outputInterceptorCleanup();
416
+ this.outputInterceptorCleanup = undefined;
417
+ }
360
418
  this.disposed = true;
361
419
  this.enabled = false;
362
420
  this.disableScrollRegion();
@@ -373,7 +431,6 @@ export class TerminalInput extends EventEmitter {
373
431
  case 'c':
374
432
  if (this.buffer.length > 0) {
375
433
  this.clear();
376
- this.write('^C\n');
377
434
  }
378
435
  else {
379
436
  this.emit('interrupt');
@@ -455,6 +512,10 @@ export class TerminalInput extends EventEmitter {
455
512
  this.moveCursorToLineEnd();
456
513
  return true;
457
514
  case 'tab':
515
+ if (key.shift) {
516
+ this.toggleEditMode();
517
+ return true;
518
+ }
458
519
  this.insertText(' ');
459
520
  return true;
460
521
  }
@@ -464,42 +525,58 @@ export class TerminalInput extends EventEmitter {
464
525
  const clean = this.sanitize(text);
465
526
  if (!clean)
466
527
  return;
467
- const available = this.config.maxLength - this.buffer.length;
528
+ const available = this.config.maxLength - this.getComposedLength();
468
529
  if (available <= 0)
469
530
  return;
470
531
  const chunk = clean.slice(0, available);
471
- this.buffer = this.buffer.slice(0, this.cursor) + chunk + this.buffer.slice(this.cursor);
472
- this.cursor += chunk.length;
532
+ const placeholder = this.findPlaceholderAt(this.cursor);
533
+ const insertPos = placeholder && this.cursor > placeholder.start ? placeholder.end : this.cursor;
534
+ this.insertPlainText(chunk, insertPos);
535
+ this.cursor = insertPos + chunk.length;
473
536
  this.emit('change', this.buffer);
474
537
  this.scheduleRender();
475
538
  }
476
539
  insertNewline() {
477
- if (this.buffer.length >= this.config.maxLength)
540
+ if (this.getComposedLength() >= this.config.maxLength)
478
541
  return;
479
- this.buffer = this.buffer.slice(0, this.cursor) + '\n' + this.buffer.slice(this.cursor);
480
- this.cursor++;
542
+ const placeholder = this.findPlaceholderAt(this.cursor);
543
+ const insertPos = placeholder && this.cursor > placeholder.start ? placeholder.end : this.cursor;
544
+ this.insertPlainText('\n', insertPos);
545
+ this.cursor = insertPos + 1;
481
546
  this.emit('change', this.buffer);
482
547
  this.scheduleRender();
483
548
  }
484
549
  deleteBackward() {
485
550
  if (this.cursor === 0)
486
551
  return;
487
- this.buffer = this.buffer.slice(0, this.cursor - 1) + this.buffer.slice(this.cursor);
488
- this.cursor--;
552
+ const placeholder = this.findPlaceholderAt(this.cursor - 1);
553
+ if (placeholder) {
554
+ this.deletePlaceholder(placeholder);
555
+ }
556
+ else {
557
+ this.removeRange(this.cursor - 1, this.cursor);
558
+ this.cursor = Math.max(0, this.cursor - 1);
559
+ }
489
560
  this.emit('change', this.buffer);
490
561
  this.scheduleRender();
491
562
  }
492
563
  deleteForward() {
493
564
  if (this.cursor >= this.buffer.length)
494
565
  return;
495
- this.buffer = this.buffer.slice(0, this.cursor) + this.buffer.slice(this.cursor + 1);
566
+ const placeholder = this.findPlaceholderAt(this.cursor);
567
+ if (placeholder) {
568
+ this.deletePlaceholder(placeholder);
569
+ }
570
+ else {
571
+ this.removeRange(this.cursor, this.cursor + 1);
572
+ }
496
573
  this.emit('change', this.buffer);
497
574
  this.scheduleRender();
498
575
  }
499
576
  deleteToStart() {
500
577
  if (this.cursor === 0)
501
578
  return;
502
- this.buffer = this.buffer.slice(this.cursor);
579
+ this.removeRange(0, this.cursor);
503
580
  this.cursor = 0;
504
581
  this.emit('change', this.buffer);
505
582
  this.scheduleRender();
@@ -507,13 +584,20 @@ export class TerminalInput extends EventEmitter {
507
584
  deleteToEnd() {
508
585
  if (this.cursor >= this.buffer.length)
509
586
  return;
510
- this.buffer = this.buffer.slice(0, this.cursor);
587
+ this.removeRange(this.cursor, this.buffer.length);
511
588
  this.emit('change', this.buffer);
512
589
  this.scheduleRender();
513
590
  }
514
591
  deleteWord() {
515
592
  if (this.cursor === 0)
516
593
  return;
594
+ const placeholder = this.findPlaceholderAt(this.cursor - 1);
595
+ if (placeholder) {
596
+ this.deletePlaceholder(placeholder);
597
+ this.emit('change', this.buffer);
598
+ this.scheduleRender();
599
+ return;
600
+ }
517
601
  let pos = this.cursor;
518
602
  // Skip whitespace
519
603
  while (pos > 0 && this.isWhitespace(this.buffer[pos - 1]))
@@ -678,7 +762,7 @@ export class TerminalInput extends EventEmitter {
678
762
  };
679
763
  }
680
764
  submit() {
681
- const text = this.buffer.trim();
765
+ const text = this.assembleText().trim();
682
766
  if (!text)
683
767
  return;
684
768
  // Add to history
@@ -714,12 +798,22 @@ export class TerminalInput extends EventEmitter {
714
798
  this.isPasting = false;
715
799
  if (!content)
716
800
  return;
717
- // Insert paste content at cursor
718
801
  const clean = this.sanitize(content);
719
- const available = this.config.maxLength - this.buffer.length;
802
+ if (!clean)
803
+ return;
804
+ const available = this.config.maxLength - this.getComposedLength();
805
+ if (available <= 0)
806
+ return;
720
807
  const chunk = clean.slice(0, available);
721
- this.buffer = this.buffer.slice(0, this.cursor) + chunk + this.buffer.slice(this.cursor);
722
- this.cursor += chunk.length;
808
+ if (isMultilinePaste(chunk)) {
809
+ this.insertPastePlaceholder(chunk);
810
+ }
811
+ else {
812
+ const placeholder = this.findPlaceholderAt(this.cursor);
813
+ const insertPos = placeholder && this.cursor > placeholder.start ? placeholder.end : this.cursor;
814
+ this.insertPlainText(chunk, insertPos);
815
+ this.cursor = insertPos + chunk.length;
816
+ }
723
817
  this.emit('change', this.buffer);
724
818
  this.scheduleRender();
725
819
  }
@@ -808,6 +902,139 @@ export class TerminalInput extends EventEmitter {
808
902
  // ===========================================================================
809
903
  // UTILITIES
810
904
  // ===========================================================================
905
+ getComposedLength() {
906
+ if (this.pastePlaceholders.length === 0) {
907
+ return this.buffer.length;
908
+ }
909
+ const sorted = [...this.pastePlaceholders].sort((a, b) => a.start - b.start);
910
+ let composed = 0;
911
+ let cursor = 0;
912
+ for (const placeholder of sorted) {
913
+ composed += Math.max(0, placeholder.start - cursor);
914
+ composed += placeholder.content.length;
915
+ cursor = placeholder.end;
916
+ }
917
+ composed += Math.max(0, this.buffer.length - cursor);
918
+ return composed;
919
+ }
920
+ assembleText() {
921
+ if (this.pastePlaceholders.length === 0) {
922
+ return this.buffer;
923
+ }
924
+ const sorted = [...this.pastePlaceholders].sort((a, b) => a.start - b.start);
925
+ let result = '';
926
+ let cursor = 0;
927
+ for (const placeholder of sorted) {
928
+ result += this.buffer.slice(cursor, placeholder.start);
929
+ result += placeholder.content;
930
+ cursor = placeholder.end;
931
+ }
932
+ result += this.buffer.slice(cursor);
933
+ return result;
934
+ }
935
+ shiftPlaceholders(start, delta, excludeId) {
936
+ if (delta === 0)
937
+ return;
938
+ for (const placeholder of this.pastePlaceholders) {
939
+ if (excludeId && placeholder.id === excludeId)
940
+ continue;
941
+ if (placeholder.start >= start) {
942
+ placeholder.start += delta;
943
+ placeholder.end += delta;
944
+ }
945
+ }
946
+ }
947
+ removeRange(start, end) {
948
+ // Expand deletion to cover any placeholder that overlaps the range
949
+ let adjustedStart = start;
950
+ let adjustedEnd = end;
951
+ for (const ph of this.pastePlaceholders) {
952
+ if (ph.start < adjustedEnd && ph.end > adjustedStart) {
953
+ adjustedStart = Math.min(adjustedStart, ph.start);
954
+ adjustedEnd = Math.max(adjustedEnd, ph.end);
955
+ }
956
+ }
957
+ const length = Math.max(0, adjustedEnd - adjustedStart);
958
+ if (length === 0)
959
+ return;
960
+ this.buffer = this.buffer.slice(0, adjustedStart) + this.buffer.slice(adjustedEnd);
961
+ // Remove any placeholders that were within the deleted range
962
+ this.pastePlaceholders = this.pastePlaceholders.filter((ph) => {
963
+ return ph.end <= adjustedStart || ph.start >= adjustedEnd;
964
+ });
965
+ // Shift remaining placeholders that were after the deleted range
966
+ this.shiftPlaceholders(adjustedEnd, -length);
967
+ this.clampCursor();
968
+ }
969
+ insertPlainText(text, position) {
970
+ if (!text)
971
+ return;
972
+ this.shiftPlaceholders(position, text.length);
973
+ this.buffer = this.buffer.slice(0, position) + text + this.buffer.slice(position);
974
+ }
975
+ findPlaceholderAt(position) {
976
+ return this.pastePlaceholders.find((ph) => position >= ph.start && position < ph.end) ?? null;
977
+ }
978
+ buildPlaceholder(lineCount) {
979
+ const id = ++this.pasteCounter;
980
+ const plural = lineCount === 1 ? '' : 's';
981
+ const placeholder = `[Pasted text #${id} +${lineCount} line${plural}]`;
982
+ return { id, placeholder };
983
+ }
984
+ insertPastePlaceholder(content) {
985
+ const available = this.config.maxLength - this.getComposedLength();
986
+ if (available <= 0)
987
+ return;
988
+ const cleanContent = content.slice(0, available);
989
+ const lineCount = cleanContent.split('\n').length;
990
+ const { id, placeholder } = this.buildPlaceholder(lineCount);
991
+ const insertPos = this.cursor;
992
+ this.shiftPlaceholders(insertPos, placeholder.length);
993
+ this.pastePlaceholders.push({
994
+ id,
995
+ content: cleanContent,
996
+ lineCount,
997
+ placeholder,
998
+ start: insertPos,
999
+ end: insertPos + placeholder.length,
1000
+ });
1001
+ this.buffer = this.buffer.slice(0, insertPos) + placeholder + this.buffer.slice(insertPos);
1002
+ this.cursor = insertPos + placeholder.length;
1003
+ }
1004
+ deletePlaceholder(placeholder) {
1005
+ const length = placeholder.end - placeholder.start;
1006
+ this.buffer = this.buffer.slice(0, placeholder.start) + this.buffer.slice(placeholder.end);
1007
+ this.pastePlaceholders = this.pastePlaceholders.filter((ph) => ph.id !== placeholder.id);
1008
+ this.shiftPlaceholders(placeholder.end, -length, placeholder.id);
1009
+ this.cursor = placeholder.start;
1010
+ }
1011
+ updateContextUsage(value) {
1012
+ if (value === null || !Number.isFinite(value)) {
1013
+ this.contextUsage = null;
1014
+ }
1015
+ else {
1016
+ const bounded = Math.max(0, Math.min(100, Math.round(value)));
1017
+ this.contextUsage = bounded;
1018
+ }
1019
+ this.scheduleRender();
1020
+ }
1021
+ getEditMode() {
1022
+ return this.editMode;
1023
+ }
1024
+ applyEditMode(mode) {
1025
+ this.setEditMode(mode);
1026
+ }
1027
+ setEditMode(mode) {
1028
+ if (this.editMode === mode)
1029
+ return;
1030
+ this.editMode = mode;
1031
+ this.emit('mode-change', this.editMode);
1032
+ this.scheduleRender();
1033
+ }
1034
+ toggleEditMode() {
1035
+ const next = this.editMode === 'display-edits' ? 'ask-permission' : 'display-edits';
1036
+ this.setEditMode(next);
1037
+ }
811
1038
  scheduleRender() {
812
1039
  if (!this.canRender())
813
1040
  return;