@codacy/verity-cli 0.31.0 → 0.31.1-experimental.68fca47
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 +244 -0
- package/README.md +28 -3
- package/bin/verity.js +2136 -451
- 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;
|
|
@@ -10506,7 +10507,7 @@ var SECURITY_PATTERNS = [
|
|
|
10506
10507
|
/Dockerfile/
|
|
10507
10508
|
];
|
|
10508
10509
|
var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
|
|
10509
|
-
var DEFAULT_SERVICE_URL = "".length > 0 ? "" : PROD_SERVICE_URL;
|
|
10510
|
+
var DEFAULT_SERVICE_URL = "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1".length > 0 ? "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1" : PROD_SERVICE_URL;
|
|
10510
10511
|
var GITHUB_CLIENT_ID = "Iv23li88HxAi3ZrbYzWh";
|
|
10511
10512
|
var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
10512
10513
|
var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
@@ -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");
|
|
@@ -14419,6 +14607,7 @@ function reduce(state, events, now) {
|
|
|
14419
14607
|
existing.line_sha = ev.line_sha ?? existing.line_sha;
|
|
14420
14608
|
existing.register = ev.register;
|
|
14421
14609
|
if (existing.outcome === "fixed") existing.outcome = "recurred";
|
|
14610
|
+
else if (existing.outcome !== "open") existing.outcome = "open";
|
|
14422
14611
|
break;
|
|
14423
14612
|
}
|
|
14424
14613
|
byAnchor.set(ev.anchor_key, {
|
|
@@ -15349,10 +15538,10 @@ function recordVerdict(d, v) {
|
|
|
15349
15538
|
const at = src?.[f.line - 1];
|
|
15350
15539
|
appendEvent(d, {
|
|
15351
15540
|
k: "statement",
|
|
15352
|
-
//
|
|
15353
|
-
//
|
|
15354
|
-
//
|
|
15355
|
-
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),
|
|
15356
15545
|
file: f.file,
|
|
15357
15546
|
line: f.line,
|
|
15358
15547
|
pattern_id: f.pattern_id,
|
|
@@ -15789,33 +15978,6 @@ ${addedLines}`,
|
|
|
15789
15978
|
}
|
|
15790
15979
|
return { diffs, has_snapshots: true };
|
|
15791
15980
|
}
|
|
15792
|
-
function ensureSnapshotGitignored() {
|
|
15793
|
-
let content = "";
|
|
15794
|
-
try {
|
|
15795
|
-
content = (0, import_node_fs16.readFileSync)(".gitignore", "utf-8");
|
|
15796
|
-
} catch {
|
|
15797
|
-
}
|
|
15798
|
-
let ignored = null;
|
|
15799
|
-
try {
|
|
15800
|
-
(0, import_node_child_process6.execSync)("git check-ignore -q -- .verity/.snapshot/__probe__", { stdio: "pipe" });
|
|
15801
|
-
ignored = true;
|
|
15802
|
-
} catch (err) {
|
|
15803
|
-
ignored = err.status === 1 ? false : null;
|
|
15804
|
-
}
|
|
15805
|
-
if (ignored === true) return "covered";
|
|
15806
|
-
if (ignored === null) {
|
|
15807
|
-
const lines = content.split("\n").map((l) => l.trim());
|
|
15808
|
-
const covering = [".verity/.snapshot/", ".verity/.snapshot", ".verity/", ".verity", ".verity/*"];
|
|
15809
|
-
if (lines.some((l) => covering.includes(l))) return "covered";
|
|
15810
|
-
}
|
|
15811
|
-
try {
|
|
15812
|
-
const block = "# Verity \u2014 snapshots of analyzed files (machine state, never commit)\n.verity/.snapshot/\n";
|
|
15813
|
-
(0, import_node_fs16.writeFileSync)(".gitignore", content ? content + (content.endsWith("\n") ? "" : "\n") + "\n" + block : block);
|
|
15814
|
-
return "added";
|
|
15815
|
-
} catch {
|
|
15816
|
-
return "failed";
|
|
15817
|
-
}
|
|
15818
|
-
}
|
|
15819
15981
|
function saveSnapshots(files) {
|
|
15820
15982
|
const snapshotPaths = /* @__PURE__ */ new Set();
|
|
15821
15983
|
for (const file of files) {
|
|
@@ -16737,19 +16899,19 @@ function loc(f) {
|
|
|
16737
16899
|
if (!f.file) return "";
|
|
16738
16900
|
return f.line != null ? `${f.file}:${f.line}` : f.file;
|
|
16739
16901
|
}
|
|
16740
|
-
function formatRunDetail(
|
|
16902
|
+
function formatRunDetail(run2) {
|
|
16741
16903
|
const lines = [];
|
|
16742
|
-
const q =
|
|
16743
|
-
const s =
|
|
16904
|
+
const q = run2.assessment?.quality_score;
|
|
16905
|
+
const s = run2.assessment?.security_score;
|
|
16744
16906
|
const qStr = q != null ? `${q}` : "-";
|
|
16745
16907
|
const sStr = s != null ? `${s}` : "-";
|
|
16746
|
-
lines.push(`${
|
|
16908
|
+
lines.push(`${run2.run_id} ${run2.gate_decision} Q ${qStr}/10 S ${sStr}/10`);
|
|
16747
16909
|
const meta = [];
|
|
16748
|
-
if (
|
|
16749
|
-
if (
|
|
16750
|
-
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", " "));
|
|
16751
16913
|
if (meta.length > 0) lines.push(meta.join(" \xB7 "));
|
|
16752
|
-
const findings =
|
|
16914
|
+
const findings = run2.findings ?? [];
|
|
16753
16915
|
if (findings.length === 0) {
|
|
16754
16916
|
lines.push("");
|
|
16755
16917
|
lines.push("No findings \u2014 clean.");
|
|
@@ -16766,7 +16928,7 @@ function formatRunDetail(run) {
|
|
|
16766
16928
|
if (f.scope === "pre-existing") lines.push(" (pre-existing)");
|
|
16767
16929
|
}
|
|
16768
16930
|
}
|
|
16769
|
-
const pending =
|
|
16931
|
+
const pending = run2.pending_items ?? [];
|
|
16770
16932
|
if (pending.length > 0) {
|
|
16771
16933
|
lines.push("");
|
|
16772
16934
|
lines.push(`PENDING (${pending.length})`);
|
|
@@ -16774,9 +16936,9 @@ function formatRunDetail(run) {
|
|
|
16774
16936
|
lines.push(` [${(p.priority ?? "").toUpperCase()}] ${p.description}`);
|
|
16775
16937
|
}
|
|
16776
16938
|
}
|
|
16777
|
-
if (
|
|
16939
|
+
if (run2.assessment?.narrative) {
|
|
16778
16940
|
lines.push("");
|
|
16779
|
-
lines.push(
|
|
16941
|
+
lines.push(run2.assessment.narrative);
|
|
16780
16942
|
}
|
|
16781
16943
|
return lines;
|
|
16782
16944
|
}
|
|
@@ -17148,7 +17310,7 @@ function registerStatusCommand(program2) {
|
|
|
17148
17310
|
return;
|
|
17149
17311
|
}
|
|
17150
17312
|
if (mem?.configured === false) {
|
|
17151
|
-
printInfo(
|
|
17313
|
+
printInfo('Verity is not configured for this project. Run "verity init".');
|
|
17152
17314
|
return;
|
|
17153
17315
|
}
|
|
17154
17316
|
printInfo("=== Verity Status ===");
|
|
@@ -17180,7 +17342,7 @@ function registerStatusCommand(program2) {
|
|
|
17180
17342
|
if (hookStatus.stop) moments.push("stop");
|
|
17181
17343
|
if (hookStatus.guardOn.includes("commit")) moments.push("pre-commit");
|
|
17182
17344
|
if (hookStatus.guardOn.includes("push")) moments.push("pre-push/PR");
|
|
17183
|
-
printInfo(`Moments: ${moments.length > 0 ? moments.join(", ") :
|
|
17345
|
+
printInfo(`Moments: ${moments.length > 0 ? moments.join(", ") : 'none (run "verity init")'}`);
|
|
17184
17346
|
if (!mem) return;
|
|
17185
17347
|
if (mem.recent_runs) {
|
|
17186
17348
|
const r = mem.recent_runs;
|
|
@@ -17229,7 +17391,8 @@ function registerStatusCommand(program2) {
|
|
|
17229
17391
|
printInfo("");
|
|
17230
17392
|
printInfo("--- Pending Items ---");
|
|
17231
17393
|
for (const item of mem.pending_items) {
|
|
17232
|
-
|
|
17394
|
+
const priority = typeof item.priority === "string" && item.priority.length > 0 ? item.priority.toUpperCase() : "UNSPECIFIED";
|
|
17395
|
+
printInfo(` [${priority}] ${item.description ?? "(no description)"}`);
|
|
17233
17396
|
}
|
|
17234
17397
|
}
|
|
17235
17398
|
const recentTasks = mem.recent_tasks;
|
|
@@ -17265,12 +17428,12 @@ function registerStatusCommand(program2) {
|
|
|
17265
17428
|
printInfo("");
|
|
17266
17429
|
printInfo("--- Recent Runs ---");
|
|
17267
17430
|
printInfo(`${"Run ID".padEnd(32)} ${"Decision".padEnd(10)}${"Q".padEnd(4)}${"S".padEnd(4)}${"Findings".padEnd(32)}Date`);
|
|
17268
|
-
for (const
|
|
17269
|
-
const q =
|
|
17270
|
-
const s =
|
|
17271
|
-
const findings = formatFindingsSummary(
|
|
17272
|
-
const date =
|
|
17273
|
-
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}`);
|
|
17274
17437
|
}
|
|
17275
17438
|
}
|
|
17276
17439
|
}
|
|
@@ -17405,7 +17568,6 @@ function createRun(opts, globals) {
|
|
|
17405
17568
|
token: "",
|
|
17406
17569
|
modeDecision: null,
|
|
17407
17570
|
sessionIdForMemory: "",
|
|
17408
|
-
contextFilePaths: [],
|
|
17409
17571
|
analysisMode: "standard",
|
|
17410
17572
|
sessionAuthoredCode: false,
|
|
17411
17573
|
staticResults: {
|
|
@@ -17415,6 +17577,7 @@ function createRun(opts, globals) {
|
|
|
17415
17577
|
},
|
|
17416
17578
|
codeDelta: { files: [], total_lines: 0, total_files: 0, excluded: [] },
|
|
17417
17579
|
snapshotResult: { has_snapshots: false, diffs: [] },
|
|
17580
|
+
repoContext: null,
|
|
17418
17581
|
contentHash: null,
|
|
17419
17582
|
iteration: 1,
|
|
17420
17583
|
currentCommit: "",
|
|
@@ -17506,35 +17669,35 @@ function row(label2, value) {
|
|
|
17506
17669
|
return ` \u25B8 ${label2.padEnd(10)} ${value}
|
|
17507
17670
|
`;
|
|
17508
17671
|
}
|
|
17509
|
-
function formatRunEvidence(
|
|
17672
|
+
function formatRunEvidence(run2, startedAt) {
|
|
17510
17673
|
const ms = Date.now() - startedAt;
|
|
17511
|
-
const sent =
|
|
17512
|
-
const context =
|
|
17674
|
+
const sent = run2.codeDelta.files.filter((f) => f.role !== "context").map((f) => f.path);
|
|
17675
|
+
const context = run2.codeDelta.files.filter((f) => f.role === "context").map((f) => f.path);
|
|
17513
17676
|
let out = ` \u2500\u2500 what verity saw \u2500\u2500
|
|
17514
17677
|
`;
|
|
17515
|
-
out += row("turn", `${
|
|
17516
|
-
out += row("reached", `${
|
|
17517
|
-
if (
|
|
17518
|
-
const f =
|
|
17678
|
+
out += row("turn", `${run2.turnId || "(unminted)"}${run2.sessionId ? ` \xB7 session ${run2.sessionId}` : ""}`);
|
|
17679
|
+
out += row("reached", `${run2.phaseReached || "(none)"}${run2.skipReason ? ` \xB7 SKIPPED: ${run2.skipReason}` : ""} \xB7 ${ms}ms`);
|
|
17680
|
+
if (run2.treeFrame) {
|
|
17681
|
+
const f = run2.treeFrame;
|
|
17519
17682
|
out += row("tree", f.worktreeRoot ? `${f.worktreeRoot}${f.isLinkedWorktree ? " \xB7 linked worktree" : ""}${f.branch ? ` \xB7 branch ${f.branch}` : " \xB7 detached"}` : `(unresolved: ${f.refusal ?? "unknown"})`);
|
|
17520
17683
|
}
|
|
17521
|
-
out += row("changed", `${
|
|
17522
|
-
const done = (phase) =>
|
|
17684
|
+
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}`);
|
|
17685
|
+
const done = (phase) => run2.phasesCompleted.includes(phase);
|
|
17523
17686
|
const ifDone = (phase, value) => done(phase) ? value : "?";
|
|
17524
|
-
const md =
|
|
17687
|
+
const md = run2.modeDecision;
|
|
17525
17688
|
if (md) {
|
|
17526
17689
|
const how = md.forced ? "forced by --mode" : `predicted=${md.predicted ?? "none"} \u2192 ${md.resolved}`;
|
|
17527
17690
|
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"}`);
|
|
17528
17691
|
} else {
|
|
17529
|
-
out += row("mode", `? (this run stopped in ${
|
|
17692
|
+
out += row("mode", `? (this run stopped in ${run2.phaseReached || "no phase"}, before the mode was decided)`);
|
|
17530
17693
|
}
|
|
17531
|
-
out += row("signals", `baseline=${ifDone("bootstrap",
|
|
17694
|
+
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}` : ""));
|
|
17532
17695
|
if (!done("intentInputs")) {
|
|
17533
|
-
out += row("", `(\`?\` = the phase that determines it did not complete \u2014 this run stopped in ${
|
|
17696
|
+
out += row("", `(\`?\` = the phase that determines it did not complete \u2014 this run stopped in ${run2.phaseReached})`);
|
|
17534
17697
|
}
|
|
17535
17698
|
out += row("sent", `${sent.length} \xB7 ${list(sent)}`);
|
|
17536
17699
|
if (context.length > 0) out += row("context", `${context.length} \xB7 ${list(context)}`);
|
|
17537
|
-
const withheld =
|
|
17700
|
+
const withheld = run2.reviewCoverage.notReviewed;
|
|
17538
17701
|
if (withheld.length > 0) {
|
|
17539
17702
|
const byReason = /* @__PURE__ */ new Map();
|
|
17540
17703
|
for (const w of withheld) {
|
|
@@ -17547,18 +17710,18 @@ function formatRunEvidence(run, startedAt) {
|
|
|
17547
17710
|
first = false;
|
|
17548
17711
|
}
|
|
17549
17712
|
} else {
|
|
17550
|
-
const sentSet = new Set(
|
|
17551
|
-
const notSent =
|
|
17713
|
+
const sentSet = new Set(run2.codeDelta.files.map((f) => f.path));
|
|
17714
|
+
const notSent = run2.changedUniverse.filter((p) => !sentSet.has(p));
|
|
17552
17715
|
if (notSent.length > 0) {
|
|
17553
17716
|
out += row("not sent", `${list(notSent)}`);
|
|
17554
|
-
out += row("", `(stage unknown \u2014 the coverage ledger is built in phase 13, and this run reached ${
|
|
17555
|
-
} else if (
|
|
17717
|
+
out += row("", `(stage unknown \u2014 the coverage ledger is built in phase 13, and this run reached ${run2.phaseReached || "no phase"})`);
|
|
17718
|
+
} else if (run2.changedUniverse.length > 0) {
|
|
17556
17719
|
out += row("withheld", "(nothing \u2014 every changed file was reviewed)");
|
|
17557
17720
|
}
|
|
17558
17721
|
}
|
|
17559
|
-
const cov =
|
|
17722
|
+
const cov = run2.foldResult?.coverage;
|
|
17560
17723
|
if (cov) {
|
|
17561
|
-
const delegated =
|
|
17724
|
+
const delegated = run2.foldResult.authored.filter((a) => a.owner === "subagent").length;
|
|
17562
17725
|
if (cov.dispatched > 0 || cov.subagentFiles > 0 || cov.subagentSkipped > 0) {
|
|
17563
17726
|
out += row("delegated", `${cov.dispatched} dispatched \xB7 ${cov.subagentFiles} agent log(s) read \xB7 ${delegated} path(s) attributed to subagents`);
|
|
17564
17727
|
}
|
|
@@ -17572,54 +17735,740 @@ function formatRunEvidence(run, startedAt) {
|
|
|
17572
17735
|
out += row("", `${cov.outsideRepo} authored path(s) refused as outside the repo`);
|
|
17573
17736
|
}
|
|
17574
17737
|
}
|
|
17575
|
-
if (
|
|
17576
|
-
const shown =
|
|
17738
|
+
if (run2.foldResult?.tools?.length) {
|
|
17739
|
+
const shown = run2.foldResult.tools.slice(0, 6).map((t) => {
|
|
17577
17740
|
const outcome = t.failed > 0 ? `${t.failed} failed` : t.last_status === 0 ? "ok" : "?";
|
|
17578
17741
|
const where = t.targets.length > 0 ? ` \u2192 ${t.targets.slice(0, 2).join(", ")}` : "";
|
|
17579
17742
|
return `${t.runs}\xD7 ${t.name} (${outcome})${where}`;
|
|
17580
17743
|
});
|
|
17581
|
-
const more =
|
|
17744
|
+
const more = run2.foldResult.tools.length > 6 ? ` \u2026 +${run2.foldResult.tools.length - 6} more` : "";
|
|
17582
17745
|
out += row("tools", shown.join(" \xB7 ") + more);
|
|
17583
|
-
if (
|
|
17584
|
-
out += row("", `\u26A0 ${
|
|
17746
|
+
if (run2.foldResult.coverage.toolNamesDropped > 0) {
|
|
17747
|
+
out += row("", `\u26A0 ${run2.foldResult.coverage.toolNamesDropped} tool name(s) refused by the cap`);
|
|
17585
17748
|
}
|
|
17586
17749
|
}
|
|
17587
|
-
if (
|
|
17588
|
-
const t =
|
|
17750
|
+
if (run2.foldResult?.tasks?.length) {
|
|
17751
|
+
const t = run2.foldResult.tasks;
|
|
17589
17752
|
const done2 = t.filter((x) => x.status === "completed").length;
|
|
17590
17753
|
out += row("tasks", `${t.length} \xB7 ${done2} completed \xB7 ` + list(t.slice(0, 4).map((x) => `#${x.id} ${x.name} [${x.status}]`), 4));
|
|
17591
17754
|
}
|
|
17592
|
-
if (
|
|
17593
|
-
const readThisSession = new Set(
|
|
17594
|
-
const labelled =
|
|
17755
|
+
if (run2.specs?.length) {
|
|
17756
|
+
const readThisSession = new Set(run2.actionSummary?.files_read ?? []);
|
|
17757
|
+
const labelled = run2.specs.map(
|
|
17595
17758
|
(s) => `${s.path}${readThisSession.has(s.path) ? " (read)" : " (positional)"}`
|
|
17596
17759
|
);
|
|
17597
|
-
out += row("specs", `${
|
|
17760
|
+
out += row("specs", `${run2.specs.length} \xB7 ${list(labelled, 5)}`);
|
|
17598
17761
|
}
|
|
17599
|
-
if (
|
|
17600
|
-
out += row("static", `${
|
|
17762
|
+
if (run2.staticResults.findings.length > 0) {
|
|
17763
|
+
out += row("static", `${run2.staticResults.findings.length} finding(s) from ${run2.staticResults.summary.tools_run.join(", ") || "no tools"}`);
|
|
17601
17764
|
}
|
|
17602
|
-
if (
|
|
17603
|
-
out += row("verdict",
|
|
17765
|
+
if (run2.decision && run2.decision !== "(unrecognised)") {
|
|
17766
|
+
out += row("verdict", run2.decision + (run2.silenced ? ` \xB7 agent channel silenced (${run2.silenced})` : ""));
|
|
17604
17767
|
}
|
|
17605
17768
|
return out;
|
|
17606
17769
|
}
|
|
17607
|
-
function installRunEvidence(
|
|
17770
|
+
function installRunEvidence(run2) {
|
|
17608
17771
|
const startedAt = Date.now();
|
|
17609
17772
|
process.on("exit", () => {
|
|
17610
17773
|
try {
|
|
17611
|
-
logToFileOnly(formatRunEvidence(
|
|
17774
|
+
logToFileOnly(formatRunEvidence(run2, startedAt));
|
|
17612
17775
|
} catch {
|
|
17613
17776
|
}
|
|
17614
17777
|
});
|
|
17615
17778
|
}
|
|
17616
17779
|
|
|
17617
17780
|
// src/lib/git-frame.ts
|
|
17618
|
-
var
|
|
17781
|
+
var import_node_child_process8 = require("node:child_process");
|
|
17619
17782
|
var import_node_fs24 = require("node:fs");
|
|
17620
|
-
var
|
|
17783
|
+
var import_node_os4 = require("node:os");
|
|
17621
17784
|
var import_node_path18 = require("node:path");
|
|
17622
17785
|
var import_node_path19 = require("node:path");
|
|
17786
|
+
|
|
17787
|
+
// src/lib/repo-context.ts
|
|
17788
|
+
var import_node_child_process7 = require("node:child_process");
|
|
17789
|
+
var import_node_os3 = require("node:os");
|
|
17790
|
+
function rgInvocations(env = process.env) {
|
|
17791
|
+
const out = [{ cmd: "rg" }];
|
|
17792
|
+
if (env.CLAUDE_CODE_EXECPATH) out.push({ cmd: env.CLAUDE_CODE_EXECPATH, argv0: "rg" });
|
|
17793
|
+
out.push({ cmd: `${(0, import_node_os3.homedir)()}/.local/bin/claude`, argv0: "rg" });
|
|
17794
|
+
return out;
|
|
17795
|
+
}
|
|
17796
|
+
var MAX_SYMBOLS = 12;
|
|
17797
|
+
var MAX_SITES = 24;
|
|
17798
|
+
var MAX_SITES_PER_FILE = 3;
|
|
17799
|
+
var MAX_HITS_PER_SYMBOL = 50;
|
|
17800
|
+
var MAX_TEST_SLOTS = 8;
|
|
17801
|
+
var SITE_TEXT_MAX = 160;
|
|
17802
|
+
var ENCLOSING_SCAN_LINES = 200;
|
|
17803
|
+
var RG_TIMEOUT_MS = 1500;
|
|
17804
|
+
var IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
17805
|
+
var STOPLIST = /* @__PURE__ */ new Set([
|
|
17806
|
+
// keyword-shaped captures
|
|
17807
|
+
"if",
|
|
17808
|
+
"for",
|
|
17809
|
+
"while",
|
|
17810
|
+
"switch",
|
|
17811
|
+
"catch",
|
|
17812
|
+
"return",
|
|
17813
|
+
"function",
|
|
17814
|
+
"class",
|
|
17815
|
+
"const",
|
|
17816
|
+
"let",
|
|
17817
|
+
"var",
|
|
17818
|
+
"new",
|
|
17819
|
+
"else",
|
|
17820
|
+
"try",
|
|
17821
|
+
"finally",
|
|
17822
|
+
"throw",
|
|
17823
|
+
"await",
|
|
17824
|
+
"async",
|
|
17825
|
+
"yield",
|
|
17826
|
+
"delete",
|
|
17827
|
+
"typeof",
|
|
17828
|
+
"instanceof",
|
|
17829
|
+
"void",
|
|
17830
|
+
"this",
|
|
17831
|
+
"super",
|
|
17832
|
+
"import",
|
|
17833
|
+
"export",
|
|
17834
|
+
"default",
|
|
17835
|
+
"extends",
|
|
17836
|
+
"implements",
|
|
17837
|
+
"interface",
|
|
17838
|
+
"enum",
|
|
17839
|
+
"type",
|
|
17840
|
+
"public",
|
|
17841
|
+
"private",
|
|
17842
|
+
"protected",
|
|
17843
|
+
"static",
|
|
17844
|
+
"get",
|
|
17845
|
+
"set",
|
|
17846
|
+
"constructor",
|
|
17847
|
+
"def",
|
|
17848
|
+
"elif",
|
|
17849
|
+
"lambda",
|
|
17850
|
+
"with",
|
|
17851
|
+
"pass",
|
|
17852
|
+
"self",
|
|
17853
|
+
"cls",
|
|
17854
|
+
"not",
|
|
17855
|
+
"and",
|
|
17856
|
+
"or",
|
|
17857
|
+
"raise",
|
|
17858
|
+
"except",
|
|
17859
|
+
"func",
|
|
17860
|
+
"defer",
|
|
17861
|
+
"chan",
|
|
17862
|
+
"select",
|
|
17863
|
+
"range",
|
|
17864
|
+
"module",
|
|
17865
|
+
"struct",
|
|
17866
|
+
"trait",
|
|
17867
|
+
"impl",
|
|
17868
|
+
"using",
|
|
17869
|
+
"namespace",
|
|
17870
|
+
// universal noise
|
|
17871
|
+
"main",
|
|
17872
|
+
"init",
|
|
17873
|
+
"index",
|
|
17874
|
+
"data",
|
|
17875
|
+
"value",
|
|
17876
|
+
"result",
|
|
17877
|
+
"item",
|
|
17878
|
+
"name",
|
|
17879
|
+
"key",
|
|
17880
|
+
"run",
|
|
17881
|
+
"test",
|
|
17882
|
+
"setup",
|
|
17883
|
+
"update",
|
|
17884
|
+
"create",
|
|
17885
|
+
"handle",
|
|
17886
|
+
"check",
|
|
17887
|
+
"load",
|
|
17888
|
+
"save",
|
|
17889
|
+
"list",
|
|
17890
|
+
"map",
|
|
17891
|
+
"args",
|
|
17892
|
+
"params",
|
|
17893
|
+
"props",
|
|
17894
|
+
"state",
|
|
17895
|
+
"error",
|
|
17896
|
+
"err",
|
|
17897
|
+
"res",
|
|
17898
|
+
"req",
|
|
17899
|
+
"ctx",
|
|
17900
|
+
"config",
|
|
17901
|
+
"options",
|
|
17902
|
+
"util",
|
|
17903
|
+
"utils",
|
|
17904
|
+
"helper",
|
|
17905
|
+
"render",
|
|
17906
|
+
"build",
|
|
17907
|
+
"parse",
|
|
17908
|
+
"format",
|
|
17909
|
+
"apply",
|
|
17910
|
+
"process",
|
|
17911
|
+
"start",
|
|
17912
|
+
"stop"
|
|
17913
|
+
]);
|
|
17914
|
+
var TS_RULES = [
|
|
17915
|
+
/\b(?:function|class|interface|enum)\s+([A-Za-z_][A-Za-z0-9_]*)/,
|
|
17916
|
+
/\btype\s+([A-Za-z_][A-Za-z0-9_]*)\s*=/,
|
|
17917
|
+
// const foo = (…) => · const foo = x => · const foo = function.
|
|
17918
|
+
// ⚠ REQUIRES the arrow or `function` ON THE LINE. The first version accepted
|
|
17919
|
+
// any `= (` — and `const started = (rows?.[0] as Row)?.at` is a PARENTHESIZED
|
|
17920
|
+
// CAST, not a function. Replayed over 12 real commits, that one shape put
|
|
17921
|
+
// three local variables into the symbol set per commit. A multi-line arrow
|
|
17922
|
+
// is the accepted false negative; a cast is not an accepted false positive.
|
|
17923
|
+
/\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*=>)/,
|
|
17924
|
+
// method shape: name(…) { — keyword captures die at the stoplist
|
|
17925
|
+
/^\s*(?:(?:public|private|protected|static|readonly|async|override)\s+)*(?:\*\s*)?([A-Za-z_][A-Za-z0-9_]*)\s*\([^)]*\)\s*(?::[^{;\n]+)?\s*\{/
|
|
17926
|
+
];
|
|
17927
|
+
var PY_RULES = [
|
|
17928
|
+
/^\s*(?:async\s+)?def\s+([A-Za-z_]\w*)/,
|
|
17929
|
+
/^\s*class\s+([A-Za-z_]\w*)/
|
|
17930
|
+
];
|
|
17931
|
+
var GO_RULES = [
|
|
17932
|
+
/^func\s+(?:\([^)]*\)\s*)?([A-Za-z_]\w*)/,
|
|
17933
|
+
/^type\s+([A-Za-z_]\w*)/
|
|
17934
|
+
];
|
|
17935
|
+
var CLIKE_RULES = [
|
|
17936
|
+
/\b(?:class|interface|enum|record|struct)\s+([A-Za-z_]\w*)/,
|
|
17937
|
+
// access-modifier method shape: `public async Task<Foo> BarBaz(…`
|
|
17938
|
+
/(?:public|private|protected|internal|static|final|virtual|override|sealed|abstract)[\w<>[\],?\s]*?\s([A-Za-z_]\w*)\s*\(/
|
|
17939
|
+
];
|
|
17940
|
+
var RB_RULES = [
|
|
17941
|
+
/^\s*def\s+(?:self\.)?([A-Za-z_]\w*)/,
|
|
17942
|
+
/^\s*(?:class|module)\s+([A-Z]\w*)/
|
|
17943
|
+
];
|
|
17944
|
+
var RS_RULES = [
|
|
17945
|
+
/\bfn\s+([A-Za-z_]\w*)/,
|
|
17946
|
+
/\b(?:struct|enum|trait)\s+([A-Za-z_]\w*)/
|
|
17947
|
+
];
|
|
17948
|
+
var PHP_RULES = [
|
|
17949
|
+
/\bfunction\s+([A-Za-z_]\w*)/,
|
|
17950
|
+
/\bclass\s+([A-Za-z_]\w*)/
|
|
17951
|
+
];
|
|
17952
|
+
var C_RULES = [
|
|
17953
|
+
/^(?: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*\([^;]*$/,
|
|
17954
|
+
/\b(?:struct|enum|union|class)\s+([A-Za-z_]\w*)/,
|
|
17955
|
+
/::\s*~?([A-Za-z_]\w*)\s*\([^;]*$/
|
|
17956
|
+
// out-of-line C++ method definition
|
|
17957
|
+
];
|
|
17958
|
+
var SH_RULES = [
|
|
17959
|
+
/^\s*(?:function\s+)?([A-Za-z_]\w*)\s*\(\)\s*\{/,
|
|
17960
|
+
/^function\s+([A-Za-z_]\w*)/
|
|
17961
|
+
];
|
|
17962
|
+
var SQL_RULES = [
|
|
17963
|
+
/\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
|
|
17964
|
+
];
|
|
17965
|
+
var TF_RULES = [
|
|
17966
|
+
/^\s*(?:resource|data)\s+"[^"]+"\s+"([A-Za-z_]\w*)"/,
|
|
17967
|
+
/^\s*(?:module|variable|output)\s+"([A-Za-z_]\w*)"/
|
|
17968
|
+
];
|
|
17969
|
+
var SWIFT_RULES = [
|
|
17970
|
+
/\bfunc\s+([A-Za-z_]\w*)/,
|
|
17971
|
+
/\b(?:class|struct|enum|protocol|extension|actor)\s+([A-Za-z_]\w*)/
|
|
17972
|
+
];
|
|
17973
|
+
var DART_RULES = [
|
|
17974
|
+
/\b(?:class|enum|mixin|extension)\s+([A-Za-z_]\w*)/,
|
|
17975
|
+
/^\s*(?:static\s+)?(?:Future<[^>]*>|Stream<[^>]*>|void|int|double|bool|String|num|dynamic|[A-Z]\w*(?:<[^>]*>)?)\s+([a-z_]\w*)\s*\(/
|
|
17976
|
+
];
|
|
17977
|
+
var LUA_RULES = [
|
|
17978
|
+
/^\s*(?:local\s+)?function\s+(?:[\w.]+[.:])?([A-Za-z_]\w*)/
|
|
17979
|
+
];
|
|
17980
|
+
var EX_RULES = [
|
|
17981
|
+
/^\s*def(?:p|macro)?\s+([a-z_]\w*)/,
|
|
17982
|
+
/^\s*defmodule\s+(?:[\w.]*\.)?([A-Z]\w*)/
|
|
17983
|
+
];
|
|
17984
|
+
var PROTO_RULES = [
|
|
17985
|
+
/^\s*(?:message|service|enum)\s+([A-Za-z_]\w*)/,
|
|
17986
|
+
/^\s*rpc\s+([A-Za-z_]\w*)/
|
|
17987
|
+
];
|
|
17988
|
+
var GRAPHQL_RULES = [
|
|
17989
|
+
/^\s*(?:type|interface|enum|input|union|scalar)\s+([A-Za-z_]\w*)/
|
|
17990
|
+
];
|
|
17991
|
+
var RULES_BY_EXT = {
|
|
17992
|
+
ts: TS_RULES,
|
|
17993
|
+
tsx: TS_RULES,
|
|
17994
|
+
js: TS_RULES,
|
|
17995
|
+
jsx: TS_RULES,
|
|
17996
|
+
mjs: TS_RULES,
|
|
17997
|
+
cjs: TS_RULES,
|
|
17998
|
+
svelte: TS_RULES,
|
|
17999
|
+
vue: TS_RULES,
|
|
18000
|
+
// script blocks
|
|
18001
|
+
py: PY_RULES,
|
|
18002
|
+
go: GO_RULES,
|
|
18003
|
+
java: CLIKE_RULES,
|
|
18004
|
+
cs: CLIKE_RULES,
|
|
18005
|
+
kt: CLIKE_RULES,
|
|
18006
|
+
scala: CLIKE_RULES,
|
|
18007
|
+
rb: RB_RULES,
|
|
18008
|
+
rs: RS_RULES,
|
|
18009
|
+
php: PHP_RULES,
|
|
18010
|
+
c: C_RULES,
|
|
18011
|
+
cpp: C_RULES,
|
|
18012
|
+
cc: C_RULES,
|
|
18013
|
+
h: C_RULES,
|
|
18014
|
+
hpp: C_RULES,
|
|
18015
|
+
sh: SH_RULES,
|
|
18016
|
+
bash: SH_RULES,
|
|
18017
|
+
zsh: SH_RULES,
|
|
18018
|
+
sql: SQL_RULES,
|
|
18019
|
+
tf: TF_RULES,
|
|
18020
|
+
hcl: TF_RULES,
|
|
18021
|
+
swift: SWIFT_RULES,
|
|
18022
|
+
dart: DART_RULES,
|
|
18023
|
+
lua: LUA_RULES,
|
|
18024
|
+
ex: EX_RULES,
|
|
18025
|
+
exs: EX_RULES,
|
|
18026
|
+
proto: PROTO_RULES,
|
|
18027
|
+
graphql: GRAPHQL_RULES,
|
|
18028
|
+
gql: GRAPHQL_RULES
|
|
18029
|
+
};
|
|
18030
|
+
var HUNK_HEADER = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
18031
|
+
function parseDiffSignals(diff) {
|
|
18032
|
+
const addedRanges = [];
|
|
18033
|
+
const touchPoints = [];
|
|
18034
|
+
const deletedLines = [];
|
|
18035
|
+
let newLine = 0;
|
|
18036
|
+
let oldRemaining = 0;
|
|
18037
|
+
let newRemaining = 0;
|
|
18038
|
+
let runStart = -1;
|
|
18039
|
+
let deletionRun = false;
|
|
18040
|
+
const closeAddedRun = () => {
|
|
18041
|
+
if (runStart >= 0) addedRanges.push([runStart, newLine - 1]);
|
|
18042
|
+
runStart = -1;
|
|
18043
|
+
};
|
|
18044
|
+
const closeDeletionRun = () => {
|
|
18045
|
+
if (deletionRun) touchPoints.push(Math.max(1, newLine));
|
|
18046
|
+
deletionRun = false;
|
|
18047
|
+
};
|
|
18048
|
+
for (const line of diff.split("\n")) {
|
|
18049
|
+
const inHunk = oldRemaining > 0 || newRemaining > 0;
|
|
18050
|
+
if (!inHunk) {
|
|
18051
|
+
closeAddedRun();
|
|
18052
|
+
closeDeletionRun();
|
|
18053
|
+
const header = HUNK_HEADER.exec(line);
|
|
18054
|
+
if (header) {
|
|
18055
|
+
newLine = parseInt(header[3], 10);
|
|
18056
|
+
oldRemaining = header[2] === void 0 ? 1 : parseInt(header[2], 10);
|
|
18057
|
+
newRemaining = header[4] === void 0 ? 1 : parseInt(header[4], 10);
|
|
18058
|
+
if (newRemaining === 0) newLine = Math.max(1, newLine);
|
|
18059
|
+
}
|
|
18060
|
+
continue;
|
|
18061
|
+
}
|
|
18062
|
+
if (line.startsWith("\\")) continue;
|
|
18063
|
+
if (line.startsWith("+") && newRemaining > 0) {
|
|
18064
|
+
deletionRun = false;
|
|
18065
|
+
if (runStart < 0) runStart = newLine;
|
|
18066
|
+
newLine++;
|
|
18067
|
+
newRemaining--;
|
|
18068
|
+
continue;
|
|
18069
|
+
}
|
|
18070
|
+
if (line.startsWith("-") && oldRemaining > 0) {
|
|
18071
|
+
closeAddedRun();
|
|
18072
|
+
deletionRun = true;
|
|
18073
|
+
deletedLines.push(line.slice(1));
|
|
18074
|
+
oldRemaining--;
|
|
18075
|
+
continue;
|
|
18076
|
+
}
|
|
18077
|
+
closeAddedRun();
|
|
18078
|
+
closeDeletionRun();
|
|
18079
|
+
newLine++;
|
|
18080
|
+
if (oldRemaining > 0) oldRemaining--;
|
|
18081
|
+
if (newRemaining > 0) newRemaining--;
|
|
18082
|
+
}
|
|
18083
|
+
closeAddedRun();
|
|
18084
|
+
closeDeletionRun();
|
|
18085
|
+
return { addedRanges, touchPoints, deletedLines };
|
|
18086
|
+
}
|
|
18087
|
+
function declNameOn(line, rules) {
|
|
18088
|
+
for (const r of rules) {
|
|
18089
|
+
const m = r.exec(line);
|
|
18090
|
+
if (m?.[1]) return m[1];
|
|
18091
|
+
}
|
|
18092
|
+
return null;
|
|
18093
|
+
}
|
|
18094
|
+
function acceptable(name) {
|
|
18095
|
+
if (!name || !IDENTIFIER.test(name) || STOPLIST.has(name.toLowerCase())) return false;
|
|
18096
|
+
if (name.length < 3) return false;
|
|
18097
|
+
if (name.length === 3 && name === name.toLowerCase() && !name.includes("_")) return false;
|
|
18098
|
+
return true;
|
|
18099
|
+
}
|
|
18100
|
+
function isMultiSegment(name) {
|
|
18101
|
+
return /[a-z][A-Z]/.test(name) || name.includes("_");
|
|
18102
|
+
}
|
|
18103
|
+
function extractFileSymbols(path, content, signals) {
|
|
18104
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
18105
|
+
const rules = RULES_BY_EXT[ext];
|
|
18106
|
+
if (!rules) return [];
|
|
18107
|
+
if (signals.addedRanges.length === 0 && signals.touchPoints.length === 0 && signals.deletedLines.length === 0) return [];
|
|
18108
|
+
const lines = content.split("\n");
|
|
18109
|
+
const found = [];
|
|
18110
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18111
|
+
const add = (name) => {
|
|
18112
|
+
if (acceptable(name) && !seen.has(name)) {
|
|
18113
|
+
seen.add(name);
|
|
18114
|
+
found.push(name);
|
|
18115
|
+
}
|
|
18116
|
+
};
|
|
18117
|
+
for (const deleted of signals.deletedLines) {
|
|
18118
|
+
add(declNameOn(deleted, rules));
|
|
18119
|
+
}
|
|
18120
|
+
for (const [start, end] of signals.addedRanges) {
|
|
18121
|
+
for (let n = start; n <= Math.min(end, lines.length); n++) {
|
|
18122
|
+
add(declNameOn(lines[n - 1] ?? "", rules));
|
|
18123
|
+
}
|
|
18124
|
+
}
|
|
18125
|
+
const scanStarts = [
|
|
18126
|
+
...signals.addedRanges.map(([start]) => start),
|
|
18127
|
+
...signals.touchPoints
|
|
18128
|
+
];
|
|
18129
|
+
for (const start of scanStarts) {
|
|
18130
|
+
const floor = Math.max(1, start - ENCLOSING_SCAN_LINES);
|
|
18131
|
+
for (let n = Math.min(start, lines.length); n >= floor; n--) {
|
|
18132
|
+
const name = declNameOn(lines[n - 1] ?? "", rules);
|
|
18133
|
+
if (acceptable(name)) {
|
|
18134
|
+
add(name);
|
|
18135
|
+
break;
|
|
18136
|
+
}
|
|
18137
|
+
}
|
|
18138
|
+
}
|
|
18139
|
+
return found;
|
|
18140
|
+
}
|
|
18141
|
+
function rankSymbols(symbols) {
|
|
18142
|
+
return symbols.map((s, i) => ({ s, i })).sort((a, b) => {
|
|
18143
|
+
const seg = Number(isMultiSegment(b.s)) - Number(isMultiSegment(a.s));
|
|
18144
|
+
if (seg !== 0) return seg;
|
|
18145
|
+
if (b.s.length !== a.s.length) return b.s.length - a.s.length;
|
|
18146
|
+
return a.i - b.i;
|
|
18147
|
+
}).slice(0, MAX_SYMBOLS).map((x) => x.s);
|
|
18148
|
+
}
|
|
18149
|
+
var TEST_PATH = /(^|\/)(tests?|specs?|__tests__|e2e)(\/|$)/i;
|
|
18150
|
+
var TEST_FILE = /(\.(test|spec|e2e)\.[^./]+|_test\.[^./]+|_spec\.rb)$/i;
|
|
18151
|
+
var TEST_PY_PREFIX = /(^|\/)test_[^/]+\.py$/i;
|
|
18152
|
+
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)/;
|
|
18153
|
+
var BARE_MEMBER_LINE = /^(type\s+)?[A-Za-z_$][\w$]*\s*,?$/;
|
|
18154
|
+
var GO_IMPORT_PATH_LINE = /^"[^"]+",?$/;
|
|
18155
|
+
var SITE_CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
18156
|
+
"ts",
|
|
18157
|
+
"tsx",
|
|
18158
|
+
"js",
|
|
18159
|
+
"jsx",
|
|
18160
|
+
"mjs",
|
|
18161
|
+
"cjs",
|
|
18162
|
+
"py",
|
|
18163
|
+
"go",
|
|
18164
|
+
"java",
|
|
18165
|
+
"kt",
|
|
18166
|
+
"rb",
|
|
18167
|
+
"rs",
|
|
18168
|
+
"scala",
|
|
18169
|
+
"c",
|
|
18170
|
+
"cpp",
|
|
18171
|
+
"cc",
|
|
18172
|
+
"h",
|
|
18173
|
+
"hpp",
|
|
18174
|
+
"cs",
|
|
18175
|
+
"php",
|
|
18176
|
+
"swift",
|
|
18177
|
+
"dart",
|
|
18178
|
+
"lua",
|
|
18179
|
+
"sh",
|
|
18180
|
+
"bash",
|
|
18181
|
+
"zsh",
|
|
18182
|
+
"svelte",
|
|
18183
|
+
"vue",
|
|
18184
|
+
"ex",
|
|
18185
|
+
"exs",
|
|
18186
|
+
// sql/tf carry REAL call sites (SELECT my_function(...), module.name) —
|
|
18187
|
+
// excluded in an earlier round because of migration-comment noise, which the
|
|
18188
|
+
// COMMENT_LINE filter now handles on its own.
|
|
18189
|
+
"sql",
|
|
18190
|
+
"tf",
|
|
18191
|
+
"hcl"
|
|
18192
|
+
]);
|
|
18193
|
+
var COMMENT_LINE = /^(\/\/|#(?!\[)|\*|\/\*|--\s|<!--)/;
|
|
18194
|
+
function isCodeSiteFile(path) {
|
|
18195
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
18196
|
+
return SITE_CODE_EXTENSIONS.has(ext);
|
|
18197
|
+
}
|
|
18198
|
+
function isTestPath(path) {
|
|
18199
|
+
return TEST_PATH.test(path) || TEST_FILE.test(path) || TEST_PY_PREFIX.test(path);
|
|
18200
|
+
}
|
|
18201
|
+
function parseRgLine(line) {
|
|
18202
|
+
const first = line.indexOf(":");
|
|
18203
|
+
if (first <= 0) return null;
|
|
18204
|
+
const second = line.indexOf(":", first + 1);
|
|
18205
|
+
if (second < 0) return null;
|
|
18206
|
+
const n = parseInt(line.slice(first + 1, second), 10);
|
|
18207
|
+
if (!Number.isFinite(n) || n < 1) return null;
|
|
18208
|
+
return { file: line.slice(0, first), line: n, text: line.slice(second + 1) };
|
|
18209
|
+
}
|
|
18210
|
+
var isWordChar = (c) => c !== void 0 && /[A-Za-z0-9_]/.test(c);
|
|
18211
|
+
function wordHit(text, symbol) {
|
|
18212
|
+
let from = 0;
|
|
18213
|
+
for (; ; ) {
|
|
18214
|
+
const at = text.indexOf(symbol, from);
|
|
18215
|
+
if (at < 0) return false;
|
|
18216
|
+
const before = at === 0 ? void 0 : text[at - 1];
|
|
18217
|
+
const after = text[at + symbol.length];
|
|
18218
|
+
if (!isWordChar(before) && !isWordChar(after)) return true;
|
|
18219
|
+
from = at + 1;
|
|
18220
|
+
}
|
|
18221
|
+
}
|
|
18222
|
+
function isContractLine(text, symbol) {
|
|
18223
|
+
for (const kw of ["implements", "extends"]) {
|
|
18224
|
+
let from = 0;
|
|
18225
|
+
for (; ; ) {
|
|
18226
|
+
const at = text.indexOf(kw, from);
|
|
18227
|
+
if (at < 0) break;
|
|
18228
|
+
from = at + 1;
|
|
18229
|
+
const before = at === 0 ? void 0 : text[at - 1];
|
|
18230
|
+
const after = text[at + kw.length];
|
|
18231
|
+
if (isWordChar(before) || isWordChar(after)) continue;
|
|
18232
|
+
let clause = text.slice(at + kw.length);
|
|
18233
|
+
const stop = Math.min(
|
|
18234
|
+
...[clause.indexOf(";"), clause.indexOf("{")].filter((i) => i >= 0)
|
|
18235
|
+
);
|
|
18236
|
+
if (Number.isFinite(stop)) clause = clause.slice(0, stop);
|
|
18237
|
+
if (wordHit(clause, symbol)) return true;
|
|
18238
|
+
}
|
|
18239
|
+
}
|
|
18240
|
+
return false;
|
|
18241
|
+
}
|
|
18242
|
+
function partitionSites(rgLines, symbols, opts) {
|
|
18243
|
+
const hits = [];
|
|
18244
|
+
const hitCount = /* @__PURE__ */ new Map();
|
|
18245
|
+
for (const line of rgLines) {
|
|
18246
|
+
const hit = parseRgLine(line);
|
|
18247
|
+
if (!hit) continue;
|
|
18248
|
+
const matched = symbols.filter((s) => wordHit(hit.text, s));
|
|
18249
|
+
for (const s of matched) hitCount.set(s, (hitCount.get(s) ?? 0) + 1);
|
|
18250
|
+
if (matched.length === 0) continue;
|
|
18251
|
+
hits.push({ ...hit, symbol: matched[0] });
|
|
18252
|
+
}
|
|
18253
|
+
hits.sort((a, b) => a.file < b.file ? -1 : a.file > b.file ? 1 : a.line - b.line);
|
|
18254
|
+
const dropped = symbols.filter((s) => (hitCount.get(s) ?? 0) > MAX_HITS_PER_SYMBOL);
|
|
18255
|
+
const droppedSet = new Set(dropped);
|
|
18256
|
+
const perFile = /* @__PURE__ */ new Map();
|
|
18257
|
+
const callers = [];
|
|
18258
|
+
const tests = [];
|
|
18259
|
+
for (const h of hits) {
|
|
18260
|
+
if (droppedSet.has(h.symbol)) continue;
|
|
18261
|
+
if (opts.sentPaths.has(h.file)) continue;
|
|
18262
|
+
if (opts.isExcluded(h.file)) continue;
|
|
18263
|
+
if (!isCodeSiteFile(h.file)) continue;
|
|
18264
|
+
const text = h.text.trim();
|
|
18265
|
+
if (text.length === 0 || IMPORT_LINE.test(h.text)) continue;
|
|
18266
|
+
if (COMMENT_LINE.test(text)) continue;
|
|
18267
|
+
if (BARE_MEMBER_LINE.test(text) || GO_IMPORT_PATH_LINE.test(text)) continue;
|
|
18268
|
+
const n = perFile.get(h.file) ?? 0;
|
|
18269
|
+
if (n >= MAX_SITES_PER_FILE) continue;
|
|
18270
|
+
const site = {
|
|
18271
|
+
file: h.file,
|
|
18272
|
+
line: h.line,
|
|
18273
|
+
text: text.slice(0, SITE_TEXT_MAX),
|
|
18274
|
+
symbol: h.symbol,
|
|
18275
|
+
// R2 falls out of R1 for free: a word search for `Sym` already matches
|
|
18276
|
+
// `implements Sym` / `extends Sym` lines — classification is all R2 is.
|
|
18277
|
+
...isContractLine(text, h.symbol) ? { kind: "contract" } : {}
|
|
18278
|
+
};
|
|
18279
|
+
if (isTestPath(h.file)) {
|
|
18280
|
+
if (tests.length < MAX_TEST_SLOTS) {
|
|
18281
|
+
tests.push(site);
|
|
18282
|
+
perFile.set(h.file, n + 1);
|
|
18283
|
+
}
|
|
18284
|
+
} else if (callers.length + tests.length < MAX_SITES) {
|
|
18285
|
+
callers.push(site);
|
|
18286
|
+
perFile.set(h.file, n + 1);
|
|
18287
|
+
}
|
|
18288
|
+
}
|
|
18289
|
+
while (callers.length + tests.length > MAX_SITES) callers.pop();
|
|
18290
|
+
return { callers, tests, dropped };
|
|
18291
|
+
}
|
|
18292
|
+
function buildRepoContext(input) {
|
|
18293
|
+
const started = Date.now();
|
|
18294
|
+
let signalsByPath;
|
|
18295
|
+
if (input.signalsByPath) {
|
|
18296
|
+
if (input.signalsByPath.size === 0) return { state: "absent", reason: "no-diffs" };
|
|
18297
|
+
signalsByPath = input.signalsByPath;
|
|
18298
|
+
} else {
|
|
18299
|
+
if (input.diffs.length === 0) return { state: "absent", reason: "no-diffs" };
|
|
18300
|
+
signalsByPath = /* @__PURE__ */ new Map();
|
|
18301
|
+
for (const d of input.diffs) signalsByPath.set(d.path, parseDiffSignals(d.diff));
|
|
18302
|
+
}
|
|
18303
|
+
const collected = [];
|
|
18304
|
+
const unsupported = /* @__PURE__ */ new Set();
|
|
18305
|
+
const noteUnsupported = (path) => {
|
|
18306
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
18307
|
+
if (ext && !RULES_BY_EXT[ext]) unsupported.add(ext);
|
|
18308
|
+
};
|
|
18309
|
+
const deltaPathSet = new Set(input.deltaFiles.map((f) => f.path));
|
|
18310
|
+
for (const f of input.deltaFiles) {
|
|
18311
|
+
const signals = signalsByPath.get(f.path);
|
|
18312
|
+
if (!signals) continue;
|
|
18313
|
+
noteUnsupported(f.path);
|
|
18314
|
+
collected.push(...extractFileSymbols(f.path, f.content, signals));
|
|
18315
|
+
}
|
|
18316
|
+
for (const [path, signals] of signalsByPath) {
|
|
18317
|
+
if (deltaPathSet.has(path)) continue;
|
|
18318
|
+
if (signals.deletedLines.length > 0) {
|
|
18319
|
+
noteUnsupported(path);
|
|
18320
|
+
collected.push(...extractFileSymbols(path, "", signals));
|
|
18321
|
+
}
|
|
18322
|
+
}
|
|
18323
|
+
const unsupportedExts = [...unsupported].sort().slice(0, 8);
|
|
18324
|
+
const audit = unsupportedExts.length > 0 ? { unsupported_exts: unsupportedExts } : {};
|
|
18325
|
+
const symbols = rankSymbols([...new Set(collected)]);
|
|
18326
|
+
if (symbols.length === 0) {
|
|
18327
|
+
const everySupportedFileFoundNothing = unsupportedExts.length > 0;
|
|
18328
|
+
return {
|
|
18329
|
+
state: "absent",
|
|
18330
|
+
reason: everySupportedFileFoundNothing ? "unsupported-language" : "no-symbols",
|
|
18331
|
+
...audit
|
|
18332
|
+
};
|
|
18333
|
+
}
|
|
18334
|
+
const args = [
|
|
18335
|
+
"-n",
|
|
18336
|
+
"-w",
|
|
18337
|
+
"-F",
|
|
18338
|
+
"--no-heading",
|
|
18339
|
+
"--color",
|
|
18340
|
+
"never",
|
|
18341
|
+
// ⚠ NO `--sort path` — it single-threads rg, and on a large worktree that
|
|
18342
|
+
// is the difference between 17ms and a timeout. Determinism is restored by
|
|
18343
|
+
// sorting the hits in partitionSites instead.
|
|
18344
|
+
//
|
|
18345
|
+
// `-m 8` bounds output PER FILE (shared across all patterns), so a noisy
|
|
18346
|
+
// repo cannot blow the 4MB read buffer and turn the whole feature into
|
|
18347
|
+
// `absent/error`. Cost, accepted: the frequency gate sees per-file-capped
|
|
18348
|
+
// counts, so a symbol concentrated in a handful of files can slip a gate
|
|
18349
|
+
// a full count would have tripped — but the ≤3-sites-per-file cap already
|
|
18350
|
+
// bounds exactly that shape's damage; the gate exists for the many-file
|
|
18351
|
+
// 'init' shape, which 8-per-file still trips (>6 files ⇒ >50).
|
|
18352
|
+
"-m",
|
|
18353
|
+
"8",
|
|
18354
|
+
"--max-columns",
|
|
18355
|
+
"300",
|
|
18356
|
+
"--max-columns-preview",
|
|
18357
|
+
...symbols.flatMap((s) => ["-e", s]),
|
|
18358
|
+
"-g",
|
|
18359
|
+
"!**/{dist,build,out,vendor,node_modules,.git,coverage,target,__pycache__}/**",
|
|
18360
|
+
"./"
|
|
18361
|
+
];
|
|
18362
|
+
let res = null;
|
|
18363
|
+
for (const inv of rgInvocations()) {
|
|
18364
|
+
res = (0, import_node_child_process7.spawnSync)(inv.cmd, args, {
|
|
18365
|
+
...inv.argv0 ? { argv0: inv.argv0 } : {},
|
|
18366
|
+
cwd: input.cwd ?? process.cwd(),
|
|
18367
|
+
timeout: input.timeoutMs ?? RG_TIMEOUT_MS,
|
|
18368
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
18369
|
+
encoding: "utf8"
|
|
18370
|
+
});
|
|
18371
|
+
if (res.error?.code !== "ENOENT") break;
|
|
18372
|
+
}
|
|
18373
|
+
if (!res || res.error?.code === "ENOENT") {
|
|
18374
|
+
return { state: "absent", reason: "no-tool", symbols, ...audit };
|
|
18375
|
+
}
|
|
18376
|
+
if (res.error) {
|
|
18377
|
+
const code = res.error.code;
|
|
18378
|
+
if (code === "ETIMEDOUT") return { state: "absent", reason: "timeout", symbols, ...audit };
|
|
18379
|
+
return { state: "absent", reason: "error", symbols, ...audit };
|
|
18380
|
+
}
|
|
18381
|
+
if (res.signal) return { state: "absent", reason: "timeout", symbols, ...audit };
|
|
18382
|
+
if (res.status !== 0 && res.status !== 1) return { state: "absent", reason: "error", symbols, ...audit };
|
|
18383
|
+
const lines = (res.stdout ?? "").split("\n").map((l) => l.replace(/^\.\//, "")).filter(Boolean);
|
|
18384
|
+
const { callers, tests, dropped } = partitionSites(lines, symbols, {
|
|
18385
|
+
sentPaths: input.sentPaths,
|
|
18386
|
+
isExcluded: input.isExcluded
|
|
18387
|
+
});
|
|
18388
|
+
if (callers.length === 0 && tests.length === 0) {
|
|
18389
|
+
return {
|
|
18390
|
+
state: "absent",
|
|
18391
|
+
reason: "no-sites",
|
|
18392
|
+
symbols,
|
|
18393
|
+
...dropped.length > 0 ? { dropped_symbols: dropped } : {},
|
|
18394
|
+
...audit,
|
|
18395
|
+
elapsed_ms: Date.now() - started
|
|
18396
|
+
};
|
|
18397
|
+
}
|
|
18398
|
+
return {
|
|
18399
|
+
state: "ok",
|
|
18400
|
+
symbols,
|
|
18401
|
+
...dropped.length > 0 ? { dropped_symbols: dropped } : {},
|
|
18402
|
+
...audit,
|
|
18403
|
+
callers,
|
|
18404
|
+
tests,
|
|
18405
|
+
elapsed_ms: Date.now() - started
|
|
18406
|
+
};
|
|
18407
|
+
}
|
|
18408
|
+
var MAX_EXCERPTS = 12;
|
|
18409
|
+
var MAX_EXCERPTS_PER_FILE = 2;
|
|
18410
|
+
var EXCERPT_MAX_LINES = 30;
|
|
18411
|
+
var EXCERPT_MAX_CHARS = 2400;
|
|
18412
|
+
var EXCERPT_TOTAL_BYTES = 24576;
|
|
18413
|
+
var EXCERPT_DECL_SCAN = 40;
|
|
18414
|
+
function extractEnclosingExcerpt(content, siteLine, path) {
|
|
18415
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
18416
|
+
const rules = RULES_BY_EXT[ext] ?? [];
|
|
18417
|
+
const lines = content.split("\n");
|
|
18418
|
+
if (siteLine < 1 || siteLine > lines.length) return null;
|
|
18419
|
+
let declStart = null;
|
|
18420
|
+
const floor = Math.max(1, siteLine - EXCERPT_DECL_SCAN);
|
|
18421
|
+
for (let n = siteLine; n >= floor; n--) {
|
|
18422
|
+
if (declNameOn(lines[n - 1] ?? "", rules) !== null) {
|
|
18423
|
+
declStart = n;
|
|
18424
|
+
break;
|
|
18425
|
+
}
|
|
18426
|
+
}
|
|
18427
|
+
let start;
|
|
18428
|
+
if (declStart !== null && siteLine - declStart < EXCERPT_MAX_LINES) {
|
|
18429
|
+
start = declStart;
|
|
18430
|
+
} else {
|
|
18431
|
+
start = Math.max(1, siteLine - (EXCERPT_MAX_LINES - 6));
|
|
18432
|
+
}
|
|
18433
|
+
const end = Math.min(lines.length, start + EXCERPT_MAX_LINES - 1);
|
|
18434
|
+
const text = lines.slice(start - 1, end).join("\n").slice(0, EXCERPT_MAX_CHARS);
|
|
18435
|
+
return { start_line: start, text };
|
|
18436
|
+
}
|
|
18437
|
+
function upgradeToExcerpts(rc, opts) {
|
|
18438
|
+
if (rc.state !== "ok") return;
|
|
18439
|
+
const ranked = [
|
|
18440
|
+
...(rc.callers ?? []).filter((s) => s.kind === "contract"),
|
|
18441
|
+
...rc.tests ?? [],
|
|
18442
|
+
...(rc.callers ?? []).filter((s) => s.kind !== "contract")
|
|
18443
|
+
];
|
|
18444
|
+
const perFile = /* @__PURE__ */ new Map();
|
|
18445
|
+
const contentCache = /* @__PURE__ */ new Map();
|
|
18446
|
+
const excerpts = [];
|
|
18447
|
+
let totalBytes = 0;
|
|
18448
|
+
for (const site of ranked) {
|
|
18449
|
+
if (excerpts.length >= MAX_EXCERPTS) break;
|
|
18450
|
+
const used = perFile.get(site.file) ?? 0;
|
|
18451
|
+
if (used >= MAX_EXCERPTS_PER_FILE) continue;
|
|
18452
|
+
if (!contentCache.has(site.file)) contentCache.set(site.file, opts.readFile(site.file));
|
|
18453
|
+
const content = contentCache.get(site.file);
|
|
18454
|
+
if (content === null || content === void 0) continue;
|
|
18455
|
+
const ex = extractEnclosingExcerpt(content, site.line, site.file);
|
|
18456
|
+
if (!ex) continue;
|
|
18457
|
+
if (totalBytes + ex.text.length > EXCERPT_TOTAL_BYTES) break;
|
|
18458
|
+
excerpts.push({
|
|
18459
|
+
file: site.file,
|
|
18460
|
+
start_line: ex.start_line,
|
|
18461
|
+
symbol: site.symbol,
|
|
18462
|
+
kind: site.kind === "contract" ? "contract" : isTestPath(site.file) ? "test" : "caller",
|
|
18463
|
+
text: ex.text
|
|
18464
|
+
});
|
|
18465
|
+
totalBytes += ex.text.length;
|
|
18466
|
+
perFile.set(site.file, used + 1);
|
|
18467
|
+
}
|
|
18468
|
+
if (excerpts.length > 0) rc.excerpts = excerpts;
|
|
18469
|
+
}
|
|
18470
|
+
|
|
18471
|
+
// src/lib/git-frame.ts
|
|
17623
18472
|
var VALUE_TOKEN = `(?:'[^']*'|"[^"]*"|\\S+)`;
|
|
17624
18473
|
var GIT_GLOBAL_OPTS = `(?:\\s+(?:-[Cc]\\s+${VALUE_TOKEN}|--?[\\w-]+(?:=\\S+)?))*`;
|
|
17625
18474
|
var COMMIT_HEAD = `git${GIT_GLOBAL_OPTS}\\s+commit(?![\\w-])`;
|
|
@@ -17665,14 +18514,14 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
|
|
|
17665
18514
|
if (!m) continue;
|
|
17666
18515
|
named = true;
|
|
17667
18516
|
if (m[1] === void 0) {
|
|
17668
|
-
dir = (0,
|
|
18517
|
+
dir = (0, import_node_os4.homedir)();
|
|
17669
18518
|
continue;
|
|
17670
18519
|
}
|
|
17671
18520
|
const raw = unquote(m[1]);
|
|
17672
18521
|
if (SHELL_DYNAMIC.test(raw) || raw === "-") {
|
|
17673
18522
|
return { dir: null, named: true, unresolvable: `cd target not statically resolvable: ${raw}` };
|
|
17674
18523
|
}
|
|
17675
|
-
const expanded = raw === "~" ? (0,
|
|
18524
|
+
const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
|
|
17676
18525
|
dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
|
|
17677
18526
|
}
|
|
17678
18527
|
const seg = segments[segmentIndex];
|
|
@@ -17691,7 +18540,7 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
|
|
|
17691
18540
|
if (SHELL_DYNAMIC.test(raw)) {
|
|
17692
18541
|
return { dir: null, named: true, unresolvable: `-C target not statically resolvable: ${raw}` };
|
|
17693
18542
|
}
|
|
17694
|
-
const expanded = raw === "~" ? (0,
|
|
18543
|
+
const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
|
|
17695
18544
|
dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
|
|
17696
18545
|
}
|
|
17697
18546
|
}
|
|
@@ -17748,7 +18597,7 @@ function parsePushTarget(segment) {
|
|
|
17748
18597
|
}
|
|
17749
18598
|
function gitAt(dir, args) {
|
|
17750
18599
|
try {
|
|
17751
|
-
return (0,
|
|
18600
|
+
return (0, import_node_child_process8.execFileSync)("git", args, { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
17752
18601
|
} catch {
|
|
17753
18602
|
return "";
|
|
17754
18603
|
}
|
|
@@ -17887,17 +18736,51 @@ function rangeFiles(frame, range) {
|
|
|
17887
18736
|
}
|
|
17888
18737
|
return out.split("\n").filter((l) => l.length > 0).filter((f) => !isVerityOwnedPath(f));
|
|
17889
18738
|
}
|
|
17890
|
-
function
|
|
17891
|
-
|
|
17892
|
-
|
|
17893
|
-
|
|
17894
|
-
|
|
17895
|
-
|
|
17896
|
-
|
|
17897
|
-
|
|
17898
|
-
|
|
17899
|
-
|
|
17900
|
-
|
|
18739
|
+
function rangeChangeSignals(frame, range, paths) {
|
|
18740
|
+
const out = /* @__PURE__ */ new Map();
|
|
18741
|
+
if (range.kind === "nothing" || paths.length === 0) return out;
|
|
18742
|
+
const args = range.kind === "staged" ? ["diff", "--cached", "--unified=0"] : ["diff", "--unified=0", range.base, range.head === "INDEX" ? "HEAD" : range.head];
|
|
18743
|
+
const diff = frameGit(frame, [...args, "--", ...paths]);
|
|
18744
|
+
let current = null;
|
|
18745
|
+
let oldSide = null;
|
|
18746
|
+
let buf = [];
|
|
18747
|
+
const flush = () => {
|
|
18748
|
+
if (current !== null && buf.length > 0) out.set(current, parseDiffSignals(buf.join("\n")));
|
|
18749
|
+
buf = [];
|
|
18750
|
+
};
|
|
18751
|
+
for (const line of diff.split("\n")) {
|
|
18752
|
+
if (line.startsWith("diff --git ")) {
|
|
18753
|
+
flush();
|
|
18754
|
+
current = null;
|
|
18755
|
+
oldSide = null;
|
|
18756
|
+
continue;
|
|
18757
|
+
}
|
|
18758
|
+
const minusM = /^--- (?:a\/)?(.+)$/.exec(line);
|
|
18759
|
+
if (minusM) {
|
|
18760
|
+
oldSide = minusM[1] === "/dev/null" ? null : minusM[1];
|
|
18761
|
+
continue;
|
|
18762
|
+
}
|
|
18763
|
+
const plusM = /^\+\+\+ (?:b\/)?(.+)$/.exec(line);
|
|
18764
|
+
if (plusM) {
|
|
18765
|
+
current = plusM[1] === "/dev/null" ? oldSide : plusM[1];
|
|
18766
|
+
continue;
|
|
18767
|
+
}
|
|
18768
|
+
if (current !== null) buf.push(line);
|
|
18769
|
+
}
|
|
18770
|
+
flush();
|
|
18771
|
+
return out;
|
|
18772
|
+
}
|
|
18773
|
+
function rangeMessages(frame, range) {
|
|
18774
|
+
if (range.kind === "staged" || range.kind === "nothing" || !range.base) return "";
|
|
18775
|
+
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");
|
|
18776
|
+
}
|
|
18777
|
+
function frameTelemetry(frame, range, divergence) {
|
|
18778
|
+
const t = {
|
|
18779
|
+
anchor: frame.anchor,
|
|
18780
|
+
linked_worktree: frame.isLinkedWorktree,
|
|
18781
|
+
range_via: range?.via ?? null,
|
|
18782
|
+
refusal: frame.refusal
|
|
18783
|
+
};
|
|
17901
18784
|
if (divergence) {
|
|
17902
18785
|
t.root_differs = !!frame.worktreeRoot && !!divergence.actualRoot && realpathOr(frame.worktreeRoot) !== realpathOr(divergence.actualRoot);
|
|
17903
18786
|
const a = [...divergence.actualFiles].sort().join("\n");
|
|
@@ -17946,6 +18829,7 @@ var MAX_COMMANDS = 10;
|
|
|
17946
18829
|
var MAX_COMMAND_CHARS = 80;
|
|
17947
18830
|
var MAX_TOOL_BLOCKS = 200;
|
|
17948
18831
|
var MAX_SUMMARY_BYTES = 4096;
|
|
18832
|
+
var MAX_SEARCHES_DETAIL = 10;
|
|
17949
18833
|
var HOME = process.env.HOME ?? "";
|
|
17950
18834
|
var BASH_INPUT_RE = /^\s*<bash-input>([\s\S]*?)<\/bash-input>/;
|
|
17951
18835
|
var BASH_ECHO_RE = /^\s*<bash-(?:stdout|stderr)>/;
|
|
@@ -18031,6 +18915,7 @@ function buildSummary(lines) {
|
|
|
18031
18915
|
let userCommandsTruncated = false;
|
|
18032
18916
|
let commandsTruncated = false;
|
|
18033
18917
|
let searches = 0;
|
|
18918
|
+
const searchesDetail = [];
|
|
18034
18919
|
let subagents = 0;
|
|
18035
18920
|
let webFetches = 0;
|
|
18036
18921
|
let totalToolCalls = 0;
|
|
@@ -18102,9 +18987,19 @@ function buildSummary(lines) {
|
|
|
18102
18987
|
break;
|
|
18103
18988
|
}
|
|
18104
18989
|
case "Grep":
|
|
18105
|
-
case "Glob":
|
|
18990
|
+
case "Glob": {
|
|
18106
18991
|
searches++;
|
|
18992
|
+
const pattern = typeof input.pattern === "string" ? input.pattern.slice(0, 120) : "";
|
|
18993
|
+
if (pattern && searchesDetail.length < MAX_SEARCHES_DETAIL) {
|
|
18994
|
+
const scopePath = typeof input.path === "string" ? input.path.slice(0, 200) : void 0;
|
|
18995
|
+
searchesDetail.push({
|
|
18996
|
+
tool: toolName,
|
|
18997
|
+
pattern,
|
|
18998
|
+
...scopePath ? { path: scopePath } : {}
|
|
18999
|
+
});
|
|
19000
|
+
}
|
|
18107
19001
|
break;
|
|
19002
|
+
}
|
|
18108
19003
|
case "Agent":
|
|
18109
19004
|
case "Task":
|
|
18110
19005
|
case "Workflow":
|
|
@@ -18139,6 +19034,7 @@ function buildSummary(lines) {
|
|
|
18139
19034
|
...cappedOut(filesCreated, MAX_CREATED_LIST)
|
|
18140
19035
|
],
|
|
18141
19036
|
searches,
|
|
19037
|
+
...searchesDetail.length > 0 ? { searches_detail: searchesDetail } : {},
|
|
18142
19038
|
commands,
|
|
18143
19039
|
...commandsTruncated ? { commands_truncated: true } : {},
|
|
18144
19040
|
user_commands: userCommands,
|
|
@@ -18149,6 +19045,9 @@ function buildSummary(lines) {
|
|
|
18149
19045
|
turn_messages: turnMessages,
|
|
18150
19046
|
turn_duration_ms: turnDurationMs
|
|
18151
19047
|
};
|
|
19048
|
+
if (JSON.stringify(summary).length > MAX_SUMMARY_BYTES) {
|
|
19049
|
+
delete summary.searches_detail;
|
|
19050
|
+
}
|
|
18152
19051
|
if (JSON.stringify(summary).length > MAX_SUMMARY_BYTES) {
|
|
18153
19052
|
summary.commands = [];
|
|
18154
19053
|
summary.commands_truncated = true;
|
|
@@ -18259,13 +19158,13 @@ async function readStopHookStdin() {
|
|
|
18259
19158
|
return empty;
|
|
18260
19159
|
}
|
|
18261
19160
|
}
|
|
18262
|
-
async function bootstrap(
|
|
18263
|
-
const { opts, globals } =
|
|
19161
|
+
async function bootstrap(run2) {
|
|
19162
|
+
const { opts, globals } = run2;
|
|
18264
19163
|
try {
|
|
18265
19164
|
process.chdir(repoRoot());
|
|
18266
19165
|
} catch {
|
|
18267
19166
|
}
|
|
18268
|
-
|
|
19167
|
+
run2.treeFrame = resolveFrame({ command: "", on: [], hookCwd: null }).frame;
|
|
18269
19168
|
const turnId = `t-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
18270
19169
|
let reachability = resolveReachability({
|
|
18271
19170
|
autonomousFlag: process.env.VERITY_AUTONOMOUS === "1" || opts.mode === "autonomous",
|
|
@@ -18279,7 +19178,7 @@ async function bootstrap(run) {
|
|
|
18279
19178
|
const rawSessionId = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
|
|
18280
19179
|
const baselineSessionId = sessionScopeKey(scopeToken, rawSessionId);
|
|
18281
19180
|
if (tokenResult.ok) {
|
|
18282
|
-
|
|
19181
|
+
run2.beacon = {
|
|
18283
19182
|
resolveServiceUrl: async () => {
|
|
18284
19183
|
const u = await resolveServiceUrl(globals.serviceUrl);
|
|
18285
19184
|
return u.ok ? u.data : null;
|
|
@@ -18299,7 +19198,7 @@ async function bootstrap(run) {
|
|
|
18299
19198
|
age_ms: Date.now() - baseline.captured_at
|
|
18300
19199
|
});
|
|
18301
19200
|
}
|
|
18302
|
-
Object.assign(
|
|
19201
|
+
Object.assign(run2, { actionSummary, assistantResponse, baseline, baselineSessionId, reachability, sessionId, stopReason, tokenResult, transcriptPath, turnId });
|
|
18303
19202
|
}
|
|
18304
19203
|
|
|
18305
19204
|
// src/lib/self-scope.ts
|
|
@@ -18452,7 +19351,7 @@ function channelSilence(input) {
|
|
|
18452
19351
|
// src/lib/cli-version.ts
|
|
18453
19352
|
function cliVersion() {
|
|
18454
19353
|
try {
|
|
18455
|
-
return true ? "0.31.
|
|
19354
|
+
return true ? "0.31.1-experimental.68fca47" : "dev";
|
|
18456
19355
|
} catch {
|
|
18457
19356
|
return "dev";
|
|
18458
19357
|
}
|
|
@@ -18492,7 +19391,7 @@ async function sendSkipBeacon(ctx, reason) {
|
|
|
18492
19391
|
}
|
|
18493
19392
|
|
|
18494
19393
|
// src/lib/static-analysis.ts
|
|
18495
|
-
var
|
|
19394
|
+
var import_node_child_process9 = require("node:child_process");
|
|
18496
19395
|
var import_node_fs26 = require("node:fs");
|
|
18497
19396
|
var SEVERITY_ORDER = {
|
|
18498
19397
|
Error: 0,
|
|
@@ -18505,7 +19404,7 @@ var SEVERITY_ORDER = {
|
|
|
18505
19404
|
};
|
|
18506
19405
|
function isCodacyAvailable() {
|
|
18507
19406
|
try {
|
|
18508
|
-
(0,
|
|
19407
|
+
(0, import_node_child_process9.execSync)("which codacy-analysis", { stdio: "pipe" });
|
|
18509
19408
|
return true;
|
|
18510
19409
|
} catch {
|
|
18511
19410
|
return false;
|
|
@@ -18547,7 +19446,7 @@ function runCodacyAnalysis(files) {
|
|
|
18547
19446
|
}
|
|
18548
19447
|
});
|
|
18549
19448
|
if (existingFiles.length === 0) return empty;
|
|
18550
|
-
const proc = (0,
|
|
19449
|
+
const proc = (0, import_node_child_process9.spawnSync)("codacy-analysis", buildAnalyzerArgv(existingFiles), {
|
|
18551
19450
|
encoding: "utf-8",
|
|
18552
19451
|
maxBuffer: 10 * 1024 * 1024
|
|
18553
19452
|
});
|
|
@@ -18558,12 +19457,12 @@ function runCodacyAnalysis(files) {
|
|
|
18558
19457
|
spawnError: proc.error?.message
|
|
18559
19458
|
});
|
|
18560
19459
|
}
|
|
18561
|
-
function interpretAnalyzerRun(
|
|
18562
|
-
const output =
|
|
19460
|
+
function interpretAnalyzerRun(run2) {
|
|
19461
|
+
const output = run2.stdout ?? "";
|
|
18563
19462
|
if (!output.trim()) {
|
|
18564
19463
|
return withFailure(
|
|
18565
|
-
|
|
18566
|
-
|
|
19464
|
+
run2.spawnError ? "spawn_failed" : "no_output",
|
|
19465
|
+
run2.spawnError ?? run2.stderr ?? `exit ${run2.status}`
|
|
18567
19466
|
);
|
|
18568
19467
|
}
|
|
18569
19468
|
let parsed;
|
|
@@ -18702,9 +19601,10 @@ function describeOpenElsewhere(open) {
|
|
|
18702
19601
|
const lines = open.slice(0, 5).map((o) => ` ${o.file}:${o.line} [${o.pattern_id}]`);
|
|
18703
19602
|
const more = open.length > 5 ? `
|
|
18704
19603
|
(+${open.length - 5} more)` : "";
|
|
18705
|
-
return `STILL OPEN ELSEWHERE. ${open.length}
|
|
19604
|
+
return `STILL OPEN ELSEWHERE. ${open.length} finding(s) Verity raised earlier are still on disk in files this run did not review:
|
|
18706
19605
|
${lines.join("\n")}${more}
|
|
18707
|
-
|
|
19606
|
+
They did not fail this run \u2014 this verdict covers the current change only. The tree is not clean.
|
|
19607
|
+
Fix them, or record a disposition: verity waive <pattern-id> --file <path> --reason "\u2026"`;
|
|
18708
19608
|
}
|
|
18709
19609
|
|
|
18710
19610
|
// src/commands/analyze/exit.ts
|
|
@@ -18729,9 +19629,9 @@ function localOnlyAndExit(staticResults) {
|
|
|
18729
19629
|
});
|
|
18730
19630
|
process.exit(0);
|
|
18731
19631
|
}
|
|
18732
|
-
async function passAndExit(
|
|
18733
|
-
|
|
18734
|
-
const sent = await sendSkipBeacon(
|
|
19632
|
+
async function passAndExit(run2, reason, skip, kindOverride) {
|
|
19633
|
+
run2.skipReason = skip;
|
|
19634
|
+
const sent = await sendSkipBeacon(run2.beacon, skip);
|
|
18735
19635
|
logEvent("skip", { reason: skip, beacon: sent });
|
|
18736
19636
|
const POLICY_SKIPS = /* @__PURE__ */ new Set([
|
|
18737
19637
|
"no-analyzable-files",
|
|
@@ -18746,7 +19646,7 @@ async function passAndExit(run, reason, skip, kindOverride) {
|
|
|
18746
19646
|
"no-delta-since-last-review"
|
|
18747
19647
|
]);
|
|
18748
19648
|
const skipKind = kindOverride ?? (POLICY_SKIPS.has(skip) ? "policy" : "capacity");
|
|
18749
|
-
const changed =
|
|
19649
|
+
const changed = run2.changedUniverse;
|
|
18750
19650
|
const { coverage, unaccounted } = reconcileCoverage(changed, {
|
|
18751
19651
|
reviewed: [],
|
|
18752
19652
|
notReviewed: changed.map((path) => ({ path, reason: skip, stage: "pre-flight", kind: skipKind }))
|
|
@@ -18774,10 +19674,10 @@ async function passAndExit(run, reason, skip, kindOverride) {
|
|
|
18774
19674
|
}
|
|
18775
19675
|
|
|
18776
19676
|
// src/commands/analyze/phases/02-scope.ts
|
|
18777
|
-
async function scope(
|
|
18778
|
-
const { assistantResponse } =
|
|
19677
|
+
async function scope(run2) {
|
|
19678
|
+
const { assistantResponse } = run2;
|
|
18779
19679
|
const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
|
|
18780
|
-
|
|
19680
|
+
run2.changedUniverse = allChanged;
|
|
18781
19681
|
const { kept: external } = partitionVerityOwned(allChanged);
|
|
18782
19682
|
const verityIgnore = loadVerityIgnore();
|
|
18783
19683
|
const ignored = partitionIgnored(external, verityIgnore);
|
|
@@ -18802,10 +19702,10 @@ async function scope(run) {
|
|
|
18802
19702
|
const securityFiles = filterSecurity(inScope);
|
|
18803
19703
|
const noFilesChanged = analyzable.length === 0 && reviewable.length === 0 && securityFiles.length === 0;
|
|
18804
19704
|
if (noFilesChanged && !assistantResponse) {
|
|
18805
|
-
await passAndExit(
|
|
19705
|
+
await passAndExit(run2, "No analyzable files changed", "no-analyzable-files");
|
|
18806
19706
|
}
|
|
18807
19707
|
const allForReview = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable]));
|
|
18808
|
-
Object.assign(
|
|
19708
|
+
Object.assign(run2, { allChanged, allForReview, analyzable, hasRecentCommitFiles, noFilesChanged, reviewable, securityFiles, verityIgnored: ignored });
|
|
18809
19709
|
}
|
|
18810
19710
|
|
|
18811
19711
|
// src/lib/specs.ts
|
|
@@ -18951,8 +19851,8 @@ function discoverGuardDocs(rangeFiles2) {
|
|
|
18951
19851
|
}
|
|
18952
19852
|
|
|
18953
19853
|
// src/commands/analyze/phases/03-intent-inputs.ts
|
|
18954
|
-
async function intentInputs(
|
|
18955
|
-
const { actionSummary, allForReview, assistantResponse, baseline, baselineSessionId } =
|
|
19854
|
+
async function intentInputs(run2) {
|
|
19855
|
+
const { actionSummary, allForReview, assistantResponse, baseline, baselineSessionId } = run2;
|
|
18956
19856
|
if (isCommandOnlyTurn({
|
|
18957
19857
|
userCommands: actionSummary?.user_commands,
|
|
18958
19858
|
userCommandsTruncated: actionSummary?.user_commands_truncated,
|
|
@@ -18960,11 +19860,11 @@ async function intentInputs(run) {
|
|
|
18960
19860
|
agentToolCalls: actionSummary?.total_tool_calls ?? 0,
|
|
18961
19861
|
authorshipIsObservable: !!actionSummary && actionSummary.transcript_windowed !== "orphaned"
|
|
18962
19862
|
})) {
|
|
18963
|
-
await passAndExit(
|
|
19863
|
+
await passAndExit(run2, "User command only \u2014 skipping analysis", "command-only-turn");
|
|
18964
19864
|
}
|
|
18965
19865
|
{
|
|
18966
19866
|
const ignoreKeys = ignoreStateKeys(
|
|
18967
|
-
|
|
19867
|
+
run2.tokenResult.ok ? run2.tokenResult.data.token : void 0,
|
|
18968
19868
|
null
|
|
18969
19869
|
);
|
|
18970
19870
|
const found = resolveIgnoreState([baselineSessionId, ...ignoreKeys]);
|
|
@@ -18988,7 +19888,7 @@ async function intentInputs(run) {
|
|
|
18988
19888
|
if (declaration.scope === "turn" && found) clearActiveDeclaration(found.key);
|
|
18989
19889
|
logEvent("ignore_honoured", { scope: declaration.scope, origin: declaration.origin });
|
|
18990
19890
|
await passAndExit(
|
|
18991
|
-
|
|
19891
|
+
run2,
|
|
18992
19892
|
`skipping this turn \u2014 declared housekeeping ("${declaration.reason}")`,
|
|
18993
19893
|
"declared-ignore"
|
|
18994
19894
|
);
|
|
@@ -19002,7 +19902,7 @@ async function intentInputs(run) {
|
|
|
19002
19902
|
const notice = `Verity: the ignore declared for this window ("${declaration.reason}") was voided \u2014 ${outcome.why}. Reviewing normally.`;
|
|
19003
19903
|
process.stderr.write(`${notice}
|
|
19004
19904
|
`);
|
|
19005
|
-
|
|
19905
|
+
run2.voidedIgnoreNotice = notice;
|
|
19006
19906
|
}
|
|
19007
19907
|
}
|
|
19008
19908
|
}
|
|
@@ -19024,34 +19924,33 @@ async function intentInputs(run) {
|
|
|
19024
19924
|
const adopted = absorbIntoBaseline(setupAuthored, baselineSessionId);
|
|
19025
19925
|
logEvent("baseline_absorbed", { skip: "verity-command", offered: setupAuthored.length, adopted });
|
|
19026
19926
|
}
|
|
19027
|
-
await passAndExit(
|
|
19927
|
+
await passAndExit(run2, "Verity command \u2014 skipping analysis", "verity-command");
|
|
19028
19928
|
}
|
|
19029
19929
|
if (shouldSkipForBareAck({ prompt: latestPrompt, turnAuthoredCode, canSeeTurnAuthorship })) {
|
|
19030
|
-
await passAndExit(
|
|
19930
|
+
await passAndExit(run2, "Bare acknowledgment \u2014 skipping analysis", "bare-acknowledgment");
|
|
19031
19931
|
}
|
|
19032
19932
|
if (isReflectionQuestion(assistantResponse) && !turnAuthoredCode && canSeeTurnAuthorship) {
|
|
19033
|
-
await passAndExit(
|
|
19933
|
+
await passAndExit(run2, "Reflection-prompt turn \u2014 skipping analysis", "reflection-prompt");
|
|
19034
19934
|
}
|
|
19035
|
-
Object.assign(
|
|
19935
|
+
Object.assign(run2, { authorshipIsObservable, conversation, earlyFold, plans, specs, turnAuthoredCode });
|
|
19036
19936
|
}
|
|
19037
19937
|
|
|
19038
19938
|
// src/commands/analyze/phases/04-connect.ts
|
|
19039
|
-
async function connect(
|
|
19040
|
-
const { opts, globals } =
|
|
19041
|
-
const { analyzable, baseline, securityFiles, tokenResult } =
|
|
19939
|
+
async function connect(run2) {
|
|
19940
|
+
const { opts, globals } = run2;
|
|
19941
|
+
const { analyzable, baseline, securityFiles, tokenResult } = run2;
|
|
19042
19942
|
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
19043
19943
|
if (!tokenResult.ok || !urlResult.ok) {
|
|
19044
19944
|
localOnlyAndExit(runLocalStatic(analyzable, securityFiles, baseline, !!opts.skipStatic));
|
|
19045
19945
|
}
|
|
19046
|
-
Object.assign(
|
|
19946
|
+
Object.assign(run2, { urlResult, serviceUrl: urlResult.data, token: tokenResult.data.token });
|
|
19047
19947
|
}
|
|
19048
19948
|
|
|
19049
19949
|
// src/commands/analyze/phases/05-mode.ts
|
|
19050
|
-
async function mode(
|
|
19051
|
-
const { opts, globals } =
|
|
19052
|
-
const { actionSummary, allForReview, assistantResponse, baseline, conversation, memory, noFilesChanged, serviceUrl, sessionId, token, turnAuthoredCode } =
|
|
19950
|
+
async function mode(run2) {
|
|
19951
|
+
const { opts, globals } = run2;
|
|
19952
|
+
const { actionSummary, allForReview, assistantResponse, baseline, conversation, memory, noFilesChanged, serviceUrl, sessionId, token, turnAuthoredCode } = run2;
|
|
19053
19953
|
const sessionIdForMemory = sessionId || process.env.CLAUDE_SESSION_ID || "";
|
|
19054
|
-
let contextFilePaths = [];
|
|
19055
19954
|
let predictedMode;
|
|
19056
19955
|
try {
|
|
19057
19956
|
const memoryPath2 = sessionIdForMemory ? `/memory?session_id=${encodeURIComponent(sessionIdForMemory)}` : "/memory";
|
|
@@ -19065,9 +19964,6 @@ async function mode(run) {
|
|
|
19065
19964
|
cmd: "analyze_context"
|
|
19066
19965
|
});
|
|
19067
19966
|
if (memoryResult.ok) {
|
|
19068
|
-
if (Array.isArray(memoryResult.data.context_files)) {
|
|
19069
|
-
contextFilePaths = memoryResult.data.context_files;
|
|
19070
|
-
}
|
|
19071
19967
|
const rawMode = memoryResult.data.predicted_mode;
|
|
19072
19968
|
if (rawMode && ["standard", "plan", "debug", "skip"].includes(rawMode)) {
|
|
19073
19969
|
predictedMode = rawMode;
|
|
@@ -19090,7 +19986,7 @@ async function mode(run) {
|
|
|
19090
19986
|
);
|
|
19091
19987
|
}
|
|
19092
19988
|
const investigated = didAgentInvestigate(actionSummary);
|
|
19093
|
-
|
|
19989
|
+
run2.modeDecision = {
|
|
19094
19990
|
predicted: predictedMode ?? null,
|
|
19095
19991
|
resolved: analysisMode,
|
|
19096
19992
|
authored: turnAuthoredCode,
|
|
@@ -19110,13 +20006,13 @@ async function mode(run) {
|
|
|
19110
20006
|
});
|
|
19111
20007
|
if (analysisMode === "skip") {
|
|
19112
20008
|
await passAndExit(
|
|
19113
|
-
|
|
20009
|
+
run2,
|
|
19114
20010
|
"Skip mode \u2014 no code work to analyze",
|
|
19115
20011
|
"skip-mode",
|
|
19116
20012
|
turnAuthoredCode ? "capacity" : void 0
|
|
19117
20013
|
);
|
|
19118
20014
|
}
|
|
19119
|
-
Object.assign(
|
|
20015
|
+
Object.assign(run2, { analysisMode, sessionAuthoredCode, sessionIdForMemory });
|
|
19120
20016
|
}
|
|
19121
20017
|
|
|
19122
20018
|
// src/lib/fold.ts
|
|
@@ -19576,12 +20472,12 @@ function checkConservation(changedFiles, result, repoRoot2) {
|
|
|
19576
20472
|
}
|
|
19577
20473
|
|
|
19578
20474
|
// src/commands/analyze/phases/06-evidence.ts
|
|
19579
|
-
async function evidence(
|
|
19580
|
-
const { opts } =
|
|
19581
|
-
const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath, turnAuthoredCode } =
|
|
19582
|
-
let { analysisMode, earlyFold } =
|
|
20475
|
+
async function evidence(run2) {
|
|
20476
|
+
const { opts } = run2;
|
|
20477
|
+
const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath, turnAuthoredCode } = run2;
|
|
20478
|
+
let { analysisMode, earlyFold } = run2;
|
|
19583
20479
|
const recordFlip = (stage) => {
|
|
19584
|
-
if (
|
|
20480
|
+
if (run2.modeDecision) run2.modeDecision = { ...run2.modeDecision, resolved: "plan", flip: stage };
|
|
19585
20481
|
logEvent("mode_flipped", { stage, to: "plan" });
|
|
19586
20482
|
};
|
|
19587
20483
|
const planWorthy = !!assistantResponse && !turnAuthoredCode;
|
|
@@ -19608,7 +20504,7 @@ async function evidence(run) {
|
|
|
19608
20504
|
analysisMode = "plan";
|
|
19609
20505
|
recordFlip("debounce");
|
|
19610
20506
|
} else {
|
|
19611
|
-
await passAndExit(
|
|
20507
|
+
await passAndExit(run2, debounceSkip, "debounce");
|
|
19612
20508
|
}
|
|
19613
20509
|
}
|
|
19614
20510
|
if (analysisMode !== "plan") {
|
|
@@ -19619,7 +20515,7 @@ async function evidence(run) {
|
|
|
19619
20515
|
analysisMode = "plan";
|
|
19620
20516
|
recordFlip("mtime");
|
|
19621
20517
|
} else {
|
|
19622
|
-
await passAndExit(
|
|
20518
|
+
await passAndExit(run2, mtimeSkip, "no-delta-since-last-review");
|
|
19623
20519
|
}
|
|
19624
20520
|
}
|
|
19625
20521
|
}
|
|
@@ -19632,7 +20528,7 @@ async function evidence(run) {
|
|
|
19632
20528
|
analysisMode = "plan";
|
|
19633
20529
|
recordFlip("content-hash");
|
|
19634
20530
|
} else {
|
|
19635
|
-
await passAndExit(
|
|
20531
|
+
await passAndExit(run2, hashResult.skip, "no-delta-since-last-review");
|
|
19636
20532
|
}
|
|
19637
20533
|
}
|
|
19638
20534
|
contentHash = hashResult.hash;
|
|
@@ -19640,7 +20536,7 @@ async function evidence(run) {
|
|
|
19640
20536
|
const scoped = scopeToAuthored(allForReview, actionSummary);
|
|
19641
20537
|
const canTrustNoneAuthored = scoped.signal === "none-authored" && authorshipIsObservable;
|
|
19642
20538
|
if (canTrustNoneAuthored && !hasNonEditAuthorship(actionSummary, sessionAuthoredCode)) {
|
|
19643
|
-
await passAndExit(
|
|
20539
|
+
await passAndExit(run2, "No agent-authored code this turn \u2014 working-tree changes were not authored by this session", "zero-increment");
|
|
19644
20540
|
}
|
|
19645
20541
|
if (scoped.signal === "none-authored" && !authorshipIsObservable) {
|
|
19646
20542
|
logEvent("none_authored_unverifiable", {
|
|
@@ -19697,7 +20593,7 @@ async function evidence(run) {
|
|
|
19697
20593
|
recordFlip("empty-after-scoping");
|
|
19698
20594
|
} else {
|
|
19699
20595
|
await passAndExit(
|
|
19700
|
-
|
|
20596
|
+
run2,
|
|
19701
20597
|
"No files within size limits to analyze",
|
|
19702
20598
|
"size-limit",
|
|
19703
20599
|
codeDelta.excluded.length > 0 ? "capacity" : "policy"
|
|
@@ -19722,7 +20618,7 @@ async function evidence(run) {
|
|
|
19722
20618
|
currentCommit = getCurrentCommit();
|
|
19723
20619
|
iteration = readIteration(currentCommit);
|
|
19724
20620
|
}
|
|
19725
|
-
Object.assign(
|
|
20621
|
+
Object.assign(run2, { analysisMode, codeDelta, contentHash, currentCommit, earlyFold, iteration, snapshotResult, staticResults });
|
|
19726
20622
|
}
|
|
19727
20623
|
|
|
19728
20624
|
// src/lib/cache-cleanup.ts
|
|
@@ -19754,15 +20650,52 @@ function pruneStaleCache() {
|
|
|
19754
20650
|
|
|
19755
20651
|
// src/lib/context-files.ts
|
|
19756
20652
|
var import_node_fs30 = require("node:fs");
|
|
20653
|
+
var import_node_os5 = require("node:os");
|
|
19757
20654
|
var MAX_CONTEXT_FILES = 10;
|
|
19758
20655
|
var MAX_CONTEXT_FILE_BYTES = 10240;
|
|
19759
|
-
var MAX_CONTEXT_TOTAL_BYTES =
|
|
19760
|
-
function
|
|
20656
|
+
var MAX_CONTEXT_TOTAL_BYTES = 24576;
|
|
20657
|
+
function readSetContextPaths(summary, deltaFiles) {
|
|
20658
|
+
const reads = summary?.files_read ?? [];
|
|
20659
|
+
if (reads.length === 0) return [];
|
|
20660
|
+
const root = process.cwd().replace(/\/+$/, "");
|
|
20661
|
+
const home = (0, import_node_os5.homedir)();
|
|
20662
|
+
const toRepoRelative2 = (p) => {
|
|
20663
|
+
if (!p) return null;
|
|
20664
|
+
let abs;
|
|
20665
|
+
if (p.startsWith("/")) {
|
|
20666
|
+
abs = p;
|
|
20667
|
+
} else {
|
|
20668
|
+
const rebuilt = `${home}/${p}`;
|
|
20669
|
+
abs = rebuilt.startsWith(`${root}/`) ? rebuilt : `${root}/${p}`;
|
|
20670
|
+
}
|
|
20671
|
+
if (!abs.startsWith(`${root}/`)) return null;
|
|
20672
|
+
return abs.slice(root.length + 1);
|
|
20673
|
+
};
|
|
20674
|
+
const authored = /* @__PURE__ */ new Set();
|
|
20675
|
+
for (const p of [...summary?.files_edited ?? [], ...summary?.files_created ?? []]) {
|
|
20676
|
+
const rel = toRepoRelative2(p);
|
|
20677
|
+
if (rel) authored.add(rel);
|
|
20678
|
+
}
|
|
20679
|
+
for (const f of deltaFiles) authored.add(f.path);
|
|
20680
|
+
const out = [];
|
|
20681
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20682
|
+
for (const p of reads) {
|
|
20683
|
+
const rel = toRepoRelative2(p);
|
|
20684
|
+
if (!rel || seen.has(rel) || authored.has(rel)) continue;
|
|
20685
|
+
seen.add(rel);
|
|
20686
|
+
const ext = rel.split(".").pop()?.toLowerCase() ?? "";
|
|
20687
|
+
if (!ANALYZABLE_EXTENSIONS.has(ext) && !REVIEWABLE_EXTENSIONS.has(ext)) continue;
|
|
20688
|
+
out.push(rel);
|
|
20689
|
+
}
|
|
20690
|
+
return out;
|
|
20691
|
+
}
|
|
20692
|
+
function gatherContextFiles(contextPaths, deltaFiles, opts) {
|
|
20693
|
+
const fileCap = Math.max(0, Math.min(MAX_CONTEXT_FILES, opts?.maxFiles ?? MAX_CONTEXT_FILES));
|
|
19761
20694
|
const deltaPaths = new Set(deltaFiles.map((f) => f.path));
|
|
19762
20695
|
const result = [];
|
|
19763
20696
|
let totalBytes = 0;
|
|
19764
20697
|
for (const filePath of contextPaths) {
|
|
19765
|
-
if (result.length >=
|
|
20698
|
+
if (result.length >= fileCap) break;
|
|
19766
20699
|
if (deltaPaths.has(filePath)) continue;
|
|
19767
20700
|
if (isVerityOwnedPath(filePath)) {
|
|
19768
20701
|
logEvent("context_file_skipped", { path: filePath, reason: "verity_owned" });
|
|
@@ -19819,10 +20752,15 @@ function gatherContextFiles(contextPaths, deltaFiles) {
|
|
|
19819
20752
|
}
|
|
19820
20753
|
|
|
19821
20754
|
// src/commands/analyze/phases/07-context-files.ts
|
|
19822
|
-
async function contextFiles(
|
|
19823
|
-
const { codeDelta
|
|
19824
|
-
const
|
|
19825
|
-
const
|
|
20755
|
+
async function contextFiles(run2) {
|
|
20756
|
+
const { codeDelta } = run2;
|
|
20757
|
+
const readSet = readSetContextPaths(run2.actionSummary, codeDelta.files);
|
|
20758
|
+
const { kept: externalContext } = partitionVerityOwned(readSet);
|
|
20759
|
+
const ig = loadVerityIgnore();
|
|
20760
|
+
const unfenced = run2.verityIgnored.suspended ? externalContext : externalContext.filter((p) => !isIgnored(ig, p));
|
|
20761
|
+
const contextFiles2 = gatherContextFiles(unfenced, codeDelta.files, {
|
|
20762
|
+
maxFiles: MAX_FILES - codeDelta.files.length
|
|
20763
|
+
});
|
|
19826
20764
|
for (const f of codeDelta.files) {
|
|
19827
20765
|
f.role = "delta";
|
|
19828
20766
|
}
|
|
@@ -19834,6 +20772,30 @@ async function contextFiles(run) {
|
|
|
19834
20772
|
});
|
|
19835
20773
|
}
|
|
19836
20774
|
|
|
20775
|
+
// src/commands/analyze/phases/07b-repo-context.ts
|
|
20776
|
+
async function repoContext(run2) {
|
|
20777
|
+
const { codeDelta, snapshotResult } = run2;
|
|
20778
|
+
const deltaFiles = codeDelta.files.filter((f) => f.role !== "context");
|
|
20779
|
+
const sentPaths = new Set(codeDelta.files.map((f) => f.path));
|
|
20780
|
+
const ig = loadVerityIgnore();
|
|
20781
|
+
const isExcluded = (p) => isVerityOwnedPath(p) || !run2.verityIgnored.suspended && isIgnored(ig, p);
|
|
20782
|
+
run2.repoContext = buildRepoContext({
|
|
20783
|
+
deltaFiles,
|
|
20784
|
+
diffs: snapshotResult.diffs,
|
|
20785
|
+
sentPaths,
|
|
20786
|
+
isExcluded
|
|
20787
|
+
});
|
|
20788
|
+
logEvent("repo_context", {
|
|
20789
|
+
state: run2.repoContext.state,
|
|
20790
|
+
reason: run2.repoContext.reason ?? null,
|
|
20791
|
+
symbols: run2.repoContext.symbols?.length ?? 0,
|
|
20792
|
+
dropped: run2.repoContext.dropped_symbols?.length ?? 0,
|
|
20793
|
+
callers: run2.repoContext.callers?.length ?? 0,
|
|
20794
|
+
tests: run2.repoContext.tests?.length ?? 0,
|
|
20795
|
+
elapsed_ms: run2.repoContext.elapsed_ms ?? null
|
|
20796
|
+
});
|
|
20797
|
+
}
|
|
20798
|
+
|
|
19837
20799
|
// src/lib/seed-runner.ts
|
|
19838
20800
|
var import_promises11 = require("node:fs/promises");
|
|
19839
20801
|
var import_node_fs31 = require("node:fs");
|
|
@@ -20177,9 +21139,9 @@ async function runSeed(opts) {
|
|
|
20177
21139
|
// src/commands/analyze/phases/08-memory-manifest.ts
|
|
20178
21140
|
var import_node_fs32 = require("node:fs");
|
|
20179
21141
|
var import_node_path24 = require("node:path");
|
|
20180
|
-
async function memoryManifest(
|
|
20181
|
-
const { globals } =
|
|
20182
|
-
const { serviceUrl, token } =
|
|
21142
|
+
async function memoryManifest(run2) {
|
|
21143
|
+
const { globals } = run2;
|
|
21144
|
+
const { serviceUrl, token } = run2;
|
|
20183
21145
|
let memoryManifest2;
|
|
20184
21146
|
let deletedNodePaths = [];
|
|
20185
21147
|
let editedUploads = [];
|
|
@@ -20231,12 +21193,12 @@ async function memoryManifest(run) {
|
|
|
20231
21193
|
editedUploads = await computeEditedNodeUploads();
|
|
20232
21194
|
} catch {
|
|
20233
21195
|
}
|
|
20234
|
-
Object.assign(
|
|
21196
|
+
Object.assign(run2, { autoSeedNotice, deletedNodePaths, editedUploads, memoryManifest: memoryManifest2 });
|
|
20235
21197
|
}
|
|
20236
21198
|
|
|
20237
21199
|
// src/commands/analyze/phases/09-fold-transcript.ts
|
|
20238
|
-
async function foldTranscript(
|
|
20239
|
-
const { allForReview, earlyFold, transcriptPath } =
|
|
21200
|
+
async function foldTranscript(run2) {
|
|
21201
|
+
const { allForReview, earlyFold, transcriptPath } = run2;
|
|
20240
21202
|
let foldResult = null;
|
|
20241
21203
|
let foldConservation = null;
|
|
20242
21204
|
if (transcriptPath) {
|
|
@@ -20253,7 +21215,7 @@ async function foldTranscript(run) {
|
|
|
20253
21215
|
foldResult = null;
|
|
20254
21216
|
}
|
|
20255
21217
|
}
|
|
20256
|
-
Object.assign(
|
|
21218
|
+
Object.assign(run2, { foldConservation, foldResult });
|
|
20257
21219
|
}
|
|
20258
21220
|
|
|
20259
21221
|
// src/lib/increment.ts
|
|
@@ -20305,10 +21267,10 @@ function computeIncrement(reviewedPaths, hashOf, priorAuthored) {
|
|
|
20305
21267
|
|
|
20306
21268
|
// src/commands/analyze/phases/10-working-memory.ts
|
|
20307
21269
|
var import_node_path25 = require("node:path");
|
|
20308
|
-
async function workingMemory(
|
|
20309
|
-
const { opts } =
|
|
20310
|
-
const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } =
|
|
20311
|
-
let { reachability } =
|
|
21270
|
+
async function workingMemory(run2) {
|
|
21271
|
+
const { opts } = run2;
|
|
21272
|
+
const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } = run2;
|
|
21273
|
+
let { reachability } = run2;
|
|
20312
21274
|
const memorySession = sessionDossier(token, sessionId ?? process.env.CLAUDE_SESSION_ID ?? null);
|
|
20313
21275
|
let memory = null;
|
|
20314
21276
|
let incrementReport = null;
|
|
@@ -20394,7 +21356,7 @@ async function workingMemory(run) {
|
|
|
20394
21356
|
hasUserPrompt: (conversation?.prompts?.length ?? 0) > 0,
|
|
20395
21357
|
isTTY: process.stdout.isTTY === true
|
|
20396
21358
|
});
|
|
20397
|
-
Object.assign(
|
|
21359
|
+
Object.assign(run2, { incrementReport, memory, memorySession, reachability });
|
|
20398
21360
|
}
|
|
20399
21361
|
|
|
20400
21362
|
// src/lib/note-budget.ts
|
|
@@ -20467,7 +21429,7 @@ function isExplicitlyAutonomous(env = process.env) {
|
|
|
20467
21429
|
}
|
|
20468
21430
|
|
|
20469
21431
|
// src/lib/task-context.ts
|
|
20470
|
-
var
|
|
21432
|
+
var import_node_child_process10 = require("node:child_process");
|
|
20471
21433
|
var CLOSING_RE = /\b(close[sd]?|fix(?:e[sd])?|resolve[sd]?)\b[\s:]*#(\d+)/i;
|
|
20472
21434
|
var BRANCH_RE = /(?:^|[/_-])(?:issue|gh|fix)[-_/]?(\d+)\b/i;
|
|
20473
21435
|
function parseLinkedIssue(sources) {
|
|
@@ -20483,7 +21445,7 @@ function parseLinkedIssue(sources) {
|
|
|
20483
21445
|
}
|
|
20484
21446
|
function safeExec(cmd, timeout) {
|
|
20485
21447
|
try {
|
|
20486
|
-
return (0,
|
|
21448
|
+
return (0, import_node_child_process10.execSync)(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout }).trim();
|
|
20487
21449
|
} catch {
|
|
20488
21450
|
return "";
|
|
20489
21451
|
}
|
|
@@ -20513,8 +21475,8 @@ function resolveTaskContext(opts) {
|
|
|
20513
21475
|
// src/commands/analyze/phases/11-build-request.ts
|
|
20514
21476
|
var MAX_ASSISTANT_RESPONSE_CHARS_PLAN = 32768;
|
|
20515
21477
|
var MAX_ASSISTANT_RESPONSE_CHARS_DEFAULT = 8e3;
|
|
20516
|
-
async function buildRequest(
|
|
20517
|
-
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 } =
|
|
21478
|
+
async function buildRequest(run2) {
|
|
21479
|
+
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;
|
|
20518
21480
|
const excludedByReason = {};
|
|
20519
21481
|
for (const e of codeDelta.excluded ?? []) {
|
|
20520
21482
|
excludedByReason[e.reason] = (excludedByReason[e.reason] ?? 0) + 1;
|
|
@@ -20544,15 +21506,15 @@ async function buildRequest(run) {
|
|
|
20544
21506
|
// the state, so the number is one turn lagged by construction. The
|
|
20545
21507
|
// degenerate win for the budget is a dead channel that looks like clean
|
|
20546
21508
|
// code; this is what makes "did delivery rate collapse" a query.
|
|
20547
|
-
advisory_delivered_prior: readAdvisoryEpisode(
|
|
21509
|
+
advisory_delivered_prior: readAdvisoryEpisode(run2.baselineSessionId)?.delivered ?? 0,
|
|
20548
21510
|
// `.verityignore` — see CoverageTelemetry.verityignore for why the SHARE is
|
|
20549
21511
|
// the number that matters and why no paths travel with it.
|
|
20550
21512
|
verityignore: {
|
|
20551
|
-
rules:
|
|
20552
|
-
excluded:
|
|
20553
|
-
share: ignoreShare(
|
|
20554
|
-
security_excluded:
|
|
20555
|
-
suspended:
|
|
21513
|
+
rules: run2.verityIgnored.rules,
|
|
21514
|
+
excluded: run2.verityIgnored.ignored.length,
|
|
21515
|
+
share: ignoreShare(run2.verityIgnored.kept.length, run2.verityIgnored.ignored.length),
|
|
21516
|
+
security_excluded: run2.verityIgnored.securityExcluded.length,
|
|
21517
|
+
suspended: run2.verityIgnored.suspended
|
|
20556
21518
|
}
|
|
20557
21519
|
};
|
|
20558
21520
|
const requestBody = {
|
|
@@ -20690,6 +21652,9 @@ async function buildRequest(run) {
|
|
|
20690
21652
|
if (snapshotResult.has_snapshots && snapshotResult.diffs.length > 0) {
|
|
20691
21653
|
requestBody.snapshot_diffs = snapshotResult.diffs;
|
|
20692
21654
|
}
|
|
21655
|
+
if (run2.repoContext) {
|
|
21656
|
+
requestBody.repo_context = run2.repoContext;
|
|
21657
|
+
}
|
|
20693
21658
|
const noHumanPrompt = (conversation?.prompts?.length ?? 0) === 0;
|
|
20694
21659
|
const w4Task = noHumanPrompt && isExplicitlyAutonomous() ? resolveTaskContext() : null;
|
|
20695
21660
|
const planApprovalActive = foldResult?.planApproval?.activeSinceLastPrompt === true;
|
|
@@ -20747,7 +21712,7 @@ async function buildRequest(run) {
|
|
|
20747
21712
|
}
|
|
20748
21713
|
requestBody.intent_context = intentContext;
|
|
20749
21714
|
}
|
|
20750
|
-
Object.assign(
|
|
21715
|
+
Object.assign(run2, { requestBody });
|
|
20751
21716
|
}
|
|
20752
21717
|
|
|
20753
21718
|
// src/lib/offline.ts
|
|
@@ -20808,9 +21773,9 @@ function shouldWarmRetryAnalyze(result) {
|
|
|
20808
21773
|
}
|
|
20809
21774
|
|
|
20810
21775
|
// src/commands/analyze/phases/12-transmit.ts
|
|
20811
|
-
async function transmit(
|
|
20812
|
-
const { globals } =
|
|
20813
|
-
const { codeDelta, requestBody, serviceUrl, staticResults, token } =
|
|
21776
|
+
async function transmit(run2) {
|
|
21777
|
+
const { globals } = run2;
|
|
21778
|
+
const { codeDelta, requestBody, serviceUrl, staticResults, token } = run2;
|
|
20814
21779
|
const ANALYZE_TIMEOUT_MS = 1e5;
|
|
20815
21780
|
let result = await analyzeRequest({
|
|
20816
21781
|
serviceUrl,
|
|
@@ -20873,15 +21838,34 @@ async function transmit(run) {
|
|
|
20873
21838
|
}
|
|
20874
21839
|
const response = result.data;
|
|
20875
21840
|
const decision = response.gate_decision ?? "(unrecognised)";
|
|
20876
|
-
Object.assign(
|
|
21841
|
+
Object.assign(run2, { decision, response });
|
|
20877
21842
|
}
|
|
20878
21843
|
|
|
20879
21844
|
// src/commands/analyze/phases/13-reconcile.ts
|
|
20880
21845
|
var import_node_fs35 = require("node:fs");
|
|
20881
21846
|
var import_node_path26 = require("node:path");
|
|
20882
|
-
async function reconcile(
|
|
20883
|
-
const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } =
|
|
21847
|
+
async function reconcile(run2) {
|
|
21848
|
+
const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run2;
|
|
20884
21849
|
const sentPaths = codeDelta.files.map((f) => f.path);
|
|
21850
|
+
if (memorySession) {
|
|
21851
|
+
try {
|
|
21852
|
+
const settledSites = response.metadata?.settled_sites;
|
|
21853
|
+
if (Array.isArray(settledSites)) {
|
|
21854
|
+
for (const site of settledSites) {
|
|
21855
|
+
const siteRecord = site;
|
|
21856
|
+
const file = typeof siteRecord?.file === "string" ? siteRecord.file : null;
|
|
21857
|
+
const patternId = typeof siteRecord?.pattern_id === "string" ? siteRecord.pattern_id : null;
|
|
21858
|
+
if (!file || !patternId) continue;
|
|
21859
|
+
appendEvent(memorySession.d, {
|
|
21860
|
+
k: "outcome",
|
|
21861
|
+
anchor_key: statementAnchorKey(file, patternId),
|
|
21862
|
+
outcome: "answered"
|
|
21863
|
+
});
|
|
21864
|
+
}
|
|
21865
|
+
}
|
|
21866
|
+
} catch {
|
|
21867
|
+
}
|
|
21868
|
+
}
|
|
20885
21869
|
let openElsewhere = [];
|
|
20886
21870
|
if (memorySession) {
|
|
20887
21871
|
try {
|
|
@@ -20962,7 +21946,7 @@ async function reconcile(run) {
|
|
|
20962
21946
|
// below is the signal that replaces the noise.
|
|
20963
21947
|
//
|
|
20964
21948
|
// Taken from the run, not recomputed — see context.ts `verityIgnored`.
|
|
20965
|
-
...
|
|
21949
|
+
...run2.verityIgnored.ignored.map((path) => ({
|
|
20966
21950
|
path,
|
|
20967
21951
|
reason: "verityignore",
|
|
20968
21952
|
stage: "verityignore",
|
|
@@ -21082,11 +22066,11 @@ async function reconcile(run) {
|
|
|
21082
22066
|
`
|
|
21083
22067
|
);
|
|
21084
22068
|
}
|
|
21085
|
-
Object.assign(
|
|
22069
|
+
Object.assign(run2, { intentRepeatCount, openElsewhere, priorPendingFingerprints, reviewCoverage, sentPaths, silenced, watermarkHash, watermarkIsPartial });
|
|
21086
22070
|
}
|
|
21087
22071
|
|
|
21088
22072
|
// src/lib/emit.ts
|
|
21089
|
-
var
|
|
22073
|
+
var YELLOW3 = "\x1B[33m";
|
|
21090
22074
|
var NC2 = "\x1B[0m";
|
|
21091
22075
|
function emitVerdict(input) {
|
|
21092
22076
|
const exit = input.exit ?? ((code) => process.exit(code));
|
|
@@ -21097,7 +22081,7 @@ function emitVerdict(input) {
|
|
|
21097
22081
|
const note = [describeCoverage(coverage), describeOpenElsewhere(openElsewhere)].filter(Boolean).join("\n\n") || null;
|
|
21098
22082
|
if (unaccounted.length > 0) {
|
|
21099
22083
|
process.stderr.write(
|
|
21100
|
-
`${
|
|
22084
|
+
`${YELLOW3}Verity: ${unaccounted.length} changed file(s) could not be attributed to any review stage \u2014 counted as unreviewed.${NC2}
|
|
21101
22085
|
`
|
|
21102
22086
|
);
|
|
21103
22087
|
}
|
|
@@ -21109,7 +22093,7 @@ ${input.agentContext}
|
|
|
21109
22093
|
`);
|
|
21110
22094
|
}
|
|
21111
22095
|
if (note && !input.silenced) process.stderr.write(`
|
|
21112
|
-
${
|
|
22096
|
+
${YELLOW3}${note}${NC2}
|
|
21113
22097
|
`);
|
|
21114
22098
|
return exit(2);
|
|
21115
22099
|
}
|
|
@@ -21195,10 +22179,10 @@ function screenRemediation(fix, findingFile) {
|
|
|
21195
22179
|
function agentContextFor(response, intentRepeat = 0, priorPendingFingerprints = []) {
|
|
21196
22180
|
return buildAgentContext(channelInputFrom(response, intentRepeat, priorPendingFingerprints));
|
|
21197
22181
|
}
|
|
21198
|
-
async function render(
|
|
21199
|
-
const { opts, globals } =
|
|
21200
|
-
const { actionSummary, assistantResponse, autoSeedNotice, voidedIgnoreNotice, baselineSessionId, codeDelta, contentHash, conversation, currentCommit, decision, intentRepeatCount, memory, openElsewhere, priorPendingFingerprints, response, reviewCoverage, serviceUrl, sessionIdForMemory, silenced, token, watermarkHash, watermarkIsPartial } =
|
|
21201
|
-
let { iteration } =
|
|
22182
|
+
async function render(run2) {
|
|
22183
|
+
const { opts, globals } = run2;
|
|
22184
|
+
const { actionSummary, assistantResponse, autoSeedNotice, voidedIgnoreNotice, baselineSessionId, codeDelta, contentHash, conversation, currentCommit, decision, intentRepeatCount, memory, openElsewhere, priorPendingFingerprints, response, reviewCoverage, serviceUrl, sessionIdForMemory, silenced, token, watermarkHash, watermarkIsPartial } = run2;
|
|
22185
|
+
let { iteration } = run2;
|
|
21202
22186
|
const metadata = response.metadata ?? {};
|
|
21203
22187
|
const intentAmbiguity = metadata.intent_ambiguity;
|
|
21204
22188
|
if (intentAmbiguity != null && intentAmbiguity > 5) {
|
|
@@ -21305,7 +22289,7 @@ async function render(run) {
|
|
|
21305
22289
|
const blocks = prior.blocks + 1;
|
|
21306
22290
|
const decisionNow = mayBlock({
|
|
21307
22291
|
reviewedFileCount: codeDelta.files.length,
|
|
21308
|
-
staticFindingCount:
|
|
22292
|
+
staticFindingCount: run2.staticResults?.findings?.length ?? 0,
|
|
21309
22293
|
cycleCutFired: silenced !== null,
|
|
21310
22294
|
attempts,
|
|
21311
22295
|
blocks,
|
|
@@ -21336,7 +22320,7 @@ async function render(run) {
|
|
|
21336
22320
|
});
|
|
21337
22321
|
emitVerdict({
|
|
21338
22322
|
proposed: "WARN",
|
|
21339
|
-
changed:
|
|
22323
|
+
changed: run2.changedUniverse,
|
|
21340
22324
|
coverage: reviewCoverage,
|
|
21341
22325
|
userSummary: lines.length > 0 ? `${summary}
|
|
21342
22326
|
${lines.join("\n")}` : summary,
|
|
@@ -21428,7 +22412,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
21428
22412
|
`);
|
|
21429
22413
|
emitVerdict({
|
|
21430
22414
|
proposed: "FAIL",
|
|
21431
|
-
changed:
|
|
22415
|
+
changed: run2.changedUniverse,
|
|
21432
22416
|
coverage: reviewCoverage,
|
|
21433
22417
|
userSummary: "",
|
|
21434
22418
|
// Subject to the SAME cycle cut as PASS/WARN. Suppressing here is safe:
|
|
@@ -21453,7 +22437,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
21453
22437
|
userSummary += loginNudge + grantNudge;
|
|
21454
22438
|
emitVerdict({
|
|
21455
22439
|
proposed: "PASS",
|
|
21456
|
-
changed:
|
|
22440
|
+
changed: run2.changedUniverse,
|
|
21457
22441
|
coverage: reviewCoverage,
|
|
21458
22442
|
userSummary,
|
|
21459
22443
|
agentContext: silenced ? null : agentContextFor(response, intentRepeatCount, priorPendingFingerprints),
|
|
@@ -21475,7 +22459,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
21475
22459
|
userSummary += loginNudge + grantNudge;
|
|
21476
22460
|
emitVerdict({
|
|
21477
22461
|
proposed: "WARN",
|
|
21478
|
-
changed:
|
|
22462
|
+
changed: run2.changedUniverse,
|
|
21479
22463
|
coverage: reviewCoverage,
|
|
21480
22464
|
userSummary,
|
|
21481
22465
|
agentContext: silenced ? null : agentContextFor(response, intentRepeatCount, priorPendingFingerprints),
|
|
@@ -21500,7 +22484,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
21500
22484
|
process.exit(0);
|
|
21501
22485
|
}
|
|
21502
22486
|
}
|
|
21503
|
-
Object.assign(
|
|
22487
|
+
Object.assign(run2, { iteration });
|
|
21504
22488
|
}
|
|
21505
22489
|
|
|
21506
22490
|
// src/commands/analyze/index.ts
|
|
@@ -21519,6 +22503,8 @@ var PIPELINE = [
|
|
|
21519
22503
|
// ← THE NARROWING. what gets sent, and why not the rest
|
|
21520
22504
|
["contextFiles", contextFiles],
|
|
21521
22505
|
// supporting files, merged INTO the delta array
|
|
22506
|
+
["repoContext", repoContext],
|
|
22507
|
+
// R1/R3 — call sites of changed symbols, one line each
|
|
21522
22508
|
["memoryManifest", memoryManifest],
|
|
21523
22509
|
// knowledge-graph manifest + one-time auto-seed
|
|
21524
22510
|
["foldTranscript", foldTranscript],
|
|
@@ -21535,7 +22521,7 @@ var PIPELINE = [
|
|
|
21535
22521
|
// say it — stderr, stdout, disk
|
|
21536
22522
|
];
|
|
21537
22523
|
function registerAnalyzeCommand(program2) {
|
|
21538
|
-
program2.command("analyze").description("Run Verity analysis on changed files (stop hook)").option("--debounce <seconds>", "Skip if last analysis was within N seconds",
|
|
22524
|
+
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) => {
|
|
21539
22525
|
const globals = program2.opts();
|
|
21540
22526
|
try {
|
|
21541
22527
|
await runAnalyze(opts, globals);
|
|
@@ -21550,18 +22536,18 @@ function registerAnalyzeCommand(program2) {
|
|
|
21550
22536
|
}
|
|
21551
22537
|
var tracing = () => process.env.VERITY_TRACE_PHASES === "1";
|
|
21552
22538
|
async function runAnalyze(opts, globals) {
|
|
21553
|
-
const
|
|
21554
|
-
installRunEvidence(
|
|
22539
|
+
const run2 = createRun(opts, globals);
|
|
22540
|
+
installRunEvidence(run2);
|
|
21555
22541
|
for (const [name, phase] of PIPELINE) {
|
|
21556
|
-
|
|
22542
|
+
run2.phaseReached = name;
|
|
21557
22543
|
if (!tracing()) {
|
|
21558
|
-
await phase(
|
|
21559
|
-
|
|
22544
|
+
await phase(run2);
|
|
22545
|
+
run2.phasesCompleted.push(name);
|
|
21560
22546
|
continue;
|
|
21561
22547
|
}
|
|
21562
22548
|
const started = Date.now();
|
|
21563
|
-
await phase(
|
|
21564
|
-
|
|
22549
|
+
await phase(run2);
|
|
22550
|
+
run2.phasesCompleted.push(name);
|
|
21565
22551
|
process.stderr.write(`verity\xB7phase ${name} ${Date.now() - started}ms
|
|
21566
22552
|
`);
|
|
21567
22553
|
}
|
|
@@ -21664,8 +22650,8 @@ async function runReview(opts, globals) {
|
|
|
21664
22650
|
for (const p of specPaths) {
|
|
21665
22651
|
if (!(0, import_node_fs37.existsSync)(p)) continue;
|
|
21666
22652
|
try {
|
|
21667
|
-
const { readFileSync:
|
|
21668
|
-
const content =
|
|
22653
|
+
const { readFileSync: readFileSync25 } = await import("node:fs");
|
|
22654
|
+
const content = readFileSync25(p, "utf-8");
|
|
21669
22655
|
specs.push({ path: p, content: content.slice(0, 10240) });
|
|
21670
22656
|
} catch {
|
|
21671
22657
|
}
|
|
@@ -22000,6 +22986,39 @@ async function runGuard(opts, globals) {
|
|
|
22000
22986
|
statedIntent,
|
|
22001
22987
|
buildGuardCoverage(files, codeDelta, frame, range)
|
|
22002
22988
|
);
|
|
22989
|
+
try {
|
|
22990
|
+
const ig = loadVerityIgnore();
|
|
22991
|
+
const repoContext2 = buildRepoContext({
|
|
22992
|
+
deltaFiles: codeDelta.files,
|
|
22993
|
+
diffs: [],
|
|
22994
|
+
signalsByPath: rangeChangeSignals(frame, range, codeDelta.files.map((f) => f.path)),
|
|
22995
|
+
sentPaths: new Set(codeDelta.files.map((f) => f.path)),
|
|
22996
|
+
isExcluded: (p) => isVerityOwnedPath(p) || isIgnored(ig, p),
|
|
22997
|
+
cwd: frame.worktreeRoot ?? process.cwd()
|
|
22998
|
+
});
|
|
22999
|
+
upgradeToExcerpts(repoContext2, {
|
|
23000
|
+
readFile: (rel) => {
|
|
23001
|
+
try {
|
|
23002
|
+
return (0, import_node_fs38.readFileSync)((0, import_node_path27.join)(frame.worktreeRoot ?? process.cwd(), rel), "utf8");
|
|
23003
|
+
} catch {
|
|
23004
|
+
return null;
|
|
23005
|
+
}
|
|
23006
|
+
}
|
|
23007
|
+
});
|
|
23008
|
+
requestBody.repo_context = repoContext2;
|
|
23009
|
+
logEvent("repo_context", {
|
|
23010
|
+
moment,
|
|
23011
|
+
state: repoContext2.state,
|
|
23012
|
+
reason: repoContext2.reason ?? null,
|
|
23013
|
+
symbols: repoContext2.symbols?.length ?? 0,
|
|
23014
|
+
callers: repoContext2.callers?.length ?? 0,
|
|
23015
|
+
tests: repoContext2.tests?.length ?? 0,
|
|
23016
|
+
excerpts: repoContext2.excerpts?.length ?? 0,
|
|
23017
|
+
elapsed_ms: repoContext2.elapsed_ms ?? null
|
|
23018
|
+
});
|
|
23019
|
+
} catch (e) {
|
|
23020
|
+
logEvent("repo_context", { moment, state: "absent", reason: "exception", message: e.message });
|
|
23021
|
+
}
|
|
22003
23022
|
const coverage = {
|
|
22004
23023
|
moment,
|
|
22005
23024
|
root: frame.worktreeRoot,
|
|
@@ -22254,22 +23273,297 @@ function registerWaiveCommand(program2) {
|
|
|
22254
23273
|
}
|
|
22255
23274
|
|
|
22256
23275
|
// src/commands/init.ts
|
|
22257
|
-
var
|
|
22258
|
-
var
|
|
23276
|
+
var import_node_fs44 = require("node:fs");
|
|
23277
|
+
var import_promises14 = require("node:fs/promises");
|
|
22259
23278
|
var import_node_path29 = require("node:path");
|
|
23279
|
+
var import_node_child_process14 = require("node:child_process");
|
|
23280
|
+
|
|
23281
|
+
// src/lib/banner.ts
|
|
23282
|
+
var WORDMARK = [
|
|
23283
|
+
"\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",
|
|
23284
|
+
"\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",
|
|
23285
|
+
"\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 ",
|
|
23286
|
+
"\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 ",
|
|
23287
|
+
" \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 ",
|
|
23288
|
+
" \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 "
|
|
23289
|
+
];
|
|
23290
|
+
var WORDMARK_NARROW = [
|
|
23291
|
+
"\u2588 \u2588 \u2588\u2580\u2580\u2580\u2580 \u2588\u2580\u2580\u2580\u2584 \u2580\u2580\u2588\u2580\u2580 \u2580\u2580\u2588\u2580\u2580 \u2588 \u2588",
|
|
23292
|
+
"\u2588 \u2588 \u2588\u2580\u2580\u2580 \u2588\u2584\u2584\u2584\u2580 \u2588 \u2588 \u2580\u2584\u2580 ",
|
|
23293
|
+
" \u2580\u2584\u2580 \u2588\u2584\u2584\u2584\u2584 \u2588 \u2580\u2584 \u2584\u2584\u2588\u2584\u2584 \u2588 \u2588 "
|
|
23294
|
+
];
|
|
23295
|
+
var TAGLINE = "reviews every turn, remembers every lesson";
|
|
23296
|
+
var WORDMARK_COLOR = ["\x1B[0;32m"];
|
|
23297
|
+
var DIM3 = "\x1B[2m";
|
|
23298
|
+
var RESET2 = "\x1B[0m";
|
|
23299
|
+
var INDENT = " ";
|
|
23300
|
+
function artWidth(art) {
|
|
23301
|
+
return Math.max(...art.map((r) => r.length)) + INDENT.length;
|
|
23302
|
+
}
|
|
23303
|
+
function canRenderArt() {
|
|
23304
|
+
return !!process.stderr.isTTY;
|
|
23305
|
+
}
|
|
23306
|
+
function printBanner(opts) {
|
|
23307
|
+
if (!opts.interactive) return;
|
|
23308
|
+
const columns = opts.columns ?? process.stderr.columns ?? 80;
|
|
23309
|
+
const color = colorEnabled();
|
|
23310
|
+
const version = `v${cliVersion()}`;
|
|
23311
|
+
const art = [WORDMARK, WORDMARK_NARROW].find((a) => artWidth(a) <= columns) ?? null;
|
|
23312
|
+
const dim = (text) => color ? `${DIM3}${text}${RESET2}` : text;
|
|
23313
|
+
process.stderr.write("\n");
|
|
23314
|
+
if (!art) {
|
|
23315
|
+
const candidates = [`Verity ${version}`, "Verity"];
|
|
23316
|
+
const text = candidates.find((c) => c.length + INDENT.length <= columns);
|
|
23317
|
+
if (text) process.stderr.write(`${INDENT}${dim(text)}
|
|
23318
|
+
`);
|
|
23319
|
+
process.stderr.write("\n");
|
|
23320
|
+
return;
|
|
23321
|
+
}
|
|
23322
|
+
art.forEach((row2, i) => {
|
|
23323
|
+
const tint = color && WORDMARK_COLOR.length > 0 ? WORDMARK_COLOR[Math.min(i, WORDMARK_COLOR.length - 1)] : "";
|
|
23324
|
+
const reset = color && tint ? RESET2 : "";
|
|
23325
|
+
process.stderr.write(`${INDENT}${tint}${row2}${reset}
|
|
23326
|
+
`);
|
|
23327
|
+
});
|
|
23328
|
+
if (TAGLINE.length + INDENT.length <= columns) {
|
|
23329
|
+
process.stderr.write(`${INDENT}${dim(TAGLINE)}
|
|
23330
|
+
`);
|
|
23331
|
+
}
|
|
23332
|
+
if (version.length + INDENT.length <= columns) {
|
|
23333
|
+
process.stderr.write(`${INDENT}${dim(version)}
|
|
23334
|
+
`);
|
|
23335
|
+
}
|
|
23336
|
+
process.stderr.write("\n");
|
|
23337
|
+
}
|
|
23338
|
+
function printPhase(n, of, title, subtitle) {
|
|
23339
|
+
const color = colorEnabled();
|
|
23340
|
+
const columns = process.stderr.columns ?? 72;
|
|
23341
|
+
const width = Math.min(columns, 72);
|
|
23342
|
+
const label2 = `\u2500\u2500 Phase ${n} of ${of} \xB7 ${title} `;
|
|
23343
|
+
const rule = label2 + "\u2500".repeat(Math.max(0, width - label2.length - 2));
|
|
23344
|
+
process.stderr.write("\n");
|
|
23345
|
+
process.stderr.write(color ? ` ${DIM3}${rule}${RESET2}
|
|
23346
|
+
` : ` ${rule}
|
|
23347
|
+
`);
|
|
23348
|
+
if (subtitle && subtitle.length + INDENT.length <= columns) {
|
|
23349
|
+
process.stderr.write(color ? ` ${DIM3}${subtitle}${RESET2}
|
|
23350
|
+
` : ` ${subtitle}
|
|
23351
|
+
`);
|
|
23352
|
+
}
|
|
23353
|
+
process.stderr.write("\n");
|
|
23354
|
+
}
|
|
23355
|
+
|
|
23356
|
+
// src/commands/doctor.ts
|
|
23357
|
+
var import_node_fs42 = require("node:fs");
|
|
23358
|
+
|
|
23359
|
+
// src/lib/prereqs.ts
|
|
22260
23360
|
var import_node_child_process11 = require("node:child_process");
|
|
22261
|
-
var
|
|
23361
|
+
var MIN_NODE_MAJOR = 20;
|
|
23362
|
+
function which(bin) {
|
|
23363
|
+
try {
|
|
23364
|
+
const out = (0, import_node_child_process11.execSync)(`command -v ${bin}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
23365
|
+
return out || null;
|
|
23366
|
+
} catch {
|
|
23367
|
+
return null;
|
|
23368
|
+
}
|
|
23369
|
+
}
|
|
23370
|
+
function checkNode() {
|
|
23371
|
+
const version = process.version;
|
|
23372
|
+
const major = Number.parseInt(version.slice(1), 10);
|
|
23373
|
+
const ok = Number.isFinite(major) && major >= MIN_NODE_MAJOR;
|
|
23374
|
+
return {
|
|
23375
|
+
id: "node",
|
|
23376
|
+
label: "Node.js",
|
|
23377
|
+
status: ok ? "ok" : "missing",
|
|
23378
|
+
detail: version,
|
|
23379
|
+
remedy: ok ? void 0 : `Node.js ${MIN_NODE_MAJOR}+ required \u2014 update from https://nodejs.org`,
|
|
23380
|
+
required: true
|
|
23381
|
+
};
|
|
23382
|
+
}
|
|
23383
|
+
function checkGit() {
|
|
23384
|
+
let detail = "";
|
|
23385
|
+
try {
|
|
23386
|
+
detail = (0, import_node_child_process11.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
23387
|
+
} catch {
|
|
23388
|
+
return {
|
|
23389
|
+
id: "git",
|
|
23390
|
+
label: "git",
|
|
23391
|
+
status: "missing",
|
|
23392
|
+
detail: "not found",
|
|
23393
|
+
remedy: "Install git from https://git-scm.com",
|
|
23394
|
+
required: true
|
|
23395
|
+
};
|
|
23396
|
+
}
|
|
23397
|
+
return { id: "git", label: "git", status: "ok", detail, required: true };
|
|
23398
|
+
}
|
|
23399
|
+
function checkClaude() {
|
|
23400
|
+
const path = which("claude");
|
|
23401
|
+
return {
|
|
23402
|
+
id: "claude",
|
|
23403
|
+
label: "Claude Code",
|
|
23404
|
+
status: path ? "ok" : "warn",
|
|
23405
|
+
detail: path ?? "not found",
|
|
23406
|
+
remedy: path ? void 0 : "Hooks are wired but need Claude Code to fire \u2014 https://claude.com/claude-code",
|
|
23407
|
+
required: false
|
|
23408
|
+
};
|
|
23409
|
+
}
|
|
23410
|
+
function checkAnalysisCli() {
|
|
23411
|
+
const path = which("codacy-analysis");
|
|
23412
|
+
return {
|
|
23413
|
+
id: "analysis-cli",
|
|
23414
|
+
label: "@codacy/analysis-cli",
|
|
23415
|
+
status: path ? "ok" : "warn",
|
|
23416
|
+
detail: path ?? "not found",
|
|
23417
|
+
remedy: path ? void 0 : "npm install -g @codacy/analysis-cli (static findings are unavailable until then)",
|
|
23418
|
+
required: false
|
|
23419
|
+
};
|
|
23420
|
+
}
|
|
23421
|
+
var INSTALL_TIMEOUT_MS = 12e4;
|
|
23422
|
+
function run(command, args, opts = {}) {
|
|
23423
|
+
return new Promise((resolve4) => {
|
|
23424
|
+
const child = (0, import_node_child_process11.spawn)(command, args, {
|
|
23425
|
+
stdio: opts.inherit ? "inherit" : "pipe",
|
|
23426
|
+
timeout: INSTALL_TIMEOUT_MS
|
|
23427
|
+
});
|
|
23428
|
+
child.on("error", () => resolve4(false));
|
|
23429
|
+
child.on("close", (code) => resolve4(code === 0));
|
|
23430
|
+
});
|
|
23431
|
+
}
|
|
23432
|
+
async function installAnalysisCli() {
|
|
23433
|
+
const spinner = startSpinner("Installing @codacy/analysis-cli");
|
|
23434
|
+
const ok = await run("npm", ["install", "-g", "@codacy/analysis-cli"]);
|
|
23435
|
+
if (ok) {
|
|
23436
|
+
const check = checkAnalysisCli();
|
|
23437
|
+
if (check.status === "ok") {
|
|
23438
|
+
spinner.succeed("@codacy/analysis-cli installed");
|
|
23439
|
+
return { ...check, justInstalled: true };
|
|
23440
|
+
}
|
|
23441
|
+
spinner.warn("npm reported success but codacy-analysis is not on PATH");
|
|
23442
|
+
return check;
|
|
23443
|
+
}
|
|
23444
|
+
spinner.stop();
|
|
23445
|
+
{
|
|
23446
|
+
const failed = {
|
|
23447
|
+
id: "analysis-cli",
|
|
23448
|
+
label: "@codacy/analysis-cli",
|
|
23449
|
+
status: "warn",
|
|
23450
|
+
detail: "install failed",
|
|
23451
|
+
remedy: "Install manually: npm install -g @codacy/analysis-cli",
|
|
23452
|
+
required: false
|
|
23453
|
+
};
|
|
23454
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return failed;
|
|
23455
|
+
process.stderr.write(" Retrying with sudo (you may be asked for your password)\u2026\n");
|
|
23456
|
+
const sudoOk = await run("sudo", ["npm", "install", "-g", "@codacy/analysis-cli"], { inherit: true });
|
|
23457
|
+
if (!sudoOk) return failed;
|
|
23458
|
+
const check = checkAnalysisCli();
|
|
23459
|
+
return check.status === "ok" ? { ...check, justInstalled: true } : check;
|
|
23460
|
+
}
|
|
23461
|
+
}
|
|
23462
|
+
async function checkPrereqs(opts = {}) {
|
|
23463
|
+
const checks = [checkNode(), checkGit(), checkClaude()];
|
|
23464
|
+
let analysis = checkAnalysisCli();
|
|
23465
|
+
if (analysis.status !== "ok" && opts.install) {
|
|
23466
|
+
analysis = await installAnalysisCli();
|
|
23467
|
+
}
|
|
23468
|
+
checks.push(analysis);
|
|
23469
|
+
return { checks, blocked: checks.some((c) => c.required && c.status !== "ok") };
|
|
23470
|
+
}
|
|
22262
23471
|
|
|
22263
|
-
// src/
|
|
23472
|
+
// src/lib/telemetry.ts
|
|
23473
|
+
var import_promises12 = require("node:fs/promises");
|
|
23474
|
+
|
|
23475
|
+
// src/lib/gitignore.ts
|
|
23476
|
+
var import_node_child_process12 = require("node:child_process");
|
|
22264
23477
|
var import_node_fs40 = require("node:fs");
|
|
22265
|
-
var
|
|
22266
|
-
var
|
|
23478
|
+
var VERITY_GITIGNORE_MARKER = "# Verity \u2014 machine-local state.";
|
|
23479
|
+
var SETTINGS_LOCAL_IGNORE_ENTRY = ".claude/settings.local.json";
|
|
23480
|
+
var VERITY_GITIGNORE_BLOCK = [
|
|
23481
|
+
"# Verity \u2014 machine-local state. Everything in .verity/ is ignored EXCEPT the",
|
|
23482
|
+
"# shared standard and the knowledge graph, which are meant to be committed.",
|
|
23483
|
+
".verity/*",
|
|
23484
|
+
"!.verity/standard.yaml",
|
|
23485
|
+
"!.verity/memory/",
|
|
23486
|
+
".verity/memory/log.md",
|
|
23487
|
+
SETTINGS_LOCAL_IGNORE_ENTRY,
|
|
23488
|
+
""
|
|
23489
|
+
].join("\n");
|
|
23490
|
+
var BREAKING_ENTRIES = /* @__PURE__ */ new Set([".verity/", ".verity"]);
|
|
23491
|
+
function isIgnored2(path) {
|
|
23492
|
+
try {
|
|
23493
|
+
(0, import_node_child_process12.execSync)(`git check-ignore -q -- "${path}"`, { stdio: "pipe" });
|
|
23494
|
+
return true;
|
|
23495
|
+
} catch (err) {
|
|
23496
|
+
return err.status === 1 ? false : null;
|
|
23497
|
+
}
|
|
23498
|
+
}
|
|
23499
|
+
function semanticsHold() {
|
|
23500
|
+
const snapshot = isIgnored2(".verity/.snapshot/__probe__");
|
|
23501
|
+
const standard = isIgnored2(".verity/standard.yaml");
|
|
23502
|
+
const node = isIgnored2(".verity/memory/domain/__probe__.md");
|
|
23503
|
+
const futureState = isIgnored2(".verity/.__probe-future-state__");
|
|
23504
|
+
if (snapshot === null || standard === null || node === null || futureState === null) return null;
|
|
23505
|
+
return snapshot === true && futureState === true && standard === false && node === false;
|
|
23506
|
+
}
|
|
23507
|
+
function ensureVerityGitignore() {
|
|
23508
|
+
let content = "";
|
|
23509
|
+
try {
|
|
23510
|
+
content = (0, import_node_fs40.readFileSync)(".gitignore", "utf-8");
|
|
23511
|
+
} catch {
|
|
23512
|
+
}
|
|
23513
|
+
const hasMarker = content.includes(VERITY_GITIGNORE_MARKER);
|
|
23514
|
+
const lines = content.split("\n");
|
|
23515
|
+
const breakingCount = lines.filter((l) => BREAKING_ENTRIES.has(l.trim())).length;
|
|
23516
|
+
const needsRepair = breakingCount > 0;
|
|
23517
|
+
const verified = (result) => semanticsHold() === false ? "conflict" : result;
|
|
23518
|
+
if (hasMarker && !needsRepair) return verified("covered");
|
|
23519
|
+
if (!hasMarker && !needsRepair) {
|
|
23520
|
+
if (semanticsHold() === true) return "covered";
|
|
23521
|
+
}
|
|
23522
|
+
try {
|
|
23523
|
+
let next = content;
|
|
23524
|
+
if (needsRepair) {
|
|
23525
|
+
next = lines.map((l) => BREAKING_ENTRIES.has(l.trim()) ? ".verity/*" : l).join("\n");
|
|
23526
|
+
}
|
|
23527
|
+
if (!hasMarker) {
|
|
23528
|
+
const sep2 = next === "" ? "" : next.endsWith("\n") ? "\n" : "\n\n";
|
|
23529
|
+
next = next + sep2 + VERITY_GITIGNORE_BLOCK;
|
|
23530
|
+
}
|
|
23531
|
+
(0, import_node_fs40.writeFileSync)(".gitignore", next);
|
|
23532
|
+
return verified(needsRepair ? "repaired" : "added");
|
|
23533
|
+
} catch {
|
|
23534
|
+
return "failed";
|
|
23535
|
+
}
|
|
23536
|
+
}
|
|
23537
|
+
function committedVerityState() {
|
|
23538
|
+
let out = "";
|
|
23539
|
+
try {
|
|
23540
|
+
out = (0, import_node_child_process12.execSync)("git ls-files -z -- .verity", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
23541
|
+
} catch {
|
|
23542
|
+
return [];
|
|
23543
|
+
}
|
|
23544
|
+
return out.split("\0").filter(Boolean).filter((p) => p !== ".verity/standard.yaml" && !(p.startsWith(".verity/memory/") && p !== ".verity/memory/log.md"));
|
|
23545
|
+
}
|
|
23546
|
+
function untrackVerityState() {
|
|
23547
|
+
const tracked = committedVerityState();
|
|
23548
|
+
if (tracked.length === 0) return "none";
|
|
23549
|
+
try {
|
|
23550
|
+
(0, import_node_child_process12.execSync)("git rm -r --cached --quiet -- .verity", { stdio: "pipe" });
|
|
23551
|
+
for (const keep of [".verity/standard.yaml", ".verity/memory"]) {
|
|
23552
|
+
try {
|
|
23553
|
+
(0, import_node_child_process12.execSync)(`git add -- "${keep}"`, { stdio: "pipe" });
|
|
23554
|
+
} catch {
|
|
23555
|
+
}
|
|
23556
|
+
}
|
|
23557
|
+
return "untracked";
|
|
23558
|
+
} catch {
|
|
23559
|
+
return "failed";
|
|
23560
|
+
}
|
|
23561
|
+
}
|
|
22267
23562
|
|
|
22268
23563
|
// src/lib/telemetry.ts
|
|
22269
|
-
var import_promises12 = require("node:fs/promises");
|
|
22270
23564
|
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
22271
23565
|
var GITIGNORE_FILE = ".gitignore";
|
|
22272
|
-
var GITIGNORE_ENTRY =
|
|
23566
|
+
var GITIGNORE_ENTRY = SETTINGS_LOCAL_IGNORE_ENTRY;
|
|
22273
23567
|
var OTEL_HEADERS_HELPER_CMD = "verity telemetry headers";
|
|
22274
23568
|
var LEGACY_TELEMETRY_ENV_KEYS = ["OTEL_EXPORTER_OTLP_HEADERS"];
|
|
22275
23569
|
function deriveOtlpEndpoint(serviceUrl) {
|
|
@@ -22355,14 +23649,119 @@ async function uninstallTelemetry() {
|
|
|
22355
23649
|
return { ok: true, data: { removed } };
|
|
22356
23650
|
}
|
|
22357
23651
|
|
|
23652
|
+
// src/lib/setup-state.ts
|
|
23653
|
+
var import_promises13 = require("node:fs/promises");
|
|
23654
|
+
var import_node_fs41 = require("node:fs");
|
|
23655
|
+
var SETUP_STATE_FILE = `${VERITY_DIR}/setup.json`;
|
|
23656
|
+
async function readSetupState() {
|
|
23657
|
+
const path = projectPath(SETUP_STATE_FILE);
|
|
23658
|
+
if (!(0, import_node_fs41.existsSync)(path)) return null;
|
|
23659
|
+
try {
|
|
23660
|
+
const parsed = JSON.parse(await (0, import_promises13.readFile)(path, "utf-8"));
|
|
23661
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
23662
|
+
} catch {
|
|
23663
|
+
return null;
|
|
23664
|
+
}
|
|
23665
|
+
}
|
|
23666
|
+
async function writeSetupState(patch) {
|
|
23667
|
+
const current = await readSetupState() ?? { version: 1 };
|
|
23668
|
+
const next = { ...current, ...patch, version: 1 };
|
|
23669
|
+
await writeJsonFilePreservingStyle(projectPath(SETUP_STATE_FILE), next);
|
|
23670
|
+
return next;
|
|
23671
|
+
}
|
|
23672
|
+
|
|
23673
|
+
// src/commands/doctor.ts
|
|
23674
|
+
async function buildReport() {
|
|
23675
|
+
const prereqs = await checkPrereqs({ install: false });
|
|
23676
|
+
const state = await readSetupState();
|
|
23677
|
+
const hooks = await checkAllVerityHooks();
|
|
23678
|
+
const telemetry = await checkTelemetry();
|
|
23679
|
+
const artifacts = {
|
|
23680
|
+
standard: (0, import_node_fs42.existsSync)(projectPath(STANDARD_FILE)),
|
|
23681
|
+
analysisConfig: (0, import_node_fs42.existsSync)(projectPath(CODACY_CONFIG_FILE)),
|
|
23682
|
+
verityMd: (0, import_node_fs42.existsSync)(projectPath(VERITY_MD_FILE))
|
|
23683
|
+
};
|
|
23684
|
+
const next = [];
|
|
23685
|
+
for (const c of prereqs.checks) {
|
|
23686
|
+
if (c.status !== "ok" && c.remedy) next.push(c.remedy);
|
|
23687
|
+
}
|
|
23688
|
+
if (!state?.init) next.push('Run "verity init" \u2014 the deterministic setup phase has not completed here.');
|
|
23689
|
+
if (!artifacts.standard || !artifacts.analysisConfig || !artifacts.verityMd) {
|
|
23690
|
+
next.push("Run /verity-setup in Claude Code \u2014 the Standard, analysis config, and VERITY.md are synthesized there.");
|
|
23691
|
+
}
|
|
23692
|
+
const noAnalysisMoment = !hooks.stop && hooks.guardOn.length === 0;
|
|
23693
|
+
if (noAnalysisMoment) {
|
|
23694
|
+
next.push('No analysis moment is active \u2014 run "verity hooks install --moments stop" or re-run "verity init".');
|
|
23695
|
+
}
|
|
23696
|
+
if (state?.telemetry === "deferred") {
|
|
23697
|
+
next.push('Telemetry was requested but needs a token \u2014 run "verity login", then "verity telemetry install".');
|
|
23698
|
+
}
|
|
23699
|
+
return {
|
|
23700
|
+
prerequisites: prereqs.checks,
|
|
23701
|
+
blocked: prereqs.blocked,
|
|
23702
|
+
phases: {
|
|
23703
|
+
init: { done: !!state?.init, ...state?.init ?? {} },
|
|
23704
|
+
setup: { done: artifacts.standard && artifacts.analysisConfig && artifacts.verityMd }
|
|
23705
|
+
},
|
|
23706
|
+
answers: {
|
|
23707
|
+
intensity: state?.intensity ?? null,
|
|
23708
|
+
moments: state?.moments ?? null,
|
|
23709
|
+
telemetry: state?.telemetry ?? null
|
|
23710
|
+
},
|
|
23711
|
+
hooks: { ...hooks, noAnalysisMoment },
|
|
23712
|
+
telemetry: { enabled: telemetry.enabled, endpoint: telemetry.endpoint },
|
|
23713
|
+
artifacts,
|
|
23714
|
+
next
|
|
23715
|
+
};
|
|
23716
|
+
}
|
|
23717
|
+
function registerDoctorCommand(program2) {
|
|
23718
|
+
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) => {
|
|
23719
|
+
const report = await buildReport();
|
|
23720
|
+
if (opts.json) {
|
|
23721
|
+
printJson(report);
|
|
23722
|
+
if (report.blocked) process.exit(1);
|
|
23723
|
+
return;
|
|
23724
|
+
}
|
|
23725
|
+
printInfo("Prerequisites:");
|
|
23726
|
+
for (const c of report.prerequisites) {
|
|
23727
|
+
if (c.status === "ok") printInfo(` ${c.label} ${c.detail} \u2713`);
|
|
23728
|
+
else printWarn(` ${c.label}: ${c.detail}${c.remedy ? ` \u2014 ${c.remedy}` : ""}`);
|
|
23729
|
+
}
|
|
23730
|
+
printInfo("Setup:");
|
|
23731
|
+
printInfo(` verity init: ${report.phases.init.done ? `done ${report.phases.init.completed_at ?? ""}`.trim() : "NOT run here"}`);
|
|
23732
|
+
printInfo(` /verity-setup: ${report.phases.setup.done ? "done" : "not completed"}`);
|
|
23733
|
+
printInfo(` intensity: ${report.answers.intensity ?? "\u2014"} moments: ${report.answers.moments?.join(", ") ?? "\u2014"}`);
|
|
23734
|
+
printInfo("Hooks:");
|
|
23735
|
+
printInfo(` Stop (verity analyze): ${report.hooks.stop ? "on" : "off"}`);
|
|
23736
|
+
printInfo(` Git-moment gate: ${report.hooks.guardOn.length ? report.hooks.guardOn.join(", ") : "off"}`);
|
|
23737
|
+
printInfo(` Infra (intent/baseline/compact/session-end): ${[report.hooks.intent, report.hooks.baseline, report.hooks.compact, report.hooks.sessionEnd].filter(Boolean).length}/4`);
|
|
23738
|
+
printInfo(`Telemetry: ${report.telemetry.enabled ? `enabled \u2192 ${report.telemetry.endpoint}` : "disabled"}`);
|
|
23739
|
+
printInfo("Artifacts:");
|
|
23740
|
+
printInfo(` .verity/standard.yaml: ${report.artifacts.standard ? "\u2713" : "missing"}`);
|
|
23741
|
+
printInfo(` .codacy/codacy.config.json: ${report.artifacts.analysisConfig ? "\u2713" : "missing"}`);
|
|
23742
|
+
printInfo(` VERITY.md: ${report.artifacts.verityMd ? "\u2713" : "missing"}`);
|
|
23743
|
+
if (report.next.length > 0) {
|
|
23744
|
+
console.log("");
|
|
23745
|
+
printWarn("Next:");
|
|
23746
|
+
for (const n of report.next) printWarn(` - ${n}`);
|
|
23747
|
+
} else {
|
|
23748
|
+
printInfo("Setup is complete.");
|
|
23749
|
+
}
|
|
23750
|
+
if (report.blocked) process.exit(1);
|
|
23751
|
+
});
|
|
23752
|
+
}
|
|
23753
|
+
|
|
22358
23754
|
// src/commands/migrate.ts
|
|
23755
|
+
var import_node_fs43 = require("node:fs");
|
|
23756
|
+
var import_node_path28 = require("node:path");
|
|
23757
|
+
var import_node_child_process13 = require("node:child_process");
|
|
22359
23758
|
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
22360
23759
|
function defaultNpmRemover(pkg) {
|
|
22361
|
-
(0,
|
|
23760
|
+
(0, import_node_child_process13.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
|
|
22362
23761
|
}
|
|
22363
23762
|
function isGitTracked(cwd, relPath) {
|
|
22364
23763
|
try {
|
|
22365
|
-
(0,
|
|
23764
|
+
(0, import_node_child_process13.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
|
|
22366
23765
|
return true;
|
|
22367
23766
|
} catch {
|
|
22368
23767
|
return false;
|
|
@@ -22370,7 +23769,7 @@ function isGitTracked(cwd, relPath) {
|
|
|
22370
23769
|
}
|
|
22371
23770
|
function isGitRepo(cwd) {
|
|
22372
23771
|
try {
|
|
22373
|
-
(0,
|
|
23772
|
+
(0, import_node_child_process13.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
|
|
22374
23773
|
return true;
|
|
22375
23774
|
} catch {
|
|
22376
23775
|
return false;
|
|
@@ -22393,10 +23792,10 @@ async function runMigration(opts = {}) {
|
|
|
22393
23792
|
function migrateProjectDir(root, actions) {
|
|
22394
23793
|
const gateDir = (0, import_node_path28.join)(root, ".gate");
|
|
22395
23794
|
const verityDir = (0, import_node_path28.join)(root, ".verity");
|
|
22396
|
-
if ((0,
|
|
23795
|
+
if ((0, import_node_fs43.existsSync)(gateDir) && !(0, import_node_fs43.existsSync)(verityDir)) {
|
|
22397
23796
|
return migrateProjectDirRename(root, gateDir, verityDir, actions);
|
|
22398
23797
|
}
|
|
22399
|
-
if ((0,
|
|
23798
|
+
if ((0, import_node_fs43.existsSync)(gateDir) && (0, import_node_fs43.existsSync)(verityDir)) {
|
|
22400
23799
|
return migrateProjectDirCarry(gateDir, verityDir, actions);
|
|
22401
23800
|
}
|
|
22402
23801
|
return false;
|
|
@@ -22410,20 +23809,20 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
|
|
|
22410
23809
|
);
|
|
22411
23810
|
}
|
|
22412
23811
|
try {
|
|
22413
|
-
(0,
|
|
23812
|
+
(0, import_node_child_process13.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
|
|
22414
23813
|
actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
|
|
22415
23814
|
moved = true;
|
|
22416
23815
|
} catch {
|
|
22417
23816
|
}
|
|
22418
23817
|
}
|
|
22419
23818
|
if (moved) {
|
|
22420
|
-
if ((0,
|
|
23819
|
+
if ((0, import_node_fs43.existsSync)(gateDir)) {
|
|
22421
23820
|
const carried = carryLegacyContents(gateDir, verityDir);
|
|
22422
23821
|
if (carried > 0) {
|
|
22423
23822
|
actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
|
|
22424
23823
|
}
|
|
22425
23824
|
try {
|
|
22426
|
-
(0,
|
|
23825
|
+
(0, import_node_fs43.rmSync)(gateDir, { recursive: true, force: true });
|
|
22427
23826
|
} catch {
|
|
22428
23827
|
}
|
|
22429
23828
|
}
|
|
@@ -22439,7 +23838,7 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
|
|
|
22439
23838
|
actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
|
|
22440
23839
|
}
|
|
22441
23840
|
try {
|
|
22442
|
-
(0,
|
|
23841
|
+
(0, import_node_fs43.rmSync)(gateDir, { recursive: true, force: true });
|
|
22443
23842
|
} catch {
|
|
22444
23843
|
}
|
|
22445
23844
|
return carried > 0;
|
|
@@ -22448,9 +23847,9 @@ function migrateGlobalCredentials(home, actions) {
|
|
|
22448
23847
|
if (!home) return;
|
|
22449
23848
|
const gateCreds = (0, import_node_path28.join)(home, ".gate", "credentials");
|
|
22450
23849
|
const verityCreds = (0, import_node_path28.join)(home, ".verity", "credentials");
|
|
22451
|
-
if (!(0,
|
|
22452
|
-
if (!(0,
|
|
22453
|
-
(0,
|
|
23850
|
+
if (!(0, import_node_fs43.existsSync)(gateCreds)) return;
|
|
23851
|
+
if (!(0, import_node_fs43.existsSync)(verityCreds)) {
|
|
23852
|
+
(0, import_node_fs43.mkdirSync)((0, import_node_path28.join)(home, ".verity"), { recursive: true });
|
|
22454
23853
|
moveFile(gateCreds, verityCreds);
|
|
22455
23854
|
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
22456
23855
|
return;
|
|
@@ -22473,7 +23872,7 @@ async function migrateLegacyHooks(root, actions) {
|
|
|
22473
23872
|
}
|
|
22474
23873
|
async function migrateClaudeMd(root, actions) {
|
|
22475
23874
|
const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
|
|
22476
|
-
const hadLegacyBlock = (0,
|
|
23875
|
+
const hadLegacyBlock = (0, import_node_fs43.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
|
|
22477
23876
|
if (!hadLegacyBlock) return;
|
|
22478
23877
|
try {
|
|
22479
23878
|
await ensureClaudeMdPointer(root);
|
|
@@ -22485,11 +23884,11 @@ async function migrateClaudeMd(root, actions) {
|
|
|
22485
23884
|
function migrateStandardFile(root, actions) {
|
|
22486
23885
|
const gateMd = (0, import_node_path28.join)(root, "GATE.md");
|
|
22487
23886
|
const verityMd = (0, import_node_path28.join)(root, "VERITY.md");
|
|
22488
|
-
if (!(0,
|
|
23887
|
+
if (!(0, import_node_fs43.existsSync)(gateMd) || (0, import_node_fs43.existsSync)(verityMd)) return;
|
|
22489
23888
|
let moved = false;
|
|
22490
23889
|
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
22491
23890
|
try {
|
|
22492
|
-
(0,
|
|
23891
|
+
(0, import_node_child_process13.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
|
|
22493
23892
|
moved = true;
|
|
22494
23893
|
} catch {
|
|
22495
23894
|
}
|
|
@@ -22497,12 +23896,12 @@ function migrateStandardFile(root, actions) {
|
|
|
22497
23896
|
if (!moved) moveFile(gateMd, verityMd);
|
|
22498
23897
|
const content = readFileSyncSafe(verityMd);
|
|
22499
23898
|
const refreshed = content.split("GATE.md").join("VERITY.md");
|
|
22500
|
-
if (refreshed !== content) (0,
|
|
23899
|
+
if (refreshed !== content) (0, import_node_fs43.writeFileSync)(verityMd, refreshed);
|
|
22501
23900
|
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
22502
23901
|
}
|
|
22503
23902
|
async function migrateTelemetryHeaders(root, actions) {
|
|
22504
23903
|
const file = (0, import_node_path28.join)(root, ".claude", "settings.local.json");
|
|
22505
|
-
if (!(0,
|
|
23904
|
+
if (!(0, import_node_fs43.existsSync)(file)) return;
|
|
22506
23905
|
let settings;
|
|
22507
23906
|
try {
|
|
22508
23907
|
settings = JSON.parse(readFileSyncSafe(file) || "{}");
|
|
@@ -22550,21 +23949,21 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
|
|
|
22550
23949
|
}
|
|
22551
23950
|
if (toAppend.length > 0) {
|
|
22552
23951
|
const sep2 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
|
|
22553
|
-
(0,
|
|
23952
|
+
(0, import_node_fs43.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
|
|
22554
23953
|
}
|
|
22555
|
-
(0,
|
|
23954
|
+
(0, import_node_fs43.rmSync)(gateCreds, { force: true });
|
|
22556
23955
|
return toAppend.length;
|
|
22557
23956
|
}
|
|
22558
23957
|
function readFileSyncSafe(path) {
|
|
22559
23958
|
try {
|
|
22560
|
-
return (0,
|
|
23959
|
+
return (0, import_node_fs43.readFileSync)(path, "utf-8");
|
|
22561
23960
|
} catch {
|
|
22562
23961
|
return "";
|
|
22563
23962
|
}
|
|
22564
23963
|
}
|
|
22565
23964
|
function hasStagedChanges(root) {
|
|
22566
23965
|
try {
|
|
22567
|
-
(0,
|
|
23966
|
+
(0, import_node_child_process13.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
|
|
22568
23967
|
return false;
|
|
22569
23968
|
} catch {
|
|
22570
23969
|
return true;
|
|
@@ -22572,35 +23971,35 @@ function hasStagedChanges(root) {
|
|
|
22572
23971
|
}
|
|
22573
23972
|
function moveDir(from, to) {
|
|
22574
23973
|
try {
|
|
22575
|
-
(0,
|
|
23974
|
+
(0, import_node_fs43.renameSync)(from, to);
|
|
22576
23975
|
} catch (err) {
|
|
22577
23976
|
if (err.code !== "EXDEV") throw err;
|
|
22578
|
-
(0,
|
|
22579
|
-
(0,
|
|
23977
|
+
(0, import_node_fs43.cpSync)(from, to, { recursive: true });
|
|
23978
|
+
(0, import_node_fs43.rmSync)(from, { recursive: true, force: true });
|
|
22580
23979
|
}
|
|
22581
23980
|
}
|
|
22582
23981
|
function moveFile(from, to) {
|
|
22583
23982
|
try {
|
|
22584
|
-
(0,
|
|
23983
|
+
(0, import_node_fs43.renameSync)(from, to);
|
|
22585
23984
|
} catch (err) {
|
|
22586
23985
|
if (err.code !== "EXDEV") throw err;
|
|
22587
|
-
(0,
|
|
22588
|
-
(0,
|
|
23986
|
+
(0, import_node_fs43.cpSync)(from, to);
|
|
23987
|
+
(0, import_node_fs43.rmSync)(from, { force: true });
|
|
22589
23988
|
}
|
|
22590
23989
|
}
|
|
22591
23990
|
function carryLegacyContents(gateDir, verityDir) {
|
|
22592
23991
|
let copied = 0;
|
|
22593
23992
|
const walk = (relDir) => {
|
|
22594
23993
|
const srcDir = (0, import_node_path28.join)(gateDir, relDir);
|
|
22595
|
-
for (const entry of (0,
|
|
23994
|
+
for (const entry of (0, import_node_fs43.readdirSync)(srcDir)) {
|
|
22596
23995
|
const rel = relDir ? (0, import_node_path28.join)(relDir, entry) : entry;
|
|
22597
23996
|
const src = (0, import_node_path28.join)(gateDir, rel);
|
|
22598
23997
|
const dest = (0, import_node_path28.join)(verityDir, rel);
|
|
22599
|
-
if ((0,
|
|
23998
|
+
if ((0, import_node_fs43.statSync)(src).isDirectory()) {
|
|
22600
23999
|
walk(rel);
|
|
22601
|
-
} else if (!(0,
|
|
22602
|
-
(0,
|
|
22603
|
-
(0,
|
|
24000
|
+
} else if (!(0, import_node_fs43.existsSync)(dest)) {
|
|
24001
|
+
(0, import_node_fs43.mkdirSync)((0, import_node_path28.dirname)(dest), { recursive: true });
|
|
24002
|
+
(0, import_node_fs43.cpSync)(src, dest);
|
|
22604
24003
|
copied++;
|
|
22605
24004
|
}
|
|
22606
24005
|
}
|
|
@@ -22611,20 +24010,20 @@ function carryLegacyContents(gateDir, verityDir) {
|
|
|
22611
24010
|
async function needsMigration(root = repoRoot()) {
|
|
22612
24011
|
const gateDir = (0, import_node_path28.join)(root, ".gate");
|
|
22613
24012
|
const verityDir = (0, import_node_path28.join)(root, ".verity");
|
|
22614
|
-
if ((0,
|
|
22615
|
-
if ((0,
|
|
22616
|
-
if ((0,
|
|
24013
|
+
if ((0, import_node_fs43.existsSync)(gateDir) && !(0, import_node_fs43.existsSync)(verityDir)) return true;
|
|
24014
|
+
if ((0, import_node_fs43.existsSync)(gateDir) && (0, import_node_fs43.existsSync)(verityDir)) {
|
|
24015
|
+
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"))) {
|
|
22617
24016
|
return true;
|
|
22618
24017
|
}
|
|
22619
|
-
if ((0,
|
|
24018
|
+
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"))) {
|
|
22620
24019
|
return true;
|
|
22621
24020
|
}
|
|
22622
24021
|
}
|
|
22623
24022
|
const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
|
|
22624
|
-
if ((0,
|
|
24023
|
+
if ((0, import_node_fs43.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
22625
24024
|
return true;
|
|
22626
24025
|
}
|
|
22627
|
-
if ((0,
|
|
24026
|
+
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"))) {
|
|
22628
24027
|
return true;
|
|
22629
24028
|
}
|
|
22630
24029
|
if (await hasLegacyHooksAt(root)) return true;
|
|
@@ -22649,17 +24048,96 @@ function registerMigrateCommand(program2) {
|
|
|
22649
24048
|
});
|
|
22650
24049
|
}
|
|
22651
24050
|
|
|
22652
|
-
// src/
|
|
22653
|
-
|
|
22654
|
-
|
|
22655
|
-
|
|
24051
|
+
// src/lib/prompt.ts
|
|
24052
|
+
var readline2 = __toESM(require("node:readline/promises"));
|
|
24053
|
+
function interactive() {
|
|
24054
|
+
return !!process.stdin.isTTY && !!process.stdout.isTTY;
|
|
24055
|
+
}
|
|
24056
|
+
async function askLine(question, io = {}) {
|
|
24057
|
+
const rl = readline2.createInterface({
|
|
24058
|
+
input: io.input ?? process.stdin,
|
|
24059
|
+
output: io.output ?? process.stdout
|
|
24060
|
+
});
|
|
22656
24061
|
try {
|
|
22657
|
-
|
|
22658
|
-
|
|
24062
|
+
return await new Promise((resolve4) => {
|
|
24063
|
+
rl.question(question).then((a) => resolve4(a.trim())).catch(() => resolve4(null));
|
|
24064
|
+
rl.once("close", () => setImmediate(() => resolve4(null)));
|
|
24065
|
+
});
|
|
22659
24066
|
} finally {
|
|
22660
24067
|
rl.close();
|
|
22661
24068
|
}
|
|
22662
24069
|
}
|
|
24070
|
+
var ask = (question) => askLine(question);
|
|
24071
|
+
function parseChoice(answer, choices, fallback) {
|
|
24072
|
+
const a = answer.trim().toLowerCase();
|
|
24073
|
+
if (a === "") return { id: fallback, recognized: true };
|
|
24074
|
+
const byNumber = Number.parseInt(a, 10);
|
|
24075
|
+
if (Number.isInteger(byNumber) && byNumber >= 1 && byNumber <= choices.length) {
|
|
24076
|
+
return { id: choices[byNumber - 1].id, recognized: true };
|
|
24077
|
+
}
|
|
24078
|
+
const byId = choices.find((c) => c.id.toLowerCase() === a || c.label.toLowerCase() === a);
|
|
24079
|
+
if (byId) return { id: byId.id, recognized: true };
|
|
24080
|
+
return { id: fallback, recognized: false };
|
|
24081
|
+
}
|
|
24082
|
+
function parseMultiSelect(answer, choices, fallback) {
|
|
24083
|
+
const a = answer.trim().toLowerCase();
|
|
24084
|
+
if (a === "") return { ids: [...fallback], recognized: true };
|
|
24085
|
+
const picked = /* @__PURE__ */ new Set();
|
|
24086
|
+
for (const part of a.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
24087
|
+
const n = Number.parseInt(part, 10);
|
|
24088
|
+
if (Number.isInteger(n) && n >= 1 && n <= choices.length) {
|
|
24089
|
+
picked.add(choices[n - 1].id);
|
|
24090
|
+
continue;
|
|
24091
|
+
}
|
|
24092
|
+
const byId = choices.find((c) => c.id.toLowerCase() === part || c.label.toLowerCase() === part);
|
|
24093
|
+
if (byId) picked.add(byId.id);
|
|
24094
|
+
}
|
|
24095
|
+
if (picked.size === 0) return { ids: [...fallback], recognized: false };
|
|
24096
|
+
return { ids: choices.filter((c) => picked.has(c.id)).map((c) => c.id), recognized: true };
|
|
24097
|
+
}
|
|
24098
|
+
async function promptYes(question, opts) {
|
|
24099
|
+
if (!interactive()) return opts.nonInteractive;
|
|
24100
|
+
const answer = await ask(question);
|
|
24101
|
+
if (answer === null) return opts.nonInteractive;
|
|
24102
|
+
const a = answer.toLowerCase();
|
|
24103
|
+
return a === "" || a === "y" || a === "yes";
|
|
24104
|
+
}
|
|
24105
|
+
function printOptions(question, choices) {
|
|
24106
|
+
console.log("");
|
|
24107
|
+
console.log(` ${question}`);
|
|
24108
|
+
choices.forEach((c, i) => {
|
|
24109
|
+
const tag = c.recommended ? " (recommended)" : "";
|
|
24110
|
+
console.log(` ${i + 1}. ${c.label}${tag}${c.hint ? ` \u2014 ${c.hint}` : ""}`);
|
|
24111
|
+
});
|
|
24112
|
+
}
|
|
24113
|
+
async function promptChoice(question, choices, fallback) {
|
|
24114
|
+
if (!interactive()) return fallback;
|
|
24115
|
+
printOptions(question, choices);
|
|
24116
|
+
const defaultIdx = choices.findIndex((c) => c.id === fallback);
|
|
24117
|
+
const answer = await ask(` Choose [${defaultIdx + 1}]: `);
|
|
24118
|
+
if (answer === null) {
|
|
24119
|
+
console.log(` (no answer \u2014 using ${fallback})`);
|
|
24120
|
+
return fallback;
|
|
24121
|
+
}
|
|
24122
|
+
const parsed = parseChoice(answer, choices, fallback);
|
|
24123
|
+
if (!parsed.recognized) console.log(` Unrecognized answer "${answer}" \u2014 using ${fallback}.`);
|
|
24124
|
+
return parsed.id;
|
|
24125
|
+
}
|
|
24126
|
+
async function promptMultiSelect(question, choices, fallback) {
|
|
24127
|
+
if (!interactive()) return [...fallback];
|
|
24128
|
+
printOptions(question, choices);
|
|
24129
|
+
const defaultLabel = choices.map((c, i) => fallback.includes(c.id) ? String(i + 1) : null).filter(Boolean).join(",");
|
|
24130
|
+
const answer = await ask(` Choose one or more, comma-separated [${defaultLabel}]: `);
|
|
24131
|
+
if (answer === null) {
|
|
24132
|
+
console.log(" (no answer \u2014 using the default)");
|
|
24133
|
+
return [...fallback];
|
|
24134
|
+
}
|
|
24135
|
+
const parsed = parseMultiSelect(answer, choices, fallback);
|
|
24136
|
+
if (!parsed.recognized) console.log(` Unrecognized answer "${answer}" \u2014 using the default.`);
|
|
24137
|
+
return parsed.ids;
|
|
24138
|
+
}
|
|
24139
|
+
|
|
24140
|
+
// src/commands/init.ts
|
|
22663
24141
|
async function confirmExistingLogin(serviceUrl, remote, opts) {
|
|
22664
24142
|
const existing = await resolveToken(opts.token);
|
|
22665
24143
|
if (!existing.ok) return "drive-login";
|
|
@@ -22717,7 +24195,7 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
22717
24195
|
}
|
|
22718
24196
|
let remote = "";
|
|
22719
24197
|
try {
|
|
22720
|
-
remote = (0,
|
|
24198
|
+
remote = (0, import_node_child_process14.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
22721
24199
|
} catch {
|
|
22722
24200
|
}
|
|
22723
24201
|
if (!healed) {
|
|
@@ -22728,7 +24206,7 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
22728
24206
|
printInfo("Verity runs in local-only mode: the gate still runs and shows static findings, but nothing uploads.");
|
|
22729
24207
|
printInfo(' Authenticate anytime: run "verity login" (one login covers every repo you can write to).');
|
|
22730
24208
|
};
|
|
22731
|
-
if (
|
|
24209
|
+
if (interactive() && !opts.yes) {
|
|
22732
24210
|
console.log("");
|
|
22733
24211
|
console.log(" Signing in is optional. What it does:");
|
|
22734
24212
|
console.log(" - Confirms which repositories you can write to. The GitHub token is");
|
|
@@ -22743,9 +24221,12 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
22743
24221
|
console.log(" findings, but nothing is uploaded.");
|
|
22744
24222
|
console.log("");
|
|
22745
24223
|
}
|
|
22746
|
-
const wantsAuth =
|
|
24224
|
+
const wantsAuth = opts.yes ? false : await promptYes(
|
|
24225
|
+
"Authenticate with GitHub now to upload results to Verity? [Y/skip] ",
|
|
24226
|
+
{ nonInteractive: false }
|
|
24227
|
+
);
|
|
22747
24228
|
if (!wantsAuth) {
|
|
22748
|
-
printInfo("Skipped authentication.");
|
|
24229
|
+
printInfo(opts.yes ? "Skipped authentication (unattended run)." : "Skipped authentication.");
|
|
22749
24230
|
localOnlyNote();
|
|
22750
24231
|
return;
|
|
22751
24232
|
}
|
|
@@ -22768,7 +24249,7 @@ function resolveDataDir() {
|
|
|
22768
24249
|
// local dev: running from repo root
|
|
22769
24250
|
];
|
|
22770
24251
|
for (const candidate of candidates) {
|
|
22771
|
-
if ((0,
|
|
24252
|
+
if ((0, import_node_fs44.existsSync)((0, import_node_path29.join)(candidate, "skills"))) {
|
|
22772
24253
|
return candidate;
|
|
22773
24254
|
}
|
|
22774
24255
|
}
|
|
@@ -22777,22 +24258,197 @@ function resolveDataDir() {
|
|
|
22777
24258
|
);
|
|
22778
24259
|
}
|
|
22779
24260
|
async function copyDir(src, dest) {
|
|
22780
|
-
await (0,
|
|
22781
|
-
await (0,
|
|
24261
|
+
await (0, import_promises14.mkdir)(dest, { recursive: true });
|
|
24262
|
+
await (0, import_promises14.cp)(src, dest, { recursive: true, force: true });
|
|
24263
|
+
}
|
|
24264
|
+
async function skillIsCurrent(src, dest) {
|
|
24265
|
+
const list2 = (dir) => {
|
|
24266
|
+
const out = [];
|
|
24267
|
+
const walk = (d, prefix) => {
|
|
24268
|
+
for (const e of (0, import_node_fs44.readdirSync)(d, { withFileTypes: true })) {
|
|
24269
|
+
const rel = prefix ? `${prefix}/${e.name}` : e.name;
|
|
24270
|
+
if (e.isDirectory()) walk((0, import_node_path29.join)(d, e.name), rel);
|
|
24271
|
+
else if (e.isFile()) out.push(rel);
|
|
24272
|
+
}
|
|
24273
|
+
};
|
|
24274
|
+
walk(dir, "");
|
|
24275
|
+
return out.sort();
|
|
24276
|
+
};
|
|
24277
|
+
try {
|
|
24278
|
+
const shipped = list2(src);
|
|
24279
|
+
if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
|
|
24280
|
+
for (const rel of shipped) {
|
|
24281
|
+
const a = await (0, import_promises14.readFile)((0, import_node_path29.join)(src, rel), "utf-8");
|
|
24282
|
+
const b = await (0, import_promises14.readFile)((0, import_node_path29.join)(dest, rel), "utf-8");
|
|
24283
|
+
if (a !== b) return false;
|
|
24284
|
+
}
|
|
24285
|
+
return true;
|
|
24286
|
+
} catch {
|
|
24287
|
+
return false;
|
|
24288
|
+
}
|
|
24289
|
+
}
|
|
24290
|
+
var SKILLS = [
|
|
24291
|
+
"verity-setup",
|
|
24292
|
+
"verity-analyze",
|
|
24293
|
+
"verity-status",
|
|
24294
|
+
"verity-feedback",
|
|
24295
|
+
"verity-learn",
|
|
24296
|
+
"verity-memory",
|
|
24297
|
+
"verity-insights",
|
|
24298
|
+
"verity-reflect"
|
|
24299
|
+
];
|
|
24300
|
+
var INTENSITY_CHOICES = [
|
|
24301
|
+
{ id: "lightweight", label: "lightweight", hint: "critical security only, fastest (~3s)" },
|
|
24302
|
+
{ id: "balanced", label: "balanced", hint: "security + quality (~8s)", recommended: true },
|
|
24303
|
+
{ id: "thorough", label: "thorough", hint: "all tools, all rules (~15s)" }
|
|
24304
|
+
];
|
|
24305
|
+
var MOMENT_CHOICES = [
|
|
24306
|
+
{ id: "stop", label: "On stop", hint: "after every agent turn \u2014 fast feedback while you work", recommended: true },
|
|
24307
|
+
{ id: "pre-commit", label: "Before commit", hint: "reviews the staged diff, blocks the commit on FAIL" },
|
|
24308
|
+
{ id: "pre-push", label: "Before push / PR", hint: "reviews the to-be-pushed commits, blocks on FAIL" }
|
|
24309
|
+
];
|
|
24310
|
+
var DEFAULT_MOMENTS = ["stop"];
|
|
24311
|
+
async function askSetupQuestions(defaultsOnly, previous) {
|
|
24312
|
+
const intensityDefault = previous?.intensity ?? "balanced";
|
|
24313
|
+
const momentsDefault = previous?.moments?.length ? previous.moments : DEFAULT_MOMENTS;
|
|
24314
|
+
if (defaultsOnly) {
|
|
24315
|
+
return { intensity: intensityDefault, moments: momentsDefault, telemetry: "not-asked" };
|
|
24316
|
+
}
|
|
24317
|
+
if (previous?.intensity || previous?.moments) {
|
|
24318
|
+
printInfo(`Current: ${intensityDefault} \xB7 ${momentsDefault.join(", ")} \u2014 press Enter to keep either.`);
|
|
24319
|
+
}
|
|
24320
|
+
const intensity = await promptChoice(
|
|
24321
|
+
"Analysis intensity \u2014 how deeply should Verity review?",
|
|
24322
|
+
INTENSITY_CHOICES,
|
|
24323
|
+
intensityDefault
|
|
24324
|
+
);
|
|
24325
|
+
const moments = await promptMultiSelect(
|
|
24326
|
+
"When should Verity review your code?",
|
|
24327
|
+
MOMENT_CHOICES,
|
|
24328
|
+
momentsDefault
|
|
24329
|
+
);
|
|
24330
|
+
const current = await checkTelemetry();
|
|
24331
|
+
if (current.enabled) {
|
|
24332
|
+
printInfo(`Cost & usage telemetry: already enabled \u2192 ${current.endpoint}`);
|
|
24333
|
+
printInfo(' (turn it off with "verity telemetry uninstall")');
|
|
24334
|
+
return { intensity, moments, telemetry: "already-on" };
|
|
24335
|
+
}
|
|
24336
|
+
console.log("");
|
|
24337
|
+
console.log(" Cost & usage telemetry (opt-in) \u2014 powers the /usage dashboard.");
|
|
24338
|
+
console.log(" Sends Claude Code's own OpenTelemetry metrics and traces only: model names,");
|
|
24339
|
+
console.log(" token counts, USD cost, agent types, session ids. NOT your prompts, code, or");
|
|
24340
|
+
console.log(" tool input/output. Without it, /usage stays empty.");
|
|
24341
|
+
const wants = await promptYes(" Enable cost & usage telemetry? [Y/n] ", { nonInteractive: false });
|
|
24342
|
+
return { intensity, moments, telemetry: wants ? "yes" : "no" };
|
|
24343
|
+
}
|
|
24344
|
+
function insideClaudeCode() {
|
|
24345
|
+
return !!process.env.CLAUDECODE || !!process.env.CLAUDE_SESSION_ID;
|
|
24346
|
+
}
|
|
24347
|
+
function resumeDeferredPending(previous) {
|
|
24348
|
+
return previous?.telemetry === "deferred";
|
|
24349
|
+
}
|
|
24350
|
+
var PHASE_TWO_ARTIFACTS = [
|
|
24351
|
+
{
|
|
24352
|
+
path: ".verity/standard.yaml",
|
|
24353
|
+
what: "the Standard, synthesized from your codebase",
|
|
24354
|
+
// The presence flag travels WITH the row. Read positionally from a parallel
|
|
24355
|
+
// array, a reorder of this list would silently move every ✓ onto the wrong
|
|
24356
|
+
// path — a report that is confidently wrong about what got created.
|
|
24357
|
+
present: (a) => a.standard
|
|
24358
|
+
},
|
|
24359
|
+
{
|
|
24360
|
+
path: ".codacy/codacy.config.json",
|
|
24361
|
+
what: "curated static-analysis patterns",
|
|
24362
|
+
present: (a) => a.analysisConfig
|
|
24363
|
+
},
|
|
24364
|
+
{
|
|
24365
|
+
path: "VERITY.md",
|
|
24366
|
+
what: "project quality overview",
|
|
24367
|
+
present: (a) => a.verityMd
|
|
24368
|
+
}
|
|
24369
|
+
];
|
|
24370
|
+
async function reportPhaseTwo(startedAt) {
|
|
24371
|
+
const elapsed = Math.round((Date.now() - startedAt) / 1e3);
|
|
24372
|
+
let report = null;
|
|
24373
|
+
try {
|
|
24374
|
+
report = await buildReport();
|
|
24375
|
+
} catch {
|
|
24376
|
+
}
|
|
24377
|
+
console.log("");
|
|
24378
|
+
if (!report) {
|
|
24379
|
+
printWarn('Could not verify what the setup session produced \u2014 run "verity doctor".');
|
|
24380
|
+
return;
|
|
24381
|
+
}
|
|
24382
|
+
const { artifacts } = report;
|
|
24383
|
+
const complete = PHASE_TWO_ARTIFACTS.every((a) => a.present(artifacts));
|
|
24384
|
+
printInfo(complete ? `Setup complete (${elapsed}s).` : `Setup session ended after ${elapsed}s.`);
|
|
24385
|
+
for (const { path, what, present } of PHASE_TWO_ARTIFACTS) {
|
|
24386
|
+
const ok = present(artifacts);
|
|
24387
|
+
console.log(` ${ok ? "\u2713" : "\xB7"} ${path.padEnd(30)} ${ok ? what : `${what} \u2014 NOT created`}`);
|
|
24388
|
+
}
|
|
24389
|
+
if (!complete) {
|
|
24390
|
+
console.log("");
|
|
24391
|
+
printWarn('Setup did not finish. Re-run "/verity-setup" in Claude Code to complete it \u2014');
|
|
24392
|
+
printWarn(" nothing is lost; it picks up from what is already on disk.");
|
|
24393
|
+
}
|
|
24394
|
+
}
|
|
24395
|
+
async function handoffToSetup(enabled, claudeInstalled) {
|
|
24396
|
+
const instruct = (why) => {
|
|
24397
|
+
console.log("");
|
|
24398
|
+
printInfo("Still to do \u2014 this is what /verity-setup does (it needs a model):");
|
|
24399
|
+
for (const { path, what } of PHASE_TWO_ARTIFACTS) {
|
|
24400
|
+
console.log(` ${path.padEnd(30)} ${what}`);
|
|
24401
|
+
}
|
|
24402
|
+
console.log("");
|
|
24403
|
+
printInfo("Next step: run /verity-setup in Claude Code.");
|
|
24404
|
+
printInfo(` (${why})`);
|
|
24405
|
+
};
|
|
24406
|
+
if (!enabled) return instruct("--no-setup was passed");
|
|
24407
|
+
if (insideClaudeCode()) return instruct("you are already in a Claude Code session \u2014 invoke the skill there");
|
|
24408
|
+
if (!interactive()) return instruct("no interactive terminal here");
|
|
24409
|
+
if (!claudeInstalled) return instruct("Claude Code is not installed on this machine yet");
|
|
24410
|
+
printPhase(2, 2, "your Standard", "reading the codebase \xB7 synthesizing the Standard \xB7 curating patterns");
|
|
24411
|
+
console.log(" Claude Code takes over the screen from here. It will:");
|
|
24412
|
+
console.log(" \xB7 read your codebase \u2014 languages, frameworks, architecture");
|
|
24413
|
+
console.log(" \xB7 synthesize .verity/standard.yaml and show it to you");
|
|
24414
|
+
console.log(" \xB7 write the analysis config, then VERITY.md");
|
|
24415
|
+
console.log(" Usually a minute or two. Quit any time \u2014 re-running /verity-setup resumes.");
|
|
24416
|
+
console.log("");
|
|
24417
|
+
const startedAt = Date.now();
|
|
24418
|
+
const run2 = (0, import_node_child_process14.spawnSync)("claude", ["/verity-setup"], { stdio: "inherit" });
|
|
24419
|
+
if (run2.error) {
|
|
24420
|
+
printWarn(`Could not start Claude Code: ${run2.error.message}`);
|
|
24421
|
+
return instruct("start it yourself and run the skill there");
|
|
24422
|
+
}
|
|
24423
|
+
await reportPhaseTwo(startedAt);
|
|
22782
24424
|
}
|
|
22783
24425
|
function registerInitCommand(program2) {
|
|
22784
|
-
program2.command("init").description("
|
|
24426
|
+
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) => {
|
|
22785
24427
|
const force = opts.force ?? false;
|
|
24428
|
+
const wantsHandoff = opts.setup !== false;
|
|
24429
|
+
const defaultsOnly = (opts.yes ?? false) || !interactive();
|
|
22786
24430
|
const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
|
|
22787
|
-
const isProject = projectMarkers.some((m) => (0,
|
|
24431
|
+
const isProject = projectMarkers.some((m) => (0, import_node_fs44.existsSync)(m));
|
|
22788
24432
|
if (!isProject) {
|
|
22789
24433
|
printError("No project detected in the current directory.");
|
|
22790
24434
|
printInfo('Run "verity init" from your project root.');
|
|
22791
24435
|
process.exit(1);
|
|
22792
24436
|
}
|
|
22793
|
-
|
|
22794
|
-
|
|
22795
|
-
|
|
24437
|
+
const showArt = canRenderArt() && !insideClaudeCode();
|
|
24438
|
+
printBanner({ interactive: showArt });
|
|
24439
|
+
if (!showArt) {
|
|
24440
|
+
console.log("");
|
|
24441
|
+
printInfo("Initializing Verity in this project...");
|
|
24442
|
+
console.log("");
|
|
24443
|
+
} else {
|
|
24444
|
+
printPhase(1, 2, "this machine", "prerequisites \xB7 skills \xB7 hooks \xB7 sign-in");
|
|
24445
|
+
}
|
|
24446
|
+
const TOTAL_STEPS = 8;
|
|
24447
|
+
let stepNo = 0;
|
|
24448
|
+
const step = (label2) => {
|
|
24449
|
+
stepNo++;
|
|
24450
|
+
printInfo(`${DIM}[${stepNo}/${TOTAL_STEPS}]${NC} ${label2}`);
|
|
24451
|
+
};
|
|
22796
24452
|
if (await needsMigration()) {
|
|
22797
24453
|
printInfo("Legacy GATE.md install detected \u2014 migrating to Verity...");
|
|
22798
24454
|
try {
|
|
@@ -22803,143 +24459,171 @@ function registerInitCommand(program2) {
|
|
|
22803
24459
|
}
|
|
22804
24460
|
console.log("");
|
|
22805
24461
|
}
|
|
22806
|
-
|
|
22807
|
-
const
|
|
22808
|
-
const
|
|
22809
|
-
|
|
22810
|
-
|
|
22811
|
-
|
|
24462
|
+
step("Checking prerequisites");
|
|
24463
|
+
const prereqs = await checkPrereqs({ install: true });
|
|
24464
|
+
for (const c of prereqs.checks) {
|
|
24465
|
+
if (c.status === "ok") {
|
|
24466
|
+
if (c.justInstalled) continue;
|
|
24467
|
+
printInfo(` ${c.label} ${c.detail} \u2713`);
|
|
24468
|
+
} else {
|
|
24469
|
+
printWarn(` ${c.label}: ${c.detail}`);
|
|
24470
|
+
if (c.remedy) printWarn(` ${c.remedy}`);
|
|
24471
|
+
}
|
|
22812
24472
|
}
|
|
22813
|
-
|
|
22814
|
-
|
|
22815
|
-
const gitVersion = (0, import_node_child_process11.execSync)("git --version", { encoding: "utf-8" }).trim();
|
|
22816
|
-
printInfo(` ${gitVersion} \u2713`);
|
|
22817
|
-
} catch {
|
|
22818
|
-
printError("git is required but not installed. Install from https://git-scm.com");
|
|
24473
|
+
if (prereqs.blocked) {
|
|
24474
|
+
printError("A required prerequisite is missing \u2014 cannot continue.");
|
|
22819
24475
|
process.exit(1);
|
|
22820
24476
|
}
|
|
22821
|
-
|
|
22822
|
-
(0, import_node_child_process11.execSync)("which claude", { encoding: "utf-8" });
|
|
22823
|
-
printInfo(" Claude Code \u2713");
|
|
22824
|
-
} catch {
|
|
22825
|
-
printWarn(" Claude Code not found \u2014 hooks will be configured but need Claude Code to run.");
|
|
22826
|
-
}
|
|
22827
|
-
try {
|
|
22828
|
-
(0, import_node_child_process11.execSync)("which codacy-analysis", { encoding: "utf-8", stdio: "pipe" });
|
|
22829
|
-
printInfo(" @codacy/analysis-cli \u2713");
|
|
22830
|
-
} catch {
|
|
22831
|
-
printInfo(" Installing @codacy/analysis-cli...");
|
|
22832
|
-
try {
|
|
22833
|
-
(0, import_node_child_process11.execSync)("npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "pipe", timeout: 12e4 });
|
|
22834
|
-
printInfo(" @codacy/analysis-cli installed \u2713");
|
|
22835
|
-
} catch {
|
|
22836
|
-
try {
|
|
22837
|
-
printWarn(" Retrying with sudo...");
|
|
22838
|
-
(0, import_node_child_process11.execSync)("sudo npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
|
|
22839
|
-
printInfo(" @codacy/analysis-cli installed \u2713");
|
|
22840
|
-
} catch {
|
|
22841
|
-
printWarn(" Could not install @codacy/analysis-cli automatically.");
|
|
22842
|
-
printWarn(" Install manually: npm install -g @codacy/analysis-cli");
|
|
22843
|
-
printWarn(" Static analysis will be unavailable until installed.");
|
|
22844
|
-
}
|
|
22845
|
-
}
|
|
22846
|
-
}
|
|
24477
|
+
const claudeInstalled = prereqs.checks.some((c) => c.id === "claude" && c.status === "ok");
|
|
22847
24478
|
console.log("");
|
|
22848
|
-
|
|
24479
|
+
step("Installing skills");
|
|
22849
24480
|
const dataDir = resolveDataDir();
|
|
22850
24481
|
const skillsSource = (0, import_node_path29.join)(dataDir, "skills");
|
|
22851
24482
|
const skillsDest = ".claude/skills";
|
|
22852
|
-
const skills = ["verity-setup", "verity-analyze", "verity-status", "verity-feedback", "verity-learn", "verity-memory", "verity-insights", "verity-reflect"];
|
|
22853
24483
|
let skillsInstalled = 0;
|
|
22854
|
-
for (const skill of
|
|
24484
|
+
for (const skill of SKILLS) {
|
|
22855
24485
|
const src = (0, import_node_path29.join)(skillsSource, skill);
|
|
22856
24486
|
const dest = (0, import_node_path29.join)(skillsDest, skill);
|
|
22857
|
-
if (!(0,
|
|
24487
|
+
if (!(0, import_node_fs44.existsSync)(src)) {
|
|
22858
24488
|
printWarn(` Skill data not found: ${skill}`);
|
|
22859
24489
|
continue;
|
|
22860
24490
|
}
|
|
22861
|
-
if ((0,
|
|
22862
|
-
|
|
22863
|
-
|
|
22864
|
-
if ((0, import_node_fs41.existsSync)(destSkill)) {
|
|
22865
|
-
try {
|
|
22866
|
-
const srcContent = await (0, import_promises13.readFile)(srcSkill, "utf-8");
|
|
22867
|
-
const destContent = await (0, import_promises13.readFile)(destSkill, "utf-8");
|
|
22868
|
-
if (srcContent === destContent) {
|
|
22869
|
-
skillsInstalled++;
|
|
22870
|
-
continue;
|
|
22871
|
-
}
|
|
22872
|
-
} catch {
|
|
22873
|
-
}
|
|
22874
|
-
}
|
|
24491
|
+
if ((0, import_node_fs44.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) {
|
|
24492
|
+
skillsInstalled++;
|
|
24493
|
+
continue;
|
|
22875
24494
|
}
|
|
22876
24495
|
await copyDir(src, dest);
|
|
22877
24496
|
skillsInstalled++;
|
|
22878
24497
|
}
|
|
22879
|
-
printInfo(` ${skillsInstalled}/${
|
|
22880
|
-
|
|
22881
|
-
const
|
|
22882
|
-
const
|
|
22883
|
-
const
|
|
22884
|
-
if (
|
|
22885
|
-
|
|
22886
|
-
printInfo(" Stop hook: verity analyze \u2713");
|
|
22887
|
-
printInfo(" Intent hook: verity intent capture \u2713");
|
|
22888
|
-
printInfo(" Baseline hook: verity baseline capture \u2713");
|
|
22889
|
-
} else {
|
|
22890
|
-
printWarn(` ${hookResult.error}`);
|
|
22891
|
-
printInfo(' Run "verity hooks install --force" to overwrite.');
|
|
24498
|
+
printInfo(` ${skillsInstalled}/${SKILLS.length} skills installed to .claude/skills/ \u2713`);
|
|
24499
|
+
step(defaultsOnly ? "Setup answers (defaults)" : "Your setup answers");
|
|
24500
|
+
const previous = await readSetupState();
|
|
24501
|
+
const answers = await askSetupQuestions(defaultsOnly, previous);
|
|
24502
|
+
const { intensity, moments } = answers;
|
|
24503
|
+
if (defaultsOnly) {
|
|
24504
|
+
printInfo(` intensity: ${intensity} \xB7 moments: ${moments.join(", ") || "none"} (no questions asked)`);
|
|
22892
24505
|
}
|
|
22893
|
-
|
|
24506
|
+
step("Knowledge base, .gitignore and CLAUDE.md");
|
|
24507
|
+
await (0, import_promises14.mkdir)(VERITY_DIR, { recursive: true });
|
|
22894
24508
|
await ensureMemoryDir();
|
|
22895
|
-
const ignoreResult =
|
|
24509
|
+
const ignoreResult = ensureVerityGitignore();
|
|
22896
24510
|
if (ignoreResult === "failed") {
|
|
22897
|
-
printWarn(" .gitignore: could not
|
|
24511
|
+
printWarn(" .gitignore: could not write the Verity block \u2014 add it manually");
|
|
24512
|
+
printWarn(" (.verity/ holds copies of analyzed files, including any secret the gate flagged)");
|
|
24513
|
+
} else if (ignoreResult === "conflict") {
|
|
24514
|
+
printWarn(" .gitignore: the Verity block is in place but git still ignores .verity/standard.yaml");
|
|
24515
|
+
printWarn(" Something outside this file covers it \u2014 a global (~/.gitignore) or nested");
|
|
24516
|
+
printWarn(" .gitignore, or a pattern we do not recognise. Check: git check-ignore -v .verity/standard.yaml");
|
|
24517
|
+
printWarn(" Until it is fixed, the Standard and the knowledge graph cannot be committed.");
|
|
24518
|
+
} else if (ignoreResult === "repaired") {
|
|
24519
|
+
printInfo(" .gitignore: rewrote `.verity/` to `.verity/*` so the standard stays committable \u2713");
|
|
22898
24520
|
} else {
|
|
22899
|
-
printInfo(` .gitignore:
|
|
24521
|
+
printInfo(` .gitignore: Verity block ${ignoreResult === "added" ? "added" : "already covered"} \u2713`);
|
|
24522
|
+
}
|
|
24523
|
+
const tracked = committedVerityState();
|
|
24524
|
+
if (tracked.length > 0) {
|
|
24525
|
+
printWarn(` ${tracked.length} Verity state file(s) are tracked in git (e.g. ${tracked[0]}).`);
|
|
24526
|
+
const untrack = defaultsOnly ? false : await promptYes(" Untrack them now (files stay on disk)? [Y/n] ", { nonInteractive: false });
|
|
24527
|
+
if (untrack) {
|
|
24528
|
+
const result = untrackVerityState();
|
|
24529
|
+
if (result === "untracked") printInfo(" Untracked (staged) \u2014 commit to finish \u2713");
|
|
24530
|
+
else if (result === "failed") printWarn(' Could not untrack \u2014 run "git rm -r --cached .verity" manually');
|
|
24531
|
+
} else {
|
|
24532
|
+
printWarn(" Left tracked. Fix with: git rm -r --cached .verity && git add .verity/standard.yaml .verity/memory");
|
|
24533
|
+
}
|
|
22900
24534
|
}
|
|
22901
24535
|
try {
|
|
22902
24536
|
await ensureClaudeMdPointer();
|
|
22903
|
-
printInfo(" CLAUDE.md
|
|
24537
|
+
printInfo(" CLAUDE.md instructions \u2713");
|
|
22904
24538
|
} catch (err) {
|
|
22905
24539
|
printWarn(` Could not update CLAUDE.md: ${err.message}`);
|
|
22906
24540
|
}
|
|
22907
24541
|
const globalVerityDir = (0, import_node_path29.join)(process.env.HOME ?? "", ".verity");
|
|
22908
|
-
await (0,
|
|
24542
|
+
await (0, import_promises14.mkdir)(globalVerityDir, { recursive: true });
|
|
22909
24543
|
console.log("");
|
|
24544
|
+
step("Wiring Claude Code hooks");
|
|
24545
|
+
await applyMomentSelection(moments);
|
|
24546
|
+
const hookStatus = await checkAllVerityHooks();
|
|
24547
|
+
printInfo(` Stop (verity analyze): ${hookStatus.stop ? "on" : "off"}`);
|
|
24548
|
+
printInfo(` Pre-commit gate: ${hookStatus.guardOn.includes("commit") ? "on" : "off"}`);
|
|
24549
|
+
printInfo(` Pre-push/PR gate: ${hookStatus.guardOn.includes("push") ? "on" : "off"}`);
|
|
24550
|
+
printInfo(` Intent + baseline + compact + session-end: always on \u2713`);
|
|
24551
|
+
if (!hookStatus.stop && hookStatus.guardOn.length === 0) {
|
|
24552
|
+
printWarn(" No analysis moment is active \u2014 code changes will NOT be reviewed.");
|
|
24553
|
+
printWarn(" Enable one: verity hooks install --moments stop");
|
|
24554
|
+
}
|
|
24555
|
+
console.log("");
|
|
24556
|
+
step("Sign in to Verity (optional)");
|
|
22910
24557
|
try {
|
|
22911
24558
|
const globals = program2.opts();
|
|
22912
24559
|
const resolution = await resolveServiceUrlForAuth(globals.serviceUrl);
|
|
22913
24560
|
await runOptionalAuth(resolution, {
|
|
22914
24561
|
token: globals.token,
|
|
22915
|
-
verbose: globals.verbose
|
|
24562
|
+
verbose: globals.verbose,
|
|
24563
|
+
yes: defaultsOnly
|
|
22916
24564
|
});
|
|
22917
24565
|
} catch (err) {
|
|
22918
24566
|
printWarn(`Authentication step skipped: ${err.message}`);
|
|
22919
24567
|
}
|
|
24568
|
+
const resumeDeferred = answers.telemetry === "not-asked" && resumeDeferredPending(previous);
|
|
24569
|
+
const wantsTelemetry = answers.telemetry === "yes" || resumeDeferred;
|
|
24570
|
+
step("Cost & usage telemetry");
|
|
24571
|
+
if (answers.telemetry === "not-asked" && !resumeDeferredPending(previous)) {
|
|
24572
|
+
printInfo(' not asked (unattended run) \u2014 enable later with "verity telemetry install"');
|
|
24573
|
+
} else if (answers.telemetry === "no") {
|
|
24574
|
+
printInfo(' declined \u2014 enable later with "verity telemetry install"');
|
|
24575
|
+
}
|
|
24576
|
+
let telemetryChoice = answers.telemetry === "already-on" ? "enabled" : answers.telemetry === "no" ? "declined" : wantsTelemetry ? "deferred" : void 0;
|
|
24577
|
+
if (wantsTelemetry) {
|
|
24578
|
+
const globals = program2.opts();
|
|
24579
|
+
const token = await resolveToken(globals.token);
|
|
24580
|
+
const url = await resolveServiceUrl(globals.serviceUrl);
|
|
24581
|
+
if (token.ok && url.ok) {
|
|
24582
|
+
const installed2 = await installTelemetry(url.data);
|
|
24583
|
+
if (installed2.ok) {
|
|
24584
|
+
telemetryChoice = "enabled";
|
|
24585
|
+
printInfo(`Telemetry enabled \u2192 ${installed2.data.endpoint}`);
|
|
24586
|
+
printInfo(" takes effect on your NEXT Claude Code session; view cost & usage at /usage");
|
|
24587
|
+
} else {
|
|
24588
|
+
printWarn(`Could not enable telemetry: ${installed2.error}`);
|
|
24589
|
+
}
|
|
24590
|
+
} else {
|
|
24591
|
+
printWarn('Telemetry needs a Verity token \u2014 run "verity login", then "verity telemetry install".');
|
|
24592
|
+
}
|
|
24593
|
+
}
|
|
24594
|
+
step("Recording your answers");
|
|
24595
|
+
try {
|
|
24596
|
+
await writeSetupState({
|
|
24597
|
+
intensity,
|
|
24598
|
+
moments,
|
|
24599
|
+
...telemetryChoice ? { telemetry: telemetryChoice } : {},
|
|
24600
|
+
init: {
|
|
24601
|
+
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24602
|
+
cli_version: true ? "0.31.1-experimental.68fca47" : "dev"
|
|
24603
|
+
}
|
|
24604
|
+
});
|
|
24605
|
+
} catch (err) {
|
|
24606
|
+
printWarn(`Could not record setup answers: ${err.message}`);
|
|
24607
|
+
}
|
|
22920
24608
|
console.log("");
|
|
22921
|
-
printInfo("
|
|
24609
|
+
printInfo("This machine is set up.");
|
|
22922
24610
|
console.log("");
|
|
22923
|
-
console.log("
|
|
22924
|
-
console.log("
|
|
22925
|
-
console.log(" .claude/
|
|
22926
|
-
console.log(" .
|
|
22927
|
-
console.log(" .
|
|
22928
|
-
console.log(" .
|
|
22929
|
-
console.log(" .
|
|
22930
|
-
console.log(" .claude/skills/verity-insights/ \u2014 quality metrics + evolution");
|
|
22931
|
-
console.log(" .claude/skills/verity-reflect/ \u2014 capture learnings");
|
|
22932
|
-
console.log(" .claude/settings.json \u2014 hooks (verity analyze + intent capture)");
|
|
22933
|
-
console.log(" .verity/memory/ \u2014 knowledge base (8 domains, commit to git)");
|
|
24611
|
+
console.log(" .claude/skills/verity-*/ 8 skills (setup, analyze, status, feedback,");
|
|
24612
|
+
console.log(" learn, memory, insights, reflect)");
|
|
24613
|
+
console.log(" .claude/settings.json hooks, reconciled to your chosen moments");
|
|
24614
|
+
console.log(" .verity/memory/ knowledge base (commit to git)");
|
|
24615
|
+
console.log(" .verity/setup.json your answers, read by /verity-setup");
|
|
24616
|
+
console.log(" .gitignore Verity block (whitelist form)");
|
|
24617
|
+
console.log(" CLAUDE.md memory pointer, waive policy, reflection");
|
|
22934
24618
|
console.log("");
|
|
22935
|
-
console.log(
|
|
22936
|
-
|
|
24619
|
+
console.log(` Intensity: ${intensity} Moments: ${moments.join(", ") || "none"}`);
|
|
24620
|
+
await handoffToSetup(wantsHandoff, claudeInstalled);
|
|
22937
24621
|
console.log("");
|
|
22938
24622
|
});
|
|
22939
24623
|
}
|
|
22940
24624
|
|
|
22941
24625
|
// src/commands/uninstall.ts
|
|
22942
|
-
var
|
|
24626
|
+
var import_node_fs45 = require("node:fs");
|
|
22943
24627
|
var import_node_path30 = require("node:path");
|
|
22944
24628
|
var SKILL_NAMES = [
|
|
22945
24629
|
"verity-setup",
|
|
@@ -22960,10 +24644,10 @@ function registerUninstallCommand(program2) {
|
|
|
22960
24644
|
const skillsRoot = projectPath(".claude/skills");
|
|
22961
24645
|
for (const name of SKILL_NAMES) {
|
|
22962
24646
|
const dir = (0, import_node_path30.join)(skillsRoot, name);
|
|
22963
|
-
if ((0,
|
|
24647
|
+
if ((0, import_node_fs45.existsSync)(dir)) {
|
|
22964
24648
|
actions.push({
|
|
22965
24649
|
label: `Remove .claude/skills/${name}/`,
|
|
22966
|
-
apply: () => (0,
|
|
24650
|
+
apply: () => (0, import_node_fs45.rmSync)(dir, { recursive: true, force: true })
|
|
22967
24651
|
});
|
|
22968
24652
|
}
|
|
22969
24653
|
}
|
|
@@ -22977,24 +24661,24 @@ function registerUninstallCommand(program2) {
|
|
|
22977
24661
|
});
|
|
22978
24662
|
}
|
|
22979
24663
|
const verityDir = projectPath(VERITY_DIR);
|
|
22980
|
-
if ((0,
|
|
24664
|
+
if ((0, import_node_fs45.existsSync)(verityDir)) {
|
|
22981
24665
|
actions.push({
|
|
22982
24666
|
label: `Remove ${VERITY_DIR}/`,
|
|
22983
|
-
apply: () => (0,
|
|
24667
|
+
apply: () => (0, import_node_fs45.rmSync)(verityDir, { recursive: true, force: true })
|
|
22984
24668
|
});
|
|
22985
24669
|
}
|
|
22986
24670
|
if (!keepVerityMd) {
|
|
22987
24671
|
const verityMd = projectPath(VERITY_MD_FILE);
|
|
22988
|
-
if ((0,
|
|
24672
|
+
if ((0, import_node_fs45.existsSync)(verityMd)) {
|
|
22989
24673
|
actions.push({
|
|
22990
24674
|
label: `Remove ${VERITY_MD_FILE}`,
|
|
22991
|
-
apply: () => (0,
|
|
24675
|
+
apply: () => (0, import_node_fs45.rmSync)(verityMd, { force: true })
|
|
22992
24676
|
});
|
|
22993
24677
|
}
|
|
22994
24678
|
}
|
|
22995
24679
|
const cleanupEmptyDir = (path) => {
|
|
22996
|
-
if ((0,
|
|
22997
|
-
(0,
|
|
24680
|
+
if ((0, import_node_fs45.existsSync)(path) && (0, import_node_fs45.statSync)(path).isDirectory() && (0, import_node_fs45.readdirSync)(path).length === 0) {
|
|
24681
|
+
(0, import_node_fs45.rmdirSync)(path);
|
|
22998
24682
|
}
|
|
22999
24683
|
};
|
|
23000
24684
|
actions.push({
|
|
@@ -23006,10 +24690,10 @@ function registerUninstallCommand(program2) {
|
|
|
23006
24690
|
});
|
|
23007
24691
|
const home = process.env.HOME ?? "";
|
|
23008
24692
|
const globalVerityDir = (0, import_node_path30.join)(home, ".verity");
|
|
23009
|
-
if (purgeGlobal && (0,
|
|
24693
|
+
if (purgeGlobal && (0, import_node_fs45.existsSync)(globalVerityDir)) {
|
|
23010
24694
|
actions.push({
|
|
23011
24695
|
label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
|
|
23012
|
-
apply: () => (0,
|
|
24696
|
+
apply: () => (0, import_node_fs45.rmSync)(globalVerityDir, { recursive: true, force: true })
|
|
23013
24697
|
});
|
|
23014
24698
|
}
|
|
23015
24699
|
if (actions.length === 0) {
|
|
@@ -23029,7 +24713,7 @@ function registerUninstallCommand(program2) {
|
|
|
23029
24713
|
if (!purgeGlobal) {
|
|
23030
24714
|
printInfo('Saved tokens at ~/.verity/credentials are preserved \u2014 re-run "verity init" to reconnect.');
|
|
23031
24715
|
} else {
|
|
23032
|
-
printWarn(
|
|
24716
|
+
printWarn('Global credentials wiped \u2014 run "verity login" (or "verity init") to reconnect.');
|
|
23033
24717
|
}
|
|
23034
24718
|
});
|
|
23035
24719
|
}
|
|
@@ -23203,7 +24887,7 @@ function registerTaskCommands(program2) {
|
|
|
23203
24887
|
}
|
|
23204
24888
|
|
|
23205
24889
|
// src/commands/reset.ts
|
|
23206
|
-
var
|
|
24890
|
+
var import_node_fs46 = require("node:fs");
|
|
23207
24891
|
var import_node_path31 = require("node:path");
|
|
23208
24892
|
function registerResetCommand(program2) {
|
|
23209
24893
|
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) => {
|
|
@@ -23241,11 +24925,11 @@ function registerResetCommand(program2) {
|
|
|
23241
24925
|
}
|
|
23242
24926
|
const cacheDir = projectPath(CACHE_DIR);
|
|
23243
24927
|
let purged = 0;
|
|
23244
|
-
if ((0,
|
|
23245
|
-
for (const entry of (0,
|
|
24928
|
+
if ((0, import_node_fs46.existsSync)(cacheDir)) {
|
|
24929
|
+
for (const entry of (0, import_node_fs46.readdirSync)(cacheDir)) {
|
|
23246
24930
|
if (entry.startsWith("pending-")) {
|
|
23247
24931
|
try {
|
|
23248
|
-
(0,
|
|
24932
|
+
(0, import_node_fs46.unlinkSync)((0, import_node_path31.join)(cacheDir, entry));
|
|
23249
24933
|
purged++;
|
|
23250
24934
|
} catch {
|
|
23251
24935
|
}
|
|
@@ -23260,19 +24944,19 @@ function registerResetCommand(program2) {
|
|
|
23260
24944
|
projectPath(`${VERITY_DIR}/.last-analysis`)
|
|
23261
24945
|
];
|
|
23262
24946
|
for (const file of filesToClear) {
|
|
23263
|
-
if ((0,
|
|
24947
|
+
if ((0, import_node_fs46.existsSync)(file)) {
|
|
23264
24948
|
try {
|
|
23265
|
-
(0,
|
|
24949
|
+
(0, import_node_fs46.writeFileSync)(file, "");
|
|
23266
24950
|
} catch {
|
|
23267
24951
|
}
|
|
23268
24952
|
}
|
|
23269
24953
|
}
|
|
23270
24954
|
if (opts.all) {
|
|
23271
24955
|
const logsDir = projectPath(`${VERITY_DIR}/.logs`);
|
|
23272
|
-
if ((0,
|
|
23273
|
-
for (const entry of (0,
|
|
24956
|
+
if ((0, import_node_fs46.existsSync)(logsDir)) {
|
|
24957
|
+
for (const entry of (0, import_node_fs46.readdirSync)(logsDir)) {
|
|
23274
24958
|
try {
|
|
23275
|
-
(0,
|
|
24959
|
+
(0, import_node_fs46.unlinkSync)((0, import_node_path31.join)(logsDir, entry));
|
|
23276
24960
|
} catch {
|
|
23277
24961
|
}
|
|
23278
24962
|
}
|
|
@@ -23580,8 +25264,8 @@ function registerTelemetryCommands(program2) {
|
|
|
23580
25264
|
}
|
|
23581
25265
|
|
|
23582
25266
|
// src/cli.ts
|
|
23583
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.31.
|
|
23584
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.
|
|
25267
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.31.1-experimental.68fca47").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) => {
|
|
25268
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.1-experimental.68fca47");
|
|
23585
25269
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
23586
25270
|
try {
|
|
23587
25271
|
await foldLegacyLocalCredential();
|
|
@@ -23607,6 +25291,7 @@ registerGuardCommand(program);
|
|
|
23607
25291
|
registerIgnoreCommand(program);
|
|
23608
25292
|
registerWaiveCommand(program);
|
|
23609
25293
|
registerInitCommand(program);
|
|
25294
|
+
registerDoctorCommand(program);
|
|
23610
25295
|
registerUninstallCommand(program);
|
|
23611
25296
|
registerTaskCommands(program);
|
|
23612
25297
|
registerResetCommand(program);
|