@claude-transcripts/cli 0.6.0 → 0.6.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.
@@ -7,7 +7,7 @@ import {
7
7
  cliArgLabel,
8
8
  CLI_SPEC,
9
9
  COMMANDS
10
- } from "./cli-tspndnr8.js";
10
+ } from "./cli-3frjxyqc.js";
11
11
  import {
12
12
  __toESM,
13
13
  __commonJS,
@@ -1,4 +1,91 @@
1
1
  // @bun
2
+ // packages/shared/src/blueprint/actions.ts
3
+ var ACTIONS = [
4
+ {
5
+ key: "write-event-marker",
6
+ summary: "Append a light append-only event marker doc",
7
+ implemented: true
8
+ },
9
+ {
10
+ key: "update-counts",
11
+ summary: "Bump per-session counters (events/prompts/errors/tools)",
12
+ implemented: true
13
+ },
14
+ {
15
+ key: "flush-transcript-chunk",
16
+ summary: "Tail the transcript \u2192 append chunk docs",
17
+ implemented: true
18
+ },
19
+ { key: "write-summary", summary: "Write summary:<id> at session end", implemented: true },
20
+ {
21
+ key: "upload-blobs",
22
+ summary: "Upload summary.json + transcript.jsonl to S3",
23
+ implemented: true
24
+ },
25
+ {
26
+ key: "seed-session-start",
27
+ summary: "Reset counters + seed the chunk offset at start",
28
+ implemented: true
29
+ },
30
+ {
31
+ key: "announce-recording",
32
+ summary: "Tell the user, in the transcript, whether and where this session is recorded",
33
+ implemented: true
34
+ },
35
+ {
36
+ key: "inject-recall-policy",
37
+ summary: "Prime the session with the recall policy + how much history this cwd has",
38
+ implemented: true
39
+ },
40
+ { key: "enrich-metadata", summary: "Post additional session metadata", implemented: false },
41
+ {
42
+ key: "extract-feature",
43
+ summary: "Map-reduce feature extraction (urls/repos/PRs/\u2026)",
44
+ implemented: false
45
+ },
46
+ { key: "detect-memory-write", summary: "Flag Edit/Write to memory paths", implemented: false },
47
+ { key: "app-log", summary: "Emit an operational log record (app-logs DB)", implemented: false },
48
+ {
49
+ key: "index-for-search",
50
+ summary: "Push derived content to the search backend",
51
+ implemented: false
52
+ },
53
+ {
54
+ key: "reconcile-session",
55
+ summary: "Finalize a stale running session from chunks/S3",
56
+ implemented: false
57
+ }
58
+ ];
59
+ var BINDINGS = [
60
+ {
61
+ event: "SessionStart",
62
+ actions: [
63
+ "seed-session-start",
64
+ "write-event-marker",
65
+ "announce-recording",
66
+ "inject-recall-policy"
67
+ ]
68
+ },
69
+ {
70
+ event: "UserPromptSubmit",
71
+ actions: ["write-event-marker", "update-counts", "flush-transcript-chunk"]
72
+ },
73
+ {
74
+ event: "PostToolUse",
75
+ actions: ["write-event-marker", "update-counts", "flush-transcript-chunk"]
76
+ },
77
+ {
78
+ event: "PostToolUseFailure",
79
+ actions: ["write-event-marker", "update-counts", "flush-transcript-chunk"]
80
+ },
81
+ { event: "Stop", actions: ["write-event-marker", "flush-transcript-chunk"] },
82
+ { event: "SubagentStart", actions: ["write-event-marker", "update-counts"] },
83
+ { event: "SubagentStop", actions: ["write-event-marker", "update-counts"] },
84
+ { event: "StopFailure", actions: ["write-event-marker", "update-counts"] },
85
+ { event: "PreCompact", actions: ["write-event-marker"] },
86
+ { event: "PostCompact", actions: ["write-event-marker"] },
87
+ { event: "SessionEnd", actions: ["flush-transcript-chunk", "write-summary", "upload-blobs"] }
88
+ ];
2
89
  // packages/shared/src/blueprint/cli-project.ts
3
90
  var isFlag = (a) => a.name.startsWith("--");
4
91
  function cliArgType(a) {
@@ -673,14 +760,20 @@ var CLI_SPEC = {
673
760
  args: [
674
761
  {
675
762
  name: "action",
676
- choices: ["run", "install", "uninstall", "status"],
763
+ choices: ["run", "upload", "install", "uninstall", "status"],
677
764
  default: "status",
678
- description: "`run` reads one event payload from stdin (Claude Code calls this)"
765
+ description: "`run` reads one event payload from stdin (Claude Code calls this); `upload` " + "ships one transcript to S3 (SessionEnd starts it detached)"
679
766
  },
680
767
  { name: "--dry-run", description: "with `install`: show the change without writing" },
681
768
  {
682
769
  name: "--force",
683
770
  description: "with `install`: register even if the plugin already does"
771
+ },
772
+ { name: "--session", type: "string", description: "with `upload`: the session id" },
773
+ {
774
+ name: "--transcript",
775
+ type: "string",
776
+ description: "with `upload`: the transcript file to ship"
684
777
  }
685
778
  ],
686
779
  examples: ["hook status", "hook install --dry-run"]
@@ -717,93 +810,6 @@ var CLI_SPEC = {
717
810
  ]
718
811
  };
719
812
 
720
- // packages/shared/src/blueprint/actions.ts
721
- var ACTIONS = [
722
- {
723
- key: "write-event-marker",
724
- summary: "Append a light append-only event marker doc",
725
- implemented: true
726
- },
727
- {
728
- key: "update-counts",
729
- summary: "Bump per-session counters (events/prompts/errors/tools)",
730
- implemented: true
731
- },
732
- {
733
- key: "flush-transcript-chunk",
734
- summary: "Tail the transcript \u2192 append chunk docs",
735
- implemented: true
736
- },
737
- { key: "write-summary", summary: "Write summary:<id> at session end", implemented: true },
738
- {
739
- key: "upload-blobs",
740
- summary: "Upload summary.json + transcript.jsonl to S3",
741
- implemented: true
742
- },
743
- {
744
- key: "seed-session-start",
745
- summary: "Reset counters + seed the chunk offset at start",
746
- implemented: true
747
- },
748
- {
749
- key: "announce-recording",
750
- summary: "Tell the user, in the transcript, whether and where this session is recorded",
751
- implemented: true
752
- },
753
- {
754
- key: "inject-recall-policy",
755
- summary: "Prime the session with the recall policy + how much history this cwd has",
756
- implemented: true
757
- },
758
- { key: "enrich-metadata", summary: "Post additional session metadata", implemented: false },
759
- {
760
- key: "extract-feature",
761
- summary: "Map-reduce feature extraction (urls/repos/PRs/\u2026)",
762
- implemented: false
763
- },
764
- { key: "detect-memory-write", summary: "Flag Edit/Write to memory paths", implemented: false },
765
- { key: "app-log", summary: "Emit an operational log record (app-logs DB)", implemented: false },
766
- {
767
- key: "index-for-search",
768
- summary: "Push derived content to the search backend",
769
- implemented: false
770
- },
771
- {
772
- key: "reconcile-session",
773
- summary: "Finalize a stale running session from chunks/S3",
774
- implemented: false
775
- }
776
- ];
777
- var BINDINGS = [
778
- {
779
- event: "SessionStart",
780
- actions: [
781
- "seed-session-start",
782
- "write-event-marker",
783
- "announce-recording",
784
- "inject-recall-policy"
785
- ]
786
- },
787
- {
788
- event: "UserPromptSubmit",
789
- actions: ["write-event-marker", "update-counts", "flush-transcript-chunk"]
790
- },
791
- {
792
- event: "PostToolUse",
793
- actions: ["write-event-marker", "update-counts", "flush-transcript-chunk"]
794
- },
795
- {
796
- event: "PostToolUseFailure",
797
- actions: ["write-event-marker", "update-counts", "flush-transcript-chunk"]
798
- },
799
- { event: "Stop", actions: ["write-event-marker", "flush-transcript-chunk"] },
800
- { event: "SubagentStart", actions: ["write-event-marker", "update-counts"] },
801
- { event: "SubagentStop", actions: ["write-event-marker", "update-counts"] },
802
- { event: "StopFailure", actions: ["write-event-marker", "update-counts"] },
803
- { event: "PreCompact", actions: ["write-event-marker"] },
804
- { event: "PostCompact", actions: ["write-event-marker"] },
805
- { event: "SessionEnd", actions: ["flush-transcript-chunk", "write-summary", "upload-blobs"] }
806
- ];
807
813
  // packages/shared/src/blueprint/env.ts
808
814
  var ENV_VARS = [
809
815
  {
@@ -2416,43 +2422,6 @@ function buildChunkEntries(jsonl) {
2416
2422
  return out;
2417
2423
  }
2418
2424
 
2419
- // packages/cli/src/lib/args.ts
2420
- function parseFlags(argv) {
2421
- const positionals = [];
2422
- const options = {};
2423
- for (let i = 0;i < argv.length; i++) {
2424
- const a = argv[i];
2425
- if (a === undefined)
2426
- continue;
2427
- if (a.startsWith("--")) {
2428
- const body = a.slice(2);
2429
- const eq = body.indexOf("=");
2430
- if (eq >= 0) {
2431
- options[body.slice(0, eq)] = body.slice(eq + 1);
2432
- } else {
2433
- const next = argv[i + 1];
2434
- if (next !== undefined && !next.startsWith("--")) {
2435
- options[body] = next;
2436
- i++;
2437
- } else {
2438
- options[body] = true;
2439
- }
2440
- }
2441
- } else {
2442
- positionals.push(a);
2443
- }
2444
- }
2445
- return { positionals, options };
2446
- }
2447
- function strOpt(opts, key) {
2448
- const v = opts[key];
2449
- return typeof v === "string" ? v : undefined;
2450
- }
2451
-
2452
- // packages/cli/src/lib/version.ts
2453
- var DEV_VERSION = "0.0.0-dev";
2454
- var VERSION = process.env.CT_VERSION ?? DEV_VERSION;
2455
-
2456
2425
  // packages/cli/src/commands/backfill.ts
2457
2426
  import { hostname } from "os";
2458
2427
 
@@ -2616,6 +2585,39 @@ async function putRaw(path, body, contentType) {
2616
2585
  }
2617
2586
  }
2618
2587
 
2588
+ // packages/cli/src/lib/args.ts
2589
+ function parseFlags(argv) {
2590
+ const positionals = [];
2591
+ const options = {};
2592
+ for (let i = 0;i < argv.length; i++) {
2593
+ const a = argv[i];
2594
+ if (a === undefined)
2595
+ continue;
2596
+ if (a.startsWith("--")) {
2597
+ const body = a.slice(2);
2598
+ const eq = body.indexOf("=");
2599
+ if (eq >= 0) {
2600
+ options[body.slice(0, eq)] = body.slice(eq + 1);
2601
+ } else {
2602
+ const next = argv[i + 1];
2603
+ if (next !== undefined && !next.startsWith("--")) {
2604
+ options[body] = next;
2605
+ i++;
2606
+ } else {
2607
+ options[body] = true;
2608
+ }
2609
+ }
2610
+ } else {
2611
+ positionals.push(a);
2612
+ }
2613
+ }
2614
+ return { positionals, options };
2615
+ }
2616
+ function strOpt(opts, key) {
2617
+ const v = opts[key];
2618
+ return typeof v === "string" ? v : undefined;
2619
+ }
2620
+
2619
2621
  // packages/cli/src/lib/claude-fs.ts
2620
2622
  import { readdir, readFile } from "fs/promises";
2621
2623
  import { homedir as homedir2 } from "os";
@@ -4162,6 +4164,12 @@ import { dirname as dirname2 } from "path";
4162
4164
 
4163
4165
  // packages/cli/src/lib/statusline.ts
4164
4166
  import { statSync } from "fs";
4167
+
4168
+ // packages/cli/src/lib/version.ts
4169
+ var DEV_VERSION = "0.0.0-dev";
4170
+ var VERSION = process.env.CT_VERSION ?? DEV_VERSION;
4171
+
4172
+ // packages/cli/src/lib/statusline.ts
4165
4173
  var STALL_AFTER_MS = 60000;
4166
4174
  var HOOK_SILENT_AFTER_MS = 5 * 60000;
4167
4175
  function transcriptMtimeMs(path) {
@@ -4349,6 +4357,31 @@ import {
4349
4357
  import { hostname as hostname4 } from "os";
4350
4358
  var {S3Client } = globalThis.Bun;
4351
4359
 
4360
+ // packages/cli/src/hook/detach.ts
4361
+ import { spawn } from "child_process";
4362
+ function cliArgv() {
4363
+ const exec = process.execPath;
4364
+ if (!/\/bun$/.test(exec))
4365
+ return [exec];
4366
+ return [exec, "run", Bun.main];
4367
+ }
4368
+ function spawnDetached(argv) {
4369
+ const [cmd, ...args] = argv;
4370
+ if (!cmd)
4371
+ return false;
4372
+ try {
4373
+ const child = spawn(cmd, args, { detached: true, stdio: "ignore", cwd: "/" });
4374
+ child.on("error", () => {});
4375
+ child.unref();
4376
+ return child.pid !== undefined;
4377
+ } catch {
4378
+ return false;
4379
+ }
4380
+ }
4381
+ function uploadArgv(sessionId, transcriptPath) {
4382
+ return [...cliArgv(), "hook", "upload", "--session", sessionId, "--transcript", transcriptPath];
4383
+ }
4384
+
4352
4385
  // packages/cli/src/hook/mirror.ts
4353
4386
  var DEFAULT_TIMEOUT_MS = 2000;
4354
4387
  var DEFAULT_BLOB_TIMEOUT_MS = 60000;
@@ -4806,7 +4839,8 @@ function buildContext(payload, config) {
4806
4839
  counts: makeCounts(sessionId),
4807
4840
  targets,
4808
4841
  sessionsDb,
4809
- sessionsBucket: config.blob?.buckets.sessions
4842
+ sessionsBucket: config.blob?.buckets.sessions,
4843
+ detachUpload: (sid, path) => spawnDetached(uploadArgv(sid, path))
4810
4844
  };
4811
4845
  }
4812
4846
  function commonFields(ctx) {
@@ -5097,16 +5131,21 @@ var writeSummary = async (ctx) => {
5097
5131
  }
5098
5132
  ctx.targets.clear();
5099
5133
  };
5100
- var uploadBlobs = async (ctx) => {
5101
- if (!ctx.blob.enabled || !ctx.sessionsBucket || !ctx.transcriptPath)
5102
- return;
5134
+ async function uploadTranscript(blob, bucket, sessionId, transcriptPath) {
5103
5135
  let data;
5104
5136
  try {
5105
- data = readFileSync5(ctx.transcriptPath, "utf8");
5137
+ data = readFileSync5(transcriptPath, "utf8");
5106
5138
  } catch {
5107
5139
  return;
5108
5140
  }
5109
- await ctx.blob.put(ctx.sessionsBucket, `${ctx.sessionId}/transcript.jsonl`, data, "application/x-ndjson");
5141
+ await blob.put(bucket, `${sessionId}/transcript.jsonl`, data, "application/x-ndjson");
5142
+ }
5143
+ var uploadBlobs = async (ctx) => {
5144
+ if (!ctx.blob.enabled || !ctx.sessionsBucket || !ctx.transcriptPath)
5145
+ return;
5146
+ if (ctx.detachUpload(ctx.sessionId, ctx.transcriptPath))
5147
+ return;
5148
+ await uploadTranscript(ctx.blob, ctx.sessionsBucket, ctx.sessionId, ctx.transcriptPath);
5110
5149
  };
5111
5150
  var HANDLERS = {
5112
5151
  "seed-session-start": seedSessionStart,
@@ -5127,9 +5166,9 @@ function hookBindings() {
5127
5166
  out[b.event] = b.actions;
5128
5167
  return out;
5129
5168
  }
5130
- var LONG_TIMEOUT_EVENTS = new Set(["SessionStart", "SessionEnd"]);
5169
+ var TIMEOUTS = { SessionStart: 180, SessionEnd: 60 };
5131
5170
  function hookTimeout(event) {
5132
- return LONG_TIMEOUT_EVENTS.has(event) ? 180 : 5;
5171
+ return TIMEOUTS[event] ?? 5;
5133
5172
  }
5134
5173
  var SYNCHRONOUS_EVENTS = new Set(["SessionStart", "SessionEnd", "UserPromptSubmit"]);
5135
5174
  function hookAsync(event) {
@@ -5238,11 +5277,8 @@ function hookCommand() {
5238
5277
  return hookLaunch(`${cliInvocation()} hook run`);
5239
5278
  }
5240
5279
  function cliInvocation() {
5241
- const exec = process.execPath;
5242
- const isCompiled = !/\/bun$/.test(exec);
5243
- if (isCompiled)
5244
- return `"${exec}"`;
5245
- return `"${exec}" run "${Bun.main}"`;
5280
+ const [exec, , entry] = cliArgv();
5281
+ return entry ? `"${exec}" run "${entry}"` : `"${exec}"`;
5246
5282
  }
5247
5283
  function isOurCommand(command) {
5248
5284
  if (!command?.includes("hook run"))
@@ -5421,11 +5457,34 @@ async function run() {
5421
5457
  }
5422
5458
  return 0;
5423
5459
  }
5460
+ var UPLOAD_DEADLINE_MS = 10 * 60000;
5461
+ async function upload(argv) {
5462
+ setTimeout(() => process.exit(0), UPLOAD_DEADLINE_MS).unref();
5463
+ const { options } = parseFlags(argv);
5464
+ const sessionId = strOpt(options, "session");
5465
+ const transcriptPath = strOpt(options, "transcript");
5466
+ if (!sessionId || !transcriptPath) {
5467
+ console.error("hook: upload needs --session <id> --transcript <path>");
5468
+ return 0;
5469
+ }
5470
+ try {
5471
+ const config = loadHookConfig(installPaths().hookConfig);
5472
+ const bucket = config?.blob?.buckets.sessions;
5473
+ if (!config || !bucket)
5474
+ return 0;
5475
+ await uploadTranscript(makeBlob(config), bucket, sessionId, transcriptPath);
5476
+ } catch (err) {
5477
+ console.error("[hook] upload failed (ignored):", err);
5478
+ }
5479
+ return 0;
5480
+ }
5424
5481
  async function runHook(argv) {
5425
5482
  const [sub = "status", ...rest] = argv;
5426
5483
  switch (sub) {
5427
5484
  case "run":
5428
5485
  return run();
5486
+ case "upload":
5487
+ return upload(rest);
5429
5488
  case "install":
5430
5489
  return install(rest);
5431
5490
  case "uninstall":
@@ -5433,7 +5492,7 @@ async function runHook(argv) {
5433
5492
  case "status":
5434
5493
  return status();
5435
5494
  default:
5436
- console.error(`hook: unknown subcommand "${sub}" (run | install | uninstall | status)`);
5495
+ console.error(`hook: unknown subcommand "${sub}" (run | upload | install | uninstall | status)`);
5437
5496
  return 2;
5438
5497
  }
5439
5498
  }
package/dist/cli.js CHANGED
@@ -7,13 +7,13 @@ import {
7
7
  parseFlags,
8
8
  VERSION,
9
9
  COMMANDS
10
- } from "./cli-tspndnr8.js";
10
+ } from "./cli-3frjxyqc.js";
11
11
 
12
12
  // packages/cli/src/cli.tsx
13
13
  var [, , first, ...rest] = process.argv;
14
14
  var USAGE_ERROR = 2;
15
15
  async function showHelp(props, stream = process.stdout) {
16
- const { renderHelp } = await import("./app-h6q0k2rz.js");
16
+ const { renderHelp } = await import("./app-zjbeac0e.js");
17
17
  renderHelp(props, stream);
18
18
  }
19
19
  var argv = first === undefined ? [] : [first, ...rest];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-transcripts/cli",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "description": "Claude Transcripts CLI — browse, search, back up, and administer your self-hosted Claude Code session history.",
6
6
  "keywords": [