@hiveai/cli 0.9.18 → 0.9.20
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 +158 -38
- package/dist/index.js.map +1 -1
- package/package.json +6 -12
package/dist/index.js
CHANGED
|
@@ -3294,8 +3294,8 @@ async function memList(input, ctx) {
|
|
|
3294
3294
|
return { memories };
|
|
3295
3295
|
}
|
|
3296
3296
|
var MemSaveInputSchema = {
|
|
3297
|
-
type: z4.enum(["convention", "decision", "gotcha", "architecture", "glossary", "attempt", "session_recap"]).describe(
|
|
3298
|
-
"Kind of memory being saved. Use 'attempt' for failed approaches (auto-validated). Use 'session_recap' via mem_session_end instead."
|
|
3297
|
+
type: z4.enum(["convention", "decision", "gotcha", "architecture", "glossary", "skill", "attempt", "session_recap"]).describe(
|
|
3298
|
+
"Kind of memory being saved. Use 'skill' for reusable procedures/playbooks agents should follow for recurring tasks (feedforward harness guide). Use 'attempt' for failed approaches (auto-validated). Use 'session_recap' via mem_session_end instead."
|
|
3299
3299
|
),
|
|
3300
3300
|
slug: z4.string().min(1).describe("Short human-readable identifier \u2014 becomes part of the filename"),
|
|
3301
3301
|
body: z4.string().describe("Markdown body of the memory"),
|
|
@@ -4895,10 +4895,10 @@ function classifyMemoryPriority(memory2, loaded, inputFiles, inputSymbols) {
|
|
|
4895
4895
|
);
|
|
4896
4896
|
const strongSemantic = (memory2.semantic_score ?? 0) >= 0.65;
|
|
4897
4897
|
const usefulSemantic = (memory2.semantic_score ?? 0) >= 0.35;
|
|
4898
|
-
if (fm?.requires_human_approval || directAnchor || directSymbol || memory2.type === "attempt" && (memory2.match_quality === "exact" || strongSemantic)) {
|
|
4898
|
+
if (fm?.requires_human_approval || directAnchor || directSymbol || memory2.type === "attempt" && (memory2.match_quality === "exact" || strongSemantic) || memory2.type === "skill" && (memory2.match_quality === "exact" || strongSemantic)) {
|
|
4899
4899
|
return "must_read";
|
|
4900
4900
|
}
|
|
4901
|
-
if (memory2.reasons.includes("module") || memory2.reasons.includes("domain") || memory2.match_quality === "exact" || usefulSemantic) {
|
|
4901
|
+
if (memory2.type === "skill" || memory2.reasons.includes("module") || memory2.reasons.includes("domain") || memory2.match_quality === "exact" || usefulSemantic) {
|
|
4902
4902
|
return "useful";
|
|
4903
4903
|
}
|
|
4904
4904
|
return "background";
|
|
@@ -4978,6 +4978,7 @@ function explainWhySurfaced(memory2, loaded, inputFiles, inferredModules) {
|
|
|
4978
4978
|
}
|
|
4979
4979
|
why.push(`Confidence: ${memory2.confidence}; read ${memory2.read_count} time${memory2.read_count === 1 ? "" : "s"}.`);
|
|
4980
4980
|
if (memory2.type === "attempt") why.push("Failed-approach record; read before repeating the same path.");
|
|
4981
|
+
if (memory2.type === "skill") why.push("Skill (reusable procedure/playbook) \u2014 follow the steps described when doing this type of task.");
|
|
4981
4982
|
if (memory2.status === "proposed" || memory2.status === "draft") {
|
|
4982
4983
|
why.push("Unvalidated record; use cautiously or ask a human before treating it as policy.");
|
|
4983
4984
|
}
|
|
@@ -6515,7 +6516,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
6515
6516
|
};
|
|
6516
6517
|
}
|
|
6517
6518
|
var SERVER_NAME = "haive";
|
|
6518
|
-
var SERVER_VERSION = "0.9.
|
|
6519
|
+
var SERVER_VERSION = "0.9.20";
|
|
6519
6520
|
function jsonResult(data) {
|
|
6520
6521
|
return {
|
|
6521
6522
|
content: [
|
|
@@ -6535,6 +6536,7 @@ var ENFORCEMENT_PROFILE_TOOLS = [
|
|
|
6535
6536
|
"mem_verify",
|
|
6536
6537
|
"mem_relevant_to",
|
|
6537
6538
|
"code_map",
|
|
6539
|
+
"code_search",
|
|
6538
6540
|
"pre_commit_check",
|
|
6539
6541
|
"mem_session_end"
|
|
6540
6542
|
];
|
|
@@ -8495,6 +8497,7 @@ function registerMemoryAdd(memory2) {
|
|
|
8495
8497
|
`Save a piece of knowledge as a persistent memory.
|
|
8496
8498
|
|
|
8497
8499
|
Memory types:
|
|
8500
|
+
skill \u2014 reusable procedure/playbook agents follow for a recurring task (e.g. deploy, review)
|
|
8498
8501
|
convention \u2014 how things are done here (naming, patterns, tooling)
|
|
8499
8502
|
decision \u2014 a choice made and WHY (tradeoffs, constraints)
|
|
8500
8503
|
gotcha \u2014 non-obvious behavior that surprises newcomers
|
|
@@ -8514,7 +8517,7 @@ function registerMemoryAdd(memory2) {
|
|
|
8514
8517
|
haive memory add --type convention --slug flyway-no-modify --topic flyway \\\\
|
|
8515
8518
|
--scope team --body "Never modify existing migrations. Create V{n+1}__desc.sql."
|
|
8516
8519
|
`
|
|
8517
|
-
).requiredOption("--type <type>", "convention | decision | gotcha | architecture | glossary | attempt").requiredOption("--slug <slug>", "short kebab-case identifier used in the file name").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("--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) => {
|
|
8520
|
+
).requiredOption("--type <type>", "skill | convention | decision | gotcha | architecture | glossary | attempt").requiredOption("--slug <slug>", "short kebab-case identifier used in the file name").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("--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) => {
|
|
8518
8521
|
const root = findProjectRoot13(opts.dir);
|
|
8519
8522
|
const paths = resolveHaivePaths10(root);
|
|
8520
8523
|
if (!existsSync33(paths.haiveDir)) {
|
|
@@ -8638,7 +8641,8 @@ TODO \u2014 write the memory body.
|
|
|
8638
8641
|
if (inferredTags.length > 0) {
|
|
8639
8642
|
ui.info(`auto-tagged: ${inferredTags.join(", ")} (use --no-auto-tag to disable)`);
|
|
8640
8643
|
}
|
|
8641
|
-
|
|
8644
|
+
const typeNeedsAnchor = !["skill", "glossary", "session_recap"].includes(opts.type);
|
|
8645
|
+
if (anchorPaths.length === 0 && typeNeedsAnchor) {
|
|
8642
8646
|
ui.warn(
|
|
8643
8647
|
`This memory has no anchor paths \u2014 staleness cannot be detected automatically.
|
|
8644
8648
|
Add file anchors: haive memory update ${frontmatter.id} --paths <file1,file2>`
|
|
@@ -11145,6 +11149,7 @@ import {
|
|
|
11145
11149
|
aggregateUsage as aggregateUsage2,
|
|
11146
11150
|
buildFrontmatter as buildFrontmatter11,
|
|
11147
11151
|
findProjectRoot as findProjectRoot39,
|
|
11152
|
+
loadConfig as loadConfig9,
|
|
11148
11153
|
loadMemoriesFromDir as loadMemoriesFromDir31,
|
|
11149
11154
|
memoryFilePath as memoryFilePath10,
|
|
11150
11155
|
parseSince as parseSince2,
|
|
@@ -11158,10 +11163,14 @@ var SEARCH_TOOLS = /* @__PURE__ */ new Set([
|
|
|
11158
11163
|
"mem_relevant_to",
|
|
11159
11164
|
"get_briefing"
|
|
11160
11165
|
]);
|
|
11166
|
+
var SYNTHETIC_QUERY_RE = /\b(auto-promote-marker|local enforcement smoke|cli-test-session)\b/i;
|
|
11167
|
+
function isSyntheticSuggestionQuery(query) {
|
|
11168
|
+
return SYNTHETIC_QUERY_RE.test(query);
|
|
11169
|
+
}
|
|
11161
11170
|
function registerMemorySuggest(memory2) {
|
|
11162
11171
|
memory2.command("suggest").description(
|
|
11163
|
-
"Suggest memories to create based on recurring search queries in the usage log.\n\n Use --auto-save to
|
|
11164
|
-
).option("--since <window>", "ISO date or relative (e.g. '7d', '24h')", "30d").option("--min <count>", "minimum repeat count to surface a query", "2").option("--top-n <n>", "with --auto-save, draft this many top suggestions", "3").option("--scope <scope>", "with --auto-save, scope of
|
|
11172
|
+
"Suggest memories to create based on recurring search queries in the usage log.\n\n Use --auto-save to save the top-N suggestions using the project defaults.\n In autopilot, suggestions land as validated team records; in manual mode they stay draft."
|
|
11173
|
+
).option("--since <window>", "ISO date or relative (e.g. '7d', '24h')", "30d").option("--min <count>", "minimum repeat count to surface a query", "2").option("--top-n <n>", "with --auto-save, draft this many top suggestions", "3").option("--scope <scope>", "with --auto-save, scope of saved memories (personal | team; default: config default)").option("--auto-save", "save top-N suggestions as memories on disk", false).option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
11165
11174
|
const root = findProjectRoot39(opts.dir);
|
|
11166
11175
|
const paths = resolveHaivePaths35(root);
|
|
11167
11176
|
const events = await readUsageEvents3(paths);
|
|
@@ -11182,6 +11191,7 @@ function registerMemorySuggest(memory2) {
|
|
|
11182
11191
|
if (!SEARCH_TOOLS.has(e.tool)) continue;
|
|
11183
11192
|
const key = (e.summary ?? "").toLowerCase().trim();
|
|
11184
11193
|
if (!key) continue;
|
|
11194
|
+
if (isSyntheticSuggestionQuery(key)) continue;
|
|
11185
11195
|
const prior = queries.get(key);
|
|
11186
11196
|
if (prior) {
|
|
11187
11197
|
prior.count++;
|
|
@@ -11200,11 +11210,13 @@ function registerMemorySuggest(memory2) {
|
|
|
11200
11210
|
inferred_type: inferType(v.tools, query)
|
|
11201
11211
|
})).sort((a, b) => b.count - a.count);
|
|
11202
11212
|
if (opts.autoSave) {
|
|
11213
|
+
const config = await loadConfig9(paths);
|
|
11203
11214
|
const topN = Math.max(1, parseInt(opts.topN ?? "3", 10));
|
|
11204
|
-
const scope = opts.scope === "
|
|
11215
|
+
const scope = opts.scope === "personal" || opts.scope === "team" ? opts.scope : config.defaultScope ?? "personal";
|
|
11216
|
+
const status = config.defaultStatus === "validated" ? "validated" : "draft";
|
|
11205
11217
|
const top = suggestions.slice(0, topN);
|
|
11206
11218
|
if (top.length === 0) {
|
|
11207
|
-
ui.warn(`No suggestions met --min=${minCount} \u2014 nothing to
|
|
11219
|
+
ui.warn(`No suggestions met --min=${minCount} \u2014 nothing to save.`);
|
|
11208
11220
|
return;
|
|
11209
11221
|
}
|
|
11210
11222
|
const created = [];
|
|
@@ -11227,10 +11239,10 @@ function registerMemorySuggest(memory2) {
|
|
|
11227
11239
|
scope,
|
|
11228
11240
|
tags: ["auto-suggested", ...s.tools],
|
|
11229
11241
|
paths: [],
|
|
11230
|
-
symbols: []
|
|
11242
|
+
symbols: [],
|
|
11243
|
+
status
|
|
11231
11244
|
});
|
|
11232
|
-
fm.status
|
|
11233
|
-
const body = renderTemplate(s);
|
|
11245
|
+
const body = renderTemplate(s, fm.id, status);
|
|
11234
11246
|
const file = memoryFilePath10(paths, fm.scope, fm.id, fm.module);
|
|
11235
11247
|
await mkdir18(path41.dirname(file), { recursive: true });
|
|
11236
11248
|
if (existsSync60(file)) {
|
|
@@ -11245,7 +11257,7 @@ function registerMemorySuggest(memory2) {
|
|
|
11245
11257
|
return;
|
|
11246
11258
|
}
|
|
11247
11259
|
for (const c of created) {
|
|
11248
|
-
ui.success(
|
|
11260
|
+
ui.success(`${status === "validated" ? "Saved" : "Drafted"} ${c.id} \u2192 ${c.file}`);
|
|
11249
11261
|
console.log(` ${ui.dim("from query:")} ${truncate2(c.query, 60)}`);
|
|
11250
11262
|
}
|
|
11251
11263
|
for (const s of skipped) {
|
|
@@ -11253,7 +11265,11 @@ function registerMemorySuggest(memory2) {
|
|
|
11253
11265
|
}
|
|
11254
11266
|
if (created.length > 0) {
|
|
11255
11267
|
console.log();
|
|
11256
|
-
|
|
11268
|
+
if (status === "validated") {
|
|
11269
|
+
ui.info("Autopilot defaults applied: suggestions are status=validated and active.");
|
|
11270
|
+
} else {
|
|
11271
|
+
ui.info("Drafts are status=draft \u2014 edit them, then run `haive memory promote <id>`.");
|
|
11272
|
+
}
|
|
11257
11273
|
}
|
|
11258
11274
|
return;
|
|
11259
11275
|
}
|
|
@@ -11278,7 +11294,7 @@ function registerMemorySuggest(memory2) {
|
|
|
11278
11294
|
console.log(` ${ui.dim("\u2192")} ${s.reason}`);
|
|
11279
11295
|
}
|
|
11280
11296
|
console.log();
|
|
11281
|
-
ui.info("Run with --auto-save to
|
|
11297
|
+
ui.info("Run with --auto-save to save the top-3 using the project defaults.");
|
|
11282
11298
|
});
|
|
11283
11299
|
}
|
|
11284
11300
|
function chooseReason(tools, count) {
|
|
@@ -11299,7 +11315,8 @@ function inferType(tools, query) {
|
|
|
11299
11315
|
}
|
|
11300
11316
|
return "convention";
|
|
11301
11317
|
}
|
|
11302
|
-
function renderTemplate(s) {
|
|
11318
|
+
function renderTemplate(s, id, status) {
|
|
11319
|
+
const nextStep = status === "validated" ? `This record is already active because project autopilot defaults set status=validated. Replace the template body with the actual answer when known.` : `Then run \`haive memory promote ${id}\` to move it into team review.`;
|
|
11303
11320
|
return [
|
|
11304
11321
|
`# Auto-drafted from recurring searches`,
|
|
11305
11322
|
``,
|
|
@@ -11319,7 +11336,7 @@ function renderTemplate(s) {
|
|
|
11319
11336
|
`- **Why** \u2014 the rationale or root cause`,
|
|
11320
11337
|
`- **How to apply** \u2014 what an agent should do when this comes up again`,
|
|
11321
11338
|
``,
|
|
11322
|
-
|
|
11339
|
+
nextStep
|
|
11323
11340
|
].join("\n");
|
|
11324
11341
|
}
|
|
11325
11342
|
function slugify(s) {
|
|
@@ -11455,7 +11472,7 @@ import {
|
|
|
11455
11472
|
findProjectRoot as findProjectRoot41,
|
|
11456
11473
|
getUsage as getUsage20,
|
|
11457
11474
|
loadCodeMap as loadCodeMap7,
|
|
11458
|
-
loadConfig as
|
|
11475
|
+
loadConfig as loadConfig10,
|
|
11459
11476
|
loadMemoriesFromDir as loadMemoriesFromDir33,
|
|
11460
11477
|
loadUsageIndex as loadUsageIndex26,
|
|
11461
11478
|
readUsageEvents as readUsageEvents4,
|
|
@@ -11470,6 +11487,7 @@ function registerDoctor(program2) {
|
|
|
11470
11487
|
const paths = resolveHaivePaths37(root);
|
|
11471
11488
|
const findings = [];
|
|
11472
11489
|
const repairs = [];
|
|
11490
|
+
const config = await loadConfig10(paths);
|
|
11473
11491
|
if (!existsSync63(paths.haiveDir)) {
|
|
11474
11492
|
findings.push({
|
|
11475
11493
|
severity: "error",
|
|
@@ -11548,7 +11566,7 @@ function registerDoctor(program2) {
|
|
|
11548
11566
|
});
|
|
11549
11567
|
}
|
|
11550
11568
|
const anchorless = memories.filter(
|
|
11551
|
-
(m) => m.memory.frontmatter.anchor.paths.length === 0 && m.memory.frontmatter.anchor.symbols.length === 0 && m.memory.frontmatter.type !== "session_recap" && m.memory.frontmatter.type !== "glossary"
|
|
11569
|
+
(m) => m.memory.frontmatter.anchor.paths.length === 0 && m.memory.frontmatter.anchor.symbols.length === 0 && m.memory.frontmatter.type !== "session_recap" && m.memory.frontmatter.type !== "glossary" && m.memory.frontmatter.type !== "skill"
|
|
11552
11570
|
);
|
|
11553
11571
|
if (anchorless.length / Math.max(memories.length, 1) > 0.3) {
|
|
11554
11572
|
findings.push({
|
|
@@ -11573,6 +11591,18 @@ function registerDoctor(program2) {
|
|
|
11573
11591
|
});
|
|
11574
11592
|
}
|
|
11575
11593
|
}
|
|
11594
|
+
const lintReport = await lintMemoriesAsync(root);
|
|
11595
|
+
if (lintReport.findings.length > 0) {
|
|
11596
|
+
const warnCount = lintReport.findings.filter((finding) => finding.severity === "warn").length;
|
|
11597
|
+
const errorCount = lintReport.findings.filter((finding) => finding.severity === "error").length;
|
|
11598
|
+
const severity = errorCount > 0 ? "error" : warnCount > 0 ? "warn" : "info";
|
|
11599
|
+
findings.push({
|
|
11600
|
+
severity,
|
|
11601
|
+
code: "memory-lint-findings",
|
|
11602
|
+
message: `memory lint reports ${lintReport.findings.length} finding${lintReport.findings.length === 1 ? "" : "s"} (${errorCount} error, ${warnCount} warn, ${lintReport.findings.length - errorCount - warnCount} info).`,
|
|
11603
|
+
fix: "haive memory lint --fix --apply"
|
|
11604
|
+
});
|
|
11605
|
+
}
|
|
11576
11606
|
const codeMap = await loadCodeMap7(paths);
|
|
11577
11607
|
if (!codeMap) {
|
|
11578
11608
|
findings.push({
|
|
@@ -11594,6 +11624,8 @@ function registerDoctor(program2) {
|
|
|
11594
11624
|
});
|
|
11595
11625
|
}
|
|
11596
11626
|
}
|
|
11627
|
+
findings.push(...await collectHarnessCoverageFindings(codeMap, memories));
|
|
11628
|
+
findings.push(...await collectSemanticIndexFindings(paths, config, memories.length, codeMap));
|
|
11597
11629
|
const events = await readUsageEvents4(paths);
|
|
11598
11630
|
if (events.length === 0) {
|
|
11599
11631
|
findings.push({
|
|
@@ -11607,6 +11639,7 @@ function registerDoctor(program2) {
|
|
|
11607
11639
|
if (!isSearchTool(e.tool)) continue;
|
|
11608
11640
|
const key = (e.summary ?? "").toLowerCase().trim();
|
|
11609
11641
|
if (!key) continue;
|
|
11642
|
+
if (isSyntheticSuggestionQuery(key)) continue;
|
|
11610
11643
|
queryRepeats.set(key, (queryRepeats.get(key) ?? 0) + 1);
|
|
11611
11644
|
}
|
|
11612
11645
|
const repeated = [...queryRepeats.entries()].filter(([, n]) => n >= 3);
|
|
@@ -11628,7 +11661,6 @@ function registerDoctor(program2) {
|
|
|
11628
11661
|
});
|
|
11629
11662
|
}
|
|
11630
11663
|
}
|
|
11631
|
-
const config = await loadConfig9(paths);
|
|
11632
11664
|
if (config.enforcement?.requireBriefingFirst) {
|
|
11633
11665
|
const claudeSettings = path44.join(root, ".claude", "settings.local.json");
|
|
11634
11666
|
let hasClaudeEnforcement = false;
|
|
@@ -11658,14 +11690,14 @@ function registerDoctor(program2) {
|
|
|
11658
11690
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
|
|
11659
11691
|
});
|
|
11660
11692
|
}
|
|
11661
|
-
findings.push(...await collectInstallFindings(root, "0.9.
|
|
11693
|
+
findings.push(...await collectInstallFindings(root, "0.9.20"));
|
|
11662
11694
|
try {
|
|
11663
11695
|
const legacyRaw = execSync3("haive-mcp --version", {
|
|
11664
11696
|
encoding: "utf8",
|
|
11665
11697
|
timeout: 3e3,
|
|
11666
11698
|
stdio: ["ignore", "pipe", "ignore"]
|
|
11667
11699
|
}).trim();
|
|
11668
|
-
const cliVersion = "0.9.
|
|
11700
|
+
const cliVersion = "0.9.20";
|
|
11669
11701
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
11670
11702
|
findings.push({
|
|
11671
11703
|
severity: "warn",
|
|
@@ -11713,7 +11745,7 @@ function emit(findings, opts, repairs = []) {
|
|
|
11713
11745
|
console.log(ui.bold(`hAIve doctor \u2014 ${classified.length} finding${classified.length === 1 ? "" : "s"}`));
|
|
11714
11746
|
console.log(
|
|
11715
11747
|
ui.dim(
|
|
11716
|
-
` protection=${scores.protection_score} context=${scores.context_quality_score} corpus=${scores.corpus_quality_score}
|
|
11748
|
+
` protection=${scores.protection_score} context=${scores.context_quality_score} corpus=${scores.corpus_quality_score} harness-coverage=${scores.harness_coverage_score}%`
|
|
11717
11749
|
)
|
|
11718
11750
|
);
|
|
11719
11751
|
console.log();
|
|
@@ -11722,6 +11754,7 @@ function emit(findings, opts, repairs = []) {
|
|
|
11722
11754
|
"Agent coverage",
|
|
11723
11755
|
"Context quality",
|
|
11724
11756
|
"Corpus health",
|
|
11757
|
+
"Harness coverage",
|
|
11725
11758
|
"Index health",
|
|
11726
11759
|
"Next actions"
|
|
11727
11760
|
];
|
|
@@ -11759,6 +11792,7 @@ function emit(findings, opts, repairs = []) {
|
|
|
11759
11792
|
function sectionForFinding(finding) {
|
|
11760
11793
|
if (finding.code.includes("haive") || finding.code.includes("integration") || finding.code.includes("claude") || finding.code.includes("autopilot")) return "Agent coverage";
|
|
11761
11794
|
if (finding.code.includes("context") || finding.code.includes("briefing") || finding.code.includes("search")) return "Context quality";
|
|
11795
|
+
if (finding.code.includes("harness-coverage")) return "Harness coverage";
|
|
11762
11796
|
if (finding.code.includes("code-map") || finding.code.includes("index")) return "Index health";
|
|
11763
11797
|
if (finding.code.includes("memory") || finding.code.includes("anchor") || finding.code.includes("pending") || finding.code.includes("decay")) return "Corpus health";
|
|
11764
11798
|
if (finding.severity === "error") return "Protection";
|
|
@@ -11774,10 +11808,13 @@ function computeDoctorScores(findings) {
|
|
|
11774
11808
|
}, 0);
|
|
11775
11809
|
return Math.max(0, 100 - penalty);
|
|
11776
11810
|
};
|
|
11811
|
+
const coverageFinding = findings.find((f) => f.code === "harness-coverage");
|
|
11812
|
+
const harnessCoverageScore = coverageFinding ? parseInt((coverageFinding.message.match(/\((\d+)%\)/) ?? [])[1] ?? "0", 10) : 0;
|
|
11777
11813
|
return {
|
|
11778
11814
|
protection_score: scoreFor(["Protection", "Agent coverage"]),
|
|
11779
11815
|
context_quality_score: scoreFor(["Context quality", "Index health"]),
|
|
11780
|
-
corpus_quality_score: scoreFor(["Corpus health"])
|
|
11816
|
+
corpus_quality_score: scoreFor(["Corpus health"]),
|
|
11817
|
+
harness_coverage_score: harnessCoverageScore
|
|
11781
11818
|
};
|
|
11782
11819
|
}
|
|
11783
11820
|
function groupBySection(findings) {
|
|
@@ -11786,6 +11823,7 @@ function groupBySection(findings) {
|
|
|
11786
11823
|
"Agent coverage": [],
|
|
11787
11824
|
"Context quality": [],
|
|
11788
11825
|
"Corpus health": [],
|
|
11826
|
+
"Harness coverage": [],
|
|
11789
11827
|
"Index health": [],
|
|
11790
11828
|
"Next actions": []
|
|
11791
11829
|
};
|
|
@@ -11795,6 +11833,87 @@ function groupBySection(findings) {
|
|
|
11795
11833
|
function nextActions(findings) {
|
|
11796
11834
|
return [...new Set(findings.flatMap((finding) => finding.fix ? finding.fix.split("\n") : []))].filter(Boolean);
|
|
11797
11835
|
}
|
|
11836
|
+
async function collectHarnessCoverageFindings(codeMap, memories) {
|
|
11837
|
+
if (!codeMap) return [];
|
|
11838
|
+
const codeFiles = Object.keys(codeMap.files);
|
|
11839
|
+
const total = codeFiles.length;
|
|
11840
|
+
if (total === 0) return [];
|
|
11841
|
+
const validatedWithAnchors = memories.filter(
|
|
11842
|
+
(m) => (m.memory.frontmatter.status === "validated" || m.memory.frontmatter.status === "proposed") && m.memory.frontmatter.anchor.paths.length > 0
|
|
11843
|
+
);
|
|
11844
|
+
const coveredFiles = /* @__PURE__ */ new Set();
|
|
11845
|
+
for (const m of validatedWithAnchors) {
|
|
11846
|
+
for (const anchorPath of m.memory.frontmatter.anchor.paths) {
|
|
11847
|
+
const normalized = anchorPath.replace(/^\/+/, "");
|
|
11848
|
+
for (const codeFile of codeFiles) {
|
|
11849
|
+
if (codeFile === normalized || codeFile.startsWith(normalized + "/") || normalized.startsWith(codeFile + "/")) {
|
|
11850
|
+
coveredFiles.add(codeFile);
|
|
11851
|
+
}
|
|
11852
|
+
}
|
|
11853
|
+
}
|
|
11854
|
+
}
|
|
11855
|
+
const covered = coveredFiles.size;
|
|
11856
|
+
const pct = Math.round(covered / total * 100);
|
|
11857
|
+
const findings = [];
|
|
11858
|
+
findings.push({
|
|
11859
|
+
severity: pct < 10 && total > 10 ? "info" : "info",
|
|
11860
|
+
code: "harness-coverage",
|
|
11861
|
+
message: `${covered}/${total} code-map files have validated memory anchors (${pct}%). ` + (pct < 10 && total > 10 ? "Low coverage \u2014 add memory anchors on key modules to improve harness enforcement." : pct < 30 ? "Partial coverage \u2014 consider anchoring critical modules and patterns." : "Good harness coverage."),
|
|
11862
|
+
fix: pct < 10 && total > 10 ? "haive memory add --type gotcha|convention|architecture --paths <key-file> --scope team" : void 0,
|
|
11863
|
+
section: "Harness coverage"
|
|
11864
|
+
});
|
|
11865
|
+
return findings;
|
|
11866
|
+
}
|
|
11867
|
+
async function collectSemanticIndexFindings(paths, config, memoryCount, codeMap) {
|
|
11868
|
+
const findings = [];
|
|
11869
|
+
const autoWantsCodeSearch = Boolean(config.autopilot || config.autoRepair?.codeSearch);
|
|
11870
|
+
let mod;
|
|
11871
|
+
try {
|
|
11872
|
+
mod = await import("@hiveai/embeddings");
|
|
11873
|
+
} catch {
|
|
11874
|
+
findings.push({
|
|
11875
|
+
severity: autoWantsCodeSearch ? "warn" : "info",
|
|
11876
|
+
code: "embeddings-unavailable",
|
|
11877
|
+
message: "@hiveai/embeddings is not available, so get_briefing falls back to lexical ranking and code_search cannot run.",
|
|
11878
|
+
fix: "npm install -g @hiveai/cli@latest\nhaive embeddings status",
|
|
11879
|
+
section: "Index health"
|
|
11880
|
+
});
|
|
11881
|
+
return findings;
|
|
11882
|
+
}
|
|
11883
|
+
if (memoryCount > 0) {
|
|
11884
|
+
const stat2 = await mod.indexStat(paths).catch(() => ({ exists: false, count: 0 }));
|
|
11885
|
+
if (!stat2.exists || stat2.count === 0) {
|
|
11886
|
+
findings.push({
|
|
11887
|
+
severity: "warn",
|
|
11888
|
+
code: "semantic-memory-index-missing",
|
|
11889
|
+
message: "Memory embeddings index is missing or empty; get_briefing will report literal_fallback instead of semantic ranking.",
|
|
11890
|
+
fix: "haive embeddings index",
|
|
11891
|
+
section: "Index health"
|
|
11892
|
+
});
|
|
11893
|
+
}
|
|
11894
|
+
}
|
|
11895
|
+
if (autoWantsCodeSearch || codeMap) {
|
|
11896
|
+
const codeIndex = await mod.loadCodeIndex(paths).catch(() => null);
|
|
11897
|
+
if (!codeIndex || codeIndex.entries.length === 0) {
|
|
11898
|
+
findings.push({
|
|
11899
|
+
severity: autoWantsCodeSearch ? "warn" : "info",
|
|
11900
|
+
code: "code-search-index-missing",
|
|
11901
|
+
message: "Code-search embeddings index is missing or empty; MCP code_search is unavailable until it is built.",
|
|
11902
|
+
fix: "haive index code-search",
|
|
11903
|
+
section: "Index health"
|
|
11904
|
+
});
|
|
11905
|
+
} else if (codeMap && codeIndex.source_generated_at !== codeMap.generated_at) {
|
|
11906
|
+
findings.push({
|
|
11907
|
+
severity: "info",
|
|
11908
|
+
code: "code-search-index-outdated",
|
|
11909
|
+
message: "Code-search embeddings index was built from an older code-map; semantic code search may miss recent symbols.",
|
|
11910
|
+
fix: "haive index code-search",
|
|
11911
|
+
section: "Index health"
|
|
11912
|
+
});
|
|
11913
|
+
}
|
|
11914
|
+
}
|
|
11915
|
+
return findings;
|
|
11916
|
+
}
|
|
11798
11917
|
function isSearchTool(name) {
|
|
11799
11918
|
return ["mem_search", "code_search", "mem_relevant_to", "get_briefing"].includes(name);
|
|
11800
11919
|
}
|
|
@@ -12149,12 +12268,13 @@ import {
|
|
|
12149
12268
|
resolveHaivePaths as resolveHaivePaths40
|
|
12150
12269
|
} from "@hiveai/core";
|
|
12151
12270
|
var TYPE_RANK = {
|
|
12152
|
-
|
|
12153
|
-
|
|
12154
|
-
|
|
12155
|
-
|
|
12156
|
-
|
|
12157
|
-
|
|
12271
|
+
skill: 0,
|
|
12272
|
+
decision: 1,
|
|
12273
|
+
architecture: 2,
|
|
12274
|
+
convention: 3,
|
|
12275
|
+
glossary: 4,
|
|
12276
|
+
gotcha: 5,
|
|
12277
|
+
attempt: 6
|
|
12158
12278
|
};
|
|
12159
12279
|
function registerWelcome(program2) {
|
|
12160
12280
|
program2.command("welcome").description(
|
|
@@ -12381,7 +12501,7 @@ import {
|
|
|
12381
12501
|
findProjectRoot as findProjectRoot48,
|
|
12382
12502
|
hasRecentBriefingMarker,
|
|
12383
12503
|
isFreshIsoDate,
|
|
12384
|
-
loadConfig as
|
|
12504
|
+
loadConfig as loadConfig11,
|
|
12385
12505
|
loadMemoriesFromDir as loadMemoriesFromDir36,
|
|
12386
12506
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths6,
|
|
12387
12507
|
readRecentBriefingMarker,
|
|
@@ -12402,7 +12522,7 @@ function registerEnforce(program2) {
|
|
|
12402
12522
|
const root = findProjectRoot48(opts.dir);
|
|
12403
12523
|
const paths = resolveHaivePaths44(root);
|
|
12404
12524
|
await mkdir19(paths.haiveDir, { recursive: true });
|
|
12405
|
-
const current = await
|
|
12525
|
+
const current = await loadConfig11(paths);
|
|
12406
12526
|
await saveConfig4(paths, {
|
|
12407
12527
|
...current,
|
|
12408
12528
|
enforcement: {
|
|
@@ -12651,7 +12771,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
12651
12771
|
const root = findProjectRoot48(dir);
|
|
12652
12772
|
const paths = resolveHaivePaths44(root);
|
|
12653
12773
|
const initialized = existsSync69(paths.haiveDir);
|
|
12654
|
-
const config = initialized ? await
|
|
12774
|
+
const config = initialized ? await loadConfig11(paths) : {};
|
|
12655
12775
|
const mode = config.enforcement?.mode ?? "strict";
|
|
12656
12776
|
const findings = [];
|
|
12657
12777
|
if (!initialized) {
|
|
@@ -12680,7 +12800,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
12680
12800
|
findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
|
|
12681
12801
|
});
|
|
12682
12802
|
}
|
|
12683
|
-
findings.push(...await inspectIntegrationVersions(root, "0.9.
|
|
12803
|
+
findings.push(...await inspectIntegrationVersions(root, "0.9.20"));
|
|
12684
12804
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
12685
12805
|
const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
|
|
12686
12806
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
|
|
@@ -13168,7 +13288,7 @@ function registerRun(program2) {
|
|
|
13168
13288
|
|
|
13169
13289
|
// src/index.ts
|
|
13170
13290
|
var program = new Command51();
|
|
13171
|
-
program.name("haive").description("hAIve \u2014
|
|
13291
|
+
program.name("haive").description("hAIve \u2014 the memory and enforcement layer of your agent harness").version("0.9.20").option("--advanced", "show maintenance and experimental commands in help");
|
|
13172
13292
|
registerInit(program);
|
|
13173
13293
|
registerWelcome(program);
|
|
13174
13294
|
registerResolveProject(program);
|
|
@@ -13265,7 +13385,7 @@ function applySurfaceVisibility(root) {
|
|
|
13265
13385
|
"after",
|
|
13266
13386
|
[
|
|
13267
13387
|
"",
|
|
13268
|
-
"Default help shows the core
|
|
13388
|
+
"Default help shows the core harness workflow: init, doctor, agent setup, briefing, enforcement,",
|
|
13269
13389
|
"sync, session recaps, and high-signal memory commands.",
|
|
13270
13390
|
"Run `haive --advanced --help` or set HAIVE_SHOW_ADVANCED=1 to show maintenance and experimental commands."
|
|
13271
13391
|
].join("\n")
|