Skip to content

Conversation

@Shironex
Copy link
Collaborator

@Shironex Shironex commented Jan 17, 2026

Summary

This PR adds several enhancements to the worktree panel and fixes various bugs:

New Features

  • Open in Terminal: Open system terminal directly in a worktree directory
  • View Changes Dialog: View git diffs for any worktree
  • Discard Changes: Discard all uncommitted changes in a worktree with confirmation
  • Improved Conflict Resolution: Choose between pulling from worktree's remote branch or the main branch
  • Branch Tracking Status: Show "new" badge for branches without remote tracking

Bug Fixes

  • FeatureId Sanitization: Sanitize featureId in worktree routes to prevent path traversal issues
  • Mobile Responsiveness: Fix input zoom issues on iOS by using 16px font size
  • Claude Usage Service: Simplify PTY handling and improve ANSI escape code stripping

Other Improvements

  • Add branchName to running agents API for correct diff display
  • Show actual target branch name in merge UI instead of hardcoded "main"
  • Cross-platform terminal opening (macOS, Windows, Linux)

Test plan

  • Test "Open in Terminal" on worktree dropdown menu
  • Test "View Changes" dialog shows correct diffs
  • Test "Discard Changes" properly resets worktree
  • Test conflict resolution options (worktree branch vs main branch)
  • Test on mobile device to verify no input zoom
  • Verify Claude usage tracking still works

Closes

Shironex and others added 7 commits January 17, 2026 18:40
Sanitize featureId using the same pattern as worktree creation
(replace non-alphanumeric chars with dashes) to ensure consistent
path resolution and prevent path traversal issues.

Affected routes: diffs, file-diff, info, status

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Remove complex ConPTY/winpty fallback logic on Windows
- Improve ANSI escape code stripping to handle OSC, CSI, and
  private mode sequences more comprehensively
- Simplify authentication error detection
- Fix reset time parsing for edge cases
- Add tests for ANSI stripping edge cases

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add utility function to open a terminal in a specified directory:
- macOS: Uses Terminal.app via AppleScript
- Windows: Tries Windows Terminal, falls back to cmd
- Linux: Tries common terminal emulators (gnome-terminal,
  konsole, xfce4-terminal, xterm, x-terminal-emulator)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add two new worktree API endpoints:

- POST /open-in-terminal: Opens system terminal in worktree directory
  using the cross-platform openInTerminal utility

- POST /discard-changes: Discards all uncommitted changes in a
  worktree (git reset, checkout, clean)

Both endpoints validate worktreePath and use appropriate middleware.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add multiple new features to the worktree panel:

- Open in Terminal: Opens system terminal in worktree directory,
  navigates to terminal view with pending cwd
- View Changes: New dialog to view git diffs for a worktree
- Discard Changes: Discard all uncommitted changes with confirmation
- Improved conflict resolution: Choose between pulling from
  worktree's remote branch or the main branch
- Branch tracking: Show "new" badge for branches without remote
- Target branch display: Show actual target branch name in merge UI

Also adds:
- pendingTerminalCwd to app store for terminal navigation
- hasRemoteTracking to branch management hook
- API client methods for new endpoints
- TypeScript types for new functionality

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Increase command input font size to 16px to prevent iOS zoom
- Increase branch switch dropdown input font size
- Make dev server logs panel full-screen on mobile with proper
  header spacing for close button

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Include branchName in running agents API response
- Pass branchName to agent output modal for correct diff display
- Also includes featureId sanitization in auto-mode-service
  for worktree path resolution

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@coderabbitai
Copy link

coderabbitai bot commented Jan 17, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Shironex, 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 delivers significant enhancements and crucial bug fixes to the worktree panel, aiming to streamline developer workflows and improve the overall user experience. New capabilities such as direct terminal access, comprehensive change viewing, and a robust discard changes mechanism empower users with more control over their worktrees. The refined conflict resolution and clearer branch tracking status contribute to a more intuitive Git interaction. Additionally, a critical security vulnerability has been addressed, and the UI has been made more responsive across devices, ensuring a stable and user-friendly environment.

