@mariozechner/pi-coding-agent 0.47.0 → 0.48.0

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 (97) hide show
  1. package/CHANGELOG.md +32 -1
  2. package/README.md +34 -2
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +10 -1
  5. package/dist/config.js.map +1 -1
  6. package/dist/core/agent-session.d.ts +5 -0
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +44 -8
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/compaction/branch-summarization.d.ts +2 -0
  11. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  12. package/dist/core/compaction/branch-summarization.js +11 -4
  13. package/dist/core/compaction/branch-summarization.js.map +1 -1
  14. package/dist/core/extensions/loader.d.ts.map +1 -1
  15. package/dist/core/extensions/loader.js.map +1 -1
  16. package/dist/core/extensions/runner.d.ts +3 -0
  17. package/dist/core/extensions/runner.d.ts.map +1 -1
  18. package/dist/core/extensions/runner.js.map +1 -1
  19. package/dist/core/extensions/types.d.ts +21 -5
  20. package/dist/core/extensions/types.d.ts.map +1 -1
  21. package/dist/core/extensions/types.js.map +1 -1
  22. package/dist/core/prompt-templates.d.ts +5 -1
  23. package/dist/core/prompt-templates.d.ts.map +1 -1
  24. package/dist/core/prompt-templates.js +18 -1
  25. package/dist/core/prompt-templates.js.map +1 -1
  26. package/dist/core/sdk.d.ts.map +1 -1
  27. package/dist/core/sdk.js +5 -1
  28. package/dist/core/sdk.js.map +1 -1
  29. package/dist/core/session-manager.d.ts +8 -0
  30. package/dist/core/session-manager.d.ts.map +1 -1
  31. package/dist/core/session-manager.js +43 -0
  32. package/dist/core/session-manager.js.map +1 -1
  33. package/dist/core/settings-manager.d.ts +9 -0
  34. package/dist/core/settings-manager.d.ts.map +1 -1
  35. package/dist/core/settings-manager.js +21 -0
  36. package/dist/core/settings-manager.js.map +1 -1
  37. package/dist/core/tools/bash.d.ts +2 -0
  38. package/dist/core/tools/bash.d.ts.map +1 -1
  39. package/dist/core/tools/bash.js +4 -1
  40. package/dist/core/tools/bash.js.map +1 -1
  41. package/dist/core/tools/index.d.ts +4 -1
  42. package/dist/core/tools/index.d.ts.map +1 -1
  43. package/dist/core/tools/index.js +8 -3
  44. package/dist/core/tools/index.js.map +1 -1
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +2 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/main.d.ts.map +1 -1
  50. package/dist/main.js +52 -14
  51. package/dist/main.js.map +1 -1
  52. package/dist/modes/interactive/components/custom-editor.d.ts +2 -2
  53. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  54. package/dist/modes/interactive/components/custom-editor.js +2 -2
  55. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  56. package/dist/modes/interactive/components/extension-editor.d.ts +2 -2
  57. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  58. package/dist/modes/interactive/components/extension-editor.js +3 -3
  59. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  60. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  61. package/dist/modes/interactive/components/session-selector.js +5 -3
  62. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  63. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  64. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  65. package/dist/modes/interactive/components/settings-selector.js +12 -0
  66. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  67. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  68. package/dist/modes/interactive/components/tool-execution.js +3 -1
  69. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  70. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  71. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  72. package/dist/modes/interactive/interactive-mode.js +120 -78
  73. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  74. package/dist/modes/interactive/theme/theme-schema.json +23 -3
  75. package/dist/modes/print-mode.d.ts.map +1 -1
  76. package/dist/modes/print-mode.js +6 -1
  77. package/dist/modes/print-mode.js.map +1 -1
  78. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  79. package/dist/modes/rpc/rpc-mode.js +6 -1
  80. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  81. package/dist/utils/image-convert.d.ts.map +1 -1
  82. package/dist/utils/image-convert.js +6 -1
  83. package/dist/utils/image-convert.js.map +1 -1
  84. package/dist/utils/image-resize.d.ts.map +1 -1
  85. package/dist/utils/image-resize.js +14 -1
  86. package/dist/utils/image-resize.js.map +1 -1
  87. package/dist/utils/photon.d.ts +28 -0
  88. package/dist/utils/photon.d.ts.map +1 -0
  89. package/dist/utils/photon.js +51 -0
  90. package/dist/utils/photon.js.map +1 -0
  91. package/docs/extensions.md +32 -4
  92. package/docs/rpc.md +2 -0
  93. package/docs/sdk.md +1 -1
  94. package/docs/tree.md +20 -2
  95. package/examples/extensions/with-deps/package-lock.json +2 -2
  96. package/examples/extensions/with-deps/package.json +1 -1
  97. package/package.json +4 -4
