@mneme-ai/core 0.8.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.
Files changed (142) hide show
  1. package/README.md +31 -0
  2. package/dist/correlate/index.d.ts +44 -0
  3. package/dist/correlate/index.d.ts.map +1 -0
  4. package/dist/correlate/index.js +21 -0
  5. package/dist/correlate/index.js.map +1 -0
  6. package/dist/enrich/index.d.ts +35 -0
  7. package/dist/enrich/index.d.ts.map +1 -0
  8. package/dist/enrich/index.js +69 -0
  9. package/dist/enrich/index.js.map +1 -0
  10. package/dist/entities/cosine-clones.d.ts +6 -0
  11. package/dist/entities/cosine-clones.d.ts.map +1 -0
  12. package/dist/entities/cosine-clones.js +142 -0
  13. package/dist/entities/cosine-clones.js.map +1 -0
  14. package/dist/entities/cosine-clones.test.d.ts +2 -0
  15. package/dist/entities/cosine-clones.test.d.ts.map +1 -0
  16. package/dist/entities/cosine-clones.test.js +109 -0
  17. package/dist/entities/cosine-clones.test.js.map +1 -0
  18. package/dist/entities/index.d.ts +74 -0
  19. package/dist/entities/index.d.ts.map +1 -0
  20. package/dist/entities/index.js +24 -0
  21. package/dist/entities/index.js.map +1 -0
  22. package/dist/entities/python-parser.d.ts +16 -0
  23. package/dist/entities/python-parser.d.ts.map +1 -0
  24. package/dist/entities/python-parser.js +248 -0
  25. package/dist/entities/python-parser.js.map +1 -0
  26. package/dist/entities/typescript-parser.d.ts +18 -0
  27. package/dist/entities/typescript-parser.d.ts.map +1 -0
  28. package/dist/entities/typescript-parser.js +220 -0
  29. package/dist/entities/typescript-parser.js.map +1 -0
  30. package/dist/entities/typescript-parser.test.d.ts +2 -0
  31. package/dist/entities/typescript-parser.test.d.ts.map +1 -0
  32. package/dist/entities/typescript-parser.test.js +103 -0
  33. package/dist/entities/typescript-parser.test.js.map +1 -0
  34. package/dist/git/blame.d.ts +9 -0
  35. package/dist/git/blame.d.ts.map +1 -0
  36. package/dist/git/blame.js +56 -0
  37. package/dist/git/blame.js.map +1 -0
  38. package/dist/git/exec.d.ts +13 -0
  39. package/dist/git/exec.d.ts.map +1 -0
  40. package/dist/git/exec.js +40 -0
  41. package/dist/git/exec.js.map +1 -0
  42. package/dist/git/github.d.ts +62 -0
  43. package/dist/git/github.d.ts.map +1 -0
  44. package/dist/git/github.js +115 -0
  45. package/dist/git/github.js.map +1 -0
  46. package/dist/git/github.test.d.ts +2 -0
  47. package/dist/git/github.test.d.ts.map +1 -0
  48. package/dist/git/github.test.js +86 -0
  49. package/dist/git/github.test.js.map +1 -0
  50. package/dist/git/gitlab.d.ts +66 -0
  51. package/dist/git/gitlab.d.ts.map +1 -0
  52. package/dist/git/gitlab.js +121 -0
  53. package/dist/git/gitlab.js.map +1 -0
  54. package/dist/git/gitlab.test.d.ts +2 -0
  55. package/dist/git/gitlab.test.d.ts.map +1 -0
  56. package/dist/git/gitlab.test.js +122 -0
  57. package/dist/git/gitlab.test.js.map +1 -0
  58. package/dist/git/index.d.ts +7 -0
  59. package/dist/git/index.d.ts.map +1 -0
  60. package/dist/git/index.js +7 -0
  61. package/dist/git/index.js.map +1 -0
  62. package/dist/git/log.d.ts +11 -0
  63. package/dist/git/log.d.ts.map +1 -0
  64. package/dist/git/log.js +107 -0
  65. package/dist/git/log.js.map +1 -0
  66. package/dist/git/log.test.d.ts +2 -0
  67. package/dist/git/log.test.d.ts.map +1 -0
  68. package/dist/git/log.test.js +88 -0
  69. package/dist/git/log.test.js.map +1 -0
  70. package/dist/git/repo.d.ts +12 -0
  71. package/dist/git/repo.d.ts.map +1 -0
  72. package/dist/git/repo.js +50 -0
  73. package/dist/git/repo.js.map +1 -0
  74. package/dist/git/repo.test.d.ts +2 -0
  75. package/dist/git/repo.test.d.ts.map +1 -0
  76. package/dist/git/repo.test.js +35 -0
  77. package/dist/git/repo.test.js.map +1 -0
  78. package/dist/index.d.ts +10 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +10 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/indexer/index.d.ts +2 -0
  83. package/dist/indexer/index.d.ts.map +1 -0
  84. package/dist/indexer/index.js +2 -0
  85. package/dist/indexer/index.js.map +1 -0
  86. package/dist/indexer/indexer.d.ts +22 -0
  87. package/dist/indexer/indexer.d.ts.map +1 -0
  88. package/dist/indexer/indexer.js +107 -0
  89. package/dist/indexer/indexer.js.map +1 -0
  90. package/dist/indexer/indexer.test.d.ts +2 -0
  91. package/dist/indexer/indexer.test.d.ts.map +1 -0
  92. package/dist/indexer/indexer.test.js +80 -0
  93. package/dist/indexer/indexer.test.js.map +1 -0
  94. package/dist/retrieve/index.d.ts +3 -0
  95. package/dist/retrieve/index.d.ts.map +1 -0
  96. package/dist/retrieve/index.js +3 -0
  97. package/dist/retrieve/index.js.map +1 -0
  98. package/dist/retrieve/rerank.d.ts +44 -0
  99. package/dist/retrieve/rerank.d.ts.map +1 -0
  100. package/dist/retrieve/rerank.js +68 -0
  101. package/dist/retrieve/rerank.js.map +1 -0
  102. package/dist/retrieve/rerank.test.d.ts +2 -0
  103. package/dist/retrieve/rerank.test.d.ts.map +1 -0
  104. package/dist/retrieve/rerank.test.js +52 -0
  105. package/dist/retrieve/rerank.test.js.map +1 -0
  106. package/dist/retrieve/search.d.ts +31 -0
  107. package/dist/retrieve/search.d.ts.map +1 -0
  108. package/dist/retrieve/search.js +170 -0
  109. package/dist/retrieve/search.js.map +1 -0
  110. package/dist/retrieve/search.test.d.ts +2 -0
  111. package/dist/retrieve/search.test.d.ts.map +1 -0
  112. package/dist/retrieve/search.test.js +105 -0
  113. package/dist/retrieve/search.test.js.map +1 -0
  114. package/dist/store/index.d.ts +3 -0
  115. package/dist/store/index.d.ts.map +1 -0
  116. package/dist/store/index.js +3 -0
  117. package/dist/store/index.js.map +1 -0
  118. package/dist/store/schema.d.ts +11 -0
  119. package/dist/store/schema.d.ts.map +1 -0
  120. package/dist/store/schema.js +129 -0
  121. package/dist/store/schema.js.map +1 -0
  122. package/dist/store/sqlite.d.ts +51 -0
  123. package/dist/store/sqlite.d.ts.map +1 -0
  124. package/dist/store/sqlite.js +262 -0
  125. package/dist/store/sqlite.js.map +1 -0
  126. package/dist/store/sqlite.test.d.ts +2 -0
  127. package/dist/store/sqlite.test.d.ts.map +1 -0
  128. package/dist/store/sqlite.test.js +128 -0
  129. package/dist/store/sqlite.test.js.map +1 -0
  130. package/dist/types.d.ts +115 -0
  131. package/dist/types.d.ts.map +1 -0
  132. package/dist/types.js +6 -0
  133. package/dist/types.js.map +1 -0
  134. package/dist/util/index.d.ts +15 -0
  135. package/dist/util/index.d.ts.map +1 -0
  136. package/dist/util/index.js +65 -0
  137. package/dist/util/index.js.map +1 -0
  138. package/dist/util/index.test.d.ts +2 -0
  139. package/dist/util/index.test.d.ts.map +1 -0
  140. package/dist/util/index.test.js +37 -0
  141. package/dist/util/index.test.js.map +1 -0
  142. package/package.json +62 -0
