@mariozechner/pi-coding-agent 0.51.1 → 0.51.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 (65) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/config.d.ts +3 -0
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +36 -0
  5. package/dist/config.js.map +1 -1
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +49 -2
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/extensions/index.d.ts +2 -1
  10. package/dist/core/extensions/index.d.ts.map +1 -1
  11. package/dist/core/extensions/index.js.map +1 -1
  12. package/dist/core/extensions/loader.d.ts.map +1 -1
  13. package/dist/core/extensions/loader.js +4 -0
  14. package/dist/core/extensions/loader.js.map +1 -1
  15. package/dist/core/extensions/runner.d.ts +3 -1
  16. package/dist/core/extensions/runner.d.ts.map +1 -1
  17. package/dist/core/extensions/runner.js +17 -1
  18. package/dist/core/extensions/runner.js.map +1 -1
  19. package/dist/core/extensions/types.d.ts +9 -0
  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/package-manager.d.ts +2 -0
  23. package/dist/core/package-manager.d.ts.map +1 -1
  24. package/dist/core/package-manager.js +43 -11
  25. package/dist/core/package-manager.js.map +1 -1
  26. package/dist/core/sdk.d.ts +1 -1
  27. package/dist/core/sdk.d.ts.map +1 -1
  28. package/dist/core/sdk.js +7 -1
  29. package/dist/core/sdk.js.map +1 -1
  30. package/dist/core/slash-commands.d.ts +15 -0
  31. package/dist/core/slash-commands.d.ts.map +1 -0
  32. package/dist/core/slash-commands.js +21 -0
  33. package/dist/core/slash-commands.js.map +1 -0
  34. package/dist/core/tools/path-utils.d.ts.map +1 -1
  35. package/dist/core/tools/path-utils.js +4 -1
  36. package/dist/core/tools/path-utils.js.map +1 -1
  37. package/dist/index.d.ts +1 -1
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/main.d.ts.map +1 -1
  41. package/dist/main.js +60 -12
  42. package/dist/main.js.map +1 -1
  43. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  44. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  45. package/dist/modes/interactive/interactive-mode.js +47 -53
  46. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  47. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  48. package/dist/modes/rpc/rpc-mode.js +8 -1
  49. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  50. package/dist/modes/rpc/rpc-types.d.ts +1 -1
  51. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  52. package/dist/modes/rpc/rpc-types.js.map +1 -1
  53. package/docs/extensions.md +32 -0
  54. package/docs/packages.md +24 -1
  55. package/docs/rpc.md +4 -3
  56. package/docs/terminal-setup.md +5 -0
  57. package/examples/extensions/commands.ts +72 -0
  58. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  59. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  60. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  61. package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
  62. package/examples/extensions/notify.ts +41 -11
  63. package/examples/extensions/with-deps/package-lock.json +2 -2
  64. package/examples/extensions/with-deps/package.json +1 -1
  65. package/package.json +4 -4
@@ -9,13 +9,14 @@ 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, getShareViewerUrl, isBunBinary, isBunRuntime, VERSION, } from "../../config.js";
12
+ import { APP_NAME, getAuthPath, getDebugLogPath, getShareViewerUrl, getUpdateInstruction, VERSION, } from "../../config.js";
13
13
  import { parseSkillBlock } from "../../core/agent-session.js";
14
14
  import { FooterDataProvider } from "../../core/footer-data-provider.js";
15
15
  import { KeybindingsManager } from "../../core/keybindings.js";
16
16
  import { createCompactionSummaryMessage } from "../../core/messages.js";
17
17
  import { resolveModelScope } from "../../core/model-resolver.js";
18
18
  import { SessionManager } from "../../core/session-manager.js";
19
+ import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
19
20
  import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/changelog.js";
20
21
  import { copyToClipboard } from "../../utils/clipboard.js";
21
22
  import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
@@ -166,59 +167,44 @@ export class InteractiveMode {
166
167
  }
