@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/index.cjs CHANGED
@@ -2271,10 +2271,24 @@ var Neo4jGraphStore = class {
2271
2271
  this.driver = driver;
2272
2272
  this.cfg = cfg;
2273
2273
  this.labelPrefix = cfg.labelPrefix ?? "Petri";
2274
+ this.emitProgress = cfg.emit ?? null;
2274
2275
  }
2275
2276
  id = "neo4j";
2276
2277
  labelPrefix;
2277
2278
  schemaVersionLatest = 2;
2279
+ emitProgress;
2280
+ opStart(op, args) {
2281
+ try {
2282
+ this.emitProgress?.({ type: "graph/neo4j/op/start", op, repoId: args?.repoId, path: args?.path });
2283
+ } catch {
2284
+ }
2285
+ }
2286
+ opDone(op, startedAt, args) {
2287
+ try {
2288
+ this.emitProgress?.({ type: "graph/neo4j/op/done", op, repoId: args?.repoId, path: args?.path, ms: Date.now() - startedAt });
2289
+ } catch {
2290
+ }
2291
+ }
2278
2292
  labels() {
2279
2293
  return {
2280
2294
  Repo: lp(this.labelPrefix, "Repo"),
@@ -2285,6 +2299,8 @@ var Neo4jGraphStore = class {
2285
2299
  };
2286
2300
  }
2287
2301
  async init() {
2302
+ const startedAt = Date.now();
2303
+ this.opStart("init");
2288
2304
  const { Schema } = this.labels();
2289
2305
  await runSession(this.driver, this.cfg.database, async (s) => {
2290
2306
  await s.run(
@@ -2295,6 +2311,7 @@ var Neo4jGraphStore = class {
2295
2311
  );
2296
2312
  });
2297
2313
  await this.runMigrations().catch(() => void 0);
2314
+ this.opDone("init", startedAt);
2298
2315
  }
2299
2316
  async getSchemaVersion() {
2300
2317
  const { Schema } = this.labels();
@@ -2318,6 +2335,8 @@ var Neo4jGraphStore = class {
2318
2335
  });
2319
2336
  }
2320
2337
  async runMigrations() {
2338
+ const startedAt = Date.now();
2339
+ this.opStart("migrations");
2321
2340
  const { Repo, File, Symbol: Symbol2, External } = this.labels();
2322
2341
  let v = await this.getSchemaVersion();
2323
2342
  if (v < 1) {
@@ -2358,8 +2377,11 @@ var Neo4jGraphStore = class {
2358
2377
  if (v !== this.schemaVersionLatest) {
2359
2378
  await this.setSchemaVersion(this.schemaVersionLatest);
2360
2379
  }
2380
+ this.opDone("migrations", startedAt);
2361
2381
  }
2362
2382
  async setRepoHead(args) {
2383
+ const startedAt = Date.now();
2384
+ this.opStart("setRepoHead", { repoId: args.repoId });
2363
2385
  const { Repo, File, Symbol: Symbol2, External } = this.labels();
2364
2386
  const { repoId, repoRoot, commit, branch } = args;
2365
2387
  await runSession(this.driver, this.cfg.database, async (s) => {
@@ -2390,15 +2412,21 @@ var Neo4jGraphStore = class {
2390
2412
  await s.run(`MATCH (r:${Repo} {repo_id:$repoId}) SET r.commit=$commit`, { repoId, commit });
2391
2413
  }
2392
2414
  });
2415
+ this.opDone("setRepoHead", startedAt, { repoId: args.repoId });
2393
2416
  }
2394
2417
  async deleteFile(args) {
2418
+ const startedAt = Date.now();
2419
+ this.opStart("deleteFile", { repoId: args.repoId, path: args.path });
2395
2420
  const { File, Symbol: Symbol2 } = this.labels();
2396
2421
  await runSession(this.driver, this.cfg.database, async (s) => {
2397
2422
  await s.run(`MATCH (s:${Symbol2} {repo_id:$repoId, path:$path}) DETACH DELETE s`, args);
2398
2423
  await s.run(`MATCH (f:${File} {repo_id:$repoId, path:$path}) DETACH DELETE f`, args);
2399
2424
  });
2425
+ this.opDone("deleteFile", startedAt, { repoId: args.repoId, path: args.path });
2400
2426
  }
2401
2427
  async replaceOutgoingSymbolEdgesFromFile(args) {
2428
+ const startedAt = Date.now();
2429
+ this.opStart("replaceOutgoingSymbolEdgesFromFile", { repoId: args.repoId, path: args.fromPath });
2402
2430
  const { Symbol: Symbol2 } = this.labels();
2403
2431
  const kinds = args.edges.length > 0 ? Array.from(new Set(args.edges.map((e) => e.kind))) : ["definition", "reference", "implementation", "typeDefinition"];
2404
2432
  const keep = args.edges.map((e) => `${e.fromId}|${e.kind}|${e.toId}`);
@@ -2430,8 +2458,11 @@ var Neo4jGraphStore = class {
2430
2458
  { repoId: args.repoId, fromPath: args.fromPath, kinds, keep }
2431
2459
  ).catch(() => void 0);
2432
2460
  });
2461
+ this.opDone("replaceOutgoingSymbolEdgesFromFile", startedAt, { repoId: args.repoId, path: args.fromPath });
2433
2462
  }
2434
2463
  async replaceFileGraph(update) {
2464
+ const startedAt = Date.now();
2465
+ this.opStart("replaceFileGraph", { repoId: update.repoId, path: update.path });
2435
2466
  const { Repo, File, Symbol: Symbol2, External } = this.labels();
2436
2467
  const symbols = update.symbols.map((s) => ({
2437
2468
  id: s.id,
@@ -2543,8 +2574,11 @@ var Neo4jGraphStore = class {
2543
2574
  }
2544
2575
  }
2545
2576
  });
2577
+ this.opDone("replaceFileGraph", startedAt, { repoId: update.repoId, path: update.path });
2546
2578
  }
2547
2579
  async replaceRepoLinks(args) {
2580
+ const startedAt = Date.now();
2581
+ this.opStart("replaceRepoLinks");
2548
2582
  const { Repo } = this.labels();
2549
2583
  const byFrom = /* @__PURE__ */ new Map();
2550
2584
  for (const l of args.links) {
@@ -2575,8 +2609,11 @@ var Neo4jGraphStore = class {
2575
2609
  ).catch(() => void 0);
2576
2610
  });
2577
2611
  }
