@hiveai/cli 0.12.0 → 0.12.3

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
@@ -187,6 +187,35 @@ Autogenerated sensors are conservative: they start as `warn` and `autogen: true`
187
187
  high-confidence sensors to `severity: block`, which makes a deterministic pre-commit blocker when the
188
188
  sensor matches added diff lines.
189
189
 
190
+ ### `haive eval`
191
+
192
+ Run the repeatable quality gate for hAIve itself or for a project using hAIve:
193
+
194
+ ```bash
195
+ haive eval
196
+ haive eval --semantic-only
197
+ haive eval --spec .ai/eval/spec.json --fail-under 80
198
+ ```
199
+
200
+ Without `--spec`, hAIve synthesizes retrieval cases from anchored memories. If `.ai/eval/spec.json`
201
+ exists, it is loaded automatically and merged with those synthesized retrieval cases. Use that file
202
+ for labeled sensor cases and hard retrieval probes so CI measures both “did the right memory surface?”
203
+ and “did the executable guardrail fire?”.
204
+
205
+ ### `haive doctor`
206
+
207
+ `doctor` is the first stop when hAIve feels inconsistent locally:
208
+
209
+ ```bash
210
+ haive doctor
211
+ haive doctor --json
212
+ haive doctor --fix
213
+ ```
214
+
215
+ It reports missing `pnpm`, stale workspace `dist` artifacts after a pull, global CLI/MCP version skew,
216
+ outdated code-search indexes, memory-lint findings, and harness coverage. The output is intentionally
217
+ actionable: every setup finding should carry the exact command to run next.
218
+
190
219
  ### `haive benchmark`
191
220
 
192
221
  Turn hAIve-vs-plain agent trials into a repeatable demo/report.
package/dist/index.js CHANGED
@@ -2743,7 +2743,7 @@ ${SEED_FOOTER(stack)}` });
2743
2743
  }
2744
2744
 
2745
2745
  // src/commands/init.ts
2746
- var HAIVE_GITHUB_ACTION_REF = `v${"0.12.0"}`;
2746
+ var HAIVE_GITHUB_ACTION_REF = `v${"0.12.3"}`;
2747
2747
  var PROJECT_CONTEXT_TEMPLATE = `# Project context
2748
2748
 
