@mariozechner/pi-coding-agent 0.12.10 → 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.
Files changed (51) hide show
  1. package/CHANGELOG.md +138 -268
  2. package/README.md +50 -0
  3. package/dist/fuzzy.d.ts +7 -0
  4. package/dist/fuzzy.d.ts.map +1 -0
  5. package/dist/fuzzy.js +64 -0
  6. package/dist/fuzzy.js.map +1 -0
  7. package/dist/fuzzy.test.d.ts +2 -0
  8. package/dist/fuzzy.test.d.ts.map +1 -0
  9. package/dist/fuzzy.test.js +76 -0
  10. package/dist/fuzzy.test.js.map +1 -0
  11. package/dist/main.d.ts.map +1 -1
  12. package/dist/main.js +61 -49
  13. package/dist/main.js.map +1 -1
  14. package/dist/model-config.d.ts.map +1 -1
  15. package/dist/model-config.js +9 -1
  16. package/dist/model-config.js.map +1 -1
  17. package/dist/settings-manager.d.ts +3 -0
  18. package/dist/settings-manager.d.ts.map +1 -1
  19. package/dist/settings-manager.js +7 -0
  20. package/dist/settings-manager.js.map +1 -1
  21. package/dist/tui/assistant-message.d.ts +3 -1
  22. package/dist/tui/assistant-message.d.ts.map +1 -1
  23. package/dist/tui/assistant-message.js +26 -9
  24. package/dist/tui/assistant-message.js.map +1 -1
  25. package/dist/tui/compaction.d.ts.map +1 -1
  26. package/dist/tui/compaction.js +5 -4
  27. package/dist/tui/compaction.js.map +1 -1
  28. package/dist/tui/custom-editor.d.ts +1 -0
  29. package/dist/tui/custom-editor.d.ts.map +1 -1
  30. package/dist/tui/custom-editor.js +6 -0
  31. package/dist/tui/custom-editor.js.map +1 -1
  32. package/dist/tui/footer.d.ts.map +1 -1
  33. package/dist/tui/footer.js +18 -6
  34. package/dist/tui/footer.js.map +1 -1
  35. package/dist/tui/model-selector.d.ts.map +1 -1
  36. package/dist/tui/model-selector.js +2 -13
  37. package/dist/tui/model-selector.js.map +1 -1
  38. package/dist/tui/session-selector.d.ts.map +1 -1
  39. package/dist/tui/session-selector.js +2 -14
  40. package/dist/tui/session-selector.js.map +1 -1
  41. package/dist/tui/tool-execution.d.ts.map +1 -1
  42. package/dist/tui/tool-execution.js +11 -1
  43. package/dist/tui/tool-execution.js.map +1 -1
  44. package/dist/tui/tui-renderer.d.ts +6 -1
  45. package/dist/tui/tui-renderer.d.ts.map +1 -1
  46. package/dist/tui/tui-renderer.js +133 -15
  47. package/dist/tui/tui-renderer.js.map +1 -1
  48. package/dist/tui/user-message-selector.d.ts.map +1 -1
  49. package/dist/tui/user-message-selector.js +3 -3
  50. package/dist/tui/user-message-selector.js.map +1 -1
  51. package/package.json +4 -4
@@ -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)
@@ -70,6 +73,8 @@ export class TuiRenderer {
70
73
  scopedModels = [];
71
74
  // Tool output expansion state
72
75
  toolOutputExpanded = false;
76
+ // Thinking block visibility state
77
+ hideThinkingBlock = false;
73
78
  // Agent subscription unsubscribe function
74
79
  unsubscribe;
75
80
  // File-based slash commands
@@ -148,6 +153,12 @@ export class TuiRenderer {
148
153
  name: "autocompact",
149
154
  description: "Toggle automatic context compaction",
150
155
  };
156
+ const resumeCommand = {
157
+ name: "resume",
158
+ description: "Resume a different session",
159
+ };
160
+ // Load hide thinking block setting
161
+ this.hideThinkingBlock = settingsManager.getHideThinkingBlock();
151
162
  // Load file-based slash commands
152
163
  this.fileCommands = loadSlashCommands();
153
164
  // Convert file commands to SlashCommand format
@@ -171,6 +182,7 @@ export class TuiRenderer {
171
182
  clearCommand,
172
183
  compactCommand,
173
184
  autocompactCommand,
185
+ resumeCommand,
174
186
  ...fileSlashCommands,
175
187
  ], process.cwd(), fdPath);
