@hiveai/mcp 0.21.0 → 0.24.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/server.d.ts CHANGED
@@ -670,6 +670,13 @@ declare function memTimeline(input: MemTimelineInput, ctx: HaiveContext): Promis
670
670
  notice: string | undefined;
671
671
  }>;
672
672
 
673
+ interface SuggestedResolution {
674
+ keep_id: string;
675
+ supersede_id: string;
676
+ reason: string;
677
+ /** Copy-paste command that APPLIES the guided supersede (promotes winner, deprecates loser). */
678
+ command: string;
679
+ }
673
680
  declare const MemConflictCandidatesInputSchema: {
674
681
  since_days: z.ZodDefault<z.ZodNumber>;
675
682
  types: z.ZodDefault<z.ZodArray<z.ZodEnum<["decision", "architecture", "convention", "gotcha"]>, "many">>;
@@ -682,8 +689,20 @@ type MemConflictCandidatesInput = {
682
689
  [K in keyof typeof MemConflictCandidatesInputSchema]: z.infer<(typeof MemConflictCandidatesInputSchema)[K]>;
683
690
  };
684
691
  declare function memConflictCandidates(input: MemConflictCandidatesInput, ctx: HaiveContext): Promise<{
685
- pairs: _hiveai_core.ConflictCandidatePair[];
686
- topic_status_pairs: _hiveai_core.TopicStatusPair[];
692
+ pairs: {
693
+ suggested_resolution: SuggestedResolution | null;
694
+ id_a: string;
695
+ id_b: string;
696
+ jaccard: number;
697
+ }[];
698
+ topic_status_pairs: {
699
+ suggested_resolution: SuggestedResolution | null;
700
+ id_a: string;
701
+ id_b: string;
702
+ topic: string;
703
+ status_a: string;
704
+ status_b: string;
705
+ }[];
687
706
  scanned: number;
688
707
  truncated: boolean;
689
708
  notice: string | undefined;
package/dist/server.js CHANGED
@@ -1149,7 +1149,15 @@ async function memTried(input, ctx) {
1149
1149
  throw new Error(`Memory already exists at ${file}`);
1150
1150
  }
1151
1151
  await writeFile8(file, serializeMemory7({ frontmatter, body }), "utf8");
1152
- return { id: frontmatter.id, scope: frontmatter.scope, file_path: file };
1152
+ const sensorGenerated = Boolean(sensor);
1153
+ const hint = sensorGenerated ? void 0 : input.paths.length === 0 ? "No sensor was generated (no `paths` given), so this lesson is feedforward-only \u2014 it will be briefed but the gate cannot block the repeat. Re-run with `paths` set to the file(s) where the mistake lives to close the loop." : "No sensor could be derived from the wording (no distinctive code token). The lesson is briefed but not enforced; add a concrete forbidden token/value, or attach a sensor manually, to make the gate block the repeat.";
1154
+ return {
1155
+ id: frontmatter.id,
1156
+ scope: frontmatter.scope,
1157
+ file_path: file,
1158
+ sensor_generated: sensorGenerated,
1159
+ ...hint ? { hint } : {}
1160
+ };
1153
1161
  }
1154
1162
 
1155
1163
  // src/tools/ingest-findings.ts
@@ -1577,6 +1585,7 @@ async function memSessionEnd(input, ctx) {
1577
1585
  // src/tools/get-briefing.ts
1578
1586
  import { readFile as readFile5, writeFile as writeFile13 } from "fs/promises";
1579
1587
  import { existsSync as existsSync21 } from "fs";
1588
+ import path11 from "path";
1580
1589
  import {
1581
1590
  allocateBudget,
1582
1591
  briefingProofLine,
@@ -2020,6 +2029,7 @@ async function getBriefing(input, ctx) {
2020
2029
  const topSymbols = Object.entries(codeMap.files).flatMap(
2021
2030
  ([fp, entry]) => entry.exports.slice(0, 3).map((e) => `${e.name} (${fp.split("/").slice(-2).join("/")})`)
2022
2031
  ).slice(0, 15).join(", ");
2032
+ const commands = await detectRunCommands(ctx.paths.root);
2023
2033
  projectContext = `# Project context (auto-generated by hAIve)
2024
2034
 
2025
2035
  > \u26A0 This is a minimal auto-generated context based on the code-map. Invoke the \`bootstrap_project\` MCP prompt to replace it with a full analysis.
@@ -2029,7 +2039,10 @@ async function getBriefing(input, ctx) {
2029
2039
  - **Main file types:** ${topExts}
2030
2040
  - **Generated at:** ${codeMap.generated_at}
2031
2041
 
2032
- ## Key exports (sample)
2042
+ ` + (commands ? `## Commands
2043
+ ${commands}
2044
+
2045
+ ` : "") + `## Key exports (sample)
2033
2046
  ` + topSymbols + "\n";
2034
2047
  autoContextGenerated = true;
2035
2048
  setupWarnings.push(
@@ -2309,6 +2322,19 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
2309
2322
  }
2310
2323
  };
2311
2324
  }
2325
+ async function detectRunCommands(root) {
2326
+ const pkgPath = path11.join(root, "package.json");
2327
+ if (!existsSync21(pkgPath)) return null;
2328
+ try {
2329
+ const pkg = JSON.parse(await readFile5(pkgPath, "utf8"));
2330
+ const scripts = pkg.scripts ?? {};
2331
+ const order = ["test", "build", "lint", "typecheck", "type-check", "dev", "start"];
2332
+ const lines = order.filter((name) => typeof scripts[name] === "string" && scripts[name].trim() !== "").map((name) => `- \`${name}\`: \`${scripts[name]}\``);
2333
+ return lines.length > 0 ? lines.join("\n") : null;
2334
+ } catch {
2335
+ return null;
2336
+ }
2337
+ }
2312
2338
 
2313
2339
  // src/tools/code-map.ts
2314
2340
  import { estimateTokens as estimateTokens2, loadCodeMap as loadCodeMap2, queryCodeMap as queryCodeMap2 } from "@hiveai/core";
@@ -2548,7 +2574,7 @@ async function codeSearch(input, ctx) {
2548
2574
  // src/tools/why-this-file.ts
2549
2575
  import { existsSync as existsSync24 } from "fs";
2550
2576
  import { spawn } from "child_process";
2551
- import path11 from "path";
2577
+ import path12 from "path";
2552
2578
  import {
2553
2579
  deriveConfidence as deriveConfidence5,
2554
2580
  getUsage as getUsage7,
@@ -2566,7 +2592,7 @@ var WhyThisFileInputSchema = {
2566
2592
  memory_limit: z25.number().int().positive().max(20).default(5).describe("Cap on memories anchored to this path.")
2567
2593
  };
2568
2594
  async function whyThisFile(input, ctx) {
2569
- const fileExists = existsSync24(path11.join(ctx.paths.root, input.path));
2595
+ const fileExists = existsSync24(path12.join(ctx.paths.root, input.path));
2570
2596
  const [commits, memories, codeMap] = await Promise.all([
2571
2597
  runGitLog(ctx.paths.root, input.path, input.git_log_limit).catch(() => []),
2572
2598
  collectAnchoredMemories(ctx, input.path, input.memory_limit),
@@ -2665,7 +2691,6 @@ function runCommand(cmd, args, cwd) {
2665
2691
  import { existsSync as existsSync25 } from "fs";
2666
2692
  import {
2667
2693
  addedLinesFromDiff,
2668
- appendPreventionEvent,
2669
2694
  BRIDGE_TARGET_PATH,
2670
2695
  buildDocFrequency,
2671
2696
  CODE_STOPWORDS,
@@ -2677,9 +2702,8 @@ import {
2677
2702
  loadUsageIndex as loadUsageIndex10,
2678
2703
  literalMatchesAnyToken as literalMatchesAnyToken3,
2679
2704
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths4,
2680
- recordPrevention,
2705
+ recordPreventionHits,
2681
2706
  runSensors,
2682
- saveUsageIndex as saveUsageIndex4,
2683
2707
  sensorTargetsFromDiff,
2684
2708
  tokenizeQuery as tokenizeQuery3
2685
2709
  } from "@hiveai/core";
@@ -2864,19 +2888,7 @@ async function antiPatternsCheck(input, ctx) {
2864
2888
  const strongCatches = warnings.filter(
2865
2889
  (w) => w.reasons.includes("sensor") || w.reasons.includes("anchor") && w.reasons.includes("literal")
2866
2890
  );
2867
- if (strongCatches.length > 0) {
2868
- const recordedIds = [];
2869
- for (const w of strongCatches) if (recordPrevention(usage, w.id)) recordedIds.push(w.id);
2870
- if (recordedIds.length > 0) {
2871
- await saveUsageIndex4(ctx.paths, usage).catch(() => {
2872
- });
2873
- const at = (/* @__PURE__ */ new Date()).toISOString();
2874
- for (const id of recordedIds) {
2875
- await appendPreventionEvent(ctx.paths, { at, id, source: "anti-pattern" }).catch(() => {
2876
- });
2877
- }
2878
- }
2879
- }
2891
+ await recordPreventionHits(ctx.paths, strongCatches.map((w) => w.id), "anti-pattern");
2880
2892
  return {
2881
2893
  scanned: negative.length,
2882
2894
  warnings
@@ -3633,7 +3645,7 @@ function repairTargetPathForWarning(warning, paths) {
3633
3645
  // src/tools/pattern-detect.ts
3634
3646
  import { mkdir as mkdir8, writeFile as writeFile14 } from "fs/promises";
3635
3647
  import { existsSync as existsSync29 } from "fs";
3636
- import path12 from "path";
3648
+ import path13 from "path";
3637
3649
  import { execSync as execSync2 } from "child_process";
3638
3650
  import {
3639
3651
  buildFrontmatter as buildFrontmatter5,
@@ -3682,13 +3694,13 @@ async function patternDetect(input, ctx) {
3682
3694
  try {
3683
3695
  const changedFiles = gitChangedFiles(ctx.paths.root, input.since_days);
3684
3696
  const configFiles = changedFiles.filter(
3685
- (f) => CONFIG_PATTERNS.some((p) => path12.basename(f.toLowerCase()).includes(p))
3697
+ (f) => CONFIG_PATTERNS.some((p) => path13.basename(f.toLowerCase()).includes(p))
3686
3698
  );
3687
3699
  for (const file of configFiles.slice(0, 5)) {
3688
3700
  const diff = gitFileDiff(ctx.paths.root, file, input.since_days);
3689
3701
  if (!diff) continue;
3690
- const parentDir = path12.basename(path12.dirname(file));
3691
- const baseName = path12.basename(file).replace(/\.[^.]+$/, "");
3702
+ const parentDir = path13.basename(path13.dirname(file));
3703
+ const baseName = path13.basename(file).replace(/\.[^.]+$/, "");
3692
3704
  const slug = `${parentDir}-${baseName}`.replace(/[^a-z0-9]/gi, "-").toLowerCase().slice(0, 40);
3693
3705
  matches.push({
3694
3706
  kind: "config_change",
@@ -3752,7 +3764,7 @@ async function patternDetect(input, ctx) {
3752
3764
  for (const [p, { count, tools }] of pathCounts) {
3753
3765
  if (count < HOT_FILE_MIN) continue;
3754
3766
  if (tools.has("mem_tried") || tools.has("mem_observe")) continue;
3755
- if (CONFIG_PATTERNS.some((cp) => path12.basename(p).includes(cp))) continue;
3767
+ if (CONFIG_PATTERNS.some((cp) => path13.basename(p).includes(cp))) continue;
3756
3768
  const slug = p.replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-").slice(0, 40);
3757
3769
  matches.push({
3758
3770
  kind: "hot_file",
@@ -3799,7 +3811,7 @@ async function patternDetect(input, ctx) {
3799
3811
  void 0
3800
3812
  );
3801
3813
  if (existsSync29(file)) continue;
3802
- await mkdir8(path12.dirname(file), { recursive: true });
3814
+ await mkdir8(path13.dirname(file), { recursive: true });
3803
3815
  await writeFile14(
3804
3816
  file,
3805
3817
  serializeMemory12({ frontmatter: fm, body: match.proposed_body }),
@@ -3848,9 +3860,22 @@ import { existsSync as existsSync30 } from "fs";
3848
3860
  import {
3849
3861
  findLexicalConflictPairs,
3850
3862
  findTopicStatusConflictPairs,
3851
- loadMemoriesFromDir as loadMemoriesFromDir23
3863
+ loadMemoriesFromDir as loadMemoriesFromDir23,
3864
+ planConflictResolution
3852
3865
  } from "@hiveai/core";
3853
3866
  import { z as z32 } from "zod";
3867
+ function suggestResolution(byId, idA, idB) {
3868
+ const a = byId.get(idA);
3869
+ const b = byId.get(idB);
3870
+ if (!a || !b) return null;
3871
+ const plan = planConflictResolution(a, b);
3872
+ return {
3873
+ keep_id: plan.keep_id,
3874
+ supersede_id: plan.supersede_id,
3875
+ reason: plan.reason,
3876
+ command: `haive memory resolve-conflict ${plan.keep_id} ${plan.supersede_id} --yes`
3877
+ };
3878
+ }
3854
3879
  var MemConflictCandidatesInputSchema = {
3855
3880
  since_days: z32.number().int().positive().max(3650).default(365).describe("Only memories created since N days ago"),
3856
3881
  types: z32.array(z32.enum(["decision", "architecture", "convention", "gotcha"])).default(["decision", "architecture"]).describe("Memory types scanned for pairwise lexical overlap"),
@@ -3872,6 +3897,7 @@ async function memConflictCandidates(input, ctx) {
3872
3897
  };
3873
3898
  }
3874
3899
  const all = await loadMemoriesFromDir23(ctx.paths.memoriesDir);
3900
+ const byId = new Map(all.map((m) => [m.memory.frontmatter.id, m]));
3875
3901
  const { pairs, scanned, truncated } = findLexicalConflictPairs(all, {
3876
3902
  sinceDays: input.since_days,
3877
3903
  types: input.types,
@@ -3880,8 +3906,22 @@ async function memConflictCandidates(input, ctx) {
3880
3906
  maxScan: input.max_scan
3881
3907
  });
3882
3908
  const topicStatusPairs = findTopicStatusConflictPairs(all, input.max_topic_pairs);
3909
+ const enrichedPairs = pairs.map((p) => ({
3910
+ ...p,
3911
+ suggested_resolution: suggestResolution(byId, p.id_a, p.id_b)
3912
+ }));
3913
+ const enrichedTopicStatusPairs = topicStatusPairs.map((p) => ({
3914
+ ...p,
3915
+ suggested_resolution: suggestResolution(byId, p.id_a, p.id_b)
3916
+ }));
3883
3917
  const notice = pairs.length === 0 && topicStatusPairs.length === 0 ? "No lexical or topic-status candidates \u2014 widen since_days/types or lower min_jaccard." : void 0;
3884
- return { pairs, topic_status_pairs: topicStatusPairs, scanned, truncated, notice };
3918
+ return {
3919
+ pairs: enrichedPairs,
3920
+ topic_status_pairs: enrichedTopicStatusPairs,
3921
+ scanned,
3922
+ truncated,
3923
+ notice
3924
+ };
3885
3925
  }
3886
3926
 
3887
3927
  // src/tools/mem-resolve-project.ts
@@ -4228,7 +4268,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
4228
4268
  // src/server.ts
4229
4269
  import { hasRecentBriefingMarker, loadConfigSync } from "@hiveai/core";
4230
4270
  var SERVER_NAME = "haive";
4231
- var SERVER_VERSION = "0.21.0";
4271
+ var SERVER_VERSION = "0.24.0";
4232
4272
  function jsonResult(data) {
4233
4273
  return {
4234
4274
  content: [