2749
2749
  > Generated by \`haive init\`. Run \`haive init --bootstrap\` to auto-fill from your codebase,
@@ -3782,6 +3782,7 @@ import {
3782
3782
  loadMemoriesFromDir as loadMemoriesFromDir14,
3783
3783
  loadUsageIndex as loadUsageIndex8,
3784
3784
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths22,
3785
+ rankMemoriesLexical as rankMemoriesLexical2,
3785
3786
  queryCodeMap as queryCodeMap2,
3786
3787
  resolveBriefingBudget as resolveBriefingBudget2,
3787
3788
  serializeMemory as serializeMemory9,
@@ -5472,13 +5473,25 @@ async function getBriefing(input, ctx) {
5472
5473
  }
5473
5474
  if (act.applicable && act.activated) activatedSkills.add(id);
5474
5475
  }
5476
+ const lexNorm = /* @__PURE__ */ new Map();
5477
+ if (input.task) {
5478
+ const candidates = [...seen.keys()].map((id) => byId.get(id)).filter((x) => Boolean(x));
5479
+ const lex = rankMemoriesLexical2(candidates, input.task, candidates.length);
5480
+ const maxScore = lex.scores.reduce((m, s) => s > m ? s : m, 0);
5481
+ if (maxScore > 0) {
5482
+ lex.ranked.forEach((loaded, i) => {
5483
+ lexNorm.set(loaded.memory.frontmatter.id, (lex.scores[i] ?? 0) / maxScore);
5484
+ });
5485
+ }
5486
+ }
5475
5487
  const ranked = [...seen.values()].sort((a, b) => {
5476
5488
  const reasonScore = (m) => (m.type === "attempt" ? 3 : 0) + (m.reasons.includes("anchor") ? 4 : 0) + (m.reasons.includes("symbol") ? 4 : 0) + (m.reasons.includes("module") ? 2 : 0) + (m.reasons.includes("semantic") ? 2 : 0) + (m.reasons.includes("domain") ? 1 : 0);
5477
5489
  const confidenceScore = (m) => m.confidence === "authoritative" ? 4 : m.confidence === "trusted" ? 3 : m.confidence === "low" ? 1 : m.confidence === "stale" ? -2 : 0;
5478
5490
  const impactScore = (m) => (m.impact_score ?? 0) * 3;
5479
5491
  const activationBoost = (m) => activatedSkills.has(m.id) ? 5 : 0;
5480
- const sa = priorityRank(classifyMemoryPriority(a, byId.get(a.id), input.files, input.symbols)) * 100 + reasonScore(a) + confidenceScore(a) + impactScore(a) + activationBoost(a) + (a.semantic_score ?? 0);
5481
- const sb = priorityRank(classifyMemoryPriority(b, byId.get(b.id), input.files, input.symbols)) * 100 + reasonScore(b) + confidenceScore(b) + impactScore(b) + activationBoost(b) + (b.semantic_score ?? 0);
5492
+ const lexScore = (m) => 12 * (lexNorm.get(m.id) ?? 0);
5493
+ const sa = priorityRank(classifyMemoryPriority(a, byId.get(a.id), input.files, input.symbols)) * 100 + reasonScore(a) + confidenceScore(a) + impactScore(a) + activationBoost(a) + lexScore(a) + (a.semantic_score ?? 0);
5494
+ const sb = priorityRank(classifyMemoryPriority(b, byId.get(b.id), input.files, input.symbols)) * 100 + reasonScore(b) + confidenceScore(b) + impactScore(b) + activationBoost(b) + lexScore(b) + (b.semantic_score ?? 0);
5482
5495
  return sb - sa;
5483
5496
  });
5484
5497
  for (const mem of ranked.slice(0, briefingMaxMemories)) {
@@ -7518,7 +7531,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
7518
7531
  };
7519
7532
  }
7520
7533
  var SERVER_NAME = "haive";
7521
- var SERVER_VERSION = "0.12.0";
7534
+ var SERVER_VERSION = "0.12.3";
7522
7535
  function jsonResult(data) {
7523
7536
  return {
7524
7537
  content: [
@@ -12246,7 +12259,8 @@ function registerEval(program2) {
12246
12259
  }
12247
12260
  const k = Math.max(1, parseInt(opts.top ?? "8", 10) || 8);
12248
12261
  const ctx = { paths };
12249
- const spec = await resolveSpec(opts, paths.memoriesDir);
12262
+ const resolvedSpec = await resolveSpec(opts, root, paths.memoriesDir);
12263
+ const spec = resolvedSpec.spec;
12250
12264
  if ((spec.retrieval?.length ?? 0) === 0 && (spec.sensors?.length ?? 0) === 0) {
12251
12265
  ui.warn("No eval cases (no anchored memories and no --spec). Nothing to score.");
12252
12266
  return;
@@ -12271,9 +12285,9 @@ function registerEval(program2) {
12271
12285
  }
12272
12286
  const report = buildReport(retrievalAgg, sensorAgg);
12273
12287
  if (opts.json) {
12274
- console.log(JSON.stringify({ root, k, report }, null, 2));
12288
+ console.log(JSON.stringify({ root, k, spec_source: resolvedSpec.source, report }, null, 2));
12275
12289
  } else {
12276
- const md = renderMarkdown2(root, k, report);
12290
+ const md = renderMarkdown2(root, k, resolvedSpec.source, report);
12277
12291
  if (opts.out) {
12278
12292
  const outFile = path43.isAbsolute(opts.out) ? opts.out : path43.join(root, opts.out);
12279
12293
  await writeFile29(outFile, md, "utf8");
@@ -12294,14 +12308,31 @@ function registerEval(program2) {
12294
12308
  }
12295
12309
  });
12296
12310
  }
12297
- async function resolveSpec(opts, memoriesDir) {
12311
+ async function resolveSpec(opts, root, memoriesDir) {
12298
12312
  if (opts.spec) {
12299
12313
  const file = path43.resolve(opts.spec);
12300
12314
  const raw = await readFile20(file, "utf8");
12301
- return JSON.parse(raw);
12315
+ return { spec: JSON.parse(raw), source: file };
12316
+ }
12317
+ const defaultSpec = path43.join(root, ".ai", "eval", "spec.json");
12318
+ if (existsSync64(defaultSpec)) {
12319
+ const raw = await readFile20(defaultSpec, "utf8");
12320
+ const explicit = JSON.parse(raw);
12321
+ const memories2 = await loadMemoriesFromDir26(memoriesDir);
12322
+ const synthesized = synthesizeSelfEvalCases(memories2, { includeFiles: !opts.semanticOnly });
12323
+ return {
12324
+ spec: {
12325
+ retrieval: [...synthesized, ...explicit.retrieval ?? []],
12326
+ sensors: explicit.sensors ?? []
12327
+ },
12328
+ source: ".ai/eval/spec.json + synthesized anchored retrieval"
12329
+ };
12302
12330
  }
12303
12331
  const memories = await loadMemoriesFromDir26(memoriesDir);
12304
- return { retrieval: synthesizeSelfEvalCases(memories, { includeFiles: !opts.semanticOnly }) };
12332
+ return {
12333
+ spec: { retrieval: synthesizeSelfEvalCases(memories, { includeFiles: !opts.semanticOnly }) },
12334
+ source: "synthesized anchored retrieval"
12335
+ };
12305
12336
  }
12306
12337
  async function runRetrieval(c, k, ctx) {
12307
12338
  const out = await getBriefing(
@@ -12333,11 +12364,12 @@ async function runSensorCase(c, ctx) {
12333
12364
  function pct(n) {
12334
12365
  return `${Math.round(n * 100)}%`;
12335
12366
  }
12336
- function renderMarkdown2(root, k, report) {
12367
+ function renderMarkdown2(root, k, source, report) {
12337
12368
  const lines = [
12338
12369
  "# hAIve eval report",
12339
12370
  "",
12340
12371
  `Project: \`${root}\` \xB7 top-k: ${k}`,
