@raindrop-ai/claude-code 0.0.3 → 0.0.5

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/cli.js CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/event-mapper.ts
4
- import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
4
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, readdirSync, unlinkSync, writeFileSync } from "fs";
5
+ import { execSync } from "child_process";
5
6
  import { randomUUID as randomUUID2 } from "crypto";
6
7
  import { tmpdir } from "os";
7
8
  import { join } from "path";
@@ -653,7 +654,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
653
654
 
654
655
  // src/package-info.ts
655
656
  var PACKAGE_NAME = "@raindrop-ai/claude-code";
656
- var PACKAGE_VERSION = "0.0.1";
657
+ var PACKAGE_VERSION = "0.0.5";
657
658
 
658
659
  // src/shipper.ts
659
660
  var EventShipper2 = class extends EventShipper {
@@ -690,6 +691,138 @@ var TraceShipper2 = class extends TraceShipper {
690
691
  }
691
692
  };
692
693
 
694
+ // src/transcript.ts
695
+ import { existsSync, readFileSync } from "fs";
696
+ function parseTranscript(transcriptPath) {
697
+ var _a, _b, _c, _d, _e;
698
+ try {
699
+ if (!existsSync(transcriptPath)) return void 0;
700
+ const content = readFileSync(transcriptPath, "utf-8");
701
+ const lines = content.split("\n").filter((l) => l.trim());
702
+ const summary = {
703
+ totalInputTokens: 0,
704
+ totalOutputTokens: 0,
705
+ totalCacheReadTokens: 0,
706
+ totalCacheCreationTokens: 0,
707
+ turnCount: 0,
708
+ toolsUsed: [],
709
+ hasThinking: false
710
+ };
711
+ const toolNames = /* @__PURE__ */ new Set();
712
+ let lastUsage;
713
+ for (const line of lines) {
714
+ let entry;
715
+ try {
716
+ entry = JSON.parse(line);
717
+ } catch (e) {
718
+ continue;
719
+ }
720
+ if (entry.type === "system" && entry.subtype === "turn_duration") {
721
+ const durationMs = entry["durationMs"];
722
+ if (typeof durationMs === "number") {
723
+ summary.totalDurationMs = ((_a = summary.totalDurationMs) != null ? _a : 0) + durationMs;
724
+ }
725
+ const version = entry["version"];
726
+ if (typeof version === "string") {
727
+ summary.codeVersion = version;
728
+ }
729
+ const branch = entry["gitBranch"];
730
+ if (typeof branch === "string") {
731
+ summary.gitBranch = branch;
732
+ }
733
+ continue;
734
+ }
735
+ if (entry.type !== "assistant") continue;
736
+ const msg = entry.message;
737
+ if (!msg) continue;
738
+ summary.turnCount++;
739
+ if (msg.model) {
740
+ summary.model = msg.model;
741
+ }
742
+ if (msg.stop_reason) {
743
+ summary.stopReason = msg.stop_reason;
744
+ }
745
+ const entryVersion = entry["version"];
746
+ if (typeof entryVersion === "string") {
747
+ summary.codeVersion = entryVersion;
748
+ }
749
+ const entryBranch = entry["gitBranch"];
750
+ if (typeof entryBranch === "string") {
751
+ summary.gitBranch = entryBranch;
752
+ }
753
+ if (msg.usage) {
754
+ const u = msg.usage;
755
+ summary.totalInputTokens += (_b = u.input_tokens) != null ? _b : 0;
756
+ summary.totalOutputTokens += (_c = u.output_tokens) != null ? _c : 0;
757
+ summary.totalCacheReadTokens += (_d = u.cache_read_input_tokens) != null ? _d : 0;
758
+ summary.totalCacheCreationTokens += (_e = u.cache_creation_input_tokens) != null ? _e : 0;
759
+ if (u.service_tier) {
760
+ summary.serviceTier = u.service_tier;
761
+ }
762
+ lastUsage = u;
763
+ }
764
+ if (Array.isArray(msg.content)) {
765
+ for (const block of msg.content) {
766
+ if (block.type === "tool_use" && block.name) {
767
+ toolNames.add(block.name);
768
+ }
769
+ if (block.type === "thinking") {
770
+ summary.hasThinking = true;
771
+ }
772
+ }
773
+ }
774
+ }
775
+ if (lastUsage) {
776
+ summary.lastTurnInputTokens = lastUsage.input_tokens;
777
+ summary.lastTurnOutputTokens = lastUsage.output_tokens;
778
+ summary.lastTurnCacheReadTokens = lastUsage.cache_read_input_tokens;
779
+ }
780
+ summary.toolsUsed = [...toolNames].sort();
781
+ return summary;
782
+ } catch (e) {
783
+ return void 0;
784
+ }
785
+ }
786
+ function transcriptToProperties(summary) {
787
+ var _a, _b;
788
+ const props = {};
789
+ props["ai.usage.prompt_tokens"] = (_a = summary.lastTurnInputTokens) != null ? _a : summary.totalInputTokens;
790
+ props["ai.usage.completion_tokens"] = (_b = summary.lastTurnOutputTokens) != null ? _b : summary.totalOutputTokens;
791
+ if (summary.lastTurnCacheReadTokens !== void 0) {
792
+ props["ai.usage.cache_read_tokens"] = summary.lastTurnCacheReadTokens;
793
+ }
794
+ props["ai.usage.session_total.prompt_tokens"] = summary.totalInputTokens;
795
+ props["ai.usage.session_total.completion_tokens"] = summary.totalOutputTokens;
796
+ props["ai.usage.session_total.cache_read_tokens"] = summary.totalCacheReadTokens;
797
+ props["ai.usage.session_total.cache_creation_tokens"] = summary.totalCacheCreationTokens;
798
+ if (summary.model) {
799
+ props["ai.model"] = summary.model;
800
+ }
801
+ if (summary.serviceTier) {
802
+ props["ai.service_tier"] = summary.serviceTier;
803
+ }
804
+ if (summary.stopReason) {
805
+ props["ai.stop_reason"] = summary.stopReason;
806
+ }
807
+ if (summary.toolsUsed.length > 0) {
808
+ props["ai.tools_used"] = summary.toolsUsed.join(", ");
809
+ }
810
+ props["ai.turn_count"] = summary.turnCount;
811
+ if (summary.totalDurationMs !== void 0) {
812
+ props["ai.duration_ms"] = summary.totalDurationMs;
813
+ }
814
+ if (summary.codeVersion) {
815
+ props["claude_code.version"] = summary.codeVersion;
816
+ }
817
+ if (summary.gitBranch) {
818
+ props["git.branch"] = summary.gitBranch;
819
+ }
820
+ if (summary.hasThinking) {
821
+ props["ai.has_thinking"] = true;
822
+ }
823
+ return props;
824
+ }
825
+
693
826
  // src/event-mapper.ts
