@codacy/verity-cli 0.31.1-experimental.80cf3ac → 0.31.1-experimental.b9b3862
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 +121 -0
- package/README.md +50 -3
- package/bin/verity.js +2377 -459
- package/data/skills/verity-setup/SKILL.md +127 -278
- package/package.json +1 -1
package/bin/verity.js
CHANGED
|
@@ -675,8 +675,8 @@ var require_option = __commonJS({
|
|
|
675
675
|
* @param {(string | string[])} names
|
|
676
676
|
* @return {Option}
|
|
677
677
|
*/
|
|
678
|
-
conflicts(
|
|
679
|
-
this.conflictsWith = this.conflictsWith.concat(
|
|
678
|
+
conflicts(names2) {
|
|
679
|
+
this.conflictsWith = this.conflictsWith.concat(names2);
|
|
680
680
|
return this;
|
|
681
681
|
}
|
|
682
682
|
/**
|
|
@@ -1249,8 +1249,8 @@ var require_command = __commonJS({
|
|
|
1249
1249
|
* @param {string} names
|
|
1250
1250
|
* @return {Command} `this` command for chaining
|
|
1251
1251
|
*/
|
|
1252
|
-
arguments(
|
|
1253
|
-
|
|
1252
|
+
arguments(names2) {
|
|
1253
|
+
names2.trim().split(/ +/).forEach((detail) => {
|
|
1254
1254
|
this.argument(detail);
|
|
1255
1255
|
});
|
|
1256
1256
|
return this;
|
|
@@ -10395,6 +10395,7 @@ var MAX_DELTA_BYTES = 194560;
|
|
|
10395
10395
|
var MAX_FILES = 40;
|
|
10396
10396
|
var MAX_FILE_BYTES = 51200;
|
|
10397
10397
|
var DEBOUNCE_SECONDS = 30;
|
|
10398
|
+
var MAX_ITERATIONS = 2;
|
|
10398
10399
|
var MAX_SPEC_FILES = 6;
|
|
10399
10400
|
var MAX_SPEC_FILE_BYTES = 512e3;
|
|
10400
10401
|
var MAX_TOTAL_SPEC_BYTES = 512e3;
|
|
@@ -10519,13 +10520,20 @@ var ADVISORY_EPISODE_FILE = `${VERITY_DIR}/.advisory-episode`;
|
|
|
10519
10520
|
var IGNORE_DECLARATION_FILE = `${VERITY_DIR}/.ignore-declaration`;
|
|
10520
10521
|
|
|
10521
10522
|
// src/lib/output.ts
|
|
10522
|
-
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
|
|
10528
|
-
var
|
|
10523
|
+
function colorEnabled() {
|
|
10524
|
+
if (process.env.FORCE_COLOR) return true;
|
|
10525
|
+
if (process.env.NO_COLOR !== void 0) return false;
|
|
10526
|
+
if (process.env.TERM === "dumb") return false;
|
|
10527
|
+
return !!process.stderr.isTTY;
|
|
10528
|
+
}
|
|
10529
|
+
var COLOR = colorEnabled();
|
|
10530
|
+
var RED = COLOR ? "\x1B[0;31m" : "";
|
|
10531
|
+
var YELLOW = COLOR ? "\x1B[1;33m" : "";
|
|
10532
|
+
var GREEN = COLOR ? "\x1B[0;32m" : "";
|
|
10533
|
+
var CYAN = COLOR ? "\x1B[0;36m" : "";
|
|
10534
|
+
var BOLD = COLOR ? "\x1B[1m" : "";
|
|
10535
|
+
var DIM = COLOR ? "\x1B[2m" : "";
|
|
10536
|
+
var NC = COLOR ? "\x1B[0m" : "";
|
|
10529
10537
|
function printJson(data) {
|
|
10530
10538
|
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
10531
10539
|
}
|
|
@@ -11252,7 +11260,7 @@ async function resolveServiceUrlDetailed(flagUrl, opts = {}) {
|
|
|
11252
11260
|
}
|
|
11253
11261
|
return {
|
|
11254
11262
|
ok: false,
|
|
11255
|
-
error: 'No Verity service URL found. Run "verity login" to get started, or
|
|
11263
|
+
error: 'No Verity service URL found. Run "verity login" to get started, or "verity init" to set up this project.'
|
|
11256
11264
|
};
|
|
11257
11265
|
}
|
|
11258
11266
|
async function resolveServiceUrlForAuth(flagUrl) {
|
|
@@ -11299,7 +11307,7 @@ async function resolveToken(flagToken) {
|
|
|
11299
11307
|
}
|
|
11300
11308
|
return {
|
|
11301
11309
|
ok: false,
|
|
11302
|
-
error: 'No Verity token found. Run "verity login" to sign in, or
|
|
11310
|
+
error: 'No Verity token found. Run "verity login" to sign in, or "verity init" to set up this project.'
|
|
11303
11311
|
};
|
|
11304
11312
|
}
|
|
11305
11313
|
async function whoami(token, serviceUrl, verbose) {
|
|
@@ -11393,6 +11401,91 @@ async function maybeHealServiceUrl(resolution, verbose) {
|
|
|
11393
11401
|
var readline = __toESM(require("node:readline/promises"));
|
|
11394
11402
|
var import_node_os = require("node:os");
|
|
11395
11403
|
|
|
11404
|
+
// src/lib/spinner.ts
|
|
11405
|
+
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
11406
|
+
var FRAME_MS = 80;
|
|
11407
|
+
var DIM2 = "\x1B[2m";
|
|
11408
|
+
var GREEN2 = "\x1B[0;32m";
|
|
11409
|
+
var YELLOW2 = "\x1B[1;33m";
|
|
11410
|
+
var RESET = "\x1B[0m";
|
|
11411
|
+
function secondsSince(start) {
|
|
11412
|
+
return `${Math.round((Date.now() - start) / 1e3)}s`;
|
|
11413
|
+
}
|
|
11414
|
+
function startSpinner(label2, opts = {}) {
|
|
11415
|
+
const stream = process.stderr;
|
|
11416
|
+
const color = colorEnabled();
|
|
11417
|
+
const animate = opts.animate ?? !!stream.isTTY;
|
|
11418
|
+
const showElapsed = opts.elapsed ?? true;
|
|
11419
|
+
const start = Date.now();
|
|
11420
|
+
let current = label2;
|
|
11421
|
+
let frame = 0;
|
|
11422
|
+
let timer = null;
|
|
11423
|
+
let done = false;
|
|
11424
|
+
const fit = (label3, clock, columns) => {
|
|
11425
|
+
const budget = columns - 5;
|
|
11426
|
+
if (budget <= 0) return { label: "", clock: "" };
|
|
11427
|
+
if (label3.length + clock.length <= budget) return { label: label3, clock };
|
|
11428
|
+
if (clock.length >= budget) return { label: "", clock: clock.slice(0, budget) };
|
|
11429
|
+
const room = budget - clock.length;
|
|
11430
|
+
return { label: room <= 1 ? label3.slice(0, room) : `${label3.slice(0, room - 1)}\u2026`, clock };
|
|
11431
|
+
};
|
|
11432
|
+
const paint = () => {
|
|
11433
|
+
const glyph = FRAMES[frame++ % FRAMES.length];
|
|
11434
|
+
const columns = stream.columns ?? 80;
|
|
11435
|
+
const { label: label3, clock } = fit(current, showElapsed ? ` ${secondsSince(start)}` : "", columns);
|
|
11436
|
+
const line = color ? ` ${GREEN2}${glyph}${RESET} ${label3}${DIM2}${clock}${RESET}` : ` ${glyph} ${label3}${clock}`;
|
|
11437
|
+
stream.write(`\r\x1B[2K${line}`);
|
|
11438
|
+
};
|
|
11439
|
+
const clearLine = () => {
|
|
11440
|
+
if (animate) stream.write("\r\x1B[2K");
|
|
11441
|
+
};
|
|
11442
|
+
if (animate) {
|
|
11443
|
+
paint();
|
|
11444
|
+
timer = setInterval(paint, FRAME_MS);
|
|
11445
|
+
timer.unref?.();
|
|
11446
|
+
} else {
|
|
11447
|
+
stream.write(` ${current}\u2026
|
|
11448
|
+
`);
|
|
11449
|
+
}
|
|
11450
|
+
const finish = (render2) => {
|
|
11451
|
+
if (done) return;
|
|
11452
|
+
done = true;
|
|
11453
|
+
if (timer) clearInterval(timer);
|
|
11454
|
+
clearLine();
|
|
11455
|
+
render2();
|
|
11456
|
+
};
|
|
11457
|
+
return {
|
|
11458
|
+
update(next) {
|
|
11459
|
+
current = next;
|
|
11460
|
+
if (animate) paint();
|
|
11461
|
+
else stream.write(` ${next}\u2026
|
|
11462
|
+
`);
|
|
11463
|
+
},
|
|
11464
|
+
succeed(message) {
|
|
11465
|
+
finish(() => {
|
|
11466
|
+
const text = message ?? current;
|
|
11467
|
+
const clock = showElapsed ? ` (${secondsSince(start)})` : "";
|
|
11468
|
+
stream.write(
|
|
11469
|
+
color ? ` ${GREEN2}\u2713${RESET} ${text}${DIM2}${clock}${RESET}
|
|
11470
|
+
` : ` \u2713 ${text}${clock}
|
|
11471
|
+
`
|
|
11472
|
+
);
|
|
11473
|
+
});
|
|
11474
|
+
},
|
|
11475
|
+
warn(message) {
|
|
11476
|
+
finish(() => {
|
|
11477
|
+
stream.write(color ? ` ${YELLOW2}\u26A0${RESET} ${message}
|
|
11478
|
+
` : ` \u26A0 ${message}
|
|
11479
|
+
`);
|
|
11480
|
+
});
|
|
11481
|
+
},
|
|
11482
|
+
stop() {
|
|
11483
|
+
finish(() => {
|
|
11484
|
+
});
|
|
11485
|
+
}
|
|
11486
|
+
};
|
|
11487
|
+
}
|
|
11488
|
+
|
|
11396
11489
|
// src/lib/provider-auth.ts
|
|
11397
11490
|
var sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
11398
11491
|
var form = (fields) => new URLSearchParams(fields).toString();
|
|
@@ -11459,42 +11552,49 @@ async function githubDeviceFlow() {
|
|
|
11459
11552
|
printInfo("");
|
|
11460
11553
|
printInfo(`To authorize Verity, open: ${dc.verification_uri}`);
|
|
11461
11554
|
printInfo(`And enter the code: ${dc.user_code}`);
|
|
11462
|
-
|
|
11555
|
+
const spinner = startSpinner(`Waiting for you to approve in the browser \xB7 code ${dc.user_code}`);
|
|
11463
11556
|
const deadline = Date.now() + (dc.expires_in || 900) * 1e3;
|
|
11464
11557
|
let interval = dc.interval || 5;
|
|
11465
|
-
|
|
11466
|
-
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
|
|
11476
|
-
|
|
11477
|
-
|
|
11478
|
-
|
|
11479
|
-
|
|
11480
|
-
|
|
11481
|
-
|
|
11482
|
-
|
|
11483
|
-
|
|
11484
|
-
|
|
11485
|
-
|
|
11486
|
-
|
|
11487
|
-
|
|
11488
|
-
|
|
11489
|
-
|
|
11490
|
-
|
|
11491
|
-
|
|
11492
|
-
|
|
11493
|
-
|
|
11494
|
-
|
|
11558
|
+
try {
|
|
11559
|
+
while (Date.now() < deadline) {
|
|
11560
|
+
await sleep(interval * 1e3);
|
|
11561
|
+
let data;
|
|
11562
|
+
try {
|
|
11563
|
+
const res = await fetch(GITHUB_ACCESS_TOKEN_URL, {
|
|
11564
|
+
method: "POST",
|
|
11565
|
+
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
|
|
11566
|
+
body: form({
|
|
11567
|
+
client_id: GITHUB_CLIENT_ID,
|
|
11568
|
+
device_code: dc.device_code,
|
|
11569
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
11570
|
+
})
|
|
11571
|
+
});
|
|
11572
|
+
data = await res.json().catch(() => ({}));
|
|
11573
|
+
} catch {
|
|
11574
|
+
continue;
|
|
11575
|
+
}
|
|
11576
|
+
if (data.access_token) {
|
|
11577
|
+
spinner.succeed("Authorized on GitHub");
|
|
11578
|
+
return { ok: true, data: data.access_token };
|
|
11579
|
+
}
|
|
11580
|
+
switch (data.error) {
|
|
11581
|
+
case "authorization_pending":
|
|
11582
|
+
break;
|
|
11583
|
+
case "slow_down":
|
|
11584
|
+
interval += 5;
|
|
11585
|
+
break;
|
|
11586
|
+
case "access_denied":
|
|
11587
|
+
return { ok: false, error: "Authorization was denied on GitHub." };
|
|
11588
|
+
case "expired_token":
|
|
11589
|
+
return { ok: false, error: "The authorization code expired. Re-run register." };
|
|
11590
|
+
default:
|
|
11591
|
+
if (data.error) return { ok: false, error: `GitHub auth error: ${data.error}` };
|
|
11592
|
+
}
|
|
11495
11593
|
}
|
|
11594
|
+
return { ok: false, error: "Timed out waiting for GitHub authorization." };
|
|
11595
|
+
} finally {
|
|
11596
|
+
spinner.stop();
|
|
11496
11597
|
}
|
|
11497
|
-
return { ok: false, error: "Timed out waiting for GitHub authorization." };
|
|
11498
11598
|
}
|
|
11499
11599
|
|
|
11500
11600
|
// src/lib/register.ts
|
|
@@ -12263,12 +12363,20 @@ var VERITY_STOP_RE = /(?:^|[\/\s"'])verity\s+analyze\b/;
|
|
|
12263
12363
|
var VERITY_INTENT_RE = /(?:^|[\/\s"'])verity\s+intent\s+capture\b/;
|
|
12264
12364
|
var VERITY_GUARD_RE = /(?:^|[\/\s"'])verity\s+guard\b/;
|
|
12265
12365
|
var VERITY_BASELINE_RE = /(?:^|[\/\s"'])verity\s+baseline\s+capture\b/;
|
|
12366
|
+
var VERITY_COMPACT_RE = /(?:^|[\/\s"'])verity\s+compact\b/;
|
|
12367
|
+
var VERITY_SESSION_END_RE = /(?:^|[\/\s"'])verity\s+session\s+end\b/;
|
|
12266
12368
|
function isVerityGuardHook(entry) {
|
|
12267
12369
|
return VERITY_GUARD_RE.test(entry.command ?? "");
|
|
12268
12370
|
}
|
|
12269
12371
|
function isVerityBaselineHook(entry) {
|
|
12270
12372
|
return VERITY_BASELINE_RE.test(entry.command ?? "");
|
|
12271
12373
|
}
|
|
12374
|
+
function isVerityCompactHook(entry) {
|
|
12375
|
+
return VERITY_COMPACT_RE.test(entry.command ?? "");
|
|
12376
|
+
}
|
|
12377
|
+
function isVeritySessionEndHook(entry) {
|
|
12378
|
+
return VERITY_SESSION_END_RE.test(entry.command ?? "");
|
|
12379
|
+
}
|
|
12272
12380
|
var LEGACY_STOP_RE = /(?:^|[\/\s"'])gate\s+analyze\b/;
|
|
12273
12381
|
var LEGACY_INTENT_RE = /(?:^|[\/\s"'])gate\s+intent\s+capture\b/;
|
|
12274
12382
|
function isVerityStopHook(entry) {
|
|
@@ -12280,7 +12388,7 @@ function isVerityIntentHook(entry) {
|
|
|
12280
12388
|
return VERITY_INTENT_RE.test(c) || LEGACY_INTENT_RE.test(c) || c.includes(".verity/hooks/capture-intent.sh") || c.includes(".gate/hooks/capture-intent.sh");
|
|
12281
12389
|
}
|
|
12282
12390
|
function isVerityHook(entry) {
|
|
12283
|
-
return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
|
|
12391
|
+
return isVerityStopHook(entry) || isVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry) || isVerityCompactHook(entry) || isVeritySessionEndHook(entry);
|
|
12284
12392
|
}
|
|
12285
12393
|
function isCurrentVerityStopHook(entry) {
|
|
12286
12394
|
const c = entry.command ?? "";
|
|
@@ -12291,7 +12399,7 @@ function isCurrentVerityIntentHook(entry) {
|
|
|
12291
12399
|
return VERITY_INTENT_RE.test(c) || c.includes(".verity/hooks/capture-intent.sh");
|
|
12292
12400
|
}
|
|
12293
12401
|
function isCurrentVerityHook(entry) {
|
|
12294
|
-
return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry);
|
|
12402
|
+
return isCurrentVerityStopHook(entry) || isCurrentVerityIntentHook(entry) || isVerityGuardHook(entry) || isVerityBaselineHook(entry) || isVerityCompactHook(entry) || isVeritySessionEndHook(entry);
|
|
12295
12403
|
}
|
|
12296
12404
|
function settingsHasLegacyHook(settings) {
|
|
12297
12405
|
for (const groups of Object.values(settings.hooks ?? {})) {
|
|
@@ -12353,6 +12461,8 @@ async function checkAllVerityHooks() {
|
|
|
12353
12461
|
async function checkExternalVerityHooks() {
|
|
12354
12462
|
let stop = false;
|
|
12355
12463
|
let intent = false;
|
|
12464
|
+
let compact = false;
|
|
12465
|
+
let sessionEnd = false;
|
|
12356
12466
|
let guardOn = [];
|
|
12357
12467
|
for (const f of [SETTINGS_LOCAL_FILE, globalSettingsFile()]) {
|
|
12358
12468
|
let settings;
|
|
@@ -12364,9 +12474,11 @@ async function checkExternalVerityHooks() {
|
|
|
12364
12474
|
const r = checkVerityHooks(settings);
|
|
12365
12475
|
stop = stop || r.stop;
|
|
12366
12476
|
intent = intent || r.intent;
|
|
12477
|
+
compact = compact || r.compact;
|
|
12478
|
+
sessionEnd = sessionEnd || r.sessionEnd;
|
|
12367
12479
|
if (r.guardOn.length > guardOn.length) guardOn = r.guardOn;
|
|
12368
12480
|
}
|
|
12369
|
-
return { stop, intent, guardOn };
|
|
12481
|
+
return { stop, intent, compact, sessionEnd, guardOn };
|
|
12370
12482
|
}
|
|
12371
12483
|
async function checkAllVerityHooksDetailed() {
|
|
12372
12484
|
let stop = false;
|
|
@@ -12446,11 +12558,11 @@ function checkVerityHooks(settings) {
|
|
|
12446
12558
|
}
|
|
12447
12559
|
const compactGroups = hooks["PostCompact"] ?? [];
|
|
12448
12560
|
const hasCompact = compactGroups.some(
|
|
12449
|
-
(g) => g.hooks?.some((h) => h
|
|
12561
|
+
(g) => g.hooks?.some((h) => isVerityCompactHook(h))
|
|
12450
12562
|
);
|
|
12451
12563
|
const endGroups = hooks["SessionEnd"] ?? [];
|
|
12452
12564
|
const hasSessionEnd = endGroups.some(
|
|
12453
|
-
(g) => g.hooks?.some((h) => h
|
|
12565
|
+
(g) => g.hooks?.some((h) => isVeritySessionEndHook(h))
|
|
12454
12566
|
);
|
|
12455
12567
|
return {
|
|
12456
12568
|
stop: hasStop,
|
|
@@ -12564,6 +12676,8 @@ function reconcileMomentHooks(settings, moments, externalPresent = {
|
|
|
12564
12676
|
};
|
|
12565
12677
|
if (!externalPresent.intent) push("UserPromptSubmit", { hooks: [VERITY_INTENT_HOOK] });
|
|
12566
12678
|
push("SessionStart", { hooks: [VERITY_BASELINE_HOOK] });
|
|
12679
|
+
if (!externalPresent.compact) push("PostCompact", { hooks: [VERITY_COMPACT_HOOK] });
|
|
12680
|
+
if (!externalPresent.sessionEnd) push("SessionEnd", { hooks: [VERITY_SESSION_END_HOOK] });
|
|
12567
12681
|
if (moments.includes("stop") && !externalPresent.stop) {
|
|
12568
12682
|
push("Stop", { hooks: [VERITY_STOP_HOOK] });
|
|
12569
12683
|
}
|
|
@@ -13540,6 +13654,74 @@ var LEGACY_MD_END = "<!-- gate-memory:end -->";
|
|
|
13540
13654
|
var LEGACY_PRESERVE_START = "<!-- gate-memory:preserve -->";
|
|
13541
13655
|
var LEGACY_PRESERVE_END = "<!-- /gate-memory:preserve -->";
|
|
13542
13656
|
var CLAUDE_MD_PROSE = [
|
|
13657
|
+
"## Project Memory",
|
|
13658
|
+
"",
|
|
13659
|
+
"This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
|
|
13660
|
+
"non-trivial work, scan `.verity/memory/index.md` for decisions, gotchas, and patterns",
|
|
13661
|
+
"that may apply to the change you are about to make. Open specific node files via",
|
|
13662
|
+
"the Read tool when the title or scope suggests relevance.",
|
|
13663
|
+
"",
|
|
13664
|
+
"The graph is auto-maintained by Verity. Files at `.verity/memory/_archive/` are",
|
|
13665
|
+
"superseded \u2014 ignore them unless investigating history.",
|
|
13666
|
+
"",
|
|
13667
|
+
"## Quality gate: accepted risks",
|
|
13668
|
+
"",
|
|
13669
|
+
"When the Verity pre-commit/pre-push gate FAILs, fix the findings \u2014 that is the",
|
|
13670
|
+
'default. Use `verity waive <pattern-id> --file <path> --reason "\u2026"` ONLY to relay',
|
|
13671
|
+
"a risk a human has explicitly accepted: a named code-review finding, an ADR, or",
|
|
13672
|
+
"the user saying so in this conversation. The --reason must cite that source.",
|
|
13673
|
+
"",
|
|
13674
|
+
"Never waive on your own judgment, to get past a block, or pre-emptively. A waive",
|
|
13675
|
+
"binds to the file's current bytes and voids automatically when the file changes,",
|
|
13676
|
+
"and every waive is recorded in the run ledger. For a pattern-level false positive",
|
|
13677
|
+
"use `verity feedback finding <run-id> <pattern-id> false_positive` instead.",
|
|
13678
|
+
"",
|
|
13679
|
+
"## Post-task reflection",
|
|
13680
|
+
"",
|
|
13681
|
+
`When a task is complete (you've created a PR, the user says "done" or "ship it",`,
|
|
13682
|
+
"or the work is clearly finished), **draft the reflection yourself first** \u2014 1\u20133",
|
|
13683
|
+
"concrete things worth remembering (a decision, a gotcha, or a pattern), each",
|
|
13684
|
+
"cited with the files / PR / commands / error-signatures it came from. Skip",
|
|
13685
|
+
"entirely if nothing non-obvious happened.",
|
|
13686
|
+
"",
|
|
13687
|
+
"Then, depending on how the session is running:",
|
|
13688
|
+
"",
|
|
13689
|
+
"- **Interactive** (a person is at the keyboard): show your draft and ask one",
|
|
13690
|
+
` question before recording \u2014 "Here's my reflection draft for the project`,
|
|
13691
|
+
" memory: <draft>. Anything to confirm, correct, or add before I record it?",
|
|
13692
|
+
` (Say 'skip' to drop it.)" Record the final text (with their corrections) via`,
|
|
13693
|
+
' `verity reflect --user-input "<final reflection>" --kind <kind>`. If they say',
|
|
13694
|
+
' "skip", do not record.',
|
|
13695
|
+
"- **Autonomous** (headless / CI / cron, or `VERITY_AUTONOMOUS=1`): record your",
|
|
13696
|
+
' draft directly, never blocking \u2014 `verity reflect --user-input "<your draft>"',
|
|
13697
|
+
" --kind <kind> --autonomous`.",
|
|
13698
|
+
"",
|
|
13699
|
+
"> Durable, hand-curated guidance goes in the preserve region below (it survives",
|
|
13700
|
+
"> regeneration) or anywhere OUTSIDE these markers. Everything else between the",
|
|
13701
|
+
"> markers is tool-owned and overwritten on each run.",
|
|
13702
|
+
"",
|
|
13703
|
+
"## Housekeeping Turns",
|
|
13704
|
+
"",
|
|
13705
|
+
"When a turn will be pure housekeeping \u2014 pulling, installing dependencies,",
|
|
13706
|
+
"rebasing, a formatting sweep you are not authoring \u2014 declare it BEFORE doing it:",
|
|
13707
|
+
"",
|
|
13708
|
+
"```bash",
|
|
13709
|
+
'verity ignore --turn --agent --reason "pulling latest before starting"',
|
|
13710
|
+
"```",
|
|
13711
|
+
"",
|
|
13712
|
+
"This skips the review for that turn, which saves the turn Verity would",
|
|
13713
|
+
"otherwise spend saying it had nothing to say. Use `--for 30m` instead of",
|
|
13714
|
+
"`--turn` when a single piece of housekeeping spans several turns.",
|
|
13715
|
+
"",
|
|
13716
|
+
"**It is a claim about the turn, not a way to silence review.** The declaration",
|
|
13717
|
+
"is checked against what the turn actually did: if anything is authored \u2014 by you,",
|
|
13718
|
+
"by a subagent, or by a shell command that can write files \u2014 it voids, the review",
|
|
13719
|
+
"runs anyway, and the broken declaration is reported. So declare housekeeping you",
|
|
13720
|
+
"are about to do, never work you have already done, and never as a way to get past",
|
|
13721
|
+
"a finding. Declarations are budgeted per session and every one is recorded with",
|
|
13722
|
+
"its reason."
|
|
13723
|
+
].join("\n");
|
|
13724
|
+
var CLAUDE_MD_PROSE_PRE_REFLECT = [
|
|
13543
13725
|
"## Project Memory",
|
|
13544
13726
|
"",
|
|
13545
13727
|
"This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
|
|
@@ -13750,6 +13932,7 @@ function stripKnownProse(interior) {
|
|
|
13750
13932
|
const trimmed = interior.replace(/^\n+/, "");
|
|
13751
13933
|
for (const prose of [
|
|
13752
13934
|
CLAUDE_MD_PROSE,
|
|
13935
|
+
CLAUDE_MD_PROSE_PRE_REFLECT,
|
|
13753
13936
|
CLAUDE_MD_PROSE_PRE_WAIVE,
|
|
13754
13937
|
CLAUDE_MD_PROSE_PRE_IGNORE,
|
|
13755
13938
|
CLAUDE_MD_PROSE_LEGACY
|
|
@@ -15795,33 +15978,6 @@ ${addedLines}`,
|
|
|
15795
15978
|
}
|
|
15796
15979
|
return { diffs, has_snapshots: true };
|
|
15797
15980
|
}
|
|
15798
|
-
function ensureSnapshotGitignored() {
|
|
15799
|
-
let content = "";
|
|
15800
|
-
try {
|
|
15801
|
-
content = (0, import_node_fs16.readFileSync)(".gitignore", "utf-8");
|
|
15802
|
-
} catch {
|
|
15803
|
-
}
|
|
15804
|
-
let ignored = null;
|
|
15805
|
-
try {
|
|
15806
|
-
(0, import_node_child_process6.execSync)("git check-ignore -q -- .verity/.snapshot/__probe__", { stdio: "pipe" });
|
|
15807
|
-
ignored = true;
|
|
15808
|
-
} catch (err) {
|
|
15809
|
-
ignored = err.status === 1 ? false : null;
|
|
15810
|
-
}
|
|
15811
|
-
if (ignored === true) return "covered";
|
|
15812
|
-
if (ignored === null) {
|
|
15813
|
-
const lines = content.split("\n").map((l) => l.trim());
|
|
15814
|
-
const covering = [".verity/.snapshot/", ".verity/.snapshot", ".verity/", ".verity", ".verity/*"];
|
|
15815
|
-
if (lines.some((l) => covering.includes(l))) return "covered";
|
|
15816
|
-
}
|
|
15817
|
-
try {
|
|
15818
|
-
const block = "# Verity \u2014 snapshots of analyzed files (machine state, never commit)\n.verity/.snapshot/\n";
|
|
15819
|
-
(0, import_node_fs16.writeFileSync)(".gitignore", content ? content + (content.endsWith("\n") ? "" : "\n") + "\n" + block : block);
|
|
15820
|
-
return "added";
|
|
15821
|
-
} catch {
|
|
15822
|
-
return "failed";
|
|
15823
|
-
}
|
|
15824
|
-
}
|
|
15825
15981
|
function saveSnapshots(files) {
|
|
15826
15982
|
const snapshotPaths = /* @__PURE__ */ new Set();
|
|
15827
15983
|
for (const file of files) {
|
|
@@ -16743,19 +16899,19 @@ function loc(f) {
|
|
|
16743
16899
|
if (!f.file) return "";
|
|
16744
16900
|
return f.line != null ? `${f.file}:${f.line}` : f.file;
|
|
16745
16901
|
}
|
|
16746
|
-
function formatRunDetail(
|
|
16902
|
+
function formatRunDetail(run2) {
|
|
16747
16903
|
const lines = [];
|
|
16748
|
-
const q =
|
|
16749
|
-
const s =
|
|
16904
|
+
const q = run2.assessment?.quality_score;
|
|
16905
|
+
const s = run2.assessment?.security_score;
|
|
16750
16906
|
const qStr = q != null ? `${q}` : "-";
|
|
16751
16907
|
const sStr = s != null ? `${s}` : "-";
|
|
16752
|
-
lines.push(`${
|
|
16908
|
+
lines.push(`${run2.run_id} ${run2.gate_decision} Q ${qStr}/10 S ${sStr}/10`);
|
|
16753
16909
|
const meta = [];
|
|
16754
|
-
if (
|
|
16755
|
-
if (
|
|
16756
|
-
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", " "));
|
|
16757
16913
|
if (meta.length > 0) lines.push(meta.join(" \xB7 "));
|
|
16758
|
-
const findings =
|
|
16914
|
+
const findings = run2.findings ?? [];
|
|
16759
16915
|
if (findings.length === 0) {
|
|
16760
16916
|
lines.push("");
|
|
16761
16917
|
lines.push("No findings \u2014 clean.");
|
|
@@ -16772,7 +16928,7 @@ function formatRunDetail(run) {
|
|
|
16772
16928
|
if (f.scope === "pre-existing") lines.push(" (pre-existing)");
|
|
16773
16929
|
}
|
|
16774
16930
|
}
|
|
16775
|
-
const pending =
|
|
16931
|
+
const pending = run2.pending_items ?? [];
|
|
16776
16932
|
if (pending.length > 0) {
|
|
16777
16933
|
lines.push("");
|
|
16778
16934
|
lines.push(`PENDING (${pending.length})`);
|
|
@@ -16780,9 +16936,9 @@ function formatRunDetail(run) {
|
|
|
16780
16936
|
lines.push(` [${(p.priority ?? "").toUpperCase()}] ${p.description}`);
|
|
16781
16937
|
}
|
|
16782
16938
|
}
|
|
16783
|
-
if (
|
|
16939
|
+
if (run2.assessment?.narrative) {
|
|
16784
16940
|
lines.push("");
|
|
16785
|
-
lines.push(
|
|
16941
|
+
lines.push(run2.assessment.narrative);
|
|
16786
16942
|
}
|
|
16787
16943
|
return lines;
|
|
16788
16944
|
}
|
|
@@ -17154,7 +17310,7 @@ function registerStatusCommand(program2) {
|
|
|
17154
17310
|
return;
|
|
17155
17311
|
}
|
|
17156
17312
|
if (mem?.configured === false) {
|
|
17157
|
-
printInfo(
|
|
17313
|
+
printInfo('Verity is not configured for this project. Run "verity init".');
|
|
17158
17314
|
return;
|
|
17159
17315
|
}
|
|
17160
17316
|
printInfo("=== Verity Status ===");
|
|
@@ -17186,7 +17342,7 @@ function registerStatusCommand(program2) {
|
|
|
17186
17342
|
if (hookStatus.stop) moments.push("stop");
|
|
17187
17343
|
if (hookStatus.guardOn.includes("commit")) moments.push("pre-commit");
|
|
17188
17344
|
if (hookStatus.guardOn.includes("push")) moments.push("pre-push/PR");
|
|
17189
|
-
printInfo(`Moments: ${moments.length > 0 ? moments.join(", ") :
|
|
17345
|
+
printInfo(`Moments: ${moments.length > 0 ? moments.join(", ") : 'none (run "verity init")'}`);
|
|
17190
17346
|
if (!mem) return;
|
|
17191
17347
|
if (mem.recent_runs) {
|
|
17192
17348
|
const r = mem.recent_runs;
|
|
@@ -17272,12 +17428,12 @@ function registerStatusCommand(program2) {
|
|
|
17272
17428
|
printInfo("");
|
|
17273
17429
|
printInfo("--- Recent Runs ---");
|
|
17274
17430
|
printInfo(`${"Run ID".padEnd(32)} ${"Decision".padEnd(10)}${"Q".padEnd(4)}${"S".padEnd(4)}${"Findings".padEnd(32)}Date`);
|
|
17275
|
-
for (const
|
|
17276
|
-
const q =
|
|
17277
|
-
const s =
|
|
17278
|
-
const findings = formatFindingsSummary(
|
|
17279
|
-
const date =
|
|
17280
|
-
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}`);
|
|
17281
17437
|
}
|
|
17282
17438
|
}
|
|
17283
17439
|
}
|
|
@@ -17412,7 +17568,6 @@ function createRun(opts, globals) {
|
|
|
17412
17568
|
token: "",
|
|
17413
17569
|
modeDecision: null,
|
|
17414
17570
|
sessionIdForMemory: "",
|
|
17415
|
-
contextFilePaths: [],
|
|
17416
17571
|
analysisMode: "standard",
|
|
17417
17572
|
sessionAuthoredCode: false,
|
|
17418
17573
|
staticResults: {
|
|
@@ -17422,6 +17577,7 @@ function createRun(opts, globals) {
|
|
|
17422
17577
|
},
|
|
17423
17578
|
codeDelta: { files: [], total_lines: 0, total_files: 0, excluded: [] },
|
|
17424
17579
|
snapshotResult: { has_snapshots: false, diffs: [] },
|
|
17580
|
+
repoContext: null,
|
|
17425
17581
|
contentHash: null,
|
|
17426
17582
|
iteration: 1,
|
|
17427
17583
|
currentCommit: "",
|
|
@@ -17503,6 +17659,704 @@ function logToFileOnly(text) {
|
|
|
17503
17659
|
`);
|
|
17504
17660
|
}
|
|
17505
17661
|
|
|
17662
|
+
// src/lib/repo-context.ts
|
|
17663
|
+
var import_node_child_process7 = require("node:child_process");
|
|
17664
|
+
var import_node_os3 = require("node:os");
|
|
17665
|
+
function rgInvocations(env = process.env) {
|
|
17666
|
+
const out = [{ cmd: "rg" }];
|
|
17667
|
+
if (env.CLAUDE_CODE_EXECPATH) out.push({ cmd: env.CLAUDE_CODE_EXECPATH, argv0: "rg" });
|
|
17668
|
+
out.push({ cmd: `${(0, import_node_os3.homedir)()}/.local/bin/claude`, argv0: "rg" });
|
|
17669
|
+
return out;
|
|
17670
|
+
}
|
|
17671
|
+
var MAX_SYMBOLS = 12;
|
|
17672
|
+
var MAX_SITES = 24;
|
|
17673
|
+
var MAX_SITES_PER_FILE = 3;
|
|
17674
|
+
var MAX_HITS_PER_SYMBOL = 50;
|
|
17675
|
+
var MAX_TEST_SLOTS = 8;
|
|
17676
|
+
var SITE_TEXT_MAX = 160;
|
|
17677
|
+
var ENCLOSING_SCAN_LINES = 200;
|
|
17678
|
+
var RG_TIMEOUT_MS = 1500;
|
|
17679
|
+
var IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
17680
|
+
var STOPLIST = /* @__PURE__ */ new Set([
|
|
17681
|
+
// keyword-shaped captures
|
|
17682
|
+
"if",
|
|
17683
|
+
"for",
|
|
17684
|
+
"while",
|
|
17685
|
+
"switch",
|
|
17686
|
+
"catch",
|
|
17687
|
+
"return",
|
|
17688
|
+
"function",
|
|
17689
|
+
"class",
|
|
17690
|
+
"const",
|
|
17691
|
+
"let",
|
|
17692
|
+
"var",
|
|
17693
|
+
"new",
|
|
17694
|
+
"else",
|
|
17695
|
+
"try",
|
|
17696
|
+
"finally",
|
|
17697
|
+
"throw",
|
|
17698
|
+
"await",
|
|
17699
|
+
"async",
|
|
17700
|
+
"yield",
|
|
17701
|
+
"delete",
|
|
17702
|
+
"typeof",
|
|
17703
|
+
"instanceof",
|
|
17704
|
+
"void",
|
|
17705
|
+
"this",
|
|
17706
|
+
"super",
|
|
17707
|
+
"import",
|
|
17708
|
+
"export",
|
|
17709
|
+
"default",
|
|
17710
|
+
"extends",
|
|
17711
|
+
"implements",
|
|
17712
|
+
"interface",
|
|
17713
|
+
"enum",
|
|
17714
|
+
"type",
|
|
17715
|
+
"public",
|
|
17716
|
+
"private",
|
|
17717
|
+
"protected",
|
|
17718
|
+
"static",
|
|
17719
|
+
"get",
|
|
17720
|
+
"set",
|
|
17721
|
+
"constructor",
|
|
17722
|
+
"def",
|
|
17723
|
+
"elif",
|
|
17724
|
+
"lambda",
|
|
17725
|
+
"with",
|
|
17726
|
+
"pass",
|
|
17727
|
+
"self",
|
|
17728
|
+
"cls",
|
|
17729
|
+
"not",
|
|
17730
|
+
"and",
|
|
17731
|
+
"or",
|
|
17732
|
+
"raise",
|
|
17733
|
+
"except",
|
|
17734
|
+
"func",
|
|
17735
|
+
"defer",
|
|
17736
|
+
"chan",
|
|
17737
|
+
"select",
|
|
17738
|
+
"range",
|
|
17739
|
+
"module",
|
|
17740
|
+
"struct",
|
|
17741
|
+
"trait",
|
|
17742
|
+
"impl",
|
|
17743
|
+
"using",
|
|
17744
|
+
"namespace",
|
|
17745
|
+
// universal noise
|
|
17746
|
+
"main",
|
|
17747
|
+
"init",
|
|
17748
|
+
"index",
|
|
17749
|
+
"data",
|
|
17750
|
+
"value",
|
|
17751
|
+
"result",
|
|
17752
|
+
"item",
|
|
17753
|
+
"name",
|
|
17754
|
+
"key",
|
|
17755
|
+
"run",
|
|
17756
|
+
"test",
|
|
17757
|
+
"setup",
|
|
17758
|
+
"update",
|
|
17759
|
+
"create",
|
|
17760
|
+
"handle",
|
|
17761
|
+
"check",
|
|
17762
|
+
"load",
|
|
17763
|
+
"save",
|
|
17764
|
+
"list",
|
|
17765
|
+
"map",
|
|
17766
|
+
"args",
|
|
17767
|
+
"params",
|
|
17768
|
+
"props",
|
|
17769
|
+
"state",
|
|
17770
|
+
"error",
|
|
17771
|
+
"err",
|
|
17772
|
+
"res",
|
|
17773
|
+
"req",
|
|
17774
|
+
"ctx",
|
|
17775
|
+
"config",
|
|
17776
|
+
"options",
|
|
17777
|
+
"util",
|
|
17778
|
+
"utils",
|
|
17779
|
+
"helper",
|
|
17780
|
+
"render",
|
|
17781
|
+
"build",
|
|
17782
|
+
"parse",
|
|
17783
|
+
"format",
|
|
17784
|
+
"apply",
|
|
17785
|
+
"process",
|
|
17786
|
+
"start",
|
|
17787
|
+
"stop"
|
|
17788
|
+
]);
|
|
17789
|
+
var TS_RULES = [
|
|
17790
|
+
/\b(?:function|class|interface|enum)\s+([A-Za-z_][A-Za-z0-9_]*)/,
|
|
17791
|
+
/\btype\s+([A-Za-z_][A-Za-z0-9_]*)\s*=/,
|
|
17792
|
+
// const foo = (…) => · const foo = x => · const foo = function.
|
|
17793
|
+
// ⚠ REQUIRES the arrow or `function` ON THE LINE. The first version accepted
|
|
17794
|
+
// any `= (` — and `const started = (rows?.[0] as Row)?.at` is a PARENTHESIZED
|
|
17795
|
+
// CAST, not a function. Replayed over 12 real commits, that one shape put
|
|
17796
|
+
// three local variables into the symbol set per commit. A multi-line arrow
|
|
17797
|
+
// is the accepted false negative; a cast is not an accepted false positive.
|
|
17798
|
+
/\b(?:const|let|var)\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?::[^=\n]+)?=\s*(?:async\s+)?(?:function\b|\([^)]*\)(?:\s*:[^=\n]+)?\s*=>|[A-Za-z_$][\w$]*\s*=>)/,
|
|
17799
|
+
// method shape: name(…) { — keyword captures die at the stoplist
|
|
17800
|
+
/^\s*(?:(?:public|private|protected|static|readonly|async|override)\s+)*(?:\*\s*)?([A-Za-z_][A-Za-z0-9_]*)\s*\([^)]*\)\s*(?::[^{;\n]+)?\s*\{/
|
|
17801
|
+
];
|
|
17802
|
+
var PY_RULES = [
|
|
17803
|
+
/^\s*(?:async\s+)?def\s+([A-Za-z_]\w*)/,
|
|
17804
|
+
/^\s*class\s+([A-Za-z_]\w*)/
|
|
17805
|
+
];
|
|
17806
|
+
var GO_RULES = [
|
|
17807
|
+
/^func\s+(?:\([^)]*\)\s*)?([A-Za-z_]\w*)/,
|
|
17808
|
+
/^type\s+([A-Za-z_]\w*)/
|
|
17809
|
+
];
|
|
17810
|
+
var CLIKE_RULES = [
|
|
17811
|
+
/\b(?:class|interface|enum|record|struct)\s+([A-Za-z_]\w*)/,
|
|
17812
|
+
// access-modifier method shape: `public async Task<Foo> BarBaz(…`
|
|
17813
|
+
/(?:public|private|protected|internal|static|final|virtual|override|sealed|abstract)[\w<>[\],?\s]*?\s([A-Za-z_]\w*)\s*\(/
|
|
17814
|
+
];
|
|
17815
|
+
var RB_RULES = [
|
|
17816
|
+
/^\s*def\s+(?:self\.)?([A-Za-z_]\w*)/,
|
|
17817
|
+
/^\s*(?:class|module)\s+([A-Z]\w*)/
|
|
17818
|
+
];
|
|
17819
|
+
var RS_RULES = [
|
|
17820
|
+
/\bfn\s+([A-Za-z_]\w*)/,
|
|
17821
|
+
/\b(?:struct|enum|trait)\s+([A-Za-z_]\w*)/
|
|
17822
|
+
];
|
|
17823
|
+
var PHP_RULES = [
|
|
17824
|
+
/\bfunction\s+([A-Za-z_]\w*)/,
|
|
17825
|
+
/\bclass\s+([A-Za-z_]\w*)/
|
|
17826
|
+
];
|
|
17827
|
+
var C_RULES = [
|
|
17828
|
+
/^(?:static\s+|inline\s+|extern\s+|constexpr\s+)*(?:struct\s+|enum\s+|union\s+|unsigned\s+|const\s+)*[A-Za-z_]\w*(?:\s*[*&]+\s*|\s+)([A-Za-z_]\w*)\s*\([^;]*$/,
|
|
17829
|
+
/\b(?:struct|enum|union|class)\s+([A-Za-z_]\w*)/,
|
|
17830
|
+
/::\s*~?([A-Za-z_]\w*)\s*\([^;]*$/
|
|
17831
|
+
// out-of-line C++ method definition
|
|
17832
|
+
];
|
|
17833
|
+
var SH_RULES = [
|
|
17834
|
+
/^\s*(?:function\s+)?([A-Za-z_]\w*)\s*\(\)\s*\{/,
|
|
17835
|
+
/^function\s+([A-Za-z_]\w*)/
|
|
17836
|
+
];
|
|
17837
|
+
var SQL_RULES = [
|
|
17838
|
+
/\bcreate\s+(?:or\s+replace\s+)?(?:table|view|materialized\s+view|function|procedure|index|trigger|type|policy)\s+(?:if\s+not\s+exists\s+)?(?:[\w".]*\.)?"?([A-Za-z_]\w*)"?/i
|
|
17839
|
+
];
|
|
17840
|
+
var TF_RULES = [
|
|
17841
|
+
/^\s*(?:resource|data)\s+"[^"]+"\s+"([A-Za-z_]\w*)"/,
|
|
17842
|
+
/^\s*(?:module|variable|output)\s+"([A-Za-z_]\w*)"/
|
|
17843
|
+
];
|
|
17844
|
+
var SWIFT_RULES = [
|
|
17845
|
+
/\bfunc\s+([A-Za-z_]\w*)/,
|
|
17846
|
+
/\b(?:class|struct|enum|protocol|extension|actor)\s+([A-Za-z_]\w*)/
|
|
17847
|
+
];
|
|
17848
|
+
var DART_RULES = [
|
|
17849
|
+
/\b(?:class|enum|mixin|extension)\s+([A-Za-z_]\w*)/,
|
|
17850
|
+
/^\s*(?:static\s+)?(?:Future<[^>]*>|Stream<[^>]*>|void|int|double|bool|String|num|dynamic|[A-Z]\w*(?:<[^>]*>)?)\s+([a-z_]\w*)\s*\(/
|
|
17851
|
+
];
|
|
17852
|
+
var LUA_RULES = [
|
|
17853
|
+
/^\s*(?:local\s+)?function\s+(?:[\w.]+[.:])?([A-Za-z_]\w*)/
|
|
17854
|
+
];
|
|
17855
|
+
var EX_RULES = [
|
|
17856
|
+
/^\s*def(?:p|macro)?\s+([a-z_]\w*)/,
|
|
17857
|
+
/^\s*defmodule\s+(?:[\w.]*\.)?([A-Z]\w*)/
|
|
17858
|
+
];
|
|
17859
|
+
var PROTO_RULES = [
|
|
17860
|
+
/^\s*(?:message|service|enum)\s+([A-Za-z_]\w*)/,
|
|
17861
|
+
/^\s*rpc\s+([A-Za-z_]\w*)/
|
|
17862
|
+
];
|
|
17863
|
+
var GRAPHQL_RULES = [
|
|
17864
|
+
/^\s*(?:type|interface|enum|input|union|scalar)\s+([A-Za-z_]\w*)/
|
|
17865
|
+
];
|
|
17866
|
+
var RULES_BY_EXT = {
|
|
17867
|
+
ts: TS_RULES,
|
|
17868
|
+
tsx: TS_RULES,
|
|
17869
|
+
js: TS_RULES,
|
|
17870
|
+
jsx: TS_RULES,
|
|
17871
|
+
mjs: TS_RULES,
|
|
17872
|
+
cjs: TS_RULES,
|
|
17873
|
+
svelte: TS_RULES,
|
|
17874
|
+
vue: TS_RULES,
|
|
17875
|
+
// script blocks
|
|
17876
|
+
py: PY_RULES,
|
|
17877
|
+
go: GO_RULES,
|
|
17878
|
+
java: CLIKE_RULES,
|
|
17879
|
+
cs: CLIKE_RULES,
|
|
17880
|
+
kt: CLIKE_RULES,
|
|
17881
|
+
scala: CLIKE_RULES,
|
|
17882
|
+
rb: RB_RULES,
|
|
17883
|
+
rs: RS_RULES,
|
|
17884
|
+
php: PHP_RULES,
|
|
17885
|
+
c: C_RULES,
|
|
17886
|
+
cpp: C_RULES,
|
|
17887
|
+
cc: C_RULES,
|
|
17888
|
+
h: C_RULES,
|
|
17889
|
+
hpp: C_RULES,
|
|
17890
|
+
sh: SH_RULES,
|
|
17891
|
+
bash: SH_RULES,
|
|
17892
|
+
zsh: SH_RULES,
|
|
17893
|
+
sql: SQL_RULES,
|
|
17894
|
+
tf: TF_RULES,
|
|
17895
|
+
hcl: TF_RULES,
|
|
17896
|
+
swift: SWIFT_RULES,
|
|
17897
|
+
dart: DART_RULES,
|
|
17898
|
+
lua: LUA_RULES,
|
|
17899
|
+
ex: EX_RULES,
|
|
17900
|
+
exs: EX_RULES,
|
|
17901
|
+
proto: PROTO_RULES,
|
|
17902
|
+
graphql: GRAPHQL_RULES,
|
|
17903
|
+
gql: GRAPHQL_RULES
|
|
17904
|
+
};
|
|
17905
|
+
var HUNK_HEADER = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
17906
|
+
function parseDiffSignals(diff) {
|
|
17907
|
+
const addedRanges = [];
|
|
17908
|
+
const touchPoints = [];
|
|
17909
|
+
const deletedLines = [];
|
|
17910
|
+
let newLine = 0;
|
|
17911
|
+
let oldRemaining = 0;
|
|
17912
|
+
let newRemaining = 0;
|
|
17913
|
+
let runStart = -1;
|
|
17914
|
+
let deletionRun = false;
|
|
17915
|
+
const closeAddedRun = () => {
|
|
17916
|
+
if (runStart >= 0) addedRanges.push([runStart, newLine - 1]);
|
|
17917
|
+
runStart = -1;
|
|
17918
|
+
};
|
|
17919
|
+
const closeDeletionRun = () => {
|
|
17920
|
+
if (deletionRun) touchPoints.push(Math.max(1, newLine));
|
|
17921
|
+
deletionRun = false;
|
|
17922
|
+
};
|
|
17923
|
+
for (const line of diff.split("\n")) {
|
|
17924
|
+
const inHunk = oldRemaining > 0 || newRemaining > 0;
|
|
17925
|
+
if (!inHunk) {
|
|
17926
|
+
closeAddedRun();
|
|
17927
|
+
closeDeletionRun();
|
|
17928
|
+
const header = HUNK_HEADER.exec(line);
|
|
17929
|
+
if (header) {
|
|
17930
|
+
newLine = parseInt(header[3], 10);
|
|
17931
|
+
oldRemaining = header[2] === void 0 ? 1 : parseInt(header[2], 10);
|
|
17932
|
+
newRemaining = header[4] === void 0 ? 1 : parseInt(header[4], 10);
|
|
17933
|
+
if (newRemaining === 0) newLine = Math.max(1, newLine);
|
|
17934
|
+
}
|
|
17935
|
+
continue;
|
|
17936
|
+
}
|
|
17937
|
+
if (line.startsWith("\\")) continue;
|
|
17938
|
+
if (line.startsWith("+") && newRemaining > 0) {
|
|
17939
|
+
deletionRun = false;
|
|
17940
|
+
if (runStart < 0) runStart = newLine;
|
|
17941
|
+
newLine++;
|
|
17942
|
+
newRemaining--;
|
|
17943
|
+
continue;
|
|
17944
|
+
}
|
|
17945
|
+
if (line.startsWith("-") && oldRemaining > 0) {
|
|
17946
|
+
closeAddedRun();
|
|
17947
|
+
deletionRun = true;
|
|
17948
|
+
deletedLines.push(line.slice(1));
|
|
17949
|
+
oldRemaining--;
|
|
17950
|
+
continue;
|
|
17951
|
+
}
|
|
17952
|
+
closeAddedRun();
|
|
17953
|
+
closeDeletionRun();
|
|
17954
|
+
newLine++;
|
|
17955
|
+
if (oldRemaining > 0) oldRemaining--;
|
|
17956
|
+
if (newRemaining > 0) newRemaining--;
|
|
17957
|
+
}
|
|
17958
|
+
closeAddedRun();
|
|
17959
|
+
closeDeletionRun();
|
|
17960
|
+
return { addedRanges, touchPoints, deletedLines };
|
|
17961
|
+
}
|
|
17962
|
+
function declNameOn(line, rules) {
|
|
17963
|
+
for (const r of rules) {
|
|
17964
|
+
const m = r.exec(line);
|
|
17965
|
+
if (m?.[1]) return m[1];
|
|
17966
|
+
}
|
|
17967
|
+
return null;
|
|
17968
|
+
}
|
|
17969
|
+
function acceptable(name) {
|
|
17970
|
+
if (!name || !IDENTIFIER.test(name) || STOPLIST.has(name.toLowerCase())) return false;
|
|
17971
|
+
if (name.length < 3) return false;
|
|
17972
|
+
if (name.length === 3 && name === name.toLowerCase() && !name.includes("_")) return false;
|
|
17973
|
+
return true;
|
|
17974
|
+
}
|
|
17975
|
+
function isMultiSegment(name) {
|
|
17976
|
+
return /[a-z][A-Z]/.test(name) || name.includes("_");
|
|
17977
|
+
}
|
|
17978
|
+
function extractFileSymbols(path, content, signals) {
|
|
17979
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
17980
|
+
const rules = RULES_BY_EXT[ext];
|
|
17981
|
+
if (!rules) return [];
|
|
17982
|
+
if (signals.addedRanges.length === 0 && signals.touchPoints.length === 0 && signals.deletedLines.length === 0) return [];
|
|
17983
|
+
const lines = content.split("\n");
|
|
17984
|
+
const found = [];
|
|
17985
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17986
|
+
const add = (name) => {
|
|
17987
|
+
if (acceptable(name) && !seen.has(name)) {
|
|
17988
|
+
seen.add(name);
|
|
17989
|
+
found.push(name);
|
|
17990
|
+
}
|
|
17991
|
+
};
|
|
17992
|
+
for (const deleted of signals.deletedLines) {
|
|
17993
|
+
add(declNameOn(deleted, rules));
|
|
17994
|
+
}
|
|
17995
|
+
for (const [start, end] of signals.addedRanges) {
|
|
17996
|
+
for (let n = start; n <= Math.min(end, lines.length); n++) {
|
|
17997
|
+
add(declNameOn(lines[n - 1] ?? "", rules));
|
|
17998
|
+
}
|
|
17999
|
+
}
|
|
18000
|
+
const scanStarts = [
|
|
18001
|
+
...signals.addedRanges.map(([start]) => start),
|
|
18002
|
+
...signals.touchPoints
|
|
18003
|
+
];
|
|
18004
|
+
for (const start of scanStarts) {
|
|
18005
|
+
const floor = Math.max(1, start - ENCLOSING_SCAN_LINES);
|
|
18006
|
+
for (let n = Math.min(start, lines.length); n >= floor; n--) {
|
|
18007
|
+
const name = declNameOn(lines[n - 1] ?? "", rules);
|
|
18008
|
+
if (acceptable(name)) {
|
|
18009
|
+
add(name);
|
|
18010
|
+
break;
|
|
18011
|
+
}
|
|
18012
|
+
}
|
|
18013
|
+
}
|
|
18014
|
+
return found;
|
|
18015
|
+
}
|
|
18016
|
+
function rankSymbols(symbols) {
|
|
18017
|
+
return symbols.map((s, i) => ({ s, i })).sort((a, b) => {
|
|
18018
|
+
const seg = Number(isMultiSegment(b.s)) - Number(isMultiSegment(a.s));
|
|
18019
|
+
if (seg !== 0) return seg;
|
|
18020
|
+
if (b.s.length !== a.s.length) return b.s.length - a.s.length;
|
|
18021
|
+
return a.i - b.i;
|
|
18022
|
+
}).slice(0, MAX_SYMBOLS).map((x) => x.s);
|
|
18023
|
+
}
|
|
18024
|
+
var TEST_PATH = /(^|\/)(tests?|specs?|__tests__|e2e)(\/|$)/i;
|
|
18025
|
+
var TEST_FILE = /(\.(test|spec|e2e)\.[^./]+|_test\.[^./]+|_spec\.rb)$/i;
|
|
18026
|
+
var TEST_PY_PREFIX = /(^|\/)test_[^/]+\.py$/i;
|
|
18027
|
+
var IMPORT_LINE = /^\s*(import\s|from\s+\S+\s+import\s|const\s+.*=\s*require\s*\(|require\s*\(|using\s+[\w.]+;|#include|export\s+\{[^}]*\}\s+from|export\s+\*\s+from)/;
|
|
18028
|
+
var BARE_MEMBER_LINE = /^(type\s+)?[A-Za-z_$][\w$]*\s*,?$/;
|
|
18029
|
+
var GO_IMPORT_PATH_LINE = /^"[^"]+",?$/;
|
|
18030
|
+
var SITE_CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
18031
|
+
"ts",
|
|
18032
|
+
"tsx",
|
|
18033
|
+
"js",
|
|
18034
|
+
"jsx",
|
|
18035
|
+
"mjs",
|
|
18036
|
+
"cjs",
|
|
18037
|
+
"py",
|
|
18038
|
+
"go",
|
|
18039
|
+
"java",
|
|
18040
|
+
"kt",
|
|
18041
|
+
"rb",
|
|
18042
|
+
"rs",
|
|
18043
|
+
"scala",
|
|
18044
|
+
"c",
|
|
18045
|
+
"cpp",
|
|
18046
|
+
"cc",
|
|
18047
|
+
"h",
|
|
18048
|
+
"hpp",
|
|
18049
|
+
"cs",
|
|
18050
|
+
"php",
|
|
18051
|
+
"swift",
|
|
18052
|
+
"dart",
|
|
18053
|
+
"lua",
|
|
18054
|
+
"sh",
|
|
18055
|
+
"bash",
|
|
18056
|
+
"zsh",
|
|
18057
|
+
"svelte",
|
|
18058
|
+
"vue",
|
|
18059
|
+
"ex",
|
|
18060
|
+
"exs",
|
|
18061
|
+
// sql/tf carry REAL call sites (SELECT my_function(...), module.name) —
|
|
18062
|
+
// excluded in an earlier round because of migration-comment noise, which the
|
|
18063
|
+
// COMMENT_LINE filter now handles on its own.
|
|
18064
|
+
"sql",
|
|
18065
|
+
"tf",
|
|
18066
|
+
"hcl"
|
|
18067
|
+
]);
|
|
18068
|
+
var COMMENT_LINE = /^(\/\/|#(?!\[)|\*|\/\*|--\s|<!--)/;
|
|
18069
|
+
function isCodeSiteFile(path) {
|
|
18070
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
18071
|
+
return SITE_CODE_EXTENSIONS.has(ext);
|
|
18072
|
+
}
|
|
18073
|
+
function isTestPath(path) {
|
|
18074
|
+
return TEST_PATH.test(path) || TEST_FILE.test(path) || TEST_PY_PREFIX.test(path);
|
|
18075
|
+
}
|
|
18076
|
+
function parseRgLine(line) {
|
|
18077
|
+
const first = line.indexOf(":");
|
|
18078
|
+
if (first <= 0) return null;
|
|
18079
|
+
const second = line.indexOf(":", first + 1);
|
|
18080
|
+
if (second < 0) return null;
|
|
18081
|
+
const n = parseInt(line.slice(first + 1, second), 10);
|
|
18082
|
+
if (!Number.isFinite(n) || n < 1) return null;
|
|
18083
|
+
return { file: line.slice(0, first), line: n, text: line.slice(second + 1) };
|
|
18084
|
+
}
|
|
18085
|
+
var isWordChar = (c) => c !== void 0 && /[A-Za-z0-9_]/.test(c);
|
|
18086
|
+
function wordHit(text, symbol) {
|
|
18087
|
+
let from = 0;
|
|
18088
|
+
for (; ; ) {
|
|
18089
|
+
const at = text.indexOf(symbol, from);
|
|
18090
|
+
if (at < 0) return false;
|
|
18091
|
+
const before = at === 0 ? void 0 : text[at - 1];
|
|
18092
|
+
const after = text[at + symbol.length];
|
|
18093
|
+
if (!isWordChar(before) && !isWordChar(after)) return true;
|
|
18094
|
+
from = at + 1;
|
|
18095
|
+
}
|
|
18096
|
+
}
|
|
18097
|
+
function isContractLine(text, symbol) {
|
|
18098
|
+
for (const kw of ["implements", "extends"]) {
|
|
18099
|
+
let from = 0;
|
|
18100
|
+
for (; ; ) {
|
|
18101
|
+
const at = text.indexOf(kw, from);
|
|
18102
|
+
if (at < 0) break;
|
|
18103
|
+
from = at + 1;
|
|
18104
|
+
const before = at === 0 ? void 0 : text[at - 1];
|
|
18105
|
+
const after = text[at + kw.length];
|
|
18106
|
+
if (isWordChar(before) || isWordChar(after)) continue;
|
|
18107
|
+
let clause = text.slice(at + kw.length);
|
|
18108
|
+
const stop = Math.min(
|
|
18109
|
+
...[clause.indexOf(";"), clause.indexOf("{")].filter((i) => i >= 0)
|
|
18110
|
+
);
|
|
18111
|
+
if (Number.isFinite(stop)) clause = clause.slice(0, stop);
|
|
18112
|
+
if (wordHit(clause, symbol)) return true;
|
|
18113
|
+
}
|
|
18114
|
+
}
|
|
18115
|
+
return false;
|
|
18116
|
+
}
|
|
18117
|
+
function partitionSites(rgLines, symbols, opts) {
|
|
18118
|
+
const hits = [];
|
|
18119
|
+
const hitCount = /* @__PURE__ */ new Map();
|
|
18120
|
+
for (const line of rgLines) {
|
|
18121
|
+
const hit = parseRgLine(line);
|
|
18122
|
+
if (!hit) continue;
|
|
18123
|
+
const matched = symbols.filter((s) => wordHit(hit.text, s));
|
|
18124
|
+
for (const s of matched) hitCount.set(s, (hitCount.get(s) ?? 0) + 1);
|
|
18125
|
+
if (matched.length === 0) continue;
|
|
18126
|
+
hits.push({ ...hit, symbol: matched[0] });
|
|
18127
|
+
}
|
|
18128
|
+
hits.sort((a, b) => a.file < b.file ? -1 : a.file > b.file ? 1 : a.line - b.line);
|
|
18129
|
+
const dropped = symbols.filter((s) => (hitCount.get(s) ?? 0) > MAX_HITS_PER_SYMBOL);
|
|
18130
|
+
const droppedSet = new Set(dropped);
|
|
18131
|
+
const perFile = /* @__PURE__ */ new Map();
|
|
18132
|
+
const callers = [];
|
|
18133
|
+
const tests = [];
|
|
18134
|
+
for (const h of hits) {
|
|
18135
|
+
if (droppedSet.has(h.symbol)) continue;
|
|
18136
|
+
if (opts.sentPaths.has(h.file)) continue;
|
|
18137
|
+
if (opts.isExcluded(h.file)) continue;
|
|
18138
|
+
if (!isCodeSiteFile(h.file)) continue;
|
|
18139
|
+
const text = h.text.trim();
|
|
18140
|
+
if (text.length === 0 || IMPORT_LINE.test(h.text)) continue;
|
|
18141
|
+
if (COMMENT_LINE.test(text)) continue;
|
|
18142
|
+
if (BARE_MEMBER_LINE.test(text) || GO_IMPORT_PATH_LINE.test(text)) continue;
|
|
18143
|
+
const n = perFile.get(h.file) ?? 0;
|
|
18144
|
+
if (n >= MAX_SITES_PER_FILE) continue;
|
|
18145
|
+
const site = {
|
|
18146
|
+
file: h.file,
|
|
18147
|
+
line: h.line,
|
|
18148
|
+
text: text.slice(0, SITE_TEXT_MAX),
|
|
18149
|
+
symbol: h.symbol,
|
|
18150
|
+
// R2 falls out of R1 for free: a word search for `Sym` already matches
|
|
18151
|
+
// `implements Sym` / `extends Sym` lines — classification is all R2 is.
|
|
18152
|
+
...isContractLine(text, h.symbol) ? { kind: "contract" } : {}
|
|
18153
|
+
};
|
|
18154
|
+
if (isTestPath(h.file)) {
|
|
18155
|
+
if (tests.length < MAX_TEST_SLOTS) {
|
|
18156
|
+
tests.push(site);
|
|
18157
|
+
perFile.set(h.file, n + 1);
|
|
18158
|
+
}
|
|
18159
|
+
} else if (callers.length + tests.length < MAX_SITES) {
|
|
18160
|
+
callers.push(site);
|
|
18161
|
+
perFile.set(h.file, n + 1);
|
|
18162
|
+
}
|
|
18163
|
+
}
|
|
18164
|
+
while (callers.length + tests.length > MAX_SITES) callers.pop();
|
|
18165
|
+
return { callers, tests, dropped };
|
|
18166
|
+
}
|
|
18167
|
+
function buildRepoContext(input) {
|
|
18168
|
+
const started = Date.now();
|
|
18169
|
+
let signalsByPath;
|
|
18170
|
+
if (input.signalsByPath) {
|
|
18171
|
+
if (input.signalsByPath.size === 0) return { state: "absent", reason: "no-diffs" };
|
|
18172
|
+
signalsByPath = input.signalsByPath;
|
|
18173
|
+
} else {
|
|
18174
|
+
if (input.diffs.length === 0) return { state: "absent", reason: "no-diffs" };
|
|
18175
|
+
signalsByPath = /* @__PURE__ */ new Map();
|
|
18176
|
+
for (const d of input.diffs) signalsByPath.set(d.path, parseDiffSignals(d.diff));
|
|
18177
|
+
}
|
|
18178
|
+
const collected = [];
|
|
18179
|
+
const unsupported = /* @__PURE__ */ new Set();
|
|
18180
|
+
const noteUnsupported = (path) => {
|
|
18181
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
18182
|
+
if (ext && !RULES_BY_EXT[ext]) unsupported.add(ext);
|
|
18183
|
+
};
|
|
18184
|
+
const deltaPathSet = new Set(input.deltaFiles.map((f) => f.path));
|
|
18185
|
+
for (const f of input.deltaFiles) {
|
|
18186
|
+
const signals = signalsByPath.get(f.path);
|
|
18187
|
+
if (!signals) continue;
|
|
18188
|
+
noteUnsupported(f.path);
|
|
18189
|
+
collected.push(...extractFileSymbols(f.path, f.content, signals));
|
|
18190
|
+
}
|
|
18191
|
+
for (const [path, signals] of signalsByPath) {
|
|
18192
|
+
if (deltaPathSet.has(path)) continue;
|
|
18193
|
+
if (signals.deletedLines.length > 0) {
|
|
18194
|
+
noteUnsupported(path);
|
|
18195
|
+
collected.push(...extractFileSymbols(path, "", signals));
|
|
18196
|
+
}
|
|
18197
|
+
}
|
|
18198
|
+
const unsupportedExts = [...unsupported].sort().slice(0, 8);
|
|
18199
|
+
const audit = unsupportedExts.length > 0 ? { unsupported_exts: unsupportedExts } : {};
|
|
18200
|
+
const symbols = rankSymbols([...new Set(collected)]);
|
|
18201
|
+
if (symbols.length === 0) {
|
|
18202
|
+
const everySupportedFileFoundNothing = unsupportedExts.length > 0;
|
|
18203
|
+
return {
|
|
18204
|
+
state: "absent",
|
|
18205
|
+
reason: everySupportedFileFoundNothing ? "unsupported-language" : "no-symbols",
|
|
18206
|
+
...audit
|
|
18207
|
+
};
|
|
18208
|
+
}
|
|
18209
|
+
const args = [
|
|
18210
|
+
"-n",
|
|
18211
|
+
"-w",
|
|
18212
|
+
"-F",
|
|
18213
|
+
"--no-heading",
|
|
18214
|
+
"--color",
|
|
18215
|
+
"never",
|
|
18216
|
+
// ⚠ NO `--sort path` — it single-threads rg, and on a large worktree that
|
|
18217
|
+
// is the difference between 17ms and a timeout. Determinism is restored by
|
|
18218
|
+
// sorting the hits in partitionSites instead.
|
|
18219
|
+
//
|
|
18220
|
+
// `-m 8` bounds output PER FILE (shared across all patterns), so a noisy
|
|
18221
|
+
// repo cannot blow the 4MB read buffer and turn the whole feature into
|
|
18222
|
+
// `absent/error`. Cost, accepted: the frequency gate sees per-file-capped
|
|
18223
|
+
// counts, so a symbol concentrated in a handful of files can slip a gate
|
|
18224
|
+
// a full count would have tripped — but the ≤3-sites-per-file cap already
|
|
18225
|
+
// bounds exactly that shape's damage; the gate exists for the many-file
|
|
18226
|
+
// 'init' shape, which 8-per-file still trips (>6 files ⇒ >50).
|
|
18227
|
+
"-m",
|
|
18228
|
+
"8",
|
|
18229
|
+
"--max-columns",
|
|
18230
|
+
"300",
|
|
18231
|
+
"--max-columns-preview",
|
|
18232
|
+
...symbols.flatMap((s) => ["-e", s]),
|
|
18233
|
+
"-g",
|
|
18234
|
+
"!**/{dist,build,out,vendor,node_modules,.git,coverage,target,__pycache__}/**",
|
|
18235
|
+
"./"
|
|
18236
|
+
];
|
|
18237
|
+
let res = null;
|
|
18238
|
+
for (const inv of rgInvocations()) {
|
|
18239
|
+
res = (0, import_node_child_process7.spawnSync)(inv.cmd, args, {
|
|
18240
|
+
...inv.argv0 ? { argv0: inv.argv0 } : {},
|
|
18241
|
+
cwd: input.cwd ?? process.cwd(),
|
|
18242
|
+
timeout: input.timeoutMs ?? RG_TIMEOUT_MS,
|
|
18243
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
18244
|
+
encoding: "utf8"
|
|
18245
|
+
});
|
|
18246
|
+
if (res.error?.code !== "ENOENT") break;
|
|
18247
|
+
}
|
|
18248
|
+
if (!res || res.error?.code === "ENOENT") {
|
|
18249
|
+
return { state: "absent", reason: "no-tool", symbols, ...audit };
|
|
18250
|
+
}
|
|
18251
|
+
if (res.error) {
|
|
18252
|
+
const code = res.error.code;
|
|
18253
|
+
if (code === "ETIMEDOUT") return { state: "absent", reason: "timeout", symbols, ...audit };
|
|
18254
|
+
return { state: "absent", reason: "error", symbols, ...audit };
|
|
18255
|
+
}
|
|
18256
|
+
if (res.signal) return { state: "absent", reason: "timeout", symbols, ...audit };
|
|
18257
|
+
if (res.status !== 0 && res.status !== 1) return { state: "absent", reason: "error", symbols, ...audit };
|
|
18258
|
+
const lines = (res.stdout ?? "").split("\n").map((l) => l.replace(/^\.\//, "")).filter(Boolean);
|
|
18259
|
+
const { callers, tests, dropped } = partitionSites(lines, symbols, {
|
|
18260
|
+
sentPaths: input.sentPaths,
|
|
18261
|
+
isExcluded: input.isExcluded
|
|
18262
|
+
});
|
|
18263
|
+
if (callers.length === 0 && tests.length === 0) {
|
|
18264
|
+
return {
|
|
18265
|
+
state: "absent",
|
|
18266
|
+
reason: "no-sites",
|
|
18267
|
+
symbols,
|
|
18268
|
+
...dropped.length > 0 ? { dropped_symbols: dropped } : {},
|
|
18269
|
+
...audit,
|
|
18270
|
+
elapsed_ms: Date.now() - started
|
|
18271
|
+
};
|
|
18272
|
+
}
|
|
18273
|
+
return {
|
|
18274
|
+
state: "ok",
|
|
18275
|
+
symbols,
|
|
18276
|
+
...dropped.length > 0 ? { dropped_symbols: dropped } : {},
|
|
18277
|
+
...audit,
|
|
18278
|
+
callers,
|
|
18279
|
+
tests,
|
|
18280
|
+
elapsed_ms: Date.now() - started
|
|
18281
|
+
};
|
|
18282
|
+
}
|
|
18283
|
+
var MAX_EXCERPTS = 12;
|
|
18284
|
+
var MAX_EXCERPTS_PER_FILE = 2;
|
|
18285
|
+
var EXCERPT_MAX_LINES = 30;
|
|
18286
|
+
var EXCERPT_MAX_CHARS = 2400;
|
|
18287
|
+
var EXCERPT_TOTAL_BYTES = 24576;
|
|
18288
|
+
var EXCERPT_DECL_SCAN = 40;
|
|
18289
|
+
function extractEnclosingExcerpt(content, siteLine, path) {
|
|
18290
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
18291
|
+
const rules = RULES_BY_EXT[ext] ?? [];
|
|
18292
|
+
const lines = content.split("\n");
|
|
18293
|
+
if (siteLine < 1 || siteLine > lines.length) return null;
|
|
18294
|
+
let declStart = null;
|
|
18295
|
+
const floor = Math.max(1, siteLine - EXCERPT_DECL_SCAN);
|
|
18296
|
+
for (let n = siteLine; n >= floor; n--) {
|
|
18297
|
+
if (declNameOn(lines[n - 1] ?? "", rules) !== null) {
|
|
18298
|
+
declStart = n;
|
|
18299
|
+
break;
|
|
18300
|
+
}
|
|
18301
|
+
}
|
|
18302
|
+
let start;
|
|
18303
|
+
if (declStart !== null && siteLine - declStart < EXCERPT_MAX_LINES) {
|
|
18304
|
+
start = declStart;
|
|
18305
|
+
} else {
|
|
18306
|
+
start = Math.max(1, siteLine - (EXCERPT_MAX_LINES - 6));
|
|
18307
|
+
}
|
|
18308
|
+
const end = Math.min(lines.length, start + EXCERPT_MAX_LINES - 1);
|
|
18309
|
+
const text = lines.slice(start - 1, end).join("\n").slice(0, EXCERPT_MAX_CHARS);
|
|
18310
|
+
return { start_line: start, text };
|
|
18311
|
+
}
|
|
18312
|
+
function upgradeToExcerpts(rc, opts) {
|
|
18313
|
+
if (rc.state !== "ok") return;
|
|
18314
|
+
const ranked = [
|
|
18315
|
+
...(rc.callers ?? []).filter((s) => s.kind === "contract"),
|
|
18316
|
+
...rc.tests ?? [],
|
|
18317
|
+
...(rc.callers ?? []).filter((s) => s.kind !== "contract")
|
|
18318
|
+
];
|
|
18319
|
+
const perFile = /* @__PURE__ */ new Map();
|
|
18320
|
+
const contentCache = /* @__PURE__ */ new Map();
|
|
18321
|
+
const excerpts = [];
|
|
18322
|
+
let totalBytes = 0;
|
|
18323
|
+
for (const site of ranked) {
|
|
18324
|
+
if (excerpts.length >= MAX_EXCERPTS) break;
|
|
18325
|
+
const used = perFile.get(site.file) ?? 0;
|
|
18326
|
+
if (used >= MAX_EXCERPTS_PER_FILE) continue;
|
|
18327
|
+
if (!contentCache.has(site.file)) contentCache.set(site.file, opts.readFile(site.file));
|
|
18328
|
+
const content = contentCache.get(site.file);
|
|
18329
|
+
if (content === null || content === void 0) continue;
|
|
18330
|
+
const ex = extractEnclosingExcerpt(content, site.line, site.file);
|
|
18331
|
+
if (!ex) continue;
|
|
18332
|
+
if (totalBytes + ex.text.length > EXCERPT_TOTAL_BYTES) break;
|
|
18333
|
+
excerpts.push({
|
|
18334
|
+
file: site.file,
|
|
18335
|
+
start_line: ex.start_line,
|
|
18336
|
+
symbol: site.symbol,
|
|
18337
|
+
kind: site.kind === "contract" ? "contract" : isTestPath(site.file) ? "test" : "caller",
|
|
18338
|
+
text: ex.text
|
|
18339
|
+
});
|
|
18340
|
+
totalBytes += ex.text.length;
|
|
18341
|
+
perFile.set(site.file, used + 1);
|
|
18342
|
+
}
|
|
18343
|
+
if (excerpts.length > 0) rc.excerpts = excerpts;
|
|
18344
|
+
}
|
|
18345
|
+
function describeRepoContext(rc) {
|
|
18346
|
+
if (rc.state !== "ok") {
|
|
18347
|
+
const exts = rc.unsupported_exts?.length ? ` \xB7 no rules for: ${rc.unsupported_exts.join(", ")}` : "";
|
|
18348
|
+
return `absent (${rc.reason ?? "unknown"})${exts}`;
|
|
18349
|
+
}
|
|
18350
|
+
const parts = [
|
|
18351
|
+
`${rc.symbols?.length ?? 0} symbol(s) \u2192 ${rc.callers?.length ?? 0} caller(s) \xB7 ${rc.tests?.length ?? 0} test(s)`
|
|
18352
|
+
];
|
|
18353
|
+
if (rc.excerpts?.length) parts.push(`${rc.excerpts.length} excerpt(s)`);
|
|
18354
|
+
if (rc.dropped_symbols?.length) parts.push(`dropped too-common: ${rc.dropped_symbols.join(", ")}`);
|
|
18355
|
+
if (rc.unsupported_exts?.length) parts.push(`no rules for: ${rc.unsupported_exts.join(", ")}`);
|
|
18356
|
+
if (typeof rc.elapsed_ms === "number") parts.push(`${rc.elapsed_ms}ms`);
|
|
18357
|
+
return parts.join(" \xB7 ");
|
|
18358
|
+
}
|
|
18359
|
+
|
|
17506
18360
|
// src/commands/analyze/evidence-log.ts
|
|
17507
18361
|
function list(paths, cap = 12) {
|
|
17508
18362
|
if (paths.length === 0) return "(none)";
|
|
@@ -17513,35 +18367,36 @@ function row(label2, value) {
|
|
|
17513
18367
|
return ` \u25B8 ${label2.padEnd(10)} ${value}
|
|
17514
18368
|
`;
|
|
17515
18369
|
}
|
|
17516
|
-
function formatRunEvidence(
|
|
18370
|
+
function formatRunEvidence(run2, startedAt) {
|
|
17517
18371
|
const ms = Date.now() - startedAt;
|
|
17518
|
-
const sent =
|
|
17519
|
-
const context =
|
|
18372
|
+
const sent = run2.codeDelta.files.filter((f) => f.role !== "context").map((f) => f.path);
|
|
18373
|
+
const context = run2.codeDelta.files.filter((f) => f.role === "context").map((f) => f.path);
|
|
17520
18374
|
let out = ` \u2500\u2500 what verity saw \u2500\u2500
|
|
17521
18375
|
`;
|
|
17522
|
-
out += row("turn", `${
|
|
17523
|
-
out += row("reached", `${
|
|
17524
|
-
if (
|
|
17525
|
-
const f =
|
|
18376
|
+
out += row("turn", `${run2.turnId || "(unminted)"}${run2.sessionId ? ` \xB7 session ${run2.sessionId}` : ""}`);
|
|
18377
|
+
out += row("reached", `${run2.phaseReached || "(none)"}${run2.skipReason ? ` \xB7 SKIPPED: ${run2.skipReason}` : ""} \xB7 ${ms}ms`);
|
|
18378
|
+
if (run2.treeFrame) {
|
|
18379
|
+
const f = run2.treeFrame;
|
|
17526
18380
|
out += row("tree", f.worktreeRoot ? `${f.worktreeRoot}${f.isLinkedWorktree ? " \xB7 linked worktree" : ""}${f.branch ? ` \xB7 branch ${f.branch}` : " \xB7 detached"}` : `(unresolved: ${f.refusal ?? "unknown"})`);
|
|
17527
18381
|
}
|
|
17528
|
-
out += row("changed", `${
|
|
17529
|
-
const done = (phase) =>
|
|
18382
|
+
out += row("changed", `${run2.changedUniverse.length} from git \xB7 analyzable ${run2.analyzable.length} \xB7 reviewable ${run2.reviewable.length} \xB7 security ${run2.securityFiles.length} \xB7 forReview ${run2.allForReview.length}`);
|
|
18383
|
+
const done = (phase) => run2.phasesCompleted.includes(phase);
|
|
17530
18384
|
const ifDone = (phase, value) => done(phase) ? value : "?";
|
|
17531
|
-
const md =
|
|
18385
|
+
const md = run2.modeDecision;
|
|
17532
18386
|
if (md) {
|
|
17533
18387
|
const how = md.forced ? "forced by --mode" : `predicted=${md.predicted ?? "none"} \u2192 ${md.resolved}`;
|
|
17534
18388
|
out += row("mode", `${md.resolved} \xB7 ${how}` + (md.flip ? ` (flipped to plan: no delta at ${md.flip})` : "") + ` \xB7 authored=${md.authored ? "yes" : "no"} \xB7 investigated=${md.investigated ? "yes" : "no"}`);
|
|
17535
18389
|
} else {
|
|
17536
|
-
out += row("mode", `? (this run stopped in ${
|
|
18390
|
+
out += row("mode", `? (this run stopped in ${run2.phaseReached || "no phase"}, before the mode was decided)`);
|
|
17537
18391
|
}
|
|
17538
|
-
out += row("signals", `baseline=${ifDone("bootstrap",
|
|
18392
|
+
out += row("signals", `baseline=${ifDone("bootstrap", run2.baseline ? "yes" : "no")} \xB7 authored=${ifDone("intentInputs", run2.turnAuthoredCode ? "yes" : "no")} \xB7 observable=${ifDone("intentInputs", run2.authorshipIsObservable ? "yes" : "no")}` + (run2.actionSummary?.transcript_windowed ? ` \xB7 window=${run2.actionSummary.transcript_windowed}` : ""));
|
|
17539
18393
|
if (!done("intentInputs")) {
|
|
17540
|
-
out += row("", `(\`?\` = the phase that determines it did not complete \u2014 this run stopped in ${
|
|
18394
|
+
out += row("", `(\`?\` = the phase that determines it did not complete \u2014 this run stopped in ${run2.phaseReached})`);
|
|
17541
18395
|
}
|
|
17542
18396
|
out += row("sent", `${sent.length} \xB7 ${list(sent)}`);
|
|
17543
18397
|
if (context.length > 0) out += row("context", `${context.length} \xB7 ${list(context)}`);
|
|
17544
|
-
|
|
18398
|
+
if (run2.repoContext) out += row("repo", describeRepoContext(run2.repoContext));
|
|
18399
|
+
const withheld = run2.reviewCoverage.notReviewed;
|
|
17545
18400
|
if (withheld.length > 0) {
|
|
17546
18401
|
const byReason = /* @__PURE__ */ new Map();
|
|
17547
18402
|
for (const w of withheld) {
|
|
@@ -17554,18 +18409,18 @@ function formatRunEvidence(run, startedAt) {
|
|
|
17554
18409
|
first = false;
|
|
17555
18410
|
}
|
|
17556
18411
|
} else {
|
|
17557
|
-
const sentSet = new Set(
|
|
17558
|
-
const notSent =
|
|
18412
|
+
const sentSet = new Set(run2.codeDelta.files.map((f) => f.path));
|
|
18413
|
+
const notSent = run2.changedUniverse.filter((p) => !sentSet.has(p));
|
|
17559
18414
|
if (notSent.length > 0) {
|
|
17560
18415
|
out += row("not sent", `${list(notSent)}`);
|
|
17561
|
-
out += row("", `(stage unknown \u2014 the coverage ledger is built in phase 13, and this run reached ${
|
|
17562
|
-
} else if (
|
|
18416
|
+
out += row("", `(stage unknown \u2014 the coverage ledger is built in phase 13, and this run reached ${run2.phaseReached || "no phase"})`);
|
|
18417
|
+
} else if (run2.changedUniverse.length > 0) {
|
|
17563
18418
|
out += row("withheld", "(nothing \u2014 every changed file was reviewed)");
|
|
17564
18419
|
}
|
|
17565
18420
|
}
|
|
17566
|
-
const cov =
|
|
18421
|
+
const cov = run2.foldResult?.coverage;
|
|
17567
18422
|
if (cov) {
|
|
17568
|
-
const delegated =
|
|
18423
|
+
const delegated = run2.foldResult.authored.filter((a) => a.owner === "subagent").length;
|
|
17569
18424
|
if (cov.dispatched > 0 || cov.subagentFiles > 0 || cov.subagentSkipped > 0) {
|
|
17570
18425
|
out += row("delegated", `${cov.dispatched} dispatched \xB7 ${cov.subagentFiles} agent log(s) read \xB7 ${delegated} path(s) attributed to subagents`);
|
|
17571
18426
|
}
|
|
@@ -17579,52 +18434,52 @@ function formatRunEvidence(run, startedAt) {
|
|
|
17579
18434
|
out += row("", `${cov.outsideRepo} authored path(s) refused as outside the repo`);
|
|
17580
18435
|
}
|
|
17581
18436
|
}
|
|
17582
|
-
if (
|
|
17583
|
-
const shown =
|
|
18437
|
+
if (run2.foldResult?.tools?.length) {
|
|
18438
|
+
const shown = run2.foldResult.tools.slice(0, 6).map((t) => {
|
|
17584
18439
|
const outcome = t.failed > 0 ? `${t.failed} failed` : t.last_status === 0 ? "ok" : "?";
|
|
17585
18440
|
const where = t.targets.length > 0 ? ` \u2192 ${t.targets.slice(0, 2).join(", ")}` : "";
|
|
17586
18441
|
return `${t.runs}\xD7 ${t.name} (${outcome})${where}`;
|
|
17587
18442
|
});
|
|
17588
|
-
const more =
|
|
18443
|
+
const more = run2.foldResult.tools.length > 6 ? ` \u2026 +${run2.foldResult.tools.length - 6} more` : "";
|
|
17589
18444
|
out += row("tools", shown.join(" \xB7 ") + more);
|
|
17590
|
-
if (
|
|
17591
|
-
out += row("", `\u26A0 ${
|
|
18445
|
+
if (run2.foldResult.coverage.toolNamesDropped > 0) {
|
|
18446
|
+
out += row("", `\u26A0 ${run2.foldResult.coverage.toolNamesDropped} tool name(s) refused by the cap`);
|
|
17592
18447
|
}
|
|
17593
18448
|
}
|
|
17594
|
-
if (
|
|
17595
|
-
const t =
|
|
18449
|
+
if (run2.foldResult?.tasks?.length) {
|
|
18450
|
+
const t = run2.foldResult.tasks;
|
|
17596
18451
|
const done2 = t.filter((x) => x.status === "completed").length;
|
|
17597
18452
|
out += row("tasks", `${t.length} \xB7 ${done2} completed \xB7 ` + list(t.slice(0, 4).map((x) => `#${x.id} ${x.name} [${x.status}]`), 4));
|
|
17598
18453
|
}
|
|
17599
|
-
if (
|
|
17600
|
-
const readThisSession = new Set(
|
|
17601
|
-
const labelled =
|
|
18454
|
+
if (run2.specs?.length) {
|
|
18455
|
+
const readThisSession = new Set(run2.actionSummary?.files_read ?? []);
|
|
18456
|
+
const labelled = run2.specs.map(
|
|
17602
18457
|
(s) => `${s.path}${readThisSession.has(s.path) ? " (read)" : " (positional)"}`
|
|
17603
18458
|
);
|
|
17604
|
-
out += row("specs", `${
|
|
18459
|
+
out += row("specs", `${run2.specs.length} \xB7 ${list(labelled, 5)}`);
|
|
17605
18460
|
}
|
|
17606
|
-
if (
|
|
17607
|
-
out += row("static", `${
|
|
18461
|
+
if (run2.staticResults.findings.length > 0) {
|
|
18462
|
+
out += row("static", `${run2.staticResults.findings.length} finding(s) from ${run2.staticResults.summary.tools_run.join(", ") || "no tools"}`);
|
|
17608
18463
|
}
|
|
17609
|
-
if (
|
|
17610
|
-
out += row("verdict",
|
|
18464
|
+
if (run2.decision && run2.decision !== "(unrecognised)") {
|
|
18465
|
+
out += row("verdict", run2.decision + (run2.silenced ? ` \xB7 agent channel silenced (${run2.silenced})` : ""));
|
|
17611
18466
|
}
|
|
17612
18467
|
return out;
|
|
17613
18468
|
}
|
|
17614
|
-
function installRunEvidence(
|
|
18469
|
+
function installRunEvidence(run2) {
|
|
17615
18470
|
const startedAt = Date.now();
|
|
17616
18471
|
process.on("exit", () => {
|
|
17617
18472
|
try {
|
|
17618
|
-
logToFileOnly(formatRunEvidence(
|
|
18473
|
+
logToFileOnly(formatRunEvidence(run2, startedAt));
|
|
17619
18474
|
} catch {
|
|
17620
18475
|
}
|
|
17621
18476
|
});
|
|
17622
18477
|
}
|
|
17623
18478
|
|
|
17624
18479
|
// src/lib/git-frame.ts
|
|
17625
|
-
var
|
|
18480
|
+
var import_node_child_process8 = require("node:child_process");
|
|
17626
18481
|
var import_node_fs24 = require("node:fs");
|
|
17627
|
-
var
|
|
18482
|
+
var import_node_os4 = require("node:os");
|
|
17628
18483
|
var import_node_path18 = require("node:path");
|
|
17629
18484
|
var import_node_path19 = require("node:path");
|
|
17630
18485
|
var VALUE_TOKEN = `(?:'[^']*'|"[^"]*"|\\S+)`;
|
|
@@ -17672,14 +18527,14 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
|
|
|
17672
18527
|
if (!m) continue;
|
|
17673
18528
|
named = true;
|
|
17674
18529
|
if (m[1] === void 0) {
|
|
17675
|
-
dir = (0,
|
|
18530
|
+
dir = (0, import_node_os4.homedir)();
|
|
17676
18531
|
continue;
|
|
17677
18532
|
}
|
|
17678
18533
|
const raw = unquote(m[1]);
|
|
17679
18534
|
if (SHELL_DYNAMIC.test(raw) || raw === "-") {
|
|
17680
18535
|
return { dir: null, named: true, unresolvable: `cd target not statically resolvable: ${raw}` };
|
|
17681
18536
|
}
|
|
17682
|
-
const expanded = raw === "~" ? (0,
|
|
18537
|
+
const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
|
|
17683
18538
|
dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
|
|
17684
18539
|
}
|
|
17685
18540
|
const seg = segments[segmentIndex];
|
|
@@ -17698,7 +18553,7 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
|
|
|
17698
18553
|
if (SHELL_DYNAMIC.test(raw)) {
|
|
17699
18554
|
return { dir: null, named: true, unresolvable: `-C target not statically resolvable: ${raw}` };
|
|
17700
18555
|
}
|
|
17701
|
-
const expanded = raw === "~" ? (0,
|
|
18556
|
+
const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
|
|
17702
18557
|
dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
|
|
17703
18558
|
}
|
|
17704
18559
|
}
|
|
@@ -17755,7 +18610,7 @@ function parsePushTarget(segment) {
|
|
|
17755
18610
|
}
|
|
17756
18611
|
function gitAt(dir, args) {
|
|
17757
18612
|
try {
|
|
17758
|
-
return (0,
|
|
18613
|
+
return (0, import_node_child_process8.execFileSync)("git", args, { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
17759
18614
|
} catch {
|
|
17760
18615
|
return "";
|
|
17761
18616
|
}
|
|
@@ -17894,16 +18749,50 @@ function rangeFiles(frame, range) {
|
|
|
17894
18749
|
}
|
|
17895
18750
|
return out.split("\n").filter((l) => l.length > 0).filter((f) => !isVerityOwnedPath(f));
|
|
17896
18751
|
}
|
|
17897
|
-
function
|
|
17898
|
-
|
|
17899
|
-
|
|
17900
|
-
|
|
17901
|
-
|
|
17902
|
-
|
|
17903
|
-
|
|
17904
|
-
|
|
17905
|
-
|
|
17906
|
-
|
|
18752
|
+
function rangeChangeSignals(frame, range, paths) {
|
|
18753
|
+
const out = /* @__PURE__ */ new Map();
|
|
18754
|
+
if (range.kind === "nothing" || paths.length === 0) return out;
|
|
18755
|
+
const args = range.kind === "staged" ? ["diff", "--cached", "--unified=0"] : ["diff", "--unified=0", range.base, range.head === "INDEX" ? "HEAD" : range.head];
|
|
18756
|
+
const diff = frameGit(frame, [...args, "--", ...paths]);
|
|
18757
|
+
let current = null;
|
|
18758
|
+
let oldSide = null;
|
|
18759
|
+
let buf = [];
|
|
18760
|
+
const flush = () => {
|
|
18761
|
+
if (current !== null && buf.length > 0) out.set(current, parseDiffSignals(buf.join("\n")));
|
|
18762
|
+
buf = [];
|
|
18763
|
+
};
|
|
18764
|
+
for (const line of diff.split("\n")) {
|
|
18765
|
+
if (line.startsWith("diff --git ")) {
|
|
18766
|
+
flush();
|
|
18767
|
+
current = null;
|
|
18768
|
+
oldSide = null;
|
|
18769
|
+
continue;
|
|
18770
|
+
}
|
|
18771
|
+
const minusM = /^--- (?:a\/)?(.+)$/.exec(line);
|
|
18772
|
+
if (minusM) {
|
|
18773
|
+
oldSide = minusM[1] === "/dev/null" ? null : minusM[1];
|
|
18774
|
+
continue;
|
|
18775
|
+
}
|
|
18776
|
+
const plusM = /^\+\+\+ (?:b\/)?(.+)$/.exec(line);
|
|
18777
|
+
if (plusM) {
|
|
18778
|
+
current = plusM[1] === "/dev/null" ? oldSide : plusM[1];
|
|
18779
|
+
continue;
|
|
18780
|
+
}
|
|
18781
|
+
if (current !== null) buf.push(line);
|
|
18782
|
+
}
|
|
18783
|
+
flush();
|
|
18784
|
+
return out;
|
|
18785
|
+
}
|
|
18786
|
+
function rangeMessages(frame, range) {
|
|
18787
|
+
if (range.kind === "staged" || range.kind === "nothing" || !range.base) return "";
|
|
18788
|
+
return frameGit(frame, ["log", `${range.base}..${range.head === "INDEX" ? "HEAD" : range.head}`, "--format=%B%x00"]).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
18789
|
+
}
|
|
18790
|
+
function frameTelemetry(frame, range, divergence) {
|
|
18791
|
+
const t = {
|
|
18792
|
+
anchor: frame.anchor,
|
|
18793
|
+
linked_worktree: frame.isLinkedWorktree,
|
|
18794
|
+
range_via: range?.via ?? null,
|
|
18795
|
+
refusal: frame.refusal
|
|
17907
18796
|
};
|
|
17908
18797
|
if (divergence) {
|
|
17909
18798
|
t.root_differs = !!frame.worktreeRoot && !!divergence.actualRoot && realpathOr(frame.worktreeRoot) !== realpathOr(divergence.actualRoot);
|
|
@@ -17953,6 +18842,7 @@ var MAX_COMMANDS = 10;
|
|
|
17953
18842
|
var MAX_COMMAND_CHARS = 80;
|
|
17954
18843
|
var MAX_TOOL_BLOCKS = 200;
|
|
17955
18844
|
var MAX_SUMMARY_BYTES = 4096;
|
|
18845
|
+
var MAX_SEARCHES_DETAIL = 10;
|
|
17956
18846
|
var HOME = process.env.HOME ?? "";
|
|
17957
18847
|
var BASH_INPUT_RE = /^\s*<bash-input>([\s\S]*?)<\/bash-input>/;
|
|
17958
18848
|
var BASH_ECHO_RE = /^\s*<bash-(?:stdout|stderr)>/;
|
|
@@ -18038,6 +18928,7 @@ function buildSummary(lines) {
|
|
|
18038
18928
|
let userCommandsTruncated = false;
|
|
18039
18929
|
let commandsTruncated = false;
|
|
18040
18930
|
let searches = 0;
|
|
18931
|
+
const searchesDetail = [];
|
|
18041
18932
|
let subagents = 0;
|
|
18042
18933
|
let webFetches = 0;
|
|
18043
18934
|
let totalToolCalls = 0;
|
|
@@ -18109,9 +19000,19 @@ function buildSummary(lines) {
|
|
|
18109
19000
|
break;
|
|
18110
19001
|
}
|
|
18111
19002
|
case "Grep":
|
|
18112
|
-
case "Glob":
|
|
19003
|
+
case "Glob": {
|
|
18113
19004
|
searches++;
|
|
19005
|
+
const pattern = typeof input.pattern === "string" ? input.pattern.slice(0, 120) : "";
|
|
19006
|
+
if (pattern && searchesDetail.length < MAX_SEARCHES_DETAIL) {
|
|
19007
|
+
const scopePath = typeof input.path === "string" ? input.path.slice(0, 200) : void 0;
|
|
19008
|
+
searchesDetail.push({
|
|
19009
|
+
tool: toolName,
|
|
19010
|
+
pattern,
|
|
19011
|
+
...scopePath ? { path: scopePath } : {}
|
|
19012
|
+
});
|
|
19013
|
+
}
|
|
18114
19014
|
break;
|
|
19015
|
+
}
|
|
18115
19016
|
case "Agent":
|
|
18116
19017
|
case "Task":
|
|
18117
19018
|
case "Workflow":
|
|
@@ -18146,6 +19047,7 @@ function buildSummary(lines) {
|
|
|
18146
19047
|
...cappedOut(filesCreated, MAX_CREATED_LIST)
|
|
18147
19048
|
],
|
|
18148
19049
|
searches,
|
|
19050
|
+
...searchesDetail.length > 0 ? { searches_detail: searchesDetail } : {},
|
|
18149
19051
|
commands,
|
|
18150
19052
|
...commandsTruncated ? { commands_truncated: true } : {},
|
|
18151
19053
|
user_commands: userCommands,
|
|
@@ -18156,6 +19058,9 @@ function buildSummary(lines) {
|
|
|
18156
19058
|
turn_messages: turnMessages,
|
|
18157
19059
|
turn_duration_ms: turnDurationMs
|
|
18158
19060
|
};
|
|
19061
|
+
if (JSON.stringify(summary).length > MAX_SUMMARY_BYTES) {
|
|
19062
|
+
delete summary.searches_detail;
|
|
19063
|
+
}
|
|
18159
19064
|
if (JSON.stringify(summary).length > MAX_SUMMARY_BYTES) {
|
|
18160
19065
|
summary.commands = [];
|
|
18161
19066
|
summary.commands_truncated = true;
|
|
@@ -18266,13 +19171,13 @@ async function readStopHookStdin() {
|
|
|
18266
19171
|
return empty;
|
|
18267
19172
|
}
|
|
18268
19173
|
}
|
|
18269
|
-
async function bootstrap(
|
|
18270
|
-
const { opts, globals } =
|
|
19174
|
+
async function bootstrap(run2) {
|
|
19175
|
+
const { opts, globals } = run2;
|
|
18271
19176
|
try {
|
|
18272
19177
|
process.chdir(repoRoot());
|
|
18273
19178
|
} catch {
|
|
18274
19179
|
}
|
|
18275
|
-
|
|
19180
|
+
run2.treeFrame = resolveFrame({ command: "", on: [], hookCwd: null }).frame;
|
|
18276
19181
|
const turnId = `t-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
18277
19182
|
let reachability = resolveReachability({
|
|
18278
19183
|
autonomousFlag: process.env.VERITY_AUTONOMOUS === "1" || opts.mode === "autonomous",
|
|
@@ -18286,7 +19191,7 @@ async function bootstrap(run) {
|
|
|
18286
19191
|
const rawSessionId = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
|
|
18287
19192
|
const baselineSessionId = sessionScopeKey(scopeToken, rawSessionId);
|
|
18288
19193
|
if (tokenResult.ok) {
|
|
18289
|
-
|
|
19194
|
+
run2.beacon = {
|
|
18290
19195
|
resolveServiceUrl: async () => {
|
|
18291
19196
|
const u = await resolveServiceUrl(globals.serviceUrl);
|
|
18292
19197
|
return u.ok ? u.data : null;
|
|
@@ -18306,7 +19211,7 @@ async function bootstrap(run) {
|
|
|
18306
19211
|
age_ms: Date.now() - baseline.captured_at
|
|
18307
19212
|
});
|
|
18308
19213
|
}
|
|
18309
|
-
Object.assign(
|
|
19214
|
+
Object.assign(run2, { actionSummary, assistantResponse, baseline, baselineSessionId, reachability, sessionId, stopReason, tokenResult, transcriptPath, turnId });
|
|
18310
19215
|
}
|
|
18311
19216
|
|
|
18312
19217
|
// src/lib/self-scope.ts
|
|
@@ -18459,7 +19364,7 @@ function channelSilence(input) {
|
|
|
18459
19364
|
// src/lib/cli-version.ts
|
|
18460
19365
|
function cliVersion() {
|
|
18461
19366
|
try {
|
|
18462
|
-
return true ? "0.31.1-experimental.
|
|
19367
|
+
return true ? "0.31.1-experimental.b9b3862" : "dev";
|
|
18463
19368
|
} catch {
|
|
18464
19369
|
return "dev";
|
|
18465
19370
|
}
|
|
@@ -18499,7 +19404,7 @@ async function sendSkipBeacon(ctx, reason) {
|
|
|
18499
19404
|
}
|
|
18500
19405
|
|
|
18501
19406
|
// src/lib/static-analysis.ts
|
|
18502
|
-
var
|
|
19407
|
+
var import_node_child_process9 = require("node:child_process");
|
|
18503
19408
|
var import_node_fs26 = require("node:fs");
|
|
18504
19409
|
var SEVERITY_ORDER = {
|
|
18505
19410
|
Error: 0,
|
|
@@ -18512,7 +19417,7 @@ var SEVERITY_ORDER = {
|
|
|
18512
19417
|
};
|
|
18513
19418
|
function isCodacyAvailable() {
|
|
18514
19419
|
try {
|
|
18515
|
-
(0,
|
|
19420
|
+
(0, import_node_child_process9.execSync)("which codacy-analysis", { stdio: "pipe" });
|
|
18516
19421
|
return true;
|
|
18517
19422
|
} catch {
|
|
18518
19423
|
return false;
|
|
@@ -18554,7 +19459,7 @@ function runCodacyAnalysis(files) {
|
|
|
18554
19459
|
}
|
|
18555
19460
|
});
|
|
18556
19461
|
if (existingFiles.length === 0) return empty;
|
|
18557
|
-
const proc = (0,
|
|
19462
|
+
const proc = (0, import_node_child_process9.spawnSync)("codacy-analysis", buildAnalyzerArgv(existingFiles), {
|
|
18558
19463
|
encoding: "utf-8",
|
|
18559
19464
|
maxBuffer: 10 * 1024 * 1024
|
|
18560
19465
|
});
|
|
@@ -18565,12 +19470,12 @@ function runCodacyAnalysis(files) {
|
|
|
18565
19470
|
spawnError: proc.error?.message
|
|
18566
19471
|
});
|
|
18567
19472
|
}
|
|
18568
|
-
function interpretAnalyzerRun(
|
|
18569
|
-
const output =
|
|
19473
|
+
function interpretAnalyzerRun(run2) {
|
|
19474
|
+
const output = run2.stdout ?? "";
|
|
18570
19475
|
if (!output.trim()) {
|
|
18571
19476
|
return withFailure(
|
|
18572
|
-
|
|
18573
|
-
|
|
19477
|
+
run2.spawnError ? "spawn_failed" : "no_output",
|
|
19478
|
+
run2.spawnError ?? run2.stderr ?? `exit ${run2.status}`
|
|
18574
19479
|
);
|
|
18575
19480
|
}
|
|
18576
19481
|
let parsed;
|
|
@@ -18737,9 +19642,9 @@ function localOnlyAndExit(staticResults) {
|
|
|
18737
19642
|
});
|
|
18738
19643
|
process.exit(0);
|
|
18739
19644
|
}
|
|
18740
|
-
async function passAndExit(
|
|
18741
|
-
|
|
18742
|
-
const sent = await sendSkipBeacon(
|
|
19645
|
+
async function passAndExit(run2, reason, skip, kindOverride) {
|
|
19646
|
+
run2.skipReason = skip;
|
|
19647
|
+
const sent = await sendSkipBeacon(run2.beacon, skip);
|
|
18743
19648
|
logEvent("skip", { reason: skip, beacon: sent });
|
|
18744
19649
|
const POLICY_SKIPS = /* @__PURE__ */ new Set([
|
|
18745
19650
|
"no-analyzable-files",
|
|
@@ -18754,7 +19659,7 @@ async function passAndExit(run, reason, skip, kindOverride) {
|
|
|
18754
19659
|
"no-delta-since-last-review"
|
|
18755
19660
|
]);
|
|
18756
19661
|
const skipKind = kindOverride ?? (POLICY_SKIPS.has(skip) ? "policy" : "capacity");
|
|
18757
|
-
const changed =
|
|
19662
|
+
const changed = run2.changedUniverse;
|
|
18758
19663
|
const { coverage, unaccounted } = reconcileCoverage(changed, {
|
|
18759
19664
|
reviewed: [],
|
|
18760
19665
|
notReviewed: changed.map((path) => ({ path, reason: skip, stage: "pre-flight", kind: skipKind }))
|
|
@@ -18782,10 +19687,10 @@ async function passAndExit(run, reason, skip, kindOverride) {
|
|
|
18782
19687
|
}
|
|
18783
19688
|
|
|
18784
19689
|
// src/commands/analyze/phases/02-scope.ts
|
|
18785
|
-
async function scope(
|
|
18786
|
-
const { assistantResponse } =
|
|
19690
|
+
async function scope(run2) {
|
|
19691
|
+
const { assistantResponse } = run2;
|
|
18787
19692
|
const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
|
|
18788
|
-
|
|
19693
|
+
run2.changedUniverse = allChanged;
|
|
18789
19694
|
const { kept: external } = partitionVerityOwned(allChanged);
|
|
18790
19695
|
const verityIgnore = loadVerityIgnore();
|
|
18791
19696
|
const ignored = partitionIgnored(external, verityIgnore);
|
|
@@ -18810,10 +19715,10 @@ async function scope(run) {
|
|
|
18810
19715
|
const securityFiles = filterSecurity(inScope);
|
|
18811
19716
|
const noFilesChanged = analyzable.length === 0 && reviewable.length === 0 && securityFiles.length === 0;
|
|
18812
19717
|
if (noFilesChanged && !assistantResponse) {
|
|
18813
|
-
await passAndExit(
|
|
19718
|
+
await passAndExit(run2, "No analyzable files changed", "no-analyzable-files");
|
|
18814
19719
|
}
|
|
18815
19720
|
const allForReview = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable]));
|
|
18816
|
-
Object.assign(
|
|
19721
|
+
Object.assign(run2, { allChanged, allForReview, analyzable, hasRecentCommitFiles, noFilesChanged, reviewable, securityFiles, verityIgnored: ignored });
|
|
18817
19722
|
}
|
|
18818
19723
|
|
|
18819
19724
|
// src/lib/specs.ts
|
|
@@ -18959,8 +19864,8 @@ function discoverGuardDocs(rangeFiles2) {
|
|
|
18959
19864
|
}
|
|
18960
19865
|
|
|
18961
19866
|
// src/commands/analyze/phases/03-intent-inputs.ts
|
|
18962
|
-
async function intentInputs(
|
|
18963
|
-
const { actionSummary, allForReview, assistantResponse, baseline, baselineSessionId } =
|
|
19867
|
+
async function intentInputs(run2) {
|
|
19868
|
+
const { actionSummary, allForReview, assistantResponse, baseline, baselineSessionId } = run2;
|
|
18964
19869
|
if (isCommandOnlyTurn({
|
|
18965
19870
|
userCommands: actionSummary?.user_commands,
|
|
18966
19871
|
userCommandsTruncated: actionSummary?.user_commands_truncated,
|
|
@@ -18968,11 +19873,11 @@ async function intentInputs(run) {
|
|
|
18968
19873
|
agentToolCalls: actionSummary?.total_tool_calls ?? 0,
|
|
18969
19874
|
authorshipIsObservable: !!actionSummary && actionSummary.transcript_windowed !== "orphaned"
|
|
18970
19875
|
})) {
|
|
18971
|
-
await passAndExit(
|
|
19876
|
+
await passAndExit(run2, "User command only \u2014 skipping analysis", "command-only-turn");
|
|
18972
19877
|
}
|
|
18973
19878
|
{
|
|
18974
19879
|
const ignoreKeys = ignoreStateKeys(
|
|
18975
|
-
|
|
19880
|
+
run2.tokenResult.ok ? run2.tokenResult.data.token : void 0,
|
|
18976
19881
|
null
|
|
18977
19882
|
);
|
|
18978
19883
|
const found = resolveIgnoreState([baselineSessionId, ...ignoreKeys]);
|
|
@@ -18996,7 +19901,7 @@ async function intentInputs(run) {
|
|
|
18996
19901
|
if (declaration.scope === "turn" && found) clearActiveDeclaration(found.key);
|
|
18997
19902
|
logEvent("ignore_honoured", { scope: declaration.scope, origin: declaration.origin });
|
|
18998
19903
|
await passAndExit(
|
|
18999
|
-
|
|
19904
|
+
run2,
|
|
19000
19905
|
`skipping this turn \u2014 declared housekeeping ("${declaration.reason}")`,
|
|
19001
19906
|
"declared-ignore"
|
|
19002
19907
|
);
|
|
@@ -19010,7 +19915,7 @@ async function intentInputs(run) {
|
|
|
19010
19915
|
const notice = `Verity: the ignore declared for this window ("${declaration.reason}") was voided \u2014 ${outcome.why}. Reviewing normally.`;
|
|
19011
19916
|
process.stderr.write(`${notice}
|
|
19012
19917
|
`);
|
|
19013
|
-
|
|
19918
|
+
run2.voidedIgnoreNotice = notice;
|
|
19014
19919
|
}
|
|
19015
19920
|
}
|
|
19016
19921
|
}
|
|
@@ -19032,34 +19937,33 @@ async function intentInputs(run) {
|
|
|
19032
19937
|
const adopted = absorbIntoBaseline(setupAuthored, baselineSessionId);
|
|
19033
19938
|
logEvent("baseline_absorbed", { skip: "verity-command", offered: setupAuthored.length, adopted });
|
|
19034
19939
|
}
|
|
19035
|
-
await passAndExit(
|
|
19940
|
+
await passAndExit(run2, "Verity command \u2014 skipping analysis", "verity-command");
|
|
19036
19941
|
}
|
|
19037
19942
|
if (shouldSkipForBareAck({ prompt: latestPrompt, turnAuthoredCode, canSeeTurnAuthorship })) {
|
|
19038
|
-
await passAndExit(
|
|
19943
|
+
await passAndExit(run2, "Bare acknowledgment \u2014 skipping analysis", "bare-acknowledgment");
|
|
19039
19944
|
}
|
|
19040
19945
|
if (isReflectionQuestion(assistantResponse) && !turnAuthoredCode && canSeeTurnAuthorship) {
|
|
19041
|
-
await passAndExit(
|
|
19946
|
+
await passAndExit(run2, "Reflection-prompt turn \u2014 skipping analysis", "reflection-prompt");
|
|
19042
19947
|
}
|
|
19043
|
-
Object.assign(
|
|
19948
|
+
Object.assign(run2, { authorshipIsObservable, conversation, earlyFold, plans, specs, turnAuthoredCode });
|
|
19044
19949
|
}
|
|
19045
19950
|
|
|
19046
19951
|
// src/commands/analyze/phases/04-connect.ts
|
|
19047
|
-
async function connect(
|
|
19048
|
-
const { opts, globals } =
|
|
19049
|
-
const { analyzable, baseline, securityFiles, tokenResult } =
|
|
19952
|
+
async function connect(run2) {
|
|
19953
|
+
const { opts, globals } = run2;
|
|
19954
|
+
const { analyzable, baseline, securityFiles, tokenResult } = run2;
|
|
19050
19955
|
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
19051
19956
|
if (!tokenResult.ok || !urlResult.ok) {
|
|
19052
19957
|
localOnlyAndExit(runLocalStatic(analyzable, securityFiles, baseline, !!opts.skipStatic));
|
|
19053
19958
|
}
|
|
19054
|
-
Object.assign(
|
|
19959
|
+
Object.assign(run2, { urlResult, serviceUrl: urlResult.data, token: tokenResult.data.token });
|
|
19055
19960
|
}
|
|
19056
19961
|
|
|
19057
19962
|
// src/commands/analyze/phases/05-mode.ts
|
|
19058
|
-
async function mode(
|
|
19059
|
-
const { opts, globals } =
|
|
19060
|
-
const { actionSummary, allForReview, assistantResponse, baseline, conversation, memory, noFilesChanged, serviceUrl, sessionId, token, turnAuthoredCode } =
|
|
19963
|
+
async function mode(run2) {
|
|
19964
|
+
const { opts, globals } = run2;
|
|
19965
|
+
const { actionSummary, allForReview, assistantResponse, baseline, conversation, memory, noFilesChanged, serviceUrl, sessionId, token, turnAuthoredCode } = run2;
|
|
19061
19966
|
const sessionIdForMemory = sessionId || process.env.CLAUDE_SESSION_ID || "";
|
|
19062
|
-
let contextFilePaths = [];
|
|
19063
19967
|
let predictedMode;
|
|
19064
19968
|
try {
|
|
19065
19969
|
const memoryPath2 = sessionIdForMemory ? `/memory?session_id=${encodeURIComponent(sessionIdForMemory)}` : "/memory";
|
|
@@ -19073,9 +19977,6 @@ async function mode(run) {
|
|
|
19073
19977
|
cmd: "analyze_context"
|
|
19074
19978
|
});
|
|
19075
19979
|
if (memoryResult.ok) {
|
|
19076
|
-
if (Array.isArray(memoryResult.data.context_files)) {
|
|
19077
|
-
contextFilePaths = memoryResult.data.context_files;
|
|
19078
|
-
}
|
|
19079
19980
|
const rawMode = memoryResult.data.predicted_mode;
|
|
19080
19981
|
if (rawMode && ["standard", "plan", "debug", "skip"].includes(rawMode)) {
|
|
19081
19982
|
predictedMode = rawMode;
|
|
@@ -19098,7 +19999,7 @@ async function mode(run) {
|
|
|
19098
19999
|
);
|
|
19099
20000
|
}
|
|
19100
20001
|
const investigated = didAgentInvestigate(actionSummary);
|
|
19101
|
-
|
|
20002
|
+
run2.modeDecision = {
|
|
19102
20003
|
predicted: predictedMode ?? null,
|
|
19103
20004
|
resolved: analysisMode,
|
|
19104
20005
|
authored: turnAuthoredCode,
|
|
@@ -19118,13 +20019,13 @@ async function mode(run) {
|
|
|
19118
20019
|
});
|
|
19119
20020
|
if (analysisMode === "skip") {
|
|
19120
20021
|
await passAndExit(
|
|
19121
|
-
|
|
20022
|
+
run2,
|
|
19122
20023
|
"Skip mode \u2014 no code work to analyze",
|
|
19123
20024
|
"skip-mode",
|
|
19124
20025
|
turnAuthoredCode ? "capacity" : void 0
|
|
19125
20026
|
);
|
|
19126
20027
|
}
|
|
19127
|
-
Object.assign(
|
|
20028
|
+
Object.assign(run2, { analysisMode, sessionAuthoredCode, sessionIdForMemory });
|
|
19128
20029
|
}
|
|
19129
20030
|
|
|
19130
20031
|
// src/lib/fold.ts
|
|
@@ -19584,12 +20485,12 @@ function checkConservation(changedFiles, result, repoRoot2) {
|
|
|
19584
20485
|
}
|
|
19585
20486
|
|
|
19586
20487
|
// src/commands/analyze/phases/06-evidence.ts
|
|
19587
|
-
async function evidence(
|
|
19588
|
-
const { opts } =
|
|
19589
|
-
const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath, turnAuthoredCode } =
|
|
19590
|
-
let { analysisMode, earlyFold } =
|
|
20488
|
+
async function evidence(run2) {
|
|
20489
|
+
const { opts } = run2;
|
|
20490
|
+
const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath, turnAuthoredCode } = run2;
|
|
20491
|
+
let { analysisMode, earlyFold } = run2;
|
|
19591
20492
|
const recordFlip = (stage) => {
|
|
19592
|
-
if (
|
|
20493
|
+
if (run2.modeDecision) run2.modeDecision = { ...run2.modeDecision, resolved: "plan", flip: stage };
|
|
19593
20494
|
logEvent("mode_flipped", { stage, to: "plan" });
|
|
19594
20495
|
};
|
|
19595
20496
|
const planWorthy = !!assistantResponse && !turnAuthoredCode;
|
|
@@ -19616,7 +20517,7 @@ async function evidence(run) {
|
|
|
19616
20517
|
analysisMode = "plan";
|
|
19617
20518
|
recordFlip("debounce");
|
|
19618
20519
|
} else {
|
|
19619
|
-
await passAndExit(
|
|
20520
|
+
await passAndExit(run2, debounceSkip, "debounce");
|
|
19620
20521
|
}
|
|
19621
20522
|
}
|
|
19622
20523
|
if (analysisMode !== "plan") {
|
|
@@ -19627,7 +20528,7 @@ async function evidence(run) {
|
|
|
19627
20528
|
analysisMode = "plan";
|
|
19628
20529
|
recordFlip("mtime");
|
|
19629
20530
|
} else {
|
|
19630
|
-
await passAndExit(
|
|
20531
|
+
await passAndExit(run2, mtimeSkip, "no-delta-since-last-review");
|
|
19631
20532
|
}
|
|
19632
20533
|
}
|
|
19633
20534
|
}
|
|
@@ -19640,7 +20541,7 @@ async function evidence(run) {
|
|
|
19640
20541
|
analysisMode = "plan";
|
|
19641
20542
|
recordFlip("content-hash");
|
|
19642
20543
|
} else {
|
|
19643
|
-
await passAndExit(
|
|
20544
|
+
await passAndExit(run2, hashResult.skip, "no-delta-since-last-review");
|
|
19644
20545
|
}
|
|
19645
20546
|
}
|
|
19646
20547
|
contentHash = hashResult.hash;
|
|
@@ -19648,7 +20549,7 @@ async function evidence(run) {
|
|
|
19648
20549
|
const scoped = scopeToAuthored(allForReview, actionSummary);
|
|
19649
20550
|
const canTrustNoneAuthored = scoped.signal === "none-authored" && authorshipIsObservable;
|
|
19650
20551
|
if (canTrustNoneAuthored && !hasNonEditAuthorship(actionSummary, sessionAuthoredCode)) {
|
|
19651
|
-
await passAndExit(
|
|
20552
|
+
await passAndExit(run2, "No agent-authored code this turn \u2014 working-tree changes were not authored by this session", "zero-increment");
|
|
19652
20553
|
}
|
|
19653
20554
|
if (scoped.signal === "none-authored" && !authorshipIsObservable) {
|
|
19654
20555
|
logEvent("none_authored_unverifiable", {
|
|
@@ -19705,7 +20606,7 @@ async function evidence(run) {
|
|
|
19705
20606
|
recordFlip("empty-after-scoping");
|
|
19706
20607
|
} else {
|
|
19707
20608
|
await passAndExit(
|
|
19708
|
-
|
|
20609
|
+
run2,
|
|
19709
20610
|
"No files within size limits to analyze",
|
|
19710
20611
|
"size-limit",
|
|
19711
20612
|
codeDelta.excluded.length > 0 ? "capacity" : "policy"
|
|
@@ -19730,7 +20631,7 @@ async function evidence(run) {
|
|
|
19730
20631
|
currentCommit = getCurrentCommit();
|
|
19731
20632
|
iteration = readIteration(currentCommit);
|
|
19732
20633
|
}
|
|
19733
|
-
Object.assign(
|
|
20634
|
+
Object.assign(run2, { analysisMode, codeDelta, contentHash, currentCommit, earlyFold, iteration, snapshotResult, staticResults });
|
|
19734
20635
|
}
|
|
19735
20636
|
|
|
19736
20637
|
// src/lib/cache-cleanup.ts
|
|
@@ -19762,15 +20663,52 @@ function pruneStaleCache() {
|
|
|
19762
20663
|
|
|
19763
20664
|
// src/lib/context-files.ts
|
|
19764
20665
|
var import_node_fs30 = require("node:fs");
|
|
20666
|
+
var import_node_os5 = require("node:os");
|
|
19765
20667
|
var MAX_CONTEXT_FILES = 10;
|
|
19766
20668
|
var MAX_CONTEXT_FILE_BYTES = 10240;
|
|
19767
|
-
var MAX_CONTEXT_TOTAL_BYTES =
|
|
19768
|
-
function
|
|
20669
|
+
var MAX_CONTEXT_TOTAL_BYTES = 24576;
|
|
20670
|
+
function readSetContextPaths(summary, deltaFiles) {
|
|
20671
|
+
const reads = summary?.files_read ?? [];
|
|
20672
|
+
if (reads.length === 0) return [];
|
|
20673
|
+
const root = process.cwd().replace(/\/+$/, "");
|
|
20674
|
+
const home = (0, import_node_os5.homedir)();
|
|
20675
|
+
const toRepoRelative2 = (p) => {
|
|
20676
|
+
if (!p) return null;
|
|
20677
|
+
let abs;
|
|
20678
|
+
if (p.startsWith("/")) {
|
|
20679
|
+
abs = p;
|
|
20680
|
+
} else {
|
|
20681
|
+
const rebuilt = `${home}/${p}`;
|
|
20682
|
+
abs = rebuilt.startsWith(`${root}/`) ? rebuilt : `${root}/${p}`;
|
|
20683
|
+
}
|
|
20684
|
+
if (!abs.startsWith(`${root}/`)) return null;
|
|
20685
|
+
return abs.slice(root.length + 1);
|
|
20686
|
+
};
|
|
20687
|
+
const authored = /* @__PURE__ */ new Set();
|
|
20688
|
+
for (const p of [...summary?.files_edited ?? [], ...summary?.files_created ?? []]) {
|
|
20689
|
+
const rel = toRepoRelative2(p);
|
|
20690
|
+
if (rel) authored.add(rel);
|
|
20691
|
+
}
|
|
20692
|
+
for (const f of deltaFiles) authored.add(f.path);
|
|
20693
|
+
const out = [];
|
|
20694
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20695
|
+
for (const p of reads) {
|
|
20696
|
+
const rel = toRepoRelative2(p);
|
|
20697
|
+
if (!rel || seen.has(rel) || authored.has(rel)) continue;
|
|
20698
|
+
seen.add(rel);
|
|
20699
|
+
const ext = rel.split(".").pop()?.toLowerCase() ?? "";
|
|
20700
|
+
if (!ANALYZABLE_EXTENSIONS.has(ext) && !REVIEWABLE_EXTENSIONS.has(ext)) continue;
|
|
20701
|
+
out.push(rel);
|
|
20702
|
+
}
|
|
20703
|
+
return out;
|
|
20704
|
+
}
|
|
20705
|
+
function gatherContextFiles(contextPaths, deltaFiles, opts) {
|
|
20706
|
+
const fileCap = Math.max(0, Math.min(MAX_CONTEXT_FILES, opts?.maxFiles ?? MAX_CONTEXT_FILES));
|
|
19769
20707
|
const deltaPaths = new Set(deltaFiles.map((f) => f.path));
|
|
19770
20708
|
const result = [];
|
|
19771
20709
|
let totalBytes = 0;
|
|
19772
20710
|
for (const filePath of contextPaths) {
|
|
19773
|
-
if (result.length >=
|
|
20711
|
+
if (result.length >= fileCap) break;
|
|
19774
20712
|
if (deltaPaths.has(filePath)) continue;
|
|
19775
20713
|
if (isVerityOwnedPath(filePath)) {
|
|
19776
20714
|
logEvent("context_file_skipped", { path: filePath, reason: "verity_owned" });
|
|
@@ -19827,10 +20765,15 @@ function gatherContextFiles(contextPaths, deltaFiles) {
|
|
|
19827
20765
|
}
|
|
19828
20766
|
|
|
19829
20767
|
// src/commands/analyze/phases/07-context-files.ts
|
|
19830
|
-
async function contextFiles(
|
|
19831
|
-
const { codeDelta
|
|
19832
|
-
const
|
|
19833
|
-
const
|
|
20768
|
+
async function contextFiles(run2) {
|
|
20769
|
+
const { codeDelta } = run2;
|
|
20770
|
+
const readSet = readSetContextPaths(run2.actionSummary, codeDelta.files);
|
|
20771
|
+
const { kept: externalContext } = partitionVerityOwned(readSet);
|
|
20772
|
+
const ig = loadVerityIgnore();
|
|
20773
|
+
const unfenced = run2.verityIgnored.suspended ? externalContext : externalContext.filter((p) => !isIgnored(ig, p));
|
|
20774
|
+
const contextFiles2 = gatherContextFiles(unfenced, codeDelta.files, {
|
|
20775
|
+
maxFiles: MAX_FILES - codeDelta.files.length
|
|
20776
|
+
});
|
|
19834
20777
|
for (const f of codeDelta.files) {
|
|
19835
20778
|
f.role = "delta";
|
|
19836
20779
|
}
|
|
@@ -19842,6 +20785,30 @@ async function contextFiles(run) {
|
|
|
19842
20785
|
});
|
|
19843
20786
|
}
|
|
19844
20787
|
|
|
20788
|
+
// src/commands/analyze/phases/07b-repo-context.ts
|
|
20789
|
+
async function repoContext(run2) {
|
|
20790
|
+
const { codeDelta, snapshotResult } = run2;
|
|
20791
|
+
const deltaFiles = codeDelta.files.filter((f) => f.role !== "context");
|
|
20792
|
+
const sentPaths = new Set(codeDelta.files.map((f) => f.path));
|
|
20793
|
+
const ig = loadVerityIgnore();
|
|
20794
|
+
const isExcluded = (p) => isVerityOwnedPath(p) || !run2.verityIgnored.suspended && isIgnored(ig, p);
|
|
20795
|
+
run2.repoContext = buildRepoContext({
|
|
20796
|
+
deltaFiles,
|
|
20797
|
+
diffs: snapshotResult.diffs,
|
|
20798
|
+
sentPaths,
|
|
20799
|
+
isExcluded
|
|
20800
|
+
});
|
|
20801
|
+
logEvent("repo_context", {
|
|
20802
|
+
state: run2.repoContext.state,
|
|
20803
|
+
reason: run2.repoContext.reason ?? null,
|
|
20804
|
+
symbols: run2.repoContext.symbols?.length ?? 0,
|
|
20805
|
+
dropped: run2.repoContext.dropped_symbols?.length ?? 0,
|
|
20806
|
+
callers: run2.repoContext.callers?.length ?? 0,
|
|
20807
|
+
tests: run2.repoContext.tests?.length ?? 0,
|
|
20808
|
+
elapsed_ms: run2.repoContext.elapsed_ms ?? null
|
|
20809
|
+
});
|
|
20810
|
+
}
|
|
20811
|
+
|
|
19845
20812
|
// src/lib/seed-runner.ts
|
|
19846
20813
|
var import_promises11 = require("node:fs/promises");
|
|
19847
20814
|
var import_node_fs31 = require("node:fs");
|
|
@@ -20185,9 +21152,9 @@ async function runSeed(opts) {
|
|
|
20185
21152
|
// src/commands/analyze/phases/08-memory-manifest.ts
|
|
20186
21153
|
var import_node_fs32 = require("node:fs");
|
|
20187
21154
|
var import_node_path24 = require("node:path");
|
|
20188
|
-
async function memoryManifest(
|
|
20189
|
-
const { globals } =
|
|
20190
|
-
const { serviceUrl, token } =
|
|
21155
|
+
async function memoryManifest(run2) {
|
|
21156
|
+
const { globals } = run2;
|
|
21157
|
+
const { serviceUrl, token } = run2;
|
|
20191
21158
|
let memoryManifest2;
|
|
20192
21159
|
let deletedNodePaths = [];
|
|
20193
21160
|
let editedUploads = [];
|
|
@@ -20239,12 +21206,12 @@ async function memoryManifest(run) {
|
|
|
20239
21206
|
editedUploads = await computeEditedNodeUploads();
|
|
20240
21207
|
} catch {
|
|
20241
21208
|
}
|
|
20242
|
-
Object.assign(
|
|
21209
|
+
Object.assign(run2, { autoSeedNotice, deletedNodePaths, editedUploads, memoryManifest: memoryManifest2 });
|
|
20243
21210
|
}
|
|
20244
21211
|
|
|
20245
21212
|
// src/commands/analyze/phases/09-fold-transcript.ts
|
|
20246
|
-
async function foldTranscript(
|
|
20247
|
-
const { allForReview, earlyFold, transcriptPath } =
|
|
21213
|
+
async function foldTranscript(run2) {
|
|
21214
|
+
const { allForReview, earlyFold, transcriptPath } = run2;
|
|
20248
21215
|
let foldResult = null;
|
|
20249
21216
|
let foldConservation = null;
|
|
20250
21217
|
if (transcriptPath) {
|
|
@@ -20261,7 +21228,7 @@ async function foldTranscript(run) {
|
|
|
20261
21228
|
foldResult = null;
|
|
20262
21229
|
}
|
|
20263
21230
|
}
|
|
20264
|
-
Object.assign(
|
|
21231
|
+
Object.assign(run2, { foldConservation, foldResult });
|
|
20265
21232
|
}
|
|
20266
21233
|
|
|
20267
21234
|
// src/lib/increment.ts
|
|
@@ -20313,10 +21280,10 @@ function computeIncrement(reviewedPaths, hashOf, priorAuthored) {
|
|
|
20313
21280
|
|
|
20314
21281
|
// src/commands/analyze/phases/10-working-memory.ts
|
|
20315
21282
|
var import_node_path25 = require("node:path");
|
|
20316
|
-
async function workingMemory(
|
|
20317
|
-
const { opts } =
|
|
20318
|
-
const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } =
|
|
20319
|
-
let { reachability } =
|
|
21283
|
+
async function workingMemory(run2) {
|
|
21284
|
+
const { opts } = run2;
|
|
21285
|
+
const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } = run2;
|
|
21286
|
+
let { reachability } = run2;
|
|
20320
21287
|
const memorySession = sessionDossier(token, sessionId ?? process.env.CLAUDE_SESSION_ID ?? null);
|
|
20321
21288
|
let memory = null;
|
|
20322
21289
|
let incrementReport = null;
|
|
@@ -20402,7 +21369,7 @@ async function workingMemory(run) {
|
|
|
20402
21369
|
hasUserPrompt: (conversation?.prompts?.length ?? 0) > 0,
|
|
20403
21370
|
isTTY: process.stdout.isTTY === true
|
|
20404
21371
|
});
|
|
20405
|
-
Object.assign(
|
|
21372
|
+
Object.assign(run2, { incrementReport, memory, memorySession, reachability });
|
|
20406
21373
|
}
|
|
20407
21374
|
|
|
20408
21375
|
// src/lib/note-budget.ts
|
|
@@ -20475,7 +21442,7 @@ function isExplicitlyAutonomous(env = process.env) {
|
|
|
20475
21442
|
}
|
|
20476
21443
|
|
|
20477
21444
|
// src/lib/task-context.ts
|
|
20478
|
-
var
|
|
21445
|
+
var import_node_child_process10 = require("node:child_process");
|
|
20479
21446
|
var CLOSING_RE = /\b(close[sd]?|fix(?:e[sd])?|resolve[sd]?)\b[\s:]*#(\d+)/i;
|
|
20480
21447
|
var BRANCH_RE = /(?:^|[/_-])(?:issue|gh|fix)[-_/]?(\d+)\b/i;
|
|
20481
21448
|
function parseLinkedIssue(sources) {
|
|
@@ -20491,7 +21458,7 @@ function parseLinkedIssue(sources) {
|
|
|
20491
21458
|
}
|
|
20492
21459
|
function safeExec(cmd, timeout) {
|
|
20493
21460
|
try {
|
|
20494
|
-
return (0,
|
|
21461
|
+
return (0, import_node_child_process10.execSync)(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout }).trim();
|
|
20495
21462
|
} catch {
|
|
20496
21463
|
return "";
|
|
20497
21464
|
}
|
|
@@ -20521,8 +21488,8 @@ function resolveTaskContext(opts) {
|
|
|
20521
21488
|
// src/commands/analyze/phases/11-build-request.ts
|
|
20522
21489
|
var MAX_ASSISTANT_RESPONSE_CHARS_PLAN = 32768;
|
|
20523
21490
|
var MAX_ASSISTANT_RESPONSE_CHARS_DEFAULT = 8e3;
|
|
20524
|
-
async function buildRequest(
|
|
20525
|
-
const { actionSummary, allChanged, allForReview, analysisMode, analyzable, assistantResponse, codeDelta, conversation, deletedNodePaths, editedUploads, foldConservation, foldResult, incrementReport, iteration, memory, memoryManifest: memoryManifest2, memorySession, plans, reachability, reviewable, securityFiles, sessionId, snapshotResult, specs, staticResults, stopReason, turnId } =
|
|
21491
|
+
async function buildRequest(run2) {
|
|
21492
|
+
const { actionSummary, allChanged, allForReview, analysisMode, analyzable, assistantResponse, codeDelta, conversation, deletedNodePaths, editedUploads, foldConservation, foldResult, incrementReport, iteration, memory, memoryManifest: memoryManifest2, memorySession, plans, reachability, reviewable, securityFiles, sessionId, snapshotResult, specs, staticResults, stopReason, turnId } = run2;
|
|
20526
21493
|
const excludedByReason = {};
|
|
20527
21494
|
for (const e of codeDelta.excluded ?? []) {
|
|
20528
21495
|
excludedByReason[e.reason] = (excludedByReason[e.reason] ?? 0) + 1;
|
|
@@ -20552,15 +21519,15 @@ async function buildRequest(run) {
|
|
|
20552
21519
|
// the state, so the number is one turn lagged by construction. The
|
|
20553
21520
|
// degenerate win for the budget is a dead channel that looks like clean
|
|
20554
21521
|
// code; this is what makes "did delivery rate collapse" a query.
|
|
20555
|
-
advisory_delivered_prior: readAdvisoryEpisode(
|
|
21522
|
+
advisory_delivered_prior: readAdvisoryEpisode(run2.baselineSessionId)?.delivered ?? 0,
|
|
20556
21523
|
// `.verityignore` — see CoverageTelemetry.verityignore for why the SHARE is
|
|
20557
21524
|
// the number that matters and why no paths travel with it.
|
|
20558
21525
|
verityignore: {
|
|
20559
|
-
rules:
|
|
20560
|
-
excluded:
|
|
20561
|
-
share: ignoreShare(
|
|
20562
|
-
security_excluded:
|
|
20563
|
-
suspended:
|
|
21526
|
+
rules: run2.verityIgnored.rules,
|
|
21527
|
+
excluded: run2.verityIgnored.ignored.length,
|
|
21528
|
+
share: ignoreShare(run2.verityIgnored.kept.length, run2.verityIgnored.ignored.length),
|
|
21529
|
+
security_excluded: run2.verityIgnored.securityExcluded.length,
|
|
21530
|
+
suspended: run2.verityIgnored.suspended
|
|
20564
21531
|
}
|
|
20565
21532
|
};
|
|
20566
21533
|
const requestBody = {
|
|
@@ -20698,6 +21665,9 @@ async function buildRequest(run) {
|
|
|
20698
21665
|
if (snapshotResult.has_snapshots && snapshotResult.diffs.length > 0) {
|
|
20699
21666
|
requestBody.snapshot_diffs = snapshotResult.diffs;
|
|
20700
21667
|
}
|
|
21668
|
+
if (run2.repoContext) {
|
|
21669
|
+
requestBody.repo_context = run2.repoContext;
|
|
21670
|
+
}
|
|
20701
21671
|
const noHumanPrompt = (conversation?.prompts?.length ?? 0) === 0;
|
|
20702
21672
|
const w4Task = noHumanPrompt && isExplicitlyAutonomous() ? resolveTaskContext() : null;
|
|
20703
21673
|
const planApprovalActive = foldResult?.planApproval?.activeSinceLastPrompt === true;
|
|
@@ -20755,7 +21725,7 @@ async function buildRequest(run) {
|
|
|
20755
21725
|
}
|
|
20756
21726
|
requestBody.intent_context = intentContext;
|
|
20757
21727
|
}
|
|
20758
|
-
Object.assign(
|
|
21728
|
+
Object.assign(run2, { requestBody });
|
|
20759
21729
|
}
|
|
20760
21730
|
|
|
20761
21731
|
// src/lib/offline.ts
|
|
@@ -20816,9 +21786,9 @@ function shouldWarmRetryAnalyze(result) {
|
|
|
20816
21786
|
}
|
|
20817
21787
|
|
|
20818
21788
|
// src/commands/analyze/phases/12-transmit.ts
|
|
20819
|
-
async function transmit(
|
|
20820
|
-
const { globals } =
|
|
20821
|
-
const { codeDelta, requestBody, serviceUrl, staticResults, token } =
|
|
21789
|
+
async function transmit(run2) {
|
|
21790
|
+
const { globals } = run2;
|
|
21791
|
+
const { codeDelta, requestBody, serviceUrl, staticResults, token } = run2;
|
|
20822
21792
|
const ANALYZE_TIMEOUT_MS = 1e5;
|
|
20823
21793
|
let result = await analyzeRequest({
|
|
20824
21794
|
serviceUrl,
|
|
@@ -20881,14 +21851,14 @@ async function transmit(run) {
|
|
|
20881
21851
|
}
|
|
20882
21852
|
const response = result.data;
|
|
20883
21853
|
const decision = response.gate_decision ?? "(unrecognised)";
|
|
20884
|
-
Object.assign(
|
|
21854
|
+
Object.assign(run2, { decision, response });
|
|
20885
21855
|
}
|
|
20886
21856
|
|
|
20887
21857
|
// src/commands/analyze/phases/13-reconcile.ts
|
|
20888
21858
|
var import_node_fs35 = require("node:fs");
|
|
20889
21859
|
var import_node_path26 = require("node:path");
|
|
20890
|
-
async function reconcile(
|
|
20891
|
-
const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } =
|
|
21860
|
+
async function reconcile(run2) {
|
|
21861
|
+
const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run2;
|
|
20892
21862
|
const sentPaths = codeDelta.files.map((f) => f.path);
|
|
20893
21863
|
if (memorySession) {
|
|
20894
21864
|
try {
|
|
@@ -20989,7 +21959,7 @@ async function reconcile(run) {
|
|
|
20989
21959
|
// below is the signal that replaces the noise.
|
|
20990
21960
|
//
|
|
20991
21961
|
// Taken from the run, not recomputed — see context.ts `verityIgnored`.
|
|
20992
|
-
...
|
|
21962
|
+
...run2.verityIgnored.ignored.map((path) => ({
|
|
20993
21963
|
path,
|
|
20994
21964
|
reason: "verityignore",
|
|
20995
21965
|
stage: "verityignore",
|
|
@@ -21109,11 +22079,11 @@ async function reconcile(run) {
|
|
|
21109
22079
|
`
|
|
21110
22080
|
);
|
|
21111
22081
|
}
|
|
21112
|
-
Object.assign(
|
|
22082
|
+
Object.assign(run2, { intentRepeatCount, openElsewhere, priorPendingFingerprints, reviewCoverage, sentPaths, silenced, watermarkHash, watermarkIsPartial });
|
|
21113
22083
|
}
|
|
21114
22084
|
|
|
21115
22085
|
// src/lib/emit.ts
|
|
21116
|
-
var
|
|
22086
|
+
var YELLOW3 = "\x1B[33m";
|
|
21117
22087
|
var NC2 = "\x1B[0m";
|
|
21118
22088
|
function emitVerdict(input) {
|
|
21119
22089
|
const exit = input.exit ?? ((code) => process.exit(code));
|
|
@@ -21124,7 +22094,7 @@ function emitVerdict(input) {
|
|
|
21124
22094
|
const note = [describeCoverage(coverage), describeOpenElsewhere(openElsewhere)].filter(Boolean).join("\n\n") || null;
|
|
21125
22095
|
if (unaccounted.length > 0) {
|
|
21126
22096
|
process.stderr.write(
|
|
21127
|
-
`${
|
|
22097
|
+
`${YELLOW3}Verity: ${unaccounted.length} changed file(s) could not be attributed to any review stage \u2014 counted as unreviewed.${NC2}
|
|
21128
22098
|
`
|
|
21129
22099
|
);
|
|
21130
22100
|
}
|
|
@@ -21136,7 +22106,7 @@ ${input.agentContext}
|
|
|
21136
22106
|
`);
|
|
21137
22107
|
}
|
|
21138
22108
|
if (note && !input.silenced) process.stderr.write(`
|
|
21139
|
-
${
|
|
22109
|
+
${YELLOW3}${note}${NC2}
|
|
21140
22110
|
`);
|
|
21141
22111
|
return exit(2);
|
|
21142
22112
|
}
|
|
@@ -21222,10 +22192,10 @@ function screenRemediation(fix, findingFile) {
|
|
|
21222
22192
|
function agentContextFor(response, intentRepeat = 0, priorPendingFingerprints = []) {
|
|
21223
22193
|
return buildAgentContext(channelInputFrom(response, intentRepeat, priorPendingFingerprints));
|
|
21224
22194
|
}
|
|
21225
|
-
async function render(
|
|
21226
|
-
const { opts, globals } =
|
|
21227
|
-
const { actionSummary, assistantResponse, autoSeedNotice, voidedIgnoreNotice, baselineSessionId, codeDelta, contentHash, conversation, currentCommit, decision, intentRepeatCount, memory, openElsewhere, priorPendingFingerprints, response, reviewCoverage, serviceUrl, sessionIdForMemory, silenced, token, watermarkHash, watermarkIsPartial } =
|
|
21228
|
-
let { iteration } =
|
|
22195
|
+
async function render(run2) {
|
|
22196
|
+
const { opts, globals } = run2;
|
|
22197
|
+
const { actionSummary, assistantResponse, autoSeedNotice, voidedIgnoreNotice, baselineSessionId, codeDelta, contentHash, conversation, currentCommit, decision, intentRepeatCount, memory, openElsewhere, priorPendingFingerprints, response, reviewCoverage, serviceUrl, sessionIdForMemory, silenced, token, watermarkHash, watermarkIsPartial } = run2;
|
|
22198
|
+
let { iteration } = run2;
|
|
21229
22199
|
const metadata = response.metadata ?? {};
|
|
21230
22200
|
const intentAmbiguity = metadata.intent_ambiguity;
|
|
21231
22201
|
if (intentAmbiguity != null && intentAmbiguity > 5) {
|
|
@@ -21332,7 +22302,7 @@ async function render(run) {
|
|
|
21332
22302
|
const blocks = prior.blocks + 1;
|
|
21333
22303
|
const decisionNow = mayBlock({
|
|
21334
22304
|
reviewedFileCount: codeDelta.files.length,
|
|
21335
|
-
staticFindingCount:
|
|
22305
|
+
staticFindingCount: run2.staticResults?.findings?.length ?? 0,
|
|
21336
22306
|
cycleCutFired: silenced !== null,
|
|
21337
22307
|
attempts,
|
|
21338
22308
|
blocks,
|
|
@@ -21363,7 +22333,7 @@ async function render(run) {
|
|
|
21363
22333
|
});
|
|
21364
22334
|
emitVerdict({
|
|
21365
22335
|
proposed: "WARN",
|
|
21366
|
-
changed:
|
|
22336
|
+
changed: run2.changedUniverse,
|
|
21367
22337
|
coverage: reviewCoverage,
|
|
21368
22338
|
userSummary: lines.length > 0 ? `${summary}
|
|
21369
22339
|
${lines.join("\n")}` : summary,
|
|
@@ -21455,7 +22425,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
21455
22425
|
`);
|
|
21456
22426
|
emitVerdict({
|
|
21457
22427
|
proposed: "FAIL",
|
|
21458
|
-
changed:
|
|
22428
|
+
changed: run2.changedUniverse,
|
|
21459
22429
|
coverage: reviewCoverage,
|
|
21460
22430
|
userSummary: "",
|
|
21461
22431
|
// Subject to the SAME cycle cut as PASS/WARN. Suppressing here is safe:
|
|
@@ -21480,7 +22450,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
21480
22450
|
userSummary += loginNudge + grantNudge;
|
|
21481
22451
|
emitVerdict({
|
|
21482
22452
|
proposed: "PASS",
|
|
21483
|
-
changed:
|
|
22453
|
+
changed: run2.changedUniverse,
|
|
21484
22454
|
coverage: reviewCoverage,
|
|
21485
22455
|
userSummary,
|
|
21486
22456
|
agentContext: silenced ? null : agentContextFor(response, intentRepeatCount, priorPendingFingerprints),
|
|
@@ -21502,7 +22472,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
21502
22472
|
userSummary += loginNudge + grantNudge;
|
|
21503
22473
|
emitVerdict({
|
|
21504
22474
|
proposed: "WARN",
|
|
21505
|
-
changed:
|
|
22475
|
+
changed: run2.changedUniverse,
|
|
21506
22476
|
coverage: reviewCoverage,
|
|
21507
22477
|
userSummary,
|
|
21508
22478
|
agentContext: silenced ? null : agentContextFor(response, intentRepeatCount, priorPendingFingerprints),
|
|
@@ -21527,7 +22497,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
21527
22497
|
process.exit(0);
|
|
21528
22498
|
}
|
|
21529
22499
|
}
|
|
21530
|
-
Object.assign(
|
|
22500
|
+
Object.assign(run2, { iteration });
|
|
21531
22501
|
}
|
|
21532
22502
|
|
|
21533
22503
|
// src/commands/analyze/index.ts
|
|
@@ -21546,6 +22516,8 @@ var PIPELINE = [
|
|
|
21546
22516
|
// ← THE NARROWING. what gets sent, and why not the rest
|
|
21547
22517
|
["contextFiles", contextFiles],
|
|
21548
22518
|
// supporting files, merged INTO the delta array
|
|
22519
|
+
["repoContext", repoContext],
|
|
22520
|
+
// R1/R3 — call sites of changed symbols, one line each
|
|
21549
22521
|
["memoryManifest", memoryManifest],
|
|
21550
22522
|
// knowledge-graph manifest + one-time auto-seed
|
|
21551
22523
|
["foldTranscript", foldTranscript],
|
|
@@ -21562,7 +22534,7 @@ var PIPELINE = [
|
|
|
21562
22534
|
// say it — stderr, stdout, disk
|
|
21563
22535
|
];
|
|
21564
22536
|
function registerAnalyzeCommand(program2) {
|
|
21565
|
-
program2.command("analyze").description("Run Verity analysis on changed files (stop hook)").option("--debounce <seconds>", "Skip if last analysis was within N seconds",
|
|
22537
|
+
program2.command("analyze").description("Run Verity analysis on changed files (stop hook)").option("--debounce <seconds>", "Skip if last analysis was within N seconds", String(DEBOUNCE_SECONDS)).option("--max-iterations <n>", "Force PASS after N FAIL cycles", String(MAX_ITERATIONS)).option("--max-files <n>", "Max files to send for review", String(MAX_FILES)).option("--max-file-size <bytes>", "Skip files larger than N bytes", String(MAX_FILE_BYTES)).option("--max-total-size <bytes>", "Stop collecting files at N total bytes", String(MAX_DELTA_BYTES)).option("--skip-static", "Skip codacy-analysis").option("--mode <mode>", "Force analysis mode (standard|plan|debug|skip)").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
21566
22538
|
const globals = program2.opts();
|
|
21567
22539
|
try {
|
|
21568
22540
|
await runAnalyze(opts, globals);
|
|
@@ -21577,18 +22549,18 @@ function registerAnalyzeCommand(program2) {
|
|
|
21577
22549
|
}
|
|
21578
22550
|
var tracing = () => process.env.VERITY_TRACE_PHASES === "1";
|
|
21579
22551
|
async function runAnalyze(opts, globals) {
|
|
21580
|
-
const
|
|
21581
|
-
installRunEvidence(
|
|
22552
|
+
const run2 = createRun(opts, globals);
|
|
22553
|
+
installRunEvidence(run2);
|
|
21582
22554
|
for (const [name, phase] of PIPELINE) {
|
|
21583
|
-
|
|
22555
|
+
run2.phaseReached = name;
|
|
21584
22556
|
if (!tracing()) {
|
|
21585
|
-
await phase(
|
|
21586
|
-
|
|
22557
|
+
await phase(run2);
|
|
22558
|
+
run2.phasesCompleted.push(name);
|
|
21587
22559
|
continue;
|
|
21588
22560
|
}
|
|
21589
22561
|
const started = Date.now();
|
|
21590
|
-
await phase(
|
|
21591
|
-
|
|
22562
|
+
await phase(run2);
|
|
22563
|
+
run2.phasesCompleted.push(name);
|
|
21592
22564
|
process.stderr.write(`verity\xB7phase ${name} ${Date.now() - started}ms
|
|
21593
22565
|
`);
|
|
21594
22566
|
}
|
|
@@ -21691,8 +22663,8 @@ async function runReview(opts, globals) {
|
|
|
21691
22663
|
for (const p of specPaths) {
|
|
21692
22664
|
if (!(0, import_node_fs37.existsSync)(p)) continue;
|
|
21693
22665
|
try {
|
|
21694
|
-
const { readFileSync:
|
|
21695
|
-
const content =
|
|
22666
|
+
const { readFileSync: readFileSync25 } = await import("node:fs");
|
|
22667
|
+
const content = readFileSync25(p, "utf-8");
|
|
21696
22668
|
specs.push({ path: p, content: content.slice(0, 10240) });
|
|
21697
22669
|
} catch {
|
|
21698
22670
|
}
|
|
@@ -21961,6 +22933,7 @@ function coverageBlock(c) {
|
|
|
21961
22933
|
const tree = c.root ? `${c.root}${c.linked ? " (linked worktree)" : ""}${c.branch ? ` \xB7 branch ${c.branch}` : ""}` : "(no tree resolved)";
|
|
21962
22934
|
lines.push(`Reviewed (${c.moment}): ${c.sent.length} file(s)${c.range ? ` @ ${c.range}` : ""}`);
|
|
21963
22935
|
lines.push(` Tree: ${tree}`);
|
|
22936
|
+
if (c.repoContext) lines.push(` Repo context: ${describeRepoContext(c.repoContext)}`);
|
|
21964
22937
|
for (const f of c.sent) lines.push(` - ${f}`);
|
|
21965
22938
|
if (c.excluded.length > 0) {
|
|
21966
22939
|
lines.push(` Excluded (${c.excluded.length}):`);
|
|
@@ -22027,6 +23000,39 @@ async function runGuard(opts, globals) {
|
|
|
22027
23000
|
statedIntent,
|
|
22028
23001
|
buildGuardCoverage(files, codeDelta, frame, range)
|
|
22029
23002
|
);
|
|
23003
|
+
try {
|
|
23004
|
+
const ig = loadVerityIgnore();
|
|
23005
|
+
const repoContext2 = buildRepoContext({
|
|
23006
|
+
deltaFiles: codeDelta.files,
|
|
23007
|
+
diffs: [],
|
|
23008
|
+
signalsByPath: rangeChangeSignals(frame, range, codeDelta.files.map((f) => f.path)),
|
|
23009
|
+
sentPaths: new Set(codeDelta.files.map((f) => f.path)),
|
|
23010
|
+
isExcluded: (p) => isVerityOwnedPath(p) || isIgnored(ig, p),
|
|
23011
|
+
cwd: frame.worktreeRoot ?? process.cwd()
|
|
23012
|
+
});
|
|
23013
|
+
upgradeToExcerpts(repoContext2, {
|
|
23014
|
+
readFile: (rel) => {
|
|
23015
|
+
try {
|
|
23016
|
+
return (0, import_node_fs38.readFileSync)((0, import_node_path27.join)(frame.worktreeRoot ?? process.cwd(), rel), "utf8");
|
|
23017
|
+
} catch {
|
|
23018
|
+
return null;
|
|
23019
|
+
}
|
|
23020
|
+
}
|
|
23021
|
+
});
|
|
23022
|
+
requestBody.repo_context = repoContext2;
|
|
23023
|
+
logEvent("repo_context", {
|
|
23024
|
+
moment,
|
|
23025
|
+
state: repoContext2.state,
|
|
23026
|
+
reason: repoContext2.reason ?? null,
|
|
23027
|
+
symbols: repoContext2.symbols?.length ?? 0,
|
|
23028
|
+
callers: repoContext2.callers?.length ?? 0,
|
|
23029
|
+
tests: repoContext2.tests?.length ?? 0,
|
|
23030
|
+
excerpts: repoContext2.excerpts?.length ?? 0,
|
|
23031
|
+
elapsed_ms: repoContext2.elapsed_ms ?? null
|
|
23032
|
+
});
|
|
23033
|
+
} catch (e) {
|
|
23034
|
+
logEvent("repo_context", { moment, state: "absent", reason: "exception", message: e.message });
|
|
23035
|
+
}
|
|
22030
23036
|
const coverage = {
|
|
22031
23037
|
moment,
|
|
22032
23038
|
root: frame.worktreeRoot,
|
|
@@ -22034,7 +23040,8 @@ async function runGuard(opts, globals) {
|
|
|
22034
23040
|
linked: frame.isLinkedWorktree,
|
|
22035
23041
|
range: describeRange(range),
|
|
22036
23042
|
sent: codeDelta.files.map((f) => f.path),
|
|
22037
|
-
excluded: codeDelta.excluded.map((e) => ({ path: e.path, reason: e.reason }))
|
|
23043
|
+
excluded: codeDelta.excluded.map((e) => ({ path: e.path, reason: e.reason })),
|
|
23044
|
+
...requestBody.repo_context ? { repoContext: requestBody.repo_context } : {}
|
|
22038
23045
|
};
|
|
22039
23046
|
logToFileOnly(coverageBlock(coverage));
|
|
22040
23047
|
const reviewStart = Date.now();
|
|
@@ -22281,22 +23288,297 @@ function registerWaiveCommand(program2) {
|
|
|
22281
23288
|
}
|
|
22282
23289
|
|
|
22283
23290
|
// src/commands/init.ts
|
|
22284
|
-
var
|
|
22285
|
-
var
|
|
22286
|
-
var
|
|
23291
|
+
var import_node_fs45 = require("node:fs");
|
|
23292
|
+
var import_promises15 = require("node:fs/promises");
|
|
23293
|
+
var import_node_path30 = require("node:path");
|
|
23294
|
+
var import_node_child_process14 = require("node:child_process");
|
|
23295
|
+
|
|
23296
|
+
// src/lib/banner.ts
|
|
23297
|
+
var WORDMARK = [
|
|
23298
|
+
"\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557",
|
|
23299
|
+
"\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D",
|
|
23300
|
+
"\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2554\u255D ",
|
|
23301
|
+
"\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2554\u255D ",
|
|
23302
|
+
" \u255A\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 ",
|
|
23303
|
+
" \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D "
|
|
23304
|
+
];
|
|
23305
|
+
var WORDMARK_NARROW = [
|
|
23306
|
+
"\u2588 \u2588 \u2588\u2580\u2580\u2580\u2580 \u2588\u2580\u2580\u2580\u2584 \u2580\u2580\u2588\u2580\u2580 \u2580\u2580\u2588\u2580\u2580 \u2588 \u2588",
|
|
23307
|
+
"\u2588 \u2588 \u2588\u2580\u2580\u2580 \u2588\u2584\u2584\u2584\u2580 \u2588 \u2588 \u2580\u2584\u2580 ",
|
|
23308
|
+
" \u2580\u2584\u2580 \u2588\u2584\u2584\u2584\u2584 \u2588 \u2580\u2584 \u2584\u2584\u2588\u2584\u2584 \u2588 \u2588 "
|
|
23309
|
+
];
|
|
23310
|
+
var TAGLINE = "reviews every turn, remembers every lesson";
|
|
23311
|
+
var WORDMARK_COLOR = ["\x1B[0;32m"];
|
|
23312
|
+
var DIM3 = "\x1B[2m";
|
|
23313
|
+
var RESET2 = "\x1B[0m";
|
|
23314
|
+
var INDENT = " ";
|
|
23315
|
+
function artWidth(art) {
|
|
23316
|
+
return Math.max(...art.map((r) => r.length)) + INDENT.length;
|
|
23317
|
+
}
|
|
23318
|
+
function canRenderArt() {
|
|
23319
|
+
return !!process.stderr.isTTY;
|
|
23320
|
+
}
|
|
23321
|
+
function printBanner(opts) {
|
|
23322
|
+
if (!opts.interactive) return;
|
|
23323
|
+
const columns = opts.columns ?? process.stderr.columns ?? 80;
|
|
23324
|
+
const color = colorEnabled();
|
|
23325
|
+
const version = `v${cliVersion()}`;
|
|
23326
|
+
const art = [WORDMARK, WORDMARK_NARROW].find((a) => artWidth(a) <= columns) ?? null;
|
|
23327
|
+
const dim = (text) => color ? `${DIM3}${text}${RESET2}` : text;
|
|
23328
|
+
process.stderr.write("\n");
|
|
23329
|
+
if (!art) {
|
|
23330
|
+
const candidates = [`Verity ${version}`, "Verity"];
|
|
23331
|
+
const text = candidates.find((c) => c.length + INDENT.length <= columns);
|
|
23332
|
+
if (text) process.stderr.write(`${INDENT}${dim(text)}
|
|
23333
|
+
`);
|
|
23334
|
+
process.stderr.write("\n");
|
|
23335
|
+
return;
|
|
23336
|
+
}
|
|
23337
|
+
art.forEach((row2, i) => {
|
|
23338
|
+
const tint = color && WORDMARK_COLOR.length > 0 ? WORDMARK_COLOR[Math.min(i, WORDMARK_COLOR.length - 1)] : "";
|
|
23339
|
+
const reset = color && tint ? RESET2 : "";
|
|
23340
|
+
process.stderr.write(`${INDENT}${tint}${row2}${reset}
|
|
23341
|
+
`);
|
|
23342
|
+
});
|
|
23343
|
+
if (TAGLINE.length + INDENT.length <= columns) {
|
|
23344
|
+
process.stderr.write(`${INDENT}${dim(TAGLINE)}
|
|
23345
|
+
`);
|
|
23346
|
+
}
|
|
23347
|
+
if (version.length + INDENT.length <= columns) {
|
|
23348
|
+
process.stderr.write(`${INDENT}${dim(version)}
|
|
23349
|
+
`);
|
|
23350
|
+
}
|
|
23351
|
+
process.stderr.write("\n");
|
|
23352
|
+
}
|
|
23353
|
+
function printPhase(n, of, title, subtitle) {
|
|
23354
|
+
const color = colorEnabled();
|
|
23355
|
+
const columns = process.stderr.columns ?? 72;
|
|
23356
|
+
const width = Math.min(columns, 72);
|
|
23357
|
+
const label2 = `\u2500\u2500 Phase ${n} of ${of} \xB7 ${title} `;
|
|
23358
|
+
const rule = label2 + "\u2500".repeat(Math.max(0, width - label2.length - 2));
|
|
23359
|
+
process.stderr.write("\n");
|
|
23360
|
+
process.stderr.write(color ? ` ${DIM3}${rule}${RESET2}
|
|
23361
|
+
` : ` ${rule}
|
|
23362
|
+
`);
|
|
23363
|
+
if (subtitle && subtitle.length + INDENT.length <= columns) {
|
|
23364
|
+
process.stderr.write(color ? ` ${DIM3}${subtitle}${RESET2}
|
|
23365
|
+
` : ` ${subtitle}
|
|
23366
|
+
`);
|
|
23367
|
+
}
|
|
23368
|
+
process.stderr.write("\n");
|
|
23369
|
+
}
|
|
23370
|
+
|
|
23371
|
+
// src/commands/doctor.ts
|
|
23372
|
+
var import_node_fs42 = require("node:fs");
|
|
23373
|
+
|
|
23374
|
+
// src/lib/prereqs.ts
|
|
22287
23375
|
var import_node_child_process11 = require("node:child_process");
|
|
22288
|
-
var
|
|
23376
|
+
var MIN_NODE_MAJOR = 20;
|
|
23377
|
+
function which(bin) {
|
|
23378
|
+
try {
|
|
23379
|
+
const out = (0, import_node_child_process11.execSync)(`command -v ${bin}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
23380
|
+
return out || null;
|
|
23381
|
+
} catch {
|
|
23382
|
+
return null;
|
|
23383
|
+
}
|
|
23384
|
+
}
|
|
23385
|
+
function checkNode() {
|
|
23386
|
+
const version = process.version;
|
|
23387
|
+
const major = Number.parseInt(version.slice(1), 10);
|
|
23388
|
+
const ok = Number.isFinite(major) && major >= MIN_NODE_MAJOR;
|
|
23389
|
+
return {
|
|
23390
|
+
id: "node",
|
|
23391
|
+
label: "Node.js",
|
|
23392
|
+
status: ok ? "ok" : "missing",
|
|
23393
|
+
detail: version,
|
|
23394
|
+
remedy: ok ? void 0 : `Node.js ${MIN_NODE_MAJOR}+ required \u2014 update from https://nodejs.org`,
|
|
23395
|
+
required: true
|
|
23396
|
+
};
|
|
23397
|
+
}
|
|
23398
|
+
function checkGit() {
|
|
23399
|
+
let detail = "";
|
|
23400
|
+
try {
|
|
23401
|
+
detail = (0, import_node_child_process11.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
23402
|
+
} catch {
|
|
23403
|
+
return {
|
|
23404
|
+
id: "git",
|
|
23405
|
+
label: "git",
|
|
23406
|
+
status: "missing",
|
|
23407
|
+
detail: "not found",
|
|
23408
|
+
remedy: "Install git from https://git-scm.com",
|
|
23409
|
+
required: true
|
|
23410
|
+
};
|
|
23411
|
+
}
|
|
23412
|
+
return { id: "git", label: "git", status: "ok", detail, required: true };
|
|
23413
|
+
}
|
|
23414
|
+
function checkClaude() {
|
|
23415
|
+
const path = which("claude");
|
|
23416
|
+
return {
|
|
23417
|
+
id: "claude",
|
|
23418
|
+
label: "Claude Code",
|
|
23419
|
+
status: path ? "ok" : "warn",
|
|
23420
|
+
detail: path ?? "not found",
|
|
23421
|
+
remedy: path ? void 0 : "Hooks are wired but need Claude Code to fire \u2014 https://claude.com/claude-code",
|
|
23422
|
+
required: false
|
|
23423
|
+
};
|
|
23424
|
+
}
|
|
23425
|
+
function checkAnalysisCli() {
|
|
23426
|
+
const path = which("codacy-analysis");
|
|
23427
|
+
return {
|
|
23428
|
+
id: "analysis-cli",
|
|
23429
|
+
label: "@codacy/analysis-cli",
|
|
23430
|
+
status: path ? "ok" : "warn",
|
|
23431
|
+
detail: path ?? "not found",
|
|
23432
|
+
remedy: path ? void 0 : "npm install -g @codacy/analysis-cli (static findings are unavailable until then)",
|
|
23433
|
+
required: false
|
|
23434
|
+
};
|
|
23435
|
+
}
|
|
23436
|
+
var INSTALL_TIMEOUT_MS = 12e4;
|
|
23437
|
+
function run(command, args, opts = {}) {
|
|
23438
|
+
return new Promise((resolve4) => {
|
|
23439
|
+
const child = (0, import_node_child_process11.spawn)(command, args, {
|
|
23440
|
+
stdio: opts.inherit ? "inherit" : "pipe",
|
|
23441
|
+
timeout: INSTALL_TIMEOUT_MS
|
|
23442
|
+
});
|
|
23443
|
+
child.on("error", () => resolve4(false));
|
|
23444
|
+
child.on("close", (code) => resolve4(code === 0));
|
|
23445
|
+
});
|
|
23446
|
+
}
|
|
23447
|
+
async function installAnalysisCli() {
|
|
23448
|
+
const spinner = startSpinner("Installing @codacy/analysis-cli");
|
|
23449
|
+
const ok = await run("npm", ["install", "-g", "@codacy/analysis-cli"]);
|
|
23450
|
+
if (ok) {
|
|
23451
|
+
const check = checkAnalysisCli();
|
|
23452
|
+
if (check.status === "ok") {
|
|
23453
|
+
spinner.succeed("@codacy/analysis-cli installed");
|
|
23454
|
+
return { ...check, justInstalled: true };
|
|
23455
|
+
}
|
|
23456
|
+
spinner.warn("npm reported success but codacy-analysis is not on PATH");
|
|
23457
|
+
return check;
|
|
23458
|
+
}
|
|
23459
|
+
spinner.stop();
|
|
23460
|
+
{
|
|
23461
|
+
const failed = {
|
|
23462
|
+
id: "analysis-cli",
|
|
23463
|
+
label: "@codacy/analysis-cli",
|
|
23464
|
+
status: "warn",
|
|
23465
|
+
detail: "install failed",
|
|
23466
|
+
remedy: "Install manually: npm install -g @codacy/analysis-cli",
|
|
23467
|
+
required: false
|
|
23468
|
+
};
|
|
23469
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return failed;
|
|
23470
|
+
process.stderr.write(" Retrying with sudo (you may be asked for your password)\u2026\n");
|
|
23471
|
+
const sudoOk = await run("sudo", ["npm", "install", "-g", "@codacy/analysis-cli"], { inherit: true });
|
|
23472
|
+
if (!sudoOk) return failed;
|
|
23473
|
+
const check = checkAnalysisCli();
|
|
23474
|
+
return check.status === "ok" ? { ...check, justInstalled: true } : check;
|
|
23475
|
+
}
|
|
23476
|
+
}
|
|
23477
|
+
async function checkPrereqs(opts = {}) {
|
|
23478
|
+
const checks = [checkNode(), checkGit(), checkClaude()];
|
|
23479
|
+
let analysis = checkAnalysisCli();
|
|
23480
|
+
if (analysis.status !== "ok" && opts.install) {
|
|
23481
|
+
analysis = await installAnalysisCli();
|
|
23482
|
+
}
|
|
23483
|
+
checks.push(analysis);
|
|
23484
|
+
return { checks, blocked: checks.some((c) => c.required && c.status !== "ok") };
|
|
23485
|
+
}
|
|
22289
23486
|
|
|
22290
|
-
// src/
|
|
23487
|
+
// src/lib/telemetry.ts
|
|
23488
|
+
var import_promises12 = require("node:fs/promises");
|
|
23489
|
+
|
|
23490
|
+
// src/lib/gitignore.ts
|
|
23491
|
+
var import_node_child_process12 = require("node:child_process");
|
|
22291
23492
|
var import_node_fs40 = require("node:fs");
|
|
22292
|
-
var
|
|
22293
|
-
var
|
|
23493
|
+
var VERITY_GITIGNORE_MARKER = "# Verity \u2014 machine-local state.";
|
|
23494
|
+
var SETTINGS_LOCAL_IGNORE_ENTRY = ".claude/settings.local.json";
|
|
23495
|
+
var VERITY_GITIGNORE_BLOCK = [
|
|
23496
|
+
"# Verity \u2014 machine-local state. Everything in .verity/ is ignored EXCEPT the",
|
|
23497
|
+
"# shared standard and the knowledge graph, which are meant to be committed.",
|
|
23498
|
+
".verity/*",
|
|
23499
|
+
"!.verity/standard.yaml",
|
|
23500
|
+
"!.verity/memory/",
|
|
23501
|
+
".verity/memory/log.md",
|
|
23502
|
+
SETTINGS_LOCAL_IGNORE_ENTRY,
|
|
23503
|
+
""
|
|
23504
|
+
].join("\n");
|
|
23505
|
+
var BREAKING_ENTRIES = /* @__PURE__ */ new Set([".verity/", ".verity"]);
|
|
23506
|
+
function isIgnored2(path) {
|
|
23507
|
+
try {
|
|
23508
|
+
(0, import_node_child_process12.execSync)(`git check-ignore -q -- "${path}"`, { stdio: "pipe" });
|
|
23509
|
+
return true;
|
|
23510
|
+
} catch (err) {
|
|
23511
|
+
return err.status === 1 ? false : null;
|
|
23512
|
+
}
|
|
23513
|
+
}
|
|
23514
|
+
function semanticsHold() {
|
|
23515
|
+
const snapshot = isIgnored2(".verity/.snapshot/__probe__");
|
|
23516
|
+
const standard = isIgnored2(".verity/standard.yaml");
|
|
23517
|
+
const node = isIgnored2(".verity/memory/domain/__probe__.md");
|
|
23518
|
+
const futureState = isIgnored2(".verity/.__probe-future-state__");
|
|
23519
|
+
if (snapshot === null || standard === null || node === null || futureState === null) return null;
|
|
23520
|
+
return snapshot === true && futureState === true && standard === false && node === false;
|
|
23521
|
+
}
|
|
23522
|
+
function ensureVerityGitignore() {
|
|
23523
|
+
let content = "";
|
|
23524
|
+
try {
|
|
23525
|
+
content = (0, import_node_fs40.readFileSync)(".gitignore", "utf-8");
|
|
23526
|
+
} catch {
|
|
23527
|
+
}
|
|
23528
|
+
const hasMarker = content.includes(VERITY_GITIGNORE_MARKER);
|
|
23529
|
+
const lines = content.split("\n");
|
|
23530
|
+
const breakingCount = lines.filter((l) => BREAKING_ENTRIES.has(l.trim())).length;
|
|
23531
|
+
const needsRepair = breakingCount > 0;
|
|
23532
|
+
const verified = (result) => semanticsHold() === false ? "conflict" : result;
|
|
23533
|
+
if (hasMarker && !needsRepair) return verified("covered");
|
|
23534
|
+
if (!hasMarker && !needsRepair) {
|
|
23535
|
+
if (semanticsHold() === true) return "covered";
|
|
23536
|
+
}
|
|
23537
|
+
try {
|
|
23538
|
+
let next = content;
|
|
23539
|
+
if (needsRepair) {
|
|
23540
|
+
next = lines.map((l) => BREAKING_ENTRIES.has(l.trim()) ? ".verity/*" : l).join("\n");
|
|
23541
|
+
}
|
|
23542
|
+
if (!hasMarker) {
|
|
23543
|
+
const sep2 = next === "" ? "" : next.endsWith("\n") ? "\n" : "\n\n";
|
|
23544
|
+
next = next + sep2 + VERITY_GITIGNORE_BLOCK;
|
|
23545
|
+
}
|
|
23546
|
+
(0, import_node_fs40.writeFileSync)(".gitignore", next);
|
|
23547
|
+
return verified(needsRepair ? "repaired" : "added");
|
|
23548
|
+
} catch {
|
|
23549
|
+
return "failed";
|
|
23550
|
+
}
|
|
23551
|
+
}
|
|
23552
|
+
function committedVerityState() {
|
|
23553
|
+
let out = "";
|
|
23554
|
+
try {
|
|
23555
|
+
out = (0, import_node_child_process12.execSync)("git ls-files -z -- .verity", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
23556
|
+
} catch {
|
|
23557
|
+
return [];
|
|
23558
|
+
}
|
|
23559
|
+
return out.split("\0").filter(Boolean).filter((p) => p !== ".verity/standard.yaml" && !(p.startsWith(".verity/memory/") && p !== ".verity/memory/log.md"));
|
|
23560
|
+
}
|
|
23561
|
+
function untrackVerityState() {
|
|
23562
|
+
const tracked = committedVerityState();
|
|
23563
|
+
if (tracked.length === 0) return "none";
|
|
23564
|
+
try {
|
|
23565
|
+
(0, import_node_child_process12.execSync)("git rm -r --cached --quiet -- .verity", { stdio: "pipe" });
|
|
23566
|
+
for (const keep of [".verity/standard.yaml", ".verity/memory"]) {
|
|
23567
|
+
try {
|
|
23568
|
+
(0, import_node_child_process12.execSync)(`git add -- "${keep}"`, { stdio: "pipe" });
|
|
23569
|
+
} catch {
|
|
23570
|
+
}
|
|
23571
|
+
}
|
|
23572
|
+
return "untracked";
|
|
23573
|
+
} catch {
|
|
23574
|
+
return "failed";
|
|
23575
|
+
}
|
|
23576
|
+
}
|
|
22294
23577
|
|
|
22295
23578
|
// src/lib/telemetry.ts
|
|
22296
|
-
var import_promises12 = require("node:fs/promises");
|
|
22297
23579
|
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
22298
23580
|
var GITIGNORE_FILE = ".gitignore";
|
|
22299
|
-
var GITIGNORE_ENTRY =
|
|
23581
|
+
var GITIGNORE_ENTRY = SETTINGS_LOCAL_IGNORE_ENTRY;
|
|
22300
23582
|
var OTEL_HEADERS_HELPER_CMD = "verity telemetry headers";
|
|
22301
23583
|
var LEGACY_TELEMETRY_ENV_KEYS = ["OTEL_EXPORTER_OTLP_HEADERS"];
|
|
22302
23584
|
function deriveOtlpEndpoint(serviceUrl) {
|
|
@@ -22382,14 +23664,130 @@ async function uninstallTelemetry() {
|
|
|
22382
23664
|
return { ok: true, data: { removed } };
|
|
22383
23665
|
}
|
|
22384
23666
|
|
|
23667
|
+
// src/lib/setup-state.ts
|
|
23668
|
+
var import_promises13 = require("node:fs/promises");
|
|
23669
|
+
var import_node_fs41 = require("node:fs");
|
|
23670
|
+
var SETUP_STATE_FILE = `${VERITY_DIR}/setup.json`;
|
|
23671
|
+
async function readSetupState() {
|
|
23672
|
+
const path = projectPath(SETUP_STATE_FILE);
|
|
23673
|
+
if (!(0, import_node_fs41.existsSync)(path)) return null;
|
|
23674
|
+
try {
|
|
23675
|
+
const parsed = JSON.parse(await (0, import_promises13.readFile)(path, "utf-8"));
|
|
23676
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
23677
|
+
} catch {
|
|
23678
|
+
return null;
|
|
23679
|
+
}
|
|
23680
|
+
}
|
|
23681
|
+
async function writeSetupState(patch) {
|
|
23682
|
+
const current = await readSetupState() ?? { version: 1 };
|
|
23683
|
+
const next = { ...current, ...patch, version: 1 };
|
|
23684
|
+
await writeJsonFilePreservingStyle(projectPath(SETUP_STATE_FILE), next);
|
|
23685
|
+
return next;
|
|
23686
|
+
}
|
|
23687
|
+
|
|
23688
|
+
// src/commands/doctor.ts
|
|
23689
|
+
async function buildReport() {
|
|
23690
|
+
const prereqs = await checkPrereqs({ install: false });
|
|
23691
|
+
const state = await readSetupState();
|
|
23692
|
+
const hooks = await checkAllVerityHooks();
|
|
23693
|
+
const telemetry = await checkTelemetry();
|
|
23694
|
+
const artifacts = {
|
|
23695
|
+
standard: (0, import_node_fs42.existsSync)(projectPath(STANDARD_FILE)),
|
|
23696
|
+
analysisConfig: (0, import_node_fs42.existsSync)(projectPath(CODACY_CONFIG_FILE)),
|
|
23697
|
+
verityMd: (0, import_node_fs42.existsSync)(projectPath(VERITY_MD_FILE))
|
|
23698
|
+
};
|
|
23699
|
+
const next = [];
|
|
23700
|
+
for (const c of prereqs.checks) {
|
|
23701
|
+
if (c.status !== "ok" && c.remedy) next.push(c.remedy);
|
|
23702
|
+
}
|
|
23703
|
+
if (!state?.init) next.push('Run "verity init" \u2014 the deterministic setup phase has not completed here.');
|
|
23704
|
+
const missing = [
|
|
23705
|
+
!artifacts.standard && ".verity/standard.yaml",
|
|
23706
|
+
!artifacts.analysisConfig && ".codacy/codacy.config.json",
|
|
23707
|
+
!artifacts.verityMd && "VERITY.md"
|
|
23708
|
+
].filter((x) => !!x);
|
|
23709
|
+
if (missing.length === 3) {
|
|
23710
|
+
next.push("Run /verity-setup in Claude Code \u2014 it synthesizes the Standard, the analysis config, and VERITY.md.");
|
|
23711
|
+
} else if (missing.length === 1 && !artifacts.analysisConfig) {
|
|
23712
|
+
next.push(
|
|
23713
|
+
'No analysis config, so static analysis runs nothing. Run /verity-setup in Claude Code to write one (it keeps the Standard you already have), or have a teammate run "verity config push".'
|
|
23714
|
+
);
|
|
23715
|
+
} else if (missing.length > 0) {
|
|
23716
|
+
next.push(`Missing ${missing.join(" and ")}. Run "/verity-setup --force" in Claude Code to regenerate.`);
|
|
23717
|
+
}
|
|
23718
|
+
const noAnalysisMoment = !hooks.stop && hooks.guardOn.length === 0;
|
|
23719
|
+
if (noAnalysisMoment) {
|
|
23720
|
+
next.push('No analysis moment is active \u2014 run "verity hooks install --moments stop" or re-run "verity init".');
|
|
23721
|
+
}
|
|
23722
|
+
if (state?.telemetry === "deferred") {
|
|
23723
|
+
next.push('Telemetry was requested but needs a token \u2014 run "verity login", then "verity telemetry install".');
|
|
23724
|
+
}
|
|
23725
|
+
return {
|
|
23726
|
+
prerequisites: prereqs.checks,
|
|
23727
|
+
blocked: prereqs.blocked,
|
|
23728
|
+
phases: {
|
|
23729
|
+
init: { done: !!state?.init, ...state?.init ?? {} },
|
|
23730
|
+
setup: { done: artifacts.standard && artifacts.analysisConfig && artifacts.verityMd }
|
|
23731
|
+
},
|
|
23732
|
+
answers: {
|
|
23733
|
+
intensity: state?.intensity ?? null,
|
|
23734
|
+
moments: state?.moments ?? null,
|
|
23735
|
+
telemetry: state?.telemetry ?? null
|
|
23736
|
+
},
|
|
23737
|
+
hooks: { ...hooks, noAnalysisMoment },
|
|
23738
|
+
telemetry: { enabled: telemetry.enabled, endpoint: telemetry.endpoint },
|
|
23739
|
+
artifacts,
|
|
23740
|
+
next
|
|
23741
|
+
};
|
|
23742
|
+
}
|
|
23743
|
+
function registerDoctorCommand(program2) {
|
|
23744
|
+
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) => {
|
|
23745
|
+
const report = await buildReport();
|
|
23746
|
+
if (opts.json) {
|
|
23747
|
+
printJson(report);
|
|
23748
|
+
if (report.blocked) process.exit(1);
|
|
23749
|
+
return;
|
|
23750
|
+
}
|
|
23751
|
+
printInfo("Prerequisites:");
|
|
23752
|
+
for (const c of report.prerequisites) {
|
|
23753
|
+
if (c.status === "ok") printInfo(` ${c.label} ${c.detail} \u2713`);
|
|
23754
|
+
else printWarn(` ${c.label}: ${c.detail}${c.remedy ? ` \u2014 ${c.remedy}` : ""}`);
|
|
23755
|
+
}
|
|
23756
|
+
printInfo("Setup:");
|
|
23757
|
+
printInfo(` verity init: ${report.phases.init.done ? `done ${report.phases.init.completed_at ?? ""}`.trim() : "NOT run here"}`);
|
|
23758
|
+
printInfo(` /verity-setup: ${report.phases.setup.done ? "done" : "not completed"}`);
|
|
23759
|
+
printInfo(` intensity: ${report.answers.intensity ?? "\u2014"} moments: ${report.answers.moments?.join(", ") ?? "\u2014"}`);
|
|
23760
|
+
printInfo("Hooks:");
|
|
23761
|
+
printInfo(` Stop (verity analyze): ${report.hooks.stop ? "on" : "off"}`);
|
|
23762
|
+
printInfo(` Git-moment gate: ${report.hooks.guardOn.length ? report.hooks.guardOn.join(", ") : "off"}`);
|
|
23763
|
+
printInfo(` Infra (intent/baseline/compact/session-end): ${[report.hooks.intent, report.hooks.baseline, report.hooks.compact, report.hooks.sessionEnd].filter(Boolean).length}/4`);
|
|
23764
|
+
printInfo(`Telemetry: ${report.telemetry.enabled ? `enabled \u2192 ${report.telemetry.endpoint}` : "disabled"}`);
|
|
23765
|
+
printInfo("Artifacts:");
|
|
23766
|
+
printInfo(` .verity/standard.yaml: ${report.artifacts.standard ? "\u2713" : "missing"}`);
|
|
23767
|
+
printInfo(` .codacy/codacy.config.json: ${report.artifacts.analysisConfig ? "\u2713" : "missing"}`);
|
|
23768
|
+
printInfo(` VERITY.md: ${report.artifacts.verityMd ? "\u2713" : "missing"}`);
|
|
23769
|
+
if (report.next.length > 0) {
|
|
23770
|
+
console.log("");
|
|
23771
|
+
printWarn("Next:");
|
|
23772
|
+
for (const n of report.next) printWarn(` - ${n}`);
|
|
23773
|
+
} else {
|
|
23774
|
+
printInfo("Setup is complete.");
|
|
23775
|
+
}
|
|
23776
|
+
if (report.blocked) process.exit(1);
|
|
23777
|
+
});
|
|
23778
|
+
}
|
|
23779
|
+
|
|
22385
23780
|
// src/commands/migrate.ts
|
|
23781
|
+
var import_node_fs43 = require("node:fs");
|
|
23782
|
+
var import_node_path28 = require("node:path");
|
|
23783
|
+
var import_node_child_process13 = require("node:child_process");
|
|
22386
23784
|
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
22387
23785
|
function defaultNpmRemover(pkg) {
|
|
22388
|
-
(0,
|
|
23786
|
+
(0, import_node_child_process13.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
|
|
22389
23787
|
}
|
|
22390
23788
|
function isGitTracked(cwd, relPath) {
|
|
22391
23789
|
try {
|
|
22392
|
-
(0,
|
|
23790
|
+
(0, import_node_child_process13.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
|
|
22393
23791
|
return true;
|
|
22394
23792
|
} catch {
|
|
22395
23793
|
return false;
|
|
@@ -22397,7 +23795,7 @@ function isGitTracked(cwd, relPath) {
|
|
|
22397
23795
|
}
|
|
22398
23796
|
function isGitRepo(cwd) {
|
|
22399
23797
|
try {
|
|
22400
|
-
(0,
|
|
23798
|
+
(0, import_node_child_process13.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
|
|
22401
23799
|
return true;
|
|
22402
23800
|
} catch {
|
|
22403
23801
|
return false;
|
|
@@ -22420,10 +23818,10 @@ async function runMigration(opts = {}) {
|
|
|
22420
23818
|
function migrateProjectDir(root, actions) {
|
|
22421
23819
|
const gateDir = (0, import_node_path28.join)(root, ".gate");
|
|
22422
23820
|
const verityDir = (0, import_node_path28.join)(root, ".verity");
|
|
22423
|
-
if ((0,
|
|
23821
|
+
if ((0, import_node_fs43.existsSync)(gateDir) && !(0, import_node_fs43.existsSync)(verityDir)) {
|
|
22424
23822
|
return migrateProjectDirRename(root, gateDir, verityDir, actions);
|
|
22425
23823
|
}
|
|
22426
|
-
if ((0,
|
|
23824
|
+
if ((0, import_node_fs43.existsSync)(gateDir) && (0, import_node_fs43.existsSync)(verityDir)) {
|
|
22427
23825
|
return migrateProjectDirCarry(gateDir, verityDir, actions);
|
|
22428
23826
|
}
|
|
22429
23827
|
return false;
|
|
@@ -22437,20 +23835,20 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
|
|
|
22437
23835
|
);
|
|
22438
23836
|
}
|
|
22439
23837
|
try {
|
|
22440
|
-
(0,
|
|
23838
|
+
(0, import_node_child_process13.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
|
|
22441
23839
|
actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
|
|
22442
23840
|
moved = true;
|
|
22443
23841
|
} catch {
|
|
22444
23842
|
}
|
|
22445
23843
|
}
|
|
22446
23844
|
if (moved) {
|
|
22447
|
-
if ((0,
|
|
23845
|
+
if ((0, import_node_fs43.existsSync)(gateDir)) {
|
|
22448
23846
|
const carried = carryLegacyContents(gateDir, verityDir);
|
|
22449
23847
|
if (carried > 0) {
|
|
22450
23848
|
actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
|
|
22451
23849
|
}
|
|
22452
23850
|
try {
|
|
22453
|
-
(0,
|
|
23851
|
+
(0, import_node_fs43.rmSync)(gateDir, { recursive: true, force: true });
|
|
22454
23852
|
} catch {
|
|
22455
23853
|
}
|
|
22456
23854
|
}
|
|
@@ -22466,7 +23864,7 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
|
|
|
22466
23864
|
actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
|
|
22467
23865
|
}
|
|
22468
23866
|
try {
|
|
22469
|
-
(0,
|
|
23867
|
+
(0, import_node_fs43.rmSync)(gateDir, { recursive: true, force: true });
|
|
22470
23868
|
} catch {
|
|
22471
23869
|
}
|
|
22472
23870
|
return carried > 0;
|
|
@@ -22475,9 +23873,9 @@ function migrateGlobalCredentials(home, actions) {
|
|
|
22475
23873
|
if (!home) return;
|
|
22476
23874
|
const gateCreds = (0, import_node_path28.join)(home, ".gate", "credentials");
|
|
22477
23875
|
const verityCreds = (0, import_node_path28.join)(home, ".verity", "credentials");
|
|
22478
|
-
if (!(0,
|
|
22479
|
-
if (!(0,
|
|
22480
|
-
(0,
|
|
23876
|
+
if (!(0, import_node_fs43.existsSync)(gateCreds)) return;
|
|
23877
|
+
if (!(0, import_node_fs43.existsSync)(verityCreds)) {
|
|
23878
|
+
(0, import_node_fs43.mkdirSync)((0, import_node_path28.join)(home, ".verity"), { recursive: true });
|
|
22481
23879
|
moveFile(gateCreds, verityCreds);
|
|
22482
23880
|
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
22483
23881
|
return;
|
|
@@ -22500,7 +23898,7 @@ async function migrateLegacyHooks(root, actions) {
|
|
|
22500
23898
|
}
|
|
22501
23899
|
async function migrateClaudeMd(root, actions) {
|
|
22502
23900
|
const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
|
|
22503
|
-
const hadLegacyBlock = (0,
|
|
23901
|
+
const hadLegacyBlock = (0, import_node_fs43.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
|
|
22504
23902
|
if (!hadLegacyBlock) return;
|
|
22505
23903
|
try {
|
|
22506
23904
|
await ensureClaudeMdPointer(root);
|
|
@@ -22512,11 +23910,11 @@ async function migrateClaudeMd(root, actions) {
|
|
|
22512
23910
|
function migrateStandardFile(root, actions) {
|
|
22513
23911
|
const gateMd = (0, import_node_path28.join)(root, "GATE.md");
|
|
22514
23912
|
const verityMd = (0, import_node_path28.join)(root, "VERITY.md");
|
|
22515
|
-
if (!(0,
|
|
23913
|
+
if (!(0, import_node_fs43.existsSync)(gateMd) || (0, import_node_fs43.existsSync)(verityMd)) return;
|
|
22516
23914
|
let moved = false;
|
|
22517
23915
|
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
22518
23916
|
try {
|
|
22519
|
-
(0,
|
|
23917
|
+
(0, import_node_child_process13.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
|
|
22520
23918
|
moved = true;
|
|
22521
23919
|
} catch {
|
|
22522
23920
|
}
|
|
@@ -22524,12 +23922,12 @@ function migrateStandardFile(root, actions) {
|
|
|
22524
23922
|
if (!moved) moveFile(gateMd, verityMd);
|
|
22525
23923
|
const content = readFileSyncSafe(verityMd);
|
|
22526
23924
|
const refreshed = content.split("GATE.md").join("VERITY.md");
|
|
22527
|
-
if (refreshed !== content) (0,
|
|
23925
|
+
if (refreshed !== content) (0, import_node_fs43.writeFileSync)(verityMd, refreshed);
|
|
22528
23926
|
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
22529
23927
|
}
|
|
22530
23928
|
async function migrateTelemetryHeaders(root, actions) {
|
|
22531
23929
|
const file = (0, import_node_path28.join)(root, ".claude", "settings.local.json");
|
|
22532
|
-
if (!(0,
|
|
23930
|
+
if (!(0, import_node_fs43.existsSync)(file)) return;
|
|
22533
23931
|
let settings;
|
|
22534
23932
|
try {
|
|
22535
23933
|
settings = JSON.parse(readFileSyncSafe(file) || "{}");
|
|
@@ -22577,21 +23975,21 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
|
|
|
22577
23975
|
}
|
|
22578
23976
|
if (toAppend.length > 0) {
|
|
22579
23977
|
const sep2 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
|
|
22580
|
-
(0,
|
|
23978
|
+
(0, import_node_fs43.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
|
|
22581
23979
|
}
|
|
22582
|
-
(0,
|
|
23980
|
+
(0, import_node_fs43.rmSync)(gateCreds, { force: true });
|
|
22583
23981
|
return toAppend.length;
|
|
22584
23982
|
}
|
|
22585
23983
|
function readFileSyncSafe(path) {
|
|
22586
23984
|
try {
|
|
22587
|
-
return (0,
|
|
23985
|
+
return (0, import_node_fs43.readFileSync)(path, "utf-8");
|
|
22588
23986
|
} catch {
|
|
22589
23987
|
return "";
|
|
22590
23988
|
}
|
|
22591
23989
|
}
|
|
22592
23990
|
function hasStagedChanges(root) {
|
|
22593
23991
|
try {
|
|
22594
|
-
(0,
|
|
23992
|
+
(0, import_node_child_process13.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
|
|
22595
23993
|
return false;
|
|
22596
23994
|
} catch {
|
|
22597
23995
|
return true;
|
|
@@ -22599,35 +23997,35 @@ function hasStagedChanges(root) {
|
|
|
22599
23997
|
}
|
|
22600
23998
|
function moveDir(from, to) {
|
|
22601
23999
|
try {
|
|
22602
|
-
(0,
|
|
24000
|
+
(0, import_node_fs43.renameSync)(from, to);
|
|
22603
24001
|
} catch (err) {
|
|
22604
24002
|
if (err.code !== "EXDEV") throw err;
|
|
22605
|
-
(0,
|
|
22606
|
-
(0,
|
|
24003
|
+
(0, import_node_fs43.cpSync)(from, to, { recursive: true });
|
|
24004
|
+
(0, import_node_fs43.rmSync)(from, { recursive: true, force: true });
|
|
22607
24005
|
}
|
|
22608
24006
|
}
|
|
22609
24007
|
function moveFile(from, to) {
|
|
22610
24008
|
try {
|
|
22611
|
-
(0,
|
|
24009
|
+
(0, import_node_fs43.renameSync)(from, to);
|
|
22612
24010
|
} catch (err) {
|
|
22613
24011
|
if (err.code !== "EXDEV") throw err;
|
|
22614
|
-
(0,
|
|
22615
|
-
(0,
|
|
24012
|
+
(0, import_node_fs43.cpSync)(from, to);
|
|
24013
|
+
(0, import_node_fs43.rmSync)(from, { force: true });
|
|
22616
24014
|
}
|
|
22617
24015
|
}
|
|
22618
24016
|
function carryLegacyContents(gateDir, verityDir) {
|
|
22619
24017
|
let copied = 0;
|
|
22620
24018
|
const walk = (relDir) => {
|
|
22621
24019
|
const srcDir = (0, import_node_path28.join)(gateDir, relDir);
|
|
22622
|
-
for (const entry of (0,
|
|
24020
|
+
for (const entry of (0, import_node_fs43.readdirSync)(srcDir)) {
|
|
22623
24021
|
const rel = relDir ? (0, import_node_path28.join)(relDir, entry) : entry;
|
|
22624
24022
|
const src = (0, import_node_path28.join)(gateDir, rel);
|
|
22625
24023
|
const dest = (0, import_node_path28.join)(verityDir, rel);
|
|
22626
|
-
if ((0,
|
|
24024
|
+
if ((0, import_node_fs43.statSync)(src).isDirectory()) {
|
|
22627
24025
|
walk(rel);
|
|
22628
|
-
} else if (!(0,
|
|
22629
|
-
(0,
|
|
22630
|
-
(0,
|
|
24026
|
+
} else if (!(0, import_node_fs43.existsSync)(dest)) {
|
|
24027
|
+
(0, import_node_fs43.mkdirSync)((0, import_node_path28.dirname)(dest), { recursive: true });
|
|
24028
|
+
(0, import_node_fs43.cpSync)(src, dest);
|
|
22631
24029
|
copied++;
|
|
22632
24030
|
}
|
|
22633
24031
|
}
|
|
@@ -22638,20 +24036,20 @@ function carryLegacyContents(gateDir, verityDir) {
|
|
|
22638
24036
|
async function needsMigration(root = repoRoot()) {
|
|
22639
24037
|
const gateDir = (0, import_node_path28.join)(root, ".gate");
|
|
22640
24038
|
const verityDir = (0, import_node_path28.join)(root, ".verity");
|
|
22641
|
-
if ((0,
|
|
22642
|
-
if ((0,
|
|
22643
|
-
if ((0,
|
|
24039
|
+
if ((0, import_node_fs43.existsSync)(gateDir) && !(0, import_node_fs43.existsSync)(verityDir)) return true;
|
|
24040
|
+
if ((0, import_node_fs43.existsSync)(gateDir) && (0, import_node_fs43.existsSync)(verityDir)) {
|
|
24041
|
+
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"))) {
|
|
22644
24042
|
return true;
|
|
22645
24043
|
}
|
|
22646
|
-
if ((0,
|
|
24044
|
+
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"))) {
|
|
22647
24045
|
return true;
|
|
22648
24046
|
}
|
|
22649
24047
|
}
|
|
22650
24048
|
const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
|
|
22651
|
-
if ((0,
|
|
24049
|
+
if ((0, import_node_fs43.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
22652
24050
|
return true;
|
|
22653
24051
|
}
|
|
22654
|
-
if ((0,
|
|
24052
|
+
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"))) {
|
|
22655
24053
|
return true;
|
|
22656
24054
|
}
|
|
22657
24055
|
if (await hasLegacyHooksAt(root)) return true;
|
|
@@ -22676,17 +24074,236 @@ function registerMigrateCommand(program2) {
|
|
|
22676
24074
|
});
|
|
22677
24075
|
}
|
|
22678
24076
|
|
|
22679
|
-
// src/
|
|
22680
|
-
|
|
22681
|
-
|
|
22682
|
-
|
|
24077
|
+
// src/lib/prompt.ts
|
|
24078
|
+
var readline2 = __toESM(require("node:readline/promises"));
|
|
24079
|
+
function interactive() {
|
|
24080
|
+
return !!process.stdin.isTTY && !!process.stdout.isTTY;
|
|
24081
|
+
}
|
|
24082
|
+
async function askLine(question, io = {}) {
|
|
24083
|
+
const rl = readline2.createInterface({
|
|
24084
|
+
input: io.input ?? process.stdin,
|
|
24085
|
+
output: io.output ?? process.stdout
|
|
24086
|
+
});
|
|
22683
24087
|
try {
|
|
22684
|
-
|
|
22685
|
-
|
|
24088
|
+
return await new Promise((resolve4) => {
|
|
24089
|
+
rl.question(question).then((a) => resolve4(a.trim())).catch(() => resolve4(null));
|
|
24090
|
+
rl.once("close", () => setImmediate(() => resolve4(null)));
|
|
24091
|
+
});
|
|
22686
24092
|
} finally {
|
|
22687
24093
|
rl.close();
|
|
22688
24094
|
}
|
|
22689
24095
|
}
|
|
24096
|
+
var ask = (question) => askLine(question);
|
|
24097
|
+
function parseChoice(answer, choices, fallback) {
|
|
24098
|
+
const a = answer.trim().toLowerCase();
|
|
24099
|
+
if (a === "") return { id: fallback, recognized: true };
|
|
24100
|
+
const byNumber = Number.parseInt(a, 10);
|
|
24101
|
+
if (Number.isInteger(byNumber) && byNumber >= 1 && byNumber <= choices.length) {
|
|
24102
|
+
return { id: choices[byNumber - 1].id, recognized: true };
|
|
24103
|
+
}
|
|
24104
|
+
const byId = choices.find((c) => c.id.toLowerCase() === a || c.label.toLowerCase() === a);
|
|
24105
|
+
if (byId) return { id: byId.id, recognized: true };
|
|
24106
|
+
return { id: fallback, recognized: false };
|
|
24107
|
+
}
|
|
24108
|
+
function parseMultiSelect(answer, choices, fallback) {
|
|
24109
|
+
const a = answer.trim().toLowerCase();
|
|
24110
|
+
if (a === "") return { ids: [...fallback], recognized: true };
|
|
24111
|
+
const picked = /* @__PURE__ */ new Set();
|
|
24112
|
+
for (const part of a.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
24113
|
+
const n = Number.parseInt(part, 10);
|
|
24114
|
+
if (Number.isInteger(n) && n >= 1 && n <= choices.length) {
|
|
24115
|
+
picked.add(choices[n - 1].id);
|
|
24116
|
+
continue;
|
|
24117
|
+
}
|
|
24118
|
+
const byId = choices.find((c) => c.id.toLowerCase() === part || c.label.toLowerCase() === part);
|
|
24119
|
+
if (byId) picked.add(byId.id);
|
|
24120
|
+
}
|
|
24121
|
+
if (picked.size === 0) return { ids: [...fallback], recognized: false };
|
|
24122
|
+
return { ids: choices.filter((c) => picked.has(c.id)).map((c) => c.id), recognized: true };
|
|
24123
|
+
}
|
|
24124
|
+
async function promptYes(question, opts) {
|
|
24125
|
+
if (!interactive()) return opts.nonInteractive;
|
|
24126
|
+
const answer = await ask(question);
|
|
24127
|
+
if (answer === null) return opts.nonInteractive;
|
|
24128
|
+
const a = answer.toLowerCase();
|
|
24129
|
+
return a === "" || a === "y" || a === "yes";
|
|
24130
|
+
}
|
|
24131
|
+
function printOptions(question, choices) {
|
|
24132
|
+
console.log("");
|
|
24133
|
+
console.log(` ${question}`);
|
|
24134
|
+
choices.forEach((c, i) => {
|
|
24135
|
+
const tag = c.recommended ? " (recommended)" : "";
|
|
24136
|
+
console.log(` ${i + 1}. ${c.label}${tag}${c.hint ? ` \u2014 ${c.hint}` : ""}`);
|
|
24137
|
+
});
|
|
24138
|
+
}
|
|
24139
|
+
async function promptChoice(question, choices, fallback) {
|
|
24140
|
+
if (!interactive()) return fallback;
|
|
24141
|
+
printOptions(question, choices);
|
|
24142
|
+
const defaultIdx = choices.findIndex((c) => c.id === fallback);
|
|
24143
|
+
const answer = await ask(` Choose [${defaultIdx + 1}]: `);
|
|
24144
|
+
if (answer === null) {
|
|
24145
|
+
console.log(` (no answer \u2014 using ${fallback})`);
|
|
24146
|
+
return fallback;
|
|
24147
|
+
}
|
|
24148
|
+
const parsed = parseChoice(answer, choices, fallback);
|
|
24149
|
+
if (!parsed.recognized) console.log(` Unrecognized answer "${answer}" \u2014 using ${fallback}.`);
|
|
24150
|
+
return parsed.id;
|
|
24151
|
+
}
|
|
24152
|
+
async function promptMultiSelect(question, choices, fallback) {
|
|
24153
|
+
if (!interactive()) return [...fallback];
|
|
24154
|
+
printOptions(question, choices);
|
|
24155
|
+
const defaultLabel = choices.map((c, i) => fallback.includes(c.id) ? String(i + 1) : null).filter(Boolean).join(",");
|
|
24156
|
+
const answer = await ask(` Choose one or more, comma-separated [${defaultLabel}]: `);
|
|
24157
|
+
if (answer === null) {
|
|
24158
|
+
console.log(" (no answer \u2014 using the default)");
|
|
24159
|
+
return [...fallback];
|
|
24160
|
+
}
|
|
24161
|
+
const parsed = parseMultiSelect(answer, choices, fallback);
|
|
24162
|
+
if (!parsed.recognized) console.log(` Unrecognized answer "${answer}" \u2014 using the default.`);
|
|
24163
|
+
return parsed.ids;
|
|
24164
|
+
}
|
|
24165
|
+
|
|
24166
|
+
// src/lib/remote-config.ts
|
|
24167
|
+
var import_node_fs44 = require("node:fs");
|
|
24168
|
+
var import_promises14 = require("node:fs/promises");
|
|
24169
|
+
var import_node_path29 = require("node:path");
|
|
24170
|
+
var import_yaml3 = __toESM(require_dist());
|
|
24171
|
+
var IGNORE_RIDER = "verityignore";
|
|
24172
|
+
async function fetchRemoteSetup(opts) {
|
|
24173
|
+
const standard = await apiRequest({
|
|
24174
|
+
method: "GET",
|
|
24175
|
+
path: "/standards/latest",
|
|
24176
|
+
serviceUrl: opts.serviceUrl,
|
|
24177
|
+
token: opts.token,
|
|
24178
|
+
verbose: opts.verbose
|
|
24179
|
+
});
|
|
24180
|
+
if (!standard.ok) {
|
|
24181
|
+
const missing = /STANDARD_NOT_FOUND|not found|404/i.test(standard.error);
|
|
24182
|
+
return missing ? { status: "none" } : { status: "unavailable", reason: standard.error };
|
|
24183
|
+
}
|
|
24184
|
+
const config = await apiRequest({
|
|
24185
|
+
method: "GET",
|
|
24186
|
+
path: "/analysis-configs",
|
|
24187
|
+
serviceUrl: opts.serviceUrl,
|
|
24188
|
+
token: opts.token,
|
|
24189
|
+
verbose: opts.verbose
|
|
24190
|
+
});
|
|
24191
|
+
return {
|
|
24192
|
+
status: "found",
|
|
24193
|
+
standard: {
|
|
24194
|
+
version: standard.data.version,
|
|
24195
|
+
content: standard.data.content ?? {},
|
|
24196
|
+
createdBy: standard.data.created_by,
|
|
24197
|
+
createdAt: standard.data.created_at
|
|
24198
|
+
},
|
|
24199
|
+
// Absent is normal and not an error: the two are stored independently, and a
|
|
24200
|
+
// project can have a Standard with no analysis config.
|
|
24201
|
+
analysisConfig: config.ok ? config.data.content ?? null : null
|
|
24202
|
+
};
|
|
24203
|
+
}
|
|
24204
|
+
async function adoptRemoteSetup(found, opts) {
|
|
24205
|
+
const written = [];
|
|
24206
|
+
const notes = [];
|
|
24207
|
+
const content = { ...found.standard.content };
|
|
24208
|
+
const rider = typeof content[IGNORE_RIDER] === "string" ? content[IGNORE_RIDER] : null;
|
|
24209
|
+
delete content[IGNORE_RIDER];
|
|
24210
|
+
await writeOut(STANDARD_FILE, (0, import_yaml3.stringify)(content));
|
|
24211
|
+
written.push(STANDARD_FILE);
|
|
24212
|
+
if (rider !== null) {
|
|
24213
|
+
const localIgnore = projectPath(VERITYIGNORE_FILE);
|
|
24214
|
+
if (!(0, import_node_fs44.existsSync)(localIgnore)) {
|
|
24215
|
+
await writeOut(VERITYIGNORE_FILE, rider);
|
|
24216
|
+
written.push(VERITYIGNORE_FILE);
|
|
24217
|
+
} else {
|
|
24218
|
+
const local = await (0, import_promises14.readFile)(localIgnore, "utf-8").catch(() => null);
|
|
24219
|
+
if (local !== null && local !== rider) {
|
|
24220
|
+
notes.push(`${VERITYIGNORE_FILE} already exists here and differs from the pushed copy \u2014 kept yours.`);
|
|
24221
|
+
}
|
|
24222
|
+
}
|
|
24223
|
+
}
|
|
24224
|
+
if (found.analysisConfig) {
|
|
24225
|
+
await writeOut(CODACY_CONFIG_FILE, JSON.stringify(found.analysisConfig, null, 2) + "\n");
|
|
24226
|
+
written.push(CODACY_CONFIG_FILE);
|
|
24227
|
+
} else {
|
|
24228
|
+
notes.push(
|
|
24229
|
+
"No analysis config is stored for this repository, so static analysis has nothing to run. Run /verity-setup in Claude Code to generate one."
|
|
24230
|
+
);
|
|
24231
|
+
}
|
|
24232
|
+
await writeOut(VERITY_MD_FILE, renderVerityMd(found.standard, opts));
|
|
24233
|
+
written.push(VERITY_MD_FILE);
|
|
24234
|
+
return { written, notes };
|
|
24235
|
+
}
|
|
24236
|
+
async function writeOut(relative, body) {
|
|
24237
|
+
const target = projectPath(relative);
|
|
24238
|
+
await (0, import_promises14.mkdir)((0, import_node_path29.dirname)(target), { recursive: true });
|
|
24239
|
+
await (0, import_promises14.writeFile)(target, body);
|
|
24240
|
+
}
|
|
24241
|
+
function names(content, key) {
|
|
24242
|
+
const raw = content[key];
|
|
24243
|
+
if (!Array.isArray(raw)) return [];
|
|
24244
|
+
return raw.map((entry) => {
|
|
24245
|
+
if (typeof entry === "string") return entry;
|
|
24246
|
+
if (entry && typeof entry === "object") {
|
|
24247
|
+
const rec = entry;
|
|
24248
|
+
const label2 = rec.name ?? rec.id ?? rec.dimension ?? rec.pattern;
|
|
24249
|
+
return typeof label2 === "string" ? label2 : null;
|
|
24250
|
+
}
|
|
24251
|
+
return null;
|
|
24252
|
+
}).filter((x) => !!x);
|
|
24253
|
+
}
|
|
24254
|
+
function renderVerityMd(standard, opts) {
|
|
24255
|
+
const dimensions = names(standard.content, "quality_dimensions");
|
|
24256
|
+
const patterns = names(standard.content, "security_patterns");
|
|
24257
|
+
const custom = names(standard.content, "custom_patterns");
|
|
24258
|
+
const list2 = (items, fallback) => (items.length > 0 ? items : fallback).map((i) => `- ${i}`).join("\n");
|
|
24259
|
+
return `# VERITY.md \u2014 Quality Gate
|
|
24260
|
+
|
|
24261
|
+
> This project uses [Verity](https://verity.md) to enforce quality and security standards on AI-generated code.
|
|
24262
|
+
|
|
24263
|
+
**URL:** ${opts.serviceUrl}
|
|
24264
|
+
**Project:** ${opts.projectId ?? "this repository"}
|
|
24265
|
+
**Standard:** v${standard.version} (adopted from the service${standard.createdBy ? `, created by ${standard.createdBy}` : ""})
|
|
24266
|
+
|
|
24267
|
+
## Quality Dimensions
|
|
24268
|
+
${list2(dimensions, ["comprehensibility", "modularity", "type_safety", "test_adequacy"])}
|
|
24269
|
+
|
|
24270
|
+
## Security Patterns
|
|
24271
|
+
${list2(patterns, [
|
|
24272
|
+
"No hardcoded secrets (CWE-798)",
|
|
24273
|
+
"Input sanitization (CWE-20)",
|
|
24274
|
+
"Parameterized queries (CWE-89)",
|
|
24275
|
+
"Dependency verification (CWE-1395)",
|
|
24276
|
+
"No unsafe deserialization (CWE-502)",
|
|
24277
|
+
"Access control checks (CWE-639)",
|
|
24278
|
+
"Config file integrity (CWE-15)"
|
|
24279
|
+
])}
|
|
24280
|
+
${custom.length > 0 ? `
|
|
24281
|
+
## Project Patterns
|
|
24282
|
+
${list2(custom, [])}
|
|
24283
|
+
` : ""}
|
|
24284
|
+
## How It Works
|
|
24285
|
+
Every time the coding agent stops, the Verity hook:
|
|
24286
|
+
1. Runs static analysis via @codacy/analysis-cli
|
|
24287
|
+
2. Sends results + code to the Verity service
|
|
24288
|
+
3. An independent model reviews the code
|
|
24289
|
+
4. Returns PASS / WARN / FAIL with actionable findings
|
|
24290
|
+
|
|
24291
|
+
_This Standard came from the service \u2014 the team's shared version. To change it,
|
|
24292
|
+
edit \`.verity/standard.yaml\` and run \`verity standard push\`, or re-synthesize
|
|
24293
|
+
from the codebase with \`/verity-setup --force\`._
|
|
24294
|
+
`;
|
|
24295
|
+
}
|
|
24296
|
+
function describeRemote(found) {
|
|
24297
|
+
const when = found.standard.createdAt.slice(0, 10);
|
|
24298
|
+
const who = found.standard.createdBy ? ` by ${found.standard.createdBy}` : "";
|
|
24299
|
+
const extras = [];
|
|
24300
|
+
if (found.analysisConfig) extras.push("analysis config");
|
|
24301
|
+
if (typeof found.standard.content[IGNORE_RIDER] === "string") extras.push(".verityignore");
|
|
24302
|
+
const also = extras.length > 0 ? ` \xB7 with ${extras.join(" and ")}` : "";
|
|
24303
|
+
return `version ${found.standard.version}, pushed${who} on ${when}${also}`;
|
|
24304
|
+
}
|
|
24305
|
+
|
|
24306
|
+
// src/commands/init.ts
|
|
22690
24307
|
async function confirmExistingLogin(serviceUrl, remote, opts) {
|
|
22691
24308
|
const existing = await resolveToken(opts.token);
|
|
22692
24309
|
if (!existing.ok) return "drive-login";
|
|
@@ -22744,7 +24361,7 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
22744
24361
|
}
|
|
22745
24362
|
let remote = "";
|
|
22746
24363
|
try {
|
|
22747
|
-
remote = (0,
|
|
24364
|
+
remote = (0, import_node_child_process14.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
22748
24365
|
} catch {
|
|
22749
24366
|
}
|
|
22750
24367
|
if (!healed) {
|
|
@@ -22755,7 +24372,7 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
22755
24372
|
printInfo("Verity runs in local-only mode: the gate still runs and shows static findings, but nothing uploads.");
|
|
22756
24373
|
printInfo(' Authenticate anytime: run "verity login" (one login covers every repo you can write to).');
|
|
22757
24374
|
};
|
|
22758
|
-
if (
|
|
24375
|
+
if (interactive() && !opts.yes) {
|
|
22759
24376
|
console.log("");
|
|
22760
24377
|
console.log(" Signing in is optional. What it does:");
|
|
22761
24378
|
console.log(" - Confirms which repositories you can write to. The GitHub token is");
|
|
@@ -22770,9 +24387,12 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
22770
24387
|
console.log(" findings, but nothing is uploaded.");
|
|
22771
24388
|
console.log("");
|
|
22772
24389
|
}
|
|
22773
|
-
const wantsAuth =
|
|
24390
|
+
const wantsAuth = opts.yes ? false : await promptYes(
|
|
24391
|
+
"Authenticate with GitHub now to upload results to Verity? [Y/skip] ",
|
|
24392
|
+
{ nonInteractive: false }
|
|
24393
|
+
);
|
|
22774
24394
|
if (!wantsAuth) {
|
|
22775
|
-
printInfo("Skipped authentication.");
|
|
24395
|
+
printInfo(opts.yes ? "Skipped authentication (unattended run)." : "Skipped authentication.");
|
|
22776
24396
|
localOnlyNote();
|
|
22777
24397
|
return;
|
|
22778
24398
|
}
|
|
@@ -22787,15 +24407,15 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
22787
24407
|
}
|
|
22788
24408
|
function resolveDataDir() {
|
|
22789
24409
|
const candidates = [
|
|
22790
|
-
(0,
|
|
24410
|
+
(0, import_node_path30.join)(__dirname, "..", "data"),
|
|
22791
24411
|
// installed: node_modules/@codacy/verity-cli/data
|
|
22792
|
-
(0,
|
|
24412
|
+
(0, import_node_path30.join)(__dirname, "..", "..", "data"),
|
|
22793
24413
|
// edge case: nested resolution
|
|
22794
|
-
(0,
|
|
24414
|
+
(0, import_node_path30.join)(process.cwd(), "cli", "data")
|
|
22795
24415
|
// local dev: running from repo root
|
|
22796
24416
|
];
|
|
22797
24417
|
for (const candidate of candidates) {
|
|
22798
|
-
if ((0,
|
|
24418
|
+
if ((0, import_node_fs45.existsSync)((0, import_node_path30.join)(candidate, "skills"))) {
|
|
22799
24419
|
return candidate;
|
|
22800
24420
|
}
|
|
22801
24421
|
}
|
|
@@ -22804,22 +24424,273 @@ function resolveDataDir() {
|
|
|
22804
24424
|
);
|
|
22805
24425
|
}
|
|
22806
24426
|
async function copyDir(src, dest) {
|
|
22807
|
-
await (0,
|
|
22808
|
-
await (0,
|
|
24427
|
+
await (0, import_promises15.mkdir)(dest, { recursive: true });
|
|
24428
|
+
await (0, import_promises15.cp)(src, dest, { recursive: true, force: true });
|
|
24429
|
+
}
|
|
24430
|
+
async function skillIsCurrent(src, dest) {
|
|
24431
|
+
const list2 = (dir) => {
|
|
24432
|
+
const out = [];
|
|
24433
|
+
const walk = (d, prefix) => {
|
|
24434
|
+
for (const e of (0, import_node_fs45.readdirSync)(d, { withFileTypes: true })) {
|
|
24435
|
+
const rel = prefix ? `${prefix}/${e.name}` : e.name;
|
|
24436
|
+
if (e.isDirectory()) walk((0, import_node_path30.join)(d, e.name), rel);
|
|
24437
|
+
else if (e.isFile()) out.push(rel);
|
|
24438
|
+
}
|
|
24439
|
+
};
|
|
24440
|
+
walk(dir, "");
|
|
24441
|
+
return out.sort();
|
|
24442
|
+
};
|
|
24443
|
+
try {
|
|
24444
|
+
const shipped = list2(src);
|
|
24445
|
+
if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
|
|
24446
|
+
for (const rel of shipped) {
|
|
24447
|
+
const a = await (0, import_promises15.readFile)((0, import_node_path30.join)(src, rel), "utf-8");
|
|
24448
|
+
const b = await (0, import_promises15.readFile)((0, import_node_path30.join)(dest, rel), "utf-8");
|
|
24449
|
+
if (a !== b) return false;
|
|
24450
|
+
}
|
|
24451
|
+
return true;
|
|
24452
|
+
} catch {
|
|
24453
|
+
return false;
|
|
24454
|
+
}
|
|
24455
|
+
}
|
|
24456
|
+
var SKILLS = [
|
|
24457
|
+
"verity-setup",
|
|
24458
|
+
"verity-analyze",
|
|
24459
|
+
"verity-status",
|
|
24460
|
+
"verity-feedback",
|
|
24461
|
+
"verity-learn",
|
|
24462
|
+
"verity-memory",
|
|
24463
|
+
"verity-insights",
|
|
24464
|
+
"verity-reflect"
|
|
24465
|
+
];
|
|
24466
|
+
var INTENSITY_CHOICES = [
|
|
24467
|
+
{ id: "lightweight", label: "lightweight", hint: "critical security only, fastest (~3s)" },
|
|
24468
|
+
{ id: "balanced", label: "balanced", hint: "security + quality (~8s)", recommended: true },
|
|
24469
|
+
{ id: "thorough", label: "thorough", hint: "all tools, all rules (~15s)" }
|
|
24470
|
+
];
|
|
24471
|
+
var MOMENT_CHOICES = [
|
|
24472
|
+
{ id: "stop", label: "On stop", hint: "after every agent turn \u2014 fast feedback while you work", recommended: true },
|
|
24473
|
+
{ id: "pre-commit", label: "Before commit", hint: "reviews the staged diff, blocks the commit on FAIL" },
|
|
24474
|
+
{ id: "pre-push", label: "Before push / PR", hint: "reviews the to-be-pushed commits, blocks on FAIL" }
|
|
24475
|
+
];
|
|
24476
|
+
var DEFAULT_MOMENTS = ["stop"];
|
|
24477
|
+
async function momentsFromInstalledHooks() {
|
|
24478
|
+
const status = await checkAllVerityHooks();
|
|
24479
|
+
const wired = [];
|
|
24480
|
+
if (status.stop) wired.push("stop");
|
|
24481
|
+
if (status.guardOn.includes("commit")) wired.push("pre-commit");
|
|
24482
|
+
if (status.guardOn.includes("push")) wired.push("pre-push");
|
|
24483
|
+
return wired.length > 0 ? wired : DEFAULT_MOMENTS;
|
|
24484
|
+
}
|
|
24485
|
+
async function askSetupQuestions(defaultsOnly, previous) {
|
|
24486
|
+
const intensityDefault = previous?.intensity ?? "balanced";
|
|
24487
|
+
const momentsDefault = previous?.moments?.length ? previous.moments : await momentsFromInstalledHooks();
|
|
24488
|
+
if (defaultsOnly) {
|
|
24489
|
+
return { intensity: intensityDefault, moments: momentsDefault, telemetry: "not-asked" };
|
|
24490
|
+
}
|
|
24491
|
+
if (previous?.intensity || previous?.moments) {
|
|
24492
|
+
printInfo(`Current: ${intensityDefault} \xB7 ${momentsDefault.join(", ")} \u2014 press Enter to keep either.`);
|
|
24493
|
+
}
|
|
24494
|
+
const intensity = await promptChoice(
|
|
24495
|
+
"Analysis intensity \u2014 how deeply should Verity review?",
|
|
24496
|
+
INTENSITY_CHOICES,
|
|
24497
|
+
intensityDefault
|
|
24498
|
+
);
|
|
24499
|
+
const moments = await promptMultiSelect(
|
|
24500
|
+
"When should Verity review your code?",
|
|
24501
|
+
MOMENT_CHOICES,
|
|
24502
|
+
momentsDefault
|
|
24503
|
+
);
|
|
24504
|
+
const current = await checkTelemetry();
|
|
24505
|
+
if (current.enabled) {
|
|
24506
|
+
printInfo(`Cost & usage telemetry: already enabled \u2192 ${current.endpoint}`);
|
|
24507
|
+
printInfo(' (turn it off with "verity telemetry uninstall")');
|
|
24508
|
+
return { intensity, moments, telemetry: "already-on" };
|
|
24509
|
+
}
|
|
24510
|
+
console.log("");
|
|
24511
|
+
console.log(" Cost & usage telemetry (opt-in) \u2014 powers the /usage dashboard.");
|
|
24512
|
+
console.log(" Sends Claude Code's own OpenTelemetry metrics and traces only: model names,");
|
|
24513
|
+
console.log(" token counts, USD cost, agent types, session ids. NOT your prompts, code, or");
|
|
24514
|
+
console.log(" tool input/output. Without it, /usage stays empty.");
|
|
24515
|
+
const wants = await promptYes(" Enable cost & usage telemetry? [Y/n] ", { nonInteractive: false });
|
|
24516
|
+
return { intensity, moments, telemetry: wants ? "yes" : "no" };
|
|
24517
|
+
}
|
|
24518
|
+
function insideClaudeCode() {
|
|
24519
|
+
return !!process.env.CLAUDECODE || !!process.env.CLAUDE_SESSION_ID;
|
|
24520
|
+
}
|
|
24521
|
+
function resumeDeferredPending(previous) {
|
|
24522
|
+
return previous?.telemetry === "deferred";
|
|
24523
|
+
}
|
|
24524
|
+
async function maybeAdoptRemoteSetup(opts) {
|
|
24525
|
+
if (!opts.adopt) {
|
|
24526
|
+
printInfo(" Skipped (--no-adopt) \u2014 the setup below runs from this codebase.");
|
|
24527
|
+
return false;
|
|
24528
|
+
}
|
|
24529
|
+
const token = await resolveToken(opts.globals.token);
|
|
24530
|
+
const url = await resolveServiceUrl(opts.globals.serviceUrl);
|
|
24531
|
+
if (!token.ok || !url.ok) {
|
|
24532
|
+
printInfo(" Not signed in, so there is nothing to fetch \u2014 the setup below runs from your codebase.");
|
|
24533
|
+
return false;
|
|
24534
|
+
}
|
|
24535
|
+
const found = await fetchRemoteSetup({
|
|
24536
|
+
token: token.data.token,
|
|
24537
|
+
serviceUrl: url.data,
|
|
24538
|
+
verbose: opts.globals.verbose
|
|
24539
|
+
});
|
|
24540
|
+
if (found.status === "unavailable") {
|
|
24541
|
+
printWarn(` Could not ask the service (${found.reason}) \u2014 continuing with the regular setup.`);
|
|
24542
|
+
return false;
|
|
24543
|
+
}
|
|
24544
|
+
if (found.status === "none") {
|
|
24545
|
+
printInfo(" No Standard stored for this repository yet \u2014 this setup will create the first one.");
|
|
24546
|
+
return false;
|
|
24547
|
+
}
|
|
24548
|
+
if (opts.previous?.declinedStandardVersion === found.standard.version) {
|
|
24549
|
+
printInfo(` Standard v${found.standard.version} is available; you declined this version before \u2014 not asking again.`);
|
|
24550
|
+
printInfo(" A newer version will be offered. To take it now: verity standard get, or /verity-setup.");
|
|
24551
|
+
return false;
|
|
24552
|
+
}
|
|
24553
|
+
console.log("");
|
|
24554
|
+
console.log(` This repository already has a Standard on Verity: ${describeRemote(found)}`);
|
|
24555
|
+
console.log(" Using it keeps you consistent with everyone else on the project.");
|
|
24556
|
+
console.log(" Synthesizing a new one from this codebase would push a competing version.");
|
|
24557
|
+
console.log("");
|
|
24558
|
+
const use = opts.defaultsOnly ? true : await promptYes(" Use the existing Standard? [Y/n] ", { nonInteractive: true });
|
|
24559
|
+
if (!use) {
|
|
24560
|
+
printInfo(" Keeping the existing version untouched; /verity-setup will synthesize a new one.");
|
|
24561
|
+
await writeSetupState({ declinedStandardVersion: found.standard.version }).catch(() => {
|
|
24562
|
+
});
|
|
24563
|
+
return false;
|
|
24564
|
+
}
|
|
24565
|
+
try {
|
|
24566
|
+
const outcome = await adoptRemoteSetup(found, {
|
|
24567
|
+
serviceUrl: url.data,
|
|
24568
|
+
projectId: await projectLabel(token.data.token, url.data, opts.globals.verbose)
|
|
24569
|
+
});
|
|
24570
|
+
for (const path of outcome.written) printInfo(` ${path} \u2713`);
|
|
24571
|
+
for (const note of outcome.notes) printWarn(` ${note}`);
|
|
24572
|
+
if (opts.previous?.declinedStandardVersion != null) {
|
|
24573
|
+
await writeSetupState({ declinedStandardVersion: void 0 }).catch(() => {
|
|
24574
|
+
});
|
|
24575
|
+
}
|
|
24576
|
+
return true;
|
|
24577
|
+
} catch (err) {
|
|
24578
|
+
printWarn(` Could not write the fetched configuration: ${err.message}`);
|
|
24579
|
+
printInfo(" Falling back to the regular setup.");
|
|
24580
|
+
return false;
|
|
24581
|
+
}
|
|
24582
|
+
}
|
|
24583
|
+
async function projectLabel(token, serviceUrl, verbose) {
|
|
24584
|
+
try {
|
|
24585
|
+
const who = await whoami(token, serviceUrl, verbose);
|
|
24586
|
+
return who.ok ? who.data.project_id : null;
|
|
24587
|
+
} catch {
|
|
24588
|
+
return null;
|
|
24589
|
+
}
|
|
24590
|
+
}
|
|
24591
|
+
var PHASE_TWO_ARTIFACTS = [
|
|
24592
|
+
{
|
|
24593
|
+
path: ".verity/standard.yaml",
|
|
24594
|
+
what: "the Standard, synthesized from your codebase",
|
|
24595
|
+
// The presence flag travels WITH the row. Read positionally from a parallel
|
|
24596
|
+
// array, a reorder of this list would silently move every ✓ onto the wrong
|
|
24597
|
+
// path — a report that is confidently wrong about what got created.
|
|
24598
|
+
present: (a) => a.standard
|
|
24599
|
+
},
|
|
24600
|
+
{
|
|
24601
|
+
path: ".codacy/codacy.config.json",
|
|
24602
|
+
what: "curated static-analysis patterns",
|
|
24603
|
+
present: (a) => a.analysisConfig
|
|
24604
|
+
},
|
|
24605
|
+
{
|
|
24606
|
+
path: "VERITY.md",
|
|
24607
|
+
what: "project quality overview",
|
|
24608
|
+
present: (a) => a.verityMd
|
|
24609
|
+
}
|
|
24610
|
+
];
|
|
24611
|
+
async function reportPhaseTwo(startedAt) {
|
|
24612
|
+
const elapsed = Math.round((Date.now() - startedAt) / 1e3);
|
|
24613
|
+
let report = null;
|
|
24614
|
+
try {
|
|
24615
|
+
report = await buildReport();
|
|
24616
|
+
} catch {
|
|
24617
|
+
}
|
|
24618
|
+
console.log("");
|
|
24619
|
+
if (!report) {
|
|
24620
|
+
printWarn('Could not verify what the setup session produced \u2014 run "verity doctor".');
|
|
24621
|
+
return;
|
|
24622
|
+
}
|
|
24623
|
+
const { artifacts } = report;
|
|
24624
|
+
const complete = PHASE_TWO_ARTIFACTS.every((a) => a.present(artifacts));
|
|
24625
|
+
printInfo(complete ? `Setup complete (${elapsed}s).` : `Setup session ended after ${elapsed}s.`);
|
|
24626
|
+
for (const { path, what, present } of PHASE_TWO_ARTIFACTS) {
|
|
24627
|
+
const ok = present(artifacts);
|
|
24628
|
+
console.log(` ${ok ? "\u2713" : "\xB7"} ${path.padEnd(30)} ${ok ? what : `${what} \u2014 NOT created`}`);
|
|
24629
|
+
}
|
|
24630
|
+
if (!complete) {
|
|
24631
|
+
console.log("");
|
|
24632
|
+
printWarn('Setup did not finish. Re-run "/verity-setup" in Claude Code to complete it \u2014');
|
|
24633
|
+
printWarn(" nothing is lost; it picks up from what is already on disk.");
|
|
24634
|
+
}
|
|
24635
|
+
}
|
|
24636
|
+
async function handoffToSetup(enabled, claudeInstalled) {
|
|
24637
|
+
const instruct = (why) => {
|
|
24638
|
+
console.log("");
|
|
24639
|
+
printInfo("Still to do \u2014 this is what /verity-setup does (it needs a model):");
|
|
24640
|
+
for (const { path, what } of PHASE_TWO_ARTIFACTS) {
|
|
24641
|
+
console.log(` ${path.padEnd(30)} ${what}`);
|
|
24642
|
+
}
|
|
24643
|
+
console.log("");
|
|
24644
|
+
printInfo("Next step: run /verity-setup in Claude Code.");
|
|
24645
|
+
printInfo(` (${why})`);
|
|
24646
|
+
};
|
|
24647
|
+
if (!enabled) return instruct("--no-setup was passed");
|
|
24648
|
+
if (insideClaudeCode()) return instruct("you are already in a Claude Code session \u2014 invoke the skill there");
|
|
24649
|
+
if (!interactive()) return instruct("no interactive terminal here");
|
|
24650
|
+
if (!claudeInstalled) return instruct("Claude Code is not installed on this machine yet");
|
|
24651
|
+
printPhase(2, 2, "your Standard", "reading the codebase \xB7 synthesizing the Standard \xB7 curating patterns");
|
|
24652
|
+
console.log(" Claude Code takes over the screen from here. It will:");
|
|
24653
|
+
console.log(" \xB7 read your codebase \u2014 languages, frameworks, architecture");
|
|
24654
|
+
console.log(" \xB7 synthesize .verity/standard.yaml and show it to you");
|
|
24655
|
+
console.log(" \xB7 write the analysis config, then VERITY.md");
|
|
24656
|
+
console.log(" Usually a minute or two. Quit any time \u2014 re-running /verity-setup resumes.");
|
|
24657
|
+
console.log("");
|
|
24658
|
+
const startedAt = Date.now();
|
|
24659
|
+
const run2 = (0, import_node_child_process14.spawnSync)("claude", ["/verity-setup"], { stdio: "inherit" });
|
|
24660
|
+
if (run2.error) {
|
|
24661
|
+
printWarn(`Could not start Claude Code: ${run2.error.message}`);
|
|
24662
|
+
return instruct("start it yourself and run the skill there");
|
|
24663
|
+
}
|
|
24664
|
+
await reportPhaseTwo(startedAt);
|
|
22809
24665
|
}
|
|
22810
24666
|
function registerInitCommand(program2) {
|
|
22811
|
-
program2.command("init").description("
|
|
24667
|
+
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").option("--no-adopt", "Don't offer this repository's existing Standard from the service; synthesize a new one").action(async (opts) => {
|
|
22812
24668
|
const force = opts.force ?? false;
|
|
24669
|
+
const wantsHandoff = opts.setup !== false;
|
|
24670
|
+
const wantsAdopt = opts.adopt !== false;
|
|
24671
|
+
const defaultsOnly = (opts.yes ?? false) || !interactive();
|
|
22813
24672
|
const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
|
|
22814
|
-
const isProject = projectMarkers.some((m) => (0,
|
|
24673
|
+
const isProject = projectMarkers.some((m) => (0, import_node_fs45.existsSync)(m));
|
|
22815
24674
|
if (!isProject) {
|
|
22816
24675
|
printError("No project detected in the current directory.");
|
|
22817
24676
|
printInfo('Run "verity init" from your project root.');
|
|
22818
24677
|
process.exit(1);
|
|
22819
24678
|
}
|
|
22820
|
-
|
|
22821
|
-
|
|
22822
|
-
|
|
24679
|
+
const showArt = canRenderArt() && !insideClaudeCode();
|
|
24680
|
+
printBanner({ interactive: showArt });
|
|
24681
|
+
if (!showArt) {
|
|
24682
|
+
console.log("");
|
|
24683
|
+
printInfo("Initializing Verity in this project...");
|
|
24684
|
+
console.log("");
|
|
24685
|
+
} else {
|
|
24686
|
+
printPhase(1, 2, "this machine", "prerequisites \xB7 skills \xB7 hooks \xB7 sign-in");
|
|
24687
|
+
}
|
|
24688
|
+
const TOTAL_STEPS = 9;
|
|
24689
|
+
let stepNo = 0;
|
|
24690
|
+
const step = (label2) => {
|
|
24691
|
+
stepNo++;
|
|
24692
|
+
printInfo(`${DIM}[${stepNo}/${TOTAL_STEPS}]${NC} ${label2}`);
|
|
24693
|
+
};
|
|
22823
24694
|
if (await needsMigration()) {
|
|
22824
24695
|
printInfo("Legacy GATE.md install detected \u2014 migrating to Verity...");
|
|
22825
24696
|
try {
|
|
@@ -22830,144 +24701,190 @@ function registerInitCommand(program2) {
|
|
|
22830
24701
|
}
|
|
22831
24702
|
console.log("");
|
|
22832
24703
|
}
|
|
22833
|
-
|
|
22834
|
-
const
|
|
22835
|
-
const
|
|
22836
|
-
|
|
22837
|
-
|
|
22838
|
-
|
|
24704
|
+
step("Checking prerequisites");
|
|
24705
|
+
const prereqs = await checkPrereqs({ install: true });
|
|
24706
|
+
for (const c of prereqs.checks) {
|
|
24707
|
+
if (c.status === "ok") {
|
|
24708
|
+
if (c.justInstalled) continue;
|
|
24709
|
+
printInfo(` ${c.label} ${c.detail} \u2713`);
|
|
24710
|
+
} else {
|
|
24711
|
+
printWarn(` ${c.label}: ${c.detail}`);
|
|
24712
|
+
if (c.remedy) printWarn(` ${c.remedy}`);
|
|
24713
|
+
}
|
|
22839
24714
|
}
|
|
22840
|
-
|
|
22841
|
-
|
|
22842
|
-
const gitVersion = (0, import_node_child_process11.execSync)("git --version", { encoding: "utf-8" }).trim();
|
|
22843
|
-
printInfo(` ${gitVersion} \u2713`);
|
|
22844
|
-
} catch {
|
|
22845
|
-
printError("git is required but not installed. Install from https://git-scm.com");
|
|
24715
|
+
if (prereqs.blocked) {
|
|
24716
|
+
printError("A required prerequisite is missing \u2014 cannot continue.");
|
|
22846
24717
|
process.exit(1);
|
|
22847
24718
|
}
|
|
22848
|
-
|
|
22849
|
-
(0, import_node_child_process11.execSync)("which claude", { encoding: "utf-8" });
|
|
22850
|
-
printInfo(" Claude Code \u2713");
|
|
22851
|
-
} catch {
|
|
22852
|
-
printWarn(" Claude Code not found \u2014 hooks will be configured but need Claude Code to run.");
|
|
22853
|
-
}
|
|
22854
|
-
try {
|
|
22855
|
-
(0, import_node_child_process11.execSync)("which codacy-analysis", { encoding: "utf-8", stdio: "pipe" });
|
|
22856
|
-
printInfo(" @codacy/analysis-cli \u2713");
|
|
22857
|
-
} catch {
|
|
22858
|
-
printInfo(" Installing @codacy/analysis-cli...");
|
|
22859
|
-
try {
|
|
22860
|
-
(0, import_node_child_process11.execSync)("npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "pipe", timeout: 12e4 });
|
|
22861
|
-
printInfo(" @codacy/analysis-cli installed \u2713");
|
|
22862
|
-
} catch {
|
|
22863
|
-
try {
|
|
22864
|
-
printWarn(" Retrying with sudo...");
|
|
22865
|
-
(0, import_node_child_process11.execSync)("sudo npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
|
|
22866
|
-
printInfo(" @codacy/analysis-cli installed \u2713");
|
|
22867
|
-
} catch {
|
|
22868
|
-
printWarn(" Could not install @codacy/analysis-cli automatically.");
|
|
22869
|
-
printWarn(" Install manually: npm install -g @codacy/analysis-cli");
|
|
22870
|
-
printWarn(" Static analysis will be unavailable until installed.");
|
|
22871
|
-
}
|
|
22872
|
-
}
|
|
22873
|
-
}
|
|
24719
|
+
const claudeInstalled = prereqs.checks.some((c) => c.id === "claude" && c.status === "ok");
|
|
22874
24720
|
console.log("");
|
|
22875
|
-
|
|
24721
|
+
step("Installing skills");
|
|
22876
24722
|
const dataDir = resolveDataDir();
|
|
22877
|
-
const skillsSource = (0,
|
|
24723
|
+
const skillsSource = (0, import_node_path30.join)(dataDir, "skills");
|
|
22878
24724
|
const skillsDest = ".claude/skills";
|
|
22879
|
-
const skills = ["verity-setup", "verity-analyze", "verity-status", "verity-feedback", "verity-learn", "verity-memory", "verity-insights", "verity-reflect"];
|
|
22880
24725
|
let skillsInstalled = 0;
|
|
22881
|
-
for (const skill of
|
|
22882
|
-
const src = (0,
|
|
22883
|
-
const dest = (0,
|
|
22884
|
-
if (!(0,
|
|
24726
|
+
for (const skill of SKILLS) {
|
|
24727
|
+
const src = (0, import_node_path30.join)(skillsSource, skill);
|
|
24728
|
+
const dest = (0, import_node_path30.join)(skillsDest, skill);
|
|
24729
|
+
if (!(0, import_node_fs45.existsSync)(src)) {
|
|
22885
24730
|
printWarn(` Skill data not found: ${skill}`);
|
|
22886
24731
|
continue;
|
|
22887
24732
|
}
|
|
22888
|
-
if ((0,
|
|
22889
|
-
|
|
22890
|
-
|
|
22891
|
-
if ((0, import_node_fs41.existsSync)(destSkill)) {
|
|
22892
|
-
try {
|
|
22893
|
-
const srcContent = await (0, import_promises13.readFile)(srcSkill, "utf-8");
|
|
22894
|
-
const destContent = await (0, import_promises13.readFile)(destSkill, "utf-8");
|
|
22895
|
-
if (srcContent === destContent) {
|
|
22896
|
-
skillsInstalled++;
|
|
22897
|
-
continue;
|
|
22898
|
-
}
|
|
22899
|
-
} catch {
|
|
22900
|
-
}
|
|
22901
|
-
}
|
|
24733
|
+
if ((0, import_node_fs45.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) {
|
|
24734
|
+
skillsInstalled++;
|
|
24735
|
+
continue;
|
|
22902
24736
|
}
|
|
22903
24737
|
await copyDir(src, dest);
|
|
22904
24738
|
skillsInstalled++;
|
|
22905
24739
|
}
|
|
22906
|
-
printInfo(` ${skillsInstalled}/${
|
|
22907
|
-
|
|
22908
|
-
const
|
|
22909
|
-
const
|
|
22910
|
-
const
|
|
22911
|
-
if (
|
|
22912
|
-
|
|
22913
|
-
printInfo(" Stop hook: verity analyze \u2713");
|
|
22914
|
-
printInfo(" Intent hook: verity intent capture \u2713");
|
|
22915
|
-
printInfo(" Baseline hook: verity baseline capture \u2713");
|
|
22916
|
-
} else {
|
|
22917
|
-
printWarn(` ${hookResult.error}`);
|
|
22918
|
-
printInfo(' Run "verity hooks install --force" to overwrite.');
|
|
24740
|
+
printInfo(` ${skillsInstalled}/${SKILLS.length} skills installed to .claude/skills/ \u2713`);
|
|
24741
|
+
step(defaultsOnly ? "Setup answers (defaults)" : "Your setup answers");
|
|
24742
|
+
const previous = await readSetupState();
|
|
24743
|
+
const answers = await askSetupQuestions(defaultsOnly, previous);
|
|
24744
|
+
const { intensity, moments } = answers;
|
|
24745
|
+
if (defaultsOnly) {
|
|
24746
|
+
printInfo(` intensity: ${intensity} \xB7 moments: ${moments.join(", ") || "none"} (no questions asked)`);
|
|
22919
24747
|
}
|
|
22920
|
-
|
|
24748
|
+
step("Knowledge base, .gitignore and CLAUDE.md");
|
|
24749
|
+
await (0, import_promises15.mkdir)(VERITY_DIR, { recursive: true });
|
|
22921
24750
|
await ensureMemoryDir();
|
|
22922
|
-
const ignoreResult =
|
|
24751
|
+
const ignoreResult = ensureVerityGitignore();
|
|
22923
24752
|
if (ignoreResult === "failed") {
|
|
22924
|
-
printWarn(" .gitignore: could not
|
|
24753
|
+
printWarn(" .gitignore: could not write the Verity block \u2014 add it manually");
|
|
24754
|
+
printWarn(" (.verity/ holds copies of analyzed files, including any secret the gate flagged)");
|
|
24755
|
+
} else if (ignoreResult === "conflict") {
|
|
24756
|
+
printWarn(" .gitignore: the Verity block is in place but git still ignores .verity/standard.yaml");
|
|
24757
|
+
printWarn(" Something outside this file covers it \u2014 a global (~/.gitignore) or nested");
|
|
24758
|
+
printWarn(" .gitignore, or a pattern we do not recognise. Check: git check-ignore -v .verity/standard.yaml");
|
|
24759
|
+
printWarn(" Until it is fixed, the Standard and the knowledge graph cannot be committed.");
|
|
24760
|
+
} else if (ignoreResult === "repaired") {
|
|
24761
|
+
printInfo(" .gitignore: rewrote `.verity/` to `.verity/*` so the standard stays committable \u2713");
|
|
22925
24762
|
} else {
|
|
22926
|
-
printInfo(` .gitignore:
|
|
24763
|
+
printInfo(` .gitignore: Verity block ${ignoreResult === "added" ? "added" : "already covered"} \u2713`);
|
|
24764
|
+
}
|
|
24765
|
+
const tracked = committedVerityState();
|
|
24766
|
+
if (tracked.length > 0) {
|
|
24767
|
+
printWarn(` ${tracked.length} Verity state file(s) are tracked in git (e.g. ${tracked[0]}).`);
|
|
24768
|
+
const untrack = defaultsOnly ? false : await promptYes(" Untrack them now (files stay on disk)? [Y/n] ", { nonInteractive: false });
|
|
24769
|
+
if (untrack) {
|
|
24770
|
+
const result = untrackVerityState();
|
|
24771
|
+
if (result === "untracked") printInfo(" Untracked (staged) \u2014 commit to finish \u2713");
|
|
24772
|
+
else if (result === "failed") printWarn(' Could not untrack \u2014 run "git rm -r --cached .verity" manually');
|
|
24773
|
+
} else {
|
|
24774
|
+
printWarn(" Left tracked. Fix with: git rm -r --cached .verity && git add .verity/standard.yaml .verity/memory");
|
|
24775
|
+
}
|
|
22927
24776
|
}
|
|
22928
24777
|
try {
|
|
22929
24778
|
await ensureClaudeMdPointer();
|
|
22930
|
-
printInfo(" CLAUDE.md
|
|
24779
|
+
printInfo(" CLAUDE.md instructions \u2713");
|
|
22931
24780
|
} catch (err) {
|
|
22932
24781
|
printWarn(` Could not update CLAUDE.md: ${err.message}`);
|
|
22933
24782
|
}
|
|
22934
|
-
const globalVerityDir = (0,
|
|
22935
|
-
await (0,
|
|
24783
|
+
const globalVerityDir = (0, import_node_path30.join)(process.env.HOME ?? "", ".verity");
|
|
24784
|
+
await (0, import_promises15.mkdir)(globalVerityDir, { recursive: true });
|
|
22936
24785
|
console.log("");
|
|
24786
|
+
step("Wiring Claude Code hooks");
|
|
24787
|
+
await applyMomentSelection(moments);
|
|
24788
|
+
const hookStatus = await checkAllVerityHooks();
|
|
24789
|
+
printInfo(` Stop (verity analyze): ${hookStatus.stop ? "on" : "off"}`);
|
|
24790
|
+
printInfo(` Pre-commit gate: ${hookStatus.guardOn.includes("commit") ? "on" : "off"}`);
|
|
24791
|
+
printInfo(` Pre-push/PR gate: ${hookStatus.guardOn.includes("push") ? "on" : "off"}`);
|
|
24792
|
+
printInfo(` Intent + baseline + compact + session-end: always on \u2713`);
|
|
24793
|
+
if (!hookStatus.stop && hookStatus.guardOn.length === 0) {
|
|
24794
|
+
printWarn(" No analysis moment is active \u2014 code changes will NOT be reviewed.");
|
|
24795
|
+
printWarn(" Enable one: verity hooks install --moments stop");
|
|
24796
|
+
}
|
|
24797
|
+
console.log("");
|
|
24798
|
+
step("Sign in to Verity (optional)");
|
|
22937
24799
|
try {
|
|
22938
24800
|
const globals = program2.opts();
|
|
22939
24801
|
const resolution = await resolveServiceUrlForAuth(globals.serviceUrl);
|
|
22940
24802
|
await runOptionalAuth(resolution, {
|
|
22941
24803
|
token: globals.token,
|
|
22942
|
-
verbose: globals.verbose
|
|
24804
|
+
verbose: globals.verbose,
|
|
24805
|
+
yes: defaultsOnly
|
|
22943
24806
|
});
|
|
22944
24807
|
} catch (err) {
|
|
22945
24808
|
printWarn(`Authentication step skipped: ${err.message}`);
|
|
22946
24809
|
}
|
|
24810
|
+
const resumeDeferred = answers.telemetry === "not-asked" && resumeDeferredPending(previous);
|
|
24811
|
+
const wantsTelemetry = answers.telemetry === "yes" || resumeDeferred;
|
|
24812
|
+
step("Cost & usage telemetry");
|
|
24813
|
+
if (answers.telemetry === "not-asked" && !resumeDeferredPending(previous)) {
|
|
24814
|
+
printInfo(' not asked (unattended run) \u2014 enable later with "verity telemetry install"');
|
|
24815
|
+
} else if (answers.telemetry === "no") {
|
|
24816
|
+
printInfo(' declined \u2014 enable later with "verity telemetry install"');
|
|
24817
|
+
}
|
|
24818
|
+
let telemetryChoice = answers.telemetry === "already-on" ? "enabled" : answers.telemetry === "no" ? "declined" : wantsTelemetry ? "deferred" : void 0;
|
|
24819
|
+
if (wantsTelemetry) {
|
|
24820
|
+
const globals = program2.opts();
|
|
24821
|
+
const token = await resolveToken(globals.token);
|
|
24822
|
+
const url = await resolveServiceUrl(globals.serviceUrl);
|
|
24823
|
+
if (token.ok && url.ok) {
|
|
24824
|
+
const installed2 = await installTelemetry(url.data);
|
|
24825
|
+
if (installed2.ok) {
|
|
24826
|
+
telemetryChoice = "enabled";
|
|
24827
|
+
printInfo(`Telemetry enabled \u2192 ${installed2.data.endpoint}`);
|
|
24828
|
+
printInfo(" takes effect on your NEXT Claude Code session; view cost & usage at /usage");
|
|
24829
|
+
} else {
|
|
24830
|
+
printWarn(`Could not enable telemetry: ${installed2.error}`);
|
|
24831
|
+
}
|
|
24832
|
+
} else {
|
|
24833
|
+
printWarn('Telemetry needs a Verity token \u2014 run "verity login", then "verity telemetry install".');
|
|
24834
|
+
}
|
|
24835
|
+
}
|
|
24836
|
+
step("Checking Verity for an existing setup");
|
|
24837
|
+
let adopted = false;
|
|
24838
|
+
if ((0, import_node_fs45.existsSync)(projectPath(STANDARD_FILE))) {
|
|
24839
|
+
printInfo(" This project already has .verity/standard.yaml \u2014 keeping it.");
|
|
24840
|
+
} else {
|
|
24841
|
+
adopted = await maybeAdoptRemoteSetup({
|
|
24842
|
+
globals: program2.opts(),
|
|
24843
|
+
defaultsOnly,
|
|
24844
|
+
previous,
|
|
24845
|
+
adopt: wantsAdopt
|
|
24846
|
+
});
|
|
24847
|
+
}
|
|
24848
|
+
step("Recording your answers");
|
|
24849
|
+
try {
|
|
24850
|
+
await writeSetupState({
|
|
24851
|
+
intensity,
|
|
24852
|
+
moments,
|
|
24853
|
+
...telemetryChoice ? { telemetry: telemetryChoice } : {},
|
|
24854
|
+
init: {
|
|
24855
|
+
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24856
|
+
cli_version: true ? "0.31.1-experimental.b9b3862" : "dev"
|
|
24857
|
+
}
|
|
24858
|
+
});
|
|
24859
|
+
} catch (err) {
|
|
24860
|
+
printWarn(`Could not record setup answers: ${err.message}`);
|
|
24861
|
+
}
|
|
22947
24862
|
console.log("");
|
|
22948
|
-
printInfo("
|
|
24863
|
+
printInfo("This machine is set up.");
|
|
22949
24864
|
console.log("");
|
|
22950
|
-
console.log("
|
|
22951
|
-
console.log("
|
|
22952
|
-
console.log(" .claude/
|
|
22953
|
-
console.log(" .
|
|
22954
|
-
console.log(" .
|
|
22955
|
-
console.log(" .
|
|
22956
|
-
console.log(" .
|
|
22957
|
-
console.log(" .claude/skills/verity-insights/ \u2014 quality metrics + evolution");
|
|
22958
|
-
console.log(" .claude/skills/verity-reflect/ \u2014 capture learnings");
|
|
22959
|
-
console.log(" .claude/settings.json \u2014 hooks (verity analyze + intent capture)");
|
|
22960
|
-
console.log(" .verity/memory/ \u2014 knowledge base (8 domains, commit to git)");
|
|
24865
|
+
console.log(" .claude/skills/verity-*/ 8 skills (setup, analyze, status, feedback,");
|
|
24866
|
+
console.log(" learn, memory, insights, reflect)");
|
|
24867
|
+
console.log(" .claude/settings.json hooks, reconciled to your chosen moments");
|
|
24868
|
+
console.log(" .verity/memory/ knowledge base (commit to git)");
|
|
24869
|
+
console.log(" .verity/setup.json your answers, read by /verity-setup");
|
|
24870
|
+
console.log(" .gitignore Verity block (whitelist form)");
|
|
24871
|
+
console.log(" CLAUDE.md memory pointer, waive policy, reflection");
|
|
22961
24872
|
console.log("");
|
|
22962
|
-
console.log(
|
|
22963
|
-
|
|
24873
|
+
console.log(` Intensity: ${intensity} Moments: ${moments.join(", ") || "none"}`);
|
|
24874
|
+
if (adopted) {
|
|
24875
|
+
console.log("");
|
|
24876
|
+
printInfo("Setup complete \u2014 this project uses your team's Standard.");
|
|
24877
|
+
printInfo(" Re-synthesize from this codebase instead: /verity-setup --force");
|
|
24878
|
+
} else {
|
|
24879
|
+
await handoffToSetup(wantsHandoff, claudeInstalled);
|
|
24880
|
+
}
|
|
22964
24881
|
console.log("");
|
|
22965
24882
|
});
|
|
22966
24883
|
}
|
|
22967
24884
|
|
|
22968
24885
|
// src/commands/uninstall.ts
|
|
22969
|
-
var
|
|
22970
|
-
var
|
|
24886
|
+
var import_node_fs46 = require("node:fs");
|
|
24887
|
+
var import_node_path31 = require("node:path");
|
|
22971
24888
|
var SKILL_NAMES = [
|
|
22972
24889
|
"verity-setup",
|
|
22973
24890
|
"verity-analyze",
|
|
@@ -22986,11 +24903,11 @@ function registerUninstallCommand(program2) {
|
|
|
22986
24903
|
const actions = [];
|
|
22987
24904
|
const skillsRoot = projectPath(".claude/skills");
|
|
22988
24905
|
for (const name of SKILL_NAMES) {
|
|
22989
|
-
const dir = (0,
|
|
22990
|
-
if ((0,
|
|
24906
|
+
const dir = (0, import_node_path31.join)(skillsRoot, name);
|
|
24907
|
+
if ((0, import_node_fs46.existsSync)(dir)) {
|
|
22991
24908
|
actions.push({
|
|
22992
24909
|
label: `Remove .claude/skills/${name}/`,
|
|
22993
|
-
apply: () => (0,
|
|
24910
|
+
apply: () => (0, import_node_fs46.rmSync)(dir, { recursive: true, force: true })
|
|
22994
24911
|
});
|
|
22995
24912
|
}
|
|
22996
24913
|
}
|
|
@@ -23004,24 +24921,24 @@ function registerUninstallCommand(program2) {
|
|
|
23004
24921
|
});
|
|
23005
24922
|
}
|
|
23006
24923
|
const verityDir = projectPath(VERITY_DIR);
|
|
23007
|
-
if ((0,
|
|
24924
|
+
if ((0, import_node_fs46.existsSync)(verityDir)) {
|
|
23008
24925
|
actions.push({
|
|
23009
24926
|
label: `Remove ${VERITY_DIR}/`,
|
|
23010
|
-
apply: () => (0,
|
|
24927
|
+
apply: () => (0, import_node_fs46.rmSync)(verityDir, { recursive: true, force: true })
|
|
23011
24928
|
});
|
|
23012
24929
|
}
|
|
23013
24930
|
if (!keepVerityMd) {
|
|
23014
24931
|
const verityMd = projectPath(VERITY_MD_FILE);
|
|
23015
|
-
if ((0,
|
|
24932
|
+
if ((0, import_node_fs46.existsSync)(verityMd)) {
|
|
23016
24933
|
actions.push({
|
|
23017
24934
|
label: `Remove ${VERITY_MD_FILE}`,
|
|
23018
|
-
apply: () => (0,
|
|
24935
|
+
apply: () => (0, import_node_fs46.rmSync)(verityMd, { force: true })
|
|
23019
24936
|
});
|
|
23020
24937
|
}
|
|
23021
24938
|
}
|
|
23022
24939
|
const cleanupEmptyDir = (path) => {
|
|
23023
|
-
if ((0,
|
|
23024
|
-
(0,
|
|
24940
|
+
if ((0, import_node_fs46.existsSync)(path) && (0, import_node_fs46.statSync)(path).isDirectory() && (0, import_node_fs46.readdirSync)(path).length === 0) {
|
|
24941
|
+
(0, import_node_fs46.rmdirSync)(path);
|
|
23025
24942
|
}
|
|
23026
24943
|
};
|
|
23027
24944
|
actions.push({
|
|
@@ -23032,11 +24949,11 @@ function registerUninstallCommand(program2) {
|
|
|
23032
24949
|
}
|
|
23033
24950
|
});
|
|
23034
24951
|
const home = process.env.HOME ?? "";
|
|
23035
|
-
const globalVerityDir = (0,
|
|
23036
|
-
if (purgeGlobal && (0,
|
|
24952
|
+
const globalVerityDir = (0, import_node_path31.join)(home, ".verity");
|
|
24953
|
+
if (purgeGlobal && (0, import_node_fs46.existsSync)(globalVerityDir)) {
|
|
23037
24954
|
actions.push({
|
|
23038
24955
|
label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
|
|
23039
|
-
apply: () => (0,
|
|
24956
|
+
apply: () => (0, import_node_fs46.rmSync)(globalVerityDir, { recursive: true, force: true })
|
|
23040
24957
|
});
|
|
23041
24958
|
}
|
|
23042
24959
|
if (actions.length === 0) {
|
|
@@ -23056,7 +24973,7 @@ function registerUninstallCommand(program2) {
|
|
|
23056
24973
|
if (!purgeGlobal) {
|
|
23057
24974
|
printInfo('Saved tokens at ~/.verity/credentials are preserved \u2014 re-run "verity init" to reconnect.');
|
|
23058
24975
|
} else {
|
|
23059
|
-
printWarn(
|
|
24976
|
+
printWarn('Global credentials wiped \u2014 run "verity login" (or "verity init") to reconnect.');
|
|
23060
24977
|
}
|
|
23061
24978
|
});
|
|
23062
24979
|
}
|
|
@@ -23230,8 +25147,8 @@ function registerTaskCommands(program2) {
|
|
|
23230
25147
|
}
|
|
23231
25148
|
|
|
23232
25149
|
// src/commands/reset.ts
|
|
23233
|
-
var
|
|
23234
|
-
var
|
|
25150
|
+
var import_node_fs47 = require("node:fs");
|
|
25151
|
+
var import_node_path32 = require("node:path");
|
|
23235
25152
|
function registerResetCommand(program2) {
|
|
23236
25153
|
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) => {
|
|
23237
25154
|
const globals = program2.opts();
|
|
@@ -23268,11 +25185,11 @@ function registerResetCommand(program2) {
|
|
|
23268
25185
|
}
|
|
23269
25186
|
const cacheDir = projectPath(CACHE_DIR);
|
|
23270
25187
|
let purged = 0;
|
|
23271
|
-
if ((0,
|
|
23272
|
-
for (const entry of (0,
|
|
25188
|
+
if ((0, import_node_fs47.existsSync)(cacheDir)) {
|
|
25189
|
+
for (const entry of (0, import_node_fs47.readdirSync)(cacheDir)) {
|
|
23273
25190
|
if (entry.startsWith("pending-")) {
|
|
23274
25191
|
try {
|
|
23275
|
-
(0,
|
|
25192
|
+
(0, import_node_fs47.unlinkSync)((0, import_node_path32.join)(cacheDir, entry));
|
|
23276
25193
|
purged++;
|
|
23277
25194
|
} catch {
|
|
23278
25195
|
}
|
|
@@ -23287,19 +25204,19 @@ function registerResetCommand(program2) {
|
|
|
23287
25204
|
projectPath(`${VERITY_DIR}/.last-analysis`)
|
|
23288
25205
|
];
|
|
23289
25206
|
for (const file of filesToClear) {
|
|
23290
|
-
if ((0,
|
|
25207
|
+
if ((0, import_node_fs47.existsSync)(file)) {
|
|
23291
25208
|
try {
|
|
23292
|
-
(0,
|
|
25209
|
+
(0, import_node_fs47.writeFileSync)(file, "");
|
|
23293
25210
|
} catch {
|
|
23294
25211
|
}
|
|
23295
25212
|
}
|
|
23296
25213
|
}
|
|
23297
25214
|
if (opts.all) {
|
|
23298
25215
|
const logsDir = projectPath(`${VERITY_DIR}/.logs`);
|
|
23299
|
-
if ((0,
|
|
23300
|
-
for (const entry of (0,
|
|
25216
|
+
if ((0, import_node_fs47.existsSync)(logsDir)) {
|
|
25217
|
+
for (const entry of (0, import_node_fs47.readdirSync)(logsDir)) {
|
|
23301
25218
|
try {
|
|
23302
|
-
(0,
|
|
25219
|
+
(0, import_node_fs47.unlinkSync)((0, import_node_path32.join)(logsDir, entry));
|
|
23303
25220
|
} catch {
|
|
23304
25221
|
}
|
|
23305
25222
|
}
|
|
@@ -23607,8 +25524,8 @@ function registerTelemetryCommands(program2) {
|
|
|
23607
25524
|
}
|
|
23608
25525
|
|
|
23609
25526
|
// src/cli.ts
|
|
23610
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.31.1-experimental.
|
|
23611
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.1-experimental.
|
|
25527
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.31.1-experimental.b9b3862").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) => {
|
|
25528
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.1-experimental.b9b3862");
|
|
23612
25529
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
23613
25530
|
try {
|
|
23614
25531
|
await foldLegacyLocalCredential();
|
|
@@ -23634,6 +25551,7 @@ registerGuardCommand(program);
|
|
|
23634
25551
|
registerIgnoreCommand(program);
|
|
23635
25552
|
registerWaiveCommand(program);
|
|
23636
25553
|
registerInitCommand(program);
|
|
25554
|
+
registerDoctorCommand(program);
|
|
23637
25555
|
registerUninstallCommand(program);
|
|
23638
25556
|
registerTaskCommands(program);
|
|
23639
25557
|
registerResetCommand(program);
|