2612
+ this.opDone("replaceRepoLinks", startedAt);
2578
2613
  }
2579
2614
  async neighborFiles(args) {
2615
+ const startedAt = Date.now();
2616
+ this.opStart("neighborFiles");
2580
2617
  const { File, Symbol: Symbol2 } = this.labels();
2581
2618
  const limit = args.limit ?? 20;
2582
2619
  const kinds = args.kinds && args.kinds.length > 0 ? args.kinds : null;
@@ -2600,9 +2637,12 @@ var Neo4jGraphStore = class {
2600
2637
  rows.push({ repoId, path: p, weight: Number.isFinite(w) ? w : 0 });
2601
2638
  }
2602
2639
  });
2640
+ this.opDone("neighborFiles", startedAt);
2603
2641
  return rows;
2604
2642
  }
2605
2643
  async shortestFilePath(args) {
2644
+ const startedAt = Date.now();
2645
+ this.opStart("shortestFilePath");
2606
2646
  const { File } = this.labels();
2607
2647
  const maxRels = args.maxRels ?? 16;
2608
2648
  let out = null;
@@ -2618,9 +2658,12 @@ var Neo4jGraphStore = class {
2618
2658
  if (!Array.isArray(files)) return;
2619
2659
  out = files.map((x) => ({ repoId: String(x.repoId), path: String(x.path) }));
2620
2660
  });
2661
+ this.opDone("shortestFilePath", startedAt);
2621
2662
  return out;
2622
2663
  }