Highlights

  • New Worktree Panel Features: Introduced 'Open in Terminal' to launch a system terminal directly in a worktree directory, a 'View Changes' dialog to inspect git diffs for any worktree, and a 'Discard Changes' option with confirmation to reset uncommitted changes.
  • Improved Conflict Resolution: Enhanced the 'Pull & Resolve Conflicts' functionality to allow users to choose between pulling from the worktree's remote branch or the main branch, providing more control over merge strategies.
  • Branch Tracking Status: Added a 'new' badge to indicate branches without remote tracking, improving visibility into the state of local branches.
  • Security Fix: FeatureId Sanitization: Implemented sanitization for featureId in worktree routes and services to prevent potential path traversal vulnerabilities.
  • Mobile Responsiveness & UI Refinements: Fixed input zoom issues on iOS by adjusting font sizes in command inputs and branch switch dropdowns, and updated the merge UI to dynamically display the actual target branch name instead of a hardcoded 'main'.
  • Claude Usage Service Enhancements: Simplified PTY handling by removing Windows ConPTY fallback logic and improved ANSI escape code stripping for more reliable output parsing, especially for usage data and authentication errors.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

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.

@Shironex Shironex added Enhancement Improvements to existing functionality or UI. Testers-Requested Request for others to test an enhancement or bug fix/etc. Do Not Merge Use this label if something should not be merged. labels Jan 17, 2026
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 introduces significant enhancements to the worktree panel, including new features like opening in terminal, viewing changes, and discarding changes, alongside several bug fixes and improvements. The code is generally well-structured. My review focuses on a critical security vulnerability in the new "Open in Terminal" feature and a suggestion to refactor duplicated code for better maintainability.