@@ -0,0 +1,107 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readCommits, readFileChanges } from "../git/log.js";
3
+ export class Indexer {
4
+ opts;
5
+ constructor(opts) {
6
+ this.opts = opts;
7
+ }
8
+ async run() {
9
+ const report = (p) => this.opts.onProgress?.(p);
10
+ report({ phase: "git_log", current: 0, total: 0, message: "reading git history" });
11
+ const commits = await readCommits({
12
+ cwd: this.opts.cwd,
13
+ since: this.opts.since,
14
+ maxCount: this.opts.maxCount,
15
+ });
16
+ report({
17
+ phase: "writing",
18
+ current: 0,
19
+ total: commits.length,
20
+ message: `persisting ${commits.length} commits`,
21
+ });
22
+ this.opts.store.upsertCommits(commits);
23
+ for (let i = 0; i < commits.length; i++) {
24
+ const c = commits[i];
25
+ const changes = await readFileChanges(this.opts.cwd, c.hash);
26
+ this.opts.store.upsertFileChanges(changes);
27
+ if (i % 50 === 0) {
28
+ report({
29
+ phase: "writing",
30
+ current: i,
31
+ total: commits.length,
32
+ message: "indexing file changes",
33
+ });
34
+ }
35
+ }
36
+ const chunks = buildChunks(commits);
37
+ if (this.opts.embedder) {
38
+ const model = this.opts.embedder.name;
39
+ const batchSize = this.opts.embedBatchSize ?? 32;
40
+ for (let i = 0; i < chunks.length; i += batchSize) {
41
+ const batch = chunks.slice(i, i + batchSize);
42
+ const vecs = await this.opts.embedder.embed(batch.map((c) => c.text));
43
+ for (let j = 0; j < batch.length; j++)
44
+ batch[j].embedding = vecs[j];
45
+ this.opts.store.upsertChunks(batch, model);
46
+ report({
47
+ phase: "embedding",
48
+ current: Math.min(i + batchSize, chunks.length),
49
+ total: chunks.length,
50
+ message: `embedding with ${model}`,
51
+ });
52
+ }
53
+ }
54
+ else {
55
+ this.opts.store.upsertChunks(chunks);
56
+ }
57
+ report({ phase: "done", current: chunks.length, total: chunks.length });
58
+ return { commits: commits.length, chunks: chunks.length };
59
+ }
60
+ }
61
+ export function buildChunks(commits) {
62
+ const chunks = [];
63
+ for (const c of commits) {
64
+ if (c.subject) {
65
+ chunks.push(makeChunk(c.hash, "subject", c.subject));
66
+ }
67
+ if (c.body) {
68
+ for (const segment of splitBody(c.body)) {
69
+ chunks.push(makeChunk(c.hash, "body", segment));
70
+ }
71
+ }
72
+ if (c.prTitle)
73
+ chunks.push(makeChunk(c.hash, "pr_title", c.prTitle));
74
+ if (c.prBody) {
75
+ for (const segment of splitBody(c.prBody)) {
76
+ chunks.push(makeChunk(c.hash, "pr_body", segment));
77
+ }
78
+ }
79
+ }
80
+ return chunks;
81
+ }
82
+ function makeChunk(commitHash, kind, text) {
83
+ const id = createHash("sha1").update(`${commitHash}|${kind}|${text}`).digest("hex").slice(0, 16);
84
+ return { id: `${commitHash.slice(0, 12)}-${kind}-${id}`, commitHash, kind, text };
85
+ }
86
+ export function splitBody(text, maxChars = 800) {
87
+ if (text.length <= maxChars)
88
+ return [text];
89
+ const parts = [];
90
+ const paragraphs = text.split(/\n\s*\n/);
91
+ let buf = "";
92
+ for (const p of paragraphs) {
93
+ if (!p.trim())
94
+ continue;
95
+ if (buf.length + p.length + 2 > maxChars && buf) {
96
+ parts.push(buf);
97
+ buf = p;
98
+ }
99
+ else {
100
+ buf = buf ? `${buf}\n\n${p}` : p;
101
+ }
102
+ }
103
+ if (buf)
104
+ parts.push(buf);
105
+ return parts;
106
+ }
107
+ //# sourceMappingURL=indexer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexer.js","sourceRoot":"","sources":["../../src/indexer/indexer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=indexer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexer.test.d.ts","sourceRoot":"","sources":["../../src/indexer/indexer.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,80 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { buildChunks, splitBody } from "./indexer.js";
3
+ const baseCommit = (overrides = {}) => ({
4
+ hash: "abc1234567890abc1234567890abc1234567890a",
5
+ shortHash: "abc1234",
6
+ authorName: "Alice",
7
+ authorEmail: "a@x.io",
8
+ authorDate: "2025-01-01T00:00:00Z",
9
+ committerDate: "2025-01-01T00:00:00Z",
10
+ subject: "subject",
11
+ body: "",
12
+ parents: [],
13
+ files: [],
14
+ ...overrides,
15
+ });
16
+ describe("splitBody", () => {
17
+ it("returns text unchanged when below limit", () => {
18
+ expect(splitBody("hello world", 100)).toEqual(["hello world"]);
19
+ });
20
+ it("splits at paragraph boundaries", () => {
21
+ const text = "para one\n\npara two that is much longer than allowed";
22
+ const parts = splitBody(text, 20);
23
+ expect(parts.length).toBeGreaterThan(1);
24
+ for (const p of parts)
25
+ expect(p.length).toBeLessThanOrEqual(60);
26
+ });
27
+ it("never produces empty parts", () => {
28
+ const text = "a\n\n\n\nb\n\n\n\nc";
29
+ const parts = splitBody(text, 5);
30
+ for (const p of parts)
31
+ expect(p.trim().length).toBeGreaterThan(0);
32
+ });
33
+ it("preserves order", () => {
34
+ const parts = splitBody("ALPHA\n\nBETA\n\nGAMMA", 6);
35
+ const joined = parts.join(" ");
36
+ expect(joined.indexOf("ALPHA")).toBeLessThan(joined.indexOf("BETA"));
37
+ expect(joined.indexOf("BETA")).toBeLessThan(joined.indexOf("GAMMA"));
38
+ });
39
+ });
40
+ describe("buildChunks", () => {
41
+ it("returns subject chunk for every commit with a subject", () => {
42
+ const chunks = buildChunks([baseCommit({ subject: "fix payment bug" })]);
43
+ expect(chunks.some((c) => c.kind === "subject" && c.text === "fix payment bug")).toBe(true);
44
+ });
45
+ it("emits body chunks separate from subject", () => {
46
+ const chunks = buildChunks([
47
+ baseCommit({ subject: "S", body: "explanation in the body" }),
48
+ ]);
49
+ const kinds = new Set(chunks.map((c) => c.kind));
50
+ expect(kinds.has("subject")).toBe(true);
51
+ expect(kinds.has("body")).toBe(true);
52
+ });
53
+ it("emits PR chunks when present", () => {
54
+ const chunks = buildChunks([
55
+ baseCommit({ prTitle: "PR title", prBody: "PR body content" }),
56
+ ]);
57
+ const kinds = new Set(chunks.map((c) => c.kind));
58
+ expect(kinds.has("pr_title")).toBe(true);
59
+ expect(kinds.has("pr_body")).toBe(true);
60
+ });
61
+ it("skips empty fields", () => {
62
+ const chunks = buildChunks([baseCommit({ subject: "", body: "" })]);
63
+ expect(chunks).toEqual([]);
64
+ });
65
+ it("produces deterministic chunk ids", () => {
66
+ const c = baseCommit({ subject: "deterministic" });
67
+ const a = buildChunks([c]);
68
+ const b = buildChunks([c]);
69
+ expect(a.map((x) => x.id)).toEqual(b.map((x) => x.id));
70
+ });
71
+ it("chunks are uniquely keyed across commits", () => {
72
+ const chunks = buildChunks([
73
+ baseCommit({ hash: "1".repeat(40), shortHash: "1111111", subject: "shared" }),
74
+ baseCommit({ hash: "2".repeat(40), shortHash: "2222222", subject: "shared" }),
75
+ ]);
76
+ const ids = chunks.map((c) => c.id);
77
+ expect(new Set(ids).size).toBe(ids.length);
78
+ });
79
+ });
80
+ //# sourceMappingURL=indexer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexer.test.js","sourceRoot":"","sources":["../../src/indexer/indexer.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export * from "./search.js";
2
+ export * from "./rerank.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/retrieve/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from "./search.js";
2
+ export * from "./rerank.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/retrieve/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Reranker — second-stage scoring over the top-K results from search().
3
+ *
4
+ * The first stage (BM25 + vector RRF) optimizes for *recall* — pull a wide net
5
+ * so the right answer is somewhere in the top-50. The reranker then re-scores
6
+ * those candidates with a stronger signal to produce a precise top-3/top-10.
7
+ *
8
+ * Phase 1 ships two implementations:
9
+ * - NoopReranker — passthrough (default, zero overhead)
10
+ * - QueryDensityReranker — scores by lexical density of question terms in
11
+ * the candidate text. Cheap, deterministic, +5–10%
12
+ * precision@3 on most corpora.
13
+ *
14
+ * Phase 2 will add CrossEncoderReranker (calls a small cross-encoder model
15
+ * like bge-reranker-base via Ollama). The interface is stable.
16
+ */
17
+ import type { SearchResult } from "../types.js";
18
+ export interface Reranker {
19
+ readonly name: string;
20
+ rerank(query: string, candidates: SearchResult[], topK: number): Promise<SearchResult[]>;
21
+ }
22
+ export declare class NoopReranker implements Reranker {
23
+ readonly name = "noop";
24
+ rerank(_q: string, candidates: SearchResult[], topK: number): Promise<SearchResult[]>;
25
+ }
26
+ /**
27
+ * Term-density reranker.
28
+ *
29
+ * For each candidate we compute:
30
+ * density = (# query terms appearing in candidate) / (# unique query terms)
31
+ *
32
+ * Final score = α * original_score + (1-α) * density.
33
+ *
34
+ * Cheap, no extra deps, and consistently lifts precision@3 because it
35
+ * down-ranks results that scored high via topical-noise but don't actually
36
+ * contain the user's keywords.
37
+ */
38
+ export declare class QueryDensityReranker implements Reranker {
39
+ private readonly alpha;
40
+ readonly name = "query-density-v1";
41
+ constructor(alpha?: number);
42
+ rerank(query: string, candidates: SearchResult[], topK: number): Promise<SearchResult[]>;
43
+ }
44
+ //# sourceMappingURL=rerank.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rerank.d.ts","sourceRoot":"","sources":["../../src/retrieve/rerank.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAe,YAAY,EAAE,MAAM,aAAa,CAAC;AAE7D,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;CAC1F;AAED,qBAAa,YAAa,YAAW,QAAQ;IAC3C,QAAQ,CAAC,IAAI,UAAU;IACjB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CAG5F;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,oBAAqB,YAAW,QAAQ;IAEvC,OAAO,CAAC,QAAQ,CAAC,KAAK;IADlC,QAAQ,CAAC,IAAI,sBAAsB;gBACN,KAAK,SAAM;IAElC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CAc/F"}
@@ -0,0 +1,68 @@
1
+ export class NoopReranker {
2
+ name = "noop";
3
+ async rerank(_q, candidates, topK) {
4
+ return candidates.slice(0, topK);
5
+ }
6
+ }
7
+ /**
8
+ * Term-density reranker.
9
+ *
10
+ * For each candidate we compute:
11
+ * density = (# query terms appearing in candidate) / (# unique query terms)
12
+ *
13
+ * Final score = α * original_score + (1-α) * density.
14
+ *
15
+ * Cheap, no extra deps, and consistently lifts precision@3 because it
16
+ * down-ranks results that scored high via topical-noise but don't actually
17
+ * contain the user's keywords.
18
+ */
19
+ export class QueryDensityReranker {
20
+ alpha;
21
+ name = "query-density-v1";
22
+ constructor(alpha = 0.6) {
23
+ this.alpha = alpha;
24
+ }
25
+ async rerank(query, candidates, topK) {
26
+ const terms = tokenize(query);
27
+ if (!terms.size)
28
+ return candidates.slice(0, topK);
29
+ const rescored = candidates.map((c) => {
30
+ const text = combineText(c.matchedChunks, c.commit);
31
+ const tokens = tokenize(text);
32
+ const overlap = countOverlap(terms, tokens);
33
+ const density = overlap / terms.size;
34
+ const score = this.alpha * c.score + (1 - this.alpha) * density;
35
+ return { ...c, score };
36
+ });
37
+ rescored.sort((a, b) => b.score - a.score);
38
+ return rescored.slice(0, topK);
39
+ }
40
+ }
41
+ function tokenize(text) {
42
+ const tokens = text
43
+ .toLowerCase()
44
+ .replace(/[^a-z0-9_]+/g, " ")
45
+ .split(/\s+/)
46
+ .filter((t) => t.length >= 2 && !STOP.has(t));
47
+ return new Set(tokens);
48
+ }
49
+ function combineText(chunks, commit) {
50
+ const parts = [commit.subject, commit.body];
51
+ for (const c of chunks)
52
+ parts.push(c.text);
53
+ return parts.filter(Boolean).join(" ");
54
+ }
55
+ function countOverlap(query, doc) {
56
+ let n = 0;
57
+ for (const t of query)
58
+ if (doc.has(t))
59
+ n++;
60
+ return n;
61
+ }
62
+ const STOP = new Set([
63
+ "the", "and", "for", "are", "with", "this", "that", "from", "have", "has",
64
+ "was", "were", "but", "not", "you", "your", "what", "why", "how", "when",
65
+ "where", "who", "does", "did", "can", "could", "should", "would", "will",
66
+ "into", "out", "off", "over", "under", "about",
67
+ ]);
68
+ //# sourceMappingURL=rerank.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rerank.js","sourceRoot":"","sources":["../../src/retrieve/rerank.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rerank.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rerank.test.d.ts","sourceRoot":"","sources":["../../src/retrieve/rerank.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,52 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { NoopReranker, QueryDensityReranker } from "./rerank.js";
3
+ const c = (subject, body = "", score = 0.5) => ({
4
+ commit: {
5
+ hash: subject,
6
+ shortHash: subject,
7
+ authorName: "x",
8
+ authorEmail: "x@x",
9
+ authorDate: "2025-01-01T00:00:00Z",
10
+ committerDate: "2025-01-01T00:00:00Z",
11
+ subject,
12
+ body,
13
+ parents: [],
14
+ files: [],
15
+ },
16
+ score,
17
+ matchedChunks: [],
18
+ });
19
+ describe("NoopReranker", () => {
20
+ it("passes through and truncates to topK", async () => {
21
+ const r = new NoopReranker();
22
+ const out = await r.rerank("q", [c("a"), c("b"), c("c")], 2);
23
+ expect(out.map((x) => x.commit.subject)).toEqual(["a", "b"]);
24
+ });
25
+ });
26
+ describe("QueryDensityReranker", () => {
27
+ const r = new QueryDensityReranker(0.5);
28
+ it("promotes candidates that contain query terms", async () => {
29
+ const out = await r.rerank("stripe webhook idempotency", [
30
+ c("unrelated documentation update", "", 0.8),
31
+ c("stripe webhook idempotency dedup", "", 0.5),
32
+ ], 5);
33
+ expect(out[0].commit.subject).toBe("stripe webhook idempotency dedup");
34
+ });
35
+ it("falls back to original ranking when no query terms match", async () => {
36
+ const out = await r.rerank("xyzzyplugh", [c("alpha beta", "", 0.9), c("gamma delta", "", 0.5)], 5);
37
+ expect(out[0].commit.subject).toBe("alpha beta");
38
+ });
39
+ it("respects alpha = 1 (pure first-stage score)", async () => {
40
+ const pure = new QueryDensityReranker(1);
41
+ const out = await pure.rerank("something", [c("nothing", "", 0.9), c("something here", "", 0.5)], 5);
42
+ expect(out[0].commit.subject).toBe("nothing");
43
+ });
44
+ it("ignores stopwords like 'why' and 'the'", async () => {
45
+ const out = await r.rerank("why does the webhook retry", [
46
+ c("the why does this exist", "", 0.5),
47
+ c("webhook retry mechanism", "", 0.5),
48
+ ], 5);
49
+ expect(out[0].commit.subject).toBe("webhook retry mechanism");
50
+ });
51
+ });
52
+ //# sourceMappingURL=rerank.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rerank.test.js","sourceRoot":"","sources":["../../src/retrieve/rerank.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { AskResult, CommitChunk, EmbeddingProvider, RepoMeta, SearchResult } from "../types.js";
2
+ import type { MnemeStore } from "../store/sqlite.js";
3
+ export interface SearchOptions {
4
+ store: MnemeStore;
5
+ embedder?: EmbeddingProvider;
6
+ repo?: RepoMeta;
7
+ topK?: number;
8
+ /** Weight for semantic vs lexical (0 = pure lexical, 1 = pure semantic). */
9
+ semanticWeight?: number;
10
+ }
11
+ /**
12
+ * Hybrid retrieval: combine FTS (BM25) with vector cosine, fuse with weighted RRF.
13
+ * Falls back to FTS-only if no embedder is configured or no embeddings exist.
14
+ */
15
+ export declare function search(query: string, opts: SearchOptions): Promise<SearchResult[]>;
16
+ export declare function ask(question: string, opts: SearchOptions): Promise<AskResult>;
17
+ export declare function cosine(a: Float32Array, b: Float32Array): number;
18
+ export interface RrfInput {
19
+ chunk: CommitChunk;
20
+ rank: number;
21
+ raw: number;
22
+ }
23
+ export declare function reciprocalRankFusion(lex: RrfInput[], sem: RrfInput[], cfg: {
24
+ lexicalWeight: number;
25
+ semanticWeight: number;
26
+ k: number;
27
+ }): Array<{
28
+ chunk: CommitChunk;
29
+ score: number;
30
+ }>;
31
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/retrieve/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EAGT,WAAW,EACX,iBAAiB,EACjB,QAAQ,EACR,YAAY,EACb,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAiExF;AAED,wBAAsB,GAAG,CACvB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,SAAS,CAAC,CAQpB;AAiDD,wBAAgB,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM,CAc/D;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,WAAW,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,QAAQ,EAAE,EACf,GAAG,EAAE,QAAQ,EAAE,EACf,GAAG,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAChE,KAAK,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAW9C"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Hybrid retrieval: combine FTS (BM25) with vector cosine, fuse with weighted RRF.
3
+ * Falls back to FTS-only if no embedder is configured or no embeddings exist.
4
+ */
5
+ export async function search(query, opts) {
6
+ const topK = opts.topK ?? 10;
7
+ const semanticWeight = clamp(opts.semanticWeight ?? 0.65, 0, 1);
8
+ const ftsHits = opts.store.ftsSearch(query, topK * 4);
9
+ const ftsScored = ftsHits.map((h, i) => ({
10
+ chunk: { id: h.id, commitHash: h.commitHash, kind: h.kind, text: h.text },
11
+ rank: i + 1,
12
+ raw: h.bm25,
13
+ }));
14
+ let semanticScored = [];
15
+ if (opts.embedder && opts.store.countChunksWithEmbedding() > 0) {
16
+ try {
17
+ const [qvec] = await opts.embedder.embed([query]);
18
+ if (qvec) {
19
+ const sims = [];
20
+ for (const r of opts.store.iterEmbeddedChunks()) {
21
+ // Skip chunks whose embedding dimension differs from the query —
22
+ // happens when the configured embedder is different from the one
23
+ // used at index time. cosine() returns 0 for mismatches, but we
24
+ // also avoid emitting noisy zero-scored hits.
25
+ if (r.vec.length !== qvec.length)
26
+ continue;
27
+ const sim = cosine(qvec, r.vec);
28
+ sims.push({
29
+ chunk: { id: r.id, commitHash: r.commitHash, kind: r.kind, text: r.text },
30
+ sim,
31
+ });
32
+ }
33
+ sims.sort((a, b) => b.sim - a.sim);
34
+ semanticScored = sims.slice(0, topK * 4).map((s, i) => ({ chunk: s.chunk, rank: i + 1, raw: s.sim }));
35
+ }
36
+ }
37
+ catch {
38
+ // Embedder unavailable (e.g. Ollama model not pulled). Fall back to
39
+ // lexical-only retrieval — better than crashing the whole query.
40
+ semanticScored = [];
41
+ }
42
+ }
43
+ const fused = reciprocalRankFusion(ftsScored, semanticScored, {
44
+ lexicalWeight: 1 - semanticWeight,
45
+ semanticWeight,
46
+ k: 60,
47
+ });
48
+ const byCommit = new Map();
49
+ for (const f of fused) {
50
+ const existing = byCommit.get(f.chunk.commitHash);
51
+ if (existing) {
52
+ existing.score = Math.max(existing.score, f.score);
53
+ existing.matchedChunks.push(f.chunk);
54
+ }
55
+ else {
56
+ const commit = opts.store.getCommit(f.chunk.commitHash);
57
+ if (!commit)
58
+ continue;
59
+ byCommit.set(f.chunk.commitHash, {
60
+ commit,
61
+ score: f.score,
62
+ matchedChunks: [f.chunk],
63
+ });
64
+ }
65
+ }
66
+ return Array.from(byCommit.values())
67
+ .sort((a, b) => b.score - a.score)
68
+ .slice(0, topK);
69
+ }
70
+ export async function ask(question, opts) {
71
+ const results = await search(question, opts);
72
+ return {
73
+ question,
74
+ summary: synthesizeSummary(question, results),
75
+ citations: results.flatMap((r) => commitToCitations(r.commit, opts.repo)).slice(0, 8),
76
+ searchResults: results,
77
+ };
78
+ }
79
+ function commitToCitations(commit, repo) {
80
+ const cites = [];
81
+ cites.push({
82
+ kind: "commit",
83
+ id: commit.shortHash || commit.hash.slice(0, 7),
84
+ title: commit.subject,
85
+ url: buildCommitUrl(commit.hash, repo),
86
+ excerpt: truncate(commit.body || commit.subject, 240),
87
+ });
88
+ if (commit.prNumber && repo?.host === "github" && repo.owner && repo.repo) {
89
+ cites.push({
90
+ kind: "pr",
91
+ id: `#${commit.prNumber}`,
92
+ title: commit.prTitle ?? commit.subject,
93
+ url: `https://github.com/${repo.owner}/${repo.repo}/pull/${commit.prNumber}`,
94
+ excerpt: truncate(commit.prBody ?? commit.body, 240),
95
+ });
96
+ }
97
+ return cites;
98
+ }
99
+ function buildCommitUrl(hash, repo) {
100
+ if (!repo?.owner || !repo.repo)
101
+ return undefined;
102
+ if (repo.host === "github")
103
+ return `https://github.com/${repo.owner}/${repo.repo}/commit/${hash}`;
104
+ if (repo.host === "gitlab")
105
+ return `https://gitlab.com/${repo.owner}/${repo.repo}/-/commit/${hash}`;
106
+ if (repo.host === "bitbucket")
107
+ return `https://bitbucket.org/${repo.owner}/${repo.repo}/commits/${hash}`;
108
+ return undefined;
109
+ }
110
+ function synthesizeSummary(question, results) {
111
+ if (!results.length) {
112
+ return `No relevant commits or PRs were found for: "${question}". This usually means the WHY behind this code lives outside the git history (slack threads, design docs, in-person decisions).`;
113
+ }
114
+ const top = results.slice(0, 3);
115
+ const lines = top.map((r, i) => {
116
+ const date = r.commit.authorDate.slice(0, 10);
117
+ const author = r.commit.authorName;
118
+ const ref = r.commit.prNumber ? `PR #${r.commit.prNumber}` : r.commit.shortHash;
119
+ return `${i + 1}. ${ref} (${date}, ${author}): ${r.commit.subject}`;
120
+ });
121
+ return [
122
+ `Found ${results.length} relevant commit(s) for: "${question}".`,
123
+ "Top matches:",
124
+ ...lines,
125
+ ].join("\n");
126
+ }
127
+ export function cosine(a, b) {
128
+ if (a.length !== b.length)
129
+ return 0;
130
+ let dot = 0;
131
+ let na = 0;
132
+ let nb = 0;
133
+ for (let i = 0; i < a.length; i++) {
134
+ const av = a[i];
135
+ const bv = b[i];
136
+ dot += av * bv;
137
+ na += av * av;
138
+ nb += bv * bv;
139
+ }
140
+ const denom = Math.sqrt(na) * Math.sqrt(nb);
141
+ return denom === 0 ? 0 : dot / denom;
142
+ }
143
+ export function reciprocalRankFusion(lex, sem, cfg) {
144
+ const map = new Map();
145
+ for (const r of lex) {
146
+ const inc = cfg.lexicalWeight / (cfg.k + r.rank);
147
+ upsert(map, r.chunk, inc);
148
+ }
149
+ for (const r of sem) {
150
+ const inc = cfg.semanticWeight / (cfg.k + r.rank);
151
+ upsert(map, r.chunk, inc);
152
+ }
153
+ return Array.from(map.values()).sort((a, b) => b.score - a.score);
154
+ }
155
+ function upsert(map, chunk, inc) {
156
+ const cur = map.get(chunk.id);
157
+ if (cur)
158
+ cur.score += inc;
159
+ else
160
+ map.set(chunk.id, { chunk, score: inc });
161
+ }
162
+ function truncate(s, n) {
163
+ if (s.length <= n)
164
+ return s;
165
+ return `${s.slice(0, n - 1)}…`;
166
+ }
167
+ function clamp(n, lo, hi) {
168
+ return Math.min(hi, Math.max(lo, n));
169
+ }
170
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/retrieve/search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=search.test.d.ts.map