@mariozechner/pi-coding-agent 0.37.2 → 0.37.4

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 (64) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +13 -1
  3. package/dist/core/agent-session.d.ts +11 -1
  4. package/dist/core/agent-session.d.ts.map +1 -1
  5. package/dist/core/agent-session.js +39 -0
  6. package/dist/core/agent-session.js.map +1 -1
  7. package/dist/core/auth-storage.d.ts.map +1 -1
  8. package/dist/core/auth-storage.js +4 -6
  9. package/dist/core/auth-storage.js.map +1 -1
  10. package/dist/core/extensions/index.d.ts +1 -1
  11. package/dist/core/extensions/index.d.ts.map +1 -1
  12. package/dist/core/extensions/index.js.map +1 -1
  13. package/dist/core/extensions/loader.d.ts.map +1 -1
  14. package/dist/core/extensions/loader.js +15 -3
  15. package/dist/core/extensions/loader.js.map +1 -1
  16. package/dist/core/extensions/runner.d.ts +2 -1
  17. package/dist/core/extensions/runner.d.ts.map +1 -1
  18. package/dist/core/extensions/runner.js +3 -0
  19. package/dist/core/extensions/runner.js.map +1 -1
  20. package/dist/core/extensions/types.d.ts +19 -0
  21. package/dist/core/extensions/types.d.ts.map +1 -1
  22. package/dist/core/extensions/types.js.map +1 -1
  23. package/dist/core/sdk.d.ts.map +1 -1
  24. package/dist/core/sdk.js +36 -1
  25. package/dist/core/sdk.js.map +1 -1
  26. package/dist/core/settings-manager.d.ts +3 -0
  27. package/dist/core/settings-manager.d.ts.map +1 -1
  28. package/dist/core/settings-manager.js +24 -14
  29. package/dist/core/settings-manager.js.map +1 -1
  30. package/dist/main.d.ts.map +1 -1
  31. package/dist/main.js +20 -1
  32. package/dist/main.js.map +1 -1
  33. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  34. package/dist/modes/interactive/components/footer.js +31 -7
  35. package/dist/modes/interactive/components/footer.js.map +1 -1
  36. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  37. package/dist/modes/interactive/components/session-selector.js +1 -1
  38. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  39. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  40. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  41. package/dist/modes/interactive/components/settings-selector.js +12 -0
  42. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  43. package/dist/modes/interactive/interactive-mode.d.ts +11 -0
  44. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  45. package/dist/modes/interactive/interactive-mode.js +88 -12
  46. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  47. package/dist/modes/print-mode.d.ts.map +1 -1
  48. package/dist/modes/print-mode.js +5 -0
  49. package/dist/modes/print-mode.js.map +1 -1
  50. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  51. package/dist/modes/rpc/rpc-mode.js +11 -0
  52. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  53. package/dist/utils/clipboard-image.d.ts +11 -0
  54. package/dist/utils/clipboard-image.d.ts.map +1 -0
  55. package/dist/utils/clipboard-image.js +117 -0
  56. package/dist/utils/clipboard-image.js.map +1 -0
  57. package/docs/extensions.md +37 -2
  58. package/examples/extensions/README.md +1 -0
  59. package/examples/extensions/custom-footer.ts +86 -0
  60. package/examples/extensions/custom-header.ts +72 -0
  61. package/examples/extensions/send-user-message.ts +97 -0
  62. package/examples/extensions/with-deps/package-lock.json +2 -2
  63. package/examples/extensions/with-deps/package.json +1 -1
  64. package/package.json +5 -4
@@ -6,7 +6,6 @@ import * as crypto from "node:crypto";
6
6
  import * as fs from "node:fs";
7
7
  import * as os from "node:os";
8
8
  import * as path from "node:path";
9
- import Clipboard from "@crosscopy/clipboard";
10
9
  import { getOAuthProviders } from "@mariozechner/pi-ai";
11
10
  import { CombinedAutocompleteProvider, Container, getEditorKeybindings, Loader, Markdown, matchesKey, ProcessTerminal, Spacer, Text, TruncatedText, TUI, visibleWidth, } from "@mariozechner/pi-tui";
12
11
  import { spawn, spawnSync } from "child_process";
@@ -18,6 +17,7 @@ import { loadSkills } from "../../core/skills.js";
18
17
  import { loadProjectContextFiles } from "../../core/system-prompt.js";
19
18
  import { getChangelogPath, parseChangelog } from "../../utils/changelog.js";
20
19
  import { copyToClipboard } from "../../utils/clipboard.js";
