@cydm/pie 1.0.42 → 1.0.43

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 (2) hide show
  1. package/dist/cli.js +386 -90
  2. package/package.json +3 -3
package/dist/cli.js CHANGED
@@ -46314,6 +46314,24 @@ var SettingsManager = class {
46314
46314
  getBellNotificationsEnabled() {
46315
46315
  return this.settings.notifications?.bell !== false;
46316
46316
  }
46317
+ setBellNotificationsEnabled(enabled) {
46318
+ this.settings.notifications = { ...this.settings.notifications, bell: enabled };
46319
+ this.writeSettings();
46320
+ }
46321
+ getDesktopNotificationsEnabled() {
46322
+ return this.settings.notifications?.desktop !== false;
46323
+ }
46324
+ setDesktopNotificationsEnabled(enabled) {
46325
+ this.settings.notifications = { ...this.settings.notifications, desktop: enabled };
46326
+ this.writeSettings();
46327
+ }
46328
+ getCalmModeEnabled() {
46329
+ return this.settings.calmMode === true;
46330
+ }
46331
+ setCalmModeEnabled(enabled) {
46332
+ this.settings.calmMode = enabled;
46333
+ this.writeSettings();
46334
+ }
46317
46335
  // ==========================================================================
46318
46336
  // Extension enablement (user ~/.pie/settings.json + project ./.pie/settings.json)
46319
46337
  // ==========================================================================
@@ -49807,10 +49825,15 @@ var Text = class {
49807
49825
  for (const line of wrappedLines) {
49808
49826
  const lineWithMargins = leftMargin + line + rightMargin;
49809
49827
  if (this.customBgFn) {
49810
- contentLines.push(applyBackgroundToLine(lineWithMargins, width, this.customBgFn));
49828
+ const clamped = visibleWidth(lineWithMargins) > width ? truncateToWidth(lineWithMargins, width, "") : lineWithMargins;
49829
+ contentLines.push(applyBackgroundToLine(clamped, width, this.customBgFn));
49811
49830
  } else {
49812
49831
  const visibleLen = visibleWidth(lineWithMargins);
49813
- const paddingNeeded = Math.max(0, width - visibleLen);
49832
+ if (visibleLen > width) {
49833
+ contentLines.push(truncateToWidth(lineWithMargins, width, ""));
49834
+ continue;
49835
+ }
49836
+ const paddingNeeded = width - visibleLen;
49814
49837
  contentLines.push(lineWithMargins + " ".repeat(paddingNeeded));
49815
49838
  }
49816
49839
  }
@@ -50369,6 +50392,8 @@ var TUI = class extends Container {
50369
50392
  // Track previous viewport top for resize-aware cursor moves
50370
50393
  fullRedrawCount = 0;
50371
50394
  stopped = false;
50395
+ /** Guard: never paint before terminal.start() has run (modes like DECAWM-off are set there). */
50396
+ started = false;
50372
50397
  constructor(terminal, showHardwareCursor) {
50373
50398
  super();
50374
50399
  this.terminal = terminal;
@@ -50431,6 +50456,7 @@ var TUI = class extends Container {
50431
50456
  }
50432
50457
  start() {
50433
50458
  this.stopped = false;
50459
+ this.started = true;
50434
50460
  this.terminal.start(
50435
50461
  (data) => this.handleInput(data),
50436
50462
  () => this.requestRender()
@@ -50470,6 +50496,7 @@ var TUI = class extends Container {
50470
50496
  this.previousViewportTop = 0;
50471
50497
  }
50472
50498
  if (this.renderRequested) return;
50499
+ if (!this.started || this.stopped) return;
50473
50500
  this.renderRequested = true;
50474
50501
  process.nextTick(() => {
50475
50502
  this.renderRequested = false;
@@ -50809,7 +50836,7 @@ var SelectList = class {
50809
50836
  render(width) {
50810
50837
  const lines = [];
50811
50838
  if (this.filteredItems.length === 0) {
50812
- lines.push(this.theme.noMatch(" No matching commands"));
50839
+ lines.push(truncateToWidth(this.theme.noMatch(" No matching commands"), width));
50813
50840
  return lines;
50814
50841
  }
50815
50842
  const { startIndex, endIndex } = this.getVisibleRange();
@@ -51133,8 +51160,8 @@ var Editor = class {
51133
51160
  const rightPadding = leftPadding;
51134
51161
  if (this.scrollOffset > 0) {
51135
51162
  const indicator = `\u2500\u2500\u2500 \u2191 ${this.scrollOffset} more `;
51136
- const remaining = width - visibleWidth(indicator);
51137
- result.push(this.borderColor(indicator + "\u2500".repeat(Math.max(0, remaining))));
51163
+ const fill = "\u2500".repeat(Math.max(0, width - visibleWidth(indicator)));
51164
+ result.push(this.borderColor(truncateToWidth(indicator + fill, width, "")));
51138
51165
  } else {
51139
51166
  result.push(horizontal.repeat(width));
51140
51167
  }
@@ -51164,13 +51191,13 @@ var Editor = class {
51164
51191
  }
51165
51192
  const padding = " ".repeat(Math.max(0, contentWidth - lineVisibleWidth));
51166
51193
  const lineRightPadding = cursorInPadding ? rightPadding.slice(1) : rightPadding;
51167
- result.push(`${leftPadding}${displayText}${padding}${lineRightPadding}`);
51194
+ result.push(truncateToWidth(`${leftPadding}${displayText}${padding}${lineRightPadding}`, width, ""));
51168
51195
  }
51169
51196
  const linesBelow = layoutLines.length - (this.scrollOffset + visibleLines.length);
51170
51197
  if (linesBelow > 0) {
51171
51198
  const indicator = `\u2500\u2500\u2500 \u2193 ${linesBelow} more `;
51172
- const remaining = width - visibleWidth(indicator);
51173
- result.push(this.borderColor(indicator + "\u2500".repeat(Math.max(0, remaining))));
51199
+ const fill = "\u2500".repeat(Math.max(0, width - visibleWidth(indicator)));
51200
+ result.push(this.borderColor(truncateToWidth(indicator + fill, width, "")));
51174
51201
  } else {
51175
51202
  result.push(horizontal.repeat(width));
51176
51203
  }
@@ -51728,26 +51755,55 @@ var Editor = class {
51728
51755
  /**
51729
51756
  * Move cursor to a target visual line, applying sticky column logic.
51730
51757
  * Shared by moveCursor() and pageScroll().
51758
+ *
51759
+ * Column math is done in terminal columns (visibleWidth), not UTF-16 code
51760
+ * units, so CJK/emoji text keeps the cursor visually aligned when moving
51761
+ * up/down across wrapped segments.
51731
51762
  */
51732
51763
  moveToVisualLine(visualLines, currentVisualLine, targetVisualLine) {
51733
51764
  const currentVL = visualLines[currentVisualLine];
51734
51765
  const targetVL = visualLines[targetVisualLine];
51735
51766
  if (currentVL && targetVL) {
51736
- const currentVisualCol = this.state.cursorCol - currentVL.startCol;
51767
+ const sourceLine = this.state.lines[currentVL.logicalLine] || "";
51768
+ const sourceText = sourceLine.slice(currentVL.startUnit, currentVL.startUnit + currentVL.lengthUnits);
51769
+ const currentVisualCol = visibleWidth(sourceLine.slice(currentVL.startUnit, this.state.cursorCol));
51737
51770
  const isLastSourceSegment = currentVisualLine === visualLines.length - 1 || visualLines[currentVisualLine + 1]?.logicalLine !== currentVL.logicalLine;
51738
- const sourceMaxVisualCol = isLastSourceSegment ? currentVL.length : Math.max(0, currentVL.length - 1);
51771
+ const sourceMaxVisualCol = isLastSourceSegment ? currentVL.widthCols : Math.max(0, currentVL.widthCols - this.lastGraphemeColumns(sourceText));
51772
+ const targetLine = this.state.lines[targetVL.logicalLine] || "";
51773
+ const targetText = targetLine.slice(targetVL.startUnit, targetVL.startUnit + targetVL.lengthUnits);
51739
51774
  const isLastTargetSegment = targetVisualLine === visualLines.length - 1 || visualLines[targetVisualLine + 1]?.logicalLine !== targetVL.logicalLine;
51740
- const targetMaxVisualCol = isLastTargetSegment ? targetVL.length : Math.max(0, targetVL.length - 1);
51775
+ const targetMaxVisualCol = isLastTargetSegment ? targetVL.widthCols : Math.max(0, targetVL.widthCols - this.lastGraphemeColumns(targetText));
51741
51776
  const moveToVisualCol = this.computeVerticalMoveColumn(
51742
51777
  currentVisualCol,
51743
51778
  sourceMaxVisualCol,
51744
51779
  targetMaxVisualCol
51745
51780
  );
51746
51781
  this.state.cursorLine = targetVL.logicalLine;
51747
- const targetCol = targetVL.startCol + moveToVisualCol;
51748
- const logicalLine = this.state.lines[targetVL.logicalLine] || "";
51749
- this.state.cursorCol = Math.min(targetCol, logicalLine.length);
51782
+ const targetCol = targetVL.startUnit + this.columnToUnit(targetText, moveToVisualCol);
51783
+ this.state.cursorCol = Math.min(targetCol, targetLine.length);
51784
+ }
51785
+ }
51786
+ /** Map a terminal-column offset to the closest code-unit offset at or before it. */
51787
+ columnToUnit(text, col) {
51788
+ if (col <= 0) return 0;
51789
+ let units = 0;
51790
+ let cols = 0;
51791
+ for (const seg of segmenter2.segment(text)) {
51792
+ const width = visibleWidth(seg.segment);
51793
+ if (cols + width > col) break;
51794
+ cols += width;
51795
+ units += seg.segment.length;
51796
+ }
51797
+ return units;
51798
+ }
51799
+ /** Visible width (columns) of the last grapheme of a non-empty string. */
51800
+ lastGraphemeColumns(text) {
51801
+ if (!text) return 0;
51802
+ let width = 0;
51803
+ for (const seg of segmenter2.segment(text)) {
51804
+ width = visibleWidth(seg.segment);
51750
51805
  }
51806
+ return width;
51751
51807
  }
51752
51808
  /**
51753
51809
  * Compute the target visual column for vertical cursor movement.
@@ -51937,11 +51993,8 @@ var Editor = class {
51937
51993
  }
51938
51994
  }
51939
51995
  /**
51940
- * Build a mapping from visual lines to logical positions.
51941
- * Returns an array where each element represents a visual line with:
51942
- * - logicalLine: index into this.state.lines
51943
- * - startCol: starting column in the logical line
51944
- * - length: length of this visual line segment
51996
+ * A wrapped segment of a logical line: both UTF-16 offsets (for editing)
51997
+ * and terminal columns (for visual cursor movement) are tracked.
51945
51998
  */
51946
51999
  buildVisualLineMap(width) {
51947
52000
  const visualLines = [];
@@ -51949,17 +52002,22 @@ var Editor = class {
51949
52002
  const line = this.state.lines[i] || "";
51950
52003
  const lineVisWidth = visibleWidth(line);
51951
52004
  if (line.length === 0) {
51952
- visualLines.push({ logicalLine: i, startCol: 0, length: 0 });
52005
+ visualLines.push({ logicalLine: i, startUnit: 0, lengthUnits: 0, startCol: 0, widthCols: 0 });
51953
52006
  } else if (lineVisWidth <= width) {
51954
- visualLines.push({ logicalLine: i, startCol: 0, length: line.length });
52007
+ visualLines.push({ logicalLine: i, startUnit: 0, lengthUnits: line.length, startCol: 0, widthCols: lineVisWidth });
51955
52008
  } else {
51956
52009
  const chunks = wordWrapLine(line, width);
52010
+ let col = 0;
51957
52011
  for (const chunk of chunks) {
52012
+ const chunkWidth = visibleWidth(chunk.text);
51958
52013
  visualLines.push({
51959
52014
  logicalLine: i,
51960
- startCol: chunk.startIndex,
51961
- length: chunk.endIndex - chunk.startIndex
52015
+ startUnit: chunk.startIndex,
52016
+ lengthUnits: chunk.endIndex - chunk.startIndex,
52017
+ startCol: col,
52018
+ widthCols: chunkWidth
51962
52019
  });
52020
+ col += chunkWidth;
51963
52021
  }
51964
52022
  }
51965
52023
  }
@@ -51973,9 +52031,9 @@ var Editor = class {
51973
52031
  const vl = visualLines[i];
51974
52032
  if (!vl) continue;
51975
52033
  if (vl.logicalLine === this.state.cursorLine) {
51976
- const colInSegment = this.state.cursorCol - vl.startCol;
52034
+ const unitInSegment = this.state.cursorCol - vl.startUnit;
51977
52035
  const isLastSegmentOfLine = i === visualLines.length - 1 || visualLines[i + 1]?.logicalLine !== vl.logicalLine;
51978
- if (colInSegment >= 0 && (colInSegment < vl.length || isLastSegmentOfLine && colInSegment <= vl.length)) {
52036
+ if (unitInSegment >= 0 && (unitInSegment < vl.lengthUnits || isLastSegmentOfLine && unitInSegment <= vl.lengthUnits)) {
51979
52037
  return i;
51980
52038
  }
51981
52039
  }
@@ -52361,7 +52419,14 @@ var Input = class {
52361
52419
  }
52362
52420
  setValue(value) {
52363
52421
  this.value = value;
52364
- this.cursor = Math.min(this.cursor, value.length);
52422
+ const clamped = Math.min(this.cursor, value.length);
52423
+ let units = 0;
52424
+ for (const seg of segmenter3.segment(value)) {
52425
+ const segLength = seg.segment.length;
52426
+ if (units + segLength > clamped) break;
52427
+ units += segLength;
52428
+ }
52429
+ this.cursor = units;
52365
52430
  }
52366
52431
  handleInput(data) {
52367
52432
  if (data.includes("\x1B[200~")) {
@@ -52646,47 +52711,53 @@ var Input = class {
52646
52711
  if (availableWidth <= 0) {
52647
52712
  return [prompt];
52648
52713
  }
52649
- let visibleText = "";
52650
- let cursorDisplay = this.cursor;
52651
- if (this.value.length < availableWidth) {
52652
- visibleText = this.value;
52653
- } else {
52654
- const scrollWidth = this.cursor === this.value.length ? availableWidth - 1 : availableWidth;
52714
+ const cursorAtEnd = this.cursor >= this.value.length;
52715
+ const scrollWidth = cursorAtEnd ? Math.max(0, availableWidth - 1) : availableWidth;
52716
+ const valueGraphemes = [];
52717
+ let totalWidth = 0;
52718
+ for (const seg of segmenter3.segment(this.value)) {
52719
+ const text = seg.segment;
52720
+ const gWidth = visibleWidth(text);
52721
+ valueGraphemes.push({ text, startUnit: seg.index, startCol: totalWidth, width: gWidth });
52722
+ totalWidth += gWidth;
52723
+ }
52724
+ let cursorCol = 0;
52725
+ let cursorGraphemeIndex = valueGraphemes.length;
52726
+ for (let i = 0; i < valueGraphemes.length; i++) {
52727
+ const g2 = valueGraphemes[i];
52728
+ if (g2.startUnit >= this.cursor) break;
52729
+ cursorCol = g2.startCol + g2.width;
52730
+ cursorGraphemeIndex = i + 1;
52731
+ }
52732
+ const cursorCellWidth = valueGraphemes[cursorGraphemeIndex]?.width ?? 1;
52733
+ const cursorEndCol = cursorAtEnd ? cursorCol + 1 : cursorCol + cursorCellWidth;
52734
+ let startCol = 0;
52735
+ if (cursorEndCol > availableWidth) {
52655
52736
  const halfWidth = Math.floor(scrollWidth / 2);
52656
- const findValidStart = (start) => {
52657
- while (start < this.value.length) {
52658
- const charCode = this.value.charCodeAt(start);
52659
- if (charCode >= 56320 && charCode < 57344) {
52660
- start++;
52661
- continue;
52662
- }
52663
- break;
52664
- }
52665
- return start;
52666
- };
52667
- const findValidEnd = (end) => {
52668
- while (end > 0) {
52669
- const charCode = this.value.charCodeAt(end - 1);
52670
- if (charCode >= 55296 && charCode < 56320) {
52671
- end--;
52672
- continue;
52673
- }
52674
- break;
52675
- }
52676
- return end;
52677
- };
52678
- if (this.cursor < halfWidth) {
52679
- visibleText = this.value.slice(0, findValidEnd(scrollWidth));
52680
- cursorDisplay = this.cursor;
52681
- } else if (this.cursor > this.value.length - halfWidth) {
52682
- const start = findValidStart(this.value.length - scrollWidth);
52683
- visibleText = this.value.slice(start);
52684
- cursorDisplay = this.cursor - start;
52737
+ if (cursorCol < halfWidth) {
52738
+ startCol = 0;
52739
+ } else if (cursorEndCol > totalWidth - halfWidth) {
52740
+ startCol = Math.max(0, totalWidth - scrollWidth);
52685
52741
  } else {
52686
- const start = findValidStart(this.cursor - halfWidth);
52687
- visibleText = this.value.slice(start, findValidEnd(start + scrollWidth));
52688
- cursorDisplay = halfWidth;
52742
+ startCol = cursorCol - halfWidth;
52689
52743
  }
52744
+ startCol = Math.max(0, Math.min(startCol, cursorEndCol - scrollWidth));
52745
+ }
52746
+ const windowEnd = startCol + scrollWidth;
52747
+ let visibleText = "";
52748
+ let cursorDisplay = 0;
52749
+ let cursorPlaced = false;
52750
+ for (const g2 of valueGraphemes) {
52751
+ if (g2.startCol >= startCol && g2.startCol + g2.width <= windowEnd) {
52752
+ if (!cursorPlaced && this.cursor <= g2.startUnit) {
52753
+ cursorDisplay = visibleText.length;
52754
+ cursorPlaced = true;
52755
+ }
52756
+ visibleText += g2.text;
52757
+ }
52758
+ }
52759
+ if (!cursorPlaced) {
52760
+ cursorDisplay = visibleText.length;
52690
52761
  }
52691
52762
  const graphemes = [...segmenter3.segment(visibleText.slice(cursorDisplay))];
52692
52763
  const cursorGrapheme = graphemes[0];
@@ -53981,10 +54052,15 @@ var Markdown = class {
53981
54052
  }
53982
54053
  const lineWithMargins = leftMargin + line + rightMargin;
53983
54054
  if (bgFn) {
53984
- contentLines.push(applyBackgroundToLine(lineWithMargins, width, bgFn));
54055
+ const clamped = visibleWidth(lineWithMargins) > width ? truncateToWidth(lineWithMargins, width, "") : lineWithMargins;
54056
+ contentLines.push(applyBackgroundToLine(clamped, width, bgFn));
53985
54057
  } else {
53986
54058
  const visibleLen = visibleWidth(lineWithMargins);
53987
- const paddingNeeded = Math.max(0, width - visibleLen);
54059
+ if (visibleLen > width) {
54060
+ contentLines.push(truncateToWidth(lineWithMargins, width, ""));
54061
+ continue;
54062
+ }
54063
+ const paddingNeeded = width - visibleLen;
53988
54064
  contentLines.push(lineWithMargins + " ".repeat(paddingNeeded));
53989
54065
  }
53990
54066
  }
@@ -54513,7 +54589,7 @@ var SettingsList = class {
54513
54589
  lines.push("");
54514
54590
  }
54515
54591
  if (this.items.length === 0) {
54516
- lines.push(this.theme.hint(" No settings available"));
54592
+ lines.push(truncateToWidth(this.theme.hint(" No settings available"), width));
54517
54593
  if (this.searchEnabled) {
54518
54594
  this.addHintLine(lines, width);
54519
54595
  }
@@ -54554,7 +54630,7 @@ var SettingsList = class {
54554
54630
  lines.push("");
54555
54631
  const wrappedDesc = wrapTextWithAnsi(selectedItem.description, width - 4);
54556
54632
  for (const line of wrappedDesc) {
54557
- lines.push(this.theme.description(` ${line}`));
54633
+ lines.push(truncateToWidth(this.theme.description(` ${line}`), width));
54558
54634
  }
54559
54635
  }
54560
54636
  this.addHintLine(lines, width);
@@ -54919,6 +54995,7 @@ var ProcessTerminal = class {
54919
54995
  process.stdin.setEncoding("utf8");
54920
54996
  process.stdin.resume();
54921
54997
  process.stdout.write("\x1B[?2004h");
54998
+ process.stdout.write("\x1B[?7l");
54922
54999
  process.stdout.on("resize", this.resizeHandler);
54923
55000
  if (process.platform !== "win32") {
54924
55001
  process.kill(process.pid, "SIGWINCH");
@@ -55030,6 +55107,7 @@ var ProcessTerminal = class {
55030
55107
  }
55031
55108
  stop() {
55032
55109
  process.stdout.write("\x1B[?2004l");
55110
+ process.stdout.write("\x1B[?7h");
55033
55111
  if (this._kittyProtocolActive && !process.env.PIE_DISABLE_KITTY_PROTOCOL) {
55034
55112
  process.stdout.write("\x1B[<u");
55035
55113
  this._kittyProtocolActive = false;
@@ -55093,7 +55171,26 @@ var ProcessTerminal = class {
55093
55171
  setTitle(title) {
55094
55172
  process.stdout.write(`\x1B]0;${title}\x07`);
55095
55173
  }
55174
+ notify(title, body) {
55175
+ if (!process.stdout.isTTY) return;
55176
+ for (const sequence of buildDesktopNotificationSequences(title, body)) {
55177
+ process.stdout.write(sequence);
55178
+ }
55179
+ }
55096
55180
  };
55181
+ var NOTIFICATION_UNSAFE_CHARS = /[\x00-\x1f\x7f-\x9f;]/g;
55182
+ function sanitizeNotificationText(text) {
55183
+ return text.replace(NOTIFICATION_UNSAFE_CHARS, "");
55184
+ }
55185
+ function buildDesktopNotificationSequences(title, body) {
55186
+ const sanitizedTitle = sanitizeNotificationText(title);
55187
+ const sanitizedBody = sanitizeNotificationText(body);
55188
+ return [
55189
+ `\x1B]9;${sanitizedTitle}: ${sanitizedBody}\x07`,
55190
+ `\x1B]777;notify;${sanitizedTitle};${sanitizedBody}\x07`,
55191
+ `\x1B]99;i=1:d=0;${sanitizedBody}\x07`
55192
+ ];
55193
+ }
55097
55194
 
55098
55195
  // src/extensions/runner.ts
55099
55196
  function createLoggedProxy(obj, contextPrefix) {
@@ -61079,6 +61176,27 @@ var SettingsSelectorComponent = class extends Container {
61079
61176
  }
61080
61177
  )
61081
61178
  },
61179
+ {
61180
+ id: "calm",
61181
+ label: "Calm Mode",
61182
+ description: "Show only questions and answers; hide tool runs and thinking",
61183
+ currentValue: config.calmMode ? "on" : "off",
61184
+ values: ["on", "off"]
61185
+ },
61186
+ {
61187
+ id: "notificationsBell",
61188
+ label: "Bell",
61189
+ description: "Ring the terminal bell when a turn needs you",
61190
+ currentValue: config.notificationsBell ? "on" : "off",
61191
+ values: ["on", "off"]
61192
+ },
61193
+ {
61194
+ id: "notificationsDesktop",
61195
+ label: "Desktop Notify",
61196
+ description: "Desktop notification (banner / dock badge) via terminal OSC",
61197
+ currentValue: config.notificationsDesktop ? "on" : "off",
61198
+ values: ["on", "off"]
61199
+ },
61082
61200
  {
61083
61201
  id: "extensions",
61084
61202
  label: "Extensions",
@@ -61126,6 +61244,15 @@ var SettingsSelectorComponent = class extends Container {
61126
61244
  case "thinking":
61127
61245
  callbacks.onThinkingLevelChange(newValue);
61128
61246
  break;
61247
+ case "calm":
61248
+ callbacks.onCalmToggle(newValue === "on");
61249
+ break;
61250
+ case "notificationsBell":
61251
+ callbacks.onBellNotificationsChange(newValue === "on");
61252
+ break;
61253
+ case "notificationsDesktop":
61254
+ callbacks.onDesktopNotificationsChange(newValue === "on");
61255
+ break;
61129
61256
  }
61130
61257
  },
61131
61258
  callbacks.onCancel,
@@ -61698,6 +61825,7 @@ function handleExtensionsCommand(host, args) {
61698
61825
 
61699
61826
  // src/slash-commands.ts
61700
61827
  var BUILTIN_SLASH_COMMANDS = [
61828
+ { name: "sessions", description: "Pick a saved session to switch to" },
61701
61829
  { name: "sessions/compact", description: "Summarize history and add a compact checkpoint to the session" },
61702
61830
  { name: "sessions/new", description: "Start a new session" },
61703
61831
  { name: "sessions/resume", description: "Resume a different session" },
@@ -61711,6 +61839,7 @@ var BUILTIN_SLASH_COMMANDS = [
61711
61839
  { name: "settings/model", description: "Select AI model" },
61712
61840
  { name: "settings/theme", description: "Change theme" },
61713
61841
  { name: "settings/thinking", description: "Set thinking level" },
61842
+ { name: "settings/calm", description: "Toggle calm mode: show only questions and answers" },
61714
61843
  { name: "settings/yolo", description: "Toggle or set YOLO mode" },
61715
61844
  { name: "settings/debugs/cache-test", description: "Run provider cache diagnostics with a dedicated 2-request probe" },
61716
61845
  { name: "settings/debugs/context", description: "Append a context summary to ~/.pie/logs/pie-cli.log" },
@@ -62181,11 +62310,8 @@ var SessionSelectorComponent = class extends Container {
62181
62310
  var RESET = "\x1B[0m";
62182
62311
  var INVERSE = "\x1B[7m";
62183
62312
  var DIM = "\x1B[2m";
62184
- function stripAnsi(str2) {
62185
- return str2.replace(/\x1b\[[0-9;]*m/g, "");
62186
- }
62187
62313
  function visibleLength(str2) {
62188
- return stripAnsi(str2).length;
62314
+ return visibleWidth(str2);
62189
62315
  }
62190
62316
  var TREE_CHARS = {
62191
62317
  branch: "\u251C\u2500\u2500 ",
@@ -62625,6 +62751,7 @@ var TreeSelectorComponent = class {
62625
62751
  var AssistantMessageComponent = class extends Container {
62626
62752
  contentContainer;
62627
62753
  hideThinkingBlock;
62754
+ calmMode = false;
62628
62755
  markdownTheme;
62629
62756
  lastMessage;
62630
62757
  constructor(message, hideThinkingBlock = false, markdownTheme = getMarkdownTheme()) {
@@ -62646,12 +62773,26 @@ var AssistantMessageComponent = class extends Container {
62646
62773
  setHideThinkingBlock(hide) {
62647
62774
  this.hideThinkingBlock = hide;
62648
62775
  }
62776
+ /**
62777
+ * Calm mode: suppress thinking traces entirely (no "Thinking..." label,
62778
+ * no surrounding spacers). Errors and text still render.
62779
+ */
62780
+ setCalmMode(calm) {
62781
+ if (this.calmMode === calm) return;
62782
+ this.calmMode = calm;
62783
+ if (this.lastMessage) {
62784
+ this.updateContent(this.lastMessage);
62785
+ }
62786
+ }
62787
+ isCalmMode() {
62788
+ return this.calmMode;
62789
+ }
62649
62790
  updateContent(message) {
62650
62791
  this.lastMessage = message;
62651
62792
  this.contentContainer.clear();
62652
- const hasVisibleContent = message.content.some(
62653
- (c) => c.type === "text" && c.text.trim() || c.type === "thinking" && c.thinking.trim()
62654
- );
62793
+ const hasTextContent = message.content.some((c) => c.type === "text" && c.text.trim());
62794
+ const hasThinkingContent = message.content.some((c) => c.type === "thinking" && c.thinking.trim());
62795
+ const hasVisibleContent = this.calmMode ? hasTextContent : hasTextContent || hasThinkingContent;
62655
62796
  if (hasVisibleContent) {
62656
62797
  this.contentContainer.addChild(new Spacer(1));
62657
62798
  }
@@ -62660,6 +62801,9 @@ var AssistantMessageComponent = class extends Container {
62660
62801
  if (content.type === "text" && content.text.trim()) {
62661
62802
  this.contentContainer.addChild(new Markdown(content.text.trim(), 1, 0, this.markdownTheme));
62662
62803
  } else if (content.type === "thinking" && content.thinking.trim()) {
62804
+ if (this.calmMode) {
62805
+ continue;
62806
+ }
62663
62807
  const hasVisibleContentAfter = message.content.slice(i + 1).some((c) => c.type === "text" && c.text.trim() || c.type === "thinking" && c.thinking.trim());
62664
62808
  if (this.hideThinkingBlock) {
62665
62809
  this.contentContainer.addChild(new Text(theme.italic(theme.fg("thinkingText", "Thinking...")), 1, 0));
@@ -63912,13 +64056,14 @@ async function loadSession(host, sessionId) {
63912
64056
  }
63913
64057
  if (contentBlocks.length > 0) {
63914
64058
  const msgComponent = new AssistantMessageComponent(void 0, host.thinkingLevel === "off");
64059
+ msgComponent.setCalmMode(host.calmMode);
63915
64060
  msgComponent.updateContent({
63916
64061
  role: "assistant",
63917
64062
  content: contentBlocks
63918
64063
  });
63919
64064
  host.chatContainer.addChild(msgComponent);
63920
64065
  }
63921
- if (Array.isArray(assistantMsg.content)) {
64066
+ if (Array.isArray(assistantMsg.content) && !host.calmMode) {
63922
64067
  for (const c of assistantMsg.content) {
63923
64068
  if (typeof c === "object" && c && "type" in c && c.type === "toolCall") {
63924
64069
  const toolCall = c;
@@ -64196,7 +64341,9 @@ function renderMessageHistory(host, messages) {
64196
64341
  stopReason: msg.stopReason,
64197
64342
  errorMessage: msg.errorMessage
64198
64343
  };
64199
- host.chatContainer.addChild(new AssistantMessageComponent(assistantMsg, host.thinkingLevel === "off"));
64344
+ const msgComponent = new AssistantMessageComponent(assistantMsg, host.thinkingLevel === "off");
64345
+ msgComponent.setCalmMode(host.calmMode);
64346
+ host.chatContainer.addChild(msgComponent);
64200
64347
  }
64201
64348
  }
64202
64349
  host.ui.requestRender();
@@ -64565,7 +64712,10 @@ function showSettingsSelector(host) {
64565
64712
  availableThemes: getAvailableThemes(),
64566
64713
  currentModel: currentFullId,
64567
64714
  availableModels,
64568
- yoloMode: host.yoloMode
64715
+ yoloMode: host.yoloMode,
64716
+ calmMode: host.calmMode,
64717
+ notificationsBell: host.options.settingsManager.getBellNotificationsEnabled(),
64718
+ notificationsDesktop: host.options.settingsManager.getDesktopNotificationsEnabled()
64569
64719
  },
64570
64720
  {
64571
64721
  onThinkingLevelChange: (level) => {
@@ -64598,6 +64748,17 @@ function showSettingsSelector(host) {
64598
64748
  await host.handleYoloCommand();
64599
64749
  done();
64600
64750
  },
64751
+ onCalmToggle: (enabled) => {
64752
+ host.setCalmMode(enabled);
64753
+ },
64754
+ onBellNotificationsChange: (enabled) => {
64755
+ host.options.settingsManager.setBellNotificationsEnabled(enabled);
64756
+ host.showStatus(`Bell notifications ${enabled ? "on" : "off"}`);
64757
+ },
64758
+ onDesktopNotificationsChange: (enabled) => {
64759
+ host.options.settingsManager.setDesktopNotificationsEnabled(enabled);
64760
+ host.showStatus(`Desktop notifications ${enabled ? "on" : "off"}`);
64761
+ },
64601
64762
  createExtensionsSubmenu: (onCancel) => createExtensionSelector(host, "user", onCancel),
64602
64763
  onHelp: () => {
64603
64764
  host.handleHotkeysCommand();
@@ -64791,6 +64952,23 @@ function handleThinkingCommand(host, level) {
64791
64952
  }
64792
64953
  host.setThinkingLevel(level);
64793
64954
  }
64955
+ async function handleCalmCommand(host, args) {
64956
+ const mode = (args || "").trim().toLowerCase();
64957
+ if (mode === "status") {
64958
+ host.showStatus(`Calm mode is ${host.calmMode ? "on" : "off"}`);
64959
+ return;
64960
+ }
64961
+ if (mode && !["on", "off", "enable", "enabled", "disable", "disabled", "true", "false", "1", "0"].includes(mode)) {
64962
+ host.showStatus("Usage: /settings/calm [on|off|status]");
64963
+ return;
64964
+ }
64965
+ const nextCalmMode = mode ? ["on", "enable", "enabled", "true", "1"].includes(mode) : !host.calmMode;
64966
+ if (host.calmMode === nextCalmMode) {
64967
+ host.showStatus(`Calm mode already ${host.calmMode ? "on" : "off"}`);
64968
+ return;
64969
+ }
64970
+ host.setCalmMode(nextCalmMode);
64971
+ }
64794
64972
  async function handleYoloCommand(host, args) {
64795
64973
  const mode = (args || "").trim().toLowerCase();
64796
64974
  if (mode === "status") {
@@ -65060,6 +65238,10 @@ function createExtensionContextForCommands(host) {
65060
65238
  };
65061
65239
  }
65062
65240
  function registerBuiltinCommands(host) {
65241
+ commandTree.register(["sessions"], {
65242
+ description: "Pick a saved session to switch to (tree/fork/new below)",
65243
+ handler: () => host.handleResumeCommand()
65244
+ });
65063
65245
  commandTree.register(["sessions", "compact"], {
65064
65246
  description: "Summarize history and replace it with a compact checkpoint",
65065
65247
  handler: () => host.handleCompactCommand()
@@ -65112,6 +65294,10 @@ function registerBuiltinCommands(host) {
65112
65294
  description: "Toggle or set YOLO mode (on/off/status)",
65113
65295
  handler: (_2, args) => host.handleYoloCommand(args)
65114
65296
  });
65297
+ commandTree.register(["settings", "calm"], {
65298
+ description: "Toggle calm mode: show only questions and answers (on/off/status)",
65299
+ handler: (_2, args) => host.handleCalmCommand(args)
65300
+ });
65115
65301
  commandTree.register(["settings", "extensions"], {
65116
65302
  description: "List extensions and manage enable/disable toggles",
65117
65303
  handler: (_2, args) => host.handleExtensionsCommand(args)
@@ -65307,7 +65493,37 @@ function ringTerminalBell(options = {}) {
65307
65493
  function ringTurnSettledBell(host) {
65308
65494
  if ((host.agentSessionController?.pendingMessageCount ?? 0) > 0) return;
65309
65495
  if (host.executionState.read().awaitingContinuation) return;
65310
- host.ringAttentionBell?.();
65496
+ host.ringAttentionBell?.("turn-settled");
65497
+ }
65498
+ function attentionMessage(reason) {
65499
+ switch (reason) {
65500
+ case "interaction":
65501
+ return { title: "Pie", body: "Waiting for your input" };
65502
+ case "turn-settled":
65503
+ return { title: "Pie", body: "Response ready" };
65504
+ case "turn-failed":
65505
+ return { title: "Pie", body: "Turn failed" };
65506
+ case "stall-recovery":
65507
+ return { title: "Pie", body: "Recovered from a stalled turn" };
65508
+ }
65509
+ }
65510
+ var DEFAULT_NOTIFICATION_MIN_INTERVAL_MS = 1e3;
65511
+ var lastNotificationAtMs = 0;
65512
+ function sendDesktopNotification(reason, options = {}) {
65513
+ const {
65514
+ enabled = true,
65515
+ terminal,
65516
+ now = Date.now,
65517
+ minIntervalMs = DEFAULT_NOTIFICATION_MIN_INTERVAL_MS
65518
+ } = options;
65519
+ if (!enabled) return false;
65520
+ if (!terminal || typeof terminal.notify !== "function") return false;
65521
+ const atMs = now();
65522
+ if (atMs - lastNotificationAtMs < minIntervalMs) return false;
65523
+ lastNotificationAtMs = atMs;
65524
+ const { title, body } = attentionMessage(reason);
65525
+ terminal.notify(title, body);
65526
+ return true;
65311
65527
  }
65312
65528
 
65313
65529
  // src/runtime/interactive-runtime-events.ts
@@ -65515,7 +65731,7 @@ function handleSemanticAgentEvent(host, event) {
65515
65731
  host.appendWarningSummary(outcome.terminalSummary);
65516
65732
  }
65517
65733
  host.sessionTrace.completeTurn(event.message, true);
65518
- host.ringAttentionBell?.();
65734
+ host.ringAttentionBell?.("turn-failed");
65519
65735
  host._semanticTurnIndex++;
65520
65736
  break;
65521
65737
  }
@@ -65556,6 +65772,7 @@ function handleMessageStart(host, event) {
65556
65772
  if (event.message?.role === "assistant") {
65557
65773
  host.clearContinuationTimeout("assistant_message_start");
65558
65774
  host.streamingComponent = new AssistantMessageComponent(void 0, host.thinkingLevel === "off");
65775
+ host.streamingComponent.setCalmMode(host.calmMode);
65559
65776
  host.chatContainer.addChild(host.streamingComponent);
65560
65777
  host.sessionTrace.updateAssistantMessage(event.message);
65561
65778
  }
@@ -65584,6 +65801,9 @@ function handleMessageUpdate(host, event) {
65584
65801
  thinkingContent += content2.thinking || "";
65585
65802
  } else if (content2.type === "toolCall") {
65586
65803
  toolCallCount += 1;
65804
+ if (host.calmMode) {
65805
+ continue;
65806
+ }
65587
65807
  const toolCall = content2;
65588
65808
  const contentIndex = message.content.indexOf(content2);
65589
65809
  const partialToolId = `__partial_tool__:${contentIndex}`;
@@ -65649,7 +65869,7 @@ function findToolRenderHint(host, toolName) {
65649
65869
  function handleToolStart(host, event) {
65650
65870
  host.logDebug("tool:start", { toolName: event.toolName, toolCallId: event.toolCallId });
65651
65871
  host.sessionTrace.startTool(event.toolCallId, event.toolName, event.args);
65652
- if (event.toolCallId) {
65872
+ if (event.toolCallId && !host.calmMode) {
65653
65873
  const toolComponent = host.pendingTools.get(event.toolCallId);
65654
65874
  if (toolComponent) {
65655
65875
  toolComponent.updateArgs(event.args);
@@ -65740,6 +65960,22 @@ function handleMessageEnd(host, event) {
65740
65960
  host.safeRequestRender();
65741
65961
  }
65742
65962
 
65963
+ // src/runtime/interactive-calm.ts
65964
+ function applyCalmMode(host, enabled) {
65965
+ const streaming = host.streamingComponent;
65966
+ if (streaming) {
65967
+ streaming.setCalmMode(enabled);
65968
+ }
65969
+ host.chatContainer.clear();
65970
+ host.pendingTools.clear();
65971
+ host.lastStatusSpacer = void 0;
65972
+ host.lastStatusText = void 0;
65973
+ host.renderMessageHistory(host.sessionManager.getMessages());
65974
+ if (streaming) {
65975
+ host.chatContainer.addChild(streaming);
65976
+ }
65977
+ }
65978
+
65743
65979
  // src/input/interactive-input-pipeline.ts
65744
65980
  function shouldQueueUserInput(host) {
65745
65981
  return host.submitLock || (host.agentSessionController?.hasQueuedMessages ?? false);
@@ -67880,6 +68116,13 @@ function isCommandLikeInput(text) {
67880
68116
  // src/components/extension-widgets.ts
67881
68117
  var ExtensionWidgetsComponent = class extends Container {
67882
68118
  slots = /* @__PURE__ */ new Map();
68119
+ calmMode = false;
68120
+ /** Calm mode hides ambient extension widgets (slots keep their data). */
68121
+ setCalmMode(calm) {
68122
+ if (this.calmMode === calm) return;
68123
+ this.calmMode = calm;
68124
+ this.rebuild();
68125
+ }
67883
68126
  setWidget(key, lines) {
67884
68127
  const normalized = (lines ?? []).map((line) => String(line ?? "")).filter((line) => line.trim().length > 0);
67885
68128
  if (normalized.length === 0) {
@@ -67896,6 +68139,9 @@ var ExtensionWidgetsComponent = class extends Container {
67896
68139
  }
67897
68140
  rebuild() {
67898
68141
  this.clear();
68142
+ if (this.calmMode) {
68143
+ return;
68144
+ }
67899
68145
  for (const lines of this.slots.values()) {
67900
68146
  for (const line of lines) {
67901
68147
  this.addChild(new Text(theme.fg("dim", `\xB7 ${line}`), 1, 0));
@@ -68007,9 +68253,10 @@ var FooterComponent = class {
68007
68253
  if (visibleWidth(pwd) > width) {
68008
68254
  const half = Math.floor(width / 2) - 2;
68009
68255
  if (half > 1) {
68010
- const start = pwd.slice(0, half);
68011
- const end = pwd.slice(-(half - 1));
68012
- pwd = `${start}...${end}`;
68256
+ const total = visibleWidth(pwd);
68257
+ const head = sliceByColumn(pwd, 0, half);
68258
+ const tail = sliceByColumn(pwd, Math.max(0, total - (half - 1)), half - 1);
68259
+ pwd = `${head}...${tail}`;
68013
68260
  } else {
68014
68261
  pwd = truncateToWidth(pwd, width);
68015
68262
  }
@@ -68035,6 +68282,9 @@ var FooterComponent = class {
68035
68282
  if (state.planMode) {
68036
68283
  statsParts.push(theme.fg("accent", "[PLAN]"));
68037
68284
  }
68285
+ if (state.calmMode) {
68286
+ statsParts.push(theme.fg("dim", "[CALM]"));
68287
+ }
68038
68288
  if (state.yoloMode) {
68039
68289
  statsParts.push(theme.fg("warning", "[YOLO]"));
68040
68290
  }
@@ -68078,8 +68328,8 @@ var FooterComponent = class {
68078
68328
  const availableForRight = width - statsLeftWidth - minPadding;
68079
68329
  if (availableForRight > 3) {
68080
68330
  const plainRight = rightSide.replace(/\x1b\[[0-9;]*m/g, "");
68081
- const truncated = plainRight.substring(0, availableForRight);
68082
- const padding = " ".repeat(width - statsLeftWidth - truncated.length);
68331
+ const truncated = truncateToWidth(plainRight, availableForRight, "");
68332
+ const padding = " ".repeat(Math.max(0, width - statsLeftWidth - visibleWidth(truncated)));
68083
68333
  statsLine = statsLeft + padding + truncated;
68084
68334
  } else {
68085
68335
  statsLine = truncateToWidth(statsLeft, width);
@@ -68096,13 +68346,27 @@ var FooterComponent = class {
68096
68346
  // src/components/todo-widget.ts
68097
68347
  var TodoWidgetComponent = class extends Container {
68098
68348
  state;
68349
+ calmMode = false;
68099
68350
  constructor(state) {
68100
68351
  super();
68101
68352
  this.state = state;
68102
68353
  void this.refresh();
68103
68354
  }
68355
+ /** Calm mode hides the progress widget (questions/answers only). */
68356
+ setCalmMode(calm) {
68357
+ if (this.calmMode === calm) return;
68358
+ this.calmMode = calm;
68359
+ if (calm) {
68360
+ this.clear();
68361
+ } else {
68362
+ void this.refresh();
68363
+ }
68364
+ }
68104
68365
  async refresh() {
68105
68366
  this.clear();
68367
+ if (this.calmMode) {
68368
+ return;
68369
+ }
68106
68370
  const lines = renderExecutionWidgetLines(this.state.getWidgetSteps());
68107
68371
  if (lines.length === 0) {
68108
68372
  return;
@@ -68390,6 +68654,7 @@ function setupUI(host) {
68390
68654
  modelName: host.options.model ? `${host.options.model.provider}/${host.options.model.id}` : "unknown",
68391
68655
  status: "ready",
68392
68656
  planMode: host.planModeEnabled,
68657
+ calmMode: host.calmMode,
68393
68658
  yoloMode: Boolean(host.yoloMode),
68394
68659
  thinkingLevel: host.thinkingLevel,
68395
68660
  contextWindow: host.options.model?.contextWindow ?? 0
@@ -68918,7 +69183,7 @@ async function recoverHardStalledTurn(host, stalledMs) {
68918
69183
  void host.refreshTodoWidget();
68919
69184
  host.footer.setState({ status: "ready" });
68920
69185
  host.safeRequestRender();
68921
- host.ringAttentionBell?.();
69186
+ host.ringAttentionBell?.("stall-recovery");
68922
69187
  appendWarningSummary(host, "Recovered from a stalled turn (no progress for " + Math.round(stalledMs / 1e3) + "s). The previous turn was interrupted; review its partial output before continuing.");
68923
69188
  if (abortOutcome.terminalSummary) {
68924
69189
  appendWarningSummary(host, abortOutcome.terminalSummary);
@@ -69175,7 +69440,7 @@ function createInteractiveAgentSessionController(host) {
69175
69440
  host.showStatus("Todo list preserved. Send 'continue' when you want to resume it.");
69176
69441
  }
69177
69442
  host.showError(event.failure.message);
69178
- host.ringAttentionBell?.();
69443
+ host.ringAttentionBell?.("turn-failed");
69179
69444
  host.setControllerBusy(false);
69180
69445
  } else if (event.type === "turn_cancelled") {
69181
69446
  const outcome = host.executionState.abort();
@@ -69554,7 +69819,7 @@ function installInteractiveInteractionHandler(host) {
69554
69819
  host.activeInteractionCount++;
69555
69820
  host.setWaitingForUser(true);
69556
69821
  host.footer.setState({ lifecycleEvent: "waiting_for_user_input" });
69557
- host.ringAttentionBell?.();
69822
+ host.ringAttentionBell?.("interaction");
69558
69823
  host.safeRequestRender();
69559
69824
  };
69560
69825
  const endWaitingForUser = () => {
@@ -70164,6 +70429,8 @@ var InteractiveMode = class {
70164
70429
  mentionPickerOptions = [];
70165
70430
  attachmentResolver;
70166
70431
  yoloMode = false;
70432
+ /** Calm mode: render only questions and answers (display-only; persisted). */
70433
+ calmMode = false;
70167
70434
  originalTools = [];
70168
70435
  isInterrupting = false;
70169
70436
  isCompacting = false;
@@ -70214,9 +70481,17 @@ var InteractiveMode = class {
70214
70481
  * interaction prompts, turn completion/failure, and stall recovery. The
70215
70482
  * bell itself enforces a cross-call-site cooldown; the settings gate here
70216
70483
  * is the single opt-out point (settings.json `notifications.bell`).
70484
+ *
70485
+ * A desktop notification (OSC 9/777/99, gated by `notifications.desktop`)
70486
+ * rides along so the host terminal app can badge its dock icon / post a
70487
+ * notification-center banner — the persistent marker the bell can't give.
70217
70488
  */
70218
- ringAttentionBell() {
70489
+ ringAttentionBell(reason) {
70219
70490
  ringTerminalBell({ enabled: this.options.settingsManager.getBellNotificationsEnabled() });
70491
+ sendDesktopNotification(reason ?? "turn-settled", {
70492
+ enabled: this.options.settingsManager.getDesktopNotificationsEnabled(),
70493
+ terminal: this.ui.terminal
70494
+ });
70220
70495
  }
70221
70496
  getContinuationTimeoutMs() {
70222
70497
  return getContinuationTimeoutMs();
@@ -70266,6 +70541,7 @@ var InteractiveMode = class {
70266
70541
  this.originalTools = [...options.allTools];
70267
70542
  this.skillsSection = formatSkillsForPrompt(options.skills);
70268
70543
  this.knowledgeSection = buildCliProjectContextSection(options.cwd);
70544
+ this.calmMode = options.settingsManager.getCalmModeEnabled();
70269
70545
  }
70270
70546
  options;
70271
70547
  get runtimeEventHost() {
@@ -70664,6 +70940,25 @@ var InteractiveMode = class {
70664
70940
  async handleYoloCommand(args) {
70665
70941
  return handleYoloCommand(this, args);
70666
70942
  }
70943
+ /**
70944
+ * Toggle calm mode and rebuild the chat so already-rendered rows follow
70945
+ * the new mode. Delivery, tool execution, and session storage are
70946
+ * unaffected — this is purely a presentation change.
70947
+ */
70948
+ setCalmMode(enabled) {
70949
+ if (this.calmMode === enabled) return;
70950
+ this.calmMode = enabled;
70951
+ this.options.settingsManager.setCalmModeEnabled(enabled);
70952
+ this.footer.setState({ calmMode: enabled });
70953
+ applyCalmMode(this, enabled);
70954
+ this.todoWidget.setCalmMode(enabled);
70955
+ this.extensionWidgets.setCalmMode(enabled);
70956
+ this.showStatus(`Calm mode ${enabled ? "on \u2014 showing questions and answers only" : "off"}`);
70957
+ this.safeRequestRender();
70958
+ }
70959
+ async handleCalmCommand(args) {
70960
+ return handleCalmCommand(this, args);
70961
+ }
70667
70962
  setThinkingLevel(level) {
70668
70963
  return setThinkingLevel(this, level);
70669
70964
  }
@@ -72925,6 +73220,7 @@ async function startChat(initialPrompt, testCommand) {
72925
73220
  }
72926
73221
  flushEarlyLogs();
72927
73222
  if (testCommand) {
73223
+ await interactive.run();
72928
73224
  console.log(`[TEST] Auto-executing command: ${testCommand}`);
72929
73225
  await new Promise((resolve7) => setTimeout(resolve7, 500));
72930
73226
  await interactive.executeCommand(testCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cydm/pie",
3
- "version": "1.0.42",
3
+ "version": "1.0.43",
4
4
  "description": "Pie AI Agent CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,9 +18,9 @@
18
18
  "test": "npm run test:offline",
19
19
  "pretest:offline": "npm run build --workspace @pie/agent-framework && npm run build --workspace @pie/shared-headless-capabilities && npm run build",
20
20
  "posttest:offline": "node --test --import tsx test/remote-model-catalog.test.ts",
21
- "test:offline": "node --test --import tsx test/core-services.test.ts test/cli.test.ts test/cli-auto-compaction.test.ts test/execution-state-manager.test.ts test/queued-steer-execution-state.test.ts test/architecture-boundaries.test.ts test/attachment-resolver.test.ts test/kimi-attachment-extension.test.ts test/document-attachment-extension.test.ts test/auto-compact-overflow.test.ts test/hierarchical-command-system.test.ts test/extensions-enablement.test.ts test/message-queue-unit.test.ts test/evals-harness.test.ts test/session-trace.test.ts test/runtime-mode-context.test.ts test/tool-policy.test.ts test/retry-policy.test.ts test/yolo-command.test.ts test/footer-lifecycle.test.ts test/files-utils.test.ts test/source-size-guard.test.ts test/runtime-logger.test.ts test/release-readiness.test.ts test/package-artifacts.test.ts test/code-intel-typescript-provider.test.ts test/init-extension-contract.test.ts test/skills-runtime.test.ts test/skills-agents-discovery.test.ts test/skills-symlink.test.ts test/model-overrides.test.ts test/auth-storage.test.ts test/extension-register-provider.test.ts test/extension-reload.test.ts test/extension-terminal-input.test.ts test/extension-session-before-events.test.ts test/extension-widgets.test.ts test/extension-render-hint.test.ts test/extension-oauth-restore.test.ts test/cross-host-parity.test.ts test/tool-name-rendering.test.ts test/session-selector.test.ts test/terminal-title.test.ts test/interactive-controller-title.test.ts test/todo-title.terminal.test.ts test/plan-mode-extension.test.ts test/tools-registry.test.ts test/system-prompt-contract.test.ts test/direct-bash.test.ts test/terminal-bell.test.ts test/daily-agent-smoke.terminal.test.ts",
21
+ "test:offline": "node --test --import tsx test/core-services.test.ts test/cli.test.ts test/cli-auto-compaction.test.ts test/execution-state-manager.test.ts test/queued-steer-execution-state.test.ts test/architecture-boundaries.test.ts test/attachment-resolver.test.ts test/kimi-attachment-extension.test.ts test/document-attachment-extension.test.ts test/auto-compact-overflow.test.ts test/hierarchical-command-system.test.ts test/extensions-enablement.test.ts test/message-queue-unit.test.ts test/evals-harness.test.ts test/session-trace.test.ts test/runtime-mode-context.test.ts test/tool-policy.test.ts test/retry-policy.test.ts test/yolo-command.test.ts test/footer-lifecycle.test.ts test/files-utils.test.ts test/source-size-guard.test.ts test/runtime-logger.test.ts test/release-readiness.test.ts test/package-artifacts.test.ts test/code-intel-typescript-provider.test.ts test/init-extension-contract.test.ts test/skills-runtime.test.ts test/skills-agents-discovery.test.ts test/skills-symlink.test.ts test/model-overrides.test.ts test/auth-storage.test.ts test/extension-register-provider.test.ts test/extension-reload.test.ts test/extension-terminal-input.test.ts test/extension-session-before-events.test.ts test/extension-widgets.test.ts test/extension-render-hint.test.ts test/extension-oauth-restore.test.ts test/cross-host-parity.test.ts test/tool-name-rendering.test.ts test/session-selector.test.ts test/terminal-title.test.ts test/interactive-controller-title.test.ts test/todo-title.terminal.test.ts test/plan-mode-extension.test.ts test/tools-registry.test.ts test/system-prompt-contract.test.ts test/direct-bash.test.ts test/terminal-bell.test.ts test/calm-mode.test.ts test/daily-agent-smoke.terminal.test.ts",
22
22
  "test:all": "npm run test:offline && npm run test:terminal:vitest",
23
- "test:terminal:vitest": "vitest run test/mention-file-select.terminal.test.ts test/tool-partial-display.terminal.test.ts test/viewport-position-final.test.ts --no-file-parallelism",
23
+ "test:terminal:vitest": "vitest run test/mention-file-select.terminal.test.ts test/tool-partial-display.terminal.test.ts test/viewport-position-final.test.ts test/resume-header.terminal.test.ts --no-file-parallelism",
24
24
  "test:todo-tui": "PIE_REAL_TODO_TUI=1 node --test --import tsx test/todo-closure.terminal.test.ts",
25
25
  "test:daily-tui": "npm run test:daily-tui:quick",
26
26
  "test:daily-tui:quick": "PIE_REAL_DAILY_TUI=1 PIE_DAILY_TUI_LEVEL=quick node --test --import tsx test/daily-agent-smoke.terminal.test.ts",