Skip to content

Commit dbbf714

Browse files
NickCirvclaude
andcommitted
feat: engram hud-label command for Claude HUD integration
New CLI command `engram hud-label` outputs JSON for Claude HUD's --extra-cmd protocol. Shows ⚡engram with token savings + visual hit rate bar (▰/▱). Runs in <20ms via hook-log parsing. Users can add to their Claude HUD: --extra-cmd="engram hud-label" States: ready → listening → savings + bar + percentage. Bar fills as hit rate climbs. Savings number grows over session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 31491ec commit dbbf714

2 files changed

Lines changed: 55 additions & 1 deletion

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "engramx",
3-
"version": "0.4.2",
3+
"version": "0.4.3",
44
"description": "The structural code graph your AI agent can't forget to use. A Claude Code hook layer that intercepts Read/Edit/Write/Bash and replaces file contents with ~300-token structural graph summaries. 82% measured token reduction. Context rot is empirically solved — cite Chroma. Local SQLite, zero LLM cost, zero cloud, zero native deps.",
55
"type": "module",
66
"bin": {

src/cli.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,60 @@ program
192192
await new Promise(() => {});
193193
});
194194

195+
program
196+
.command("hud-label")
197+
.description("Output JSON label for Claude HUD --extra-cmd (fast, <20ms)")
198+
.argument("[path]", "Project directory", ".")
199+
.action(async (projectPath: string) => {
200+
const resolvedPath = pathResolve(projectPath);
201+
const logPath = join(resolvedPath, ".engram", "hook-log.jsonl");
202+
203+
if (!existsSync(join(resolvedPath, ".engram", "graph.db"))) {
204+
console.log('{"label":""}');
205+
return;
206+
}
207+
208+
if (!existsSync(logPath)) {
209+
console.log('{"label":"⚡engram ░░░░░░░░░░ ready"}');
210+
return;
211+
}
212+
213+
try {
214+
const entries = readHookLog(resolvedPath);
215+
const summary = summarizeHookLog(entries);
216+
217+
if (summary.totalInvocations === 0) {
218+
console.log('{"label":"⚡engram ░░░░░░░░░░ listening..."}');
219+
return;
220+
}
221+
222+
const totalPreTool =
223+
(summary.byDecision["deny"] ?? 0) +
224+
(summary.byDecision["allow"] ?? 0) +
225+
(summary.byDecision["passthrough"] ?? 0);
226+
const denied = summary.readDenyCount;
227+
const hitRate = totalPreTool > 0 ? Math.round((denied / totalPreTool) * 100) : 0;
228+
const tokens = summary.estimatedTokensSaved;
229+
230+
// Format tokens
231+
let formatted: string;
232+
if (tokens >= 1_000_000) formatted = (tokens / 1_000_000).toFixed(1) + "M";
233+
else if (tokens >= 1_000) formatted = (tokens / 1_000).toFixed(1) + "K";
234+
else formatted = String(tokens);
235+
236+
// Build bar (10 chars)
237+
const barWidth = 10;
238+
let filled = Math.round((hitRate / 100) * barWidth);
239+
if (filled > barWidth) filled = barWidth;
240+
if (denied > 0 && filled === 0) filled = 1;
241+
const bar = "▰".repeat(filled) + "▱".repeat(barWidth - filled);
242+
243+
console.log(JSON.stringify({ label: `⚡engram ${formatted} saved ${bar} ${hitRate}%` }));
244+
} catch {
245+
console.log('{"label":"⚡engram"}');
246+
}
247+
});
248+
195249
program
196250
.command("query")
197251
.description("Query the knowledge graph")

0 commit comments

Comments
 (0)