@hiveai/cli 0.4.5 → 0.5.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 Command32 } from "commander";
4
+ import { Command as Command35 } from "commander";
5
5
 
6
6
  // src/commands/briefing.ts
7
7
  import { existsSync } from "fs";
@@ -341,6 +341,33 @@ function registerIndexCode(program2) {
341
341
  `Indexed ${fileCount} file(s) with ${exportCount} export(s) \u2192 ${path2.relative(root, codeMapPath(paths))}`
342
342
  );
343
343
  });
344
+ idx.command("code-search").description(
345
+ "Build the semantic-search embeddings index for code (powers the code_search MCP tool).\n\n Reads .ai/code-map.json (run `haive index code` first) and embeds each exported\n symbol's metadata (filename + name + kind + description).\n\n Re-runs are incremental: unchanged entries keep their cached vectors, only the\n diff is re-embedded. First run downloads the bge-small-en-v1.5 model (~110MB).\n"
346
+ ).option("-d, --dir <dir>", "project root").action(async (opts) => {
347
+ const root = findProjectRoot4(opts.dir);
348
+ const paths = resolveHaivePaths3(root);
349
+ let mod;
350
+ try {
351
+ mod = await import("@hiveai/embeddings");
352
+ } catch {
353
+ ui.error(
354
+ "@hiveai/embeddings is not installed. Install it (`pnpm add @hiveai/embeddings`) or run `haive embeddings install`."
355
+ );
356
+ process.exit(1);
357
+ }
358
+ ui.info("Loading embedder (first run downloads ~110MB)\u2026");
359
+ const embedder = await mod.Embedder.create();
360
+ ui.info(`Embedding code-map symbols\u2026`);
361
+ try {
362
+ const { report } = await mod.rebuildCodeIndex(paths, embedder);
363
+ ui.success(
364
+ `Code-search index ready: ${report.total} symbols (+${report.added} new, ~${report.updated} updated, =${report.unchanged} cached, -${report.removed} removed)`
365
+ );
366
+ } catch (err) {
367
+ ui.error(err instanceof Error ? err.message : String(err));
368
+ process.exit(1);
369
+ }
370
+ });
344
371
  }
345
372
 
346
373
  // src/commands/init.ts
@@ -3344,9 +3371,294 @@ Next steps:
3344
3371
  });
3345
3372
  }
3346
3373
 
