@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 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";
@@ -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 path2 from "path";
139
+ import path3 from "path";
96
140
  import "commander";
97
- import { resolveHaivePaths as resolveHaivePaths2 } from "@hiveai/core";
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 = path2.resolve(opts.dir);
126
- const paths = resolveHaivePaths2(root);
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 ${path2.relative(root, paths.projectContext)}`);
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, path2.join(".github", "copilot-instructions.md"));
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 = path2.join(root, relPath);
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(path2.dirname(target), { recursive: true });
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 existsSync3 } from "fs";
255
+ import { existsSync as existsSync4 } from "fs";
161
256
  import { createRequire } from "module";
162
- import path3 from "path";
257
+ import path5 from "path";
163
258
  import { fileURLToPath } from "url";
164
259
  import "commander";
165
- import { findProjectRoot as findProjectRoot3 } from "@hiveai/core";
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 = findProjectRoot3(opts.dir);
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 = path3.dirname(pkgPath);
188
- const candidate = path3.join(pkgDir, "dist", "index.js");
189
- if (existsSync3(candidate)) return candidate;
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 = path3.dirname(fileURLToPath(import.meta.url));
193
- const sibling = path3.resolve(here, "..", "..", "..", "mcp", "dist", "index.js");
194
- if (existsSync3(sibling)) return sibling;
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 mkdir2, writeFile as writeFile2 } from "fs/promises";
200
- import { existsSync as existsSync4 } from "fs";
201
- import path4 from "path";
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 findProjectRoot4,
436
+ findProjectRoot as findProjectRoot7,
437
+ inferModulesFromPaths,
206
438
  memoryFilePath,
207
- resolveHaivePaths as resolveHaivePaths3,
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 = findProjectRoot4(opts.dir);
213
- const paths = resolveHaivePaths3(root);
214
- if (!existsSync4(paths.haiveDir)) {
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: parseCsv(opts.tags),
461
+ tags: mergedTags,
225
462
  domain: opts.domain,
226
463
  author: opts.author,
227
- paths: parseCsv(opts.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 mkdir2(path4.dirname(file), { recursive: true });
237
- if (existsSync4(file)) {
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 writeFile2(file, serializeMemory({ frontmatter, body }), "utf8");
243
- ui.success(`Created ${path4.relative(root, file)}`);
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 existsSync5 } from "fs";
254
- import path5 from "path";
493
+ import { existsSync as existsSync7 } from "fs";
494
+ import path8 from "path";
255
495
  import "commander";
256
- import { findProjectRoot as findProjectRoot5, resolveHaivePaths as resolveHaivePaths4 } from "@hiveai/core";
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 = findProjectRoot5(opts.dir);
269
- const paths = resolveHaivePaths4(root);
270
- if (!existsSync5(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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(path5.relative(root, filePath))}`);
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 mkdir3, unlink, writeFile as writeFile3 } from "fs/promises";
305
- import { existsSync as existsSync6 } from "fs";
306
- import path6 from "path";
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 findProjectRoot6,
549
+ findProjectRoot as findProjectRoot9,
310
550
  memoryFilePath as memoryFilePath2,
311
- resolveHaivePaths as resolveHaivePaths5,
312
- serializeMemory as serializeMemory2
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 = findProjectRoot6(opts.dir);
317
- const paths = resolveHaivePaths5(root);
318
- if (!existsSync6(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.personalDir);
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 mkdir3(path6.dirname(newPath), { recursive: true });
340
- await writeFile3(newPath, serializeMemory2(updated), "utf8");
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 ${path6.relative(root, newPath)}`);
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 existsSync7 } from "fs";
349
- import { writeFile as writeFile4 } from "fs/promises";
350
- import path7 from "path";
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 findProjectRoot7,
354
- resolveHaivePaths as resolveHaivePaths6,
355
- serializeMemory as serializeMemory3
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 = findProjectRoot7(opts.dir);
360
- const paths = resolveHaivePaths6(root);
361
- if (!existsSync7(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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 writeFile4(found.filePath, serializeMemory3(next), "utf8");
625
+ await writeFile6(found.filePath, serializeMemory4(next), "utf8");
386
626
  ui.success(`Approved ${id} (status=validated)`);
387
- ui.info(path7.relative(root, found.filePath));
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 writeFile5 } from "fs/promises";
393
- import { existsSync as existsSync8 } from "fs";
394
- import path8 from "path";
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 findProjectRoot8,
399
- getUsage,
400
- isAutoPromoteEligible,
401
- loadUsageIndex,
402
- resolveHaivePaths as resolveHaivePaths7,
403
- serializeMemory as serializeMemory4
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(DEFAULT_AUTO_PROMOTE_RULE.minReads)).option(
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(DEFAULT_AUTO_PROMOTE_RULE.maxRejections)
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 = findProjectRoot8(opts.dir);
412
- const paths = resolveHaivePaths7(root);
413
- if (!existsSync8(paths.memoriesDir)) {
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 ?? DEFAULT_AUTO_PROMOTE_RULE.minReads),
420
- maxRejections: Number(opts.maxRejections ?? DEFAULT_AUTO_PROMOTE_RULE.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 loadMemoriesFromDir(paths.memoriesDir);
423
- const usage = await loadUsageIndex(paths);
662
+ const memories = await loadMemoriesFromDir2(paths.memoriesDir);
663
+ const usage = await loadUsageIndex2(paths);
424
664
  const eligible = memories.filter(
425
- ({ memory: memory3 }) => isAutoPromoteEligible(memory3.frontmatter, getUsage(usage, memory3.frontmatter.id), rule)
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 = getUsage(usage, mem.frontmatter.id);
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(path8.relative(root, filePath))}`);
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 writeFile5(filePath, serializeMemory4(next), "utf8");
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 existsSync9 } from "fs";
457
- import { readFile } from "fs/promises";
458
- import path9 from "path";
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 findProjectRoot9,
701
+ findProjectRoot as findProjectRoot12,
462
702
  parseMemory,
463
- resolveHaivePaths as resolveHaivePaths8
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 = findProjectRoot9(opts.dir);
468
- const paths = resolveHaivePaths8(root);
469
- if (!existsSync9(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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 ${path9.relative(root, found.filePath)} with ${editor}\u2026`);
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 readFile(found.filePath, "utf8");
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 existsSync10 } from "fs";
510
- import path10 from "path";
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 findProjectRoot10,
515
- getUsage as getUsage2,
516
- inferModulesFromPaths,
517
- loadUsageIndex as loadUsageIndex2,
754
+ findProjectRoot as findProjectRoot13,
755
+ getUsage as getUsage3,
756
+ inferModulesFromPaths as inferModulesFromPaths2,
757
+ loadUsageIndex as loadUsageIndex3,
518
758
  memoryMatchesAnchorPaths,
519
- resolveHaivePaths as resolveHaivePaths9
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 = findProjectRoot10(opts.dir);
524
- const paths = resolveHaivePaths9(root);
525
- if (!existsSync10(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
531
- const usage = await loadUsageIndex2(paths);
532
- const inferred = inferModulesFromPaths(files);
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 = getUsage2(usage, fm.id);
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(path10.relative(root, filePath))}`);
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 existsSync11 } from "fs";
584
- import path11 from "path";
873
+ import { existsSync as existsSync14 } from "fs";
874
+ import path15 from "path";
585
875
  import "commander";
586
876
  import {
587
- findProjectRoot as findProjectRoot11,
588
- getUsage as getUsage3,
589
- loadUsageIndex as loadUsageIndex3,
590
- resolveHaivePaths as resolveHaivePaths10
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 = findProjectRoot11(opts.dir);
595
- const paths = resolveHaivePaths10(root);
596
- if (!existsSync11(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
602
- const usage = await loadUsageIndex3(paths);
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) => getUsage3(usage, b.memory.frontmatter.id).read_count - getUsage3(usage, a.memory.frontmatter.id).read_count
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 = getUsage3(usage, fm.id);
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(path11.relative(root, filePath))}`);
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 existsSync12 } from "fs";
632
- import path12 from "path";
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 findProjectRoot12,
926
+ findProjectRoot as findProjectRoot16,
637
927
  literalMatchesAllTokens,
638
928
  pickSnippetNeedle,
639
- resolveHaivePaths as resolveHaivePaths11,
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 = findProjectRoot12(opts.dir);
645
- const paths = resolveHaivePaths11(root);
646
- if (!existsSync12(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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(path12.relative(root, filePath))}`);
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 existsSync13 } from "fs";
965
+ import { existsSync as existsSync16 } from "fs";
676
966
  import "commander";
