Skip to content

feat(THI-244): default cwd setting + new-pane cwd inheritance (sessions mode) - #112

Merged
tdody merged 3 commits into
mainfrom
thibaultdody/thi-244-default-cwd
Jun 7, 2026
Merged

feat(THI-244): default cwd setting + new-pane cwd inheritance (sessions mode)#112
tdody merged 3 commits into
mainfrom
thibaultdody/thi-244-default-cwd

Conversation

@tdody

@tdody tdody commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Summary

Today the +session / +window overlays start panes wherever tmux decides (usually the server-process cwd), so users routinely retype the same paths. This PR ships the Sessions-mode half of THI-244: a configurable default cwd plus the "+window inherits its session's cwd" behavior.

Resolution order (matches the spec's sessions-mode rules):

  1. Per-modal override (one-shot input on each overlay).
  2. For +window: the launching session's first window cwd, looked up server-side. Matches the user's expectation that "+" lands next to peers.
  3. For +session: the configured Default directory setting.
  4. Otherwise None — tmux uses its own default.

What ships

Backend

  • POST /window and POST /session accept an optional CwdBody ({cwd}).
  • _resolve_cwd validates: ~-expansion against the server's $HOME, must be an existing absolute directory. Anything invalid silently resolves to None so a bad setting never breaks new-pane creation with a 4xx.
  • _first_window_cwd looks up the launching session's first window cwd via libtmux; used as the fallback when the body has no explicit cwd.
  • 8 new test cases cover the resolver + both routes' threading paths (explicit cwd, invalid cwd, relative cwd, ~ expansion, first-window fallback).

Frontend

  • New defaultDirectory: string setting (empty default). Persisted to localStorage like every other setting.
  • New Defaults section in SettingsModal with the text input.
  • createSession / createWindow API helpers accept an optional cwd and body-encode it as JSON when provided.
  • NewSessionOverlay prefills its cwd input from settings; the user can override per-modal without mutating the saved setting.
  • NewWindowOverlay adds the same one-shot cwd input; blank means "let the backend fall back to this session's first window cwd".
  • 4 new tests (2 for settings round-trip, 2 for client.ts cwd threading).

Explicitly deferred

The discovery-mode auto-derive half of the spec ("in discovery mode, ignore the global default; use the owning repo's path") depends on:

  1. THI-243 landing.
  2. THI-243's discovery UI exposing per-repo + buttons.

THI-243's MVP doesn't yet expose those (ListView only renders headers, no per-repo create affordance). The hook isn't reachable, so the discovery branch lands as a small additive change in a follow-up. This PR's resolution function already has the right shape — adding a (repoKey?: string) => string branch on top will be a one-liner.

Test plan (automated)

  • Backend: 380 tests pass (uv run pytest). ruff check + ruff format --check: clean.
  • Frontend: 553 tests pass (npm test). Typecheck + build: clean.
  • New tests: 8 backend (resolver + 2 routes) + 2 client.ts + 2 settings = 12 new tests.

Manual smoke (needs a live tmux server — only the user can run this)

Settings UI

  • Open Settings → scroll to Defaults. The "Default directory for new sessions" input is present.
  • Enter ~/dev (or some path you have). Close + reopen Settings: the value persists.
  • Enter a garbage path like /nope/nope/nope. Save it. (Backend will silently drop on use — no toast / error in Settings itself.)

New session

  • Click "+ session". The cwd input is prefilled with the configured Default directory.
  • Create the session, focus into it in tmux (tmux attach), run pwd. It should be the configured directory (or $HOME if you used ~).
  • With the garbage path saved as default, create a new session. It should still succeed (no error), landing in tmux's fallback dir (the switchboard server-process cwd).
  • In the modal, override the prefill with a different path. Confirm pwd matches the override, not the saved default.

New window

  • Pick an existing session whose first window has a known cwd (e.g. ~/foo).
  • Click "+ window" on that session — leave cwd blank.
  • tmux attach, jump to the new window, pwd. It should match the launching session's first window cwd.
  • Try "+ window" with an explicit cwd override. Confirm pwd matches the override.
  • Try "+ window" with a non-existent cwd like /nope/nope. Confirm the window is created (no error) and lands in tmux's fallback.

Edge cases

  • Set Default directory to ~ only. New session lands in $HOME (server-side expansion).
  • Set it to a relative path like subdir. New session falls through to tmux's default (relative paths rejected server-side).

🤖 Generated with Claude Code

tdody and others added 3 commits June 7, 2026 10:01
…ns mode)

Today the "+session" / "+window" overlays start panes wherever tmux
decides (usually the server-process cwd), so users routinely retype
the same paths. This PR adds a configurable default cwd plus the
"+window inherits its session's cwd" behavior.

Resolution order (matches the spec's sessions-mode rules):
1. Per-modal override (one-shot input on each overlay).
2. For "+window": the launching session's first window cwd, looked up
   server-side. Matches the user's expectation that "+" lands next to
   peers.
3. For "+session": the configured Default directory setting.
4. Otherwise None — tmux uses its own default.

Backend:
- POST /window and POST /session accept an optional CwdBody (`{cwd}`).
- _resolve_cwd validates: ~-expansion against the server's $HOME,
  must be an existing absolute directory. Anything invalid silently
  resolves to None so a bad setting never breaks new-pane creation
  with a 4xx.
- _first_window_cwd looks up the launching session's first window cwd
  via libtmux; the route uses it when the body has no explicit cwd.
- 8 new test cases cover the resolver + both routes' threading paths.

Frontend:
- New `defaultDirectory: string` setting (empty default). Persisted
  to localStorage like every other setting.
- New "Defaults" section in SettingsModal with the text input.
- createSession / createWindow API helpers accept an optional cwd
  and body-encode it as JSON when provided.
- NewSessionOverlay prefills its cwd input from settings; the user
  can override per-modal without mutating the saved setting.
- NewWindowOverlay adds the same one-shot cwd input; blank means
  "let the backend fall back to this session's first window cwd".
- 4 new tests (2 for settings round-trip, 2 for client.ts cwd
  threading).

Discovery-mode auto-derive (the half of the spec that uses THI-243's
repo-group launch context) is explicitly deferred — THI-243's MVP
doesn't yet expose per-repo "+" buttons, so the spec hook isn't
reachable. Lands as an additive change in a follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…default-cwd

# Conflicts:
#	frontend/src/lib/settings.ts
@tdody
tdody merged commit dd15c24 into main Jun 7, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant