From 2e1f07bac27fbcd81dcaa56032d6f2209b8745b8 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Sat, 23 May 2026 09:53:36 +0200 Subject: [PATCH] fix(coding-agent): avoid invalid footer home abbreviation closes #4878 --- packages/coding-agent/CHANGELOG.md | 1 + .../modes/interactive/components/footer.ts | 21 ++++++++++++++----- .../coding-agent/test/footer-width.test.ts | 13 +++++++++++- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 83253924..ba96b2a8 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -22,6 +22,7 @@ - Published a 0.74.2 rescue release that tells Node 20 users to upgrade Node before updating to newer Pi versions ([#4876](https://github.com/earendil-works/pi/issues/4876)). - Fixed final bash tool cards to avoid rendering duplicate full-output truncation paths ([#4819](https://github.com/earendil-works/pi/issues/4819)). - Fixed bash tool truncation line counts to ignore the trailing newline as an extra output line ([#4818](https://github.com/earendil-works/pi/issues/4818)). +- Fixed footer home-directory abbreviation to avoid shortening sibling paths that only share the same prefix ([#4878](https://github.com/earendil-works/pi/issues/4878)). ## [0.75.4] - 2026-05-20 diff --git a/packages/coding-agent/src/modes/interactive/components/footer.ts b/packages/coding-agent/src/modes/interactive/components/footer.ts index a48898a5..47ff58ae 100644 --- a/packages/coding-agent/src/modes/interactive/components/footer.ts +++ b/packages/coding-agent/src/modes/interactive/components/footer.ts @@ -1,3 +1,4 @@ +import { isAbsolute, relative, resolve, sep } from "node:path"; import { type Component, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui"; import type { AgentSession } from "../../../core/agent-session.ts"; import type { ReadonlyFooterDataProvider } from "../../../core/footer-data-provider.ts"; @@ -26,6 +27,20 @@ function formatTokens(count: number): string { return `${Math.round(count / 1000000)}M`; } +export function formatCwdForFooter(cwd: string, home: string | undefined): string { + if (!home) return cwd; + + const resolvedCwd = resolve(cwd); + const resolvedHome = resolve(home); + const relativeToHome = relative(resolvedHome, resolvedCwd); + const isInsideHome = + relativeToHome === "" || + (relativeToHome !== ".." && !relativeToHome.startsWith(`..${sep}`) && !isAbsolute(relativeToHome)); + + if (!isInsideHome) return cwd; + return relativeToHome === "" ? "~" : `~${sep}${relativeToHome}`; +} + /** * Footer component that shows pwd, token stats, and context usage. * Computes token/context stats from session, gets git branch and extension statuses from provider. @@ -92,11 +107,7 @@ export class FooterComponent implements Component { const contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : "?"; // Replace home directory with ~ - let pwd = this.session.sessionManager.getCwd(); - const home = process.env.HOME || process.env.USERPROFILE; - if (home && pwd.startsWith(home)) { - pwd = `~${pwd.slice(home.length)}`; - } + let pwd = formatCwdForFooter(this.session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE); // Add git branch if available const branch = this.footerData.getGitBranch(); diff --git a/packages/coding-agent/test/footer-width.test.ts b/packages/coding-agent/test/footer-width.test.ts index 2d912647..d5586d7d 100644 --- a/packages/coding-agent/test/footer-width.test.ts +++ b/packages/coding-agent/test/footer-width.test.ts @@ -2,7 +2,7 @@ import { visibleWidth } from "@earendil-works/pi-tui"; import { beforeAll, describe, expect, it } from "vitest"; import type { AgentSession } from "../src/core/agent-session.ts"; import type { ReadonlyFooterDataProvider } from "../src/core/footer-data-provider.ts"; -import { FooterComponent } from "../src/modes/interactive/components/footer.ts"; +import { FooterComponent, formatCwdForFooter } from "../src/modes/interactive/components/footer.ts"; import { initTheme } from "../src/modes/interactive/theme/theme.ts"; type AssistantUsage = { @@ -73,6 +73,17 @@ function createFooterData(providerCount: number): ReadonlyFooterDataProvider { return provider; } +describe("formatCwdForFooter", () => { + it("does not abbreviate sibling paths that share the home prefix", () => { + expect(formatCwdForFooter("/home/user2", "/home/user")).toBe("/home/user2"); + }); + + it("abbreviates the home directory and descendants", () => { + expect(formatCwdForFooter("/home/user", "/home/user")).toBe("~"); + expect(formatCwdForFooter("/home/user/project", "/home/user")).toBe("~/project"); + }); +}); + describe("FooterComponent width handling", () => { beforeAll(() => { initTheme(undefined, false);