@hiveai/cli 0.10.5 → 0.10.8
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 +355 -358
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -3746,9 +3746,8 @@ import { mkdir as mkdir52, writeFile as writeFile92, rm } from "fs/promises";
|
|
|
3746
3746
|
import { existsSync as existsSync16 } from "fs";
|
|
3747
3747
|
import path72 from "path";
|
|
3748
3748
|
import { execSync } from "child_process";
|
|
3749
|
-
import { readFile as
|
|
3750
|
-
import { existsSync as
|
|
3751
|
-
import path92 from "path";
|
|
3749
|
+
import { readFile as readFile42, writeFile as writeFile11 } from "fs/promises";
|
|
3750
|
+
import { existsSync as existsSync19 } from "fs";
|
|
3752
3751
|
import {
|
|
3753
3752
|
allocateBudget,
|
|
3754
3753
|
DEFAULT_AUTO_PROMOTE_RULE,
|
|
@@ -3757,11 +3756,9 @@ import {
|
|
|
3757
3756
|
extractActionsBriefBody as extractActionsBriefBody2,
|
|
3758
3757
|
getUsage as getUsage5,
|
|
3759
3758
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
3760
|
-
isGlobPath,
|
|
3761
|
-
isRetiredMemory,
|
|
3762
3759
|
isAutoPromoteEligible,
|
|
3763
3760
|
isDecaying,
|
|
3764
|
-
|
|
3761
|
+
isRetiredMemory,
|
|
3765
3762
|
literalMatchesAllTokens as literalMatchesAllTokens22,
|
|
3766
3763
|
literalMatchesAnyToken as literalMatchesAnyToken22,
|
|
3767
3764
|
loadCodeMap as loadCodeMap5,
|
|
@@ -3769,7 +3766,6 @@ import {
|
|
|
3769
3766
|
loadMemoriesFromDir as loadMemoriesFromDir13,
|
|
3770
3767
|
loadUsageIndex as loadUsageIndex7,
|
|
3771
3768
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths22,
|
|
3772
|
-
pathsOverlap,
|
|
3773
3769
|
queryCodeMap as queryCodeMap2,
|
|
3774
3770
|
resolveBriefingBudget as resolveBriefingBudget2,
|
|
3775
3771
|
serializeMemory as serializeMemory9,
|
|
@@ -3781,17 +3777,21 @@ import {
|
|
|
3781
3777
|
writeBriefingMarker as writeBriefingMarker2
|
|
3782
3778
|
} from "@hiveai/core";
|
|
3783
3779
|
import { z as z17 } from "zod";
|
|
3780
|
+
import { readdir as readdir3, readFile as readFile32 } from "fs/promises";
|
|
3781
|
+
import { existsSync as existsSync18 } from "fs";
|
|
3782
|
+
import path92 from "path";
|
|
3783
|
+
import { isGlobPath, isStackPackSeed as isStackPackSeed2, pathsOverlap } from "@hiveai/core";
|
|
3784
3784
|
import { estimateTokens as estimateTokens2, loadCodeMap as loadCodeMap22, queryCodeMap as queryCodeMap22 } from "@hiveai/core";
|
|
3785
3785
|
import { z as z18 } from "zod";
|
|
3786
|
-
import { existsSync as
|
|
3786
|
+
import { existsSync as existsSync20 } from "fs";
|
|
3787
3787
|
import { loadMemoriesFromDir as loadMemoriesFromDir14 } from "@hiveai/core";
|
|
3788
3788
|
import { z as z19 } from "zod";
|
|
3789
|
-
import { existsSync as
|
|
3789
|
+
import { existsSync as existsSync21 } from "fs";
|
|
3790
3790
|
import { loadMemoriesFromDir as loadMemoriesFromDir15 } from "@hiveai/core";
|
|
3791
3791
|
import { z as z20 } from "zod";
|
|
3792
3792
|
import { z as z21 } from "zod";
|
|
3793
3793
|
import { z as z22 } from "zod";
|
|
3794
|
-
import { existsSync as
|
|
3794
|
+
import { existsSync as existsSync222 } from "fs";
|
|
3795
3795
|
import { spawn } from "child_process";
|
|
3796
3796
|
import path102 from "path";
|
|
3797
3797
|
import {
|
|
@@ -3803,7 +3803,7 @@ import {
|
|
|
3803
3803
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths3
|
|
3804
3804
|
} from "@hiveai/core";
|
|
3805
3805
|
import { z as z23 } from "zod";
|
|
3806
|
-
import { existsSync as
|
|
3806
|
+
import { existsSync as existsSync23 } from "fs";
|
|
3807
3807
|
import {
|
|
3808
3808
|
addedLinesFromDiff,
|
|
3809
3809
|
deriveConfidence as deriveConfidence6,
|
|
@@ -3818,13 +3818,13 @@ import {
|
|
|
3818
3818
|
tokenizeQuery as tokenizeQuery3
|
|
3819
3819
|
} from "@hiveai/core";
|
|
3820
3820
|
import { z as z24 } from "zod";
|
|
3821
|
-
import { existsSync as
|
|
3821
|
+
import { existsSync as existsSync24 } from "fs";
|
|
3822
3822
|
import {
|
|
3823
3823
|
loadMemoriesFromDir as loadMemoriesFromDir18,
|
|
3824
3824
|
tokenizeQuery as tokenizeQuery4
|
|
3825
3825
|
} from "@hiveai/core";
|
|
3826
3826
|
import { z as z25 } from "zod";
|
|
3827
|
-
import { existsSync as
|
|
3827
|
+
import { existsSync as existsSync25 } from "fs";
|
|
3828
3828
|
import { spawn as spawn2 } from "child_process";
|
|
3829
3829
|
import {
|
|
3830
3830
|
deriveConfidence as deriveConfidence7,
|
|
@@ -3834,7 +3834,7 @@ import {
|
|
|
3834
3834
|
pathsOverlap as singlePathsOverlap
|
|
3835
3835
|
} from "@hiveai/core";
|
|
3836
3836
|
import { z as z26 } from "zod";
|
|
3837
|
-
import { existsSync as
|
|
3837
|
+
import { existsSync as existsSync26 } from "fs";
|
|
3838
3838
|
import {
|
|
3839
3839
|
deriveConfidence as deriveConfidence8,
|
|
3840
3840
|
getUsage as getUsage9,
|
|
@@ -3846,7 +3846,7 @@ import {
|
|
|
3846
3846
|
import { z as z27 } from "zod";
|
|
3847
3847
|
import { z as z28 } from "zod";
|
|
3848
3848
|
import { mkdir as mkdir72, writeFile as writeFile12 } from "fs/promises";
|
|
3849
|
-
import { existsSync as
|
|
3849
|
+
import { existsSync as existsSync27 } from "fs";
|
|
3850
3850
|
import path112 from "path";
|
|
3851
3851
|
import { execSync as execSync2 } from "child_process";
|
|
3852
3852
|
import {
|
|
@@ -3856,7 +3856,7 @@ import {
|
|
|
3856
3856
|
serializeMemory as serializeMemory10
|
|
3857
3857
|
} from "@hiveai/core";
|
|
3858
3858
|
import { z as z29 } from "zod";
|
|
3859
|
-
import { existsSync as
|
|
3859
|
+
import { existsSync as existsSync28 } from "fs";
|
|
3860
3860
|
import {
|
|
3861
3861
|
findLexicalConflictPairs,
|
|
3862
3862
|
findTopicStatusConflictPairs,
|
|
@@ -3867,7 +3867,7 @@ import { resolveProjectInfo } from "@hiveai/core";
|
|
|
3867
3867
|
import { z as z31 } from "zod";
|
|
3868
3868
|
import { MemoryTypeSchema, suggestTopicKey } from "@hiveai/core";
|
|
3869
3869
|
import { z as z32 } from "zod";
|
|
3870
|
-
import { existsSync as
|
|
3870
|
+
import { existsSync as existsSync29 } from "fs";
|
|
3871
3871
|
import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir222 } from "@hiveai/core";
|
|
3872
3872
|
import { z as z33 } from "zod";
|
|
3873
3873
|
import { appendRuntimeJournalEntry as appendRuntimeJournalEntry2 } from "@hiveai/core";
|
|
@@ -3877,7 +3877,7 @@ import { z as z35 } from "zod";
|
|
|
3877
3877
|
import { z as z36 } from "zod";
|
|
3878
3878
|
import { z as z37 } from "zod";
|
|
3879
3879
|
import { z as z38 } from "zod";
|
|
3880
|
-
import { loadConfigSync } from "@hiveai/core";
|
|
3880
|
+
import { hasRecentBriefingMarker, loadConfigSync } from "@hiveai/core";
|
|
3881
3881
|
function createContext(options = {}) {
|
|
3882
3882
|
const env = options.env ?? process.env;
|
|
3883
3883
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -5108,6 +5108,144 @@ async function memSessionEnd(input, ctx) {
|
|
|
5108
5108
|
revision_count: 0
|
|
5109
5109
|
};
|
|
5110
5110
|
}
|
|
5111
|
+
function compactSummary(body) {
|
|
5112
|
+
for (const line of body.split("\n")) {
|
|
5113
|
+
const trimmed = line.replace(/^#+\s*/, "").trim();
|
|
5114
|
+
if (trimmed.length > 0) return trimmed.slice(0, 120);
|
|
5115
|
+
}
|
|
5116
|
+
return body.slice(0, 120);
|
|
5117
|
+
}
|
|
5118
|
+
function classifyMemoryPriority(memory2, loaded, inputFiles, inputSymbols) {
|
|
5119
|
+
const fm = loaded?.memory.frontmatter;
|
|
5120
|
+
const directAnchor = Boolean(
|
|
5121
|
+
fm && inputFiles.length > 0 && fm.anchor.paths.some((p) => inputFiles.some((file) => pathsOverlap(p, file)))
|
|
5122
|
+
);
|
|
5123
|
+
const directSymbol = Boolean(
|
|
5124
|
+
fm && inputSymbols.length > 0 && fm.anchor.symbols.some(
|
|
5125
|
+
(sym) => inputSymbols.some((wanted) => wanted.toLowerCase() === sym.toLowerCase())
|
|
5126
|
+
)
|
|
5127
|
+
);
|
|
5128
|
+
const strongSemantic = (memory2.semantic_score ?? 0) >= 0.65;
|
|
5129
|
+
const usefulSemantic = (memory2.semantic_score ?? 0) >= 0.35;
|
|
5130
|
+
if (fm?.requires_human_approval || directAnchor || directSymbol || memory2.type === "attempt" && (memory2.match_quality === "exact" || strongSemantic) || memory2.type === "skill" && (memory2.match_quality === "exact" || strongSemantic)) {
|
|
5131
|
+
return "must_read";
|
|
5132
|
+
}
|
|
5133
|
+
if (isStackPackSeed2(fm)) {
|
|
5134
|
+
return "background";
|
|
5135
|
+
}
|
|
5136
|
+
if (memory2.type === "skill" || memory2.reasons.includes("module") || memory2.reasons.includes("domain") || memory2.match_quality === "exact" || usefulSemantic) {
|
|
5137
|
+
return "useful";
|
|
5138
|
+
}
|
|
5139
|
+
return "background";
|
|
5140
|
+
}
|
|
5141
|
+
function priorityRank(priority) {
|
|
5142
|
+
return priority === "must_read" ? 3 : priority === "useful" ? 2 : 1;
|
|
5143
|
+
}
|
|
5144
|
+
function classifyBriefingQuality(memories, context) {
|
|
5145
|
+
const mustRead = memories.filter((m) => m.priority === "must_read").length;
|
|
5146
|
+
const useful = memories.filter((m) => m.priority === "useful").length;
|
|
5147
|
+
const background = memories.filter((m) => m.priority === "background").length;
|
|
5148
|
+
const weakSemantic = memories.filter(
|
|
5149
|
+
(m) => m.reasons.length === 1 && m.reasons.includes("semantic") && (m.semantic_score ?? 0) > 0 && (m.semantic_score ?? 0) < 0.35
|
|
5150
|
+
).length;
|
|
5151
|
+
const reasons = [];
|
|
5152
|
+
if (memories.length === 0) reasons.push("no memories matched the task or files");
|
|
5153
|
+
if (context.isTemplateContext && !context.autoContextGenerated) reasons.push("project context is still a template");
|
|
5154
|
+
if (!context.hasLastSession) reasons.push("no previous session recap");
|
|
5155
|
+
if (mustRead > 0) reasons.push(`${mustRead} must_read memor${mustRead === 1 ? "y" : "ies"} matched directly`);
|
|
5156
|
+
if (useful > 0) reasons.push(`${useful} useful memor${useful === 1 ? "y" : "ies"} matched`);
|
|
5157
|
+
if (background > useful + mustRead && background > 2) reasons.push(`${background} background memories dominate the result`);
|
|
5158
|
+
if (weakSemantic > 0) reasons.push(`${weakSemantic} weak semantic-only match${weakSemantic === 1 ? "" : "es"}`);
|
|
5159
|
+
if (context.searchMode === "literal_fallback") reasons.push("semantic index unavailable or empty; literal fallback used");
|
|
5160
|
+
if (memories.length === 0 || mustRead === 0 && useful === 0) {
|
|
5161
|
+
return { level: "thin", reasons };
|
|
5162
|
+
}
|
|
5163
|
+
if (background > useful + mustRead && background > 2) {
|
|
5164
|
+
return { level: "noisy", reasons };
|
|
5165
|
+
}
|
|
5166
|
+
return { level: "strong", reasons };
|
|
5167
|
+
}
|
|
5168
|
+
function explainWhySurfaced(memory2, loaded, inputFiles, inferredModules) {
|
|
5169
|
+
const why = [];
|
|
5170
|
+
const fm = loaded?.memory.frontmatter;
|
|
5171
|
+
if (memory2.reasons.includes("anchor") && fm) {
|
|
5172
|
+
const matching = fm.anchor.paths.filter(
|
|
5173
|
+
(p) => inputFiles.length === 0 || inputFiles.some((file) => pathsOverlap(p, file))
|
|
5174
|
+
);
|
|
5175
|
+
if (matching.length > 0) {
|
|
5176
|
+
const exact = matching.filter(
|
|
5177
|
+
(p) => !isGlobPath(p) && inputFiles.some((file) => p === file || pathsOverlap(p, file))
|
|
5178
|
+
);
|
|
5179
|
+
const glob = matching.filter((p) => isGlobPath(p));
|
|
5180
|
+
if (exact.length > 0) {
|
|
5181
|
+
why.push(`Exact/file anchor match: ${exact.slice(0, 4).join(", ")}`);
|
|
5182
|
+
}
|
|
5183
|
+
if (glob.length > 0) {
|
|
5184
|
+
why.push(`Glob anchor match: ${glob.slice(0, 4).join(", ")}`);
|
|
5185
|
+
}
|
|
5186
|
+
if (exact.length === 0 && glob.length === 0) {
|
|
5187
|
+
why.push(`Anchored to touched path${matching.length === 1 ? "" : "s"}: ${matching.slice(0, 4).join(", ")}`);
|
|
5188
|
+
}
|
|
5189
|
+
} else if (fm.anchor.paths.length > 0) {
|
|
5190
|
+
why.push(`Pulled by related anchor: ${fm.anchor.paths.slice(0, 4).join(", ")}`);
|
|
5191
|
+
}
|
|
5192
|
+
if (fm.anchor.symbols.length > 0) {
|
|
5193
|
+
why.push(`Anchor symbol${fm.anchor.symbols.length === 1 ? "" : "s"}: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
|
|
5194
|
+
}
|
|
5195
|
+
}
|
|
5196
|
+
if (memory2.reasons.includes("symbol") && fm) {
|
|
5197
|
+
why.push(`Explicit symbol match: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
|
|
5198
|
+
}
|
|
5199
|
+
if (memory2.reasons.includes("module")) {
|
|
5200
|
+
const moduleHints = [
|
|
5201
|
+
...memory2.module ? [memory2.module] : [],
|
|
5202
|
+
...memory2.tags.filter((tag) => inferredModules.includes(tag))
|
|
5203
|
+
];
|
|
5204
|
+
const shown = moduleHints.length > 0 ? [...new Set(moduleHints)].join(", ") : inferredModules.join(", ");
|
|
5205
|
+
why.push(shown ? `Matched inferred module/tag: ${shown}` : "Matched inferred module context.");
|
|
5206
|
+
}
|
|
5207
|
+
if (memory2.reasons.includes("domain")) {
|
|
5208
|
+
why.push("Matched inferred domain from the target file paths.");
|
|
5209
|
+
}
|
|
5210
|
+
if (memory2.reasons.includes("semantic")) {
|
|
5211
|
+
const score = memory2.semantic_score !== void 0 ? ` score=${Math.round(memory2.semantic_score * 100) / 100}` : "";
|
|
5212
|
+
why.push(`${memory2.match_quality === "exact" ? "Literal task match" : "Semantic/task relevance"}${score}.`);
|
|
5213
|
+
}
|
|
5214
|
+
why.push(`Confidence: ${memory2.confidence}; read ${memory2.read_count} time${memory2.read_count === 1 ? "" : "s"}.`);
|
|
5215
|
+
if (memory2.type === "attempt") why.push("Failed-approach record; read before repeating the same path.");
|
|
5216
|
+
if (memory2.type === "skill") why.push("Skill (reusable procedure/playbook) \u2014 follow the steps described when doing this type of task.");
|
|
5217
|
+
if (memory2.status === "proposed" || memory2.status === "draft") {
|
|
5218
|
+
why.push("Unvalidated record; use cautiously or ask a human before treating it as policy.");
|
|
5219
|
+
}
|
|
5220
|
+
return why;
|
|
5221
|
+
}
|
|
5222
|
+
async function trySemanticHits(ctx, task, limit) {
|
|
5223
|
+
let mod;
|
|
5224
|
+
try {
|
|
5225
|
+
mod = await import("@hiveai/embeddings");
|
|
5226
|
+
} catch {
|
|
5227
|
+
return null;
|
|
5228
|
+
}
|
|
5229
|
+
const result = await mod.semanticSearch(ctx.paths, task, { limit });
|
|
5230
|
+
if (!result) return null;
|
|
5231
|
+
return result.hits.map((h) => ({ id: h.id, score: h.score }));
|
|
5232
|
+
}
|
|
5233
|
+
async function loadModuleContexts2(ctx, modules) {
|
|
5234
|
+
if (modules.length === 0) return [];
|
|
5235
|
+
if (!existsSync18(ctx.paths.modulesContextDir)) return [];
|
|
5236
|
+
const available = new Set(
|
|
5237
|
+
(await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
5238
|
+
);
|
|
5239
|
+
const out = [];
|
|
5240
|
+
for (const m of modules) {
|
|
5241
|
+
if (!available.has(m)) continue;
|
|
5242
|
+
const file = path92.join(ctx.paths.modulesContextDir, m, "context.md");
|
|
5243
|
+
if (existsSync18(file)) {
|
|
5244
|
+
out.push({ name: m, content: await readFile32(file, "utf8") });
|
|
5245
|
+
}
|
|
5246
|
+
}
|
|
5247
|
+
return out;
|
|
5248
|
+
}
|
|
5111
5249
|
var GetBriefingInputSchema = {
|
|
5112
5250
|
task: z17.string().optional().describe(
|
|
5113
5251
|
"What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
|
|
@@ -5153,7 +5291,7 @@ async function getBriefing(input, ctx) {
|
|
|
5153
5291
|
let usage = { version: 1, updated_at: "", by_id: {} };
|
|
5154
5292
|
let byId = /* @__PURE__ */ new Map();
|
|
5155
5293
|
let lastSession;
|
|
5156
|
-
if (
|
|
5294
|
+
if (existsSync19(ctx.paths.memoriesDir)) {
|
|
5157
5295
|
const allLoaded = await loadMemoriesFromDir13(ctx.paths.memoriesDir);
|
|
5158
5296
|
const recaps = allLoaded.filter(({ memory: memory2 }) => memory2.frontmatter.type === "session_recap").sort(
|
|
5159
5297
|
(a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
|
|
@@ -5238,34 +5376,25 @@ async function getBriefing(input, ctx) {
|
|
|
5238
5376
|
if (input.task) {
|
|
5239
5377
|
const tokens = tokenizeQuery22(input.task);
|
|
5240
5378
|
const andHits = allMemories.filter((m) => literalMatchesAllTokens22(m.memory, tokens));
|
|
5241
|
-
for (const loaded of andHits)
|
|
5242
|
-
addOrUpdate(loaded, "semantic", void 0, "exact");
|
|
5243
|
-
}
|
|
5379
|
+
for (const loaded of andHits) addOrUpdate(loaded, "semantic", void 0, "exact");
|
|
5244
5380
|
if (andHits.length === 0 && tokens.length > 1) {
|
|
5245
5381
|
for (const loaded of allMemories) {
|
|
5246
|
-
if (literalMatchesAnyToken22(loaded.memory, tokens))
|
|
5247
|
-
addOrUpdate(loaded, "semantic", void 0, "partial");
|
|
5248
|
-
}
|
|
5382
|
+
if (literalMatchesAnyToken22(loaded.memory, tokens)) addOrUpdate(loaded, "semantic", void 0, "partial");
|
|
5249
5383
|
}
|
|
5250
5384
|
}
|
|
5251
5385
|
if (semanticHits) {
|
|
5252
5386
|
for (const hit of semanticHits) {
|
|
5253
|
-
if (hit.score < input.min_semantic_score)
|
|
5254
|
-
const existing = seen.get(hit.id);
|
|
5255
|
-
if (!existing) continue;
|
|
5256
|
-
}
|
|
5387
|
+
if (hit.score < input.min_semantic_score && !seen.has(hit.id)) continue;
|
|
5257
5388
|
const loaded = byId.get(hit.id);
|
|
5258
5389
|
if (loaded) addOrUpdate(loaded, "semantic", hit.score, "semantic");
|
|
5259
5390
|
}
|
|
5260
5391
|
}
|
|
5261
5392
|
}
|
|
5262
5393
|
const ranked = [...seen.values()].sort((a, b) => {
|
|
5263
|
-
const
|
|
5264
|
-
const reasonScore = (m) => (m.type === "attempt" ? 3 : 0) + // attempt = negative knowledge, surface first to prevent repeating mistakes
|
|
5265
|
-
(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);
|
|
5394
|
+
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);
|
|
5266
5395
|
const confidenceScore = (m) => m.confidence === "authoritative" ? 4 : m.confidence === "trusted" ? 3 : m.confidence === "low" ? 1 : m.confidence === "stale" ? -2 : 0;
|
|
5267
|
-
const sa =
|
|
5268
|
-
const sb =
|
|
5396
|
+
const sa = priorityRank(classifyMemoryPriority(a, byId.get(a.id), input.files, input.symbols)) * 100 + reasonScore(a) + confidenceScore(a) + (a.semantic_score ?? 0);
|
|
5397
|
+
const sb = priorityRank(classifyMemoryPriority(b, byId.get(b.id), input.files, input.symbols)) * 100 + reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
|
|
5269
5398
|
return sb - sa;
|
|
5270
5399
|
});
|
|
5271
5400
|
for (const mem of ranked.slice(0, briefingMaxMemories)) {
|
|
@@ -5294,11 +5423,7 @@ async function getBriefing(input, ctx) {
|
|
|
5294
5423
|
if (!isAutoPromoteEligible(loaded.memory.frontmatter, u, rule)) continue;
|
|
5295
5424
|
const newFm = { ...loaded.memory.frontmatter, status: "validated" };
|
|
5296
5425
|
try {
|
|
5297
|
-
await writeFile11(
|
|
5298
|
-
loaded.filePath,
|
|
5299
|
-
serializeMemory9({ frontmatter: newFm, body: loaded.memory.body }),
|
|
5300
|
-
"utf8"
|
|
5301
|
-
);
|
|
5426
|
+
await writeFile11(loaded.filePath, serializeMemory9({ frontmatter: newFm, body: loaded.memory.body }), "utf8");
|
|
5302
5427
|
m.status = "validated";
|
|
5303
5428
|
m.confidence = "trusted";
|
|
5304
5429
|
} catch {
|
|
@@ -5306,12 +5431,12 @@ async function getBriefing(input, ctx) {
|
|
|
5306
5431
|
}
|
|
5307
5432
|
}
|
|
5308
5433
|
}
|
|
5309
|
-
const projectContextRaw = input.include_project_context &&
|
|
5434
|
+
const projectContextRaw = input.include_project_context && existsSync19(ctx.paths.projectContext) ? await readFile42(ctx.paths.projectContext, "utf8") : "";
|
|
5310
5435
|
const isTemplateContext = projectContextRaw.includes("TODO \u2014 high-level overview") || projectContextRaw.includes("Generated by `haive init`");
|
|
5311
5436
|
const setupWarnings = [];
|
|
5312
5437
|
let autoContextGenerated = false;
|
|
5313
5438
|
let projectContext = isTemplateContext ? "" : projectContextRaw;
|
|
5314
|
-
if ((isTemplateContext || !
|
|
5439
|
+
if ((isTemplateContext || !existsSync19(ctx.paths.projectContext)) && input.include_project_context) {
|
|
5315
5440
|
const haiveConfig = await loadConfig3(ctx.paths);
|
|
5316
5441
|
if (haiveConfig.autoContext) {
|
|
5317
5442
|
const codeMap = await loadCodeMap5(ctx.paths);
|
|
@@ -5347,15 +5472,9 @@ async function getBriefing(input, ctx) {
|
|
|
5347
5472
|
);
|
|
5348
5473
|
}
|
|
5349
5474
|
} else {
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
);
|
|
5354
|
-
} else {
|
|
5355
|
-
setupWarnings.push(
|
|
5356
|
-
"No project-context.md found. Run `haive init` then invoke the bootstrap_project MCP prompt."
|
|
5357
|
-
);
|
|
5358
|
-
}
|
|
5475
|
+
setupWarnings.push(
|
|
5476
|
+
isTemplateContext ? "project-context.md still contains the default template. Invoke the bootstrap_project MCP prompt to auto-fill it from your codebase. Until then, get_briefing returns no project context." : "No project-context.md found. Run `haive init` then invoke the bootstrap_project MCP prompt."
|
|
5477
|
+
);
|
|
5359
5478
|
}
|
|
5360
5479
|
}
|
|
5361
5480
|
const moduleContents = briefingIncludeModules ? await loadModuleContexts2(ctx, inferred) : [];
|
|
@@ -5418,10 +5537,7 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
5418
5537
|
const createdAt = loaded?.memory.frontmatter.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
5419
5538
|
if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
|
|
5420
5539
|
}
|
|
5421
|
-
const formattedMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : input.format === "actions" ? trimmedMemories.map((m) => ({
|
|
5422
|
-
...m,
|
|
5423
|
-
body: extractActionsBriefBody2(m.body)
|
|
5424
|
-
})) : trimmedMemories;
|
|
5540
|
+
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;
|
|
5425
5541
|
const outputMemories = formattedMemories.map((m) => ({
|
|
5426
5542
|
...m,
|
|
5427
5543
|
priority: classifyMemoryPriority(m, byId.get(m.id), input.files, input.symbols),
|
|
@@ -5436,8 +5552,7 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
5436
5552
|
let symbolLocations;
|
|
5437
5553
|
const symbolsToLookup = new Set(input.symbols);
|
|
5438
5554
|
for (const m of outputMemories) {
|
|
5439
|
-
const
|
|
5440
|
-
for (const sym of loaded?.memory.frontmatter.anchor.symbols ?? []) {
|
|
5555
|
+
for (const sym of byId.get(m.id)?.memory.frontmatter.anchor.symbols ?? []) {
|
|
5441
5556
|
symbolsToLookup.add(sym);
|
|
5442
5557
|
}
|
|
5443
5558
|
}
|
|
@@ -5465,41 +5580,37 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
5465
5580
|
}
|
|
5466
5581
|
}
|
|
5467
5582
|
const actionRequired = [];
|
|
5468
|
-
|
|
5469
|
-
const
|
|
5470
|
-
if (!loaded?.memory.frontmatter.requires_human_approval) continue;
|
|
5471
|
-
const bodyLines = loaded.memory.body.split("\n");
|
|
5583
|
+
const extractActionItem = (id, body) => {
|
|
5584
|
+
const bodyLines = body.split("\n");
|
|
5472
5585
|
const quoteBlock = bodyLines.filter((l) => l.startsWith("> ")).map((l) => l.slice(2)).join(" ").replace(/^\*«\s*/, "").replace(/\s*»\*$/, "").trim();
|
|
5473
5586
|
const headingLine = bodyLines.find((l) => l.startsWith("## "));
|
|
5474
|
-
const summary = headingLine?.replace(/^##\s*/, "").trim() ??
|
|
5475
|
-
|
|
5476
|
-
id
|
|
5587
|
+
const summary = headingLine?.replace(/^##\s*/, "").trim() ?? id;
|
|
5588
|
+
return {
|
|
5589
|
+
id,
|
|
5477
5590
|
summary,
|
|
5478
|
-
developer_message: quoteBlock || `Une modification externe potentiellement incompatible a \xE9t\xE9 d\xE9tect\xE9e (${
|
|
5479
|
-
}
|
|
5591
|
+
developer_message: quoteBlock || `Une modification externe potentiellement incompatible a \xE9t\xE9 d\xE9tect\xE9e (${id}). Veux-tu que j'analyse l'impact et que je propose des mises \xE0 jour ?`
|
|
5592
|
+
};
|
|
5593
|
+
};
|
|
5594
|
+
for (const m of outputMemories) {
|
|
5595
|
+
const loaded = byId.get(m.id);
|
|
5596
|
+
if (loaded?.memory.frontmatter.requires_human_approval) {
|
|
5597
|
+
actionRequired.push(extractActionItem(m.id, loaded.memory.body));
|
|
5598
|
+
}
|
|
5480
5599
|
}
|
|
5481
|
-
if (
|
|
5600
|
+
if (existsSync19(ctx.paths.memoriesDir)) {
|
|
5482
5601
|
const allMems = await loadMemoriesFromDir13(ctx.paths.memoriesDir);
|
|
5483
5602
|
for (const { memory: memory2 } of allMems) {
|
|
5484
5603
|
const fm = memory2.frontmatter;
|
|
5485
5604
|
if (!fm.requires_human_approval) continue;
|
|
5486
5605
|
if (fm.status === "rejected" || fm.status === "deprecated") continue;
|
|
5487
5606
|
if (actionRequired.some((a) => a.id === fm.id)) continue;
|
|
5488
|
-
|
|
5489
|
-
const quoteBlock = bodyLines.filter((l) => l.startsWith("> ")).map((l) => l.slice(2)).join(" ").replace(/^\*«\s*/, "").replace(/\s*»\*$/, "").trim();
|
|
5490
|
-
const headingLine = bodyLines.find((l) => l.startsWith("## "));
|
|
5491
|
-
const summary = headingLine?.replace(/^##\s*/, "").trim() ?? fm.id;
|
|
5492
|
-
actionRequired.push({
|
|
5493
|
-
id: fm.id,
|
|
5494
|
-
summary,
|
|
5495
|
-
developer_message: quoteBlock || `Une modification externe potentiellement incompatible a \xE9t\xE9 d\xE9tect\xE9e (${fm.id}). Veux-tu que j'analyse l'impact et que je propose des mises \xE0 jour ?`
|
|
5496
|
-
});
|
|
5607
|
+
actionRequired.push(extractActionItem(fm.id, memory2.body));
|
|
5497
5608
|
}
|
|
5498
5609
|
}
|
|
5499
5610
|
const pendingDistillFile = pendingDistillPath(ctx);
|
|
5500
|
-
if (
|
|
5611
|
+
if (existsSync19(pendingDistillFile)) {
|
|
5501
5612
|
try {
|
|
5502
|
-
const raw = await
|
|
5613
|
+
const raw = await readFile42(pendingDistillFile, "utf8");
|
|
5503
5614
|
const pd = JSON.parse(raw);
|
|
5504
5615
|
const ageMs = Date.now() - new Date(pd.session_end).getTime();
|
|
5505
5616
|
const SEVEN_DAYS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -5526,7 +5637,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
5526
5637
|
}
|
|
5527
5638
|
}
|
|
5528
5639
|
const memoriesEmpty = outputMemories.length === 0;
|
|
5529
|
-
const hasMemoriesDir =
|
|
5640
|
+
const hasMemoriesDir = existsSync19(ctx.paths.memoriesDir);
|
|
5530
5641
|
const isColdStart = isTemplateContext && memoriesEmpty && !lastSession && !autoContextGenerated;
|
|
5531
5642
|
const hasUnguessableSignal = outputMemories.some(
|
|
5532
5643
|
(m) => (m.priority === "must_read" || m.priority === "useful") && specificityScore2(m.body) >= GUESSABLE_THRESHOLD
|
|
@@ -5573,7 +5684,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
5573
5684
|
"No team-specific policy matched these files/task \u2014 nothing here a capable model can't infer. The auto-generated project context was trimmed to keep this briefing near-zero-cost; proceed with normal Read/Grep."
|
|
5574
5685
|
);
|
|
5575
5686
|
}
|
|
5576
|
-
if (
|
|
5687
|
+
if (existsSync19(ctx.paths.haiveDir)) {
|
|
5577
5688
|
await writeBriefingMarker2(ctx.paths, {
|
|
5578
5689
|
sessionId: process.env.HAIVE_SESSION_ID,
|
|
5579
5690
|
...input.task ? { task: input.task } : {},
|
|
@@ -5621,144 +5732,6 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
5621
5732
|
}
|
|
5622
5733
|
};
|
|
5623
5734
|
}
|
|
5624
|
-
function compactSummary(body) {
|
|
5625
|
-
for (const line of body.split("\n")) {
|
|
5626
|
-
const trimmed = line.replace(/^#+\s*/, "").trim();
|
|
5627
|
-
if (trimmed.length > 0) return trimmed.slice(0, 120);
|
|
5628
|
-
}
|
|
5629
|
-
return body.slice(0, 120);
|
|
5630
|
-
}
|
|
5631
|
-
function classifyMemoryPriority(memory2, loaded, inputFiles, inputSymbols) {
|
|
5632
|
-
const fm = loaded?.memory.frontmatter;
|
|
5633
|
-
const directAnchor = Boolean(
|
|
5634
|
-
fm && inputFiles.length > 0 && fm.anchor.paths.some((p) => inputFiles.some((file) => pathsOverlap(p, file)))
|
|
5635
|
-
);
|
|
5636
|
-
const directSymbol = Boolean(
|
|
5637
|
-
fm && inputSymbols.length > 0 && fm.anchor.symbols.some(
|
|
5638
|
-
(sym) => inputSymbols.some((wanted) => wanted.toLowerCase() === sym.toLowerCase())
|
|
5639
|
-
)
|
|
5640
|
-
);
|
|
5641
|
-
const strongSemantic = (memory2.semantic_score ?? 0) >= 0.65;
|
|
5642
|
-
const usefulSemantic = (memory2.semantic_score ?? 0) >= 0.35;
|
|
5643
|
-
if (fm?.requires_human_approval || directAnchor || directSymbol || memory2.type === "attempt" && (memory2.match_quality === "exact" || strongSemantic) || memory2.type === "skill" && (memory2.match_quality === "exact" || strongSemantic)) {
|
|
5644
|
-
return "must_read";
|
|
5645
|
-
}
|
|
5646
|
-
if (isStackPackSeed2(fm)) {
|
|
5647
|
-
return "background";
|
|
5648
|
-
}
|
|
5649
|
-
if (memory2.type === "skill" || memory2.reasons.includes("module") || memory2.reasons.includes("domain") || memory2.match_quality === "exact" || usefulSemantic) {
|
|
5650
|
-
return "useful";
|
|
5651
|
-
}
|
|
5652
|
-
return "background";
|
|
5653
|
-
}
|
|
5654
|
-
function priorityRank(priority) {
|
|
5655
|
-
return priority === "must_read" ? 3 : priority === "useful" ? 2 : 1;
|
|
5656
|
-
}
|
|
5657
|
-
function classifyBriefingQuality(memories, context) {
|
|
5658
|
-
const mustRead = memories.filter((m) => m.priority === "must_read").length;
|
|
5659
|
-
const useful = memories.filter((m) => m.priority === "useful").length;
|
|
5660
|
-
const background = memories.filter((m) => m.priority === "background").length;
|
|
5661
|
-
const weakSemantic = memories.filter(
|
|
5662
|
-
(m) => m.reasons.length === 1 && m.reasons.includes("semantic") && (m.semantic_score ?? 0) > 0 && (m.semantic_score ?? 0) < 0.35
|
|
5663
|
-
).length;
|
|
5664
|
-
const reasons = [];
|
|
5665
|
-
if (memories.length === 0) reasons.push("no memories matched the task or files");
|
|
5666
|
-
if (context.isTemplateContext && !context.autoContextGenerated) reasons.push("project context is still a template");
|
|
5667
|
-
if (!context.hasLastSession) reasons.push("no previous session recap");
|
|
5668
|
-
if (mustRead > 0) reasons.push(`${mustRead} must_read memor${mustRead === 1 ? "y" : "ies"} matched directly`);
|
|
5669
|
-
if (useful > 0) reasons.push(`${useful} useful memor${useful === 1 ? "y" : "ies"} matched`);
|
|
5670
|
-
if (background > useful + mustRead && background > 2) reasons.push(`${background} background memories dominate the result`);
|
|
5671
|
-
if (weakSemantic > 0) reasons.push(`${weakSemantic} weak semantic-only match${weakSemantic === 1 ? "" : "es"}`);
|
|
5672
|
-
if (context.searchMode === "literal_fallback") reasons.push("semantic index unavailable or empty; literal fallback used");
|
|
5673
|
-
if (memories.length === 0 || mustRead === 0 && useful === 0) {
|
|
5674
|
-
return { level: "thin", reasons };
|
|
5675
|
-
}
|
|
5676
|
-
if (background > useful + mustRead && background > 2) {
|
|
5677
|
-
return { level: "noisy", reasons };
|
|
5678
|
-
}
|
|
5679
|
-
return { level: "strong", reasons };
|
|
5680
|
-
}
|
|
5681
|
-
function explainWhySurfaced(memory2, loaded, inputFiles, inferredModules) {
|
|
5682
|
-
const why = [];
|
|
5683
|
-
const fm = loaded?.memory.frontmatter;
|
|
5684
|
-
if (memory2.reasons.includes("anchor") && fm) {
|
|
5685
|
-
const matching = fm.anchor.paths.filter(
|
|
5686
|
-
(p) => inputFiles.length === 0 || inputFiles.some((file) => pathsOverlap(p, file))
|
|
5687
|
-
);
|
|
5688
|
-
if (matching.length > 0) {
|
|
5689
|
-
const exact = matching.filter(
|
|
5690
|
-
(p) => !isGlobPath(p) && inputFiles.some((file) => p === file || pathsOverlap(p, file))
|
|
5691
|
-
);
|
|
5692
|
-
const glob = matching.filter((p) => isGlobPath(p));
|
|
5693
|
-
if (exact.length > 0) {
|
|
5694
|
-
why.push(`Exact/file anchor match: ${exact.slice(0, 4).join(", ")}`);
|
|
5695
|
-
}
|
|
5696
|
-
if (glob.length > 0) {
|
|
5697
|
-
why.push(`Glob anchor match: ${glob.slice(0, 4).join(", ")}`);
|
|
5698
|
-
}
|
|
5699
|
-
if (exact.length === 0 && glob.length === 0) {
|
|
5700
|
-
why.push(`Anchored to touched path${matching.length === 1 ? "" : "s"}: ${matching.slice(0, 4).join(", ")}`);
|
|
5701
|
-
}
|
|
5702
|
-
} else if (fm.anchor.paths.length > 0) {
|
|
5703
|
-
why.push(`Pulled by related anchor: ${fm.anchor.paths.slice(0, 4).join(", ")}`);
|
|
5704
|
-
}
|
|
5705
|
-
if (fm.anchor.symbols.length > 0) {
|
|
5706
|
-
why.push(`Anchor symbol${fm.anchor.symbols.length === 1 ? "" : "s"}: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
|
|
5707
|
-
}
|
|
5708
|
-
}
|
|
5709
|
-
if (memory2.reasons.includes("symbol") && fm) {
|
|
5710
|
-
why.push(`Explicit symbol match: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
|
|
5711
|
-
}
|
|
5712
|
-
if (memory2.reasons.includes("module")) {
|
|
5713
|
-
const moduleHints = [
|
|
5714
|
-
...memory2.module ? [memory2.module] : [],
|
|
5715
|
-
...memory2.tags.filter((tag) => inferredModules.includes(tag))
|
|
5716
|
-
];
|
|
5717
|
-
const shown = moduleHints.length > 0 ? [...new Set(moduleHints)].join(", ") : inferredModules.join(", ");
|
|
5718
|
-
why.push(shown ? `Matched inferred module/tag: ${shown}` : "Matched inferred module context.");
|
|
5719
|
-
}
|
|
5720
|
-
if (memory2.reasons.includes("domain")) {
|
|
5721
|
-
why.push("Matched inferred domain from the target file paths.");
|
|
5722
|
-
}
|
|
5723
|
-
if (memory2.reasons.includes("semantic")) {
|
|
5724
|
-
const score = memory2.semantic_score !== void 0 ? ` score=${Math.round(memory2.semantic_score * 100) / 100}` : "";
|
|
5725
|
-
why.push(`${memory2.match_quality === "exact" ? "Literal task match" : "Semantic/task relevance"}${score}.`);
|
|
5726
|
-
}
|
|
5727
|
-
why.push(`Confidence: ${memory2.confidence}; read ${memory2.read_count} time${memory2.read_count === 1 ? "" : "s"}.`);
|
|
5728
|
-
if (memory2.type === "attempt") why.push("Failed-approach record; read before repeating the same path.");
|
|
5729
|
-
if (memory2.type === "skill") why.push("Skill (reusable procedure/playbook) \u2014 follow the steps described when doing this type of task.");
|
|
5730
|
-
if (memory2.status === "proposed" || memory2.status === "draft") {
|
|
5731
|
-
why.push("Unvalidated record; use cautiously or ask a human before treating it as policy.");
|
|
5732
|
-
}
|
|
5733
|
-
return why;
|
|
5734
|
-
}
|
|
5735
|
-
async function trySemanticHits(ctx, task, limit) {
|
|
5736
|
-
let mod;
|
|
5737
|
-
try {
|
|
5738
|
-
mod = await import("@hiveai/embeddings");
|
|
5739
|
-
} catch {
|
|
5740
|
-
return null;
|
|
5741
|
-
}
|
|
5742
|
-
const result = await mod.semanticSearch(ctx.paths, task, { limit });
|
|
5743
|
-
if (!result) return null;
|
|
5744
|
-
return result.hits.map((h) => ({ id: h.id, score: h.score }));
|
|
5745
|
-
}
|
|
5746
|
-
async function loadModuleContexts2(ctx, modules) {
|
|
5747
|
-
if (modules.length === 0) return [];
|
|
5748
|
-
if (!existsSync18(ctx.paths.modulesContextDir)) return [];
|
|
5749
|
-
const available = new Set(
|
|
5750
|
-
(await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
5751
|
-
);
|
|
5752
|
-
const out = [];
|
|
5753
|
-
for (const m of modules) {
|
|
5754
|
-
if (!available.has(m)) continue;
|
|
5755
|
-
const file = path92.join(ctx.paths.modulesContextDir, m, "context.md");
|
|
5756
|
-
if (existsSync18(file)) {
|
|
5757
|
-
out.push({ name: m, content: await readFile32(file, "utf8") });
|
|
5758
|
-
}
|
|
5759
|
-
}
|
|
5760
|
-
return out;
|
|
5761
|
-
}
|
|
5762
5735
|
var CodeMapInputSchema = {
|
|
5763
5736
|
file: z18.string().optional().describe("Filter to files whose path contains this substring"),
|
|
5764
5737
|
symbol: z18.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
|
|
@@ -5842,7 +5815,7 @@ var MemDiffInputSchema = {
|
|
|
5842
5815
|
id_b: z19.string().min(1).describe("Second memory id")
|
|
5843
5816
|
};
|
|
5844
5817
|
async function memDiff(input, ctx) {
|
|
5845
|
-
if (!
|
|
5818
|
+
if (!existsSync20(ctx.paths.memoriesDir)) {
|
|
5846
5819
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
5847
5820
|
}
|
|
5848
5821
|
const all = await loadMemoriesFromDir14(ctx.paths.memoriesDir);
|
|
@@ -5883,7 +5856,7 @@ var GetRecapInputSchema = {
|
|
|
5883
5856
|
)
|
|
5884
5857
|
};
|
|
5885
5858
|
async function getRecap(input, ctx) {
|
|
5886
|
-
if (!
|
|
5859
|
+
if (!existsSync21(ctx.paths.memoriesDir)) {
|
|
5887
5860
|
return { recap: null, notice: "No .ai/memories directory \u2014 haive not initialized here." };
|
|
5888
5861
|
}
|
|
5889
5862
|
const all = await loadMemoriesFromDir15(ctx.paths.memoriesDir);
|
|
@@ -5982,7 +5955,7 @@ var WhyThisFileInputSchema = {
|
|
|
5982
5955
|
memory_limit: z23.number().int().positive().max(20).default(5).describe("Cap on memories anchored to this path.")
|
|
5983
5956
|
};
|
|
5984
5957
|
async function whyThisFile(input, ctx) {
|
|
5985
|
-
const fileExists =
|
|
5958
|
+
const fileExists = existsSync222(path102.join(ctx.paths.root, input.path));
|
|
5986
5959
|
const [commits, memories, codeMap] = await Promise.all([
|
|
5987
5960
|
runGitLog(ctx.paths.root, input.path, input.git_log_limit).catch(() => []),
|
|
5988
5961
|
collectAnchoredMemories(ctx, input.path, input.memory_limit),
|
|
@@ -6023,7 +5996,7 @@ async function whyThisFile(input, ctx) {
|
|
|
6023
5996
|
};
|
|
6024
5997
|
}
|
|
6025
5998
|
async function collectAnchoredMemories(ctx, filePath, limit) {
|
|
6026
|
-
if (!
|
|
5999
|
+
if (!existsSync222(ctx.paths.memoriesDir)) return [];
|
|
6027
6000
|
const all = await loadMemoriesFromDir16(ctx.paths.memoriesDir);
|
|
6028
6001
|
const usage = await loadUsageIndex8(ctx.paths);
|
|
6029
6002
|
const out = [];
|
|
@@ -6155,7 +6128,7 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
6155
6128
|
notice: "Nothing to check \u2014 provide either `diff` text or `paths`."
|
|
6156
6129
|
};
|
|
6157
6130
|
}
|
|
6158
|
-
if (!
|
|
6131
|
+
if (!existsSync23(ctx.paths.memoriesDir)) {
|
|
6159
6132
|
return { scanned: 0, warnings: [], notice: "No .ai/memories directory \u2014 nothing to check against." };
|
|
6160
6133
|
}
|
|
6161
6134
|
const all = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
|
|
@@ -6190,6 +6163,7 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
6190
6163
|
reasons: [reason],
|
|
6191
6164
|
tags: fm.tags ?? [],
|
|
6192
6165
|
anchor_paths: fm.anchor?.paths ?? [],
|
|
6166
|
+
...fm.sensor != null ? { has_sensor: true } : {},
|
|
6193
6167
|
...score !== void 0 ? { semantic_score: score } : {}
|
|
6194
6168
|
});
|
|
6195
6169
|
};
|
|
@@ -6302,7 +6276,7 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
|
6302
6276
|
"error"
|
|
6303
6277
|
]);
|
|
6304
6278
|
async function memDistill(input, ctx) {
|
|
6305
|
-
if (!
|
|
6279
|
+
if (!existsSync24(ctx.paths.memoriesDir)) {
|
|
6306
6280
|
return { scanned: 0, singletons: 0, clusters: [], notice: "No .ai/memories directory." };
|
|
6307
6281
|
}
|
|
6308
6282
|
const cutoff = Date.now() - input.since_days * MS_PER_DAY;
|
|
@@ -6413,7 +6387,7 @@ var WhyThisDecisionInputSchema = {
|
|
|
6413
6387
|
git_log_limit: z26.number().int().positive().max(20).default(5).describe("How many recent commits per anchor path to surface.")
|
|
6414
6388
|
};
|
|
6415
6389
|
async function whyThisDecision(input, ctx) {
|
|
6416
|
-
if (!
|
|
6390
|
+
if (!existsSync25(ctx.paths.memoriesDir)) {
|
|
6417
6391
|
return {
|
|
6418
6392
|
found: false,
|
|
6419
6393
|
related: [],
|
|
@@ -6551,7 +6525,7 @@ var MemConflictsInputSchema = {
|
|
|
6551
6525
|
var POSITIVE_PATTERNS = /\b(use|prefer|always|should use|do this|recommended|ok to)\b/i;
|
|
6552
6526
|
var NEGATIVE_PATTERNS = /\b(do not use|don'?t use|never|avoid|forbidden|deprecated|stop using|do NOT|❌)\b/i;
|
|
6553
6527
|
async function memConflicts(input, ctx) {
|
|
6554
|
-
if (!
|
|
6528
|
+
if (!existsSync26(ctx.paths.memoriesDir)) {
|
|
6555
6529
|
return { found: false, scanned: 0, conflicts: [], notice: "No .ai/memories directory." };
|
|
6556
6530
|
}
|
|
6557
6531
|
const all = await loadMemoriesFromDir20(ctx.paths.memoriesDir);
|
|
@@ -6772,6 +6746,15 @@ function classifyWarning(warning, paths, anchoredBlocks = false) {
|
|
|
6772
6746
|
};
|
|
6773
6747
|
}
|
|
6774
6748
|
if (isBlockingWarning(warning)) {
|
|
6749
|
+
if (warning.has_sensor && !warning.reasons.includes("sensor")) {
|
|
6750
|
+
return {
|
|
6751
|
+
...warning,
|
|
6752
|
+
level: "review",
|
|
6753
|
+
rationale: "memory has a sensor that did not fire \u2014 sensor is the authoritative check; strong semantic match alone is insufficient to block",
|
|
6754
|
+
affected_files: affectedFiles,
|
|
6755
|
+
repair_command: repairCommand
|
|
6756
|
+
};
|
|
6757
|
+
}
|
|
6775
6758
|
return {
|
|
6776
6759
|
...warning,
|
|
6777
6760
|
level: "blocking",
|
|
@@ -6784,6 +6767,15 @@ function classifyWarning(warning, paths, anchoredBlocks = false) {
|
|
|
6784
6767
|
const semanticScore = warning.semantic_score ?? 0;
|
|
6785
6768
|
const highConfidence = warning.confidence === "authoritative" || warning.confidence === "trusted";
|
|
6786
6769
|
if (anchoredBlocks && highConfidence && warning.reasons.includes("anchor") && (warning.reasons.includes("literal") || hasSemantic && semanticScore >= 0.45)) {
|
|
6770
|
+
if (warning.has_sensor && !warning.reasons.includes("sensor")) {
|
|
6771
|
+
return {
|
|
6772
|
+
...warning,
|
|
6773
|
+
level: "review",
|
|
6774
|
+
rationale: "memory has a sensor that did not fire \u2014 literal match alone is insufficient to block; sensor is the authoritative check",
|
|
6775
|
+
affected_files: affectedFiles,
|
|
6776
|
+
repair_command: repairCommand
|
|
6777
|
+
};
|
|
6778
|
+
}
|
|
6787
6779
|
return {
|
|
6788
6780
|
...warning,
|
|
6789
6781
|
level: "blocking",
|
|
@@ -6938,7 +6930,7 @@ var PatternDetectInputSchema = {
|
|
|
6938
6930
|
scope: z29.enum(["personal", "team"]).default("team").describe("Scope for proposed memories.")
|
|
6939
6931
|
};
|
|
6940
6932
|
async function patternDetect(input, ctx) {
|
|
6941
|
-
if (!
|
|
6933
|
+
if (!existsSync27(ctx.paths.haiveDir)) {
|
|
6942
6934
|
return {
|
|
6943
6935
|
scanned_events: 0,
|
|
6944
6936
|
matches: [],
|
|
@@ -7067,7 +7059,7 @@ async function patternDetect(input, ctx) {
|
|
|
7067
7059
|
fm.id,
|
|
7068
7060
|
void 0
|
|
7069
7061
|
);
|
|
7070
|
-
if (
|
|
7062
|
+
if (existsSync27(file)) continue;
|
|
7071
7063
|
await mkdir72(path112.dirname(file), { recursive: true });
|
|
7072
7064
|
await writeFile12(
|
|
7073
7065
|
file,
|
|
@@ -7122,7 +7114,7 @@ var MemConflictCandidatesInputSchema = {
|
|
|
7122
7114
|
)
|
|
7123
7115
|
};
|
|
7124
7116
|
async function memConflictCandidates(input, ctx) {
|
|
7125
|
-
if (!
|
|
7117
|
+
if (!existsSync28(ctx.paths.memoriesDir)) {
|
|
7126
7118
|
return {
|
|
7127
7119
|
pairs: [],
|
|
7128
7120
|
topic_status_pairs: [],
|
|
@@ -7170,7 +7162,7 @@ var MemTimelineInputSchema = {
|
|
|
7170
7162
|
limit: z33.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
|
|
7171
7163
|
};
|
|
7172
7164
|
async function memTimeline(input, ctx) {
|
|
7173
|
-
if (!
|
|
7165
|
+
if (!existsSync29(ctx.paths.memoriesDir)) {
|
|
7174
7166
|
return { entries: [], total: 0, notice: "No .ai/memories directory." };
|
|
7175
7167
|
}
|
|
7176
7168
|
const all = await loadMemoriesFromDir222(ctx.paths.memoriesDir);
|
|
@@ -7442,7 +7434,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
7442
7434
|
};
|
|
7443
7435
|
}
|
|
7444
7436
|
var SERVER_NAME = "haive";
|
|
7445
|
-
var SERVER_VERSION = "0.10.
|
|
7437
|
+
var SERVER_VERSION = "0.10.8";
|
|
7446
7438
|
function jsonResult(data) {
|
|
7447
7439
|
return {
|
|
7448
7440
|
content: [
|
|
@@ -7546,11 +7538,16 @@ function createHaiveServer(options = {}) {
|
|
|
7546
7538
|
return await handler(input);
|
|
7547
7539
|
}
|
|
7548
7540
|
if (requireBriefingFirst && MUTATING_TOOLS.has(name) && !briefingLoaded) {
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7541
|
+
const hasDiskMarker = await hasRecentBriefingMarker(context.paths).catch(() => false);
|
|
7542
|
+
if (hasDiskMarker) {
|
|
7543
|
+
briefingLoaded = true;
|
|
7544
|
+
} else {
|
|
7545
|
+
return jsonResult({
|
|
7546
|
+
error: "haive_briefing_required",
|
|
7547
|
+
message: "This hAIve project requires get_briefing or mem_relevant_to before state-changing hAIve tools. Call get_briefing({ task: '...' }) first.",
|
|
7548
|
+
tool: name
|
|
7549
|
+
});
|
|
7550
|
+
}
|
|
7554
7551
|
}
|
|
7555
7552
|
return await handler(input);
|
|
7556
7553
|
}
|
|
@@ -8412,7 +8409,7 @@ function registerMcp(program2) {
|
|
|
8412
8409
|
// src/commands/sync.ts
|
|
8413
8410
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
8414
8411
|
import { readFile as readFile9, writeFile as writeFile13, mkdir as mkdir10 } from "fs/promises";
|
|
8415
|
-
import { existsSync as
|
|
8412
|
+
import { existsSync as existsSync30 } from "fs";
|
|
8416
8413
|
import path15 from "path";
|
|
8417
8414
|
import "commander";
|
|
8418
8415
|
import {
|
|
@@ -8449,7 +8446,7 @@ function registerSync(program2) {
|
|
|
8449
8446
|
).option("--bridge-file <path>", "bridge file to inject into (default: CLAUDE.md)").option("--bridge-max-memories <n>", "max memories to inject into bridge file", "5").option("--embed", "rebuild embeddings index after sync (requires @haive/embeddings)").option("--no-cross-repo", "skip cross-repo memory pull even if crossRepoSources is configured").option("--no-deps", "skip dependency version tracking").option("--no-contracts", "skip contract file diff checking").option("--dry-run", "report what would change without writing any files").action(async (opts) => {
|
|
8450
8447
|
const root = findProjectRoot12(opts.dir);
|
|
8451
8448
|
const paths = resolveHaivePaths9(root);
|
|
8452
|
-
if (!
|
|
8449
|
+
if (!existsSync30(paths.memoriesDir)) {
|
|
8453
8450
|
if (!opts.quiet) ui.warn(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
8454
8451
|
process.exitCode = 1;
|
|
8455
8452
|
return;
|
|
@@ -8873,7 +8870,7 @@ function bridgeSummaryLine(body) {
|
|
|
8873
8870
|
return oneLine.length > 140 ? oneLine.slice(0, 137) + "\u2026" : oneLine;
|
|
8874
8871
|
}
|
|
8875
8872
|
async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
|
|
8876
|
-
if (!
|
|
8873
|
+
if (!existsSync30(memoriesDir)) return;
|
|
8877
8874
|
const all = await loadMemoriesFromDir23(memoriesDir);
|
|
8878
8875
|
const top = all.filter(({ memory: memory2 }) => {
|
|
8879
8876
|
const s = memory2.frontmatter.status;
|
|
@@ -8899,7 +8896,7 @@ async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
|
|
|
8899
8896
|
` + block + `
|
|
8900
8897
|
|
|
8901
8898
|
${BRIDGE_END}`;
|
|
8902
|
-
const fileExists =
|
|
8899
|
+
const fileExists = existsSync30(bridgeFile);
|
|
8903
8900
|
let existing = fileExists ? await readFile9(bridgeFile, "utf8") : "";
|
|
8904
8901
|
existing = existing.replace(/\r\n/g, "\n");
|
|
8905
8902
|
const startIdx = existing.indexOf(BRIDGE_START);
|
|
@@ -8950,7 +8947,7 @@ function collectSinceChanges(root, ref) {
|
|
|
8950
8947
|
// src/commands/memory-add.ts
|
|
8951
8948
|
import { createHash as createHash2 } from "crypto";
|
|
8952
8949
|
import { mkdir as mkdir11, readFile as readFile10, writeFile as writeFile14 } from "fs/promises";
|
|
8953
|
-
import { existsSync as
|
|
8950
|
+
import { existsSync as existsSync31 } from "fs";
|
|
8954
8951
|
import path16 from "path";
|
|
8955
8952
|
import "commander";
|
|
8956
8953
|
import {
|
|
@@ -8992,7 +8989,7 @@ function registerMemoryAdd(memory2) {
|
|
|
8992
8989
|
).requiredOption("--type <type>", "skill | convention | decision | gotcha | architecture | glossary | attempt").option("--slug <slug>", "short kebab-case identifier used in the file name (auto-derived from --title/--body when omitted)").option("--title <text>", "memory title \u2014 becomes the first heading of the body").option("--scope <scope>", "personal | team | module (default: config default; team in autopilot)").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags for easier retrieval").option("--domain <domain>", "domain (e.g. transactions)").option("--author <author>", "author email or handle").option("--paths <csv>", "anchor to source files \u2014 used for staleness detection by haive sync").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--symbols <csv>", "anchor to specific symbols (class/function names)").option("--commit <sha>", "anchor to a specific commit SHA").option("--body <text>", "memory body content (Markdown) \u2014 overrides --title default body").option("--body-file <path>", "read memory body from a Markdown file \u2014 for long content").option("--no-auto-tag", "disable automatic tag suggestions inferred from anchor paths").option("--topic <key>", "stable key for upsert: if a memory with this topic+scope already exists, update it in-place (revision_count++)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8993
8990
|
const root = findProjectRoot13(opts.dir);
|
|
8994
8991
|
const paths = resolveHaivePaths10(root);
|
|
8995
|
-
if (!
|
|
8992
|
+
if (!existsSync31(paths.haiveDir)) {
|
|
8996
8993
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
8997
8994
|
process.exitCode = 1;
|
|
8998
8995
|
return;
|
|
@@ -9004,7 +9001,7 @@ function registerMemoryAdd(memory2) {
|
|
|
9004
9001
|
const inferredTags = autoTagsEnabled ? inferModulesFromPaths3(anchorPaths) : [];
|
|
9005
9002
|
const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
|
|
9006
9003
|
if (anchorPaths.length > 0) {
|
|
9007
|
-
const missing = anchorPaths.filter((p) => !
|
|
9004
|
+
const missing = anchorPaths.filter((p) => !existsSync31(path16.resolve(root, p)));
|
|
9008
9005
|
if (missing.length > 0) {
|
|
9009
9006
|
ui.warn(`Anchor path${missing.length > 1 ? "s" : ""} not found in project:`);
|
|
9010
9007
|
for (const p of missing) ui.warn(` \u2717 ${p}`);
|
|
@@ -9017,7 +9014,7 @@ function registerMemoryAdd(memory2) {
|
|
|
9017
9014
|
const slug = slugify(opts.slug ?? opts.title ?? opts.topic ?? opts.body ?? `${opts.type}-memory`);
|
|
9018
9015
|
let body;
|
|
9019
9016
|
if (opts.bodyFile !== void 0) {
|
|
9020
|
-
if (!
|
|
9017
|
+
if (!existsSync31(opts.bodyFile)) {
|
|
9021
9018
|
ui.error(`--body-file not found: ${opts.bodyFile}`);
|
|
9022
9019
|
process.exitCode = 1;
|
|
9023
9020
|
return;
|
|
@@ -9033,7 +9030,7 @@ TODO \u2014 write the memory body.
|
|
|
9033
9030
|
`;
|
|
9034
9031
|
}
|
|
9035
9032
|
const scope = opts.scope ?? config.defaultScope ?? "personal";
|
|
9036
|
-
if (
|
|
9033
|
+
if (existsSync31(paths.memoriesDir)) {
|
|
9037
9034
|
const incomingHash = createHash2("sha256").update(body.trim()).digest("hex").slice(0, 12);
|
|
9038
9035
|
const allForHash = await loadMemoriesFromDir24(paths.memoriesDir);
|
|
9039
9036
|
const hashDup = allForHash.find(
|
|
@@ -9046,7 +9043,7 @@ TODO \u2014 write the memory body.
|
|
|
9046
9043
|
return;
|
|
9047
9044
|
}
|
|
9048
9045
|
}
|
|
9049
|
-
if (opts.topic &&
|
|
9046
|
+
if (opts.topic && existsSync31(paths.memoriesDir)) {
|
|
9050
9047
|
const existing = await loadMemoriesFromDir24(paths.memoriesDir);
|
|
9051
9048
|
const topicMatch = existing.find(
|
|
9052
9049
|
({ memory: memory3 }) => memory3.frontmatter.topic === opts.topic && memory3.frontmatter.scope === scope && (!opts.module || memory3.frontmatter.module === opts.module)
|
|
@@ -9091,12 +9088,12 @@ TODO \u2014 write the memory body.
|
|
|
9091
9088
|
});
|
|
9092
9089
|
const file = memoryFilePath6(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
9093
9090
|
await mkdir11(path16.dirname(file), { recursive: true });
|
|
9094
|
-
if (
|
|
9091
|
+
if (existsSync31(file)) {
|
|
9095
9092
|
ui.error(`Memory already exists at ${file}`);
|
|
9096
9093
|
process.exitCode = 1;
|
|
9097
9094
|
return;
|
|
9098
9095
|
}
|
|
9099
|
-
if (
|
|
9096
|
+
if (existsSync31(paths.memoriesDir)) {
|
|
9100
9097
|
const existing = await loadMemoriesFromDir24(paths.memoriesDir);
|
|
9101
9098
|
const slugTokens = slug.toLowerCase().split(/[-_\s]+/).filter(Boolean);
|
|
9102
9099
|
const similar = existing.filter(({ memory: memory3 }) => {
|
|
@@ -9188,7 +9185,7 @@ function slugify(value) {
|
|
|
9188
9185
|
}
|
|
9189
9186
|
|
|
9190
9187
|
// src/commands/memory-list.ts
|
|
9191
|
-
import { existsSync as
|
|
9188
|
+
import { existsSync as existsSync33 } from "fs";
|
|
9192
9189
|
import path17 from "path";
|
|
9193
9190
|
import "commander";
|
|
9194
9191
|
import { findProjectRoot as findProjectRoot14, resolveHaivePaths as resolveHaivePaths11 } from "@hiveai/core";
|
|
@@ -9205,7 +9202,7 @@ function registerMemoryList(memory2) {
|
|
|
9205
9202
|
memory2.command("list").description("List memories with optional filters").option("--scope <scope>", "personal | team | module").option("--type <type>", "filter by type").option("--tag <tag>", "filter by tag").option("--module <name>", "filter by module name").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected,deprecated)").option("--show-rejected", "include rejected memories (hidden by default)").option("--limit <n>", "max memories to display").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9206
9203
|
const root = findProjectRoot14(opts.dir);
|
|
9207
9204
|
const paths = resolveHaivePaths11(root);
|
|
9208
|
-
if (!
|
|
9205
|
+
if (!existsSync33(paths.memoriesDir)) {
|
|
9209
9206
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
9210
9207
|
process.exitCode = 1;
|
|
9211
9208
|
return;
|
|
@@ -9280,7 +9277,7 @@ function matchesFilters(loaded, opts) {
|
|
|
9280
9277
|
|
|
9281
9278
|
// src/commands/memory-promote.ts
|
|
9282
9279
|
import { mkdir as mkdir12, unlink as unlink2, writeFile as writeFile15 } from "fs/promises";
|
|
9283
|
-
import { existsSync as
|
|
9280
|
+
import { existsSync as existsSync34 } from "fs";
|
|
9284
9281
|
import path18 from "path";
|
|
9285
9282
|
import "commander";
|
|
9286
9283
|
import {
|
|
@@ -9293,7 +9290,7 @@ function registerMemoryPromote(memory2) {
|
|
|
9293
9290
|
memory2.command("promote <id>").description("Promote a personal memory to team scope (status -> proposed)").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
9294
9291
|
const root = findProjectRoot15(opts.dir);
|
|
9295
9292
|
const paths = resolveHaivePaths12(root);
|
|
9296
|
-
if (!
|
|
9293
|
+
if (!existsSync34(paths.memoriesDir)) {
|
|
9297
9294
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
9298
9295
|
process.exitCode = 1;
|
|
9299
9296
|
return;
|
|
@@ -9338,7 +9335,7 @@ function registerMemoryPromote(memory2) {
|
|
|
9338
9335
|
}
|
|
9339
9336
|
|
|
9340
9337
|
// src/commands/memory-approve.ts
|
|
9341
|
-
import { existsSync as
|
|
9338
|
+
import { existsSync as existsSync35 } from "fs";
|
|
9342
9339
|
import { writeFile as writeFile16 } from "fs/promises";
|
|
9343
9340
|
import path19 from "path";
|
|
9344
9341
|
import "commander";
|
|
@@ -9351,7 +9348,7 @@ function registerMemoryApprove(memory2) {
|
|
|
9351
9348
|
memory2.command("approve [id]").description("Mark a memory as 'validated'. Use --all to bulk-approve all proposed/draft memories.").option("--all", "approve all proposed and draft memories at once").option("--pending", "approve all memories with status 'proposed'").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
9352
9349
|
const root = findProjectRoot16(opts.dir);
|
|
9353
9350
|
const paths = resolveHaivePaths13(root);
|
|
9354
|
-
if (!
|
|
9351
|
+
if (!existsSync35(paths.memoriesDir)) {
|
|
9355
9352
|
ui.error(`No .ai/memories at ${root}.`);
|
|
9356
9353
|
process.exitCode = 1;
|
|
9357
9354
|
return;
|
|
@@ -9410,7 +9407,7 @@ function registerMemoryApprove(memory2) {
|
|
|
9410
9407
|
|
|
9411
9408
|
// src/commands/memory-update.ts
|
|
9412
9409
|
import { readFile as readFile11, writeFile as writeFile17 } from "fs/promises";
|
|
9413
|
-
import { existsSync as
|
|
9410
|
+
import { existsSync as existsSync36 } from "fs";
|
|
9414
9411
|
import path20 from "path";
|
|
9415
9412
|
import "commander";
|
|
9416
9413
|
import {
|
|
@@ -9422,7 +9419,7 @@ function registerMemoryUpdate(memory2) {
|
|
|
9422
9419
|
memory2.command("update <id>").description("Update body, tags, or anchor of an existing memory (preserves id and usage history)").option("--type <type>", "change the memory type (convention | decision | gotcha | architecture | glossary | skill | attempt)").option("--title <text>", "new title \u2014 replaces the first heading of the body").option("--body <text>", "new Markdown body \u2014 replaces the existing body").option("--body-file <path>", "read new body from a Markdown file \u2014 for long content").option("--tags <csv>", "new tags, comma-separated \u2014 fully replaces existing tags").option("--paths <csv>", "new anchor paths, comma-separated").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--symbols <csv>", "new anchor symbols, comma-separated").option("--commit <sha>", "new anchor commit SHA").option("--domain <domain>", "new domain label").option("--author <author>", "new author handle or email").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
9423
9420
|
const root = findProjectRoot17(opts.dir);
|
|
9424
9421
|
const paths = resolveHaivePaths14(root);
|
|
9425
|
-
if (!
|
|
9422
|
+
if (!existsSync36(paths.memoriesDir)) {
|
|
9426
9423
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
9427
9424
|
process.exitCode = 1;
|
|
9428
9425
|
return;
|
|
@@ -9464,7 +9461,7 @@ function registerMemoryUpdate(memory2) {
|
|
|
9464
9461
|
if (opts.author !== void 0) updated.push("author");
|
|
9465
9462
|
let newBody;
|
|
9466
9463
|
if (opts.bodyFile !== void 0) {
|
|
9467
|
-
if (!
|
|
9464
|
+
if (!existsSync36(opts.bodyFile)) {
|
|
9468
9465
|
ui.error(`--body-file not found: ${opts.bodyFile}`);
|
|
9469
9466
|
process.exitCode = 1;
|
|
9470
9467
|
return;
|
|
@@ -9510,7 +9507,7 @@ function parseCsv3(value) {
|
|
|
9510
9507
|
|
|
9511
9508
|
// src/commands/memory-auto-promote.ts
|
|
9512
9509
|
import { writeFile as writeFile18 } from "fs/promises";
|
|
9513
|
-
import { existsSync as
|
|
9510
|
+
import { existsSync as existsSync37 } from "fs";
|
|
9514
9511
|
import path21 from "path";
|
|
9515
9512
|
import "commander";
|
|
9516
9513
|
import {
|
|
@@ -9530,7 +9527,7 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
9530
9527
|
).option("--apply", "actually write status=validated to disk (default: dry-run)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9531
9528
|
const root = findProjectRoot18(opts.dir);
|
|
9532
9529
|
const paths = resolveHaivePaths15(root);
|
|
9533
|
-
if (!
|
|
9530
|
+
if (!existsSync37(paths.memoriesDir)) {
|
|
9534
9531
|
ui.error(`No .ai/memories at ${root}.`);
|
|
9535
9532
|
process.exitCode = 1;
|
|
9536
9533
|
return;
|
|
@@ -9573,7 +9570,7 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
9573
9570
|
|
|
9574
9571
|
// src/commands/memory-edit.ts
|
|
9575
9572
|
import { spawn as spawn3 } from "child_process";
|
|
9576
|
-
import { existsSync as
|
|
9573
|
+
import { existsSync as existsSync38 } from "fs";
|
|
9577
9574
|
import { readFile as readFile12 } from "fs/promises";
|
|
9578
9575
|
import path23 from "path";
|
|
9579
9576
|
import "commander";
|
|
@@ -9586,7 +9583,7 @@ function registerMemoryEdit(memory2) {
|
|
|
9586
9583
|
memory2.command("edit <id>").description("Open a memory in $EDITOR and re-validate when you save").option("-e, --editor <cmd>", "editor command (defaults to $EDITOR or 'vi')").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
9587
9584
|
const root = findProjectRoot19(opts.dir);
|
|
9588
9585
|
const paths = resolveHaivePaths16(root);
|
|
9589
|
-
if (!
|
|
9586
|
+
if (!existsSync38(paths.memoriesDir)) {
|
|
9590
9587
|
ui.error(`No .ai/memories at ${root}.`);
|
|
9591
9588
|
process.exitCode = 1;
|
|
9592
9589
|
return;
|
|
@@ -9626,7 +9623,7 @@ function runEditor(editor, file) {
|
|
|
9626
9623
|
}
|
|
9627
9624
|
|
|
9628
9625
|
// src/commands/memory-for-files.ts
|
|
9629
|
-
import { existsSync as
|
|
9626
|
+
import { existsSync as existsSync39 } from "fs";
|
|
9630
9627
|
import path24 from "path";
|
|
9631
9628
|
import "commander";
|
|
9632
9629
|
import {
|
|
@@ -9642,7 +9639,7 @@ function registerMemoryForFiles(memory2) {
|
|
|
9642
9639
|
memory2.command("for-files <files...>").description("Show memories relevant to the given files (anchor overlap, module, domain)").option("-d, --dir <dir>", "project root").action(async (files, opts) => {
|
|
9643
9640
|
const root = findProjectRoot20(opts.dir);
|
|
9644
9641
|
const paths = resolveHaivePaths17(root);
|
|
9645
|
-
if (!
|
|
9642
|
+
if (!existsSync39(paths.memoriesDir)) {
|
|
9646
9643
|
ui.error(`No .ai/memories at ${root}.`);
|
|
9647
9644
|
process.exitCode = 1;
|
|
9648
9645
|
return;
|
|
@@ -9754,7 +9751,7 @@ function printGroup(root, label, loaded, usage) {
|
|
|
9754
9751
|
}
|
|
9755
9752
|
|
|
9756
9753
|
// src/commands/memory-hot.ts
|
|
9757
|
-
import { existsSync as
|
|
9754
|
+
import { existsSync as existsSync40 } from "fs";
|
|
9758
9755
|
import path25 from "path";
|
|
9759
9756
|
import "commander";
|
|
9760
9757
|
import {
|
|
@@ -9769,7 +9766,7 @@ function registerMemoryHot(memory2) {
|
|
|
9769
9766
|
).option("--threshold <n>", "minimum read_count to qualify (default: 3)", "3").option("--status <status>", "limit to one status (default: draft + proposed)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9770
9767
|
const root = findProjectRoot21(opts.dir);
|
|
9771
9768
|
const paths = resolveHaivePaths18(root);
|
|
9772
|
-
if (!
|
|
9769
|
+
if (!existsSync40(paths.memoriesDir)) {
|
|
9773
9770
|
ui.error(`No .ai/memories at ${root}.`);
|
|
9774
9771
|
process.exitCode = 1;
|
|
9775
9772
|
return;
|
|
@@ -9808,7 +9805,7 @@ function registerMemoryHot(memory2) {
|
|
|
9808
9805
|
|
|
9809
9806
|
// src/commands/memory-tried.ts
|
|
9810
9807
|
import { mkdir as mkdir13, writeFile as writeFile19 } from "fs/promises";
|
|
9811
|
-
import { existsSync as
|
|
9808
|
+
import { existsSync as existsSync41 } from "fs";
|
|
9812
9809
|
import path26 from "path";
|
|
9813
9810
|
import "commander";
|
|
9814
9811
|
import {
|
|
@@ -9838,7 +9835,7 @@ function registerMemoryTried(memory2) {
|
|
|
9838
9835
|
).requiredOption("--what <text>", "what approach was tried (short, descriptive title)").requiredOption("--why-failed <text>", "why it failed or should NOT be used (include the exact error if possible)").option("--instead <text>", "the correct approach to use instead").option("--scope <scope>", "personal | team | module", "personal").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags").option("--paths <csv>", "anchor paths, comma-separated").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--author <author>", "author email or handle").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9839
9836
|
const root = findProjectRoot22(opts.dir);
|
|
9840
9837
|
const paths = resolveHaivePaths19(root);
|
|
9841
|
-
if (!
|
|
9838
|
+
if (!existsSync41(paths.haiveDir)) {
|
|
9842
9839
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
9843
9840
|
process.exitCode = 1;
|
|
9844
9841
|
return;
|
|
@@ -9866,7 +9863,7 @@ function registerMemoryTried(memory2) {
|
|
|
9866
9863
|
}
|
|
9867
9864
|
const file = memoryFilePath8(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
9868
9865
|
await mkdir13(path26.dirname(file), { recursive: true });
|
|
9869
|
-
if (
|
|
9866
|
+
if (existsSync41(file)) {
|
|
9870
9867
|
ui.error(`Memory already exists at ${file}`);
|
|
9871
9868
|
process.exitCode = 1;
|
|
9872
9869
|
return;
|
|
@@ -9884,7 +9881,7 @@ function parseCsv4(value) {
|
|
|
9884
9881
|
|
|
9885
9882
|
// src/commands/memory-seed.ts
|
|
9886
9883
|
import { readFile as readFile13 } from "fs/promises";
|
|
9887
|
-
import { existsSync as
|
|
9884
|
+
import { existsSync as existsSync43 } from "fs";
|
|
9888
9885
|
import path27 from "path";
|
|
9889
9886
|
import "commander";
|
|
9890
9887
|
import {
|
|
@@ -9924,7 +9921,7 @@ function registerMemorySeed(memory2) {
|
|
|
9924
9921
|
}
|
|
9925
9922
|
return;
|
|
9926
9923
|
}
|
|
9927
|
-
if (!
|
|
9924
|
+
if (!existsSync43(paths.haiveDir)) {
|
|
9928
9925
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
9929
9926
|
process.exitCode = 1;
|
|
9930
9927
|
return;
|
|
@@ -9980,7 +9977,7 @@ function registerMemorySeed(memory2) {
|
|
|
9980
9977
|
}
|
|
9981
9978
|
|
|
9982
9979
|
// src/commands/memory-pending.ts
|
|
9983
|
-
import { existsSync as
|
|
9980
|
+
import { existsSync as existsSync44 } from "fs";
|
|
9984
9981
|
import path28 from "path";
|
|
9985
9982
|
import "commander";
|
|
9986
9983
|
import {
|
|
@@ -9993,7 +9990,7 @@ function registerMemoryPending(memory2) {
|
|
|
9993
9990
|
memory2.command("pending").description("List draft and proposed memories awaiting review (sorted by reads desc).\n\n draft = created but not yet activated \xB7 proposed = promoted, awaiting team validation").option("--scope <scope>", "filter by scope (personal | team | module)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9994
9991
|
const root = findProjectRoot24(opts.dir);
|
|
9995
9992
|
const paths = resolveHaivePaths21(root);
|
|
9996
|
-
if (!
|
|
9993
|
+
if (!existsSync44(paths.memoriesDir)) {
|
|
9997
9994
|
ui.error(`No .ai/memories at ${root}.`);
|
|
9998
9995
|
process.exitCode = 1;
|
|
9999
9996
|
return;
|
|
@@ -10051,7 +10048,7 @@ function registerMemoryPending(memory2) {
|
|
|
10051
10048
|
}
|
|
10052
10049
|
|
|
10053
10050
|
// src/commands/memory-query.ts
|
|
10054
|
-
import { existsSync as
|
|
10051
|
+
import { existsSync as existsSync45 } from "fs";
|
|
10055
10052
|
import path29 from "path";
|
|
10056
10053
|
import "commander";
|
|
10057
10054
|
import {
|
|
@@ -10068,7 +10065,7 @@ function registerMemoryQuery(memory2) {
|
|
|
10068
10065
|
memory2.command("query <text>").alias("search").description("Search memories by id, tag, or substring (AND, OR fallback). Alias: search").option("-d, --dir <dir>", "project root").option("--limit <n>", "max results", "20").option("--scope <scope>", "personal | team | module").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected)").option("--show-rejected", "include rejected memories (hidden by default)").action(async (text, opts) => {
|
|
10069
10066
|
const root = findProjectRoot25(opts.dir);
|
|
10070
10067
|
const paths = resolveHaivePaths22(root);
|
|
10071
|
-
if (!
|
|
10068
|
+
if (!existsSync45(paths.memoriesDir)) {
|
|
10072
10069
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
10073
10070
|
process.exitCode = 1;
|
|
10074
10071
|
return;
|
|
@@ -10127,7 +10124,7 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
|
|
|
10127
10124
|
|
|
10128
10125
|
// src/commands/memory-reject.ts
|
|
10129
10126
|
import { writeFile as writeFile20 } from "fs/promises";
|
|
10130
|
-
import { existsSync as
|
|
10127
|
+
import { existsSync as existsSync46 } from "fs";
|
|
10131
10128
|
import "commander";
|
|
10132
10129
|
import {
|
|
10133
10130
|
findProjectRoot as findProjectRoot26,
|
|
@@ -10141,7 +10138,7 @@ function registerMemoryReject(memory2) {
|
|
|
10141
10138
|
memory2.command("reject <id>").description("Record a rejection (blocks auto-promotion and lowers confidence)").option("-r, --reason <reason>", "why this memory is being rejected").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
10142
10139
|
const root = findProjectRoot26(opts.dir);
|
|
10143
10140
|
const paths = resolveHaivePaths23(root);
|
|
10144
|
-
if (!
|
|
10141
|
+
if (!existsSync46(paths.memoriesDir)) {
|
|
10145
10142
|
ui.error(`No .ai/memories at ${root}.`);
|
|
10146
10143
|
process.exitCode = 1;
|
|
10147
10144
|
return;
|
|
@@ -10177,7 +10174,7 @@ function registerMemoryReject(memory2) {
|
|
|
10177
10174
|
}
|
|
10178
10175
|
|
|
10179
10176
|
// src/commands/memory-rm.ts
|
|
10180
|
-
import { existsSync as
|
|
10177
|
+
import { existsSync as existsSync47 } from "fs";
|
|
10181
10178
|
import { unlink as unlink3 } from "fs/promises";
|
|
10182
10179
|
import path30 from "path";
|
|
10183
10180
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
@@ -10192,7 +10189,7 @@ function registerMemoryRm(memory2) {
|
|
|
10192
10189
|
memory2.command("rm <id>").description("Delete a memory file (and its usage entry by default)").option("-y, --yes", "skip the confirmation prompt").option("--keep-usage", "do not remove the usage.json entry").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
10193
10190
|
const root = findProjectRoot27(opts.dir);
|
|
10194
10191
|
const paths = resolveHaivePaths24(root);
|
|
10195
|
-
if (!
|
|
10192
|
+
if (!existsSync47(paths.memoriesDir)) {
|
|
10196
10193
|
ui.error(`No .ai/memories at ${root}.`);
|
|
10197
10194
|
process.exitCode = 1;
|
|
10198
10195
|
return;
|
|
@@ -10228,7 +10225,7 @@ function registerMemoryRm(memory2) {
|
|
|
10228
10225
|
}
|
|
10229
10226
|
|
|
10230
10227
|
// src/commands/memory-show.ts
|
|
10231
|
-
import { existsSync as
|
|
10228
|
+
import { existsSync as existsSync48 } from "fs";
|
|
10232
10229
|
import { readFile as readFile14 } from "fs/promises";
|
|
10233
10230
|
import path31 from "path";
|
|
10234
10231
|
import "commander";
|
|
@@ -10243,7 +10240,7 @@ function registerMemoryShow(memory2) {
|
|
|
10243
10240
|
memory2.command("show <id>").description("Print a memory's frontmatter, body, and confidence/usage").option("--raw", "print the raw file contents instead of a summary").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
10244
10241
|
const root = findProjectRoot28(opts.dir);
|
|
10245
10242
|
const paths = resolveHaivePaths25(root);
|
|
10246
|
-
if (!
|
|
10243
|
+
if (!existsSync48(paths.memoriesDir)) {
|
|
10247
10244
|
ui.error(`No .ai/memories at ${root}.`);
|
|
10248
10245
|
process.exitCode = 1;
|
|
10249
10246
|
return;
|
|
@@ -10287,7 +10284,7 @@ function registerMemoryShow(memory2) {
|
|
|
10287
10284
|
}
|
|
10288
10285
|
|
|
10289
10286
|
// src/commands/memory-stats.ts
|
|
10290
|
-
import { existsSync as
|
|
10287
|
+
import { existsSync as existsSync49 } from "fs";
|
|
10291
10288
|
import path33 from "path";
|
|
10292
10289
|
import "commander";
|
|
10293
10290
|
import {
|
|
@@ -10301,7 +10298,7 @@ function registerMemoryStats(memory2) {
|
|
|
10301
10298
|
memory2.command("stats").description("Show usage stats and confidence levels per memory").option("--id <id>", "show stats for a single memory id").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10302
10299
|
const root = findProjectRoot29(opts.dir);
|
|
10303
10300
|
const paths = resolveHaivePaths26(root);
|
|
10304
|
-
if (!
|
|
10301
|
+
if (!existsSync49(paths.memoriesDir)) {
|
|
10305
10302
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
10306
10303
|
process.exitCode = 1;
|
|
10307
10304
|
return;
|
|
@@ -10333,7 +10330,7 @@ function registerMemoryStats(memory2) {
|
|
|
10333
10330
|
|
|
10334
10331
|
// src/commands/memory-verify.ts
|
|
10335
10332
|
import { writeFile as writeFile21 } from "fs/promises";
|
|
10336
|
-
import { existsSync as
|
|
10333
|
+
import { existsSync as existsSync50 } from "fs";
|
|
10337
10334
|
import path34 from "path";
|
|
10338
10335
|
import "commander";
|
|
10339
10336
|
import {
|
|
@@ -10348,7 +10345,7 @@ function registerMemoryVerify(memory2) {
|
|
|
10348
10345
|
).option("--id <id>", "verify a single memory by id").option("--all", "verify every memory (default if --id is omitted)").option("--update", "write status=stale or status=validated back to disk").option("--json", "emit machine-readable JSON (for CI / agents)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10349
10346
|
const root = findProjectRoot30(opts.dir);
|
|
10350
10347
|
const paths = resolveHaivePaths27(root);
|
|
10351
|
-
if (!
|
|
10348
|
+
if (!existsSync50(paths.memoriesDir)) {
|
|
10352
10349
|
if (opts.json) {
|
|
10353
10350
|
console.log(JSON.stringify({ error: "not-initialized", root }, null, 2));
|
|
10354
10351
|
} else {
|
|
@@ -10469,7 +10466,7 @@ function applyVerification2(mem, result) {
|
|
|
10469
10466
|
|
|
10470
10467
|
// src/commands/memory-import.ts
|
|
10471
10468
|
import { readFile as readFile15 } from "fs/promises";
|
|
10472
|
-
import { existsSync as
|
|
10469
|
+
import { existsSync as existsSync51 } from "fs";
|
|
10473
10470
|
import "commander";
|
|
10474
10471
|
import {
|
|
10475
10472
|
findProjectRoot as findProjectRoot31,
|
|
@@ -10481,12 +10478,12 @@ function registerMemoryImport(memory2) {
|
|
|
10481
10478
|
).requiredOption("--from <file>", "Markdown/text file to import from").option("--scope <scope>", "personal | team (default: team)", "team").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10482
10479
|
const root = findProjectRoot31(opts.dir);
|
|
10483
10480
|
const paths = resolveHaivePaths28(root);
|
|
10484
|
-
if (!
|
|
10481
|
+
if (!existsSync51(paths.haiveDir)) {
|
|
10485
10482
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
10486
10483
|
process.exitCode = 1;
|
|
10487
10484
|
return;
|
|
10488
10485
|
}
|
|
10489
|
-
if (!
|
|
10486
|
+
if (!existsSync51(opts.from)) {
|
|
10490
10487
|
ui.error(`File not found: ${opts.from}`);
|
|
10491
10488
|
process.exitCode = 1;
|
|
10492
10489
|
return;
|
|
@@ -10519,7 +10516,7 @@ function registerMemoryImport(memory2) {
|
|
|
10519
10516
|
}
|
|
10520
10517
|
|
|
10521
10518
|
// src/commands/memory-import-changelog.ts
|
|
10522
|
-
import { existsSync as
|
|
10519
|
+
import { existsSync as existsSync53 } from "fs";
|
|
10523
10520
|
import { readFile as readFile16, mkdir as mkdir14, writeFile as writeFile23 } from "fs/promises";
|
|
10524
10521
|
import path35 from "path";
|
|
10525
10522
|
import "commander";
|
|
@@ -10594,7 +10591,7 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
10594
10591
|
const root = findProjectRoot32(opts.dir);
|
|
10595
10592
|
const paths = resolveHaivePaths29(root);
|
|
10596
10593
|
const changelogPath = path35.resolve(root, opts.fromChangelog);
|
|
10597
|
-
if (!
|
|
10594
|
+
if (!existsSync53(changelogPath)) {
|
|
10598
10595
|
ui.error(`CHANGELOG not found: ${changelogPath}`);
|
|
10599
10596
|
process.exitCode = 1;
|
|
10600
10597
|
return;
|
|
@@ -10681,7 +10678,7 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
|
|
|
10681
10678
|
}
|
|
10682
10679
|
|
|
10683
10680
|
// src/commands/memory-digest.ts
|
|
10684
|
-
import { existsSync as
|
|
10681
|
+
import { existsSync as existsSync54 } from "fs";
|
|
10685
10682
|
import { writeFile as writeFile24 } from "fs/promises";
|
|
10686
10683
|
import path36 from "path";
|
|
10687
10684
|
import "commander";
|
|
@@ -10706,7 +10703,7 @@ function registerMemoryDigest(program2) {
|
|
|
10706
10703
|
).option("--days <n>", "look-back window in days (default: 7)", "7").option("--scope <scope>", "personal | team | module | all (default: team)", "team").option("--out <file>", "write digest to a file instead of stdout").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10707
10704
|
const root = findProjectRoot33(opts.dir);
|
|
10708
10705
|
const paths = resolveHaivePaths30(root);
|
|
10709
|
-
if (!
|
|
10706
|
+
if (!existsSync54(paths.memoriesDir)) {
|
|
10710
10707
|
ui.error("No .ai/memories found. Run `haive init` first.");
|
|
10711
10708
|
process.exitCode = 1;
|
|
10712
10709
|
return;
|
|
@@ -10789,7 +10786,7 @@ function registerMemoryDigest(program2) {
|
|
|
10789
10786
|
|
|
10790
10787
|
// src/commands/session-end.ts
|
|
10791
10788
|
import { writeFile as writeFile25, mkdir as mkdir15, readFile as readFile17, rm as rm2 } from "fs/promises";
|
|
10792
|
-
import { existsSync as
|
|
10789
|
+
import { existsSync as existsSync55 } from "fs";
|
|
10793
10790
|
import { spawn as spawn4 } from "child_process";
|
|
10794
10791
|
import path37 from "path";
|
|
10795
10792
|
import "commander";
|
|
@@ -10803,7 +10800,7 @@ import {
|
|
|
10803
10800
|
} from "@hiveai/core";
|
|
10804
10801
|
async function buildAutoRecap(paths) {
|
|
10805
10802
|
const obsFile = path37.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
10806
|
-
if (!
|
|
10803
|
+
if (!existsSync55(obsFile)) return await buildGitAutoRecap(paths);
|
|
10807
10804
|
const raw = await readFile17(obsFile, "utf8").catch(() => "");
|
|
10808
10805
|
if (!raw.trim()) return await buildGitAutoRecap(paths);
|
|
10809
10806
|
const lines = raw.split("\n").filter(Boolean);
|
|
@@ -10994,7 +10991,7 @@ function registerSessionEnd(session2) {
|
|
|
10994
10991
|
).option("--goal <text>", "what you were trying to accomplish (1\u20132 sentences)").option("--accomplished <text>", "what was actually done (bullet list recommended)").option("--discoveries <text>", "bugs, surprises, or inconsistencies found during this session").option("--files <csv>", "key files touched, comma-separated (used as anchor for staleness detection)").option("--next <text>", "what should happen next (for the next session or a teammate)").option("--scope <scope>", "personal | team | module (default: personal)", "personal").option("--module <name>", "module name (required when scope=module)").option("--auto", "synthesize the recap from .ai/.cache/observations.jsonl (used by Claude Code SessionEnd hook)").option("--quiet", "suppress non-error output (for hook use)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10995
10992
|
const root = findProjectRoot34(opts.dir);
|
|
10996
10993
|
const paths = resolveHaivePaths31(root);
|
|
10997
|
-
if (!
|
|
10994
|
+
if (!existsSync55(paths.haiveDir)) {
|
|
10998
10995
|
if (opts.auto || opts.quiet) return;
|
|
10999
10996
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
11000
10997
|
process.exitCode = 1;
|
|
@@ -11027,7 +11024,7 @@ function registerSessionEnd(session2) {
|
|
|
11027
11024
|
});
|
|
11028
11025
|
const topic = recapTopic2(scope, opts.module);
|
|
11029
11026
|
const filesTouched = parseCsv5(resolvedFiles).map((p) => normalizeAnchorPath(root, p));
|
|
11030
|
-
const missingPaths = filesTouched.filter((p) => !
|
|
11027
|
+
const missingPaths = filesTouched.filter((p) => !existsSync55(path37.resolve(root, p)));
|
|
11031
11028
|
if (missingPaths.length > 0 && !opts.quiet) {
|
|
11032
11029
|
ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
|
|
11033
11030
|
for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
|
|
@@ -11035,10 +11032,10 @@ function registerSessionEnd(session2) {
|
|
|
11035
11032
|
const cleanupObservations = async () => {
|
|
11036
11033
|
if (!opts.auto) return;
|
|
11037
11034
|
const obsFile = path37.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
11038
|
-
if (
|
|
11035
|
+
if (existsSync55(obsFile)) await rm2(obsFile).catch(() => {
|
|
11039
11036
|
});
|
|
11040
11037
|
};
|
|
11041
|
-
if (
|
|
11038
|
+
if (existsSync55(paths.memoriesDir)) {
|
|
11042
11039
|
const existing = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
11043
11040
|
const topicMatch = existing.find(
|
|
11044
11041
|
({ memory: memory2 }) => memory2.frontmatter.topic === topic && memory2.frontmatter.scope === scope && (!opts.module || memory2.frontmatter.module === opts.module)
|
|
@@ -11100,7 +11097,7 @@ function normalizeAnchorPath(root, filePath) {
|
|
|
11100
11097
|
}
|
|
11101
11098
|
|
|
11102
11099
|
// src/commands/snapshot.ts
|
|
11103
|
-
import { existsSync as
|
|
11100
|
+
import { existsSync as existsSync56 } from "fs";
|
|
11104
11101
|
import { readdir as readdir4 } from "fs/promises";
|
|
11105
11102
|
import path38 from "path";
|
|
11106
11103
|
import "commander";
|
|
@@ -11135,14 +11132,14 @@ function registerSnapshot(program2) {
|
|
|
11135
11132
|
).option("--diff", "compare the contract against its stored snapshot").option("--list", "list all stored contract snapshots").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
11136
11133
|
const root = findProjectRoot35(opts.dir);
|
|
11137
11134
|
const paths = resolveHaivePaths32(root);
|
|
11138
|
-
if (!
|
|
11135
|
+
if (!existsSync56(paths.haiveDir)) {
|
|
11139
11136
|
ui.error("No .ai/ found. Run `haive init` first.");
|
|
11140
11137
|
process.exitCode = 1;
|
|
11141
11138
|
return;
|
|
11142
11139
|
}
|
|
11143
11140
|
if (opts.list) {
|
|
11144
11141
|
const contractsDir = path38.join(paths.haiveDir, "contracts");
|
|
11145
|
-
if (!
|
|
11142
|
+
if (!existsSync56(contractsDir)) {
|
|
11146
11143
|
console.log(ui.dim("No contract snapshots found."));
|
|
11147
11144
|
return;
|
|
11148
11145
|
}
|
|
@@ -11266,7 +11263,7 @@ function detectFormat(filePath) {
|
|
|
11266
11263
|
}
|
|
11267
11264
|
|
|
11268
11265
|
// src/commands/hub.ts
|
|
11269
|
-
import { existsSync as
|
|
11266
|
+
import { existsSync as existsSync57 } from "fs";
|
|
11270
11267
|
import { mkdir as mkdir16, readFile as readFile18, writeFile as writeFile26, copyFile } from "fs/promises";
|
|
11271
11268
|
import path39 from "path";
|
|
11272
11269
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
@@ -11368,7 +11365,7 @@ Next steps:
|
|
|
11368
11365
|
return;
|
|
11369
11366
|
}
|
|
11370
11367
|
const hubRoot = path39.resolve(root, config.hubPath);
|
|
11371
|
-
if (!
|
|
11368
|
+
if (!existsSync57(hubRoot)) {
|
|
11372
11369
|
ui.error(`Hub not found at ${hubRoot}. Run \`haive hub init ${config.hubPath}\` first.`);
|
|
11373
11370
|
process.exitCode = 1;
|
|
11374
11371
|
return;
|
|
@@ -11438,7 +11435,7 @@ Next steps:
|
|
|
11438
11435
|
}
|
|
11439
11436
|
const hubRoot = path39.resolve(root, config.hubPath);
|
|
11440
11437
|
const hubSharedDir = path39.join(hubRoot, ".ai", "memories", "shared");
|
|
11441
|
-
if (!
|
|
11438
|
+
if (!existsSync57(hubSharedDir)) {
|
|
11442
11439
|
ui.warn("Hub has no shared memories yet. Run `haive hub push` from other projects first.");
|
|
11443
11440
|
return;
|
|
11444
11441
|
}
|
|
@@ -11493,7 +11490,7 @@ Next steps:
|
|
|
11493
11490
|
` hubPath: ${config.hubPath ? ui.green(config.hubPath) : ui.dim("not configured")}`
|
|
11494
11491
|
);
|
|
11495
11492
|
const sharedDir = path39.join(paths.memoriesDir, "shared");
|
|
11496
|
-
if (
|
|
11493
|
+
if (existsSync57(sharedDir)) {
|
|
11497
11494
|
const { readdir: readdir7 } = await import("fs/promises");
|
|
11498
11495
|
const sources = (await readdir7(sharedDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
11499
11496
|
console.log(`
|
|
@@ -11522,7 +11519,7 @@ Next steps:
|
|
|
11522
11519
|
|
|
11523
11520
|
// src/commands/stats.ts
|
|
11524
11521
|
import "commander";
|
|
11525
|
-
import { existsSync as
|
|
11522
|
+
import { existsSync as existsSync58 } from "fs";
|
|
11526
11523
|
import { mkdir as mkdir17, writeFile as writeFile27 } from "fs/promises";
|
|
11527
11524
|
import path40 from "path";
|
|
11528
11525
|
import {
|
|
@@ -11600,7 +11597,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
|
|
|
11600
11597
|
const size = await usageLogSize(paths);
|
|
11601
11598
|
let events = await readUsageEvents2(paths);
|
|
11602
11599
|
let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
|
|
11603
|
-
if (
|
|
11600
|
+
if (existsSync58(paths.memoriesDir)) {
|
|
11604
11601
|
const mems = await loadMemoriesFromDir29(paths.memoriesDir);
|
|
11605
11602
|
for (const { memory: memory2 } of mems) {
|
|
11606
11603
|
const fm = memory2.frontmatter;
|
|
@@ -11820,7 +11817,7 @@ function summarize(name, t0, payload, notes) {
|
|
|
11820
11817
|
}
|
|
11821
11818
|
|
|
11822
11819
|
// src/commands/benchmark.ts
|
|
11823
|
-
import { existsSync as
|
|
11820
|
+
import { existsSync as existsSync59 } from "fs";
|
|
11824
11821
|
import { readdir as readdir5, readFile as readFile19, writeFile as writeFile28 } from "fs/promises";
|
|
11825
11822
|
import path41 from "path";
|
|
11826
11823
|
import "commander";
|
|
@@ -11868,14 +11865,14 @@ function resolveBenchmarkRoot(dir) {
|
|
|
11868
11865
|
return path41.join(projectRoot, candidate);
|
|
11869
11866
|
}
|
|
11870
11867
|
async function collectRows(root) {
|
|
11871
|
-
if (!
|
|
11868
|
+
if (!existsSync59(root)) throw new Error(`Benchmark directory not found: ${root}`);
|
|
11872
11869
|
const entries = await readdir5(root, { withFileTypes: true });
|
|
11873
11870
|
const rows = [];
|
|
11874
11871
|
for (const entry of entries) {
|
|
11875
11872
|
if (!entry.isDirectory()) continue;
|
|
11876
11873
|
const fixtureDir = path41.join(root, entry.name);
|
|
11877
11874
|
const reportFile = path41.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
|
|
11878
|
-
if (!
|
|
11875
|
+
if (!existsSync59(reportFile)) continue;
|
|
11879
11876
|
const report = await readFile19(reportFile, "utf8");
|
|
11880
11877
|
rows.push(parseAgentReport(entry.name, report));
|
|
11881
11878
|
}
|
|
@@ -11966,7 +11963,7 @@ function escapeRegExp(value) {
|
|
|
11966
11963
|
|
|
11967
11964
|
// src/commands/memory-suggest.ts
|
|
11968
11965
|
import { mkdir as mkdir18, writeFile as writeFile29 } from "fs/promises";
|
|
11969
|
-
import { existsSync as
|
|
11966
|
+
import { existsSync as existsSync60 } from "fs";
|
|
11970
11967
|
import path43 from "path";
|
|
11971
11968
|
import "commander";
|
|
11972
11969
|
import {
|
|
@@ -12045,7 +12042,7 @@ function registerMemorySuggest(memory2) {
|
|
|
12045
12042
|
}
|
|
12046
12043
|
const created = [];
|
|
12047
12044
|
const skipped = [];
|
|
12048
|
-
const existing =
|
|
12045
|
+
const existing = existsSync60(paths.memoriesDir) ? await loadMemoriesFromDir30(paths.memoriesDir) : [];
|
|
12049
12046
|
for (const s of top) {
|
|
12050
12047
|
const slug = slugify2(s.query);
|
|
12051
12048
|
if (!slug) {
|
|
@@ -12069,7 +12066,7 @@ function registerMemorySuggest(memory2) {
|
|
|
12069
12066
|
const body = renderTemplate(s, fm.id, status);
|
|
12070
12067
|
const file = memoryFilePath10(paths, fm.scope, fm.id, fm.module);
|
|
12071
12068
|
await mkdir18(path43.dirname(file), { recursive: true });
|
|
12072
|
-
if (
|
|
12069
|
+
if (existsSync60(file)) {
|
|
12073
12070
|
skipped.push({ query: s.query, reason: `file already exists at ${path43.relative(root, file)}` });
|
|
12074
12071
|
continue;
|
|
12075
12072
|
}
|
|
@@ -12172,7 +12169,7 @@ function truncate2(text, max) {
|
|
|
12172
12169
|
}
|
|
12173
12170
|
|
|
12174
12171
|
// src/commands/memory-archive.ts
|
|
12175
|
-
import { existsSync as
|
|
12172
|
+
import { existsSync as existsSync61 } from "fs";
|
|
12176
12173
|
import { writeFile as writeFile30 } from "fs/promises";
|
|
12177
12174
|
import path44 from "path";
|
|
12178
12175
|
import "commander";
|
|
@@ -12193,7 +12190,7 @@ function registerMemoryArchive(memory2) {
|
|
|
12193
12190
|
).option("--since <window>", "minimum age since last read (e.g. '180d', '6m'). Default: enforcement.decayAfterDays or 180d").option("--type <type>", "limit to a memory type (default 'attempt'). Pass 'all' to scan all types.", "attempt").option("--unread", "decay by unread-age ALONE (ignore anchor status) \u2014 more aggressive corpus hygiene", false).option("--apply", "actually rewrite files (default: dry run)", false).option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
12194
12191
|
const root = findProjectRoot41(opts.dir);
|
|
12195
12192
|
const paths = resolveHaivePaths37(root);
|
|
12196
|
-
if (!
|
|
12193
|
+
if (!existsSync61(paths.memoriesDir)) {
|
|
12197
12194
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
12198
12195
|
process.exitCode = 1;
|
|
12199
12196
|
return;
|
|
@@ -12218,7 +12215,7 @@ function registerMemoryArchive(memory2) {
|
|
|
12218
12215
|
if (fm.status === "deprecated" || fm.status === "rejected") continue;
|
|
12219
12216
|
const retired = retirementSignal2(fm, mem.body);
|
|
12220
12217
|
const hasAnyAnchor = fm.anchor.paths.length + fm.anchor.symbols.length > 0;
|
|
12221
|
-
const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !
|
|
12218
|
+
const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync61(path44.join(paths.root, p)));
|
|
12222
12219
|
const isAnchorless = !hasAnyAnchor;
|
|
12223
12220
|
if (!retired.retired && !opts.unread && !isAnchorless && !allPathsGone) continue;
|
|
12224
12221
|
const u = getUsage18(usage, fm.id);
|
|
@@ -12293,7 +12290,7 @@ function parseDays(input) {
|
|
|
12293
12290
|
}
|
|
12294
12291
|
|
|
12295
12292
|
// src/commands/doctor.ts
|
|
12296
|
-
import { existsSync as
|
|
12293
|
+
import { existsSync as existsSync63, statSync as statSync2 } from "fs";
|
|
12297
12294
|
import { readFile as readFile20, stat, writeFile as writeFile31 } from "fs/promises";
|
|
12298
12295
|
import path45 from "path";
|
|
12299
12296
|
import { execFileSync, execSync as execSync3 } from "child_process";
|
|
@@ -12319,7 +12316,7 @@ function registerDoctor(program2) {
|
|
|
12319
12316
|
const findings = [];
|
|
12320
12317
|
const repairs = [];
|
|
12321
12318
|
const config = await loadConfig11(paths);
|
|
12322
|
-
if (!
|
|
12319
|
+
if (!existsSync63(paths.haiveDir)) {
|
|
12323
12320
|
findings.push({
|
|
12324
12321
|
severity: "error",
|
|
12325
12322
|
code: "not-initialized",
|
|
@@ -12340,7 +12337,7 @@ function registerDoctor(program2) {
|
|
|
12340
12337
|
})
|
|
12341
12338
|
);
|
|
12342
12339
|
}
|
|
12343
|
-
if (!
|
|
12340
|
+
if (!existsSync63(paths.projectContext)) {
|
|
12344
12341
|
findings.push({
|
|
12345
12342
|
severity: "warn",
|
|
12346
12343
|
code: "no-project-context",
|
|
@@ -12369,7 +12366,7 @@ function registerDoctor(program2) {
|
|
|
12369
12366
|
});
|
|
12370
12367
|
}
|
|
12371
12368
|
}
|
|
12372
|
-
const memories =
|
|
12369
|
+
const memories = existsSync63(paths.memoriesDir) ? await loadMemoriesFromDir33(paths.memoriesDir) : [];
|
|
12373
12370
|
const now = Date.now();
|
|
12374
12371
|
if (memories.length === 0) {
|
|
12375
12372
|
findings.push({
|
|
@@ -12512,7 +12509,7 @@ function registerDoctor(program2) {
|
|
|
12512
12509
|
if (config.enforcement?.requireBriefingFirst) {
|
|
12513
12510
|
const claudeSettings = path45.join(root, ".claude", "settings.local.json");
|
|
12514
12511
|
let hasClaudeEnforcement = false;
|
|
12515
|
-
if (
|
|
12512
|
+
if (existsSync63(claudeSettings)) {
|
|
12516
12513
|
try {
|
|
12517
12514
|
const { readFile: readFile24 } = await import("fs/promises");
|
|
12518
12515
|
const raw = await readFile24(claudeSettings, "utf8");
|
|
@@ -12538,14 +12535,14 @@ function registerDoctor(program2) {
|
|
|
12538
12535
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
|
|
12539
12536
|
});
|
|
12540
12537
|
}
|
|
12541
|
-
findings.push(...await collectInstallFindings(root, "0.10.
|
|
12538
|
+
findings.push(...await collectInstallFindings(root, "0.10.8"));
|
|
12542
12539
|
try {
|
|
12543
12540
|
const legacyRaw = execSync3("haive-mcp --version", {
|
|
12544
12541
|
encoding: "utf8",
|
|
12545
12542
|
timeout: 3e3,
|
|
12546
12543
|
stdio: ["ignore", "pipe", "ignore"]
|
|
12547
12544
|
}).trim();
|
|
12548
|
-
const cliVersion = "0.10.
|
|
12545
|
+
const cliVersion = "0.10.8";
|
|
12549
12546
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
12550
12547
|
findings.push({
|
|
12551
12548
|
severity: "warn",
|
|
@@ -12567,7 +12564,7 @@ npm uninstall -g @hiveai/mcp`
|
|
|
12567
12564
|
];
|
|
12568
12565
|
const staleConfigs = [];
|
|
12569
12566
|
for (const cfgPath of configPaths) {
|
|
12570
|
-
if (!
|
|
12567
|
+
if (!existsSync63(cfgPath)) continue;
|
|
12571
12568
|
try {
|
|
12572
12569
|
const raw = await readFile20(cfgPath, "utf8");
|
|
12573
12570
|
if (raw.includes('"haive-mcp"') || raw.includes("'haive-mcp'")) {
|
|
@@ -12862,7 +12859,7 @@ which -a haive`
|
|
|
12862
12859
|
];
|
|
12863
12860
|
for (const rel of integrationFiles) {
|
|
12864
12861
|
const file = path45.join(root, rel);
|
|
12865
|
-
if (!
|
|
12862
|
+
if (!existsSync63(file)) continue;
|
|
12866
12863
|
const text = await readFile20(file, "utf8").catch(() => "");
|
|
12867
12864
|
for (const bin of extractAbsoluteHaiveBins(text)) {
|
|
12868
12865
|
const version = versionForBinary(bin);
|
|
@@ -12958,7 +12955,7 @@ function collectGlobalHivemoduleFindings(expectedVersion) {
|
|
|
12958
12955
|
}
|
|
12959
12956
|
}
|
|
12960
12957
|
async function readJson(file) {
|
|
12961
|
-
if (!
|
|
12958
|
+
if (!existsSync63(file)) return null;
|
|
12962
12959
|
try {
|
|
12963
12960
|
return JSON.parse(await readFile20(file, "utf8"));
|
|
12964
12961
|
} catch {
|
|
@@ -13005,7 +13002,7 @@ function extractAbsoluteHaiveBins(text) {
|
|
|
13005
13002
|
}
|
|
13006
13003
|
|
|
13007
13004
|
// src/commands/playback.ts
|
|
13008
|
-
import { existsSync as
|
|
13005
|
+
import { existsSync as existsSync64 } from "fs";
|
|
13009
13006
|
import "commander";
|
|
13010
13007
|
import {
|
|
13011
13008
|
findProjectRoot as findProjectRoot43,
|
|
@@ -13035,7 +13032,7 @@ function registerPlayback(program2) {
|
|
|
13035
13032
|
const filtered = cutoff > 0 ? events.filter((e) => Date.parse(e.at) >= cutoff) : events;
|
|
13036
13033
|
const gapMs = Math.max(1, parseInt(opts.sessionGap ?? "30", 10)) * MS_PER_MINUTE;
|
|
13037
13034
|
const sessions = bucketSessions(filtered, gapMs);
|
|
13038
|
-
const all =
|
|
13035
|
+
const all = existsSync64(paths.memoriesDir) ? await loadMemoriesFromDir34(paths.memoriesDir) : [];
|
|
13039
13036
|
const memByCreatedAt = all.filter(({ memory: memory2 }) => memory2.frontmatter.type !== "session_recap").map(({ memory: memory2 }) => ({ id: memory2.frontmatter.id, at: Date.parse(memory2.frontmatter.created_at) })).sort((a, b) => a.at - b.at);
|
|
13040
13037
|
const enriched = sessions.map((s, i) => {
|
|
13041
13038
|
const startMs = Date.parse(s.start);
|
|
@@ -13277,7 +13274,7 @@ function runCommand3(cmd, args, cwd) {
|
|
|
13277
13274
|
}
|
|
13278
13275
|
|
|
13279
13276
|
// src/commands/welcome.ts
|
|
13280
|
-
import { existsSync as
|
|
13277
|
+
import { existsSync as existsSync65 } from "fs";
|
|
13281
13278
|
import "commander";
|
|
13282
13279
|
import {
|
|
13283
13280
|
findProjectRoot as findProjectRoot45,
|
|
@@ -13299,7 +13296,7 @@ function registerWelcome(program2) {
|
|
|
13299
13296
|
).option("--limit <n>", "maximum memories listed", "20").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
13300
13297
|
const root = findProjectRoot45(opts.dir);
|
|
13301
13298
|
const paths = resolveHaivePaths41(root);
|
|
13302
|
-
if (!
|
|
13299
|
+
if (!existsSync65(paths.memoriesDir)) {
|
|
13303
13300
|
ui.error(`No memories at ${paths.memoriesDir}. Run 'haive init' first.`);
|
|
13304
13301
|
process.exitCode = 1;
|
|
13305
13302
|
return;
|
|
@@ -13371,7 +13368,7 @@ function registerResolveProject(program2) {
|
|
|
13371
13368
|
}
|
|
13372
13369
|
|
|
13373
13370
|
// src/commands/runtime-journal.ts
|
|
13374
|
-
import { existsSync as
|
|
13371
|
+
import { existsSync as existsSync66 } from "fs";
|
|
13375
13372
|
import path47 from "path";
|
|
13376
13373
|
import "commander";
|
|
13377
13374
|
import {
|
|
@@ -13397,7 +13394,7 @@ function registerRuntime(program2) {
|
|
|
13397
13394
|
const root = path47.resolve(opts.dir ?? process.cwd());
|
|
13398
13395
|
const paths = resolveHaivePaths42(findProjectRoot46(root));
|
|
13399
13396
|
const limit = Math.min(500, Math.max(1, parseInt(opts.limit, 10) || 30));
|
|
13400
|
-
if (!
|
|
13397
|
+
if (!existsSync66(paths.haiveDir)) {
|
|
13401
13398
|
ui.error("No .ai/ \u2014 run `haive init` first.");
|
|
13402
13399
|
process.exitCode = 1;
|
|
13403
13400
|
return;
|
|
@@ -13412,7 +13409,7 @@ function registerRuntime(program2) {
|
|
|
13412
13409
|
}
|
|
13413
13410
|
|
|
13414
13411
|
// src/commands/memory-timeline.ts
|
|
13415
|
-
import { existsSync as
|
|
13412
|
+
import { existsSync as existsSync67 } from "fs";
|
|
13416
13413
|
import path48 from "path";
|
|
13417
13414
|
import "commander";
|
|
13418
13415
|
import {
|
|
@@ -13431,7 +13428,7 @@ function registerMemoryTimeline(memory2) {
|
|
|
13431
13428
|
}
|
|
13432
13429
|
const root = path48.resolve(opts.dir ?? process.cwd());
|
|
13433
13430
|
const paths = resolveHaivePaths43(findProjectRoot47(root));
|
|
13434
|
-
if (!
|
|
13431
|
+
if (!existsSync67(paths.memoriesDir)) {
|
|
13435
13432
|
ui.error("No memories \u2014 run `haive init`.");
|
|
13436
13433
|
process.exitCode = 1;
|
|
13437
13434
|
return;
|
|
@@ -13449,7 +13446,7 @@ function registerMemoryTimeline(memory2) {
|
|
|
13449
13446
|
}
|
|
13450
13447
|
|
|
13451
13448
|
// src/commands/memory-conflict-candidates.ts
|
|
13452
|
-
import { existsSync as
|
|
13449
|
+
import { existsSync as existsSync68 } from "fs";
|
|
13453
13450
|
import path49 from "path";
|
|
13454
13451
|
import "commander";
|
|
13455
13452
|
import {
|
|
@@ -13474,7 +13471,7 @@ function registerMemoryConflictCandidates(memory2) {
|
|
|
13474
13471
|
).option("--min-jaccard <x>", "minimum Jaccard for lexical pairs", "0.45").option("--max-pairs <n>", "cap lexical pairs", "20").option("--max-scan <n>", "max memories scanned (lexical)", "500").option("--max-topic-pairs <n>", "cap topic/status pairs", "20").action(async (opts) => {
|
|
13475
13472
|
const root = path49.resolve(opts.dir ?? process.cwd());
|
|
13476
13473
|
const paths = resolveHaivePaths44(findProjectRoot48(root));
|
|
13477
|
-
if (!
|
|
13474
|
+
if (!existsSync68(paths.memoriesDir)) {
|
|
13478
13475
|
ui.error("No memories \u2014 run `haive init`.");
|
|
13479
13476
|
process.exitCode = 1;
|
|
13480
13477
|
return;
|
|
@@ -13510,14 +13507,14 @@ function registerMemoryConflictCandidates(memory2) {
|
|
|
13510
13507
|
|
|
13511
13508
|
// src/commands/enforce.ts
|
|
13512
13509
|
import { execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
|
|
13513
|
-
import { existsSync as
|
|
13510
|
+
import { existsSync as existsSync69, statSync as statSync3 } from "fs";
|
|
13514
13511
|
import { chmod as chmod2, mkdir as mkdir19, readFile as readFile21, readdir as readdir6, rm as rm3, writeFile as writeFile33 } from "fs/promises";
|
|
13515
13512
|
import path50 from "path";
|
|
13516
13513
|
import "commander";
|
|
13517
13514
|
import {
|
|
13518
13515
|
antiPatternGateParams as antiPatternGateParams2,
|
|
13519
13516
|
findProjectRoot as findProjectRoot49,
|
|
13520
|
-
hasRecentBriefingMarker,
|
|
13517
|
+
hasRecentBriefingMarker as hasRecentBriefingMarker2,
|
|
13521
13518
|
isFreshIsoDate,
|
|
13522
13519
|
loadConfig as loadConfig13,
|
|
13523
13520
|
loadMemoriesFromDir as loadMemoriesFromDir36,
|
|
@@ -13585,14 +13582,14 @@ function registerEnforce(program2) {
|
|
|
13585
13582
|
const root = findProjectRoot49(opts.dir);
|
|
13586
13583
|
const paths = resolveHaivePaths45(root);
|
|
13587
13584
|
const cacheDir = path50.join(paths.haiveDir, ".cache");
|
|
13588
|
-
if (
|
|
13585
|
+
if (existsSync69(cacheDir)) {
|
|
13589
13586
|
if (opts.dryRun) ui.info(`would clean ${path50.relative(root, cacheDir)} (preserving .gitignore)`);
|
|
13590
13587
|
else {
|
|
13591
13588
|
const removed = await cleanupCacheDir(cacheDir);
|
|
13592
13589
|
ui.success(`cleaned ${path50.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
|
|
13593
13590
|
}
|
|
13594
13591
|
}
|
|
13595
|
-
if (
|
|
13592
|
+
if (existsSync69(paths.runtimeDir)) {
|
|
13596
13593
|
if (opts.dryRun) ui.info(`would clean ${path50.relative(root, paths.runtimeDir)} (preserving briefing markers)`);
|
|
13597
13594
|
else {
|
|
13598
13595
|
const removed = await cleanupRuntimeDir(paths.runtimeDir);
|
|
@@ -13610,7 +13607,7 @@ function registerEnforce(program2) {
|
|
|
13610
13607
|
const root = resolveRoot(opts.dir, payload);
|
|
13611
13608
|
if (!root) return;
|
|
13612
13609
|
const paths = resolveHaivePaths45(root);
|
|
13613
|
-
if (!
|
|
13610
|
+
if (!existsSync69(paths.haiveDir)) return;
|
|
13614
13611
|
await mkdir19(paths.runtimeDir, { recursive: true });
|
|
13615
13612
|
const sessionId = opts.sessionId ?? payload.session_id;
|
|
13616
13613
|
const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this hAIve-initialized project.";
|
|
@@ -13673,9 +13670,9 @@ ${briefing.project_context.content.slice(0, 1800)}`);
|
|
|
13673
13670
|
const root = resolveRoot(opts.dir, payload);
|
|
13674
13671
|
if (!root) return;
|
|
13675
13672
|
const paths = resolveHaivePaths45(root);
|
|
13676
|
-
if (!
|
|
13673
|
+
if (!existsSync69(paths.haiveDir)) return;
|
|
13677
13674
|
if (!isWriteLikeTool(payload)) return;
|
|
13678
|
-
const ok = await
|
|
13675
|
+
const ok = await hasRecentBriefingMarker2(paths, payload.session_id);
|
|
13679
13676
|
if (ok) {
|
|
13680
13677
|
const targetFiles = extractToolPaths(payload, root);
|
|
13681
13678
|
if (targetFiles.length === 0) return;
|
|
@@ -13717,7 +13714,7 @@ ${briefing.project_context.content.slice(0, 1800)}`);
|
|
|
13717
13714
|
async function runWithEnforcement(command, args, opts) {
|
|
13718
13715
|
const root = findProjectRoot49(opts.dir);
|
|
13719
13716
|
const paths = resolveHaivePaths45(root);
|
|
13720
|
-
if (!
|
|
13717
|
+
if (!existsSync69(paths.haiveDir)) {
|
|
13721
13718
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
13722
13719
|
process.exit(1);
|
|
13723
13720
|
}
|
|
@@ -13812,7 +13809,7 @@ async function writeWrapperBriefing(paths, sessionId, task) {
|
|
|
13812
13809
|
async function buildEnforcementReport(dir, stage, sessionId) {
|
|
13813
13810
|
const root = findProjectRoot49(dir);
|
|
13814
13811
|
const paths = resolveHaivePaths45(root);
|
|
13815
|
-
const initialized =
|
|
13812
|
+
const initialized = existsSync69(paths.haiveDir);
|
|
13816
13813
|
const config = initialized ? await loadConfig13(paths) : {};
|
|
13817
13814
|
if (initialized) await applyLightweightRepairs(root, paths);
|
|
13818
13815
|
const mode = config.enforcement?.mode ?? "strict";
|
|
@@ -13843,9 +13840,9 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
13843
13840
|
findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
|
|
13844
13841
|
});
|
|
13845
13842
|
}
|
|
13846
|
-
findings.push(...await inspectIntegrationVersions(root, "0.10.
|
|
13843
|
+
findings.push(...await inspectIntegrationVersions(root, "0.10.8"));
|
|
13847
13844
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
13848
|
-
const hasBriefing = await
|
|
13845
|
+
const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
|
|
13849
13846
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
|
|
13850
13847
|
severity: "error",
|
|
13851
13848
|
code: "briefing-missing",
|
|
@@ -13913,7 +13910,7 @@ function withCategories(report) {
|
|
|
13913
13910
|
};
|
|
13914
13911
|
}
|
|
13915
13912
|
async function hasRecentSessionRecap(paths) {
|
|
13916
|
-
if (!
|
|
13913
|
+
if (!existsSync69(paths.memoriesDir)) return false;
|
|
13917
13914
|
const all = await loadMemoriesFromDir36(paths.memoriesDir);
|
|
13918
13915
|
return all.some(({ memory: memory2 }) => {
|
|
13919
13916
|
const fm = memory2.frontmatter;
|
|
@@ -13922,7 +13919,7 @@ async function hasRecentSessionRecap(paths) {
|
|
|
13922
13919
|
});
|
|
13923
13920
|
}
|
|
13924
13921
|
async function verifyMemoryPolicy(paths, config) {
|
|
13925
|
-
if (!
|
|
13922
|
+
if (!existsSync69(paths.memoriesDir)) return [];
|
|
13926
13923
|
const all = await loadMemoriesFromDir36(paths.memoriesDir);
|
|
13927
13924
|
const findings = [];
|
|
13928
13925
|
const staleImportant = [];
|
|
@@ -13960,7 +13957,7 @@ async function verifyMemoryPolicy(paths, config) {
|
|
|
13960
13957
|
return findings;
|
|
13961
13958
|
}
|
|
13962
13959
|
async function verifyDecisionCoverage(paths, stage, sessionId) {
|
|
13963
|
-
if (!
|
|
13960
|
+
if (!existsSync69(paths.memoriesDir)) return [];
|
|
13964
13961
|
const changedFiles = await getChangedFiles(paths.root, stage);
|
|
13965
13962
|
if (changedFiles.length === 0) {
|
|
13966
13963
|
return [{ severity: "info", code: "decision-coverage-no-changes", message: "No changed files to match against policy memories." }];
|
|
@@ -14071,7 +14068,7 @@ async function cleanupRuntimeDir(runtimeDir) {
|
|
|
14071
14068
|
removed++;
|
|
14072
14069
|
}
|
|
14073
14070
|
await writeFile33(path50.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
|
|
14074
|
-
if (!
|
|
14071
|
+
if (!existsSync69(path50.join(runtimeDir, "README.md"))) {
|
|
14075
14072
|
await writeFile33(
|
|
14076
14073
|
path50.join(runtimeDir, "README.md"),
|
|
14077
14074
|
"# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. hAIve cleanup preserves briefing markers so enforcement state remains valid.\n",
|
|
@@ -14114,7 +14111,7 @@ async function inspectIntegrationVersions(root, expectedVersion) {
|
|
|
14114
14111
|
const findings = [];
|
|
14115
14112
|
for (const rel of files) {
|
|
14116
14113
|
const file = path50.join(root, rel);
|
|
14117
|
-
if (!
|
|
14114
|
+
if (!existsSync69(file)) continue;
|
|
14118
14115
|
const text = await readFile21(file, "utf8").catch(() => "");
|
|
14119
14116
|
for (const bin of extractAbsoluteHaiveBins2(text)) {
|
|
14120
14117
|
const version = versionForBinary2(bin);
|
|
@@ -14210,7 +14207,7 @@ function buildScore(findings, threshold = 80) {
|
|
|
14210
14207
|
}
|
|
14211
14208
|
async function installGitEnforcement(root) {
|
|
14212
14209
|
const hooksDir = path50.join(root, ".git", "hooks");
|
|
14213
|
-
if (!
|
|
14210
|
+
if (!existsSync69(path50.join(root, ".git"))) {
|
|
14214
14211
|
ui.warn("No .git directory found; git enforcement hooks skipped.");
|
|
14215
14212
|
return;
|
|
14216
14213
|
}
|
|
@@ -14233,7 +14230,7 @@ haive enforce check --stage pre-push --dir . || exit $?
|
|
|
14233
14230
|
];
|
|
14234
14231
|
for (const hook of hooks) {
|
|
14235
14232
|
const file = path50.join(hooksDir, hook.name);
|
|
14236
|
-
if (
|
|
14233
|
+
if (existsSync69(file)) {
|
|
14237
14234
|
const current = await readFile21(file, "utf8").catch(() => "");
|
|
14238
14235
|
if (current.includes(ENFORCE_HOOK_MARKER)) {
|
|
14239
14236
|
await writeFile33(file, hook.body, "utf8");
|
|
@@ -14252,7 +14249,7 @@ ${hook.body}`, "utf8");
|
|
|
14252
14249
|
async function installCiEnforcement(root) {
|
|
14253
14250
|
const workflowPath = path50.join(root, ".github", "workflows", "haive-enforcement.yml");
|
|
14254
14251
|
await mkdir19(path50.dirname(workflowPath), { recursive: true });
|
|
14255
|
-
if (
|
|
14252
|
+
if (existsSync69(workflowPath)) {
|
|
14256
14253
|
ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
|
|
14257
14254
|
return;
|
|
14258
14255
|
}
|
|
@@ -14381,7 +14378,7 @@ function normalizeToolPath(file, root) {
|
|
|
14381
14378
|
return path50.relative(root, normalized).replace(/\\/g, "/");
|
|
14382
14379
|
}
|
|
14383
14380
|
async function missingRequiredMemoriesForFiles(paths, files, sessionId) {
|
|
14384
|
-
if (!
|
|
14381
|
+
if (!existsSync69(paths.memoriesDir)) return [];
|
|
14385
14382
|
const marker = await readRecentBriefingMarker(paths, sessionId);
|
|
14386
14383
|
const consulted = new Set(marker?.memory_ids ?? []);
|
|
14387
14384
|
const policyTypes = /* @__PURE__ */ new Set(["decision", "gotcha", "architecture", "convention", "attempt"]);
|
|
@@ -14455,7 +14452,7 @@ function registerRun(program2) {
|
|
|
14455
14452
|
|
|
14456
14453
|
// src/commands/sensors.ts
|
|
14457
14454
|
import { execFile as execFile2 } from "child_process";
|
|
14458
|
-
import { existsSync as
|
|
14455
|
+
import { existsSync as existsSync70 } from "fs";
|
|
14459
14456
|
import { chmod as chmod3, mkdir as mkdir20, readFile as readFile23, writeFile as writeFile34 } from "fs/promises";
|
|
14460
14457
|
import path51 from "path";
|
|
14461
14458
|
import { promisify as promisify2 } from "util";
|
|
@@ -14538,7 +14535,7 @@ function registerSensors(program2) {
|
|
|
14538
14535
|
}
|
|
14539
14536
|
const root = findProjectRoot50(opts.dir);
|
|
14540
14537
|
const paths = resolveHaivePaths46(root);
|
|
14541
|
-
const loaded =
|
|
14538
|
+
const loaded = existsSync70(paths.memoriesDir) ? await loadMemoriesFromDir37(paths.memoriesDir) : [];
|
|
14542
14539
|
const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
|
|
14543
14540
|
if (!found) {
|
|
14544
14541
|
ui.error(`No memory found with id ${id}`);
|
|
@@ -14600,7 +14597,7 @@ async function sensorRows(paths) {
|
|
|
14600
14597
|
});
|
|
14601
14598
|
}
|
|
14602
14599
|
async function runnableSensorMemories(paths, regexOnly = true) {
|
|
14603
|
-
if (!
|
|
14600
|
+
if (!existsSync70(paths.memoriesDir)) return [];
|
|
14604
14601
|
const loaded = await loadMemoriesFromDir37(paths.memoriesDir);
|
|
14605
14602
|
return loaded.map(({ memory: memory2 }) => memory2).filter((memory2) => {
|
|
14606
14603
|
const sensor = memory2.frontmatter.sensor;
|
|
@@ -14643,7 +14640,7 @@ function shellQuote(value) {
|
|
|
14643
14640
|
|
|
14644
14641
|
// src/index.ts
|
|
14645
14642
|
var program = new Command53();
|
|
14646
|
-
program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.10.
|
|
14643
|
+
program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.10.8").option("--advanced", "show maintenance and experimental commands in help");
|
|
14647
14644
|
registerInit(program);
|
|
14648
14645
|
registerWelcome(program);
|
|
14649
14646
|
registerResolveProject(program);
|