@oh-my-pi/pi-coding-agent 13.19.0 → 14.0.2

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 (202) hide show
  1. package/CHANGELOG.md +266 -1
  2. package/package.json +86 -20
  3. package/scripts/format-prompts.ts +2 -2
  4. package/src/autoresearch/apply-contract-to-state.ts +24 -0
  5. package/src/autoresearch/contract.ts +0 -44
  6. package/src/autoresearch/dashboard.ts +1 -2
  7. package/src/autoresearch/git.ts +91 -0
  8. package/src/autoresearch/helpers.ts +49 -0
  9. package/src/autoresearch/index.ts +28 -187
  10. package/src/autoresearch/prompt.md +26 -9
  11. package/src/autoresearch/state.ts +0 -6
  12. package/src/autoresearch/tools/init-experiment.ts +202 -117
  13. package/src/autoresearch/tools/log-experiment.ts +83 -125
  14. package/src/autoresearch/tools/run-experiment.ts +48 -10
  15. package/src/autoresearch/types.ts +2 -2
  16. package/src/capability/index.ts +4 -2
  17. package/src/cli/file-processor.ts +3 -3
  18. package/src/cli/grep-cli.ts +8 -8
  19. package/src/cli/grievances-cli.ts +78 -0
  20. package/src/cli/read-cli.ts +67 -0
  21. package/src/cli/setup-cli.ts +4 -4
  22. package/src/cli/update-cli.ts +3 -3
  23. package/src/cli.ts +2 -0
  24. package/src/commands/grep.ts +6 -1
  25. package/src/commands/grievances.ts +20 -0
  26. package/src/commands/read.ts +33 -0
  27. package/src/commit/agentic/agent.ts +5 -5
  28. package/src/commit/agentic/index.ts +3 -4
  29. package/src/commit/agentic/tools/analyze-file.ts +3 -3
  30. package/src/commit/agentic/validation.ts +1 -1
  31. package/src/commit/analysis/conventional.ts +4 -4
  32. package/src/commit/analysis/summary.ts +3 -3
  33. package/src/commit/changelog/generate.ts +4 -4
  34. package/src/commit/map-reduce/map-phase.ts +4 -4
  35. package/src/commit/map-reduce/reduce-phase.ts +4 -4
  36. package/src/commit/pipeline.ts +3 -4
  37. package/src/config/prompt-templates.ts +44 -226
  38. package/src/config/resolve-config-value.ts +4 -2
  39. package/src/config/settings-schema.ts +54 -2
  40. package/src/config/settings.ts +25 -26
  41. package/src/dap/client.ts +674 -0
  42. package/src/dap/config.ts +150 -0
  43. package/src/dap/defaults.json +211 -0
  44. package/src/dap/index.ts +4 -0
  45. package/src/dap/session.ts +1255 -0
  46. package/src/dap/types.ts +600 -0
  47. package/src/debug/log-viewer.ts +3 -2
  48. package/src/discovery/builtin.ts +1 -2
  49. package/src/discovery/codex.ts +2 -2
  50. package/src/discovery/github.ts +2 -1
  51. package/src/discovery/helpers.ts +2 -2
  52. package/src/discovery/opencode.ts +2 -2
  53. package/src/edit/diff.ts +818 -0
  54. package/src/edit/index.ts +309 -0
  55. package/src/edit/line-hash.ts +67 -0
  56. package/src/edit/modes/chunk.ts +454 -0
  57. package/src/{patch → edit/modes}/hashline.ts +741 -361
  58. package/src/{patch/applicator.ts → edit/modes/patch.ts} +420 -117
  59. package/src/{patch/fuzzy.ts → edit/modes/replace.ts} +519 -197
  60. package/src/{patch → edit}/normalize.ts +97 -76
  61. package/src/{patch/shared.ts → edit/renderer.ts} +181 -108
  62. package/src/exec/bash-executor.ts +4 -2
  63. package/src/exec/idle-timeout-watchdog.ts +126 -0
  64. package/src/exec/non-interactive-env.ts +5 -0
  65. package/src/extensibility/custom-commands/bundled/ci-green/index.ts +2 -2
  66. package/src/extensibility/custom-commands/bundled/review/index.ts +2 -2
  67. package/src/extensibility/custom-commands/loader.ts +1 -2
  68. package/src/extensibility/custom-tools/loader.ts +34 -11
  69. package/src/extensibility/extensions/loader.ts +9 -4
  70. package/src/extensibility/extensions/runner.ts +24 -1
  71. package/src/extensibility/extensions/types.ts +1 -1
  72. package/src/extensibility/hooks/loader.ts +5 -6
  73. package/src/extensibility/hooks/types.ts +1 -1
  74. package/src/extensibility/plugins/doctor.ts +2 -1
  75. package/src/extensibility/slash-commands.ts +3 -7
  76. package/src/index.ts +2 -1
  77. package/src/internal-urls/docs-index.generated.ts +11 -11
  78. package/src/ipy/executor.ts +58 -17
  79. package/src/ipy/gateway-coordinator.ts +6 -4
  80. package/src/ipy/kernel.ts +45 -22
  81. package/src/ipy/runtime.ts +2 -2
  82. package/src/lsp/client.ts +7 -4
  83. package/src/lsp/clients/lsp-linter-client.ts +4 -4
  84. package/src/lsp/config.ts +2 -2
  85. package/src/lsp/defaults.json +688 -154
  86. package/src/lsp/index.ts +234 -45
  87. package/src/lsp/lspmux.ts +2 -2
  88. package/src/lsp/startup-events.ts +13 -0
  89. package/src/lsp/types.ts +12 -1
  90. package/src/lsp/utils.ts +8 -1
  91. package/src/main.ts +102 -46
  92. package/src/memories/index.ts +4 -5
  93. package/src/modes/acp/acp-agent.ts +563 -163
  94. package/src/modes/acp/acp-event-mapper.ts +9 -1
  95. package/src/modes/acp/acp-mode.ts +4 -2
  96. package/src/modes/components/agent-dashboard.ts +3 -4
  97. package/src/modes/components/diff.ts +6 -7
  98. package/src/modes/components/read-tool-group.ts +6 -12
  99. package/src/modes/components/settings-defs.ts +5 -0
  100. package/src/modes/components/tool-execution.ts +1 -1
  101. package/src/modes/components/welcome.ts +1 -1
  102. package/src/modes/controllers/btw-controller.ts +2 -2
  103. package/src/modes/controllers/command-controller.ts +3 -2
  104. package/src/modes/controllers/input-controller.ts +12 -8
  105. package/src/modes/index.ts +20 -2
  106. package/src/modes/interactive-mode.ts +94 -37
  107. package/src/modes/rpc/host-tools.ts +186 -0
  108. package/src/modes/rpc/rpc-client.ts +178 -13
  109. package/src/modes/rpc/rpc-mode.ts +73 -3
  110. package/src/modes/rpc/rpc-types.ts +53 -1
  111. package/src/modes/theme/theme.ts +80 -8
  112. package/src/modes/types.ts +2 -2
  113. package/src/prompts/system/system-prompt.md +2 -1
  114. package/src/prompts/tools/chunk-edit.md +219 -0
  115. package/src/prompts/tools/debug.md +43 -0
  116. package/src/prompts/tools/grep.md +3 -0
  117. package/src/prompts/tools/lsp.md +5 -5
  118. package/src/prompts/tools/read-chunk.md +17 -0
  119. package/src/prompts/tools/read.md +19 -5
  120. package/src/sdk.ts +190 -154
  121. package/src/secrets/obfuscator.ts +1 -1
  122. package/src/session/agent-session.ts +306 -256
  123. package/src/session/agent-storage.ts +12 -12
  124. package/src/session/compaction/branch-summarization.ts +3 -3
  125. package/src/session/compaction/compaction.ts +5 -6
  126. package/src/session/compaction/utils.ts +3 -3
  127. package/src/session/history-storage.ts +62 -19
  128. package/src/session/messages.ts +3 -3
  129. package/src/session/session-dump-format.ts +203 -0
  130. package/src/session/session-storage.ts +4 -2
  131. package/src/session/streaming-output.ts +1 -1
  132. package/src/session/tool-choice-queue.ts +213 -0
  133. package/src/slash-commands/builtin-registry.ts +56 -8
  134. package/src/ssh/connection-manager.ts +2 -2
  135. package/src/ssh/sshfs-mount.ts +5 -5
  136. package/src/stt/downloader.ts +4 -4
  137. package/src/stt/recorder.ts +4 -4
  138. package/src/stt/transcriber.ts +2 -2
  139. package/src/system-prompt.ts +21 -13
  140. package/src/task/agents.ts +5 -6
  141. package/src/task/commands.ts +2 -5
  142. package/src/task/executor.ts +4 -4
  143. package/src/task/index.ts +3 -4
  144. package/src/task/template.ts +2 -2
  145. package/src/task/worktree.ts +4 -4
  146. package/src/tools/ask.ts +2 -3
  147. package/src/tools/ast-edit.ts +7 -7
  148. package/src/tools/ast-grep.ts +7 -7
  149. package/src/tools/auto-generated-guard.ts +36 -41
  150. package/src/tools/await-tool.ts +2 -2
  151. package/src/tools/bash.ts +5 -23
  152. package/src/tools/browser.ts +4 -5
  153. package/src/tools/calculator.ts +2 -3
  154. package/src/tools/cancel-job.ts +2 -2
  155. package/src/tools/checkpoint.ts +3 -3
  156. package/src/tools/debug.ts +1007 -0
  157. package/src/tools/exit-plan-mode.ts +2 -3
  158. package/src/tools/fetch.ts +67 -3
  159. package/src/tools/find.ts +4 -5
  160. package/src/tools/fs-cache-invalidation.ts +5 -0
  161. package/src/tools/gemini-image.ts +13 -5
  162. package/src/tools/gh.ts +10 -11
  163. package/src/tools/grep.ts +57 -9
  164. package/src/tools/index.ts +44 -22
  165. package/src/tools/inspect-image.ts +4 -4
  166. package/src/tools/output-meta.ts +1 -1
  167. package/src/tools/python.ts +19 -6
  168. package/src/tools/read.ts +198 -67
  169. package/src/tools/render-mermaid.ts +2 -3
  170. package/src/tools/render-utils.ts +20 -6
  171. package/src/tools/renderers.ts +3 -1
  172. package/src/tools/report-tool-issue.ts +80 -0
  173. package/src/tools/resolve.ts +70 -39
  174. package/src/tools/search-tool-bm25.ts +2 -2
  175. package/src/tools/ssh.ts +2 -2
  176. package/src/tools/todo-write.ts +2 -2
  177. package/src/tools/tool-timeouts.ts +1 -0
  178. package/src/tools/write.ts +5 -6
  179. package/src/tui/tree-list.ts +3 -1
  180. package/src/utils/clipboard.ts +80 -0
  181. package/src/utils/commit-message-generator.ts +2 -3
  182. package/src/utils/edit-mode.ts +49 -0
  183. package/src/utils/file-display-mode.ts +6 -5
  184. package/src/utils/file-mentions.ts +8 -7
  185. package/src/utils/git.ts +4 -4
  186. package/src/utils/image-loading.ts +98 -0
  187. package/src/utils/title-generator.ts +2 -3
  188. package/src/utils/tools-manager.ts +6 -6
  189. package/src/web/scrapers/choosealicense.ts +1 -1
  190. package/src/web/search/index.ts +3 -3
  191. package/src/autoresearch/command-initialize.md +0 -34
  192. package/src/patch/diff.ts +0 -433
  193. package/src/patch/index.ts +0 -888
  194. package/src/patch/parser.ts +0 -532
  195. package/src/patch/types.ts +0 -292
  196. package/src/prompts/agents/oracle.md +0 -77
  197. package/src/tools/pending-action.ts +0 -49
  198. package/src/utils/child-process.ts +0 -88
  199. package/src/utils/frontmatter.ts +0 -117
  200. package/src/utils/image-input.ts +0 -274
  201. package/src/utils/mime.ts +0 -53
  202. package/src/utils/prompt-format.ts +0 -170
