@merittdev/horus 0.8.18 → 0.8.19

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/index.cjs +62 -20
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -65880,8 +65880,8 @@ init_cjs_shims();
65880
65880
 
65881
65881
  // ../../packages/core/src/version.ts
65882
65882
  init_cjs_shims();
65883
- var HORUS_VERSION = true ? "0.8.18" : "dev";
65884
- var PINNED_SOURCE_VERSION = "1.5.4";
65883
+ var HORUS_VERSION = true ? "0.8.19" : "dev";
65884
+ var PINNED_SOURCE_VERSION = "1.5.5";
65885
65885
 
65886
65886
  // ../../packages/core/src/evidence.ts
65887
65887
  init_cjs_shims();
@@ -71008,11 +71008,22 @@ function isAnalyzed(root) {
71008
71008
  async function analyzeRepo(root) {
71009
71009
  const bin = await resolveSourceBin();
71010
71010
  if (!bin) throw new Error("horus-source not found on PATH. Install it: curl -fsSL https://horus.sh/install.sh | bash");
71011
- await exec(bin, ["analyze", "."], {
71012
- cwd: root,
71013
- timeout: 9e5,
71014
- maxBuffer: 64 * 1024 * 1024
71015
- });
71011
+ try {
71012
+ await exec(bin, ["analyze", "."], {
71013
+ cwd: root,
71014
+ timeout: 9e5,
71015
+ maxBuffer: 64 * 1024 * 1024
71016
+ });
71017
+ } catch (err2) {
71018
+ const e = err2;
71019
+ const tail = (e.stderr ?? "").trim().slice(-800);
71020
+ if (e.killed || e.signal === "SIGTERM" || e.code === "ETIMEDOUT") {
71021
+ throw new Error(
71022
+ `horus-source analyze timed out after 900s (large repo / slow embeddings phase)` + (tail ? ` \u2014 last output: ${tail}` : "")
71023
+ );
71024
+ }
71025
+ throw new Error(tail ? `horus-source analyze failed: ${tail}` : e.message ?? "horus-source analyze failed");
71026
+ }
71016
71027
  }
71017
71028
  function readSourceHostUrl(root) {
71018
71029
  const p5 = (0, import_node_path3.join)(root, ".horus", "source", "host.json");
@@ -92084,13 +92095,24 @@ function scoreSeed(s5, index2, hintTokens, changedFiles, hintHasCode) {
92084
92095
  }
92085
92096
  return score;
92086
92097
  }
92098
+ function isDeprioritizedSeedPath(filePath) {
92099
+ return /(^|\/)(tests?|__tests__|spec)\//i.test(filePath) || /(\.|_)(test|spec)\.[jt]sx?$/i.test(filePath) || /(^|\/)test_[^/]*\.py$/i.test(filePath) || /_test\.py$/i.test(filePath) || /(^|\/)(examples?|samples?|demos?|fixtures?|sandbox|playground|docs|docs_src|tutorials?)(\/|$)/i.test(
92100
+ filePath
92101
+ );
92102
+ }
92087
92103
  function rankSeeds(seeds, hintTokens, changedFiles, hintHasCode) {
92088
- return seeds.map((symbol, i3) => ({
92104
+ const scored = seeds.map((symbol, i3) => ({
92089
92105
  symbol,
92090
92106
  score: scoreSeed(symbol, i3, hintTokens, changedFiles, hintHasCode),
92091
92107
  role: seedRole(symbol),
92092
- i: i3
92093
- })).sort((a2, b5) => b5.score === a2.score ? a2.i - b5.i : b5.score - a2.score).map(({ symbol, score, role }) => ({ symbol, score, role }));
92108
+ i: i3,
92109
+ deprio: isDeprioritizedSeedPath(symbol.filePath)
92110
+ }));
92111
+ const hasReal = scored.some((s5) => !s5.deprio);
92112
+ if (hasReal) {
92113
+ for (const s5 of scored) if (s5.deprio) s5.score -= 1e3;
92114
+ }
92115
+ return scored.sort((a2, b5) => b5.score === a2.score ? a2.i - b5.i : b5.score - a2.score).map(({ symbol, score, role }) => ({ symbol, score, role }));
92094
92116
  }
92095
92117
 
92096
92118
  // ../../packages/engine/src/normalize.ts
@@ -94526,7 +94548,17 @@ var EXTERNAL_MARKERS = [
94526
94548
  "postgres",
94527
94549
  "mariadb",
94528
94550
  "mysql2",
94529
- "mssql"
94551
+ "mssql",
94552
+ // Python async DB drivers (HOR-379): the markers above missed several backends real Python
94553
+ // DB libs implement (e.g. `databases` caught sqlalchemy/postgres/psycopg but missed asyncpg,
94554
+ // aiomysql/asyncmy, aiopg, aiosqlite). Their names don't substring-match the sync drivers, so
94555
+ // they need explicit markers. 'sqlite' also covers sqlite3/aiosqlite.
94556
+ "sqlite",
94557
+ "asyncpg",
94558
+ "aiomysql",
94559
+ "asyncmy",
94560
+ "aiopg",
94561
+ "pymysql"
94530
94562
  ];
