cc-query 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/query-session.js +107 -0
- package/src/repl.js +25 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-query",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "SQL REPL for querying Claude Code session data",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"node": ">=24"
|
|
16
16
|
},
|
|
17
17
|
"bin": {
|
|
18
|
-
"cc-query": "
|
|
18
|
+
"cc-query": "bin/cc-query.js"
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
21
|
"index.js",
|
package/src/query-session.js
CHANGED
|
@@ -39,6 +39,16 @@ function formatResults(result) {
|
|
|
39
39
|
const ms = Number(val.micros) / 1000;
|
|
40
40
|
return new Date(ms).toISOString().replace("T", " ").replace("Z", "");
|
|
41
41
|
}
|
|
42
|
+
// Handle DuckDB UUID objects (returned as {hugeint: string})
|
|
43
|
+
if ("hugeint" in val) {
|
|
44
|
+
// Convert 128-bit signed decimal to UUID hex string
|
|
45
|
+
// DuckDB XORs the high bit for sorting, so flip it back
|
|
46
|
+
let n = BigInt(val.hugeint);
|
|
47
|
+
if (n < 0n) n += 1n << 128n; // Convert from signed to unsigned
|
|
48
|
+
n ^= 1n << 127n; // Flip high bit (undo DuckDB's sort optimization)
|
|
49
|
+
const hex = n.toString(16).padStart(32, "0");
|
|
50
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
51
|
+
}
|
|
42
52
|
return JSON.stringify(val, (_, v) =>
|
|
43
53
|
typeof v === "bigint" ? v.toString() : v,
|
|
44
54
|
);
|
|
@@ -334,6 +344,103 @@ export class QuerySession {
|
|
|
334
344
|
json as raw
|
|
335
345
|
FROM read_ndjson_objects('${this.#filePattern}', ignore_errors=true)
|
|
336
346
|
WHERE json->>'uuid' IS NOT NULL AND length(json->>'uuid') > 0;
|
|
347
|
+
|
|
348
|
+
-- Tool uses: All tool calls with unnested content blocks
|
|
349
|
+
CREATE OR REPLACE VIEW tool_uses AS
|
|
350
|
+
SELECT
|
|
351
|
+
m.uuid,
|
|
352
|
+
m.timestamp,
|
|
353
|
+
m.sessionId,
|
|
354
|
+
m.isAgent,
|
|
355
|
+
m.agentId,
|
|
356
|
+
m.project,
|
|
357
|
+
m.rownum,
|
|
358
|
+
block->>'name' as tool_name,
|
|
359
|
+
block->>'id' as tool_id,
|
|
360
|
+
block->'input' as tool_input,
|
|
361
|
+
row_number() OVER (PARTITION BY m.uuid ORDER BY (SELECT NULL)) - 1 as block_index
|
|
362
|
+
FROM assistant_messages m,
|
|
363
|
+
LATERAL UNNEST(CAST(message->'content' AS JSON[])) as t(block)
|
|
364
|
+
WHERE block->>'type' = 'tool_use';
|
|
365
|
+
|
|
366
|
+
-- Tool results: All tool results with duration
|
|
367
|
+
CREATE OR REPLACE VIEW tool_results AS
|
|
368
|
+
WITH array_messages AS (
|
|
369
|
+
SELECT * FROM user_messages
|
|
370
|
+
WHERE json_type(message->'content') = 'ARRAY'
|
|
371
|
+
)
|
|
372
|
+
SELECT
|
|
373
|
+
m.uuid,
|
|
374
|
+
m.timestamp,
|
|
375
|
+
m.sessionId,
|
|
376
|
+
m.isAgent,
|
|
377
|
+
m.agentId,
|
|
378
|
+
m.project,
|
|
379
|
+
m.rownum,
|
|
380
|
+
block->>'tool_use_id' as tool_use_id,
|
|
381
|
+
CAST(block->>'is_error' AS BOOLEAN) as is_error,
|
|
382
|
+
block->>'content' as result_content,
|
|
383
|
+
CAST(m.toolUseResult->>'durationMs' AS INTEGER) as duration_ms,
|
|
384
|
+
m.sourceToolAssistantUUID
|
|
385
|
+
FROM array_messages m,
|
|
386
|
+
LATERAL UNNEST(CAST(message->'content' AS JSON[])) as t(block)
|
|
387
|
+
WHERE block->>'type' = 'tool_result';
|
|
388
|
+
|
|
389
|
+
-- Token usage: Pre-cast token counts
|
|
390
|
+
CREATE OR REPLACE VIEW token_usage AS
|
|
391
|
+
SELECT
|
|
392
|
+
uuid,
|
|
393
|
+
timestamp,
|
|
394
|
+
sessionId,
|
|
395
|
+
isAgent,
|
|
396
|
+
agentId,
|
|
397
|
+
project,
|
|
398
|
+
message->>'model' as model,
|
|
399
|
+
message->>'stop_reason' as stop_reason,
|
|
400
|
+
CAST(message->'usage'->>'input_tokens' AS BIGINT) as input_tokens,
|
|
401
|
+
CAST(message->'usage'->>'output_tokens' AS BIGINT) as output_tokens,
|
|
402
|
+
CAST(message->'usage'->>'cache_read_input_tokens' AS BIGINT) as cache_read_tokens,
|
|
403
|
+
CAST(message->'usage'->>'cache_creation_input_tokens' AS BIGINT) as cache_creation_tokens
|
|
404
|
+
FROM assistant_messages
|
|
405
|
+
WHERE (message->'usage') IS NOT NULL;
|
|
406
|
+
|
|
407
|
+
-- Bash commands: Bash tool uses with extracted command
|
|
408
|
+
CREATE OR REPLACE VIEW bash_commands AS
|
|
409
|
+
SELECT
|
|
410
|
+
uuid,
|
|
411
|
+
timestamp,
|
|
412
|
+
sessionId,
|
|
413
|
+
isAgent,
|
|
414
|
+
agentId,
|
|
415
|
+
project,
|
|
416
|
+
rownum,
|
|
417
|
+
tool_id,
|
|
418
|
+
tool_input->>'command' as command,
|
|
419
|
+
tool_input->>'description' as description,
|
|
420
|
+
CAST(tool_input->>'timeout' AS INTEGER) as timeout,
|
|
421
|
+
CAST(tool_input->>'run_in_background' AS BOOLEAN) as run_in_background
|
|
422
|
+
FROM tool_uses
|
|
423
|
+
WHERE tool_name = 'Bash';
|
|
424
|
+
|
|
425
|
+
-- File operations: Read/Write/Edit/Glob/Grep with extracted paths
|
|
426
|
+
CREATE OR REPLACE VIEW file_operations AS
|
|
427
|
+
SELECT
|
|
428
|
+
uuid,
|
|
429
|
+
timestamp,
|
|
430
|
+
sessionId,
|
|
431
|
+
isAgent,
|
|
432
|
+
agentId,
|
|
433
|
+
project,
|
|
434
|
+
rownum,
|
|
435
|
+
tool_id,
|
|
436
|
+
tool_name,
|
|
437
|
+
COALESCE(
|
|
438
|
+
tool_input->>'file_path',
|
|
439
|
+
tool_input->>'path'
|
|
440
|
+
) as file_path,
|
|
441
|
+
tool_input->>'pattern' as pattern
|
|
442
|
+
FROM tool_uses
|
|
443
|
+
WHERE tool_name IN ('Read', 'Write', 'Edit', 'Glob', 'Grep');
|
|
337
444
|
`;
|
|
338
445
|
}
|
|
339
446
|
}
|
package/src/repl.js
CHANGED
|
@@ -52,15 +52,22 @@ function getHelpText() {
|
|
|
52
52
|
return `
|
|
53
53
|
Commands:
|
|
54
54
|
.help, .h Show this help
|
|
55
|
-
.schema, .s Show
|
|
55
|
+
.schema, .s Show schemas for all views
|
|
56
56
|
.schema <view> Show schema for a specific view
|
|
57
57
|
.quit, .q Exit
|
|
58
58
|
|
|
59
59
|
Views:
|
|
60
60
|
messages All messages (user, assistant, system)
|
|
61
61
|
user_messages User messages with user-specific fields
|
|
62
|
+
human_messages Human-typed messages (excludes tool results)
|
|
62
63
|
assistant_messages Assistant messages with error, requestId, etc.
|
|
63
64
|
system_messages System messages with hooks, retry info, etc.
|
|
65
|
+
raw_messages Raw JSON for each message by uuid
|
|
66
|
+
tool_uses All tool calls with unnested content blocks
|
|
67
|
+
tool_results Tool results with duration and error status
|
|
68
|
+
token_usage Token counts per assistant message
|
|
69
|
+
bash_commands Bash tool calls with extracted command
|
|
70
|
+
file_operations Read/Write/Edit/Glob/Grep with file paths
|
|
64
71
|
|
|
65
72
|
Example queries:
|
|
66
73
|
-- Count messages by type
|
|
@@ -137,7 +144,23 @@ async function handleDotCommand(command, qs) {
|
|
|
137
144
|
}
|
|
138
145
|
|
|
139
146
|
if (cmd === ".schema" || cmd === ".s") {
|
|
140
|
-
|
|
147
|
+
const views = [
|
|
148
|
+
"messages",
|
|
149
|
+
"user_messages",
|
|
150
|
+
"human_messages",
|
|
151
|
+
"assistant_messages",
|
|
152
|
+
"system_messages",
|
|
153
|
+
"raw_messages",
|
|
154
|
+
"tool_uses",
|
|
155
|
+
"tool_results",
|
|
156
|
+
"token_usage",
|
|
157
|
+
"bash_commands",
|
|
158
|
+
"file_operations",
|
|
159
|
+
];
|
|
160
|
+
for (const view of views) {
|
|
161
|
+
console.log(`\n=== ${view} ===`);
|
|
162
|
+
await executeQuery(qs, `DESCRIBE ${view}`);
|
|
163
|
+
}
|
|
141
164
|
return false;
|
|
142
165
|
}
|
|
143
166
|
|