Comment on lines 384 to 464
export async function openInTerminal(targetPath: string): Promise<{ terminalName: string }> {
if (isMac) {
// Use AppleScript to open Terminal.app in the specified directory
const script = `
tell application "Terminal"
do script "cd ${targetPath.replace(/"/g, '\\"').replace(/\$/g, '\\$')}"
activate
end tell
`;
await execFileAsync('osascript', ['-e', script]);
return { terminalName: 'Terminal' };
} else if (isWindows) {
// Try Windows Terminal first
try {
return await new Promise((resolve, reject) => {
const child: ChildProcess = spawn('wt', ['-d', targetPath], {
shell: true,
stdio: 'ignore',
detached: true,
});
child.unref();

child.on('error', () => {
reject(new Error('Windows Terminal not available'));
});

setTimeout(() => resolve({ terminalName: 'Windows Terminal' }), 100);
});
} catch {
// Fall back to cmd
return await new Promise((resolve, reject) => {
const child: ChildProcess = spawn(
'cmd',
['/c', 'start', 'cmd', '/k', `cd /d "${targetPath}"`],
{
shell: true,
stdio: 'ignore',
detached: true,
}
);
child.unref();

child.on('error', (err) => {
reject(err);
});

setTimeout(() => resolve({ terminalName: 'Command Prompt' }), 100);
});
}
} else {
// Linux: Try common terminal emulators in order
const terminals = [
{
name: 'GNOME Terminal',
command: 'gnome-terminal',
args: ['--working-directory', targetPath],
},
{ name: 'Konsole', command: 'konsole', args: ['--workdir', targetPath] },
{
name: 'xfce4-terminal',
command: 'xfce4-terminal',
args: ['--working-directory', targetPath],
},
{ name: 'xterm', command: 'xterm', args: ['-e', `cd "${targetPath}" && $SHELL`] },
{
name: 'x-terminal-emulator',
command: 'x-terminal-emulator',
args: ['--working-directory', targetPath],
},
];

for (const terminal of terminals) {
if (await commandExists(terminal.command)) {
await execFileAsync(terminal.command, terminal.args);
return { terminalName: terminal.name };
}
}

throw new Error('No terminal emulator found');
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

The openInTerminal function has multiple command injection vulnerabilities across different platforms due to unsafe path interpolation into shell commands.

  • macOS: The AppleScript do script command executes the cd command with an unquoted path, allowing for arbitrary command execution.
  • Windows: The fallback to cmd uses cd /d "${targetPath}", which is vulnerable if targetPath contains double quotes.
  • Linux: The xterm command constructs a shell command string cd "${targetPath}" && $SHELL, which is also vulnerable.

A malicious targetPath could lead to arbitrary code execution on the user's machine.

To fix this, the function should be refactored to avoid shell interpolation and use platform-specific features for setting the working directory safely. For example:

  • On macOS, pass the path as an argument to osascript and use quoted form of.
  • On Windows, use the cwd option of spawn or the /D flag of the start command.
  • On Linux, use the appropriate argument for each terminal emulator, like -cd for xterm or --working-directory for gnome-terminal.
export async function openInTerminal(targetPath: string): Promise<{ terminalName: string }> {
  if (isMac) {
    // Use AppleScript to safely open Terminal.app in the specified directory.
    // Passing the path as an argument to osascript and using "quoted form of"
    // prevents command injection vulnerabilities.
    const script = `
      on run argv
        tell application "Terminal"
          if not running then
            activate
          end if
          do script "cd " & quoted form of (item 1 of argv) in window 1
          activate
        end tell
      end run
    `;
    try {
      await execFileAsync('osascript', ['-e', script, targetPath]);
      return { terminalName: 'Terminal' };
    } catch (error) {
      // Fallback for when AppleScript fails (e.g., permissions)
      // 'open -a Terminal /path' is safer but opens a new window.
      await execFileAsync('open', ['-a', 'Terminal', targetPath]);
      return { terminalName: 'Terminal' };
    }
  } else if (isWindows) {
    // Try Windows Terminal first, passing the directory as a safe argument.
    try {
      return await new Promise((resolve, reject) => {
        const child: ChildProcess = spawn('wt.exe', ['-d', targetPath], {
          shell: false, // Set to false for security
          stdio: 'ignore',
          detached: true,
        });
        child.unref();
        child.on('error', (err) => {
          // ENOENT means wt.exe is not in PATH.
          if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
            reject(new Error('Windows Terminal not available'));
          } else {
            reject(err);
          }
        });
        // Assume success if it doesn't error out immediately.
        setTimeout(() => resolve({ terminalName: 'Windows Terminal' }), 200);
      });
    } catch {
      // Fall back to cmd, using the cwd option for safety.
      return await new Promise((resolve, reject) => {
        const child: ChildProcess = spawn('cmd.exe', ['/c', 'start', 'cmd.exe', '/k'], {
          cwd: targetPath,
          shell: false,
          stdio: 'ignore',
          detached: true,
        });
        child.unref();
        child.on('error', reject);
        setTimeout(() => resolve({ terminalName: 'Command Prompt' }), 200);
      });
    }
  } else {
    // Linux: Try common terminal emulators in order, using safe arguments.
    const terminals = [
      {
        name: 'GNOME Terminal',
        command: 'gnome-terminal',
        args: ['--working-directory', targetPath],
      },
      { name: 'Konsole', command: 'konsole', args: ['--workdir', targetPath] },
      {
        name: 'xfce4-terminal',
        command: 'xfce4-terminal',
        args: ['--working-directory', targetPath],
      },
      { name: 'xterm', command: 'xterm', args: ['-cd', targetPath] }, // Use -cd for safety
      {
        name: 'x-terminal-emulator',
        command: 'x-terminal-emulator',
        args: ['--working-directory', targetPath],
      },
    ];

    for (const terminal of terminals) {
      if (await commandExists(terminal.command)) {
        await execFileAsync(terminal.command, terminal.args);
        return { terminalName: terminal.name };
      }
    }

    throw new Error('No supported terminal emulator found on your system.');
  }
}

Comment on lines +42 to +45
// Sanitize featureId the same way it's sanitized when creating worktrees
// (see create.ts: branchName.replace(/[^a-zA-Z0-9_-]/g, '-'))
const sanitizedFeatureId = featureId.replace(/[^a-zA-Z0-9_-]/g, '-');
const worktreePath = path.join(projectPath, '.worktrees', sanitizedFeatureId);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This featureId sanitization logic is duplicated in multiple places. This reduces maintainability, as any change to the sanitization rule would need to be updated in all locations. Consider creating a shared helper function to centralize this logic.

This logic is repeated in:

  • apps/server/src/routes/worktree/routes/file-diff.ts
  • apps/server/src/routes/worktree/routes/info.ts
  • apps/server/src/routes/worktree/routes/status.ts
  • apps/server/src/services/auto-mode-service.ts

A shared function like getWorktreePath(projectPath, featureId) could be created in a common utility file.

Shironex and others added 2 commits January 17, 2026 18:56
- Fix Windows crash: Remove SIGTERM argument from pty.kill() since
  Windows doesn't support Unix signals
- Fix command injection in openInTerminal:
  - macOS: Use AppleScript's "quoted form of" for safe path handling
  - Windows: Use shell: false and cwd option instead of shell interpolation
  - Linux: Use -cd flag for xterm instead of shell command

Addresses PR review comments from #558.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
ConPTY requires AttachConsole which fails when running in Electron
or service mode without a console. Force winpty on Windows by setting
useConpty: false in PTY spawn options.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
stefandevo added a commit to stefandevo/automaker that referenced this pull request Jan 17, 2026
Add POST /open-in-terminal endpoint to open a system terminal in the
worktree directory using the cross-platform openInTerminal utility.

The endpoint validates that worktreePath is provided and is an
absolute path for security.

Extracted from PR AutoMaker-Org#558.
stefandevo added a commit to stefandevo/automaker that referenced this pull request Jan 17, 2026
Add "Open in Terminal" option to the worktree actions dropdown menu.
This opens the system terminal in the worktree directory.

Changes:
- Add openInTerminal method to http-api-client
- Add Terminal icon and menu item to worktree-actions-dropdown
- Add onOpenInTerminal prop to WorktreeTab component
- Add handleOpenInTerminal handler to use-worktree-actions hook
- Wire up handler in worktree-panel for both mobile and desktop views

Extracted from PR AutoMaker-Org#558.
stefandevo added a commit to stefandevo/automaker that referenced this pull request Jan 17, 2026
Instead of opening the system terminal, the "Open in Terminal" action
now opens Automaker's built-in terminal with the worktree directory:

- Add pendingTerminalCwd state to app store
- Update use-worktree-actions to set pending cwd and navigate to /terminal
- Add effect in terminal-view to create session with pending cwd

This matches the original PR AutoMaker-Org#558 behavior.
stefandevo added a commit to stefandevo/automaker that referenced this pull request Jan 17, 2026
Add support for opening worktree directories in external terminals
(iTerm2, Warp, Ghostty, System Terminal, etc.) while retaining the
integrated terminal as the default option.

Changes:
- Add terminal detection for macOS, Windows, and Linux
- Add "Open in Terminal" split-button in worktree dropdown
- Add external terminal selection in Settings > Terminal
- Add default open mode setting (new tab vs split)
- Display branch name in terminal panel header
- Support 20+ terminals across platforms

Part of AutoMaker-Org#558, Closes AutoMaker-Org#550
Base automatically changed from v0.12.0rc to main January 17, 2026 23:50
stefandevo added a commit to stefandevo/automaker that referenced this pull request Jan 18, 2026
Add POST /open-in-terminal endpoint to open a system terminal in the
worktree directory using the cross-platform openInTerminal utility.

The endpoint validates that worktreePath is provided and is an
absolute path for security.

Extracted from PR AutoMaker-Org#558.
stefandevo added a commit to stefandevo/automaker that referenced this pull request Jan 18, 2026
Add "Open in Terminal" option to the worktree actions dropdown menu.
This opens the system terminal in the worktree directory.

Changes:
- Add openInTerminal method to http-api-client
- Add Terminal icon and menu item to worktree-actions-dropdown
- Add onOpenInTerminal prop to WorktreeTab component
- Add handleOpenInTerminal handler to use-worktree-actions hook
- Wire up handler in worktree-panel for both mobile and desktop views

Extracted from PR AutoMaker-Org#558.
stefandevo added a commit to stefandevo/automaker that referenced this pull request Jan 18, 2026
Instead of opening the system terminal, the "Open in Terminal" action
now opens Automaker's built-in terminal with the worktree directory:

- Add pendingTerminalCwd state to app store
- Update use-worktree-actions to set pending cwd and navigate to /terminal
- Add effect in terminal-view to create session with pending cwd

This matches the original PR AutoMaker-Org#558 behavior.
stefandevo added a commit to stefandevo/automaker that referenced this pull request Jan 18, 2026
Add support for opening worktree directories in external terminals
(iTerm2, Warp, Ghostty, System Terminal, etc.) while retaining the
integrated terminal as the default option.

Changes:
- Add terminal detection for macOS, Windows, and Linux
- Add "Open in Terminal" split-button in worktree dropdown
- Add external terminal selection in Settings > Terminal
- Add default open mode setting (new tab vs split)
- Display branch name in terminal panel header
- Support 20+ terminals across platforms

Part of AutoMaker-Org#558, Closes AutoMaker-Org#550
stefandevo added a commit to stefandevo/automaker that referenced this pull request Jan 18, 2026
Add POST /open-in-terminal endpoint to open a system terminal in the
worktree directory using the cross-platform openInTerminal utility.

The endpoint validates that worktreePath is provided and is an
absolute path for security.

Extracted from PR AutoMaker-Org#558.
stefandevo added a commit to stefandevo/automaker that referenced this pull request Jan 18, 2026
Add "Open in Terminal" option to the worktree actions dropdown menu.
This opens the system terminal in the worktree directory.

Changes:
- Add openInTerminal method to http-api-client
- Add Terminal icon and menu item to worktree-actions-dropdown
- Add onOpenInTerminal prop to WorktreeTab component
- Add handleOpenInTerminal handler to use-worktree-actions hook
- Wire up handler in worktree-panel for both mobile and desktop views

Extracted from PR AutoMaker-Org#558.
stefandevo added a commit to stefandevo/automaker that referenced this pull request Jan 18, 2026
Instead of opening the system terminal, the "Open in Terminal" action
now opens Automaker's built-in terminal with the worktree directory:

- Add pendingTerminalCwd state to app store
- Update use-worktree-actions to set pending cwd and navigate to /terminal
- Add effect in terminal-view to create session with pending cwd

This matches the original PR AutoMaker-Org#558 behavior.
stefandevo added a commit to stefandevo/automaker that referenced this pull request Jan 18, 2026
Add support for opening worktree directories in external terminals
(iTerm2, Warp, Ghostty, System Terminal, etc.) while retaining the
integrated terminal as the default option.

Changes:
- Add terminal detection for macOS, Windows, and Linux
- Add "Open in Terminal" split-button in worktree dropdown
- Add external terminal selection in Settings > Terminal
- Add default open mode setting (new tab vs split)
- Display branch name in terminal panel header
- Support 20+ terminals across platforms

Part of AutoMaker-Org#558, Closes AutoMaker-Org#550
stefandevo added a commit that referenced this pull request Jan 19, 2026
* feat(platform): add cross-platform openInTerminal utility

Add utility function to open a terminal in a specified directory:
- macOS: Uses Terminal.app via AppleScript
- Windows: Tries Windows Terminal, falls back to cmd
- Linux: Tries common terminal emulators (gnome-terminal,
  konsole, xfce4-terminal, xterm, x-terminal-emulator)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* feat(server): add open-in-terminal endpoint

Add POST /open-in-terminal endpoint to open a system terminal in the
worktree directory using the cross-platform openInTerminal utility.

The endpoint validates that worktreePath is provided and is an
absolute path for security.

Extracted from PR #558.

* feat(ui): add Open in Terminal action to worktree dropdown

Add "Open in Terminal" option to the worktree actions dropdown menu.
This opens the system terminal in the worktree directory.

Changes:
- Add openInTerminal method to http-api-client
- Add Terminal icon and menu item to worktree-actions-dropdown
- Add onOpenInTerminal prop to WorktreeTab component
- Add handleOpenInTerminal handler to use-worktree-actions hook
- Wire up handler in worktree-panel for both mobile and desktop views

Extracted from PR #558.

* fix(ui): open in terminal navigates to Automaker terminal view

Instead of opening the system terminal, the "Open in Terminal" action
now opens Automaker's built-in terminal with the worktree directory:

- Add pendingTerminalCwd state to app store
- Update use-worktree-actions to set pending cwd and navigate to /terminal
- Add effect in terminal-view to create session with pending cwd

This matches the original PR #558 behavior.

* feat(ui): add terminal open mode setting (new tab vs split)

Add a setting to choose how "Open in Terminal" behaves:
- New Tab: Creates a new tab named after the branch (default)
- Split: Adds to current tab as a split view

Changes:
- Add openTerminalMode setting to terminal state ('newTab' | 'split')
- Update terminal-view to respect the setting
- Add UI in Terminal Settings to toggle the behavior
- Rename pendingTerminalCwd to pendingTerminal with branch name

The new tab mode names tabs after the branch for easy identification.
The split mode is useful for comparing terminals side by side.

* feat(ui): display branch name in terminal header with git icon

- Move branch name display from tab name to terminal header
- Show full branch name (no truncation) with GitBranch icon
- Display branch name for both 'new tab' and 'split' modes
- Persist openTerminalMode setting to server and include in import/export
- Update settings dropdown to simplified "New Tab" label

* feat: add external terminal support with cross-platform detection

Add support for opening worktree directories in external terminals
(iTerm2, Warp, Ghostty, System Terminal, etc.) while retaining the
integrated terminal as the default option.

Changes:
- Add terminal detection for macOS, Windows, and Linux
- Add "Open in Terminal" split-button in worktree dropdown
- Add external terminal selection in Settings > Terminal
- Add default open mode setting (new tab vs split)
- Display branch name in terminal panel header
- Support 20+ terminals across platforms

Part of #558, Closes #550

* fix: address PR review comments

- Add nonce parameter to terminal navigation to allow reopening same
  worktree multiple times
- Fix shell path escaping in editor.ts using single-quote wrapper
- Add validatePathParams middleware to open-in-external-terminal route
- Remove redundant validation block from createOpenInExternalTerminalHandler
- Remove unused pendingTerminal state and setPendingTerminal action
- Remove unused getTerminalInfo function from editor.ts

* fix: address PR review security and validation issues

- Add runtime type check for worktreePath in open-in-terminal handler
- Fix Windows Terminal detection using commandExists before spawn
- Fix xterm shell injection by using sh -c with escapeShellArg
- Use loose equality for null/undefined in useEffectiveDefaultTerminal
- Consolidate duplicate imports from open-in-terminal.js

* chore: update package-lock.json

* fix: use response.json() to prevent disposal race condition in E2E test

Replace response.body() with response.json() in open-existing-project.spec.ts
to fix the "Response has been disposed" error. This matches the pattern used
in other test files.

* Revert "fix: use response.json() to prevent disposal race condition in E2E test"

This reverts commit 36bdf8c.

* fix: address PR review feedback for terminal feature

- Add explicit validation for worktreePath in createOpenInExternalTerminalHandler
- Add aria-label to refresh button in terminal settings for accessibility
- Only show "no terminals" message when not refreshing
- Reset initialCwdHandledRef on failure to allow retries
- Use z.coerce.number() for nonce URL param to handle string coercion
- Preserve branchName when creating layout for empty tab
- Update getDefaultTerminal return type to allow null result

---------

Co-authored-by: Kacper <[email protected]>
Co-authored-by: Claude Opus 4.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Do Not Merge Use this label if something should not be merged. Enhancement Improvements to existing functionality or UI. Testers-Requested Request for others to test an enhancement or bug fix/etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants