@hiveai/cli 0.9.19 → 0.9.21
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 +153 -68
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
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.21";
|
|
6519
6520
|
function jsonResult(data) {
|
|
6520
6521
|
return {
|
|
6521
6522
|
content: [
|
|
@@ -8496,6 +8497,7 @@ function registerMemoryAdd(memory2) {
|
|
|
8496
8497
|
`Save a piece of knowledge as a persistent memory.
|
|
8497
8498
|
|
|
8498
8499
|
Memory types:
|
|
8500
|
+
skill \u2014 reusable procedure/playbook agents follow for a recurring task (e.g. deploy, review)
|
|
8499
8501
|
convention \u2014 how things are done here (naming, patterns, tooling)
|
|
8500
8502
|
decision \u2014 a choice made and WHY (tradeoffs, constraints)
|
|
8501
8503
|
gotcha \u2014 non-obvious behavior that surprises newcomers
|
|
@@ -8515,7 +8517,7 @@ function registerMemoryAdd(memory2) {
|
|
|
8515
8517
|
haive memory add --type convention --slug flyway-no-modify --topic flyway \\\\
|
|
8516
8518
|
--scope team --body "Never modify existing migrations. Create V{n+1}__desc.sql."
|
|
8517
8519
|
`
|
|
8518
|
-
).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) => {
|
|
8519
8521
|
const root = findProjectRoot13(opts.dir);
|
|
8520
8522
|
const paths = resolveHaivePaths10(root);
|
|
8521
8523
|
if (!existsSync33(paths.haiveDir)) {
|
|
@@ -8639,7 +8641,8 @@ TODO \u2014 write the memory body.
|
|
|
8639
8641
|
if (inferredTags.length > 0) {
|
|
8640
8642
|
ui.info(`auto-tagged: ${inferredTags.join(", ")} (use --no-auto-tag to disable)`);
|
|
8641
8643
|
}
|
|
8642
|
-
|
|
8644
|
+
const typeNeedsAnchor = !["skill", "glossary", "session_recap"].includes(opts.type);
|
|
8645
|
+
if (anchorPaths.length === 0 && typeNeedsAnchor) {
|
|
8643
8646
|
ui.warn(
|
|
8644
8647
|
`This memory has no anchor paths \u2014 staleness cannot be detected automatically.
|
|
8645
8648
|
Add file anchors: haive memory update ${frontmatter.id} --paths <file1,file2>`
|
|
@@ -8715,6 +8718,8 @@ function registerMemoryList(memory2) {
|
|
|
8715
8718
|
console.log(
|
|
8716
8719
|
`${ui.bold(fm.id)} ${ui.dim(fm.scope)}/${ui.dim(fm.type)} ${statusBadge}${moduleStr}${tagStr}`
|
|
8717
8720
|
);
|
|
8721
|
+
const title = mem.body.match(/^#\s+(.+)$/m)?.[1]?.trim();
|
|
8722
|
+
if (title && title !== fm.id) console.log(` ${title}`);
|
|
8718
8723
|
console.log(` ${ui.dim(path17.relative(root, filePath))}`);
|
|
8719
8724
|
}
|
|
8720
8725
|
console.log(ui.dim(`
|
|
@@ -8880,7 +8885,7 @@ function registerMemoryApprove(memory2) {
|
|
|
8880
8885
|
}
|
|
8881
8886
|
|
|
8882
8887
|
// src/commands/memory-update.ts
|
|
8883
|
-
import { writeFile as writeFile19 } from "fs/promises";
|
|
8888
|
+
import { readFile as readFile11, writeFile as writeFile19 } from "fs/promises";
|
|
8884
8889
|
import { existsSync as existsSync37 } from "fs";
|
|
8885
8890
|
import path20 from "path";
|
|
8886
8891
|
import "commander";
|
|
@@ -8890,7 +8895,7 @@ import {
|
|
|
8890
8895
|
serializeMemory as serializeMemory16
|
|
8891
8896
|
} from "@hiveai/core";
|
|
8892
8897
|
function registerMemoryUpdate(memory2) {
|
|
8893
|
-
memory2.command("update <id>").description("Update body, tags, or anchor of an existing memory (preserves id and usage history)").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("--tags <csv>", "new tags, comma-separated \u2014 fully replaces existing tags").option("--paths <csv>", "new anchor paths, comma-separated").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) => {
|
|
8898
|
+
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("--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) => {
|
|
8894
8899
|
const root = findProjectRoot17(opts.dir);
|
|
8895
8900
|
const paths = resolveHaivePaths14(root);
|
|
8896
8901
|
if (!existsSync37(paths.memoriesDir)) {
|
|
@@ -8923,19 +8928,34 @@ function registerMemoryUpdate(memory2) {
|
|
|
8923
8928
|
const newFrontmatter = {
|
|
8924
8929
|
...frontmatter,
|
|
8925
8930
|
anchor: newAnchor,
|
|
8931
|
+
...opts.type !== void 0 ? { type: opts.type } : {},
|
|
8926
8932
|
...opts.tags !== void 0 ? { tags: parseCsv3(opts.tags) } : {},
|
|
8927
8933
|
...opts.domain !== void 0 ? { domain: opts.domain } : {},
|
|
8928
8934
|
...opts.author !== void 0 ? { author: opts.author } : {}
|
|
8929
8935
|
};
|
|
8936
|
+
if (opts.type !== void 0) updated.push("type");
|
|
8930
8937
|
if (opts.tags !== void 0) updated.push("tags");
|
|
8931
8938
|
if (opts.domain !== void 0) updated.push("domain");
|
|
8932
8939
|
if (opts.author !== void 0) updated.push("author");
|
|
8933
|
-
let newBody
|
|
8940
|
+
let newBody;
|
|
8941
|
+
if (opts.bodyFile !== void 0) {
|
|
8942
|
+
if (!existsSync37(opts.bodyFile)) {
|
|
8943
|
+
ui.error(`--body-file not found: ${opts.bodyFile}`);
|
|
8944
|
+
process.exitCode = 1;
|
|
8945
|
+
return;
|
|
8946
|
+
}
|
|
8947
|
+
newBody = await readFile11(opts.bodyFile, "utf8");
|
|
8948
|
+
updated.push("body");
|
|
8949
|
+
} else if (opts.body !== void 0) {
|
|
8950
|
+
newBody = opts.body;
|
|
8951
|
+
updated.push("body");
|
|
8952
|
+
} else {
|
|
8953
|
+
newBody = body;
|
|
8954
|
+
}
|
|
8934
8955
|
if (opts.title !== void 0) {
|
|
8935
8956
|
newBody = replaceFirstHeading(newBody, opts.title);
|
|
8936
8957
|
updated.push("title");
|
|
8937
8958
|
}
|
|
8938
|
-
if (opts.body !== void 0) updated.push("body");
|
|
8939
8959
|
if (updated.length === 0) {
|
|
8940
8960
|
ui.warn("Nothing to update \u2014 provide at least one option.");
|
|
8941
8961
|
return;
|
|
@@ -9029,7 +9049,7 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
9029
9049
|
// src/commands/memory-edit.ts
|
|
9030
9050
|
import { spawn as spawn3 } from "child_process";
|
|
9031
9051
|
import { existsSync as existsSync39 } from "fs";
|
|
9032
|
-
import { readFile as
|
|
9052
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
9033
9053
|
import path23 from "path";
|
|
9034
9054
|
import "commander";
|
|
9035
9055
|
import {
|
|
@@ -9060,7 +9080,7 @@ function registerMemoryEdit(memory2) {
|
|
|
9060
9080
|
ui.warn(`Editor exited with status ${code}.`);
|
|
9061
9081
|
}
|
|
9062
9082
|
try {
|
|
9063
|
-
const fresh = await
|
|
9083
|
+
const fresh = await readFile12(found.filePath, "utf8");
|
|
9064
9084
|
parseMemory(fresh);
|
|
9065
9085
|
ui.success("Memory still parses cleanly.");
|
|
9066
9086
|
} catch (err) {
|
|
@@ -9286,7 +9306,7 @@ function registerMemoryTried(memory2) {
|
|
|
9286
9306
|
--instead "use static import in the entry file" \\\\
|
|
9287
9307
|
--paths packages/cli/src/index.ts
|
|
9288
9308
|
`
|
|
9289
|
-
).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
|
|
9309
|
+
).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("--author <author>", "author email or handle").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9290
9310
|
const root = findProjectRoot22(opts.dir);
|
|
9291
9311
|
const paths = resolveHaivePaths19(root);
|
|
9292
9312
|
if (!existsSync43(paths.haiveDir)) {
|
|
@@ -9339,7 +9359,7 @@ import {
|
|
|
9339
9359
|
resolveHaivePaths as resolveHaivePaths20
|
|
9340
9360
|
} from "@hiveai/core";
|
|
9341
9361
|
function registerMemoryPending(memory2) {
|
|
9342
|
-
memory2.command("pending").description("List
|
|
9362
|
+
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) => {
|
|
9343
9363
|
const root = findProjectRoot23(opts.dir);
|
|
9344
9364
|
const paths = resolveHaivePaths20(root);
|
|
9345
9365
|
if (!existsSync44(paths.memoriesDir)) {
|
|
@@ -9349,30 +9369,53 @@ function registerMemoryPending(memory2) {
|
|
|
9349
9369
|
}
|
|
9350
9370
|
const all = await loadMemoriesFromDir26(paths.memoriesDir);
|
|
9351
9371
|
const usage = await loadUsageIndex17(paths);
|
|
9352
|
-
const
|
|
9353
|
-
if (mem.frontmatter.status !== "proposed") return false;
|
|
9372
|
+
const filterFn = ({ memory: mem }) => {
|
|
9373
|
+
if (mem.frontmatter.status !== "proposed" && mem.frontmatter.status !== "draft") return false;
|
|
9354
9374
|
if (opts.scope && mem.frontmatter.scope !== opts.scope) return false;
|
|
9355
9375
|
return true;
|
|
9356
|
-
}
|
|
9357
|
-
|
|
9358
|
-
|
|
9376
|
+
};
|
|
9377
|
+
const pending = all.filter(filterFn);
|
|
9378
|
+
if (pending.length === 0) {
|
|
9379
|
+
ui.info("No draft or proposed memories awaiting review.");
|
|
9380
|
+
ui.info("Drafts are created by `haive memory add` without `--status validated`.");
|
|
9359
9381
|
return;
|
|
9360
9382
|
}
|
|
9361
|
-
|
|
9383
|
+
pending.sort(
|
|
9362
9384
|
(a, b) => getUsage15(usage, b.memory.frontmatter.id).read_count - getUsage15(usage, a.memory.frontmatter.id).read_count
|
|
9363
9385
|
);
|
|
9364
9386
|
const now = Date.now();
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
|
|
9369
|
-
const
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
|
|
9387
|
+
const drafts = pending.filter((m) => m.memory.frontmatter.status === "draft");
|
|
9388
|
+
const proposed = pending.filter((m) => m.memory.frontmatter.status === "proposed");
|
|
9389
|
+
if (proposed.length > 0) {
|
|
9390
|
+
console.log(ui.bold(`Proposed (${proposed.length}) \u2014 awaiting team validation`));
|
|
9391
|
+
for (const { memory: mem, filePath } of proposed) {
|
|
9392
|
+
const fm = mem.frontmatter;
|
|
9393
|
+
const u = getUsage15(usage, fm.id);
|
|
9394
|
+
const ageDays = Math.floor((now - new Date(fm.created_at).getTime()) / 864e5);
|
|
9395
|
+
const ageStr = ageDays === 0 ? "today" : `${ageDays}d`;
|
|
9396
|
+
console.log(
|
|
9397
|
+
` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`
|
|
9398
|
+
);
|
|
9399
|
+
console.log(` ${ui.dim(path27.relative(root, filePath))}`);
|
|
9400
|
+
}
|
|
9401
|
+
if (proposed.length > 0) console.log(ui.dim(` \u2192 haive memory approve <id> or haive memory auto-promote`));
|
|
9402
|
+
console.log();
|
|
9403
|
+
}
|
|
9404
|
+
if (drafts.length > 0) {
|
|
9405
|
+
console.log(ui.bold(`Draft (${drafts.length}) \u2014 created but not yet activated`));
|
|
9406
|
+
for (const { memory: mem, filePath } of drafts) {
|
|
9407
|
+
const fm = mem.frontmatter;
|
|
9408
|
+
const u = getUsage15(usage, fm.id);
|
|
9409
|
+
const ageDays = Math.floor((now - new Date(fm.created_at).getTime()) / 864e5);
|
|
9410
|
+
const ageStr = ageDays === 0 ? "today" : `${ageDays}d`;
|
|
9411
|
+
console.log(
|
|
9412
|
+
` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`
|
|
9413
|
+
);
|
|
9414
|
+
console.log(` ${ui.dim(path27.relative(root, filePath))}`);
|
|
9415
|
+
}
|
|
9416
|
+
console.log(ui.dim(` \u2192 haive memory approve <id> (activate) | haive memory promote <id> (share with team)`));
|
|
9374
9417
|
}
|
|
9375
|
-
ui.info(`${proposed.length}
|
|
9418
|
+
ui.info(`${pending.length} total pending (${proposed.length} proposed \xB7 ${drafts.length} draft)`);
|
|
9376
9419
|
});
|
|
9377
9420
|
}
|
|
9378
9421
|
|
|
@@ -9555,7 +9598,7 @@ function registerMemoryRm(memory2) {
|
|
|
9555
9598
|
|
|
9556
9599
|
// src/commands/memory-show.ts
|
|
9557
9600
|
import { existsSync as existsSync48 } from "fs";
|
|
9558
|
-
import { readFile as
|
|
9601
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
9559
9602
|
import path30 from "path";
|
|
9560
9603
|
import "commander";
|
|
9561
9604
|
import {
|
|
@@ -9582,7 +9625,7 @@ function registerMemoryShow(memory2) {
|
|
|
9582
9625
|
return;
|
|
9583
9626
|
}
|
|
9584
9627
|
if (opts.raw) {
|
|
9585
|
-
console.log(await
|
|
9628
|
+
console.log(await readFile13(found.filePath, "utf8"));
|
|
9586
9629
|
return;
|
|
9587
9630
|
}
|
|
9588
9631
|
const fm = found.memory.frontmatter;
|
|
@@ -9760,7 +9803,7 @@ function applyVerification2(mem, result) {
|
|
|
9760
9803
|
}
|
|
9761
9804
|
|
|
9762
9805
|
// src/commands/memory-import.ts
|
|
9763
|
-
import { readFile as
|
|
9806
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
9764
9807
|
import { existsSync as existsSync51 } from "fs";
|
|
9765
9808
|
import "commander";
|
|
9766
9809
|
import {
|
|
@@ -9783,7 +9826,7 @@ function registerMemoryImport(memory2) {
|
|
|
9783
9826
|
process.exitCode = 1;
|
|
9784
9827
|
return;
|
|
9785
9828
|
}
|
|
9786
|
-
const content = await
|
|
9829
|
+
const content = await readFile14(opts.from, "utf8");
|
|
9787
9830
|
const scope = opts.scope ?? "team";
|
|
9788
9831
|
ui.info(`Preparing import from: ${opts.from} (scope=${scope})`);
|
|
9789
9832
|
ui.info(`Content length: ${content.length} chars`);
|
|
@@ -9812,7 +9855,7 @@ function registerMemoryImport(memory2) {
|
|
|
9812
9855
|
|
|
9813
9856
|
// src/commands/memory-import-changelog.ts
|
|
9814
9857
|
import { existsSync as existsSync53 } from "fs";
|
|
9815
|
-
import { readFile as
|
|
9858
|
+
import { readFile as readFile15, mkdir as mkdir14, writeFile as writeFile25 } from "fs/promises";
|
|
9816
9859
|
import path34 from "path";
|
|
9817
9860
|
import "commander";
|
|
9818
9861
|
import {
|
|
@@ -9891,7 +9934,7 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
9891
9934
|
process.exitCode = 1;
|
|
9892
9935
|
return;
|
|
9893
9936
|
}
|
|
9894
|
-
const content = await
|
|
9937
|
+
const content = await readFile15(changelogPath, "utf8");
|
|
9895
9938
|
let entries = parseChangelog(content);
|
|
9896
9939
|
if (entries.length === 0) {
|
|
9897
9940
|
ui.warn("No breaking changes, deprecations, or removals found in the CHANGELOG.");
|
|
@@ -10080,7 +10123,7 @@ function registerMemoryDigest(program2) {
|
|
|
10080
10123
|
}
|
|
10081
10124
|
|
|
10082
10125
|
// src/commands/session-end.ts
|
|
10083
|
-
import { writeFile as writeFile27, mkdir as mkdir15, readFile as
|
|
10126
|
+
import { writeFile as writeFile27, mkdir as mkdir15, readFile as readFile16, rm as rm2 } from "fs/promises";
|
|
10084
10127
|
import { existsSync as existsSync55 } from "fs";
|
|
10085
10128
|
import path36 from "path";
|
|
10086
10129
|
import "commander";
|
|
@@ -10095,7 +10138,7 @@ import {
|
|
|
10095
10138
|
async function buildAutoRecap(paths) {
|
|
10096
10139
|
const obsFile = path36.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
10097
10140
|
if (!existsSync55(obsFile)) return null;
|
|
10098
|
-
const raw = await
|
|
10141
|
+
const raw = await readFile16(obsFile, "utf8").catch(() => "");
|
|
10099
10142
|
if (!raw.trim()) return null;
|
|
10100
10143
|
const lines = raw.split("\n").filter(Boolean);
|
|
10101
10144
|
const obs = [];
|
|
@@ -10440,7 +10483,7 @@ function detectFormat(filePath) {
|
|
|
10440
10483
|
|
|
10441
10484
|
// src/commands/hub.ts
|
|
10442
10485
|
import { existsSync as existsSync57 } from "fs";
|
|
10443
|
-
import { mkdir as mkdir16, readFile as
|
|
10486
|
+
import { mkdir as mkdir16, readFile as readFile17, writeFile as writeFile28, copyFile } from "fs/promises";
|
|
10444
10487
|
import path38 from "path";
|
|
10445
10488
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
10446
10489
|
import "commander";
|
|
@@ -10635,7 +10678,7 @@ Next steps:
|
|
|
10635
10678
|
for (const file of sourceFiles) {
|
|
10636
10679
|
const srcPath = path38.join(sourceDir, file);
|
|
10637
10680
|
const destPath = path38.join(destDir, file);
|
|
10638
|
-
const fileContent = await
|
|
10681
|
+
const fileContent = await readFile17(srcPath, "utf8");
|
|
10639
10682
|
const alreadyTagged = fileContent.includes(`cross-repo:${sourceName}`);
|
|
10640
10683
|
if (!alreadyTagged) {
|
|
10641
10684
|
await copyFile(srcPath, destPath);
|
|
@@ -10687,7 +10730,7 @@ Next steps:
|
|
|
10687
10730
|
if (outgoing.length > 0) {
|
|
10688
10731
|
console.log(ui.dim(" Run `haive hub push` to publish them to the hub."));
|
|
10689
10732
|
}
|
|
10690
|
-
void
|
|
10733
|
+
void readFile17;
|
|
10691
10734
|
void writeFile28;
|
|
10692
10735
|
void saveConfig3;
|
|
10693
10736
|
});
|
|
@@ -10994,7 +11037,7 @@ function summarize(name, t0, payload, notes) {
|
|
|
10994
11037
|
|
|
10995
11038
|
// src/commands/benchmark.ts
|
|
10996
11039
|
import { existsSync as existsSync59 } from "fs";
|
|
10997
|
-
import { readdir as readdir5, readFile as
|
|
11040
|
+
import { readdir as readdir5, readFile as readFile18, writeFile as writeFile30 } from "fs/promises";
|
|
10998
11041
|
import path40 from "path";
|
|
10999
11042
|
import "commander";
|
|
11000
11043
|
import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot38 } from "@hiveai/core";
|
|
@@ -11049,7 +11092,7 @@ async function collectRows(root) {
|
|
|
11049
11092
|
const fixtureDir = path40.join(root, entry.name);
|
|
11050
11093
|
const reportFile = path40.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
|
|
11051
11094
|
if (!existsSync59(reportFile)) continue;
|
|
11052
|
-
const report = await
|
|
11095
|
+
const report = await readFile18(reportFile, "utf8");
|
|
11053
11096
|
rows.push(parseAgentReport(entry.name, report));
|
|
11054
11097
|
}
|
|
11055
11098
|
return rows.sort((a, b) => a.fixture.localeCompare(b.fixture));
|
|
@@ -11460,7 +11503,7 @@ function parseDays(input) {
|
|
|
11460
11503
|
|
|
11461
11504
|
// src/commands/doctor.ts
|
|
11462
11505
|
import { existsSync as existsSync63 } from "fs";
|
|
11463
|
-
import { readFile as
|
|
11506
|
+
import { readFile as readFile19, stat } from "fs/promises";
|
|
11464
11507
|
import path44 from "path";
|
|
11465
11508
|
import { execFileSync, execSync as execSync3 } from "child_process";
|
|
11466
11509
|
import "commander";
|
|
@@ -11513,8 +11556,8 @@ function registerDoctor(program2) {
|
|
|
11513
11556
|
fix: "haive init"
|
|
11514
11557
|
});
|
|
11515
11558
|
} else {
|
|
11516
|
-
const { readFile:
|
|
11517
|
-
const content = await
|
|
11559
|
+
const { readFile: readFile21 } = await import("fs/promises");
|
|
11560
|
+
const content = await readFile21(paths.projectContext, "utf8");
|
|
11518
11561
|
const isTemplate = content.includes("TODO \u2014 high-level overview") || content.includes("Generated by `haive init`");
|
|
11519
11562
|
if (isTemplate) {
|
|
11520
11563
|
findings.push({
|
|
@@ -11563,7 +11606,7 @@ function registerDoctor(program2) {
|
|
|
11563
11606
|
});
|
|
11564
11607
|
}
|
|
11565
11608
|
const anchorless = memories.filter(
|
|
11566
|
-
(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"
|
|
11609
|
+
(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"
|
|
11567
11610
|
);
|
|
11568
11611
|
if (anchorless.length / Math.max(memories.length, 1) > 0.3) {
|
|
11569
11612
|
findings.push({
|
|
@@ -11621,6 +11664,7 @@ function registerDoctor(program2) {
|
|
|
11621
11664
|
});
|
|
11622
11665
|
}
|
|
11623
11666
|
}
|
|
11667
|
+
findings.push(...await collectHarnessCoverageFindings(codeMap, memories));
|
|
11624
11668
|
findings.push(...await collectSemanticIndexFindings(paths, config, memories.length, codeMap));
|
|
11625
11669
|
const events = await readUsageEvents4(paths);
|
|
11626
11670
|
if (events.length === 0) {
|
|
@@ -11662,8 +11706,8 @@ function registerDoctor(program2) {
|
|
|
11662
11706
|
let hasClaudeEnforcement = false;
|
|
11663
11707
|
if (existsSync63(claudeSettings)) {
|
|
11664
11708
|
try {
|
|
11665
|
-
const { readFile:
|
|
11666
|
-
const raw = await
|
|
11709
|
+
const { readFile: readFile21 } = await import("fs/promises");
|
|
11710
|
+
const raw = await readFile21(claudeSettings, "utf8");
|
|
11667
11711
|
hasClaudeEnforcement = raw.includes("haive enforce session-start") && raw.includes("haive enforce pre-tool-use");
|
|
11668
11712
|
} catch {
|
|
11669
11713
|
hasClaudeEnforcement = false;
|
|
@@ -11686,14 +11730,14 @@ function registerDoctor(program2) {
|
|
|
11686
11730
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
|
|
11687
11731
|
});
|
|
11688
11732
|
}
|
|
11689
|
-
findings.push(...await collectInstallFindings(root, "0.9.
|
|
11733
|
+
findings.push(...await collectInstallFindings(root, "0.9.21"));
|
|
11690
11734
|
try {
|
|
11691
11735
|
const legacyRaw = execSync3("haive-mcp --version", {
|
|
11692
11736
|
encoding: "utf8",
|
|
11693
11737
|
timeout: 3e3,
|
|
11694
11738
|
stdio: ["ignore", "pipe", "ignore"]
|
|
11695
11739
|
}).trim();
|
|
11696
|
-
const cliVersion = "0.9.
|
|
11740
|
+
const cliVersion = "0.9.21";
|
|
11697
11741
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
11698
11742
|
findings.push({
|
|
11699
11743
|
severity: "warn",
|
|
@@ -11741,7 +11785,7 @@ function emit(findings, opts, repairs = []) {
|
|
|
11741
11785
|
console.log(ui.bold(`hAIve doctor \u2014 ${classified.length} finding${classified.length === 1 ? "" : "s"}`));
|
|
11742
11786
|
console.log(
|
|
11743
11787
|
ui.dim(
|
|
11744
|
-
` protection=${scores.protection_score} context=${scores.context_quality_score} corpus=${scores.corpus_quality_score}
|
|
11788
|
+
` protection=${scores.protection_score} context=${scores.context_quality_score} corpus=${scores.corpus_quality_score} harness-coverage=${scores.harness_coverage_score}%`
|
|
11745
11789
|
)
|
|
11746
11790
|
);
|
|
11747
11791
|
console.log();
|
|
@@ -11750,6 +11794,7 @@ function emit(findings, opts, repairs = []) {
|
|
|
11750
11794
|
"Agent coverage",
|
|
11751
11795
|
"Context quality",
|
|
11752
11796
|
"Corpus health",
|
|
11797
|
+
"Harness coverage",
|
|
11753
11798
|
"Index health",
|
|
11754
11799
|
"Next actions"
|
|
11755
11800
|
];
|
|
@@ -11787,6 +11832,7 @@ function emit(findings, opts, repairs = []) {
|
|
|
11787
11832
|
function sectionForFinding(finding) {
|
|
11788
11833
|
if (finding.code.includes("haive") || finding.code.includes("integration") || finding.code.includes("claude") || finding.code.includes("autopilot")) return "Agent coverage";
|
|
11789
11834
|
if (finding.code.includes("context") || finding.code.includes("briefing") || finding.code.includes("search")) return "Context quality";
|
|
11835
|
+
if (finding.code.includes("harness-coverage")) return "Harness coverage";
|
|
11790
11836
|
if (finding.code.includes("code-map") || finding.code.includes("index")) return "Index health";
|
|
11791
11837
|
if (finding.code.includes("memory") || finding.code.includes("anchor") || finding.code.includes("pending") || finding.code.includes("decay")) return "Corpus health";
|
|
11792
11838
|
if (finding.severity === "error") return "Protection";
|
|
@@ -11802,10 +11848,13 @@ function computeDoctorScores(findings) {
|
|
|
11802
11848
|
}, 0);
|
|
11803
11849
|
return Math.max(0, 100 - penalty);
|
|
11804
11850
|
};
|
|
11851
|
+
const coverageFinding = findings.find((f) => f.code === "harness-coverage");
|
|
11852
|
+
const harnessCoverageScore = coverageFinding ? parseInt((coverageFinding.message.match(/\((\d+)%\)/) ?? [])[1] ?? "0", 10) : 0;
|
|
11805
11853
|
return {
|
|
11806
11854
|
protection_score: scoreFor(["Protection", "Agent coverage"]),
|
|
11807
11855
|
context_quality_score: scoreFor(["Context quality", "Index health"]),
|
|
11808
|
-
corpus_quality_score: scoreFor(["Corpus health"])
|
|
11856
|
+
corpus_quality_score: scoreFor(["Corpus health"]),
|
|
11857
|
+
harness_coverage_score: harnessCoverageScore
|
|
11809
11858
|
};
|
|
11810
11859
|
}
|
|
11811
11860
|
function groupBySection(findings) {
|
|
@@ -11814,6 +11863,7 @@ function groupBySection(findings) {
|
|
|
11814
11863
|
"Agent coverage": [],
|
|
11815
11864
|
"Context quality": [],
|
|
11816
11865
|
"Corpus health": [],
|
|
11866
|
+
"Harness coverage": [],
|
|
11817
11867
|
"Index health": [],
|
|
11818
11868
|
"Next actions": []
|
|
11819
11869
|
};
|
|
@@ -11823,6 +11873,37 @@ function groupBySection(findings) {
|
|
|
11823
11873
|
function nextActions(findings) {
|
|
11824
11874
|
return [...new Set(findings.flatMap((finding) => finding.fix ? finding.fix.split("\n") : []))].filter(Boolean);
|
|
11825
11875
|
}
|
|
11876
|
+
async function collectHarnessCoverageFindings(codeMap, memories) {
|
|
11877
|
+
if (!codeMap) return [];
|
|
11878
|
+
const codeFiles = Object.keys(codeMap.files);
|
|
11879
|
+
const total = codeFiles.length;
|
|
11880
|
+
if (total === 0) return [];
|
|
11881
|
+
const validatedWithAnchors = memories.filter(
|
|
11882
|
+
(m) => (m.memory.frontmatter.status === "validated" || m.memory.frontmatter.status === "proposed") && m.memory.frontmatter.anchor.paths.length > 0
|
|
11883
|
+
);
|
|
11884
|
+
const coveredFiles = /* @__PURE__ */ new Set();
|
|
11885
|
+
for (const m of validatedWithAnchors) {
|
|
11886
|
+
for (const anchorPath of m.memory.frontmatter.anchor.paths) {
|
|
11887
|
+
const normalized = anchorPath.replace(/^\/+/, "");
|
|
11888
|
+
for (const codeFile of codeFiles) {
|
|
11889
|
+
if (codeFile === normalized || codeFile.startsWith(normalized + "/") || normalized.startsWith(codeFile + "/")) {
|
|
11890
|
+
coveredFiles.add(codeFile);
|
|
11891
|
+
}
|
|
11892
|
+
}
|
|
11893
|
+
}
|
|
11894
|
+
}
|
|
11895
|
+
const covered = coveredFiles.size;
|
|
11896
|
+
const pct = Math.round(covered / total * 100);
|
|
11897
|
+
const findings = [];
|
|
11898
|
+
findings.push({
|
|
11899
|
+
severity: pct < 10 && total > 10 ? "info" : "info",
|
|
11900
|
+
code: "harness-coverage",
|
|
11901
|
+
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."),
|
|
11902
|
+
fix: pct < 10 && total > 10 ? "haive memory add --type gotcha|convention|architecture --paths <key-file> --scope team" : void 0,
|
|
11903
|
+
section: "Harness coverage"
|
|
11904
|
+
});
|
|
11905
|
+
return findings;
|
|
11906
|
+
}
|
|
11826
11907
|
async function collectSemanticIndexFindings(paths, config, memoryCount, codeMap) {
|
|
11827
11908
|
const findings = [];
|
|
11828
11909
|
const autoWantsCodeSearch = Boolean(config.autopilot || config.autoRepair?.codeSearch);
|
|
@@ -11919,7 +12000,7 @@ which -a haive`
|
|
|
11919
12000
|
for (const rel of integrationFiles) {
|
|
11920
12001
|
const file = path44.join(root, rel);
|
|
11921
12002
|
if (!existsSync63(file)) continue;
|
|
11922
|
-
const text = await
|
|
12003
|
+
const text = await readFile19(file, "utf8").catch(() => "");
|
|
11923
12004
|
for (const bin of extractAbsoluteHaiveBins(text)) {
|
|
11924
12005
|
const version = versionForBinary(bin);
|
|
11925
12006
|
if (!version) {
|
|
@@ -12227,12 +12308,13 @@ import {
|
|
|
12227
12308
|
resolveHaivePaths as resolveHaivePaths40
|
|
12228
12309
|
} from "@hiveai/core";
|
|
12229
12310
|
var TYPE_RANK = {
|
|
12230
|
-
|
|
12231
|
-
|
|
12232
|
-
|
|
12233
|
-
|
|
12234
|
-
|
|
12235
|
-
|
|
12311
|
+
skill: 0,
|
|
12312
|
+
decision: 1,
|
|
12313
|
+
architecture: 2,
|
|
12314
|
+
convention: 3,
|
|
12315
|
+
glossary: 4,
|
|
12316
|
+
gotcha: 5,
|
|
12317
|
+
attempt: 6
|
|
12236
12318
|
};
|
|
12237
12319
|
function registerWelcome(program2) {
|
|
12238
12320
|
program2.command("welcome").description(
|
|
@@ -12452,7 +12534,7 @@ function registerMemoryConflictCandidates(memory2) {
|
|
|
12452
12534
|
// src/commands/enforce.ts
|
|
12453
12535
|
import { execFileSync as execFileSync2, spawn as spawn5 } from "child_process";
|
|
12454
12536
|
import { existsSync as existsSync69 } from "fs";
|
|
12455
|
-
import { chmod as chmod2, mkdir as mkdir19, readFile as
|
|
12537
|
+
import { chmod as chmod2, mkdir as mkdir19, readFile as readFile20, rm as rm3, writeFile as writeFile34 } from "fs/promises";
|
|
12456
12538
|
import path49 from "path";
|
|
12457
12539
|
import "commander";
|
|
12458
12540
|
import {
|
|
@@ -12758,7 +12840,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
12758
12840
|
findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
|
|
12759
12841
|
});
|
|
12760
12842
|
}
|
|
12761
|
-
findings.push(...await inspectIntegrationVersions(root, "0.9.
|
|
12843
|
+
findings.push(...await inspectIntegrationVersions(root, "0.9.21"));
|
|
12762
12844
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
12763
12845
|
const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
|
|
12764
12846
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
|
|
@@ -12970,7 +13052,7 @@ async function inspectIntegrationVersions(root, expectedVersion) {
|
|
|
12970
13052
|
for (const rel of files) {
|
|
12971
13053
|
const file = path49.join(root, rel);
|
|
12972
13054
|
if (!existsSync69(file)) continue;
|
|
12973
|
-
const text = await
|
|
13055
|
+
const text = await readFile20(file, "utf8").catch(() => "");
|
|
12974
13056
|
for (const bin of extractAbsoluteHaiveBins2(text)) {
|
|
12975
13057
|
const version = versionForBinary2(bin);
|
|
12976
13058
|
if (!version) {
|
|
@@ -13083,7 +13165,7 @@ haive enforce check --stage pre-push --dir . || exit $?
|
|
|
13083
13165
|
for (const hook of hooks) {
|
|
13084
13166
|
const file = path49.join(hooksDir, hook.name);
|
|
13085
13167
|
if (existsSync69(file)) {
|
|
13086
|
-
const current = await
|
|
13168
|
+
const current = await readFile20(file, "utf8").catch(() => "");
|
|
13087
13169
|
if (current.includes(ENFORCE_HOOK_MARKER)) {
|
|
13088
13170
|
await writeFile34(file, hook.body, "utf8");
|
|
13089
13171
|
} else {
|
|
@@ -13246,7 +13328,7 @@ function registerRun(program2) {
|
|
|
13246
13328
|
|
|
13247
13329
|
// src/index.ts
|
|
13248
13330
|
var program = new Command51();
|
|
13249
|
-
program.name("haive").description("hAIve \u2014
|
|
13331
|
+
program.name("haive").description("hAIve \u2014 the memory and enforcement layer of your agent harness").version("0.9.21").option("--advanced", "show maintenance and experimental commands in help");
|
|
13250
13332
|
registerInit(program);
|
|
13251
13333
|
registerWelcome(program);
|
|
13252
13334
|
registerResolveProject(program);
|
|
@@ -13289,7 +13371,9 @@ registerMemoryTimeline(memory);
|
|
|
13289
13371
|
registerMemoryConflictCandidates(memory);
|
|
13290
13372
|
registerMemoryArchive(memory);
|
|
13291
13373
|
registerMemoryLint(memory);
|
|
13292
|
-
var session = program.command("session").description(
|
|
13374
|
+
var session = program.command("session").description(
|
|
13375
|
+
"Manage session lifecycle.\n\n Session start is automatic \u2014 hAIve loads context via `get_briefing` at the start\n of each agent session (Claude Code SessionStart hook or MCP first call).\n Use `haive session end` to save a rich end-of-session recap for the next session."
|
|
13376
|
+
);
|
|
13293
13377
|
registerSessionEnd(session);
|
|
13294
13378
|
registerSnapshot(program);
|
|
13295
13379
|
registerHub(program);
|
|
@@ -13302,6 +13386,7 @@ registerPrecommit(program);
|
|
|
13302
13386
|
var CORE_ROOT_COMMANDS = /* @__PURE__ */ new Set([
|
|
13303
13387
|
"init",
|
|
13304
13388
|
"doctor",
|
|
13389
|
+
"tui",
|
|
13305
13390
|
"agent",
|
|
13306
13391
|
"enforce",
|
|
13307
13392
|
"run",
|
|
@@ -13343,7 +13428,7 @@ function applySurfaceVisibility(root) {
|
|
|
13343
13428
|
"after",
|
|
13344
13429
|
[
|
|
13345
13430
|
"",
|
|
13346
|
-
"Default help shows the core
|
|
13431
|
+
"Default help shows the core harness workflow: init, doctor, agent setup, briefing, enforcement,",
|
|
13347
13432
|
"sync, session recaps, and high-signal memory commands.",
|
|
13348
13433
|
"Run `haive --advanced --help` or set HAIVE_SHOW_ADVANCED=1 to show maintenance and experimental commands."
|
|
13349
13434
|
].join("\n")
|