@fodx/codelens 1.0.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/README.md +212 -0
- package/adapters/pi/codelens.extension.ts +280 -0
- package/adapters/pi/extension.json +10 -0
- package/build/src/cli.js +265 -0
- package/build/src/cli.js.map +1 -0
- package/build/src/context/schema.sql +23 -0
- package/build/src/context/store.js +91 -0
- package/build/src/context/store.js.map +1 -0
- package/build/src/db/db.js +65 -0
- package/build/src/db/db.js.map +1 -0
- package/build/src/db/migrations.js +88 -0
- package/build/src/db/migrations.js.map +1 -0
- package/build/src/db/schema.js +11 -0
- package/build/src/db/schema.js.map +1 -0
- package/build/src/db/schema.sql +111 -0
- package/build/src/git/scope.js +68 -0
- package/build/src/git/scope.js.map +1 -0
- package/build/src/graph/edges.js +76 -0
- package/build/src/graph/edges.js.map +1 -0
- package/build/src/graph/grammars.js +57 -0
- package/build/src/graph/grammars.js.map +1 -0
- package/build/src/graph/query.js +52 -0
- package/build/src/graph/query.js.map +1 -0
- package/build/src/graph/resolve.js +68 -0
- package/build/src/graph/resolve.js.map +1 -0
- package/build/src/graph/symbols.js +84 -0
- package/build/src/graph/symbols.js.map +1 -0
- package/build/src/graph/tests.js +73 -0
- package/build/src/graph/tests.js.map +1 -0
- package/build/src/index/autoprune.js +29 -0
- package/build/src/index/autoprune.js.map +1 -0
- package/build/src/index/deny.js +40 -0
- package/build/src/index/deny.js.map +1 -0
- package/build/src/index/freshness.js +60 -0
- package/build/src/index/freshness.js.map +1 -0
- package/build/src/index/fts.js +125 -0
- package/build/src/index/fts.js.map +1 -0
- package/build/src/index/identity.js +21 -0
- package/build/src/index/identity.js.map +1 -0
- package/build/src/index/indexer.js +32 -0
- package/build/src/index/indexer.js.map +1 -0
- package/build/src/index/manager.js +48 -0
- package/build/src/index/manager.js.map +1 -0
- package/build/src/index/queue.js +47 -0
- package/build/src/index/queue.js.map +1 -0
- package/build/src/index/recovery.js +51 -0
- package/build/src/index/recovery.js.map +1 -0
- package/build/src/index/reindex.js +70 -0
- package/build/src/index/reindex.js.map +1 -0
- package/build/src/index/scanner.js +147 -0
- package/build/src/index/scanner.js.map +1 -0
- package/build/src/index/ttl.js +87 -0
- package/build/src/index/ttl.js.map +1 -0
- package/build/src/index/watcher.js +74 -0
- package/build/src/index/watcher.js.map +1 -0
- package/build/src/installer/agents.js +440 -0
- package/build/src/installer/agents.js.map +1 -0
- package/build/src/obs/doctor.js +53 -0
- package/build/src/obs/doctor.js.map +1 -0
- package/build/src/obs/stats.js +28 -0
- package/build/src/obs/stats.js.map +1 -0
- package/build/src/obs/usage.js +136 -0
- package/build/src/obs/usage.js.map +1 -0
- package/build/src/search/rank.js +70 -0
- package/build/src/search/rank.js.map +1 -0
- package/build/src/search/snippet.js +66 -0
- package/build/src/search/snippet.js.map +1 -0
- package/build/src/server.js +126 -0
- package/build/src/server.js.map +1 -0
- package/build/src/tools/current.js +32 -0
- package/build/src/tools/current.js.map +1 -0
- package/build/src/tools/expand.js +54 -0
- package/build/src/tools/expand.js.map +1 -0
- package/build/src/tools/prune.js +46 -0
- package/build/src/tools/prune.js.map +1 -0
- package/build/src/tools/refresh.js +14 -0
- package/build/src/tools/refresh.js.map +1 -0
- package/build/src/tools/registry.js +176 -0
- package/build/src/tools/registry.js.map +1 -0
- package/build/src/tools/related.js +28 -0
- package/build/src/tools/related.js.map +1 -0
- package/build/src/tools/save.js +15 -0
- package/build/src/tools/save.js.map +1 -0
- package/build/src/tools/search.js +124 -0
- package/build/src/tools/search.js.map +1 -0
- package/build/src/upgrade.js +74 -0
- package/build/src/upgrade.js.map +1 -0
- package/build/src/util/hash.js +15 -0
- package/build/src/util/hash.js.map +1 -0
- package/build/src/util/paths.js +67 -0
- package/build/src/util/paths.js.map +1 -0
- package/build/src/version.js +27 -0
- package/build/src/version.js.map +1 -0
- package/docs/agent-guide.md +47 -0
- package/docs/codelens-preview.png +0 -0
- package/docs/routing.md +59 -0
- package/docs/tools.md +53 -0
- package/package.json +103 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import DatabaseSync from "better-sqlite3";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { mkdirSync } from "node:fs";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
/**
|
|
7
|
+
* Usage tracking — GLOBAL (per-user, not per-repo).
|
|
8
|
+
*
|
|
9
|
+
* Stored in a single ~/.codelens/usage.db so switching repos doesn't
|
|
10
|
+
* hide your real usage. Each row is keyed by (tool, repo_id), so the snapshot
|
|
11
|
+
* can show both totals and a per-repo breakdown.
|
|
12
|
+
*
|
|
13
|
+
* "bytes_saved" is a defensible estimate for DISCOVERY tools (cl_search,
|
|
14
|
+
* cl_related): the agent would otherwise grep + read ~N files at ~avg file
|
|
15
|
+
* size; instead it got compact handles. saved ≈ max(0, N*AVG_FILE_BYTES -
|
|
16
|
+
* bytesServed). Non-discovery tools record calls + bytes_served but claim no
|
|
17
|
+
* savings.
|
|
18
|
+
*/
|
|
19
|
+
const AVG_FILE_BYTES = 4096;
|
|
20
|
+
export const DISCOVERY_TOOLS = new Set(["cl_search", "cl_related"]);
|
|
21
|
+
/** Tools that count as "usage" (retrieval + context management). Operational
|
|
22
|
+
* tools (refresh/doctor/stats/prune/drop/current/usage) are NOT tracked — they
|
|
23
|
+
* are maintenance, not the agent using the retrieval system. */
|
|
24
|
+
export const TRACKED_TOOLS = new Set(["cl_search", "cl_related", "cl_expand", "cl_save", "cl_load"]);
|
|
25
|
+
function usageDbPath() {
|
|
26
|
+
const dir = join(homedir(), ".codelens");
|
|
27
|
+
mkdirSync(dir, { recursive: true });
|
|
28
|
+
return join(dir, "usage.db");
|
|
29
|
+
}
|
|
30
|
+
export function repoId(repoRoot) {
|
|
31
|
+
return createHash("sha256").update(repoRoot).digest("hex").slice(0, 16);
|
|
32
|
+
}
|
|
33
|
+
/** Open the global usage DB (creates the table if missing). */
|
|
34
|
+
export function initUsageTable(db) {
|
|
35
|
+
db.exec(`CREATE TABLE IF NOT EXISTS tool_usage (
|
|
36
|
+
tool TEXT NOT NULL,
|
|
37
|
+
repo_id TEXT NOT NULL,
|
|
38
|
+
calls INTEGER NOT NULL DEFAULT 0,
|
|
39
|
+
last_called_at INTEGER,
|
|
40
|
+
bytes_served INTEGER NOT NULL DEFAULT 0,
|
|
41
|
+
bytes_saved INTEGER NOT NULL DEFAULT 0,
|
|
42
|
+
PRIMARY KEY (tool, repo_id)
|
|
43
|
+
)`);
|
|
44
|
+
}
|
|
45
|
+
export function openGlobalUsageDb() {
|
|
46
|
+
const db = new DatabaseSync(usageDbPath());
|
|
47
|
+
db.pragma("journal_mode = WAL");
|
|
48
|
+
initUsageTable(db);
|
|
49
|
+
return db;
|
|
50
|
+
}
|
|
51
|
+
export class UsageTracker {
|
|
52
|
+
db;
|
|
53
|
+
constructor(db) {
|
|
54
|
+
this.db = db;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Record one tool call. `repoRoot` scopes the row; `resultText` is the JSON
|
|
58
|
+
* returned. `savedOverride` (when provided) is a precomputed savings estimate
|
|
59
|
+
* from actual indexed file sizes — used by the server wrapper for discovery
|
|
60
|
+
* tools. When omitted, falls back to the flat `handles × AVG_FILE_BYTES` proxy.
|
|
61
|
+
*/
|
|
62
|
+
record(tool, repoRoot, resultText, isError = false, savedOverride) {
|
|
63
|
+
if (isError)
|
|
64
|
+
return;
|
|
65
|
+
const bytesServed = Buffer.byteLength(resultText, "utf-8");
|
|
66
|
+
let bytesSaved;
|
|
67
|
+
if (savedOverride !== undefined) {
|
|
68
|
+
bytesSaved = Math.max(0, savedOverride);
|
|
69
|
+
}
|
|
70
|
+
else if (DISCOVERY_TOOLS.has(tool)) {
|
|
71
|
+
const handles = countHandles(tool, resultText);
|
|
72
|
+
bytesSaved = Math.max(0, handles * AVG_FILE_BYTES - bytesServed);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
bytesSaved = 0;
|
|
76
|
+
}
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
const rid = repoId(repoRoot);
|
|
79
|
+
this.db.prepare(`INSERT INTO tool_usage (tool, repo_id, calls, last_called_at, bytes_served, bytes_saved)
|
|
80
|
+
VALUES (?, ?, 1, ?, ?, ?)
|
|
81
|
+
ON CONFLICT(tool, repo_id) DO UPDATE SET
|
|
82
|
+
calls = calls + 1,
|
|
83
|
+
last_called_at = excluded.last_called_at,
|
|
84
|
+
bytes_served = bytes_served + excluded.bytes_served,
|
|
85
|
+
bytes_saved = bytes_saved + excluded.bytes_saved`).run(tool, rid, now, bytesServed, bytesSaved);
|
|
86
|
+
}
|
|
87
|
+
snapshot() {
|
|
88
|
+
// Only report tracked (retrieval/context) tools — operational tools
|
|
89
|
+
// (refresh/doctor/stats/...) are never usage even if stale rows exist.
|
|
90
|
+
const tracked = [...TRACKED_TOOLS].map((t) => `'${t}'`).join(",");
|
|
91
|
+
const perTool = this.db.prepare(`SELECT tool, SUM(calls) AS calls, MAX(last_called_at) AS last_called_at,
|
|
92
|
+
SUM(bytes_served) AS bytes_served, SUM(bytes_saved) AS bytes_saved
|
|
93
|
+
FROM tool_usage WHERE tool IN (${tracked}) GROUP BY tool ORDER BY calls DESC`).all();
|
|
94
|
+
const perRepo = this.db.prepare(`SELECT repo_id, SUM(calls) AS calls, SUM(bytes_served) AS bytes_served, SUM(bytes_saved) AS bytes_saved
|
|
95
|
+
FROM tool_usage WHERE tool IN (${tracked}) GROUP BY repo_id ORDER BY calls DESC`).all();
|
|
96
|
+
const totals = perTool.reduce((a, r) => ({ calls: a.calls + r.calls, bytes_served: a.bytes_served + r.bytes_served, bytes_saved: a.bytes_saved + r.bytes_saved }), { calls: 0, bytes_served: 0, bytes_saved: 0 });
|
|
97
|
+
return { perTool, perRepo, totals };
|
|
98
|
+
}
|
|
99
|
+
reset() { this.db.prepare("DELETE FROM tool_usage").run(); }
|
|
100
|
+
}
|
|
101
|
+
function countHandles(tool, resultText) {
|
|
102
|
+
void tool;
|
|
103
|
+
try {
|
|
104
|
+
const obj = JSON.parse(resultText);
|
|
105
|
+
return Array.isArray(obj.results) ? obj.results.length : 0;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Estimate context saved for a discovery call using ACTUAL indexed file sizes.
|
|
113
|
+
* saved = max(0, sum(distinct result files' sizes) − bytesServed).
|
|
114
|
+
* Capped at `cap` distinct files so a 100-neighbor `cl_related` result doesn't
|
|
115
|
+
* inflate the total (the agent wouldn't read all 100 without the tool).
|
|
116
|
+
*/
|
|
117
|
+
export function estimateSavedFromPaths(db, indexId, paths, bytesServed, cap = 50) {
|
|
118
|
+
const distinct = [...new Set(paths)].slice(0, cap);
|
|
119
|
+
if (distinct.length === 0)
|
|
120
|
+
return 0;
|
|
121
|
+
const placeholders = distinct.map(() => "?").join(",");
|
|
122
|
+
const row = db.prepare(`SELECT COALESCE(SUM(size), 0) AS total FROM files WHERE index_id = ? AND path IN (${placeholders})`).get(indexId, ...distinct);
|
|
123
|
+
return Math.max(0, (row.total ?? 0) - bytesServed);
|
|
124
|
+
}
|
|
125
|
+
/** Extract distinct result paths from a discovery tool's JSON result. */
|
|
126
|
+
export function extractDiscoveryPaths(resultText) {
|
|
127
|
+
try {
|
|
128
|
+
const obj = JSON.parse(resultText);
|
|
129
|
+
return (obj.results ?? []).map((r) => r.path).filter((p) => typeof p === "string");
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
export { AVG_FILE_BYTES };
|
|
136
|
+
//# sourceMappingURL=usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../../src/obs/usage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid ranking.
|
|
3
|
+
*
|
|
4
|
+
* Combines lexical FTS, symbol name match, graph proximity, and recency into
|
|
5
|
+
* one ranked list via deterministic weighted fusion (tunable later).
|
|
6
|
+
*
|
|
7
|
+
* Default weights (sum to 1):
|
|
8
|
+
* FTS 0.45 / symbol 0.25 / graph 0.30
|
|
9
|
+
* (recency 0 — reserved for future use; weight stays in the schema but unused
|
|
10
|
+
* until a recency signal is populated.)
|
|
11
|
+
*/
|
|
12
|
+
export const DEFAULT_WEIGHTS = {
|
|
13
|
+
fts: 0.40,
|
|
14
|
+
symbol: 0.22,
|
|
15
|
+
graph: 0.28,
|
|
16
|
+
code: 0.10, // modest code-over-prose boost for code-discovery queries
|
|
17
|
+
recency: 0.0,
|
|
18
|
+
};
|
|
19
|
+
/** Normalize a raw score to 0-1 using a min-max against the batch (or clamp). */
|
|
20
|
+
export function normalize(values) {
|
|
21
|
+
if (values.length === 0)
|
|
22
|
+
return [];
|
|
23
|
+
const max = Math.max(...values);
|
|
24
|
+
const min = Math.min(...values);
|
|
25
|
+
if (max === min)
|
|
26
|
+
return values.map(() => 1);
|
|
27
|
+
return values.map((v) => (v - min) / (max - min));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Fuse signal scores into ranked results. Missing signals (undefined) are
|
|
31
|
+
* treated as 0. Weights for absent signals are redistributed proportionally
|
|
32
|
+
* across the present ones so the active weights always sum to 1.
|
|
33
|
+
*/
|
|
34
|
+
export function rank(signals, weights = DEFAULT_WEIGHTS) {
|
|
35
|
+
if (signals.length === 0)
|
|
36
|
+
return [];
|
|
37
|
+
const present = ["fts", "symbol", "graph", "code", "recency"].filter((k) => signals.some((s) => s[k] !== undefined));
|
|
38
|
+
const w = { ...weights };
|
|
39
|
+
const absent = ["fts", "symbol", "graph", "code", "recency"].filter((k) => !present.includes(k));
|
|
40
|
+
const presentTotal = present.reduce((sum, k) => sum + w[k], 0);
|
|
41
|
+
if (absent.length > 0 && presentTotal > 0) {
|
|
42
|
+
const absentTotal = absent.reduce((sum, k) => sum + w[k], 0);
|
|
43
|
+
for (const k of present)
|
|
44
|
+
w[k] = w[k] + (absentTotal * (w[k] / presentTotal));
|
|
45
|
+
for (const k of absent)
|
|
46
|
+
w[k] = 0;
|
|
47
|
+
}
|
|
48
|
+
const fused = signals.map((s) => {
|
|
49
|
+
const score = (s.fts ?? 0) * w.fts +
|
|
50
|
+
(s.symbol ?? 0) * w.symbol +
|
|
51
|
+
(s.graph ?? 0) * w.graph +
|
|
52
|
+
(s.code ?? 0) * w.code +
|
|
53
|
+
(s.recency ?? 0) * w.recency;
|
|
54
|
+
const why = [];
|
|
55
|
+
if (s.fts)
|
|
56
|
+
why.push("fts");
|
|
57
|
+
if (s.symbol)
|
|
58
|
+
why.push("symbol");
|
|
59
|
+
if (s.graph)
|
|
60
|
+
why.push("graph");
|
|
61
|
+
if (s.code)
|
|
62
|
+
why.push("code");
|
|
63
|
+
if (s.recency)
|
|
64
|
+
why.push("recency");
|
|
65
|
+
return { path: s.path, startLine: s.startLine, endLine: s.endLine, chunkId: s.chunkId, score, why };
|
|
66
|
+
});
|
|
67
|
+
fused.sort((a, b) => b.score - a.score);
|
|
68
|
+
return fused;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=rank.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rank.js","sourceRoot":"","sources":["../../../src/search/rank.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snippet + dedent helpers for agent-facing output.
|
|
3
|
+
*
|
|
4
|
+
* Best practice for agent-consumable tool output: compact, minimal redundant
|
|
5
|
+
* whitespace, relative code structure preserved. `dedent` strips the COMMON
|
|
6
|
+
* leading whitespace across non-blank lines so a deeply-nested function body
|
|
7
|
+
* isn't padded by its base indent — lossless for understanding, fewer tokens.
|
|
8
|
+
*/
|
|
9
|
+
const STOPWORDS = new Set(["the", "a", "an", "and", "or", "of", "to", "in", "for", "is", "on"]);
|
|
10
|
+
function tokenize(query) {
|
|
11
|
+
return query.toLowerCase().split(/[^a-z0-9_]+/i).filter((t) => t.length > 1 && !STOPWORDS.has(t));
|
|
12
|
+
}
|
|
13
|
+
/** Strip the common leading whitespace from non-blank lines (Python-style dedent). */
|
|
14
|
+
export function dedent(text) {
|
|
15
|
+
const lines = text.split("\n");
|
|
16
|
+
let minIndent = Infinity;
|
|
17
|
+
for (const line of lines) {
|
|
18
|
+
if (line.trim().length === 0)
|
|
19
|
+
continue; // ignore blank lines
|
|
20
|
+
const indent = line.match(/^[ \t]*/)?.[0]?.length ?? 0;
|
|
21
|
+
if (indent < minIndent)
|
|
22
|
+
minIndent = indent;
|
|
23
|
+
}
|
|
24
|
+
if (!Number.isFinite(minIndent) || minIndent === 0)
|
|
25
|
+
return text;
|
|
26
|
+
return lines.map((l) => (l.length >= minIndent ? l.slice(minIndent) : l)).join("\n");
|
|
27
|
+
}
|
|
28
|
+
/** Extract a windowed snippet around the first matched term, highlight, dedent. */
|
|
29
|
+
export function extractSnippet(content, query, maxChars = 1500) {
|
|
30
|
+
const terms = tokenize(query);
|
|
31
|
+
let snippet;
|
|
32
|
+
if (terms.length === 0) {
|
|
33
|
+
snippet = content.length > maxChars ? content.slice(0, maxChars) + "\n…" : content;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const lower = content.toLowerCase();
|
|
37
|
+
let bestIdx = -1;
|
|
38
|
+
for (const t of terms) {
|
|
39
|
+
const idx = lower.indexOf(t);
|
|
40
|
+
if (idx >= 0 && (bestIdx === -1 || idx < bestIdx))
|
|
41
|
+
bestIdx = idx;
|
|
42
|
+
}
|
|
43
|
+
if (bestIdx === -1) {
|
|
44
|
+
snippet = content.length > maxChars ? content.slice(0, maxChars) + "\n…" : content;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const half = Math.floor(maxChars / 2);
|
|
48
|
+
const start = Math.max(0, bestIdx - half);
|
|
49
|
+
const end = Math.min(content.length, start + maxChars);
|
|
50
|
+
let s = content.slice(start, end);
|
|
51
|
+
if (start > 0)
|
|
52
|
+
s = "…" + s;
|
|
53
|
+
if (end < content.length)
|
|
54
|
+
s = s + "\n…";
|
|
55
|
+
snippet = s;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
snippet = dedent(snippet);
|
|
59
|
+
// Highlight matched terms (on the dedented text).
|
|
60
|
+
for (const t of terms) {
|
|
61
|
+
const re = new RegExp(`(${t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})`, "ig");
|
|
62
|
+
snippet = snippet.replace(re, "**$1**");
|
|
63
|
+
}
|
|
64
|
+
return snippet;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=snippet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippet.js","sourceRoot":"","sources":["../../../src/search/snippet.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AAEhG,SAAS,QAAQ,CAAC,KAAa;IAC7B,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACpG,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,SAAS,GAAG,QAAQ,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS,CAAC,qBAAqB;QAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC;QACvD,IAAI,MAAM,GAAG,SAAS;YAAE,SAAS,GAAG,MAAM,CAAC;IAC7C,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAChE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvF,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,KAAa,EAAE,QAAQ,GAAG,IAAI;IAC5E,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9B,IAAI,OAAe,CAAC;IACpB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;IACrF,CAAC;SAAM,CAAC;QACN,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;QACpC,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC;QACjB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC7B,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,CAAC,IAAI,GAAG,GAAG,OAAO,CAAC;gBAAE,OAAO,GAAG,GAAG,CAAC;QACnE,CAAC;QACD,IAAI,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC;YACnB,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;QACrF,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;YACtC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,CAAC;YAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,GAAG,QAAQ,CAAC,CAAC;YACvD,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAClC,IAAI,KAAK,GAAG,CAAC;gBAAE,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;YAC3B,IAAI,GAAG,GAAG,OAAO,CAAC,MAAM;gBAAE,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACxC,OAAO,GAAG,CAAC,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1B,kDAAkD;IAClD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC7E,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { resolveReal } from "./util/paths.js";
|
|
5
|
+
import { openDb, openMemoryDb, CorruptDb } from "./db/db.js";
|
|
6
|
+
import { openContextDb, openMemoryContextDb } from "./context/store.js";
|
|
7
|
+
import { TOOLS } from "./tools/registry.js";
|
|
8
|
+
import { UsageTracker, openGlobalUsageDb, DISCOVERY_TOOLS, TRACKED_TOOLS, estimateSavedFromPaths, extractDiscoveryPaths } from "./obs/usage.js";
|
|
9
|
+
import { getActiveIndexId } from "./index/manager.js";
|
|
10
|
+
import { scheduleAutoPrune } from "./index/autoprune.js";
|
|
11
|
+
import { FileWatcher } from "./index/watcher.js";
|
|
12
|
+
import { cli } from "./cli.js";
|
|
13
|
+
import { registerWatcher } from "./index/reindex.js";
|
|
14
|
+
import { VERSION } from "./version.js";
|
|
15
|
+
import { createHash } from "node:crypto";
|
|
16
|
+
import { mkdirSync } from "node:fs";
|
|
17
|
+
import { homedir } from "node:os";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
/**
|
|
20
|
+
* CodeLens MCP server (Step 24 wiring).
|
|
21
|
+
*
|
|
22
|
+
* Resolves the repo root from cwd, opens the core index DB and the contexts DB
|
|
23
|
+
* (separate file), registers all 10 tools from the registry, and starts the
|
|
24
|
+
* stdio transport. Auto-prune runs on startup + periodic idle timer.
|
|
25
|
+
*/
|
|
26
|
+
function createServer(ctx) {
|
|
27
|
+
const server = new McpServer({ name: "codelens", version: VERSION });
|
|
28
|
+
for (const tool of TOOLS) {
|
|
29
|
+
server.tool(tool.name, tool.description, tool.schema, async (args) => {
|
|
30
|
+
const usage = new UsageTracker(openGlobalUsageDb());
|
|
31
|
+
try {
|
|
32
|
+
const result = await tool.handler(ctx, args);
|
|
33
|
+
const text = JSON.stringify(result, null, 2);
|
|
34
|
+
let savedOverride;
|
|
35
|
+
if (DISCOVERY_TOOLS.has(tool.name)) {
|
|
36
|
+
try {
|
|
37
|
+
const indexId = getActiveIndexId();
|
|
38
|
+
if (indexId)
|
|
39
|
+
savedOverride = estimateSavedFromPaths(ctx.coreDb, indexId, extractDiscoveryPaths(text), Buffer.byteLength(text));
|
|
40
|
+
}
|
|
41
|
+
catch { /* fall back to flat proxy */ }
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
if (TRACKED_TOOLS.has(tool.name))
|
|
45
|
+
usage.record(tool.name, ctx.repoRoot, text, false, savedOverride);
|
|
46
|
+
}
|
|
47
|
+
catch { /* usage best-effort */ }
|
|
48
|
+
return { content: [{ type: "text", text }] };
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
const text = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
52
|
+
try {
|
|
53
|
+
if (TRACKED_TOOLS.has(tool.name))
|
|
54
|
+
usage.record(tool.name, ctx.repoRoot, text, true);
|
|
55
|
+
}
|
|
56
|
+
catch { /* usage best-effort */ }
|
|
57
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return server;
|
|
62
|
+
}
|
|
63
|
+
function repoRootFromCwd() {
|
|
64
|
+
return resolveReal(process.cwd());
|
|
65
|
+
}
|
|
66
|
+
const CLI_COMMANDS = new Set(["current", "index", "refresh", "search", "related", "stats", "usage", "doctor", "install", "uninstall", "upgrade", "version", "--print-config", "-v", "--version", "--help", "-h"]);
|
|
67
|
+
export async function main() {
|
|
68
|
+
// CLI dispatch: if the first arg is a known subcommand (bare word like
|
|
69
|
+
// `install` or a flag-style one like `--print-config`/`--version`), run the
|
|
70
|
+
// CLI with the FULL arg list so --target/--command/--yes are preserved.
|
|
71
|
+
// Otherwise start the MCP stdio server.
|
|
72
|
+
const fullArgs = process.argv.slice(2);
|
|
73
|
+
const head = fullArgs[0];
|
|
74
|
+
if (head && CLI_COMMANDS.has(head)) {
|
|
75
|
+
const code = await cli(fullArgs);
|
|
76
|
+
process.exit(code);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// Smoke mode: register tools against an in-memory context and print names.
|
|
80
|
+
if (process.argv.includes("--smoke")) {
|
|
81
|
+
const ctx = { coreDb: openMemoryDb(), ctxDb: openMemoryContextDb(), repoRoot: "/tmp" };
|
|
82
|
+
const server = createServer(ctx);
|
|
83
|
+
void server;
|
|
84
|
+
console.log(JSON.stringify({ ok: true, tools: TOOLS.map((t) => t.name) }));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const repoRoot = repoRootFromCwd();
|
|
88
|
+
let coreDb;
|
|
89
|
+
try {
|
|
90
|
+
coreDb = openDb(dbPathFor(repoRoot));
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
if (err instanceof CorruptDb) {
|
|
94
|
+
// Production recovery: delete the corrupt DB file and rebuild on next refresh.
|
|
95
|
+
const { rmSync } = await import("node:fs");
|
|
96
|
+
try {
|
|
97
|
+
rmSync(dbPathFor(repoRoot));
|
|
98
|
+
}
|
|
99
|
+
catch { /* ignore */ }
|
|
100
|
+
coreDb = openDb(dbPathFor(repoRoot));
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
throw err;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const ctxDb = openContextDb(repoRoot);
|
|
107
|
+
const ctx = { coreDb, ctxDb, repoRoot };
|
|
108
|
+
scheduleAutoPrune(coreDb);
|
|
109
|
+
const watcher = new FileWatcher(repoRoot);
|
|
110
|
+
watcher.start();
|
|
111
|
+
registerWatcher(watcher);
|
|
112
|
+
const server = createServer(ctx);
|
|
113
|
+
const transport = new StdioServerTransport();
|
|
114
|
+
await server.connect(transport);
|
|
115
|
+
}
|
|
116
|
+
function dbPathFor(repoRoot) {
|
|
117
|
+
const dir = join(homedir(), ".codelens", "indexes");
|
|
118
|
+
mkdirSync(dir, { recursive: true });
|
|
119
|
+
const rid = createHash("sha256").update(repoRoot).digest("hex").slice(0, 16);
|
|
120
|
+
return join(dir, `index-${rid}.db`);
|
|
121
|
+
}
|
|
122
|
+
main().catch((err) => {
|
|
123
|
+
console.error("codelens server error:", err);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
});
|
|
126
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { detectScope } from "../git/scope.js";
|
|
2
|
+
import { getActiveIndexId, getIndex } from "../index/manager.js";
|
|
3
|
+
export function ctxCurrent(db, repoRoot, scope) {
|
|
4
|
+
const s = scope ?? detectScope(repoRoot);
|
|
5
|
+
if (!s) {
|
|
6
|
+
return {
|
|
7
|
+
repo: repoRoot,
|
|
8
|
+
branch: "",
|
|
9
|
+
headSha: "",
|
|
10
|
+
indexId: null,
|
|
11
|
+
status: "missing",
|
|
12
|
+
dirtyFiles: 0,
|
|
13
|
+
lastIndexedAt: null,
|
|
14
|
+
indexStatus: null,
|
|
15
|
+
inGitRepo: false,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const activeId = getActiveIndexId();
|
|
19
|
+
const row = activeId ? getIndex(db, activeId) : undefined;
|
|
20
|
+
return {
|
|
21
|
+
repo: s.repoRoot,
|
|
22
|
+
branch: s.branch,
|
|
23
|
+
headSha: s.headSha,
|
|
24
|
+
indexId: activeId,
|
|
25
|
+
status: row ? (row.status === "active" ? "active" : "stale") : "missing",
|
|
26
|
+
dirtyFiles: s.dirtyFiles.length,
|
|
27
|
+
lastIndexedAt: row?.last_accessed_at ?? null,
|
|
28
|
+
indexStatus: row?.status ?? null,
|
|
29
|
+
inGitRepo: true,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=current.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"current.js","sourceRoot":"","sources":["../../../src/tools/current.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAiB,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAoBjE,MAAM,UAAU,UAAU,CAAC,EAAqB,EAAE,QAAgB,EAAE,KAAuB;IACzF,MAAM,CAAC,GAAG,KAAK,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,OAAO;YACL,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,EAAE;YACV,OAAO,EAAE,EAAE;YACX,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,SAAS;YACjB,UAAU,EAAE,CAAC;YACb,aAAa,EAAE,IAAI;YACnB,WAAW,EAAE,IAAI;YACjB,SAAS,EAAE,KAAK;SACjB,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1D,OAAO;QACL,IAAI,EAAE,CAAC,CAAC,QAAQ;QAChB,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,OAAO,EAAE,QAAQ;QACjB,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;QACxE,UAAU,EAAE,CAAC,CAAC,UAAU,CAAC,MAAM;QAC/B,aAAa,EAAE,GAAG,EAAE,gBAAgB,IAAI,IAAI;QAC5C,WAAW,EAAE,GAAG,EAAE,MAAM,IAAI,IAAI;QAChC,SAAS,EAAE,IAAI;KAChB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { resolveReal, repoRelative } from "../util/paths.js";
|
|
4
|
+
import { getActiveIndexId } from "../index/manager.js";
|
|
5
|
+
import { dedent } from "../search/snippet.js";
|
|
6
|
+
const DEFAULT_BUDGET = 4000; // ~1000 tokens
|
|
7
|
+
export function ctxExpand(db, repoRoot, opts) {
|
|
8
|
+
const indexId = getActiveIndexId();
|
|
9
|
+
if (!indexId)
|
|
10
|
+
throw new Error("no active index — call cl_refresh first");
|
|
11
|
+
void db;
|
|
12
|
+
let path;
|
|
13
|
+
let startLine = opts.startLine;
|
|
14
|
+
let endLine = opts.endLine;
|
|
15
|
+
if (opts.handle) {
|
|
16
|
+
// Resolve handle → chunk row to get path + range.
|
|
17
|
+
const row = db
|
|
18
|
+
.prepare("SELECT path, start_line, end_line FROM chunks WHERE id = ? AND index_id = ?")
|
|
19
|
+
.get(opts.handle, indexId);
|
|
20
|
+
if (!row)
|
|
21
|
+
throw new Error(`handle not found in current index: ${opts.handle}`);
|
|
22
|
+
path = row.path;
|
|
23
|
+
if (startLine === undefined)
|
|
24
|
+
startLine = row.start_line;
|
|
25
|
+
if (endLine === undefined)
|
|
26
|
+
endLine = row.end_line;
|
|
27
|
+
}
|
|
28
|
+
else if (opts.path) {
|
|
29
|
+
// Normalize the path to repo-relative POSIX; throws on traversal.
|
|
30
|
+
path = repoRelative(repoRoot, opts.path);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
throw new Error("cl_expand requires path or handle");
|
|
34
|
+
}
|
|
35
|
+
// (path is set in both branches above; TS needs a definite assignment)
|
|
36
|
+
path = path;
|
|
37
|
+
const root = resolveReal(repoRoot);
|
|
38
|
+
const abs = join(root, path);
|
|
39
|
+
// Security: ensure resolved path stays inside repo.
|
|
40
|
+
repoRelative(repoRoot, abs);
|
|
41
|
+
const text = readFileSync(abs, "utf-8");
|
|
42
|
+
const lines = text.split("\n");
|
|
43
|
+
const s = Math.max(1, startLine ?? 1);
|
|
44
|
+
const e = Math.min(lines.length, endLine ?? lines.length);
|
|
45
|
+
const slice = lines.slice(s - 1, e).join("\n");
|
|
46
|
+
const budget = opts.budget ?? DEFAULT_BUDGET;
|
|
47
|
+
const truncated = slice.length > budget;
|
|
48
|
+
const trimmed = truncated ? slice.slice(0, budget) + "\n…[truncated — increase budget]" : slice;
|
|
49
|
+
// Dedent (strip common leading whitespace) for compact agent reading. Use
|
|
50
|
+
// raw read for exact bytes when editing — line numbers stay accurate.
|
|
51
|
+
const content = dedent(trimmed);
|
|
52
|
+
return { path, startLine: s, endLine: e, content, truncated, chars: content.length };
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=expand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expand.js","sourceRoot":"","sources":["../../../src/tools/expand.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { getActiveIndexId, getIndex, getOrCreateIndex } from "../index/manager.js";
|
|
2
|
+
import { pruneIndexes } from "../index/ttl.js";
|
|
3
|
+
/**
|
|
4
|
+
* cl_prune + cl_drop tools (Step 23).
|
|
5
|
+
*
|
|
6
|
+
* `cl_prune` runs the TTL sweep manually. `cl_drop` deletes a specific
|
|
7
|
+
* branch/index by id, refusing to drop the currently active index.
|
|
8
|
+
*/
|
|
9
|
+
export function ctxPrune(db) {
|
|
10
|
+
return pruneIndexes(db);
|
|
11
|
+
}
|
|
12
|
+
/** Drop a specific index by id or branch name. Refuses the active index. */
|
|
13
|
+
export function ctxDrop(db, target) {
|
|
14
|
+
const active = getActiveIndexId();
|
|
15
|
+
let indexId = target.indexId;
|
|
16
|
+
if (!indexId && target.branch) {
|
|
17
|
+
// Prefer an exact (branch, worktree) match; fall back to the most-recently-
|
|
18
|
+
// accessed index on that branch so same-branch worktrees don't collide.
|
|
19
|
+
const row = (target.worktreePath
|
|
20
|
+
? db.prepare("SELECT id FROM indexes WHERE branch_name = ? AND worktree_path = ? ORDER BY last_accessed_at DESC LIMIT 1").get(target.branch, target.worktreePath)
|
|
21
|
+
: db.prepare("SELECT id FROM indexes WHERE branch_name = ? ORDER BY last_accessed_at DESC LIMIT 1").get(target.branch));
|
|
22
|
+
indexId = row?.id;
|
|
23
|
+
}
|
|
24
|
+
if (!indexId)
|
|
25
|
+
return { deleted: false, indexId: "", reason: "not found" };
|
|
26
|
+
if (indexId === active)
|
|
27
|
+
return { deleted: false, indexId, reason: "refused: active index" };
|
|
28
|
+
const row = getIndex(db, indexId);
|
|
29
|
+
if (!row)
|
|
30
|
+
return { deleted: false, indexId, reason: "not found" };
|
|
31
|
+
if (row.pinned)
|
|
32
|
+
return { deleted: false, indexId, reason: "refused: pinned" };
|
|
33
|
+
const tx = db.transaction(() => {
|
|
34
|
+
db.prepare("DELETE FROM chunks_fts WHERE index_id = ?").run(indexId);
|
|
35
|
+
db.prepare("DELETE FROM chunks WHERE index_id = ?").run(indexId);
|
|
36
|
+
db.prepare("DELETE FROM symbols WHERE index_id = ?").run(indexId);
|
|
37
|
+
db.prepare("DELETE FROM edges WHERE index_id = ?").run(indexId);
|
|
38
|
+
db.prepare("DELETE FROM files WHERE index_id = ?").run(indexId);
|
|
39
|
+
db.prepare("DELETE FROM index_locks WHERE index_id = ?").run(indexId);
|
|
40
|
+
db.prepare("DELETE FROM indexes WHERE id = ?").run(indexId);
|
|
41
|
+
});
|
|
42
|
+
tx();
|
|
43
|
+
return { deleted: true, indexId };
|
|
44
|
+
}
|
|
45
|
+
void getOrCreateIndex;
|
|
46
|
+
//# sourceMappingURL=prune.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prune.js","sourceRoot":"","sources":["../../../src/tools/prune.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,EAAE,YAAY,EAAoB,MAAM,iBAAiB,CAAC;AAEjE;;;;;GAKG;AAEH,MAAM,UAAU,QAAQ,CAAC,EAAqB;IAC5C,OAAO,YAAY,CAAC,EAAE,CAAC,CAAC;AAC1B,CAAC;AAQD,4EAA4E;AAC5E,MAAM,UAAU,OAAO,CAAC,EAAqB,EAAE,MAAoE;IACjH,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAClC,IAAI,OAAO,GAAuB,MAAM,CAAC,OAAO,CAAC;IAEjD,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAC9B,4EAA4E;QAC5E,wEAAwE;QACxE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,YAAY;YAC9B,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,2GAA2G,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC;YACjK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,qFAAqF,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAA+B,CAAC;QACxJ,OAAO,GAAG,GAAG,EAAE,EAAE,CAAC;IACpB,CAAC;IACD,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAC1E,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;IAE5F,MAAM,GAAG,GAAG,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAClE,IAAI,GAAG,CAAC,MAAM;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAE9E,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;QAC7B,EAAE,CAAC,OAAO,CAAC,2CAA2C,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrE,EAAE,CAAC,OAAO,CAAC,uCAAuC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACjE,EAAE,CAAC,OAAO,CAAC,wCAAwC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAClE,EAAE,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5D,EAAE,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACpE,EAAE,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACtE,EAAE,CAAC,OAAO,CAAC,kCAAkC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IACH,EAAE,EAAE,CAAC;IACL,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACpC,CAAC;AAED,KAAK,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { buildIndex } from "../index/indexer.js";
|
|
2
|
+
export function ctxRefresh(db, scope) {
|
|
3
|
+
const r = buildIndex(db, scope);
|
|
4
|
+
return {
|
|
5
|
+
indexId: r.indexId,
|
|
6
|
+
branch: scope.branch,
|
|
7
|
+
headSha: scope.headSha,
|
|
8
|
+
indexedFiles: r.indexedFiles,
|
|
9
|
+
totalChunks: r.totalChunks,
|
|
10
|
+
skipped: r.skipped,
|
|
11
|
+
status: "ready",
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=refresh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../../src/tools/refresh.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAmBjD,MAAM,UAAU,UAAU,CAAC,EAAqB,EAAE,KAAe;IAC/D,MAAM,CAAC,GAAG,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAChC,OAAO;QACL,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,MAAM,EAAE,OAAO;KAChB,CAAC;AACJ,CAAC"}
|