@usevalt/cli 0.7.4 → 0.9.0

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 +799 -78
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -1035,13 +1035,340 @@ 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, statSync, openSync, readSync, closeSync } from "fs";
1039
- import { createReadStream } from "fs";
1040
- import { createInterface } from "readline";
1038
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, existsSync as existsSync4, readdirSync, statSync, openSync, readSync, closeSync } from "fs";
1039
+ import { createReadStream as createReadStream2 } from "fs";
1040
+ import { createInterface as createInterface2 } from "readline";
1041
1041
  import { execSync } from "child_process";
1042
1042
  import { basename, join } from "path";
1043
1043
  import { createHash } from "crypto";
1044
1044
  import os2 from "os";
1045
+
1046
+ // src/lib/content.ts
1047
+ import { basename as pathBasename } from "path";
1048
+ var MAX_CONTENT_LENGTH = 2048;
1049
+ function truncateContent(s, max = MAX_CONTENT_LENGTH) {
1050
+ if (s.length <= max) return s;
1051
+ return s.slice(0, max - 3) + "...";
1052
+ }
1053
+ function fileBasename(p) {
1054
+ if (!p) return "unknown";
1055
+ return pathBasename(p);
1056
+ }
1057
+ var COMMAND_PATTERNS = [
1058
+ ["test", /vitest|jest|pytest|go\s+test|cargo\s+test|npm\s+test|pnpm\s+test|mocha|ava/i],
1059
+ ["typecheck", /tsc\s+--noEmit|mypy|pyright|pnpm\s+typecheck/i],
1060
+ ["build", /tsc(?!\s+--noEmit)|webpack|vite\s+build|cargo\s+build|go\s+build|npm\s+run\s+build|pnpm\s+build|esbuild|tsup/i],
1061
+ ["lint", /eslint|biome(?!\s+format)|ruff|clippy|golangci-lint|pylint|oxlint/i],
1062
+ ["format", /prettier|black|gofmt|rustfmt|biome\s+format/i],
1063
+ ["git", /^git\s/i],
1064
+ ["deploy", /vercel|fly\s|docker\s+push|kubectl|railway/i],
1065
+ ["install", /npm\s+install|pnpm\s+(add|install)|pip\s+install|cargo\s+add|yarn\s+add|bun\s+add/i],
1066
+ ["dev", /npm\s+run\s+dev|pnpm\s+dev|cargo\s+run|go\s+run|bun\s+dev/i],
1067
+ ["search", /grep|rg\s|find\s|fd\s|ag\s/i]
1068
+ ];
1069
+ function classifyCommand(command) {
1070
+ for (const [category, pattern] of COMMAND_PATTERNS) {
1071
+ if (pattern.test(command)) return category;
1072
+ }
1073
+ return "other";
1074
+ }
1075
+ function summarizeToolInput(toolName, toolInput) {
1076
+ switch (toolName) {
1077
+ case "Read":
1078
+ return `Read ${fileBasename(toolInput["file_path"])}`;
1079
+ case "Write": {
1080
+ const content = toolInput["content"];
1081
+ const chars = content ? ` (${content.length} chars)` : "";
1082
+ return `Write ${fileBasename(toolInput["file_path"])}${chars}`;
1083
+ }
1084
+ case "Edit":
1085
+ return `Edit ${fileBasename(toolInput["file_path"])}`;
1086
+ case "NotebookEdit":
1087
+ return `Edit notebook ${fileBasename(toolInput["notebook_path"])}`;
1088
+ case "Bash": {
1089
+ const cmd = toolInput["command"] ?? "";
1090
+ const desc = toolInput["description"];
1091
+ const category = classifyCommand(cmd);
1092
+ const label = desc || truncateContent(cmd, 200);
1093
+ return `[${category}] ${label}`;
1094
+ }
1095
+ case "Glob":
1096
+ return `Search files: ${toolInput["pattern"] ?? "*"}`;
1097
+ case "Grep":
1098
+ return `Search content: /${toolInput["pattern"] ?? ""}/ in ${fileBasename(toolInput["path"])}`;
1099
+ case "WebFetch":
1100
+ return `Fetch: ${truncateContent(toolInput["url"] ?? "", 120)}`;
1101
+ case "WebSearch":
1102
+ return `Search web: ${truncateContent(toolInput["query"] ?? "", 120)}`;
1103
+ case "Task":
1104
+ return `Subagent: ${toolInput["description"] ?? "task"}`;
1105
+ case "TaskCreate":
1106
+ return `Create task: ${toolInput["subject"] ?? ""}`;
1107
+ case "TaskUpdate":
1108
+ return `Update task #${toolInput["taskId"] ?? "?"}: ${toolInput["status"] ?? ""}`;
1109
+ case "Skill":
1110
+ return `Skill: ${toolInput["skill"] ?? ""}`;
1111
+ case "AskUserQuestion": {
1112
+ const questions = toolInput["questions"];
1113
+ const q = questions?.[0]?.question ?? "";
1114
+ return `Ask user: ${truncateContent(q, 120)}`;
1115
+ }
1116
+ default:
1117
+ return `Tool: ${toolName}`;
1118
+ }
1119
+ }
1120
+ function summarizeToolResponse(toolName, _toolInput, toolResponse) {
1121
+ switch (toolName) {
1122
+ case "Bash": {
1123
+ const exitCode = toolResponse["exit_code"] ?? toolResponse["exitCode"] ?? 0;
1124
+ const stdout = toolResponse["stdout"] ?? toolResponse["output"] ?? "";
1125
+ const stderr = toolResponse["stderr"] ?? "";
1126
+ const output = Number(exitCode) !== 0 ? stderr || stdout : stdout;
1127
+ return `Result: exit=${exitCode} \u2014 ${lastLine(output)}`;
1128
+ }
1129
+ case "Read": {
1130
+ const content = toolResponse["content"] ?? "";
1131
+ const lines = content ? content.split("\n").length : 0;
1132
+ return `Result: Read \u2014 ${lines} lines`;
1133
+ }
1134
+ case "Write":
1135
+ return "Result: Write \u2014 success";
1136
+ case "Edit":
1137
+ return "Result: Edit \u2014 success";
1138
+ case "Glob": {
1139
+ const files = toolResponse["files"];
1140
+ const numFiles = toolResponse["numFiles"] ?? files?.length ?? 0;
1141
+ return `Result: Glob \u2014 ${numFiles} matches`;
1142
+ }
1143
+ case "Grep": {
1144
+ const numFiles = toolResponse["numFiles"] ?? toolResponse["numLines"] ?? 0;
1145
+ return `Result: Grep \u2014 ${numFiles} matches`;
1146
+ }
1147
+ case "WebFetch": {
1148
+ const content = toolResponse["content"] ?? "";
1149
+ return `Result: Fetch \u2014 ${content.length} chars`;
1150
+ }
1151
+ case "WebSearch": {
1152
+ const results = toolResponse["results"];
1153
+ const numResults = toolResponse["numResults"] ?? results?.length ?? 0;
1154
+ return `Result: Search \u2014 ${numResults} results`;
1155
+ }
1156
+ case "Task": {
1157
+ const status = toolResponse["status"] ?? "done";
1158
+ return `Result: Subagent \u2014 ${status}`;
1159
+ }
1160
+ default:
1161
+ return `Result: ${toolName} \u2014 success`;
1162
+ }
1163
+ }
1164
+ function lastLine(text) {
1165
+ if (!text) return "";
1166
+ const lines = text.split("\n").filter((l) => l.trim() !== "");
1167
+ const line = lines[lines.length - 1] ?? "";
1168
+ return truncateContent(line, 120);
1169
+ }
1170
+
1171
+ // src/lib/transcriptParser.ts
1172
+ import { existsSync as existsSync3 } from "fs";
1173
+ import { createReadStream } from "fs";
1174
+ import { createInterface } from "readline";
1175
+ import { randomUUID } from "crypto";
1176
+ var MAX_TOOL_DATA_LENGTH = 2048;
1177
+ var MAX_THINKING_LENGTH = 500;
1178
+ var MAX_TEXT_LENGTH = 500;
1179
+ function truncateObject(obj, maxLen) {
1180
+ const str = JSON.stringify(obj);
1181
+ if (str.length <= maxLen) return obj;
1182
+ const result = {};
1183
+ let currentLen = 2;
1184
+ for (const [key, value] of Object.entries(obj)) {
1185
+ const valStr = typeof value === "string" ? `"${value.slice(0, 200)}"` : JSON.stringify(value)?.slice(0, 200) ?? "null";
1186
+ const entryLen = key.length + valStr.length + 4;
1187
+ if (currentLen + entryLen > maxLen) {
1188
+ result["_truncated"] = true;
1189
+ break;
1190
+ }
1191
+ result[key] = typeof value === "string" && value.length > 200 ? value.slice(0, 200) + "..." : value;
1192
+ currentLen += entryLen;
1193
+ }
1194
+ return result;
1195
+ }
1196
+ async function extractTranscriptEvents(transcriptPath) {
1197
+ const events = [];
1198
+ if (!existsSync3(transcriptPath)) return events;
1199
+ const seenMessageIds = /* @__PURE__ */ new Map();
1200
+ const toolUseIdMap = /* @__PURE__ */ new Map();
1201
+ try {
1202
+ const rl = createInterface({
1203
+ input: createReadStream(transcriptPath, "utf-8"),
1204
+ crlfDelay: Infinity
1205
+ });
1206
+ for await (const line of rl) {
1207
+ if (!line.trim()) continue;
1208
+ try {
1209
+ const entry = JSON.parse(line);
1210
+ const entryType = entry.type ?? entry.role ?? "";
1211
+ const subtype = entry.subtype;
1212
+ if (["file-history-snapshot", "progress", "queue-operation"].includes(entryType)) {
1213
+ continue;
1214
+ }
1215
+ const message = entry.message;
1216
+ const messageId = message?.id;
1217
+ const timestamp = entry.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
1218
+ let usage;
1219
+ let model;
1220
+ if (message) {
1221
+ const rawUsage = message.usage;
1222
+ if (rawUsage) {
1223
+ usage = {
1224
+ inputTokens: rawUsage.input_tokens ?? 0,
1225
+ outputTokens: rawUsage.output_tokens ?? 0,
1226
+ cacheReadTokens: rawUsage.cache_read_input_tokens ?? 0,
1227
+ cacheCreationTokens: rawUsage.cache_creation_input_tokens ?? 0
1228
+ };
1229
+ }
1230
+ model = message.model;
1231
+ if (messageId) {
1232
+ seenMessageIds.set(messageId, { usage, model });
1233
+ }
1234
+ }
1235
+ const content = message?.content ?? entry.content;
1236
+ if (entryType === "assistant") {
1237
+ if (!Array.isArray(content)) continue;
1238
+ for (const block of content) {
1239
+ if (typeof block !== "object" || block === null) continue;
1240
+ const blockRec = block;
1241
+ const blockType = blockRec.type;
1242
+ if (blockType === "tool_use") {
1243
+ const toolName = blockRec.name ?? "unknown";
1244
+ const toolUseId = blockRec.id;
1245
+ const toolInput = truncateObject(
1246
+ blockRec.input ?? {},
1247
+ MAX_TOOL_DATA_LENGTH
1248
+ );
1249
+ const contentSummary = summarizeToolInput(toolName, toolInput);
1250
+ const eventUuid = randomUUID();
1251
+ if (toolUseId) {
1252
+ toolUseIdMap.set(toolUseId, eventUuid);
1253
+ }
1254
+ events.push({
1255
+ uuid: eventUuid,
1256
+ parentUuid: null,
1257
+ timestamp,
1258
+ type: "tool_use",
1259
+ toolName,
1260
+ toolUseId,
1261
+ toolInput,
1262
+ usage,
1263
+ model,
1264
+ content: contentSummary
1265
+ });
1266
+ } else if (blockType === "thinking") {
1267
+ const thinkingText = blockRec.thinking ?? "";
1268
+ events.push({
1269
+ uuid: randomUUID(),
1270
+ parentUuid: null,
1271
+ timestamp,
1272
+ type: "thinking",
1273
+ thinkingText: thinkingText.slice(0, MAX_THINKING_LENGTH),
1274
+ usage,
1275
+ model,
1276
+ content: `Thinking: ${truncateContent(thinkingText, 120)}`
1277
+ });
1278
+ } else if (blockType === "text") {
1279
+ const text = blockRec.text ?? "";
1280
+ events.push({
1281
+ uuid: randomUUID(),
1282
+ parentUuid: null,
1283
+ timestamp,
1284
+ type: "text",
1285
+ text: text.slice(0, MAX_TEXT_LENGTH),
1286
+ usage,
1287
+ model,
1288
+ content: `Text: ${truncateContent(text, 120)}`
1289
+ });
1290
+ }
1291
+ }
1292
+ }
1293
+ if (entryType === "human" || entryType === "user") {
1294
+ if (!Array.isArray(content)) continue;
1295
+ for (const block of content) {
1296
+ if (typeof block !== "object" || block === null) continue;
1297
+ const blockRec = block;
1298
+ if (blockRec.type !== "tool_result") continue;
1299
+ const toolUseId = blockRec.tool_use_id;
1300
+ const isError = blockRec.is_error ?? false;
1301
+ const resultContent = blockRec.content;
1302
+ const toolResponse = truncateObject(
1303
+ typeof resultContent === "string" ? { output: resultContent.slice(0, MAX_TOOL_DATA_LENGTH) } : resultContent ?? {},
1304
+ MAX_TOOL_DATA_LENGTH
1305
+ );
1306
+ const parentUuid = toolUseId ? toolUseIdMap.get(toolUseId) ?? null : null;
1307
+ let toolName = "unknown";
1308
+ if (parentUuid) {
1309
+ const parent = events.find((e) => e.uuid === parentUuid);
1310
+ if (parent?.toolName) toolName = parent.toolName;
1311
+ }
1312
+ const contentSummary = isError ? `Error: ${toolName} \u2014 ${truncateContent(typeof resultContent === "string" ? resultContent : "failed", 200)}` : summarizeToolResponse(toolName, {}, toolResponse);
1313
+ events.push({
1314
+ uuid: randomUUID(),
1315
+ parentUuid,
1316
+ timestamp,
1317
+ type: "tool_result",
1318
+ toolUseId,
1319
+ toolName,
1320
+ toolResponse,
1321
+ isError,
1322
+ content: contentSummary
1323
+ });
1324
+ }
1325
+ }
1326
+ if (entryType === "system") {
1327
+ if (subtype === "compact_boundary") {
1328
+ const trigger = entry.trigger ?? "auto";
1329
+ const preTokens = entry.pre_tokens;
1330
+ events.push({
1331
+ uuid: randomUUID(),
1332
+ parentUuid: null,
1333
+ timestamp,
1334
+ type: "compact",
1335
+ compactTrigger: trigger,
1336
+ preTokens,
1337
+ content: `Context compacted (${trigger}${preTokens ? `, ${preTokens} tokens` : ""})`
1338
+ });
1339
+ } else if (subtype === "api_error") {
1340
+ const errorCause = entry.error ?? entry.message ?? "unknown";
1341
+ const retryAttempt = entry.retry_attempt;
1342
+ events.push({
1343
+ uuid: randomUUID(),
1344
+ parentUuid: null,
1345
+ timestamp,
1346
+ type: "api_error",
1347
+ errorCause: typeof errorCause === "string" ? errorCause : "unknown",
1348
+ retryAttempt,
1349
+ content: `API error: ${truncateContent(typeof errorCause === "string" ? errorCause : "unknown", 200)}`
1350
+ });
1351
+ } else if (subtype === "turn_duration") {
1352
+ const durationMs = entry.duration_ms;
1353
+ events.push({
1354
+ uuid: randomUUID(),
1355
+ parentUuid: null,
1356
+ timestamp,
1357
+ type: "turn_duration",
1358
+ durationMs,
1359
+ content: `Turn duration: ${durationMs ?? 0}ms`
1360
+ });
1361
+ }
1362
+ }
1363
+ } catch {
1364
+ }
1365
+ }
1366
+ } catch {
1367
+ }
1368
+ return events;
1369
+ }
1370
+
1371
+ // src/commands/hook.ts
1045
1372
  function getSessionFile(claudeSessionId) {
1046
1373
  return join(os2.tmpdir(), `valt-session-${claudeSessionId}.json`);
1047
1374
  }
