@earendil-works/pi-coding-agent 0.81.0 → 0.82.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 (113) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +13 -0
  3. package/dist/cli/config-selector.d.ts.map +1 -1
  4. package/dist/cli/config-selector.js +1 -1
  5. package/dist/cli/config-selector.js.map +1 -1
  6. package/dist/cli/startup-ui.d.ts.map +1 -1
  7. package/dist/cli/startup-ui.js +1 -1
  8. package/dist/cli/startup-ui.js.map +1 -1
  9. package/dist/core/agent-session.d.ts +33 -0
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +37 -3
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/compaction/branch-summarization.d.ts +5 -0
  14. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  15. package/dist/core/compaction/branch-summarization.js +5 -7
  16. package/dist/core/compaction/branch-summarization.js.map +1 -1
  17. package/dist/core/compaction/compaction.d.ts +13 -4
  18. package/dist/core/compaction/compaction.d.ts.map +1 -1
  19. package/dist/core/compaction/compaction.js +29 -17
  20. package/dist/core/compaction/compaction.js.map +1 -1
  21. package/dist/core/extensions/runner.d.ts.map +1 -1
  22. package/dist/core/extensions/runner.js +4 -0
  23. package/dist/core/extensions/runner.js.map +1 -1
  24. package/dist/core/extensions/types.d.ts +5 -1
  25. package/dist/core/extensions/types.d.ts.map +1 -1
  26. package/dist/core/extensions/types.js.map +1 -1
  27. package/dist/core/model-config.d.ts +30 -0
  28. package/dist/core/model-config.d.ts.map +1 -1
  29. package/dist/core/model-config.js +6 -0
  30. package/dist/core/model-config.js.map +1 -1
  31. package/dist/core/model-registry.d.ts.map +1 -1
  32. package/dist/core/model-registry.js +2 -2
  33. package/dist/core/model-registry.js.map +1 -1
  34. package/dist/core/model-resolver.d.ts.map +1 -1
  35. package/dist/core/model-resolver.js +7 -0
  36. package/dist/core/model-resolver.js.map +1 -1
  37. package/dist/core/model-runtime.d.ts +0 -1
  38. package/dist/core/model-runtime.d.ts.map +1 -1
  39. package/dist/core/model-runtime.js +3 -5
  40. package/dist/core/model-runtime.js.map +1 -1
  41. package/dist/core/remote-catalog-provider.d.ts +1 -1
  42. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  43. package/dist/core/remote-catalog-provider.js +5 -10
  44. package/dist/core/remote-catalog-provider.js.map +1 -1
  45. package/dist/core/sdk.d.ts.map +1 -1
  46. package/dist/core/sdk.js +7 -3
  47. package/dist/core/sdk.js.map +1 -1
  48. package/dist/core/settings-manager.d.ts +1 -1
  49. package/dist/core/settings-manager.d.ts.map +1 -1
  50. package/dist/core/settings-manager.js.map +1 -1
  51. package/dist/core/system-prompt.d.ts.map +1 -1
  52. package/dist/core/system-prompt.js +1 -1
  53. package/dist/core/system-prompt.js.map +1 -1
  54. package/dist/core/tools/bash.d.ts +2 -0
  55. package/dist/core/tools/bash.d.ts.map +1 -1
  56. package/dist/core/tools/bash.js +34 -5
  57. package/dist/core/tools/bash.js.map +1 -1
  58. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  59. package/dist/core/tools/tool-definition-wrapper.js +3 -1
  60. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  61. package/dist/extensions/llama/provider.d.ts.map +1 -1
  62. package/dist/extensions/llama/provider.js +1 -2
  63. package/dist/extensions/llama/provider.js.map +1 -1
  64. package/dist/main.d.ts.map +1 -1
  65. package/dist/main.js +2 -1
  66. package/dist/main.js.map +1 -1
  67. package/dist/modes/interactive/components/extension-editor.d.ts +1 -2
  68. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  69. package/dist/modes/interactive/components/extension-editor.js +16 -46
  70. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  71. package/dist/modes/interactive/external-editor.d.ts +12 -0
  72. package/dist/modes/interactive/external-editor.d.ts.map +1 -0
  73. package/dist/modes/interactive/external-editor.js +37 -0
  74. package/dist/modes/interactive/external-editor.js.map +1 -0
  75. package/dist/modes/interactive/interactive-mode.d.ts +2 -1
  76. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  77. package/dist/modes/interactive/interactive-mode.js +58 -46
  78. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  79. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  80. package/dist/modes/rpc/rpc-mode.js +1 -0
  81. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  82. package/dist/utils/clipboard.d.ts.map +1 -1
  83. package/dist/utils/clipboard.js +19 -8
  84. package/dist/utils/clipboard.js.map +1 -1
  85. package/dist/utils/version-check.d.ts.map +1 -1
  86. package/dist/utils/version-check.js +3 -1
  87. package/dist/utils/version-check.js.map +1 -1
  88. package/docs/compaction.md +1 -1
  89. package/docs/custom-provider.md +5 -2
  90. package/docs/environment-variables.md +88 -0
  91. package/docs/extensions.md +11 -3
  92. package/docs/index.md +1 -0
  93. package/docs/json.md +5 -1
  94. package/docs/models.md +6 -2
  95. package/docs/providers.md +8 -1
  96. package/docs/rpc.md +56 -6
  97. package/docs/sdk.md +3 -0
  98. package/docs/settings.md +1 -1
  99. package/docs/usage.md +0 -13
  100. package/examples/extensions/custom-compaction.ts +3 -0
  101. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  102. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  103. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  104. package/examples/extensions/gondolin/package-lock.json +2 -2
  105. package/examples/extensions/gondolin/package.json +1 -1
  106. package/examples/extensions/handoff.ts +9 -1
  107. package/examples/extensions/sandbox/package-lock.json +2 -2
  108. package/examples/extensions/sandbox/package.json +1 -1
  109. package/examples/extensions/summarize.ts +3 -0
  110. package/examples/extensions/with-deps/package-lock.json +2 -2
  111. package/examples/extensions/with-deps/package.json +1 -1
  112. package/npm-shrinkwrap.json +16 -15
  113. package/package.json +5 -4
