@hivelore/cli 0.43.2 → 0.46.0

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/README.md CHANGED
@@ -233,6 +233,7 @@ Run the repeatable quality gate for Hivelore itself or for a project using Hivel
233
233
  ```bash
234
234
  hivelore eval
235
235
  hivelore eval --semantic-only
236
+ hivelore eval --semantic-ranking # require and exercise the real embeddings-backed ranker
236
237
  hivelore eval --spec .ai/eval/spec.json --fail-under 80
237
238
  ```
238
239
 
@@ -533,14 +534,14 @@ hivelore install-hooks --dir /path/to/project
533
534
 
534
535
  ---
535
536
 
536
- ### `hivelore embeddings`
537
+ ### `hivelore index`
537
538
 
538
539
  Manage the local semantic search index (requires `@hivelore/embeddings` to be installed).
539
540
 
540
541
  ```bash
541
- hivelore embeddings index # Build or refresh the embeddings index
542
- hivelore embeddings status # Show index stats (count, last updated, model)
543
- hivelore embeddings query "how do we handle retries on payment failures"
542
+ hivelore index memories # Build or refresh the embeddings index
543
+ hivelore index status # Show index stats (count, last updated, model)
544
+ hivelore index query "how do we handle retries on payment failures"
544
545
  ```
545
546
 
546
547
  The model (`bge-small-en-v1.5`, ~110MB) is downloaded on first use and cached locally. **No data leaves your machine.**
@@ -655,8 +656,8 @@ Install `@hivelore/embeddings` for similarity-based memory retrieval:
655
656
 
656
657
  ```bash
657
658
  npm install -g @hivelore/embeddings
658
- hivelore embeddings index # First run downloads the model (~110MB)
659
- hivelore embeddings query "payment retry logic"
659
+ hivelore index memories # First run downloads the model (~110MB)
660
+ hivelore index query "payment retry logic"
660
661
  ```
661
662
 
662
663
  From MCP: set `semantic: true` on `mem_search` or `get_briefing`.
@@ -3,6 +3,8 @@
3
3
  // ../mcp/dist/server.js
4
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { mkdir as mkdir8, writeFile as writeFile15 } from "fs/promises";
7
+ import path15 from "path";
6
8
  import { findProjectRoot, resolveHaivePaths } from "@hivelore/core";
7
9
  import { mkdir, writeFile } from "fs/promises";
8
10
  import { existsSync } from "fs";
@@ -153,9 +155,12 @@ import path5 from "path";
153
155
  import { existsSync as existsSync17, statSync } from "fs";
154
156
  import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile10 } from "fs/promises";
155
157
  import path8 from "path";
158
+ import { execFile } from "child_process";
159
+ import { promisify } from "util";
156
160
  import { z as z17 } from "zod";
157
161
  import {
158
162
  buildProposeCommand,
163
+ incidentHintsFromDiff,
159
164
  loadMemoriesFromDir as loadMemoriesFromDir14,
160
165
  normalizeFramework,
161
166
  parseLessonFields,
@@ -1914,6 +1919,12 @@ async function memTried(input, ctx) {
1914
1919
  hint
1915
1920
  };
1916
1921
  }
1922
+ var execFileAsync = promisify(execFile);
1923
+ async function gitDiffText(root, redRef, paths) {
1924
+ const args = ["diff", redRef, "HEAD", "--", ...paths];
1925
+ const { stdout } = await execFileAsync("git", args, { cwd: root, maxBuffer: 64 * 1024 * 1024 });
1926
+ return stdout;
1927
+ }
1917
1928
  var PY_SIGNALS = ["pyproject.toml", "setup.py", "pytest.ini", "requirements.txt", "tox.ini"];