2623
2664
  async extractFileSubgraph(args) {
2665
+ const startedAt = Date.now();
2666
+ this.opStart("extractFileSubgraph");
2624
2667
  const { File, Symbol: Symbol2 } = this.labels();
2625
2668
  const limitEdges = args.limitEdges ?? 200;
2626
2669
  const nodes = /* @__PURE__ */ new Map();
@@ -2642,6 +2685,7 @@ var Neo4jGraphStore = class {
2642
2685
  edges.push({ from, to, kind });
2643
2686
  }
2644
2687
  });
2688
+ this.opDone("extractFileSubgraph", startedAt);
2645
2689
  return { nodes: Array.from(nodes.values()), edges };
2646
2690
  }
2647
2691
  async close() {
@@ -3408,6 +3452,16 @@ var RepoFileIndexer = class {
3408
3452
  return;
3409
3453
  }
3410
3454
  this.emit({ type: "repo/index/file/start", repoRoot: this.repoRoot, path: posixRelPath });
3455
+ const stage = (stageName, stageStartedAt) => {
3456
+ this.emit({
3457
+ type: "repo/index/file/stage",
3458
+ repoRoot: this.repoRoot,
3459
+ path: posixRelPath,
3460
+ stage: stageName,
3461
+ ms: Date.now() - stageStartedAt
3462
+ });
3463
+ };
3464
+ const readStartedAt = Date.now();
3411
3465
  const abs = import_node_path14.default.join(this.repoRoot, fromPosixPath(posixRelPath));
3412
3466
  let stat;
3413
3467
  try {
@@ -3431,16 +3485,20 @@ var RepoFileIndexer = class {
3431
3485
  }
3432
3486
  const raw = buf.toString("utf8");
3433
3487
  const redacted = this.applyRedactions(raw);
3488
+ stage("read", readStartedAt);
3434
3489
  const fileHash = sha256Hex(redacted);
3435
3490
  const prev = this.store.getFileHash(posixRelPath);
3436
3491
  if (prev === fileHash) {
3437
3492
  this.emit({ type: "repo/index/file/skip", repoRoot: this.repoRoot, path: posixRelPath, reason: "unchanged" });
3438
3493
  return;
3439
3494
  }
3495
+ const chunkStartedAt = Date.now();
3440
3496
  const { language, chunks } = chunkSource(posixRelPath, redacted, this.config.chunk);
3497
+ stage("chunk", chunkStartedAt);
3441
3498
  let imports = [];
3442
3499
  let exports2 = [];
3443
3500
  if (language === "typescript" || language === "javascript") {
3501
+ const relationsStartedAt = Date.now();
3444
3502
  const rel = extractTsRelations(posixRelPath, redacted);
3445
3503
  imports = rel.imports;
3446
3504
  exports2 = rel.exports;
@@ -3448,12 +3506,14 @@ var RepoFileIndexer = class {
3448
3506
  this.store.setEdges(posixRelPath, "export", rel.exports);
3449
3507
  this.workspaceStore?.setEdges(this.repoId, posixRelPath, "import", rel.imports);
3450
3508
  this.workspaceStore?.setEdges(this.repoId, posixRelPath, "export", rel.exports);
3509
+ stage("relations", relationsStartedAt);
3451
3510
  } else {
3452
3511
  this.store.setEdges(posixRelPath, "import", []);
3453
3512
  this.store.setEdges(posixRelPath, "export", []);
3454
3513
  this.workspaceStore?.setEdges(this.repoId, posixRelPath, "import", []);
3455
3514
  this.workspaceStore?.setEdges(this.repoId, posixRelPath, "export", []);
3456
3515
  }
3516
+ const synopsisStartedAt = Date.now();
3457
3517
  const synopsisText = buildSynopsis(posixRelPath, language, redacted);
3458
3518
  const synopsis = synopsisText.trim() ? [
3459
3519
  {
@@ -3478,14 +3538,29 @@ var RepoFileIndexer = class {
3478
3538
  }
3479
3539
  ] : [];
3480
3540
  const combined = [...synopsis, ...headerChunk, ...chunks.map((c) => ({ ...c, kind: "chunk" }))];
3541
+ stage("synopsis", synopsisStartedAt);
3542
+ const embedStartedAt = Date.now();
3481
3543
  const embedTexts = [];
3482
3544
  const embedPlan = [];
3483
3545
  const embeddings = combined.map(() => null);
3546
+ let cached2 = 0;
3484
3547
  for (let i = 0; i < combined.length; i++) {
3485
3548
  const ch = combined[i];
3486
- const cached2 = this.embeddingCache.get(this.embedder.id, ch.contentHash);
3487
- if (cached2) {
3488
- embeddings[i] = cached2;
3549
+ const cachedEmb = this.embeddingCache.get(this.embedder.id, ch.contentHash);
3550
+ if (cachedEmb) {
3551
+ embeddings[i] = cachedEmb;
3552
+ cached2++;
3553
+ this.emit({
3554
+ type: "repo/index/file/embed/chunk",
3555
+ repoRoot: this.repoRoot,
3556
+ path: posixRelPath,
3557
+ chunkIndex: i,
3558
+ chunksTotal: combined.length,
3559
+ kind: ch.kind,
3560
+ startLine: ch.startLine,
3561
+ endLine: ch.endLine,
3562
+ cached: true
3563
+ });
3489
3564
  continue;
3490
3565
  }
3491
3566
  embedTexts.push(
@@ -3497,7 +3572,7 @@ lines:${ch.startLine}-${ch.endLine}
3497
3572
  ---
3498
3573
  ${ch.text}`
3499
3574
  );
3500
- embedPlan.push({ chunkIdx: i, contentHash: ch.contentHash });
3575
+ embedPlan.push({ chunkIdx: i, contentHash: ch.contentHash, kind: ch.kind, startLine: ch.startLine, endLine: ch.endLine });
3501
3576
  }
3502
3577
  const batchSize = this.config.embed.batchSize;
3503
3578
  for (let start = 0; start < embedTexts.length; start += batchSize) {
@@ -3515,13 +3590,35 @@ ${ch.text}`
3515
3590
  const plan = embedPlan[start + j];
3516
3591
  embeddings[plan.chunkIdx] = vecs[j];
3517
3592
  this.embeddingCache.put(this.embedder.id, plan.contentHash, vecs[j]);
3593
+ this.emit({
3594
+ type: "repo/index/file/embed/chunk",
3595
+ repoRoot: this.repoRoot,
3596
+ path: posixRelPath,
3597
+ chunkIndex: plan.chunkIdx,
3598
+ chunksTotal: combined.length,
3599
+ kind: plan.kind,
3600
+ startLine: plan.startLine,
3601
+ endLine: plan.endLine,
3602
+ cached: false
3603
+ });
3518
3604
  }
3519
3605
  }
3606
+ this.emit({
3607
+ type: "repo/index/file/embed/done",
3608
+ repoRoot: this.repoRoot,
3609
+ path: posixRelPath,
3610
+ chunks: combined.length,
3611
+ cached: cached2,
3612
+ computed: embedTexts.length,
3613
+ ms: Date.now() - embedStartedAt
3614
+ });
3615
+ stage("embed", embedStartedAt);
3520
3616
  const fileMtime = stat.mtimeMs;
3521
3617
  const ftsMode = this.config.storage.ftsMode;
3522
3618
  const storeText = this.config.storage.storeText;
3523
3619
  const oldChunkIds = this.store.listChunksForFile(posixRelPath).map((r) => r.id);
3524
3620
  const points = [];
3621
+ const storeStartedAt = Date.now();
3525
3622
  const rows = combined.map((ch, i) => {
3526
3623
  const id = sha256Hex(
3527
3624
  `${this.repoId}:${posixRelPath}:${ch.kind}:${ch.startLine}:${ch.endLine}:${ch.contentHash}`
@@ -3549,7 +3646,22 @@ ${ch.text}`
3549
3646
  this.store.replaceChunksForFile(posixRelPath, rows);
3550
3647
  this.workspaceStore?.upsertFile(this.repoId, posixRelPath, fileHash, fileMtime, language, stat.size);
3551
3648
  this.workspaceStore?.replaceChunksForFile(this.repoId, this.repoRoot, posixRelPath, rows);
3649
+ stage("store", storeStartedAt);
3650
+ const symbolsStartedAt = Date.now();
3651
+ if (this.symbolGraphProvider && this.symbolGraphProvider.supports(language)) {
3652
+ this.emit({ type: "repo/index/file/symbols/start", repoRoot: this.repoRoot, path: posixRelPath, providerId: this.symbolGraphProvider.id });
3653
+ }
3552
3654
  const symbolOut = await this.indexSymbolsIfEnabled(posixRelPath, language, redacted, fileHash);
3655
+ this.emit({
3656
+ type: "repo/index/file/symbols",
3657
+ repoRoot: this.repoRoot,
3658
+ path: posixRelPath,
3659
+ symbols: symbolOut?.symbols?.length ?? 0,
3660
+ edges: symbolOut?.edges?.length ?? 0,
3661
+ ms: Date.now() - symbolsStartedAt
3662
+ });
3663
+ stage("symbols", symbolsStartedAt);
3664
+ const graphStartedAt = Date.now();
3553
3665
  const head = this.getHead();
3554
3666
  if (this.graphStore && head) {
3555
3667
  await this.graphStore.replaceFileGraph({
@@ -3565,7 +3677,10 @@ ${ch.text}`
3565
3677
  symbolEdges: (symbolOut?.edges ?? []).map((e) => ({ fromId: e.fromId, toId: e.toId, kind: e.kind }))
3566
3678
  }).catch(() => void 0);
3567
3679
  }
3680
+ stage("graph", graphStartedAt);
3681
+ const vectorStartedAt = Date.now();
3568
3682
  await this.ensureVectorUpToDate(points, oldChunkIds);
3683
+ stage("vector", vectorStartedAt);
3569
3684
  this.emit({
3570
3685
  type: "repo/index/file/done",
3571
3686
  repoRoot: this.repoRoot,
@@ -4176,28 +4291,46 @@ var RepoIndexer = class {
4176
4291
  const uniq3 = Array.from(new Set(posixPaths)).slice(0, maxFiles);
4177
4292
  for (const p of uniq3) {
4178
4293
  if (opts?.signal?.aborted) return;
4294
+ const startedAt = Date.now();
4295
+ this.emitProgress({ type: "repo/symbolGraph/expand/start", repoRoot: this.repoRoot, path: p });
4179
4296
  const abs = import_node_path17.default.join(this.repoRoot, p.split("/").join(import_node_path17.default.sep));
4180
4297
  let text = "";
4181
4298
  try {
4182
4299
  text = import_node_fs12.default.readFileSync(abs, "utf8");
4183
4300
  } catch {
4301
+ this.emitProgress({ type: "repo/symbolGraph/expand/done", repoRoot: this.repoRoot, path: p, edges: 0, ms: Date.now() - startedAt });
4184
4302
  continue;
4185
4303
  }
4186
4304
  const contentHash = this.store.getFileHash(p) ?? void 0;
4187
4305
  const lang = languageFromPath(p);
4188
4306
  const edges = await provider.expandDocumentEdges({ repoRoot: this.repoRoot, path: p, language: lang, text, contentHash }, { signal: opts?.signal }).catch(() => []);
4189
4307
  const normalized = (Array.isArray(edges) ? edges : []).map((e) => ({
4190
- fromId: e.fromId,
4191
- toId: e.toId,
4192
- kind: e.kind,
4193
- toPath: String(e.toPath ?? "")
4308
+ fromId: String(e?.fromId ?? ""),
4309
+ toId: String(e?.toId ?? ""),
4310
+ kind: String(e?.kind ?? ""),
4311
+ toPath: String(e?.toPath ?? "")
4194
4312
  }));
4313
+ const byKind = {};
4314
+ for (const e of normalized) {
4315
+ const kind = e.kind;
4316
+ if (kind === "definition" || kind === "reference" || kind === "implementation" || kind === "typeDefinition") {
4317
+ byKind[kind] = (byKind[kind] ?? 0) + 1;
4318
+ }
4319
+ }
4195
4320
  this.workspaceStore.replaceSymbolEdgesFromFile(this.repoId, p, normalized);
4196
4321
  await this.graphStore?.replaceOutgoingSymbolEdgesFromFile?.({
4197
4322
  repoId: this.repoId,
4198
4323
  fromPath: p,
4199
4324
  edges: normalized.map((e) => ({ fromId: e.fromId, toId: e.toId, kind: e.kind, toPath: e.toPath }))
4200
4325
  }).catch(() => void 0);
4326
+ this.emitProgress({
4327
+ type: "repo/symbolGraph/expand/done",
4328
+ repoRoot: this.repoRoot,
4329
+ path: p,
4330
+ edges: normalized.length,
4331
+ byKind,
4332
+ ms: Date.now() - startedAt
4333
+ });
4201
4334
  }
4202
4335
  }
4203
4336
  async watch() {
@@ -4587,7 +4720,8 @@ var WorkspaceIndexer = class {
4587
4720
  user: n.user,
4588
4721
  password: n.password,
4589
4722
  database: n.database,
4590
- labelPrefix: n.labelPrefix
4723
+ labelPrefix: n.labelPrefix,
4724
+ emit: (e) => this.emitProgress(e)
4591
4725
  });
4592
4726
  } catch (e) {
4593
4727
  this.emitProgress({
@@ -4784,11 +4918,14 @@ var WorkspaceIndexer = class {
4784
4918
  if (seeds.length >= 4) break;
4785
4919
  }
4786
4920
  if (seeds.length > 0) {
4921
+ const gs = Date.now();
4922
+ this.emitProgress({ type: "workspace/retrieve/graph/start", workspaceRoot: this.workspaceRoot, seeds: seeds.length });
4787
4923
  graphNeighborFiles = await this.graphStore.neighborFiles({
4788
4924
  seeds,
4789
4925
  limit: profile.name === "architecture" ? 16 : 10,
4790
4926
  kinds: ["definition", "reference", "implementation", "typeDefinition"]
4791
4927
  });
4928
+ this.emitProgress({ type: "workspace/retrieve/graph/done", workspaceRoot: this.workspaceRoot, neighbors: graphNeighborFiles.length, ms: Date.now() - gs });
4792
4929
  }
4793
4930
  }
4794
4931
  } catch {
package/dist/index.d.cts CHANGED
@@ -253,6 +253,15 @@ type IndexerProgressEvent = {
253
253
  lexical: number;
254
254
  merged: number;
255
255
  };
256
+ } | {
257
+ type: "workspace/retrieve/graph/start";
258
+ workspaceRoot: string;
259
+ seeds: number;
260
+ } | {
261
+ type: "workspace/retrieve/graph/done";
262
+ workspaceRoot: string;
263
+ ms: number;
264
+ neighbors: number;
256
265
  } | {
257
266
  type: "repo/open";
258
267
  repoRoot: string;
@@ -267,11 +276,47 @@ type IndexerProgressEvent = {
267
276
  type: "repo/index/file/start";
268
277
  repoRoot: string;
269
278
  path: string;
279
+ } | {
280
+ type: "repo/index/file/stage";
281
+ repoRoot: string;
282
+ path: string;
283
+ stage: "read" | "chunk" | "relations" | "synopsis" | "embed" | "store" | "symbols" | "graph" | "vector";
284
+ ms?: number;
270
285
  } | {
271
286
  type: "repo/index/file/skip";
272
287
  repoRoot: string;
273
288
  path: string;
274
289
  reason: string;
290
+ } | {
291
+ type: "repo/index/file/embed/chunk";
292
+ repoRoot: string;
293
+ path: string;
294
+ chunkIndex: number;
295
+ chunksTotal: number;
296
+ kind: "chunk" | "synopsis";
297
+ startLine: number;
298
+ endLine: number;
299
+ cached: boolean;
300
+ } | {
301
+ type: "repo/index/file/embed/done";
302
+ repoRoot: string;
303
+ path: string;
304
+ chunks: number;
305
+ cached: number;
306
+ computed: number;
307
+ ms: number;
308
+ } | {
309
+ type: "repo/index/file/symbols/start";
310
+ repoRoot: string;
311
+ path: string;
312
+ providerId: string;
313
+ } | {
314
+ type: "repo/index/file/symbols";
315
+ repoRoot: string;
316
+ path: string;
317
+ symbols: number;
318
+ edges: number;
319
+ ms: number;
275
320
  } | {
276
321
  type: "repo/index/file/done";
277
322
  repoRoot: string;
@@ -306,6 +351,17 @@ type IndexerProgressEvent = {
306
351
  type: "repo/vector/flush";
307
352
  repoRoot: string;
308
353
  kind: string;
354
+ } | {
355
+ type: "repo/symbolGraph/expand/start";
356
+ repoRoot: string;
357
+ path: string;
358
+ } | {
359
+ type: "repo/symbolGraph/expand/done";
360
+ repoRoot: string;
361
+ path: string;
362
+ edges: number;
363
+ byKind?: Partial<Record<"definition" | "reference" | "implementation" | "typeDefinition", number>>;
364
+ ms: number;
309
365
  } | {
310
366
  type: "repo/watch/start";
311
367
  repoRoot: string;
@@ -319,6 +375,17 @@ type IndexerProgressEvent = {
319
375
  repoRoot: string;
320
376
  fileCount: number;
321
377
  ms: number;
378
+ } | {
379
+ type: "graph/neo4j/op/start";
380
+ op: string;
381
+ repoId?: string;
382
+ path?: string;
383
+ } | {
384
+ type: "graph/neo4j/op/done";
385
+ op: string;
386
+ repoId?: string;
387
+ path?: string;
388
+ ms: number;
322
389
  } | {
323
390
  type: "error";
324
391
  scope: "workspace" | "repo";
@@ -984,6 +1051,7 @@ type Neo4jConfig = {
984
1051
  password: string;
985
1052
  database?: string;
986
1053
  labelPrefix?: string;
1054
+ emit?: (e: IndexerProgressEvent) => void;
987
1055
  };
988
1056
  declare class Neo4jGraphStore implements GraphStore {
989
1057
  private driver;
@@ -991,7 +1059,10 @@ declare class Neo4jGraphStore implements GraphStore {
991
1059
  id: string;
992
1060
  private labelPrefix;
993
1061
  private schemaVersionLatest;
1062
+ private emitProgress;
994
1063
  constructor(driver: Neo4jLikeDriver, cfg: Neo4jConfig);
1064
+ private opStart;
1065
+ private opDone;
995
1066
  private labels;
996
1067
  init(): Promise<void>;
997
1068
  private getSchemaVersion;
package/dist/index.d.ts CHANGED
@@ -253,6 +253,15 @@ type IndexerProgressEvent = {
253
253
  lexical: number;
254
254
  merged: number;
255
255
  };
256
+ } | {
257
+ type: "workspace/retrieve/graph/start";
258
+ workspaceRoot: string;
259
+ seeds: number;
260
+ } | {
261
+ type: "workspace/retrieve/graph/done";
262
+ workspaceRoot: string;
263
+ ms: number;
264
+ neighbors: number;
256
265
  } | {
257
266
  type: "repo/open";
258
267
  repoRoot: string;
@@ -267,11 +276,47 @@ type IndexerProgressEvent = {
267
276
  type: "repo/index/file/start";
268
277
  repoRoot: string;
269
278
  path: string;
279
+ } | {
280
+ type: "repo/index/file/stage";
281
+ repoRoot: string;
282
+ path: string;
283
+ stage: "read" | "chunk" | "relations" | "synopsis" | "embed" | "store" | "symbols" | "graph" | "vector";
284
+ ms?: number;
270
285
  } | {
271
286
  type: "repo/index/file/skip";
272
287
  repoRoot: string;
273
288
  path: string;
274
289
  reason: string;
290
+ } | {
291
+ type: "repo/index/file/embed/chunk";
292
+ repoRoot: string;
293
+ path: string;
294
+ chunkIndex: number;
295
+ chunksTotal: number;
296
+ kind: "chunk" | "synopsis";
297
+ startLine: number;
298
+ endLine: number;
299
+ cached: boolean;
300
+ } | {
301
+ type: "repo/index/file/embed/done";
302
+ repoRoot: string;
303
+ path: string;
304
+ chunks: number;
305
+ cached: number;
306
+ computed: number;
307
+ ms: number;
308
+ } | {
309
+ type: "repo/index/file/symbols/start";
310
+ repoRoot: string;
311
+ path: string;
312
+ providerId: string;
313
+ } | {
314
+ type: "repo/index/file/symbols";
315
+ repoRoot: string;
316
+ path: string;
317
+ symbols: number;
318
+ edges: number;
319
+ ms: number;
275
320
  } | {
276
321
  type: "repo/index/file/done";
277
322
  repoRoot: string;
@@ -306,6 +351,17 @@ type IndexerProgressEvent = {
306
351
  type: "repo/vector/flush";
307
352
  repoRoot: string;
308
353
  kind: string;
354
+ } | {
355
+ type: "repo/symbolGraph/expand/start";
356
+ repoRoot: string;
357
+ path: string;
358
+ } | {
359
+ type: "repo/symbolGraph/expand/done";
360
+ repoRoot: string;
361
+ path: string;
362
+ edges: number;
363
+ byKind?: Partial<Record<"definition" | "reference" | "implementation" | "typeDefinition", number>>;
364
+ ms: number;
309
365
  } | {
310
366
  type: "repo/watch/start";
311
367
  repoRoot: string;
@@ -319,6 +375,17 @@ type IndexerProgressEvent = {
319
375
  repoRoot: string;
320
376
  fileCount: number;
321
377
  ms: number;
378
+ } | {
379
+ type: "graph/neo4j/op/start";
380
+ op: string;
381
+ repoId?: string;
382
+ path?: string;
383
+ } | {
384
+ type: "graph/neo4j/op/done";
385
+ op: string;
386
+ repoId?: string;
387
+ path?: string;
388
+ ms: number;
322
389
  } | {
323
390
  type: "error";
324
391
  scope: "workspace" | "repo";
@@ -984,6 +1051,7 @@ type Neo4jConfig = {
984
1051
  password: string;
985
1052
  database?: string;
986
1053
  labelPrefix?: string;
1054
+ emit?: (e: IndexerProgressEvent) => void;
987
1055
  };
988
1056
  declare class Neo4jGraphStore implements GraphStore {
989
1057
  private driver;
@@ -991,7 +1059,10 @@ declare class Neo4jGraphStore implements GraphStore {
991
1059
  id: string;
992
1060
  private labelPrefix;
993
1061
  private schemaVersionLatest;
1062
+ private emitProgress;
994
1063
  constructor(driver: Neo4jLikeDriver, cfg: Neo4jConfig);
1064
+ private opStart;
1065
+ private opDone;
995
1066
  private labels;
996
1067
  init(): Promise<void>;
997
1068
  private getSchemaVersion;
package/dist/index.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  mergeIndexerConfig,
28
28
  pickRepoOverride,
29
29
  stableSymbolId
30
- } from "./chunk-IXKSW7IX.js";
30
+ } from "./chunk-FUUQXFJQ.js";
31
31
 
32
32
  // src/symbolGraph/vscodeProvider.ts
33
33
  import path2 from "path";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neuralsea/workspace-indexer",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Local-first multi-repo workspace indexer (semantic embeddings + git-aware incremental updates + hybrid retrieval profiles) for AI agents.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",