@@ -1144,7 +1471,7 @@ function readSessionFile(claudeSessionId) {
1144
1471
  if (claudeSessionId) {
1145
1472
  const path = getSessionFile(claudeSessionId);
1146
1473
  try {
1147
- if (existsSync3(path)) {
1474
+ if (existsSync4(path)) {
1148
1475
  return JSON.parse(readFileSync3(path, "utf-8"));
1149
1476
  }
1150
1477
  } catch {
@@ -1272,10 +1599,10 @@ async function parseTranscript(transcriptPath) {
1272
1599
  cacheCreationTokens: 0,
1273
1600
  model: "unknown"
1274
1601
  };
1275
- if (!existsSync3(transcriptPath)) return result;
1602
+ if (!existsSync4(transcriptPath)) return result;
1276
1603
  try {
1277
- const rl = createInterface({
1278
- input: createReadStream(transcriptPath, "utf-8"),
1604
+ const rl = createInterface2({
1605
+ input: createReadStream2(transcriptPath, "utf-8"),
1279
1606
  crlfDelay: Infinity
1280
1607
  });
1281
1608
  for await (const line of rl) {
@@ -1397,6 +1724,7 @@ var sessionStartCommand = new Command13("session-start").description("Hook: call
1397
1724
  session_id: sessionId,
1398
1725
  event_type: "session.start",
1399
1726
  timestamp: startedAt,
1727
+ content: `Session started \u2014 ${model} on ${projectSlug}`,
1400
1728
  tool: "claude-code",
1401
1729
  model,
1402
1730
  metadata: {
@@ -1468,6 +1796,8 @@ var toolCallCommand = new Command13("tool-call").description("Hook: called on ea
1468
1796
  }
1469
1797
  }
1470
1798
  const { eventType, filePath, command } = classifyToolCall(toolName, toolInput);
1799
+ const contentSummary = summarizeToolInput(toolName, toolInput);
1800
+ const toolInputSummaryMeta = JSON.stringify(toolInput).slice(0, 1024);
1471
1801
  const events = [];
1472
1802
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1473
1803
  events.push({
@@ -1475,20 +1805,23 @@ var toolCallCommand = new Command13("tool-call").description("Hook: called on ea
1475
1805
  session_id: session.sessionId,
1476
1806
  event_type: "tool.call",
1477
1807
  timestamp,
1808
+ content: contentSummary,
1478
1809
  tool: "claude-code",
1479
1810
  tool_name: toolName,
1480
1811
  metadata: {
1481
1812
  tool_name: toolName,
1482
1813
  file_path: filePath,
1483
- command
1814
+ command,
1815
+ tool_input_summary: toolInputSummaryMeta
1484
1816
  }
1485
1817
  });
1486
1818
  if (eventType !== "tool.call") {
1487
- events.push({
1819
+ const classifiedEvent = {
1488
1820
  event_id: crypto.randomUUID(),
1489
1821
  session_id: session.sessionId,
1490
1822
  event_type: eventType,
1491
1823
  timestamp,
1824
+ content: contentSummary,
1492
1825
  tool: "claude-code",
1493
1826
  tool_name: toolName,
1494
1827
  file_path: filePath,
@@ -1497,7 +1830,11 @@ var toolCallCommand = new Command13("tool-call").description("Hook: called on ea
1497
1830
  ...filePath ? { file_path: filePath } : {},
1498
1831
  ...command ? { command } : {}
1499
1832
  }
1500
- });
1833
+ };
1834
+ if (eventType === "command.execute" && command) {
1835
+ classifiedEvent["command_category"] = classifyCommand(command);
1836
+ }
1837
+ events.push(classifiedEvent);
1501
1838
  }
1502
1839
  await sendEvents(session.endpoint, session.apiKey, events);
1503
1840
  } catch {
@@ -1510,19 +1847,24 @@ var postToolUseCommand = new Command13("post-tool-use").description("Hook: calle
1510
1847
  const session = readSessionFile(claudeSessionId);
1511
1848
  if (!session) return;
1512
1849
  const toolName = hookData["tool_name"] ?? "unknown";
1850
+ const toolInput = hookData["tool_input"] ?? {};
1513
1851
  const toolResponse = hookData["tool_response"];
1514
1852
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1853
+ const resultContent = summarizeToolResponse(toolName, toolInput, toolResponse ?? {});
1854
+ const toolResponseSummaryMeta = JSON.stringify(toolResponse ?? {}).slice(0, 1024);
1515
1855
  const events = [{
1516
1856
  event_id: crypto.randomUUID(),
1517
1857
  session_id: session.sessionId,
1518
1858
  event_type: "tool.result",
1519
1859
  timestamp,
1860
+ content: resultContent,
1520
1861
  tool: "claude-code",
1521
1862
  tool_name: toolName,
1522
1863
  metadata: {
1523
1864
  tool_name: toolName,
1524
1865
  exit_code: toolResponse?.["exit_code"],
1525
- success: toolResponse?.["success"] ?? true
1866
+ success: toolResponse?.["success"] ?? true,
1867
+ tool_response_summary: toolResponseSummaryMeta
1526
1868
  }
1527
1869
  }];
1528
1870
  if (toolName === "Bash" && toolResponse) {
@@ -1530,11 +1872,13 @@ var postToolUseCommand = new Command13("post-tool-use").description("Hook: calle
1530
1872
  if (stdout) {
1531
1873
  const testResults = parseTestResults(stdout);
1532
1874
  if (testResults) {
1875
+ const testContent = `Tests: ${testResults.testsPassed}/${testResults.testsRun} passed (${testResults.framework}) ${testResults.testsFailed === 0 ? "PASS" : "FAIL"}`;
1533
1876
  events.push({
1534
1877
  event_id: crypto.randomUUID(),
1535
1878
  session_id: session.sessionId,
1536
1879
  event_type: "test.run",
1537
1880
  timestamp,
1881
+ content: testContent,
1538
1882
  tool: "claude-code",
1539
1883
  metadata: {
1540
1884
  tests_run: testResults.testsRun,
@@ -1549,7 +1893,7 @@ var postToolUseCommand = new Command13("post-tool-use").description("Hook: calle
1549
1893
  }
1550
1894
  try {
1551
1895
  const tPath = hookData["transcript_path"] ?? session.transcriptPath;
1552
- if (tPath && existsSync3(tPath)) {
1896
+ if (tPath && existsSync4(tPath)) {
1553
1897
  if (!session.transcriptPath) session.transcriptPath = tPath;
1554
1898
  const deltas = parseTranscriptIncremental(
1555
1899
  tPath,
@@ -1604,6 +1948,7 @@ var toolErrorCommand = new Command13("tool-error").description("Hook: called whe
1604
1948
  session_id: session.sessionId,
1605
1949
  event_type: "tool.error",
1606
1950
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1951
+ content: `Error: ${toolName} \u2014 ${truncateContent(errorMsg, 200)}`,
1607
1952
  tool: "claude-code",
1608
1953
  tool_name: toolName,
1609
1954
  metadata: {
@@ -1621,15 +1966,17 @@ var subagentStartCommand = new Command13("subagent-start").description("Hook: ca
1621
1966
  const claudeSessionId = hookData["session_id"];
1622
1967
  const session = readSessionFile(claudeSessionId);
1623
1968
  if (!session) return;
1969
+ const agentType = hookData["agent_type"] ?? "unknown";
1624
1970
  await sendEvents(session.endpoint, session.apiKey, [{
1625
1971
  event_id: crypto.randomUUID(),
1626
1972
  session_id: session.sessionId,
1627
1973
  event_type: "subagent.start",
1628
1974
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1975
+ content: `Subagent started: ${agentType}`,
1629
1976
  tool: "claude-code",
1630
1977
  metadata: {
1631
1978
  agent_id: hookData["agent_id"],
1632
- agent_type: hookData["agent_type"]
1979
+ agent_type: agentType
1633
1980
  }
1634
1981
  }]);
1635
1982
  } catch {
@@ -1641,15 +1988,17 @@ var subagentStopCommand = new Command13("subagent-stop").description("Hook: call
1641
1988
  const claudeSessionId = hookData["session_id"];
1642
1989
  const session = readSessionFile(claudeSessionId);
1643
1990
  if (!session) return;
1991
+ const stopAgentType = hookData["agent_type"] ?? "unknown";
1644
1992
  await sendEvents(session.endpoint, session.apiKey, [{
1645
1993
  event_id: crypto.randomUUID(),
1646
1994
  session_id: session.sessionId,
1647
1995
  event_type: "subagent.stop",
1648
1996
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1997
+ content: `Subagent stopped: ${stopAgentType}`,
1649
1998
  tool: "claude-code",
1650
1999
  metadata: {
1651
2000
  agent_id: hookData["agent_id"],
1652
- agent_type: hookData["agent_type"],
2001
+ agent_type: stopAgentType,
1653
2002
  transcript_path: hookData["agent_transcript_path"]
1654
2003
  }
1655
2004
  }]);
@@ -1669,6 +2018,7 @@ var promptSubmitCommand = new Command13("prompt-submit").description("Hook: call
1669
2018
  session_id: session.sessionId,
1670
2019
  event_type: "prompt.submit",
1671
2020
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2021
+ content: `Prompt submitted (${promptText.length} chars)`,
1672
2022
  tool: "claude-code",
1673
2023
  metadata: {
1674
2024
  prompt_hash: promptHashValue,
@@ -1728,12 +2078,15 @@ var sessionEndCommand = new Command13("session-end").description("Hook: called w
1728
2078
  }).trim();
1729
2079
  } catch {
1730
2080
  }
2081
+ const durationSec = Math.round(durationMs / 1e3);
2082
+ const costStr = metrics?.totalCostUsd ? `$${metrics.totalCostUsd.toFixed(4)}` : "$0";
1731
2083
  const events = [
1732
2084
  {
1733
2085
  event_id: crypto.randomUUID(),
1734
2086
  session_id: session.sessionId,
1735
2087
  event_type: "session.end",
1736
2088
  timestamp: now.toISOString(),
2089
+ content: `Session ended \u2014 ${durationSec}s, ${costStr}`,
1737
2090
  duration_ms: durationMs,
1738
2091
  tool: "claude-code",
1739
2092
  model: metrics?.model ?? session.model,
@@ -1752,11 +2105,13 @@ var sessionEndCommand = new Command13("session-end").description("Hook: called w
1752
2105
  }
1753
2106
  ];
1754
2107
  if (metrics && (metrics.totalCostUsd > 0 || metrics.promptTokens > 0)) {
2108
+ const totalTokens = metrics.promptTokens + metrics.completionTokens;
1755
2109
  events.push({
1756
2110
  event_id: crypto.randomUUID(),
1757
2111
  session_id: session.sessionId,
1758
2112
  event_type: "cost",
1759
2113
  timestamp: now.toISOString(),
2114
+ content: `Cost: $${metrics.totalCostUsd.toFixed(4)} (${totalTokens} tokens)`,
1760
2115
  tool: "claude-code",
1761
2116
  tokens_prompt: metrics.promptTokens,
1762
2117
  tokens_completion: metrics.completionTokens,
@@ -1770,114 +2125,480 @@ var sessionEndCommand = new Command13("session-end").description("Hook: called w
1770
2125
  }
1771
2126
  sendEvents(session.endpoint, session.apiKey, events).catch(() => {
1772
2127
  });
2128
+ if (transcriptPath) {
2129
+ extractTranscriptEvents(transcriptPath).then((transcriptEvents) => {
2130
+ if (transcriptEvents.length === 0) return;
2131
+ const valtEvents = transcriptEvents.map((te) => ({
2132
+ event_id: te.uuid,
2133
+ session_id: session.sessionId,
2134
+ event_type: te.type === "tool_use" ? "tool.call" : te.type === "tool_result" ? "tool.result" : te.type === "thinking" ? "reasoning" : te.type === "text" ? "completion" : te.type === "compact" ? "context.compact" : te.type === "api_error" ? "error" : "cost",
2135
+ event_subtype: `transcript.${te.type}`,
2136
+ timestamp: te.timestamp,
2137
+ content: te.content,
2138
+ tool: "claude-code",
2139
+ tool_name: te.toolName ?? "",
2140
+ model: te.model ?? "",
2141
+ tokens_prompt: te.usage?.inputTokens ?? 0,
2142
+ tokens_completion: te.usage?.outputTokens ?? 0,
2143
+ metadata: {
2144
+ source: "transcript",
2145
+ parent_uuid: te.parentUuid,
2146
+ tool_use_id: te.toolUseId,
2147
+ is_error: te.isError,
2148
+ cache_read_tokens: te.usage?.cacheReadTokens,
2149
+ cache_creation_tokens: te.usage?.cacheCreationTokens
2150
+ }
2151
+ }));
2152
+ const BATCH_SIZE = 100;
2153
+ for (let i = 0; i < valtEvents.length; i += BATCH_SIZE) {
2154
+ const batch = valtEvents.slice(i, i + BATCH_SIZE);
2155
+ sendEvents(session.endpoint, session.apiKey, batch).catch(() => {
2156
+ });
2157
+ }
2158
+ }).catch(() => {
2159
+ });
2160
+ }
1773
2161
  if (sessionFilePath) {
1774
2162
  try {
1775
2163
  unlinkSync2(sessionFilePath);
1776
2164
  } catch {
1777
2165
  }
1778
2166
  }
1779
- const costStr = metrics?.totalCostUsd ? ` $${metrics.totalCostUsd.toFixed(4)}` : "";
1780
- const tokenStr = metrics?.promptTokens ? ` ${metrics.promptTokens + metrics.completionTokens} tokens` : "";
1781
- success(`Valt session ended: ${session.sessionId.slice(0, 8)} (${Math.round(durationMs / 1e3)}s${costStr}${tokenStr})`);
2167
+ const logCostStr = metrics?.totalCostUsd ? ` $${metrics.totalCostUsd.toFixed(4)}` : "";
2168
+ const logTokenStr = metrics?.promptTokens ? ` ${metrics.promptTokens + metrics.completionTokens} tokens` : "";
2169
+ success(`Valt session ended: ${session.sessionId.slice(0, 8)} (${Math.round(durationMs / 1e3)}s${logCostStr}${logTokenStr})`);
1782
2170
  } catch {
1783
2171
  }
1784
2172
  });
1785
- var hookCommand = new Command13("hook").description("Claude Code hook handlers for Valt session tracking").addCommand(sessionStartCommand).addCommand(toolCallCommand).addCommand(postToolUseCommand).addCommand(toolErrorCommand).addCommand(subagentStartCommand).addCommand(subagentStopCommand).addCommand(promptSubmitCommand).addCommand(sessionEndCommand);
2173
+ var stopCommand = new Command13("stop").description("Hook: called when the agent stops responding (Stop)").action(async () => {
2174
+ try {
2175
+ const hookData = await readStdin();
2176
+ const claudeSessionId = hookData["session_id"];
2177
+ const session = readSessionFile(claudeSessionId);
2178
+ if (!session) return;
2179
+ const lastMsg = truncateContent(hookData["last_assistant_message"] ?? "", 120);
2180
+ await sendEvents(session.endpoint, session.apiKey, [{
2181
+ event_id: crypto.randomUUID(),
2182
+ session_id: session.sessionId,
2183
+ event_type: "session.stop",
2184
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2185
+ content: `Agent stopping \u2014 "${lastMsg}"`,
2186
+ tool: "claude-code",
2187
+ metadata: { ...hookData }
2188
+ }]);
2189
+ } catch {
2190
+ }
2191
+ });
2192
+ var stopFailureCommand = new Command13("stop-failure").description("Hook: called when a stop attempt fails (StopFailure)").action(async () => {
2193
+ try {
2194
+ const hookData = await readStdin();
2195
+ const claudeSessionId = hookData["session_id"];
2196
+ const session = readSessionFile(claudeSessionId);
2197
+ if (!session) return;
2198
+ const err = hookData["error"] ?? "unknown";
2199
+ const details = hookData["error_details"] ?? "";
2200
+ await sendEvents(session.endpoint, session.apiKey, [{
2201
+ event_id: crypto.randomUUID(),
2202
+ session_id: session.sessionId,
2203
+ event_type: "session.stop_failure",
2204
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2205
+ content: `Stop failed: ${err} \u2014 ${truncateContent(details, 150)}`,
2206
+ tool: "claude-code",
2207
+ metadata: { ...hookData }
2208
+ }]);
2209
+ } catch {
2210
+ }
2211
+ });
2212
+ var compactCommand = new Command13("compact").description("Hook: called on context compaction (PreCompact/PostCompact)").action(async () => {
2213
+ try {
2214
+ const hookData = await readStdin();
2215
+ const claudeSessionId = hookData["session_id"];
2216
+ const session = readSessionFile(claudeSessionId);
2217
+ if (!session) return;
2218
+ const hookEventName = hookData["hook_event_name"] ?? "";
2219
+ const isPost = hookEventName.toLowerCase().includes("post");
2220
+ const trigger = hookData["trigger"] ?? "auto";
2221
+ const preTokens = hookData["pre_tokens"];
2222
+ const summary = hookData["summary"] ?? "";
2223
+ const content = isPost ? `Context compacted (${trigger}) \u2014 ${truncateContent(summary, 150)}` : `Context compacting (${trigger}${preTokens ? `, ${preTokens} tokens` : ""})`;
2224
+ await sendEvents(session.endpoint, session.apiKey, [{
2225
+ event_id: crypto.randomUUID(),
2226
+ session_id: session.sessionId,
2227
+ event_type: "context.compact",
2228
+ event_subtype: isPost ? "post" : "pre",
2229
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2230
+ content,
2231
+ tool: "claude-code",
2232
+ metadata: { trigger, pre_tokens: preTokens, ...hookData }
2233
+ }]);
2234
+ } catch {
2235
+ }
2236
+ });
2237
+ var notificationCommand = new Command13("notification").description("Hook: called on agent notifications (Notification)").action(async () => {
2238
+ try {
2239
+ const hookData = await readStdin();
2240
+ const claudeSessionId = hookData["session_id"];
2241
+ const session = readSessionFile(claudeSessionId);
2242
+ if (!session) return;
2243
+ const nType = hookData["type"] ?? "info";
2244
+ const title = hookData["title"] ?? hookData["message"] ?? "";
2245
+ await sendEvents(session.endpoint, session.apiKey, [{
2246
+ event_id: crypto.randomUUID(),
2247
+ session_id: session.sessionId,
2248
+ event_type: "notification",
2249
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2250
+ content: `Notification [${nType}]: ${truncateContent(title, 200)}`,
2251
+ tool: "claude-code",
2252
+ metadata: { ...hookData }
2253
+ }]);
2254
+ } catch {
2255
+ }
2256
+ });
2257
+ var permissionRequestCommand = new Command13("permission-request").description("Hook: called when a permission dialog appears (PermissionRequest)").action(async () => {
2258
+ try {
2259
+ const hookData = await readStdin();
2260
+ const claudeSessionId = hookData["session_id"];
2261
+ const session = readSessionFile(claudeSessionId);
2262
+ if (!session) return;
2263
+ const toolName = hookData["tool_name"] ?? "unknown";
2264
+ const toolInput = hookData["tool_input"] ?? {};
2265
+ const inputSummary = summarizeToolInput(toolName, toolInput);
2266
+ await sendEvents(session.endpoint, session.apiKey, [{
2267
+ event_id: crypto.randomUUID(),
2268
+ session_id: session.sessionId,
2269
+ event_type: "permission.request",
2270
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2271
+ content: `Permission requested: ${toolName} \u2014 ${inputSummary}`,
2272
+ tool: "claude-code",
2273
+ tool_name: toolName,
2274
+ metadata: { ...hookData, tool_input_summary: JSON.stringify(toolInput).slice(0, 500) }
2275
+ }]);
2276
+ } catch {
2277
+ }
2278
+ });
2279
+ var configLoadedCommand = new Command13("config-loaded").description("Hook: called when instructions are loaded (InstructionsLoaded)").action(async () => {
2280
+ try {
2281
+ const hookData = await readStdin();
2282
+ const claudeSessionId = hookData["session_id"];
2283
+ const session = readSessionFile(claudeSessionId);
2284
+ if (!session) return;
2285
+ const filePath = hookData["file_path"] ?? "";
2286
+ const memoryType = hookData["memory_type"] ?? "";
2287
+ const loadReason = hookData["load_reason"] ?? "";
2288
+ await sendEvents(session.endpoint, session.apiKey, [{
2289
+ event_id: crypto.randomUUID(),
2290
+ session_id: session.sessionId,
2291
+ event_type: "config.loaded",
2292
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2293
+ content: `Instructions loaded: ${fileBasename(filePath)} (${memoryType}, ${loadReason})`,
2294
+ tool: "claude-code",
2295
+ metadata: { ...hookData }
2296
+ }]);
2297
+ } catch {
2298
+ }
2299
+ });
2300
+ var configChangeCommand = new Command13("config-change").description("Hook: called when config changes (ConfigChange)").action(async () => {
2301
+ try {
2302
+ const hookData = await readStdin();
2303
+ const claudeSessionId = hookData["session_id"];
2304
+ const session = readSessionFile(claudeSessionId);
2305
+ if (!session) return;
2306
+ const source = hookData["source"] ?? "unknown";
2307
+ const filePath = hookData["file_path"];
2308
+ const label = filePath ? `${source} (${fileBasename(filePath)})` : source;
2309
+ await sendEvents(session.endpoint, session.apiKey, [{
2310
+ event_id: crypto.randomUUID(),
2311
+ session_id: session.sessionId,
2312
+ event_type: "config.change",
2313
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2314
+ content: `Config changed: ${label}`,
2315
+ tool: "claude-code",
2316
+ metadata: { ...hookData }
2317
+ }]);
2318
+ } catch {
2319
+ }
2320
+ });
2321
+ var elicitationCommand = new Command13("elicitation").description("Hook: called on MCP elicitation (Elicitation)").action(async () => {
2322
+ try {
2323
+ const hookData = await readStdin();
2324
+ const claudeSessionId = hookData["session_id"];
2325
+ const session = readSessionFile(claudeSessionId);
2326
+ if (!session) return;
2327
+ const mode = hookData["mode"] ?? "unknown";
2328
+ const serverName = hookData["server_name"] ?? "unknown";
2329
+ const message = hookData["message"] ?? "";
2330
+ await sendEvents(session.endpoint, session.apiKey, [{
2331
+ event_id: crypto.randomUUID(),
2332
+ session_id: session.sessionId,
2333
+ event_type: "mcp.elicitation",
2334
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2335
+ content: `MCP elicitation [${mode}]: ${serverName} \u2014 ${truncateContent(message, 150)}`,
2336
+ tool: "claude-code",
2337
+ metadata: { ...hookData }
2338
+ }]);
2339
+ } catch {
2340
+ }
2341
+ });
2342
+ var elicitationResultCommand = new Command13("elicitation-result").description("Hook: called on MCP elicitation result (ElicitationResult)").action(async () => {
2343
+ try {
2344
+ const hookData = await readStdin();
2345
+ const claudeSessionId = hookData["session_id"];
2346
+ const session = readSessionFile(claudeSessionId);
2347
+ if (!session) return;
2348
+ const serverName = hookData["server_name"] ?? "unknown";
2349
+ const action = hookData["action"] ?? "unknown";
2350
+ await sendEvents(session.endpoint, session.apiKey, [{
2351
+ event_id: crypto.randomUUID(),
2352
+ session_id: session.sessionId,
2353
+ event_type: "mcp.elicitation_result",
2354
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2355
+ content: `MCP elicitation result: ${serverName} \u2014 ${action}`,
2356
+ tool: "claude-code",
2357
+ metadata: { ...hookData }
2358
+ }]);
2359
+ } catch {
2360
+ }
2361
+ });
2362
+ var worktreeCreateCommand = new Command13("worktree-create").description("Hook: called when a worktree is created (WorktreeCreate)").action(async () => {
2363
+ try {
2364
+ const hookData = await readStdin();
2365
+ const claudeSessionId = hookData["session_id"];
2366
+ const session = readSessionFile(claudeSessionId);
2367
+ if (!session) return;
2368
+ const name = hookData["name"] ?? "unknown";
2369
+ await sendEvents(session.endpoint, session.apiKey, [{
2370
+ event_id: crypto.randomUUID(),
2371
+ session_id: session.sessionId,
2372
+ event_type: "worktree.create",
2373
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2374
+ content: `Worktree creating: ${name}`,
2375
+ tool: "claude-code",
2376
+ metadata: { ...hookData }
2377
+ }]);
2378
+ } catch {
2379
+ }
2380
+ });
2381
+ var worktreeRemoveCommand = new Command13("worktree-remove").description("Hook: called when a worktree is removed (WorktreeRemove)").action(async () => {
2382
+ try {
2383
+ const hookData = await readStdin();
2384
+ const claudeSessionId = hookData["session_id"];
2385
+ const session = readSessionFile(claudeSessionId);
2386
+ if (!session) return;
2387
+ const worktreePath = hookData["worktree_path"] ?? "";
2388
+ await sendEvents(session.endpoint, session.apiKey, [{
2389
+ event_id: crypto.randomUUID(),
2390
+ session_id: session.sessionId,
2391
+ event_type: "worktree.remove",
2392
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2393
+ content: `Worktree removing: ${fileBasename(worktreePath)}`,
2394
+ tool: "claude-code",
2395
+ metadata: { ...hookData }
2396
+ }]);
2397
+ } catch {
2398
+ }
2399
+ });
2400
+ var teammateIdleCommand = new Command13("teammate-idle").description("Hook: called when a teammate is idle (TeammateIdle)").action(async () => {
2401
+ try {
2402
+ const hookData = await readStdin();
2403
+ const claudeSessionId = hookData["session_id"];
2404
+ const session = readSessionFile(claudeSessionId);
2405
+ if (!session) return;
2406
+ const teammateName = hookData["teammate_name"] ?? "unknown";
2407
+ const teamName = hookData["team_name"] ?? "unknown";
2408
+ await sendEvents(session.endpoint, session.apiKey, [{
2409
+ event_id: crypto.randomUUID(),
2410
+ session_id: session.sessionId,
2411
+ event_type: "agent.team",
2412
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2413
+ content: `Teammate idle: ${teammateName} in ${teamName}`,
2414
+ tool: "claude-code",
2415
+ metadata: { ...hookData }
2416
+ }]);
2417
+ } catch {
2418
+ }
2419
+ });
2420
+ var taskCompletedCommand = new Command13("task-completed").description("Hook: called when a task completes (TaskCompleted)").action(async () => {
2421
+ try {
2422
+ const hookData = await readStdin();
2423
+ const claudeSessionId = hookData["session_id"];
2424
+ const session = readSessionFile(claudeSessionId);
2425
+ if (!session) return;
2426
+ const taskId = hookData["task_id"] ?? "?";
2427
+ const taskSubject = hookData["task_subject"] ?? "";
2428
+ await sendEvents(session.endpoint, session.apiKey, [{
2429
+ event_id: crypto.randomUUID(),
2430
+ session_id: session.sessionId,
2431
+ event_type: "task.completed",
2432
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2433
+ content: `Task completed: #${taskId} \u2014 ${truncateContent(taskSubject, 150)}`,
2434
+ tool: "claude-code",
2435
+ metadata: { ...hookData }
2436
+ }]);
2437
+ } catch {
2438
+ }
2439
+ });
2440
+ var setupHookCommand = new Command13("setup-hook").description("Hook: called during setup (Setup)").action(async () => {
2441
+ try {
2442
+ const hookData = await readStdin();
2443
+ const claudeSessionId = hookData["session_id"];
2444
+ const session = readSessionFile(claudeSessionId);
2445
+ if (!session) return;
2446
+ const source = hookData["source"] ?? "unknown";
2447
+ await sendEvents(session.endpoint, session.apiKey, [{
2448
+ event_id: crypto.randomUUID(),
2449
+ session_id: session.sessionId,
2450
+ event_type: "setup",
2451
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2452
+ content: `Setup: ${source}`,
2453
+ tool: "claude-code",
2454
+ metadata: { ...hookData }
2455
+ }]);
2456
+ } catch {
2457
+ }
2458
+ });
2459
+ var hookCommand = new Command13("hook").description("Claude Code hook handlers for Valt session tracking").addCommand(sessionStartCommand).addCommand(toolCallCommand).addCommand(postToolUseCommand).addCommand(toolErrorCommand).addCommand(subagentStartCommand).addCommand(subagentStopCommand).addCommand(promptSubmitCommand).addCommand(sessionEndCommand).addCommand(stopCommand).addCommand(stopFailureCommand).addCommand(compactCommand).addCommand(notificationCommand).addCommand(permissionRequestCommand).addCommand(configLoadedCommand).addCommand(configChangeCommand).addCommand(elicitationCommand).addCommand(elicitationResultCommand).addCommand(worktreeCreateCommand).addCommand(worktreeRemoveCommand).addCommand(teammateIdleCommand).addCommand(taskCompletedCommand).addCommand(setupHookCommand);
1786
2460
 
1787
2461
  // src/commands/setup.ts
1788
2462
  import { Command as Command14 } from "commander";
1789
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync4, unlinkSync as unlinkSync3 } from "fs";
1790
- import { join as join2 } from "path";
2463
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync5, unlinkSync as unlinkSync3 } from "fs";
2464
+ import { join as join2, dirname } from "path";
2465
+ import { fileURLToPath } from "url";
1791
2466
  import os3 from "os";
2467
+ var __dirname = dirname(fileURLToPath(import.meta.url));
2468
+ var pkgVersion = JSON.parse(readFileSync4(join2(__dirname, "../../package.json"), "utf-8")).version;
1792
2469
  var CLAUDE_DIR = join2(os3.homedir(), ".claude");
1793
2470
  var SETTINGS_FILE = join2(CLAUDE_DIR, "settings.json");
1794
2471
  var LEGACY_HOOKS_FILE = join2(CLAUDE_DIR, "hooks.json");
1795
- var HOOK_PREFIX = "npx --yes @usevalt/cli";
2472
+ var HOOK_PREFIX = `npx --yes @usevalt/cli@${pkgVersion}`;
1796
2473
  function getValtHooks() {
1797
2474
  return {
2475
+ Setup: [
2476
+ {
2477
+ matcher: "*",
2478
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook setup-hook` }]
2479
+ }
2480
+ ],
1798
2481
  SessionStart: [
1799
2482
  {
1800
- hooks: [
1801
- {
1802
- type: "command",
1803
- command: `${HOOK_PREFIX} hook session-start`
1804
- }
1805
- ]
2483
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook session-start` }]
2484
+ }
2485
+ ],
2486
+ InstructionsLoaded: [
2487
+ {
2488
+ matcher: "*",
2489
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook config-loaded` }]
2490
+ }
2491
+ ],
2492
+ UserPromptSubmit: [
2493
+ {
2494
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook prompt-submit` }]
1806
2495
  }
1807
2496
  ],
1808
2497
  PreToolUse: [
1809
2498
  {
1810
2499
  matcher: "*",
1811
- hooks: [
1812
- {
1813
- type: "command",
1814
- command: `${HOOK_PREFIX} hook tool-call`
1815
- }
1816
- ]
2500
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook tool-call` }]
2501
+ }
2502
+ ],
2503
+ PermissionRequest: [
2504
+ {
2505
+ matcher: "*",
2506
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook permission-request` }]
1817
2507
  }
1818
2508
  ],
1819
2509
  PostToolUse: [
1820
2510
  {
1821
2511
  matcher: "*",
1822
- hooks: [
1823
- {
1824
- type: "command",
1825
- command: `${HOOK_PREFIX} hook post-tool-use`
1826
- }
1827
- ]
2512
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook post-tool-use` }]
1828
2513
  }
1829
2514
  ],
1830
2515
  PostToolUseFailure: [
1831
2516
  {
1832
2517
  matcher: "*",
1833
- hooks: [
1834
- {
1835
- type: "command",
1836
- command: `${HOOK_PREFIX} hook tool-error`
1837
- }
1838
- ]
2518
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook tool-error` }]
2519
+ }
2520
+ ],
2521
+ Notification: [
2522
+ {
2523
+ matcher: "*",
2524
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook notification` }]
1839
2525
  }
1840
2526
  ],
1841
2527
  SubagentStart: [
1842
2528
  {
1843
- hooks: [
1844
- {
1845
- type: "command",
1846
- command: `${HOOK_PREFIX} hook subagent-start`
1847
- }
1848
- ]
2529
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook subagent-start` }]
1849
2530
  }
1850
2531
  ],
1851
2532
  SubagentStop: [
1852
2533
  {
1853
- hooks: [
1854
- {
1855
- type: "command",
1856
- command: `${HOOK_PREFIX} hook subagent-stop`
1857
- }
1858
- ]
2534
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook subagent-stop` }]
1859
2535
  }
1860
2536
  ],
1861
- UserPromptSubmit: [
2537
+ Stop: [
1862
2538
  {
1863
- hooks: [
1864
- {
1865
- type: "command",
1866
- command: `${HOOK_PREFIX} hook prompt-submit`
1867
- }
1868
- ]
2539
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook stop` }]
2540
+ }
2541
+ ],
2542
+ StopFailure: [
2543
+ {
2544
+ matcher: "*",
2545
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook stop-failure` }]
2546
+ }
2547
+ ],
2548
+ TeammateIdle: [
2549
+ {
2550
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook teammate-idle` }]
2551
+ }
2552
+ ],
2553
+ TaskCompleted: [
2554
+ {
2555
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook task-completed` }]
2556
+ }
2557
+ ],
2558
+ ConfigChange: [
2559
+ {
2560
+ matcher: "*",
2561
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook config-change` }]
2562
+ }
2563
+ ],
2564
+ WorktreeCreate: [
2565
+ {
2566
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook worktree-create` }]
2567
+ }
2568
+ ],
2569
+ WorktreeRemove: [
2570
+ {
2571
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook worktree-remove` }]
2572
+ }
2573
+ ],
2574
+ PreCompact: [
2575
+ {
2576
+ matcher: "*",
2577
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook compact` }]
2578
+ }
2579
+ ],
2580
+ PostCompact: [
2581
+ {
2582
+ matcher: "*",
2583
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook compact` }]
2584
+ }
2585
+ ],
2586
+ Elicitation: [
2587
+ {
2588
+ matcher: "*",
2589
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook elicitation` }]
2590
+ }
2591
+ ],
2592
+ ElicitationResult: [
2593
+ {
2594
+ matcher: "*",
2595
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook elicitation-result` }]
1869
2596
  }
1870
2597
  ],
1871
2598
  SessionEnd: [
1872
2599
  {
1873
2600
  matcher: "*",
1874
- hooks: [
1875
- {
1876
- type: "command",
1877
- command: `${HOOK_PREFIX} hook session-end`,
1878
- timeout: 1e4
1879
- }
1880
- ]
2601
+ hooks: [{ type: "command", command: `${HOOK_PREFIX} hook session-end`, timeout: 15e3 }]
1881
2602
  }
1882
2603
  ]
1883
2604
  };
@@ -1889,7 +2610,7 @@ var setupCommand = new Command14("setup").description("Configure Claude Code hoo
1889
2610
  error("No API key found. Run `valt login` first.");
1890
2611
  process.exit(1);
1891
2612
  }
1892
- if (!existsSync4(CLAUDE_DIR)) {
2613
+ if (!existsSync5(CLAUDE_DIR)) {
1893
2614
  mkdirSync2(CLAUDE_DIR, { recursive: true });
1894
2615
  }
1895
2616
  if (opts.remove) {
@@ -1897,7 +2618,7 @@ var setupCommand = new Command14("setup").description("Configure Claude Code hoo
1897
2618
  return;
1898
2619
  }
1899
2620
  let config = {};
1900
- if (existsSync4(SETTINGS_FILE)) {
2621
+ if (existsSync5(SETTINGS_FILE)) {
1901
2622
  try {
1902
2623
  const raw = readFileSync4(SETTINGS_FILE, "utf-8");
1903
2624
  config = JSON.parse(raw);
@@ -1923,7 +2644,7 @@ var setupCommand = new Command14("setup").description("Configure Claude Code hoo
1923
2644
  }
1924
2645
  }
1925
2646
  writeFileSync4(SETTINGS_FILE, JSON.stringify(config, null, 2) + "\n", "utf-8");
1926
- if (existsSync4(LEGACY_HOOKS_FILE)) {
2647
+ if (existsSync5(LEGACY_HOOKS_FILE)) {
1927
2648
  try {
1928
2649
  removeLegacyHooks();
1929
2650
  info(`Migrated hooks from legacy hooks.json to settings.json.`);
@@ -1960,7 +2681,7 @@ function removeValtHooksFromConfig(config) {
1960
2681
  }
1961
2682
  }
1962
2683
  function removeLegacyHooks() {
1963
- if (!existsSync4(LEGACY_HOOKS_FILE)) return;
2684
+ if (!existsSync5(LEGACY_HOOKS_FILE)) return;
1964
2685
  try {
1965
2686
  const raw = readFileSync4(LEGACY_HOOKS_FILE, "utf-8");
1966
2687
  const config = JSON.parse(raw);
@@ -1979,7 +2700,7 @@ function removeLegacyHooks() {
1979
2700
  }
1980
2701
  }
1981
2702
  function removeHooks() {
1982
- if (existsSync4(SETTINGS_FILE)) {
2703
+ if (existsSync5(SETTINGS_FILE)) {
1983
2704
  try {
1984
2705
  const raw = readFileSync4(SETTINGS_FILE, "utf-8");
1985
2706
  const config = JSON.parse(raw);
@@ -1991,14 +2712,14 @@ function removeHooks() {
1991
2712
  process.exit(1);
1992
2713
  }
1993
2714
  }
1994
- if (existsSync4(LEGACY_HOOKS_FILE)) {
2715
+ if (existsSync5(LEGACY_HOOKS_FILE)) {
1995
2716
  try {
1996
2717
  removeLegacyHooks();
1997
2718
  info("Legacy hooks.json cleaned up.");
1998
2719
  } catch {
1999
2720
  }
2000
2721
  }
2001
- if (!existsSync4(SETTINGS_FILE) && !existsSync4(LEGACY_HOOKS_FILE)) {
2722
+ if (!existsSync5(SETTINGS_FILE) && !existsSync5(LEGACY_HOOKS_FILE)) {
2002
2723
  info("No hooks configured. Nothing to remove.");
2003
2724
  }
2004
2725
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usevalt/cli",
3
- "version": "0.7.4",
3
+ "version": "0.9.0",
4
4
  "description": "Valt CLI — trust layer for AI-assisted development",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -39,8 +39,8 @@
39
39
  "tsup": "^8.4.0",
40
40
  "typescript": "^5.7.0",
41
41
  "vitest": "^3.2.0",
42
- "@usevalt/typescript-config": "0.0.0",
43
- "@usevalt/eslint-config": "0.0.0"
42
+ "@usevalt/eslint-config": "0.0.0",
43
+ "@usevalt/typescript-config": "0.0.0"
44
44
  },
45
45
  "scripts": {
46
46
  "build": "tsup",