12372
+ `Spec: ${source}`,
12341
12373
  "",
12342
12374
  `## Overall score: ${report.score}/100`,
12343
12375
  ""
@@ -12969,14 +13001,15 @@ function registerDoctor(program2) {
12969
13001
  fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
12970
13002
  });
12971
13003
  }
12972
- findings.push(...await collectInstallFindings(root, "0.12.0"));
13004
+ findings.push(...await collectInstallFindings(root, "0.12.3"));
13005
+ findings.push(...await collectToolchainFindings(root));
12973
13006
  try {
12974
13007
  const legacyRaw = execSync3("haive-mcp --version", {
12975
13008
  encoding: "utf8",
12976
13009
  timeout: 3e3,
12977
13010
  stdio: ["ignore", "pipe", "ignore"]
12978
13011
  }).trim();
12979
- const cliVersion = "0.12.0";
13012
+ const cliVersion = "0.12.3";
12980
13013
  if (legacyRaw && legacyRaw !== cliVersion) {
12981
13014
  findings.push({
12982
13015
  severity: "warn",
@@ -13253,6 +13286,7 @@ function isSearchTool(name) {
13253
13286
  async function collectInstallFindings(root, expectedVersion) {
13254
13287
  const findings = [];
13255
13288
  findings.push(...await collectWorkspaceVersionFindings(root, expectedVersion));
13289
+ findings.push(...await collectDistFreshnessFindings(root, expectedVersion));
13256
13290
  const haiveBins = listHaiveBins();
13257
13291
  if (haiveBins.length === 0) {
13258
13292
  findings.push({
@@ -13316,6 +13350,71 @@ which -a haive`
13316
13350
  }
13317
13351
  return findings;
13318
13352
  }
13353
+ async function collectToolchainFindings(root) {
13354
+ const findings = [];
13355
+ const pkg = await readJson(
13356
+ path46.join(root, "package.json")
13357
+ );
13358
+ const wantsPnpm = pkg?.packageManager?.startsWith("pnpm@") || Object.values(pkg?.scripts ?? {}).some((script) => /\bpnpm\b/.test(script));
13359
+ if (!wantsPnpm) return findings;
13360
+ const expected = pkg?.packageManager?.replace(/^pnpm@/, "") ?? "9.14.2";
13361
+ if (!commandExists2("pnpm", ["--version"])) {
13362
+ const corepackAvailable = commandExists2("corepack", ["--version"]);
13363
+ findings.push({
13364
+ severity: "warn",
13365
+ code: "pnpm-not-on-path",
13366
+ message: `This workspace uses pnpm${expected ? ` ${expected}` : ""}, but no pnpm binary is available on PATH. Local build/test commands may fail even though CI works.`,
13367
+ fix: corepackAvailable ? `corepack prepare pnpm@${expected} --activate` : `npx pnpm@${expected} install --frozen-lockfile`,
13368
+ section: "Agent coverage"
13369
+ });
13370
+ }
13371
+ return findings;
13372
+ }
13373
+ async function collectDistFreshnessFindings(root, expectedVersion) {
13374
+ const findings = [];
13375
+ const isHaiveWorkspace = (await readJson(path46.join(root, "package.json")))?.name === "haive-monorepo";
13376
+ if (!isHaiveWorkspace) return findings;
13377
+ const cliDist = path46.join(root, "packages/cli/dist/index.js");
13378
+ if (!existsSync67(cliDist)) {
13379
+ findings.push({
13380
+ severity: "warn",
13381
+ code: "workspace-dist-missing",
13382
+ message: "packages/cli/dist/index.js is missing; local hAIve smoke commands cannot reflect source changes.",
13383
+ fix: "pnpm -r build",
13384
+ section: "Agent coverage"
13385
+ });
13386
+ return findings;
13387
+ }
13388
+ const distVersion = versionForNodeEntrypoint(cliDist);
13389
+ if (distVersion && distVersion !== expectedVersion) {
13390
+ findings.push({
13391
+ severity: "warn",
13392
+ code: "workspace-dist-version-mismatch",
13393
+ message: `packages/cli/dist/index.js reports ${distVersion}, but this source build expects ${expectedVersion}. Run a fresh workspace build after pull before trusting local doctor/enforce output.`,
13394
+ fix: "pnpm -r build\npnpm check:artifacts",
13395
+ section: "Agent coverage"
13396
+ });
13397
+ }
13398
+ const sourceFiles = [
13399
+ "packages/core/src/index.ts",
13400
+ "packages/mcp/src/server.ts",
13401
+ "packages/cli/src/index.ts"
13402
+ ].map((rel) => path46.join(root, rel)).filter(existsSync67);
13403
+ if (sourceFiles.length > 0) {
13404
+ const distMtime = statSync2(cliDist).mtimeMs;
13405
+ const newestSource = Math.max(...sourceFiles.map((file) => statSync2(file).mtimeMs));
13406
+ if (newestSource > distMtime + 1e3) {
13407
+ findings.push({
13408
+ severity: "info",
13409
+ code: "workspace-dist-older-than-source",
13410
+ message: "Built CLI artifacts are older than key source files; rebuild before running release smoke checks.",
13411
+ fix: "pnpm -r build\npnpm check:artifacts",
13412
+ section: "Agent coverage"
13413
+ });
13414
+ }
13415
+ }
13416
+ return findings;
13417
+ }
13319
13418
  async function collectWorkspaceVersionFindings(root, expectedVersion) {
13320
13419
  const findings = [];
13321
13420
  const rootPkg = await readJson(path46.join(root, "package.json"));
@@ -13419,6 +13518,30 @@ function versionForBinary(bin) {
13419
13518
  return null;
13420
13519
  }
13421
13520
  }
13521
+ function versionForNodeEntrypoint(file) {
13522
+ try {
13523
+ const out = execFileSync(process.execPath, [file, "--version"], {
13524
+ encoding: "utf8",
13525
+ timeout: 3e3,
13526
+ stdio: ["ignore", "pipe", "ignore"]
13527
+ }).trim();
13528
+ return out.match(/\d+\.\d+\.\d+/)?.[0] ?? null;
13529
+ } catch {
13530
+ return null;
13531
+ }
13532
+ }
13533
+ function commandExists2(command, args) {
13534
+ try {
13535
+ execFileSync(command, args, {
13536
+ encoding: "utf8",
13537
+ timeout: 3e3,
13538
+ stdio: ["ignore", "pipe", "ignore"]
13539
+ });
13540
+ return true;
13541
+ } catch {
13542
+ return false;
13543
+ }
13544
+ }
13422
13545
  function extractAbsoluteHaiveBins(text) {
13423
13546
  const out = /* @__PURE__ */ new Set();
13424
13547
  const re = /(["'\s])((?:\/[^"'\s]+)*\/haive)\b/g;
@@ -14477,7 +14600,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
14477
14600
  findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
14478
14601
  });
14479
14602
  }
14480
- findings.push(...await inspectIntegrationVersions(root, "0.12.0"));
14603
+ findings.push(...await inspectIntegrationVersions(root, "0.12.3"));
14481
14604
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
14482
14605
  const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
14483
14606
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
@@ -14615,6 +14738,15 @@ async function verifyDecisionCoverage(paths, stage, sessionId) {
14615
14738
  }];
14616
14739
  }
14617
14740
  const marker = await readRecentBriefingMarker(paths, sessionId);
14741
+ if (stage === "ci" && !marker) {
14742
+ return [{
14743
+ severity: "ok",
14744
+ code: "decision-coverage-ci-pass",
14745
+ message: `CI surfaced ${relevant.length} relevant anchored decision/polic${relevant.length === 1 ? "y" : "ies"} for ${changedFiles.length} changed file(s). Runtime briefing markers are local-only and are not expected on GitHub Actions.`,
14746
+ memory_ids: relevant.slice(0, 20).map((memory2) => memory2.frontmatter.id),
14747
+ affected_files: changedFiles.slice(0, 10)
14748
+ }];
14749
+ }
14618
14750
  const consulted = new Set(marker?.memory_ids ?? []);
14619
14751
  const missing = relevant.filter((memory2) => !consulted.has(memory2.frontmatter.id));
14620
14752
  if (missing.length === 0) {
@@ -15476,7 +15608,7 @@ function shellQuote(value) {
15476
15608
 
15477
15609
  // src/index.ts
15478
15610
  var program = new Command56();
15479
- program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.12.0").option("--advanced", "show maintenance and experimental commands in help");
15611
+ program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.12.3").option("--advanced", "show maintenance and experimental commands in help");
15480
15612
  registerInit(program);
15481
15613
  registerWelcome(program);
15482
15614
  registerResolveProject(program);