skill-distill 1.0.1 → 1.0.4
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 +250 -64
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.js +212 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -145,13 +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>;
|
|
156
|
+
private findProjectSessionFile;
|
|
152
157
|
private readTranscriptFile;
|
|
153
|
-
private
|
|
154
|
-
private
|
|
158
|
+
private readProjectFile;
|
|
159
|
+
private transformTranscriptSession;
|
|
160
|
+
private transformProjectSession;
|
|
161
|
+
private extractMessageContent;
|
|
162
|
+
private decodeProjectPath;
|
|
163
|
+
private extractTranscriptSummary;
|
|
164
|
+
private extractProjectSummary;
|
|
155
165
|
}
|
|
156
166
|
|
|
157
167
|
declare class CodexSessionLoader extends SessionLoader {
|
package/dist/index.js
CHANGED
|
@@ -91,54 +91,144 @@ 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() {
|
|
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() {
|
|
101
115
|
const sessions = [];
|
|
102
116
|
try {
|
|
103
|
-
const files = await fs2.readdir(this.
|
|
117
|
+
const files = await fs2.readdir(this.transcriptsPath);
|
|
104
118
|
for (const file of files) {
|
|
105
119
|
if (!file.endsWith(".jsonl") || !file.startsWith("ses_")) continue;
|
|
106
|
-
const filePath = join2(this.
|
|
107
|
-
const stat = await fs2.stat(filePath);
|
|
120
|
+
const filePath = join2(this.transcriptsPath, file);
|
|
108
121
|
const sessionId = file.replace(".jsonl", "");
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
+
}
|
|
122
139
|
}
|
|
123
|
-
} catch
|
|
124
|
-
throw new Error(`Failed to list sessions: ${error}`);
|
|
140
|
+
} catch {
|
|
125
141
|
}
|
|
126
|
-
return sessions
|
|
127
|
-
|
|
128
|
-
|
|
142
|
+
return sessions;
|
|
143
|
+
}
|
|
144
|
+
async listProjectSessions() {
|
|
145
|
+
const sessions = [];
|
|
146
|
+
const sessionMap = /* @__PURE__ */ new Map();
|
|
147
|
+
try {
|
|
148
|
+
const projectDirs = await fs2.readdir(this.projectsPath);
|
|
149
|
+
for (const projectDir of projectDirs) {
|
|
150
|
+
if (projectDir.startsWith(".")) continue;
|
|
151
|
+
const projectPath = join2(this.projectsPath, projectDir);
|
|
152
|
+
const stat = await fs2.stat(projectPath);
|
|
153
|
+
if (!stat.isDirectory()) continue;
|
|
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) {
|
|
169
|
+
try {
|
|
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
|
+
});
|
|
185
|
+
} catch {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} catch {
|
|
190
|
+
}
|
|
191
|
+
return sessions;
|
|
129
192
|
}
|
|
130
193
|
async getSession(id) {
|
|
131
|
-
const
|
|
194
|
+
const transcriptPath = join2(this.transcriptsPath, `${id}.jsonl`);
|
|
132
195
|
try {
|
|
133
|
-
await fs2.access(
|
|
196
|
+
await fs2.access(transcriptPath);
|
|
197
|
+
const entries = await this.readTranscriptFile(transcriptPath);
|
|
198
|
+
if (entries.length > 0) {
|
|
199
|
+
return this.transformTranscriptSession(id, entries);
|
|
200
|
+
}
|
|
134
201
|
} catch {
|
|
135
|
-
throw new Error(`Session not found: ${id}`);
|
|
136
202
|
}
|
|
137
|
-
const
|
|
138
|
-
if (
|
|
139
|
-
|
|
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);
|
|
140
207
|
}
|
|
141
|
-
|
|
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 {
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
142
232
|
}
|
|
143
233
|
async readTranscriptFile(path) {
|
|
144
234
|
try {
|
|
@@ -149,7 +239,16 @@ var ClaudeSessionLoader = class extends SessionLoader {
|
|
|
149
239
|
return [];
|
|
150
240
|
}
|
|
151
241
|
}
|
|
152
|
-
|
|
242
|
+
async readProjectFile(path) {
|
|
243
|
+
try {
|
|
244
|
+
const content = await fs2.readFile(path, "utf-8");
|
|
245
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
246
|
+
return lines.map((line) => JSON.parse(line));
|
|
247
|
+
} catch {
|
|
248
|
+
return [];
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
transformTranscriptSession(id, entries) {
|
|
153
252
|
const messages = [];
|
|
154
253
|
let currentAssistantContent = "";
|
|
155
254
|
let currentToolCalls = [];
|
|
@@ -160,7 +259,7 @@ var ClaudeSessionLoader = class extends SessionLoader {
|
|
|
160
259
|
if (currentAssistantContent || currentToolCalls?.length) {
|
|
161
260
|
messages.push({
|
|
162
261
|
role: "assistant",
|
|
163
|
-
content: currentAssistantContent,
|
|
262
|
+
content: currentAssistantContent.trim(),
|
|
164
263
|
timestamp: lastTimestamp,
|
|
165
264
|
toolCalls: currentToolCalls.length > 0 ? currentToolCalls : void 0,
|
|
166
265
|
toolResults: currentToolResults.length > 0 ? currentToolResults : void 0
|
|
@@ -205,19 +304,64 @@ var ClaudeSessionLoader = class extends SessionLoader {
|
|
|
205
304
|
const lastEntry = entries[entries.length - 1];
|
|
206
305
|
return {
|
|
207
306
|
id,
|
|
208
|
-
projectPath: "",
|
|
307
|
+
projectPath: "[transcripts]",
|
|
209
308
|
startTime: firstEntry.timestamp,
|
|
210
309
|
endTime: lastEntry.timestamp,
|
|
211
310
|
messages
|
|
212
311
|
};
|
|
213
312
|
}
|
|
214
|
-
|
|
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);
|
|
329
|
+
return {
|
|
330
|
+
id,
|
|
331
|
+
projectPath,
|
|
332
|
+
startTime: timestamps[0] ?? "",
|
|
333
|
+
endTime: timestamps[timestamps.length - 1] ?? "",
|
|
334
|
+
messages
|
|
335
|
+
};
|
|
336
|
+
}
|
|
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) {
|
|
215
350
|
const firstUserEntry = entries.find((e) => e.type === "user");
|
|
216
351
|
if (!firstUserEntry?.content) return "";
|
|
217
352
|
const content = firstUserEntry.content;
|
|
218
353
|
const firstLine = content.split("\n")[0];
|
|
219
354
|
return firstLine.length > 80 ? firstLine.slice(0, 80) + "..." : firstLine;
|
|
220
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;
|
|
364
|
+
}
|
|
221
365
|
};
|
|
222
366
|
|
|
223
367
|
// src/loaders/codex.ts
|
|
@@ -462,7 +606,41 @@ ${userPrompts.map((p) => `- ${p}`).join("\n")}` : "";
|
|
|
462
606
|
qualityEnhancement(JSON.stringify(skill, null, 2)),
|
|
463
607
|
SYSTEM_SKILL_WRITER
|
|
464
608
|
);
|
|
465
|
-
|
|
609
|
+
try {
|
|
610
|
+
const enhanced = this.parseJson(response, z2.any());
|
|
611
|
+
const mergedSteps = (enhanced.steps ?? skill.steps ?? []).map((step) => {
|
|
612
|
+
if (typeof step === "object" && step !== null) {
|
|
613
|
+
const s = step;
|
|
614
|
+
return {
|
|
615
|
+
title: String(s.title ?? s.name ?? "Step"),
|
|
616
|
+
description: String(s.description ?? s.content ?? ""),
|
|
617
|
+
substeps: Array.isArray(s.substeps) ? s.substeps.map(String) : void 0
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
return { title: "Step", description: String(step) };
|
|
621
|
+
});
|
|
622
|
+
const mergedExamples = (enhanced.examples ?? skill.examples ?? []).map((ex) => {
|
|
623
|
+
if (typeof ex === "string") return ex;
|
|
624
|
+
if (typeof ex === "object" && ex !== null) {
|
|
625
|
+
const e = ex;
|
|
626
|
+
return String(e.text ?? e.example ?? e.content ?? JSON.stringify(ex));
|
|
627
|
+
}
|
|
628
|
+
return String(ex);
|
|
629
|
+
});
|
|
630
|
+
return {
|
|
631
|
+
metadata: enhanced.metadata ?? skill.metadata ?? { name: "Untitled", description: "", version: "1.0.0" },
|
|
632
|
+
overview: enhanced.overview ?? skill.overview ?? "",
|
|
633
|
+
triggers: enhanced.triggers ?? skill.triggers ?? [],
|
|
634
|
+
prerequisites: enhanced.prerequisites ?? skill.prerequisites ?? [],
|
|
635
|
+
steps: mergedSteps,
|
|
636
|
+
parameters: enhanced.parameters ?? skill.parameters ?? [],
|
|
637
|
+
errorHandling: enhanced.errorHandling ?? skill.errorHandling ?? {},
|
|
638
|
+
examples: mergedExamples,
|
|
639
|
+
notes: enhanced.notes ?? skill.notes ?? []
|
|
640
|
+
};
|
|
641
|
+
} catch {
|
|
642
|
+
return skill;
|
|
643
|
+
}
|
|
466
644
|
}
|
|
467
645
|
async generateDescription(intent) {
|
|
468
646
|
const response = await this.chat(
|