Skip to content

feat(copilotcli):Implement updating plan file in exit plan mode handling#309454

Merged
DonJayamanne merged 2 commits intomainfrom
don/super-herring
Apr 14, 2026
Merged

feat(copilotcli):Implement updating plan file in exit plan mode handling#309454
DonJayamanne merged 2 commits intomainfrom
don/super-herring

Conversation

@DonJayamanne
Copy link
Copy Markdown
Contributor

No description provided.

Copilot AI review requested due to automatic review settings April 13, 2026 11:50
@DonJayamanne DonJayamanne self-assigned this Apr 13, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extracts Copilot CLI “exit plan mode” handling into a dedicated handleExitPlanMode helper and adds a new unit test suite, with the goal of supporting updating the SDK session plan when the user edits plan.md during exit-plan approval.

Changes:

  • Introduces exitPlanModeHandler.ts to centralize autopilot/interactive exit-plan decisions and plan syncing logic.
  • Updates CopilotCLISession to delegate exit_plan_mode.requested handling to the new helper.
  • Adds exitPlanModeHandler.spec.ts with coverage for autopilot/interactive flows and plan file monitoring.
Show a summary per file
File Description
extensions/copilot/src/extension/chatSessions/copilotcli/node/exitPlanModeHandler.ts New handler + plan file monitor implementation.
extensions/copilot/src/extension/chatSessions/copilotcli/node/copilotcliSession.ts Wires the SDK event to the new handler.
extensions/copilot/src/extension/chatSessions/copilotcli/node/test/exitPlanModeHandler.spec.ts New unit tests for handler behavior and monitoring.

Copilot's findings

Comments suppressed due to low confidence (4)

extensions/copilot/src/extension/chatSessions/copilotcli/node/exitPlanModeHandler.ts:85

  • ExitPlanModeEventData models actions/recommendedAction as required strings, but existing callers/tests show these fields can be omitted (e.g. actions undefined). As written, resolveInteractive will throw when it does event.actions.map(...) if actions is missing. Make these fields optional (or default them defensively in handleExitPlanMode) and ensure all usages tolerate undefined/empty values.
export interface ExitPlanModeEventData {
	readonly requestId: string;
	readonly actions: string[];
	readonly recommendedAction: string;
}

extensions/copilot/src/extension/chatSessions/copilotcli/node/exitPlanModeHandler.ts:59

  • Plan syncing currently depends on onDidChangeTextDocument events and only writes when doc.isDirty === false. Saving a document typically does not fire a change event, so if the user saves after the delayer fires (or without a format-on-save edit), the SDK session will never be updated. Consider listening to an explicit save event (preferred) or re-triggering/polling until the document becomes clean while the question is open.
		this.add(workspaceService.onDidChangeTextDocument(e => {
			if (e.contentChanges.length === 0 || !isEqual(e.document.uri, planUri)) {
				return;
			}
			this._lastChangedDocument = e.document;
			this._delayer.trigger(() => this._syncIfSaved());
		}));
	}

	private _syncIfSaved(): void {
		const doc = this._lastChangedDocument;
		if (!doc || doc.isDirty) {
			return;
		}

extensions/copilot/src/extension/chatSessions/copilotcli/node/exitPlanModeHandler.ts:65

  • _pendingWrite is replaced on each sync, allowing multiple writePlan calls to overlap and potentially complete out-of-order (older write finishing last). Chain writes onto the previous promise (serialize) so the final plan content is deterministic, and flush() truly waits for all queued writes.
		const content = doc.getText();
		this._logService.trace('[ExitPlanModeHandler] Plan file saved by user, syncing to SDK session');
		this._pendingWrite = this._session.writePlan(content).catch(err => {
			this._logService.error(err, '[ExitPlanModeHandler] Failed to write plan changes to SDK session');
		});
	}

