skill-distill 1.0.1 → 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 +177 -33
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.js +177 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -42,54 +42,144 @@ var SessionLoader = class {
|
|
|
42
42
|
|
|
43
43
|
// src/loaders/claude.ts
|
|
44
44
|
var ClaudeSessionLoader = class extends SessionLoader {
|
|
45
|
+
transcriptsPath;
|
|
46
|
+
projectsPath;
|
|
45
47
|
constructor(options = {}) {
|
|
46
48
|
super(options);
|
|
49
|
+
const claudeDir = join(homedir(), ".claude");
|
|
50
|
+
this.transcriptsPath = join(claudeDir, "transcripts");
|
|
51
|
+
this.projectsPath = join(claudeDir, "projects");
|
|
47
52
|
}
|
|
48
53
|
getDefaultBasePath() {
|
|
49
|
-
return join(homedir(), ".claude"
|
|
54
|
+
return join(homedir(), ".claude");
|
|
50
55
|
}
|
|
51
56
|
async listSessions() {
|
|
57
|
+
const sessions = [];
|
|
58
|
+
const transcriptSessions = await this.listTranscriptSessions();
|
|
59
|
+
const projectSessions = await this.listProjectSessions();
|
|
60
|
+
sessions.push(...transcriptSessions, ...projectSessions);
|
|
61
|
+
return sessions.sort(
|
|
62
|
+
(a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime()
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
async listTranscriptSessions() {
|
|
52
66
|
const sessions = [];
|
|
53
67
|
try {
|
|
54
|
-
const files = await fs.readdir(this.
|
|
68
|
+
const files = await fs.readdir(this.transcriptsPath);
|
|
55
69
|
for (const file of files) {
|
|
56
70
|
if (!file.endsWith(".jsonl") || !file.startsWith("ses_")) continue;
|
|
57
|
-
const filePath = join(this.
|
|
58
|
-
const stat = await fs.stat(filePath);
|
|
71
|
+
const filePath = join(this.transcriptsPath, file);
|
|
59
72
|
const sessionId = file.replace(".jsonl", "");
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
try {
|
|
74
|
+
const entries = await this.readTranscriptFile(filePath);
|
|
75
|
+
if (entries.length === 0) continue;
|
|
76
|
+
const userMessages = entries.filter((e) => e.type === "user");
|
|
77
|
+
const firstEntry = entries[0];
|
|
78
|
+
const lastEntry = entries[entries.length - 1];
|
|
79
|
+
sessions.push({
|
|
80
|
+
id: sessionId,
|
|
81
|
+
projectPath: "[transcripts]",
|
|
82
|
+
startTime: firstEntry.timestamp,
|
|
83
|
+
endTime: lastEntry.timestamp,
|
|
84
|
+
messageCount: userMessages.length,
|
|
85
|
+
summary: this.extractTranscriptSummary(entries)
|
|
86
|
+
});
|
|
87
|
+
} catch {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
73
90
|
}
|
|
74
|
-
} catch
|
|
75
|
-
throw new Error(`Failed to list sessions: ${error}`);
|
|
91
|
+
} catch {
|
|
76
92
|
}
|
|
77
|
-
return sessions
|
|
78
|
-
|
|
79
|
-
|
|
93
|
+
return sessions;
|
|
94
|
+
}
|
|
95
|
+
async listProjectSessions() {
|
|
96
|
+
const sessions = [];
|
|
97
|
+
const sessionMap = /* @__PURE__ */ new Map();
|
|
98
|
+
try {
|
|
99
|
+
const projectDirs = await fs.readdir(this.projectsPath);
|
|
100
|
+
for (const projectDir of projectDirs) {
|
|
101
|
+
if (projectDir.startsWith(".")) continue;
|
|
102
|
+
const projectPath = join(this.projectsPath, projectDir);
|
|
103
|
+
const stat = await fs.stat(projectPath);
|
|
104
|
+
if (!stat.isDirectory()) continue;
|
|
105
|
+
const files = await fs.readdir(projectPath);
|
|
106
|
+
for (const file of files) {
|
|
107
|
+
if (!file.endsWith(".jsonl")) continue;
|
|
108
|
+
if (file.startsWith("agent-")) continue;
|
|
109
|
+
const filePath = join(projectPath, file);
|
|
110
|
+
const sessionId = file.replace(".jsonl", "");
|
|
111
|
+
sessionMap.set(sessionId, {
|
|
112
|
+
id: sessionId,
|
|
113
|
+
filePath,
|
|
114
|
+
projectPath: this.decodeProjectPath(projectDir),
|
|
115
|
+
source: "projects"
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
for (const [sessionId, source] of sessionMap) {
|
|
120
|
+
try {
|
|
121
|
+
const entries = await this.readProjectFile(source.filePath);
|
|
122
|
+
if (entries.length === 0) continue;
|
|
123
|
+
const userMessages = entries.filter(
|
|
124
|
+
(e) => e.type === "user" && e.message?.role === "user"
|
|
125
|
+
);
|
|
126
|
+
const timestamps = entries.filter((e) => e.timestamp).map((e) => e.timestamp);
|
|
127
|
+
if (timestamps.length === 0) continue;
|
|
128
|
+
sessions.push({
|
|
129
|
+
id: sessionId,
|
|
130
|
+
projectPath: source.projectPath,
|
|
131
|
+
startTime: timestamps[0],
|
|
132
|
+
endTime: timestamps[timestamps.length - 1],
|
|
133
|
+
messageCount: userMessages.length,
|
|
134
|
+
summary: this.extractProjectSummary(entries)
|
|
135
|
+
});
|
|
136
|
+
} catch {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
}
|
|
142
|
+
return sessions;
|
|
80
143
|
}
|
|
81
144
|
async getSession(id) {
|
|
82
|
-
const
|
|
145
|
+
const transcriptPath = join(this.transcriptsPath, `${id}.jsonl`);
|
|
83
146
|
try {
|
|
84
|
-
await fs.access(
|
|
147
|
+
await fs.access(transcriptPath);
|
|
148
|
+
const entries = await this.readTranscriptFile(transcriptPath);
|
|
149
|
+
if (entries.length > 0) {
|
|
150
|
+
return this.transformTranscriptSession(id, entries);
|
|
151
|
+
}
|
|
85
152
|
} catch {
|
|
86
|
-
throw new Error(`Session not found: ${id}`);
|
|
87
153
|
}
|
|
88
|
-
const
|
|
89
|
-
if (
|
|
90
|
-
|
|
154
|
+
const projectFile = await this.findProjectSessionFile(id);
|
|
155
|
+
if (projectFile) {
|
|
156
|
+
const entries = await this.readProjectFile(projectFile.filePath);
|
|
157
|
+
return this.transformProjectSession(id, entries, projectFile.projectPath);
|
|
158
|
+
}
|
|
159
|
+
throw new Error(`Session not found: ${id}`);
|
|
160
|
+
}
|
|
161
|
+
async findProjectSessionFile(sessionId) {
|
|
162
|
+
try {
|
|
163
|
+
const projectDirs = await fs.readdir(this.projectsPath);
|
|
164
|
+
for (const projectDir of projectDirs) {
|
|
165
|
+
if (projectDir.startsWith(".")) continue;
|
|
166
|
+
const projectPath = join(this.projectsPath, projectDir);
|
|
167
|
+
const stat = await fs.stat(projectPath);
|
|
168
|
+
if (!stat.isDirectory()) continue;
|
|
169
|
+
const sessionFile = join(projectPath, `${sessionId}.jsonl`);
|
|
170
|
+
try {
|
|
171
|
+
await fs.access(sessionFile);
|
|
172
|
+
return {
|
|
173
|
+
filePath: sessionFile,
|
|
174
|
+
projectPath: this.decodeProjectPath(projectDir)
|
|
175
|
+
};
|
|
176
|
+
} catch {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} catch {
|
|
91
181
|
}
|
|
92
|
-
return
|
|
182
|
+
return null;
|
|
93
183
|
}
|
|
94
184
|
async readTranscriptFile(path) {
|
|
95
185
|
try {
|
|
@@ -100,7 +190,16 @@ var ClaudeSessionLoader = class extends SessionLoader {
|
|
|
100
190
|
return [];
|
|
101
191
|
}
|
|
102
192
|
}
|
|
103
|
-
|
|
193
|
+
async readProjectFile(path) {
|
|
194
|
+
try {
|
|
195
|
+
const content = await fs.readFile(path, "utf-8");
|
|
196
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
197
|
+
return lines.map((line) => JSON.parse(line));
|
|
198
|
+
} catch {
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
transformTranscriptSession(id, entries) {
|
|
104
203
|
const messages = [];
|
|
105
204
|
let currentAssistantContent = "";
|
|
106
205
|
let currentToolCalls = [];
|
|
@@ -111,7 +210,7 @@ var ClaudeSessionLoader = class extends SessionLoader {
|
|
|
111
210
|
if (currentAssistantContent || currentToolCalls?.length) {
|
|
112
211
|
messages.push({
|
|
113
212
|
role: "assistant",
|
|
114
|
-
content: currentAssistantContent,
|
|
213
|
+
content: currentAssistantContent.trim(),
|
|
115
214
|
timestamp: lastTimestamp,
|
|
116
215
|
toolCalls: currentToolCalls.length > 0 ? currentToolCalls : void 0,
|
|
117
216
|
toolResults: currentToolResults.length > 0 ? currentToolResults : void 0
|
|
@@ -156,19 +255,64 @@ var ClaudeSessionLoader = class extends SessionLoader {
|
|
|
156
255
|
const lastEntry = entries[entries.length - 1];
|
|
157
256
|
return {
|
|
158
257
|
id,
|
|
159
|
-
projectPath: "",
|
|
258
|
+
projectPath: "[transcripts]",
|
|
160
259
|
startTime: firstEntry.timestamp,
|
|
161
260
|
endTime: lastEntry.timestamp,
|
|
162
261
|
messages
|
|
163
262
|
};
|
|
164
263
|
}
|
|
165
|
-
|
|
264
|
+
transformProjectSession(id, entries, projectPath) {
|
|
265
|
+
const messages = [];
|
|
266
|
+
for (const entry of entries) {
|
|
267
|
+
if (entry.type === "file-history-snapshot") continue;
|
|
268
|
+
if (entry.message) {
|
|
269
|
+
const content = this.extractMessageContent(entry.message.content);
|
|
270
|
+
if (content) {
|
|
271
|
+
messages.push({
|
|
272
|
+
role: entry.message.role === "user" ? "user" : "assistant",
|
|
273
|
+
content,
|
|
274
|
+
timestamp: entry.timestamp
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const timestamps = entries.filter((e) => e.timestamp).map((e) => e.timestamp);
|
|
280
|
+
return {
|
|
281
|
+
id,
|
|
282
|
+
projectPath,
|
|
283
|
+
startTime: timestamps[0] ?? "",
|
|
284
|
+
endTime: timestamps[timestamps.length - 1] ?? "",
|
|
285
|
+
messages
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
extractMessageContent(content) {
|
|
289
|
+
if (typeof content === "string") {
|
|
290
|
+
return content;
|
|
291
|
+
}
|
|
292
|
+
if (Array.isArray(content)) {
|
|
293
|
+
return content.filter((c) => c.type === "text" && c.text).map((c) => c.text).join("\n");
|
|
294
|
+
}
|
|
295
|
+
return "";
|
|
296
|
+
}
|
|
297
|
+
decodeProjectPath(encoded) {
|
|
298
|
+
return encoded.replace(/-/g, "/").replace(/^\//, "");
|
|
299
|
+
}
|
|
300
|
+
extractTranscriptSummary(entries) {
|
|
166
301
|
const firstUserEntry = entries.find((e) => e.type === "user");
|
|
167
302
|
if (!firstUserEntry?.content) return "";
|
|
168
303
|
const content = firstUserEntry.content;
|
|
169
304
|
const firstLine = content.split("\n")[0];
|
|
170
305
|
return firstLine.length > 80 ? firstLine.slice(0, 80) + "..." : firstLine;
|
|
171
306
|
}
|
|
307
|
+
extractProjectSummary(entries) {
|
|
308
|
+
const firstUserEntry = entries.find(
|
|
309
|
+
(e) => e.type === "user" && e.message?.role === "user"
|
|
310
|
+
);
|
|
311
|
+
if (!firstUserEntry?.message) return "";
|
|
312
|
+
const content = this.extractMessageContent(firstUserEntry.message.content);
|
|
313
|
+
const firstLine = content.split("\n")[0];
|
|
314
|
+
return firstLine.length > 80 ? firstLine.slice(0, 80) + "..." : firstLine;
|
|
315
|
+
}
|
|
172
316
|
};
|
|
173
317
|
|
|
174
318
|
// src/loaders/codex.ts
|