@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 CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command18 } from "commander";
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. Install it or run `pnpm build` in the monorepo."
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 path2 from "path";
140
+ import path3 from "path";
96
141
  import "commander";
97
- import { resolveHaivePaths as resolveHaivePaths2 } from "@hiveai/core";
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 = path2.resolve(opts.dir);
126
- const paths = resolveHaivePaths2(root);
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 ${path2.relative(root, paths.projectContext)}`);
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, path2.join(".github", "copilot-instructions.md"));
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 = path2.join(root, relPath);
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(path2.dirname(target), { recursive: true });
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 existsSync3 } from "fs";
256
+ import { existsSync as existsSync4 } from "fs";
161
257
  import { createRequire } from "module";
162
- import path3 from "path";
258
+ import path5 from "path";
163
259
  import { fileURLToPath } from "url";
164
260
  import "commander";
165
- import { findProjectRoot as findProjectRoot3 } from "@hiveai/core";
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 = findProjectRoot3(opts.dir);
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 = path3.dirname(pkgPath);
188
- const candidate = path3.join(pkgDir, "dist", "index.js");
189
- if (existsSync3(candidate)) return candidate;
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 = path3.dirname(fileURLToPath(import.meta.url));
193
- const sibling = path3.resolve(here, "..", "..", "..", "mcp", "dist", "index.js");
194
- if (existsSync3(sibling)) return sibling;
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 mkdir2, writeFile as writeFile2 } from "fs/promises";
200
- import { existsSync as existsSync4 } from "fs";
201
- import path4 from "path";
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 findProjectRoot4,
438
+ findProjectRoot as findProjectRoot7,
439
+ inferModulesFromPaths,
206
440
  memoryFilePath,
207
- resolveHaivePaths as resolveHaivePaths3,
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 = findProjectRoot4(opts.dir);
213
- const paths = resolveHaivePaths3(root);
214
- if (!existsSync4(paths.haiveDir)) {
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: parseCsv(opts.tags),
463
+ tags: mergedTags,
225
464
  domain: opts.domain,
226
465
  author: opts.author,
227
- paths: parseCsv(opts.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 mkdir2(path4.dirname(file), { recursive: true });
237
- if (existsSync4(file)) {
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 writeFile2(file, serializeMemory({ frontmatter, body }), "utf8");
243
- ui.success(`Created ${path4.relative(root, file)}`);
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 existsSync5 } from "fs";
254
- import path5 from "path";
495
+ import { existsSync as existsSync7 } from "fs";
496
+ import path8 from "path";
255
497
  import "commander";
256
- import { findProjectRoot as findProjectRoot5, resolveHaivePaths as resolveHaivePaths4 } from "@hiveai/core";
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 = findProjectRoot5(opts.dir);
269
- const paths = resolveHaivePaths4(root);
270
- if (!existsSync5(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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(path5.relative(root, filePath))}`);
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 mkdir3, unlink, writeFile as writeFile3 } from "fs/promises";
305
- import { existsSync as existsSync6 } from "fs";
306
- import path6 from "path";
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 findProjectRoot6,
551
+ findProjectRoot as findProjectRoot9,
310
552
  memoryFilePath as memoryFilePath2,
311
- resolveHaivePaths as resolveHaivePaths5,
312
- serializeMemory as serializeMemory2
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 = findProjectRoot6(opts.dir);
317
- const paths = resolveHaivePaths5(root);
318
- if (!existsSync6(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.personalDir);
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 mkdir3(path6.dirname(newPath), { recursive: true });
340
- await writeFile3(newPath, serializeMemory2(updated), "utf8");
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 ${path6.relative(root, newPath)}`);
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 existsSync7 } from "fs";
349
- import { writeFile as writeFile4 } from "fs/promises";
350
- import path7 from "path";
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 findProjectRoot7,
354
- resolveHaivePaths as resolveHaivePaths6,
355
- serializeMemory as serializeMemory3
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 = findProjectRoot7(opts.dir);
360
- const paths = resolveHaivePaths6(root);
361
- if (!existsSync7(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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 writeFile4(found.filePath, serializeMemory3(next), "utf8");
627
+ await writeFile6(found.filePath, serializeMemory4(next), "utf8");
386
628
  ui.success(`Approved ${id} (status=validated)`);
387
- ui.info(path7.relative(root, found.filePath));
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 writeFile5 } from "fs/promises";
393
- import { existsSync as existsSync8 } from "fs";
394
- import path8 from "path";
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 findProjectRoot8,
399
- getUsage,
400
- isAutoPromoteEligible,
401
- loadUsageIndex,
402
- resolveHaivePaths as resolveHaivePaths7,
403
- serializeMemory as serializeMemory4
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(DEFAULT_AUTO_PROMOTE_RULE.minReads)).option(
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(DEFAULT_AUTO_PROMOTE_RULE.maxRejections)
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 = findProjectRoot8(opts.dir);
412
- const paths = resolveHaivePaths7(root);
413
- if (!existsSync8(paths.memoriesDir)) {
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 ?? DEFAULT_AUTO_PROMOTE_RULE.minReads),
420
- maxRejections: Number(opts.maxRejections ?? DEFAULT_AUTO_PROMOTE_RULE.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 loadMemoriesFromDir(paths.memoriesDir);
423
- const usage = await loadUsageIndex(paths);
664
+ const memories = await loadMemoriesFromDir2(paths.memoriesDir);
665
+ const usage = await loadUsageIndex2(paths);
424
666
  const eligible = memories.filter(
425
- ({ memory: memory3 }) => isAutoPromoteEligible(memory3.frontmatter, getUsage(usage, memory3.frontmatter.id), rule)
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 = getUsage(usage, mem.frontmatter.id);
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(path8.relative(root, filePath))}`);
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 writeFile5(filePath, serializeMemory4(next), "utf8");
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 existsSync9 } from "fs";
457
- import { readFile } from "fs/promises";
458
- import path9 from "path";
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 findProjectRoot9,
703
+ findProjectRoot as findProjectRoot12,
462
704
  parseMemory,
463
- resolveHaivePaths as resolveHaivePaths8
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 = findProjectRoot9(opts.dir);
468
- const paths = resolveHaivePaths8(root);
469
- if (!existsSync9(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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 ${path9.relative(root, found.filePath)} with ${editor}\u2026`);
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 readFile(found.filePath, "utf8");
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 existsSync10 } from "fs";
510
- import path10 from "path";
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 findProjectRoot10,
515
- getUsage as getUsage2,
516
- inferModulesFromPaths,
517
- loadUsageIndex as loadUsageIndex2,
756
+ findProjectRoot as findProjectRoot13,
757
+ getUsage as getUsage3,
758
+ inferModulesFromPaths as inferModulesFromPaths2,
759
+ loadUsageIndex as loadUsageIndex3,
518
760
  memoryMatchesAnchorPaths,
519
- resolveHaivePaths as resolveHaivePaths9
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 = findProjectRoot10(opts.dir);
524
- const paths = resolveHaivePaths9(root);
525
- if (!existsSync10(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
531
- const usage = await loadUsageIndex2(paths);
532
- const inferred = inferModulesFromPaths(files);
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 = getUsage2(usage, fm.id);
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(path10.relative(root, filePath))}`);
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 existsSync11 } from "fs";
584
- import path11 from "path";
875
+ import { existsSync as existsSync14 } from "fs";
876
+ import path15 from "path";
585
877
  import "commander";
586
878
  import {
587
- findProjectRoot as findProjectRoot11,
588
- getUsage as getUsage3,
589
- loadUsageIndex as loadUsageIndex3,
590
- resolveHaivePaths as resolveHaivePaths10
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 = findProjectRoot11(opts.dir);
595
- const paths = resolveHaivePaths10(root);
596
- if (!existsSync11(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
602
- const usage = await loadUsageIndex3(paths);
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) => getUsage3(usage, b.memory.frontmatter.id).read_count - getUsage3(usage, a.memory.frontmatter.id).read_count
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 = getUsage3(usage, fm.id);
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(path11.relative(root, filePath))}`);
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 existsSync12 } from "fs";
632
- import path12 from "path";
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 findProjectRoot12,
928
+ findProjectRoot as findProjectRoot16,
637
929
  literalMatchesAllTokens,
638
930
  pickSnippetNeedle,
639
- resolveHaivePaths as resolveHaivePaths11,
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 = findProjectRoot12(opts.dir);
645
- const paths = resolveHaivePaths11(root);
646
- if (!existsSync12(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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(path12.relative(root, filePath))}`);
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 existsSync13 } from "fs";
967
+ import { existsSync as existsSync16 } from "fs";
676
968
  import "commander";
677
969
  import {
678
- findProjectRoot as findProjectRoot13,
679
- loadUsageIndex as loadUsageIndex4,
970
+ findProjectRoot as findProjectRoot17,
971
+ loadUsageIndex as loadUsageIndex6,
680
972
  recordRejection,
681
- resolveHaivePaths as resolveHaivePaths12,
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 = findProjectRoot13(opts.dir);
687
- const paths = resolveHaivePaths12(root);
688
- if (!existsSync13(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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 loadUsageIndex4(paths);
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 existsSync14 } from "fs";
1003
+ import { existsSync as existsSync17 } from "fs";
712
1004
  import { unlink as unlink2 } from "fs/promises";
713
- import path13 from "path";
1005
+ import path17 from "path";
714
1006
  import { createInterface } from "readline/promises";
715
1007
  import "commander";
716
1008
  import {
717
- findProjectRoot as findProjectRoot14,
718
- loadUsageIndex as loadUsageIndex5,
719
- resolveHaivePaths as resolveHaivePaths13,
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 = findProjectRoot14(opts.dir);
725
- const paths = resolveHaivePaths13(root);
726
- if (!existsSync14(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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 = path13.relative(root, found.filePath);
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 loadUsageIndex5(paths);
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 existsSync15 } from "fs";
763
- import { readFile as readFile2 } from "fs/promises";
764
- import path14 from "path";
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 findProjectRoot15,
769
- getUsage as getUsage4,
770
- loadUsageIndex as loadUsageIndex6,
771
- resolveHaivePaths as resolveHaivePaths14
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 = findProjectRoot15(opts.dir);
776
- const paths = resolveHaivePaths14(root);
777
- if (!existsSync15(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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 readFile2(found.filePath, "utf8"));
1082
+ console.log(await readFile3(found.filePath, "utf8"));
791
1083
  return;
792
1084
  }
793
1085
  const fm = found.memory.frontmatter;
794
- const usage = await loadUsageIndex6(paths);
795
- const u = getUsage4(usage, fm.id);
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:")} ${path14.relative(root, found.filePath)}`);
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 existsSync16 } from "fs";
822
- import path15 from "path";
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 findProjectRoot16,
827
- getUsage as getUsage5,
828
- loadUsageIndex as loadUsageIndex7,
829
- resolveHaivePaths as resolveHaivePaths15
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 = findProjectRoot16(opts.dir);
834
- const paths = resolveHaivePaths15(root);
835
- if (!existsSync16(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
841
- const usage = await loadUsageIndex7(paths);
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) => getUsage5(usage, b.memory.frontmatter.id).read_count - getUsage5(usage, a.memory.frontmatter.id).read_count
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 = getUsage5(usage, fm.id);
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(path15.relative(root, filePath))}`);
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 writeFile6 } from "fs/promises";
867
- import { existsSync as existsSync17 } from "fs";
868
- import path16 from "path";
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 findProjectRoot17,
872
- resolveHaivePaths as resolveHaivePaths16,
873
- serializeMemory as serializeMemory5,
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 = findProjectRoot17(opts.dir);
879
- const paths = resolveHaivePaths16(root);
880
- if (!existsSync17(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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 verifyAnchor(mem, { projectRoot: root });
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 = path16.relative(root, filePath);
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 writeFile6(filePath, serializeMemory5(next), "utf8");
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 Command18();
955
- program.name("haive").description("hAIve \u2014 team-first persistent memory layer for AI coding agents").version("0.1.0");
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
- console.error(err instanceof Error ? err.message : err);
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