20
+ import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
21
21
  import { ArminComponent } from "./components/armin.js";
22
22
  import { AssistantMessageComponent } from "./components/assistant-message.js";
23
23
  import { BashExecutionComponent } from "./components/bash-execution.js";
@@ -97,6 +97,12 @@ export class InteractiveMode {
97
97
  // Extension widgets (components rendered above the editor)
98
98
  extensionWidgets = new Map();
99
99
  widgetContainer;
100
+ // Custom footer from extension (undefined = use built-in footer)
101
+ customFooter = undefined;
102
+ // Built-in header (logo + keybinding hints + changelog)
103
+ builtInHeader = undefined;
104
+ // Custom header from extension (undefined = use built-in header)
105
+ customHeader = undefined;
100
106
  // Convenience accessors
101
107
  get agent() {
102
108
  return this.session.agent;
@@ -197,7 +203,7 @@ export class InteractiveMode {
197
203
  theme.fg("muted", " to suspend") +
198
204
  "\n" +
199
205
  theme.fg("dim", deleteToLineEnd) +
200
- theme.fg("muted", " to delete line") +
206
+ theme.fg("muted", " to delete to end") +
201
207
  "\n" +
202
208
  theme.fg("dim", cycleThinkingLevel) +
203
209
  theme.fg("muted", " to cycle thinking") +
@@ -223,6 +229,9 @@ export class InteractiveMode {
223
229
  theme.fg("dim", "!") +
224
230
  theme.fg("muted", " to run bash") +
225
231
  "\n" +
232
+ theme.fg("dim", "!!") +
233
+ theme.fg("muted", " to run bash (no context)") +
234
+ "\n" +
226
235
  theme.fg("dim", followUp) +
227
236
  theme.fg("muted", " to queue follow-up") +
228
237
  "\n" +
@@ -231,10 +240,10 @@ export class InteractiveMode {
231
240
  "\n" +
232
241
  theme.fg("dim", "drop files") +
233
242
  theme.fg("muted", " to attach");
234
- const header = new Text(`${logo}\n${instructions}`, 1, 0);
243
+ this.builtInHeader = new Text(`${logo}\n${instructions}`, 1, 0);
235
244
  // Setup UI layout
236
245
  this.ui.addChild(new Spacer(1));
237
- this.ui.addChild(header);
246
+ this.ui.addChild(this.builtInHeader);
238
247
  this.ui.addChild(new Spacer(1));
239
248
  // Add changelog if provided
240
249
  if (this.changelogMarkdown) {
@@ -340,6 +349,11 @@ export class InteractiveMode {
340
349
  this.showError(`Extension sendMessage failed: ${err instanceof Error ? err.message : String(err)}`);
341
350
  });
342
351
  },
352
+ sendUserMessageHandler: (content, options) => {
353
+ this.session.sendUserMessage(content, options).catch((err) => {
354
+ this.showError(`Extension sendUserMessage failed: ${err instanceof Error ? err.message : String(err)}`);
355
+ });
356
+ },
343
357
  appendEntryHandler: (customType, data) => {
344
358
  this.sessionManager.appendCustomEntry(customType, data);
345
359
  },
@@ -523,6 +537,64 @@ export class InteractiveMode {
523
537
  }
524
538
  this.ui.requestRender();
525
539
  }
540
+ /**
541
+ * Set a custom footer component, or restore the built-in footer.
542
+ */
543
+ setExtensionFooter(factory) {
544
+ // Dispose existing custom footer
545
+ if (this.customFooter?.dispose) {
546
+ this.customFooter.dispose();
547
+ }
548
+ // Remove current footer from UI
549
+ if (this.customFooter) {
550
+ this.ui.removeChild(this.customFooter);
551
+ }
552
+ else {
553
+ this.ui.removeChild(this.footer);
554
+ }
555
+ if (factory) {
556
+ // Create and add custom footer
557
+ this.customFooter = factory(this.ui, theme);
558
+ this.ui.addChild(this.customFooter);
559
+ }
560
+ else {
561
+ // Restore built-in footer
562
+ this.customFooter = undefined;
563
+ this.ui.addChild(this.footer);
564
+ }
565
+ this.ui.requestRender();
566
+ }
567
+ /**
568
+ * Set a custom header component, or restore the built-in header.
569
+ */
570
+ setExtensionHeader(factory) {
571
+ // Header may not be initialized yet if called during early initialization
572
+ if (!this.builtInHeader) {
573
+ return;
574
+ }
575
+ // Dispose existing custom header
576
+ if (this.customHeader?.dispose) {
577
+ this.customHeader.dispose();
578
+ }
579
+ // Remove current header from UI
580
+ if (this.customHeader) {
581
+ this.ui.removeChild(this.customHeader);
582
+ }
583
+ else {
584
+ this.ui.removeChild(this.builtInHeader);
585
+ }
586
+ if (factory) {
587
+ // Create and add custom header at position 1 (after initial spacer)
588
+ this.customHeader = factory(this.ui, theme);
589
+ this.ui.children.splice(1, 0, this.customHeader);
590
+ }
591
+ else {
592
+ // Restore built-in header at position 1
593
+ this.customHeader = undefined;
594
+ this.ui.children.splice(1, 0, this.builtInHeader);
595
+ }
596
+ this.ui.requestRender();
597
+ }
526
598
  /**
527
599
  * Create the ExtensionUIContext for extensions.
528
600
  */
@@ -534,6 +606,8 @@ export class InteractiveMode {
534
606
  notify: (message, type) => this.showExtensionNotify(message, type),
535
607
  setStatus: (key, text) => this.setExtensionStatus(key, text),
536
608
  setWidget: (key, content) => this.setExtensionWidget(key, content),
609
+ setFooter: (factory) => this.setExtensionFooter(factory),
610
+ setHeader: (factory) => this.setExtensionHeader(factory),
537
611
  setTitle: (title) => this.ui.terminal.setTitle(title),
538
612
  custom: (factory) => this.showExtensionCustom(factory),
539
613
  setEditorText: (text) => this.editor.setText(text),
@@ -763,18 +837,16 @@ export class InteractiveMode {
763
837
  }
764
838
  async handleClipboardImagePaste() {
765
839
  try {
766
- if (!Clipboard.hasImage()) {
767
- return;
768
- }
769
- const imageData = await Clipboard.getImageBinary();
770
- if (!imageData || imageData.length === 0) {
840
+ const image = await readClipboardImage();
841
+ if (!image) {
771
842
  return;
772
843
  }
773
844
  // Write to temp file
774
845
  const tmpDir = os.tmpdir();
775
- const fileName = `pi-clipboard-${crypto.randomUUID()}.png`;
846
+ const ext = extensionForImageMimeType(image.mimeType) ?? "png";
847
+ const fileName = `pi-clipboard-${crypto.randomUUID()}.${ext}`;
776
848
  const filePath = path.join(tmpDir, fileName);
777
- fs.writeFileSync(filePath, Buffer.from(imageData));
849
+ fs.writeFileSync(filePath, Buffer.from(image.bytes));
778
850
  // Insert file path directly
779
851
  this.editor.insertTextAtCursor(filePath);
780
852
  this.ui.requestRender();
@@ -955,7 +1027,6 @@ export class InteractiveMode {
955
1027
  }
956
1028
  else if (event.message.role === "user") {
957
1029
  this.addMessageToChat(event.message);
958
- this.editor.setText("");
959
1030
  this.updatePendingMessagesDisplay();
960
1031
  this.ui.requestRender();
961
1032
  }
@@ -1657,6 +1728,7 @@ export class InteractiveMode {
1657
1728
  autoCompact: this.session.autoCompactionEnabled,
1658
1729
  showImages: this.settingsManager.getShowImages(),
1659
1730
  autoResizeImages: this.settingsManager.getImageAutoResize(),
1731
+ blockImages: this.settingsManager.getBlockImages(),
1660
1732
  steeringMode: this.session.steeringMode,
1661
1733
  followUpMode: this.session.followUpMode,
1662
1734
  thinkingLevel: this.session.thinkingLevel,
@@ -1682,6 +1754,9 @@ export class InteractiveMode {
1682
1754
  onAutoResizeImagesChange: (enabled) => {
1683
1755
  this.settingsManager.setImageAutoResize(enabled);
1684
1756
  },
1757
+ onBlockImagesChange: (blocked) => {
1758
+ this.settingsManager.setBlockImages(blocked);
1759
+ },
1685
1760
  onSteeringModeChange: (mode) => {
1686
1761
  this.session.setSteeringMode(mode);
1687
1762
  },
@@ -2254,6 +2329,7 @@ export class InteractiveMode {
2254
2329
  | \`Ctrl+V\` | Paste image from clipboard |
2255
2330
  | \`/\` | Slash commands |
2256
2331
  | \`!\` | Run bash command |
2332
+ | \`!!\` | Run bash command (excluded from context) |
2257
2333
  `;
2258
2334
  // Add extension-registered shortcuts
2259
2335
  const extensionRunner = this.session.extensionRunner;