diff --git a/agents/ollama.ts b/agents/ollama.ts index 3c422ef..9eb3da4 100644 --- a/agents/ollama.ts +++ b/agents/ollama.ts @@ -117,6 +117,7 @@ async function runOllamaLoop(ctx: AgentRunContext): Promise { model, messages, tools, + keep_alive: "10m", options: { think: false, } as Record, @@ -140,7 +141,9 @@ async function runOllamaLoop(ctx: AgentRunContext): Promise { // of whether the mode nudge is still pending (the model may have stopped // right after select_mode before acting on the guidance). 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; messages.push({ role: "user", diff --git a/mcp/checkout.ts b/mcp/checkout.ts index 6432d84..f2d81ed 100644 --- a/mcp/checkout.ts +++ b/mcp/checkout.ts @@ -487,9 +487,8 @@ export function CheckoutPrTool(ctx: ToolContext) { hookWarning: checkoutResult.hookWarning, instructions: `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 ",p" ${join(tempDir, `pr-${pull_number}-${headShort}.diff`)}', description: 'read diff section' }). ` + - `use the TOC line ranges as your checklist and read specific sections. ` + - `for example, if the TOC says "src/foo.ts → lines 5-42", run: shell({ command: 'sed -n "5,42p" ', description: 'read foo.ts diff' }). ` + + `use the read_file MCP tool to read sections of it. ` + + `for example, if the TOC says "src/foo.ts → lines 5-42", call: read_file({ path: diffPath, start_line: 5, end_line: 42 }). ` + `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' — ` + `if you must use git, use 'origin/${pr.baseRef}' as the base (e.g. git log origin/${pr.baseRef}..HEAD), ` + diff --git a/mcp/readFile.ts b/mcp/readFile.ts new file mode 100644 index 0000000..c3decf9 --- /dev/null +++ b/mcp/readFile.ts @@ -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") }; + }), + }); +} diff --git a/mcp/server.ts b/mcp/server.ts index 71d41a1..9687cd3 100644 --- a/mcp/server.ts +++ b/mcp/server.ts @@ -35,6 +35,7 @@ import { GetReviewCommentsTool, ListPullRequestReviewsTool, } from "./reviewComments.ts"; +import { ReadFileTool } from "./readFile.ts"; import { SelectModeTool } from "./selectMode.ts"; import { addTools } from "./shared.ts"; import { KillBackgroundTool, ShellTool } from "./shell.ts"; @@ -118,6 +119,7 @@ function buildCommonTools(ctx: ToolContext, outputSchema?: JsonSchema): Tool,p" /path/to/file', description: 'read lines' })\`\n- Search in a file: \`shell({ command: 'grep -n "pattern" /path/to/file', description: 'search' })\``; +function getFileInstructions(_shell: ResolvedPayload["shell"]): string { + 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\`.`; } function getStandaloneModeInstructions(