@hiveai/cli 0.15.0 → 0.17.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/dist/index.js CHANGED
@@ -9,9 +9,10 @@ import { mkdir, readFile as readFile2 } from "fs/promises";
9
9
  import path3 from "path";
10
10
  import "commander";
11
11
  import {
12
+ classifyMemoryPriority,
13
+ compactAutoRecapBody,
12
14
  extractActionsBriefBody,
13
15
  findProjectRoot as findProjectRoot2,
14
- isStackPackSeed,
15
16
  literalMatchesAllTokens,
16
17
  literalMatchesAnyToken,
17
18
  loadCodeMap as loadCodeMap3,
@@ -954,7 +955,7 @@ function registerBriefing(program2) {
954
955
  out(`${ui.bold("=== Last Session Recap ===")}
955
956
  `);
956
957
  out(ui.dim(`${fm.id} (${fm.scope}${rev})`));
957
- out(recap.memory.body.trim());
958
+ out(compactAutoRecapBody(recap.memory.body).trim());
958
959
  out("");
959
960
  }
960
961
  if (existsSync3(paths.projectContext) && !stopped()) {
@@ -1124,12 +1125,19 @@ ${ui.bold("=== Symbol Locations ===")}
1124
1125
  function classifyCliPriority(item, filePaths, tokens, exactTaskHit, partialTaskHit) {
1125
1126
  const fm = item.memory.frontmatter;
1126
1127
  const anchored = filePaths.length > 0 && memoryMatchesAnchorPaths(item.memory, filePaths);
1127
- if (anchored || fm.type === "attempt" && exactTaskHit) return "must_read";
1128
- if (isStackPackSeed(fm)) return "background";
1129
- if (exactTaskHit || partialTaskHit || item.score >= 4 || tokens && fm.tags.some((tag) => tokens.includes(tag))) {
1130
- return "useful";
1131
- }
1132
- return "background";
1128
+ return classifyMemoryPriority({
1129
+ type: fm.type,
1130
+ tags: fm.tags,
1131
+ requiresHumanApproval: Boolean(fm.requires_human_approval),
1132
+ directAnchor: anchored,
1133
+ directSymbol: false,
1134
+ // symbol lookup is rendered separately in the CLI, not via anchor priority
1135
+ exactTaskMatch: exactTaskHit,
1136
+ strongSemantic: false,
1137
+ usefulSemantic: partialTaskHit || item.score >= 4,
1138
+ moduleOrDomainMatch: false,
1139
+ tagTaskMatch: Boolean(tokens && fm.tags.some((tag) => tokens.includes(tag)))
1140
+ });
1133
1141
  }
1134
1142
  function priorityBadge(priority) {
1135
1143
  if (priority === "must_read") return ui.red("[must_read]");
@@ -3019,7 +3027,7 @@ ${SEED_FOOTER(stack)}` });
3019
3027
  }
3020
3028
 
3021
3029
  // src/commands/init.ts
3022
- var HAIVE_GITHUB_ACTION_REF = `v${"0.15.0"}`;
3030
+ var HAIVE_GITHUB_ACTION_REF = `v${"0.17.0"}`;
3023
3031
  var PROJECT_CONTEXT_TEMPLATE = `# Project context
3024
3032
 
3025
3033
  > Generated by \`haive init\`. Run \`haive init --bootstrap\` to auto-fill from your codebase,
@@ -3834,14 +3842,21 @@ async function readStdin(maxBytes) {
3834
3842
  setTimeout(finish, 2e3);
3835
3843
  });
3836
3844
  }
3845
+ function isExpectedNonzeroExit(command) {
3846
+ if (!command) return false;
3847
+ if (command.includes("|")) return true;
3848
+ if (/\|\|\s*true\b/.test(command)) return true;
3849
+ return /(^|\s|;|&&)(grep|egrep|fgrep|rg|ag|find|test|diff|\[\[?)\b/.test(command);
3850
+ }
3837
3851
  function detectFailure(payload) {
3838
3852
  const response = payload.tool_response;
3839
3853
  if (!response) return false;
3840
3854
  const responseText = typeof response === "string" ? response : JSON.stringify(response);
3841
3855
  if (payload.tool_name === "Bash") {
3856
+ const command = typeof payload.tool_input?.["command"] === "string" ? payload.tool_input["command"] : "";
3842
3857
  if (typeof response === "object") {
3843
3858
  const code = response["exit_code"] ?? response["exitCode"];
3844
- if (typeof code === "number" && code !== 0) return true;
3859
+ if (typeof code === "number" && code !== 0 && !isExpectedNonzeroExit(command)) return true;
3845
3860
  }
3846
3861
  if (/\b(command not found|No such file or directory|ERR_MODULE_NOT_FOUND|ENOENT|EACCES)\b/.test(responseText)) return true;
3847
3862
  if (/\berror TS\d+:/i.test(responseText)) return true;
@@ -4083,6 +4098,7 @@ import {
4083
4098
  deriveConfidence as deriveConfidence4,
4084
4099
  estimateTokens,
4085
4100
  evaluateSkillActivation,
4101
+ compactAutoRecapBody as compactAutoRecapBody2,
4086
4102
  extractActionsBriefBody as extractActionsBriefBody2,
4087
4103
  getUsage as getUsage6,
4088
4104
  inferModulesFromPaths as inferModulesFromPaths2,
@@ -4114,7 +4130,12 @@ import { z as z19 } from "zod";
4114
4130
  import { readdir as readdir3, readFile as readFile42 } from "fs/promises";
4115
4131
  import { existsSync as existsSync20 } from "fs";
4116
4132
  import path102 from "path";
4117
- import { isGlobPath, isStackPackSeed as isStackPackSeed2, pathsOverlap } from "@hiveai/core";
4133
+ import {
4134
+ classifyMemoryPriority as coreClassifyPriority,
4135
+ isGlobPath,
4136
+ pathsOverlap,
4137
+ priorityRank as corePriorityRank
4138
+ } from "@hiveai/core";
4118
4139
  import { estimateTokens as estimateTokens2, loadCodeMap as loadCodeMap22, queryCodeMap as queryCodeMap22 } from "@hiveai/core";
4119
4140
  import { z as z20 } from "zod";
4120
4141
  import { existsSync as existsSync222 } from "fs";
@@ -5577,7 +5598,7 @@ function compactSummary(body) {
5577
5598
  }
5578
5599
  return body.slice(0, 120);
5579
5600
  }
5580
- function classifyMemoryPriority(memory2, loaded, inputFiles, inputSymbols) {
5601
+ function classifyMemoryPriority2(memory2, loaded, inputFiles, inputSymbols) {
5581
5602
  const fm = loaded?.memory.frontmatter;
5582
5603
  const directAnchor = Boolean(
5583
5604
  fm && inputFiles.length > 0 && fm.anchor.paths.some((p) => inputFiles.some((file) => pathsOverlap(p, file)))
@@ -5587,21 +5608,23 @@ function classifyMemoryPriority(memory2, loaded, inputFiles, inputSymbols) {
5587
5608
  (sym) => inputSymbols.some((wanted) => wanted.toLowerCase() === sym.toLowerCase())
5588
5609
  )
5589
5610
  );
5590
- const strongSemantic = (memory2.semantic_score ?? 0) >= 0.65;
5591
- const usefulSemantic = (memory2.semantic_score ?? 0) >= 0.35;
5592
- if (fm?.requires_human_approval || directAnchor || directSymbol || memory2.type === "attempt" && (memory2.match_quality === "exact" || strongSemantic) || memory2.type === "skill" && (memory2.match_quality === "exact" || strongSemantic)) {
5593
- return "must_read";
5594
- }
5595
- if (isStackPackSeed2(fm)) {
5596
- return "background";
5597
- }
5598
- if (memory2.type === "skill" || memory2.reasons.includes("module") || memory2.reasons.includes("domain") || memory2.match_quality === "exact" || usefulSemantic) {
5599
- return "useful";
5600
- }
5601
- return "background";
5611
+ const semantic = memory2.semantic_score ?? 0;
5612
+ return coreClassifyPriority({
5613
+ type: memory2.type,
5614
+ tags: fm?.tags ?? memory2.tags ?? [],
5615
+ requiresHumanApproval: Boolean(fm?.requires_human_approval),
5616
+ directAnchor,
5617
+ directSymbol,
5618
+ exactTaskMatch: memory2.match_quality === "exact",
5619
+ strongSemantic: semantic >= 0.65,
5620
+ usefulSemantic: semantic >= 0.35,
5621
+ moduleOrDomainMatch: memory2.reasons.includes("module") || memory2.reasons.includes("domain"),
5622
+ tagTaskMatch: false
5623
+ // MCP ranking doesn't use a separate tag-token signal
5624
+ });
5602
5625
  }
5603
5626
  function priorityRank(priority) {
5604
- return priority === "must_read" ? 3 : priority === "useful" ? 2 : 1;
5627
+ return corePriorityRank(priority);
5605
5628
  }
5606
5629
  function classifyBriefingQuality(memories, context) {
5607
5630
  const mustRead = memories.filter((m) => m.priority === "must_read").length;
@@ -5768,7 +5791,9 @@ async function getBriefing(input, ctx) {
5768
5791
  id: fm.id,
5769
5792
  scope: fm.scope,
5770
5793
  revision_count: fm.revision_count ?? 0,
5771
- body: r.memory.body
5794
+ // Auto-generated recaps are low-signal tool dumps — compact them so they inform without
5795
+ // dominating the briefing head. Human/post_task recaps pass through unchanged.
5796
+ body: compactAutoRecapBody2(r.memory.body)
5772
5797
  };
5773
5798
  }
5774
5799
  const allMemories = allLoaded.filter(({ memory: memory2 }) => {
@@ -5890,8 +5915,8 @@ async function getBriefing(input, ctx) {
5890
5915
  const impactScore = (m) => (m.impact_score ?? 0) * 3;
5891
5916
  const activationBoost = (m) => activatedSkills.has(m.id) ? 5 : 0;
5892
5917
  const lexScore = (m) => 12 * (lexNorm.get(m.id) ?? 0);
5893
- 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);
5894
- 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);
5918
+ const sa = priorityRank(classifyMemoryPriority2(a, byId.get(a.id), input.files, input.symbols)) * 100 + reasonScore(a) + confidenceScore(a) + impactScore(a) + activationBoost(a) + lexScore(a) + (a.semantic_score ?? 0);
5919
+ const sb = priorityRank(classifyMemoryPriority2(b, byId.get(b.id), input.files, input.symbols)) * 100 + reasonScore(b) + confidenceScore(b) + impactScore(b) + activationBoost(b) + lexScore(b) + (b.semantic_score ?? 0);
5895
5920
  return sb - sa;
5896
5921
  });
5897
5922
  for (const mem of ranked.slice(0, briefingMaxMemories)) {
@@ -6047,7 +6072,7 @@ ${m.content}`).join("\n\n---\n\n"),
6047
6072
  const formattedMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : input.format === "actions" ? trimmedMemories.map((m) => ({ ...m, body: extractActionsBriefBody2(m.body) })) : trimmedMemories;
6048
6073
  const outputMemories = formattedMemories.map((m) => ({
6049
6074
  ...m,
6050
- priority: classifyMemoryPriority(m, byId.get(m.id), input.files, input.symbols),
6075
+ priority: classifyMemoryPriority2(m, byId.get(m.id), input.files, input.symbols),
6051
6076
  why: explainWhySurfaced(m, byId.get(m.id), input.files, inferred)
6052
6077
  }));
6053
6078
  const briefingQuality = classifyBriefingQuality(outputMemories, {
@@ -6584,6 +6609,27 @@ function tokenizeDiffForLiteral(diff) {
6584
6609
  const wordTokens = source.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 4 && !CODE_STOPWORDS.has(t));
6585
6610
  return [.../* @__PURE__ */ new Set([...wsTokens, ...wordTokens])];
6586
6611
  }
6612
+ function stripAiDirHunks(diff) {
6613
+ if (!diff.includes("diff --git")) return diff;
6614
+ const out = [];
6615
+ let block = [];
6616
+ let keep = true;
6617
+ const flush = () => {
6618
+ if (keep) out.push(...block);
6619
+ block = [];
6620
+ keep = true;
6621
+ };
6622
+ for (const line of diff.split("\n")) {
6623
+ if (line.startsWith("diff --git ")) {
6624
+ flush();
6625
+ const target = line.match(/ b\/(.+)$/)?.[1] ?? "";
6626
+ keep = !target.startsWith(".ai/");
6627
+ }
6628
+ block.push(line);
6629
+ }
6630
+ flush();
6631
+ return out.join("\n");
6632
+ }
6587
6633
  async function antiPatternsCheck(input, ctx) {
6588
6634
  if (!input.diff && input.paths.length === 0) {
6589
6635
  return {
@@ -6639,10 +6685,11 @@ async function antiPatternsCheck(input, ctx) {
6639
6685
  }
6640
6686
  }
6641
6687
  }
6642
- if (input.diff) {
6643
- const tokens = tokenizeDiffForLiteral(input.diff);
6644
- const added = addedLinesFromDiff(input.diff);
6645
- const addedText = added.trim().length > 0 ? added : input.diff;
6688
+ const scanDiff = input.diff ? stripAiDirHunks(input.diff) : input.diff;
6689
+ if (scanDiff) {
6690
+ const tokens = tokenizeDiffForLiteral(scanDiff);
6691
+ const added = addedLinesFromDiff(scanDiff);
6692
+ const addedText = added.trim().length > 0 ? added : scanDiff;
6646
6693
  if (tokens.length > 0) {
6647
6694
  for (const { memory: memory2 } of negative) {
6648
6695
  if (literalMatchesAnyToken3(memory2, tokens)) {
@@ -6672,10 +6719,10 @@ async function antiPatternsCheck(input, ctx) {
6672
6719
  }
6673
6720
  }
6674
6721
  }
6675
- if (input.semantic && input.diff) {
6722
+ if (input.semantic && scanDiff) {
6676
6723
  try {
6677
6724
  const mod = await import("@hiveai/embeddings");
6678
- const result = await mod.semanticSearch(ctx.paths, input.diff, { limit: input.limit * 2 });
6725
+ const result = await mod.semanticSearch(ctx.paths, scanDiff, { limit: input.limit * 2 });
6679
6726
  if (result) {
6680
6727
  const negativeIds = new Set(negative.map(({ memory: memory2 }) => memory2.frontmatter.id));
6681
6728
  for (const hit of result.hits) {
@@ -7962,7 +8009,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
7962
8009
  };
7963
8010
  }
7964
8011
  var SERVER_NAME = "haive";
7965
- var SERVER_VERSION = "0.15.0";
8012
+ var SERVER_VERSION = "0.17.0";
7966
8013
  function jsonResult(data) {
7967
8014
  return {
7968
8015
  content: [
@@ -9005,7 +9052,7 @@ import {
9005
9052
  getUsage as getUsage11,
9006
9053
  isAutoPromoteEligible as isAutoPromoteEligible2,
9007
9054
  isDecaying as isDecaying2,
9008
- isStackPackSeed as isStackPackSeed3,
9055
+ isStackPackSeed,
9009
9056
  loadCodeMap as loadCodeMap6,
9010
9057
  loadConfig as loadConfig4,
9011
9058
  loadMemoriesFromDir as loadMemoriesFromDir25,
@@ -9470,7 +9517,7 @@ async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
9470
9517
  const top = all.filter(({ memory: memory2 }) => {
9471
9518
  const s = memory2.frontmatter.status;
9472
9519
  if (memory2.frontmatter.type === "session_recap") return false;
9473
- if (isStackPackSeed3(memory2.frontmatter)) return false;
9520
+ if (isStackPackSeed(memory2.frontmatter)) return false;
9474
9521
  return s === "validated" || s === "proposed";
9475
9522
  }).sort((a, b) => {
9476
9523
  const score = (m) => {
@@ -13317,7 +13364,7 @@ import {
13317
13364
  codeMapPath as codeMapPath2,
13318
13365
  findProjectRoot as findProjectRoot45,
13319
13366
  getUsage as getUsage23,
13320
- isStackPackSeed as isStackPackSeed4,
13367
+ isStackPackSeed as isStackPackSeed2,
13321
13368
  loadCodeMap as loadCodeMap7,
13322
13369
  loadConfig as loadConfig11,
13323
13370
  loadMemoriesFromDir as loadMemoriesFromDir35,
@@ -13429,11 +13476,11 @@ function registerDoctor(program2) {
13429
13476
  fix: "haive memory approve <id> # activate\nhaive memory delete <id> # or delete if obsolete"
13430
13477
  });
13431
13478
  }
13432
- const policyMemories = memories.filter((m) => !isStackPackSeed4(m.memory.frontmatter));
13479
+ const policyMemories = memories.filter((m) => !isStackPackSeed2(m.memory.frontmatter));
13433
13480
  const anchorless = policyMemories.filter(
13434
13481
  (m) => m.memory.frontmatter.anchor.paths.length === 0 && m.memory.frontmatter.anchor.symbols.length === 0 && m.memory.frontmatter.type !== "session_recap" && m.memory.frontmatter.type !== "glossary" && m.memory.frontmatter.type !== "skill"
13435
13482
  );
13436
- const stackSeeds = memories.filter((m) => isStackPackSeed4(m.memory.frontmatter));
13483
+ const stackSeeds = memories.filter((m) => isStackPackSeed2(m.memory.frontmatter));
13437
13484
  if (anchorless.length / Math.max(policyMemories.length, 1) > 0.3) {
13438
13485
  findings.push({
13439
13486
  severity: "warn",
@@ -13563,7 +13610,7 @@ function registerDoctor(program2) {
13563
13610
  fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
13564
13611
  });
13565
13612
  }
13566
- findings.push(...await collectInstallFindings(root, "0.15.0"));
13613
+ findings.push(...await collectInstallFindings(root, "0.17.0"));
13567
13614
  findings.push(...await collectToolchainFindings(root));
13568
13615
  try {
13569
13616
  const legacyRaw = execSync3("haive-mcp --version", {
@@ -13571,7 +13618,7 @@ function registerDoctor(program2) {
13571
13618
  timeout: 3e3,
13572
13619
  stdio: ["ignore", "pipe", "ignore"]
13573
13620
  }).trim();
13574
- const cliVersion = "0.15.0";
13621
+ const cliVersion = "0.17.0";
13575
13622
  if (legacyRaw && legacyRaw !== cliVersion) {
13576
13623
  findings.push({
13577
13624
  severity: "warn",
@@ -14913,7 +14960,7 @@ async function buildFinishReport(dir) {
14913
14960
  severity: "error",
14914
14961
  code: shippableDirty.length > 0 ? "git-sync-uncommitted-shippable" : "git-sync-uncommitted-changes",
14915
14962
  message: shippableDirty.length > 0 ? `${shippableDirty.length} shippable file(s) are modified but not committed.` : `${status.dirtyFiles.length} file(s) are modified but not committed.`,
14916
- fix: shippableDirty.length > 0 ? "Bump the lockstep package version if needed, then `git add`, `git commit`, `git tag vX.Y.Z`, `git push && git push --tags`." : "Commit and push these changes before reporting the task done.",
14963
+ fix: shippableDirty.length > 0 ? "Bump the lockstep package version if needed, then `git add`, `git commit`, `git tag vX.Y.Z`, `git push && git push origin vX.Y.Z` (not `--tags`)." : "Commit and push these changes before reporting the task done.",
14917
14964
  reason: "The multi-agent git-sync decision requires agents to leave completed work committed and pushed, not as a local diff.",
14918
14965
  affected_files: status.dirtyFiles.slice(0, 12),
14919
14966
  impact: 100
@@ -15038,7 +15085,7 @@ async function buildFinishReport(dir) {
15038
15085
  severity: "error",
15039
15086
  code: "release-tag-unpushed",
15040
15087
  message: `Tag ${tag} is not present on the remote.`,
15041
- fix: "Run `git push --tags`.",
15088
+ fix: `Run \`git push origin ${tag}\` (avoid \`git push --tags\` \u2014 it fails on pre-existing divergent tags).`,
15042
15089
  impact: 50
15043
15090
  });
15044
15091
  } else if (remoteTag === true) {
@@ -15052,7 +15099,7 @@ async function buildFinishReport(dir) {
15052
15099
  severity: "warn",
15053
15100
  code: "release-tag-remote-unverified",
15054
15101
  message: `Could not verify whether tag ${tag} exists on the remote.`,
15055
- fix: "Run `git push --tags` if you have not already.",
15102
+ fix: `Run \`git push origin ${tag}\` if you have not already (avoid \`git push --tags\`).`,
15056
15103
  impact: 10
15057
15104
  });
15058
15105
  }
@@ -15244,7 +15291,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
15244
15291
  findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
15245
15292
  });
15246
15293
  }
15247
- findings.push(...await inspectIntegrationVersions(root, "0.15.0"));
15294
+ findings.push(...await inspectIntegrationVersions(root, "0.17.0"));
15248
15295
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
15249
15296
  const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
15250
15297
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
@@ -15404,7 +15451,7 @@ async function verifyDecisionCoverage(paths, stage, sessionId) {
15404
15451
  severity: stage === "local" ? "warn" : "error",
15405
15452
  code: "decision-coverage-missing",
15406
15453
  message: `${missing.length}/${relevant.length} relevant anchored decisions/policies were not present in the latest briefing: ${missing.slice(0, 6).map((m) => m.frontmatter.id).join(", ")}`,
15407
- fix: `Run \`haive briefing --files "${changedFiles.slice(0, 10).join(",")}" --task "..."\` before committing.`,
15454
+ fix: `Run \`haive briefing --files "${changedFiles.slice(0, 12).join(",")}" --max-memories 60 --task "..."\` before committing (briefings now accumulate, so several smaller briefings also work).`,
15408
15455
  reason: "Changed files overlap validated anchored policy memories that were not recorded in the latest briefing marker.",
15409
15456
  affected_files: changedFiles.slice(0, 10),
15410
15457
  memory_ids: missing.slice(0, 10).map((m) => m.frontmatter.id),
@@ -17138,7 +17185,7 @@ async function readCommits(root, days) {
17138
17185
 
17139
17186
  // src/index.ts
17140
17187
  var program = new Command63();
17141
- program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.15.0").option("--advanced", "show maintenance and experimental commands in help");
17188
+ program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.17.0").option("--advanced", "show maintenance and experimental commands in help");
17142
17189
  registerInit(program);
17143
17190
  registerWelcome(program);
17144
17191
  registerResolveProject(program);