@neuralsea/workspace-indexer 0.3.3 → 0.3.5

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/cli.cjs CHANGED
@@ -1604,6 +1604,16 @@ var RepoFileIndexer = class {
1604
1604
  return;
1605
1605
  }
1606
1606
  this.emit({ type: "repo/index/file/start", repoRoot: this.repoRoot, path: posixRelPath });
1607
+ const stage = (stageName, stageStartedAt) => {
1608
+ this.emit({
1609
+ type: "repo/index/file/stage",
1610
+ repoRoot: this.repoRoot,
1611
+ path: posixRelPath,
1612
+ stage: stageName,
1613
+ ms: Date.now() - stageStartedAt
1614
+ });
1615
+ };
1616
+ const readStartedAt = Date.now();
1607
1617
  const abs = import_node_path9.default.join(this.repoRoot, fromPosixPath(posixRelPath));
1608
1618
  let stat;
1609
1619
  try {
@@ -1627,16 +1637,20 @@ var RepoFileIndexer = class {
1627
1637
  }
1628
1638
  const raw = buf.toString("utf8");
1629
1639
  const redacted = this.applyRedactions(raw);
1640
+ stage("read", readStartedAt);
1630
1641
  const fileHash = sha256Hex(redacted);
1631
1642
  const prev = this.store.getFileHash(posixRelPath);
1632
1643
  if (prev === fileHash) {
1633
1644
  this.emit({ type: "repo/index/file/skip", repoRoot: this.repoRoot, path: posixRelPath, reason: "unchanged" });
1634
1645
  return;
1635
1646
  }
1647
+ const chunkStartedAt = Date.now();
1636
1648
  const { language, chunks } = chunkSource(posixRelPath, redacted, this.config.chunk);
1649
+ stage("chunk", chunkStartedAt);
1637
1650
  let imports = [];
1638
1651
  let exports2 = [];
1639
1652
  if (language === "typescript" || language === "javascript") {
1653
+ const relationsStartedAt = Date.now();
1640
1654
  const rel = extractTsRelations(posixRelPath, redacted);
1641
1655
  imports = rel.imports;
1642
1656
  exports2 = rel.exports;
@@ -1644,12 +1658,14 @@ var RepoFileIndexer = class {
1644
1658
  this.store.setEdges(posixRelPath, "export", rel.exports);
1645
1659
  this.workspaceStore?.setEdges(this.repoId, posixRelPath, "import", rel.imports);
1646
1660
  this.workspaceStore?.setEdges(this.repoId, posixRelPath, "export", rel.exports);
1661
+ stage("relations", relationsStartedAt);
1647
1662
  } else {
1648
1663
  this.store.setEdges(posixRelPath, "import", []);
1649
1664
  this.store.setEdges(posixRelPath, "export", []);
1650
1665
  this.workspaceStore?.setEdges(this.repoId, posixRelPath, "import", []);
1651
1666
  this.workspaceStore?.setEdges(this.repoId, posixRelPath, "export", []);
1652
1667
  }
1668
+ const synopsisStartedAt = Date.now();
1653
1669
  const synopsisText = buildSynopsis(posixRelPath, language, redacted);
1654
1670
  const synopsis = synopsisText.trim() ? [
1655
1671
  {
@@ -1674,14 +1690,29 @@ var RepoFileIndexer = class {
1674
1690
  }
1675
1691
  ] : [];
1676
1692
  const combined = [...synopsis, ...headerChunk, ...chunks.map((c) => ({ ...c, kind: "chunk" }))];
1693
+ stage("synopsis", synopsisStartedAt);
1694
+ const embedStartedAt = Date.now();
1677
1695
  const embedTexts = [];
1678
1696
  const embedPlan = [];
1679
1697
  const embeddings = combined.map(() => null);
1698
+ let cached2 = 0;
1680
1699
  for (let i = 0; i < combined.length; i++) {
1681
1700
  const ch = combined[i];
1682
- const cached2 = this.embeddingCache.get(this.embedder.id, ch.contentHash);
1683
- if (cached2) {
1684
- embeddings[i] = cached2;
1701
+ const cachedEmb = this.embeddingCache.get(this.embedder.id, ch.contentHash);
1702
+ if (cachedEmb) {
1703
+ embeddings[i] = cachedEmb;
1704
+ cached2++;
1705
+ this.emit({
1706
+ type: "repo/index/file/embed/chunk",
1707
+ repoRoot: this.repoRoot,
1708
+ path: posixRelPath,
1709
+ chunkIndex: i,
1710
+ chunksTotal: combined.length,
1711
+ kind: ch.kind,
1712
+ startLine: ch.startLine,
1713
+ endLine: ch.endLine,
1714
+ cached: true
1715
+ });
1685
1716
  continue;
1686
1717
  }
1687
1718
  embedTexts.push(
@@ -1693,7 +1724,7 @@ lines:${ch.startLine}-${ch.endLine}
1693
1724
  ---
1694
1725
  ${ch.text}`
1695
1726
  );
1696
- embedPlan.push({ chunkIdx: i, contentHash: ch.contentHash });
1727
+ embedPlan.push({ chunkIdx: i, contentHash: ch.contentHash, kind: ch.kind, startLine: ch.startLine, endLine: ch.endLine });
1697
1728
  }
1698
1729
  const batchSize = this.config.embed.batchSize;
1699
1730
  for (let start = 0; start < embedTexts.length; start += batchSize) {
@@ -1711,13 +1742,35 @@ ${ch.text}`
1711
1742
  const plan = embedPlan[start + j];
1712
1743
  embeddings[plan.chunkIdx] = vecs[j];
1713
1744
  this.embeddingCache.put(this.embedder.id, plan.contentHash, vecs[j]);
1745
+ this.emit({
1746
+ type: "repo/index/file/embed/chunk",
1747
+ repoRoot: this.repoRoot,
1748
+ path: posixRelPath,
1749
+ chunkIndex: plan.chunkIdx,
1750
+ chunksTotal: combined.length,
1751
+ kind: plan.kind,
1752
+ startLine: plan.startLine,
1753
+ endLine: plan.endLine,
1754
+ cached: false
1755
+ });
1714
1756
  }
1715
1757
  }
1758
+ this.emit({
1759
+ type: "repo/index/file/embed/done",
1760
+ repoRoot: this.repoRoot,
1761
+ path: posixRelPath,
1762
+ chunks: combined.length,
1763
+ cached: cached2,
1764
+ computed: embedTexts.length,
1765
+ ms: Date.now() - embedStartedAt
1766
+ });
1767
+ stage("embed", embedStartedAt);
1716
1768
  const fileMtime = stat.mtimeMs;
1717
1769
  const ftsMode = this.config.storage.ftsMode;
1718
1770
  const storeText = this.config.storage.storeText;
1719
1771
  const oldChunkIds = this.store.listChunksForFile(posixRelPath).map((r) => r.id);
1720
1772
  const points = [];
1773
+ const storeStartedAt = Date.now();
1721
1774
  const rows = combined.map((ch, i) => {
1722
1775
  const id = sha256Hex(
1723
1776
  `${this.repoId}:${posixRelPath}:${ch.kind}:${ch.startLine}:${ch.endLine}:${ch.contentHash}`
@@ -1745,7 +1798,22 @@ ${ch.text}`
1745
1798
  this.store.replaceChunksForFile(posixRelPath, rows);
1746
1799
  this.workspaceStore?.upsertFile(this.repoId, posixRelPath, fileHash, fileMtime, language, stat.size);
1747
1800
  this.workspaceStore?.replaceChunksForFile(this.repoId, this.repoRoot, posixRelPath, rows);
1801
+ stage("store", storeStartedAt);
1802
+ const symbolsStartedAt = Date.now();
1803
+ if (this.symbolGraphProvider && this.symbolGraphProvider.supports(language)) {
1804
+ this.emit({ type: "repo/index/file/symbols/start", repoRoot: this.repoRoot, path: posixRelPath, providerId: this.symbolGraphProvider.id });
1805
+ }
1748
1806
  const symbolOut = await this.indexSymbolsIfEnabled(posixRelPath, language, redacted, fileHash);
1807
+ this.emit({
1808
+ type: "repo/index/file/symbols",
1809
+ repoRoot: this.repoRoot,
1810
+ path: posixRelPath,
1811
+ symbols: symbolOut?.symbols?.length ?? 0,
1812
+ edges: symbolOut?.edges?.length ?? 0,
1813
+ ms: Date.now() - symbolsStartedAt
1814
+ });
1815
+ stage("symbols", symbolsStartedAt);
1816
+ const graphStartedAt = Date.now();
1749
1817
  const head = this.getHead();
1750
1818
  if (this.graphStore && head) {
1751
1819
  await this.graphStore.replaceFileGraph({
@@ -1761,7 +1829,10 @@ ${ch.text}`
1761
1829
  symbolEdges: (symbolOut?.edges ?? []).map((e) => ({ fromId: e.fromId, toId: e.toId, kind: e.kind }))
1762
1830
  }).catch(() => void 0);
1763
1831
  }
1832
+ stage("graph", graphStartedAt);
1833
+ const vectorStartedAt = Date.now();
1764
1834
  await this.ensureVectorUpToDate(points, oldChunkIds);
1835
+ stage("vector", vectorStartedAt);
1765
1836
  this.emit({
1766
1837
  type: "repo/index/file/done",
1767
1838
  repoRoot: this.repoRoot,
@@ -2401,28 +2472,46 @@ var RepoIndexer = class {
2401
2472
  const uniq3 = Array.from(new Set(posixPaths)).slice(0, maxFiles);
2402
2473
  for (const p of uniq3) {
2403
2474
  if (opts?.signal?.aborted) return;
2475
+ const startedAt = Date.now();
2476
+ this.emitProgress({ type: "repo/symbolGraph/expand/start", repoRoot: this.repoRoot, path: p });
2404
2477
  const abs = import_node_path12.default.join(this.repoRoot, p.split("/").join(import_node_path12.default.sep));
2405
2478
  let text = "";
2406
2479
  try {
2407
2480
  text = import_node_fs9.default.readFileSync(abs, "utf8");
2408
2481
  } catch {
2482
+ this.emitProgress({ type: "repo/symbolGraph/expand/done", repoRoot: this.repoRoot, path: p, edges: 0, ms: Date.now() - startedAt });
2409
2483
  continue;
2410
2484
  }
2411
2485
  const contentHash = this.store.getFileHash(p) ?? void 0;
2412
2486
  const lang = languageFromPath(p);
2413
2487
  const edges = await provider.expandDocumentEdges({ repoRoot: this.repoRoot, path: p, language: lang, text, contentHash }, { signal: opts?.signal }).catch(() => []);
2414
2488
  const normalized = (Array.isArray(edges) ? edges : []).map((e) => ({
2415
- fromId: e.fromId,
2416
- toId: e.toId,
2417
- kind: e.kind,
2418
- toPath: String(e.toPath ?? "")
2489
+ fromId: String(e?.fromId ?? ""),
2490
+ toId: String(e?.toId ?? ""),
2491
+ kind: String(e?.kind ?? ""),
2492
+ toPath: String(e?.toPath ?? "")
2419
2493
  }));
2494
+ const byKind = {};
2495
+ for (const e of normalized) {
2496
+ const kind = e.kind;
2497
+ if (kind === "definition" || kind === "reference" || kind === "implementation" || kind === "typeDefinition") {
2498
+ byKind[kind] = (byKind[kind] ?? 0) + 1;
2499
+ }
2500
+ }
2420
2501
  this.workspaceStore.replaceSymbolEdgesFromFile(this.repoId, p, normalized);
2421
2502
  await this.graphStore?.replaceOutgoingSymbolEdgesFromFile?.({
2422
2503
  repoId: this.repoId,
2423
2504
  fromPath: p,
2424
2505
  edges: normalized.map((e) => ({ fromId: e.fromId, toId: e.toId, kind: e.kind, toPath: e.toPath }))
2425
2506
  }).catch(() => void 0);
2507
+ this.emitProgress({
2508
+ type: "repo/symbolGraph/expand/done",
2509
+ repoRoot: this.repoRoot,
2510
+ path: p,
2511
+ edges: normalized.length,
2512
+ byKind,
2513
+ ms: Date.now() - startedAt
2514
+ });
2426
2515
  }
2427
2516
  }
2428
2517
  async watch() {
@@ -3321,10 +3410,24 @@ var Neo4jGraphStore = class {
3321
3410
  this.driver = driver;
3322
3411
  this.cfg = cfg;
3323
3412
  this.labelPrefix = cfg.labelPrefix ?? "Petri";
3413
+ this.emitProgress = cfg.emit ?? null;
3324
3414
  }
3325
3415
  id = "neo4j";
3326
3416
  labelPrefix;
3327
3417
  schemaVersionLatest = 2;
3418
+ emitProgress;
3419
+ opStart(op, args) {
3420
+ try {
3421
+ this.emitProgress?.({ type: "graph/neo4j/op/start", op, repoId: args?.repoId, path: args?.path });
3422
+ } catch {
3423
+ }
3424
+ }
3425
+ opDone(op, startedAt, args) {
3426
+ try {
3427
+ this.emitProgress?.({ type: "graph/neo4j/op/done", op, repoId: args?.repoId, path: args?.path, ms: Date.now() - startedAt });
3428
+ } catch {
3429
+ }
3430
+ }
3328
3431
  labels() {
3329
3432
  return {
3330
3433
  Repo: lp(this.labelPrefix, "Repo"),
@@ -3335,6 +3438,8 @@ var Neo4jGraphStore = class {
3335
3438
  };
3336
3439
  }
3337
3440
  async init() {
3441
+ const startedAt = Date.now();
3442
+ this.opStart("init");
3338
3443
  const { Schema } = this.labels();
3339
3444
  await runSession(this.driver, this.cfg.database, async (s) => {
3340
3445
  await s.run(
@@ -3345,6 +3450,7 @@ var Neo4jGraphStore = class {
3345
3450
  );
3346
3451
  });
3347
3452
  await this.runMigrations().catch(() => void 0);
3453
+ this.opDone("init", startedAt);
3348
3454
  }
3349
3455
  async getSchemaVersion() {
3350
3456
  const { Schema } = this.labels();
@@ -3368,6 +3474,8 @@ var Neo4jGraphStore = class {
3368
3474
  });
3369
3475
  }
3370
3476
  async runMigrations() {
3477
+ const startedAt = Date.now();
3478
+ this.opStart("migrations");
3371
3479
  const { Repo, File, Symbol: Symbol2, External } = this.labels();
3372
3480
  let v = await this.getSchemaVersion();
3373
3481
  if (v < 1) {
@@ -3408,8 +3516,11 @@ var Neo4jGraphStore = class {
3408
3516
  if (v !== this.schemaVersionLatest) {
3409
3517
  await this.setSchemaVersion(this.schemaVersionLatest);
3410
3518
  }
3519
+ this.opDone("migrations", startedAt);
3411
3520
  }
3412
3521
  async setRepoHead(args) {
3522
+ const startedAt = Date.now();
3523
+ this.opStart("setRepoHead", { repoId: args.repoId });
3413
3524
  const { Repo, File, Symbol: Symbol2, External } = this.labels();
3414
3525
  const { repoId, repoRoot, commit, branch } = args;
3415
3526
  await runSession(this.driver, this.cfg.database, async (s) => {
@@ -3440,15 +3551,21 @@ var Neo4jGraphStore = class {
3440
3551
  await s.run(`MATCH (r:${Repo} {repo_id:$repoId}) SET r.commit=$commit`, { repoId, commit });
3441
3552
  }
3442
3553
  });
3554
+ this.opDone("setRepoHead", startedAt, { repoId: args.repoId });
3443
3555
  }
3444
3556
  async deleteFile(args) {
3557
+ const startedAt = Date.now();
3558
+ this.opStart("deleteFile", { repoId: args.repoId, path: args.path });
3445
3559
  const { File, Symbol: Symbol2 } = this.labels();
3446
3560
  await runSession(this.driver, this.cfg.database, async (s) => {
3447
3561
  await s.run(`MATCH (s:${Symbol2} {repo_id:$repoId, path:$path}) DETACH DELETE s`, args);
3448
3562
  await s.run(`MATCH (f:${File} {repo_id:$repoId, path:$path}) DETACH DELETE f`, args);
3449
3563
  });
3564
+ this.opDone("deleteFile", startedAt, { repoId: args.repoId, path: args.path });
3450
3565
  }
3451
3566
  async replaceOutgoingSymbolEdgesFromFile(args) {
3567
+ const startedAt = Date.now();
3568
+ this.opStart("replaceOutgoingSymbolEdgesFromFile", { repoId: args.repoId, path: args.fromPath });
3452
3569
  const { Symbol: Symbol2 } = this.labels();
3453
3570
  const kinds = args.edges.length > 0 ? Array.from(new Set(args.edges.map((e) => e.kind))) : ["definition", "reference", "implementation", "typeDefinition"];
3454
3571
  const keep = args.edges.map((e) => `${e.fromId}|${e.kind}|${e.toId}`);
@@ -3480,8 +3597,11 @@ var Neo4jGraphStore = class {
3480
3597
  { repoId: args.repoId, fromPath: args.fromPath, kinds, keep }
3481
3598
  ).catch(() => void 0);
3482
3599
  });
3600
+ this.opDone("replaceOutgoingSymbolEdgesFromFile", startedAt, { repoId: args.repoId, path: args.fromPath });
3483
3601
  }
3484
3602
  async replaceFileGraph(update) {
3603
+ const startedAt = Date.now();
3604
+ this.opStart("replaceFileGraph", { repoId: update.repoId, path: update.path });
3485
3605
  const { Repo, File, Symbol: Symbol2, External } = this.labels();
3486
3606
  const symbols = update.symbols.map((s) => ({
3487
3607
  id: s.id,
@@ -3593,8 +3713,11 @@ var Neo4jGraphStore = class {
3593
3713
  }
3594
3714
  }
3595
3715
  });
3716
+ this.opDone("replaceFileGraph", startedAt, { repoId: update.repoId, path: update.path });
3596
3717
  }
3597
3718
  async replaceRepoLinks(args) {
3719
+ const startedAt = Date.now();
3720
+ this.opStart("replaceRepoLinks");
3598
3721
  const { Repo } = this.labels();
3599
3722
  const byFrom = /* @__PURE__ */ new Map();
3600
3723
  for (const l of args.links) {
@@ -3625,8 +3748,11 @@ var Neo4jGraphStore = class {
3625
3748
  ).catch(() => void 0);
3626
3749
  });
3627
3750
  }
3751
+ this.opDone("replaceRepoLinks", startedAt);
3628
3752
  }
3629
3753
  async neighborFiles(args) {
3754
+ const startedAt = Date.now();
3755
+ this.opStart("neighborFiles");
3630
3756
  const { File, Symbol: Symbol2 } = this.labels();
3631
3757
  const limit = args.limit ?? 20;
3632
3758
  const kinds = args.kinds && args.kinds.length > 0 ? args.kinds : null;
@@ -3650,9 +3776,12 @@ var Neo4jGraphStore = class {
3650
3776
  rows.push({ repoId, path: p, weight: Number.isFinite(w) ? w : 0 });
3651
3777
  }
3652
3778
  });
3779
+ this.opDone("neighborFiles", startedAt);
3653
3780
  return rows;
3654
3781
  }
3655
3782
  async shortestFilePath(args) {
3783
+ const startedAt = Date.now();
3784
+ this.opStart("shortestFilePath");
3656
3785
  const { File } = this.labels();
3657
3786
  const maxRels = args.maxRels ?? 16;
3658
3787
  let out = null;
@@ -3668,9 +3797,12 @@ var Neo4jGraphStore = class {
3668
3797
  if (!Array.isArray(files)) return;
3669
3798
  out = files.map((x) => ({ repoId: String(x.repoId), path: String(x.path) }));
3670
3799
  });
3800
+ this.opDone("shortestFilePath", startedAt);
3671
3801
  return out;
3672
3802
  }
3673
3803
  async extractFileSubgraph(args) {
3804
+ const startedAt = Date.now();
3805
+ this.opStart("extractFileSubgraph");
3674
3806
  const { File, Symbol: Symbol2 } = this.labels();
3675
3807
  const limitEdges = args.limitEdges ?? 200;
3676
3808
  const nodes = /* @__PURE__ */ new Map();
@@ -3692,6 +3824,7 @@ var Neo4jGraphStore = class {
3692
3824
  edges.push({ from, to, kind });
3693
3825
  }
3694
3826
  });
3827
+ this.opDone("extractFileSubgraph", startedAt);
3695
3828
  return { nodes: Array.from(nodes.values()), edges };
3696
3829
  }
3697
3830
  async close() {
@@ -4051,7 +4184,8 @@ var WorkspaceIndexer = class {
4051
4184
  user: n.user,
4052
4185
  password: n.password,
4053
4186
  database: n.database,
4054
- labelPrefix: n.labelPrefix
4187
+ labelPrefix: n.labelPrefix,
4188
+ emit: (e) => this.emitProgress(e)
4055
4189
  });
4056
4190
  } catch (e) {
4057
4191
  this.emitProgress({
@@ -4248,11 +4382,14 @@ var WorkspaceIndexer = class {
4248
4382
  if (seeds.length >= 4) break;
4249
4383
  }
4250
4384
  if (seeds.length > 0) {
4385
+ const gs = Date.now();
4386
+ this.emitProgress({ type: "workspace/retrieve/graph/start", workspaceRoot: this.workspaceRoot, seeds: seeds.length });
4251
4387
  graphNeighborFiles = await this.graphStore.neighborFiles({
4252
4388
  seeds,
4253
4389
  limit: profile.name === "architecture" ? 16 : 10,
4254
4390
  kinds: ["definition", "reference", "implementation", "typeDefinition"]
4255
4391
  });
4392
+ this.emitProgress({ type: "workspace/retrieve/graph/done", workspaceRoot: this.workspaceRoot, neighbors: graphNeighborFiles.length, ms: Date.now() - gs });
4256
4393
  }
4257
4394
  }
4258
4395
  } catch {
package/dist/cli.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  OpenAIEmbeddingsProvider,
6
6
  WorkspaceIndexer,
7
7
  loadConfigFile
8
- } from "./chunk-IXKSW7IX.js";
8
+ } from "./chunk-FUUQXFJQ.js";
9
9
 
10
10
  // src/cli.ts
11
11
  import yargs from "yargs";