claude-code-hud 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +36 -31
  2. package/package.json +1 -1
  3. package/tui/hud.tsx +171 -26
package/README.md CHANGED
@@ -8,27 +8,7 @@
8
8
 
9
9
  Claude Code로 작업할 때 토큰 사용량, git 상태, 파일 구조를 IDE나 별도 탭 없이 터미널 하나에서 확인할 수 있는 HUD입니다.
10
10
 
11
- ```
12
- ┌──────────────────────────────────────────────────────────────────────────────┐
13
- │ ◆ HUD │ ◉ TOKENS │ ○ PROJECT │ ○ GIT sonnet-4-6 · up 4m │
14
- ├──────────────────────────────────────────────────────────────────────────────┤
15
- │ ▸ CONTEXT WINDOW │
16
- │ ████████████████████░░░░░░░░░░░░░░░░░░░░░░░ 46% 92K / 200K OK │
17
- │ │
18
- │ ▸ USAGE WINDOW │
19
- │ 5h ████████░░░░░░░░░░░░░░░░░░░░ 28.0% resets in 3h 12m │
20
- │ wk ███░░░░░░░░░░░░░░░░░░░░░░░░░ 9.0% resets in 6h 48m │
21
- │ │
22
- │ ▸ TOKENS (this session) │
23
- │ input ░░░░░░░░░░░░░░░░░░░░░░░░ 4.8K 0% │
24
- │ output ░░░░░░░░░░░░░░░░░░░░░░░░ 188.5K 0% │
25
- │ cache-read ████████████████████████ 51.5M 100% │
26
- │ cache-write ██░░░░░░░░░░░░░░░░░░░░░░ 3.8M 7% │
27
- │ │
28
- │ ▸ OUTPUT TOKENS / HR │
29
- │ total 2.1M │ avg 48.2K/hr │ peak 312K/hr │
30
- └──────────────────────────────────────────────────────────────────────────────┘
31
- ```
11
+ <img src="./demo.gif" width="700" alt="demo">
32
12
 
33
13
  ### 사용법
34
14
 
@@ -59,9 +39,6 @@ npx claude-code-hud
59
39
  # 전역 설치
60
40
  npm install -g claude-code-hud
61
41
  claude-hud
62
-
63
- # Claude Code 플러그인
64
- /plugin install letsgojh0810/hud-plugin
65
42
  ```
66
43
 
67
44
  ### 기능
@@ -117,17 +94,30 @@ claude-hud
117
94
  - Claude Pro / Max 플랜 권장 (5h / 주간 사용률 표시)
118
95
  - Git (GIT 탭 사용 시)
119
96
 
97
+ ### 플랫폼 지원
98
+
99
+ | 기능 | macOS | Windows |
100
+ |------|-------|---------|
101
+ | 기본 실행 | ✅ | ✅ Node.js 설치 후 `npx` |
102
+ | 토큰 / Git / 파일 브라우저 | ✅ | ✅ |
103
+ | 5h / 주간 사용률 | ✅ Keychain 자동 인식 | ⚠️ `~/.claude/.credentials.json` 폴백 |
104
+ | 터미널 렌더링 | ✅ | ✅ Windows Terminal 권장 (cmd.exe 깨짐) |
105
+ | 한글 키보드 | ✅ | ⚠️ IME 방식 차이로 미지원 가능 |
106
+
107
+ **Windows 권장 환경:**
108
+ - [Windows Terminal](https://aka.ms/terminal) 사용
109
+ - WSL2 환경이면 macOS와 동일하게 동작
110
+
111
+ **Windows에서 5h/wk 사용률이 안 보일 때:**
112
+ Claude Code를 한 번 실행하면 `~/.claude/.credentials.json`에 credentials이 저장됩니다. HUD는 이 파일을 자동으로 읽습니다.
113
+
120
114
  ---
121
115
 
122
116
  ## English
123
117
 
124
118
  A Terminal HUD (Heads-Up Display) for Claude Code — real-time token usage, git status, and interactive project file browser. No IDE, no extra tabs. Just a second terminal window.
125
119
 
126
- ```
127
- ┌──────────────────────────────────────────────────────────────────────────────┐
128
- │ ◆ HUD │ ◉ TOKENS │ ○ PROJECT │ ○ GIT sonnet-4-6 · up 4m │
129
- └──────────────────────────────────────────────────────────────────────────────┘
130
- ```
120
+ <img src="./demo.gif" width="700" alt="demo">
131
121
 
132
122
  ### Usage
133
123
 
@@ -157,8 +147,6 @@ npx claude-code-hud
157
147
  npm install -g claude-code-hud
158
148
  claude-hud
159
149
 
160
- # Claude Code plugin
161
- /plugin install letsgojh0810/hud-plugin
162
150
  ```
