From fe85adfa530988683fa5aaa16ba90cfb330d469c Mon Sep 17 00:00:00 2001 From: wolfy Date: Sun, 31 May 2026 11:52:45 -0500 Subject: [PATCH] fix: reviews failing to call next tool --- agents/ollama.ts | 35 +++++++++++++++++++++++++++++++++-- mcp/checkout.ts | 6 +++--- utils/instructions.ts | 9 ++++++--- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/agents/ollama.ts b/agents/ollama.ts index ecbc714..3c422ef 100644 --- a/agents/ollama.ts +++ b/agents/ollama.ts @@ -95,8 +95,17 @@ async function runOllamaLoop(ctx: AgentRunContext): Promise { }, ]; + // Tools that signal the agent has produced its final output. + const OUTPUT_TOOLS = new Set([ + "create_pull_request_review", + "report_progress", + "set_output", + ]); + let iterations = 0; let pendingModeNudge = false; + let calledOutputTool = false; + let addedContinueNudge = false; while (iterations < MAX_ITERATIONS) { iterations++; @@ -124,8 +133,26 @@ async function runOllamaLoop(ctx: AgentRunContext): Promise { const toolCalls: ToolCall[] | undefined = assistantMessage.tool_calls; if (!toolCalls || toolCalls.length === 0) { - // If we just gave the model a nudge after select_mode and it still won't - // call tools, it's genuinely done (or stuck) — exit cleanly. + log.debug(` model text: ${assistantMessage.content?.slice(0, 500)}`); + + // If the model stopped before ever calling an output tool and we haven't + // nudged yet, give it one more push to continue the workflow — regardless + // 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"); + addedContinueNudge = true; + messages.push({ + role: "user", + content: + "Your task is not complete yet. Continue executing the workflow — " + + "call the next required tool to finish. " + + "Do not stop until you have submitted a review (create_pull_request_review) " + + "or called report_progress with a final summary.", + }); + continue; + } + if (pendingModeNudge) { log.info("» agent finished after mode nudge (no tool calls)"); } else { @@ -150,6 +177,10 @@ async function runOllamaLoop(ctx: AgentRunContext): Promise { log.info(`» calling tool: ${toolName}`); log.debug(` args: ${JSON.stringify(toolArgs)}`); + if (OUTPUT_TOOLS.has(toolName)) { + calledOutputTool = true; + } + if (ctx.onToolUse) { ctx.onToolUse({ toolName, input: toolArgs }); } diff --git a/mcp/checkout.ts b/mcp/checkout.ts index 3596668..6432d84 100644 --- a/mcp/checkout.ts +++ b/mcp/checkout.ts @@ -487,9 +487,9 @@ 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. ` + - `use the TOC line ranges as your checklist and read specific files from the diff. ` + - `for example, if the TOC says "src/foo.ts → lines 5-42", read lines 5-42 from diffPath. ` + - `review files selectively based on relevance. ` + + `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' }). ` + `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/utils/instructions.ts b/utils/instructions.ts index c98fe4c..12d61c4 100644 --- a/utils/instructions.ts +++ b/utils/instructions.ts @@ -107,8 +107,11 @@ function getShellInstructions( } } -function getFileInstructions(): string { - return `### File operations\n\nUse your native file read/write/edit tools for all file operations.`; +function getFileInstructions(shell: ResolvedPayload["shell"]): string { + if (shell === "disabled") { + 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 ",p" /path/to/file', description: 'read lines' })\`\n- Search in a file: \`shell({ command: 'grep -n "pattern" /path/to/file', description: 'search' })\``; } function getStandaloneModeInstructions( @@ -241,7 +244,7 @@ Use MCP tools from ${shockbotMcpName} for all Gitea operations. Never use the \` ${getShellInstructions(ctx.payload.shell, t)} -${getFileInstructions()} +${getFileInstructions(ctx.payload.shell)} ${getStandaloneModeInstructions(ctx.payload.event.trigger, t, ctx.outputSchema)}