@@ -64,6 +64,7 @@ import { TreeSelectorComponent } from "./components/tree-selector.js";
64
64
  import { TrustSelectorComponent } from "./components/trust-selector.js";
65
65
  import { UserMessageComponent } from "./components/user-message.js";
66
66
  import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
67
+ import { editInExternalEditor } from "./external-editor.js";
67
68
  import { getModelSearchText } from "./model-search.js";
68
69
  import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
69
70
  import { InteractiveThemeController } from "./theme/theme-controller.js";
@@ -272,7 +273,7 @@ export class InteractiveMode {
272
273
  await this.rebindCurrentSession({ renderBeforeBind: true });
273
274
  });
274
275
  this.version = VERSION;
275
- this.ui = new TUI(new ProcessTerminal(), this.settingsManager.getShowHardwareCursor());
276
+ this.ui = new TUI(new ProcessTerminal(), this.settingsManager.getShowHardwareCursor(), getAgentDir());
276
277
  this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
277
278
  this.headerContainer = new Container();
278
279
  this.loadedResourcesContainer = new Container();
@@ -580,6 +581,12 @@ export class InteractiveMode {
580
581
  */
581
582
  async run() {
582
583
  await this.init();
584
+ if (!process.env.PI_OFFLINE) {
585
+ void this.session.modelRuntime
586
+ .refresh()
587
+ .then(() => this.updateAvailableProviderCount())
588
+ .catch(() => { });
589
+ }
583
590
  // Start version check asynchronously
584
591
  checkForNewPiVersion(this.version).then((newRelease) => {
585
592
  if (newRelease) {
@@ -791,8 +798,15 @@ export class InteractiveMode {
791
798
  * Get a short path relative to the package root for display.
792
799
  */
793
800
  getShortPath(fullPath, sourceInfo) {
801
+ const normalizedFullPath = fullPath.replace(/\\/g, "/");
794
802
  const baseDir = sourceInfo?.baseDir;
795
803
  if (baseDir && this.isPackageSource(sourceInfo)) {
804
+ const normalizedBaseDir = baseDir.replace(/\\/g, "/");
805
+ const npmRootMatch = normalizedBaseDir.match(/^(.*\/node_modules)\/(@?[^/]+(?:\/[^/]+)?)$/);
806
+ // If fullPath is under the same node_modules root as baseDir, preserve that relative topology.
807
+ if (npmRootMatch?.[1] && normalizedFullPath.startsWith(`${npmRootMatch[1]}/`)) {
808
+ return path.posix.relative(normalizedBaseDir, normalizedFullPath);
809
+ }
796
810
  const relativePath = path.relative(path.resolve(baseDir), path.resolve(fullPath));
797
811
  if (relativePath &&
798
812
  relativePath !== "." &&
@@ -803,11 +817,11 @@ export class InteractiveMode {
803
817
  }
804
818
  }
805
819
  const source = sourceInfo?.source ?? "";
806
- const npmMatch = fullPath.match(/node_modules\/(@?[^/]+(?:\/[^/]+)?)\/(.*)/);
820
+ const npmMatch = normalizedFullPath.match(/node_modules\/(@?[^/]+(?:\/[^/]+)?)\/(.*)/);
807
821
  if (npmMatch && source.startsWith("npm:")) {
808
822
  return npmMatch[2];
809
823
  }
810
- const gitMatch = fullPath.match(/git\/[^/]+\/[^/]+\/(.*)/);
824
+ const gitMatch = normalizedFullPath.match(/git\/[^/]+\/[^/]+\/(.*)/);
811
825
  if (gitMatch && source.startsWith("git:")) {
812
826
  return gitMatch[1];
813
827
  }
@@ -1347,6 +1361,7 @@ export class InteractiveMode {
1347
1361
  sessionManager: this.sessionManager,
1348
1362
  modelRegistry: extensionRunner.getModelRegistry(),
1349
1363
  model: this.session.model,
1364
+ thinkingLevel: this.session.thinkingLevel,
1350
1365
  isIdle: () => this.session.isIdle,
1351
1366
  isProjectTrusted: () => this.settingsManager.isProjectTrusted(),
1352
1367
  signal: this.session.agent.signal,
@@ -2032,7 +2047,7 @@ export class InteractiveMode {
2032
2047
  this.defaultEditor.onAction("app.model.select", () => this.showModelSelector());
2033
2048
  this.defaultEditor.onAction("app.tools.expand", () => this.toggleToolOutputExpansion());
2034
2049
  this.defaultEditor.onAction("app.thinking.toggle", () => this.toggleThinkingBlockVisibility());
2035
- this.defaultEditor.onAction("app.editor.external", () => this.openExternalEditor());
2050
+ this.defaultEditor.onAction("app.editor.external", () => void this.handleOpenExternalEditor());
2036
2051
  this.defaultEditor.onAction("app.message.copy", () => void this.handleCopyCommand());
2037
2052
  this.defaultEditor.onAction("app.message.followUp", () => this.handleFollowUp());
2038
2053
  this.defaultEditor.onAction("app.message.dequeue", () => this.handleDequeue());
@@ -2394,6 +2409,9 @@ export class InteractiveMode {
2394
2409
  }
2395
2410
  this.ui.requestRender();
2396
2411
  break;
2412
+ case "bash_execution_update":
2413
+ // The bash execution callback handles TUI output rendering.
2414
+ break;
2397
2415
  case "tool_execution_start": {
2398
2416
  let component = this.pendingTools.get(event.toolCallId);
2399
2417
  if (!component) {
@@ -2515,6 +2533,28 @@ export class InteractiveMode {
2515
2533
  this.ui.requestRender();
2516
2534
  break;
2517
2535
  }
2536
+ case "summarization_retry_scheduled": {
2537
+ this.showError(event.errorMessage);
2538
+ this.showStatusIndicator(new RetryStatusIndicator(this.ui, event.attempt, event.maxAttempts, event.delayMs));
2539
+ this.ui.requestRender();
2540
+ break;
2541
+ }
2542
+ case "summarization_retry_attempt_start": {
2543
+ this.clearStatusIndicator("retry");
2544
+ if (event.source === "branchSummary") {
2545
+ this.showStatusIndicator(new BranchSummaryStatusIndicator(this.ui));
2546
+ }
2547
+ else {
2548
+ this.showStatusIndicator(new CompactionStatusIndicator(this.ui, event.reason));
2549
+ }
2550
+ this.ui.requestRender();
2551
+ break;
2552
+ }
2553
+ case "summarization_retry_finished": {
2554
+ this.clearStatusIndicator("retry");
2555
+ this.ui.requestRender();
2556
+ break;
2557
+ }
2518
2558
  }
2519
2559
  }
2520
2560
  /** Extract text content from a user message */
@@ -3094,51 +3134,21 @@ export class InteractiveMode {
3094
3134
  }
3095
3135
  this.showStatus(`Thinking blocks: ${this.hideThinkingBlock ? "hidden" : "visible"}`);
3096
3136
  }
3097
- async openExternalEditor() {
3137
+ async handleOpenExternalEditor() {
3098
3138
  const editorCmd = this.settingsManager.getExternalEditorCommand();
3099
- if (!editorCmd) {
3100
- this.showWarning("No editor configured. Set externalEditor in settings.json or $VISUAL/$EDITOR.");
3101
- return;
3102
- }
3103
- const currentText = this.editor.getExpandedText?.() ?? this.editor.getText();
3104
- const tmpFile = path.join(os.tmpdir(), `pi-editor-${Date.now()}.pi.md`);
3139
+ const content = this.editor.getExpandedText?.() ?? this.editor.getText();
3140
+ this.ui.stop();
3105
3141
  try {
3106
- // Write current content to temp file
3107
- fs.writeFileSync(tmpFile, currentText, "utf-8");
3108
- // Stop TUI to release terminal
3109
- this.ui.stop();
3110
- // Split by space to support editor arguments (e.g., "code --wait")
3111
- const [editor, ...editorArgs] = editorCmd.split(" ");
3112
- process.stdout.write(`Launching external editor: ${editorCmd}\nPi will resume when the editor exits.\n`);
3113
- // Do not use spawnSync here. On Windows, synchronous child_process calls can keep
3114
- // Node/libuv's console input read active after ui.stop() pauses stdin, racing
3115
- // vim/nvim for the console input buffer until Ctrl+C cancels the pending read.
3116
- const status = await new Promise((resolve) => {
3117
- const child = spawn(editor, [...editorArgs, tmpFile], {
3118
- stdio: "inherit",
3119
- shell: process.platform === "win32",
3120
- });
3121
- child.on("error", () => resolve(null));
3122
- child.on("close", (code) => resolve(code));
3142
+ const result = await editInExternalEditor({
3143
+ command: editorCmd,
3144
+ content,
3123
3145
  });
3124
- // On successful exit (status 0), replace editor content
3125
- if (status === 0) {
3126
- const newContent = fs.readFileSync(tmpFile, "utf-8").replace(/\n$/, "");
3127
- this.editor.setText(newContent);
3146
+ if (result.status === "complete") {
3147
+ this.editor.setText(result.content);
3128
3148
  }
3129
- // On non-zero exit, keep original text (no action needed)
3130
3149
  }
3131
3150
  finally {
3132
- // Clean up temp file
3133
- try {
3134
- fs.unlinkSync(tmpFile);
3135
- }
3136
- catch {
3137
- // Ignore cleanup errors
3138
- }
3139
- // Restart TUI
3140
3151
  this.ui.start();
3141
- // Force full re-render since external editor uses alternate screen
3142
3152
  this.ui.requestRender(true);
3143
3153
  }
3144
3154
  }
@@ -3593,10 +3603,12 @@ export class InteractiveMode {
3593
3603
  return [];
3594
3604
  }
3595
3605
  }
3596
- /** Update the footer's available provider count from current model candidates */
3597
- async updateAvailableProviderCount() {
3598
- const models = await this.getModelCandidates();
3599
- const uniqueProviders = new Set(models.map((m) => m.provider));
3606
+ /** Update the footer's available provider count from the current snapshot without refreshing catalogs. */
3607
+ updateAvailableProviderCount() {
3608
+ const models = this.session.scopedModels.length > 0
3609
+ ? this.session.scopedModels.map((scoped) => scoped.model)
3610
+ : this.session.modelRuntime.getAvailableSnapshot();
3611
+ const uniqueProviders = new Set(models.map((model) => model.provider));
3600
3612
  this.footerDataProvider.setAvailableProviderCount(uniqueProviders.size);
3601
3613
  }
3602
3614
  async maybeWarnAboutAnthropicSubscriptionAuth(model = this.session.model) {