Files
oh-my-pi/packages/coding-agent/src/tools/exit-plan-mode.ts
T
can1357 79079e6ecd feat(coding-agent): added plan mode with approval workflow and tool gating
- Plan mode provides structured workflow where agents propose plans for user approval before execution.
- Added plan:// internal URL protocol for accessing plan files and injecting plan-mode context into subagent prompts.
- Added plan mode toggle shortcut and paused status indicator in status line.
- Fixed plan reference injection to properly pass workflow state to plan-mode system prompts.
- Improved autocomplete fuzzy matching to support subsequence matching for skill suggestions.
2026-01-25 19:35:26 +01:00

63 lines
1.9 KiB
TypeScript

import * as fs from "node:fs/promises";
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
import { isEnoent } from "@oh-my-pi/pi-utils";
import { Type } from "@sinclair/typebox";
import { renderPromptTemplate } from "../config/prompt-templates";
import exitPlanModeDescription from "../prompts/tools/exit-plan-mode.md" with { type: "text" };
import type { ToolSession } from ".";
import { resolvePlanPath } from "./plan-mode-guard";
import { ToolError } from "./tool-errors";
const exitPlanModeSchema = Type.Object({});
export interface ExitPlanModeDetails {
planFilePath: string;
planExists: boolean;
}
export class ExitPlanModeTool implements AgentTool<typeof exitPlanModeSchema, ExitPlanModeDetails> {
public readonly name = "exit_plan_mode";
public readonly label = "ExitPlanMode";
public readonly description: string;
public readonly parameters = exitPlanModeSchema;
private readonly session: ToolSession;
constructor(session: ToolSession) {
this.session = session;
this.description = renderPromptTemplate(exitPlanModeDescription);
}
public async execute(
_toolCallId: string,
_params: Record<string, never>,
_signal?: AbortSignal,
_onUpdate?: AgentToolUpdateCallback<ExitPlanModeDetails>,
_context?: AgentToolContext,
): Promise<AgentToolResult<ExitPlanModeDetails>> {
const state = this.session.getPlanModeState?.();
if (!state?.enabled) {
throw new ToolError("Plan mode is not active.");
}
const resolvedPlanPath = resolvePlanPath(this.session, state.planFilePath);
let planExists = false;
try {
const stat = await fs.stat(resolvedPlanPath);
planExists = stat.isFile();
} catch (error) {
if (!isEnoent(error)) {
throw error;
}
}
return {
content: [{ type: "text", text: "Plan ready for approval." }],
details: {
planFilePath: state.planFilePath,
planExists,
},
};
}
}