@merittdev/horus 0.21.2 → 0.21.4
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/horus_source.whl +0 -0
- package/dist/index.cjs +104 -8
- package/package.json +1 -1
package/dist/horus_source.whl
CHANGED
|
Binary file
|
package/dist/index.cjs
CHANGED
|
@@ -3057,7 +3057,7 @@ var init_version = __esm({
|
|
|
3057
3057
|
"../../packages/core/src/version.ts"() {
|
|
3058
3058
|
"use strict";
|
|
3059
3059
|
init_cjs_shims();
|
|
3060
|
-
HORUS_VERSION = true ? "0.21.
|
|
3060
|
+
HORUS_VERSION = true ? "0.21.4" : "dev";
|
|
3061
3061
|
PINNED_SOURCE_VERSION = HORUS_VERSION;
|
|
3062
3062
|
SOURCE_PIN_ENFORCED = HORUS_VERSION !== "dev";
|
|
3063
3063
|
}
|
|
@@ -72160,12 +72160,17 @@ function readSourceMeta(root) {
|
|
|
72160
72160
|
return {
|
|
72161
72161
|
storeBackend: typeof j3["store_backend"] === "string" ? j3["store_backend"].toLowerCase() : void 0,
|
|
72162
72162
|
embeddingsComplete: typeof j3["embeddings_complete"] === "boolean" ? j3["embeddings_complete"] : void 0,
|
|
72163
|
+
structuralComplete: typeof j3["structural_complete"] === "boolean" ? j3["structural_complete"] : void 0,
|
|
72163
72164
|
stats: stats ? { symbols: stats["symbols"], embeddings: stats["embeddings"] } : void 0
|
|
72164
72165
|
};
|
|
72165
72166
|
} catch {
|
|
72166
72167
|
return null;
|
|
72167
72168
|
}
|
|
72168
72169
|
}
|
|
72170
|
+
function indexEmbeddingsPending(root) {
|
|
72171
|
+
const meta = readSourceMeta(root);
|
|
72172
|
+
return meta?.structuralComplete === true && meta?.embeddingsComplete === false;
|
|
72173
|
+
}
|
|
72169
72174
|
function metaHasEmbeddings(meta) {
|
|
72170
72175
|
if (meta.embeddingsComplete === false) return false;
|
|
72171
72176
|
const embeddings = meta.stats?.embeddings ?? 0;
|
|
@@ -72183,25 +72188,88 @@ function indexNeedsReanalyze(root) {
|
|
|
72183
72188
|
return `store backend changed (${meta.storeBackend} -> ${CURRENT_STORE_BACKEND})`;
|
|
72184
72189
|
}
|
|
72185
72190
|
if (!metaHasEmbeddings(meta)) {
|
|
72191
|
+
if (meta.structuralComplete === true) return null;
|
|
72186
72192
|
return "index has no semantic embeddings (symbols present but 0 vectors)";
|
|
72187
72193
|
}
|
|
72188
72194
|
return null;
|
|
72189
72195
|
}
|
|
72196
|
+
var BASE_ANALYZE_TIMEOUT_MS = 9e5;
|
|
72197
|
+
var MAX_ANALYZE_TIMEOUT_MS = 36e5;
|
|
72198
|
+
var ANALYZE_BASELINE_FILES = 5e3;
|
|
72199
|
+
var ANALYZE_TIMEOUT_PER_FILE_MS = 15;
|
|
72200
|
+
var ANALYZE_COUNT_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
72201
|
+
"node_modules",
|
|
72202
|
+
".git",
|
|
72203
|
+
".horus",
|
|
72204
|
+
"dist",
|
|
72205
|
+
"build",
|
|
72206
|
+
"out",
|
|
72207
|
+
"target",
|
|
72208
|
+
"vendor",
|
|
72209
|
+
".venv",
|
|
72210
|
+
"venv",
|
|
72211
|
+
"__pycache__",
|
|
72212
|
+
".next",
|
|
72213
|
+
".turbo",
|
|
72214
|
+
"coverage",
|
|
72215
|
+
".cache"
|
|
72216
|
+
]);
|
|
72217
|
+
function countRepoFiles(root, limit = 2e5) {
|
|
72218
|
+
let count = 0;
|
|
72219
|
+
const stack = [root];
|
|
72220
|
+
while (stack.length > 0) {
|
|
72221
|
+
const dir = stack.pop();
|
|
72222
|
+
let entries;
|
|
72223
|
+
try {
|
|
72224
|
+
entries = (0, import_node_fs4.readdirSync)(dir, { withFileTypes: true });
|
|
72225
|
+
} catch {
|
|
72226
|
+
continue;
|
|
72227
|
+
}
|
|
72228
|
+
for (const e of entries) {
|
|
72229
|
+
if (e.isDirectory()) {
|
|
72230
|
+
if (ANALYZE_COUNT_SKIP_DIRS.has(e.name) || e.name.startsWith(".")) continue;
|
|
72231
|
+
stack.push((0, import_node_path4.join)(dir, e.name));
|
|
72232
|
+
} else if (e.isFile()) {
|
|
72233
|
+
count++;
|
|
72234
|
+
if (count >= limit) return count;
|
|
72235
|
+
}
|
|
72236
|
+
}
|
|
72237
|
+
}
|
|
72238
|
+
return count;
|
|
72239
|
+
}
|
|
72240
|
+
function resolveAnalyzeTimeoutMs(root) {
|
|
72241
|
+
const override = process.env["HORUS_ANALYZE_TIMEOUT_MS"];
|
|
72242
|
+
if (override !== void 0 && override.trim() !== "") {
|
|
72243
|
+
const n4 = Number(override);
|
|
72244
|
+
if (Number.isFinite(n4) && n4 > 0) return Math.floor(n4);
|
|
72245
|
+
}
|
|
72246
|
+
let files = 0;
|
|
72247
|
+
try {
|
|
72248
|
+
files = countRepoFiles(root);
|
|
72249
|
+
} catch {
|
|
72250
|
+
return BASE_ANALYZE_TIMEOUT_MS;
|
|
72251
|
+
}
|
|
72252
|
+
if (files <= ANALYZE_BASELINE_FILES) return BASE_ANALYZE_TIMEOUT_MS;
|
|
72253
|
+
const scaled = BASE_ANALYZE_TIMEOUT_MS + (files - ANALYZE_BASELINE_FILES) * ANALYZE_TIMEOUT_PER_FILE_MS;
|
|
72254
|
+
return Math.min(scaled, MAX_ANALYZE_TIMEOUT_MS);
|
|
72255
|
+
}
|
|
72190
72256
|
async function analyzeRepo(root) {
|
|
72191
72257
|
const bin = await resolveSourceBin();
|
|
72192
72258
|
if (!bin) throw new Error("horus-source not found on PATH. Install it: curl -fsSL https://horus.sh/install.sh | bash");
|
|
72259
|
+
const timeoutMs = resolveAnalyzeTimeoutMs(root);
|
|
72193
72260
|
try {
|
|
72194
|
-
await exec(bin, ["analyze", "."], {
|
|
72261
|
+
await exec(bin, ["analyze", ".", "--defer-embeddings"], {
|
|
72195
72262
|
cwd: root,
|
|
72196
|
-
timeout:
|
|
72263
|
+
timeout: timeoutMs,
|
|
72197
72264
|
maxBuffer: 64 * 1024 * 1024
|
|
72198
72265
|
});
|
|
72199
72266
|
} catch (err2) {
|
|
72200
72267
|
const e = err2;
|
|
72201
72268
|
const tail = redactSecrets((e.stderr ?? "").trim()).slice(-800);
|
|
72202
72269
|
if (e.killed || e.signal === "SIGTERM" || e.code === "ETIMEDOUT") {
|
|
72270
|
+
const secs = Math.round(timeoutMs / 1e3);
|
|
72203
72271
|
throw new Error(
|
|
72204
|
-
`horus-source analyze timed out after
|
|
72272
|
+
`horus-source analyze timed out after ${secs}s while building the structural index (large repo). Raise the cap with HORUS_ANALYZE_TIMEOUT_MS.` + (tail ? ` \u2014 last output: ${tail}` : "")
|
|
72205
72273
|
);
|
|
72206
72274
|
}
|
|
72207
72275
|
throw new Error(tail ? `horus-source analyze failed: ${tail}` : redactSecrets(e.message ?? "horus-source analyze failed"));
|
|
@@ -72396,7 +72464,7 @@ var SourceCodeProvider = class {
|
|
|
72396
72464
|
kind = "code";
|
|
72397
72465
|
// --- helpers -------------------------------------------------------------
|
|
72398
72466
|
nodeToSymbol(n4) {
|
|
72399
|
-
|
|
72467
|
+
const sym = {
|
|
72400
72468
|
id: n4.id,
|
|
72401
72469
|
name: n4.name,
|
|
72402
72470
|
filePath: n4.filePath,
|
|
@@ -72406,6 +72474,8 @@ var SourceCodeProvider = class {
|
|
|
72406
72474
|
language: n4.language,
|
|
72407
72475
|
className: n4.className
|
|
72408
72476
|
};
|
|
72477
|
+
if (n4.aliasOf) sym.aliasOf = n4.aliasOf;
|
|
72478
|
+
return sym;
|
|
72409
72479
|
}
|
|
72410
72480
|
/** Map a serialized graph node to a `Symbol`, coercing the host's `0` line sentinel to undefined. */
|
|
72411
72481
|
serializedToSymbol(n4) {
|
|
@@ -72419,6 +72489,7 @@ var SourceCodeProvider = class {
|
|
|
72419
72489
|
if (n4.className) sym.className = n4.className;
|
|
72420
72490
|
if (n4.signature) sym.signature = n4.signature;
|
|
72421
72491
|
if (n4.language) sym.language = n4.language;
|
|
72492
|
+
if (n4.aliasOf) sym.aliasOf = n4.aliasOf;
|
|
72422
72493
|
return sym;
|
|
72423
72494
|
}
|
|
72424
72495
|
/**
|
|
@@ -97554,6 +97625,16 @@ function parseSymbolQuery(query) {
|
|
|
97554
97625
|
if (container.length < 2 || m5[2].length < 2) return null;
|
|
97555
97626
|
return { symbol: m5[2], container, isPath: false };
|
|
97556
97627
|
}
|
|
97628
|
+
function redirectExportAlias(candidates) {
|
|
97629
|
+
const head = candidates[0];
|
|
97630
|
+
if (head === void 0 || head.aliasOf === void 0 || head.aliasOf === "") return candidates;
|
|
97631
|
+
const implName = head.aliasOf.toLowerCase();
|
|
97632
|
+
const impl = candidates.find(
|
|
97633
|
+
(s5) => s5.id !== head.id && s5.name.toLowerCase() === implName && s5.filePath === head.filePath
|
|
97634
|
+
) ?? candidates.find((s5) => s5.id !== head.id && s5.name.toLowerCase() === implName);
|
|
97635
|
+
if (impl === void 0) return candidates;
|
|
97636
|
+
return [impl, ...candidates.filter((s5) => s5.id !== impl.id)];
|
|
97637
|
+
}
|
|
97557
97638
|
function resolveSymbolQuery(query, symbols, opts) {
|
|
97558
97639
|
const includeTests = opts?.includeTests === true;
|
|
97559
97640
|
const qualifier = parseSymbolQuery(query);
|
|
@@ -97577,7 +97658,10 @@ function resolveSymbolQuery(query, symbols, opts) {
|
|
|
97577
97658
|
const ambiguous = kind !== "fuzzy" && kind !== "none" && first !== void 0 && second !== void 0 && first.score === second.score && first.symbol.filePath !== second.symbol.filePath;
|
|
97578
97659
|
return {
|
|
97579
97660
|
kind,
|
|
97580
|
-
|
|
97661
|
+
// B2: redirect a public export-alias hit (`Component` → `BaseComponent`) to the
|
|
97662
|
+
// implementation so explain/blast-radius/search land on real product code, not the
|
|
97663
|
+
// empty alias stub. No-op unless the head candidate carries `aliasOf`.
|
|
97664
|
+
candidates: redirectExportAlias([...scoredTier.map((s5) => s5.symbol), ...rest]),
|
|
97581
97665
|
ambiguous,
|
|
97582
97666
|
qualifier
|
|
97583
97667
|
};
|
|
@@ -97591,7 +97675,16 @@ async function resolveSeedSymbol(code, query, opts) {
|
|
|
97591
97675
|
const seen = new Set(pool.map((s5) => s5.id));
|
|
97592
97676
|
for (const s5 of extra) if (!seen.has(s5.id)) pool.push(s5);
|
|
97593
97677
|
}
|
|
97594
|
-
|
|
97678
|
+
const includeTests = opts?.includeTests ?? false;
|
|
97679
|
+
let resolution = resolveSymbolQuery(query, pool, { includeTests });
|
|
97680
|
+
const head = resolution.candidates[0];
|
|
97681
|
+
if (head?.aliasOf !== void 0 && head.aliasOf !== "" && !pool.some((s5) => s5.id !== head.id && s5.name.toLowerCase() === head.aliasOf.toLowerCase())) {
|
|
97682
|
+
const implHits = await code.searchSymbols(head.aliasOf, Math.max(limit, 20));
|
|
97683
|
+
const seen = new Set(pool.map((s5) => s5.id));
|
|
97684
|
+
for (const s5 of implHits) if (!seen.has(s5.id)) pool.push(s5);
|
|
97685
|
+
resolution = resolveSymbolQuery(query, pool, { includeTests });
|
|
97686
|
+
}
|
|
97687
|
+
return resolution;
|
|
97595
97688
|
}
|
|
97596
97689
|
function rankSeeds(seeds, hintTokens, changedFiles, hintHasCode, qualifier, preferNamed) {
|
|
97597
97690
|
const scored = seeds.map((symbol, i3) => ({
|
|
@@ -124755,7 +124848,7 @@ async function runIndex(opts) {
|
|
|
124755
124848
|
if (incomplete) {
|
|
124756
124849
|
console.error(
|
|
124757
124850
|
import_picocolors41.default.red(
|
|
124758
|
-
` source analysis did not finish (${incomplete}) \u2014 the index
|
|
124851
|
+
` source analysis did not finish (${incomplete}) \u2014 the structural index is missing or broken. Re-run \`horus init\` to rebuild it.`
|
|
124759
124852
|
)
|
|
124760
124853
|
);
|
|
124761
124854
|
return 1;
|
|
@@ -124810,6 +124903,9 @@ async function runIndex(opts) {
|
|
|
124810
124903
|
);
|
|
124811
124904
|
return 1;
|
|
124812
124905
|
}
|
|
124906
|
+
if (indexEmbeddingsPending(root)) {
|
|
124907
|
+
console.log(import_picocolors41.default.dim(" index ready (semantic search warming up)"));
|
|
124908
|
+
}
|
|
124813
124909
|
try {
|
|
124814
124910
|
await stitchQueueMap(hostUrl, label);
|
|
124815
124911
|
} catch (err2) {
|