694
827
  var MAX_ATTR_LENGTH = 32768;
695
828
  function truncate(value) {
@@ -734,8 +867,8 @@ function writeState(key, value) {
734
867
  function readState(key) {
735
868
  try {
736
869
  const filePath = statePath(key);
737
- if (!existsSync(filePath)) return void 0;
738
- return readFileSync(filePath, "utf-8");
870
+ if (!existsSync2(filePath)) return void 0;
871
+ return readFileSync2(filePath, "utf-8");
739
872
  } catch (e) {
740
873
  return void 0;
741
874
  }
@@ -796,15 +929,19 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
796
929
  var _a;
797
930
  const convoId = payload.session_id;
798
931
  const baseProperties = {
932
+ ...config.customProperties,
799
933
  cwd: payload.cwd,
800
934
  permission_mode: payload.permission_mode,
801
935
  plugin_version: PACKAGE_VERSION,
936
+ sdk: PACKAGE_NAME,
937
+ sdk_version: PACKAGE_VERSION,
802
938
  ...payload.agent_id ? { agent_id: payload.agent_id } : {},
803
939
  ...payload.agent_type ? { agent_type: payload.agent_type } : {}
804
940
  };
805
941
  switch (payload.hook_event_name) {
806
942
  case "SessionStart":
807
943
  writeState(`model_${payload.session_id}`, (_a = payload.model) != null ? _a : "");
944
+ tryCaptureAppendSystemPrompt(payload.session_id);
808
945
  break;
809
946
  case "UserPromptSubmit":
810
947
  await handleUserPromptSubmit(payload, convoId, config, baseProperties, eventShipper, traceShipper);
@@ -819,10 +956,13 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
819
956
  handlePostToolUseFailure(payload, getCurrentEventId(payload.session_id), traceShipper);
820
957
  break;
821
958
  case "Stop":
822
- await handleStop(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
959
+ await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper, traceShipper);
823
960
  break;
824
961
  case "StopFailure":
825
- await handleStopFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
962
+ await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper, traceShipper, {
963
+ error: payload.error,
964
+ error_details: payload.error_details
965
+ });
826
966
  break;
827
967
  case "SubagentStart":
828
968
  handleSubagentStart(payload, getCurrentEventId(payload.session_id), traceShipper);
@@ -833,6 +973,9 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
833
973
  case "PermissionDenied":
834
974
  handlePermissionDenied(payload, getCurrentEventId(payload.session_id), traceShipper);
835
975
  break;
976
+ case "InstructionsLoaded":
977
+ handleInstructionsLoaded(payload);
978
+ break;
836
979
  case "PostCompact":
837
980
  await handlePostCompact(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
838
981
  break;
@@ -841,6 +984,8 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
841
984
  deleteState(turnEventKey(payload.session_id));
842
985
  deleteState(rootSpanKey(payload.session_id));
843
986
  deleteState(`model_${payload.session_id}`);
987
+ deleteState(appendSystemPromptKey(payload.session_id));
988
+ cleanupInstructions(payload.session_id);
844
989
  break;
845
990
  default:
846
991
  if (config.debug) {
@@ -870,7 +1015,7 @@ async function handleUserPromptSubmit(payload, convoId, config, properties, even
870
1015
  isPending: true,
871
1016
  userId: config.userId,
872
1017
  convoId,
873
- eventName: "claude_code_session",
1018
+ eventName: config.eventName,
874
1019
  input: payload.prompt,
875
1020
  model,
876
1021
  properties
@@ -990,7 +1135,7 @@ async function handlePostCompact(payload, eventId, config, properties, eventShip
990
1135
  isPending: true,
991
1136
  userId: config.userId,
992
1137
  convoId: payload.session_id,
993
- eventName: "claude_code_session",
1138
+ eventName: config.eventName,
994
1139
  properties: {
995
1140
  ...properties,
996
1141
  compaction_trigger: payload.trigger,
@@ -998,27 +1143,251 @@ async function handlePostCompact(payload, eventId, config, properties, eventShip
998
1143
  }
999
1144
  });
1000
1145
  }
1001
- async function handleStop(payload, eventId, config, properties, eventShipper) {
1002
- await eventShipper.finish(eventId, {
1003
- userId: config.userId,
1004
- output: payload.last_assistant_message,
1005
- properties
1006
- });
1146
+ function handleInstructionsLoaded(payload) {
1147
+ var _a;
1148
+ if (!payload.file_path || !payload.session_id) return;
1149
+ try {
1150
+ if (existsSync2(payload.file_path)) {
1151
+ const content = readFileSync2(payload.file_path, "utf-8");
1152
+ const MAX_INSTRUCTIONS_LENGTH = 8192;
1153
+ const truncated = content.length > MAX_INSTRUCTIONS_LENGTH ? content.slice(0, MAX_INSTRUCTIONS_LENGTH) + "\n...[truncated]" : content;
1154
+ const fileKey = safeKey(payload.file_path);
1155
+ const header = `# ${(_a = payload.memory_type) != null ? _a : "instructions"}: ${payload.file_path}
1156
+ `;
1157
+ writeState(
1158
+ `instr_${payload.session_id}_${fileKey}`,
1159
+ header + truncated
1160
+ );
1161
+ }
1162
+ } catch (e) {
1163
+ }
1164
+ }
1165
+ function gatherInstructions(sessionId) {
1166
+ try {
1167
+ if (!existsSync2(STATE_DIR)) return void 0;
1168
+ const prefix = safeKey(`instr_${sessionId}_`);
1169
+ const files = readdirSync(STATE_DIR).filter((f) => f.startsWith(prefix));
1170
+ if (files.length === 0) return void 0;
1171
+ const parts = [];
1172
+ for (const file of files.sort()) {
1173
+ try {
1174
+ parts.push(readFileSync2(join(STATE_DIR, file), "utf-8"));
1175
+ } catch (e) {
1176
+ continue;
1177
+ }
1178
+ }
1179
+ return parts.length > 0 ? parts.join("\n\n---\n\n") : void 0;
1180
+ } catch (e) {
1181
+ return void 0;
1182
+ }
1183
+ }
1184
+ function cleanupInstructions(sessionId) {
1185
+ try {
1186
+ if (!existsSync2(STATE_DIR)) return;
1187
+ const prefix = safeKey(`instr_${sessionId}_`);
1188
+ for (const file of readdirSync(STATE_DIR).filter((f) => f.startsWith(prefix))) {
1189
+ try {
1190
+ unlinkSync(join(STATE_DIR, file));
1191
+ } catch (e) {
1192
+ }
1193
+ }
1194
+ } catch (e) {
1195
+ }
1196
+ }
1197
+ var APPEND_FLAG = "--append-system-prompt";
1198
+ var APPEND_FILE_FLAG = "--append-system-prompt-file";
1199
+ var MAX_ANCESTOR_DEPTH = 15;
1200
+ var PS_TIMEOUT_MS = 3e3;
1201
+ function appendSystemPromptKey(sessionId) {
1202
+ return `append_sysprompt_${sessionId}`;
1203
+ }
1204
+ function tryCaptureAppendSystemPrompt(sessionId) {
1205
+ try {
1206
+ const content = readAncestorAppendSystemPrompt();
1207
+ if (content) {
1208
+ writeState(appendSystemPromptKey(sessionId), content);
1209
+ }
1210
+ } catch (e) {
1211
+ }
1212
+ }
1213
+ function readAncestorAppendSystemPrompt() {
1214
+ let pid;
1215
+ try {
1216
+ pid = process.ppid;
1217
+ } catch (e) {
1218
+ return void 0;
1219
+ }
1220
+ if (!pid || pid <= 1) return void 0;
1221
+ for (let depth = 0; depth < MAX_ANCESTOR_DEPTH && pid != null && pid > 1; depth++) {
1222
+ try {
1223
+ const args2 = getProcessArgs(pid);
1224
+ if (args2 && args2.length > 0) {
1225
+ const content = extractAppendSystemPrompt(args2);
1226
+ if (content) return content;
1227
+ }
1228
+ } catch (e) {
1229
+ }
1230
+ try {
1231
+ pid = getParentPid(pid);
1232
+ } catch (e) {
1233
+ break;
1234
+ }
1235
+ }
1236
+ return void 0;
1237
+ }
1238
+ function getProcessArgs(pid) {
1239
+ if (process.platform === "linux") {
1240
+ try {
1241
+ const raw = readFileSync2(`/proc/${pid}/cmdline`, "utf-8");
1242
+ const args2 = raw.split("\0").filter(Boolean);
1243
+ return args2.length > 0 ? args2 : void 0;
1244
+ } catch (e) {
1245
+ }
1246
+ }
1247
+ try {
1248
+ const output = execSync(`ps -ww -o args= -p ${pid}`, {
1249
+ encoding: "utf-8",
1250
+ timeout: PS_TIMEOUT_MS,
1251
+ stdio: ["ignore", "pipe", "ignore"]
1252
+ });
1253
+ const trimmed = output.trim();
1254
+ if (!trimmed) return void 0;
1255
+ return trimmed.split(/\s+/);
1256
+ } catch (e) {
1257
+ return void 0;
1258
+ }
1007
1259
  }
1008
- async function handleStopFailure(payload, eventId, config, properties, eventShipper) {
1009
- await eventShipper.finish(eventId, {
1260
+ function getParentPid(pid) {
1261
+ try {
1262
+ const output = execSync(`ps -o ppid= -p ${pid}`, {
1263
+ encoding: "utf-8",
1264
+ timeout: PS_TIMEOUT_MS,
1265
+ stdio: ["ignore", "pipe", "ignore"]
1266
+ });
1267
+ const ppid = parseInt(output.trim(), 10);
1268
+ return Number.isFinite(ppid) && ppid > 0 ? ppid : void 0;
1269
+ } catch (e) {
1270
+ return void 0;
1271
+ }
1272
+ }
1273
+ function extractAppendSystemPrompt(args2) {
1274
+ const parts = [];
1275
+ for (let i = 0; i < args2.length; i++) {
1276
+ const arg = args2[i];
1277
+ try {
1278
+ if (arg === APPEND_FILE_FLAG && i + 1 < args2.length) {
1279
+ const filePath = args2[i + 1];
1280
+ i++;
1281
+ try {
1282
+ if (existsSync2(filePath)) {
1283
+ parts.push(readFileSync2(filePath, "utf-8"));
1284
+ }
1285
+ } catch (e) {
1286
+ }
1287
+ continue;
1288
+ }
1289
+ if (arg.startsWith(APPEND_FILE_FLAG + "=")) {
1290
+ const filePath = arg.slice(APPEND_FILE_FLAG.length + 1);
1291
+ try {
1292
+ if (filePath && existsSync2(filePath)) {
1293
+ parts.push(readFileSync2(filePath, "utf-8"));
1294
+ }
1295
+ } catch (e) {
1296
+ }
1297
+ continue;
1298
+ }
1299
+ if (arg === APPEND_FLAG && i + 1 < args2.length) {
1300
+ const valueParts = [];
1301
+ for (let j = i + 1; j < args2.length; j++) {
1302
+ if (args2[j].startsWith("--")) break;
1303
+ valueParts.push(args2[j]);
1304
+ i = j;
1305
+ }
1306
+ if (valueParts.length > 0) {
1307
+ parts.push(valueParts.join(" "));
1308
+ }
1309
+ continue;
1310
+ }
1311
+ if (arg.startsWith(APPEND_FLAG + "=") && !arg.startsWith(APPEND_FILE_FLAG)) {
1312
+ const value = arg.slice(APPEND_FLAG.length + 1);
1313
+ if (value) {
1314
+ parts.push(value);
1315
+ }
1316
+ continue;
1317
+ }
1318
+ } catch (e) {
1319
+ continue;
1320
+ }
1321
+ }
1322
+ return parts.length > 0 ? parts.join("\n") : void 0;
1323
+ }
1324
+ function readAppendSystemPrompt(sessionId) {
1325
+ try {
1326
+ return readState(appendSystemPromptKey(sessionId)) || void 0;
1327
+ } catch (e) {
1328
+ return void 0;
1329
+ }
1330
+ }
1331
+ function enrichFromTranscript(payload, eventId, traceShipper) {
1332
+ var _a, _b;
1333
+ if (!payload.transcript_path) {
1334
+ return { summary: void 0, props: {} };
1335
+ }
1336
+ try {
1337
+ const summary = parseTranscript(payload.transcript_path);
1338
+ if (!summary) return { summary: void 0, props: {} };
1339
+ const props = transcriptToProperties(summary);
1340
+ const spanInputTokens = (_a = summary.lastTurnInputTokens) != null ? _a : summary.totalInputTokens;
1341
+ const spanOutputTokens = (_b = summary.lastTurnOutputTokens) != null ? _b : summary.totalOutputTokens;
1342
+ if (summary.model && (spanInputTokens > 0 || spanOutputTokens > 0)) {
1343
+ const parent = getParentContext(payload);
1344
+ const now = nowUnixNanoString();
1345
+ traceShipper.createSpan({
1346
+ name: summary.model,
1347
+ eventId,
1348
+ parent,
1349
+ startTimeUnixNano: now,
1350
+ endTimeUnixNano: now,
1351
+ attributes: [
1352
+ attrString("ai.operationId", "generateText"),
1353
+ attrString("gen_ai.response.model", summary.model),
1354
+ attrString("gen_ai.system", "anthropic"),
1355
+ attrInt("gen_ai.usage.prompt_tokens", spanInputTokens),
1356
+ attrInt("gen_ai.usage.completion_tokens", spanOutputTokens),
1357
+ ...summary.lastTurnCacheReadTokens != null && summary.lastTurnCacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", summary.lastTurnCacheReadTokens)] : []
1358
+ ]
1359
+ });
1360
+ }
1361
+ return { summary, props };
1362
+ } catch (e) {
1363
+ return { summary: void 0, props: {} };
1364
+ }
1365
+ }
1366
+ async function handleStopOrFailure(payload, eventId, config, properties, eventShipper, traceShipper, extraProperties) {
1367
+ const { summary, props: transcriptProps } = enrichFromTranscript(payload, eventId, traceShipper);
1368
+ const instructions = gatherInstructions(payload.session_id);
1369
+ const appendSysPrompt = readAppendSystemPrompt(payload.session_id);
1370
+ await eventShipper.patch(eventId, {
1371
+ isPending: false,
1010
1372
  userId: config.userId,
1373
+ convoId: payload.session_id,
1374
+ eventName: config.eventName,
1011
1375
  output: payload.last_assistant_message,
1376
+ ...(summary == null ? void 0 : summary.model) ? { model: summary.model } : {},
1012
1377
  properties: {
1013
1378
  ...properties,
1014
- error: payload.error,
1015
- error_details: payload.error_details
1379
+ ...transcriptProps,
1380
+ ...instructions ? { system_instructions: truncate(instructions) } : {},
1381
+ ...appendSysPrompt ? { append_system_prompt: truncate(appendSysPrompt) } : {},
1382
+ ...extraProperties
1016
1383
  }
1017
1384
  });
1018
1385
  }
1019
1386
  async function handleSessionEnd(payload, eventId, config, properties, eventShipper) {
1020
- await eventShipper.finish(eventId, {
1387
+ await eventShipper.patch(eventId, {
1388
+ isPending: false,
1021
1389
  userId: config.userId,
1390
+ eventName: config.eventName,
1022
1391
  properties: {
1023
1392
  ...properties,
1024
1393
  session_end_reason: payload.reason
@@ -1027,16 +1396,16 @@ async function handleSessionEnd(payload, eventId, config, properties, eventShipp
1027
1396
  }
1028
1397
 
1029
1398
  // src/config.ts
1030
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
1399
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
1031
1400
  import { homedir, userInfo } from "os";
1032
1401
  import { dirname, join as join2 } from "path";
1033
1402
  var CONFIG_PATH = join2(homedir(), ".config", "raindrop", "config.json");
1034
1403
  function loadConfig() {
1035
- var _a, _b, _c, _d, _e, _f, _g, _h;
1404
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1036
1405
  let file = {};
1037
1406
  try {
1038
- if (existsSync2(CONFIG_PATH)) {
1039
- file = JSON.parse(readFileSync2(CONFIG_PATH, "utf-8"));
1407
+ if (existsSync3(CONFIG_PATH)) {
1408
+ file = JSON.parse(readFileSync3(CONFIG_PATH, "utf-8"));
1040
1409
  }
1041
1410
  } catch (e) {
1042
1411
  }
@@ -1047,12 +1416,25 @@ function loadConfig() {
1047
1416
  return "unknown";
1048
1417
  }
1049
1418
  })();
1419
+ let customProperties = (_a = file.custom_properties) != null ? _a : {};
1420
+ const envProps = process.env["RAINDROP_PROPERTIES"];
1421
+ if (envProps) {
1422
+ try {
1423
+ const parsed = JSON.parse(envProps);
1424
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1425
+ customProperties = { ...customProperties, ...parsed };
1426
+ }
1427
+ } catch (e) {
1428
+ }
1429
+ }
1050
1430
  return {
1051
- writeKey: (_b = (_a = process.env["RAINDROP_WRITE_KEY"]) != null ? _a : file.write_key) != null ? _b : "",
1052
- endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : file.api_url) != null ? _d : "https://api.raindrop.ai/v1",
1053
- userId: (_f = (_e = process.env["RAINDROP_USER_ID"]) != null ? _e : file.user_id) != null ? _f : systemUser,
1054
- debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_g = file.debug) != null ? _g : false,
1055
- enabled: (_h = file.enabled) != null ? _h : true
1431
+ writeKey: (_c = (_b = process.env["RAINDROP_WRITE_KEY"]) != null ? _b : file.write_key) != null ? _c : "",
1432
+ endpoint: (_e = (_d = process.env["RAINDROP_API_URL"]) != null ? _d : file.api_url) != null ? _e : "https://api.raindrop.ai/v1",
1433
+ userId: (_g = (_f = process.env["RAINDROP_USER_ID"]) != null ? _f : file.user_id) != null ? _g : systemUser,
1434
+ debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_h = file.debug) != null ? _h : false,
1435
+ enabled: (_i = file.enabled) != null ? _i : true,
1436
+ eventName: (_k = (_j = process.env["RAINDROP_EVENT_NAME"]) != null ? _j : file.event_name) != null ? _k : "claude_code_session",
1437
+ customProperties
1056
1438
  };
1057
1439
  }
1058
1440
  function getConfigPath() {
@@ -1063,8 +1445,8 @@ function updateConfig(patch) {
1063
1445
  mkdirSync2(dir, { recursive: true });
1064
1446
  let existing = {};
1065
1447
  try {
1066
- if (existsSync2(CONFIG_PATH)) {
1067
- existing = JSON.parse(readFileSync2(CONFIG_PATH, "utf-8"));
1448
+ if (existsSync3(CONFIG_PATH)) {
1449
+ existing = JSON.parse(readFileSync3(CONFIG_PATH, "utf-8"));
1068
1450
  }
1069
1451
  } catch (e) {
1070
1452
  }
@@ -1072,7 +1454,7 @@ function updateConfig(patch) {
1072
1454
  }
1073
1455
 
1074
1456
  // src/local-debugger.ts
1075
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
1457
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
1076
1458
  import { tmpdir as tmpdir2 } from "os";
1077
1459
  import { join as join3 } from "path";
1078
1460
  var DEFAULT_PORT = 5899;
@@ -1083,8 +1465,8 @@ var CACHE_FILE = join3(CACHE_DIR, "debugger_cache");
1083
1465
  var CACHE_TTL_MS = 5e3;
1084
1466
  function readCache() {
1085
1467
  try {
1086
- if (!existsSync3(CACHE_FILE)) return void 0;
1087
- const data = JSON.parse(readFileSync3(CACHE_FILE, "utf-8"));
1468
+ if (!existsSync4(CACHE_FILE)) return void 0;
1469
+ const data = JSON.parse(readFileSync4(CACHE_FILE, "utf-8"));
1088
1470
  if (Date.now() - data.ts > CACHE_TTL_MS) return void 0;
1089
1471
  return data;
1090
1472
  } catch (e) {
@@ -1153,7 +1535,7 @@ function mirrorEventToLocalDebugger(baseUrl, payload, debug) {
1153
1535
  }
1154
1536
 
1155
1537
  // src/hook-handler.ts
1156
- import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
1538
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
1157
1539
  import { join as join4 } from "path";
1158
1540
  import { tmpdir as tmpdir3 } from "os";
1159
1541
  var STDIN_TIMEOUT_MS = 5e3;
@@ -1162,8 +1544,8 @@ function getOtlpTraceId(sessionId) {
1162
1544
  try {
1163
1545
  const key = `rootspan_${sessionId}`.replace(/[^a-zA-Z0-9_\-]/g, "_");
1164
1546
  const filePath = join4(STATE_DIR2, key);
1165
- if (!existsSync4(filePath)) return void 0;
1166
- const ctx = JSON.parse(readFileSync4(filePath, "utf-8"));
1547
+ if (!existsSync5(filePath)) return void 0;
1548
+ const ctx = JSON.parse(readFileSync5(filePath, "utf-8"));
1167
1549
  if (!ctx.traceIdB64) return void 0;
1168
1550
  return Buffer.from(ctx.traceIdB64, "base64").toString("hex");
1169
1551
  } catch (e) {
@@ -1223,7 +1605,9 @@ async function handleHook() {
1223
1605
  }
1224
1606
  const mapperConfig = {
1225
1607
  userId: config.userId,
1226
- debug: config.debug
1608
+ debug: config.debug,
1609
+ eventName: config.eventName,
1610
+ customProperties: config.customProperties
1227
1611
  };
1228
1612
  const eventShipper = new EventShipper2({
1229
1613
  writeKey: config.writeKey,
@@ -1280,11 +1664,11 @@ async function handleHook() {
1280
1664
  }
1281
1665
 
1282
1666
  // src/setup.ts
1283
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
1667
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
1284
1668
  import { homedir as homedir2, userInfo as userInfo2 } from "os";
1285
1669
  import { dirname as dirname2, join as join5, resolve } from "path";
1286
1670
  import { createInterface } from "readline";
1287
- import { execSync } from "child_process";
1671
+ import { execSync as execSync2 } from "child_process";
1288
1672
  function getClaudeSettingsPath(scope, cwd) {
1289
1673
  if (scope === "project") {
1290
1674
  return resolve(cwd, ".claude", "settings.json");
@@ -1303,8 +1687,9 @@ var CORE_HOOK_EVENTS = [
1303
1687
  "SessionEnd"
1304
1688
  ];
1305
1689
  var VERSIONED_HOOK_EVENTS = [
1306
- { minVersion: "2.1.78", events: ["StopFailure"] },
1307
1690
  { minVersion: "2.1.76", events: ["PostCompact"] },
1691
+ { minVersion: "2.1.78", events: ["StopFailure"] },
1692
+ { minVersion: "2.1.78", events: ["InstructionsLoaded"] },
1308
1693
  { minVersion: "2.1.89", events: ["PermissionDenied"] }
1309
1694
  ];
1310
1695
  function parseVersion(version) {
@@ -1323,7 +1708,7 @@ function isVersionAtLeast(current, required) {
1323
1708
  }
1324
1709
  function detectClaudeCodeVersion() {
1325
1710
  try {
1326
- const output = execSync("claude --version", {
1711
+ const output = execSync2("claude --version", {
1327
1712
  encoding: "utf-8",
1328
1713
  timeout: 5e3,
1329
1714
  stdio: ["ignore", "pipe", "ignore"]
@@ -1402,8 +1787,8 @@ async function runSetup(args2) {
1402
1787
  mkdirSync4(configDir, { recursive: true });
1403
1788
  let existingConfig = {};
1404
1789
  try {
1405
- if (existsSync5(configPath)) {
1406
- existingConfig = JSON.parse(readFileSync5(configPath, "utf-8"));
1790
+ if (existsSync6(configPath)) {
1791
+ existingConfig = JSON.parse(readFileSync6(configPath, "utf-8"));
1407
1792
  }
1408
1793
  } catch (e) {
1409
1794
  }
@@ -1427,9 +1812,9 @@ async function runSetup(args2) {
1427
1812
  const settingsDir = dirname2(settingsPath);
1428
1813
  mkdirSync4(settingsDir, { recursive: true });
1429
1814
  let settings = {};
1430
- if (existsSync5(settingsPath)) {
1815
+ if (existsSync6(settingsPath)) {
1431
1816
  try {
1432
- settings = JSON.parse(readFileSync5(settingsPath, "utf-8"));
1817
+ settings = JSON.parse(readFileSync6(settingsPath, "utf-8"));
1433
1818
  } catch (e) {
1434
1819
  console.warn(` Warning: could not parse ${settingsPath}, creating fresh`);
1435
1820
  settings = {};
@@ -1459,7 +1844,7 @@ async function runSetup(args2) {
1459
1844
  console.log(` Updated Claude Code hooks in ${settingsPath} (${scopeLabel})`);
1460
1845
  const whichCmd = process.platform === "win32" ? "where" : "which";
1461
1846
  try {
1462
- execSync(`${whichCmd} raindrop-claude-code`, { stdio: "ignore" });
1847
+ execSync2(`${whichCmd} raindrop-claude-code`, { stdio: "ignore" });
1463
1848
  } catch (e) {
1464
1849
  console.log(`
1465
1850
  Warning: 'raindrop-claude-code' is not in your PATH.
@@ -1569,6 +1954,8 @@ async function main() {
1569
1954
  Environment:
1570
1955
  RAINDROP_WRITE_KEY API write key (alternative to --write-key or config file)
1571
1956
  RAINDROP_USER_ID User ID override
1957
+ RAINDROP_EVENT_NAME Custom event name (default: "claude_code_session")
1958
+ RAINDROP_PROPERTIES JSON object merged into every event's properties
1572
1959
  RAINDROP_API_URL Custom API endpoint
1573
1960
  RAINDROP_LOCAL_DEBUGGER Local debugger URL (auto-detected if running on :5899)
1574
1961
  RAINDROP_DEBUG Set to "true" for verbose logging