@mariozechner/pi-coding-agent 0.49.1 → 0.49.3

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 (100) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/README.md +5 -11
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +1 -0
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/config.d.ts +2 -0
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +6 -0
  9. package/dist/config.js.map +1 -1
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +6 -0
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/export-html/template.css +19 -0
  14. package/dist/core/export-html/template.js +70 -5
  15. package/dist/core/extensions/index.d.ts +1 -1
  16. package/dist/core/extensions/index.d.ts.map +1 -1
  17. package/dist/core/extensions/index.js.map +1 -1
  18. package/dist/core/extensions/types.d.ts +10 -3
  19. package/dist/core/extensions/types.d.ts.map +1 -1
  20. package/dist/core/extensions/types.js.map +1 -1
  21. package/dist/core/model-registry.d.ts.map +1 -1
  22. package/dist/core/model-registry.js +1 -3
  23. package/dist/core/model-registry.js.map +1 -1
  24. package/dist/core/sdk.d.ts.map +1 -1
  25. package/dist/core/sdk.js +11 -3
  26. package/dist/core/sdk.js.map +1 -1
  27. package/dist/core/settings-manager.d.ts +5 -0
  28. package/dist/core/settings-manager.d.ts.map +1 -1
  29. package/dist/core/settings-manager.js +3 -0
  30. package/dist/core/settings-manager.js.map +1 -1
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js.map +1 -1
  34. package/dist/main.d.ts.map +1 -1
  35. package/dist/main.js +2 -1
  36. package/dist/main.js.map +1 -1
  37. package/dist/modes/interactive/components/assistant-message.d.ts +3 -2
  38. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  39. package/dist/modes/interactive/components/assistant-message.js +5 -3
  40. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  41. package/dist/modes/interactive/components/branch-summary-message.d.ts +3 -2
  42. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  43. package/dist/modes/interactive/components/branch-summary-message.js +4 -2
  44. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  45. package/dist/modes/interactive/components/compaction-summary-message.d.ts +3 -2
  46. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  47. package/dist/modes/interactive/components/compaction-summary-message.js +4 -2
  48. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  49. package/dist/modes/interactive/components/custom-message.d.ts +3 -2
  50. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  51. package/dist/modes/interactive/components/custom-message.js +4 -2
  52. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  53. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  54. package/dist/modes/interactive/components/footer.js +5 -0
  55. package/dist/modes/interactive/components/footer.js.map +1 -1
  56. package/dist/modes/interactive/components/model-selector.d.ts +9 -0
  57. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  58. package/dist/modes/interactive/components/model-selector.js +84 -38
  59. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  60. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  61. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  62. package/dist/modes/interactive/components/settings-selector.js +10 -0
  63. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  64. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  65. package/dist/modes/interactive/components/tool-execution.js +7 -0
  66. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  67. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  68. package/dist/modes/interactive/components/tree-selector.js +2 -2
  69. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  70. package/dist/modes/interactive/components/user-message.d.ts +2 -2
  71. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  72. package/dist/modes/interactive/components/user-message.js +2 -2
  73. package/dist/modes/interactive/components/user-message.js.map +1 -1
  74. package/dist/modes/interactive/interactive-mode.d.ts +10 -2
  75. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  76. package/dist/modes/interactive/interactive-mode.js +81 -38
  77. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  78. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  79. package/dist/modes/interactive/theme/theme.js +7 -3
  80. package/dist/modes/interactive/theme/theme.js.map +1 -1
  81. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  82. package/dist/modes/rpc/rpc-mode.js +2 -1
  83. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  84. package/dist/modes/rpc/rpc-types.d.ts +1 -0
  85. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  86. package/dist/modes/rpc/rpc-types.js.map +1 -1
  87. package/dist/utils/shell.d.ts.map +1 -1
  88. package/dist/utils/shell.js +3 -2
  89. package/dist/utils/shell.js.map +1 -1
  90. package/docs/extensions.md +5 -3
  91. package/docs/tui.md +6 -3
  92. package/examples/extensions/README.md +3 -0
  93. package/examples/extensions/antigravity-image-gen.ts +413 -0
  94. package/examples/extensions/inline-bash.ts +94 -0
  95. package/examples/extensions/question.ts +9 -22
  96. package/examples/extensions/space-invaders.ts +560 -0
  97. package/examples/extensions/widget-placement.ts +17 -0
  98. package/examples/extensions/with-deps/package-lock.json +2 -2
  99. package/examples/extensions/with-deps/package.json +1 -1
  100. package/package.json +4 -4
