@usevalt/cli 0.7.3 → 0.7.4

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 (2) hide show
  1. package/dist/index.js +94 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1035,7 +1035,7 @@ proxyCommand.command("stop").description("Stop the MCP proxy").action(() => {
1035
1035
 
1036
1036
  // src/commands/hook.ts
1037
1037
  import { Command as Command13 } from "commander";
1038
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, existsSync as existsSync3, readdirSync } from "fs";
1038
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, existsSync as existsSync3, readdirSync, statSync, openSync, readSync, closeSync } from "fs";
1039
1039
  import { createReadStream } from "fs";
1040
1040
  import { createInterface } from "readline";
1041
1041
  import { execSync } from "child_process";
@@ -1311,6 +1311,51 @@ async function parseTranscript(transcriptPath) {
1311
1311
  }
1312
1312
  return result;
1313
1313
  }
1314
+ function parseTranscriptIncremental(transcriptPath, fromByteOffset, model) {
1315
+ const result = {
1316
+ deltaPromptTokens: 0,
1317
+ deltaCompletionTokens: 0,
1318
+ deltaCacheReadTokens: 0,
1319
+ deltaCostUsd: 0,
1320
+ newByteOffset: fromByteOffset
1321
+ };
1322
+ try {
1323
+ const fileSize = statSync(transcriptPath).size;
1324
+ if (fileSize <= fromByteOffset) return result;
1325
+ const bytesToRead = fileSize - fromByteOffset;
1326
+ const buffer = Buffer.alloc(bytesToRead);
1327
+ const fd = openSync(transcriptPath, "r");
1328
+ try {
1329
+ readSync(fd, buffer, 0, bytesToRead, fromByteOffset);
1330
+ } finally {
1331
+ closeSync(fd);
1332
+ }
1333
+ const chunk = buffer.toString("utf-8");
1334
+ const lines = chunk.split("\n");
1335
+ for (const line of lines) {
1336
+ if (!line.trim()) continue;
1337
+ try {
1338
+ const entry = JSON.parse(line);
1339
+ const usage = entry.message?.usage ?? entry.usage;
1340
+ if (usage) {
1341
+ result.deltaPromptTokens += usage.input_tokens ?? 0;
1342
+ result.deltaCompletionTokens += usage.output_tokens ?? 0;
1343
+ result.deltaCacheReadTokens += usage.cache_read_input_tokens ?? 0;
1344
+ }
1345
+ } catch {
1346
+ }
1347
+ }
1348
+ result.newByteOffset = fileSize;
1349
+ result.deltaCostUsd = calculateCost(
1350
+ model,
1351
+ result.deltaPromptTokens,
1352
+ result.deltaCompletionTokens,
1353
+ result.deltaCacheReadTokens
1354
+ );
1355
+ } catch {
1356
+ }
1357
+ return result;
1358
+ }
1314
1359
  var sessionStartCommand = new Command13("session-start").description("Hook: called when a Claude Code session starts").action(async () => {
1315
1360
  try {
1316
1361
  const { apiKey, endpoint, apiEndpoint } = resolveConfig();
@@ -1322,6 +1367,7 @@ var sessionStartCommand = new Command13("session-start").description("Hook: call
1322
1367
  const model = hookData["model"] || process.env["CLAUDE_MODEL"] || "unknown";
1323
1368
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
1324
1369
  const cwd = hookData["cwd"] || process.cwd();
1370
+ const transcriptPath = hookData["transcript_path"] || void 0;
1325
1371
  const state = {
1326
1372
  sessionId,
1327
1373
  claudeSessionId,
@@ -1331,7 +1377,8 @@ var sessionStartCommand = new Command13("session-start").description("Hook: call
1331
1377
  apiEndpoint,
1332
1378
  projectSlug,
1333
1379
  model,
1334
- cwd
1380
+ cwd,
1381
+ transcriptPath
1335
1382
  };
1336
1383
  writeFileSync3(getSessionFile(claudeSessionId), JSON.stringify(state, null, 2), { mode: 384 });
1337
1384
  let gitCommitBefore;
@@ -1500,6 +1547,45 @@ var postToolUseCommand = new Command13("post-tool-use").description("Hook: calle
1500
1547
  }
1501
1548
  }
1502
1549
  }
1550
+ try {
1551
+ const tPath = hookData["transcript_path"] ?? session.transcriptPath;
1552
+ if (tPath && existsSync3(tPath)) {
1553
+ if (!session.transcriptPath) session.transcriptPath = tPath;
1554
+ const deltas = parseTranscriptIncremental(
1555
+ tPath,
1556
+ session.transcriptByteOffset ?? 0,
1557
+ session.model ?? "unknown"
1558
+ );
1559
+ if (deltas.deltaPromptTokens > 0 || deltas.deltaCompletionTokens > 0) {
1560
+ events.push({
1561
+ event_id: crypto.randomUUID(),
1562
+ session_id: session.sessionId,
1563
+ event_type: "cost",
1564
+ timestamp,
1565
+ tool: "claude-code",
1566
+ tokens_prompt: deltas.deltaPromptTokens,
1567
+ tokens_completion: deltas.deltaCompletionTokens,
1568
+ cost_usd: deltas.deltaCostUsd,
1569
+ metadata: {
1570
+ model: session.model,
1571
+ source: "incremental",
1572
+ cache_read_tokens: deltas.deltaCacheReadTokens
1573
+ }
1574
+ });
1575
+ }
1576
+ session.transcriptByteOffset = deltas.newByteOffset;
1577
+ session.runningPromptTokens = (session.runningPromptTokens ?? 0) + deltas.deltaPromptTokens;
1578
+ session.runningCompletionTokens = (session.runningCompletionTokens ?? 0) + deltas.deltaCompletionTokens;
1579
+ session.runningCacheReadTokens = (session.runningCacheReadTokens ?? 0) + deltas.deltaCacheReadTokens;
1580
+ session.runningCostUsd = (session.runningCostUsd ?? 0) + deltas.deltaCostUsd;
1581
+ writeFileSync3(
1582
+ getSessionFile(session.claudeSessionId),
1583
+ JSON.stringify(session, null, 2),
1584
+ { mode: 384 }
1585
+ );
1586
+ }
1587
+ } catch {
1588
+ }
1503
1589
  await sendEvents(session.endpoint, session.apiKey, events);
1504
1590
  } catch {
1505
1591
  }
@@ -1626,6 +1712,12 @@ var sessionEndCommand = new Command13("session-end").description("Hook: called w
1626
1712
  const durationMs = now.getTime() - startedAt.getTime();
1627
1713
  const transcriptPath = hookData["transcript_path"];
1628
1714
  const metrics = transcriptPath ? await parseTranscript(transcriptPath) : null;
1715
+ if (metrics && (session.runningPromptTokens || session.runningCostUsd)) {
1716
+ metrics.promptTokens = Math.max(0, metrics.promptTokens - (session.runningPromptTokens ?? 0));
1717
+ metrics.completionTokens = Math.max(0, metrics.completionTokens - (session.runningCompletionTokens ?? 0));
1718
+ metrics.cacheReadTokens = Math.max(0, metrics.cacheReadTokens - (session.runningCacheReadTokens ?? 0));
1719
+ metrics.totalCostUsd = Math.max(0, metrics.totalCostUsd - (session.runningCostUsd ?? 0));
1720
+ }
1629
1721
  let gitCommitAfter;
1630
1722
  try {
1631
1723
  gitCommitAfter = execSync("git rev-parse HEAD", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usevalt/cli",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "description": "Valt CLI — trust layer for AI-assisted development",
5
5
  "license": "MIT",
6
6
  "repository": {