diff --git a/README.md b/README.md index cea0eee..7e121ff 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ Claude HUD gives you better insights into what's happening in your Claude Code s | What You See | Why It Matters | |--------------|----------------| +| **Project name** | Always know which project you're working in | | **Context health** | Know exactly how full your context window is before it's too late | | **Tool activity** | Watch Claude read, edit, and search files as it happens | | **Agent tracking** | See which subagents are running and what they're doing | @@ -45,8 +46,9 @@ Claude HUD gives you better insights into what's happening in your Claude Code s ### Session Info ``` -[Opus 4.5] ████░░░░░░ 19% | 2 CLAUDE.md | 8 rules | 6 MCPs | 6 hooks | ⏱️ 1m +📁 my-project | [Opus 4.5] ████░░░░░░ 19% | 2 CLAUDE.md | 8 rules | 6 MCPs | 6 hooks | ⏱️ 1m ``` +- **Project** — Current working directory folder name - **Model** — Current model in use - **Context bar** — Visual meter with color coding (green → yellow → red as it fills) - **Config counts** — Rules, MCPs, and hooks loaded diff --git a/src/render/session-line.ts b/src/render/session-line.ts index db17a10..aaa9fc9 100644 --- a/src/render/session-line.ts +++ b/src/render/session-line.ts @@ -1,3 +1,4 @@ +import path from 'node:path'; import type { RenderContext } from '../types.js'; import { getContextPercent, getModelName } from '../stdin.js'; import { coloredBar, cyan, dim, red, getContextColor, RESET } from './colors.js'; @@ -9,6 +10,12 @@ export function renderSessionLine(ctx: RenderContext): string { const parts: string[] = []; + // Add project folder name for context + if (ctx.stdin.cwd) { + const projectName = path.basename(ctx.stdin.cwd) || ctx.stdin.cwd; + parts.push(cyan(`📁 ${projectName}`)); + } + parts.push(`${cyan(`[${model}]`)} ${bar} ${getContextColor(percent)}${percent}%${RESET}`); if (ctx.claudeMdCount > 0) { diff --git a/tests/fixtures/expected/render-basic.txt b/tests/fixtures/expected/render-basic.txt index 5b2ab6e..fdcfd97 100644 --- a/tests/fixtures/expected/render-basic.txt +++ b/tests/fixtures/expected/render-basic.txt @@ -1,4 +1,4 @@ -[Opus] █████░░░░░ 45% +📁 my-project | [Opus] █████░░░░░ 45% ◐ Edit: .../authentication.ts | ✓ Read ×1 ✓ explore [haiku]: Finding auth code (<1s) ▸ Add tests (1/2) diff --git a/tests/integration.test.js b/tests/integration.test.js index b9f8bd4..5f3cc4d 100644 --- a/tests/integration.test.js +++ b/tests/integration.test.js @@ -20,6 +20,9 @@ test('CLI renders expected output for a basic transcript', async () => { const expected = readFileSync(expectedPath, 'utf8').trimEnd(); const homeDir = await mkdtemp(path.join(tmpdir(), 'claude-hud-home-')); + // Use a fixed project name for deterministic test output + const projectDir = path.join(homeDir, 'my-project'); + await import('node:fs/promises').then(fs => fs.mkdir(projectDir)); try { const stdin = JSON.stringify({ model: { display_name: 'Opus' }, @@ -28,7 +31,7 @@ test('CLI renders expected output for a basic transcript', async () => { current_usage: { input_tokens: 45000 }, }, transcript_path: fixturePath, - cwd: homeDir, + cwd: projectDir, }); const result = spawnSync('node', ['dist/index.js'], { diff --git a/tests/render.test.js b/tests/render.test.js index 26c484e..febb2e0 100644 --- a/tests/render.test.js +++ b/tests/render.test.js @@ -99,6 +99,37 @@ test('renderSessionLine includes config counts when present', () => { assert.ok(line.includes('hooks')); }); +test('renderSessionLine displays project name from POSIX cwd', () => { + const ctx = baseContext(); + ctx.stdin.cwd = '/Users/jarrod/my-project'; + const line = renderSessionLine(ctx); + assert.ok(line.includes('my-project')); + assert.ok(!line.includes('/Users/jarrod')); +}); + +test('renderSessionLine displays project name from Windows cwd', { skip: process.platform !== 'win32' }, () => { + const ctx = baseContext(); + ctx.stdin.cwd = 'C:\\Users\\jarrod\\my-project'; + const line = renderSessionLine(ctx); + assert.ok(line.includes('my-project')); + assert.ok(!line.includes('C:\\')); +}); + +test('renderSessionLine handles root path gracefully', () => { + const ctx = baseContext(); + ctx.stdin.cwd = '/'; + const line = renderSessionLine(ctx); + assert.ok(line.includes('[Opus]')); +}); + +test('renderSessionLine omits project name when cwd is undefined', () => { + const ctx = baseContext(); + ctx.stdin.cwd = undefined; + const line = renderSessionLine(ctx); + assert.ok(line.includes('[Opus]')); + assert.ok(!line.includes('📁')); +}); + test('renderToolsLine renders running and completed tools', () => { const ctx = baseContext(); ctx.transcript.tools = [