@hiveai/cli 0.2.2 → 0.2.3
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 +400 -187
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command24 } from "commander";
|
|
5
5
|
|
|
6
|
-
// src/commands/
|
|
6
|
+
// src/commands/briefing.ts
|
|
7
7
|
import { existsSync } from "fs";
|
|
8
|
-
import
|
|
8
|
+
import { readFile } from "fs/promises";
|
|
9
9
|
import "commander";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
findProjectRoot,
|
|
12
|
+
literalMatchesAllTokens,
|
|
13
|
+
loadMemoriesFromDir,
|
|
14
|
+
memoryMatchesAnchorPaths,
|
|
15
|
+
resolveHaivePaths,
|
|
16
|
+
tokenizeQuery
|
|
17
|
+
} from "@hiveai/core";
|
|
11
18
|
|
|
12
19
|
// src/utils/ui.ts
|
|
13
20
|
import pc from "picocolors";
|
|
@@ -17,16 +24,107 @@ var ui = {
|
|
|
17
24
|
warn: (msg) => console.log(pc.yellow("\u26A0"), msg),
|
|
18
25
|
error: (msg) => console.error(pc.red("\u2717"), msg),
|
|
19
26
|
dim: (msg) => pc.dim(msg),
|
|
20
|
-
bold: (msg) => pc.bold(msg)
|
|
27
|
+
bold: (msg) => pc.bold(msg),
|
|
28
|
+
green: (msg) => pc.green(msg),
|
|
29
|
+
yellow: (msg) => pc.yellow(msg),
|
|
30
|
+
red: (msg) => pc.red(msg),
|
|
31
|
+
statusBadge: (status) => {
|
|
32
|
+
switch (status) {
|
|
33
|
+
case "validated":
|
|
34
|
+
return pc.green(status);
|
|
35
|
+
case "proposed":
|
|
36
|
+
return pc.yellow(status);
|
|
37
|
+
case "stale":
|
|
38
|
+
return pc.yellow(status);
|
|
39
|
+
case "rejected":
|
|
40
|
+
return pc.red(status);
|
|
41
|
+
case "deprecated":
|
|
42
|
+
return pc.dim(status);
|
|
43
|
+
default:
|
|
44
|
+
return pc.dim(status);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
21
47
|
};
|
|
22
48
|
|
|
49
|
+
// src/commands/briefing.ts
|
|
50
|
+
function registerBriefing(program2) {
|
|
51
|
+
program2.command("briefing").description(
|
|
52
|
+
"Print project context + relevant memories in one shot \u2014 ideal for agent onboarding"
|
|
53
|
+
).option("--task <text>", "what you are about to do \u2014 filters memories by relevance").option("--files <csv>", "comma-separated file paths being worked on (anchors memories)").option("--max-memories <n>", "cap on memories surfaced", "10").option(
|
|
54
|
+
"--scope <scope>",
|
|
55
|
+
"personal | team | module (default: team + validated only)",
|
|
56
|
+
"team"
|
|
57
|
+
).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
58
|
+
const root = findProjectRoot(opts.dir);
|
|
59
|
+
const paths = resolveHaivePaths(root);
|
|
60
|
+
if (existsSync(paths.projectContext)) {
|
|
61
|
+
const ctx = await readFile(paths.projectContext, "utf8");
|
|
62
|
+
console.log(`${ui.bold("=== Project Context ===")}
|
|
63
|
+
`);
|
|
64
|
+
console.log(ctx.trim());
|
|
65
|
+
console.log();
|
|
66
|
+
} else {
|
|
67
|
+
ui.warn(
|
|
68
|
+
"No project-context.md found. Run `haive init` and the `bootstrap_project` MCP prompt to set it up."
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
if (!existsSync(paths.memoriesDir)) return;
|
|
72
|
+
const all = await loadMemoriesFromDir(paths.memoriesDir);
|
|
73
|
+
const filePaths = parseCsv(opts.files);
|
|
74
|
+
const tokens = opts.task ? tokenizeQuery(opts.task) : null;
|
|
75
|
+
const maxMemories = Math.max(1, Number(opts.maxMemories ?? 10));
|
|
76
|
+
const scopeFilter = opts.scope ?? "team";
|
|
77
|
+
const candidates = all.filter(({ memory: mem }) => {
|
|
78
|
+
const fm = mem.frontmatter;
|
|
79
|
+
if (fm.status === "rejected" || fm.status === "deprecated") return false;
|
|
80
|
+
if (scopeFilter !== "all" && fm.scope !== scopeFilter) return false;
|
|
81
|
+
return true;
|
|
82
|
+
});
|
|
83
|
+
const scored = candidates.map(({ memory: mem, filePath }) => {
|
|
84
|
+
const fm = mem.frontmatter;
|
|
85
|
+
let score = 0;
|
|
86
|
+
if (fm.status === "validated") score += 3;
|
|
87
|
+
else if (fm.status === "proposed") score += 1;
|
|
88
|
+
if (filePaths.length > 0 && memoryMatchesAnchorPaths(mem, filePaths)) score += 4;
|
|
89
|
+
if (tokens && literalMatchesAllTokens(mem, tokens)) score += 3;
|
|
90
|
+
return { memory: mem, filePath, score };
|
|
91
|
+
});
|
|
92
|
+
scored.sort((a, b) => b.score - a.score);
|
|
93
|
+
const top = scored.slice(0, maxMemories);
|
|
94
|
+
if (top.length === 0) {
|
|
95
|
+
ui.info("No relevant memories found.");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
console.log(`${ui.bold("=== Relevant Memories ===")}
|
|
99
|
+
`);
|
|
100
|
+
for (const { memory: mem } of top) {
|
|
101
|
+
const fm = mem.frontmatter;
|
|
102
|
+
const badge = ui.statusBadge(fm.status);
|
|
103
|
+
console.log(
|
|
104
|
+
`${ui.bold(fm.id)} ${ui.dim(fm.scope + "/" + fm.type)} ${badge}`
|
|
105
|
+
);
|
|
106
|
+
console.log(mem.body.trim());
|
|
107
|
+
console.log();
|
|
108
|
+
}
|
|
109
|
+
console.log(ui.dim(`${top.length} memor${top.length === 1 ? "y" : "ies"} surfaced`));
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function parseCsv(value) {
|
|
113
|
+
if (!value) return [];
|
|
114
|
+
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
115
|
+
}
|
|
116
|
+
|
|
23
117
|
// src/commands/embeddings.ts
|
|
118
|
+
import { existsSync as existsSync2 } from "fs";
|
|
119
|
+
import path from "path";
|
|
120
|
+
import "commander";
|
|
121
|
+
import { findProjectRoot as findProjectRoot2, resolveHaivePaths as resolveHaivePaths2 } from "@hiveai/core";
|
|
24
122
|
function registerEmbeddings(program2) {
|
|
25
123
|
const embeddings = program2.command("embeddings").description("Manage local embeddings index for semantic search");
|
|
26
124
|
embeddings.command("index").description("Generate or refresh the embeddings index for all memories").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
27
|
-
const root =
|
|
28
|
-
const paths =
|
|
29
|
-
if (!
|
|
125
|
+
const root = findProjectRoot2(opts.dir);
|
|
126
|
+
const paths = resolveHaivePaths2(root);
|
|
127
|
+
if (!existsSync2(paths.memoriesDir)) {
|
|
30
128
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
31
129
|
process.exitCode = 1;
|
|
32
130
|
return;
|
|
@@ -41,8 +139,8 @@ function registerEmbeddings(program2) {
|
|
|
41
139
|
);
|
|
42
140
|
});
|
|
43
141
|
embeddings.command("query <text>").description("Run a semantic search against the local embeddings index").option("-d, --dir <dir>", "project root").option("--limit <n>", "max results", "10").option("--min-score <n>", "minimum cosine similarity (0-1)", "0").action(async (text, opts) => {
|
|
44
|
-
const root =
|
|
45
|
-
const paths =
|
|
142
|
+
const root = findProjectRoot2(opts.dir);
|
|
143
|
+
const paths = resolveHaivePaths2(root);
|
|
46
144
|
const { semanticSearch } = await loadEmbeddings();
|
|
47
145
|
const result = await semanticSearch(paths, text, {
|
|
48
146
|
limit: Number(opts.limit ?? 10),
|
|
@@ -64,8 +162,8 @@ function registerEmbeddings(program2) {
|
|
|
64
162
|
}
|
|
65
163
|
});
|
|
66
164
|
embeddings.command("status").description("Show the embeddings index status").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
67
|
-
const root =
|
|
68
|
-
const paths =
|
|
165
|
+
const root = findProjectRoot2(opts.dir);
|
|
166
|
+
const paths = resolveHaivePaths2(root);
|
|
69
167
|
const { indexStat } = await loadEmbeddings();
|
|
70
168
|
const stat = await indexStat(paths);
|
|
71
169
|
if (!stat.exists) {
|
|
@@ -95,8 +193,8 @@ import "commander";
|
|
|
95
193
|
import {
|
|
96
194
|
buildCodeMap,
|
|
97
195
|
codeMapPath,
|
|
98
|
-
findProjectRoot as
|
|
99
|
-
resolveHaivePaths as
|
|
196
|
+
findProjectRoot as findProjectRoot3,
|
|
197
|
+
resolveHaivePaths as resolveHaivePaths3,
|
|
100
198
|
saveCodeMap
|
|
101
199
|
} from "@hiveai/core";
|
|
102
200
|
function registerIndexCode(program2) {
|
|
@@ -107,8 +205,8 @@ function registerIndexCode(program2) {
|
|
|
107
205
|
"extra directory names to skip (comma-separated)",
|
|
108
206
|
""
|
|
109
207
|
).action(async (opts) => {
|
|
110
|
-
const root =
|
|
111
|
-
const paths =
|
|
208
|
+
const root = findProjectRoot3(opts.dir);
|
|
209
|
+
const paths = resolveHaivePaths3(root);
|
|
112
210
|
const extraExcludes = (opts.exclude ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
113
211
|
ui.info(`Indexing source files in ${root}\u2026`);
|
|
114
212
|
const map = await buildCodeMap(root, {
|
|
@@ -136,10 +234,10 @@ function registerIndexCode(program2) {
|
|
|
136
234
|
|
|
137
235
|
// src/commands/init.ts
|
|
138
236
|
import { mkdir, writeFile } from "fs/promises";
|
|
139
|
-
import { existsSync as
|
|
237
|
+
import { existsSync as existsSync3 } from "fs";
|
|
140
238
|
import path3 from "path";
|
|
141
239
|
import "commander";
|
|
142
|
-
import { resolveHaivePaths as
|
|
240
|
+
import { resolveHaivePaths as resolveHaivePaths4 } from "@hiveai/core";
|
|
143
241
|
var PROJECT_CONTEXT_TEMPLATE = `# Project context
|
|
144
242
|
|
|
145
243
|
> Generated by \`haive init\`. Edit this file (or let your AI agent fill it via the upcoming MCP \`bootstrap_project\` tool).
|
|
@@ -168,15 +266,15 @@ Memories live under \`.ai/memories/\` (personal/team/module).
|
|
|
168
266
|
function registerInit(program2) {
|
|
169
267
|
program2.command("init").description("Initialize a hAIve project (.ai/ structure + bridge files)").option("-d, --dir <dir>", "project root", process.cwd()).option("--no-bridges", "do not generate CLAUDE.md / .cursorrules / copilot-instructions.md").action(async (opts) => {
|
|
170
268
|
const root = path3.resolve(opts.dir);
|
|
171
|
-
const paths =
|
|
172
|
-
if (
|
|
269
|
+
const paths = resolveHaivePaths4(root);
|
|
270
|
+
if (existsSync3(paths.haiveDir)) {
|
|
173
271
|
ui.warn(`.ai/ already exists at ${paths.haiveDir} \u2014 leaving existing files in place.`);
|
|
174
272
|
}
|
|
175
273
|
await mkdir(paths.personalDir, { recursive: true });
|
|
176
274
|
await mkdir(paths.teamDir, { recursive: true });
|
|
177
275
|
await mkdir(paths.moduleDir, { recursive: true });
|
|
178
276
|
await mkdir(paths.modulesContextDir, { recursive: true });
|
|
179
|
-
if (!
|
|
277
|
+
if (!existsSync3(paths.projectContext)) {
|
|
180
278
|
await writeFile(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
|
|
181
279
|
ui.success(`Created ${path3.relative(root, paths.projectContext)}`);
|
|
182
280
|
}
|
|
@@ -191,7 +289,7 @@ function registerInit(program2) {
|
|
|
191
289
|
}
|
|
192
290
|
async function writeBridge(root, relPath) {
|
|
193
291
|
const target = path3.join(root, relPath);
|
|
194
|
-
if (
|
|
292
|
+
if (existsSync3(target)) {
|
|
195
293
|
ui.info(`Bridge ${relPath} already exists \u2014 skipped`);
|
|
196
294
|
return;
|
|
197
295
|
}
|
|
@@ -201,11 +299,11 @@ async function writeBridge(root, relPath) {
|
|
|
201
299
|
}
|
|
202
300
|
|
|
203
301
|
// src/commands/install-hooks.ts
|
|
204
|
-
import { mkdir as mkdir2, writeFile as writeFile2, chmod, readFile } from "fs/promises";
|
|
205
|
-
import { existsSync as
|
|
302
|
+
import { mkdir as mkdir2, writeFile as writeFile2, chmod, readFile as readFile2 } from "fs/promises";
|
|
303
|
+
import { existsSync as existsSync4 } from "fs";
|
|
206
304
|
import path4 from "path";
|
|
207
305
|
import "commander";
|
|
208
|
-
import { findProjectRoot as
|
|
306
|
+
import { findProjectRoot as findProjectRoot5 } from "@hiveai/core";
|
|
209
307
|
var HOOK_MARKER = "# hAIve auto-generated";
|
|
210
308
|
var HOOK_BODY = `#!/bin/sh
|
|
211
309
|
${HOOK_MARKER} \u2014 keep this block to allow upgrades. Hand-edit anything outside it.
|
|
@@ -221,9 +319,9 @@ fi
|
|
|
221
319
|
var HOOKS = ["post-merge", "post-rewrite"];
|
|
222
320
|
function registerInstallHooks(program2) {
|
|
223
321
|
program2.command("install-hooks").description("Install git hooks that run `haive sync` after pull/merge").option("-d, --dir <dir>", "project root").option("--force", "overwrite existing hooks").action(async (opts) => {
|
|
224
|
-
const root =
|
|
322
|
+
const root = findProjectRoot5(opts.dir);
|
|
225
323
|
const gitDir = path4.join(root, ".git");
|
|
226
|
-
if (!
|
|
324
|
+
if (!existsSync4(gitDir)) {
|
|
227
325
|
ui.error(`No .git directory at ${root}.`);
|
|
228
326
|
process.exitCode = 1;
|
|
229
327
|
return;
|
|
@@ -234,8 +332,8 @@ function registerInstallHooks(program2) {
|
|
|
234
332
|
let skipped = 0;
|
|
235
333
|
for (const name of HOOKS) {
|
|
236
334
|
const file = path4.join(hooksDir, name);
|
|
237
|
-
if (
|
|
238
|
-
const existing = await
|
|
335
|
+
if (existsSync4(file) && !opts.force) {
|
|
336
|
+
const existing = await readFile2(file, "utf8");
|
|
239
337
|
if (!existing.includes(HOOK_MARKER)) {
|
|
240
338
|
ui.warn(`${name} already exists and was not written by hAIve. Re-run with --force to overwrite.`);
|
|
241
339
|
skipped++;
|
|
@@ -253,16 +351,16 @@ function registerInstallHooks(program2) {
|
|
|
253
351
|
|
|
254
352
|
// src/commands/mcp.ts
|
|
255
353
|
import { spawn } from "child_process";
|
|
256
|
-
import { existsSync as
|
|
354
|
+
import { existsSync as existsSync5 } from "fs";
|
|
257
355
|
import { createRequire } from "module";
|
|
258
356
|
import path5 from "path";
|
|
259
357
|
import { fileURLToPath } from "url";
|
|
260
358
|
import "commander";
|
|
261
|
-
import { findProjectRoot as
|
|
359
|
+
import { findProjectRoot as findProjectRoot6 } from "@hiveai/core";
|
|
262
360
|
var require2 = createRequire(import.meta.url);
|
|
263
361
|
function registerMcp(program2) {
|
|
264
362
|
program2.command("mcp").description("Start the hAIve MCP server (stdio transport)").option("-d, --dir <dir>", "project root (defaults to nearest .ai/ or .git/)").action((opts) => {
|
|
265
|
-
const root =
|
|
363
|
+
const root = findProjectRoot6(opts.dir);
|
|
266
364
|
const bin = locateMcpBin();
|
|
267
365
|
if (!bin) {
|
|
268
366
|
ui.error(
|
|
@@ -282,29 +380,29 @@ function locateMcpBin() {
|
|
|
282
380
|
const pkgPath = require2.resolve("@hiveai/mcp/package.json");
|
|
283
381
|
const pkgDir = path5.dirname(pkgPath);
|
|
284
382
|
const candidate = path5.join(pkgDir, "dist", "index.js");
|
|
285
|
-
if (
|
|
383
|
+
if (existsSync5(candidate)) return candidate;
|
|
286
384
|
} catch {
|
|
287
385
|
}
|
|
288
386
|
const here = path5.dirname(fileURLToPath(import.meta.url));
|
|
289
387
|
const sibling = path5.resolve(here, "..", "..", "..", "mcp", "dist", "index.js");
|
|
290
|
-
if (
|
|
388
|
+
if (existsSync5(sibling)) return sibling;
|
|
291
389
|
return null;
|
|
292
390
|
}
|
|
293
391
|
|
|
294
392
|
// src/commands/sync.ts
|
|
295
393
|
import { spawnSync } from "child_process";
|
|
296
394
|
import { writeFile as writeFile3 } from "fs/promises";
|
|
297
|
-
import { existsSync as
|
|
395
|
+
import { existsSync as existsSync6 } from "fs";
|
|
298
396
|
import "path";
|
|
299
397
|
import "commander";
|
|
300
398
|
import {
|
|
301
399
|
DEFAULT_AUTO_PROMOTE_RULE,
|
|
302
|
-
findProjectRoot as
|
|
400
|
+
findProjectRoot as findProjectRoot7,
|
|
303
401
|
getUsage,
|
|
304
402
|
isAutoPromoteEligible,
|
|
305
|
-
loadMemoriesFromDir,
|
|
403
|
+
loadMemoriesFromDir as loadMemoriesFromDir2,
|
|
306
404
|
loadUsageIndex,
|
|
307
|
-
resolveHaivePaths as
|
|
405
|
+
resolveHaivePaths as resolveHaivePaths5,
|
|
308
406
|
serializeMemory,
|
|
309
407
|
verifyAnchor
|
|
310
408
|
} from "@hiveai/core";
|
|
@@ -313,9 +411,9 @@ function registerSync(program2) {
|
|
|
313
411
|
"--since <ref>",
|
|
314
412
|
"git ref/commit to compare against; report memories added/modified/removed since"
|
|
315
413
|
).option("--no-verify", "skip the anchor verification step").option("--no-promote", "skip the auto-promotion step").action(async (opts) => {
|
|
316
|
-
const root =
|
|
317
|
-
const paths =
|
|
318
|
-
if (!
|
|
414
|
+
const root = findProjectRoot7(opts.dir);
|
|
415
|
+
const paths = resolveHaivePaths5(root);
|
|
416
|
+
if (!existsSync6(paths.memoriesDir)) {
|
|
319
417
|
if (!opts.quiet) ui.warn(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
320
418
|
process.exitCode = 1;
|
|
321
419
|
return;
|
|
@@ -327,7 +425,7 @@ function registerSync(program2) {
|
|
|
327
425
|
let revalidated = 0;
|
|
328
426
|
let promoted = 0;
|
|
329
427
|
if (opts.verify !== false) {
|
|
330
|
-
const memories = await
|
|
428
|
+
const memories = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
331
429
|
for (const { memory: memory2, filePath } of memories) {
|
|
332
430
|
const isAnchored = memory2.frontmatter.anchor.paths.length > 0 || memory2.frontmatter.anchor.symbols.length > 0;
|
|
333
431
|
if (!isAnchored) continue;
|
|
@@ -369,7 +467,7 @@ function registerSync(program2) {
|
|
|
369
467
|
}
|
|
370
468
|
}
|
|
371
469
|
if (opts.promote !== false) {
|
|
372
|
-
const memories = await
|
|
470
|
+
const memories = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
373
471
|
const usage = await loadUsageIndex(paths);
|
|
374
472
|
for (const { memory: memory2, filePath } of memories) {
|
|
375
473
|
if (isAutoPromoteEligible(
|
|
@@ -390,9 +488,20 @@ function registerSync(program2) {
|
|
|
390
488
|
}
|
|
391
489
|
}
|
|
392
490
|
const sinceReport = opts.since ? collectSinceChanges(root, opts.since) : null;
|
|
491
|
+
const draftMemories = (await loadMemoriesFromDir2(paths.memoriesDir)).filter(
|
|
492
|
+
(m) => m.memory.frontmatter.status === "draft"
|
|
493
|
+
);
|
|
494
|
+
const draftCount = draftMemories.length;
|
|
393
495
|
log(
|
|
394
496
|
`${ui.dim("sync:")} ${staleMarked} stale \xB7 ${revalidated} revalidated \xB7 ${promoted} promoted${sinceReport ? ` \xB7 ${sinceReport.added.length}+/${sinceReport.modified.length}~/${sinceReport.removed.length}- since ${opts.since}` : ""}`
|
|
395
497
|
);
|
|
498
|
+
if (!opts.quiet && draftCount > 0) {
|
|
499
|
+
log(
|
|
500
|
+
ui.dim(
|
|
501
|
+
`\u2139 ${draftCount} memor${draftCount === 1 ? "y" : "ies"} in draft \u2014 run \`haive memory approve <id>\` to activate or \`haive memory list --status draft\` to review`
|
|
502
|
+
)
|
|
503
|
+
);
|
|
504
|
+
}
|
|
396
505
|
if (sinceReport && !opts.quiet) {
|
|
397
506
|
if (sinceReport.added.length > 0) {
|
|
398
507
|
log(ui.bold("\nNew memories:"));
|
|
@@ -430,28 +539,28 @@ function collectSinceChanges(root, ref) {
|
|
|
430
539
|
|
|
431
540
|
// src/commands/memory-add.ts
|
|
432
541
|
import { mkdir as mkdir3, writeFile as writeFile4 } from "fs/promises";
|
|
433
|
-
import { existsSync as
|
|
542
|
+
import { existsSync as existsSync7 } from "fs";
|
|
434
543
|
import path7 from "path";
|
|
435
544
|
import "commander";
|
|
436
545
|
import {
|
|
437
546
|
buildFrontmatter,
|
|
438
|
-
findProjectRoot as
|
|
547
|
+
findProjectRoot as findProjectRoot8,
|
|
439
548
|
inferModulesFromPaths,
|
|
440
549
|
memoryFilePath,
|
|
441
|
-
resolveHaivePaths as
|
|
550
|
+
resolveHaivePaths as resolveHaivePaths6,
|
|
442
551
|
serializeMemory as serializeMemory2
|
|
443
552
|
} from "@hiveai/core";
|
|
444
553
|
function registerMemoryAdd(memory2) {
|
|
445
|
-
memory2.command("add").description("Add a new memory (defaults to personal scope
|
|
446
|
-
const root =
|
|
447
|
-
const paths =
|
|
448
|
-
if (!
|
|
554
|
+
memory2.command("add").description("Add a new memory (defaults to personal scope)").requiredOption("--type <type>", "convention | decision | gotcha | architecture | glossary").requiredOption("--slug <slug>", "short 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", "personal").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags").option("--domain <domain>", "domain (e.g. transactions)").option("--author <author>", "author email or handle").option("--paths <csv>", "anchor paths, comma-separated").option("--symbols <csv>", "anchor symbols, comma-separated").option("--commit <sha>", "anchor commit SHA").option("--body <text>", "memory body content (Markdown) \u2014 overrides --title default body").option("--no-auto-tag", "disable automatic tag suggestions inferred from anchor paths").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
555
|
+
const root = findProjectRoot8(opts.dir);
|
|
556
|
+
const paths = resolveHaivePaths6(root);
|
|
557
|
+
if (!existsSync7(paths.haiveDir)) {
|
|
449
558
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
450
559
|
process.exitCode = 1;
|
|
451
560
|
return;
|
|
452
561
|
}
|
|
453
|
-
const userTags =
|
|
454
|
-
const anchorPaths =
|
|
562
|
+
const userTags = parseCsv2(opts.tags);
|
|
563
|
+
const anchorPaths = parseCsv2(opts.paths);
|
|
455
564
|
const autoTagsEnabled = opts.autoTag !== false;
|
|
456
565
|
const inferredTags = autoTagsEnabled ? inferModulesFromPaths(anchorPaths) : [];
|
|
457
566
|
const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
|
|
@@ -464,73 +573,112 @@ function registerMemoryAdd(memory2) {
|
|
|
464
573
|
domain: opts.domain,
|
|
465
574
|
author: opts.author,
|
|
466
575
|
paths: anchorPaths,
|
|
467
|
-
symbols:
|
|
576
|
+
symbols: parseCsv2(opts.symbols),
|
|
468
577
|
commit: opts.commit
|
|
469
578
|
});
|
|
470
|
-
const
|
|
579
|
+
const title = opts.title ?? opts.slug;
|
|
580
|
+
let body;
|
|
581
|
+
if (opts.body !== void 0) {
|
|
582
|
+
body = opts.title ? `# ${opts.title}
|
|
583
|
+
|
|
584
|
+
${opts.body}` : opts.body;
|
|
585
|
+
} else {
|
|
586
|
+
body = `# ${title}
|
|
471
587
|
|
|
472
588
|
TODO \u2014 write the memory body.
|
|
473
589
|
`;
|
|
590
|
+
}
|
|
474
591
|
const file = memoryFilePath(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
475
592
|
await mkdir3(path7.dirname(file), { recursive: true });
|
|
476
|
-
if (
|
|
593
|
+
if (existsSync7(file)) {
|
|
477
594
|
ui.error(`Memory already exists at ${file}`);
|
|
478
595
|
process.exitCode = 1;
|
|
479
596
|
return;
|
|
480
597
|
}
|
|
481
598
|
await writeFile4(file, serializeMemory2({ frontmatter, body }), "utf8");
|
|
482
599
|
ui.success(`Created ${path7.relative(root, file)}`);
|
|
483
|
-
ui.info(`
|
|
600
|
+
ui.info(`id=${frontmatter.id} scope=${frontmatter.scope} status=${frontmatter.status}`);
|
|
484
601
|
if (inferredTags.length > 0) {
|
|
485
602
|
ui.info(`auto-tagged: ${inferredTags.join(", ")} (use --no-auto-tag to disable)`);
|
|
486
603
|
}
|
|
604
|
+
if (frontmatter.scope === "personal") {
|
|
605
|
+
console.log(
|
|
606
|
+
ui.dim(
|
|
607
|
+
`\u2192 next: haive memory approve ${frontmatter.id} (activate) | haive memory promote ${frontmatter.id} (share with team)`
|
|
608
|
+
)
|
|
609
|
+
);
|
|
610
|
+
} else {
|
|
611
|
+
console.log(
|
|
612
|
+
ui.dim(`\u2192 next: haive memory approve ${frontmatter.id} (mark as validated)`)
|
|
613
|
+
);
|
|
614
|
+
}
|
|
487
615
|
});
|
|
488
616
|
}
|
|
489
|
-
function
|
|
617
|
+
function parseCsv2(value) {
|
|
490
618
|
if (!value) return [];
|
|
491
619
|
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
492
620
|
}
|
|
493
621
|
|
|
494
622
|
// src/commands/memory-list.ts
|
|
495
|
-
import { existsSync as
|
|
623
|
+
import { existsSync as existsSync8 } from "fs";
|
|
496
624
|
import path8 from "path";
|
|
497
625
|
import "commander";
|
|
498
|
-
import { findProjectRoot as
|
|
626
|
+
import { findProjectRoot as findProjectRoot9, resolveHaivePaths as resolveHaivePaths7 } from "@hiveai/core";
|
|
499
627
|
|
|
500
628
|
// src/utils/fs.ts
|
|
501
629
|
import {
|
|
502
|
-
loadMemoriesFromDir as
|
|
630
|
+
loadMemoriesFromDir as loadMemoriesFromDir3,
|
|
503
631
|
loadMemory,
|
|
504
632
|
listMarkdownFilesRecursive
|
|
505
633
|
} from "@hiveai/core";
|
|
506
634
|
|
|
507
635
|
// src/commands/memory-list.ts
|
|
508
636
|
function registerMemoryList(memory2) {
|
|
509
|
-
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("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
510
|
-
const root =
|
|
511
|
-
const paths =
|
|
512
|
-
if (!
|
|
637
|
+
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) => {
|
|
638
|
+
const root = findProjectRoot9(opts.dir);
|
|
639
|
+
const paths = resolveHaivePaths7(root);
|
|
640
|
+
if (!existsSync8(paths.memoriesDir)) {
|
|
513
641
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
514
642
|
process.exitCode = 1;
|
|
515
643
|
return;
|
|
516
644
|
}
|
|
517
|
-
const all = await
|
|
518
|
-
const
|
|
645
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
646
|
+
const statusFilter = opts.status ? opts.status.split(",").map((s) => s.trim()) : null;
|
|
647
|
+
const filtered = all.filter((m) => {
|
|
648
|
+
if (!matchesFilters(m, opts)) return false;
|
|
649
|
+
const status = m.memory.frontmatter.status;
|
|
650
|
+
if (!opts.showRejected && status === "rejected") return false;
|
|
651
|
+
if (statusFilter && !statusFilter.includes(status)) return false;
|
|
652
|
+
return true;
|
|
653
|
+
});
|
|
519
654
|
if (filtered.length === 0) {
|
|
520
655
|
ui.info("No memories match the filters.");
|
|
656
|
+
const rejectedCount = all.filter((m) => m.memory.frontmatter.status === "rejected").length;
|
|
657
|
+
if (rejectedCount > 0 && !opts.showRejected) {
|
|
658
|
+
ui.info(`(${rejectedCount} rejected hidden \u2014 use --show-rejected to include)`);
|
|
659
|
+
}
|
|
521
660
|
return;
|
|
522
661
|
}
|
|
523
662
|
for (const { memory: mem, filePath } of filtered) {
|
|
524
663
|
const fm = mem.frontmatter;
|
|
525
664
|
const tagStr = fm.tags.length ? ui.dim(` [${fm.tags.join(", ")}]`) : "";
|
|
526
665
|
const moduleStr = fm.module ? ui.dim(` (${fm.module})`) : "";
|
|
666
|
+
const statusBadge = ui.statusBadge(fm.status);
|
|
527
667
|
console.log(
|
|
528
|
-
`${ui.bold(fm.id)} ${ui.dim(fm.scope)}/${ui.dim(fm.type)}${moduleStr}${tagStr}`
|
|
668
|
+
`${ui.bold(fm.id)} ${ui.dim(fm.scope)}/${ui.dim(fm.type)} ${statusBadge}${moduleStr}${tagStr}`
|
|
529
669
|
);
|
|
530
670
|
console.log(` ${ui.dim(path8.relative(root, filePath))}`);
|
|
531
671
|
}
|
|
532
672
|
console.log(ui.dim(`
|
|
533
673
|
${filtered.length} memor${filtered.length === 1 ? "y" : "ies"}`));
|
|
674
|
+
const draftCount = filtered.filter((m) => m.memory.frontmatter.status === "draft").length;
|
|
675
|
+
if (draftCount > 0) {
|
|
676
|
+
console.log(
|
|
677
|
+
ui.dim(
|
|
678
|
+
`\u2139 ${draftCount} in draft \u2014 use \`haive memory approve <id>\` to activate or \`haive memory promote <id>\` to share with team`
|
|
679
|
+
)
|
|
680
|
+
);
|
|
681
|
+
}
|
|
534
682
|
});
|
|
535
683
|
}
|
|
536
684
|
function matchesFilters(loaded, opts) {
|
|
@@ -544,25 +692,39 @@ function matchesFilters(loaded, opts) {
|
|
|
544
692
|
|
|
545
693
|
// src/commands/memory-promote.ts
|
|
546
694
|
import { mkdir as mkdir4, unlink, writeFile as writeFile5 } from "fs/promises";
|
|
547
|
-
import { existsSync as
|
|
695
|
+
import { existsSync as existsSync9 } from "fs";
|
|
548
696
|
import path9 from "path";
|
|
549
697
|
import "commander";
|
|
550
698
|
import {
|
|
551
|
-
findProjectRoot as
|
|
699
|
+
findProjectRoot as findProjectRoot10,
|
|
552
700
|
memoryFilePath as memoryFilePath2,
|
|
553
|
-
resolveHaivePaths as
|
|
701
|
+
resolveHaivePaths as resolveHaivePaths8,
|
|
554
702
|
serializeMemory as serializeMemory3
|
|
555
703
|
} from "@hiveai/core";
|
|
556
704
|
function registerMemoryPromote(memory2) {
|
|
557
705
|
memory2.command("promote <id>").description("Promote a personal memory to team scope (status -> proposed)").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
558
|
-
const root =
|
|
559
|
-
const paths =
|
|
560
|
-
if (!
|
|
706
|
+
const root = findProjectRoot10(opts.dir);
|
|
707
|
+
const paths = resolveHaivePaths8(root);
|
|
708
|
+
if (!existsSync9(paths.memoriesDir)) {
|
|
561
709
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
562
710
|
process.exitCode = 1;
|
|
563
711
|
return;
|
|
564
712
|
}
|
|
565
|
-
const
|
|
713
|
+
const teamAndModule = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
714
|
+
const alreadyShared = teamAndModule.find(
|
|
715
|
+
(m) => m.memory.frontmatter.id === id && (m.memory.frontmatter.scope === "team" || m.memory.frontmatter.scope === "module")
|
|
716
|
+
);
|
|
717
|
+
if (alreadyShared) {
|
|
718
|
+
const fm = alreadyShared.memory.frontmatter;
|
|
719
|
+
ui.warn(
|
|
720
|
+
`"${id}" is already in ${fm.scope} scope (status=${fm.status}).`
|
|
721
|
+
);
|
|
722
|
+
if (fm.status !== "validated") {
|
|
723
|
+
ui.info(`\u2192 run \`haive memory approve ${id}\` to validate it`);
|
|
724
|
+
}
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
const all = await loadMemoriesFromDir3(paths.personalDir);
|
|
566
728
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
567
729
|
if (!found) {
|
|
568
730
|
ui.error(`No personal memory with id "${id}". (Promotion only applies to personal scope.)`);
|
|
@@ -583,29 +745,57 @@ function registerMemoryPromote(memory2) {
|
|
|
583
745
|
await unlink(found.filePath);
|
|
584
746
|
ui.success(`Promoted ${id} to team scope (status=proposed)`);
|
|
585
747
|
ui.info(`Now at ${path9.relative(root, newPath)}`);
|
|
748
|
+
console.log(ui.dim(`\u2192 next: haive memory approve ${id} (validate for team use)`));
|
|
586
749
|
});
|
|
587
750
|
}
|
|
588
751
|
|
|
589
752
|
// src/commands/memory-approve.ts
|
|
590
|
-
import { existsSync as
|
|
753
|
+
import { existsSync as existsSync10 } from "fs";
|
|
591
754
|
import { writeFile as writeFile6 } from "fs/promises";
|
|
592
755
|
import path10 from "path";
|
|
593
756
|
import "commander";
|
|
594
757
|
import {
|
|
595
|
-
findProjectRoot as
|
|
596
|
-
resolveHaivePaths as
|
|
758
|
+
findProjectRoot as findProjectRoot11,
|
|
759
|
+
resolveHaivePaths as resolveHaivePaths9,
|
|
597
760
|
serializeMemory as serializeMemory4
|
|
598
761
|
} from "@hiveai/core";
|
|
599
762
|
function registerMemoryApprove(memory2) {
|
|
600
|
-
memory2.command("approve
|
|
601
|
-
const root =
|
|
602
|
-
const paths =
|
|
603
|
-
if (!
|
|
763
|
+
memory2.command("approve [id]").description("Mark a memory as 'validated'. Use --all to bulk-approve all proposed/draft memories.").option("--all", "approve all proposed and draft memories at once").option("--pending", "approve all memories with status 'proposed'").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
764
|
+
const root = findProjectRoot11(opts.dir);
|
|
765
|
+
const paths = resolveHaivePaths9(root);
|
|
766
|
+
if (!existsSync10(paths.memoriesDir)) {
|
|
604
767
|
ui.error(`No .ai/memories at ${root}.`);
|
|
605
768
|
process.exitCode = 1;
|
|
606
769
|
return;
|
|
607
770
|
}
|
|
608
|
-
const all = await
|
|
771
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
772
|
+
if (opts.all || opts.pending) {
|
|
773
|
+
const candidates = all.filter((m) => {
|
|
774
|
+
const s = m.memory.frontmatter.status;
|
|
775
|
+
if (opts.all) return s === "proposed" || s === "draft";
|
|
776
|
+
return s === "proposed";
|
|
777
|
+
});
|
|
778
|
+
if (candidates.length === 0) {
|
|
779
|
+
ui.info(opts.all ? "No draft or proposed memories to approve." : "No proposed memories to approve.");
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
let count = 0;
|
|
783
|
+
for (const found2 of candidates) {
|
|
784
|
+
const next2 = {
|
|
785
|
+
frontmatter: { ...found2.memory.frontmatter, status: "validated" },
|
|
786
|
+
body: found2.memory.body
|
|
787
|
+
};
|
|
788
|
+
await writeFile6(found2.filePath, serializeMemory4(next2), "utf8");
|
|
789
|
+
count++;
|
|
790
|
+
}
|
|
791
|
+
ui.success(`Approved ${count} memor${count === 1 ? "y" : "ies"} (status=validated)`);
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
if (!id) {
|
|
795
|
+
ui.error("Provide a memory id or use --all / --pending for bulk approval.");
|
|
796
|
+
process.exitCode = 1;
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
609
799
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
610
800
|
if (!found) {
|
|
611
801
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -632,24 +822,24 @@ function registerMemoryApprove(memory2) {
|
|
|
632
822
|
|
|
633
823
|
// src/commands/memory-update.ts
|
|
634
824
|
import { writeFile as writeFile7 } from "fs/promises";
|
|
635
|
-
import { existsSync as
|
|
825
|
+
import { existsSync as existsSync11 } from "fs";
|
|
636
826
|
import path11 from "path";
|
|
637
827
|
import "commander";
|
|
638
828
|
import {
|
|
639
|
-
findProjectRoot as
|
|
640
|
-
resolveHaivePaths as
|
|
829
|
+
findProjectRoot as findProjectRoot12,
|
|
830
|
+
resolveHaivePaths as resolveHaivePaths10,
|
|
641
831
|
serializeMemory as serializeMemory5
|
|
642
832
|
} from "@hiveai/core";
|
|
643
833
|
function registerMemoryUpdate(memory2) {
|
|
644
|
-
memory2.command("update <id>").description("Update body, tags, or anchor of an existing memory (preserves id and usage history)").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) => {
|
|
645
|
-
const root =
|
|
646
|
-
const paths =
|
|
647
|
-
if (!
|
|
834
|
+
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) => {
|
|
835
|
+
const root = findProjectRoot12(opts.dir);
|
|
836
|
+
const paths = resolveHaivePaths10(root);
|
|
837
|
+
if (!existsSync11(paths.memoriesDir)) {
|
|
648
838
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
649
839
|
process.exitCode = 1;
|
|
650
840
|
return;
|
|
651
841
|
}
|
|
652
|
-
const memories = await
|
|
842
|
+
const memories = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
653
843
|
const loaded = memories.find((m) => m.memory.frontmatter.id === id);
|
|
654
844
|
if (!loaded) {
|
|
655
845
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -660,11 +850,11 @@ function registerMemoryUpdate(memory2) {
|
|
|
660
850
|
const { frontmatter, body } = loaded.memory;
|
|
661
851
|
const newAnchor = { ...frontmatter.anchor };
|
|
662
852
|
if (opts.paths !== void 0) {
|
|
663
|
-
newAnchor.paths =
|
|
853
|
+
newAnchor.paths = parseCsv3(opts.paths);
|
|
664
854
|
updated.push("anchor.paths");
|
|
665
855
|
}
|
|
666
856
|
if (opts.symbols !== void 0) {
|
|
667
|
-
newAnchor.symbols =
|
|
857
|
+
newAnchor.symbols = parseCsv3(opts.symbols);
|
|
668
858
|
updated.push("anchor.symbols");
|
|
669
859
|
}
|
|
670
860
|
if (opts.commit !== void 0) {
|
|
@@ -674,14 +864,18 @@ function registerMemoryUpdate(memory2) {
|
|
|
674
864
|
const newFrontmatter = {
|
|
675
865
|
...frontmatter,
|
|
676
866
|
anchor: newAnchor,
|
|
677
|
-
...opts.tags !== void 0 ? { tags:
|
|
867
|
+
...opts.tags !== void 0 ? { tags: parseCsv3(opts.tags) } : {},
|
|
678
868
|
...opts.domain !== void 0 ? { domain: opts.domain } : {},
|
|
679
869
|
...opts.author !== void 0 ? { author: opts.author } : {}
|
|
680
870
|
};
|
|
681
871
|
if (opts.tags !== void 0) updated.push("tags");
|
|
682
872
|
if (opts.domain !== void 0) updated.push("domain");
|
|
683
873
|
if (opts.author !== void 0) updated.push("author");
|
|
684
|
-
|
|
874
|
+
let newBody = opts.body !== void 0 ? opts.body : body;
|
|
875
|
+
if (opts.title !== void 0) {
|
|
876
|
+
newBody = replaceFirstHeading(newBody, opts.title);
|
|
877
|
+
updated.push("title");
|
|
878
|
+
}
|
|
685
879
|
if (opts.body !== void 0) updated.push("body");
|
|
686
880
|
if (updated.length === 0) {
|
|
687
881
|
ui.warn("Nothing to update \u2014 provide at least one option.");
|
|
@@ -696,22 +890,32 @@ function registerMemoryUpdate(memory2) {
|
|
|
696
890
|
ui.info(`fields: ${updated.join(", ")}`);
|
|
697
891
|
});
|
|
698
892
|
}
|
|
699
|
-
function
|
|
893
|
+
function replaceFirstHeading(body, title) {
|
|
894
|
+
const headingRe = /^#\s+.+$/m;
|
|
895
|
+
const replacement = `# ${title}`;
|
|
896
|
+
if (headingRe.test(body)) {
|
|
897
|
+
return body.replace(headingRe, replacement);
|
|
898
|
+
}
|
|
899
|
+
return `${replacement}
|
|
900
|
+
|
|
901
|
+
${body}`;
|
|
902
|
+
}
|
|
903
|
+
function parseCsv3(value) {
|
|
700
904
|
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
701
905
|
}
|
|
702
906
|
|
|
703
907
|
// src/commands/memory-auto-promote.ts
|
|
704
908
|
import { writeFile as writeFile8 } from "fs/promises";
|
|
705
|
-
import { existsSync as
|
|
909
|
+
import { existsSync as existsSync12 } from "fs";
|
|
706
910
|
import path12 from "path";
|
|
707
911
|
import "commander";
|
|
708
912
|
import {
|
|
709
913
|
DEFAULT_AUTO_PROMOTE_RULE as DEFAULT_AUTO_PROMOTE_RULE2,
|
|
710
|
-
findProjectRoot as
|
|
914
|
+
findProjectRoot as findProjectRoot13,
|
|
711
915
|
getUsage as getUsage2,
|
|
712
916
|
isAutoPromoteEligible as isAutoPromoteEligible2,
|
|
713
917
|
loadUsageIndex as loadUsageIndex2,
|
|
714
|
-
resolveHaivePaths as
|
|
918
|
+
resolveHaivePaths as resolveHaivePaths11,
|
|
715
919
|
serializeMemory as serializeMemory6
|
|
716
920
|
} from "@hiveai/core";
|
|
717
921
|
function registerMemoryAutoPromote(memory2) {
|
|
@@ -720,9 +924,9 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
720
924
|
"memories with more rejections than this are skipped",
|
|
721
925
|
String(DEFAULT_AUTO_PROMOTE_RULE2.maxRejections)
|
|
722
926
|
).option("--apply", "actually write status=validated to disk (default: dry-run)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
723
|
-
const root =
|
|
724
|
-
const paths =
|
|
725
|
-
if (!
|
|
927
|
+
const root = findProjectRoot13(opts.dir);
|
|
928
|
+
const paths = resolveHaivePaths11(root);
|
|
929
|
+
if (!existsSync12(paths.memoriesDir)) {
|
|
726
930
|
ui.error(`No .ai/memories at ${root}.`);
|
|
727
931
|
process.exitCode = 1;
|
|
728
932
|
return;
|
|
@@ -731,7 +935,7 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
731
935
|
minReads: Number(opts.minReads ?? DEFAULT_AUTO_PROMOTE_RULE2.minReads),
|
|
732
936
|
maxRejections: Number(opts.maxRejections ?? DEFAULT_AUTO_PROMOTE_RULE2.maxRejections)
|
|
733
937
|
};
|
|
734
|
-
const memories = await
|
|
938
|
+
const memories = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
735
939
|
const usage = await loadUsageIndex2(paths);
|
|
736
940
|
const eligible = memories.filter(
|
|
737
941
|
({ memory: memory3 }) => isAutoPromoteEligible2(memory3.frontmatter, getUsage2(usage, memory3.frontmatter.id), rule)
|
|
@@ -765,25 +969,25 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
765
969
|
|
|
766
970
|
// src/commands/memory-edit.ts
|
|
767
971
|
import { spawn as spawn2 } from "child_process";
|
|
768
|
-
import { existsSync as
|
|
769
|
-
import { readFile as
|
|
972
|
+
import { existsSync as existsSync13 } from "fs";
|
|
973
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
770
974
|
import path13 from "path";
|
|
771
975
|
import "commander";
|
|
772
976
|
import {
|
|
773
|
-
findProjectRoot as
|
|
977
|
+
findProjectRoot as findProjectRoot14,
|
|
774
978
|
parseMemory,
|
|
775
|
-
resolveHaivePaths as
|
|
979
|
+
resolveHaivePaths as resolveHaivePaths12
|
|
776
980
|
} from "@hiveai/core";
|
|
777
981
|
function registerMemoryEdit(memory2) {
|
|
778
982
|
memory2.command("edit <id>").description("Open a memory in $EDITOR and re-validate when you save").option("-e, --editor <cmd>", "editor command (defaults to $EDITOR or 'vi')").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
779
|
-
const root =
|
|
780
|
-
const paths =
|
|
781
|
-
if (!
|
|
983
|
+
const root = findProjectRoot14(opts.dir);
|
|
984
|
+
const paths = resolveHaivePaths12(root);
|
|
985
|
+
if (!existsSync13(paths.memoriesDir)) {
|
|
782
986
|
ui.error(`No .ai/memories at ${root}.`);
|
|
783
987
|
process.exitCode = 1;
|
|
784
988
|
return;
|
|
785
989
|
}
|
|
786
|
-
const all = await
|
|
990
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
787
991
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
788
992
|
if (!found) {
|
|
789
993
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -797,7 +1001,7 @@ function registerMemoryEdit(memory2) {
|
|
|
797
1001
|
ui.warn(`Editor exited with status ${code}.`);
|
|
798
1002
|
}
|
|
799
1003
|
try {
|
|
800
|
-
const fresh = await
|
|
1004
|
+
const fresh = await readFile3(found.filePath, "utf8");
|
|
801
1005
|
parseMemory(fresh);
|
|
802
1006
|
ui.success("Memory still parses cleanly.");
|
|
803
1007
|
} catch (err) {
|
|
@@ -818,28 +1022,28 @@ function runEditor(editor, file) {
|
|
|
818
1022
|
}
|
|
819
1023
|
|
|
820
1024
|
// src/commands/memory-for-files.ts
|
|
821
|
-
import { existsSync as
|
|
1025
|
+
import { existsSync as existsSync14 } from "fs";
|
|
822
1026
|
import path14 from "path";
|
|
823
1027
|
import "commander";
|
|
824
1028
|
import {
|
|
825
1029
|
deriveConfidence,
|
|
826
|
-
findProjectRoot as
|
|
1030
|
+
findProjectRoot as findProjectRoot15,
|
|
827
1031
|
getUsage as getUsage3,
|
|
828
1032
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
829
1033
|
loadUsageIndex as loadUsageIndex3,
|
|
830
|
-
memoryMatchesAnchorPaths,
|
|
831
|
-
resolveHaivePaths as
|
|
1034
|
+
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
1035
|
+
resolveHaivePaths as resolveHaivePaths13
|
|
832
1036
|
} from "@hiveai/core";
|
|
833
1037
|
function registerMemoryForFiles(memory2) {
|
|
834
1038
|
memory2.command("for-files <files...>").description("Show memories relevant to the given files (anchor overlap, module, domain)").option("-d, --dir <dir>", "project root").action(async (files, opts) => {
|
|
835
|
-
const root =
|
|
836
|
-
const paths =
|
|
837
|
-
if (!
|
|
1039
|
+
const root = findProjectRoot15(opts.dir);
|
|
1040
|
+
const paths = resolveHaivePaths13(root);
|
|
1041
|
+
if (!existsSync14(paths.memoriesDir)) {
|
|
838
1042
|
ui.error(`No .ai/memories at ${root}.`);
|
|
839
1043
|
process.exitCode = 1;
|
|
840
1044
|
return;
|
|
841
1045
|
}
|
|
842
|
-
const all = await
|
|
1046
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
843
1047
|
const usage = await loadUsageIndex3(paths);
|
|
844
1048
|
const inferred = inferModulesFromPaths2(files);
|
|
845
1049
|
const byAnchor = [];
|
|
@@ -847,7 +1051,7 @@ function registerMemoryForFiles(memory2) {
|
|
|
847
1051
|
const byDomain = [];
|
|
848
1052
|
const seen = /* @__PURE__ */ new Set();
|
|
849
1053
|
for (const loaded of all) {
|
|
850
|
-
if (
|
|
1054
|
+
if (memoryMatchesAnchorPaths2(loaded.memory, files)) {
|
|
851
1055
|
byAnchor.push(loaded);
|
|
852
1056
|
seen.add(loaded.memory.frontmatter.id);
|
|
853
1057
|
}
|
|
@@ -892,26 +1096,26 @@ function printGroup(root, label, loaded, usage) {
|
|
|
892
1096
|
}
|
|
893
1097
|
|
|
894
1098
|
// src/commands/memory-hot.ts
|
|
895
|
-
import { existsSync as
|
|
1099
|
+
import { existsSync as existsSync15 } from "fs";
|
|
896
1100
|
import path15 from "path";
|
|
897
1101
|
import "commander";
|
|
898
1102
|
import {
|
|
899
|
-
findProjectRoot as
|
|
1103
|
+
findProjectRoot as findProjectRoot16,
|
|
900
1104
|
getUsage as getUsage4,
|
|
901
1105
|
loadUsageIndex as loadUsageIndex4,
|
|
902
|
-
resolveHaivePaths as
|
|
1106
|
+
resolveHaivePaths as resolveHaivePaths14
|
|
903
1107
|
} from "@hiveai/core";
|
|
904
1108
|
function registerMemoryHot(memory2) {
|
|
905
1109
|
memory2.command("hot").description("List memories actively used but not yet validated (good promotion candidates)").option("--threshold <n>", "minimum read_count to qualify", "3").option("--status <status>", "limit to one status (default: draft + proposed)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
906
|
-
const root =
|
|
907
|
-
const paths =
|
|
908
|
-
if (!
|
|
1110
|
+
const root = findProjectRoot16(opts.dir);
|
|
1111
|
+
const paths = resolveHaivePaths14(root);
|
|
1112
|
+
if (!existsSync15(paths.memoriesDir)) {
|
|
909
1113
|
ui.error(`No .ai/memories at ${root}.`);
|
|
910
1114
|
process.exitCode = 1;
|
|
911
1115
|
return;
|
|
912
1116
|
}
|
|
913
1117
|
const threshold = Math.max(1, Number(opts.threshold ?? 3));
|
|
914
|
-
const all = await
|
|
1118
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
915
1119
|
const usage = await loadUsageIndex4(paths);
|
|
916
1120
|
const candidates = all.filter(({ memory: mem }) => {
|
|
917
1121
|
const fm = mem.frontmatter;
|
|
@@ -942,25 +1146,25 @@ function registerMemoryHot(memory2) {
|
|
|
942
1146
|
}
|
|
943
1147
|
|
|
944
1148
|
// src/commands/memory-pending.ts
|
|
945
|
-
import { existsSync as
|
|
1149
|
+
import { existsSync as existsSync16 } from "fs";
|
|
946
1150
|
import path16 from "path";
|
|
947
1151
|
import "commander";
|
|
948
1152
|
import {
|
|
949
|
-
findProjectRoot as
|
|
1153
|
+
findProjectRoot as findProjectRoot17,
|
|
950
1154
|
getUsage as getUsage5,
|
|
951
1155
|
loadUsageIndex as loadUsageIndex5,
|
|
952
|
-
resolveHaivePaths as
|
|
1156
|
+
resolveHaivePaths as resolveHaivePaths15
|
|
953
1157
|
} from "@hiveai/core";
|
|
954
1158
|
function registerMemoryPending(memory2) {
|
|
955
1159
|
memory2.command("pending").description("List 'proposed' memories awaiting review (sorted by reads desc)").option("--scope <scope>", "filter by scope (personal | team | module)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
956
|
-
const root =
|
|
957
|
-
const paths =
|
|
958
|
-
if (!
|
|
1160
|
+
const root = findProjectRoot17(opts.dir);
|
|
1161
|
+
const paths = resolveHaivePaths15(root);
|
|
1162
|
+
if (!existsSync16(paths.memoriesDir)) {
|
|
959
1163
|
ui.error(`No .ai/memories at ${root}.`);
|
|
960
1164
|
process.exitCode = 1;
|
|
961
1165
|
return;
|
|
962
1166
|
}
|
|
963
|
-
const all = await
|
|
1167
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
964
1168
|
const usage = await loadUsageIndex5(paths);
|
|
965
1169
|
const proposed = all.filter(({ memory: mem }) => {
|
|
966
1170
|
if (mem.frontmatter.status !== "proposed") return false;
|
|
@@ -990,29 +1194,35 @@ function registerMemoryPending(memory2) {
|
|
|
990
1194
|
}
|
|
991
1195
|
|
|
992
1196
|
// src/commands/memory-query.ts
|
|
993
|
-
import { existsSync as
|
|
1197
|
+
import { existsSync as existsSync17 } from "fs";
|
|
994
1198
|
import path17 from "path";
|
|
995
1199
|
import "commander";
|
|
996
1200
|
import {
|
|
997
1201
|
extractSnippet,
|
|
998
|
-
findProjectRoot as
|
|
999
|
-
literalMatchesAllTokens,
|
|
1202
|
+
findProjectRoot as findProjectRoot18,
|
|
1203
|
+
literalMatchesAllTokens as literalMatchesAllTokens2,
|
|
1000
1204
|
pickSnippetNeedle,
|
|
1001
|
-
resolveHaivePaths as
|
|
1002
|
-
tokenizeQuery
|
|
1205
|
+
resolveHaivePaths as resolveHaivePaths16,
|
|
1206
|
+
tokenizeQuery as tokenizeQuery2
|
|
1003
1207
|
} from "@hiveai/core";
|
|
1004
1208
|
function registerMemoryQuery(memory2) {
|
|
1005
|
-
memory2.command("query <text>").description("Search memories by id, tag, or substring (multi-word AND)").option("-d, --dir <dir>", "project root").option("--limit <n>", "max results", "20").action(async (text, opts) => {
|
|
1006
|
-
const root =
|
|
1007
|
-
const paths =
|
|
1008
|
-
if (!
|
|
1209
|
+
memory2.command("query <text>").description("Search memories by id, tag, or substring (multi-word AND)").option("-d, --dir <dir>", "project root").option("--limit <n>", "max results", "20").option("--scope <scope>", "personal | team | module").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected)").action(async (text, opts) => {
|
|
1210
|
+
const root = findProjectRoot18(opts.dir);
|
|
1211
|
+
const paths = resolveHaivePaths16(root);
|
|
1212
|
+
if (!existsSync17(paths.memoriesDir)) {
|
|
1009
1213
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
1010
1214
|
process.exitCode = 1;
|
|
1011
1215
|
return;
|
|
1012
1216
|
}
|
|
1013
|
-
const tokens =
|
|
1014
|
-
const
|
|
1015
|
-
const
|
|
1217
|
+
const tokens = tokenizeQuery2(text);
|
|
1218
|
+
const statusFilter = opts.status ? opts.status.split(",").map((s) => s.trim()) : null;
|
|
1219
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
1220
|
+
const matches = all.filter(({ memory: mem }) => {
|
|
1221
|
+
const fm = mem.frontmatter;
|
|
1222
|
+
if (opts.scope && fm.scope !== opts.scope) return false;
|
|
1223
|
+
if (statusFilter && !statusFilter.includes(fm.status)) return false;
|
|
1224
|
+
return literalMatchesAllTokens2(mem, tokens);
|
|
1225
|
+
});
|
|
1016
1226
|
const limit = Math.max(1, Number(opts.limit ?? 20));
|
|
1017
1227
|
const top = matches.slice(0, limit);
|
|
1018
1228
|
if (top.length === 0) {
|
|
@@ -1021,9 +1231,11 @@ function registerMemoryQuery(memory2) {
|
|
|
1021
1231
|
}
|
|
1022
1232
|
const snippetNeedle = pickSnippetNeedle(text);
|
|
1023
1233
|
for (const { memory: mem, filePath } of top) {
|
|
1024
|
-
const
|
|
1025
|
-
|
|
1234
|
+
const fm = mem.frontmatter;
|
|
1235
|
+
const statusBadge = ui.statusBadge(fm.status);
|
|
1236
|
+
console.log(`${ui.bold(fm.id)} ${ui.dim(fm.scope)} ${statusBadge}`);
|
|
1026
1237
|
console.log(` ${ui.dim(path17.relative(root, filePath))}`);
|
|
1238
|
+
const snippet = extractSnippet(mem.body, snippetNeedle);
|
|
1027
1239
|
if (snippet) console.log(` ${snippet}`);
|
|
1028
1240
|
}
|
|
1029
1241
|
console.log(
|
|
@@ -1035,26 +1247,26 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
|
|
|
1035
1247
|
|
|
1036
1248
|
// src/commands/memory-reject.ts
|
|
1037
1249
|
import { writeFile as writeFile9 } from "fs/promises";
|
|
1038
|
-
import { existsSync as
|
|
1250
|
+
import { existsSync as existsSync18 } from "fs";
|
|
1039
1251
|
import "commander";
|
|
1040
1252
|
import {
|
|
1041
|
-
findProjectRoot as
|
|
1253
|
+
findProjectRoot as findProjectRoot19,
|
|
1042
1254
|
loadUsageIndex as loadUsageIndex6,
|
|
1043
1255
|
recordRejection,
|
|
1044
|
-
resolveHaivePaths as
|
|
1256
|
+
resolveHaivePaths as resolveHaivePaths17,
|
|
1045
1257
|
saveUsageIndex,
|
|
1046
1258
|
serializeMemory as serializeMemory7
|
|
1047
1259
|
} from "@hiveai/core";
|
|
1048
1260
|
function registerMemoryReject(memory2) {
|
|
1049
1261
|
memory2.command("reject <id>").description("Record a rejection (blocks auto-promotion and lowers confidence)").option("-r, --reason <reason>", "why this memory is being rejected").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
1050
|
-
const root =
|
|
1051
|
-
const paths =
|
|
1052
|
-
if (!
|
|
1262
|
+
const root = findProjectRoot19(opts.dir);
|
|
1263
|
+
const paths = resolveHaivePaths17(root);
|
|
1264
|
+
if (!existsSync18(paths.memoriesDir)) {
|
|
1053
1265
|
ui.error(`No .ai/memories at ${root}.`);
|
|
1054
1266
|
process.exitCode = 1;
|
|
1055
1267
|
return;
|
|
1056
1268
|
}
|
|
1057
|
-
const memories = await
|
|
1269
|
+
const memories = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
1058
1270
|
const loaded = memories.find((m) => m.memory.frontmatter.id === id);
|
|
1059
1271
|
if (!loaded) {
|
|
1060
1272
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -1081,27 +1293,27 @@ function registerMemoryReject(memory2) {
|
|
|
1081
1293
|
}
|
|
1082
1294
|
|
|
1083
1295
|
// src/commands/memory-rm.ts
|
|
1084
|
-
import { existsSync as
|
|
1296
|
+
import { existsSync as existsSync19 } from "fs";
|
|
1085
1297
|
import { unlink as unlink2 } from "fs/promises";
|
|
1086
1298
|
import path18 from "path";
|
|
1087
1299
|
import { createInterface } from "readline/promises";
|
|
1088
1300
|
import "commander";
|
|
1089
1301
|
import {
|
|
1090
|
-
findProjectRoot as
|
|
1302
|
+
findProjectRoot as findProjectRoot20,
|
|
1091
1303
|
loadUsageIndex as loadUsageIndex7,
|
|
1092
|
-
resolveHaivePaths as
|
|
1304
|
+
resolveHaivePaths as resolveHaivePaths18,
|
|
1093
1305
|
saveUsageIndex as saveUsageIndex2
|
|
1094
1306
|
} from "@hiveai/core";
|
|
1095
1307
|
function registerMemoryRm(memory2) {
|
|
1096
1308
|
memory2.command("rm <id>").description("Delete a memory file (and its usage entry by default)").option("-y, --yes", "skip the confirmation prompt").option("--keep-usage", "do not remove the usage.json entry").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
1097
|
-
const root =
|
|
1098
|
-
const paths =
|
|
1099
|
-
if (!
|
|
1309
|
+
const root = findProjectRoot20(opts.dir);
|
|
1310
|
+
const paths = resolveHaivePaths18(root);
|
|
1311
|
+
if (!existsSync19(paths.memoriesDir)) {
|
|
1100
1312
|
ui.error(`No .ai/memories at ${root}.`);
|
|
1101
1313
|
process.exitCode = 1;
|
|
1102
1314
|
return;
|
|
1103
1315
|
}
|
|
1104
|
-
const all = await
|
|
1316
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
1105
1317
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
1106
1318
|
if (!found) {
|
|
1107
1319
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -1132,27 +1344,27 @@ function registerMemoryRm(memory2) {
|
|
|
1132
1344
|
}
|
|
1133
1345
|
|
|
1134
1346
|
// src/commands/memory-show.ts
|
|
1135
|
-
import { existsSync as
|
|
1136
|
-
import { readFile as
|
|
1347
|
+
import { existsSync as existsSync20 } from "fs";
|
|
1348
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1137
1349
|
import path19 from "path";
|
|
1138
1350
|
import "commander";
|
|
1139
1351
|
import {
|
|
1140
1352
|
deriveConfidence as deriveConfidence2,
|
|
1141
|
-
findProjectRoot as
|
|
1353
|
+
findProjectRoot as findProjectRoot21,
|
|
1142
1354
|
getUsage as getUsage6,
|
|
1143
1355
|
loadUsageIndex as loadUsageIndex8,
|
|
1144
|
-
resolveHaivePaths as
|
|
1356
|
+
resolveHaivePaths as resolveHaivePaths19
|
|
1145
1357
|
} from "@hiveai/core";
|
|
1146
1358
|
function registerMemoryShow(memory2) {
|
|
1147
1359
|
memory2.command("show <id>").description("Print a memory's frontmatter, body, and confidence/usage").option("--raw", "print the raw file contents instead of a summary").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
1148
|
-
const root =
|
|
1149
|
-
const paths =
|
|
1150
|
-
if (!
|
|
1360
|
+
const root = findProjectRoot21(opts.dir);
|
|
1361
|
+
const paths = resolveHaivePaths19(root);
|
|
1362
|
+
if (!existsSync20(paths.memoriesDir)) {
|
|
1151
1363
|
ui.error(`No .ai/memories at ${root}.`);
|
|
1152
1364
|
process.exitCode = 1;
|
|
1153
1365
|
return;
|
|
1154
1366
|
}
|
|
1155
|
-
const all = await
|
|
1367
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
1156
1368
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
1157
1369
|
if (!found) {
|
|
1158
1370
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -1160,7 +1372,7 @@ function registerMemoryShow(memory2) {
|
|
|
1160
1372
|
return;
|
|
1161
1373
|
}
|
|
1162
1374
|
if (opts.raw) {
|
|
1163
|
-
console.log(await
|
|
1375
|
+
console.log(await readFile4(found.filePath, "utf8"));
|
|
1164
1376
|
return;
|
|
1165
1377
|
}
|
|
1166
1378
|
const fm = found.memory.frontmatter;
|
|
@@ -1191,26 +1403,26 @@ function registerMemoryShow(memory2) {
|
|
|
1191
1403
|
}
|
|
1192
1404
|
|
|
1193
1405
|
// src/commands/memory-stats.ts
|
|
1194
|
-
import { existsSync as
|
|
1406
|
+
import { existsSync as existsSync21 } from "fs";
|
|
1195
1407
|
import path20 from "path";
|
|
1196
1408
|
import "commander";
|
|
1197
1409
|
import {
|
|
1198
1410
|
deriveConfidence as deriveConfidence3,
|
|
1199
|
-
findProjectRoot as
|
|
1411
|
+
findProjectRoot as findProjectRoot22,
|
|
1200
1412
|
getUsage as getUsage7,
|
|
1201
1413
|
loadUsageIndex as loadUsageIndex9,
|
|
1202
|
-
resolveHaivePaths as
|
|
1414
|
+
resolveHaivePaths as resolveHaivePaths20
|
|
1203
1415
|
} from "@hiveai/core";
|
|
1204
1416
|
function registerMemoryStats(memory2) {
|
|
1205
1417
|
memory2.command("stats").description("Show usage stats and confidence levels per memory").option("--id <id>", "show stats for a single memory id").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
1206
|
-
const root =
|
|
1207
|
-
const paths =
|
|
1208
|
-
if (!
|
|
1418
|
+
const root = findProjectRoot22(opts.dir);
|
|
1419
|
+
const paths = resolveHaivePaths20(root);
|
|
1420
|
+
if (!existsSync21(paths.memoriesDir)) {
|
|
1209
1421
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
1210
1422
|
process.exitCode = 1;
|
|
1211
1423
|
return;
|
|
1212
1424
|
}
|
|
1213
|
-
const all = await
|
|
1425
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
1214
1426
|
const usage = await loadUsageIndex9(paths);
|
|
1215
1427
|
const target = opts.id ? all.filter((m) => m.memory.frontmatter.id === opts.id) : all;
|
|
1216
1428
|
if (target.length === 0) {
|
|
@@ -1237,25 +1449,25 @@ function registerMemoryStats(memory2) {
|
|
|
1237
1449
|
|
|
1238
1450
|
// src/commands/memory-verify.ts
|
|
1239
1451
|
import { writeFile as writeFile10 } from "fs/promises";
|
|
1240
|
-
import { existsSync as
|
|
1452
|
+
import { existsSync as existsSync22 } from "fs";
|
|
1241
1453
|
import path21 from "path";
|
|
1242
1454
|
import "commander";
|
|
1243
1455
|
import {
|
|
1244
|
-
findProjectRoot as
|
|
1245
|
-
resolveHaivePaths as
|
|
1456
|
+
findProjectRoot as findProjectRoot23,
|
|
1457
|
+
resolveHaivePaths as resolveHaivePaths21,
|
|
1246
1458
|
serializeMemory as serializeMemory8,
|
|
1247
1459
|
verifyAnchor as verifyAnchor2
|
|
1248
1460
|
} from "@hiveai/core";
|
|
1249
1461
|
function registerMemoryVerify(memory2) {
|
|
1250
1462
|
memory2.command("verify").description("Check memory anchors against current code, optionally marking stale ones").option("--id <id>", "verify a single memory by id").option("--all", "verify every memory (default if --id is omitted)").option("--update", "write status=stale (or status=validated for re-freshed) back to disk").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
1251
|
-
const root =
|
|
1252
|
-
const paths =
|
|
1253
|
-
if (!
|
|
1463
|
+
const root = findProjectRoot23(opts.dir);
|
|
1464
|
+
const paths = resolveHaivePaths21(root);
|
|
1465
|
+
if (!existsSync22(paths.memoriesDir)) {
|
|
1254
1466
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
1255
1467
|
process.exitCode = 1;
|
|
1256
1468
|
return;
|
|
1257
1469
|
}
|
|
1258
|
-
const all = await
|
|
1470
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
1259
1471
|
const targets = opts.id ? all.filter((m) => m.memory.frontmatter.id === opts.id) : all;
|
|
1260
1472
|
if (opts.id && targets.length === 0) {
|
|
1261
1473
|
ui.error(`No memory with id "${opts.id}".`);
|
|
@@ -1324,10 +1536,11 @@ function applyVerification(mem, result) {
|
|
|
1324
1536
|
}
|
|
1325
1537
|
|
|
1326
1538
|
// src/index.ts
|
|
1327
|
-
var program = new
|
|
1328
|
-
program.name("haive").description("hAIve \u2014 team-first persistent memory layer for AI coding agents").version("0.2.
|
|
1539
|
+
var program = new Command24();
|
|
1540
|
+
program.name("haive").description("hAIve \u2014 team-first persistent memory layer for AI coding agents").version("0.2.3");
|
|
1329
1541
|
registerInit(program);
|
|
1330
1542
|
registerMcp(program);
|
|
1543
|
+
registerBriefing(program);
|
|
1331
1544
|
registerEmbeddings(program);
|
|
1332
1545
|
registerSync(program);
|
|
1333
1546
|
registerInstallHooks(program);
|