@averyyy/pi-coding-agent 0.81.1-piclient.1 → 0.82.1-piclient.1

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 (114) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +13 -0
  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/cli/config-selector.d.ts.map +1 -1
  7. package/dist/cli/config-selector.js +1 -1
  8. package/dist/cli/config-selector.js.map +1 -1
  9. package/dist/cli/startup-ui.d.ts.map +1 -1
  10. package/dist/cli/startup-ui.js +1 -1
  11. package/dist/cli/startup-ui.js.map +1 -1
  12. package/dist/core/agent-session.d.ts +11 -0
  13. package/dist/core/agent-session.d.ts.map +1 -1
  14. package/dist/core/agent-session.js +7 -8
  15. package/dist/core/agent-session.js.map +1 -1
  16. package/dist/core/compaction/compaction.d.ts.map +1 -1
  17. package/dist/core/compaction/compaction.js +11 -3
  18. package/dist/core/compaction/compaction.js.map +1 -1
  19. package/dist/core/extensions/runner.d.ts.map +1 -1
  20. package/dist/core/extensions/runner.js +4 -0
  21. package/dist/core/extensions/runner.js.map +1 -1
  22. package/dist/core/extensions/types.d.ts +7 -1
  23. package/dist/core/extensions/types.d.ts.map +1 -1
  24. package/dist/core/extensions/types.js.map +1 -1
  25. package/dist/core/model-config.d.ts +30 -0
  26. package/dist/core/model-config.d.ts.map +1 -1
  27. package/dist/core/model-config.js +6 -0
  28. package/dist/core/model-config.js.map +1 -1
  29. package/dist/core/model-registry.d.ts.map +1 -1
  30. package/dist/core/model-registry.js +2 -2
  31. package/dist/core/model-registry.js.map +1 -1
  32. package/dist/core/model-resolver.d.ts +1 -0
  33. package/dist/core/model-resolver.d.ts.map +1 -1
  34. package/dist/core/model-resolver.js +20 -3
  35. package/dist/core/model-resolver.js.map +1 -1
  36. package/dist/core/model-runtime.d.ts +0 -1
  37. package/dist/core/model-runtime.d.ts.map +1 -1
  38. package/dist/core/model-runtime.js +3 -5
  39. package/dist/core/model-runtime.js.map +1 -1
  40. package/dist/core/remote-catalog-provider.d.ts +1 -1
  41. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  42. package/dist/core/remote-catalog-provider.js +24 -11
  43. package/dist/core/remote-catalog-provider.js.map +1 -1
  44. package/dist/core/resource-loader.d.ts.map +1 -1
  45. package/dist/core/resource-loader.js +3 -0
  46. package/dist/core/resource-loader.js.map +1 -1
  47. package/dist/core/settings-manager.d.ts +1 -1
  48. package/dist/core/settings-manager.d.ts.map +1 -1
  49. package/dist/core/settings-manager.js.map +1 -1
  50. package/dist/core/system-prompt.d.ts.map +1 -1
  51. package/dist/core/system-prompt.js +1 -1
  52. package/dist/core/system-prompt.js.map +1 -1
  53. package/dist/core/tools/bash.d.ts +2 -0
  54. package/dist/core/tools/bash.d.ts.map +1 -1
  55. package/dist/core/tools/bash.js +34 -5
  56. package/dist/core/tools/bash.js.map +1 -1
  57. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  58. package/dist/core/tools/tool-definition-wrapper.js +3 -1
  59. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  60. package/dist/extensions/llama/provider.d.ts.map +1 -1
  61. package/dist/extensions/llama/provider.js +7 -2
  62. package/dist/extensions/llama/provider.js.map +1 -1
  63. package/dist/modes/interactive/components/custom-message.d.ts +3 -1
  64. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  65. package/dist/modes/interactive/components/custom-message.js +10 -2
  66. package/dist/modes/interactive/components/custom-message.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/components/scoped-models-selector.d.ts.map +1 -1
  72. package/dist/modes/interactive/components/scoped-models-selector.js +22 -13
  73. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  74. package/dist/modes/interactive/external-editor.d.ts +12 -0
  75. package/dist/modes/interactive/external-editor.d.ts.map +1 -0
  76. package/dist/modes/interactive/external-editor.js +37 -0
  77. package/dist/modes/interactive/external-editor.js.map +1 -0
  78. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  79. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  80. package/dist/modes/interactive/interactive-mode.js +52 -58
  81. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  82. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  83. package/dist/modes/rpc/rpc-mode.js +1 -0
  84. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  85. package/dist/utils/clipboard.d.ts.map +1 -1
  86. package/dist/utils/clipboard.js +19 -8
  87. package/dist/utils/clipboard.js.map +1 -1
  88. package/dist/utils/version-check.d.ts.map +1 -1
  89. package/dist/utils/version-check.js +3 -1
  90. package/dist/utils/version-check.js.map +1 -1
  91. package/docs/compaction.md +1 -1
  92. package/docs/custom-provider.md +5 -2
  93. package/docs/environment-variables.md +88 -0
  94. package/docs/extensions.md +13 -5
  95. package/docs/index.md +1 -0
  96. package/docs/models.md +6 -2
  97. package/docs/providers.md +8 -1
  98. package/docs/rpc.md +23 -6
  99. package/docs/settings.md +1 -1
  100. package/docs/usage.md +0 -13
  101. package/examples/extensions/custom-compaction.ts +3 -0
  102. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  103. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  104. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  105. package/examples/extensions/gondolin/package-lock.json +2 -2
  106. package/examples/extensions/gondolin/package.json +1 -1
  107. package/examples/extensions/handoff.ts +9 -1
  108. package/examples/extensions/message-renderer.ts +2 -2
  109. package/examples/extensions/sandbox/package-lock.json +2 -2
  110. package/examples/extensions/sandbox/package.json +1 -1
  111. package/examples/extensions/summarize.ts +3 -0
  112. package/examples/extensions/with-deps/package-lock.json +2 -2
  113. package/examples/extensions/with-deps/package.json +1 -1
  114. package/package.json +5 -4
