Skip to content

feat(plan): support opening and modifying plan in external editor#20348

Merged
jacob314 merged 7 commits intomainfrom
adibakm/open-edit-plan-file-ide
Feb 26, 2026
Merged

feat(plan): support opening and modifying plan in external editor#20348
jacob314 merged 7 commits intomainfrom
adibakm/open-edit-plan-file-ide

Conversation

@Adib234
Copy link
Contributor

@Adib234 Adib234 commented Feb 25, 2026

Summary

Support opening and modifying the current plan in an external editor during Plan Mode approval.

Details

  • Added Ctrl+X shortcut to ExitPlanModeDialog to open the plan file in an external editor.
  • The dialog content is refreshed automatically after the editor is closed.
  • Refactored editor spawning logic into a common editorUtils.ts utility, which is also now used by text-buffer.ts.
  • Plumbed getPreferredEditor through UIActions to ensure the user's preferred editor is respected.
  • Updated AskUserDialog and DialogFooter to support custom keyboard shortcut hints.

Related Issues

Closes #17721

How to Validate

  1. Enter Plan Mode: gemini --plan "some task"
  2. Wait for the plan to be generated.
  3. In the plan approval dialog, press Ctrl+X.
  4. The plan file should open in your default or configured editor.
  5. Modify the plan (e.g., add/remove a step) and save/close the editor.
  6. The CLI should refresh and show the updated plan.

To test opening with a terminal editor like vim make sure that you have a terminal editor selected in /editor, same applies with testing with an IDE

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • Validated on required platforms/methods:
    • MacOS
      • npm run
      • npx
      • Docker
      • Podman
      • Seatbelt

@Adib234 Adib234 requested a review from a team as a code owner February 25, 2026 21:19
@Adib234 Adib234 self-assigned this Feb 25, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Adib234, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the user experience in Plan Mode by allowing direct interaction with plan files using external editors. It centralizes editor management, ensures user preferences are respected, and provides immediate feedback on changes, making the plan approval workflow more efficient and adaptable to individual user habits.

Highlights

  • External Editor Support for Plan Mode: Introduced the ability to open and modify the current plan in an external editor directly from the Plan Mode approval dialog, enhancing user flexibility during the planning process.
  • New Keyboard Shortcut: Added a Ctrl+P shortcut within the ExitPlanModeDialog to quickly open the plan file in the user's preferred external editor.
  • Automatic Plan Refresh: Implemented automatic refreshing of the plan content in the CLI after the external editor is closed, ensuring that any modifications made are immediately reflected.
  • Centralized Editor Spawning Logic: Refactored the logic for spawning external editors into a new editorUtils.ts utility, which is now utilized by both the Plan Mode dialog and the text-buffer.ts component, promoting code reuse and maintainability.
  • Configurable Preferred Editor: Integrated the getPreferredEditor function through the UIActions context, allowing the CLI to respect the user's configured editor preferences when opening files.
  • Enhanced Dialog Footer: Updated AskUserDialog and DialogFooter components to support custom keyboard shortcut hints, providing more informative UI feedback to the user.