94531
94563
  var TESTY_TOKENS = /* @__PURE__ */ new Set([
94532
94564
  "test",
@@ -94550,6 +94582,15 @@ var TESTY_TOKENS = /* @__PURE__ */ new Set([
94550
94582
  function isTestyCommunity(name2) {
94551
94583
  return name2.toLowerCase().split(/[+\s/_.-]+/).some((t2) => TESTY_TOKENS.has(t2));
94552
94584
  }
94585
+ function cleanSubsystemName(name2) {
94586
+ let n4 = name2.replace(/^[_.]+/, "").trim();
94587
+ const parts2 = n4.split("+");
94588
+ if (parts2.length === 2) {
94589
+ const [a2, b5] = parts2;
94590
+ if (a2 && b5 && a2.toLowerCase() === b5.toLowerCase()) n4 = a2;
94591
+ }
94592
+ return n4 || name2;
94593
+ }
94553
94594
  var TEST_EXAMPLE_PATH_RE = /(^|\/)(tests?|__tests__|examples?|samples?|demos?|docs|docs_src|tutorials?|fixtures?|spec|specs)(\/|$)/i;
94554
94595
  function isTestOrExamplePath(p5) {
94555
94596
  return TEST_EXAMPLE_PATH_RE.test(p5);
@@ -94574,7 +94615,7 @@ async function discoverArchitecture(deps) {
94574
94615
  'MATCH (m)-[r:CodeRelation]->(c:Community) WHERE r.rel_type = "member_of" RETURN c.name, count(m) ORDER BY count(m) DESC'
94575
94616
  );
94576
94617
  const rows = result.rows.map((row) => ({
94577
- name: String(row[0] ?? ""),
94618
+ name: cleanSubsystemName(String(row[0] ?? "")),
94578
94619
  members: Number(row[1] ?? 0)
94579
94620
  }));
94580
94621
  rows.sort((a2, b5) => {
@@ -94701,7 +94742,8 @@ function renderArchitecture(m5) {
94701
94742
  lines.push("(none)");
94702
94743
  } else {
94703
94744
  for (const s5 of m5.subsystems) {
94704
- lines.push(`- ${s5.name} \u2014 ${s5.members} members`);
94745
+ const tag = isTestyCommunity(s5.name) ? " (tests)" : "";
94746
+ lines.push(`- ${s5.name} \u2014 ${s5.members} members${tag}`);
94705
94747
  }
94706
94748
  }
94707
94749
  lines.push("");
@@ -97423,8 +97465,8 @@ function extractQueueGraph(input) {
97423
97465
  }
97424
97466
  return { queues, producers, workers, edges };
97425
97467
  }
97426
- var CELERY_TASK_DEF_RE = /@(?:[\w]+\.)*(?:shared_task|periodic_task|task)\b[^\n]*(?:\n[ \t]*@[^\n]*)*\n[ \t]*(?:async[ \t]+)?def[ \t]+(\w+)/g;
97427
- var CELERY_ENQUEUE_RE = /([A-Za-z_]\w*)\s*\.\s*(?:delay|apply_async)\s*\(/g;
97468
+ var CELERY_TASK_DEF_RE = /@(?:[\w]+\.)*(?:shared_task|db_periodic_task|periodic_task|db_task|task)\b[^\n]*(?:\n[ \t]*@[^\n]*)*\n[ \t]*(?:async[ \t]+)?def[ \t]+(\w+)/g;
97469
+ var CELERY_ENQUEUE_RE = /([A-Za-z_]\w*)\s*\.\s*(?:delay|apply_async|schedule)\s*\(/g;
97428
97470
  function extractCeleryQueueGraph(nodes) {
97429
97471
  const producers = [];
97430
97472
  const workers = [];
@@ -97511,7 +97553,7 @@ async function stitch(client, db, opts = {}) {
97511
97553
  }));
97512
97554
  const graph = extractQueueGraph({ producerClasses, workerFiles });
97513
97555
  const celeryRows = (await client.cypher(
97514
- 'MATCH (n) WHERE n.content CONTAINS ".delay(" OR n.content CONTAINS ".apply_async(" OR n.content CONTAINS "@shared_task" OR n.content CONTAINS "@task" OR n.content CONTAINS ".task" RETURN n.name, n.file_path, n.content'
97556
+ 'MATCH (n) WHERE n.content CONTAINS ".delay(" OR n.content CONTAINS ".apply_async(" OR n.content CONTAINS ".schedule(" OR n.content CONTAINS "@shared_task" OR n.content CONTAINS "@task" OR n.content CONTAINS ".task" OR n.content CONTAINS "periodic_task" OR n.content CONTAINS "db_task" RETURN n.name, n.file_path, n.content'
97515
97557
  )).rows;
97516
97558
  const celeryNodes = celeryRows.map((r) => ({
97517
97559
  name: String(r[0] ?? ""),
@@ -114493,12 +114535,12 @@ async function stopBackendServerPid(root, port) {
114493
114535
  const info2 = await getProcessInfo(rec.pid);
114494
114536
  if (info2 === null) return "none";
114495
114537
  if (!hostArgvRegex(port).test(info2.args)) {
114496
- console.error(
114497
- import_picocolors37.default.red(
114498
- `Backend host pid ${rec.pid} args do not match "horus-source host --port ${port}". Not signalling \u2014 possible stale source/host.json or PID reuse.`
114538
+ console.log(
114539
+ import_picocolors37.default.dim(
114540
+ `Host pid ${rec.pid} is now an unrelated process (stale source/host.json / PID reuse) \u2014 host already gone.`
114499
114541
  )
114500
114542
  );
114501
- return "failed";
114543
+ return "none";
114502
114544
  }
114503
114545
  let terminated;
114504
114546
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@merittdev/horus",
3
- "version": "0.8.18",
3
+ "version": "0.8.19",
4
4
  "description": "Local-first, source-aware production-incident investigation engine",
5
5
  "type": "module",
6
6
  "bin": {