runtape 0.6.0 → 0.7.1

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.
@@ -14,7 +14,12 @@ var CliAugment = z.object({
14
14
  });
15
15
  var SessionStartEvent = ClaudeHookBase.extend(CliAugment.shape).extend({
16
16
  type: z.literal("session_start"),
17
- source: z.string()
17
+ source: z.string(),
18
+ // Best-effort project label derived from the session's cwd. The CLI fills
19
+ // this in on SessionStart (outermost git repo basename, falling back to
20
+ // nearest package.json name, finally to basename(cwd)). Optional so older
21
+ // CLIs that never emit it remain compatible.
22
+ project_name: z.string().min(1).optional()
18
23
  });
19
24
  var UserPromptEvent = ClaudeHookBase.extend(CliAugment.shape).extend({
20
25
  type: z.literal("user_prompt"),
@@ -77,7 +82,7 @@ var RuntapeEvent = z.discriminatedUnion("type", [
77
82
  SessionCloseEvent
78
83
  ]);
79
84
  var IngestionRequest = z.object({
80
- events: z.array(RuntapeEvent).min(1).max(100)
85
+ events: z.array(RuntapeEvent).min(1).max(500)
81
86
  });
82
87
  var IngestionResponse = z.object({
83
88
  accepted: z.number().int().nonnegative(),
@@ -102,4 +107,4 @@ export {
102
107
  IngestionRequest,
103
108
  IngestionResponse
104
109
  };
105
- //# sourceMappingURL=chunk-LGAXYSLN.js.map
110
+ //# sourceMappingURL=chunk-54VJGDD2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import { z } from 'zod';\n\n// Every Claude Code hook event carries these envelope fields.\nconst ClaudeHookBase = z.object({\n session_id: z.string().min(1),\n transcript_path: z.string().min(1),\n cwd: z.string().min(1),\n hook_event_name: z.string().min(1),\n permission_mode: z.string().optional(),\n});\n\n// CLI-augmented envelope adds monotonic ordering + wall-clock timestamp.\n// agent_tool_use_id is set when the CLI is synthesizing events from a\n// subagent transcript — it carries the parent Agent step's tool_use_id so\n// the server can resolve parent_step_id deterministically instead of\n// relying on the temporal open-stack heuristic. Optional so the field is\n// absent for normal top-level events.\nconst CliAugment = z.object({\n wall_ts: z.string().datetime(),\n sequence: z.number().int().nonnegative(),\n agent_tool_use_id: z.string().min(1).optional(),\n});\n\nexport const SessionStartEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('session_start'),\n source: z.string(),\n // Best-effort project label derived from the session's cwd. The CLI fills\n // this in on SessionStart (outermost git repo basename, falling back to\n // nearest package.json name, finally to basename(cwd)). Optional so older\n // CLIs that never emit it remain compatible.\n project_name: z.string().min(1).optional(),\n});\n\nexport const UserPromptEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('user_prompt'),\n prompt: z.string(),\n});\n\nexport const ToolAttemptEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('tool_attempt'),\n tool_name: z.string().min(1),\n tool_input: z.unknown(),\n tool_use_id: z.string().min(1),\n});\n\nexport const ToolCallEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('tool_call'),\n tool_name: z.string().min(1),\n tool_input: z.unknown(),\n tool_response: z.unknown(),\n tool_use_id: z.string().min(1),\n duration_ms: z.number().nonnegative(),\n // Set by the CLI when the tool_response signals an error (Bash non-zero\n // exit, Edit/Write rejection, tool_response.is_error, etc.). Optional so\n // older CLI versions stay forward-compatible.\n is_error: z.boolean().optional(),\n error_message: z.string().optional(),\n});\n\n// Emitted by the CLI on Stop / PostToolUse after scanning the Claude Code\n// transcript JSONL. One event per assistant message we haven't seen yet,\n// keyed by message_uuid so re-deliveries dedupe at the server. Carries the\n// model identifier and token usage for that turn — the only place either\n// datum is available in Claude Code's emit chain.\nexport const AssistantTurnEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('assistant_turn'),\n message_uuid: z.string().min(1),\n model: z.string().min(1),\n input_tokens: z.number().int().nonnegative(),\n output_tokens: z.number().int().nonnegative(),\n cache_read_tokens: z.number().int().nonnegative().default(0),\n cache_creation_tokens: z.number().int().nonnegative().default(0),\n text: z.string().optional(),\n});\n\nexport const SubagentEndEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('subagent_end'),\n agent_id: z.string().min(1),\n agent_type: z.string().min(1),\n agent_transcript_path: z.string().min(1),\n last_assistant_message: z.string(),\n stop_hook_active: z.boolean().optional(),\n});\n\n// Stop hook (fires every turn). Despite the literal name 'session_end', this\n// is \"turn end\" semantically. Kept for backward compatibility with CLI <= 0.3.x.\n// New CLIs still emit this; the server interprets it as \"run is idle\".\nexport const SessionEndEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('session_end'),\n last_assistant_message: z.string().optional(),\n stop_hook_active: z.boolean().optional(),\n});\n\n// SessionEnd hook (fires once when Claude Code actually closes the session).\n// Server uses this to promote the run from 'idle' to 'ended'.\nexport const SessionCloseEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('session_close'),\n reason: z.string().optional(),\n});\n\nexport const RuntapeEvent = z.discriminatedUnion('type', [\n SessionStartEvent,\n UserPromptEvent,\n ToolAttemptEvent,\n ToolCallEvent,\n AssistantTurnEvent,\n SubagentEndEvent,\n SessionEndEvent,\n SessionCloseEvent,\n]);\n\nexport type RuntapeEvent = z.infer<typeof RuntapeEvent>;\n\n// POST /v1/events body — a batch of up to 500 events. The cap MUST match the\n// CLI flusher's BATCH_MAX (src/lib/flusher.ts). They diverged silently from\n// 0.5.0–0.7.0 (flusher = 500, schema = 100), causing every overflow batch to\n// 400 with \"too_big\" → flusher's poison-drop path nuked them, taking out\n// Stop hooks and any other events that piled up with them. Keep these two\n// numbers in lockstep — if you raise one, raise the other.\nexport const IngestionRequest = z.object({\n events: z.array(RuntapeEvent).min(1).max(500),\n});\n\nexport type IngestionRequest = z.infer<typeof IngestionRequest>;\n\n// Response shape (server returns 200 on accepted, 400 on Zod failure, 401 on bad auth).\nexport const IngestionResponse = z.object({\n accepted: z.number().int().nonnegative(),\n errors: z\n .array(\n z.object({\n index: z.number().int().nonnegative(),\n reason: z.string(),\n }),\n )\n .default([]),\n});\n\nexport type IngestionResponse = z.infer<typeof IngestionResponse>;\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  RuntapeEvent
3
- } from "./chunk-LGAXYSLN.js";
3
+ } from "./chunk-54VJGDD2.js";
4
4
 
5
5
  // src/index.ts
6
6
  import { readFileSync } from "fs";
7
7
  import { fileURLToPath as fileURLToPath2 } from "url";
8
- import { dirname as dirname8, join as join2 } from "path";
8
+ import { dirname as dirname9, join as join3 } from "path";
9
9
  import { Command } from "commander";
10
10
 
11
11
  // src/commands/login.ts
@@ -773,6 +773,55 @@ function normalizePath(input4) {
773
773
  return abs.replace(/\/+$/, "") || "/";
774
774
  }
775
775
 
776
+ // src/lib/project-name.ts
777
+ import { stat as stat2, readFile as readFile7 } from "fs/promises";
778
+ import { basename, dirname as dirname7, join as join2 } from "path";
779
+ async function detectProjectName(cwd) {
780
+ const repoRoot = await findOutermostGitRoot(cwd);
781
+ if (repoRoot) return basename(repoRoot);
782
+ const pkgName = await findNearestPackageName(cwd);
783
+ if (pkgName) return pkgName;
784
+ return basename(cwd) || cwd;
785
+ }
786
+ async function pathExists(p) {
787
+ try {
788
+ await stat2(p);
789
+ return true;
790
+ } catch {
791
+ return false;
792
+ }
793
+ }
794
+ async function findOutermostGitRoot(cwd) {
795
+ let dir = cwd;
796
+ let lastFound = null;
797
+ for (let i = 0; i < 100; i++) {
798
+ if (await pathExists(join2(dir, ".git"))) {
799
+ lastFound = dir;
800
+ }
801
+ const parent = dirname7(dir);
802
+ if (parent === dir) break;
803
+ dir = parent;
804
+ }
805
+ return lastFound;
806
+ }
807
+ async function findNearestPackageName(cwd) {
808
+ let dir = cwd;
809
+ for (let i = 0; i < 100; i++) {
810
+ try {
811
+ const raw = await readFile7(join2(dir, "package.json"), "utf8");
812
+ const parsed = JSON.parse(raw);
813
+ if (typeof parsed.name === "string" && parsed.name.trim() !== "") {
814
+ return parsed.name.trim();
815
+ }
816
+ } catch {
817
+ }
818
+ const parent = dirname7(dir);
819
+ if (parent === dir) break;
820
+ dir = parent;
821
+ }
822
+ return null;
823
+ }
824
+
776
825
  // src/commands/push.ts
777
826
  function isDisabledByEnv() {
778
827
  const v = (process.env.RUNTAPE_DISABLE ?? "").trim().toLowerCase();
@@ -833,6 +882,12 @@ async function pushCommand(opts) {
833
882
  `);
834
883
  return 0;
835
884
  }
885
+ if (result.event.type === "session_start" && cwd) {
886
+ const projectName = await detectProjectName(cwd);
887
+ if (projectName) {
888
+ result.event.project_name = projectName;
889
+ }
890
+ }
836
891
  await appendEvent(sessionId, result.event);
837
892
  if (opts.event === "PostToolUse" || opts.event === "Stop" || opts.event === "SubagentStop") {
838
893
  const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : "";
@@ -951,10 +1006,10 @@ async function pushCommand(opts) {
951
1006
  }
952
1007
 
953
1008
  // src/commands/status.ts
954
- import { readFile as readFile7 } from "fs/promises";
1009
+ import { readFile as readFile8 } from "fs/promises";
955
1010
  async function readFlusherPid() {
956
1011
  try {
957
- const raw = await readFile7(paths.flusherPid, "utf8");
1012
+ const raw = await readFile8(paths.flusherPid, "utf8");
958
1013
  const n = Number.parseInt(raw.trim(), 10);
959
1014
  return Number.isFinite(n) ? n : null;
960
1015
  } catch (err) {
@@ -1245,15 +1300,15 @@ async function watchResetCommand() {
1245
1300
  }
1246
1301
 
1247
1302
  // src/lib/flusher.ts
1248
- import { appendFile as appendFile2, mkdir as mkdir7, readFile as readFile8, unlink as unlink3, writeFile as writeFile7 } from "fs/promises";
1249
- import { dirname as dirname7 } from "path";
1303
+ import { appendFile as appendFile2, mkdir as mkdir7, readFile as readFile9, unlink as unlink3, writeFile as writeFile7 } from "fs/promises";
1304
+ import { dirname as dirname8 } from "path";
1250
1305
  var POLL_INTERVAL_MS = 5e3;
1251
1306
  var IDLE_EXIT_MS = 3e4;
1252
1307
  var BATCH_MAX = 500;
1253
1308
  var BACKOFF_STEPS_MS = [1e3, 2e3, 4e3, 8e3, 16e3, 32e3, 6e4];
1254
1309
  async function log(line) {
1255
1310
  try {
1256
- await mkdir7(dirname7(paths.flusherLog), { recursive: true });
1311
+ await mkdir7(dirname8(paths.flusherLog), { recursive: true });
1257
1312
  await appendFile2(paths.flusherLog, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
1258
1313
  `);
1259
1314
  } catch {
@@ -1270,9 +1325,9 @@ async function isProcessAlive(pid) {
1270
1325
  }
1271
1326
  }
1272
1327
  async function acquirePidLock() {
1273
- await mkdir7(dirname7(paths.flusherPid), { recursive: true });
1328
+ await mkdir7(dirname8(paths.flusherPid), { recursive: true });
1274
1329
  try {
1275
- const existing = await readFile8(paths.flusherPid, "utf8");
1330
+ const existing = await readFile9(paths.flusherPid, "utf8");
1276
1331
  const pid = Number.parseInt(existing.trim(), 10);
1277
1332
  if (Number.isFinite(pid) && await isProcessAlive(pid)) {
1278
1333
  return false;
@@ -1292,6 +1347,38 @@ async function releasePidLock() {
1292
1347
  function delay(ms) {
1293
1348
  return new Promise((resolve3) => setTimeout(resolve3, ms));
1294
1349
  }
1350
+ function isTooBigError(error) {
1351
+ return /too_big/.test(error) || /too many|max(imum)? .*element/i.test(error);
1352
+ }
1353
+ async function flushSliceResilient(serverUrl, apiKey, slice, sessionId) {
1354
+ if (slice.length === 0) return { ok: true, advanced: 0 };
1355
+ const result = await postEvents(serverUrl, apiKey, slice);
1356
+ if (result.ok) return { ok: true, advanced: slice.length };
1357
+ if (result.retryable) {
1358
+ return { ok: false, retryable: true, status: result.status, error: result.error };
1359
+ }
1360
+ if (slice.length === 1) {
1361
+ await log(`drop_poison_single session=${sessionId} status=${result.status} error=${result.error.slice(0, 200)}`);
1362
+ return { ok: false, retryable: false, advanced: 1 };
1363
+ }
1364
+ if (isTooBigError(result.error)) {
1365
+ await log(`bisect_too_big session=${sessionId} size=${slice.length}`);
1366
+ } else {
1367
+ await log(`bisect_poison session=${sessionId} size=${slice.length} error=${result.error.slice(0, 120)}`);
1368
+ }
1369
+ const mid = Math.floor(slice.length / 2);
1370
+ const first = await flushSliceResilient(serverUrl, apiKey, slice.slice(0, mid), sessionId);
1371
+ if (first.ok === false && first.retryable) return first;
1372
+ const advancedFirst = first.ok ? first.advanced : first.advanced;
1373
+ const second = await flushSliceResilient(serverUrl, apiKey, slice.slice(mid), sessionId);
1374
+ if (second.ok === false && second.retryable) {
1375
+ return { ok: false, retryable: false, advanced: advancedFirst };
1376
+ }
1377
+ const advancedSecond = second.ok ? second.advanced : second.advanced;
1378
+ const totalAdvanced = advancedFirst + advancedSecond;
1379
+ if (totalAdvanced === slice.length) return { ok: true, advanced: totalAdvanced };
1380
+ return { ok: false, retryable: false, advanced: totalAdvanced };
1381
+ }
1295
1382
  async function drainSession(sessionId, serverUrl, apiKey) {
1296
1383
  const snapshot = await readBufferedSession(sessionId);
1297
1384
  if (!snapshot || snapshot.events.length === 0) {
@@ -1302,20 +1389,22 @@ async function drainSession(sessionId, serverUrl, apiKey) {
1302
1389
  let anyFlushed = false;
1303
1390
  while (cursor < snapshot.events.length) {
1304
1391
  const slice = snapshot.events.slice(cursor, cursor + BATCH_MAX);
1305
- const result = await postEvents(serverUrl, apiKey, slice);
1306
- if (result.ok) {
1307
- cursor += slice.length;
1392
+ const outcome = await flushSliceResilient(serverUrl, apiKey, slice, sessionId);
1393
+ if (outcome.ok) {
1394
+ cursor += outcome.advanced;
1308
1395
  anyFlushed = true;
1309
1396
  continue;
1310
1397
  }
1311
- if (!result.retryable) {
1312
- await log(`drop_poison session=${sessionId} status=${result.status} error=${result.error.slice(0, 200)}`);
1313
- cursor += slice.length;
1314
- anyFlushed = true;
1315
- continue;
1398
+ if (outcome.retryable) {
1399
+ await log(`retryable session=${sessionId} status=${outcome.status} cursor=${cursor} error=${outcome.error.slice(0, 200)}`);
1400
+ break;
1401
+ }
1402
+ cursor += outcome.advanced;
1403
+ if (outcome.advanced > 0) anyFlushed = true;
1404
+ else {
1405
+ cursor += 1;
1406
+ await log(`drop_zero_advance session=${sessionId}`);
1316
1407
  }
1317
- await log(`retryable session=${sessionId} status=${result.status} cursor=${cursor} error=${result.error.slice(0, 200)}`);
1318
- break;
1319
1408
  }
1320
1409
  const remaining = snapshot.raw.slice(cursor);
1321
1410
  await rewriteBufferedSession(sessionId, remaining);
@@ -1365,7 +1454,7 @@ async function runFlusher() {
1365
1454
  }
1366
1455
 
1367
1456
  // src/index.ts
1368
- var pkgPath = join2(dirname8(fileURLToPath2(import.meta.url)), "..", "package.json");
1457
+ var pkgPath = join3(dirname9(fileURLToPath2(import.meta.url)), "..", "package.json");
1369
1458
  var PKG_VERSION = JSON.parse(readFileSync(pkgPath, "utf8")).version;
1370
1459
  if (process.argv.includes("--internal-flusher")) {
1371
1460
  void runFlusher().then(
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands/login.ts","../src/lib/config.ts","../src/lib/paths.ts","../src/lib/api.ts","../src/commands/logout.ts","../src/commands/install.ts","../src/lib/hooks-installer.ts","../src/lib/hook-mapping.ts","../src/lib/cli-bin.ts","../src/commands/uninstall.ts","../src/commands/push.ts","../src/lib/sequence.ts","../src/lib/buffer.ts","../src/lib/transcript.ts","../src/lib/subagent-transcript.ts","../src/lib/watch.ts","../src/commands/status.ts","../src/commands/runs.ts","../src/commands/setup.ts","../src/commands/watch.ts","../src/lib/flusher.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\nimport { Command } from 'commander';\nimport { loginCommand } from './commands/login.js';\nimport { logoutCommand } from './commands/logout.js';\nimport { installCommand } from './commands/install.js';\nimport { uninstallCommand } from './commands/uninstall.js';\nimport { pushCommand } from './commands/push.js';\nimport { statusCommand } from './commands/status.js';\nimport { runsCommand } from './commands/runs.js';\nimport { setupCommand } from './commands/setup.js';\nimport {\n watchListCommand,\n watchIgnoreCommand,\n watchOnlyCommand,\n watchUnignoreCommand,\n watchResetCommand,\n} from './commands/watch.js';\nimport { runFlusher } from './lib/flusher.js';\n\n// Read package.json at runtime so --version can never desync from the publish.\nconst pkgPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');\nconst PKG_VERSION = (JSON.parse(readFileSync(pkgPath, 'utf8')) as { version: string }).version;\n\n// Internal flag for the detached flusher daemon — not a user-facing subcommand.\n// Goes before commander.parse so we can short-circuit before commander sees argv.\nif (process.argv.includes('--internal-flusher')) {\n void runFlusher().then(\n () => process.exit(0),\n (err: unknown) => {\n console.error(err);\n process.exit(1);\n },\n );\n} else {\n const program = new Command();\n\n program\n .name('runtape')\n .description('Flight recorder for AI coding agents.')\n .version(PKG_VERSION);\n\n program\n .command('setup')\n .description('Guided onboarding: login + install hooks + verify in one flow.')\n .option('--no-browser', 'Do not open the dashboard URL in a browser')\n .action(async (opts) => process.exit(await setupCommand(opts)));\n\n program\n .command('login')\n .description('Paste your API key from runtape.dev/dashboard and save it locally.')\n .option('-k, --key <key>', 'API key (skip the prompt)')\n .option('-s, --server-url <url>', 'Override server URL')\n .action(async (opts) => process.exit(await loginCommand(opts)));\n\n program\n .command('logout')\n .description('Remove saved credentials.')\n .action(async () => process.exit(await logoutCommand()));\n\n program\n .command('install')\n .description('Add Runtape hooks to ~/.claude/settings.json (or project-local with --project).')\n .option('--project', 'Install into ./.claude/settings.json instead of user-level')\n .option('-y, --yes', 'Skip the confirmation prompt')\n .action(async (opts) => process.exit(await installCommand(opts)));\n\n program\n .command('uninstall')\n .description('Remove Runtape hooks from Claude settings.')\n .option('--project', 'Operate on ./.claude/settings.json instead of user-level')\n .action(async (opts) => process.exit(await uninstallCommand(opts)));\n\n program\n .command('push')\n .description('Internal: invoked by Claude Code hooks. Reads stdin and buffers an event.')\n .requiredOption('--event <name>', 'Claude hook event name (SessionStart, PostToolUse, …)')\n .action(async (opts) => process.exit(await pushCommand(opts)));\n\n program\n .command('status')\n .description('Show current login, buffer state, and server reachability.')\n .action(async () => process.exit(await statusCommand()));\n\n program\n .command('runs')\n .description('Open your Runtape dashboard in the default browser.')\n .action(async () => process.exit(await runsCommand()));\n\n const watch = program\n .command('watch')\n .description('Control which directories the hooks capture (allow / deny lists).');\n watch\n .command('list')\n .description('Show the current watch mode and paths.')\n .action(async () => process.exit(await watchListCommand()));\n watch\n .command('ignore <path>')\n .description('Stop capturing sessions whose cwd is under <path>. Switches mode to deny_list.')\n .action(async (path: string) => process.exit(await watchIgnoreCommand(path)));\n watch\n .command('only <path>')\n .description('Capture ONLY sessions whose cwd is under <path>. Switches mode to allow_list.')\n .action(async (path: string) => process.exit(await watchOnlyCommand(path)));\n watch\n .command('unignore <path>')\n .description('Remove <path> from the current watch list.')\n .action(async (path: string) => process.exit(await watchUnignoreCommand(path)));\n watch\n .command('reset')\n .description('Capture every session (clear all watch settings).')\n .action(async () => process.exit(await watchResetCommand()));\n\n program.parseAsync(process.argv).catch((err) => {\n console.error(err);\n process.exit(1);\n });\n}\n","import { createInterface } from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { Config, defaultServerUrl, writeConfig } from '../lib/config.js';\nimport { pingProject } from '../lib/api.js';\n\nexport async function loginCommand(opts: { key?: string; serverUrl?: string }): Promise<number> {\n const serverUrl = opts.serverUrl ?? defaultServerUrl();\n\n let apiKey = opts.key;\n if (!apiKey) {\n const rl = createInterface({ input, output });\n apiKey = (await rl.question('Paste your Runtape API key (rtk_…): ')).trim();\n rl.close();\n }\n\n const validation = Config.shape.api_key.safeParse(apiKey);\n if (!validation.success) {\n process.stderr.write(`Invalid API key format. Expected rtk_<64 hex chars>.\\n`);\n return 2;\n }\n\n process.stdout.write(`Validating against ${serverUrl}…\\n`);\n const ping = await pingProject(serverUrl, apiKey);\n if (!ping.ok) {\n process.stderr.write(`Login failed: ${ping.status === 401 ? 'unknown API key' : ping.detail ?? 'server unreachable'}\\n`);\n return 1;\n }\n\n await writeConfig({ api_key: apiKey, server_url: serverUrl });\n process.stdout.write(`Saved. You can now run: runtape install\\n`);\n return 0;\n}\n","import { chmod, mkdir, readFile, writeFile, unlink } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { z } from 'zod';\nimport { paths } from './paths.js';\n\n// Path watcher: controls which directories get captured by the CLI hooks.\n// allow_all — capture every session (default; equivalent to no `watch` key).\n// deny_list — capture every session EXCEPT cwds matching `paths`.\n// allow_list — capture ONLY cwds matching `paths`.\n// Matching is prefix-based on absolute, normalized paths.\nexport const WatchMode = z.enum(['allow_all', 'deny_list', 'allow_list']);\nexport type WatchMode = z.infer<typeof WatchMode>;\n\nexport const WatchConfig = z.object({\n mode: WatchMode.default('allow_all'),\n paths: z.array(z.string()).default([]),\n});\nexport type WatchConfig = z.infer<typeof WatchConfig>;\n\nexport const Config = z.object({\n api_key: z.string().regex(/^rtk_[a-f0-9]{64}$/, 'api_key must be rtk_ followed by 64 hex chars'),\n server_url: z.string().url(),\n // Optional so any existing 0.5.x config keeps parsing — push.ts treats\n // missing/undefined as allow_all.\n watch: WatchConfig.optional(),\n});\n\nexport type Config = z.infer<typeof Config>;\n\nconst DEFAULT_SERVER_URL = process.env.RUNTAPE_API_URL ?? 'https://runtape.dev';\n\nexport async function readConfig(): Promise<Config | null> {\n let raw: string;\n try {\n raw = await readFile(paths.config, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n const parsed = Config.safeParse(JSON.parse(raw));\n if (!parsed.success) {\n throw new Error(`Invalid config at ${paths.config}: ${parsed.error.message}`);\n }\n return parsed.data;\n}\n\nexport async function writeConfig(c: Config): Promise<void> {\n await mkdir(dirname(paths.config), { recursive: true });\n await writeFile(paths.config, JSON.stringify(c, null, 2) + '\\n', { mode: 0o600 });\n // writeFile's `mode` is only applied on create. Re-chmod explicitly so a re-login\n // tightens permissions if the file was previously loosened (e.g. `chmod 644`).\n await chmod(paths.config, 0o600);\n}\n\nexport async function clearConfig(): Promise<void> {\n try {\n await unlink(paths.config);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw err;\n }\n}\n\nexport function defaultServerUrl(): string {\n return DEFAULT_SERVER_URL;\n}\n","import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst RUNTAPE_HOME = process.env.RUNTAPE_HOME ?? join(homedir(), '.runtape');\n\nexport const paths = {\n home: RUNTAPE_HOME,\n config: join(RUNTAPE_HOME, 'config.json'),\n bufferDir: join(RUNTAPE_HOME, 'buffer'),\n seqDir: join(RUNTAPE_HOME, 'seq'),\n flusherPid: join(RUNTAPE_HOME, 'flusher.pid'),\n flusherLog: join(RUNTAPE_HOME, 'flusher.log'),\n bufferFile: (sessionId: string) => join(RUNTAPE_HOME, 'buffer', `${sessionId}.ndjson`),\n seqFile: (sessionId: string) => join(RUNTAPE_HOME, 'seq', sessionId),\n // Per-session marker recording the last assistant message uuid we've\n // emitted from the transcript. Lets transcript scans be idempotent across\n // hook invocations without re-reading + re-emitting everything.\n transcriptCursorFile: (sessionId: string) => join(RUNTAPE_HOME, 'transcript', sessionId),\n transcriptDir: join(RUNTAPE_HOME, 'transcript'),\n claudeSettings: (scope: 'user' | 'project') =>\n scope === 'user' ? join(homedir(), '.claude', 'settings.json') : join(process.cwd(), '.claude', 'settings.json'),\n claudeSettingsBackup: (scope: 'user' | 'project') =>\n scope === 'user'\n ? join(homedir(), '.claude', 'settings.json.runtape-backup')\n : join(process.cwd(), '.claude', 'settings.json.runtape-backup'),\n};\n","import type { RuntapeEvent } from '../types.js';\n\nexport type PostResult =\n | { ok: true; accepted: number; errors: Array<{ index: number; reason: string }> }\n | { ok: false; status: number; error: string; retryable: boolean };\n\nfunction isRetryableStatus(status: number): boolean {\n // 408 timeout, 425 too-early, 429 rate-limited, 5xx — retry. 4xx — drop (poison).\n return status === 408 || status === 425 || status === 429 || status >= 500;\n}\n\nexport async function postEvents(\n serverUrl: string,\n apiKey: string,\n events: RuntapeEvent[],\n): Promise<PostResult> {\n let response: Response;\n try {\n response = await fetch(`${serverUrl.replace(/\\/$/, '')}/api/v1/events`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ events }),\n });\n } catch (err) {\n // Network failure (ENOTFOUND, ECONNREFUSED, fetch abort). Retryable.\n return {\n ok: false,\n status: 0,\n error: err instanceof Error ? err.message : String(err),\n retryable: true,\n };\n }\n\n if (response.ok) {\n const body = (await response.json()) as {\n accepted: number;\n errors: Array<{ index: number; reason: string }>;\n };\n return { ok: true, accepted: body.accepted, errors: body.errors };\n }\n\n let detail = '';\n try {\n detail = await response.text();\n } catch {\n /* ignore */\n }\n return {\n ok: false,\n status: response.status,\n error: detail || response.statusText,\n retryable: isRetryableStatus(response.status),\n };\n}\n\nexport async function pingProject(serverUrl: string, apiKey: string): Promise<{ ok: boolean; status: number; detail?: string }> {\n // We don't have a dedicated /me endpoint yet (we may add one in Task 3b).\n // For now, \"ping\" = POST /api/v1/events with an empty events array and inspect the response.\n // Server returns 400 invalid_request_body (events must be ≥1) on success — that proves auth works.\n // 401 = bad key; 5xx = server problem.\n let response: Response;\n try {\n response = await fetch(`${serverUrl.replace(/\\/$/, '')}/api/v1/events`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ events: [] }),\n });\n } catch (err) {\n return { ok: false, status: 0, detail: err instanceof Error ? err.message : String(err) };\n }\n\n if (response.status === 400) return { ok: true, status: 400 };\n if (response.status === 401) return { ok: false, status: 401, detail: 'invalid api key' };\n return { ok: false, status: response.status, detail: response.statusText };\n}\n","import { clearConfig } from '../lib/config.js';\n\nexport async function logoutCommand(): Promise<number> {\n await clearConfig();\n process.stdout.write('Logged out. Config removed.\\n');\n return 0;\n}\n","import { createInterface } from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { readConfig } from '../lib/config.js';\nimport { installHooks } from '../lib/hooks-installer.js';\nimport { resolveCliBinPath } from '../lib/cli-bin.js';\n\nexport async function installCommand(opts: { project?: boolean; yes?: boolean }): Promise<number> {\n const cfg = await readConfig();\n if (!cfg) {\n process.stderr.write('Not logged in. Run `runtape login` first.\\n');\n return 1;\n }\n\n const scope: 'user' | 'project' = opts.project ? 'project' : 'user';\n const cliBinPath = resolveCliBinPath();\n\n if (!opts.yes) {\n const rl = createInterface({ input, output });\n const answer = (await rl.question(`Install Runtape hooks into ${scope} settings (yes/no)? `)).trim().toLowerCase();\n rl.close();\n if (answer !== 'y' && answer !== 'yes') {\n process.stdout.write('Aborted.\\n');\n return 0;\n }\n }\n\n const result = await installHooks(scope, cliBinPath);\n process.stdout.write(`Updated ${result.settingsPath}\\n`);\n process.stdout.write(`Backup: ${result.backupPath}\\n`);\n if (result.addedHooks.length === 0) {\n process.stdout.write('Hooks already installed — nothing changed.\\n');\n } else {\n process.stdout.write(`Added: ${result.addedHooks.join(', ')}\\n`);\n }\n return 0;\n}\n","import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { paths } from './paths.js';\nimport { SUPPORTED_HOOKS } from './hook-mapping.js';\n\nconst RUNTAPE_MARKER = 'runtape:managed';\n\ntype HookEntry = { type: 'command'; command: string; [k: string]: unknown };\ntype HookMatcher = { matcher: string; hooks: HookEntry[]; [k: string]: unknown };\ntype HooksBlock = Record<string, HookMatcher[]>;\ntype ClaudeSettings = { hooks?: HooksBlock; [k: string]: unknown };\n\nfunction runtapeEntry(hookName: string, cliBinPath: string): HookEntry {\n return {\n type: 'command',\n command: `${cliBinPath} push --event ${hookName}`,\n [RUNTAPE_MARKER]: true,\n };\n}\n\nasync function readSettings(file: string): Promise<ClaudeSettings> {\n try {\n const raw = await readFile(file, 'utf8');\n return JSON.parse(raw) as ClaudeSettings;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return {};\n throw err;\n }\n}\n\nasync function writeSettings(file: string, data: ClaudeSettings): Promise<void> {\n await mkdir(dirname(file), { recursive: true });\n await writeFile(file, JSON.stringify(data, null, 2) + '\\n');\n}\n\nexport type InstallResult = {\n settingsPath: string;\n backupPath: string;\n addedHooks: string[];\n};\n\nexport async function installHooks(scope: 'user' | 'project', cliBinPath: string): Promise<InstallResult> {\n const settingsPath = paths.claudeSettings(scope);\n const backupPath = paths.claudeSettingsBackup(scope);\n\n // Backup (no-op if settings doesn't exist yet — we still create the dir).\n try {\n await copyFile(settingsPath, backupPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n\n const settings = await readSettings(settingsPath);\n settings.hooks = settings.hooks ?? {};\n\n const added: string[] = [];\n for (const hookName of SUPPORTED_HOOKS) {\n const matchers = (settings.hooks[hookName] = settings.hooks[hookName] ?? []);\n // Find or create a matcher: \"*\". Append our entry there.\n let star = matchers.find((m) => m.matcher === '*');\n if (!star) {\n star = { matcher: '*', hooks: [] };\n matchers.push(star);\n }\n star.hooks = star.hooks ?? [];\n const already = star.hooks.some((h) => (h as HookEntry)[RUNTAPE_MARKER] === true);\n if (!already) {\n star.hooks.push(runtapeEntry(hookName, cliBinPath));\n added.push(hookName);\n }\n }\n\n await writeSettings(settingsPath, settings);\n return { settingsPath, backupPath, addedHooks: added };\n}\n\nexport type UninstallResult = {\n settingsPath: string;\n removedHooks: string[];\n};\n\nexport async function uninstallHooks(scope: 'user' | 'project'): Promise<UninstallResult> {\n const settingsPath = paths.claudeSettings(scope);\n const settings = await readSettings(settingsPath);\n const removed: string[] = [];\n\n if (settings.hooks) {\n for (const hookName of Object.keys(settings.hooks)) {\n const matchers = settings.hooks[hookName];\n for (const matcher of matchers) {\n if (!Array.isArray(matcher.hooks)) continue;\n const before = matcher.hooks.length;\n matcher.hooks = matcher.hooks.filter((h) => (h as HookEntry)[RUNTAPE_MARKER] !== true);\n if (matcher.hooks.length < before) removed.push(hookName);\n }\n // Clean up matchers that are now empty.\n settings.hooks[hookName] = matchers.filter((m) => Array.isArray(m.hooks) && m.hooks.length > 0);\n if (settings.hooks[hookName].length === 0) delete settings.hooks[hookName];\n }\n if (Object.keys(settings.hooks).length === 0) delete settings.hooks;\n }\n\n await writeSettings(settingsPath, settings);\n return { settingsPath, removedHooks: Array.from(new Set(removed)) };\n}\n","import { z } from 'zod';\nimport { RuntapeEvent } from '../types.js';\n\n// The seven Claude Code hook names we register, plus a sentinel \"drop\" for unknown events.\n// Validated 2026-05-14 against Claude Code 2.1.128 — see spike findings spec.\nexport type ClaudeHookName =\n | 'SessionStart'\n | 'UserPromptSubmit'\n | 'PreToolUse'\n | 'PostToolUse'\n | 'Stop'\n | 'SubagentStop'\n | 'SessionEnd'\n | 'Notification';\n\nexport const SUPPORTED_HOOKS: ClaudeHookName[] = [\n 'SessionStart',\n 'UserPromptSubmit',\n 'PreToolUse',\n 'PostToolUse',\n 'Stop',\n 'SubagentStop',\n 'SessionEnd',\n];\n\nexport type MappedEvent = z.infer<typeof RuntapeEvent>;\n\nexport type MapResult =\n | { kind: 'event'; event: MappedEvent }\n | { kind: 'drop'; reason: string };\n\n// PostToolUse tool_response shapes vary by tool. We look at the canonical\n// signals each tool emits and collapse them into a uniform (is_error, message)\n// pair so the server doesn't need per-tool knowledge.\n// - Anthropic-tagged: { is_error: true, content: [{text}] } (the official\n// content-block error form used by some Claude Code tools).\n// - Bash: { interrupted, stdout, stderr, output, ... }. Non-zero exits also\n// surface in stderr and an explicit `exitCode` when available.\n// - Edit/Write/MultiEdit: success returns a structured payload; errors\n// return `{ error: '...' }`.\nfunction inspectToolResponse(tool_response: unknown): { is_error: boolean; error_message?: string } {\n if (!tool_response || typeof tool_response !== 'object') return { is_error: false };\n const r = tool_response as Record<string, unknown>;\n if (r.is_error === true) {\n const content = r.content;\n if (Array.isArray(content)) {\n for (const block of content) {\n if (block && typeof block === 'object' && 'text' in block) {\n const t = (block as { text?: unknown }).text;\n if (typeof t === 'string') return { is_error: true, error_message: t };\n }\n }\n }\n if (typeof r.message === 'string') return { is_error: true, error_message: r.message };\n return { is_error: true };\n }\n if (typeof r.error === 'string' && r.error.trim() !== '') {\n return { is_error: true, error_message: r.error };\n }\n if (r.interrupted === true) {\n return { is_error: true, error_message: 'Interrupted' };\n }\n return { is_error: false };\n}\n\n// Maps a Claude hook payload + the hook name we were invoked with into a RuntapeEvent.\n// Returns { kind: 'drop' } for Notification or unknown events (we just exit cleanly).\nexport function mapHookPayload(\n hookName: string,\n payload: Record<string, unknown>,\n augment: { wall_ts: string; sequence: number },\n): MapResult {\n // Shared envelope every Claude hook carries.\n const base = {\n session_id: payload.session_id,\n transcript_path: payload.transcript_path,\n cwd: payload.cwd,\n hook_event_name: payload.hook_event_name ?? hookName,\n permission_mode: payload.permission_mode,\n wall_ts: augment.wall_ts,\n sequence: augment.sequence,\n };\n\n let candidate: Record<string, unknown>;\n switch (hookName) {\n case 'SessionStart':\n candidate = { ...base, type: 'session_start', source: payload.source ?? 'startup' };\n break;\n case 'UserPromptSubmit':\n candidate = { ...base, type: 'user_prompt', prompt: payload.prompt };\n break;\n case 'PreToolUse':\n candidate = {\n ...base,\n type: 'tool_attempt',\n tool_name: payload.tool_name,\n tool_input: payload.tool_input,\n tool_use_id: payload.tool_use_id,\n };\n break;\n case 'PostToolUse': {\n const err = inspectToolResponse(payload.tool_response);\n candidate = {\n ...base,\n type: 'tool_call',\n tool_name: payload.tool_name,\n tool_input: payload.tool_input,\n tool_response: payload.tool_response,\n tool_use_id: payload.tool_use_id,\n duration_ms: payload.duration_ms,\n is_error: err.is_error,\n error_message: err.error_message,\n };\n break;\n }\n case 'Stop':\n // Despite the event type literal, this is \"turn end\" — Stop fires every\n // time the assistant yields back to the user. Server treats it as\n // 'run is idle'. Real session close uses SessionEnd → 'session_close'.\n candidate = {\n ...base,\n type: 'session_end',\n last_assistant_message: payload.last_assistant_message,\n stop_hook_active: payload.stop_hook_active,\n };\n break;\n case 'SessionEnd':\n candidate = {\n ...base,\n type: 'session_close',\n reason: payload.reason,\n };\n break;\n case 'SubagentStop':\n candidate = {\n ...base,\n type: 'subagent_end',\n agent_id: payload.agent_id,\n agent_type: payload.agent_type,\n agent_transcript_path: payload.agent_transcript_path,\n last_assistant_message: payload.last_assistant_message,\n stop_hook_active: payload.stop_hook_active,\n };\n break;\n default:\n return { kind: 'drop', reason: `unsupported hook: ${hookName}` };\n }\n\n const parsed = RuntapeEvent.safeParse(candidate);\n if (!parsed.success) {\n return { kind: 'drop', reason: `validation failed: ${parsed.error.issues.map((i) => i.path.join('.') + ': ' + i.message).join('; ')}` };\n }\n return { kind: 'event', event: parsed.data };\n}\n","import { fileURLToPath } from 'node:url';\nimport { dirname, resolve, sep } from 'node:path';\n\n// Returns an absolute path to the `runtape` binary the user just invoked.\n// This is what we write into `~/.claude/settings.json` so the hooks call the same install.\n// Falls back to \"runtape\" (on PATH) if we can't resolve it — useful when users want a\n// non-pinned reference and have the CLI installed globally.\nexport function resolveCliBinPath(): string {\n if (process.env.RUNTAPE_CLI_BIN) return process.env.RUNTAPE_CLI_BIN;\n\n const argv1 = process.argv[1];\n if (argv1) {\n // If we live under a node_modules tree, the absolute path embeds the\n // package's extraction location, which changes on `npm install -g <newer>`.\n // Writing that path into ~/.claude/settings.json would break the hook on\n // the next upgrade. Fall back to the bare command name — `runtape` is on\n // PATH after `npm install -g`, and a fresh install re-points the symlink.\n if (argv1.includes(`${sep}node_modules${sep}`)) return 'runtape';\n try {\n return resolve(argv1);\n } catch {\n /* fall through */\n }\n }\n // Last resort: assume \"runtape\" is on PATH.\n return 'runtape';\n}\n\n// Exported for tests — turns a relative path into something we can stick in JSON.\nexport function moduleFileFromImportMeta(metaUrl: string): string {\n return fileURLToPath(metaUrl);\n}\n\n// Suppress unused-import warning for `dirname`.\nvoid dirname;\n","import { uninstallHooks } from '../lib/hooks-installer.js';\n\nexport async function uninstallCommand(opts: { project?: boolean }): Promise<number> {\n const scope: 'user' | 'project' = opts.project ? 'project' : 'user';\n const result = await uninstallHooks(scope);\n if (result.removedHooks.length === 0) {\n process.stdout.write(`No Runtape hooks found in ${result.settingsPath}.\\n`);\n } else {\n process.stdout.write(`Removed Runtape entries from: ${result.removedHooks.join(', ')}\\n`);\n }\n return 0;\n}\n","import { spawn } from 'node:child_process';\nimport { mapHookPayload } from '../lib/hook-mapping.js';\nimport { nextSequence } from '../lib/sequence.js';\nimport { appendEvent } from '../lib/buffer.js';\nimport { readConfig } from '../lib/config.js';\nimport { resolveCliBinPath } from '../lib/cli-bin.js';\nimport { readNewAssistantTurns, persistCursor } from '../lib/transcript.js';\nimport { readNewSubagentEvents, persistSubagentCursor } from '../lib/subagent-transcript.js';\nimport { isCaptureAllowed } from '../lib/watch.js';\nimport type { RuntapeEvent } from '../types.js';\n\nfunction isDisabledByEnv(): boolean {\n // Surgical opt-out for individual sessions:\n // `RUNTAPE_DISABLE=1 claude` skips capture entirely.\n // Accept `1`, `true`, `yes` (case-insensitive) so any reasonable truthy\n // value works. Empty / \"0\" / \"false\" → not disabled.\n const v = (process.env.RUNTAPE_DISABLE ?? '').trim().toLowerCase();\n return v === '1' || v === 'true' || v === 'yes';\n}\n\nasync function readStdin(): Promise<string> {\n if (process.stdin.isTTY) return '';\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n\nfunction spawnFlusher(cliBinPath: string): void {\n // Detached so it survives this process exit. stdio:'ignore' so the daemon\n // doesn't inherit the hook's stdin/stdout (Claude Code reads hook stdout!).\n const child = spawn(cliBinPath, ['--internal-flusher'], {\n detached: true,\n stdio: 'ignore',\n env: process.env,\n });\n child.unref();\n}\n\nexport async function pushCommand(opts: { event: string }): Promise<number> {\n // The contract is: never block the hook. If we can't parse/auth/whatever, exit 0 quietly\n // (printing to stderr is fine; printing to stdout could be parsed by Claude Code).\n try {\n // Earliest opt-out: env var fully bypasses the CLI. Cheap, no I/O.\n if (isDisabledByEnv()) return 0;\n\n const cfg = await readConfig();\n if (!cfg) {\n process.stderr.write('runtape: not logged in — skipping event\\n');\n return 0;\n }\n\n const raw = await readStdin();\n if (!raw.trim()) {\n // SessionStart in some Claude Code versions may invoke hooks with no stdin; ignore.\n return 0;\n }\n\n let payload: Record<string, unknown>;\n try {\n payload = JSON.parse(raw) as Record<string, unknown>;\n } catch (err) {\n process.stderr.write(`runtape: invalid JSON on stdin: ${err instanceof Error ? err.message : String(err)}\\n`);\n return 0;\n }\n\n const sessionId = typeof payload.session_id === 'string' ? payload.session_id : null;\n if (!sessionId) {\n process.stderr.write('runtape: missing session_id on hook payload\\n');\n return 0;\n }\n\n // Watch list gate: if the user has configured allow_list / deny_list and\n // this cwd doesn't pass, silently no-op the hook. Nothing hits the buffer,\n // nothing flushes — the server never sees a byte from this session.\n const cwd = typeof payload.cwd === 'string' ? payload.cwd : '';\n if (cwd && !isCaptureAllowed(cfg.watch, cwd)) {\n return 0;\n }\n\n const sequence = await nextSequence(sessionId);\n const result = mapHookPayload(opts.event, payload, {\n wall_ts: new Date().toISOString(),\n sequence,\n });\n\n if (result.kind === 'drop') {\n // Notification, unknown hook, or validation failure. Quiet log + exit clean.\n process.stderr.write(`runtape: dropped ${opts.event}: ${result.reason}\\n`);\n return 0;\n }\n\n await appendEvent(sessionId, result.event);\n\n // After the primary event lands in the buffer, scan the transcript for any\n // new assistant turns and emit one assistant_turn event per uuid we haven't\n // seen. PostToolUse and Stop are the hooks that follow assistant output;\n // scanning on other hooks is cheap (no new turns) and harmless.\n if (opts.event === 'PostToolUse' || opts.event === 'Stop' || opts.event === 'SubagentStop') {\n const transcriptPath = typeof payload.transcript_path === 'string' ? payload.transcript_path : '';\n if (transcriptPath !== '') {\n try {\n const { turns, seen } = await readNewAssistantTurns(sessionId, transcriptPath);\n const newlyEmitted: string[] = [];\n for (const t of turns) {\n const seq = await nextSequence(sessionId);\n const ev: RuntapeEvent = {\n type: 'assistant_turn',\n session_id: sessionId,\n transcript_path: transcriptPath,\n cwd: typeof payload.cwd === 'string' ? payload.cwd : '',\n hook_event_name: opts.event,\n permission_mode: typeof payload.permission_mode === 'string' ? payload.permission_mode : undefined,\n wall_ts: new Date().toISOString(),\n sequence: seq,\n message_uuid: t.message_uuid,\n model: t.model,\n input_tokens: t.input_tokens,\n output_tokens: t.output_tokens,\n cache_read_tokens: t.cache_read_tokens,\n cache_creation_tokens: t.cache_creation_tokens,\n text: t.text,\n };\n await appendEvent(sessionId, ev);\n newlyEmitted.push(t.message_uuid);\n }\n if (newlyEmitted.length > 0) {\n await persistCursor(sessionId, seen, newlyEmitted);\n }\n } catch (err) {\n // Transcript scan failures must never fail the hook.\n process.stderr.write(`runtape: transcript scan failed: ${err instanceof Error ? err.message : String(err)}\\n`);\n }\n }\n }\n\n // On SubagentStop, ALSO scan the subagent's own transcript and synthesize\n // the full event stream of its internal session — user prompt, assistant\n // turns with model+usage, plus tool_attempt/tool_call pairs. Tag every\n // synthesized event with agent_tool_use_id so the server can resolve the\n // parent Agent step and stamp parent_step_id deterministically.\n if (opts.event === 'SubagentStop') {\n const subTranscript = typeof payload.agent_transcript_path === 'string' ? payload.agent_transcript_path : '';\n const agentToolUseId = typeof payload.agent_id === 'string' ? payload.agent_id : '';\n if (subTranscript !== '' && agentToolUseId !== '') {\n try {\n const { emits, seen } = await readNewSubagentEvents(sessionId, agentToolUseId, subTranscript);\n const newlyEmitted: string[] = [];\n const cwd = typeof payload.cwd === 'string' ? payload.cwd : '';\n for (const e of emits) {\n const seq = await nextSequence(sessionId);\n const baseEnvelope = {\n session_id: sessionId,\n transcript_path: subTranscript,\n cwd,\n hook_event_name: opts.event,\n permission_mode: typeof payload.permission_mode === 'string' ? payload.permission_mode : undefined,\n wall_ts: new Date().toISOString(),\n sequence: seq,\n agent_tool_use_id: agentToolUseId,\n };\n let ev: RuntapeEvent | null = null;\n if (e.kind === 'user_prompt') {\n ev = { ...baseEnvelope, type: 'user_prompt', prompt: e.prompt };\n } else if (e.kind === 'assistant_turn') {\n ev = {\n ...baseEnvelope,\n type: 'assistant_turn',\n message_uuid: e.message_uuid,\n model: e.model,\n input_tokens: e.input_tokens,\n output_tokens: e.output_tokens,\n cache_read_tokens: e.cache_read_tokens,\n cache_creation_tokens: e.cache_creation_tokens,\n text: e.text,\n };\n } else if (e.kind === 'tool_attempt') {\n ev = {\n ...baseEnvelope,\n type: 'tool_attempt',\n tool_name: e.tool_name,\n tool_input: e.tool_input,\n tool_use_id: e.tool_use_id,\n };\n } else if (e.kind === 'tool_call') {\n ev = {\n ...baseEnvelope,\n type: 'tool_call',\n tool_name: e.tool_name,\n tool_input: null,\n tool_response: e.tool_response,\n tool_use_id: e.tool_use_id,\n duration_ms: 0,\n is_error: e.is_error,\n error_message: e.error_message,\n };\n }\n if (ev) {\n await appendEvent(sessionId, ev);\n newlyEmitted.push(e.uuid);\n }\n }\n if (newlyEmitted.length > 0) {\n await persistSubagentCursor(sessionId, agentToolUseId, seen, newlyEmitted);\n }\n } catch (err) {\n process.stderr.write(`runtape: subagent transcript scan failed: ${err instanceof Error ? err.message : String(err)}\\n`);\n }\n }\n }\n\n spawnFlusher(resolveCliBinPath());\n return 0;\n } catch (err) {\n process.stderr.write(`runtape: push error: ${err instanceof Error ? err.message : String(err)}\\n`);\n return 0; // Never fail the hook.\n }\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { paths } from './paths.js';\n\n// Per-session monotonic counter. Persisted as a single integer in seq/<session_id>.\n// Hooks fire one-at-a-time per session (Claude Code waits for the hook to exit before\n// firing the next), so we do NOT need cross-process locking — but we DO need the value\n// to survive across hook invocations, since each `runtape push` is a separate process.\nexport async function nextSequence(sessionId: string): Promise<number> {\n const file = paths.seqFile(sessionId);\n await mkdir(dirname(file), { recursive: true });\n\n let current = 0;\n try {\n const raw = await readFile(file, 'utf8');\n const parsed = Number.parseInt(raw.trim(), 10);\n if (Number.isFinite(parsed) && parsed >= 0) current = parsed;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n\n const next = current + 1;\n await writeFile(file, String(next));\n return next - 1; // First call returns 0 (matching the Zod schema's nonnegative invariant).\n}\n","import { appendFile, mkdir, readFile, readdir, writeFile, unlink, stat } from 'node:fs/promises';\nimport type { RuntapeEvent } from '../types.js';\nimport { paths } from './paths.js';\n\n// Atomic append-as-line. POSIX guarantees a single write() of <PIPE_BUF bytes (≥512) is\n// atomic, and we further constrain ourselves to one line per call. Two concurrent\n// appenders (e.g. two Claude Code instances writing to the same session — impossible\n// today, but cheap defense) cannot interleave a single line.\nexport async function appendEvent(sessionId: string, event: RuntapeEvent): Promise<void> {\n await mkdir(paths.bufferDir, { recursive: true });\n const line = JSON.stringify(event) + '\\n';\n await appendFile(paths.bufferFile(sessionId), line, { encoding: 'utf8' });\n}\n\nexport type BufferedSession = {\n sessionId: string;\n events: RuntapeEvent[];\n raw: string[]; // Raw lines, so we can rewrite exactly what we read on partial-flush.\n};\n\nexport async function listBufferedSessions(): Promise<string[]> {\n let entries: string[];\n try {\n entries = await readdir(paths.bufferDir);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw err;\n }\n return entries.filter((e) => e.endsWith('.ndjson')).map((e) => e.slice(0, -'.ndjson'.length));\n}\n\nexport async function readBufferedSession(sessionId: string): Promise<BufferedSession | null> {\n let raw: string;\n try {\n raw = await readFile(paths.bufferFile(sessionId), 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n const lines = raw.split('\\n').filter((l) => l.length > 0);\n const events: RuntapeEvent[] = [];\n for (const line of lines) {\n try {\n events.push(JSON.parse(line) as RuntapeEvent);\n } catch {\n // Skip malformed lines — they could only get there via a half-written disk; drop them.\n }\n }\n return { sessionId, events, raw: lines };\n}\n\n// Atomic truncate-after-flush: write a temp file with the unflushed remainder, then rename.\n// If `unflushedLines` is empty, delete the buffer file entirely.\nexport async function rewriteBufferedSession(sessionId: string, unflushedLines: string[]): Promise<void> {\n const file = paths.bufferFile(sessionId);\n if (unflushedLines.length === 0) {\n try {\n await unlink(file);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n return;\n }\n const tmp = file + '.tmp';\n await writeFile(tmp, unflushedLines.map((l) => l + '\\n').join(''));\n // rename is atomic within the same filesystem; both paths are in ~/.runtape/buffer.\n const { rename } = await import('node:fs/promises');\n await rename(tmp, file);\n}\n\nexport async function bufferSize(sessionId: string): Promise<number> {\n try {\n const s = await stat(paths.bufferFile(sessionId));\n return s.size;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return 0;\n throw err;\n }\n}\n\nexport function bufferDirPath(): string {\n return paths.bufferDir;\n}\n\n// Used by tests + future GC: stat-mtime in ms for the buffer file.\nexport async function bufferMtimeMs(sessionId: string): Promise<number | null> {\n try {\n const s = await stat(paths.bufferFile(sessionId));\n return s.mtimeMs;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n}\n\n// Re-export for callers that want to know where the file lives without importing paths twice.\nexport function bufferFilePath(sessionId: string): string {\n return paths.bufferFile(sessionId);\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { paths } from './paths.js';\n\n// Claude Code maintains a JSONL transcript at `transcript_path`. Every line is\n// a single message object with at minimum `uuid`, `type` (user|assistant|...)\n// and a `message` payload. Assistant messages additionally carry the model\n// identifier and Anthropic API usage block — the *only* place either datum\n// appears in the hook emit chain.\n//\n// We scan the transcript on every PostToolUse/Stop hook and emit one\n// AssistantTurn event per assistant message whose uuid is not in our seen\n// set. The cursor file persists the seen set across hook invocations.\n//\n// The transcript is append-only within a session so the scan is cheap; we\n// only ever read the file once per hook fire.\n\nexport type TranscriptUsage = {\n message_uuid: string;\n model: string;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n text?: string;\n};\n\ntype AssistantLine = {\n uuid?: unknown;\n type?: unknown;\n message?: {\n role?: unknown;\n model?: unknown;\n content?: unknown;\n usage?: {\n input_tokens?: unknown;\n output_tokens?: unknown;\n cache_read_input_tokens?: unknown;\n cache_creation_input_tokens?: unknown;\n };\n };\n};\n\nfunction asInt(x: unknown): number {\n if (typeof x === 'number' && Number.isFinite(x) && x >= 0) return Math.trunc(x);\n return 0;\n}\n\nfunction extractAssistantText(content: unknown): string | undefined {\n // Anthropic content blocks: [{ type: 'text', text: '...' }, { type: 'tool_use', ... }]\n if (!Array.isArray(content)) return undefined;\n const parts: string[] = [];\n for (const block of content) {\n if (block && typeof block === 'object' && 'type' in block) {\n const b = block as { type: unknown; text?: unknown };\n if (b.type === 'text' && typeof b.text === 'string') parts.push(b.text);\n }\n }\n const joined = parts.join('\\n').trim();\n return joined === '' ? undefined : joined;\n}\n\n// Parses the transcript and returns assistant turns whose uuid is not in\n// `seenUuids`. The seen set is read from the cursor file; the caller is\n// responsible for persisting it after the events have been buffered.\nexport async function readNewAssistantTurns(\n sessionId: string,\n transcriptPath: string,\n): Promise<{ turns: TranscriptUsage[]; seen: Set<string> }> {\n const seen = await readCursor(sessionId);\n\n let raw: string;\n try {\n raw = await readFile(transcriptPath, 'utf8');\n } catch {\n return { turns: [], seen };\n }\n\n const turns: TranscriptUsage[] = [];\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed === '') continue;\n let parsed: AssistantLine;\n try {\n parsed = JSON.parse(trimmed) as AssistantLine;\n } catch {\n // Truncated or partial line at the tail — skip silently. The next hook\n // fire will see the full line once Claude Code has finished writing it.\n continue;\n }\n if (parsed.type !== 'assistant') continue;\n const uuid = typeof parsed.uuid === 'string' ? parsed.uuid : '';\n if (uuid === '' || seen.has(uuid)) continue;\n const msg = parsed.message ?? {};\n const model = typeof msg.model === 'string' ? msg.model : '';\n // Claude Code emits placeholder turns with model \"<synthetic>\" (compaction\n // summaries, internal markers) that always have zero usage. Skip them —\n // they add noise without information.\n if (model === '' || model === '<synthetic>') continue;\n const usage = msg.usage ?? {};\n turns.push({\n message_uuid: uuid,\n model,\n input_tokens: asInt(usage.input_tokens),\n output_tokens: asInt(usage.output_tokens),\n cache_read_tokens: asInt(usage.cache_read_input_tokens),\n cache_creation_tokens: asInt(usage.cache_creation_input_tokens),\n text: extractAssistantText(msg.content),\n });\n }\n\n return { turns, seen };\n}\n\n// Cursor file is a newline-separated list of uuids. Trim to the most recent\n// 200 — the seen set only needs to cover the tail of the transcript that the\n// next hook might re-scan, not the full history.\nconst CURSOR_KEEP = 200;\n\nasync function readCursor(sessionId: string): Promise<Set<string>> {\n try {\n const raw = await readFile(paths.transcriptCursorFile(sessionId), 'utf8');\n return new Set(\n raw\n .split('\\n')\n .map((s) => s.trim())\n .filter((s) => s !== ''),\n );\n } catch {\n return new Set();\n }\n}\n\nexport async function persistCursor(\n sessionId: string,\n seen: Set<string>,\n newlyEmitted: string[],\n): Promise<void> {\n for (const u of newlyEmitted) seen.add(u);\n const file = paths.transcriptCursorFile(sessionId);\n await mkdir(dirname(file), { recursive: true });\n // Keep only the tail so the file doesn't grow unbounded over long sessions.\n const arr = [...seen];\n const trimmed = arr.length > CURSOR_KEEP ? arr.slice(arr.length - CURSOR_KEEP) : arr;\n await writeFile(file, trimmed.join('\\n') + '\\n');\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { paths } from './paths.js';\n\n// The subagent transcript is a separate JSONL Claude Code keeps for each\n// subagent it spawns via the Task tool. Format mirrors the main transcript:\n//\n// { uuid, type: 'user', message: { content: [...] } }\n// { uuid, type: 'assistant', message: { model, content: [...], usage: {...} } }\n//\n// We unpack it into a richer stream than the main transcript reader because\n// the subagent's internal life is what we're trying to expose:\n// - user entries with text content -> user_prompt\n// - user entries with tool_result blks -> tool_call (one per result)\n// - assistant entries -> assistant_turn (model + usage)\n// - tool_use blocks inside assistant -> tool_attempt (one per block)\n//\n// Cursor lives at ~/.runtape/transcript/subagent-<parent_session>-<agent_tool_use_id>\n// and tracks per-uuid emission idempotency.\n\nexport type SubagentEmit =\n | {\n kind: 'user_prompt';\n uuid: string;\n prompt: string;\n }\n | {\n kind: 'assistant_turn';\n uuid: string;\n message_uuid: string;\n model: string;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n text?: string;\n }\n | {\n kind: 'tool_attempt';\n uuid: string;\n tool_use_id: string;\n tool_name: string;\n tool_input: unknown;\n }\n | {\n kind: 'tool_call';\n uuid: string;\n tool_use_id: string;\n tool_name: string;\n tool_response: unknown;\n is_error: boolean;\n error_message?: string;\n };\n\ntype TranscriptLine = {\n uuid?: unknown;\n type?: unknown;\n message?: {\n role?: unknown;\n model?: unknown;\n content?: unknown;\n usage?: {\n input_tokens?: unknown;\n output_tokens?: unknown;\n cache_read_input_tokens?: unknown;\n cache_creation_input_tokens?: unknown;\n };\n };\n};\n\nfunction asInt(x: unknown): number {\n if (typeof x === 'number' && Number.isFinite(x) && x >= 0) return Math.trunc(x);\n return 0;\n}\n\nfunction textFromBlocks(content: unknown): string | undefined {\n if (!Array.isArray(content)) return typeof content === 'string' ? content : undefined;\n const parts: string[] = [];\n for (const block of content) {\n if (block && typeof block === 'object' && 'type' in block) {\n const b = block as { type: unknown; text?: unknown };\n if (b.type === 'text' && typeof b.text === 'string') parts.push(b.text);\n }\n }\n const joined = parts.join('\\n').trim();\n return joined === '' ? undefined : joined;\n}\n\nfunction summarizeToolResult(content: unknown): { is_error: boolean; error_message?: string } {\n if (!Array.isArray(content)) {\n if (typeof content === 'string') return { is_error: false };\n return { is_error: false };\n }\n // Anthropic tool_result blocks can carry their own is_error flag.\n let isError = false;\n const texts: string[] = [];\n for (const block of content) {\n if (block && typeof block === 'object') {\n const b = block as { type?: unknown; text?: unknown; is_error?: unknown };\n if (b.is_error === true) isError = true;\n if (typeof b.text === 'string') texts.push(b.text);\n }\n }\n if (isError && texts.length > 0) return { is_error: true, error_message: texts.join('\\n').slice(0, 500) };\n if (isError) return { is_error: true };\n return { is_error: false };\n}\n\nexport async function readNewSubagentEvents(\n parentSessionId: string,\n agentToolUseId: string,\n transcriptPath: string,\n): Promise<{ emits: SubagentEmit[]; seen: Set<string> }> {\n const seen = await readCursor(parentSessionId, agentToolUseId);\n\n let raw: string;\n try {\n raw = await readFile(transcriptPath, 'utf8');\n } catch {\n return { emits: [], seen };\n }\n\n const emits: SubagentEmit[] = [];\n // Map tool_use_id -> tool_name learned from assistant tool_use blocks so we\n // can echo it back on the corresponding tool_call (the tool_result echo\n // doesn't carry the tool name itself).\n const toolNameByUseId = new Map<string, string>();\n\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed === '') continue;\n let parsed: TranscriptLine;\n try {\n parsed = JSON.parse(trimmed) as TranscriptLine;\n } catch {\n continue;\n }\n const uuid = typeof parsed.uuid === 'string' ? parsed.uuid : '';\n if (uuid === '' || seen.has(uuid)) continue;\n\n const msg = parsed.message ?? {};\n\n if (parsed.type === 'user') {\n // Two flavors of user entries: real user prompts vs. tool_result echoes.\n const content = msg.content;\n const hasToolResults =\n Array.isArray(content) &&\n content.some(\n (b) => b && typeof b === 'object' && (b as { type?: unknown }).type === 'tool_result',\n );\n if (hasToolResults) {\n for (const block of content as unknown[]) {\n if (!block || typeof block !== 'object') continue;\n const b = block as { type?: unknown; tool_use_id?: unknown; content?: unknown };\n if (b.type !== 'tool_result') continue;\n const tuid = typeof b.tool_use_id === 'string' ? b.tool_use_id : '';\n if (tuid === '') continue;\n const { is_error, error_message } = summarizeToolResult(b.content);\n emits.push({\n kind: 'tool_call',\n uuid: `${uuid}:${tuid}`,\n tool_use_id: tuid,\n tool_name: toolNameByUseId.get(tuid) ?? 'unknown',\n tool_response: b.content,\n is_error,\n error_message,\n });\n }\n } else {\n const text = textFromBlocks(content);\n if (text !== undefined) {\n emits.push({ kind: 'user_prompt', uuid, prompt: text });\n }\n }\n continue;\n }\n\n if (parsed.type === 'assistant') {\n const model = typeof msg.model === 'string' ? msg.model : '';\n // Synthetic placeholders carry no useful information.\n if (model === '' || model === '<synthetic>') continue;\n const usage = msg.usage ?? {};\n emits.push({\n kind: 'assistant_turn',\n uuid,\n message_uuid: uuid,\n model,\n input_tokens: asInt(usage.input_tokens),\n output_tokens: asInt(usage.output_tokens),\n cache_read_tokens: asInt(usage.cache_read_input_tokens),\n cache_creation_tokens: asInt(usage.cache_creation_input_tokens),\n text: textFromBlocks(msg.content),\n });\n // Emit one tool_attempt per tool_use block embedded in this assistant\n // message. The matching tool_result will land in the next user entry.\n if (Array.isArray(msg.content)) {\n for (const block of msg.content) {\n if (!block || typeof block !== 'object') continue;\n const b = block as { type?: unknown; id?: unknown; name?: unknown; input?: unknown };\n if (b.type !== 'tool_use') continue;\n const tuid = typeof b.id === 'string' ? b.id : '';\n const tname = typeof b.name === 'string' ? b.name : '';\n if (tuid === '' || tname === '') continue;\n toolNameByUseId.set(tuid, tname);\n emits.push({\n kind: 'tool_attempt',\n uuid: `${uuid}:${tuid}`,\n tool_use_id: tuid,\n tool_name: tname,\n tool_input: b.input,\n });\n }\n }\n continue;\n }\n }\n\n return { emits, seen };\n}\n\nconst CURSOR_KEEP = 500;\n\nasync function readCursor(parentSessionId: string, agentToolUseId: string): Promise<Set<string>> {\n try {\n const raw = await readFile(cursorFile(parentSessionId, agentToolUseId), 'utf8');\n return new Set(\n raw\n .split('\\n')\n .map((s) => s.trim())\n .filter((s) => s !== ''),\n );\n } catch {\n return new Set();\n }\n}\n\nfunction cursorFile(parentSessionId: string, agentToolUseId: string): string {\n // Two-level encoding so different parents and different agent invocations\n // keep independent cursors even if their uuids overlap by accident.\n return `${paths.transcriptDir}/subagent-${parentSessionId}-${agentToolUseId}`;\n}\n\nexport async function persistSubagentCursor(\n parentSessionId: string,\n agentToolUseId: string,\n seen: Set<string>,\n newlyEmitted: string[],\n): Promise<void> {\n for (const u of newlyEmitted) seen.add(u);\n const file = cursorFile(parentSessionId, agentToolUseId);\n await mkdir(dirname(file), { recursive: true });\n const arr = [...seen];\n const trimmed = arr.length > CURSOR_KEEP ? arr.slice(arr.length - CURSOR_KEEP) : arr;\n await writeFile(file, trimmed.join('\\n') + '\\n');\n}\n","import { homedir } from 'node:os';\nimport { resolve } from 'node:path';\nimport type { WatchConfig } from './config.js';\n\n// Pure decision: should this cwd be captured under the given watch config?\n// Returns true when capture should proceed, false when the hook must silently\n// no-op. Mirrors the semantics of allow_list / deny_list / allow_all.\n//\n// Matching is prefix-based on normalized absolute paths. `/Users/me/work`\n// matches both `/Users/me/work` and `/Users/me/work/repo`, but NOT\n// `/Users/me/work-other` (we anchor the prefix on a path separator boundary).\nexport function isCaptureAllowed(watch: WatchConfig | undefined, cwd: string): boolean {\n if (!watch || watch.mode === 'allow_all') return true;\n if (watch.paths.length === 0) {\n // deny_list with no paths => deny nothing (capture). allow_list with no\n // paths => allow nothing (skip). Either way the semantics are intuitive.\n return watch.mode === 'deny_list';\n }\n const cwdNorm = normalizePath(cwd);\n const matched = watch.paths.some((p) => isPrefixMatch(cwdNorm, normalizePath(p)));\n return watch.mode === 'allow_list' ? matched : !matched;\n}\n\nfunction isPrefixMatch(cwd: string, prefix: string): boolean {\n return cwd === prefix || cwd.startsWith(prefix + '/');\n}\n\n// Normalize a path to an absolute form so comparisons don't break on `~`\n// expansion or trailing slashes. Tilde expansion is best-effort: the user's\n// shell expands it before we ever see it in most flows, but `runtape watch\n// ignore ~/foo` is a likely input from a terminal where the shell didn't.\nexport function normalizePath(input: string): string {\n let expanded = input;\n if (expanded === '~') expanded = homedir();\n else if (expanded.startsWith('~/')) expanded = `${homedir()}/${expanded.slice(2)}`;\n const abs = resolve(expanded);\n return abs.replace(/\\/+$/, '') || '/';\n}\n","import { readFile } from 'node:fs/promises';\nimport { readConfig } from '../lib/config.js';\nimport { listBufferedSessions, bufferSize, bufferMtimeMs } from '../lib/buffer.js';\nimport { paths } from '../lib/paths.js';\nimport { pingProject } from '../lib/api.js';\n\nasync function readFlusherPid(): Promise<number | null> {\n try {\n const raw = await readFile(paths.flusherPid, 'utf8');\n const n = Number.parseInt(raw.trim(), 10);\n return Number.isFinite(n) ? n : null;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n}\n\nexport async function statusCommand(): Promise<number> {\n const cfg = await readConfig();\n if (!cfg) {\n process.stdout.write('Not logged in. Run `runtape login`.\\n');\n return 0;\n }\n\n process.stdout.write(`Server: ${cfg.server_url}\\n`);\n process.stdout.write(`API key: ${cfg.api_key.slice(0, 8)}…${cfg.api_key.slice(-4)}\\n`);\n\n const ping = await pingProject(cfg.server_url, cfg.api_key);\n process.stdout.write(`Reachable: ${ping.ok ? 'yes' : `no (${ping.detail ?? ping.status})`}\\n`);\n\n const sessions = await listBufferedSessions();\n if (sessions.length === 0) {\n process.stdout.write('Buffer: empty.\\n');\n } else {\n process.stdout.write(`Buffer: ${sessions.length} session(s) pending.\\n`);\n for (const s of sessions) {\n const size = await bufferSize(s);\n const mtime = await bufferMtimeMs(s);\n const ageSec = mtime ? Math.round((Date.now() - mtime) / 1000) : null;\n process.stdout.write(` ${s}: ${size} bytes${ageSec !== null ? `, updated ${ageSec}s ago` : ''}\\n`);\n }\n }\n\n const flusherPid = await readFlusherPid();\n if (flusherPid !== null) {\n process.stdout.write(`Flusher: PID ${flusherPid}\\n`);\n } else {\n process.stdout.write('Flusher: not running.\\n');\n }\n\n return 0;\n}\n","import { spawn } from 'node:child_process';\nimport { platform } from 'node:process';\nimport { readConfig } from '../lib/config.js';\n\nfunction openCommand(): { cmd: string; args: string[] } {\n // Cross-platform \"open this URL in the default browser\".\n // macOS: `open <url>`. Linux: `xdg-open <url>`. Windows: `cmd /c start \"\" <url>`.\n if (platform === 'darwin') return { cmd: 'open', args: [] };\n if (platform === 'win32') return { cmd: 'cmd', args: ['/c', 'start', ''] };\n return { cmd: 'xdg-open', args: [] };\n}\n\nexport async function runsCommand(): Promise<number> {\n const cfg = await readConfig();\n if (!cfg) {\n process.stderr.write('Not logged in. Run `runtape login` first.\\n');\n return 1;\n }\n\n const url = `${cfg.server_url.replace(/\\/$/, '')}/dashboard/runs`;\n const { cmd, args } = openCommand();\n\n const child = spawn(cmd, [...args, url], { stdio: 'ignore', detached: true });\n child.on('error', (err) => {\n process.stderr.write(`Could not launch browser (${err.message}). Open this manually:\\n${url}\\n`);\n });\n child.unref();\n\n process.stdout.write(`Opening ${url}\\n`);\n return 0;\n}\n","import { spawn } from 'node:child_process';\nimport { platform } from 'node:process';\nimport { createInterface } from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { Config, defaultServerUrl, readConfig, writeConfig } from '../lib/config.js';\nimport { pingProject } from '../lib/api.js';\nimport { installHooks } from '../lib/hooks-installer.js';\nimport { resolveCliBinPath } from '../lib/cli-bin.js';\n\nfunction openCommand(): { cmd: string; args: string[] } {\n if (platform === 'darwin') return { cmd: 'open', args: [] };\n if (platform === 'win32') return { cmd: 'cmd', args: ['/c', 'start', ''] };\n return { cmd: 'xdg-open', args: [] };\n}\n\nfunction openInBrowser(url: string): void {\n const { cmd, args } = openCommand();\n const child = spawn(cmd, [...args, url], { stdio: 'ignore', detached: true });\n child.on('error', () => {\n /* swallow — caller already printed the URL to fall back on */\n });\n child.unref();\n}\n\nasync function promptYesNo(rl: Awaited<ReturnType<typeof createInterface>>, question: string, defaultYes: boolean): Promise<boolean> {\n const suffix = defaultYes ? '(Y/n)' : '(y/N)';\n const answer = (await rl.question(`${question} ${suffix} `)).trim().toLowerCase();\n if (answer === '') return defaultYes;\n return answer === 'y' || answer === 'yes';\n}\n\nexport async function setupCommand(opts: { noBrowser?: boolean }): Promise<number> {\n const rl = createInterface({ input, output });\n\n try {\n process.stdout.write('\\nRuntape setup\\n');\n process.stdout.write('Let\\'s get your Claude Code runs captured.\\n');\n process.stdout.write('(Press Enter at any prompt to accept the default shown in [brackets].)\\n\\n');\n\n // Step 1 — short-circuit if already configured\n const existing = await readConfig();\n if (existing) {\n process.stdout.write(`Already logged in to ${existing.server_url}\\n`);\n process.stdout.write(`API key: ${existing.api_key.slice(0, 8)}…${existing.api_key.slice(-4)}\\n`);\n const reconfigure = await promptYesNo(rl, 'Reconfigure?', false);\n if (!reconfigure) {\n process.stdout.write('\\nKeeping existing credentials. Moving on to hook install.\\n');\n return await installAndFinish(rl, existing.server_url);\n }\n }\n\n // Step 2 — server URL\n const suggestedUrl = defaultServerUrl();\n process.stdout.write(`Step 1/3 — Backend\\n`);\n const urlInput = (await rl.question(`Server URL [${suggestedUrl}] (Enter to use this): `)).trim();\n const serverUrl = urlInput === '' ? suggestedUrl : urlInput;\n\n // Step 3 — open dashboard for the user to grab their key\n const dashboardUrl = `${serverUrl.replace(/\\/$/, '')}/dashboard`;\n process.stdout.write(`\\nStep 2/3 — API key\\n`);\n process.stdout.write(`Your API key lives in the dashboard:\\n ${dashboardUrl}\\n`);\n if (!opts.noBrowser) {\n openInBrowser(dashboardUrl);\n process.stdout.write('(opened in your browser)\\n');\n }\n\n // Step 4 — paste + validate\n const apiKey = (await rl.question('\\nPaste your API key (rtk_…): ')).trim();\n const validation = Config.shape.api_key.safeParse(apiKey);\n if (!validation.success) {\n process.stderr.write(`\\nInvalid API key format. Expected rtk_<64 hex chars>.\\n`);\n return 2;\n }\n\n process.stdout.write(`Validating against ${serverUrl}…\\n`);\n const ping = await pingProject(serverUrl, apiKey);\n if (!ping.ok) {\n process.stderr.write(\n `\\nLogin failed: ${ping.status === 401 ? 'unknown API key' : ping.detail ?? 'server unreachable'}\\n`,\n );\n return 1;\n }\n\n await writeConfig({ api_key: apiKey, server_url: serverUrl });\n process.stdout.write('Credentials saved.\\n');\n\n // Step 5 — install hooks\n return await installAndFinish(rl, serverUrl);\n } finally {\n rl.close();\n }\n}\n\nasync function installAndFinish(\n rl: Awaited<ReturnType<typeof createInterface>>,\n serverUrl: string,\n): Promise<number> {\n process.stdout.write(`\\nStep 3/3 — Claude Code hooks\\n`);\n const install = await promptYesNo(\n rl,\n 'Install Runtape hooks into ~/.claude/settings.json now?',\n true,\n );\n\n if (!install) {\n process.stdout.write('\\nSkipped hook install. Run `runtape install` when ready.\\n');\n return 0;\n }\n\n const cliBinPath = resolveCliBinPath();\n const result = await installHooks('user', cliBinPath);\n process.stdout.write(`Updated ${result.settingsPath}\\n`);\n if (result.addedHooks.length === 0) {\n process.stdout.write('Hooks already installed — nothing changed.\\n');\n } else {\n process.stdout.write(`Added: ${result.addedHooks.join(', ')}\\n`);\n }\n\n process.stdout.write('\\nSetup complete.\\n');\n process.stdout.write('Now run `claude -p \"any prompt\"` from any directory and watch the run land at:\\n');\n process.stdout.write(` ${serverUrl.replace(/\\/$/, '')}/dashboard\\n\\n`);\n return 0;\n}\n","import { readConfig, writeConfig, type Config, type WatchMode } from '../lib/config.js';\nimport { normalizePath } from '../lib/watch.js';\n\n// Subcommand surface for the path-watcher feature. Mutates the CLI config's\n// `watch` block; no server interaction. Every operation reads → mutates →\n// writeConfig() — that re-applies chmod 600 so the credentials stay locked\n// down even if the user touched the file manually between calls.\n\nfunction modeLabel(mode: WatchMode): string {\n if (mode === 'allow_all') return 'allow_all (capturing every session)';\n if (mode === 'allow_list') return 'allow_list (capturing ONLY listed paths)';\n return 'deny_list (capturing every session EXCEPT listed paths)';\n}\n\nasync function readOrExit(): Promise<Config> {\n const cfg = await readConfig();\n if (!cfg) {\n process.stderr.write('Not logged in. Run `runtape login` first.\\n');\n process.exit(1);\n }\n return cfg;\n}\n\nfunction ensureWatch(cfg: Config): { mode: WatchMode; paths: string[] } {\n return cfg.watch\n ? { mode: cfg.watch.mode, paths: [...cfg.watch.paths] }\n : { mode: 'allow_all', paths: [] };\n}\n\nexport async function watchListCommand(): Promise<number> {\n const cfg = await readOrExit();\n const w = ensureWatch(cfg);\n process.stdout.write(`Mode: ${modeLabel(w.mode)}\\n`);\n if (w.paths.length === 0) {\n process.stdout.write('Paths: (none)\\n');\n } else {\n process.stdout.write('Paths:\\n');\n for (const p of w.paths) process.stdout.write(` ${p}\\n`);\n }\n process.stdout.write('\\nEnv override: set RUNTAPE_DISABLE=1 to skip capture for a single session.\\n');\n return 0;\n}\n\nexport async function watchIgnoreCommand(path: string): Promise<number> {\n const cfg = await readOrExit();\n const w = ensureWatch(cfg);\n if (w.mode === 'allow_list') {\n process.stderr.write(\n \"Current mode is allow_list (only listed paths are captured). `ignore` only makes sense in allow_all/deny_list mode.\\nRun `runtape watch reset` to start over, or use `runtape watch only <path>` to add to the allow list.\\n\",\n );\n return 1;\n }\n const normalized = normalizePath(path);\n if (w.paths.includes(normalized)) {\n process.stdout.write(`Already ignored: ${normalized}\\n`);\n return 0;\n }\n w.paths.push(normalized);\n // Auto-switch: first ignore on a fresh allow_all config flips us into deny_list.\n const next: WatchMode = w.mode === 'allow_all' ? 'deny_list' : w.mode;\n await writeConfig({ ...cfg, watch: { mode: next, paths: w.paths } });\n if (next !== w.mode) process.stdout.write(`Mode switched to deny_list.\\n`);\n process.stdout.write(`Ignoring ${normalized}\\n`);\n return 0;\n}\n\nexport async function watchOnlyCommand(path: string): Promise<number> {\n const cfg = await readOrExit();\n const w = ensureWatch(cfg);\n if (w.mode === 'deny_list') {\n process.stderr.write(\n \"Current mode is deny_list (all except listed paths). `only` would conflict.\\nRun `runtape watch reset` to start over, then `runtape watch only <path>` to switch to allow_list.\\n\",\n );\n return 1;\n }\n const normalized = normalizePath(path);\n if (w.paths.includes(normalized)) {\n process.stdout.write(`Already in allow list: ${normalized}\\n`);\n return 0;\n }\n w.paths.push(normalized);\n const next: WatchMode = w.mode === 'allow_all' ? 'allow_list' : w.mode;\n await writeConfig({ ...cfg, watch: { mode: next, paths: w.paths } });\n if (next !== w.mode) process.stdout.write(`Mode switched to allow_list.\\n`);\n process.stdout.write(`Capturing only: ${normalized}\\n`);\n return 0;\n}\n\nexport async function watchUnignoreCommand(path: string): Promise<number> {\n const cfg = await readOrExit();\n const w = ensureWatch(cfg);\n const normalized = normalizePath(path);\n const filtered = w.paths.filter((p) => p !== normalized);\n if (filtered.length === w.paths.length) {\n process.stdout.write(`Not in the list: ${normalized}\\n`);\n return 0;\n }\n await writeConfig({ ...cfg, watch: { mode: w.mode, paths: filtered } });\n process.stdout.write(`Removed: ${normalized}\\n`);\n return 0;\n}\n\nexport async function watchResetCommand(): Promise<number> {\n const cfg = await readOrExit();\n // We strip the watch key entirely rather than persist `{mode: allow_all, paths: []}`\n // so the config file goes back to its 0.5.x shape — useful for users who\n // downgrade or who inspect the file by hand.\n const { watch: _unused, ...rest } = cfg;\n await writeConfig(rest as Config);\n process.stdout.write('Reset to allow_all. All sessions will be captured.\\n');\n return 0;\n}\n","import { appendFile, mkdir, readFile, unlink, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { readConfig } from './config.js';\nimport { listBufferedSessions, readBufferedSession, rewriteBufferedSession } from './buffer.js';\nimport { postEvents } from './api.js';\nimport { paths } from './paths.js';\n\n// Tuned in 0.5.0 to cut PostgREST/Vercel invocation pressure under heavy\n// sessions. A power Claude Code user generates 5-10 events/sec; 500-event\n// batches at a 5s cadence still feel real-time in the dashboard while\n// reducing request count ~5x vs the prior 100-event/1.5s defaults.\nconst POLL_INTERVAL_MS = 5_000;\nconst IDLE_EXIT_MS = 30_000;\nconst BATCH_MAX = 500;\nconst BACKOFF_STEPS_MS = [1000, 2000, 4000, 8000, 16_000, 32_000, 60_000];\n\nasync function log(line: string): Promise<void> {\n try {\n await mkdir(dirname(paths.flusherLog), { recursive: true });\n await appendFile(paths.flusherLog, `${new Date().toISOString()} ${line}\\n`);\n } catch {\n /* never throw out of logging */\n }\n}\n\nasync function isProcessAlive(pid: number): Promise<boolean> {\n try {\n process.kill(pid, 0); // Signal 0 = existence check, doesn't actually signal.\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ESRCH') return false;\n if ((err as NodeJS.ErrnoException).code === 'EPERM') return true; // Exists, owned by someone else.\n return false;\n }\n}\n\n// Acquire-or-detect-running. Returns true if we became the flusher; false if one is already running.\nexport async function acquirePidLock(): Promise<boolean> {\n await mkdir(dirname(paths.flusherPid), { recursive: true });\n try {\n const existing = await readFile(paths.flusherPid, 'utf8');\n const pid = Number.parseInt(existing.trim(), 10);\n if (Number.isFinite(pid) && (await isProcessAlive(pid))) {\n return false; // Another flusher is alive.\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n await writeFile(paths.flusherPid, String(process.pid));\n return true;\n}\n\nasync function releasePidLock(): Promise<void> {\n try {\n await unlink(paths.flusherPid);\n } catch {\n /* ignore */\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// Drain a single session's buffer in batches of up to BATCH_MAX. Returns true if any\n// events were successfully flushed.\nasync function drainSession(sessionId: string, serverUrl: string, apiKey: string): Promise<boolean> {\n const snapshot = await readBufferedSession(sessionId);\n if (!snapshot || snapshot.events.length === 0) {\n await rewriteBufferedSession(sessionId, []);\n return false;\n }\n\n let cursor = 0;\n let anyFlushed = false;\n while (cursor < snapshot.events.length) {\n const slice = snapshot.events.slice(cursor, cursor + BATCH_MAX);\n const result = await postEvents(serverUrl, apiKey, slice);\n\n if (result.ok) {\n cursor += slice.length;\n anyFlushed = true;\n continue;\n }\n\n if (!result.retryable) {\n // Poison batch — log + drop the slice to prevent stuck buffer. 4xx is on us (or stale CLI vs server).\n await log(`drop_poison session=${sessionId} status=${result.status} error=${result.error.slice(0, 200)}`);\n cursor += slice.length;\n anyFlushed = true; // We've made forward progress (toward emptying the buffer), so don't backoff.\n continue;\n }\n\n // Retryable — stop draining this session, leave the rest for next poll cycle.\n await log(`retryable session=${sessionId} status=${result.status} cursor=${cursor} error=${result.error.slice(0, 200)}`);\n break;\n }\n\n const remaining = snapshot.raw.slice(cursor);\n await rewriteBufferedSession(sessionId, remaining);\n return anyFlushed;\n}\n\nexport async function runFlusher(): Promise<void> {\n const acquired = await acquirePidLock();\n if (!acquired) {\n await log('exit_already_running');\n return;\n }\n\n await log(`start pid=${process.pid}`);\n\n let lastActivityMs = Date.now();\n let backoffIdx = 0;\n\n try {\n while (true) {\n const cfg = await readConfig();\n if (!cfg) {\n // No config yet — we shouldn't have been spawned. Exit cleanly.\n await log('exit_no_config');\n return;\n }\n\n const sessions = await listBufferedSessions();\n let flushedThisCycle = false;\n for (const sessionId of sessions) {\n const flushed = await drainSession(sessionId, cfg.server_url, cfg.api_key);\n flushedThisCycle = flushedThisCycle || flushed;\n }\n\n if (flushedThisCycle) {\n lastActivityMs = Date.now();\n backoffIdx = 0;\n }\n\n // Idle exit.\n const remaining = await listBufferedSessions();\n const idleMs = Date.now() - lastActivityMs;\n if (remaining.length === 0 && idleMs >= IDLE_EXIT_MS) {\n await log(`exit_idle idle_ms=${idleMs}`);\n return;\n }\n\n // Backoff when buffer is non-empty but we couldn't drain anything (server down).\n // Reset backoff when we made progress.\n const wait =\n remaining.length > 0 && !flushedThisCycle\n ? BACKOFF_STEPS_MS[Math.min(backoffIdx++, BACKOFF_STEPS_MS.length - 1)]\n : POLL_INTERVAL_MS;\n await delay(wait);\n }\n } catch (err) {\n await log(`crash error=${err instanceof Error ? err.message : String(err)}`);\n throw err;\n } finally {\n await releasePidLock();\n }\n}\n"],"mappings":"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","names":["fileURLToPath","dirname","join","createInterface","input","output","mkdir","readFile","writeFile","dirname","readFile","mkdir","dirname","writeFile","dirname","createInterface","input","output","mkdir","readFile","writeFile","dirname","mkdir","dirname","readFile","writeFile","mkdir","readFile","writeFile","unlink","mkdir","readFile","unlink","writeFile","mkdir","readFile","writeFile","dirname","readFile","mkdir","dirname","writeFile","mkdir","readFile","writeFile","dirname","asInt","readCursor","readFile","CURSOR_KEEP","mkdir","dirname","writeFile","homedir","resolve","input","cwd","readFile","readFile","spawn","spawn","spawn","platform","createInterface","input","output","openCommand","platform","spawn","createInterface","input","output","appendFile","mkdir","readFile","unlink","writeFile","dirname","mkdir","dirname","appendFile","readFile","writeFile","unlink","resolve","join","dirname","fileURLToPath"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/login.ts","../src/lib/config.ts","../src/lib/paths.ts","../src/lib/api.ts","../src/commands/logout.ts","../src/commands/install.ts","../src/lib/hooks-installer.ts","../src/lib/hook-mapping.ts","../src/lib/cli-bin.ts","../src/commands/uninstall.ts","../src/commands/push.ts","../src/lib/sequence.ts","../src/lib/buffer.ts","../src/lib/transcript.ts","../src/lib/subagent-transcript.ts","../src/lib/watch.ts","../src/lib/project-name.ts","../src/commands/status.ts","../src/commands/runs.ts","../src/commands/setup.ts","../src/commands/watch.ts","../src/lib/flusher.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\nimport { Command } from 'commander';\nimport { loginCommand } from './commands/login.js';\nimport { logoutCommand } from './commands/logout.js';\nimport { installCommand } from './commands/install.js';\nimport { uninstallCommand } from './commands/uninstall.js';\nimport { pushCommand } from './commands/push.js';\nimport { statusCommand } from './commands/status.js';\nimport { runsCommand } from './commands/runs.js';\nimport { setupCommand } from './commands/setup.js';\nimport {\n watchListCommand,\n watchIgnoreCommand,\n watchOnlyCommand,\n watchUnignoreCommand,\n watchResetCommand,\n} from './commands/watch.js';\nimport { runFlusher } from './lib/flusher.js';\n\n// Read package.json at runtime so --version can never desync from the publish.\nconst pkgPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');\nconst PKG_VERSION = (JSON.parse(readFileSync(pkgPath, 'utf8')) as { version: string }).version;\n\n// Internal flag for the detached flusher daemon — not a user-facing subcommand.\n// Goes before commander.parse so we can short-circuit before commander sees argv.\nif (process.argv.includes('--internal-flusher')) {\n void runFlusher().then(\n () => process.exit(0),\n (err: unknown) => {\n console.error(err);\n process.exit(1);\n },\n );\n} else {\n const program = new Command();\n\n program\n .name('runtape')\n .description('Flight recorder for AI coding agents.')\n .version(PKG_VERSION);\n\n program\n .command('setup')\n .description('Guided onboarding: login + install hooks + verify in one flow.')\n .option('--no-browser', 'Do not open the dashboard URL in a browser')\n .action(async (opts) => process.exit(await setupCommand(opts)));\n\n program\n .command('login')\n .description('Paste your API key from runtape.dev/dashboard and save it locally.')\n .option('-k, --key <key>', 'API key (skip the prompt)')\n .option('-s, --server-url <url>', 'Override server URL')\n .action(async (opts) => process.exit(await loginCommand(opts)));\n\n program\n .command('logout')\n .description('Remove saved credentials.')\n .action(async () => process.exit(await logoutCommand()));\n\n program\n .command('install')\n .description('Add Runtape hooks to ~/.claude/settings.json (or project-local with --project).')\n .option('--project', 'Install into ./.claude/settings.json instead of user-level')\n .option('-y, --yes', 'Skip the confirmation prompt')\n .action(async (opts) => process.exit(await installCommand(opts)));\n\n program\n .command('uninstall')\n .description('Remove Runtape hooks from Claude settings.')\n .option('--project', 'Operate on ./.claude/settings.json instead of user-level')\n .action(async (opts) => process.exit(await uninstallCommand(opts)));\n\n program\n .command('push')\n .description('Internal: invoked by Claude Code hooks. Reads stdin and buffers an event.')\n .requiredOption('--event <name>', 'Claude hook event name (SessionStart, PostToolUse, …)')\n .action(async (opts) => process.exit(await pushCommand(opts)));\n\n program\n .command('status')\n .description('Show current login, buffer state, and server reachability.')\n .action(async () => process.exit(await statusCommand()));\n\n program\n .command('runs')\n .description('Open your Runtape dashboard in the default browser.')\n .action(async () => process.exit(await runsCommand()));\n\n const watch = program\n .command('watch')\n .description('Control which directories the hooks capture (allow / deny lists).');\n watch\n .command('list')\n .description('Show the current watch mode and paths.')\n .action(async () => process.exit(await watchListCommand()));\n watch\n .command('ignore <path>')\n .description('Stop capturing sessions whose cwd is under <path>. Switches mode to deny_list.')\n .action(async (path: string) => process.exit(await watchIgnoreCommand(path)));\n watch\n .command('only <path>')\n .description('Capture ONLY sessions whose cwd is under <path>. Switches mode to allow_list.')\n .action(async (path: string) => process.exit(await watchOnlyCommand(path)));\n watch\n .command('unignore <path>')\n .description('Remove <path> from the current watch list.')\n .action(async (path: string) => process.exit(await watchUnignoreCommand(path)));\n watch\n .command('reset')\n .description('Capture every session (clear all watch settings).')\n .action(async () => process.exit(await watchResetCommand()));\n\n program.parseAsync(process.argv).catch((err) => {\n console.error(err);\n process.exit(1);\n });\n}\n","import { createInterface } from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { Config, defaultServerUrl, writeConfig } from '../lib/config.js';\nimport { pingProject } from '../lib/api.js';\n\nexport async function loginCommand(opts: { key?: string; serverUrl?: string }): Promise<number> {\n const serverUrl = opts.serverUrl ?? defaultServerUrl();\n\n let apiKey = opts.key;\n if (!apiKey) {\n const rl = createInterface({ input, output });\n apiKey = (await rl.question('Paste your Runtape API key (rtk_…): ')).trim();\n rl.close();\n }\n\n const validation = Config.shape.api_key.safeParse(apiKey);\n if (!validation.success) {\n process.stderr.write(`Invalid API key format. Expected rtk_<64 hex chars>.\\n`);\n return 2;\n }\n\n process.stdout.write(`Validating against ${serverUrl}…\\n`);\n const ping = await pingProject(serverUrl, apiKey);\n if (!ping.ok) {\n process.stderr.write(`Login failed: ${ping.status === 401 ? 'unknown API key' : ping.detail ?? 'server unreachable'}\\n`);\n return 1;\n }\n\n await writeConfig({ api_key: apiKey, server_url: serverUrl });\n process.stdout.write(`Saved. You can now run: runtape install\\n`);\n return 0;\n}\n","import { chmod, mkdir, readFile, writeFile, unlink } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { z } from 'zod';\nimport { paths } from './paths.js';\n\n// Path watcher: controls which directories get captured by the CLI hooks.\n// allow_all — capture every session (default; equivalent to no `watch` key).\n// deny_list — capture every session EXCEPT cwds matching `paths`.\n// allow_list — capture ONLY cwds matching `paths`.\n// Matching is prefix-based on absolute, normalized paths.\nexport const WatchMode = z.enum(['allow_all', 'deny_list', 'allow_list']);\nexport type WatchMode = z.infer<typeof WatchMode>;\n\nexport const WatchConfig = z.object({\n mode: WatchMode.default('allow_all'),\n paths: z.array(z.string()).default([]),\n});\nexport type WatchConfig = z.infer<typeof WatchConfig>;\n\nexport const Config = z.object({\n api_key: z.string().regex(/^rtk_[a-f0-9]{64}$/, 'api_key must be rtk_ followed by 64 hex chars'),\n server_url: z.string().url(),\n // Optional so any existing 0.5.x config keeps parsing — push.ts treats\n // missing/undefined as allow_all.\n watch: WatchConfig.optional(),\n});\n\nexport type Config = z.infer<typeof Config>;\n\nconst DEFAULT_SERVER_URL = process.env.RUNTAPE_API_URL ?? 'https://runtape.dev';\n\nexport async function readConfig(): Promise<Config | null> {\n let raw: string;\n try {\n raw = await readFile(paths.config, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n const parsed = Config.safeParse(JSON.parse(raw));\n if (!parsed.success) {\n throw new Error(`Invalid config at ${paths.config}: ${parsed.error.message}`);\n }\n return parsed.data;\n}\n\nexport async function writeConfig(c: Config): Promise<void> {\n await mkdir(dirname(paths.config), { recursive: true });\n await writeFile(paths.config, JSON.stringify(c, null, 2) + '\\n', { mode: 0o600 });\n // writeFile's `mode` is only applied on create. Re-chmod explicitly so a re-login\n // tightens permissions if the file was previously loosened (e.g. `chmod 644`).\n await chmod(paths.config, 0o600);\n}\n\nexport async function clearConfig(): Promise<void> {\n try {\n await unlink(paths.config);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw err;\n }\n}\n\nexport function defaultServerUrl(): string {\n return DEFAULT_SERVER_URL;\n}\n","import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst RUNTAPE_HOME = process.env.RUNTAPE_HOME ?? join(homedir(), '.runtape');\n\nexport const paths = {\n home: RUNTAPE_HOME,\n config: join(RUNTAPE_HOME, 'config.json'),\n bufferDir: join(RUNTAPE_HOME, 'buffer'),\n seqDir: join(RUNTAPE_HOME, 'seq'),\n flusherPid: join(RUNTAPE_HOME, 'flusher.pid'),\n flusherLog: join(RUNTAPE_HOME, 'flusher.log'),\n bufferFile: (sessionId: string) => join(RUNTAPE_HOME, 'buffer', `${sessionId}.ndjson`),\n seqFile: (sessionId: string) => join(RUNTAPE_HOME, 'seq', sessionId),\n // Per-session marker recording the last assistant message uuid we've\n // emitted from the transcript. Lets transcript scans be idempotent across\n // hook invocations without re-reading + re-emitting everything.\n transcriptCursorFile: (sessionId: string) => join(RUNTAPE_HOME, 'transcript', sessionId),\n transcriptDir: join(RUNTAPE_HOME, 'transcript'),\n claudeSettings: (scope: 'user' | 'project') =>\n scope === 'user' ? join(homedir(), '.claude', 'settings.json') : join(process.cwd(), '.claude', 'settings.json'),\n claudeSettingsBackup: (scope: 'user' | 'project') =>\n scope === 'user'\n ? join(homedir(), '.claude', 'settings.json.runtape-backup')\n : join(process.cwd(), '.claude', 'settings.json.runtape-backup'),\n};\n","import type { RuntapeEvent } from '../types.js';\n\nexport type PostResult =\n | { ok: true; accepted: number; errors: Array<{ index: number; reason: string }> }\n | { ok: false; status: number; error: string; retryable: boolean };\n\nfunction isRetryableStatus(status: number): boolean {\n // 408 timeout, 425 too-early, 429 rate-limited, 5xx — retry. 4xx — drop (poison).\n return status === 408 || status === 425 || status === 429 || status >= 500;\n}\n\nexport async function postEvents(\n serverUrl: string,\n apiKey: string,\n events: RuntapeEvent[],\n): Promise<PostResult> {\n let response: Response;\n try {\n response = await fetch(`${serverUrl.replace(/\\/$/, '')}/api/v1/events`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ events }),\n });\n } catch (err) {\n // Network failure (ENOTFOUND, ECONNREFUSED, fetch abort). Retryable.\n return {\n ok: false,\n status: 0,\n error: err instanceof Error ? err.message : String(err),\n retryable: true,\n };\n }\n\n if (response.ok) {\n const body = (await response.json()) as {\n accepted: number;\n errors: Array<{ index: number; reason: string }>;\n };\n return { ok: true, accepted: body.accepted, errors: body.errors };\n }\n\n let detail = '';\n try {\n detail = await response.text();\n } catch {\n /* ignore */\n }\n return {\n ok: false,\n status: response.status,\n error: detail || response.statusText,\n retryable: isRetryableStatus(response.status),\n };\n}\n\nexport async function pingProject(serverUrl: string, apiKey: string): Promise<{ ok: boolean; status: number; detail?: string }> {\n // We don't have a dedicated /me endpoint yet (we may add one in Task 3b).\n // For now, \"ping\" = POST /api/v1/events with an empty events array and inspect the response.\n // Server returns 400 invalid_request_body (events must be ≥1) on success — that proves auth works.\n // 401 = bad key; 5xx = server problem.\n let response: Response;\n try {\n response = await fetch(`${serverUrl.replace(/\\/$/, '')}/api/v1/events`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ events: [] }),\n });\n } catch (err) {\n return { ok: false, status: 0, detail: err instanceof Error ? err.message : String(err) };\n }\n\n if (response.status === 400) return { ok: true, status: 400 };\n if (response.status === 401) return { ok: false, status: 401, detail: 'invalid api key' };\n return { ok: false, status: response.status, detail: response.statusText };\n}\n","import { clearConfig } from '../lib/config.js';\n\nexport async function logoutCommand(): Promise<number> {\n await clearConfig();\n process.stdout.write('Logged out. Config removed.\\n');\n return 0;\n}\n","import { createInterface } from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { readConfig } from '../lib/config.js';\nimport { installHooks } from '../lib/hooks-installer.js';\nimport { resolveCliBinPath } from '../lib/cli-bin.js';\n\nexport async function installCommand(opts: { project?: boolean; yes?: boolean }): Promise<number> {\n const cfg = await readConfig();\n if (!cfg) {\n process.stderr.write('Not logged in. Run `runtape login` first.\\n');\n return 1;\n }\n\n const scope: 'user' | 'project' = opts.project ? 'project' : 'user';\n const cliBinPath = resolveCliBinPath();\n\n if (!opts.yes) {\n const rl = createInterface({ input, output });\n const answer = (await rl.question(`Install Runtape hooks into ${scope} settings (yes/no)? `)).trim().toLowerCase();\n rl.close();\n if (answer !== 'y' && answer !== 'yes') {\n process.stdout.write('Aborted.\\n');\n return 0;\n }\n }\n\n const result = await installHooks(scope, cliBinPath);\n process.stdout.write(`Updated ${result.settingsPath}\\n`);\n process.stdout.write(`Backup: ${result.backupPath}\\n`);\n if (result.addedHooks.length === 0) {\n process.stdout.write('Hooks already installed — nothing changed.\\n');\n } else {\n process.stdout.write(`Added: ${result.addedHooks.join(', ')}\\n`);\n }\n return 0;\n}\n","import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { paths } from './paths.js';\nimport { SUPPORTED_HOOKS } from './hook-mapping.js';\n\nconst RUNTAPE_MARKER = 'runtape:managed';\n\ntype HookEntry = { type: 'command'; command: string; [k: string]: unknown };\ntype HookMatcher = { matcher: string; hooks: HookEntry[]; [k: string]: unknown };\ntype HooksBlock = Record<string, HookMatcher[]>;\ntype ClaudeSettings = { hooks?: HooksBlock; [k: string]: unknown };\n\nfunction runtapeEntry(hookName: string, cliBinPath: string): HookEntry {\n return {\n type: 'command',\n command: `${cliBinPath} push --event ${hookName}`,\n [RUNTAPE_MARKER]: true,\n };\n}\n\nasync function readSettings(file: string): Promise<ClaudeSettings> {\n try {\n const raw = await readFile(file, 'utf8');\n return JSON.parse(raw) as ClaudeSettings;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return {};\n throw err;\n }\n}\n\nasync function writeSettings(file: string, data: ClaudeSettings): Promise<void> {\n await mkdir(dirname(file), { recursive: true });\n await writeFile(file, JSON.stringify(data, null, 2) + '\\n');\n}\n\nexport type InstallResult = {\n settingsPath: string;\n backupPath: string;\n addedHooks: string[];\n};\n\nexport async function installHooks(scope: 'user' | 'project', cliBinPath: string): Promise<InstallResult> {\n const settingsPath = paths.claudeSettings(scope);\n const backupPath = paths.claudeSettingsBackup(scope);\n\n // Backup (no-op if settings doesn't exist yet — we still create the dir).\n try {\n await copyFile(settingsPath, backupPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n\n const settings = await readSettings(settingsPath);\n settings.hooks = settings.hooks ?? {};\n\n const added: string[] = [];\n for (const hookName of SUPPORTED_HOOKS) {\n const matchers = (settings.hooks[hookName] = settings.hooks[hookName] ?? []);\n // Find or create a matcher: \"*\". Append our entry there.\n let star = matchers.find((m) => m.matcher === '*');\n if (!star) {\n star = { matcher: '*', hooks: [] };\n matchers.push(star);\n }\n star.hooks = star.hooks ?? [];\n const already = star.hooks.some((h) => (h as HookEntry)[RUNTAPE_MARKER] === true);\n if (!already) {\n star.hooks.push(runtapeEntry(hookName, cliBinPath));\n added.push(hookName);\n }\n }\n\n await writeSettings(settingsPath, settings);\n return { settingsPath, backupPath, addedHooks: added };\n}\n\nexport type UninstallResult = {\n settingsPath: string;\n removedHooks: string[];\n};\n\nexport async function uninstallHooks(scope: 'user' | 'project'): Promise<UninstallResult> {\n const settingsPath = paths.claudeSettings(scope);\n const settings = await readSettings(settingsPath);\n const removed: string[] = [];\n\n if (settings.hooks) {\n for (const hookName of Object.keys(settings.hooks)) {\n const matchers = settings.hooks[hookName];\n for (const matcher of matchers) {\n if (!Array.isArray(matcher.hooks)) continue;\n const before = matcher.hooks.length;\n matcher.hooks = matcher.hooks.filter((h) => (h as HookEntry)[RUNTAPE_MARKER] !== true);\n if (matcher.hooks.length < before) removed.push(hookName);\n }\n // Clean up matchers that are now empty.\n settings.hooks[hookName] = matchers.filter((m) => Array.isArray(m.hooks) && m.hooks.length > 0);\n if (settings.hooks[hookName].length === 0) delete settings.hooks[hookName];\n }\n if (Object.keys(settings.hooks).length === 0) delete settings.hooks;\n }\n\n await writeSettings(settingsPath, settings);\n return { settingsPath, removedHooks: Array.from(new Set(removed)) };\n}\n","import { z } from 'zod';\nimport { RuntapeEvent } from '../types.js';\n\n// The seven Claude Code hook names we register, plus a sentinel \"drop\" for unknown events.\n// Validated 2026-05-14 against Claude Code 2.1.128 — see spike findings spec.\nexport type ClaudeHookName =\n | 'SessionStart'\n | 'UserPromptSubmit'\n | 'PreToolUse'\n | 'PostToolUse'\n | 'Stop'\n | 'SubagentStop'\n | 'SessionEnd'\n | 'Notification';\n\nexport const SUPPORTED_HOOKS: ClaudeHookName[] = [\n 'SessionStart',\n 'UserPromptSubmit',\n 'PreToolUse',\n 'PostToolUse',\n 'Stop',\n 'SubagentStop',\n 'SessionEnd',\n];\n\nexport type MappedEvent = z.infer<typeof RuntapeEvent>;\n\nexport type MapResult =\n | { kind: 'event'; event: MappedEvent }\n | { kind: 'drop'; reason: string };\n\n// PostToolUse tool_response shapes vary by tool. We look at the canonical\n// signals each tool emits and collapse them into a uniform (is_error, message)\n// pair so the server doesn't need per-tool knowledge.\n// - Anthropic-tagged: { is_error: true, content: [{text}] } (the official\n// content-block error form used by some Claude Code tools).\n// - Bash: { interrupted, stdout, stderr, output, ... }. Non-zero exits also\n// surface in stderr and an explicit `exitCode` when available.\n// - Edit/Write/MultiEdit: success returns a structured payload; errors\n// return `{ error: '...' }`.\nfunction inspectToolResponse(tool_response: unknown): { is_error: boolean; error_message?: string } {\n if (!tool_response || typeof tool_response !== 'object') return { is_error: false };\n const r = tool_response as Record<string, unknown>;\n if (r.is_error === true) {\n const content = r.content;\n if (Array.isArray(content)) {\n for (const block of content) {\n if (block && typeof block === 'object' && 'text' in block) {\n const t = (block as { text?: unknown }).text;\n if (typeof t === 'string') return { is_error: true, error_message: t };\n }\n }\n }\n if (typeof r.message === 'string') return { is_error: true, error_message: r.message };\n return { is_error: true };\n }\n if (typeof r.error === 'string' && r.error.trim() !== '') {\n return { is_error: true, error_message: r.error };\n }\n if (r.interrupted === true) {\n return { is_error: true, error_message: 'Interrupted' };\n }\n return { is_error: false };\n}\n\n// Maps a Claude hook payload + the hook name we were invoked with into a RuntapeEvent.\n// Returns { kind: 'drop' } for Notification or unknown events (we just exit cleanly).\nexport function mapHookPayload(\n hookName: string,\n payload: Record<string, unknown>,\n augment: { wall_ts: string; sequence: number },\n): MapResult {\n // Shared envelope every Claude hook carries.\n const base = {\n session_id: payload.session_id,\n transcript_path: payload.transcript_path,\n cwd: payload.cwd,\n hook_event_name: payload.hook_event_name ?? hookName,\n permission_mode: payload.permission_mode,\n wall_ts: augment.wall_ts,\n sequence: augment.sequence,\n };\n\n let candidate: Record<string, unknown>;\n switch (hookName) {\n case 'SessionStart':\n candidate = { ...base, type: 'session_start', source: payload.source ?? 'startup' };\n break;\n case 'UserPromptSubmit':\n candidate = { ...base, type: 'user_prompt', prompt: payload.prompt };\n break;\n case 'PreToolUse':\n candidate = {\n ...base,\n type: 'tool_attempt',\n tool_name: payload.tool_name,\n tool_input: payload.tool_input,\n tool_use_id: payload.tool_use_id,\n };\n break;\n case 'PostToolUse': {\n const err = inspectToolResponse(payload.tool_response);\n candidate = {\n ...base,\n type: 'tool_call',\n tool_name: payload.tool_name,\n tool_input: payload.tool_input,\n tool_response: payload.tool_response,\n tool_use_id: payload.tool_use_id,\n duration_ms: payload.duration_ms,\n is_error: err.is_error,\n error_message: err.error_message,\n };\n break;\n }\n case 'Stop':\n // Despite the event type literal, this is \"turn end\" — Stop fires every\n // time the assistant yields back to the user. Server treats it as\n // 'run is idle'. Real session close uses SessionEnd → 'session_close'.\n candidate = {\n ...base,\n type: 'session_end',\n last_assistant_message: payload.last_assistant_message,\n stop_hook_active: payload.stop_hook_active,\n };\n break;\n case 'SessionEnd':\n candidate = {\n ...base,\n type: 'session_close',\n reason: payload.reason,\n };\n break;\n case 'SubagentStop':\n candidate = {\n ...base,\n type: 'subagent_end',\n agent_id: payload.agent_id,\n agent_type: payload.agent_type,\n agent_transcript_path: payload.agent_transcript_path,\n last_assistant_message: payload.last_assistant_message,\n stop_hook_active: payload.stop_hook_active,\n };\n break;\n default:\n return { kind: 'drop', reason: `unsupported hook: ${hookName}` };\n }\n\n const parsed = RuntapeEvent.safeParse(candidate);\n if (!parsed.success) {\n return { kind: 'drop', reason: `validation failed: ${parsed.error.issues.map((i) => i.path.join('.') + ': ' + i.message).join('; ')}` };\n }\n return { kind: 'event', event: parsed.data };\n}\n","import { fileURLToPath } from 'node:url';\nimport { dirname, resolve, sep } from 'node:path';\n\n// Returns an absolute path to the `runtape` binary the user just invoked.\n// This is what we write into `~/.claude/settings.json` so the hooks call the same install.\n// Falls back to \"runtape\" (on PATH) if we can't resolve it — useful when users want a\n// non-pinned reference and have the CLI installed globally.\nexport function resolveCliBinPath(): string {\n if (process.env.RUNTAPE_CLI_BIN) return process.env.RUNTAPE_CLI_BIN;\n\n const argv1 = process.argv[1];\n if (argv1) {\n // If we live under a node_modules tree, the absolute path embeds the\n // package's extraction location, which changes on `npm install -g <newer>`.\n // Writing that path into ~/.claude/settings.json would break the hook on\n // the next upgrade. Fall back to the bare command name — `runtape` is on\n // PATH after `npm install -g`, and a fresh install re-points the symlink.\n if (argv1.includes(`${sep}node_modules${sep}`)) return 'runtape';\n try {\n return resolve(argv1);\n } catch {\n /* fall through */\n }\n }\n // Last resort: assume \"runtape\" is on PATH.\n return 'runtape';\n}\n\n// Exported for tests — turns a relative path into something we can stick in JSON.\nexport function moduleFileFromImportMeta(metaUrl: string): string {\n return fileURLToPath(metaUrl);\n}\n\n// Suppress unused-import warning for `dirname`.\nvoid dirname;\n","import { uninstallHooks } from '../lib/hooks-installer.js';\n\nexport async function uninstallCommand(opts: { project?: boolean }): Promise<number> {\n const scope: 'user' | 'project' = opts.project ? 'project' : 'user';\n const result = await uninstallHooks(scope);\n if (result.removedHooks.length === 0) {\n process.stdout.write(`No Runtape hooks found in ${result.settingsPath}.\\n`);\n } else {\n process.stdout.write(`Removed Runtape entries from: ${result.removedHooks.join(', ')}\\n`);\n }\n return 0;\n}\n","import { spawn } from 'node:child_process';\nimport { mapHookPayload } from '../lib/hook-mapping.js';\nimport { nextSequence } from '../lib/sequence.js';\nimport { appendEvent } from '../lib/buffer.js';\nimport { readConfig } from '../lib/config.js';\nimport { resolveCliBinPath } from '../lib/cli-bin.js';\nimport { readNewAssistantTurns, persistCursor } from '../lib/transcript.js';\nimport { readNewSubagentEvents, persistSubagentCursor } from '../lib/subagent-transcript.js';\nimport { isCaptureAllowed } from '../lib/watch.js';\nimport { detectProjectName } from '../lib/project-name.js';\nimport type { RuntapeEvent } from '../types.js';\n\nfunction isDisabledByEnv(): boolean {\n // Surgical opt-out for individual sessions:\n // `RUNTAPE_DISABLE=1 claude` skips capture entirely.\n // Accept `1`, `true`, `yes` (case-insensitive) so any reasonable truthy\n // value works. Empty / \"0\" / \"false\" → not disabled.\n const v = (process.env.RUNTAPE_DISABLE ?? '').trim().toLowerCase();\n return v === '1' || v === 'true' || v === 'yes';\n}\n\nasync function readStdin(): Promise<string> {\n if (process.stdin.isTTY) return '';\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n\nfunction spawnFlusher(cliBinPath: string): void {\n // Detached so it survives this process exit. stdio:'ignore' so the daemon\n // doesn't inherit the hook's stdin/stdout (Claude Code reads hook stdout!).\n const child = spawn(cliBinPath, ['--internal-flusher'], {\n detached: true,\n stdio: 'ignore',\n env: process.env,\n });\n child.unref();\n}\n\nexport async function pushCommand(opts: { event: string }): Promise<number> {\n // The contract is: never block the hook. If we can't parse/auth/whatever, exit 0 quietly\n // (printing to stderr is fine; printing to stdout could be parsed by Claude Code).\n try {\n // Earliest opt-out: env var fully bypasses the CLI. Cheap, no I/O.\n if (isDisabledByEnv()) return 0;\n\n const cfg = await readConfig();\n if (!cfg) {\n process.stderr.write('runtape: not logged in — skipping event\\n');\n return 0;\n }\n\n const raw = await readStdin();\n if (!raw.trim()) {\n // SessionStart in some Claude Code versions may invoke hooks with no stdin; ignore.\n return 0;\n }\n\n let payload: Record<string, unknown>;\n try {\n payload = JSON.parse(raw) as Record<string, unknown>;\n } catch (err) {\n process.stderr.write(`runtape: invalid JSON on stdin: ${err instanceof Error ? err.message : String(err)}\\n`);\n return 0;\n }\n\n const sessionId = typeof payload.session_id === 'string' ? payload.session_id : null;\n if (!sessionId) {\n process.stderr.write('runtape: missing session_id on hook payload\\n');\n return 0;\n }\n\n // Watch list gate: if the user has configured allow_list / deny_list and\n // this cwd doesn't pass, silently no-op the hook. Nothing hits the buffer,\n // nothing flushes — the server never sees a byte from this session.\n const cwd = typeof payload.cwd === 'string' ? payload.cwd : '';\n if (cwd && !isCaptureAllowed(cfg.watch, cwd)) {\n return 0;\n }\n\n const sequence = await nextSequence(sessionId);\n const result = mapHookPayload(opts.event, payload, {\n wall_ts: new Date().toISOString(),\n sequence,\n });\n\n if (result.kind === 'drop') {\n // Notification, unknown hook, or validation failure. Quiet log + exit clean.\n process.stderr.write(`runtape: dropped ${opts.event}: ${result.reason}\\n`);\n return 0;\n }\n\n // Enrich SessionStart with a best-effort project label derived from cwd.\n // Done here (async) rather than inside the sync mapHookPayload so the\n // mapper stays pure. detectProjectName never throws — falls back to the\n // basename of cwd — so we don't need to wrap in try/catch.\n if (result.event.type === 'session_start' && cwd) {\n const projectName = await detectProjectName(cwd);\n if (projectName) {\n result.event.project_name = projectName;\n }\n }\n\n await appendEvent(sessionId, result.event);\n\n // After the primary event lands in the buffer, scan the transcript for any\n // new assistant turns and emit one assistant_turn event per uuid we haven't\n // seen. PostToolUse and Stop are the hooks that follow assistant output;\n // scanning on other hooks is cheap (no new turns) and harmless.\n if (opts.event === 'PostToolUse' || opts.event === 'Stop' || opts.event === 'SubagentStop') {\n const transcriptPath = typeof payload.transcript_path === 'string' ? payload.transcript_path : '';\n if (transcriptPath !== '') {\n try {\n const { turns, seen } = await readNewAssistantTurns(sessionId, transcriptPath);\n const newlyEmitted: string[] = [];\n for (const t of turns) {\n const seq = await nextSequence(sessionId);\n const ev: RuntapeEvent = {\n type: 'assistant_turn',\n session_id: sessionId,\n transcript_path: transcriptPath,\n cwd: typeof payload.cwd === 'string' ? payload.cwd : '',\n hook_event_name: opts.event,\n permission_mode: typeof payload.permission_mode === 'string' ? payload.permission_mode : undefined,\n wall_ts: new Date().toISOString(),\n sequence: seq,\n message_uuid: t.message_uuid,\n model: t.model,\n input_tokens: t.input_tokens,\n output_tokens: t.output_tokens,\n cache_read_tokens: t.cache_read_tokens,\n cache_creation_tokens: t.cache_creation_tokens,\n text: t.text,\n };\n await appendEvent(sessionId, ev);\n newlyEmitted.push(t.message_uuid);\n }\n if (newlyEmitted.length > 0) {\n await persistCursor(sessionId, seen, newlyEmitted);\n }\n } catch (err) {\n // Transcript scan failures must never fail the hook.\n process.stderr.write(`runtape: transcript scan failed: ${err instanceof Error ? err.message : String(err)}\\n`);\n }\n }\n }\n\n // On SubagentStop, ALSO scan the subagent's own transcript and synthesize\n // the full event stream of its internal session — user prompt, assistant\n // turns with model+usage, plus tool_attempt/tool_call pairs. Tag every\n // synthesized event with agent_tool_use_id so the server can resolve the\n // parent Agent step and stamp parent_step_id deterministically.\n if (opts.event === 'SubagentStop') {\n const subTranscript = typeof payload.agent_transcript_path === 'string' ? payload.agent_transcript_path : '';\n const agentToolUseId = typeof payload.agent_id === 'string' ? payload.agent_id : '';\n if (subTranscript !== '' && agentToolUseId !== '') {\n try {\n const { emits, seen } = await readNewSubagentEvents(sessionId, agentToolUseId, subTranscript);\n const newlyEmitted: string[] = [];\n const cwd = typeof payload.cwd === 'string' ? payload.cwd : '';\n for (const e of emits) {\n const seq = await nextSequence(sessionId);\n const baseEnvelope = {\n session_id: sessionId,\n transcript_path: subTranscript,\n cwd,\n hook_event_name: opts.event,\n permission_mode: typeof payload.permission_mode === 'string' ? payload.permission_mode : undefined,\n wall_ts: new Date().toISOString(),\n sequence: seq,\n agent_tool_use_id: agentToolUseId,\n };\n let ev: RuntapeEvent | null = null;\n if (e.kind === 'user_prompt') {\n ev = { ...baseEnvelope, type: 'user_prompt', prompt: e.prompt };\n } else if (e.kind === 'assistant_turn') {\n ev = {\n ...baseEnvelope,\n type: 'assistant_turn',\n message_uuid: e.message_uuid,\n model: e.model,\n input_tokens: e.input_tokens,\n output_tokens: e.output_tokens,\n cache_read_tokens: e.cache_read_tokens,\n cache_creation_tokens: e.cache_creation_tokens,\n text: e.text,\n };\n } else if (e.kind === 'tool_attempt') {\n ev = {\n ...baseEnvelope,\n type: 'tool_attempt',\n tool_name: e.tool_name,\n tool_input: e.tool_input,\n tool_use_id: e.tool_use_id,\n };\n } else if (e.kind === 'tool_call') {\n ev = {\n ...baseEnvelope,\n type: 'tool_call',\n tool_name: e.tool_name,\n tool_input: null,\n tool_response: e.tool_response,\n tool_use_id: e.tool_use_id,\n duration_ms: 0,\n is_error: e.is_error,\n error_message: e.error_message,\n };\n }\n if (ev) {\n await appendEvent(sessionId, ev);\n newlyEmitted.push(e.uuid);\n }\n }\n if (newlyEmitted.length > 0) {\n await persistSubagentCursor(sessionId, agentToolUseId, seen, newlyEmitted);\n }\n } catch (err) {\n process.stderr.write(`runtape: subagent transcript scan failed: ${err instanceof Error ? err.message : String(err)}\\n`);\n }\n }\n }\n\n spawnFlusher(resolveCliBinPath());\n return 0;\n } catch (err) {\n process.stderr.write(`runtape: push error: ${err instanceof Error ? err.message : String(err)}\\n`);\n return 0; // Never fail the hook.\n }\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { paths } from './paths.js';\n\n// Per-session monotonic counter. Persisted as a single integer in seq/<session_id>.\n// Hooks fire one-at-a-time per session (Claude Code waits for the hook to exit before\n// firing the next), so we do NOT need cross-process locking — but we DO need the value\n// to survive across hook invocations, since each `runtape push` is a separate process.\nexport async function nextSequence(sessionId: string): Promise<number> {\n const file = paths.seqFile(sessionId);\n await mkdir(dirname(file), { recursive: true });\n\n let current = 0;\n try {\n const raw = await readFile(file, 'utf8');\n const parsed = Number.parseInt(raw.trim(), 10);\n if (Number.isFinite(parsed) && parsed >= 0) current = parsed;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n\n const next = current + 1;\n await writeFile(file, String(next));\n return next - 1; // First call returns 0 (matching the Zod schema's nonnegative invariant).\n}\n","import { appendFile, mkdir, readFile, readdir, writeFile, unlink, stat } from 'node:fs/promises';\nimport type { RuntapeEvent } from '../types.js';\nimport { paths } from './paths.js';\n\n// Atomic append-as-line. POSIX guarantees a single write() of <PIPE_BUF bytes (≥512) is\n// atomic, and we further constrain ourselves to one line per call. Two concurrent\n// appenders (e.g. two Claude Code instances writing to the same session — impossible\n// today, but cheap defense) cannot interleave a single line.\nexport async function appendEvent(sessionId: string, event: RuntapeEvent): Promise<void> {\n await mkdir(paths.bufferDir, { recursive: true });\n const line = JSON.stringify(event) + '\\n';\n await appendFile(paths.bufferFile(sessionId), line, { encoding: 'utf8' });\n}\n\nexport type BufferedSession = {\n sessionId: string;\n events: RuntapeEvent[];\n raw: string[]; // Raw lines, so we can rewrite exactly what we read on partial-flush.\n};\n\nexport async function listBufferedSessions(): Promise<string[]> {\n let entries: string[];\n try {\n entries = await readdir(paths.bufferDir);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw err;\n }\n return entries.filter((e) => e.endsWith('.ndjson')).map((e) => e.slice(0, -'.ndjson'.length));\n}\n\nexport async function readBufferedSession(sessionId: string): Promise<BufferedSession | null> {\n let raw: string;\n try {\n raw = await readFile(paths.bufferFile(sessionId), 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n const lines = raw.split('\\n').filter((l) => l.length > 0);\n const events: RuntapeEvent[] = [];\n for (const line of lines) {\n try {\n events.push(JSON.parse(line) as RuntapeEvent);\n } catch {\n // Skip malformed lines — they could only get there via a half-written disk; drop them.\n }\n }\n return { sessionId, events, raw: lines };\n}\n\n// Atomic truncate-after-flush: write a temp file with the unflushed remainder, then rename.\n// If `unflushedLines` is empty, delete the buffer file entirely.\nexport async function rewriteBufferedSession(sessionId: string, unflushedLines: string[]): Promise<void> {\n const file = paths.bufferFile(sessionId);\n if (unflushedLines.length === 0) {\n try {\n await unlink(file);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n return;\n }\n const tmp = file + '.tmp';\n await writeFile(tmp, unflushedLines.map((l) => l + '\\n').join(''));\n // rename is atomic within the same filesystem; both paths are in ~/.runtape/buffer.\n const { rename } = await import('node:fs/promises');\n await rename(tmp, file);\n}\n\nexport async function bufferSize(sessionId: string): Promise<number> {\n try {\n const s = await stat(paths.bufferFile(sessionId));\n return s.size;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return 0;\n throw err;\n }\n}\n\nexport function bufferDirPath(): string {\n return paths.bufferDir;\n}\n\n// Used by tests + future GC: stat-mtime in ms for the buffer file.\nexport async function bufferMtimeMs(sessionId: string): Promise<number | null> {\n try {\n const s = await stat(paths.bufferFile(sessionId));\n return s.mtimeMs;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n}\n\n// Re-export for callers that want to know where the file lives without importing paths twice.\nexport function bufferFilePath(sessionId: string): string {\n return paths.bufferFile(sessionId);\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { paths } from './paths.js';\n\n// Claude Code maintains a JSONL transcript at `transcript_path`. Every line is\n// a single message object with at minimum `uuid`, `type` (user|assistant|...)\n// and a `message` payload. Assistant messages additionally carry the model\n// identifier and Anthropic API usage block — the *only* place either datum\n// appears in the hook emit chain.\n//\n// We scan the transcript on every PostToolUse/Stop hook and emit one\n// AssistantTurn event per assistant message whose uuid is not in our seen\n// set. The cursor file persists the seen set across hook invocations.\n//\n// The transcript is append-only within a session so the scan is cheap; we\n// only ever read the file once per hook fire.\n\nexport type TranscriptUsage = {\n message_uuid: string;\n model: string;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n text?: string;\n};\n\ntype AssistantLine = {\n uuid?: unknown;\n type?: unknown;\n message?: {\n role?: unknown;\n model?: unknown;\n content?: unknown;\n usage?: {\n input_tokens?: unknown;\n output_tokens?: unknown;\n cache_read_input_tokens?: unknown;\n cache_creation_input_tokens?: unknown;\n };\n };\n};\n\nfunction asInt(x: unknown): number {\n if (typeof x === 'number' && Number.isFinite(x) && x >= 0) return Math.trunc(x);\n return 0;\n}\n\nfunction extractAssistantText(content: unknown): string | undefined {\n // Anthropic content blocks: [{ type: 'text', text: '...' }, { type: 'tool_use', ... }]\n if (!Array.isArray(content)) return undefined;\n const parts: string[] = [];\n for (const block of content) {\n if (block && typeof block === 'object' && 'type' in block) {\n const b = block as { type: unknown; text?: unknown };\n if (b.type === 'text' && typeof b.text === 'string') parts.push(b.text);\n }\n }\n const joined = parts.join('\\n').trim();\n return joined === '' ? undefined : joined;\n}\n\n// Parses the transcript and returns assistant turns whose uuid is not in\n// `seenUuids`. The seen set is read from the cursor file; the caller is\n// responsible for persisting it after the events have been buffered.\nexport async function readNewAssistantTurns(\n sessionId: string,\n transcriptPath: string,\n): Promise<{ turns: TranscriptUsage[]; seen: Set<string> }> {\n const seen = await readCursor(sessionId);\n\n let raw: string;\n try {\n raw = await readFile(transcriptPath, 'utf8');\n } catch {\n return { turns: [], seen };\n }\n\n const turns: TranscriptUsage[] = [];\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed === '') continue;\n let parsed: AssistantLine;\n try {\n parsed = JSON.parse(trimmed) as AssistantLine;\n } catch {\n // Truncated or partial line at the tail — skip silently. The next hook\n // fire will see the full line once Claude Code has finished writing it.\n continue;\n }\n if (parsed.type !== 'assistant') continue;\n const uuid = typeof parsed.uuid === 'string' ? parsed.uuid : '';\n if (uuid === '' || seen.has(uuid)) continue;\n const msg = parsed.message ?? {};\n const model = typeof msg.model === 'string' ? msg.model : '';\n // Claude Code emits placeholder turns with model \"<synthetic>\" (compaction\n // summaries, internal markers) that always have zero usage. Skip them —\n // they add noise without information.\n if (model === '' || model === '<synthetic>') continue;\n const usage = msg.usage ?? {};\n turns.push({\n message_uuid: uuid,\n model,\n input_tokens: asInt(usage.input_tokens),\n output_tokens: asInt(usage.output_tokens),\n cache_read_tokens: asInt(usage.cache_read_input_tokens),\n cache_creation_tokens: asInt(usage.cache_creation_input_tokens),\n text: extractAssistantText(msg.content),\n });\n }\n\n return { turns, seen };\n}\n\n// Cursor file is a newline-separated list of uuids. Trim to the most recent\n// 200 — the seen set only needs to cover the tail of the transcript that the\n// next hook might re-scan, not the full history.\nconst CURSOR_KEEP = 200;\n\nasync function readCursor(sessionId: string): Promise<Set<string>> {\n try {\n const raw = await readFile(paths.transcriptCursorFile(sessionId), 'utf8');\n return new Set(\n raw\n .split('\\n')\n .map((s) => s.trim())\n .filter((s) => s !== ''),\n );\n } catch {\n return new Set();\n }\n}\n\nexport async function persistCursor(\n sessionId: string,\n seen: Set<string>,\n newlyEmitted: string[],\n): Promise<void> {\n for (const u of newlyEmitted) seen.add(u);\n const file = paths.transcriptCursorFile(sessionId);\n await mkdir(dirname(file), { recursive: true });\n // Keep only the tail so the file doesn't grow unbounded over long sessions.\n const arr = [...seen];\n const trimmed = arr.length > CURSOR_KEEP ? arr.slice(arr.length - CURSOR_KEEP) : arr;\n await writeFile(file, trimmed.join('\\n') + '\\n');\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { paths } from './paths.js';\n\n// The subagent transcript is a separate JSONL Claude Code keeps for each\n// subagent it spawns via the Task tool. Format mirrors the main transcript:\n//\n// { uuid, type: 'user', message: { content: [...] } }\n// { uuid, type: 'assistant', message: { model, content: [...], usage: {...} } }\n//\n// We unpack it into a richer stream than the main transcript reader because\n// the subagent's internal life is what we're trying to expose:\n// - user entries with text content -> user_prompt\n// - user entries with tool_result blks -> tool_call (one per result)\n// - assistant entries -> assistant_turn (model + usage)\n// - tool_use blocks inside assistant -> tool_attempt (one per block)\n//\n// Cursor lives at ~/.runtape/transcript/subagent-<parent_session>-<agent_tool_use_id>\n// and tracks per-uuid emission idempotency.\n\nexport type SubagentEmit =\n | {\n kind: 'user_prompt';\n uuid: string;\n prompt: string;\n }\n | {\n kind: 'assistant_turn';\n uuid: string;\n message_uuid: string;\n model: string;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n text?: string;\n }\n | {\n kind: 'tool_attempt';\n uuid: string;\n tool_use_id: string;\n tool_name: string;\n tool_input: unknown;\n }\n | {\n kind: 'tool_call';\n uuid: string;\n tool_use_id: string;\n tool_name: string;\n tool_response: unknown;\n is_error: boolean;\n error_message?: string;\n };\n\ntype TranscriptLine = {\n uuid?: unknown;\n type?: unknown;\n message?: {\n role?: unknown;\n model?: unknown;\n content?: unknown;\n usage?: {\n input_tokens?: unknown;\n output_tokens?: unknown;\n cache_read_input_tokens?: unknown;\n cache_creation_input_tokens?: unknown;\n };\n };\n};\n\nfunction asInt(x: unknown): number {\n if (typeof x === 'number' && Number.isFinite(x) && x >= 0) return Math.trunc(x);\n return 0;\n}\n\nfunction textFromBlocks(content: unknown): string | undefined {\n if (!Array.isArray(content)) return typeof content === 'string' ? content : undefined;\n const parts: string[] = [];\n for (const block of content) {\n if (block && typeof block === 'object' && 'type' in block) {\n const b = block as { type: unknown; text?: unknown };\n if (b.type === 'text' && typeof b.text === 'string') parts.push(b.text);\n }\n }\n const joined = parts.join('\\n').trim();\n return joined === '' ? undefined : joined;\n}\n\nfunction summarizeToolResult(content: unknown): { is_error: boolean; error_message?: string } {\n if (!Array.isArray(content)) {\n if (typeof content === 'string') return { is_error: false };\n return { is_error: false };\n }\n // Anthropic tool_result blocks can carry their own is_error flag.\n let isError = false;\n const texts: string[] = [];\n for (const block of content) {\n if (block && typeof block === 'object') {\n const b = block as { type?: unknown; text?: unknown; is_error?: unknown };\n if (b.is_error === true) isError = true;\n if (typeof b.text === 'string') texts.push(b.text);\n }\n }\n if (isError && texts.length > 0) return { is_error: true, error_message: texts.join('\\n').slice(0, 500) };\n if (isError) return { is_error: true };\n return { is_error: false };\n}\n\nexport async function readNewSubagentEvents(\n parentSessionId: string,\n agentToolUseId: string,\n transcriptPath: string,\n): Promise<{ emits: SubagentEmit[]; seen: Set<string> }> {\n const seen = await readCursor(parentSessionId, agentToolUseId);\n\n let raw: string;\n try {\n raw = await readFile(transcriptPath, 'utf8');\n } catch {\n return { emits: [], seen };\n }\n\n const emits: SubagentEmit[] = [];\n // Map tool_use_id -> tool_name learned from assistant tool_use blocks so we\n // can echo it back on the corresponding tool_call (the tool_result echo\n // doesn't carry the tool name itself).\n const toolNameByUseId = new Map<string, string>();\n\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed === '') continue;\n let parsed: TranscriptLine;\n try {\n parsed = JSON.parse(trimmed) as TranscriptLine;\n } catch {\n continue;\n }\n const uuid = typeof parsed.uuid === 'string' ? parsed.uuid : '';\n if (uuid === '' || seen.has(uuid)) continue;\n\n const msg = parsed.message ?? {};\n\n if (parsed.type === 'user') {\n // Two flavors of user entries: real user prompts vs. tool_result echoes.\n const content = msg.content;\n const hasToolResults =\n Array.isArray(content) &&\n content.some(\n (b) => b && typeof b === 'object' && (b as { type?: unknown }).type === 'tool_result',\n );\n if (hasToolResults) {\n for (const block of content as unknown[]) {\n if (!block || typeof block !== 'object') continue;\n const b = block as { type?: unknown; tool_use_id?: unknown; content?: unknown };\n if (b.type !== 'tool_result') continue;\n const tuid = typeof b.tool_use_id === 'string' ? b.tool_use_id : '';\n if (tuid === '') continue;\n const { is_error, error_message } = summarizeToolResult(b.content);\n emits.push({\n kind: 'tool_call',\n uuid: `${uuid}:${tuid}`,\n tool_use_id: tuid,\n tool_name: toolNameByUseId.get(tuid) ?? 'unknown',\n tool_response: b.content,\n is_error,\n error_message,\n });\n }\n } else {\n const text = textFromBlocks(content);\n if (text !== undefined) {\n emits.push({ kind: 'user_prompt', uuid, prompt: text });\n }\n }\n continue;\n }\n\n if (parsed.type === 'assistant') {\n const model = typeof msg.model === 'string' ? msg.model : '';\n // Synthetic placeholders carry no useful information.\n if (model === '' || model === '<synthetic>') continue;\n const usage = msg.usage ?? {};\n emits.push({\n kind: 'assistant_turn',\n uuid,\n message_uuid: uuid,\n model,\n input_tokens: asInt(usage.input_tokens),\n output_tokens: asInt(usage.output_tokens),\n cache_read_tokens: asInt(usage.cache_read_input_tokens),\n cache_creation_tokens: asInt(usage.cache_creation_input_tokens),\n text: textFromBlocks(msg.content),\n });\n // Emit one tool_attempt per tool_use block embedded in this assistant\n // message. The matching tool_result will land in the next user entry.\n if (Array.isArray(msg.content)) {\n for (const block of msg.content) {\n if (!block || typeof block !== 'object') continue;\n const b = block as { type?: unknown; id?: unknown; name?: unknown; input?: unknown };\n if (b.type !== 'tool_use') continue;\n const tuid = typeof b.id === 'string' ? b.id : '';\n const tname = typeof b.name === 'string' ? b.name : '';\n if (tuid === '' || tname === '') continue;\n toolNameByUseId.set(tuid, tname);\n emits.push({\n kind: 'tool_attempt',\n uuid: `${uuid}:${tuid}`,\n tool_use_id: tuid,\n tool_name: tname,\n tool_input: b.input,\n });\n }\n }\n continue;\n }\n }\n\n return { emits, seen };\n}\n\nconst CURSOR_KEEP = 500;\n\nasync function readCursor(parentSessionId: string, agentToolUseId: string): Promise<Set<string>> {\n try {\n const raw = await readFile(cursorFile(parentSessionId, agentToolUseId), 'utf8');\n return new Set(\n raw\n .split('\\n')\n .map((s) => s.trim())\n .filter((s) => s !== ''),\n );\n } catch {\n return new Set();\n }\n}\n\nfunction cursorFile(parentSessionId: string, agentToolUseId: string): string {\n // Two-level encoding so different parents and different agent invocations\n // keep independent cursors even if their uuids overlap by accident.\n return `${paths.transcriptDir}/subagent-${parentSessionId}-${agentToolUseId}`;\n}\n\nexport async function persistSubagentCursor(\n parentSessionId: string,\n agentToolUseId: string,\n seen: Set<string>,\n newlyEmitted: string[],\n): Promise<void> {\n for (const u of newlyEmitted) seen.add(u);\n const file = cursorFile(parentSessionId, agentToolUseId);\n await mkdir(dirname(file), { recursive: true });\n const arr = [...seen];\n const trimmed = arr.length > CURSOR_KEEP ? arr.slice(arr.length - CURSOR_KEEP) : arr;\n await writeFile(file, trimmed.join('\\n') + '\\n');\n}\n","import { homedir } from 'node:os';\nimport { resolve } from 'node:path';\nimport type { WatchConfig } from './config.js';\n\n// Pure decision: should this cwd be captured under the given watch config?\n// Returns true when capture should proceed, false when the hook must silently\n// no-op. Mirrors the semantics of allow_list / deny_list / allow_all.\n//\n// Matching is prefix-based on normalized absolute paths. `/Users/me/work`\n// matches both `/Users/me/work` and `/Users/me/work/repo`, but NOT\n// `/Users/me/work-other` (we anchor the prefix on a path separator boundary).\nexport function isCaptureAllowed(watch: WatchConfig | undefined, cwd: string): boolean {\n if (!watch || watch.mode === 'allow_all') return true;\n if (watch.paths.length === 0) {\n // deny_list with no paths => deny nothing (capture). allow_list with no\n // paths => allow nothing (skip). Either way the semantics are intuitive.\n return watch.mode === 'deny_list';\n }\n const cwdNorm = normalizePath(cwd);\n const matched = watch.paths.some((p) => isPrefixMatch(cwdNorm, normalizePath(p)));\n return watch.mode === 'allow_list' ? matched : !matched;\n}\n\nfunction isPrefixMatch(cwd: string, prefix: string): boolean {\n return cwd === prefix || cwd.startsWith(prefix + '/');\n}\n\n// Normalize a path to an absolute form so comparisons don't break on `~`\n// expansion or trailing slashes. Tilde expansion is best-effort: the user's\n// shell expands it before we ever see it in most flows, but `runtape watch\n// ignore ~/foo` is a likely input from a terminal where the shell didn't.\nexport function normalizePath(input: string): string {\n let expanded = input;\n if (expanded === '~') expanded = homedir();\n else if (expanded.startsWith('~/')) expanded = `${homedir()}/${expanded.slice(2)}`;\n const abs = resolve(expanded);\n return abs.replace(/\\/+$/, '') || '/';\n}\n","import { stat, readFile } from 'node:fs/promises';\nimport { basename, dirname, join } from 'node:path';\n\n// Best-effort label for the project the user is working on. Resolution order:\n// 1. Outermost git repo root (basename of the dir containing `.git`).\n// Matches the user mental model \"I'm working in <repo>\" even when\n// Claude Code is launched from a sub-package of a monorepo.\n// 2. Nearest ancestor with a `package.json` that has a non-empty `name`.\n// Catches Node projects that aren't in a git repo yet.\n// 3. basename(cwd) — final fallback, never null.\n//\n// Returns the raw string with no transformation (scoped names like\n// `@runtape/web` come through as-is).\nexport async function detectProjectName(cwd: string): Promise<string> {\n const repoRoot = await findOutermostGitRoot(cwd);\n if (repoRoot) return basename(repoRoot);\n\n const pkgName = await findNearestPackageName(cwd);\n if (pkgName) return pkgName;\n\n return basename(cwd) || cwd;\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\n// Walk from cwd to filesystem root, return the OUTERMOST directory that\n// contains a `.git` entry. That gives the actual repo root in a nested-repo\n// layout (rare but valid: submodules, .git directories in subfolders).\nasync function findOutermostGitRoot(cwd: string): Promise<string | null> {\n let dir = cwd;\n let lastFound: string | null = null;\n // Hard ceiling so a broken filesystem can't loop us forever.\n for (let i = 0; i < 100; i++) {\n if (await pathExists(join(dir, '.git'))) {\n lastFound = dir;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return lastFound;\n}\n\n// Walk from cwd to filesystem root, return the FIRST package.json whose\n// `name` field is a non-empty string.\nasync function findNearestPackageName(cwd: string): Promise<string | null> {\n let dir = cwd;\n for (let i = 0; i < 100; i++) {\n try {\n const raw = await readFile(join(dir, 'package.json'), 'utf8');\n const parsed = JSON.parse(raw) as { name?: unknown };\n if (typeof parsed.name === 'string' && parsed.name.trim() !== '') {\n return parsed.name.trim();\n }\n } catch {\n /* keep walking */\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n","import { readFile } from 'node:fs/promises';\nimport { readConfig } from '../lib/config.js';\nimport { listBufferedSessions, bufferSize, bufferMtimeMs } from '../lib/buffer.js';\nimport { paths } from '../lib/paths.js';\nimport { pingProject } from '../lib/api.js';\n\nasync function readFlusherPid(): Promise<number | null> {\n try {\n const raw = await readFile(paths.flusherPid, 'utf8');\n const n = Number.parseInt(raw.trim(), 10);\n return Number.isFinite(n) ? n : null;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n}\n\nexport async function statusCommand(): Promise<number> {\n const cfg = await readConfig();\n if (!cfg) {\n process.stdout.write('Not logged in. Run `runtape login`.\\n');\n return 0;\n }\n\n process.stdout.write(`Server: ${cfg.server_url}\\n`);\n process.stdout.write(`API key: ${cfg.api_key.slice(0, 8)}…${cfg.api_key.slice(-4)}\\n`);\n\n const ping = await pingProject(cfg.server_url, cfg.api_key);\n process.stdout.write(`Reachable: ${ping.ok ? 'yes' : `no (${ping.detail ?? ping.status})`}\\n`);\n\n const sessions = await listBufferedSessions();\n if (sessions.length === 0) {\n process.stdout.write('Buffer: empty.\\n');\n } else {\n process.stdout.write(`Buffer: ${sessions.length} session(s) pending.\\n`);\n for (const s of sessions) {\n const size = await bufferSize(s);\n const mtime = await bufferMtimeMs(s);\n const ageSec = mtime ? Math.round((Date.now() - mtime) / 1000) : null;\n process.stdout.write(` ${s}: ${size} bytes${ageSec !== null ? `, updated ${ageSec}s ago` : ''}\\n`);\n }\n }\n\n const flusherPid = await readFlusherPid();\n if (flusherPid !== null) {\n process.stdout.write(`Flusher: PID ${flusherPid}\\n`);\n } else {\n process.stdout.write('Flusher: not running.\\n');\n }\n\n return 0;\n}\n","import { spawn } from 'node:child_process';\nimport { platform } from 'node:process';\nimport { readConfig } from '../lib/config.js';\n\nfunction openCommand(): { cmd: string; args: string[] } {\n // Cross-platform \"open this URL in the default browser\".\n // macOS: `open <url>`. Linux: `xdg-open <url>`. Windows: `cmd /c start \"\" <url>`.\n if (platform === 'darwin') return { cmd: 'open', args: [] };\n if (platform === 'win32') return { cmd: 'cmd', args: ['/c', 'start', ''] };\n return { cmd: 'xdg-open', args: [] };\n}\n\nexport async function runsCommand(): Promise<number> {\n const cfg = await readConfig();\n if (!cfg) {\n process.stderr.write('Not logged in. Run `runtape login` first.\\n');\n return 1;\n }\n\n const url = `${cfg.server_url.replace(/\\/$/, '')}/dashboard/runs`;\n const { cmd, args } = openCommand();\n\n const child = spawn(cmd, [...args, url], { stdio: 'ignore', detached: true });\n child.on('error', (err) => {\n process.stderr.write(`Could not launch browser (${err.message}). Open this manually:\\n${url}\\n`);\n });\n child.unref();\n\n process.stdout.write(`Opening ${url}\\n`);\n return 0;\n}\n","import { spawn } from 'node:child_process';\nimport { platform } from 'node:process';\nimport { createInterface } from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { Config, defaultServerUrl, readConfig, writeConfig } from '../lib/config.js';\nimport { pingProject } from '../lib/api.js';\nimport { installHooks } from '../lib/hooks-installer.js';\nimport { resolveCliBinPath } from '../lib/cli-bin.js';\n\nfunction openCommand(): { cmd: string; args: string[] } {\n if (platform === 'darwin') return { cmd: 'open', args: [] };\n if (platform === 'win32') return { cmd: 'cmd', args: ['/c', 'start', ''] };\n return { cmd: 'xdg-open', args: [] };\n}\n\nfunction openInBrowser(url: string): void {\n const { cmd, args } = openCommand();\n const child = spawn(cmd, [...args, url], { stdio: 'ignore', detached: true });\n child.on('error', () => {\n /* swallow — caller already printed the URL to fall back on */\n });\n child.unref();\n}\n\nasync function promptYesNo(rl: Awaited<ReturnType<typeof createInterface>>, question: string, defaultYes: boolean): Promise<boolean> {\n const suffix = defaultYes ? '(Y/n)' : '(y/N)';\n const answer = (await rl.question(`${question} ${suffix} `)).trim().toLowerCase();\n if (answer === '') return defaultYes;\n return answer === 'y' || answer === 'yes';\n}\n\nexport async function setupCommand(opts: { noBrowser?: boolean }): Promise<number> {\n const rl = createInterface({ input, output });\n\n try {\n process.stdout.write('\\nRuntape setup\\n');\n process.stdout.write('Let\\'s get your Claude Code runs captured.\\n');\n process.stdout.write('(Press Enter at any prompt to accept the default shown in [brackets].)\\n\\n');\n\n // Step 1 — short-circuit if already configured\n const existing = await readConfig();\n if (existing) {\n process.stdout.write(`Already logged in to ${existing.server_url}\\n`);\n process.stdout.write(`API key: ${existing.api_key.slice(0, 8)}…${existing.api_key.slice(-4)}\\n`);\n const reconfigure = await promptYesNo(rl, 'Reconfigure?', false);\n if (!reconfigure) {\n process.stdout.write('\\nKeeping existing credentials. Moving on to hook install.\\n');\n return await installAndFinish(rl, existing.server_url);\n }\n }\n\n // Step 2 — server URL\n const suggestedUrl = defaultServerUrl();\n process.stdout.write(`Step 1/3 — Backend\\n`);\n const urlInput = (await rl.question(`Server URL [${suggestedUrl}] (Enter to use this): `)).trim();\n const serverUrl = urlInput === '' ? suggestedUrl : urlInput;\n\n // Step 3 — open dashboard for the user to grab their key\n const dashboardUrl = `${serverUrl.replace(/\\/$/, '')}/dashboard`;\n process.stdout.write(`\\nStep 2/3 — API key\\n`);\n process.stdout.write(`Your API key lives in the dashboard:\\n ${dashboardUrl}\\n`);\n if (!opts.noBrowser) {\n openInBrowser(dashboardUrl);\n process.stdout.write('(opened in your browser)\\n');\n }\n\n // Step 4 — paste + validate\n const apiKey = (await rl.question('\\nPaste your API key (rtk_…): ')).trim();\n const validation = Config.shape.api_key.safeParse(apiKey);\n if (!validation.success) {\n process.stderr.write(`\\nInvalid API key format. Expected rtk_<64 hex chars>.\\n`);\n return 2;\n }\n\n process.stdout.write(`Validating against ${serverUrl}…\\n`);\n const ping = await pingProject(serverUrl, apiKey);\n if (!ping.ok) {\n process.stderr.write(\n `\\nLogin failed: ${ping.status === 401 ? 'unknown API key' : ping.detail ?? 'server unreachable'}\\n`,\n );\n return 1;\n }\n\n await writeConfig({ api_key: apiKey, server_url: serverUrl });\n process.stdout.write('Credentials saved.\\n');\n\n // Step 5 — install hooks\n return await installAndFinish(rl, serverUrl);\n } finally {\n rl.close();\n }\n}\n\nasync function installAndFinish(\n rl: Awaited<ReturnType<typeof createInterface>>,\n serverUrl: string,\n): Promise<number> {\n process.stdout.write(`\\nStep 3/3 — Claude Code hooks\\n`);\n const install = await promptYesNo(\n rl,\n 'Install Runtape hooks into ~/.claude/settings.json now?',\n true,\n );\n\n if (!install) {\n process.stdout.write('\\nSkipped hook install. Run `runtape install` when ready.\\n');\n return 0;\n }\n\n const cliBinPath = resolveCliBinPath();\n const result = await installHooks('user', cliBinPath);\n process.stdout.write(`Updated ${result.settingsPath}\\n`);\n if (result.addedHooks.length === 0) {\n process.stdout.write('Hooks already installed — nothing changed.\\n');\n } else {\n process.stdout.write(`Added: ${result.addedHooks.join(', ')}\\n`);\n }\n\n process.stdout.write('\\nSetup complete.\\n');\n process.stdout.write('Now run `claude -p \"any prompt\"` from any directory and watch the run land at:\\n');\n process.stdout.write(` ${serverUrl.replace(/\\/$/, '')}/dashboard\\n\\n`);\n return 0;\n}\n","import { readConfig, writeConfig, type Config, type WatchMode } from '../lib/config.js';\nimport { normalizePath } from '../lib/watch.js';\n\n// Subcommand surface for the path-watcher feature. Mutates the CLI config's\n// `watch` block; no server interaction. Every operation reads → mutates →\n// writeConfig() — that re-applies chmod 600 so the credentials stay locked\n// down even if the user touched the file manually between calls.\n\nfunction modeLabel(mode: WatchMode): string {\n if (mode === 'allow_all') return 'allow_all (capturing every session)';\n if (mode === 'allow_list') return 'allow_list (capturing ONLY listed paths)';\n return 'deny_list (capturing every session EXCEPT listed paths)';\n}\n\nasync function readOrExit(): Promise<Config> {\n const cfg = await readConfig();\n if (!cfg) {\n process.stderr.write('Not logged in. Run `runtape login` first.\\n');\n process.exit(1);\n }\n return cfg;\n}\n\nfunction ensureWatch(cfg: Config): { mode: WatchMode; paths: string[] } {\n return cfg.watch\n ? { mode: cfg.watch.mode, paths: [...cfg.watch.paths] }\n : { mode: 'allow_all', paths: [] };\n}\n\nexport async function watchListCommand(): Promise<number> {\n const cfg = await readOrExit();\n const w = ensureWatch(cfg);\n process.stdout.write(`Mode: ${modeLabel(w.mode)}\\n`);\n if (w.paths.length === 0) {\n process.stdout.write('Paths: (none)\\n');\n } else {\n process.stdout.write('Paths:\\n');\n for (const p of w.paths) process.stdout.write(` ${p}\\n`);\n }\n process.stdout.write('\\nEnv override: set RUNTAPE_DISABLE=1 to skip capture for a single session.\\n');\n return 0;\n}\n\nexport async function watchIgnoreCommand(path: string): Promise<number> {\n const cfg = await readOrExit();\n const w = ensureWatch(cfg);\n if (w.mode === 'allow_list') {\n process.stderr.write(\n \"Current mode is allow_list (only listed paths are captured). `ignore` only makes sense in allow_all/deny_list mode.\\nRun `runtape watch reset` to start over, or use `runtape watch only <path>` to add to the allow list.\\n\",\n );\n return 1;\n }\n const normalized = normalizePath(path);\n if (w.paths.includes(normalized)) {\n process.stdout.write(`Already ignored: ${normalized}\\n`);\n return 0;\n }\n w.paths.push(normalized);\n // Auto-switch: first ignore on a fresh allow_all config flips us into deny_list.\n const next: WatchMode = w.mode === 'allow_all' ? 'deny_list' : w.mode;\n await writeConfig({ ...cfg, watch: { mode: next, paths: w.paths } });\n if (next !== w.mode) process.stdout.write(`Mode switched to deny_list.\\n`);\n process.stdout.write(`Ignoring ${normalized}\\n`);\n return 0;\n}\n\nexport async function watchOnlyCommand(path: string): Promise<number> {\n const cfg = await readOrExit();\n const w = ensureWatch(cfg);\n if (w.mode === 'deny_list') {\n process.stderr.write(\n \"Current mode is deny_list (all except listed paths). `only` would conflict.\\nRun `runtape watch reset` to start over, then `runtape watch only <path>` to switch to allow_list.\\n\",\n );\n return 1;\n }\n const normalized = normalizePath(path);\n if (w.paths.includes(normalized)) {\n process.stdout.write(`Already in allow list: ${normalized}\\n`);\n return 0;\n }\n w.paths.push(normalized);\n const next: WatchMode = w.mode === 'allow_all' ? 'allow_list' : w.mode;\n await writeConfig({ ...cfg, watch: { mode: next, paths: w.paths } });\n if (next !== w.mode) process.stdout.write(`Mode switched to allow_list.\\n`);\n process.stdout.write(`Capturing only: ${normalized}\\n`);\n return 0;\n}\n\nexport async function watchUnignoreCommand(path: string): Promise<number> {\n const cfg = await readOrExit();\n const w = ensureWatch(cfg);\n const normalized = normalizePath(path);\n const filtered = w.paths.filter((p) => p !== normalized);\n if (filtered.length === w.paths.length) {\n process.stdout.write(`Not in the list: ${normalized}\\n`);\n return 0;\n }\n await writeConfig({ ...cfg, watch: { mode: w.mode, paths: filtered } });\n process.stdout.write(`Removed: ${normalized}\\n`);\n return 0;\n}\n\nexport async function watchResetCommand(): Promise<number> {\n const cfg = await readOrExit();\n // We strip the watch key entirely rather than persist `{mode: allow_all, paths: []}`\n // so the config file goes back to its 0.5.x shape — useful for users who\n // downgrade or who inspect the file by hand.\n const { watch: _unused, ...rest } = cfg;\n await writeConfig(rest as Config);\n process.stdout.write('Reset to allow_all. All sessions will be captured.\\n');\n return 0;\n}\n","import { appendFile, mkdir, readFile, unlink, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { readConfig } from './config.js';\nimport { listBufferedSessions, readBufferedSession, rewriteBufferedSession } from './buffer.js';\nimport { postEvents } from './api.js';\nimport { paths } from './paths.js';\nimport type { RuntapeEvent } from '../types.js';\n\n// Tuned in 0.5.0 to cut PostgREST/Vercel invocation pressure under heavy\n// sessions. A power Claude Code user generates 5-10 events/sec; 500-event\n// batches at a 5s cadence still feel real-time in the dashboard while\n// reducing request count ~5x vs the prior 100-event/1.5s defaults.\nconst POLL_INTERVAL_MS = 5_000;\nconst IDLE_EXIT_MS = 30_000;\nconst BATCH_MAX = 500;\nconst BACKOFF_STEPS_MS = [1000, 2000, 4000, 8000, 16_000, 32_000, 60_000];\n\nasync function log(line: string): Promise<void> {\n try {\n await mkdir(dirname(paths.flusherLog), { recursive: true });\n await appendFile(paths.flusherLog, `${new Date().toISOString()} ${line}\\n`);\n } catch {\n /* never throw out of logging */\n }\n}\n\nasync function isProcessAlive(pid: number): Promise<boolean> {\n try {\n process.kill(pid, 0); // Signal 0 = existence check, doesn't actually signal.\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ESRCH') return false;\n if ((err as NodeJS.ErrnoException).code === 'EPERM') return true; // Exists, owned by someone else.\n return false;\n }\n}\n\n// Acquire-or-detect-running. Returns true if we became the flusher; false if one is already running.\nexport async function acquirePidLock(): Promise<boolean> {\n await mkdir(dirname(paths.flusherPid), { recursive: true });\n try {\n const existing = await readFile(paths.flusherPid, 'utf8');\n const pid = Number.parseInt(existing.trim(), 10);\n if (Number.isFinite(pid) && (await isProcessAlive(pid))) {\n return false; // Another flusher is alive.\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n await writeFile(paths.flusherPid, String(process.pid));\n return true;\n}\n\nasync function releasePidLock(): Promise<void> {\n try {\n await unlink(paths.flusherPid);\n } catch {\n /* ignore */\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// Returns true when the server's 400 means \"this whole batch is too big\" —\n// we recover by splitting in half and retrying each half. Different from\n// poison events (a specific row failing validation), where bisection would\n// still surface the bad row but in the right granularity.\nfunction isTooBigError(error: string): boolean {\n // The server's IngestionRequest schema rejects oversize batches with the\n // Zod `too_big` issue code on path [\"events\"]. Match defensively so the\n // CLI works against older servers that may phrase it differently.\n return /too_big/.test(error) || /too many|max(imum)? .*element/i.test(error);\n}\n\n// Posts a slice with adaptive resilience:\n// - 2xx success: caller advances cursor.\n// - Retryable (5xx, 408/425/429, network): caller stops; flusher backs off.\n// - 4xx too_big: bisect down to 1 event before giving up. Cheap and\n// completely recovers from CLI<->server batch-size mismatches.\n// - 4xx other (truly poisoned events): drop the smallest unit we can.\n// For multi-event slices we still bisect so one bad row doesn't take\n// 500 good ones down with it.\ntype FlushOutcome =\n | { ok: true; advanced: number }\n | { ok: false; retryable: true; status: number; error: string }\n | { ok: false; retryable: false; advanced: number };\n\nasync function flushSliceResilient(\n serverUrl: string,\n apiKey: string,\n slice: RuntapeEvent[],\n sessionId: string,\n): Promise<FlushOutcome> {\n if (slice.length === 0) return { ok: true, advanced: 0 };\n\n const result = await postEvents(serverUrl, apiKey, slice);\n if (result.ok) return { ok: true, advanced: slice.length };\n\n if (result.retryable) {\n return { ok: false, retryable: true, status: result.status, error: result.error };\n }\n\n // Single event still rejected as 4xx → it's genuinely poison. Drop one and\n // record so the user has breadcrumbs in the log. This is the ONLY place\n // events leave the buffer without reaching the server.\n if (slice.length === 1) {\n await log(`drop_poison_single session=${sessionId} status=${result.status} error=${result.error.slice(0, 200)}`);\n return { ok: false, retryable: false, advanced: 1 };\n }\n\n // Multi-event 4xx (typically \"too_big\"): bisect. Log once at the top of\n // the split so the log isn't drowned by recursion.\n if (isTooBigError(result.error)) {\n await log(`bisect_too_big session=${sessionId} size=${slice.length}`);\n } else {\n await log(`bisect_poison session=${sessionId} size=${slice.length} error=${result.error.slice(0, 120)}`);\n }\n const mid = Math.floor(slice.length / 2);\n const first = await flushSliceResilient(serverUrl, apiKey, slice.slice(0, mid), sessionId);\n if (first.ok === false && first.retryable) return first;\n const advancedFirst = first.ok ? first.advanced : first.advanced;\n\n const second = await flushSliceResilient(serverUrl, apiKey, slice.slice(mid), sessionId);\n if (second.ok === false && second.retryable) {\n // Still progress was made on the first half — surface advanced count via\n // a non-retryable success so the caller can move the cursor that far.\n return { ok: false, retryable: false, advanced: advancedFirst };\n }\n const advancedSecond = second.ok ? second.advanced : second.advanced;\n const totalAdvanced = advancedFirst + advancedSecond;\n // If everything advanced cleanly via bisection, signal ok so the cursor\n // moves and we don't enter backoff.\n if (totalAdvanced === slice.length) return { ok: true, advanced: totalAdvanced };\n return { ok: false, retryable: false, advanced: totalAdvanced };\n}\n\n// Drain a single session's buffer in batches of up to BATCH_MAX. Returns true if any\n// events were successfully flushed (or definitively dealt with via bisection).\nasync function drainSession(sessionId: string, serverUrl: string, apiKey: string): Promise<boolean> {\n const snapshot = await readBufferedSession(sessionId);\n if (!snapshot || snapshot.events.length === 0) {\n await rewriteBufferedSession(sessionId, []);\n return false;\n }\n\n let cursor = 0;\n let anyFlushed = false;\n while (cursor < snapshot.events.length) {\n const slice = snapshot.events.slice(cursor, cursor + BATCH_MAX);\n const outcome = await flushSliceResilient(serverUrl, apiKey, slice, sessionId);\n\n if (outcome.ok) {\n cursor += outcome.advanced;\n anyFlushed = true;\n continue;\n }\n\n if (outcome.retryable) {\n await log(`retryable session=${sessionId} status=${outcome.status} cursor=${cursor} error=${outcome.error.slice(0, 200)}`);\n break;\n }\n\n // Non-retryable: bisect advanced us through some (possibly 0) events.\n // Move cursor by the count it covered and continue. anyFlushed reflects\n // whether the buffer is shrinking even on partial bisection.\n cursor += outcome.advanced;\n if (outcome.advanced > 0) anyFlushed = true;\n else {\n // Zero progress on a non-retryable batch is pathological — guard\n // against an infinite loop by advancing one event regardless.\n cursor += 1;\n await log(`drop_zero_advance session=${sessionId}`);\n }\n }\n\n const remaining = snapshot.raw.slice(cursor);\n await rewriteBufferedSession(sessionId, remaining);\n return anyFlushed;\n}\n\nexport async function runFlusher(): Promise<void> {\n const acquired = await acquirePidLock();\n if (!acquired) {\n await log('exit_already_running');\n return;\n }\n\n await log(`start pid=${process.pid}`);\n\n let lastActivityMs = Date.now();\n let backoffIdx = 0;\n\n try {\n while (true) {\n const cfg = await readConfig();\n if (!cfg) {\n // No config yet — we shouldn't have been spawned. Exit cleanly.\n await log('exit_no_config');\n return;\n }\n\n const sessions = await listBufferedSessions();\n let flushedThisCycle = false;\n for (const sessionId of sessions) {\n const flushed = await drainSession(sessionId, cfg.server_url, cfg.api_key);\n flushedThisCycle = flushedThisCycle || flushed;\n }\n\n if (flushedThisCycle) {\n lastActivityMs = Date.now();\n backoffIdx = 0;\n }\n\n // Idle exit.\n const remaining = await listBufferedSessions();\n const idleMs = Date.now() - lastActivityMs;\n if (remaining.length === 0 && idleMs >= IDLE_EXIT_MS) {\n await log(`exit_idle idle_ms=${idleMs}`);\n return;\n }\n\n // Backoff when buffer is non-empty but we couldn't drain anything (server down).\n // Reset backoff when we made progress.\n const wait =\n remaining.length > 0 && !flushedThisCycle\n ? BACKOFF_STEPS_MS[Math.min(backoffIdx++, BACKOFF_STEPS_MS.length - 1)]\n : POLL_INTERVAL_MS;\n await delay(wait);\n }\n } catch (err) {\n await log(`crash error=${err instanceof Error ? err.message : String(err)}`);\n throw err;\n } finally {\n await releasePidLock();\n }\n}\n"],"mappings":"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","names":["fileURLToPath","dirname","join","createInterface","input","output","mkdir","readFile","writeFile","dirname","readFile","mkdir","dirname","writeFile","dirname","createInterface","input","output","mkdir","readFile","writeFile","dirname","mkdir","dirname","readFile","writeFile","mkdir","readFile","writeFile","unlink","mkdir","readFile","unlink","writeFile","mkdir","readFile","writeFile","dirname","readFile","mkdir","dirname","writeFile","mkdir","readFile","writeFile","dirname","asInt","readCursor","readFile","CURSOR_KEEP","mkdir","dirname","writeFile","homedir","resolve","input","stat","readFile","dirname","join","cwd","readFile","readFile","spawn","spawn","spawn","platform","createInterface","input","output","openCommand","platform","spawn","createInterface","input","output","appendFile","mkdir","readFile","unlink","writeFile","dirname","mkdir","dirname","appendFile","readFile","writeFile","unlink","resolve","join","dirname","fileURLToPath"]}
package/dist/types.d.ts CHANGED
@@ -13,6 +13,7 @@ declare const SessionStartEvent: z.ZodObject<{
13
13
  } & {
14
14
  type: z.ZodLiteral<"session_start">;
15
15
  source: z.ZodString;
16
+ project_name: z.ZodOptional<z.ZodString>;
16
17
  }, "strip", z.ZodTypeAny, {
17
18
  session_id: string;
18
19
  transcript_path: string;
@@ -24,6 +25,7 @@ declare const SessionStartEvent: z.ZodObject<{
24
25
  source: string;
25
26
  permission_mode?: string | undefined;
26
27
  agent_tool_use_id?: string | undefined;
28
+ project_name?: string | undefined;
27
29
  }, {
28
30
  session_id: string;
29
31
  transcript_path: string;
@@ -35,6 +37,7 @@ declare const SessionStartEvent: z.ZodObject<{
35
37
  source: string;
36
38
  permission_mode?: string | undefined;
37
39
  agent_tool_use_id?: string | undefined;
40
+ project_name?: string | undefined;
38
41
  }>;
39
42
  declare const UserPromptEvent: z.ZodObject<{
40
43
  session_id: z.ZodString;
@@ -358,6 +361,7 @@ declare const RuntapeEvent: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
358
361
  } & {
359
362
  type: z.ZodLiteral<"session_start">;
360
363
  source: z.ZodString;
364
+ project_name: z.ZodOptional<z.ZodString>;
361
365
  }, "strip", z.ZodTypeAny, {
362
366
  session_id: string;
363
367
  transcript_path: string;
@@ -369,6 +373,7 @@ declare const RuntapeEvent: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
369
373
  source: string;
370
374
  permission_mode?: string | undefined;
371
375
  agent_tool_use_id?: string | undefined;
376
+ project_name?: string | undefined;
372
377
  }, {
373
378
  session_id: string;
374
379
  transcript_path: string;
@@ -380,6 +385,7 @@ declare const RuntapeEvent: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
380
385
  source: string;
381
386
  permission_mode?: string | undefined;
382
387
  agent_tool_use_id?: string | undefined;
388
+ project_name?: string | undefined;
383
389
  }>, z.ZodObject<{
384
390
  session_id: z.ZodString;
385
391
  transcript_path: z.ZodString;
@@ -698,6 +704,7 @@ declare const IngestionRequest: z.ZodObject<{
698
704
  } & {
699
705
  type: z.ZodLiteral<"session_start">;
700
706
  source: z.ZodString;
707
+ project_name: z.ZodOptional<z.ZodString>;
701
708
  }, "strip", z.ZodTypeAny, {
702
709
  session_id: string;
703
710
  transcript_path: string;
@@ -709,6 +716,7 @@ declare const IngestionRequest: z.ZodObject<{
709
716
  source: string;
710
717
  permission_mode?: string | undefined;
711
718
  agent_tool_use_id?: string | undefined;
719
+ project_name?: string | undefined;
712
720
  }, {
713
721
  session_id: string;
714
722
  transcript_path: string;
@@ -720,6 +728,7 @@ declare const IngestionRequest: z.ZodObject<{
720
728
  source: string;
721
729
  permission_mode?: string | undefined;
722
730
  agent_tool_use_id?: string | undefined;
731
+ project_name?: string | undefined;
723
732
  }>, z.ZodObject<{
724
733
  session_id: z.ZodString;
725
734
  transcript_path: z.ZodString;
@@ -1035,6 +1044,7 @@ declare const IngestionRequest: z.ZodObject<{
1035
1044
  source: string;
1036
1045
  permission_mode?: string | undefined;
1037
1046
  agent_tool_use_id?: string | undefined;
1047
+ project_name?: string | undefined;
1038
1048
  } | {
1039
1049
  session_id: string;
1040
1050
  transcript_path: string;
@@ -1144,6 +1154,7 @@ declare const IngestionRequest: z.ZodObject<{
1144
1154
  source: string;
1145
1155
  permission_mode?: string | undefined;
1146
1156
  agent_tool_use_id?: string | undefined;
1157
+ project_name?: string | undefined;
1147
1158
  } | {
1148
1159
  session_id: string;
1149
1160
  transcript_path: string;
package/dist/types.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  ToolAttemptEvent,
11
11
  ToolCallEvent,
12
12
  UserPromptEvent
13
- } from "./chunk-LGAXYSLN.js";
13
+ } from "./chunk-54VJGDD2.js";
14
14
  export {
15
15
  AssistantTurnEvent,
16
16
  IngestionRequest,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runtape",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Flight recorder for AI coding agents. Captures Claude Code sessions for replay in runtape.dev.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://runtape.dev",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import { z } from 'zod';\n\n// Every Claude Code hook event carries these envelope fields.\nconst ClaudeHookBase = z.object({\n session_id: z.string().min(1),\n transcript_path: z.string().min(1),\n cwd: z.string().min(1),\n hook_event_name: z.string().min(1),\n permission_mode: z.string().optional(),\n});\n\n// CLI-augmented envelope adds monotonic ordering + wall-clock timestamp.\n// agent_tool_use_id is set when the CLI is synthesizing events from a\n// subagent transcript — it carries the parent Agent step's tool_use_id so\n// the server can resolve parent_step_id deterministically instead of\n// relying on the temporal open-stack heuristic. Optional so the field is\n// absent for normal top-level events.\nconst CliAugment = z.object({\n wall_ts: z.string().datetime(),\n sequence: z.number().int().nonnegative(),\n agent_tool_use_id: z.string().min(1).optional(),\n});\n\nexport const SessionStartEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('session_start'),\n source: z.string(),\n});\n\nexport const UserPromptEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('user_prompt'),\n prompt: z.string(),\n});\n\nexport const ToolAttemptEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('tool_attempt'),\n tool_name: z.string().min(1),\n tool_input: z.unknown(),\n tool_use_id: z.string().min(1),\n});\n\nexport const ToolCallEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('tool_call'),\n tool_name: z.string().min(1),\n tool_input: z.unknown(),\n tool_response: z.unknown(),\n tool_use_id: z.string().min(1),\n duration_ms: z.number().nonnegative(),\n // Set by the CLI when the tool_response signals an error (Bash non-zero\n // exit, Edit/Write rejection, tool_response.is_error, etc.). Optional so\n // older CLI versions stay forward-compatible.\n is_error: z.boolean().optional(),\n error_message: z.string().optional(),\n});\n\n// Emitted by the CLI on Stop / PostToolUse after scanning the Claude Code\n// transcript JSONL. One event per assistant message we haven't seen yet,\n// keyed by message_uuid so re-deliveries dedupe at the server. Carries the\n// model identifier and token usage for that turn — the only place either\n// datum is available in Claude Code's emit chain.\nexport const AssistantTurnEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('assistant_turn'),\n message_uuid: z.string().min(1),\n model: z.string().min(1),\n input_tokens: z.number().int().nonnegative(),\n output_tokens: z.number().int().nonnegative(),\n cache_read_tokens: z.number().int().nonnegative().default(0),\n cache_creation_tokens: z.number().int().nonnegative().default(0),\n text: z.string().optional(),\n});\n\nexport const SubagentEndEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('subagent_end'),\n agent_id: z.string().min(1),\n agent_type: z.string().min(1),\n agent_transcript_path: z.string().min(1),\n last_assistant_message: z.string(),\n stop_hook_active: z.boolean().optional(),\n});\n\n// Stop hook (fires every turn). Despite the literal name 'session_end', this\n// is \"turn end\" semantically. Kept for backward compatibility with CLI <= 0.3.x.\n// New CLIs still emit this; the server interprets it as \"run is idle\".\nexport const SessionEndEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('session_end'),\n last_assistant_message: z.string().optional(),\n stop_hook_active: z.boolean().optional(),\n});\n\n// SessionEnd hook (fires once when Claude Code actually closes the session).\n// Server uses this to promote the run from 'idle' to 'ended'.\nexport const SessionCloseEvent = ClaudeHookBase.extend(CliAugment.shape).extend({\n type: z.literal('session_close'),\n reason: z.string().optional(),\n});\n\nexport const RuntapeEvent = z.discriminatedUnion('type', [\n SessionStartEvent,\n UserPromptEvent,\n ToolAttemptEvent,\n ToolCallEvent,\n AssistantTurnEvent,\n SubagentEndEvent,\n SessionEndEvent,\n SessionCloseEvent,\n]);\n\nexport type RuntapeEvent = z.infer<typeof RuntapeEvent>;\n\n// POST /v1/events body — a batch of up to 100 events.\nexport const IngestionRequest = z.object({\n events: z.array(RuntapeEvent).min(1).max(100),\n});\n\nexport type IngestionRequest = z.infer<typeof IngestionRequest>;\n\n// Response shape (server returns 200 on accepted, 400 on Zod failure, 401 on bad auth).\nexport const IngestionResponse = z.object({\n accepted: z.number().int().nonnegative(),\n errors: z\n .array(\n z.object({\n index: z.number().int().nonnegative(),\n reason: z.string(),\n }),\n )\n .default([]),\n});\n\nexport type IngestionResponse = z.infer<typeof IngestionResponse>;\n"],"mappings":"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","names":[]}