176
188
  this.editor.setAutocompleteProvider(autocompleteProvider);
@@ -201,6 +213,9 @@ export class TuiRenderer {
201
213
  theme.fg("dim", "ctrl+o") +
202
214
  theme.fg("muted", " to expand tools") +
203
215
  "\n" +
216
+ theme.fg("dim", "ctrl+t") +
217
+ theme.fg("muted", " to toggle thinking") +
218
+ "\n" +
204
219
  theme.fg("dim", "/") +
205
220
  theme.fg("muted", " for commands") +
206
221
  "\n" +
@@ -269,6 +284,9 @@ export class TuiRenderer {
269
284
  this.editor.onCtrlO = () => {
270
285
  this.toggleToolOutputExpansion();
271
286
  };
287
+ this.editor.onCtrlT = () => {
288
+ this.toggleThinkingBlockVisibility();
289
+ };
272
290
  // Handle editor submission
273
291
  this.editor.onSubmit = async (text) => {
274
292
  text = text.trim();
@@ -367,6 +385,12 @@ export class TuiRenderer {
367
385
  this.editor.setText("");
368
386
  return;
369
387
  }
388
+ // Check for /resume command
389
+ if (text === "/resume") {
390
+ this.showSessionSelector();
391
+ this.editor.setText("");
392
+ return;
393
+ }
370
394
  // Check for file-based slash commands
371
395
  text = expandSlashCommand(text, this.fileCommands);
372
396
  // Normal message submission - validate model and API key first
@@ -398,6 +422,8 @@ export class TuiRenderer {
398
422
  });
399
423
  // Update pending messages display
400
424
  this.updatePendingMessagesDisplay();
425
+ // Add to history for up/down arrow navigation
426
+ this.editor.addToHistory(text);
401
427
  // Clear editor
402
428
  this.editor.setText("");
403
429
  this.ui.requestRender();
@@ -407,6 +433,8 @@ export class TuiRenderer {
407
433
  if (this.onInputCallback) {
408
434
  this.onInputCallback(text);
409
435
  }
436
+ // Add to history for up/down arrow navigation
437
+ this.editor.addToHistory(text);
410
438
  };
411
439
  // Start the UI
412
440
  this.ui.start();
@@ -508,7 +536,7 @@ export class TuiRenderer {
508
536
  }
509
537
  else if (event.message.role === "assistant") {
510
538
  // Create assistant component for streaming
511
- this.streamingComponent = new AssistantMessageComponent();
539
+ this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock);
512
540
  this.chatContainer.addChild(this.streamingComponent);
513
541
  this.streamingComponent.updateContent(event.message);
514
542
  this.ui.requestRender();
@@ -634,7 +662,7 @@ export class TuiRenderer {
634
662
  else if (message.role === "assistant") {
635
663
  const assistantMsg = message;
636
664
  // Add assistant message component
637
- const assistantComponent = new AssistantMessageComponent(assistantMsg);
665
+ const assistantComponent = new AssistantMessageComponent(assistantMsg, this.hideThinkingBlock);
638
666
  this.chatContainer.addChild(assistantComponent);
639
667
  }
640
668
  // Note: tool calls and results are now handled via tool_execution_start/end events
@@ -675,7 +703,7 @@ export class TuiRenderer {
675
703
  }
676
704
  else if (message.role === "assistant") {
677
705
  const assistantMsg = message;
678
- const assistantComponent = new AssistantMessageComponent(assistantMsg);
706
+ const assistantComponent = new AssistantMessageComponent(assistantMsg, this.hideThinkingBlock);
679
707
  this.chatContainer.addChild(assistantComponent);
680
708
  // Create tool execution components for any tool calls
681
709
  for (const content of assistantMsg.content) {
@@ -715,6 +743,19 @@ export class TuiRenderer {
715
743
  }
716
744
  // Clear pending tools after rendering initial messages
717
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
+ }
718
759
  this.ui.requestRender();
719
760
  }
720
761
  async getUserInput() {
@@ -755,7 +796,7 @@ export class TuiRenderer {
755
796
  }
756
797
  else if (message.role === "assistant") {
757
798
  const assistantMsg = message;
758
- const assistantComponent = new AssistantMessageComponent(assistantMsg);
799
+ const assistantComponent = new AssistantMessageComponent(assistantMsg, this.hideThinkingBlock);
759
800
  this.chatContainer.addChild(assistantComponent);
760
801
  for (const content of assistantMsg.content) {
761
802
  if (content.type === "toolCall") {
@@ -923,6 +964,24 @@ export class TuiRenderer {
923
964
  }
924
965
  this.ui.requestRender();
925
966
  }
967
+ toggleThinkingBlockVisibility() {
968
+ this.hideThinkingBlock = !this.hideThinkingBlock;
969
+ this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock);
970
+ // Update all assistant message components and rebuild their content
971
+ for (const child of this.chatContainer.children) {
972
+ if (child instanceof AssistantMessageComponent) {
973
+ child.setHideThinkingBlock(this.hideThinkingBlock);
974
+ }
975
+ }
976
+ // Rebuild chat to apply visibility change
977
+ this.chatContainer.clear();
978
+ this.rebuildChatFromMessages();
979
+ // Show brief notification
980
+ const status = this.hideThinkingBlock ? "hidden" : "visible";
981
+ this.chatContainer.addChild(new Spacer(1));
982
+ this.chatContainer.addChild(new Text(theme.fg("dim", `Thinking blocks: ${status}`), 1, 0));
983
+ this.ui.requestRender();
984
+ }
926
985
  clearEditor() {
927
986
  this.editor.setText("");
928
987
  this.ui.requestRender();
@@ -939,14 +998,6 @@ export class TuiRenderer {
939
998
  this.chatContainer.addChild(new Text(theme.fg("warning", `Warning: ${warningMessage}`), 1, 0));
940
999
  this.ui.requestRender();
941
1000
  }
942
- showSuccess(message, detail) {
943
- this.chatContainer.addChild(new Spacer(1));
944
- const text = detail
945
- ? `${theme.fg("success", message)}\n${theme.fg("muted", detail)}`
946
- : theme.fg("success", message);
947
- this.chatContainer.addChild(new Text(text, 1, 1));
948
- this.ui.requestRender();
949
- }
950
1001
  showThinkingSelector() {
951
1002
  // Create thinking selector with current level
952
1003
  this.thinkingSelector = new ThinkingSelectorComponent(this.agent.state.thinkingLevel, (level) => {
@@ -1176,6 +1227,76 @@ export class TuiRenderer {
1176
1227
  this.userMessageSelector = null;
1177
1228
  this.ui.setFocus(this.editor);
1178
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
+ }
1179
1300
  async showOAuthSelector(mode) {
1180
1301
  // For logout mode, filter to only show logged-in providers
1181
1302
  let providersToShow = [];
@@ -1521,9 +1642,6 @@ export class TuiRenderer {
1521
1642
  this.chatContainer.addChild(compactionComponent);
1522
1643
  // Update footer with new state (fixes context % display)
1523
1644
  this.footer.updateState(this.agent.state);
1524
- // Show success message
1525
- const successTitle = isAuto ? "✓ Context auto-compacted" : "✓ Context compacted";
1526
- this.showSuccess(successTitle, `Reduced from ${compactionEntry.tokensBefore.toLocaleString()} tokens`);
1527
1645
  }
1528
1646
  catch (error) {
1529
1647
  const message = error instanceof Error ? error.message : String(error);