677
967
  import {
678
- findProjectRoot as findProjectRoot13,
679
- loadUsageIndex as loadUsageIndex4,
968
+ findProjectRoot as findProjectRoot17,
969
+ loadUsageIndex as loadUsageIndex6,
680
970
  recordRejection,
681
- resolveHaivePaths as resolveHaivePaths12,
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 = findProjectRoot13(opts.dir);
687
- const paths = resolveHaivePaths12(root);
688
- if (!existsSync13(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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 loadUsageIndex4(paths);
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 existsSync14 } from "fs";
1001
+ import { existsSync as existsSync17 } from "fs";
712
1002
  import { unlink as unlink2 } from "fs/promises";
713
- import path13 from "path";
1003
+ import path17 from "path";
714
1004
  import { createInterface } from "readline/promises";
715
1005
  import "commander";
716
1006
  import {
717
- findProjectRoot as findProjectRoot14,
718
- loadUsageIndex as loadUsageIndex5,
719
- resolveHaivePaths as resolveHaivePaths13,
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 = findProjectRoot14(opts.dir);
725
- const paths = resolveHaivePaths13(root);
726
- if (!existsSync14(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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 = path13.relative(root, found.filePath);
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 loadUsageIndex5(paths);
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 existsSync15 } from "fs";
763
- import { readFile as readFile2 } from "fs/promises";
764
- import path14 from "path";
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 findProjectRoot15,
769
- getUsage as getUsage4,
770
- loadUsageIndex as loadUsageIndex6,
771
- resolveHaivePaths as resolveHaivePaths14
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 = findProjectRoot15(opts.dir);
776
- const paths = resolveHaivePaths14(root);
777
- if (!existsSync15(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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 readFile2(found.filePath, "utf8"));
1080
+ console.log(await readFile3(found.filePath, "utf8"));
791
1081
  return;
792
1082
  }
793
1083
  const fm = found.memory.frontmatter;
794
- const usage = await loadUsageIndex6(paths);
795
- const u = getUsage4(usage, fm.id);
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:")} ${path14.relative(root, found.filePath)}`);
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 existsSync16 } from "fs";
822
- import path15 from "path";
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 findProjectRoot16,
827
- getUsage as getUsage5,
828
- loadUsageIndex as loadUsageIndex7,
829
- resolveHaivePaths as resolveHaivePaths15
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 = findProjectRoot16(opts.dir);
834
- const paths = resolveHaivePaths15(root);
835
- if (!existsSync16(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
841
- const usage = await loadUsageIndex7(paths);
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) => getUsage5(usage, b.memory.frontmatter.id).read_count - getUsage5(usage, a.memory.frontmatter.id).read_count
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 = getUsage5(usage, fm.id);
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(path15.relative(root, filePath))}`);
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 writeFile6 } from "fs/promises";
867
- import { existsSync as existsSync17 } from "fs";
868
- import path16 from "path";
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 findProjectRoot17,
872
- resolveHaivePaths as resolveHaivePaths16,
873
- serializeMemory as serializeMemory5,
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 = findProjectRoot17(opts.dir);
879
- const paths = resolveHaivePaths16(root);
880
- if (!existsSync17(paths.memoriesDir)) {
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 loadMemoriesFromDir(paths.memoriesDir);
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 verifyAnchor(mem, { projectRoot: root });
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 = path16.relative(root, filePath);
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 writeFile6(filePath, serializeMemory5(next), "utf8");
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 Command18();
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);