@mariozechner/pi-coding-agent 0.31.1 → 0.32.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 (112) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +56 -5
  3. package/dist/cli/file-processor.d.ts +5 -1
  4. package/dist/cli/file-processor.d.ts.map +1 -1
  5. package/dist/cli/file-processor.js +28 -8
  6. package/dist/cli/file-processor.js.map +1 -1
  7. package/dist/core/agent-session.d.ts +41 -16
  8. package/dist/core/agent-session.d.ts.map +1 -1
  9. package/dist/core/agent-session.js +90 -41
  10. package/dist/core/agent-session.js.map +1 -1
  11. package/dist/core/auth-storage.d.ts +6 -1
  12. package/dist/core/auth-storage.d.ts.map +1 -1
  13. package/dist/core/auth-storage.js +16 -1
  14. package/dist/core/auth-storage.js.map +1 -1
  15. package/dist/core/custom-tools/types.d.ts +1 -1
  16. package/dist/core/custom-tools/types.d.ts.map +1 -1
  17. package/dist/core/custom-tools/types.js.map +1 -1
  18. package/dist/core/hooks/loader.d.ts +4 -1
  19. package/dist/core/hooks/loader.d.ts.map +1 -1
  20. package/dist/core/hooks/loader.js +2 -2
  21. package/dist/core/hooks/loader.js.map +1 -1
  22. package/dist/core/hooks/runner.d.ts +2 -2
  23. package/dist/core/hooks/runner.d.ts.map +1 -1
  24. package/dist/core/hooks/runner.js +3 -3
  25. package/dist/core/hooks/runner.js.map +1 -1
  26. package/dist/core/hooks/types.d.ts +10 -4
  27. package/dist/core/hooks/types.d.ts.map +1 -1
  28. package/dist/core/hooks/types.js.map +1 -1
  29. package/dist/core/model-registry.d.ts +5 -2
  30. package/dist/core/model-registry.d.ts.map +1 -1
  31. package/dist/core/model-registry.js +85 -49
  32. package/dist/core/model-registry.js.map +1 -1
  33. package/dist/core/model-resolver.d.ts.map +1 -1
  34. package/dist/core/model-resolver.js +1 -0
  35. package/dist/core/model-resolver.js.map +1 -1
  36. package/dist/core/sdk.d.ts.map +1 -1
  37. package/dist/core/sdk.js +9 -6
  38. package/dist/core/sdk.js.map +1 -1
  39. package/dist/core/settings-manager.d.ts +17 -3
  40. package/dist/core/settings-manager.d.ts.map +1 -1
  41. package/dist/core/settings-manager.js +41 -6
  42. package/dist/core/settings-manager.js.map +1 -1
  43. package/dist/core/tools/index.d.ts +9 -4
  44. package/dist/core/tools/index.d.ts.map +1 -1
  45. package/dist/core/tools/index.js +6 -6
  46. package/dist/core/tools/index.js.map +1 -1
  47. package/dist/core/tools/read.d.ts +5 -1
  48. package/dist/core/tools/read.d.ts.map +1 -1
  49. package/dist/core/tools/read.js +22 -5
  50. package/dist/core/tools/read.js.map +1 -1
  51. package/dist/index.d.ts +3 -3
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +2 -2
  54. package/dist/index.js.map +1 -1
  55. package/dist/main.d.ts.map +1 -1
  56. package/dist/main.js +5 -5
  57. package/dist/main.js.map +1 -1
  58. package/dist/modes/interactive/components/custom-editor.d.ts +1 -0
  59. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  60. package/dist/modes/interactive/components/custom-editor.js +7 -1
  61. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  62. package/dist/modes/interactive/components/hook-editor.d.ts.map +1 -1
  63. package/dist/modes/interactive/components/hook-editor.js +3 -3
  64. package/dist/modes/interactive/components/hook-editor.js.map +1 -1
  65. package/dist/modes/interactive/components/hook-input.d.ts.map +1 -1
  66. package/dist/modes/interactive/components/hook-input.js +3 -3
  67. package/dist/modes/interactive/components/hook-input.js.map +1 -1
  68. package/dist/modes/interactive/components/hook-selector.d.ts.map +1 -1
  69. package/dist/modes/interactive/components/hook-selector.js +3 -3
  70. package/dist/modes/interactive/components/hook-selector.js.map +1 -1
  71. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  72. package/dist/modes/interactive/components/model-selector.js +3 -3
  73. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  74. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  75. package/dist/modes/interactive/components/oauth-selector.js +3 -3
  76. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  77. package/dist/modes/interactive/components/settings-selector.d.ts +8 -2
  78. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  79. package/dist/modes/interactive/components/settings-selector.js +37 -6
  80. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  81. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  82. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  83. package/dist/modes/interactive/interactive-mode.js +66 -19
  84. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  85. package/dist/modes/print-mode.d.ts.map +1 -1
  86. package/dist/modes/print-mode.js +3 -3
  87. package/dist/modes/print-mode.js.map +1 -1
  88. package/dist/modes/rpc/rpc-client.d.ts +12 -4
  89. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  90. package/dist/modes/rpc/rpc-client.js +18 -6
  91. package/dist/modes/rpc/rpc-client.js.map +1 -1
  92. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  93. package/dist/modes/rpc/rpc-mode.js +21 -12
  94. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  95. package/dist/modes/rpc/rpc-types.d.ts +25 -6
  96. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  97. package/dist/modes/rpc/rpc-types.js.map +1 -1
  98. package/dist/utils/image-resize.d.ts +29 -0
  99. package/dist/utils/image-resize.d.ts.map +1 -0
  100. package/dist/utils/image-resize.js +111 -0
  101. package/dist/utils/image-resize.js.map +1 -0
  102. package/docs/hooks.md +16 -9
  103. package/examples/README.md +6 -0
  104. package/examples/custom-tools/README.md +2 -0
  105. package/examples/hooks/README.md +1 -0
  106. package/examples/hooks/file-trigger.ts +1 -1
  107. package/examples/hooks/todo/index.ts +134 -0
  108. package/package.json +6 -5
  109. package/dist/modes/interactive/components/queue-mode-selector.d.ts +0 -10
  110. package/dist/modes/interactive/components/queue-mode-selector.d.ts.map +0 -1
  111. package/dist/modes/interactive/components/queue-mode-selector.js +0 -42
  112. package/dist/modes/interactive/components/queue-mode-selector.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.32.0] - 2026-01-03
