@furkankoykiran/contextify-cli 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/batcher.d.ts +36 -0
  2. package/dist/batcher.d.ts.map +1 -0
  3. package/dist/batcher.js +94 -0
  4. package/dist/batcher.js.map +1 -0
  5. package/dist/commands/hooks.d.ts +32 -0
  6. package/dist/commands/hooks.d.ts.map +1 -0
  7. package/dist/commands/hooks.js +253 -0
  8. package/dist/commands/hooks.js.map +1 -0
  9. package/dist/commands/init.d.ts +9 -0
  10. package/dist/commands/init.d.ts.map +1 -0
  11. package/dist/commands/init.js +49 -0
  12. package/dist/commands/init.js.map +1 -0
  13. package/dist/commands/install-hooks.d.ts +42 -0
  14. package/dist/commands/install-hooks.d.ts.map +1 -0
  15. package/dist/commands/install-hooks.js +162 -0
  16. package/dist/commands/install-hooks.js.map +1 -0
  17. package/dist/commands/install.d.ts +53 -0
  18. package/dist/commands/install.d.ts.map +1 -0
  19. package/dist/commands/install.js +129 -0
  20. package/dist/commands/install.js.map +1 -0
  21. package/dist/commands/login.d.ts +9 -0
  22. package/dist/commands/login.d.ts.map +1 -0
  23. package/dist/commands/login.js +36 -0
  24. package/dist/commands/login.js.map +1 -0
  25. package/dist/commands/prompt.d.ts +17 -0
  26. package/dist/commands/prompt.d.ts.map +1 -0
  27. package/dist/commands/prompt.js +141 -0
  28. package/dist/commands/prompt.js.map +1 -0
  29. package/dist/commands/ship.d.ts +6 -0
  30. package/dist/commands/ship.d.ts.map +1 -0
  31. package/dist/commands/ship.js +20 -0
  32. package/dist/commands/ship.js.map +1 -0
  33. package/dist/commands/wrap.d.ts +10 -0
  34. package/dist/commands/wrap.d.ts.map +1 -0
  35. package/dist/commands/wrap.js +83 -0
  36. package/dist/commands/wrap.js.map +1 -0
  37. package/dist/config.d.ts +16 -0
  38. package/dist/config.d.ts.map +1 -0
  39. package/dist/config.js +84 -0
  40. package/dist/config.js.map +1 -0
  41. package/dist/credentials.d.ts +34 -0
  42. package/dist/credentials.d.ts.map +1 -0
  43. package/dist/credentials.js +68 -0
  44. package/dist/credentials.js.map +1 -0
  45. package/dist/identity.d.ts +42 -0
  46. package/dist/identity.d.ts.map +1 -0
  47. package/dist/identity.js +201 -0
  48. package/dist/identity.js.map +1 -0
  49. package/dist/index.d.ts +9 -0
  50. package/dist/index.d.ts.map +1 -0
  51. package/dist/index.js +0 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/shipper.d.ts +49 -0
  54. package/dist/shipper.d.ts.map +1 -0
  55. package/dist/shipper.js +110 -0
  56. package/dist/shipper.js.map +1 -0
  57. package/dist/transcript.d.ts +52 -0
  58. package/dist/transcript.d.ts.map +1 -0
  59. package/dist/transcript.js +216 -0
  60. package/dist/transcript.js.map +1 -0
  61. package/package.json +69 -0
  62. package/src/hooks/session-end.sh +5 -0
  63. package/src/hooks/session-start.sh +5 -0
  64. package/src/hooks/stop.sh +5 -0
