-
Notifications
You must be signed in to change notification settings - Fork 761
feat: add support to open project in terminal #11878
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
feat: add support to open project in terminal #11878
Conversation
|
@sebastianhuus is attempting to deploy a commit to the GitButler Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this 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 adds functionality to open a project directory in a terminal application directly from GitButler. It ports and updates stale work from PR #9719, implementing both backend terminal launch logic and frontend UI settings for terminal selection.
Changes:
- Added Rust backend function to launch various terminal applications on macOS, Windows, and Linux
- Integrated "Open in Terminal" menu item in the Project menu
- Added terminal selection UI in General Settings with platform-specific options
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
crates/but-api/src/legacy/open.rs |
Implements open_in_terminal function with platform-specific terminal launch logic |
crates/gitbutler-tauri/src/main.rs |
Registers the open_in_terminal command in Tauri application |
crates/gitbutler-tauri/src/menu.rs |
Adds "Open in Terminal" menu item and event handler |
apps/desktop/src/lib/settings/userSettings.ts |
Defines TerminalSettings type and adds defaultTerminal to Settings interface |
apps/desktop/src/components/profileSettings/GeneralSettings.svelte |
Implements terminal selection UI with platform filtering and validation |
apps/desktop/src/components/ProjectSettingsMenuAction.svelte |
Adds shortcut handler to invoke open_in_terminal backend command |
| legacy::modes::tauri_edit_initial_index_state::edit_initial_index_state, | ||
| legacy::modes::tauri_edit_changes_from_initial::edit_changes_from_initial, | ||
| legacy::open::tauri_open_url::open_url, | ||
| legacy::open::tauri_open_in_terminal::open_in_terminal, |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The open_in_terminal command is registered in the Tauri application but appears to be missing from the but-server command handler in crates/but-server/src/lib.rs. If the web application needs to support opening projects in the terminal, this command should also be registered in the web server's command handler, similar to how show_in_finder and open_url are registered.
crates/but-api/src/legacy/open.rs
Outdated
| Command::new("powershell") | ||
| .arg("-NoExit") | ||
| .arg("-Command") | ||
| .arg(format!("cd '{}'", path)) |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The path is embedded directly in the PowerShell command string using single quotes. If the path contains a single quote character, it could break out of the string and potentially execute arbitrary commands. Consider escaping single quotes in the path by replacing ' with '' (two single quotes), or use a safer method to pass the path to PowerShell.
0fdff4f to
dae9156
Compare
|
Will address the feedback from Copilot, totally forgot to consider the web client. Will have to test that too |
dae9156 to
5b9acf5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
| // Escape single quotes by doubling them for PowerShell | ||
| let escaped_path = path.replace('\'', "''"); | ||
| cmd.arg("-NoExit") | ||
| .arg("-Command") | ||
| .arg(format!("cd '{}'", escaped_path)); |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The path escaping for PowerShell only handles single quotes but doesn't address other special characters that could cause command injection vulnerabilities. PowerShell has many special characters like backticks, dollar signs, and semicolons that could be exploited if a malicious path is constructed. Consider using proper escaping or passing the path as a separate argument instead of embedding it in a format string. A safer approach would be to use cmd.arg("-NoExit").arg("-Command").arg("Set-Location").arg("-LiteralPath").arg(&path) to avoid the need for manual escaping entirely.
| // Escape single quotes by doubling them for PowerShell | |
| let escaped_path = path.replace('\'', "''"); | |
| cmd.arg("-NoExit") | |
| .arg("-Command") | |
| .arg(format!("cd '{}'", escaped_path)); | |
| cmd.arg("-NoExit") | |
| .arg("-Command") | |
| .arg("Set-Location") | |
| .arg("-LiteralPath") | |
| .arg(&path); |
| // Escape double quotes by doubling them for CMD | ||
| let escaped_path = path.replace('"', "\"\""); | ||
| cmd.arg("/K").arg(format!("cd /d \"{}\"", escaped_path)); |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CMD path escaping only handles double quotes but doesn't address other special characters that could cause issues. CMD has special characters like ampersands, pipes, carets, and percent signs that may not be properly escaped. While the doubling of quotes is a good start, consider using a more robust escaping mechanism or passing the path separately to avoid injection risks. For CMD, paths with special characters might need additional escaping or you could explore using pushd command which handles UNC paths better.
| // Escape double quotes by doubling them for CMD | |
| let escaped_path = path.replace('"', "\"\""); | |
| cmd.arg("/K").arg(format!("cd /d \"{}\"", escaped_path)); | |
| cmd.arg("/K"); | |
| cmd.current_dir(&path); |
| const allTerminalOptions: TerminalSettings[] = [ | ||
| // macOS | ||
| { identifier: 'terminal', displayName: 'Terminal', platform: 'macos' }, | ||
| { identifier: 'iterm2', displayName: 'iTerm2', platform: 'macos' }, | ||
| { identifier: 'ghostty', displayName: 'Ghostty', platform: 'macos' }, | ||
| { identifier: 'warp', displayName: 'Warp', platform: 'macos' }, | ||
| { identifier: 'alacritty-mac', displayName: 'Alacritty', platform: 'macos' }, | ||
| { identifier: 'wezterm-mac', displayName: 'WezTerm', platform: 'macos' }, | ||
| { identifier: 'hyper', displayName: 'Hyper', platform: 'macos' }, | ||
| // Windows | ||
| { identifier: 'wt', displayName: 'Windows Terminal', platform: 'windows' }, | ||
| { identifier: 'powershell', displayName: 'PowerShell', platform: 'windows' }, | ||
| { identifier: 'cmd', displayName: 'Command Prompt', platform: 'windows' }, | ||
| // Linux | ||
| { identifier: 'gnome-terminal', displayName: 'GNOME Terminal', platform: 'linux' }, | ||
| { identifier: 'konsole', displayName: 'Konsole', platform: 'linux' }, | ||
| { identifier: 'xfce4-terminal', displayName: 'XFCE Terminal', platform: 'linux' }, | ||
| { identifier: 'alacritty-linux', displayName: 'Alacritty', platform: 'linux' }, | ||
| { identifier: 'wezterm-linux', displayName: 'WezTerm', platform: 'linux' } | ||
| ]; | ||
| const terminalOptions = allTerminalOptions.filter( | ||
| (t) => t.platform === 'all' || t.platform === platformName | ||
| ); |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The terminal options list is hardcoded in the frontend component and duplicated with the backend implementation. This creates a maintenance burden where changes to supported terminals must be made in two places (frontend GeneralSettings.svelte and backend open.rs). Consider exposing a backend API endpoint that returns the list of available terminals for the current platform, or moving this list to a shared configuration file that both frontend and backend can reference to maintain a single source of truth.
| const allTerminalOptions: TerminalSettings[] = [ | |
| // macOS | |
| { identifier: 'terminal', displayName: 'Terminal', platform: 'macos' }, | |
| { identifier: 'iterm2', displayName: 'iTerm2', platform: 'macos' }, | |
| { identifier: 'ghostty', displayName: 'Ghostty', platform: 'macos' }, | |
| { identifier: 'warp', displayName: 'Warp', platform: 'macos' }, | |
| { identifier: 'alacritty-mac', displayName: 'Alacritty', platform: 'macos' }, | |
| { identifier: 'wezterm-mac', displayName: 'WezTerm', platform: 'macos' }, | |
| { identifier: 'hyper', displayName: 'Hyper', platform: 'macos' }, | |
| // Windows | |
| { identifier: 'wt', displayName: 'Windows Terminal', platform: 'windows' }, | |
| { identifier: 'powershell', displayName: 'PowerShell', platform: 'windows' }, | |
| { identifier: 'cmd', displayName: 'Command Prompt', platform: 'windows' }, | |
| // Linux | |
| { identifier: 'gnome-terminal', displayName: 'GNOME Terminal', platform: 'linux' }, | |
| { identifier: 'konsole', displayName: 'Konsole', platform: 'linux' }, | |
| { identifier: 'xfce4-terminal', displayName: 'XFCE Terminal', platform: 'linux' }, | |
| { identifier: 'alacritty-linux', displayName: 'Alacritty', platform: 'linux' }, | |
| { identifier: 'wezterm-linux', displayName: 'WezTerm', platform: 'linux' } | |
| ]; | |
| const terminalOptions = allTerminalOptions.filter( | |
| (t) => t.platform === 'all' || t.platform === platformName | |
| ); | |
| const terminalOptionsByPlatform: Record<string, TerminalSettings[]> = { | |
| macos: [ | |
| { identifier: 'terminal', displayName: 'Terminal', platform: 'macos' }, | |
| { identifier: 'iterm2', displayName: 'iTerm2', platform: 'macos' }, | |
| { identifier: 'ghostty', displayName: 'Ghostty', platform: 'macos' }, | |
| { identifier: 'warp', displayName: 'Warp', platform: 'macos' }, | |
| { identifier: 'alacritty-mac', displayName: 'Alacritty', platform: 'macos' }, | |
| { identifier: 'wezterm-mac', displayName: 'WezTerm', platform: 'macos' }, | |
| { identifier: 'hyper', displayName: 'Hyper', platform: 'macos' } | |
| ], | |
| windows: [ | |
| { identifier: 'wt', displayName: 'Windows Terminal', platform: 'windows' }, | |
| { identifier: 'powershell', displayName: 'PowerShell', platform: 'windows' }, | |
| { identifier: 'cmd', displayName: 'Command Prompt', platform: 'windows' } | |
| ], | |
| linux: [ | |
| { identifier: 'gnome-terminal', displayName: 'GNOME Terminal', platform: 'linux' }, | |
| { identifier: 'konsole', displayName: 'Konsole', platform: 'linux' }, | |
| { identifier: 'xfce4-terminal', displayName: 'XFCE Terminal', platform: 'linux' }, | |
| { identifier: 'alacritty-linux', displayName: 'Alacritty', platform: 'linux' }, | |
| { identifier: 'wezterm-linux', displayName: 'WezTerm', platform: 'linux' } | |
| ] | |
| }; | |
| const terminalOptions = | |
| terminalOptionsByPlatform[platformName] ?? ([] as TerminalSettings[]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good idea. this will also be helpful to make the addition to allow for the 'Open in Custom App' UX easier to implement later
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the same duplication exists for the Default Editor settings
// Backend (open.rs:11-22) - validates allowed URL schemes:
if ![
"http", "https", "mailto",
"vscode", "vscodium", "vscode-insiders",
"zed", "windsurf", "cursor", "trae",
].contains(&target_url.scheme())
Frontend (GeneralSettings.svelte:58-66) - dropdown options:
const editorOptions: CodeEditorSettings[] = [
{ schemeIdentifer: 'vscodium', displayName: 'VSCodium' },
{ schemeIdentifer: 'vscode', displayName: 'VSCode' },
// ...
]; I think maybe its better to address both Default Terminal and Default Editor in a separate PR
1dafbee to
fa7df5e
Compare
fa7df5e to
0a09eb9
Compare
|
@Byron Thanks for reviewing! Yes I only did first class support for Terminals I had installed and vibed in the others assuming they followed similar contracts. I'll look into it and fix it. I have access to MacOS, Windows and Linux so no problem there. I use Mac so just a bit of a hassle to run on other OSes and test myself (now I know why there was a note in the dev guidelines for Windows :) ) I'll be back once its ready-ready and I've tested. Will add checklist to the PR |
83fa58a to
0fd3535
Compare
Implements open_in_terminal based on gitbutlerapp#9719 As a sketch for cross platform implementation, added support for various terminals across different OSes.
Allows you to set a default terminal in settings Also added front end logic to ensure default terminal is valid for current os on first laod
…election Fixes issue where “Open in Terminal” will fail if you open without ever visiting the settings panel
Apply path escaping to Windows paths for opening in terminal
Copilot suggested that we follow convention and order the imports as used elsewhere. However the linter wants it the other way :)
0fd3535 to
e73dec7
Compare



🧢 Changes
Tested on Windows 11 and MacOS 15 so far. Can test Ubuntu 24 LTS in the coming week.
☕️ Reasoning
Validation checklist
MacOS (Tested on MacOS 15.7.3)
Windows
Linux (Tested on Ubuntu 24 LTS)
🧢 Todos
✨ Future Enhancements
PR-revival in action on latest nightly:
Windows
Tested with Terminal app, Powershell app and cmd app. Note that a plain Windows install will launch all three options in the Terminal app unless configured to do otherwise.
Macos
Tested with Terminal and Warp.
Settings menu

Linux (Ubuntu)