@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,312 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { appendFileSync } from "node:fs";
3
+ import { DAEMON_LOG_PATH, ensureVirDir } from "../config.js";
4
+ import { StateDb } from "../state/db.js";
5
+ import * as ui from "../ui/display.js";
6
+ import { Distiller } from "./distiller.js";
7
+ import { scoreSession } from "./filter.js";
8
+ import { parseSession } from "./parser.js";
9
+ import { scanSessions } from "./scanner.js";
10
+ import { scrub } from "./scrubber.js";
11
+ import { summarizeProject } from "./summarizer.js";
12
+ import { kebab, VaultWriter } from "./writer.js";
13
+ export async function runPipeline(cfg, opts = {}) {
14
+ ensureVirDir();
15
+ const db = new StateDb();
16
+ const writer = new VaultWriter(cfg, db);
17
+ const summary = {
18
+ scanned: 0,
19
+ alreadyProcessed: 0,
20
+ skippedByFilter: 0,
21
+ distilled: 0,
22
+ lowConfidence: 0,
23
+ errored: 0,
24
+ rewritten: 0,
25
+ notesWritten: [],
26
+ };
27
+ const interactive = !opts.quiet;
28
+ // File-only logging — used for the daemon run.log regardless of UI mode.
29
+ const fileLog = (msg) => {
30
+ if (!opts.logToFile)
31
+ return;
32
+ try {
33
+ appendFileSync(DAEMON_LOG_PATH, `[${new Date().toISOString()}] ${msg}\n`);
34
+ }
35
+ catch {
36
+ // ignore log errors
37
+ }
38
+ };
39
+ if (interactive) {
40
+ ui.header(opts.rewriteOnly
41
+ ? "run --rewrite-only"
42
+ : opts.full
43
+ ? "run --full"
44
+ : "run");
45
+ ui.blank();
46
+ }
47
+ fileLog(`vir run start (full=${opts.full ? "true" : "false"} rewriteOnly=${opts.rewriteOnly ? "true" : "false"})`);
48
+ if (opts.rewriteOnly) {
49
+ const rows = db.listDistilled();
50
+ fileLog(`rewrite-only: ${rows.length} distilled sessions in db`);
51
+ if (interactive) {
52
+ const sp = ui.spinner(`rewriting ${rows.length} notes`).start();
53
+ try {
54
+ for (const row of rows) {
55
+ try {
56
+ const written = await rewriteOne(writer, row);
57
+ summary.rewritten += 1;
58
+ summary.notesWritten.push(...written);
59
+ }
60
+ catch (err) {
61
+ summary.errored += 1;
62
+ fileLog(`error on ${row.path}: ${err.message}`);
63
+ }
64
+ }
65
+ sp.succeed(ui.text(`rewrote ${summary.rewritten} notes`));
66
+ }
67
+ catch (err) {
68
+ sp.fail(ui.errorColor(err.message));
69
+ throw err;
70
+ }
71
+ }
72
+ else {
73
+ for (const row of rows) {
74
+ try {
75
+ const written = await rewriteOne(writer, row);
76
+ summary.rewritten += 1;
77
+ summary.notesWritten.push(...written);
78
+ }
79
+ catch (err) {
80
+ summary.errored += 1;
81
+ fileLog(`error on ${row.path}: ${err.message}`);
82
+ }
83
+ }
84
+ }
85
+ fileLog(`vir run done — rewriteOnly rewritten=${summary.rewritten} errored=${summary.errored}`);
86
+ if (interactive) {
87
+ ui.blank();
88
+ ui.divider();
89
+ ui.summary({
90
+ rewritten: { value: summary.rewritten, color: ui.success },
91
+ errored: {
92
+ value: summary.errored,
93
+ color: summary.errored > 0 ? ui.errorColor : ui.dim,
94
+ },
95
+ });
96
+ ui.divider();
97
+ }
98
+ db.close();
99
+ return summary;
100
+ }
101
+ const distiller = new Distiller(cfg);
102
+ const newPerProject = new Map();
103
+ const scanSpinner = interactive
104
+ ? ui.spinner("scanning ~/.claude/projects").start()
105
+ : null;
106
+ let discovered;
107
+ try {
108
+ discovered = scanSessions(cfg.claudeProjectsDir);
109
+ }
110
+ catch (err) {
111
+ if (scanSpinner)
112
+ scanSpinner.fail(ui.errorColor("scan failed"));
113
+ fileLog(`scanner failed: ${err.message}`);
114
+ db.close();
115
+ return summary;
116
+ }
117
+ summary.scanned = discovered.length;
118
+ if (scanSpinner) {
119
+ scanSpinner.succeed(ui.text(`scanned ${ui.info(String(discovered.length))} ${ui.dim("jsonl files")}`));
120
+ }
121
+ fileLog(`scanned ${discovered.length} jsonl files`);
122
+ if (interactive)
123
+ ui.blank();
124
+ // Precompute how many sessions actually need LLM work so the CLI can show
125
+ // an accurate cost confirmation before we hit the API. Also surfaces the
126
+ // found/cached/new breakdown so a fresh DB never silently looks like a
127
+ // stale-cache no-op (the symptom of the state.db → vir.db rename bug).
128
+ let preflightNew = 0;
129
+ for (const found of discovered) {
130
+ if (opts.full || !db.isProcessed(found.path, found.hash))
131
+ preflightNew += 1;
132
+ }
133
+ const cached = discovered.length - preflightNew;
134
+ if (interactive) {
135
+ ui.line(ui.dim(` ${discovered.length} files found · ${cached} cached · ${preflightNew} new`));
136
+ ui.blank();
137
+ }
138
+ fileLog(`preflight: found=${discovered.length} cached=${cached} new=${preflightNew}`);
139
+ if (opts.onConfirm) {
140
+ const proceed = await opts.onConfirm(preflightNew);
141
+ if (!proceed) {
142
+ fileLog("aborted by user at cost prompt");
143
+ db.close();
144
+ return summary;
145
+ }
146
+ }
147
+ for (const found of discovered) {
148
+ try {
149
+ if (!opts.full && db.isProcessed(found.path, found.hash)) {
150
+ summary.alreadyProcessed += 1;
151
+ continue;
152
+ }
153
+ const parsed = parseSession(found.path, found.hash);
154
+ const filter = scoreSession(parsed, cfg.filterThreshold);
155
+ if (!filter.passes) {
156
+ summary.skippedByFilter += 1;
157
+ db.record({
158
+ path: found.path,
159
+ hash: found.hash,
160
+ skipped: true,
161
+ notePaths: [],
162
+ });
163
+ continue;
164
+ }
165
+ const scrubbedSummary = scrub(parsed.rawSummary);
166
+ const scrubbedContent = scrub(parsed.assistantText + "\n\n" + parsed.userText);
167
+ const note = await distiller.run(parsed, scrubbedSummary, scrubbedContent);
168
+ if (!note) {
169
+ summary.lowConfidence += 1;
170
+ db.record({
171
+ path: found.path,
172
+ hash: found.hash,
173
+ skipped: true,
174
+ notePaths: [],
175
+ });
176
+ continue;
177
+ }
178
+ const written = await writer.write(parsed, note);
179
+ summary.distilled += 1;
180
+ summary.notesWritten.push(...written);
181
+ db.record({
182
+ path: found.path,
183
+ hash: found.hash,
184
+ skipped: false,
185
+ notePaths: written,
186
+ content: note.markdown,
187
+ category: note.classification.category,
188
+ topic: note.classification.topic,
189
+ project: note.classification.project,
190
+ confidence: note.classification.confidence,
191
+ startedAt: parsed.startedAt,
192
+ });
193
+ if (interactive) {
194
+ ui.categoryRow(note.classification.category, note.classification.topic);
195
+ }
196
+ fileLog(`distilled ${parsed.sessionId.slice(0, 8)} → ${note.classification.category}/${note.classification.topic}`);
197
+ if (note.classification.confidence >= 0.8) {
198
+ notify(`Vir — new ${note.classification.category}`, `${note.classification.topic} · ${note.classification.project}`);
199
+ }
200
+ const slug = kebab(note.classification.project);
201
+ if (slug.length > 0) {
202
+ newPerProject.set(slug, (newPerProject.get(slug) ?? 0) + 1);
203
+ }
204
+ await new Promise((r) => setTimeout(r, 2000));
205
+ }
206
+ catch (err) {
207
+ summary.errored += 1;
208
+ const msg = err.message ?? String(err);
209
+ if (interactive)
210
+ ui.row(ui.errorColor(ui.CROSS), ui.text(`error: ${msg}`));
211
+ fileLog(`error on ${found.path}: ${msg}`);
212
+ try {
213
+ db.record({
214
+ path: found.path,
215
+ hash: found.hash,
216
+ skipped: false,
217
+ notePaths: [],
218
+ error: msg,
219
+ });
220
+ }
221
+ catch {
222
+ // ignore record errors
223
+ }
224
+ }
225
+ }
226
+ for (const [slug, count] of newPerProject) {
227
+ if (count < 3)
228
+ continue;
229
+ try {
230
+ const res = await summarizeProject(cfg, slug, db);
231
+ if (res) {
232
+ if (interactive)
233
+ ui.row(ui.success(ui.CHECK), ui.text(`summarized project/${slug}`));
234
+ fileLog(`summarized project/${slug}`);
235
+ }
236
+ }
237
+ catch (err) {
238
+ const msg = err.message;
239
+ if (interactive)
240
+ ui.row(ui.errorColor(ui.CROSS), ui.text(`summary failed for project/${slug}: ${msg}`));
241
+ fileLog(`summary failed for project/${slug}: ${msg}`);
242
+ }
243
+ }
244
+ fileLog(`vir run done — scanned=${summary.scanned} new=${summary.scanned - summary.alreadyProcessed} distilled=${summary.distilled} skipped=${summary.skippedByFilter} lowConf=${summary.lowConfidence} errored=${summary.errored}`);
245
+ if (interactive) {
246
+ ui.blank();
247
+ ui.divider();
248
+ ui.summary({
249
+ scanned: { value: summary.scanned, color: ui.info },
250
+ new: {
251
+ value: summary.scanned - summary.alreadyProcessed,
252
+ color: ui.info,
253
+ },
254
+ distilled: { value: summary.distilled, color: ui.success },
255
+ skipped: { value: summary.skippedByFilter, color: ui.warn },
256
+ errored: {
257
+ value: summary.errored,
258
+ color: summary.errored > 0 ? ui.errorColor : ui.dim,
259
+ },
260
+ });
261
+ ui.divider();
262
+ }
263
+ db.close();
264
+ return summary;
265
+ }
266
+ async function rewriteOne(writer, row) {
267
+ const parsed = {
268
+ path: row.path,
269
+ hash: "",
270
+ sessionId: row.sessionId,
271
+ projectSlug: row.project,
272
+ startedAt: row.startedAt,
273
+ endedAt: null,
274
+ lineCount: 0,
275
+ toolCallCount: 0,
276
+ filesTouched: [],
277
+ assistantText: "",
278
+ userText: "",
279
+ rawSummary: "",
280
+ };
281
+ const note = {
282
+ classification: {
283
+ category: row.category,
284
+ topic: row.topic,
285
+ project: row.project,
286
+ confidence: row.confidence,
287
+ },
288
+ markdown: row.content,
289
+ };
290
+ return writer.write(parsed, note);
291
+ }
292
+ // macOS notification via osascript. Uses spawnSync (no shell, no injection).
293
+ // Embedded values are escaped for AppleScript's string literal rules.
294
+ function notify(title, message) {
295
+ if (process.platform !== "darwin")
296
+ return;
297
+ try {
298
+ const safeTitle = escapeAppleScript(title);
299
+ const safeMessage = escapeAppleScript(message);
300
+ spawnSync("osascript", [
301
+ "-e",
302
+ `display notification "${safeMessage}" with title "${safeTitle}" sound name "Glass"`,
303
+ ], { stdio: "ignore" });
304
+ }
305
+ catch {
306
+ // notification failure must never crash the pipeline
307
+ }
308
+ }
309
+ function escapeAppleScript(s) {
310
+ return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
311
+ }
312
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/pipeline/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync, readdirSync, statSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ export function hashFile(path) {
5
+ const data = readFileSync(path);
6
+ return createHash("sha256").update(data).digest("hex");
7
+ }
8
+ export function scanSessions(projectsDir) {
9
+ const out = [];
10
+ walk(projectsDir, out);
11
+ return out;
12
+ }
13
+ function walk(dir, acc) {
14
+ let entries;
15
+ try {
16
+ entries = readdirSync(dir);
17
+ }
18
+ catch {
19
+ return;
20
+ }
21
+ for (const name of entries) {
22
+ const full = join(dir, name);
23
+ let st;
24
+ try {
25
+ st = statSync(full);
26
+ }
27
+ catch {
28
+ continue;
29
+ }
30
+ if (st.isDirectory()) {
31
+ walk(full, acc);
32
+ }
33
+ else if (st.isFile() && name.endsWith(".jsonl")) {
34
+ try {
35
+ acc.push({
36
+ path: full,
37
+ hash: hashFile(full),
38
+ size: st.size,
39
+ });
40
+ }
41
+ catch {
42
+ // unreadable file - skip
43
+ }
44
+ }
45
+ }
46
+ }
47
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/pipeline/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAQjC,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAChC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,WAAmB;IAC9C,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;IACvB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,IAAI,CAAC,GAAW,EAAE,GAAwB;IACjD,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC7B,IAAI,EAAE,CAAC;QACP,IAAI,CAAC;YACH,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;YACrB,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAClB,CAAC;aAAM,IAAI,EAAE,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClD,IAAI,CAAC;gBACH,GAAG,CAAC,IAAI,CAAC;oBACP,IAAI,EAAE,IAAI;oBACV,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;oBACpB,IAAI,EAAE,EAAE,CAAC,IAAI;iBACd,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,yBAAyB;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,51 @@
1
+ import { homedir } from "node:os";
2
+ import { basename } from "node:path";
3
+ const HOME = homedir();
4
+ const PATTERNS = [
5
+ // Anthropic API keys
6
+ { re: /sk-ant-[A-Za-z0-9_-]{20,}/g, replace: "[REDACTED_ANTHROPIC_KEY]" },
7
+ // OpenAI API keys
8
+ { re: /sk-(?:proj-)?[A-Za-z0-9_-]{20,}/g, replace: "[REDACTED_OPENAI_KEY]" },
9
+ // Generic bearer tokens
10
+ {
11
+ re: /\bBearer\s+[A-Za-z0-9_\-.=]+/gi,
12
+ replace: "Bearer [REDACTED_TOKEN]",
13
+ },
14
+ // GitHub PATs
15
+ { re: /\bghp_[A-Za-z0-9]{30,}\b/g, replace: "[REDACTED_GH_TOKEN]" },
16
+ { re: /\bgho_[A-Za-z0-9]{30,}\b/g, replace: "[REDACTED_GH_TOKEN]" },
17
+ // AWS access keys
18
+ { re: /\bAKIA[0-9A-Z]{16}\b/g, replace: "[REDACTED_AWS_KEY]" },
19
+ // Email addresses
20
+ {
21
+ re: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,
22
+ replace: "[REDACTED_EMAIL]",
23
+ },
24
+ ];
25
+ export function scrub(input) {
26
+ let out = input;
27
+ for (const { re, replace } of PATTERNS) {
28
+ if (typeof replace === "string") {
29
+ out = out.replace(re, replace);
30
+ }
31
+ else {
32
+ out = out.replace(re, replace);
33
+ }
34
+ }
35
+ out = normalizePaths(out);
36
+ return out;
37
+ }
38
+ function normalizePaths(input) {
39
+ // Replace user home prefix
40
+ const homeRe = new RegExp(escapeRegex(HOME), "g");
41
+ let out = input.replace(homeRe, "~");
42
+ // Replace remaining absolute paths with ~/<basename>
43
+ // Match paths like /Users/.../file, /var/..., /tmp/..., /etc/...
44
+ // Conservative: only collapse deeply-nested user-ish paths to avoid mangling /usr/bin/node etc.
45
+ out = out.replace(/\/Users\/[^\s"'`)\]]+/g, (m) => `~/${basename(m)}`);
46
+ return out;
47
+ }
48
+ function escapeRegex(s) {
49
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
50
+ }
51
+ //# sourceMappingURL=scrubber.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrubber.js","sourceRoot":"","sources":["../../src/pipeline/scrubber.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;AAEvB,MAAM,QAAQ,GAAqE;IACjF,qBAAqB;IACrB,EAAE,EAAE,EAAE,4BAA4B,EAAE,OAAO,EAAE,0BAA0B,EAAE;IACzE,kBAAkB;IAClB,EAAE,EAAE,EAAE,kCAAkC,EAAE,OAAO,EAAE,uBAAuB,EAAE;IAC5E,wBAAwB;IACxB;QACE,EAAE,EAAE,gCAAgC;QACpC,OAAO,EAAE,yBAAyB;KACnC;IACD,cAAc;IACd,EAAE,EAAE,EAAE,2BAA2B,EAAE,OAAO,EAAE,qBAAqB,EAAE;IACnE,EAAE,EAAE,EAAE,2BAA2B,EAAE,OAAO,EAAE,qBAAqB,EAAE;IACnE,kBAAkB;IAClB,EAAE,EAAE,EAAE,uBAAuB,EAAE,OAAO,EAAE,oBAAoB,EAAE;IAC9D,kBAAkB;IAClB;QACE,EAAE,EAAE,qDAAqD;QACzD,OAAO,EAAE,kBAAkB;KAC5B;CACF,CAAC;AAEF,MAAM,UAAU,KAAK,CAAC,KAAa;IACjC,IAAI,GAAG,GAAG,KAAK,CAAC;IAChB,KAAK,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,QAAQ,EAAE,CAAC;QACvC,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IACD,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IAC1B,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,2BAA2B;IAC3B,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;IAClD,IAAI,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAErC,qDAAqD;IACrD,iEAAiE;IACjE,gGAAgG;IAChG,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACvE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,WAAW,CAAC,CAAS;IAC5B,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC"}
@@ -0,0 +1,162 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { buildAnthropicClient, callLLM, normalizeModelName, withRateLimitRetry, } from "./distiller.js";
4
+ import { kebab } from "./writer.js";
5
+ const EXCERPT_LEN = 200;
6
+ const CHANGELOG_HEADER = "## Changelog";
7
+ export function groupByProject(rows) {
8
+ const out = new Map();
9
+ for (const r of rows) {
10
+ const slug = kebab(r.project);
11
+ if (slug.length === 0)
12
+ continue;
13
+ let g = out.get(slug);
14
+ if (!g) {
15
+ g = { slug, displayName: r.project, rows: [] };
16
+ out.set(slug, g);
17
+ }
18
+ g.rows.push(r);
19
+ }
20
+ return out;
21
+ }
22
+ export function countByCategory(rows) {
23
+ let patterns = 0, gotchas = 0, decisions = 0, tools = 0;
24
+ for (const r of rows) {
25
+ if (r.category === "pattern")
26
+ patterns += 1;
27
+ else if (r.category === "gotcha")
28
+ gotchas += 1;
29
+ else if (r.category === "decision")
30
+ decisions += 1;
31
+ else if (r.category === "tool")
32
+ tools += 1;
33
+ }
34
+ return { patterns, gotchas, decisions, tools, total: rows.length };
35
+ }
36
+ export async function summarizeProject(cfg, projectSlug, db) {
37
+ const allRows = db.listDistilled();
38
+ const grouped = groupByProject(allRows);
39
+ const group = grouped.get(projectSlug);
40
+ if (!group || group.rows.length === 0)
41
+ return null;
42
+ const counts = countByCategory(group.rows);
43
+ const prompt = buildPrompt(group, counts);
44
+ const client = buildAnthropicClient(cfg);
45
+ const model = normalizeModelName(cfg.models.distill, cfg.provider);
46
+ const body = await withRateLimitRetry(() => callLLM(cfg, client, { prompt, model, maxTokens: 1500 }));
47
+ const outPath = writeSummaryFile(cfg, projectSlug, body.trim(), counts);
48
+ return { slug: projectSlug, path: outPath, counts };
49
+ }
50
+ export async function summarizeAll(cfg, db) {
51
+ const grouped = groupByProject(db.listDistilled());
52
+ const results = [];
53
+ for (const slug of grouped.keys()) {
54
+ const res = await summarizeProject(cfg, slug, db);
55
+ if (res)
56
+ results.push(res);
57
+ }
58
+ return results;
59
+ }
60
+ function buildPrompt(group, counts) {
61
+ const byCat = {
62
+ pattern: [],
63
+ gotcha: [],
64
+ decision: [],
65
+ tool: [],
66
+ };
67
+ for (const r of group.rows)
68
+ byCat[r.category].push(r);
69
+ const renderList = (rows) => {
70
+ if (rows.length === 0)
71
+ return "(none)";
72
+ return rows
73
+ .map((r) => {
74
+ const excerpt = r.content
75
+ .replace(/\s+/g, " ")
76
+ .trim()
77
+ .slice(0, EXCERPT_LEN);
78
+ return `- ${r.topic}: ${excerpt}`;
79
+ })
80
+ .join("\n");
81
+ };
82
+ return `You are synthesizing a project knowledge summary from distilled Claude Code session notes.
83
+
84
+ Project: ${group.slug}
85
+ Total sessions: ${counts.total}
86
+
87
+ Patterns (${counts.patterns}):
88
+ ${renderList(byCat.pattern)}
89
+
90
+ Gotchas (${counts.gotchas}):
91
+ ${renderList(byCat.gotcha)}
92
+
93
+ Decisions (${counts.decisions}):
94
+ ${renderList(byCat.decision)}
95
+
96
+ Tools (${counts.tools}):
97
+ ${renderList(byCat.tool)}
98
+
99
+ Write a project summary with these exact sections:
100
+ ## Overview
101
+ 2-3 sentences: what this project is, what stack/approach dominates
102
+
103
+ ## Key Patterns
104
+ Bullet list of the most reusable patterns, 1 sentence each
105
+
106
+ ## Watch Out For
107
+ Bullet list of the most important gotchas, 1 sentence each
108
+
109
+ ## Architecture Decisions
110
+ Bullet list of significant decisions made, 1 sentence each
111
+
112
+ ## Knowledge Gaps
113
+ 1-2 sentences: what topics appear underrepresented or missing
114
+
115
+ Be specific and direct. Use the actual topic names.`;
116
+ }
117
+ function writeSummaryFile(cfg, projectSlug, body, counts) {
118
+ const dir = join(cfg.vaultPath, cfg.outputDir, "projects");
119
+ if (!existsSync(dir))
120
+ mkdirSync(dir, { recursive: true });
121
+ const filePath = join(dir, `${projectSlug}.md`);
122
+ const generated = new Date().toISOString();
123
+ const date = generated.slice(0, 10);
124
+ const newEntry = `- ${date}: ${counts.total} sessions, ${counts.patterns} patterns, ${counts.gotchas} gotchas, ${counts.decisions} decisions, ${counts.tools} tools`;
125
+ const existingChangelog = readExistingChangelog(filePath);
126
+ const changelog = [CHANGELOG_HEADER, newEntry, ...existingChangelog].join("\n");
127
+ const frontmatter = [
128
+ "---",
129
+ `project: ${projectSlug}`,
130
+ `generated: ${generated}`,
131
+ `sessions: ${counts.total}`,
132
+ "---",
133
+ "",
134
+ `Project: [[${projectSlug}]]`,
135
+ "",
136
+ ].join("\n");
137
+ const content = `${frontmatter}${body}\n\n---\n${changelog}\n`;
138
+ writeFileSync(filePath, content);
139
+ return filePath;
140
+ }
141
+ // Returns the existing changelog entries (the lines after `## Changelog`),
142
+ // excluding the header itself. Empty array if file is missing or has no
143
+ // changelog section yet.
144
+ function readExistingChangelog(filePath) {
145
+ if (!existsSync(filePath))
146
+ return [];
147
+ let raw;
148
+ try {
149
+ raw = readFileSync(filePath, "utf8");
150
+ }
151
+ catch {
152
+ return [];
153
+ }
154
+ const idx = raw.indexOf(CHANGELOG_HEADER);
155
+ if (idx === -1)
156
+ return [];
157
+ const rest = raw.slice(idx + CHANGELOG_HEADER.length).trim();
158
+ if (rest.length === 0)
159
+ return [];
160
+ return rest.split("\n").filter((l) => l.trim().length > 0);
161
+ }
162
+ //# sourceMappingURL=summarizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summarizer.js","sourceRoot":"","sources":["../../src/pipeline/summarizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/pipeline/types.ts"],"names":[],"mappings":""}