feat: workspaces tmux support #1

Merged
waylon merged 4 commits from feat/workspaces-tmux-support into main 2025-11-25 20:23:28 -06:00
Showing only changes of commit 42d034eef2 - Show all commits

View file

@ -28,6 +28,8 @@ from rich.console import Console
from rich.table import Table from rich.table import Table
from rich.prompt import Prompt, Confirm from rich.prompt import Prompt, Confirm
from rich.panel import Panel from rich.panel import Panel
from rich.syntax import Syntax
from rich.panel import Panel
from iterfzf import iterfzf from iterfzf import iterfzf
app = typer.Typer( app = typer.Typer(
@ -1163,12 +1165,11 @@ def diff_workspace(
): ):
""" """
Show git diff for all repos in the workspace. Show git diff for all repos in the workspace.
Uses rich Syntax highlighter for diffs.
- If --staged: uses `git diff --cached` (only staged changes).
- Otherwise: uses `git diff` (staged + unstaged).
""" """
_settings, _repos_dir, workspaces_dir = get_ctx_paths(ctx) _settings, _repos_dir, workspaces_dir = get_ctx_paths(ctx)
ws_dir = find_workspace_dir(workspaces_dir, workspace) ws_dir = find_workspace_dir(workspaces_dir, workspace)
if not ws_dir.exists(): if not ws_dir.exists():
console.print(f"[red]Workspace '{ws_dir.name}' does not exist at {ws_dir}[/red]") console.print(f"[red]Workspace '{ws_dir.name}' does not exist at {ws_dir}[/red]")
raise typer.Exit(1) raise typer.Exit(1)
@ -1178,7 +1179,7 @@ def diff_workspace(
f"Showing diffs for workspace [bold]{title or ws_dir.name}[/bold] (dir: {ws_dir.name})" f"Showing diffs for workspace [bold]{title or ws_dir.name}[/bold] (dir: {ws_dir.name})"
) )
# Collect worktrees (repos) in this workspace # Collect repos
worktrees: List[Path] = [] worktrees: List[Path] = []
for child in sorted(p for p in ws_dir.iterdir() if p.is_dir()): for child in sorted(p for p in ws_dir.iterdir() if p.is_dir()):
if child.name.lower() == "readme.md": if child.name.lower() == "readme.md":
@ -1194,12 +1195,13 @@ def diff_workspace(
any_diffs = False any_diffs = False
for wt in worktrees: for wt in worktrees:
# Quick check: skip repos with no changes at all # Skip clean repos
changes = git_status_porcelain(wt) changes = git_status_porcelain(wt)
if not changes: if not changes:
continue continue
any_diffs = True any_diffs = True
console.rule(f"[bold]{wt.name}[/bold]") console.rule(f"[bold]{wt.name}[/bold]")
cmd = ["git", "diff"] cmd = ["git", "diff"]
@ -1215,14 +1217,22 @@ def diff_workspace(
if not out.strip(): if not out.strip():
console.print("[dim]No diff output.[/dim]") console.print("[dim]No diff output.[/dim]")
else: continue
# Use markup=False so diff characters like [ ] don't get eaten by Rich
console.print(out, markup=False) syntax = Syntax(
out,
"diff",
theme="nord",
line_numbers=False,
word_wrap=False,
)
# Wrap each repo diff in a Panel for clarity
console.print(Panel(syntax, title=wt.name, border_style="cyan"))
if not any_diffs: if not any_diffs:
console.print("[green]No changes to diff in any repo.[/green]") console.print("[green]No changes to diff in any repo.[/green]")
def not_in_tmux() -> bool: def not_in_tmux() -> bool:
"""Return True if not inside tmux or zellij.""" """Return True if not inside tmux or zellij."""
return not os.environ.get("TMUX") and not os.environ.get("ZELLIJ") return not os.environ.get("TMUX") and not os.environ.get("ZELLIJ")