feat: add read_file tool
This commit is contained in:
+4
-1
@@ -117,6 +117,7 @@ async function runOllamaLoop(ctx: AgentRunContext): Promise<AgentResult> {
|
|||||||
model,
|
model,
|
||||||
messages,
|
messages,
|
||||||
tools,
|
tools,
|
||||||
|
keep_alive: "10m",
|
||||||
options: {
|
options: {
|
||||||
think: false,
|
think: false,
|
||||||
} as Record<string, unknown>,
|
} as Record<string, unknown>,
|
||||||
@@ -140,7 +141,9 @@ async function runOllamaLoop(ctx: AgentRunContext): Promise<AgentResult> {
|
|||||||
// of whether the mode nudge is still pending (the model may have stopped
|
// of whether the mode nudge is still pending (the model may have stopped
|
||||||
// right after select_mode before acting on the guidance).
|
// right after select_mode before acting on the guidance).
|
||||||
if (!calledOutputTool && !addedContinueNudge) {
|
if (!calledOutputTool && !addedContinueNudge) {
|
||||||
log.info("» model stopped before completing task — nudging to continue");
|
log.info(
|
||||||
|
"» model stopped before completing task — nudging to continue",
|
||||||
|
);
|
||||||
addedContinueNudge = true;
|
addedContinueNudge = true;
|
||||||
messages.push({
|
messages.push({
|
||||||
role: "user",
|
role: "user",
|
||||||
|
|||||||
+2
-3
@@ -487,9 +487,8 @@ export function CheckoutPrTool(ctx: ToolContext) {
|
|||||||
hookWarning: checkoutResult.hookWarning,
|
hookWarning: checkoutResult.hookWarning,
|
||||||
instructions:
|
instructions:
|
||||||
`the diff file at diffPath contains a table of contents (TOC) listing every changed file with its line range. ` +
|
`the diff file at diffPath contains a table of contents (TOC) listing every changed file with its line range. ` +
|
||||||
`to read the diff, use the shell MCP tool — for example: shell({ command: 'sed -n "<start>,<end>p" ${join(tempDir, `pr-${pull_number}-${headShort}.diff`)}', description: 'read diff section' }). ` +
|
`use the read_file MCP tool to read sections of it. ` +
|
||||||
`use the TOC line ranges as your checklist and read specific sections. ` +
|
`for example, if the TOC says "src/foo.ts → lines 5-42", call: read_file({ path: diffPath, start_line: 5, end_line: 42 }). ` +
|
||||||
`for example, if the TOC says "src/foo.ts → lines 5-42", run: shell({ command: 'sed -n "5,42p" <diffPath>', description: 'read foo.ts diff' }). ` +
|
|
||||||
`IMPORTANT: to inspect the PR's changed files, read diffPath directly — ` +
|
`IMPORTANT: to inspect the PR's changed files, read diffPath directly — ` +
|
||||||
`do NOT run git diff or git show. The PR base branch is '${pr.baseRef}', NOT necessarily 'main' — ` +
|
`do NOT run git diff or git show. The PR base branch is '${pr.baseRef}', NOT necessarily 'main' — ` +
|
||||||
`if you must use git, use 'origin/${pr.baseRef}' as the base (e.g. git log origin/${pr.baseRef}..HEAD), ` +
|
`if you must use git, use 'origin/${pr.baseRef}' as the base (e.g. git log origin/${pr.baseRef}..HEAD), ` +
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import { type } from "arktype";
|
||||||
|
import type { ToolContext } from "./server.ts";
|
||||||
|
import { execute, tool } from "./shared.ts";
|
||||||
|
|
||||||
|
export const ReadFileParams = type({
|
||||||
|
path: type.string.describe("absolute path to the file to read"),
|
||||||
|
"start_line?": type.number.describe("start line, 1-based inclusive (default: 1)"),
|
||||||
|
"end_line?": type.number.describe("end line, 1-based inclusive (default: end of file)"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export function ReadFileTool(_ctx: ToolContext) {
|
||||||
|
return tool({
|
||||||
|
name: "read_file",
|
||||||
|
description:
|
||||||
|
"Read lines from a file. Use this to read sections of the PR diff returned by checkout_pr. " +
|
||||||
|
"Example: `read_file({ path: diffPath, start_line: 5, end_line: 42 })`.",
|
||||||
|
parameters: ReadFileParams,
|
||||||
|
execute: execute(async ({ path, start_line, end_line }) => {
|
||||||
|
let content: string;
|
||||||
|
try {
|
||||||
|
content = readFileSync(path, "utf-8");
|
||||||
|
} catch (err) {
|
||||||
|
const msg = err instanceof Error ? err.message : String(err);
|
||||||
|
throw new Error(`failed to read ${path}: ${msg}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_line === undefined && end_line === undefined) {
|
||||||
|
return { content };
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines = content.split("\n");
|
||||||
|
const start = Math.max(0, (start_line ?? 1) - 1);
|
||||||
|
const end = Math.min(lines.length, end_line ?? lines.length);
|
||||||
|
return { content: lines.slice(start, end).join("\n") };
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ import {
|
|||||||
GetReviewCommentsTool,
|
GetReviewCommentsTool,
|
||||||
ListPullRequestReviewsTool,
|
ListPullRequestReviewsTool,
|
||||||
} from "./reviewComments.ts";
|
} from "./reviewComments.ts";
|
||||||
|
import { ReadFileTool } from "./readFile.ts";
|
||||||
import { SelectModeTool } from "./selectMode.ts";
|
import { SelectModeTool } from "./selectMode.ts";
|
||||||
import { addTools } from "./shared.ts";
|
import { addTools } from "./shared.ts";
|
||||||
import { KillBackgroundTool, ShellTool } from "./shell.ts";
|
import { KillBackgroundTool, ShellTool } from "./shell.ts";
|
||||||
@@ -118,6 +119,7 @@ function buildCommonTools(ctx: ToolContext, outputSchema?: JsonSchema): Tool<any
|
|||||||
GitTool(ctx),
|
GitTool(ctx),
|
||||||
GitFetchTool(ctx),
|
GitFetchTool(ctx),
|
||||||
UploadFileTool(ctx),
|
UploadFileTool(ctx),
|
||||||
|
ReadFileTool(ctx),
|
||||||
];
|
];
|
||||||
|
|
||||||
const isStandalone = ctx.payload.event.trigger === "unknown";
|
const isStandalone = ctx.payload.event.trigger === "unknown";
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ function getShellInstructions(
|
|||||||
case "restricted":
|
case "restricted":
|
||||||
return `### Shell commands\n\nUse the \`${t("shell")}\` MCP tool for all shell command execution. This tool provides a secure environment with filtered credentials. Do NOT use any native shell tool — it is disabled for security. For long-running processes, use \`shell({ command, background: true })\`. Use \`${t("kill_background")}\` to stop background processes.`;
|
return `### Shell commands\n\nUse the \`${t("shell")}\` MCP tool for all shell command execution. This tool provides a secure environment with filtered credentials. Do NOT use any native shell tool — it is disabled for security. For long-running processes, use \`shell({ command, background: true })\`. Use \`${t("kill_background")}\` to stop background processes.`;
|
||||||
case "enabled":
|
case "enabled":
|
||||||
return `### Shell commands\n\nUse the \`${t("shell")}\` MCP tool for all shell command execution (unrestricted environment, credentials available).`;
|
return `### Shell commands\n\nUse your native shell tool for shell command execution.`;
|
||||||
default: {
|
default: {
|
||||||
const _exhaustive: never = shell;
|
const _exhaustive: never = shell;
|
||||||
return _exhaustive satisfies never;
|
return _exhaustive satisfies never;
|
||||||
@@ -107,11 +107,8 @@ function getShellInstructions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFileInstructions(shell: ResolvedPayload["shell"]): string {
|
function getFileInstructions(_shell: ResolvedPayload["shell"]): string {
|
||||||
if (shell === "disabled") {
|
return `### File operations\n\nUse the \`read_file\` MCP tool to read files. For example: \`read_file({ path: diffPath, start_line: 5, end_line: 42 })\`. This is the primary way to read the PR diff returned by \`checkout_pr\`.`;
|
||||||
return `### File operations\n\nShell is disabled — you cannot read arbitrary files. Use the diff content returned by MCP tools directly.`;
|
|
||||||
}
|
|
||||||
return `### File operations\n\nUse the \`shell\` MCP tool for all file operations. Examples:\n- Read a file: \`shell({ command: 'cat /path/to/file', description: 'read file' })\`\n- Read specific lines: \`shell({ command: 'sed -n "<start>,<end>p" /path/to/file', description: 'read lines' })\`\n- Search in a file: \`shell({ command: 'grep -n "pattern" /path/to/file', description: 'search' })\``;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStandaloneModeInstructions(
|
function getStandaloneModeInstructions(
|
||||||
|
|||||||
Reference in New Issue
Block a user