@hiveai/cli 0.1.1 → 0.2.1
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 +514 -208
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command22 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/embeddings.ts
|
|
7
7
|
import { existsSync } from "fs";
|
|
@@ -83,18 +83,63 @@ async function loadEmbeddings() {
|
|
|
83
83
|
return await import("@hiveai/embeddings");
|
|
84
84
|
} catch (err) {
|
|
85
85
|
ui.error(
|
|
86
|
-
"Could not load @hiveai/embeddings.
|
|
86
|
+
"Could not load @hiveai/embeddings. Run: npm install -g @hiveai/embeddings (or `pnpm build` in the monorepo)"
|
|
87
87
|
);
|
|
88
88
|
throw err;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// src/commands/index-code.ts
|
|
93
|
+
import path2 from "path";
|
|
94
|
+
import "commander";
|
|
95
|
+
import {
|
|
96
|
+
buildCodeMap,
|
|
97
|
+
codeMapPath,
|
|
98
|
+
findProjectRoot as findProjectRoot2,
|
|
99
|
+
resolveHaivePaths as resolveHaivePaths2,
|
|
100
|
+
saveCodeMap
|
|
101
|
+
} from "@hiveai/core";
|
|
102
|
+
function registerIndexCode(program2) {
|
|
103
|
+
const idx = program2.command("index").description("Build local indexes that help AIs read less code");
|
|
104
|
+
idx.action(() => idx.help());
|
|
105
|
+
idx.command("code").description("Scan source files and write .ai/code-map.json (file \u2192 exports + 1-line description)").option("-d, --dir <dir>", "project root").option(
|
|
106
|
+
"--exclude <csv>",
|
|
107
|
+
"extra directory names to skip (comma-separated)",
|
|
108
|
+
""
|
|
109
|
+
).action(async (opts) => {
|
|
110
|
+
const root = findProjectRoot2(opts.dir);
|
|
111
|
+
const paths = resolveHaivePaths2(root);
|
|
112
|
+
const extraExcludes = (opts.exclude ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
113
|
+
ui.info(`Indexing source files in ${root}\u2026`);
|
|
114
|
+
const map = await buildCodeMap(root, {
|
|
115
|
+
excludeDirs: [
|
|
116
|
+
"node_modules",
|
|
117
|
+
"dist",
|
|
118
|
+
"build",
|
|
119
|
+
"out",
|
|
120
|
+
".git",
|
|
121
|
+
".next",
|
|
122
|
+
".turbo",
|
|
123
|
+
".vitest-cache",
|
|
124
|
+
"coverage",
|
|
125
|
+
...extraExcludes
|
|
126
|
+
]
|
|
127
|
+
});
|
|
128
|
+
await saveCodeMap(paths, map);
|
|
129
|
+
const fileCount = Object.keys(map.files).length;
|
|
130
|
+
const exportCount = Object.values(map.files).reduce((s, f) => s + f.exports.length, 0);
|
|
131
|
+
ui.success(
|
|
132
|
+
`Indexed ${fileCount} file(s) with ${exportCount} export(s) \u2192 ${path2.relative(root, codeMapPath(paths))}`
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
92
137
|
// src/commands/init.ts
|
|
93
138
|
import { mkdir, writeFile } from "fs/promises";
|
|
94
139
|
import { existsSync as existsSync2 } from "fs";
|
|
95
|
-
import
|
|
140
|
+
import path3 from "path";
|
|
96
141
|
import "commander";
|
|
97
|
-
import { resolveHaivePaths as
|
|
142
|
+
import { resolveHaivePaths as resolveHaivePaths3 } from "@hiveai/core";
|
|
98
143
|
var PROJECT_CONTEXT_TEMPLATE = `# Project context
|
|
99
144
|
|
|
100
145
|
> Generated by \`haive init\`. Edit this file (or let your AI agent fill it via the upcoming MCP \`bootstrap_project\` tool).
|
|
@@ -122,8 +167,8 @@ Memories live under \`.ai/memories/\` (personal/team/module).
|
|
|
122
167
|
`;
|
|
123
168
|
function registerInit(program2) {
|
|
124
169
|
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) => {
|
|
125
|
-
const root =
|
|
126
|
-
const paths =
|
|
170
|
+
const root = path3.resolve(opts.dir);
|
|
171
|
+
const paths = resolveHaivePaths3(root);
|
|
127
172
|
if (existsSync2(paths.haiveDir)) {
|
|
128
173
|
ui.warn(`.ai/ already exists at ${paths.haiveDir} \u2014 leaving existing files in place.`);
|
|
129
174
|
}
|
|
@@ -133,40 +178,91 @@ function registerInit(program2) {
|
|
|
133
178
|
await mkdir(paths.modulesContextDir, { recursive: true });
|
|
134
179
|
if (!existsSync2(paths.projectContext)) {
|
|
135
180
|
await writeFile(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
|
|
136
|
-
ui.success(`Created ${
|
|
181
|
+
ui.success(`Created ${path3.relative(root, paths.projectContext)}`);
|
|
137
182
|
}
|
|
138
183
|
if (opts.bridges) {
|
|
139
184
|
await writeBridge(root, "CLAUDE.md");
|
|
140
185
|
await writeBridge(root, ".cursorrules");
|
|
141
|
-
await writeBridge(root,
|
|
186
|
+
await writeBridge(root, path3.join(".github", "copilot-instructions.md"));
|
|
142
187
|
}
|
|
143
188
|
ui.success(`hAIve initialized at ${root}`);
|
|
144
189
|
ui.info("Next: " + ui.bold("haive memory add --type convention --slug <name>"));
|
|
145
190
|
});
|
|
146
191
|
}
|
|
147
192
|
async function writeBridge(root, relPath) {
|
|
148
|
-
const target =
|
|
193
|
+
const target = path3.join(root, relPath);
|
|
149
194
|
if (existsSync2(target)) {
|
|
150
195
|
ui.info(`Bridge ${relPath} already exists \u2014 skipped`);
|
|
151
196
|
return;
|
|
152
197
|
}
|
|
153
|
-
await mkdir(
|
|
198
|
+
await mkdir(path3.dirname(target), { recursive: true });
|
|
154
199
|
await writeFile(target, BRIDGE_BODY, "utf8");
|
|
155
200
|
ui.success(`Created bridge ${relPath}`);
|
|
156
201
|
}
|
|
157
202
|
|
|
203
|
+
// src/commands/install-hooks.ts
|
|
204
|
+
import { mkdir as mkdir2, writeFile as writeFile2, chmod, readFile } from "fs/promises";
|
|
205
|
+
import { existsSync as existsSync3 } from "fs";
|
|
206
|
+
import path4 from "path";
|
|
207
|
+
import "commander";
|
|
208
|
+
import { findProjectRoot as findProjectRoot4 } from "@hiveai/core";
|
|
209
|
+
var HOOK_MARKER = "# hAIve auto-generated";
|
|
210
|
+
var HOOK_BODY = `#!/bin/sh
|
|
211
|
+
${HOOK_MARKER} \u2014 keep this block to allow upgrades. Hand-edit anything outside it.
|
|
212
|
+
|
|
213
|
+
# After a merge or pull, refresh memory anchors and auto-promote eligible
|
|
214
|
+
# memories so that everyone on this branch sees consistent confidence levels.
|
|
215
|
+
if command -v haive >/dev/null 2>&1; then
|
|
216
|
+
haive sync --quiet --since ORIG_HEAD || true
|
|
217
|
+
elif [ -x ./node_modules/.bin/haive ]; then
|
|
218
|
+
./node_modules/.bin/haive sync --quiet --since ORIG_HEAD || true
|
|
219
|
+
fi
|
|
220
|
+
`;
|
|
221
|
+
var HOOKS = ["post-merge", "post-rewrite"];
|
|
222
|
+
function registerInstallHooks(program2) {
|
|
223
|
+
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 = findProjectRoot4(opts.dir);
|
|
225
|
+
const gitDir = path4.join(root, ".git");
|
|
226
|
+
if (!existsSync3(gitDir)) {
|
|
227
|
+
ui.error(`No .git directory at ${root}.`);
|
|
228
|
+
process.exitCode = 1;
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const hooksDir = path4.join(gitDir, "hooks");
|
|
232
|
+
await mkdir2(hooksDir, { recursive: true });
|
|
233
|
+
let installed = 0;
|
|
234
|
+
let skipped = 0;
|
|
235
|
+
for (const name of HOOKS) {
|
|
236
|
+
const file = path4.join(hooksDir, name);
|
|
237
|
+
if (existsSync3(file) && !opts.force) {
|
|
238
|
+
const existing = await readFile(file, "utf8");
|
|
239
|
+
if (!existing.includes(HOOK_MARKER)) {
|
|
240
|
+
ui.warn(`${name} already exists and was not written by hAIve. Re-run with --force to overwrite.`);
|
|
241
|
+
skipped++;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
await writeFile2(file, HOOK_BODY, "utf8");
|
|
246
|
+
await chmod(file, 493);
|
|
247
|
+
installed++;
|
|
248
|
+
}
|
|
249
|
+
ui.success(`Installed ${installed} hook(s) in .git/hooks/${skipped ? `, skipped ${skipped}` : ""}`);
|
|
250
|
+
ui.info("Test with: git pull (or any merge), then check .ai/memories for status updates.");
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
158
254
|
// src/commands/mcp.ts
|
|
159
255
|
import { spawn } from "child_process";
|
|
160
|
-
import { existsSync as
|
|
256
|
+
import { existsSync as existsSync4 } from "fs";
|
|
161
257
|
import { createRequire } from "module";
|
|
162
|
-
import
|
|
258
|
+
import path5 from "path";
|
|
163
259
|
import { fileURLToPath } from "url";
|
|
164
260
|
import "commander";
|
|
165
|
-
import { findProjectRoot as
|
|
261
|
+
import { findProjectRoot as findProjectRoot5 } from "@hiveai/core";
|
|
166
262
|
var require2 = createRequire(import.meta.url);
|
|
167
263
|
function registerMcp(program2) {
|
|
168
264
|
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) => {
|
|
169
|
-
const root =
|
|
265
|
+
const root = findProjectRoot5(opts.dir);
|
|
170
266
|
const bin = locateMcpBin();
|
|
171
267
|
if (!bin) {
|
|
172
268
|
ui.error(
|
|
@@ -184,47 +280,190 @@ function registerMcp(program2) {
|
|
|
184
280
|
function locateMcpBin() {
|
|
185
281
|
try {
|
|
186
282
|
const pkgPath = require2.resolve("@hiveai/mcp/package.json");
|
|
187
|
-
const pkgDir =
|
|
188
|
-
const candidate =
|
|
189
|
-
if (
|
|
283
|
+
const pkgDir = path5.dirname(pkgPath);
|
|
284
|
+
const candidate = path5.join(pkgDir, "dist", "index.js");
|
|
285
|
+
if (existsSync4(candidate)) return candidate;
|
|
190
286
|
} catch {
|
|
191
287
|
}
|
|
192
|
-
const here =
|
|
193
|
-
const sibling =
|
|
194
|
-
if (
|
|
288
|
+
const here = path5.dirname(fileURLToPath(import.meta.url));
|
|
289
|
+
const sibling = path5.resolve(here, "..", "..", "..", "mcp", "dist", "index.js");
|
|
290
|
+
if (existsSync4(sibling)) return sibling;
|
|
195
291
|
return null;
|
|
196
292
|
}
|
|
197
293
|
|
|
294
|
+
// src/commands/sync.ts
|
|
295
|
+
import { spawnSync } from "child_process";
|
|
296
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
297
|
+
import { existsSync as existsSync5 } from "fs";
|
|
298
|
+
import "path";
|
|
299
|
+
import "commander";
|
|
300
|
+
import {
|
|
301
|
+
DEFAULT_AUTO_PROMOTE_RULE,
|
|
302
|
+
findProjectRoot as findProjectRoot6,
|
|
303
|
+
getUsage,
|
|
304
|
+
isAutoPromoteEligible,
|
|
305
|
+
loadMemoriesFromDir,
|
|
306
|
+
loadUsageIndex,
|
|
307
|
+
resolveHaivePaths as resolveHaivePaths4,
|
|
308
|
+
serializeMemory,
|
|
309
|
+
verifyAnchor
|
|
310
|
+
} from "@hiveai/core";
|
|
311
|
+
function registerSync(program2) {
|
|
312
|
+
program2.command("sync").description("Refresh memory state after a pull/merge: verify anchors, auto-promote, report changes").option("-d, --dir <dir>", "project root").option("--quiet", "minimal output (suitable for git hooks)").option(
|
|
313
|
+
"--since <ref>",
|
|
314
|
+
"git ref/commit to compare against; report memories added/modified/removed since"
|
|
315
|
+
).option("--no-verify", "skip the anchor verification step").option("--no-promote", "skip the auto-promotion step").action(async (opts) => {
|
|
316
|
+
const root = findProjectRoot6(opts.dir);
|
|
317
|
+
const paths = resolveHaivePaths4(root);
|
|
318
|
+
if (!existsSync5(paths.memoriesDir)) {
|
|
319
|
+
if (!opts.quiet) ui.warn(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
320
|
+
process.exitCode = 1;
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const log = (msg) => {
|
|
324
|
+
if (!opts.quiet) console.log(msg);
|
|
325
|
+
};
|
|
326
|
+
let staleMarked = 0;
|
|
327
|
+
let revalidated = 0;
|
|
328
|
+
let promoted = 0;
|
|
329
|
+
if (opts.verify !== false) {
|
|
330
|
+
const memories = await loadMemoriesFromDir(paths.memoriesDir);
|
|
331
|
+
for (const { memory: memory2, filePath } of memories) {
|
|
332
|
+
const isAnchored = memory2.frontmatter.anchor.paths.length > 0 || memory2.frontmatter.anchor.symbols.length > 0;
|
|
333
|
+
if (!isAnchored) continue;
|
|
334
|
+
const result = await verifyAnchor(memory2, { projectRoot: root });
|
|
335
|
+
const verifiedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
336
|
+
if (result.stale) {
|
|
337
|
+
if (memory2.frontmatter.status !== "stale") {
|
|
338
|
+
await writeFile3(
|
|
339
|
+
filePath,
|
|
340
|
+
serializeMemory({
|
|
341
|
+
frontmatter: {
|
|
342
|
+
...memory2.frontmatter,
|
|
343
|
+
status: "stale",
|
|
344
|
+
verified_at: verifiedAt,
|
|
345
|
+
stale_reason: result.reason
|
|
346
|
+
},
|
|
347
|
+
body: memory2.body
|
|
348
|
+
}),
|
|
349
|
+
"utf8"
|
|
350
|
+
);
|
|
351
|
+
staleMarked++;
|
|
352
|
+
}
|
|
353
|
+
} else if (memory2.frontmatter.status === "stale") {
|
|
354
|
+
await writeFile3(
|
|
355
|
+
filePath,
|
|
356
|
+
serializeMemory({
|
|
357
|
+
frontmatter: {
|
|
358
|
+
...memory2.frontmatter,
|
|
359
|
+
status: "validated",
|
|
360
|
+
verified_at: verifiedAt,
|
|
361
|
+
stale_reason: null
|
|
362
|
+
},
|
|
363
|
+
body: memory2.body
|
|
364
|
+
}),
|
|
365
|
+
"utf8"
|
|
366
|
+
);
|
|
367
|
+
revalidated++;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (opts.promote !== false) {
|
|
372
|
+
const memories = await loadMemoriesFromDir(paths.memoriesDir);
|
|
373
|
+
const usage = await loadUsageIndex(paths);
|
|
374
|
+
for (const { memory: memory2, filePath } of memories) {
|
|
375
|
+
if (isAutoPromoteEligible(
|
|
376
|
+
memory2.frontmatter,
|
|
377
|
+
getUsage(usage, memory2.frontmatter.id),
|
|
378
|
+
DEFAULT_AUTO_PROMOTE_RULE
|
|
379
|
+
)) {
|
|
380
|
+
await writeFile3(
|
|
381
|
+
filePath,
|
|
382
|
+
serializeMemory({
|
|
383
|
+
frontmatter: { ...memory2.frontmatter, status: "validated" },
|
|
384
|
+
body: memory2.body
|
|
385
|
+
}),
|
|
386
|
+
"utf8"
|
|
387
|
+
);
|
|
388
|
+
promoted++;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const sinceReport = opts.since ? collectSinceChanges(root, opts.since) : null;
|
|
393
|
+
log(
|
|
394
|
+
`${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
|
+
);
|
|
396
|
+
if (sinceReport && !opts.quiet) {
|
|
397
|
+
if (sinceReport.added.length > 0) {
|
|
398
|
+
log(ui.bold("\nNew memories:"));
|
|
399
|
+
for (const f of sinceReport.added) log(` + ${f}`);
|
|
400
|
+
}
|
|
401
|
+
if (sinceReport.modified.length > 0) {
|
|
402
|
+
log(ui.bold("\nModified:"));
|
|
403
|
+
for (const f of sinceReport.modified) log(` ~ ${f}`);
|
|
404
|
+
}
|
|
405
|
+
if (sinceReport.removed.length > 0) {
|
|
406
|
+
log(ui.bold("\nRemoved:"));
|
|
407
|
+
for (const f of sinceReport.removed) log(` - ${f}`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
function collectSinceChanges(root, ref) {
|
|
413
|
+
const result = spawnSync(
|
|
414
|
+
"git",
|
|
415
|
+
["-C", root, "diff", "--name-status", "--diff-filter=AMD", `${ref}...HEAD`, "--", ".ai/memories"],
|
|
416
|
+
{ encoding: "utf8" }
|
|
417
|
+
);
|
|
418
|
+
if (result.status !== 0) return null;
|
|
419
|
+
const report = { added: [], modified: [], removed: [] };
|
|
420
|
+
for (const line of result.stdout.split("\n")) {
|
|
421
|
+
const [status, ...rest] = line.split(" ");
|
|
422
|
+
const file = rest.join(" ").trim();
|
|
423
|
+
if (!file) continue;
|
|
424
|
+
if (status === "A") report.added.push(file);
|
|
425
|
+
else if (status === "M") report.modified.push(file);
|
|
426
|
+
else if (status === "D") report.removed.push(file);
|
|
427
|
+
}
|
|
428
|
+
return report;
|
|
429
|
+
}
|
|
430
|
+
|
|
198
431
|
// src/commands/memory-add.ts
|
|
199
|
-
import { mkdir as
|
|
200
|
-
import { existsSync as
|
|
201
|
-
import
|
|
432
|
+
import { mkdir as mkdir3, writeFile as writeFile4 } from "fs/promises";
|
|
433
|
+
import { existsSync as existsSync6 } from "fs";
|
|
434
|
+
import path7 from "path";
|
|
202
435
|
import "commander";
|
|
203
436
|
import {
|
|
204
437
|
buildFrontmatter,
|
|
205
|
-
findProjectRoot as
|
|
438
|
+
findProjectRoot as findProjectRoot7,
|
|
439
|
+
inferModulesFromPaths,
|
|
206
440
|
memoryFilePath,
|
|
207
|
-
resolveHaivePaths as
|
|
208
|
-
serializeMemory
|
|
441
|
+
resolveHaivePaths as resolveHaivePaths5,
|
|
442
|
+
serializeMemory as serializeMemory2
|
|
209
443
|
} from "@hiveai/core";
|
|
210
444
|
function registerMemoryAdd(memory2) {
|
|
211
|
-
memory2.command("add").description("Add a new memory (defaults to personal scope \u2014 v0.1 approach B)").requiredOption("--type <type>", "convention | decision | gotcha | architecture | glossary").requiredOption("--slug <slug>", "short identifier used in the file name").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)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
212
|
-
const root =
|
|
213
|
-
const paths =
|
|
214
|
-
if (!
|
|
445
|
+
memory2.command("add").description("Add a new memory (defaults to personal scope \u2014 v0.1 approach B)").requiredOption("--type <type>", "convention | decision | gotcha | architecture | glossary").requiredOption("--slug <slug>", "short identifier used in the file name").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)").option("--no-auto-tag", "disable automatic tag suggestions inferred from anchor paths").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
446
|
+
const root = findProjectRoot7(opts.dir);
|
|
447
|
+
const paths = resolveHaivePaths5(root);
|
|
448
|
+
if (!existsSync6(paths.haiveDir)) {
|
|
215
449
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
216
450
|
process.exitCode = 1;
|
|
217
451
|
return;
|
|
218
452
|
}
|
|
453
|
+
const userTags = parseCsv(opts.tags);
|
|
454
|
+
const anchorPaths = parseCsv(opts.paths);
|
|
455
|
+
const autoTagsEnabled = opts.autoTag !== false;
|
|
456
|
+
const inferredTags = autoTagsEnabled ? inferModulesFromPaths(anchorPaths) : [];
|
|
457
|
+
const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
|
|
219
458
|
const frontmatter = buildFrontmatter({
|
|
220
459
|
type: opts.type,
|
|
221
460
|
slug: opts.slug,
|
|
222
461
|
scope: opts.scope,
|
|
223
462
|
module: opts.module,
|
|
224
|
-
tags:
|
|
463
|
+
tags: mergedTags,
|
|
225
464
|
domain: opts.domain,
|
|
226
465
|
author: opts.author,
|
|
227
|
-
paths:
|
|
466
|
+
paths: anchorPaths,
|
|
228
467
|
symbols: parseCsv(opts.symbols),
|
|
229
468
|
commit: opts.commit
|
|
230
469
|
});
|
|
@@ -233,15 +472,18 @@ function registerMemoryAdd(memory2) {
|
|
|
233
472
|
TODO \u2014 write the memory body.
|
|
234
473
|
`;
|
|
235
474
|
const file = memoryFilePath(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
236
|
-
await
|
|
237
|
-
if (
|
|
475
|
+
await mkdir3(path7.dirname(file), { recursive: true });
|
|
476
|
+
if (existsSync6(file)) {
|
|
238
477
|
ui.error(`Memory already exists at ${file}`);
|
|
239
478
|
process.exitCode = 1;
|
|
240
479
|
return;
|
|
241
480
|
}
|
|
242
|
-
await
|
|
243
|
-
ui.success(`Created ${
|
|
481
|
+
await writeFile4(file, serializeMemory2({ frontmatter, body }), "utf8");
|
|
482
|
+
ui.success(`Created ${path7.relative(root, file)}`);
|
|
244
483
|
ui.info(`scope=${frontmatter.scope} status=${frontmatter.status} id=${frontmatter.id}`);
|
|
484
|
+
if (inferredTags.length > 0) {
|
|
485
|
+
ui.info(`auto-tagged: ${inferredTags.join(", ")} (use --no-auto-tag to disable)`);
|
|
486
|
+
}
|
|
245
487
|
});
|
|
246
488
|
}
|
|
247
489
|
function parseCsv(value) {
|
|
@@ -250,14 +492,14 @@ function parseCsv(value) {
|
|
|
250
492
|
}
|
|
251
493
|
|
|
252
494
|
// src/commands/memory-list.ts
|
|
253
|
-
import { existsSync as
|
|
254
|
-
import
|
|
495
|
+
import { existsSync as existsSync7 } from "fs";
|
|
496
|
+
import path8 from "path";
|
|
255
497
|
import "commander";
|
|
256
|
-
import { findProjectRoot as
|
|
498
|
+
import { findProjectRoot as findProjectRoot8, resolveHaivePaths as resolveHaivePaths6 } from "@hiveai/core";
|
|
257
499
|
|
|
258
500
|
// src/utils/fs.ts
|
|
259
501
|
import {
|
|
260
|
-
loadMemoriesFromDir,
|
|
502
|
+
loadMemoriesFromDir as loadMemoriesFromDir2,
|
|
261
503
|
loadMemory,
|
|
262
504
|
listMarkdownFilesRecursive
|
|
263
505
|
} from "@hiveai/core";
|
|
@@ -265,14 +507,14 @@ import {
|
|
|
265
507
|
// src/commands/memory-list.ts
|
|
266
508
|
function registerMemoryList(memory2) {
|
|
267
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) => {
|
|
268
|
-
const root =
|
|
269
|
-
const paths =
|
|
270
|
-
if (!
|
|
510
|
+
const root = findProjectRoot8(opts.dir);
|
|
511
|
+
const paths = resolveHaivePaths6(root);
|
|
512
|
+
if (!existsSync7(paths.memoriesDir)) {
|
|
271
513
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
272
514
|
process.exitCode = 1;
|
|
273
515
|
return;
|
|
274
516
|
}
|
|
275
|
-
const all = await
|
|
517
|
+
const all = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
276
518
|
const filtered = all.filter((m) => matchesFilters(m, opts));
|
|
277
519
|
if (filtered.length === 0) {
|
|
278
520
|
ui.info("No memories match the filters.");
|
|
@@ -285,7 +527,7 @@ function registerMemoryList(memory2) {
|
|
|
285
527
|
console.log(
|
|
286
528
|
`${ui.bold(fm.id)} ${ui.dim(fm.scope)}/${ui.dim(fm.type)}${moduleStr}${tagStr}`
|
|
287
529
|
);
|
|
288
|
-
console.log(` ${ui.dim(
|
|
530
|
+
console.log(` ${ui.dim(path8.relative(root, filePath))}`);
|
|
289
531
|
}
|
|
290
532
|
console.log(ui.dim(`
|
|
291
533
|
${filtered.length} memor${filtered.length === 1 ? "y" : "ies"}`));
|
|
@@ -301,26 +543,26 @@ function matchesFilters(loaded, opts) {
|
|
|
301
543
|
}
|
|
302
544
|
|
|
303
545
|
// src/commands/memory-promote.ts
|
|
304
|
-
import { mkdir as
|
|
305
|
-
import { existsSync as
|
|
306
|
-
import
|
|
546
|
+
import { mkdir as mkdir4, unlink, writeFile as writeFile5 } from "fs/promises";
|
|
547
|
+
import { existsSync as existsSync8 } from "fs";
|
|
548
|
+
import path9 from "path";
|
|
307
549
|
import "commander";
|
|
308
550
|
import {
|
|
309
|
-
findProjectRoot as
|
|
551
|
+
findProjectRoot as findProjectRoot9,
|
|
310
552
|
memoryFilePath as memoryFilePath2,
|
|
311
|
-
resolveHaivePaths as
|
|
312
|
-
serializeMemory as
|
|
553
|
+
resolveHaivePaths as resolveHaivePaths7,
|
|
554
|
+
serializeMemory as serializeMemory3
|
|
313
555
|
} from "@hiveai/core";
|
|
314
556
|
function registerMemoryPromote(memory2) {
|
|
315
557
|
memory2.command("promote <id>").description("Promote a personal memory to team scope (status -> proposed)").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
316
|
-
const root =
|
|
317
|
-
const paths =
|
|
318
|
-
if (!
|
|
558
|
+
const root = findProjectRoot9(opts.dir);
|
|
559
|
+
const paths = resolveHaivePaths7(root);
|
|
560
|
+
if (!existsSync8(paths.memoriesDir)) {
|
|
319
561
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
320
562
|
process.exitCode = 1;
|
|
321
563
|
return;
|
|
322
564
|
}
|
|
323
|
-
const all = await
|
|
565
|
+
const all = await loadMemoriesFromDir2(paths.personalDir);
|
|
324
566
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
325
567
|
if (!found) {
|
|
326
568
|
ui.error(`No personal memory with id "${id}". (Promotion only applies to personal scope.)`);
|
|
@@ -336,34 +578,34 @@ function registerMemoryPromote(memory2) {
|
|
|
336
578
|
body: found.memory.body
|
|
337
579
|
};
|
|
338
580
|
const newPath = memoryFilePath2(paths, "team", updated.frontmatter.id);
|
|
339
|
-
await
|
|
340
|
-
await
|
|
581
|
+
await mkdir4(path9.dirname(newPath), { recursive: true });
|
|
582
|
+
await writeFile5(newPath, serializeMemory3(updated), "utf8");
|
|
341
583
|
await unlink(found.filePath);
|
|
342
584
|
ui.success(`Promoted ${id} to team scope (status=proposed)`);
|
|
343
|
-
ui.info(`Now at ${
|
|
585
|
+
ui.info(`Now at ${path9.relative(root, newPath)}`);
|
|
344
586
|
});
|
|
345
587
|
}
|
|
346
588
|
|
|
347
589
|
// src/commands/memory-approve.ts
|
|
348
|
-
import { existsSync as
|
|
349
|
-
import { writeFile as
|
|
350
|
-
import
|
|
590
|
+
import { existsSync as existsSync9 } from "fs";
|
|
591
|
+
import { writeFile as writeFile6 } from "fs/promises";
|
|
592
|
+
import path10 from "path";
|
|
351
593
|
import "commander";
|
|
352
594
|
import {
|
|
353
|
-
findProjectRoot as
|
|
354
|
-
resolveHaivePaths as
|
|
355
|
-
serializeMemory as
|
|
595
|
+
findProjectRoot as findProjectRoot10,
|
|
596
|
+
resolveHaivePaths as resolveHaivePaths8,
|
|
597
|
+
serializeMemory as serializeMemory4
|
|
356
598
|
} from "@hiveai/core";
|
|
357
599
|
function registerMemoryApprove(memory2) {
|
|
358
600
|
memory2.command("approve <id>").description("Mark a 'proposed' memory as 'validated' immediately (explicit review)").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
359
|
-
const root =
|
|
360
|
-
const paths =
|
|
361
|
-
if (!
|
|
601
|
+
const root = findProjectRoot10(opts.dir);
|
|
602
|
+
const paths = resolveHaivePaths8(root);
|
|
603
|
+
if (!existsSync9(paths.memoriesDir)) {
|
|
362
604
|
ui.error(`No .ai/memories at ${root}.`);
|
|
363
605
|
process.exitCode = 1;
|
|
364
606
|
return;
|
|
365
607
|
}
|
|
366
|
-
const all = await
|
|
608
|
+
const all = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
367
609
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
368
610
|
if (!found) {
|
|
369
611
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -382,47 +624,47 @@ function registerMemoryApprove(memory2) {
|
|
|
382
624
|
frontmatter: { ...found.memory.frontmatter, status: "validated" },
|
|
383
625
|
body: found.memory.body
|
|
384
626
|
};
|
|
385
|
-
await
|
|
627
|
+
await writeFile6(found.filePath, serializeMemory4(next), "utf8");
|
|
386
628
|
ui.success(`Approved ${id} (status=validated)`);
|
|
387
|
-
ui.info(
|
|
629
|
+
ui.info(path10.relative(root, found.filePath));
|
|
388
630
|
});
|
|
389
631
|
}
|
|
390
632
|
|
|
391
633
|
// src/commands/memory-auto-promote.ts
|
|
392
|
-
import { writeFile as
|
|
393
|
-
import { existsSync as
|
|
394
|
-
import
|
|
634
|
+
import { writeFile as writeFile7 } from "fs/promises";
|
|
635
|
+
import { existsSync as existsSync10 } from "fs";
|
|
636
|
+
import path11 from "path";
|
|
395
637
|
import "commander";
|
|
396
638
|
import {
|
|
397
|
-
DEFAULT_AUTO_PROMOTE_RULE,
|
|
398
|
-
findProjectRoot as
|
|
399
|
-
getUsage,
|
|
400
|
-
isAutoPromoteEligible,
|
|
401
|
-
loadUsageIndex,
|
|
402
|
-
resolveHaivePaths as
|
|
403
|
-
serializeMemory as
|
|
639
|
+
DEFAULT_AUTO_PROMOTE_RULE as DEFAULT_AUTO_PROMOTE_RULE2,
|
|
640
|
+
findProjectRoot as findProjectRoot11,
|
|
641
|
+
getUsage as getUsage2,
|
|
642
|
+
isAutoPromoteEligible as isAutoPromoteEligible2,
|
|
643
|
+
loadUsageIndex as loadUsageIndex2,
|
|
644
|
+
resolveHaivePaths as resolveHaivePaths9,
|
|
645
|
+
serializeMemory as serializeMemory5
|
|
404
646
|
} from "@hiveai/core";
|
|
405
647
|
function registerMemoryAutoPromote(memory2) {
|
|
406
|
-
memory2.command("auto-promote").description("Promote eligible 'proposed' memories to 'validated' based on usage").option("--min-reads <n>", "minimum read_count to qualify", String(
|
|
648
|
+
memory2.command("auto-promote").description("Promote eligible 'proposed' memories to 'validated' based on usage").option("--min-reads <n>", "minimum read_count to qualify", String(DEFAULT_AUTO_PROMOTE_RULE2.minReads)).option(
|
|
407
649
|
"--max-rejections <n>",
|
|
408
650
|
"memories with more rejections than this are skipped",
|
|
409
|
-
String(
|
|
651
|
+
String(DEFAULT_AUTO_PROMOTE_RULE2.maxRejections)
|
|
410
652
|
).option("--apply", "actually write status=validated to disk (default: dry-run)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
411
|
-
const root =
|
|
412
|
-
const paths =
|
|
413
|
-
if (!
|
|
653
|
+
const root = findProjectRoot11(opts.dir);
|
|
654
|
+
const paths = resolveHaivePaths9(root);
|
|
655
|
+
if (!existsSync10(paths.memoriesDir)) {
|
|
414
656
|
ui.error(`No .ai/memories at ${root}.`);
|
|
415
657
|
process.exitCode = 1;
|
|
416
658
|
return;
|
|
417
659
|
}
|
|
418
660
|
const rule = {
|
|
419
|
-
minReads: Number(opts.minReads ??
|
|
420
|
-
maxRejections: Number(opts.maxRejections ??
|
|
661
|
+
minReads: Number(opts.minReads ?? DEFAULT_AUTO_PROMOTE_RULE2.minReads),
|
|
662
|
+
maxRejections: Number(opts.maxRejections ?? DEFAULT_AUTO_PROMOTE_RULE2.maxRejections)
|
|
421
663
|
};
|
|
422
|
-
const memories = await
|
|
423
|
-
const usage = await
|
|
664
|
+
const memories = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
665
|
+
const usage = await loadUsageIndex2(paths);
|
|
424
666
|
const eligible = memories.filter(
|
|
425
|
-
({ memory: memory3 }) =>
|
|
667
|
+
({ memory: memory3 }) => isAutoPromoteEligible2(memory3.frontmatter, getUsage2(usage, memory3.frontmatter.id), rule)
|
|
426
668
|
);
|
|
427
669
|
if (eligible.length === 0) {
|
|
428
670
|
ui.info(
|
|
@@ -432,17 +674,17 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
432
674
|
}
|
|
433
675
|
let written = 0;
|
|
434
676
|
for (const { memory: mem, filePath } of eligible) {
|
|
435
|
-
const u =
|
|
677
|
+
const u = getUsage2(usage, mem.frontmatter.id);
|
|
436
678
|
console.log(
|
|
437
679
|
`${ui.bold(opts.apply ? "PROMOTE" : "would promote")} ${mem.frontmatter.id} ${ui.dim(`reads=${u.read_count} rejections=${u.rejected_count}`)}`
|
|
438
680
|
);
|
|
439
|
-
console.log(` ${ui.dim(
|
|
681
|
+
console.log(` ${ui.dim(path11.relative(root, filePath))}`);
|
|
440
682
|
if (opts.apply) {
|
|
441
683
|
const next = {
|
|
442
684
|
frontmatter: { ...mem.frontmatter, status: "validated" },
|
|
443
685
|
body: mem.body
|
|
444
686
|
};
|
|
445
|
-
await
|
|
687
|
+
await writeFile7(filePath, serializeMemory5(next), "utf8");
|
|
446
688
|
written++;
|
|
447
689
|
}
|
|
448
690
|
}
|
|
@@ -453,25 +695,25 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
453
695
|
|
|
454
696
|
// src/commands/memory-edit.ts
|
|
455
697
|
import { spawn as spawn2 } from "child_process";
|
|
456
|
-
import { existsSync as
|
|
457
|
-
import { readFile } from "fs/promises";
|
|
458
|
-
import
|
|
698
|
+
import { existsSync as existsSync11 } from "fs";
|
|
699
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
700
|
+
import path12 from "path";
|
|
459
701
|
import "commander";
|
|
460
702
|
import {
|
|
461
|
-
findProjectRoot as
|
|
703
|
+
findProjectRoot as findProjectRoot12,
|
|
462
704
|
parseMemory,
|
|
463
|
-
resolveHaivePaths as
|
|
705
|
+
resolveHaivePaths as resolveHaivePaths10
|
|
464
706
|
} from "@hiveai/core";
|
|
465
707
|
function registerMemoryEdit(memory2) {
|
|
466
708
|
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) => {
|
|
467
|
-
const root =
|
|
468
|
-
const paths =
|
|
469
|
-
if (!
|
|
709
|
+
const root = findProjectRoot12(opts.dir);
|
|
710
|
+
const paths = resolveHaivePaths10(root);
|
|
711
|
+
if (!existsSync11(paths.memoriesDir)) {
|
|
470
712
|
ui.error(`No .ai/memories at ${root}.`);
|
|
471
713
|
process.exitCode = 1;
|
|
472
714
|
return;
|
|
473
715
|
}
|
|
474
|
-
const all = await
|
|
716
|
+
const all = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
475
717
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
476
718
|
if (!found) {
|
|
477
719
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -479,13 +721,13 @@ function registerMemoryEdit(memory2) {
|
|
|
479
721
|
return;
|
|
480
722
|
}
|
|
481
723
|
const editor = opts.editor ?? process.env.EDITOR ?? process.env.VISUAL ?? "vi";
|
|
482
|
-
ui.info(`Opening ${
|
|
724
|
+
ui.info(`Opening ${path12.relative(root, found.filePath)} with ${editor}\u2026`);
|
|
483
725
|
const code = await runEditor(editor, found.filePath);
|
|
484
726
|
if (code !== 0) {
|
|
485
727
|
ui.warn(`Editor exited with status ${code}.`);
|
|
486
728
|
}
|
|
487
729
|
try {
|
|
488
|
-
const fresh = await
|
|
730
|
+
const fresh = await readFile2(found.filePath, "utf8");
|
|
489
731
|
parseMemory(fresh);
|
|
490
732
|
ui.success("Memory still parses cleanly.");
|
|
491
733
|
} catch (err) {
|
|
@@ -506,30 +748,30 @@ function runEditor(editor, file) {
|
|
|
506
748
|
}
|
|
507
749
|
|
|
508
750
|
// src/commands/memory-for-files.ts
|
|
509
|
-
import { existsSync as
|
|
510
|
-
import
|
|
751
|
+
import { existsSync as existsSync12 } from "fs";
|
|
752
|
+
import path13 from "path";
|
|
511
753
|
import "commander";
|
|
512
754
|
import {
|
|
513
755
|
deriveConfidence,
|
|
514
|
-
findProjectRoot as
|
|
515
|
-
getUsage as
|
|
516
|
-
inferModulesFromPaths,
|
|
517
|
-
loadUsageIndex as
|
|
756
|
+
findProjectRoot as findProjectRoot13,
|
|
757
|
+
getUsage as getUsage3,
|
|
758
|
+
inferModulesFromPaths as inferModulesFromPaths2,
|
|
759
|
+
loadUsageIndex as loadUsageIndex3,
|
|
518
760
|
memoryMatchesAnchorPaths,
|
|
519
|
-
resolveHaivePaths as
|
|
761
|
+
resolveHaivePaths as resolveHaivePaths11
|
|
520
762
|
} from "@hiveai/core";
|
|
521
763
|
function registerMemoryForFiles(memory2) {
|
|
522
764
|
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) => {
|
|
523
|
-
const root =
|
|
524
|
-
const paths =
|
|
525
|
-
if (!
|
|
765
|
+
const root = findProjectRoot13(opts.dir);
|
|
766
|
+
const paths = resolveHaivePaths11(root);
|
|
767
|
+
if (!existsSync12(paths.memoriesDir)) {
|
|
526
768
|
ui.error(`No .ai/memories at ${root}.`);
|
|
527
769
|
process.exitCode = 1;
|
|
528
770
|
return;
|
|
529
771
|
}
|
|
530
|
-
const all = await
|
|
531
|
-
const usage = await
|
|
532
|
-
const inferred =
|
|
772
|
+
const all = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
773
|
+
const usage = await loadUsageIndex3(paths);
|
|
774
|
+
const inferred = inferModulesFromPaths2(files);
|
|
533
775
|
const byAnchor = [];
|
|
534
776
|
const byModule = [];
|
|
535
777
|
const byDomain = [];
|
|
@@ -572,34 +814,84 @@ function printGroup(root, label, loaded, usage) {
|
|
|
572
814
|
\u2014 ${label} \u2014`));
|
|
573
815
|
for (const { memory: mem, filePath } of loaded) {
|
|
574
816
|
const fm = mem.frontmatter;
|
|
575
|
-
const u =
|
|
817
|
+
const u = getUsage3(usage, fm.id);
|
|
576
818
|
const conf = deriveConfidence(fm, u);
|
|
577
819
|
console.log(`${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.bold(conf)}`);
|
|
578
|
-
console.log(` ${ui.dim(
|
|
820
|
+
console.log(` ${ui.dim(path13.relative(root, filePath))}`);
|
|
579
821
|
}
|
|
580
822
|
}
|
|
581
823
|
|
|
824
|
+
// src/commands/memory-hot.ts
|
|
825
|
+
import { existsSync as existsSync13 } from "fs";
|
|
826
|
+
import path14 from "path";
|
|
827
|
+
import "commander";
|
|
828
|
+
import {
|
|
829
|
+
findProjectRoot as findProjectRoot14,
|
|
830
|
+
getUsage as getUsage4,
|
|
831
|
+
loadUsageIndex as loadUsageIndex4,
|
|
832
|
+
resolveHaivePaths as resolveHaivePaths12
|
|
833
|
+
} from "@hiveai/core";
|
|
834
|
+
function registerMemoryHot(memory2) {
|
|
835
|
+
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) => {
|
|
836
|
+
const root = findProjectRoot14(opts.dir);
|
|
837
|
+
const paths = resolveHaivePaths12(root);
|
|
838
|
+
if (!existsSync13(paths.memoriesDir)) {
|
|
839
|
+
ui.error(`No .ai/memories at ${root}.`);
|
|
840
|
+
process.exitCode = 1;
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
const threshold = Math.max(1, Number(opts.threshold ?? 3));
|
|
844
|
+
const all = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
845
|
+
const usage = await loadUsageIndex4(paths);
|
|
846
|
+
const candidates = all.filter(({ memory: mem }) => {
|
|
847
|
+
const fm = mem.frontmatter;
|
|
848
|
+
if (opts.status && fm.status !== opts.status) return false;
|
|
849
|
+
if (opts.status === void 0 && fm.status !== "draft" && fm.status !== "proposed") {
|
|
850
|
+
return false;
|
|
851
|
+
}
|
|
852
|
+
return getUsage4(usage, fm.id).read_count >= threshold;
|
|
853
|
+
}).sort(
|
|
854
|
+
(a, b) => getUsage4(usage, b.memory.frontmatter.id).read_count - getUsage4(usage, a.memory.frontmatter.id).read_count
|
|
855
|
+
);
|
|
856
|
+
if (candidates.length === 0) {
|
|
857
|
+
ui.info(`No hot memories (threshold=${threshold}).`);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
for (const { memory: mem, filePath } of candidates) {
|
|
861
|
+
const fm = mem.frontmatter;
|
|
862
|
+
const u = getUsage4(usage, fm.id);
|
|
863
|
+
console.log(
|
|
864
|
+
`${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.bold(fm.status)} ${ui.dim(`reads=${u.read_count} rejections=${u.rejected_count}`)}`
|
|
865
|
+
);
|
|
866
|
+
console.log(` ${ui.dim(path14.relative(root, filePath))}`);
|
|
867
|
+
}
|
|
868
|
+
ui.info(
|
|
869
|
+
`${candidates.length} hot \u2014 promote drafts with \`haive memory promote <id>\`, then \`haive memory auto-promote --apply\`.`
|
|
870
|
+
);
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
|
|
582
874
|
// src/commands/memory-pending.ts
|
|
583
|
-
import { existsSync as
|
|
584
|
-
import
|
|
875
|
+
import { existsSync as existsSync14 } from "fs";
|
|
876
|
+
import path15 from "path";
|
|
585
877
|
import "commander";
|
|
586
878
|
import {
|
|
587
|
-
findProjectRoot as
|
|
588
|
-
getUsage as
|
|
589
|
-
loadUsageIndex as
|
|
590
|
-
resolveHaivePaths as
|
|
879
|
+
findProjectRoot as findProjectRoot15,
|
|
880
|
+
getUsage as getUsage5,
|
|
881
|
+
loadUsageIndex as loadUsageIndex5,
|
|
882
|
+
resolveHaivePaths as resolveHaivePaths13
|
|
591
883
|
} from "@hiveai/core";
|
|
592
884
|
function registerMemoryPending(memory2) {
|
|
593
885
|
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) => {
|
|
594
|
-
const root =
|
|
595
|
-
const paths =
|
|
596
|
-
if (!
|
|
886
|
+
const root = findProjectRoot15(opts.dir);
|
|
887
|
+
const paths = resolveHaivePaths13(root);
|
|
888
|
+
if (!existsSync14(paths.memoriesDir)) {
|
|
597
889
|
ui.error(`No .ai/memories at ${root}.`);
|
|
598
890
|
process.exitCode = 1;
|
|
599
891
|
return;
|
|
600
892
|
}
|
|
601
|
-
const all = await
|
|
602
|
-
const usage = await
|
|
893
|
+
const all = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
894
|
+
const usage = await loadUsageIndex5(paths);
|
|
603
895
|
const proposed = all.filter(({ memory: mem }) => {
|
|
604
896
|
if (mem.frontmatter.status !== "proposed") return false;
|
|
605
897
|
if (opts.scope && mem.frontmatter.scope !== opts.scope) return false;
|
|
@@ -610,46 +902,46 @@ function registerMemoryPending(memory2) {
|
|
|
610
902
|
return;
|
|
611
903
|
}
|
|
612
904
|
proposed.sort(
|
|
613
|
-
(a, b) =>
|
|
905
|
+
(a, b) => getUsage5(usage, b.memory.frontmatter.id).read_count - getUsage5(usage, a.memory.frontmatter.id).read_count
|
|
614
906
|
);
|
|
615
907
|
const now = Date.now();
|
|
616
908
|
for (const { memory: mem, filePath } of proposed) {
|
|
617
909
|
const fm = mem.frontmatter;
|
|
618
|
-
const u =
|
|
910
|
+
const u = getUsage5(usage, fm.id);
|
|
619
911
|
const ageDays = Math.floor((now - new Date(fm.created_at).getTime()) / 864e5);
|
|
620
912
|
const ageStr = ageDays === 0 ? "today" : `${ageDays}d`;
|
|
621
913
|
console.log(
|
|
622
914
|
`${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count} rejections=${u.rejected_count}`)}`
|
|
623
915
|
);
|
|
624
|
-
console.log(` ${ui.dim(
|
|
916
|
+
console.log(` ${ui.dim(path15.relative(root, filePath))}`);
|
|
625
917
|
}
|
|
626
918
|
ui.info(`${proposed.length} pending`);
|
|
627
919
|
});
|
|
628
920
|
}
|
|
629
921
|
|
|
630
922
|
// src/commands/memory-query.ts
|
|
631
|
-
import { existsSync as
|
|
632
|
-
import
|
|
923
|
+
import { existsSync as existsSync15 } from "fs";
|
|
924
|
+
import path16 from "path";
|
|
633
925
|
import "commander";
|
|
634
926
|
import {
|
|
635
927
|
extractSnippet,
|
|
636
|
-
findProjectRoot as
|
|
928
|
+
findProjectRoot as findProjectRoot16,
|
|
637
929
|
literalMatchesAllTokens,
|
|
638
930
|
pickSnippetNeedle,
|
|
639
|
-
resolveHaivePaths as
|
|
931
|
+
resolveHaivePaths as resolveHaivePaths14,
|
|
640
932
|
tokenizeQuery
|
|
641
933
|
} from "@hiveai/core";
|
|
642
934
|
function registerMemoryQuery(memory2) {
|
|
643
935
|
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) => {
|
|
644
|
-
const root =
|
|
645
|
-
const paths =
|
|
646
|
-
if (!
|
|
936
|
+
const root = findProjectRoot16(opts.dir);
|
|
937
|
+
const paths = resolveHaivePaths14(root);
|
|
938
|
+
if (!existsSync15(paths.memoriesDir)) {
|
|
647
939
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
648
940
|
process.exitCode = 1;
|
|
649
941
|
return;
|
|
650
942
|
}
|
|
651
943
|
const tokens = tokenizeQuery(text);
|
|
652
|
-
const all = await
|
|
944
|
+
const all = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
653
945
|
const matches = all.filter(({ memory: mem }) => literalMatchesAllTokens(mem, tokens));
|
|
654
946
|
const limit = Math.max(1, Number(opts.limit ?? 20));
|
|
655
947
|
const top = matches.slice(0, limit);
|
|
@@ -661,7 +953,7 @@ function registerMemoryQuery(memory2) {
|
|
|
661
953
|
for (const { memory: mem, filePath } of top) {
|
|
662
954
|
const snippet = extractSnippet(mem.body, snippetNeedle);
|
|
663
955
|
console.log(`${ui.bold(mem.frontmatter.id)} ${ui.dim(mem.frontmatter.scope)}`);
|
|
664
|
-
console.log(` ${ui.dim(
|
|
956
|
+
console.log(` ${ui.dim(path16.relative(root, filePath))}`);
|
|
665
957
|
if (snippet) console.log(` ${snippet}`);
|
|
666
958
|
}
|
|
667
959
|
console.log(
|
|
@@ -672,31 +964,31 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
|
|
|
672
964
|
}
|
|
673
965
|
|
|
674
966
|
// src/commands/memory-reject.ts
|
|
675
|
-
import { existsSync as
|
|
967
|
+
import { existsSync as existsSync16 } from "fs";
|
|
676
968
|
import "commander";
|
|
677
969
|
import {
|
|
678
|
-
findProjectRoot as
|
|
679
|
-
loadUsageIndex as
|
|
970
|
+
findProjectRoot as findProjectRoot17,
|
|
971
|
+
loadUsageIndex as loadUsageIndex6,
|
|
680
972
|
recordRejection,
|
|
681
|
-
resolveHaivePaths as
|
|
973
|
+
resolveHaivePaths as resolveHaivePaths15,
|
|
682
974
|
saveUsageIndex
|
|
683
975
|
} from "@hiveai/core";
|
|
684
976
|
function registerMemoryReject(memory2) {
|
|
685
977
|
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) => {
|
|
686
|
-
const root =
|
|
687
|
-
const paths =
|
|
688
|
-
if (!
|
|
978
|
+
const root = findProjectRoot17(opts.dir);
|
|
979
|
+
const paths = resolveHaivePaths15(root);
|
|
980
|
+
if (!existsSync16(paths.memoriesDir)) {
|
|
689
981
|
ui.error(`No .ai/memories at ${root}.`);
|
|
690
982
|
process.exitCode = 1;
|
|
691
983
|
return;
|
|
692
984
|
}
|
|
693
|
-
const memories = await
|
|
985
|
+
const memories = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
694
986
|
if (!memories.some((m) => m.memory.frontmatter.id === id)) {
|
|
695
987
|
ui.error(`No memory with id "${id}".`);
|
|
696
988
|
process.exitCode = 1;
|
|
697
989
|
return;
|
|
698
990
|
}
|
|
699
|
-
const idx = await
|
|
991
|
+
const idx = await loadUsageIndex6(paths);
|
|
700
992
|
recordRejection(idx, id, opts.reason ?? null);
|
|
701
993
|
await saveUsageIndex(paths, idx);
|
|
702
994
|
const u = idx.by_id[id];
|
|
@@ -708,34 +1000,34 @@ function registerMemoryReject(memory2) {
|
|
|
708
1000
|
}
|
|
709
1001
|
|
|
710
1002
|
// src/commands/memory-rm.ts
|
|
711
|
-
import { existsSync as
|
|
1003
|
+
import { existsSync as existsSync17 } from "fs";
|
|
712
1004
|
import { unlink as unlink2 } from "fs/promises";
|
|
713
|
-
import
|
|
1005
|
+
import path17 from "path";
|
|
714
1006
|
import { createInterface } from "readline/promises";
|
|
715
1007
|
import "commander";
|
|
716
1008
|
import {
|
|
717
|
-
findProjectRoot as
|
|
718
|
-
loadUsageIndex as
|
|
719
|
-
resolveHaivePaths as
|
|
1009
|
+
findProjectRoot as findProjectRoot18,
|
|
1010
|
+
loadUsageIndex as loadUsageIndex7,
|
|
1011
|
+
resolveHaivePaths as resolveHaivePaths16,
|
|
720
1012
|
saveUsageIndex as saveUsageIndex2
|
|
721
1013
|
} from "@hiveai/core";
|
|
722
1014
|
function registerMemoryRm(memory2) {
|
|
723
1015
|
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) => {
|
|
724
|
-
const root =
|
|
725
|
-
const paths =
|
|
726
|
-
if (!
|
|
1016
|
+
const root = findProjectRoot18(opts.dir);
|
|
1017
|
+
const paths = resolveHaivePaths16(root);
|
|
1018
|
+
if (!existsSync17(paths.memoriesDir)) {
|
|
727
1019
|
ui.error(`No .ai/memories at ${root}.`);
|
|
728
1020
|
process.exitCode = 1;
|
|
729
1021
|
return;
|
|
730
1022
|
}
|
|
731
|
-
const all = await
|
|
1023
|
+
const all = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
732
1024
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
733
1025
|
if (!found) {
|
|
734
1026
|
ui.error(`No memory with id "${id}".`);
|
|
735
1027
|
process.exitCode = 1;
|
|
736
1028
|
return;
|
|
737
1029
|
}
|
|
738
|
-
const rel =
|
|
1030
|
+
const rel = path17.relative(root, found.filePath);
|
|
739
1031
|
if (!opts.yes) {
|
|
740
1032
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
741
1033
|
const answer = (await rl.question(`Delete ${rel}? [y/N] `)).trim().toLowerCase();
|
|
@@ -748,7 +1040,7 @@ function registerMemoryRm(memory2) {
|
|
|
748
1040
|
await unlink2(found.filePath);
|
|
749
1041
|
ui.success(`Deleted ${rel}`);
|
|
750
1042
|
if (!opts.keepUsage) {
|
|
751
|
-
const idx = await
|
|
1043
|
+
const idx = await loadUsageIndex7(paths);
|
|
752
1044
|
if (idx.by_id[id]) {
|
|
753
1045
|
delete idx.by_id[id];
|
|
754
1046
|
await saveUsageIndex2(paths, idx);
|
|
@@ -759,27 +1051,27 @@ function registerMemoryRm(memory2) {
|
|
|
759
1051
|
}
|
|
760
1052
|
|
|
761
1053
|
// src/commands/memory-show.ts
|
|
762
|
-
import { existsSync as
|
|
763
|
-
import { readFile as
|
|
764
|
-
import
|
|
1054
|
+
import { existsSync as existsSync18 } from "fs";
|
|
1055
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1056
|
+
import path18 from "path";
|
|
765
1057
|
import "commander";
|
|
766
1058
|
import {
|
|
767
1059
|
deriveConfidence as deriveConfidence2,
|
|
768
|
-
findProjectRoot as
|
|
769
|
-
getUsage as
|
|
770
|
-
loadUsageIndex as
|
|
771
|
-
resolveHaivePaths as
|
|
1060
|
+
findProjectRoot as findProjectRoot19,
|
|
1061
|
+
getUsage as getUsage6,
|
|
1062
|
+
loadUsageIndex as loadUsageIndex8,
|
|
1063
|
+
resolveHaivePaths as resolveHaivePaths17
|
|
772
1064
|
} from "@hiveai/core";
|
|
773
1065
|
function registerMemoryShow(memory2) {
|
|
774
1066
|
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) => {
|
|
775
|
-
const root =
|
|
776
|
-
const paths =
|
|
777
|
-
if (!
|
|
1067
|
+
const root = findProjectRoot19(opts.dir);
|
|
1068
|
+
const paths = resolveHaivePaths17(root);
|
|
1069
|
+
if (!existsSync18(paths.memoriesDir)) {
|
|
778
1070
|
ui.error(`No .ai/memories at ${root}.`);
|
|
779
1071
|
process.exitCode = 1;
|
|
780
1072
|
return;
|
|
781
1073
|
}
|
|
782
|
-
const all = await
|
|
1074
|
+
const all = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
783
1075
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
784
1076
|
if (!found) {
|
|
785
1077
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -787,12 +1079,12 @@ function registerMemoryShow(memory2) {
|
|
|
787
1079
|
return;
|
|
788
1080
|
}
|
|
789
1081
|
if (opts.raw) {
|
|
790
|
-
console.log(await
|
|
1082
|
+
console.log(await readFile3(found.filePath, "utf8"));
|
|
791
1083
|
return;
|
|
792
1084
|
}
|
|
793
1085
|
const fm = found.memory.frontmatter;
|
|
794
|
-
const usage = await
|
|
795
|
-
const u =
|
|
1086
|
+
const usage = await loadUsageIndex8(paths);
|
|
1087
|
+
const u = getUsage6(usage, fm.id);
|
|
796
1088
|
const conf = deriveConfidence2(fm, u);
|
|
797
1089
|
console.log(ui.bold(fm.id));
|
|
798
1090
|
console.log(`${ui.dim("scope:")} ${fm.scope}${fm.module ? ` / ${fm.module}` : ""}`);
|
|
@@ -803,7 +1095,7 @@ function registerMemoryShow(memory2) {
|
|
|
803
1095
|
if (fm.verified_at) console.log(`${ui.dim("verified:")} ${fm.verified_at}`);
|
|
804
1096
|
if (fm.stale_reason) console.log(`${ui.dim("stale:")} ${fm.stale_reason}`);
|
|
805
1097
|
console.log(`${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`);
|
|
806
|
-
console.log(`${ui.dim("file:")} ${
|
|
1098
|
+
console.log(`${ui.dim("file:")} ${path18.relative(root, found.filePath)}`);
|
|
807
1099
|
if (fm.anchor.paths.length || fm.anchor.symbols.length) {
|
|
808
1100
|
console.log(ui.dim("anchor:"));
|
|
809
1101
|
if (fm.anchor.commit) console.log(` ${ui.dim("commit:")} ${fm.anchor.commit}`);
|
|
@@ -818,38 +1110,38 @@ function registerMemoryShow(memory2) {
|
|
|
818
1110
|
}
|
|
819
1111
|
|
|
820
1112
|
// src/commands/memory-stats.ts
|
|
821
|
-
import { existsSync as
|
|
822
|
-
import
|
|
1113
|
+
import { existsSync as existsSync19 } from "fs";
|
|
1114
|
+
import path19 from "path";
|
|
823
1115
|
import "commander";
|
|
824
1116
|
import {
|
|
825
1117
|
deriveConfidence as deriveConfidence3,
|
|
826
|
-
findProjectRoot as
|
|
827
|
-
getUsage as
|
|
828
|
-
loadUsageIndex as
|
|
829
|
-
resolveHaivePaths as
|
|
1118
|
+
findProjectRoot as findProjectRoot20,
|
|
1119
|
+
getUsage as getUsage7,
|
|
1120
|
+
loadUsageIndex as loadUsageIndex9,
|
|
1121
|
+
resolveHaivePaths as resolveHaivePaths18
|
|
830
1122
|
} from "@hiveai/core";
|
|
831
1123
|
function registerMemoryStats(memory2) {
|
|
832
1124
|
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) => {
|
|
833
|
-
const root =
|
|
834
|
-
const paths =
|
|
835
|
-
if (!
|
|
1125
|
+
const root = findProjectRoot20(opts.dir);
|
|
1126
|
+
const paths = resolveHaivePaths18(root);
|
|
1127
|
+
if (!existsSync19(paths.memoriesDir)) {
|
|
836
1128
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
837
1129
|
process.exitCode = 1;
|
|
838
1130
|
return;
|
|
839
1131
|
}
|
|
840
|
-
const all = await
|
|
841
|
-
const usage = await
|
|
1132
|
+
const all = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
1133
|
+
const usage = await loadUsageIndex9(paths);
|
|
842
1134
|
const target = opts.id ? all.filter((m) => m.memory.frontmatter.id === opts.id) : all;
|
|
843
1135
|
if (target.length === 0) {
|
|
844
1136
|
ui.info(opts.id ? `No memory with id "${opts.id}".` : "No memories.");
|
|
845
1137
|
return;
|
|
846
1138
|
}
|
|
847
1139
|
target.sort(
|
|
848
|
-
(a, b) =>
|
|
1140
|
+
(a, b) => getUsage7(usage, b.memory.frontmatter.id).read_count - getUsage7(usage, a.memory.frontmatter.id).read_count
|
|
849
1141
|
);
|
|
850
1142
|
for (const { memory: mem, filePath } of target) {
|
|
851
1143
|
const fm = mem.frontmatter;
|
|
852
|
-
const u =
|
|
1144
|
+
const u = getUsage7(usage, fm.id);
|
|
853
1145
|
const conf = deriveConfidence3(fm, u);
|
|
854
1146
|
console.log(
|
|
855
1147
|
`${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.bold(conf)}`
|
|
@@ -857,32 +1149,32 @@ function registerMemoryStats(memory2) {
|
|
|
857
1149
|
console.log(
|
|
858
1150
|
` ${ui.dim("status:")} ${fm.status} ${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`
|
|
859
1151
|
);
|
|
860
|
-
console.log(` ${ui.dim(
|
|
1152
|
+
console.log(` ${ui.dim(path19.relative(root, filePath))}`);
|
|
861
1153
|
}
|
|
862
1154
|
});
|
|
863
1155
|
}
|
|
864
1156
|
|
|
865
1157
|
// src/commands/memory-verify.ts
|
|
866
|
-
import { writeFile as
|
|
867
|
-
import { existsSync as
|
|
868
|
-
import
|
|
1158
|
+
import { writeFile as writeFile8 } from "fs/promises";
|
|
1159
|
+
import { existsSync as existsSync20 } from "fs";
|
|
1160
|
+
import path20 from "path";
|
|
869
1161
|
import "commander";
|
|
870
1162
|
import {
|
|
871
|
-
findProjectRoot as
|
|
872
|
-
resolveHaivePaths as
|
|
873
|
-
serializeMemory as
|
|
874
|
-
verifyAnchor
|
|
1163
|
+
findProjectRoot as findProjectRoot21,
|
|
1164
|
+
resolveHaivePaths as resolveHaivePaths19,
|
|
1165
|
+
serializeMemory as serializeMemory6,
|
|
1166
|
+
verifyAnchor as verifyAnchor2
|
|
875
1167
|
} from "@hiveai/core";
|
|
876
1168
|
function registerMemoryVerify(memory2) {
|
|
877
1169
|
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) => {
|
|
878
|
-
const root =
|
|
879
|
-
const paths =
|
|
880
|
-
if (!
|
|
1170
|
+
const root = findProjectRoot21(opts.dir);
|
|
1171
|
+
const paths = resolveHaivePaths19(root);
|
|
1172
|
+
if (!existsSync20(paths.memoriesDir)) {
|
|
881
1173
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
882
1174
|
process.exitCode = 1;
|
|
883
1175
|
return;
|
|
884
1176
|
}
|
|
885
|
-
const all = await
|
|
1177
|
+
const all = await loadMemoriesFromDir2(paths.memoriesDir);
|
|
886
1178
|
const targets = opts.id ? all.filter((m) => m.memory.frontmatter.id === opts.id) : all;
|
|
887
1179
|
if (opts.id && targets.length === 0) {
|
|
888
1180
|
ui.error(`No memory with id "${opts.id}".`);
|
|
@@ -894,13 +1186,13 @@ function registerMemoryVerify(memory2) {
|
|
|
894
1186
|
let anchorless = 0;
|
|
895
1187
|
let updated = 0;
|
|
896
1188
|
for (const { memory: mem, filePath } of targets) {
|
|
897
|
-
const result = await
|
|
1189
|
+
const result = await verifyAnchor2(mem, { projectRoot: root });
|
|
898
1190
|
const isAnchored = mem.frontmatter.anchor.paths.length > 0 || mem.frontmatter.anchor.symbols.length > 0;
|
|
899
1191
|
if (!isAnchored) {
|
|
900
1192
|
anchorless++;
|
|
901
1193
|
continue;
|
|
902
1194
|
}
|
|
903
|
-
const rel =
|
|
1195
|
+
const rel = path20.relative(root, filePath);
|
|
904
1196
|
if (result.stale) {
|
|
905
1197
|
staleCount++;
|
|
906
1198
|
console.log(`${ui.bold("STALE")} ${mem.frontmatter.id}`);
|
|
@@ -912,7 +1204,7 @@ function registerMemoryVerify(memory2) {
|
|
|
912
1204
|
}
|
|
913
1205
|
if (opts.update) {
|
|
914
1206
|
const next = applyVerification(mem, result);
|
|
915
|
-
await
|
|
1207
|
+
await writeFile8(filePath, serializeMemory6(next), "utf8");
|
|
916
1208
|
updated++;
|
|
917
1209
|
}
|
|
918
1210
|
}
|
|
@@ -951,11 +1243,14 @@ function applyVerification(mem, result) {
|
|
|
951
1243
|
}
|
|
952
1244
|
|
|
953
1245
|
// src/index.ts
|
|
954
|
-
var program = new
|
|
955
|
-
program.name("haive").description("hAIve \u2014 team-first persistent memory layer for AI coding agents").version("0.1
|
|
1246
|
+
var program = new Command22();
|
|
1247
|
+
program.name("haive").description("hAIve \u2014 team-first persistent memory layer for AI coding agents").version("0.2.1");
|
|
956
1248
|
registerInit(program);
|
|
957
1249
|
registerMcp(program);
|
|
958
1250
|
registerEmbeddings(program);
|
|
1251
|
+
registerSync(program);
|
|
1252
|
+
registerInstallHooks(program);
|
|
1253
|
+
registerIndexCode(program);
|
|
959
1254
|
var memory = program.command("memory").description("Manage memory entries");
|
|
960
1255
|
registerMemoryAdd(memory);
|
|
961
1256
|
registerMemoryList(memory);
|
|
@@ -971,8 +1266,19 @@ registerMemoryEdit(memory);
|
|
|
971
1266
|
registerMemoryRm(memory);
|
|
972
1267
|
registerMemoryPending(memory);
|
|
973
1268
|
registerMemoryApprove(memory);
|
|
1269
|
+
registerMemoryHot(memory);
|
|
974
1270
|
program.parseAsync(process.argv).catch((err) => {
|
|
975
|
-
|
|
1271
|
+
if (isZodError(err)) {
|
|
1272
|
+
for (const issue of err.issues) {
|
|
1273
|
+
const field = issue.path.length > 0 ? `${String(issue.path.join("."))}: ` : "";
|
|
1274
|
+
console.error(`\x1B[31m\u2717\x1B[0m ${field}${issue.message}`);
|
|
1275
|
+
}
|
|
1276
|
+
} else {
|
|
1277
|
+
console.error(err instanceof Error ? err.message : err);
|
|
1278
|
+
}
|
|
976
1279
|
process.exit(1);
|
|
977
1280
|
});
|
|
1281
|
+
function isZodError(err) {
|
|
1282
|
+
return err !== null && typeof err === "object" && "issues" in err && Array.isArray(err.issues);
|
|
1283
|
+
}
|
|
978
1284
|
//# sourceMappingURL=index.js.map
|