@@ -0,0 +1,49 @@
1
+ import { type ResolvedCredentials } from './credentials.js';
2
+ export interface Batch {
3
+ readonly projectId: string;
4
+ readonly projectName?: string;
5
+ readonly sessionId: string;
6
+ readonly payload: string;
7
+ /**
8
+ * Origin discriminator. Omitted by the legacy wrap path; the API
9
+ * defaults missing values to 'terminal' so old callers keep working.
10
+ */
11
+ readonly source?: 'terminal' | 'claude-code';
12
+ }
13
+ export interface ShipOptions {
14
+ readonly serverUrl: string;
15
+ readonly cwd: string;
16
+ readonly fetchImpl?: typeof fetch;
17
+ readonly timeoutMs?: number;
18
+ /** When true, skip the network attempt entirely and spool. Useful for tests. */
19
+ readonly forceSpool?: boolean;
20
+ /**
21
+ * Optional explicit credentials. Defaults to resolving via env then
22
+ * `~/.contextify/credentials.json`. When null, the request goes
23
+ * unauthenticated (server falls back to LEGACY_TENANT_ID in dev).
24
+ * Tests can override this to assert the auth header is present.
25
+ */
26
+ readonly credentials?: ResolvedCredentials | null;
27
+ }
28
+ export interface ShipResult {
29
+ readonly status: 'sent' | 'spooled' | 'error';
30
+ readonly statusCode?: number;
31
+ readonly spoolPath?: string;
32
+ readonly error?: string;
33
+ }
34
+ export declare const SPOOL_DIR = ".contextify/spool";
35
+ export declare function shipBatch(batch: Batch, opts: ShipOptions): Promise<ShipResult>;
36
+ export declare function spoolBatch(batch: Batch, cwd: string, reason: string): Promise<ShipResult>;
37
+ export interface FlushOptions {
38
+ readonly serverUrl: string;
39
+ readonly cwd: string;
40
+ readonly fetchImpl?: typeof fetch;
41
+ readonly timeoutMs?: number;
42
+ }
43
+ export interface FlushResult {
44
+ readonly attempted: number;
45
+ readonly sent: number;
46
+ readonly remaining: number;
47
+ }
48
+ export declare function flushSpool(opts: FlushOptions): Promise<FlushResult>;
49
+ //# sourceMappingURL=shipper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipper.d.ts","sourceRoot":"","sources":["../src/shipper.ts"],"names":[],"mappings":"AAeA,OAAO,EAAiB,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE3E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,aAAa,CAAC;CAC9C;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,gFAAgF;IAChF,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,SAAS,sBAAsB,CAAC;AAE7C,wBAAsB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAqCpF;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAc/F;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAiCzE"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Webhook shipper with disk-spool fallback.
3
+ *
4
+ * The shipper takes a batch envelope and POSTs it gzip-compressed to the
5
+ * server's /api/telemetry/ingest endpoint. If the server is unreachable
6
+ * or returns a non-2xx, the batch is written to `<cwd>/.contextify/spool/`
7
+ * so a later `contextify ship --once` can flush it.
8
+ *
9
+ * The CLI must never block the user's terminal — so all error handling
10
+ * is silent (logs go to stderr only with --verbose, not implemented yet).
11
+ */
12
+ import { existsSync } from 'node:fs';
13
+ import { mkdir, readdir, readFile, rename, unlink, writeFile } from 'node:fs/promises';
14
+ import { join } from 'node:path';
15
+ import { gzipSync } from 'node:zlib';
16
+ import { resolveApiKey } from './credentials.js';
17
+ export const SPOOL_DIR = '.contextify/spool';
18
+ export async function shipBatch(batch, opts) {
19
+ if (opts.forceSpool) {
20
+ return spoolBatch(batch, opts.cwd, 'forceSpool');
21
+ }
22
+ const fetchImpl = opts.fetchImpl ?? fetch;
23
+ const url = new URL('/api/telemetry/ingest', opts.serverUrl).toString();
24
+ const body = gzipSync(Buffer.from(JSON.stringify(batch), 'utf8'));
25
+ const ctrl = new AbortController();
26
+ const timeout = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 10_000);
27
+ try {
28
+ // Resolve credentials lazily so call sites can inject test fixtures
29
+ // via `opts.credentials`. `undefined` triggers the default lookup;
30
+ // `null` means "send no Authorization header" (explicit unauth).
31
+ const creds = opts.credentials === undefined ? resolveApiKey() : opts.credentials;
32
+ const headers = {
33
+ 'content-type': 'application/json',
34
+ 'content-encoding': 'gzip',
35
+ };
36
+ if (creds) {
37
+ headers.authorization = `Bearer ${creds.apiKey}`;
38
+ }
39
+ const res = await fetchImpl(url, {
40
+ method: 'POST',
41
+ headers,
42
+ body,
43
+ signal: ctrl.signal,
44
+ });
45
+ if (!res.ok) {
46
+ return spoolBatch(batch, opts.cwd, `HTTP ${res.status}`);
47
+ }
48
+ return { status: 'sent', statusCode: res.status };
49
+ }
50
+ catch (err) {
51
+ return spoolBatch(batch, opts.cwd, err.message);
52
+ }
53
+ finally {
54
+ clearTimeout(timeout);
55
+ }
56
+ }
57
+ export async function spoolBatch(batch, cwd, reason) {
58
+ try {
59
+ const dir = join(cwd, SPOOL_DIR);
60
+ if (!existsSync(dir))
61
+ await mkdir(dir, { recursive: true });
62
+ const stamp = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
63
+ const tmp = join(dir, `${stamp}.json.gz.tmp`);
64
+ const final = join(dir, `${stamp}.json.gz`);
65
+ const body = gzipSync(Buffer.from(JSON.stringify(batch), 'utf8'));
66
+ await writeFile(tmp, body);
67
+ await rename(tmp, final);
68
+ return { status: 'spooled', spoolPath: final, error: reason };
69
+ }
70
+ catch (err) {
71
+ return { status: 'error', error: err.message };
72
+ }
73
+ }
74
+ export async function flushSpool(opts) {
75
+ const dir = join(opts.cwd, SPOOL_DIR);
76
+ if (!existsSync(dir))
77
+ return { attempted: 0, sent: 0, remaining: 0 };
78
+ const files = (await readdir(dir)).filter((f) => f.endsWith('.json.gz'));
79
+ let sent = 0;
80
+ for (const file of files) {
81
+ const full = join(dir, file);
82
+ const raw = await readFile(full);
83
+ let batch;
84
+ try {
85
+ // The spool stores gzipped JSON. We could re-ship the raw gzip body
86
+ // directly but that complicates the request — decompress and let
87
+ // shipBatch re-compress so the wire shape stays consistent.
88
+ const { gunzipSync } = await import('node:zlib');
89
+ batch = JSON.parse(gunzipSync(raw).toString('utf8'));
90
+ }
91
+ catch {
92
+ // Corrupt spool entry — drop it so we don't loop forever.
93
+ await unlink(full).catch(() => { });
94
+ continue;
95
+ }
96
+ const result = await shipBatch(batch, {
97
+ serverUrl: opts.serverUrl,
98
+ cwd: opts.cwd,
99
+ fetchImpl: opts.fetchImpl,
100
+ timeoutMs: opts.timeoutMs,
101
+ });
102
+ if (result.status === 'sent') {
103
+ await unlink(full).catch(() => { });
104
+ sent += 1;
105
+ }
106
+ }
107
+ const remaining = (await readdir(dir)).filter((f) => f.endsWith('.json.gz')).length;
108
+ return { attempted: files.length, sent, remaining };
109
+ }
110
+ //# sourceMappingURL=shipper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipper.js","sourceRoot":"","sources":["../src/shipper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Transcript parser.
3
+ *
4
+ * Reads a Claude Code session JSONL (one JSON object per line) and extracts
5
+ * the most recent completed user → assistant turn, including:
6
+ * - the user's text prompt
7
+ * - the assistant's final text response
8
+ * - the sequence of *actions* the assistant executed between them
9
+ * (Bash commands, file Writes/Edits/MultiEdits, WebFetches)
10
+ *
11
+ * Read-only tool calls (Read, Grep, Glob, LS, etc.) are dropped — they're
12
+ * introspection, not durable insight, and they explode payload size for no
13
+ * extraction value. tool_result blocks are likewise dropped (the *action*
14
+ * is what's durable; the output is noise the LLM doesn't need).
15
+ *
16
+ * Pure with respect to I/O: takes the raw JSONL text, returns a turn or null.
17
+ */
18
+ /** Kinds of actions worth shipping to the worker. */
19
+ export type ActionKind = 'bash' | 'write' | 'edit' | 'multiedit' | 'webfetch';
20
+ export interface DialogAction {
21
+ readonly kind: ActionKind;
22
+ /** Single-line summary of the action (command string, file path, URL, etc.). */
23
+ readonly detail: string;
24
+ }
25
+ export interface DialogTurn {
26
+ readonly userText: string;
27
+ readonly assistantText: string;
28
+ readonly actions: ReadonlyArray<DialogAction>;
29
+ readonly userAt: string | null;
30
+ readonly assistantAt: string | null;
31
+ readonly cwd: string | null;
32
+ readonly transcriptUuid: string;
33
+ }
34
+ export interface ParseOptions {
35
+ /** Cap the assistant text at this many chars (head+tail). */
36
+ readonly maxAssistantChars?: number;
37
+ /** Cap the actions array length. Default 50. */
38
+ readonly maxActions?: number;
39
+ }
40
+ /**
41
+ * Find the latest completed user → assistant text turn in the transcript.
42
+ *
43
+ * Strategy:
44
+ * 1. Walk lines in order, indexing every text-bearing user message.
45
+ * 2. Track the latest assistant message that contains text.
46
+ * 3. Pair the latest-assistant with the closest preceding user-text.
47
+ * 4. Collect every action (Bash / Write / Edit / MultiEdit / WebFetch)
48
+ * from every line *between* those two anchors (inclusive of the
49
+ * assistant message's own tool_use blocks).
50
+ */
51
+ export declare function parseLatestTurn(jsonl: string, options?: ParseOptions): DialogTurn | null;
52
+ //# sourceMappingURL=transcript.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,qDAAqD;AACrD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;AAE9E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAmGD,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,gDAAgD;IAChD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAKD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,UAAU,GAAG,IAAI,CAgG5F"}
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Transcript parser.
3
+ *
4
+ * Reads a Claude Code session JSONL (one JSON object per line) and extracts
5
+ * the most recent completed user → assistant turn, including:
6
+ * - the user's text prompt
7
+ * - the assistant's final text response
8
+ * - the sequence of *actions* the assistant executed between them
9
+ * (Bash commands, file Writes/Edits/MultiEdits, WebFetches)
10
+ *
11
+ * Read-only tool calls (Read, Grep, Glob, LS, etc.) are dropped — they're
12
+ * introspection, not durable insight, and they explode payload size for no
13
+ * extraction value. tool_result blocks are likewise dropped (the *action*
14
+ * is what's durable; the output is noise the LLM doesn't need).
15
+ *
16
+ * Pure with respect to I/O: takes the raw JSONL text, returns a turn or null.
17
+ */
18
+ /** Concatenate all `type:text` blocks; ignore tool_use/tool_result/etc. */
19
+ function extractText(message) {
20
+ if (!message)
21
+ return '';
22
+ const c = message.content;
23
+ if (typeof c === 'string')
24
+ return c;
25
+ if (!Array.isArray(c))
26
+ return '';
27
+ const parts = [];
28
+ for (const block of c) {
29
+ if (!block || typeof block !== 'object')
30
+ continue;
31
+ const b = block;
32
+ if (b.type === 'text' && typeof b.text === 'string' && b.text.trim().length > 0) {
33
+ parts.push(b.text);
34
+ }
35
+ }
36
+ return parts.join('\n\n');
37
+ }
38
+ const MAX_DETAIL_CHARS = 400;
39
+ /**
40
+ * Map a tool_use block to a DialogAction, or null if it's a read-only
41
+ * inspection tool we want to drop on the floor.
42
+ */
43
+ function classifyToolUse(block) {
44
+ const name = typeof block.name === 'string' ? block.name : '';
45
+ const input = (block.input ?? {});
46
+ switch (name) {
47
+ case 'Bash': {
48
+ const cmd = typeof input.command === 'string' ? input.command : '';
49
+ if (cmd.trim().length === 0)
50
+ return null;
51
+ return { kind: 'bash', detail: truncateDetail(cmd) };
52
+ }
53
+ case 'Write': {
54
+ const fp = typeof input.file_path === 'string' ? input.file_path : '';
55
+ if (!fp)
56
+ return null;
57
+ return { kind: 'write', detail: truncateDetail(fp) };
58
+ }
59
+ case 'Edit': {
60
+ const fp = typeof input.file_path === 'string' ? input.file_path : '';
61
+ if (!fp)
62
+ return null;
63
+ return { kind: 'edit', detail: truncateDetail(fp) };
64
+ }
65
+ case 'MultiEdit': {
66
+ const fp = typeof input.file_path === 'string' ? input.file_path : '';
67
+ if (!fp)
68
+ return null;
69
+ return { kind: 'multiedit', detail: truncateDetail(fp) };
70
+ }
71
+ case 'WebFetch': {
72
+ const url = typeof input.url === 'string' ? input.url : '';
73
+ if (!url)
74
+ return null;
75
+ return { kind: 'webfetch', detail: truncateDetail(url) };
76
+ }
77
+ // Read-only inspections (Read, Grep, Glob, LS, NotebookRead, etc.) are
78
+ // skipped intentionally — high frequency, low signal.
79
+ default:
80
+ return null;
81
+ }
82
+ }
83
+ function truncateDetail(text) {
84
+ if (text.length <= MAX_DETAIL_CHARS)
85
+ return text;
86
+ return `${text.slice(0, MAX_DETAIL_CHARS - 3)}...`;
87
+ }
88
+ /** Collect tool_use actions from a single message body. */
89
+ function extractActions(message) {
90
+ if (!message)
91
+ return [];
92
+ const c = message.content;
93
+ if (!Array.isArray(c))
94
+ return [];
95
+ const out = [];
96
+ for (const block of c) {
97
+ if (!block || typeof block !== 'object')
98
+ continue;
99
+ const b = block;
100
+ if (b.type !== 'tool_use')
101
+ continue;
102
+ const action = classifyToolUse(b);
103
+ if (action)
104
+ out.push(action);
105
+ }
106
+ return out;
107
+ }
108
+ const DEFAULT_ASSISTANT_CAP = 50_000;
109
+ const DEFAULT_ACTIONS_CAP = 50;
110
+ /**
111
+ * Find the latest completed user → assistant text turn in the transcript.
112
+ *
113
+ * Strategy:
114
+ * 1. Walk lines in order, indexing every text-bearing user message.
115
+ * 2. Track the latest assistant message that contains text.
116
+ * 3. Pair the latest-assistant with the closest preceding user-text.
117
+ * 4. Collect every action (Bash / Write / Edit / MultiEdit / WebFetch)
118
+ * from every line *between* those two anchors (inclusive of the
119
+ * assistant message's own tool_use blocks).
120
+ */
121
+ export function parseLatestTurn(jsonl, options = {}) {
122
+ const cap = options.maxAssistantChars ?? DEFAULT_ASSISTANT_CAP;
123
+ const actionsCap = options.maxActions ?? DEFAULT_ACTIONS_CAP;
124
+ const lines = jsonl.split(/\r?\n/);
125
+ const userTextAt = [];
126
+ let latestAssistant = null;
127
+ // First pass: find the turn anchors.
128
+ const parsedLines = new Array(lines.length).fill(null);
129
+ for (let index = 0; index < lines.length; index += 1) {
130
+ const raw = lines[index];
131
+ if (!raw)
132
+ continue;
133
+ let parsed;
134
+ try {
135
+ parsed = JSON.parse(raw);
136
+ }
137
+ catch {
138
+ continue;
139
+ }
140
+ if (!parsed || typeof parsed !== 'object')
141
+ continue;
142
+ parsedLines[index] = parsed;
143
+ const role = parsed.message?.role;
144
+ if (parsed.type === 'user' && role === 'user') {
145
+ const text = extractText(parsed.message);
146
+ if (text.trim().length > 0) {
147
+ userTextAt.push({ index, user: { text, ts: parsed.timestamp ?? null } });
148
+ }
149
+ continue;
150
+ }
151
+ if (parsed.type === 'assistant' && role === 'assistant') {
152
+ const text = extractText(parsed.message);
153
+ if (text.trim().length === 0)
154
+ continue;
155
+ const uuid = typeof parsed.uuid === 'string' ? parsed.uuid : '';
156
+ if (!uuid)
157
+ continue;
158
+ latestAssistant = {
159
+ uuid,
160
+ text,
161
+ ts: parsed.timestamp ?? null,
162
+ cwd: typeof parsed.cwd === 'string' ? parsed.cwd : null,
163
+ index,
164
+ };
165
+ }
166
+ }
167
+ if (!latestAssistant)
168
+ return null;
169
+ const finalAssistant = latestAssistant;
170
+ // Find the closest preceding user-text by file order.
171
+ let pairedUser = null;
172
+ let pairedUserIndex = -1;
173
+ for (let i = userTextAt.length - 1; i >= 0; i -= 1) {
174
+ if (userTextAt[i].index < finalAssistant.index) {
175
+ pairedUser = userTextAt[i].user;
176
+ pairedUserIndex = userTextAt[i].index;
177
+ break;
178
+ }
179
+ }
180
+ if (!pairedUser)
181
+ return null;
182
+ // Second pass: collect actions in the [pairedUserIndex+1, finalAssistant.index] window.
183
+ // We include the assistant line itself because tool_use can live alongside text
184
+ // in the same assistant message.
185
+ const actions = [];
186
+ for (let i = pairedUserIndex + 1; i <= finalAssistant.index; i += 1) {
187
+ const parsed = parsedLines[i];
188
+ if (!parsed)
189
+ continue;
190
+ if (parsed.message?.role !== 'assistant')
191
+ continue;
192
+ for (const action of extractActions(parsed.message)) {
193
+ if (actions.length >= actionsCap)
194
+ break;
195
+ actions.push(action);
196
+ }
197
+ if (actions.length >= actionsCap)
198
+ break;
199
+ }
200
+ return {
201
+ userText: pairedUser.text,
202
+ assistantText: truncate(finalAssistant.text, cap),
203
+ actions,
204
+ userAt: pairedUser.ts,
205
+ assistantAt: finalAssistant.ts,
206
+ cwd: finalAssistant.cwd,
207
+ transcriptUuid: finalAssistant.uuid,
208
+ };
209
+ }
210
+ function truncate(text, cap) {
211
+ if (text.length <= cap)
212
+ return text;
213
+ const half = Math.floor((cap - 64) / 2);
214
+ return `${text.slice(0, half)}\n... [truncated ${text.length - cap} chars] ...\n${text.slice(-half)}`;
215
+ }
216
+ //# sourceMappingURL=transcript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcript.js","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@furkankoykiran/contextify-cli",
3
+ "version": "0.4.0",
4
+ "description": "Contextify telemetry CLI — shadows terminal sessions, ships logs to the webhook",
5
+ "homepage": "https://contextify.live",
6
+ "license": "MIT",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "type": "module",
11
+ "main": "./dist/index.js",
12
+ "bin": {
13
+ "contextify": "./dist/index.js"
14
+ },
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ },
20
+ "./hooks": {
21
+ "types": "./dist/commands/hooks.d.ts",
22
+ "import": "./dist/commands/hooks.js"
23
+ },
24
+ "./transcript": {
25
+ "types": "./dist/transcript.d.ts",
26
+ "import": "./dist/transcript.js"
27
+ },
28
+ "./identity": {
29
+ "types": "./dist/identity.d.ts",
30
+ "import": "./dist/identity.js"
31
+ },
32
+ "./install-hooks": {
33
+ "types": "./dist/commands/install-hooks.d.ts",
34
+ "import": "./dist/commands/install-hooks.js"
35
+ },
36
+ "./install": {
37
+ "types": "./dist/commands/install.d.ts",
38
+ "import": "./dist/commands/install.js"
39
+ },
40
+ "./prompt": {
41
+ "types": "./dist/commands/prompt.d.ts",
42
+ "import": "./dist/commands/prompt.js"
43
+ }
44
+ },
45
+ "files": [
46
+ "dist",
47
+ "src/hooks"
48
+ ],
49
+ "scripts": {
50
+ "build": "tsc -p tsconfig.json && chmod +x dist/index.js",
51
+ "start": "node ./dist/index.js",
52
+ "dev": "tsc -p tsconfig.json --watch",
53
+ "clean": "rm -rf dist .turbo *.tsbuildinfo",
54
+ "lint": "eslint src --max-warnings=0",
55
+ "typecheck": "tsc -p tsconfig.json --noEmit",
56
+ "test": "vitest run"
57
+ },
58
+ "devDependencies": {
59
+ "@contextify/eslint-config": "workspace:*",
60
+ "@contextify/tsconfig": "workspace:*",
61
+ "@types/node": "20.14.10",
62
+ "@typescript-eslint/eslint-plugin": "8.18.2",
63
+ "@typescript-eslint/parser": "8.18.2",
64
+ "eslint": "8.57.1",
65
+ "eslint-config-prettier": "9.1.0",
66
+ "typescript": "5.5.4",
67
+ "vitest": "2.1.8"
68
+ }
69
+ }
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ # Contextify hook — SessionEnd.
3
+ # Pipes the Claude Code hook stdin into `contextify hooks session-end`.
4
+ # Silent on success, never blocks.
5
+ exec contextify hooks session-end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ # Contextify hook — SessionStart.
3
+ # Pipes the Claude Code hook stdin into `contextify hooks session-start`.
4
+ # Silent on success, never blocks.
5
+ exec contextify hooks session-start
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ # Contextify hook — Stop.
3
+ # Pipes the Claude Code hook stdin into `contextify hooks stop`.
4
+ # Silent on success, never blocks.
5
+ exec contextify hooks stop