@unerr-ai/unerr 0.0.0-beta.7 → 0.0.0-beta.8

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.
Files changed (2) hide show
  1. package/dist/cli.js +93 -96
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1656,8 +1656,21 @@ async function createIfMissing(db, existing, name, schema) {
1656
1656
  if (existing.has(name)) return;
1657
1657
  await db.run(schema);
1658
1658
  }
1659
+ async function dropIfStale(db, existing, name, requiredColumns) {
1660
+ if (!existing.has(name)) return;
1661
+ try {
1662
+ const colList = ["key", ...requiredColumns].join(", ");
1663
+ await db.run(
1664
+ `?[] := *${name}{${colList}}, key = '__schema_probe__'`
1665
+ );
1666
+ } catch {
1667
+ await db.run(`::remove ${name}`);
1668
+ existing.delete(name);
1669
+ }
1670
+ }
1659
1671
  async function initSchema(db) {
1660
1672
  const existing = await getExistingRelations(db);
1673
+ await dropIfStale(db, existing, "entities", ["end_line", "is_test"]);
1661
1674
  await createIfMissing(
1662
1675
  db,
1663
1676
  existing,
@@ -1681,32 +1694,6 @@ async function initSchema(db) {
1681
1694
  }
1682
1695
  `
1683
1696
  );
1684
- if (existing.has("entities")) {
1685
- try {
1686
- await db.run(
1687
- `?[key, kind, name, file_path, start_line, end_line, signature, body, fan_in, fan_out, risk_level, community, is_test] :=
1688
- *entities{key, kind, name, file_path, start_line, signature, body, fan_in, fan_out, risk_level, community, is_test},
1689
- end_line = 0
1690
- :replace entities {
1691
- key: String
1692
- =>
1693
- kind: String,
1694
- name: String,
1695
- file_path: String,
1696
- start_line: Int default 0,
1697
- end_line: Int default 0,
1698
- signature: String default "",
1699
- body: String default "",
1700
- fan_in: Int default 0,
1701
- fan_out: Int default 0,
1702
- risk_level: String default "normal",
1703
- community: Int default -1,
1704
- is_test: Bool default false
1705
- }`
1706
- );
1707
- } catch {
1708
- }
1709
- }
1710
1697
  await createIfMissing(
1711
1698
  db,
1712
1699
  existing,
@@ -9464,7 +9451,7 @@ async function detectScipBinary(language) {
9464
9451
  path: null
9465
9452
  };
9466
9453
  }
9467
- function detectPrimaryLanguage(files) {
9454
+ function detectProjectLanguages(files) {
9468
9455
  const counts = {};
9469
9456
  for (const file of files) {
9470
9457
  const dotIdx = file.lastIndexOf(".");
@@ -9475,15 +9462,7 @@ function detectPrimaryLanguage(files) {
9475
9462
  counts[lang] = (counts[lang] ?? 0) + 1;
9476
9463
  }
9477
9464
  }
9478
- let maxLang = null;
9479
- let maxCount = 0;
9480
- for (const [lang, count] of Object.entries(counts)) {
9481
- if (count > maxCount) {
9482
- maxCount = count;
9483
- maxLang = lang;
9484
- }
9485
- }
9486
- return maxLang;
9465
+ return Object.entries(counts).map(([language, fileCount]) => ({ language, fileCount })).sort((a, b) => b.fileCount - a.fileCount);
9487
9466
  }
9488
9467
  var BUNDLED_SCIP, EXTERNAL_SCIP, EXT_TO_SCIP_LANG;
9489
9468
  var init_detector = __esm({
@@ -9707,77 +9686,94 @@ var init_runner = __esm({
9707
9686
  // src/intelligence/indexer/scip/orchestrator.ts
9708
9687
  import { join as join23 } from "path";
9709
9688
  async function enrichWithScip(files, projectRoot, existingEdges, entities) {
9710
- const language = detectPrimaryLanguage(files);
9711
- if (!language) {
9689
+ const languages = detectProjectLanguages(files);
9690
+ if (languages.length === 0) {
9712
9691
  return skipResult(existingEdges, "No supported language detected");
9713
9692
  }
9714
- let binaryInfo = await detectScipBinary(language);
9715
- if (!binaryInfo.available && isAutoDownloadSupported(language)) {
9716
- log5.info(`SCIP binary not found for ${language}, downloading...`);
9717
- const downloadResult = await downloadScipBinary(language, (msg) => {
9718
- log5.info(msg);
9719
- });
9720
- if (downloadResult.success && downloadResult.binaryPath) {
9721
- binaryInfo = await detectScipBinary(language);
9722
- } else {
9723
- const manualInstr = getManualInstallInstructions(language);
9724
- return skipResult(
9725
- existingEdges,
9726
- `SCIP auto-download failed for ${language}: ${downloadResult.error}${manualInstr ? `
9693
+ let currentEdges = markAsStructural(existingEdges);
9694
+ let lastRunResult = null;
9695
+ let lastDecodeResult = null;
9696
+ let lastMergeResult = null;
9697
+ let anySucceeded = false;
9698
+ const processedLanguages = [];
9699
+ for (const { language, fileCount } of languages) {
9700
+ log5.info(`SCIP: processing ${language} (${fileCount} files)`);
9701
+ let binaryInfo = await detectScipBinary(language);
9702
+ if (!binaryInfo.available && isAutoDownloadSupported(language)) {
9703
+ log5.info(`SCIP binary not found for ${language}, downloading...`);
9704
+ const downloadResult = await downloadScipBinary(language, (msg) => {
9705
+ log5.info(msg);
9706
+ });
9707
+ if (downloadResult.success && downloadResult.binaryPath) {
9708
+ binaryInfo = await detectScipBinary(language);
9709
+ } else {
9710
+ const manualInstr = getManualInstallInstructions(language);
9711
+ log5.warn(
9712
+ `SCIP auto-download failed for ${language}: ${downloadResult.error}${manualInstr ? `
9727
9713
  Manual install: ${manualInstr}` : ""}`
9714
+ );
9715
+ continue;
9716
+ }
9717
+ }
9718
+ if (!binaryInfo.available) {
9719
+ log5.info(
9720
+ `SCIP binary not available for ${language}, skipping`
9728
9721
  );
9722
+ continue;
9729
9723
  }
9724
+ log5.info(
9725
+ `SCIP enrichment: ${language} (${binaryInfo.bundled ? "bundled" : "external"}: ${binaryInfo.binaryName})`
9726
+ );
9727
+ const outputDir = join23(projectRoot, ".unerr", "scip");
9728
+ const binaryPath = binaryInfo.path ?? binaryInfo.binaryName;
9729
+ const runResult = await runScipIndexer({
9730
+ language,
9731
+ binaryPath,
9732
+ projectRoot,
9733
+ outputDir,
9734
+ timeoutMs: 3e4
9735
+ });
9736
+ lastRunResult = runResult;
9737
+ if (!runResult.success || !runResult.outputPath) {
9738
+ log5.warn(`SCIP indexer failed for ${language}: ${runResult.error}`);
9739
+ continue;
9740
+ }
9741
+ const decodeResult = await decodeScipOutput(runResult.outputPath);
9742
+ lastDecodeResult = decodeResult;
9743
+ const indexedEdges = currentEdges.map((e) => ({
9744
+ from_key: e.from_key,
9745
+ to_key: e.to_key,
9746
+ type: e.type,
9747
+ file_path: e.file_path,
9748
+ line: e.line
9749
+ }));
9750
+ const { edges: enrichedEdges, result: mergeResult } = mergeScipResults(
9751
+ indexedEdges,
9752
+ decodeResult,
9753
+ entities
9754
+ );
9755
+ currentEdges = enrichedEdges;
9756
+ lastMergeResult = mergeResult;
9757
+ anySucceeded = true;
9758
+ processedLanguages.push(language);
9759
+ log5.info(
9760
+ `SCIP ${language}: ${mergeResult.edgesUpgraded} edges upgraded to compiler-verified (${Math.round(runResult.durationMs)}ms)`
9761
+ );
9730
9762
  }
9731
- if (!binaryInfo.available) {
9763
+ if (!anySucceeded) {
9732
9764
  return skipResult(
9733
9765
  existingEdges,
9734
- `SCIP binary not available for ${language} (not bundled, not on PATH, no auto-download)`
9766
+ `No SCIP binary available for any detected language (${languages.map((l) => l.language).join(", ")})`
9735
9767
  );
9736
9768
  }
9737
- log5.info(
9738
- `SCIP enrichment: ${language} (${binaryInfo.bundled ? "bundled" : "external"}: ${binaryInfo.binaryName})`
9739
- );
9740
- const outputDir = join23(projectRoot, ".unerr", "scip");
9741
- const binaryPath = binaryInfo.path ?? binaryInfo.binaryName;
9742
- const runResult = await runScipIndexer({
9743
- language,
9744
- binaryPath,
9745
- projectRoot,
9746
- outputDir,
9747
- timeoutMs: 3e4
9748
- // 30s max for inline indexing
9749
- });
9750
- if (!runResult.success || !runResult.outputPath) {
9751
- log5.warn(`SCIP indexer failed for ${language}: ${runResult.error}`);
9752
- return {
9753
- language,
9754
- binaryAvailable: true,
9755
- bundled: binaryInfo.bundled,
9756
- runResult,
9757
- decodeResult: null,
9758
- mergeResult: null,
9759
- enrichedEdges: markAsStructural(existingEdges),
9760
- skipped: false,
9761
- skipReason: null
9762
- };
9763
- }
9764
- const decodeResult = await decodeScipOutput(runResult.outputPath);
9765
- const { edges: enrichedEdges, result: mergeResult } = mergeScipResults(
9766
- existingEdges,
9767
- decodeResult,
9768
- entities
9769
- );
9770
- log5.info(
9771
- `SCIP enrichment complete: ${mergeResult.edgesUpgraded} edges upgraded to compiler-verified (${Math.round(runResult.durationMs)}ms)`
9772
- );
9773
9769
  return {
9774
- language,
9770
+ language: processedLanguages.join("+"),
9775
9771
  binaryAvailable: true,
9776
- bundled: binaryInfo.bundled,
9777
- runResult,
9778
- decodeResult,
9779
- mergeResult,
9780
- enrichedEdges,
9772
+ bundled: false,
9773
+ runResult: lastRunResult,
9774
+ decodeResult: lastDecodeResult,
9775
+ mergeResult: lastMergeResult,
9776
+ enrichedEdges: currentEdges,
9781
9777
  skipped: false,
9782
9778
  skipReason: null
9783
9779
  };
@@ -10969,7 +10965,8 @@ async function populateCozoDB(graphStore, entities, edges) {
10969
10965
  { key: `file:${fp}`, name: basename2(fp), fp, is_test: isTestFile(fp) }
10970
10966
  );
10971
10967
  } catch (err) {
10972
- process.stderr.write(`[unerr] \u26A0 File entity insert failed for ${fp}: ${err instanceof Error ? err.message : String(err)}
10968
+ const msg = err instanceof Error ? err.message : JSON.stringify(err);
10969
+ process.stderr.write(`[unerr] \u26A0 File entity insert failed for ${fp}: ${msg}
10973
10970
  `);
10974
10971
  }
10975
10972
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unerr-ai/unerr",
3
- "version": "0.0.0-beta.7",
3
+ "version": "0.0.0-beta.8",
4
4
  "description": "Local-first code intelligence CLI for unerr",
5
5
  "type": "module",
6
6
  "bin": {