@mneme-ai/core 0.9.0 → 0.10.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.
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/insights/bus-factor.d.ts +58 -0
- package/dist/insights/bus-factor.d.ts.map +1 -0
- package/dist/insights/bus-factor.js +117 -0
- package/dist/insights/bus-factor.js.map +1 -0
- package/dist/insights/bus-factor.test.d.ts +2 -0
- package/dist/insights/bus-factor.test.d.ts.map +1 -0
- package/dist/insights/bus-factor.test.js +149 -0
- package/dist/insights/bus-factor.test.js.map +1 -0
- package/dist/insights/commit-coach.d.ts +80 -0
- package/dist/insights/commit-coach.d.ts.map +1 -0
- package/dist/insights/commit-coach.js +230 -0
- package/dist/insights/commit-coach.js.map +1 -0
- package/dist/insights/commit-coach.test.d.ts +2 -0
- package/dist/insights/commit-coach.test.d.ts.map +1 -0
- package/dist/insights/commit-coach.test.js +163 -0
- package/dist/insights/commit-coach.test.js.map +1 -0
- package/dist/insights/crystal-ball.d.ts +76 -0
- package/dist/insights/crystal-ball.d.ts.map +1 -0
- package/dist/insights/crystal-ball.js +219 -0
- package/dist/insights/crystal-ball.js.map +1 -0
- package/dist/insights/crystal-ball.test.d.ts +2 -0
- package/dist/insights/crystal-ball.test.d.ts.map +1 -0
- package/dist/insights/crystal-ball.test.js +157 -0
- package/dist/insights/crystal-ball.test.js.map +1 -0
- package/dist/insights/index.d.ts +5 -0
- package/dist/insights/index.d.ts.map +1 -1
- package/dist/insights/index.js +5 -0
- package/dist/insights/index.js.map +1 -1
- package/dist/insights/paradox.d.ts +36 -0
- package/dist/insights/paradox.d.ts.map +1 -0
- package/dist/insights/paradox.js +201 -0
- package/dist/insights/paradox.js.map +1 -0
- package/dist/insights/paradox.test.d.ts +2 -0
- package/dist/insights/paradox.test.d.ts.map +1 -0
- package/dist/insights/paradox.test.js +88 -0
- package/dist/insights/paradox.test.js.map +1 -0
- package/dist/insights/regret.d.ts +57 -0
- package/dist/insights/regret.d.ts.map +1 -0
- package/dist/insights/regret.js +137 -0
- package/dist/insights/regret.js.map +1 -0
- package/dist/insights/regret.test.d.ts +2 -0
- package/dist/insights/regret.test.d.ts.map +1 -0
- package/dist/insights/regret.test.js +153 -0
- package/dist/insights/regret.test.js.map +1 -0
- package/dist/insights/who-knows.d.ts +18 -0
- package/dist/insights/who-knows.d.ts.map +1 -1
- package/dist/insights/who-knows.js +29 -0
- package/dist/insights/who-knows.js.map +1 -1
- package/dist/insights/who-knows.test.js +63 -1
- package/dist/insights/who-knows.test.js.map +1 -1
- package/dist/quant/alpha.d.ts +87 -0
- package/dist/quant/alpha.d.ts.map +1 -0
- package/dist/quant/alpha.js +103 -0
- package/dist/quant/alpha.js.map +1 -0
- package/dist/quant/alpha.test.d.ts +2 -0
- package/dist/quant/alpha.test.d.ts.map +1 -0
- package/dist/quant/alpha.test.js +147 -0
- package/dist/quant/alpha.test.js.map +1 -0
- package/dist/quant/backtest.d.ts +57 -0
- package/dist/quant/backtest.d.ts.map +1 -0
- package/dist/quant/backtest.js +90 -0
- package/dist/quant/backtest.js.map +1 -0
- package/dist/quant/backtest.test.d.ts +2 -0
- package/dist/quant/backtest.test.d.ts.map +1 -0
- package/dist/quant/backtest.test.js +133 -0
- package/dist/quant/backtest.test.js.map +1 -0
- package/dist/quant/black-swan.d.ts +45 -0
- package/dist/quant/black-swan.d.ts.map +1 -0
- package/dist/quant/black-swan.js +112 -0
- package/dist/quant/black-swan.js.map +1 -0
- package/dist/quant/black-swan.test.d.ts +2 -0
- package/dist/quant/black-swan.test.d.ts.map +1 -0
- package/dist/quant/black-swan.test.js +131 -0
- package/dist/quant/black-swan.test.js.map +1 -0
- package/dist/quant/correlation-matrix.d.ts +54 -0
- package/dist/quant/correlation-matrix.d.ts.map +1 -0
- package/dist/quant/correlation-matrix.js +103 -0
- package/dist/quant/correlation-matrix.js.map +1 -0
- package/dist/quant/correlation-matrix.test.d.ts +2 -0
- package/dist/quant/correlation-matrix.test.d.ts.map +1 -0
- package/dist/quant/correlation-matrix.test.js +118 -0
- package/dist/quant/correlation-matrix.test.js.map +1 -0
- package/dist/quant/drawdown.d.ts +51 -0
- package/dist/quant/drawdown.d.ts.map +1 -0
- package/dist/quant/drawdown.js +96 -0
- package/dist/quant/drawdown.js.map +1 -0
- package/dist/quant/drawdown.test.d.ts +2 -0
- package/dist/quant/drawdown.test.d.ts.map +1 -0
- package/dist/quant/drawdown.test.js +166 -0
- package/dist/quant/drawdown.test.js.map +1 -0
- package/dist/quant/greek.d.ts +55 -0
- package/dist/quant/greek.d.ts.map +1 -0
- package/dist/quant/greek.js +157 -0
- package/dist/quant/greek.js.map +1 -0
- package/dist/quant/greek.test.d.ts +2 -0
- package/dist/quant/greek.test.d.ts.map +1 -0
- package/dist/quant/greek.test.js +138 -0
- package/dist/quant/greek.test.js.map +1 -0
- package/dist/quant/implied-volatility.d.ts +65 -0
- package/dist/quant/implied-volatility.d.ts.map +1 -0
- package/dist/quant/implied-volatility.js +149 -0
- package/dist/quant/implied-volatility.js.map +1 -0
- package/dist/quant/implied-volatility.test.d.ts +2 -0
- package/dist/quant/implied-volatility.test.d.ts.map +1 -0
- package/dist/quant/implied-volatility.test.js +127 -0
- package/dist/quant/implied-volatility.test.js.map +1 -0
- package/dist/quant/index.d.ts +28 -0
- package/dist/quant/index.d.ts.map +1 -0
- package/dist/quant/index.js +28 -0
- package/dist/quant/index.js.map +1 -0
- package/dist/quant/insider-trading.d.ts +56 -0
- package/dist/quant/insider-trading.d.ts.map +1 -0
- package/dist/quant/insider-trading.js +129 -0
- package/dist/quant/insider-trading.js.map +1 -0
- package/dist/quant/insider-trading.test.d.ts +2 -0
- package/dist/quant/insider-trading.test.d.ts.map +1 -0
- package/dist/quant/insider-trading.test.js +130 -0
- package/dist/quant/insider-trading.test.js.map +1 -0
- package/dist/quant/moneyball.d.ts +48 -0
- package/dist/quant/moneyball.d.ts.map +1 -0
- package/dist/quant/moneyball.js +110 -0
- package/dist/quant/moneyball.js.map +1 -0
- package/dist/quant/moneyball.test.d.ts +2 -0
- package/dist/quant/moneyball.test.d.ts.map +1 -0
- package/dist/quant/moneyball.test.js +137 -0
- package/dist/quant/moneyball.test.js.map +1 -0
- package/dist/quant/tax-loss-harvest.d.ts +59 -0
- package/dist/quant/tax-loss-harvest.d.ts.map +1 -0
- package/dist/quant/tax-loss-harvest.js +126 -0
- package/dist/quant/tax-loss-harvest.js.map +1 -0
- package/dist/quant/tax-loss-harvest.test.d.ts +2 -0
- package/dist/quant/tax-loss-harvest.test.d.ts.map +1 -0
- package/dist/quant/tax-loss-harvest.test.js +126 -0
- package/dist/quant/tax-loss-harvest.test.js.map +1 -0
- package/dist/retrieve/synthesize.d.ts.map +1 -1
- package/dist/retrieve/synthesize.js +56 -25
- package/dist/retrieve/synthesize.js.map +1 -1
- package/dist/retrieve/synthesize.test.js +26 -15
- package/dist/retrieve/synthesize.test.js.map +1 -1
- package/dist/store/schema.d.ts +2 -2
- package/dist/store/schema.d.ts.map +1 -1
- package/dist/store/schema.js +6 -2
- package/dist/store/schema.js.map +1 -1
- package/dist/store/sqlite.d.ts +2 -0
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +24 -0
- package/dist/store/sqlite.js.map +1 -1
- package/dist/store/sqlite.test.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mneme tax-loss-harvest` — find dead code that can be deleted to "offset"
|
|
3
|
+
* technical debt.
|
|
4
|
+
*
|
|
5
|
+
* The metaphor: in finance, you sell losing positions to harvest tax losses,
|
|
6
|
+
* which offset gains elsewhere. In a codebase, deleting dead code reduces
|
|
7
|
+
* cognitive surface area, freeing up budget to absorb new debt elsewhere.
|
|
8
|
+
*
|
|
9
|
+
* Scoring per file:
|
|
10
|
+
* • staleness — days since last touch
|
|
11
|
+
* • commit count — fewer commits = less load-bearing
|
|
12
|
+
* • entity references — files with no entities or no inbound refs
|
|
13
|
+
* • risk if deleted — files with incidents historically = higher risk
|
|
14
|
+
*
|
|
15
|
+
* harvest_score = staleness × (1 / max(commits, 1)) × (1 - risk)
|
|
16
|
+
*
|
|
17
|
+
* Pure data analysis. No LLM. Output is a candidate list with risk gauges.
|
|
18
|
+
*/
|
|
19
|
+
export function findHarvestCandidates(store, opts = {}) {
|
|
20
|
+
const minStale = opts.minStaleDays ?? 180;
|
|
21
|
+
const maxCommits = opts.maxCommits ?? 5;
|
|
22
|
+
const topN = opts.topN ?? 20;
|
|
23
|
+
const now = opts.now ?? new Date();
|
|
24
|
+
// 1. Per-file commit count + last touch.
|
|
25
|
+
const fileRows = store.db
|
|
26
|
+
.prepare(`SELECT
|
|
27
|
+
fc.path AS path,
|
|
28
|
+
COUNT(*) AS commits,
|
|
29
|
+
MAX(c.author_date) AS last_touch
|
|
30
|
+
FROM file_changes fc
|
|
31
|
+
JOIN commits c ON c.hash = fc.commit_hash
|
|
32
|
+
GROUP BY fc.path`)
|
|
33
|
+
.all();
|
|
34
|
+
// 2. Entity count per file.
|
|
35
|
+
const entityRows = store.db
|
|
36
|
+
.prepare(`SELECT file_path, COUNT(*) AS n FROM entities GROUP BY file_path`)
|
|
37
|
+
.all();
|
|
38
|
+
const entityCountByFile = new Map(entityRows.map((r) => [r.file_path, r.n]));
|
|
39
|
+
// 3. Incident-affected files (best-effort).
|
|
40
|
+
const incidentRows = store.db
|
|
41
|
+
.prepare(`SELECT affected_files FROM incidents`)
|
|
42
|
+
.all();
|
|
43
|
+
const incidentCountByFile = new Map();
|
|
44
|
+
for (const r of incidentRows) {
|
|
45
|
+
if (!r.affected_files)
|
|
46
|
+
continue;
|
|
47
|
+
let files = [];
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(r.affected_files);
|
|
50
|
+
if (Array.isArray(parsed))
|
|
51
|
+
files = parsed.filter((x) => typeof x === "string");
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
files = r.affected_files.split(",").map((s) => s.trim()).filter(Boolean);
|
|
55
|
+
}
|
|
56
|
+
for (const f of files)
|
|
57
|
+
incidentCountByFile.set(f, (incidentCountByFile.get(f) ?? 0) + 1);
|
|
58
|
+
}
|
|
59
|
+
// 4. Score each candidate.
|
|
60
|
+
const candidates = [];
|
|
61
|
+
for (const r of fileRows) {
|
|
62
|
+
const days = (now.getTime() - new Date(r.last_touch).getTime()) / 86_400_000;
|
|
63
|
+
if (days < minStale)
|
|
64
|
+
continue;
|
|
65
|
+
if (r.commits > maxCommits)
|
|
66
|
+
continue;
|
|
67
|
+
const entities = entityCountByFile.get(r.path) ?? 0;
|
|
68
|
+
const incidents = incidentCountByFile.get(r.path) ?? 0;
|
|
69
|
+
// Risk: incident history makes deletion riskier.
|
|
70
|
+
const risk = Math.min(1, incidents / 5);
|
|
71
|
+
const harvestScore = (days / 365) * (1 / Math.max(r.commits, 1)) * (1 - risk);
|
|
72
|
+
candidates.push({
|
|
73
|
+
filePath: r.path,
|
|
74
|
+
daysSinceTouch: Math.round(days),
|
|
75
|
+
commitCount: r.commits,
|
|
76
|
+
entityCount: entities,
|
|
77
|
+
incidentCount: incidents,
|
|
78
|
+
harvestScore: Math.round(harvestScore * 1000) / 1000,
|
|
79
|
+
risk: classifyHarvestRisk(incidents, entities, r.commits),
|
|
80
|
+
recommendation: buildHarvestRecommendation(days, entities, incidents),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
candidates.sort((a, b) => b.harvestScore - a.harvestScore);
|
|
84
|
+
return candidates.slice(0, topN);
|
|
85
|
+
}
|
|
86
|
+
export function classifyHarvestRisk(incidents, entities, commits) {
|
|
87
|
+
if (incidents >= 3)
|
|
88
|
+
return "risky";
|
|
89
|
+
if (incidents >= 1 || entities >= 10)
|
|
90
|
+
return "moderate";
|
|
91
|
+
if (commits >= 4)
|
|
92
|
+
return "low-risk";
|
|
93
|
+
return "safe";
|
|
94
|
+
}
|
|
95
|
+
function buildHarvestRecommendation(days, entities, incidents) {
|
|
96
|
+
if (incidents >= 3) {
|
|
97
|
+
return "Risky to delete — past incidents associated with this file. Inspect manually.";
|
|
98
|
+
}
|
|
99
|
+
if (entities === 0 && days > 365) {
|
|
100
|
+
return "Likely safe to delete — no entities parsed and untouched for over a year.";
|
|
101
|
+
}
|
|
102
|
+
if (entities === 0) {
|
|
103
|
+
return "Probably dead — no entities. Verify no runtime imports before deleting.";
|
|
104
|
+
}
|
|
105
|
+
if (days > 730) {
|
|
106
|
+
return "Untouched for 2+ years. Strong candidate, but check downstream usage first.";
|
|
107
|
+
}
|
|
108
|
+
return "Stale candidate. Run static analysis to confirm no inbound references.";
|
|
109
|
+
}
|
|
110
|
+
export function summarizeHarvest(candidates) {
|
|
111
|
+
if (candidates.length === 0) {
|
|
112
|
+
return { candidateCount: 0, estimatedLinesSaved: 0, netSavings: 0, summary: "No harvestable candidates." };
|
|
113
|
+
}
|
|
114
|
+
// Approximate 50 LOC per file as a rough heuristic — refine by reading file size if needed.
|
|
115
|
+
const linesPerFile = 50;
|
|
116
|
+
const estLines = candidates.length * linesPerFile;
|
|
117
|
+
const avgRisk = candidates.reduce((s, c) => s + (c.risk === "risky" ? 1 : c.risk === "moderate" ? 0.5 : 0.1), 0) / candidates.length;
|
|
118
|
+
const netSavings = Math.round(estLines * (1 - avgRisk));
|
|
119
|
+
return {
|
|
120
|
+
candidateCount: candidates.length,
|
|
121
|
+
estimatedLinesSaved: estLines,
|
|
122
|
+
netSavings,
|
|
123
|
+
summary: `${candidates.length} harvestable files — ~${estLines} LOC potential, ~${netSavings} net of risk.`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=tax-loss-harvest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tax-loss-harvest.js","sourceRoot":"","sources":["../../src/quant/tax-loss-harvest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tax-loss-harvest.test.d.ts","sourceRoot":"","sources":["../../src/quant/tax-loss-harvest.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { MnemeStore } from "../store/sqlite.js";
|
|
6
|
+
import { findHarvestCandidates, classifyHarvestRisk, summarizeHarvest } from "./tax-loss-harvest.js";
|
|
7
|
+
let tmpDir;
|
|
8
|
+
let store;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
tmpDir = mkdtempSync(join(tmpdir(), "mneme-tlh-"));
|
|
11
|
+
store = new MnemeStore(join(tmpDir, "mneme.db"));
|
|
12
|
+
});
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
store.close();
|
|
15
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
16
|
+
});
|
|
17
|
+
const cmt = (hash, date, files) => ({
|
|
18
|
+
hash,
|
|
19
|
+
shortHash: hash.slice(0, 7),
|
|
20
|
+
authorName: "alice",
|
|
21
|
+
authorEmail: "a@x",
|
|
22
|
+
authorDate: `${date}T00:00:00Z`,
|
|
23
|
+
committerDate: `${date}T00:00:00Z`,
|
|
24
|
+
subject: "x",
|
|
25
|
+
body: "",
|
|
26
|
+
parents: [],
|
|
27
|
+
files,
|
|
28
|
+
});
|
|
29
|
+
function seed(commits) {
|
|
30
|
+
store.upsertCommits(commits);
|
|
31
|
+
for (const c of commits) {
|
|
32
|
+
store.upsertFileChanges(c.files.map((f) => ({ commitHash: c.hash, path: f, changeKind: "M", insertions: 1, deletions: 0 })));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function seedIncident(affectedFiles) {
|
|
36
|
+
store.db
|
|
37
|
+
.prepare(`INSERT INTO incidents (id, source, title, occurred_at, severity, affected_files)
|
|
38
|
+
VALUES (?, 'manual', 'inc', ?, 'medium', ?)`)
|
|
39
|
+
.run(`inc-${Math.random()}`, "2024-01-01T00:00:00Z", JSON.stringify(affectedFiles));
|
|
40
|
+
}
|
|
41
|
+
describe("classifyHarvestRisk", () => {
|
|
42
|
+
it("'risky' when incidents ≥ 3", () => {
|
|
43
|
+
expect(classifyHarvestRisk(3, 0, 1)).toBe("risky");
|
|
44
|
+
});
|
|
45
|
+
it("'moderate' when entities ≥ 10 OR incidents ≥ 1", () => {
|
|
46
|
+
expect(classifyHarvestRisk(0, 12, 1)).toBe("moderate");
|
|
47
|
+
expect(classifyHarvestRisk(1, 0, 1)).toBe("moderate");
|
|
48
|
+
});
|
|
49
|
+
it("'low-risk' when commits ≥ 4 (some history)", () => {
|
|
50
|
+
expect(classifyHarvestRisk(0, 0, 4)).toBe("low-risk");
|
|
51
|
+
});
|
|
52
|
+
it("'safe' when nothing else applies", () => {
|
|
53
|
+
expect(classifyHarvestRisk(0, 0, 1)).toBe("safe");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe("findHarvestCandidates — basic detection", () => {
|
|
57
|
+
it("returns empty when no files are stale enough", () => {
|
|
58
|
+
seed([cmt("a1", "2024-11-01", ["src/active.ts"])]);
|
|
59
|
+
expect(findHarvestCandidates(store, { now: new Date("2024-12-01"), minStaleDays: 180 })).toEqual([]);
|
|
60
|
+
});
|
|
61
|
+
it("flags a stale solo-touched file as a candidate", () => {
|
|
62
|
+
seed([cmt("a1", "2023-01-01", ["src/legacy.ts"])]);
|
|
63
|
+
const candidates = findHarvestCandidates(store, { now: new Date("2024-12-01") });
|
|
64
|
+
expect(candidates.find((c) => c.filePath === "src/legacy.ts")).toBeDefined();
|
|
65
|
+
});
|
|
66
|
+
it("respects maxCommits filter", () => {
|
|
67
|
+
const commits = Array.from({ length: 10 }, (_, i) => cmt(`a${i}`.padEnd(7, "x"), "2023-01-01", ["src/hot.ts"]));
|
|
68
|
+
seed(commits);
|
|
69
|
+
expect(findHarvestCandidates(store, { now: new Date("2024-12-01"), maxCommits: 5 }).find((c) => c.filePath === "src/hot.ts")).toBeUndefined();
|
|
70
|
+
});
|
|
71
|
+
it("higher harvest score for older + lower-commit files", () => {
|
|
72
|
+
seed([
|
|
73
|
+
cmt("a1", "2020-01-01", ["src/very-old.ts"]),
|
|
74
|
+
cmt("b1", "2023-06-01", ["src/recent-stale.ts"]),
|
|
75
|
+
]);
|
|
76
|
+
const candidates = findHarvestCandidates(store, { now: new Date("2024-12-01"), minStaleDays: 90 });
|
|
77
|
+
expect(candidates[0].filePath).toBe("src/very-old.ts");
|
|
78
|
+
});
|
|
79
|
+
it("'risky' tier when file appears in 3+ incidents", () => {
|
|
80
|
+
seed([cmt("a1", "2023-01-01", ["src/payment.ts"])]);
|
|
81
|
+
seedIncident(["src/payment.ts"]);
|
|
82
|
+
seedIncident(["src/payment.ts"]);
|
|
83
|
+
seedIncident(["src/payment.ts"]);
|
|
84
|
+
const c = findHarvestCandidates(store, { now: new Date("2024-12-01") }).find((x) => x.filePath === "src/payment.ts");
|
|
85
|
+
expect(c.risk).toBe("risky");
|
|
86
|
+
expect(c.recommendation.toLowerCase()).toMatch(/risk|past incidents/);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe("summarizeHarvest", () => {
|
|
90
|
+
it("returns zero metrics for empty input", () => {
|
|
91
|
+
const s = summarizeHarvest([]);
|
|
92
|
+
expect(s.candidateCount).toBe(0);
|
|
93
|
+
expect(s.estimatedLinesSaved).toBe(0);
|
|
94
|
+
expect(s.summary.toLowerCase()).toContain("no");
|
|
95
|
+
});
|
|
96
|
+
it("computes net-of-risk savings", () => {
|
|
97
|
+
const candidates = [
|
|
98
|
+
{
|
|
99
|
+
filePath: "src/a.ts",
|
|
100
|
+
daysSinceTouch: 500,
|
|
101
|
+
commitCount: 1,
|
|
102
|
+
entityCount: 0,
|
|
103
|
+
incidentCount: 0,
|
|
104
|
+
harvestScore: 1,
|
|
105
|
+
risk: "safe",
|
|
106
|
+
recommendation: "",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
filePath: "src/b.ts",
|
|
110
|
+
daysSinceTouch: 600,
|
|
111
|
+
commitCount: 1,
|
|
112
|
+
entityCount: 0,
|
|
113
|
+
incidentCount: 4,
|
|
114
|
+
harvestScore: 0.5,
|
|
115
|
+
risk: "risky",
|
|
116
|
+
recommendation: "",
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
const s = summarizeHarvest(candidates);
|
|
120
|
+
expect(s.candidateCount).toBe(2);
|
|
121
|
+
expect(s.estimatedLinesSaved).toBe(100);
|
|
122
|
+
// avg risk = (0.1 + 1) / 2 = 0.55 → net = 100 × (1 - 0.55) = 45
|
|
123
|
+
expect(s.netSavings).toBe(45);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
//# sourceMappingURL=tax-loss-harvest.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tax-loss-harvest.test.js","sourceRoot":"","sources":["../../src/quant/tax-loss-harvest.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"synthesize.d.ts","sourceRoot":"","sources":["../../src/retrieve/synthesize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,KAAK,EAAE;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,EAAE,KAAK,GAAG,YAAY,GAAG,YAAY,CAAC;IAC5C,oFAAoF;IACpF,UAAU,EAAE,eAAe,CAAC;IAC5B,uDAAuD;IACvD,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"synthesize.d.ts","sourceRoot":"","sources":["../../src/retrieve/synthesize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,KAAK,EAAE;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,EAAE,KAAK,GAAG,YAAY,GAAG,YAAY,CAAC;IAC5C,oFAAoF;IACpF,UAAU,EAAE,eAAe,CAAC;IAC5B,uDAAuD;IACvD,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;CACpB;AAmBD;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,YAAY,EAAE,EACvB,UAAU,EAAE,eAAe,EAC3B,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,UAAU,SAAI,GACb,OAAO,CAAC,iBAAiB,CAAC,CAoD5B"}
|
|
@@ -16,13 +16,19 @@
|
|
|
16
16
|
*/
|
|
17
17
|
const SYNTH_SYSTEM_PROMPT = [
|
|
18
18
|
"You are a senior engineer answering a question about a code repository's history.",
|
|
19
|
-
"You
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
19
|
+
"You receive the user's question and the top-K commits retrieved from git.",
|
|
20
|
+
"",
|
|
21
|
+
"Write a VERDICT-SHAPED answer:",
|
|
22
|
+
" Line 1: A single declarative sentence stating the verdict.",
|
|
23
|
+
" Line 2-3: 1-2 sentences of supporting evidence, citing at least one",
|
|
24
|
+
" commit hash using `inline backticks`.",
|
|
25
|
+
" Optionally line 4: A 1-line caveat IF the evidence is thin.",
|
|
26
|
+
"",
|
|
27
|
+
"Strict rules:",
|
|
28
|
+
" • Never invent claims. If evidence is contradictory, say \"the evidence is mixed\".",
|
|
29
|
+
" • No lists, no markdown headings, no preamble like \"Based on the commits...\".",
|
|
30
|
+
" • Don't hedge with \"Possibly\", \"Likely\", \"It seems\" in line 1 — STATE the verdict.",
|
|
31
|
+
" • The user already sees the evidence cards below. Don't repeat them.",
|
|
26
32
|
].join("\n");
|
|
27
33
|
/**
|
|
28
34
|
* Synthesize an answer from retrieval results.
|
|
@@ -127,26 +133,51 @@ function cleanLlmOutput(text) {
|
|
|
127
133
|
return t.trim();
|
|
128
134
|
}
|
|
129
135
|
/**
|
|
130
|
-
* Template-based answer — used when no LLM is configured or LLM fails.
|
|
131
|
-
*
|
|
136
|
+
* Template-based VERDICT answer — used when no LLM is configured or LLM fails.
|
|
137
|
+
* Aggregates results into an actionable summary, not a list of evidence rows.
|
|
138
|
+
*
|
|
139
|
+
* Shape:
|
|
140
|
+
* Line 1 — verdict ("X did 73% of the work" / "Most relevant commit is `abc`")
|
|
141
|
+
* Line 2 — supporting context (date range or top file)
|
|
142
|
+
* (Caveat if confidence is low.)
|
|
132
143
|
*/
|
|
133
144
|
function extractiveAnswer(_question, results, confidence) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
145
|
+
if (results.length === 0)
|
|
146
|
+
return "No matching commits found.";
|
|
147
|
+
const top = results[0];
|
|
148
|
+
const topHash = top.commit.shortHash || top.commit.hash.slice(0, 7);
|
|
149
|
+
// Aggregate author distribution.
|
|
150
|
+
const authors = new Map();
|
|
151
|
+
for (const r of results) {
|
|
152
|
+
const a = r.commit.authorName;
|
|
153
|
+
authors.set(a, (authors.get(a) ?? 0) + 1);
|
|
154
|
+
}
|
|
155
|
+
const sortedAuthors = [...authors.entries()].sort((a, b) => b[1] - a[1]);
|
|
156
|
+
const topAuthor = sortedAuthors[0];
|
|
157
|
+
const topAuthorPct = Math.round((topAuthor[1] / results.length) * 100);
|
|
158
|
+
// Date range.
|
|
159
|
+
const dates = results.map((r) => r.commit.authorDate.slice(0, 10)).sort();
|
|
160
|
+
const fromDate = dates[0];
|
|
161
|
+
const toDate = dates[dates.length - 1];
|
|
162
|
+
const dateRange = fromDate === toDate ? fromDate : `${fromDate} → ${toDate}`;
|
|
163
|
+
// Verdict line — author-aggregated when one dominates, otherwise commit-first.
|
|
164
|
+
let verdict;
|
|
165
|
+
if (topAuthorPct >= 60 && results.length >= 3) {
|
|
166
|
+
verdict = `${topAuthor[0]} authored ${topAuthor[1]} of ${results.length} relevant commits (${topAuthorPct}%) — likely the person to ask.`;
|
|
167
|
+
}
|
|
168
|
+
else if (results.length === 1) {
|
|
169
|
+
verdict = `The single relevant commit is \`${topHash}\` — ${top.commit.subject}.`;
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
verdict = `${results.length} relevant commits across ${sortedAuthors.length} author(s); the strongest match is \`${topHash}\` (${top.commit.authorName}).`;
|
|
173
|
+
}
|
|
174
|
+
// Supporting line — date range + top commit subject (if not already in verdict).
|
|
175
|
+
const support = `Range: ${dateRange}. Top: \`${topHash}\` — ${top.commit.subject}.`;
|
|
176
|
+
// Caveat for thin evidence.
|
|
177
|
+
const caveat = confidence === "low"
|
|
178
|
+
? " The evidence is thin — verify before trusting this answer."
|
|
179
|
+
: "";
|
|
180
|
+
return `${verdict}\n${support}${caveat}`;
|
|
150
181
|
}
|
|
151
182
|
/** Honest "no context found" — never returns a result. */
|
|
152
183
|
function noContextAnswer(_question) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"synthesize.js","sourceRoot":"","sources":["../../src/retrieve/synthesize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAgCH,MAAM,mBAAmB,GAAG;IAC1B,mFAAmF;IACnF,
|
|
1
|
+
{"version":3,"file":"synthesize.js","sourceRoot":"","sources":["../../src/retrieve/synthesize.ts"],"names":[],"mappings":"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"}
|
|
@@ -37,26 +37,37 @@ describe("synthesize — no LLM (extractive fallback)", () => {
|
|
|
37
37
|
expect(r.answer).toContain("abc1234");
|
|
38
38
|
expect(r.evidenceCommitHashes).toEqual(["abc1234", "def5678"]);
|
|
39
39
|
});
|
|
40
|
-
it("extractive answer
|
|
40
|
+
it("extractive answer adds a caveat ONLY for low confidence", async () => {
|
|
41
41
|
const high = await synthesize("q", [result("abc1234", 0.05, "subject")], "high");
|
|
42
|
-
const medium = await synthesize("q", [result("abc1234", 0.05, "subject")], "medium");
|
|
43
42
|
const low = await synthesize("q", [result("abc1234", 0.05, "subject")], "low");
|
|
44
|
-
expect(high.answer).toContain("most likely");
|
|
45
|
-
expect(medium.answer.toLowerCase()).toContain("possibly");
|
|
46
43
|
expect(low.answer.toLowerCase()).toContain("verify");
|
|
44
|
+
expect(high.answer.toLowerCase()).not.toContain("verify");
|
|
47
45
|
});
|
|
48
|
-
it("extractive
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
it("extractive verdict aggregates by dominant author when ≥60%", async () => {
|
|
47
|
+
// 3 commits by alice, 1 by bob → alice dominates at 75%
|
|
48
|
+
const r = await synthesize("q", [
|
|
49
|
+
{ commit: { ...sampleCommit("a1", "s1", "", "alice") }, score: 0.05, matchedChunks: [] },
|
|
50
|
+
{ commit: { ...sampleCommit("a2", "s2", "", "alice") }, score: 0.04, matchedChunks: [] },
|
|
51
|
+
{ commit: { ...sampleCommit("a3", "s3", "", "alice") }, score: 0.03, matchedChunks: [] },
|
|
52
|
+
{ commit: { ...sampleCommit("b1", "s4", "", "bob") }, score: 0.02, matchedChunks: [] },
|
|
55
53
|
], "high");
|
|
56
|
-
|
|
57
|
-
expect(
|
|
58
|
-
expect(
|
|
59
|
-
expect(
|
|
54
|
+
// verdict line names the dominant author + percentage
|
|
55
|
+
expect(r.answer).toContain("alice");
|
|
56
|
+
expect(r.answer).toMatch(/75%|75 percent/);
|
|
57
|
+
expect(r.answer).toContain("likely the person to ask");
|
|
58
|
+
});
|
|
59
|
+
it("extractive verdict adapts to single-result case", async () => {
|
|
60
|
+
const r = await synthesize("q", [result("abc1234", 0.05, "subject")], "high");
|
|
61
|
+
expect(r.answer.toLowerCase()).toContain("single");
|
|
62
|
+
expect(r.answer).toContain("abc1234");
|
|
63
|
+
});
|
|
64
|
+
it("extractive verdict reports range when authors are mixed", async () => {
|
|
65
|
+
const r = await synthesize("q", [
|
|
66
|
+
{ commit: { ...sampleCommit("a1", "s1", "", "alice") }, score: 0.05, matchedChunks: [] },
|
|
67
|
+
{ commit: { ...sampleCommit("b1", "s2", "", "bob") }, score: 0.04, matchedChunks: [] },
|
|
68
|
+
], "high");
|
|
69
|
+
// Mixed authors at 2/2 → no dominant majority (≥60% but n<3 short-circuits)
|
|
70
|
+
expect(r.answer.toLowerCase()).toContain("strongest match");
|
|
60
71
|
});
|
|
61
72
|
});
|
|
62
73
|
describe("synthesize — with LLM (enricher branch)", () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"synthesize.test.js","sourceRoot":"","sources":["../../src/retrieve/synthesize.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,UAAU,EAA0B,MAAM,iBAAiB,CAAC;AAGrE,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,OAAe,EAAE,IAAI,GAAG,EAAE,EAAE,MAAM,GAAG,OAAO,EAAE,IAAI,GAAG,YAAY,EAAU,EAAE,CAAC,CAAC;IACjH,IAAI;IACJ,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;IAC3B,UAAU,EAAE,MAAM;IAClB,WAAW,EAAE,GAAG,MAAM,cAAc;IACpC,UAAU,EAAE,GAAG,IAAI,YAAY;IAC/B,aAAa,EAAE,GAAG,IAAI,YAAY;IAClC,OAAO;IACP,IAAI;IACJ,OAAO,EAAE,EAAE;IACX,KAAK,EAAE,EAAE;CACV,CAAC,CAAC;AAEH,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,KAAa,EAAE,OAAO,GAAG,aAAa,EAAE,IAAI,GAAG,EAAE,EAAgB,EAAE,CAAC,CAAC;IACjG,MAAM,EAAE,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC;IACzC,KAAK;IACL,aAAa,EAAE,EAAE;CAClB,CAAC,CAAC;AAEH,QAAQ,CAAC,2CAA2C,EAAE,GAAG,EAAE;IACzD,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QACjE,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,aAAa,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;QACtD,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACpC,MAAM,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC3C,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;QACrE,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,WAAW,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;QACpD,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,CAAC,GAAG,MAAM,UAAU,CACxB,eAAe,EACf;YACE,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,4BAA4B,CAAC;YACrD,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,mCAAmC,CAAC;SAC7D,EACD,MAAM,CACP,CAAC;QACF,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACpC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACtC,MAAM,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"synthesize.test.js","sourceRoot":"","sources":["../../src/retrieve/synthesize.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,UAAU,EAA0B,MAAM,iBAAiB,CAAC;AAGrE,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,OAAe,EAAE,IAAI,GAAG,EAAE,EAAE,MAAM,GAAG,OAAO,EAAE,IAAI,GAAG,YAAY,EAAU,EAAE,CAAC,CAAC;IACjH,IAAI;IACJ,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;IAC3B,UAAU,EAAE,MAAM;IAClB,WAAW,EAAE,GAAG,MAAM,cAAc;IACpC,UAAU,EAAE,GAAG,IAAI,YAAY;IAC/B,aAAa,EAAE,GAAG,IAAI,YAAY;IAClC,OAAO;IACP,IAAI;IACJ,OAAO,EAAE,EAAE;IACX,KAAK,EAAE,EAAE;CACV,CAAC,CAAC;AAEH,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,KAAa,EAAE,OAAO,GAAG,aAAa,EAAE,IAAI,GAAG,EAAE,EAAgB,EAAE,CAAC,CAAC;IACjG,MAAM,EAAE,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC;IACzC,KAAK;IACL,aAAa,EAAE,EAAE;CAClB,CAAC,CAAC;AAEH,QAAQ,CAAC,2CAA2C,EAAE,GAAG,EAAE;IACzD,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QACjE,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,aAAa,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;QACtD,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACpC,MAAM,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC3C,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;QACrE,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,WAAW,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;QACpD,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,CAAC,GAAG,MAAM,UAAU,CACxB,eAAe,EACf;YACE,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,4BAA4B,CAAC;YACrD,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,mCAAmC,CAAC;SAC7D,EACD,MAAM,CACP,CAAC;QACF,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACpC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACtC,MAAM,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QACvE,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACjF,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAC/E,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC1E,wDAAwD;QACxD,MAAM,CAAC,GAAG,MAAM,UAAU,CACxB,GAAG,EACH;YACE,EAAE,MAAM,EAAE,EAAE,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE;YACxF,EAAE,MAAM,EAAE,EAAE,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE;YACxF,EAAE,MAAM,EAAE,EAAE,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE;YACxF,EAAE,MAAM,EAAE,EAAE,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE;SACvF,EACD,MAAM,CACP,CAAC;QACF,sDAAsD;QACtD,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAC3C,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9E,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACnD,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QACvE,MAAM,CAAC,GAAG,MAAM,UAAU,CACxB,GAAG,EACH;YACE,EAAE,MAAM,EAAE,EAAE,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE;YACxF,EAAE,MAAM,EAAE,EAAE,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE;SACvF,EACD,MAAM,CACP,CAAC;QACF,4EAA4E;QAC5E,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,yCAAyC,EAAE,GAAG,EAAE;IACvD,MAAM,YAAY,GAAG,CAAC,UAAkB,EAAqB,EAAE,CAAC,CAAC;QAC/D,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;KAC3C,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,CAAC,GAAG,MAAM,UAAU,CACxB,mBAAmB,EACnB,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,EACjC,MAAM,EACN,YAAY,CAAC,uCAAuC,CAAC,CACtD,CAAC;QACF,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QACxD,MAAM,eAAe,GAAsB;YACzC,IAAI,EAAE,WAAW;YACjB,MAAM,EAAE,KAAK,IAAI,EAAE;gBACjB,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;YAC1C,CAAC;SACF,CAAC;QACF,MAAM,CAAC,GAAG,MAAM,UAAU,CACxB,GAAG,EACH,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,EACjC,MAAM,EACN,eAAe,CAChB,CAAC;QACF,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACpC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,CAAC,GAAG,MAAM,UAAU,CACxB,GAAG,EACH,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,EACjC,MAAM,EACN,YAAY,CAAC,KAAK,CAAC,CACpB,CAAC;QACF,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,CAAC,GAAG,MAAM,UAAU,CACxB,GAAG,EACH,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,EACjC,MAAM,EACN,YAAY,CAAC,oCAAoC,CAAC,CACnD,CAAC;QACF,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAC1C,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,KAAK,IAAI,EAAE;QAC7C,MAAM,CAAC,GAAG,MAAM,UAAU,CACxB,GAAG,EACH,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,EACjC,MAAM,EACN,YAAY,CAAC,uBAAuB,CAAC,CACtC,CAAC;QACF,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,CAAC,GAAG,MAAM,UAAU,CACxB,GAAG,EACH,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,EACjC,MAAM,EACN,YAAY,CAAC,kDAAkD,CAAC,CACjE,CAAC;QACF,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QACzE,MAAM,CAAC,GAAG,MAAM,UAAU,CACxB,GAAG,EACH,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,EAClD,MAAM,EACN,YAAY,CAAC,QAAQ,CAAC,CACvB,CAAC;QACF,MAAM,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,yCAAyC,EAAE,GAAG,EAAE;IACvD,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC1C,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;QAC5C,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;QAC3C,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC1E,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/store/schema.d.ts
CHANGED
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
* Phase 3 — incidents, correlations
|
|
7
7
|
* Phase 4 — graph_snapshots (for temporal viz)
|
|
8
8
|
*/
|
|
9
|
-
export declare const SCHEMA_VERSION =
|
|
10
|
-
export declare const SCHEMA_SQL = "\nCREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS commits (\n hash TEXT PRIMARY KEY,\n short_hash TEXT NOT NULL,\n author_name TEXT NOT NULL,\n author_email TEXT NOT NULL,\n author_date TEXT NOT NULL,\n committer_date TEXT NOT NULL,\n subject TEXT NOT NULL,\n body TEXT NOT NULL,\n parents TEXT NOT NULL,\n pr_number INTEGER,\n pr_title TEXT,\n pr_body TEXT,\n issue_refs TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_commits_author_date ON commits(author_date);\nCREATE INDEX IF NOT EXISTS idx_commits_pr ON commits(pr_number);\n\nCREATE TABLE IF NOT EXISTS file_changes (\n commit_hash TEXT NOT NULL,\n path TEXT NOT NULL,\n change_kind TEXT NOT NULL,\n insertions INTEGER NOT NULL DEFAULT 0,\n deletions INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (commit_hash, path),\n FOREIGN KEY (commit_hash) REFERENCES commits(hash) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS idx_file_changes_path ON file_changes(path);\n\nCREATE TABLE IF NOT EXISTS chunks (\n id TEXT PRIMARY KEY,\n commit_hash TEXT NOT NULL,\n kind TEXT NOT NULL,\n text TEXT NOT NULL,\n embedding BLOB,\n embedding_model TEXT,\n FOREIGN KEY (commit_hash) REFERENCES commits(hash) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS idx_chunks_commit ON chunks(commit_hash);\nCREATE INDEX IF NOT EXISTS idx_chunks_kind ON chunks(kind);\n\nCREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(\n id UNINDEXED,\n commit_hash UNINDEXED,\n text,\n tokenize = '
|
|
9
|
+
export declare const SCHEMA_VERSION = 3;
|
|
10
|
+
export declare const SCHEMA_SQL = "\nCREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS commits (\n hash TEXT PRIMARY KEY,\n short_hash TEXT NOT NULL,\n author_name TEXT NOT NULL,\n author_email TEXT NOT NULL,\n author_date TEXT NOT NULL,\n committer_date TEXT NOT NULL,\n subject TEXT NOT NULL,\n body TEXT NOT NULL,\n parents TEXT NOT NULL,\n pr_number INTEGER,\n pr_title TEXT,\n pr_body TEXT,\n issue_refs TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_commits_author_date ON commits(author_date);\nCREATE INDEX IF NOT EXISTS idx_commits_pr ON commits(pr_number);\n\nCREATE TABLE IF NOT EXISTS file_changes (\n commit_hash TEXT NOT NULL,\n path TEXT NOT NULL,\n change_kind TEXT NOT NULL,\n insertions INTEGER NOT NULL DEFAULT 0,\n deletions INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (commit_hash, path),\n FOREIGN KEY (commit_hash) REFERENCES commits(hash) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS idx_file_changes_path ON file_changes(path);\n\nCREATE TABLE IF NOT EXISTS chunks (\n id TEXT PRIMARY KEY,\n commit_hash TEXT NOT NULL,\n kind TEXT NOT NULL,\n text TEXT NOT NULL,\n embedding BLOB,\n embedding_model TEXT,\n FOREIGN KEY (commit_hash) REFERENCES commits(hash) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS idx_chunks_commit ON chunks(commit_hash);\nCREATE INDEX IF NOT EXISTS idx_chunks_kind ON chunks(kind);\n\n-- FTS5 with trigram tokenizer \u2014 works for Thai, CJK, Arabic, and any\n-- non-space-separated language. The tradeoff vs porter is that BM25 is\n-- slightly less precise for English, but the gain in cross-language\n-- coverage is enormous. Also: trigram makes substring search free.\nCREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(\n id UNINDEXED,\n commit_hash UNINDEXED,\n text,\n tokenize = 'trigram'\n);\n\n-- Phase 2: entity graph\nCREATE TABLE IF NOT EXISTS entities (\n id TEXT PRIMARY KEY,\n kind TEXT NOT NULL,\n name TEXT NOT NULL,\n file_path TEXT NOT NULL,\n start_line INTEGER NOT NULL,\n end_line INTEGER NOT NULL,\n signature TEXT,\n language TEXT NOT NULL,\n embedding BLOB\n);\nCREATE INDEX IF NOT EXISTS idx_entities_file ON entities(file_path);\nCREATE INDEX IF NOT EXISTS idx_entities_name ON entities(name);\n\n-- Phase 3: incidents and correlations (the moat)\nCREATE TABLE IF NOT EXISTS incidents (\n id TEXT PRIMARY KEY,\n source TEXT NOT NULL,\n external_id TEXT,\n title TEXT NOT NULL,\n occurred_at TEXT NOT NULL,\n resolved_at TEXT,\n severity TEXT NOT NULL,\n affected_files TEXT,\n stack_frames TEXT,\n url TEXT,\n metadata TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_incidents_occurred ON incidents(occurred_at);\nCREATE INDEX IF NOT EXISTS idx_incidents_source ON incidents(source);\n\nCREATE TABLE IF NOT EXISTS correlations (\n id TEXT PRIMARY KEY,\n from_kind TEXT NOT NULL,\n from_id TEXT NOT NULL,\n to_kind TEXT NOT NULL,\n to_id TEXT NOT NULL,\n weight REAL NOT NULL,\n reason TEXT NOT NULL,\n evidence TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_corr_from ON correlations(from_kind, from_id);\nCREATE INDEX IF NOT EXISTS idx_corr_to ON correlations(to_kind, to_id);\n\n-- Phase 4: temporal graph snapshots\nCREATE TABLE IF NOT EXISTS graph_snapshots (\n id TEXT PRIMARY KEY,\n taken_at TEXT NOT NULL,\n payload BLOB NOT NULL\n);\n\n-- WILD #1: AI-synthesized notes for commits with poor messages.\n-- The original commit is never modified. The synthesized note is searched\n-- alongside the original chunks but always marked as kind='synthesized'\n-- so users can verify against the underlying diff.\nCREATE TABLE IF NOT EXISTS synthesized_notes (\n commit_hash TEXT PRIMARY KEY,\n note TEXT NOT NULL,\n model TEXT NOT NULL,\n diff_chars INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n FOREIGN KEY (commit_hash) REFERENCES commits(hash) ON DELETE CASCADE\n);\n\n-- Phase 4 - Wisdom Mutant Engine\n-- The \"always-learning\" loop. Three append-only tables that record what the\n-- system saw, what it tried, and how well it worked. Per-repo, never leaves\n-- the machine. Honest contract: every recommendation in mneme adapt and\n-- every threshold in mneme ask is traceable back to a row here.\n\n-- Every query records its result-set fingerprint. was_helpful is set later,\n-- either by the user (--feedback up/down) or by an implicit signal (the\n-- query was not re-run with a refined version within 60s, or one of the\n-- returned commits was navigated to via mneme why).\nCREATE TABLE IF NOT EXISTS wisdom_feedback (\n id TEXT PRIMARY KEY,\n query TEXT NOT NULL,\n result_hashes TEXT NOT NULL, -- JSON array of commit hashes returned\n top_score REAL, -- top RRF score at query time\n was_helpful INTEGER, -- 1=yes, 0=no, NULL=unknown\n source TEXT NOT NULL, -- 'explicit' | 'implicit-reuse' | 'implicit-revisit'\n semantic_weight REAL, -- the search config that produced this result\n min_sem_cosine REAL,\n rrf_k INTEGER,\n created_at TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_feedback_created ON wisdom_feedback(created_at);\nCREATE INDEX IF NOT EXISTS idx_feedback_helpful ON wisdom_feedback(was_helpful);\n\n-- The current calibration of search/retrieval knobs. One row per knob.\n-- Updated by the auto-calibrator after evaluating against the feedback set.\nCREATE TABLE IF NOT EXISTS wisdom_calibration (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL, -- JSON\n metric_value REAL, -- the metric this calibration achieved\n metric_name TEXT, -- e.g. \"hit_rate\", \"recall_at_3\"\n calibrated_at TEXT NOT NULL,\n notes TEXT\n);\n\n-- Append-only history of self-eval runs. Lets the user (and any auditor)\n-- see whether quality is trending up or down over time.\nCREATE TABLE IF NOT EXISTS wisdom_eval_run (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n ran_at TEXT NOT NULL,\n variant TEXT NOT NULL, -- 'baseline' | 'reranked' | 'calibrated' | ...\n recall_at_3 REAL NOT NULL,\n mrr REAL NOT NULL,\n ndcg_at_10 REAL,\n hit_rate REAL NOT NULL,\n semantic_weight REAL,\n min_sem_cosine REAL,\n rrf_k INTEGER,\n num_queries INTEGER NOT NULL,\n notes TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_eval_ran_at ON wisdom_eval_run(ran_at);\n";
|
|
11
11
|
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/store/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/store/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,eAAO,MAAM,UAAU,upMAgLtB,CAAC"}
|
package/dist/store/schema.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Phase 3 — incidents, correlations
|
|
7
7
|
* Phase 4 — graph_snapshots (for temporal viz)
|
|
8
8
|
*/
|
|
9
|
-
export const SCHEMA_VERSION =
|
|
9
|
+
export const SCHEMA_VERSION = 3;
|
|
10
10
|
export const SCHEMA_SQL = `
|
|
11
11
|
CREATE TABLE IF NOT EXISTS meta (
|
|
12
12
|
key TEXT PRIMARY KEY,
|
|
@@ -54,11 +54,15 @@ CREATE TABLE IF NOT EXISTS chunks (
|
|
|
54
54
|
CREATE INDEX IF NOT EXISTS idx_chunks_commit ON chunks(commit_hash);
|
|
55
55
|
CREATE INDEX IF NOT EXISTS idx_chunks_kind ON chunks(kind);
|
|
56
56
|
|
|
57
|
+
-- FTS5 with trigram tokenizer — works for Thai, CJK, Arabic, and any
|
|
58
|
+
-- non-space-separated language. The tradeoff vs porter is that BM25 is
|
|
59
|
+
-- slightly less precise for English, but the gain in cross-language
|
|
60
|
+
-- coverage is enormous. Also: trigram makes substring search free.
|
|
57
61
|
CREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(
|
|
58
62
|
id UNINDEXED,
|
|
59
63
|
commit_hash UNINDEXED,
|
|
60
64
|
text,
|
|
61
|
-
tokenize = '
|
|
65
|
+
tokenize = 'trigram'
|
|
62
66
|
);
|
|
63
67
|
|
|
64
68
|
-- Phase 2: entity graph
|
package/dist/store/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/store/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEhC,MAAM,CAAC,MAAM,UAAU,GAAG
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/store/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEhC,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgLzB,CAAC"}
|
package/dist/store/sqlite.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export declare class MnemeStore {
|
|
|
4
4
|
readonly dbPath: string;
|
|
5
5
|
readonly db: Database.Database;
|
|
6
6
|
constructor(dbPath: string);
|
|
7
|
+
/** Drop + recreate chunks_fts with the new trigram tokenizer, then rebuild from chunks. */
|
|
8
|
+
private migrateFtsToTrigram;
|
|
7
9
|
close(): void;
|
|
8
10
|
setMeta(key: string, value: string): void;
|
|
9
11
|
getMeta(key: string): string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/store/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAGtC,OAAO,KAAK,EACV,MAAM,EACN,WAAW,EACX,MAAM,EACN,UAAU,EACV,QAAQ,EACR,WAAW,EACZ,MAAM,aAAa,CAAC;AAGrB,qBAAa,UAAU;aAGO,MAAM,EAAE,MAAM;IAF1C,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC;gBAEH,MAAM,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/store/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAGtC,OAAO,KAAK,EACV,MAAM,EACN,WAAW,EACX,MAAM,EACN,UAAU,EACV,QAAQ,EACR,WAAW,EACZ,MAAM,aAAa,CAAC;AAGrB,qBAAa,UAAU;aAGO,MAAM,EAAE,MAAM;IAF1C,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC;gBAEH,MAAM,EAAE,MAAM;IAmB1C,2FAA2F;IAC3F,OAAO,CAAC,mBAAmB;IAiB3B,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAMzC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAOxC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI;IA6BtC,iBAAiB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI;IAc9C,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI;IAqBlE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAQ3C,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAOtC,YAAY,IAAI,MAAM;IAKtB,WAAW,IAAI,MAAM;IAKrB,wBAAwB,IAAI,MAAM;IAOlC,mFAAmF;IAClF,kBAAkB,IAAI,SAAS,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,YAAY,CAAA;KAAE,CAAC;IAuBnH,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAuB5H,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI;IA0BjE,aAAa,IAAI,MAAM;IAKvB,0BAA0B,IAAI,MAAM;IAOpC,uBAAuB,IAAI,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAMhE,oBAAoB,IAAI,SAAS,CAAC,MAAM,CAAC;IAgC1C,eAAe,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI;IA2B5C,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAU/F,kBAAkB,CAChB,UAAU,EAAE,MAAM,GACjB;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS;IAQjE,qBAAqB,IAAI,MAAM;IAO/B,kBAAkB,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,IAAI;CAsBtD"}
|