diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 9824a7ba..5ec93051 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -15,6 +15,7 @@ ### Improved +- **Git Branch Display**: Footer now shows the active git branch after the directory path (e.g., `~/project (main)`). Branch is detected by reading `.git/HEAD` directly (fast, synchronous). Cache is refreshed after each assistant message to detect branch changes from git commands executed by the agent. ([#55](https://github.com/badlogic/pi-mono/issues/55)) - **HTML Export**: Added timestamps to each message, fixed text clipping with proper word-wrapping CSS, improved font selection (`ui-monospace`, `Cascadia Code`, `Source Code Pro`), reduced font sizes for more compact display (12px base), added model switch indicators in conversation timeline, created dedicated Tokens & Cost section with cumulative statistics (input/output/cache tokens, cost breakdown by type), added context usage display showing token count and percentage for the last model used, and now displays all models used during the session. ([#51](https://github.com/badlogic/pi-mono/issues/51), [#52](https://github.com/badlogic/pi-mono/issues/52)) ## [0.10.0] - 2025-11-27 diff --git a/packages/coding-agent/src/tui/footer.ts b/packages/coding-agent/src/tui/footer.ts index 79481aa9..bc45dc05 100644 --- a/packages/coding-agent/src/tui/footer.ts +++ b/packages/coding-agent/src/tui/footer.ts @@ -1,6 +1,8 @@ import type { AgentState } from "@mariozechner/pi-agent-core"; import type { AssistantMessage } from "@mariozechner/pi-ai"; import { type Component, visibleWidth } from "@mariozechner/pi-tui"; +import { readFileSync } from "fs"; +import { join } from "path"; import { theme } from "../theme/theme.js"; /** @@ -8,6 +10,7 @@ import { theme } from "../theme/theme.js"; */ export class FooterComponent implements Component { private state: AgentState; + private cachedBranch: string | null | undefined = undefined; // undefined = not checked yet, null = not in git repo, string = branch name constructor(state: AgentState) { this.state = state; @@ -18,7 +21,37 @@ export class FooterComponent implements Component { } invalidate(): void { - // No cached state to invalidate currently + // Invalidate cached branch so it gets re-read on next render + this.cachedBranch = undefined; + } + + /** + * Get current git branch by reading .git/HEAD directly. + * Returns null if not in a git repo, branch name otherwise. + */ + private getCurrentBranch(): string | null { + // Return cached value if available + if (this.cachedBranch !== undefined) { + return this.cachedBranch; + } + + try { + const gitHeadPath = join(process.cwd(), ".git", "HEAD"); + const content = readFileSync(gitHeadPath, "utf8").trim(); + + if (content.startsWith("ref: refs/heads/")) { + // Normal branch: extract branch name + this.cachedBranch = content.slice(16); + } else { + // Detached HEAD state + this.cachedBranch = "detached"; + } + } catch { + // Not in a git repo or error reading file + this.cachedBranch = null; + } + + return this.cachedBranch; } render(width: number): string[] { @@ -71,6 +104,12 @@ export class FooterComponent implements Component { pwd = "~" + pwd.slice(home.length); } + // Add git branch if available + const branch = this.getCurrentBranch(); + if (branch) { + pwd = `${pwd} (${branch})`; + } + // Truncate path if too long to fit width const maxPathLength = Math.max(20, width - 10); // Leave some margin if (pwd.length > maxPathLength) { diff --git a/packages/coding-agent/src/tui/tui-renderer.ts b/packages/coding-agent/src/tui/tui-renderer.ts index 512aabd5..e0a9e7c0 100644 --- a/packages/coding-agent/src/tui/tui-renderer.ts +++ b/packages/coding-agent/src/tui/tui-renderer.ts @@ -589,6 +589,9 @@ export class TuiRenderer { // Keep the streaming component - it's now the final assistant message this.streamingComponent = null; + + // Invalidate footer cache to refresh git branch (in case agent executed git commands) + this.footer.invalidate(); } this.ui.requestRender(); break;