@hiveai/cli 0.9.26 → 0.9.27
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 +202 -102
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -4815,7 +4815,8 @@ var SessionTracker = class {
|
|
|
4815
4815
|
const ranPostTask = this.events.some(
|
|
4816
4816
|
(e) => e.tool === "mem_session_end" && !e.summary?.startsWith("Auto-captured")
|
|
4817
4817
|
);
|
|
4818
|
-
|
|
4818
|
+
const isSubstantialSession = totalCalls >= 3 || writingTools.length > 0;
|
|
4819
|
+
if (!ranPostTask && isSubstantialSession && existsSync16(this.ctx.paths.haiveDir)) {
|
|
4819
4820
|
try {
|
|
4820
4821
|
const memoriesSaved = writingTools.map((e) => e.summary ?? "").filter(Boolean).slice(0, 20);
|
|
4821
4822
|
const payload = {
|
|
@@ -6564,9 +6565,36 @@ function isDocLikePath(file) {
|
|
|
6564
6565
|
function isPackageOrConfigPath(file) {
|
|
6565
6566
|
const lower = file.toLowerCase();
|
|
6566
6567
|
const base = lower.split("/").pop() ?? lower;
|
|
6567
|
-
return lower.endsWith("package.json") || lower.endsWith("package-lock.json") || lower.endsWith("pnpm-lock.yaml") || lower.endsWith("yarn.lock") || lower.endsWith("bun.lockb") || lower.endsWith(".config.ts") || lower.endsWith(".config.js") ||
|
|
6568
|
+
return lower.endsWith("package.json") || lower.endsWith("package-lock.json") || lower.endsWith("pnpm-lock.yaml") || lower.endsWith("yarn.lock") || lower.endsWith("bun.lockb") || lower.endsWith(".config.ts") || lower.endsWith(".config.js") || isJsonConfigFile(base) || lower.endsWith(".yml") || lower.endsWith(".yaml") || lower.endsWith(".toml") || lower.startsWith(".github/workflows/") || lower.startsWith(".github/") && lower.endsWith(".yml") || // Dotfiles that are pure configuration/tooling — never trigger runtime gotchas
|
|
6568
6569
|
base === ".gitignore" || base === ".gitattributes" || base === ".gitmodules" || base === ".editorconfig" || base === ".nvmrc" || base === ".node-version" || base === ".npmrc" || base === ".yarnrc" || base === ".yarnrc.yml" || base === ".dockerignore" || base === "dockerfile" || base.startsWith("dockerfile.") || base === ".env.example" || base === ".env.template" || lower.endsWith(".prettierrc") || lower.endsWith(".eslintrc") || lower.endsWith(".eslintignore") || lower.endsWith(".prettierignore") || lower.endsWith(".stylelintrc") || lower.endsWith(".browserslistrc");
|
|
6569
6570
|
}
|
|
6571
|
+
function isJsonConfigFile(base) {
|
|
6572
|
+
const knownConfigs = /* @__PURE__ */ new Set([
|
|
6573
|
+
"tsconfig.json",
|
|
6574
|
+
"jsconfig.json",
|
|
6575
|
+
"deno.json",
|
|
6576
|
+
"deno.jsonc",
|
|
6577
|
+
"nx.json",
|
|
6578
|
+
"turbo.json",
|
|
6579
|
+
"lerna.json",
|
|
6580
|
+
"rush.json",
|
|
6581
|
+
"jest.config.json",
|
|
6582
|
+
"vitest.config.json",
|
|
6583
|
+
"babel.config.json",
|
|
6584
|
+
".babelrc.json",
|
|
6585
|
+
".swcrc",
|
|
6586
|
+
".mocharc.json",
|
|
6587
|
+
"renovate.json",
|
|
6588
|
+
"dependabot.json",
|
|
6589
|
+
".prettierrc.json",
|
|
6590
|
+
".eslintrc.json",
|
|
6591
|
+
".stylelintrc.json"
|
|
6592
|
+
]);
|
|
6593
|
+
if (knownConfigs.has(base)) return true;
|
|
6594
|
+
if (/^tsconfig\..+\.json$/.test(base)) return true;
|
|
6595
|
+
if (/^\.[a-z]+rc\.json$/.test(base)) return true;
|
|
6596
|
+
return false;
|
|
6597
|
+
}
|
|
6570
6598
|
function repairCommandForWarning(warning, paths) {
|
|
6571
6599
|
const firstPath = paths[0];
|
|
6572
6600
|
return firstPath ? `haive briefing --files "${firstPath}" --task "review ${warning.id}"` : `haive memory show ${warning.id}`;
|
|
@@ -7102,7 +7130,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
7102
7130
|
};
|
|
7103
7131
|
}
|
|
7104
7132
|
var SERVER_NAME = "haive";
|
|
7105
|
-
var SERVER_VERSION = "0.9.
|
|
7133
|
+
var SERVER_VERSION = "0.9.27";
|
|
7106
7134
|
function jsonResult(data) {
|
|
7107
7135
|
return {
|
|
7108
7136
|
content: [
|
|
@@ -8099,14 +8127,14 @@ var BRIDGE_START = "<!-- haive:memories-start -->";
|
|
|
8099
8127
|
var BRIDGE_END = "<!-- haive:memories-end -->";
|
|
8100
8128
|
function registerSync(program2) {
|
|
8101
8129
|
program2.command("sync").description(
|
|
8102
|
-
"Refresh memory state after a git pull or merge.\n What it does:\n 1. Verifies anchor paths \u2014 marks stale if files/symbols moved or deleted\n 2. Re-validates previously stale memories that are now fresh\n 3. Auto-promotes proposed memories (by usage count or time delay in autopilot)\n 4. Auto-refreshes code-map if source files changed\n 5. Reports decay warnings for memories unused >90 days\n\n Install git hooks to run sync automatically: haive install-hooks\n\n Examples:\n haive sync\n haive sync --since main # also report memories changed since main\n haive sync --embed # also rebuild embeddings index\n"
|
|
8130
|
+
"Refresh memory state after a git pull or merge.\n What it does:\n 1. Verifies anchor paths \u2014 marks stale if files/symbols moved or deleted\n 2. Re-validates previously stale memories that are now fresh\n 3. Auto-promotes proposed memories (by usage count or time delay in autopilot)\n 4. Auto-refreshes code-map if source files changed\n 5. Reports decay warnings for memories unused >90 days\n\n Install git hooks to run sync automatically: haive install-hooks\n\n Examples:\n haive sync\n haive sync --dry-run # preview what would change without writing\n haive sync --since main # also report memories changed since main\n haive sync --embed # also rebuild embeddings index\n"
|
|
8103
8131
|
).option("-d, --dir <dir>", "project root").option("--quiet", "minimal output (suitable for git hooks)").option(
|
|
8104
8132
|
"--since <ref>",
|
|
8105
8133
|
"git ref/commit to compare against; report memories added/modified/removed since"
|
|
8106
8134
|
).option("--no-verify", "skip the anchor verification step").option("--no-promote", "skip the auto-promotion step").option(
|
|
8107
8135
|
"--inject-bridge",
|
|
8108
8136
|
"inject top validated memories into CLAUDE.md (or --bridge-file) between <!-- haive:memories-start/end --> markers"
|
|
8109
|
-
).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").action(async (opts) => {
|
|
8137
|
+
).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) => {
|
|
8110
8138
|
const root = findProjectRoot12(opts.dir);
|
|
8111
8139
|
const paths = resolveHaivePaths9(root);
|
|
8112
8140
|
if (!existsSync29(paths.memoriesDir)) {
|
|
@@ -8121,6 +8149,8 @@ function registerSync(program2) {
|
|
|
8121
8149
|
const autoApproveDelayHours = config.autoApproveDelayHours ?? null;
|
|
8122
8150
|
const autoPromoteMinReads = config.autoPromoteMinReads ?? DEFAULT_AUTO_PROMOTE_RULE2.minReads;
|
|
8123
8151
|
const autoRepair = config.autoRepair ?? {};
|
|
8152
|
+
const dryRun = opts.dryRun === true;
|
|
8153
|
+
if (dryRun) log(ui.yellow("(dry run \u2014 no files will be written)"));
|
|
8124
8154
|
let staleMarked = 0;
|
|
8125
8155
|
let revalidated = 0;
|
|
8126
8156
|
let promoted = 0;
|
|
@@ -8130,19 +8160,21 @@ function registerSync(program2) {
|
|
|
8130
8160
|
for (const { memory: memory2, filePath } of memories) {
|
|
8131
8161
|
if (memory2.frontmatter.type === "session_recap") {
|
|
8132
8162
|
if (memory2.frontmatter.status === "stale") {
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
|
|
8144
|
-
|
|
8145
|
-
|
|
8163
|
+
if (!dryRun) {
|
|
8164
|
+
await writeFile13(
|
|
8165
|
+
filePath,
|
|
8166
|
+
serializeMemory11({
|
|
8167
|
+
frontmatter: {
|
|
8168
|
+
...memory2.frontmatter,
|
|
8169
|
+
status: "validated",
|
|
8170
|
+
stale_reason: null,
|
|
8171
|
+
verified_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
8172
|
+
},
|
|
8173
|
+
body: memory2.body
|
|
8174
|
+
}),
|
|
8175
|
+
"utf8"
|
|
8176
|
+
);
|
|
8177
|
+
}
|
|
8146
8178
|
revalidated++;
|
|
8147
8179
|
}
|
|
8148
8180
|
continue;
|
|
@@ -8153,35 +8185,39 @@ function registerSync(program2) {
|
|
|
8153
8185
|
const verifiedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8154
8186
|
if (result.stale) {
|
|
8155
8187
|
if (memory2.frontmatter.status !== "stale") {
|
|
8188
|
+
if (!dryRun) {
|
|
8189
|
+
await writeFile13(
|
|
8190
|
+
filePath,
|
|
8191
|
+
serializeMemory11({
|
|
8192
|
+
frontmatter: {
|
|
8193
|
+
...memory2.frontmatter,
|
|
8194
|
+
status: "stale",
|
|
8195
|
+
verified_at: verifiedAt,
|
|
8196
|
+
stale_reason: result.reason
|
|
8197
|
+
},
|
|
8198
|
+
body: memory2.body
|
|
8199
|
+
}),
|
|
8200
|
+
"utf8"
|
|
8201
|
+
);
|
|
8202
|
+
}
|
|
8203
|
+
staleMarked++;
|
|
8204
|
+
}
|
|
8205
|
+
} else if (memory2.frontmatter.status === "stale") {
|
|
8206
|
+
if (!dryRun) {
|
|
8156
8207
|
await writeFile13(
|
|
8157
8208
|
filePath,
|
|
8158
8209
|
serializeMemory11({
|
|
8159
8210
|
frontmatter: {
|
|
8160
8211
|
...memory2.frontmatter,
|
|
8161
|
-
status: "
|
|
8212
|
+
status: "validated",
|
|
8162
8213
|
verified_at: verifiedAt,
|
|
8163
|
-
stale_reason:
|
|
8214
|
+
stale_reason: null
|
|
8164
8215
|
},
|
|
8165
8216
|
body: memory2.body
|
|
8166
8217
|
}),
|
|
8167
8218
|
"utf8"
|
|
8168
8219
|
);
|
|
8169
|
-
staleMarked++;
|
|
8170
8220
|
}
|
|
8171
|
-
} else if (memory2.frontmatter.status === "stale") {
|
|
8172
|
-
await writeFile13(
|
|
8173
|
-
filePath,
|
|
8174
|
-
serializeMemory11({
|
|
8175
|
-
frontmatter: {
|
|
8176
|
-
...memory2.frontmatter,
|
|
8177
|
-
status: "validated",
|
|
8178
|
-
verified_at: verifiedAt,
|
|
8179
|
-
stale_reason: null
|
|
8180
|
-
},
|
|
8181
|
-
body: memory2.body
|
|
8182
|
-
}),
|
|
8183
|
-
"utf8"
|
|
8184
|
-
);
|
|
8185
8221
|
revalidated++;
|
|
8186
8222
|
}
|
|
8187
8223
|
}
|
|
@@ -8197,35 +8233,39 @@ function registerSync(program2) {
|
|
|
8197
8233
|
minReads: autoPromoteMinReads,
|
|
8198
8234
|
maxRejections: DEFAULT_AUTO_PROMOTE_RULE2.maxRejections
|
|
8199
8235
|
})) {
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8236
|
+
if (!dryRun) {
|
|
8237
|
+
await writeFile13(
|
|
8238
|
+
filePath,
|
|
8239
|
+
serializeMemory11({ frontmatter: { ...fm, status: "validated" }, body: memory2.body }),
|
|
8240
|
+
"utf8"
|
|
8241
|
+
);
|
|
8242
|
+
}
|
|
8205
8243
|
promoted++;
|
|
8206
8244
|
continue;
|
|
8207
8245
|
}
|
|
8208
8246
|
if (autoApproveDelayHours !== null && fm.status === "proposed" && fm.scope === "team") {
|
|
8209
8247
|
const ageHours = (nowMs - new Date(fm.created_at).getTime()) / (1e3 * 60 * 60);
|
|
8210
8248
|
if (ageHours >= autoApproveDelayHours) {
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8249
|
+
if (!dryRun) {
|
|
8250
|
+
await writeFile13(
|
|
8251
|
+
filePath,
|
|
8252
|
+
serializeMemory11({
|
|
8253
|
+
frontmatter: {
|
|
8254
|
+
...fm,
|
|
8255
|
+
status: "validated",
|
|
8256
|
+
verified_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
8257
|
+
},
|
|
8258
|
+
body: memory2.body
|
|
8259
|
+
}),
|
|
8260
|
+
"utf8"
|
|
8261
|
+
);
|
|
8262
|
+
}
|
|
8223
8263
|
autoApproved++;
|
|
8224
8264
|
}
|
|
8225
8265
|
}
|
|
8226
8266
|
}
|
|
8227
8267
|
}
|
|
8228
|
-
if (config.autopilot || autoRepair.context || autoRepair.corpus) {
|
|
8268
|
+
if (!dryRun && (config.autopilot || autoRepair.context || autoRepair.corpus)) {
|
|
8229
8269
|
const repairs = await applyAutopilotRepairs(root, paths, {
|
|
8230
8270
|
applyContext: autoRepair.context ?? config.autopilot,
|
|
8231
8271
|
applyCorpus: autoRepair.corpus ?? config.autopilot,
|
|
@@ -8357,14 +8397,16 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
8357
8397
|
paths: [result.file],
|
|
8358
8398
|
topic: `dep-bump-${slugParts}`
|
|
8359
8399
|
});
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8400
|
+
if (!dryRun) {
|
|
8401
|
+
const teamDir = path15.join(paths.memoriesDir, "team");
|
|
8402
|
+
await mkdir10(teamDir, { recursive: true });
|
|
8403
|
+
await writeFile13(
|
|
8404
|
+
path15.join(teamDir, `${fm.id}.md`),
|
|
8405
|
+
serializeMemory11({ frontmatter: { ...fm, requires_human_approval: true }, body }),
|
|
8406
|
+
"utf8"
|
|
8407
|
+
);
|
|
8408
|
+
}
|
|
8409
|
+
log(ui.yellow(` \u2192 memory${dryRun ? " would be" : ""} created: ${fm.id}`));
|
|
8368
8410
|
}
|
|
8369
8411
|
}
|
|
8370
8412
|
}
|
|
@@ -8424,14 +8466,16 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
8424
8466
|
paths: [diff.file],
|
|
8425
8467
|
topic: `contract-breaking-${diff.contract}`
|
|
8426
8468
|
});
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8430
|
-
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8469
|
+
if (!dryRun) {
|
|
8470
|
+
const teamDir = path15.join(paths.memoriesDir, "team");
|
|
8471
|
+
await mkdir10(teamDir, { recursive: true });
|
|
8472
|
+
await writeFile13(
|
|
8473
|
+
path15.join(teamDir, `${fm.id}.md`),
|
|
8474
|
+
serializeMemory11({ frontmatter: { ...fm, requires_human_approval: true }, body }),
|
|
8475
|
+
"utf8"
|
|
8476
|
+
);
|
|
8477
|
+
}
|
|
8478
|
+
log(ui.yellow(` \u2192 memory${dryRun ? " would be" : ""} created: ${fm.id}`));
|
|
8435
8479
|
}
|
|
8436
8480
|
}
|
|
8437
8481
|
} catch (err) {
|
|
@@ -8439,7 +8483,7 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
8439
8483
|
}
|
|
8440
8484
|
}
|
|
8441
8485
|
const existingMap = await loadCodeMap5(paths);
|
|
8442
|
-
if (!existingMap && (config.autopilot || autoRepair.codeMap)) {
|
|
8486
|
+
if (!dryRun && !existingMap && (config.autopilot || autoRepair.codeMap)) {
|
|
8443
8487
|
try {
|
|
8444
8488
|
const { buildCodeMap: buildCodeMap4, saveCodeMap: saveCodeMap4 } = await import("@hiveai/core");
|
|
8445
8489
|
log(ui.dim("code-map: missing \u2014 building index\u2026"));
|
|
@@ -8474,17 +8518,21 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
8474
8518
|
);
|
|
8475
8519
|
const changedSourceFiles = (gitResult.stdout ?? "").trim();
|
|
8476
8520
|
if (changedSourceFiles.length > 0) {
|
|
8477
|
-
|
|
8478
|
-
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8521
|
+
if (!dryRun) {
|
|
8522
|
+
try {
|
|
8523
|
+
const { buildCodeMap: buildCodeMap4, saveCodeMap: saveCodeMap4 } = await import("@hiveai/core");
|
|
8524
|
+
log(ui.dim("code-map: source files changed \u2014 refreshing index\u2026"));
|
|
8525
|
+
const newMap = await buildCodeMap4(root);
|
|
8526
|
+
await saveCodeMap4(paths, newMap);
|
|
8527
|
+
log(ui.dim(`code-map: refreshed (${Object.keys(newMap.files).length} files)`));
|
|
8528
|
+
} catch {
|
|
8529
|
+
}
|
|
8530
|
+
} else {
|
|
8531
|
+
log(ui.dim("code-map: source files changed \u2014 would refresh index (skipped in dry-run)"));
|
|
8484
8532
|
}
|
|
8485
8533
|
}
|
|
8486
8534
|
}
|
|
8487
|
-
if (opts.embed || autoRepair.codeSearch) {
|
|
8535
|
+
if (!dryRun && (opts.embed || autoRepair.codeSearch)) {
|
|
8488
8536
|
try {
|
|
8489
8537
|
const { Embedder, rebuildCodeIndex, rebuildIndex } = await import("@hiveai/embeddings");
|
|
8490
8538
|
log(ui.dim("embed: rebuilding index\u2026"));
|
|
@@ -8824,7 +8872,7 @@ import {
|
|
|
8824
8872
|
|
|
8825
8873
|
// src/commands/memory-list.ts
|
|
8826
8874
|
function registerMemoryList(memory2) {
|
|
8827
|
-
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("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8875
|
+
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) => {
|
|
8828
8876
|
const root = findProjectRoot14(opts.dir);
|
|
8829
8877
|
const paths = resolveHaivePaths11(root);
|
|
8830
8878
|
if (!existsSync31(paths.memoriesDir)) {
|
|
@@ -8834,6 +8882,7 @@ function registerMemoryList(memory2) {
|
|
|
8834
8882
|
}
|
|
8835
8883
|
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
8836
8884
|
const statusFilter = opts.status ? opts.status.split(",").map((s) => s.trim()) : null;
|
|
8885
|
+
const limit = opts.limit ? Math.max(1, parseInt(opts.limit, 10)) : void 0;
|
|
8837
8886
|
const filtered = all.filter((m) => {
|
|
8838
8887
|
if (!matchesFilters(m, opts)) return false;
|
|
8839
8888
|
const status = m.memory.frontmatter.status;
|
|
@@ -8851,7 +8900,9 @@ function registerMemoryList(memory2) {
|
|
|
8851
8900
|
}
|
|
8852
8901
|
return;
|
|
8853
8902
|
}
|
|
8854
|
-
|
|
8903
|
+
const displayed = limit !== void 0 ? filtered.slice(0, limit) : filtered;
|
|
8904
|
+
const clipped = filtered.length - displayed.length;
|
|
8905
|
+
for (const { memory: mem, filePath } of displayed) {
|
|
8855
8906
|
const fm = mem.frontmatter;
|
|
8856
8907
|
const tagStr = fm.tags.length ? ui.dim(` [${fm.tags.join(", ")}]`) : "";
|
|
8857
8908
|
const moduleStr = fm.module ? ui.dim(` (${fm.module})`) : "";
|
|
@@ -8863,8 +8914,10 @@ function registerMemoryList(memory2) {
|
|
|
8863
8914
|
if (title && title !== fm.id) console.log(` ${title}`);
|
|
8864
8915
|
console.log(` ${ui.dim(path17.relative(root, filePath))}`);
|
|
8865
8916
|
}
|
|
8866
|
-
|
|
8867
|
-
${
|
|
8917
|
+
const totalLabel = clipped > 0 ? `
|
|
8918
|
+
${displayed.length} of ${filtered.length} memories shown (use --limit to adjust)` : `
|
|
8919
|
+
${filtered.length} memor${filtered.length === 1 ? "y" : "ies"}`;
|
|
8920
|
+
console.log(ui.dim(totalLabel));
|
|
8868
8921
|
if (hiddenRejectedCount > 0) {
|
|
8869
8922
|
console.log(
|
|
8870
8923
|
ui.dim(`(${hiddenRejectedCount} rejected hidden \u2014 use --show-rejected to include)`)
|
|
@@ -9380,7 +9433,9 @@ import {
|
|
|
9380
9433
|
resolveHaivePaths as resolveHaivePaths18
|
|
9381
9434
|
} from "@hiveai/core";
|
|
9382
9435
|
function registerMemoryHot(memory2) {
|
|
9383
|
-
memory2.command("hot").description(
|
|
9436
|
+
memory2.command("hot").description(
|
|
9437
|
+
"List unvalidated memories with high read_count \u2014 proven-useful promotion candidates.\n\n Unlike `haive memory pending` (which lists ALL draft/proposed by status),\n `hot` filters by usage: only memories read \u2265N times qualify.\n Use it to quickly find memories that agents are already relying on\n but that haven't been formally validated yet."
|
|
9438
|
+
).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) => {
|
|
9384
9439
|
const root = findProjectRoot21(opts.dir);
|
|
9385
9440
|
const paths = resolveHaivePaths18(root);
|
|
9386
9441
|
if (!existsSync39(paths.memoriesDir)) {
|
|
@@ -9414,7 +9469,8 @@ function registerMemoryHot(memory2) {
|
|
|
9414
9469
|
console.log(` ${ui.dim(path25.relative(root, filePath))}`);
|
|
9415
9470
|
}
|
|
9416
9471
|
ui.info(
|
|
9417
|
-
`${candidates.length} hot \u2014
|
|
9472
|
+
`${candidates.length} hot (read \u2265${threshold}\xD7) \u2014 agents rely on these; promote with \`haive memory promote <id>\`.
|
|
9473
|
+
Tip: \`haive memory pending\` lists ALL unvalidated memories regardless of read count.`
|
|
9418
9474
|
);
|
|
9419
9475
|
});
|
|
9420
9476
|
}
|
|
@@ -11763,7 +11819,7 @@ function parseDays(input) {
|
|
|
11763
11819
|
|
|
11764
11820
|
// src/commands/doctor.ts
|
|
11765
11821
|
import { existsSync as existsSync60, statSync } from "fs";
|
|
11766
|
-
import { readFile as readFile19, stat } from "fs/promises";
|
|
11822
|
+
import { readFile as readFile19, stat, writeFile as writeFile31 } from "fs/promises";
|
|
11767
11823
|
import path44 from "path";
|
|
11768
11824
|
import { execFileSync, execSync as execSync3 } from "child_process";
|
|
11769
11825
|
import "commander";
|
|
@@ -12007,26 +12063,58 @@ function registerDoctor(program2) {
|
|
|
12007
12063
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
|
|
12008
12064
|
});
|
|
12009
12065
|
}
|
|
12010
|
-
findings.push(...await collectInstallFindings(root, "0.9.
|
|
12066
|
+
findings.push(...await collectInstallFindings(root, "0.9.27"));
|
|
12011
12067
|
try {
|
|
12012
12068
|
const legacyRaw = execSync3("haive-mcp --version", {
|
|
12013
12069
|
encoding: "utf8",
|
|
12014
12070
|
timeout: 3e3,
|
|
12015
12071
|
stdio: ["ignore", "pipe", "ignore"]
|
|
12016
12072
|
}).trim();
|
|
12017
|
-
const cliVersion = "0.9.
|
|
12073
|
+
const cliVersion = "0.9.27";
|
|
12018
12074
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
12019
12075
|
findings.push({
|
|
12020
12076
|
severity: "warn",
|
|
12021
12077
|
code: "legacy-haive-mcp-stale",
|
|
12022
|
-
message: `Standalone haive-mcp on PATH is v${legacyRaw} but haive CLI is v${cliVersion}.
|
|
12023
|
-
fix:
|
|
12024
|
-
|
|
12078
|
+
message: `Standalone haive-mcp on PATH is v${legacyRaw} but haive CLI is v${cliVersion}. MCP is now bundled in haive itself \u2014 switch your client configs to command "haive" + args ["mcp", "--stdio"], then uninstall @hiveai/mcp.`,
|
|
12079
|
+
fix: `# 1. Run haive init to regenerate MCP configs pointing to bundled server:
|
|
12080
|
+
haive init
|
|
12081
|
+
# 2. Optionally remove the now-redundant standalone package:
|
|
12025
12082
|
npm uninstall -g @hiveai/mcp`
|
|
12026
12083
|
});
|
|
12027
12084
|
}
|
|
12028
12085
|
} catch {
|
|
12029
12086
|
}
|
|
12087
|
+
{
|
|
12088
|
+
const configPaths = [
|
|
12089
|
+
path44.join(root, ".mcp.json"),
|
|
12090
|
+
path44.join(root, ".cursor", "mcp.json"),
|
|
12091
|
+
path44.join(root, ".vscode", "mcp.json")
|
|
12092
|
+
];
|
|
12093
|
+
const staleConfigs = [];
|
|
12094
|
+
for (const cfgPath of configPaths) {
|
|
12095
|
+
if (!existsSync60(cfgPath)) continue;
|
|
12096
|
+
try {
|
|
12097
|
+
const raw = await readFile19(cfgPath, "utf8");
|
|
12098
|
+
if (raw.includes('"haive-mcp"') || raw.includes("'haive-mcp'")) {
|
|
12099
|
+
staleConfigs.push(path44.relative(root, cfgPath));
|
|
12100
|
+
if (opts.fix && !opts.dryRun) {
|
|
12101
|
+
const updated = raw.replace(/"command"\s*:\s*"haive-mcp"/g, '"command": "haive"').replace(/"args"\s*:\s*\[\]/g, '"args": ["mcp", "--stdio"]');
|
|
12102
|
+
await writeFile31(cfgPath, updated, "utf8");
|
|
12103
|
+
}
|
|
12104
|
+
}
|
|
12105
|
+
} catch {
|
|
12106
|
+
}
|
|
12107
|
+
}
|
|
12108
|
+
if (staleConfigs.length > 0) {
|
|
12109
|
+
findings.push({
|
|
12110
|
+
severity: "warn",
|
|
12111
|
+
code: "legacy-mcp-config",
|
|
12112
|
+
message: `${staleConfigs.length} MCP config file${staleConfigs.length === 1 ? "" : "s"} still reference the old "haive-mcp" command: ` + staleConfigs.join(", ") + `. Run \`haive doctor --fix\` to auto-migrate to the bundled server.`,
|
|
12113
|
+
fix: "haive doctor --fix",
|
|
12114
|
+
section: "Protection"
|
|
12115
|
+
});
|
|
12116
|
+
}
|
|
12117
|
+
}
|
|
12030
12118
|
if (repairs.length > 0) {
|
|
12031
12119
|
findings.push({
|
|
12032
12120
|
severity: "info",
|
|
@@ -12150,9 +12238,21 @@ function groupBySection(findings) {
|
|
|
12150
12238
|
function nextActions(findings) {
|
|
12151
12239
|
return [...new Set(findings.flatMap((finding) => finding.fix ? finding.fix.split("\n") : []))].filter(Boolean);
|
|
12152
12240
|
}
|
|
12241
|
+
function isLowValueCoverageFile(file) {
|
|
12242
|
+
const lower = file.toLowerCase();
|
|
12243
|
+
const base = lower.split("/").pop() ?? lower;
|
|
12244
|
+
if (lower.includes(".test.") || lower.includes(".spec.")) return true;
|
|
12245
|
+
if (lower.includes("/__tests__/") || lower.includes("/test/") || lower.includes("/tests/")) return true;
|
|
12246
|
+
if (lower.endsWith(".d.ts")) return true;
|
|
12247
|
+
if (base === "tsup.config.ts" || base === "vitest.config.ts" || base === "jest.config.ts") return true;
|
|
12248
|
+
if (base.endsWith(".config.ts") || base.endsWith(".config.js")) return true;
|
|
12249
|
+
if (base === "vite.config.ts" || base === "vite.config.js") return true;
|
|
12250
|
+
return false;
|
|
12251
|
+
}
|
|
12153
12252
|
async function collectHarnessCoverageFindings(codeMap, memories) {
|
|
12154
12253
|
if (!codeMap) return [];
|
|
12155
|
-
const
|
|
12254
|
+
const allFiles = Object.keys(codeMap.files);
|
|
12255
|
+
const codeFiles = allFiles.filter((f) => !isLowValueCoverageFile(f));
|
|
12156
12256
|
const total = codeFiles.length;
|
|
12157
12257
|
if (total === 0) return [];
|
|
12158
12258
|
const validatedWithAnchors = memories.filter(
|
|
@@ -12926,7 +13026,7 @@ function registerMemoryConflictCandidates(memory2) {
|
|
|
12926
13026
|
// src/commands/enforce.ts
|
|
12927
13027
|
import { execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
|
|
12928
13028
|
import { existsSync as existsSync67, statSync as statSync2 } from "fs";
|
|
12929
|
-
import { chmod as chmod2, mkdir as mkdir19, readFile as readFile20, readdir as readdir6, rm as rm3, writeFile as
|
|
13029
|
+
import { chmod as chmod2, mkdir as mkdir19, readFile as readFile20, readdir as readdir6, rm as rm3, writeFile as writeFile33 } from "fs/promises";
|
|
12930
13030
|
import path49 from "path";
|
|
12931
13031
|
import "commander";
|
|
12932
13032
|
import {
|
|
@@ -13220,7 +13320,7 @@ async function writeWrapperBriefing(paths, sessionId, task) {
|
|
|
13220
13320
|
if (briefing.setup_warnings.length > 0) {
|
|
13221
13321
|
parts.push("", "## Setup Warnings", ...briefing.setup_warnings.map((w) => `- ${w}`));
|
|
13222
13322
|
}
|
|
13223
|
-
await
|
|
13323
|
+
await writeFile33(file, parts.join("\n") + "\n", "utf8");
|
|
13224
13324
|
return file;
|
|
13225
13325
|
}
|
|
13226
13326
|
async function buildEnforcementReport(dir, stage, sessionId) {
|
|
@@ -13257,7 +13357,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
13257
13357
|
findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
|
|
13258
13358
|
});
|
|
13259
13359
|
}
|
|
13260
|
-
findings.push(...await inspectIntegrationVersions(root, "0.9.
|
|
13360
|
+
findings.push(...await inspectIntegrationVersions(root, "0.9.27"));
|
|
13261
13361
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
13262
13362
|
const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
|
|
13263
13363
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
|
|
@@ -13479,9 +13579,9 @@ async function cleanupRuntimeDir(runtimeDir) {
|
|
|
13479
13579
|
await rm3(path49.join(runtimeDir, entry.name), { recursive: true, force: true });
|
|
13480
13580
|
removed++;
|
|
13481
13581
|
}
|
|
13482
|
-
await
|
|
13582
|
+
await writeFile33(path49.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
|
|
13483
13583
|
if (!existsSync67(path49.join(runtimeDir, "README.md"))) {
|
|
13484
|
-
await
|
|
13584
|
+
await writeFile33(
|
|
13485
13585
|
path49.join(runtimeDir, "README.md"),
|
|
13486
13586
|
"# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. hAIve cleanup preserves briefing markers so enforcement state remains valid.\n",
|
|
13487
13587
|
"utf8"
|
|
@@ -13498,7 +13598,7 @@ async function cleanupCacheDir(cacheDir) {
|
|
|
13498
13598
|
await rm3(path49.join(cacheDir, entry.name), { recursive: true, force: true });
|
|
13499
13599
|
removed++;
|
|
13500
13600
|
}
|
|
13501
|
-
await
|
|
13601
|
+
await writeFile33(path49.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
|
|
13502
13602
|
return removed;
|
|
13503
13603
|
}
|
|
13504
13604
|
async function cleanupEnforcementDir(enforcementDir) {
|
|
@@ -13645,14 +13745,14 @@ haive enforce check --stage pre-push --dir . || exit $?
|
|
|
13645
13745
|
if (existsSync67(file)) {
|
|
13646
13746
|
const current = await readFile20(file, "utf8").catch(() => "");
|
|
13647
13747
|
if (current.includes(ENFORCE_HOOK_MARKER)) {
|
|
13648
|
-
await
|
|
13748
|
+
await writeFile33(file, hook.body, "utf8");
|
|
13649
13749
|
} else {
|
|
13650
|
-
await
|
|
13750
|
+
await writeFile33(file, `${current.trimEnd()}
|
|
13651
13751
|
|
|
13652
13752
|
${hook.body}`, "utf8");
|
|
13653
13753
|
}
|
|
13654
13754
|
} else {
|
|
13655
|
-
await
|
|
13755
|
+
await writeFile33(file, hook.body, "utf8");
|
|
13656
13756
|
}
|
|
13657
13757
|
await chmod2(file, 493);
|
|
13658
13758
|
}
|
|
@@ -13665,7 +13765,7 @@ async function installCiEnforcement(root) {
|
|
|
13665
13765
|
ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
|
|
13666
13766
|
return;
|
|
13667
13767
|
}
|
|
13668
|
-
await
|
|
13768
|
+
await writeFile33(workflowPath, `name: haive-enforcement
|
|
13669
13769
|
|
|
13670
13770
|
on:
|
|
13671
13771
|
pull_request:
|
|
@@ -13864,7 +13964,7 @@ function registerRun(program2) {
|
|
|
13864
13964
|
|
|
13865
13965
|
// src/index.ts
|
|
13866
13966
|
var program = new Command51();
|
|
13867
|
-
program.name("haive").description("hAIve \u2014 the memory and enforcement layer of your agent harness").version("0.9.
|
|
13967
|
+
program.name("haive").description("hAIve \u2014 the memory and enforcement layer of your agent harness").version("0.9.27").option("--advanced", "show maintenance and experimental commands in help");
|
|
13868
13968
|
registerInit(program);
|
|
13869
13969
|
registerWelcome(program);
|
|
13870
13970
|
registerResolveProject(program);
|