@proxysoul/soulforge 1.6.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -57814,7 +57814,7 @@ var package_default;
57814
57814
  var init_package = __esm(() => {
57815
57815
  package_default = {
57816
57816
  name: "@proxysoul/soulforge",
57817
- version: "1.6.2",
57817
+ version: "1.7.0",
57818
57818
  description: "Graph-powered code intelligence \u2014 multi-agent coding with codebase-aware AI",
57819
57819
  repository: {
57820
57820
  type: "git",
@@ -312475,6 +312475,8 @@ class RepoMap {
312475
312475
  );
312476
312476
  CREATE INDEX IF NOT EXISTS idx_refs_file ON refs(file_id);
312477
312477
  CREATE INDEX IF NOT EXISTS idx_refs_name ON refs(name);
312478
+ CREATE INDEX IF NOT EXISTS idx_refs_source ON refs(source_file_id);
312479
+ CREATE INDEX IF NOT EXISTS idx_refs_import ON refs(import_source);
312478
312480
  `);
312479
312481
  try {
312480
312482
  this.db.run("ALTER TABLE refs ADD COLUMN source_file_id INTEGER REFERENCES files(id) ON DELETE CASCADE");
@@ -312724,7 +312726,9 @@ class RepoMap {
312724
312726
  }
312725
312727
  this.onProgress?.(toIndex.length, toIndex.length);
312726
312728
  }
312727
- if (toIndex.length > 0 || stale.length > 0) {
312729
+ const edgeCount = this.db.query("SELECT COUNT(*) as c FROM edges").get()?.c ?? 0;
312730
+ const needsPostIndexing = toIndex.length > 0 || stale.length > 0 || edgeCount === 0;
312731
+ if (needsPostIndexing) {
312728
312732
  this.onProgress?.(-1, -1);
312729
312733
  await tick();
312730
312734
  await this.resolveUnresolvedRefs();
@@ -312737,6 +312741,7 @@ class RepoMap {
312737
312741
  this.onProgress?.(-3, -3);
312738
312742
  await tick();
312739
312743
  await this.buildEdges();
312744
+ this.onProgress?.(-4, -4);
312740
312745
  this.linkTestFiles();
312741
312746
  this.rescueOrphans();
312742
312747
  this.onProgress?.(-4, -4);
@@ -313359,6 +313364,8 @@ class RepoMap {
313359
313364
  addEdge(row.source_file_id, row.target_file_id, Math.sqrt(row.ref_count) * 3, 3);
313360
313365
  if (i2 % 500 === 499)
313361
313366
  await tick();
313367
+ if (i2 % 2000 === 0)
313368
+ this.onProgress?.(-3, -3);
313362
313369
  }
313363
313370
  const refDfMap = new Map;
313364
313371
  const dfRows = this.db.query("SELECT name, COUNT(DISTINCT file_id) AS ref_df FROM refs GROUP BY name").all();
@@ -313389,20 +313396,26 @@ class RepoMap {
313389
313396
  addEdge(row.source_file_id, row.target_file_id, w, 1);
313390
313397
  if (i2 % 500 === 499)
313391
313398
  await tick();
313399
+ if (i2 % 2000 === 0)
313400
+ this.onProgress?.(-3, -3);
313392
313401
  }
313402
+ this.onProgress?.(-3, -3);
313393
313403
  await tick();
313404
+ const uniqueExportNames = new Set;
313405
+ const exportNameCounts = this.db.query("SELECT name, COUNT(DISTINCT file_id) as fc FROM symbols WHERE is_exported = 1 GROUP BY name HAVING fc = 1").all();
313406
+ for (const row of exportNameCounts)
313407
+ uniqueExportNames.add(row.name);
313394
313408
  const inferredRows = this.db.query(`SELECT r.file_id AS source_file_id, s.file_id AS target_file_id,
313395
- r.name, COUNT(*) AS ref_count,
313396
- (SELECT COUNT(DISTINCT s2.file_id) FROM symbols s2
313397
- WHERE s2.name = r.name AND s2.is_exported = 1) AS def_count
313409
+ r.name, COUNT(*) AS ref_count
313398
313410
  FROM refs r
313399
313411
  JOIN symbols s ON r.name = s.name AND s.is_exported = 1
313400
313412
  WHERE r.source_file_id IS NULL AND r.import_source IS NULL
313401
313413
  AND r.file_id != s.file_id
313402
- GROUP BY r.file_id, s.file_id, r.name
313403
- HAVING def_count = 1`).all();
313414
+ GROUP BY r.file_id, s.file_id, r.name`).all();
313404
313415
  for (let i2 = 0;i2 < inferredRows.length; i2++) {
313405
313416
  const row = inferredRows[i2];
313417
+ if (!uniqueExportNames.has(row.name))
313418
+ continue;
313406
313419
  const refDf = refDfMap.get(row.name) ?? 1;
313407
313420
  const idf = Math.log((totalFiles - refDf + 0.5) / (refDf + 0.5));
313408
313421
  if (idf <= 0)
@@ -313416,6 +313429,8 @@ class RepoMap {
313416
313429
  addEdge(row.source_file_id, row.target_file_id, w, 1);
313417
313430
  if (i2 % 500 === 499)
313418
313431
  await tick();
313432
+ if (i2 % 2000 === 0)
313433
+ this.onProgress?.(-3, -3);
313419
313434
  }
313420
313435
  const cochangeRows = this.db.query("SELECT file_id_a, file_id_b, count FROM cochanges WHERE count >= 3").all();
313421
313436
  for (const row of cochangeRows) {
@@ -313472,6 +313487,8 @@ class RepoMap {
313472
313487
  try {
313473
313488
  tx();
313474
313489
  } catch {}
313490
+ if (i2 % 2000 === 0)
313491
+ this.onProgress?.(-3, -3);
313475
313492
  if (i2 + BATCH < entries.length)
313476
313493
  await tick();
313477
313494
  }
@@ -313815,7 +313832,9 @@ class RepoMap {
313815
313832
  }
313816
313833
  const pairCounts = new Map;
313817
313834
  const commits = logOutput.split("---COMMIT---").filter((s) => s.trim());
313818
- for (const commit of commits) {
313835
+ const tick = () => new Promise((r) => setTimeout(r, 1));
313836
+ for (let ci = 0;ci < commits.length; ci++) {
313837
+ const commit = commits[ci];
313819
313838
  const files = commit.split(`
313820
313839
  `).map((l) => l.trim()).filter((l) => l && pathToId.has(l));
313821
313840
  if (files.length < 2 || files.length > MAX_COCHANGE_FILES_PER_COMMIT)
@@ -313828,23 +313847,33 @@ class RepoMap {
313828
313847
  pairCounts.set(key, (pairCounts.get(key) ?? 0) + 1);
313829
313848
  }
313830
313849
  }
313850
+ if (ci % 50 === 0) {
313851
+ this.onProgress?.(-5, -5);
313852
+ await tick();
313853
+ }
313831
313854
  }
313832
313855
  if (pairCounts.size === 0)
313833
313856
  return;
313857
+ this.onProgress?.(-5, -5);
313834
313858
  const insert = this.db.prepare("INSERT OR REPLACE INTO cochanges (file_id_a, file_id_b, count) VALUES (?, ?, ?)");
313835
- const tx = this.db.transaction(() => {
313836
- for (const [key, count] of pairCounts) {
313837
- if (count < 2)
313838
- continue;
313839
- const [a, b] = key.split("\x00");
313840
- const idA = pathToId.get(a);
313841
- const idB = pathToId.get(b);
313842
- if (idA !== undefined && idB !== undefined) {
313843
- insert.run(idA, idB, count);
313859
+ const BATCH = 5000;
313860
+ const entries = [...pairCounts.entries()].filter(([, count]) => count >= 2);
313861
+ for (let i2 = 0;i2 < entries.length; i2 += BATCH) {
313862
+ const batch = entries.slice(i2, i2 + BATCH);
313863
+ const tx = this.db.transaction(() => {
313864
+ for (const [key, count] of batch) {
313865
+ const [a, b] = key.split("\x00");
313866
+ const idA = pathToId.get(a);
313867
+ const idB = pathToId.get(b);
313868
+ if (idA !== undefined && idB !== undefined) {
313869
+ insert.run(idA, idB, count);
313870
+ }
313844
313871
  }
313845
- }
313846
- });
313847
- tx();
313872
+ });
313873
+ tx();
313874
+ if (i2 % 1e4 === 0)
313875
+ this.onProgress?.(-5, -5);
313876
+ }
313848
313877
  }
313849
313878
  getCoChangePartners(fileIds) {
313850
313879
  if (fileIds.size === 0)
@@ -360768,7 +360797,7 @@ var init_intelligence_client = __esm(() => {
360768
360797
  onProgress = null;
360769
360798
  onScanComplete = null;
360770
360799
  onStaleSymbols = null;
360771
- static SCAN_IDLE_TIMEOUT = 120000;
360800
+ static SCAN_IDLE_TIMEOUT = 600000;
360772
360801
  constructor(cwd2) {
360773
360802
  const workerPath = IS_COMPILED2 ? join27(homedir17(), ".soulforge", "workers", "intelligence.worker.js") : IS_DIST2 ? join27(import.meta.dir, "workers", "intelligence.worker.js") : join27(import.meta.dir, "intelligence.worker.ts");
360774
360803
  super(workerPath, {
@@ -361350,9 +361379,11 @@ class ContextManager {
361350
361379
  this.repoMap.onProgress = (indexed, total) => {
361351
361380
  const store = useRepoMapStore.getState();
361352
361381
  const phaseLabels = {
361353
- [-1]: "building edges",
361354
- [-2]: "computing pagerank",
361355
- [-3]: "analyzing git history"
361382
+ [-1]: "resolving refs",
361383
+ [-2]: "building call graph",
361384
+ [-3]: "building edges",
361385
+ [-4]: "linking tests",
361386
+ [-5]: "analyzing git history"
361356
361387
  };
361357
361388
  const label = phaseLabels[indexed] ?? `${String(indexed)}/${String(total)}`;
361358
361389
  store.setScanProgress(label);
@@ -24204,6 +24204,8 @@ class RepoMap {
24204
24204
  );
24205
24205
  CREATE INDEX IF NOT EXISTS idx_refs_file ON refs(file_id);
24206
24206
  CREATE INDEX IF NOT EXISTS idx_refs_name ON refs(name);
24207
+ CREATE INDEX IF NOT EXISTS idx_refs_source ON refs(source_file_id);
24208
+ CREATE INDEX IF NOT EXISTS idx_refs_import ON refs(import_source);
24207
24209
  `);
24208
24210
  try {
24209
24211
  this.db.run("ALTER TABLE refs ADD COLUMN source_file_id INTEGER REFERENCES files(id) ON DELETE CASCADE");
@@ -24453,7 +24455,9 @@ class RepoMap {
24453
24455
  }
24454
24456
  this.onProgress?.(toIndex.length, toIndex.length);
24455
24457
  }
24456
- if (toIndex.length > 0 || stale.length > 0) {
24458
+ const edgeCount = this.db.query("SELECT COUNT(*) as c FROM edges").get()?.c ?? 0;
24459
+ const needsPostIndexing = toIndex.length > 0 || stale.length > 0 || edgeCount === 0;
24460
+ if (needsPostIndexing) {
24457
24461
  this.onProgress?.(-1, -1);
24458
24462
  await tick();
24459
24463
  await this.resolveUnresolvedRefs();
@@ -24466,6 +24470,7 @@ class RepoMap {
24466
24470
  this.onProgress?.(-3, -3);
24467
24471
  await tick();
24468
24472
  await this.buildEdges();
24473
+ this.onProgress?.(-4, -4);
24469
24474
  this.linkTestFiles();
24470
24475
  this.rescueOrphans();
24471
24476
  this.onProgress?.(-4, -4);
@@ -25088,6 +25093,8 @@ class RepoMap {
25088
25093
  addEdge(row.source_file_id, row.target_file_id, Math.sqrt(row.ref_count) * 3, 3);
25089
25094
  if (i4 % 500 === 499)
25090
25095
  await tick();
25096
+ if (i4 % 2000 === 0)
25097
+ this.onProgress?.(-3, -3);
25091
25098
  }
25092
25099
  const refDfMap = new Map;
25093
25100
  const dfRows = this.db.query("SELECT name, COUNT(DISTINCT file_id) AS ref_df FROM refs GROUP BY name").all();
@@ -25118,20 +25125,26 @@ class RepoMap {
25118
25125
  addEdge(row.source_file_id, row.target_file_id, w3, 1);
25119
25126
  if (i4 % 500 === 499)
25120
25127
  await tick();
25128
+ if (i4 % 2000 === 0)
25129
+ this.onProgress?.(-3, -3);
25121
25130
  }
25131
+ this.onProgress?.(-3, -3);
25122
25132
  await tick();
25133
+ const uniqueExportNames = new Set;
25134
+ const exportNameCounts = this.db.query("SELECT name, COUNT(DISTINCT file_id) as fc FROM symbols WHERE is_exported = 1 GROUP BY name HAVING fc = 1").all();
25135
+ for (const row of exportNameCounts)
25136
+ uniqueExportNames.add(row.name);
25123
25137
  const inferredRows = this.db.query(`SELECT r.file_id AS source_file_id, s.file_id AS target_file_id,
25124
- r.name, COUNT(*) AS ref_count,
25125
- (SELECT COUNT(DISTINCT s2.file_id) FROM symbols s2
25126
- WHERE s2.name = r.name AND s2.is_exported = 1) AS def_count
25138
+ r.name, COUNT(*) AS ref_count
25127
25139
  FROM refs r
25128
25140
  JOIN symbols s ON r.name = s.name AND s.is_exported = 1
25129
25141
  WHERE r.source_file_id IS NULL AND r.import_source IS NULL
25130
25142
  AND r.file_id != s.file_id
25131
- GROUP BY r.file_id, s.file_id, r.name
25132
- HAVING def_count = 1`).all();
25143
+ GROUP BY r.file_id, s.file_id, r.name`).all();
25133
25144
  for (let i4 = 0;i4 < inferredRows.length; i4++) {
25134
25145
  const row = inferredRows[i4];
25146
+ if (!uniqueExportNames.has(row.name))
25147
+ continue;
25135
25148
  const refDf = refDfMap.get(row.name) ?? 1;
25136
25149
  const idf = Math.log((totalFiles - refDf + 0.5) / (refDf + 0.5));
25137
25150
  if (idf <= 0)
@@ -25145,6 +25158,8 @@ class RepoMap {
25145
25158
  addEdge(row.source_file_id, row.target_file_id, w3, 1);
25146
25159
  if (i4 % 500 === 499)
25147
25160
  await tick();
25161
+ if (i4 % 2000 === 0)
25162
+ this.onProgress?.(-3, -3);
25148
25163
  }
25149
25164
  const cochangeRows = this.db.query("SELECT file_id_a, file_id_b, count FROM cochanges WHERE count >= 3").all();
25150
25165
  for (const row of cochangeRows) {
@@ -25201,6 +25216,8 @@ class RepoMap {
25201
25216
  try {
25202
25217
  tx();
25203
25218
  } catch {}
25219
+ if (i4 % 2000 === 0)
25220
+ this.onProgress?.(-3, -3);
25204
25221
  if (i4 + BATCH < entries.length)
25205
25222
  await tick();
25206
25223
  }
@@ -25544,7 +25561,9 @@ class RepoMap {
25544
25561
  }
25545
25562
  const pairCounts = new Map;
25546
25563
  const commits = logOutput.split("---COMMIT---").filter((s2) => s2.trim());
25547
- for (const commit of commits) {
25564
+ const tick = () => new Promise((r4) => setTimeout(r4, 1));
25565
+ for (let ci = 0;ci < commits.length; ci++) {
25566
+ const commit = commits[ci];
25548
25567
  const files = commit.split(`
25549
25568
  `).map((l3) => l3.trim()).filter((l3) => l3 && pathToId.has(l3));
25550
25569
  if (files.length < 2 || files.length > MAX_COCHANGE_FILES_PER_COMMIT)
@@ -25557,23 +25576,33 @@ class RepoMap {
25557
25576
  pairCounts.set(key2, (pairCounts.get(key2) ?? 0) + 1);
25558
25577
  }
25559
25578
  }
25579
+ if (ci % 50 === 0) {
25580
+ this.onProgress?.(-5, -5);
25581
+ await tick();
25582
+ }
25560
25583
  }
25561
25584
  if (pairCounts.size === 0)
25562
25585
  return;
25586
+ this.onProgress?.(-5, -5);
25563
25587
  const insert = this.db.prepare("INSERT OR REPLACE INTO cochanges (file_id_a, file_id_b, count) VALUES (?, ?, ?)");
25564
- const tx = this.db.transaction(() => {
25565
- for (const [key2, count] of pairCounts) {
25566
- if (count < 2)
25567
- continue;
25568
- const [a2, b3] = key2.split("\x00");
25569
- const idA = pathToId.get(a2);
25570
- const idB = pathToId.get(b3);
25571
- if (idA !== undefined && idB !== undefined) {
25572
- insert.run(idA, idB, count);
25588
+ const BATCH = 5000;
25589
+ const entries = [...pairCounts.entries()].filter(([, count]) => count >= 2);
25590
+ for (let i4 = 0;i4 < entries.length; i4 += BATCH) {
25591
+ const batch = entries.slice(i4, i4 + BATCH);
25592
+ const tx = this.db.transaction(() => {
25593
+ for (const [key2, count] of batch) {
25594
+ const [a2, b3] = key2.split("\x00");
25595
+ const idA = pathToId.get(a2);
25596
+ const idB = pathToId.get(b3);
25597
+ if (idA !== undefined && idB !== undefined) {
25598
+ insert.run(idA, idB, count);
25599
+ }
25573
25600
  }
25574
- }
25575
- });
25576
- tx();
25601
+ });
25602
+ tx();
25603
+ if (i4 % 1e4 === 0)
25604
+ this.onProgress?.(-5, -5);
25605
+ }
25577
25606
  }
25578
25607
  getCoChangePartners(fileIds) {
25579
25608
  if (fileIds.size === 0)
@@ -272809,17 +272838,36 @@ ctx = createWorkerHandler(handlers, async (config) => {
272809
272838
  if (typeof config.maxFiles === "number" && config.maxFiles > 0) {
272810
272839
  repoMap.maxFiles = config.maxFiles;
272811
272840
  }
272841
+ let lastStats = {
272842
+ files: 0,
272843
+ symbols: 0,
272844
+ edges: 0,
272845
+ summaries: 0,
272846
+ calls: 0
272847
+ };
272848
+ let lastDbSize = 0;
272812
272849
  repoMap.onProgress = (indexed, total) => {
272813
272850
  const rm = repoMap;
272814
272851
  if (!rm)
272815
272852
  return;
272816
- const stats = rm.getStats();
272817
- const dbSize = rm.dbSizeBytes();
272853
+ if (indexed < 0) {
272854
+ ctx.emit("progress", {
272855
+ indexed,
272856
+ total,
272857
+ stats: lastStats,
272858
+ dbSize: lastDbSize
272859
+ });
272860
+ return;
272861
+ }
272862
+ try {
272863
+ lastStats = rm.getStats();
272864
+ lastDbSize = rm.dbSizeBytes();
272865
+ } catch {}
272818
272866
  ctx.emit("progress", {
272819
272867
  indexed,
272820
272868
  total,
272821
- stats,
272822
- dbSize
272869
+ stats: lastStats,
272870
+ dbSize: lastDbSize
272823
272871
  });
272824
272872
  };
272825
272873
  repoMap.onScanComplete = (success) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proxysoul/soulforge",
3
- "version": "1.6.2",
3
+ "version": "1.7.0",
4
4
  "description": "Graph-powered code intelligence — multi-agent coding with codebase-aware AI",
5
5
  "repository": {
6
6
  "type": "git",