@merittdev/horus 0.1.11 → 0.1.12
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.cjs +215 -7
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -50314,7 +50314,7 @@ init_cjs_shims();
|
|
|
50314
50314
|
|
|
50315
50315
|
// ../../packages/core/src/version.ts
|
|
50316
50316
|
init_cjs_shims();
|
|
50317
|
-
var HORUS_VERSION = true ? "0.1.
|
|
50317
|
+
var HORUS_VERSION = true ? "0.1.12" : "dev";
|
|
50318
50318
|
var PINNED_AXON_VERSION = "1.0.7";
|
|
50319
50319
|
var PINNED_SOURCE_VERSION = PINNED_AXON_VERSION;
|
|
50320
50320
|
|
|
@@ -70237,8 +70237,10 @@ async function investigate(input, deps) {
|
|
|
70237
70237
|
const latencyMetricEvIds = [];
|
|
70238
70238
|
const queueMetricEvIds = [];
|
|
70239
70239
|
const queueMetricEvIdsByQueue = /* @__PURE__ */ new Map();
|
|
70240
|
+
const nominalMetricEvIds = [];
|
|
70240
70241
|
let metricsCollected = false;
|
|
70241
70242
|
let metricsFailureReason;
|
|
70243
|
+
let metricSeriesChecked = 0;
|
|
70242
70244
|
if (deps.metrics) {
|
|
70243
70245
|
const ac = new AbortController();
|
|
70244
70246
|
let metricsTimerId;
|
|
@@ -70298,6 +70300,19 @@ async function investigate(input, deps) {
|
|
|
70298
70300
|
}
|
|
70299
70301
|
}
|
|
70300
70302
|
}
|
|
70303
|
+
metricSeriesChecked = mFindings.length;
|
|
70304
|
+
if (metricEvIds.length === 0 && mFindings.length > 0) {
|
|
70305
|
+
const panels = [...new Set(mFindings.map((f) => f.panelTitle))];
|
|
70306
|
+
const ev = mkEv(
|
|
70307
|
+
"metric",
|
|
70308
|
+
`Metrics checked \u2014 ${mFindings.length} series across ${panels.length} panel(s), no anomalies in window`,
|
|
70309
|
+
{ seriesChecked: mFindings.length, panelCount: panels.length, panels: panels.slice(0, 10), anomalies: 0, stance: "neutral" },
|
|
70310
|
+
{},
|
|
70311
|
+
collectedAt2,
|
|
70312
|
+
0.2
|
|
70313
|
+
);
|
|
70314
|
+
nominalMetricEvIds.push(ev.id);
|
|
70315
|
+
}
|
|
70301
70316
|
metricsCollected = true;
|
|
70302
70317
|
} catch (metricsErr) {
|
|
70303
70318
|
metricsFailureReason = metricsErr?.message?.slice(0, 120) ?? "unknown error";
|
|
@@ -70495,6 +70510,13 @@ async function investigate(input, deps) {
|
|
|
70495
70510
|
confidence: 0.7,
|
|
70496
70511
|
evidenceIds: metricEvIds
|
|
70497
70512
|
});
|
|
70513
|
+
} else if (nominalMetricEvIds.length > 0) {
|
|
70514
|
+
findings2.push({
|
|
70515
|
+
kind: "observation",
|
|
70516
|
+
title: `Metrics nominal \u2014 ${metricSeriesChecked} series checked, no anomalies in window`,
|
|
70517
|
+
confidence: 0.5,
|
|
70518
|
+
evidenceIds: nominalMetricEvIds
|
|
70519
|
+
});
|
|
70498
70520
|
}
|
|
70499
70521
|
const causeInputs = [];
|
|
70500
70522
|
const blastRadius = impact.affected;
|
|
@@ -72551,6 +72573,167 @@ function refinedToJSON(r, v) {
|
|
|
72551
72573
|
);
|
|
72552
72574
|
}
|
|
72553
72575
|
|
|
72576
|
+
// ../../packages/engine/src/qa.ts
|
|
72577
|
+
init_cjs_shims();
|
|
72578
|
+
var CONTRADICTS_RE = /\b(contradict|contradicts|argues?\s+against|evidence\s+against|rule\s+out|disprove|refute|weaken)\b/i;
|
|
72579
|
+
var MISSING_RE = /\b(missing|absent|gaps?|don'?t\s+have|do\s+not\s+have|lack(?:ing)?|what\s+else)\b/i;
|
|
72580
|
+
var CONFIDENCE_RE = /\b(confidence|confident|certainty|sure|why\s+(?:is\s+)?(?:it\s+)?not\s+higher)\b/i;
|
|
72581
|
+
function detectQuestion(text2) {
|
|
72582
|
+
const t = text2.toLowerCase();
|
|
72583
|
+
if (CONFIDENCE_RE.test(t)) return "confidence";
|
|
72584
|
+
if (CONTRADICTS_RE.test(t)) return "contradicts";
|
|
72585
|
+
if (MISSING_RE.test(t)) return "missing-evidence";
|
|
72586
|
+
return null;
|
|
72587
|
+
}
|
|
72588
|
+
function categoriesForTopic(text2) {
|
|
72589
|
+
const t = text2.toLowerCase();
|
|
72590
|
+
for (const [topic, entry2] of Object.entries(TOPIC_MAP)) {
|
|
72591
|
+
if (topic === t.trim()) return { topic, categories: entry2.categories };
|
|
72592
|
+
if (entry2.keywords.some((kw) => new RegExp(`\\b${kw}\\b`).test(t))) {
|
|
72593
|
+
return { topic, categories: entry2.categories };
|
|
72594
|
+
}
|
|
72595
|
+
}
|
|
72596
|
+
return null;
|
|
72597
|
+
}
|
|
72598
|
+
function evidenceById(report) {
|
|
72599
|
+
return new Map(report.evidence.map((e) => [e.id, e]));
|
|
72600
|
+
}
|
|
72601
|
+
function answerContradicts(report, question) {
|
|
72602
|
+
const matched = categoriesForTopic(question);
|
|
72603
|
+
const byId = evidenceById(report);
|
|
72604
|
+
const hyps = matched ? report.hypotheses.filter((h) => matched.categories.includes(h.category)) : [];
|
|
72605
|
+
if (matched && hyps.length === 0) {
|
|
72606
|
+
const evaluated = [...new Set(report.hypotheses.map((h) => h.category))];
|
|
72607
|
+
return {
|
|
72608
|
+
question,
|
|
72609
|
+
kind: "contradicts",
|
|
72610
|
+
headline: `"${matched.topic}" was not among the evaluated hypotheses \u2014 no evidence supports it.`,
|
|
72611
|
+
details: evaluated.length > 0 ? [`Hypotheses evaluated: ${evaluated.join(", ")}.`] : ["No hypotheses were formed for this investigation."],
|
|
72612
|
+
evidence: []
|
|
72613
|
+
};
|
|
72614
|
+
}
|
|
72615
|
+
if (hyps.length === 0) {
|
|
72616
|
+
const ids3 = [...new Set(report.hypotheses.flatMap((h) => h.contradictingEvidenceIds))];
|
|
72617
|
+
const ev2 = ids3.map((id) => byId.get(id)).filter((e) => e !== void 0);
|
|
72618
|
+
return {
|
|
72619
|
+
question,
|
|
72620
|
+
kind: "contradicts",
|
|
72621
|
+
headline: ev2.length > 0 ? `${ev2.length} item(s) contradict the leading hypotheses.` : "No contradicting evidence was recorded for any hypothesis.",
|
|
72622
|
+
details: ev2.length > 0 ? [] : ["Evidence either supports or is neutral to the hypotheses."],
|
|
72623
|
+
evidence: ev2
|
|
72624
|
+
};
|
|
72625
|
+
}
|
|
72626
|
+
const ids2 = [...new Set(hyps.flatMap((h) => h.contradictingEvidenceIds))];
|
|
72627
|
+
const ev = ids2.map((id) => byId.get(id)).filter((e) => e !== void 0);
|
|
72628
|
+
const verdicts = [...new Set(hyps.map((h) => h.verdict))].join(", ");
|
|
72629
|
+
if (ev.length === 0) {
|
|
72630
|
+
return {
|
|
72631
|
+
question,
|
|
72632
|
+
kind: "contradicts",
|
|
72633
|
+
headline: `No evidence contradicts "${matched?.topic ?? "this"}" (verdict: ${verdicts}).`,
|
|
72634
|
+
details: hyps.map((h) => `${h.category}: ${h.rationale ?? h.statement}`),
|
|
72635
|
+
evidence: []
|
|
72636
|
+
};
|
|
72637
|
+
}
|
|
72638
|
+
return {
|
|
72639
|
+
question,
|
|
72640
|
+
kind: "contradicts",
|
|
72641
|
+
headline: `${ev.length} item(s) contradict "${matched?.topic ?? "this"}" (verdict: ${verdicts}).`,
|
|
72642
|
+
details: hyps.map((h) => `${h.category}: ${h.rationale ?? h.statement}`),
|
|
72643
|
+
evidence: ev
|
|
72644
|
+
};
|
|
72645
|
+
}
|
|
72646
|
+
function answerMissing(report, question) {
|
|
72647
|
+
const { gaps, blindSpots } = report.gapAnalysis;
|
|
72648
|
+
const hypMissing = [...new Set(report.hypotheses.flatMap((h) => h.missingEvidence))];
|
|
72649
|
+
if (gaps.length === 0 && hypMissing.length === 0) {
|
|
72650
|
+
return {
|
|
72651
|
+
question,
|
|
72652
|
+
kind: "missing-evidence",
|
|
72653
|
+
headline: "No evidence gaps \u2014 all expected dimensions were collected.",
|
|
72654
|
+
details: [],
|
|
72655
|
+
evidence: []
|
|
72656
|
+
};
|
|
72657
|
+
}
|
|
72658
|
+
const details = gaps.map(
|
|
72659
|
+
(g) => `${g.dimension}: ${g.why} \u2192 ${g.nextSource} (\u2212${(g.confidenceImpact * 100).toFixed(0)}% ceiling)`
|
|
72660
|
+
);
|
|
72661
|
+
if (hypMissing.length > 0) {
|
|
72662
|
+
details.push(`To confirm hypotheses: ${hypMissing.join("; ")}.`);
|
|
72663
|
+
}
|
|
72664
|
+
for (const bs of blindSpots) details.push(`Blind spot: ${bs}`);
|
|
72665
|
+
return {
|
|
72666
|
+
question,
|
|
72667
|
+
kind: "missing-evidence",
|
|
72668
|
+
headline: `${gaps.length} evidence gap(s) limit this investigation.`,
|
|
72669
|
+
details,
|
|
72670
|
+
evidence: []
|
|
72671
|
+
};
|
|
72672
|
+
}
|
|
72673
|
+
function answerConfidence(report, question) {
|
|
72674
|
+
const { gaps, confidenceCeiling } = report.gapAnalysis;
|
|
72675
|
+
const ceilingPct = Math.round(confidenceCeiling * 100);
|
|
72676
|
+
const actualPct = Math.round(report.confidence * 100);
|
|
72677
|
+
const limiting = [...gaps].sort((a, b2) => b2.confidenceImpact - a.confidenceImpact);
|
|
72678
|
+
const details = [];
|
|
72679
|
+
if (limiting.length > 0) {
|
|
72680
|
+
details.push(`Confidence is capped at ${ceilingPct}% by missing evidence:`);
|
|
72681
|
+
for (const g of limiting) {
|
|
72682
|
+
details.push(` \u2022 ${g.dimension} (\u2212${(g.confidenceImpact * 100).toFixed(0)}%): ${g.why}`);
|
|
72683
|
+
}
|
|
72684
|
+
} else {
|
|
72685
|
+
details.push("No evidence gaps cap the ceiling.");
|
|
72686
|
+
}
|
|
72687
|
+
const weak = report.hypotheses.filter((h) => h.verdict === "weakened" || h.verdict === "unconfirmed");
|
|
72688
|
+
if (weak.length > 0) {
|
|
72689
|
+
details.push(
|
|
72690
|
+
`Unconfirmed/weakened hypotheses: ${weak.map((h) => `${h.category} (${h.verdict})`).join(", ")}.`
|
|
72691
|
+
);
|
|
72692
|
+
}
|
|
72693
|
+
return {
|
|
72694
|
+
question,
|
|
72695
|
+
kind: "confidence",
|
|
72696
|
+
headline: `Confidence is ${actualPct}% (ceiling ${ceilingPct}%). ${limiting.length > 0 ? "Missing evidence is the main limiter." : "Limited by hypothesis support, not gaps."}`,
|
|
72697
|
+
details,
|
|
72698
|
+
evidence: []
|
|
72699
|
+
};
|
|
72700
|
+
}
|
|
72701
|
+
function answerQuestion(report, question) {
|
|
72702
|
+
const kind = detectQuestion(question);
|
|
72703
|
+
if (kind === null) return null;
|
|
72704
|
+
if (kind === "contradicts") return answerContradicts(report, question);
|
|
72705
|
+
if (kind === "missing-evidence") return answerMissing(report, question);
|
|
72706
|
+
return answerConfidence(report, question);
|
|
72707
|
+
}
|
|
72708
|
+
function renderQAAnswer(a) {
|
|
72709
|
+
const lines = [];
|
|
72710
|
+
lines.push(`Q: ${a.question}`);
|
|
72711
|
+
lines.push("");
|
|
72712
|
+
lines.push(a.headline);
|
|
72713
|
+
for (const d of a.details) lines.push(d.startsWith(" ") ? d : ` ${d}`);
|
|
72714
|
+
if (a.evidence.length > 0) {
|
|
72715
|
+
lines.push("");
|
|
72716
|
+
lines.push("Evidence:");
|
|
72717
|
+
for (const e of a.evidence) {
|
|
72718
|
+
lines.push(` [${e.id.slice(0, 8)}] (${e.kind}) ${e.title}`);
|
|
72719
|
+
}
|
|
72720
|
+
}
|
|
72721
|
+
return lines.join("\n");
|
|
72722
|
+
}
|
|
72723
|
+
function qaToJSON(a) {
|
|
72724
|
+
return JSON.stringify(
|
|
72725
|
+
{
|
|
72726
|
+
question: a.question,
|
|
72727
|
+
kind: a.kind,
|
|
72728
|
+
headline: a.headline,
|
|
72729
|
+
details: a.details,
|
|
72730
|
+
evidence: a.evidence.map((e) => ({ id: e.id, kind: e.kind, title: e.title }))
|
|
72731
|
+
},
|
|
72732
|
+
null,
|
|
72733
|
+
2
|
|
72734
|
+
);
|
|
72735
|
+
}
|
|
72736
|
+
|
|
72554
72737
|
// ../../packages/engine/src/onboard.ts
|
|
72555
72738
|
init_cjs_shims();
|
|
72556
72739
|
function tokenize2(text2) {
|
|
@@ -73714,6 +73897,12 @@ async function runChanges(base2, compare, opts) {
|
|
|
73714
73897
|
// ../../packages/cli/src/commands/timeline.ts
|
|
73715
73898
|
init_cjs_shims();
|
|
73716
73899
|
var import_picocolors7 = __toESM(require_picocolors(), 1);
|
|
73900
|
+
var DEFAULT_SINCE = "7 days ago";
|
|
73901
|
+
function resolveTimelineWindow(opts) {
|
|
73902
|
+
const usingDefault = !opts.all && opts.since === void 0;
|
|
73903
|
+
const since = opts.all ? void 0 : opts.since ?? DEFAULT_SINCE;
|
|
73904
|
+
return { since, usingDefault };
|
|
73905
|
+
}
|
|
73717
73906
|
async function runTimeline(service, opts) {
|
|
73718
73907
|
try {
|
|
73719
73908
|
const config = await loadConfig(opts.config);
|
|
@@ -73725,11 +73914,24 @@ async function runTimeline(service, opts) {
|
|
|
73725
73914
|
return 1;
|
|
73726
73915
|
}
|
|
73727
73916
|
const { code } = createConnectors(config);
|
|
73917
|
+
const { since, usingDefault: usingDefaultWindow } = resolveTimelineWindow(opts);
|
|
73728
73918
|
const t = await reconstructChangeTimeline(
|
|
73729
|
-
{ repoPath: renv.path, since
|
|
73919
|
+
{ repoPath: renv.path, since, until: opts.until, service },
|
|
73730
73920
|
{ code }
|
|
73731
73921
|
);
|
|
73732
|
-
|
|
73922
|
+
if (opts.json) {
|
|
73923
|
+
console.log(changeTimelineToJSON(t));
|
|
73924
|
+
} else {
|
|
73925
|
+
console.log(renderChangeTimeline(t));
|
|
73926
|
+
if (usingDefaultWindow) {
|
|
73927
|
+
console.log(
|
|
73928
|
+
import_picocolors7.default.dim(
|
|
73929
|
+
`
|
|
73930
|
+
Showing the last 7 days (default). Widen with ${import_picocolors7.default.bold('--since "30 days ago"')}, pin a range with ${import_picocolors7.default.bold("--since <when> --until <when>")}, or see everything with ${import_picocolors7.default.bold("--all")}.`
|
|
73931
|
+
)
|
|
73932
|
+
);
|
|
73933
|
+
}
|
|
73934
|
+
}
|
|
73733
73935
|
return 0;
|
|
73734
73936
|
} catch (err) {
|
|
73735
73937
|
console.error(import_picocolors7.default.red(err.message));
|
|
@@ -73740,7 +73942,7 @@ async function runTimeline(service, opts) {
|
|
|
73740
73942
|
// ../../packages/cli/src/commands/what-changed.ts
|
|
73741
73943
|
init_cjs_shims();
|
|
73742
73944
|
var import_picocolors8 = __toESM(require_picocolors(), 1);
|
|
73743
|
-
var
|
|
73945
|
+
var DEFAULT_SINCE2 = "7 days ago";
|
|
73744
73946
|
async function runWhatChanged(service, opts) {
|
|
73745
73947
|
try {
|
|
73746
73948
|
const config = await loadConfig(opts.config);
|
|
@@ -73752,7 +73954,7 @@ async function runWhatChanged(service, opts) {
|
|
|
73752
73954
|
return 1;
|
|
73753
73955
|
}
|
|
73754
73956
|
const { code } = createConnectors(config);
|
|
73755
|
-
const since = opts.since ??
|
|
73957
|
+
const since = opts.since ?? DEFAULT_SINCE2;
|
|
73756
73958
|
const r = await whatChanged(
|
|
73757
73959
|
{ repoPath: renv.path, since, until: opts.until, service },
|
|
73758
73960
|
{ code }
|
|
@@ -74213,6 +74415,11 @@ async function runAsk(id, directive, opts) {
|
|
|
74213
74415
|
return 1;
|
|
74214
74416
|
}
|
|
74215
74417
|
const report = migrateReport(row.report);
|
|
74418
|
+
const answer = answerQuestion(report, directive);
|
|
74419
|
+
if (answer) {
|
|
74420
|
+
console.log(opts.json ? qaToJSON(answer) : renderQAAnswer(answer));
|
|
74421
|
+
return 0;
|
|
74422
|
+
}
|
|
74216
74423
|
const v = refineInvestigation(report, directive);
|
|
74217
74424
|
console.log(opts.json ? refinedToJSON(report, v) : renderRefined(report, v));
|
|
74218
74425
|
if (!opts.json && report.aiJudgment) {
|
|
@@ -76677,13 +76884,14 @@ Examples:
|
|
|
76677
76884
|
});
|
|
76678
76885
|
program2.command("timeline [service]").description(
|
|
76679
76886
|
"Reconstruct what changed in a time window (git + change-impact) \u2014 evidence, not conclusions"
|
|
76680
|
-
).option("-c, --config <path>", "path to horus.config.ts").option("--repo <name>", "repository name from config").option("--since <when>", 'git --since (e.g. "
|
|
76887
|
+
).option("-c, --config <path>", "path to horus.config.ts").option("--repo <name>", "repository name from config").option("--since <when>", 'git --since (default "7 days ago"; e.g. "30 days ago", a date)').option("--until <when>", "git --until").option("--all", "include all history instead of the default recent window").option("--json", "output JSON").action(
|
|
76681
76888
|
async (service, opts) => {
|
|
76682
76889
|
process.exitCode = await runTimeline(service, {
|
|
76683
76890
|
config: opts.config,
|
|
76684
76891
|
repo: opts.repo,
|
|
76685
76892
|
since: opts.since,
|
|
76686
76893
|
until: opts.until,
|
|
76894
|
+
all: opts.all,
|
|
76687
76895
|
json: opts.json
|
|
76688
76896
|
});
|
|
76689
76897
|
}
|
|
@@ -76781,7 +76989,7 @@ Examples:
|
|
|
76781
76989
|
process.exitCode = await runScore(id, { config: opts.config, json: opts.json });
|
|
76782
76990
|
});
|
|
76783
76991
|
program2.command("ask <id> <directive>").description(
|
|
76784
|
-
'
|
|
76992
|
+
'Ask about or refine a saved investigation \u2014 reuses evidence, no re-query.\n Questions (direct answers):\n "what evidence contradicts <topic>?" \xB7 "what evidence is missing?"\n "why is confidence not higher?"\n Topic filters (deterministic scoping):\n "focus on queue behavior" \xB7 "ignore deployment changes" \xB7 "retry"'
|
|
76785
76993
|
).option("-c, --config <path>", "path to horus.config.ts").option("--json", "output JSON").action(async (id, directive, opts) => {
|
|
76786
76994
|
process.exitCode = await runAsk(id, directive, { config: opts.config, json: opts.json });
|
|
76787
76995
|
});
|