@hiveai/cli 0.12.1 → 0.12.3
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/README.md +29 -0
- package/dist/index.js +132 -13
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -187,6 +187,35 @@ Autogenerated sensors are conservative: they start as `warn` and `autogen: true`
|
|
|
187
187
|
high-confidence sensors to `severity: block`, which makes a deterministic pre-commit blocker when the
|
|
188
188
|
sensor matches added diff lines.
|
|
189
189
|
|
|
190
|
+
### `haive eval`
|
|
191
|
+
|
|
192
|
+
Run the repeatable quality gate for hAIve itself or for a project using hAIve:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
haive eval
|
|
196
|
+
haive eval --semantic-only
|
|
197
|
+
haive eval --spec .ai/eval/spec.json --fail-under 80
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Without `--spec`, hAIve synthesizes retrieval cases from anchored memories. If `.ai/eval/spec.json`
|
|
201
|
+
exists, it is loaded automatically and merged with those synthesized retrieval cases. Use that file
|
|
202
|
+
for labeled sensor cases and hard retrieval probes so CI measures both “did the right memory surface?”
|
|
203
|
+
and “did the executable guardrail fire?”.
|
|
204
|
+
|
|
205
|
+
### `haive doctor`
|
|
206
|
+
|
|
207
|
+
`doctor` is the first stop when hAIve feels inconsistent locally:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
haive doctor
|
|
211
|
+
haive doctor --json
|
|
212
|
+
haive doctor --fix
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
It reports missing `pnpm`, stale workspace `dist` artifacts after a pull, global CLI/MCP version skew,
|
|
216
|
+
outdated code-search indexes, memory-lint findings, and harness coverage. The output is intentionally
|
|
217
|
+
actionable: every setup finding should carry the exact command to run next.
|
|
218
|
+
|
|
190
219
|
### `haive benchmark`
|
|
191
220
|
|
|
192
221
|
Turn hAIve-vs-plain agent trials into a repeatable demo/report.
|
package/dist/index.js
CHANGED
|
@@ -2743,7 +2743,7 @@ ${SEED_FOOTER(stack)}` });
|
|
|
2743
2743
|
}
|
|
2744
2744
|
|
|
2745
2745
|
// src/commands/init.ts
|
|
2746
|
-
var HAIVE_GITHUB_ACTION_REF = `v${"0.12.
|
|
2746
|
+
var HAIVE_GITHUB_ACTION_REF = `v${"0.12.3"}`;
|
|
2747
2747
|
var PROJECT_CONTEXT_TEMPLATE = `# Project context
|
|
2748
2748
|
|
|
2749
2749
|
> Generated by \`haive init\`. Run \`haive init --bootstrap\` to auto-fill from your codebase,
|
|
@@ -7531,7 +7531,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
7531
7531
|
};
|
|
7532
7532
|
}
|
|
7533
7533
|
var SERVER_NAME = "haive";
|
|
7534
|
-
var SERVER_VERSION = "0.12.
|
|
7534
|
+
var SERVER_VERSION = "0.12.3";
|
|
7535
7535
|
function jsonResult(data) {
|
|
7536
7536
|
return {
|
|
7537
7537
|
content: [
|
|
@@ -12259,7 +12259,8 @@ function registerEval(program2) {
|
|
|
12259
12259
|
}
|
|
12260
12260
|
const k = Math.max(1, parseInt(opts.top ?? "8", 10) || 8);
|
|
12261
12261
|
const ctx = { paths };
|
|
12262
|
-
const
|
|
12262
|
+
const resolvedSpec = await resolveSpec(opts, root, paths.memoriesDir);
|
|
12263
|
+
const spec = resolvedSpec.spec;
|
|
12263
12264
|
if ((spec.retrieval?.length ?? 0) === 0 && (spec.sensors?.length ?? 0) === 0) {
|
|
12264
12265
|
ui.warn("No eval cases (no anchored memories and no --spec). Nothing to score.");
|
|
12265
12266
|
return;
|
|
@@ -12284,9 +12285,9 @@ function registerEval(program2) {
|
|
|
12284
12285
|
}
|
|
12285
12286
|
const report = buildReport(retrievalAgg, sensorAgg);
|
|
12286
12287
|
if (opts.json) {
|
|
12287
|
-
console.log(JSON.stringify({ root, k, report }, null, 2));
|
|
12288
|
+
console.log(JSON.stringify({ root, k, spec_source: resolvedSpec.source, report }, null, 2));
|
|
12288
12289
|
} else {
|
|
12289
|
-
const md = renderMarkdown2(root, k, report);
|
|
12290
|
+
const md = renderMarkdown2(root, k, resolvedSpec.source, report);
|
|
12290
12291
|
if (opts.out) {
|
|
12291
12292
|
const outFile = path43.isAbsolute(opts.out) ? opts.out : path43.join(root, opts.out);
|
|
12292
12293
|
await writeFile29(outFile, md, "utf8");
|
|
@@ -12307,14 +12308,31 @@ function registerEval(program2) {
|
|
|
12307
12308
|
}
|
|
12308
12309
|
});
|
|
12309
12310
|
}
|
|
12310
|
-
async function resolveSpec(opts, memoriesDir) {
|
|
12311
|
+
async function resolveSpec(opts, root, memoriesDir) {
|
|
12311
12312
|
if (opts.spec) {
|
|
12312
12313
|
const file = path43.resolve(opts.spec);
|
|
12313
12314
|
const raw = await readFile20(file, "utf8");
|
|
12314
|
-
return JSON.parse(raw);
|
|
12315
|
+
return { spec: JSON.parse(raw), source: file };
|
|
12316
|
+
}
|
|
12317
|
+
const defaultSpec = path43.join(root, ".ai", "eval", "spec.json");
|
|
12318
|
+
if (existsSync64(defaultSpec)) {
|
|
12319
|
+
const raw = await readFile20(defaultSpec, "utf8");
|
|
12320
|
+
const explicit = JSON.parse(raw);
|
|
12321
|
+
const memories2 = await loadMemoriesFromDir26(memoriesDir);
|
|
12322
|
+
const synthesized = synthesizeSelfEvalCases(memories2, { includeFiles: !opts.semanticOnly });
|
|
12323
|
+
return {
|
|
12324
|
+
spec: {
|
|
12325
|
+
retrieval: [...synthesized, ...explicit.retrieval ?? []],
|
|
12326
|
+
sensors: explicit.sensors ?? []
|
|
12327
|
+
},
|
|
12328
|
+
source: ".ai/eval/spec.json + synthesized anchored retrieval"
|
|
12329
|
+
};
|
|
12315
12330
|
}
|
|
12316
12331
|
const memories = await loadMemoriesFromDir26(memoriesDir);
|
|
12317
|
-
return {
|
|
12332
|
+
return {
|
|
12333
|
+
spec: { retrieval: synthesizeSelfEvalCases(memories, { includeFiles: !opts.semanticOnly }) },
|
|
12334
|
+
source: "synthesized anchored retrieval"
|
|
12335
|
+
};
|
|
12318
12336
|
}
|
|
12319
12337
|
async function runRetrieval(c, k, ctx) {
|
|
12320
12338
|
const out = await getBriefing(
|
|
@@ -12346,11 +12364,12 @@ async function runSensorCase(c, ctx) {
|
|
|
12346
12364
|
function pct(n) {
|
|
12347
12365
|
return `${Math.round(n * 100)}%`;
|
|
12348
12366
|
}
|
|
12349
|
-
function renderMarkdown2(root, k, report) {
|
|
12367
|
+
function renderMarkdown2(root, k, source, report) {
|
|
12350
12368
|
const lines = [
|
|
12351
12369
|
"# hAIve eval report",
|
|
12352
12370
|
"",
|
|
12353
12371
|
`Project: \`${root}\` \xB7 top-k: ${k}`,
|
|
12372
|
+
`Spec: ${source}`,
|
|
12354
12373
|
"",
|
|
12355
12374
|
`## Overall score: ${report.score}/100`,
|
|
12356
12375
|
""
|
|
@@ -12982,14 +13001,15 @@ function registerDoctor(program2) {
|
|
|
12982
13001
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
|
|
12983
13002
|
});
|
|
12984
13003
|
}
|
|
12985
|
-
findings.push(...await collectInstallFindings(root, "0.12.
|
|
13004
|
+
findings.push(...await collectInstallFindings(root, "0.12.3"));
|
|
13005
|
+
findings.push(...await collectToolchainFindings(root));
|
|
12986
13006
|
try {
|
|
12987
13007
|
const legacyRaw = execSync3("haive-mcp --version", {
|
|
12988
13008
|
encoding: "utf8",
|
|
12989
13009
|
timeout: 3e3,
|
|
12990
13010
|
stdio: ["ignore", "pipe", "ignore"]
|
|
12991
13011
|
}).trim();
|
|
12992
|
-
const cliVersion = "0.12.
|
|
13012
|
+
const cliVersion = "0.12.3";
|
|
12993
13013
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
12994
13014
|
findings.push({
|
|
12995
13015
|
severity: "warn",
|
|
@@ -13266,6 +13286,7 @@ function isSearchTool(name) {
|
|
|
13266
13286
|
async function collectInstallFindings(root, expectedVersion) {
|
|
13267
13287
|
const findings = [];
|
|
13268
13288
|
findings.push(...await collectWorkspaceVersionFindings(root, expectedVersion));
|
|
13289
|
+
findings.push(...await collectDistFreshnessFindings(root, expectedVersion));
|
|
13269
13290
|
const haiveBins = listHaiveBins();
|
|
13270
13291
|
if (haiveBins.length === 0) {
|
|
13271
13292
|
findings.push({
|
|
@@ -13329,6 +13350,71 @@ which -a haive`
|
|
|
13329
13350
|
}
|
|
13330
13351
|
return findings;
|
|
13331
13352
|
}
|
|
13353
|
+
async function collectToolchainFindings(root) {
|
|
13354
|
+
const findings = [];
|
|
13355
|
+
const pkg = await readJson(
|
|
13356
|
+
path46.join(root, "package.json")
|
|
13357
|
+
);
|
|
13358
|
+
const wantsPnpm = pkg?.packageManager?.startsWith("pnpm@") || Object.values(pkg?.scripts ?? {}).some((script) => /\bpnpm\b/.test(script));
|
|
13359
|
+
if (!wantsPnpm) return findings;
|
|
13360
|
+
const expected = pkg?.packageManager?.replace(/^pnpm@/, "") ?? "9.14.2";
|
|
13361
|
+
if (!commandExists2("pnpm", ["--version"])) {
|
|
13362
|
+
const corepackAvailable = commandExists2("corepack", ["--version"]);
|
|
13363
|
+
findings.push({
|
|
13364
|
+
severity: "warn",
|
|
13365
|
+
code: "pnpm-not-on-path",
|
|
13366
|
+
message: `This workspace uses pnpm${expected ? ` ${expected}` : ""}, but no pnpm binary is available on PATH. Local build/test commands may fail even though CI works.`,
|
|
13367
|
+
fix: corepackAvailable ? `corepack prepare pnpm@${expected} --activate` : `npx pnpm@${expected} install --frozen-lockfile`,
|
|
13368
|
+
section: "Agent coverage"
|
|
13369
|
+
});
|
|
13370
|
+
}
|
|
13371
|
+
return findings;
|
|
13372
|
+
}
|
|
13373
|
+
async function collectDistFreshnessFindings(root, expectedVersion) {
|
|
13374
|
+
const findings = [];
|
|
13375
|
+
const isHaiveWorkspace = (await readJson(path46.join(root, "package.json")))?.name === "haive-monorepo";
|
|
13376
|
+
if (!isHaiveWorkspace) return findings;
|
|
13377
|
+
const cliDist = path46.join(root, "packages/cli/dist/index.js");
|
|
13378
|
+
if (!existsSync67(cliDist)) {
|
|
13379
|
+
findings.push({
|
|
13380
|
+
severity: "warn",
|
|
13381
|
+
code: "workspace-dist-missing",
|
|
13382
|
+
message: "packages/cli/dist/index.js is missing; local hAIve smoke commands cannot reflect source changes.",
|
|
13383
|
+
fix: "pnpm -r build",
|
|
13384
|
+
section: "Agent coverage"
|
|
13385
|
+
});
|
|
13386
|
+
return findings;
|
|
13387
|
+
}
|
|
13388
|
+
const distVersion = versionForNodeEntrypoint(cliDist);
|
|
13389
|
+
if (distVersion && distVersion !== expectedVersion) {
|
|
13390
|
+
findings.push({
|
|
13391
|
+
severity: "warn",
|
|
13392
|
+
code: "workspace-dist-version-mismatch",
|
|
13393
|
+
message: `packages/cli/dist/index.js reports ${distVersion}, but this source build expects ${expectedVersion}. Run a fresh workspace build after pull before trusting local doctor/enforce output.`,
|
|
13394
|
+
fix: "pnpm -r build\npnpm check:artifacts",
|
|
13395
|
+
section: "Agent coverage"
|
|
13396
|
+
});
|
|
13397
|
+
}
|
|
13398
|
+
const sourceFiles = [
|
|
13399
|
+
"packages/core/src/index.ts",
|
|
13400
|
+
"packages/mcp/src/server.ts",
|
|
13401
|
+
"packages/cli/src/index.ts"
|
|
13402
|
+
].map((rel) => path46.join(root, rel)).filter(existsSync67);
|
|
13403
|
+
if (sourceFiles.length > 0) {
|
|
13404
|
+
const distMtime = statSync2(cliDist).mtimeMs;
|
|
13405
|
+
const newestSource = Math.max(...sourceFiles.map((file) => statSync2(file).mtimeMs));
|
|
13406
|
+
if (newestSource > distMtime + 1e3) {
|
|
13407
|
+
findings.push({
|
|
13408
|
+
severity: "info",
|
|
13409
|
+
code: "workspace-dist-older-than-source",
|
|
13410
|
+
message: "Built CLI artifacts are older than key source files; rebuild before running release smoke checks.",
|
|
13411
|
+
fix: "pnpm -r build\npnpm check:artifacts",
|
|
13412
|
+
section: "Agent coverage"
|
|
13413
|
+
});
|
|
13414
|
+
}
|
|
13415
|
+
}
|
|
13416
|
+
return findings;
|
|
13417
|
+
}
|
|
13332
13418
|
async function collectWorkspaceVersionFindings(root, expectedVersion) {
|
|
13333
13419
|
const findings = [];
|
|
13334
13420
|
const rootPkg = await readJson(path46.join(root, "package.json"));
|
|
@@ -13432,6 +13518,30 @@ function versionForBinary(bin) {
|
|
|
13432
13518
|
return null;
|
|
13433
13519
|
}
|
|
13434
13520
|
}
|
|
13521
|
+
function versionForNodeEntrypoint(file) {
|
|
13522
|
+
try {
|
|
13523
|
+
const out = execFileSync(process.execPath, [file, "--version"], {
|
|
13524
|
+
encoding: "utf8",
|
|
13525
|
+
timeout: 3e3,
|
|
13526
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
13527
|
+
}).trim();
|
|
13528
|
+
return out.match(/\d+\.\d+\.\d+/)?.[0] ?? null;
|
|
13529
|
+
} catch {
|
|
13530
|
+
return null;
|
|
13531
|
+
}
|
|
13532
|
+
}
|
|
13533
|
+
function commandExists2(command, args) {
|
|
13534
|
+
try {
|
|
13535
|
+
execFileSync(command, args, {
|
|
13536
|
+
encoding: "utf8",
|
|
13537
|
+
timeout: 3e3,
|
|
13538
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
13539
|
+
});
|
|
13540
|
+
return true;
|
|
13541
|
+
} catch {
|
|
13542
|
+
return false;
|
|
13543
|
+
}
|
|
13544
|
+
}
|
|
13435
13545
|
function extractAbsoluteHaiveBins(text) {
|
|
13436
13546
|
const out = /* @__PURE__ */ new Set();
|
|
13437
13547
|
const re = /(["'\s])((?:\/[^"'\s]+)*\/haive)\b/g;
|
|
@@ -14490,7 +14600,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
14490
14600
|
findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
|
|
14491
14601
|
});
|
|
14492
14602
|
}
|
|
14493
|
-
findings.push(...await inspectIntegrationVersions(root, "0.12.
|
|
14603
|
+
findings.push(...await inspectIntegrationVersions(root, "0.12.3"));
|
|
14494
14604
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
14495
14605
|
const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
|
|
14496
14606
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
|
|
@@ -14628,6 +14738,15 @@ async function verifyDecisionCoverage(paths, stage, sessionId) {
|
|
|
14628
14738
|
}];
|
|
14629
14739
|
}
|
|
14630
14740
|
const marker = await readRecentBriefingMarker(paths, sessionId);
|
|
14741
|
+
if (stage === "ci" && !marker) {
|
|
14742
|
+
return [{
|
|
14743
|
+
severity: "ok",
|
|
14744
|
+
code: "decision-coverage-ci-pass",
|
|
14745
|
+
message: `CI surfaced ${relevant.length} relevant anchored decision/polic${relevant.length === 1 ? "y" : "ies"} for ${changedFiles.length} changed file(s). Runtime briefing markers are local-only and are not expected on GitHub Actions.`,
|
|
14746
|
+
memory_ids: relevant.slice(0, 20).map((memory2) => memory2.frontmatter.id),
|
|
14747
|
+
affected_files: changedFiles.slice(0, 10)
|
|
14748
|
+
}];
|
|
14749
|
+
}
|
|
14631
14750
|
const consulted = new Set(marker?.memory_ids ?? []);
|
|
14632
14751
|
const missing = relevant.filter((memory2) => !consulted.has(memory2.frontmatter.id));
|
|
14633
14752
|
if (missing.length === 0) {
|
|
@@ -15489,7 +15608,7 @@ function shellQuote(value) {
|
|
|
15489
15608
|
|
|
15490
15609
|
// src/index.ts
|
|
15491
15610
|
var program = new Command56();
|
|
15492
|
-
program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.12.
|
|
15611
|
+
program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.12.3").option("--advanced", "show maintenance and experimental commands in help");
|
|
15493
15612
|
registerInit(program);
|
|
15494
15613
|
registerWelcome(program);
|
|
15495
15614
|
registerResolveProject(program);
|