@djolex999/vir-cli 0.1.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.
Files changed (69) hide show
  1. package/CLAUDE.md +149 -0
  2. package/LICENSE +21 -0
  3. package/README.md +155 -0
  4. package/dist/claude/updater.js +230 -0
  5. package/dist/claude/updater.js.map +1 -0
  6. package/dist/cli.js +779 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/config.js +82 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/daemon/launchd.js +93 -0
  11. package/dist/daemon/launchd.js.map +1 -0
  12. package/dist/dedupe/detector.js +159 -0
  13. package/dist/dedupe/detector.js.map +1 -0
  14. package/dist/dedupe/merger.js +116 -0
  15. package/dist/dedupe/merger.js.map +1 -0
  16. package/dist/lint/linter.js +224 -0
  17. package/dist/lint/linter.js.map +1 -0
  18. package/dist/pipeline/distiller.js +208 -0
  19. package/dist/pipeline/distiller.js.map +1 -0
  20. package/dist/pipeline/filter.js +28 -0
  21. package/dist/pipeline/filter.js.map +1 -0
  22. package/dist/pipeline/parser.js +109 -0
  23. package/dist/pipeline/parser.js.map +1 -0
  24. package/dist/pipeline/run.js +312 -0
  25. package/dist/pipeline/run.js.map +1 -0
  26. package/dist/pipeline/scanner.js +47 -0
  27. package/dist/pipeline/scanner.js.map +1 -0
  28. package/dist/pipeline/scrubber.js +51 -0
  29. package/dist/pipeline/scrubber.js.map +1 -0
  30. package/dist/pipeline/summarizer.js +162 -0
  31. package/dist/pipeline/summarizer.js.map +1 -0
  32. package/dist/pipeline/types.js +2 -0
  33. package/dist/pipeline/types.js.map +1 -0
  34. package/dist/pipeline/writer.js +195 -0
  35. package/dist/pipeline/writer.js.map +1 -0
  36. package/dist/search/embedder.js +93 -0
  37. package/dist/search/embedder.js.map +1 -0
  38. package/dist/search/retriever.js +212 -0
  39. package/dist/search/retriever.js.map +1 -0
  40. package/dist/search/synthesizer.js +26 -0
  41. package/dist/search/synthesizer.js.map +1 -0
  42. package/dist/state/db.js +309 -0
  43. package/dist/state/db.js.map +1 -0
  44. package/dist/ui/display.js +148 -0
  45. package/dist/ui/display.js.map +1 -0
  46. package/package.json +50 -0
  47. package/src/claude/updater.ts +273 -0
  48. package/src/cli.ts +953 -0
  49. package/src/config.ts +89 -0
  50. package/src/daemon/launchd.ts +115 -0
  51. package/src/dedupe/detector.ts +197 -0
  52. package/src/dedupe/merger.ts +172 -0
  53. package/src/lint/linter.ts +286 -0
  54. package/src/pipeline/distiller.ts +280 -0
  55. package/src/pipeline/filter.ts +43 -0
  56. package/src/pipeline/parser.ts +118 -0
  57. package/src/pipeline/run.ts +378 -0
  58. package/src/pipeline/scanner.ts +51 -0
  59. package/src/pipeline/scrubber.ts +55 -0
  60. package/src/pipeline/summarizer.ts +204 -0
  61. package/src/pipeline/types.ts +41 -0
  62. package/src/pipeline/writer.ts +242 -0
  63. package/src/search/embedder.ts +88 -0
  64. package/src/search/retriever.ts +255 -0
  65. package/src/search/synthesizer.ts +45 -0
  66. package/src/state/db.ts +451 -0
  67. package/src/ui/display.ts +184 -0
  68. package/tsconfig.json +23 -0
  69. package/vir-flow.html +708 -0
