skill-distill 1.0.0 → 1.0.3
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/dist/cli.js +236 -58
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +13 -4
- package/dist/index.js +236 -58
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -145,14 +145,23 @@ declare abstract class SessionLoader {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
declare class ClaudeSessionLoader extends SessionLoader {
|
|
148
|
+
private transcriptsPath;
|
|
149
|
+
private projectsPath;
|
|
148
150
|
constructor(options?: SessionLoaderOptions);
|
|
149
151
|
getDefaultBasePath(): string;
|
|
150
152
|
listSessions(): Promise<SessionInfo[]>;
|
|
153
|
+
private listTranscriptSessions;
|
|
154
|
+
private listProjectSessions;
|
|
151
155
|
getSession(id: string): Promise<Session>;
|
|
152
|
-
private
|
|
153
|
-
private
|
|
154
|
-
private
|
|
155
|
-
private
|
|
156
|
+
private findProjectSessionFile;
|
|
157
|
+
private readTranscriptFile;
|
|
158
|
+
private readProjectFile;
|
|
159
|
+
private transformTranscriptSession;
|
|
160
|
+
private transformProjectSession;
|
|
161
|
+
private extractMessageContent;
|
|
162
|
+
private decodeProjectPath;
|
|
163
|
+
private extractTranscriptSummary;
|
|
164
|
+
private extractProjectSummary;
|
|
156
165
|
}
|
|
157
166
|
|
|
158
167
|
declare class CodexSessionLoader extends SessionLoader {
|
package/dist/index.js
CHANGED
|
@@ -91,98 +91,276 @@ var SessionLoader = class {
|
|
|
91
91
|
|
|
92
92
|
// src/loaders/claude.ts
|
|
93
93
|
var ClaudeSessionLoader = class extends SessionLoader {
|
|
94
|
+
transcriptsPath;
|
|
95
|
+
projectsPath;
|
|
94
96
|
constructor(options = {}) {
|
|
95
97
|
super(options);
|
|
98
|
+
const claudeDir = join2(homedir2(), ".claude");
|
|
99
|
+
this.transcriptsPath = join2(claudeDir, "transcripts");
|
|
100
|
+
this.projectsPath = join2(claudeDir, "projects");
|
|
96
101
|
}
|
|
97
102
|
getDefaultBasePath() {
|
|
98
|
-
return join2(homedir2(), ".claude"
|
|
103
|
+
return join2(homedir2(), ".claude");
|
|
99
104
|
}
|
|
100
105
|
async listSessions() {
|
|
101
106
|
const sessions = [];
|
|
107
|
+
const transcriptSessions = await this.listTranscriptSessions();
|
|
108
|
+
const projectSessions = await this.listProjectSessions();
|
|
109
|
+
sessions.push(...transcriptSessions, ...projectSessions);
|
|
110
|
+
return sessions.sort(
|
|
111
|
+
(a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime()
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
async listTranscriptSessions() {
|
|
115
|
+
const sessions = [];
|
|
116
|
+
try {
|
|
117
|
+
const files = await fs2.readdir(this.transcriptsPath);
|
|
118
|
+
for (const file of files) {
|
|
119
|
+
if (!file.endsWith(".jsonl") || !file.startsWith("ses_")) continue;
|
|
120
|
+
const filePath = join2(this.transcriptsPath, file);
|
|
121
|
+
const sessionId = file.replace(".jsonl", "");
|
|
122
|
+
try {
|
|
123
|
+
const entries = await this.readTranscriptFile(filePath);
|
|
124
|
+
if (entries.length === 0) continue;
|
|
125
|
+
const userMessages = entries.filter((e) => e.type === "user");
|
|
126
|
+
const firstEntry = entries[0];
|
|
127
|
+
const lastEntry = entries[entries.length - 1];
|
|
128
|
+
sessions.push({
|
|
129
|
+
id: sessionId,
|
|
130
|
+
projectPath: "[transcripts]",
|
|
131
|
+
startTime: firstEntry.timestamp,
|
|
132
|
+
endTime: lastEntry.timestamp,
|
|
133
|
+
messageCount: userMessages.length,
|
|
134
|
+
summary: this.extractTranscriptSummary(entries)
|
|
135
|
+
});
|
|
136
|
+
} catch {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
}
|
|
142
|
+
return sessions;
|
|
143
|
+
}
|
|
144
|
+
async listProjectSessions() {
|
|
145
|
+
const sessions = [];
|
|
146
|
+
const sessionMap = /* @__PURE__ */ new Map();
|
|
102
147
|
try {
|
|
103
|
-
const projectDirs = await fs2.readdir(this.
|
|
148
|
+
const projectDirs = await fs2.readdir(this.projectsPath);
|
|
104
149
|
for (const projectDir of projectDirs) {
|
|
105
|
-
|
|
150
|
+
if (projectDir.startsWith(".")) continue;
|
|
151
|
+
const projectPath = join2(this.projectsPath, projectDir);
|
|
106
152
|
const stat = await fs2.stat(projectPath);
|
|
107
153
|
if (!stat.isDirectory()) continue;
|
|
108
|
-
const
|
|
154
|
+
const files = await fs2.readdir(projectPath);
|
|
155
|
+
for (const file of files) {
|
|
156
|
+
if (!file.endsWith(".jsonl")) continue;
|
|
157
|
+
if (file.startsWith("agent-")) continue;
|
|
158
|
+
const filePath = join2(projectPath, file);
|
|
159
|
+
const sessionId = file.replace(".jsonl", "");
|
|
160
|
+
sessionMap.set(sessionId, {
|
|
161
|
+
id: sessionId,
|
|
162
|
+
filePath,
|
|
163
|
+
projectPath: this.decodeProjectPath(projectDir),
|
|
164
|
+
source: "projects"
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
for (const [sessionId, source] of sessionMap) {
|
|
109
169
|
try {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
170
|
+
const entries = await this.readProjectFile(source.filePath);
|
|
171
|
+
if (entries.length === 0) continue;
|
|
172
|
+
const userMessages = entries.filter(
|
|
173
|
+
(e) => e.type === "user" && e.message?.role === "user"
|
|
174
|
+
);
|
|
175
|
+
const timestamps = entries.filter((e) => e.timestamp).map((e) => e.timestamp);
|
|
176
|
+
if (timestamps.length === 0) continue;
|
|
177
|
+
sessions.push({
|
|
178
|
+
id: sessionId,
|
|
179
|
+
projectPath: source.projectPath,
|
|
180
|
+
startTime: timestamps[0],
|
|
181
|
+
endTime: timestamps[timestamps.length - 1],
|
|
182
|
+
messageCount: userMessages.length,
|
|
183
|
+
summary: this.extractProjectSummary(entries)
|
|
184
|
+
});
|
|
126
185
|
} catch {
|
|
127
186
|
continue;
|
|
128
187
|
}
|
|
129
188
|
}
|
|
130
|
-
} catch
|
|
131
|
-
throw new Error(`Failed to list sessions: ${error}`);
|
|
189
|
+
} catch {
|
|
132
190
|
}
|
|
133
191
|
return sessions;
|
|
134
192
|
}
|
|
135
193
|
async getSession(id) {
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
194
|
+
const transcriptPath = join2(this.transcriptsPath, `${id}.jsonl`);
|
|
195
|
+
try {
|
|
196
|
+
await fs2.access(transcriptPath);
|
|
197
|
+
const entries = await this.readTranscriptFile(transcriptPath);
|
|
198
|
+
if (entries.length > 0) {
|
|
199
|
+
return this.transformTranscriptSession(id, entries);
|
|
200
|
+
}
|
|
201
|
+
} catch {
|
|
202
|
+
}
|
|
203
|
+
const projectFile = await this.findProjectSessionFile(id);
|
|
204
|
+
if (projectFile) {
|
|
205
|
+
const entries = await this.readProjectFile(projectFile.filePath);
|
|
206
|
+
return this.transformProjectSession(id, entries, projectFile.projectPath);
|
|
207
|
+
}
|
|
208
|
+
throw new Error(`Session not found: ${id}`);
|
|
209
|
+
}
|
|
210
|
+
async findProjectSessionFile(sessionId) {
|
|
211
|
+
try {
|
|
212
|
+
const projectDirs = await fs2.readdir(this.projectsPath);
|
|
213
|
+
for (const projectDir of projectDirs) {
|
|
214
|
+
if (projectDir.startsWith(".")) continue;
|
|
215
|
+
const projectPath = join2(this.projectsPath, projectDir);
|
|
216
|
+
const stat = await fs2.stat(projectPath);
|
|
217
|
+
if (!stat.isDirectory()) continue;
|
|
218
|
+
const sessionFile = join2(projectPath, `${sessionId}.jsonl`);
|
|
219
|
+
try {
|
|
220
|
+
await fs2.access(sessionFile);
|
|
221
|
+
return {
|
|
222
|
+
filePath: sessionFile,
|
|
223
|
+
projectPath: this.decodeProjectPath(projectDir)
|
|
224
|
+
};
|
|
225
|
+
} catch {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
} catch {
|
|
140
230
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
async readTranscriptFile(path) {
|
|
234
|
+
try {
|
|
235
|
+
const content = await fs2.readFile(path, "utf-8");
|
|
236
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
237
|
+
return lines.map((line) => JSON.parse(line));
|
|
238
|
+
} catch {
|
|
239
|
+
return [];
|
|
145
240
|
}
|
|
146
|
-
return this.transformSession(data, sessionInfo.projectPath);
|
|
147
241
|
}
|
|
148
|
-
async
|
|
242
|
+
async readProjectFile(path) {
|
|
149
243
|
try {
|
|
150
244
|
const content = await fs2.readFile(path, "utf-8");
|
|
151
|
-
|
|
245
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
246
|
+
return lines.map((line) => JSON.parse(line));
|
|
152
247
|
} catch {
|
|
153
|
-
return
|
|
248
|
+
return [];
|
|
154
249
|
}
|
|
155
250
|
}
|
|
156
|
-
|
|
251
|
+
transformTranscriptSession(id, entries) {
|
|
252
|
+
const messages = [];
|
|
253
|
+
let currentAssistantContent = "";
|
|
254
|
+
let currentToolCalls = [];
|
|
255
|
+
let currentToolResults = [];
|
|
256
|
+
let lastTimestamp = "";
|
|
257
|
+
for (const entry of entries) {
|
|
258
|
+
if (entry.type === "user") {
|
|
259
|
+
if (currentAssistantContent || currentToolCalls?.length) {
|
|
260
|
+
messages.push({
|
|
261
|
+
role: "assistant",
|
|
262
|
+
content: currentAssistantContent.trim(),
|
|
263
|
+
timestamp: lastTimestamp,
|
|
264
|
+
toolCalls: currentToolCalls.length > 0 ? currentToolCalls : void 0,
|
|
265
|
+
toolResults: currentToolResults.length > 0 ? currentToolResults : void 0
|
|
266
|
+
});
|
|
267
|
+
currentAssistantContent = "";
|
|
268
|
+
currentToolCalls = [];
|
|
269
|
+
currentToolResults = [];
|
|
270
|
+
}
|
|
271
|
+
messages.push({
|
|
272
|
+
role: "user",
|
|
273
|
+
content: entry.content ?? "",
|
|
274
|
+
timestamp: entry.timestamp
|
|
275
|
+
});
|
|
276
|
+
} else if (entry.type === "assistant") {
|
|
277
|
+
currentAssistantContent += (entry.content ?? "") + "\n";
|
|
278
|
+
lastTimestamp = entry.timestamp;
|
|
279
|
+
} else if (entry.type === "tool_use") {
|
|
280
|
+
currentToolCalls.push({
|
|
281
|
+
name: entry.tool_name ?? "unknown",
|
|
282
|
+
input: entry.tool_input ?? {}
|
|
283
|
+
});
|
|
284
|
+
lastTimestamp = entry.timestamp;
|
|
285
|
+
} else if (entry.type === "tool_result") {
|
|
286
|
+
currentToolResults.push({
|
|
287
|
+
name: entry.tool_name ?? "unknown",
|
|
288
|
+
output: entry.tool_output?.preview ?? "",
|
|
289
|
+
success: true
|
|
290
|
+
});
|
|
291
|
+
lastTimestamp = entry.timestamp;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (currentAssistantContent || currentToolCalls?.length) {
|
|
295
|
+
messages.push({
|
|
296
|
+
role: "assistant",
|
|
297
|
+
content: currentAssistantContent.trim(),
|
|
298
|
+
timestamp: lastTimestamp,
|
|
299
|
+
toolCalls: currentToolCalls.length > 0 ? currentToolCalls : void 0,
|
|
300
|
+
toolResults: currentToolResults.length > 0 ? currentToolResults : void 0
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
const firstEntry = entries[0];
|
|
304
|
+
const lastEntry = entries[entries.length - 1];
|
|
157
305
|
return {
|
|
158
|
-
id
|
|
159
|
-
projectPath,
|
|
160
|
-
startTime:
|
|
161
|
-
endTime:
|
|
162
|
-
messages
|
|
306
|
+
id,
|
|
307
|
+
projectPath: "[transcripts]",
|
|
308
|
+
startTime: firstEntry.timestamp,
|
|
309
|
+
endTime: lastEntry.timestamp,
|
|
310
|
+
messages
|
|
163
311
|
};
|
|
164
312
|
}
|
|
165
|
-
|
|
313
|
+
transformProjectSession(id, entries, projectPath) {
|
|
314
|
+
const messages = [];
|
|
315
|
+
for (const entry of entries) {
|
|
316
|
+
if (entry.type === "file-history-snapshot") continue;
|
|
317
|
+
if (entry.message) {
|
|
318
|
+
const content = this.extractMessageContent(entry.message.content);
|
|
319
|
+
if (content) {
|
|
320
|
+
messages.push({
|
|
321
|
+
role: entry.message.role === "user" ? "user" : "assistant",
|
|
322
|
+
content,
|
|
323
|
+
timestamp: entry.timestamp
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const timestamps = entries.filter((e) => e.timestamp).map((e) => e.timestamp);
|
|
166
329
|
return {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
input: t.input
|
|
173
|
-
})),
|
|
174
|
-
toolResults: msg.toolResultBlocks?.map((t) => ({
|
|
175
|
-
name: t.toolUseId,
|
|
176
|
-
output: t.content,
|
|
177
|
-
success: !t.isError
|
|
178
|
-
}))
|
|
330
|
+
id,
|
|
331
|
+
projectPath,
|
|
332
|
+
startTime: timestamps[0] ?? "",
|
|
333
|
+
endTime: timestamps[timestamps.length - 1] ?? "",
|
|
334
|
+
messages
|
|
179
335
|
};
|
|
180
336
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
337
|
+
extractMessageContent(content) {
|
|
338
|
+
if (typeof content === "string") {
|
|
339
|
+
return content;
|
|
340
|
+
}
|
|
341
|
+
if (Array.isArray(content)) {
|
|
342
|
+
return content.filter((c) => c.type === "text" && c.text).map((c) => c.text).join("\n");
|
|
343
|
+
}
|
|
344
|
+
return "";
|
|
345
|
+
}
|
|
346
|
+
decodeProjectPath(encoded) {
|
|
347
|
+
return encoded.replace(/-/g, "/").replace(/^\//, "");
|
|
348
|
+
}
|
|
349
|
+
extractTranscriptSummary(entries) {
|
|
350
|
+
const firstUserEntry = entries.find((e) => e.type === "user");
|
|
351
|
+
if (!firstUserEntry?.content) return "";
|
|
352
|
+
const content = firstUserEntry.content;
|
|
353
|
+
const firstLine = content.split("\n")[0];
|
|
354
|
+
return firstLine.length > 80 ? firstLine.slice(0, 80) + "..." : firstLine;
|
|
355
|
+
}
|
|
356
|
+
extractProjectSummary(entries) {
|
|
357
|
+
const firstUserEntry = entries.find(
|
|
358
|
+
(e) => e.type === "user" && e.message?.role === "user"
|
|
359
|
+
);
|
|
360
|
+
if (!firstUserEntry?.message) return "";
|
|
361
|
+
const content = this.extractMessageContent(firstUserEntry.message.content);
|
|
362
|
+
const firstLine = content.split("\n")[0];
|
|
363
|
+
return firstLine.length > 80 ? firstLine.slice(0, 80) + "..." : firstLine;
|
|
186
364
|
}
|
|
187
365
|
};
|
|
188
366
|
|