Files
shockbot/mcp/selectMode.ts
T

94 lines
3.9 KiB
TypeScript

import { type } from "arktype";
import { formatMcpToolRef } from "../external.ts";
import type { Mode } from "../modes.ts";
import type { ToolContext } from "./server.ts";
import { execute, tool } from "./shared.ts";
export const SelectModeParams = type({
mode: type.string.describe(
"the name of the mode to select (e.g., 'Build', 'Plan', 'Review', 'IncrementalReview', 'Fix', 'AddressReviews', 'Task', 'ResolveConflicts')"
),
"issue_number?": type("number").describe(
"optional issue number; when provided with Plan mode, used to look up an existing plan comment"
),
});
function resolveMode(modes: Mode[], modeName: string): Mode | null {
return modes.find((m) => m.name.toLowerCase() === modeName.toLowerCase()) ?? null;
}
function buildModeOverrides(t: (name: string) => string): Record<string, string> {
return {
PlanEdit: `### Checklist (editing existing plan)
An existing plan comment was found for this issue. Update that comment with the revised plan — do not create a new plan comment.
1. **task list**: create your task list for this run as your first action.
2. Use \`previousPlanBody\` from this response as the plan to revise; do not call \`get_issue\` or \`get_issue_comments\`.
3. Revise the plan based on the user's request.
4. Call \`${t("report_progress")}\` with the full revised plan text and \`{ target_plan_comment: true }\` so it updates the existing plan comment.
5. Then post a short note to the progress comment via \`${t("report_progress")}\` so it is not left as "Leaping...".`,
};
}
export function SelectModeTool(ctx: ToolContext) {
return tool({
name: "select_mode",
description:
"Select the operating mode for this run. Call this first to get the workflow for your task. " +
"Example: `select_mode({ mode: 'Review' })`.",
parameters: SelectModeParams,
execute: execute(async ({ mode, issue_number }) => {
const t = (toolName: string) => formatMcpToolRef(ctx.agentId, toolName);
const overrides = buildModeOverrides(t);
// find mode in available list
const foundMode = resolveMode(ctx.modes, mode);
if (!foundMode) {
const available = ctx.modes.map((m) => m.name).join(", ");
throw new Error(
`Unknown mode "${mode}". Available modes: ${available}`
);
}
ctx.toolState.selectedMode = foundMode.name;
const overrideGuidance = overrides[foundMode.name];
const hardcoded = overrideGuidance ?? foundMode.prompt ?? "";
const userInstructions = ctx.modeInstructions[foundMode.name] ?? "";
const guidance = [hardcoded, userInstructions].filter(Boolean).join("\n\n");
const response: Record<string, unknown> = {
modeName: foundMode.name,
description: foundMode.description,
orchestratorGuidance: guidance,
};
// For Plan mode with issue_number, look up existing plan comment
if (foundMode.name === "Plan" && issue_number !== undefined) {
try {
const commentsResp = await ctx.gitea.paginate(ctx.gitea.rest.issue.issueGetComments, {
owner: ctx.repo.owner,
repo: ctx.repo.name,
index: issue_number,
});
const comments = commentsResp;
// Look for a plan comment (one with our footer)
const planComment = comments.find((c) => c.body?.includes("<!-- shockbot-footer -->"));
if (planComment) {
if (planComment.id !== undefined) ctx.toolState.existingPlanCommentId = planComment.id;
ctx.toolState.previousPlanBody = planComment.body ?? "";
response.existingPlanCommentFound = true;
response.previousPlanBody = ctx.toolState.previousPlanBody;
response.orchestratorGuidance = (overrides["PlanEdit"] ?? guidance) + "\n\n" + (userInstructions ? `\n\n${userInstructions}` : "");
}
} catch {
// Best-effort — if we can't find the plan comment, proceed normally
}
}
return response;
}),
});
}