claude-code-hud 0.3.13 → 0.3.14

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-hud",
3
- "version": "0.3.13",
3
+ "version": "0.3.14",
4
4
  "description": "Terminal HUD for Claude Code — real-time token usage, git status, project monitor",
5
5
  "type": "module",
6
6
  "bin": {
@@ -75,7 +75,7 @@ function findLatestSession(cwd) {
75
75
  }
76
76
 
77
77
  /** Collect all JSONL lines for the given cwd (or all projects if no cwd) */
78
- function readAllLines(cwd) {
78
+ async function readAllLines(cwd) {
79
79
  const projectsDir = path.join(os.homedir(), '.claude', 'projects');
80
80
  if (!fs.existsSync(projectsDir)) return [];
81
81
  const targetDir = cwd ? cwdToProjectDir(cwd) : null;
@@ -88,7 +88,8 @@ function readAllLines(cwd) {
88
88
  const fullPath = path.join(projDir, file);
89
89
  const fileMtime = fs.statSync(fullPath).mtimeMs;
90
90
  try {
91
- const lines = fs.readFileSync(fullPath, 'utf8').split('\n').filter(Boolean);
91
+ const raw = await fs.promises.readFile(fullPath, 'utf8');
92
+ const lines = raw.split('\n').filter(Boolean);
92
93
  for (const line of lines) {
93
94
  try {
94
95
  const obj = JSON.parse(line);
@@ -103,8 +104,8 @@ function readAllLines(cwd) {
103
104
  return result;
104
105
  }
105
106
 
106
- export function readTokenHistory(cwd) {
107
- const allLines = readAllLines(cwd);
107
+ export async function readTokenHistory(cwd) {
108
+ const allLines = await readAllLines(cwd);
108
109
  const now = Date.now();
109
110
  const h5 = now - 5 * 60 * 60 * 1000;
110
111
  const wk = now - 7 * 24 * 60 * 60 * 1000;
@@ -155,7 +156,7 @@ export function readTokenHistory(cwd) {
155
156
  return { last5h: acc5h, lastWeek: accWk, today: accToday, hourlyBuckets: buckets };
156
157
  }
157
158
 
158
- export function readTokenUsage(cwd) {
159
+ export async function readTokenUsage(cwd) {
159
160
  const sessionFile = findLatestSession(cwd);
160
161
  if (!sessionFile) {
161
162
  return empty();
@@ -170,7 +171,8 @@ export function readTokenUsage(cwd) {
170
171
  // For context window: use the LAST turn's snapshot (what's in context right now)
171
172
  let lastUsage = null;
172
173
 
173
- const lines = fs.readFileSync(sessionFile, 'utf8').split('\n').filter(Boolean);
174
+ const raw = await fs.promises.readFile(sessionFile, 'utf8');
175
+ const lines = raw.split('\n').filter(Boolean);
174
176
  for (const line of lines) {
175
177
  try {
176
178
  const obj = JSON.parse(line);
package/tui/hud.tsx CHANGED
@@ -9,7 +9,9 @@ import { fileURLToPath } from 'url';
9
9
  import { dirname, join, basename } from 'path';
10
10
  import fs from 'fs';
11
11
  import os from 'os';
12
- import { execSync } from 'child_process';
12
+ import { exec as execCb } from 'child_process';
13
+ import { promisify } from 'util';
14
+ const execAsync = promisify(execCb);
13
15
 
14
16
  const __dir = dirname(fileURLToPath(import.meta.url));
15
17
  const { readTokenUsage, readTokenHistory } = await import(join(__dir, '../scripts/lib/token-reader.mjs'));
@@ -197,11 +199,11 @@ function flattenTree(node: DirNode, depth: number, expanded: Record<string, bool
197
199
  }
198
200
 
199
201
  // ── Branch helper ───────────────────────────────────────────────────────────
200
- function getBranches(cwd: string): string[] {
202
+ async function getBranches(cwd: string): Promise<string[]> {
201
203
  try {
202
- const out = execSync('git branch', { cwd, encoding: 'utf8' });
203
- return out.split('\n')
204
- .map(b => b.replace(/^\*?\s+/, '').trim())
204
+ const { stdout } = await execAsync('git branch', { cwd });
205
+ return stdout.split('\n')
206
+ .map((b: string) => b.replace(/^\*?\s+/, '').trim())
205
207
  .filter(Boolean);
206
208
  } catch {
207
209
  return [];
@@ -239,7 +241,7 @@ async function readSessionTimeline(cwd: string): Promise<TimelineEntry[]> {
239
241
 
240
242
  for (const filePath of allFiles) {
241
243
  try {
242
- const lines = fs.readFileSync(filePath, 'utf-8').split('\n').filter(Boolean);
244
+ const lines = (await fs.promises.readFile(filePath, 'utf-8')).split('\n').filter(Boolean);
243
245
  for (const line of lines) {
244
246
  try {
245
247
  const obj = JSON.parse(line);
@@ -749,8 +751,8 @@ function App() {
749
751
  const cwd = process.env.CLAUDE_PROJECT_ROOT || process.cwd();
750
752
  const C = makeTheme(accent);
751
753
 
752
- const [usage, setUsage] = useState<any>(readTokenUsage(cwd));
753
- const [history, setHistory] = useState<any>(readTokenHistory(cwd));
754
+ const [usage, setUsage] = useState<any>({ inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, totalTokens: 0, contextWindow: 200000, model: 'claude-sonnet-4', cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } });
755
+ const [history, setHistory] = useState<any>({ last5h: null, lastWeek: null, today: null, hourlyBuckets: Array(12).fill(0) });
754
756
  const [git, setGit] = useState<any>({ isRepo: false, branch: 'loading…', modified: [], added: [], deleted: [], recentCommits: [], totalChanges: 0 });
755
757
  const [project, setProject] = useState<ProjectInfo | null>(null);
756
758
  const [rateLimits, setRateLimits] = useState<any>(getUsageSync());
@@ -789,8 +791,8 @@ function App() {
789
791
  const [currentActivity, setCurrentActivity] = useState<string>('');
790
792
 
791
793
  const refresh = useCallback(() => {
792
- setUsage(readTokenUsage(cwd));
793
- setHistory(readTokenHistory(cwd));
794
+ readTokenUsage(cwd).then(setUsage).catch(() => {});
795
+ readTokenHistory(cwd).then(setHistory).catch(() => {});
794
796
  setUpdatedAt(Date.now());
795
797
  readGitInfo(cwd).then(setGit).catch(() => {});
796
798
  getUsage().then(setRateLimits).catch(() => {});
@@ -804,6 +806,9 @@ function App() {
804
806
  }, [cwd]);
805
807
 
806
808
  useEffect(() => {
809
+ // Initial token data loads (async)
810
+ readTokenUsage(cwd).then(setUsage).catch(() => {});
811
+ readTokenHistory(cwd).then(setHistory).catch(() => {});
807
812
  // Scan project once
808
813
  // Quick shallow scan first → show UI immediately
809
814
  scanProject(cwd, 2).then(p => { setProject(p); setLoading(false); })
@@ -881,11 +886,13 @@ function App() {
881
886
  if (key.return) {
882
887
  const selected = branchList[branchCursor];
883
888
  if (selected && selected !== git.branch) {
884
- try {
885
- execSync(`git checkout ${selected}`, { cwd });
886
- process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
887
- refresh();
888
- } catch {}
889
+ execAsync(`git checkout ${selected}`, { cwd })
890
+ .then(() => {
891
+ process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
892
+ readGitInfo(cwd).then(setGit).catch(() => {});
893
+ refresh();
894
+ })
895
+ .catch(() => {});
889
896
  }
890
897
  setBranchMode(false);
891
898
  return;
@@ -899,11 +906,12 @@ function App() {
899
906
 
900
907
  // b (or Korean ㅠ) = open branch switcher in GIT tab
901
908
  if ((input === 'b' || input === 'ㅠ') && tab === 2) {
902
- const branches = getBranches(cwd);
903
- setBranchList(branches);
904
- const idx = branches.findIndex(b => b === git.branch);
905
- setBranchCursor(idx >= 0 ? idx : 0);
906
- setBranchMode(true);
909
+ getBranches(cwd).then(branches => {
910
+ setBranchList(branches);
911
+ const idx = branches.findIndex((b: string) => b === git.branch);
912
+ setBranchCursor(idx >= 0 ? idx : 0);
913
+ setBranchMode(true);
914
+ });
907
915
  return;
908
916
  }
909
917