@hiveai/cli 0.12.1 → 0.12.4

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 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.1"}`;
2746
+ var HAIVE_GITHUB_ACTION_REF = `v${"0.12.4"}`;
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,
@@ -2778,7 +2778,7 @@ This repo uses **hAIve** for shared context. The map:
2778
2778
  1. **Before editing** for a goal, call \`get_briefing\` (task + files/symbols) to load ranked context \u2014 or \`mem_relevant_to\` if project context is already loaded this session.
2779
2779
  2. **When an approach fails**, call \`mem_tried\` right away so the next agent skips the dead end.
2780
2780
  3. **Before closing** a substantive session, run the \`post_task\` prompt to capture what was learned.
2781
- 4. **Before final response**, run \`haive enforce finish\`. If it blocks, commit/push, bump/tag shippable releases, then rerun it.
2781
+ 4. **Before final response**, run \`haive enforce finish\`. If it blocks, commit/push, bump/tag shippable releases, wait for GitHub Actions to pass when applicable, then rerun it.
2782
2782
 
2783
2783
  If the haive MCP server is not available, tell the developer rather than silently skipping it.
2784
2784
 
@@ -2805,7 +2805,7 @@ This repository uses **hAIve**. Running \`haive init\` means the team expects ag
2805
2805
 
2806
2806
  - On failure: **\`mem_tried\`** immediately.
2807
2807
  - Before closing a substantive session: MCP prompt **\`post_task\`** when there is something worth capturing.
2808
- - Before final response: **\`haive enforce finish\`** must pass; it checks commit/push and release version/tag protocol.
2808
+ - Before final response: **\`haive enforce finish\`** must pass; it checks commit/push, release version/tag protocol, and GitHub Actions success for pushed HEAD when the repo has a GitHub remote.
2809
2809
 
2810
2810
  ## If haive MCP is missing
2811
2811
 
@@ -7440,7 +7440,7 @@ This creates/updates a single rolling recap that **get_briefing automatically su
7440
7440
 
7441
7441
  Calling \`mem_session_end\` also **clears the pending-distill marker** (if any), confirming that this session's learnings have been properly captured rather than left as an auto-recap skeleton.
7442
7442
 
7443
- ### 7. Verify the git/release exit protocol \u2014 always
7443
+ ### 7. Verify the git/release/pipeline exit protocol \u2014 always
7444
7444
  Run **\`haive enforce finish\`** before your final response.
7445
7445
 
7446
7446
  This executable gate checks the multi-agent git-sync decision:
@@ -7448,11 +7448,12 @@ This executable gate checks the multi-agent git-sync decision:
7448
7448
  - shippable package changes have a lockstep version bump
7449
7449
  - the release tag \`vX.Y.Z\` exists when a version was bumped
7450
7450
  - commits and tags have been pushed
7451
+ - the pushed HEAD's GitHub Actions workflow runs have completed successfully when the repo has a GitHub remote
7451
7452
  - agents never run \`npm publish\` (publication remains human-owned)
7452
7453
 
7453
- If it blocks, fix the reported Git/version/tag/push issue before telling the developer the task is done.
7454
+ If it blocks, fix the reported Git/version/tag/push/pipeline issue before telling the developer the task is done.
7454
7455
 
7455
- When done, respond with a brief summary: "Saved N memories: [list of IDs]. Session recap saved. hAIve finish gate passed."
7456
+ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Session recap saved. hAIve finish gate passed; GitHub Actions passed when applicable."
7456
7457
  `;
7457
7458
  return {
7458
7459
  description: "Post-task reflection: capture what you learned before closing the session",
@@ -7531,7 +7532,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
7531
7532
  };
7532
7533
  }
7533
7534
  var SERVER_NAME = "haive";