167
168
  setupAutocomplete(fdPath) {
168
169
  // Define commands for autocomplete
169
- const slashCommands = [
170
- { name: "settings", description: "Open settings menu" },
171
- {
172
- name: "model",
173
- description: "Select model (opens selector UI)",
174
- getArgumentCompletions: (prefix) => {
175
- // Get available models (scoped or from registry)
176
- const models = this.session.scopedModels.length > 0
177
- ? this.session.scopedModels.map((s) => s.model)
178
- : this.session.modelRegistry.getAvailable();
179
- if (models.length === 0)
180
- return null;
181
- // Create items with provider/id format
182
- const items = models.map((m) => ({
183
- id: m.id,
184
- provider: m.provider,
185
- label: `${m.provider}/${m.id}`,
186
- }));
187
- // Fuzzy filter by model ID + provider (allows "opus anthropic" to match)
188
- const filtered = fuzzyFilter(items, prefix, (item) => `${item.id} ${item.provider}`);
189
- if (filtered.length === 0)
190
- return null;
191
- return filtered.map((item) => ({
192
- value: item.label,
193
- label: item.id,
194
- description: item.provider,
195
- }));
196
- },
197
- },
198
- { name: "scoped-models", description: "Enable/disable models for Ctrl+P cycling" },
199
- { name: "export", description: "Export session to HTML file" },
200
- { name: "share", description: "Share session as a secret GitHub gist" },
201
- { name: "copy", description: "Copy last agent message to clipboard" },
202
- { name: "name", description: "Set session display name" },
203
- { name: "session", description: "Show session info and stats" },
204
- { name: "changelog", description: "Show changelog entries" },
205
- { name: "hotkeys", description: "Show all keyboard shortcuts" },
206
- { name: "fork", description: "Create a new fork from a previous message" },
207
- { name: "tree", description: "Navigate session tree (switch branches)" },
208
- { name: "login", description: "Login with OAuth provider" },
209
- { name: "logout", description: "Logout from OAuth provider" },
210
- { name: "new", description: "Start a new session" },
211
- { name: "compact", description: "Manually compact the session context" },
212
- { name: "resume", description: "Resume a different session" },
213
- { name: "reload", description: "Reload extensions, skills, prompts, and themes" },
214
- ];
170
+ const slashCommands = BUILTIN_SLASH_COMMANDS.map((command) => ({
171
+ name: command.name,
172
+ description: command.description,
173
+ }));
174
+ const modelCommand = slashCommands.find((command) => command.name === "model");
175
+ if (modelCommand) {
176
+ modelCommand.getArgumentCompletions = (prefix) => {
177
+ // Get available models (scoped or from registry)
178
+ const models = this.session.scopedModels.length > 0
179
+ ? this.session.scopedModels.map((s) => s.model)
180
+ : this.session.modelRegistry.getAvailable();
181
+ if (models.length === 0)
182
+ return null;
183
+ // Create items with provider/id format
184
+ const items = models.map((m) => ({
185
+ id: m.id,
186
+ provider: m.provider,
187
+ label: `${m.provider}/${m.id}`,
188
+ }));
189
+ // Fuzzy filter by model ID + provider (allows "opus anthropic" to match)
190
+ const filtered = fuzzyFilter(items, prefix, (item) => `${item.id} ${item.provider}`);
191
+ if (filtered.length === 0)
192
+ return null;
193
+ return filtered.map((item) => ({
194
+ value: item.label,
195
+ label: item.id,
196
+ description: item.provider,
197
+ }));
198
+ };
199
+ }
215
200
  // Convert prompt templates to SlashCommand format for autocomplete
216
201
  const templateCommands = this.session.promptTemplates.map((cmd) => ({
217
202
  name: cmd.name,
218
203
  description: cmd.description,
219
204
  }));
220
205
  // Convert extension commands to SlashCommand format
221
- const extensionCommands = (this.session.extensionRunner?.getRegisteredCommands() ?? []).map((cmd) => ({
206
+ const builtinCommandNames = new Set(slashCommands.map((c) => c.name));
207
+ const extensionCommands = (this.session.extensionRunner?.getRegisteredCommands(builtinCommandNames) ?? []).map((cmd) => ({
222
208
  name: cmd.name,
223
209
  description: cmd.description ?? "(extension command)",
224
210
  getArgumentCompletions: cmd.getArgumentCompletions,
@@ -724,6 +710,8 @@ export class InteractiveMode {
724
710
  extensionDiagnostics.push({ type: "error", message: error.error, path: error.path });
725
711
  }
726
712
  }
713
+ const commandDiagnostics = this.session.extensionRunner?.getCommandDiagnostics() ?? [];
714
+ extensionDiagnostics.push(...commandDiagnostics);
727
715
  const shortcutDiagnostics = this.session.extensionRunner?.getShortcutDiagnostics() ?? [];
728
716
  extensionDiagnostics.push(...shortcutDiagnostics);
729
717
  if (extensionDiagnostics.length > 0) {
@@ -1112,6 +1100,8 @@ export class InteractiveMode {
1112
1100
  }
1113
1101
  return result;
1114
1102
  },
1103
+ getToolsExpanded: () => this.toolOutputExpanded,
1104
+ setToolsExpanded: (expanded) => this.setToolsExpanded(expanded),
1115
1105
  };
1116
1106
  }
1117
1107
  /**
@@ -2110,6 +2100,9 @@ export class InteractiveMode {
2110
2100
  // Wait for any pending renders to complete
2111
2101
  // requestRender() uses process.nextTick(), so we wait one tick
2112
2102
  await new Promise((resolve) => process.nextTick(resolve));
2103
+ // Drain any in-flight Kitty key release events before stopping.
2104
+ // This prevents escape sequences from leaking to the parent shell over slow SSH.
2105
+ await this.ui.terminal.drainInput(1000);
2113
2106
  this.stop();
2114
2107
  process.exit(0);
2115
2108
  }
@@ -2211,10 +2204,13 @@ export class InteractiveMode {
2211
2204
  }
2212
2205
  }
2213
2206
  toggleToolOutputExpansion() {
2214
- this.toolOutputExpanded = !this.toolOutputExpanded;
2207
+ this.setToolsExpanded(!this.toolOutputExpanded);
2208
+ }
2209
+ setToolsExpanded(expanded) {
2210
+ this.toolOutputExpanded = expanded;
2215
2211
  for (const child of this.chatContainer.children) {
2216
2212
  if (isExpandable(child)) {
2217
- child.setExpanded(this.toolOutputExpanded);
2213
+ child.setExpanded(expanded);
2218
2214
  }
2219
2215
  }
2220
2216
  this.ui.requestRender();
@@ -2292,9 +2288,7 @@ export class InteractiveMode {
2292
2288
  this.ui.requestRender();
2293
2289
  }
2294
2290
  showNewVersionNotification(newVersion) {
2295
- const action = isBunBinary
2296
- ? `Download from: ${theme.fg("accent", "https://github.com/badlogic/pi-mono/releases/latest")}`
2297
- : `Run: ${theme.fg("accent", `${isBunRuntime ? "bun" : "npm"} install -g @mariozechner/pi-coding-agent`)}`;
2291
+ const action = theme.fg("accent", getUpdateInstruction("@mariozechner/pi-coding-agent"));
2298
2292
  const updateInstruction = theme.fg("muted", `New version ${newVersion} is available. `) + action;
2299
2293
  const changelogUrl = theme.fg("accent", "https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/CHANGELOG.md");
2300
2294
  const changelogLine = theme.fg("muted", "Changelog: ") + changelogUrl;