refactor(coding-agent/tools): removed enter-plan-mode tool

- Removed the enter-plan-mode tool, associated prompt, and all event handling code.
This commit is contained in:
can1357
2026-01-27 04:14:16 +01:00
parent a8743c1f91
commit 0f4829376f
9 changed files with 3 additions and 211 deletions
+2
View File
@@ -5,6 +5,8 @@
### Fixed
- Reduced Task tool progress update overhead to keep the UI responsive during high-volume streaming output
### Removed
- Removed enter-plan-mode tool
## [8.4.5] - 2026-01-26
### Added
@@ -247,12 +247,6 @@ export class EventController {
this.ctx.setTodos(details.todos);
}
}
if (event.toolName === "enter_plan_mode" && !event.isError) {
const details = event.result.details as import("../../tools").EnterPlanModeDetails | undefined;
if (details) {
await this.ctx.handleEnterPlanModeTool(details);
}
}
if (event.toolName === "exit_plan_mode" && !event.isError) {
const details = event.result.details as ExitPlanModeDetails | undefined;
if (details) {
@@ -29,7 +29,7 @@ import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
import { HistoryStorage } from "../session/history-storage";
import type { SessionContext, SessionManager } from "../session/session-manager";
import { getRecentSessions } from "../session/session-manager";
import type { EnterPlanModeDetails, ExitPlanModeDetails } from "../tools";
import type { ExitPlanModeDetails } from "../tools";
import { setTerminalTitle } from "../utils/title-generator";
import type { AssistantMessageComponent } from "./components/assistant-message";
import type { BashExecutionComponent } from "./components/bash-execution";
@@ -631,25 +631,6 @@ export class InteractiveMode implements InteractiveModeContext {
await this.enterPlanMode();
}
async handleEnterPlanModeTool(details: EnterPlanModeDetails): Promise<void> {
if (this.planModeEnabled) {
this.showWarning("Plan mode is already active.");
return;
}
const confirmed = await this.showHookConfirm(
"Enter plan mode?",
"This enables read-only planning and creates a plan file for approval.",
);
if (!confirmed) {
return;
}
const planFilePath = details.planFilePath || this.getPlanFilePath();
this.planModePlanFilePath = planFilePath;
await this.enterPlanMode({ planFilePath, workflow: details.workflow });
}
async handleExitPlanModeTool(details: ExitPlanModeDetails): Promise<void> {
if (!this.planModeEnabled) {
this.showWarning("Plan mode is not active.");
-1
View File
@@ -178,7 +178,6 @@ export interface InteractiveModeContext {
openExternalEditor(): void;
registerExtensionShortcuts(): void;
handlePlanModeCommand(): Promise<void>;
handleEnterPlanModeTool(details: import("../tools").EnterPlanModeDetails): Promise<void>;
handleExitPlanModeTool(details: ExitPlanModeDetails): Promise<void>;
// Hook UI methods
@@ -1,98 +0,0 @@
Transitions to plan mode for designing implementation approaches before writing code.
<conditions>
Prefer using EnterPlanMode for implementation tasks unless they're simple. Use it when ANY of these conditions apply:
1. **New Feature Implementation**: Adding meaningful new functionality
- Example: "Add a logout button" — where should it go? What should happen on click?
- Example: "Add form validation" — what rules? What error messages?
2. **Multiple Valid Approaches**: The task can be solved in several different ways
- Example: "Add caching to the API" — could use Redis, in-memory, file-based, etc.
- Example: "Improve performance" — many optimization strategies possible
3. **Code Modifications**: Changes that affect existing behavior or structure
- Example: "Update the login flow" — what exactly should change?
- Example: "Refactor this component" — what's the target architecture?
4. **Architectural Decisions**: The task requires choosing between patterns or technologies
- Example: "Add real-time updates" — WebSockets vs SSE vs polling
- Example: "Implement state management" — Redux vs Context vs custom solution
5. **Multi-File Changes**: The task will likely touch more than 2-3 files
- Example: "Refactor the authentication system"
- Example: "Add a new API endpoint with tests"
6. **Unclear Requirements**: You need to explore before understanding the full scope
- Example: "Make the app faster" — need to profile and identify bottlenecks
- Example: "Fix the bug in checkout" — need to investigate root cause
7. **User Preferences Matter**: The implementation could reasonably go multiple ways
- If you would use `ask` to clarify the approach, use EnterPlanMode instead
- Plan mode lets you explore first, then present options with context
</conditions>
<instruction>
In plan mode:
1. Explore codebase with `find`, `grep`, `read`, `ls`
2. Understand existing patterns and architecture
3. Design implementation approach
4. Use `ask` if clarification needed
5. Call `exit_plan_mode` when ready
</instruction>
<output>
Requires user approval to enter. Once approved, you enter read-only exploration mode with restricted tool access.
</output>
<parameters>
Optional parameters:
- `parallel`: Explore independent threads in parallel before synthesizing.
- `iterative`: One thread at a time with checkpoints between steps.
</parameters>
<example name="auth">
User: "Add user authentication to the app"
→ Use plan mode: architectural decisions (session vs JWT, where to store tokens, middleware structure)
</example>
<example name="optimization">
User: "Optimize the database queries"
→ Use plan mode: multiple approaches possible, need to profile first, significant impact
</example>
<example name="dark-mode">
User: "Implement dark mode"
→ Use plan mode: architectural decision on theme system, affects many components
</example>
<example name="delete-button">
User: "Add a delete button to the user profile"
→ Use plan mode: seems simple but involves placement, confirmation dialog, API call, error handling, state updates
</example>
<example name="error-handling">
User: "Update the error handling in the API"
→ Use plan mode: affects multiple files, user should approve the approach
</example>
<example name="typo-skip">
User: "Fix the typo in the README"
→ Skip plan mode: straightforward, no planning needed
</example>
<example name="debug-skip">
User: "Add a console.log to debug this function"
→ Skip plan mode: simple, obvious implementation
</example>
<example name="research-skip">
User: "What files handle routing?"
→ Skip plan mode: research task, not implementation planning
</example>
<avoid>
- Single-line or few-line fixes (typos, obvious bugs)
- Adding a single function with clear requirements
- Tasks with very specific, detailed instructions
- Pure research/exploration tasks
</avoid>
<critical>
- This tool REQUIRES user approval — they must consent to entering plan mode
- If unsure whether to use it, err on the side of planning — alignment upfront beats rework
- Users appreciate being consulted before significant changes are made to their codebase
</critical>
@@ -1,81 +0,0 @@
import * as fs from "node:fs/promises";
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
import { StringEnum } from "@oh-my-pi/pi-ai";
import { isEnoent } from "@oh-my-pi/pi-utils";
import { Type } from "@sinclair/typebox";
import { renderPromptTemplate } from "../config/prompt-templates";
import { resolvePlanUrlToPath } from "../internal-urls";
import enterPlanModeDescription from "../prompts/tools/enter-plan-mode.md" with { type: "text" };
import type { ToolSession } from ".";
import { ToolError } from "./tool-errors";
const enterPlanModeSchema = Type.Object({
workflow: Type.Optional(
StringEnum(["parallel", "iterative"], {
description: "Planning workflow to use",
}),
),
});
export interface EnterPlanModeDetails {
planFilePath: string;
planExists: boolean;
workflow?: "parallel" | "iterative";
}
export class EnterPlanModeTool implements AgentTool<typeof enterPlanModeSchema, EnterPlanModeDetails> {
public readonly name = "enter_plan_mode";
public readonly label = "EnterPlanMode";
public readonly description: string;
public readonly parameters = enterPlanModeSchema;
private readonly session: ToolSession;
constructor(session: ToolSession) {
this.session = session;
this.description = renderPromptTemplate(enterPlanModeDescription);
}
public async execute(
_toolCallId: string,
params: { workflow?: "parallel" | "iterative" },
_signal?: AbortSignal,
_onUpdate?: AgentToolUpdateCallback<EnterPlanModeDetails>,
_context?: AgentToolContext,
): Promise<AgentToolResult<EnterPlanModeDetails>> {
const state = this.session.getPlanModeState?.();
if (state?.enabled) {
throw new ToolError("Plan mode is already active.");
}
const sessionId = this.session.getSessionId?.();
if (!sessionId) {
throw new ToolError("Plan mode requires an active session.");
}
const settingsManager = this.session.settingsManager;
if (!settingsManager) {
throw new ToolError("Settings manager unavailable for plan mode.");
}
const planFilePath = `plan://${sessionId}/plan.md`;
const resolvedPlanPath = resolvePlanUrlToPath(planFilePath, {
getPlansDirectory: settingsManager.getPlansDirectory.bind(settingsManager),
cwd: this.session.cwd,
});
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 mode requested." }],
details: { planFilePath, planExists, workflow: params.workflow },
};
}
}
-3
View File
@@ -19,7 +19,6 @@ import { AskTool } from "./ask";
import { BashTool } from "./bash";
import { CalculatorTool } from "./calculator";
import { CompleteTool } from "./complete";
import { EnterPlanModeTool } from "./enter-plan-mode";
import { ExitPlanModeTool } from "./exit-plan-mode";
import { FetchTool } from "./fetch";
import { FindTool } from "./find";
@@ -73,7 +72,6 @@ export { AskTool, type AskToolDetails } from "./ask";
export { BashTool, type BashToolDetails, type BashToolOptions } from "./bash";
export { CalculatorTool, type CalculatorToolDetails } from "./calculator";
export { CompleteTool } from "./complete";
export { type EnterPlanModeDetails, EnterPlanModeTool } from "./enter-plan-mode";
export { type ExitPlanModeDetails, ExitPlanModeTool } from "./exit-plan-mode";
export { FetchTool, type FetchToolDetails } from "./fetch";
export { type FindOperations, FindTool, type FindToolDetails, type FindToolOptions } from "./find";
@@ -199,7 +197,6 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
fetch: s => new FetchTool(s),
web_search: s => new WebSearchTool(s),
write: s => new WriteTool(s),
enter_plan_mode: s => new EnterPlanModeTool(s),
};
export const HIDDEN_TOOLS: Record<string, ToolFactory> = {
@@ -155,7 +155,6 @@ describe("createTools", () => {
"fetch",
"web_search",
"write",
"enter_plan_mode",
];
for (const tool of expectedTools) {
@@ -383,7 +383,6 @@ describe("tool schema validation (post-sanitization)", () => {
"fetch",
"web_search",
"write",
"enter_plan_mode",
];
expect(Object.keys(BUILTIN_TOOLS).sort()).toEqual(expectedTools.sort());