@@ -0,0 +1,224 @@
1
+ import { readFileSync, readdirSync, statSync } from "node:fs";
2
+ import { join, relative } from "node:path";
3
+ import { buildAnthropicClient, callLLM, normalizeModelName, withRateLimitRetry, } from "../pipeline/distiller.js";
4
+ import { kebab } from "../pipeline/writer.js";
5
+ const SKIP_BASENAMES = new Set(["index.md", "log.md"]);
6
+ const SKIP_DIRS = new Set(["projects"]);
7
+ const CATEGORY_DIRS = ["patterns", "gotchas", "decisions", "tools"];
8
+ const STALE_AGE_DAYS = 90;
9
+ const RECENT_AGE_DAYS = 30;
10
+ const MAX_CONTRADICTION_PAIRS = 20;
11
+ export function orphanCheck(cfg) {
12
+ const notes = loadVaultNotes(cfg);
13
+ const idSet = new Set(notes.map((n) => n.id));
14
+ const outgoing = new Map();
15
+ const incoming = new Map();
16
+ for (const n of notes) {
17
+ const targets = new Set();
18
+ const linkRe = /\[\[([^\]\n]+?)\]\]/g;
19
+ let m;
20
+ while ((m = linkRe.exec(n.raw)) !== null) {
21
+ const inner = (m[1] ?? "").trim();
22
+ if (inner.length === 0)
23
+ continue;
24
+ const target = inner.split("|")[0]?.trim() ?? inner;
25
+ const tail = target.includes("/")
26
+ ? (target.split("/").pop() ?? target)
27
+ : target;
28
+ // Only count links that resolve to actual note files; Project/Category
29
+ // virtual nodes (e.g. [[growthq]], [[pattern]]) don't count.
30
+ if (idSet.has(tail) && tail !== n.id) {
31
+ targets.add(tail);
32
+ if (!incoming.has(tail))
33
+ incoming.set(tail, new Set());
34
+ incoming.get(tail).add(n.id);
35
+ }
36
+ }
37
+ outgoing.set(n.id, targets);
38
+ }
39
+ const orphans = [];
40
+ for (const n of notes) {
41
+ const out = outgoing.get(n.id) ?? new Set();
42
+ const inc = incoming.get(n.id) ?? new Set();
43
+ if (out.size === 0 && inc.size === 0)
44
+ orphans.push(n.noteRef);
45
+ }
46
+ orphans.sort();
47
+ return { orphans };
48
+ }
49
+ export function stalenessCheck(_cfg, db) {
50
+ const rows = db.listDistilled();
51
+ const now = Date.now();
52
+ const day = 24 * 60 * 60 * 1000;
53
+ const parsed = rows
54
+ .map((r) => ({ r, t: r.startedAt ? Date.parse(r.startedAt) : NaN }))
55
+ .filter((x) => Number.isFinite(x.t));
56
+ const stale = [];
57
+ for (const { r, t } of parsed) {
58
+ const ageDays = Math.floor((now - t) / day);
59
+ if (ageDays < STALE_AGE_DAYS)
60
+ continue;
61
+ const projectSlug = kebab(r.project);
62
+ const hasRecentSameCat = parsed.some((x) => kebab(x.r.project) === projectSlug &&
63
+ x.r.category === r.category &&
64
+ x.t > t &&
65
+ (now - x.t) / day <= RECENT_AGE_DAYS);
66
+ if (!hasRecentSameCat)
67
+ continue;
68
+ const newerSameProj = parsed.filter((x) => kebab(x.r.project) === projectSlug && x.t > t).length;
69
+ stale.push({
70
+ relPath: noteRef(r),
71
+ project: projectSlug,
72
+ ageDays,
73
+ newerSameProjectCount: newerSameProj,
74
+ startedAt: r.startedAt ?? "",
75
+ });
76
+ }
77
+ stale.sort((a, b) => b.ageDays - a.ageDays);
78
+ return stale;
79
+ }
80
+ export async function contradictionCheck(cfg, db) {
81
+ const rows = db.listDistilled();
82
+ const pairs = rankCandidatePairs(rows).slice(0, MAX_CONTRADICTION_PAIRS);
83
+ if (pairs.length === 0) {
84
+ return { checked: 0, contradictions: [] };
85
+ }
86
+ const client = buildAnthropicClient(cfg);
87
+ const model = normalizeModelName(cfg.models.classify, cfg.provider);
88
+ const contradictions = [];
89
+ for (const pair of pairs) {
90
+ const prompt = `Do these two knowledge notes contradict each other?
91
+ Answer JSON only: { "contradicts": boolean, "reason": "string (max 20 words)" }
92
+
93
+ Note A (topic: ${pair.a.topic}):
94
+ ${excerpt(pair.a.content)}
95
+
96
+ Note B (topic: ${pair.b.topic}):
97
+ ${excerpt(pair.b.content)}`;
98
+ try {
99
+ const text = await withRateLimitRetry(() => callLLM(cfg, client, { prompt, model, maxTokens: 200 }));
100
+ const parsed = parseContradictionResponse(text);
101
+ if (parsed.contradicts) {
102
+ contradictions.push({
103
+ a: noteRef(pair.a),
104
+ b: noteRef(pair.b),
105
+ reason: parsed.reason,
106
+ });
107
+ }
108
+ }
109
+ catch {
110
+ // ignore individual pair failures — don't abort the whole check
111
+ }
112
+ }
113
+ return { checked: pairs.length, contradictions };
114
+ }
115
+ function rankCandidatePairs(rows) {
116
+ const pairs = [];
117
+ for (let i = 0; i < rows.length; i += 1) {
118
+ const a = rows[i];
119
+ if (!a)
120
+ continue;
121
+ for (let j = i + 1; j < rows.length; j += 1) {
122
+ const b = rows[j];
123
+ if (!b)
124
+ continue;
125
+ const sameProjCat = kebab(a.project) === kebab(b.project) && a.category === b.category;
126
+ const sharedTokens = countSharedTokens(a.topic, b.topic);
127
+ if (!sameProjCat && sharedTokens < 2)
128
+ continue;
129
+ const score = (sameProjCat ? 2 : 0) + sharedTokens;
130
+ pairs.push({ a, b, score });
131
+ }
132
+ }
133
+ pairs.sort((x, y) => y.score - x.score);
134
+ return pairs;
135
+ }
136
+ function countSharedTokens(a, b) {
137
+ const at = new Set(kebab(a).split("-").filter((t) => t.length >= 3));
138
+ let n = 0;
139
+ for (const t of kebab(b).split("-")) {
140
+ if (t.length >= 3 && at.has(t))
141
+ n += 1;
142
+ }
143
+ return n;
144
+ }
145
+ function parseContradictionResponse(text) {
146
+ const match = text.match(/\{[\s\S]*\}/);
147
+ if (!match)
148
+ return { contradicts: false, reason: "" };
149
+ try {
150
+ const obj = JSON.parse(match[0]);
151
+ return {
152
+ contradicts: obj.contradicts === true,
153
+ reason: typeof obj.reason === "string" ? obj.reason : "",
154
+ };
155
+ }
156
+ catch {
157
+ return { contradicts: false, reason: "" };
158
+ }
159
+ }
160
+ function excerpt(s) {
161
+ return s.replace(/\s+/g, " ").trim().slice(0, 300);
162
+ }
163
+ function loadVaultNotes(cfg) {
164
+ const root = join(cfg.vaultPath, cfg.outputDir);
165
+ const files = [];
166
+ walkVault(root, files);
167
+ const notes = [];
168
+ for (const full of files) {
169
+ const rel = relative(root, full);
170
+ const parts = rel.split("/");
171
+ const base = parts[parts.length - 1] ?? "";
172
+ if (SKIP_BASENAMES.has(base))
173
+ continue;
174
+ const firstDir = parts[0] ?? "";
175
+ if (SKIP_DIRS.has(firstDir))
176
+ continue;
177
+ if (!CATEGORY_DIRS.includes(firstDir))
178
+ continue;
179
+ let raw;
180
+ try {
181
+ raw = readFileSync(full, "utf8");
182
+ }
183
+ catch {
184
+ continue;
185
+ }
186
+ notes.push({
187
+ relPath: rel,
188
+ noteRef: rel.replace(/\.md$/, ""),
189
+ id: base.replace(/\.md$/, ""),
190
+ raw,
191
+ });
192
+ }
193
+ return notes;
194
+ }
195
+ function walkVault(dir, acc) {
196
+ let entries;
197
+ try {
198
+ entries = readdirSync(dir);
199
+ }
200
+ catch {
201
+ return;
202
+ }
203
+ for (const name of entries) {
204
+ const full = join(dir, name);
205
+ let st;
206
+ try {
207
+ st = statSync(full);
208
+ }
209
+ catch {
210
+ continue;
211
+ }
212
+ if (st.isDirectory())
213
+ walkVault(full, acc);
214
+ else if (st.isFile() && name.endsWith(".md"))
215
+ acc.push(full);
216
+ }
217
+ }
218
+ function noteRef(r) {
219
+ const dir = `${r.category}s`;
220
+ const slug = kebab(r.topic);
221
+ const suffix = r.sessionId.slice(0, 8);
222
+ return `${dir}/${slug}-${suffix}`;
223
+ }
224
+ //# sourceMappingURL=linter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linter.js","sourceRoot":"","sources":["../../src/lint/linter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,208 @@
1
+ import Anthropic from "@anthropic-ai/sdk";
2
+ const CATEGORIES = ["pattern", "gotcha", "decision", "tool"];
3
+ class HttpError extends Error {
4
+ status;
5
+ constructor(status, message) {
6
+ super(message);
7
+ this.status = status;
8
+ this.name = "HttpError";
9
+ }
10
+ }
11
+ export function buildAnthropicClient(config) {
12
+ return new Anthropic({ apiKey: config.anthropicApiKey ?? "" });
13
+ }
14
+ // Canonical model IDs accepted by Kie's /claude/v1/messages endpoint.
15
+ // Anything that *starts with* one of these keys collapses to the bare ID,
16
+ // so a stray suffix in config (date stamp, accidental path fragment like
17
+ // "v1messages", etc.) can't corrupt the outgoing model string.
18
+ const KIE_CANONICAL_MODELS = ["claude-haiku-4-5", "claude-sonnet-4-6"];
19
+ export function normalizeModelName(model, provider) {
20
+ if (provider !== "kie")
21
+ return model;
22
+ for (const canonical of KIE_CANONICAL_MODELS) {
23
+ if (model.startsWith(canonical))
24
+ return canonical;
25
+ }
26
+ // Fallback: still strip a trailing -YYYYMMDD date suffix.
27
+ return model.replace(/-\d{8}$/, "");
28
+ }
29
+ async function callKie(opts) {
30
+ const response = await fetch("https://api.kie.ai/claude/v1/messages", {
31
+ method: "POST",
32
+ headers: {
33
+ Authorization: `Bearer ${opts.apiKey}`,
34
+ "Content-Type": "application/json",
35
+ },
36
+ body: JSON.stringify({
37
+ model: opts.model,
38
+ max_tokens: opts.maxTokens,
39
+ messages: [{ role: "user", content: opts.prompt }],
40
+ }),
41
+ });
42
+ if (!response.ok) {
43
+ const body = await response.text().catch(() => "");
44
+ throw new HttpError(response.status, `Kie ${response.status}: ${body.slice(0, 500)}`);
45
+ }
46
+ const data = (await response.json());
47
+ const text = data.content?.[0]?.text ?? "";
48
+ return text;
49
+ }
50
+ async function callAnthropic(opts) {
51
+ const resp = await opts.client.messages.create({
52
+ model: opts.model,
53
+ max_tokens: opts.maxTokens,
54
+ messages: [{ role: "user", content: opts.prompt }],
55
+ });
56
+ const parts = [];
57
+ for (const block of resp.content) {
58
+ if (block.type === "text")
59
+ parts.push(block.text);
60
+ }
61
+ return parts.join("\n");
62
+ }
63
+ export async function callLLM(config, client, opts) {
64
+ if (config.provider === "kie") {
65
+ return callKie({
66
+ apiKey: config.kieApiKey ?? "",
67
+ model: opts.model,
68
+ maxTokens: opts.maxTokens,
69
+ prompt: opts.prompt,
70
+ });
71
+ }
72
+ return callAnthropic({
73
+ client,
74
+ model: opts.model,
75
+ maxTokens: opts.maxTokens,
76
+ prompt: opts.prompt,
77
+ });
78
+ }
79
+ export class Distiller {
80
+ client;
81
+ cfg;
82
+ classifyModel;
83
+ distillModel;
84
+ constructor(cfg) {
85
+ this.cfg = cfg;
86
+ this.client = buildAnthropicClient(cfg);
87
+ this.classifyModel = normalizeModelName(cfg.models.classify, cfg.provider);
88
+ this.distillModel = normalizeModelName(cfg.models.distill, cfg.provider);
89
+ }
90
+ async classify(session, scrubbedSummary) {
91
+ const prompt = `Given this Claude Code session summary, output JSON only:
92
+ { "category": "pattern" | "gotcha" | "decision" | "tool",
93
+ "topic": string (2-4 words, kebab-friendly),
94
+ "project": string,
95
+ "confidence": number (0..1) }
96
+
97
+ Project slug from path: ${session.projectSlug}
98
+
99
+ Session:
100
+ ${scrubbedSummary}`;
101
+ const text = await withRateLimitRetry(() => callLLM(this.cfg, this.client, {
102
+ prompt,
103
+ model: this.classifyModel,
104
+ maxTokens: 400,
105
+ }));
106
+ return parseClassification(text, session.projectSlug);
107
+ }
108
+ async distill(session, scrubbedContent, cls) {
109
+ const prompt = `Extract durable knowledge from this Claude Code session.
110
+
111
+ Output a markdown page with these sections (no preamble, start with '## Summary'):
112
+ - ## Summary (2-3 sentences)
113
+ - ## What Was Learned
114
+ - ## Context (project: ${cls.project}, category: ${cls.category}, date: ${session.startedAt ?? "unknown"})
115
+ - ## Related
116
+
117
+ Be concise. Only include information a future developer would reuse.
118
+ Omit implementation details that won't generalize.
119
+
120
+ Session:
121
+ ${scrubbedContent}`;
122
+ const text = await withRateLimitRetry(() => callLLM(this.cfg, this.client, {
123
+ prompt,
124
+ model: this.distillModel,
125
+ maxTokens: 1500,
126
+ }));
127
+ return text.trim();
128
+ }
129
+ async run(session, scrubbedSummary, scrubbedContent) {
130
+ const cls = await this.classify(session, scrubbedSummary);
131
+ if (cls.confidence <= 0.6)
132
+ return null;
133
+ const md = await this.distill(session, scrubbedContent, cls);
134
+ return { classification: cls, markdown: md };
135
+ }
136
+ }
137
+ const RETRY_DELAYS_MS = [60_000, 120_000, 240_000];
138
+ function isRateLimit(err) {
139
+ if (!err || typeof err !== "object")
140
+ return false;
141
+ const e = err;
142
+ if (e.status === 429 || e.statusCode === 429)
143
+ return true;
144
+ if (err instanceof Anthropic.APIError && err.status === 429)
145
+ return true;
146
+ if (err instanceof HttpError && err.status === 429)
147
+ return true;
148
+ return false;
149
+ }
150
+ async function sleep(ms) {
151
+ return new Promise((r) => setTimeout(r, ms));
152
+ }
153
+ export async function withRateLimitRetry(fn) {
154
+ for (let attempt = 0; attempt < RETRY_DELAYS_MS.length; attempt += 1) {
155
+ try {
156
+ return await fn();
157
+ }
158
+ catch (err) {
159
+ if (!isRateLimit(err))
160
+ throw err;
161
+ const delay = RETRY_DELAYS_MS[attempt] ?? 240_000;
162
+ console.warn(`[vir] 429 rate limit — retry ${attempt + 1}/${RETRY_DELAYS_MS.length} in ${delay / 1000}s`);
163
+ await sleep(delay);
164
+ }
165
+ }
166
+ return await fn();
167
+ }
168
+ function parseClassification(text, fallbackProject) {
169
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
170
+ if (!jsonMatch) {
171
+ return {
172
+ category: "pattern",
173
+ topic: "unknown",
174
+ project: fallbackProject,
175
+ confidence: 0,
176
+ };
177
+ }
178
+ let obj;
179
+ try {
180
+ obj = JSON.parse(jsonMatch[0]);
181
+ }
182
+ catch {
183
+ return {
184
+ category: "pattern",
185
+ topic: "unknown",
186
+ project: fallbackProject,
187
+ confidence: 0,
188
+ };
189
+ }
190
+ const rawCat = typeof obj.category === "string" ? obj.category : "pattern";
191
+ const category = CATEGORIES.includes(rawCat)
192
+ ? rawCat
193
+ : "pattern";
194
+ const topic = typeof obj.topic === "string" && obj.topic.trim().length > 0
195
+ ? obj.topic.trim()
196
+ : "unknown";
197
+ const project = typeof obj.project === "string" && obj.project.trim().length > 0
198
+ ? obj.project.trim()
199
+ : fallbackProject;
200
+ const confidenceRaw = typeof obj.confidence === "number"
201
+ ? obj.confidence
202
+ : Number(obj.confidence ?? 0);
203
+ const confidence = Number.isFinite(confidenceRaw)
204
+ ? Math.max(0, Math.min(1, confidenceRaw))
205
+ : 0;
206
+ return { category, topic, project, confidence };
207
+ }
208
+ //# sourceMappingURL=distiller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"distiller.js","sourceRoot":"","sources":["../../src/pipeline/distiller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ const SIGNAL_REGEX = /\b(error|fixed|bug|learned|gotcha|workaround|fixed it|root cause)\b/i;
2
+ export function scoreSession(session, threshold) {
3
+ let score = 0;
4
+ const reasons = [];
5
+ if (session.lineCount > 50) {
6
+ score += 0.3;
7
+ reasons.push(`lineCount>50 (+0.3)`);
8
+ }
9
+ if (session.toolCallCount > 5) {
10
+ score += 0.3;
11
+ reasons.push(`toolCalls>5 (+0.3)`);
12
+ }
13
+ if (session.filesTouched.length > 2) {
14
+ score += 0.2;
15
+ reasons.push(`files>2 (+0.2)`);
16
+ }
17
+ if (SIGNAL_REGEX.test(session.assistantText) ||
18
+ SIGNAL_REGEX.test(session.userText)) {
19
+ score += 0.2;
20
+ reasons.push(`signal-word (+0.2)`);
21
+ }
22
+ return {
23
+ score: Math.round(score * 100) / 100,
24
+ passes: score >= threshold,
25
+ reasons,
26
+ };
27
+ }
28
+ //# sourceMappingURL=filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/pipeline/filter.ts"],"names":[],"mappings":"AAEA,MAAM,YAAY,GAAG,sEAAsE,CAAC;AAQ5F,MAAM,UAAU,YAAY,CAC1B,OAAsB,EACtB,SAAiB;IAEjB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,IAAI,OAAO,CAAC,SAAS,GAAG,EAAE,EAAE,CAAC;QAC3B,KAAK,IAAI,GAAG,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,OAAO,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;QAC9B,KAAK,IAAI,GAAG,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,OAAO,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,KAAK,IAAI,GAAG,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACjC,CAAC;IACD,IACE,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC;QACxC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EACnC,CAAC;QACD,KAAK,IAAI,GAAG,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IACrC,CAAC;IAED,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG;QACpC,MAAM,EAAE,KAAK,IAAI,SAAS;QAC1B,OAAO;KACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,109 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { basename, dirname } from "node:path";
3
+ const FILE_TOOLS = new Set([
4
+ "Read",
5
+ "Edit",
6
+ "Write",
7
+ "NotebookEdit",
8
+ "MultiEdit",
9
+ ]);
10
+ export function parseSession(path, hash) {
11
+ const raw = readFileSync(path, "utf8");
12
+ const lines = raw.split("\n").filter((l) => l.trim().length > 0);
13
+ let startedAt = null;
14
+ let endedAt = null;
15
+ let toolCallCount = 0;
16
+ const filesTouched = new Set();
17
+ const assistantBlocks = [];
18
+ const userBlocks = [];
19
+ for (const line of lines) {
20
+ let evt;
21
+ try {
22
+ evt = JSON.parse(line);
23
+ }
24
+ catch {
25
+ continue;
26
+ }
27
+ const ts = typeof evt.timestamp === "string" ? evt.timestamp : null;
28
+ if (ts) {
29
+ if (!startedAt)
30
+ startedAt = ts;
31
+ endedAt = ts;
32
+ }
33
+ const msg = evt.message ?? evt;
34
+ const role = typeof msg.role === "string" ? msg.role : evt.role;
35
+ const content = msg.content ?? evt.content;
36
+ if (Array.isArray(content)) {
37
+ for (const block of content) {
38
+ if (!block || typeof block !== "object")
39
+ continue;
40
+ const b = block;
41
+ const blockType = typeof b.type === "string" ? b.type : null;
42
+ if (blockType === "text" && typeof b.text === "string") {
43
+ if (role === "assistant")
44
+ assistantBlocks.push(b.text);
45
+ else if (role === "user")
46
+ userBlocks.push(b.text);
47
+ }
48
+ if (blockType === "tool_use") {
49
+ toolCallCount += 1;
50
+ const toolName = typeof b.name === "string" ? b.name : "";
51
+ const input = b.input ?? {};
52
+ if (FILE_TOOLS.has(toolName)) {
53
+ const fp = typeof input.file_path === "string"
54
+ ? input.file_path
55
+ : typeof input.path === "string"
56
+ ? input.path
57
+ : null;
58
+ if (fp)
59
+ filesTouched.add(fp);
60
+ }
61
+ }
62
+ }
63
+ }
64
+ else if (typeof content === "string") {
65
+ if (role === "assistant")
66
+ assistantBlocks.push(content);
67
+ else if (role === "user")
68
+ userBlocks.push(content);
69
+ }
70
+ }
71
+ const assistantText = assistantBlocks.join("\n\n");
72
+ const userText = userBlocks.join("\n\n");
73
+ const rawSummary = buildRawSummary({
74
+ userText,
75
+ assistantText,
76
+ toolCallCount,
77
+ filesTouched: [...filesTouched],
78
+ });
79
+ return {
80
+ path,
81
+ hash,
82
+ sessionId: basename(path, ".jsonl"),
83
+ projectSlug: basename(dirname(path)),
84
+ startedAt,
85
+ endedAt,
86
+ lineCount: lines.length,
87
+ toolCallCount,
88
+ filesTouched: [...filesTouched],
89
+ assistantText,
90
+ userText,
91
+ rawSummary,
92
+ };
93
+ }
94
+ function buildRawSummary(opts) {
95
+ const userPreview = truncate(opts.userText, 4000);
96
+ const assistantPreview = truncate(opts.assistantText, 8000);
97
+ return [
98
+ `# User messages\n${userPreview}`,
99
+ `# Assistant messages\n${assistantPreview}`,
100
+ `# Tool calls: ${opts.toolCallCount}`,
101
+ `# Files touched (${opts.filesTouched.length}):\n${opts.filesTouched.slice(0, 50).join("\n")}`,
102
+ ].join("\n\n");
103
+ }
104
+ function truncate(s, n) {
105
+ if (s.length <= n)
106
+ return s;
107
+ return s.slice(0, n) + `\n…[truncated ${s.length - n} chars]`;
108
+ }
109
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/pipeline/parser.ts"],"names":[],"mappings":"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"}