Skip to content

Commit d2dda5f

Browse files
committed
fix(cli[load]): Only fire on_project_start when load proceeds
why: on_project_start fired before the session-exists check, so it ran even when the user declined the reattach prompt. what: - Remove early on_project_start block (was before Server creation) - Fire on_project_start inside session-exists block only after user confirms (_confirmed or detached) - Fire on_project_start before new-session build path - Add test_load_on_project_start_skipped_on_decline
1 parent 6c09d5d commit d2dda5f

2 files changed

Lines changed: 72 additions & 17 deletions

File tree

src/tmuxp/cli/load.py

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -676,14 +676,6 @@ def _cleanup_debug() -> None:
676676
# propagate workspace inheritance (e.g. session -> window, window -> pane)
677677
expanded_workspace = loader.trickle(expanded_workspace)
678678

679-
# Run on_project_start hook — fires on every tmuxp load invocation
680-
if "on_project_start" in expanded_workspace:
681-
_hook_cwd = expanded_workspace.get("start_directory")
682-
util.run_hook_commands(
683-
expanded_workspace["on_project_start"],
684-
cwd=_hook_cwd,
685-
)
686-
687679
t = Server( # create tmux server object
688680
socket_name=socket_name,
689681
socket_path=socket_path,
@@ -716,25 +708,41 @@ def _cleanup_debug() -> None:
716708

717709
# Session-exists check — outside spinner so prompt_yes_no is safe
718710
if builder.session_exists(session_name) and not append and not here:
719-
# Run on_project_restart hook — fires when reattaching
720-
if "on_project_restart" in expanded_workspace:
721-
_hook_cwd = expanded_workspace.get("start_directory")
722-
util.run_hook_commands(
723-
expanded_workspace["on_project_restart"],
724-
cwd=_hook_cwd,
725-
)
726-
if not detached and (
711+
_confirmed = not detached and (
727712
answer_yes
728713
or prompt_yes_no(
729714
f"{cli_colors.highlight(session_name)} is already running. Attach?",
730715
default=True,
731716
color_mode=cli_colors.mode,
732717
)
733-
):
718+
)
719+
if _confirmed or detached:
720+
if "on_project_start" in expanded_workspace:
721+
_hook_cwd = expanded_workspace.get("start_directory")
722+
util.run_hook_commands(
723+
expanded_workspace["on_project_start"],
724+
cwd=_hook_cwd,
725+
)
726+
# Run on_project_restart hook — fires when reattaching
727+
if "on_project_restart" in expanded_workspace:
728+
_hook_cwd = expanded_workspace.get("start_directory")
729+
util.run_hook_commands(
730+
expanded_workspace["on_project_restart"],
731+
cwd=_hook_cwd,
732+
)
733+
if _confirmed:
734734
_reattach(builder, cli_colors)
735735
_cleanup_debug()
736736
return None
737737

738+
# Run on_project_start hook — fires before new session build
739+
if "on_project_start" in expanded_workspace:
740+
_hook_cwd = expanded_workspace.get("start_directory")
741+
util.run_hook_commands(
742+
expanded_workspace["on_project_start"],
743+
cwd=_hook_cwd,
744+
)
745+
738746
if _progress_disabled:
739747
_private_path = str(PrivatePath(workspace_file))
740748
result = _dispatch_build(

tests/cli/test_load.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,53 @@ def test_load_on_project_restart_runs_hook(
11961196
session.kill()
11971197

11981198

1199+
def test_load_on_project_start_skipped_on_decline(
1200+
tmp_path: pathlib.Path,
1201+
server: Server,
1202+
monkeypatch: pytest.MonkeyPatch,
1203+
) -> None:
1204+
"""Tmuxp load skips on_project_start when user declines reattach."""
1205+
monkeypatch.delenv("TMUX", raising=False)
1206+
1207+
marker = tmp_path / "start_hook_ran"
1208+
workspace_file = tmp_path / "hook_start_decline.yaml"
1209+
workspace_file.write_text(
1210+
f"""\
1211+
session_name: hook-start-decline
1212+
on_project_start: "touch {marker}"
1213+
windows:
1214+
- window_name: main
1215+
panes:
1216+
- echo hello
1217+
""",
1218+
encoding="utf-8",
1219+
)
1220+
1221+
# First load creates the session
1222+
session = load_workspace(
1223+
workspace_file,
1224+
socket_name=server.socket_name,
1225+
detached=True,
1226+
)
1227+
assert session is not None
1228+
assert marker.exists()
1229+
marker.unlink()
1230+
1231+
# Second load: session exists, user declines reattach
1232+
monkeypatch.setattr(
1233+
"tmuxp.cli.load.prompt_yes_no",
1234+
lambda *a, **kw: False,
1235+
)
1236+
load_workspace(
1237+
workspace_file,
1238+
socket_name=server.socket_name,
1239+
detached=False,
1240+
)
1241+
assert not marker.exists()
1242+
1243+
session.kill()
1244+
1245+
11991246
class ConfigKeyPrecedenceFixture(t.NamedTuple):
12001247
"""Test fixture for config key precedence tests."""
12011248

0 commit comments

Comments
 (0)