@hiveai/cli 0.2.2 → 0.2.4
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 +427 -188
- 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,115 @@ 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 | all (default: team)",
|
|
56
|
+
"team"
|
|
57
|
+
).option("--include-draft", "include draft memories (excluded by default)").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 (!opts.includeDraft && fm.status === "draft") return false;
|
|
81
|
+
if (scopeFilter !== "all" && fm.scope !== scopeFilter) return false;
|
|
82
|
+
return true;
|
|
83
|
+
});
|
|
84
|
+
const scored = candidates.map(({ memory: mem, filePath }) => {
|
|
85
|
+
const fm = mem.frontmatter;
|
|
86
|
+
let score = 0;
|
|
87
|
+
if (fm.status === "validated") score += 3;
|
|
88
|
+
else if (fm.status === "proposed") score += 1;
|
|
89
|
+
if (filePaths.length > 0 && memoryMatchesAnchorPaths(mem, filePaths)) score += 4;
|
|
90
|
+
if (tokens && literalMatchesAllTokens(mem, tokens)) score += 3;
|
|
91
|
+
return { memory: mem, filePath, score };
|
|
92
|
+
});
|
|
93
|
+
scored.sort((a, b) => b.score - a.score);
|
|
94
|
+
const top = scored.slice(0, maxMemories);
|
|
95
|
+
if (top.length === 0) {
|
|
96
|
+
ui.info("No relevant memories found.");
|
|
97
|
+
const draftCount = all.filter(
|
|
98
|
+
(m) => m.memory.frontmatter.status === "draft" && (scopeFilter === "all" || m.memory.frontmatter.scope === scopeFilter)
|
|
99
|
+
).length;
|
|
100
|
+
if (draftCount > 0) {
|
|
101
|
+
ui.info(`(${draftCount} draft memories excluded \u2014 use --include-draft to show)`);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
console.log(`${ui.bold("=== Relevant Memories ===")}
|
|
106
|
+
`);
|
|
107
|
+
for (const { memory: mem } of top) {
|
|
108
|
+
const fm = mem.frontmatter;
|
|
109
|
+
const badge = ui.statusBadge(fm.status);
|
|
110
|
+
const draftMarker = fm.status === "draft" ? ui.yellow(" [DRAFT]") : "";
|
|
111
|
+
console.log(
|
|
112
|
+
`${ui.bold(fm.id)} ${ui.dim(fm.scope + "/" + fm.type)} ${badge}${draftMarker}`
|
|
113
|
+
);
|
|
114
|
+
console.log(mem.body.trim());
|
|
115
|
+
console.log();
|
|
116
|
+
}
|
|
117
|
+
console.log(ui.dim(`${top.length} memor${top.length === 1 ? "y" : "ies"} surfaced`));
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function parseCsv(value) {
|
|
121
|
+
if (!value) return [];
|
|
122
|
+
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
123
|
+
}
|
|
124
|
+
|
|
23
125
|
// src/commands/embeddings.ts
|
|
126
|
+
import { existsSync as existsSync2 } from "fs";
|
|
127
|
+
import path from "path";
|
|
128
|
+
import "commander";
|
|
129
|
+
import { findProjectRoot as findProjectRoot2, resolveHaivePaths as resolveHaivePaths2 } from "@hiveai/core";
|
|
24
130
|
function registerEmbeddings(program2) {
|
|
25
131
|
const embeddings = program2.command("embeddings").description("Manage local embeddings index for semantic search");
|
|
26
132
|
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 (!
|
|
133
|
+
const root = findProjectRoot2(opts.dir);
|
|
134
|
+
const paths = resolveHaivePaths2(root);
|
|
135
|
+
if (!existsSync2(paths.memoriesDir)) {
|
|
30
136
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
31
137
|
process.exitCode = 1;
|
|
32
138
|
return;
|
|
@@ -41,8 +147,8 @@ function registerEmbeddings(program2) {
|
|
|
41
147
|
);
|
|
42
148
|
});
|
|
43
149
|
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 =
|
|
150
|
+
const root = findProjectRoot2(opts.dir);
|
|
151
|
+
const paths = resolveHaivePaths2(root);
|
|
46
152
|
const { semanticSearch } = await loadEmbeddings();
|
|
47
153
|
const result = await semanticSearch(paths, text, {
|
|
48
154
|
limit: Number(opts.limit ?? 10),
|
|
@@ -64,8 +170,8 @@ function registerEmbeddings(program2) {
|
|
|
64
170
|
}
|
|
65
171
|
});
|
|
66
172
|
embeddings.command("status").description("Show the embeddings index status").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
67
|
-
const root =
|
|
68
|
-
const paths =
|
|
173
|
+
const root = findProjectRoot2(opts.dir);
|
|
174
|
+
const paths = resolveHaivePaths2(root);
|
|
69
175
|
const { indexStat } = await loadEmbeddings();
|
|
70
176
|
const stat = await indexStat(paths);
|
|
71
177
|
if (!stat.exists) {
|
|
@@ -95,8 +201,8 @@ import "commander";
|
|
|
95
201
|
import {
|
|
96
202
|
buildCodeMap,
|
|
97
203
|
codeMapPath,
|
|
98
|
-
findProjectRoot as
|
|
99
|
-
resolveHaivePaths as
|
|
204
|
+
findProjectRoot as findProjectRoot3,
|
|
205
|
+
resolveHaivePaths as resolveHaivePaths3,
|
|
100
206
|
saveCodeMap
|
|
101
207
|
} from "@hiveai/core";
|
|
102
208
|
function registerIndexCode(program2) {
|
|
@@ -107,8 +213,8 @@ function registerIndexCode(program2) {
|
|
|
107
213
|
"extra directory names to skip (comma-separated)",
|
|
108
214
|
""
|
|
109
215
|
).action(async (opts) => {
|
|
110
|
-
const root =
|
|
111
|
-
const paths =
|
|
216
|
+
const root = findProjectRoot3(opts.dir);
|
|
217
|
+
const paths = resolveHaivePaths3(root);
|
|
112
218
|
const extraExcludes = (opts.exclude ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
113
219
|
ui.info(`Indexing source files in ${root}\u2026`);
|
|
114
220
|
const map = await buildCodeMap(root, {
|
|
@@ -136,10 +242,10 @@ function registerIndexCode(program2) {
|
|
|
136
242
|
|
|
137
243
|
// src/commands/init.ts
|
|
138
244
|
import { mkdir, writeFile } from "fs/promises";
|
|
139
|
-
import { existsSync as
|
|
245
|
+
import { existsSync as existsSync3 } from "fs";
|
|
140
246
|
import path3 from "path";
|
|
141
247
|
import "commander";
|
|
142
|
-
import { resolveHaivePaths as
|
|
248
|
+
import { resolveHaivePaths as resolveHaivePaths4 } from "@hiveai/core";
|
|
143
249
|
var PROJECT_CONTEXT_TEMPLATE = `# Project context
|
|
144
250
|
|
|
145
251
|
> Generated by \`haive init\`. Edit this file (or let your AI agent fill it via the upcoming MCP \`bootstrap_project\` tool).
|
|
@@ -168,15 +274,15 @@ Memories live under \`.ai/memories/\` (personal/team/module).
|
|
|
168
274
|
function registerInit(program2) {
|
|
169
275
|
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
276
|
const root = path3.resolve(opts.dir);
|
|
171
|
-
const paths =
|
|
172
|
-
if (
|
|
277
|
+
const paths = resolveHaivePaths4(root);
|
|
278
|
+
if (existsSync3(paths.haiveDir)) {
|
|
173
279
|
ui.warn(`.ai/ already exists at ${paths.haiveDir} \u2014 leaving existing files in place.`);
|
|
174
280
|
}
|
|
175
281
|
await mkdir(paths.personalDir, { recursive: true });
|
|
176
282
|
await mkdir(paths.teamDir, { recursive: true });
|
|
177
283
|
await mkdir(paths.moduleDir, { recursive: true });
|
|
178
284
|
await mkdir(paths.modulesContextDir, { recursive: true });
|
|
179
|
-
if (!
|
|
285
|
+
if (!existsSync3(paths.projectContext)) {
|
|
180
286
|
await writeFile(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
|
|
181
287
|
ui.success(`Created ${path3.relative(root, paths.projectContext)}`);
|
|
182
288
|
}
|
|
@@ -191,7 +297,7 @@ function registerInit(program2) {
|
|
|
191
297
|
}
|
|
192
298
|
async function writeBridge(root, relPath) {
|
|
193
299
|
const target = path3.join(root, relPath);
|
|
194
|
-
if (
|
|
300
|
+
if (existsSync3(target)) {
|
|
195
301
|
ui.info(`Bridge ${relPath} already exists \u2014 skipped`);
|
|
196
302
|
return;
|
|
197
303
|
}
|
|
@@ -201,11 +307,11 @@ async function writeBridge(root, relPath) {
|
|
|
201
307
|
}
|
|
202
308
|
|
|
203
309
|
// src/commands/install-hooks.ts
|
|
204
|
-
import { mkdir as mkdir2, writeFile as writeFile2, chmod, readFile } from "fs/promises";
|
|
205
|
-
import { existsSync as
|
|
310
|
+
import { mkdir as mkdir2, writeFile as writeFile2, chmod, readFile as readFile2 } from "fs/promises";
|
|
311
|
+
import { existsSync as existsSync4 } from "fs";
|
|
206
312
|
import path4 from "path";
|
|
207
313
|
import "commander";
|
|
208
|
-
import { findProjectRoot as
|
|
314
|
+
import { findProjectRoot as findProjectRoot5 } from "@hiveai/core";
|
|
209
315
|
var HOOK_MARKER = "# hAIve auto-generated";
|
|
210
316
|
var HOOK_BODY = `#!/bin/sh
|
|
211
317
|
${HOOK_MARKER} \u2014 keep this block to allow upgrades. Hand-edit anything outside it.
|
|
@@ -221,9 +327,9 @@ fi
|
|
|
221
327
|
var HOOKS = ["post-merge", "post-rewrite"];
|
|
222
328
|
function registerInstallHooks(program2) {
|
|
223
329
|
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 =
|
|
330
|
+
const root = findProjectRoot5(opts.dir);
|
|
225
331
|
const gitDir = path4.join(root, ".git");
|
|
226
|
-
if (!
|
|
332
|
+
if (!existsSync4(gitDir)) {
|
|
227
333
|
ui.error(`No .git directory at ${root}.`);
|
|
228
334
|
process.exitCode = 1;
|
|
229
335
|
return;
|
|
@@ -234,8 +340,8 @@ function registerInstallHooks(program2) {
|
|
|
234
340
|
let skipped = 0;
|
|
235
341
|
for (const name of HOOKS) {
|
|
236
342
|
const file = path4.join(hooksDir, name);
|
|
237
|
-
if (
|
|
238
|
-
const existing = await
|
|
343
|
+
if (existsSync4(file) && !opts.force) {
|
|
344
|
+
const existing = await readFile2(file, "utf8");
|
|
239
345
|
if (!existing.includes(HOOK_MARKER)) {
|
|
240
346
|
ui.warn(`${name} already exists and was not written by hAIve. Re-run with --force to overwrite.`);
|
|
241
347
|
skipped++;
|
|
@@ -253,16 +359,16 @@ function registerInstallHooks(program2) {
|
|
|
253
359
|
|
|
254
360
|
// src/commands/mcp.ts
|
|
255
361
|
import { spawn } from "child_process";
|
|
256
|
-
import { existsSync as
|
|
362
|
+
import { existsSync as existsSync5 } from "fs";
|
|
257
363
|
import { createRequire } from "module";
|
|
258
364
|
import path5 from "path";
|
|
259
365
|
import { fileURLToPath } from "url";
|
|
260
366
|
import "commander";
|
|
261
|
-
import { findProjectRoot as
|
|
367
|
+
import { findProjectRoot as findProjectRoot6 } from "@hiveai/core";
|
|
262
368
|
var require2 = createRequire(import.meta.url);
|
|
263
369
|
function registerMcp(program2) {
|
|
264
370
|
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 =
|
|
371
|
+
const root = findProjectRoot6(opts.dir);
|
|
266
372
|
const bin = locateMcpBin();
|
|
267
373
|
if (!bin) {
|
|
268
374
|
ui.error(
|
|
@@ -282,29 +388,29 @@ function locateMcpBin() {
|
|
|
282
388
|
const pkgPath = require2.resolve("@hiveai/mcp/package.json");
|
|
283
389
|
const pkgDir = path5.dirname(pkgPath);
|
|
284
390
|
const candidate = path5.join(pkgDir, "dist", "index.js");
|
|
285
|
-
if (
|
|
391
|
+
if (existsSync5(candidate)) return candidate;
|
|
286
392
|
} catch {
|
|
287
393
|
}
|
|
288
394
|
const here = path5.dirname(fileURLToPath(import.meta.url));
|
|
289
395
|
const sibling = path5.resolve(here, "..", "..", "..", "mcp", "dist", "index.js");
|
|
290
|
-
if (
|
|
396
|
+
if (existsSync5(sibling)) return sibling;
|
|
291
397
|
return null;
|
|
292
398
|
}
|
|
293
399
|
|
|
294
400
|
// src/commands/sync.ts
|
|
295
401
|
import { spawnSync } from "child_process";
|
|
296
402
|
import { writeFile as writeFile3 } from "fs/promises";
|
|
297
|
-
import { existsSync as
|
|
403
|
+
import { existsSync as existsSync6 } from "fs";
|
|
298
404
|
import "path";
|
|
299
405
|
import "commander";
|
|
300
406
|
import {
|
|
301
407
|
DEFAULT_AUTO_PROMOTE_RULE,
|
|
302
|
-
findProjectRoot as
|
|
408
|
+
findProjectRoot as findProjectRoot7,
|
|
303
409
|
getUsage,
|
|
304
410
|
isAutoPromoteEligible,
|
|
305
|
-
loadMemoriesFromDir,
|
|
411
|
+
loadMemoriesFromDir as loadMemoriesFromDir2,
|
|
306
412
|
loadUsageIndex,
|
|
307
|
-
resolveHaivePaths as
|
|
413
|
+
resolveHaivePaths as resolveHaivePaths5,
|
|
308
414
|
serializeMemory,
|
|
309
415
|
verifyAnchor
|
|
310
416
|
} from "@hiveai/core";
|
|
@@ -313,9 +419,9 @@ function registerSync(program2) {
|
|
|
313
419
|
"--since <ref>",
|
|
314
420
|
"git ref/commit to compare against; report memories added/modified/removed since"
|
|
315
421
|
).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 (!
|
|
422
|
+
const root = findProjectRoot7(opts.dir);
|
|
423
|
+
const paths = resolveHaivePaths5(root);
|
|
424
|
+
if (!existsSync6(paths.memoriesDir)) {
|
|
319
425
|
if (!opts.quiet) ui.warn(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
320
426
|
process.exitCode = 1;
|
|
321
427
|
return;
|
|
@@ -327,7 +433,7 @@ function registerSync(program2) {
|
|
|
327
433
|
let revalidated = 0;
|
|
328
434
|
let promoted = 0;
|
|
329
435
|
if (opts.verify !== false) {
|
|
330
|
-
const memories = await
|
|
436
|
+
const memories = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
331
437
|
for (const { memory: memory2, filePath } of memories) {
|
|
332
438
|
const isAnchored = memory2.frontmatter.anchor.paths.length > 0 || memory2.frontmatter.anchor.symbols.length > 0;
|
|
333
439
|
if (!isAnchored) continue;
|
|
@@ -369,7 +475,7 @@ function registerSync(program2) {
|
|
|
369
475
|
}
|
|
370
476
|
}
|
|
371
477
|
if (opts.promote !== false) {
|
|
372
|
-
const memories = await
|
|
478
|
+
const memories = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
373
479
|
const usage = await loadUsageIndex(paths);
|
|
374
480
|
for (const { memory: memory2, filePath } of memories) {
|
|
375
481
|
if (isAutoPromoteEligible(
|
|
@@ -390,9 +496,20 @@ function registerSync(program2) {
|
|
|
390
496
|
}
|
|
391
497
|
}
|
|
392
498
|
const sinceReport = opts.since ? collectSinceChanges(root, opts.since) : null;
|
|
499
|
+
const draftMemories = (await loadMemoriesFromDir2(paths.memoriesDir)).filter(
|
|
500
|
+
(m) => m.memory.frontmatter.status === "draft"
|
|
501
|
+
);
|
|
502
|
+
const draftCount = draftMemories.length;
|
|
393
503
|
log(
|
|
394
504
|
`${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
505
|
);
|
|
506
|
+
if (!opts.quiet && draftCount > 0) {
|
|
507
|
+
log(
|
|
508
|
+
ui.dim(
|
|
509
|
+
`\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`
|
|
510
|
+
)
|
|
511
|
+
);
|
|
512
|
+
}
|
|
396
513
|
if (sinceReport && !opts.quiet) {
|
|
397
514
|
if (sinceReport.added.length > 0) {
|
|
398
515
|
log(ui.bold("\nNew memories:"));
|
|
@@ -430,28 +547,28 @@ function collectSinceChanges(root, ref) {
|
|
|
430
547
|
|
|
431
548
|
// src/commands/memory-add.ts
|
|
432
549
|
import { mkdir as mkdir3, writeFile as writeFile4 } from "fs/promises";
|
|
433
|
-
import { existsSync as
|
|
550
|
+
import { existsSync as existsSync7 } from "fs";
|
|
434
551
|
import path7 from "path";
|
|
435
552
|
import "commander";
|
|
436
553
|
import {
|
|
437
554
|
buildFrontmatter,
|
|
438
|
-
findProjectRoot as
|
|
555
|
+
findProjectRoot as findProjectRoot8,
|
|
439
556
|
inferModulesFromPaths,
|
|
440
557
|
memoryFilePath,
|
|
441
|
-
resolveHaivePaths as
|
|
558
|
+
resolveHaivePaths as resolveHaivePaths6,
|
|
442
559
|
serializeMemory as serializeMemory2
|
|
443
560
|
} from "@hiveai/core";
|
|
444
561
|
function registerMemoryAdd(memory2) {
|
|
445
|
-
memory2.command("add").description("Add a new memory (defaults to personal scope
|
|
446
|
-
const root =
|
|
447
|
-
const paths =
|
|
448
|
-
if (!
|
|
562
|
+
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) => {
|
|
563
|
+
const root = findProjectRoot8(opts.dir);
|
|
564
|
+
const paths = resolveHaivePaths6(root);
|
|
565
|
+
if (!existsSync7(paths.haiveDir)) {
|
|
449
566
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
450
567
|
process.exitCode = 1;
|
|
451
568
|
return;
|
|
452
569
|
}
|
|
453
|
-
const userTags =
|
|
454
|
-
const anchorPaths =
|
|
570
|
+
const userTags = parseCsv2(opts.tags);
|
|
571
|
+
const anchorPaths = parseCsv2(opts.paths);
|
|
455
572
|
const autoTagsEnabled = opts.autoTag !== false;
|
|
456
573
|
const inferredTags = autoTagsEnabled ? inferModulesFromPaths(anchorPaths) : [];
|
|
457
574
|
const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
|
|
@@ -464,73 +581,125 @@ function registerMemoryAdd(memory2) {
|
|
|
464
581
|
domain: opts.domain,
|
|
465
582
|
author: opts.author,
|
|
466
583
|
paths: anchorPaths,
|
|
467
|
-
symbols:
|
|
584
|
+
symbols: parseCsv2(opts.symbols),
|
|
468
585
|
commit: opts.commit
|
|
469
586
|
});
|
|
470
|
-
const
|
|
587
|
+
const title = opts.title ?? opts.slug;
|
|
588
|
+
let body;
|
|
589
|
+
if (opts.body !== void 0) {
|
|
590
|
+
body = opts.title ? `# ${opts.title}
|
|
591
|
+
|
|
592
|
+
${opts.body}` : opts.body;
|
|
593
|
+
} else {
|
|
594
|
+
body = `# ${title}
|
|
471
595
|
|
|
472
596
|
TODO \u2014 write the memory body.
|
|
473
597
|
`;
|
|
598
|
+
}
|
|
474
599
|
const file = memoryFilePath(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
475
600
|
await mkdir3(path7.dirname(file), { recursive: true });
|
|
476
|
-
if (
|
|
601
|
+
if (existsSync7(file)) {
|
|
477
602
|
ui.error(`Memory already exists at ${file}`);
|
|
478
603
|
process.exitCode = 1;
|
|
479
604
|
return;
|
|
480
605
|
}
|
|
481
606
|
await writeFile4(file, serializeMemory2({ frontmatter, body }), "utf8");
|
|
482
607
|
ui.success(`Created ${path7.relative(root, file)}`);
|
|
483
|
-
ui.info(`
|
|
608
|
+
ui.info(`id=${frontmatter.id} scope=${frontmatter.scope} status=${frontmatter.status}`);
|
|
484
609
|
if (inferredTags.length > 0) {
|
|
485
610
|
ui.info(`auto-tagged: ${inferredTags.join(", ")} (use --no-auto-tag to disable)`);
|
|
486
611
|
}
|
|
612
|
+
if (frontmatter.scope === "personal") {
|
|
613
|
+
console.log(
|
|
614
|
+
ui.dim(
|
|
615
|
+
`\u2192 next: haive memory approve ${frontmatter.id} (activate) | haive memory promote ${frontmatter.id} (share with team)`
|
|
616
|
+
)
|
|
617
|
+
);
|
|
618
|
+
} else {
|
|
619
|
+
console.log(
|
|
620
|
+
ui.dim(`\u2192 next: haive memory approve ${frontmatter.id} (mark as validated)`)
|
|
621
|
+
);
|
|
622
|
+
}
|
|
487
623
|
});
|
|
488
624
|
}
|
|
489
|
-
function
|
|
625
|
+
function parseCsv2(value) {
|
|
490
626
|
if (!value) return [];
|
|
491
627
|
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
492
628
|
}
|
|
493
629
|
|
|
494
630
|
// src/commands/memory-list.ts
|
|
495
|
-
import { existsSync as
|
|
631
|
+
import { existsSync as existsSync8 } from "fs";
|
|
496
632
|
import path8 from "path";
|
|
497
633
|
import "commander";
|
|
498
|
-
import { findProjectRoot as
|
|
634
|
+
import { findProjectRoot as findProjectRoot9, resolveHaivePaths as resolveHaivePaths7 } from "@hiveai/core";
|
|
499
635
|
|
|
500
636
|
// src/utils/fs.ts
|
|
501
637
|
import {
|
|
502
|
-
loadMemoriesFromDir as
|
|
638
|
+
loadMemoriesFromDir as loadMemoriesFromDir3,
|
|
503
639
|
loadMemory,
|
|
504
640
|
listMarkdownFilesRecursive
|
|
505
641
|
} from "@hiveai/core";
|
|
506
642
|
|
|
507
643
|
// src/commands/memory-list.ts
|
|
508
644
|
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 (!
|
|
645
|
+
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) => {
|
|
646
|
+
const root = findProjectRoot9(opts.dir);
|
|
647
|
+
const paths = resolveHaivePaths7(root);
|
|
648
|
+
if (!existsSync8(paths.memoriesDir)) {
|
|
513
649
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
514
650
|
process.exitCode = 1;
|
|
515
651
|
return;
|
|
516
652
|
}
|
|
517
|
-
const all = await
|
|
518
|
-
const
|
|
653
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
654
|
+
const statusFilter = opts.status ? opts.status.split(",").map((s) => s.trim()) : null;
|
|
655
|
+
const filtered = all.filter((m) => {
|
|
656
|
+
if (!matchesFilters(m, opts)) return false;
|
|
657
|
+
const status = m.memory.frontmatter.status;
|
|
658
|
+
if (!opts.showRejected && !statusFilter && status === "rejected") return false;
|
|
659
|
+
if (statusFilter && !statusFilter.includes(status)) return false;
|
|
660
|
+
return true;
|
|
661
|
+
});
|
|
662
|
+
const hiddenRejectedCount = !opts.showRejected && !statusFilter ? all.filter(
|
|
663
|
+
(m) => matchesFilters(m, opts) && m.memory.frontmatter.status === "rejected"
|
|
664
|
+
).length : 0;
|
|
519
665
|
if (filtered.length === 0) {
|
|
520
666
|
ui.info("No memories match the filters.");
|
|
667
|
+
if (hiddenRejectedCount > 0) {
|
|
668
|
+
ui.info(`(${hiddenRejectedCount} rejected hidden \u2014 use --show-rejected to include)`);
|
|
669
|
+
}
|
|
521
670
|
return;
|
|
522
671
|
}
|
|
523
672
|
for (const { memory: mem, filePath } of filtered) {
|
|
524
673
|
const fm = mem.frontmatter;
|
|
525
674
|
const tagStr = fm.tags.length ? ui.dim(` [${fm.tags.join(", ")}]`) : "";
|
|
526
675
|
const moduleStr = fm.module ? ui.dim(` (${fm.module})`) : "";
|
|
676
|
+
const statusBadge = ui.statusBadge(fm.status);
|
|
527
677
|
console.log(
|
|
528
|
-
`${ui.bold(fm.id)} ${ui.dim(fm.scope)}/${ui.dim(fm.type)}${moduleStr}${tagStr}`
|
|
678
|
+
`${ui.bold(fm.id)} ${ui.dim(fm.scope)}/${ui.dim(fm.type)} ${statusBadge}${moduleStr}${tagStr}`
|
|
529
679
|
);
|
|
530
680
|
console.log(` ${ui.dim(path8.relative(root, filePath))}`);
|
|
531
681
|
}
|
|
532
682
|
console.log(ui.dim(`
|
|
533
683
|
${filtered.length} memor${filtered.length === 1 ? "y" : "ies"}`));
|
|
684
|
+
if (hiddenRejectedCount > 0) {
|
|
685
|
+
console.log(
|
|
686
|
+
ui.dim(`(${hiddenRejectedCount} rejected hidden \u2014 use --show-rejected to include)`)
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
const draftItems = filtered.filter((m) => m.memory.frontmatter.status === "draft");
|
|
690
|
+
if (draftItems.length > 0) {
|
|
691
|
+
const hasPersonalDrafts = draftItems.some(
|
|
692
|
+
(m) => m.memory.frontmatter.scope === "personal"
|
|
693
|
+
);
|
|
694
|
+
const hasTeamDrafts = draftItems.some(
|
|
695
|
+
(m) => m.memory.frontmatter.scope !== "personal"
|
|
696
|
+
);
|
|
697
|
+
let hint = `\u2139 ${draftItems.length} in draft \u2014 use \`haive memory approve <id>\` to activate`;
|
|
698
|
+
if (hasPersonalDrafts && !hasTeamDrafts) {
|
|
699
|
+
hint += " or `haive memory promote <id>` to share with team";
|
|
700
|
+
}
|
|
701
|
+
console.log(ui.dim(hint));
|
|
702
|
+
}
|
|
534
703
|
});
|
|
535
704
|
}
|
|
536
705
|
function matchesFilters(loaded, opts) {
|
|
@@ -544,25 +713,39 @@ function matchesFilters(loaded, opts) {
|
|
|
544
713
|
|
|
545
714
|
// src/commands/memory-promote.ts
|
|
546
715
|
import { mkdir as mkdir4, unlink, writeFile as writeFile5 } from "fs/promises";
|
|
547
|
-
import { existsSync as
|
|
716
|
+
import { existsSync as existsSync9 } from "fs";
|
|
548
717
|
import path9 from "path";
|
|
549
718
|
import "commander";
|
|
550
719
|
import {
|
|
551
|
-
findProjectRoot as
|
|
720
|
+
findProjectRoot as findProjectRoot10,
|
|
552
721
|
memoryFilePath as memoryFilePath2,
|
|
553
|
-
resolveHaivePaths as
|
|
722
|
+
resolveHaivePaths as resolveHaivePaths8,
|
|
554
723
|
serializeMemory as serializeMemory3
|
|
555
724
|
} from "@hiveai/core";
|
|
556
725
|
function registerMemoryPromote(memory2) {
|
|
557
726
|
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 (!
|
|
727
|
+
const root = findProjectRoot10(opts.dir);
|
|
728
|
+
const paths = resolveHaivePaths8(root);
|
|
729
|
+
if (!existsSync9(paths.memoriesDir)) {
|
|
561
730
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
562
731
|
process.exitCode = 1;
|
|
563
732
|
return;
|
|
564
733
|
}
|
|
565
|
-
const
|
|
734
|
+
const teamAndModule = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
735
|
+
const alreadyShared = teamAndModule.find(
|
|
736
|
+
(m) => m.memory.frontmatter.id === id && (m.memory.frontmatter.scope === "team" || m.memory.frontmatter.scope === "module")
|
|
737
|
+
);
|
|
738
|
+
if (alreadyShared) {
|
|
739
|
+
const fm = alreadyShared.memory.frontmatter;
|
|
740
|
+
ui.warn(
|
|
741
|
+
`"${id}" is already in ${fm.scope} scope (status=${fm.status}).`
|
|
742
|
+
);
|
|
743
|
+
if (fm.status !== "validated") {
|
|
744
|
+
ui.info(`\u2192 run \`haive memory approve ${id}\` to validate it`);
|
|
745
|
+
}
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
const all = await loadMemoriesFromDir3(paths.personalDir);
|
|
566
749
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
567
750
|
if (!found) {
|
|
568
751
|
ui.error(`No personal memory with id "${id}". (Promotion only applies to personal scope.)`);
|
|
@@ -583,29 +766,57 @@ function registerMemoryPromote(memory2) {
|
|
|
583
766
|
await unlink(found.filePath);
|
|
584
767
|
ui.success(`Promoted ${id} to team scope (status=proposed)`);
|
|
585
768
|
ui.info(`Now at ${path9.relative(root, newPath)}`);
|
|
769
|
+
console.log(ui.dim(`\u2192 next: haive memory approve ${id} (validate for team use)`));
|
|
586
770
|
});
|
|
587
771
|
}
|
|
588
772
|
|
|
589
773
|
// src/commands/memory-approve.ts
|
|
590
|
-
import { existsSync as
|
|
774
|
+
import { existsSync as existsSync10 } from "fs";
|
|
591
775
|
import { writeFile as writeFile6 } from "fs/promises";
|
|
592
776
|
import path10 from "path";
|
|
593
777
|
import "commander";
|
|
594
778
|
import {
|
|
595
|
-
findProjectRoot as
|
|
596
|
-
resolveHaivePaths as
|
|
779
|
+
findProjectRoot as findProjectRoot11,
|
|
780
|
+
resolveHaivePaths as resolveHaivePaths9,
|
|
597
781
|
serializeMemory as serializeMemory4
|
|
598
782
|
} from "@hiveai/core";
|
|
599
783
|
function registerMemoryApprove(memory2) {
|
|
600
|
-
memory2.command("approve
|
|
601
|
-
const root =
|
|
602
|
-
const paths =
|
|
603
|
-
if (!
|
|
784
|
+
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) => {
|
|
785
|
+
const root = findProjectRoot11(opts.dir);
|
|
786
|
+
const paths = resolveHaivePaths9(root);
|
|
787
|
+
if (!existsSync10(paths.memoriesDir)) {
|
|
604
788
|
ui.error(`No .ai/memories at ${root}.`);
|
|
605
789
|
process.exitCode = 1;
|
|
606
790
|
return;
|
|
607
791
|
}
|
|
608
|
-
const all = await
|
|
792
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
793
|
+
if (opts.all || opts.pending) {
|
|
794
|
+
const candidates = all.filter((m) => {
|
|
795
|
+
const s = m.memory.frontmatter.status;
|
|
796
|
+
if (opts.all) return s === "proposed" || s === "draft";
|
|
797
|
+
return s === "proposed";
|
|
798
|
+
});
|
|
799
|
+
if (candidates.length === 0) {
|
|
800
|
+
ui.info(opts.all ? "No draft or proposed memories to approve." : "No proposed memories to approve.");
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
let count = 0;
|
|
804
|
+
for (const found2 of candidates) {
|
|
805
|
+
const next2 = {
|
|
806
|
+
frontmatter: { ...found2.memory.frontmatter, status: "validated" },
|
|
807
|
+
body: found2.memory.body
|
|
808
|
+
};
|
|
809
|
+
await writeFile6(found2.filePath, serializeMemory4(next2), "utf8");
|
|
810
|
+
count++;
|
|
811
|
+
}
|
|
812
|
+
ui.success(`Approved ${count} memor${count === 1 ? "y" : "ies"} (status=validated)`);
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
if (!id) {
|
|
816
|
+
ui.error("Provide a memory id or use --all / --pending for bulk approval.");
|
|
817
|
+
process.exitCode = 1;
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
609
820
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
610
821
|
if (!found) {
|
|
611
822
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -632,24 +843,24 @@ function registerMemoryApprove(memory2) {
|
|
|
632
843
|
|
|
633
844
|
// src/commands/memory-update.ts
|
|
634
845
|
import { writeFile as writeFile7 } from "fs/promises";
|
|
635
|
-
import { existsSync as
|
|
846
|
+
import { existsSync as existsSync11 } from "fs";
|
|
636
847
|
import path11 from "path";
|
|
637
848
|
import "commander";
|
|
638
849
|
import {
|
|
639
|
-
findProjectRoot as
|
|
640
|
-
resolveHaivePaths as
|
|
850
|
+
findProjectRoot as findProjectRoot12,
|
|
851
|
+
resolveHaivePaths as resolveHaivePaths10,
|
|
641
852
|
serializeMemory as serializeMemory5
|
|
642
853
|
} from "@hiveai/core";
|
|
643
854
|
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 (!
|
|
855
|
+
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) => {
|
|
856
|
+
const root = findProjectRoot12(opts.dir);
|
|
857
|
+
const paths = resolveHaivePaths10(root);
|
|
858
|
+
if (!existsSync11(paths.memoriesDir)) {
|
|
648
859
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
649
860
|
process.exitCode = 1;
|
|
650
861
|
return;
|
|
651
862
|
}
|
|
652
|
-
const memories = await
|
|
863
|
+
const memories = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
653
864
|
const loaded = memories.find((m) => m.memory.frontmatter.id === id);
|
|
654
865
|
if (!loaded) {
|
|
655
866
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -660,11 +871,11 @@ function registerMemoryUpdate(memory2) {
|
|
|
660
871
|
const { frontmatter, body } = loaded.memory;
|
|
661
872
|
const newAnchor = { ...frontmatter.anchor };
|
|
662
873
|
if (opts.paths !== void 0) {
|
|
663
|
-
newAnchor.paths =
|
|
874
|
+
newAnchor.paths = parseCsv3(opts.paths);
|
|
664
875
|
updated.push("anchor.paths");
|
|
665
876
|
}
|
|
666
877
|
if (opts.symbols !== void 0) {
|
|
667
|
-
newAnchor.symbols =
|
|
878
|
+
newAnchor.symbols = parseCsv3(opts.symbols);
|
|
668
879
|
updated.push("anchor.symbols");
|
|
669
880
|
}
|
|
670
881
|
if (opts.commit !== void 0) {
|
|
@@ -674,14 +885,18 @@ function registerMemoryUpdate(memory2) {
|
|
|
674
885
|
const newFrontmatter = {
|
|
675
886
|
...frontmatter,
|
|
676
887
|
anchor: newAnchor,
|
|
677
|
-
...opts.tags !== void 0 ? { tags:
|
|
888
|
+
...opts.tags !== void 0 ? { tags: parseCsv3(opts.tags) } : {},
|
|
678
889
|
...opts.domain !== void 0 ? { domain: opts.domain } : {},
|
|
679
890
|
...opts.author !== void 0 ? { author: opts.author } : {}
|
|
680
891
|
};
|
|
681
892
|
if (opts.tags !== void 0) updated.push("tags");
|
|
682
893
|
if (opts.domain !== void 0) updated.push("domain");
|
|
683
894
|
if (opts.author !== void 0) updated.push("author");
|
|
684
|
-
|
|
895
|
+
let newBody = opts.body !== void 0 ? opts.body : body;
|
|
896
|
+
if (opts.title !== void 0) {
|
|
897
|
+
newBody = replaceFirstHeading(newBody, opts.title);
|
|
898
|
+
updated.push("title");
|
|
899
|
+
}
|
|
685
900
|
if (opts.body !== void 0) updated.push("body");
|
|
686
901
|
if (updated.length === 0) {
|
|
687
902
|
ui.warn("Nothing to update \u2014 provide at least one option.");
|
|
@@ -696,22 +911,32 @@ function registerMemoryUpdate(memory2) {
|
|
|
696
911
|
ui.info(`fields: ${updated.join(", ")}`);
|
|
697
912
|
});
|
|
698
913
|
}
|
|
699
|
-
function
|
|
914
|
+
function replaceFirstHeading(body, title) {
|
|
915
|
+
const headingRe = /^#\s+.+$/m;
|
|
916
|
+
const replacement = `# ${title}`;
|
|
917
|
+
if (headingRe.test(body)) {
|
|
918
|
+
return body.replace(headingRe, replacement);
|
|
919
|
+
}
|
|
920
|
+
return `${replacement}
|
|
921
|
+
|
|
922
|
+
${body}`;
|
|
923
|
+
}
|
|
924
|
+
function parseCsv3(value) {
|
|
700
925
|
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
701
926
|
}
|
|
702
927
|
|
|
703
928
|
// src/commands/memory-auto-promote.ts
|
|
704
929
|
import { writeFile as writeFile8 } from "fs/promises";
|
|
705
|
-
import { existsSync as
|
|
930
|
+
import { existsSync as existsSync12 } from "fs";
|
|
706
931
|
import path12 from "path";
|
|
707
932
|
import "commander";
|
|
708
933
|
import {
|
|
709
934
|
DEFAULT_AUTO_PROMOTE_RULE as DEFAULT_AUTO_PROMOTE_RULE2,
|
|
710
|
-
findProjectRoot as
|
|
935
|
+
findProjectRoot as findProjectRoot13,
|
|
711
936
|
getUsage as getUsage2,
|
|
712
937
|
isAutoPromoteEligible as isAutoPromoteEligible2,
|
|
713
938
|
loadUsageIndex as loadUsageIndex2,
|
|
714
|
-
resolveHaivePaths as
|
|
939
|
+
resolveHaivePaths as resolveHaivePaths11,
|
|
715
940
|
serializeMemory as serializeMemory6
|
|
716
941
|
} from "@hiveai/core";
|
|
717
942
|
function registerMemoryAutoPromote(memory2) {
|
|
@@ -720,9 +945,9 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
720
945
|
"memories with more rejections than this are skipped",
|
|
721
946
|
String(DEFAULT_AUTO_PROMOTE_RULE2.maxRejections)
|
|
722
947
|
).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 (!
|
|
948
|
+
const root = findProjectRoot13(opts.dir);
|
|
949
|
+
const paths = resolveHaivePaths11(root);
|
|
950
|
+
if (!existsSync12(paths.memoriesDir)) {
|
|
726
951
|
ui.error(`No .ai/memories at ${root}.`);
|
|
727
952
|
process.exitCode = 1;
|
|
728
953
|
return;
|
|
@@ -731,7 +956,7 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
731
956
|
minReads: Number(opts.minReads ?? DEFAULT_AUTO_PROMOTE_RULE2.minReads),
|
|
732
957
|
maxRejections: Number(opts.maxRejections ?? DEFAULT_AUTO_PROMOTE_RULE2.maxRejections)
|
|
733
958
|
};
|
|
734
|
-
const memories = await
|
|
959
|
+
const memories = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
735
960
|
const usage = await loadUsageIndex2(paths);
|
|
736
961
|
const eligible = memories.filter(
|
|
737
962
|
({ memory: memory3 }) => isAutoPromoteEligible2(memory3.frontmatter, getUsage2(usage, memory3.frontmatter.id), rule)
|
|
@@ -765,25 +990,25 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
765
990
|
|
|
766
991
|
// src/commands/memory-edit.ts
|
|
767
992
|
import { spawn as spawn2 } from "child_process";
|
|
768
|
-
import { existsSync as
|
|
769
|
-
import { readFile as
|
|
993
|
+
import { existsSync as existsSync13 } from "fs";
|
|
994
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
770
995
|
import path13 from "path";
|
|
771
996
|
import "commander";
|
|
772
997
|
import {
|
|
773
|
-
findProjectRoot as
|
|
998
|
+
findProjectRoot as findProjectRoot14,
|
|
774
999
|
parseMemory,
|
|
775
|
-
resolveHaivePaths as
|
|
1000
|
+
resolveHaivePaths as resolveHaivePaths12
|
|
776
1001
|
} from "@hiveai/core";
|
|
777
1002
|
function registerMemoryEdit(memory2) {
|
|
778
1003
|
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 (!
|
|
1004
|
+
const root = findProjectRoot14(opts.dir);
|
|
1005
|
+
const paths = resolveHaivePaths12(root);
|
|
1006
|
+
if (!existsSync13(paths.memoriesDir)) {
|
|
782
1007
|
ui.error(`No .ai/memories at ${root}.`);
|
|
783
1008
|
process.exitCode = 1;
|
|
784
1009
|
return;
|
|
785
1010
|
}
|
|
786
|
-
const all = await
|
|
1011
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
787
1012
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
788
1013
|
if (!found) {
|
|
789
1014
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -797,7 +1022,7 @@ function registerMemoryEdit(memory2) {
|
|
|
797
1022
|
ui.warn(`Editor exited with status ${code}.`);
|
|
798
1023
|
}
|
|
799
1024
|
try {
|
|
800
|
-
const fresh = await
|
|
1025
|
+
const fresh = await readFile3(found.filePath, "utf8");
|
|
801
1026
|
parseMemory(fresh);
|
|
802
1027
|
ui.success("Memory still parses cleanly.");
|
|
803
1028
|
} catch (err) {
|
|
@@ -818,28 +1043,28 @@ function runEditor(editor, file) {
|
|
|
818
1043
|
}
|
|
819
1044
|
|
|
820
1045
|
// src/commands/memory-for-files.ts
|
|
821
|
-
import { existsSync as
|
|
1046
|
+
import { existsSync as existsSync14 } from "fs";
|
|
822
1047
|
import path14 from "path";
|
|
823
1048
|
import "commander";
|
|
824
1049
|
import {
|
|
825
1050
|
deriveConfidence,
|
|
826
|
-
findProjectRoot as
|
|
1051
|
+
findProjectRoot as findProjectRoot15,
|
|
827
1052
|
getUsage as getUsage3,
|
|
828
1053
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
829
1054
|
loadUsageIndex as loadUsageIndex3,
|
|
830
|
-
memoryMatchesAnchorPaths,
|
|
831
|
-
resolveHaivePaths as
|
|
1055
|
+
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
1056
|
+
resolveHaivePaths as resolveHaivePaths13
|
|
832
1057
|
} from "@hiveai/core";
|
|
833
1058
|
function registerMemoryForFiles(memory2) {
|
|
834
1059
|
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 (!
|
|
1060
|
+
const root = findProjectRoot15(opts.dir);
|
|
1061
|
+
const paths = resolveHaivePaths13(root);
|
|
1062
|
+
if (!existsSync14(paths.memoriesDir)) {
|
|
838
1063
|
ui.error(`No .ai/memories at ${root}.`);
|
|
839
1064
|
process.exitCode = 1;
|
|
840
1065
|
return;
|
|
841
1066
|
}
|
|
842
|
-
const all = await
|
|
1067
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
843
1068
|
const usage = await loadUsageIndex3(paths);
|
|
844
1069
|
const inferred = inferModulesFromPaths2(files);
|
|
845
1070
|
const byAnchor = [];
|
|
@@ -847,7 +1072,7 @@ function registerMemoryForFiles(memory2) {
|
|
|
847
1072
|
const byDomain = [];
|
|
848
1073
|
const seen = /* @__PURE__ */ new Set();
|
|
849
1074
|
for (const loaded of all) {
|
|
850
|
-
if (
|
|
1075
|
+
if (memoryMatchesAnchorPaths2(loaded.memory, files)) {
|
|
851
1076
|
byAnchor.push(loaded);
|
|
852
1077
|
seen.add(loaded.memory.frontmatter.id);
|
|
853
1078
|
}
|
|
@@ -892,26 +1117,26 @@ function printGroup(root, label, loaded, usage) {
|
|
|
892
1117
|
}
|
|
893
1118
|
|
|
894
1119
|
// src/commands/memory-hot.ts
|
|
895
|
-
import { existsSync as
|
|
1120
|
+
import { existsSync as existsSync15 } from "fs";
|
|
896
1121
|
import path15 from "path";
|
|
897
1122
|
import "commander";
|
|
898
1123
|
import {
|
|
899
|
-
findProjectRoot as
|
|
1124
|
+
findProjectRoot as findProjectRoot16,
|
|
900
1125
|
getUsage as getUsage4,
|
|
901
1126
|
loadUsageIndex as loadUsageIndex4,
|
|
902
|
-
resolveHaivePaths as
|
|
1127
|
+
resolveHaivePaths as resolveHaivePaths14
|
|
903
1128
|
} from "@hiveai/core";
|
|
904
1129
|
function registerMemoryHot(memory2) {
|
|
905
1130
|
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 (!
|
|
1131
|
+
const root = findProjectRoot16(opts.dir);
|
|
1132
|
+
const paths = resolveHaivePaths14(root);
|
|
1133
|
+
if (!existsSync15(paths.memoriesDir)) {
|
|
909
1134
|
ui.error(`No .ai/memories at ${root}.`);
|
|
910
1135
|
process.exitCode = 1;
|
|
911
1136
|
return;
|
|
912
1137
|
}
|
|
913
1138
|
const threshold = Math.max(1, Number(opts.threshold ?? 3));
|
|
914
|
-
const all = await
|
|
1139
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
915
1140
|
const usage = await loadUsageIndex4(paths);
|
|
916
1141
|
const candidates = all.filter(({ memory: mem }) => {
|
|
917
1142
|
const fm = mem.frontmatter;
|
|
@@ -942,25 +1167,25 @@ function registerMemoryHot(memory2) {
|
|
|
942
1167
|
}
|
|
943
1168
|
|
|
944
1169
|
// src/commands/memory-pending.ts
|
|
945
|
-
import { existsSync as
|
|
1170
|
+
import { existsSync as existsSync16 } from "fs";
|
|
946
1171
|
import path16 from "path";
|
|
947
1172
|
import "commander";
|
|
948
1173
|
import {
|
|
949
|
-
findProjectRoot as
|
|
1174
|
+
findProjectRoot as findProjectRoot17,
|
|
950
1175
|
getUsage as getUsage5,
|
|
951
1176
|
loadUsageIndex as loadUsageIndex5,
|
|
952
|
-
resolveHaivePaths as
|
|
1177
|
+
resolveHaivePaths as resolveHaivePaths15
|
|
953
1178
|
} from "@hiveai/core";
|
|
954
1179
|
function registerMemoryPending(memory2) {
|
|
955
1180
|
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 (!
|
|
1181
|
+
const root = findProjectRoot17(opts.dir);
|
|
1182
|
+
const paths = resolveHaivePaths15(root);
|
|
1183
|
+
if (!existsSync16(paths.memoriesDir)) {
|
|
959
1184
|
ui.error(`No .ai/memories at ${root}.`);
|
|
960
1185
|
process.exitCode = 1;
|
|
961
1186
|
return;
|
|
962
1187
|
}
|
|
963
|
-
const all = await
|
|
1188
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
964
1189
|
const usage = await loadUsageIndex5(paths);
|
|
965
1190
|
const proposed = all.filter(({ memory: mem }) => {
|
|
966
1191
|
if (mem.frontmatter.status !== "proposed") return false;
|
|
@@ -990,29 +1215,36 @@ function registerMemoryPending(memory2) {
|
|
|
990
1215
|
}
|
|
991
1216
|
|
|
992
1217
|
// src/commands/memory-query.ts
|
|
993
|
-
import { existsSync as
|
|
1218
|
+
import { existsSync as existsSync17 } from "fs";
|
|
994
1219
|
import path17 from "path";
|
|
995
1220
|
import "commander";
|
|
996
1221
|
import {
|
|
997
1222
|
extractSnippet,
|
|
998
|
-
findProjectRoot as
|
|
999
|
-
literalMatchesAllTokens,
|
|
1223
|
+
findProjectRoot as findProjectRoot18,
|
|
1224
|
+
literalMatchesAllTokens as literalMatchesAllTokens2,
|
|
1000
1225
|
pickSnippetNeedle,
|
|
1001
|
-
resolveHaivePaths as
|
|
1002
|
-
tokenizeQuery
|
|
1226
|
+
resolveHaivePaths as resolveHaivePaths16,
|
|
1227
|
+
tokenizeQuery as tokenizeQuery2
|
|
1003
1228
|
} from "@hiveai/core";
|
|
1004
1229
|
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 (!
|
|
1230
|
+
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)").option("--show-rejected", "include rejected memories (hidden by default)").action(async (text, opts) => {
|
|
1231
|
+
const root = findProjectRoot18(opts.dir);
|
|
1232
|
+
const paths = resolveHaivePaths16(root);
|
|
1233
|
+
if (!existsSync17(paths.memoriesDir)) {
|
|
1009
1234
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
1010
1235
|
process.exitCode = 1;
|
|
1011
1236
|
return;
|
|
1012
1237
|
}
|
|
1013
|
-
const tokens =
|
|
1014
|
-
const
|
|
1015
|
-
const
|
|
1238
|
+
const tokens = tokenizeQuery2(text);
|
|
1239
|
+
const statusFilter = opts.status ? opts.status.split(",").map((s) => s.trim()) : null;
|
|
1240
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
1241
|
+
const matches = all.filter(({ memory: mem }) => {
|
|
1242
|
+
const fm = mem.frontmatter;
|
|
1243
|
+
if (opts.scope && fm.scope !== opts.scope) return false;
|
|
1244
|
+
if (!opts.showRejected && !statusFilter && fm.status === "rejected") return false;
|
|
1245
|
+
if (statusFilter && !statusFilter.includes(fm.status)) return false;
|
|
1246
|
+
return literalMatchesAllTokens2(mem, tokens);
|
|
1247
|
+
});
|
|
1016
1248
|
const limit = Math.max(1, Number(opts.limit ?? 20));
|
|
1017
1249
|
const top = matches.slice(0, limit);
|
|
1018
1250
|
if (top.length === 0) {
|
|
@@ -1021,9 +1253,11 @@ function registerMemoryQuery(memory2) {
|
|
|
1021
1253
|
}
|
|
1022
1254
|
const snippetNeedle = pickSnippetNeedle(text);
|
|
1023
1255
|
for (const { memory: mem, filePath } of top) {
|
|
1024
|
-
const
|
|
1025
|
-
|
|
1256
|
+
const fm = mem.frontmatter;
|
|
1257
|
+
const statusBadge = ui.statusBadge(fm.status);
|
|
1258
|
+
console.log(`${ui.bold(fm.id)} ${ui.dim(fm.scope)} ${statusBadge}`);
|
|
1026
1259
|
console.log(` ${ui.dim(path17.relative(root, filePath))}`);
|
|
1260
|
+
const snippet = extractSnippet(mem.body, snippetNeedle);
|
|
1027
1261
|
if (snippet) console.log(` ${snippet}`);
|
|
1028
1262
|
}
|
|
1029
1263
|
console.log(
|
|
@@ -1035,26 +1269,26 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
|
|
|
1035
1269
|
|
|
1036
1270
|
// src/commands/memory-reject.ts
|
|
1037
1271
|
import { writeFile as writeFile9 } from "fs/promises";
|
|
1038
|
-
import { existsSync as
|
|
1272
|
+
import { existsSync as existsSync18 } from "fs";
|
|
1039
1273
|
import "commander";
|
|
1040
1274
|
import {
|
|
1041
|
-
findProjectRoot as
|
|
1275
|
+
findProjectRoot as findProjectRoot19,
|
|
1042
1276
|
loadUsageIndex as loadUsageIndex6,
|
|
1043
1277
|
recordRejection,
|
|
1044
|
-
resolveHaivePaths as
|
|
1278
|
+
resolveHaivePaths as resolveHaivePaths17,
|
|
1045
1279
|
saveUsageIndex,
|
|
1046
1280
|
serializeMemory as serializeMemory7
|
|
1047
1281
|
} from "@hiveai/core";
|
|
1048
1282
|
function registerMemoryReject(memory2) {
|
|
1049
1283
|
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 (!
|
|
1284
|
+
const root = findProjectRoot19(opts.dir);
|
|
1285
|
+
const paths = resolveHaivePaths17(root);
|
|
1286
|
+
if (!existsSync18(paths.memoriesDir)) {
|
|
1053
1287
|
ui.error(`No .ai/memories at ${root}.`);
|
|
1054
1288
|
process.exitCode = 1;
|
|
1055
1289
|
return;
|
|
1056
1290
|
}
|
|
1057
|
-
const memories = await
|
|
1291
|
+
const memories = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
1058
1292
|
const loaded = memories.find((m) => m.memory.frontmatter.id === id);
|
|
1059
1293
|
if (!loaded) {
|
|
1060
1294
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -1064,7 +1298,11 @@ function registerMemoryReject(memory2) {
|
|
|
1064
1298
|
await writeFile9(
|
|
1065
1299
|
loaded.filePath,
|
|
1066
1300
|
serializeMemory7({
|
|
1067
|
-
frontmatter: {
|
|
1301
|
+
frontmatter: {
|
|
1302
|
+
...loaded.memory.frontmatter,
|
|
1303
|
+
status: "rejected",
|
|
1304
|
+
stale_reason: opts.reason ?? loaded.memory.frontmatter.stale_reason ?? null
|
|
1305
|
+
},
|
|
1068
1306
|
body: loaded.memory.body
|
|
1069
1307
|
}),
|
|
1070
1308
|
"utf8"
|
|
@@ -1081,27 +1319,27 @@ function registerMemoryReject(memory2) {
|
|
|
1081
1319
|
}
|
|
1082
1320
|
|
|
1083
1321
|
// src/commands/memory-rm.ts
|
|
1084
|
-
import { existsSync as
|
|
1322
|
+
import { existsSync as existsSync19 } from "fs";
|
|
1085
1323
|
import { unlink as unlink2 } from "fs/promises";
|
|
1086
1324
|
import path18 from "path";
|
|
1087
1325
|
import { createInterface } from "readline/promises";
|
|
1088
1326
|
import "commander";
|
|
1089
1327
|
import {
|
|
1090
|
-
findProjectRoot as
|
|
1328
|
+
findProjectRoot as findProjectRoot20,
|
|
1091
1329
|
loadUsageIndex as loadUsageIndex7,
|
|
1092
|
-
resolveHaivePaths as
|
|
1330
|
+
resolveHaivePaths as resolveHaivePaths18,
|
|
1093
1331
|
saveUsageIndex as saveUsageIndex2
|
|
1094
1332
|
} from "@hiveai/core";
|
|
1095
1333
|
function registerMemoryRm(memory2) {
|
|
1096
1334
|
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 (!
|
|
1335
|
+
const root = findProjectRoot20(opts.dir);
|
|
1336
|
+
const paths = resolveHaivePaths18(root);
|
|
1337
|
+
if (!existsSync19(paths.memoriesDir)) {
|
|
1100
1338
|
ui.error(`No .ai/memories at ${root}.`);
|
|
1101
1339
|
process.exitCode = 1;
|
|
1102
1340
|
return;
|
|
1103
1341
|
}
|
|
1104
|
-
const all = await
|
|
1342
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
1105
1343
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
1106
1344
|
if (!found) {
|
|
1107
1345
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -1132,27 +1370,27 @@ function registerMemoryRm(memory2) {
|
|
|
1132
1370
|
}
|
|
1133
1371
|
|
|
1134
1372
|
// src/commands/memory-show.ts
|
|
1135
|
-
import { existsSync as
|
|
1136
|
-
import { readFile as
|
|
1373
|
+
import { existsSync as existsSync20 } from "fs";
|
|
1374
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1137
1375
|
import path19 from "path";
|
|
1138
1376
|
import "commander";
|
|
1139
1377
|
import {
|
|
1140
1378
|
deriveConfidence as deriveConfidence2,
|
|
1141
|
-
findProjectRoot as
|
|
1379
|
+
findProjectRoot as findProjectRoot21,
|
|
1142
1380
|
getUsage as getUsage6,
|
|
1143
1381
|
loadUsageIndex as loadUsageIndex8,
|
|
1144
|
-
resolveHaivePaths as
|
|
1382
|
+
resolveHaivePaths as resolveHaivePaths19
|
|
1145
1383
|
} from "@hiveai/core";
|
|
1146
1384
|
function registerMemoryShow(memory2) {
|
|
1147
1385
|
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 (!
|
|
1386
|
+
const root = findProjectRoot21(opts.dir);
|
|
1387
|
+
const paths = resolveHaivePaths19(root);
|
|
1388
|
+
if (!existsSync20(paths.memoriesDir)) {
|
|
1151
1389
|
ui.error(`No .ai/memories at ${root}.`);
|
|
1152
1390
|
process.exitCode = 1;
|
|
1153
1391
|
return;
|
|
1154
1392
|
}
|
|
1155
|
-
const all = await
|
|
1393
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
1156
1394
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
1157
1395
|
if (!found) {
|
|
1158
1396
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -1160,7 +1398,7 @@ function registerMemoryShow(memory2) {
|
|
|
1160
1398
|
return;
|
|
1161
1399
|
}
|
|
1162
1400
|
if (opts.raw) {
|
|
1163
|
-
console.log(await
|
|
1401
|
+
console.log(await readFile4(found.filePath, "utf8"));
|
|
1164
1402
|
return;
|
|
1165
1403
|
}
|
|
1166
1404
|
const fm = found.memory.frontmatter;
|
|
@@ -1191,26 +1429,26 @@ function registerMemoryShow(memory2) {
|
|
|
1191
1429
|
}
|
|
1192
1430
|
|
|
1193
1431
|
// src/commands/memory-stats.ts
|
|
1194
|
-
import { existsSync as
|
|
1432
|
+
import { existsSync as existsSync21 } from "fs";
|
|
1195
1433
|
import path20 from "path";
|
|
1196
1434
|
import "commander";
|
|
1197
1435
|
import {
|
|
1198
1436
|
deriveConfidence as deriveConfidence3,
|
|
1199
|
-
findProjectRoot as
|
|
1437
|
+
findProjectRoot as findProjectRoot22,
|
|
1200
1438
|
getUsage as getUsage7,
|
|
1201
1439
|
loadUsageIndex as loadUsageIndex9,
|
|
1202
|
-
resolveHaivePaths as
|
|
1440
|
+
resolveHaivePaths as resolveHaivePaths20
|
|
1203
1441
|
} from "@hiveai/core";
|
|
1204
1442
|
function registerMemoryStats(memory2) {
|
|
1205
1443
|
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 (!
|
|
1444
|
+
const root = findProjectRoot22(opts.dir);
|
|
1445
|
+
const paths = resolveHaivePaths20(root);
|
|
1446
|
+
if (!existsSync21(paths.memoriesDir)) {
|
|
1209
1447
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
1210
1448
|
process.exitCode = 1;
|
|
1211
1449
|
return;
|
|
1212
1450
|
}
|
|
1213
|
-
const all = await
|
|
1451
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
1214
1452
|
const usage = await loadUsageIndex9(paths);
|
|
1215
1453
|
const target = opts.id ? all.filter((m) => m.memory.frontmatter.id === opts.id) : all;
|
|
1216
1454
|
if (target.length === 0) {
|
|
@@ -1237,25 +1475,25 @@ function registerMemoryStats(memory2) {
|
|
|
1237
1475
|
|
|
1238
1476
|
// src/commands/memory-verify.ts
|
|
1239
1477
|
import { writeFile as writeFile10 } from "fs/promises";
|
|
1240
|
-
import { existsSync as
|
|
1478
|
+
import { existsSync as existsSync22 } from "fs";
|
|
1241
1479
|
import path21 from "path";
|
|
1242
1480
|
import "commander";
|
|
1243
1481
|
import {
|
|
1244
|
-
findProjectRoot as
|
|
1245
|
-
resolveHaivePaths as
|
|
1482
|
+
findProjectRoot as findProjectRoot23,
|
|
1483
|
+
resolveHaivePaths as resolveHaivePaths21,
|
|
1246
1484
|
serializeMemory as serializeMemory8,
|
|
1247
1485
|
verifyAnchor as verifyAnchor2
|
|
1248
1486
|
} from "@hiveai/core";
|
|
1249
1487
|
function registerMemoryVerify(memory2) {
|
|
1250
1488
|
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 (!
|
|
1489
|
+
const root = findProjectRoot23(opts.dir);
|
|
1490
|
+
const paths = resolveHaivePaths21(root);
|
|
1491
|
+
if (!existsSync22(paths.memoriesDir)) {
|
|
1254
1492
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
1255
1493
|
process.exitCode = 1;
|
|
1256
1494
|
return;
|
|
1257
1495
|
}
|
|
1258
|
-
const all = await
|
|
1496
|
+
const all = await loadMemoriesFromDir3(paths.memoriesDir);
|
|
1259
1497
|
const targets = opts.id ? all.filter((m) => m.memory.frontmatter.id === opts.id) : all;
|
|
1260
1498
|
if (opts.id && targets.length === 0) {
|
|
1261
1499
|
ui.error(`No memory with id "${opts.id}".`);
|
|
@@ -1324,10 +1562,11 @@ function applyVerification(mem, result) {
|
|
|
1324
1562
|
}
|
|
1325
1563
|
|
|
1326
1564
|
// 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.
|
|
1565
|
+
var program = new Command24();
|
|
1566
|
+
program.name("haive").description("hAIve \u2014 team-first persistent memory layer for AI coding agents").version("0.2.4");
|
|
1329
1567
|
registerInit(program);
|
|
1330
1568
|
registerMcp(program);
|
|
1569
|
+
registerBriefing(program);
|
|
1331
1570
|
registerEmbeddings(program);
|
|
1332
1571
|
registerSync(program);
|
|
1333
1572
|
registerInstallHooks(program);
|