@hiveai/cli 0.12.0 → 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 +147 -15
- 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,
|
|
@@ -3782,6 +3782,7 @@ import {
|
|
|
3782
3782
|
loadMemoriesFromDir as loadMemoriesFromDir14,
|
|
3783
3783
|
loadUsageIndex as loadUsageIndex8,
|
|
3784
3784
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths22,
|
|
3785
|
+
rankMemoriesLexical as rankMemoriesLexical2,
|
|
3785
3786
|
queryCodeMap as queryCodeMap2,
|
|
3786
3787
|
resolveBriefingBudget as resolveBriefingBudget2,
|
|
3787
3788
|
serializeMemory as serializeMemory9,
|
|
@@ -5472,13 +5473,25 @@ async function getBriefing(input, ctx) {
|
|
|
5472
5473
|
}
|
|
5473
5474
|
if (act.applicable && act.activated) activatedSkills.add(id);
|
|
5474
5475
|
}
|
|
5476
|
+
const lexNorm = /* @__PURE__ */ new Map();
|
|
5477
|
+
if (input.task) {
|
|
5478
|
+
const candidates = [...seen.keys()].map((id) => byId.get(id)).filter((x) => Boolean(x));
|
|
5479
|
+
const lex = rankMemoriesLexical2(candidates, input.task, candidates.length);
|
|
5480
|
+
const maxScore = lex.scores.reduce((m, s) => s > m ? s : m, 0);
|
|
5481
|
+
if (maxScore > 0) {
|
|
5482
|
+
lex.ranked.forEach((loaded, i) => {
|
|
5483
|
+
lexNorm.set(loaded.memory.frontmatter.id, (lex.scores[i] ?? 0) / maxScore);
|
|
5484
|
+
});
|
|
5485
|
+
}
|
|
5486
|
+
}
|
|
5475
5487
|
const ranked = [...seen.values()].sort((a, b) => {
|
|
5476
5488
|
const reasonScore = (m) => (m.type === "attempt" ? 3 : 0) + (m.reasons.includes("anchor") ? 4 : 0) + (m.reasons.includes("symbol") ? 4 : 0) + (m.reasons.includes("module") ? 2 : 0) + (m.reasons.includes("semantic") ? 2 : 0) + (m.reasons.includes("domain") ? 1 : 0);
|
|
5477
5489
|
const confidenceScore = (m) => m.confidence === "authoritative" ? 4 : m.confidence === "trusted" ? 3 : m.confidence === "low" ? 1 : m.confidence === "stale" ? -2 : 0;
|
|
5478
5490
|
const impactScore = (m) => (m.impact_score ?? 0) * 3;
|
|
5479
5491
|
const activationBoost = (m) => activatedSkills.has(m.id) ? 5 : 0;
|
|
5480
|
-
const
|
|
5481
|
-
const
|
|
5492
|
+
const lexScore = (m) => 12 * (lexNorm.get(m.id) ?? 0);
|
|
5493
|
+
const sa = priorityRank(classifyMemoryPriority(a, byId.get(a.id), input.files, input.symbols)) * 100 + reasonScore(a) + confidenceScore(a) + impactScore(a) + activationBoost(a) + lexScore(a) + (a.semantic_score ?? 0);
|
|
5494
|
+
const sb = priorityRank(classifyMemoryPriority(b, byId.get(b.id), input.files, input.symbols)) * 100 + reasonScore(b) + confidenceScore(b) + impactScore(b) + activationBoost(b) + lexScore(b) + (b.semantic_score ?? 0);
|
|
5482
5495
|
return sb - sa;
|
|
5483
5496
|
});
|
|
5484
5497
|
for (const mem of ranked.slice(0, briefingMaxMemories)) {
|
|
@@ -7518,7 +7531,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
7518
7531
|
};
|
|
7519
7532
|
}
|
|
7520
7533
|
var SERVER_NAME = "haive";
|
|
7521
|
-
var SERVER_VERSION = "0.12.
|
|
7534
|
+
var SERVER_VERSION = "0.12.3";
|
|
7522
7535
|
function jsonResult(data) {
|
|
7523
7536
|
return {
|
|
7524
7537
|
content: [
|
|
@@ -12246,7 +12259,8 @@ function registerEval(program2) {
|
|
|
12246
12259
|
}
|
|
12247
12260
|
const k = Math.max(1, parseInt(opts.top ?? "8", 10) || 8);
|
|
12248
12261
|
const ctx = { paths };
|
|
12249
|
-
const
|
|
12262
|
+
const resolvedSpec = await resolveSpec(opts, root, paths.memoriesDir);
|
|
12263
|
+
const spec = resolvedSpec.spec;
|
|
12250
12264
|
if ((spec.retrieval?.length ?? 0) === 0 && (spec.sensors?.length ?? 0) === 0) {
|
|
12251
12265
|
ui.warn("No eval cases (no anchored memories and no --spec). Nothing to score.");
|
|
12252
12266
|
return;
|
|
@@ -12271,9 +12285,9 @@ function registerEval(program2) {
|
|
|
12271
12285
|
}
|
|
12272
12286
|
const report = buildReport(retrievalAgg, sensorAgg);
|
|
12273
12287
|
if (opts.json) {
|
|
12274
|
-
console.log(JSON.stringify({ root, k, report }, null, 2));
|
|
12288
|
+
console.log(JSON.stringify({ root, k, spec_source: resolvedSpec.source, report }, null, 2));
|
|
12275
12289
|
} else {
|
|
12276
|
-
const md = renderMarkdown2(root, k, report);
|
|
12290
|
+
const md = renderMarkdown2(root, k, resolvedSpec.source, report);
|
|
12277
12291
|
if (opts.out) {
|
|
12278
12292
|
const outFile = path43.isAbsolute(opts.out) ? opts.out : path43.join(root, opts.out);
|
|
12279
12293
|
await writeFile29(outFile, md, "utf8");
|
|
@@ -12294,14 +12308,31 @@ function registerEval(program2) {
|
|
|
12294
12308
|
}
|
|
12295
12309
|
});
|
|
12296
12310
|
}
|
|
12297
|
-
async function resolveSpec(opts, memoriesDir) {
|
|
12311
|
+
async function resolveSpec(opts, root, memoriesDir) {
|
|
12298
12312
|
if (opts.spec) {
|
|
12299
12313
|
const file = path43.resolve(opts.spec);
|
|
12300
12314
|
const raw = await readFile20(file, "utf8");
|
|
12301
|
-
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
|
+
};
|
|
12302
12330
|
}
|
|
12303
12331
|
const memories = await loadMemoriesFromDir26(memoriesDir);
|
|
12304
|
-
return {
|
|
12332
|
+
return {
|
|
12333
|
+
spec: { retrieval: synthesizeSelfEvalCases(memories, { includeFiles: !opts.semanticOnly }) },
|
|
12334
|
+
source: "synthesized anchored retrieval"
|
|
12335
|
+
};
|
|
12305
12336
|
}
|
|
12306
12337
|
async function runRetrieval(c, k, ctx) {
|
|
12307
12338
|
const out = await getBriefing(
|
|
@@ -12333,11 +12364,12 @@ async function runSensorCase(c, ctx) {
|
|
|
12333
12364
|
function pct(n) {
|
|
12334
12365
|
return `${Math.round(n * 100)}%`;
|
|
12335
12366
|
}
|
|
12336
|
-
function renderMarkdown2(root, k, report) {
|
|
12367
|
+
function renderMarkdown2(root, k, source, report) {
|
|
12337
12368
|
const lines = [
|
|
12338
12369
|
"# hAIve eval report",
|
|
12339
12370
|
"",
|
|
12340
12371
|
`Project: \`${root}\` \xB7 top-k: ${k}`,
|
|
12372
|
+
`Spec: ${source}`,
|
|
12341
12373
|
"",
|
|
12342
12374
|
`## Overall score: ${report.score}/100`,
|
|
12343
12375
|
""
|
|
@@ -12969,14 +13001,15 @@ function registerDoctor(program2) {
|
|
|
12969
13001
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
|
|
12970
13002
|
});
|
|
12971
13003
|
}
|
|
12972
|
-
findings.push(...await collectInstallFindings(root, "0.12.
|
|
13004
|
+
findings.push(...await collectInstallFindings(root, "0.12.3"));
|
|
13005
|
+
findings.push(...await collectToolchainFindings(root));
|
|
12973
13006
|
try {
|
|
12974
13007
|
const legacyRaw = execSync3("haive-mcp --version", {
|
|
12975
13008
|
encoding: "utf8",
|
|
12976
13009
|
timeout: 3e3,
|
|
12977
13010
|
stdio: ["ignore", "pipe", "ignore"]
|
|
12978
13011
|
}).trim();
|
|
12979
|
-
const cliVersion = "0.12.
|
|
13012
|
+
const cliVersion = "0.12.3";
|
|
12980
13013
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
12981
13014
|
findings.push({
|
|
12982
13015
|
severity: "warn",
|
|
@@ -13253,6 +13286,7 @@ function isSearchTool(name) {
|
|
|
13253
13286
|
async function collectInstallFindings(root, expectedVersion) {
|
|
13254
13287
|
const findings = [];
|
|
13255
13288
|
findings.push(...await collectWorkspaceVersionFindings(root, expectedVersion));
|
|
13289
|
+
findings.push(...await collectDistFreshnessFindings(root, expectedVersion));
|
|
13256
13290
|
const haiveBins = listHaiveBins();
|
|
13257
13291
|
if (haiveBins.length === 0) {
|
|
13258
13292
|
findings.push({
|
|
@@ -13316,6 +13350,71 @@ which -a haive`
|
|
|
13316
13350
|
}
|
|
13317
13351
|
return findings;
|
|
13318
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
|
+
}
|
|
13319
13418
|
async function collectWorkspaceVersionFindings(root, expectedVersion) {
|
|
13320
13419
|
const findings = [];
|
|
13321
13420
|
const rootPkg = await readJson(path46.join(root, "package.json"));
|
|
@@ -13419,6 +13518,30 @@ function versionForBinary(bin) {
|
|
|
13419
13518
|
return null;
|
|
13420
13519
|
}
|
|
13421
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
|
+
}
|
|
13422
13545
|
function extractAbsoluteHaiveBins(text) {
|
|
13423
13546
|
const out = /* @__PURE__ */ new Set();
|
|
13424
13547
|
const re = /(["'\s])((?:\/[^"'\s]+)*\/haive)\b/g;
|
|
@@ -14477,7 +14600,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
14477
14600
|
findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
|
|
14478
14601
|
});
|
|
14479
14602
|
}
|
|
14480
|
-
findings.push(...await inspectIntegrationVersions(root, "0.12.
|
|
14603
|
+
findings.push(...await inspectIntegrationVersions(root, "0.12.3"));
|
|
14481
14604
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
14482
14605
|
const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
|
|
14483
14606
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
|
|
@@ -14615,6 +14738,15 @@ async function verifyDecisionCoverage(paths, stage, sessionId) {
|
|
|
14615
14738
|
}];
|
|
14616
14739
|
}
|
|
14617
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
|
+
}
|
|
14618
14750
|
const consulted = new Set(marker?.memory_ids ?? []);
|
|
14619
14751
|
const missing = relevant.filter((memory2) => !consulted.has(memory2.frontmatter.id));
|
|
14620
14752
|
if (missing.length === 0) {
|
|
@@ -15476,7 +15608,7 @@ function shellQuote(value) {
|
|
|
15476
15608
|
|
|
15477
15609
|
// src/index.ts
|
|
15478
15610
|
var program = new Command56();
|
|
15479
|
-
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");
|
|
15480
15612
|
registerInit(program);
|
|
15481
15613
|
registerWelcome(program);
|
|
15482
15614
|
registerResolveProject(program);
|