@codacy/verity-cli 0.31.1-experimental.79dc9c2 → 0.31.1-experimental.83a8619
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/CHANGELOG.md +81 -3
- package/README.md +28 -3
- package/bin/verity.js +2145 -456
- package/data/skills/verity-setup/SKILL.md +120 -278
- package/package.json +1 -1
package/bin/verity.js
CHANGED
|
@@ -10395,6 +10395,7 @@ var MAX_DELTA_BYTES = 194560;
|
|
|
10395
10395
|
var MAX_FILES = 40;
|
|
10396
10396
|
var MAX_FILE_BYTES = 51200;
|
|
10397
10397
|
var DEBOUNCE_SECONDS = 30;
|
|
10398
|
+
var MAX_ITERATIONS = 2;
|
|
10398
10399
|
var MAX_SPEC_FILES = 6;
|
|
10399
10400
|
var MAX_SPEC_FILE_BYTES = 512e3;
|
|
10400
10401
|
var MAX_TOTAL_SPEC_BYTES = 512e3;
|
|
@@ -10519,13 +10520,20 @@ var ADVISORY_EPISODE_FILE = `${VERITY_DIR}/.advisory-episode`;
|
|
|
10519
10520
|
var IGNORE_DECLARATION_FILE = `${VERITY_DIR}/.ignore-declaration`;
|
|
10520
10521
|
|
|
10521
10522
|
// src/lib/output.ts
|
|
10522
|
-
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
|
|
10528
|
-
var
|
|
10523
|
+
function colorEnabled() {
|
|
10524
|
+
if (process.env.FORCE_COLOR) return true;
|
|
10525
|
+
if (process.env.NO_COLOR !== void 0) return false;
|
|
10526
|
+
if (process.env.TERM === "dumb") return false;
|
|
10527
|
+
return !!process.stderr.isTTY;
|
|
10528
|
+
}
|
|
10529
|
+
var COLOR = colorEnabled();
|
|
10530
|
+
var RED = COLOR ? "\x1B[0;31m" : "";
|
|
10531
|
+
var YELLOW = COLOR ? "\x1B[1;33m" : "";
|
|
10532
|
+
var GREEN = COLOR ? "\x1B[0;32m" : "";
|
|
10533
|
+
var CYAN = COLOR ? "\x1B[0;36m" : "";
|
|
10534
|
+
var BOLD = COLOR ? "\x1B[1m" : "";
|
|
10535
|
+
var DIM = COLOR ? "\x1B[2m" : "";
|
|
10536
|
+
var NC = COLOR ? "\x1B[0m" : "";
|
|
10529
10537
|
function printJson(data) {
|
|
10530
10538
|
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
10531
10539
|
}
|
|
@@ -11252,7 +11260,7 @@ async function resolveServiceUrlDetailed(flagUrl, opts = {}) {
|
|
|
11252
11260
|
}
|
|
11253
11261
|
return {
|
|
11254
11262
|
ok: false,
|
|
11255
|
-
error: 'No Verity service URL found. Run "verity login" to get started, or
|
|
11263
|
+
error: 'No Verity service URL found. Run "verity login" to get started, or "verity init" to set up this project.'
|
|
11256
11264
|
};
|
|
11257
11265
|
}
|
|
11258
11266
|
async function resolveServiceUrlForAuth(flagUrl) {
|
|
@@ -11299,7 +11307,7 @@ async function resolveToken(flagToken) {
|
|
|
11299
11307
|
}
|
|
11300
11308
|
return {
|
|
11301
11309
|
ok: false,
|
|
11302
|
-
error: 'No Verity token found. Run "verity login" to sign in, or
|
|
11310
|
+
error: 'No Verity token found. Run "verity login" to sign in, or "verity init" to set up this project.'
|
|
11303
11311
|
};
|
|
11304
11312
|
}
|
|
11305
11313
|
async function whoami(token, serviceUrl, verbose) {
|
|
@@ -11393,6 +11401,91 @@ async function maybeHealServiceUrl(resolution, verbose) {
|
|
|
11393
11401
|
var readline = __toESM(require("node:readline/promises"));
|
|
11394
11402
|
var import_node_os = require("node:os");
|
|
11395
11403
|
|
|
11404
|
+
// src/lib/spinner.ts
|
|
11405
|
+
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
11406
|
+
var FRAME_MS = 80;
|
|
11407
|
+
var DIM2 = "\x1B[2m";
|
|
11408
|
+
var GREEN2 = "\x1B[0;32m";
|
|
11409
|
+
var YELLOW2 = "\x1B[1;33m";
|
|
11410
|
+
var RESET = "\x1B[0m";
|
|
11411
|
+
function secondsSince(start) {
|
|
11412
|
+
return `${Math.round((Date.now() - start) / 1e3)}s`;
|
|
11413
|
+
}
|
|
11414
|
+
function startSpinner(label2, opts = {}) {
|
|
11415
|
+
const stream = process.stderr;
|
|
11416
|
+
const color = colorEnabled();
|
|
11417
|
+
const animate = opts.animate ?? !!stream.isTTY;
|
|
11418
|
+
const showElapsed = opts.elapsed ?? true;
|
|
11419
|
+
const start = Date.now();
|
|
11420
|
+
let current = label2;
|
|
11421
|
+
let frame = 0;
|
|
11422
|
+
let timer = null;
|
|
11423
|
+
let done = false;
|
|
11424
|
+
const fit = (label3, clock, columns) => {
|
|
11425
|
+
const budget = columns - 5;
|
|
11426
|
+
if (budget <= 0) return { label: "", clock: "" };
|
|
11427
|
+
if (label3.length + clock.length <= budget) return { label: label3, clock };
|
|
11428
|
+
if (clock.length >= budget) return { label: "", clock: clock.slice(0, budget) };
|
|
11429
|
+
const room = budget - clock.length;
|
|
11430
|
+
return { label: room <= 1 ? label3.slice(0, room) : `${label3.slice(0, room - 1)}\u2026`, clock };
|
|
11431
|
+
};
|
|
11432
|
+
const paint = () => {
|
|
11433
|
+
const glyph = FRAMES[frame++ % FRAMES.length];
|
|
11434
|
+
const columns = stream.columns ?? 80;
|
|
11435
|
+
const { label: label3, clock } = fit(current, showElapsed ? ` ${secondsSince(start)}` : "", columns);
|
|
11436
|
+
const line = color ? ` ${GREEN2}${glyph}${RESET} ${label3}${DIM2}${clock}${RESET}` : ` ${glyph} ${label3}${clock}`;
|
|
11437
|
+
stream.write(`\r\x1B[2K${line}`);
|
|
11438
|
+
};
|
|
11439
|
+
const clearLine = () => {
|
|
11440
|
+
if (animate) stream.write("\r\x1B[2K");
|
|
11441
|
+
};
|
|
11442
|
+
if (animate) {
|
|
11443
|
+
paint();
|
|
11444
|
+
timer = setInterval(paint, FRAME_MS);
|
|
11445
|
+
timer.unref?.();
|
|
11446
|
+
} else {
|
|
11447
|
+
stream.write(` ${current}\u2026
|
|
11448
|
+
`);
|
|
11449
|
+
}
|
|
11450
|
+
const finish = (render2) => {
|
|
11451
|
+
if (done) return;
|
|
11452
|
+
done = true;
|
|
11453
|
+
if (timer) clearInterval(timer);
|
|
11454
|
+
clearLine();
|
|
11455
|
+
render2();
|
|
11456
|
+
};
|
|
11457
|
+
return {
|
|
11458
|
+
update(next) {
|
|
11459
|
+
current = next;
|
|
11460
|
+
if (animate) paint();
|
|
11461
|
+
else stream.write(` ${next}\u2026
|
|
11462
|
+
`);
|
|
11463
|
+
},
|
|
11464
|
+
succeed(message) {
|
|
11465
|
+
finish(() => {
|
|
11466
|
+
const text = message ?? current;
|
|
11467
|
+
const clock = showElapsed ? ` (${secondsSince(start)})` : "";
|
|
11468
|
+
stream.write(
|
|
11469
|
+
color ? ` ${GREEN2}\u2713${RESET} ${text}${DIM2}${clock}${RESET}
|
|
11470
|
+
` : ` \u2713 ${text}${clock}
|
|
11471
|
+
`
|
|
11472
|
+
);
|
|
11473
|
+
});
|
|
11474
|
+
},
|
|
11475
|
+
warn(message) {
|
|
11476
|
+
finish(() => {
|
|
11477
|
+
stream.write(color ? ` ${YELLOW2}\u26A0${RESET} ${message}
|
|
11478
|
+
` : ` \u26A0 ${message}
|
|
11479
|
+
`);
|
|
11480
|
+
});
|
|
11481
|
+
},
|
|
11482
|
+
stop() {
|
|
11483
|
+
finish(() => {
|
|
11484
|
+
});
|
|
11485
|
+
}
|
|
11486
|
+
};
|
|
11487
|
+
}
|
|
11488
|
+
|
|
11396
11489
|
// src/lib/provider-auth.ts
|
|
11397
11490
|
var sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
11398
11491
|
var form = (fields) => new URLSearchParams(fields).toString();
|
|
@@ -11459,42 +11552,49 @@ async function githubDeviceFlow() {
|
|
|
11459
11552
|
printInfo("");
|
|
11460
11553
|
printInfo(`To authorize Verity, open: ${dc.verification_uri}`);
|
|
11461
11554
|
printInfo(`And enter the code: ${dc.user_code}`);
|
|
11462
|
-
|
|
11555
|
+
const spinner = startSpinner(`Waiting for you to approve in the browser \xB7 code ${dc.user_code}`);
|
|
11463
11556
|
const deadline = Date.now() + (dc.expires_in || 900) * 1e3;
|
|
11464
11557
|
let interval = dc.interval || 5;
|
|
11465
|
-
|
|
11466
|
-
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
|
|
11476
|
-
|
|
11477
|
-
|
|
11478
|
-
|
|
11479
|
-
|
|
11480
|
-
|
|
11481
|
-
|
|
11482
|
-
|
|
11483
|
-
|
|
11484
|
-
|
|
11485
|
-
|
|
11486
|
-
|
|
11487
|
-
|
|
11488
|
-
|
|
11489
|
-
|
|
11490
|
-
|
|
11491
|
-
|
|
11492
|
-
|
|
11493
|
-
|
|
11494
|
-
|
|
11558
|
+
try {
|
|
11559
|
+
while (Date.now() < deadline) {
|
|
11560
|
+
await sleep(interval * 1e3);
|
|
11561
|
+
let data;
|
|
11562
|
+
try {
|
|
11563
|
+
const res = await fetch(GITHUB_ACCESS_TOKEN_URL, {
|
|
11564
|
+
method: "POST",
|
|
11565
|
+
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
|
|
11566
|
+
body: form({
|
|
11567
|
+
client_id: GITHUB_CLIENT_ID,
|
|
11568
|
+
device_code: dc.device_code,
|
|
11569
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
11570
|
+
})
|
|
11571
|
+
});
|
|
11572
|
+
data = await res.json().catch(() => ({}));
|
|
11573
|
+
} catch {
|
|
11574
|
+
continue;
|
|
11575
|
+
}
|
|
11576
|
+
if (data.access_token) {
|
|
11577
|
+
spinner.succeed("Authorized on GitHub");
|
|
11578
|
+
return { ok: true, data: data.access_token };
|
|
11579
|
+
}
|
|
11580
|
+
switch (data.error) {
|
|
11581
|
+
case "authorization_pending":
|
|
11582
|
+
break;
|
|
11583
|
+
case "slow_down":
|
|
11584
|
+
interval += 5;
|
|
11585
|
+
break;
|
|
11586
|
+
case "access_denied":
|
|
11587
|
+
return { ok: false, error: "Authorization was denied on GitHub." };
|
|
11588
|
+
case "expired_token":
|
|
11589
|
+
return { ok: false, error: "The authorization code expired. Re-run register." };
|
|
11590
|
+
default:
|
|
11591
|
+
if (data.error) return { ok: false, error: `GitHub auth error: ${data.error}` };
|
|
11592
|
+
}
|
|
11495
11593
|
}
|
|
11594
|
+
return { ok: false, error: "Timed out waiting for GitHub authorization." };
|
|
11595
|
+
} finally {
|
|
11596
|
+
spinner.stop();
|
|
11496
11597
|
}
|
|
11497
|
-
return { ok: false, error: "Timed out waiting for GitHub authorization." };
|
|
11498
11598
|
}
|
|
11499
11599
|
|
|
11500
11600
|
// src/lib/register.ts
|
|
@@ -12263,12 +12363,20 @@ var VERITY_STOP_RE = /(?:^|[\/\s"'])verity\s+analyze\b/;
|
|
|
12263
12363
|
var VERITY_INTENT_RE = /(?:^|[\/\s"'])verity\s+intent\s+capture\b/;
|
|
12264
12364
|
var VERITY_GUARD_RE = /(?:^|[\/\s"'])verity\s+guard\b/;
|
|
12265
12365
|
var VERITY_BASELINE_RE = /(?:^|[\/\s"'])verity\s+baseline\s+capture\b/;
|
|
12366
|
+
var VERITY_COMPACT_RE = /(?:^|[\/\s"'])verity\s+compact\b/;
|
|
12367
|
+
var VERITY_SESSION_END_RE = /(?:^|[\/\s"'])verity\s+session\s+end\b/;
|
|
12266
12368
|
function isVerityGuardHook(entry) {
|
|
12267
12369
|
return VERITY_GUARD_RE.test(entry.command ?? "");
|
|
12268
12370
|
}
|
|
12269
12371
|
function isVerityBaselineHook(entry) {
|
|
12270
12372
|
return VERITY_BASELINE_RE.test(entry.command ?? "");
|
|
12271
12373
|
}
|
|
12374
|
+
function isVerityCompactHook(entry) {
|
|
12375
|
+
return VERITY_COMPACT_RE.test(entry.command ?? "");
|
|
12376
|
+
}
|
|
12377
|
+
function isVeritySessionEndHook(entry) {
|
|
12378
|
+
return VERITY_SESSION_END_RE.test(entry.command ?? "");
|
|
12379
|
+
}
|
|
12272
12380
|
var LEGACY_STOP_RE = /(?:^|[\/\s"'])gate\s+analyze\b/;
|
|
12273
12381
|
var LEGACY_INTENT_RE = /(?:^|[\/\s"'])gate\s+intent\s+capture\b/;
|
|
12274
12382
|
function isVerityStopHook(entry) {
|
|
@@ -12280,7 +12388,7 @@ function isVerityIntentHook(entry) {
|
|
|
12280
12388
|
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
12389
|
}
|
|
12282
12390
|
function isVerityHook(entry) {
|
|
12283
|
-
return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
|
|
12391
|
+
return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry) || isVerityCompactHook(entry) || isVeritySessionEndHook(entry);
|
|
12284
12392
|
}
|
|
12285
12393
|
function isCurrentVerityStopHook(entry) {
|
|
12286
12394
|
const c = entry.command ?? "";
|
|
@@ -12291,7 +12399,7 @@ function isCurrentVerityIntentHook(entry) {
|
|
|
12291
12399
|
return VERITY_INTENT_RE.test(c) || c.includes(".verity/hooks/capture-intent.sh");
|
|
12292
12400
|
}
|
|
12293
12401
|
function isCurrentVerityHook(entry) {
|
|
12294
|
-
return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
|
|
12402
|
+
return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry) || isVerityCompactHook(entry) || isVeritySessionEndHook(entry);
|
|
12295
12403
|
}
|
|
12296
12404
|
function settingsHasLegacyHook(settings) {
|
|
12297
12405
|
for (const groups of Object.values(settings.hooks ?? {})) {
|
|
@@ -12353,6 +12461,8 @@ async function checkAllVerityHooks() {
|
|
|
12353
12461
|
async function checkExternalVerityHooks() {
|
|
12354
12462
|
let stop = false;
|
|
12355
12463
|
let intent = false;
|
|
12464
|
+
let compact = false;
|
|
12465
|
+
let sessionEnd = false;
|
|
12356
12466
|
let guardOn = [];
|
|
12357
12467
|
for (const f of [SETTINGS_LOCAL_FILE, globalSettingsFile()]) {
|
|
12358
12468
|
let settings;
|
|
@@ -12364,9 +12474,11 @@ async function checkExternalVerityHooks() {
|
|
|
12364
12474
|
const r = checkVerityHooks(settings);
|
|
12365
12475
|
stop = stop || r.stop;
|
|
12366
12476
|
intent = intent || r.intent;
|
|
12477
|
+
compact = compact || r.compact;
|
|
12478
|
+
sessionEnd = sessionEnd || r.sessionEnd;
|
|
12367
12479
|
if (r.guardOn.length > guardOn.length) guardOn = r.guardOn;
|
|
12368
12480
|
}
|
|
12369
|
-
return { stop, intent, guardOn };
|
|
12481
|
+
return { stop, intent, compact, sessionEnd, guardOn };
|
|
12370
12482
|
}
|
|
12371
12483
|
async function checkAllVerityHooksDetailed() {
|
|
12372
12484
|
let stop = false;
|
|
@@ -12446,11 +12558,11 @@ function checkVerityHooks(settings) {
|
|
|
12446
12558
|
}
|
|
12447
12559
|
const compactGroups = hooks["PostCompact"] ?? [];
|
|
12448
12560
|
const hasCompact = compactGroups.some(
|
|
12449
|
-
(g) => g.hooks?.some((h) => h
|
|
12561
|
+
(g) => g.hooks?.some((h) => isVerityCompactHook(h))
|
|
12450
12562
|
);
|
|
12451
12563
|
const endGroups = hooks["SessionEnd"] ?? [];
|
|
12452
12564
|
const hasSessionEnd = endGroups.some(
|
|
12453
|
-
(g) => g.hooks?.some((h) => h
|
|
12565
|
+
(g) => g.hooks?.some((h) => isVeritySessionEndHook(h))
|
|
12454
12566
|
);
|
|
12455
12567
|
return {
|
|
12456
12568
|
stop: hasStop,
|
|
@@ -12564,6 +12676,8 @@ function reconcileMomentHooks(settings, moments, externalPresent = {
|
|
|
12564
12676
|
};
|
|
12565
12677
|
if (!externalPresent.intent) push("UserPromptSubmit", { hooks: [VERITY_INTENT_HOOK] });
|
|
12566
12678
|
push("SessionStart", { hooks: [VERITY_BASELINE_HOOK] });
|
|
12679
|
+
if (!externalPresent.compact) push("PostCompact", { hooks: [VERITY_COMPACT_HOOK] });
|
|
12680
|
+
if (!externalPresent.sessionEnd) push("SessionEnd", { hooks: [VERITY_SESSION_END_HOOK] });
|
|
12567
12681
|
if (moments.includes("stop") && !externalPresent.stop) {
|
|
12568
12682
|
push("Stop", { hooks: [VERITY_STOP_HOOK] });
|
|
12569
12683
|
}
|
|
@@ -13540,6 +13654,74 @@ var LEGACY_MD_END = "<!-- gate-memory:end -->";
|
|
|
13540
13654
|
var LEGACY_PRESERVE_START = "<!-- gate-memory:preserve -->";
|
|
13541
13655
|
var LEGACY_PRESERVE_END = "<!-- /gate-memory:preserve -->";
|
|
13542
13656
|
var CLAUDE_MD_PROSE = [
|
|
13657
|
+
"## Project Memory",
|
|
13658
|
+
"",
|
|
13659
|
+
"This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
|
|
13660
|
+
"non-trivial work, scan `.verity/memory/index.md` for decisions, gotchas, and patterns",
|
|
13661
|
+
"that may apply to the change you are about to make. Open specific node files via",
|
|
13662
|
+
"the Read tool when the title or scope suggests relevance.",
|
|
13663
|
+
"",
|
|
13664
|
+
"The graph is auto-maintained by Verity. Files at `.verity/memory/_archive/` are",
|
|
13665
|
+
"superseded \u2014 ignore them unless investigating history.",
|
|
13666
|
+
"",
|
|
13667
|
+
"## Quality gate: accepted risks",
|
|
13668
|
+
"",
|
|
13669
|
+
"When the Verity pre-commit/pre-push gate FAILs, fix the findings \u2014 that is the",
|
|
13670
|
+
'default. Use `verity waive <pattern-id> --file <path> --reason "\u2026"` ONLY to relay',
|
|
13671
|
+
"a risk a human has explicitly accepted: a named code-review finding, an ADR, or",
|
|
13672
|
+
"the user saying so in this conversation. The --reason must cite that source.",
|
|
13673
|
+
"",
|
|
13674
|
+
"Never waive on your own judgment, to get past a block, or pre-emptively. A waive",
|
|
13675
|
+
"binds to the file's current bytes and voids automatically when the file changes,",
|
|
13676
|
+
"and every waive is recorded in the run ledger. For a pattern-level false positive",
|
|
13677
|
+
"use `verity feedback finding <run-id> <pattern-id> false_positive` instead.",
|
|
13678
|
+
"",
|
|
13679
|
+
"## Post-task reflection",
|
|
13680
|
+
"",
|
|
13681
|
+
`When a task is complete (you've created a PR, the user says "done" or "ship it",`,
|
|
13682
|
+
"or the work is clearly finished), **draft the reflection yourself first** \u2014 1\u20133",
|
|
13683
|
+
"concrete things worth remembering (a decision, a gotcha, or a pattern), each",
|
|
13684
|
+
"cited with the files / PR / commands / error-signatures it came from. Skip",
|
|
13685
|
+
"entirely if nothing non-obvious happened.",
|
|
13686
|
+
"",
|
|
13687
|
+
"Then, depending on how the session is running:",
|
|
13688
|
+
"",
|
|
13689
|
+
"- **Interactive** (a person is at the keyboard): show your draft and ask one",
|
|
13690
|
+
` question before recording \u2014 "Here's my reflection draft for the project`,
|
|
13691
|
+
" memory: <draft>. Anything to confirm, correct, or add before I record it?",
|
|
13692
|
+
` (Say 'skip' to drop it.)" Record the final text (with their corrections) via`,
|
|
13693
|
+
' `verity reflect --user-input "<final reflection>" --kind <kind>`. If they say',
|
|
13694
|
+
' "skip", do not record.',
|
|
13695
|
+
"- **Autonomous** (headless / CI / cron, or `VERITY_AUTONOMOUS=1`): record your",
|
|
13696
|
+
' draft directly, never blocking \u2014 `verity reflect --user-input "<your draft>"',
|
|
13697
|
+
" --kind <kind> --autonomous`.",
|
|
13698
|
+
"",
|
|
13699
|
+
"> Durable, hand-curated guidance goes in the preserve region below (it survives",
|
|
13700
|
+
"> regeneration) or anywhere OUTSIDE these markers. Everything else between the",
|
|
13701
|
+
"> markers is tool-owned and overwritten on each run.",
|
|
13702
|
+
"",
|
|
13703
|
+
"## Housekeeping Turns",
|
|
13704
|
+
"",
|
|
13705
|
+
"When a turn will be pure housekeeping \u2014 pulling, installing dependencies,",
|
|
13706
|
+
"rebasing, a formatting sweep you are not authoring \u2014 declare it BEFORE doing it:",
|
|
13707
|
+
"",
|
|
13708
|
+
"```bash",
|
|
13709
|
+
'verity ignore --turn --agent --reason "pulling latest before starting"',
|
|
13710
|
+
"```",
|
|
13711
|
+
"",
|
|
13712
|
+
"This skips the review for that turn, which saves the turn Verity would",
|
|
13713
|
+
"otherwise spend saying it had nothing to say. Use `--for 30m` instead of",
|
|
13714
|
+
"`--turn` when a single piece of housekeeping spans several turns.",
|
|
13715
|
+
"",
|
|
13716
|
+
"**It is a claim about the turn, not a way to silence review.** The declaration",
|
|
13717
|
+
"is checked against what the turn actually did: if anything is authored \u2014 by you,",
|
|
13718
|
+
"by a subagent, or by a shell command that can write files \u2014 it voids, the review",
|
|
13719
|
+
"runs anyway, and the broken declaration is reported. So declare housekeeping you",
|
|
13720
|
+
"are about to do, never work you have already done, and never as a way to get past",
|
|
13721
|
+
"a finding. Declarations are budgeted per session and every one is recorded with",
|
|
13722
|
+
"its reason."
|
|
13723
|
+
].join("\n");
|
|
13724
|
+
var CLAUDE_MD_PROSE_PRE_REFLECT = [
|
|
13543
13725
|
"## Project Memory",
|
|
13544
13726
|
"",
|
|
13545
13727
|
"This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
|
|
@@ -13750,6 +13932,7 @@ function stripKnownProse(interior) {
|
|
|
13750
13932
|
const trimmed = interior.replace(/^\n+/, "");
|
|
13751
13933
|
for (const prose of [
|
|
13752
13934
|
CLAUDE_MD_PROSE,
|
|
13935
|
+
CLAUDE_MD_PROSE_PRE_REFLECT,
|
|
13753
13936
|
CLAUDE_MD_PROSE_PRE_WAIVE,
|
|
13754
13937
|
CLAUDE_MD_PROSE_PRE_IGNORE,
|
|
13755
13938
|
CLAUDE_MD_PROSE_LEGACY
|
|
@@ -14136,6 +14319,11 @@ var REANCHOR_WINDOW = 20;
|
|
|
14136
14319
|
var STATEMENT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
14137
14320
|
var DEFAULT_MEMORY_BUDGET_BYTES = 4096;
|
|
14138
14321
|
|
|
14322
|
+
// src/lib/dossier/events.ts
|
|
14323
|
+
function statementAnchorKey(file, patternId) {
|
|
14324
|
+
return `${file}::${patternId}`;
|
|
14325
|
+
}
|
|
14326
|
+
|
|
14139
14327
|
// src/lib/dossier/log.ts
|
|
14140
14328
|
var import_node_crypto4 = require("node:crypto");
|
|
14141
14329
|
var import_node_fs10 = require("node:fs");
|
|
@@ -15350,10 +15538,10 @@ function recordVerdict(d, v) {
|
|
|
15350
15538
|
const at = src?.[f.line - 1];
|
|
15351
15539
|
appendEvent(d, {
|
|
15352
15540
|
k: "statement",
|
|
15353
|
-
//
|
|
15354
|
-
//
|
|
15355
|
-
//
|
|
15356
|
-
anchor_key:
|
|
15541
|
+
// ⚠ ONE OWNER. The server's settled feed is resolved against this exact
|
|
15542
|
+
// key in `13-reconcile.ts`; spelling the template literal twice is how the
|
|
15543
|
+
// two sides drift apart. See `statementAnchorKey`.
|
|
15544
|
+
anchor_key: statementAnchorKey(f.file, f.pattern_id),
|
|
15357
15545
|
file: f.file,
|
|
15358
15546
|
line: f.line,
|
|
15359
15547
|
pattern_id: f.pattern_id,
|
|
@@ -15790,33 +15978,6 @@ ${addedLines}`,
|
|
|
15790
15978
|
}
|
|
15791
15979
|
return { diffs, has_snapshots: true };
|
|
15792
15980
|
}
|
|
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
15981
|
function saveSnapshots(files) {
|
|
15821
15982
|
const snapshotPaths = /* @__PURE__ */ new Set();
|
|
15822
15983
|
for (const file of files) {
|
|
@@ -16738,19 +16899,19 @@ function loc(f) {
|
|
|
16738
16899
|
if (!f.file) return "";
|
|
16739
16900
|
return f.line != null ? `${f.file}:${f.line}` : f.file;
|
|
16740
16901
|
}
|
|
16741
|
-
function formatRunDetail(
|
|
16902
|
+
function formatRunDetail(run2) {
|
|
16742
16903
|
const lines = [];
|
|
16743
|
-
const q =
|
|
16744
|
-
const s =
|
|
16904
|
+
const q = run2.assessment?.quality_score;
|
|
16905
|
+
const s = run2.assessment?.security_score;
|
|
16745
16906
|
const qStr = q != null ? `${q}` : "-";
|
|
16746
16907
|
const sStr = s != null ? `${s}` : "-";
|
|
16747
|
-
lines.push(`${
|
|
16908
|
+
lines.push(`${run2.run_id} ${run2.gate_decision} Q ${qStr}/10 S ${sStr}/10`);
|
|
16748
16909
|
const meta = [];
|
|
16749
|
-
if (
|
|
16750
|
-
if (
|
|
16751
|
-
if (
|
|
16910
|
+
if (run2.trigger) meta.push(`trigger: ${run2.trigger}`);
|
|
16911
|
+
if (run2.standard_version != null) meta.push(`standard v${run2.standard_version}`);
|
|
16912
|
+
if (run2.created_at) meta.push(run2.created_at.slice(0, 19).replace("T", " "));
|
|
16752
16913
|
if (meta.length > 0) lines.push(meta.join(" \xB7 "));
|
|
16753
|
-
const findings =
|
|
16914
|
+
const findings = run2.findings ?? [];
|
|
16754
16915
|
if (findings.length === 0) {
|
|
16755
16916
|
lines.push("");
|
|
16756
16917
|
lines.push("No findings \u2014 clean.");
|
|
@@ -16767,7 +16928,7 @@ function formatRunDetail(run) {
|
|
|
16767
16928
|
if (f.scope === "pre-existing") lines.push(" (pre-existing)");
|
|
16768
16929
|
}
|
|
16769
16930
|
}
|
|
16770
|
-
const pending =
|
|
16931
|
+
const pending = run2.pending_items ?? [];
|
|
16771
16932
|
if (pending.length > 0) {
|
|
16772
16933
|
lines.push("");
|
|
16773
16934
|
lines.push(`PENDING (${pending.length})`);
|
|
@@ -16775,9 +16936,9 @@ function formatRunDetail(run) {
|
|
|
16775
16936
|
lines.push(` [${(p.priority ?? "").toUpperCase()}] ${p.description}`);
|
|
16776
16937
|
}
|
|
16777
16938
|
}
|
|
16778
|
-
if (
|
|
16939
|
+
if (run2.assessment?.narrative) {
|
|
16779
16940
|
lines.push("");
|
|
16780
|
-
lines.push(
|
|
16941
|
+
lines.push(run2.assessment.narrative);
|
|
16781
16942
|
}
|
|
16782
16943
|
return lines;
|
|
16783
16944
|
}
|
|
@@ -17149,7 +17310,7 @@ function registerStatusCommand(program2) {
|
|
|
17149
17310
|
return;
|
|
17150
17311
|
}
|
|
17151
17312
|
if (mem?.configured === false) {
|
|
17152
|
-
printInfo(
|
|
17313
|
+
printInfo('Verity is not configured for this project. Run "verity init".');
|
|
17153
17314
|
return;
|
|
17154
17315
|
}
|
|
17155
17316
|
printInfo("=== Verity Status ===");
|
|
@@ -17181,7 +17342,7 @@ function registerStatusCommand(program2) {
|
|
|
17181
17342
|
if (hookStatus.stop) moments.push("stop");
|
|
17182
17343
|
if (hookStatus.guardOn.includes("commit")) moments.push("pre-commit");
|
|
17183
17344
|
if (hookStatus.guardOn.includes("push")) moments.push("pre-push/PR");
|
|
17184
|
-
printInfo(`Moments: ${moments.length > 0 ? moments.join(", ") :
|
|
17345
|
+
printInfo(`Moments: ${moments.length > 0 ? moments.join(", ") : 'none (run "verity init")'}`);
|
|
17185
17346
|
if (!mem) return;
|
|
17186
17347
|
if (mem.recent_runs) {
|
|
17187
17348
|
const r = mem.recent_runs;
|
|
@@ -17267,12 +17428,12 @@ function registerStatusCommand(program2) {
|
|
|
17267
17428
|
printInfo("");
|
|
17268
17429
|
printInfo("--- Recent Runs ---");
|
|
17269
17430
|
printInfo(`${"Run ID".padEnd(32)} ${"Decision".padEnd(10)}${"Q".padEnd(4)}${"S".padEnd(4)}${"Findings".padEnd(32)}Date`);
|
|
17270
|
-
for (const
|
|
17271
|
-
const q =
|
|
17272
|
-
const s =
|
|
17273
|
-
const findings = formatFindingsSummary(
|
|
17274
|
-
const date =
|
|
17275
|
-
printInfo(`${
|
|
17431
|
+
for (const run2 of runsResult.data.runs) {
|
|
17432
|
+
const q = run2.quality_score != null ? `${run2.quality_score}` : "-";
|
|
17433
|
+
const s = run2.security_score != null ? `${run2.security_score}` : "-";
|
|
17434
|
+
const findings = formatFindingsSummary(run2.findings_count);
|
|
17435
|
+
const date = run2.created_at.slice(0, 19).replace("T", " ");
|
|
17436
|
+
printInfo(`${run2.run_id.padEnd(32)} ${run2.gate_decision.padEnd(10)}${q.padEnd(4)}${s.padEnd(4)}${findings.padEnd(32)}${date}`);
|
|
17276
17437
|
}
|
|
17277
17438
|
}
|
|
17278
17439
|
}
|
|
@@ -17407,7 +17568,6 @@ function createRun(opts, globals) {
|
|
|
17407
17568
|
token: "",
|
|
17408
17569
|
modeDecision: null,
|
|
17409
17570
|
sessionIdForMemory: "",
|
|
17410
|
-
contextFilePaths: [],
|
|
17411
17571
|
analysisMode: "standard",
|
|
17412
17572
|
sessionAuthoredCode: false,
|
|
17413
17573
|
staticResults: {
|
|
@@ -17417,6 +17577,7 @@ function createRun(opts, globals) {
|
|
|
17417
17577
|
},
|
|
17418
17578
|
codeDelta: { files: [], total_lines: 0, total_files: 0, excluded: [] },
|
|
17419
17579
|
snapshotResult: { has_snapshots: false, diffs: [] },
|
|
17580
|
+
repoContext: null,
|
|
17420
17581
|
contentHash: null,
|
|
17421
17582
|
iteration: 1,
|
|
17422
17583
|
currentCommit: "",
|
|
@@ -17498,6 +17659,704 @@ function logToFileOnly(text) {
|
|
|
17498
17659
|
`);
|
|
17499
17660
|
}
|
|
17500
17661
|
|
|
17662
|
+
// src/lib/repo-context.ts
|
|
17663
|
+
var import_node_child_process7 = require("node:child_process");
|
|
17664
|
+
var import_node_os3 = require("node:os");
|
|
17665
|
+
function rgInvocations(env = process.env) {
|
|
17666
|
+
const out = [{ cmd: "rg" }];
|
|
17667
|
+
if (env.CLAUDE_CODE_EXECPATH) out.push({ cmd: env.CLAUDE_CODE_EXECPATH, argv0: "rg" });
|
|
17668
|
+
out.push({ cmd: `${(0, import_node_os3.homedir)()}/.local/bin/claude`, argv0: "rg" });
|
|
17669
|
+
return out;
|
|
17670
|
+
}
|
|
17671
|
+
var MAX_SYMBOLS = 12;
|
|
17672
|
+
var MAX_SITES = 24;
|
|
17673
|
+
var MAX_SITES_PER_FILE = 3;
|
|
17674
|
+
var MAX_HITS_PER_SYMBOL = 50;
|
|
17675
|
+
var MAX_TEST_SLOTS = 8;
|
|
17676
|
+
var SITE_TEXT_MAX = 160;
|
|
17677
|
+
var ENCLOSING_SCAN_LINES = 200;
|
|
17678
|
+
var RG_TIMEOUT_MS = 1500;
|
|
17679
|
+
var IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
17680
|
+
var STOPLIST = /* @__PURE__ */ new Set([
|
|
17681
|
+
// keyword-shaped captures
|
|
17682
|
+
"if",
|
|
17683
|
+
"for",
|
|
17684
|
+
"while",
|
|
17685
|
+
"switch",
|
|
17686
|
+
"catch",
|
|
17687
|
+
"return",
|
|
17688
|
+
"function",
|
|
17689
|
+
"class",
|
|
17690
|
+
"const",
|
|
17691
|
+
"let",
|
|
17692
|
+
"var",
|
|
17693
|
+
"new",
|
|
17694
|
+
"else",
|
|
17695
|
+
"try",
|
|
17696
|
+
"finally",
|
|
17697
|
+
"throw",
|
|
17698
|
+
"await",
|
|
17699
|
+
"async",
|
|
17700
|
+
"yield",
|
|
17701
|
+
"delete",
|
|
17702
|
+
"typeof",
|
|
17703
|
+
"instanceof",
|
|
17704
|
+
"void",
|
|
17705
|
+
"this",
|
|
17706
|
+
"super",
|
|
17707
|
+
"import",
|
|
17708
|
+
"export",
|
|
17709
|
+
"default",
|
|
17710
|
+
"extends",
|
|
17711
|
+
"implements",
|
|
17712
|
+
"interface",
|
|
17713
|
+
"enum",
|
|
17714
|
+
"type",
|
|
17715
|
+
"public",
|
|
17716
|
+
"private",
|
|
17717
|
+
"protected",
|
|
17718
|
+
"static",
|
|
17719
|
+
"get",
|
|
17720
|
+
"set",
|
|
17721
|
+
"constructor",
|
|
17722
|
+
"def",
|
|
17723
|
+
"elif",
|
|
17724
|
+
"lambda",
|
|
17725
|
+
"with",
|
|
17726
|
+
"pass",
|
|
17727
|
+
"self",
|
|
17728
|
+
"cls",
|
|
17729
|
+
"not",
|
|
17730
|
+
"and",
|
|
17731
|
+
"or",
|
|
17732
|
+
"raise",
|
|
17733
|
+
"except",
|
|
17734
|
+
"func",
|
|
17735
|
+
"defer",
|
|
17736
|
+
"chan",
|
|
17737
|
+
"select",
|
|
17738
|
+
"range",
|
|
17739
|
+
"module",
|
|
17740
|
+
"struct",
|
|
17741
|
+
"trait",
|
|
17742
|
+
"impl",
|
|
17743
|
+
"using",
|
|
17744
|
+
"namespace",
|
|
17745
|
+
// universal noise
|
|
17746
|
+
"main",
|
|
17747
|
+
"init",
|
|
17748
|
+
"index",
|
|
17749
|
+
"data",
|
|
17750
|
+
"value",
|
|
17751
|
+
"result",
|
|
17752
|
+
"item",
|
|
17753
|
+
"name",
|
|
17754
|
+
"key",
|
|
17755
|
+
"run",
|
|
17756
|
+
"test",
|
|
17757
|
+
"setup",
|
|
17758
|
+
"update",
|
|
17759
|
+
"create",
|
|
17760
|
+
"handle",
|
|
17761
|
+
"check",
|
|
17762
|
+
"load",
|
|
17763
|
+
"save",
|
|
17764
|
+
"list",
|
|
17765
|
+
"map",
|
|
17766
|
+
"args",
|
|
17767
|
+
"params",
|
|
17768
|
+
"props",
|
|
17769
|
+
"state",
|
|
17770
|
+
"error",
|
|
17771
|
+
"err",
|
|
17772
|
+
"res",
|
|
17773
|
+
"req",
|
|
17774
|
+
"ctx",
|
|
17775
|
+
"config",
|
|
17776
|
+
"options",
|
|
17777
|
+
"util",
|
|
17778
|
+
"utils",
|
|
17779
|
+
"helper",
|
|
17780
|
+
"render",
|
|
17781
|
+
"build",
|
|
17782
|
+
"parse",
|
|
17783
|
+
"format",
|
|
17784
|
+
"apply",
|
|
17785
|
+
"process",
|
|
17786
|
+
"start",
|
|
17787
|
+
"stop"
|
|
17788
|
+
]);
|
|
17789
|
+
var TS_RULES = [
|
|
17790
|
+
/\b(?:function|class|interface|enum)\s+([A-Za-z_][A-Za-z0-9_]*)/,
|
|
17791
|
+
/\btype\s+([A-Za-z_][A-Za-z0-9_]*)\s*=/,
|
|
17792
|
+
// const foo = (…) => · const foo = x => · const foo = function.
|
|
17793
|
+
// ⚠ REQUIRES the arrow or `function` ON THE LINE. The first version accepted
|
|
17794
|
+
// any `= (` — and `const started = (rows?.[0] as Row)?.at` is a PARENTHESIZED
|
|
17795
|
+
// CAST, not a function. Replayed over 12 real commits, that one shape put
|
|
17796
|
+
// three local variables into the symbol set per commit. A multi-line arrow
|
|
17797
|
+
// is the accepted false negative; a cast is not an accepted false positive.
|
|
17798
|
+
/\b(?:const|let|var)\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?::[^=\n]+)?=\s*(?:async\s+)?(?:function\b|\([^)]*\)(?:\s*:[^=\n]+)?\s*=>|[A-Za-z_$][\w$]*\s*=>)/,
|
|
17799
|
+
// method shape: name(…) { — keyword captures die at the stoplist
|
|
17800
|
+
/^\s*(?:(?:public|private|protected|static|readonly|async|override)\s+)*(?:\*\s*)?([A-Za-z_][A-Za-z0-9_]*)\s*\([^)]*\)\s*(?::[^{;\n]+)?\s*\{/
|
|
17801
|
+
];
|
|
17802
|
+
var PY_RULES = [
|
|
17803
|
+
/^\s*(?:async\s+)?def\s+([A-Za-z_]\w*)/,
|
|
17804
|
+
/^\s*class\s+([A-Za-z_]\w*)/
|
|
17805
|
+
];
|
|
17806
|
+
var GO_RULES = [
|
|
17807
|
+
/^func\s+(?:\([^)]*\)\s*)?([A-Za-z_]\w*)/,
|
|
17808
|
+
/^type\s+([A-Za-z_]\w*)/
|
|
17809
|
+
];
|
|
17810
|
+
var CLIKE_RULES = [
|
|
17811
|
+
/\b(?:class|interface|enum|record|struct)\s+([A-Za-z_]\w*)/,
|
|
17812
|
+
// access-modifier method shape: `public async Task<Foo> BarBaz(…`
|
|
17813
|
+
/(?:public|private|protected|internal|static|final|virtual|override|sealed|abstract)[\w<>[\],?\s]*?\s([A-Za-z_]\w*)\s*\(/
|
|
17814
|
+
];
|
|
17815
|
+
var RB_RULES = [
|
|
17816
|
+
/^\s*def\s+(?:self\.)?([A-Za-z_]\w*)/,
|
|
17817
|
+
/^\s*(?:class|module)\s+([A-Z]\w*)/
|
|
17818
|
+
];
|
|
17819
|
+
var RS_RULES = [
|
|
17820
|
+
/\bfn\s+([A-Za-z_]\w*)/,
|
|
17821
|
+
/\b(?:struct|enum|trait)\s+([A-Za-z_]\w*)/
|
|
17822
|
+
];
|
|
17823
|
+
var PHP_RULES = [
|
|
17824
|
+
/\bfunction\s+([A-Za-z_]\w*)/,
|
|
17825
|
+
/\bclass\s+([A-Za-z_]\w*)/
|
|
17826
|
+
];
|
|
17827
|
+
var C_RULES = [
|
|
17828
|
+
/^(?:static\s+|inline\s+|extern\s+|constexpr\s+)*(?:struct\s+|enum\s+|union\s+|unsigned\s+|const\s+)*[A-Za-z_]\w*(?:\s*[*&]+\s*|\s+)([A-Za-z_]\w*)\s*\([^;]*$/,
|
|
17829
|
+
/\b(?:struct|enum|union|class)\s+([A-Za-z_]\w*)/,
|
|
17830
|
+
/::\s*~?([A-Za-z_]\w*)\s*\([^;]*$/
|
|
17831
|
+
// out-of-line C++ method definition
|
|
17832
|
+
];
|
|
17833
|
+
var SH_RULES = [
|
|
17834
|
+
/^\s*(?:function\s+)?([A-Za-z_]\w*)\s*\(\)\s*\{/,
|
|
17835
|
+
/^function\s+([A-Za-z_]\w*)/
|
|
17836
|
+
];
|
|
17837
|
+
var SQL_RULES = [
|
|
17838
|
+
/\bcreate\s+(?:or\s+replace\s+)?(?:table|view|materialized\s+view|function|procedure|index|trigger|type|policy)\s+(?:if\s+not\s+exists\s+)?(?:[\w".]*\.)?"?([A-Za-z_]\w*)"?/i
|
|
17839
|
+
];
|
|
17840
|
+
var TF_RULES = [
|
|
17841
|
+
/^\s*(?:resource|data)\s+"[^"]+"\s+"([A-Za-z_]\w*)"/,
|
|
17842
|
+
/^\s*(?:module|variable|output)\s+"([A-Za-z_]\w*)"/
|
|
17843
|
+
];
|
|
17844
|
+
var SWIFT_RULES = [
|
|
17845
|
+
/\bfunc\s+([A-Za-z_]\w*)/,
|
|
17846
|
+
/\b(?:class|struct|enum|protocol|extension|actor)\s+([A-Za-z_]\w*)/
|
|
17847
|
+
];
|
|
17848
|
+
var DART_RULES = [
|
|
17849
|
+
/\b(?:class|enum|mixin|extension)\s+([A-Za-z_]\w*)/,
|
|
17850
|
+
/^\s*(?:static\s+)?(?:Future<[^>]*>|Stream<[^>]*>|void|int|double|bool|String|num|dynamic|[A-Z]\w*(?:<[^>]*>)?)\s+([a-z_]\w*)\s*\(/
|
|
17851
|
+
];
|
|
17852
|
+
var LUA_RULES = [
|
|
17853
|
+
/^\s*(?:local\s+)?function\s+(?:[\w.]+[.:])?([A-Za-z_]\w*)/
|
|
17854
|
+
];
|
|
17855
|
+
var EX_RULES = [
|
|
17856
|
+
/^\s*def(?:p|macro)?\s+([a-z_]\w*)/,
|
|
17857
|
+
/^\s*defmodule\s+(?:[\w.]*\.)?([A-Z]\w*)/
|
|
17858
|
+
];
|
|
17859
|
+
var PROTO_RULES = [
|
|
17860
|
+
/^\s*(?:message|service|enum)\s+([A-Za-z_]\w*)/,
|
|
17861
|
+
/^\s*rpc\s+([A-Za-z_]\w*)/
|
|
17862
|
+
];
|
|
17863
|
+
var GRAPHQL_RULES = [
|
|
17864
|
+
/^\s*(?:type|interface|enum|input|union|scalar)\s+([A-Za-z_]\w*)/
|
|
17865
|
+
];
|
|
17866
|
+
var RULES_BY_EXT = {
|
|
17867
|
+
ts: TS_RULES,
|
|
17868
|
+
tsx: TS_RULES,
|
|
17869
|
+
js: TS_RULES,
|
|
17870
|
+
jsx: TS_RULES,
|
|
17871
|
+
mjs: TS_RULES,
|
|
17872
|
+
cjs: TS_RULES,
|
|
17873
|
+
svelte: TS_RULES,
|
|
17874
|
+
vue: TS_RULES,
|
|
17875
|
+
// script blocks
|
|
17876
|
+
py: PY_RULES,
|
|
17877
|
+
go: GO_RULES,
|
|
17878
|
+
java: CLIKE_RULES,
|
|
17879
|
+
cs: CLIKE_RULES,
|
|
17880
|
+
kt: CLIKE_RULES,
|
|
17881
|
+
scala: CLIKE_RULES,
|
|
17882
|
+
rb: RB_RULES,
|
|
17883
|
+
rs: RS_RULES,
|
|
17884
|
+
php: PHP_RULES,
|
|
17885
|
+
c: C_RULES,
|
|
17886
|
+
cpp: C_RULES,
|
|
17887
|
+
cc: C_RULES,
|
|
17888
|
+
h: C_RULES,
|
|
17889
|
+
hpp: C_RULES,
|
|
17890
|
+
sh: SH_RULES,
|
|
17891
|
+
bash: SH_RULES,
|
|
17892
|
+
zsh: SH_RULES,
|
|
17893
|
+
sql: SQL_RULES,
|
|
17894
|
+
tf: TF_RULES,
|
|
17895
|
+
hcl: TF_RULES,
|
|
17896
|
+
swift: SWIFT_RULES,
|
|
17897
|
+
dart: DART_RULES,
|
|
17898
|
+
lua: LUA_RULES,
|
|
17899
|
+
ex: EX_RULES,
|
|
17900
|
+
exs: EX_RULES,
|
|
17901
|
+
proto: PROTO_RULES,
|
|
17902
|
+
graphql: GRAPHQL_RULES,
|
|
17903
|
+
gql: GRAPHQL_RULES
|
|
17904
|
+
};
|
|
17905
|
+
var HUNK_HEADER = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
17906
|
+
function parseDiffSignals(diff) {
|
|
17907
|
+
const addedRanges = [];
|
|
17908
|
+
const touchPoints = [];
|
|
17909
|
+
const deletedLines = [];
|
|
17910
|
+
let newLine = 0;
|
|
17911
|
+
let oldRemaining = 0;
|
|
17912
|
+
let newRemaining = 0;
|
|
17913
|
+
let runStart = -1;
|
|
17914
|
+
let deletionRun = false;
|
|
17915
|
+
const closeAddedRun = () => {
|
|
17916
|
+
if (runStart >= 0) addedRanges.push([runStart, newLine - 1]);
|
|
17917
|
+
runStart = -1;
|
|
17918
|
+
};
|
|
17919
|
+
const closeDeletionRun = () => {
|
|
17920
|
+
if (deletionRun) touchPoints.push(Math.max(1, newLine));
|
|
17921
|
+
deletionRun = false;
|
|
17922
|
+
};
|
|
17923
|
+
for (const line of diff.split("\n")) {
|
|
17924
|
+
const inHunk = oldRemaining > 0 || newRemaining > 0;
|
|
17925
|
+
if (!inHunk) {
|
|
17926
|
+
closeAddedRun();
|
|
17927
|
+
closeDeletionRun();
|
|
17928
|
+
const header = HUNK_HEADER.exec(line);
|
|
17929
|
+
if (header) {
|
|
17930
|
+
newLine = parseInt(header[3], 10);
|
|
17931
|
+
oldRemaining = header[2] === void 0 ? 1 : parseInt(header[2], 10);
|
|
17932
|
+
newRemaining = header[4] === void 0 ? 1 : parseInt(header[4], 10);
|
|
17933
|
+
if (newRemaining === 0) newLine = Math.max(1, newLine);
|
|
17934
|
+
}
|
|
17935
|
+
continue;
|
|
17936
|
+
}
|
|
17937
|
+
if (line.startsWith("\\")) continue;
|
|
17938
|
+
if (line.startsWith("+") && newRemaining > 0) {
|
|
17939
|
+
deletionRun = false;
|
|
17940
|
+
if (runStart < 0) runStart = newLine;
|
|
17941
|
+
newLine++;
|
|
17942
|
+
newRemaining--;
|
|
17943
|
+
continue;
|
|
17944
|
+
}
|
|
17945
|
+
if (line.startsWith("-") && oldRemaining > 0) {
|
|
17946
|
+
closeAddedRun();
|
|
17947
|
+
deletionRun = true;
|
|
17948
|
+
deletedLines.push(line.slice(1));
|
|
17949
|
+
oldRemaining--;
|
|
17950
|
+
continue;
|
|
17951
|
+
}
|
|
17952
|
+
closeAddedRun();
|
|
17953
|
+
closeDeletionRun();
|
|
17954
|
+
newLine++;
|
|
17955
|
+
if (oldRemaining > 0) oldRemaining--;
|
|
17956
|
+
if (newRemaining > 0) newRemaining--;
|
|
17957
|
+
}
|
|
17958
|
+
closeAddedRun();
|
|
17959
|
+
closeDeletionRun();
|
|
17960
|
+
return { addedRanges, touchPoints, deletedLines };
|
|
17961
|
+
}
|
|
17962
|
+
function declNameOn(line, rules) {
|
|
17963
|
+
for (const r of rules) {
|
|
17964
|
+
const m = r.exec(line);
|
|
17965
|
+
if (m?.[1]) return m[1];
|
|
17966
|
+
}
|
|
17967
|
+
return null;
|
|
17968
|
+
}
|
|
17969
|
+
function acceptable(name) {
|
|
17970
|
+
if (!name || !IDENTIFIER.test(name) || STOPLIST.has(name.toLowerCase())) return false;
|
|
17971
|
+
if (name.length < 3) return false;
|
|
17972
|
+
if (name.length === 3 && name === name.toLowerCase() && !name.includes("_")) return false;
|
|
17973
|
+
return true;
|
|
17974
|
+
}
|
|
17975
|
+
function isMultiSegment(name) {
|
|
17976
|
+
return /[a-z][A-Z]/.test(name) || name.includes("_");
|
|
17977
|
+
}
|
|
17978
|
+
function extractFileSymbols(path, content, signals) {
|
|
17979
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
17980
|
+
const rules = RULES_BY_EXT[ext];
|
|
17981
|
+
if (!rules) return [];
|
|
17982
|
+
if (signals.addedRanges.length === 0 && signals.touchPoints.length === 0 && signals.deletedLines.length === 0) return [];
|
|
17983
|
+
const lines = content.split("\n");
|
|
17984
|
+
const found = [];
|
|
17985
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17986
|
+
const add = (name) => {
|
|
17987
|
+
if (acceptable(name) && !seen.has(name)) {
|
|
17988
|
+
seen.add(name);
|
|
17989
|
+
found.push(name);
|
|
17990
|
+
}
|
|
17991
|
+
};
|
|
17992
|
+
for (const deleted of signals.deletedLines) {
|
|
17993
|
+
add(declNameOn(deleted, rules));
|
|
17994
|
+
}
|
|
17995
|
+
for (const [start, end] of signals.addedRanges) {
|
|
17996
|
+
for (let n = start; n <= Math.min(end, lines.length); n++) {
|
|
17997
|
+
add(declNameOn(lines[n - 1] ?? "", rules));
|
|
17998
|
+
}
|
|
17999
|
+
}
|
|
18000
|
+
const scanStarts = [
|
|
18001
|
+
...signals.addedRanges.map(([start]) => start),
|
|
18002
|
+
...signals.touchPoints
|
|
18003
|
+
];
|
|
18004
|
+
for (const start of scanStarts) {
|
|
18005
|
+
const floor = Math.max(1, start - ENCLOSING_SCAN_LINES);
|
|
18006
|
+
for (let n = Math.min(start, lines.length); n >= floor; n--) {
|
|
18007
|
+
const name = declNameOn(lines[n - 1] ?? "", rules);
|
|
18008
|
+
if (acceptable(name)) {
|
|
18009
|
+
add(name);
|
|
18010
|
+
break;
|
|
18011
|
+
}
|
|
18012
|
+
}
|
|
18013
|
+
}
|
|
18014
|
+
return found;
|
|
18015
|
+
}
|
|
18016
|
+
function rankSymbols(symbols) {
|
|
18017
|
+
return symbols.map((s, i) => ({ s, i })).sort((a, b) => {
|
|
18018
|
+
const seg = Number(isMultiSegment(b.s)) - Number(isMultiSegment(a.s));
|
|
18019
|
+
if (seg !== 0) return seg;
|
|
18020
|
+
if (b.s.length !== a.s.length) return b.s.length - a.s.length;
|
|
18021
|
+
return a.i - b.i;
|
|
18022
|
+
}).slice(0, MAX_SYMBOLS).map((x) => x.s);
|
|
18023
|
+
}
|
|
18024
|
+
var TEST_PATH = /(^|\/)(tests?|specs?|__tests__|e2e)(\/|$)/i;
|
|
18025
|
+
var TEST_FILE = /(\.(test|spec|e2e)\.[^./]+|_test\.[^./]+|_spec\.rb)$/i;
|
|
18026
|
+
var TEST_PY_PREFIX = /(^|\/)test_[^/]+\.py$/i;
|
|
18027
|
+
var IMPORT_LINE = /^\s*(import\s|from\s+\S+\s+import\s|const\s+.*=\s*require\s*\(|require\s*\(|using\s+[\w.]+;|#include|export\s+\{[^}]*\}\s+from|export\s+\*\s+from)/;
|
|
18028
|
+
var BARE_MEMBER_LINE = /^(type\s+)?[A-Za-z_$][\w$]*\s*,?$/;
|
|
18029
|
+
var GO_IMPORT_PATH_LINE = /^"[^"]+",?$/;
|
|
18030
|
+
var SITE_CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
18031
|
+
"ts",
|
|
18032
|
+
"tsx",
|
|
18033
|
+
"js",
|
|
18034
|
+
"jsx",
|
|
18035
|
+
"mjs",
|
|
18036
|
+
"cjs",
|
|
18037
|
+
"py",
|
|
18038
|
+
"go",
|
|
18039
|
+
"java",
|
|
18040
|
+
"kt",
|
|
18041
|
+
"rb",
|
|
18042
|
+
"rs",
|
|
18043
|
+
"scala",
|
|
18044
|
+
"c",
|
|
18045
|
+
"cpp",
|
|
18046
|
+
"cc",
|
|
18047
|
+
"h",
|
|
18048
|
+
"hpp",
|
|
18049
|
+
"cs",
|
|
18050
|
+
"php",
|
|
18051
|
+
"swift",
|
|
18052
|
+
"dart",
|
|
18053
|
+
"lua",
|
|
18054
|
+
"sh",
|
|
18055
|
+
"bash",
|
|
18056
|
+
"zsh",
|
|
18057
|
+
"svelte",
|
|
18058
|
+
"vue",
|
|
18059
|
+
"ex",
|
|
18060
|
+
"exs",
|
|
18061
|
+
// sql/tf carry REAL call sites (SELECT my_function(...), module.name) —
|
|
18062
|
+
// excluded in an earlier round because of migration-comment noise, which the
|
|
18063
|
+
// COMMENT_LINE filter now handles on its own.
|
|
18064
|
+
"sql",
|
|
18065
|
+
"tf",
|
|
18066
|
+
"hcl"
|
|
18067
|
+
]);
|
|
18068
|
+
var COMMENT_LINE = /^(\/\/|#(?!\[)|\*|\/\*|--\s|<!--)/;
|
|
18069
|
+
function isCodeSiteFile(path) {
|
|
18070
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
18071
|
+
return SITE_CODE_EXTENSIONS.has(ext);
|
|
18072
|
+
}
|
|
18073
|
+
function isTestPath(path) {
|
|
18074
|
+
return TEST_PATH.test(path) || TEST_FILE.test(path) || TEST_PY_PREFIX.test(path);
|
|
18075
|
+
}
|
|
18076
|
+
function parseRgLine(line) {
|
|
18077
|
+
const first = line.indexOf(":");
|
|
18078
|
+
if (first <= 0) return null;
|
|
18079
|
+
const second = line.indexOf(":", first + 1);
|
|
18080
|
+
if (second < 0) return null;
|
|
18081
|
+
const n = parseInt(line.slice(first + 1, second), 10);
|
|
18082
|
+
if (!Number.isFinite(n) || n < 1) return null;
|
|
18083
|
+
return { file: line.slice(0, first), line: n, text: line.slice(second + 1) };
|
|
18084
|
+
}
|
|
18085
|
+
var isWordChar = (c) => c !== void 0 && /[A-Za-z0-9_]/.test(c);
|
|
18086
|
+
function wordHit(text, symbol) {
|
|
18087
|
+
let from = 0;
|
|
18088
|
+
for (; ; ) {
|
|
18089
|
+
const at = text.indexOf(symbol, from);
|
|
18090
|
+
if (at < 0) return false;
|
|
18091
|
+
const before = at === 0 ? void 0 : text[at - 1];
|
|
18092
|
+
const after = text[at + symbol.length];
|
|
18093
|
+
if (!isWordChar(before) && !isWordChar(after)) return true;
|
|
18094
|
+
from = at + 1;
|
|
18095
|
+
}
|
|
18096
|
+
}
|
|
18097
|
+
function isContractLine(text, symbol) {
|
|
18098
|
+
for (const kw of ["implements", "extends"]) {
|
|
18099
|
+
let from = 0;
|
|
18100
|
+
for (; ; ) {
|
|
18101
|
+
const at = text.indexOf(kw, from);
|
|
18102
|
+
if (at < 0) break;
|
|
18103
|
+
from = at + 1;
|
|
18104
|
+
const before = at === 0 ? void 0 : text[at - 1];
|
|
18105
|
+
const after = text[at + kw.length];
|
|
18106
|
+
if (isWordChar(before) || isWordChar(after)) continue;
|
|
18107
|
+
let clause = text.slice(at + kw.length);
|
|
18108
|
+
const stop = Math.min(
|
|
18109
|
+
...[clause.indexOf(";"), clause.indexOf("{")].filter((i) => i >= 0)
|
|
18110
|
+
);
|
|
18111
|
+
if (Number.isFinite(stop)) clause = clause.slice(0, stop);
|
|
18112
|
+
if (wordHit(clause, symbol)) return true;
|
|
18113
|
+
}
|
|
18114
|
+
}
|
|
18115
|
+
return false;
|
|
18116
|
+
}
|
|
18117
|
+
function partitionSites(rgLines, symbols, opts) {
|
|
18118
|
+
const hits = [];
|
|
18119
|
+
const hitCount = /* @__PURE__ */ new Map();
|
|
18120
|
+
for (const line of rgLines) {
|
|
18121
|
+
const hit = parseRgLine(line);
|
|
18122
|
+
if (!hit) continue;
|
|
18123
|
+
const matched = symbols.filter((s) => wordHit(hit.text, s));
|
|
18124
|
+
for (const s of matched) hitCount.set(s, (hitCount.get(s) ?? 0) + 1);
|
|
18125
|
+
if (matched.length === 0) continue;
|
|
18126
|
+
hits.push({ ...hit, symbol: matched[0] });
|
|
18127
|
+
}
|
|
18128
|
+
hits.sort((a, b) => a.file < b.file ? -1 : a.file > b.file ? 1 : a.line - b.line);
|
|
18129
|
+
const dropped = symbols.filter((s) => (hitCount.get(s) ?? 0) > MAX_HITS_PER_SYMBOL);
|
|
18130
|
+
const droppedSet = new Set(dropped);
|
|
18131
|
+
const perFile = /* @__PURE__ */ new Map();
|
|
18132
|
+
const callers = [];
|
|
18133
|
+
const tests = [];
|
|
18134
|
+
for (const h of hits) {
|
|
18135
|
+
if (droppedSet.has(h.symbol)) continue;
|
|
18136
|
+
if (opts.sentPaths.has(h.file)) continue;
|
|
18137
|
+
if (opts.isExcluded(h.file)) continue;
|
|
18138
|
+
if (!isCodeSiteFile(h.file)) continue;
|
|
18139
|
+
const text = h.text.trim();
|
|
18140
|
+
if (text.length === 0 || IMPORT_LINE.test(h.text)) continue;
|
|
18141
|
+
if (COMMENT_LINE.test(text)) continue;
|
|
18142
|
+
if (BARE_MEMBER_LINE.test(text) || GO_IMPORT_PATH_LINE.test(text)) continue;
|
|
18143
|
+
const n = perFile.get(h.file) ?? 0;
|
|
18144
|
+
if (n >= MAX_SITES_PER_FILE) continue;
|
|
18145
|
+
const site = {
|
|
18146
|
+
file: h.file,
|
|
18147
|
+
line: h.line,
|
|
18148
|
+
text: text.slice(0, SITE_TEXT_MAX),
|
|
18149
|
+
symbol: h.symbol,
|
|
18150
|
+
// R2 falls out of R1 for free: a word search for `Sym` already matches
|
|
18151
|
+
// `implements Sym` / `extends Sym` lines — classification is all R2 is.
|
|
18152
|
+
...isContractLine(text, h.symbol) ? { kind: "contract" } : {}
|
|
18153
|
+
};
|
|
18154
|
+
if (isTestPath(h.file)) {
|
|
18155
|
+
if (tests.length < MAX_TEST_SLOTS) {
|
|
18156
|
+
tests.push(site);
|
|
18157
|
+
perFile.set(h.file, n + 1);
|
|
18158
|
+
}
|
|
18159
|
+
} else if (callers.length + tests.length < MAX_SITES) {
|
|
18160
|
+
callers.push(site);
|
|
18161
|
+
perFile.set(h.file, n + 1);
|
|
18162
|
+
}
|
|
18163
|
+
}
|
|
18164
|
+
while (callers.length + tests.length > MAX_SITES) callers.pop();
|
|
18165
|
+
return { callers, tests, dropped };
|
|
18166
|
+
}
|
|
18167
|
+
function buildRepoContext(input) {
|
|
18168
|
+
const started = Date.now();
|
|
18169
|
+
let signalsByPath;
|
|
18170
|
+
if (input.signalsByPath) {
|
|
18171
|
+
if (input.signalsByPath.size === 0) return { state: "absent", reason: "no-diffs" };
|
|
18172
|
+
signalsByPath = input.signalsByPath;
|
|
18173
|
+
} else {
|
|
18174
|
+
if (input.diffs.length === 0) return { state: "absent", reason: "no-diffs" };
|
|
18175
|
+
signalsByPath = /* @__PURE__ */ new Map();
|
|
18176
|
+
for (const d of input.diffs) signalsByPath.set(d.path, parseDiffSignals(d.diff));
|
|
18177
|
+
}
|
|
18178
|
+
const collected = [];
|
|
18179
|
+
const unsupported = /* @__PURE__ */ new Set();
|
|
18180
|
+
const noteUnsupported = (path) => {
|
|
18181
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
18182
|
+
if (ext && !RULES_BY_EXT[ext]) unsupported.add(ext);
|
|
18183
|
+
};
|
|
18184
|
+
const deltaPathSet = new Set(input.deltaFiles.map((f) => f.path));
|
|
18185
|
+
for (const f of input.deltaFiles) {
|
|
18186
|
+
const signals = signalsByPath.get(f.path);
|
|
18187
|
+
if (!signals) continue;
|
|
18188
|
+
noteUnsupported(f.path);
|
|
18189
|
+
collected.push(...extractFileSymbols(f.path, f.content, signals));
|
|
18190
|
+
}
|
|
18191
|
+
for (const [path, signals] of signalsByPath) {
|
|
18192
|
+
if (deltaPathSet.has(path)) continue;
|
|
18193
|
+
if (signals.deletedLines.length > 0) {
|
|
18194
|
+
noteUnsupported(path);
|
|
18195
|
+
collected.push(...extractFileSymbols(path, "", signals));
|
|
18196
|
+
}
|
|
18197
|
+
}
|
|
18198
|
+
const unsupportedExts = [...unsupported].sort().slice(0, 8);
|
|
18199
|
+
const audit = unsupportedExts.length > 0 ? { unsupported_exts: unsupportedExts } : {};
|
|
18200
|
+
const symbols = rankSymbols([...new Set(collected)]);
|
|
18201
|
+
if (symbols.length === 0) {
|
|
18202
|
+
const everySupportedFileFoundNothing = unsupportedExts.length > 0;
|
|
18203
|
+
return {
|
|
18204
|
+
state: "absent",
|
|
18205
|
+
reason: everySupportedFileFoundNothing ? "unsupported-language" : "no-symbols",
|
|
18206
|
+
...audit
|
|
18207
|
+
};
|
|
18208
|
+
}
|
|
18209
|
+
const args = [
|
|
18210
|
+
"-n",
|
|
18211
|
+
"-w",
|
|
18212
|
+
"-F",
|
|
18213
|
+
"--no-heading",
|
|
18214
|
+
"--color",
|
|
18215
|
+
"never",
|
|
18216
|
+
// ⚠ NO `--sort path` — it single-threads rg, and on a large worktree that
|
|
18217
|
+
// is the difference between 17ms and a timeout. Determinism is restored by
|
|
18218
|
+
// sorting the hits in partitionSites instead.
|
|
18219
|
+
//
|
|
18220
|
+
// `-m 8` bounds output PER FILE (shared across all patterns), so a noisy
|
|
18221
|
+
// repo cannot blow the 4MB read buffer and turn the whole feature into
|
|
18222
|
+
// `absent/error`. Cost, accepted: the frequency gate sees per-file-capped
|
|
18223
|
+
// counts, so a symbol concentrated in a handful of files can slip a gate
|
|
18224
|
+
// a full count would have tripped — but the ≤3-sites-per-file cap already
|
|
18225
|
+
// bounds exactly that shape's damage; the gate exists for the many-file
|
|
18226
|
+
// 'init' shape, which 8-per-file still trips (>6 files ⇒ >50).
|
|
18227
|
+
"-m",
|
|
18228
|
+
"8",
|
|
18229
|
+
"--max-columns",
|
|
18230
|
+
"300",
|
|
18231
|
+
"--max-columns-preview",
|
|
18232
|
+
...symbols.flatMap((s) => ["-e", s]),
|
|
18233
|
+
"-g",
|
|
18234
|
+
"!**/{dist,build,out,vendor,node_modules,.git,coverage,target,__pycache__}/**",
|
|
18235
|
+
"./"
|
|
18236
|
+
];
|
|
18237
|
+
let res = null;
|
|
18238
|
+
for (const inv of rgInvocations()) {
|
|
18239
|
+
res = (0, import_node_child_process7.spawnSync)(inv.cmd, args, {
|
|
18240
|
+
...inv.argv0 ? { argv0: inv.argv0 } : {},
|
|
18241
|
+
cwd: input.cwd ?? process.cwd(),
|
|
18242
|
+
timeout: input.timeoutMs ?? RG_TIMEOUT_MS,
|
|
18243
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
18244
|
+
encoding: "utf8"
|
|
18245
|
+
});
|
|
18246
|
+
if (res.error?.code !== "ENOENT") break;
|
|
18247
|
+
}
|
|
18248
|
+
if (!res || res.error?.code === "ENOENT") {
|
|
18249
|
+
return { state: "absent", reason: "no-tool", symbols, ...audit };
|
|
18250
|
+
}
|
|
18251
|
+
if (res.error) {
|
|
18252
|
+
const code = res.error.code;
|
|
18253
|
+
if (code === "ETIMEDOUT") return { state: "absent", reason: "timeout", symbols, ...audit };
|
|
18254
|
+
return { state: "absent", reason: "error", symbols, ...audit };
|
|
18255
|
+
}
|
|
18256
|
+
if (res.signal) return { state: "absent", reason: "timeout", symbols, ...audit };
|
|
18257
|
+
if (res.status !== 0 && res.status !== 1) return { state: "absent", reason: "error", symbols, ...audit };
|
|
18258
|
+
const lines = (res.stdout ?? "").split("\n").map((l) => l.replace(/^\.\//, "")).filter(Boolean);
|
|
18259
|
+
const { callers, tests, dropped } = partitionSites(lines, symbols, {
|
|
18260
|
+
sentPaths: input.sentPaths,
|
|
18261
|
+
isExcluded: input.isExcluded
|
|
18262
|
+
});
|
|
18263
|
+
if (callers.length === 0 && tests.length === 0) {
|
|
18264
|
+
return {
|
|
18265
|
+
state: "absent",
|
|
18266
|
+
reason: "no-sites",
|
|
18267
|
+
symbols,
|
|
18268
|
+
...dropped.length > 0 ? { dropped_symbols: dropped } : {},
|
|
18269
|
+
...audit,
|
|
18270
|
+
elapsed_ms: Date.now() - started
|
|
18271
|
+
};
|
|
18272
|
+
}
|
|
18273
|
+
return {
|
|
18274
|
+
state: "ok",
|
|
18275
|
+
symbols,
|
|
18276
|
+
...dropped.length > 0 ? { dropped_symbols: dropped } : {},
|
|
18277
|
+
...audit,
|
|
18278
|
+
callers,
|
|
18279
|
+
tests,
|
|
18280
|
+
elapsed_ms: Date.now() - started
|
|
18281
|
+
};
|
|
18282
|
+
}
|
|
18283
|
+
var MAX_EXCERPTS = 12;
|
|
18284
|
+
var MAX_EXCERPTS_PER_FILE = 2;
|
|
18285
|
+
var EXCERPT_MAX_LINES = 30;
|
|
18286
|
+
var EXCERPT_MAX_CHARS = 2400;
|
|
18287
|
+
var EXCERPT_TOTAL_BYTES = 24576;
|
|
18288
|
+
var EXCERPT_DECL_SCAN = 40;
|
|
18289
|
+
function extractEnclosingExcerpt(content, siteLine, path) {
|
|
18290
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
18291
|
+
const rules = RULES_BY_EXT[ext] ?? [];
|
|
18292
|
+
const lines = content.split("\n");
|
|
18293
|
+
if (siteLine < 1 || siteLine > lines.length) return null;
|
|
18294
|
+
let declStart = null;
|
|
18295
|
+
const floor = Math.max(1, siteLine - EXCERPT_DECL_SCAN);
|
|
18296
|
+
for (let n = siteLine; n >= floor; n--) {
|
|
18297
|
+
if (declNameOn(lines[n - 1] ?? "", rules) !== null) {
|
|
18298
|
+
declStart = n;
|
|
18299
|
+
break;
|
|
18300
|
+
}
|
|
18301
|
+
}
|
|
18302
|
+
let start;
|
|
18303
|
+
if (declStart !== null && siteLine - declStart < EXCERPT_MAX_LINES) {
|
|
18304
|
+
start = declStart;
|
|
18305
|
+
} else {
|
|
18306
|
+
start = Math.max(1, siteLine - (EXCERPT_MAX_LINES - 6));
|
|
18307
|
+
}
|
|
18308
|
+
const end = Math.min(lines.length, start + EXCERPT_MAX_LINES - 1);
|
|
18309
|
+
const text = lines.slice(start - 1, end).join("\n").slice(0, EXCERPT_MAX_CHARS);
|
|
18310
|
+
return { start_line: start, text };
|
|
18311
|
+
}
|
|
18312
|
+
function upgradeToExcerpts(rc, opts) {
|
|
18313
|
+
if (rc.state !== "ok") return;
|
|
18314
|
+
const ranked = [
|
|
18315
|
+
...(rc.callers ?? []).filter((s) => s.kind === "contract"),
|
|
18316
|
+
...rc.tests ?? [],
|
|
18317
|
+
...(rc.callers ?? []).filter((s) => s.kind !== "contract")
|
|
18318
|
+
];
|
|
18319
|
+
const perFile = /* @__PURE__ */ new Map();
|
|
18320
|
+
const contentCache = /* @__PURE__ */ new Map();
|
|
18321
|
+
const excerpts = [];
|
|
18322
|
+
let totalBytes = 0;
|
|
18323
|
+
for (const site of ranked) {
|
|
18324
|
+
if (excerpts.length >= MAX_EXCERPTS) break;
|
|
18325
|
+
const used = perFile.get(site.file) ?? 0;
|
|
18326
|
+
if (used >= MAX_EXCERPTS_PER_FILE) continue;
|
|
18327
|
+
if (!contentCache.has(site.file)) contentCache.set(site.file, opts.readFile(site.file));
|
|
18328
|
+
const content = contentCache.get(site.file);
|
|
18329
|
+
if (content === null || content === void 0) continue;
|
|
18330
|
+
const ex = extractEnclosingExcerpt(content, site.line, site.file);
|
|
18331
|
+
if (!ex) continue;
|
|
18332
|
+
if (totalBytes + ex.text.length > EXCERPT_TOTAL_BYTES) break;
|
|
18333
|
+
excerpts.push({
|
|
18334
|
+
file: site.file,
|
|
18335
|
+
start_line: ex.start_line,
|
|
18336
|
+
symbol: site.symbol,
|
|
18337
|
+
kind: site.kind === "contract" ? "contract" : isTestPath(site.file) ? "test" : "caller",
|
|
18338
|
+
text: ex.text
|
|
18339
|
+
});
|
|
18340
|
+
totalBytes += ex.text.length;
|
|
18341
|
+
perFile.set(site.file, used + 1);
|
|
18342
|
+
}
|
|
18343
|
+
if (excerpts.length > 0) rc.excerpts = excerpts;
|
|
18344
|
+
}
|
|
18345
|
+
function describeRepoContext(rc) {
|
|
18346
|
+
if (rc.state !== "ok") {
|
|
18347
|
+
const exts = rc.unsupported_exts?.length ? ` \xB7 no rules for: ${rc.unsupported_exts.join(", ")}` : "";
|
|
18348
|
+
return `absent (${rc.reason ?? "unknown"})${exts}`;
|
|
18349
|
+
}
|
|
18350
|
+
const parts = [
|
|
18351
|
+
`${rc.symbols?.length ?? 0} symbol(s) \u2192 ${rc.callers?.length ?? 0} caller(s) \xB7 ${rc.tests?.length ?? 0} test(s)`
|
|
18352
|
+
];
|
|
18353
|
+
if (rc.excerpts?.length) parts.push(`${rc.excerpts.length} excerpt(s)`);
|
|
18354
|
+
if (rc.dropped_symbols?.length) parts.push(`dropped too-common: ${rc.dropped_symbols.join(", ")}`);
|
|
18355
|
+
if (rc.unsupported_exts?.length) parts.push(`no rules for: ${rc.unsupported_exts.join(", ")}`);
|
|
18356
|
+
if (typeof rc.elapsed_ms === "number") parts.push(`${rc.elapsed_ms}ms`);
|
|
18357
|
+
return parts.join(" \xB7 ");
|
|
18358
|
+
}
|
|
18359
|
+
|
|
17501
18360
|
// src/commands/analyze/evidence-log.ts
|
|
17502
18361
|
function list(paths, cap = 12) {
|
|
17503
18362
|
if (paths.length === 0) return "(none)";
|
|
@@ -17508,35 +18367,36 @@ function row(label2, value) {
|
|
|
17508
18367
|
return ` \u25B8 ${label2.padEnd(10)} ${value}
|
|
17509
18368
|
`;
|
|
17510
18369
|
}
|
|
17511
|
-
function formatRunEvidence(
|
|
18370
|
+
function formatRunEvidence(run2, startedAt) {
|
|
17512
18371
|
const ms = Date.now() - startedAt;
|
|
17513
|
-
const sent =
|
|
17514
|
-
const context =
|
|
18372
|
+
const sent = run2.codeDelta.files.filter((f) => f.role !== "context").map((f) => f.path);
|
|
18373
|
+
const context = run2.codeDelta.files.filter((f) => f.role === "context").map((f) => f.path);
|
|
17515
18374
|
let out = ` \u2500\u2500 what verity saw \u2500\u2500
|
|
17516
18375
|
`;
|
|
17517
|
-
out += row("turn", `${
|
|
17518
|
-
out += row("reached", `${
|
|
17519
|
-
if (
|
|
17520
|
-
const f =
|
|
18376
|
+
out += row("turn", `${run2.turnId || "(unminted)"}${run2.sessionId ? ` \xB7 session ${run2.sessionId}` : ""}`);
|
|
18377
|
+
out += row("reached", `${run2.phaseReached || "(none)"}${run2.skipReason ? ` \xB7 SKIPPED: ${run2.skipReason}` : ""} \xB7 ${ms}ms`);
|
|
18378
|
+
if (run2.treeFrame) {
|
|
18379
|
+
const f = run2.treeFrame;
|
|
17521
18380
|
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
18381
|
}
|
|
17523
|
-
out += row("changed", `${
|
|
17524
|
-
const done = (phase) =>
|
|
18382
|
+
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}`);
|
|
18383
|
+
const done = (phase) => run2.phasesCompleted.includes(phase);
|
|
17525
18384
|
const ifDone = (phase, value) => done(phase) ? value : "?";
|
|
17526
|
-
const md =
|
|
18385
|
+
const md = run2.modeDecision;
|
|
17527
18386
|
if (md) {
|
|
17528
18387
|
const how = md.forced ? "forced by --mode" : `predicted=${md.predicted ?? "none"} \u2192 ${md.resolved}`;
|
|
17529
18388
|
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
18389
|
} else {
|
|
17531
|
-
out += row("mode", `? (this run stopped in ${
|
|
18390
|
+
out += row("mode", `? (this run stopped in ${run2.phaseReached || "no phase"}, before the mode was decided)`);
|
|
17532
18391
|
}
|
|
17533
|
-
out += row("signals", `baseline=${ifDone("bootstrap",
|
|
18392
|
+
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
18393
|
if (!done("intentInputs")) {
|
|
17535
|
-
out += row("", `(\`?\` = the phase that determines it did not complete \u2014 this run stopped in ${
|
|
18394
|
+
out += row("", `(\`?\` = the phase that determines it did not complete \u2014 this run stopped in ${run2.phaseReached})`);
|
|
17536
18395
|
}
|
|
17537
18396
|
out += row("sent", `${sent.length} \xB7 ${list(sent)}`);
|
|
17538
18397
|
if (context.length > 0) out += row("context", `${context.length} \xB7 ${list(context)}`);
|
|
17539
|
-
|
|
18398
|
+
if (run2.repoContext) out += row("repo", describeRepoContext(run2.repoContext));
|
|
18399
|
+
const withheld = run2.reviewCoverage.notReviewed;
|
|
17540
18400
|
if (withheld.length > 0) {
|
|
17541
18401
|
const byReason = /* @__PURE__ */ new Map();
|
|
17542
18402
|
for (const w of withheld) {
|
|
@@ -17549,18 +18409,18 @@ function formatRunEvidence(run, startedAt) {
|
|
|
17549
18409
|
first = false;
|
|
17550
18410
|
}
|
|
17551
18411
|
} else {
|
|
17552
|
-
const sentSet = new Set(
|
|
17553
|
-
const notSent =
|
|
18412
|
+
const sentSet = new Set(run2.codeDelta.files.map((f) => f.path));
|
|
18413
|
+
const notSent = run2.changedUniverse.filter((p) => !sentSet.has(p));
|
|
17554
18414
|
if (notSent.length > 0) {
|
|
17555
18415
|
out += row("not sent", `${list(notSent)}`);
|
|
17556
|
-
out += row("", `(stage unknown \u2014 the coverage ledger is built in phase 13, and this run reached ${
|
|
17557
|
-
} else if (
|
|
18416
|
+
out += row("", `(stage unknown \u2014 the coverage ledger is built in phase 13, and this run reached ${run2.phaseReached || "no phase"})`);
|
|
18417
|
+
} else if (run2.changedUniverse.length > 0) {
|
|
17558
18418
|
out += row("withheld", "(nothing \u2014 every changed file was reviewed)");
|
|
17559
18419
|
}
|
|
17560
18420
|
}
|
|
17561
|
-
const cov =
|
|
18421
|
+
const cov = run2.foldResult?.coverage;
|
|
17562
18422
|
if (cov) {
|
|
17563
|
-
const delegated =
|
|
18423
|
+
const delegated = run2.foldResult.authored.filter((a) => a.owner === "subagent").length;
|
|
17564
18424
|
if (cov.dispatched > 0 || cov.subagentFiles > 0 || cov.subagentSkipped > 0) {
|
|
17565
18425
|
out += row("delegated", `${cov.dispatched} dispatched \xB7 ${cov.subagentFiles} agent log(s) read \xB7 ${delegated} path(s) attributed to subagents`);
|
|
17566
18426
|
}
|
|
@@ -17574,52 +18434,52 @@ function formatRunEvidence(run, startedAt) {
|
|
|
17574
18434
|
out += row("", `${cov.outsideRepo} authored path(s) refused as outside the repo`);
|
|
17575
18435
|
}
|
|
17576
18436
|
}
|
|
17577
|
-
if (
|
|
17578
|
-
const shown =
|
|
18437
|
+
if (run2.foldResult?.tools?.length) {
|
|
18438
|
+
const shown = run2.foldResult.tools.slice(0, 6).map((t) => {
|
|
17579
18439
|
const outcome = t.failed > 0 ? `${t.failed} failed` : t.last_status === 0 ? "ok" : "?";
|
|
17580
18440
|
const where = t.targets.length > 0 ? ` \u2192 ${t.targets.slice(0, 2).join(", ")}` : "";
|
|
17581
18441
|
return `${t.runs}\xD7 ${t.name} (${outcome})${where}`;
|
|
17582
18442
|
});
|
|
17583
|
-
const more =
|
|
18443
|
+
const more = run2.foldResult.tools.length > 6 ? ` \u2026 +${run2.foldResult.tools.length - 6} more` : "";
|
|
17584
18444
|
out += row("tools", shown.join(" \xB7 ") + more);
|
|
17585
|
-
if (
|
|
17586
|
-
out += row("", `\u26A0 ${
|
|
18445
|
+
if (run2.foldResult.coverage.toolNamesDropped > 0) {
|
|
18446
|
+
out += row("", `\u26A0 ${run2.foldResult.coverage.toolNamesDropped} tool name(s) refused by the cap`);
|
|
17587
18447
|
}
|
|
17588
18448
|
}
|
|
17589
|
-
if (
|
|
17590
|
-
const t =
|
|
18449
|
+
if (run2.foldResult?.tasks?.length) {
|
|
18450
|
+
const t = run2.foldResult.tasks;
|
|
17591
18451
|
const done2 = t.filter((x) => x.status === "completed").length;
|
|
17592
18452
|
out += row("tasks", `${t.length} \xB7 ${done2} completed \xB7 ` + list(t.slice(0, 4).map((x) => `#${x.id} ${x.name} [${x.status}]`), 4));
|
|
17593
18453
|
}
|
|
17594
|
-
if (
|
|
17595
|
-
const readThisSession = new Set(
|
|
17596
|
-
const labelled =
|
|
18454
|
+
if (run2.specs?.length) {
|
|
18455
|
+
const readThisSession = new Set(run2.actionSummary?.files_read ?? []);
|
|
18456
|
+
const labelled = run2.specs.map(
|
|
17597
18457
|
(s) => `${s.path}${readThisSession.has(s.path) ? " (read)" : " (positional)"}`
|
|
17598
18458
|
);
|
|
17599
|
-
out += row("specs", `${
|
|
18459
|
+
out += row("specs", `${run2.specs.length} \xB7 ${list(labelled, 5)}`);
|
|
17600
18460
|
}
|
|
17601
|
-
if (
|
|
17602
|
-
out += row("static", `${
|
|
18461
|
+
if (run2.staticResults.findings.length > 0) {
|
|
18462
|
+
out += row("static", `${run2.staticResults.findings.length} finding(s) from ${run2.staticResults.summary.tools_run.join(", ") || "no tools"}`);
|
|
17603
18463
|
}
|
|
17604
|
-
if (
|
|
17605
|
-
out += row("verdict",
|
|
18464
|
+
if (run2.decision && run2.decision !== "(unrecognised)") {
|
|
18465
|
+
out += row("verdict", run2.decision + (run2.silenced ? ` \xB7 agent channel silenced (${run2.silenced})` : ""));
|
|
17606
18466
|
}
|
|
17607
18467
|
return out;
|
|
17608
18468
|
}
|
|
17609
|
-
function installRunEvidence(
|
|
18469
|
+
function installRunEvidence(run2) {
|
|
17610
18470
|
const startedAt = Date.now();
|
|
17611
18471
|
process.on("exit", () => {
|
|
17612
18472
|
try {
|
|
17613
|
-
logToFileOnly(formatRunEvidence(
|
|
18473
|
+
logToFileOnly(formatRunEvidence(run2, startedAt));
|
|
17614
18474
|
} catch {
|
|
17615
18475
|
}
|
|
17616
18476
|
});
|
|
17617
18477
|
}
|
|
17618
18478
|
|
|
17619
18479
|
// src/lib/git-frame.ts
|
|
17620
|
-
var
|
|
18480
|
+
var import_node_child_process8 = require("node:child_process");
|
|
17621
18481
|
var import_node_fs24 = require("node:fs");
|
|
17622
|
-
var
|
|
18482
|
+
var import_node_os4 = require("node:os");
|
|
17623
18483
|
var import_node_path18 = require("node:path");
|
|
17624
18484
|
var import_node_path19 = require("node:path");
|
|
17625
18485
|
var VALUE_TOKEN = `(?:'[^']*'|"[^"]*"|\\S+)`;
|
|
@@ -17667,14 +18527,14 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
|
|
|
17667
18527
|
if (!m) continue;
|
|
17668
18528
|
named = true;
|
|
17669
18529
|
if (m[1] === void 0) {
|
|
17670
|
-
dir = (0,
|
|
18530
|
+
dir = (0, import_node_os4.homedir)();
|
|
17671
18531
|
continue;
|
|
17672
18532
|
}
|
|
17673
18533
|
const raw = unquote(m[1]);
|
|
17674
18534
|
if (SHELL_DYNAMIC.test(raw) || raw === "-") {
|
|
17675
18535
|
return { dir: null, named: true, unresolvable: `cd target not statically resolvable: ${raw}` };
|
|
17676
18536
|
}
|
|
17677
|
-
const expanded = raw === "~" ? (0,
|
|
18537
|
+
const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
|
|
17678
18538
|
dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
|
|
17679
18539
|
}
|
|
17680
18540
|
const seg = segments[segmentIndex];
|
|
@@ -17693,7 +18553,7 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
|
|
|
17693
18553
|
if (SHELL_DYNAMIC.test(raw)) {
|
|
17694
18554
|
return { dir: null, named: true, unresolvable: `-C target not statically resolvable: ${raw}` };
|
|
17695
18555
|
}
|
|
17696
|
-
const expanded = raw === "~" ? (0,
|
|
18556
|
+
const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
|
|
17697
18557
|
dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
|
|
17698
18558
|
}
|
|
17699
18559
|
}
|
|
@@ -17750,7 +18610,7 @@ function parsePushTarget(segment) {
|
|
|
17750
18610
|
}
|
|
17751
18611
|
function gitAt(dir, args) {
|
|
17752
18612
|
try {
|
|
17753
|
-
return (0,
|
|
18613
|
+
return (0, import_node_child_process8.execFileSync)("git", args, { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
17754
18614
|
} catch {
|
|
17755
18615
|
return "";
|
|
17756
18616
|
}
|
|
@@ -17889,16 +18749,50 @@ function rangeFiles(frame, range) {
|
|
|
17889
18749
|
}
|
|
17890
18750
|
return out.split("\n").filter((l) => l.length > 0).filter((f) => !isVerityOwnedPath(f));
|
|
17891
18751
|
}
|
|
17892
|
-
function
|
|
17893
|
-
|
|
17894
|
-
|
|
17895
|
-
|
|
17896
|
-
|
|
17897
|
-
|
|
17898
|
-
|
|
17899
|
-
|
|
17900
|
-
|
|
17901
|
-
|
|
18752
|
+
function rangeChangeSignals(frame, range, paths) {
|
|
18753
|
+
const out = /* @__PURE__ */ new Map();
|
|
18754
|
+
if (range.kind === "nothing" || paths.length === 0) return out;
|
|
18755
|
+
const args = range.kind === "staged" ? ["diff", "--cached", "--unified=0"] : ["diff", "--unified=0", range.base, range.head === "INDEX" ? "HEAD" : range.head];
|
|
18756
|
+
const diff = frameGit(frame, [...args, "--", ...paths]);
|
|
18757
|
+
let current = null;
|
|
18758
|
+
let oldSide = null;
|
|
18759
|
+
let buf = [];
|
|
18760
|
+
const flush = () => {
|
|
18761
|
+
if (current !== null && buf.length > 0) out.set(current, parseDiffSignals(buf.join("\n")));
|
|
18762
|
+
buf = [];
|
|
18763
|
+
};
|
|
18764
|
+
for (const line of diff.split("\n")) {
|
|
18765
|
+
if (line.startsWith("diff --git ")) {
|
|
18766
|
+
flush();
|
|
18767
|
+
current = null;
|
|
18768
|
+
oldSide = null;
|
|
18769
|
+
continue;
|
|
18770
|
+
}
|
|
18771
|
+
const minusM = /^--- (?:a\/)?(.+)$/.exec(line);
|
|
18772
|
+
if (minusM) {
|
|
18773
|
+
oldSide = minusM[1] === "/dev/null" ? null : minusM[1];
|
|
18774
|
+
continue;
|
|
18775
|
+
}
|
|
18776
|
+
const plusM = /^\+\+\+ (?:b\/)?(.+)$/.exec(line);
|
|
18777
|
+
if (plusM) {
|
|
18778
|
+
current = plusM[1] === "/dev/null" ? oldSide : plusM[1];
|
|
18779
|
+
continue;
|
|
18780
|
+
}
|
|
18781
|
+
if (current !== null) buf.push(line);
|
|
18782
|
+
}
|
|
18783
|
+
flush();
|
|
18784
|
+
return out;
|
|
18785
|
+
}
|
|
18786
|
+
function rangeMessages(frame, range) {
|
|
18787
|
+
if (range.kind === "staged" || range.kind === "nothing" || !range.base) return "";
|
|
18788
|
+
return frameGit(frame, ["log", `${range.base}..${range.head === "INDEX" ? "HEAD" : range.head}`, "--format=%B%x00"]).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
18789
|
+
}
|
|
18790
|
+
function frameTelemetry(frame, range, divergence) {
|
|
18791
|
+
const t = {
|
|
18792
|
+
anchor: frame.anchor,
|
|
18793
|
+
linked_worktree: frame.isLinkedWorktree,
|
|
18794
|
+
range_via: range?.via ?? null,
|
|
18795
|
+
refusal: frame.refusal
|
|
17902
18796
|
};
|
|
17903
18797
|
if (divergence) {
|
|
17904
18798
|
t.root_differs = !!frame.worktreeRoot && !!divergence.actualRoot && realpathOr(frame.worktreeRoot) !== realpathOr(divergence.actualRoot);
|
|
@@ -17948,6 +18842,7 @@ var MAX_COMMANDS = 10;
|
|
|
17948
18842
|
var MAX_COMMAND_CHARS = 80;
|
|
17949
18843
|
var MAX_TOOL_BLOCKS = 200;
|
|
17950
18844
|
var MAX_SUMMARY_BYTES = 4096;
|
|
18845
|
+
var MAX_SEARCHES_DETAIL = 10;
|
|
17951
18846
|
var HOME = process.env.HOME ?? "";
|
|
17952
18847
|
var BASH_INPUT_RE = /^\s*<bash-input>([\s\S]*?)<\/bash-input>/;
|
|
17953
18848
|
var BASH_ECHO_RE = /^\s*<bash-(?:stdout|stderr)>/;
|
|
@@ -18033,6 +18928,7 @@ function buildSummary(lines) {
|
|
|
18033
18928
|
let userCommandsTruncated = false;
|
|
18034
18929
|
let commandsTruncated = false;
|
|
18035
18930
|
let searches = 0;
|
|
18931
|
+
const searchesDetail = [];
|
|
18036
18932
|
let subagents = 0;
|
|
18037
18933
|
let webFetches = 0;
|
|
18038
18934
|
let totalToolCalls = 0;
|
|
@@ -18104,9 +19000,19 @@ function buildSummary(lines) {
|
|
|
18104
19000
|
break;
|
|
18105
19001
|
}
|
|
18106
19002
|
case "Grep":
|
|
18107
|
-
case "Glob":
|
|
19003
|
+
case "Glob": {
|
|
18108
19004
|
searches++;
|
|
19005
|
+
const pattern = typeof input.pattern === "string" ? input.pattern.slice(0, 120) : "";
|
|
19006
|
+
if (pattern && searchesDetail.length < MAX_SEARCHES_DETAIL) {
|
|
19007
|
+
const scopePath = typeof input.path === "string" ? input.path.slice(0, 200) : void 0;
|
|
19008
|
+
searchesDetail.push({
|
|
19009
|
+
tool: toolName,
|
|
19010
|
+
pattern,
|
|
19011
|
+
...scopePath ? { path: scopePath } : {}
|
|
19012
|
+
});
|
|
19013
|
+
}
|
|
18109
19014
|
break;
|
|
19015
|
+
}
|
|
18110
19016
|
case "Agent":
|
|
18111
19017
|
case "Task":
|
|
18112
19018
|
case "Workflow":
|
|
@@ -18141,6 +19047,7 @@ function buildSummary(lines) {
|
|
|
18141
19047
|
...cappedOut(filesCreated, MAX_CREATED_LIST)
|
|
18142
19048
|
],
|
|
18143
19049
|
searches,
|
|
19050
|
+
...searchesDetail.length > 0 ? { searches_detail: searchesDetail } : {},
|
|
18144
19051
|
commands,
|
|
18145
19052
|
...commandsTruncated ? { commands_truncated: true } : {},
|
|
18146
19053
|
user_commands: userCommands,
|
|
@@ -18151,6 +19058,9 @@ function buildSummary(lines) {
|
|
|
18151
19058
|
turn_messages: turnMessages,
|
|
18152
19059
|
turn_duration_ms: turnDurationMs
|
|
18153
19060
|
};
|
|
19061
|
+
if (JSON.stringify(summary).length > MAX_SUMMARY_BYTES) {
|
|
19062
|
+
delete summary.searches_detail;
|
|
19063
|
+
}
|
|
18154
19064
|
if (JSON.stringify(summary).length > MAX_SUMMARY_BYTES) {
|
|
18155
19065
|
summary.commands = [];
|
|
18156
19066
|
summary.commands_truncated = true;
|
|
@@ -18261,13 +19171,13 @@ async function readStopHookStdin() {
|
|
|
18261
19171
|
return empty;
|
|
18262
19172
|
}
|
|
18263
19173
|
}
|
|
18264
|
-
async function bootstrap(
|
|
18265
|
-
const { opts, globals } =
|
|
19174
|
+
async function bootstrap(run2) {
|
|
19175
|
+
const { opts, globals } = run2;
|
|
18266
19176
|
try {
|
|
18267
19177
|
process.chdir(repoRoot());
|
|
18268
19178
|
} catch {
|
|
18269
19179
|
}
|
|
18270
|
-
|
|
19180
|
+
run2.treeFrame = resolveFrame({ command: "", on: [], hookCwd: null }).frame;
|
|
18271
19181
|
const turnId = `t-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
18272
19182
|
let reachability = resolveReachability({
|
|
18273
19183
|
autonomousFlag: process.env.VERITY_AUTONOMOUS === "1" || opts.mode === "autonomous",
|
|
@@ -18281,7 +19191,7 @@ async function bootstrap(run) {
|
|
|
18281
19191
|
const rawSessionId = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
|
|
18282
19192
|
const baselineSessionId = sessionScopeKey(scopeToken, rawSessionId);
|
|
18283
19193
|
if (tokenResult.ok) {
|
|
18284
|
-
|
|
19194
|
+
run2.beacon = {
|
|
18285
19195
|
resolveServiceUrl: async () => {
|
|
18286
19196
|
const u = await resolveServiceUrl(globals.serviceUrl);
|
|
18287
19197
|
return u.ok ? u.data : null;
|
|
@@ -18301,7 +19211,7 @@ async function bootstrap(run) {
|
|
|
18301
19211
|
age_ms: Date.now() - baseline.captured_at
|
|
18302
19212
|
});
|
|
18303
19213
|
}
|
|
18304
|
-
Object.assign(
|
|
19214
|
+
Object.assign(run2, { actionSummary, assistantResponse, baseline, baselineSessionId, reachability, sessionId, stopReason, tokenResult, transcriptPath, turnId });
|
|
18305
19215
|
}
|
|
18306
19216
|
|
|
18307
19217
|
// src/lib/self-scope.ts
|
|
@@ -18454,7 +19364,7 @@ function channelSilence(input) {
|
|
|
18454
19364
|
// src/lib/cli-version.ts
|
|
18455
19365
|
function cliVersion() {
|
|
18456
19366
|
try {
|
|
18457
|
-
return true ? "0.31.1-experimental.
|
|
19367
|
+
return true ? "0.31.1-experimental.83a8619" : "dev";
|
|
18458
19368
|
} catch {
|
|
18459
19369
|
return "dev";
|
|
18460
19370
|
}
|
|
@@ -18494,7 +19404,7 @@ async function sendSkipBeacon(ctx, reason) {
|
|
|
18494
19404
|
}
|
|
18495
19405
|
|
|
18496
19406
|
// src/lib/static-analysis.ts
|
|
18497
|
-
var
|
|
19407
|
+
var import_node_child_process9 = require("node:child_process");
|
|
18498
19408
|
var import_node_fs26 = require("node:fs");
|
|
18499
19409
|
var SEVERITY_ORDER = {
|
|
18500
19410
|
Error: 0,
|
|
@@ -18507,7 +19417,7 @@ var SEVERITY_ORDER = {
|
|
|
18507
19417
|
};
|
|
18508
19418
|
function isCodacyAvailable() {
|
|
18509
19419
|
try {
|
|
18510
|
-
(0,
|
|
19420
|
+
(0, import_node_child_process9.execSync)("which codacy-analysis", { stdio: "pipe" });
|
|
18511
19421
|
return true;
|
|
18512
19422
|
} catch {
|
|
18513
19423
|
return false;
|
|
@@ -18549,7 +19459,7 @@ function runCodacyAnalysis(files) {
|
|
|
18549
19459
|
}
|
|
18550
19460
|
});
|
|
18551
19461
|
if (existingFiles.length === 0) return empty;
|
|
18552
|
-
const proc = (0,
|
|
19462
|
+
const proc = (0, import_node_child_process9.spawnSync)("codacy-analysis", buildAnalyzerArgv(existingFiles), {
|
|
18553
19463
|
encoding: "utf-8",
|
|
18554
19464
|
maxBuffer: 10 * 1024 * 1024
|
|
18555
19465
|
});
|
|
@@ -18560,12 +19470,12 @@ function runCodacyAnalysis(files) {
|
|
|
18560
19470
|
spawnError: proc.error?.message
|
|
18561
19471
|
});
|
|
18562
19472
|
}
|
|
18563
|
-
function interpretAnalyzerRun(
|
|
18564
|
-
const output =
|
|
19473
|
+
function interpretAnalyzerRun(run2) {
|
|
19474
|
+
const output = run2.stdout ?? "";
|
|
18565
19475
|
if (!output.trim()) {
|
|
18566
19476
|
return withFailure(
|
|
18567
|
-
|
|
18568
|
-
|
|
19477
|
+
run2.spawnError ? "spawn_failed" : "no_output",
|
|
19478
|
+
run2.spawnError ?? run2.stderr ?? `exit ${run2.status}`
|
|
18569
19479
|
);
|
|
18570
19480
|
}
|
|
18571
19481
|
let parsed;
|
|
@@ -18706,7 +19616,7 @@ function describeOpenElsewhere(open) {
|
|
|
18706
19616
|
(+${open.length - 5} more)` : "";
|
|
18707
19617
|
return `STILL OPEN ELSEWHERE. ${open.length} finding(s) Verity raised earlier are still on disk in files this run did not review:
|
|
18708
19618
|
${lines.join("\n")}${more}
|
|
18709
|
-
They did not
|
|
19619
|
+
They did not fail this run \u2014 this verdict covers the current change only. The tree is not clean.
|
|
18710
19620
|
Fix them, or record a disposition: verity waive <pattern-id> --file <path> --reason "\u2026"`;
|
|
18711
19621
|
}
|
|
18712
19622
|
|
|
@@ -18732,9 +19642,9 @@ function localOnlyAndExit(staticResults) {
|
|
|
18732
19642
|
});
|
|
18733
19643
|
process.exit(0);
|
|
18734
19644
|
}
|
|
18735
|
-
async function passAndExit(
|
|
18736
|
-
|
|
18737
|
-
const sent = await sendSkipBeacon(
|
|
19645
|
+
async function passAndExit(run2, reason, skip, kindOverride) {
|
|
19646
|
+
run2.skipReason = skip;
|
|
19647
|
+
const sent = await sendSkipBeacon(run2.beacon, skip);
|
|
18738
19648
|
logEvent("skip", { reason: skip, beacon: sent });
|
|
18739
19649
|
const POLICY_SKIPS = /* @__PURE__ */ new Set([
|
|
18740
19650
|
"no-analyzable-files",
|
|
@@ -18749,7 +19659,7 @@ async function passAndExit(run, reason, skip, kindOverride) {
|
|
|
18749
19659
|
"no-delta-since-last-review"
|
|
18750
19660
|
]);
|
|
18751
19661
|
const skipKind = kindOverride ?? (POLICY_SKIPS.has(skip) ? "policy" : "capacity");
|
|
18752
|
-
const changed =
|
|
19662
|
+
const changed = run2.changedUniverse;
|
|
18753
19663
|
const { coverage, unaccounted } = reconcileCoverage(changed, {
|
|
18754
19664
|
reviewed: [],
|
|
18755
19665
|
notReviewed: changed.map((path) => ({ path, reason: skip, stage: "pre-flight", kind: skipKind }))
|
|
@@ -18777,10 +19687,10 @@ async function passAndExit(run, reason, skip, kindOverride) {
|
|
|
18777
19687
|
}
|
|
18778
19688
|
|
|
18779
19689
|
// src/commands/analyze/phases/02-scope.ts
|
|
18780
|
-
async function scope(
|
|
18781
|
-
const { assistantResponse } =
|
|
19690
|
+
async function scope(run2) {
|
|
19691
|
+
const { assistantResponse } = run2;
|
|
18782
19692
|
const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
|
|
18783
|
-
|
|
19693
|
+
run2.changedUniverse = allChanged;
|
|
18784
19694
|
const { kept: external } = partitionVerityOwned(allChanged);
|
|
18785
19695
|
const verityIgnore = loadVerityIgnore();
|
|
18786
19696
|
const ignored = partitionIgnored(external, verityIgnore);
|
|
@@ -18805,10 +19715,10 @@ async function scope(run) {
|
|
|
18805
19715
|
const securityFiles = filterSecurity(inScope);
|
|
18806
19716
|
const noFilesChanged = analyzable.length === 0 && reviewable.length === 0 && securityFiles.length === 0;
|
|
18807
19717
|
if (noFilesChanged && !assistantResponse) {
|
|
18808
|
-
await passAndExit(
|
|
19718
|
+
await passAndExit(run2, "No analyzable files changed", "no-analyzable-files");
|
|
18809
19719
|
}
|
|
18810
19720
|
const allForReview = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable]));
|
|
18811
|
-
Object.assign(
|
|
19721
|
+
Object.assign(run2, { allChanged, allForReview, analyzable, hasRecentCommitFiles, noFilesChanged, reviewable, securityFiles, verityIgnored: ignored });
|
|
18812
19722
|
}
|
|
18813
19723
|
|
|
18814
19724
|
// src/lib/specs.ts
|
|
@@ -18954,8 +19864,8 @@ function discoverGuardDocs(rangeFiles2) {
|
|
|
18954
19864
|
}
|
|
18955
19865
|
|
|
18956
19866
|
// src/commands/analyze/phases/03-intent-inputs.ts
|
|
18957
|
-
async function intentInputs(
|
|
18958
|
-
const { actionSummary, allForReview, assistantResponse, baseline, baselineSessionId } =
|
|
19867
|
+
async function intentInputs(run2) {
|
|
19868
|
+
const { actionSummary, allForReview, assistantResponse, baseline, baselineSessionId } = run2;
|
|
18959
19869
|
if (isCommandOnlyTurn({
|
|
18960
19870
|
userCommands: actionSummary?.user_commands,
|
|
18961
19871
|
userCommandsTruncated: actionSummary?.user_commands_truncated,
|
|
@@ -18963,11 +19873,11 @@ async function intentInputs(run) {
|
|
|
18963
19873
|
agentToolCalls: actionSummary?.total_tool_calls ?? 0,
|
|
18964
19874
|
authorshipIsObservable: !!actionSummary && actionSummary.transcript_windowed !== "orphaned"
|
|
18965
19875
|
})) {
|
|
18966
|
-
await passAndExit(
|
|
19876
|
+
await passAndExit(run2, "User command only \u2014 skipping analysis", "command-only-turn");
|
|
18967
19877
|
}
|
|
18968
19878
|
{
|
|
18969
19879
|
const ignoreKeys = ignoreStateKeys(
|
|
18970
|
-
|
|
19880
|
+
run2.tokenResult.ok ? run2.tokenResult.data.token : void 0,
|
|
18971
19881
|
null
|
|
18972
19882
|
);
|
|
18973
19883
|
const found = resolveIgnoreState([baselineSessionId, ...ignoreKeys]);
|
|
@@ -18991,7 +19901,7 @@ async function intentInputs(run) {
|
|
|
18991
19901
|
if (declaration.scope === "turn" && found) clearActiveDeclaration(found.key);
|
|
18992
19902
|
logEvent("ignore_honoured", { scope: declaration.scope, origin: declaration.origin });
|
|
18993
19903
|
await passAndExit(
|
|
18994
|
-
|
|
19904
|
+
run2,
|
|
18995
19905
|
`skipping this turn \u2014 declared housekeeping ("${declaration.reason}")`,
|
|
18996
19906
|
"declared-ignore"
|
|
18997
19907
|
);
|
|
@@ -19005,7 +19915,7 @@ async function intentInputs(run) {
|
|
|
19005
19915
|
const notice = `Verity: the ignore declared for this window ("${declaration.reason}") was voided \u2014 ${outcome.why}. Reviewing normally.`;
|
|
19006
19916
|
process.stderr.write(`${notice}
|
|
19007
19917
|
`);
|
|
19008
|
-
|
|
19918
|
+
run2.voidedIgnoreNotice = notice;
|
|
19009
19919
|
}
|
|
19010
19920
|
}
|
|
19011
19921
|
}
|
|
@@ -19027,34 +19937,33 @@ async function intentInputs(run) {
|
|
|
19027
19937
|
const adopted = absorbIntoBaseline(setupAuthored, baselineSessionId);
|
|
19028
19938
|
logEvent("baseline_absorbed", { skip: "verity-command", offered: setupAuthored.length, adopted });
|
|
19029
19939
|
}
|
|
19030
|
-
await passAndExit(
|
|
19940
|
+
await passAndExit(run2, "Verity command \u2014 skipping analysis", "verity-command");
|
|
19031
19941
|
}
|
|
19032
19942
|
if (shouldSkipForBareAck({ prompt: latestPrompt, turnAuthoredCode, canSeeTurnAuthorship })) {
|
|
19033
|
-
await passAndExit(
|
|
19943
|
+
await passAndExit(run2, "Bare acknowledgment \u2014 skipping analysis", "bare-acknowledgment");
|
|
19034
19944
|
}
|
|
19035
19945
|
if (isReflectionQuestion(assistantResponse) && !turnAuthoredCode && canSeeTurnAuthorship) {
|
|
19036
|
-
await passAndExit(
|
|
19946
|
+
await passAndExit(run2, "Reflection-prompt turn \u2014 skipping analysis", "reflection-prompt");
|
|
19037
19947
|
}
|
|
19038
|
-
Object.assign(
|
|
19948
|
+
Object.assign(run2, { authorshipIsObservable, conversation, earlyFold, plans, specs, turnAuthoredCode });
|
|
19039
19949
|
}
|
|
19040
19950
|
|
|
19041
19951
|
// src/commands/analyze/phases/04-connect.ts
|
|
19042
|
-
async function connect(
|
|
19043
|
-
const { opts, globals } =
|
|
19044
|
-
const { analyzable, baseline, securityFiles, tokenResult } =
|
|
19952
|
+
async function connect(run2) {
|
|
19953
|
+
const { opts, globals } = run2;
|
|
19954
|
+
const { analyzable, baseline, securityFiles, tokenResult } = run2;
|
|
19045
19955
|
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
19046
19956
|
if (!tokenResult.ok || !urlResult.ok) {
|
|
19047
19957
|
localOnlyAndExit(runLocalStatic(analyzable, securityFiles, baseline, !!opts.skipStatic));
|
|
19048
19958
|
}
|
|
19049
|
-
Object.assign(
|
|
19959
|
+
Object.assign(run2, { urlResult, serviceUrl: urlResult.data, token: tokenResult.data.token });
|
|
19050
19960
|
}
|
|
19051
19961
|
|
|
19052
19962
|
// src/commands/analyze/phases/05-mode.ts
|
|
19053
|
-
async function mode(
|
|
19054
|
-
const { opts, globals } =
|
|
19055
|
-
const { actionSummary, allForReview, assistantResponse, baseline, conversation, memory, noFilesChanged, serviceUrl, sessionId, token, turnAuthoredCode } =
|
|
19963
|
+
async function mode(run2) {
|
|
19964
|
+
const { opts, globals } = run2;
|
|
19965
|
+
const { actionSummary, allForReview, assistantResponse, baseline, conversation, memory, noFilesChanged, serviceUrl, sessionId, token, turnAuthoredCode } = run2;
|
|
19056
19966
|
const sessionIdForMemory = sessionId || process.env.CLAUDE_SESSION_ID || "";
|
|
19057
|
-
let contextFilePaths = [];
|
|
19058
19967
|
let predictedMode;
|
|
19059
19968
|
try {
|
|
19060
19969
|
const memoryPath2 = sessionIdForMemory ? `/memory?session_id=${encodeURIComponent(sessionIdForMemory)}` : "/memory";
|
|
@@ -19068,9 +19977,6 @@ async function mode(run) {
|
|
|
19068
19977
|
cmd: "analyze_context"
|
|
19069
19978
|
});
|
|
19070
19979
|
if (memoryResult.ok) {
|
|
19071
|
-
if (Array.isArray(memoryResult.data.context_files)) {
|
|
19072
|
-
contextFilePaths = memoryResult.data.context_files;
|
|
19073
|
-
}
|
|
19074
19980
|
const rawMode = memoryResult.data.predicted_mode;
|
|
19075
19981
|
if (rawMode && ["standard", "plan", "debug", "skip"].includes(rawMode)) {
|
|
19076
19982
|
predictedMode = rawMode;
|
|
@@ -19093,7 +19999,7 @@ async function mode(run) {
|
|
|
19093
19999
|
);
|
|
19094
20000
|
}
|
|
19095
20001
|
const investigated = didAgentInvestigate(actionSummary);
|
|
19096
|
-
|
|
20002
|
+
run2.modeDecision = {
|
|
19097
20003
|
predicted: predictedMode ?? null,
|
|
19098
20004
|
resolved: analysisMode,
|
|
19099
20005
|
authored: turnAuthoredCode,
|
|
@@ -19113,13 +20019,13 @@ async function mode(run) {
|
|
|
19113
20019
|
});
|
|
19114
20020
|
if (analysisMode === "skip") {
|
|
19115
20021
|
await passAndExit(
|
|
19116
|
-
|
|
20022
|
+
run2,
|
|
19117
20023
|
"Skip mode \u2014 no code work to analyze",
|
|
19118
20024
|
"skip-mode",
|
|
19119
20025
|
turnAuthoredCode ? "capacity" : void 0
|
|
19120
20026
|
);
|
|
19121
20027
|
}
|
|
19122
|
-
Object.assign(
|
|
20028
|
+
Object.assign(run2, { analysisMode, sessionAuthoredCode, sessionIdForMemory });
|
|
19123
20029
|
}
|
|
19124
20030
|
|
|
19125
20031
|
// src/lib/fold.ts
|
|
@@ -19579,12 +20485,12 @@ function checkConservation(changedFiles, result, repoRoot2) {
|
|
|
19579
20485
|
}
|
|
19580
20486
|
|
|
19581
20487
|
// src/commands/analyze/phases/06-evidence.ts
|
|
19582
|
-
async function evidence(
|
|
19583
|
-
const { opts } =
|
|
19584
|
-
const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath, turnAuthoredCode } =
|
|
19585
|
-
let { analysisMode, earlyFold } =
|
|
20488
|
+
async function evidence(run2) {
|
|
20489
|
+
const { opts } = run2;
|
|
20490
|
+
const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath, turnAuthoredCode } = run2;
|
|
20491
|
+
let { analysisMode, earlyFold } = run2;
|
|
19586
20492
|
const recordFlip = (stage) => {
|
|
19587
|
-
if (
|
|
20493
|
+
if (run2.modeDecision) run2.modeDecision = { ...run2.modeDecision, resolved: "plan", flip: stage };
|
|
19588
20494
|
logEvent("mode_flipped", { stage, to: "plan" });
|
|
19589
20495
|
};
|
|
19590
20496
|
const planWorthy = !!assistantResponse && !turnAuthoredCode;
|
|
@@ -19611,7 +20517,7 @@ async function evidence(run) {
|
|
|
19611
20517
|
analysisMode = "plan";
|
|
19612
20518
|
recordFlip("debounce");
|
|
19613
20519
|
} else {
|
|
19614
|
-
await passAndExit(
|
|
20520
|
+
await passAndExit(run2, debounceSkip, "debounce");
|
|
19615
20521
|
}
|
|
19616
20522
|
}
|
|
19617
20523
|
if (analysisMode !== "plan") {
|
|
@@ -19622,7 +20528,7 @@ async function evidence(run) {
|
|
|
19622
20528
|
analysisMode = "plan";
|
|
19623
20529
|
recordFlip("mtime");
|
|
19624
20530
|
} else {
|
|
19625
|
-
await passAndExit(
|
|
20531
|
+
await passAndExit(run2, mtimeSkip, "no-delta-since-last-review");
|
|
19626
20532
|
}
|
|
19627
20533
|
}
|
|
19628
20534
|
}
|
|
@@ -19635,7 +20541,7 @@ async function evidence(run) {
|
|
|
19635
20541
|
analysisMode = "plan";
|
|
19636
20542
|
recordFlip("content-hash");
|
|
19637
20543
|
} else {
|
|
19638
|
-
await passAndExit(
|
|
20544
|
+
await passAndExit(run2, hashResult.skip, "no-delta-since-last-review");
|
|
19639
20545
|
}
|
|
19640
20546
|
}
|
|
19641
20547
|
contentHash = hashResult.hash;
|
|
@@ -19643,7 +20549,7 @@ async function evidence(run) {
|
|
|
19643
20549
|
const scoped = scopeToAuthored(allForReview, actionSummary);
|
|
19644
20550
|
const canTrustNoneAuthored = scoped.signal === "none-authored" && authorshipIsObservable;
|
|
19645
20551
|
if (canTrustNoneAuthored && !hasNonEditAuthorship(actionSummary, sessionAuthoredCode)) {
|
|
19646
|
-
await passAndExit(
|
|
20552
|
+
await passAndExit(run2, "No agent-authored code this turn \u2014 working-tree changes were not authored by this session", "zero-increment");
|
|
19647
20553
|
}
|
|
19648
20554
|
if (scoped.signal === "none-authored" && !authorshipIsObservable) {
|
|
19649
20555
|
logEvent("none_authored_unverifiable", {
|
|
@@ -19700,7 +20606,7 @@ async function evidence(run) {
|
|
|
19700
20606
|
recordFlip("empty-after-scoping");
|
|
19701
20607
|
} else {
|
|
19702
20608
|
await passAndExit(
|
|
19703
|
-
|
|
20609
|
+
run2,
|
|
19704
20610
|
"No files within size limits to analyze",
|
|
19705
20611
|
"size-limit",
|
|
19706
20612
|
codeDelta.excluded.length > 0 ? "capacity" : "policy"
|
|
@@ -19725,7 +20631,7 @@ async function evidence(run) {
|
|
|
19725
20631
|
currentCommit = getCurrentCommit();
|
|
19726
20632
|
iteration = readIteration(currentCommit);
|
|
19727
20633
|
}
|
|
19728
|
-
Object.assign(
|
|
20634
|
+
Object.assign(run2, { analysisMode, codeDelta, contentHash, currentCommit, earlyFold, iteration, snapshotResult, staticResults });
|
|
19729
20635
|
}
|
|
19730
20636
|
|
|
19731
20637
|
// src/lib/cache-cleanup.ts
|
|
@@ -19757,15 +20663,52 @@ function pruneStaleCache() {
|
|
|
19757
20663
|
|
|
19758
20664
|
// src/lib/context-files.ts
|
|
19759
20665
|
var import_node_fs30 = require("node:fs");
|
|
20666
|
+
var import_node_os5 = require("node:os");
|
|
19760
20667
|
var MAX_CONTEXT_FILES = 10;
|
|
19761
20668
|
var MAX_CONTEXT_FILE_BYTES = 10240;
|
|
19762
|
-
var MAX_CONTEXT_TOTAL_BYTES =
|
|
19763
|
-
function
|
|
20669
|
+
var MAX_CONTEXT_TOTAL_BYTES = 24576;
|
|
20670
|
+
function readSetContextPaths(summary, deltaFiles) {
|
|
20671
|
+
const reads = summary?.files_read ?? [];
|
|
20672
|
+
if (reads.length === 0) return [];
|
|
20673
|
+
const root = process.cwd().replace(/\/+$/, "");
|
|
20674
|
+
const home = (0, import_node_os5.homedir)();
|
|
20675
|
+
const toRepoRelative2 = (p) => {
|
|
20676
|
+
if (!p) return null;
|
|
20677
|
+
let abs;
|
|
20678
|
+
if (p.startsWith("/")) {
|
|
20679
|
+
abs = p;
|
|
20680
|
+
} else {
|
|
20681
|
+
const rebuilt = `${home}/${p}`;
|
|
20682
|
+
abs = rebuilt.startsWith(`${root}/`) ? rebuilt : `${root}/${p}`;
|
|
20683
|
+
}
|
|
20684
|
+
if (!abs.startsWith(`${root}/`)) return null;
|
|
20685
|
+
return abs.slice(root.length + 1);
|
|
20686
|
+
};
|
|
20687
|
+
const authored = /* @__PURE__ */ new Set();
|
|
20688
|
+
for (const p of [...summary?.files_edited ?? [], ...summary?.files_created ?? []]) {
|
|
20689
|
+
const rel = toRepoRelative2(p);
|
|
20690
|
+
if (rel) authored.add(rel);
|
|
20691
|
+
}
|
|
20692
|
+
for (const f of deltaFiles) authored.add(f.path);
|
|
20693
|
+
const out = [];
|
|
20694
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20695
|
+
for (const p of reads) {
|
|
20696
|
+
const rel = toRepoRelative2(p);
|
|
20697
|
+
if (!rel || seen.has(rel) || authored.has(rel)) continue;
|
|
20698
|
+
seen.add(rel);
|
|
20699
|
+
const ext = rel.split(".").pop()?.toLowerCase() ?? "";
|
|
20700
|
+
if (!ANALYZABLE_EXTENSIONS.has(ext) && !REVIEWABLE_EXTENSIONS.has(ext)) continue;
|
|
20701
|
+
out.push(rel);
|
|
20702
|
+
}
|
|
20703
|
+
return out;
|
|
20704
|
+
}
|
|
20705
|
+
function gatherContextFiles(contextPaths, deltaFiles, opts) {
|
|
20706
|
+
const fileCap = Math.max(0, Math.min(MAX_CONTEXT_FILES, opts?.maxFiles ?? MAX_CONTEXT_FILES));
|
|
19764
20707
|
const deltaPaths = new Set(deltaFiles.map((f) => f.path));
|
|
19765
20708
|
const result = [];
|
|
19766
20709
|
let totalBytes = 0;
|
|
19767
20710
|
for (const filePath of contextPaths) {
|
|
19768
|
-
if (result.length >=
|
|
20711
|
+
if (result.length >= fileCap) break;
|
|
19769
20712
|
if (deltaPaths.has(filePath)) continue;
|
|
19770
20713
|
if (isVerityOwnedPath(filePath)) {
|
|
19771
20714
|
logEvent("context_file_skipped", { path: filePath, reason: "verity_owned" });
|
|
@@ -19822,10 +20765,15 @@ function gatherContextFiles(contextPaths, deltaFiles) {
|
|
|
19822
20765
|
}
|
|
19823
20766
|
|
|
19824
20767
|
// src/commands/analyze/phases/07-context-files.ts
|
|
19825
|
-
async function contextFiles(
|
|
19826
|
-
const { codeDelta
|
|
19827
|
-
const
|
|
19828
|
-
const
|
|
20768
|
+
async function contextFiles(run2) {
|
|
20769
|
+
const { codeDelta } = run2;
|
|
20770
|
+
const readSet = readSetContextPaths(run2.actionSummary, codeDelta.files);
|
|
20771
|
+
const { kept: externalContext } = partitionVerityOwned(readSet);
|
|
20772
|
+
const ig = loadVerityIgnore();
|
|
20773
|
+
const unfenced = run2.verityIgnored.suspended ? externalContext : externalContext.filter((p) => !isIgnored(ig, p));
|
|
20774
|
+
const contextFiles2 = gatherContextFiles(unfenced, codeDelta.files, {
|
|
20775
|
+
maxFiles: MAX_FILES - codeDelta.files.length
|
|
20776
|
+
});
|
|
19829
20777
|
for (const f of codeDelta.files) {
|
|
19830
20778
|
f.role = "delta";
|
|
19831
20779
|
}
|
|
@@ -19837,6 +20785,30 @@ async function contextFiles(run) {
|
|
|
19837
20785
|
});
|
|
19838
20786
|
}
|
|
19839
20787
|
|
|
20788
|
+
// src/commands/analyze/phases/07b-repo-context.ts
|
|
20789
|
+
async function repoContext(run2) {
|
|
20790
|
+
const { codeDelta, snapshotResult } = run2;
|
|
20791
|
+
const deltaFiles = codeDelta.files.filter((f) => f.role !== "context");
|
|
20792
|
+
const sentPaths = new Set(codeDelta.files.map((f) => f.path));
|
|
20793
|
+
const ig = loadVerityIgnore();
|
|
20794
|
+
const isExcluded = (p) => isVerityOwnedPath(p) || !run2.verityIgnored.suspended && isIgnored(ig, p);
|
|
20795
|
+
run2.repoContext = buildRepoContext({
|
|
20796
|
+
deltaFiles,
|
|
20797
|
+
diffs: snapshotResult.diffs,
|
|
20798
|
+
sentPaths,
|
|
20799
|
+
isExcluded
|
|
20800
|
+
});
|
|
20801
|
+
logEvent("repo_context", {
|
|
20802
|
+
state: run2.repoContext.state,
|
|
20803
|
+
reason: run2.repoContext.reason ?? null,
|
|
20804
|
+
symbols: run2.repoContext.symbols?.length ?? 0,
|
|
20805
|
+
dropped: run2.repoContext.dropped_symbols?.length ?? 0,
|
|
20806
|
+
callers: run2.repoContext.callers?.length ?? 0,
|
|
20807
|
+
tests: run2.repoContext.tests?.length ?? 0,
|
|
20808
|
+
elapsed_ms: run2.repoContext.elapsed_ms ?? null
|
|
20809
|
+
});
|
|
20810
|
+
}
|
|
20811
|
+
|
|
19840
20812
|
// src/lib/seed-runner.ts
|
|
19841
20813
|
var import_promises11 = require("node:fs/promises");
|
|
19842
20814
|
var import_node_fs31 = require("node:fs");
|
|
@@ -20180,9 +21152,9 @@ async function runSeed(opts) {
|
|
|
20180
21152
|
// src/commands/analyze/phases/08-memory-manifest.ts
|
|
20181
21153
|
var import_node_fs32 = require("node:fs");
|
|
20182
21154
|
var import_node_path24 = require("node:path");
|
|
20183
|
-
async function memoryManifest(
|
|
20184
|
-
const { globals } =
|
|
20185
|
-
const { serviceUrl, token } =
|
|
21155
|
+
async function memoryManifest(run2) {
|
|
21156
|
+
const { globals } = run2;
|
|
21157
|
+
const { serviceUrl, token } = run2;
|
|
20186
21158
|
let memoryManifest2;
|
|
20187
21159
|
let deletedNodePaths = [];
|
|
20188
21160
|
let editedUploads = [];
|
|
@@ -20234,12 +21206,12 @@ async function memoryManifest(run) {
|
|
|
20234
21206
|
editedUploads = await computeEditedNodeUploads();
|
|
20235
21207
|
} catch {
|
|
20236
21208
|
}
|
|
20237
|
-
Object.assign(
|
|
21209
|
+
Object.assign(run2, { autoSeedNotice, deletedNodePaths, editedUploads, memoryManifest: memoryManifest2 });
|
|
20238
21210
|
}
|
|
20239
21211
|
|
|
20240
21212
|
// src/commands/analyze/phases/09-fold-transcript.ts
|
|
20241
|
-
async function foldTranscript(
|
|
20242
|
-
const { allForReview, earlyFold, transcriptPath } =
|
|
21213
|
+
async function foldTranscript(run2) {
|
|
21214
|
+
const { allForReview, earlyFold, transcriptPath } = run2;
|
|
20243
21215
|
let foldResult = null;
|
|
20244
21216
|
let foldConservation = null;
|
|
20245
21217
|
if (transcriptPath) {
|
|
@@ -20256,7 +21228,7 @@ async function foldTranscript(run) {
|
|
|
20256
21228
|
foldResult = null;
|
|
20257
21229
|
}
|
|
20258
21230
|
}
|
|
20259
|
-
Object.assign(
|
|
21231
|
+
Object.assign(run2, { foldConservation, foldResult });
|
|
20260
21232
|
}
|
|
20261
21233
|
|
|
20262
21234
|
// src/lib/increment.ts
|
|
@@ -20308,10 +21280,10 @@ function computeIncrement(reviewedPaths, hashOf, priorAuthored) {
|
|
|
20308
21280
|
|
|
20309
21281
|
// src/commands/analyze/phases/10-working-memory.ts
|
|
20310
21282
|
var import_node_path25 = require("node:path");
|
|
20311
|
-
async function workingMemory(
|
|
20312
|
-
const { opts } =
|
|
20313
|
-
const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } =
|
|
20314
|
-
let { reachability } =
|
|
21283
|
+
async function workingMemory(run2) {
|
|
21284
|
+
const { opts } = run2;
|
|
21285
|
+
const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } = run2;
|
|
21286
|
+
let { reachability } = run2;
|
|
20315
21287
|
const memorySession = sessionDossier(token, sessionId ?? process.env.CLAUDE_SESSION_ID ?? null);
|
|
20316
21288
|
let memory = null;
|
|
20317
21289
|
let incrementReport = null;
|
|
@@ -20397,7 +21369,7 @@ async function workingMemory(run) {
|
|
|
20397
21369
|
hasUserPrompt: (conversation?.prompts?.length ?? 0) > 0,
|
|
20398
21370
|
isTTY: process.stdout.isTTY === true
|
|
20399
21371
|
});
|
|
20400
|
-
Object.assign(
|
|
21372
|
+
Object.assign(run2, { incrementReport, memory, memorySession, reachability });
|
|
20401
21373
|
}
|
|
20402
21374
|
|
|
20403
21375
|
// src/lib/note-budget.ts
|
|
@@ -20470,7 +21442,7 @@ function isExplicitlyAutonomous(env = process.env) {
|
|
|
20470
21442
|
}
|
|
20471
21443
|
|
|
20472
21444
|
// src/lib/task-context.ts
|
|
20473
|
-
var
|
|
21445
|
+
var import_node_child_process10 = require("node:child_process");
|
|
20474
21446
|
var CLOSING_RE = /\b(close[sd]?|fix(?:e[sd])?|resolve[sd]?)\b[\s:]*#(\d+)/i;
|
|
20475
21447
|
var BRANCH_RE = /(?:^|[/_-])(?:issue|gh|fix)[-_/]?(\d+)\b/i;
|
|
20476
21448
|
function parseLinkedIssue(sources) {
|
|
@@ -20486,7 +21458,7 @@ function parseLinkedIssue(sources) {
|
|
|
20486
21458
|
}
|
|
20487
21459
|
function safeExec(cmd, timeout) {
|
|
20488
21460
|
try {
|
|
20489
|
-
return (0,
|
|
21461
|
+
return (0, import_node_child_process10.execSync)(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout }).trim();
|
|
20490
21462
|
} catch {
|
|
20491
21463
|
return "";
|
|
20492
21464
|
}
|
|
@@ -20516,8 +21488,8 @@ function resolveTaskContext(opts) {
|
|
|
20516
21488
|
// src/commands/analyze/phases/11-build-request.ts
|
|
20517
21489
|
var MAX_ASSISTANT_RESPONSE_CHARS_PLAN = 32768;
|
|
20518
21490
|
var MAX_ASSISTANT_RESPONSE_CHARS_DEFAULT = 8e3;
|
|
20519
|
-
async function buildRequest(
|
|
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 } =
|
|
21491
|
+
async function buildRequest(run2) {
|
|
21492
|
+
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
21493
|
const excludedByReason = {};
|
|
20522
21494
|
for (const e of codeDelta.excluded ?? []) {
|
|
20523
21495
|
excludedByReason[e.reason] = (excludedByReason[e.reason] ?? 0) + 1;
|
|
@@ -20547,15 +21519,15 @@ async function buildRequest(run) {
|
|
|
20547
21519
|
// the state, so the number is one turn lagged by construction. The
|
|
20548
21520
|
// degenerate win for the budget is a dead channel that looks like clean
|
|
20549
21521
|
// code; this is what makes "did delivery rate collapse" a query.
|
|
20550
|
-
advisory_delivered_prior: readAdvisoryEpisode(
|
|
21522
|
+
advisory_delivered_prior: readAdvisoryEpisode(run2.baselineSessionId)?.delivered ?? 0,
|
|
20551
21523
|
// `.verityignore` — see CoverageTelemetry.verityignore for why the SHARE is
|
|
20552
21524
|
// the number that matters and why no paths travel with it.
|
|
20553
21525
|
verityignore: {
|
|
20554
|
-
rules:
|
|
20555
|
-
excluded:
|
|
20556
|
-
share: ignoreShare(
|
|
20557
|
-
security_excluded:
|
|
20558
|
-
suspended:
|
|
21526
|
+
rules: run2.verityIgnored.rules,
|
|
21527
|
+
excluded: run2.verityIgnored.ignored.length,
|
|
21528
|
+
share: ignoreShare(run2.verityIgnored.kept.length, run2.verityIgnored.ignored.length),
|
|
21529
|
+
security_excluded: run2.verityIgnored.securityExcluded.length,
|
|
21530
|
+
suspended: run2.verityIgnored.suspended
|
|
20559
21531
|
}
|
|
20560
21532
|
};
|
|
20561
21533
|
const requestBody = {
|
|
@@ -20693,6 +21665,9 @@ async function buildRequest(run) {
|
|
|
20693
21665
|
if (snapshotResult.has_snapshots && snapshotResult.diffs.length > 0) {
|
|
20694
21666
|
requestBody.snapshot_diffs = snapshotResult.diffs;
|
|
20695
21667
|
}
|
|
21668
|
+
if (run2.repoContext) {
|
|
21669
|
+
requestBody.repo_context = run2.repoContext;
|
|
21670
|
+
}
|
|
20696
21671
|
const noHumanPrompt = (conversation?.prompts?.length ?? 0) === 0;
|
|
20697
21672
|
const w4Task = noHumanPrompt && isExplicitlyAutonomous() ? resolveTaskContext() : null;
|
|
20698
21673
|
const planApprovalActive = foldResult?.planApproval?.activeSinceLastPrompt === true;
|
|
@@ -20750,7 +21725,7 @@ async function buildRequest(run) {
|
|
|
20750
21725
|
}
|
|
20751
21726
|
requestBody.intent_context = intentContext;
|
|
20752
21727
|
}
|
|
20753
|
-
Object.assign(
|
|
21728
|
+
Object.assign(run2, { requestBody });
|
|
20754
21729
|
}
|
|
20755
21730
|
|
|
20756
21731
|
// src/lib/offline.ts
|
|
@@ -20811,9 +21786,9 @@ function shouldWarmRetryAnalyze(result) {
|
|
|
20811
21786
|
}
|
|
20812
21787
|
|
|
20813
21788
|
// src/commands/analyze/phases/12-transmit.ts
|
|
20814
|
-
async function transmit(
|
|
20815
|
-
const { globals } =
|
|
20816
|
-
const { codeDelta, requestBody, serviceUrl, staticResults, token } =
|
|
21789
|
+
async function transmit(run2) {
|
|
21790
|
+
const { globals } = run2;
|
|
21791
|
+
const { codeDelta, requestBody, serviceUrl, staticResults, token } = run2;
|
|
20817
21792
|
const ANALYZE_TIMEOUT_MS = 1e5;
|
|
20818
21793
|
let result = await analyzeRequest({
|
|
20819
21794
|
serviceUrl,
|
|
@@ -20876,15 +21851,34 @@ async function transmit(run) {
|
|
|
20876
21851
|
}
|
|
20877
21852
|
const response = result.data;
|
|
20878
21853
|
const decision = response.gate_decision ?? "(unrecognised)";
|
|
20879
|
-
Object.assign(
|
|
21854
|
+
Object.assign(run2, { decision, response });
|
|
20880
21855
|
}
|
|
20881
21856
|
|
|
20882
21857
|
// src/commands/analyze/phases/13-reconcile.ts
|
|
20883
21858
|
var import_node_fs35 = require("node:fs");
|
|
20884
21859
|
var import_node_path26 = require("node:path");
|
|
20885
|
-
async function reconcile(
|
|
20886
|
-
const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } =
|
|
21860
|
+
async function reconcile(run2) {
|
|
21861
|
+
const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run2;
|
|
20887
21862
|
const sentPaths = codeDelta.files.map((f) => f.path);
|
|
21863
|
+
if (memorySession) {
|
|
21864
|
+
try {
|
|
21865
|
+
const settledSites = response.metadata?.settled_sites;
|
|
21866
|
+
if (Array.isArray(settledSites)) {
|
|
21867
|
+
for (const site of settledSites) {
|
|
21868
|
+
const siteRecord = site;
|
|
21869
|
+
const file = typeof siteRecord?.file === "string" ? siteRecord.file : null;
|
|
21870
|
+
const patternId = typeof siteRecord?.pattern_id === "string" ? siteRecord.pattern_id : null;
|
|
21871
|
+
if (!file || !patternId) continue;
|
|
21872
|
+
appendEvent(memorySession.d, {
|
|
21873
|
+
k: "outcome",
|
|
21874
|
+
anchor_key: statementAnchorKey(file, patternId),
|
|
21875
|
+
outcome: "answered"
|
|
21876
|
+
});
|
|
21877
|
+
}
|
|
21878
|
+
}
|
|
21879
|
+
} catch {
|
|
21880
|
+
}
|
|
21881
|
+
}
|
|
20888
21882
|
let openElsewhere = [];
|
|
20889
21883
|
if (memorySession) {
|
|
20890
21884
|
try {
|
|
@@ -20965,7 +21959,7 @@ async function reconcile(run) {
|
|
|
20965
21959
|
// below is the signal that replaces the noise.
|
|
20966
21960
|
//
|
|
20967
21961
|
// Taken from the run, not recomputed — see context.ts `verityIgnored`.
|
|
20968
|
-
...
|
|
21962
|
+
...run2.verityIgnored.ignored.map((path) => ({
|
|
20969
21963
|
path,
|
|
20970
21964
|
reason: "verityignore",
|
|
20971
21965
|
stage: "verityignore",
|
|
@@ -21045,14 +22039,6 @@ async function reconcile(run) {
|
|
|
21045
22039
|
})() : [];
|
|
21046
22040
|
if (memorySession) {
|
|
21047
22041
|
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
22042
|
recordVerdict(memorySession.d, {
|
|
21057
22043
|
runId: response.run_id ?? turnId,
|
|
21058
22044
|
decision,
|
|
@@ -21093,11 +22079,11 @@ async function reconcile(run) {
|
|
|
21093
22079
|
`
|
|
21094
22080
|
);
|
|
21095
22081
|
}
|
|
21096
|
-
Object.assign(
|
|
22082
|
+
Object.assign(run2, { intentRepeatCount, openElsewhere, priorPendingFingerprints, reviewCoverage, sentPaths, silenced, watermarkHash, watermarkIsPartial });
|
|
21097
22083
|
}
|
|
21098
22084
|
|
|
21099
22085
|
// src/lib/emit.ts
|
|
21100
|
-
var
|
|
22086
|
+
var YELLOW3 = "\x1B[33m";
|
|
21101
22087
|
var NC2 = "\x1B[0m";
|
|
21102
22088
|
function emitVerdict(input) {
|
|
21103
22089
|
const exit = input.exit ?? ((code) => process.exit(code));
|
|
@@ -21108,7 +22094,7 @@ function emitVerdict(input) {
|
|
|
21108
22094
|
const note = [describeCoverage(coverage), describeOpenElsewhere(openElsewhere)].filter(Boolean).join("\n\n") || null;
|
|
21109
22095
|
if (unaccounted.length > 0) {
|
|
21110
22096
|
process.stderr.write(
|
|
21111
|
-
`${
|
|
22097
|
+
`${YELLOW3}Verity: ${unaccounted.length} changed file(s) could not be attributed to any review stage \u2014 counted as unreviewed.${NC2}
|
|
21112
22098
|
`
|
|
21113
22099
|
);
|
|
21114
22100
|
}
|
|
@@ -21120,7 +22106,7 @@ ${input.agentContext}
|
|
|
21120
22106
|
`);
|
|
21121
22107
|
}
|
|
21122
22108
|
if (note && !input.silenced) process.stderr.write(`
|
|
21123
|
-
${
|
|
22109
|
+
${YELLOW3}${note}${NC2}
|
|
21124
22110
|
`);
|
|
21125
22111
|
return exit(2);
|
|
21126
22112
|
}
|
|
@@ -21206,10 +22192,10 @@ function screenRemediation(fix, findingFile) {
|
|
|
21206
22192
|
function agentContextFor(response, intentRepeat = 0, priorPendingFingerprints = []) {
|
|
21207
22193
|
return buildAgentContext(channelInputFrom(response, intentRepeat, priorPendingFingerprints));
|
|
21208
22194
|
}
|
|
21209
|
-
async function render(
|
|
21210
|
-
const { opts, globals } =
|
|
21211
|
-
const { actionSummary, assistantResponse, autoSeedNotice, voidedIgnoreNotice, baselineSessionId, codeDelta, contentHash, conversation, currentCommit, decision, intentRepeatCount, memory, openElsewhere, priorPendingFingerprints, response, reviewCoverage, serviceUrl, sessionIdForMemory, silenced, token, watermarkHash, watermarkIsPartial } =
|
|
21212
|
-
let { iteration } =
|
|
22195
|
+
async function render(run2) {
|
|
22196
|
+
const { opts, globals } = run2;
|
|
22197
|
+
const { actionSummary, assistantResponse, autoSeedNotice, voidedIgnoreNotice, baselineSessionId, codeDelta, contentHash, conversation, currentCommit, decision, intentRepeatCount, memory, openElsewhere, priorPendingFingerprints, response, reviewCoverage, serviceUrl, sessionIdForMemory, silenced, token, watermarkHash, watermarkIsPartial } = run2;
|
|
22198
|
+
let { iteration } = run2;
|
|
21213
22199
|
const metadata = response.metadata ?? {};
|
|
21214
22200
|
const intentAmbiguity = metadata.intent_ambiguity;
|
|
21215
22201
|
if (intentAmbiguity != null && intentAmbiguity > 5) {
|
|
@@ -21316,7 +22302,7 @@ async function render(run) {
|
|
|
21316
22302
|
const blocks = prior.blocks + 1;
|
|
21317
22303
|
const decisionNow = mayBlock({
|
|
21318
22304
|
reviewedFileCount: codeDelta.files.length,
|
|
21319
|
-
staticFindingCount:
|
|
22305
|
+
staticFindingCount: run2.staticResults?.findings?.length ?? 0,
|
|
21320
22306
|
cycleCutFired: silenced !== null,
|
|
21321
22307
|
attempts,
|
|
21322
22308
|
blocks,
|
|
@@ -21347,7 +22333,7 @@ async function render(run) {
|
|
|
21347
22333
|
});
|
|
21348
22334
|
emitVerdict({
|
|
21349
22335
|
proposed: "WARN",
|
|
21350
|
-
changed:
|
|
22336
|
+
changed: run2.changedUniverse,
|
|
21351
22337
|
coverage: reviewCoverage,
|
|
21352
22338
|
userSummary: lines.length > 0 ? `${summary}
|
|
21353
22339
|
${lines.join("\n")}` : summary,
|
|
@@ -21439,7 +22425,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
21439
22425
|
`);
|
|
21440
22426
|
emitVerdict({
|
|
21441
22427
|
proposed: "FAIL",
|
|
21442
|
-
changed:
|
|
22428
|
+
changed: run2.changedUniverse,
|
|
21443
22429
|
coverage: reviewCoverage,
|
|
21444
22430
|
userSummary: "",
|
|
21445
22431
|
// Subject to the SAME cycle cut as PASS/WARN. Suppressing here is safe:
|
|
@@ -21464,7 +22450,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
21464
22450
|
userSummary += loginNudge + grantNudge;
|
|
21465
22451
|
emitVerdict({
|
|
21466
22452
|
proposed: "PASS",
|
|
21467
|
-
changed:
|
|
22453
|
+
changed: run2.changedUniverse,
|
|
21468
22454
|
coverage: reviewCoverage,
|
|
21469
22455
|
userSummary,
|
|
21470
22456
|
agentContext: silenced ? null : agentContextFor(response, intentRepeatCount, priorPendingFingerprints),
|
|
@@ -21486,7 +22472,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
21486
22472
|
userSummary += loginNudge + grantNudge;
|
|
21487
22473
|
emitVerdict({
|
|
21488
22474
|
proposed: "WARN",
|
|
21489
|
-
changed:
|
|
22475
|
+
changed: run2.changedUniverse,
|
|
21490
22476
|
coverage: reviewCoverage,
|
|
21491
22477
|
userSummary,
|
|
21492
22478
|
agentContext: silenced ? null : agentContextFor(response, intentRepeatCount, priorPendingFingerprints),
|
|
@@ -21511,7 +22497,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
21511
22497
|
process.exit(0);
|
|
21512
22498
|
}
|
|
21513
22499
|
}
|
|
21514
|
-
Object.assign(
|
|
22500
|
+
Object.assign(run2, { iteration });
|
|
21515
22501
|
}
|
|
21516
22502
|
|
|
21517
22503
|
// src/commands/analyze/index.ts
|
|
@@ -21530,6 +22516,8 @@ var PIPELINE = [
|
|
|
21530
22516
|
// ← THE NARROWING. what gets sent, and why not the rest
|
|
21531
22517
|
["contextFiles", contextFiles],
|
|
21532
22518
|
// supporting files, merged INTO the delta array
|
|
22519
|
+
["repoContext", repoContext],
|
|
22520
|
+
// R1/R3 — call sites of changed symbols, one line each
|
|
21533
22521
|
["memoryManifest", memoryManifest],
|
|
21534
22522
|
// knowledge-graph manifest + one-time auto-seed
|
|
21535
22523
|
["foldTranscript", foldTranscript],
|
|
@@ -21546,7 +22534,7 @@ var PIPELINE = [
|
|
|
21546
22534
|
// say it — stderr, stdout, disk
|
|
21547
22535
|
];
|
|
21548
22536
|
function registerAnalyzeCommand(program2) {
|
|
21549
|
-
program2.command("analyze").description("Run Verity analysis on changed files (stop hook)").option("--debounce <seconds>", "Skip if last analysis was within N seconds",
|
|
22537
|
+
program2.command("analyze").description("Run Verity analysis on changed files (stop hook)").option("--debounce <seconds>", "Skip if last analysis was within N seconds", String(DEBOUNCE_SECONDS)).option("--max-iterations <n>", "Force PASS after N FAIL cycles", String(MAX_ITERATIONS)).option("--max-files <n>", "Max files to send for review", String(MAX_FILES)).option("--max-file-size <bytes>", "Skip files larger than N bytes", String(MAX_FILE_BYTES)).option("--max-total-size <bytes>", "Stop collecting files at N total bytes", String(MAX_DELTA_BYTES)).option("--skip-static", "Skip codacy-analysis").option("--mode <mode>", "Force analysis mode (standard|plan|debug|skip)").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
21550
22538
|
const globals = program2.opts();
|
|
21551
22539
|
try {
|
|
21552
22540
|
await runAnalyze(opts, globals);
|
|
@@ -21561,18 +22549,18 @@ function registerAnalyzeCommand(program2) {
|
|
|
21561
22549
|
}
|
|
21562
22550
|
var tracing = () => process.env.VERITY_TRACE_PHASES === "1";
|
|
21563
22551
|
async function runAnalyze(opts, globals) {
|
|
21564
|
-
const
|
|
21565
|
-
installRunEvidence(
|
|
22552
|
+
const run2 = createRun(opts, globals);
|
|
22553
|
+
installRunEvidence(run2);
|
|
21566
22554
|
for (const [name, phase] of PIPELINE) {
|
|
21567
|
-
|
|
22555
|
+
run2.phaseReached = name;
|
|
21568
22556
|
if (!tracing()) {
|
|
21569
|
-
await phase(
|
|
21570
|
-
|
|
22557
|
+
await phase(run2);
|
|
22558
|
+
run2.phasesCompleted.push(name);
|
|
21571
22559
|
continue;
|
|
21572
22560
|
}
|
|
21573
22561
|
const started = Date.now();
|
|
21574
|
-
await phase(
|
|
21575
|
-
|
|
22562
|
+
await phase(run2);
|
|
22563
|
+
run2.phasesCompleted.push(name);
|
|
21576
22564
|
process.stderr.write(`verity\xB7phase ${name} ${Date.now() - started}ms
|
|
21577
22565
|
`);
|
|
21578
22566
|
}
|
|
@@ -21675,8 +22663,8 @@ async function runReview(opts, globals) {
|
|
|
21675
22663
|
for (const p of specPaths) {
|
|
21676
22664
|
if (!(0, import_node_fs37.existsSync)(p)) continue;
|
|
21677
22665
|
try {
|
|
21678
|
-
const { readFileSync:
|
|
21679
|
-
const content =
|
|
22666
|
+
const { readFileSync: readFileSync25 } = await import("node:fs");
|
|
22667
|
+
const content = readFileSync25(p, "utf-8");
|
|
21680
22668
|
specs.push({ path: p, content: content.slice(0, 10240) });
|
|
21681
22669
|
} catch {
|
|
21682
22670
|
}
|
|
@@ -21945,6 +22933,7 @@ function coverageBlock(c) {
|
|
|
21945
22933
|
const tree = c.root ? `${c.root}${c.linked ? " (linked worktree)" : ""}${c.branch ? ` \xB7 branch ${c.branch}` : ""}` : "(no tree resolved)";
|
|
21946
22934
|
lines.push(`Reviewed (${c.moment}): ${c.sent.length} file(s)${c.range ? ` @ ${c.range}` : ""}`);
|
|
21947
22935
|
lines.push(` Tree: ${tree}`);
|
|
22936
|
+
if (c.repoContext) lines.push(` Repo context: ${describeRepoContext(c.repoContext)}`);
|
|
21948
22937
|
for (const f of c.sent) lines.push(` - ${f}`);
|
|
21949
22938
|
if (c.excluded.length > 0) {
|
|
21950
22939
|
lines.push(` Excluded (${c.excluded.length}):`);
|
|
@@ -22011,6 +23000,39 @@ async function runGuard(opts, globals) {
|
|
|
22011
23000
|
statedIntent,
|
|
22012
23001
|
buildGuardCoverage(files, codeDelta, frame, range)
|
|
22013
23002
|
);
|
|
23003
|
+
try {
|
|
23004
|
+
const ig = loadVerityIgnore();
|
|
23005
|
+
const repoContext2 = buildRepoContext({
|
|
23006
|
+
deltaFiles: codeDelta.files,
|
|
23007
|
+
diffs: [],
|
|
23008
|
+
signalsByPath: rangeChangeSignals(frame, range, codeDelta.files.map((f) => f.path)),
|
|
23009
|
+
sentPaths: new Set(codeDelta.files.map((f) => f.path)),
|
|
23010
|
+
isExcluded: (p) => isVerityOwnedPath(p) || isIgnored(ig, p),
|
|
23011
|
+
cwd: frame.worktreeRoot ?? process.cwd()
|
|
23012
|
+
});
|
|
23013
|
+
upgradeToExcerpts(repoContext2, {
|
|
23014
|
+
readFile: (rel) => {
|
|
23015
|
+
try {
|
|
23016
|
+
return (0, import_node_fs38.readFileSync)((0, import_node_path27.join)(frame.worktreeRoot ?? process.cwd(), rel), "utf8");
|
|
23017
|
+
} catch {
|
|
23018
|
+
return null;
|
|
23019
|
+
}
|
|
23020
|
+
}
|
|
23021
|
+
});
|
|
23022
|
+
requestBody.repo_context = repoContext2;
|
|
23023
|
+
logEvent("repo_context", {
|
|
23024
|
+
moment,
|
|
23025
|
+
state: repoContext2.state,
|
|
23026
|
+
reason: repoContext2.reason ?? null,
|
|
23027
|
+
symbols: repoContext2.symbols?.length ?? 0,
|
|
23028
|
+
callers: repoContext2.callers?.length ?? 0,
|
|
23029
|
+
tests: repoContext2.tests?.length ?? 0,
|
|
23030
|
+
excerpts: repoContext2.excerpts?.length ?? 0,
|
|
23031
|
+
elapsed_ms: repoContext2.elapsed_ms ?? null
|
|
23032
|
+
});
|
|
23033
|
+
} catch (e) {
|
|
23034
|
+
logEvent("repo_context", { moment, state: "absent", reason: "exception", message: e.message });
|
|
23035
|
+
}
|
|
22014
23036
|
const coverage = {
|
|
22015
23037
|
moment,
|
|
22016
23038
|
root: frame.worktreeRoot,
|
|
@@ -22018,7 +23040,8 @@ async function runGuard(opts, globals) {
|
|
|
22018
23040
|
linked: frame.isLinkedWorktree,
|
|
22019
23041
|
range: describeRange(range),
|
|
22020
23042
|
sent: codeDelta.files.map((f) => f.path),
|
|
22021
|
-
excluded: codeDelta.excluded.map((e) => ({ path: e.path, reason: e.reason }))
|
|
23043
|
+
excluded: codeDelta.excluded.map((e) => ({ path: e.path, reason: e.reason })),
|
|
23044
|
+
...requestBody.repo_context ? { repoContext: requestBody.repo_context } : {}
|
|
22022
23045
|
};
|
|
22023
23046
|
logToFileOnly(coverageBlock(coverage));
|
|
22024
23047
|
const reviewStart = Date.now();
|
|
@@ -22265,22 +23288,297 @@ function registerWaiveCommand(program2) {
|
|
|
22265
23288
|
}
|
|
22266
23289
|
|
|
22267
23290
|
// src/commands/init.ts
|
|
22268
|
-
var
|
|
22269
|
-
var
|
|
23291
|
+
var import_node_fs44 = require("node:fs");
|
|
23292
|
+
var import_promises14 = require("node:fs/promises");
|
|
22270
23293
|
var import_node_path29 = require("node:path");
|
|
23294
|
+
var import_node_child_process14 = require("node:child_process");
|
|
23295
|
+
|
|
23296
|
+
// src/lib/banner.ts
|
|
23297
|
+
var WORDMARK = [
|
|
23298
|
+
"\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",
|
|
23299
|
+
"\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",
|
|
23300
|
+
"\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 ",
|
|
23301
|
+
"\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 ",
|
|
23302
|
+
" \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 ",
|
|
23303
|
+
" \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 "
|
|
23304
|
+
];
|
|
23305
|
+
var WORDMARK_NARROW = [
|
|
23306
|
+
"\u2588 \u2588 \u2588\u2580\u2580\u2580\u2580 \u2588\u2580\u2580\u2580\u2584 \u2580\u2580\u2588\u2580\u2580 \u2580\u2580\u2588\u2580\u2580 \u2588 \u2588",
|
|
23307
|
+
"\u2588 \u2588 \u2588\u2580\u2580\u2580 \u2588\u2584\u2584\u2584\u2580 \u2588 \u2588 \u2580\u2584\u2580 ",
|
|
23308
|
+
" \u2580\u2584\u2580 \u2588\u2584\u2584\u2584\u2584 \u2588 \u2580\u2584 \u2584\u2584\u2588\u2584\u2584 \u2588 \u2588 "
|
|
23309
|
+
];
|
|
23310
|
+
var TAGLINE = "reviews every turn, remembers every lesson";
|
|
23311
|
+
var WORDMARK_COLOR = ["\x1B[0;32m"];
|
|
23312
|
+
var DIM3 = "\x1B[2m";
|
|
23313
|
+
var RESET2 = "\x1B[0m";
|
|
23314
|
+
var INDENT = " ";
|
|
23315
|
+
function artWidth(art) {
|
|
23316
|
+
return Math.max(...art.map((r) => r.length)) + INDENT.length;
|
|
23317
|
+
}
|
|
23318
|
+
function canRenderArt() {
|
|
23319
|
+
return !!process.stderr.isTTY;
|
|
23320
|
+
}
|
|
23321
|
+
function printBanner(opts) {
|
|
23322
|
+
if (!opts.interactive) return;
|
|
23323
|
+
const columns = opts.columns ?? process.stderr.columns ?? 80;
|
|
23324
|
+
const color = colorEnabled();
|
|
23325
|
+
const version = `v${cliVersion()}`;
|
|
23326
|
+
const art = [WORDMARK, WORDMARK_NARROW].find((a) => artWidth(a) <= columns) ?? null;
|
|
23327
|
+
const dim = (text) => color ? `${DIM3}${text}${RESET2}` : text;
|
|
23328
|
+
process.stderr.write("\n");
|
|
23329
|
+
if (!art) {
|
|
23330
|
+
const candidates = [`Verity ${version}`, "Verity"];
|
|
23331
|
+
const text = candidates.find((c) => c.length + INDENT.length <= columns);
|
|
23332
|
+
if (text) process.stderr.write(`${INDENT}${dim(text)}
|
|
23333
|
+
`);
|
|
23334
|
+
process.stderr.write("\n");
|
|
23335
|
+
return;
|
|
23336
|
+
}
|
|
23337
|
+
art.forEach((row2, i) => {
|
|
23338
|
+
const tint = color && WORDMARK_COLOR.length > 0 ? WORDMARK_COLOR[Math.min(i, WORDMARK_COLOR.length - 1)] : "";
|
|
23339
|
+
const reset = color && tint ? RESET2 : "";
|
|
23340
|
+
process.stderr.write(`${INDENT}${tint}${row2}${reset}
|
|
23341
|
+
`);
|
|
23342
|
+
});
|
|
23343
|
+
if (TAGLINE.length + INDENT.length <= columns) {
|
|
23344
|
+
process.stderr.write(`${INDENT}${dim(TAGLINE)}
|
|
23345
|
+
`);
|
|
23346
|
+
}
|
|
23347
|
+
if (version.length + INDENT.length <= columns) {
|
|
23348
|
+
process.stderr.write(`${INDENT}${dim(version)}
|
|
23349
|
+
`);
|
|
23350
|
+
}
|
|
23351
|
+
process.stderr.write("\n");
|
|
23352
|
+
}
|
|
23353
|
+
function printPhase(n, of, title, subtitle) {
|
|
23354
|
+
const color = colorEnabled();
|
|
23355
|
+
const columns = process.stderr.columns ?? 72;
|
|
23356
|
+
const width = Math.min(columns, 72);
|
|
23357
|
+
const label2 = `\u2500\u2500 Phase ${n} of ${of} \xB7 ${title} `;
|
|
23358
|
+
const rule = label2 + "\u2500".repeat(Math.max(0, width - label2.length - 2));
|
|
23359
|
+
process.stderr.write("\n");
|
|
23360
|
+
process.stderr.write(color ? ` ${DIM3}${rule}${RESET2}
|
|
23361
|
+
` : ` ${rule}
|
|
23362
|
+
`);
|
|
23363
|
+
if (subtitle && subtitle.length + INDENT.length <= columns) {
|
|
23364
|
+
process.stderr.write(color ? ` ${DIM3}${subtitle}${RESET2}
|
|
23365
|
+
` : ` ${subtitle}
|
|
23366
|
+
`);
|
|
23367
|
+
}
|
|
23368
|
+
process.stderr.write("\n");
|
|
23369
|
+
}
|
|
23370
|
+
|
|
23371
|
+
// src/commands/doctor.ts
|
|
23372
|
+
var import_node_fs42 = require("node:fs");
|
|
23373
|
+
|
|
23374
|
+
// src/lib/prereqs.ts
|
|
22271
23375
|
var import_node_child_process11 = require("node:child_process");
|
|
22272
|
-
var
|
|
23376
|
+
var MIN_NODE_MAJOR = 20;
|
|
23377
|
+
function which(bin) {
|
|
23378
|
+
try {
|
|
23379
|
+
const out = (0, import_node_child_process11.execSync)(`command -v ${bin}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
23380
|
+
return out || null;
|
|
23381
|
+
} catch {
|
|
23382
|
+
return null;
|
|
23383
|
+
}
|
|
23384
|
+
}
|
|
23385
|
+
function checkNode() {
|
|
23386
|
+
const version = process.version;
|
|
23387
|
+
const major = Number.parseInt(version.slice(1), 10);
|
|
23388
|
+
const ok = Number.isFinite(major) && major >= MIN_NODE_MAJOR;
|
|
23389
|
+
return {
|
|
23390
|
+
id: "node",
|
|
23391
|
+
label: "Node.js",
|
|
23392
|
+
status: ok ? "ok" : "missing",
|
|
23393
|
+
detail: version,
|
|
23394
|
+
remedy: ok ? void 0 : `Node.js ${MIN_NODE_MAJOR}+ required \u2014 update from https://nodejs.org`,
|
|
23395
|
+
required: true
|
|
23396
|
+
};
|
|
23397
|
+
}
|
|
23398
|
+
function checkGit() {
|
|
23399
|
+
let detail = "";
|
|
23400
|
+
try {
|
|
23401
|
+
detail = (0, import_node_child_process11.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
23402
|
+
} catch {
|
|
23403
|
+
return {
|
|
23404
|
+
id: "git",
|
|
23405
|
+
label: "git",
|
|
23406
|
+
status: "missing",
|
|
23407
|
+
detail: "not found",
|
|
23408
|
+
remedy: "Install git from https://git-scm.com",
|
|
23409
|
+
required: true
|
|
23410
|
+
};
|
|
23411
|
+
}
|
|
23412
|
+
return { id: "git", label: "git", status: "ok", detail, required: true };
|
|
23413
|
+
}
|
|
23414
|
+
function checkClaude() {
|
|
23415
|
+
const path = which("claude");
|
|
23416
|
+
return {
|
|
23417
|
+
id: "claude",
|
|
23418
|
+
label: "Claude Code",
|
|
23419
|
+
status: path ? "ok" : "warn",
|
|
23420
|
+
detail: path ?? "not found",
|
|
23421
|
+
remedy: path ? void 0 : "Hooks are wired but need Claude Code to fire \u2014 https://claude.com/claude-code",
|
|
23422
|
+
required: false
|
|
23423
|
+
};
|
|
23424
|
+
}
|
|
23425
|
+
function checkAnalysisCli() {
|
|
23426
|
+
const path = which("codacy-analysis");
|
|
23427
|
+
return {
|
|
23428
|
+
id: "analysis-cli",
|
|
23429
|
+
label: "@codacy/analysis-cli",
|
|
23430
|
+
status: path ? "ok" : "warn",
|
|
23431
|
+
detail: path ?? "not found",
|
|
23432
|
+
remedy: path ? void 0 : "npm install -g @codacy/analysis-cli (static findings are unavailable until then)",
|
|
23433
|
+
required: false
|
|
23434
|
+
};
|
|
23435
|
+
}
|
|
23436
|
+
var INSTALL_TIMEOUT_MS = 12e4;
|
|
23437
|
+
function run(command, args, opts = {}) {
|
|
23438
|
+
return new Promise((resolve4) => {
|
|
23439
|
+
const child = (0, import_node_child_process11.spawn)(command, args, {
|
|
23440
|
+
stdio: opts.inherit ? "inherit" : "pipe",
|
|
23441
|
+
timeout: INSTALL_TIMEOUT_MS
|
|
23442
|
+
});
|
|
23443
|
+
child.on("error", () => resolve4(false));
|
|
23444
|
+
child.on("close", (code) => resolve4(code === 0));
|
|
23445
|
+
});
|
|
23446
|
+
}
|
|
23447
|
+
async function installAnalysisCli() {
|
|
23448
|
+
const spinner = startSpinner("Installing @codacy/analysis-cli");
|
|
23449
|
+
const ok = await run("npm", ["install", "-g", "@codacy/analysis-cli"]);
|
|
23450
|
+
if (ok) {
|
|
23451
|
+
const check = checkAnalysisCli();
|
|
23452
|
+
if (check.status === "ok") {
|
|
23453
|
+
spinner.succeed("@codacy/analysis-cli installed");
|
|
23454
|
+
return { ...check, justInstalled: true };
|
|
23455
|
+
}
|
|
23456
|
+
spinner.warn("npm reported success but codacy-analysis is not on PATH");
|
|
23457
|
+
return check;
|
|
23458
|
+
}
|
|
23459
|
+
spinner.stop();
|
|
23460
|
+
{
|
|
23461
|
+
const failed = {
|
|
23462
|
+
id: "analysis-cli",
|
|
23463
|
+
label: "@codacy/analysis-cli",
|
|
23464
|
+
status: "warn",
|
|
23465
|
+
detail: "install failed",
|
|
23466
|
+
remedy: "Install manually: npm install -g @codacy/analysis-cli",
|
|
23467
|
+
required: false
|
|
23468
|
+
};
|
|
23469
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return failed;
|
|
23470
|
+
process.stderr.write(" Retrying with sudo (you may be asked for your password)\u2026\n");
|
|
23471
|
+
const sudoOk = await run("sudo", ["npm", "install", "-g", "@codacy/analysis-cli"], { inherit: true });
|
|
23472
|
+
if (!sudoOk) return failed;
|
|
23473
|
+
const check = checkAnalysisCli();
|
|
23474
|
+
return check.status === "ok" ? { ...check, justInstalled: true } : check;
|
|
23475
|
+
}
|
|
23476
|
+
}
|
|
23477
|
+
async function checkPrereqs(opts = {}) {
|
|
23478
|
+
const checks = [checkNode(), checkGit(), checkClaude()];
|
|
23479
|
+
let analysis = checkAnalysisCli();
|
|
23480
|
+
if (analysis.status !== "ok" && opts.install) {
|
|
23481
|
+
analysis = await installAnalysisCli();
|
|
23482
|
+
}
|
|
23483
|
+
checks.push(analysis);
|
|
23484
|
+
return { checks, blocked: checks.some((c) => c.required && c.status !== "ok") };
|
|
23485
|
+
}
|
|
22273
23486
|
|
|
22274
|
-
// src/
|
|
23487
|
+
// src/lib/telemetry.ts
|
|
23488
|
+
var import_promises12 = require("node:fs/promises");
|
|
23489
|
+
|
|
23490
|
+
// src/lib/gitignore.ts
|
|
23491
|
+
var import_node_child_process12 = require("node:child_process");
|
|
22275
23492
|
var import_node_fs40 = require("node:fs");
|
|
22276
|
-
var
|
|
22277
|
-
var
|
|
23493
|
+
var VERITY_GITIGNORE_MARKER = "# Verity \u2014 machine-local state.";
|
|
23494
|
+
var SETTINGS_LOCAL_IGNORE_ENTRY = ".claude/settings.local.json";
|
|
23495
|
+
var VERITY_GITIGNORE_BLOCK = [
|
|
23496
|
+
"# Verity \u2014 machine-local state. Everything in .verity/ is ignored EXCEPT the",
|
|
23497
|
+
"# shared standard and the knowledge graph, which are meant to be committed.",
|
|
23498
|
+
".verity/*",
|
|
23499
|
+
"!.verity/standard.yaml",
|
|
23500
|
+
"!.verity/memory/",
|
|
23501
|
+
".verity/memory/log.md",
|
|
23502
|
+
SETTINGS_LOCAL_IGNORE_ENTRY,
|
|
23503
|
+
""
|
|
23504
|
+
].join("\n");
|
|
23505
|
+
var BREAKING_ENTRIES = /* @__PURE__ */ new Set([".verity/", ".verity"]);
|
|
23506
|
+
function isIgnored2(path) {
|
|
23507
|
+
try {
|
|
23508
|
+
(0, import_node_child_process12.execSync)(`git check-ignore -q -- "${path}"`, { stdio: "pipe" });
|
|
23509
|
+
return true;
|
|
23510
|
+
} catch (err) {
|
|
23511
|
+
return err.status === 1 ? false : null;
|
|
23512
|
+
}
|
|
23513
|
+
}
|
|
23514
|
+
function semanticsHold() {
|
|
23515
|
+
const snapshot = isIgnored2(".verity/.snapshot/__probe__");
|
|
23516
|
+
const standard = isIgnored2(".verity/standard.yaml");
|
|
23517
|
+
const node = isIgnored2(".verity/memory/domain/__probe__.md");
|
|
23518
|
+
const futureState = isIgnored2(".verity/.__probe-future-state__");
|
|
23519
|
+
if (snapshot === null || standard === null || node === null || futureState === null) return null;
|
|
23520
|
+
return snapshot === true && futureState === true && standard === false && node === false;
|
|
23521
|
+
}
|
|
23522
|
+
function ensureVerityGitignore() {
|
|
23523
|
+
let content = "";
|
|
23524
|
+
try {
|
|
23525
|
+
content = (0, import_node_fs40.readFileSync)(".gitignore", "utf-8");
|
|
23526
|
+
} catch {
|
|
23527
|
+
}
|
|
23528
|
+
const hasMarker = content.includes(VERITY_GITIGNORE_MARKER);
|
|
23529
|
+
const lines = content.split("\n");
|
|
23530
|
+
const breakingCount = lines.filter((l) => BREAKING_ENTRIES.has(l.trim())).length;
|
|
23531
|
+
const needsRepair = breakingCount > 0;
|
|
23532
|
+
const verified = (result) => semanticsHold() === false ? "conflict" : result;
|
|
23533
|
+
if (hasMarker && !needsRepair) return verified("covered");
|
|
23534
|
+
if (!hasMarker && !needsRepair) {
|
|
23535
|
+
if (semanticsHold() === true) return "covered";
|
|
23536
|
+
}
|
|
23537
|
+
try {
|
|
23538
|
+
let next = content;
|
|
23539
|
+
if (needsRepair) {
|
|
23540
|
+
next = lines.map((l) => BREAKING_ENTRIES.has(l.trim()) ? ".verity/*" : l).join("\n");
|
|
23541
|
+
}
|
|
23542
|
+
if (!hasMarker) {
|
|
23543
|
+
const sep2 = next === "" ? "" : next.endsWith("\n") ? "\n" : "\n\n";
|
|
23544
|
+
next = next + sep2 + VERITY_GITIGNORE_BLOCK;
|
|
23545
|
+
}
|
|
23546
|
+
(0, import_node_fs40.writeFileSync)(".gitignore", next);
|
|
23547
|
+
return verified(needsRepair ? "repaired" : "added");
|
|
23548
|
+
} catch {
|
|
23549
|
+
return "failed";
|
|
23550
|
+
}
|
|
23551
|
+
}
|
|
23552
|
+
function committedVerityState() {
|
|
23553
|
+
let out = "";
|
|
23554
|
+
try {
|
|
23555
|
+
out = (0, import_node_child_process12.execSync)("git ls-files -z -- .verity", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
23556
|
+
} catch {
|
|
23557
|
+
return [];
|
|
23558
|
+
}
|
|
23559
|
+
return out.split("\0").filter(Boolean).filter((p) => p !== ".verity/standard.yaml" && !(p.startsWith(".verity/memory/") && p !== ".verity/memory/log.md"));
|
|
23560
|
+
}
|
|
23561
|
+
function untrackVerityState() {
|
|
23562
|
+
const tracked = committedVerityState();
|
|
23563
|
+
if (tracked.length === 0) return "none";
|
|
23564
|
+
try {
|
|
23565
|
+
(0, import_node_child_process12.execSync)("git rm -r --cached --quiet -- .verity", { stdio: "pipe" });
|
|
23566
|
+
for (const keep of [".verity/standard.yaml", ".verity/memory"]) {
|
|
23567
|
+
try {
|
|
23568
|
+
(0, import_node_child_process12.execSync)(`git add -- "${keep}"`, { stdio: "pipe" });
|
|
23569
|
+
} catch {
|
|
23570
|
+
}
|
|
23571
|
+
}
|
|
23572
|
+
return "untracked";
|
|
23573
|
+
} catch {
|
|
23574
|
+
return "failed";
|
|
23575
|
+
}
|
|
23576
|
+
}
|
|
22278
23577
|
|
|
22279
23578
|
// src/lib/telemetry.ts
|
|
22280
|
-
var import_promises12 = require("node:fs/promises");
|
|
22281
23579
|
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
22282
23580
|
var GITIGNORE_FILE = ".gitignore";
|
|
22283
|
-
var GITIGNORE_ENTRY =
|
|
23581
|
+
var GITIGNORE_ENTRY = SETTINGS_LOCAL_IGNORE_ENTRY;
|
|
22284
23582
|
var OTEL_HEADERS_HELPER_CMD = "verity telemetry headers";
|
|
22285
23583
|
var LEGACY_TELEMETRY_ENV_KEYS = ["OTEL_EXPORTER_OTLP_HEADERS"];
|
|
22286
23584
|
function deriveOtlpEndpoint(serviceUrl) {
|
|
@@ -22366,14 +23664,119 @@ async function uninstallTelemetry() {
|
|
|
22366
23664
|
return { ok: true, data: { removed } };
|
|
22367
23665
|
}
|
|
22368
23666
|
|
|
23667
|
+
// src/lib/setup-state.ts
|
|
23668
|
+
var import_promises13 = require("node:fs/promises");
|
|
23669
|
+
var import_node_fs41 = require("node:fs");
|
|
23670
|
+
var SETUP_STATE_FILE = `${VERITY_DIR}/setup.json`;
|
|
23671
|
+
async function readSetupState() {
|
|
23672
|
+
const path = projectPath(SETUP_STATE_FILE);
|
|
23673
|
+
if (!(0, import_node_fs41.existsSync)(path)) return null;
|
|
23674
|
+
try {
|
|
23675
|
+
const parsed = JSON.parse(await (0, import_promises13.readFile)(path, "utf-8"));
|
|
23676
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
23677
|
+
} catch {
|
|
23678
|
+
return null;
|
|
23679
|
+
}
|
|
23680
|
+
}
|
|
23681
|
+
async function writeSetupState(patch) {
|
|
23682
|
+
const current = await readSetupState() ?? { version: 1 };
|
|
23683
|
+
const next = { ...current, ...patch, version: 1 };
|
|
23684
|
+
await writeJsonFilePreservingStyle(projectPath(SETUP_STATE_FILE), next);
|
|
23685
|
+
return next;
|
|
23686
|
+
}
|
|
23687
|
+
|
|
23688
|
+
// src/commands/doctor.ts
|
|
23689
|
+
async function buildReport() {
|
|
23690
|
+
const prereqs = await checkPrereqs({ install: false });
|
|
23691
|
+
const state = await readSetupState();
|
|
23692
|
+
const hooks = await checkAllVerityHooks();
|
|
23693
|
+
const telemetry = await checkTelemetry();
|
|
23694
|
+
const artifacts = {
|
|
23695
|
+
standard: (0, import_node_fs42.existsSync)(projectPath(STANDARD_FILE)),
|
|
23696
|
+
analysisConfig: (0, import_node_fs42.existsSync)(projectPath(CODACY_CONFIG_FILE)),
|
|
23697
|
+
verityMd: (0, import_node_fs42.existsSync)(projectPath(VERITY_MD_FILE))
|
|
23698
|
+
};
|
|
23699
|
+
const next = [];
|
|
23700
|
+
for (const c of prereqs.checks) {
|
|
23701
|
+
if (c.status !== "ok" && c.remedy) next.push(c.remedy);
|
|
23702
|
+
}
|
|
23703
|
+
if (!state?.init) next.push('Run "verity init" \u2014 the deterministic setup phase has not completed here.');
|
|
23704
|
+
if (!artifacts.standard || !artifacts.analysisConfig || !artifacts.verityMd) {
|
|
23705
|
+
next.push("Run /verity-setup in Claude Code \u2014 the Standard, analysis config, and VERITY.md are synthesized there.");
|
|
23706
|
+
}
|
|
23707
|
+
const noAnalysisMoment = !hooks.stop && hooks.guardOn.length === 0;
|
|
23708
|
+
if (noAnalysisMoment) {
|
|
23709
|
+
next.push('No analysis moment is active \u2014 run "verity hooks install --moments stop" or re-run "verity init".');
|
|
23710
|
+
}
|
|
23711
|
+
if (state?.telemetry === "deferred") {
|
|
23712
|
+
next.push('Telemetry was requested but needs a token \u2014 run "verity login", then "verity telemetry install".');
|
|
23713
|
+
}
|
|
23714
|
+
return {
|
|
23715
|
+
prerequisites: prereqs.checks,
|
|
23716
|
+
blocked: prereqs.blocked,
|
|
23717
|
+
phases: {
|
|
23718
|
+
init: { done: !!state?.init, ...state?.init ?? {} },
|
|
23719
|
+
setup: { done: artifacts.standard && artifacts.analysisConfig && artifacts.verityMd }
|
|
23720
|
+
},
|
|
23721
|
+
answers: {
|
|
23722
|
+
intensity: state?.intensity ?? null,
|
|
23723
|
+
moments: state?.moments ?? null,
|
|
23724
|
+
telemetry: state?.telemetry ?? null
|
|
23725
|
+
},
|
|
23726
|
+
hooks: { ...hooks, noAnalysisMoment },
|
|
23727
|
+
telemetry: { enabled: telemetry.enabled, endpoint: telemetry.endpoint },
|
|
23728
|
+
artifacts,
|
|
23729
|
+
next
|
|
23730
|
+
};
|
|
23731
|
+
}
|
|
23732
|
+
function registerDoctorCommand(program2) {
|
|
23733
|
+
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) => {
|
|
23734
|
+
const report = await buildReport();
|
|
23735
|
+
if (opts.json) {
|
|
23736
|
+
printJson(report);
|
|
23737
|
+
if (report.blocked) process.exit(1);
|
|
23738
|
+
return;
|
|
23739
|
+
}
|
|
23740
|
+
printInfo("Prerequisites:");
|
|
23741
|
+
for (const c of report.prerequisites) {
|
|
23742
|
+
if (c.status === "ok") printInfo(` ${c.label} ${c.detail} \u2713`);
|
|
23743
|
+
else printWarn(` ${c.label}: ${c.detail}${c.remedy ? ` \u2014 ${c.remedy}` : ""}`);
|
|
23744
|
+
}
|
|
23745
|
+
printInfo("Setup:");
|
|
23746
|
+
printInfo(` verity init: ${report.phases.init.done ? `done ${report.phases.init.completed_at ?? ""}`.trim() : "NOT run here"}`);
|
|
23747
|
+
printInfo(` /verity-setup: ${report.phases.setup.done ? "done" : "not completed"}`);
|
|
23748
|
+
printInfo(` intensity: ${report.answers.intensity ?? "\u2014"} moments: ${report.answers.moments?.join(", ") ?? "\u2014"}`);
|
|
23749
|
+
printInfo("Hooks:");
|
|
23750
|
+
printInfo(` Stop (verity analyze): ${report.hooks.stop ? "on" : "off"}`);
|
|
23751
|
+
printInfo(` Git-moment gate: ${report.hooks.guardOn.length ? report.hooks.guardOn.join(", ") : "off"}`);
|
|
23752
|
+
printInfo(` Infra (intent/baseline/compact/session-end): ${[report.hooks.intent, report.hooks.baseline, report.hooks.compact, report.hooks.sessionEnd].filter(Boolean).length}/4`);
|
|
23753
|
+
printInfo(`Telemetry: ${report.telemetry.enabled ? `enabled \u2192 ${report.telemetry.endpoint}` : "disabled"}`);
|
|
23754
|
+
printInfo("Artifacts:");
|
|
23755
|
+
printInfo(` .verity/standard.yaml: ${report.artifacts.standard ? "\u2713" : "missing"}`);
|
|
23756
|
+
printInfo(` .codacy/codacy.config.json: ${report.artifacts.analysisConfig ? "\u2713" : "missing"}`);
|
|
23757
|
+
printInfo(` VERITY.md: ${report.artifacts.verityMd ? "\u2713" : "missing"}`);
|
|
23758
|
+
if (report.next.length > 0) {
|
|
23759
|
+
console.log("");
|
|
23760
|
+
printWarn("Next:");
|
|
23761
|
+
for (const n of report.next) printWarn(` - ${n}`);
|
|
23762
|
+
} else {
|
|
23763
|
+
printInfo("Setup is complete.");
|
|
23764
|
+
}
|
|
23765
|
+
if (report.blocked) process.exit(1);
|
|
23766
|
+
});
|
|
23767
|
+
}
|
|
23768
|
+
|
|
22369
23769
|
// src/commands/migrate.ts
|
|
23770
|
+
var import_node_fs43 = require("node:fs");
|
|
23771
|
+
var import_node_path28 = require("node:path");
|
|
23772
|
+
var import_node_child_process13 = require("node:child_process");
|
|
22370
23773
|
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
22371
23774
|
function defaultNpmRemover(pkg) {
|
|
22372
|
-
(0,
|
|
23775
|
+
(0, import_node_child_process13.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
|
|
22373
23776
|
}
|
|
22374
23777
|
function isGitTracked(cwd, relPath) {
|
|
22375
23778
|
try {
|
|
22376
|
-
(0,
|
|
23779
|
+
(0, import_node_child_process13.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
|
|
22377
23780
|
return true;
|
|
22378
23781
|
} catch {
|
|
22379
23782
|
return false;
|
|
@@ -22381,7 +23784,7 @@ function isGitTracked(cwd, relPath) {
|
|
|
22381
23784
|
}
|
|
22382
23785
|
function isGitRepo(cwd) {
|
|
22383
23786
|
try {
|
|
22384
|
-
(0,
|
|
23787
|
+
(0, import_node_child_process13.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
|
|
22385
23788
|
return true;
|
|
22386
23789
|
} catch {
|
|
22387
23790
|
return false;
|
|
@@ -22404,10 +23807,10 @@ async function runMigration(opts = {}) {
|
|
|
22404
23807
|
function migrateProjectDir(root, actions) {
|
|
22405
23808
|
const gateDir = (0, import_node_path28.join)(root, ".gate");
|
|
22406
23809
|
const verityDir = (0, import_node_path28.join)(root, ".verity");
|
|
22407
|
-
if ((0,
|
|
23810
|
+
if ((0, import_node_fs43.existsSync)(gateDir) && !(0, import_node_fs43.existsSync)(verityDir)) {
|
|
22408
23811
|
return migrateProjectDirRename(root, gateDir, verityDir, actions);
|
|
22409
23812
|
}
|
|
22410
|
-
if ((0,
|
|
23813
|
+
if ((0, import_node_fs43.existsSync)(gateDir) && (0, import_node_fs43.existsSync)(verityDir)) {
|
|
22411
23814
|
return migrateProjectDirCarry(gateDir, verityDir, actions);
|
|
22412
23815
|
}
|
|
22413
23816
|
return false;
|
|
@@ -22421,20 +23824,20 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
|
|
|
22421
23824
|
);
|
|
22422
23825
|
}
|
|
22423
23826
|
try {
|
|
22424
|
-
(0,
|
|
23827
|
+
(0, import_node_child_process13.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
|
|
22425
23828
|
actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
|
|
22426
23829
|
moved = true;
|
|
22427
23830
|
} catch {
|
|
22428
23831
|
}
|
|
22429
23832
|
}
|
|
22430
23833
|
if (moved) {
|
|
22431
|
-
if ((0,
|
|
23834
|
+
if ((0, import_node_fs43.existsSync)(gateDir)) {
|
|
22432
23835
|
const carried = carryLegacyContents(gateDir, verityDir);
|
|
22433
23836
|
if (carried > 0) {
|
|
22434
23837
|
actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
|
|
22435
23838
|
}
|
|
22436
23839
|
try {
|
|
22437
|
-
(0,
|
|
23840
|
+
(0, import_node_fs43.rmSync)(gateDir, { recursive: true, force: true });
|
|
22438
23841
|
} catch {
|
|
22439
23842
|
}
|
|
22440
23843
|
}
|
|
@@ -22450,7 +23853,7 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
|
|
|
22450
23853
|
actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
|
|
22451
23854
|
}
|
|
22452
23855
|
try {
|
|
22453
|
-
(0,
|
|
23856
|
+
(0, import_node_fs43.rmSync)(gateDir, { recursive: true, force: true });
|
|
22454
23857
|
} catch {
|
|
22455
23858
|
}
|
|
22456
23859
|
return carried > 0;
|
|
@@ -22459,9 +23862,9 @@ function migrateGlobalCredentials(home, actions) {
|
|
|
22459
23862
|
if (!home) return;
|
|
22460
23863
|
const gateCreds = (0, import_node_path28.join)(home, ".gate", "credentials");
|
|
22461
23864
|
const verityCreds = (0, import_node_path28.join)(home, ".verity", "credentials");
|
|
22462
|
-
if (!(0,
|
|
22463
|
-
if (!(0,
|
|
22464
|
-
(0,
|
|
23865
|
+
if (!(0, import_node_fs43.existsSync)(gateCreds)) return;
|
|
23866
|
+
if (!(0, import_node_fs43.existsSync)(verityCreds)) {
|
|
23867
|
+
(0, import_node_fs43.mkdirSync)((0, import_node_path28.join)(home, ".verity"), { recursive: true });
|
|
22465
23868
|
moveFile(gateCreds, verityCreds);
|
|
22466
23869
|
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
22467
23870
|
return;
|
|
@@ -22484,7 +23887,7 @@ async function migrateLegacyHooks(root, actions) {
|
|
|
22484
23887
|
}
|
|
22485
23888
|
async function migrateClaudeMd(root, actions) {
|
|
22486
23889
|
const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
|
|
22487
|
-
const hadLegacyBlock = (0,
|
|
23890
|
+
const hadLegacyBlock = (0, import_node_fs43.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
|
|
22488
23891
|
if (!hadLegacyBlock) return;
|
|
22489
23892
|
try {
|
|
22490
23893
|
await ensureClaudeMdPointer(root);
|
|
@@ -22496,11 +23899,11 @@ async function migrateClaudeMd(root, actions) {
|
|
|
22496
23899
|
function migrateStandardFile(root, actions) {
|
|
22497
23900
|
const gateMd = (0, import_node_path28.join)(root, "GATE.md");
|
|
22498
23901
|
const verityMd = (0, import_node_path28.join)(root, "VERITY.md");
|
|
22499
|
-
if (!(0,
|
|
23902
|
+
if (!(0, import_node_fs43.existsSync)(gateMd) || (0, import_node_fs43.existsSync)(verityMd)) return;
|
|
22500
23903
|
let moved = false;
|
|
22501
23904
|
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
22502
23905
|
try {
|
|
22503
|
-
(0,
|
|
23906
|
+
(0, import_node_child_process13.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
|
|
22504
23907
|
moved = true;
|
|
22505
23908
|
} catch {
|
|
22506
23909
|
}
|
|
@@ -22508,12 +23911,12 @@ function migrateStandardFile(root, actions) {
|
|
|
22508
23911
|
if (!moved) moveFile(gateMd, verityMd);
|
|
22509
23912
|
const content = readFileSyncSafe(verityMd);
|
|
22510
23913
|
const refreshed = content.split("GATE.md").join("VERITY.md");
|
|
22511
|
-
if (refreshed !== content) (0,
|
|
23914
|
+
if (refreshed !== content) (0, import_node_fs43.writeFileSync)(verityMd, refreshed);
|
|
22512
23915
|
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
22513
23916
|
}
|
|
22514
23917
|
async function migrateTelemetryHeaders(root, actions) {
|
|
22515
23918
|
const file = (0, import_node_path28.join)(root, ".claude", "settings.local.json");
|
|
22516
|
-
if (!(0,
|
|
23919
|
+
if (!(0, import_node_fs43.existsSync)(file)) return;
|
|
22517
23920
|
let settings;
|
|
22518
23921
|
try {
|
|
22519
23922
|
settings = JSON.parse(readFileSyncSafe(file) || "{}");
|
|
@@ -22561,21 +23964,21 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
|
|
|
22561
23964
|
}
|
|
22562
23965
|
if (toAppend.length > 0) {
|
|
22563
23966
|
const sep2 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
|
|
22564
|
-
(0,
|
|
23967
|
+
(0, import_node_fs43.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
|
|
22565
23968
|
}
|
|
22566
|
-
(0,
|
|
23969
|
+
(0, import_node_fs43.rmSync)(gateCreds, { force: true });
|
|
22567
23970
|
return toAppend.length;
|
|
22568
23971
|
}
|
|
22569
23972
|
function readFileSyncSafe(path) {
|
|
22570
23973
|
try {
|
|
22571
|
-
return (0,
|
|
23974
|
+
return (0, import_node_fs43.readFileSync)(path, "utf-8");
|
|
22572
23975
|
} catch {
|
|
22573
23976
|
return "";
|
|
22574
23977
|
}
|
|
22575
23978
|
}
|
|
22576
23979
|
function hasStagedChanges(root) {
|
|
22577
23980
|
try {
|
|
22578
|
-
(0,
|
|
23981
|
+
(0, import_node_child_process13.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
|
|
22579
23982
|
return false;
|
|
22580
23983
|
} catch {
|
|
22581
23984
|
return true;
|
|
@@ -22583,35 +23986,35 @@ function hasStagedChanges(root) {
|
|
|
22583
23986
|
}
|
|
22584
23987
|
function moveDir(from, to) {
|
|
22585
23988
|
try {
|
|
22586
|
-
(0,
|
|
23989
|
+
(0, import_node_fs43.renameSync)(from, to);
|
|
22587
23990
|
} catch (err) {
|
|
22588
23991
|
if (err.code !== "EXDEV") throw err;
|
|
22589
|
-
(0,
|
|
22590
|
-
(0,
|
|
23992
|
+
(0, import_node_fs43.cpSync)(from, to, { recursive: true });
|
|
23993
|
+
(0, import_node_fs43.rmSync)(from, { recursive: true, force: true });
|
|
22591
23994
|
}
|
|
22592
23995
|
}
|
|
22593
23996
|
function moveFile(from, to) {
|
|
22594
23997
|
try {
|
|
22595
|
-
(0,
|
|
23998
|
+
(0, import_node_fs43.renameSync)(from, to);
|
|
22596
23999
|
} catch (err) {
|
|
22597
24000
|
if (err.code !== "EXDEV") throw err;
|
|
22598
|
-
(0,
|
|
22599
|
-
(0,
|
|
24001
|
+
(0, import_node_fs43.cpSync)(from, to);
|
|
24002
|
+
(0, import_node_fs43.rmSync)(from, { force: true });
|
|
22600
24003
|
}
|
|
22601
24004
|
}
|
|
22602
24005
|
function carryLegacyContents(gateDir, verityDir) {
|
|
22603
24006
|
let copied = 0;
|
|
22604
24007
|
const walk = (relDir) => {
|
|
22605
24008
|
const srcDir = (0, import_node_path28.join)(gateDir, relDir);
|
|
22606
|
-
for (const entry of (0,
|
|
24009
|
+
for (const entry of (0, import_node_fs43.readdirSync)(srcDir)) {
|
|
22607
24010
|
const rel = relDir ? (0, import_node_path28.join)(relDir, entry) : entry;
|
|
22608
24011
|
const src = (0, import_node_path28.join)(gateDir, rel);
|
|
22609
24012
|
const dest = (0, import_node_path28.join)(verityDir, rel);
|
|
22610
|
-
if ((0,
|
|
24013
|
+
if ((0, import_node_fs43.statSync)(src).isDirectory()) {
|
|
22611
24014
|
walk(rel);
|
|
22612
|
-
} else if (!(0,
|
|
22613
|
-
(0,
|
|
22614
|
-
(0,
|
|
24015
|
+
} else if (!(0, import_node_fs43.existsSync)(dest)) {
|
|
24016
|
+
(0, import_node_fs43.mkdirSync)((0, import_node_path28.dirname)(dest), { recursive: true });
|
|
24017
|
+
(0, import_node_fs43.cpSync)(src, dest);
|
|
22615
24018
|
copied++;
|
|
22616
24019
|
}
|
|
22617
24020
|
}
|
|
@@ -22622,20 +24025,20 @@ function carryLegacyContents(gateDir, verityDir) {
|
|
|
22622
24025
|
async function needsMigration(root = repoRoot()) {
|
|
22623
24026
|
const gateDir = (0, import_node_path28.join)(root, ".gate");
|
|
22624
24027
|
const verityDir = (0, import_node_path28.join)(root, ".verity");
|
|
22625
|
-
if ((0,
|
|
22626
|
-
if ((0,
|
|
22627
|
-
if ((0,
|
|
24028
|
+
if ((0, import_node_fs43.existsSync)(gateDir) && !(0, import_node_fs43.existsSync)(verityDir)) return true;
|
|
24029
|
+
if ((0, import_node_fs43.existsSync)(gateDir) && (0, import_node_fs43.existsSync)(verityDir)) {
|
|
24030
|
+
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
24031
|
return true;
|
|
22629
24032
|
}
|
|
22630
|
-
if ((0,
|
|
24033
|
+
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
24034
|
return true;
|
|
22632
24035
|
}
|
|
22633
24036
|
}
|
|
22634
24037
|
const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
|
|
22635
|
-
if ((0,
|
|
24038
|
+
if ((0, import_node_fs43.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
22636
24039
|
return true;
|
|
22637
24040
|
}
|
|
22638
|
-
if ((0,
|
|
24041
|
+
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
24042
|
return true;
|
|
22640
24043
|
}
|
|
22641
24044
|
if (await hasLegacyHooksAt(root)) return true;
|
|
@@ -22660,17 +24063,96 @@ function registerMigrateCommand(program2) {
|
|
|
22660
24063
|
});
|
|
22661
24064
|
}
|
|
22662
24065
|
|
|
22663
|
-
// src/
|
|
22664
|
-
|
|
22665
|
-
|
|
22666
|
-
|
|
24066
|
+
// src/lib/prompt.ts
|
|
24067
|
+
var readline2 = __toESM(require("node:readline/promises"));
|
|
24068
|
+
function interactive() {
|
|
24069
|
+
return !!process.stdin.isTTY && !!process.stdout.isTTY;
|
|
24070
|
+
}
|
|
24071
|
+
async function askLine(question, io = {}) {
|
|
24072
|
+
const rl = readline2.createInterface({
|
|
24073
|
+
input: io.input ?? process.stdin,
|
|
24074
|
+
output: io.output ?? process.stdout
|
|
24075
|
+
});
|
|
22667
24076
|
try {
|
|
22668
|
-
|
|
22669
|
-
|
|
24077
|
+
return await new Promise((resolve4) => {
|
|
24078
|
+
rl.question(question).then((a) => resolve4(a.trim())).catch(() => resolve4(null));
|
|
24079
|
+
rl.once("close", () => setImmediate(() => resolve4(null)));
|
|
24080
|
+
});
|
|
22670
24081
|
} finally {
|
|
22671
24082
|
rl.close();
|
|
22672
24083
|
}
|
|
22673
24084
|
}
|
|
24085
|
+
var ask = (question) => askLine(question);
|
|
24086
|
+
function parseChoice(answer, choices, fallback) {
|
|
24087
|
+
const a = answer.trim().toLowerCase();
|
|
24088
|
+
if (a === "") return { id: fallback, recognized: true };
|
|
24089
|
+
const byNumber = Number.parseInt(a, 10);
|
|
24090
|
+
if (Number.isInteger(byNumber) && byNumber >= 1 && byNumber <= choices.length) {
|
|
24091
|
+
return { id: choices[byNumber - 1].id, recognized: true };
|
|
24092
|
+
}
|
|
24093
|
+
const byId = choices.find((c) => c.id.toLowerCase() === a || c.label.toLowerCase() === a);
|
|
24094
|
+
if (byId) return { id: byId.id, recognized: true };
|
|
24095
|
+
return { id: fallback, recognized: false };
|
|
24096
|
+
}
|
|
24097
|
+
function parseMultiSelect(answer, choices, fallback) {
|
|
24098
|
+
const a = answer.trim().toLowerCase();
|
|
24099
|
+
if (a === "") return { ids: [...fallback], recognized: true };
|
|
24100
|
+
const picked = /* @__PURE__ */ new Set();
|
|
24101
|
+
for (const part of a.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
24102
|
+
const n = Number.parseInt(part, 10);
|
|
24103
|
+
if (Number.isInteger(n) && n >= 1 && n <= choices.length) {
|
|
24104
|
+
picked.add(choices[n - 1].id);
|
|
24105
|
+
continue;
|
|
24106
|
+
}
|
|
24107
|
+
const byId = choices.find((c) => c.id.toLowerCase() === part || c.label.toLowerCase() === part);
|
|
24108
|
+
if (byId) picked.add(byId.id);
|
|
24109
|
+
}
|
|
24110
|
+
if (picked.size === 0) return { ids: [...fallback], recognized: false };
|
|
24111
|
+
return { ids: choices.filter((c) => picked.has(c.id)).map((c) => c.id), recognized: true };
|
|
24112
|
+
}
|
|
24113
|
+
async function promptYes(question, opts) {
|
|
24114
|
+
if (!interactive()) return opts.nonInteractive;
|
|
24115
|
+
const answer = await ask(question);
|
|
24116
|
+
if (answer === null) return opts.nonInteractive;
|
|
24117
|
+
const a = answer.toLowerCase();
|
|
24118
|
+
return a === "" || a === "y" || a === "yes";
|
|
24119
|
+
}
|
|
24120
|
+
function printOptions(question, choices) {
|
|
24121
|
+
console.log("");
|
|
24122
|
+
console.log(` ${question}`);
|
|
24123
|
+
choices.forEach((c, i) => {
|
|
24124
|
+
const tag = c.recommended ? " (recommended)" : "";
|
|
24125
|
+
console.log(` ${i + 1}. ${c.label}${tag}${c.hint ? ` \u2014 ${c.hint}` : ""}`);
|
|
24126
|
+
});
|
|
24127
|
+
}
|
|
24128
|
+
async function promptChoice(question, choices, fallback) {
|
|
24129
|
+
if (!interactive()) return fallback;
|
|
24130
|
+
printOptions(question, choices);
|
|
24131
|
+
const defaultIdx = choices.findIndex((c) => c.id === fallback);
|
|
24132
|
+
const answer = await ask(` Choose [${defaultIdx + 1}]: `);
|
|
24133
|
+
if (answer === null) {
|
|
24134
|
+
console.log(` (no answer \u2014 using ${fallback})`);
|
|
24135
|
+
return fallback;
|
|
24136
|
+
}
|
|
24137
|
+
const parsed = parseChoice(answer, choices, fallback);
|
|
24138
|
+
if (!parsed.recognized) console.log(` Unrecognized answer "${answer}" \u2014 using ${fallback}.`);
|
|
24139
|
+
return parsed.id;
|
|
24140
|
+
}
|
|
24141
|
+
async function promptMultiSelect(question, choices, fallback) {
|
|
24142
|
+
if (!interactive()) return [...fallback];
|
|
24143
|
+
printOptions(question, choices);
|
|
24144
|
+
const defaultLabel = choices.map((c, i) => fallback.includes(c.id) ? String(i + 1) : null).filter(Boolean).join(",");
|
|
24145
|
+
const answer = await ask(` Choose one or more, comma-separated [${defaultLabel}]: `);
|
|
24146
|
+
if (answer === null) {
|
|
24147
|
+
console.log(" (no answer \u2014 using the default)");
|
|
24148
|
+
return [...fallback];
|
|
24149
|
+
}
|
|
24150
|
+
const parsed = parseMultiSelect(answer, choices, fallback);
|
|
24151
|
+
if (!parsed.recognized) console.log(` Unrecognized answer "${answer}" \u2014 using the default.`);
|
|
24152
|
+
return parsed.ids;
|
|
24153
|
+
}
|
|
24154
|
+
|
|
24155
|
+
// src/commands/init.ts
|
|
22674
24156
|
async function confirmExistingLogin(serviceUrl, remote, opts) {
|
|
22675
24157
|
const existing = await resolveToken(opts.token);
|
|
22676
24158
|
if (!existing.ok) return "drive-login";
|
|
@@ -22728,7 +24210,7 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
22728
24210
|
}
|
|
22729
24211
|
let remote = "";
|
|
22730
24212
|
try {
|
|
22731
|
-
remote = (0,
|
|
24213
|
+
remote = (0, import_node_child_process14.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
22732
24214
|
} catch {
|
|
22733
24215
|
}
|
|
22734
24216
|
if (!healed) {
|
|
@@ -22739,7 +24221,7 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
22739
24221
|
printInfo("Verity runs in local-only mode: the gate still runs and shows static findings, but nothing uploads.");
|
|
22740
24222
|
printInfo(' Authenticate anytime: run "verity login" (one login covers every repo you can write to).');
|
|
22741
24223
|
};
|
|
22742
|
-
if (
|
|
24224
|
+
if (interactive() && !opts.yes) {
|
|
22743
24225
|
console.log("");
|
|
22744
24226
|
console.log(" Signing in is optional. What it does:");
|
|
22745
24227
|
console.log(" - Confirms which repositories you can write to. The GitHub token is");
|
|
@@ -22754,9 +24236,12 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
22754
24236
|
console.log(" findings, but nothing is uploaded.");
|
|
22755
24237
|
console.log("");
|
|
22756
24238
|
}
|
|
22757
|
-
const wantsAuth =
|
|
24239
|
+
const wantsAuth = opts.yes ? false : await promptYes(
|
|
24240
|
+
"Authenticate with GitHub now to upload results to Verity? [Y/skip] ",
|
|
24241
|
+
{ nonInteractive: false }
|
|
24242
|
+
);
|
|
22758
24243
|
if (!wantsAuth) {
|
|
22759
|
-
printInfo("Skipped authentication.");
|
|
24244
|
+
printInfo(opts.yes ? "Skipped authentication (unattended run)." : "Skipped authentication.");
|
|
22760
24245
|
localOnlyNote();
|
|
22761
24246
|
return;
|
|
22762
24247
|
}
|
|
@@ -22779,7 +24264,7 @@ function resolveDataDir() {
|
|
|
22779
24264
|
// local dev: running from repo root
|
|
22780
24265
|
];
|
|
22781
24266
|
for (const candidate of candidates) {
|
|
22782
|
-
if ((0,
|
|
24267
|
+
if ((0, import_node_fs44.existsSync)((0, import_node_path29.join)(candidate, "skills"))) {
|
|
22783
24268
|
return candidate;
|
|
22784
24269
|
}
|
|
22785
24270
|
}
|
|
@@ -22788,22 +24273,197 @@ function resolveDataDir() {
|
|
|
22788
24273
|
);
|
|
22789
24274
|
}
|
|
22790
24275
|
async function copyDir(src, dest) {
|
|
22791
|
-
await (0,
|
|
22792
|
-
await (0,
|
|
24276
|
+
await (0, import_promises14.mkdir)(dest, { recursive: true });
|
|
24277
|
+
await (0, import_promises14.cp)(src, dest, { recursive: true, force: true });
|
|
24278
|
+
}
|
|
24279
|
+
async function skillIsCurrent(src, dest) {
|
|
24280
|
+
const list2 = (dir) => {
|
|
24281
|
+
const out = [];
|
|
24282
|
+
const walk = (d, prefix) => {
|
|
24283
|
+
for (const e of (0, import_node_fs44.readdirSync)(d, { withFileTypes: true })) {
|
|
24284
|
+
const rel = prefix ? `${prefix}/${e.name}` : e.name;
|
|
24285
|
+
if (e.isDirectory()) walk((0, import_node_path29.join)(d, e.name), rel);
|
|
24286
|
+
else if (e.isFile()) out.push(rel);
|
|
24287
|
+
}
|
|
24288
|
+
};
|
|
24289
|
+
walk(dir, "");
|
|
24290
|
+
return out.sort();
|
|
24291
|
+
};
|
|
24292
|
+
try {
|
|
24293
|
+
const shipped = list2(src);
|
|
24294
|
+
if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
|
|
24295
|
+
for (const rel of shipped) {
|
|
24296
|
+
const a = await (0, import_promises14.readFile)((0, import_node_path29.join)(src, rel), "utf-8");
|
|
24297
|
+
const b = await (0, import_promises14.readFile)((0, import_node_path29.join)(dest, rel), "utf-8");
|
|
24298
|
+
if (a !== b) return false;
|
|
24299
|
+
}
|
|
24300
|
+
return true;
|
|
24301
|
+
} catch {
|
|
24302
|
+
return false;
|
|
24303
|
+
}
|
|
24304
|
+
}
|
|
24305
|
+
var SKILLS = [
|
|
24306
|
+
"verity-setup",
|
|
24307
|
+
"verity-analyze",
|
|
24308
|
+
"verity-status",
|
|
24309
|
+
"verity-feedback",
|
|
24310
|
+
"verity-learn",
|
|
24311
|
+
"verity-memory",
|
|
24312
|
+
"verity-insights",
|
|
24313
|
+
"verity-reflect"
|
|
24314
|
+
];
|
|
24315
|
+
var INTENSITY_CHOICES = [
|
|
24316
|
+
{ id: "lightweight", label: "lightweight", hint: "critical security only, fastest (~3s)" },
|
|
24317
|
+
{ id: "balanced", label: "balanced", hint: "security + quality (~8s)", recommended: true },
|
|
24318
|
+
{ id: "thorough", label: "thorough", hint: "all tools, all rules (~15s)" }
|
|
24319
|
+
];
|
|
24320
|
+
var MOMENT_CHOICES = [
|
|
24321
|
+
{ id: "stop", label: "On stop", hint: "after every agent turn \u2014 fast feedback while you work", recommended: true },
|
|
24322
|
+
{ id: "pre-commit", label: "Before commit", hint: "reviews the staged diff, blocks the commit on FAIL" },
|
|
24323
|
+
{ id: "pre-push", label: "Before push / PR", hint: "reviews the to-be-pushed commits, blocks on FAIL" }
|
|
24324
|
+
];
|
|
24325
|
+
var DEFAULT_MOMENTS = ["stop"];
|
|
24326
|
+
async function askSetupQuestions(defaultsOnly, previous) {
|
|
24327
|
+
const intensityDefault = previous?.intensity ?? "balanced";
|
|
24328
|
+
const momentsDefault = previous?.moments?.length ? previous.moments : DEFAULT_MOMENTS;
|
|
24329
|
+
if (defaultsOnly) {
|
|
24330
|
+
return { intensity: intensityDefault, moments: momentsDefault, telemetry: "not-asked" };
|
|
24331
|
+
}
|
|
24332
|
+
if (previous?.intensity || previous?.moments) {
|
|
24333
|
+
printInfo(`Current: ${intensityDefault} \xB7 ${momentsDefault.join(", ")} \u2014 press Enter to keep either.`);
|
|
24334
|
+
}
|
|
24335
|
+
const intensity = await promptChoice(
|
|
24336
|
+
"Analysis intensity \u2014 how deeply should Verity review?",
|
|
24337
|
+
INTENSITY_CHOICES,
|
|
24338
|
+
intensityDefault
|
|
24339
|
+
);
|
|
24340
|
+
const moments = await promptMultiSelect(
|
|
24341
|
+
"When should Verity review your code?",
|
|
24342
|
+
MOMENT_CHOICES,
|
|
24343
|
+
momentsDefault
|
|
24344
|
+
);
|
|
24345
|
+
const current = await checkTelemetry();
|
|
24346
|
+
if (current.enabled) {
|
|
24347
|
+
printInfo(`Cost & usage telemetry: already enabled \u2192 ${current.endpoint}`);
|
|
24348
|
+
printInfo(' (turn it off with "verity telemetry uninstall")');
|
|
24349
|
+
return { intensity, moments, telemetry: "already-on" };
|
|
24350
|
+
}
|
|
24351
|
+
console.log("");
|
|
24352
|
+
console.log(" Cost & usage telemetry (opt-in) \u2014 powers the /usage dashboard.");
|
|
24353
|
+
console.log(" Sends Claude Code's own OpenTelemetry metrics and traces only: model names,");
|
|
24354
|
+
console.log(" token counts, USD cost, agent types, session ids. NOT your prompts, code, or");
|
|
24355
|
+
console.log(" tool input/output. Without it, /usage stays empty.");
|
|
24356
|
+
const wants = await promptYes(" Enable cost & usage telemetry? [Y/n] ", { nonInteractive: false });
|
|
24357
|
+
return { intensity, moments, telemetry: wants ? "yes" : "no" };
|
|
24358
|
+
}
|
|
24359
|
+
function insideClaudeCode() {
|
|
24360
|
+
return !!process.env.CLAUDECODE || !!process.env.CLAUDE_SESSION_ID;
|
|
24361
|
+
}
|
|
24362
|
+
function resumeDeferredPending(previous) {
|
|
24363
|
+
return previous?.telemetry === "deferred";
|
|
24364
|
+
}
|
|
24365
|
+
var PHASE_TWO_ARTIFACTS = [
|
|
24366
|
+
{
|
|
24367
|
+
path: ".verity/standard.yaml",
|
|
24368
|
+
what: "the Standard, synthesized from your codebase",
|
|
24369
|
+
// The presence flag travels WITH the row. Read positionally from a parallel
|
|
24370
|
+
// array, a reorder of this list would silently move every ✓ onto the wrong
|
|
24371
|
+
// path — a report that is confidently wrong about what got created.
|
|
24372
|
+
present: (a) => a.standard
|
|
24373
|
+
},
|
|
24374
|
+
{
|
|
24375
|
+
path: ".codacy/codacy.config.json",
|
|
24376
|
+
what: "curated static-analysis patterns",
|
|
24377
|
+
present: (a) => a.analysisConfig
|
|
24378
|
+
},
|
|
24379
|
+
{
|
|
24380
|
+
path: "VERITY.md",
|
|
24381
|
+
what: "project quality overview",
|
|
24382
|
+
present: (a) => a.verityMd
|
|
24383
|
+
}
|
|
24384
|
+
];
|
|
24385
|
+
async function reportPhaseTwo(startedAt) {
|
|
24386
|
+
const elapsed = Math.round((Date.now() - startedAt) / 1e3);
|
|
24387
|
+
let report = null;
|
|
24388
|
+
try {
|
|
24389
|
+
report = await buildReport();
|
|
24390
|
+
} catch {
|
|
24391
|
+
}
|
|
24392
|
+
console.log("");
|
|
24393
|
+
if (!report) {
|
|
24394
|
+
printWarn('Could not verify what the setup session produced \u2014 run "verity doctor".');
|
|
24395
|
+
return;
|
|
24396
|
+
}
|
|
24397
|
+
const { artifacts } = report;
|
|
24398
|
+
const complete = PHASE_TWO_ARTIFACTS.every((a) => a.present(artifacts));
|
|
24399
|
+
printInfo(complete ? `Setup complete (${elapsed}s).` : `Setup session ended after ${elapsed}s.`);
|
|
24400
|
+
for (const { path, what, present } of PHASE_TWO_ARTIFACTS) {
|
|
24401
|
+
const ok = present(artifacts);
|
|
24402
|
+
console.log(` ${ok ? "\u2713" : "\xB7"} ${path.padEnd(30)} ${ok ? what : `${what} \u2014 NOT created`}`);
|
|
24403
|
+
}
|
|
24404
|
+
if (!complete) {
|
|
24405
|
+
console.log("");
|
|
24406
|
+
printWarn('Setup did not finish. Re-run "/verity-setup" in Claude Code to complete it \u2014');
|
|
24407
|
+
printWarn(" nothing is lost; it picks up from what is already on disk.");
|
|
24408
|
+
}
|
|
24409
|
+
}
|
|
24410
|
+
async function handoffToSetup(enabled, claudeInstalled) {
|
|
24411
|
+
const instruct = (why) => {
|
|
24412
|
+
console.log("");
|
|
24413
|
+
printInfo("Still to do \u2014 this is what /verity-setup does (it needs a model):");
|
|
24414
|
+
for (const { path, what } of PHASE_TWO_ARTIFACTS) {
|
|
24415
|
+
console.log(` ${path.padEnd(30)} ${what}`);
|
|
24416
|
+
}
|
|
24417
|
+
console.log("");
|
|
24418
|
+
printInfo("Next step: run /verity-setup in Claude Code.");
|
|
24419
|
+
printInfo(` (${why})`);
|
|
24420
|
+
};
|
|
24421
|
+
if (!enabled) return instruct("--no-setup was passed");
|
|
24422
|
+
if (insideClaudeCode()) return instruct("you are already in a Claude Code session \u2014 invoke the skill there");
|
|
24423
|
+
if (!interactive()) return instruct("no interactive terminal here");
|
|
24424
|
+
if (!claudeInstalled) return instruct("Claude Code is not installed on this machine yet");
|
|
24425
|
+
printPhase(2, 2, "your Standard", "reading the codebase \xB7 synthesizing the Standard \xB7 curating patterns");
|
|
24426
|
+
console.log(" Claude Code takes over the screen from here. It will:");
|
|
24427
|
+
console.log(" \xB7 read your codebase \u2014 languages, frameworks, architecture");
|
|
24428
|
+
console.log(" \xB7 synthesize .verity/standard.yaml and show it to you");
|
|
24429
|
+
console.log(" \xB7 write the analysis config, then VERITY.md");
|
|
24430
|
+
console.log(" Usually a minute or two. Quit any time \u2014 re-running /verity-setup resumes.");
|
|
24431
|
+
console.log("");
|
|
24432
|
+
const startedAt = Date.now();
|
|
24433
|
+
const run2 = (0, import_node_child_process14.spawnSync)("claude", ["/verity-setup"], { stdio: "inherit" });
|
|
24434
|
+
if (run2.error) {
|
|
24435
|
+
printWarn(`Could not start Claude Code: ${run2.error.message}`);
|
|
24436
|
+
return instruct("start it yourself and run the skill there");
|
|
24437
|
+
}
|
|
24438
|
+
await reportPhaseTwo(startedAt);
|
|
22793
24439
|
}
|
|
22794
24440
|
function registerInitCommand(program2) {
|
|
22795
|
-
program2.command("init").description("
|
|
24441
|
+
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
24442
|
const force = opts.force ?? false;
|
|
24443
|
+
const wantsHandoff = opts.setup !== false;
|
|
24444
|
+
const defaultsOnly = (opts.yes ?? false) || !interactive();
|
|
22797
24445
|
const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
|
|
22798
|
-
const isProject = projectMarkers.some((m) => (0,
|
|
24446
|
+
const isProject = projectMarkers.some((m) => (0, import_node_fs44.existsSync)(m));
|
|
22799
24447
|
if (!isProject) {
|
|
22800
24448
|
printError("No project detected in the current directory.");
|
|
22801
24449
|
printInfo('Run "verity init" from your project root.');
|
|
22802
24450
|
process.exit(1);
|
|
22803
24451
|
}
|
|
22804
|
-
|
|
22805
|
-
|
|
22806
|
-
|
|
24452
|
+
const showArt = canRenderArt() && !insideClaudeCode();
|
|
24453
|
+
printBanner({ interactive: showArt });
|
|
24454
|
+
if (!showArt) {
|
|
24455
|
+
console.log("");
|
|
24456
|
+
printInfo("Initializing Verity in this project...");
|
|
24457
|
+
console.log("");
|
|
24458
|
+
} else {
|
|
24459
|
+
printPhase(1, 2, "this machine", "prerequisites \xB7 skills \xB7 hooks \xB7 sign-in");
|
|
24460
|
+
}
|
|
24461
|
+
const TOTAL_STEPS = 8;
|
|
24462
|
+
let stepNo = 0;
|
|
24463
|
+
const step = (label2) => {
|
|
24464
|
+
stepNo++;
|
|
24465
|
+
printInfo(`${DIM}[${stepNo}/${TOTAL_STEPS}]${NC} ${label2}`);
|
|
24466
|
+
};
|
|
22807
24467
|
if (await needsMigration()) {
|
|
22808
24468
|
printInfo("Legacy GATE.md install detected \u2014 migrating to Verity...");
|
|
22809
24469
|
try {
|
|
@@ -22814,143 +24474,171 @@ function registerInitCommand(program2) {
|
|
|
22814
24474
|
}
|
|
22815
24475
|
console.log("");
|
|
22816
24476
|
}
|
|
22817
|
-
|
|
22818
|
-
const
|
|
22819
|
-
const
|
|
22820
|
-
|
|
22821
|
-
|
|
22822
|
-
|
|
24477
|
+
step("Checking prerequisites");
|
|
24478
|
+
const prereqs = await checkPrereqs({ install: true });
|
|
24479
|
+
for (const c of prereqs.checks) {
|
|
24480
|
+
if (c.status === "ok") {
|
|
24481
|
+
if (c.justInstalled) continue;
|
|
24482
|
+
printInfo(` ${c.label} ${c.detail} \u2713`);
|
|
24483
|
+
} else {
|
|
24484
|
+
printWarn(` ${c.label}: ${c.detail}`);
|
|
24485
|
+
if (c.remedy) printWarn(` ${c.remedy}`);
|
|
24486
|
+
}
|
|
22823
24487
|
}
|
|
22824
|
-
|
|
22825
|
-
|
|
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");
|
|
24488
|
+
if (prereqs.blocked) {
|
|
24489
|
+
printError("A required prerequisite is missing \u2014 cannot continue.");
|
|
22830
24490
|
process.exit(1);
|
|
22831
24491
|
}
|
|
22832
|
-
|
|
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
|
-
}
|
|
24492
|
+
const claudeInstalled = prereqs.checks.some((c) => c.id === "claude" && c.status === "ok");
|
|
22858
24493
|
console.log("");
|
|
22859
|
-
|
|
24494
|
+
step("Installing skills");
|
|
22860
24495
|
const dataDir = resolveDataDir();
|
|
22861
24496
|
const skillsSource = (0, import_node_path29.join)(dataDir, "skills");
|
|
22862
24497
|
const skillsDest = ".claude/skills";
|
|
22863
|
-
const skills = ["verity-setup", "verity-analyze", "verity-status", "verity-feedback", "verity-learn", "verity-memory", "verity-insights", "verity-reflect"];
|
|
22864
24498
|
let skillsInstalled = 0;
|
|
22865
|
-
for (const skill of
|
|
24499
|
+
for (const skill of SKILLS) {
|
|
22866
24500
|
const src = (0, import_node_path29.join)(skillsSource, skill);
|
|
22867
24501
|
const dest = (0, import_node_path29.join)(skillsDest, skill);
|
|
22868
|
-
if (!(0,
|
|
24502
|
+
if (!(0, import_node_fs44.existsSync)(src)) {
|
|
22869
24503
|
printWarn(` Skill data not found: ${skill}`);
|
|
22870
24504
|
continue;
|
|
22871
24505
|
}
|
|
22872
|
-
if ((0,
|
|
22873
|
-
|
|
22874
|
-
|
|
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
|
-
}
|
|
24506
|
+
if ((0, import_node_fs44.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) {
|
|
24507
|
+
skillsInstalled++;
|
|
24508
|
+
continue;
|
|
22886
24509
|
}
|
|
22887
24510
|
await copyDir(src, dest);
|
|
22888
24511
|
skillsInstalled++;
|
|
22889
24512
|
}
|
|
22890
|
-
printInfo(` ${skillsInstalled}/${
|
|
22891
|
-
|
|
22892
|
-
const
|
|
22893
|
-
const
|
|
22894
|
-
const
|
|
22895
|
-
if (
|
|
22896
|
-
|
|
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.');
|
|
24513
|
+
printInfo(` ${skillsInstalled}/${SKILLS.length} skills installed to .claude/skills/ \u2713`);
|
|
24514
|
+
step(defaultsOnly ? "Setup answers (defaults)" : "Your setup answers");
|
|
24515
|
+
const previous = await readSetupState();
|
|
24516
|
+
const answers = await askSetupQuestions(defaultsOnly, previous);
|
|
24517
|
+
const { intensity, moments } = answers;
|
|
24518
|
+
if (defaultsOnly) {
|
|
24519
|
+
printInfo(` intensity: ${intensity} \xB7 moments: ${moments.join(", ") || "none"} (no questions asked)`);
|
|
22903
24520
|
}
|
|
22904
|
-
|
|
24521
|
+
step("Knowledge base, .gitignore and CLAUDE.md");
|
|
24522
|
+
await (0, import_promises14.mkdir)(VERITY_DIR, { recursive: true });
|
|
22905
24523
|
await ensureMemoryDir();
|
|
22906
|
-
const ignoreResult =
|
|
24524
|
+
const ignoreResult = ensureVerityGitignore();
|
|
22907
24525
|
if (ignoreResult === "failed") {
|
|
22908
|
-
printWarn(" .gitignore: could not
|
|
24526
|
+
printWarn(" .gitignore: could not write the Verity block \u2014 add it manually");
|
|
24527
|
+
printWarn(" (.verity/ holds copies of analyzed files, including any secret the gate flagged)");
|
|
24528
|
+
} else if (ignoreResult === "conflict") {
|
|
24529
|
+
printWarn(" .gitignore: the Verity block is in place but git still ignores .verity/standard.yaml");
|
|
24530
|
+
printWarn(" Something outside this file covers it \u2014 a global (~/.gitignore) or nested");
|
|
24531
|
+
printWarn(" .gitignore, or a pattern we do not recognise. Check: git check-ignore -v .verity/standard.yaml");
|
|
24532
|
+
printWarn(" Until it is fixed, the Standard and the knowledge graph cannot be committed.");
|
|
24533
|
+
} else if (ignoreResult === "repaired") {
|
|
24534
|
+
printInfo(" .gitignore: rewrote `.verity/` to `.verity/*` so the standard stays committable \u2713");
|
|
22909
24535
|
} else {
|
|
22910
|
-
printInfo(` .gitignore:
|
|
24536
|
+
printInfo(` .gitignore: Verity block ${ignoreResult === "added" ? "added" : "already covered"} \u2713`);
|
|
24537
|
+
}
|
|
24538
|
+
const tracked = committedVerityState();
|
|
24539
|
+
if (tracked.length > 0) {
|
|
24540
|
+
printWarn(` ${tracked.length} Verity state file(s) are tracked in git (e.g. ${tracked[0]}).`);
|
|
24541
|
+
const untrack = defaultsOnly ? false : await promptYes(" Untrack them now (files stay on disk)? [Y/n] ", { nonInteractive: false });
|
|
24542
|
+
if (untrack) {
|
|
24543
|
+
const result = untrackVerityState();
|
|
24544
|
+
if (result === "untracked") printInfo(" Untracked (staged) \u2014 commit to finish \u2713");
|
|
24545
|
+
else if (result === "failed") printWarn(' Could not untrack \u2014 run "git rm -r --cached .verity" manually');
|
|
24546
|
+
} else {
|
|
24547
|
+
printWarn(" Left tracked. Fix with: git rm -r --cached .verity && git add .verity/standard.yaml .verity/memory");
|
|
24548
|
+
}
|
|
22911
24549
|
}
|
|
22912
24550
|
try {
|
|
22913
24551
|
await ensureClaudeMdPointer();
|
|
22914
|
-
printInfo(" CLAUDE.md
|
|
24552
|
+
printInfo(" CLAUDE.md instructions \u2713");
|
|
22915
24553
|
} catch (err) {
|
|
22916
24554
|
printWarn(` Could not update CLAUDE.md: ${err.message}`);
|
|
22917
24555
|
}
|
|
22918
24556
|
const globalVerityDir = (0, import_node_path29.join)(process.env.HOME ?? "", ".verity");
|
|
22919
|
-
await (0,
|
|
24557
|
+
await (0, import_promises14.mkdir)(globalVerityDir, { recursive: true });
|
|
22920
24558
|
console.log("");
|
|
24559
|
+
step("Wiring Claude Code hooks");
|
|
24560
|
+
await applyMomentSelection(moments);
|
|
24561
|
+
const hookStatus = await checkAllVerityHooks();
|
|
24562
|
+
printInfo(` Stop (verity analyze): ${hookStatus.stop ? "on" : "off"}`);
|
|
24563
|
+
printInfo(` Pre-commit gate: ${hookStatus.guardOn.includes("commit") ? "on" : "off"}`);
|
|
24564
|
+
printInfo(` Pre-push/PR gate: ${hookStatus.guardOn.includes("push") ? "on" : "off"}`);
|
|
24565
|
+
printInfo(` Intent + baseline + compact + session-end: always on \u2713`);
|
|
24566
|
+
if (!hookStatus.stop && hookStatus.guardOn.length === 0) {
|
|
24567
|
+
printWarn(" No analysis moment is active \u2014 code changes will NOT be reviewed.");
|
|
24568
|
+
printWarn(" Enable one: verity hooks install --moments stop");
|
|
24569
|
+
}
|
|
24570
|
+
console.log("");
|
|
24571
|
+
step("Sign in to Verity (optional)");
|
|
22921
24572
|
try {
|
|
22922
24573
|
const globals = program2.opts();
|
|
22923
24574
|
const resolution = await resolveServiceUrlForAuth(globals.serviceUrl);
|
|
22924
24575
|
await runOptionalAuth(resolution, {
|
|
22925
24576
|
token: globals.token,
|
|
22926
|
-
verbose: globals.verbose
|
|
24577
|
+
verbose: globals.verbose,
|
|
24578
|
+
yes: defaultsOnly
|
|
22927
24579
|
});
|
|
22928
24580
|
} catch (err) {
|
|
22929
24581
|
printWarn(`Authentication step skipped: ${err.message}`);
|
|
22930
24582
|
}
|
|
24583
|
+
const resumeDeferred = answers.telemetry === "not-asked" && resumeDeferredPending(previous);
|
|
24584
|
+
const wantsTelemetry = answers.telemetry === "yes" || resumeDeferred;
|
|
24585
|
+
step("Cost & usage telemetry");
|
|
24586
|
+
if (answers.telemetry === "not-asked" && !resumeDeferredPending(previous)) {
|
|
24587
|
+
printInfo(' not asked (unattended run) \u2014 enable later with "verity telemetry install"');
|
|
24588
|
+
} else if (answers.telemetry === "no") {
|
|
24589
|
+
printInfo(' declined \u2014 enable later with "verity telemetry install"');
|
|
24590
|
+
}
|
|
24591
|
+
let telemetryChoice = answers.telemetry === "already-on" ? "enabled" : answers.telemetry === "no" ? "declined" : wantsTelemetry ? "deferred" : void 0;
|
|
24592
|
+
if (wantsTelemetry) {
|
|
24593
|
+
const globals = program2.opts();
|
|
24594
|
+
const token = await resolveToken(globals.token);
|
|
24595
|
+
const url = await resolveServiceUrl(globals.serviceUrl);
|
|
24596
|
+
if (token.ok && url.ok) {
|
|
24597
|
+
const installed2 = await installTelemetry(url.data);
|
|
24598
|
+
if (installed2.ok) {
|
|
24599
|
+
telemetryChoice = "enabled";
|
|
24600
|
+
printInfo(`Telemetry enabled \u2192 ${installed2.data.endpoint}`);
|
|
24601
|
+
printInfo(" takes effect on your NEXT Claude Code session; view cost & usage at /usage");
|
|
24602
|
+
} else {
|
|
24603
|
+
printWarn(`Could not enable telemetry: ${installed2.error}`);
|
|
24604
|
+
}
|
|
24605
|
+
} else {
|
|
24606
|
+
printWarn('Telemetry needs a Verity token \u2014 run "verity login", then "verity telemetry install".');
|
|
24607
|
+
}
|
|
24608
|
+
}
|
|
24609
|
+
step("Recording your answers");
|
|
24610
|
+
try {
|
|
24611
|
+
await writeSetupState({
|
|
24612
|
+
intensity,
|
|
24613
|
+
moments,
|
|
24614
|
+
...telemetryChoice ? { telemetry: telemetryChoice } : {},
|
|
24615
|
+
init: {
|
|
24616
|
+
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24617
|
+
cli_version: true ? "0.31.1-experimental.83a8619" : "dev"
|
|
24618
|
+
}
|
|
24619
|
+
});
|
|
24620
|
+
} catch (err) {
|
|
24621
|
+
printWarn(`Could not record setup answers: ${err.message}`);
|
|
24622
|
+
}
|
|
22931
24623
|
console.log("");
|
|
22932
|
-
printInfo("
|
|
24624
|
+
printInfo("This machine is set up.");
|
|
22933
24625
|
console.log("");
|
|
22934
|
-
console.log("
|
|
22935
|
-
console.log("
|
|
22936
|
-
console.log(" .claude/
|
|
22937
|
-
console.log(" .
|
|
22938
|
-
console.log(" .
|
|
22939
|
-
console.log(" .
|
|
22940
|
-
console.log(" .
|
|
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)");
|
|
24626
|
+
console.log(" .claude/skills/verity-*/ 8 skills (setup, analyze, status, feedback,");
|
|
24627
|
+
console.log(" learn, memory, insights, reflect)");
|
|
24628
|
+
console.log(" .claude/settings.json hooks, reconciled to your chosen moments");
|
|
24629
|
+
console.log(" .verity/memory/ knowledge base (commit to git)");
|
|
24630
|
+
console.log(" .verity/setup.json your answers, read by /verity-setup");
|
|
24631
|
+
console.log(" .gitignore Verity block (whitelist form)");
|
|
24632
|
+
console.log(" CLAUDE.md memory pointer, waive policy, reflection");
|
|
22945
24633
|
console.log("");
|
|
22946
|
-
console.log(
|
|
22947
|
-
|
|
24634
|
+
console.log(` Intensity: ${intensity} Moments: ${moments.join(", ") || "none"}`);
|
|
24635
|
+
await handoffToSetup(wantsHandoff, claudeInstalled);
|
|
22948
24636
|
console.log("");
|
|
22949
24637
|
});
|
|
22950
24638
|
}
|
|
22951
24639
|
|
|
22952
24640
|
// src/commands/uninstall.ts
|
|
22953
|
-
var
|
|
24641
|
+
var import_node_fs45 = require("node:fs");
|
|
22954
24642
|
var import_node_path30 = require("node:path");
|
|
22955
24643
|
var SKILL_NAMES = [
|
|
22956
24644
|
"verity-setup",
|
|
@@ -22971,10 +24659,10 @@ function registerUninstallCommand(program2) {
|
|
|
22971
24659
|
const skillsRoot = projectPath(".claude/skills");
|
|
22972
24660
|
for (const name of SKILL_NAMES) {
|
|
22973
24661
|
const dir = (0, import_node_path30.join)(skillsRoot, name);
|
|
22974
|
-
if ((0,
|
|
24662
|
+
if ((0, import_node_fs45.existsSync)(dir)) {
|
|
22975
24663
|
actions.push({
|
|
22976
24664
|
label: `Remove .claude/skills/${name}/`,
|
|
22977
|
-
apply: () => (0,
|
|
24665
|
+
apply: () => (0, import_node_fs45.rmSync)(dir, { recursive: true, force: true })
|
|
22978
24666
|
});
|
|
22979
24667
|
}
|
|
22980
24668
|
}
|
|
@@ -22988,24 +24676,24 @@ function registerUninstallCommand(program2) {
|
|
|
22988
24676
|
});
|
|
22989
24677
|
}
|
|
22990
24678
|
const verityDir = projectPath(VERITY_DIR);
|
|
22991
|
-
if ((0,
|
|
24679
|
+
if ((0, import_node_fs45.existsSync)(verityDir)) {
|
|
22992
24680
|
actions.push({
|
|
22993
24681
|
label: `Remove ${VERITY_DIR}/`,
|
|
22994
|
-
apply: () => (0,
|
|
24682
|
+
apply: () => (0, import_node_fs45.rmSync)(verityDir, { recursive: true, force: true })
|
|
22995
24683
|
});
|
|
22996
24684
|
}
|
|
22997
24685
|
if (!keepVerityMd) {
|
|
22998
24686
|
const verityMd = projectPath(VERITY_MD_FILE);
|
|
22999
|
-
if ((0,
|
|
24687
|
+
if ((0, import_node_fs45.existsSync)(verityMd)) {
|
|
23000
24688
|
actions.push({
|
|
23001
24689
|
label: `Remove ${VERITY_MD_FILE}`,
|
|
23002
|
-
apply: () => (0,
|
|
24690
|
+
apply: () => (0, import_node_fs45.rmSync)(verityMd, { force: true })
|
|
23003
24691
|
});
|
|
23004
24692
|
}
|
|
23005
24693
|
}
|
|
23006
24694
|
const cleanupEmptyDir = (path) => {
|
|
23007
|
-
if ((0,
|
|
23008
|
-
(0,
|
|
24695
|
+
if ((0, import_node_fs45.existsSync)(path) && (0, import_node_fs45.statSync)(path).isDirectory() && (0, import_node_fs45.readdirSync)(path).length === 0) {
|
|
24696
|
+
(0, import_node_fs45.rmdirSync)(path);
|
|
23009
24697
|
}
|
|
23010
24698
|
};
|
|
23011
24699
|
actions.push({
|
|
@@ -23017,10 +24705,10 @@ function registerUninstallCommand(program2) {
|
|
|
23017
24705
|
});
|
|
23018
24706
|
const home = process.env.HOME ?? "";
|
|
23019
24707
|
const globalVerityDir = (0, import_node_path30.join)(home, ".verity");
|
|
23020
|
-
if (purgeGlobal && (0,
|
|
24708
|
+
if (purgeGlobal && (0, import_node_fs45.existsSync)(globalVerityDir)) {
|
|
23021
24709
|
actions.push({
|
|
23022
24710
|
label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
|
|
23023
|
-
apply: () => (0,
|
|
24711
|
+
apply: () => (0, import_node_fs45.rmSync)(globalVerityDir, { recursive: true, force: true })
|
|
23024
24712
|
});
|
|
23025
24713
|
}
|
|
23026
24714
|
if (actions.length === 0) {
|
|
@@ -23040,7 +24728,7 @@ function registerUninstallCommand(program2) {
|
|
|
23040
24728
|
if (!purgeGlobal) {
|
|
23041
24729
|
printInfo('Saved tokens at ~/.verity/credentials are preserved \u2014 re-run "verity init" to reconnect.');
|
|
23042
24730
|
} else {
|
|
23043
|
-
printWarn(
|
|
24731
|
+
printWarn('Global credentials wiped \u2014 run "verity login" (or "verity init") to reconnect.');
|
|
23044
24732
|
}
|
|
23045
24733
|
});
|
|
23046
24734
|
}
|
|
@@ -23214,7 +24902,7 @@ function registerTaskCommands(program2) {
|
|
|
23214
24902
|
}
|
|
23215
24903
|
|
|
23216
24904
|
// src/commands/reset.ts
|
|
23217
|
-
var
|
|
24905
|
+
var import_node_fs46 = require("node:fs");
|
|
23218
24906
|
var import_node_path31 = require("node:path");
|
|
23219
24907
|
function registerResetCommand(program2) {
|
|
23220
24908
|
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 +24940,11 @@ function registerResetCommand(program2) {
|
|
|
23252
24940
|
}
|
|
23253
24941
|
const cacheDir = projectPath(CACHE_DIR);
|
|
23254
24942
|
let purged = 0;
|
|
23255
|
-
if ((0,
|
|
23256
|
-
for (const entry of (0,
|
|
24943
|
+
if ((0, import_node_fs46.existsSync)(cacheDir)) {
|
|
24944
|
+
for (const entry of (0, import_node_fs46.readdirSync)(cacheDir)) {
|
|
23257
24945
|
if (entry.startsWith("pending-")) {
|
|
23258
24946
|
try {
|
|
23259
|
-
(0,
|
|
24947
|
+
(0, import_node_fs46.unlinkSync)((0, import_node_path31.join)(cacheDir, entry));
|
|
23260
24948
|
purged++;
|
|
23261
24949
|
} catch {
|
|
23262
24950
|
}
|
|
@@ -23271,19 +24959,19 @@ function registerResetCommand(program2) {
|
|
|
23271
24959
|
projectPath(`${VERITY_DIR}/.last-analysis`)
|
|
23272
24960
|
];
|
|
23273
24961
|
for (const file of filesToClear) {
|
|
23274
|
-
if ((0,
|
|
24962
|
+
if ((0, import_node_fs46.existsSync)(file)) {
|
|
23275
24963
|
try {
|
|
23276
|
-
(0,
|
|
24964
|
+
(0, import_node_fs46.writeFileSync)(file, "");
|
|
23277
24965
|
} catch {
|
|
23278
24966
|
}
|
|
23279
24967
|
}
|
|
23280
24968
|
}
|
|
23281
24969
|
if (opts.all) {
|
|
23282
24970
|
const logsDir = projectPath(`${VERITY_DIR}/.logs`);
|
|
23283
|
-
if ((0,
|
|
23284
|
-
for (const entry of (0,
|
|
24971
|
+
if ((0, import_node_fs46.existsSync)(logsDir)) {
|
|
24972
|
+
for (const entry of (0, import_node_fs46.readdirSync)(logsDir)) {
|
|
23285
24973
|
try {
|
|
23286
|
-
(0,
|
|
24974
|
+
(0, import_node_fs46.unlinkSync)((0, import_node_path31.join)(logsDir, entry));
|
|
23287
24975
|
} catch {
|
|
23288
24976
|
}
|
|
23289
24977
|
}
|
|
@@ -23591,8 +25279,8 @@ function registerTelemetryCommands(program2) {
|
|
|
23591
25279
|
}
|
|
23592
25280
|
|
|
23593
25281
|
// src/cli.ts
|
|
23594
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.31.1-experimental.
|
|
23595
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.1-experimental.
|
|
25282
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.31.1-experimental.83a8619").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) => {
|
|
25283
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.1-experimental.83a8619");
|
|
23596
25284
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
23597
25285
|
try {
|
|
23598
25286
|
await foldLegacyLocalCredential();
|
|
@@ -23618,6 +25306,7 @@ registerGuardCommand(program);
|
|
|
23618
25306
|
registerIgnoreCommand(program);
|
|
23619
25307
|
registerWaiveCommand(program);
|
|
23620
25308
|
registerInitCommand(program);
|
|
25309
|
+
registerDoctorCommand(program);
|
|
23621
25310
|
registerUninstallCommand(program);
|
|
23622
25311
|
registerTaskCommands(program);
|
|
23623
25312
|
registerResetCommand(program);
|