163
151
 
164
152
  ### Features
@@ -198,6 +186,23 @@ claude-hud
198
186
 
199
187
  > Korean keyboard layout supported — `ㅓ/ㅏ` (j/k), `ㅇ` (d), `ㄱ` (r), `ㅂ` (q), `ㅠ` (b)
200
188
 
189
+ ### Platform Support
190
+
191
+ | Feature | macOS | Windows |
192
+ |---------|-------|---------|
193
+ | Basic run | ✅ | ✅ via `npx` with Node.js |
194
+ | Tokens / Git / File browser | ✅ | ✅ |
195
+ | 5h / weekly usage % | ✅ Keychain auto-detected | ⚠️ Falls back to `~/.claude/.credentials.json` |
196
+ | Terminal rendering | ✅ | ✅ Windows Terminal recommended (cmd.exe may break) |
197
+ | Korean keyboard | ✅ | ⚠️ May not work depending on IME |
198
+
199
+ **Windows recommendations:**
200
+ - Use [Windows Terminal](https://aka.ms/terminal) for proper Unicode rendering
201
+ - WSL2 works identically to macOS
202
+
203
+ **5h / weekly usage not showing on Windows?**
204
+ Run `claude` once to authenticate — credentials are saved to `~/.claude/.credentials.json` which the HUD reads automatically.
205
+
201
206
  ### How it works
202
207
 
203
208
  - **Token data**: Watches `~/.claude/projects/*/sessions/*.jsonl` with chokidar — updates instantly on each Claude response
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-hud",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Terminal HUD for Claude Code — real-time token usage, git status, project monitor",
5
5
  "type": "module",
6
6
  "bin": {
package/tui/hud.tsx CHANGED
@@ -6,7 +6,7 @@
6
6
  import React, { useState, useEffect, useCallback } from 'react';
7
7
  import { render, Box, Text, useStdout, useInput } from 'ink';
8
8
  import { fileURLToPath } from 'url';
9
- import { dirname, join } from 'path';
9
+ import { dirname, join, basename } from 'path';
10
10
  import fs from 'fs';
11
11
  import os from 'os';
12
12
  import { execSync } from 'child_process';
@@ -16,23 +16,34 @@ const { readTokenUsage, readTokenHistory } = await import(join(__dir, '../script
16
16
  const { readGitInfo } = await import(join(__dir, '../scripts/lib/git-info.mjs'));
17
17
  const { getUsage, getUsageSync } = await import(join(__dir, '../scripts/lib/usage-api.mjs'));
18
18
 
19
- // Clear terminal before starting
20
- process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
19
+ // Enter alternate screen buffer (like vim/htop — terminal never scrolls, header stays fixed)
20
+ process.stdout.write('\x1b[?1049h\x1b[2J\x1b[H');
21
+ process.on('exit', () => process.stdout.write('\x1b[?1049l'));
22
+ process.on('SIGINT', () => { process.stdout.write('\x1b[?1049l'); process.exit(0); });
23
+ process.on('SIGTERM', () => { process.stdout.write('\x1b[?1049l'); process.exit(0); });
21
24
 
22
25
  const SESSION_START = Date.now();
23
26
 
24
27
  // ── Themes ─────────────────────────────────────────────────────────────────
25
- const DARK = {
26
- brand: '#3182F6', text: '#E6EDF3', dim: '#8B949E', dimmer: '#6E7681',
28
+ // Base is always dark. Only accent colors cycle with `d`.
29
+ const BASE = {
30
+ text: '#E6EDF3', dim: '#8B949E', dimmer: '#6E7681',
27
31
  border: '#30363D', green: '#3FB950', yellow: '#D29922', red: '#F85149',
28
- purple: '#A371F7', cyan: '#58A6FF',
29
- };
30
- const LIGHT = {
31
- brand: '#3182F6', text: '#1F2328', dim: '#656D76', dimmer: '#8C959F',
32
- border: '#D8DEE4', green: '#1A7F37', yellow: '#9A6700', red: '#CF222E',
33
- purple: '#8250DF', cyan: '#0969DA',
34
32
  };
35
33
 
34
+ const ACCENTS = [
35
+ { brand: '#3B82F6', cyan: '#60A5FA', purple: '#A78BFA' }, // blue
36
+ { brand: '#F43F5E', cyan: '#FB7185', purple: '#F9A8D4' }, // red
37
+ { brand: '#F59E0B', cyan: '#FCD34D', purple: '#FDE68A' }, // amber
38
+ { brand: '#10B981', cyan: '#34D399', purple: '#6EE7B7' }, // emerald
39
+ { brand: '#EC4899', cyan: '#F472B6', purple: '#F9A8D4' }, // pink
40
+ ] as const;
41
+
42
+ type Theme = typeof BASE & typeof ACCENTS[number];
43
+ function makeTheme(accentIdx: number): Theme {
44
+ return { ...BASE, ...ACCENTS[accentIdx] };
45
+ }
46
+
36
47
  // ── Helpers ────────────────────────────────────────────────────────────────
37
48
  const fmtNum = (n: number) =>
38
49
  n >= 1_000_000 ? (n / 1_000_000).toFixed(1) + 'M' :
@@ -197,6 +208,62 @@ function getBranches(cwd: string): string[] {
197
208
  }
198
209
  }
199
210
 
211
+ // ── Timeline ────────────────────────────────────────────────────────────────
212
+ type TimelineEntry = {
213
+ time: string;
214
+ text: string;
215
+ };
216
+
217
+ async function readSessionTimeline(cwd: string): Promise<TimelineEntry[]> {
218
+ const projectsDir = join(os.homedir(), '.claude', 'projects');
219
+ if (!fs.existsSync(projectsDir)) return [];
220
+
221
+ let latestFile: string | null = null;
222
+ let latestMtime = 0;
223
+ try {
224
+ for (const projectHash of fs.readdirSync(projectsDir)) {
225
+ const sessionDir = join(projectsDir, projectHash);
226
+ if (!fs.statSync(sessionDir).isDirectory()) continue;
227
+ for (const file of fs.readdirSync(sessionDir)) {
228
+ if (!file.endsWith('.jsonl')) continue;
229
+ const filePath = join(sessionDir, file);
230
+ try {
231
+ const mtime = fs.statSync(filePath).mtimeMs;
232
+ if (mtime > latestMtime) { latestMtime = mtime; latestFile = filePath; }
233
+ } catch {}
234
+ }
235
+ }
236
+ } catch {}
237
+
238
+ if (!latestFile) return [];
239
+
240
+ const lines = fs.readFileSync(latestFile, 'utf-8').split('\n').filter(Boolean);
241
+ const entries: TimelineEntry[] = [];
242
+
243
+ for (const line of lines) {
244
+ try {
245
+ const obj = JSON.parse(line);
246
+ if (obj.type !== 'user') continue;
247
+ const content = obj.message?.content;
248
+ const textBlock = Array.isArray(content)
249
+ ? content.find((b: any) => b.type === 'text')
250
+ : null;
251
+ const text: string = textBlock?.text ?? (typeof content === 'string' ? content : '');
252
+ if (!text.trim()) continue;
253
+
254
+ const ts: string = obj.timestamp ?? '';
255
+ let time = '';
256
+ if (ts) {
257
+ try { time = new Date(ts).toTimeString().slice(0, 5); } catch {}
258
+ }
259
+
260
+ entries.push({ time, text: text.replace(/\n/g, ' ').slice(0, 80) });
261
+ } catch {}
262
+ }
263
+
264
+ return entries.slice(-30);
265
+ }
266
+
200
267
  // ── UI Components ──────────────────────────────────────────────────────────
201
268
  function Bar({ ratio, width, color, C }: { ratio: number; width: number; color: string; C: typeof DARK }) {
202
269
  const filled = Math.max(0, Math.min(width, Math.round(ratio * width)));
@@ -218,7 +285,7 @@ function Section({ title, children, C, accent }: { title: string; children: Reac
218
285
  }
219
286
 
220
287
  // ── Tab 1: TOKENS ──────────────────────────────────────────────────────────
221
- function TokensTab({ usage, history, rateLimits, termWidth, C }: any) {
288
+ function TokensTab({ usage, history, rateLimits, termWidth, currentActivity, C }: any) {
222
289
  const ctxPct = usage.contextWindow > 0 ? usage.totalTokens / usage.contextWindow : 0;
223
290
  const ctxColor = ctxPct > 0.85 ? C.red : ctxPct > 0.65 ? C.yellow : C.brand;
224
291
  const ctxLabel = ctxPct > 0.85 ? 'WARN' : ctxPct > 0.65 ? 'MID' : 'OK';
@@ -330,6 +397,14 @@ function TokensTab({ usage, history, rateLimits, termWidth, C }: any) {
330
397
  </Section>
331
398
  );
332
399
  })()}
400
+
401
+ {/* Current activity */}
402
+ {currentActivity && (
403
+ <Box borderStyle="single" borderColor={C.border} paddingX={1}>
404
+ <Text color={C.dimmer}>now </Text>
405
+ <Text color={C.brand}>{currentActivity.slice(0, termWidth - 12)}</Text>
406
+ </Box>
407
+ )}
333
408
  </Box>
334
409
  );
335
410
  }
@@ -599,18 +674,46 @@ function GitTab({ git, C, termWidth, branchMode, branchList, branchCursor }: any
599
674
  );
600
675
  }
601
676
 
677
+ // ── Tab 4: TIMELINE ────────────────────────────────────────────────────────
678
+ const TIMELINE_VISIBLE = 10;
679
+
680
+ function TimelineTab({ timeline, timelineScroll, C }: any) {
681
+ const entries = timeline as TimelineEntry[];
682
+ const visible = entries.slice(timelineScroll, timelineScroll + TIMELINE_VISIBLE);
683
+ return (
684
+ <Box flexDirection="column" borderStyle="single" borderColor={C.border} paddingX={1}>
685
+ <Text color={C.dimmer} bold>▸ <Text color={C.text}>SESSION HISTORY</Text>
686
+ {entries.length > 0 && <Text color={C.dimmer}> {timelineScroll + 1}–{Math.min(timelineScroll + TIMELINE_VISIBLE, entries.length)} / {entries.length}</Text>}
687
+ </Text>
688
+ <Box flexDirection="column" marginTop={1}>
689
+ {entries.length === 0 && <Text color={C.dimmer}> no messages yet</Text>}
690
+ {visible.map((entry, i) => (
691
+ <Box key={i} marginBottom={1}>
692
+ <Box width={6}><Text color={C.dimmer}>{entry.time}</Text></Box>
693
+ <Text color={C.text}>{entry.text}</Text>
694
+ </Box>
695
+ ))}
696
+ {entries.length > TIMELINE_VISIBLE && (
697
+ <Text color={C.dimmer}> [j/k] scroll</Text>
698
+ )}
699
+ </Box>
700
+ </Box>
701
+ );
702
+ }
703
+
602
704
  // ── Main App ───────────────────────────────────────────────────────────────
603
705
  function App() {
604
706
  const { stdout } = useStdout();
605
- const [termWidth, setTermWidth] = useState(stdout?.columns ?? 80);
707
+ const [termWidth, setTermWidth] = useState(stdout?.columns ?? 80);
708
+ const [termHeight, setTermHeight] = useState(stdout?.rows ?? 24);
606
709
  const [tab, setTab] = useState(0); // 0=TOKENS 1=PROJECT 2=GIT
607
- const [dark, setDark] = useState(true);
710
+ const [accent, setAccent] = useState(3); // 0=blue 1=red 2=amber 3=emerald 4=pink
608
711
  const [scrollY, setScrollY] = useState(0);
609
712
  const [tick, setTick] = useState(0);
610
713
  const [updatedAt, setUpdatedAt] = useState(Date.now());
611
714
 
612
715
  const cwd = process.env.CLAUDE_PROJECT_ROOT || process.cwd();
613
- const C = dark ? DARK : LIGHT;
716
+ const C = makeTheme(accent);
614
717
 
615
718
  const [usage, setUsage] = useState<any>(readTokenUsage());
616
719
  const [history, setHistory] = useState<any>(readTokenHistory());
@@ -632,12 +735,24 @@ function App() {
632
735
  const [branchList, setBranchList] = useState<string[]>([]);
633
736
  const [branchCursor, setBranchCursor] = useState(0);
634
737
 
738
+ // Timeline state
739
+ const [timeline, setTimeline] = useState<TimelineEntry[]>([]);
740
+ const [timelineScroll, setTimelineScroll] = useState(0);
741
+ const [currentActivity, setCurrentActivity] = useState<string>('');
742
+
635
743
  const refresh = useCallback(() => {
636
744
  setUsage(readTokenUsage());
637
745
  setHistory(readTokenHistory());
638
746
  setGit(readGitInfo(cwd));
639
747
  setUpdatedAt(Date.now());
640
748
  getUsage().then(setRateLimits).catch(() => {});
749
+ readSessionTimeline(cwd).then(entries => {
750
+ setTimeline(entries);
751
+ if (entries.length > 0) {
752
+ const last = entries[entries.length - 1];
753
+ setCurrentActivity(last.text);
754
+ }
755
+ }).catch(() => {});
641
756
  }, [cwd]);
642
757
 
643
758
  useEffect(() => {
@@ -645,10 +760,19 @@ function App() {
645
760
  scanProject(cwd).then(setProject).catch(() => {});
646
761
  // Initial API usage fetch
647
762
  getUsage().then(setRateLimits).catch(() => {});
763
+ // Initial timeline load
764
+ readSessionTimeline(cwd).then(entries => {
765
+ setTimeline(entries);
766
+ if (entries.length > 0) {
767
+ const last = entries[entries.length - 1];
768
+ setCurrentActivity(last.text);
769
+ }
770
+ }).catch(() => {});
648
771
 
649
772
  const onResize = () => {
650
773
  process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
651
774
  setTermWidth(stdout?.columns ?? 80);
775
+ setTermHeight(stdout?.rows ?? 24);
652
776
  };
653
777
  stdout?.on('resize', onResize);
654
778
 
@@ -692,6 +816,7 @@ function App() {
692
816
  if (selected && selected !== git.branch) {
693
817
  try {
694
818
  execSync(`git checkout ${selected}`, { cwd });
819
+ process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
695
820
  refresh();
696
821
  } catch {}
697
822
  }
@@ -726,7 +851,8 @@ function App() {
726
851
  if (input === '1') { setTab(0); setScrollY(0); }
727
852
  if (input === '2') { setTab(1); setScrollY(0); }
728
853
  if (input === '3') { setTab(2); setScrollY(0); }
729
- if (input === 'd' || input === 'ㅇ') setDark(d => !d);
854
+ if (input === '4') { setTab(3); setScrollY(0); }
855
+ if (input === 'd' || input === 'ㅇ') setAccent(a => (a + 1) % ACCENTS.length);
730
856
 
731
857
  // r = manual refresh
732
858
  if (input === 'r' || input === 'ㄱ') {
@@ -742,6 +868,8 @@ function App() {
742
868
  } else if (tab === 1) {
743
869
  const flat = project?.dirTree ? flattenTree(project.dirTree, 0, treeExpanded) : [];
744
870
  setTreeCursor(c => Math.min(c + 1, flat.length - 1));
871
+ } else if (tab === 3) {
872
+ setTimelineScroll(s => Math.min(s + 1, Math.max(0, timeline.length - 10)));
745
873
  } else {
746
874
  setScrollY(s => Math.min(s + 1, 20));
747
875
  }
@@ -751,6 +879,8 @@ function App() {
751
879
  setFileScroll(s => Math.max(s - 1, 0));
752
880
  } else if (tab === 1) {
753
881
  setTreeCursor(c => Math.max(c - 1, 0));
882
+ } else if (tab === 3) {
883
+ setTimelineScroll(s => Math.max(s - 1, 0));
754
884
  } else {
755
885
  setScrollY(s => Math.max(s - 1, 0));
756
886
  }
@@ -797,7 +927,7 @@ function App() {
797
927
  }
798
928
  });
799
929
 
800
- const TAB_NAMES = ['TOKENS', 'PROJECT', 'GIT'];
930
+ const TAB_NAMES = ['TOKENS', 'PROJECT', 'GIT', 'TIMELINE'];
801
931
  const since = fmtSince(Date.now() - updatedAt);
802
932
  const uptime = fmtSince(SESSION_START - Date.now() + (Date.now() - SESSION_START)); // forces tick dep
803
933
  void tick;
@@ -825,26 +955,41 @@ function App() {
825
955
  </Box>
826
956
  </Box>
827
957
 
828
- {/* ── Content (with scroll offset) ── */}
829
- <Box flexDirection="column" marginTop={-scrollY}>
830
- {tab === 0 && <TokensTab usage={usage} history={history} rateLimits={rateLimits} termWidth={termWidth} C={C} />}
831
- {tab === 1 && <ProjectTab info={project} treeCursor={treeCursor} treeExpanded={treeExpanded} selectedFile={selectedFile} fileLines={fileLines} fileScroll={fileScroll} termWidth={termWidth} git={git} C={C} />}
832
- {tab === 2 && <GitTab git={git} termWidth={termWidth} branchMode={branchMode} branchList={branchList} branchCursor={branchCursor} C={C} />}
833
- </Box>
958
+ {/* ── Content: fixed height so header/footer never get pushed off screen ── */}
959
+ {(() => {
960
+ // header ~3 rows, footer key row ~1, footer dir row ~3 = 7 total chrome
961
+ const contentH = Math.max(4, termHeight - 7);
962
+ return (
963
+ <Box flexDirection="column" height={contentH} overflow="hidden">
964
+ <Box flexDirection="column" marginTop={-scrollY}>
965
+ {tab === 0 && <TokensTab usage={usage} history={history} rateLimits={rateLimits} termWidth={termWidth} currentActivity={currentActivity} C={C} />}
966
+ {tab === 1 && <ProjectTab info={project} treeCursor={treeCursor} treeExpanded={treeExpanded} selectedFile={selectedFile} fileLines={fileLines} fileScroll={fileScroll} termWidth={termWidth} git={git} C={C} />}
967
+ {tab === 2 && <GitTab git={git} termWidth={termWidth} branchMode={branchMode} branchList={branchList} branchCursor={branchCursor} C={C} />}
968
+ {tab === 3 && <TimelineTab timeline={timeline} timelineScroll={timelineScroll} C={C} />}
969
+ </Box>
970
+ </Box>
971
+ );
972
+ })()}
834
973
 
835
- {/* ── Footer ── */}
974
+ {/* ── Footer row 1: keys ── */}
836
975
  <Box justifyContent="space-between" paddingX={1}>
837
976
  <Box>
838
977
  <Text color={C.green}>● </Text>
839
- <Text color={C.dimmer}>[1/2/3] tabs </Text>
978
+ <Text color={C.dimmer}>[1/2/3/4] tabs </Text>
840
979
  <Text color={tab === 1 ? C.brand : C.dimmer}>[j/k] {tab === 1 ? 'tree' : 'scroll'} </Text>
841
980
  <Text color={tab === 1 ? C.brand : C.dimmer}>{tab === 1 ? (selectedFile ? '[esc/←] close [j/k] scroll ' : '[enter] open [→←] expand ') : ''}</Text>
842
981
  {tab === 2 && !branchMode && <Text color={C.brand}>[b] branch </Text>}
843
- <Text color={C.dimmer}>[r] refresh [d] theme [q] quit</Text>
982
+ <Text color={C.dimmer}>[r] refresh [d] color [q] quit</Text>
844
983
  </Box>
845
984
  <Text color={C.dimmer}>↻ {since}</Text>
846
985
  </Box>
847
986
 
987
+ {/* ── Footer row 2: current dir ── */}
988
+ <Box paddingX={1} borderStyle="single" borderColor={C.brand}>
989
+ <Text color={C.brand} bold>◆ </Text>
990
+ <Text color={C.text} bold>~/{basename(cwd)}</Text>
991
+ </Box>
992
+
848
993
  </Box>
849
994
  );
850
995
  }