extensions/copilot/src/extension/chatSessions/copilotcli/node/exitPlanModeHandler.ts:186

  • Interactive resolution assumes answer.selected[0] exists whenever freeText is empty. If the UI returns an answer with no selection (or skipped: true), this will respond approved: true with an invalid/undefined selectedAction. Handle answer.skipped / empty selections by returning { approved: false } (or falling back to a safe default from event.actions).
		if (!answer) {
			return { approved: false };
		}
		if (answer.freeText) {
			return { approved: false, feedback: answer.freeText };
		}

		let selectedAction: ExitPlanModeActionType = answer.selected[0] as ExitPlanModeActionType;
		for (const [action, desc] of Object.entries(actionDescriptions)) {
			if (desc.label === selectedAction) {
				selectedAction = action as ExitPlanModeActionType;
				break;
			}
		}
		const autoApproveEdits = permissionLevel === 'autoApprove' ? true : undefined;
		return { approved: true, selectedAction, autoApproveEdits };
  • Files reviewed: 3/3 changed files
  • Comments generated: 2

@DonJayamanne DonJayamanne marked this pull request as ready for review April 14, 2026 04:32
@github-actions
Copy link
Copy Markdown
Contributor

Screenshot Changes

Base: 82109004 Current: d663c483

Changed (62)

chat/aiCustomizations/aiCustomizationListWidget/InstructionsTabWithItems/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationListWidget/InstructionsTabWithItems/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/WelcomePage/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/WelcomePage/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/LocalHarness/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/LocalHarness/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/CliHarness/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/CliHarness/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/Sessions/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/Sessions/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/SessionsSkillsTab/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/SessionsSkillsTab/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/McpServersTab/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/McpServersTab/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/AgentsTab/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/AgentsTab/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/SkillsTab/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/SkillsTab/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/InstructionsTab/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/InstructionsTab/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/HooksTab/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/HooksTab/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/PromptsTab/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/PromptsTab/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/PluginsTab/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/PluginsTab/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/McpBrowseMode/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/PromptsTabScrolled/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/PromptsTabScrolled/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/McpServersTabScrolled/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/McpServersTabScrolled/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/PluginsTabScrolled/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/PluginsTabScrolled/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/McpServersTabNarrow/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/McpServersTabNarrow/Light
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/AgentsTabNarrow/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationManagementEditor/AgentsTabNarrow/Light
Before After
before after
chat/aiCustomizations/aiCustomizationWelcomePages/WelcomePagePromptLaunchers/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationWelcomePages/WelcomePagePromptLaunchers/Light
Before After
before after
chat/aiCustomizations/aiCustomizationWelcomePages/WelcomePageSelectorPromptLaunchers/Dark
Before After
before after
chat/aiCustomizations/aiCustomizationWelcomePages/WelcomePageSelectorPromptLaunchers/Light
Before After
before after
editor/inlineCompletions/views/SideBySideViewSmall/Dark
Before After
before after
editor/inlineCompletions/views/SideBySideViewSmall/Light
Before After
before after
editor/inlineCompletions/views/SideBySideViewWide/Dark
Before After
before after
editor/inlineCompletions/views/SideBySideViewWide/Light
Before After
before after
editor/inlineCompletions/views/WordReplacementView/Dark
Before After
before after
editor/inlineCompletions/views/WordReplacementView/Light
Before After
before after
editor/inlineCompletions/views/DeletionView/Dark
Before After
before after
editor/inlineCompletions/views/DeletionView/Light
Before After
before after
editor/inlineCompletions/views/LineReplacementView/Dark
Before After
before after
editor/inlineCompletions/views/LineReplacementView/Light
Before After
before after
editor/multiDiffEditor/MultiDiffEditor/Light
Before After
before after
editor/suggestWidget/MethodCompletions/Dark
Before After
before after
editor/suggestWidget/MethodCompletions/Light
Before After
before after
peekReference/PeekReferences/Dark
Before After
before after
peekReference/PeekReferences/Light
Before After
before after
agentSessionsViewer/CompletedUnread/Dark
Before After
before after
agentSessionsViewer/CompletedUnread/Light
Before After
before after
agentSessionsViewer/WithDiffChanges/Dark
Before After
before after
agentSessionsViewer/WithDiffChanges/Light
Before After
before after
agentSessionsViewer/WithMarkdownBadge/Dark
Before After
before after
agentSessionsViewer/WithMarkdownBadge/Light
Before After
before after

@DonJayamanne DonJayamanne merged commit b825817 into main Apr 14, 2026
26 checks passed
@DonJayamanne DonJayamanne deleted the don/super-herring branch April 14, 2026 08:00
@vs-code-engineering vs-code-engineering bot added this to the 1.117.0 milestone Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants