diff --git a/packages/treebeard/src/bun/services/launcher.test.ts b/packages/treebeard/src/bun/services/launcher.test.ts index 4568330..b3717f9 100644 --- a/packages/treebeard/src/bun/services/launcher.test.ts +++ b/packages/treebeard/src/bun/services/launcher.test.ts @@ -29,13 +29,13 @@ describe('launcher service', () => { ) }) - it('launches ghostty with open -a and path argument', async () => { + it('launches ghostty with AppleScript and sets tab title', async () => { const spawn = setBunSpawnQueue([{ stdout: '' }]) - await launchGhostty('/repo/worktree') + await launchGhostty('/Users/user/projects/node-commons/this-is-the-worktree') expect(spawn).toHaveBeenCalledWith( - ['open', '-a', 'Ghostty.app', '/repo/worktree'], + ['/usr/bin/osascript', '-e', expect.stringContaining('tell application "Ghostty"')], expect.objectContaining({ stdout: 'ignore', stderr: 'ignore' }) ) }) diff --git a/packages/treebeard/src/bun/services/launcher.ts b/packages/treebeard/src/bun/services/launcher.ts index f46deae..f6f63cd 100644 --- a/packages/treebeard/src/bun/services/launcher.ts +++ b/packages/treebeard/src/bun/services/launcher.ts @@ -12,15 +12,32 @@ export async function launchIde(ideId: IdeId, worktreePath: string): Promise { - // Pass the path as a file argument so open(1) forwards it to the running - // instance via application:openFile:, which Ghostty maps to a new tab/window - // in the correct directory without spawning a second process. - const env = await getShellEnv() - Bun.spawn(['open', '-a', 'Ghostty.app', worktreePath], { - stdout: 'ignore', - stderr: 'ignore', - env - }) + // Use AppleScript to switch to an existing Ghostty tab for this worktree, + // or open a new tab in the correct directory if none exists. + // This ensures proper tab naming and tab management like the opencode launcher. + const pathParts = worktreePath.split('/') + const worktreeName = pathParts.pop() || '' + const projectName = pathParts.pop() || '' + const tabTitle = `${projectName} / ${worktreeName}` + const script = ` +tell application "Ghostty" + set targetPath to "${worktreePath}" + set targetWindow to window 1 + repeat with t in every tab of targetWindow + set term to focused terminal of t + if working directory of term is targetPath then + select tab t + focus term + return + end if + end repeat + set cfg to new surface configuration + set initial working directory of cfg to targetPath + set newTab to new tab in targetWindow with configuration cfg + perform action "set_tab_title:${tabTitle}" on (focused terminal of newTab) +end tell +` + Bun.spawn(['/usr/bin/osascript', '-e', script], { stdout: 'ignore', stderr: 'ignore' }) } export async function launchOpencode(worktreePath: string): Promise { diff --git a/packages/treebeard/src/components/LaunchButtons.tsx b/packages/treebeard/src/components/LaunchButtons.tsx index 7837239..8ed9950 100644 --- a/packages/treebeard/src/components/LaunchButtons.tsx +++ b/packages/treebeard/src/components/LaunchButtons.tsx @@ -2,7 +2,6 @@ import { useEffect, useState } from 'react' import { ActionIcon, Group, Tooltip } from '@mantine/core' import { IconGhost } from '@tabler/icons-react' import { IdeIcon } from './IdeIcon' -import { OpencodeIcon } from './OpencodeIcon' import { IDE_REGISTRY } from '../shared/ide-registry' import { rpc } from '../rpc' import type { IdeId } from '../shared/types' @@ -14,11 +13,6 @@ interface LaunchButtonsProps { export function LaunchButtons({ worktreePath, defaultIde }: LaunchButtonsProps) { const ide = IDE_REGISTRY[defaultIde] - const [opencodePath, setOpencodePath] = useState(null) - - useEffect(() => { - rpc().request['system:opencodePath']({}).then(setOpencodePath).catch(() => setOpencodePath(null)) - }, []) const handleIde = async () => { await rpc().request['launch:ide']({ ideId: defaultIde, worktreePath }) @@ -28,10 +22,6 @@ export function LaunchButtons({ worktreePath, defaultIde }: LaunchButtonsProps) await rpc().request['launch:ghostty']({ worktreePath }) } - const handleOpencode = async () => { - await rpc().request['launch:opencode']({ worktreePath }) - } - return ( @@ -44,13 +34,6 @@ export function LaunchButtons({ worktreePath, defaultIde }: LaunchButtonsProps) - {opencodePath && ( - - - - - - )} ) }