7534
- var SERVER_VERSION = "0.12.1";
7535
+ var SERVER_VERSION = "0.12.4";
7535
7536
  function jsonResult(data) {
7536
7537
  return {
7537
7538
  content: [
@@ -12259,7 +12260,8 @@ function registerEval(program2) {
12259
12260
  }
12260
12261
  const k = Math.max(1, parseInt(opts.top ?? "8", 10) || 8);
12261
12262
  const ctx = { paths };
12262
- const spec = await resolveSpec(opts, paths.memoriesDir);
12263
+ const resolvedSpec = await resolveSpec(opts, root, paths.memoriesDir);
12264
+ const spec = resolvedSpec.spec;
12263
12265
  if ((spec.retrieval?.length ?? 0) === 0 && (spec.sensors?.length ?? 0) === 0) {
12264
12266
  ui.warn("No eval cases (no anchored memories and no --spec). Nothing to score.");
12265
12267
  return;
@@ -12284,9 +12286,9 @@ function registerEval(program2) {
12284
12286
  }
12285
12287
  const report = buildReport(retrievalAgg, sensorAgg);
12286
12288
  if (opts.json) {
12287
- console.log(JSON.stringify({ root, k, report }, null, 2));
12289
+ console.log(JSON.stringify({ root, k, spec_source: resolvedSpec.source, report }, null, 2));
12288
12290
  } else {
12289
- const md = renderMarkdown2(root, k, report);
12291
+ const md = renderMarkdown2(root, k, resolvedSpec.source, report);
12290
12292
  if (opts.out) {
12291
12293
  const outFile = path43.isAbsolute(opts.out) ? opts.out : path43.join(root, opts.out);
12292
12294
  await writeFile29(outFile, md, "utf8");
@@ -12307,14 +12309,31 @@ function registerEval(program2) {
12307
12309
  }
12308
12310
  });
12309
12311
  }
12310
- async function resolveSpec(opts, memoriesDir) {
12312
+ async function resolveSpec(opts, root, memoriesDir) {
12311
12313
  if (opts.spec) {
12312
12314
  const file = path43.resolve(opts.spec);
12313
12315
  const raw = await readFile20(file, "utf8");
12314
- return JSON.parse(raw);
12316
+ return { spec: JSON.parse(raw), source: file };
12317
+ }
12318
+ const defaultSpec = path43.join(root, ".ai", "eval", "spec.json");
12319
+ if (existsSync64(defaultSpec)) {
12320
+ const raw = await readFile20(defaultSpec, "utf8");
12321
+ const explicit = JSON.parse(raw);
12322
+ const memories2 = await loadMemoriesFromDir26(memoriesDir);
12323
+ const synthesized = synthesizeSelfEvalCases(memories2, { includeFiles: !opts.semanticOnly });
12324
+ return {
12325
+ spec: {
12326
+ retrieval: [...synthesized, ...explicit.retrieval ?? []],
12327
+ sensors: explicit.sensors ?? []
12328
+ },
12329
+ source: ".ai/eval/spec.json + synthesized anchored retrieval"
12330
+ };
12315
12331
  }
12316
12332
  const memories = await loadMemoriesFromDir26(memoriesDir);
12317
- return { retrieval: synthesizeSelfEvalCases(memories, { includeFiles: !opts.semanticOnly }) };
12333
+ return {
12334
+ spec: { retrieval: synthesizeSelfEvalCases(memories, { includeFiles: !opts.semanticOnly }) },
12335
+ source: "synthesized anchored retrieval"
12336
+ };
12318
12337
  }
12319
12338
  async function runRetrieval(c, k, ctx) {
12320
12339
  const out = await getBriefing(
@@ -12346,11 +12365,12 @@ async function runSensorCase(c, ctx) {
12346
12365
  function pct(n) {
12347
12366
  return `${Math.round(n * 100)}%`;
12348
12367
  }
12349
- function renderMarkdown2(root, k, report) {
12368
+ function renderMarkdown2(root, k, source, report) {
12350
12369
  const lines = [
12351
12370
  "# hAIve eval report",
12352
12371
  "",
12353
12372
  `Project: \`${root}\` \xB7 top-k: ${k}`,
12373
+ `Spec: ${source}`,
12354
12374
  "",
12355
12375
  `## Overall score: ${report.score}/100`,
12356
12376
  ""
@@ -12982,14 +13002,15 @@ function registerDoctor(program2) {
12982
13002
  fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
12983
13003
  });
12984
13004
  }
12985
- findings.push(...await collectInstallFindings(root, "0.12.1"));
13005
+ findings.push(...await collectInstallFindings(root, "0.12.4"));
13006
+ findings.push(...await collectToolchainFindings(root));
12986
13007
  try {
12987
13008
  const legacyRaw = execSync3("haive-mcp --version", {
12988
13009
  encoding: "utf8",
12989
13010
  timeout: 3e3,
12990
13011
  stdio: ["ignore", "pipe", "ignore"]
12991
13012
  }).trim();
12992
- const cliVersion = "0.12.1";
13013
+ const cliVersion = "0.12.4";
12993
13014
  if (legacyRaw && legacyRaw !== cliVersion) {
12994
13015
  findings.push({
12995
13016
  severity: "warn",
@@ -13266,6 +13287,7 @@ function isSearchTool(name) {
13266
13287
  async function collectInstallFindings(root, expectedVersion) {
13267
13288
  const findings = [];
13268
13289
  findings.push(...await collectWorkspaceVersionFindings(root, expectedVersion));
13290
+ findings.push(...await collectDistFreshnessFindings(root, expectedVersion));
13269
13291
  const haiveBins = listHaiveBins();
13270
13292
  if (haiveBins.length === 0) {
13271
13293
  findings.push({
@@ -13329,6 +13351,71 @@ which -a haive`
13329
13351
  }
13330
13352
  return findings;
13331
13353
  }
13354
+ async function collectToolchainFindings(root) {
13355
+ const findings = [];
13356
+ const pkg = await readJson(
13357
+ path46.join(root, "package.json")
13358
+ );
13359
+ const wantsPnpm = pkg?.packageManager?.startsWith("pnpm@") || Object.values(pkg?.scripts ?? {}).some((script) => /\bpnpm\b/.test(script));
13360
+ if (!wantsPnpm) return findings;
13361
+ const expected = pkg?.packageManager?.replace(/^pnpm@/, "") ?? "9.14.2";
13362
+ if (!commandExists2("pnpm", ["--version"])) {
13363
+ const corepackAvailable = commandExists2("corepack", ["--version"]);
13364
+ findings.push({
13365
+ severity: "warn",
13366
+ code: "pnpm-not-on-path",
13367
+ 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.`,
13368
+ fix: corepackAvailable ? `corepack prepare pnpm@${expected} --activate` : `npx pnpm@${expected} install --frozen-lockfile`,
13369
+ section: "Agent coverage"
13370
+ });
13371
+ }
13372
+ return findings;
13373
+ }
13374
+ async function collectDistFreshnessFindings(root, expectedVersion) {
13375
+ const findings = [];
13376
+ const isHaiveWorkspace = (await readJson(path46.join(root, "package.json")))?.name === "haive-monorepo";
13377
+ if (!isHaiveWorkspace) return findings;
13378
+ const cliDist = path46.join(root, "packages/cli/dist/index.js");
13379
+ if (!existsSync67(cliDist)) {
13380
+ findings.push({
13381
+ severity: "warn",
13382
+ code: "workspace-dist-missing",
13383
+ message: "packages/cli/dist/index.js is missing; local hAIve smoke commands cannot reflect source changes.",
13384
+ fix: "pnpm -r build",
13385
+ section: "Agent coverage"
13386
+ });
13387
+ return findings;
13388
+ }
13389
+ const distVersion = versionForNodeEntrypoint(cliDist);
13390
+ if (distVersion && distVersion !== expectedVersion) {
13391
+ findings.push({
13392
+ severity: "warn",
13393
+ code: "workspace-dist-version-mismatch",
13394
+ 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.`,
13395
+ fix: "pnpm -r build\npnpm check:artifacts",
13396
+ section: "Agent coverage"
13397
+ });
13398
+ }
13399
+ const sourceFiles = [
13400
+ "packages/core/src/index.ts",
13401
+ "packages/mcp/src/server.ts",
13402
+ "packages/cli/src/index.ts"
13403
+ ].map((rel) => path46.join(root, rel)).filter(existsSync67);
13404
+ if (sourceFiles.length > 0) {
13405
+ const distMtime = statSync2(cliDist).mtimeMs;
13406
+ const newestSource = Math.max(...sourceFiles.map((file) => statSync2(file).mtimeMs));
13407
+ if (newestSource > distMtime + 1e3) {
13408
+ findings.push({
13409
+ severity: "info",
13410
+ code: "workspace-dist-older-than-source",
13411
+ message: "Built CLI artifacts are older than key source files; rebuild before running release smoke checks.",
13412
+ fix: "pnpm -r build\npnpm check:artifacts",
13413
+ section: "Agent coverage"
13414
+ });
13415
+ }
13416
+ }
13417
+ return findings;
13418
+ }
13332
13419
  async function collectWorkspaceVersionFindings(root, expectedVersion) {
13333
13420
  const findings = [];
13334
13421
  const rootPkg = await readJson(path46.join(root, "package.json"));
@@ -13432,6 +13519,30 @@ function versionForBinary(bin) {
13432
13519
  return null;
13433
13520
  }
13434
13521
  }
13522
+ function versionForNodeEntrypoint(file) {
13523
+ try {
13524
+ const out = execFileSync(process.execPath, [file, "--version"], {
13525
+ encoding: "utf8",
13526
+ timeout: 3e3,
13527
+ stdio: ["ignore", "pipe", "ignore"]
13528
+ }).trim();
13529
+ return out.match(/\d+\.\d+\.\d+/)?.[0] ?? null;
13530
+ } catch {
13531
+ return null;
13532
+ }
13533
+ }
13534
+ function commandExists2(command, args) {
13535
+ try {
13536
+ execFileSync(command, args, {
13537
+ encoding: "utf8",
13538
+ timeout: 3e3,
13539
+ stdio: ["ignore", "pipe", "ignore"]
13540
+ });
13541
+ return true;
13542
+ } catch {
13543
+ return false;
13544
+ }
13545
+ }
13435
13546
  function extractAbsoluteHaiveBins(text) {
13436
13547
  const out = /* @__PURE__ */ new Set();
13437
13548
  const re = /(["'\s])((?:\/[^"'\s]+)*\/haive)\b/g;
@@ -14261,6 +14372,7 @@ async function buildFinishReport(dir) {
14261
14372
  code: "release-version-not-required",
14262
14373
  message: "No shippable package code changed since upstream; no version/tag required."
14263
14374
  });
14375
+ findings.push(...await verifyGithubActionsForHead(root, status));
14264
14376
  return finishReport(root, initialized, mode, findings, config);
14265
14377
  }
14266
14378
  findings.push({
@@ -14347,6 +14459,7 @@ async function buildFinishReport(dir) {
14347
14459
  impact: 10
14348
14460
  });
14349
14461
  }
14462
+ findings.push(...await verifyGithubActionsForHead(root, status));
14350
14463
  return finishReport(root, initialized, mode, findings, config);
14351
14464
  }
14352
14465
  function finishReport(root, initialized, mode, findings, config) {
@@ -14490,7 +14603,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
14490
14603
  findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
14491
14604
  });
14492
14605
  }
14493
- findings.push(...await inspectIntegrationVersions(root, "0.12.1"));
14606
+ findings.push(...await inspectIntegrationVersions(root, "0.12.4"));
14494
14607
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
14495
14608
  const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
14496
14609
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
@@ -14628,6 +14741,15 @@ async function verifyDecisionCoverage(paths, stage, sessionId) {
14628
14741
  }];
14629
14742
  }
14630
14743
  const marker = await readRecentBriefingMarker(paths, sessionId);
14744
+ if (stage === "ci" && !marker) {
14745
+ return [{
14746
+ severity: "ok",
14747
+ code: "decision-coverage-ci-pass",
14748
+ 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.`,
14749
+ memory_ids: relevant.slice(0, 20).map((memory2) => memory2.frontmatter.id),
14750
+ affected_files: changedFiles.slice(0, 10)
14751
+ }];
14752
+ }
14631
14753
  const consulted = new Set(marker?.memory_ids ?? []);
14632
14754
  const missing = relevant.filter((memory2) => !consulted.has(memory2.frontmatter.id));
14633
14755
  if (missing.length === 0) {
@@ -15033,6 +15155,108 @@ async function remoteTagExists(root, tag) {
15033
15155
  return null;
15034
15156
  }
15035
15157
  }
15158
+ async function verifyGithubActionsForHead(root, status) {
15159
+ if (!status.upstream) return [];
15160
+ if (status.ahead > 0) {
15161
+ return [{
15162
+ severity: "info",
15163
+ code: "github-actions-waiting-for-push",
15164
+ message: "GitHub Actions verification waits until HEAD is pushed."
15165
+ }];
15166
+ }
15167
+ const remote = await githubRemoteForCurrentBranch(root);
15168
+ if (!remote) {
15169
+ return [{
15170
+ severity: "info",
15171
+ code: "github-actions-not-applicable",
15172
+ message: "No GitHub remote was detected; GitHub Actions pipeline verification was skipped."
15173
+ }];
15174
+ }
15175
+ const sha = (await runCommand4("git", ["rev-parse", "HEAD"], root).catch(() => "")).trim();
15176
+ if (!sha) {
15177
+ return [{
15178
+ severity: "error",
15179
+ code: "github-actions-head-unreadable",
15180
+ message: "Could not read HEAD SHA for GitHub Actions verification.",
15181
+ fix: "Run `git rev-parse HEAD`, then verify GitHub Actions manually before finishing.",
15182
+ impact: 30
15183
+ }];
15184
+ }
15185
+ let runs;
15186
+ try {
15187
+ const raw = await runCommand4("gh", [
15188
+ "run",
15189
+ "list",
15190
+ "--commit",
15191
+ sha,
15192
+ "--limit",
15193
+ "50",
15194
+ "--json",
15195
+ "conclusion,databaseId,name,status,workflowName"
15196
+ ], root);
15197
+ runs = JSON.parse(raw);
15198
+ } catch {
15199
+ return [{
15200
+ severity: "error",
15201
+ code: "github-actions-unverified",
15202
+ message: "Could not verify GitHub Actions runs for HEAD.",
15203
+ fix: "Install/authenticate GitHub CLI, then run `gh run list --commit $(git rev-parse HEAD)` and ensure every workflow is successful before finishing.",
15204
+ reason: `Detected GitHub remote ${remote}, but hAIve could not query workflow runs.`,
15205
+ impact: 50
15206
+ }];
15207
+ }
15208
+ if (runs.length === 0) {
15209
+ return [{
15210
+ severity: "error",
15211
+ code: "github-actions-runs-missing",
15212
+ message: "No GitHub Actions runs were found for HEAD.",
15213
+ fix: "Wait for GitHub to create the workflow runs, or verify that the push was not skipped by a `[skip ci]` head commit; rerun `haive enforce finish` after the runs appear.",
15214
+ impact: 50
15215
+ }];
15216
+ }
15217
+ const pending = runs.filter((run) => run.status !== "completed");
15218
+ if (pending.length > 0) {
15219
+ return [{
15220
+ severity: "error",
15221
+ code: "github-actions-pending",
15222
+ message: `${pending.length}/${runs.length} GitHub Actions workflow run(s) for HEAD are still pending: ${formatGithubRunNames(pending)}.`,
15223
+ fix: "Wait for the runs to finish (`gh run watch <run-id> --exit-status`), then rerun `haive enforce finish`.",
15224
+ impact: 50
15225
+ }];
15226
+ }
15227
+ const failed = runs.filter((run) => run.conclusion !== "success");
15228
+ if (failed.length > 0) {
15229
+ return [{
15230
+ severity: "error",
15231
+ code: "github-actions-failed",
15232
+ message: `${failed.length}/${runs.length} GitHub Actions workflow run(s) for HEAD did not pass: ${formatGithubRunNames(failed)}.`,
15233
+ fix: "Inspect the failed run logs with `gh run view <run-id> --log`, fix the issue, push the fix, then rerun `haive enforce finish`.",
15234
+ impact: 80
15235
+ }];
15236
+ }
15237
+ return [{
15238
+ severity: "ok",
15239
+ code: "github-actions-pass",
15240
+ message: `All ${runs.length} GitHub Actions workflow run(s) for HEAD completed successfully.`
15241
+ }];
15242
+ }
15243
+ async function githubRemoteForCurrentBranch(root) {
15244
+ const branch = (await runCommand4("git", ["branch", "--show-current"], root).catch(() => "")).trim();
15245
+ const branchRemote = branch ? (await runCommand4("git", ["config", "--get", `branch.${branch}.remote`], root).catch(() => "")).trim() : "";
15246
+ const remoteName = branchRemote || "origin";
15247
+ const remoteUrl = (await runCommand4("git", ["config", "--get", `remote.${remoteName}.url`], root).catch(() => "")).trim();
15248
+ if (!isGithubRemoteUrl(remoteUrl)) return null;
15249
+ return remoteUrl;
15250
+ }
15251
+ function isGithubRemoteUrl(url) {
15252
+ return /(^git@github\.com:|github\.com[/:])/.test(url);
15253
+ }
15254
+ function formatGithubRunNames(runs) {
15255
+ return runs.slice(0, 6).map((run) => {
15256
+ const label = run.workflowName ?? run.name ?? "workflow";
15257
+ return run.databaseId ? `${label}#${run.databaseId}` : label;
15258
+ }).join(", ");
15259
+ }
15036
15260
  function buildScore(findings, threshold = 80) {
15037
15261
  const checks = {
15038
15262
  total: findings.length,
@@ -15489,7 +15713,7 @@ function shellQuote(value) {
15489
15713
 
15490
15714
  // src/index.ts
15491
15715
  var program = new Command56();
15492
- program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.12.1").option("--advanced", "show maintenance and experimental commands in help");
15716
+ program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.12.4").option("--advanced", "show maintenance and experimental commands in help");
15493
15717
  registerInit(program);
15494
15718
  registerWelcome(program);
15495
15719
  registerResolveProject(program);