4
+
5
+ ### Breaking Changes
6
+
7
+ - **Queue API replaced with steer/followUp**: The `queueMessage()` method has been split into two methods with different delivery semantics ([#403](https://github.com/badlogic/pi-mono/issues/403)):
8
+ - `steer(text)`: Interrupts the agent mid-run (Enter while streaming). Delivered after current tool execution.
9
+ - `followUp(text)`: Waits until the agent finishes (Alt+Enter while streaming). Delivered only when agent stops.
10
+ - **Settings renamed**: `queueMode` setting renamed to `steeringMode`. Added new `followUpMode` setting. Old settings.json files are migrated automatically.
11
+ - **AgentSession methods renamed**:
12
+ - `queueMessage()` → `steer()` and `followUp()`
13
+ - `queueMode` getter → `steeringMode` and `followUpMode` getters
14
+ - `setQueueMode()` → `setSteeringMode()` and `setFollowUpMode()`
15
+ - `queuedMessageCount` → `pendingMessageCount`
16
+ - `getQueuedMessages()` → `getSteeringMessages()` and `getFollowUpMessages()`
17
+ - `clearQueue()` now returns `{ steering: string[], followUp: string[] }`
18
+ - `hasQueuedMessages()` → `hasPendingMessages()`
19
+ - **Hook API signature changed**: `pi.sendMessage()` second parameter changed from `triggerTurn?: boolean` to `options?: { triggerTurn?, deliverAs? }`. Use `deliverAs: "followUp"` for follow-up delivery. Affects both hooks and internal `sendHookMessage()` method.
20
+ - **RPC API changes**:
21
+ - `queue_message` command → `steer` and `follow_up` commands
22
+ - `set_queue_mode` command → `set_steering_mode` and `set_follow_up_mode` commands
23
+ - `RpcSessionState.queueMode` → `steeringMode` and `followUpMode`
24
+ - **Settings UI**: "Queue mode" setting split into "Steering mode" and "Follow-up mode"
25
+
26
+ ### Added
27
+
28
+ - Configurable double-escape action: choose whether double-escape with empty editor opens `/tree` (default) or `/branch`. Configure via `/settings` or `doubleEscapeAction` in settings.json ([#404](https://github.com/badlogic/pi-mono/issues/404))
29
+ - Vertex AI provider (`google-vertex`): access Gemini models via Google Cloud Vertex AI using Application Default Credentials ([#300](https://github.com/badlogic/pi-mono/pull/300) by [@default-anton](https://github.com/default-anton))
30
+ - Built-in provider overrides in `models.json`: override just `baseUrl` to route a built-in provider through a proxy while keeping all its models, or define `models` to fully replace the provider ([#406](https://github.com/badlogic/pi-mono/pull/406) by [@yevhen](https://github.com/yevhen))
31
+ - Automatic image resizing: images larger than 2000x2000 are resized for better model compatibility. Original dimensions are injected into the prompt. Controlled via `/settings` or `images.autoResize` in settings.json. ([#402](https://github.com/badlogic/pi-mono/pull/402) by [@mitsuhiko](https://github.com/mitsuhiko))
32
+ - Alt+Enter keybind to queue follow-up messages while agent is streaming
33
+ - `Theme` and `ThemeColor` types now exported for hooks using `ctx.ui.custom()`
34
+ - Terminal window title now displays "pi - dirname" to identify which project session you're in ([#407](https://github.com/badlogic/pi-mono/pull/407) by [@kaofelix](https://github.com/kaofelix))
35
+
36
+ ### Changed
37
+
38
+ - Editor component now uses word wrapping instead of character-level wrapping for better readability ([#382](https://github.com/badlogic/pi-mono/pull/382) by [@nickseelert](https://github.com/nickseelert))
39
+
40
+ ### Fixed
41
+
42
+ - `/model` selector now opens instantly instead of waiting for OAuth token refresh. Token refresh is deferred until a model is actually used.
43
+ - Shift+Space, Shift+Backspace, and Shift+Delete now work correctly in Kitty-protocol terminals (Kitty, WezTerm, etc.) instead of being silently ignored ([#411](https://github.com/badlogic/pi-mono/pull/411) by [@nathyong](https://github.com/nathyong))
44
+ - `AgentSession.prompt()` now throws if called while the agent is already streaming, preventing race conditions. Use `steer()` or `followUp()` to queue messages during streaming.
45
+ - Ctrl+C now works like Escape in selector components, so mashing Ctrl+C will eventually close the program ([#400](https://github.com/badlogic/pi-mono/pull/400) by [@mitsuhiko](https://github.com/mitsuhiko))
46
+
3
47
  ## [0.31.1] - 2026-01-02
4
48
 
5
49
  ### Fixed
package/README.md CHANGED
@@ -188,7 +188,7 @@ The agent reads, writes, and edits files, and executes commands via bash.
188
188
 
189
189
  | Command | Description |
190
190
  |---------|-------------|
191
- | `/settings` | Open settings menu (thinking, theme, queue mode, toggles) |
191
+ | `/settings` | Open settings menu (thinking, theme, message delivery modes, toggles) |
192
192
  | `/model` | Switch models mid-session (fuzzy search, arrow keys, Enter to select) |
193
193
  | `/export [file]` | Export session to self-contained HTML |
194
194
  | `/share` | Upload session as secret GitHub gist, get shareable URL (requires `gh` CLI) |
@@ -214,7 +214,11 @@ The agent reads, writes, and edits files, and executes commands via bash.
214
214
 
215
215
  **Multi-line paste:** Pasted content is collapsed to `[paste #N <lines> lines]` but sent in full.
216
216
 
217
- **Message queuing:** Submit messages while the agent is working. They queue and process based on queue mode (configurable via `/settings`). Press Escape to abort and restore queued messages to editor.
217
+ **Message queuing:** Submit messages while the agent is working:
218
+ - **Enter** queues a *steering* message, delivered after current tool execution (interrupts remaining tools)
219
+ - **Alt+Enter** queues a *follow-up* message, delivered only after the agent finishes all work
220
+
221
+ Both modes are configurable via `/settings`: "one-at-a-time" delivers messages one by one waiting for responses, "all" delivers all queued messages at once. Press Escape to abort and restore queued messages to editor.
218
222
 
219
223
  ### Keyboard Shortcuts
220
224
 
@@ -286,6 +290,8 @@ You: What's in this screenshot? /path/to/image.png
286
290
 
287
291
  Supported formats: `.jpg`, `.jpeg`, `.png`, `.gif`, `.webp`
288
292
 
293
+ **Auto-resize:** Images larger than 2000x2000 pixels are automatically resized to fit within this limit for better compatibility with Anthropic models. The original dimensions are noted in the context so the model can map coordinates back if needed. Disable via `images.autoResize: false` in settings.
294
+
289
295
  **Inline rendering:** On terminals that support the Kitty graphics protocol (Kitty, Ghostty, WezTerm) or iTerm2 inline images, images in tool output are rendered inline. On unsupported terminals, a text placeholder is shown instead.
290
296
 
291
297
  Toggle inline images via `/settings` or set `terminal.showImages: false` in settings.
@@ -459,6 +465,37 @@ Add custom models (Ollama, vLLM, LM Studio, etc.) via `~/.pi/agent/models.json`:
459
465
  }
460
466
  ```
461
467
 
468
+ **Overriding built-in providers:**
469
+
470
+ To route a built-in provider (anthropic, openai, google, etc.) through a proxy without redefining all models, just specify the `baseUrl`:
471
+
472
+ ```json
473
+ {
474
+ "providers": {
475
+ "anthropic": {
476
+ "baseUrl": "https://my-proxy.example.com/v1"
477
+ }
478
+ }
479
+ }
480
+ ```
481
+
482
+ All built-in Anthropic models remain available with the new endpoint. Existing OAuth or API key auth continues to work.
483
+
484
+ To fully replace a built-in provider with custom models, include the `models` array:
485
+
486
+ ```json
487
+ {
488
+ "providers": {
489
+ "anthropic": {
490
+ "baseUrl": "https://my-proxy.example.com/v1",
491
+ "apiKey": "ANTHROPIC_API_KEY",
492
+ "api": "anthropic-messages",
493
+ "models": [...]
494
+ }
495
+ }
496
+ }
497
+ ```
498
+
462
499
  **Authorization header:** Set `authHeader: true` to add `Authorization: Bearer <apiKey>` automatically.
463
500
 
464
501
  **OpenAI compatibility (`compat` field):**
@@ -499,7 +536,8 @@ Global `~/.pi/agent/settings.json` stores persistent preferences:
499
536
  "defaultModel": "claude-sonnet-4-20250514",
500
537
  "defaultThinkingLevel": "medium",
501
538
  "enabledModels": ["anthropic/*", "*gpt*", "gemini-2.5-pro:high"],
502
- "queueMode": "one-at-a-time",
539
+ "steeringMode": "one-at-a-time",
540
+ "followUpMode": "one-at-a-time",
503
541
  "shellPath": "C:\\path\\to\\bash.exe",
504
542
  "hideThinkingBlock": false,
505
543
  "collapseChangelog": false,
@@ -519,6 +557,9 @@ Global `~/.pi/agent/settings.json` stores persistent preferences:
519
557
  "terminal": {
520
558
  "showImages": true
521
559
  },
560
+ "images": {
561
+ "autoResize": true
562
+ },
522
563
  "hooks": ["/path/to/hook.ts"],
523
564
  "customTools": ["/path/to/tool.ts"]
524
565
  }
@@ -531,7 +572,8 @@ Global `~/.pi/agent/settings.json` stores persistent preferences:
531
572
  | `defaultModel` | Default model ID | - |
532
573
  | `defaultThinkingLevel` | Thinking level: `off`, `minimal`, `low`, `medium`, `high`, `xhigh` | - |
533
574
  | `enabledModels` | Model patterns for cycling. Supports glob patterns (`github-copilot/*`, `*sonnet*`) and fuzzy matching. Same as `--models` CLI flag | - |
534
- | `queueMode` | Message queue mode: `all` or `one-at-a-time` | `one-at-a-time` |
575
+ | `steeringMode` | Steering message delivery: `all` or `one-at-a-time` | `one-at-a-time` |
576
+ | `followUpMode` | Follow-up message delivery: `all` or `one-at-a-time` | `one-at-a-time` |
535
577
  | `shellPath` | Custom bash path (Windows) | auto-detected |
536
578
  | `hideThinkingBlock` | Hide thinking blocks in output (Ctrl+T to toggle) | `false` |
537
579
  | `collapseChangelog` | Show condensed changelog after update | `false` |
@@ -543,6 +585,9 @@ Global `~/.pi/agent/settings.json` stores persistent preferences:
543
585
  | `retry.maxRetries` | Maximum retry attempts | `3` |
544
586
  | `retry.baseDelayMs` | Base delay for exponential backoff | `2000` |
545
587
  | `terminal.showImages` | Render images inline (supported terminals) | `true` |
588
+ | `images.autoResize` | Auto-resize images to 2000x2000 max for better model compatibility | `true` |
589
+
590
+ | `doubleEscapeAction` | Action for double-escape with empty editor: `tree` or `branch` | `tree` |
546
591
  | `hooks` | Additional hook file paths | `[]` |
547
592
  | `customTools` | Additional custom tool file paths | `[]` |
548
593
 
@@ -689,7 +734,13 @@ export default function (pi: HookAPI) {
689
734
 
690
735
  **Sending messages from hooks:**
691
736
 
692
- Use `pi.sendMessage(message, triggerTurn?)` to inject messages into the session. Messages are persisted as `CustomMessageEntry` and sent to the LLM. If the agent is streaming, the message is queued; otherwise a new agent loop starts if `triggerTurn` is true.
737
+ Use `pi.sendMessage(message, options?)` to inject messages into the session. Messages are persisted as `CustomMessageEntry` and sent to the LLM.
738
+
739
+ Options:
740
+ - `triggerTurn`: If true and agent is idle, starts a new agent turn. Default: false.
741
+ - `deliverAs`: When agent is streaming, controls delivery timing:
742
+ - `"steer"` (default): Delivered after current tool execution, interrupts remaining tools.
743
+ - `"followUp"`: Delivered only after agent finishes all work.
693
744
 
694
745
  ```typescript
695
746
  import * as fs from "node:fs";
@@ -6,6 +6,10 @@ export interface ProcessedFiles {
6
6
  text: string;
7
7
  images: ImageContent[];
8
8
  }
9
+ export interface ProcessFileOptions {
10
+ /** Whether to auto-resize images to 2000x2000 max. Default: true */
11
+ autoResizeImages?: boolean;
12
+ }
9
13
  /** Process @file arguments into text content and image attachments */
10
- export declare function processFileArguments(fileArgs: string[]): Promise<ProcessedFiles>;
14
+ export declare function processFileArguments(fileArgs: string[], options?: ProcessFileOptions): Promise<ProcessedFiles>;
11
15
  //# sourceMappingURL=file-processor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-processor.d.ts","sourceRoot":"","sources":["../../src/cli/file-processor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAMxD,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,sEAAsE;AACtE,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAsDtF","sourcesContent":["/**\n * Process @file CLI arguments into text content and image attachments\n */\n\nimport { access, readFile, stat } from \"node:fs/promises\";\nimport type { ImageContent } from \"@mariozechner/pi-ai\";\nimport chalk from \"chalk\";\nimport { resolve } from \"path\";\nimport { resolveReadPath } from \"../core/tools/path-utils.js\";\nimport { detectSupportedImageMimeTypeFromFile } from \"../utils/mime.js\";\n\nexport interface ProcessedFiles {\n\ttext: string;\n\timages: ImageContent[];\n}\n\n/** Process @file arguments into text content and image attachments */\nexport async function processFileArguments(fileArgs: string[]): Promise<ProcessedFiles> {\n\tlet text = \"\";\n\tconst images: ImageContent[] = [];\n\n\tfor (const fileArg of fileArgs) {\n\t\t// Expand and resolve path (handles ~ expansion and macOS screenshot Unicode spaces)\n\t\tconst absolutePath = resolve(resolveReadPath(fileArg, process.cwd()));\n\n\t\t// Check if file exists\n\t\ttry {\n\t\t\tawait access(absolutePath);\n\t\t} catch {\n\t\t\tconsole.error(chalk.red(`Error: File not found: ${absolutePath}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Check if file is empty\n\t\tconst stats = await stat(absolutePath);\n\t\tif (stats.size === 0) {\n\t\t\t// Skip empty files\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst mimeType = await detectSupportedImageMimeTypeFromFile(absolutePath);\n\n\t\tif (mimeType) {\n\t\t\t// Handle image file\n\t\t\tconst content = await readFile(absolutePath);\n\t\t\tconst base64Content = content.toString(\"base64\");\n\n\t\t\tconst attachment: ImageContent = {\n\t\t\t\ttype: \"image\",\n\t\t\t\tmimeType,\n\t\t\t\tdata: base64Content,\n\t\t\t};\n\n\t\t\timages.push(attachment);\n\n\t\t\t// Add text reference to image\n\t\t\ttext += `<file name=\"${absolutePath}\"></file>\\n`;\n\t\t} else {\n\t\t\t// Handle text file\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(absolutePath, \"utf-8\");\n\t\t\t\ttext += `<file name=\"${absolutePath}\">\\n${content}\\n</file>\\n`;\n\t\t\t} catch (error: unknown) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tconsole.error(chalk.red(`Error: Could not read file ${absolutePath}: ${message}`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { text, images };\n}\n"]}
1
+ {"version":3,"file":"file-processor.d.ts","sourceRoot":"","sources":["../../src/cli/file-processor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAOxD,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IAClC,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,sEAAsE;AACtE,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,cAAc,CAAC,CAwEpH","sourcesContent":["/**\n * Process @file CLI arguments into text content and image attachments\n */\n\nimport { access, readFile, stat } from \"node:fs/promises\";\nimport type { ImageContent } from \"@mariozechner/pi-ai\";\nimport chalk from \"chalk\";\nimport { resolve } from \"path\";\nimport { resolveReadPath } from \"../core/tools/path-utils.js\";\nimport { formatDimensionNote, resizeImage } from \"../utils/image-resize.js\";\nimport { detectSupportedImageMimeTypeFromFile } from \"../utils/mime.js\";\n\nexport interface ProcessedFiles {\n\ttext: string;\n\timages: ImageContent[];\n}\n\nexport interface ProcessFileOptions {\n\t/** Whether to auto-resize images to 2000x2000 max. Default: true */\n\tautoResizeImages?: boolean;\n}\n\n/** Process @file arguments into text content and image attachments */\nexport async function processFileArguments(fileArgs: string[], options?: ProcessFileOptions): Promise<ProcessedFiles> {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tlet text = \"\";\n\tconst images: ImageContent[] = [];\n\n\tfor (const fileArg of fileArgs) {\n\t\t// Expand and resolve path (handles ~ expansion and macOS screenshot Unicode spaces)\n\t\tconst absolutePath = resolve(resolveReadPath(fileArg, process.cwd()));\n\n\t\t// Check if file exists\n\t\ttry {\n\t\t\tawait access(absolutePath);\n\t\t} catch {\n\t\t\tconsole.error(chalk.red(`Error: File not found: ${absolutePath}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Check if file is empty\n\t\tconst stats = await stat(absolutePath);\n\t\tif (stats.size === 0) {\n\t\t\t// Skip empty files\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst mimeType = await detectSupportedImageMimeTypeFromFile(absolutePath);\n\n\t\tif (mimeType) {\n\t\t\t// Handle image file\n\t\t\tconst content = await readFile(absolutePath);\n\t\t\tconst base64Content = content.toString(\"base64\");\n\n\t\t\tlet attachment: ImageContent;\n\t\t\tlet dimensionNote: string | undefined;\n\n\t\t\tif (autoResizeImages) {\n\t\t\t\tconst resized = await resizeImage({ type: \"image\", data: base64Content, mimeType });\n\t\t\t\tdimensionNote = formatDimensionNote(resized);\n\t\t\t\tattachment = {\n\t\t\t\t\ttype: \"image\",\n\t\t\t\t\tmimeType: resized.mimeType,\n\t\t\t\t\tdata: resized.data,\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tattachment = {\n\t\t\t\t\ttype: \"image\",\n\t\t\t\t\tmimeType,\n\t\t\t\t\tdata: base64Content,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\timages.push(attachment);\n\n\t\t\t// Add text reference to image with optional dimension note\n\t\t\tif (dimensionNote) {\n\t\t\t\ttext += `<file name=\"${absolutePath}\">${dimensionNote}</file>\\n`;\n\t\t\t} else {\n\t\t\t\ttext += `<file name=\"${absolutePath}\"></file>\\n`;\n\t\t\t}\n\t\t} else {\n\t\t\t// Handle text file\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(absolutePath, \"utf-8\");\n\t\t\t\ttext += `<file name=\"${absolutePath}\">\\n${content}\\n</file>\\n`;\n\t\t\t} catch (error: unknown) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tconsole.error(chalk.red(`Error: Could not read file ${absolutePath}: ${message}`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { text, images };\n}\n"]}
@@ -5,9 +5,11 @@ import { access, readFile, stat } from "node:fs/promises";
5
5
  import chalk from "chalk";
6
6
  import { resolve } from "path";
7
7
  import { resolveReadPath } from "../core/tools/path-utils.js";
8
+ import { formatDimensionNote, resizeImage } from "../utils/image-resize.js";
8
9
  import { detectSupportedImageMimeTypeFromFile } from "../utils/mime.js";
9
10
  /** Process @file arguments into text content and image attachments */
10
- export async function processFileArguments(fileArgs) {
11
+ export async function processFileArguments(fileArgs, options) {
12
+ const autoResizeImages = options?.autoResizeImages ?? true;
11
13
  let text = "";
12
14
  const images = [];
13
15
  for (const fileArg of fileArgs) {
@@ -32,14 +34,32 @@ export async function processFileArguments(fileArgs) {
32
34
  // Handle image file
33
35
  const content = await readFile(absolutePath);
34
36
  const base64Content = content.toString("base64");
35
- const attachment = {
36
- type: "image",
37
- mimeType,
38
- data: base64Content,
39
- };
37
+ let attachment;
38
+ let dimensionNote;
39
+ if (autoResizeImages) {
40
+ const resized = await resizeImage({ type: "image", data: base64Content, mimeType });
41
+ dimensionNote = formatDimensionNote(resized);
42
+ attachment = {
43
+ type: "image",
44
+ mimeType: resized.mimeType,
45
+ data: resized.data,
46
+ };
47
+ }
48
+ else {
49
+ attachment = {
50
+ type: "image",
51
+ mimeType,
52
+ data: base64Content,
53
+ };
54
+ }
40
55
  images.push(attachment);
41
- // Add text reference to image
42
- text += `<file name="${absolutePath}"></file>\n`;
56
+ // Add text reference to image with optional dimension note
57
+ if (dimensionNote) {
58
+ text += `<file name="${absolutePath}">${dimensionNote}</file>\n`;
59
+ }
60
+ else {
61
+ text += `<file name="${absolutePath}"></file>\n`;
62
+ }
43
63
  }
44
64
  else {
45
65
  // Handle text file
@@ -1 +1 @@
1
- {"version":3,"file":"file-processor.js","sourceRoot":"","sources":["../../src/cli/file-processor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,oCAAoC,EAAE,MAAM,kBAAkB,CAAC;AAOxE,sEAAsE;AACtE,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,QAAkB,EAA2B;IACvF,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,MAAM,MAAM,GAAmB,EAAE,CAAC;IAElC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAChC,oFAAoF;QACpF,MAAM,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAEtE,uBAAuB;QACvB,IAAI,CAAC;YACJ,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,0BAA0B,YAAY,EAAE,CAAC,CAAC,CAAC;YACnE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;QAED,yBAAyB;QACzB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC;QACvC,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACtB,mBAAmB;YACnB,SAAS;QACV,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,oCAAoC,CAAC,YAAY,CAAC,CAAC;QAE1E,IAAI,QAAQ,EAAE,CAAC;YACd,oBAAoB;YACpB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;YAC7C,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAEjD,MAAM,UAAU,GAAiB;gBAChC,IAAI,EAAE,OAAO;gBACb,QAAQ;gBACR,IAAI,EAAE,aAAa;aACnB,CAAC;YAEF,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAExB,8BAA8B;YAC9B,IAAI,IAAI,eAAe,YAAY,aAAa,CAAC;QAClD,CAAC;aAAM,CAAC;YACP,mBAAmB;YACnB,IAAI,CAAC;gBACJ,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;gBACtD,IAAI,IAAI,eAAe,YAAY,OAAO,OAAO,aAAa,CAAC;YAChE,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACzB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACvE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,8BAA8B,YAAY,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC;gBACnF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjB,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAAA,CACxB","sourcesContent":["/**\n * Process @file CLI arguments into text content and image attachments\n */\n\nimport { access, readFile, stat } from \"node:fs/promises\";\nimport type { ImageContent } from \"@mariozechner/pi-ai\";\nimport chalk from \"chalk\";\nimport { resolve } from \"path\";\nimport { resolveReadPath } from \"../core/tools/path-utils.js\";\nimport { detectSupportedImageMimeTypeFromFile } from \"../utils/mime.js\";\n\nexport interface ProcessedFiles {\n\ttext: string;\n\timages: ImageContent[];\n}\n\n/** Process @file arguments into text content and image attachments */\nexport async function processFileArguments(fileArgs: string[]): Promise<ProcessedFiles> {\n\tlet text = \"\";\n\tconst images: ImageContent[] = [];\n\n\tfor (const fileArg of fileArgs) {\n\t\t// Expand and resolve path (handles ~ expansion and macOS screenshot Unicode spaces)\n\t\tconst absolutePath = resolve(resolveReadPath(fileArg, process.cwd()));\n\n\t\t// Check if file exists\n\t\ttry {\n\t\t\tawait access(absolutePath);\n\t\t} catch {\n\t\t\tconsole.error(chalk.red(`Error: File not found: ${absolutePath}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Check if file is empty\n\t\tconst stats = await stat(absolutePath);\n\t\tif (stats.size === 0) {\n\t\t\t// Skip empty files\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst mimeType = await detectSupportedImageMimeTypeFromFile(absolutePath);\n\n\t\tif (mimeType) {\n\t\t\t// Handle image file\n\t\t\tconst content = await readFile(absolutePath);\n\t\t\tconst base64Content = content.toString(\"base64\");\n\n\t\t\tconst attachment: ImageContent = {\n\t\t\t\ttype: \"image\",\n\t\t\t\tmimeType,\n\t\t\t\tdata: base64Content,\n\t\t\t};\n\n\t\t\timages.push(attachment);\n\n\t\t\t// Add text reference to image\n\t\t\ttext += `<file name=\"${absolutePath}\"></file>\\n`;\n\t\t} else {\n\t\t\t// Handle text file\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(absolutePath, \"utf-8\");\n\t\t\t\ttext += `<file name=\"${absolutePath}\">\\n${content}\\n</file>\\n`;\n\t\t\t} catch (error: unknown) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tconsole.error(chalk.red(`Error: Could not read file ${absolutePath}: ${message}`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { text, images };\n}\n"]}
1
+ {"version":3,"file":"file-processor.js","sourceRoot":"","sources":["../../src/cli/file-processor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Process @file CLI arguments into text content and image attachments\n */\n\nimport { access, readFile, stat } from \"node:fs/promises\";\nimport type { ImageContent } from \"@mariozechner/pi-ai\";\nimport chalk from \"chalk\";\nimport { resolve } from \"path\";\nimport { resolveReadPath } from \"../core/tools/path-utils.js\";\nimport { formatDimensionNote, resizeImage } from \"../utils/image-resize.js\";\nimport { detectSupportedImageMimeTypeFromFile } from \"../utils/mime.js\";\n\nexport interface ProcessedFiles {\n\ttext: string;\n\timages: ImageContent[];\n}\n\nexport interface ProcessFileOptions {\n\t/** Whether to auto-resize images to 2000x2000 max. Default: true */\n\tautoResizeImages?: boolean;\n}\n\n/** Process @file arguments into text content and image attachments */\nexport async function processFileArguments(fileArgs: string[], options?: ProcessFileOptions): Promise<ProcessedFiles> {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tlet text = \"\";\n\tconst images: ImageContent[] = [];\n\n\tfor (const fileArg of fileArgs) {\n\t\t// Expand and resolve path (handles ~ expansion and macOS screenshot Unicode spaces)\n\t\tconst absolutePath = resolve(resolveReadPath(fileArg, process.cwd()));\n\n\t\t// Check if file exists\n\t\ttry {\n\t\t\tawait access(absolutePath);\n\t\t} catch {\n\t\t\tconsole.error(chalk.red(`Error: File not found: ${absolutePath}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Check if file is empty\n\t\tconst stats = await stat(absolutePath);\n\t\tif (stats.size === 0) {\n\t\t\t// Skip empty files\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst mimeType = await detectSupportedImageMimeTypeFromFile(absolutePath);\n\n\t\tif (mimeType) {\n\t\t\t// Handle image file\n\t\t\tconst content = await readFile(absolutePath);\n\t\t\tconst base64Content = content.toString(\"base64\");\n\n\t\t\tlet attachment: ImageContent;\n\t\t\tlet dimensionNote: string | undefined;\n\n\t\t\tif (autoResizeImages) {\n\t\t\t\tconst resized = await resizeImage({ type: \"image\", data: base64Content, mimeType });\n\t\t\t\tdimensionNote = formatDimensionNote(resized);\n\t\t\t\tattachment = {\n\t\t\t\t\ttype: \"image\",\n\t\t\t\t\tmimeType: resized.mimeType,\n\t\t\t\t\tdata: resized.data,\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tattachment = {\n\t\t\t\t\ttype: \"image\",\n\t\t\t\t\tmimeType,\n\t\t\t\t\tdata: base64Content,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\timages.push(attachment);\n\n\t\t\t// Add text reference to image with optional dimension note\n\t\t\tif (dimensionNote) {\n\t\t\t\ttext += `<file name=\"${absolutePath}\">${dimensionNote}</file>\\n`;\n\t\t\t} else {\n\t\t\t\ttext += `<file name=\"${absolutePath}\"></file>\\n`;\n\t\t\t}\n\t\t} else {\n\t\t\t// Handle text file\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(absolutePath, \"utf-8\");\n\t\t\t\ttext += `<file name=\"${absolutePath}\">\\n${content}\\n</file>\\n`;\n\t\t\t} catch (error: unknown) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tconsole.error(chalk.red(`Error: Could not read file ${absolutePath}: ${message}`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { text, images };\n}\n"]}
@@ -105,7 +105,10 @@ export declare class AgentSession {
105
105
  private _fileCommands;
106
106
  private _unsubscribeAgent?;
107
107
  private _eventListeners;
108
- private _queuedMessages;
108
+ /** Tracks pending steering messages for UI display. Removed when delivered. */
109
+ private _steeringMessages;
110
+ /** Tracks pending follow-up messages for UI display. Removed when delivered. */
111
+ private _followUpMessages;
109
112
  private _compactionAbortController;
110
113
  private _autoCompactionAbortController;
111
114
  private _branchSummaryAbortController;
@@ -169,8 +172,10 @@ export declare class AgentSession {
169
172
  get isCompacting(): boolean;
170
173
  /** All messages including custom types like BashExecutionMessage */
171
174
  get messages(): AgentMessage[];
172
- /** Current queue mode */
173
- get queueMode(): "all" | "one-at-a-time";
175
+ /** Current steering mode */
176
+ get steeringMode(): "all" | "one-at-a-time";
177
+ /** Current follow-up mode */
178
+ get followUpMode(): "all" | "one-at-a-time";
174
179
  /** Current session file path, or undefined if sessions are disabled */
175
180
  get sessionFile(): string | undefined;
176
181
  /** Current session ID */
@@ -192,10 +197,15 @@ export declare class AgentSession {
192
197
  prompt(text: string, options?: PromptOptions): Promise<void>;
193
198
  private _tryExecuteHookCommand;
194
199
  /**
195
- * Queue a message to be sent after the current response completes.
196
- * Use when agent is currently streaming.
200
+ * Queue a steering message to interrupt the agent mid-run.
201
+ * Delivered after current tool execution, skips remaining tools.
197
202
  */
198
- queueMessage(text: string): Promise<void>;
203
+ steer(text: string): Promise<void>;
204
+ /**
205
+ * Queue a follow-up message to be processed after the agent finishes.
206
+ * Delivered only when agent has no more tool calls or steering messages.
207
+ */
208
+ followUp(text: string): Promise<void>;
199
209
  /**
200
210
  * Send a hook message to the session. Creates a CustomMessageEntry.
201
211
  *
@@ -205,18 +215,28 @@ export declare class AgentSession {
205
215
  * - Not streaming + no trigger: appends to state/session, no turn
206
216
  *
207
217
  * @param message Hook message with customType, content, display, details
208
- * @param triggerTurn If true and not streaming, triggers a new LLM turn
218
+ * @param options.triggerTurn If true and not streaming, triggers a new LLM turn
219
+ * @param options.deliverAs When streaming, use "steer" (default) for immediate or "followUp" to wait
209
220
  */
210
- sendHookMessage<T = unknown>(message: Pick<HookMessage<T>, "customType" | "content" | "display" | "details">, triggerTurn?: boolean): Promise<void>;
221
+ sendHookMessage<T = unknown>(message: Pick<HookMessage<T>, "customType" | "content" | "display" | "details">, options?: {
222
+ triggerTurn?: boolean;
223
+ deliverAs?: "steer" | "followUp";
224
+ }): Promise<void>;
211
225
  /**
212
- * Clear queued messages and return them.
226
+ * Clear all queued messages and return them.
213
227
  * Useful for restoring to editor when user aborts.
228
+ * @returns Object with steering and followUp arrays
214
229
  */
215
- clearQueue(): string[];
216
- /** Number of messages currently queued */
217
- get queuedMessageCount(): number;
218
- /** Get queued messages (read-only) */
219
- getQueuedMessages(): readonly string[];
230
+ clearQueue(): {
231
+ steering: string[];
232
+ followUp: string[];
233
+ };
234
+ /** Number of pending messages (includes both steering and follow-up) */
235
+ get pendingMessageCount(): number;
236
+ /** Get pending steering messages (read-only) */
237
+ getSteeringMessages(): readonly string[];
238
+ /** Get pending follow-up messages (read-only) */
239
+ getFollowUpMessages(): readonly string[];
220
240
  get skillsSettings(): Required<SkillsSettings> | undefined;
221
241
  /**
222
242
  * Abort current operation and wait for agent to become idle.
@@ -273,10 +293,15 @@ export declare class AgentSession {
273
293
  */
274
294
  supportsThinking(): boolean;
275
295
  /**
276
- * Set message queue mode.
296
+ * Set steering message mode.
297
+ * Saves to settings.
298
+ */
299
+ setSteeringMode(mode: "all" | "one-at-a-time"): void;
300
+ /**
301
+ * Set follow-up message mode.
277
302
  * Saves to settings.
278
303
  */
279
- setQueueMode(mode: "all" | "one-at-a-time"): void;
304
+ setFollowUpMode(mode: "all" | "one-at-a-time"): void;
280
305
  /**
281
306
  * Manually compact the session context.
282
307
  * Aborts current agent operation first.