@@ -1,8 +1,7 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
3
- import { isEnoent } from "@oh-my-pi/pi-utils";
3
+ import { isEnoent, prompt } from "@oh-my-pi/pi-utils";
4
4
  import { type Static, Type } from "@sinclair/typebox";
5
- import { renderPromptTemplate } from "../config/prompt-templates";
6
5
  import exitPlanModeDescription from "../prompts/tools/exit-plan-mode.md" with { type: "text" };
7
6
  import type { ToolSession } from ".";
8
7
  import { resolvePlanPath } from "./plan-mode-guard";
@@ -49,7 +48,7 @@ export class ExitPlanModeTool implements AgentTool<typeof exitPlanModeSchema, Ex
49
48
  readonly concurrency = "exclusive";
50
49
 
51
50
  constructor(private readonly session: ToolSession) {
52
- this.description = renderPromptTemplate(exitPlanModeDescription);
51
+ this.description = prompt.render(exitPlanModeDescription);
53
52
  }
54
53
 
55
54
  async execute(
@@ -4,7 +4,7 @@ import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
4
4
  import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai";
5
5
  import { htmlToMarkdown } from "@oh-my-pi/pi-natives";
6
6
  import { type Component, Text } from "@oh-my-pi/pi-tui";
7
- import { ptree, truncate } from "@oh-my-pi/pi-utils";
7
+ import { $which, ptree, truncate } from "@oh-my-pi/pi-utils";
8
8
  import { parseHTML } from "linkedom";
9
9
  import type { Settings } from "../config/settings";
10
10
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
@@ -23,7 +23,7 @@ import { convertWithMarkit, fetchBinary } from "../web/scrapers/utils";
23
23
  import { applyListLimit } from "./list-limit";
24
24
  import { formatStyledArtifactReference, type OutputMeta } from "./output-meta";
25
25
  import { formatExpandHint, getDomain } from "./render-utils";
26
- import { ToolAbortError } from "./tool-errors";
26
+ import { ToolAbortError, ToolError } from "./tool-errors";
27
27
  import { toolResult } from "./tool-result";
28
28
  import { clampTimeout } from "./tool-timeouts";
29
29
 
@@ -94,7 +94,7 @@ const MAX_INLINE_IMAGE_OUTPUT_BYTES = 0.75 * 1024 * 1024;
94
94
  * Check if a command exists (cross-platform)
95
95
  */
96
96
  function hasCommand(cmd: string): boolean {
97
- return Boolean(Bun.which(cmd));
97
+ return Boolean($which(cmd));
98
98
  }
99
99
 
100
100
  /**
@@ -137,6 +137,70 @@ export function isReadableUrlPath(value: string): boolean {
137
137
  return /^https?:\/\//i.test(value) || /^www\./i.test(value);
138
138
  }
139
139
 
140
+ const URL_LINE_RANGE_RE = /^L(\d+)(?:-L?(\d+))?$/i;
141
+
142
+ export interface ParsedReadUrlTarget {
143
+ path: string;
144
+ raw: boolean;
145
+ offset?: number;
146
+ limit?: number;
147
+ }
148
+
149
+ export function parseReadUrlTarget(readPath: string, sel?: string): ParsedReadUrlTarget | null {
150
+ const embedded = sel ? undefined : tryExtractEmbeddedUrlSelector(readPath);
151
+ const urlPath = embedded?.path ?? readPath;
152
+ if (!isReadableUrlPath(urlPath)) {
153
+ return null;
154
+ }
155
+
156
+ const selector = sel ?? embedded?.sel;
157
+ const raw = selector === "raw";
158
+ const lineMatch = selector ? URL_LINE_RANGE_RE.exec(selector) : null;
159
+ if (lineMatch) {
160
+ const startLine = Number.parseInt(lineMatch[1]!, 10);
161
+ if (startLine < 1) {
162
+ throw new ToolError("L0 is invalid; lines are 1-indexed. Use sel=L1.");
163
+ }
164
+ const endLine = lineMatch[2] ? Number.parseInt(lineMatch[2], 10) : undefined;
165
+ if (endLine !== undefined && endLine < startLine) {
166
+ throw new ToolError(`Invalid range L${startLine}-L${endLine}: end must be >= start.`);
167
+ }
168
+ return {
169
+ path: urlPath,
170
+ raw: false,
171
+ offset: startLine,
172
+ limit: endLine !== undefined ? endLine - startLine + 1 : undefined,
173
+ };
174
+ }
175
+
176
+ return { path: urlPath, raw };
177
+ }
178
+
179
+ function tryExtractEmbeddedUrlSelector(readPath: string): { path: string; sel?: string } | null {
180
+ const lastColonIndex = readPath.lastIndexOf(":");
181
+ if (lastColonIndex <= 0) {
182
+ return null;
183
+ }
184
+
185
+ const candidateSelector = readPath.slice(lastColonIndex + 1);
186
+ const isEmbeddedSelector = candidateSelector === "raw" || URL_LINE_RANGE_RE.test(candidateSelector);
187
+ if (!isEmbeddedSelector) {
188
+ return null;
189
+ }
190
+
191
+ const basePath = readPath.slice(0, lastColonIndex);
192
+ if (!isReadableUrlPath(basePath)) {
193
+ return null;
194
+ }
195
+
196
+ try {
197
+ new URL(basePath.startsWith("http://") || basePath.startsWith("https://") ? basePath : `https://${basePath}`);
198
+ return { path: basePath, sel: candidateSelector };
199
+ } catch {
200
+ return null;
201
+ }
202
+ }
203
+
140
204
  /**
141
205
  * Normalize MIME type (lowercase, strip charset/params)
142
206
  */
package/src/tools/find.ts CHANGED
@@ -4,10 +4,9 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
4
4
  import { FileType, type GlobMatch, glob } from "@oh-my-pi/pi-natives";
5
5
  import type { Component } from "@oh-my-pi/pi-tui";
6
6
  import { Text } from "@oh-my-pi/pi-tui";
7
- import { isEnoent, untilAborted } from "@oh-my-pi/pi-utils";
7
+ import { isEnoent, prompt, untilAborted } from "@oh-my-pi/pi-utils";
8
8
  import type { Static } from "@sinclair/typebox";
9
9
  import { Type } from "@sinclair/typebox";
10
- import { renderPromptTemplate } from "../config/prompt-templates";
11
10
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
12
11
  import type { Theme } from "../modes/theme/theme";
13
12
  import findDescription from "../prompts/tools/find.md" with { type: "text" };
@@ -86,7 +85,7 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
86
85
  options?: FindToolOptions,
87
86
  ) {
88
87
  this.#customOps = options?.operations;
89
- this.description = renderPromptTemplate(findDescription);
88
+ this.description = prompt.render(findDescription);
90
89
  }