Changelog
  • packages/cli/src/config/keyBindings.ts
    • Updated the keybinding for OPEN_EXTERNAL_EDITOR command from Ctrl+X to Ctrl+P.
  • packages/cli/src/test-utils/render.tsx
    • Added a mock for getPreferredEditor to the mockUIActions object for testing purposes.
  • packages/cli/src/ui/AppContainer.tsx
    • Passed the getPreferredEditor function to the UIActions context provider.
  • packages/cli/src/ui/components/AskUserDialog.tsx
    • Added an extraParts prop to AskUserDialogProps and ReviewViewProps to allow custom keyboard shortcut hints.
    • Passed the new extraParts prop to the DialogFooter component.
  • packages/cli/src/ui/components/ExitPlanModeDialog.test.tsx
    • Imported openFileInEditor and mocked it for testing.
    • Added getPreferredEditor prop to ExitPlanModeDialog renders in tests.
    • Included a new test case to verify that Ctrl+P opens the plan in an external editor and refreshes the content.
  • packages/cli/src/ui/components/ExitPlanModeDialog.tsx
    • Imported necessary modules for editor utilities, keypress handling, and keybinding formatting.
    • Added getPreferredEditor to ExitPlanModeDialogProps.
    • Introduced useStdin hook to manage terminal raw mode for external editor interaction.
    • Modified usePlanContent to include a refresh function and a version state to trigger content re-fetching after external edits.
    • Implemented handleOpenEditor callback to open the plan file using the new openFileInEditor utility and refresh the plan state.
    • Integrated useKeypress to listen for the OPEN_EXTERNAL_EDITOR command (Ctrl+P) and trigger handleOpenEditor.
    • Added an 'edit plan' hint to the AskUserDialog using the new extraParts prop.
  • packages/cli/src/ui/components/ToolConfirmationQueue.tsx
    • Used useUIActions to retrieve getPreferredEditor from the context.
    • Passed getPreferredEditor down to the ToolConfirmationMessage component.
  • packages/cli/src/ui/components/snapshots/ExitPlanModeDialog.test.tsx.snap
    • Updated UI snapshots to reflect the new 'Ctrl+P to edit plan' hint in the dialog footer.
  • packages/cli/src/ui/components/messages/RedirectionConfirmation.test.tsx
    • Added getPreferredEditor prop with a mock function to ToolConfirmationMessage renders.
  • packages/cli/src/ui/components/messages/ToolConfirmationMessage.test.tsx
    • Added getPreferredEditor prop with a mock function to various ToolConfirmationMessage renders.
  • packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx
    • Added getPreferredEditor to ToolConfirmationMessageProps.
    • Passed getPreferredEditor to the ExitPlanModeDialog component when rendering.
  • packages/cli/src/ui/components/shared/DialogFooter.tsx
    • Added an optional extraParts prop to DialogFooterProps to display additional keyboard shortcut hints.
    • Modified the component to include extraParts in the displayed list of actions.
  • packages/cli/src/ui/components/shared/text-buffer.ts
    • Removed direct spawnSync imports and calls for external editor handling.
    • Integrated the new openFileInEditor utility function to manage external editor interactions.
  • packages/cli/src/ui/utils/editorUtils.ts
    • Added a new utility file editorUtils.ts.
    • Implemented openFileInEditor function to centralize the logic for opening files in external editors.
    • Handled synchronous and asynchronous editor spawning based on editor type (terminal vs. GUI).
    • Managed terminal raw mode switching to ensure proper editor interaction.
    • Included error handling and feedback emission for editor-related issues.
Activity
  • The pull request author, Adib234, has implemented the feature, including refactoring editor logic, adding new UI components, and updating keybindings.
  • The author has provided detailed validation steps and a pre-merge checklist, indicating thorough testing on MacOS with npm run.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@Adib234 Adib234 changed the title feat(cli): support opening and modifying plan in external editor feat(plan): support opening and modifying plan in external editor Feb 25, 2026
@github-actions
Copy link

github-actions bot commented Feb 25, 2026

Size Change: +3.75 kB (+0.01%)

Total Size: 25.7 MB

Filename Size Change
./bundle/gemini.js 25.2 MB +3.75 kB (+0.01%)
ℹ️ View Unchanged
Filename Size
./bundle/node_modules/@google/gemini-cli-devtools/dist/client/main.js 221 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/_client-assets.js 227 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/index.js 11.5 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/types.js 132 B
./bundle/sandbox-macos-permissive-open.sb 890 B
./bundle/sandbox-macos-permissive-proxied.sb 1.31 kB
./bundle/sandbox-macos-restrictive-open.sb 3.36 kB
./bundle/sandbox-macos-restrictive-proxied.sb 3.56 kB
./bundle/sandbox-macos-strict-open.sb 4.82 kB
./bundle/sandbox-macos-strict-proxied.sb 5.02 kB

