fix(tools): resolve internal URLs in find tool before filesystem path lookup
This commit is contained in:
@@ -8,6 +8,7 @@ import { isEnoent, prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
||||
import type { Static } from "@sinclair/typebox";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
||||
import { InternalUrlRouter } from "../internal-urls";
|
||||
import type { Theme } from "../modes/theme/theme";
|
||||
import findDescription from "../prompts/tools/find.md" with { type: "text" };
|
||||
import { type TruncationResult, truncateHead } from "../session/streaming-output";
|
||||
@@ -25,6 +26,7 @@ import { applyListLimit } from "./list-limit";
|
||||
import { formatFullOutputReference, type OutputMeta } from "./output-meta";
|
||||
import {
|
||||
formatPathRelativeToCwd,
|
||||
hasGlobPathChars,
|
||||
normalizePathLikeInput,
|
||||
parseFindPattern,
|
||||
partitionExistingPaths,
|
||||
@@ -116,7 +118,23 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
||||
|
||||
return untilAborted(signal, async () => {
|
||||
const formatScopePath = (targetPath: string): string => formatPathRelativeToCwd(targetPath, this.session.cwd);
|
||||
const normalizedPatterns = paths.map(input => normalizePathLikeInput(input).replace(/\\/g, "/"));
|
||||
const rawPatterns = paths.map(input => normalizePathLikeInput(input).replace(/\\/g, "/"));
|
||||
const internalRouter = InternalUrlRouter.instance();
|
||||
const normalizedPatterns: string[] = [];
|
||||
for (const rawPattern of rawPatterns) {
|
||||
if (!internalRouter.canHandle(rawPattern)) {
|
||||
normalizedPatterns.push(rawPattern);
|
||||
continue;
|
||||
}
|
||||
if (hasGlobPathChars(rawPattern)) {
|
||||
throw new ToolError(`Glob patterns are not supported for internal URLs: ${rawPattern}`);
|
||||
}
|
||||
const resource = await internalRouter.resolve(rawPattern);
|
||||
if (!resource.sourcePath) {
|
||||
throw new ToolError(`Cannot find internal URL without a backing file: ${rawPattern}`);
|
||||
}
|
||||
normalizedPatterns.push(resource.sourcePath);
|
||||
}
|
||||
if (normalizedPatterns.some(pattern => pattern.length === 0)) {
|
||||
throw new ToolError("`paths` must contain non-empty globs or paths");
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
||||
import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
||||
import { InternalUrlRouter, LocalProtocolHandler } from "@oh-my-pi/pi-coding-agent/internal-urls";
|
||||
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { FindTool } from "@oh-my-pi/pi-coding-agent/tools/find";
|
||||
import { SearchTool } from "@oh-my-pi/pi-coding-agent/tools/search";
|
||||
import { AgentRegistry } from "../../src/registry/agent-registry";
|
||||
|
||||
@@ -149,6 +150,24 @@ describe("SearchTool internal URL resolution", () => {
|
||||
expect(text).not.toMatch(/^\*?\s*\d+[a-z]{2}\|/m);
|
||||
});
|
||||
|
||||
it("resolves local:// URLs before file-name lookup", async () => {
|
||||
const localRoot = path.join(artifactsDir, "local");
|
||||
await fs.mkdir(localRoot, { recursive: true });
|
||||
await Bun.write(path.join(localRoot, "PLAN.md"), "# Plan\n");
|
||||
|
||||
LocalProtocolHandler.setOverride({ getArtifactsDir: () => artifactsDir, getSessionId: () => "session" });
|
||||
|
||||
const session = createSession();
|
||||
const tool = new FindTool(session);
|
||||
|
||||
const result = await tool.execute("test-call", {
|
||||
paths: ["local://PLAN.md"],
|
||||
});
|
||||
|
||||
const text = getResultText(result);
|
||||
expect(text).toContain("PLAN.md");
|
||||
});
|
||||
|
||||
it("keeps hashline anchors when searching mutable local:// sources", async () => {
|
||||
const localRoot = path.join(artifactsDir, "local");
|
||||
await fs.mkdir(localRoot, { recursive: true });
|
||||
|
||||
Reference in New Issue
Block a user