sverklo 0.2.12 → 0.2.14
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/README.md +102 -13
- package/dist/bin/sverklo.js +69 -1
- package/dist/bin/sverklo.js.map +1 -1
- package/dist/src/audit-prompt.d.ts +2 -0
- package/dist/src/audit-prompt.js +91 -0
- package/dist/src/audit-prompt.js.map +1 -0
- package/dist/src/doctor.js +55 -0
- package/dist/src/doctor.js.map +1 -1
- package/dist/src/indexer/embedding-providers.d.ts +34 -0
- package/dist/src/indexer/embedding-providers.js +215 -0
- package/dist/src/indexer/embedding-providers.js.map +1 -0
- package/dist/src/indexer/embedding-providers.test.d.ts +1 -0
- package/dist/src/indexer/embedding-providers.test.js +83 -0
- package/dist/src/indexer/embedding-providers.test.js.map +1 -0
- package/dist/src/indexer/indexer-freshness.test.d.ts +1 -0
- package/dist/src/indexer/indexer-freshness.test.js +109 -0
- package/dist/src/indexer/indexer-freshness.test.js.map +1 -0
- package/dist/src/indexer/indexer-provider-integration.test.d.ts +1 -0
- package/dist/src/indexer/indexer-provider-integration.test.js +142 -0
- package/dist/src/indexer/indexer-provider-integration.test.js.map +1 -0
- package/dist/src/indexer/indexer.d.ts +44 -0
- package/dist/src/indexer/indexer.js +153 -10
- package/dist/src/indexer/indexer.js.map +1 -1
- package/dist/src/indexer/migration.test.d.ts +1 -0
- package/dist/src/indexer/migration.test.js +144 -0
- package/dist/src/indexer/migration.test.js.map +1 -0
- package/dist/src/indexer/symbol-extractor.js +17 -5
- package/dist/src/indexer/symbol-extractor.js.map +1 -1
- package/dist/src/indexer/symbol-extractor.test.d.ts +1 -0
- package/dist/src/indexer/symbol-extractor.test.js +88 -0
- package/dist/src/indexer/symbol-extractor.test.js.map +1 -0
- package/dist/src/indexer/watcher.js +4 -0
- package/dist/src/indexer/watcher.js.map +1 -1
- package/dist/src/memory/journal.d.ts +62 -0
- package/dist/src/memory/journal.js +136 -0
- package/dist/src/memory/journal.js.map +1 -0
- package/dist/src/memory/journal.test.d.ts +1 -0
- package/dist/src/memory/journal.test.js +98 -0
- package/dist/src/memory/journal.test.js.map +1 -0
- package/dist/src/modes.d.ts +16 -0
- package/dist/src/modes.js +104 -0
- package/dist/src/modes.js.map +1 -0
- package/dist/src/modes.test.d.ts +1 -0
- package/dist/src/modes.test.js +48 -0
- package/dist/src/modes.test.js.map +1 -0
- package/dist/src/search/hybrid-search.d.ts +22 -0
- package/dist/src/search/hybrid-search.js +169 -7
- package/dist/src/search/hybrid-search.js.map +1 -1
- package/dist/src/search/hybrid-search.test.d.ts +1 -0
- package/dist/src/search/hybrid-search.test.js +112 -0
- package/dist/src/search/hybrid-search.test.js.map +1 -0
- package/dist/src/search/token-budget.d.ts +1 -0
- package/dist/src/search/token-budget.js +34 -6
- package/dist/src/search/token-budget.js.map +1 -1
- package/dist/src/server/mcp-server.js +32 -25
- package/dist/src/server/mcp-server.js.map +1 -1
- package/dist/src/server/tool-overrides.d.ts +16 -0
- package/dist/src/server/tool-overrides.js +102 -0
- package/dist/src/server/tool-overrides.js.map +1 -0
- package/dist/src/server/tool-overrides.test.d.ts +1 -0
- package/dist/src/server/tool-overrides.test.js +124 -0
- package/dist/src/server/tool-overrides.test.js.map +1 -0
- package/dist/src/server/tools/context-budget.test.d.ts +1 -0
- package/dist/src/server/tools/context-budget.test.js +150 -0
- package/dist/src/server/tools/context-budget.test.js.map +1 -0
- package/dist/src/server/tools/context.d.ts +11 -1
- package/dist/src/server/tools/context.js +202 -8
- package/dist/src/server/tools/context.js.map +1 -1
- package/dist/src/server/tools/diff-heuristics.d.ts +28 -0
- package/dist/src/server/tools/diff-heuristics.js +213 -0
- package/dist/src/server/tools/diff-heuristics.js.map +1 -0
- package/dist/src/server/tools/diff-heuristics.test.d.ts +1 -0
- package/dist/src/server/tools/diff-heuristics.test.js +151 -0
- package/dist/src/server/tools/diff-heuristics.test.js.map +1 -0
- package/dist/src/server/tools/find-references.d.ts +4 -0
- package/dist/src/server/tools/find-references.js +46 -6
- package/dist/src/server/tools/find-references.js.map +1 -1
- package/dist/src/server/tools/find-references.test.d.ts +1 -0
- package/dist/src/server/tools/find-references.test.js +98 -0
- package/dist/src/server/tools/find-references.test.js.map +1 -0
- package/dist/src/server/tools/forget.js +3 -0
- package/dist/src/server/tools/forget.js.map +1 -1
- package/dist/src/server/tools/index-status.js +16 -0
- package/dist/src/server/tools/index-status.js.map +1 -1
- package/dist/src/server/tools/lookup.js +21 -13
- package/dist/src/server/tools/lookup.js.map +1 -1
- package/dist/src/server/tools/lookup.test.d.ts +1 -0
- package/dist/src/server/tools/lookup.test.js +110 -0
- package/dist/src/server/tools/lookup.test.js.map +1 -0
- package/dist/src/server/tools/recall.d.ts +5 -1
- package/dist/src/server/tools/recall.js +68 -6
- package/dist/src/server/tools/recall.js.map +1 -1
- package/dist/src/server/tools/recall.test.d.ts +1 -0
- package/dist/src/server/tools/recall.test.js +116 -0
- package/dist/src/server/tools/recall.test.js.map +1 -0
- package/dist/src/server/tools/remember.js +16 -2
- package/dist/src/server/tools/remember.js.map +1 -1
- package/dist/src/server/tools/review-diff.js +31 -0
- package/dist/src/server/tools/review-diff.js.map +1 -1
- package/dist/src/server/tools/search.js +29 -4
- package/dist/src/server/tools/search.js.map +1 -1
- package/dist/src/server/tools/search.test.d.ts +1 -0
- package/dist/src/server/tools/search.test.js +118 -0
- package/dist/src/server/tools/search.test.js.map +1 -0
- package/dist/src/server/tools/tier.js +25 -1
- package/dist/src/server/tools/tier.js.map +1 -1
- package/dist/src/storage/chunk-store.d.ts +12 -0
- package/dist/src/storage/chunk-store.js +16 -0
- package/dist/src/storage/chunk-store.js.map +1 -1
- package/dist/src/storage/chunk-store.test.d.ts +1 -0
- package/dist/src/storage/chunk-store.test.js +69 -0
- package/dist/src/storage/chunk-store.test.js.map +1 -0
- package/dist/src/storage/database.d.ts +13 -0
- package/dist/src/storage/database.js +47 -0
- package/dist/src/storage/database.js.map +1 -1
- package/package.json +5 -2
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { handleSearch } from "./search.js";
|
|
3
|
+
// Integration tests for the handleSearch footer behavior (issue #4).
|
|
4
|
+
// These lock in what users see when confidence is low, medium, or
|
|
5
|
+
// high — the visible contract of the tool, which a pure unit test
|
|
6
|
+
// of computeConfidence wouldn't catch.
|
|
7
|
+
// We mock hybridSearchWithConfidence so these tests don't need a real
|
|
8
|
+
// index. The mock drives each confidence branch independently.
|
|
9
|
+
vi.mock("../../search/hybrid-search.js", async () => {
|
|
10
|
+
return {
|
|
11
|
+
hybridSearchWithConfidence: vi.fn(async () => ({
|
|
12
|
+
results: [],
|
|
13
|
+
confidence: "high",
|
|
14
|
+
confidenceReason: null,
|
|
15
|
+
fallbackHint: null,
|
|
16
|
+
})),
|
|
17
|
+
formatResults: (results) => results.length === 0 ? "No results found." : "formatted",
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
import { hybridSearchWithConfidence } from "../../search/hybrid-search.js";
|
|
21
|
+
const mockedHybrid = hybridSearchWithConfidence;
|
|
22
|
+
describe("handleSearch — confidence footer", () => {
|
|
23
|
+
it("high confidence: no footer attached", async () => {
|
|
24
|
+
mockedHybrid.mockResolvedValueOnce({
|
|
25
|
+
results: [],
|
|
26
|
+
confidence: "high",
|
|
27
|
+
confidenceReason: null,
|
|
28
|
+
fallbackHint: null,
|
|
29
|
+
});
|
|
30
|
+
const out = await handleSearch({}, { query: "retry logic" });
|
|
31
|
+
expect(out).not.toContain("Low confidence");
|
|
32
|
+
expect(out).not.toContain("Medium confidence");
|
|
33
|
+
});
|
|
34
|
+
it("low confidence: surfaces warning + fallback hint", async () => {
|
|
35
|
+
mockedHybrid.mockResolvedValueOnce({
|
|
36
|
+
results: [],
|
|
37
|
+
confidence: "low",
|
|
38
|
+
confidenceReason: "no results matched",
|
|
39
|
+
fallbackHint: "Try Grep for exact string matching.",
|
|
40
|
+
});
|
|
41
|
+
const out = await handleSearch({}, { query: "x" });
|
|
42
|
+
expect(out).toContain("⚠️");
|
|
43
|
+
expect(out).toContain("Low confidence");
|
|
44
|
+
expect(out).toContain("no results matched");
|
|
45
|
+
expect(out).toContain("Try Grep");
|
|
46
|
+
});
|
|
47
|
+
it("low confidence on framework-wiring: hint names annotations", async () => {
|
|
48
|
+
mockedHybrid.mockResolvedValueOnce({
|
|
49
|
+
results: [],
|
|
50
|
+
confidence: "low",
|
|
51
|
+
confidenceReason: "framework wiring",
|
|
52
|
+
fallbackHint: "This query looks like a framework-wiring question. Try Grep for `@Component`.",
|
|
53
|
+
});
|
|
54
|
+
const out = await handleSearch({}, {
|
|
55
|
+
query: "how is the interceptor registered",
|
|
56
|
+
});
|
|
57
|
+
expect(out).toContain("framework-wiring");
|
|
58
|
+
expect(out).toContain("@Component");
|
|
59
|
+
});
|
|
60
|
+
it("medium confidence with hint: attaches soft hint", async () => {
|
|
61
|
+
mockedHybrid.mockResolvedValueOnce({
|
|
62
|
+
results: [],
|
|
63
|
+
confidence: "medium",
|
|
64
|
+
confidenceReason: "ambiguous top two",
|
|
65
|
+
fallbackHint: "Consider reading both or refining the query.",
|
|
66
|
+
});
|
|
67
|
+
const out = await handleSearch({}, { query: "auth" });
|
|
68
|
+
expect(out).toContain("Medium confidence");
|
|
69
|
+
expect(out).toContain("ambiguous top two");
|
|
70
|
+
});
|
|
71
|
+
it("medium confidence with no hint: no footer", async () => {
|
|
72
|
+
mockedHybrid.mockResolvedValueOnce({
|
|
73
|
+
results: [],
|
|
74
|
+
confidence: "medium",
|
|
75
|
+
confidenceReason: "mild",
|
|
76
|
+
fallbackHint: null,
|
|
77
|
+
});
|
|
78
|
+
const out = await handleSearch({}, { query: "x" });
|
|
79
|
+
expect(out).not.toContain("Medium confidence");
|
|
80
|
+
expect(out).not.toContain("Low confidence");
|
|
81
|
+
});
|
|
82
|
+
it("passes args through to hybridSearchWithConfidence", async () => {
|
|
83
|
+
mockedHybrid.mockClear();
|
|
84
|
+
mockedHybrid.mockResolvedValueOnce({
|
|
85
|
+
results: [],
|
|
86
|
+
confidence: "high",
|
|
87
|
+
confidenceReason: null,
|
|
88
|
+
fallbackHint: null,
|
|
89
|
+
});
|
|
90
|
+
await handleSearch({}, {
|
|
91
|
+
query: "test",
|
|
92
|
+
token_budget: 3000,
|
|
93
|
+
scope: "src/api/",
|
|
94
|
+
language: "typescript",
|
|
95
|
+
type: "function",
|
|
96
|
+
});
|
|
97
|
+
expect(mockedHybrid).toHaveBeenCalledTimes(1);
|
|
98
|
+
const call = mockedHybrid.mock.calls[0][1];
|
|
99
|
+
expect(call.query).toBe("test");
|
|
100
|
+
expect(call.tokenBudget).toBe(3000);
|
|
101
|
+
expect(call.scope).toBe("src/api/");
|
|
102
|
+
expect(call.language).toBe("typescript");
|
|
103
|
+
expect(call.type).toBe("function");
|
|
104
|
+
});
|
|
105
|
+
it("defaults token budget to 2000 when not provided", async () => {
|
|
106
|
+
mockedHybrid.mockClear();
|
|
107
|
+
mockedHybrid.mockResolvedValueOnce({
|
|
108
|
+
results: [],
|
|
109
|
+
confidence: "high",
|
|
110
|
+
confidenceReason: null,
|
|
111
|
+
fallbackHint: null,
|
|
112
|
+
});
|
|
113
|
+
await handleSearch({}, { query: "x" });
|
|
114
|
+
const call = mockedHybrid.mock.calls[0][1];
|
|
115
|
+
expect(call.tokenBudget).toBe(2000);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
//# sourceMappingURL=search.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.test.js","sourceRoot":"","sources":["../../../../src/server/tools/search.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,qEAAqE;AACrE,kEAAkE;AAClE,kEAAkE;AAClE,uCAAuC;AAEvC,sEAAsE;AACtE,+DAA+D;AAE/D,EAAE,CAAC,IAAI,CAAC,+BAA+B,EAAE,KAAK,IAAI,EAAE;IAClD,OAAO;QACL,0BAA0B,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;YAC7C,OAAO,EAAE,EAAE;YACX,UAAU,EAAE,MAAe;YAC3B,gBAAgB,EAAE,IAAI;YACtB,YAAY,EAAE,IAAI;SACnB,CAAC,CAAC;QACH,aAAa,EAAE,CAAC,OAAkB,EAAE,EAAE,CACpC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,WAAW;KAC3D,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAC3E,MAAM,YAAY,GAAG,0BAAiE,CAAC;AAEvF,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACnD,YAAY,CAAC,qBAAqB,CAAC;YACjC,OAAO,EAAE,EAAE;YACX,UAAU,EAAE,MAAM;YAClB,gBAAgB,EAAE,IAAI;YACtB,YAAY,EAAE,IAAI;SACnB,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,EAAW,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;QACtE,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC5C,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,YAAY,CAAC,qBAAqB,CAAC;YACjC,OAAO,EAAE,EAAE;YACX,UAAU,EAAE,KAAK;YACjB,gBAAgB,EAAE,oBAAoB;YACtC,YAAY,EAAE,qCAAqC;SACpD,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,EAAW,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5D,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACxC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QAC5C,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC1E,YAAY,CAAC,qBAAqB,CAAC;YACjC,OAAO,EAAE,EAAE;YACX,UAAU,EAAE,KAAK;YACjB,gBAAgB,EAAE,kBAAkB;YACpC,YAAY,EACV,+EAA+E;SAClF,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,EAAW,EAAE;YAC1C,KAAK,EAAE,mCAAmC;SAC3C,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,YAAY,CAAC,qBAAqB,CAAC;YACjC,OAAO,EAAE,EAAE;YACX,UAAU,EAAE,QAAQ;YACpB,gBAAgB,EAAE,mBAAmB;YACrC,YAAY,EAAE,8CAA8C;SAC7D,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,EAAW,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QAC/D,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC3C,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,YAAY,CAAC,qBAAqB,CAAC;YACjC,OAAO,EAAE,EAAE;YACX,UAAU,EAAE,QAAQ;YACpB,gBAAgB,EAAE,MAAM;YACxB,YAAY,EAAE,IAAI;SACnB,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,EAAW,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5D,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC/C,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QACjE,YAAY,CAAC,SAAS,EAAE,CAAC;QACzB,YAAY,CAAC,qBAAqB,CAAC;YACjC,OAAO,EAAE,EAAE;YACX,UAAU,EAAE,MAAM;YAClB,gBAAgB,EAAE,IAAI;YACtB,YAAY,EAAE,IAAI;SACnB,CAAC,CAAC;QAEH,MAAM,YAAY,CAAC,EAAW,EAAE;YAC9B,KAAK,EAAE,MAAM;YACb,YAAY,EAAE,IAAI;YAClB,KAAK,EAAE,UAAU;YACjB,QAAQ,EAAE,YAAY;YACtB,IAAI,EAAE,UAAU;SACjB,CAAC,CAAC;QAEH,MAAM,CAAC,YAAY,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,YAAY,CAAC,SAAS,EAAE,CAAC;QACzB,YAAY,CAAC,qBAAqB,CAAC;YACjC,OAAO,EAAE,EAAE;YACX,UAAU,EAAE,MAAM;YAClB,gBAAgB,EAAE,IAAI;YACtB,YAAY,EAAE,IAAI;SACnB,CAAC,CAAC;QAEH,MAAM,YAAY,CAAC,EAAW,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAEhD,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// Issue #11: soft cap on the core tier. Core memories are injected
|
|
2
|
+
// into every session prompt, so bloating this tier costs context-
|
|
3
|
+
// window tokens on every agent interaction. Writes above the cap still
|
|
4
|
+
// succeed — the cap is advisory — but the tool response warns the
|
|
5
|
+
// caller so humans can prune.
|
|
6
|
+
const CORE_TIER_SOFT_LIMIT = 25;
|
|
1
7
|
export const promoteTool = {
|
|
2
8
|
name: "sverklo_promote",
|
|
3
9
|
description: "Promote a memory to the core tier. Core memories are auto-injected into every session " +
|
|
@@ -45,6 +51,24 @@ function setTier(indexer, args, tier) {
|
|
|
45
51
|
return `Memory #${id} is already in ${tier} tier.`;
|
|
46
52
|
}
|
|
47
53
|
indexer.memoryStore.setTier(id, tier);
|
|
48
|
-
|
|
54
|
+
// Mirror the tier change to the JSONL journal. Issue #7.
|
|
55
|
+
if (tier === "core") {
|
|
56
|
+
indexer.memoryJournal.promote(id, tier);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
indexer.memoryJournal.demote(id, tier);
|
|
60
|
+
}
|
|
61
|
+
const base = `Moved memory #${id} to ${tier} tier: "${mem.content.slice(0, 80)}${mem.content.length > 80 ? "..." : ""}"`;
|
|
62
|
+
// Warn if promoting pushes the core tier over the soft limit.
|
|
63
|
+
if (tier === "core") {
|
|
64
|
+
const coreCount = indexer.memoryStore.getCore(1000).length;
|
|
65
|
+
if (coreCount > CORE_TIER_SOFT_LIMIT) {
|
|
66
|
+
return (base +
|
|
67
|
+
`\n\n⚠️ Core tier now has ${coreCount} memories (soft limit: ${CORE_TIER_SOFT_LIMIT}). ` +
|
|
68
|
+
"These are injected into every session prompt — too many crowds the context window. " +
|
|
69
|
+
"Consider demoting the least-critical ones with `sverklo_demote id:<n>`.");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return base;
|
|
49
73
|
}
|
|
50
74
|
//# sourceMappingURL=tier.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tier.js","sourceRoot":"","sources":["../../../../src/server/tools/tier.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,IAAI,EAAE,iBAAiB;IACvB,WAAW,EACT,wFAAwF;QACxF,2FAA2F;QAC3F,wEAAwE;IAC1E,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,EAAE,EAAE;gBACF,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,qDAAqD;aACnE;SACF;QACD,QAAQ,EAAE,CAAC,IAAI,CAAC;KACjB;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,gBAAgB;IACtB,WAAW,EACT,2FAA2F;QAC3F,wFAAwF;QACxF,yBAAyB;IAC3B,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,EAAE,EAAE;gBACF,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,qBAAqB;aACnC;SACF;QACD,QAAQ,EAAE,CAAC,IAAI,CAAC;KACjB;CACF,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,OAAgB,EAAE,IAA6B;IAC3E,OAAO,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAgB,EAAE,IAA6B;IAC1E,OAAO,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,OAAO,CAAC,OAAgB,EAAE,IAA6B,EAAE,IAAgB;IAChF,MAAM,EAAE,GAAG,IAAI,CAAC,EAAY,CAAC;IAC7B,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5C,IAAI,CAAC,GAAG;QAAE,OAAO,WAAW,EAAE,aAAa,CAAC;IAE5C,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACtB,OAAO,WAAW,EAAE,kBAAkB,IAAI,QAAQ,CAAC;IACrD,CAAC;IAED,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACtC,OAAO,iBAAiB,EAAE,OAAO,IAAI,WAAW,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"tier.js","sourceRoot":"","sources":["../../../../src/server/tools/tier.ts"],"names":[],"mappings":"AAGA,mEAAmE;AACnE,kEAAkE;AAClE,uEAAuE;AACvE,kEAAkE;AAClE,8BAA8B;AAC9B,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEhC,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,IAAI,EAAE,iBAAiB;IACvB,WAAW,EACT,wFAAwF;QACxF,2FAA2F;QAC3F,wEAAwE;IAC1E,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,EAAE,EAAE;gBACF,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,qDAAqD;aACnE;SACF;QACD,QAAQ,EAAE,CAAC,IAAI,CAAC;KACjB;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,gBAAgB;IACtB,WAAW,EACT,2FAA2F;QAC3F,wFAAwF;QACxF,yBAAyB;IAC3B,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,EAAE,EAAE;gBACF,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,qBAAqB;aACnC;SACF;QACD,QAAQ,EAAE,CAAC,IAAI,CAAC;KACjB;CACF,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,OAAgB,EAAE,IAA6B;IAC3E,OAAO,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAgB,EAAE,IAA6B;IAC1E,OAAO,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,OAAO,CAAC,OAAgB,EAAE,IAA6B,EAAE,IAAgB;IAChF,MAAM,EAAE,GAAG,IAAI,CAAC,EAAY,CAAC;IAC7B,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5C,IAAI,CAAC,GAAG;QAAE,OAAO,WAAW,EAAE,aAAa,CAAC;IAE5C,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACtB,OAAO,WAAW,EAAE,kBAAkB,IAAI,QAAQ,CAAC;IACrD,CAAC;IAED,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACtC,yDAAyD;IACzD,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;IAED,MAAM,IAAI,GAAG,iBAAiB,EAAE,OAAO,IAAI,WAAW,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;IAEzH,8DAA8D;IAC9D,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;QAC3D,IAAI,SAAS,GAAG,oBAAoB,EAAE,CAAC;YACrC,OAAO,CACL,IAAI;gBACJ,4BAA4B,SAAS,0BAA0B,oBAAoB,KAAK;gBACxF,qFAAqF;gBACrF,yEAAyE,CAC1E,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -17,6 +17,18 @@ export declare class ChunkStore {
|
|
|
17
17
|
})[];
|
|
18
18
|
getById(id: number): CodeChunk | undefined;
|
|
19
19
|
getByName(namePattern: string, limit?: number): CodeChunk[];
|
|
20
|
+
/**
|
|
21
|
+
* Same shape as getByName, but JOINs in the containing file's path and
|
|
22
|
+
* pagerank in a single indexed query so callers don't need to follow
|
|
23
|
+
* up with a full fileStore.getAll() scan. Issue #6: the first-call
|
|
24
|
+
* latency on sverklo_lookup was dominated by that scan warming up
|
|
25
|
+
* prepared statements over the files table.
|
|
26
|
+
*/
|
|
27
|
+
getByNameWithFile(namePattern: string, limit?: number): (CodeChunk & {
|
|
28
|
+
filePath: string;
|
|
29
|
+
pagerank: number;
|
|
30
|
+
fileLanguage: string;
|
|
31
|
+
})[];
|
|
20
32
|
count(): number;
|
|
21
33
|
getAllWithFile(): (CodeChunk & {
|
|
22
34
|
filePath: string;
|
|
@@ -60,6 +60,22 @@ export class ChunkStore {
|
|
|
60
60
|
getByName(namePattern, limit = 20) {
|
|
61
61
|
return this.getByNameStmt.all(`%${namePattern}%`, limit);
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Same shape as getByName, but JOINs in the containing file's path and
|
|
65
|
+
* pagerank in a single indexed query so callers don't need to follow
|
|
66
|
+
* up with a full fileStore.getAll() scan. Issue #6: the first-call
|
|
67
|
+
* latency on sverklo_lookup was dominated by that scan warming up
|
|
68
|
+
* prepared statements over the files table.
|
|
69
|
+
*/
|
|
70
|
+
getByNameWithFile(namePattern, limit = 20) {
|
|
71
|
+
return this.db
|
|
72
|
+
.prepare(`SELECT c.*, f.path as filePath, f.pagerank, f.language as fileLanguage
|
|
73
|
+
FROM chunks c JOIN files f ON c.file_id = f.id
|
|
74
|
+
WHERE c.name LIKE ?
|
|
75
|
+
ORDER BY f.pagerank DESC
|
|
76
|
+
LIMIT ?`)
|
|
77
|
+
.all(`%${namePattern}%`, limit);
|
|
78
|
+
}
|
|
63
79
|
count() {
|
|
64
80
|
return this.db.prepare("SELECT COUNT(*) as c FROM chunks").get().c;
|
|
65
81
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-store.js","sourceRoot":"","sources":["../../../src/storage/chunk-store.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,UAAU;IAQD;IAPZ,UAAU,CAAqB;IAC/B,aAAa,CAAqB;IAClC,gBAAgB,CAAqB;IACrC,aAAa,CAAqB;IAClC,WAAW,CAAqB;IAChC,aAAa,CAAqB;IAE1C,YAAoB,EAAqB;QAArB,OAAE,GAAF,EAAE,CAAmB;QACvC,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC,OAAO,CAAC;;;KAG5B,CAAC,CAAC;QACH,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC,OAAO,CAC7B,4DAA4D,CAC7D,CAAC;QACF,IAAI,CAAC,gBAAgB,GAAG,EAAE,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC;QAC3E,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;;;KAO/B,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC,OAAO,CAAC,mCAAmC,CAAC,CAAC;QACnE,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC,OAAO,CAAC;;KAE/B,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CACJ,MAAc,EACd,IAAe,EACf,IAAmB,EACnB,SAAwB,EACxB,SAAiB,EACjB,OAAe,EACf,OAAe,EACf,WAA0B,EAC1B,UAAkB;QAElB,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAChC,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,SAAS,EACT,OAAO,EACP,OAAO,EACP,WAAW,EACX,UAAU,CACX,CAAC;QACF,OAAO,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACxC,CAAC;IAED,SAAS,CAAC,MAAc;QACtB,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAgB,CAAC;IACvD,CAAC;IAED,YAAY,CAAC,MAAc;QACzB,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,SAAS,CAAC,KAAa,EAAE,QAAgB,EAAE;QACzC,IAAI,CAAC;YACH,oDAAoD;YACpD,MAAM,SAAS,GAAG,KAAK;iBACpB,OAAO,CAAC,wBAAwB,EAAE,GAAG,CAAC;iBACtC,KAAK,CAAC,KAAK,CAAC;iBACZ,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;iBAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;iBACpB,IAAI,CAAC,MAAM,CAAC,CAAC;YAChB,IAAI,CAAC,SAAS;gBAAE,OAAO,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAE1C,CAAC;QACP,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,OAAO,CAAC,EAAU;QAChB,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAA0B,CAAC;IAC3D,CAAC;IAED,SAAS,CAAC,WAAmB,EAAE,QAAgB,EAAE;QAC/C,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,WAAW,GAAG,EAAE,KAAK,CAAgB,CAAC;IAC1E,CAAC;IAED,KAAK;QACH,OACE,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,kCAAkC,CAAC,CAAC,GAAG,EACxD,CAAC,CAAC,CAAC;IACN,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,CAAC,EAAE;aACX,OAAO,CACN;;gDAEwC,CACzC;aACA,GAAG,EAA4D,CAAC;IACrE,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"chunk-store.js","sourceRoot":"","sources":["../../../src/storage/chunk-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { ChunkStore } from "./chunk-store.js";
|
|
3
|
+
import { FileStore } from "./file-store.js";
|
|
4
|
+
import { createDatabase } from "./database.js";
|
|
5
|
+
// Tests for the getByNameWithFile JOIN that backs the sverklo_lookup
|
|
6
|
+
// perf fix (issue #6). Previously lookup called chunkStore.getByName
|
|
7
|
+
// + a separate fileStore.getAll() scan — the scan dominated first-call
|
|
8
|
+
// latency. The JOIN method should return the same chunks but sorted by
|
|
9
|
+
// pagerank DESC with the file path and language attached.
|
|
10
|
+
describe("ChunkStore.getByNameWithFile", () => {
|
|
11
|
+
let db;
|
|
12
|
+
let chunkStore;
|
|
13
|
+
let fileStore;
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
db = createDatabase(":memory:");
|
|
16
|
+
fileStore = new FileStore(db);
|
|
17
|
+
chunkStore = new ChunkStore(db);
|
|
18
|
+
// Seed two files with different pagerank and matching chunk names.
|
|
19
|
+
const fileA = fileStore.upsert("src/a.ts", "typescript", "hash-a", 1000, 500);
|
|
20
|
+
const fileB = fileStore.upsert("src/b.ts", "typescript", "hash-b", 2000, 800);
|
|
21
|
+
// Set pagerank so we can verify ordering
|
|
22
|
+
db.prepare("UPDATE files SET pagerank = ? WHERE id = ?").run(0.9, fileA);
|
|
23
|
+
db.prepare("UPDATE files SET pagerank = ? WHERE id = ?").run(0.1, fileB);
|
|
24
|
+
// Insert chunks: both have a symbol called "handleRequest"
|
|
25
|
+
chunkStore.insert(fileA, "function", "handleRequest", "function handleRequest() {}", 10, 15, "body a", "desc a", 20);
|
|
26
|
+
chunkStore.insert(fileB, "function", "handleRequest", "function handleRequest() {}", 22, 27, "body b", "desc b", 20);
|
|
27
|
+
chunkStore.insert(fileA, "class", "UnrelatedClass", "class UnrelatedClass {}", 40, 45, "body c", "desc c", 15);
|
|
28
|
+
});
|
|
29
|
+
it("returns matching chunks with file path and language joined", () => {
|
|
30
|
+
const result = chunkStore.getByNameWithFile("handleRequest", 10);
|
|
31
|
+
expect(result).toHaveLength(2);
|
|
32
|
+
// Both results should have filePath populated
|
|
33
|
+
expect(result.every((r) => r.filePath.length > 0)).toBe(true);
|
|
34
|
+
// Language should come from the file record
|
|
35
|
+
expect(result.every((r) => r.fileLanguage === "typescript")).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
it("orders results by file pagerank DESC", () => {
|
|
38
|
+
const result = chunkStore.getByNameWithFile("handleRequest", 10);
|
|
39
|
+
// Fields should be sorted: file A (0.9) before file B (0.1)
|
|
40
|
+
expect(result[0].filePath).toBe("src/a.ts");
|
|
41
|
+
expect(result[0].pagerank).toBe(0.9);
|
|
42
|
+
expect(result[1].filePath).toBe("src/b.ts");
|
|
43
|
+
expect(result[1].pagerank).toBe(0.1);
|
|
44
|
+
});
|
|
45
|
+
it("respects the limit parameter", () => {
|
|
46
|
+
const result = chunkStore.getByNameWithFile("handleRequest", 1);
|
|
47
|
+
expect(result).toHaveLength(1);
|
|
48
|
+
});
|
|
49
|
+
it("returns empty array when no chunks match", () => {
|
|
50
|
+
const result = chunkStore.getByNameWithFile("nonexistentSymbol", 10);
|
|
51
|
+
expect(result).toEqual([]);
|
|
52
|
+
});
|
|
53
|
+
it("supports prefix match via the wildcard", () => {
|
|
54
|
+
// getByNameWithFile wraps the input in LIKE '%<name>%' so substring
|
|
55
|
+
// matches work — same contract as the legacy getByName.
|
|
56
|
+
const result = chunkStore.getByNameWithFile("handle", 10);
|
|
57
|
+
expect(result.length).toBeGreaterThan(0);
|
|
58
|
+
expect(result.every((r) => r.name?.includes("handle"))).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it("returns the same chunks as getByName (but with extra fields)", () => {
|
|
61
|
+
const joined = chunkStore.getByNameWithFile("handleRequest", 10);
|
|
62
|
+
const legacy = chunkStore.getByName("handleRequest", 10);
|
|
63
|
+
expect(joined.length).toBe(legacy.length);
|
|
64
|
+
const joinedIds = new Set(joined.map((c) => c.id));
|
|
65
|
+
const legacyIds = new Set(legacy.map((c) => c.id));
|
|
66
|
+
expect(joinedIds).toEqual(legacyIds);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=chunk-store.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-store.test.js","sourceRoot":"","sources":["../../../src/storage/chunk-store.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAE1D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,qEAAqE;AACrE,qEAAqE;AACrE,uEAAuE;AACvE,uEAAuE;AACvE,0DAA0D;AAE1D,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,IAAI,EAAqB,CAAC;IAC1B,IAAI,UAAsB,CAAC;IAC3B,IAAI,SAAoB,CAAC;IAEzB,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;QAChC,SAAS,GAAG,IAAI,SAAS,CAAC,EAAE,CAAC,CAAC;QAC9B,UAAU,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;QAEhC,mEAAmE;QACnE,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;QAC9E,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;QAE9E,yCAAyC;QACzC,EAAE,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACzE,EAAE,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAEzE,2DAA2D;QAC3D,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,6BAA6B,EAAE,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACrH,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,6BAA6B,EAAE,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACrH,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;IACjH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,MAAM,GAAG,UAAU,CAAC,iBAAiB,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;QAEjE,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC/B,8CAA8C;QAC9C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,4CAA4C;QAC5C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,MAAM,GAAG,UAAU,CAAC,iBAAiB,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;QAEjE,4DAA4D;QAC5D,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,MAAM,GAAG,UAAU,CAAC,iBAAiB,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;QAChE,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,MAAM,GAAG,UAAU,CAAC,iBAAiB,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;QACrE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,oEAAoE;QACpE,wDAAwD;QACxD,MAAM,MAAM,GAAG,UAAU,CAAC,iBAAiB,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,MAAM,GAAG,UAAU,CAAC,iBAAiB,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;QACjE,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;QAEzD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACnD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACnD,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,2 +1,15 @@
|
|
|
1
1
|
import Database from "better-sqlite3";
|
|
2
|
+
export declare const CURRENT_DATA_VERSION = 2;
|
|
2
3
|
export declare function createDatabase(dbPath: string): Database.Database;
|
|
4
|
+
/**
|
|
5
|
+
* Read the stored data version. Returns 0 for fresh databases that
|
|
6
|
+
* have never been tagged, so callers can treat any version < current
|
|
7
|
+
* as "migrate me".
|
|
8
|
+
*/
|
|
9
|
+
export declare function getDataVersion(db: Database.Database): number;
|
|
10
|
+
/**
|
|
11
|
+
* Stamp the database with the current data version. Call after a
|
|
12
|
+
* migration completes (or on a fresh database after the first full
|
|
13
|
+
* index, so subsequent runs don't waste time re-migrating).
|
|
14
|
+
*/
|
|
15
|
+
export declare function setDataVersion(db: Database.Database, version: number): void;
|
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import Database from "better-sqlite3";
|
|
2
|
+
// Current data-layer version. Bump this when existing stored data
|
|
3
|
+
// (not just schema shape) has become incompatible with the new code.
|
|
4
|
+
// The Indexer runs data migrations on startup when the stored version
|
|
5
|
+
// is lower than this.
|
|
6
|
+
//
|
|
7
|
+
// 1 — initial. Tables as defined in SCHEMA below.
|
|
8
|
+
// 2 — fix for github.com/sverklo/sverklo/issues/13: symbol_refs
|
|
9
|
+
// generated by v0.2.13 and earlier used chunk-wide dedupe in
|
|
10
|
+
// extractReferences(), which collapsed every repeat call of a
|
|
11
|
+
// symbol in the same chunk to one row. Upgrading triggers
|
|
12
|
+
// re-extraction of references from existing chunks (no file
|
|
13
|
+
// I/O — just regex over chunk.content).
|
|
14
|
+
export const CURRENT_DATA_VERSION = 2;
|
|
2
15
|
const SCHEMA = `
|
|
3
16
|
-- Indexed files
|
|
4
17
|
CREATE TABLE IF NOT EXISTS files (
|
|
@@ -148,6 +161,15 @@ CREATE INDEX IF NOT EXISTS idx_memories_created ON memories(created_at);
|
|
|
148
161
|
CREATE INDEX IF NOT EXISTS idx_memories_stale ON memories(is_stale);
|
|
149
162
|
CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier);
|
|
150
163
|
CREATE INDEX IF NOT EXISTS idx_memories_valid_until ON memories(valid_until_sha);
|
|
164
|
+
|
|
165
|
+
-- Key-value metadata. Used to track the data-layer version so we can
|
|
166
|
+
-- run inline migrations when the code's expectations about stored
|
|
167
|
+
-- data drift from what's on disk. Single-row-per-key; no schema here
|
|
168
|
+
-- because values are small strings.
|
|
169
|
+
CREATE TABLE IF NOT EXISTS meta (
|
|
170
|
+
key TEXT PRIMARY KEY,
|
|
171
|
+
value TEXT NOT NULL
|
|
172
|
+
);
|
|
151
173
|
`;
|
|
152
174
|
// Additive migrations — run after SCHEMA to upgrade existing databases
|
|
153
175
|
const MIGRATIONS = [
|
|
@@ -176,4 +198,29 @@ export function createDatabase(dbPath) {
|
|
|
176
198
|
}
|
|
177
199
|
return db;
|
|
178
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* Read the stored data version. Returns 0 for fresh databases that
|
|
203
|
+
* have never been tagged, so callers can treat any version < current
|
|
204
|
+
* as "migrate me".
|
|
205
|
+
*/
|
|
206
|
+
export function getDataVersion(db) {
|
|
207
|
+
try {
|
|
208
|
+
const row = db
|
|
209
|
+
.prepare("SELECT value FROM meta WHERE key = 'data_version'")
|
|
210
|
+
.get();
|
|
211
|
+
return row ? parseInt(row.value, 10) || 0 : 0;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return 0;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Stamp the database with the current data version. Call after a
|
|
219
|
+
* migration completes (or on a fresh database after the first full
|
|
220
|
+
* index, so subsequent runs don't waste time re-migrating).
|
|
221
|
+
*/
|
|
222
|
+
export function setDataVersion(db, version) {
|
|
223
|
+
db.prepare("INSERT INTO meta (key, value) VALUES ('data_version', ?) " +
|
|
224
|
+
"ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(String(version));
|
|
225
|
+
}
|
|
179
226
|
//# sourceMappingURL=database.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../../src/storage/database.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC,MAAM,MAAM,GAAG
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../../src/storage/database.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC,kEAAkE;AAClE,qEAAqE;AACrE,sEAAsE;AACtE,sBAAsB;AACtB,EAAE;AACF,oDAAoD;AACpD,kEAAkE;AAClE,mEAAmE;AACnE,oEAAoE;AACpE,gEAAgE;AAChE,kEAAkE;AAClE,8CAA8C;AAC9C,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAEtC,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8Jd,CAAC;AAEF,uEAAuE;AACvE,MAAM,UAAU,GAAG;IACjB,6DAA6D;IAC7D,qDAAqD;IACrD,sDAAsD;IACtD,wDAAwD;IACxD,uDAAuD;CACxD,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEhC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC/B,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAClC,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAAC,aAAa;IAE/C,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhB,yEAAyE;IACzE,sEAAsE;IACtE,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACP,2DAA2D;QAC7D,CAAC;IACH,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,EAAqB;IAClD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE;aACX,OAAO,CAAC,mDAAmD,CAAC;aAC5D,GAAG,EAAmC,CAAC;QAC1C,OAAO,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,EAAqB,EAAE,OAAe;IACnE,EAAE,CAAC,OAAO,CACR,2DAA2D;QACzD,uDAAuD,CAC1D,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;AACzB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sverklo",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.14",
|
|
4
4
|
"description": "Sverklo — local-first code intelligence MCP server. Diff-aware MR review, risk scoring, hybrid semantic search, PageRank ranking, persistent memory. Zero config.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,7 +21,10 @@
|
|
|
21
21
|
"lint": "eslint src/",
|
|
22
22
|
"prepublishOnly": "rm -rf dist && npm run build",
|
|
23
23
|
"benchmark": "node --experimental-strip-types benchmark/benchmark.ts",
|
|
24
|
-
"bench
|
|
24
|
+
"bench": "node scripts/bench-reproducer.mjs",
|
|
25
|
+
"bench:quick": "node --experimental-strip-types --no-warnings benchmark/src/index.ts",
|
|
26
|
+
"bench:latency": "node scripts/mcp-latency-profile.mjs",
|
|
27
|
+
"bench:playground": "node scripts/generate-playground-data.mjs"
|
|
25
28
|
},
|
|
26
29
|
"keywords": [
|
|
27
30
|
"sverklo",
|