@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 +318 -3
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as 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
|
|
3349
|
-
program.name("haive").description("hAIve \u2014 team-first persistent memory layer for AI coding agents").version("0.
|
|
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) {
|