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 +1 -1
- package/scripts/lib/token-reader.mjs +8 -6
- package/tui/hud.tsx +28 -20
package/package.json
CHANGED
|
@@ -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
|
|
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
|
|
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 {
|
|
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
|
|
203
|
-
return
|
|
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.
|
|
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>(
|
|
753
|
-
const [history, setHistory] = useState<any>(
|
|
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
|
-
|
|
793
|
-
|
|
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
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
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
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
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
|
|