@codacy/verity-cli 0.31.1-experimental.79dc9c2 → 0.31.1-experimental.be74f71

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/bin/verity.js CHANGED
@@ -10519,13 +10519,20 @@ var ADVISORY_EPISODE_FILE = `${VERITY_DIR}/.advisory-episode`;
10519
10519
  var IGNORE_DECLARATION_FILE = `${VERITY_DIR}/.ignore-declaration`;
10520
10520
 
10521
10521
  // src/lib/output.ts
10522
- var RED = "\x1B[0;31m";
10523
- var YELLOW = "\x1B[1;33m";
10524
- var GREEN = "\x1B[0;32m";
10525
- var CYAN = "\x1B[0;36m";
10526
- var BOLD = "\x1B[1m";
10527
- var DIM = "\x1B[2m";
10528
- var NC = "\x1B[0m";
10522
+ function colorEnabled() {
10523
+ if (process.env.FORCE_COLOR) return true;
10524
+ if (process.env.NO_COLOR !== void 0) return false;
10525
+ if (process.env.TERM === "dumb") return false;
10526
+ return !!process.stderr.isTTY;
10527
+ }
10528
+ var COLOR = colorEnabled();
10529
+ var RED = COLOR ? "\x1B[0;31m" : "";
10530
+ var YELLOW = COLOR ? "\x1B[1;33m" : "";
10531
+ var GREEN = COLOR ? "\x1B[0;32m" : "";
10532
+ var CYAN = COLOR ? "\x1B[0;36m" : "";
10533
+ var BOLD = COLOR ? "\x1B[1m" : "";
10534
+ var DIM = COLOR ? "\x1B[2m" : "";
10535
+ var NC = COLOR ? "\x1B[0m" : "";
10529
10536
  function printJson(data) {
10530
10537
  process.stdout.write(JSON.stringify(data, null, 2) + "\n");
10531
10538
  }
@@ -11252,7 +11259,7 @@ async function resolveServiceUrlDetailed(flagUrl, opts = {}) {
11252
11259
  }
11253
11260
  return {
11254
11261
  ok: false,
11255
- error: 'No Verity service URL found. Run "verity login" to get started, or /verity-setup to configure this project.'
11262
+ error: 'No Verity service URL found. Run "verity login" to get started, or "verity init" to set up this project.'
11256
11263
  };
11257
11264
  }
11258
11265
  async function resolveServiceUrlForAuth(flagUrl) {
@@ -11299,7 +11306,7 @@ async function resolveToken(flagToken) {
11299
11306
  }
11300
11307
  return {
11301
11308
  ok: false,
11302
- error: 'No Verity token found. Run "verity login" to sign in, or /verity-setup to set up this project.'
11309
+ error: 'No Verity token found. Run "verity login" to sign in, or "verity init" to set up this project.'
11303
11310
  };
11304
11311
  }
