@usevalt/cli 0.7.3 → 0.8.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.
- package/dist/index.js +886 -76
- 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
|
|
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 (
|
|
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 (!
|
|
1602
|
+
if (!existsSync4(transcriptPath)) return result;
|
|
1276
1603
|
try {
|
|
1277
|
-
const rl =
|
|
1278
|
-
input:
|
|
1604
|
+
const rl = createInterface2({
|
|
1605
|
+
input: createReadStream2(transcriptPath, "utf-8"),
|
|
1279
1606
|
crlfDelay: Infinity
|
|
1280
1607
|
});
|
|
1281
1608
|
for await (const line of rl) {
|
|
@@ -1311,6 +1638,51 @@ async function parseTranscript(transcriptPath) {
|
|
|
1311
1638
|
}
|
|
1312
1639
|
return result;
|
|
1313
1640
|
}
|
|
1641
|
+
function parseTranscriptIncremental(transcriptPath, fromByteOffset, model) {
|
|
1642
|
+
const result = {
|
|
1643
|
+
deltaPromptTokens: 0,
|
|
1644
|
+
deltaCompletionTokens: 0,
|
|
1645
|
+
deltaCacheReadTokens: 0,
|
|
1646
|
+
deltaCostUsd: 0,
|
|
1647
|
+
newByteOffset: fromByteOffset
|
|
1648
|
+
};
|
|
1649
|
+
try {
|
|
1650
|
+
const fileSize = statSync(transcriptPath).size;
|
|
1651
|
+
if (fileSize <= fromByteOffset) return result;
|
|
1652
|
+
const bytesToRead = fileSize - fromByteOffset;
|
|
1653
|
+
const buffer = Buffer.alloc(bytesToRead);
|
|
1654
|
+
const fd = openSync(transcriptPath, "r");
|
|
1655
|
+
try {
|
|
1656
|
+
readSync(fd, buffer, 0, bytesToRead, fromByteOffset);
|
|
1657
|
+
} finally {
|
|
1658
|
+
closeSync(fd);
|
|
1659
|
+
}
|
|
1660
|
+
const chunk = buffer.toString("utf-8");
|
|
1661
|
+
const lines = chunk.split("\n");
|
|
1662
|
+
for (const line of lines) {
|
|
1663
|
+
if (!line.trim()) continue;
|
|
1664
|
+
try {
|
|
1665
|
+
const entry = JSON.parse(line);
|
|
1666
|
+
const usage = entry.message?.usage ?? entry.usage;
|
|
1667
|
+
if (usage) {
|
|
1668
|
+
result.deltaPromptTokens += usage.input_tokens ?? 0;
|
|
1669
|
+
result.deltaCompletionTokens += usage.output_tokens ?? 0;
|
|
1670
|
+
result.deltaCacheReadTokens += usage.cache_read_input_tokens ?? 0;
|
|
1671
|
+
}
|
|
1672
|
+
} catch {
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
result.newByteOffset = fileSize;
|
|
1676
|
+
result.deltaCostUsd = calculateCost(
|
|
1677
|
+
model,
|
|
1678
|
+
result.deltaPromptTokens,
|
|
1679
|
+
result.deltaCompletionTokens,
|
|
1680
|
+
result.deltaCacheReadTokens
|
|
1681
|
+
);
|
|
1682
|
+
} catch {
|
|
1683
|
+
}
|
|
1684
|
+
return result;
|
|
1685
|
+
}
|
|
1314
1686
|
var sessionStartCommand = new Command13("session-start").description("Hook: called when a Claude Code session starts").action(async () => {
|
|
1315
1687
|
try {
|
|
1316
1688
|
const { apiKey, endpoint, apiEndpoint } = resolveConfig();
|
|
@@ -1322,6 +1694,7 @@ var sessionStartCommand = new Command13("session-start").description("Hook: call
|
|
|
1322
1694
|
const model = hookData["model"] || process.env["CLAUDE_MODEL"] || "unknown";
|
|
1323
1695
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1324
1696
|
const cwd = hookData["cwd"] || process.cwd();
|
|
1697
|
+
const transcriptPath = hookData["transcript_path"] || void 0;
|
|
1325
1698
|
const state = {
|
|
1326
1699
|
sessionId,
|
|
1327
1700
|
claudeSessionId,
|
|
@@ -1331,7 +1704,8 @@ var sessionStartCommand = new Command13("session-start").description("Hook: call
|
|
|
1331
1704
|
apiEndpoint,
|
|
1332
1705
|
projectSlug,
|
|
1333
1706
|
model,
|
|
1334
|
-
cwd
|
|
1707
|
+
cwd,
|
|
1708
|
+
transcriptPath
|
|
1335
1709
|
};
|
|
1336
1710
|
writeFileSync3(getSessionFile(claudeSessionId), JSON.stringify(state, null, 2), { mode: 384 });
|
|
1337
1711
|
let gitCommitBefore;
|
|
@@ -1350,6 +1724,7 @@ var sessionStartCommand = new Command13("session-start").description("Hook: call
|
|
|
1350
1724
|
session_id: sessionId,
|
|
1351
1725
|
event_type: "session.start",
|
|
1352
1726
|
timestamp: startedAt,
|
|
1727
|
+
content: `Session started \u2014 ${model} on ${projectSlug}`,
|
|
1353
1728
|
tool: "claude-code",
|
|
1354
1729
|
model,
|
|
1355
1730
|
metadata: {
|
|
@@ -1421,6 +1796,8 @@ var toolCallCommand = new Command13("tool-call").description("Hook: called on ea
|
|
|
1421
1796
|
}
|
|
1422
1797
|
}
|
|
1423
1798
|
const { eventType, filePath, command } = classifyToolCall(toolName, toolInput);
|
|
1799
|
+
const contentSummary = summarizeToolInput(toolName, toolInput);
|
|
1800
|
+
const toolInputSummaryMeta = JSON.stringify(toolInput).slice(0, 1024);
|
|
1424
1801
|
const events = [];
|
|
1425
1802
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1426
1803
|
events.push({
|
|
@@ -1428,20 +1805,23 @@ var toolCallCommand = new Command13("tool-call").description("Hook: called on ea
|
|
|
1428
1805
|
session_id: session.sessionId,
|
|
1429
1806
|
event_type: "tool.call",
|
|
1430
1807
|
timestamp,
|
|
1808
|
+
content: contentSummary,
|
|
1431
1809
|
tool: "claude-code",
|
|
1432
1810
|
tool_name: toolName,
|
|
1433
1811
|
metadata: {
|
|
1434
1812
|
tool_name: toolName,
|
|
1435
1813
|
file_path: filePath,
|
|
1436
|
-
command
|
|
1814
|
+
command,
|
|
1815
|
+
tool_input_summary: toolInputSummaryMeta
|
|
1437
1816
|
}
|
|
1438
1817
|
});
|
|
1439
1818
|
if (eventType !== "tool.call") {
|
|
1440
|
-
|
|
1819
|
+
const classifiedEvent = {
|
|
1441
1820
|
event_id: crypto.randomUUID(),
|
|
1442
1821
|
session_id: session.sessionId,
|
|
1443
1822
|
event_type: eventType,
|
|
1444
1823
|
timestamp,
|
|
1824
|
+
content: contentSummary,
|
|
1445
1825
|
tool: "claude-code",
|
|
1446
1826
|
tool_name: toolName,
|
|
1447
1827
|
file_path: filePath,
|
|
@@ -1450,7 +1830,11 @@ var toolCallCommand = new Command13("tool-call").description("Hook: called on ea
|
|
|
1450
1830
|
...filePath ? { file_path: filePath } : {},
|
|
1451
1831
|
...command ? { command } : {}
|
|
1452
1832
|
}
|
|
1453
|
-
}
|
|
1833
|
+
};
|
|
1834
|
+
if (eventType === "command.execute" && command) {
|
|
1835
|
+
classifiedEvent["command_category"] = classifyCommand(command);
|
|
1836
|
+
}
|
|
1837
|
+
events.push(classifiedEvent);
|
|
1454
1838
|
}
|
|
1455
1839
|
await sendEvents(session.endpoint, session.apiKey, events);
|
|
1456
1840
|
} catch {
|
|
@@ -1463,19 +1847,24 @@ var postToolUseCommand = new Command13("post-tool-use").description("Hook: calle
|
|
|
1463
1847
|
const session = readSessionFile(claudeSessionId);
|
|
1464
1848
|
if (!session) return;
|
|
1465
1849
|
const toolName = hookData["tool_name"] ?? "unknown";
|
|
1850
|
+
const toolInput = hookData["tool_input"] ?? {};
|
|
1466
1851
|
const toolResponse = hookData["tool_response"];
|
|
1467
1852
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1853
|
+
const resultContent = summarizeToolResponse(toolName, toolInput, toolResponse ?? {});
|
|
1854
|
+
const toolResponseSummaryMeta = JSON.stringify(toolResponse ?? {}).slice(0, 1024);
|
|
1468
1855
|
const events = [{
|
|
1469
1856
|
event_id: crypto.randomUUID(),
|
|
1470
1857
|
session_id: session.sessionId,
|
|
1471
1858
|
event_type: "tool.result",
|
|
1472
1859
|
timestamp,
|
|
1860
|
+
content: resultContent,
|
|
1473
1861
|
tool: "claude-code",
|
|
1474
1862
|
tool_name: toolName,
|
|
1475
1863
|
metadata: {
|
|
1476
1864
|
tool_name: toolName,
|
|
1477
1865
|
exit_code: toolResponse?.["exit_code"],
|
|
1478
|
-
success: toolResponse?.["success"] ?? true
|
|
1866
|
+
success: toolResponse?.["success"] ?? true,
|
|
1867
|
+
tool_response_summary: toolResponseSummaryMeta
|
|
1479
1868
|
}
|
|
1480
1869
|
}];
|
|
1481
1870
|
if (toolName === "Bash" && toolResponse) {
|
|
@@ -1483,11 +1872,13 @@ var postToolUseCommand = new Command13("post-tool-use").description("Hook: calle
|
|
|
1483
1872
|
if (stdout) {
|
|
1484
1873
|
const testResults = parseTestResults(stdout);
|
|
1485
1874
|
if (testResults) {
|
|
1875
|
+
const testContent = `Tests: ${testResults.testsPassed}/${testResults.testsRun} passed (${testResults.framework}) ${testResults.testsFailed === 0 ? "PASS" : "FAIL"}`;
|
|
1486
1876
|
events.push({
|
|
1487
1877
|
event_id: crypto.randomUUID(),
|
|
1488
1878
|
session_id: session.sessionId,
|
|
1489
1879
|
event_type: "test.run",
|
|
1490
1880
|
timestamp,
|
|
1881
|
+
content: testContent,
|
|
1491
1882
|
tool: "claude-code",
|
|
1492
1883
|
metadata: {
|
|
1493
1884
|
tests_run: testResults.testsRun,
|
|
@@ -1500,6 +1891,45 @@ var postToolUseCommand = new Command13("post-tool-use").description("Hook: calle
|
|
|
1500
1891
|
}
|
|
1501
1892
|
}
|
|
1502
1893
|
}
|
|
1894
|
+
try {
|
|
1895
|
+
const tPath = hookData["transcript_path"] ?? session.transcriptPath;
|
|
1896
|
+
if (tPath && existsSync4(tPath)) {
|
|
1897
|
+
if (!session.transcriptPath) session.transcriptPath = tPath;
|
|
1898
|
+
const deltas = parseTranscriptIncremental(
|
|
1899
|
+
tPath,
|
|
1900
|
+
session.transcriptByteOffset ?? 0,
|
|
1901
|
+
session.model ?? "unknown"
|
|
1902
|
+
);
|
|
1903
|
+
if (deltas.deltaPromptTokens > 0 || deltas.deltaCompletionTokens > 0) {
|
|
1904
|
+
events.push({
|
|
1905
|
+
event_id: crypto.randomUUID(),
|
|
1906
|
+
session_id: session.sessionId,
|
|
1907
|
+
event_type: "cost",
|
|
1908
|
+
timestamp,
|
|
1909
|
+
tool: "claude-code",
|
|
1910
|
+
tokens_prompt: deltas.deltaPromptTokens,
|
|
1911
|
+
tokens_completion: deltas.deltaCompletionTokens,
|
|
1912
|
+
cost_usd: deltas.deltaCostUsd,
|
|
1913
|
+
metadata: {
|
|
1914
|
+
model: session.model,
|
|
1915
|
+
source: "incremental",
|
|
1916
|
+
cache_read_tokens: deltas.deltaCacheReadTokens
|
|
1917
|
+
}
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
1920
|
+
session.transcriptByteOffset = deltas.newByteOffset;
|
|
1921
|
+
session.runningPromptTokens = (session.runningPromptTokens ?? 0) + deltas.deltaPromptTokens;
|
|
1922
|
+
session.runningCompletionTokens = (session.runningCompletionTokens ?? 0) + deltas.deltaCompletionTokens;
|
|
1923
|
+
session.runningCacheReadTokens = (session.runningCacheReadTokens ?? 0) + deltas.deltaCacheReadTokens;
|
|
1924
|
+
session.runningCostUsd = (session.runningCostUsd ?? 0) + deltas.deltaCostUsd;
|
|
1925
|
+
writeFileSync3(
|
|
1926
|
+
getSessionFile(session.claudeSessionId),
|
|
1927
|
+
JSON.stringify(session, null, 2),
|
|
1928
|
+
{ mode: 384 }
|
|
1929
|
+
);
|
|
1930
|
+
}
|
|
1931
|
+
} catch {
|
|
1932
|
+
}
|
|
1503
1933
|
await sendEvents(session.endpoint, session.apiKey, events);
|
|
1504
1934
|
} catch {
|
|
1505
1935
|
}
|
|
@@ -1518,6 +1948,7 @@ var toolErrorCommand = new Command13("tool-error").description("Hook: called whe
|
|
|
1518
1948
|
session_id: session.sessionId,
|
|
1519
1949
|
event_type: "tool.error",
|
|
1520
1950
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1951
|
+
content: `Error: ${toolName} \u2014 ${truncateContent(errorMsg, 200)}`,
|
|
1521
1952
|
tool: "claude-code",
|
|
1522
1953
|
tool_name: toolName,
|
|
1523
1954
|
metadata: {
|
|
@@ -1535,15 +1966,17 @@ var subagentStartCommand = new Command13("subagent-start").description("Hook: ca
|
|
|
1535
1966
|
const claudeSessionId = hookData["session_id"];
|
|
1536
1967
|
const session = readSessionFile(claudeSessionId);
|
|
1537
1968
|
if (!session) return;
|
|
1969
|
+
const agentType = hookData["agent_type"] ?? "unknown";
|
|
1538
1970
|
await sendEvents(session.endpoint, session.apiKey, [{
|
|
1539
1971
|
event_id: crypto.randomUUID(),
|
|
1540
1972
|
session_id: session.sessionId,
|
|
1541
1973
|
event_type: "subagent.start",
|
|
1542
1974
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1975
|
+
content: `Subagent started: ${agentType}`,
|
|
1543
1976
|
tool: "claude-code",
|
|
1544
1977
|
metadata: {
|
|
1545
1978
|
agent_id: hookData["agent_id"],
|
|
1546
|
-
agent_type:
|
|
1979
|
+
agent_type: agentType
|
|
1547
1980
|
}
|
|
1548
1981
|
}]);
|
|
1549
1982
|
} catch {
|
|
@@ -1555,15 +1988,17 @@ var subagentStopCommand = new Command13("subagent-stop").description("Hook: call
|
|
|
1555
1988
|
const claudeSessionId = hookData["session_id"];
|
|
1556
1989
|
const session = readSessionFile(claudeSessionId);
|
|
1557
1990
|
if (!session) return;
|
|
1991
|
+
const stopAgentType = hookData["agent_type"] ?? "unknown";
|
|
1558
1992
|
await sendEvents(session.endpoint, session.apiKey, [{
|
|
1559
1993
|
event_id: crypto.randomUUID(),
|
|
1560
1994
|
session_id: session.sessionId,
|
|
1561
1995
|
event_type: "subagent.stop",
|
|
1562
1996
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1997
|
+
content: `Subagent stopped: ${stopAgentType}`,
|
|
1563
1998
|
tool: "claude-code",
|
|
1564
1999
|
metadata: {
|
|
1565
2000
|
agent_id: hookData["agent_id"],
|
|
1566
|
-
agent_type:
|
|
2001
|
+
agent_type: stopAgentType,
|
|
1567
2002
|
transcript_path: hookData["agent_transcript_path"]
|
|
1568
2003
|
}
|
|
1569
2004
|
}]);
|
|
@@ -1583,6 +2018,7 @@ var promptSubmitCommand = new Command13("prompt-submit").description("Hook: call
|
|
|
1583
2018
|
session_id: session.sessionId,
|
|
1584
2019
|
event_type: "prompt.submit",
|
|
1585
2020
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2021
|
+
content: `Prompt submitted (${promptText.length} chars)`,
|
|
1586
2022
|
tool: "claude-code",
|
|
1587
2023
|
metadata: {
|
|
1588
2024
|
prompt_hash: promptHashValue,
|
|
@@ -1626,6 +2062,12 @@ var sessionEndCommand = new Command13("session-end").description("Hook: called w
|
|
|
1626
2062
|
const durationMs = now.getTime() - startedAt.getTime();
|
|
1627
2063
|
const transcriptPath = hookData["transcript_path"];
|
|
1628
2064
|
const metrics = transcriptPath ? await parseTranscript(transcriptPath) : null;
|
|
2065
|
+
if (metrics && (session.runningPromptTokens || session.runningCostUsd)) {
|
|
2066
|
+
metrics.promptTokens = Math.max(0, metrics.promptTokens - (session.runningPromptTokens ?? 0));
|
|
2067
|
+
metrics.completionTokens = Math.max(0, metrics.completionTokens - (session.runningCompletionTokens ?? 0));
|
|
2068
|
+
metrics.cacheReadTokens = Math.max(0, metrics.cacheReadTokens - (session.runningCacheReadTokens ?? 0));
|
|
2069
|
+
metrics.totalCostUsd = Math.max(0, metrics.totalCostUsd - (session.runningCostUsd ?? 0));
|
|
2070
|
+
}
|
|
1629
2071
|
let gitCommitAfter;
|
|
1630
2072
|
try {
|
|
1631
2073
|
gitCommitAfter = execSync("git rev-parse HEAD", {
|
|
@@ -1636,12 +2078,15 @@ var sessionEndCommand = new Command13("session-end").description("Hook: called w
|
|
|
1636
2078
|
}).trim();
|
|
1637
2079
|
} catch {
|
|
1638
2080
|
}
|
|
2081
|
+
const durationSec = Math.round(durationMs / 1e3);
|
|
2082
|
+
const costStr = metrics?.totalCostUsd ? `$${metrics.totalCostUsd.toFixed(4)}` : "$0";
|
|
1639
2083
|
const events = [
|
|
1640
2084
|
{
|
|
1641
2085
|
event_id: crypto.randomUUID(),
|
|
1642
2086
|
session_id: session.sessionId,
|
|
1643
2087
|
event_type: "session.end",
|
|
1644
2088
|
timestamp: now.toISOString(),
|
|
2089
|
+
content: `Session ended \u2014 ${durationSec}s, ${costStr}`,
|
|
1645
2090
|
duration_ms: durationMs,
|
|
1646
2091
|
tool: "claude-code",
|
|
1647
2092
|
model: metrics?.model ?? session.model,
|
|
@@ -1660,11 +2105,13 @@ var sessionEndCommand = new Command13("session-end").description("Hook: called w
|
|
|
1660
2105
|
}
|
|
1661
2106
|
];
|
|
1662
2107
|
if (metrics && (metrics.totalCostUsd > 0 || metrics.promptTokens > 0)) {
|
|
2108
|
+
const totalTokens = metrics.promptTokens + metrics.completionTokens;
|
|
1663
2109
|
events.push({
|
|
1664
2110
|
event_id: crypto.randomUUID(),
|
|
1665
2111
|
session_id: session.sessionId,
|
|
1666
2112
|
event_type: "cost",
|
|
1667
2113
|
timestamp: now.toISOString(),
|
|
2114
|
+
content: `Cost: $${metrics.totalCostUsd.toFixed(4)} (${totalTokens} tokens)`,
|
|
1668
2115
|
tool: "claude-code",
|
|
1669
2116
|
tokens_prompt: metrics.promptTokens,
|
|
1670
2117
|
tokens_completion: metrics.completionTokens,
|
|
@@ -1678,23 +2125,342 @@ var sessionEndCommand = new Command13("session-end").description("Hook: called w
|
|
|
1678
2125
|
}
|
|
1679
2126
|
sendEvents(session.endpoint, session.apiKey, events).catch(() => {
|
|
1680
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
|
+
}
|
|
1681
2161
|
if (sessionFilePath) {
|
|
1682
2162
|
try {
|
|
1683
2163
|
unlinkSync2(sessionFilePath);
|
|
1684
2164
|
} catch {
|
|
1685
2165
|
}
|
|
1686
2166
|
}
|
|
1687
|
-
const
|
|
1688
|
-
const
|
|
1689
|
-
success(`Valt session ended: ${session.sessionId.slice(0, 8)} (${Math.round(durationMs / 1e3)}s${
|
|
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})`);
|
|
2170
|
+
} catch {
|
|
2171
|
+
}
|
|
2172
|
+
});
|
|
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
|
+
}]);
|
|
1690
2209
|
} catch {
|
|
1691
2210
|
}
|
|
1692
2211
|
});
|
|
1693
|
-
var
|
|
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);
|
|
1694
2460
|
|
|
1695
2461
|
// src/commands/setup.ts
|
|
1696
2462
|
import { Command as Command14 } from "commander";
|
|
1697
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as
|
|
2463
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync5, unlinkSync as unlinkSync3 } from "fs";
|
|
1698
2464
|
import { join as join2 } from "path";
|
|
1699
2465
|
import os3 from "os";
|
|
1700
2466
|
var CLAUDE_DIR = join2(os3.homedir(), ".claude");
|
|
@@ -1703,89 +2469,133 @@ var LEGACY_HOOKS_FILE = join2(CLAUDE_DIR, "hooks.json");
|
|
|
1703
2469
|
var HOOK_PREFIX = "npx --yes @usevalt/cli";
|
|
1704
2470
|
function getValtHooks() {
|
|
1705
2471
|
return {
|
|
2472
|
+
Setup: [
|
|
2473
|
+
{
|
|
2474
|
+
matcher: "*",
|
|
2475
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook setup-hook` }]
|
|
2476
|
+
}
|
|
2477
|
+
],
|
|
1706
2478
|
SessionStart: [
|
|
1707
2479
|
{
|
|
1708
|
-
hooks: [
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
2480
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook session-start` }]
|
|
2481
|
+
}
|
|
2482
|
+
],
|
|
2483
|
+
InstructionsLoaded: [
|
|
2484
|
+
{
|
|
2485
|
+
matcher: "*",
|
|
2486
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook config-loaded` }]
|
|
2487
|
+
}
|
|
2488
|
+
],
|
|
2489
|
+
UserPromptSubmit: [
|
|
2490
|
+
{
|
|
2491
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook prompt-submit` }]
|
|
1714
2492
|
}
|
|
1715
2493
|
],
|
|
1716
2494
|
PreToolUse: [
|
|
1717
2495
|
{
|
|
1718
2496
|
matcher: "*",
|
|
1719
|
-
hooks: [
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
2497
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook tool-call` }]
|
|
2498
|
+
}
|
|
2499
|
+
],
|
|
2500
|
+
PermissionRequest: [
|
|
2501
|
+
{
|
|
2502
|
+
matcher: "*",
|
|
2503
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook permission-request` }]
|
|
1725
2504
|
}
|
|
1726
2505
|
],
|
|
1727
2506
|
PostToolUse: [
|
|
1728
2507
|
{
|
|
1729
2508
|
matcher: "*",
|
|
1730
|
-
hooks: [
|
|
1731
|
-
{
|
|
1732
|
-
type: "command",
|
|
1733
|
-
command: `${HOOK_PREFIX} hook post-tool-use`
|
|
1734
|
-
}
|
|
1735
|
-
]
|
|
2509
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook post-tool-use` }]
|
|
1736
2510
|
}
|
|
1737
2511
|
],
|
|
1738
2512
|
PostToolUseFailure: [
|
|
1739
2513
|
{
|
|
1740
2514
|
matcher: "*",
|
|
1741
|
-
hooks: [
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
2515
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook tool-error` }]
|
|
2516
|
+
}
|
|
2517
|
+
],
|
|
2518
|
+
Notification: [
|
|
2519
|
+
{
|
|
2520
|
+
matcher: "*",
|
|
2521
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook notification` }]
|
|
1747
2522
|
}
|
|
1748
2523
|
],
|
|
1749
2524
|
SubagentStart: [
|
|
1750
2525
|
{
|
|
1751
|
-
hooks: [
|
|
1752
|
-
{
|
|
1753
|
-
type: "command",
|
|
1754
|
-
command: `${HOOK_PREFIX} hook subagent-start`
|
|
1755
|
-
}
|
|
1756
|
-
]
|
|
2526
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook subagent-start` }]
|
|
1757
2527
|
}
|
|
1758
2528
|
],
|
|
1759
2529
|
SubagentStop: [
|
|
1760
2530
|
{
|
|
1761
|
-
hooks: [
|
|
1762
|
-
{
|
|
1763
|
-
type: "command",
|
|
1764
|
-
command: `${HOOK_PREFIX} hook subagent-stop`
|
|
1765
|
-
}
|
|
1766
|
-
]
|
|
2531
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook subagent-stop` }]
|
|
1767
2532
|
}
|
|
1768
2533
|
],
|
|
1769
|
-
|
|
2534
|
+
Stop: [
|
|
1770
2535
|
{
|
|
1771
|
-
hooks: [
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
2536
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook stop` }]
|
|
2537
|
+
}
|
|
2538
|
+
],
|
|
2539
|
+
StopFailure: [
|
|
2540
|
+
{
|
|
2541
|
+
matcher: "*",
|
|
2542
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook stop-failure` }]
|
|
2543
|
+
}
|
|
2544
|
+
],
|
|
2545
|
+
TeammateIdle: [
|
|
2546
|
+
{
|
|
2547
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook teammate-idle` }]
|
|
2548
|
+
}
|
|
2549
|
+
],
|
|
2550
|
+
TaskCompleted: [
|
|
2551
|
+
{
|
|
2552
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook task-completed` }]
|
|
2553
|
+
}
|
|
2554
|
+
],
|
|
2555
|
+
ConfigChange: [
|
|
2556
|
+
{
|
|
2557
|
+
matcher: "*",
|
|
2558
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook config-change` }]
|
|
2559
|
+
}
|
|
2560
|
+
],
|
|
2561
|
+
WorktreeCreate: [
|
|
2562
|
+
{
|
|
2563
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook worktree-create` }]
|
|
2564
|
+
}
|
|
2565
|
+
],
|
|
2566
|
+
WorktreeRemove: [
|
|
2567
|
+
{
|
|
2568
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook worktree-remove` }]
|
|
2569
|
+
}
|
|
2570
|
+
],
|
|
2571
|
+
PreCompact: [
|
|
2572
|
+
{
|
|
2573
|
+
matcher: "*",
|
|
2574
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook compact` }]
|
|
2575
|
+
}
|
|
2576
|
+
],
|
|
2577
|
+
PostCompact: [
|
|
2578
|
+
{
|
|
2579
|
+
matcher: "*",
|
|
2580
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook compact` }]
|
|
2581
|
+
}
|
|
2582
|
+
],
|
|
2583
|
+
Elicitation: [
|
|
2584
|
+
{
|
|
2585
|
+
matcher: "*",
|
|
2586
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook elicitation` }]
|
|
2587
|
+
}
|
|
2588
|
+
],
|
|
2589
|
+
ElicitationResult: [
|
|
2590
|
+
{
|
|
2591
|
+
matcher: "*",
|
|
2592
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook elicitation-result` }]
|
|
1777
2593
|
}
|
|
1778
2594
|
],
|
|
1779
2595
|
SessionEnd: [
|
|
1780
2596
|
{
|
|
1781
2597
|
matcher: "*",
|
|
1782
|
-
hooks: [
|
|
1783
|
-
{
|
|
1784
|
-
type: "command",
|
|
1785
|
-
command: `${HOOK_PREFIX} hook session-end`,
|
|
1786
|
-
timeout: 1e4
|
|
1787
|
-
}
|
|
1788
|
-
]
|
|
2598
|
+
hooks: [{ type: "command", command: `${HOOK_PREFIX} hook session-end`, timeout: 15e3 }]
|
|
1789
2599
|
}
|
|
1790
2600
|
]
|
|
1791
2601
|
};
|
|
@@ -1797,7 +2607,7 @@ var setupCommand = new Command14("setup").description("Configure Claude Code hoo
|
|
|
1797
2607
|
error("No API key found. Run `valt login` first.");
|
|
1798
2608
|
process.exit(1);
|
|
1799
2609
|
}
|
|
1800
|
-
if (!
|
|
2610
|
+
if (!existsSync5(CLAUDE_DIR)) {
|
|
1801
2611
|
mkdirSync2(CLAUDE_DIR, { recursive: true });
|
|
1802
2612
|
}
|
|
1803
2613
|
if (opts.remove) {
|
|
@@ -1805,7 +2615,7 @@ var setupCommand = new Command14("setup").description("Configure Claude Code hoo
|
|
|
1805
2615
|
return;
|
|
1806
2616
|
}
|
|
1807
2617
|
let config = {};
|
|
1808
|
-
if (
|
|
2618
|
+
if (existsSync5(SETTINGS_FILE)) {
|
|
1809
2619
|
try {
|
|
1810
2620
|
const raw = readFileSync4(SETTINGS_FILE, "utf-8");
|
|
1811
2621
|
config = JSON.parse(raw);
|
|
@@ -1831,7 +2641,7 @@ var setupCommand = new Command14("setup").description("Configure Claude Code hoo
|
|
|
1831
2641
|
}
|
|
1832
2642
|
}
|
|
1833
2643
|
writeFileSync4(SETTINGS_FILE, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1834
|
-
if (
|
|
2644
|
+
if (existsSync5(LEGACY_HOOKS_FILE)) {
|
|
1835
2645
|
try {
|
|
1836
2646
|
removeLegacyHooks();
|
|
1837
2647
|
info(`Migrated hooks from legacy hooks.json to settings.json.`);
|
|
@@ -1868,7 +2678,7 @@ function removeValtHooksFromConfig(config) {
|
|
|
1868
2678
|
}
|
|
1869
2679
|
}
|
|
1870
2680
|
function removeLegacyHooks() {
|
|
1871
|
-
if (!
|
|
2681
|
+
if (!existsSync5(LEGACY_HOOKS_FILE)) return;
|
|
1872
2682
|
try {
|
|
1873
2683
|
const raw = readFileSync4(LEGACY_HOOKS_FILE, "utf-8");
|
|
1874
2684
|
const config = JSON.parse(raw);
|
|
@@ -1887,7 +2697,7 @@ function removeLegacyHooks() {
|
|
|
1887
2697
|
}
|
|
1888
2698
|
}
|
|
1889
2699
|
function removeHooks() {
|
|
1890
|
-
if (
|
|
2700
|
+
if (existsSync5(SETTINGS_FILE)) {
|
|
1891
2701
|
try {
|
|
1892
2702
|
const raw = readFileSync4(SETTINGS_FILE, "utf-8");
|
|
1893
2703
|
const config = JSON.parse(raw);
|
|
@@ -1899,14 +2709,14 @@ function removeHooks() {
|
|
|
1899
2709
|
process.exit(1);
|
|
1900
2710
|
}
|
|
1901
2711
|
}
|
|
1902
|
-
if (
|
|
2712
|
+
if (existsSync5(LEGACY_HOOKS_FILE)) {
|
|
1903
2713
|
try {
|
|
1904
2714
|
removeLegacyHooks();
|
|
1905
2715
|
info("Legacy hooks.json cleaned up.");
|
|
1906
2716
|
} catch {
|
|
1907
2717
|
}
|
|
1908
2718
|
}
|
|
1909
|
-
if (!
|
|
2719
|
+
if (!existsSync5(SETTINGS_FILE) && !existsSync5(LEGACY_HOOKS_FILE)) {
|
|
1910
2720
|
info("No hooks configured. Nothing to remove.");
|
|
1911
2721
|
}
|
|
1912
2722
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usevalt/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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/
|
|
43
|
-
"@usevalt/
|
|
42
|
+
"@usevalt/eslint-config": "0.0.0",
|
|
43
|
+
"@usevalt/typescript-config": "0.0.0"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "tsup",
|