@@ -17,7 +17,7 @@ import { FooterDataProvider } from "../../core/footer-data-provider.js";
17
17
  import { configureHttpDispatcher, formatHttpIdleTimeoutMs } from "../../core/http-dispatcher.js";
18
18
  import { KeybindingsManager } from "../../core/keybindings.js";
19
19
  import { createCompactionSummaryMessage } from "../../core/messages.js";
20
- import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScope } from "../../core/model-resolver.js";
20
+ import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScope, resolveModelScopeWithDiagnostics, } from "../../core/model-resolver.js";
21
21
  import { DefaultPackageManager } from "../../core/package-manager.js";
22
22
  import { getPiClientUpdateMarkerPath, PI_CLIENT_RUNTIME_RELOAD_EXIT_CODE, readPiClientUpdateMarker, writePiClientRuntimeReloadState, } from "../../core/pi-client-runtime-update.js";
23
23
  import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../core/session-cwd.js";
@@ -65,6 +65,7 @@ import { TreeSelectorComponent } from "./components/tree-selector.js";
65
65
  import { TrustSelectorComponent } from "./components/trust-selector.js";
66
66
  import { UserMessageComponent } from "./components/user-message.js";
67
67
  import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
68
+ import { editInExternalEditor } from "./external-editor.js";
68
69
  import { getModelSearchText } from "./model-search.js";
69
70
  import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
70
71
  import { InteractiveThemeController } from "./theme/theme-controller.js";
@@ -273,7 +274,7 @@ export class InteractiveMode {
273
274
  await this.rebindCurrentSession({ renderBeforeBind: true });
274
275
  });
275
276
  this.version = VERSION;
276
- this.ui = new TUI(new ProcessTerminal(), this.settingsManager.getShowHardwareCursor());
277
+ this.ui = new TUI(new ProcessTerminal(), this.settingsManager.getShowHardwareCursor(), getAgentDir());
277
278
  this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
278
279
  this.headerContainer = new Container();
279
280
  this.loadedResourcesContainer = new Container();