91
90
 
92
91
  async execute(
@@ -234,8 +233,8 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
234
233
  });
235
234
  };
236
235
  const onMatch = onUpdate
237
- ? (match: GlobMatch | null) => {
238
- if (signal?.aborted || !match) return;
236
+ ? (err: Error | null, match: GlobMatch | null) => {
237
+ if (err || signal?.aborted || !match) return;
239
238
  let relativePath = match.path;
240
239
  if (!relativePath) return;
241
240
  if (match.fileType === FileType.Dir && !relativePath.endsWith("/")) {
@@ -1,10 +1,12 @@
1
1
  import { invalidateFsScanCache } from "@oh-my-pi/pi-natives";
2
+ import { invalidateChunkCache } from "../edit/modes/chunk";
2
3
 
3
4
  /**
4
5
  * Invalidate shared filesystem scan caches after a content write/update.
5
6
  */
6
7
  export function invalidateFsScanAfterWrite(path: string): void {
7
8
  invalidateFsScanCache(path);
9
+ invalidateChunkCache(path);
8
10
  }
9
11
 
10
12
  /**
@@ -12,6 +14,7 @@ export function invalidateFsScanAfterWrite(path: string): void {
12
14
  */
13
15
  export function invalidateFsScanAfterDelete(path: string): void {
14
16
  invalidateFsScanCache(path);
17
+ invalidateChunkCache(path);
15
18
  }
16
19
 
17
20
  /**
@@ -22,7 +25,9 @@ export function invalidateFsScanAfterDelete(path: string): void {
22
25
  */
23
26
  export function invalidateFsScanAfterRename(oldPath: string, newPath: string): void {
24
27
  invalidateFsScanCache(oldPath);
28
+ invalidateChunkCache(oldPath);
25
29
  if (newPath !== oldPath) {
26
30
  invalidateFsScanCache(newPath);
31
+ invalidateChunkCache(newPath);
27
32
  }
28
33
  }
@@ -1,13 +1,20 @@
1
1
  import * as os from "node:os";
2
2
  import * as path from "node:path";
3
3
  import { getAntigravityHeaders, getEnvApiKey, StringEnum } from "@oh-my-pi/pi-ai";
4
- import { $env, isEnoent, ptree, readSseJson, Snowflake, untilAborted } from "@oh-my-pi/pi-utils";
4
+ import {
5
+ $env,
6
+ isEnoent,
7
+ parseImageMetadata,
8
+ prompt,
9
+ ptree,
10
+ readSseJson,
11
+ Snowflake,
12
+ untilAborted,
13
+ } from "@oh-my-pi/pi-utils";
5
14
  import { type Static, Type } from "@sinclair/typebox";
6
15
  import type { ModelRegistry } from "../config/model-registry";
7
- import { renderPromptTemplate } from "../config/prompt-templates";
8
16
  import type { CustomTool } from "../extensibility/custom-tools/types";
9
17
  import geminiImageDescription from "../prompts/tools/gemini-image.md" with { type: "text" };
10
- import { detectSupportedImageMimeTypeFromFile } from "../utils/mime";
11
18
  import { resolveReadPath } from "./path-utils";
12
19
 
13
20
  const DEFAULT_MODEL = "gemini-3-pro-image-preview";
@@ -417,7 +424,8 @@ async function loadImageFromPath(imagePath: string, cwd: string): Promise<Inline
417
424
  throw new Error(`Image file too large: ${imagePath}`);
418
425
  }
419
426
 
420
- const mimeType = await detectSupportedImageMimeTypeFromFile(resolved);
427
+ const metadata = parseImageMetadata(buffer);
428
+ const mimeType = metadata?.mimeType;
421
429
  if (!mimeType) {
422
430
  throw new Error(`Unsupported image type: ${imagePath}`);
423
431
  }
@@ -599,7 +607,7 @@ async function parseAntigravitySseForImage(response: Response, signal?: AbortSig
599
607
  export const geminiImageTool: CustomTool<typeof geminiImageSchema, GeminiImageToolDetails> = {
600
608
  name: "generate_image",
601
609
  label: "GenerateImage",
602
- description: renderPromptTemplate(geminiImageDescription),
610
+ description: prompt.render(geminiImageDescription),
603
611
  parameters: geminiImageSchema,
604
612
  async execute(_toolCallId, params, _onUpdate, ctx, signal) {
605
613
  return untilAborted(signal, async () => {
package/src/tools/gh.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
3
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
4
- import { abortableSleep, isEnoent, untilAborted } from "@oh-my-pi/pi-utils";
4
+ import { abortableSleep, isEnoent, prompt, untilAborted } from "@oh-my-pi/pi-utils";
5
5
  import { type Static, Type } from "@sinclair/typebox";
6
- import { renderPromptTemplate } from "../config/prompt-templates";
7
6
  import ghIssueViewDescription from "../prompts/tools/gh-issue-view.md" with { type: "text" };
8
7
  import ghPrCheckoutDescription from "../prompts/tools/gh-pr-checkout.md" with { type: "text" };
9
8
  import ghPrDiffDescription from "../prompts/tools/gh-pr-diff.md" with { type: "text" };
@@ -1902,7 +1901,7 @@ function buildTextResult(
1902
1901
  export class GhRepoViewTool implements AgentTool<typeof ghRepoViewSchema, GhToolDetails> {
1903
1902
  readonly name = "gh_repo_view";
1904
1903
  readonly label = "GitHub Repo";
1905
- readonly description = renderPromptTemplate(ghRepoViewDescription);
1904
+ readonly description = prompt.render(ghRepoViewDescription);
1906
1905
  readonly parameters = ghRepoViewSchema;
1907
1906
  readonly strict = true;
1908
1907
 
@@ -1943,7 +1942,7 @@ export class GhRepoViewTool implements AgentTool<typeof ghRepoViewSchema, GhTool
1943
1942
  export class GhIssueViewTool implements AgentTool<typeof ghIssueViewSchema, GhToolDetails> {
1944
1943
  readonly name = "gh_issue_view";
1945
1944
  readonly label = "GitHub Issue";
1946
- readonly description = renderPromptTemplate(ghIssueViewDescription);
1945
+ readonly description = prompt.render(ghIssueViewDescription);
1947
1946
  readonly parameters = ghIssueViewSchema;
1948
1947
  readonly strict = true;
1949
1948
 
@@ -1980,7 +1979,7 @@ export class GhIssueViewTool implements AgentTool<typeof ghIssueViewSchema, GhTo
1980
1979
  export class GhPrViewTool implements AgentTool<typeof ghPrViewSchema, GhToolDetails> {
1981
1980
  readonly name = "gh_pr_view";
1982
1981
  readonly label = "GitHub PR";
1983
- readonly description = renderPromptTemplate(ghPrViewDescription);
1982
+ readonly description = prompt.render(ghPrViewDescription);
1984
1983
  readonly parameters = ghPrViewSchema;
1985
1984
  readonly strict = true;
1986
1985
 
@@ -2024,7 +2023,7 @@ export class GhPrViewTool implements AgentTool<typeof ghPrViewSchema, GhToolDeta
2024
2023
  export class GhPrDiffTool implements AgentTool<typeof ghPrDiffSchema, GhToolDetails> {
2025
2024
  readonly name = "gh_pr_diff";
2026
2025
  readonly label = "GitHub PR Diff";
2027
- readonly description = renderPromptTemplate(ghPrDiffDescription);
2026
+ readonly description = prompt.render(ghPrDiffDescription);
2028
2027
  readonly parameters = ghPrDiffSchema;
2029
2028
  readonly strict = true;
2030
2029
 
@@ -2073,7 +2072,7 @@ export class GhPrDiffTool implements AgentTool<typeof ghPrDiffSchema, GhToolDeta
2073
2072
  export class GhPrCheckoutTool implements AgentTool<typeof ghPrCheckoutSchema, GhToolDetails> {
2074
2073
  readonly name = "gh_pr_checkout";
2075
2074
  readonly label = "GitHub PR Checkout";
2076
- readonly description = renderPromptTemplate(ghPrCheckoutDescription);
2075
+ readonly description = prompt.render(ghPrCheckoutDescription);
2077
2076
  readonly parameters = ghPrCheckoutSchema;
2078
2077
  readonly strict = true;
2079
2078
 
@@ -2204,7 +2203,7 @@ export class GhPrCheckoutTool implements AgentTool<typeof ghPrCheckoutSchema, Gh
2204
2203
  export class GhPrPushTool implements AgentTool<typeof ghPrPushSchema, GhToolDetails> {
2205
2204
  readonly name = "gh_pr_push";
2206
2205
  readonly label = "GitHub PR Push";
2207
- readonly description = renderPromptTemplate(ghPrPushDescription);
2206
+ readonly description = prompt.render(ghPrPushDescription);
2208
2207
  readonly parameters = ghPrPushSchema;
2209
2208
  readonly strict = true;
2210
2209
 
@@ -2265,7 +2264,7 @@ export class GhPrPushTool implements AgentTool<typeof ghPrPushSchema, GhToolDeta
2265
2264
  export class GhSearchIssuesTool implements AgentTool<typeof ghSearchIssuesSchema, GhToolDetails> {
2266
2265
  readonly name = "gh_search_issues";
2267
2266
  readonly label = "GitHub Issue Search";
2268
- readonly description = renderPromptTemplate(ghSearchIssuesDescription);
2267
+ readonly description = prompt.render(ghSearchIssuesDescription);
2269
2268
  readonly parameters = ghSearchIssuesSchema;
2270
2269
  readonly strict = true;
2271
2270
 
@@ -2300,7 +2299,7 @@ export class GhSearchIssuesTool implements AgentTool<typeof ghSearchIssuesSchema
2300
2299
  export class GhSearchPrsTool implements AgentTool<typeof ghSearchPrsSchema, GhToolDetails> {
2301
2300
  readonly name = "gh_search_prs";
2302
2301
  readonly label = "GitHub PR Search";
2303
- readonly description = renderPromptTemplate(ghSearchPrsDescription);
2302
+ readonly description = prompt.render(ghSearchPrsDescription);
2304
2303
  readonly parameters = ghSearchPrsSchema;
2305
2304
  readonly strict = true;
2306
2305
 
@@ -2335,7 +2334,7 @@ export class GhSearchPrsTool implements AgentTool<typeof ghSearchPrsSchema, GhTo
2335
2334
  export class GhRunWatchTool implements AgentTool<typeof ghRunWatchSchema, GhToolDetails> {
2336
2335
  readonly name = "gh_run_watch";
2337
2336
  readonly label = "GitHub Run Watch";
2338
- readonly description = renderPromptTemplate(ghRunWatchDescription);
2337
+ readonly description = prompt.render(ghRunWatchDescription);
2339
2338
  readonly parameters = ghRunWatchSchema;
2340
2339
  readonly strict = true;
2341
2340
 
package/src/tools/grep.ts CHANGED
@@ -1,18 +1,19 @@
1
1
  import * as path from "node:path";
2
2
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
3
3
 
4
- import { type GrepMatch, type GrepResult, grep } from "@oh-my-pi/pi-natives";
4
+ import { type GrepMatch, GrepOutputMode, type GrepResult, grep } from "@oh-my-pi/pi-natives";
5
5
  import type { Component } from "@oh-my-pi/pi-tui";
6
6
  import { Text } from "@oh-my-pi/pi-tui";
7
- import { untilAborted } from "@oh-my-pi/pi-utils";
7
+ import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
8
8
  import { type Static, Type } from "@sinclair/typebox";
9
- import { renderPromptTemplate } from "../config/prompt-templates";
9
+ import { computeLineHash } from "../edit/line-hash";
10
+ import { formatChunkedGrepLine } from "../edit/modes/chunk";
10
11
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
11
- import type { Theme } from "../modes/theme/theme";
12
- import { computeLineHash } from "../patch/hashline";
12
+ import { getLanguageFromPath, type Theme } from "../modes/theme/theme";
13
13
  import grepDescription from "../prompts/tools/grep.md" with { type: "text" };
14
14
  import { DEFAULT_MAX_COLUMN, type TruncationResult, truncateHead } from "../session/streaming-output";
15
15
  import { Ellipsis, Hasher, type RenderCache, renderStatusLine, renderTreeList, truncateToWidth } from "../tui";
16
+ import { resolveEditMode } from "../utils/edit-mode";
16
17
  import { resolveFileDisplayMode } from "../utils/file-display-mode";
17
18
  import type { ToolSession } from ".";
18
19
  import { formatFullOutputReference, type OutputMeta } from "./output-meta";
@@ -72,9 +73,10 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
72
73
 
73
74
  constructor(private readonly session: ToolSession) {
74
75
  const displayMode = resolveFileDisplayMode(session);
75
- this.description = renderPromptTemplate(grepDescription, {
76
+ this.description = prompt.render(grepDescription, {
76
77
  IS_HASHLINE_MODE: displayMode.hashLines,
77
78
  IS_LINE_NUMBER_MODE: !displayMode.hashLines && displayMode.lineNumbers,
79
+ IS_CHUNK_MODE: displayMode.chunked,
78
80
  });
79
81
  }
80
82
 
@@ -89,6 +91,7 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
89
91
 
90
92
  return untilAborted(signal, async () => {
91
93
  const normalizedPattern = pattern.trim();
94
+ const chunkMode = resolveEditMode(this.session) === "chunk";
92
95
  if (!normalizedPattern) {
93
96
  throw new ToolError("Pattern must not be empty");
94
97
  }
@@ -162,7 +165,7 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
162
165
  throw new ToolError(`Path not found: ${scopePath}`);
163
166
  }
164
167
 
165
- const effectiveOutputMode = "content";
168
+ const effectiveOutputMode = GrepOutputMode.Content;
166
169
  const effectiveLimit = normalizedLimit ?? DEFAULT_MATCH_LIMIT;
167
170
  const internalLimit = Math.min(effectiveLimit * 5, 2000);
168
171
 
@@ -270,6 +273,50 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
270
273
  }
271
274
  matchesByFile.get(relativePath)!.push(match);
272
275
  }
276
+ if (chunkMode) {
277
+ const annotatedLines = await Promise.all(
278
+ selectedMatches.map(match => {
279
+ const relativePath = match.path.startsWith("/") ? match.path.slice(1) : match.path;
280
+ const absoluteFilePath = isDirectory ? path.join(searchPath, relativePath) : searchPath;
281
+ const displayPath = formatPath(match.path);
282
+ fileMatchCounts.set(displayPath, (fileMatchCounts.get(displayPath) ?? 0) + 1);
283
+ return formatChunkedGrepLine({
284
+ filePath: absoluteFilePath,
285
+ lineNumber: match.lineNumber,
286
+ line: match.line,
287
+ cwd: this.session.cwd,
288
+ language: getLanguageFromPath(absoluteFilePath),
289
+ });
290
+ }),
291
+ );
292
+ const rawOutput = annotatedLines.join("\n");
293
+ const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
294
+ const truncated = Boolean(matchLimitReached || result.limitReached || truncation.truncated);
295
+ const details: GrepToolDetails = {
296
+ scopePath,
297
+ matchCount: selectedMatches.length,
298
+ fileCount: fileList.length,
299
+ files: fileList,
300
+ fileMatches: fileList.map(path => ({
301
+ path,
302
+ count: fileMatchCounts.get(path) ?? 0,
303
+ })),
304
+ truncated,
305
+ matchLimitReached: matchLimitReached ? effectiveLimit : undefined,
306
+ resultLimitReached: result.limitReached ? internalLimit : undefined,
307
+ };
308
+ if (truncation.truncated) details.truncation = truncation;
309
+ const resultBuilder = toolResult(details)
310
+ .text(truncation.content)
311
+ .limits({
312
+ matchLimit: matchLimitReached ? effectiveLimit : undefined,
313
+ resultLimit: result.limitReached ? internalLimit : undefined,
314
+ });
315
+ if (truncation.truncated) {
316
+ resultBuilder.truncation(truncation, { direction: "head" });
317
+ }
318
+ return resultBuilder.done();
319
+ }
273
320
  const renderMatchesForFile = (relativePath: string) => {
274
321
  const fileMatches = matchesByFile.get(relativePath) ?? [];
275
322
  for (const match of fileMatches) {
@@ -286,12 +333,13 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
286
333
  }
287
334
  const lineWidth = Math.max(...lineNumbers.map(value => value.toString().length));
288
335
  const formatLine = (lineNumber: number, line: string, isMatch: boolean): string => {
336
+ const separator = isMatch ? ":" : "-";
289
337
  if (useHashLines) {
290
338
  const ref = `${lineNumber}#${computeLineHash(lineNumber, line)}`;
291
- return isMatch ? `>>${ref}:${line}` : ` ${ref}:${line}`;
339
+ return `${ref}${separator}${line}`;
292
340
  }
293
341
  const padded = lineNumber.toString().padStart(lineWidth, " ");
294
- return isMatch ? `>>${padded}:${line}` : ` ${padded}:${line}`;
342
+ return `${padded}${separator}${line}`;
295
343
  };
296
344
  if (match.contextBefore) {
297
345
  for (const ctx of match.contextBefore) {
@@ -1,17 +1,20 @@
1
1
  import type { AgentTool } from "@oh-my-pi/pi-agent-core";
2
+ import type { ToolChoice } from "@oh-my-pi/pi-ai";
2
3
  import type { SearchDb } from "@oh-my-pi/pi-natives";
3
4
  import { $env, logger } from "@oh-my-pi/pi-utils";
4
5
  import type { AsyncJobManager } from "../async";
5
6
  import type { PromptTemplate } from "../config/prompt-templates";
6
7
  import type { Settings } from "../config/settings";
8
+ import { EditTool } from "../edit";
7
9
  import type { Skill } from "../extensibility/skills";
8
10
  import type { InternalUrlRouter } from "../internal-urls";
9
11
  import { getPreludeDocs, warmPythonEnvironment } from "../ipy/executor";
10
12
  import { checkPythonKernelAvailability } from "../ipy/kernel";
11
13
  import { LspTool } from "../lsp";
12
14
  import type { DiscoverableMCPSearchIndex, DiscoverableMCPTool } from "../mcp/discoverable-tool-metadata";
13
- import { EditTool } from "../patch";
14
15
  import type { PlanModeState } from "../plan-mode/state";
16
+ import type { CustomMessage } from "../session/messages";
17
+ import type { ToolChoiceQueue } from "../session/tool-choice-queue";
15
18
  import { TaskTool } from "../task";
16
19
  import type { AgentOutputManager } from "../task/output-manager";
17
20
  import type { EventBus } from "../utils/event-bus";
@@ -22,9 +25,11 @@ import { AstGrepTool } from "./ast-grep";
22
25
  import { AwaitTool } from "./await-tool";
23
26
  import { BashTool } from "./bash";
24
27
  import { BrowserTool } from "./browser";
28
+
25
29
  import { CalculatorTool } from "./calculator";
26
30
  import { CancelJobTool } from "./cancel-job";
27
31
  import { type CheckpointState, CheckpointTool, RewindTool } from "./checkpoint";
32
+ import { DebugTool } from "./debug";
28
33
  import { ExitPlanModeTool } from "./exit-plan-mode";
29
34
  import { FindTool } from "./find";
30
35
  import {
@@ -45,6 +50,7 @@ import { wrapToolWithMetaNotice } from "./output-meta";
45
50
  import { PythonTool } from "./python";
46
51
  import { ReadTool } from "./read";
47
52
  import { RenderMermaidTool } from "./render-mermaid";
53
+ import { createReportToolIssueTool, isAutoQaEnabled } from "./report-tool-issue";
48
54
  import { ResolveTool } from "./resolve";
49
55
  import { reportFindingTool } from "./review";
50
56
  import { SearchToolBm25Tool } from "./search-tool-bm25";
@@ -55,10 +61,10 @@ import { WriteTool } from "./write";
55
61
 
56
62
  // Exa MCP tools (22 tools)
57
63
 
64
+ export * from "../edit";
58
65
  export * from "../exa";
59
66
  export type * from "../exa/types";
60
67
  export * from "../lsp";
61
- export * from "../patch";
62
68
  export * from "../session/streaming-output";
63
69
  export * from "../task";
64
70
  export * from "../web/search";
@@ -71,6 +77,7 @@ export * from "./browser";
71
77
  export * from "./calculator";
72
78
  export * from "./cancel-job";
73
79
  export * from "./checkpoint";
80
+ export * from "./debug";
74
81
  export * from "./exit-plan-mode";
75
82
  export * from "./find";
76
83
  export * from "./gemini-image";
@@ -78,10 +85,10 @@ export * from "./gh";
78
85
  export * from "./grep";
79
86
  export * from "./inspect-image";
80
87
  export * from "./notebook";
81
- export * from "./pending-action";
82
88
  export * from "./python";
83
89
  export * from "./read";
84
90
  export * from "./render-mermaid";
91
+ export * from "./report-tool-issue";
85
92
  export * from "./resolve";
86
93
  export * from "./review";
87
94
  export * from "./search-tool-bm25";
@@ -175,12 +182,21 @@ export interface ToolSession {
175
182
  getSelectedMCPToolNames?: () => string[];
176
183
  /** Merge MCP tool selections into the active session tool set. */
177
184
  activateDiscoveredMCPTools?: (toolNames: string[]) => Promise<string[]>;
178
- /** Pending action store for preview/apply workflows */
179
- pendingActionStore?: import("./pending-action").PendingActionStore;
185
+ /** The tool-choice queue used to force forthcoming tool invocations and carry invocation handlers. */
186
+ getToolChoiceQueue?(): ToolChoiceQueue;
187
+ /** Build a model-provider-specific ToolChoice that targets the named tool, or undefined if unsupported. */
188
+ buildToolChoice?(toolName: string): ToolChoice | undefined;
189
+ /** Steer a hidden custom message into the conversation (e.g. a preview reminder). */
190
+ steer?(message: { customType: string; content: string; details?: unknown }): void;
191
+ /** Peek the currently in-flight tool-choice queue directive's invocation handler. Used by the `resolve` tool to dispatch to the pending action. */
192
+ peekQueueInvoker?(): ((input: unknown) => Promise<unknown> | unknown) | undefined;
180
193
  /** Get active checkpoint state if any. */
181
194
  getCheckpointState?: () => CheckpointState | undefined;
182
195
  /** Set or clear active checkpoint state. */
183
196
  setCheckpointState?: (state: CheckpointState | null) => void;
197
+
198
+ /** Queue a hidden message to be injected at the next agent turn. */
199
+ queueDeferredMessage?(message: CustomMessage): void;
184
200
  }
185
201
 
186
202
  type ToolFactory = (session: ToolSession) => Tool | null | Promise<Tool | null>;
@@ -191,6 +207,7 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
191
207
  render_mermaid: s => new RenderMermaidTool(s),
192
208
  ask: AskTool.createIf,
193
209
  bash: s => new BashTool(s),
210
+ debug: DebugTool.createIf,
194
211
  python: s => new PythonTool(s),
195
212
  calc: s => new CalculatorTool(s),
196
213
  ssh: loadSshTool,
@@ -225,6 +242,7 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
225
242
  export const HIDDEN_TOOLS: Record<string, ToolFactory> = {
226
243
  submit_result: s => new SubmitResultTool(s),
227
244
  report_finding: () => reportFindingTool,
245
+ report_tool_issue: s => createReportToolIssueTool(s),
228
246
  exit_plan_mode: s => new ExitPlanModeTool(s),
229
247
  resolve: s => new ResolveTool(s),
230
248
  };
@@ -282,11 +300,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
282
300
  const isTestEnv = Bun.env.BUN_ENV === "test" || Bun.env.NODE_ENV === "test";
283
301
  const skipPythonWarm = isTestEnv || $env.PI_PYTHON_SKIP_CHECK === "1";
284
302
  if (shouldCheckPython) {
285
- const availability = await logger.timeAsync(
286
- "createTools:pythonCheck",
287
- checkPythonKernelAvailability,
288
- session.cwd,
289
- );
303
+ const availability = await logger.time("createTools:pythonCheck", checkPythonKernelAvailability, session.cwd);
290
304
  pythonAvailable = availability.ok;
291
305
  if (!availability.ok) {
292
306
  logger.warn("Python kernel unavailable, falling back to bash", {
@@ -296,12 +310,13 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
296
310
  const sessionFile = session.getSessionFile?.() ?? undefined;
297
311
  const warmSessionId = sessionFile ? `session:${sessionFile}:cwd:${session.cwd}` : `cwd:${session.cwd}`;
298
312
  try {
299
- await logger.timeAsync(
313
+ await logger.time(
300
314
  "createTools:warmPython",
301
315
  warmPythonEnvironment,
302
316
  session.cwd,
303
317
  warmSessionId,
304
318
  session.settings.get("python.sharedGateway"),
319
+ sessionFile,
305
320
  );
306
321
  } catch (err) {
307
322
  logger.warn("Failed to warm Python environment", {
@@ -343,9 +358,10 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
343
358
  }
344
359
  const allTools: Record<string, ToolFactory> = { ...BUILTIN_TOOLS, ...HIDDEN_TOOLS };
345
360
  const isToolAllowed = (name: string) => {
346
- if (name === "lsp") return enableLsp;
361
+ if (name === "lsp") return enableLsp && session.settings.get("lsp.enabled");
347
362
  if (name === "bash") return allowBash;
348
363
  if (name === "python") return allowPython;
364
+ if (name === "debug") return session.settings.get("debug.enabled");
349
365
  if (name === "todo_write") return !includeSubmitResult && session.settings.get("todo.enabled");
350
366
  if (name === "find") return session.settings.get("find.enabled");
351
367
  if (name === "grep") return session.settings.get("grep.enabled");
@@ -357,7 +373,6 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
357
373
  if (name === "inspect_image") return session.settings.get("inspect_image.enabled");
358
374
  if (name === "web_search") return session.settings.get("web_search.enabled");
359
375
  if (name === "search_tool_bm25") return session.settings.get("mcp.discoveryMode");
360
- if (name === "lsp") return session.settings.get("lsp.enabled");
361
376
  if (name === "calc") return session.settings.get("calc.enabled");
362
377
  if (name === "browser") return session.settings.get("browser.enabled");
363
378
  if (name === "checkpoint" || name === "rewind") return session.settings.get("checkpoint.enabled");
@@ -384,21 +399,28 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
384
399
 
385
400
  const baseResults = await Promise.all(
386
401
  baseEntries.map(async ([name, factory]) => {
387
- const tool = await logger.timeAsync(`createTools:${name}`, factory, session);
402
+ const tool = await logger.time(`createTools:${name}`, factory, session);
388
403
  return tool ? wrapToolWithMetaNotice(tool) : null;
389
404
  }),
390
405
  );
391
406
  const tools = baseResults.filter((r): r is Tool => r !== null);
392
407
  const hasDeferrableTools = tools.some(tool => tool.deferrable === true);
393
- if (!hasDeferrableTools) {
394
- return tools;
395
- }
396
- if (tools.some(tool => tool.name === "resolve")) {
397
- return tools;
408
+ if (hasDeferrableTools && !tools.some(tool => tool.name === "resolve")) {
409
+ const resolveTool = await logger.time("createTools:resolve", HIDDEN_TOOLS.resolve, session);
410
+ if (resolveTool) {
411
+ tools.push(wrapToolWithMetaNotice(resolveTool));
412
+ }
398
413
  }
399
- const resolveTool = await logger.timeAsync("createTools:resolve", HIDDEN_TOOLS.resolve, session);
400
- if (resolveTool) {
401
- tools.push(wrapToolWithMetaNotice(resolveTool));
414
+
415
+ // Auto-inject report_tool_issue when autoqa is enabled (env or setting).
416
+ // Injected unconditionally into every agent, regardless of requested tool list.
417
+ const autoQA = isAutoQaEnabled(session.settings);
418
+ if (autoQA && !tools.some(t => t.name === "report_tool_issue")) {
419
+ const qaTool = await HIDDEN_TOOLS.report_tool_issue(session);
420
+ if (qaTool) {
421
+ tools.push(wrapToolWithMetaNotice(qaTool));
422
+ }
402
423
  }
424
+
403
425
  return tools;
404
426
  }
@@ -1,8 +1,8 @@
1
1
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
2
  import { type Api, type AssistantMessage, completeSimple, type Model } from "@oh-my-pi/pi-ai";
3
+ import { prompt } from "@oh-my-pi/pi-utils";
3
4
  import { type Static, Type } from "@sinclair/typebox";
4
5
  import { expandRoleAlias, resolveModelFromString } from "../config/model-resolver";
5
- import { renderPromptTemplate } from "../config/prompt-templates";
6
6
  import inspectImageDescription from "../prompts/tools/inspect-image.md" with { type: "text" };
7
7
  import inspectImageSystemPromptTemplate from "../prompts/tools/inspect-image-system.md" with { type: "text" };
8
8
  import {
@@ -10,7 +10,7 @@ import {
10
10
  type LoadedImageInput,
11
11
  loadImageInput,
12
12
  MAX_IMAGE_INPUT_BYTES,
13
- } from "../utils/image-input";
13
+ } from "../utils/image-loading";
14
14
  import type { ToolSession } from "./index";
15
15
  import { ToolError } from "./tool-errors";
16
16
 
@@ -49,7 +49,7 @@ export class InspectImageTool implements AgentTool<typeof inspectImageSchema, In
49
49
  private readonly session: ToolSession,
50
50
  private readonly completeImageRequest: typeof completeSimple = completeSimple,
51
51
  ) {
52
- this.description = renderPromptTemplate(inspectImageDescription);
52
+ this.description = prompt.render(inspectImageDescription);
53
53
  }
54
54
 
55
55
  async execute(
@@ -127,7 +127,7 @@ export class InspectImageTool implements AgentTool<typeof inspectImageSchema, In
127
127
  const response = await this.completeImageRequest(
128
128
  model,
129
129
  {
130
- systemPrompt: renderPromptTemplate(inspectImageSystemPromptTemplate),
130
+ systemPrompt: prompt.render(inspectImageSystemPromptTemplate),
131
131
  messages: [
132
132
  {
133
133
  role: "user",