@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.
- package/dist/cli.js +93 -96
- 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
|
|
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
|
-
|
|
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
|
|
9711
|
-
if (
|
|
9689
|
+
const languages = detectProjectLanguages(files);
|
|
9690
|
+
if (languages.length === 0) {
|
|
9712
9691
|
return skipResult(existingEdges, "No supported language detected");
|
|
9713
9692
|
}
|
|
9714
|
-
let
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
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 (!
|
|
9763
|
+
if (!anySucceeded) {
|
|
9732
9764
|
return skipResult(
|
|
9733
9765
|
existingEdges,
|
|
9734
|
-
`SCIP binary
|
|
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:
|
|
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
|
-
|
|
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
|
}
|