@@ -65,6 +65,7 @@ export class InteractiveMode {
65
65
  keybindings;
66
66
  version;
67
67
  isInitialized = false;
68
+ hasRenderedInitialMessages = false;
68
69
  onInputCallback;
69
70
  loadingAnimation = undefined;
70
71
  defaultWorkingMessage = "Working...";
@@ -136,7 +137,8 @@ export class InteractiveMode {
136
137
  this.statusContainer = new Container();
137
138
  this.widgetContainer = new Container();
138
139
  this.keybindings = KeybindingsManager.create();
139
- this.defaultEditor = new CustomEditor(this.ui, getEditorTheme(), this.keybindings);
140
+ const editorPaddingX = this.settingsManager.getEditorPaddingX();
141
+ this.defaultEditor = new CustomEditor(this.ui, getEditorTheme(), this.keybindings, { paddingX: editorPaddingX });
140
142
  this.editor = this.defaultEditor;
141
143
  this.editorContainer = new Container();
142
144
  this.editorContainer.addChild(this.editor);
@@ -204,6 +206,7 @@ export class InteractiveMode {
204
206
  const extensionCommands = (this.session.extensionRunner?.getRegisteredCommands() ?? []).map((cmd) => ({
205
207
  name: cmd.name,
206
208
  description: cmd.description ?? "(extension command)",
209
+ getArgumentCompletions: cmd.getArgumentCompletions,
207
210
  }));
208
211
  // Build skill commands from session.skills (if enabled)
209
212
  this.skillCommands.clear();
@@ -230,53 +233,67 @@ export class InteractiveMode {
230
233
  // Setup autocomplete with fd tool for file path completion
231
234
  this.fdPath = await ensureTool("fd");
232
235
  this.setupAutocomplete(this.fdPath);
233
- // Add header with keybindings from config
234
- const logo = theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
235
- // Build startup instructions using keybinding hint helpers
236
- const kb = this.keybindings;
237
- const hint = (action, desc) => appKeyHint(kb, action, desc);
238
- const instructions = [
239
- hint("interrupt", "to interrupt"),
240
- hint("clear", "to clear"),
241
- rawKeyHint(`${appKey(kb, "clear")} twice`, "to exit"),
242
- hint("exit", "to exit (empty)"),
243
- hint("suspend", "to suspend"),
244
- keyHint("deleteToLineEnd", "to delete to end"),
245
- hint("cycleThinkingLevel", "to cycle thinking"),
246
- rawKeyHint(`${appKey(kb, "cycleModelForward")}/${appKey(kb, "cycleModelBackward")}`, "to cycle models"),
247
- hint("selectModel", "to select model"),
248
- hint("expandTools", "to expand tools"),
249
- hint("toggleThinking", "to toggle thinking"),
250
- hint("externalEditor", "for external editor"),
251
- rawKeyHint("/", "for commands"),
252
- rawKeyHint("!", "to run bash"),
253
- rawKeyHint("!!", "to run bash (no context)"),
254
- hint("followUp", "to queue follow-up"),
255
- hint("dequeue", "to edit all queued messages"),
256
- hint("pasteImage", "to paste image"),
257
- rawKeyHint("drop files", "to attach"),
258
- ].join("\n");
259
- this.builtInHeader = new Text(`${logo}\n${instructions}`, 1, 0);
260
- // Setup UI layout
261
- this.ui.addChild(new Spacer(1));
262
- this.ui.addChild(this.builtInHeader);
263
- this.ui.addChild(new Spacer(1));
264
- // Add changelog if provided
265
- if (this.changelogMarkdown) {
266
- this.ui.addChild(new DynamicBorder());
267
- if (this.settingsManager.getCollapseChangelog()) {
236
+ // Add header with keybindings from config (unless silenced)
237
+ if (!this.settingsManager.getQuietStartup()) {
238
+ const logo = theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
239
+ // Build startup instructions using keybinding hint helpers
240
+ const kb = this.keybindings;
241
+ const hint = (action, desc) => appKeyHint(kb, action, desc);
242
+ const instructions = [
243
+ hint("interrupt", "to interrupt"),
244
+ hint("clear", "to clear"),
245
+ rawKeyHint(`${appKey(kb, "clear")} twice`, "to exit"),
246
+ hint("exit", "to exit (empty)"),
247
+ hint("suspend", "to suspend"),
248
+ keyHint("deleteToLineEnd", "to delete to end"),
249
+ hint("cycleThinkingLevel", "to cycle thinking"),
250
+ rawKeyHint(`${appKey(kb, "cycleModelForward")}/${appKey(kb, "cycleModelBackward")}`, "to cycle models"),
251
+ hint("selectModel", "to select model"),
252
+ hint("expandTools", "to expand tools"),
253
+ hint("toggleThinking", "to toggle thinking"),
254
+ hint("externalEditor", "for external editor"),
255
+ rawKeyHint("/", "for commands"),
256
+ rawKeyHint("!", "to run bash"),
257
+ rawKeyHint("!!", "to run bash (no context)"),
258
+ hint("followUp", "to queue follow-up"),
259
+ hint("dequeue", "to edit all queued messages"),
260
+ hint("pasteImage", "to paste image"),
261
+ rawKeyHint("drop files", "to attach"),
262
+ ].join("\n");
263
+ this.builtInHeader = new Text(`${logo}\n${instructions}`, 1, 0);
264
+ // Setup UI layout
265
+ this.ui.addChild(new Spacer(1));
266
+ this.ui.addChild(this.builtInHeader);
267
+ this.ui.addChild(new Spacer(1));
268
+ // Add changelog if provided
269
+ if (this.changelogMarkdown) {
270
+ this.ui.addChild(new DynamicBorder());
271
+ if (this.settingsManager.getCollapseChangelog()) {
272
+ const versionMatch = this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/);
273
+ const latestVersion = versionMatch ? versionMatch[1] : this.version;
274
+ const condensedText = `Updated to v${latestVersion}. Use ${theme.bold("/changelog")} to view full changelog.`;
275
+ this.ui.addChild(new Text(condensedText, 1, 0));
276
+ }
277
+ else {
278
+ this.ui.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
279
+ this.ui.addChild(new Spacer(1));
280
+ this.ui.addChild(new Markdown(this.changelogMarkdown.trim(), 1, 0, getMarkdownTheme()));
281
+ this.ui.addChild(new Spacer(1));
282
+ }
283
+ this.ui.addChild(new DynamicBorder());
284
+ }
285
+ }
286
+ else {
287
+ // Minimal header when silenced
288
+ this.builtInHeader = new Text("", 0, 0);
289
+ if (this.changelogMarkdown) {
290
+ // Still show changelog notification even in silent mode
291
+ this.ui.addChild(new Spacer(1));
268
292
  const versionMatch = this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/);
269
293
  const latestVersion = versionMatch ? versionMatch[1] : this.version;
270
294
  const condensedText = `Updated to v${latestVersion}. Use ${theme.bold("/changelog")} to view full changelog.`;
271
295
  this.ui.addChild(new Text(condensedText, 1, 0));
272
296
  }
273
- else {
274
- this.ui.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
275
- this.ui.addChild(new Spacer(1));
276
- this.ui.addChild(new Markdown(this.changelogMarkdown.trim(), 1, 0, getMarkdownTheme()));
277
- this.ui.addChild(new Spacer(1));
278
- }
279
- this.ui.addChild(new DynamicBorder());
280
297
  }
281
298
  this.ui.addChild(this.chatContainer);
282
299
  this.ui.addChild(this.pendingMessagesContainer);
@@ -421,33 +438,38 @@ export class InteractiveMode {
421
438
  * Initialize the extension system with TUI-based UI context.
422
439
  */
423
440
  async initExtensions() {
424
- // Show loaded project context files
425
- const contextFiles = loadProjectContextFiles();
426
- if (contextFiles.length > 0) {
427
- const contextList = contextFiles.map((f) => theme.fg("dim", ` ${f.path}`)).join("\n");
428
- this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded context:\n") + contextList, 0, 0));
429
- this.chatContainer.addChild(new Spacer(1));
430
- }
431
- // Show loaded skills (already discovered by SDK)
432
- const skills = this.session.skills;
433
- if (skills.length > 0) {
434
- const skillList = skills.map((s) => theme.fg("dim", ` ${s.filePath}`)).join("\n");
435
- this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded skills:\n") + skillList, 0, 0));
436
- this.chatContainer.addChild(new Spacer(1));
437
- }
438
- // Show skill warnings if any
439
- const skillWarnings = this.session.skillWarnings;
440
- if (skillWarnings.length > 0) {
441
- const warningList = skillWarnings.map((w) => theme.fg("warning", ` ${w.skillPath}: ${w.message}`)).join("\n");
442
- this.chatContainer.addChild(new Text(theme.fg("warning", "Skill warnings:\n") + warningList, 0, 0));
443
- this.chatContainer.addChild(new Spacer(1));
444
- }
445
- // Show loaded prompt templates
446
- const templates = this.session.promptTemplates;
447
- if (templates.length > 0) {
448
- const templateList = templates.map((t) => theme.fg("dim", ` /${t.name} ${t.source}`)).join("\n");
449
- this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded prompt templates:\n") + templateList, 0, 0));
450
- this.chatContainer.addChild(new Spacer(1));
441
+ // Show discovery info unless silenced
442
+ if (!this.settingsManager.getQuietStartup()) {
443
+ // Show loaded project context files
444
+ const contextFiles = loadProjectContextFiles();
445
+ if (contextFiles.length > 0) {
446
+ const contextList = contextFiles.map((f) => theme.fg("dim", ` ${f.path}`)).join("\n");
447
+ this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded context:\n") + contextList, 0, 0));
448
+ this.chatContainer.addChild(new Spacer(1));
449
+ }
450
+ // Show loaded skills (already discovered by SDK)
451
+ const skills = this.session.skills;
452
+ if (skills.length > 0) {
453
+ const skillList = skills.map((s) => theme.fg("dim", ` ${s.filePath}`)).join("\n");
454
+ this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded skills:\n") + skillList, 0, 0));
455
+ this.chatContainer.addChild(new Spacer(1));
456
+ }
457
+ // Show skill warnings if any
458
+ const skillWarnings = this.session.skillWarnings;
459
+ if (skillWarnings.length > 0) {
460
+ const warningList = skillWarnings
461
+ .map((w) => theme.fg("warning", ` ${w.skillPath}: ${w.message}`))
462
+ .join("\n");
463
+ this.chatContainer.addChild(new Text(theme.fg("warning", "Skill warnings:\n") + warningList, 0, 0));
464
+ this.chatContainer.addChild(new Spacer(1));
465
+ }
466
+ // Show loaded prompt templates
467
+ const templates = this.session.promptTemplates;
468
+ if (templates.length > 0) {
469
+ const templateList = templates.map((t) => theme.fg("dim", ` /${t.name} ${t.source}`)).join("\n");
470
+ this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded prompt templates:\n") + templateList, 0, 0));
471
+ this.chatContainer.addChild(new Spacer(1));
472
+ }
451
473
  }
452
474
  const extensionRunner = this.session.extensionRunner;
453
475
  if (!extensionRunner) {
@@ -463,7 +485,9 @@ export class InteractiveMode {
463
485
  this.session
464
486
  .sendCustomMessage(message, options)
465
487
  .then(() => {
466
- if (!wasStreaming && message.display) {
488
+ // Don't rebuild if initial render hasn't happened yet
489
+ // (renderInitialMessages will handle it)
490
+ if (!wasStreaming && message.display && this.hasRenderedInitialMessages) {
467
491
  this.rebuildChatFromMessages();
468
492
  }
469
493
  })
@@ -547,7 +571,12 @@ export class InteractiveMode {
547
571
  return { cancelled: false };
548
572
  },
549
573
  navigateTree: async (targetId, options) => {
550
- const result = await this.session.navigateTree(targetId, { summarize: options?.summarize });
574
+ const result = await this.session.navigateTree(targetId, {
575
+ summarize: options?.summarize,
576
+ customInstructions: options?.customInstructions,
577
+ replaceInstructions: options?.replaceInstructions,
578
+ label: options?.label,
579
+ });
551
580
  if (result.cancelled) {
552
581
  return { cancelled: true };
553
582
  }
@@ -566,12 +595,14 @@ export class InteractiveMode {
566
595
  });
567
596
  // Set up extension-registered shortcuts
568
597
  this.setupExtensionShortcuts(extensionRunner);
569
- // Show loaded extensions
570
- const extensionPaths = extensionRunner.getExtensionPaths();
571
- if (extensionPaths.length > 0) {
572
- const extList = extensionPaths.map((p) => theme.fg("dim", ` ${p}`)).join("\n");
573
- this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded extensions:\n") + extList, 0, 0));
574
- this.chatContainer.addChild(new Spacer(1));
598
+ // Show loaded extensions (unless silenced)
599
+ if (!this.settingsManager.getQuietStartup()) {
600
+ const extensionPaths = extensionRunner.getExtensionPaths();
601
+ if (extensionPaths.length > 0) {
602
+ const extList = extensionPaths.map((p) => theme.fg("dim", ` ${p}`)).join("\n");
603
+ this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded extensions:\n") + extList, 0, 0));
604
+ this.chatContainer.addChild(new Spacer(1));
605
+ }
575
606
  }
576
607
  // Emit session_start event
577
608
  await extensionRunner.emit({
@@ -1490,6 +1521,11 @@ export class InteractiveMode {
1490
1521
  });
1491
1522
  this.footer.invalidate();
1492
1523
  }
1524
+ else if (event.errorMessage) {
1525
+ // Compaction failed (e.g., quota exceeded, API error)
1526
+ this.chatContainer.addChild(new Spacer(1));
1527
+ this.chatContainer.addChild(new Text(theme.fg("error", event.errorMessage), 1, 0));
1528
+ }
1493
1529
  void this.flushCompactionQueue({ willRetry: event.willRetry });
1494
1530
  this.ui.requestRender();
1495
1531
  break;
@@ -1677,6 +1713,7 @@ export class InteractiveMode {
1677
1713
  this.ui.requestRender();
1678
1714
  }
1679
1715
  renderInitialMessages() {
1716
+ this.hasRenderedInitialMessages = true;
1680
1717
  // Get aligned messages and entries from session context
1681
1718
  const context = this.sessionManager.buildSessionContext();
1682
1719
  this.renderSessionContext(context, {
@@ -2107,6 +2144,7 @@ export class InteractiveMode {
2107
2144
  hideThinkingBlock: this.hideThinkingBlock,
2108
2145
  collapseChangelog: this.settingsManager.getCollapseChangelog(),
2109
2146
  doubleEscapeAction: this.settingsManager.getDoubleEscapeAction(),
2147
+ editorPaddingX: this.settingsManager.getEditorPaddingX(),
2110
2148
  }, {
2111
2149
  onAutoCompactChange: (enabled) => {
2112
2150
  this.session.setAutoCompactionEnabled(enabled);
@@ -2173,6 +2211,10 @@ export class InteractiveMode {
2173
2211
  onDoubleEscapeActionChange: (action) => {
2174
2212
  this.settingsManager.setDoubleEscapeAction(action);
2175
2213
  },
2214
+ onEditorPaddingXChange: (padding) => {
2215
+ this.settingsManager.setEditorPaddingX(padding);
2216
+ this.defaultEditor.setPaddingX(padding);
2217
+ },
2176
2218
  onCancel: () => {
2177
2219
  done();
2178
2220
  this.ui.requestRender();