@@ -9,7 +9,7 @@ import * as path from "node:path";
9
9
  import { getOAuthProviders, } from "@mariozechner/pi-ai";
10
10
  import { CombinedAutocompleteProvider, Container, fuzzyFilter, Loader, Markdown, matchesKey, ProcessTerminal, Spacer, Text, TruncatedText, TUI, visibleWidth, } from "@mariozechner/pi-tui";
11
11
  import { spawn, spawnSync } from "child_process";
12
- import { APP_NAME, getAuthPath, getDebugLogPath, isBunBinary, isBunRuntime, VERSION } from "../../config.js";
12
+ import { APP_NAME, getAuthPath, getDebugLogPath, getShareViewerUrl, isBunBinary, isBunRuntime, VERSION, } from "../../config.js";
13
13
  import { FooterDataProvider } from "../../core/footer-data-provider.js";
14
14
  import { KeybindingsManager } from "../../core/keybindings.js";
15
15
  import { createCompactionSummaryMessage } from "../../core/messages.js";
@@ -108,9 +108,11 @@ export class InteractiveMode {
108
108
  extensionSelector = undefined;
109
109
  extensionInput = undefined;
110
110
  extensionEditor = undefined;
111
- // Extension widgets (components rendered above the editor)
112
- extensionWidgets = new Map();
113
- widgetContainer;
111
+ // Extension widgets (components rendered above/below the editor)
112
+ extensionWidgetsAbove = new Map();
113
+ extensionWidgetsBelow = new Map();
114
+ widgetContainerAbove;
115
+ widgetContainerBelow;
114
116
  // Custom footer from extension (undefined = use built-in footer)
115
117
  customFooter = undefined;
116
118
  // Built-in header (logo + keybinding hints + changelog)
@@ -135,7 +137,8 @@ export class InteractiveMode {
135
137
  this.chatContainer = new Container();
136
138
  this.pendingMessagesContainer = new Container();
137
139
  this.statusContainer = new Container();
138
- this.widgetContainer = new Container();
140
+ this.widgetContainerAbove = new Container();
141
+ this.widgetContainerBelow = new Container();
139
142
  this.keybindings = KeybindingsManager.create();
140
143
  const editorPaddingX = this.settingsManager.getEditorPaddingX();
141
144
  this.defaultEditor = new CustomEditor(this.ui, getEditorTheme(), this.keybindings, { paddingX: editorPaddingX });
@@ -277,7 +280,7 @@ export class InteractiveMode {
277
280
  else {
278
281
  this.ui.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
279
282
  this.ui.addChild(new Spacer(1));
280
- this.ui.addChild(new Markdown(this.changelogMarkdown.trim(), 1, 0, getMarkdownTheme()));
283
+ this.ui.addChild(new Markdown(this.changelogMarkdown.trim(), 1, 0, this.getMarkdownThemeWithSettings()));
281
284
  this.ui.addChild(new Spacer(1));
282
285
  }
283
286
  this.ui.addChild(new DynamicBorder());
@@ -298,9 +301,10 @@ export class InteractiveMode {
298
301
  this.ui.addChild(this.chatContainer);
299
302
  this.ui.addChild(this.pendingMessagesContainer);
300
303
  this.ui.addChild(this.statusContainer);
301
- this.ui.addChild(this.widgetContainer);
302
304
  this.renderWidgets(); // Initialize with default spacer
305
+ this.ui.addChild(this.widgetContainerAbove);
303
306
  this.ui.addChild(this.editorContainer);
307
+ this.ui.addChild(this.widgetContainerBelow);
304
308
  this.ui.addChild(this.footer);
305
309
  this.ui.setFocus(this.editor);
306
310
  this.setupKeyHandlers();
@@ -309,8 +313,7 @@ export class InteractiveMode {
309
313
  this.ui.start();
310
314
  this.isInitialized = true;
311
315
  // Set terminal title
312
- const cwdBasename = path.basename(process.cwd());
313
- this.ui.terminal.setTitle(`pi - ${cwdBasename}`);
316
+ this.updateTerminalTitle();
314
317
  // Initialize extensions with TUI-based UI context
315
318
  await this.initExtensions();
316
319
  // Subscribe to agent events
@@ -326,6 +329,19 @@ export class InteractiveMode {
326
329
  this.ui.requestRender();
327
330
  });
328
331
  }
332
+ /**
333
+ * Update terminal title with session name and cwd.
334
+ */
335
+ updateTerminalTitle() {
336
+ const cwdBasename = path.basename(process.cwd());
337
+ const sessionName = this.sessionManager.getSessionName();
338
+ if (sessionName) {
339
+ this.ui.terminal.setTitle(`π - ${sessionName} - ${cwdBasename}`);
340
+ }
341
+ else {
342
+ this.ui.terminal.setTitle(`π - ${cwdBasename}`);
343
+ }
344
+ }
329
345
  /**
330
346
  * Run the interactive mode. This is the main entry point.
331
347
  * Initializes the UI, shows warnings, processes initial messages, and starts the interactive loop.
@@ -431,6 +447,12 @@ export class InteractiveMode {
431
447
  }
432
448
  return undefined;
433
449
  }
450
+ getMarkdownThemeWithSettings() {
451
+ return {
452
+ ...getMarkdownTheme(),
453
+ codeBlockIndent: this.settingsManager.getCodeBlockIndent(),
454
+ };
455
+ }
434
456
  // =========================================================================
435
457
  // Extension System
436
458
  // =========================================================================
@@ -505,6 +527,7 @@ export class InteractiveMode {
505
527
  },
506
528
  setSessionName: (name) => {
507
529
  this.sessionManager.appendSessionInfo(name);
530
+ this.updateTerminalTitle();
508
531
  },
509
532
  getSessionName: () => {
510
533
  return this.sessionManager.getSessionName();
@@ -697,15 +720,22 @@ export class InteractiveMode {
697
720
  /**
698
721
  * Set an extension widget (string array or custom component).
699
722
  */
700
- setExtensionWidget(key, content) {
701
- // Dispose and remove existing widget
702
- const existing = this.extensionWidgets.get(key);
703
- if (existing?.dispose)
704
- existing.dispose();
723
+ setExtensionWidget(key, content, options) {
724
+ const placement = options?.placement ?? "aboveEditor";
725
+ const removeExisting = (map) => {
726
+ const existing = map.get(key);
727
+ if (existing?.dispose)
728
+ existing.dispose();
729
+ map.delete(key);
730
+ };
731
+ removeExisting(this.extensionWidgetsAbove);
732
+ removeExisting(this.extensionWidgetsBelow);
705
733
  if (content === undefined) {
706
- this.extensionWidgets.delete(key);
734
+ this.renderWidgets();
735
+ return;
707
736
  }
708
- else if (Array.isArray(content)) {
737
+ let component;
738
+ if (Array.isArray(content)) {
709
739
  // Wrap string array in a Container with Text components
710
740
  const container = new Container();
711
741
  for (const line of content.slice(0, InteractiveMode.MAX_WIDGET_LINES)) {
@@ -714,13 +744,14 @@ export class InteractiveMode {
714
744
  if (content.length > InteractiveMode.MAX_WIDGET_LINES) {
715
745
  container.addChild(new Text(theme.fg("muted", "... (widget truncated)"), 1, 0));
716
746
  }
717
- this.extensionWidgets.set(key, container);
747
+ component = container;
718
748
  }
719
749
  else {
720
750
  // Factory function - create component
721
- const component = content(this.ui, theme);
722
- this.extensionWidgets.set(key, component);
751
+ component = content(this.ui, theme);
723
752
  }
753
+ const targetMap = placement === "belowEditor" ? this.extensionWidgetsBelow : this.extensionWidgetsAbove;
754
+ targetMap.set(key, component);
724
755
  this.renderWidgets();
725
756
  }
726
757
  // Maximum total widget lines to prevent viewport overflow
@@ -729,19 +760,26 @@ export class InteractiveMode {
729
760
  * Render all extension widgets to the widget container.
730
761
  */
731
762
  renderWidgets() {
732
- if (!this.widgetContainer)
763
+ if (!this.widgetContainerAbove || !this.widgetContainerBelow)
733
764
  return;
734
- this.widgetContainer.clear();
735
- if (this.extensionWidgets.size === 0) {
736
- this.widgetContainer.addChild(new Spacer(1));
737
- this.ui.requestRender();
765
+ this.renderWidgetContainer(this.widgetContainerAbove, this.extensionWidgetsAbove, true, true);
766
+ this.renderWidgetContainer(this.widgetContainerBelow, this.extensionWidgetsBelow, false, false);
767
+ this.ui.requestRender();
768
+ }
769
+ renderWidgetContainer(container, widgets, spacerWhenEmpty, leadingSpacer) {
770
+ container.clear();
771
+ if (widgets.size === 0) {
772
+ if (spacerWhenEmpty) {
773
+ container.addChild(new Spacer(1));
774
+ }
738
775
  return;
739
776
  }
740
- this.widgetContainer.addChild(new Spacer(1));
741
- for (const [_key, component] of this.extensionWidgets) {
742
- this.widgetContainer.addChild(component);
777
+ if (leadingSpacer) {
778
+ container.addChild(new Spacer(1));
779
+ }
780
+ for (const component of widgets.values()) {
781
+ container.addChild(component);
743
782
  }
744
- this.ui.requestRender();
745
783
  }
746
784
  /**
747
785
  * Set a custom footer component, or restore the built-in footer.
@@ -821,7 +859,7 @@ export class InteractiveMode {
821
859
  }
822
860
  }
823
861
  },
824
- setWidget: (key, content) => this.setExtensionWidget(key, content),
862
+ setWidget: (key, content, options) => this.setExtensionWidget(key, content, options),
825
863
  setFooter: (factory) => this.setExtensionFooter(factory),
826
864
  setHeader: (factory) => this.setExtensionHeader(factory),
827
865
  setTitle: (title) => this.ui.terminal.setTitle(title),
@@ -1394,7 +1432,7 @@ export class InteractiveMode {
1394
1432
  this.ui.requestRender();
1395
1433
  }
1396
1434
  else if (event.message.role === "assistant") {
1397
- this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock);
1435
+ this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings());
1398
1436
  this.streamingMessage = event.message;
1399
1437
  this.chatContainer.addChild(this.streamingComponent);
1400
1438
  this.streamingComponent.updateContent(this.streamingMessage);
@@ -1644,20 +1682,20 @@ export class InteractiveMode {
1644
1682
  case "custom": {
1645
1683
  if (message.display) {
1646
1684
  const renderer = this.session.extensionRunner?.getMessageRenderer(message.customType);
1647
- this.chatContainer.addChild(new CustomMessageComponent(message, renderer));
1685
+ this.chatContainer.addChild(new CustomMessageComponent(message, renderer, this.getMarkdownThemeWithSettings()));
1648
1686
  }
1649
1687
  break;
1650
1688
  }
1651
1689
  case "compactionSummary": {
1652
1690
  this.chatContainer.addChild(new Spacer(1));
1653
- const component = new CompactionSummaryMessageComponent(message);
1691
+ const component = new CompactionSummaryMessageComponent(message, this.getMarkdownThemeWithSettings());
1654
1692
  component.setExpanded(this.toolOutputExpanded);
1655
1693
  this.chatContainer.addChild(component);
1656
1694
  break;
1657
1695
  }
1658
1696
  case "branchSummary": {
1659
1697
  this.chatContainer.addChild(new Spacer(1));
1660
- const component = new BranchSummaryMessageComponent(message);
1698
+ const component = new BranchSummaryMessageComponent(message, this.getMarkdownThemeWithSettings());
1661
1699
  component.setExpanded(this.toolOutputExpanded);
1662
1700
  this.chatContainer.addChild(component);
1663
1701
  break;
@@ -1665,7 +1703,7 @@ export class InteractiveMode {
1665
1703
  case "user": {
1666
1704
  const textContent = this.getUserMessageText(message);
1667
1705
  if (textContent) {
1668
- const userComponent = new UserMessageComponent(textContent);
1706
+ const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings());
1669
1707
  this.chatContainer.addChild(userComponent);
1670
1708
  if (options?.populateHistory) {
1671
1709
  this.editor.addToHistory?.(textContent);
@@ -1674,7 +1712,7 @@ export class InteractiveMode {
1674
1712
  break;
1675
1713
  }
1676
1714
  case "assistant": {
1677
- const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock);
1715
+ const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock, this.getMarkdownThemeWithSettings());
1678
1716
  this.chatContainer.addChild(assistantComponent);
1679
1717
  break;
1680
1718
  }
@@ -2179,6 +2217,7 @@ export class InteractiveMode {
2179
2217
  doubleEscapeAction: this.settingsManager.getDoubleEscapeAction(),
2180
2218
  showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
2181
2219
  editorPaddingX: this.settingsManager.getEditorPaddingX(),
2220
+ quietStartup: this.settingsManager.getQuietStartup(),
2182
2221
  }, {
2183
2222
  onAutoCompactChange: (enabled) => {
2184
2223
  this.session.setAutoCompactionEnabled(enabled);
@@ -2242,6 +2281,9 @@ export class InteractiveMode {
2242
2281
  onCollapseChangelogChange: (collapsed) => {
2243
2282
  this.settingsManager.setCollapseChangelog(collapsed);
2244
2283
  },
2284
+ onQuietStartupChange: (enabled) => {
2285
+ this.settingsManager.setQuietStartup(enabled);
2286
+ },
2245
2287
  onDoubleEscapeActionChange: (action) => {
2246
2288
  this.settingsManager.setDoubleEscapeAction(action);
2247
2289
  },
@@ -2817,7 +2859,7 @@ export class InteractiveMode {
2817
2859
  return;
2818
2860
  }
2819
2861
  // Create the preview URL
2820
- const previewUrl = `https://buildwithpi.ai/session/#${gistId}`;
2862
+ const previewUrl = getShareViewerUrl(gistId);
2821
2863
  this.showStatus(`Share URL: ${previewUrl}\nGist: ${gistUrl}`);
2822
2864
  }
2823
2865
  catch (error) {
@@ -2856,6 +2898,7 @@ export class InteractiveMode {
2856
2898
  return;
2857
2899
  }
2858
2900
  this.sessionManager.appendSessionInfo(name);
2901
+ this.updateTerminalTitle();
2859
2902
  this.chatContainer.addChild(new Spacer(1));
2860
2903
  this.chatContainer.addChild(new Text(theme.fg("dim", `Session name set: ${name}`), 1, 0));
2861
2904
  this.ui.requestRender();
@@ -2906,7 +2949,7 @@ export class InteractiveMode {
2906
2949
  this.chatContainer.addChild(new DynamicBorder());
2907
2950
  this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
2908
2951
  this.chatContainer.addChild(new Spacer(1));
2909
- this.chatContainer.addChild(new Markdown(changelogMarkdown, 1, 1, getMarkdownTheme()));
2952
+ this.chatContainer.addChild(new Markdown(changelogMarkdown, 1, 1, this.getMarkdownThemeWithSettings()));
2910
2953
  this.chatContainer.addChild(new DynamicBorder());
2911
2954
  this.ui.requestRender();
2912
2955
  }
@@ -3025,7 +3068,7 @@ export class InteractiveMode {
3025
3068
  this.chatContainer.addChild(new DynamicBorder());
3026
3069
  this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "Keyboard Shortcuts")), 1, 0));
3027
3070
  this.chatContainer.addChild(new Spacer(1));
3028
- this.chatContainer.addChild(new Markdown(hotkeys.trim(), 1, 1, getMarkdownTheme()));
3071
+ this.chatContainer.addChild(new Markdown(hotkeys.trim(), 1, 1, this.getMarkdownThemeWithSettings()));
3029
3072
  this.chatContainer.addChild(new DynamicBorder());
3030
3073
  this.ui.requestRender();
3031
3074
  }