3374
+ // src/commands/stats.ts
3375
+ import "commander";
3376
+ import {
3377
+ aggregateUsage,
3378
+ findProjectRoot as findProjectRoot32,
3379
+ parseSince,
3380
+ readUsageEvents,
3381
+ resolveHaivePaths as resolveHaivePaths29,
3382
+ usageLogSize
3383
+ } from "@hiveai/core";
3384
+ function registerStats(program2) {
3385
+ program2.command("stats").description("Show MCP tool-usage stats over a window (e.g. --since 7d).").option("--since <window>", "ISO date or relative (e.g. '7d', '24h', '30m')", "30d").option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
3386
+ const root = findProjectRoot32(opts.dir);
3387
+ const paths = resolveHaivePaths29(root);
3388
+ const size = await usageLogSize(paths);
3389
+ if (!size.exists) {
3390
+ if (opts.json) {
3391
+ console.log(JSON.stringify({ error: "no_usage_log" }));
3392
+ return;
3393
+ }
3394
+ ui.warn(
3395
+ `No usage log found at ${root}. Stats are populated as the MCP server logs each tool call. Run a session first, then re-check.`
3396
+ );
3397
+ return;
3398
+ }
3399
+ const events = await readUsageEvents(paths);
3400
+ const since = parseSince(opts.since);
3401
+ const aggregate = aggregateUsage(events, since ?? void 0);
3402
+ if (opts.json) {
3403
+ console.log(JSON.stringify(aggregate, null, 2));
3404
+ return;
3405
+ }
3406
+ const window = opts.since ?? "all time";
3407
+ console.log(ui.bold(`hAIve usage stats (${window})`));
3408
+ console.log(
3409
+ ` ${ui.dim("total calls:")} ${aggregate.total} ${ui.dim("unique tools:")} ${aggregate.by_tool.length} ${ui.dim("log lines:")} ${size.lines}`
3410
+ );
3411
+ if (aggregate.window_start) {
3412
+ console.log(
3413
+ ` ${ui.dim("window:")} ${aggregate.window_start.slice(0, 19)} \u2192 ${aggregate.window_end?.slice(0, 19)}`
3414
+ );
3415
+ }
3416
+ if (aggregate.by_tool.length === 0) {
3417
+ ui.info(`No events in window. Try a wider --since (current: ${window}).`);
3418
+ return;
3419
+ }
3420
+ console.log();
3421
+ console.log(ui.bold("Top tools:"));
3422
+ const maxCount = aggregate.by_tool[0]?.count ?? 1;
3423
+ for (const t of aggregate.by_tool.slice(0, 20)) {
3424
+ const bar = "\u2588".repeat(Math.max(1, Math.round(t.count / maxCount * 30)));
3425
+ const pct = (t.count / aggregate.total * 100).toFixed(1);
3426
+ console.log(
3427
+ ` ${t.tool.padEnd(28)} ${ui.green(bar)} ${ui.bold(String(t.count))} ${ui.dim(`(${pct}%, last ${t.last_used.slice(0, 19)})`)}`
3428
+ );
3429
+ }
3430
+ });
3431
+ }
3432
+
3433
+ // src/commands/bench.ts
3434
+ import { performance } from "perf_hooks";
3435
+ import "commander";
3436
+ import {
3437
+ estimateTokens,
3438
+ findProjectRoot as findProjectRoot33,
3439
+ resolveHaivePaths as resolveHaivePaths30
3440
+ } from "@hiveai/core";
3441
+ import {
3442
+ antiPatternsCheck,
3443
+ codeMapTool,
3444
+ codeSearch,
3445
+ getBriefing,
3446
+ getRecap,
3447
+ memRelevantTo
3448
+ } from "@hiveai/mcp";
3449
+ function registerBench(program2) {
3450
+ program2.command("bench").description("Self-test the local hAIve setup: runs core MCP tools against this project and reports latency + payload size.").option("-t, --task <task>", "task description for ranking-aware tools", "audit dependencies for security risks").option("--json", "emit JSON instead of a table", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
3451
+ const root = findProjectRoot33(opts.dir);
3452
+ const paths = resolveHaivePaths30(root);
3453
+ const ctx = { paths };
3454
+ const task = opts.task ?? "audit dependencies for security risks";
3455
+ const scenarios = [
3456
+ async () => {
3457
+ const t0 = performance.now();
3458
+ const out = await getBriefing(
3459
+ {
3460
+ task,
3461
+ files: [],
3462
+ max_tokens: 4e3,
3463
+ max_memories: 8,
3464
+ include_project_context: true,
3465
+ include_module_contexts: true,
3466
+ semantic: true,
3467
+ include_stale: false,
3468
+ track: false,
3469
+ format: "compact",
3470
+ symbols: [],
3471
+ min_semantic_score: 0
3472
+ },
3473
+ ctx
3474
+ );
3475
+ return summarize("get_briefing(compact)", t0, out, [
3476
+ out.low_value ? "low_value (cold-start)" : `${out.memories.length} memories`,
3477
+ `search=${out.search_mode}`
3478
+ ]);
3479
+ },
3480
+ async () => {
3481
+ const t0 = performance.now();
3482
+ const out = await codeMapTool({ paths: [], max_files: 40, max_tokens: 2e3 }, ctx);
3483
+ return summarize("code_map(budget=2k)", t0, out, [
3484
+ out.available ? `${out.files.length}/${out.total_files} files` : "unavailable",
3485
+ out.budget_clipped ? "clipped" : "fits"
3486
+ ]);
3487
+ },
3488
+ async () => {
3489
+ const t0 = performance.now();
3490
+ const out = await getRecap({ scope: "any" }, ctx);
3491
+ return summarize("get_recap", t0, out, [
3492
+ out.recap ? `${out.recap.id.slice(0, 30)}\u2026` : "no recap"
3493
+ ]);
3494
+ },
3495
+ async () => {
3496
+ const t0 = performance.now();
3497
+ const out = await memRelevantTo(
3498
+ { task, files: [], limit: 8, min_semantic_score: 0.25, format: "compact" },
3499
+ ctx
3500
+ );
3501
+ return summarize("mem_relevant_to", t0, out, [
3502
+ `${out.memories.length} memories`,
3503
+ `search=${out.search_mode}`
3504
+ ]);
3505
+ },
3506
+ async () => {
3507
+ const t0 = performance.now();
3508
+ const out = await codeSearch({ query: task, k: 5, min_score: 0.2 }, ctx);
3509
+ return summarize("code_search", t0, out, [
3510
+ out.available ? `${out.hits.length} hits` : "needs index (haive index code-search)"
3511
+ ]);
3512
+ },
3513
+ async () => {
3514
+ const t0 = performance.now();
3515
+ const out = await antiPatternsCheck({ diff: task, paths: [], limit: 5, semantic: true }, ctx);
3516
+ return summarize("anti_patterns_check", t0, out, [
3517
+ `${out.warnings.length}/${out.scanned} warn`
3518
+ ]);
3519
+ }
3520
+ ];
3521
+ const results = [];
3522
+ for (const run of scenarios) {
3523
+ try {
3524
+ results.push(await run());
3525
+ } catch (err) {
3526
+ results.push({
3527
+ name: "(error)",
3528
+ ok: false,
3529
+ latency_ms: 0,
3530
+ payload_tokens: 0,
3531
+ notes: [err instanceof Error ? err.message : String(err)]
3532
+ });
3533
+ }
3534
+ }
3535
+ if (opts.json) {
3536
+ console.log(JSON.stringify({ root, task, scenarios: results }, null, 2));
3537
+ return;
3538
+ }
3539
+ console.log(ui.bold(`hAIve bench \u2014 ${root}`));
3540
+ console.log(ui.dim(`task: ${task}`));
3541
+ console.log();
3542
+ console.log(
3543
+ `${"scenario".padEnd(28)} ${"latency".padStart(8)} ${"tokens".padStart(7)} notes`
3544
+ );
3545
+ console.log("\u2500".repeat(88));
3546
+ for (const r of results) {
3547
+ const status = r.ok ? ui.green("\u2713") : ui.red("\u2717");
3548
+ console.log(
3549
+ `${status} ${r.name.padEnd(26)} ${`${r.latency_ms.toFixed(0)} ms`.padStart(8)} ${String(r.payload_tokens).padStart(7)} ${r.notes.join("; ")}`
3550
+ );
3551
+ }
3552
+ const totalTokens = results.reduce((s, r) => s + r.payload_tokens, 0);
3553
+ const totalMs = results.reduce((s, r) => s + r.latency_ms, 0);
3554
+ console.log("\u2500".repeat(88));
3555
+ console.log(
3556
+ `${ui.dim("totals:")} ${`${totalMs.toFixed(0)} ms`.padStart(8)} ${String(totalTokens).padStart(7)}`
3557
+ );
3558
+ });
3559
+ }
3560
+ function summarize(name, t0, payload, notes) {
3561
+ return {
3562
+ name,
3563
+ ok: true,
3564
+ latency_ms: performance.now() - t0,
3565
+ payload_tokens: estimateTokens(JSON.stringify(payload)),
3566
+ notes
3567
+ };
3568
+ }
3569
+
3570
+ // src/commands/memory-suggest.ts
3571
+ import "commander";
3572
+ import {
3573
+ aggregateUsage as aggregateUsage2,
3574
+ findProjectRoot as findProjectRoot34,
3575
+ parseSince as parseSince2,
3576
+ readUsageEvents as readUsageEvents2,
3577
+ resolveHaivePaths as resolveHaivePaths31
3578
+ } from "@hiveai/core";
3579
+ var SEARCH_TOOLS = /* @__PURE__ */ new Set([
3580
+ "mem_search",
3581
+ "code_search",
3582
+ "mem_relevant_to",
3583
+ "get_briefing"
3584
+ ]);
3585
+ function registerMemorySuggest(memory2) {
3586
+ memory2.command("suggest").description("Suggest memories to create based on recurring search queries in the usage log.").option("--since <window>", "ISO date or relative (e.g. '7d', '24h')", "30d").option("--min <count>", "minimum repeat count to surface a query", "2").option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
3587
+ const root = findProjectRoot34(opts.dir);
3588
+ const paths = resolveHaivePaths31(root);
3589
+ const events = await readUsageEvents2(paths);
3590
+ if (events.length === 0) {
3591
+ if (opts.json) {
3592
+ console.log(JSON.stringify({ suggestions: [] }));
3593
+ return;
3594
+ }
3595
+ ui.warn("No usage log entries yet. Suggestions appear after the MCP server records some calls.");
3596
+ return;
3597
+ }
3598
+ const since = parseSince2(opts.since);
3599
+ const minCount = Math.max(1, parseInt(opts.min ?? "2", 10));
3600
+ const cutoff = since ? since.getTime() : 0;
3601
+ const queries = /* @__PURE__ */ new Map();
3602
+ for (const e of events) {
3603
+ if (cutoff && Date.parse(e.at) < cutoff) continue;
3604
+ if (!SEARCH_TOOLS.has(e.tool)) continue;
3605
+ const key = (e.summary ?? "").toLowerCase().trim();
3606
+ if (!key) continue;
3607
+ const prior = queries.get(key);
3608
+ if (prior) {
3609
+ prior.count++;
3610
+ prior.tools.add(e.tool);
3611
+ if (e.at > prior.last) prior.last = e.at;
3612
+ } else {
3613
+ queries.set(key, { count: 1, tools: /* @__PURE__ */ new Set([e.tool]), last: e.at });
3614
+ }
3615
+ }
3616
+ const suggestions = [...queries.entries()].filter(([, v]) => v.count >= minCount).map(([query, v]) => ({
3617
+ query,
3618
+ count: v.count,
3619
+ tools: [...v.tools].sort(),
3620
+ last_used: v.last,
3621
+ reason: chooseReason(v.tools, v.count)
3622
+ })).sort((a, b) => b.count - a.count);
3623
+ if (opts.json) {
3624
+ console.log(JSON.stringify({ window: opts.since, suggestions }, null, 2));
3625
+ return;
3626
+ }
3627
+ const totals = aggregateUsage2(events, since ?? void 0);
3628
+ console.log(ui.bold(`hAIve memory suggestions (${opts.since ?? "all time"})`));
3629
+ console.log(
3630
+ ui.dim(`scanned ${totals.total} events, ${suggestions.length} repeated queries (\u2265${minCount})`)
3631
+ );
3632
+ console.log();
3633
+ if (suggestions.length === 0) {
3634
+ ui.info("No recurring searches yet \u2014 nothing to suggest.");
3635
+ return;
3636
+ }
3637
+ for (const s of suggestions.slice(0, 30)) {
3638
+ console.log(
3639
+ ` ${ui.bold(`\xD7${s.count}`)} ${ui.dim(`[${s.tools.join(",")}]`)} ${truncate(s.query, 70)}`
3640
+ );
3641
+ console.log(` ${ui.dim("\u2192")} ${s.reason}`);
3642
+ }
3643
+ });
3644
+ }
3645
+ function chooseReason(tools, count) {
3646
+ if (tools.has("code_search")) {
3647
+ return `${count} agents searched the code for this \u2014 consider mem_save (architecture/decision) capturing where it lives.`;
3648
+ }
3649
+ if (tools.has("mem_search") || tools.has("mem_relevant_to")) {
3650
+ return `${count} agents asked but the memory layer had no clear answer \u2014 consider mem_save (convention/decision/gotcha).`;
3651
+ }
3652
+ return `${count} agents asked the briefing for this \u2014 consider promoting the answer to a team memory.`;
3653
+ }
3654
+ function truncate(text, max) {
3655
+ if (text.length <= max) return text;
3656
+ return text.slice(0, max - 1) + "\u2026";
3657
+ }
3658
+
3347
3659
  // src/index.ts
3348
- var program = new Command32();
3349
- program.name("haive").description("hAIve \u2014 team-first persistent memory layer for AI coding agents").version("0.4.5");
3660
+ var program = new Command35();
3661
+ program.name("haive").description("hAIve \u2014 team-first persistent memory layer for AI coding agents").version("0.5.0");
3350
3662
  registerInit(program);
3351
3663
  registerMcp(program);
3352
3664
  registerBriefing(program);
@@ -3376,10 +3688,13 @@ registerMemoryTried(memory);
3376
3688
  registerMemoryImport(memory);
3377
3689
  registerMemoryImportChangelog(memory);
3378
3690
  registerMemoryDigest(memory);
3691
+ registerMemorySuggest(memory);
3379
3692
  var session = program.command("session").description("Manage session lifecycle");
3380
3693
  registerSessionEnd(session);
3381
3694
  registerSnapshot(program);
3382
3695
  registerHub(program);
3696
+ registerStats(program);
3697
+ registerBench(program);
3383
3698
  program.parseAsync(process.argv).catch((err) => {
3384
3699
  if (isZodError(err)) {
3385
3700
  for (const issue of err.issues) {