code-session-memory 0.3.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/README.md +290 -0
- package/dist/mcp/index.d.ts +15 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +140 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +60 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +192 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/src/chunker.d.ts +18 -0
- package/dist/src/chunker.d.ts.map +1 -0
- package/dist/src/chunker.js +149 -0
- package/dist/src/chunker.js.map +1 -0
- package/dist/src/cli.d.ts +11 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +514 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/database.d.ts +50 -0
- package/dist/src/database.d.ts.map +1 -0
- package/dist/src/database.js +238 -0
- package/dist/src/database.js.map +1 -0
- package/dist/src/embedder.d.ts +19 -0
- package/dist/src/embedder.d.ts.map +1 -0
- package/dist/src/embedder.js +80 -0
- package/dist/src/embedder.js.map +1 -0
- package/dist/src/indexer-cli-claude.d.ts +14 -0
- package/dist/src/indexer-cli-claude.d.ts.map +1 -0
- package/dist/src/indexer-cli-claude.js +67 -0
- package/dist/src/indexer-cli-claude.js.map +1 -0
- package/dist/src/indexer-cli.d.ts +16 -0
- package/dist/src/indexer-cli.d.ts.map +1 -0
- package/dist/src/indexer-cli.js +53 -0
- package/dist/src/indexer-cli.js.map +1 -0
- package/dist/src/indexer.d.ts +44 -0
- package/dist/src/indexer.d.ts.map +1 -0
- package/dist/src/indexer.js +129 -0
- package/dist/src/indexer.js.map +1 -0
- package/dist/src/session-to-md.d.ts +15 -0
- package/dist/src/session-to-md.d.ts.map +1 -0
- package/dist/src/session-to-md.js +148 -0
- package/dist/src/session-to-md.js.map +1 -0
- package/dist/src/transcript-to-messages.d.ts +32 -0
- package/dist/src/transcript-to-messages.d.ts.map +1 -0
- package/dist/src/transcript-to-messages.js +230 -0
- package/dist/src/transcript-to-messages.js.map +1 -0
- package/dist/src/types.d.ts +91 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +3 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +59 -0
- package/plugin/memory.ts +42 -0
- package/skill/memory.md +61 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.indexNewMessages = indexNewMessages;
|
|
4
|
+
exports.indexNewMessagesWithOptions = indexNewMessagesWithOptions;
|
|
5
|
+
exports.reindexSession = reindexSession;
|
|
6
|
+
const database_1 = require("./database");
|
|
7
|
+
const chunker_1 = require("./chunker");
|
|
8
|
+
const embedder_1 = require("./embedder");
|
|
9
|
+
const session_to_md_1 = require("./session-to-md");
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Core indexer (accepts an already-open DB)
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
/**
|
|
14
|
+
* Incrementally indexes new messages from a session into the vector DB.
|
|
15
|
+
*
|
|
16
|
+
* Only messages that have not been previously indexed are processed:
|
|
17
|
+
* - We store the last indexed message ID in sessions_meta.
|
|
18
|
+
* - On each call, we filter to messages that come AFTER that ID.
|
|
19
|
+
* - Each new message is converted to markdown, chunked, embedded and stored.
|
|
20
|
+
*
|
|
21
|
+
* @param db Already-open database connection (caller manages lifecycle)
|
|
22
|
+
* @param session Session metadata (id, title, directory)
|
|
23
|
+
* @param messages All messages in the session
|
|
24
|
+
* @param source Which tool produced the session ("opencode" | "claude-code")
|
|
25
|
+
* @param options Optional overrides for API key / model
|
|
26
|
+
*/
|
|
27
|
+
async function indexNewMessages(db, session, messages, source = "opencode", options = {}) {
|
|
28
|
+
if (messages.length === 0) {
|
|
29
|
+
return { indexed: 0, skipped: 0 };
|
|
30
|
+
}
|
|
31
|
+
const sessionId = session.id;
|
|
32
|
+
const sessionTitle = session.title ?? sessionId;
|
|
33
|
+
const project = session.directory ?? "";
|
|
34
|
+
// Load or initialise the session meta record
|
|
35
|
+
const meta = (0, database_1.getSessionMeta)(db, sessionId);
|
|
36
|
+
const lastIndexedId = meta?.last_indexed_message_id ?? null;
|
|
37
|
+
// Filter to only messages after the last indexed one
|
|
38
|
+
let newMessages;
|
|
39
|
+
if (lastIndexedId === null) {
|
|
40
|
+
newMessages = messages;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
const lastIdx = messages.findIndex((m) => m.info.id === lastIndexedId);
|
|
44
|
+
newMessages = lastIdx === -1 ? messages : messages.slice(lastIdx + 1);
|
|
45
|
+
}
|
|
46
|
+
if (newMessages.length === 0) {
|
|
47
|
+
return { indexed: 0, skipped: messages.length };
|
|
48
|
+
}
|
|
49
|
+
const embedder = (0, embedder_1.createEmbedder)({
|
|
50
|
+
apiKey: options.openAiApiKey,
|
|
51
|
+
model: options.embeddingModel,
|
|
52
|
+
});
|
|
53
|
+
let totalChunksIndexed = 0;
|
|
54
|
+
for (const msg of newMessages) {
|
|
55
|
+
const md = (0, session_to_md_1.messageToMarkdown)(msg);
|
|
56
|
+
if (!md.trim())
|
|
57
|
+
continue;
|
|
58
|
+
const msgUrl = `session://${sessionId}#${msg.info.id}`;
|
|
59
|
+
const chunks = (0, chunker_1.chunkMarkdown)(md, {
|
|
60
|
+
sessionId,
|
|
61
|
+
sessionTitle,
|
|
62
|
+
project,
|
|
63
|
+
baseUrl: msgUrl,
|
|
64
|
+
});
|
|
65
|
+
if (chunks.length === 0)
|
|
66
|
+
continue;
|
|
67
|
+
const texts = chunks.map((c) => c.content);
|
|
68
|
+
const embeddings = await embedder.embedBatch(texts);
|
|
69
|
+
(0, database_1.insertChunks)(db, chunks, embeddings);
|
|
70
|
+
totalChunksIndexed += chunks.length;
|
|
71
|
+
}
|
|
72
|
+
void totalChunksIndexed;
|
|
73
|
+
// Update session meta with the last message we processed
|
|
74
|
+
const lastMsg = newMessages[newMessages.length - 1];
|
|
75
|
+
(0, database_1.upsertSessionMeta)(db, {
|
|
76
|
+
session_id: sessionId,
|
|
77
|
+
session_title: sessionTitle,
|
|
78
|
+
project,
|
|
79
|
+
source,
|
|
80
|
+
last_indexed_message_id: lastMsg.info.id,
|
|
81
|
+
updated_at: Date.now(),
|
|
82
|
+
});
|
|
83
|
+
return { indexed: newMessages.length, skipped: messages.length - newMessages.length };
|
|
84
|
+
}
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Convenience wrappers (open+close their own DB connection)
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
/**
|
|
89
|
+
* Convenience wrapper that opens its own DB connection.
|
|
90
|
+
* Used by the OpenCode indexer-cli and by tests.
|
|
91
|
+
*/
|
|
92
|
+
async function indexNewMessagesWithOptions(session, messages, source = "opencode", options = {}) {
|
|
93
|
+
if (messages.length === 0)
|
|
94
|
+
return { indexed: 0, skipped: 0 };
|
|
95
|
+
const dbPath = (0, database_1.resolveDbPath)(options.dbPath);
|
|
96
|
+
const db = (0, database_1.openDatabase)({ dbPath });
|
|
97
|
+
try {
|
|
98
|
+
return await indexNewMessages(db, session, messages, source, options);
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
db.close();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Re-indexes all messages in a session from scratch.
|
|
106
|
+
* Useful for repairing a corrupted or stale index.
|
|
107
|
+
*/
|
|
108
|
+
async function reindexSession(session, messages, options = {}) {
|
|
109
|
+
const dbPath = (0, database_1.resolveDbPath)(options.dbPath);
|
|
110
|
+
const db = (0, database_1.openDatabase)({ dbPath });
|
|
111
|
+
try {
|
|
112
|
+
// Reset the last_indexed_message_id so all messages are treated as new
|
|
113
|
+
const existing = (0, database_1.getSessionMeta)(db, session.id);
|
|
114
|
+
(0, database_1.upsertSessionMeta)(db, {
|
|
115
|
+
session_id: session.id,
|
|
116
|
+
session_title: session.title ?? session.id,
|
|
117
|
+
project: session.directory ?? "",
|
|
118
|
+
source: existing?.source ?? "opencode",
|
|
119
|
+
last_indexed_message_id: null,
|
|
120
|
+
updated_at: Date.now(),
|
|
121
|
+
});
|
|
122
|
+
const result = await indexNewMessages(db, session, messages, existing?.source ?? "opencode", options);
|
|
123
|
+
return { indexed: result.indexed };
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
db.close();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=indexer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.js","sourceRoot":"","sources":["../../src/indexer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SessionInfo, FullMessage } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Converts an OpenCode session + its messages (as returned by the SDK) into
|
|
4
|
+
* a single markdown string suitable for chunking and embedding.
|
|
5
|
+
*
|
|
6
|
+
* @param session Session metadata from client.session.get()
|
|
7
|
+
* @param messages Messages array from client.session.messages()
|
|
8
|
+
*/
|
|
9
|
+
export declare function sessionToMarkdown(session: SessionInfo, messages: FullMessage[]): string;
|
|
10
|
+
/**
|
|
11
|
+
* Converts a single message to markdown (used for incremental indexing of
|
|
12
|
+
* individual new messages rather than the full session).
|
|
13
|
+
*/
|
|
14
|
+
export declare function messageToMarkdown(msg: FullMessage): string;
|
|
15
|
+
//# sourceMappingURL=session-to-md.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-to-md.d.ts","sourceRoot":"","sources":["../../src/session-to-md.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAe,MAAM,SAAS,CAAC;AAwHrE;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,WAAW,EAAE,GACtB,MAAM,CAgCR;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAE1D"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sessionToMarkdown = sessionToMarkdown;
|
|
4
|
+
exports.messageToMarkdown = messageToMarkdown;
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Part renderers
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
function renderToolPart(part) {
|
|
9
|
+
const lines = [];
|
|
10
|
+
const name = part.toolName ?? "unknown";
|
|
11
|
+
lines.push(`**Tool: ${name}**`);
|
|
12
|
+
lines.push("");
|
|
13
|
+
if (part.args !== undefined) {
|
|
14
|
+
lines.push("**Input:**");
|
|
15
|
+
lines.push("```json");
|
|
16
|
+
lines.push(JSON.stringify(part.args, null, 2));
|
|
17
|
+
lines.push("```");
|
|
18
|
+
lines.push("");
|
|
19
|
+
}
|
|
20
|
+
if (part.state === "result" && part.result !== undefined) {
|
|
21
|
+
const result = part.result;
|
|
22
|
+
const resultStr = typeof result === "string"
|
|
23
|
+
? result
|
|
24
|
+
: JSON.stringify(result, null, 2);
|
|
25
|
+
lines.push("**Output:**");
|
|
26
|
+
// Only wrap in code block if not already markdown-formatted
|
|
27
|
+
if (typeof result === "string" && !result.startsWith("```")) {
|
|
28
|
+
lines.push("```");
|
|
29
|
+
lines.push(resultStr);
|
|
30
|
+
lines.push("```");
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
lines.push(resultStr);
|
|
34
|
+
}
|
|
35
|
+
lines.push("");
|
|
36
|
+
}
|
|
37
|
+
return lines.join("\n");
|
|
38
|
+
}
|
|
39
|
+
function renderFilePart(part) {
|
|
40
|
+
if (part.filename) {
|
|
41
|
+
return `**File:** \`${part.filename}\`\n`;
|
|
42
|
+
}
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
45
|
+
function renderPart(part) {
|
|
46
|
+
switch (part.type) {
|
|
47
|
+
case "text":
|
|
48
|
+
return (part.text ?? "").trim();
|
|
49
|
+
case "tool-invocation":
|
|
50
|
+
return renderToolPart(part);
|
|
51
|
+
case "file":
|
|
52
|
+
return renderFilePart(part);
|
|
53
|
+
// Internal bookkeeping — skip silently
|
|
54
|
+
case "step-start":
|
|
55
|
+
case "step-finish":
|
|
56
|
+
case "reasoning":
|
|
57
|
+
return "";
|
|
58
|
+
default:
|
|
59
|
+
// Unknown part type — try to render text if present
|
|
60
|
+
if (part.text)
|
|
61
|
+
return part.text.trim();
|
|
62
|
+
return "";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Message renderer
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
function formatDuration(msg) {
|
|
69
|
+
const { created, completed } = msg.info.time ?? {};
|
|
70
|
+
if (created && completed) {
|
|
71
|
+
const secs = ((completed - created) / 1000).toFixed(1);
|
|
72
|
+
return `${secs}s`;
|
|
73
|
+
}
|
|
74
|
+
return "";
|
|
75
|
+
}
|
|
76
|
+
function renderMessageHeading(msg) {
|
|
77
|
+
const { role, agent, modelID } = msg.info;
|
|
78
|
+
if (role === "user") {
|
|
79
|
+
return "## User";
|
|
80
|
+
}
|
|
81
|
+
// Assistant: include agent + model + duration where available
|
|
82
|
+
const parts = [];
|
|
83
|
+
if (agent)
|
|
84
|
+
parts.push(agent);
|
|
85
|
+
if (modelID)
|
|
86
|
+
parts.push(modelID);
|
|
87
|
+
const duration = formatDuration(msg);
|
|
88
|
+
if (duration)
|
|
89
|
+
parts.push(duration);
|
|
90
|
+
const suffix = parts.length > 0 ? ` (${parts.join(" · ")})` : "";
|
|
91
|
+
return `## Assistant${suffix}`;
|
|
92
|
+
}
|
|
93
|
+
function renderMessage(msg) {
|
|
94
|
+
const heading = renderMessageHeading(msg);
|
|
95
|
+
const bodyParts = msg.parts
|
|
96
|
+
.map(renderPart)
|
|
97
|
+
.filter((s) => s.trim().length > 0);
|
|
98
|
+
if (bodyParts.length === 0)
|
|
99
|
+
return "";
|
|
100
|
+
return [heading, "", bodyParts.join("\n\n"), ""].join("\n");
|
|
101
|
+
}
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
// Session renderer
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
/**
|
|
106
|
+
* Converts an OpenCode session + its messages (as returned by the SDK) into
|
|
107
|
+
* a single markdown string suitable for chunking and embedding.
|
|
108
|
+
*
|
|
109
|
+
* @param session Session metadata from client.session.get()
|
|
110
|
+
* @param messages Messages array from client.session.messages()
|
|
111
|
+
*/
|
|
112
|
+
function sessionToMarkdown(session, messages) {
|
|
113
|
+
const title = session.title ?? session.id;
|
|
114
|
+
const directory = session.directory ?? "";
|
|
115
|
+
const created = messages[0]?.info.time?.created;
|
|
116
|
+
const updated = messages[messages.length - 1]?.info.time?.completed
|
|
117
|
+
?? messages[messages.length - 1]?.info.time?.created;
|
|
118
|
+
const header = [
|
|
119
|
+
`# ${title}`,
|
|
120
|
+
"",
|
|
121
|
+
`**Session ID:** ${session.id}`,
|
|
122
|
+
directory ? `**Project:** ${directory}` : null,
|
|
123
|
+
created
|
|
124
|
+
? `**Created:** ${new Date(created).toLocaleString()}`
|
|
125
|
+
: null,
|
|
126
|
+
updated
|
|
127
|
+
? `**Updated:** ${new Date(updated).toLocaleString()}`
|
|
128
|
+
: null,
|
|
129
|
+
"",
|
|
130
|
+
"---",
|
|
131
|
+
"",
|
|
132
|
+
]
|
|
133
|
+
.filter((l) => l !== null)
|
|
134
|
+
.join("\n");
|
|
135
|
+
const body = messages
|
|
136
|
+
.map(renderMessage)
|
|
137
|
+
.filter((s) => s.trim().length > 0)
|
|
138
|
+
.join("\n---\n\n");
|
|
139
|
+
return header + body;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Converts a single message to markdown (used for incremental indexing of
|
|
143
|
+
* individual new messages rather than the full session).
|
|
144
|
+
*/
|
|
145
|
+
function messageToMarkdown(msg) {
|
|
146
|
+
return renderMessage(msg);
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=session-to-md.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-to-md.js","sourceRoot":"","sources":["../../src/session-to-md.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a Claude Code JSONL transcript into the FullMessage[] format used
|
|
3
|
+
* by the indexer.
|
|
4
|
+
*
|
|
5
|
+
* Transcript format observations:
|
|
6
|
+
* - Each line is a JSON object with a `type` field
|
|
7
|
+
* - `type: "file-history-snapshot"` — internal snapshot, skip
|
|
8
|
+
* - `type: "user"` with `isMeta: true` — local command caveat, skip
|
|
9
|
+
* - `type: "user"` with array `content` of `tool_result` — tool output, keep
|
|
10
|
+
* - `type: "user"` with string `content` — regular user message, keep
|
|
11
|
+
* (but skip /command messages and login stdout noise)
|
|
12
|
+
* - `type: "assistant"` — may appear multiple times with the same `message.id`
|
|
13
|
+
* (streaming chunks); we keep the LAST entry per message.id
|
|
14
|
+
* - `isApiErrorMessage: true` — skip
|
|
15
|
+
* - Assistant `content` blocks of type "thinking" — skip (internal reasoning)
|
|
16
|
+
* - Assistant `content` blocks of type "tool_use" — keep (tool call)
|
|
17
|
+
* - User `content` array with `tool_result` — keep (tool output)
|
|
18
|
+
*/
|
|
19
|
+
import type { FullMessage } from "./types";
|
|
20
|
+
/**
|
|
21
|
+
* Parses a Claude Code JSONL transcript file into FullMessage[].
|
|
22
|
+
*
|
|
23
|
+
* Deduplicates streaming assistant chunks (same message.id → keep last),
|
|
24
|
+
* and pairs tool_use calls with their tool_result responses.
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseTranscript(transcriptPath: string): FullMessage[];
|
|
27
|
+
/**
|
|
28
|
+
* Derives a session title from the transcript messages.
|
|
29
|
+
* Uses the first meaningful user message text, truncated to 60 chars.
|
|
30
|
+
*/
|
|
31
|
+
export declare function deriveSessionTitle(messages: FullMessage[]): string;
|
|
32
|
+
//# sourceMappingURL=transcript-to-messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript-to-messages.d.ts","sourceRoot":"","sources":["../../src/transcript-to-messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAe,MAAM,SAAS,CAAC;AAgKxD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,WAAW,EAAE,CAgGrE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,MAAM,CAWlE"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parses a Claude Code JSONL transcript into the FullMessage[] format used
|
|
4
|
+
* by the indexer.
|
|
5
|
+
*
|
|
6
|
+
* Transcript format observations:
|
|
7
|
+
* - Each line is a JSON object with a `type` field
|
|
8
|
+
* - `type: "file-history-snapshot"` — internal snapshot, skip
|
|
9
|
+
* - `type: "user"` with `isMeta: true` — local command caveat, skip
|
|
10
|
+
* - `type: "user"` with array `content` of `tool_result` — tool output, keep
|
|
11
|
+
* - `type: "user"` with string `content` — regular user message, keep
|
|
12
|
+
* (but skip /command messages and login stdout noise)
|
|
13
|
+
* - `type: "assistant"` — may appear multiple times with the same `message.id`
|
|
14
|
+
* (streaming chunks); we keep the LAST entry per message.id
|
|
15
|
+
* - `isApiErrorMessage: true` — skip
|
|
16
|
+
* - Assistant `content` blocks of type "thinking" — skip (internal reasoning)
|
|
17
|
+
* - Assistant `content` blocks of type "tool_use" — keep (tool call)
|
|
18
|
+
* - User `content` array with `tool_result` — keep (tool output)
|
|
19
|
+
*/
|
|
20
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
21
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
|
+
};
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.parseTranscript = parseTranscript;
|
|
25
|
+
exports.deriveSessionTitle = deriveSessionTitle;
|
|
26
|
+
const fs_1 = __importDefault(require("fs"));
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Converters
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
function convertUserMessage(line) {
|
|
31
|
+
const content = line.message.content;
|
|
32
|
+
// Skip meta messages (local command caveats, /login stdout, etc.)
|
|
33
|
+
if (line.isMeta)
|
|
34
|
+
return null;
|
|
35
|
+
// Skip API error messages
|
|
36
|
+
if (line.isApiErrorMessage)
|
|
37
|
+
return null;
|
|
38
|
+
const parts = [];
|
|
39
|
+
if (typeof content === "string") {
|
|
40
|
+
const text = content.trim();
|
|
41
|
+
// Skip empty, slash-commands, and local-command XML noise
|
|
42
|
+
if (!text)
|
|
43
|
+
return null;
|
|
44
|
+
if (text.startsWith("<local-command") || text.startsWith("<command-name>"))
|
|
45
|
+
return null;
|
|
46
|
+
parts.push({ type: "text", text });
|
|
47
|
+
}
|
|
48
|
+
else if (Array.isArray(content)) {
|
|
49
|
+
// Tool results
|
|
50
|
+
for (const block of content) {
|
|
51
|
+
if (block.type === "tool_result") {
|
|
52
|
+
const resultText = typeof block.content === "string" ? block.content : "";
|
|
53
|
+
parts.push({
|
|
54
|
+
type: "tool-invocation",
|
|
55
|
+
toolName: "tool_result",
|
|
56
|
+
toolCallId: block.tool_use_id,
|
|
57
|
+
state: "result",
|
|
58
|
+
result: resultText || (block.is_error ? "(error)" : "(no output)"),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (parts.length === 0)
|
|
64
|
+
return null;
|
|
65
|
+
return {
|
|
66
|
+
info: {
|
|
67
|
+
id: line.uuid,
|
|
68
|
+
role: "user",
|
|
69
|
+
time: { created: new Date(line.timestamp).getTime() },
|
|
70
|
+
},
|
|
71
|
+
parts,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function convertAssistantMessage(line) {
|
|
75
|
+
if (line.isApiErrorMessage)
|
|
76
|
+
return null;
|
|
77
|
+
const parts = [];
|
|
78
|
+
for (const block of line.message.content) {
|
|
79
|
+
switch (block.type) {
|
|
80
|
+
case "text":
|
|
81
|
+
if (block.text?.trim()) {
|
|
82
|
+
parts.push({ type: "text", text: block.text.trim() });
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
case "tool_use":
|
|
86
|
+
parts.push({
|
|
87
|
+
type: "tool-invocation",
|
|
88
|
+
toolName: block.name ?? "unknown",
|
|
89
|
+
toolCallId: block.id,
|
|
90
|
+
state: "call",
|
|
91
|
+
args: block.input,
|
|
92
|
+
});
|
|
93
|
+
break;
|
|
94
|
+
case "thinking":
|
|
95
|
+
// Skip internal reasoning — not useful to index
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (parts.length === 0)
|
|
100
|
+
return null;
|
|
101
|
+
return {
|
|
102
|
+
info: {
|
|
103
|
+
id: line.uuid,
|
|
104
|
+
role: "assistant",
|
|
105
|
+
modelID: line.message.model,
|
|
106
|
+
time: { created: new Date(line.timestamp).getTime() },
|
|
107
|
+
},
|
|
108
|
+
parts,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Public API
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
/**
|
|
115
|
+
* Parses a Claude Code JSONL transcript file into FullMessage[].
|
|
116
|
+
*
|
|
117
|
+
* Deduplicates streaming assistant chunks (same message.id → keep last),
|
|
118
|
+
* and pairs tool_use calls with their tool_result responses.
|
|
119
|
+
*/
|
|
120
|
+
function parseTranscript(transcriptPath) {
|
|
121
|
+
const raw = fs_1.default.readFileSync(transcriptPath, "utf8");
|
|
122
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
123
|
+
// First pass: collect all lines, deduplicate assistant messages by message.id
|
|
124
|
+
// (streaming sends partial chunks — we want only the final complete message)
|
|
125
|
+
const assistantByMsgId = new Map();
|
|
126
|
+
const orderedEntries = [];
|
|
127
|
+
const seenUuids = new Set();
|
|
128
|
+
for (const raw of lines) {
|
|
129
|
+
let line;
|
|
130
|
+
try {
|
|
131
|
+
line = JSON.parse(raw);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (line.type === "file-history-snapshot")
|
|
137
|
+
continue;
|
|
138
|
+
if (line.type === "assistant") {
|
|
139
|
+
const al = line;
|
|
140
|
+
const msgId = al.message?.id;
|
|
141
|
+
if (msgId) {
|
|
142
|
+
// Always overwrite — last chunk wins (most complete content)
|
|
143
|
+
assistantByMsgId.set(msgId, al);
|
|
144
|
+
// Track insertion order by uuid (first occurrence)
|
|
145
|
+
if (!seenUuids.has(al.uuid)) {
|
|
146
|
+
seenUuids.add(al.uuid);
|
|
147
|
+
orderedEntries.push({ uuid: al.uuid, line });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (line.type === "user" || line.type === "system") {
|
|
153
|
+
const ul = line;
|
|
154
|
+
if (!seenUuids.has(ul.uuid)) {
|
|
155
|
+
seenUuids.add(ul.uuid);
|
|
156
|
+
orderedEntries.push({ uuid: ul.uuid, line });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Second pass: resolve assistant lines to their final (deduplicated) version
|
|
161
|
+
// and pair tool_use with tool_result
|
|
162
|
+
const messages = [];
|
|
163
|
+
// Build a map: tool_use_id → tool result text (from user tool_result messages)
|
|
164
|
+
const toolResults = new Map();
|
|
165
|
+
for (const { line } of orderedEntries) {
|
|
166
|
+
if (line.type !== "user")
|
|
167
|
+
continue;
|
|
168
|
+
const ul = line;
|
|
169
|
+
if (Array.isArray(ul.message.content)) {
|
|
170
|
+
for (const block of ul.message.content) {
|
|
171
|
+
if (block.type === "tool_result") {
|
|
172
|
+
toolResults.set(block.tool_use_id, block.content ?? "");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
for (const { line } of orderedEntries) {
|
|
178
|
+
if (line.type === "assistant") {
|
|
179
|
+
const al = line;
|
|
180
|
+
const msgId = al.message?.id;
|
|
181
|
+
// Use the final (deduplicated) version
|
|
182
|
+
const finalLine = msgId ? (assistantByMsgId.get(msgId) ?? al) : al;
|
|
183
|
+
// Enrich tool_use blocks with their results
|
|
184
|
+
const enriched = {
|
|
185
|
+
...finalLine,
|
|
186
|
+
message: {
|
|
187
|
+
...finalLine.message,
|
|
188
|
+
content: finalLine.message.content.map((block) => {
|
|
189
|
+
if (block.type === "tool_use" && block.id && toolResults.has(block.id)) {
|
|
190
|
+
// We'll render the result separately via tool_result user messages
|
|
191
|
+
// Just keep the call here
|
|
192
|
+
}
|
|
193
|
+
return block;
|
|
194
|
+
}),
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
const msg = convertAssistantMessage(enriched);
|
|
198
|
+
if (msg)
|
|
199
|
+
messages.push(msg);
|
|
200
|
+
}
|
|
201
|
+
else if (line.type === "user") {
|
|
202
|
+
const ul = line;
|
|
203
|
+
// Skip tool_result-only messages (already captured in assistant context)
|
|
204
|
+
// but DO include them so the indexer can see what tools returned
|
|
205
|
+
const msg = convertUserMessage(ul);
|
|
206
|
+
if (msg)
|
|
207
|
+
messages.push(msg);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return messages;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Derives a session title from the transcript messages.
|
|
214
|
+
* Uses the first meaningful user message text, truncated to 60 chars.
|
|
215
|
+
*/
|
|
216
|
+
function deriveSessionTitle(messages) {
|
|
217
|
+
for (const msg of messages) {
|
|
218
|
+
if (msg.info.role !== "user")
|
|
219
|
+
continue;
|
|
220
|
+
for (const part of msg.parts) {
|
|
221
|
+
if (part.type === "text" && part.text) {
|
|
222
|
+
const title = part.text.replace(/\s+/g, " ").trim().slice(0, 60);
|
|
223
|
+
if (title)
|
|
224
|
+
return title;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return "Claude Code Session";
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=transcript-to-messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript-to-messages.js","sourceRoot":"","sources":["../../src/transcript-to-messages.ts"],"names":[],"mappings":"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"}
|