Tags: flipbit03/forestui
Tags
Add custom Claude buttons configurable in settings (#23) Users can now configure arbitrary "New Session: X" buttons that spawn claude with any custom command. Each button has a display label, a tmux window prefix (auto-derived from label, independently editable), and a command string used as-is when the button is pressed. Buttons containing --dangerously-skip-permissions are auto-styled red like the existing YOLO button. Buttons render in the CLAUDE section (wrapping 4-per-row) and as compact pills on each row of the recent-sessions list. The resume path reuses the custom command/prefix with `-r <session-id>` appended. Also: - Drop the pre-existing (unused) per-repo/per-worktree/global custom_claude_command override — custom buttons subsume the use case - Move Add Worktree out of the CLAUDE section to sit next to Git Pull in the repo header, where it belongs - Widen Settings + Manage modals (new modal-wide class) and let them fill available vertical space so the Manage button is always visible - Bump textual to >=8.2.4
Fix window renaming on reattach after forestui is killed (#22) * Fix window renaming on reattach after forestui is killed When forestui was killed and then re-launched to reattach to an existing tmux session, it would incorrectly rename the first window (e.g., "ssh") to "forestui" in addition to creating the correct new forestui window. Root cause: TmuxService.current_window used session.active_window (the window the user is viewing) instead of the window the forestui process is actually running in. On reattach, the active window is window 1, not the newly created forestui window. Fix: Use TMUX_PANE env var to find the process's own window. Also fix the hardcoded "forestui" name in the recreated window to respect dev mode. Additionally improves the test-forestui skill with lessons learned: - Never call tmux directly, always go through tu - Use a parent bash shell for detach/reattach test flows - Check .tmux.conf for rename-window prompt behavior - Proactively test visual/behavioral changes Strengthens CLAUDE.md to require make check before pushing/opening PRs. * Add CI workflow to run make check on PRs and pushes to main * test: deliberate type error to verify CI catches failures * Revert deliberate type error used to verify CI failure detection * test: deliberate formatting error to verify CI catches it * Add format check to make check and CI Adds `ruff format --check` as a `format-check` target to the Makefile, included in `make check`. CI now catches lint, type errors, and unformatted code.
Use grouped tmux sessions for independent window navigation (#21) * Use grouped tmux sessions for independent window navigation (#20) Replace `tmux attach-session` with `tmux new-session -t` when reattaching to an existing session. This creates a grouped session that shares windows but allows each terminal to independently select the active window. The `destroy-unattached` option ensures grouped sessions are cleaned up when the terminal disconnects. * Fix grouped tmux session creation by splitting into separate commands The semicolon command separator in os.execvp doesn't work reliably with tmux. Instead, create the grouped session detached with a PID-based unique name, set destroy-unattached on it, then attach. * Use tmux hook to defer destroy-unattached until after client attaches Setting destroy-unattached on a detached session causes tmux to destroy it immediately. Use a client-attached hook instead so the option is set only after the client connects, avoiding the race condition. * Fix TmuxService.session to resolve the actual current session Use tmux display-message to get the session ID of the process's own session instead of picking the first attached one. In grouped session setups, multiple sessions are attached simultaneously, and the old approach would always find the original session, causing window operations (open editor, terminal, claude) to switch the active window in the wrong terminal. * Fix TmuxService.session to resolve the actual current session The forestui process runs in the original session's pane, so display-message always returned the original session ID regardless of which grouped session the user is viewing from. Instead, use list-clients sorted by client_activity to find the most recently active client's session — that's the terminal the user just interacted with. Remove session caching since the active client can change between grouped sessions at any time. * Hide PID-suffixed session name in tmux status bar for grouped sessions Override status-left on grouped sessions to show the base session name (e.g. forestui-forest) instead of the internal PID-suffixed name (e.g. forestui-forest-23116). The grouped session is an implementation detail that should be transparent to the user. * Address PR review: fix injection, side effects, and session scoping 1. Fix shell injection via unquoted forest_path (shlex.quote) 2. Replace select-window existence check with read-only list-windows to avoid switching the active window in the original session 3. Scope list-clients to our session_group so unrelated tmux sessions don't cause forestui to resolve to the wrong session 4. Use destroy-unattached keep-last instead of on, preventing the last session in the group from being destroyed 5. Use show-options -gv for status-left to get just the value without option name prefix or quoting 6. Add error handling for grouped session creation with fallback to direct attach * Fix forestui window detection for dev mode window names The existence check used exact match on "forestui", missing dev mode windows named "forestui-dev-HHMM". This caused a second terminal to spawn a duplicate forestui process instead of just attaching. * Preserve trailing space in status-left template for grouped sessions strip() was removing a trailing space that's part of the tmux status-left template, causing the separator between the session name and the first window tab to disappear. Use rstrip('\n') to only remove the subprocess newline. * Add test-forestui skill for self-driven visual testing with tu Provides a skill that lets Claude launch forestui in headless virtual terminals via tu, visually verify UI behavior, and interact with the running app. Supports multi-terminal testing with isolated tmux servers for verifying grouped session behavior. * Warn against running forestui without tmux isolation in test skill The user is likely running their own tmux/forestui session. Without TMUX_TMPDIR isolation the agent would interfere with their live session. * Use exact-match session targeting to avoid tmux prefix matching tmux's -t flag does prefix matching by default, so has-session -t forestui-forest would match orphaned grouped sessions like forestui-forest-12345. The = prefix forces exact match on all session targets: has-session, list-windows, new-window, new-session. * Enforce PNG screenshots in test-forestui skill Plain text screenshots lose colors and highlighting, making it impossible to tell which tmux window is active. PNG mode captures the full terminal with color for accurate visual verification.
Add fuzzy branch search with dropdown for existing branch selection (#19 ) * Add fuzzy branch search with dropdown for existing branch selection Replace the prefix-only SuggestFromList inline suggestion with a BranchSearchInput widget that shows a visible dropdown of matches. Matching uses substring search on any portion of the branch name (including through remote prefixes like origin/) plus Levenshtein distance for typo tolerance. * Address PR review: use rapidfuzz, dynamic remotes, extract utils - Replace hand-rolled Levenshtein with rapidfuzz library - Use actual git remotes (from `git remote`) instead of hardcoded prefixes like "origin"/"upstream" for stripping remote prefixes - Move matching functions to forestui/utils.py, keeping only widget classes in branch_search.py - Thread remotes list from git service through app -> modals -> widgets - Update _compute_default_base_branch to iterate actual remotes * Drop rapidfuzz dependency, use hand-rolled Levenshtein rapidfuzz is a compiled C extension that may lack wheels for some architectures. The Levenshtein DP implementation is ~15 lines and perfectly adequate for matching short branch name strings.
Add base branch selection and worktree origin tracking (#18) Features: - Add base branch selector with autocomplete to "Create Worktree from Issue" modal - Add Fetch button to refresh remote branches before creating worktree - Replace Select dropdown with Input + autocomplete for existing branch selection - Track base_branch and created_from_ref on worktrees - Display "Based on: <branch> (<ref>)" in worktree detail view Improvements: - list_branches() now includes all remotes (not just origin/upstream) - Wider modal container (60 -> 80 chars) for better readability - Clear error messages when user types in Add Worktree modal - Validate existing branch exists before creating worktree - Better error messages with branch name context
Use worktree name instead of branch name for tmux window titles (#17) Fixes tmux window names to use the shorter worktree directory name (e.g., "title-is-wt-name-not-branch") instead of the full git branch name (e.g., "feat/title-is-wt-name-not-branch"). Also renames the helper function to _get_tmux_window_name since it's used for all tmux windows, not just Claude sessions.
Fix auto-update to use uv tool upgrade for cleaner detection Switch from `uv tool install --force --upgrade` to `uv tool upgrade` which provides consistent output for detecting update status: - "Nothing to upgrade" when already on latest - "Updated" with version info when upgraded
Fix auto-update to use uv tool upgrade for cleaner detection Switch from `uv tool install --force --upgrade` to `uv tool upgrade` which provides consistent output for detecting update status: - "Nothing to upgrade" when already on latest - "Updated" with version info when upgraded
PreviousNext