@oh-my-pi/pi-coding-agent 3.15.1 → 3.20.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 (129) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/docs/extensions.md +1055 -0
  3. package/docs/rpc.md +69 -13
  4. package/docs/session-tree-plan.md +1 -1
  5. package/examples/extensions/README.md +141 -0
  6. package/examples/extensions/api-demo.ts +87 -0
  7. package/examples/extensions/chalk-logger.ts +26 -0
  8. package/examples/extensions/hello.ts +33 -0
  9. package/examples/extensions/pirate.ts +44 -0
  10. package/examples/extensions/plan-mode.ts +551 -0
  11. package/examples/extensions/subagent/agents/reviewer.md +35 -0
  12. package/examples/extensions/todo.ts +299 -0
  13. package/examples/extensions/tools.ts +145 -0
  14. package/examples/extensions/with-deps/index.ts +36 -0
  15. package/examples/extensions/with-deps/package-lock.json +31 -0
  16. package/examples/extensions/with-deps/package.json +16 -0
  17. package/examples/sdk/02-custom-model.ts +3 -3
  18. package/examples/sdk/05-tools.ts +7 -3
  19. package/examples/sdk/06-extensions.ts +81 -0
  20. package/examples/sdk/06-hooks.ts +14 -13
  21. package/examples/sdk/08-prompt-templates.ts +42 -0
  22. package/examples/sdk/08-slash-commands.ts +17 -12
  23. package/examples/sdk/09-api-keys-and-oauth.ts +2 -2
  24. package/examples/sdk/12-full-control.ts +6 -6
  25. package/package.json +11 -7
  26. package/src/capability/extension-module.ts +34 -0
  27. package/src/cli/args.ts +22 -7
  28. package/src/cli/file-processor.ts +38 -67
  29. package/src/cli/list-models.ts +1 -1
  30. package/src/config.ts +25 -14
  31. package/src/core/agent-session.ts +505 -242
  32. package/src/core/auth-storage.ts +33 -21
  33. package/src/core/compaction/branch-summarization.ts +4 -4
  34. package/src/core/compaction/compaction.ts +3 -3
  35. package/src/core/custom-commands/bundled/wt/index.ts +430 -0
  36. package/src/core/custom-commands/loader.ts +9 -0
  37. package/src/core/custom-tools/wrapper.ts +5 -0
  38. package/src/core/event-bus.ts +59 -0
  39. package/src/core/export-html/vendor/highlight.min.js +1213 -0
  40. package/src/core/export-html/vendor/marked.min.js +6 -0
  41. package/src/core/extensions/index.ts +100 -0
  42. package/src/core/extensions/loader.ts +501 -0
  43. package/src/core/extensions/runner.ts +477 -0
  44. package/src/core/extensions/types.ts +712 -0
  45. package/src/core/extensions/wrapper.ts +147 -0
  46. package/src/core/hooks/types.ts +2 -2
  47. package/src/core/index.ts +10 -21
  48. package/src/core/keybindings.ts +199 -0
  49. package/src/core/messages.ts +26 -7
  50. package/src/core/model-registry.ts +123 -46
  51. package/src/core/model-resolver.ts +7 -5
  52. package/src/core/prompt-templates.ts +242 -0
  53. package/src/core/sdk.ts +378 -295
  54. package/src/core/session-manager.ts +72 -58
  55. package/src/core/settings-manager.ts +118 -22
  56. package/src/core/system-prompt.ts +24 -1
  57. package/src/core/terminal-notify.ts +37 -0
  58. package/src/core/tools/context.ts +4 -4
  59. package/src/core/tools/exa/mcp-client.ts +5 -4
  60. package/src/core/tools/exa/render.ts +176 -131
  61. package/src/core/tools/find.ts +7 -1
  62. package/src/core/tools/gemini-image.ts +361 -0
  63. package/src/core/tools/git.ts +216 -0
  64. package/src/core/tools/index.ts +28 -15
  65. package/src/core/tools/ls.ts +9 -2
  66. package/src/core/tools/lsp/config.ts +5 -4
  67. package/src/core/tools/lsp/index.ts +17 -12
  68. package/src/core/tools/lsp/render.ts +39 -47
  69. package/src/core/tools/read.ts +66 -29
  70. package/src/core/tools/render-utils.ts +268 -0
  71. package/src/core/tools/renderers.ts +243 -225
  72. package/src/core/tools/task/discovery.ts +2 -2
  73. package/src/core/tools/task/executor.ts +66 -58
  74. package/src/core/tools/task/index.ts +29 -10
  75. package/src/core/tools/task/model-resolver.ts +8 -13
  76. package/src/core/tools/task/omp-command.ts +24 -0
  77. package/src/core/tools/task/render.ts +37 -62
  78. package/src/core/tools/task/types.ts +3 -0
  79. package/src/core/tools/web-fetch.ts +29 -28
  80. package/src/core/tools/web-search/index.ts +6 -5
  81. package/src/core/tools/web-search/providers/exa.ts +6 -5
  82. package/src/core/tools/web-search/render.ts +66 -111
  83. package/src/core/voice-controller.ts +135 -0
  84. package/src/core/voice-supervisor.ts +1003 -0
  85. package/src/core/voice.ts +308 -0
  86. package/src/discovery/builtin.ts +75 -1
  87. package/src/discovery/claude.ts +47 -1
  88. package/src/discovery/codex.ts +54 -2
  89. package/src/discovery/gemini.ts +55 -2
  90. package/src/discovery/helpers.ts +100 -1
  91. package/src/discovery/index.ts +2 -0
  92. package/src/index.ts +14 -9
  93. package/src/lib/worktree/collapse.ts +179 -0
  94. package/src/lib/worktree/constants.ts +14 -0
  95. package/src/lib/worktree/errors.ts +23 -0
  96. package/src/lib/worktree/git.ts +110 -0
  97. package/src/lib/worktree/index.ts +23 -0
  98. package/src/lib/worktree/operations.ts +216 -0
  99. package/src/lib/worktree/session.ts +114 -0
  100. package/src/lib/worktree/stats.ts +67 -0
  101. package/src/main.ts +61 -37
  102. package/src/migrations.ts +37 -7
  103. package/src/modes/interactive/components/bash-execution.ts +6 -4
  104. package/src/modes/interactive/components/custom-editor.ts +55 -0
  105. package/src/modes/interactive/components/custom-message.ts +95 -0
  106. package/src/modes/interactive/components/extensions/extension-list.ts +5 -0
  107. package/src/modes/interactive/components/extensions/inspector-panel.ts +18 -12
  108. package/src/modes/interactive/components/extensions/state-manager.ts +12 -0
  109. package/src/modes/interactive/components/extensions/types.ts +1 -0
  110. package/src/modes/interactive/components/footer.ts +324 -0
  111. package/src/modes/interactive/components/hook-selector.ts +3 -3
  112. package/src/modes/interactive/components/model-selector.ts +7 -6
  113. package/src/modes/interactive/components/oauth-selector.ts +3 -3
  114. package/src/modes/interactive/components/settings-defs.ts +55 -6
  115. package/src/modes/interactive/components/status-line.ts +45 -37
  116. package/src/modes/interactive/components/tool-execution.ts +95 -23
  117. package/src/modes/interactive/interactive-mode.ts +643 -113
  118. package/src/modes/interactive/theme/defaults/index.ts +16 -16
  119. package/src/modes/print-mode.ts +14 -72
  120. package/src/modes/rpc/rpc-client.ts +23 -9
  121. package/src/modes/rpc/rpc-mode.ts +137 -125
  122. package/src/modes/rpc/rpc-types.ts +46 -24
  123. package/src/prompts/task.md +1 -0
  124. package/src/prompts/tools/gemini-image.md +4 -0
  125. package/src/prompts/tools/git.md +9 -0
  126. package/src/prompts/voice-summary.md +12 -0
  127. package/src/utils/image-convert.ts +26 -0
  128. package/src/utils/image-resize.ts +215 -0
  129. package/src/utils/shell-snapshot.ts +22 -20
