@mariozechner/pi-coding-agent 0.12.11 → 0.12.12

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.
@@ -20,6 +20,7 @@ import { FooterComponent } from "./footer.js";
20
20
  import { ModelSelectorComponent } from "./model-selector.js";
21
21
  import { OAuthSelectorComponent } from "./oauth-selector.js";
22
22
  import { QueueModeSelectorComponent } from "./queue-mode-selector.js";
23
+ import { SessionSelectorComponent } from "./session-selector.js";
23
24
  import { ThemeSelectorComponent } from "./theme-selector.js";
24
25
  import { ThinkingSelectorComponent } from "./thinking-selector.js";
25
26
  import { ToolExecutionComponent } from "./tool-execution.js";
@@ -62,6 +63,8 @@ export class TuiRenderer {
62
63
  modelSelector = null;
63
64
  // User message selector (for branching)
64
65
  userMessageSelector = null;
66
+ // Session selector (for resume)
67
+ sessionSelector = null;
65
68
  // OAuth selector
66
69
  oauthSelector = null;
67
70
  // Track if this is the first user message (to skip spacer)
@@ -150,6 +153,10 @@ export class TuiRenderer {
150
153
  name: "autocompact",
151
154
  description: "Toggle automatic context compaction",
152
155
  };
156
+ const resumeCommand = {
157
+ name: "resume",
158
+ description: "Resume a different session",
159
+ };
153
160
  // Load hide thinking block setting
154
161
  this.hideThinkingBlock = settingsManager.getHideThinkingBlock();
155
162
  // Load file-based slash commands
@@ -175,6 +182,7 @@ export class TuiRenderer {
175
182
  clearCommand,
176
183
  compactCommand,
177
184
  autocompactCommand,
185
+ resumeCommand,
178
186
  ...fileSlashCommands,
179
187
  ], process.cwd(), fdPath);
180
188
  this.editor.setAutocompleteProvider(autocompleteProvider);
@@ -377,6 +385,12 @@ export class TuiRenderer {
377
385
  this.editor.setText("");
378
386
  return;
379
387
  }
388
+ // Check for /resume command
389
+ if (text === "/resume") {
390
+ this.showSessionSelector();
391
+ this.editor.setText("");
392
+ return;
393
+ }
380
394
  // Check for file-based slash commands
381
395
  text = expandSlashCommand(text, this.fileCommands);
382
396
  // Normal message submission - validate model and API key first
@@ -408,6 +422,8 @@ export class TuiRenderer {
408
422
  });
409
423
  // Update pending messages display
410
424
  this.updatePendingMessagesDisplay();
425
+ // Add to history for up/down arrow navigation
426
+ this.editor.addToHistory(text);
411
427
  // Clear editor
412
428
  this.editor.setText("");
413
429
  this.ui.requestRender();
@@ -417,6 +433,8 @@ export class TuiRenderer {
417
433
  if (this.onInputCallback) {
418
434
  this.onInputCallback(text);
419
435
  }
436
+ // Add to history for up/down arrow navigation
437
+ this.editor.addToHistory(text);
420
438
  };
421
439
  // Start the UI
422
440
  this.ui.start();
@@ -725,6 +743,19 @@ export class TuiRenderer {
725
743
  }
726
744
  // Clear pending tools after rendering initial messages
727
745
  this.pendingTools.clear();
746
+ // Populate editor history with user messages from the session (oldest first so newest is at index 0)
747
+ for (const message of state.messages) {
748
+ if (message.role === "user") {
749
+ const textBlocks = typeof message.content === "string"
750
+ ? [{ type: "text", text: message.content }]
751
+ : message.content.filter((c) => c.type === "text");
752
+ const textContent = textBlocks.map((c) => c.text).join("");
753
+ // Skip compaction summary messages
754
+ if (textContent && !textContent.startsWith(SUMMARY_PREFIX)) {
755
+ this.editor.addToHistory(textContent);
756
+ }
757
+ }
758
+ }
728
759
  this.ui.requestRender();
729
760
  }
730
761
  async getUserInput() {
@@ -1196,6 +1227,76 @@ export class TuiRenderer {
1196
1227
  this.userMessageSelector = null;
1197
1228
  this.ui.setFocus(this.editor);
1198
1229
  }
1230
+ showSessionSelector() {
1231
+ // Create session selector
1232
+ this.sessionSelector = new SessionSelectorComponent(this.sessionManager, async (sessionPath) => {
1233
+ this.hideSessionSelector();
1234
+ await this.handleResumeSession(sessionPath);
1235
+ }, () => {
1236
+ // Just hide the selector
1237
+ this.hideSessionSelector();
1238
+ this.ui.requestRender();
1239
+ });
1240
+ // Replace editor with selector
1241
+ this.editorContainer.clear();
1242
+ this.editorContainer.addChild(this.sessionSelector);
1243
+ this.ui.setFocus(this.sessionSelector.getSessionList());
1244
+ this.ui.requestRender();
1245
+ }
1246
+ async handleResumeSession(sessionPath) {
1247
+ // Unsubscribe first to prevent processing events during transition
1248
+ this.unsubscribe?.();
1249
+ // Abort and wait for completion
1250
+ this.agent.abort();
1251
+ await this.agent.waitForIdle();
1252
+ // Stop loading animation
1253
+ if (this.loadingAnimation) {
1254
+ this.loadingAnimation.stop();
1255
+ this.loadingAnimation = null;
1256
+ }
1257
+ this.statusContainer.clear();
1258
+ // Clear UI state
1259
+ this.queuedMessages = [];
1260
+ this.pendingMessagesContainer.clear();
1261
+ this.streamingComponent = null;
1262
+ this.pendingTools.clear();
1263
+ // Set the selected session as active
1264
+ this.sessionManager.setSessionFile(sessionPath);
1265
+ // Reload the session
1266
+ const loaded = loadSessionFromEntries(this.sessionManager.loadEntries());
1267
+ this.agent.replaceMessages(loaded.messages);
1268
+ // Restore model if saved in session
1269
+ const savedModel = this.sessionManager.loadModel();
1270
+ if (savedModel) {
1271
+ const availableModels = (await getAvailableModels()).models;
1272
+ const match = availableModels.find((m) => m.provider === savedModel.provider && m.id === savedModel.modelId);
1273
+ if (match) {
1274
+ this.agent.setModel(match);
1275
+ }
1276
+ }
1277
+ // Restore thinking level if saved in session
1278
+ const savedThinking = this.sessionManager.loadThinkingLevel();
1279
+ if (savedThinking) {
1280
+ this.agent.setThinkingLevel(savedThinking);
1281
+ }
1282
+ // Resubscribe to agent
1283
+ this.subscribeToAgent();
1284
+ // Clear and re-render the chat
1285
+ this.chatContainer.clear();
1286
+ this.isFirstUserMessage = true;
1287
+ this.renderInitialMessages(this.agent.state);
1288
+ // Show confirmation message
1289
+ this.chatContainer.addChild(new Spacer(1));
1290
+ this.chatContainer.addChild(new Text(theme.fg("dim", "Resumed session"), 1, 0));
1291
+ this.ui.requestRender();
1292
+ }
1293
+ hideSessionSelector() {
1294
+ // Replace selector with editor in the container
1295
+ this.editorContainer.clear();
1296
+ this.editorContainer.addChild(this.editor);
1297
+ this.sessionSelector = null;
1298
+ this.ui.setFocus(this.editor);
1299
+ }
1199
1300
  async showOAuthSelector(mode) {
1200
1301
  // For logout mode, filter to only show logged-in providers
1201
1302
  let providersToShow = [];