feat(coding-agent): added review tools and /review command
- Added report_finding and submit_review built-in tools (hidden by default) - Added hidden field to AgentTool interface for tools that require explicit opt-in - Added /review bundled command to launch reviewer agent - Updated bundled reviewer agent with comprehensive code review guidelines
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
export { type AskToolDetails, askTool, createAskTool } from "./ask.js";
|
||||
export { type BashToolDetails, bashTool, createBashTool } from "./bash.js";
|
||||
export { createEditTool, type EditToolOptions, editTool } from "./edit.js";
|
||||
export { type AskToolDetails, askTool, createAskTool } from "./ask";
|
||||
export { type BashToolDetails, bashTool, createBashTool } from "./bash";
|
||||
export { createEditTool, type EditToolOptions, editTool } from "./edit";
|
||||
// Exa MCP tools (22 tools)
|
||||
export { exaTools } from "./exa/index.js";
|
||||
export type { ExaRenderDetails, ExaSearchResponse, ExaSearchResult } from "./exa/types.js";
|
||||
export { createFindTool, type FindToolDetails, findTool } from "./find.js";
|
||||
export { createGrepTool, type GrepToolDetails, grepTool } from "./grep.js";
|
||||
export { createLsTool, type LsToolDetails, lsTool } from "./ls.js";
|
||||
export { exaTools } from "./exa/index";
|
||||
export type { ExaRenderDetails, ExaSearchResponse, ExaSearchResult } from "./exa/types";
|
||||
export { createFindTool, type FindToolDetails, findTool } from "./find";
|
||||
export { createGrepTool, type GrepToolDetails, grepTool } from "./grep";
|
||||
export { createLsTool, type LsToolDetails, lsTool } from "./ls";
|
||||
export {
|
||||
createLspTool,
|
||||
type FileDiagnosticsResult,
|
||||
@@ -19,12 +19,13 @@ export {
|
||||
type LspWarmupResult,
|
||||
lspTool,
|
||||
warmupLspServers,
|
||||
} from "./lsp/index.js";
|
||||
export { createNotebookTool, type NotebookToolDetails, notebookTool } from "./notebook.js";
|
||||
export { createReadTool, type ReadToolDetails, readTool } from "./read.js";
|
||||
export { BUNDLED_AGENTS, createTaskTool, taskTool } from "./task/index.js";
|
||||
export type { TruncationResult } from "./truncate.js";
|
||||
export { createWebFetchTool, type WebFetchToolDetails, webFetchCustomTool, webFetchTool } from "./web-fetch.js";
|
||||
} from "./lsp/index";
|
||||
export { createNotebookTool, type NotebookToolDetails, notebookTool } from "./notebook";
|
||||
export { createReadTool, type ReadToolDetails, readTool } from "./read";
|
||||
export { createReportFindingTool, createSubmitReviewTool, reportFindingTool, submitReviewTool } from "./review";
|
||||
export { BUNDLED_AGENTS, createTaskTool, taskTool } from "./task/index";
|
||||
export type { TruncationResult } from "./truncate";
|
||||
export { createWebFetchTool, type WebFetchToolDetails, webFetchCustomTool, webFetchTool } from "./web-fetch";
|
||||
export {
|
||||
companyWebSearchTools,
|
||||
createWebSearchTool,
|
||||
@@ -42,24 +43,25 @@ export {
|
||||
webSearchDeepTool,
|
||||
webSearchLinkedinTool,
|
||||
webSearchTool,
|
||||
} from "./web-search/index.js";
|
||||
export { createWriteTool, type WriteToolDetails, type WriteToolOptions, writeTool } from "./write.js";
|
||||
} from "./web-search/index";
|
||||
export { createWriteTool, type WriteToolDetails, type WriteToolOptions, writeTool } from "./write";
|
||||
|
||||
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
||||
import { askTool, createAskTool } from "./ask.js";
|
||||
import { bashTool, createBashTool } from "./bash.js";
|
||||
import { checkBashInterception, checkSimpleLsInterception } from "./bash-interceptor.js";
|
||||
import { createEditTool, editTool } from "./edit.js";
|
||||
import { createFindTool, findTool } from "./find.js";
|
||||
import { createGrepTool, grepTool } from "./grep.js";
|
||||
import { createLsTool, lsTool } from "./ls.js";
|
||||
import { createLspTool, formatFile, getDiagnosticsForFile, lspTool } from "./lsp/index.js";
|
||||
import { createNotebookTool, notebookTool } from "./notebook.js";
|
||||
import { createReadTool, readTool } from "./read.js";
|
||||
import { createTaskTool, taskTool } from "./task/index.js";
|
||||
import { createWebFetchTool, webFetchTool } from "./web-fetch.js";
|
||||
import { createWebSearchTool, webSearchTool } from "./web-search/index.js";
|
||||
import { createWriteTool, writeTool } from "./write.js";
|
||||
import { askTool, createAskTool } from "./ask";
|
||||
import { bashTool, createBashTool } from "./bash";
|
||||
import { checkBashInterception, checkSimpleLsInterception } from "./bash-interceptor";
|
||||
import { createEditTool, editTool } from "./edit";
|
||||
import { createFindTool, findTool } from "./find";
|
||||
import { createGrepTool, grepTool } from "./grep";
|
||||
import { createLsTool, lsTool } from "./ls";
|
||||
import { createLspTool, formatFile, getDiagnosticsForFile, lspTool } from "./lsp/index";
|
||||
import { createNotebookTool, notebookTool } from "./notebook";
|
||||
import { createReadTool, readTool } from "./read";
|
||||
import { createReportFindingTool, createSubmitReviewTool, reportFindingTool, submitReviewTool } from "./review";
|
||||
import { createTaskTool, taskTool } from "./task/index";
|
||||
import { createWebFetchTool, webFetchTool } from "./web-fetch";
|
||||
import { createWebSearchTool, webSearchTool } from "./web-search/index";
|
||||
import { createWriteTool, writeTool } from "./write";
|
||||
|
||||
/** Tool type (AgentTool from pi-ai) */
|
||||
export type Tool = AgentTool<any, any, any>;
|
||||
@@ -118,6 +120,8 @@ const toolDefs: Record<string, { tool: Tool; create: ToolFactory }> = {
|
||||
task: { tool: taskTool, create: (cwd, ctx) => createTaskTool(cwd, ctx) },
|
||||
web_fetch: { tool: webFetchTool, create: createWebFetchTool },
|
||||
web_search: { tool: webSearchTool, create: createWebSearchTool },
|
||||
report_finding: { tool: reportFindingTool, create: createReportFindingTool },
|
||||
submit_review: { tool: submitReviewTool, create: createSubmitReviewTool },
|
||||
};
|
||||
|
||||
export type ToolName = keyof typeof toolDefs;
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
/**
|
||||
* Review tools - report_finding and submit_review
|
||||
*
|
||||
* Used by the reviewer agent to report findings in a structured way.
|
||||
* Both tools are hidden by default - only enabled when explicitly listed in agent's tools.
|
||||
*/
|
||||
|
||||
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
||||
import type { Component } from "@oh-my-pi/pi-tui";
|
||||
import { Container, Spacer, Text } from "@oh-my-pi/pi-tui";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import type { Theme } from "../../modes/interactive/theme/theme";
|
||||
|
||||
const PRIORITY_LABELS: Record<number, string> = {
|
||||
0: "P0",
|
||||
1: "P1",
|
||||
2: "P2",
|
||||
3: "P3",
|
||||
};
|
||||
|
||||
const PRIORITY_DESCRIPTIONS: Record<number, string> = {
|
||||
0: "Drop everything to fix. Blocking release, operations, or major usage.",
|
||||
1: "Urgent. Should be addressed in the next cycle.",
|
||||
2: "Normal. To be fixed eventually.",
|
||||
3: "Low. Nice to have.",
|
||||
};
|
||||
|
||||
// report_finding schema
|
||||
const ReportFindingParams = Type.Object({
|
||||
title: Type.String({
|
||||
description: "≤80 chars, imperative, prefixed with [P0-P3]. E.g., '[P1] Un-padding slices along wrong dimension'",
|
||||
}),
|
||||
body: Type.String({
|
||||
description: "Markdown explaining why this is a problem. One paragraph max.",
|
||||
}),
|
||||
priority: Type.Union([Type.Literal(0), Type.Literal(1), Type.Literal(2), Type.Literal(3)], {
|
||||
description: "0=P0 (critical), 1=P1 (urgent), 2=P2 (normal), 3=P3 (low)",
|
||||
}),
|
||||
confidence: Type.Number({
|
||||
minimum: 0,
|
||||
maximum: 1,
|
||||
description: "Confidence score 0.0-1.0",
|
||||
}),
|
||||
file_path: Type.String({ description: "Absolute path to the file" }),
|
||||
line_start: Type.Number({ description: "Start line of the issue" }),
|
||||
line_end: Type.Number({ description: "End line of the issue" }),
|
||||
});
|
||||
|
||||
interface ReportFindingDetails {
|
||||
title: string;
|
||||
body: string;
|
||||
priority: number;
|
||||
confidence: number;
|
||||
file_path: string;
|
||||
line_start: number;
|
||||
line_end: number;
|
||||
}
|
||||
|
||||
export const reportFindingTool: AgentTool<typeof ReportFindingParams, ReportFindingDetails, Theme> = {
|
||||
name: "report_finding",
|
||||
label: "Report Finding",
|
||||
description: "Report a code review finding. Use this for each issue found. Call submit_review when done.",
|
||||
parameters: ReportFindingParams,
|
||||
hidden: true,
|
||||
|
||||
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
||||
const { title, body, priority, confidence, file_path, line_start, line_end } = params;
|
||||
const location = `${file_path}:${line_start}${line_end !== line_start ? `-${line_end}` : ""}`;
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Finding recorded: ${PRIORITY_LABELS[priority]} ${title}\nLocation: ${location}\nConfidence: ${(confidence * 100).toFixed(0)}%`,
|
||||
},
|
||||
],
|
||||
details: { title, body, priority, confidence, file_path, line_start, line_end },
|
||||
};
|
||||
},
|
||||
|
||||
renderCall(args, theme): Component {
|
||||
const priority = PRIORITY_LABELS[args.priority as number] ?? "P?";
|
||||
const color = args.priority === 0 ? "error" : args.priority === 1 ? "warning" : "muted";
|
||||
const titleText = String(args.title).replace(/^\[P\d\]\s*/, "");
|
||||
return new Text(
|
||||
`${theme.fg("toolTitle", theme.bold("report_finding "))}${theme.fg(color, `[${priority}]`)} ${theme.fg("dim", titleText)}`,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
},
|
||||
|
||||
renderResult(result, _options, theme): Component {
|
||||
const { details } = result;
|
||||
if (!details) {
|
||||
const text = result.content[0];
|
||||
return new Text(text?.type === "text" ? text.text : "", 0, 0);
|
||||
}
|
||||
|
||||
const priority = PRIORITY_LABELS[details.priority] ?? "P?";
|
||||
const color = details.priority === 0 ? "error" : details.priority === 1 ? "warning" : "muted";
|
||||
const location = `${details.file_path}:${details.line_start}${details.line_end !== details.line_start ? `-${details.line_end}` : ""}`;
|
||||
|
||||
return new Text(
|
||||
`${theme.fg("success", "✓")} ${theme.fg(color, `[${priority}]`)} ${theme.fg("dim", location)}`,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
// submit_review schema
|
||||
const SubmitReviewParams = Type.Object({
|
||||
overall_correctness: Type.Union([Type.Literal("correct"), Type.Literal("incorrect")], {
|
||||
description: "Whether the patch is correct (no bugs, tests won't break)",
|
||||
}),
|
||||
explanation: Type.String({
|
||||
description: "1-3 sentence explanation justifying the verdict",
|
||||
}),
|
||||
confidence: Type.Number({
|
||||
minimum: 0,
|
||||
maximum: 1,
|
||||
description: "Overall confidence score 0.0-1.0",
|
||||
}),
|
||||
findings_count: Type.Number({
|
||||
description: "Total number of findings reported",
|
||||
}),
|
||||
});
|
||||
|
||||
interface SubmitReviewDetails {
|
||||
overall_correctness: "correct" | "incorrect";
|
||||
explanation: string;
|
||||
confidence: number;
|
||||
findings_count: number;
|
||||
}
|
||||
|
||||
export const submitReviewTool: AgentTool<typeof SubmitReviewParams, SubmitReviewDetails, Theme> = {
|
||||
name: "submit_review",
|
||||
label: "Submit Review",
|
||||
description: "Submit the final review verdict. Call this after all findings have been reported.",
|
||||
parameters: SubmitReviewParams,
|
||||
hidden: true,
|
||||
|
||||
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
||||
const { overall_correctness, explanation, confidence, findings_count } = params;
|
||||
|
||||
let summary = `## Review Summary\n\n`;
|
||||
summary += `**Verdict:** ${overall_correctness === "correct" ? "✓ Patch is correct" : "✗ Patch is incorrect"}\n`;
|
||||
summary += `**Confidence:** ${(confidence * 100).toFixed(0)}%\n`;
|
||||
summary += `**Findings:** ${findings_count}\n\n`;
|
||||
summary += explanation;
|
||||
|
||||
return {
|
||||
content: [{ type: "text", text: summary }],
|
||||
details: { overall_correctness, explanation, confidence, findings_count },
|
||||
};
|
||||
},
|
||||
|
||||
renderCall(args, theme): Component {
|
||||
const verdict = args.overall_correctness === "correct" ? "correct" : "incorrect";
|
||||
const color = args.overall_correctness === "correct" ? "success" : "error";
|
||||
return new Text(
|
||||
`${theme.fg("toolTitle", theme.bold("submit_review "))}${theme.fg(color, verdict)} ${theme.fg("dim", `(${((args.confidence as number) * 100).toFixed(0)}%)`)}`,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
},
|
||||
|
||||
renderResult(result, { expanded }, theme): Component {
|
||||
const { details } = result;
|
||||
if (!details) {
|
||||
const text = result.content[0];
|
||||
return new Text(text?.type === "text" ? text.text : "", 0, 0);
|
||||
}
|
||||
|
||||
const container = new Container();
|
||||
const verdictColor = details.overall_correctness === "correct" ? "success" : "error";
|
||||
const verdictIcon = details.overall_correctness === "correct" ? "✓" : "✗";
|
||||
|
||||
container.addChild(
|
||||
new Text(
|
||||
`${theme.fg(verdictColor, verdictIcon)} Patch is ${theme.fg(verdictColor, details.overall_correctness)} ${theme.fg("dim", `(${(details.confidence * 100).toFixed(0)}% confidence)`)}`,
|
||||
0,
|
||||
0,
|
||||
),
|
||||
);
|
||||
|
||||
if (details.findings_count > 0) {
|
||||
container.addChild(new Spacer(1));
|
||||
container.addChild(new Text(theme.fg("muted", `${details.findings_count} finding(s) reported`), 0, 0));
|
||||
}
|
||||
|
||||
if (expanded) {
|
||||
container.addChild(new Spacer(1));
|
||||
container.addChild(new Text(theme.fg("dim", details.explanation), 0, 0));
|
||||
}
|
||||
|
||||
return container;
|
||||
},
|
||||
};
|
||||
|
||||
export function createReportFindingTool(): AgentTool<typeof ReportFindingParams, ReportFindingDetails, Theme> {
|
||||
return reportFindingTool;
|
||||
}
|
||||
|
||||
export function createSubmitReviewTool(): AgentTool<typeof SubmitReviewParams, SubmitReviewDetails, Theme> {
|
||||
return submitReviewTool;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
description: Launch code review with the reviewer agent
|
||||
---
|
||||
|
||||
Use the Task tool to run the "reviewer" agent with this task:
|
||||
|
||||
$@
|
||||
|
||||
The reviewer agent will analyze the code changes and report findings using the report_finding and submit_review tools.
|
||||
Reference in New Issue
Block a user