@@ -96,11 +96,11 @@ import sandstone from "./sandstone.json" with { type: "json" };
96
96
  import titanium from "./titanium.json" with { type: "json" };
97
97
 
98
98
  export const defaultThemes = {
99
- "alabaster": alabaster,
100
- "amethyst": amethyst,
101
- "anthracite": anthracite,
102
- "basalt": basalt,
103
- "birch": birch,
99
+ alabaster: alabaster,
100
+ amethyst: amethyst,
101
+ anthracite: anthracite,
102
+ basalt: basalt,
103
+ birch: birch,
104
104
  "dark-abyss": dark_abyss,
105
105
  "dark-arctic": dark_arctic,
106
106
  "dark-aurora": dark_aurora,
@@ -143,7 +143,7 @@ export const defaultThemes = {
143
143
  "dark-tundra": dark_tundra,
144
144
  "dark-twilight": dark_twilight,
145
145
  "dark-volcanic": dark_volcanic,
146
- "graphite": graphite,
146
+ graphite: graphite,
147
147
  "light-arctic": light_arctic,
148
148
  "light-aurora-day": light_aurora_day,
149
149
  "light-canyon": light_canyon,
@@ -182,14 +182,14 @@ export const defaultThemes = {
182
182
  "light-tokyo-night": light_tokyo_night,
183
183
  "light-wetland": light_wetland,
184
184
  "light-zenith": light_zenith,
185
- "limestone": limestone,
186
- "mahogany": mahogany,
187
- "marble": marble,
188
- "obsidian": obsidian,
189
- "onyx": onyx,
190
- "pearl": pearl,
191
- "porcelain": porcelain,
192
- "quartz": quartz,
193
- "sandstone": sandstone,
194
- "titanium": titanium,
185
+ limestone: limestone,
186
+ mahogany: mahogany,
187
+ marble: marble,
188
+ obsidian: obsidian,
189
+ onyx: onyx,
190
+ pearl: pearl,
191
+ porcelain: porcelain,
192
+ quartz: quartz,
193
+ sandstone: sandstone,
194
+ titanium: titanium,
195
195
  };
@@ -7,34 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { AssistantMessage, ImageContent } from "@oh-my-pi/pi-ai";
10
- import { APP_NAME, VERSION } from "../config";
11
10
  import type { AgentSession } from "../core/agent-session";
12
- import { logger } from "../core/logger";
13
-
14
- /**
15
- * Print session header to stderr (text mode only).
16
- */
17
- function printHeader(session: AgentSession): void {
18
- const model = session.model;
19
- const lines = [
20
- `${APP_NAME} v${VERSION}`,
21
- "--------",
22
- `workdir: ${process.cwd()}`,
23
- `model: ${model?.id ?? "unknown"}`,
24
- `provider: ${model?.provider ?? "unknown"}`,
25
- `thinking: ${session.thinkingLevel}`,
26
- `session: ${session.sessionId}`,
27
- "--------",
28
- ];
29
- console.error(lines.join("\n"));
30
- }
31
-
32
- /**
33
- * Print session footer to stderr (text mode only).
34
- */
35
- function printFooter(): void {
36
- console.error("--------");
37
- }
38
11
 
39
12
  /**
40
13
  * Run in print (single-shot) mode.
@@ -53,61 +26,33 @@ export async function runPrintMode(
53
26
  initialMessage?: string,
54
27
  initialImages?: ImageContent[],
55
28
  ): Promise<void> {
56
- // Print header to stderr (text mode only)
57
- if (mode === "text") {
58
- printHeader(session);
59
- }
60
-
61
- // Hook runner already has no-op UI context by default (set in main.ts)
62
- // Set up hooks for print mode (no UI)
63
- const hookRunner = session.hookRunner;
64
- if (hookRunner) {
65
- hookRunner.initialize({
29
+ // Extension runner already has no-op UI context by default (set in loader)
30
+ // Set up extensions for print mode (no UI)
31
+ const extensionRunner = session.extensionRunner;
32
+ if (extensionRunner) {
33
+ extensionRunner.initialize({
66
34
  getModel: () => session.model,
67
- sendMessageHandler: (message, triggerTurn) => {
68
- session.sendHookMessage(message, triggerTurn).catch((e) => {
69
- console.error(`Hook sendMessage failed: ${e instanceof Error ? e.message : String(e)}`);
35
+ sendMessageHandler: (message, options) => {
36
+ session.sendCustomMessage(message, options).catch((e) => {
37
+ console.error(`Extension sendMessage failed: ${e instanceof Error ? e.message : String(e)}`);
70
38
  });
71
39
  },
72
40
  appendEntryHandler: (customType, data) => {
73
41
  session.sessionManager.appendCustomEntry(customType, data);
74
42
  },
43
+ getActiveToolsHandler: () => session.getActiveToolNames(),
44
+ getAllToolsHandler: () => session.getAllToolNames(),
45
+ setActiveToolsHandler: (toolNames: string[]) => session.setActiveToolsByName(toolNames),
75
46
  });
76
- hookRunner.onError((err) => {
77
- console.error(`Hook error (${err.hookPath}): ${err.error}`);
47
+ extensionRunner.onError((err) => {
48
+ console.error(`Extension error (${err.extensionPath}): ${err.error}`);
78
49
  });
79
50
  // Emit session_start event
80
- await hookRunner.emit({
51
+ await extensionRunner.emit({
81
52
  type: "session_start",
82
53
  });
83
54
  }
84
55
 
85
- // Emit session start event to custom tools (no UI in print mode)
86
- for (const { tool } of session.customTools) {
87
- if (tool.onSession) {
88
- try {
89
- await tool.onSession(
90
- {
91
- reason: "start",
92
- previousSessionFile: undefined,
93
- },
94
- {
95
- sessionManager: session.sessionManager,
96
- modelRegistry: session.modelRegistry,
97
- model: session.model,
98
- isIdle: () => !session.isStreaming,
99
- hasQueuedMessages: () => session.queuedMessageCount > 0,
100
- abort: () => {
101
- session.abort();
102
- },
103
- },
104
- );
105
- } catch (err) {
106
- logger.warn("Tool onSession error", { error: String(err) });
107
- }
108
- }
109
- }
110
-
111
56
  // Always subscribe to enable session persistence via _handleAgentEvent
112
57
  session.subscribe((event) => {
113
58
  // In JSON mode, output all events
@@ -147,9 +92,6 @@ export async function runPrintMode(
147
92
  }
148
93
  }
149
94
  }
150
-
151
- // Print footer to stderr
152
- printFooter();
153
95
  }
154
96
 
155
97
  // Ensure stdout is fully flushed before returning
@@ -210,10 +210,17 @@ export class RpcClient {
210
210
  }
211
211
 
212
212
  /**
213
- * Queue a message while agent is streaming.
213
+ * Queue a steering message to interrupt the agent mid-run.
214
214
  */
215
- async queueMessage(message: string): Promise<void> {
216
- await this.send({ type: "queue_message", message });
215
+ async steer(message: string): Promise<void> {
216
+ await this.send({ type: "steer", message });
217
+ }
218
+
219
+ /**
220
+ * Queue a follow-up message to be processed after the agent finishes.
221
+ */
222
+ async followUp(message: string): Promise<void> {
223
+ await this.send({ type: "follow_up", message });
217
224
  }
218
225
 
219
226
  /**
@@ -226,7 +233,7 @@ export class RpcClient {
226
233
  /**
227
234
  * Start a new session, optionally with parent tracking.
228
235
  * @param parentSession - Optional parent session path for lineage tracking
229
- * @returns Object with `cancelled: true` if a hook cancelled the new session
236
+ * @returns Object with `cancelled: true` if an extension cancelled the new session
230
237
  */
231
238
  async newSession(parentSession?: string): Promise<{ cancelled: boolean }> {
232
239
  const response = await this.send({ type: "new_session", parentSession });
@@ -285,10 +292,17 @@ export class RpcClient {
285
292
  }
286
293
 
287
294
  /**
288
- * Set queue mode.
295
+ * Set steering mode.
296
+ */
297
+ async setSteeringMode(mode: "all" | "one-at-a-time"): Promise<void> {
298
+ await this.send({ type: "set_steering_mode", mode });
299
+ }
300
+
301
+ /**
302
+ * Set follow-up mode.
289
303
  */
290
- async setQueueMode(mode: "all" | "one-at-a-time"): Promise<void> {
291
- await this.send({ type: "set_queue_mode", mode });
304
+ async setFollowUpMode(mode: "all" | "one-at-a-time"): Promise<void> {
305
+ await this.send({ type: "set_follow_up_mode", mode });
292
306
  }
293
307
 
294
308
  /**
@@ -353,7 +367,7 @@ export class RpcClient {
353
367
 
354
368
  /**
355
369
  * Switch to a different session file.
356
- * @returns Object with `cancelled: true` if a hook cancelled the switch
370
+ * @returns Object with `cancelled: true` if an extension cancelled the switch
357
371
  */
358
372
  async switchSession(sessionPath: string): Promise<{ cancelled: boolean }> {
359
373
  const response = await this.send({ type: "switch_session", sessionPath });
@@ -362,7 +376,7 @@ export class RpcClient {
362
376
 
363
377
  /**
364
378
  * Branch from a specific message.
365
- * @returns Object with `text` (the message text) and `cancelled` (if hook cancelled)
379
+ * @returns Object with `text` (the message text) and `cancelled` (if extension cancelled)
366
380
  */
367
381
  async branch(entryId: string): Promise<{ text: string; cancelled: boolean }> {
368
382
  const response = await this.send({ type: "branch", entryId });