@@ -798,8 +799,15 @@ export class InteractiveMode {
798
799
  * Get a short path relative to the package root for display.
799
800
  */
800
801
  getShortPath(fullPath, sourceInfo) {
802
+ const normalizedFullPath = fullPath.replace(/\\/g, "/");
801
803
  const baseDir = sourceInfo?.baseDir;
802
804
  if (baseDir && this.isPackageSource(sourceInfo)) {
805
+ const normalizedBaseDir = baseDir.replace(/\\/g, "/");
806
+ const npmRootMatch = normalizedBaseDir.match(/^(.*\/node_modules)\/(@?[^/]+(?:\/[^/]+)?)$/);
807
+ // If fullPath is under the same node_modules root as baseDir, preserve that relative topology.
808
+ if (npmRootMatch?.[1] && normalizedFullPath.startsWith(`${npmRootMatch[1]}/`)) {
809
+ return path.posix.relative(normalizedBaseDir, normalizedFullPath);
810
+ }
803
811
  const relativePath = path.relative(path.resolve(baseDir), path.resolve(fullPath));
804
812
  if (relativePath &&
805
813
  relativePath !== "." &&
@@ -810,11 +818,11 @@ export class InteractiveMode {
810
818
  }
811
819
  }
812
820
  const source = sourceInfo?.source ?? "";
813
- const npmMatch = fullPath.match(/node_modules\/(@?[^/]+(?:\/[^/]+)?)\/(.*)/);
821
+ const npmMatch = normalizedFullPath.match(/node_modules\/(@?[^/]+(?:\/[^/]+)?)\/(.*)/);
814
822
  if (npmMatch && source.startsWith("npm:")) {
815
823
  return npmMatch[2];
816
824
  }
817
- const gitMatch = fullPath.match(/git\/[^/]+\/[^/]+\/(.*)/);
825
+ const gitMatch = normalizedFullPath.match(/git\/[^/]+\/[^/]+\/(.*)/);
818
826
  if (gitMatch && source.startsWith("git:")) {
819
827
  return gitMatch[1];
820
828
  }
@@ -1354,6 +1362,7 @@ export class InteractiveMode {
1354
1362
  sessionManager: this.sessionManager,
1355
1363
  modelRegistry: extensionRunner.getModelRegistry(),
1356
1364
  model: this.session.model,
1365
+ thinkingLevel: this.session.thinkingLevel,
1357
1366
  isIdle: () => this.session.isIdle,
1358
1367
  isProjectTrusted: () => this.settingsManager.isProjectTrusted(),
1359
1368
  signal: this.session.agent.signal,
@@ -2039,7 +2048,7 @@ export class InteractiveMode {
2039
2048
  this.defaultEditor.onAction("app.model.select", () => this.showModelSelector());
2040
2049
  this.defaultEditor.onAction("app.tools.expand", () => this.toggleToolOutputExpansion());
2041
2050
  this.defaultEditor.onAction("app.thinking.toggle", () => this.toggleThinkingBlockVisibility());
2042
- this.defaultEditor.onAction("app.editor.external", () => this.openExternalEditor());
2051
+ this.defaultEditor.onAction("app.editor.external", () => void this.handleOpenExternalEditor());
2043
2052
  this.defaultEditor.onAction("app.message.copy", () => void this.handleCopyCommand());
2044
2053
  this.defaultEditor.onAction("app.message.followUp", () => this.handleFollowUp());
2045
2054
  this.defaultEditor.onAction("app.message.dequeue", () => this.handleDequeue());
@@ -2407,6 +2416,9 @@ export class InteractiveMode {
2407
2416
  }
2408
2417
  this.ui.requestRender();
2409
2418
  break;
2419
+ case "bash_execution_update":
2420
+ // The bash execution callback handles TUI output rendering.
2421
+ break;
2410
2422
  case "tool_execution_start": {
2411
2423
  let component = this.pendingTools.get(event.toolCallId);
2412
2424
  if (!component) {
@@ -2617,7 +2629,7 @@ export class InteractiveMode {
2617
2629
  case "custom": {
2618
2630
  if (message.display) {
2619
2631
  const renderer = this.session.extensionRunner.getMessageRenderer(message.customType);
2620
- const component = new CustomMessageComponent(message, renderer, this.getMarkdownThemeWithSettings());
2632
+ const component = new CustomMessageComponent(message, renderer, this.getMarkdownThemeWithSettings(), this.outputPad);
2621
2633
  component.setExpanded(this.toolOutputExpanded);
2622
2634
  this.chatContainer.addChild(component);
2623
2635
  }
@@ -3129,51 +3141,21 @@ export class InteractiveMode {
3129
3141
  }
3130
3142
  this.showStatus(`Thinking blocks: ${this.hideThinkingBlock ? "hidden" : "visible"}`);
3131
3143
  }
3132
- async openExternalEditor() {
3144
+ async handleOpenExternalEditor() {
3133
3145
  const editorCmd = this.settingsManager.getExternalEditorCommand();
3134
- if (!editorCmd) {
3135
- this.showWarning("No editor configured. Set externalEditor in settings.json or $VISUAL/$EDITOR.");
3136
- return;
3137
- }
3138
- const currentText = this.editor.getExpandedText?.() ?? this.editor.getText();
3139
- const tmpFile = path.join(os.tmpdir(), `pi-editor-${Date.now()}.pi.md`);
3146
+ const content = this.editor.getExpandedText?.() ?? this.editor.getText();
3147
+ this.ui.stop();
3140
3148
  try {
3141
- // Write current content to temp file
3142
- fs.writeFileSync(tmpFile, currentText, "utf-8");
3143
- // Stop TUI to release terminal
3144
- this.ui.stop();
3145
- // Split by space to support editor arguments (e.g., "code --wait")
3146
- const [editor, ...editorArgs] = editorCmd.split(" ");
3147
- process.stdout.write(`Launching external editor: ${editorCmd}\nPi will resume when the editor exits.\n`);
3148
- // Do not use spawnSync here. On Windows, synchronous child_process calls can keep
3149
- // Node/libuv's console input read active after ui.stop() pauses stdin, racing
3150
- // vim/nvim for the console input buffer until Ctrl+C cancels the pending read.
3151
- const status = await new Promise((resolve) => {
3152
- const child = spawn(editor, [...editorArgs, tmpFile], {
3153
- stdio: "inherit",
3154
- shell: process.platform === "win32",
3155
- });
3156
- child.on("error", () => resolve(null));
3157
- child.on("close", (code) => resolve(code));
3149
+ const result = await editInExternalEditor({
3150
+ command: editorCmd,
3151
+ content,
3158
3152
  });
3159
- // On successful exit (status 0), replace editor content
3160
- if (status === 0) {
3161
- const newContent = fs.readFileSync(tmpFile, "utf-8").replace(/\n$/, "");
3162
- this.editor.setText(newContent);
3153
+ if (result.status === "complete") {
3154
+ this.editor.setText(result.content);
3163
3155
  }
3164
- // On non-zero exit, keep original text (no action needed)
3165
3156
  }
3166
3157
  finally {
3167
- // Clean up temp file
3168
- try {
3169
- fs.unlinkSync(tmpFile);
3170
- }
3171
- catch {
3172
- // Ignore cleanup errors
3173
- }
3174
- // Restart TUI
3175
3158
  this.ui.start();
3176
- // Force full re-render since external editor uses alternate screen
3177
3159
  this.ui.requestRender(true);
3178
3160
  }
3179
3161
  }
@@ -3550,7 +3532,9 @@ export class InteractiveMode {
3550
3532
  this.outputPad = padding;
3551
3533
  if (this.streamingComponent || this.session.isStreaming) {
3552
3534
  for (const child of this.chatContainer.children) {
3553
- if (child instanceof AssistantMessageComponent || child instanceof UserMessageComponent) {
3535
+ if (child instanceof AssistantMessageComponent ||
3536
+ child instanceof CustomMessageComponent ||
3537
+ child instanceof UserMessageComponent) {
3554
3538
  child.setOutputPad(padding);
3555
3539
  }
3556
3540
  }
@@ -3735,12 +3719,17 @@ export class InteractiveMode {
3735
3719
  // Get all available models
3736
3720
  await this.session.modelRuntime.refresh();
3737
3721
  const allModels = [...(await this.session.modelRuntime.getAvailable())];
3738
- if (allModels.length === 0) {
3722
+ const allModelIds = new Set(allModels.map((model) => `${model.provider}/${model.id}`));
3723
+ const configuredPatterns = this.settingsManager.getEnabledModels();
3724
+ const sessionScopedModels = this.session.scopedModels;
3725
+ if (allModels.length === 0 && !configuredPatterns?.length && sessionScopedModels.length === 0) {
3739
3726
  this.showStatus("No models available");
3740
3727
  return;
3741
3728
  }
3729
+ const configuredScope = configuredPatterns?.length
3730
+ ? await resolveModelScopeWithDiagnostics(configuredPatterns, this.session.modelRuntime)
3731
+ : undefined;
3742
3732
  // Check if session has scoped models (from previous session-only changes or CLI --models)
3743
- const sessionScopedModels = this.session.scopedModels;
3744
3733
  const hasSessionScope = sessionScopedModels.length > 0;
3745
3734
  // Build enabled model IDs from session state or settings
3746
3735
  let currentEnabledIds = null;
@@ -3748,18 +3737,22 @@ export class InteractiveMode {
3748
3737
  // Use current session's scoped models
3749
3738
  currentEnabledIds = sessionScopedModels.map((scoped) => `${scoped.model.provider}/${scoped.model.id}`);
3750
3739
  }
3751
- else {
3752
- // Fall back to settings
3753
- const patterns = this.settingsManager.getEnabledModels();
3754
- if (patterns !== undefined && patterns.length > 0) {
3755
- const scopedModels = await resolveModelScope(patterns, this.session.modelRuntime);
3756
- currentEnabledIds = scopedModels.map((scoped) => `${scoped.model.provider}/${scoped.model.id}`);
3757
- }
3740
+ else if (configuredScope) {
3741
+ currentEnabledIds = configuredScope.scopedModels.map((scoped) => `${scoped.model.provider}/${scoped.model.id}`);
3742
+ }
3743
+ for (const diagnostic of configuredScope?.diagnostics ?? []) {
3744
+ if (diagnostic.code !== "no-match")
3745
+ continue;
3746
+ currentEnabledIds ??= [];
3747
+ if (!currentEnabledIds.includes(diagnostic.pattern))
3748
+ currentEnabledIds.push(diagnostic.pattern);
3758
3749
  }
3759
3750
  // Helper to update session's scoped models (session-only, no persist)
3760
3751
  const updateSessionModels = async (enabledIds) => {
3761
3752
  currentEnabledIds = enabledIds === null ? null : [...enabledIds];
3762
- if (enabledIds && enabledIds.length > 0 && enabledIds.length < allModels.length) {
3753
+ const hasEnabledAvailableModel = enabledIds?.some((id) => allModelIds.has(id)) ?? false;
3754
+ const allAvailableModelsEnabled = enabledIds !== null && [...allModelIds].every((id) => enabledIds.includes(id));
3755
+ if (enabledIds && hasEnabledAvailableModel && !allAvailableModelsEnabled) {
3763
3756
  const newScopedModels = await resolveModelScope(enabledIds, this.session.modelRuntime);
3764
3757
  this.session.setScopedModels(newScopedModels.map((sm) => ({
3765
3758
  model: sm.model,
@@ -3783,9 +3776,10 @@ export class InteractiveMode {
3783
3776
  },
3784
3777
  onPersist: (enabledIds) => {
3785
3778
  // Persist to settings
3786
- const newPatterns = enabledIds === null || enabledIds.length === allModels.length
3787
- ? undefined // All enabled = clear filter
3788
- : enabledIds;
3779
+ const allEnabled = enabledIds !== null &&
3780
+ enabledIds.length === allModels.length &&
3781
+ enabledIds.every((id) => allModelIds.has(id));
3782
+ const newPatterns = enabledIds === null || allEnabled ? undefined : enabledIds;
3789
3783
  this.settingsManager.setEnabledModels(newPatterns ? [...newPatterns] : undefined);
3790
3784
  this.showStatus("Model selection saved to settings");
3791
3785
  },