@@ -156,6 +156,12 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
`;
|
||||
}
|
||||
|
||||
// Check if plan file exists
|
||||
function planFileExists(): boolean {
|
||||
const planFilePath = permissionMode.getPlanFilePath();
|
||||
return !!planFilePath && existsSync(planFilePath);
|
||||
}
|
||||
|
||||
// Read plan content from the plan file
|
||||
function readPlanFile(): string {
|
||||
const planFilePath = permissionMode.getPlanFilePath();
|
||||
@@ -825,8 +831,9 @@ export default function App({
|
||||
// Check permissions for all approvals (including fancy UI tools)
|
||||
const approvalResults = await Promise.all(
|
||||
approvalsToProcess.map(async (approvalItem) => {
|
||||
// Check if approval is incomplete (missing name or arguments)
|
||||
if (!approvalItem.toolName || !approvalItem.toolArgs) {
|
||||
// Check if approval is incomplete (missing name)
|
||||
// Note: toolArgs can be empty string for tools with no arguments (e.g., EnterPlanMode)
|
||||
if (!approvalItem.toolName) {
|
||||
return {
|
||||
approval: approvalItem,
|
||||
permission: {
|
||||
@@ -3251,6 +3258,20 @@ ${recentCommits}
|
||||
[pendingApprovals, approvalResults, sendAllResults],
|
||||
);
|
||||
|
||||
// Auto-reject ExitPlanMode if plan file doesn't exist
|
||||
useEffect(() => {
|
||||
const currentIndex = approvalResults.length;
|
||||
const approval = pendingApprovals[currentIndex];
|
||||
if (approval?.toolName === "ExitPlanMode" && !planFileExists()) {
|
||||
const planFilePath = permissionMode.getPlanFilePath();
|
||||
handlePlanKeepPlanning(
|
||||
`You must write your plan to the plan file before exiting plan mode.\n` +
|
||||
`Plan file path: ${planFilePath || "not set"}\n` +
|
||||
`Use the Write tool to create your plan, then call ExitPlanMode again.`,
|
||||
);
|
||||
}
|
||||
}, [pendingApprovals, approvalResults.length, handlePlanKeepPlanning]);
|
||||
|
||||
const handleQuestionSubmit = useCallback(
|
||||
async (answers: Record<string, string>) => {
|
||||
const currentIndex = approvalResults.length;
|
||||
|
||||
@@ -272,8 +272,9 @@ export const ToolCallMessage = memo(({ line }: { line: ToolCallLine }) => {
|
||||
|
||||
// Format tool denial errors more user-friendly
|
||||
if (isError && displayText.includes("request to call tool denied")) {
|
||||
const match = displayText.match(/User reason: (.+)$/);
|
||||
const reason = match ? match[1] : "(empty)";
|
||||
// Use [\s\S]+ to match multiline reasons
|
||||
const match = displayText.match(/User reason: ([\s\S]+)$/);
|
||||
const reason = match?.[1]?.trim() || "(empty)";
|
||||
displayText = `User rejected the tool call with reason: ${reason}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -124,6 +124,11 @@ class PermissionModeManager {
|
||||
"Grep",
|
||||
"NotebookRead",
|
||||
"TodoWrite",
|
||||
// Plan mode tools (must allow exit!)
|
||||
"ExitPlanMode",
|
||||
"exit_plan_mode",
|
||||
"AskUserQuestion",
|
||||
"ask_user_question",
|
||||
// Codex toolset (snake_case)
|
||||
"read_file",
|
||||
"list_dir",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
export async function exit_plan_mode(): Promise<{ message: string }> {
|
||||
// Return confirmation message that plan was approved
|
||||
// Note: The plan is read from the plan file by the UI before this return is shown
|
||||
// The UI layer checks if the plan file exists and auto-rejects if not
|
||||
return {
|
||||
message:
|
||||
"User has approved your plan. You can now start coding.\nStart with updating your todo list if applicable",
|
||||
|
||||
Reference in New Issue
Block a user