1918
1929
  async function detectForAnchor(root, rel) {
1919
1930
  let dir = path8.resolve(root, rel);
@@ -1968,6 +1979,9 @@ var ScaffoldTestInputSchema = {
1968
1979
  memory_id: z17.string().min(1).describe("Id of the attempt/gotcha lesson to scaffold a post-incident test from."),
1969
1980
  framework: z17.enum(["vitest", "jest", "pytest", "gotest"]).optional().describe("Test framework. Auto-detected from the package that owns the lesson's anchor paths when omitted."),
1970
1981
  out_path: z17.string().optional().describe("Override the generated test file path (repo-relative)."),
1982
+ red_ref: z17.string().optional().describe(
1983
+ "Pre-fix incident commit/ref. When set, the scaffold names the symbols the fix (<red_ref>..HEAD) touched within the lesson's anchor scope and pre-fills the example around them, so the assertion is a targeted edit rather than a blank page. A bad ref falls back to the generic template."
1984
+ ),
1971
1985
  write: z17.boolean().default(true).describe("Write the file to disk (default). false = return the content for preview without writing.")
1972
1986
  };
1973
1987
  async function scaffoldTest(input, ctx) {
@@ -1981,13 +1995,23 @@ async function scaffoldTest(input, ctx) {
1981
1995
  const groups = input.out_path ? allGroups.slice(0, 1) : allGroups;
1982
1996
  const frameworkFor = (detected) => input.framework ? normalizeFramework(input.framework) ?? detected : detected;
1983
1997
  const fields = parseLessonFields(found.memory.body);
1998
+ let incidentHints;
1999
+ if (input.red_ref) {
2000
+ try {
2001
+ const diff = await gitDiffText(ctx.paths.root, input.red_ref, anchorPaths);
2002
+ const hints = incidentHintsFromDiff(diff, { redRef: input.red_ref });
2003
+ if (hints.changedSymbols.length > 0 || hints.changedFiles.length > 0) incidentHints = hints;
2004
+ } catch {
2005
+ }
2006
+ }
1984
2007
  const lesson = {
1985
2008
  memoryId: input.memory_id,
1986
2009
  title: fields.title || input.memory_id,
1987
2010
  whyFailed: fields.whyFailed,
1988
2011
  instead: fields.instead,
1989
2012
  incident: found.memory.frontmatter.sensor?.incident,
1990
- paths: anchorPaths
2013
+ paths: anchorPaths,
2014
+ incidentHints
1991
2015
  };
1992
2016
  let scaffolds = groups.map(
1993
2017
  (g) => scaffoldPostIncidentTest(lesson, { framework: frameworkFor(g.framework), outPath: input.out_path, baseDir: g.baseDir })
@@ -3211,7 +3235,7 @@ function oneLine(value) {
3211
3235
  return value.replace(/\s+/g, " ").replace(/"/g, '\\"').trim().slice(0, 120);
3212
3236
  }
3213
3237
  function serverVersion() {
3214
- return true ? "0.43.2" : "dev";
3238
+ return true ? "0.46.0" : "dev";
3215
3239
  }
3216
3240
  var CodeMapInputSchema = {
3217
3241
  file: z21.string().optional().describe("Filter to files whose path contains this substring"),
@@ -3454,7 +3478,8 @@ var AntiPatternsCheckInputSchema = {
3454
3478
  ),
3455
3479
  min_semantic_score: z26.number().min(0).max(1).default(0.45).describe(
3456
3480
  "Minimum cosine score for semantic-only anti-pattern hits. Anchor/literal matches still surface. Default 0.45 keeps broad, weakly-related memories out of review noise."
3457
- )
3481
+ ),
3482
+ track: z26.boolean().default(true).describe("Record real prevention outcomes. Set false for eval/selftest probes so synthetic cases never inflate ROI.")
3458
3483
  };
3459
3484
  function tokenizeDiffForLiteral(diff) {
3460
3485
  const lines = diff.split("\n");
@@ -3482,6 +3507,7 @@ function isHaiveOwnedPath(p) {
3482
3507
  if (/^\.github\/workflows\/haive-.*\.ya?ml$/.test(p)) return true;
3483
3508
  return false;
3484
3509
  }
3510
+ var MAX_FUZZY_SCAN_LINES = 2e4;
3485
3511
  var TEST_PATH_RE = /(?:^|\/)(?:tests?|__tests__|__mocks__|e2e|fixtures)\/|\.(?:test|spec)\.[cm]?[jt]sx?$/i;
3486
3512
  function isTestPath(p) {
3487
3513
  return TEST_PATH_RE.test(p);
@@ -3492,7 +3518,7 @@ function stripTestHunks(diff) {
3492
3518
  let block = [];
3493
3519
  let keep = true;
3494
3520
  const flush = () => {
3495
- if (keep) out.push(...block);
3521
+ if (keep) for (const l of block) out.push(l);
3496
3522
  block = [];
3497
3523
  keep = true;
3498
3524
  };
@@ -3513,7 +3539,7 @@ function stripAiDirHunks(diff) {
3513
3539
  let block = [];
3514
3540
  let keep = true;
3515
3541
  const flush = () => {
3516
- if (keep) out.push(...block);
3542
+ if (keep) for (const l of block) out.push(l);
3517
3543
  block = [];
3518
3544
  keep = true;
3519
3545
  };
@@ -3584,7 +3610,10 @@ async function antiPatternsCheck(input, ctx) {
3584
3610
  }
3585
3611
  }
3586
3612
  const scanDiff = input.diff ? stripTestHunks(stripAiDirHunks(input.diff)) : input.diff;
3587
- if (scanDiff) {
3613
+ const scanAddedLineCount = scanDiff ? addedLinesFromDiff(scanDiff).split("\n").length : 0;
3614
+ const fuzzyScanTooLarge = scanAddedLineCount > MAX_FUZZY_SCAN_LINES;
3615
+ const notice = fuzzyScanTooLarge ? `Diff is very large (${scanAddedLineCount.toLocaleString()} added lines) \u2014 literal/semantic corroboration skipped for performance; anchored lessons and deterministic sensors were still evaluated in full. If this staged node_modules or a build artifact, unstage it.` : void 0;
3616
+ if (scanDiff && !fuzzyScanTooLarge) {
3588
3617
  const tokens = tokenizeDiffForLiteral(scanDiff);
3589
3618
  const added = addedLinesFromDiff(scanDiff);
3590
3619
  const addedText = added.trim().length > 0 ? added : scanDiff;
@@ -3619,7 +3648,7 @@ async function antiPatternsCheck(input, ctx) {
3619
3648
  }
3620
3649
  }
3621
3650
  }
3622
- if (input.semantic && scanDiff) {
3651
+ if (input.semantic && scanDiff && !fuzzyScanTooLarge) {
3623
3652
  try {
3624
3653
  const mod = await import("@hivelore/embeddings");
3625
3654
  const added = addedLinesFromDiff(scanDiff);
@@ -3647,10 +3676,13 @@ async function antiPatternsCheck(input, ctx) {
3647
3676
  }).slice(0, input.limit);
3648
3677
  const isHardBlockCatch = (w) => w.reasons.includes("sensor");
3649
3678
  const strongCatches = warnings.filter(isHardBlockCatch);
3650
- await recordPreventionHits(ctx.paths, strongCatches.map((w) => w.id), "anti-pattern");
3679
+ if (input.track !== false) {
3680
+ await recordPreventionHits(ctx.paths, strongCatches.map((w) => w.id), "anti-pattern");
3681
+ }
3651
3682
  return {
3652
3683
  scanned: negative.length,
3653
- warnings
3684
+ warnings,
3685
+ ...notice ? { notice } : {}
3654
3686
  };
3655
3687
  }
3656
3688
  var MemDistillInputSchema = {
@@ -3866,6 +3898,7 @@ async function preCommitCheck(input, ctx) {
3866
3898
  relevant_memories: relevant_memories.length,
3867
3899
  stale_anchors: staleHits.length
3868
3900
  },
3901
+ ...apResult.notice ? { notice: apResult.notice } : {},
3869
3902
  warnings: classifiedWarnings,
3870
3903
  relevant_memories,
3871
3904
  stale_anchors: staleHits.map((r) => {
@@ -4376,13 +4409,14 @@ Main code areas detected: ${areas}
4376
4409
  }
4377
4410
  var PostTaskArgsSchema = {
4378
4411
  task_summary: z35.string().optional().describe("One sentence describing what you just did"),
4379
- files_touched: z35.array(z35.string()).optional().describe("Files you created or modified during the task")
4412
+ files_touched: z35.string().optional().describe("Files you created or modified during the task, as CSV or a JSON array string")
4380
4413
  };
4381
4414
  function postTaskPrompt(args, ctx) {
4382
4415
  const taskLine = args.task_summary ? `
4383
4416
  Task just completed: **${args.task_summary}**` : "";
4384
- const filesLine = args.files_touched && args.files_touched.length > 0 ? `
4385
- Files touched: ${args.files_touched.map((f) => `\`${f}\``).join(", ")}` : "";
4417
+ const filesTouched = parsePromptFilesTouched(args.files_touched);
4418
+ const filesLine = filesTouched.length > 0 ? `
4419
+ Files touched: ${filesTouched.map((f) => `\`${f}\``).join(", ")}` : "";
4386
4420
  const text = `You have just finished a task. Before closing this session, take 60 seconds to capture what you learned.
4387
4421
  ${taskLine}${filesLine}
4388
4422
 
@@ -4471,6 +4505,20 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Sessi
4471
4505
  ]
4472
4506
  };
4473
4507
  }
4508
+ function parsePromptFilesTouched(input) {
4509
+ const raw = input?.trim();
4510
+ if (!raw) return [];
4511
+ if (raw.startsWith("[")) {
4512
+ try {
4513
+ const parsed = JSON.parse(raw);
4514
+ if (Array.isArray(parsed)) {
4515
+ return parsed.filter((value) => typeof value === "string").map((value) => value.trim()).filter(Boolean);
4516
+ }
4517
+ } catch {
4518
+ }
4519
+ }
4520
+ return raw.split(",").map((value) => value.trim()).filter(Boolean);
4521
+ }
4474
4522
  var ImportDocsArgsSchema = {
4475
4523
  content: z36.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
4476
4524
  source: z36.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
@@ -4538,7 +4586,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
4538
4586
  };
4539
4587
  }
4540
4588
  var SERVER_NAME = "hivelore";
4541
- var SERVER_VERSION = "0.43.2";
4589
+ var SERVER_VERSION = "0.46.0";
4542
4590
  function jsonResult(data) {
4543
4591
  return {
4544
4592
  content: [
@@ -5477,11 +5525,22 @@ function printHaiveMcpVersion() {
5477
5525
  }
5478
5526
  async function runHaiveMcpStdio(options) {
5479
5527
  const { server, context } = createHaiveServer({ root: options.root, env: process.env });
5528
+ await writeMcpRuntimeMarker(context).catch(() => {
5529
+ });
5480
5530
  console.error(
5481
5531
  `[haive-mcp] starting server v${SERVER_VERSION} (project root: ${context.paths.root})`
5482
5532
  );
5483
5533
  await server.connect(new StdioServerTransport());
5484
5534
  }
5535
+ async function writeMcpRuntimeMarker(context) {
5536
+ await mkdir8(context.paths.runtimeDir, { recursive: true });
5537
+ await writeFile15(path15.join(context.paths.runtimeDir, "mcp-server.json"), JSON.stringify({
5538
+ version: SERVER_VERSION,
5539
+ pid: process.pid,
5540
+ started_at: (/* @__PURE__ */ new Date()).toISOString(),
5541
+ command: "hivelore mcp --stdio"
5542
+ }, null, 2), "utf8");
5543
+ }
5485
5544
 
5486
5545
  export {
5487
5546
  astEngineAvailable,
@@ -5516,6 +5575,7 @@ export {
5516
5575
  createHaiveServer,
5517
5576
  parseMcpCliArgs,
5518
5577
  printHaiveMcpVersion,
5519
- runHaiveMcpStdio
5578
+ runHaiveMcpStdio,
5579
+ writeMcpRuntimeMarker
5520
5580
  };
5521
- //# sourceMappingURL=chunk-FXDGOBPT.js.map
5581
+ //# sourceMappingURL=chunk-W7EPRKOZ.js.map