11305
11312
  async function whoami(token, serviceUrl, verbose) {
@@ -11393,6 +11400,91 @@ async function maybeHealServiceUrl(resolution, verbose) {
11393
11400
  var readline = __toESM(require("node:readline/promises"));
11394
11401
  var import_node_os = require("node:os");
11395
11402
 
11403
+ // src/lib/spinner.ts
11404
+ var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
11405
+ var FRAME_MS = 80;
11406
+ var DIM2 = "\x1B[2m";
11407
+ var GREEN2 = "\x1B[0;32m";
11408
+ var YELLOW2 = "\x1B[1;33m";
11409
+ var RESET = "\x1B[0m";
11410
+ function secondsSince(start) {
11411
+ return `${Math.round((Date.now() - start) / 1e3)}s`;
11412
+ }
11413
+ function startSpinner(label2, opts = {}) {
11414
+ const stream = process.stderr;
11415
+ const color = colorEnabled();
11416
+ const animate = opts.animate ?? !!stream.isTTY;
11417
+ const showElapsed = opts.elapsed ?? true;
11418
+ const start = Date.now();
11419
+ let current = label2;
11420
+ let frame = 0;
11421
+ let timer = null;
11422
+ let done = false;
11423
+ const fit = (label3, clock, columns) => {
11424
+ const budget = columns - 5;
11425
+ if (budget <= 0) return { label: "", clock: "" };
11426
+ if (label3.length + clock.length <= budget) return { label: label3, clock };
11427
+ if (clock.length >= budget) return { label: "", clock: clock.slice(0, budget) };
11428
+ const room = budget - clock.length;
11429
+ return { label: room <= 1 ? label3.slice(0, room) : `${label3.slice(0, room - 1)}\u2026`, clock };
11430
+ };
11431
+ const paint = () => {
11432
+ const glyph = FRAMES[frame++ % FRAMES.length];
11433
+ const columns = stream.columns ?? 80;
11434
+ const { label: label3, clock } = fit(current, showElapsed ? ` ${secondsSince(start)}` : "", columns);
11435
+ const line = color ? ` ${GREEN2}${glyph}${RESET} ${label3}${DIM2}${clock}${RESET}` : ` ${glyph} ${label3}${clock}`;
11436
+ stream.write(`\r\x1B[2K${line}`);
11437
+ };
11438
+ const clearLine = () => {
11439
+ if (animate) stream.write("\r\x1B[2K");
11440
+ };
11441
+ if (animate) {
11442
+ paint();
11443
+ timer = setInterval(paint, FRAME_MS);
11444
+ timer.unref?.();
11445
+ } else {
11446
+ stream.write(` ${current}\u2026
11447
+ `);
11448
+ }
11449
+ const finish = (render2) => {
11450
+ if (done) return;
11451
+ done = true;
11452
+ if (timer) clearInterval(timer);
11453
+ clearLine();
11454
+ render2();
11455
+ };
11456
+ return {
11457
+ update(next) {
11458
+ current = next;
11459
+ if (animate) paint();
11460
+ else stream.write(` ${next}\u2026
11461
+ `);
11462
+ },
11463
+ succeed(message) {
11464
+ finish(() => {
11465
+ const text = message ?? current;
11466
+ const clock = showElapsed ? ` (${secondsSince(start)})` : "";
11467
+ stream.write(
11468
+ color ? ` ${GREEN2}\u2713${RESET} ${text}${DIM2}${clock}${RESET}
11469
+ ` : ` \u2713 ${text}${clock}
11470
+ `
11471
+ );
11472
+ });
11473
+ },
11474
+ warn(message) {
11475
+ finish(() => {
11476
+ stream.write(color ? ` ${YELLOW2}\u26A0${RESET} ${message}
11477
+ ` : ` \u26A0 ${message}
11478
+ `);
11479
+ });
11480
+ },
11481
+ stop() {
11482
+ finish(() => {
11483
+ });
11484
+ }
11485
+ };
11486
+ }
11487
+
11396
11488
  // src/lib/provider-auth.ts
11397
11489
  var sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
11398
11490
  var form = (fields) => new URLSearchParams(fields).toString();
@@ -11459,42 +11551,49 @@ async function githubDeviceFlow() {
11459
11551
  printInfo("");
11460
11552
  printInfo(`To authorize Verity, open: ${dc.verification_uri}`);
11461
11553
  printInfo(`And enter the code: ${dc.user_code}`);
11462
- printInfo("Waiting for authorization\u2026");
11554
+ const spinner = startSpinner(`Waiting for you to approve in the browser \xB7 code ${dc.user_code}`);
11463
11555
  const deadline = Date.now() + (dc.expires_in || 900) * 1e3;
11464
11556
  let interval = dc.interval || 5;
11465
- while (Date.now() < deadline) {
11466
- await sleep(interval * 1e3);
11467
- let data;
11468
- try {
11469
- const res = await fetch(GITHUB_ACCESS_TOKEN_URL, {
11470
- method: "POST",
11471
- headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
11472
- body: form({
11473
- client_id: GITHUB_CLIENT_ID,
11474
- device_code: dc.device_code,
11475
- grant_type: "urn:ietf:params:oauth:grant-type:device_code"
11476
- })
11477
- });
11478
- data = await res.json().catch(() => ({}));
11479
- } catch {
11480
- continue;
11481
- }
11482
- if (data.access_token) return { ok: true, data: data.access_token };
11483
- switch (data.error) {
11484
- case "authorization_pending":
11485
- break;
11486
- case "slow_down":
11487
- interval += 5;
11488
- break;
11489
- case "access_denied":
11490
- return { ok: false, error: "Authorization was denied on GitHub." };
11491
- case "expired_token":
11492
- return { ok: false, error: "The authorization code expired. Re-run register." };
11493
- default:
11494
- if (data.error) return { ok: false, error: `GitHub auth error: ${data.error}` };
11557
+ try {
11558
+ while (Date.now() < deadline) {
11559
+ await sleep(interval * 1e3);
11560
+ let data;
11561
+ try {
11562
+ const res = await fetch(GITHUB_ACCESS_TOKEN_URL, {
11563
+ method: "POST",
11564
+ headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
11565
+ body: form({
11566
+ client_id: GITHUB_CLIENT_ID,
11567
+ device_code: dc.device_code,
11568
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
11569
+ })
11570
+ });
11571
+ data = await res.json().catch(() => ({}));
11572
+ } catch {
11573
+ continue;
11574
+ }
11575
+ if (data.access_token) {
11576
+ spinner.succeed("Authorized on GitHub");
11577
+ return { ok: true, data: data.access_token };
11578
+ }
11579
+ switch (data.error) {
11580
+ case "authorization_pending":
11581
+ break;
11582
+ case "slow_down":
11583
+ interval += 5;
11584
+ break;
11585
+ case "access_denied":
11586
+ return { ok: false, error: "Authorization was denied on GitHub." };
11587
+ case "expired_token":
11588
+ return { ok: false, error: "The authorization code expired. Re-run register." };
11589
+ default:
11590
+ if (data.error) return { ok: false, error: `GitHub auth error: ${data.error}` };
11591
+ }
11495
11592
  }
11593
+ return { ok: false, error: "Timed out waiting for GitHub authorization." };
11594
+ } finally {
11595
+ spinner.stop();
11496
11596
  }
11497
- return { ok: false, error: "Timed out waiting for GitHub authorization." };
11498
11597
  }
11499
11598
 
11500
11599
  // src/lib/register.ts
@@ -12263,12 +12362,20 @@ var VERITY_STOP_RE = /(?:^|[\/\s"'])verity\s+analyze\b/;
12263
12362
  var VERITY_INTENT_RE = /(?:^|[\/\s"'])verity\s+intent\s+capture\b/;
12264
12363
  var VERITY_GUARD_RE = /(?:^|[\/\s"'])verity\s+guard\b/;
12265
12364
  var VERITY_BASELINE_RE = /(?:^|[\/\s"'])verity\s+baseline\s+capture\b/;
12365
+ var VERITY_COMPACT_RE = /(?:^|[\/\s"'])verity\s+compact\b/;
12366
+ var VERITY_SESSION_END_RE = /(?:^|[\/\s"'])verity\s+session\s+end\b/;
12266
12367
  function isVerityGuardHook(entry) {
12267
12368
  return VERITY_GUARD_RE.test(entry.command ?? "");
12268
12369
  }
12269
12370
  function isVerityBaselineHook(entry) {
12270
12371
  return VERITY_BASELINE_RE.test(entry.command ?? "");
12271
12372
  }
12373
+ function isVerityCompactHook(entry) {
12374
+ return VERITY_COMPACT_RE.test(entry.command ?? "");
12375
+ }
12376
+ function isVeritySessionEndHook(entry) {
12377
+ return VERITY_SESSION_END_RE.test(entry.command ?? "");
12378
+ }
12272
12379
  var LEGACY_STOP_RE = /(?:^|[\/\s"'])gate\s+analyze\b/;
12273
12380
  var LEGACY_INTENT_RE = /(?:^|[\/\s"'])gate\s+intent\s+capture\b/;
12274
12381
  function isVerityStopHook(entry) {
@@ -12280,7 +12387,7 @@ function isVerityIntentHook(entry) {
12280
12387
  return VERITY_INTENT_RE.test(c) || LEGACY_INTENT_RE.test(c) || c.includes(".verity/hooks/capture-intent.sh") || c.includes(".gate/hooks/capture-intent.sh");
12281
12388
  }
12282
12389
  function isVerityHook(entry) {
12283
- return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
12390
+ return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry) || isVerityCompactHook(entry) || isVeritySessionEndHook(entry);
12284
12391
  }
12285
12392
  function isCurrentVerityStopHook(entry) {
12286
12393
  const c = entry.command ?? "";
@@ -12291,7 +12398,7 @@ function isCurrentVerityIntentHook(entry) {
12291
12398
  return VERITY_INTENT_RE.test(c) || c.includes(".verity/hooks/capture-intent.sh");
12292
12399
  }
12293
12400
  function isCurrentVerityHook(entry) {
12294
- return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
12401
+ return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry) || isVerityCompactHook(entry) || isVeritySessionEndHook(entry);
12295
12402
  }
12296
12403
  function settingsHasLegacyHook(settings) {
12297
12404
  for (const groups of Object.values(settings.hooks ?? {})) {
@@ -12353,6 +12460,8 @@ async function checkAllVerityHooks() {
12353
12460
  async function checkExternalVerityHooks() {
12354
12461
  let stop = false;
12355
12462
  let intent = false;
12463
+ let compact = false;
12464
+ let sessionEnd = false;
12356
12465
  let guardOn = [];
12357
12466
  for (const f of [SETTINGS_LOCAL_FILE, globalSettingsFile()]) {
12358
12467
  let settings;
@@ -12364,9 +12473,11 @@ async function checkExternalVerityHooks() {
12364
12473
  const r = checkVerityHooks(settings);
12365
12474
  stop = stop || r.stop;
12366
12475
  intent = intent || r.intent;
12476
+ compact = compact || r.compact;
12477
+ sessionEnd = sessionEnd || r.sessionEnd;
12367
12478
  if (r.guardOn.length > guardOn.length) guardOn = r.guardOn;
12368
12479
  }
12369
- return { stop, intent, guardOn };
12480
+ return { stop, intent, compact, sessionEnd, guardOn };
12370
12481
  }
12371
12482
  async function checkAllVerityHooksDetailed() {
12372
12483
  let stop = false;
@@ -12446,11 +12557,11 @@ function checkVerityHooks(settings) {
12446
12557
  }
12447
12558
  const compactGroups = hooks["PostCompact"] ?? [];
12448
12559
  const hasCompact = compactGroups.some(
12449
- (g) => g.hooks?.some((h) => h.command?.trim() === "verity compact")
12560
+ (g) => g.hooks?.some((h) => isVerityCompactHook(h))
12450
12561
  );
12451
12562
  const endGroups = hooks["SessionEnd"] ?? [];
12452
12563
  const hasSessionEnd = endGroups.some(
12453
- (g) => g.hooks?.some((h) => h.command?.trim() === "verity session end")
12564
+ (g) => g.hooks?.some((h) => isVeritySessionEndHook(h))
12454
12565
  );
12455
12566
  return {
12456
12567
  stop: hasStop,
@@ -12564,6 +12675,8 @@ function reconcileMomentHooks(settings, moments, externalPresent = {
12564
12675
  };
12565
12676
  if (!externalPresent.intent) push("UserPromptSubmit", { hooks: [VERITY_INTENT_HOOK] });
12566
12677
  push("SessionStart", { hooks: [VERITY_BASELINE_HOOK] });
12678
+ if (!externalPresent.compact) push("PostCompact", { hooks: [VERITY_COMPACT_HOOK] });
12679
+ if (!externalPresent.sessionEnd) push("SessionEnd", { hooks: [VERITY_SESSION_END_HOOK] });
12567
12680
  if (moments.includes("stop") && !externalPresent.stop) {
12568
12681
  push("Stop", { hooks: [VERITY_STOP_HOOK] });
12569
12682
  }
@@ -13540,6 +13653,74 @@ var LEGACY_MD_END = "<!-- gate-memory:end -->";
13540
13653
  var LEGACY_PRESERVE_START = "<!-- gate-memory:preserve -->";
13541
13654
  var LEGACY_PRESERVE_END = "<!-- /gate-memory:preserve -->";
13542
13655
  var CLAUDE_MD_PROSE = [
13656
+ "## Project Memory",
13657
+ "",
13658
+ "This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
13659
+ "non-trivial work, scan `.verity/memory/index.md` for decisions, gotchas, and patterns",
13660
+ "that may apply to the change you are about to make. Open specific node files via",
13661
+ "the Read tool when the title or scope suggests relevance.",
13662
+ "",
13663
+ "The graph is auto-maintained by Verity. Files at `.verity/memory/_archive/` are",
13664
+ "superseded \u2014 ignore them unless investigating history.",
13665
+ "",
13666
+ "## Quality gate: accepted risks",
13667
+ "",
13668
+ "When the Verity pre-commit/pre-push gate FAILs, fix the findings \u2014 that is the",
13669
+ 'default. Use `verity waive <pattern-id> --file <path> --reason "\u2026"` ONLY to relay',
13670
+ "a risk a human has explicitly accepted: a named code-review finding, an ADR, or",
13671
+ "the user saying so in this conversation. The --reason must cite that source.",
13672
+ "",
13673
+ "Never waive on your own judgment, to get past a block, or pre-emptively. A waive",
13674
+ "binds to the file's current bytes and voids automatically when the file changes,",
13675
+ "and every waive is recorded in the run ledger. For a pattern-level false positive",
13676
+ "use `verity feedback finding <run-id> <pattern-id> false_positive` instead.",
13677
+ "",
13678
+ "## Post-task reflection",
13679
+ "",
13680
+ `When a task is complete (you've created a PR, the user says "done" or "ship it",`,
13681
+ "or the work is clearly finished), **draft the reflection yourself first** \u2014 1\u20133",
13682
+ "concrete things worth remembering (a decision, a gotcha, or a pattern), each",
13683
+ "cited with the files / PR / commands / error-signatures it came from. Skip",
13684
+ "entirely if nothing non-obvious happened.",
13685
+ "",
13686
+ "Then, depending on how the session is running:",
13687
+ "",
13688
+ "- **Interactive** (a person is at the keyboard): show your draft and ask one",
13689
+ ` question before recording \u2014 "Here's my reflection draft for the project`,
13690
+ " memory: <draft>. Anything to confirm, correct, or add before I record it?",
13691
+ ` (Say 'skip' to drop it.)" Record the final text (with their corrections) via`,
13692
+ ' `verity reflect --user-input "<final reflection>" --kind <kind>`. If they say',
13693
+ ' "skip", do not record.',
13694
+ "- **Autonomous** (headless / CI / cron, or `VERITY_AUTONOMOUS=1`): record your",
13695
+ ' draft directly, never blocking \u2014 `verity reflect --user-input "<your draft>"',
13696
+ " --kind <kind> --autonomous`.",
13697
+ "",
13698
+ "> Durable, hand-curated guidance goes in the preserve region below (it survives",
13699
+ "> regeneration) or anywhere OUTSIDE these markers. Everything else between the",
13700
+ "> markers is tool-owned and overwritten on each run.",
13701
+ "",
13702
+ "## Housekeeping Turns",
13703
+ "",
13704
+ "When a turn will be pure housekeeping \u2014 pulling, installing dependencies,",
13705
+ "rebasing, a formatting sweep you are not authoring \u2014 declare it BEFORE doing it:",
13706
+ "",
13707
+ "```bash",
13708
+ 'verity ignore --turn --agent --reason "pulling latest before starting"',
13709
+ "```",
13710
+ "",
13711
+ "This skips the review for that turn, which saves the turn Verity would",
13712
+ "otherwise spend saying it had nothing to say. Use `--for 30m` instead of",
13713
+ "`--turn` when a single piece of housekeeping spans several turns.",
13714
+ "",
13715
+ "**It is a claim about the turn, not a way to silence review.** The declaration",
13716
+ "is checked against what the turn actually did: if anything is authored \u2014 by you,",
13717
+ "by a subagent, or by a shell command that can write files \u2014 it voids, the review",
13718
+ "runs anyway, and the broken declaration is reported. So declare housekeeping you",
13719
+ "are about to do, never work you have already done, and never as a way to get past",
13720
+ "a finding. Declarations are budgeted per session and every one is recorded with",
13721
+ "its reason."
13722
+ ].join("\n");
13723
+ var CLAUDE_MD_PROSE_PRE_REFLECT = [
13543
13724
  "## Project Memory",
13544
13725
  "",
13545
13726
  "This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
@@ -13750,6 +13931,7 @@ function stripKnownProse(interior) {
13750
13931
  const trimmed = interior.replace(/^\n+/, "");
13751
13932
  for (const prose of [
13752
13933
  CLAUDE_MD_PROSE,
13934
+ CLAUDE_MD_PROSE_PRE_REFLECT,
13753
13935
  CLAUDE_MD_PROSE_PRE_WAIVE,
13754
13936
  CLAUDE_MD_PROSE_PRE_IGNORE,
13755
13937
  CLAUDE_MD_PROSE_LEGACY
@@ -14136,6 +14318,11 @@ var REANCHOR_WINDOW = 20;
14136
14318
  var STATEMENT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
14137
14319
  var DEFAULT_MEMORY_BUDGET_BYTES = 4096;
14138
14320
 
14321
+ // src/lib/dossier/events.ts
14322
+ function statementAnchorKey(file, patternId) {
14323
+ return `${file}::${patternId}`;
14324
+ }
14325
+
14139
14326
  // src/lib/dossier/log.ts
14140
14327
  var import_node_crypto4 = require("node:crypto");
14141
14328
  var import_node_fs10 = require("node:fs");
@@ -15350,10 +15537,10 @@ function recordVerdict(d, v) {
15350
15537
  const at = src?.[f.line - 1];
15351
15538
  appendEvent(d, {
15352
15539
  k: "statement",
15353
- // Stable across title and line drift 374 of 378 recurring sites (98.9%)
15354
- // carry title drift, so a title-inclusive key fragments the recurrence it
15355
- // exists to detect.
15356
- anchor_key: `${f.file}::${f.pattern_id}`,
15540
+ // ONE OWNER. The server's settled feed is resolved against this exact
15541
+ // key in `13-reconcile.ts`; spelling the template literal twice is how the
15542
+ // two sides drift apart. See `statementAnchorKey`.
15543
+ anchor_key: statementAnchorKey(f.file, f.pattern_id),
15357
15544
  file: f.file,
15358
15545
  line: f.line,
15359
15546
  pattern_id: f.pattern_id,
@@ -15790,33 +15977,6 @@ ${addedLines}`,
15790
15977
  }
15791
15978
  return { diffs, has_snapshots: true };
15792
15979
  }
15793
- function ensureSnapshotGitignored() {
15794
- let content = "";
15795
- try {
15796
- content = (0, import_node_fs16.readFileSync)(".gitignore", "utf-8");
15797
- } catch {
15798
- }
15799
- let ignored = null;
15800
- try {
15801
- (0, import_node_child_process6.execSync)("git check-ignore -q -- .verity/.snapshot/__probe__", { stdio: "pipe" });
15802
- ignored = true;
15803
- } catch (err) {
15804
- ignored = err.status === 1 ? false : null;
15805
- }
15806
- if (ignored === true) return "covered";
15807
- if (ignored === null) {
15808
- const lines = content.split("\n").map((l) => l.trim());
15809
- const covering = [".verity/.snapshot/", ".verity/.snapshot", ".verity/", ".verity", ".verity/*"];
15810
- if (lines.some((l) => covering.includes(l))) return "covered";
15811
- }
15812
- try {
15813
- const block = "# Verity \u2014 snapshots of analyzed files (machine state, never commit)\n.verity/.snapshot/\n";
15814
- (0, import_node_fs16.writeFileSync)(".gitignore", content ? content + (content.endsWith("\n") ? "" : "\n") + "\n" + block : block);
15815
- return "added";
15816
- } catch {
15817
- return "failed";
15818
- }
15819
- }
15820
15980
  function saveSnapshots(files) {
15821
15981
  const snapshotPaths = /* @__PURE__ */ new Set();
15822
15982
  for (const file of files) {
@@ -16738,19 +16898,19 @@ function loc(f) {
16738
16898
  if (!f.file) return "";
16739
16899
  return f.line != null ? `${f.file}:${f.line}` : f.file;
16740
16900
  }
16741
- function formatRunDetail(run) {
16901
+ function formatRunDetail(run2) {
16742
16902
  const lines = [];
16743
- const q = run.assessment?.quality_score;
16744
- const s = run.assessment?.security_score;
16903
+ const q = run2.assessment?.quality_score;
16904
+ const s = run2.assessment?.security_score;
16745
16905
  const qStr = q != null ? `${q}` : "-";
16746
16906
  const sStr = s != null ? `${s}` : "-";
16747
- lines.push(`${run.run_id} ${run.gate_decision} Q ${qStr}/10 S ${sStr}/10`);
16907
+ lines.push(`${run2.run_id} ${run2.gate_decision} Q ${qStr}/10 S ${sStr}/10`);
16748
16908
  const meta = [];
16749
- if (run.trigger) meta.push(`trigger: ${run.trigger}`);
16750
- if (run.standard_version != null) meta.push(`standard v${run.standard_version}`);
16751
- if (run.created_at) meta.push(run.created_at.slice(0, 19).replace("T", " "));
16909
+ if (run2.trigger) meta.push(`trigger: ${run2.trigger}`);
16910
+ if (run2.standard_version != null) meta.push(`standard v${run2.standard_version}`);
16911
+ if (run2.created_at) meta.push(run2.created_at.slice(0, 19).replace("T", " "));
16752
16912
  if (meta.length > 0) lines.push(meta.join(" \xB7 "));
16753
- const findings = run.findings ?? [];
16913
+ const findings = run2.findings ?? [];
16754
16914
  if (findings.length === 0) {
16755
16915
  lines.push("");
16756
16916
  lines.push("No findings \u2014 clean.");
@@ -16767,7 +16927,7 @@ function formatRunDetail(run) {
16767
16927
  if (f.scope === "pre-existing") lines.push(" (pre-existing)");
16768
16928
  }
16769
16929
  }
16770
- const pending = run.pending_items ?? [];
16930
+ const pending = run2.pending_items ?? [];
16771
16931
  if (pending.length > 0) {
16772
16932
  lines.push("");
16773
16933
  lines.push(`PENDING (${pending.length})`);
@@ -16775,9 +16935,9 @@ function formatRunDetail(run) {
16775
16935
  lines.push(` [${(p.priority ?? "").toUpperCase()}] ${p.description}`);
16776
16936
  }
16777
16937
  }
16778
- if (run.assessment?.narrative) {
16938
+ if (run2.assessment?.narrative) {
16779
16939
  lines.push("");
16780
- lines.push(run.assessment.narrative);
16940
+ lines.push(run2.assessment.narrative);
16781
16941
  }
16782
16942
  return lines;
16783
16943
  }
@@ -17149,7 +17309,7 @@ function registerStatusCommand(program2) {
17149
17309
  return;
17150
17310
  }
17151
17311
  if (mem?.configured === false) {
17152
- printInfo("Verity is not configured for this project. Run /verity-setup.");
17312
+ printInfo('Verity is not configured for this project. Run "verity init".');
17153
17313
  return;
17154
17314
  }
17155
17315
  printInfo("=== Verity Status ===");
@@ -17181,7 +17341,7 @@ function registerStatusCommand(program2) {
17181
17341
  if (hookStatus.stop) moments.push("stop");
17182
17342
  if (hookStatus.guardOn.includes("commit")) moments.push("pre-commit");
17183
17343
  if (hookStatus.guardOn.includes("push")) moments.push("pre-push/PR");
17184
- printInfo(`Moments: ${moments.length > 0 ? moments.join(", ") : "none (run /verity-setup)"}`);
17344
+ printInfo(`Moments: ${moments.length > 0 ? moments.join(", ") : 'none (run "verity init")'}`);
17185
17345
  if (!mem) return;
17186
17346
  if (mem.recent_runs) {
17187
17347
  const r = mem.recent_runs;
@@ -17267,12 +17427,12 @@ function registerStatusCommand(program2) {
17267
17427
  printInfo("");
17268
17428
  printInfo("--- Recent Runs ---");
17269
17429
  printInfo(`${"Run ID".padEnd(32)} ${"Decision".padEnd(10)}${"Q".padEnd(4)}${"S".padEnd(4)}${"Findings".padEnd(32)}Date`);
17270
- for (const run of runsResult.data.runs) {
17271
- const q = run.quality_score != null ? `${run.quality_score}` : "-";
17272
- const s = run.security_score != null ? `${run.security_score}` : "-";
17273
- const findings = formatFindingsSummary(run.findings_count);
17274
- const date = run.created_at.slice(0, 19).replace("T", " ");
17275
- printInfo(`${run.run_id.padEnd(32)} ${run.gate_decision.padEnd(10)}${q.padEnd(4)}${s.padEnd(4)}${findings.padEnd(32)}${date}`);
17430
+ for (const run2 of runsResult.data.runs) {
17431
+ const q = run2.quality_score != null ? `${run2.quality_score}` : "-";
17432
+ const s = run2.security_score != null ? `${run2.security_score}` : "-";
17433
+ const findings = formatFindingsSummary(run2.findings_count);
17434
+ const date = run2.created_at.slice(0, 19).replace("T", " ");
17435
+ printInfo(`${run2.run_id.padEnd(32)} ${run2.gate_decision.padEnd(10)}${q.padEnd(4)}${s.padEnd(4)}${findings.padEnd(32)}${date}`);
17276
17436
  }
17277
17437
  }
17278
17438
  }
@@ -17508,35 +17668,35 @@ function row(label2, value) {
17508
17668
  return ` \u25B8 ${label2.padEnd(10)} ${value}
17509
17669
  `;
17510
17670
  }
17511
- function formatRunEvidence(run, startedAt) {
17671
+ function formatRunEvidence(run2, startedAt) {
17512
17672
  const ms = Date.now() - startedAt;
17513
- const sent = run.codeDelta.files.filter((f) => f.role !== "context").map((f) => f.path);
17514
- const context = run.codeDelta.files.filter((f) => f.role === "context").map((f) => f.path);
17673
+ const sent = run2.codeDelta.files.filter((f) => f.role !== "context").map((f) => f.path);
17674
+ const context = run2.codeDelta.files.filter((f) => f.role === "context").map((f) => f.path);
17515
17675
  let out = ` \u2500\u2500 what verity saw \u2500\u2500
17516
17676
  `;
17517
- out += row("turn", `${run.turnId || "(unminted)"}${run.sessionId ? ` \xB7 session ${run.sessionId}` : ""}`);
17518
- out += row("reached", `${run.phaseReached || "(none)"}${run.skipReason ? ` \xB7 SKIPPED: ${run.skipReason}` : ""} \xB7 ${ms}ms`);
17519
- if (run.treeFrame) {
17520
- const f = run.treeFrame;
17677
+ out += row("turn", `${run2.turnId || "(unminted)"}${run2.sessionId ? ` \xB7 session ${run2.sessionId}` : ""}`);
17678
+ out += row("reached", `${run2.phaseReached || "(none)"}${run2.skipReason ? ` \xB7 SKIPPED: ${run2.skipReason}` : ""} \xB7 ${ms}ms`);
17679
+ if (run2.treeFrame) {
17680
+ const f = run2.treeFrame;
17521
17681
  out += row("tree", f.worktreeRoot ? `${f.worktreeRoot}${f.isLinkedWorktree ? " \xB7 linked worktree" : ""}${f.branch ? ` \xB7 branch ${f.branch}` : " \xB7 detached"}` : `(unresolved: ${f.refusal ?? "unknown"})`);
17522
17682
  }
17523
- out += row("changed", `${run.changedUniverse.length} from git \xB7 analyzable ${run.analyzable.length} \xB7 reviewable ${run.reviewable.length} \xB7 security ${run.securityFiles.length} \xB7 forReview ${run.allForReview.length}`);
17524
- const done = (phase) => run.phasesCompleted.includes(phase);
17683
+ out += row("changed", `${run2.changedUniverse.length} from git \xB7 analyzable ${run2.analyzable.length} \xB7 reviewable ${run2.reviewable.length} \xB7 security ${run2.securityFiles.length} \xB7 forReview ${run2.allForReview.length}`);
17684
+ const done = (phase) => run2.phasesCompleted.includes(phase);
17525
17685
  const ifDone = (phase, value) => done(phase) ? value : "?";
17526
- const md = run.modeDecision;
17686
+ const md = run2.modeDecision;
17527
17687
  if (md) {
17528
17688
  const how = md.forced ? "forced by --mode" : `predicted=${md.predicted ?? "none"} \u2192 ${md.resolved}`;
17529
17689
  out += row("mode", `${md.resolved} \xB7 ${how}` + (md.flip ? ` (flipped to plan: no delta at ${md.flip})` : "") + ` \xB7 authored=${md.authored ? "yes" : "no"} \xB7 investigated=${md.investigated ? "yes" : "no"}`);
17530
17690
  } else {
17531
- out += row("mode", `? (this run stopped in ${run.phaseReached || "no phase"}, before the mode was decided)`);
17691
+ out += row("mode", `? (this run stopped in ${run2.phaseReached || "no phase"}, before the mode was decided)`);
17532
17692
  }
17533
- out += row("signals", `baseline=${ifDone("bootstrap", run.baseline ? "yes" : "no")} \xB7 authored=${ifDone("intentInputs", run.turnAuthoredCode ? "yes" : "no")} \xB7 observable=${ifDone("intentInputs", run.authorshipIsObservable ? "yes" : "no")}` + (run.actionSummary?.transcript_windowed ? ` \xB7 window=${run.actionSummary.transcript_windowed}` : ""));
17693
+ out += row("signals", `baseline=${ifDone("bootstrap", run2.baseline ? "yes" : "no")} \xB7 authored=${ifDone("intentInputs", run2.turnAuthoredCode ? "yes" : "no")} \xB7 observable=${ifDone("intentInputs", run2.authorshipIsObservable ? "yes" : "no")}` + (run2.actionSummary?.transcript_windowed ? ` \xB7 window=${run2.actionSummary.transcript_windowed}` : ""));
17534
17694
  if (!done("intentInputs")) {
17535
- out += row("", `(\`?\` = the phase that determines it did not complete \u2014 this run stopped in ${run.phaseReached})`);
17695
+ out += row("", `(\`?\` = the phase that determines it did not complete \u2014 this run stopped in ${run2.phaseReached})`);
17536
17696
  }
17537
17697
  out += row("sent", `${sent.length} \xB7 ${list(sent)}`);
17538
17698
  if (context.length > 0) out += row("context", `${context.length} \xB7 ${list(context)}`);
17539
- const withheld = run.reviewCoverage.notReviewed;
17699
+ const withheld = run2.reviewCoverage.notReviewed;
17540
17700
  if (withheld.length > 0) {
17541
17701
  const byReason = /* @__PURE__ */ new Map();
17542
17702
  for (const w of withheld) {
@@ -17549,18 +17709,18 @@ function formatRunEvidence(run, startedAt) {
17549
17709
  first = false;
17550
17710
  }
17551
17711
  } else {
17552
- const sentSet = new Set(run.codeDelta.files.map((f) => f.path));
17553
- const notSent = run.changedUniverse.filter((p) => !sentSet.has(p));
17712
+ const sentSet = new Set(run2.codeDelta.files.map((f) => f.path));
17713
+ const notSent = run2.changedUniverse.filter((p) => !sentSet.has(p));
17554
17714
  if (notSent.length > 0) {
17555
17715
  out += row("not sent", `${list(notSent)}`);
17556
- out += row("", `(stage unknown \u2014 the coverage ledger is built in phase 13, and this run reached ${run.phaseReached || "no phase"})`);
17557
- } else if (run.changedUniverse.length > 0) {
17716
+ out += row("", `(stage unknown \u2014 the coverage ledger is built in phase 13, and this run reached ${run2.phaseReached || "no phase"})`);
17717
+ } else if (run2.changedUniverse.length > 0) {
17558
17718
  out += row("withheld", "(nothing \u2014 every changed file was reviewed)");
17559
17719
  }
17560
17720
  }
17561
- const cov = run.foldResult?.coverage;
17721
+ const cov = run2.foldResult?.coverage;
17562
17722
  if (cov) {
17563
- const delegated = run.foldResult.authored.filter((a) => a.owner === "subagent").length;
17723
+ const delegated = run2.foldResult.authored.filter((a) => a.owner === "subagent").length;
17564
17724
  if (cov.dispatched > 0 || cov.subagentFiles > 0 || cov.subagentSkipped > 0) {
17565
17725
  out += row("delegated", `${cov.dispatched} dispatched \xB7 ${cov.subagentFiles} agent log(s) read \xB7 ${delegated} path(s) attributed to subagents`);
17566
17726
  }
@@ -17574,43 +17734,43 @@ function formatRunEvidence(run, startedAt) {
17574
17734
  out += row("", `${cov.outsideRepo} authored path(s) refused as outside the repo`);
17575
17735
  }
17576
17736
  }
17577
- if (run.foldResult?.tools?.length) {
17578
- const shown = run.foldResult.tools.slice(0, 6).map((t) => {
17737
+ if (run2.foldResult?.tools?.length) {
17738
+ const shown = run2.foldResult.tools.slice(0, 6).map((t) => {
17579
17739
  const outcome = t.failed > 0 ? `${t.failed} failed` : t.last_status === 0 ? "ok" : "?";
17580
17740
  const where = t.targets.length > 0 ? ` \u2192 ${t.targets.slice(0, 2).join(", ")}` : "";
17581
17741
  return `${t.runs}\xD7 ${t.name} (${outcome})${where}`;
17582
17742
  });
17583
- const more = run.foldResult.tools.length > 6 ? ` \u2026 +${run.foldResult.tools.length - 6} more` : "";
17743
+ const more = run2.foldResult.tools.length > 6 ? ` \u2026 +${run2.foldResult.tools.length - 6} more` : "";
17584
17744
  out += row("tools", shown.join(" \xB7 ") + more);
17585
- if (run.foldResult.coverage.toolNamesDropped > 0) {
17586
- out += row("", `\u26A0 ${run.foldResult.coverage.toolNamesDropped} tool name(s) refused by the cap`);
17745
+ if (run2.foldResult.coverage.toolNamesDropped > 0) {
17746
+ out += row("", `\u26A0 ${run2.foldResult.coverage.toolNamesDropped} tool name(s) refused by the cap`);
17587
17747
  }
17588
17748
  }
17589
- if (run.foldResult?.tasks?.length) {
17590
- const t = run.foldResult.tasks;
17749
+ if (run2.foldResult?.tasks?.length) {
17750
+ const t = run2.foldResult.tasks;
17591
17751
  const done2 = t.filter((x) => x.status === "completed").length;
17592
17752
  out += row("tasks", `${t.length} \xB7 ${done2} completed \xB7 ` + list(t.slice(0, 4).map((x) => `#${x.id} ${x.name} [${x.status}]`), 4));
17593
17753
  }
17594
- if (run.specs?.length) {
17595
- const readThisSession = new Set(run.actionSummary?.files_read ?? []);
17596
- const labelled = run.specs.map(
17754
+ if (run2.specs?.length) {
17755
+ const readThisSession = new Set(run2.actionSummary?.files_read ?? []);
17756
+ const labelled = run2.specs.map(
17597
17757
  (s) => `${s.path}${readThisSession.has(s.path) ? " (read)" : " (positional)"}`
17598
17758
  );
17599
- out += row("specs", `${run.specs.length} \xB7 ${list(labelled, 5)}`);
17759
+ out += row("specs", `${run2.specs.length} \xB7 ${list(labelled, 5)}`);
17600
17760
  }
17601
- if (run.staticResults.findings.length > 0) {
17602
- out += row("static", `${run.staticResults.findings.length} finding(s) from ${run.staticResults.summary.tools_run.join(", ") || "no tools"}`);
17761
+ if (run2.staticResults.findings.length > 0) {
17762
+ out += row("static", `${run2.staticResults.findings.length} finding(s) from ${run2.staticResults.summary.tools_run.join(", ") || "no tools"}`);
17603
17763
  }
17604
- if (run.decision && run.decision !== "(unrecognised)") {
17605
- out += row("verdict", run.decision + (run.silenced ? ` \xB7 agent channel silenced (${run.silenced})` : ""));
17764
+ if (run2.decision && run2.decision !== "(unrecognised)") {
17765
+ out += row("verdict", run2.decision + (run2.silenced ? ` \xB7 agent channel silenced (${run2.silenced})` : ""));
17606
17766
  }
17607
17767
  return out;
17608
17768
  }
17609
- function installRunEvidence(run) {
17769
+ function installRunEvidence(run2) {
17610
17770
  const startedAt = Date.now();
17611
17771
  process.on("exit", () => {
17612
17772
  try {
17613
- logToFileOnly(formatRunEvidence(run, startedAt));
17773
+ logToFileOnly(formatRunEvidence(run2, startedAt));
17614
17774
  } catch {
17615
17775
  }
17616
17776
  });
@@ -18261,13 +18421,13 @@ async function readStopHookStdin() {
18261
18421
  return empty;
18262
18422
  }
18263
18423
  }
18264
- async function bootstrap(run) {
18265
- const { opts, globals } = run;
18424
+ async function bootstrap(run2) {
18425
+ const { opts, globals } = run2;
18266
18426
  try {
18267
18427
  process.chdir(repoRoot());
18268
18428
  } catch {
18269
18429
  }
18270
- run.treeFrame = resolveFrame({ command: "", on: [], hookCwd: null }).frame;
18430
+ run2.treeFrame = resolveFrame({ command: "", on: [], hookCwd: null }).frame;
18271
18431
  const turnId = `t-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
18272
18432
  let reachability = resolveReachability({
18273
18433
  autonomousFlag: process.env.VERITY_AUTONOMOUS === "1" || opts.mode === "autonomous",
@@ -18281,7 +18441,7 @@ async function bootstrap(run) {
18281
18441
  const rawSessionId = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
18282
18442
  const baselineSessionId = sessionScopeKey(scopeToken, rawSessionId);
18283
18443
  if (tokenResult.ok) {
18284
- run.beacon = {
18444
+ run2.beacon = {
18285
18445
  resolveServiceUrl: async () => {
18286
18446
  const u = await resolveServiceUrl(globals.serviceUrl);
18287
18447
  return u.ok ? u.data : null;
@@ -18301,7 +18461,7 @@ async function bootstrap(run) {
18301
18461
  age_ms: Date.now() - baseline.captured_at
18302
18462
  });
18303
18463
  }
18304
- Object.assign(run, { actionSummary, assistantResponse, baseline, baselineSessionId, reachability, sessionId, stopReason, tokenResult, transcriptPath, turnId });
18464
+ Object.assign(run2, { actionSummary, assistantResponse, baseline, baselineSessionId, reachability, sessionId, stopReason, tokenResult, transcriptPath, turnId });
18305
18465
  }
18306
18466
 
18307
18467
  // src/lib/self-scope.ts
@@ -18454,7 +18614,7 @@ function channelSilence(input) {
18454
18614
  // src/lib/cli-version.ts
18455
18615
  function cliVersion() {
18456
18616
  try {
18457
- return true ? "0.31.1-experimental.79dc9c2" : "dev";
18617
+ return true ? "0.31.1-experimental.be74f71" : "dev";
18458
18618
  } catch {
18459
18619
  return "dev";
18460
18620
  }
@@ -18560,12 +18720,12 @@ function runCodacyAnalysis(files) {
18560
18720
  spawnError: proc.error?.message
18561
18721
  });
18562
18722
  }
18563
- function interpretAnalyzerRun(run) {
18564
- const output = run.stdout ?? "";
18723
+ function interpretAnalyzerRun(run2) {
18724
+ const output = run2.stdout ?? "";
18565
18725
  if (!output.trim()) {
18566
18726
  return withFailure(
18567
- run.spawnError ? "spawn_failed" : "no_output",
18568
- run.spawnError ?? run.stderr ?? `exit ${run.status}`
18727
+ run2.spawnError ? "spawn_failed" : "no_output",
18728
+ run2.spawnError ?? run2.stderr ?? `exit ${run2.status}`
18569
18729
  );
18570
18730
  }
18571
18731
  let parsed;
@@ -18706,7 +18866,7 @@ function describeOpenElsewhere(open) {
18706
18866
  (+${open.length - 5} more)` : "";
18707
18867
  return `STILL OPEN ELSEWHERE. ${open.length} finding(s) Verity raised earlier are still on disk in files this run did not review:
18708
18868
  ${lines.join("\n")}${more}
18709
- They did not gate this run \u2014 this verdict covers the current change only. The tree is not clean.
18869
+ They did not fail this run \u2014 this verdict covers the current change only. The tree is not clean.
18710
18870
  Fix them, or record a disposition: verity waive <pattern-id> --file <path> --reason "\u2026"`;
18711
18871
  }
18712
18872
 
@@ -18732,9 +18892,9 @@ function localOnlyAndExit(staticResults) {
18732
18892
  });
18733
18893
  process.exit(0);
18734
18894
  }
18735
- async function passAndExit(run, reason, skip, kindOverride) {
18736
- run.skipReason = skip;
18737
- const sent = await sendSkipBeacon(run.beacon, skip);
18895
+ async function passAndExit(run2, reason, skip, kindOverride) {
18896
+ run2.skipReason = skip;
18897
+ const sent = await sendSkipBeacon(run2.beacon, skip);
18738
18898
  logEvent("skip", { reason: skip, beacon: sent });
18739
18899
  const POLICY_SKIPS = /* @__PURE__ */ new Set([
18740
18900
  "no-analyzable-files",
@@ -18749,7 +18909,7 @@ async function passAndExit(run, reason, skip, kindOverride) {
18749
18909
  "no-delta-since-last-review"
18750
18910
  ]);
18751
18911
  const skipKind = kindOverride ?? (POLICY_SKIPS.has(skip) ? "policy" : "capacity");
18752
- const changed = run.changedUniverse;
18912
+ const changed = run2.changedUniverse;
18753
18913
  const { coverage, unaccounted } = reconcileCoverage(changed, {
18754
18914
  reviewed: [],
18755
18915
  notReviewed: changed.map((path) => ({ path, reason: skip, stage: "pre-flight", kind: skipKind }))
@@ -18777,10 +18937,10 @@ async function passAndExit(run, reason, skip, kindOverride) {
18777
18937
  }
18778
18938
 
18779
18939
  // src/commands/analyze/phases/02-scope.ts
18780
- async function scope(run) {
18781
- const { assistantResponse } = run;
18940
+ async function scope(run2) {
18941
+ const { assistantResponse } = run2;
18782
18942
  const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
18783
- run.changedUniverse = allChanged;
18943
+ run2.changedUniverse = allChanged;
18784
18944
  const { kept: external } = partitionVerityOwned(allChanged);
18785
18945
  const verityIgnore = loadVerityIgnore();
18786
18946
  const ignored = partitionIgnored(external, verityIgnore);
@@ -18805,10 +18965,10 @@ async function scope(run) {
18805
18965
  const securityFiles = filterSecurity(inScope);
18806
18966
  const noFilesChanged = analyzable.length === 0 && reviewable.length === 0 && securityFiles.length === 0;
18807
18967
  if (noFilesChanged && !assistantResponse) {
18808
- await passAndExit(run, "No analyzable files changed", "no-analyzable-files");
18968
+ await passAndExit(run2, "No analyzable files changed", "no-analyzable-files");
18809
18969
  }
18810
18970
  const allForReview = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable]));
18811
- Object.assign(run, { allChanged, allForReview, analyzable, hasRecentCommitFiles, noFilesChanged, reviewable, securityFiles, verityIgnored: ignored });
18971
+ Object.assign(run2, { allChanged, allForReview, analyzable, hasRecentCommitFiles, noFilesChanged, reviewable, securityFiles, verityIgnored: ignored });
18812
18972
  }
18813
18973
 
18814
18974
  // src/lib/specs.ts
@@ -18954,8 +19114,8 @@ function discoverGuardDocs(rangeFiles2) {
18954
19114
  }
18955
19115
 
18956
19116
  // src/commands/analyze/phases/03-intent-inputs.ts
18957
- async function intentInputs(run) {
18958
- const { actionSummary, allForReview, assistantResponse, baseline, baselineSessionId } = run;
19117
+ async function intentInputs(run2) {
19118
+ const { actionSummary, allForReview, assistantResponse, baseline, baselineSessionId } = run2;
18959
19119
  if (isCommandOnlyTurn({
18960
19120
  userCommands: actionSummary?.user_commands,
18961
19121
  userCommandsTruncated: actionSummary?.user_commands_truncated,
@@ -18963,11 +19123,11 @@ async function intentInputs(run) {
18963
19123
  agentToolCalls: actionSummary?.total_tool_calls ?? 0,
18964
19124
  authorshipIsObservable: !!actionSummary && actionSummary.transcript_windowed !== "orphaned"
18965
19125
  })) {
18966
- await passAndExit(run, "User command only \u2014 skipping analysis", "command-only-turn");
19126
+ await passAndExit(run2, "User command only \u2014 skipping analysis", "command-only-turn");
18967
19127
  }
18968
19128
  {
18969
19129
  const ignoreKeys = ignoreStateKeys(
18970
- run.tokenResult.ok ? run.tokenResult.data.token : void 0,
19130
+ run2.tokenResult.ok ? run2.tokenResult.data.token : void 0,
18971
19131
  null
18972
19132
  );
18973
19133
  const found = resolveIgnoreState([baselineSessionId, ...ignoreKeys]);
@@ -18991,7 +19151,7 @@ async function intentInputs(run) {
18991
19151
  if (declaration.scope === "turn" && found) clearActiveDeclaration(found.key);
18992
19152
  logEvent("ignore_honoured", { scope: declaration.scope, origin: declaration.origin });
18993
19153
  await passAndExit(
18994
- run,
19154
+ run2,
18995
19155
  `skipping this turn \u2014 declared housekeeping ("${declaration.reason}")`,
18996
19156
  "declared-ignore"
18997
19157
  );
@@ -19005,7 +19165,7 @@ async function intentInputs(run) {
19005
19165
  const notice = `Verity: the ignore declared for this window ("${declaration.reason}") was voided \u2014 ${outcome.why}. Reviewing normally.`;
19006
19166
  process.stderr.write(`${notice}
19007
19167
  `);
19008
- run.voidedIgnoreNotice = notice;
19168
+ run2.voidedIgnoreNotice = notice;
19009
19169
  }
19010
19170
  }
19011
19171
  }
@@ -19027,32 +19187,32 @@ async function intentInputs(run) {
19027
19187
  const adopted = absorbIntoBaseline(setupAuthored, baselineSessionId);
19028
19188
  logEvent("baseline_absorbed", { skip: "verity-command", offered: setupAuthored.length, adopted });
19029
19189
  }
19030
- await passAndExit(run, "Verity command \u2014 skipping analysis", "verity-command");
19190
+ await passAndExit(run2, "Verity command \u2014 skipping analysis", "verity-command");
19031
19191
  }
19032
19192
  if (shouldSkipForBareAck({ prompt: latestPrompt, turnAuthoredCode, canSeeTurnAuthorship })) {
19033
- await passAndExit(run, "Bare acknowledgment \u2014 skipping analysis", "bare-acknowledgment");
19193
+ await passAndExit(run2, "Bare acknowledgment \u2014 skipping analysis", "bare-acknowledgment");
19034
19194
  }
19035
19195
  if (isReflectionQuestion(assistantResponse) && !turnAuthoredCode && canSeeTurnAuthorship) {
19036
- await passAndExit(run, "Reflection-prompt turn \u2014 skipping analysis", "reflection-prompt");
19196
+ await passAndExit(run2, "Reflection-prompt turn \u2014 skipping analysis", "reflection-prompt");
19037
19197
  }
19038
- Object.assign(run, { authorshipIsObservable, conversation, earlyFold, plans, specs, turnAuthoredCode });
19198
+ Object.assign(run2, { authorshipIsObservable, conversation, earlyFold, plans, specs, turnAuthoredCode });
19039
19199
  }
19040
19200
 
19041
19201
  // src/commands/analyze/phases/04-connect.ts
19042
- async function connect(run) {
19043
- const { opts, globals } = run;
19044
- const { analyzable, baseline, securityFiles, tokenResult } = run;
19202
+ async function connect(run2) {
19203
+ const { opts, globals } = run2;
19204
+ const { analyzable, baseline, securityFiles, tokenResult } = run2;
19045
19205
  const urlResult = await resolveServiceUrl(globals.serviceUrl);
19046
19206
  if (!tokenResult.ok || !urlResult.ok) {
19047
19207
  localOnlyAndExit(runLocalStatic(analyzable, securityFiles, baseline, !!opts.skipStatic));
19048
19208
  }
19049
- Object.assign(run, { urlResult, serviceUrl: urlResult.data, token: tokenResult.data.token });
19209
+ Object.assign(run2, { urlResult, serviceUrl: urlResult.data, token: tokenResult.data.token });
19050
19210
  }
19051
19211
 
19052
19212
  // src/commands/analyze/phases/05-mode.ts
19053
- async function mode(run) {
19054
- const { opts, globals } = run;
19055
- const { actionSummary, allForReview, assistantResponse, baseline, conversation, memory, noFilesChanged, serviceUrl, sessionId, token, turnAuthoredCode } = run;
19213
+ async function mode(run2) {
19214
+ const { opts, globals } = run2;
19215
+ const { actionSummary, allForReview, assistantResponse, baseline, conversation, memory, noFilesChanged, serviceUrl, sessionId, token, turnAuthoredCode } = run2;
19056
19216
  const sessionIdForMemory = sessionId || process.env.CLAUDE_SESSION_ID || "";
19057
19217
  let contextFilePaths = [];
19058
19218
  let predictedMode;
@@ -19093,7 +19253,7 @@ async function mode(run) {
19093
19253
  );
19094
19254
  }
19095
19255
  const investigated = didAgentInvestigate(actionSummary);
19096
- run.modeDecision = {
19256
+ run2.modeDecision = {
19097
19257
  predicted: predictedMode ?? null,
19098
19258
  resolved: analysisMode,
19099
19259
  authored: turnAuthoredCode,
@@ -19113,13 +19273,13 @@ async function mode(run) {
19113
19273
  });
19114
19274
  if (analysisMode === "skip") {
19115
19275
  await passAndExit(
19116
- run,
19276
+ run2,
19117
19277
  "Skip mode \u2014 no code work to analyze",
19118
19278
  "skip-mode",
19119
19279
  turnAuthoredCode ? "capacity" : void 0
19120
19280
  );
19121
19281
  }
19122
- Object.assign(run, { analysisMode, contextFilePaths, sessionAuthoredCode, sessionIdForMemory });
19282
+ Object.assign(run2, { analysisMode, contextFilePaths, sessionAuthoredCode, sessionIdForMemory });
19123
19283
  }
19124
19284
 
19125
19285
  // src/lib/fold.ts
@@ -19579,12 +19739,12 @@ function checkConservation(changedFiles, result, repoRoot2) {
19579
19739
  }
19580
19740
 
19581
19741
  // src/commands/analyze/phases/06-evidence.ts
19582
- async function evidence(run) {
19583
- const { opts } = run;
19584
- const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath, turnAuthoredCode } = run;
19585
- let { analysisMode, earlyFold } = run;
19742
+ async function evidence(run2) {
19743
+ const { opts } = run2;
19744
+ const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath, turnAuthoredCode } = run2;
19745
+ let { analysisMode, earlyFold } = run2;
19586
19746
  const recordFlip = (stage) => {
19587
- if (run.modeDecision) run.modeDecision = { ...run.modeDecision, resolved: "plan", flip: stage };
19747
+ if (run2.modeDecision) run2.modeDecision = { ...run2.modeDecision, resolved: "plan", flip: stage };
19588
19748
  logEvent("mode_flipped", { stage, to: "plan" });
19589
19749
  };
19590
19750
  const planWorthy = !!assistantResponse && !turnAuthoredCode;
@@ -19611,7 +19771,7 @@ async function evidence(run) {
19611
19771
  analysisMode = "plan";
19612
19772
  recordFlip("debounce");
19613
19773
  } else {
19614
- await passAndExit(run, debounceSkip, "debounce");
19774
+ await passAndExit(run2, debounceSkip, "debounce");
19615
19775
  }
19616
19776
  }
19617
19777
  if (analysisMode !== "plan") {
@@ -19622,7 +19782,7 @@ async function evidence(run) {
19622
19782
  analysisMode = "plan";
19623
19783
  recordFlip("mtime");
19624
19784
  } else {
19625
- await passAndExit(run, mtimeSkip, "no-delta-since-last-review");
19785
+ await passAndExit(run2, mtimeSkip, "no-delta-since-last-review");
19626
19786
  }
19627
19787
  }
19628
19788
  }
@@ -19635,7 +19795,7 @@ async function evidence(run) {
19635
19795
  analysisMode = "plan";
19636
19796
  recordFlip("content-hash");
19637
19797
  } else {
19638
- await passAndExit(run, hashResult.skip, "no-delta-since-last-review");
19798
+ await passAndExit(run2, hashResult.skip, "no-delta-since-last-review");
19639
19799
  }
19640
19800
  }
19641
19801
  contentHash = hashResult.hash;
@@ -19643,7 +19803,7 @@ async function evidence(run) {
19643
19803
  const scoped = scopeToAuthored(allForReview, actionSummary);
19644
19804
  const canTrustNoneAuthored = scoped.signal === "none-authored" && authorshipIsObservable;
19645
19805
  if (canTrustNoneAuthored && !hasNonEditAuthorship(actionSummary, sessionAuthoredCode)) {
19646
- await passAndExit(run, "No agent-authored code this turn \u2014 working-tree changes were not authored by this session", "zero-increment");
19806
+ await passAndExit(run2, "No agent-authored code this turn \u2014 working-tree changes were not authored by this session", "zero-increment");
19647
19807
  }
19648
19808
  if (scoped.signal === "none-authored" && !authorshipIsObservable) {
19649
19809
  logEvent("none_authored_unverifiable", {
@@ -19700,7 +19860,7 @@ async function evidence(run) {
19700
19860
  recordFlip("empty-after-scoping");
19701
19861
  } else {
19702
19862
  await passAndExit(
19703
- run,
19863
+ run2,
19704
19864
  "No files within size limits to analyze",
19705
19865
  "size-limit",
19706
19866
  codeDelta.excluded.length > 0 ? "capacity" : "policy"
@@ -19725,7 +19885,7 @@ async function evidence(run) {
19725
19885
  currentCommit = getCurrentCommit();
19726
19886
  iteration = readIteration(currentCommit);
19727
19887
  }
19728
- Object.assign(run, { analysisMode, codeDelta, contentHash, currentCommit, earlyFold, iteration, snapshotResult, staticResults });
19888
+ Object.assign(run2, { analysisMode, codeDelta, contentHash, currentCommit, earlyFold, iteration, snapshotResult, staticResults });
19729
19889
  }
19730
19890
 
19731
19891
  // src/lib/cache-cleanup.ts
@@ -19822,8 +19982,8 @@ function gatherContextFiles(contextPaths, deltaFiles) {
19822
19982
  }
19823
19983
 
19824
19984
  // src/commands/analyze/phases/07-context-files.ts
19825
- async function contextFiles(run) {
19826
- const { codeDelta, contextFilePaths } = run;
19985
+ async function contextFiles(run2) {
19986
+ const { codeDelta, contextFilePaths } = run2;
19827
19987
  const { kept: externalContext } = partitionVerityOwned(contextFilePaths ?? []);
19828
19988
  const contextFiles2 = gatherContextFiles(externalContext, codeDelta.files);
19829
19989
  for (const f of codeDelta.files) {
@@ -20180,9 +20340,9 @@ async function runSeed(opts) {
20180
20340
  // src/commands/analyze/phases/08-memory-manifest.ts
20181
20341
  var import_node_fs32 = require("node:fs");
20182
20342
  var import_node_path24 = require("node:path");
20183
- async function memoryManifest(run) {
20184
- const { globals } = run;
20185
- const { serviceUrl, token } = run;
20343
+ async function memoryManifest(run2) {
20344
+ const { globals } = run2;
20345
+ const { serviceUrl, token } = run2;
20186
20346
  let memoryManifest2;
20187
20347
  let deletedNodePaths = [];
20188
20348
  let editedUploads = [];
@@ -20234,12 +20394,12 @@ async function memoryManifest(run) {
20234
20394
  editedUploads = await computeEditedNodeUploads();
20235
20395
  } catch {
20236
20396
  }
20237
- Object.assign(run, { autoSeedNotice, deletedNodePaths, editedUploads, memoryManifest: memoryManifest2 });
20397
+ Object.assign(run2, { autoSeedNotice, deletedNodePaths, editedUploads, memoryManifest: memoryManifest2 });
20238
20398
  }
20239
20399
 
20240
20400
  // src/commands/analyze/phases/09-fold-transcript.ts
20241
- async function foldTranscript(run) {
20242
- const { allForReview, earlyFold, transcriptPath } = run;
20401
+ async function foldTranscript(run2) {
20402
+ const { allForReview, earlyFold, transcriptPath } = run2;
20243
20403
  let foldResult = null;
20244
20404
  let foldConservation = null;
20245
20405
  if (transcriptPath) {
@@ -20256,7 +20416,7 @@ async function foldTranscript(run) {
20256
20416
  foldResult = null;
20257
20417
  }
20258
20418
  }
20259
- Object.assign(run, { foldConservation, foldResult });
20419
+ Object.assign(run2, { foldConservation, foldResult });
20260
20420
  }
20261
20421
 
20262
20422
  // src/lib/increment.ts
@@ -20308,10 +20468,10 @@ function computeIncrement(reviewedPaths, hashOf, priorAuthored) {
20308
20468
 
20309
20469
  // src/commands/analyze/phases/10-working-memory.ts
20310
20470
  var import_node_path25 = require("node:path");
20311
- async function workingMemory(run) {
20312
- const { opts } = run;
20313
- const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } = run;
20314
- let { reachability } = run;
20471
+ async function workingMemory(run2) {
20472
+ const { opts } = run2;
20473
+ const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } = run2;
20474
+ let { reachability } = run2;
20315
20475
  const memorySession = sessionDossier(token, sessionId ?? process.env.CLAUDE_SESSION_ID ?? null);
20316
20476
  let memory = null;
20317
20477
  let incrementReport = null;
@@ -20397,7 +20557,7 @@ async function workingMemory(run) {
20397
20557
  hasUserPrompt: (conversation?.prompts?.length ?? 0) > 0,
20398
20558
  isTTY: process.stdout.isTTY === true
20399
20559
  });
20400
- Object.assign(run, { incrementReport, memory, memorySession, reachability });
20560
+ Object.assign(run2, { incrementReport, memory, memorySession, reachability });
20401
20561
  }
20402
20562
 
20403
20563
  // src/lib/note-budget.ts
@@ -20516,8 +20676,8 @@ function resolveTaskContext(opts) {
20516
20676
  // src/commands/analyze/phases/11-build-request.ts
20517
20677
  var MAX_ASSISTANT_RESPONSE_CHARS_PLAN = 32768;
20518
20678
  var MAX_ASSISTANT_RESPONSE_CHARS_DEFAULT = 8e3;
20519
- async function buildRequest(run) {
20520
- const { actionSummary, allChanged, allForReview, analysisMode, analyzable, assistantResponse, codeDelta, conversation, deletedNodePaths, editedUploads, foldConservation, foldResult, incrementReport, iteration, memory, memoryManifest: memoryManifest2, memorySession, plans, reachability, reviewable, securityFiles, sessionId, snapshotResult, specs, staticResults, stopReason, turnId } = run;
20679
+ async function buildRequest(run2) {
20680
+ const { actionSummary, allChanged, allForReview, analysisMode, analyzable, assistantResponse, codeDelta, conversation, deletedNodePaths, editedUploads, foldConservation, foldResult, incrementReport, iteration, memory, memoryManifest: memoryManifest2, memorySession, plans, reachability, reviewable, securityFiles, sessionId, snapshotResult, specs, staticResults, stopReason, turnId } = run2;
20521
20681
  const excludedByReason = {};
20522
20682
  for (const e of codeDelta.excluded ?? []) {
20523
20683
  excludedByReason[e.reason] = (excludedByReason[e.reason] ?? 0) + 1;
@@ -20547,15 +20707,15 @@ async function buildRequest(run) {
20547
20707
  // the state, so the number is one turn lagged by construction. The
20548
20708
  // degenerate win for the budget is a dead channel that looks like clean
20549
20709
  // code; this is what makes "did delivery rate collapse" a query.
20550
- advisory_delivered_prior: readAdvisoryEpisode(run.baselineSessionId)?.delivered ?? 0,
20710
+ advisory_delivered_prior: readAdvisoryEpisode(run2.baselineSessionId)?.delivered ?? 0,
20551
20711
  // `.verityignore` — see CoverageTelemetry.verityignore for why the SHARE is
20552
20712
  // the number that matters and why no paths travel with it.
20553
20713
  verityignore: {
20554
- rules: run.verityIgnored.rules,
20555
- excluded: run.verityIgnored.ignored.length,
20556
- share: ignoreShare(run.verityIgnored.kept.length, run.verityIgnored.ignored.length),
20557
- security_excluded: run.verityIgnored.securityExcluded.length,
20558
- suspended: run.verityIgnored.suspended
20714
+ rules: run2.verityIgnored.rules,
20715
+ excluded: run2.verityIgnored.ignored.length,
20716
+ share: ignoreShare(run2.verityIgnored.kept.length, run2.verityIgnored.ignored.length),
20717
+ security_excluded: run2.verityIgnored.securityExcluded.length,
20718
+ suspended: run2.verityIgnored.suspended
20559
20719
  }
20560
20720
  };
20561
20721
  const requestBody = {
@@ -20750,7 +20910,7 @@ async function buildRequest(run) {
20750
20910
  }
20751
20911
  requestBody.intent_context = intentContext;
20752
20912
  }
20753
- Object.assign(run, { requestBody });
20913
+ Object.assign(run2, { requestBody });
20754
20914
  }
20755
20915
 
20756
20916
  // src/lib/offline.ts
@@ -20811,9 +20971,9 @@ function shouldWarmRetryAnalyze(result) {
20811
20971
  }
20812
20972
 
20813
20973
  // src/commands/analyze/phases/12-transmit.ts
20814
- async function transmit(run) {
20815
- const { globals } = run;
20816
- const { codeDelta, requestBody, serviceUrl, staticResults, token } = run;
20974
+ async function transmit(run2) {
20975
+ const { globals } = run2;
20976
+ const { codeDelta, requestBody, serviceUrl, staticResults, token } = run2;
20817
20977
  const ANALYZE_TIMEOUT_MS = 1e5;
20818
20978
  let result = await analyzeRequest({
20819
20979
  serviceUrl,
@@ -20876,15 +21036,34 @@ async function transmit(run) {
20876
21036
  }
20877
21037
  const response = result.data;
20878
21038
  const decision = response.gate_decision ?? "(unrecognised)";
20879
- Object.assign(run, { decision, response });
21039
+ Object.assign(run2, { decision, response });
20880
21040
  }
20881
21041
 
20882
21042
  // src/commands/analyze/phases/13-reconcile.ts
20883
21043
  var import_node_fs35 = require("node:fs");
20884
21044
  var import_node_path26 = require("node:path");
20885
- async function reconcile(run) {
20886
- const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run;
21045
+ async function reconcile(run2) {
21046
+ const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run2;
20887
21047
  const sentPaths = codeDelta.files.map((f) => f.path);
21048
+ if (memorySession) {
21049
+ try {
21050
+ const settledSites = response.metadata?.settled_sites;
21051
+ if (Array.isArray(settledSites)) {
21052
+ for (const site of settledSites) {
21053
+ const siteRecord = site;
21054
+ const file = typeof siteRecord?.file === "string" ? siteRecord.file : null;
21055
+ const patternId = typeof siteRecord?.pattern_id === "string" ? siteRecord.pattern_id : null;
21056
+ if (!file || !patternId) continue;
21057
+ appendEvent(memorySession.d, {
21058
+ k: "outcome",
21059
+ anchor_key: statementAnchorKey(file, patternId),
21060
+ outcome: "answered"
21061
+ });
21062
+ }
21063
+ }
21064
+ } catch {
21065
+ }
21066
+ }
20888
21067
  let openElsewhere = [];
20889
21068
  if (memorySession) {
20890
21069
  try {
@@ -20965,7 +21144,7 @@ async function reconcile(run) {
20965
21144
  // below is the signal that replaces the noise.
20966
21145
  //
20967
21146
  // Taken from the run, not recomputed — see context.ts `verityIgnored`.
20968
- ...run.verityIgnored.ignored.map((path) => ({
21147
+ ...run2.verityIgnored.ignored.map((path) => ({
20969
21148
  path,
20970
21149
  reason: "verityignore",
20971
21150
  stage: "verityignore",
@@ -21045,14 +21224,6 @@ async function reconcile(run) {
21045
21224
  })() : [];
21046
21225
  if (memorySession) {
21047
21226
  try {
21048
- const settledAnchors = response.metadata?.settled_anchors;
21049
- if (Array.isArray(settledAnchors)) {
21050
- for (const anchorKey of settledAnchors) {
21051
- if (typeof anchorKey === "string" && anchorKey.length > 0) {
21052
- appendEvent(memorySession.d, { k: "outcome", anchor_key: anchorKey, outcome: "answered" });
21053
- }
21054
- }
21055
- }
21056
21227
  recordVerdict(memorySession.d, {
21057
21228
  runId: response.run_id ?? turnId,
21058
21229
  decision,
@@ -21093,11 +21264,11 @@ async function reconcile(run) {
21093
21264
  `
21094
21265
  );
21095
21266
  }
21096
- Object.assign(run, { intentRepeatCount, openElsewhere, priorPendingFingerprints, reviewCoverage, sentPaths, silenced, watermarkHash, watermarkIsPartial });
21267
+ Object.assign(run2, { intentRepeatCount, openElsewhere, priorPendingFingerprints, reviewCoverage, sentPaths, silenced, watermarkHash, watermarkIsPartial });
21097
21268
  }
21098
21269
 
21099
21270
  // src/lib/emit.ts
21100
- var YELLOW2 = "\x1B[33m";
21271
+ var YELLOW3 = "\x1B[33m";
21101
21272
  var NC2 = "\x1B[0m";
21102
21273
  function emitVerdict(input) {
21103
21274
  const exit = input.exit ?? ((code) => process.exit(code));
@@ -21108,7 +21279,7 @@ function emitVerdict(input) {
21108
21279
  const note = [describeCoverage(coverage), describeOpenElsewhere(openElsewhere)].filter(Boolean).join("\n\n") || null;
21109
21280
  if (unaccounted.length > 0) {
21110
21281
  process.stderr.write(
21111
- `${YELLOW2}Verity: ${unaccounted.length} changed file(s) could not be attributed to any review stage \u2014 counted as unreviewed.${NC2}
21282
+ `${YELLOW3}Verity: ${unaccounted.length} changed file(s) could not be attributed to any review stage \u2014 counted as unreviewed.${NC2}
21112
21283
  `
21113
21284
  );
21114
21285
  }
@@ -21120,7 +21291,7 @@ ${input.agentContext}
21120
21291
  `);
21121
21292
  }
21122
21293
  if (note && !input.silenced) process.stderr.write(`
21123
- ${YELLOW2}${note}${NC2}
21294
+ ${YELLOW3}${note}${NC2}
21124
21295
  `);
21125
21296
  return exit(2);
21126
21297
  }
@@ -21206,10 +21377,10 @@ function screenRemediation(fix, findingFile) {
21206
21377
  function agentContextFor(response, intentRepeat = 0, priorPendingFingerprints = []) {
21207
21378
  return buildAgentContext(channelInputFrom(response, intentRepeat, priorPendingFingerprints));
21208
21379
  }
21209
- async function render(run) {
21210
- const { opts, globals } = run;
21211
- const { actionSummary, assistantResponse, autoSeedNotice, voidedIgnoreNotice, baselineSessionId, codeDelta, contentHash, conversation, currentCommit, decision, intentRepeatCount, memory, openElsewhere, priorPendingFingerprints, response, reviewCoverage, serviceUrl, sessionIdForMemory, silenced, token, watermarkHash, watermarkIsPartial } = run;
21212
- let { iteration } = run;
21380
+ async function render(run2) {
21381
+ const { opts, globals } = run2;
21382
+ const { actionSummary, assistantResponse, autoSeedNotice, voidedIgnoreNotice, baselineSessionId, codeDelta, contentHash, conversation, currentCommit, decision, intentRepeatCount, memory, openElsewhere, priorPendingFingerprints, response, reviewCoverage, serviceUrl, sessionIdForMemory, silenced, token, watermarkHash, watermarkIsPartial } = run2;
21383
+ let { iteration } = run2;
21213
21384
  const metadata = response.metadata ?? {};
21214
21385
  const intentAmbiguity = metadata.intent_ambiguity;
21215
21386
  if (intentAmbiguity != null && intentAmbiguity > 5) {
@@ -21316,7 +21487,7 @@ async function render(run) {
21316
21487
  const blocks = prior.blocks + 1;
21317
21488
  const decisionNow = mayBlock({
21318
21489
  reviewedFileCount: codeDelta.files.length,
21319
- staticFindingCount: run.staticResults?.findings?.length ?? 0,
21490
+ staticFindingCount: run2.staticResults?.findings?.length ?? 0,
21320
21491
  cycleCutFired: silenced !== null,
21321
21492
  attempts,
21322
21493
  blocks,
@@ -21347,7 +21518,7 @@ async function render(run) {
21347
21518
  });
21348
21519
  emitVerdict({
21349
21520
  proposed: "WARN",
21350
- changed: run.changedUniverse,
21521
+ changed: run2.changedUniverse,
21351
21522
  coverage: reviewCoverage,
21352
21523
  userSummary: lines.length > 0 ? `${summary}
21353
21524
  ${lines.join("\n")}` : summary,
@@ -21439,7 +21610,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
21439
21610
  `);
21440
21611
  emitVerdict({
21441
21612
  proposed: "FAIL",
21442
- changed: run.changedUniverse,
21613
+ changed: run2.changedUniverse,
21443
21614
  coverage: reviewCoverage,
21444
21615
  userSummary: "",
21445
21616
  // Subject to the SAME cycle cut as PASS/WARN. Suppressing here is safe:
@@ -21464,7 +21635,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
21464
21635
  userSummary += loginNudge + grantNudge;
21465
21636
  emitVerdict({
21466
21637
  proposed: "PASS",
21467
- changed: run.changedUniverse,
21638
+ changed: run2.changedUniverse,
21468
21639
  coverage: reviewCoverage,
21469
21640
  userSummary,
21470
21641
  agentContext: silenced ? null : agentContextFor(response, intentRepeatCount, priorPendingFingerprints),
@@ -21486,7 +21657,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
21486
21657
  userSummary += loginNudge + grantNudge;
21487
21658
  emitVerdict({
21488
21659
  proposed: "WARN",
21489
- changed: run.changedUniverse,
21660
+ changed: run2.changedUniverse,
21490
21661
  coverage: reviewCoverage,
21491
21662
  userSummary,
21492
21663
  agentContext: silenced ? null : agentContextFor(response, intentRepeatCount, priorPendingFingerprints),
@@ -21511,7 +21682,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
21511
21682
  process.exit(0);
21512
21683
  }
21513
21684
  }
21514
- Object.assign(run, { iteration });
21685
+ Object.assign(run2, { iteration });
21515
21686
  }
21516
21687
 
21517
21688
  // src/commands/analyze/index.ts
@@ -21561,18 +21732,18 @@ function registerAnalyzeCommand(program2) {
21561
21732
  }
21562
21733
  var tracing = () => process.env.VERITY_TRACE_PHASES === "1";
21563
21734
  async function runAnalyze(opts, globals) {
21564
- const run = createRun(opts, globals);
21565
- installRunEvidence(run);
21735
+ const run2 = createRun(opts, globals);
21736
+ installRunEvidence(run2);
21566
21737
  for (const [name, phase] of PIPELINE) {
21567
- run.phaseReached = name;
21738
+ run2.phaseReached = name;
21568
21739
  if (!tracing()) {
21569
- await phase(run);
21570
- run.phasesCompleted.push(name);
21740
+ await phase(run2);
21741
+ run2.phasesCompleted.push(name);
21571
21742
  continue;
21572
21743
  }
21573
21744
  const started = Date.now();
21574
- await phase(run);
21575
- run.phasesCompleted.push(name);
21745
+ await phase(run2);
21746
+ run2.phasesCompleted.push(name);
21576
21747
  process.stderr.write(`verity\xB7phase ${name} ${Date.now() - started}ms
21577
21748
  `);
21578
21749
  }
@@ -21675,8 +21846,8 @@ async function runReview(opts, globals) {
21675
21846
  for (const p of specPaths) {
21676
21847
  if (!(0, import_node_fs37.existsSync)(p)) continue;
21677
21848
  try {
21678
- const { readFileSync: readFileSync24 } = await import("node:fs");
21679
- const content = readFileSync24(p, "utf-8");
21849
+ const { readFileSync: readFileSync25 } = await import("node:fs");
21850
+ const content = readFileSync25(p, "utf-8");
21680
21851
  specs.push({ path: p, content: content.slice(0, 10240) });
21681
21852
  } catch {
21682
21853
  }
@@ -22265,22 +22436,297 @@ function registerWaiveCommand(program2) {
22265
22436
  }
22266
22437
 
22267
22438
  // src/commands/init.ts
22268
- var import_node_fs41 = require("node:fs");
22269
- var import_promises13 = require("node:fs/promises");
22439
+ var import_node_fs44 = require("node:fs");
22440
+ var import_promises14 = require("node:fs/promises");
22270
22441
  var import_node_path29 = require("node:path");
22271
- var import_node_child_process11 = require("node:child_process");
22272
- var readline2 = __toESM(require("node:readline/promises"));
22442
+ var import_node_child_process13 = require("node:child_process");
22273
22443
 
22274
- // src/commands/migrate.ts
22275
- var import_node_fs40 = require("node:fs");
22276
- var import_node_path28 = require("node:path");
22444
+ // src/lib/banner.ts
22445
+ var WORDMARK = [
22446
+ "\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557",
22447
+ "\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D",
22448
+ "\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2554\u255D ",
22449
+ "\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2554\u255D ",
22450
+ " \u255A\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 ",
22451
+ " \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D "
22452
+ ];
22453
+ var WORDMARK_NARROW = [
22454
+ "\u2588 \u2588 \u2588\u2580\u2580\u2580\u2580 \u2588\u2580\u2580\u2580\u2584 \u2580\u2580\u2588\u2580\u2580 \u2580\u2580\u2588\u2580\u2580 \u2588 \u2588",
22455
+ "\u2588 \u2588 \u2588\u2580\u2580\u2580 \u2588\u2584\u2584\u2584\u2580 \u2588 \u2588 \u2580\u2584\u2580 ",
22456
+ " \u2580\u2584\u2580 \u2588\u2584\u2584\u2584\u2584 \u2588 \u2580\u2584 \u2584\u2584\u2588\u2584\u2584 \u2588 \u2588 "
22457
+ ];
22458
+ var TAGLINE = "reviews every turn, remembers every lesson";
22459
+ var WORDMARK_COLOR = ["\x1B[0;32m"];
22460
+ var DIM3 = "\x1B[2m";
22461
+ var RESET2 = "\x1B[0m";
22462
+ var INDENT = " ";
22463
+ function artWidth(art) {
22464
+ return Math.max(...art.map((r) => r.length)) + INDENT.length;
22465
+ }
22466
+ function canRenderArt() {
22467
+ return !!process.stderr.isTTY;
22468
+ }
22469
+ function printBanner(opts) {
22470
+ if (!opts.interactive) return;
22471
+ const columns = opts.columns ?? process.stderr.columns ?? 80;
22472
+ const color = colorEnabled();
22473
+ const version = `v${cliVersion()}`;
22474
+ const art = [WORDMARK, WORDMARK_NARROW].find((a) => artWidth(a) <= columns) ?? null;
22475
+ const dim = (text) => color ? `${DIM3}${text}${RESET2}` : text;
22476
+ process.stderr.write("\n");
22477
+ if (!art) {
22478
+ const candidates = [`Verity ${version}`, "Verity"];
22479
+ const text = candidates.find((c) => c.length + INDENT.length <= columns);
22480
+ if (text) process.stderr.write(`${INDENT}${dim(text)}
22481
+ `);
22482
+ process.stderr.write("\n");
22483
+ return;
22484
+ }
22485
+ art.forEach((row2, i) => {
22486
+ const tint = color && WORDMARK_COLOR.length > 0 ? WORDMARK_COLOR[Math.min(i, WORDMARK_COLOR.length - 1)] : "";
22487
+ const reset = color && tint ? RESET2 : "";
22488
+ process.stderr.write(`${INDENT}${tint}${row2}${reset}
22489
+ `);
22490
+ });
22491
+ if (TAGLINE.length + INDENT.length <= columns) {
22492
+ process.stderr.write(`${INDENT}${dim(TAGLINE)}
22493
+ `);
22494
+ }
22495
+ if (version.length + INDENT.length <= columns) {
22496
+ process.stderr.write(`${INDENT}${dim(version)}
22497
+ `);
22498
+ }
22499
+ process.stderr.write("\n");
22500
+ }
22501
+ function printPhase(n, of, title, subtitle) {
22502
+ const color = colorEnabled();
22503
+ const columns = process.stderr.columns ?? 72;
22504
+ const width = Math.min(columns, 72);
22505
+ const label2 = `\u2500\u2500 Phase ${n} of ${of} \xB7 ${title} `;
22506
+ const rule = label2 + "\u2500".repeat(Math.max(0, width - label2.length - 2));
22507
+ process.stderr.write("\n");
22508
+ process.stderr.write(color ? ` ${DIM3}${rule}${RESET2}
22509
+ ` : ` ${rule}
22510
+ `);
22511
+ if (subtitle && subtitle.length + INDENT.length <= columns) {
22512
+ process.stderr.write(color ? ` ${DIM3}${subtitle}${RESET2}
22513
+ ` : ` ${subtitle}
22514
+ `);
22515
+ }
22516
+ process.stderr.write("\n");
22517
+ }
22518
+
22519
+ // src/commands/doctor.ts
22520
+ var import_node_fs42 = require("node:fs");
22521
+
22522
+ // src/lib/prereqs.ts
22277
22523
  var import_node_child_process10 = require("node:child_process");
22524
+ var MIN_NODE_MAJOR = 20;
22525
+ function which(bin) {
22526
+ try {
22527
+ const out = (0, import_node_child_process10.execSync)(`command -v ${bin}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
22528
+ return out || null;
22529
+ } catch {
22530
+ return null;
22531
+ }
22532
+ }
22533
+ function checkNode() {
22534
+ const version = process.version;
22535
+ const major = Number.parseInt(version.slice(1), 10);
22536
+ const ok = Number.isFinite(major) && major >= MIN_NODE_MAJOR;
22537
+ return {
22538
+ id: "node",
22539
+ label: "Node.js",
22540
+ status: ok ? "ok" : "missing",
22541
+ detail: version,
22542
+ remedy: ok ? void 0 : `Node.js ${MIN_NODE_MAJOR}+ required \u2014 update from https://nodejs.org`,
22543
+ required: true
22544
+ };
22545
+ }
22546
+ function checkGit() {
22547
+ let detail = "";
22548
+ try {
22549
+ detail = (0, import_node_child_process10.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
22550
+ } catch {
22551
+ return {
22552
+ id: "git",
22553
+ label: "git",
22554
+ status: "missing",
22555
+ detail: "not found",
22556
+ remedy: "Install git from https://git-scm.com",
22557
+ required: true
22558
+ };
22559
+ }
22560
+ return { id: "git", label: "git", status: "ok", detail, required: true };
22561
+ }
22562
+ function checkClaude() {
22563
+ const path = which("claude");
22564
+ return {
22565
+ id: "claude",
22566
+ label: "Claude Code",
22567
+ status: path ? "ok" : "warn",
22568
+ detail: path ?? "not found",
22569
+ remedy: path ? void 0 : "Hooks are wired but need Claude Code to fire \u2014 https://claude.com/claude-code",
22570
+ required: false
22571
+ };
22572
+ }
22573
+ function checkAnalysisCli() {
22574
+ const path = which("codacy-analysis");
22575
+ return {
22576
+ id: "analysis-cli",
22577
+ label: "@codacy/analysis-cli",
22578
+ status: path ? "ok" : "warn",
22579
+ detail: path ?? "not found",
22580
+ remedy: path ? void 0 : "npm install -g @codacy/analysis-cli (static findings are unavailable until then)",
22581
+ required: false
22582
+ };
22583
+ }
22584
+ var INSTALL_TIMEOUT_MS = 12e4;
22585
+ function run(command, args, opts = {}) {
22586
+ return new Promise((resolve4) => {
22587
+ const child = (0, import_node_child_process10.spawn)(command, args, {
22588
+ stdio: opts.inherit ? "inherit" : "pipe",
22589
+ timeout: INSTALL_TIMEOUT_MS
22590
+ });
22591
+ child.on("error", () => resolve4(false));
22592
+ child.on("close", (code) => resolve4(code === 0));
22593
+ });
22594
+ }
22595
+ async function installAnalysisCli() {
22596
+ const spinner = startSpinner("Installing @codacy/analysis-cli");
22597
+ const ok = await run("npm", ["install", "-g", "@codacy/analysis-cli"]);
22598
+ if (ok) {
22599
+ const check = checkAnalysisCli();
22600
+ if (check.status === "ok") {
22601
+ spinner.succeed("@codacy/analysis-cli installed");
22602
+ return { ...check, justInstalled: true };
22603
+ }
22604
+ spinner.warn("npm reported success but codacy-analysis is not on PATH");
22605
+ return check;
22606
+ }
22607
+ spinner.stop();
22608
+ {
22609
+ const failed = {
22610
+ id: "analysis-cli",
22611
+ label: "@codacy/analysis-cli",
22612
+ status: "warn",
22613
+ detail: "install failed",
22614
+ remedy: "Install manually: npm install -g @codacy/analysis-cli",
22615
+ required: false
22616
+ };
22617
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return failed;
22618
+ process.stderr.write(" Retrying with sudo (you may be asked for your password)\u2026\n");
22619
+ const sudoOk = await run("sudo", ["npm", "install", "-g", "@codacy/analysis-cli"], { inherit: true });
22620
+ if (!sudoOk) return failed;
22621
+ const check = checkAnalysisCli();
22622
+ return check.status === "ok" ? { ...check, justInstalled: true } : check;
22623
+ }
22624
+ }
22625
+ async function checkPrereqs(opts = {}) {
22626
+ const checks = [checkNode(), checkGit(), checkClaude()];
22627
+ let analysis = checkAnalysisCli();
22628
+ if (analysis.status !== "ok" && opts.install) {
22629
+ analysis = await installAnalysisCli();
22630
+ }
22631
+ checks.push(analysis);
22632
+ return { checks, blocked: checks.some((c) => c.required && c.status !== "ok") };
22633
+ }
22278
22634
 
22279
22635
  // src/lib/telemetry.ts
22280
22636
  var import_promises12 = require("node:fs/promises");
22637
+
22638
+ // src/lib/gitignore.ts
22639
+ var import_node_child_process11 = require("node:child_process");
22640
+ var import_node_fs40 = require("node:fs");
22641
+ var VERITY_GITIGNORE_MARKER = "# Verity \u2014 machine-local state.";
22642
+ var SETTINGS_LOCAL_IGNORE_ENTRY = ".claude/settings.local.json";
22643
+ var VERITY_GITIGNORE_BLOCK = [
22644
+ "# Verity \u2014 machine-local state. Everything in .verity/ is ignored EXCEPT the",
22645
+ "# shared standard and the knowledge graph, which are meant to be committed.",
22646
+ ".verity/*",
22647
+ "!.verity/standard.yaml",
22648
+ "!.verity/memory/",
22649
+ ".verity/memory/log.md",
22650
+ SETTINGS_LOCAL_IGNORE_ENTRY,
22651
+ ""
22652
+ ].join("\n");
22653
+ var BREAKING_ENTRIES = /* @__PURE__ */ new Set([".verity/", ".verity"]);
22654
+ function isIgnored2(path) {
22655
+ try {
22656
+ (0, import_node_child_process11.execSync)(`git check-ignore -q -- "${path}"`, { stdio: "pipe" });
22657
+ return true;
22658
+ } catch (err) {
22659
+ return err.status === 1 ? false : null;
22660
+ }
22661
+ }
22662
+ function semanticsHold() {
22663
+ const snapshot = isIgnored2(".verity/.snapshot/__probe__");
22664
+ const standard = isIgnored2(".verity/standard.yaml");
22665
+ const node = isIgnored2(".verity/memory/domain/__probe__.md");
22666
+ const futureState = isIgnored2(".verity/.__probe-future-state__");
22667
+ if (snapshot === null || standard === null || node === null || futureState === null) return null;
22668
+ return snapshot === true && futureState === true && standard === false && node === false;
22669
+ }
22670
+ function ensureVerityGitignore() {
22671
+ let content = "";
22672
+ try {
22673
+ content = (0, import_node_fs40.readFileSync)(".gitignore", "utf-8");
22674
+ } catch {
22675
+ }
22676
+ const hasMarker = content.includes(VERITY_GITIGNORE_MARKER);
22677
+ const lines = content.split("\n");
22678
+ const breakingCount = lines.filter((l) => BREAKING_ENTRIES.has(l.trim())).length;
22679
+ const needsRepair = breakingCount > 0;
22680
+ const verified = (result) => semanticsHold() === false ? "conflict" : result;
22681
+ if (hasMarker && !needsRepair) return verified("covered");
22682
+ if (!hasMarker && !needsRepair) {
22683
+ if (semanticsHold() === true) return "covered";
22684
+ }
22685
+ try {
22686
+ let next = content;
22687
+ if (needsRepair) {
22688
+ next = lines.map((l) => BREAKING_ENTRIES.has(l.trim()) ? ".verity/*" : l).join("\n");
22689
+ }
22690
+ if (!hasMarker) {
22691
+ const sep2 = next === "" ? "" : next.endsWith("\n") ? "\n" : "\n\n";
22692
+ next = next + sep2 + VERITY_GITIGNORE_BLOCK;
22693
+ }
22694
+ (0, import_node_fs40.writeFileSync)(".gitignore", next);
22695
+ return verified(needsRepair ? "repaired" : "added");
22696
+ } catch {
22697
+ return "failed";
22698
+ }
22699
+ }
22700
+ function committedVerityState() {
22701
+ let out = "";
22702
+ try {
22703
+ out = (0, import_node_child_process11.execSync)("git ls-files -z -- .verity", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
22704
+ } catch {
22705
+ return [];
22706
+ }
22707
+ return out.split("\0").filter(Boolean).filter((p) => p !== ".verity/standard.yaml" && !(p.startsWith(".verity/memory/") && p !== ".verity/memory/log.md"));
22708
+ }
22709
+ function untrackVerityState() {
22710
+ const tracked = committedVerityState();
22711
+ if (tracked.length === 0) return "none";
22712
+ try {
22713
+ (0, import_node_child_process11.execSync)("git rm -r --cached --quiet -- .verity", { stdio: "pipe" });
22714
+ for (const keep of [".verity/standard.yaml", ".verity/memory"]) {
22715
+ try {
22716
+ (0, import_node_child_process11.execSync)(`git add -- "${keep}"`, { stdio: "pipe" });
22717
+ } catch {
22718
+ }
22719
+ }
22720
+ return "untracked";
22721
+ } catch {
22722
+ return "failed";
22723
+ }
22724
+ }
22725
+
22726
+ // src/lib/telemetry.ts
22281
22727
  var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
22282
22728
  var GITIGNORE_FILE = ".gitignore";
22283
- var GITIGNORE_ENTRY = ".claude/settings.local.json";
22729
+ var GITIGNORE_ENTRY = SETTINGS_LOCAL_IGNORE_ENTRY;
22284
22730
  var OTEL_HEADERS_HELPER_CMD = "verity telemetry headers";
22285
22731
  var LEGACY_TELEMETRY_ENV_KEYS = ["OTEL_EXPORTER_OTLP_HEADERS"];
22286
22732
  function deriveOtlpEndpoint(serviceUrl) {
@@ -22366,14 +22812,119 @@ async function uninstallTelemetry() {
22366
22812
  return { ok: true, data: { removed } };
22367
22813
  }
22368
22814
 
22815
+ // src/lib/setup-state.ts
22816
+ var import_promises13 = require("node:fs/promises");
22817
+ var import_node_fs41 = require("node:fs");
22818
+ var SETUP_STATE_FILE = `${VERITY_DIR}/setup.json`;
22819
+ async function readSetupState() {
22820
+ const path = projectPath(SETUP_STATE_FILE);
22821
+ if (!(0, import_node_fs41.existsSync)(path)) return null;
22822
+ try {
22823
+ const parsed = JSON.parse(await (0, import_promises13.readFile)(path, "utf-8"));
22824
+ return parsed && typeof parsed === "object" ? parsed : null;
22825
+ } catch {
22826
+ return null;
22827
+ }
22828
+ }
22829
+ async function writeSetupState(patch) {
22830
+ const current = await readSetupState() ?? { version: 1 };
22831
+ const next = { ...current, ...patch, version: 1 };
22832
+ await writeJsonFilePreservingStyle(projectPath(SETUP_STATE_FILE), next);
22833
+ return next;
22834
+ }
22835
+
22836
+ // src/commands/doctor.ts
22837
+ async function buildReport() {
22838
+ const prereqs = await checkPrereqs({ install: false });
22839
+ const state = await readSetupState();
22840
+ const hooks = await checkAllVerityHooks();
22841
+ const telemetry = await checkTelemetry();
22842
+ const artifacts = {
22843
+ standard: (0, import_node_fs42.existsSync)(projectPath(STANDARD_FILE)),
22844
+ analysisConfig: (0, import_node_fs42.existsSync)(projectPath(CODACY_CONFIG_FILE)),
22845
+ verityMd: (0, import_node_fs42.existsSync)(projectPath(VERITY_MD_FILE))
22846
+ };
22847
+ const next = [];
22848
+ for (const c of prereqs.checks) {
22849
+ if (c.status !== "ok" && c.remedy) next.push(c.remedy);
22850
+ }
22851
+ if (!state?.init) next.push('Run "verity init" \u2014 the deterministic setup phase has not completed here.');
22852
+ if (!artifacts.standard || !artifacts.analysisConfig || !artifacts.verityMd) {
22853
+ next.push("Run /verity-setup in Claude Code \u2014 the Standard, analysis config, and VERITY.md are synthesized there.");
22854
+ }
22855
+ const noAnalysisMoment = !hooks.stop && hooks.guardOn.length === 0;
22856
+ if (noAnalysisMoment) {
22857
+ next.push('No analysis moment is active \u2014 run "verity hooks install --moments stop" or re-run "verity init".');
22858
+ }
22859
+ if (state?.telemetry === "deferred") {
22860
+ next.push('Telemetry was requested but needs a token \u2014 run "verity login", then "verity telemetry install".');
22861
+ }
22862
+ return {
22863
+ prerequisites: prereqs.checks,
22864
+ blocked: prereqs.blocked,
22865
+ phases: {
22866
+ init: { done: !!state?.init, ...state?.init ?? {} },
22867
+ setup: { done: artifacts.standard && artifacts.analysisConfig && artifacts.verityMd }
22868
+ },
22869
+ answers: {
22870
+ intensity: state?.intensity ?? null,
22871
+ moments: state?.moments ?? null,
22872
+ telemetry: state?.telemetry ?? null
22873
+ },
22874
+ hooks: { ...hooks, noAnalysisMoment },
22875
+ telemetry: { enabled: telemetry.enabled, endpoint: telemetry.endpoint },
22876
+ artifacts,
22877
+ next
22878
+ };
22879
+ }
22880
+ function registerDoctorCommand(program2) {
22881
+ program2.command("doctor").description("Report prerequisites, setup phase, hooks, and what is still missing").option("--json", "Output the full report as JSON (what /verity-setup reads)").action(async (opts) => {
22882
+ const report = await buildReport();
22883
+ if (opts.json) {
22884
+ printJson(report);
22885
+ if (report.blocked) process.exit(1);
22886
+ return;
22887
+ }
22888
+ printInfo("Prerequisites:");
22889
+ for (const c of report.prerequisites) {
22890
+ if (c.status === "ok") printInfo(` ${c.label} ${c.detail} \u2713`);
22891
+ else printWarn(` ${c.label}: ${c.detail}${c.remedy ? ` \u2014 ${c.remedy}` : ""}`);
22892
+ }
22893
+ printInfo("Setup:");
22894
+ printInfo(` verity init: ${report.phases.init.done ? `done ${report.phases.init.completed_at ?? ""}`.trim() : "NOT run here"}`);
22895
+ printInfo(` /verity-setup: ${report.phases.setup.done ? "done" : "not completed"}`);
22896
+ printInfo(` intensity: ${report.answers.intensity ?? "\u2014"} moments: ${report.answers.moments?.join(", ") ?? "\u2014"}`);
22897
+ printInfo("Hooks:");
22898
+ printInfo(` Stop (verity analyze): ${report.hooks.stop ? "on" : "off"}`);
22899
+ printInfo(` Git-moment gate: ${report.hooks.guardOn.length ? report.hooks.guardOn.join(", ") : "off"}`);
22900
+ printInfo(` Infra (intent/baseline/compact/session-end): ${[report.hooks.intent, report.hooks.baseline, report.hooks.compact, report.hooks.sessionEnd].filter(Boolean).length}/4`);
22901
+ printInfo(`Telemetry: ${report.telemetry.enabled ? `enabled \u2192 ${report.telemetry.endpoint}` : "disabled"}`);
22902
+ printInfo("Artifacts:");
22903
+ printInfo(` .verity/standard.yaml: ${report.artifacts.standard ? "\u2713" : "missing"}`);
22904
+ printInfo(` .codacy/codacy.config.json: ${report.artifacts.analysisConfig ? "\u2713" : "missing"}`);
22905
+ printInfo(` VERITY.md: ${report.artifacts.verityMd ? "\u2713" : "missing"}`);
22906
+ if (report.next.length > 0) {
22907
+ console.log("");
22908
+ printWarn("Next:");
22909
+ for (const n of report.next) printWarn(` - ${n}`);
22910
+ } else {
22911
+ printInfo("Setup is complete.");
22912
+ }
22913
+ if (report.blocked) process.exit(1);
22914
+ });
22915
+ }
22916
+
22369
22917
  // src/commands/migrate.ts
22918
+ var import_node_fs43 = require("node:fs");
22919
+ var import_node_path28 = require("node:path");
22920
+ var import_node_child_process12 = require("node:child_process");
22370
22921
  var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
22371
22922
  function defaultNpmRemover(pkg) {
22372
- (0, import_node_child_process10.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
22923
+ (0, import_node_child_process12.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
22373
22924
  }
22374
22925
  function isGitTracked(cwd, relPath) {
22375
22926
  try {
22376
- (0, import_node_child_process10.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
22927
+ (0, import_node_child_process12.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
22377
22928
  return true;
22378
22929
  } catch {
22379
22930
  return false;
@@ -22381,7 +22932,7 @@ function isGitTracked(cwd, relPath) {
22381
22932
  }
22382
22933
  function isGitRepo(cwd) {
22383
22934
  try {
22384
- (0, import_node_child_process10.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
22935
+ (0, import_node_child_process12.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
22385
22936
  return true;
22386
22937
  } catch {
22387
22938
  return false;
@@ -22404,10 +22955,10 @@ async function runMigration(opts = {}) {
22404
22955
  function migrateProjectDir(root, actions) {
22405
22956
  const gateDir = (0, import_node_path28.join)(root, ".gate");
22406
22957
  const verityDir = (0, import_node_path28.join)(root, ".verity");
22407
- if ((0, import_node_fs40.existsSync)(gateDir) && !(0, import_node_fs40.existsSync)(verityDir)) {
22958
+ if ((0, import_node_fs43.existsSync)(gateDir) && !(0, import_node_fs43.existsSync)(verityDir)) {
22408
22959
  return migrateProjectDirRename(root, gateDir, verityDir, actions);
22409
22960
  }
22410
- if ((0, import_node_fs40.existsSync)(gateDir) && (0, import_node_fs40.existsSync)(verityDir)) {
22961
+ if ((0, import_node_fs43.existsSync)(gateDir) && (0, import_node_fs43.existsSync)(verityDir)) {
22411
22962
  return migrateProjectDirCarry(gateDir, verityDir, actions);
22412
22963
  }
22413
22964
  return false;
@@ -22421,20 +22972,20 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
22421
22972
  );
22422
22973
  }
22423
22974
  try {
22424
- (0, import_node_child_process10.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
22975
+ (0, import_node_child_process12.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
22425
22976
  actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
22426
22977
  moved = true;
22427
22978
  } catch {
22428
22979
  }
22429
22980
  }
22430
22981
  if (moved) {
22431
- if ((0, import_node_fs40.existsSync)(gateDir)) {
22982
+ if ((0, import_node_fs43.existsSync)(gateDir)) {
22432
22983
  const carried = carryLegacyContents(gateDir, verityDir);
22433
22984
  if (carried > 0) {
22434
22985
  actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
22435
22986
  }
22436
22987
  try {
22437
- (0, import_node_fs40.rmSync)(gateDir, { recursive: true, force: true });
22988
+ (0, import_node_fs43.rmSync)(gateDir, { recursive: true, force: true });
22438
22989
  } catch {
22439
22990
  }
22440
22991
  }
@@ -22450,7 +23001,7 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
22450
23001
  actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
22451
23002
  }
22452
23003
  try {
22453
- (0, import_node_fs40.rmSync)(gateDir, { recursive: true, force: true });
23004
+ (0, import_node_fs43.rmSync)(gateDir, { recursive: true, force: true });
22454
23005
  } catch {
22455
23006
  }
22456
23007
  return carried > 0;
@@ -22459,9 +23010,9 @@ function migrateGlobalCredentials(home, actions) {
22459
23010
  if (!home) return;
22460
23011
  const gateCreds = (0, import_node_path28.join)(home, ".gate", "credentials");
22461
23012
  const verityCreds = (0, import_node_path28.join)(home, ".verity", "credentials");
22462
- if (!(0, import_node_fs40.existsSync)(gateCreds)) return;
22463
- if (!(0, import_node_fs40.existsSync)(verityCreds)) {
22464
- (0, import_node_fs40.mkdirSync)((0, import_node_path28.join)(home, ".verity"), { recursive: true });
23013
+ if (!(0, import_node_fs43.existsSync)(gateCreds)) return;
23014
+ if (!(0, import_node_fs43.existsSync)(verityCreds)) {
23015
+ (0, import_node_fs43.mkdirSync)((0, import_node_path28.join)(home, ".verity"), { recursive: true });
22465
23016
  moveFile(gateCreds, verityCreds);
22466
23017
  actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
22467
23018
  return;
@@ -22484,7 +23035,7 @@ async function migrateLegacyHooks(root, actions) {
22484
23035
  }
22485
23036
  async function migrateClaudeMd(root, actions) {
22486
23037
  const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
22487
- const hadLegacyBlock = (0, import_node_fs40.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
23038
+ const hadLegacyBlock = (0, import_node_fs43.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
22488
23039
  if (!hadLegacyBlock) return;
22489
23040
  try {
22490
23041
  await ensureClaudeMdPointer(root);
@@ -22496,11 +23047,11 @@ async function migrateClaudeMd(root, actions) {
22496
23047
  function migrateStandardFile(root, actions) {
22497
23048
  const gateMd = (0, import_node_path28.join)(root, "GATE.md");
22498
23049
  const verityMd = (0, import_node_path28.join)(root, "VERITY.md");
22499
- if (!(0, import_node_fs40.existsSync)(gateMd) || (0, import_node_fs40.existsSync)(verityMd)) return;
23050
+ if (!(0, import_node_fs43.existsSync)(gateMd) || (0, import_node_fs43.existsSync)(verityMd)) return;
22500
23051
  let moved = false;
22501
23052
  if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
22502
23053
  try {
22503
- (0, import_node_child_process10.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
23054
+ (0, import_node_child_process12.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
22504
23055
  moved = true;
22505
23056
  } catch {
22506
23057
  }
@@ -22508,12 +23059,12 @@ function migrateStandardFile(root, actions) {
22508
23059
  if (!moved) moveFile(gateMd, verityMd);
22509
23060
  const content = readFileSyncSafe(verityMd);
22510
23061
  const refreshed = content.split("GATE.md").join("VERITY.md");
22511
- if (refreshed !== content) (0, import_node_fs40.writeFileSync)(verityMd, refreshed);
23062
+ if (refreshed !== content) (0, import_node_fs43.writeFileSync)(verityMd, refreshed);
22512
23063
  actions.push("Renamed GATE.md \u2192 VERITY.md");
22513
23064
  }
22514
23065
  async function migrateTelemetryHeaders(root, actions) {
22515
23066
  const file = (0, import_node_path28.join)(root, ".claude", "settings.local.json");
22516
- if (!(0, import_node_fs40.existsSync)(file)) return;
23067
+ if (!(0, import_node_fs43.existsSync)(file)) return;
22517
23068
  let settings;
22518
23069
  try {
22519
23070
  settings = JSON.parse(readFileSyncSafe(file) || "{}");
@@ -22561,21 +23112,21 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
22561
23112
  }
22562
23113
  if (toAppend.length > 0) {
22563
23114
  const sep2 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
22564
- (0, import_node_fs40.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
23115
+ (0, import_node_fs43.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
22565
23116
  }
22566
- (0, import_node_fs40.rmSync)(gateCreds, { force: true });
23117
+ (0, import_node_fs43.rmSync)(gateCreds, { force: true });
22567
23118
  return toAppend.length;
22568
23119
  }
22569
23120
  function readFileSyncSafe(path) {
22570
23121
  try {
22571
- return (0, import_node_fs40.readFileSync)(path, "utf-8");
23122
+ return (0, import_node_fs43.readFileSync)(path, "utf-8");
22572
23123
  } catch {
22573
23124
  return "";
22574
23125
  }
22575
23126
  }
22576
23127
  function hasStagedChanges(root) {
22577
23128
  try {
22578
- (0, import_node_child_process10.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
23129
+ (0, import_node_child_process12.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
22579
23130
  return false;
22580
23131
  } catch {
22581
23132
  return true;
@@ -22583,35 +23134,35 @@ function hasStagedChanges(root) {
22583
23134
  }
22584
23135
  function moveDir(from, to) {
22585
23136
  try {
22586
- (0, import_node_fs40.renameSync)(from, to);
23137
+ (0, import_node_fs43.renameSync)(from, to);
22587
23138
  } catch (err) {
22588
23139
  if (err.code !== "EXDEV") throw err;
22589
- (0, import_node_fs40.cpSync)(from, to, { recursive: true });
22590
- (0, import_node_fs40.rmSync)(from, { recursive: true, force: true });
23140
+ (0, import_node_fs43.cpSync)(from, to, { recursive: true });
23141
+ (0, import_node_fs43.rmSync)(from, { recursive: true, force: true });
22591
23142
  }
22592
23143
  }
22593
23144
  function moveFile(from, to) {
22594
23145
  try {
22595
- (0, import_node_fs40.renameSync)(from, to);
23146
+ (0, import_node_fs43.renameSync)(from, to);
22596
23147
  } catch (err) {
22597
23148
  if (err.code !== "EXDEV") throw err;
22598
- (0, import_node_fs40.cpSync)(from, to);
22599
- (0, import_node_fs40.rmSync)(from, { force: true });
23149
+ (0, import_node_fs43.cpSync)(from, to);
23150
+ (0, import_node_fs43.rmSync)(from, { force: true });
22600
23151
  }
22601
23152
  }
22602
23153
  function carryLegacyContents(gateDir, verityDir) {
22603
23154
  let copied = 0;
22604
23155
  const walk = (relDir) => {
22605
23156
  const srcDir = (0, import_node_path28.join)(gateDir, relDir);
22606
- for (const entry of (0, import_node_fs40.readdirSync)(srcDir)) {
23157
+ for (const entry of (0, import_node_fs43.readdirSync)(srcDir)) {
22607
23158
  const rel = relDir ? (0, import_node_path28.join)(relDir, entry) : entry;
22608
23159
  const src = (0, import_node_path28.join)(gateDir, rel);
22609
23160
  const dest = (0, import_node_path28.join)(verityDir, rel);
22610
- if ((0, import_node_fs40.statSync)(src).isDirectory()) {
23161
+ if ((0, import_node_fs43.statSync)(src).isDirectory()) {
22611
23162
  walk(rel);
22612
- } else if (!(0, import_node_fs40.existsSync)(dest)) {
22613
- (0, import_node_fs40.mkdirSync)((0, import_node_path28.dirname)(dest), { recursive: true });
22614
- (0, import_node_fs40.cpSync)(src, dest);
23163
+ } else if (!(0, import_node_fs43.existsSync)(dest)) {
23164
+ (0, import_node_fs43.mkdirSync)((0, import_node_path28.dirname)(dest), { recursive: true });
23165
+ (0, import_node_fs43.cpSync)(src, dest);
22615
23166
  copied++;
22616
23167
  }
22617
23168
  }
@@ -22622,20 +23173,20 @@ function carryLegacyContents(gateDir, verityDir) {
22622
23173
  async function needsMigration(root = repoRoot()) {
22623
23174
  const gateDir = (0, import_node_path28.join)(root, ".gate");
22624
23175
  const verityDir = (0, import_node_path28.join)(root, ".verity");
22625
- if ((0, import_node_fs40.existsSync)(gateDir) && !(0, import_node_fs40.existsSync)(verityDir)) return true;
22626
- if ((0, import_node_fs40.existsSync)(gateDir) && (0, import_node_fs40.existsSync)(verityDir)) {
22627
- if ((0, import_node_fs40.existsSync)((0, import_node_path28.join)(gateDir, "credentials")) && !(0, import_node_fs40.existsSync)((0, import_node_path28.join)(verityDir, "credentials"))) {
23176
+ if ((0, import_node_fs43.existsSync)(gateDir) && !(0, import_node_fs43.existsSync)(verityDir)) return true;
23177
+ if ((0, import_node_fs43.existsSync)(gateDir) && (0, import_node_fs43.existsSync)(verityDir)) {
23178
+ if ((0, import_node_fs43.existsSync)((0, import_node_path28.join)(gateDir, "credentials")) && !(0, import_node_fs43.existsSync)((0, import_node_path28.join)(verityDir, "credentials"))) {
22628
23179
  return true;
22629
23180
  }
22630
- if ((0, import_node_fs40.existsSync)((0, import_node_path28.join)(gateDir, "memory")) && !(0, import_node_fs40.existsSync)((0, import_node_path28.join)(verityDir, "memory"))) {
23181
+ if ((0, import_node_fs43.existsSync)((0, import_node_path28.join)(gateDir, "memory")) && !(0, import_node_fs43.existsSync)((0, import_node_path28.join)(verityDir, "memory"))) {
22631
23182
  return true;
22632
23183
  }
22633
23184
  }
22634
23185
  const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
22635
- if ((0, import_node_fs40.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
23186
+ if ((0, import_node_fs43.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
22636
23187
  return true;
22637
23188
  }
22638
- if ((0, import_node_fs40.existsSync)((0, import_node_path28.join)(root, "GATE.md")) && !(0, import_node_fs40.existsSync)((0, import_node_path28.join)(root, "VERITY.md"))) {
23189
+ if ((0, import_node_fs43.existsSync)((0, import_node_path28.join)(root, "GATE.md")) && !(0, import_node_fs43.existsSync)((0, import_node_path28.join)(root, "VERITY.md"))) {
22639
23190
  return true;
22640
23191
  }
22641
23192
  if (await hasLegacyHooksAt(root)) return true;
@@ -22660,17 +23211,96 @@ function registerMigrateCommand(program2) {
22660
23211
  });
22661
23212
  }
22662
23213
 
22663
- // src/commands/init.ts
22664
- async function promptYes(question) {
22665
- if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
22666
- const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
23214
+ // src/lib/prompt.ts
23215
+ var readline2 = __toESM(require("node:readline/promises"));
23216
+ function interactive() {
23217
+ return !!process.stdin.isTTY && !!process.stdout.isTTY;
23218
+ }
23219
+ async function askLine(question, io = {}) {
23220
+ const rl = readline2.createInterface({
23221
+ input: io.input ?? process.stdin,
23222
+ output: io.output ?? process.stdout
23223
+ });
22667
23224
  try {
22668
- const answer = (await rl.question(question)).trim().toLowerCase();
22669
- return answer === "" || answer === "y" || answer === "yes";
23225
+ return await new Promise((resolve4) => {
23226
+ rl.question(question).then((a) => resolve4(a.trim())).catch(() => resolve4(null));
23227
+ rl.once("close", () => setImmediate(() => resolve4(null)));
23228
+ });
22670
23229
  } finally {
22671
23230
  rl.close();
22672
23231
  }
22673
23232
  }
23233
+ var ask = (question) => askLine(question);
23234
+ function parseChoice(answer, choices, fallback) {
23235
+ const a = answer.trim().toLowerCase();
23236
+ if (a === "") return { id: fallback, recognized: true };
23237
+ const byNumber = Number.parseInt(a, 10);
23238
+ if (Number.isInteger(byNumber) && byNumber >= 1 && byNumber <= choices.length) {
23239
+ return { id: choices[byNumber - 1].id, recognized: true };
23240
+ }
23241
+ const byId = choices.find((c) => c.id.toLowerCase() === a || c.label.toLowerCase() === a);
23242
+ if (byId) return { id: byId.id, recognized: true };
23243
+ return { id: fallback, recognized: false };
23244
+ }
23245
+ function parseMultiSelect(answer, choices, fallback) {
23246
+ const a = answer.trim().toLowerCase();
23247
+ if (a === "") return { ids: [...fallback], recognized: true };
23248
+ const picked = /* @__PURE__ */ new Set();
23249
+ for (const part of a.split(",").map((s) => s.trim()).filter(Boolean)) {
23250
+ const n = Number.parseInt(part, 10);
23251
+ if (Number.isInteger(n) && n >= 1 && n <= choices.length) {
23252
+ picked.add(choices[n - 1].id);
23253
+ continue;
23254
+ }
23255
+ const byId = choices.find((c) => c.id.toLowerCase() === part || c.label.toLowerCase() === part);
23256
+ if (byId) picked.add(byId.id);
23257
+ }
23258
+ if (picked.size === 0) return { ids: [...fallback], recognized: false };
23259
+ return { ids: choices.filter((c) => picked.has(c.id)).map((c) => c.id), recognized: true };
23260
+ }
23261
+ async function promptYes(question, opts) {
23262
+ if (!interactive()) return opts.nonInteractive;
23263
+ const answer = await ask(question);
23264
+ if (answer === null) return opts.nonInteractive;
23265
+ const a = answer.toLowerCase();
23266
+ return a === "" || a === "y" || a === "yes";
23267
+ }
23268
+ function printOptions(question, choices) {
23269
+ console.log("");
23270
+ console.log(` ${question}`);
23271
+ choices.forEach((c, i) => {
23272
+ const tag = c.recommended ? " (recommended)" : "";
23273
+ console.log(` ${i + 1}. ${c.label}${tag}${c.hint ? ` \u2014 ${c.hint}` : ""}`);
23274
+ });
23275
+ }
23276
+ async function promptChoice(question, choices, fallback) {
23277
+ if (!interactive()) return fallback;
23278
+ printOptions(question, choices);
23279
+ const defaultIdx = choices.findIndex((c) => c.id === fallback);
23280
+ const answer = await ask(` Choose [${defaultIdx + 1}]: `);
23281
+ if (answer === null) {
23282
+ console.log(` (no answer \u2014 using ${fallback})`);
23283
+ return fallback;
23284
+ }
23285
+ const parsed = parseChoice(answer, choices, fallback);
23286
+ if (!parsed.recognized) console.log(` Unrecognized answer "${answer}" \u2014 using ${fallback}.`);
23287
+ return parsed.id;
23288
+ }
23289
+ async function promptMultiSelect(question, choices, fallback) {
23290
+ if (!interactive()) return [...fallback];
23291
+ printOptions(question, choices);
23292
+ const defaultLabel = choices.map((c, i) => fallback.includes(c.id) ? String(i + 1) : null).filter(Boolean).join(",");
23293
+ const answer = await ask(` Choose one or more, comma-separated [${defaultLabel}]: `);
23294
+ if (answer === null) {
23295
+ console.log(" (no answer \u2014 using the default)");
23296
+ return [...fallback];
23297
+ }
23298
+ const parsed = parseMultiSelect(answer, choices, fallback);
23299
+ if (!parsed.recognized) console.log(` Unrecognized answer "${answer}" \u2014 using the default.`);
23300
+ return parsed.ids;
23301
+ }
23302
+
23303
+ // src/commands/init.ts
22674
23304
  async function confirmExistingLogin(serviceUrl, remote, opts) {
22675
23305
  const existing = await resolveToken(opts.token);
22676
23306
  if (!existing.ok) return "drive-login";
@@ -22728,7 +23358,7 @@ async function runOptionalAuth(resolution, opts = {}) {
22728
23358
  }
22729
23359
  let remote = "";
22730
23360
  try {
22731
- remote = (0, import_node_child_process11.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
23361
+ remote = (0, import_node_child_process13.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
22732
23362
  } catch {
22733
23363
  }
22734
23364
  if (!healed) {
@@ -22739,7 +23369,7 @@ async function runOptionalAuth(resolution, opts = {}) {
22739
23369
  printInfo("Verity runs in local-only mode: the gate still runs and shows static findings, but nothing uploads.");
22740
23370
  printInfo(' Authenticate anytime: run "verity login" (one login covers every repo you can write to).');
22741
23371
  };
22742
- if (process.stdin.isTTY && process.stdout.isTTY) {
23372
+ if (interactive() && !opts.yes) {
22743
23373
  console.log("");
22744
23374
  console.log(" Signing in is optional. What it does:");
22745
23375
  console.log(" - Confirms which repositories you can write to. The GitHub token is");
@@ -22754,9 +23384,12 @@ async function runOptionalAuth(resolution, opts = {}) {
22754
23384
  console.log(" findings, but nothing is uploaded.");
22755
23385
  console.log("");
22756
23386
  }
22757
- const wantsAuth = await promptYes("Authenticate with GitHub now to upload results to Verity? [Y/skip] ");
23387
+ const wantsAuth = opts.yes ? false : await promptYes(
23388
+ "Authenticate with GitHub now to upload results to Verity? [Y/skip] ",
23389
+ { nonInteractive: false }
23390
+ );
22758
23391
  if (!wantsAuth) {
22759
- printInfo("Skipped authentication.");
23392
+ printInfo(opts.yes ? "Skipped authentication (unattended run)." : "Skipped authentication.");
22760
23393
  localOnlyNote();
22761
23394
  return;
22762
23395
  }
@@ -22779,7 +23412,7 @@ function resolveDataDir() {
22779
23412
  // local dev: running from repo root
22780
23413
  ];
22781
23414
  for (const candidate of candidates) {
22782
- if ((0, import_node_fs41.existsSync)((0, import_node_path29.join)(candidate, "skills"))) {
23415
+ if ((0, import_node_fs44.existsSync)((0, import_node_path29.join)(candidate, "skills"))) {
22783
23416
  return candidate;
22784
23417
  }
22785
23418
  }
@@ -22788,22 +23421,197 @@ function resolveDataDir() {
22788
23421
  );
22789
23422
  }
22790
23423
  async function copyDir(src, dest) {
22791
- await (0, import_promises13.mkdir)(dest, { recursive: true });
22792
- await (0, import_promises13.cp)(src, dest, { recursive: true, force: true });
23424
+ await (0, import_promises14.mkdir)(dest, { recursive: true });
23425
+ await (0, import_promises14.cp)(src, dest, { recursive: true, force: true });
23426
+ }
23427
+ async function skillIsCurrent(src, dest) {
23428
+ const list2 = (dir) => {
23429
+ const out = [];
23430
+ const walk = (d, prefix) => {
23431
+ for (const e of (0, import_node_fs44.readdirSync)(d, { withFileTypes: true })) {
23432
+ const rel = prefix ? `${prefix}/${e.name}` : e.name;
23433
+ if (e.isDirectory()) walk((0, import_node_path29.join)(d, e.name), rel);
23434
+ else if (e.isFile()) out.push(rel);
23435
+ }
23436
+ };
23437
+ walk(dir, "");
23438
+ return out.sort();
23439
+ };
23440
+ try {
23441
+ const shipped = list2(src);
23442
+ if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
23443
+ for (const rel of shipped) {
23444
+ const a = await (0, import_promises14.readFile)((0, import_node_path29.join)(src, rel), "utf-8");
23445
+ const b = await (0, import_promises14.readFile)((0, import_node_path29.join)(dest, rel), "utf-8");
23446
+ if (a !== b) return false;
23447
+ }
23448
+ return true;
23449
+ } catch {
23450
+ return false;
23451
+ }
23452
+ }
23453
+ var SKILLS = [
23454
+ "verity-setup",
23455
+ "verity-analyze",
23456
+ "verity-status",
23457
+ "verity-feedback",
23458
+ "verity-learn",
23459
+ "verity-memory",
23460
+ "verity-insights",
23461
+ "verity-reflect"
23462
+ ];
23463
+ var INTENSITY_CHOICES = [
23464
+ { id: "lightweight", label: "lightweight", hint: "critical security only, fastest (~3s)" },
23465
+ { id: "balanced", label: "balanced", hint: "security + quality (~8s)", recommended: true },
23466
+ { id: "thorough", label: "thorough", hint: "all tools, all rules (~15s)" }
23467
+ ];
23468
+ var MOMENT_CHOICES = [
23469
+ { id: "stop", label: "On stop", hint: "after every agent turn \u2014 fast feedback while you work", recommended: true },
23470
+ { id: "pre-commit", label: "Before commit", hint: "reviews the staged diff, blocks the commit on FAIL" },
23471
+ { id: "pre-push", label: "Before push / PR", hint: "reviews the to-be-pushed commits, blocks on FAIL" }
23472
+ ];
23473
+ var DEFAULT_MOMENTS = ["stop"];
23474
+ async function askSetupQuestions(defaultsOnly, previous) {
23475
+ const intensityDefault = previous?.intensity ?? "balanced";
23476
+ const momentsDefault = previous?.moments?.length ? previous.moments : DEFAULT_MOMENTS;
23477
+ if (defaultsOnly) {
23478
+ return { intensity: intensityDefault, moments: momentsDefault, telemetry: "not-asked" };
23479
+ }
23480
+ if (previous?.intensity || previous?.moments) {
23481
+ printInfo(`Current: ${intensityDefault} \xB7 ${momentsDefault.join(", ")} \u2014 press Enter to keep either.`);
23482
+ }
23483
+ const intensity = await promptChoice(
23484
+ "Analysis intensity \u2014 how deeply should Verity review?",
23485
+ INTENSITY_CHOICES,
23486
+ intensityDefault
23487
+ );
23488
+ const moments = await promptMultiSelect(
23489
+ "When should Verity review your code?",
23490
+ MOMENT_CHOICES,
23491
+ momentsDefault
23492
+ );
23493
+ const current = await checkTelemetry();
23494
+ if (current.enabled) {
23495
+ printInfo(`Cost & usage telemetry: already enabled \u2192 ${current.endpoint}`);
23496
+ printInfo(' (turn it off with "verity telemetry uninstall")');
23497
+ return { intensity, moments, telemetry: "already-on" };
23498
+ }
23499
+ console.log("");
23500
+ console.log(" Cost & usage telemetry (opt-in) \u2014 powers the /usage dashboard.");
23501
+ console.log(" Sends Claude Code's own OpenTelemetry metrics and traces only: model names,");
23502
+ console.log(" token counts, USD cost, agent types, session ids. NOT your prompts, code, or");
23503
+ console.log(" tool input/output. Without it, /usage stays empty.");
23504
+ const wants = await promptYes(" Enable cost & usage telemetry? [Y/n] ", { nonInteractive: false });
23505
+ return { intensity, moments, telemetry: wants ? "yes" : "no" };
23506
+ }
23507
+ function insideClaudeCode() {
23508
+ return !!process.env.CLAUDECODE || !!process.env.CLAUDE_SESSION_ID;
23509
+ }
23510
+ function resumeDeferredPending(previous) {
23511
+ return previous?.telemetry === "deferred";
23512
+ }
23513
+ var PHASE_TWO_ARTIFACTS = [
23514
+ {
23515
+ path: ".verity/standard.yaml",
23516
+ what: "the Standard, synthesized from your codebase",
23517
+ // The presence flag travels WITH the row. Read positionally from a parallel
23518
+ // array, a reorder of this list would silently move every ✓ onto the wrong
23519
+ // path — a report that is confidently wrong about what got created.
23520
+ present: (a) => a.standard
23521
+ },
23522
+ {
23523
+ path: ".codacy/codacy.config.json",
23524
+ what: "curated static-analysis patterns",
23525
+ present: (a) => a.analysisConfig
23526
+ },
23527
+ {
23528
+ path: "VERITY.md",
23529
+ what: "project quality overview",
23530
+ present: (a) => a.verityMd
23531
+ }
23532
+ ];
23533
+ async function reportPhaseTwo(startedAt) {
23534
+ const elapsed = Math.round((Date.now() - startedAt) / 1e3);
23535
+ let report = null;
23536
+ try {
23537
+ report = await buildReport();
23538
+ } catch {
23539
+ }
23540
+ console.log("");
23541
+ if (!report) {
23542
+ printWarn('Could not verify what the setup session produced \u2014 run "verity doctor".');
23543
+ return;
23544
+ }
23545
+ const { artifacts } = report;
23546
+ const complete = PHASE_TWO_ARTIFACTS.every((a) => a.present(artifacts));
23547
+ printInfo(complete ? `Setup complete (${elapsed}s).` : `Setup session ended after ${elapsed}s.`);
23548
+ for (const { path, what, present } of PHASE_TWO_ARTIFACTS) {
23549
+ const ok = present(artifacts);
23550
+ console.log(` ${ok ? "\u2713" : "\xB7"} ${path.padEnd(30)} ${ok ? what : `${what} \u2014 NOT created`}`);
23551
+ }
23552
+ if (!complete) {
23553
+ console.log("");
23554
+ printWarn('Setup did not finish. Re-run "/verity-setup" in Claude Code to complete it \u2014');
23555
+ printWarn(" nothing is lost; it picks up from what is already on disk.");
23556
+ }
23557
+ }
23558
+ async function handoffToSetup(enabled, claudeInstalled) {
23559
+ const instruct = (why) => {
23560
+ console.log("");
23561
+ printInfo("Still to do \u2014 this is what /verity-setup does (it needs a model):");
23562
+ for (const { path, what } of PHASE_TWO_ARTIFACTS) {
23563
+ console.log(` ${path.padEnd(30)} ${what}`);
23564
+ }
23565
+ console.log("");
23566
+ printInfo("Next step: run /verity-setup in Claude Code.");
23567
+ printInfo(` (${why})`);
23568
+ };
23569
+ if (!enabled) return instruct("--no-setup was passed");
23570
+ if (insideClaudeCode()) return instruct("you are already in a Claude Code session \u2014 invoke the skill there");
23571
+ if (!interactive()) return instruct("no interactive terminal here");
23572
+ if (!claudeInstalled) return instruct("Claude Code is not installed on this machine yet");
23573
+ printPhase(2, 2, "your Standard", "reading the codebase \xB7 synthesizing the Standard \xB7 curating patterns");
23574
+ console.log(" Claude Code takes over the screen from here. It will:");
23575
+ console.log(" \xB7 read your codebase \u2014 languages, frameworks, architecture");
23576
+ console.log(" \xB7 synthesize .verity/standard.yaml and show it to you");
23577
+ console.log(" \xB7 write the analysis config, then VERITY.md");
23578
+ console.log(" Usually a minute or two. Quit any time \u2014 re-running /verity-setup resumes.");
23579
+ console.log("");
23580
+ const startedAt = Date.now();
23581
+ const run2 = (0, import_node_child_process13.spawnSync)("claude", ["/verity-setup"], { stdio: "inherit" });
23582
+ if (run2.error) {
23583
+ printWarn(`Could not start Claude Code: ${run2.error.message}`);
23584
+ return instruct("start it yourself and run the skill there");
23585
+ }
23586
+ await reportPhaseTwo(startedAt);
22793
23587
  }
22794
23588
  function registerInitCommand(program2) {
22795
- program2.command("init").description("Initialize Verity in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
23589
+ program2.command("init").alias("setup").description("Set up Verity in the current project (asks the setup questions, then hands off to /verity-setup)").option("--force", "Reinstall the skills even when they are already up to date (hooks are always reconciled)").option("-y, --yes", "Take the recommended answer for every question (no prompts)").option("--no-setup", "Skip the /verity-setup handoff at the end").action(async (opts) => {
22796
23590
  const force = opts.force ?? false;
23591
+ const wantsHandoff = opts.setup !== false;
23592
+ const defaultsOnly = (opts.yes ?? false) || !interactive();
22797
23593
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
22798
- const isProject = projectMarkers.some((m) => (0, import_node_fs41.existsSync)(m));
23594
+ const isProject = projectMarkers.some((m) => (0, import_node_fs44.existsSync)(m));
22799
23595
  if (!isProject) {
22800
23596
  printError("No project detected in the current directory.");
22801
23597
  printInfo('Run "verity init" from your project root.');
22802
23598
  process.exit(1);
22803
23599
  }
22804
- console.log("");
22805
- printInfo("Initializing Verity in this project...");
22806
- console.log("");
23600
+ const showArt = canRenderArt() && !insideClaudeCode();
23601
+ printBanner({ interactive: showArt });
23602
+ if (!showArt) {
23603
+ console.log("");
23604
+ printInfo("Initializing Verity in this project...");
23605
+ console.log("");
23606
+ } else {
23607
+ printPhase(1, 2, "this machine", "prerequisites \xB7 skills \xB7 hooks \xB7 sign-in");
23608
+ }
23609
+ const TOTAL_STEPS = 8;
23610
+ let stepNo = 0;
23611
+ const step = (label2) => {
23612
+ stepNo++;
23613
+ printInfo(`${DIM}[${stepNo}/${TOTAL_STEPS}]${NC} ${label2}`);
23614
+ };
22807
23615
  if (await needsMigration()) {
22808
23616
  printInfo("Legacy GATE.md install detected \u2014 migrating to Verity...");
22809
23617
  try {
@@ -22814,143 +23622,171 @@ function registerInitCommand(program2) {
22814
23622
  }
22815
23623
  console.log("");
22816
23624
  }
22817
- printInfo("Checking prerequisites...");
22818
- const nodeVersion = process.version;
22819
- const nodeMajor = parseInt(nodeVersion.slice(1), 10);
22820
- if (nodeMajor < 20) {
22821
- printError(`Node.js 20+ required (found ${nodeVersion}). Update from https://nodejs.org`);
22822
- process.exit(1);
23625
+ step("Checking prerequisites");
23626
+ const prereqs = await checkPrereqs({ install: true });
23627
+ for (const c of prereqs.checks) {
23628
+ if (c.status === "ok") {
23629
+ if (c.justInstalled) continue;
23630
+ printInfo(` ${c.label} ${c.detail} \u2713`);
23631
+ } else {
23632
+ printWarn(` ${c.label}: ${c.detail}`);
23633
+ if (c.remedy) printWarn(` ${c.remedy}`);
23634
+ }
22823
23635
  }
22824
- printInfo(` Node.js ${nodeVersion} \u2713`);
22825
- try {
22826
- const gitVersion = (0, import_node_child_process11.execSync)("git --version", { encoding: "utf-8" }).trim();
22827
- printInfo(` ${gitVersion} \u2713`);
22828
- } catch {
22829
- printError("git is required but not installed. Install from https://git-scm.com");
23636
+ if (prereqs.blocked) {
23637
+ printError("A required prerequisite is missing \u2014 cannot continue.");
22830
23638
  process.exit(1);
22831
23639
  }
22832
- try {
22833
- (0, import_node_child_process11.execSync)("which claude", { encoding: "utf-8" });
22834
- printInfo(" Claude Code \u2713");
22835
- } catch {
22836
- printWarn(" Claude Code not found \u2014 hooks will be configured but need Claude Code to run.");
22837
- }
22838
- try {
22839
- (0, import_node_child_process11.execSync)("which codacy-analysis", { encoding: "utf-8", stdio: "pipe" });
22840
- printInfo(" @codacy/analysis-cli \u2713");
22841
- } catch {
22842
- printInfo(" Installing @codacy/analysis-cli...");
22843
- try {
22844
- (0, import_node_child_process11.execSync)("npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "pipe", timeout: 12e4 });
22845
- printInfo(" @codacy/analysis-cli installed \u2713");
22846
- } catch {
22847
- try {
22848
- printWarn(" Retrying with sudo...");
22849
- (0, import_node_child_process11.execSync)("sudo npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
22850
- printInfo(" @codacy/analysis-cli installed \u2713");
22851
- } catch {
22852
- printWarn(" Could not install @codacy/analysis-cli automatically.");
22853
- printWarn(" Install manually: npm install -g @codacy/analysis-cli");
22854
- printWarn(" Static analysis will be unavailable until installed.");
22855
- }
22856
- }
22857
- }
23640
+ const claudeInstalled = prereqs.checks.some((c) => c.id === "claude" && c.status === "ok");
22858
23641
  console.log("");
22859
- printInfo("Installing skills...");
23642
+ step("Installing skills");
22860
23643
  const dataDir = resolveDataDir();
22861
23644
  const skillsSource = (0, import_node_path29.join)(dataDir, "skills");
22862
23645
  const skillsDest = ".claude/skills";
22863
- const skills = ["verity-setup", "verity-analyze", "verity-status", "verity-feedback", "verity-learn", "verity-memory", "verity-insights", "verity-reflect"];
22864
23646
  let skillsInstalled = 0;
22865
- for (const skill of skills) {
23647
+ for (const skill of SKILLS) {
22866
23648
  const src = (0, import_node_path29.join)(skillsSource, skill);
22867
23649
  const dest = (0, import_node_path29.join)(skillsDest, skill);
22868
- if (!(0, import_node_fs41.existsSync)(src)) {
23650
+ if (!(0, import_node_fs44.existsSync)(src)) {
22869
23651
  printWarn(` Skill data not found: ${skill}`);
22870
23652
  continue;
22871
23653
  }
22872
- if ((0, import_node_fs41.existsSync)(dest) && !force) {
22873
- const srcSkill = (0, import_node_path29.join)(src, "SKILL.md");
22874
- const destSkill = (0, import_node_path29.join)(dest, "SKILL.md");
22875
- if ((0, import_node_fs41.existsSync)(destSkill)) {
22876
- try {
22877
- const srcContent = await (0, import_promises13.readFile)(srcSkill, "utf-8");
22878
- const destContent = await (0, import_promises13.readFile)(destSkill, "utf-8");
22879
- if (srcContent === destContent) {
22880
- skillsInstalled++;
22881
- continue;
22882
- }
22883
- } catch {
22884
- }
22885
- }
23654
+ if ((0, import_node_fs44.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) {
23655
+ skillsInstalled++;
23656
+ continue;
22886
23657
  }
22887
23658
  await copyDir(src, dest);
22888
23659
  skillsInstalled++;
22889
23660
  }
22890
- printInfo(` ${skillsInstalled}/${skills.length} skills installed to .claude/skills/ \u2713`);
22891
- printInfo("Wiring Claude Code hooks...");
22892
- const present = await checkAllVerityHooks();
22893
- const settings = await readSettings();
22894
- const hookResult = installVerityHooks(settings, force, present);
22895
- if (hookResult.ok) {
22896
- await writeSettings(hookResult.data);
22897
- printInfo(" Stop hook: verity analyze \u2713");
22898
- printInfo(" Intent hook: verity intent capture \u2713");
22899
- printInfo(" Baseline hook: verity baseline capture \u2713");
22900
- } else {
22901
- printWarn(` ${hookResult.error}`);
22902
- printInfo(' Run "verity hooks install --force" to overwrite.');
23661
+ printInfo(` ${skillsInstalled}/${SKILLS.length} skills installed to .claude/skills/ \u2713`);
23662
+ step(defaultsOnly ? "Setup answers (defaults)" : "Your setup answers");
23663
+ const previous = await readSetupState();
23664
+ const answers = await askSetupQuestions(defaultsOnly, previous);
23665
+ const { intensity, moments } = answers;
23666
+ if (defaultsOnly) {
23667
+ printInfo(` intensity: ${intensity} \xB7 moments: ${moments.join(", ") || "none"} (no questions asked)`);
22903
23668
  }
22904
- await (0, import_promises13.mkdir)(VERITY_DIR, { recursive: true });
23669
+ step("Knowledge base, .gitignore and CLAUDE.md");
23670
+ await (0, import_promises14.mkdir)(VERITY_DIR, { recursive: true });
22905
23671
  await ensureMemoryDir();
22906
- const ignoreResult = ensureSnapshotGitignored();
23672
+ const ignoreResult = ensureVerityGitignore();
22907
23673
  if (ignoreResult === "failed") {
22908
- printWarn(" .gitignore: could not add .verity/.snapshot/ \u2014 add it manually (it holds copies of analyzed files)");
23674
+ printWarn(" .gitignore: could not write the Verity block \u2014 add it manually");
23675
+ printWarn(" (.verity/ holds copies of analyzed files, including any secret the gate flagged)");
23676
+ } else if (ignoreResult === "conflict") {
23677
+ printWarn(" .gitignore: the Verity block is in place but git still ignores .verity/standard.yaml");
23678
+ printWarn(" Something outside this file covers it \u2014 a global (~/.gitignore) or nested");
23679
+ printWarn(" .gitignore, or a pattern we do not recognise. Check: git check-ignore -v .verity/standard.yaml");
23680
+ printWarn(" Until it is fixed, the Standard and the knowledge graph cannot be committed.");
23681
+ } else if (ignoreResult === "repaired") {
23682
+ printInfo(" .gitignore: rewrote `.verity/` to `.verity/*` so the standard stays committable \u2713");
22909
23683
  } else {
22910
- printInfo(` .gitignore: .verity/.snapshot/ ${ignoreResult === "added" ? "added" : "already covered"} \u2713`);
23684
+ printInfo(` .gitignore: Verity block ${ignoreResult === "added" ? "added" : "already covered"} \u2713`);
23685
+ }
23686
+ const tracked = committedVerityState();
23687
+ if (tracked.length > 0) {
23688
+ printWarn(` ${tracked.length} Verity state file(s) are tracked in git (e.g. ${tracked[0]}).`);
23689
+ const untrack = defaultsOnly ? false : await promptYes(" Untrack them now (files stay on disk)? [Y/n] ", { nonInteractive: false });
23690
+ if (untrack) {
23691
+ const result = untrackVerityState();
23692
+ if (result === "untracked") printInfo(" Untracked (staged) \u2014 commit to finish \u2713");
23693
+ else if (result === "failed") printWarn(' Could not untrack \u2014 run "git rm -r --cached .verity" manually');
23694
+ } else {
23695
+ printWarn(" Left tracked. Fix with: git rm -r --cached .verity && git add .verity/standard.yaml .verity/memory");
23696
+ }
22911
23697
  }
22912
23698
  try {
22913
23699
  await ensureClaudeMdPointer();
22914
- printInfo(" CLAUDE.md memory pointer \u2713");
23700
+ printInfo(" CLAUDE.md instructions \u2713");
22915
23701
  } catch (err) {
22916
23702
  printWarn(` Could not update CLAUDE.md: ${err.message}`);
22917
23703
  }
22918
23704
  const globalVerityDir = (0, import_node_path29.join)(process.env.HOME ?? "", ".verity");
22919
- await (0, import_promises13.mkdir)(globalVerityDir, { recursive: true });
23705
+ await (0, import_promises14.mkdir)(globalVerityDir, { recursive: true });
23706
+ console.log("");
23707
+ step("Wiring Claude Code hooks");
23708
+ await applyMomentSelection(moments);
23709
+ const hookStatus = await checkAllVerityHooks();
23710
+ printInfo(` Stop (verity analyze): ${hookStatus.stop ? "on" : "off"}`);
23711
+ printInfo(` Pre-commit gate: ${hookStatus.guardOn.includes("commit") ? "on" : "off"}`);
23712
+ printInfo(` Pre-push/PR gate: ${hookStatus.guardOn.includes("push") ? "on" : "off"}`);
23713
+ printInfo(` Intent + baseline + compact + session-end: always on \u2713`);
23714
+ if (!hookStatus.stop && hookStatus.guardOn.length === 0) {
23715
+ printWarn(" No analysis moment is active \u2014 code changes will NOT be reviewed.");
23716
+ printWarn(" Enable one: verity hooks install --moments stop");
23717
+ }
22920
23718
  console.log("");
23719
+ step("Sign in to Verity (optional)");
22921
23720
  try {
22922
23721
  const globals = program2.opts();
22923
23722
  const resolution = await resolveServiceUrlForAuth(globals.serviceUrl);
22924
23723
  await runOptionalAuth(resolution, {
22925
23724
  token: globals.token,
22926
- verbose: globals.verbose
23725
+ verbose: globals.verbose,
23726
+ yes: defaultsOnly
22927
23727
  });
22928
23728
  } catch (err) {
22929
23729
  printWarn(`Authentication step skipped: ${err.message}`);
22930
23730
  }
23731
+ const resumeDeferred = answers.telemetry === "not-asked" && resumeDeferredPending(previous);
23732
+ const wantsTelemetry = answers.telemetry === "yes" || resumeDeferred;
23733
+ step("Cost & usage telemetry");
23734
+ if (answers.telemetry === "not-asked" && !resumeDeferredPending(previous)) {
23735
+ printInfo(' not asked (unattended run) \u2014 enable later with "verity telemetry install"');
23736
+ } else if (answers.telemetry === "no") {
23737
+ printInfo(' declined \u2014 enable later with "verity telemetry install"');
23738
+ }
23739
+ let telemetryChoice = answers.telemetry === "already-on" ? "enabled" : answers.telemetry === "no" ? "declined" : wantsTelemetry ? "deferred" : void 0;
23740
+ if (wantsTelemetry) {
23741
+ const globals = program2.opts();
23742
+ const token = await resolveToken(globals.token);
23743
+ const url = await resolveServiceUrl(globals.serviceUrl);
23744
+ if (token.ok && url.ok) {
23745
+ const installed2 = await installTelemetry(url.data);
23746
+ if (installed2.ok) {
23747
+ telemetryChoice = "enabled";
23748
+ printInfo(`Telemetry enabled \u2192 ${installed2.data.endpoint}`);
23749
+ printInfo(" takes effect on your NEXT Claude Code session; view cost & usage at /usage");
23750
+ } else {
23751
+ printWarn(`Could not enable telemetry: ${installed2.error}`);
23752
+ }
23753
+ } else {
23754
+ printWarn('Telemetry needs a Verity token \u2014 run "verity login", then "verity telemetry install".');
23755
+ }
23756
+ }
23757
+ step("Recording your answers");
23758
+ try {
23759
+ await writeSetupState({
23760
+ intensity,
23761
+ moments,
23762
+ ...telemetryChoice ? { telemetry: telemetryChoice } : {},
23763
+ init: {
23764
+ completed_at: (/* @__PURE__ */ new Date()).toISOString(),
23765
+ cli_version: true ? "0.31.1-experimental.be74f71" : "dev"
23766
+ }
23767
+ });
23768
+ } catch (err) {
23769
+ printWarn(`Could not record setup answers: ${err.message}`);
23770
+ }
22931
23771
  console.log("");
22932
- printInfo("Verity initialized!");
23772
+ printInfo("This machine is set up.");
22933
23773
  console.log("");
22934
- console.log(" Installed:");
22935
- console.log(" .claude/skills/verity-setup/ \u2014 project configuration");
22936
- console.log(" .claude/skills/verity-analyze/ \u2014 on-demand analysis");
22937
- console.log(" .claude/skills/verity-status/ \u2014 project health");
22938
- console.log(" .claude/skills/verity-feedback/ \u2014 finding feedback + suppressions");
22939
- console.log(" .claude/skills/verity-learn/ \u2014 view project knowledge");
22940
- console.log(" .claude/skills/verity-memory/ \u2014 browse knowledge graph");
22941
- console.log(" .claude/skills/verity-insights/ \u2014 quality metrics + evolution");
22942
- console.log(" .claude/skills/verity-reflect/ \u2014 capture learnings");
22943
- console.log(" .claude/settings.json \u2014 hooks (verity analyze + intent capture)");
22944
- console.log(" .verity/memory/ \u2014 knowledge base (8 domains, commit to git)");
23774
+ console.log(" .claude/skills/verity-*/ 8 skills (setup, analyze, status, feedback,");
23775
+ console.log(" learn, memory, insights, reflect)");
23776
+ console.log(" .claude/settings.json hooks, reconciled to your chosen moments");
23777
+ console.log(" .verity/memory/ knowledge base (commit to git)");
23778
+ console.log(" .verity/setup.json your answers, read by /verity-setup");
23779
+ console.log(" .gitignore Verity block (whitelist form)");
23780
+ console.log(" CLAUDE.md memory pointer, waive policy, reflection");
22945
23781
  console.log("");
22946
- console.log(" Next step: open this project in Claude Code and run /verity-setup");
22947
- console.log(' (Not authenticated? Verity runs in local-only mode until you run "verity login".)');
23782
+ console.log(` Intensity: ${intensity} Moments: ${moments.join(", ") || "none"}`);
23783
+ await handoffToSetup(wantsHandoff, claudeInstalled);
22948
23784
  console.log("");
22949
23785
  });
22950
23786
  }
22951
23787
 
22952
23788
  // src/commands/uninstall.ts
22953
- var import_node_fs42 = require("node:fs");
23789
+ var import_node_fs45 = require("node:fs");
22954
23790
  var import_node_path30 = require("node:path");
22955
23791
  var SKILL_NAMES = [
22956
23792
  "verity-setup",
@@ -22971,10 +23807,10 @@ function registerUninstallCommand(program2) {
22971
23807
  const skillsRoot = projectPath(".claude/skills");
22972
23808
  for (const name of SKILL_NAMES) {
22973
23809
  const dir = (0, import_node_path30.join)(skillsRoot, name);
22974
- if ((0, import_node_fs42.existsSync)(dir)) {
23810
+ if ((0, import_node_fs45.existsSync)(dir)) {
22975
23811
  actions.push({
22976
23812
  label: `Remove .claude/skills/${name}/`,
22977
- apply: () => (0, import_node_fs42.rmSync)(dir, { recursive: true, force: true })
23813
+ apply: () => (0, import_node_fs45.rmSync)(dir, { recursive: true, force: true })
22978
23814
  });
22979
23815
  }
22980
23816
  }
@@ -22988,24 +23824,24 @@ function registerUninstallCommand(program2) {
22988
23824
  });
22989
23825
  }
22990
23826
  const verityDir = projectPath(VERITY_DIR);
22991
- if ((0, import_node_fs42.existsSync)(verityDir)) {
23827
+ if ((0, import_node_fs45.existsSync)(verityDir)) {
22992
23828
  actions.push({
22993
23829
  label: `Remove ${VERITY_DIR}/`,
22994
- apply: () => (0, import_node_fs42.rmSync)(verityDir, { recursive: true, force: true })
23830
+ apply: () => (0, import_node_fs45.rmSync)(verityDir, { recursive: true, force: true })
22995
23831
  });
22996
23832
  }
22997
23833
  if (!keepVerityMd) {
22998
23834
  const verityMd = projectPath(VERITY_MD_FILE);
22999
- if ((0, import_node_fs42.existsSync)(verityMd)) {
23835
+ if ((0, import_node_fs45.existsSync)(verityMd)) {
23000
23836
  actions.push({
23001
23837
  label: `Remove ${VERITY_MD_FILE}`,
23002
- apply: () => (0, import_node_fs42.rmSync)(verityMd, { force: true })
23838
+ apply: () => (0, import_node_fs45.rmSync)(verityMd, { force: true })
23003
23839
  });
23004
23840
  }
23005
23841
  }
23006
23842
  const cleanupEmptyDir = (path) => {
23007
- if ((0, import_node_fs42.existsSync)(path) && (0, import_node_fs42.statSync)(path).isDirectory() && (0, import_node_fs42.readdirSync)(path).length === 0) {
23008
- (0, import_node_fs42.rmdirSync)(path);
23843
+ if ((0, import_node_fs45.existsSync)(path) && (0, import_node_fs45.statSync)(path).isDirectory() && (0, import_node_fs45.readdirSync)(path).length === 0) {
23844
+ (0, import_node_fs45.rmdirSync)(path);
23009
23845
  }
23010
23846
  };
23011
23847
  actions.push({
@@ -23017,10 +23853,10 @@ function registerUninstallCommand(program2) {
23017
23853
  });
23018
23854
  const home = process.env.HOME ?? "";
23019
23855
  const globalVerityDir = (0, import_node_path30.join)(home, ".verity");
23020
- if (purgeGlobal && (0, import_node_fs42.existsSync)(globalVerityDir)) {
23856
+ if (purgeGlobal && (0, import_node_fs45.existsSync)(globalVerityDir)) {
23021
23857
  actions.push({
23022
23858
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
23023
- apply: () => (0, import_node_fs42.rmSync)(globalVerityDir, { recursive: true, force: true })
23859
+ apply: () => (0, import_node_fs45.rmSync)(globalVerityDir, { recursive: true, force: true })
23024
23860
  });
23025
23861
  }
23026
23862
  if (actions.length === 0) {
@@ -23040,7 +23876,7 @@ function registerUninstallCommand(program2) {
23040
23876
  if (!purgeGlobal) {
23041
23877
  printInfo('Saved tokens at ~/.verity/credentials are preserved \u2014 re-run "verity init" to reconnect.');
23042
23878
  } else {
23043
- printWarn("Global credentials wiped \u2014 re-register with /verity-setup to reconnect.");
23879
+ printWarn('Global credentials wiped \u2014 run "verity login" (or "verity init") to reconnect.');
23044
23880
  }
23045
23881
  });
23046
23882
  }
@@ -23214,7 +24050,7 @@ function registerTaskCommands(program2) {
23214
24050
  }
23215
24051
 
23216
24052
  // src/commands/reset.ts
23217
- var import_node_fs43 = require("node:fs");
24053
+ var import_node_fs46 = require("node:fs");
23218
24054
  var import_node_path31 = require("node:path");
23219
24055
  function registerResetCommand(program2) {
23220
24056
  program2.command("reset").description("Close the current task and clear transient state").option("--keep-task", "Only purge caches; leave the current task open").option("--all", "Also purge diagnostic logs (.verity/.logs/)").action(async (opts) => {
@@ -23252,11 +24088,11 @@ function registerResetCommand(program2) {
23252
24088
  }
23253
24089
  const cacheDir = projectPath(CACHE_DIR);
23254
24090
  let purged = 0;
23255
- if ((0, import_node_fs43.existsSync)(cacheDir)) {
23256
- for (const entry of (0, import_node_fs43.readdirSync)(cacheDir)) {
24091
+ if ((0, import_node_fs46.existsSync)(cacheDir)) {
24092
+ for (const entry of (0, import_node_fs46.readdirSync)(cacheDir)) {
23257
24093
  if (entry.startsWith("pending-")) {
23258
24094
  try {
23259
- (0, import_node_fs43.unlinkSync)((0, import_node_path31.join)(cacheDir, entry));
24095
+ (0, import_node_fs46.unlinkSync)((0, import_node_path31.join)(cacheDir, entry));
23260
24096
  purged++;
23261
24097
  } catch {
23262
24098
  }
@@ -23271,19 +24107,19 @@ function registerResetCommand(program2) {
23271
24107
  projectPath(`${VERITY_DIR}/.last-analysis`)
23272
24108
  ];
23273
24109
  for (const file of filesToClear) {
23274
- if ((0, import_node_fs43.existsSync)(file)) {
24110
+ if ((0, import_node_fs46.existsSync)(file)) {
23275
24111
  try {
23276
- (0, import_node_fs43.writeFileSync)(file, "");
24112
+ (0, import_node_fs46.writeFileSync)(file, "");
23277
24113
  } catch {
23278
24114
  }
23279
24115
  }
23280
24116
  }
23281
24117
  if (opts.all) {
23282
24118
  const logsDir = projectPath(`${VERITY_DIR}/.logs`);
23283
- if ((0, import_node_fs43.existsSync)(logsDir)) {
23284
- for (const entry of (0, import_node_fs43.readdirSync)(logsDir)) {
24119
+ if ((0, import_node_fs46.existsSync)(logsDir)) {
24120
+ for (const entry of (0, import_node_fs46.readdirSync)(logsDir)) {
23285
24121
  try {
23286
- (0, import_node_fs43.unlinkSync)((0, import_node_path31.join)(logsDir, entry));
24122
+ (0, import_node_fs46.unlinkSync)((0, import_node_path31.join)(logsDir, entry));
23287
24123
  } catch {
23288
24124
  }
23289
24125
  }
@@ -23591,8 +24427,8 @@ function registerTelemetryCommands(program2) {
23591
24427
  }
23592
24428
 
23593
24429
  // src/cli.ts
23594
- program.name("verity").description("CLI for Verity quality gate service").version("0.31.1-experimental.79dc9c2").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
23595
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.1-experimental.79dc9c2");
24430
+ program.name("verity").description("CLI for Verity quality gate service").version("0.31.1-experimental.be74f71").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
24431
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.1-experimental.be74f71");
23596
24432
  setUserNamedServiceUrl(program.opts().serviceUrl);
23597
24433
  try {
23598
24434
  await foldLegacyLocalCredential();
@@ -23618,6 +24454,7 @@ registerGuardCommand(program);
23618
24454
  registerIgnoreCommand(program);
23619
24455
  registerWaiveCommand(program);
23620
24456
  registerInitCommand(program);
24457
+ registerDoctorCommand(program);
23621
24458
  registerUninstallCommand(program);
23622
24459
  registerTaskCommands(program);
23623
24460
  registerResetCommand(program);