From 1903d153d487e86c8e80e7dcee6e54d43c5b0672 Mon Sep 17 00:00:00 2001 From: Jun Peng <61768526+minorole@users.noreply.github.com> Date: Mon, 16 Feb 2026 10:04:54 -0500 Subject: [PATCH 1/2] fix: explicitly cd to project directory in every non-first pane Panes created after pane 1 relied on Ghostty's directory inheritance, which fails when pane 1 runs a foreground process (e.g. claude, npm run dev). Now every non-first pane gets an explicit cd before its command. --- scripts/layout-dynamic.applescript | 16 ++++++++++++++++ scripts/layout-hybrid.applescript | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/scripts/layout-dynamic.applescript b/scripts/layout-dynamic.applescript index 5dd2b0b..4f686c8 100644 --- a/scripts/layout-dynamic.applescript +++ b/scripts/layout-dynamic.applescript @@ -90,6 +90,16 @@ on run argv repeat with rowIdx from 1 to numRows set panesInRow to item rowIdx of rowCounts + -- Ensure non-first panes start in project directory + -- (pane 1 already got cd at start; row 2+ anchors + -- may not inherit if pane 1 is running a foreground process) + if spatialIdx > 1 then + set the clipboard to "cd " & (quoted form of projectDir) & " && clear" + keystroke "v" using {command down} + key code 36 -- Enter + delay 0.3 + end if + -- Type command for this row's anchor (we're already here) if spatialIdx <= (count of commands) then set cmd to item spatialIdx of commands @@ -108,6 +118,12 @@ on run argv keystroke "d" using {command down} -- Split right delay 0.6 -- Increased for split stability + -- Ensure split pane starts in project directory + set the clipboard to "cd " & (quoted form of projectDir) & " && clear" + keystroke "v" using {command down} + key code 36 -- Enter + delay 0.3 + -- Now in the new split pane, type its command if spatialIdx <= (count of commands) then set cmd to item spatialIdx of commands diff --git a/scripts/layout-hybrid.applescript b/scripts/layout-hybrid.applescript index 5f68cbc..e4aca67 100644 --- a/scripts/layout-hybrid.applescript +++ b/scripts/layout-hybrid.applescript @@ -105,6 +105,16 @@ on run argv repeat with rowIdx from 1 to numRows set panesInRow to item rowIdx of rowCounts + -- Ensure non-first panes start in project directory + -- (pane 1 already got cd at tab start; row 2+ anchors + -- may not inherit if pane 1 is running a foreground process) + if spatialIdx > 1 then + set the clipboard to "cd " & (quoted form of projectDir) & " && clear" + keystroke "v" using {command down} + key code 36 -- Enter + delay 0.3 + end if + -- Type command for this row's anchor (we're already here) set cmdIndex to tabCmdBase + spatialIdx if cmdIndex <= (count of commands) then @@ -124,6 +134,12 @@ on run argv keystroke "d" using {command down} -- Split right delay 0.6 -- Increased for split stability + -- Ensure split pane starts in project directory + set the clipboard to "cd " & (quoted form of projectDir) & " && clear" + keystroke "v" using {command down} + key code 36 -- Enter + delay 0.3 + -- Now in the new split pane, type its command set cmdIndex to tabCmdBase + spatialIdx if cmdIndex <= (count of commands) then From 3716f3af6d181d1a3affbf2be7541dd76b85a6fa Mon Sep 17 00:00:00 2001 From: Jun Peng <61768526+minorole@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:50:27 -0500 Subject: [PATCH 2/2] fix: move pane directory handling from AppleScript to zsh orchestration layer Prepend cd to every command in zsh before passing to AppleScript, making each pane command self-contained. Removes 6 fragile cd-injection blocks from AppleScript that depended on clipboard timing delays. --- lib/layouts.zsh | 40 +++++++++++++++++++++++++++--- scripts/layout-dynamic.applescript | 22 ---------------- scripts/layout-hybrid.applescript | 22 ---------------- 3 files changed, 37 insertions(+), 47 deletions(-) diff --git a/lib/layouts.zsh b/lib/layouts.zsh index 8b7de07..538c513 100644 --- a/lib/layouts.zsh +++ b/lib/layouts.zsh @@ -53,6 +53,24 @@ compute_total_panes() { echo $total } +# Prepend cd to project directory for every pane command +# Empty commands become "cd " +# Non-empty commands become "cd && " +prepend_cd_to_commands() { + local project_dir=$1 + shift + local -a cmds=("$@") + local -a result=() + for cmd in "${cmds[@]}"; do + if [[ -z "$cmd" ]]; then + result+=("cd ${(q)project_dir}") + else + result+=("cd ${(q)project_dir} && ${cmd}") + fi + done + PREPARED_COMMANDS=("${result[@]}") +} + # Validate layout spec # Returns 0 if valid, 1 if invalid (with error message to stderr) validate_layout() { @@ -149,7 +167,16 @@ layout_hybrid() { return 1 fi - run_layout "layout-hybrid" "${label}" "${reuse_window}" "${project_dir}" "${layout_spec}" "${tabs_count}" "${cmds[@]}" + # Pad commands to fill all pane slots across all tabs + local panes_per_tab + panes_per_tab=$(compute_total_panes "$layout_spec") + local total_slots=$((panes_per_tab * tabs_count)) + while (( ${#cmds[@]} < total_slots )); do + cmds+=("") + done + + prepend_cd_to_commands "${project_dir}" "${cmds[@]}" + run_layout "layout-hybrid" "${label}" "${reuse_window}" "${project_dir}" "${layout_spec}" "${tabs_count}" "${PREPARED_COMMANDS[@]}" } # Check for deprecated layout: tabs syntax @@ -196,8 +223,15 @@ layout_dynamic() { return 1 fi - # Pass commands directly in spatial order - AppleScript handles them inline - run_layout "layout-dynamic" "${label}" "${reuse_window}" "${project_dir}" "${layout_spec}" "${spatial_cmds[@]}" + # Pad commands to fill all pane slots + local total_panes + total_panes=$(compute_total_panes "$layout_spec") + while (( ${#spatial_cmds[@]} < total_panes )); do + spatial_cmds+=("") + done + + prepend_cd_to_commands "${project_dir}" "${spatial_cmds[@]}" + run_layout "layout-dynamic" "${label}" "${reuse_window}" "${project_dir}" "${layout_spec}" "${PREPARED_COMMANDS[@]}" } # Run a layout script with progress dots diff --git a/scripts/layout-dynamic.applescript b/scripts/layout-dynamic.applescript index 4f686c8..eec334a 100644 --- a/scripts/layout-dynamic.applescript +++ b/scripts/layout-dynamic.applescript @@ -60,12 +60,6 @@ on run argv set frontmost to true delay 0.5 - -- cd to project directory (this is pane 1, spatial position 0) - set the clipboard to "cd " & (quoted form of projectDir) & " && clear" - keystroke "v" using {command down} - key code 36 -- Enter - delay 0.5 - -- PHASE 1: Create row spine (n-1 vertical splits) -- This creates one anchor pane per row if numRows > 1 then @@ -90,16 +84,6 @@ on run argv repeat with rowIdx from 1 to numRows set panesInRow to item rowIdx of rowCounts - -- Ensure non-first panes start in project directory - -- (pane 1 already got cd at start; row 2+ anchors - -- may not inherit if pane 1 is running a foreground process) - if spatialIdx > 1 then - set the clipboard to "cd " & (quoted form of projectDir) & " && clear" - keystroke "v" using {command down} - key code 36 -- Enter - delay 0.3 - end if - -- Type command for this row's anchor (we're already here) if spatialIdx <= (count of commands) then set cmd to item spatialIdx of commands @@ -118,12 +102,6 @@ on run argv keystroke "d" using {command down} -- Split right delay 0.6 -- Increased for split stability - -- Ensure split pane starts in project directory - set the clipboard to "cd " & (quoted form of projectDir) & " && clear" - keystroke "v" using {command down} - key code 36 -- Enter - delay 0.3 - -- Now in the new split pane, type its command if spatialIdx <= (count of commands) then set cmd to item spatialIdx of commands diff --git a/scripts/layout-hybrid.applescript b/scripts/layout-hybrid.applescript index e4aca67..05b66b4 100644 --- a/scripts/layout-hybrid.applescript +++ b/scripts/layout-hybrid.applescript @@ -75,12 +75,6 @@ on run argv -- cmdIndex = (tabIdx - 1) * panesPerTab + spatialIdx set tabCmdBase to (tabIdx - 1) * panesPerTab - -- cd to project directory (this is pane 1 of current tab) - set the clipboard to "cd " & (quoted form of projectDir) & " && clear" - keystroke "v" using {command down} - key code 36 -- Enter - delay 0.5 - -- === PHASE 1: Create row spine (n-1 vertical splits) === -- This creates one anchor pane per row if numRows > 1 then @@ -105,16 +99,6 @@ on run argv repeat with rowIdx from 1 to numRows set panesInRow to item rowIdx of rowCounts - -- Ensure non-first panes start in project directory - -- (pane 1 already got cd at tab start; row 2+ anchors - -- may not inherit if pane 1 is running a foreground process) - if spatialIdx > 1 then - set the clipboard to "cd " & (quoted form of projectDir) & " && clear" - keystroke "v" using {command down} - key code 36 -- Enter - delay 0.3 - end if - -- Type command for this row's anchor (we're already here) set cmdIndex to tabCmdBase + spatialIdx if cmdIndex <= (count of commands) then @@ -134,12 +118,6 @@ on run argv keystroke "d" using {command down} -- Split right delay 0.6 -- Increased for split stability - -- Ensure split pane starts in project directory - set the clipboard to "cd " & (quoted form of projectDir) & " && clear" - keystroke "v" using {command down} - key code 36 -- Enter - delay 0.3 - -- Now in the new split pane, type its command set cmdIndex to tabCmdBase + spatialIdx if cmdIndex <= (count of commands) then