@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,195 @@
1
+ import chalk from "chalk";
2
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { embeddingForNote, isOllamaAvailableCached, } from "../search/embedder.js";
5
+ const CATEGORY_DIR = {
6
+ pattern: "patterns",
7
+ gotcha: "gotchas",
8
+ decision: "decisions",
9
+ tool: "tools",
10
+ };
11
+ export class VaultWriter {
12
+ root;
13
+ db;
14
+ constructor(cfg, db = null) {
15
+ this.root = join(cfg.vaultPath, cfg.outputDir);
16
+ this.db = db;
17
+ for (const sub of Object.values(CATEGORY_DIR)) {
18
+ const p = join(this.root, sub);
19
+ if (!existsSync(p))
20
+ mkdirSync(p, { recursive: true });
21
+ }
22
+ this.ensureIndex();
23
+ this.ensureLog();
24
+ }
25
+ async write(session, note) {
26
+ const { classification, markdown } = note;
27
+ const slug = makeSlug(classification.topic, session.sessionId);
28
+ const subDir = CATEGORY_DIR[classification.category];
29
+ const relPath = join(subDir, `${slug}.md`);
30
+ const fullPath = join(this.root, relPath);
31
+ const frontmatter = [
32
+ "---",
33
+ `topic: "${classification.topic.replace(/"/g, '\\"')}"`,
34
+ `category: ${classification.category}`,
35
+ `project: "${classification.project.replace(/"/g, '\\"')}"`,
36
+ `session_id: ${session.sessionId}`,
37
+ `date: ${session.startedAt ?? new Date().toISOString()}`,
38
+ `confidence: ${classification.confidence}`,
39
+ "---",
40
+ "",
41
+ ].join("\n");
42
+ const projectSlug = kebab(classification.project);
43
+ const categorySlug = classification.category;
44
+ const wikilinkHeader = `Project: [[${projectSlug}]]\n` +
45
+ `Category: [[${categorySlug}]]\n\n`;
46
+ const body = wikilinkRelated(markdown);
47
+ const finalContent = frontmatter + wikilinkHeader + body + "\n";
48
+ writeFileSync(fullPath, finalContent);
49
+ await this.maybeEmbed(session, note, finalContent);
50
+ this.appendIndex({
51
+ date: (session.startedAt ?? new Date().toISOString()).slice(0, 10),
52
+ topic: classification.topic,
53
+ category: classification.category,
54
+ project: classification.project,
55
+ relPath,
56
+ });
57
+ this.appendLog({
58
+ ts: new Date().toISOString().slice(0, 16).replace("T", " "),
59
+ category: classification.category,
60
+ topic: classification.topic,
61
+ project: classification.project,
62
+ });
63
+ return [fullPath];
64
+ }
65
+ // Best-effort: embed the freshly-written note via Ollama and store the
66
+ // vector. Any failure (Ollama down, timeout, model missing) is swallowed —
67
+ // an embedding miss must never fail a write.
68
+ async maybeEmbed(session, note, fileContent) {
69
+ if (!this.db)
70
+ return;
71
+ try {
72
+ const available = await isOllamaAvailableCached();
73
+ if (!available)
74
+ return;
75
+ const vec = await embeddingForNote(fileContent);
76
+ if (!vec)
77
+ return;
78
+ this.db.storeEmbedding(session.sessionId, vec);
79
+ console.log(chalk.dim(` embedded ${note.classification.topic} (${vec.length}d)`));
80
+ }
81
+ catch {
82
+ // never crash the writer on embedding failure
83
+ }
84
+ }
85
+ ensureIndex() {
86
+ const p = join(this.root, "index.md");
87
+ if (!existsSync(p)) {
88
+ writeFileSync(p, "# vir — Distilled Knowledge\n\n| Date | Topic | Category | Project | Link |\n|------|-------|----------|---------|------|\n");
89
+ }
90
+ }
91
+ ensureLog() {
92
+ const p = join(this.root, "log.md");
93
+ if (!existsSync(p)) {
94
+ writeFileSync(p, "# vir — Run Log\n\n");
95
+ }
96
+ }
97
+ appendIndex(row) {
98
+ const p = join(this.root, "index.md");
99
+ const link = `[[${row.relPath.replace(/\.md$/, "")}|${row.topic}]]`;
100
+ const line = `| ${row.date} | ${row.topic} | ${row.category} | ${row.project} | ${link} |\n`;
101
+ const current = readFileSync(p, "utf8");
102
+ // Insert after the table header (first occurrence of '|------')
103
+ const headerIdx = current.indexOf("|------");
104
+ if (headerIdx === -1) {
105
+ appendFileSync(p, line);
106
+ return;
107
+ }
108
+ const newlineAfter = current.indexOf("\n", headerIdx);
109
+ const updated = current.slice(0, newlineAfter + 1) + line + current.slice(newlineAfter + 1);
110
+ writeFileSync(p, updated);
111
+ }
112
+ appendLog(entry) {
113
+ const p = join(this.root, "log.md");
114
+ appendFileSync(p, `## [${entry.ts}] ${entry.category} | ${entry.topic} | ${entry.project}\n\n`);
115
+ }
116
+ noteCount() {
117
+ let n = 0;
118
+ for (const sub of Object.values(CATEGORY_DIR)) {
119
+ const dir = join(this.root, sub);
120
+ if (!existsSync(dir))
121
+ continue;
122
+ try {
123
+ n += readdirSync(dir).filter((f) => f.endsWith(".md")).length;
124
+ }
125
+ catch {
126
+ // ignore
127
+ }
128
+ }
129
+ return n;
130
+ }
131
+ }
132
+ function makeSlug(topic, sessionId) {
133
+ const base = kebab(topic).slice(0, 50);
134
+ const suffix = sessionId.slice(0, 8);
135
+ return base.length > 0 ? `${base}-${suffix}` : `note-${suffix}`;
136
+ }
137
+ export function kebab(s) {
138
+ return s
139
+ .toLowerCase()
140
+ .replace(/[^a-z0-9]+/g, "-")
141
+ .replace(/^-+|-+$/g, "");
142
+ }
143
+ // Rewrites the bullet list under a `## Related` heading so each item
144
+ // becomes an Obsidian wikilink to a kebab-cased slug of the item's text.
145
+ // Stops at the next heading or end of document.
146
+ export function wikilinkRelated(markdown) {
147
+ const lines = markdown.split("\n");
148
+ const out = [];
149
+ let inRelated = false;
150
+ for (const line of lines) {
151
+ if (/^##\s+related\b/i.test(line)) {
152
+ inRelated = true;
153
+ out.push(line);
154
+ continue;
155
+ }
156
+ if (inRelated && /^#{1,6}\s+/.test(line)) {
157
+ inRelated = false;
158
+ out.push(line);
159
+ continue;
160
+ }
161
+ if (inRelated) {
162
+ const bullet = line.match(/^(\s*[-*]\s+)(.*)$/);
163
+ if (bullet) {
164
+ const prefix = bullet[1] ?? "- ";
165
+ const text = (bullet[2] ?? "").trim();
166
+ if (text.length === 0) {
167
+ out.push(line);
168
+ }
169
+ else if (/^\[\[.+\]\]$/.test(text)) {
170
+ // already a wikilink — leave it
171
+ out.push(line);
172
+ }
173
+ else {
174
+ const slug = kebab(stripWikilink(text));
175
+ out.push(`${prefix}[[${slug}]]`);
176
+ }
177
+ continue;
178
+ }
179
+ }
180
+ out.push(line);
181
+ }
182
+ return out.join("\n");
183
+ }
184
+ function stripWikilink(s) {
185
+ // If the model already partially wrapped it like "[[Something]]" or
186
+ // "[Something](url)", reduce to the inner text before kebab-casing.
187
+ const wiki = s.match(/^\[\[(.+?)\]\]$/);
188
+ if (wiki)
189
+ return wiki[1] ?? s;
190
+ const md = s.match(/^\[(.+?)\]\(.+\)$/);
191
+ if (md)
192
+ return md[1] ?? s;
193
+ return s;
194
+ }
195
+ //# sourceMappingURL=writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writer.js","sourceRoot":"","sources":["../../src/pipeline/writer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ const OLLAMA_BASE = "http://localhost:11434";
2
+ export const EMBED_MODEL = "nomic-embed-text";
3
+ const EMBED_TIMEOUT_MS = 10_000;
4
+ const PING_TIMEOUT_MS = 3_000;
5
+ export class EmbedderError extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "EmbedderError";
9
+ }
10
+ }
11
+ export async function embed(text) {
12
+ const ac = new AbortController();
13
+ const t = setTimeout(() => ac.abort(), EMBED_TIMEOUT_MS);
14
+ try {
15
+ const resp = await fetch(`${OLLAMA_BASE}/api/embeddings`, {
16
+ method: "POST",
17
+ headers: { "Content-Type": "application/json" },
18
+ body: JSON.stringify({ model: EMBED_MODEL, prompt: text }),
19
+ signal: ac.signal,
20
+ });
21
+ if (!resp.ok) {
22
+ throw new EmbedderError(`Ollama ${resp.status}: ${await resp.text().catch(() => "")}`);
23
+ }
24
+ const data = (await resp.json());
25
+ if (data.error)
26
+ throw new EmbedderError(`Ollama error: ${data.error}`);
27
+ if (!Array.isArray(data.embedding) || data.embedding.length === 0) {
28
+ throw new EmbedderError("Ollama returned no embedding");
29
+ }
30
+ return data.embedding.map((n) => Number(n));
31
+ }
32
+ finally {
33
+ clearTimeout(t);
34
+ }
35
+ }
36
+ export function cosineSimilarity(a, b) {
37
+ if (a.length === 0 || b.length === 0 || a.length !== b.length)
38
+ return 0;
39
+ let dot = 0;
40
+ let magA = 0;
41
+ let magB = 0;
42
+ for (let i = 0; i < a.length; i += 1) {
43
+ const x = a[i] ?? 0;
44
+ const y = b[i] ?? 0;
45
+ dot += x * y;
46
+ magA += x * x;
47
+ magB += y * y;
48
+ }
49
+ if (magA === 0 || magB === 0)
50
+ return 0;
51
+ const sim = dot / (Math.sqrt(magA) * Math.sqrt(magB));
52
+ // Clamp to [0, 1] — embeddings are typically positively correlated; negative
53
+ // values would otherwise distort the topK ordering downstream.
54
+ if (!Number.isFinite(sim))
55
+ return 0;
56
+ if (sim < 0)
57
+ return 0;
58
+ if (sim > 1)
59
+ return 1;
60
+ return sim;
61
+ }
62
+ export async function isOllamaAvailable() {
63
+ const ac = new AbortController();
64
+ const t = setTimeout(() => ac.abort(), PING_TIMEOUT_MS);
65
+ try {
66
+ const resp = await fetch(`${OLLAMA_BASE}/api/tags`, { signal: ac.signal });
67
+ return resp.ok;
68
+ }
69
+ catch {
70
+ return false;
71
+ }
72
+ finally {
73
+ clearTimeout(t);
74
+ }
75
+ }
76
+ // Module-level memo so a long-running daemon doesn't hammer /api/tags on every
77
+ // session. Invalidated only by process restart, which is fine for our cadence.
78
+ let _availabilityCache = null;
79
+ export function isOllamaAvailableCached() {
80
+ if (_availabilityCache === null) {
81
+ _availabilityCache = isOllamaAvailable();
82
+ }
83
+ return _availabilityCache;
84
+ }
85
+ export async function embeddingForNote(text) {
86
+ try {
87
+ return await embed(text);
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ }
93
+ //# sourceMappingURL=embedder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedder.js","sourceRoot":"","sources":["../../src/search/embedder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,212 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
+ import { join, relative } from "node:path";
3
+ import { cosineSimilarity, embed, isOllamaAvailable, } from "./embedder.js";
4
+ const SKIP_FILES = new Set(["index.md", "log.md"]);
5
+ const MIN_EMBEDDING_SCORE = 0.3;
6
+ export async function search(cfg, db, query, topK = 8) {
7
+ if (await isOllamaAvailable()) {
8
+ const hits = await searchByEmbedding(cfg, db, query, topK);
9
+ // If embeddings produced at least one match above the floor, take it.
10
+ // Otherwise fall through to TF-IDF: low cosine on every doc means the
11
+ // query is semantically off; lexical overlap might still find a match.
12
+ if (hits.length > 0)
13
+ return hits;
14
+ }
15
+ return searchByTfIdf(cfg, query, topK);
16
+ }
17
+ async function searchByEmbedding(cfg, db, query, topK) {
18
+ let queryVec;
19
+ try {
20
+ queryVec = await embed(query);
21
+ }
22
+ catch {
23
+ return [];
24
+ }
25
+ const root = vaultRoot(cfg);
26
+ const rows = db.getEmbeddings(root);
27
+ if (rows.length === 0)
28
+ return [];
29
+ const scored = [];
30
+ for (const r of rows) {
31
+ const s = cosineSimilarity(queryVec, r.embedding);
32
+ if (s >= MIN_EMBEDDING_SCORE)
33
+ scored.push({ row: r, score: s });
34
+ }
35
+ scored.sort((a, b) => b.score - a.score);
36
+ const hits = [];
37
+ for (const { row, score } of scored.slice(0, topK)) {
38
+ let content = "";
39
+ try {
40
+ content = existsSync(row.filePath) ? readFileSync(row.filePath, "utf8") : "";
41
+ }
42
+ catch {
43
+ content = "";
44
+ }
45
+ if (content.length === 0)
46
+ continue;
47
+ const rel = relative(root, row.filePath);
48
+ hits.push({
49
+ filePath: row.filePath,
50
+ title: rel.replace(/\.md$/, ""),
51
+ content,
52
+ score: Math.round(score * 10000) / 10000,
53
+ method: "embedding",
54
+ });
55
+ }
56
+ return hits;
57
+ }
58
+ function searchByTfIdf(cfg, query, topK) {
59
+ const docs = loadIndex(cfg);
60
+ const scored = searchTfIdf(docs, query, topK);
61
+ const root = vaultRoot(cfg);
62
+ return scored.map((d) => ({
63
+ filePath: join(root, d.relPath),
64
+ title: d.title,
65
+ content: d.raw,
66
+ score: d.score,
67
+ method: "tfidf",
68
+ }));
69
+ }
70
+ export function vaultRoot(cfg) {
71
+ return join(cfg.vaultPath, cfg.outputDir);
72
+ }
73
+ export function loadIndex(cfg) {
74
+ const root = vaultRoot(cfg);
75
+ const files = [];
76
+ walk(root, files);
77
+ const docs = [];
78
+ for (const full of files) {
79
+ const rel = relative(root, full);
80
+ const base = rel.split("/").pop() ?? rel;
81
+ if (SKIP_FILES.has(base))
82
+ continue;
83
+ let raw;
84
+ try {
85
+ raw = readFileSync(full, "utf8");
86
+ }
87
+ catch {
88
+ continue;
89
+ }
90
+ const text = stripMarkdown(raw);
91
+ const tokens = tokenize(text);
92
+ const tf = termFrequency(tokens);
93
+ docs.push({
94
+ relPath: rel,
95
+ title: rel.replace(/\.md$/, ""),
96
+ raw,
97
+ text,
98
+ tokens,
99
+ tf,
100
+ });
101
+ }
102
+ return docs;
103
+ }
104
+ export function searchTfIdf(docs, query, topK = 8) {
105
+ if (docs.length === 0)
106
+ return [];
107
+ const queryTokens = uniq(tokenize(query));
108
+ if (queryTokens.length === 0)
109
+ return [];
110
+ const totalDocs = docs.length;
111
+ const dfMap = new Map();
112
+ for (const term of queryTokens) {
113
+ let df = 0;
114
+ for (const d of docs)
115
+ if (d.tf.has(term))
116
+ df += 1;
117
+ dfMap.set(term, df);
118
+ }
119
+ const scored = [];
120
+ for (const d of docs) {
121
+ let score = 0;
122
+ for (const term of queryTokens) {
123
+ const tf = d.tf.get(term) ?? 0;
124
+ if (tf === 0)
125
+ continue;
126
+ const df = dfMap.get(term) ?? 0;
127
+ if (df === 0)
128
+ continue;
129
+ const idf = Math.log(totalDocs / df);
130
+ // Normalize TF by doc length so long docs don't dominate.
131
+ const tfNorm = tf / Math.max(1, d.tokens.length);
132
+ score += tfNorm * idf;
133
+ }
134
+ if (score > 0) {
135
+ scored.push({
136
+ relPath: d.relPath,
137
+ title: d.title,
138
+ raw: d.raw,
139
+ score: Math.round(score * 10000) / 10000,
140
+ });
141
+ }
142
+ }
143
+ scored.sort((a, b) => b.score - a.score);
144
+ return scored.slice(0, topK);
145
+ }
146
+ function walk(dir, acc) {
147
+ let entries;
148
+ try {
149
+ entries = readdirSync(dir);
150
+ }
151
+ catch {
152
+ return;
153
+ }
154
+ for (const name of entries) {
155
+ const full = join(dir, name);
156
+ let st;
157
+ try {
158
+ st = statSync(full);
159
+ }
160
+ catch {
161
+ continue;
162
+ }
163
+ if (st.isDirectory())
164
+ walk(full, acc);
165
+ else if (st.isFile() && name.endsWith(".md"))
166
+ acc.push(full);
167
+ }
168
+ }
169
+ export function stripMarkdown(md) {
170
+ let out = md;
171
+ // YAML frontmatter
172
+ out = out.replace(/^---\n[\s\S]*?\n---\n?/, "");
173
+ // Fenced code blocks
174
+ out = out.replace(/```[\s\S]*?```/g, " ");
175
+ // Inline code
176
+ out = out.replace(/`[^`]*`/g, " ");
177
+ // Images
178
+ out = out.replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1");
179
+ // Markdown links -> link text
180
+ out = out.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
181
+ // Wikilinks -> inner
182
+ out = out.replace(/\[\[([^\]]+)\]\]/g, "$1");
183
+ // Headings, blockquotes, list markers
184
+ out = out.replace(/^\s{0,3}#{1,6}\s+/gm, "");
185
+ out = out.replace(/^\s*>\s?/gm, "");
186
+ out = out.replace(/^\s*[-*+]\s+/gm, "");
187
+ out = out.replace(/^\s*\d+\.\s+/gm, "");
188
+ // Emphasis markers
189
+ out = out.replace(/\*\*([^*]+)\*\*/g, "$1");
190
+ out = out.replace(/__([^_]+)__/g, "$1");
191
+ out = out.replace(/\*([^*]+)\*/g, "$1");
192
+ out = out.replace(/_([^_]+)_/g, "$1");
193
+ // Horizontal rules
194
+ out = out.replace(/^\s*[-*_]{3,}\s*$/gm, "");
195
+ return out;
196
+ }
197
+ export function tokenize(s) {
198
+ return s
199
+ .toLowerCase()
200
+ .split(/\W+/)
201
+ .filter((t) => t.length >= 3);
202
+ }
203
+ function termFrequency(tokens) {
204
+ const m = new Map();
205
+ for (const t of tokens)
206
+ m.set(t, (m.get(t) ?? 0) + 1);
207
+ return m;
208
+ }
209
+ function uniq(arr) {
210
+ return [...new Set(arr)];
211
+ }
212
+ //# sourceMappingURL=retriever.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retriever.js","sourceRoot":"","sources":["../../src/search/retriever.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { buildAnthropicClient, callLLM, normalizeModelName, withRateLimitRetry, } from "../pipeline/distiller.js";
2
+ export async function synthesize(cfg, query, hits) {
3
+ const notes = hits
4
+ .map((h) => `### ${h.title} (score: ${h.score})\n${h.content.trim()}`)
5
+ .join("\n\n---\n\n");
6
+ const prompt = `You are searching a personal knowledge base of distilled Claude Code session notes. Answer the query directly and concisely using only the provided notes as source.
7
+
8
+ Query: ${query}
9
+
10
+ Notes:
11
+ ${notes}
12
+
13
+ Instructions:
14
+ - Answer directly, 3-5 sentences max
15
+ - Quote the specific note title when citing
16
+ - If notes don't contain relevant info, say so clearly
17
+ - Do not invent information not present in the notes`;
18
+ const client = buildAnthropicClient(cfg);
19
+ const model = normalizeModelName(cfg.models.distill, cfg.provider);
20
+ return withRateLimitRetry(() => callLLM(cfg, client, {
21
+ prompt,
22
+ model,
23
+ maxTokens: 600,
24
+ }));
25
+ }
26
+ //# sourceMappingURL=synthesizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synthesizer.js","sourceRoot":"","sources":["../../src/search/synthesizer.ts"],"names":[],"mappings":"AACA,OAAO,EACL,oBAAoB,EACpB,OAAO,EACP,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AAGlC,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAW,EACX,KAAa,EACb,IAAiB;IAEjB,MAAM,KAAK,GAAG,IAAI;SACf,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,OAAO,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAC5D;SACA,IAAI,CAAC,aAAa,CAAC,CAAC;IAEvB,MAAM,MAAM,GAAG;;SAER,KAAK;;;EAGZ,KAAK;;;;;;qDAM8C,CAAC;IAEpD,MAAM,MAAM,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEnE,OAAO,kBAAkB,CAAC,GAAG,EAAE,CAC7B,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE;QACnB,MAAM;QACN,KAAK;QACL,SAAS,EAAE,GAAG;KACf,CAAC,CACH,CAAC;AACJ,CAAC"}