@neuralsea/workspace-indexer 0.3.6 → 0.4.1
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/{chunk-FUUQXFJQ.js → chunk-TQTWTPPG.js} +563 -421
- package/dist/cli.cjs +506 -377
- package/dist/cli.js +1 -1
- package/dist/index.cjs +517 -379
- package/dist/index.d.cts +57 -3
- package/dist/index.d.ts +57 -3
- package/dist/index.js +9 -1
- package/package.json +6 -3
package/dist/index.cjs
CHANGED
|
@@ -47,11 +47,14 @@ __export(index_exports, {
|
|
|
47
47
|
WorkspaceLinker: () => WorkspaceLinker,
|
|
48
48
|
WorkspaceStore: () => WorkspaceStore,
|
|
49
49
|
asProgressSink: () => asProgressSink,
|
|
50
|
+
betterSqlite3Adapter: () => betterSqlite3Adapter,
|
|
50
51
|
chunkSource: () => chunkSource,
|
|
51
52
|
createAnnIndex: () => createAnnIndex,
|
|
52
53
|
createNeo4jGraphStore: () => createNeo4jGraphStore,
|
|
53
54
|
createVSCodeSymbolGraphProvider: () => createVSCodeSymbolGraphProvider,
|
|
54
55
|
createVectorIndex: () => createVectorIndex,
|
|
56
|
+
createWorkspaceStore: () => createWorkspaceStore,
|
|
57
|
+
createWorkspaceStoreAsync: () => createWorkspaceStoreAsync,
|
|
55
58
|
deepMergeProfile: () => deepMergeProfile,
|
|
56
59
|
discoverGitRepos: () => discoverGitRepos,
|
|
57
60
|
languageFromPath: () => languageFromPath,
|
|
@@ -59,6 +62,7 @@ __export(index_exports, {
|
|
|
59
62
|
loadConfigFile: () => loadConfigFile,
|
|
60
63
|
mergeIndexerConfig: () => mergeIndexerConfig,
|
|
61
64
|
pickRepoOverride: () => pickRepoOverride,
|
|
65
|
+
sqlJsAdapter: () => sqlJsAdapter,
|
|
62
66
|
stableSymbolId: () => stableSymbolId
|
|
63
67
|
});
|
|
64
68
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -1263,8 +1267,8 @@ function mergeIndexerConfig(target, patch) {
|
|
|
1263
1267
|
}
|
|
1264
1268
|
|
|
1265
1269
|
// src/store/workspaceStore.ts
|
|
1266
|
-
var
|
|
1267
|
-
var
|
|
1270
|
+
var import_node_fs7 = __toESM(require("fs"), 1);
|
|
1271
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
1268
1272
|
|
|
1269
1273
|
// src/store/workspace/unitOfWork.ts
|
|
1270
1274
|
var UnitOfWork = class {
|
|
@@ -1484,15 +1488,35 @@ var RepoLinksRepository = class {
|
|
|
1484
1488
|
};
|
|
1485
1489
|
|
|
1486
1490
|
// src/store/workspace/factory.ts
|
|
1487
|
-
var
|
|
1488
|
-
var
|
|
1491
|
+
var import_node_fs5 = __toESM(require("fs"), 1);
|
|
1492
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
1489
1493
|
|
|
1490
1494
|
// src/store/workspace/db.ts
|
|
1491
1495
|
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
1496
|
+
var import_node_fs4 = __toESM(require("fs"), 1);
|
|
1497
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
1498
|
+
function detectFts5Support(db) {
|
|
1499
|
+
try {
|
|
1500
|
+
const rows = db.prepare(`PRAGMA compile_options`).all();
|
|
1501
|
+
if (rows.some((r) => String(r.compile_options ?? "").includes("ENABLE_FTS5"))) return true;
|
|
1502
|
+
} catch {
|
|
1503
|
+
}
|
|
1504
|
+
try {
|
|
1505
|
+
db.exec(`
|
|
1506
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS __fts5_probe USING fts5(x);
|
|
1507
|
+
DROP TABLE __fts5_probe;
|
|
1508
|
+
`);
|
|
1509
|
+
return true;
|
|
1510
|
+
} catch {
|
|
1511
|
+
return false;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1492
1514
|
var BetterSqlite3Adapter = class {
|
|
1493
1515
|
db;
|
|
1516
|
+
capabilities;
|
|
1494
1517
|
constructor(dbPath) {
|
|
1495
1518
|
this.db = new import_better_sqlite3.default(dbPath);
|
|
1519
|
+
this.capabilities = { supportsFts5: detectFts5Support(this.db) };
|
|
1496
1520
|
}
|
|
1497
1521
|
pragma(sql) {
|
|
1498
1522
|
this.db.pragma(sql);
|
|
@@ -1510,6 +1534,17 @@ var BetterSqlite3Adapter = class {
|
|
|
1510
1534
|
this.db.close();
|
|
1511
1535
|
}
|
|
1512
1536
|
};
|
|
1537
|
+
var betterSqlite3Adapter = {
|
|
1538
|
+
open(dbPath) {
|
|
1539
|
+
import_node_fs4.default.mkdirSync(import_node_path6.default.dirname(dbPath), { recursive: true });
|
|
1540
|
+
const db = new BetterSqlite3Adapter(dbPath);
|
|
1541
|
+
db.pragma("journal_mode = WAL");
|
|
1542
|
+
return db;
|
|
1543
|
+
}
|
|
1544
|
+
};
|
|
1545
|
+
|
|
1546
|
+
// src/store/workspace/fts5.sql
|
|
1547
|
+
var fts5_default = "CREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(\n id UNINDEXED,\n repo_id UNINDEXED,\n repo_root UNINDEXED,\n path,\n language,\n kind,\n text,\n tokenize='unicode61'\n);\n\n";
|
|
1513
1548
|
|
|
1514
1549
|
// src/store/workspace/fts.ts
|
|
1515
1550
|
var NoopFtsStrategy = class {
|
|
@@ -1533,18 +1568,7 @@ var Fts5Strategy = class {
|
|
|
1533
1568
|
enabled = true;
|
|
1534
1569
|
ins = null;
|
|
1535
1570
|
init(db) {
|
|
1536
|
-
db.exec(
|
|
1537
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(
|
|
1538
|
-
id UNINDEXED,
|
|
1539
|
-
repo_id UNINDEXED,
|
|
1540
|
-
repo_root UNINDEXED,
|
|
1541
|
-
path,
|
|
1542
|
-
language,
|
|
1543
|
-
kind,
|
|
1544
|
-
text,
|
|
1545
|
-
tokenize='unicode61'
|
|
1546
|
-
);
|
|
1547
|
-
`);
|
|
1571
|
+
db.exec(fts5_default);
|
|
1548
1572
|
}
|
|
1549
1573
|
clearRepo(repoId) {
|
|
1550
1574
|
this.db.prepare(`DELETE FROM chunks_fts WHERE repo_id = ?`).run(repoId);
|
|
@@ -1628,109 +1652,26 @@ var WorkspaceMigrator = class {
|
|
|
1628
1652
|
}
|
|
1629
1653
|
};
|
|
1630
1654
|
|
|
1655
|
+
// src/store/workspace/baseSchema.sql
|
|
1656
|
+
var baseSchema_default = "CREATE TABLE IF NOT EXISTS meta (\n k TEXT PRIMARY KEY,\n v TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS repos (\n repo_id TEXT PRIMARY KEY,\n repo_root TEXT NOT NULL,\n head_commit TEXT NOT NULL,\n head_branch TEXT NOT NULL,\n updated_at INTEGER NOT NULL\n);\n\nCREATE UNIQUE INDEX IF NOT EXISTS idx_repos_root ON repos(repo_root);\n\nCREATE TABLE IF NOT EXISTS files (\n repo_id TEXT NOT NULL,\n path TEXT NOT NULL,\n hash TEXT NOT NULL,\n mtime INTEGER NOT NULL,\n language TEXT NOT NULL,\n size INTEGER NOT NULL,\n PRIMARY KEY(repo_id, path)\n);\n\nCREATE INDEX IF NOT EXISTS idx_files_repo ON files(repo_id);\n\nCREATE TABLE IF NOT EXISTS chunks (\n id TEXT PRIMARY KEY,\n repo_id TEXT NOT NULL,\n repo_root TEXT NOT NULL,\n path TEXT NOT NULL,\n language TEXT NOT NULL,\n kind TEXT NOT NULL DEFAULT 'chunk',\n start_line INTEGER NOT NULL,\n end_line INTEGER NOT NULL,\n content_hash TEXT NOT NULL,\n tokens INTEGER NOT NULL,\n file_mtime INTEGER NOT NULL,\n text TEXT NOT NULL,\n embedding BLOB NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_chunks_repo_path ON chunks(repo_id, path);\nCREATE INDEX IF NOT EXISTS idx_chunks_kind_repo_path ON chunks(kind, repo_id, path);\n\nCREATE TABLE IF NOT EXISTS edges (\n repo_id TEXT NOT NULL,\n from_path TEXT NOT NULL,\n kind TEXT NOT NULL,\n value TEXT NOT NULL,\n PRIMARY KEY(repo_id, from_path, kind, value)\n);\n\nCREATE INDEX IF NOT EXISTS idx_edges_repo_from ON edges(repo_id, from_path);\n\nCREATE TABLE IF NOT EXISTS symbols (\n id TEXT PRIMARY KEY,\n repo_id TEXT NOT NULL,\n repo_root TEXT NOT NULL,\n path TEXT NOT NULL,\n language TEXT NOT NULL,\n name TEXT NOT NULL,\n kind TEXT NOT NULL,\n start_line INTEGER NOT NULL,\n start_char INTEGER NOT NULL,\n end_line INTEGER NOT NULL,\n end_char INTEGER NOT NULL,\n container_name TEXT NOT NULL DEFAULT '',\n detail TEXT NOT NULL DEFAULT ''\n);\n\nCREATE INDEX IF NOT EXISTS idx_symbols_repo_path ON symbols(repo_id, path);\nCREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name);\n\nCREATE TABLE IF NOT EXISTS symbol_edges (\n repo_id TEXT NOT NULL,\n from_id TEXT NOT NULL,\n to_id TEXT NOT NULL,\n kind TEXT NOT NULL,\n from_path TEXT NOT NULL,\n to_path TEXT NOT NULL,\n PRIMARY KEY(repo_id, from_id, to_id, kind)\n);\n\nCREATE INDEX IF NOT EXISTS idx_symbol_edges_from ON symbol_edges(repo_id, from_id);\nCREATE INDEX IF NOT EXISTS idx_symbol_edges_paths ON symbol_edges(repo_id, from_path);\n\n";
|
|
1657
|
+
|
|
1631
1658
|
// src/store/workspace/factory.ts
|
|
1632
|
-
function createWorkspaceDb(dbPath) {
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
db.pragma("journal_mode = WAL");
|
|
1636
|
-
return db;
|
|
1659
|
+
function createWorkspaceDb(dbPath, opts = {}) {
|
|
1660
|
+
import_node_fs5.default.mkdirSync(import_node_path7.default.dirname(dbPath), { recursive: true });
|
|
1661
|
+
return (opts.db ?? betterSqlite3Adapter).open(dbPath);
|
|
1637
1662
|
}
|
|
1638
1663
|
function createWorkspaceBaseSchema(db) {
|
|
1639
|
-
db.exec(
|
|
1640
|
-
CREATE TABLE IF NOT EXISTS meta (
|
|
1641
|
-
k TEXT PRIMARY KEY,
|
|
1642
|
-
v TEXT NOT NULL
|
|
1643
|
-
);
|
|
1644
|
-
|
|
1645
|
-
CREATE TABLE IF NOT EXISTS repos (
|
|
1646
|
-
repo_id TEXT PRIMARY KEY,
|
|
1647
|
-
repo_root TEXT NOT NULL,
|
|
1648
|
-
head_commit TEXT NOT NULL,
|
|
1649
|
-
head_branch TEXT NOT NULL,
|
|
1650
|
-
updated_at INTEGER NOT NULL
|
|
1651
|
-
);
|
|
1652
|
-
|
|
1653
|
-
CREATE UNIQUE INDEX IF NOT EXISTS idx_repos_root ON repos(repo_root);
|
|
1654
|
-
|
|
1655
|
-
CREATE TABLE IF NOT EXISTS files (
|
|
1656
|
-
repo_id TEXT NOT NULL,
|
|
1657
|
-
path TEXT NOT NULL,
|
|
1658
|
-
hash TEXT NOT NULL,
|
|
1659
|
-
mtime INTEGER NOT NULL,
|
|
1660
|
-
language TEXT NOT NULL,
|
|
1661
|
-
size INTEGER NOT NULL,
|
|
1662
|
-
PRIMARY KEY(repo_id, path)
|
|
1663
|
-
);
|
|
1664
|
-
|
|
1665
|
-
CREATE INDEX IF NOT EXISTS idx_files_repo ON files(repo_id);
|
|
1666
|
-
|
|
1667
|
-
CREATE TABLE IF NOT EXISTS chunks (
|
|
1668
|
-
id TEXT PRIMARY KEY,
|
|
1669
|
-
repo_id TEXT NOT NULL,
|
|
1670
|
-
repo_root TEXT NOT NULL,
|
|
1671
|
-
path TEXT NOT NULL,
|
|
1672
|
-
language TEXT NOT NULL,
|
|
1673
|
-
kind TEXT NOT NULL DEFAULT 'chunk',
|
|
1674
|
-
start_line INTEGER NOT NULL,
|
|
1675
|
-
end_line INTEGER NOT NULL,
|
|
1676
|
-
content_hash TEXT NOT NULL,
|
|
1677
|
-
tokens INTEGER NOT NULL,
|
|
1678
|
-
file_mtime INTEGER NOT NULL,
|
|
1679
|
-
text TEXT NOT NULL,
|
|
1680
|
-
embedding BLOB NOT NULL
|
|
1681
|
-
);
|
|
1682
|
-
|
|
1683
|
-
CREATE INDEX IF NOT EXISTS idx_chunks_repo_path ON chunks(repo_id, path);
|
|
1684
|
-
CREATE INDEX IF NOT EXISTS idx_chunks_kind_repo_path ON chunks(kind, repo_id, path);
|
|
1685
|
-
|
|
1686
|
-
CREATE TABLE IF NOT EXISTS edges (
|
|
1687
|
-
repo_id TEXT NOT NULL,
|
|
1688
|
-
from_path TEXT NOT NULL,
|
|
1689
|
-
kind TEXT NOT NULL,
|
|
1690
|
-
value TEXT NOT NULL,
|
|
1691
|
-
PRIMARY KEY(repo_id, from_path, kind, value)
|
|
1692
|
-
);
|
|
1693
|
-
|
|
1694
|
-
CREATE INDEX IF NOT EXISTS idx_edges_repo_from ON edges(repo_id, from_path);
|
|
1695
|
-
|
|
1696
|
-
CREATE TABLE IF NOT EXISTS symbols (
|
|
1697
|
-
id TEXT PRIMARY KEY,
|
|
1698
|
-
repo_id TEXT NOT NULL,
|
|
1699
|
-
repo_root TEXT NOT NULL,
|
|
1700
|
-
path TEXT NOT NULL,
|
|
1701
|
-
language TEXT NOT NULL,
|
|
1702
|
-
name TEXT NOT NULL,
|
|
1703
|
-
kind TEXT NOT NULL,
|
|
1704
|
-
start_line INTEGER NOT NULL,
|
|
1705
|
-
start_char INTEGER NOT NULL,
|
|
1706
|
-
end_line INTEGER NOT NULL,
|
|
1707
|
-
end_char INTEGER NOT NULL,
|
|
1708
|
-
container_name TEXT NOT NULL DEFAULT '',
|
|
1709
|
-
detail TEXT NOT NULL DEFAULT ''
|
|
1710
|
-
);
|
|
1711
|
-
|
|
1712
|
-
CREATE INDEX IF NOT EXISTS idx_symbols_repo_path ON symbols(repo_id, path);
|
|
1713
|
-
CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name);
|
|
1714
|
-
|
|
1715
|
-
CREATE TABLE IF NOT EXISTS symbol_edges (
|
|
1716
|
-
repo_id TEXT NOT NULL,
|
|
1717
|
-
from_id TEXT NOT NULL,
|
|
1718
|
-
to_id TEXT NOT NULL,
|
|
1719
|
-
kind TEXT NOT NULL,
|
|
1720
|
-
from_path TEXT NOT NULL,
|
|
1721
|
-
to_path TEXT NOT NULL,
|
|
1722
|
-
PRIMARY KEY(repo_id, from_id, to_id, kind)
|
|
1723
|
-
);
|
|
1724
|
-
|
|
1725
|
-
CREATE INDEX IF NOT EXISTS idx_symbol_edges_from ON symbol_edges(repo_id, from_id);
|
|
1726
|
-
CREATE INDEX IF NOT EXISTS idx_symbol_edges_paths ON symbol_edges(repo_id, from_path);
|
|
1727
|
-
`);
|
|
1664
|
+
db.exec(baseSchema_default);
|
|
1728
1665
|
}
|
|
1729
1666
|
function createWorkspaceFts(db, meta, opts = {}) {
|
|
1730
1667
|
if (opts.fts === "off") {
|
|
1731
1668
|
meta.set("fts", "0");
|
|
1732
1669
|
return new NoopFtsStrategy();
|
|
1733
1670
|
}
|
|
1671
|
+
if (!db.capabilities.supportsFts5) {
|
|
1672
|
+
meta.set("fts", "0");
|
|
1673
|
+
return new NoopFtsStrategy();
|
|
1674
|
+
}
|
|
1734
1675
|
try {
|
|
1735
1676
|
const fts = new Fts5Strategy(db);
|
|
1736
1677
|
fts.init(db);
|
|
@@ -1746,17 +1687,153 @@ function migrateWorkspaceDb(db, meta) {
|
|
|
1746
1687
|
migrator.migrateToLatest();
|
|
1747
1688
|
}
|
|
1748
1689
|
|
|
1690
|
+
// src/store/workspace/sqlJsAdapter.ts
|
|
1691
|
+
var import_node_fs6 = __toESM(require("fs"), 1);
|
|
1692
|
+
var import_node_module2 = require("module");
|
|
1693
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
1694
|
+
var import_meta2 = {};
|
|
1695
|
+
function detectFts5Support2(db) {
|
|
1696
|
+
try {
|
|
1697
|
+
db.exec(`
|
|
1698
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS __fts5_probe USING fts5(x);
|
|
1699
|
+
DROP TABLE __fts5_probe;
|
|
1700
|
+
`);
|
|
1701
|
+
return true;
|
|
1702
|
+
} catch {
|
|
1703
|
+
return false;
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
var SqlJsStatement = class {
|
|
1707
|
+
constructor(stmt) {
|
|
1708
|
+
this.stmt = stmt;
|
|
1709
|
+
}
|
|
1710
|
+
run(...args) {
|
|
1711
|
+
this.stmt.run(args);
|
|
1712
|
+
return void 0;
|
|
1713
|
+
}
|
|
1714
|
+
get(...args) {
|
|
1715
|
+
this.stmt.bind(args);
|
|
1716
|
+
const hasRow = this.stmt.step();
|
|
1717
|
+
if (!hasRow) {
|
|
1718
|
+
this.stmt.reset();
|
|
1719
|
+
return void 0;
|
|
1720
|
+
}
|
|
1721
|
+
const row = this.stmt.getAsObject();
|
|
1722
|
+
this.stmt.reset();
|
|
1723
|
+
return row;
|
|
1724
|
+
}
|
|
1725
|
+
all(...args) {
|
|
1726
|
+
this.stmt.bind(args);
|
|
1727
|
+
const rows = [];
|
|
1728
|
+
while (this.stmt.step()) rows.push(this.stmt.getAsObject());
|
|
1729
|
+
this.stmt.reset();
|
|
1730
|
+
return rows;
|
|
1731
|
+
}
|
|
1732
|
+
};
|
|
1733
|
+
var SqlJsDbAdapter = class {
|
|
1734
|
+
constructor(db, dbPath) {
|
|
1735
|
+
this.db = db;
|
|
1736
|
+
this.dbPath = dbPath;
|
|
1737
|
+
this.capabilities = { supportsFts5: detectFts5Support2(db) };
|
|
1738
|
+
}
|
|
1739
|
+
capabilities;
|
|
1740
|
+
pragma(sql) {
|
|
1741
|
+
this.exec(`PRAGMA ${sql}`);
|
|
1742
|
+
}
|
|
1743
|
+
exec(sql) {
|
|
1744
|
+
this.db.exec(sql);
|
|
1745
|
+
}
|
|
1746
|
+
prepare(sql) {
|
|
1747
|
+
return new SqlJsStatement(this.db.prepare(sql));
|
|
1748
|
+
}
|
|
1749
|
+
transaction(fn) {
|
|
1750
|
+
return () => {
|
|
1751
|
+
this.db.exec("BEGIN");
|
|
1752
|
+
try {
|
|
1753
|
+
const out = fn();
|
|
1754
|
+
this.db.exec("COMMIT");
|
|
1755
|
+
return out;
|
|
1756
|
+
} catch (e) {
|
|
1757
|
+
try {
|
|
1758
|
+
this.db.exec("ROLLBACK");
|
|
1759
|
+
} catch {
|
|
1760
|
+
}
|
|
1761
|
+
throw e;
|
|
1762
|
+
}
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
close() {
|
|
1766
|
+
if (this.dbPath && this.dbPath !== ":memory:") {
|
|
1767
|
+
import_node_fs6.default.mkdirSync(import_node_path8.default.dirname(this.dbPath), { recursive: true });
|
|
1768
|
+
const bytes = this.db.export();
|
|
1769
|
+
import_node_fs6.default.writeFileSync(this.dbPath, Buffer.from(bytes));
|
|
1770
|
+
}
|
|
1771
|
+
this.db.close();
|
|
1772
|
+
}
|
|
1773
|
+
};
|
|
1774
|
+
function defaultLocateFile(file) {
|
|
1775
|
+
const spec = `sql.js/dist/${file}`;
|
|
1776
|
+
try {
|
|
1777
|
+
if (typeof require === "function" && typeof require.resolve === "function") {
|
|
1778
|
+
return require.resolve(spec);
|
|
1779
|
+
}
|
|
1780
|
+
} catch {
|
|
1781
|
+
}
|
|
1782
|
+
try {
|
|
1783
|
+
const req = (0, import_node_module2.createRequire)(import_meta2.url);
|
|
1784
|
+
return req.resolve(spec);
|
|
1785
|
+
} catch {
|
|
1786
|
+
return file;
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
async function sqlJsAdapter(opts = {}) {
|
|
1790
|
+
let init;
|
|
1791
|
+
try {
|
|
1792
|
+
const mod = await import("sql.js");
|
|
1793
|
+
init = mod?.default ?? mod;
|
|
1794
|
+
} catch (e) {
|
|
1795
|
+
throw new Error(`sqlJsAdapter requires optional dependency 'sql.js' (install it to use this adapter): ${String(e?.message ?? e)}`);
|
|
1796
|
+
}
|
|
1797
|
+
const SQL = await init({
|
|
1798
|
+
locateFile: opts.locateFile ?? defaultLocateFile,
|
|
1799
|
+
wasmBinary: opts.wasmBinary
|
|
1800
|
+
});
|
|
1801
|
+
return {
|
|
1802
|
+
open(dbPath) {
|
|
1803
|
+
const abs = dbPath === ":memory:" ? ":memory:" : import_node_path8.default.resolve(dbPath);
|
|
1804
|
+
const bytes = abs !== ":memory:" && import_node_fs6.default.existsSync(abs) ? new Uint8Array(import_node_fs6.default.readFileSync(abs)) : void 0;
|
|
1805
|
+
const db = bytes ? new SQL.Database(bytes) : new SQL.Database();
|
|
1806
|
+
return new SqlJsDbAdapter(db, abs);
|
|
1807
|
+
}
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1749
1811
|
// src/store/workspaceStore.ts
|
|
1812
|
+
function createWorkspaceStore(dbPath, opts = {}) {
|
|
1813
|
+
return new WorkspaceStore(dbPath, opts);
|
|
1814
|
+
}
|
|
1815
|
+
async function defaultWorkspaceDbFactory() {
|
|
1816
|
+
try {
|
|
1817
|
+
return await sqlJsAdapter();
|
|
1818
|
+
} catch {
|
|
1819
|
+
return betterSqlite3Adapter;
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
async function createWorkspaceStoreAsync(dbPath, opts = {}) {
|
|
1823
|
+
const dbFactory = opts.db ? await Promise.resolve(opts.db) : await defaultWorkspaceDbFactory();
|
|
1824
|
+
return new WorkspaceStore(dbPath, { ...opts, db: dbFactory });
|
|
1825
|
+
}
|
|
1750
1826
|
var WorkspaceStore = class {
|
|
1751
1827
|
constructor(dbPath, opts = {}) {
|
|
1752
1828
|
this.dbPath = dbPath;
|
|
1753
1829
|
this.opts = opts;
|
|
1754
|
-
this.db = createWorkspaceDb(dbPath);
|
|
1830
|
+
this.db = createWorkspaceDb(dbPath, { db: opts.db });
|
|
1755
1831
|
this.uow = new UnitOfWork(this.db);
|
|
1756
1832
|
createWorkspaceBaseSchema(this.db);
|
|
1757
1833
|
this.meta = new MetaRepository(this.db);
|
|
1758
1834
|
migrateWorkspaceDb(this.db, this.meta);
|
|
1759
1835
|
const fts = createWorkspaceFts(this.db, this.meta, opts);
|
|
1836
|
+
this.ftsEnabledInternal = fts.enabled;
|
|
1760
1837
|
this.repoHeads = new RepoHeadsRepository(this.db);
|
|
1761
1838
|
this.files = new FilesRepository(this.db);
|
|
1762
1839
|
this.edges = new EdgesRepository(this.db);
|
|
@@ -1766,6 +1843,7 @@ var WorkspaceStore = class {
|
|
|
1766
1843
|
}
|
|
1767
1844
|
db;
|
|
1768
1845
|
uow;
|
|
1846
|
+
ftsEnabledInternal;
|
|
1769
1847
|
meta;
|
|
1770
1848
|
repoHeads;
|
|
1771
1849
|
files;
|
|
@@ -1774,6 +1852,9 @@ var WorkspaceStore = class {
|
|
|
1774
1852
|
symbols;
|
|
1775
1853
|
chunks;
|
|
1776
1854
|
opts;
|
|
1855
|
+
get ftsEnabled() {
|
|
1856
|
+
return this.ftsEnabledInternal;
|
|
1857
|
+
}
|
|
1777
1858
|
setMeta(k, v) {
|
|
1778
1859
|
this.meta.set(k, v);
|
|
1779
1860
|
}
|
|
@@ -1857,9 +1938,9 @@ var WorkspaceStore = class {
|
|
|
1857
1938
|
* The chunk boundaries are approximate; the stored row includes start/end line.
|
|
1858
1939
|
*/
|
|
1859
1940
|
getChunkTextFallback(row) {
|
|
1860
|
-
const abs =
|
|
1941
|
+
const abs = import_node_path9.default.join(row.repo_root, row.path.split("/").join(import_node_path9.default.sep));
|
|
1861
1942
|
try {
|
|
1862
|
-
const raw =
|
|
1943
|
+
const raw = import_node_fs7.default.readFileSync(abs, "utf8");
|
|
1863
1944
|
const lines = raw.split(/\r?\n/);
|
|
1864
1945
|
const start = Math.max(1, row.start_line);
|
|
1865
1946
|
const end = Math.max(start, row.end_line);
|
|
@@ -1886,17 +1967,17 @@ range:${r.startLine}:${r.startCharacter}-${r.endLine}:${r.endCharacter}`;
|
|
|
1886
1967
|
}
|
|
1887
1968
|
|
|
1888
1969
|
// src/symbolGraph/vscodeProvider.ts
|
|
1889
|
-
var
|
|
1890
|
-
var
|
|
1970
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
1971
|
+
var import_node_module3 = require("module");
|
|
1891
1972
|
|
|
1892
1973
|
// src/symbolGraph/strategies.ts
|
|
1893
|
-
var
|
|
1974
|
+
var import_node_path10 = __toESM(require("path"), 1);
|
|
1894
1975
|
function toPosixRel(repoRoot, absPath) {
|
|
1895
|
-
const abs =
|
|
1896
|
-
const root =
|
|
1897
|
-
const rel =
|
|
1898
|
-
if (!rel || rel.startsWith("..") ||
|
|
1899
|
-
return rel.split(
|
|
1976
|
+
const abs = import_node_path10.default.resolve(absPath);
|
|
1977
|
+
const root = import_node_path10.default.resolve(repoRoot);
|
|
1978
|
+
const rel = import_node_path10.default.relative(root, abs);
|
|
1979
|
+
if (!rel || rel.startsWith("..") || import_node_path10.default.isAbsolute(rel)) return null;
|
|
1980
|
+
return rel.split(import_node_path10.default.sep).join("/");
|
|
1900
1981
|
}
|
|
1901
1982
|
function fromLspRange(r) {
|
|
1902
1983
|
return {
|
|
@@ -1963,7 +2044,7 @@ var SymbolGraphIndexer = class {
|
|
|
1963
2044
|
}
|
|
1964
2045
|
cache = /* @__PURE__ */ new Map();
|
|
1965
2046
|
async indexDocument(input, cancel = {}) {
|
|
1966
|
-
const doc = await this.lsp.openTextDocument(
|
|
2047
|
+
const doc = await this.lsp.openTextDocument(import_node_path10.default.join(input.repoRoot, input.path.split("/").join(import_node_path10.default.sep)));
|
|
1967
2048
|
const key = cacheKeyFor(doc, input);
|
|
1968
2049
|
const cached2 = this.cache.get(key);
|
|
1969
2050
|
if (cached2) {
|
|
@@ -1997,7 +2078,7 @@ var SymbolGraphIndexer = class {
|
|
|
1997
2078
|
* Intended for staged indexing and on-demand expansion during retrieval.
|
|
1998
2079
|
*/
|
|
1999
2080
|
async expandDocumentEdges(input, cancel = {}) {
|
|
2000
|
-
const doc = await this.lsp.openTextDocument(
|
|
2081
|
+
const doc = await this.lsp.openTextDocument(import_node_path10.default.join(input.repoRoot, input.path.split("/").join(import_node_path10.default.sep)));
|
|
2001
2082
|
const key = cacheKeyFor(doc, input);
|
|
2002
2083
|
const cached2 = this.cache.get(key);
|
|
2003
2084
|
if (!cached2) {
|
|
@@ -2117,12 +2198,12 @@ function findSymbolAt(syms, pos) {
|
|
|
2117
2198
|
}
|
|
2118
2199
|
|
|
2119
2200
|
// src/symbolGraph/vscodeProvider.ts
|
|
2120
|
-
var
|
|
2201
|
+
var import_meta4 = {};
|
|
2121
2202
|
function fromPosixPath2(posixRelPath) {
|
|
2122
|
-
return posixRelPath.split("/").join(
|
|
2203
|
+
return posixRelPath.split("/").join(import_node_path11.default.sep);
|
|
2123
2204
|
}
|
|
2124
2205
|
function toPosixPath(p) {
|
|
2125
|
-
return p.split(
|
|
2206
|
+
return p.split(import_node_path11.default.sep).join("/");
|
|
2126
2207
|
}
|
|
2127
2208
|
function kindFromVscode(vscode, k) {
|
|
2128
2209
|
const SK = vscode?.SymbolKind;
|
|
@@ -2183,7 +2264,7 @@ function toLspSymbols(vscode, res) {
|
|
|
2183
2264
|
return res.map(visit);
|
|
2184
2265
|
}
|
|
2185
2266
|
function toIndexInput(doc, input) {
|
|
2186
|
-
const rel = toPosixPath(
|
|
2267
|
+
const rel = toPosixPath(import_node_path11.default.relative(import_node_path11.default.resolve(input.repoRoot), import_node_path11.default.resolve(doc.fsPath)));
|
|
2187
2268
|
return { ...input, path: rel || input.path };
|
|
2188
2269
|
}
|
|
2189
2270
|
function makeVscodeFacade(vscode) {
|
|
@@ -2225,7 +2306,7 @@ function makeVscodeFacade(vscode) {
|
|
|
2225
2306
|
async function createVSCodeSymbolGraphProvider(opts) {
|
|
2226
2307
|
let vscode;
|
|
2227
2308
|
try {
|
|
2228
|
-
const require2 = (0,
|
|
2309
|
+
const require2 = (0, import_node_module3.createRequire)(import_meta4.url);
|
|
2229
2310
|
vscode = require2("vscode");
|
|
2230
2311
|
} catch {
|
|
2231
2312
|
return null;
|
|
@@ -2239,12 +2320,12 @@ async function createVSCodeSymbolGraphProvider(opts) {
|
|
|
2239
2320
|
id: "vscode.symbolGraph",
|
|
2240
2321
|
supports: (language) => languages.has(language),
|
|
2241
2322
|
async indexDocument(input) {
|
|
2242
|
-
const absPath =
|
|
2323
|
+
const absPath = import_node_path11.default.join(input.repoRoot, fromPosixPath2(input.path));
|
|
2243
2324
|
const doc = await lsp.openTextDocument(absPath);
|
|
2244
2325
|
return await indexer.indexDocument(toIndexInput(doc, input), { signal: input.signal });
|
|
2245
2326
|
},
|
|
2246
2327
|
async expandDocumentEdges(input, o) {
|
|
2247
|
-
const absPath =
|
|
2328
|
+
const absPath = import_node_path11.default.join(input.repoRoot, fromPosixPath2(input.path));
|
|
2248
2329
|
const doc = await lsp.openTextDocument(absPath);
|
|
2249
2330
|
return await indexer.expandDocumentEdges(toIndexInput(doc, input), { signal: o?.signal });
|
|
2250
2331
|
}
|
|
@@ -2252,8 +2333,8 @@ async function createVSCodeSymbolGraphProvider(opts) {
|
|
|
2252
2333
|
}
|
|
2253
2334
|
|
|
2254
2335
|
// src/graph/neo4j.ts
|
|
2255
|
-
var
|
|
2256
|
-
var
|
|
2336
|
+
var import_node_module4 = require("module");
|
|
2337
|
+
var import_meta5 = {};
|
|
2257
2338
|
async function runSession(driver, database, fn) {
|
|
2258
2339
|
const session = driver.session(database ? { database } : void 0);
|
|
2259
2340
|
try {
|
|
@@ -2694,7 +2775,7 @@ var Neo4jGraphStore = class {
|
|
|
2694
2775
|
};
|
|
2695
2776
|
async function createNeo4jGraphStore(cfg) {
|
|
2696
2777
|
try {
|
|
2697
|
-
const require2 = (0,
|
|
2778
|
+
const require2 = (0, import_node_module4.createRequire)(import_meta5.url);
|
|
2698
2779
|
const neo4j = require2("neo4j-driver");
|
|
2699
2780
|
const driver = neo4j.driver(cfg.uri, neo4j.auth.basic(cfg.user, cfg.password));
|
|
2700
2781
|
const store = new Neo4jGraphStore(driver, cfg);
|
|
@@ -2737,8 +2818,8 @@ function createAnnIndex(config) {
|
|
|
2737
2818
|
}
|
|
2738
2819
|
|
|
2739
2820
|
// src/indexer/repoIndexer.ts
|
|
2740
|
-
var
|
|
2741
|
-
var
|
|
2821
|
+
var import_node_fs14 = __toESM(require("fs"), 1);
|
|
2822
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
2742
2823
|
var import_p_limit2 = __toESM(require("p-limit"), 1);
|
|
2743
2824
|
|
|
2744
2825
|
// src/git.ts
|
|
@@ -2774,28 +2855,28 @@ async function listChangedFiles(repoRoot, baseRef = "HEAD~1") {
|
|
|
2774
2855
|
}
|
|
2775
2856
|
|
|
2776
2857
|
// src/ignore.ts
|
|
2777
|
-
var
|
|
2778
|
-
var
|
|
2858
|
+
var import_node_fs8 = __toESM(require("fs"), 1);
|
|
2859
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
2779
2860
|
var import_ignore = __toESM(require("ignore"), 1);
|
|
2780
2861
|
function loadExtraIgnore(repoRoot, ignoreFiles) {
|
|
2781
2862
|
const ig = (0, import_ignore.default)();
|
|
2782
2863
|
for (const name of ignoreFiles) {
|
|
2783
|
-
const p =
|
|
2784
|
-
if (!
|
|
2785
|
-
const raw =
|
|
2864
|
+
const p = import_node_path12.default.join(repoRoot, name);
|
|
2865
|
+
if (!import_node_fs8.default.existsSync(p)) continue;
|
|
2866
|
+
const raw = import_node_fs8.default.readFileSync(p, "utf8");
|
|
2786
2867
|
ig.add(raw.split(/\r?\n/));
|
|
2787
2868
|
}
|
|
2788
2869
|
return (posixRelPath) => ig.ignores(posixRelPath);
|
|
2789
2870
|
}
|
|
2790
2871
|
|
|
2791
2872
|
// src/store/embeddingCache.ts
|
|
2792
|
-
var
|
|
2793
|
-
var
|
|
2873
|
+
var import_node_fs9 = __toESM(require("fs"), 1);
|
|
2874
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
2794
2875
|
var import_better_sqlite32 = __toESM(require("better-sqlite3"), 1);
|
|
2795
2876
|
var EmbeddingCache = class {
|
|
2796
2877
|
db;
|
|
2797
2878
|
constructor(cacheFilePath) {
|
|
2798
|
-
|
|
2879
|
+
import_node_fs9.default.mkdirSync(import_node_path13.default.dirname(cacheFilePath), { recursive: true });
|
|
2799
2880
|
this.db = new import_better_sqlite32.default(cacheFilePath);
|
|
2800
2881
|
this.db.pragma("journal_mode = WAL");
|
|
2801
2882
|
this.db.exec(`
|
|
@@ -2831,13 +2912,13 @@ var EmbeddingCache = class {
|
|
|
2831
2912
|
};
|
|
2832
2913
|
|
|
2833
2914
|
// src/store/repoStore.ts
|
|
2834
|
-
var
|
|
2835
|
-
var
|
|
2915
|
+
var import_node_fs10 = __toESM(require("fs"), 1);
|
|
2916
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
2836
2917
|
var import_better_sqlite33 = __toESM(require("better-sqlite3"), 1);
|
|
2837
2918
|
var RepoStore = class {
|
|
2838
2919
|
db;
|
|
2839
2920
|
constructor(dbPath) {
|
|
2840
|
-
|
|
2921
|
+
import_node_fs10.default.mkdirSync(import_node_path14.default.dirname(dbPath), { recursive: true });
|
|
2841
2922
|
this.db = new import_better_sqlite33.default(dbPath);
|
|
2842
2923
|
this.db.pragma("journal_mode = WAL");
|
|
2843
2924
|
this.db.exec(`
|
|
@@ -3132,10 +3213,10 @@ function resolveIndexerConfig(config = {}) {
|
|
|
3132
3213
|
}
|
|
3133
3214
|
|
|
3134
3215
|
// src/indexer/repoIndexer/utils.ts
|
|
3135
|
-
var
|
|
3136
|
-
var
|
|
3216
|
+
var import_node_fs11 = __toESM(require("fs"), 1);
|
|
3217
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
3137
3218
|
function repoIdFromRoot(repoRoot) {
|
|
3138
|
-
return sha256Hex(
|
|
3219
|
+
return sha256Hex(import_node_path15.default.resolve(repoRoot)).slice(0, 16);
|
|
3139
3220
|
}
|
|
3140
3221
|
function looksBinary(buf) {
|
|
3141
3222
|
let nul = 0;
|
|
@@ -3222,8 +3303,8 @@ var VectorManager = class {
|
|
|
3222
3303
|
};
|
|
3223
3304
|
|
|
3224
3305
|
// src/indexer/repoIndexer/fileIndexer.ts
|
|
3225
|
-
var
|
|
3226
|
-
var
|
|
3306
|
+
var import_node_fs12 = __toESM(require("fs"), 1);
|
|
3307
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
3227
3308
|
|
|
3228
3309
|
// src/relations.ts
|
|
3229
3310
|
function extractTsRelations(virtualFileName, sourceText) {
|
|
@@ -3462,10 +3543,10 @@ var RepoFileIndexer = class {
|
|
|
3462
3543
|
});
|
|
3463
3544
|
};
|
|
3464
3545
|
const readStartedAt = Date.now();
|
|
3465
|
-
const abs =
|
|
3546
|
+
const abs = import_node_path16.default.join(this.repoRoot, fromPosixPath(posixRelPath));
|
|
3466
3547
|
let stat;
|
|
3467
3548
|
try {
|
|
3468
|
-
stat =
|
|
3549
|
+
stat = import_node_fs12.default.statSync(abs);
|
|
3469
3550
|
} catch {
|
|
3470
3551
|
this.emit({ type: "repo/index/file/skip", repoRoot: this.repoRoot, path: posixRelPath, reason: "missing" });
|
|
3471
3552
|
return;
|
|
@@ -3478,7 +3559,7 @@ var RepoFileIndexer = class {
|
|
|
3478
3559
|
this.emit({ type: "repo/index/file/skip", repoRoot: this.repoRoot, path: posixRelPath, reason: "too_large" });
|
|
3479
3560
|
return;
|
|
3480
3561
|
}
|
|
3481
|
-
const buf =
|
|
3562
|
+
const buf = import_node_fs12.default.readFileSync(abs);
|
|
3482
3563
|
if (looksBinary(buf)) {
|
|
3483
3564
|
this.emit({ type: "repo/index/file/skip", repoRoot: this.repoRoot, path: posixRelPath, reason: "binary" });
|
|
3484
3565
|
return;
|
|
@@ -3564,7 +3645,7 @@ var RepoFileIndexer = class {
|
|
|
3564
3645
|
continue;
|
|
3565
3646
|
}
|
|
3566
3647
|
embedTexts.push(
|
|
3567
|
-
`repo:${
|
|
3648
|
+
`repo:${import_node_path16.default.basename(this.repoRoot)}
|
|
3568
3649
|
path:${posixRelPath}
|
|
3569
3650
|
language:${language}
|
|
3570
3651
|
kind:${ch.kind}
|
|
@@ -3758,8 +3839,8 @@ ${ch.text}`
|
|
|
3758
3839
|
};
|
|
3759
3840
|
|
|
3760
3841
|
// src/indexer/repoIndexer/retriever.ts
|
|
3761
|
-
var
|
|
3762
|
-
var
|
|
3842
|
+
var import_node_fs13 = __toESM(require("fs"), 1);
|
|
3843
|
+
var import_node_path17 = __toESM(require("path"), 1);
|
|
3763
3844
|
|
|
3764
3845
|
// src/retrieval/fts.ts
|
|
3765
3846
|
function ftsQueryFromText(input) {
|
|
@@ -3850,9 +3931,9 @@ var RepoRetriever = class {
|
|
|
3850
3931
|
return rows.map((r) => ({ id: r.id, score: bm25ToScore01(r.bm25) }));
|
|
3851
3932
|
}
|
|
3852
3933
|
readChunkTextFallback(row) {
|
|
3853
|
-
const abs =
|
|
3934
|
+
const abs = import_node_path17.default.join(this.repoRoot, fromPosixPath(row.path));
|
|
3854
3935
|
try {
|
|
3855
|
-
const raw =
|
|
3936
|
+
const raw = import_node_fs13.default.readFileSync(abs, "utf8");
|
|
3856
3937
|
const lines = raw.split(/\r?\n/);
|
|
3857
3938
|
const start = Math.max(1, row.start_line);
|
|
3858
3939
|
const end = Math.max(start, row.end_line);
|
|
@@ -3944,7 +4025,7 @@ var RepoRetriever = class {
|
|
|
3944
4025
|
`${spec}/index.tsx`,
|
|
3945
4026
|
`${spec}/index.js`,
|
|
3946
4027
|
`${spec}/index.jsx`
|
|
3947
|
-
].map((s) =>
|
|
4028
|
+
].map((s) => import_node_path17.default.posix.normalize(import_node_path17.default.posix.join(import_node_path17.default.posix.dirname(row.path), s)));
|
|
3948
4029
|
for (const c of candidates) {
|
|
3949
4030
|
const syn = store.listChunksForFile(c, "synopsis")[0];
|
|
3950
4031
|
if (syn) {
|
|
@@ -3971,7 +4052,7 @@ var RepoRetriever = class {
|
|
|
3971
4052
|
};
|
|
3972
4053
|
|
|
3973
4054
|
// src/indexer/repoIndexer/watcher.ts
|
|
3974
|
-
var
|
|
4055
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
3975
4056
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
3976
4057
|
var RepoWatcher = class {
|
|
3977
4058
|
constructor(repoRoot, debounceMs, ignored, onHeadChanged, onFileChanged, onFileAdded, onFileDeleted, onWatchEvent) {
|
|
@@ -3992,14 +4073,14 @@ var RepoWatcher = class {
|
|
|
3992
4073
|
if (timer) clearTimeout(timer);
|
|
3993
4074
|
timer = setTimeout(fn, this.debounceMs);
|
|
3994
4075
|
};
|
|
3995
|
-
const headPath =
|
|
4076
|
+
const headPath = import_node_path18.default.join(this.repoRoot, ".git", "HEAD");
|
|
3996
4077
|
this.watcher = import_chokidar.default.watch([this.repoRoot, headPath], {
|
|
3997
4078
|
ignoreInitial: true,
|
|
3998
4079
|
ignored: this.ignored
|
|
3999
4080
|
});
|
|
4000
4081
|
this.watcher.on("change", (p) => {
|
|
4001
|
-
const rel =
|
|
4002
|
-
const posix = rel.split(
|
|
4082
|
+
const rel = import_node_path18.default.relative(this.repoRoot, p);
|
|
4083
|
+
const posix = rel.split(import_node_path18.default.sep).join("/");
|
|
4003
4084
|
if (posix === ".git/HEAD") {
|
|
4004
4085
|
this.onWatchEvent?.("head", posix);
|
|
4005
4086
|
schedule(() => this.onHeadChanged());
|
|
@@ -4009,14 +4090,14 @@ var RepoWatcher = class {
|
|
|
4009
4090
|
schedule(() => this.onFileChanged(posix));
|
|
4010
4091
|
});
|
|
4011
4092
|
this.watcher.on("add", (p) => {
|
|
4012
|
-
const rel =
|
|
4013
|
-
const posix = rel.split(
|
|
4093
|
+
const rel = import_node_path18.default.relative(this.repoRoot, p);
|
|
4094
|
+
const posix = rel.split(import_node_path18.default.sep).join("/");
|
|
4014
4095
|
this.onWatchEvent?.("add", posix);
|
|
4015
4096
|
schedule(() => this.onFileAdded(posix));
|
|
4016
4097
|
});
|
|
4017
4098
|
this.watcher.on("unlink", (p) => {
|
|
4018
|
-
const rel =
|
|
4019
|
-
const posix = rel.split(
|
|
4099
|
+
const rel = import_node_path18.default.relative(this.repoRoot, p);
|
|
4100
|
+
const posix = rel.split(import_node_path18.default.sep).join("/");
|
|
4020
4101
|
this.onWatchEvent?.("unlink", posix);
|
|
4021
4102
|
schedule(() => this.onFileDeleted(posix));
|
|
4022
4103
|
});
|
|
@@ -4031,7 +4112,7 @@ var RepoWatcher = class {
|
|
|
4031
4112
|
var RepoIndexer = class {
|
|
4032
4113
|
constructor(repoRoot, embedder, config = {}, workspaceStore, graphStore) {
|
|
4033
4114
|
this.embedder = embedder;
|
|
4034
|
-
this.repoRoot =
|
|
4115
|
+
this.repoRoot = import_node_path19.default.resolve(repoRoot);
|
|
4035
4116
|
this.repoId = repoIdFromRoot(this.repoRoot);
|
|
4036
4117
|
this.rawConfig = { ...config };
|
|
4037
4118
|
if (!this.rawConfig.cacheDir) this.rawConfig.cacheDir = defaultCacheDir();
|
|
@@ -4041,7 +4122,7 @@ var RepoIndexer = class {
|
|
|
4041
4122
|
this.workspaceStore = workspaceStore ?? null;
|
|
4042
4123
|
this.graphStore = graphStore ?? null;
|
|
4043
4124
|
this.ann = createAnnIndex(this.rawConfig.ann);
|
|
4044
|
-
this.embeddingCache = new EmbeddingCache(
|
|
4125
|
+
this.embeddingCache = new EmbeddingCache(import_node_path19.default.join(this.config.cacheDir, "embedding-cache.sqlite"));
|
|
4045
4126
|
this.vector = new VectorManager(
|
|
4046
4127
|
this.config.vector,
|
|
4047
4128
|
this.vectorMetric(),
|
|
@@ -4105,7 +4186,7 @@ var RepoIndexer = class {
|
|
|
4105
4186
|
return this.store;
|
|
4106
4187
|
}
|
|
4107
4188
|
dbPathForCommit(commit) {
|
|
4108
|
-
return
|
|
4189
|
+
return import_node_path19.default.join(this.config.cacheDir, "index", this.repoId, `${commit}.sqlite`);
|
|
4109
4190
|
}
|
|
4110
4191
|
vectorMetric() {
|
|
4111
4192
|
return this.config.vector.metric ?? "cosine";
|
|
@@ -4232,8 +4313,8 @@ var RepoIndexer = class {
|
|
|
4232
4313
|
return this.serial(async () => {
|
|
4233
4314
|
await this.openForCurrentHead();
|
|
4234
4315
|
if (!this.store || !this.fileIndexer) throw new Error("RepoStore not initialised");
|
|
4235
|
-
const abs =
|
|
4236
|
-
if (!
|
|
4316
|
+
const abs = import_node_path19.default.join(this.repoRoot, posixRelPath.split("/").join(import_node_path19.default.sep));
|
|
4317
|
+
if (!import_node_fs14.default.existsSync(abs)) {
|
|
4237
4318
|
await this.deleteFile(posixRelPath);
|
|
4238
4319
|
return;
|
|
4239
4320
|
}
|
|
@@ -4293,10 +4374,10 @@ var RepoIndexer = class {
|
|
|
4293
4374
|
if (opts?.signal?.aborted) return;
|
|
4294
4375
|
const startedAt = Date.now();
|
|
4295
4376
|
this.emitProgress({ type: "repo/symbolGraph/expand/start", repoRoot: this.repoRoot, path: p });
|
|
4296
|
-
const abs =
|
|
4377
|
+
const abs = import_node_path19.default.join(this.repoRoot, p.split("/").join(import_node_path19.default.sep));
|
|
4297
4378
|
let text = "";
|
|
4298
4379
|
try {
|
|
4299
|
-
text =
|
|
4380
|
+
text = import_node_fs14.default.readFileSync(abs, "utf8");
|
|
4300
4381
|
} catch {
|
|
4301
4382
|
this.emitProgress({ type: "repo/symbolGraph/expand/done", repoRoot: this.repoRoot, path: p, edges: 0, ms: Date.now() - startedAt });
|
|
4302
4383
|
continue;
|
|
@@ -4337,9 +4418,9 @@ var RepoIndexer = class {
|
|
|
4337
4418
|
await this.openForCurrentHead();
|
|
4338
4419
|
this.emitProgress({ type: "repo/watch/start", repoRoot: this.repoRoot });
|
|
4339
4420
|
const ignored = (p) => {
|
|
4340
|
-
const rel =
|
|
4421
|
+
const rel = import_node_path19.default.relative(this.repoRoot, p);
|
|
4341
4422
|
if (!rel) return false;
|
|
4342
|
-
const posix = rel.split(
|
|
4423
|
+
const posix = rel.split(import_node_path19.default.sep).join("/");
|
|
4343
4424
|
if (posix.startsWith(".git/")) return true;
|
|
4344
4425
|
if (posix.includes("node_modules/")) return true;
|
|
4345
4426
|
if (posix.includes("/.cache/")) return true;
|
|
@@ -4380,14 +4461,14 @@ var RepoIndexer = class {
|
|
|
4380
4461
|
};
|
|
4381
4462
|
|
|
4382
4463
|
// src/indexer/workspaceIndexer.ts
|
|
4383
|
-
var
|
|
4464
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
4384
4465
|
|
|
4385
4466
|
// src/indexer/workspaceLinker.ts
|
|
4386
|
-
var
|
|
4387
|
-
var
|
|
4467
|
+
var import_node_fs15 = __toESM(require("fs"), 1);
|
|
4468
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
4388
4469
|
function readText(absPath) {
|
|
4389
4470
|
try {
|
|
4390
|
-
return
|
|
4471
|
+
return import_node_fs15.default.readFileSync(absPath, "utf8");
|
|
4391
4472
|
} catch {
|
|
4392
4473
|
return null;
|
|
4393
4474
|
}
|
|
@@ -4417,7 +4498,7 @@ var NestedRepoLinkStrategy = class {
|
|
|
4417
4498
|
for (const child of sorted) {
|
|
4418
4499
|
for (const parent of sorted) {
|
|
4419
4500
|
if (child.repoId === parent.repoId) continue;
|
|
4420
|
-
if (child.absRoot.startsWith(parent.absRoot +
|
|
4501
|
+
if (child.absRoot.startsWith(parent.absRoot + import_node_path20.default.sep)) {
|
|
4421
4502
|
out.push({
|
|
4422
4503
|
fromRepoId: child.repoId,
|
|
4423
4504
|
toRepoId: parent.repoId,
|
|
@@ -4437,7 +4518,7 @@ var NpmDependencyLinkStrategy = class {
|
|
|
4437
4518
|
const out = [];
|
|
4438
4519
|
const depSections = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
|
|
4439
4520
|
for (const r of ctx.repos) {
|
|
4440
|
-
const pkg = readJson(
|
|
4521
|
+
const pkg = readJson(import_node_path20.default.join(r.absRoot, "package.json"));
|
|
4441
4522
|
if (!pkg) continue;
|
|
4442
4523
|
for (const sec of depSections) {
|
|
4443
4524
|
const deps = pkg?.[sec];
|
|
@@ -4455,13 +4536,13 @@ var NpmDependencyLinkStrategy = class {
|
|
|
4455
4536
|
}
|
|
4456
4537
|
};
|
|
4457
4538
|
function parseGoModule(absRepoRoot) {
|
|
4458
|
-
const raw = readText(
|
|
4539
|
+
const raw = readText(import_node_path20.default.join(absRepoRoot, "go.mod"));
|
|
4459
4540
|
if (!raw) return null;
|
|
4460
4541
|
const m = raw.match(/^\s*module\s+(.+)\s*$/m);
|
|
4461
4542
|
return m ? String(m[1]).trim() : null;
|
|
4462
4543
|
}
|
|
4463
4544
|
function parseGoRequires(absRepoRoot) {
|
|
4464
|
-
const raw = readText(
|
|
4545
|
+
const raw = readText(import_node_path20.default.join(absRepoRoot, "go.mod"));
|
|
4465
4546
|
if (!raw) return [];
|
|
4466
4547
|
const out = [];
|
|
4467
4548
|
for (const line of raw.split(/\r?\n/)) {
|
|
@@ -4501,13 +4582,13 @@ function walkFiles(root, opts, onFile) {
|
|
|
4501
4582
|
if (depth > maxDepth) return;
|
|
4502
4583
|
let ents = [];
|
|
4503
4584
|
try {
|
|
4504
|
-
ents =
|
|
4585
|
+
ents = import_node_fs15.default.readdirSync(dir, { withFileTypes: true });
|
|
4505
4586
|
} catch {
|
|
4506
4587
|
return;
|
|
4507
4588
|
}
|
|
4508
4589
|
for (const e of ents) {
|
|
4509
4590
|
if (seen >= maxFiles) return;
|
|
4510
|
-
const abs =
|
|
4591
|
+
const abs = import_node_path20.default.join(dir, e.name);
|
|
4511
4592
|
if (opts.shouldVisit && !opts.shouldVisit(abs, e)) continue;
|
|
4512
4593
|
if (e.isDirectory()) {
|
|
4513
4594
|
if (isSkippableDir(e.name)) continue;
|
|
@@ -4531,7 +4612,7 @@ function collectVsCodeLanguagesForRepo(absRepoRoot) {
|
|
|
4531
4612
|
shouldVisit: (_abs, dirent) => !(dirent.isDirectory() && isSkippableDir(dirent.name))
|
|
4532
4613
|
},
|
|
4533
4614
|
(absPath) => {
|
|
4534
|
-
if (
|
|
4615
|
+
if (import_node_path20.default.basename(absPath) !== "package.json") return;
|
|
4535
4616
|
const pkg = readJson(absPath);
|
|
4536
4617
|
const langs = pkg?.contributes?.languages;
|
|
4537
4618
|
if (!Array.isArray(langs)) return;
|
|
@@ -4560,7 +4641,7 @@ function repoUsedExtensions(absRepoRoot, exts) {
|
|
|
4560
4641
|
shouldVisit: (_abs, dirent) => !(dirent.isDirectory() && isSkippableDir(dirent.name))
|
|
4561
4642
|
},
|
|
4562
4643
|
(absPath) => {
|
|
4563
|
-
const ext =
|
|
4644
|
+
const ext = import_node_path20.default.extname(absPath).toLowerCase();
|
|
4564
4645
|
if (!ext) return;
|
|
4565
4646
|
if (exts.has(ext)) used.add(ext);
|
|
4566
4647
|
}
|
|
@@ -4632,12 +4713,12 @@ var WorkspaceLinker = class _WorkspaceLinker {
|
|
|
4632
4713
|
const repos = repoRoots.map((repoRoot) => ({
|
|
4633
4714
|
repoRoot,
|
|
4634
4715
|
repoId: repoIdFromRoot(repoRoot),
|
|
4635
|
-
absRoot:
|
|
4716
|
+
absRoot: import_node_path20.default.resolve(repoRoot)
|
|
4636
4717
|
}));
|
|
4637
4718
|
const npmNameToRepoId = /* @__PURE__ */ new Map();
|
|
4638
4719
|
const goModuleToRepoId = /* @__PURE__ */ new Map();
|
|
4639
4720
|
for (const r of repos) {
|
|
4640
|
-
const pkg = readJson(
|
|
4721
|
+
const pkg = readJson(import_node_path20.default.join(r.absRoot, "package.json"));
|
|
4641
4722
|
const name = typeof pkg?.name === "string" ? pkg.name : null;
|
|
4642
4723
|
if (name) npmNameToRepoId.set(name, r.repoId);
|
|
4643
4724
|
const mod = parseGoModule(r.absRoot);
|
|
@@ -4681,13 +4762,205 @@ async function linkWorkspaceRepos(args) {
|
|
|
4681
4762
|
return { repos: ctx.repos, links };
|
|
4682
4763
|
}
|
|
4683
4764
|
|
|
4684
|
-
// src/indexer/
|
|
4765
|
+
// src/indexer/workspaceRetrieveCandidates.ts
|
|
4766
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
4767
|
+
function resolveWorkspaceProfile(config, opts) {
|
|
4768
|
+
const name = opts?.profile ?? "search";
|
|
4769
|
+
const base = DEFAULT_PROFILES[name] ?? DEFAULT_PROFILES.search;
|
|
4770
|
+
const configPatch = config.profiles?.[name] ?? {};
|
|
4771
|
+
const merged1 = deepMergeProfile(base, configPatch);
|
|
4772
|
+
const merged2 = deepMergeProfile(merged1, opts?.profileOverrides);
|
|
4773
|
+
const w = merged2.weights;
|
|
4774
|
+
const sum = Math.max(1e-6, w.vector + w.lexical + w.recency);
|
|
4775
|
+
merged2.weights = { vector: w.vector / sum, lexical: w.lexical / sum, recency: w.recency / sum };
|
|
4776
|
+
return merged2;
|
|
4777
|
+
}
|
|
4685
4778
|
function halfLifeDaysForProfile(profileName) {
|
|
4686
4779
|
if (profileName === "rca") return 7;
|
|
4687
4780
|
if (profileName === "review") return 14;
|
|
4688
4781
|
if (profileName === "refactor") return 21;
|
|
4689
4782
|
return 30;
|
|
4690
4783
|
}
|
|
4784
|
+
function buildWorkspaceLexByRepoRoot(args) {
|
|
4785
|
+
const { workspaceStore, repos, query, lexicalK, repoFilters } = args;
|
|
4786
|
+
const ftq = ftsQueryFromText(query);
|
|
4787
|
+
if (!ftq) return { lexByRepoRoot: /* @__PURE__ */ new Map(), count: 0 };
|
|
4788
|
+
const allowRoots = repoFilters ? new Set(repoFilters.map((r) => import_node_path21.default.resolve(r))) : null;
|
|
4789
|
+
const repoIds = allowRoots ? repos.filter((r) => allowRoots.has(import_node_path21.default.resolve(r.repoRoot))).map((r) => r.repoId) : void 0;
|
|
4790
|
+
const rows = workspaceStore.searchFts(ftq, lexicalK, repoIds);
|
|
4791
|
+
const lexByRepoRoot = /* @__PURE__ */ new Map();
|
|
4792
|
+
for (const r of rows) {
|
|
4793
|
+
const row = workspaceStore.getChunkById(r.id);
|
|
4794
|
+
if (!row) continue;
|
|
4795
|
+
const rootKey = import_node_path21.default.resolve(row.repo_root);
|
|
4796
|
+
const arr = lexByRepoRoot.get(rootKey) ?? [];
|
|
4797
|
+
arr.push({ id: r.id, score: bm25ToScore01(r.bm25) });
|
|
4798
|
+
lexByRepoRoot.set(rootKey, arr);
|
|
4799
|
+
}
|
|
4800
|
+
return { lexByRepoRoot, count: rows.length };
|
|
4801
|
+
}
|
|
4802
|
+
async function collectWorkspaceCandidates(args) {
|
|
4803
|
+
const { repos, qVec, query, vectorK, lexicalK, profile, opts, lexByRepoRoot, canUseWorkspaceLex } = args;
|
|
4804
|
+
const repoFilters = opts.filters?.repoRoots;
|
|
4805
|
+
const langFilter = opts.filters?.language;
|
|
4806
|
+
const pathPrefix = opts.filters?.pathPrefix;
|
|
4807
|
+
const candidates = [];
|
|
4808
|
+
let vecCount = 0;
|
|
4809
|
+
let lexCount = 0;
|
|
4810
|
+
for (const repo of repos) {
|
|
4811
|
+
if (repoFilters && !repoFilters.includes(repo.repoRoot)) continue;
|
|
4812
|
+
let includePaths = opts.scope?.includePaths?.slice();
|
|
4813
|
+
if (opts.scope?.changedOnly) {
|
|
4814
|
+
try {
|
|
4815
|
+
const changed = await listChangedFiles(repo.repoRoot, opts.scope.baseRef ?? "HEAD~1");
|
|
4816
|
+
includePaths = includePaths ? includePaths.filter((p) => changed.includes(p)) : changed;
|
|
4817
|
+
} catch {
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4820
|
+
const [vHits, lHits] = await Promise.all([
|
|
4821
|
+
repo.vectorCandidates(qVec, vectorK, includePaths),
|
|
4822
|
+
canUseWorkspaceLex ? Promise.resolve(lexByRepoRoot?.get(import_node_path21.default.resolve(repo.repoRoot)) ?? []) : repo.lexicalCandidates(query, lexicalK, includePaths)
|
|
4823
|
+
]);
|
|
4824
|
+
vecCount += vHits.length;
|
|
4825
|
+
if (!canUseWorkspaceLex) lexCount += lHits.length;
|
|
4826
|
+
const m = /* @__PURE__ */ new Map();
|
|
4827
|
+
for (const vh of vHits) {
|
|
4828
|
+
const id = vh.id;
|
|
4829
|
+
const vector01 = vectorCosineToScore01(vh.score);
|
|
4830
|
+
m.set(id, { repo, id, vector01, combined: 0 });
|
|
4831
|
+
}
|
|
4832
|
+
for (const lh of lHits) {
|
|
4833
|
+
const id = lh.id;
|
|
4834
|
+
const prev = m.get(id);
|
|
4835
|
+
if (prev) prev.lexical01 = lh.score;
|
|
4836
|
+
else m.set(id, { repo, id, lexical01: lh.score, combined: 0 });
|
|
4837
|
+
}
|
|
4838
|
+
const halfLife = halfLifeDaysForProfile(profile.name);
|
|
4839
|
+
for (const c of m.values()) {
|
|
4840
|
+
const meta = repo.getChunkMeta(c.id);
|
|
4841
|
+
if (!meta) continue;
|
|
4842
|
+
if (langFilter && meta.language !== langFilter) continue;
|
|
4843
|
+
if (pathPrefix && !meta.path.startsWith(pathPrefix)) continue;
|
|
4844
|
+
c.recency01 = profile.weights.recency > 0 ? recencyScore(meta.fileMtimeMs, halfLife) : 0;
|
|
4845
|
+
let kindFactor = 1;
|
|
4846
|
+
if (meta.kind === "synopsis" && profile.name === "search") kindFactor = 0.85;
|
|
4847
|
+
if (meta.kind === "synopsis" && profile.name === "architecture") kindFactor = 1.05;
|
|
4848
|
+
const v = c.vector01 ?? 0;
|
|
4849
|
+
const l = c.lexical01 ?? 0;
|
|
4850
|
+
const r = c.recency01 ?? 0;
|
|
4851
|
+
c.combined = clamp(kindFactor * (profile.weights.vector * v + profile.weights.lexical * l + profile.weights.recency * r), 0, 1);
|
|
4852
|
+
candidates.push(c);
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4855
|
+
return { candidates, vecCount, lexCount };
|
|
4856
|
+
}
|
|
4857
|
+
function rankWorkspaceCandidates(args) {
|
|
4858
|
+
const { candidates, maxMerged, k } = args;
|
|
4859
|
+
candidates.sort((a, b) => b.combined - a.combined);
|
|
4860
|
+
const merged = candidates.slice(0, maxMerged);
|
|
4861
|
+
const top = merged.slice(0, k);
|
|
4862
|
+
const hits = top.map((c) => {
|
|
4863
|
+
const meta = c.repo.getChunkMeta(c.id);
|
|
4864
|
+
const preview = makePreview(c.repo.getChunkText(c.id));
|
|
4865
|
+
return {
|
|
4866
|
+
score: c.combined,
|
|
4867
|
+
scoreBreakdown: { vector: c.vector01, lexical: c.lexical01, recency: c.recency01 },
|
|
4868
|
+
chunk: { ...meta, preview }
|
|
4869
|
+
};
|
|
4870
|
+
});
|
|
4871
|
+
return { merged, hits };
|
|
4872
|
+
}
|
|
4873
|
+
|
|
4874
|
+
// src/indexer/workspaceRetrieveContext.ts
|
|
4875
|
+
async function warmSymbolGraphForHits(repos, hits) {
|
|
4876
|
+
const byRepo = /* @__PURE__ */ new Map();
|
|
4877
|
+
for (const h of hits) {
|
|
4878
|
+
const s = byRepo.get(h.chunk.repoRoot) ?? /* @__PURE__ */ new Set();
|
|
4879
|
+
s.add(h.chunk.path);
|
|
4880
|
+
byRepo.set(h.chunk.repoRoot, s);
|
|
4881
|
+
}
|
|
4882
|
+
for (const [repoRoot, paths] of byRepo) {
|
|
4883
|
+
const repo = repos.find((r) => r.repoRoot === repoRoot);
|
|
4884
|
+
if (!repo) continue;
|
|
4885
|
+
await repo.warmSymbolGraphEdges(Array.from(paths), { maxFiles: 6 });
|
|
4886
|
+
}
|
|
4887
|
+
}
|
|
4888
|
+
async function fetchGraphNeighborFiles(args) {
|
|
4889
|
+
const { graphStore, repos, hits, profile, workspaceRoot, emitProgress } = args;
|
|
4890
|
+
if (!graphStore?.neighborFiles) return [];
|
|
4891
|
+
const seeds = [];
|
|
4892
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4893
|
+
for (const h of hits) {
|
|
4894
|
+
const repo = repos.find((r) => r.repoRoot === h.chunk.repoRoot);
|
|
4895
|
+
if (!repo) continue;
|
|
4896
|
+
const key = `${repo.repoId}:${h.chunk.path}`;
|
|
4897
|
+
if (seen.has(key)) continue;
|
|
4898
|
+
seen.add(key);
|
|
4899
|
+
seeds.push({ repoId: repo.repoId, path: h.chunk.path });
|
|
4900
|
+
if (seeds.length >= 4) break;
|
|
4901
|
+
}
|
|
4902
|
+
if (seeds.length === 0) return [];
|
|
4903
|
+
const startedAt = Date.now();
|
|
4904
|
+
emitProgress({ type: "workspace/retrieve/graph/start", workspaceRoot, seeds: seeds.length });
|
|
4905
|
+
try {
|
|
4906
|
+
const neighbors = await graphStore.neighborFiles({
|
|
4907
|
+
seeds,
|
|
4908
|
+
limit: profile.name === "architecture" ? 16 : 10,
|
|
4909
|
+
kinds: ["definition", "reference", "implementation", "typeDefinition"]
|
|
4910
|
+
});
|
|
4911
|
+
emitProgress({ type: "workspace/retrieve/graph/done", workspaceRoot, neighbors: neighbors.length, ms: Date.now() - startedAt });
|
|
4912
|
+
return neighbors;
|
|
4913
|
+
} catch {
|
|
4914
|
+
return [];
|
|
4915
|
+
}
|
|
4916
|
+
}
|
|
4917
|
+
async function buildContextBlocks(args) {
|
|
4918
|
+
const { repos, hits, graphNeighborFiles, profile } = args;
|
|
4919
|
+
const contextBlocks = [];
|
|
4920
|
+
const seenKey = /* @__PURE__ */ new Set();
|
|
4921
|
+
const addBlock = (repoRoot, filePath, startLine, endLine, text, reason) => {
|
|
4922
|
+
const key = `${repoRoot}:${filePath}:${startLine}:${endLine}:${text.length}:${reason}`;
|
|
4923
|
+
if (seenKey.has(key)) return;
|
|
4924
|
+
seenKey.add(key);
|
|
4925
|
+
if (!text.trim()) return;
|
|
4926
|
+
contextBlocks.push({ repoRoot, path: filePath, startLine, endLine, text, reason });
|
|
4927
|
+
};
|
|
4928
|
+
try {
|
|
4929
|
+
const byRepoId = /* @__PURE__ */ new Map();
|
|
4930
|
+
for (const r of repos) byRepoId.set(r.repoId, r);
|
|
4931
|
+
for (const n of graphNeighborFiles.slice(0, 10)) {
|
|
4932
|
+
const repo = byRepoId.get(n.repoId);
|
|
4933
|
+
if (!repo) continue;
|
|
4934
|
+
const chunkId = await repo.getRepresentativeChunkIdForFile(n.path, true);
|
|
4935
|
+
if (!chunkId) continue;
|
|
4936
|
+
const meta = repo.getChunkMeta(chunkId);
|
|
4937
|
+
if (!meta) continue;
|
|
4938
|
+
const text = repo.getChunkText(chunkId);
|
|
4939
|
+
addBlock(meta.repoRoot, meta.path, meta.startLine, meta.endLine, text, `graph neighbor (${n.weight})`);
|
|
4940
|
+
}
|
|
4941
|
+
} catch {
|
|
4942
|
+
}
|
|
4943
|
+
for (const h of hits) {
|
|
4944
|
+
const repo = repos.find((r) => r.repoRoot === h.chunk.repoRoot);
|
|
4945
|
+
if (!repo) continue;
|
|
4946
|
+
const hitText = repo.getChunkText(h.chunk.id);
|
|
4947
|
+
addBlock(h.chunk.repoRoot, h.chunk.path, h.chunk.startLine, h.chunk.endLine, hitText, "primary hit");
|
|
4948
|
+
const expanded = await repo.expandContext(h.chunk.id, {
|
|
4949
|
+
adjacentChunks: profile.expand.adjacentChunks ?? 0,
|
|
4950
|
+
followImports: profile.expand.followImports ?? 0,
|
|
4951
|
+
includeFileSynopsis: profile.expand.includeFileSynopsis ?? false
|
|
4952
|
+
});
|
|
4953
|
+
for (const ex of expanded) {
|
|
4954
|
+
const meta = repo.getChunkMeta(ex.id);
|
|
4955
|
+
if (!meta) continue;
|
|
4956
|
+
const text = repo.getChunkText(ex.id);
|
|
4957
|
+
addBlock(meta.repoRoot, meta.path, meta.startLine, meta.endLine, text, ex.reason);
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4960
|
+
return contextBlocks;
|
|
4961
|
+
}
|
|
4962
|
+
|
|
4963
|
+
// src/indexer/workspaceIndexer.ts
|
|
4691
4964
|
var WorkspaceIndexer = class {
|
|
4692
4965
|
constructor(workspaceRoot, embedder, config = {}) {
|
|
4693
4966
|
this.workspaceRoot = workspaceRoot;
|
|
@@ -4695,23 +4968,30 @@ var WorkspaceIndexer = class {
|
|
|
4695
4968
|
this.config = { ...config };
|
|
4696
4969
|
if (!this.config.cacheDir) this.config.cacheDir = defaultCacheDir();
|
|
4697
4970
|
this.progress = asProgressSink(this.config.progress);
|
|
4698
|
-
const wsId = sha256Hex(
|
|
4699
|
-
|
|
4700
|
-
this.workspaceStore = new WorkspaceStore(dbPath);
|
|
4701
|
-
this.workspaceStore.setMeta("workspaceRoot", import_node_path19.default.resolve(this.workspaceRoot));
|
|
4971
|
+
const wsId = sha256Hex(import_node_path22.default.resolve(this.workspaceRoot)).slice(0, 16);
|
|
4972
|
+
this.workspaceDbPath = import_node_path22.default.join(this.config.cacheDir, "workspace", wsId, "workspace.sqlite");
|
|
4702
4973
|
}
|
|
4703
4974
|
repos = [];
|
|
4704
4975
|
config;
|
|
4705
4976
|
progress = asProgressSink();
|
|
4706
4977
|
workspaceStore = null;
|
|
4707
4978
|
graphStore = null;
|
|
4979
|
+
workspaceDbPath;
|
|
4708
4980
|
emitProgress(event) {
|
|
4709
4981
|
try {
|
|
4710
4982
|
this.progress?.emit(event);
|
|
4711
4983
|
} catch {
|
|
4712
4984
|
}
|
|
4713
4985
|
}
|
|
4986
|
+
async ensureWorkspaceStore() {
|
|
4987
|
+
if (this.workspaceStore) return this.workspaceStore;
|
|
4988
|
+
const ws = await createWorkspaceStoreAsync(this.workspaceDbPath, { db: this.config.workspace?.db });
|
|
4989
|
+
ws.setMeta("workspaceRoot", import_node_path22.default.resolve(this.workspaceRoot));
|
|
4990
|
+
this.workspaceStore = ws;
|
|
4991
|
+
return ws;
|
|
4992
|
+
}
|
|
4714
4993
|
async open() {
|
|
4994
|
+
await this.ensureWorkspaceStore();
|
|
4715
4995
|
if (!this.graphStore && this.config.workspace?.graph?.provider === "neo4j") {
|
|
4716
4996
|
try {
|
|
4717
4997
|
const n = this.config.workspace.graph.neo4j;
|
|
@@ -4783,195 +5063,49 @@ var WorkspaceIndexer = class {
|
|
|
4783
5063
|
getRepoIndexers() {
|
|
4784
5064
|
return this.repos.slice();
|
|
4785
5065
|
}
|
|
4786
|
-
resolveProfile(opts) {
|
|
4787
|
-
const name = opts?.profile ?? "search";
|
|
4788
|
-
const base = DEFAULT_PROFILES[name] ?? DEFAULT_PROFILES.search;
|
|
4789
|
-
const configPatch = this.config.profiles?.[name] ?? {};
|
|
4790
|
-
const merged1 = deepMergeProfile(base, configPatch);
|
|
4791
|
-
const merged2 = deepMergeProfile(merged1, opts?.profileOverrides);
|
|
4792
|
-
const w = merged2.weights;
|
|
4793
|
-
const sum = Math.max(1e-6, w.vector + w.lexical + w.recency);
|
|
4794
|
-
merged2.weights = { vector: w.vector / sum, lexical: w.lexical / sum, recency: w.recency / sum };
|
|
4795
|
-
return merged2;
|
|
4796
|
-
}
|
|
4797
5066
|
async retrieve(query, opts = {}) {
|
|
4798
5067
|
if (this.repos.length === 0) await this.open();
|
|
4799
|
-
const profile = this.
|
|
5068
|
+
const profile = resolveWorkspaceProfile(this.config, opts);
|
|
4800
5069
|
const startedAt = Date.now();
|
|
4801
5070
|
this.emitProgress({ type: "workspace/retrieve/start", workspaceRoot: this.workspaceRoot, profile: profile.name, query });
|
|
4802
5071
|
const qVec = (await this.embedder.embed([query]))[0];
|
|
4803
5072
|
const vectorK = profile.candidates?.vectorK ?? Math.max(profile.k * 3, 30);
|
|
4804
5073
|
const lexicalK = profile.candidates?.lexicalK ?? Math.max(profile.k * 3, 30);
|
|
4805
5074
|
const maxMerged = profile.candidates?.maxMergedCandidates ?? Math.max(profile.k * 8, 120);
|
|
4806
|
-
const
|
|
4807
|
-
const
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
const rootKey = import_node_path19.default.resolve(row.repo_root);
|
|
4825
|
-
const arr = workspaceLexByRepoRoot.get(rootKey) ?? [];
|
|
4826
|
-
arr.push({ id: r.id, score: bm25ToScore01(r.bm25) });
|
|
4827
|
-
workspaceLexByRepoRoot.set(rootKey, arr);
|
|
4828
|
-
}
|
|
4829
|
-
}
|
|
4830
|
-
}
|
|
4831
|
-
for (const repo of this.repos) {
|
|
4832
|
-
if (repoFilters && !repoFilters.includes(repo.repoRoot)) continue;
|
|
4833
|
-
let includePaths = opts.scope?.includePaths?.slice();
|
|
4834
|
-
if (opts.scope?.changedOnly) {
|
|
4835
|
-
try {
|
|
4836
|
-
const changed = await listChangedFiles(repo.repoRoot, opts.scope.baseRef ?? "HEAD~1");
|
|
4837
|
-
includePaths = includePaths ? includePaths.filter((p) => changed.includes(p)) : changed;
|
|
4838
|
-
} catch {
|
|
4839
|
-
}
|
|
4840
|
-
}
|
|
4841
|
-
const [vHits, lHits] = await Promise.all([
|
|
4842
|
-
repo.vectorCandidates(qVec, vectorK, includePaths),
|
|
4843
|
-
canUseWorkspaceLex ? Promise.resolve(workspaceLexByRepoRoot.get(import_node_path19.default.resolve(repo.repoRoot)) ?? []) : repo.lexicalCandidates(query, lexicalK, includePaths)
|
|
4844
|
-
]);
|
|
4845
|
-
vecCount += vHits.length;
|
|
4846
|
-
if (!canUseWorkspaceLex) lexCount += lHits.length;
|
|
4847
|
-
const m = /* @__PURE__ */ new Map();
|
|
4848
|
-
for (const vh of vHits) {
|
|
4849
|
-
const id = vh.id;
|
|
4850
|
-
const vector01 = vectorCosineToScore01(vh.score);
|
|
4851
|
-
m.set(id, { repo, id, vector01, combined: 0 });
|
|
4852
|
-
}
|
|
4853
|
-
for (const lh of lHits) {
|
|
4854
|
-
const id = lh.id;
|
|
4855
|
-
const prev = m.get(id);
|
|
4856
|
-
if (prev) prev.lexical01 = lh.score;
|
|
4857
|
-
else m.set(id, { repo, id, lexical01: lh.score, combined: 0 });
|
|
4858
|
-
}
|
|
4859
|
-
const halfLife = halfLifeDaysForProfile(profile.name);
|
|
4860
|
-
for (const c of m.values()) {
|
|
4861
|
-
const meta = repo.getChunkMeta(c.id);
|
|
4862
|
-
if (!meta) continue;
|
|
4863
|
-
if (langFilter && meta.language !== langFilter) continue;
|
|
4864
|
-
if (pathPrefix && !meta.path.startsWith(pathPrefix)) continue;
|
|
4865
|
-
c.recency01 = profile.weights.recency > 0 ? recencyScore(meta.fileMtimeMs, halfLife) : 0;
|
|
4866
|
-
let kindFactor = 1;
|
|
4867
|
-
if (meta.kind === "synopsis" && profile.name === "search") kindFactor = 0.85;
|
|
4868
|
-
if (meta.kind === "synopsis" && profile.name === "architecture") kindFactor = 1.05;
|
|
4869
|
-
const v = c.vector01 ?? 0;
|
|
4870
|
-
const l = c.lexical01 ?? 0;
|
|
4871
|
-
const r = c.recency01 ?? 0;
|
|
4872
|
-
c.combined = clamp(
|
|
4873
|
-
kindFactor * (profile.weights.vector * v + profile.weights.lexical * l + profile.weights.recency * r),
|
|
4874
|
-
0,
|
|
4875
|
-
1
|
|
4876
|
-
);
|
|
4877
|
-
candidates.push(c);
|
|
4878
|
-
}
|
|
4879
|
-
}
|
|
4880
|
-
candidates.sort((a, b) => b.combined - a.combined);
|
|
4881
|
-
const merged = candidates.slice(0, maxMerged);
|
|
4882
|
-
const top = merged.slice(0, profile.k);
|
|
4883
|
-
const hits = top.map((c) => {
|
|
4884
|
-
const meta = c.repo.getChunkMeta(c.id);
|
|
4885
|
-
const preview = makePreview(c.repo.getChunkText(c.id));
|
|
4886
|
-
return {
|
|
4887
|
-
score: c.combined,
|
|
4888
|
-
scoreBreakdown: { vector: c.vector01, lexical: c.lexical01, recency: c.recency01 },
|
|
4889
|
-
chunk: { ...meta, preview }
|
|
4890
|
-
};
|
|
5075
|
+
const canUseWorkspaceLex = !!this.workspaceStore && this.workspaceStore.ftsEnabled && this.config.storage?.ftsMode !== "off" && !opts.scope?.includePaths && !opts.scope?.changedOnly;
|
|
5076
|
+
const { lexByRepoRoot, count: workspaceLexCount } = canUseWorkspaceLex && profile.weights.lexical > 0 && this.workspaceStore ? buildWorkspaceLexByRepoRoot({
|
|
5077
|
+
workspaceStore: this.workspaceStore,
|
|
5078
|
+
repos: this.repos,
|
|
5079
|
+
query,
|
|
5080
|
+
lexicalK,
|
|
5081
|
+
repoFilters: opts.filters?.repoRoots
|
|
5082
|
+
}) : { lexByRepoRoot: void 0, count: 0 };
|
|
5083
|
+
const { candidates, vecCount, lexCount } = await collectWorkspaceCandidates({
|
|
5084
|
+
repos: this.repos,
|
|
5085
|
+
qVec,
|
|
5086
|
+
query,
|
|
5087
|
+
vectorK,
|
|
5088
|
+
lexicalK,
|
|
5089
|
+
profile,
|
|
5090
|
+
opts,
|
|
5091
|
+
lexByRepoRoot,
|
|
5092
|
+
canUseWorkspaceLex
|
|
4891
5093
|
});
|
|
5094
|
+
const { merged, hits } = rankWorkspaceCandidates({ candidates, maxMerged, k: profile.k });
|
|
5095
|
+
const totalLexCount = (canUseWorkspaceLex ? workspaceLexCount : 0) + lexCount;
|
|
4892
5096
|
try {
|
|
4893
|
-
|
|
4894
|
-
for (const h of hits) {
|
|
4895
|
-
const s = byRepo.get(h.chunk.repoRoot) ?? /* @__PURE__ */ new Set();
|
|
4896
|
-
s.add(h.chunk.path);
|
|
4897
|
-
byRepo.set(h.chunk.repoRoot, s);
|
|
4898
|
-
}
|
|
4899
|
-
for (const [repoRoot, paths] of byRepo) {
|
|
4900
|
-
const repo = this.repos.find((r) => r.repoRoot === repoRoot);
|
|
4901
|
-
if (!repo) continue;
|
|
4902
|
-
await repo.warmSymbolGraphEdges(Array.from(paths), { maxFiles: 6 });
|
|
4903
|
-
}
|
|
4904
|
-
} catch {
|
|
4905
|
-
}
|
|
4906
|
-
let graphNeighborFiles = [];
|
|
4907
|
-
try {
|
|
4908
|
-
if (this.graphStore?.neighborFiles) {
|
|
4909
|
-
const seeds = [];
|
|
4910
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4911
|
-
for (const h of hits) {
|
|
4912
|
-
const repo = this.repos.find((r) => r.repoRoot === h.chunk.repoRoot);
|
|
4913
|
-
if (!repo) continue;
|
|
4914
|
-
const key = `${repo.repoId}:${h.chunk.path}`;
|
|
4915
|
-
if (seen.has(key)) continue;
|
|
4916
|
-
seen.add(key);
|
|
4917
|
-
seeds.push({ repoId: repo.repoId, path: h.chunk.path });
|
|
4918
|
-
if (seeds.length >= 4) break;
|
|
4919
|
-
}
|
|
4920
|
-
if (seeds.length > 0) {
|
|
4921
|
-
const gs = Date.now();
|
|
4922
|
-
this.emitProgress({ type: "workspace/retrieve/graph/start", workspaceRoot: this.workspaceRoot, seeds: seeds.length });
|
|
4923
|
-
graphNeighborFiles = await this.graphStore.neighborFiles({
|
|
4924
|
-
seeds,
|
|
4925
|
-
limit: profile.name === "architecture" ? 16 : 10,
|
|
4926
|
-
kinds: ["definition", "reference", "implementation", "typeDefinition"]
|
|
4927
|
-
});
|
|
4928
|
-
this.emitProgress({ type: "workspace/retrieve/graph/done", workspaceRoot: this.workspaceRoot, neighbors: graphNeighborFiles.length, ms: Date.now() - gs });
|
|
4929
|
-
}
|
|
4930
|
-
}
|
|
4931
|
-
} catch {
|
|
4932
|
-
graphNeighborFiles = [];
|
|
4933
|
-
}
|
|
4934
|
-
const contextBlocks = [];
|
|
4935
|
-
const seenKey = /* @__PURE__ */ new Set();
|
|
4936
|
-
const addBlock = (repoRoot, path21, startLine, endLine, text, reason) => {
|
|
4937
|
-
const key = `${repoRoot}:${path21}:${startLine}:${endLine}:${text.length}:${reason}`;
|
|
4938
|
-
if (seenKey.has(key)) return;
|
|
4939
|
-
seenKey.add(key);
|
|
4940
|
-
if (!text.trim()) return;
|
|
4941
|
-
contextBlocks.push({ repoRoot, path: path21, startLine, endLine, text, reason });
|
|
4942
|
-
};
|
|
4943
|
-
try {
|
|
4944
|
-
const byRepoId = /* @__PURE__ */ new Map();
|
|
4945
|
-
for (const r of this.repos) byRepoId.set(r.repoId, r);
|
|
4946
|
-
for (const n of graphNeighborFiles.slice(0, 10)) {
|
|
4947
|
-
const repo = byRepoId.get(n.repoId);
|
|
4948
|
-
if (!repo) continue;
|
|
4949
|
-
const chunkId = await repo.getRepresentativeChunkIdForFile(n.path, true);
|
|
4950
|
-
if (!chunkId) continue;
|
|
4951
|
-
const meta = repo.getChunkMeta(chunkId);
|
|
4952
|
-
if (!meta) continue;
|
|
4953
|
-
const text = repo.getChunkText(chunkId);
|
|
4954
|
-
addBlock(meta.repoRoot, meta.path, meta.startLine, meta.endLine, text, `graph neighbor (${n.weight})`);
|
|
4955
|
-
}
|
|
5097
|
+
await warmSymbolGraphForHits(this.repos, hits);
|
|
4956
5098
|
} catch {
|
|
4957
5099
|
}
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
});
|
|
4968
|
-
for (const ex of expanded) {
|
|
4969
|
-
const meta = repo.getChunkMeta(ex.id);
|
|
4970
|
-
if (!meta) continue;
|
|
4971
|
-
const t = repo.getChunkText(ex.id);
|
|
4972
|
-
addBlock(meta.repoRoot, meta.path, meta.startLine, meta.endLine, t, ex.reason);
|
|
4973
|
-
}
|
|
4974
|
-
}
|
|
5100
|
+
const graphNeighborFiles = await fetchGraphNeighborFiles({
|
|
5101
|
+
graphStore: this.graphStore,
|
|
5102
|
+
repos: this.repos,
|
|
5103
|
+
hits,
|
|
5104
|
+
profile,
|
|
5105
|
+
workspaceRoot: this.workspaceRoot,
|
|
5106
|
+
emitProgress: (e) => this.emitProgress(e)
|
|
5107
|
+
});
|
|
5108
|
+
const contextBlocks = await buildContextBlocks({ repos: this.repos, hits, graphNeighborFiles, profile });
|
|
4975
5109
|
const bundle = {
|
|
4976
5110
|
hits,
|
|
4977
5111
|
context: contextBlocks,
|
|
@@ -4980,7 +5114,7 @@ var WorkspaceIndexer = class {
|
|
|
4980
5114
|
reposSearched: this.repos.length,
|
|
4981
5115
|
candidates: {
|
|
4982
5116
|
vector: vecCount,
|
|
4983
|
-
lexical:
|
|
5117
|
+
lexical: totalLexCount,
|
|
4984
5118
|
merged: merged.length,
|
|
4985
5119
|
returned: hits.length
|
|
4986
5120
|
}
|
|
@@ -4992,7 +5126,7 @@ var WorkspaceIndexer = class {
|
|
|
4992
5126
|
profile: profile.name,
|
|
4993
5127
|
ms: Date.now() - startedAt,
|
|
4994
5128
|
hits: hits.length,
|
|
4995
|
-
candidates: { vector: vecCount, lexical:
|
|
5129
|
+
candidates: { vector: vecCount, lexical: totalLexCount, merged: merged.length }
|
|
4996
5130
|
});
|
|
4997
5131
|
return bundle;
|
|
4998
5132
|
}
|
|
@@ -5016,11 +5150,11 @@ var WorkspaceIndexer = class {
|
|
|
5016
5150
|
};
|
|
5017
5151
|
|
|
5018
5152
|
// src/config.ts
|
|
5019
|
-
var
|
|
5020
|
-
var
|
|
5153
|
+
var import_node_fs16 = __toESM(require("fs"), 1);
|
|
5154
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
5021
5155
|
function loadConfigFile(filePath) {
|
|
5022
|
-
const abs =
|
|
5023
|
-
const raw =
|
|
5156
|
+
const abs = import_node_path23.default.resolve(filePath);
|
|
5157
|
+
const raw = import_node_fs16.default.readFileSync(abs, "utf8");
|
|
5024
5158
|
const json = JSON.parse(raw);
|
|
5025
5159
|
const cfg = { ...json };
|
|
5026
5160
|
if (json.redact?.patterns && Array.isArray(json.redact.patterns)) {
|
|
@@ -5058,11 +5192,14 @@ function loadConfigFile(filePath) {
|
|
|
5058
5192
|
WorkspaceLinker,
|
|
5059
5193
|
WorkspaceStore,
|
|
5060
5194
|
asProgressSink,
|
|
5195
|
+
betterSqlite3Adapter,
|
|
5061
5196
|
chunkSource,
|
|
5062
5197
|
createAnnIndex,
|
|
5063
5198
|
createNeo4jGraphStore,
|
|
5064
5199
|
createVSCodeSymbolGraphProvider,
|
|
5065
5200
|
createVectorIndex,
|
|
5201
|
+
createWorkspaceStore,
|
|
5202
|
+
createWorkspaceStoreAsync,
|
|
5066
5203
|
deepMergeProfile,
|
|
5067
5204
|
discoverGitRepos,
|
|
5068
5205
|
languageFromPath,
|
|
@@ -5070,5 +5207,6 @@ function loadConfigFile(filePath) {
|
|
|
5070
5207
|
loadConfigFile,
|
|
5071
5208
|
mergeIndexerConfig,
|
|
5072
5209
|
pickRepoOverride,
|
|
5210
|
+
sqlJsAdapter,
|
|
5073
5211
|
stableSymbolId
|
|
5074
5212
|
});
|