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