compressed-size-action

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request adds support for opening and modifying a plan in an external editor, which is a great feature. The implementation involves refactoring editor-spawning logic into a new editorUtils.ts utility, which is a good separation of concerns. I've found one area for improvement in the error handling and resource cleanup logic within the new utility file to make it more robust and align with established best practices and rules.

@Adib234
Copy link
Contributor Author

Adib234 commented Feb 25, 2026

This is a great feature, and the separation of the editor logic into editorUtils.ts is a good architectural choice. The PR title follows Conventional Commits and the tests correctly utilize waitFor and useKeypress per our strict development rules.

However, there are two areas that need improvement before this can be merged:

1. Resource Cleanup and Error Handling in editorUtils.ts (Major)

The cleanup logic (restoring terminal raw mode and emitting ExternalEditorClosed) is currently scattered across cleanup(), catch blocks, and event callbacks.

If spawnSync itself throws an unexpected exception, or if there's a problem during the promise execution, we risk the terminal remaining in a non-raw state, leaving the user with a frozen or unresponsive CLI.

Recommendation: Refactor this to use a strict try...finally block for the synchronous isTerminal branch, ensuring setRawMode?.(true) and coreEvents.emit(...) are always executed. For the asynchronous Promise branch, ensure the cleanup runs when the promise resolves or rejects regardless of errors.

2. Excessive Re-renders in ExitPlanModeDialog.tsx (Minor)

In ExitPlanModeDialog.tsx, the usePlanContent hook returns a new object on every render:

return { ...state, refresh };

Then, handleOpenEditor includes the entire planState object in its useCallback dependency array:

const handleOpenEditor = useCallback(async () => {
  await openFileInEditor(planPath, stdin, setRawMode, getPreferredEditor());
  planState.refresh();
}, [planPath, stdin, setRawMode, getPreferredEditor, planState]);

This defeats the purpose of useCallback as handleOpenEditor will be redefined on every single render.

Recommendation: Destructure refresh from planState and only include refresh in the dependency array.

@gemini-cli gemini-cli bot added area/core Issues related to User Interface, OS Support, Core Functionality 🔒 maintainer only ⛔ Do not contribute. Internal roadmap item. labels Feb 25, 2026
@Adib234
Copy link
Contributor Author

Adib234 commented Feb 25, 2026

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request successfully adds the feature to open and modify a plan in an external editor, which is a great enhancement for usability. The refactoring of editor-spawning logic into a dedicated editorUtils.ts file improves code organization. The implementation of the refresh mechanism and the support for custom hints in the dialog footer are well done. However, I've identified a critical issue in how the editor command is executed on non-Windows platforms, which could cause the feature to fail for users with common environment variable configurations. The provided suggestion should resolve this issue.

@Adib234
Copy link
Contributor Author

Adib234 commented Feb 25, 2026

@jerop regarding the comment left here, tool-modifier.ts manages the lifecycle of intercepting tool calls and generating diffs, which doesn't apply to opening a generic file #20050 (comment)

@jacob314 jacob314 self-requested a review February 25, 2026 23:15
@jacob314
Copy link
Contributor

One thing to note in packages/cli/src/ui/utils/editorUtils.ts:

The naive command splitting (const [executable = '', ...initialArgs] = command.split(' ');) will break if the EDITOR or VISUAL environment variable contains spaces in the executable path (e.g., EDITOR="/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl"). I recognize this is an existing issue with how we handle the editor command, but it might be worth addressing now that the logic is centralized.

Comment was from review-frontend manually reviewed by jacob.

Copy link
Contributor

@jacob314 jacob314 left a comment

Choose a reason for hiding this comment

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

lgtm

@jacob314 jacob314 added this pull request to the merge queue Feb 26, 2026
Merged via the queue into main with commit ef247e2 Feb 26, 2026
27 checks passed
@jacob314 jacob314 deleted the adibakm/open-edit-plan-file-ide branch February 26, 2026 04:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core Issues related to User Interface, OS Support, Core Functionality 🔒 maintainer only ⛔ Do not contribute. Internal roadmap item.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support opening and modifying current plan in editor

2 participants