@codacy/verity-cli 0.35.0-experimental.3a13a5e → 0.35.0-experimental.7c5e75b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/verity.js +1383 -1669
- package/data/skills/verity-init/SKILL.md +5 -39
- package/package.json +1 -1
package/bin/verity.js
CHANGED
|
@@ -23881,7 +23881,7 @@ async function readHookStdin() {
|
|
|
23881
23881
|
|
|
23882
23882
|
// src/commands/standard.ts
|
|
23883
23883
|
var import_promises10 = require("node:fs/promises");
|
|
23884
|
-
var
|
|
23884
|
+
var import_yaml5 = __toESM2(require_dist());
|
|
23885
23885
|
|
|
23886
23886
|
// src/lib/synthesize.ts
|
|
23887
23887
|
var import_node_fs35 = require("node:fs");
|
|
@@ -24869,29 +24869,8 @@ function consentLines(p) {
|
|
|
24869
24869
|
];
|
|
24870
24870
|
}
|
|
24871
24871
|
|
|
24872
|
-
// src/commands/standard-generate.ts
|
|
24873
|
-
var import_yaml5 = __toESM2(require_dist());
|
|
24874
|
-
|
|
24875
24872
|
// src/lib/generate-standard.ts
|
|
24876
24873
|
var import_yaml4 = __toESM2(require_dist());
|
|
24877
|
-
async function generationStatus(opts) {
|
|
24878
|
-
const res = await apiRequest({
|
|
24879
|
-
method: "GET",
|
|
24880
|
-
path: "/standards/generate",
|
|
24881
|
-
serviceUrl: opts.serviceUrl,
|
|
24882
|
-
token: opts.token,
|
|
24883
|
-
verbose: opts.verbose,
|
|
24884
|
-
cmd: "standard_generate_status"
|
|
24885
|
-
});
|
|
24886
|
-
if (!res.ok) {
|
|
24887
|
-
if (res.http_status === 404 && /Unknown route/.test(res.error)) return "unsupported";
|
|
24888
|
-
if (res.http_status === 400 && /Version must be a positive integer/.test(res.error)) return "unsupported";
|
|
24889
|
-
return "unknown";
|
|
24890
|
-
}
|
|
24891
|
-
if (res.data.enabled === true) return "on";
|
|
24892
|
-
if (res.data.enabled === false) return "off";
|
|
24893
|
-
return "unknown";
|
|
24894
|
-
}
|
|
24895
24874
|
var GENERATE_TIMEOUT_MS = 6e4;
|
|
24896
24875
|
function rulesFrom(content, before) {
|
|
24897
24876
|
const list2 = Array.isArray(content.custom_patterns) ? content.custom_patterns : [];
|
|
@@ -24990,94 +24969,6 @@ async function promptYes(question, opts) {
|
|
|
24990
24969
|
return a === "" || a === "y" || a === "yes";
|
|
24991
24970
|
}
|
|
24992
24971
|
|
|
24993
|
-
// src/commands/standard-generate.ts
|
|
24994
|
-
function generationBase() {
|
|
24995
|
-
const local = readRepoFile(STANDARD_FILE);
|
|
24996
|
-
try {
|
|
24997
|
-
const parsed = local !== null ? (0, import_yaml5.parse)(local) : null;
|
|
24998
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : buildStandard({ detected: detectProject() }, loadCatalog());
|
|
24999
|
-
} catch (err2) {
|
|
25000
|
-
printError(`${STANDARD_FILE} is not valid YAML: ${err2.message}. Fix it, or delete it, and run this again.`);
|
|
25001
|
-
process.exit(1);
|
|
25002
|
-
}
|
|
25003
|
-
}
|
|
25004
|
-
async function consentToSend(profile, yes) {
|
|
25005
|
-
try {
|
|
25006
|
-
writeRepoFile(PROFILE_FILE, JSON.stringify(profile, null, 2) + "\n");
|
|
25007
|
-
} catch (err2) {
|
|
25008
|
-
printError(`Could not write ${PROFILE_FILE} for you to read: ${err2.message}`);
|
|
25009
|
-
process.exit(1);
|
|
25010
|
-
}
|
|
25011
|
-
for (const line2 of consentLines(profile)) printInfo(line2);
|
|
25012
|
-
if (yes) return true;
|
|
25013
|
-
if (!interactive()) {
|
|
25014
|
-
printError(`Nothing was sent. Read ${PROFILE_FILE}, then run this again with --yes to send it.`);
|
|
25015
|
-
process.exit(1);
|
|
25016
|
-
}
|
|
25017
|
-
const send = await promptYes("Send the profile? [Y/n] ", { nonInteractive: false });
|
|
25018
|
-
if (!send) printInfo("Not sent. Nothing changed.");
|
|
25019
|
-
return send;
|
|
25020
|
-
}
|
|
25021
|
-
async function runGenerate(globals, opts) {
|
|
25022
|
-
const tokenResult = await resolveToken(globals.token);
|
|
25023
|
-
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
25024
|
-
if (!tokenResult.ok || !urlResult.ok) {
|
|
25025
|
-
printError("Sign in first: verity login. Rule generation needs your Verity account.");
|
|
25026
|
-
process.exit(1);
|
|
25027
|
-
}
|
|
25028
|
-
const status = await generationStatus({ serviceUrl: urlResult.data, token: tokenResult.data.token, verbose: globals.verbose });
|
|
25029
|
-
if (opts.status) {
|
|
25030
|
-
process.stdout.write(`${status}
|
|
25031
|
-
`);
|
|
25032
|
-
return;
|
|
25033
|
-
}
|
|
25034
|
-
if (status === "off") {
|
|
25035
|
-
printWarn("Rule generation is not switched on for this project yet. The built-in rules apply. Nothing was sent.");
|
|
25036
|
-
return;
|
|
25037
|
-
}
|
|
25038
|
-
if (status === "unsupported") {
|
|
25039
|
-
printWarn("This Verity service does not write project rules yet: it is older than this CLI. The built-in rules apply. Nothing was sent.");
|
|
25040
|
-
return;
|
|
25041
|
-
}
|
|
25042
|
-
const base = generationBase();
|
|
25043
|
-
const profile = buildRepoProfile();
|
|
25044
|
-
if (!await consentToSend(profile, opts.yes === true)) return;
|
|
25045
|
-
const spinner = startSpinner("Writing rules for this repository");
|
|
25046
|
-
const outcome = await generateStandard({
|
|
25047
|
-
serviceUrl: urlResult.data,
|
|
25048
|
-
token: tokenResult.data.token,
|
|
25049
|
-
profile,
|
|
25050
|
-
base,
|
|
25051
|
-
verbose: globals.verbose
|
|
25052
|
-
});
|
|
25053
|
-
if (outcome.status === "created") {
|
|
25054
|
-
spinner.succeed(`${outcome.rules.length} ${outcome.rules.length === 1 ? "rule" : "rules"} for this repository \xB7 Standard v${outcome.version}`);
|
|
25055
|
-
for (const r of outcome.rules) {
|
|
25056
|
-
printInfo(` ${r.id}`);
|
|
25057
|
-
printInfo(` ${r.description}`);
|
|
25058
|
-
printInfo(` Applies: ${r.scope} \xB7 ${r.basis}`);
|
|
25059
|
-
}
|
|
25060
|
-
if (outcome.rejected > 0) printInfo(` ${outcome.rejected} proposed rule(s) were left out: too generic, or not safe to use.`);
|
|
25061
|
-
printInfo(`Edit them in ${STANDARD_FILE}, then: verity standard push`);
|
|
25062
|
-
if (outcome.warning) printWarn(outcome.warning);
|
|
25063
|
-
return;
|
|
25064
|
-
}
|
|
25065
|
-
if (outcome.status === "exists") {
|
|
25066
|
-
spinner.warn('This project already has a Standard on the Verity service. Run "verity init" to use it.');
|
|
25067
|
-
return;
|
|
25068
|
-
}
|
|
25069
|
-
if (outcome.status === "disabled") {
|
|
25070
|
-
spinner.warn("Rule generation is not switched on for this project yet. The built-in rules apply.");
|
|
25071
|
-
return;
|
|
25072
|
-
}
|
|
25073
|
-
if (outcome.status === "unsupported") {
|
|
25074
|
-
spinner.warn("This Verity service does not write project rules yet: it is older than this CLI. The built-in rules apply.");
|
|
25075
|
-
return;
|
|
25076
|
-
}
|
|
25077
|
-
spinner.warn(`The rules could not be written: ${outcome.reason}`);
|
|
25078
|
-
process.exit(1);
|
|
25079
|
-
}
|
|
25080
|
-
|
|
25081
24972
|
// src/commands/standard.ts
|
|
25082
24973
|
function registerStandardCommands(program2) {
|
|
25083
24974
|
const standard = program2.command("standard").description("Manage the project Standard");
|
|
@@ -25094,15 +24985,15 @@ function registerStandardCommands(program2) {
|
|
|
25094
24985
|
serviceUrl: urlResult.ok ? urlResult.data : void 0
|
|
25095
24986
|
});
|
|
25096
24987
|
if ("refused" in result) {
|
|
25097
|
-
printWarn(
|
|
24988
|
+
printWarn(result.refused);
|
|
25098
24989
|
process.exit(1);
|
|
25099
24990
|
}
|
|
25100
|
-
printInfo(` ${
|
|
25101
|
-
for (const path of result.written) printInfo(` ${
|
|
25102
|
-
for (const note of result.notes) printWarn(` ${
|
|
24991
|
+
printInfo(` ${describeDetected(result.detected)}`);
|
|
24992
|
+
for (const path of result.written) printInfo(` ${path} \u2713`);
|
|
24993
|
+
for (const note of result.notes) printWarn(` ${note}`);
|
|
25103
24994
|
if (opts.push) {
|
|
25104
24995
|
const pushed = await pushStandard(globals);
|
|
25105
|
-
for (const line2 of pushed.lines) printInfo(` ${
|
|
24996
|
+
for (const line2 of pushed.lines) printInfo(` ${line2}`);
|
|
25106
24997
|
if (pushed.standardVersion !== null) {
|
|
25107
24998
|
await correctVerityMdVersion({
|
|
25108
24999
|
standard: result.standard,
|
|
@@ -25113,7 +25004,7 @@ function registerStandardCommands(program2) {
|
|
|
25113
25004
|
}
|
|
25114
25005
|
}
|
|
25115
25006
|
});
|
|
25116
|
-
standard.command("generate").description("Have Verity write project-specific rules from a profile of this repository (asks before sending it)").option("--yes", `Send the profile without asking (you have read ${PROFILE_FILE})`).
|
|
25007
|
+
standard.command("generate").description("Have Verity write project-specific rules from a profile of this repository (asks before sending it)").option("--yes", `Send the profile without asking (you have read ${PROFILE_FILE})`).action((opts) => runGenerate(program2.opts(), opts));
|
|
25117
25008
|
standard.command("push").description("Upload the project Standard to the service").option("--file <path>", "Path to standard YAML file", STANDARD_FILE).option("--created-by <name>", "Who created this version", "claude-code").action(async (opts) => {
|
|
25118
25009
|
const globals = program2.opts();
|
|
25119
25010
|
const tokenResult = await resolveToken(globals.token);
|
|
@@ -25135,7 +25026,7 @@ function registerStandardCommands(program2) {
|
|
|
25135
25026
|
}
|
|
25136
25027
|
let content;
|
|
25137
25028
|
try {
|
|
25138
|
-
content = (0,
|
|
25029
|
+
content = (0, import_yaml5.parse)(yamlContent);
|
|
25139
25030
|
} catch (err2) {
|
|
25140
25031
|
printError(`Invalid YAML in ${opts.file}: ${err2.message}`);
|
|
25141
25032
|
process.exit(1);
|
|
@@ -25151,10 +25042,10 @@ function registerStandardCommands(program2) {
|
|
|
25151
25042
|
verbose: globals.verbose
|
|
25152
25043
|
});
|
|
25153
25044
|
if (!result.ok) {
|
|
25154
|
-
printError(
|
|
25045
|
+
printError(result.error);
|
|
25155
25046
|
process.exit(1);
|
|
25156
25047
|
}
|
|
25157
|
-
printInfo(`Standard uploaded (version ${
|
|
25048
|
+
printInfo(`Standard uploaded (version ${result.data.version})`);
|
|
25158
25049
|
printJson(result.data);
|
|
25159
25050
|
});
|
|
25160
25051
|
standard.command("get").description("Get the current Standard from the service").option("--version <n>", "Specific version number").action(async (opts) => {
|
|
@@ -25169,15 +25060,7 @@ function registerStandardCommands(program2) {
|
|
|
25169
25060
|
printError(urlResult.error);
|
|
25170
25061
|
process.exit(1);
|
|
25171
25062
|
}
|
|
25172
|
-
|
|
25173
|
-
if (opts.version !== void 0) {
|
|
25174
|
-
const version = Number(opts.version);
|
|
25175
|
-
if (!/^[1-9][0-9]*$/.test(String(opts.version)) || !Number.isSafeInteger(version)) {
|
|
25176
|
-
printError(`--version: "${terminalText(opts.version, 40)}" is not a Standard version. Use a positive whole number, such as --version 3.`);
|
|
25177
|
-
process.exit(1);
|
|
25178
|
-
}
|
|
25179
|
-
path = `/standards/${version}`;
|
|
25180
|
-
}
|
|
25063
|
+
const path = opts.version ? `/standards/${opts.version}` : "/standards/latest";
|
|
25181
25064
|
const result = await apiRequest({
|
|
25182
25065
|
method: "GET",
|
|
25183
25066
|
path,
|
|
@@ -25186,7 +25069,7 @@ function registerStandardCommands(program2) {
|
|
|
25186
25069
|
verbose: globals.verbose
|
|
25187
25070
|
});
|
|
25188
25071
|
if (!result.ok) {
|
|
25189
|
-
printError(
|
|
25072
|
+
printError(result.error);
|
|
25190
25073
|
process.exit(1);
|
|
25191
25074
|
}
|
|
25192
25075
|
printJson(result.data);
|
|
@@ -25211,24 +25094,93 @@ function registerStandardCommands(program2) {
|
|
|
25211
25094
|
verbose: globals.verbose
|
|
25212
25095
|
});
|
|
25213
25096
|
if (!result.ok) {
|
|
25214
|
-
printError(
|
|
25097
|
+
printError(result.error);
|
|
25215
25098
|
process.exit(1);
|
|
25216
25099
|
}
|
|
25217
|
-
const versions =
|
|
25100
|
+
const versions = result.data.versions;
|
|
25218
25101
|
if (versions.length === 0) {
|
|
25219
25102
|
printInfo("No Standard versions found.");
|
|
25220
25103
|
return;
|
|
25221
25104
|
}
|
|
25222
25105
|
printInfo("Standard versions:");
|
|
25223
25106
|
for (const v of versions) {
|
|
25224
|
-
const date =
|
|
25107
|
+
const date = v.created_at.slice(0, 19).replace("T", " ");
|
|
25225
25108
|
const by = v.created_by ?? "unknown";
|
|
25226
|
-
printInfo(` v${
|
|
25109
|
+
printInfo(` v${v.version} ${date} (by ${by})`);
|
|
25227
25110
|
}
|
|
25228
25111
|
});
|
|
25229
25112
|
}
|
|
25230
|
-
function
|
|
25231
|
-
|
|
25113
|
+
function generationBase() {
|
|
25114
|
+
const local = readRepoFile(STANDARD_FILE);
|
|
25115
|
+
try {
|
|
25116
|
+
const parsed = local !== null ? (0, import_yaml5.parse)(local) : null;
|
|
25117
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : buildStandard({ detected: detectProject() }, loadCatalog());
|
|
25118
|
+
} catch (err2) {
|
|
25119
|
+
printError(`${STANDARD_FILE} is not valid YAML: ${err2.message}. Fix it, or delete it, and run this again.`);
|
|
25120
|
+
process.exit(1);
|
|
25121
|
+
}
|
|
25122
|
+
}
|
|
25123
|
+
async function consentToSend(profile, yes) {
|
|
25124
|
+
try {
|
|
25125
|
+
writeRepoFile(PROFILE_FILE, JSON.stringify(profile, null, 2) + "\n");
|
|
25126
|
+
} catch (err2) {
|
|
25127
|
+
printError(`Could not write ${PROFILE_FILE} for you to read: ${err2.message}`);
|
|
25128
|
+
process.exit(1);
|
|
25129
|
+
}
|
|
25130
|
+
for (const line2 of consentLines(profile)) printInfo(line2);
|
|
25131
|
+
if (yes) return true;
|
|
25132
|
+
if (!interactive()) {
|
|
25133
|
+
printError(`Nothing was sent. Read ${PROFILE_FILE}, then run this again with --yes to send it.`);
|
|
25134
|
+
process.exit(1);
|
|
25135
|
+
}
|
|
25136
|
+
const send = await promptYes("Send the profile? [Y/n] ", { nonInteractive: false });
|
|
25137
|
+
if (!send) printInfo("Not sent. Nothing changed.");
|
|
25138
|
+
return send;
|
|
25139
|
+
}
|
|
25140
|
+
async function runGenerate(globals, opts) {
|
|
25141
|
+
const tokenResult = await resolveToken(globals.token);
|
|
25142
|
+
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
25143
|
+
if (!tokenResult.ok || !urlResult.ok) {
|
|
25144
|
+
printError("Sign in first: verity login. Rule generation needs your Verity account.");
|
|
25145
|
+
process.exit(1);
|
|
25146
|
+
}
|
|
25147
|
+
const base = generationBase();
|
|
25148
|
+
const profile = buildRepoProfile();
|
|
25149
|
+
if (!await consentToSend(profile, opts.yes === true)) return;
|
|
25150
|
+
const spinner = startSpinner("Writing rules for this repository");
|
|
25151
|
+
const outcome = await generateStandard({
|
|
25152
|
+
serviceUrl: urlResult.data,
|
|
25153
|
+
token: tokenResult.data.token,
|
|
25154
|
+
profile,
|
|
25155
|
+
base,
|
|
25156
|
+
verbose: globals.verbose
|
|
25157
|
+
});
|
|
25158
|
+
if (outcome.status === "created") {
|
|
25159
|
+
spinner.succeed(`${outcome.rules.length} ${outcome.rules.length === 1 ? "rule" : "rules"} for this repository \xB7 Standard v${outcome.version}`);
|
|
25160
|
+
for (const r of outcome.rules) {
|
|
25161
|
+
printInfo(` ${r.id}`);
|
|
25162
|
+
printInfo(` ${r.description}`);
|
|
25163
|
+
printInfo(` Applies: ${r.scope} \xB7 ${r.basis}`);
|
|
25164
|
+
}
|
|
25165
|
+
if (outcome.rejected > 0) printInfo(` ${outcome.rejected} proposed rule(s) were left out: too generic, or not safe to use.`);
|
|
25166
|
+
printInfo(`Edit them in ${STANDARD_FILE}, then: verity standard push`);
|
|
25167
|
+
if (outcome.warning) printWarn(outcome.warning);
|
|
25168
|
+
return;
|
|
25169
|
+
}
|
|
25170
|
+
if (outcome.status === "exists") {
|
|
25171
|
+
spinner.warn('This project already has a Standard on the Verity service. Run "verity init" to use it.');
|
|
25172
|
+
return;
|
|
25173
|
+
}
|
|
25174
|
+
if (outcome.status === "disabled") {
|
|
25175
|
+
spinner.warn("Rule generation is not switched on for this project yet. The built-in rules apply.");
|
|
25176
|
+
return;
|
|
25177
|
+
}
|
|
25178
|
+
if (outcome.status === "unsupported") {
|
|
25179
|
+
spinner.warn("This Verity service does not write project rules yet: it is older than this CLI. The built-in rules apply.");
|
|
25180
|
+
return;
|
|
25181
|
+
}
|
|
25182
|
+
spinner.warn(`The rules could not be written: ${outcome.reason}`);
|
|
25183
|
+
process.exit(1);
|
|
25232
25184
|
}
|
|
25233
25185
|
|
|
25234
25186
|
// src/commands/config.ts
|
|
@@ -28366,7 +28318,7 @@ function channelSilence(input) {
|
|
|
28366
28318
|
// src/lib/cli-version.ts
|
|
28367
28319
|
function cliVersion() {
|
|
28368
28320
|
try {
|
|
28369
|
-
return true ? "0.35.0-experimental.
|
|
28321
|
+
return true ? "0.35.0-experimental.7c5e75b" : "dev";
|
|
28370
28322
|
} catch {
|
|
28371
28323
|
return "dev";
|
|
28372
28324
|
}
|
|
@@ -32425,7 +32377,7 @@ async function repoContext(run4) {
|
|
|
32425
32377
|
var import_promises13 = require("node:fs/promises");
|
|
32426
32378
|
var import_node_fs50 = require("node:fs");
|
|
32427
32379
|
var import_node_path39 = require("node:path");
|
|
32428
|
-
var
|
|
32380
|
+
var import_yaml6 = __toESM2(require_dist());
|
|
32429
32381
|
|
|
32430
32382
|
// src/lib/seed.ts
|
|
32431
32383
|
function extractReadmeIntro(content) {
|
|
@@ -32736,7 +32688,7 @@ async function runSeed(opts) {
|
|
|
32736
32688
|
let standardDoc;
|
|
32737
32689
|
try {
|
|
32738
32690
|
const raw = await (0, import_promises13.readFile)(STANDARD_FILE, "utf-8");
|
|
32739
|
-
standardDoc = (0,
|
|
32691
|
+
standardDoc = (0, import_yaml6.parse)(raw);
|
|
32740
32692
|
} catch {
|
|
32741
32693
|
return { created: 0, existing: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
32742
32694
|
}
|
|
@@ -34218,11 +34170,11 @@ async function render(run4) {
|
|
|
34218
34170
|
const sev = String(f.severity ?? "").toLowerCase();
|
|
34219
34171
|
return sev === "critical" || sev === "high";
|
|
34220
34172
|
});
|
|
34221
|
-
const
|
|
34173
|
+
const fingerprint = findingsFingerprint(blocking);
|
|
34222
34174
|
const prior = readBlockState(currentCommit, {
|
|
34223
34175
|
newUserPrompt: (conversation?.prompts?.length ?? 0) > 0
|
|
34224
34176
|
});
|
|
34225
|
-
const sameProblem = isSameProblem(prior.fingerprint,
|
|
34177
|
+
const sameProblem = isSameProblem(prior.fingerprint, fingerprint);
|
|
34226
34178
|
const maxIterations = parseInt(opts.maxIterations, 10);
|
|
34227
34179
|
const attempts = sameProblem ? prior.attempts + 1 : 1;
|
|
34228
34180
|
const blocks = prior.blocks + 1;
|
|
@@ -34235,7 +34187,7 @@ async function render(run4) {
|
|
|
34235
34187
|
maxIterations
|
|
34236
34188
|
});
|
|
34237
34189
|
if (decisionNow.block) {
|
|
34238
|
-
writeBlockState(currentCommit, { attempts, blocks, fingerprint
|
|
34190
|
+
writeBlockState(currentCommit, { attempts, blocks, fingerprint });
|
|
34239
34191
|
iteration = attempts;
|
|
34240
34192
|
} else {
|
|
34241
34193
|
release = decisionNow.release;
|
|
@@ -34246,7 +34198,7 @@ async function render(run4) {
|
|
|
34246
34198
|
blocks,
|
|
34247
34199
|
reviewed_files: codeDelta.files.length,
|
|
34248
34200
|
cycle_cut: silenced,
|
|
34249
|
-
fingerprint
|
|
34201
|
+
fingerprint
|
|
34250
34202
|
});
|
|
34251
34203
|
}
|
|
34252
34204
|
}
|
|
@@ -36318,7 +36270,7 @@ async function runAgentReview(options) {
|
|
|
36318
36270
|
// src/lib/effort.ts
|
|
36319
36271
|
var import_node_fs66 = require("node:fs");
|
|
36320
36272
|
var import_node_path54 = require("node:path");
|
|
36321
|
-
var
|
|
36273
|
+
var import_yaml7 = __toESM2(require_dist());
|
|
36322
36274
|
var EFFORT_PROFILES = {
|
|
36323
36275
|
default: { pushDeadlineMs: 18e4, formal: { tier: "fast", maxRounds: 2, minScore: 0.7, costCapUsd: 1, maxTargets: 1, relaxedGates: false, dailyModels: 5, dailyCostUsd: 5 } },
|
|
36324
36276
|
high: { pushDeadlineMs: 22e4, formal: { tier: "deep", maxRounds: 2, minScore: 0.6, costCapUsd: 2, maxTargets: 2, relaxedGates: false, dailyModels: 15, dailyCostUsd: 15 } },
|
|
@@ -36330,7 +36282,7 @@ function asEffort(value) {
|
|
|
36330
36282
|
function resolveEffort(root, env = process.env) {
|
|
36331
36283
|
if (env.VERITY_EFFORT) return asEffort(env.VERITY_EFFORT);
|
|
36332
36284
|
try {
|
|
36333
|
-
const standard = (0,
|
|
36285
|
+
const standard = (0, import_yaml7.parse)((0, import_node_fs66.readFileSync)((0, import_node_path54.join)(root, STANDARD_FILE), "utf8"));
|
|
36334
36286
|
return asEffort(standard?.process_constraints?.git_moment_effort);
|
|
36335
36287
|
} catch {
|
|
36336
36288
|
return "default";
|
|
@@ -38036,8 +37988,8 @@ function readVerified(root, now = Date.now()) {
|
|
|
38036
37988
|
return [];
|
|
38037
37989
|
}
|
|
38038
37990
|
}
|
|
38039
|
-
function findVerified(root, file, symbol,
|
|
38040
|
-
return readVerified(root, now).find((r) => r.file === file && r.symbol === symbol && r.fingerprint ===
|
|
37991
|
+
function findVerified(root, file, symbol, fingerprint, now = Date.now()) {
|
|
37992
|
+
return readVerified(root, now).find((r) => r.file === file && r.symbol === symbol && r.fingerprint === fingerprint) ?? null;
|
|
38041
37993
|
}
|
|
38042
37994
|
function recordVerified(root, rec, now = Date.now()) {
|
|
38043
37995
|
const others = readVerified(root, now).filter((r) => !(r.file === rec.file && r.symbol === rec.symbol));
|
|
@@ -39970,99 +39922,14 @@ function registerWaiveCommand(program2) {
|
|
|
39970
39922
|
}
|
|
39971
39923
|
|
|
39972
39924
|
// src/commands/init/index.ts
|
|
39973
|
-
var
|
|
39925
|
+
var import_node_fs76 = require("node:fs");
|
|
39974
39926
|
var import_promises19 = require("node:fs/promises");
|
|
39975
|
-
var
|
|
39927
|
+
var import_node_path64 = require("node:path");
|
|
39976
39928
|
|
|
39977
|
-
// src/lib/init-
|
|
39929
|
+
// src/lib/init-trace.ts
|
|
39978
39930
|
var import_node_crypto23 = require("node:crypto");
|
|
39979
39931
|
var import_node_fs69 = require("node:fs");
|
|
39980
|
-
var import_node_os12 = require("node:os");
|
|
39981
39932
|
var import_node_path57 = require("node:path");
|
|
39982
|
-
var REPO_FILES = [
|
|
39983
|
-
".gitignore",
|
|
39984
|
-
"CLAUDE.md",
|
|
39985
|
-
".claude/settings.json",
|
|
39986
|
-
".claude/settings.local.json",
|
|
39987
|
-
STANDARD_FILE,
|
|
39988
|
-
VERITY_MD_FILE,
|
|
39989
|
-
CODACY_CONFIG_FILE,
|
|
39990
|
-
".codacy/tools-configs/eslint.config.mjs"
|
|
39991
|
-
];
|
|
39992
|
-
var SKILLS_DIR = ".claude/skills";
|
|
39993
|
-
var MAX_HASH_BYTES = 2 * 1024 * 1024;
|
|
39994
|
-
var MAX_TREE_ENTRIES = 2e3;
|
|
39995
|
-
function userSettingsDir(env) {
|
|
39996
|
-
return env.CLAUDE_CONFIG_DIR || (0, import_node_path57.join)(env.HOME || (0, import_node_os12.homedir)(), ".claude");
|
|
39997
|
-
}
|
|
39998
|
-
function fingerprint(root, rel) {
|
|
39999
|
-
const opened = openRegularInRoot(root, rel);
|
|
40000
|
-
if (!opened.ok) {
|
|
40001
|
-
if (opened.reason === "missing") return null;
|
|
40002
|
-
if (opened.reason === "symlink") {
|
|
40003
|
-
try {
|
|
40004
|
-
return `link:${(0, import_node_fs69.readlinkSync)((0, import_node_path57.join)(root, rel))}`;
|
|
40005
|
-
} catch {
|
|
40006
|
-
return "link";
|
|
40007
|
-
}
|
|
40008
|
-
}
|
|
40009
|
-
return `refused:${opened.reason}`;
|
|
40010
|
-
}
|
|
40011
|
-
if (opened.size > MAX_HASH_BYTES) {
|
|
40012
|
-
closeOpened(opened.fd);
|
|
40013
|
-
return `size:${opened.size}`;
|
|
40014
|
-
}
|
|
40015
|
-
try {
|
|
40016
|
-
return (0, import_node_crypto23.createHash)("sha256").update(readOpened(opened.fd, opened.size)).digest("hex");
|
|
40017
|
-
} catch {
|
|
40018
|
-
return "unreadable";
|
|
40019
|
-
}
|
|
40020
|
-
}
|
|
40021
|
-
function treeFingerprint(root, dirRel) {
|
|
40022
|
-
try {
|
|
40023
|
-
if (!(0, import_node_fs69.lstatSync)((0, import_node_path57.join)(root, dirRel)).isDirectory()) return fingerprint(root, dirRel);
|
|
40024
|
-
} catch {
|
|
40025
|
-
return null;
|
|
40026
|
-
}
|
|
40027
|
-
const parts2 = [];
|
|
40028
|
-
const walk2 = (rel) => {
|
|
40029
|
-
let names2;
|
|
40030
|
-
try {
|
|
40031
|
-
names2 = (0, import_node_fs69.readdirSync)((0, import_node_path57.join)(root, rel)).sort();
|
|
40032
|
-
} catch {
|
|
40033
|
-
return;
|
|
40034
|
-
}
|
|
40035
|
-
for (const name2 of names2) {
|
|
40036
|
-
if (parts2.length >= MAX_TREE_ENTRIES) return;
|
|
40037
|
-
const relPath = `${rel}/${name2}`;
|
|
40038
|
-
let st;
|
|
40039
|
-
try {
|
|
40040
|
-
st = (0, import_node_fs69.lstatSync)((0, import_node_path57.join)(root, relPath));
|
|
40041
|
-
} catch {
|
|
40042
|
-
continue;
|
|
40043
|
-
}
|
|
40044
|
-
if (st.isDirectory()) walk2(relPath);
|
|
40045
|
-
else parts2.push(`${relPath}\0${fingerprint(root, relPath)}`);
|
|
40046
|
-
}
|
|
40047
|
-
};
|
|
40048
|
-
walk2(dirRel);
|
|
40049
|
-
return (0, import_node_crypto23.createHash)("sha256").update(parts2.join("\n")).digest("hex");
|
|
40050
|
-
}
|
|
40051
|
-
function snapshotInitFiles(root = process.cwd(), env = process.env) {
|
|
40052
|
-
const snap = /* @__PURE__ */ new Map();
|
|
40053
|
-
for (const f of REPO_FILES) snap.set(f, fingerprint(root, f));
|
|
40054
|
-
snap.set(`${SKILLS_DIR}/`, treeFingerprint(root, SKILLS_DIR));
|
|
40055
|
-
snap.set("~/.claude/settings.json", fingerprint(userSettingsDir(env), "settings.json"));
|
|
40056
|
-
return snap;
|
|
40057
|
-
}
|
|
40058
|
-
function changedSince(before, after) {
|
|
40059
|
-
return [...after.keys()].filter((k) => before.get(k) !== after.get(k));
|
|
40060
|
-
}
|
|
40061
|
-
|
|
40062
|
-
// src/lib/init-trace.ts
|
|
40063
|
-
var import_node_crypto24 = require("node:crypto");
|
|
40064
|
-
var import_node_fs70 = require("node:fs");
|
|
40065
|
-
var import_node_path58 = require("node:path");
|
|
40066
39933
|
var INIT_STEPS = [
|
|
40067
39934
|
"started",
|
|
40068
39935
|
"prerequisites",
|
|
@@ -40108,9 +39975,9 @@ function traceDisabled(env = process.env) {
|
|
|
40108
39975
|
}
|
|
40109
39976
|
var PENDING_NAME = "init-trace.json";
|
|
40110
39977
|
var MAX_PENDING_BYTES = 64 * 1024;
|
|
40111
|
-
var verityHome2 = () => (0,
|
|
39978
|
+
var verityHome2 = () => (0, import_node_path57.join)(process.env.HOME ?? "", ".verity");
|
|
40112
39979
|
function pendingTracePath() {
|
|
40113
|
-
return (0,
|
|
39980
|
+
return (0, import_node_path57.join)(verityHome2(), PENDING_NAME);
|
|
40114
39981
|
}
|
|
40115
39982
|
var RUN_ID = /^[0-9a-f]{16}$/;
|
|
40116
39983
|
var CLI_VERSION = /^[0-9A-Za-z.+-]{1,40}$/;
|
|
@@ -40168,10 +40035,10 @@ function writePending(runs) {
|
|
|
40168
40035
|
try {
|
|
40169
40036
|
const kept = runs.filter((r) => r.events.length > 0).slice(-MAX_PENDING_RUNS);
|
|
40170
40037
|
if (kept.length === 0) {
|
|
40171
|
-
(0,
|
|
40038
|
+
(0, import_node_fs69.rmSync)(pendingTracePath(), { force: true });
|
|
40172
40039
|
return;
|
|
40173
40040
|
}
|
|
40174
|
-
(0,
|
|
40041
|
+
(0, import_node_fs69.mkdirSync)(verityHome2(), { recursive: true });
|
|
40175
40042
|
if (!replaceInRoot(verityHome2(), PENDING_NAME, JSON.stringify(kept))) {
|
|
40176
40043
|
logEvent("init_trace_save_refused", {});
|
|
40177
40044
|
}
|
|
@@ -40214,7 +40081,7 @@ function startInitTrace(opts) {
|
|
|
40214
40081
|
if (traceDisabled()) return NOOP;
|
|
40215
40082
|
const startedAt = Date.now();
|
|
40216
40083
|
const current = {
|
|
40217
|
-
run_id: (0,
|
|
40084
|
+
run_id: (0, import_node_crypto23.randomBytes)(8).toString("hex"),
|
|
40218
40085
|
mode: opts.mode,
|
|
40219
40086
|
interactive: opts.interactive,
|
|
40220
40087
|
cli_version: cliVersion(),
|
|
@@ -40864,194 +40731,32 @@ var Rail = class {
|
|
|
40864
40731
|
}
|
|
40865
40732
|
};
|
|
40866
40733
|
|
|
40867
|
-
// src/commands/
|
|
40734
|
+
// src/commands/migrate.ts
|
|
40868
40735
|
var import_node_fs71 = require("node:fs");
|
|
40736
|
+
var import_node_path61 = require("node:path");
|
|
40737
|
+
var import_node_child_process19 = require("node:child_process");
|
|
40738
|
+
|
|
40739
|
+
// src/lib/telemetry-local.ts
|
|
40869
40740
|
var import_node_path59 = require("node:path");
|
|
40870
|
-
|
|
40871
|
-
|
|
40872
|
-
|
|
40873
|
-
|
|
40874
|
-
|
|
40875
|
-
|
|
40876
|
-
|
|
40741
|
+
|
|
40742
|
+
// src/lib/telemetry-settings.ts
|
|
40743
|
+
var import_promises16 = require("node:fs/promises");
|
|
40744
|
+
var import_node_os12 = require("node:os");
|
|
40745
|
+
var import_node_path58 = require("node:path");
|
|
40746
|
+
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
40747
|
+
var OTEL_HEADERS_HELPER_CMD = "verity telemetry headers";
|
|
40748
|
+
var LEGACY_HEADER_KEY = "OTEL_EXPORTER_OTLP_HEADERS";
|
|
40749
|
+
var OTLP_ENDPOINT_KEYS = [
|
|
40750
|
+
"OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
40751
|
+
"OTEL_EXPORTER_OTLP_METRICS_ENDPOINT",
|
|
40752
|
+
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
|
|
40753
|
+
"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"
|
|
40754
|
+
];
|
|
40755
|
+
function claudeConfigDir2() {
|
|
40756
|
+
return process.env.CLAUDE_CONFIG_DIR || (0, import_node_path58.join)((0, import_node_os12.homedir)(), ".claude");
|
|
40877
40757
|
}
|
|
40878
|
-
function
|
|
40879
|
-
|
|
40880
|
-
ui.rail.stopped();
|
|
40881
|
-
ui.screen.release();
|
|
40882
|
-
process.exit(130);
|
|
40883
|
-
};
|
|
40884
|
-
ui.screen.startInput(stop2);
|
|
40885
|
-
process.once("SIGINT", stop2);
|
|
40886
|
-
}
|
|
40887
|
-
var SEEN_FILE = ".init-seen";
|
|
40888
|
-
function firstInitOnMachine(home = process.env.HOME || (0, import_node_os13.homedir)()) {
|
|
40889
|
-
return !(0, import_node_fs71.existsSync)((0, import_node_path59.join)(home, ".verity", SEEN_FILE));
|
|
40890
|
-
}
|
|
40891
|
-
function markInitSeen(home = process.env.HOME || (0, import_node_os13.homedir)()) {
|
|
40892
|
-
replaceInRoot((0, import_node_path59.join)(home, ".verity"), SEEN_FILE, `${(/* @__PURE__ */ new Date()).toISOString()}
|
|
40893
|
-
`);
|
|
40894
|
-
}
|
|
40895
|
-
function repoLabel() {
|
|
40896
|
-
try {
|
|
40897
|
-
const url = (0, import_node_child_process18.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
40898
|
-
const parsed = parseRemote(url);
|
|
40899
|
-
if (parsed) return terminalText(`${parsed.owner}/${parsed.repo}`, 120);
|
|
40900
|
-
} catch {
|
|
40901
|
-
}
|
|
40902
|
-
return terminalText((0, import_node_path59.basename)(repoRoot()), 120);
|
|
40903
|
-
}
|
|
40904
|
-
|
|
40905
|
-
// src/lib/prereqs.ts
|
|
40906
|
-
var import_node_child_process19 = require("node:child_process");
|
|
40907
|
-
var import_node_fs72 = require("node:fs");
|
|
40908
|
-
var MIN_NODE_MAJOR = 20;
|
|
40909
|
-
function which(bin) {
|
|
40910
|
-
return whichSync(bin, { real: false });
|
|
40911
|
-
}
|
|
40912
|
-
function checkNode() {
|
|
40913
|
-
const version = process.version;
|
|
40914
|
-
const major = Number.parseInt(version.slice(1), 10);
|
|
40915
|
-
const ok = Number.isFinite(major) && major >= MIN_NODE_MAJOR;
|
|
40916
|
-
return {
|
|
40917
|
-
id: "node",
|
|
40918
|
-
label: "Node.js",
|
|
40919
|
-
status: ok ? "ok" : "missing",
|
|
40920
|
-
detail: version,
|
|
40921
|
-
remedy: ok ? void 0 : `Node.js ${MIN_NODE_MAJOR}+ required \u2014 update from https://nodejs.org`,
|
|
40922
|
-
required: true
|
|
40923
|
-
};
|
|
40924
|
-
}
|
|
40925
|
-
function checkGit() {
|
|
40926
|
-
let detail = "";
|
|
40927
|
-
try {
|
|
40928
|
-
detail = (0, import_node_child_process19.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
40929
|
-
} catch {
|
|
40930
|
-
return {
|
|
40931
|
-
id: "git",
|
|
40932
|
-
label: "git",
|
|
40933
|
-
status: "missing",
|
|
40934
|
-
detail: "not found",
|
|
40935
|
-
remedy: "Install git from https://git-scm.com",
|
|
40936
|
-
required: true
|
|
40937
|
-
};
|
|
40938
|
-
}
|
|
40939
|
-
return { id: "git", label: "git", status: "ok", detail, required: true };
|
|
40940
|
-
}
|
|
40941
|
-
function checkClaude() {
|
|
40942
|
-
const path = which("claude");
|
|
40943
|
-
return {
|
|
40944
|
-
id: "claude",
|
|
40945
|
-
label: "Claude Code",
|
|
40946
|
-
status: path ? "ok" : "warn",
|
|
40947
|
-
detail: path ?? "not found",
|
|
40948
|
-
remedy: path ? void 0 : "Hooks are wired but need Claude Code to fire \u2014 https://claude.com/claude-code",
|
|
40949
|
-
required: false
|
|
40950
|
-
};
|
|
40951
|
-
}
|
|
40952
|
-
var ANALYSIS_CLI_REMEDY = "Reinstall Verity, which includes it: npm install -g @codacy/verity-cli";
|
|
40953
|
-
function checkAnalysisCli(opts = {}) {
|
|
40954
|
-
const base = { id: "analysis-cli", label: "@codacy/analysis-cli", required: false };
|
|
40955
|
-
const analyzer = resolveAnalyzer(opts);
|
|
40956
|
-
const shown = (opts.onPathDisplay ?? (() => which("codacy-analysis")))();
|
|
40957
|
-
if (!analyzer) {
|
|
40958
|
-
return { ...base, status: "warn", detail: "not found", remedy: `${ANALYSIS_CLI_REMEDY} (static findings are unavailable until then)` };
|
|
40959
|
-
}
|
|
40960
|
-
if (analyzer.source === "path") {
|
|
40961
|
-
return { ...base, status: "ok", detail: `${shown ?? analyzer.path} (on PATH)`, source: "path" };
|
|
40962
|
-
}
|
|
40963
|
-
const unused = shown && !sameFile(shown, analyzer.path) ? shown : void 0;
|
|
40964
|
-
return {
|
|
40965
|
-
...base,
|
|
40966
|
-
status: "ok",
|
|
40967
|
-
detail: `${analyzer.version} (bundled with Verity)`,
|
|
40968
|
-
source: "bundled",
|
|
40969
|
-
version: analyzer.version,
|
|
40970
|
-
...unused && { unused }
|
|
40971
|
-
};
|
|
40972
|
-
}
|
|
40973
|
-
function sameFile(a, b) {
|
|
40974
|
-
try {
|
|
40975
|
-
return (0, import_node_fs72.realpathSync)(a) === (0, import_node_fs72.realpathSync)(b);
|
|
40976
|
-
} catch {
|
|
40977
|
-
return false;
|
|
40978
|
-
}
|
|
40979
|
-
}
|
|
40980
|
-
var INSTALL_TIMEOUT_MS = 12e4;
|
|
40981
|
-
function run3(command, args2, opts = {}) {
|
|
40982
|
-
return new Promise((resolve9) => {
|
|
40983
|
-
const child = (0, import_node_child_process19.spawn)(command, args2, {
|
|
40984
|
-
stdio: opts.inherit ? "inherit" : "pipe",
|
|
40985
|
-
timeout: INSTALL_TIMEOUT_MS
|
|
40986
|
-
});
|
|
40987
|
-
child.on("error", () => resolve9(false));
|
|
40988
|
-
child.on("close", (code) => resolve9(code === 0));
|
|
40989
|
-
});
|
|
40990
|
-
}
|
|
40991
|
-
async function installAnalysisCli(opts = {}) {
|
|
40992
|
-
const spinner = opts.quiet ? { succeed: () => {
|
|
40993
|
-
}, warn: () => {
|
|
40994
|
-
}, stop: () => {
|
|
40995
|
-
} } : startSpinner("Installing @codacy/analysis-cli");
|
|
40996
|
-
const ok = await run3("npm", ["install", "-g", "@codacy/analysis-cli"]);
|
|
40997
|
-
if (ok) {
|
|
40998
|
-
const check = checkAnalysisCli();
|
|
40999
|
-
if (check.status === "ok") {
|
|
41000
|
-
spinner.succeed("@codacy/analysis-cli installed");
|
|
41001
|
-
return { ...check, justInstalled: true };
|
|
41002
|
-
}
|
|
41003
|
-
spinner.warn("npm reported success but codacy-analysis is not on PATH");
|
|
41004
|
-
return check;
|
|
41005
|
-
}
|
|
41006
|
-
spinner.stop();
|
|
41007
|
-
{
|
|
41008
|
-
const failed = {
|
|
41009
|
-
id: "analysis-cli",
|
|
41010
|
-
label: "@codacy/analysis-cli",
|
|
41011
|
-
status: "warn",
|
|
41012
|
-
detail: "install failed",
|
|
41013
|
-
remedy: ANALYSIS_CLI_REMEDY,
|
|
41014
|
-
required: false
|
|
41015
|
-
};
|
|
41016
|
-
if (opts.sudo === false || !process.stdin.isTTY || !process.stdout.isTTY) return failed;
|
|
41017
|
-
process.stderr.write(" Retrying with sudo (you may be asked for your password)\u2026\n");
|
|
41018
|
-
const sudoOk = await run3("sudo", ["npm", "install", "-g", "@codacy/analysis-cli"], { inherit: true });
|
|
41019
|
-
if (!sudoOk) return failed;
|
|
41020
|
-
const check = checkAnalysisCli();
|
|
41021
|
-
return check.status === "ok" ? { ...check, justInstalled: true } : check;
|
|
41022
|
-
}
|
|
41023
|
-
}
|
|
41024
|
-
async function checkPrereqs(opts = {}) {
|
|
41025
|
-
const checks = [checkNode(), checkGit(), checkClaude()];
|
|
41026
|
-
let analysis = checkAnalysisCli();
|
|
41027
|
-
if (analysis.status !== "ok" && opts.install) {
|
|
41028
|
-
analysis = await installAnalysisCli();
|
|
41029
|
-
}
|
|
41030
|
-
checks.push(analysis);
|
|
41031
|
-
return { checks, blocked: checks.some((c) => c.required && c.status !== "ok") };
|
|
41032
|
-
}
|
|
41033
|
-
|
|
41034
|
-
// src/lib/telemetry-local.ts
|
|
41035
|
-
var import_node_path61 = require("node:path");
|
|
41036
|
-
|
|
41037
|
-
// src/lib/telemetry-settings.ts
|
|
41038
|
-
var import_promises16 = require("node:fs/promises");
|
|
41039
|
-
var import_node_os14 = require("node:os");
|
|
41040
|
-
var import_node_path60 = require("node:path");
|
|
41041
|
-
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
41042
|
-
var OTEL_HEADERS_HELPER_CMD = "verity telemetry headers";
|
|
41043
|
-
var LEGACY_HEADER_KEY = "OTEL_EXPORTER_OTLP_HEADERS";
|
|
41044
|
-
var OTLP_ENDPOINT_KEYS = [
|
|
41045
|
-
"OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
41046
|
-
"OTEL_EXPORTER_OTLP_METRICS_ENDPOINT",
|
|
41047
|
-
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
|
|
41048
|
-
"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"
|
|
41049
|
-
];
|
|
41050
|
-
function claudeConfigDir2() {
|
|
41051
|
-
return process.env.CLAUDE_CONFIG_DIR || (0, import_node_path60.join)((0, import_node_os14.homedir)(), ".claude");
|
|
41052
|
-
}
|
|
41053
|
-
function userSettingsPath() {
|
|
41054
|
-
return (0, import_node_path60.join)(claudeConfigDir2(), "settings.json");
|
|
40758
|
+
function userSettingsPath() {
|
|
40759
|
+
return (0, import_node_path58.join)(claudeConfigDir2(), "settings.json");
|
|
41055
40760
|
}
|
|
41056
40761
|
function deriveOtlpEndpoint(serviceUrl) {
|
|
41057
40762
|
return serviceUrl.replace(/\/+$/, "").replace(/\/v1$/, "") + "/v1/otlp";
|
|
@@ -41082,7 +40787,7 @@ function envOf(settings) {
|
|
|
41082
40787
|
return env && typeof env === "object" && !Array.isArray(env) ? env : {};
|
|
41083
40788
|
}
|
|
41084
40789
|
function displayPath(path) {
|
|
41085
|
-
const home = (0,
|
|
40790
|
+
const home = (0, import_node_os12.homedir)();
|
|
41086
40791
|
return home && path.startsWith(home + "/") ? "~" + path.slice(home.length) : path;
|
|
41087
40792
|
}
|
|
41088
40793
|
async function readSettingsFile(path) {
|
|
@@ -41172,19 +40877,19 @@ async function healLocalTelemetry(root = repoRoot()) {
|
|
|
41172
40877
|
if (settings.env && Object.keys(env).length === 0) delete settings.env;
|
|
41173
40878
|
if (plan.helper) delete settings.otelHeadersHelper;
|
|
41174
40879
|
const next = nextLocalBytes(local.raw, settings);
|
|
41175
|
-
if (next !== null && !replaceInRoot((0,
|
|
40880
|
+
if (next !== null && !replaceInRoot((0, import_node_path59.join)(root, ".claude"), "settings.local.json", next)) {
|
|
41176
40881
|
return { removed: [] };
|
|
41177
40882
|
}
|
|
41178
40883
|
return { removed: [...plan.envKeys, ...plan.helper ? ["otelHeadersHelper"] : []] };
|
|
41179
40884
|
}
|
|
41180
40885
|
|
|
41181
40886
|
// src/lib/telemetry-gate.ts
|
|
41182
|
-
var
|
|
41183
|
-
var
|
|
41184
|
-
var
|
|
40887
|
+
var import_node_child_process18 = require("node:child_process");
|
|
40888
|
+
var import_node_fs70 = require("node:fs");
|
|
40889
|
+
var import_node_path60 = require("node:path");
|
|
41185
40890
|
var PROJECT_MARKERS = ["setup.json", "config.json"];
|
|
41186
40891
|
function writtenOnThisMachine(root, rel) {
|
|
41187
|
-
const r = (0,
|
|
40892
|
+
const r = (0, import_node_child_process18.spawnSync)("git", hardenedGitArgv(["ls-files", "--error-unmatch", "--", rel]), {
|
|
41188
40893
|
cwd: root,
|
|
41189
40894
|
env: hardenedGitEnv(),
|
|
41190
40895
|
encoding: "utf-8",
|
|
@@ -41198,10 +40903,10 @@ function writtenOnThisMachine(root, rel) {
|
|
|
41198
40903
|
function isTelemetryProject() {
|
|
41199
40904
|
for (const root of [repoRoot(), mainWorktreeRoot()]) {
|
|
41200
40905
|
if (!root) continue;
|
|
41201
|
-
const dir = (0,
|
|
40906
|
+
const dir = (0, import_node_path60.join)(root, VERITY_DIR);
|
|
41202
40907
|
if (isMachineStore(dir)) continue;
|
|
41203
40908
|
for (const marker of PROJECT_MARKERS) {
|
|
41204
|
-
if ((0,
|
|
40909
|
+
if ((0, import_node_fs70.existsSync)((0, import_node_path60.join)(dir, marker)) && writtenOnThisMachine(root, `${VERITY_DIR}/${marker}`)) return true;
|
|
41205
40910
|
}
|
|
41206
40911
|
}
|
|
41207
40912
|
return false;
|
|
@@ -41330,1319 +41035,1301 @@ async function uninstallTelemetry() {
|
|
|
41330
41035
|
return { ok: true, data: { removed, settingsPath: path, foreignEndpoint, healedLocalKeys: healed.removed } };
|
|
41331
41036
|
}
|
|
41332
41037
|
|
|
41333
|
-
// src/commands/
|
|
41334
|
-
|
|
41335
|
-
|
|
41336
|
-
|
|
41337
|
-
const shares = d.languages.map((l) => ({ name: displayName(l), pct: Math.round((d.languageCounts[l] ?? 0) / total * 100) }));
|
|
41338
|
-
const top = shares.slice(0, 2);
|
|
41339
|
-
const rest = 100 - top.reduce((n2, s) => n2 + s.pct, 0);
|
|
41340
|
-
return rest > 0 && shares.length > 2 ? [...top, { name: "Other", pct: rest }] : top;
|
|
41038
|
+
// src/commands/migrate.ts
|
|
41039
|
+
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
41040
|
+
function defaultNpmRemover(pkg) {
|
|
41041
|
+
(0, import_node_child_process19.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
|
|
41341
41042
|
}
|
|
41342
|
-
|
|
41343
|
-
|
|
41344
|
-
javascript: "JavaScript",
|
|
41345
|
-
python: "Python",
|
|
41346
|
-
go: "Go",
|
|
41347
|
-
java: "Java",
|
|
41348
|
-
kotlin: "Kotlin",
|
|
41349
|
-
ruby: "Ruby",
|
|
41350
|
-
rust: "Rust",
|
|
41351
|
-
c: "C",
|
|
41352
|
-
cpp: "C++",
|
|
41353
|
-
shell: "Shell",
|
|
41354
|
-
dockerfile: "Dockerfile",
|
|
41355
|
-
csharp: "C#",
|
|
41356
|
-
php: "PHP",
|
|
41357
|
-
swift: "Swift",
|
|
41358
|
-
scala: "Scala",
|
|
41359
|
-
sql: "SQL"
|
|
41360
|
-
};
|
|
41361
|
-
var displayName = (l) => NAMES[l] ?? l.charAt(0).toUpperCase() + l.slice(1);
|
|
41362
|
-
var STACK_NAMES = {
|
|
41363
|
-
"next.js": "Next.js",
|
|
41364
|
-
next: "Next.js",
|
|
41365
|
-
react: "React",
|
|
41366
|
-
vue: "Vue",
|
|
41367
|
-
angular: "Angular",
|
|
41368
|
-
svelte: "Svelte",
|
|
41369
|
-
express: "Express",
|
|
41370
|
-
nestjs: "NestJS",
|
|
41371
|
-
fastify: "Fastify",
|
|
41372
|
-
prisma: "Prisma",
|
|
41373
|
-
django: "Django",
|
|
41374
|
-
flask: "Flask",
|
|
41375
|
-
fastapi: "FastAPI",
|
|
41376
|
-
rails: "Rails",
|
|
41377
|
-
spring: "Spring",
|
|
41378
|
-
vitest: "Vitest",
|
|
41379
|
-
jest: "Jest",
|
|
41380
|
-
mocha: "Mocha",
|
|
41381
|
-
playwright: "Playwright",
|
|
41382
|
-
cypress: "Cypress",
|
|
41383
|
-
supabase: "Supabase",
|
|
41384
|
-
stripe: "Stripe",
|
|
41385
|
-
tailwind: "Tailwind"
|
|
41386
|
-
};
|
|
41387
|
-
async function repoStep(ui, detected) {
|
|
41388
|
-
ui.rail.done("Looked at your repository");
|
|
41389
|
-
await ui.rail.languages(languageShares(detected));
|
|
41390
|
-
const stack = [...detected.frameworks, detected.buildSystem, detected.testFramework].filter(Boolean).map((n2) => STACK_NAMES[n2.toLowerCase()] ?? n2);
|
|
41391
|
-
const files = `${detected.filesScanned.toLocaleString("en-US")} ${detected.filesScanned === 1 ? "file" : "files"}`;
|
|
41392
|
-
ui.rail.line([...stack, files].join(" \xB7 "), stack.length ? "plain" : "muted");
|
|
41393
|
-
ui.rail.gap();
|
|
41394
|
-
ui.rail.settle();
|
|
41043
|
+
function git2(cwd, args2) {
|
|
41044
|
+
(0, import_node_child_process19.execFileSync)("git", hardenedGitArgv(args2), { cwd, stdio: "pipe", env: hardenedGitEnv() });
|
|
41395
41045
|
}
|
|
41396
|
-
|
|
41397
|
-
|
|
41398
|
-
|
|
41399
|
-
|
|
41400
|
-
|
|
41401
|
-
|
|
41402
|
-
report.checks[report.checks.indexOf(analysis)] = installed2;
|
|
41403
|
-
if (installed2.status === "ok") ui.rail.ticked("Installed the Codacy analysis CLI");
|
|
41046
|
+
function isGitTracked(cwd, relPath) {
|
|
41047
|
+
try {
|
|
41048
|
+
git2(cwd, ["ls-files", "--error-unmatch", "--", relPath]);
|
|
41049
|
+
return true;
|
|
41050
|
+
} catch {
|
|
41051
|
+
return false;
|
|
41404
41052
|
}
|
|
41405
|
-
|
|
41406
|
-
|
|
41407
|
-
|
|
41408
|
-
|
|
41053
|
+
}
|
|
41054
|
+
function isGitRepo(cwd) {
|
|
41055
|
+
try {
|
|
41056
|
+
git2(cwd, ["rev-parse", "--is-inside-work-tree"]);
|
|
41057
|
+
return true;
|
|
41058
|
+
} catch {
|
|
41059
|
+
return false;
|
|
41409
41060
|
}
|
|
41410
|
-
ui.rail.settle();
|
|
41411
|
-
return !report.checks.some((c) => c.required && c.status !== "ok");
|
|
41412
41061
|
}
|
|
41413
|
-
async function
|
|
41414
|
-
|
|
41415
|
-
|
|
41416
|
-
|
|
41417
|
-
|
|
41418
|
-
|
|
41419
|
-
|
|
41420
|
-
|
|
41421
|
-
|
|
41062
|
+
async function runMigration(opts = {}) {
|
|
41063
|
+
const root = opts.cwd ?? repoRoot();
|
|
41064
|
+
const home = opts.home ?? process.env.HOME ?? "";
|
|
41065
|
+
const npmRemover = opts.npmRemover ?? defaultNpmRemover;
|
|
41066
|
+
const actions = [];
|
|
41067
|
+
const movedProjectDir = migrateProjectDir(root, actions);
|
|
41068
|
+
migrateGlobalCredentials(home, actions);
|
|
41069
|
+
await migrateLegacyHooks(root, actions);
|
|
41070
|
+
await migrateClaudeMd(root, actions);
|
|
41071
|
+
migrateStandardFile(root, actions);
|
|
41072
|
+
await migrateLocalTelemetry(root, actions);
|
|
41073
|
+
removeLegacyPackage(movedProjectDir, npmRemover, actions);
|
|
41074
|
+
return { actions, migrated: actions.length > 0 };
|
|
41075
|
+
}
|
|
41076
|
+
function migrateProjectDir(root, actions) {
|
|
41077
|
+
const gateDir = (0, import_node_path61.join)(root, ".gate");
|
|
41078
|
+
const verityDir = (0, import_node_path61.join)(root, ".verity");
|
|
41079
|
+
if ((0, import_node_fs71.existsSync)(gateDir) && !(0, import_node_fs71.existsSync)(verityDir)) {
|
|
41080
|
+
return migrateProjectDirRename(root, gateDir, verityDir, actions);
|
|
41422
41081
|
}
|
|
41423
|
-
if (
|
|
41424
|
-
|
|
41425
|
-
if (!(await resolveToken(globals.token)).ok) return void 0;
|
|
41426
|
-
if ((await checkTelemetry()).enabled) return "enabled";
|
|
41427
|
-
if (!await askTelemetry(ui)) {
|
|
41428
|
-
ui.rail.line("Telemetry stays off. Turn it on later: verity telemetry install", "muted");
|
|
41429
|
-
ui.rail.settle();
|
|
41430
|
-
return "declined";
|
|
41431
|
-
}
|
|
41082
|
+
if ((0, import_node_fs71.existsSync)(gateDir) && (0, import_node_fs71.existsSync)(verityDir)) {
|
|
41083
|
+
return migrateProjectDirCarry(gateDir, verityDir, actions);
|
|
41432
41084
|
}
|
|
41433
|
-
|
|
41434
|
-
|
|
41435
|
-
|
|
41436
|
-
|
|
41437
|
-
|
|
41438
|
-
|
|
41085
|
+
return false;
|
|
41086
|
+
}
|
|
41087
|
+
function migrateProjectDirRename(root, gateDir, verityDir, actions) {
|
|
41088
|
+
let moved = false;
|
|
41089
|
+
if (isGitRepo(root) && isGitTracked(root, ".gate")) {
|
|
41090
|
+
if (hasStagedChanges(root)) {
|
|
41091
|
+
printWarn(
|
|
41092
|
+
"Migrating .gate \u2192 .verity with `git mv` while the index has staged changes \u2014 the rename will be staged alongside them. Review `git status` before committing."
|
|
41093
|
+
);
|
|
41094
|
+
}
|
|
41095
|
+
try {
|
|
41096
|
+
git2(root, ["mv", "--", ".gate", ".verity"]);
|
|
41097
|
+
actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
|
|
41098
|
+
moved = true;
|
|
41099
|
+
} catch {
|
|
41100
|
+
}
|
|
41439
41101
|
}
|
|
41440
|
-
|
|
41441
|
-
|
|
41442
|
-
|
|
41443
|
-
|
|
41444
|
-
|
|
41445
|
-
|
|
41102
|
+
if (moved) {
|
|
41103
|
+
if ((0, import_node_fs71.existsSync)(gateDir)) {
|
|
41104
|
+
const carried = carryLegacyContents(gateDir, verityDir);
|
|
41105
|
+
if (carried > 0) {
|
|
41106
|
+
actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
|
|
41107
|
+
}
|
|
41108
|
+
try {
|
|
41109
|
+
(0, import_node_fs71.rmSync)(gateDir, { recursive: true, force: true });
|
|
41110
|
+
} catch {
|
|
41111
|
+
}
|
|
41112
|
+
}
|
|
41113
|
+
return true;
|
|
41446
41114
|
}
|
|
41447
|
-
|
|
41448
|
-
|
|
41449
|
-
return installed2.ok ? "enabled" : void 0;
|
|
41450
|
-
}
|
|
41451
|
-
async function askTelemetry(ui) {
|
|
41452
|
-
const { rail, screen } = ui;
|
|
41453
|
-
rail.active("Send Claude Code cost and usage from this machine to Verity?");
|
|
41454
|
-
rail.line("Token counts, cost, models, tool names and the repository name, from every Claude Code session on this machine. No prompts, no code, no file contents.");
|
|
41455
|
-
rail.line("See it on the /usage page. Turn it off any time: verity telemetry uninstall");
|
|
41456
|
-
rail.gap();
|
|
41457
|
-
rail.keys([["Enter", "yes"], ["n", "no"]]);
|
|
41458
|
-
const k = await screen.key(["enter", "n"], "enter", { freshMs: 300 });
|
|
41459
|
-
rail.collapse();
|
|
41460
|
-
return k !== "n";
|
|
41461
|
-
}
|
|
41462
|
-
function signInDetail(outcome) {
|
|
41463
|
-
if (outcome === "already" || outcome === "new") return ["ok", outcome];
|
|
41464
|
-
if (outcome === "declined" || outcome === "unattended") return ["skipped", outcome];
|
|
41465
|
-
return ["failed", outcome === "unreachable" ? "unreachable" : "error"];
|
|
41466
|
-
}
|
|
41467
|
-
function formalDefault(previous, opts = {}) {
|
|
41468
|
-
const cfg = readProjectConfig();
|
|
41469
|
-
if (previous?.init?.completed_at || opts.setUpBefore || cfg.formal !== void 0 || cfg.formal_refused) return false;
|
|
41470
|
-
writeProjectConfig({ formal: "push" });
|
|
41115
|
+
moveDir(gateDir, verityDir);
|
|
41116
|
+
actions.push("Moved .gate/ \u2192 .verity/");
|
|
41471
41117
|
return true;
|
|
41472
41118
|
}
|
|
41473
|
-
function
|
|
41474
|
-
|
|
41475
|
-
|
|
41476
|
-
|
|
41477
|
-
}
|
|
41478
|
-
function backgroundStep(ui, opts) {
|
|
41479
|
-
const { rail } = ui;
|
|
41480
|
-
const formal = readProjectConfig().formal;
|
|
41481
|
-
const said = opts.formalNew === true && formal === "push";
|
|
41482
|
-
if (said) sayFormalOn(ui);
|
|
41483
|
-
const lean = shouldStartBackgroundInstall({ formal });
|
|
41484
|
-
const leanStarted = lean.start && startBackgroundInstall({ cli: opts.cli });
|
|
41485
|
-
const trivy = startTrivyPrewarm();
|
|
41486
|
-
const parts2 = [
|
|
41487
|
-
...trivy ? ["Trivy security scanner"] : [],
|
|
41488
|
-
...leanStarted ? ["Lean 4 for formal checks at push (800 MB)"] : [],
|
|
41489
|
-
...!lean.start && lean.why === "running" ? ["Lean 4 (already downloading)"] : []
|
|
41490
|
-
];
|
|
41491
|
-
if (parts2.length) {
|
|
41492
|
-
if (said) rail.gap();
|
|
41493
|
-
rail.done("Getting ready in the background");
|
|
41494
|
-
rail.line(parts2.join(" \xB7 "));
|
|
41495
|
-
if (leanStarted) rail.line(said ? "Progress: verity lean status" : "Progress: verity lean status. Turn formal checks off: verity config formal off");
|
|
41496
|
-
} else if (!lean.start && lean.why === "installed" && formal === "push" && !said) {
|
|
41497
|
-
rail.done("Formal verification at push: on");
|
|
41498
|
-
}
|
|
41499
|
-
if (!lean.start && lean.why === "disk") {
|
|
41500
|
-
rail.warn("Formal verification is on, but Lean 4 needs more disk space");
|
|
41501
|
-
rail.line(`${lean.detail}. Free some space, then run: verity lean install`);
|
|
41502
|
-
} else if (!lean.start && lean.why === "no-release" && formal === "push") {
|
|
41503
|
-
rail.line("Lean 4 has no release for this machine, so the formal check at push is skipped.");
|
|
41119
|
+
function migrateProjectDirCarry(gateDir, verityDir, actions) {
|
|
41120
|
+
const carried = carryLegacyContents(gateDir, verityDir);
|
|
41121
|
+
if (carried > 0) {
|
|
41122
|
+
actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
|
|
41504
41123
|
}
|
|
41505
|
-
if (parts2.length || said || !lean.start && lean.why === "disk") rail.gap();
|
|
41506
|
-
rail.settle();
|
|
41507
|
-
}
|
|
41508
|
-
|
|
41509
|
-
// src/commands/init/scaffold.ts
|
|
41510
|
-
var import_promises17 = require("node:fs/promises");
|
|
41511
|
-
|
|
41512
|
-
// src/lib/codacy-legacy.ts
|
|
41513
|
-
var import_node_child_process21 = require("node:child_process");
|
|
41514
|
-
var VERITY_ESLINT_CONFIG_FILE = ".codacy/tools-configs/eslint.config.mjs";
|
|
41515
|
-
var VERITY_ESLINT_LOCAL_PATH = "./.codacy/tools-configs/eslint.config.mjs";
|
|
41516
|
-
var VERITY_ESLINT_FIRST_LINE = "// Verity-owned ESLint config for the quality gate.";
|
|
41517
|
-
var MAX_BYTES = 256 * 1024;
|
|
41518
|
-
function classifyCodacyConfig(root = repoRoot()) {
|
|
41519
|
-
const cfg = readRegularFileInRoot(root, CODACY_CONFIG_FILE, MAX_BYTES);
|
|
41520
|
-
if (!cfg.ok) return cfg.reason === "missing" ? "none" : "foreign";
|
|
41521
|
-
let parsed;
|
|
41522
41124
|
try {
|
|
41523
|
-
|
|
41125
|
+
(0, import_node_fs71.rmSync)(gateDir, { recursive: true, force: true });
|
|
41524
41126
|
} catch {
|
|
41525
|
-
return "foreign";
|
|
41526
41127
|
}
|
|
41527
|
-
|
|
41528
|
-
const pointsAtVerity2 = Array.isArray(tools) && tools.some((t) => typeof t === "object" && t !== null && t.localConfigurationFile === VERITY_ESLINT_LOCAL_PATH);
|
|
41529
|
-
if (!pointsAtVerity2) return "foreign";
|
|
41530
|
-
const eslint = readRegularFileInRoot(root, VERITY_ESLINT_CONFIG_FILE, MAX_BYTES);
|
|
41531
|
-
return eslint.ok && eslint.content.startsWith(VERITY_ESLINT_FIRST_LINE) ? "verity" : "foreign";
|
|
41128
|
+
return carried > 0;
|
|
41532
41129
|
}
|
|
41533
|
-
function
|
|
41534
|
-
|
|
41535
|
-
|
|
41536
|
-
|
|
41537
|
-
|
|
41538
|
-
|
|
41130
|
+
function migrateGlobalCredentials(home, actions) {
|
|
41131
|
+
if (!home) return;
|
|
41132
|
+
const gateCreds = (0, import_node_path61.join)(home, ".gate", "credentials");
|
|
41133
|
+
const verityCreds = (0, import_node_path61.join)(home, ".verity", "credentials");
|
|
41134
|
+
if (!(0, import_node_fs71.existsSync)(gateCreds)) return;
|
|
41135
|
+
if (!(0, import_node_fs71.existsSync)(verityCreds)) {
|
|
41136
|
+
(0, import_node_fs71.mkdirSync)((0, import_node_path61.join)(home, ".verity"), { recursive: true });
|
|
41137
|
+
moveFile(gateCreds, verityCreds);
|
|
41138
|
+
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
41139
|
+
return;
|
|
41140
|
+
}
|
|
41141
|
+
const added = mergeGlobalCredentials(gateCreds, verityCreds);
|
|
41142
|
+
if (added > 0) {
|
|
41143
|
+
actions.push(
|
|
41144
|
+
`Merged ${added} legacy credential line(s) from ~/.gate/credentials into ~/.verity/credentials`
|
|
41145
|
+
);
|
|
41539
41146
|
}
|
|
41540
41147
|
}
|
|
41541
|
-
function
|
|
41542
|
-
const
|
|
41543
|
-
const
|
|
41148
|
+
async function migrateLegacyHooks(root, actions) {
|
|
41149
|
+
const settings = await readSettingsAt(root);
|
|
41150
|
+
const settingsBefore = JSON.stringify(settings);
|
|
41151
|
+
const updatedSettings = removeVerityHooks(settings);
|
|
41152
|
+
if (settingsBefore !== JSON.stringify(updatedSettings)) {
|
|
41153
|
+
await writeSettingsAt(root, updatedSettings);
|
|
41154
|
+
actions.push("Stripped legacy gate hooks from .claude/settings.json");
|
|
41155
|
+
}
|
|
41156
|
+
}
|
|
41157
|
+
async function migrateClaudeMd(root, actions) {
|
|
41158
|
+
const claudeMd = (0, import_node_path61.join)(root, "CLAUDE.md");
|
|
41159
|
+
const hadLegacyBlock = (0, import_node_fs71.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
|
|
41160
|
+
if (!hadLegacyBlock) return;
|
|
41544
41161
|
try {
|
|
41545
|
-
|
|
41546
|
-
|
|
41547
|
-
untracked.push(f);
|
|
41548
|
-
continue;
|
|
41549
|
-
}
|
|
41550
|
-
(0, import_node_child_process21.execFileSync)("git", ["rm", "--quiet", "--", f], { cwd: root, stdio: "pipe" });
|
|
41551
|
-
removed.push(f);
|
|
41552
|
-
}
|
|
41162
|
+
await ensureClaudeMdPointer(root);
|
|
41163
|
+
actions.push("Rewrote legacy gate-memory block in CLAUDE.md");
|
|
41553
41164
|
} catch (err2) {
|
|
41554
|
-
|
|
41165
|
+
printWarn(`Could not rewrite CLAUDE.md block: ${err2.message}`);
|
|
41555
41166
|
}
|
|
41556
|
-
return { ok: true, removed, untracked };
|
|
41557
41167
|
}
|
|
41558
|
-
|
|
41559
|
-
|
|
41560
|
-
|
|
41561
|
-
|
|
41562
|
-
|
|
41563
|
-
|
|
41564
|
-
|
|
41565
|
-
|
|
41566
|
-
|
|
41567
|
-
|
|
41568
|
-
} else if (ignore === "conflict") {
|
|
41569
|
-
rail.warn(".gitignore: git still ignores .verity/standard.yaml");
|
|
41570
|
-
rail.line("A global or nested .gitignore covers it, so the Standard cannot be committed. Check: git check-ignore -v .verity/standard.yaml");
|
|
41571
|
-
} else if (ignore === "memory-tracked") {
|
|
41572
|
-
rail.warn("This project commits its knowledge graph");
|
|
41573
|
-
}
|
|
41574
|
-
const trackedMemory = memoryOptOut() ? [] : committedMemoryFiles();
|
|
41575
|
-
if (trackedMemory.length > 0) {
|
|
41576
|
-
if (given.untrackMemory) {
|
|
41577
|
-
const result = fenceMemory();
|
|
41578
|
-
if (result.ok) rail.line(`Untracked ${result.untracked} knowledge-graph file(s) (staged). Commit to finish.`);
|
|
41579
|
-
else rail.line("Could not untrack the knowledge graph. Run: verity memory untrack", "warn");
|
|
41580
|
-
} else {
|
|
41581
|
-
rail.warn(`${trackedMemory.length} knowledge-graph file(s) are tracked in git`);
|
|
41582
|
-
rail.line("They show up in every diff. Stop with: verity memory untrack (or keep them on purpose: verity memory track)");
|
|
41583
|
-
}
|
|
41584
|
-
}
|
|
41585
|
-
const tracked = committedVerityState();
|
|
41586
|
-
if (tracked.length > 0) {
|
|
41587
|
-
if (given.untrackState || ui.asking && await askUntrackState(ui, tracked)) {
|
|
41588
|
-
const result = untrackVerityState();
|
|
41589
|
-
if (result === "untracked") rail.line("Untracked the Verity state files (staged). Commit to finish.");
|
|
41590
|
-
else if (result === "failed") rail.line("Could not untrack them. Run: git rm -r --cached .verity && git add .verity/standard.yaml", "warn");
|
|
41591
|
-
} else {
|
|
41592
|
-
rail.warn(`${tracked.length} Verity state file(s) are tracked in git`);
|
|
41593
|
-
rail.line(`For example ${terminalText(tracked[0], 200)}. Fix: git rm -r --cached .verity && git add .verity/standard.yaml`);
|
|
41168
|
+
function migrateStandardFile(root, actions) {
|
|
41169
|
+
const gateMd = (0, import_node_path61.join)(root, "GATE.md");
|
|
41170
|
+
const verityMd = (0, import_node_path61.join)(root, "VERITY.md");
|
|
41171
|
+
if (!(0, import_node_fs71.existsSync)(gateMd) || (0, import_node_fs71.existsSync)(verityMd)) return;
|
|
41172
|
+
let moved = false;
|
|
41173
|
+
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
41174
|
+
try {
|
|
41175
|
+
git2(root, ["mv", "--", "GATE.md", "VERITY.md"]);
|
|
41176
|
+
moved = true;
|
|
41177
|
+
} catch {
|
|
41594
41178
|
}
|
|
41595
41179
|
}
|
|
41596
|
-
|
|
41180
|
+
if (!moved) moveFile(gateMd, verityMd);
|
|
41181
|
+
const content = readFileSyncSafe(verityMd);
|
|
41182
|
+
const refreshed = content.split("GATE.md").join("VERITY.md");
|
|
41183
|
+
if (refreshed !== content) (0, import_node_fs71.writeFileSync)(verityMd, refreshed);
|
|
41184
|
+
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
41185
|
+
}
|
|
41186
|
+
async function migrateLocalTelemetry(root, actions) {
|
|
41187
|
+
const { removed } = await healLocalTelemetry(root);
|
|
41188
|
+
if (removed.length === 0) return;
|
|
41189
|
+
actions.push(
|
|
41190
|
+
`Removed ${removed.length} old Verity telemetry key(s) from ${SETTINGS_LOCAL_FILE2} (Claude Code ignores them there). Run "verity telemetry install" to export from this machine.`
|
|
41191
|
+
);
|
|
41192
|
+
}
|
|
41193
|
+
function removeLegacyPackage(movedProjectDir, npmRemover, actions) {
|
|
41194
|
+
if (!movedProjectDir) return;
|
|
41597
41195
|
try {
|
|
41598
|
-
|
|
41196
|
+
npmRemover(LEGACY_NPM_PACKAGE);
|
|
41197
|
+
actions.push(`Removed legacy global package ${LEGACY_NPM_PACKAGE}`);
|
|
41599
41198
|
} catch (err2) {
|
|
41600
|
-
|
|
41199
|
+
printWarn(
|
|
41200
|
+
`Could not remove ${LEGACY_NPM_PACKAGE} (${err2.message}). Remove it manually: npm rm -g ${LEGACY_NPM_PACKAGE}`
|
|
41201
|
+
);
|
|
41601
41202
|
}
|
|
41602
|
-
const cfg = readProjectConfig();
|
|
41603
|
-
if (cfg.formal_refused) rail.line(`Formal verification: off \u2014 ${formalRefusalMessage(cfg.formal_refused)}`, "warn");
|
|
41604
|
-
rail.settle();
|
|
41605
41203
|
}
|
|
41606
|
-
|
|
41607
|
-
const
|
|
41608
|
-
|
|
41609
|
-
|
|
41610
|
-
|
|
41611
|
-
rail.line("Copies already pushed stay in the git history: rotate any secret the gate flagged.");
|
|
41612
|
-
rail.gap();
|
|
41613
|
-
rail.keys([["Enter", "stop tracking them"], ["n", "keep them tracked"]]);
|
|
41614
|
-
const k = await screen.key(["enter", "n"], "enter", { freshMs: 300 });
|
|
41615
|
-
rail.collapse();
|
|
41616
|
-
return k !== "n";
|
|
41204
|
+
function credentialRemoteKey(line2) {
|
|
41205
|
+
const trimmed = line2.trim();
|
|
41206
|
+
if (trimmed === "") return null;
|
|
41207
|
+
const idx = trimmed.indexOf(" token:");
|
|
41208
|
+
return idx === -1 ? trimmed : trimmed.slice(0, idx).trim();
|
|
41617
41209
|
}
|
|
41618
|
-
function
|
|
41619
|
-
const
|
|
41620
|
-
|
|
41621
|
-
|
|
41622
|
-
|
|
41623
|
-
|
|
41210
|
+
function mergeGlobalCredentials(gateCreds, verityCreds) {
|
|
41211
|
+
const verityContent = readFileSyncSafe(verityCreds);
|
|
41212
|
+
const existingKeys = new Set(
|
|
41213
|
+
verityContent.split("\n").map(credentialRemoteKey).filter((k) => k !== null)
|
|
41214
|
+
);
|
|
41215
|
+
const toAppend = [];
|
|
41216
|
+
for (const line2 of readFileSyncSafe(gateCreds).split("\n")) {
|
|
41217
|
+
const key = credentialRemoteKey(line2);
|
|
41218
|
+
if (key === null || existingKeys.has(key)) continue;
|
|
41219
|
+
existingKeys.add(key);
|
|
41220
|
+
toAppend.push(line2.replace(/\r$/, ""));
|
|
41624
41221
|
}
|
|
41625
|
-
|
|
41626
|
-
|
|
41627
|
-
|
|
41628
|
-
ui.rail.done("Removed the .codacy/ config that an older Verity wrote (staged)");
|
|
41629
|
-
ui.rail.line("Verity runs Trivy from .verity/static-analysis.json now. To keep the files: git restore --staged --worktree .codacy");
|
|
41630
|
-
}
|
|
41631
|
-
if (result.untracked.length > 0) {
|
|
41632
|
-
ui.rail.line(`An older Verity wrote ${result.untracked.join(" and ")}, and Verity no longer uses them. Remove them by hand: rm ${result.untracked.join(" ")}`, "warn");
|
|
41633
|
-
}
|
|
41634
|
-
} else {
|
|
41635
|
-
ui.rail.warn("Could not remove the .codacy/ config that an older Verity wrote");
|
|
41636
|
-
ui.rail.line(terminalText(result.error, 200));
|
|
41222
|
+
if (toAppend.length > 0) {
|
|
41223
|
+
const sep7 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
|
|
41224
|
+
(0, import_node_fs71.writeFileSync)(verityCreds, verityContent + sep7 + toAppend.join("\n") + "\n");
|
|
41637
41225
|
}
|
|
41226
|
+
(0, import_node_fs71.rmSync)(gateCreds, { force: true });
|
|
41227
|
+
return toAppend.length;
|
|
41638
41228
|
}
|
|
41639
|
-
|
|
41640
|
-
|
|
41641
|
-
|
|
41642
|
-
|
|
41643
|
-
|
|
41644
|
-
var DEFAULT_MOMENTS = ["stop", "pre-commit", "pre-push"];
|
|
41645
|
-
async function momentsFromInstalledHooks() {
|
|
41646
|
-
const status = await checkAllVerityHooks();
|
|
41647
|
-
const wired = [];
|
|
41648
|
-
if (status.stop) wired.push("stop");
|
|
41649
|
-
if (status.guardOn.includes("commit")) wired.push("pre-commit");
|
|
41650
|
-
if (status.guardOn.includes("push")) wired.push("pre-push");
|
|
41651
|
-
return wired.length > 0 ? wired : DEFAULT_MOMENTS;
|
|
41652
|
-
}
|
|
41653
|
-
async function askSetupQuestions(_defaultsOnly, previous, given = {}) {
|
|
41654
|
-
const moments = given.moments ?? (previous?.moments?.length ? previous.moments : await momentsFromInstalledHooks());
|
|
41655
|
-
let telemetry = "not-asked";
|
|
41656
|
-
if (given.telemetry !== void 0) {
|
|
41657
|
-
const enabled = (await checkTelemetry()).enabled;
|
|
41658
|
-
telemetry = given.telemetry ? enabled ? "already-on" : "yes" : enabled ? "turn-off" : "no";
|
|
41229
|
+
function readFileSyncSafe(path) {
|
|
41230
|
+
try {
|
|
41231
|
+
return (0, import_node_fs71.readFileSync)(path, "utf-8");
|
|
41232
|
+
} catch {
|
|
41233
|
+
return "";
|
|
41659
41234
|
}
|
|
41660
|
-
return { moments, telemetry };
|
|
41661
|
-
}
|
|
41662
|
-
function parseTelemetryAnswer(raw) {
|
|
41663
|
-
const v = raw.trim().toLowerCase();
|
|
41664
|
-
if (v === "on") return true;
|
|
41665
|
-
if (v === "off") return false;
|
|
41666
|
-
return null;
|
|
41667
|
-
}
|
|
41668
|
-
function planMoments(answers, opts) {
|
|
41669
|
-
const existing = readProjectConfig();
|
|
41670
|
-
const keptUserChoice = !opts.given && existing.git_moments_source === "user";
|
|
41671
|
-
const gitMoments = keptUserChoice ? existing.git_moments : [
|
|
41672
|
-
...answers.includes("pre-commit") ? ["commit"] : [],
|
|
41673
|
-
...answers.includes("pre-push") ? ["push"] : []
|
|
41674
|
-
];
|
|
41675
|
-
const turnOn = keptUserChoice && existing.turn_review ? existing.turn_review === "on" : answers.includes("stop");
|
|
41676
|
-
return {
|
|
41677
|
-
momentIds: [
|
|
41678
|
-
...turnOn ? ["stop"] : [],
|
|
41679
|
-
...gitMoments.includes("commit") ? ["pre-commit"] : [],
|
|
41680
|
-
...gitMoments.includes("push") ? ["pre-push"] : []
|
|
41681
|
-
],
|
|
41682
|
-
gitMoments,
|
|
41683
|
-
turnOn,
|
|
41684
|
-
keptUserChoice
|
|
41685
|
-
};
|
|
41686
41235
|
}
|
|
41687
|
-
function
|
|
41688
|
-
|
|
41689
|
-
|
|
41690
|
-
|
|
41691
|
-
|
|
41692
|
-
|
|
41693
|
-
ui.rail.done(`Verity reviews ${said}`);
|
|
41694
|
-
ui.rail.line("To change it, run verity config moments, or just ask Claude.");
|
|
41236
|
+
function hasStagedChanges(root) {
|
|
41237
|
+
try {
|
|
41238
|
+
git2(root, ["diff", "--cached", "--quiet", ...DIFF_READ_FLAGS]);
|
|
41239
|
+
return false;
|
|
41240
|
+
} catch {
|
|
41241
|
+
return true;
|
|
41695
41242
|
}
|
|
41696
|
-
|
|
41697
|
-
|
|
41243
|
+
}
|
|
41244
|
+
function moveDir(from, to) {
|
|
41245
|
+
try {
|
|
41246
|
+
(0, import_node_fs71.renameSync)(from, to);
|
|
41247
|
+
} catch (err2) {
|
|
41248
|
+
if (err2.code !== "EXDEV") throw err2;
|
|
41249
|
+
(0, import_node_fs71.cpSync)(from, to, { recursive: true });
|
|
41250
|
+
(0, import_node_fs71.rmSync)(from, { recursive: true, force: true });
|
|
41698
41251
|
}
|
|
41699
|
-
ui.rail.gap();
|
|
41700
|
-
ui.rail.settle();
|
|
41701
41252
|
}
|
|
41702
|
-
|
|
41703
|
-
|
|
41704
|
-
|
|
41705
|
-
|
|
41706
|
-
|
|
41707
|
-
|
|
41708
|
-
|
|
41709
|
-
"verity-insights",
|
|
41710
|
-
"verity-reflect",
|
|
41711
|
-
"verity-init",
|
|
41712
|
-
"verity-prove"
|
|
41713
|
-
];
|
|
41714
|
-
function resolveDataDir2() {
|
|
41715
|
-
const candidates2 = [
|
|
41716
|
-
(0, import_node_path63.join)(__dirname, "..", "data"),
|
|
41717
|
-
(0, import_node_path63.join)(__dirname, "..", "..", "data"),
|
|
41718
|
-
(0, import_node_path63.join)(process.cwd(), "cli", "data")
|
|
41719
|
-
];
|
|
41720
|
-
for (const candidate of candidates2) {
|
|
41721
|
-
if ((0, import_node_fs74.existsSync)((0, import_node_path63.join)(candidate, "skills"))) return candidate;
|
|
41253
|
+
function moveFile(from, to) {
|
|
41254
|
+
try {
|
|
41255
|
+
(0, import_node_fs71.renameSync)(from, to);
|
|
41256
|
+
} catch (err2) {
|
|
41257
|
+
if (err2.code !== "EXDEV") throw err2;
|
|
41258
|
+
(0, import_node_fs71.cpSync)(from, to);
|
|
41259
|
+
(0, import_node_fs71.rmSync)(from, { force: true });
|
|
41722
41260
|
}
|
|
41723
|
-
throw new Error("Could not find Verity skill data. Reinstall: npm install -g @codacy/verity-cli");
|
|
41724
41261
|
}
|
|
41725
|
-
|
|
41726
|
-
|
|
41727
|
-
|
|
41728
|
-
|
|
41729
|
-
|
|
41730
|
-
|
|
41731
|
-
|
|
41732
|
-
|
|
41262
|
+
function carryLegacyContents(gateDir, verityDir) {
|
|
41263
|
+
if (!isRealDir(gateDir) || !isRealDir(verityDir)) return 0;
|
|
41264
|
+
let copied = 0;
|
|
41265
|
+
const walk2 = (relDir) => {
|
|
41266
|
+
let entries;
|
|
41267
|
+
try {
|
|
41268
|
+
entries = (0, import_node_fs71.readdirSync)((0, import_node_path61.join)(gateDir, relDir), { withFileTypes: true });
|
|
41269
|
+
} catch {
|
|
41270
|
+
return;
|
|
41271
|
+
}
|
|
41272
|
+
for (const entry of entries) {
|
|
41273
|
+
const rel = relDir ? (0, import_node_path61.join)(relDir, entry.name) : entry.name;
|
|
41274
|
+
if (entry.isSymbolicLink()) continue;
|
|
41275
|
+
if (entry.isDirectory()) {
|
|
41276
|
+
walk2(rel);
|
|
41277
|
+
continue;
|
|
41733
41278
|
}
|
|
41734
|
-
|
|
41735
|
-
|
|
41736
|
-
|
|
41279
|
+
if (!entry.isFile() || !ensureRealDirs(verityDir, relDir)) continue;
|
|
41280
|
+
const opened = openRegularInRoot(gateDir, rel);
|
|
41281
|
+
if (!opened.ok) continue;
|
|
41282
|
+
try {
|
|
41283
|
+
(0, import_node_fs71.writeFileSync)((0, import_node_path61.join)(verityDir, rel), readOpened(opened.fd, opened.size), { flag: "wx", mode: 384 });
|
|
41284
|
+
copied++;
|
|
41285
|
+
} catch {
|
|
41286
|
+
}
|
|
41287
|
+
}
|
|
41737
41288
|
};
|
|
41289
|
+
walk2("");
|
|
41290
|
+
return copied;
|
|
41291
|
+
}
|
|
41292
|
+
function isRealDir(path) {
|
|
41738
41293
|
try {
|
|
41739
|
-
const
|
|
41740
|
-
|
|
41741
|
-
for (const rel of shipped) {
|
|
41742
|
-
if (await (0, import_promises18.readFile)((0, import_node_path63.join)(src, rel), "utf-8") !== await (0, import_promises18.readFile)((0, import_node_path63.join)(dest, rel), "utf-8")) return false;
|
|
41743
|
-
}
|
|
41744
|
-
return true;
|
|
41294
|
+
const st = (0, import_node_fs71.lstatSync)(path);
|
|
41295
|
+
return st.isDirectory() && !st.isSymbolicLink();
|
|
41745
41296
|
} catch {
|
|
41746
41297
|
return false;
|
|
41747
41298
|
}
|
|
41748
41299
|
}
|
|
41749
|
-
|
|
41750
|
-
|
|
41751
|
-
const
|
|
41752
|
-
|
|
41753
|
-
|
|
41754
|
-
|
|
41755
|
-
|
|
41756
|
-
|
|
41757
|
-
continue;
|
|
41300
|
+
function ensureRealDirs(root, relDir) {
|
|
41301
|
+
let current = root;
|
|
41302
|
+
for (const segment of relDir.split(/[\\/]/).filter(Boolean)) {
|
|
41303
|
+
current = (0, import_node_path61.join)(current, segment);
|
|
41304
|
+
try {
|
|
41305
|
+
(0, import_node_fs71.mkdirSync)(current);
|
|
41306
|
+
} catch (e) {
|
|
41307
|
+
if (e.code !== "EEXIST") return false;
|
|
41758
41308
|
}
|
|
41759
|
-
if ((
|
|
41760
|
-
await (0, import_promises18.mkdir)(dest, { recursive: true });
|
|
41761
|
-
await (0, import_promises18.cp)(src, dest, { recursive: true, force: true });
|
|
41309
|
+
if (!isRealDir(current)) return false;
|
|
41762
41310
|
}
|
|
41763
|
-
return
|
|
41311
|
+
return true;
|
|
41764
41312
|
}
|
|
41765
|
-
async function
|
|
41766
|
-
const
|
|
41767
|
-
|
|
41768
|
-
if (!
|
|
41769
|
-
|
|
41770
|
-
|
|
41771
|
-
|
|
41772
|
-
rail.done(`Hooks here: the Verity Claude Code plugin${ctx.pluginWhere}`);
|
|
41773
|
-
rail.line("Skills and hooks come from the plugin, so this run installs neither, and removes any this project still has from an npm install.");
|
|
41774
|
-
rail.line("Want this project to own its own copy instead? Uninstall the plugin, or use --no-plugin.");
|
|
41775
|
-
const settings = await readSettings();
|
|
41776
|
-
const stripped = removeVerityHooks(settings);
|
|
41777
|
-
if (JSON.stringify(settings.hooks ?? {}) !== JSON.stringify(stripped.hooks ?? {})) {
|
|
41778
|
-
await writeSettings(stripped);
|
|
41779
|
-
rail.line("Removed this project's Verity hooks from .claude/settings.json. The plugin wires them now, so each turn is reviewed once.");
|
|
41313
|
+
async function needsMigration(root = repoRoot()) {
|
|
41314
|
+
const gateDir = (0, import_node_path61.join)(root, ".gate");
|
|
41315
|
+
const verityDir = (0, import_node_path61.join)(root, ".verity");
|
|
41316
|
+
if ((0, import_node_fs71.existsSync)(gateDir) && !(0, import_node_fs71.existsSync)(verityDir)) return true;
|
|
41317
|
+
if ((0, import_node_fs71.existsSync)(gateDir) && (0, import_node_fs71.existsSync)(verityDir)) {
|
|
41318
|
+
if ((0, import_node_fs71.existsSync)((0, import_node_path61.join)(gateDir, "credentials")) && !(0, import_node_fs71.existsSync)((0, import_node_path61.join)(verityDir, "credentials"))) {
|
|
41319
|
+
return true;
|
|
41780
41320
|
}
|
|
41781
|
-
|
|
41782
|
-
|
|
41783
|
-
} else {
|
|
41784
|
-
if (ctx.overridingPlugin) {
|
|
41785
|
-
rail.warn("Hooks here: this project's .claude/settings.json (--no-plugin)");
|
|
41786
|
-
rail.line("The Verity plugin is active on this machine and would own them. Both sets fire; the settings.json copy stands down at run time, so each turn is reviewed once.");
|
|
41787
|
-
} else {
|
|
41788
|
-
rail.done("Hooks here: this project's .claude/settings.json");
|
|
41321
|
+
if ((0, import_node_fs71.existsSync)((0, import_node_path61.join)(gateDir, "memory")) && !(0, import_node_fs71.existsSync)((0, import_node_path61.join)(verityDir, "memory"))) {
|
|
41322
|
+
return true;
|
|
41789
41323
|
}
|
|
41790
|
-
const missing = await installSkills(ctx.force);
|
|
41791
|
-
if (missing.length > 0) rail.line(`Skill data not found: ${missing.join(", ")}`, "warn");
|
|
41792
|
-
await applyMomentSelection(ctx.plan.momentIds);
|
|
41793
41324
|
}
|
|
41794
|
-
|
|
41795
|
-
|
|
41325
|
+
const claudeMd = (0, import_node_path61.join)(root, "CLAUDE.md");
|
|
41326
|
+
if ((0, import_node_fs71.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
41327
|
+
return true;
|
|
41328
|
+
}
|
|
41329
|
+
if ((0, import_node_fs71.existsSync)((0, import_node_path61.join)(root, "GATE.md")) && !(0, import_node_fs71.existsSync)((0, import_node_path61.join)(root, "VERITY.md"))) {
|
|
41330
|
+
return true;
|
|
41331
|
+
}
|
|
41332
|
+
if (await hasLegacyHooksAt(root)) return true;
|
|
41333
|
+
return false;
|
|
41334
|
+
}
|
|
41335
|
+
function registerMigrateCommand(program2) {
|
|
41336
|
+
program2.command("migrate").description("Migrate a legacy GATE.md install to Verity (.gate \u2192 .verity, hooks, CLAUDE.md)").action(async () => {
|
|
41337
|
+
let result;
|
|
41338
|
+
try {
|
|
41339
|
+
result = await runMigration();
|
|
41340
|
+
} catch (err2) {
|
|
41341
|
+
printError(`Migration failed: ${err2.message}`);
|
|
41342
|
+
process.exitCode = 1;
|
|
41343
|
+
return;
|
|
41344
|
+
}
|
|
41345
|
+
if (!result.migrated) {
|
|
41346
|
+
printInfo("Nothing to migrate \u2014 this project is already on Verity.");
|
|
41347
|
+
return;
|
|
41348
|
+
}
|
|
41349
|
+
printInfo("Migration complete:");
|
|
41350
|
+
for (const a of result.actions) printInfo(` ${a} \u2713`);
|
|
41351
|
+
});
|
|
41796
41352
|
}
|
|
41797
41353
|
|
|
41798
|
-
// src/commands/init/
|
|
41799
|
-
var
|
|
41800
|
-
|
|
41801
|
-
|
|
41802
|
-
|
|
41354
|
+
// src/commands/init/ui.ts
|
|
41355
|
+
var import_node_fs72 = require("node:fs");
|
|
41356
|
+
var import_node_path62 = require("node:path");
|
|
41357
|
+
var import_node_os13 = require("node:os");
|
|
41358
|
+
var import_node_child_process20 = require("node:child_process");
|
|
41359
|
+
function createInitUi(opts) {
|
|
41360
|
+
const term = opts.term ?? detectTerm();
|
|
41361
|
+
const screen = new Screen(term);
|
|
41362
|
+
const rail = new Rail(screen);
|
|
41363
|
+
return { term, screen, rail, asking: !opts.defaultsOnly && screen.canAsk() };
|
|
41364
|
+
}
|
|
41365
|
+
function installInterrupt(ui) {
|
|
41366
|
+
const stop2 = () => {
|
|
41367
|
+
ui.rail.stopped();
|
|
41368
|
+
ui.screen.release();
|
|
41369
|
+
process.exit(130);
|
|
41370
|
+
};
|
|
41371
|
+
ui.screen.startInput(stop2);
|
|
41372
|
+
process.once("SIGINT", stop2);
|
|
41373
|
+
}
|
|
41374
|
+
var SEEN_FILE = ".init-seen";
|
|
41375
|
+
function firstInitOnMachine(home = process.env.HOME || (0, import_node_os13.homedir)()) {
|
|
41376
|
+
return !(0, import_node_fs72.existsSync)((0, import_node_path62.join)(home, ".verity", SEEN_FILE));
|
|
41377
|
+
}
|
|
41378
|
+
function markInitSeen(home = process.env.HOME || (0, import_node_os13.homedir)()) {
|
|
41379
|
+
replaceInRoot((0, import_node_path62.join)(home, ".verity"), SEEN_FILE, `${(/* @__PURE__ */ new Date()).toISOString()}
|
|
41380
|
+
`);
|
|
41381
|
+
}
|
|
41382
|
+
function repoLabel() {
|
|
41803
41383
|
try {
|
|
41804
|
-
const
|
|
41805
|
-
|
|
41384
|
+
const url = (0, import_node_child_process20.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
41385
|
+
const parsed = parseRemote(url);
|
|
41386
|
+
if (parsed) return terminalText(`${parsed.owner}/${parsed.repo}`, 120);
|
|
41806
41387
|
} catch {
|
|
41807
|
-
return null;
|
|
41808
41388
|
}
|
|
41389
|
+
return terminalText((0, import_node_path62.basename)(repoRoot()), 120);
|
|
41809
41390
|
}
|
|
41810
|
-
|
|
41811
|
-
|
|
41812
|
-
|
|
41813
|
-
|
|
41814
|
-
|
|
41815
|
-
|
|
41816
|
-
|
|
41817
|
-
const status = await generationStatus({ serviceUrl: url.data, token: token.data.token, verbose: globals.verbose });
|
|
41818
|
-
if (status === "off") ui.rail.line("Project rules are not switched on for this project yet. The built-in rules apply.");
|
|
41819
|
-
if (status === "unsupported") {
|
|
41820
|
-
ui.rail.line(`This Verity service (${terminalText(url.data, 200)}) does not write project rules yet: it is older than this CLI. The built-in rules apply.`);
|
|
41821
|
-
}
|
|
41822
|
-
return status === "on" || status === "unknown";
|
|
41391
|
+
|
|
41392
|
+
// src/lib/prereqs.ts
|
|
41393
|
+
var import_node_child_process21 = require("node:child_process");
|
|
41394
|
+
var import_node_fs73 = require("node:fs");
|
|
41395
|
+
var MIN_NODE_MAJOR = 20;
|
|
41396
|
+
function which(bin) {
|
|
41397
|
+
return whichSync(bin, { real: false });
|
|
41823
41398
|
}
|
|
41824
|
-
|
|
41825
|
-
const
|
|
41826
|
-
const
|
|
41827
|
-
|
|
41828
|
-
|
|
41829
|
-
|
|
41830
|
-
|
|
41831
|
-
|
|
41832
|
-
|
|
41833
|
-
|
|
41834
|
-
|
|
41835
|
-
|
|
41836
|
-
rail.collapse();
|
|
41837
|
-
return k === "enter";
|
|
41838
|
-
}
|
|
41839
|
-
rail.gap();
|
|
41840
|
-
for (const l of profilePreview(profile)) rail.line(l);
|
|
41841
|
-
rail.gap();
|
|
41842
|
-
rail.keys([["Enter", "send"], ["n", "skip"]]);
|
|
41843
|
-
allowed = ["enter", "n"];
|
|
41844
|
-
}
|
|
41399
|
+
function checkNode() {
|
|
41400
|
+
const version = process.version;
|
|
41401
|
+
const major = Number.parseInt(version.slice(1), 10);
|
|
41402
|
+
const ok = Number.isFinite(major) && major >= MIN_NODE_MAJOR;
|
|
41403
|
+
return {
|
|
41404
|
+
id: "node",
|
|
41405
|
+
label: "Node.js",
|
|
41406
|
+
status: ok ? "ok" : "missing",
|
|
41407
|
+
detail: version,
|
|
41408
|
+
remedy: ok ? void 0 : `Node.js ${MIN_NODE_MAJOR}+ required \u2014 update from https://nodejs.org`,
|
|
41409
|
+
required: true
|
|
41410
|
+
};
|
|
41845
41411
|
}
|
|
41846
|
-
|
|
41847
|
-
|
|
41848
|
-
|
|
41849
|
-
|
|
41850
|
-
|
|
41851
|
-
|
|
41852
|
-
|
|
41853
|
-
|
|
41854
|
-
|
|
41855
|
-
|
|
41856
|
-
|
|
41857
|
-
|
|
41858
|
-
|
|
41859
|
-
const started = Date.now();
|
|
41860
|
-
const base = buildStandard({ detected: detectProject() }, loadCatalog());
|
|
41861
|
-
const outcome = await screen.spin(`${s.gray}\u2502${s.reset} `, () => "Writing rules for this repository", generateStandard({
|
|
41862
|
-
serviceUrl: url.data,
|
|
41863
|
-
token: token.data.token,
|
|
41864
|
-
profile,
|
|
41865
|
-
base,
|
|
41866
|
-
projectId: await projectLabel(token.data.token, url.data, globals.verbose),
|
|
41867
|
-
verbose: globals.verbose,
|
|
41868
|
-
replaces
|
|
41869
|
-
}));
|
|
41870
|
-
rail.collapse();
|
|
41871
|
-
if (outcome.status === "exists") return { status: "exists" };
|
|
41872
|
-
if (outcome.status === "disabled") {
|
|
41873
|
-
rail.line("Project rules are not switched on for this project yet. The built-in rules apply.");
|
|
41874
|
-
rail.gap();
|
|
41875
|
-
return { status: "fallback" };
|
|
41876
|
-
}
|
|
41877
|
-
if (outcome.status === "unsupported") {
|
|
41878
|
-
rail.line(`This Verity service (${terminalText(url.data, 200)}) does not write project rules yet: it is older than this CLI. The built-in rules apply.`);
|
|
41879
|
-
rail.gap();
|
|
41880
|
-
return { status: "fallback" };
|
|
41412
|
+
function checkGit() {
|
|
41413
|
+
let detail = "";
|
|
41414
|
+
try {
|
|
41415
|
+
detail = (0, import_node_child_process21.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
41416
|
+
} catch {
|
|
41417
|
+
return {
|
|
41418
|
+
id: "git",
|
|
41419
|
+
label: "git",
|
|
41420
|
+
status: "missing",
|
|
41421
|
+
detail: "not found",
|
|
41422
|
+
remedy: "Install git from https://git-scm.com",
|
|
41423
|
+
required: true
|
|
41424
|
+
};
|
|
41881
41425
|
}
|
|
41882
|
-
|
|
41883
|
-
|
|
41884
|
-
|
|
41885
|
-
|
|
41886
|
-
|
|
41887
|
-
|
|
41426
|
+
return { id: "git", label: "git", status: "ok", detail, required: true };
|
|
41427
|
+
}
|
|
41428
|
+
function checkClaude() {
|
|
41429
|
+
const path = which("claude");
|
|
41430
|
+
return {
|
|
41431
|
+
id: "claude",
|
|
41432
|
+
label: "Claude Code",
|
|
41433
|
+
status: path ? "ok" : "warn",
|
|
41434
|
+
detail: path ?? "not found",
|
|
41435
|
+
remedy: path ? void 0 : "Hooks are wired but need Claude Code to fire \u2014 https://claude.com/claude-code",
|
|
41436
|
+
required: false
|
|
41437
|
+
};
|
|
41438
|
+
}
|
|
41439
|
+
var ANALYSIS_CLI_REMEDY = "Reinstall Verity, which includes it: npm install -g @codacy/verity-cli";
|
|
41440
|
+
function checkAnalysisCli(opts = {}) {
|
|
41441
|
+
const base = { id: "analysis-cli", label: "@codacy/analysis-cli", required: false };
|
|
41442
|
+
const analyzer = resolveAnalyzer(opts);
|
|
41443
|
+
const shown = (opts.onPathDisplay ?? (() => which("codacy-analysis")))();
|
|
41444
|
+
if (!analyzer) {
|
|
41445
|
+
return { ...base, status: "warn", detail: "not found", remedy: `${ANALYSIS_CLI_REMEDY} (static findings are unavailable until then)` };
|
|
41888
41446
|
}
|
|
41889
|
-
|
|
41890
|
-
|
|
41891
|
-
if (outcome.rules.length === 0) {
|
|
41892
|
-
rail.warn("No project rules this time");
|
|
41893
|
-
rail.line("The proposed rules were too generic, or not safe to use. The built-in rules apply.");
|
|
41894
|
-
} else {
|
|
41895
|
-
const n2 = outcome.rules.length;
|
|
41896
|
-
const from = [`${commits} ${commits === 1 ? "commit" : "commits"}`, ...docs ? [`${docs} ${docs === 1 ? "doc" : "docs"}`] : []].join(" and ");
|
|
41897
|
-
rail.done(`${n2} ${n2 === 1 ? "rule" : "rules"} for this repository ${s.gray}\xB7 from ${from} \xB7 ${secs2}s${s.reset}`);
|
|
41898
|
-
await rail.rules(outcome.rules);
|
|
41899
|
-
if (outcome.rejected > 0) rail.line(`${outcome.rejected} proposed ${outcome.rejected === 1 ? "rule was" : "rules were"} left out: too generic, or not safe to use.`);
|
|
41447
|
+
if (analyzer.source === "path") {
|
|
41448
|
+
return { ...base, status: "ok", detail: `${shown ?? analyzer.path} (on PATH)`, source: "path" };
|
|
41900
41449
|
}
|
|
41901
|
-
|
|
41902
|
-
|
|
41903
|
-
|
|
41904
|
-
|
|
41905
|
-
|
|
41450
|
+
const unused = shown && !sameFile(shown, analyzer.path) ? shown : void 0;
|
|
41451
|
+
return {
|
|
41452
|
+
...base,
|
|
41453
|
+
status: "ok",
|
|
41454
|
+
detail: `${analyzer.version} (bundled with Verity)`,
|
|
41455
|
+
source: "bundled",
|
|
41456
|
+
version: analyzer.version,
|
|
41457
|
+
...unused && { unused }
|
|
41458
|
+
};
|
|
41906
41459
|
}
|
|
41907
|
-
|
|
41908
|
-
|
|
41909
|
-
|
|
41910
|
-
|
|
41911
|
-
|
|
41912
|
-
const url = await resolveServiceUrl(opts.globals.serviceUrl);
|
|
41913
|
-
if (!token.ok || !url.ok) return { status: "skipped", why: "not-signed-in" };
|
|
41914
|
-
const found = await fetchRemoteSetup({ token: token.data.token, serviceUrl: url.data, verbose: opts.globals.verbose });
|
|
41915
|
-
if (found.status === "unavailable") return { status: "unavailable", reason: terminalText(found.reason, 300) };
|
|
41916
|
-
if (found.status === "none") return { status: "none" };
|
|
41917
|
-
if (opts.previous?.declinedStandardVersion === found.standard.version) {
|
|
41918
|
-
return { status: "declined-before", version: found.standard.version };
|
|
41460
|
+
function sameFile(a, b) {
|
|
41461
|
+
try {
|
|
41462
|
+
return (0, import_node_fs73.realpathSync)(a) === (0, import_node_fs73.realpathSync)(b);
|
|
41463
|
+
} catch {
|
|
41464
|
+
return false;
|
|
41919
41465
|
}
|
|
41920
|
-
return { status: "found", found, version: found.standard.version };
|
|
41921
41466
|
}
|
|
41922
|
-
|
|
41923
|
-
|
|
41924
|
-
|
|
41925
|
-
|
|
41926
|
-
|
|
41927
|
-
|
|
41928
|
-
const outcome = await adoptRemoteSetup(found, {
|
|
41929
|
-
serviceUrl: url.data,
|
|
41930
|
-
projectId: await projectLabel(token.data.token, url.data, opts.globals.verbose)
|
|
41467
|
+
var INSTALL_TIMEOUT_MS = 12e4;
|
|
41468
|
+
function run3(command, args2, opts = {}) {
|
|
41469
|
+
return new Promise((resolve9) => {
|
|
41470
|
+
const child = (0, import_node_child_process21.spawn)(command, args2, {
|
|
41471
|
+
stdio: opts.inherit ? "inherit" : "pipe",
|
|
41472
|
+
timeout: INSTALL_TIMEOUT_MS
|
|
41931
41473
|
});
|
|
41932
|
-
|
|
41933
|
-
|
|
41934
|
-
|
|
41474
|
+
child.on("error", () => resolve9(false));
|
|
41475
|
+
child.on("close", (code) => resolve9(code === 0));
|
|
41476
|
+
});
|
|
41477
|
+
}
|
|
41478
|
+
async function installAnalysisCli(opts = {}) {
|
|
41479
|
+
const spinner = opts.quiet ? { succeed: () => {
|
|
41480
|
+
}, warn: () => {
|
|
41481
|
+
}, stop: () => {
|
|
41482
|
+
} } : startSpinner("Installing @codacy/analysis-cli");
|
|
41483
|
+
const ok = await run3("npm", ["install", "-g", "@codacy/analysis-cli"]);
|
|
41484
|
+
if (ok) {
|
|
41485
|
+
const check = checkAnalysisCli();
|
|
41486
|
+
if (check.status === "ok") {
|
|
41487
|
+
spinner.succeed("@codacy/analysis-cli installed");
|
|
41488
|
+
return { ...check, justInstalled: true };
|
|
41935
41489
|
}
|
|
41936
|
-
|
|
41937
|
-
|
|
41938
|
-
|
|
41490
|
+
spinner.warn("npm reported success but codacy-analysis is not on PATH");
|
|
41491
|
+
return check;
|
|
41492
|
+
}
|
|
41493
|
+
spinner.stop();
|
|
41494
|
+
{
|
|
41495
|
+
const failed = {
|
|
41496
|
+
id: "analysis-cli",
|
|
41497
|
+
label: "@codacy/analysis-cli",
|
|
41498
|
+
status: "warn",
|
|
41499
|
+
detail: "install failed",
|
|
41500
|
+
remedy: ANALYSIS_CLI_REMEDY,
|
|
41501
|
+
required: false
|
|
41502
|
+
};
|
|
41503
|
+
if (opts.sudo === false || !process.stdin.isTTY || !process.stdout.isTTY) return failed;
|
|
41504
|
+
process.stderr.write(" Retrying with sudo (you may be asked for your password)\u2026\n");
|
|
41505
|
+
const sudoOk = await run3("sudo", ["npm", "install", "-g", "@codacy/analysis-cli"], { inherit: true });
|
|
41506
|
+
if (!sudoOk) return failed;
|
|
41507
|
+
const check = checkAnalysisCli();
|
|
41508
|
+
return check.status === "ok" ? { ...check, justInstalled: true } : check;
|
|
41939
41509
|
}
|
|
41940
41510
|
}
|
|
41941
|
-
async function
|
|
41942
|
-
const
|
|
41943
|
-
|
|
41944
|
-
|
|
41945
|
-
|
|
41946
|
-
|
|
41947
|
-
|
|
41948
|
-
|
|
41949
|
-
rail.line(`Use it to review with the same rules as everyone else. A new one becomes v${team.version + 1} for the whole team.`);
|
|
41950
|
-
rail.gap();
|
|
41951
|
-
rail.keys([["Enter", "use it"], ["n", "create a new one"]]);
|
|
41952
|
-
const k = await screen.key(["enter", "n"], "enter", { freshMs: 300 });
|
|
41953
|
-
rail.collapse();
|
|
41954
|
-
return k === "n" ? "new" : "use";
|
|
41955
|
-
}
|
|
41956
|
-
function sayAdopted(ui, team) {
|
|
41957
|
-
ui.rail.done(`Using your team's Standard v${team.version}`);
|
|
41958
|
-
ui.rail.line(terminalText(team.summary, 300));
|
|
41959
|
-
ui.rail.line(`Edit it in ${STANDARD_FILE}, then share the change: verity standard push`);
|
|
41960
|
-
for (const note of team.notes) ui.rail.line(terminalText(note, 300), "warn");
|
|
41511
|
+
async function checkPrereqs(opts = {}) {
|
|
41512
|
+
const checks = [checkNode(), checkGit(), checkClaude()];
|
|
41513
|
+
let analysis = checkAnalysisCli();
|
|
41514
|
+
if (analysis.status !== "ok" && opts.install) {
|
|
41515
|
+
analysis = await installAnalysisCli();
|
|
41516
|
+
}
|
|
41517
|
+
checks.push(analysis);
|
|
41518
|
+
return { checks, blocked: checks.some((c) => c.required && c.status !== "ok") };
|
|
41961
41519
|
}
|
|
41962
41520
|
|
|
41963
|
-
// src/commands/init/
|
|
41964
|
-
|
|
41965
|
-
const
|
|
41966
|
-
|
|
41967
|
-
|
|
41968
|
-
|
|
41969
|
-
|
|
41970
|
-
|
|
41971
|
-
return { ok: false, version: null };
|
|
41972
|
-
}
|
|
41973
|
-
const pushed = await pushStandard(globals);
|
|
41974
|
-
if (pushed.standardVersion !== null) {
|
|
41975
|
-
await correctVerityMdVersion({
|
|
41976
|
-
standard: result.standard,
|
|
41977
|
-
version: pushed.standardVersion,
|
|
41978
|
-
tools: result.tools,
|
|
41979
|
-
serviceUrl: url.ok ? url.data : void 0
|
|
41980
|
-
});
|
|
41981
|
-
ui.rail.done(`Standard v${pushed.standardVersion} saved`);
|
|
41982
|
-
ui.rail.line(`Your team gets the same rules. Edit them in ${STANDARD_FILE}`);
|
|
41983
|
-
} else {
|
|
41984
|
-
ui.rail.done("Standard saved on this machine");
|
|
41985
|
-
}
|
|
41986
|
-
for (const line2 of pushed.lines) {
|
|
41987
|
-
if (line2.startsWith("Standard pushed")) continue;
|
|
41988
|
-
if (line2.startsWith("Not signed in")) ui.rail.line(`Sign in to share it with your team: verity login, then verity standard push. Edit it in ${STANDARD_FILE}`);
|
|
41989
|
-
else ui.rail.line(terminalText(line2, 600), "warn");
|
|
41990
|
-
}
|
|
41991
|
-
for (const note of result.notes) ui.rail.line(terminalText(note, 300), "warn");
|
|
41992
|
-
return { ok: true, version: pushed.standardVersion };
|
|
41993
|
-
} catch (err2) {
|
|
41994
|
-
ui.rail.warn("No Standard was written");
|
|
41995
|
-
ui.rail.line(terminalText(err2.message, 300));
|
|
41996
|
-
return { ok: false, version: null };
|
|
41997
|
-
}
|
|
41521
|
+
// src/commands/init/steps.ts
|
|
41522
|
+
function languageShares(d) {
|
|
41523
|
+
const total = d.languages.reduce((n2, l) => n2 + (d.languageCounts[l] ?? 0), 0);
|
|
41524
|
+
if (total === 0) return [];
|
|
41525
|
+
const shares = d.languages.map((l) => ({ name: displayName(l), pct: Math.round((d.languageCounts[l] ?? 0) / total * 100) }));
|
|
41526
|
+
const top = shares.slice(0, 2);
|
|
41527
|
+
const rest = 100 - top.reduce((n2, s) => n2 + s.pct, 0);
|
|
41528
|
+
return rest > 0 && shares.length > 2 ? [...top, { name: "Other", pct: rest }] : top;
|
|
41998
41529
|
}
|
|
41999
|
-
|
|
42000
|
-
|
|
42001
|
-
|
|
42002
|
-
|
|
42003
|
-
|
|
42004
|
-
|
|
42005
|
-
|
|
42006
|
-
|
|
42007
|
-
|
|
42008
|
-
|
|
42009
|
-
|
|
42010
|
-
|
|
42011
|
-
|
|
42012
|
-
|
|
42013
|
-
|
|
42014
|
-
|
|
42015
|
-
|
|
42016
|
-
|
|
42017
|
-
|
|
42018
|
-
|
|
42019
|
-
|
|
42020
|
-
|
|
41530
|
+
var NAMES = {
|
|
41531
|
+
typescript: "TypeScript",
|
|
41532
|
+
javascript: "JavaScript",
|
|
41533
|
+
python: "Python",
|
|
41534
|
+
go: "Go",
|
|
41535
|
+
java: "Java",
|
|
41536
|
+
kotlin: "Kotlin",
|
|
41537
|
+
ruby: "Ruby",
|
|
41538
|
+
rust: "Rust",
|
|
41539
|
+
c: "C",
|
|
41540
|
+
cpp: "C++",
|
|
41541
|
+
shell: "Shell",
|
|
41542
|
+
dockerfile: "Dockerfile",
|
|
41543
|
+
csharp: "C#",
|
|
41544
|
+
php: "PHP",
|
|
41545
|
+
swift: "Swift",
|
|
41546
|
+
scala: "Scala",
|
|
41547
|
+
sql: "SQL"
|
|
41548
|
+
};
|
|
41549
|
+
var displayName = (l) => NAMES[l] ?? l.charAt(0).toUpperCase() + l.slice(1);
|
|
41550
|
+
var STACK_NAMES = {
|
|
41551
|
+
"next.js": "Next.js",
|
|
41552
|
+
next: "Next.js",
|
|
41553
|
+
react: "React",
|
|
41554
|
+
vue: "Vue",
|
|
41555
|
+
angular: "Angular",
|
|
41556
|
+
svelte: "Svelte",
|
|
41557
|
+
express: "Express",
|
|
41558
|
+
nestjs: "NestJS",
|
|
41559
|
+
fastify: "Fastify",
|
|
41560
|
+
prisma: "Prisma",
|
|
41561
|
+
django: "Django",
|
|
41562
|
+
flask: "Flask",
|
|
41563
|
+
fastapi: "FastAPI",
|
|
41564
|
+
rails: "Rails",
|
|
41565
|
+
spring: "Spring",
|
|
41566
|
+
vitest: "Vitest",
|
|
41567
|
+
jest: "Jest",
|
|
41568
|
+
mocha: "Mocha",
|
|
41569
|
+
playwright: "Playwright",
|
|
41570
|
+
cypress: "Cypress",
|
|
41571
|
+
supabase: "Supabase",
|
|
41572
|
+
stripe: "Stripe",
|
|
41573
|
+
tailwind: "Tailwind"
|
|
41574
|
+
};
|
|
41575
|
+
async function repoStep(ui, detected) {
|
|
41576
|
+
ui.rail.done("Looked at your repository");
|
|
41577
|
+
await ui.rail.languages(languageShares(detected));
|
|
41578
|
+
const stack = [...detected.frameworks, detected.buildSystem, detected.testFramework].filter(Boolean).map((n2) => STACK_NAMES[n2.toLowerCase()] ?? n2);
|
|
41579
|
+
const files = `${detected.filesScanned.toLocaleString("en-US")} ${detected.filesScanned === 1 ? "file" : "files"}`;
|
|
41580
|
+
ui.rail.line([...stack, files].join(" \xB7 "), stack.length ? "plain" : "muted");
|
|
41581
|
+
ui.rail.gap();
|
|
41582
|
+
ui.rail.settle();
|
|
41583
|
+
}
|
|
41584
|
+
async function prerequisitesStep(ui) {
|
|
41585
|
+
const report = await checkPrereqs({ install: false });
|
|
41586
|
+
const analysis = report.checks.find((c) => c.id === "analysis-cli");
|
|
41587
|
+
if (analysis && analysis.status !== "ok") {
|
|
41588
|
+
const s = ui.screen.s;
|
|
41589
|
+
const installed2 = await ui.screen.spin(`${s.gray}\u2502${s.reset} `, () => "Verity's copy of the Codacy analysis CLI is missing. Installing it (it runs the Trivy security scan)", installAnalysisCli({ quiet: true, sudo: false }));
|
|
41590
|
+
report.checks[report.checks.indexOf(analysis)] = installed2;
|
|
41591
|
+
if (installed2.status === "ok") ui.rail.ticked("Installed the Codacy analysis CLI");
|
|
42021
41592
|
}
|
|
42022
|
-
const
|
|
42023
|
-
|
|
42024
|
-
|
|
42025
|
-
|
|
42026
|
-
if (choice === "use") {
|
|
42027
|
-
const adopted = await writeTeamStandard(team, ctx);
|
|
42028
|
-
if (adopted.status === "adopted") {
|
|
42029
|
-
sayAdopted(ui, adopted);
|
|
42030
|
-
return end({ source: "adopted", version: adopted.version, profile: notAsked });
|
|
42031
|
-
}
|
|
42032
|
-
rail.warn("Could not write the team's Standard here");
|
|
42033
|
-
if (adopted.status === "write-failed") rail.line(terminalText(adopted.reason, 300));
|
|
42034
|
-
return derived(notAsked);
|
|
42035
|
-
}
|
|
42036
|
-
await writeSetupState({ declinedStandardVersion: team.version }).catch(() => {
|
|
42037
|
-
});
|
|
42038
|
-
rail.done(`Creating a new Standard ${s.gray}\xB7 it becomes v${team.version + 1} for the team${ctx.standard ? " (--standard new)" : ""}${s.reset}`);
|
|
42039
|
-
replaces = team.version;
|
|
42040
|
-
} else if (team.status === "declined-before") {
|
|
42041
|
-
rail.line(`This project turned down the team's Standard v${team.version} before, so this run builds its own.`);
|
|
42042
|
-
return derived(notAsked);
|
|
41593
|
+
for (const c of report.checks) {
|
|
41594
|
+
if (c.status === "ok") continue;
|
|
41595
|
+
ui.rail.warn(`${c.label}: ${c.detail}`);
|
|
41596
|
+
if (c.remedy) ui.rail.line(c.remedy);
|
|
42043
41597
|
}
|
|
42044
|
-
|
|
42045
|
-
|
|
42046
|
-
|
|
42047
|
-
|
|
41598
|
+
ui.rail.settle();
|
|
41599
|
+
return !report.checks.some((c) => c.required && c.status !== "ok");
|
|
41600
|
+
}
|
|
41601
|
+
async function telemetryStep(ui, answer, resumeDeferred, globals) {
|
|
41602
|
+
if (answer === "already-on") return "enabled";
|
|
41603
|
+
if (answer === "no") return "declined";
|
|
41604
|
+
if (answer === "turn-off") {
|
|
41605
|
+
const removed = await uninstallTelemetry();
|
|
41606
|
+
if (removed.ok) ui.rail.done("Cost & usage telemetry turned off (--telemetry off)");
|
|
41607
|
+
else ui.rail.warn(`Could not turn telemetry off: ${terminalText(removed.error, 200)}`);
|
|
41608
|
+
ui.rail.settle();
|
|
41609
|
+
return "declined";
|
|
42048
41610
|
}
|
|
42049
|
-
if (
|
|
42050
|
-
|
|
42051
|
-
const
|
|
42052
|
-
|
|
42053
|
-
|
|
42054
|
-
|
|
42055
|
-
|
|
42056
|
-
rail.line(terminalText(err2.message, 200));
|
|
42057
|
-
return derived({ outcome: "failed", detail: "error" });
|
|
41611
|
+
if (answer !== "yes" && !resumeDeferred) return void 0;
|
|
41612
|
+
const token = await resolveToken(globals.token);
|
|
41613
|
+
const url = await resolveServiceUrl(globals.serviceUrl);
|
|
41614
|
+
if (!token.ok || !url.ok) {
|
|
41615
|
+
ui.rail.line("Telemetry needs sign-in. Then run: verity telemetry install", "warn");
|
|
41616
|
+
ui.rail.settle();
|
|
41617
|
+
return "deferred";
|
|
42058
41618
|
}
|
|
42059
|
-
|
|
42060
|
-
|
|
42061
|
-
|
|
42062
|
-
|
|
42063
|
-
detail = "given";
|
|
42064
|
-
} else if (ui.asking) {
|
|
42065
|
-
send = await askToSend(ui, profile);
|
|
42066
|
-
detail = send ? "asked" : "declined";
|
|
41619
|
+
const installed2 = await installTelemetry(url.data);
|
|
41620
|
+
if (installed2.ok) {
|
|
41621
|
+
ui.rail.done("Cost & usage telemetry on");
|
|
41622
|
+
ui.rail.line("It starts with your next Claude Code session. See cost and usage at /usage.");
|
|
42067
41623
|
} else {
|
|
42068
|
-
|
|
42069
|
-
|
|
41624
|
+
ui.rail.warn(`Could not turn telemetry on: ${terminalText(installed2.error, 200)}`);
|
|
41625
|
+
}
|
|
41626
|
+
ui.rail.gap();
|
|
41627
|
+
ui.rail.settle();
|
|
41628
|
+
return installed2.ok ? "enabled" : "deferred";
|
|
41629
|
+
}
|
|
41630
|
+
function signInDetail(outcome) {
|
|
41631
|
+
if (outcome === "already" || outcome === "new") return ["ok", outcome];
|
|
41632
|
+
if (outcome === "declined" || outcome === "unattended") return ["skipped", outcome];
|
|
41633
|
+
return ["failed", outcome === "unreachable" ? "unreachable" : "error"];
|
|
41634
|
+
}
|
|
41635
|
+
function formalDefault(previous, opts = {}) {
|
|
41636
|
+
const cfg = readProjectConfig();
|
|
41637
|
+
if (previous?.init?.completed_at || opts.setUpBefore || cfg.formal !== void 0 || cfg.formal_refused) return false;
|
|
41638
|
+
writeProjectConfig({ formal: "push" });
|
|
41639
|
+
return true;
|
|
41640
|
+
}
|
|
41641
|
+
function sayFormalOn(ui) {
|
|
41642
|
+
ui.rail.done("Formal verification at push: on");
|
|
41643
|
+
ui.rail.line("At push, Verity checks a small model of the riskiest changed function and replays any counterexample on the real code. That function's source is sent to Verity for the replay.");
|
|
41644
|
+
ui.rail.line("Only a reproduced bug can block, and most pushes get no model. Turn it off: verity config formal off");
|
|
41645
|
+
}
|
|
41646
|
+
function backgroundStep(ui, opts) {
|
|
41647
|
+
const { rail } = ui;
|
|
41648
|
+
const formal = readProjectConfig().formal;
|
|
41649
|
+
const said = opts.formalNew === true && formal === "push";
|
|
41650
|
+
if (said) sayFormalOn(ui);
|
|
41651
|
+
const lean = shouldStartBackgroundInstall({ formal });
|
|
41652
|
+
const leanStarted = lean.start && startBackgroundInstall({ cli: opts.cli });
|
|
41653
|
+
const trivy = startTrivyPrewarm();
|
|
41654
|
+
const parts2 = [
|
|
41655
|
+
...trivy ? ["Trivy security scanner"] : [],
|
|
41656
|
+
...leanStarted ? ["Lean 4 for formal checks at push (800 MB)"] : [],
|
|
41657
|
+
...!lean.start && lean.why === "running" ? ["Lean 4 (already downloading)"] : []
|
|
41658
|
+
];
|
|
41659
|
+
if (parts2.length) {
|
|
41660
|
+
if (said) rail.gap();
|
|
41661
|
+
rail.done("Getting ready in the background");
|
|
41662
|
+
rail.line(parts2.join(" \xB7 "));
|
|
41663
|
+
if (leanStarted) rail.line(said ? "Progress: verity lean status" : "Progress: verity lean status. Turn formal checks off: verity config formal off");
|
|
41664
|
+
} else if (!lean.start && lean.why === "installed" && formal === "push" && !said) {
|
|
41665
|
+
rail.done("Formal verification at push: on");
|
|
42070
41666
|
}
|
|
42071
|
-
if (!
|
|
42072
|
-
rail.warn("
|
|
42073
|
-
rail.line(detail
|
|
42074
|
-
|
|
42075
|
-
rail.
|
|
42076
|
-
return derived({ outcome: "skipped", detail });
|
|
41667
|
+
if (!lean.start && lean.why === "disk") {
|
|
41668
|
+
rail.warn("Formal verification is on, but Lean 4 needs more disk space");
|
|
41669
|
+
rail.line(`${lean.detail}. Free some space, then run: verity lean install`);
|
|
41670
|
+
} else if (!lean.start && lean.why === "no-release" && formal === "push") {
|
|
41671
|
+
rail.line("Lean 4 has no release for this machine, so the formal check at push is skipped.");
|
|
42077
41672
|
}
|
|
42078
|
-
if (
|
|
42079
|
-
else rail.done(`Profile sent ${s.gray}\xB7 no source code${s.reset}`);
|
|
42080
|
-
rail.gap();
|
|
41673
|
+
if (parts2.length || said || !lean.start && lean.why === "disk") rail.gap();
|
|
42081
41674
|
rail.settle();
|
|
42082
|
-
const generated = await generate(ui, ctx.globals, profile, replaces);
|
|
42083
|
-
if (generated.status === "created") {
|
|
42084
|
-
return end({ source: "generated", version: generated.version, profile: { outcome: "ok", detail } });
|
|
42085
|
-
}
|
|
42086
|
-
if (generated.status === "exists") {
|
|
42087
|
-
const again = await adoptTeamStandard(ctx);
|
|
42088
|
-
if (again.status === "adopted") {
|
|
42089
|
-
sayAdopted(ui, again);
|
|
42090
|
-
return end({ source: "adopted", version: again.version, profile: { outcome: "ok", detail } });
|
|
42091
|
-
}
|
|
42092
|
-
}
|
|
42093
|
-
return derived({ outcome: "ok", detail });
|
|
42094
41675
|
}
|
|
42095
41676
|
|
|
42096
|
-
// src/commands/init/
|
|
42097
|
-
|
|
41677
|
+
// src/commands/init/scaffold.ts
|
|
41678
|
+
var import_promises17 = require("node:fs/promises");
|
|
41679
|
+
async function scaffoldStep(ui, given = {}) {
|
|
42098
41680
|
const { rail } = ui;
|
|
42099
|
-
|
|
42100
|
-
|
|
42101
|
-
|
|
42102
|
-
|
|
42103
|
-
rail.
|
|
42104
|
-
|
|
41681
|
+
await (0, import_promises17.mkdir)(VERITY_DIR, { recursive: true });
|
|
41682
|
+
await ensureMemoryDir();
|
|
41683
|
+
const ignore = ensureVerityGitignore();
|
|
41684
|
+
if (ignore === "failed") {
|
|
41685
|
+
rail.warn(".gitignore: could not write the Verity block");
|
|
41686
|
+
rail.line("Add it by hand: .verity/ holds copies of analyzed files, including any secret the gate flagged.");
|
|
41687
|
+
} else if (ignore === "conflict") {
|
|
41688
|
+
rail.warn(".gitignore: git still ignores .verity/standard.yaml");
|
|
41689
|
+
rail.line("A global or nested .gitignore covers it, so the Standard cannot be committed. Check: git check-ignore -v .verity/standard.yaml");
|
|
41690
|
+
} else if (ignore === "memory-tracked") {
|
|
41691
|
+
rail.warn("This project commits its knowledge graph");
|
|
42105
41692
|
}
|
|
42106
|
-
|
|
42107
|
-
|
|
42108
|
-
if (
|
|
42109
|
-
|
|
42110
|
-
|
|
41693
|
+
const trackedMemory = memoryOptOut() ? [] : committedMemoryFiles();
|
|
41694
|
+
if (trackedMemory.length > 0) {
|
|
41695
|
+
if (given.untrackMemory) {
|
|
41696
|
+
const result = fenceMemory();
|
|
41697
|
+
if (result.ok) rail.line(`Untracked ${result.untracked} knowledge-graph file(s) (staged). Commit to finish.`);
|
|
41698
|
+
else rail.line("Could not untrack the knowledge graph. Run: verity memory untrack", "warn");
|
|
41699
|
+
} else {
|
|
41700
|
+
rail.warn(`${trackedMemory.length} knowledge-graph file(s) are tracked in git`);
|
|
41701
|
+
rail.line("They show up in every diff. Stop with: verity memory untrack (or keep them on purpose: verity memory track)");
|
|
41702
|
+
}
|
|
42111
41703
|
}
|
|
42112
|
-
|
|
42113
|
-
|
|
42114
|
-
|
|
42115
|
-
|
|
42116
|
-
|
|
42117
|
-
|
|
42118
|
-
|
|
42119
|
-
|
|
42120
|
-
}
|
|
42121
|
-
|
|
42122
|
-
// src/commands/migrate.ts
|
|
42123
|
-
var import_node_fs76 = require("node:fs");
|
|
42124
|
-
var import_node_path64 = require("node:path");
|
|
42125
|
-
var import_node_child_process22 = require("node:child_process");
|
|
42126
|
-
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
42127
|
-
function defaultNpmRemover(pkg) {
|
|
42128
|
-
(0, import_node_child_process22.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
|
|
42129
|
-
}
|
|
42130
|
-
function git2(cwd, args2) {
|
|
42131
|
-
(0, import_node_child_process22.execFileSync)("git", hardenedGitArgv(args2), { cwd, stdio: "pipe", env: hardenedGitEnv() });
|
|
42132
|
-
}
|
|
42133
|
-
function isGitTracked(cwd, relPath) {
|
|
42134
|
-
try {
|
|
42135
|
-
git2(cwd, ["ls-files", "--error-unmatch", "--", relPath]);
|
|
42136
|
-
return true;
|
|
42137
|
-
} catch {
|
|
42138
|
-
return false;
|
|
41704
|
+
const tracked = committedVerityState();
|
|
41705
|
+
if (tracked.length > 0) {
|
|
41706
|
+
if (given.untrackState) {
|
|
41707
|
+
const result = untrackVerityState();
|
|
41708
|
+
if (result === "untracked") rail.line("Untracked the Verity state files (staged). Commit to finish.");
|
|
41709
|
+
else if (result === "failed") rail.line("Could not untrack them. Run: git rm -r --cached .verity && git add .verity/standard.yaml", "warn");
|
|
41710
|
+
} else {
|
|
41711
|
+
rail.warn(`${tracked.length} Verity state file(s) are tracked in git`);
|
|
41712
|
+
rail.line(`For example ${tracked[0]}. Fix: git rm -r --cached .verity && git add .verity/standard.yaml`);
|
|
41713
|
+
}
|
|
42139
41714
|
}
|
|
42140
|
-
}
|
|
42141
|
-
function isGitRepo(cwd) {
|
|
42142
41715
|
try {
|
|
42143
|
-
|
|
42144
|
-
|
|
42145
|
-
|
|
42146
|
-
return false;
|
|
41716
|
+
await ensureClaudeMdPointer();
|
|
41717
|
+
} catch (err2) {
|
|
41718
|
+
rail.line(`Could not update CLAUDE.md: ${err2.message}`, "warn");
|
|
42147
41719
|
}
|
|
41720
|
+
const cfg = readProjectConfig();
|
|
41721
|
+
if (cfg.formal_refused) rail.line(`Formal verification: off \u2014 ${formalRefusalMessage(cfg.formal_refused)}`, "warn");
|
|
41722
|
+
rail.settle();
|
|
42148
41723
|
}
|
|
42149
|
-
|
|
42150
|
-
|
|
42151
|
-
|
|
42152
|
-
|
|
42153
|
-
|
|
42154
|
-
|
|
42155
|
-
|
|
42156
|
-
await
|
|
42157
|
-
|
|
42158
|
-
|
|
42159
|
-
|
|
42160
|
-
|
|
42161
|
-
return
|
|
41724
|
+
|
|
41725
|
+
// src/commands/init/wiring.ts
|
|
41726
|
+
var import_node_fs74 = require("node:fs");
|
|
41727
|
+
var import_promises18 = require("node:fs/promises");
|
|
41728
|
+
var import_node_path63 = require("node:path");
|
|
41729
|
+
var DEFAULT_MOMENTS = ["stop", "pre-commit", "pre-push"];
|
|
41730
|
+
async function momentsFromInstalledHooks() {
|
|
41731
|
+
const status = await checkAllVerityHooks();
|
|
41732
|
+
const wired = [];
|
|
41733
|
+
if (status.stop) wired.push("stop");
|
|
41734
|
+
if (status.guardOn.includes("commit")) wired.push("pre-commit");
|
|
41735
|
+
if (status.guardOn.includes("push")) wired.push("pre-push");
|
|
41736
|
+
return wired.length > 0 ? wired : DEFAULT_MOMENTS;
|
|
42162
41737
|
}
|
|
42163
|
-
function
|
|
42164
|
-
const
|
|
42165
|
-
|
|
42166
|
-
if (
|
|
42167
|
-
|
|
42168
|
-
|
|
42169
|
-
if ((0, import_node_fs76.existsSync)(gateDir) && (0, import_node_fs76.existsSync)(verityDir)) {
|
|
42170
|
-
return migrateProjectDirCarry(gateDir, verityDir, actions);
|
|
41738
|
+
async function askSetupQuestions(_defaultsOnly, previous, given = {}) {
|
|
41739
|
+
const moments = given.moments ?? (previous?.moments?.length ? previous.moments : await momentsFromInstalledHooks());
|
|
41740
|
+
let telemetry = "not-asked";
|
|
41741
|
+
if (given.telemetry !== void 0) {
|
|
41742
|
+
const enabled = (await checkTelemetry()).enabled;
|
|
41743
|
+
telemetry = given.telemetry ? enabled ? "already-on" : "yes" : enabled ? "turn-off" : "no";
|
|
42171
41744
|
}
|
|
42172
|
-
return
|
|
41745
|
+
return { moments, telemetry };
|
|
42173
41746
|
}
|
|
42174
|
-
function
|
|
42175
|
-
|
|
42176
|
-
if (
|
|
42177
|
-
|
|
42178
|
-
|
|
42179
|
-
"Migrating .gate \u2192 .verity with `git mv` while the index has staged changes \u2014 the rename will be staged alongside them. Review `git status` before committing."
|
|
42180
|
-
);
|
|
42181
|
-
}
|
|
42182
|
-
try {
|
|
42183
|
-
git2(root, ["mv", "--", ".gate", ".verity"]);
|
|
42184
|
-
actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
|
|
42185
|
-
moved = true;
|
|
42186
|
-
} catch {
|
|
42187
|
-
}
|
|
42188
|
-
}
|
|
42189
|
-
if (moved) {
|
|
42190
|
-
if ((0, import_node_fs76.existsSync)(gateDir)) {
|
|
42191
|
-
const carried = carryLegacyContents(gateDir, verityDir);
|
|
42192
|
-
if (carried > 0) {
|
|
42193
|
-
actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
|
|
42194
|
-
}
|
|
42195
|
-
try {
|
|
42196
|
-
(0, import_node_fs76.rmSync)(gateDir, { recursive: true, force: true });
|
|
42197
|
-
} catch {
|
|
42198
|
-
}
|
|
42199
|
-
}
|
|
42200
|
-
return true;
|
|
42201
|
-
}
|
|
42202
|
-
moveDir(gateDir, verityDir);
|
|
42203
|
-
actions.push("Moved .gate/ \u2192 .verity/");
|
|
42204
|
-
return true;
|
|
41747
|
+
function parseTelemetryAnswer(raw) {
|
|
41748
|
+
const v = raw.trim().toLowerCase();
|
|
41749
|
+
if (v === "on") return true;
|
|
41750
|
+
if (v === "off") return false;
|
|
41751
|
+
return null;
|
|
42205
41752
|
}
|
|
42206
|
-
function
|
|
42207
|
-
const
|
|
42208
|
-
|
|
42209
|
-
|
|
42210
|
-
|
|
42211
|
-
|
|
42212
|
-
|
|
42213
|
-
|
|
42214
|
-
|
|
42215
|
-
|
|
41753
|
+
function planMoments(answers, opts) {
|
|
41754
|
+
const existing = readProjectConfig();
|
|
41755
|
+
const keptUserChoice = !opts.given && existing.git_moments_source === "user";
|
|
41756
|
+
const gitMoments = keptUserChoice ? existing.git_moments : [
|
|
41757
|
+
...answers.includes("pre-commit") ? ["commit"] : [],
|
|
41758
|
+
...answers.includes("pre-push") ? ["push"] : []
|
|
41759
|
+
];
|
|
41760
|
+
const turnOn = keptUserChoice && existing.turn_review ? existing.turn_review === "on" : answers.includes("stop");
|
|
41761
|
+
return {
|
|
41762
|
+
momentIds: [
|
|
41763
|
+
...turnOn ? ["stop"] : [],
|
|
41764
|
+
...gitMoments.includes("commit") ? ["pre-commit"] : [],
|
|
41765
|
+
...gitMoments.includes("push") ? ["pre-push"] : []
|
|
41766
|
+
],
|
|
41767
|
+
gitMoments,
|
|
41768
|
+
turnOn,
|
|
41769
|
+
keptUserChoice
|
|
41770
|
+
};
|
|
42216
41771
|
}
|
|
42217
|
-
function
|
|
42218
|
-
|
|
42219
|
-
|
|
42220
|
-
|
|
42221
|
-
|
|
42222
|
-
|
|
42223
|
-
|
|
42224
|
-
|
|
42225
|
-
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
42226
|
-
return;
|
|
41772
|
+
function momentsStep(ui, plan) {
|
|
41773
|
+
const said = describeReviewMoments(plan.momentIds);
|
|
41774
|
+
if (plan.momentIds.length === 0) {
|
|
41775
|
+
ui.rail.warn("Verity reviews nothing");
|
|
41776
|
+
ui.rail.line("Code changes are not reviewed. Turn the moments on: verity config moments turn,commit,push", "warn");
|
|
41777
|
+
} else {
|
|
41778
|
+
ui.rail.done(`Verity reviews ${said}`);
|
|
41779
|
+
ui.rail.line("To change it, run verity config moments, or just ask Claude.");
|
|
42227
41780
|
}
|
|
42228
|
-
|
|
42229
|
-
|
|
42230
|
-
actions.push(
|
|
42231
|
-
`Merged ${added} legacy credential line(s) from ~/.gate/credentials into ~/.verity/credentials`
|
|
42232
|
-
);
|
|
41781
|
+
if (plan.keptUserChoice) {
|
|
41782
|
+
ui.rail.line(`Keeping the git moments you set: ${plan.gitMoments.join(", ") || "none"} (this run asked no questions, so it does not overrule them).`);
|
|
42233
41783
|
}
|
|
41784
|
+
ui.rail.gap();
|
|
41785
|
+
ui.rail.settle();
|
|
42234
41786
|
}
|
|
42235
|
-
|
|
42236
|
-
|
|
42237
|
-
|
|
42238
|
-
|
|
42239
|
-
|
|
42240
|
-
|
|
42241
|
-
|
|
41787
|
+
var SKILLS = [
|
|
41788
|
+
"verity-setup",
|
|
41789
|
+
"verity-analyze",
|
|
41790
|
+
"verity-status",
|
|
41791
|
+
"verity-feedback",
|
|
41792
|
+
"verity-learn",
|
|
41793
|
+
"verity-memory",
|
|
41794
|
+
"verity-insights",
|
|
41795
|
+
"verity-reflect",
|
|
41796
|
+
"verity-init",
|
|
41797
|
+
"verity-prove"
|
|
41798
|
+
];
|
|
41799
|
+
function resolveDataDir2() {
|
|
41800
|
+
const candidates2 = [
|
|
41801
|
+
(0, import_node_path63.join)(__dirname, "..", "data"),
|
|
41802
|
+
(0, import_node_path63.join)(__dirname, "..", "..", "data"),
|
|
41803
|
+
(0, import_node_path63.join)(process.cwd(), "cli", "data")
|
|
41804
|
+
];
|
|
41805
|
+
for (const candidate of candidates2) {
|
|
41806
|
+
if ((0, import_node_fs74.existsSync)((0, import_node_path63.join)(candidate, "skills"))) return candidate;
|
|
42242
41807
|
}
|
|
41808
|
+
throw new Error("Could not find Verity skill data. Reinstall: npm install -g @codacy/verity-cli");
|
|
42243
41809
|
}
|
|
42244
|
-
async function
|
|
42245
|
-
const
|
|
42246
|
-
|
|
42247
|
-
|
|
41810
|
+
async function skillIsCurrent(src, dest) {
|
|
41811
|
+
const list2 = (dir) => {
|
|
41812
|
+
const out2 = [];
|
|
41813
|
+
const walk2 = (d, prefix) => {
|
|
41814
|
+
for (const e of (0, import_node_fs74.readdirSync)(d, { withFileTypes: true })) {
|
|
41815
|
+
const rel = prefix ? `${prefix}/${e.name}` : e.name;
|
|
41816
|
+
if (e.isDirectory()) walk2((0, import_node_path63.join)(d, e.name), rel);
|
|
41817
|
+
else if (e.isFile()) out2.push(rel);
|
|
41818
|
+
}
|
|
41819
|
+
};
|
|
41820
|
+
walk2(dir, "");
|
|
41821
|
+
return out2.sort();
|
|
41822
|
+
};
|
|
42248
41823
|
try {
|
|
42249
|
-
|
|
42250
|
-
|
|
42251
|
-
|
|
42252
|
-
|
|
41824
|
+
const shipped = list2(src);
|
|
41825
|
+
if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
|
|
41826
|
+
for (const rel of shipped) {
|
|
41827
|
+
if (await (0, import_promises18.readFile)((0, import_node_path63.join)(src, rel), "utf-8") !== await (0, import_promises18.readFile)((0, import_node_path63.join)(dest, rel), "utf-8")) return false;
|
|
41828
|
+
}
|
|
41829
|
+
return true;
|
|
41830
|
+
} catch {
|
|
41831
|
+
return false;
|
|
42253
41832
|
}
|
|
42254
41833
|
}
|
|
42255
|
-
function
|
|
42256
|
-
const
|
|
42257
|
-
const
|
|
42258
|
-
|
|
42259
|
-
|
|
42260
|
-
|
|
42261
|
-
|
|
42262
|
-
|
|
42263
|
-
|
|
42264
|
-
} catch {
|
|
41834
|
+
async function installSkills(force) {
|
|
41835
|
+
const source2 = (0, import_node_path63.join)(resolveDataDir2(), "skills");
|
|
41836
|
+
const missing = [];
|
|
41837
|
+
for (const skill of SKILLS) {
|
|
41838
|
+
const src = (0, import_node_path63.join)(source2, skill);
|
|
41839
|
+
const dest = (0, import_node_path63.join)(".claude/skills", skill);
|
|
41840
|
+
if (!(0, import_node_fs74.existsSync)(src)) {
|
|
41841
|
+
missing.push(skill);
|
|
41842
|
+
continue;
|
|
42265
41843
|
}
|
|
41844
|
+
if ((0, import_node_fs74.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) continue;
|
|
41845
|
+
await (0, import_promises18.mkdir)(dest, { recursive: true });
|
|
41846
|
+
await (0, import_promises18.cp)(src, dest, { recursive: true, force: true });
|
|
42266
41847
|
}
|
|
42267
|
-
|
|
42268
|
-
const content = readFileSyncSafe(verityMd);
|
|
42269
|
-
const refreshed = content.split("GATE.md").join("VERITY.md");
|
|
42270
|
-
if (refreshed !== content) (0, import_node_fs76.writeFileSync)(verityMd, refreshed);
|
|
42271
|
-
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
42272
|
-
}
|
|
42273
|
-
async function migrateLocalTelemetry(root, actions) {
|
|
42274
|
-
const { removed } = await healLocalTelemetry(root);
|
|
42275
|
-
if (removed.length === 0) return;
|
|
42276
|
-
actions.push(
|
|
42277
|
-
`Removed ${removed.length} old Verity telemetry key(s) from ${SETTINGS_LOCAL_FILE2} (Claude Code ignores them there). Run "verity telemetry install" to export from this machine.`
|
|
42278
|
-
);
|
|
41848
|
+
return missing;
|
|
42279
41849
|
}
|
|
42280
|
-
function
|
|
42281
|
-
|
|
42282
|
-
|
|
42283
|
-
|
|
42284
|
-
|
|
42285
|
-
} catch (err2) {
|
|
42286
|
-
printWarn(
|
|
42287
|
-
`Could not remove ${LEGACY_NPM_PACKAGE} (${err2.message}). Remove it manually: npm rm -g ${LEGACY_NPM_PACKAGE}`
|
|
42288
|
-
);
|
|
41850
|
+
async function hooksStep(ui, ctx) {
|
|
41851
|
+
const { rail } = ui;
|
|
41852
|
+
if (ctx.staleMarker) rail.line(`Cleared a stale plugin marker \u2014 ${ctx.staleMarker} is no longer installed.`);
|
|
41853
|
+
if (!ctx.plan.keptUserChoice) {
|
|
41854
|
+
writeProjectConfig({ git_moments: ctx.plan.gitMoments, git_moments_source: "init", turn_review: ctx.plan.turnOn ? "on" : "off" });
|
|
42289
41855
|
}
|
|
41856
|
+
if (ctx.pluginMode) {
|
|
41857
|
+
rail.done(`Hooks here: the Verity Claude Code plugin${ctx.pluginWhere}`);
|
|
41858
|
+
rail.line("Skills and hooks come from the plugin, so this run installs neither, and removes any this project still has from an npm install.");
|
|
41859
|
+
rail.line("Want this project to own its own copy instead? Uninstall the plugin, or use --no-plugin.");
|
|
41860
|
+
const settings = await readSettings();
|
|
41861
|
+
const stripped = removeVerityHooks(settings);
|
|
41862
|
+
if (JSON.stringify(settings.hooks ?? {}) !== JSON.stringify(stripped.hooks ?? {})) {
|
|
41863
|
+
await writeSettings(stripped);
|
|
41864
|
+
rail.line("Removed this project's Verity hooks from .claude/settings.json. The plugin wires them now, so each turn is reviewed once.");
|
|
41865
|
+
}
|
|
41866
|
+
const removed = removeProjectSkills();
|
|
41867
|
+
if (removed.length > 0) rail.line(`Removed ${removed.length} project-local skill${removed.length === 1 ? "" : "s"} that the plugin supersedes.`);
|
|
41868
|
+
} else {
|
|
41869
|
+
if (ctx.overridingPlugin) {
|
|
41870
|
+
rail.warn("Hooks here: this project's .claude/settings.json (--no-plugin)");
|
|
41871
|
+
rail.line("The Verity plugin is active on this machine and would own them. Both sets fire; the settings.json copy stands down at run time, so each turn is reviewed once.");
|
|
41872
|
+
} else {
|
|
41873
|
+
rail.done("Hooks here: this project's .claude/settings.json");
|
|
41874
|
+
}
|
|
41875
|
+
const missing = await installSkills(ctx.force);
|
|
41876
|
+
if (missing.length > 0) rail.line(`Skill data not found: ${missing.join(", ")}`, "warn");
|
|
41877
|
+
await applyMomentSelection(ctx.plan.momentIds);
|
|
41878
|
+
}
|
|
41879
|
+
rail.gap();
|
|
41880
|
+
rail.settle();
|
|
42290
41881
|
}
|
|
42291
|
-
|
|
42292
|
-
|
|
42293
|
-
|
|
42294
|
-
|
|
42295
|
-
|
|
42296
|
-
}
|
|
42297
|
-
|
|
42298
|
-
|
|
42299
|
-
|
|
42300
|
-
|
|
42301
|
-
|
|
42302
|
-
|
|
42303
|
-
|
|
42304
|
-
|
|
42305
|
-
|
|
42306
|
-
|
|
42307
|
-
toAppend.push(line2.replace(/\r$/, ""));
|
|
41882
|
+
|
|
41883
|
+
// src/commands/init/sign-in.ts
|
|
41884
|
+
var import_node_child_process22 = require("node:child_process");
|
|
41885
|
+
async function classifyExistingLogin(serviceUrl, remote, opts) {
|
|
41886
|
+
const existing = await resolveToken(opts.token);
|
|
41887
|
+
if (!existing.ok) return { kind: "none" };
|
|
41888
|
+
const who = await whoami(existing.data.token, serviceUrl, opts.verbose);
|
|
41889
|
+
if (who.ok && who.data.logged_in) {
|
|
41890
|
+
return {
|
|
41891
|
+
kind: "signed-in",
|
|
41892
|
+
identity: terminalText(who.data.email ?? `user #${who.data.user_id}`, 120),
|
|
41893
|
+
// Being logged in does not mean THIS repo is covered (VRT-83): grants come
|
|
41894
|
+
// from the login sweep. grant_status is non-null only when a grant exists.
|
|
41895
|
+
covered: remote ? who.data.grant_status != null : null,
|
|
41896
|
+
nudge: reverifyNudge(who.data)
|
|
41897
|
+
};
|
|
42308
41898
|
}
|
|
42309
|
-
if (
|
|
42310
|
-
const
|
|
42311
|
-
(
|
|
41899
|
+
if (!who.ok) {
|
|
41900
|
+
const denial = authDenialRemedy(who.error);
|
|
41901
|
+
if (denial) return { kind: "rejected", code: denial.code, remedy: denial.remedy };
|
|
41902
|
+
const identity = existing.data.userId != null ? terminalText(existing.data.email ?? `user #${existing.data.userId}`, 120) : null;
|
|
41903
|
+
return { kind: "cached", identity, detail: terminalText(who.error, 200) };
|
|
42312
41904
|
}
|
|
42313
|
-
|
|
42314
|
-
return toAppend.length;
|
|
41905
|
+
return { kind: "anonymous" };
|
|
42315
41906
|
}
|
|
42316
|
-
function
|
|
41907
|
+
function gitRemote() {
|
|
42317
41908
|
try {
|
|
42318
|
-
return (0,
|
|
41909
|
+
return (0, import_node_child_process22.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
42319
41910
|
} catch {
|
|
42320
41911
|
return "";
|
|
42321
41912
|
}
|
|
42322
41913
|
}
|
|
42323
|
-
function
|
|
42324
|
-
|
|
42325
|
-
|
|
42326
|
-
return false;
|
|
42327
|
-
} catch {
|
|
42328
|
-
return true;
|
|
42329
|
-
}
|
|
42330
|
-
}
|
|
42331
|
-
function moveDir(from, to) {
|
|
42332
|
-
try {
|
|
42333
|
-
(0, import_node_fs76.renameSync)(from, to);
|
|
42334
|
-
} catch (err2) {
|
|
42335
|
-
if (err2.code !== "EXDEV") throw err2;
|
|
42336
|
-
(0, import_node_fs76.cpSync)(from, to, { recursive: true });
|
|
42337
|
-
(0, import_node_fs76.rmSync)(from, { recursive: true, force: true });
|
|
42338
|
-
}
|
|
41914
|
+
function sayWhatASkipCosts(ui) {
|
|
41915
|
+
ui.rail.line("Without sign-in, only the local security scan runs. AI review needs sign-in.");
|
|
41916
|
+
ui.rail.line("Sign in later: verity login");
|
|
42339
41917
|
}
|
|
42340
|
-
function
|
|
42341
|
-
|
|
42342
|
-
(
|
|
42343
|
-
}
|
|
42344
|
-
|
|
42345
|
-
|
|
42346
|
-
(
|
|
41918
|
+
function coverageLines(ui, repo, covered) {
|
|
41919
|
+
if (covered === null) {
|
|
41920
|
+
ui.rail.line("This folder has no git remote, so there is no project to sync to.");
|
|
41921
|
+
} else if (covered) {
|
|
41922
|
+
ui.rail.line(`${repo} is covered.`);
|
|
41923
|
+
} else {
|
|
41924
|
+
ui.rail.line(`${repo} is not covered by your Verity access, so nothing syncs.`, "warn");
|
|
41925
|
+
ui.rail.line("Give the Verity GitHub App access to it, then run: verity login");
|
|
42347
41926
|
}
|
|
42348
41927
|
}
|
|
42349
|
-
function
|
|
42350
|
-
|
|
42351
|
-
|
|
42352
|
-
const
|
|
42353
|
-
|
|
42354
|
-
|
|
42355
|
-
|
|
42356
|
-
|
|
42357
|
-
|
|
41928
|
+
async function signInStep(ui, resolution, opts) {
|
|
41929
|
+
const { rail, screen } = ui;
|
|
41930
|
+
const s = screen.s;
|
|
41931
|
+
const end = (outcome) => {
|
|
41932
|
+
rail.gap();
|
|
41933
|
+
rail.settle();
|
|
41934
|
+
return outcome;
|
|
41935
|
+
};
|
|
41936
|
+
let named = false;
|
|
41937
|
+
const nameTarget = () => {
|
|
41938
|
+
if (named) return;
|
|
41939
|
+
named = true;
|
|
41940
|
+
rail.note(`Verity service: ${terminalText(resolution.url, 200)}${resolution.source === "default" ? " (the default)" : ` (from ${resolution.source})`}`);
|
|
41941
|
+
};
|
|
41942
|
+
if ((await resolveToken(opts.token)).ok) nameTarget();
|
|
41943
|
+
const heal = await maybeHealServiceUrl(resolution, opts.verbose);
|
|
41944
|
+
const serviceUrl = heal.serviceUrl;
|
|
41945
|
+
const remote = gitRemote();
|
|
41946
|
+
if (heal.healed) {
|
|
41947
|
+
rail.warn("The saved Verity service no longer answers");
|
|
41948
|
+
rail.line("Sign in again to repair ~/.verity/credentials.");
|
|
41949
|
+
} else {
|
|
41950
|
+
const state = await classifyExistingLogin(serviceUrl, remote, opts);
|
|
41951
|
+
if (state.kind === "signed-in") {
|
|
41952
|
+
rail.done(`Signed in as ${s.bold}${state.identity}${s.reset}`);
|
|
41953
|
+
coverageLines(ui, opts.repo, state.covered);
|
|
41954
|
+
if (state.nudge) rail.line(state.nudge, "warn");
|
|
41955
|
+
return end("already");
|
|
42358
41956
|
}
|
|
42359
|
-
|
|
42360
|
-
|
|
42361
|
-
|
|
42362
|
-
|
|
42363
|
-
|
|
42364
|
-
|
|
42365
|
-
}
|
|
42366
|
-
|
|
42367
|
-
|
|
42368
|
-
|
|
42369
|
-
|
|
42370
|
-
|
|
42371
|
-
|
|
42372
|
-
|
|
42373
|
-
|
|
41957
|
+
if (state.kind === "cached") {
|
|
41958
|
+
rail.done(state.identity ? `Signed in as ${s.bold}${state.identity}${s.reset} (cached)` : "Using your existing Verity token");
|
|
41959
|
+
rail.line(`Could not reach the Verity service to confirm it: ${state.detail}`);
|
|
41960
|
+
return end("unreachable");
|
|
41961
|
+
}
|
|
41962
|
+
if (state.kind === "rejected") {
|
|
41963
|
+
rail.warn(`Your Verity sign-in was rejected (${state.code})`);
|
|
41964
|
+
rail.line(state.remedy);
|
|
41965
|
+
} else if (state.kind === "anonymous") {
|
|
41966
|
+
rail.warn("This project has only an anonymous Verity token");
|
|
41967
|
+
rail.line("The gate runs, but no runs are saved and Verity keeps no memory of this project.");
|
|
41968
|
+
}
|
|
41969
|
+
rail.settle();
|
|
41970
|
+
}
|
|
41971
|
+
if (!ui.asking) {
|
|
41972
|
+
rail.warn(ui.term.screen ? "Sign-in skipped (--yes)" : "Sign-in skipped (no terminal to ask on)");
|
|
41973
|
+
sayWhatASkipCosts(ui);
|
|
41974
|
+
return end("unattended");
|
|
41975
|
+
}
|
|
41976
|
+
rail.active("Sign in with GitHub");
|
|
41977
|
+
rail.line("One sign-in covers every repository on this machine.");
|
|
41978
|
+
rail.line("Signing in does not give Verity access to the contents of your repositories.");
|
|
41979
|
+
rail.gap();
|
|
41980
|
+
rail.keys([["Enter", "open the browser"], ["s", "skip"]]);
|
|
41981
|
+
const choice = await screen.key(["enter", "s"], "s");
|
|
41982
|
+
if (choice === "s") {
|
|
41983
|
+
rail.collapse();
|
|
41984
|
+
rail.warn("Sign-in skipped");
|
|
41985
|
+
sayWhatASkipCosts(ui);
|
|
41986
|
+
return end("declined");
|
|
41987
|
+
}
|
|
41988
|
+
const failed = (why) => {
|
|
41989
|
+
rail.collapse();
|
|
41990
|
+
rail.warn("Sign-in did not complete");
|
|
41991
|
+
rail.line(terminalText(why, 300));
|
|
41992
|
+
sayWhatASkipCosts(ui);
|
|
41993
|
+
return end("failed");
|
|
41994
|
+
};
|
|
41995
|
+
nameTarget();
|
|
41996
|
+
const code = await requestDeviceCode();
|
|
41997
|
+
if (!code.ok) return failed(code.error);
|
|
41998
|
+
const dc = code.data;
|
|
41999
|
+
const copied = copyToClipboard(dc.user_code);
|
|
42000
|
+
const opened = openInBrowser(dc.verification_uri);
|
|
42001
|
+
rail.line(opened ? `Opened ${dc.verification_uri} in your browser. If no window appeared, open it and enter the code.` : `Open ${dc.verification_uri} and enter the code.`, opened ? "muted" : "plain");
|
|
42002
|
+
const prefix = `${s.gray}\u2502${s.reset} `;
|
|
42003
|
+
const polled = await screen.breathe(
|
|
42004
|
+
prefix,
|
|
42005
|
+
() => `Waiting for you in the browser \xB7 code ${s.bold}${dc.user_code}${s.reset}${copied ? ` ${s.gray}(copied)${s.reset}` : ""}`,
|
|
42006
|
+
pollDeviceToken(dc, { codeExpiresAt: Date.now() + dc.expires_in * 1e3, quiet: true })
|
|
42007
|
+
);
|
|
42008
|
+
if (!polled.ok) return failed(polled.error);
|
|
42009
|
+
let gaveUp = null;
|
|
42010
|
+
const appInstallUi = {
|
|
42011
|
+
wait: async (ctx, visible) => {
|
|
42012
|
+
rail.line(`The Verity GitHub App cannot see ${ctx.owner}/${ctx.repo} yet. Install it${ctx.opened ? " (opened in your browser)" : ""}:`, "warn");
|
|
42013
|
+
rail.line(ctx.installUrl, "plain");
|
|
42014
|
+
return screen.breathe(prefix, () => `Waiting for the install on ${ctx.owner}`, visible);
|
|
42015
|
+
},
|
|
42016
|
+
gaveUp: (message) => {
|
|
42017
|
+
gaveUp = message;
|
|
42374
42018
|
}
|
|
42375
42019
|
};
|
|
42376
|
-
|
|
42377
|
-
|
|
42020
|
+
const login = await screen.spin(prefix, () => "Signing in to Verity", loginOnce({
|
|
42021
|
+
serviceUrl,
|
|
42022
|
+
remote: remote || void 0,
|
|
42023
|
+
verbose: opts.verbose,
|
|
42024
|
+
providerToken: polled.data,
|
|
42025
|
+
appInstallUi
|
|
42026
|
+
}));
|
|
42027
|
+
if (!login.ok) return failed(login.error);
|
|
42028
|
+
const report = await describeLoginOutcome(login.data, { remote: remote || void 0, verbose: opts.verbose });
|
|
42029
|
+
rail.collapse();
|
|
42030
|
+
const identity = terminalText(login.data.email ?? (login.data.userId != null ? `user #${login.data.userId}` : "your account"), 120);
|
|
42031
|
+
rail.done(`Signed in as ${s.bold}${identity}${s.reset}`);
|
|
42032
|
+
if (gaveUp) rail.line(terminalText(gaveUp, 300), "warn");
|
|
42033
|
+
for (const line2 of report.slice(1)) {
|
|
42034
|
+
if (line2.level === "verbose") continue;
|
|
42035
|
+
rail.line(line2.text.trim().replace(/^✓\s*/, ""), line2.level === "warn" ? "warn" : "muted");
|
|
42036
|
+
}
|
|
42037
|
+
return end("new");
|
|
42378
42038
|
}
|
|
42379
|
-
|
|
42039
|
+
|
|
42040
|
+
// src/commands/init/standard.ts
|
|
42041
|
+
var import_node_fs75 = require("node:fs");
|
|
42042
|
+
|
|
42043
|
+
// src/commands/init/rules.ts
|
|
42044
|
+
async function projectLabel(token, serviceUrl, verbose) {
|
|
42380
42045
|
try {
|
|
42381
|
-
const
|
|
42382
|
-
return
|
|
42046
|
+
const who = await whoami(token, serviceUrl, verbose);
|
|
42047
|
+
return who.ok ? who.data.project_id : null;
|
|
42383
42048
|
} catch {
|
|
42384
|
-
return
|
|
42049
|
+
return null;
|
|
42385
42050
|
}
|
|
42386
42051
|
}
|
|
42387
|
-
function
|
|
42388
|
-
|
|
42389
|
-
|
|
42390
|
-
|
|
42391
|
-
|
|
42392
|
-
|
|
42393
|
-
|
|
42394
|
-
|
|
42052
|
+
function profilePreview(profile) {
|
|
42053
|
+
return JSON.stringify(profile, null, 2).split("\n").slice(0, 12).map((l) => terminalText(l, 400)).concat(["\u2026", `The whole profile: ${PROFILE_FILE}`]);
|
|
42054
|
+
}
|
|
42055
|
+
async function askToSend(ui, profile) {
|
|
42056
|
+
const { rail, screen } = ui;
|
|
42057
|
+
const lines = consentLines(profile);
|
|
42058
|
+
rail.active("Write review rules for this repository?");
|
|
42059
|
+
rail.box(lines.slice(1, -1));
|
|
42060
|
+
rail.line(lines[lines.length - 1]);
|
|
42061
|
+
rail.gap();
|
|
42062
|
+
rail.keys([["Enter", "send"], ["v", "view the profile"], ["n", "skip"]]);
|
|
42063
|
+
let allowed = ["enter", "v", "n"];
|
|
42064
|
+
for (; ; ) {
|
|
42065
|
+
const k = await screen.key(allowed, "n", { freshMs: 300 });
|
|
42066
|
+
if (k !== "v") {
|
|
42067
|
+
rail.collapse();
|
|
42068
|
+
return k === "enter";
|
|
42395
42069
|
}
|
|
42396
|
-
|
|
42070
|
+
rail.gap();
|
|
42071
|
+
for (const l of profilePreview(profile)) rail.line(l);
|
|
42072
|
+
rail.gap();
|
|
42073
|
+
rail.keys([["Enter", "send"], ["n", "skip"]]);
|
|
42074
|
+
allowed = ["enter", "n"];
|
|
42397
42075
|
}
|
|
42398
|
-
return true;
|
|
42399
42076
|
}
|
|
42400
|
-
async function
|
|
42401
|
-
const
|
|
42402
|
-
const
|
|
42403
|
-
|
|
42404
|
-
|
|
42405
|
-
|
|
42406
|
-
|
|
42407
|
-
|
|
42408
|
-
|
|
42409
|
-
|
|
42410
|
-
|
|
42077
|
+
async function generate(ui, globals, profile, replaces) {
|
|
42078
|
+
const { rail, screen } = ui;
|
|
42079
|
+
const s = screen.s;
|
|
42080
|
+
const token = await resolveToken(globals.token);
|
|
42081
|
+
const url = await resolveServiceUrl(globals.serviceUrl);
|
|
42082
|
+
if (!token.ok || !url.ok) return { status: "fallback" };
|
|
42083
|
+
const commits = profile.history.commits.length;
|
|
42084
|
+
const fixes = profile.history.commits.filter((c) => c.fix).length;
|
|
42085
|
+
const reverts = profile.history.commits.filter((c) => /^revert\b/i.test(c.subject)).length;
|
|
42086
|
+
rail.active("Writing review rules");
|
|
42087
|
+
await rail.counted(([c, f, r]) => `${s.green}\u2713${s.reset} Read ${c} commits ${s.gray}\xB7${s.reset} ${f} fixes ${s.gray}\xB7${s.reset} ${r} reverts`, [commits, fixes, reverts]);
|
|
42088
|
+
if (profile.docs.length) rail.ticked(`Read ${profile.docs.map((d) => terminalText(d.name, 120)).join(" and ")}`);
|
|
42089
|
+
if (ui.term.screen && !ui.term.motion) rail.line("Writing rules for this repository\u2026");
|
|
42090
|
+
const started = Date.now();
|
|
42091
|
+
const base = buildStandard({ detected: detectProject() }, loadCatalog());
|
|
42092
|
+
const outcome = await screen.spin(`${s.gray}\u2502${s.reset} `, () => "Writing rules for this repository", generateStandard({
|
|
42093
|
+
serviceUrl: url.data,
|
|
42094
|
+
token: token.data.token,
|
|
42095
|
+
profile,
|
|
42096
|
+
base,
|
|
42097
|
+
projectId: await projectLabel(token.data.token, url.data, globals.verbose),
|
|
42098
|
+
verbose: globals.verbose,
|
|
42099
|
+
replaces
|
|
42100
|
+
}));
|
|
42101
|
+
rail.collapse();
|
|
42102
|
+
if (outcome.status === "exists") return { status: "exists" };
|
|
42103
|
+
if (outcome.status === "disabled") {
|
|
42104
|
+
rail.line("Project rules are not switched on for this project yet. The built-in rules apply.");
|
|
42105
|
+
rail.gap();
|
|
42106
|
+
return { status: "fallback" };
|
|
42411
42107
|
}
|
|
42412
|
-
|
|
42413
|
-
|
|
42414
|
-
|
|
42108
|
+
if (outcome.status === "unsupported") {
|
|
42109
|
+
rail.line(`This Verity service (${terminalText(url.data, 200)}) does not write project rules yet: it is older than this CLI. The built-in rules apply.`);
|
|
42110
|
+
rail.gap();
|
|
42111
|
+
return { status: "fallback" };
|
|
42415
42112
|
}
|
|
42416
|
-
if (
|
|
42417
|
-
|
|
42113
|
+
if (outcome.status === "failed") {
|
|
42114
|
+
rail.warn("The rules could not be written this time");
|
|
42115
|
+
rail.line(terminalText(outcome.reason, 300));
|
|
42116
|
+
rail.line("The built-in rules apply. Try again later: verity standard generate");
|
|
42117
|
+
rail.gap();
|
|
42118
|
+
return { status: "fallback" };
|
|
42418
42119
|
}
|
|
42419
|
-
|
|
42420
|
-
|
|
42421
|
-
|
|
42422
|
-
|
|
42423
|
-
|
|
42424
|
-
|
|
42425
|
-
|
|
42426
|
-
|
|
42427
|
-
}
|
|
42428
|
-
|
|
42429
|
-
|
|
42430
|
-
|
|
42431
|
-
|
|
42432
|
-
|
|
42433
|
-
|
|
42434
|
-
|
|
42435
|
-
|
|
42436
|
-
printInfo("Migration complete:");
|
|
42437
|
-
for (const a of result.actions) printInfo(` ${a} \u2713`);
|
|
42438
|
-
});
|
|
42120
|
+
const secs2 = Math.max(1, Math.round((Date.now() - started) / 1e3));
|
|
42121
|
+
const docs = profile.docs.length;
|
|
42122
|
+
if (outcome.rules.length === 0) {
|
|
42123
|
+
rail.warn("No project rules this time");
|
|
42124
|
+
rail.line("The proposed rules were too generic, or not safe to use. The built-in rules apply.");
|
|
42125
|
+
} else {
|
|
42126
|
+
const n2 = outcome.rules.length;
|
|
42127
|
+
const from = [`${commits} ${commits === 1 ? "commit" : "commits"}`, ...docs ? [`${docs} ${docs === 1 ? "doc" : "docs"}`] : []].join(" and ");
|
|
42128
|
+
rail.done(`${n2} ${n2 === 1 ? "rule" : "rules"} for this repository ${s.gray}\xB7 from ${from} \xB7 ${secs2}s${s.reset}`);
|
|
42129
|
+
await rail.rules(outcome.rules);
|
|
42130
|
+
if (outcome.rejected > 0) rail.line(`${outcome.rejected} proposed ${outcome.rejected === 1 ? "rule was" : "rules were"} left out: too generic, or not safe to use.`);
|
|
42131
|
+
}
|
|
42132
|
+
rail.gap();
|
|
42133
|
+
rail.done(`Standard v${outcome.version} saved`);
|
|
42134
|
+
rail.line(`Your team gets the same rules. Edit them in ${STANDARD_FILE}`);
|
|
42135
|
+
if (outcome.warning) rail.line(terminalText(outcome.warning, 600), "warn");
|
|
42136
|
+
return { status: "created", version: outcome.version };
|
|
42439
42137
|
}
|
|
42440
42138
|
|
|
42441
|
-
// src/commands/init/
|
|
42442
|
-
|
|
42443
|
-
|
|
42444
|
-
const
|
|
42445
|
-
|
|
42446
|
-
|
|
42447
|
-
|
|
42448
|
-
|
|
42449
|
-
|
|
42450
|
-
|
|
42451
|
-
|
|
42452
|
-
// from the login sweep. grant_status is non-null only when a grant exists.
|
|
42453
|
-
covered: remote ? who.data.grant_status != null : null,
|
|
42454
|
-
nudge: reverifyNudge(who.data)
|
|
42455
|
-
};
|
|
42456
|
-
}
|
|
42457
|
-
if (!who.ok) {
|
|
42458
|
-
const denial = authDenialRemedy(who.error);
|
|
42459
|
-
if (denial) return { kind: "rejected", code: denial.code, remedy: denial.remedy };
|
|
42460
|
-
const identity = existing.data.userId != null ? terminalText(existing.data.email ?? `user #${existing.data.userId}`, 120) : null;
|
|
42461
|
-
return { kind: "cached", identity, detail: terminalText(who.error, 200) };
|
|
42139
|
+
// src/commands/init/standard.ts
|
|
42140
|
+
async function lookUpTeamStandard(opts) {
|
|
42141
|
+
if (!opts.adopt) return { status: "skipped", why: "no-adopt" };
|
|
42142
|
+
const token = await resolveToken(opts.globals.token);
|
|
42143
|
+
const url = await resolveServiceUrl(opts.globals.serviceUrl);
|
|
42144
|
+
if (!token.ok || !url.ok) return { status: "skipped", why: "not-signed-in" };
|
|
42145
|
+
const found = await fetchRemoteSetup({ token: token.data.token, serviceUrl: url.data, verbose: opts.globals.verbose });
|
|
42146
|
+
if (found.status === "unavailable") return { status: "unavailable", reason: terminalText(found.reason, 300) };
|
|
42147
|
+
if (found.status === "none") return { status: "none" };
|
|
42148
|
+
if (opts.previous?.declinedStandardVersion === found.standard.version) {
|
|
42149
|
+
return { status: "declined-before", version: found.standard.version };
|
|
42462
42150
|
}
|
|
42463
|
-
return {
|
|
42151
|
+
return { status: "found", found, version: found.standard.version };
|
|
42464
42152
|
}
|
|
42465
|
-
function
|
|
42153
|
+
async function writeTeamStandard(team, opts) {
|
|
42154
|
+
const token = await resolveToken(opts.globals.token);
|
|
42155
|
+
const url = await resolveServiceUrl(opts.globals.serviceUrl);
|
|
42156
|
+
if (!token.ok || !url.ok) return { status: "skipped", why: "not-signed-in" };
|
|
42157
|
+
const found = team.found;
|
|
42466
42158
|
try {
|
|
42467
|
-
|
|
42468
|
-
|
|
42469
|
-
|
|
42159
|
+
const outcome = await adoptRemoteSetup(found, {
|
|
42160
|
+
serviceUrl: url.data,
|
|
42161
|
+
projectId: await projectLabel(token.data.token, url.data, opts.globals.verbose)
|
|
42162
|
+
});
|
|
42163
|
+
if (opts.previous?.declinedStandardVersion != null) {
|
|
42164
|
+
await writeSetupState({ declinedStandardVersion: void 0 }).catch(() => {
|
|
42165
|
+
});
|
|
42166
|
+
}
|
|
42167
|
+
return { status: "adopted", version: found.standard.version, summary: describeRemote(found), notes: outcome.notes };
|
|
42168
|
+
} catch (err2) {
|
|
42169
|
+
return { status: "write-failed", reason: terminalText(err2.message, 300) };
|
|
42470
42170
|
}
|
|
42471
42171
|
}
|
|
42472
|
-
function
|
|
42473
|
-
|
|
42474
|
-
|
|
42172
|
+
async function adoptTeamStandard(opts) {
|
|
42173
|
+
const team = await lookUpTeamStandard(opts);
|
|
42174
|
+
return team.status === "found" ? writeTeamStandard(team, opts) : team;
|
|
42475
42175
|
}
|
|
42476
|
-
function
|
|
42477
|
-
|
|
42478
|
-
|
|
42479
|
-
|
|
42480
|
-
|
|
42481
|
-
|
|
42482
|
-
|
|
42483
|
-
|
|
42176
|
+
async function askTeamChoice(ui, team) {
|
|
42177
|
+
const { rail, screen } = ui;
|
|
42178
|
+
rail.active("Your team already has a Standard for this repository");
|
|
42179
|
+
rail.line(describeRemote(team.found));
|
|
42180
|
+
rail.line(`Use it to review with the same rules as everyone else. A new one becomes v${team.version + 1} for the whole team.`);
|
|
42181
|
+
rail.gap();
|
|
42182
|
+
rail.keys([["Enter", "use it"], ["n", "create a new one"]]);
|
|
42183
|
+
const k = await screen.key(["enter", "n"], "enter", { freshMs: 300 });
|
|
42184
|
+
rail.collapse();
|
|
42185
|
+
return k === "n" ? "new" : "use";
|
|
42186
|
+
}
|
|
42187
|
+
function sayAdopted(ui, team) {
|
|
42188
|
+
ui.rail.done(`Using your team's Standard v${team.version}`);
|
|
42189
|
+
ui.rail.line(terminalText(team.summary, 300));
|
|
42190
|
+
for (const note of team.notes) ui.rail.line(terminalText(note, 300), "warn");
|
|
42191
|
+
}
|
|
42192
|
+
async function deriveFromCatalogue(ui, globals) {
|
|
42193
|
+
const url = await resolveServiceUrl(globals.serviceUrl);
|
|
42194
|
+
try {
|
|
42195
|
+
const result = await runSynthesis({ serviceUrl: url.ok ? url.data : void 0 });
|
|
42196
|
+
if ("refused" in result) {
|
|
42197
|
+
ui.rail.warn("No Standard was written");
|
|
42198
|
+
ui.rail.line(result.refused);
|
|
42199
|
+
return { ok: false, version: null };
|
|
42200
|
+
}
|
|
42201
|
+
const pushed = await pushStandard(globals);
|
|
42202
|
+
if (pushed.standardVersion !== null) {
|
|
42203
|
+
await correctVerityMdVersion({
|
|
42204
|
+
standard: result.standard,
|
|
42205
|
+
version: pushed.standardVersion,
|
|
42206
|
+
tools: result.tools,
|
|
42207
|
+
serviceUrl: url.ok ? url.data : void 0
|
|
42208
|
+
});
|
|
42209
|
+
ui.rail.done(`Standard v${pushed.standardVersion} saved`);
|
|
42210
|
+
ui.rail.line(`Your team gets the same rules. Edit them in ${STANDARD_FILE}`);
|
|
42211
|
+
} else {
|
|
42212
|
+
ui.rail.done("Standard saved on this machine");
|
|
42213
|
+
}
|
|
42214
|
+
for (const line2 of pushed.lines) {
|
|
42215
|
+
if (line2.startsWith("Standard pushed")) continue;
|
|
42216
|
+
if (line2.startsWith("Not signed in")) ui.rail.line(`Sign in to share it with your team: verity login, then verity standard push. Edit it in ${STANDARD_FILE}`);
|
|
42217
|
+
else ui.rail.line(line2, "warn");
|
|
42218
|
+
}
|
|
42219
|
+
for (const note of result.notes) ui.rail.line(note, "warn");
|
|
42220
|
+
return { ok: true, version: pushed.standardVersion };
|
|
42221
|
+
} catch (err2) {
|
|
42222
|
+
ui.rail.warn("No Standard was written");
|
|
42223
|
+
ui.rail.line(terminalText(err2.message, 300));
|
|
42224
|
+
return { ok: false, version: null };
|
|
42484
42225
|
}
|
|
42485
42226
|
}
|
|
42486
|
-
async function
|
|
42487
|
-
const { rail
|
|
42488
|
-
const s = screen.s;
|
|
42489
|
-
const
|
|
42227
|
+
async function standardStep(ui, ctx) {
|
|
42228
|
+
const { rail } = ui;
|
|
42229
|
+
const s = ui.screen.s;
|
|
42230
|
+
const notAsked = { outcome: "skipped", detail: "none" };
|
|
42231
|
+
const end = (r) => {
|
|
42490
42232
|
rail.gap();
|
|
42491
42233
|
rail.settle();
|
|
42492
|
-
return
|
|
42234
|
+
return r;
|
|
42493
42235
|
};
|
|
42494
|
-
|
|
42495
|
-
|
|
42496
|
-
|
|
42497
|
-
named = true;
|
|
42498
|
-
rail.note(`Verity service: ${terminalText(resolution.url, 200)}${resolution.source === "default" ? " (the default)" : ` (from ${resolution.source})`}`);
|
|
42236
|
+
const derived = async (profile2) => {
|
|
42237
|
+
const d = await deriveFromCatalogue(ui, ctx.globals);
|
|
42238
|
+
return end({ source: d.ok ? "derived" : "none", version: d.version, profile: profile2 });
|
|
42499
42239
|
};
|
|
42500
|
-
if ((
|
|
42501
|
-
|
|
42502
|
-
|
|
42503
|
-
|
|
42504
|
-
|
|
42505
|
-
|
|
42506
|
-
|
|
42507
|
-
} else {
|
|
42508
|
-
const state = await classifyExistingLogin(serviceUrl, remote, opts);
|
|
42509
|
-
if (state.kind === "signed-in") {
|
|
42510
|
-
rail.done(`Signed in as ${s.bold}${state.identity}${s.reset}`);
|
|
42511
|
-
coverageLines(ui, opts.repo, state.covered);
|
|
42512
|
-
if (state.nudge) rail.line(state.nudge, "warn");
|
|
42513
|
-
return end("already");
|
|
42514
|
-
}
|
|
42515
|
-
if (state.kind === "cached") {
|
|
42516
|
-
rail.done(state.identity ? `Signed in as ${s.bold}${state.identity}${s.reset} (cached)` : "Using your existing Verity token");
|
|
42517
|
-
rail.line(`Could not reach the Verity service to confirm it: ${state.detail}`);
|
|
42518
|
-
return end("unreachable");
|
|
42519
|
-
}
|
|
42520
|
-
if (state.kind === "rejected") {
|
|
42521
|
-
rail.warn(`Your Verity sign-in was rejected (${state.code})`);
|
|
42522
|
-
rail.line(state.remedy);
|
|
42523
|
-
} else if (state.kind === "anonymous") {
|
|
42524
|
-
rail.warn("This project has only an anonymous Verity token");
|
|
42525
|
-
rail.line("The gate runs, but no runs are saved and Verity keeps no memory of this project.");
|
|
42240
|
+
if ((0, import_node_fs75.existsSync)(projectPath(STANDARD_FILE))) {
|
|
42241
|
+
const sync = await pushLocalStandardIfMissing(ctx.globals);
|
|
42242
|
+
rail.done("Kept your Standard");
|
|
42243
|
+
rail.line(`${STANDARD_FILE} is already here, so this run leaves it as it is.`);
|
|
42244
|
+
if (sync.status === "pushed") {
|
|
42245
|
+
rail.line(sync.version !== null ? `It was only on this machine. It is shared now as v${sync.version}.` : "It is only on this machine:", sync.version !== null ? "muted" : "warn");
|
|
42246
|
+
for (const line2 of sync.lines) if (!line2.startsWith("Standard pushed")) rail.line(line2, "warn");
|
|
42526
42247
|
}
|
|
42527
|
-
|
|
42528
|
-
}
|
|
42529
|
-
if (!ui.asking) {
|
|
42530
|
-
rail.warn(ui.term.screen ? "Sign-in skipped (--yes)" : "Sign-in skipped (no terminal to ask on)");
|
|
42531
|
-
sayWhatASkipCosts(ui);
|
|
42532
|
-
return end("unattended");
|
|
42533
|
-
}
|
|
42534
|
-
rail.active("Sign in with GitHub");
|
|
42535
|
-
rail.line("One sign-in covers every repository on this machine.");
|
|
42536
|
-
rail.line("Signing in does not give Verity access to the contents of your repositories.");
|
|
42537
|
-
rail.gap();
|
|
42538
|
-
rail.keys([["Enter", "open the browser"], ["s", "skip"]]);
|
|
42539
|
-
const choice = await screen.key(["enter", "s"], "s");
|
|
42540
|
-
if (choice === "s") {
|
|
42541
|
-
rail.collapse();
|
|
42542
|
-
rail.warn("Sign-in skipped");
|
|
42543
|
-
sayWhatASkipCosts(ui);
|
|
42544
|
-
return end("declined");
|
|
42248
|
+
return end({ source: "kept", version: sync.status === "pushed" ? sync.version : null, profile: notAsked });
|
|
42545
42249
|
}
|
|
42546
|
-
const
|
|
42547
|
-
|
|
42548
|
-
|
|
42549
|
-
|
|
42550
|
-
|
|
42551
|
-
|
|
42552
|
-
|
|
42553
|
-
|
|
42554
|
-
|
|
42555
|
-
|
|
42556
|
-
|
|
42557
|
-
|
|
42558
|
-
|
|
42559
|
-
rail.line(opened ? `Opened ${dc.verification_uri} in your browser. If no window appeared, open it and enter the code.` : `Open ${dc.verification_uri} and enter the code.`, opened ? "muted" : "plain");
|
|
42560
|
-
const prefix = `${s.gray}\u2502${s.reset} `;
|
|
42561
|
-
const polled = await screen.breathe(
|
|
42562
|
-
prefix,
|
|
42563
|
-
() => `Waiting for you in the browser \xB7 code ${s.bold}${dc.user_code}${s.reset}${copied ? ` ${s.gray}(copied)${s.reset}` : ""}`,
|
|
42564
|
-
pollDeviceToken(dc, { codeExpiresAt: Date.now() + dc.expires_in * 1e3, quiet: true })
|
|
42565
|
-
);
|
|
42566
|
-
if (!polled.ok) return failed(polled.error);
|
|
42567
|
-
let gaveUp = null;
|
|
42568
|
-
const appInstallUi = {
|
|
42569
|
-
wait: async (ctx, visible) => {
|
|
42570
|
-
rail.line(`The Verity GitHub App cannot see ${ctx.owner}/${ctx.repo} yet. Install it${ctx.opened ? " (opened in your browser)" : ""}:`, "warn");
|
|
42571
|
-
rail.line(ctx.installUrl, "plain");
|
|
42572
|
-
return screen.breathe(prefix, () => `Waiting for the install on ${ctx.owner}`, visible);
|
|
42573
|
-
},
|
|
42574
|
-
gaveUp: (message) => {
|
|
42575
|
-
gaveUp = message;
|
|
42250
|
+
const team = await lookUpTeamStandard(ctx);
|
|
42251
|
+
let replaces;
|
|
42252
|
+
if (team.status === "found") {
|
|
42253
|
+
const choice = ctx.standard ?? (ui.asking ? await askTeamChoice(ui, team) : "use");
|
|
42254
|
+
if (choice === "use") {
|
|
42255
|
+
const adopted = await writeTeamStandard(team, ctx);
|
|
42256
|
+
if (adopted.status === "adopted") {
|
|
42257
|
+
sayAdopted(ui, adopted);
|
|
42258
|
+
return end({ source: "adopted", version: adopted.version, profile: notAsked });
|
|
42259
|
+
}
|
|
42260
|
+
rail.warn("Could not write the team's Standard here");
|
|
42261
|
+
if (adopted.status === "write-failed") rail.line(adopted.reason);
|
|
42262
|
+
return derived(notAsked);
|
|
42576
42263
|
}
|
|
42577
|
-
|
|
42578
|
-
|
|
42579
|
-
|
|
42580
|
-
|
|
42581
|
-
|
|
42582
|
-
|
|
42583
|
-
|
|
42584
|
-
}));
|
|
42585
|
-
if (!login.ok) return failed(login.error);
|
|
42586
|
-
const report = await describeLoginOutcome(login.data, { remote: remote || void 0, verbose: opts.verbose });
|
|
42587
|
-
rail.collapse();
|
|
42588
|
-
const identity = terminalText(login.data.email ?? (login.data.userId != null ? `user #${login.data.userId}` : "your account"), 120);
|
|
42589
|
-
rail.done(`Signed in as ${s.bold}${identity}${s.reset}`);
|
|
42590
|
-
if (gaveUp) rail.line(terminalText(gaveUp, 300), "warn");
|
|
42591
|
-
for (const line2 of report.slice(1)) {
|
|
42592
|
-
if (line2.level === "verbose") continue;
|
|
42593
|
-
rail.line(line2.text.trim().replace(/^✓\s*/, ""), line2.level === "warn" ? "warn" : "muted");
|
|
42264
|
+
await writeSetupState({ declinedStandardVersion: team.version }).catch(() => {
|
|
42265
|
+
});
|
|
42266
|
+
rail.done(`Creating a new Standard ${s.gray}\xB7 it becomes v${team.version + 1} for the team${ctx.standard ? " (--standard new)" : ""}${s.reset}`);
|
|
42267
|
+
replaces = team.version;
|
|
42268
|
+
} else if (team.status === "declined-before") {
|
|
42269
|
+
rail.line(`This project turned down the team's Standard v${team.version} before, so this run builds its own.`);
|
|
42270
|
+
return derived(notAsked);
|
|
42594
42271
|
}
|
|
42595
|
-
|
|
42596
|
-
|
|
42597
|
-
|
|
42598
|
-
|
|
42599
|
-
async function moveGateInstall(ui) {
|
|
42600
|
-
if (!await needsMigration()) return;
|
|
42601
|
-
try {
|
|
42602
|
-
const result = await runMigration();
|
|
42603
|
-
ui.rail.done("Moved a GATE.md install to Verity");
|
|
42604
|
-
for (const a of result.actions) ui.rail.line(a);
|
|
42605
|
-
} catch (err2) {
|
|
42606
|
-
ui.rail.warn(`The GATE.md migration failed: ${terminalText(err2.message, 200)}`);
|
|
42272
|
+
if (team.status === "unavailable") {
|
|
42273
|
+
rail.warn("Could not ask the Verity service for this repository's Standard");
|
|
42274
|
+
rail.line(team.reason);
|
|
42275
|
+
return derived(notAsked);
|
|
42607
42276
|
}
|
|
42608
|
-
|
|
42609
|
-
|
|
42610
|
-
async function signInSafely(ui, globals) {
|
|
42277
|
+
if (team.status === "skipped") return derived(notAsked);
|
|
42278
|
+
const profile = buildRepoProfile();
|
|
42611
42279
|
try {
|
|
42612
|
-
|
|
42280
|
+
writeRepoFile(PROFILE_FILE, JSON.stringify(profile, null, 2) + "\n");
|
|
42613
42281
|
} catch (err2) {
|
|
42614
|
-
|
|
42615
|
-
|
|
42616
|
-
|
|
42617
|
-
ui.rail.settle();
|
|
42618
|
-
return "failed";
|
|
42282
|
+
rail.warn(`Could not write ${PROFILE_FILE} for you to read, so nothing was sent`);
|
|
42283
|
+
rail.line(terminalText(err2.message, 200));
|
|
42284
|
+
return derived({ outcome: "failed", detail: "error" });
|
|
42619
42285
|
}
|
|
42620
|
-
|
|
42621
|
-
|
|
42622
|
-
if (
|
|
42623
|
-
|
|
42624
|
-
|
|
42625
|
-
else
|
|
42626
|
-
|
|
42627
|
-
|
|
42628
|
-
|
|
42629
|
-
|
|
42630
|
-
|
|
42631
|
-
|
|
42632
|
-
|
|
42633
|
-
|
|
42634
|
-
|
|
42635
|
-
|
|
42286
|
+
let detail;
|
|
42287
|
+
let send;
|
|
42288
|
+
if (ctx.profile) {
|
|
42289
|
+
send = ctx.profile === "send";
|
|
42290
|
+
detail = "given";
|
|
42291
|
+
} else if (ui.asking) {
|
|
42292
|
+
send = await askToSend(ui, profile);
|
|
42293
|
+
detail = send ? "asked" : "declined";
|
|
42294
|
+
} else {
|
|
42295
|
+
send = false;
|
|
42296
|
+
detail = "unattended";
|
|
42297
|
+
}
|
|
42298
|
+
if (!send) {
|
|
42299
|
+
rail.warn("Profile not sent");
|
|
42300
|
+
rail.line(detail === "unattended" ? "Nobody was asked, so nothing was sent. Your Standard has the built-in rules only." : "Your Standard has the built-in rules only.");
|
|
42301
|
+
rail.line(`Add project rules later: verity standard generate (read ${PROFILE_FILE} first)`);
|
|
42302
|
+
rail.gap();
|
|
42303
|
+
return derived({ outcome: "skipped", detail });
|
|
42304
|
+
}
|
|
42305
|
+
if (detail === "given") rail.done(`Profile sent ${s.gray}\xB7 no source code (--profile send)${s.reset}`);
|
|
42306
|
+
else rail.done(`Profile sent ${s.gray}\xB7 no source code${s.reset}`);
|
|
42307
|
+
rail.gap();
|
|
42308
|
+
rail.settle();
|
|
42309
|
+
const generated = await generate(ui, ctx.globals, profile, replaces);
|
|
42310
|
+
if (generated.status === "created") {
|
|
42311
|
+
return end({ source: "generated", version: generated.version, profile: { outcome: "ok", detail } });
|
|
42312
|
+
}
|
|
42313
|
+
if (generated.status === "exists") {
|
|
42314
|
+
const again = await adoptTeamStandard(ctx);
|
|
42315
|
+
if (again.status === "adopted") {
|
|
42316
|
+
sayAdopted(ui, again);
|
|
42317
|
+
return end({ source: "adopted", version: again.version, profile: { outcome: "ok", detail } });
|
|
42318
|
+
}
|
|
42636
42319
|
}
|
|
42320
|
+
return derived({ outcome: "ok", detail });
|
|
42637
42321
|
}
|
|
42638
42322
|
|
|
42639
42323
|
// src/commands/init/index.ts
|
|
42640
|
-
function
|
|
42324
|
+
async function runInit(program2, opts) {
|
|
42325
|
+
const started = Date.now();
|
|
42326
|
+
const globals = program2.opts();
|
|
42327
|
+
const defaultsOnly = (opts.yes ?? false) || !interactive();
|
|
42641
42328
|
let givenMoments;
|
|
42642
42329
|
if (opts.moments != null) {
|
|
42643
42330
|
const parsed = parseMomentSelection(String(opts.moments));
|
|
42644
42331
|
if ("error" in parsed) {
|
|
42645
|
-
printError(
|
|
42332
|
+
printError(parsed.error);
|
|
42646
42333
|
process.exit(1);
|
|
42647
42334
|
}
|
|
42648
42335
|
givenMoments = parsed.moments;
|
|
@@ -42651,7 +42338,7 @@ function parseInitFlags(opts) {
|
|
|
42651
42338
|
if (opts.telemetry != null) {
|
|
42652
42339
|
const parsed = parseTelemetryAnswer(String(opts.telemetry));
|
|
42653
42340
|
if (parsed === null) {
|
|
42654
|
-
printError(`--telemetry: unrecognised "${
|
|
42341
|
+
printError(`--telemetry: unrecognised "${opts.telemetry}". Valid: on, off.`);
|
|
42655
42342
|
process.exit(1);
|
|
42656
42343
|
}
|
|
42657
42344
|
givenTelemetry = parsed;
|
|
@@ -42660,7 +42347,7 @@ function parseInitFlags(opts) {
|
|
|
42660
42347
|
if (opts.profile != null) {
|
|
42661
42348
|
const v = String(opts.profile).trim().toLowerCase();
|
|
42662
42349
|
if (v !== "send" && v !== "skip") {
|
|
42663
|
-
printError(`--profile: unrecognised "${
|
|
42350
|
+
printError(`--profile: unrecognised "${opts.profile}". Valid: send, skip.`);
|
|
42664
42351
|
process.exit(1);
|
|
42665
42352
|
}
|
|
42666
42353
|
profileAnswer = v;
|
|
@@ -42669,24 +42356,16 @@ function parseInitFlags(opts) {
|
|
|
42669
42356
|
if (opts.standard != null) {
|
|
42670
42357
|
const v = String(opts.standard).trim().toLowerCase();
|
|
42671
42358
|
if (v !== "use" && v !== "new") {
|
|
42672
|
-
printError(`--standard: unrecognised "${
|
|
42359
|
+
printError(`--standard: unrecognised "${opts.standard}". Valid: use, new.`);
|
|
42673
42360
|
process.exit(1);
|
|
42674
42361
|
}
|
|
42675
42362
|
standardAnswer = v;
|
|
42676
42363
|
}
|
|
42677
|
-
return { givenMoments, givenTelemetry, profileAnswer, standardAnswer };
|
|
42678
|
-
}
|
|
42679
|
-
async function runInit(program2, opts) {
|
|
42680
|
-
const started = Date.now();
|
|
42681
|
-
const globals = program2.opts();
|
|
42682
|
-
const defaultsOnly = (opts.yes ?? false) || !interactive();
|
|
42683
|
-
const { givenMoments, givenTelemetry, profileAnswer, standardAnswer } = parseInitFlags(opts);
|
|
42684
42364
|
const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
|
|
42685
|
-
if (!projectMarkers.some((m) => (0,
|
|
42365
|
+
if (!projectMarkers.some((m) => (0, import_node_fs76.existsSync)(m))) {
|
|
42686
42366
|
printError('No project detected in the current directory. Run "verity init" from your project root.');
|
|
42687
42367
|
process.exit(1);
|
|
42688
42368
|
}
|
|
42689
|
-
const before = snapshotInitFiles();
|
|
42690
42369
|
const stale = clearStalePluginMarker();
|
|
42691
42370
|
const pluginMode = opts.plugin === false ? false : opts.pluginMode ?? pluginActiveHere();
|
|
42692
42371
|
const install = pluginMode ? activePluginInstall() : null;
|
|
@@ -42699,7 +42378,16 @@ async function runInit(program2, opts) {
|
|
|
42699
42378
|
await wordmark(ui.screen, { reveal: firstInitOnMachine() });
|
|
42700
42379
|
rail.open(repoLabel());
|
|
42701
42380
|
if (opts.intensity != null) rail.line("--intensity is ignored: static analysis is Trivy only.", "warn");
|
|
42702
|
-
await
|
|
42381
|
+
if (await needsMigration()) {
|
|
42382
|
+
try {
|
|
42383
|
+
const result = await runMigration();
|
|
42384
|
+
rail.done("Moved a GATE.md install to Verity");
|
|
42385
|
+
for (const a of result.actions) rail.line(a);
|
|
42386
|
+
} catch (err2) {
|
|
42387
|
+
rail.warn(`The GATE.md migration failed: ${terminalText(err2.message, 200)}`);
|
|
42388
|
+
}
|
|
42389
|
+
rail.settle();
|
|
42390
|
+
}
|
|
42703
42391
|
if (!await prerequisitesStep(ui)) {
|
|
42704
42392
|
rail.end("Stopped: a required tool is missing.", "Install it, then run verity init again.");
|
|
42705
42393
|
trace.mark("prerequisites", "failed", "blocked");
|
|
@@ -42710,10 +42398,10 @@ async function runInit(program2, opts) {
|
|
|
42710
42398
|
trace.mark("prerequisites", "ok");
|
|
42711
42399
|
await scaffoldStep(ui, { untrackMemory: opts.untrackMemory === true, untrackState: opts.untrackState === true });
|
|
42712
42400
|
trace.mark("scaffold", "ok");
|
|
42713
|
-
await (0, import_promises19.mkdir)((0,
|
|
42401
|
+
await (0, import_promises19.mkdir)((0, import_node_path64.join)(process.env.HOME ?? "", ".verity"), { recursive: true });
|
|
42714
42402
|
await repoStep(ui, detectProject());
|
|
42715
42403
|
const previous = await readSetupState();
|
|
42716
|
-
const setUpBefore = (0,
|
|
42404
|
+
const setUpBefore = (0, import_node_fs76.existsSync)(projectPath(STANDARD_FILE));
|
|
42717
42405
|
const answers = await askSetupQuestions(defaultsOnly, previous, { moments: givenMoments, telemetry: givenTelemetry });
|
|
42718
42406
|
trace.mark("questions", "ok", givenMoments || givenTelemetry !== void 0 || profileAnswer || standardAnswer ? "asked" : "defaults");
|
|
42719
42407
|
const plan = planMoments(answers.moments, { given: givenMoments !== void 0, defaultsOnly });
|
|
@@ -42729,40 +42417,66 @@ async function runInit(program2, opts) {
|
|
|
42729
42417
|
force: opts.force ?? false
|
|
42730
42418
|
});
|
|
42731
42419
|
trace.mark("hooks", "ok", pluginMode ? "plugin" : "project");
|
|
42732
|
-
|
|
42420
|
+
let signIn;
|
|
42421
|
+
try {
|
|
42422
|
+
signIn = await signInStep(ui, await resolveServiceUrlForAuth(globals.serviceUrl), { token: globals.token, verbose: globals.verbose, repo: repoLabel() });
|
|
42423
|
+
} catch (err2) {
|
|
42424
|
+
rail.collapse();
|
|
42425
|
+
rail.warn(`Sign-in did not complete: ${terminalText(err2.message, 200)}`);
|
|
42426
|
+
rail.gap();
|
|
42427
|
+
rail.settle();
|
|
42428
|
+
signIn = "failed";
|
|
42429
|
+
}
|
|
42733
42430
|
trace.mark("sign_in", ...signInDetail(signIn));
|
|
42734
42431
|
const signedIn = signIn === "already" || signIn === "new" || signIn === "unreachable";
|
|
42735
|
-
const telemetry = await telemetryStep(ui, answers.telemetry, answers.telemetry === "not-asked" && previous?.telemetry === "deferred", globals
|
|
42736
|
-
|
|
42432
|
+
const telemetry = await telemetryStep(ui, answers.telemetry, answers.telemetry === "not-asked" && previous?.telemetry === "deferred", globals);
|
|
42433
|
+
if (telemetry === "enabled") trace.mark("telemetry", "ok", answers.telemetry === "already-on" ? "already" : "new");
|
|
42434
|
+
else if (telemetry === "declined") trace.mark("telemetry", "skipped", "declined");
|
|
42435
|
+
else if (telemetry === "deferred") trace.mark("telemetry", "failed", "none");
|
|
42436
|
+
else trace.mark("telemetry", "skipped", "unattended");
|
|
42737
42437
|
const standard = await standardStep(ui, { globals, previous, adopt: opts.adopt !== false, profile: profileAnswer, standard: standardAnswer });
|
|
42738
42438
|
trace.mark("profile", standard.profile.outcome, standard.profile.detail);
|
|
42739
42439
|
const haveStandard = standard.source !== "none";
|
|
42740
42440
|
trace.mark("standard", haveStandard ? "ok" : "failed", standard.source);
|
|
42741
42441
|
const formalNew = formalDefault(previous, { setUpBefore });
|
|
42742
42442
|
backgroundStep(ui, { cli: process.argv[1], formalNew });
|
|
42743
|
-
|
|
42443
|
+
try {
|
|
42444
|
+
await writeSetupState({
|
|
42445
|
+
// The effective answer: the next run starts from this record.
|
|
42446
|
+
moments: plan.momentIds,
|
|
42447
|
+
...telemetry ? { telemetry } : {},
|
|
42448
|
+
init: { completed_at: (/* @__PURE__ */ new Date()).toISOString(), cli_version: true ? "0.35.0-experimental.7c5e75b" : "dev" }
|
|
42449
|
+
});
|
|
42450
|
+
} catch (err2) {
|
|
42451
|
+
rail.line(`Could not record the setup answers: ${terminalText(err2.message, 200)}`, "warn");
|
|
42452
|
+
}
|
|
42744
42453
|
trace.mark("completed", haveStandard ? "ok" : "failed", haveStandard ? void 0 : "none");
|
|
42745
42454
|
await trace.settle();
|
|
42746
|
-
|
|
42747
|
-
|
|
42748
|
-
|
|
42749
|
-
|
|
42750
|
-
|
|
42751
|
-
|
|
42752
|
-
|
|
42753
|
-
|
|
42754
|
-
|
|
42455
|
+
const secs2 = Math.max(1, Math.round((Date.now() - started) / 1e3));
|
|
42456
|
+
const s = ui.screen.s;
|
|
42457
|
+
if (!haveStandard) {
|
|
42458
|
+
rail.end(`${s.yellow}Not finished:${s.reset} this project has no Standard, so the gate cannot review.`, "Run verity init again, or /verity-setup in Claude Code.");
|
|
42459
|
+
} else {
|
|
42460
|
+
if (pluginMode) {
|
|
42461
|
+
const version = activePluginVersion();
|
|
42462
|
+
if (version) await writeSetupState({ pluginSetup: { version, completed_at: (/* @__PURE__ */ new Date()).toISOString() }, wiring: void 0 });
|
|
42463
|
+
} else if (opts.plugin === false) {
|
|
42464
|
+
await writeSetupState({ wiring: "project" });
|
|
42465
|
+
}
|
|
42466
|
+
const next = plan.momentIds.length === 0 ? "Verity reviews nothing until you turn a moment on: verity config moments turn,commit,push" : pluginMode ? signedIn ? "Verity reviews from your next turn." : "The security scan runs from your next turn. Sign in for AI review: verity login" : signedIn ? "Start a new Claude Code session. Verity reviews from its first turn." : "Start a new Claude Code session. The security scan runs from the first turn.";
|
|
42467
|
+
rail.end(`${s.green}${s.bold}Ready${s.reset} ${s.gray}in ${secs2}s.${s.reset}`, next);
|
|
42468
|
+
}
|
|
42755
42469
|
markInitSeen();
|
|
42756
42470
|
} finally {
|
|
42757
42471
|
ui.screen.release();
|
|
42758
42472
|
}
|
|
42759
42473
|
}
|
|
42760
42474
|
function registerInitCommand(program2) {
|
|
42761
|
-
program2.command("init").alias("setup").description("Set up Verity in this project: sign in,
|
|
42475
|
+
program2.command("init").alias("setup").description("Set up Verity in this project: two questions (sign in, and send a repo profile); every other choice is a stated default").option("--force", "Reinstall the skills even when they are up to date (hooks are always reconciled)").option("-y, --yes", "Ask nothing: skip sign-in unless already signed in, and send the profile only with --profile send").option("--profile <send|skip>", "Answer the repo-profile question without asking (the plugin asks in the session)").option("--standard <use|new>", "When the team already has a Standard: use it, or create a new one (the plugin asks in the session; unattended runs use it)").option("--no-setup", "Ignored: init no longer hands off to /verity-setup (kept for old scripts)").option("--plugin-mode", "The Claude Code plugin owns the skills and hooks: install neither, and remove any this project already has").option("--no-plugin", "Ignore any Claude Code plugin here and wire this project's own skills and hooks").option("--no-adopt", "Don't use this repository's Standard from the service; build a new one").option("--moments <list>", 'Set when Verity reviews without the default: stop,pre-commit,pre-push \u2014 or "none"').option("--intensity <level>", "Ignored: static analysis is Trivy only (kept so older plugin skills keep working)").option("--telemetry <on|off>", "Turn cost & usage telemetry on or off (init does not ask)").option("--untrack-memory", "Stop tracking the knowledge graph in git (init does not ask)").option("--untrack-state", "Untrack committed Verity state (init does not ask)").action((opts) => runInit(program2, opts));
|
|
42762
42476
|
}
|
|
42763
42477
|
|
|
42764
42478
|
// src/commands/doctor.ts
|
|
42765
|
-
var
|
|
42479
|
+
var import_node_fs77 = require("node:fs");
|
|
42766
42480
|
async function buildReport() {
|
|
42767
42481
|
const prereqs = await checkPrereqs({ install: false });
|
|
42768
42482
|
const state = await readSetupState();
|
|
@@ -42771,8 +42485,8 @@ async function buildReport() {
|
|
|
42771
42485
|
const provenance = cliProvenance();
|
|
42772
42486
|
const telemetry = await checkTelemetry();
|
|
42773
42487
|
const artifacts = {
|
|
42774
|
-
standard: (0,
|
|
42775
|
-
verityMd: (0,
|
|
42488
|
+
standard: (0, import_node_fs77.existsSync)(projectPath(STANDARD_FILE)),
|
|
42489
|
+
verityMd: (0, import_node_fs77.existsSync)(projectPath(VERITY_MD_FILE))
|
|
42776
42490
|
};
|
|
42777
42491
|
const next = [];
|
|
42778
42492
|
for (const c of prereqs.checks) {
|
|
@@ -42931,8 +42645,8 @@ function registerDoctorCommand(program2) {
|
|
|
42931
42645
|
}
|
|
42932
42646
|
|
|
42933
42647
|
// src/commands/uninstall.ts
|
|
42934
|
-
var
|
|
42935
|
-
var
|
|
42648
|
+
var import_node_fs78 = require("node:fs");
|
|
42649
|
+
var import_node_path65 = require("node:path");
|
|
42936
42650
|
function registerUninstallCommand(program2) {
|
|
42937
42651
|
program2.command("uninstall").description("Remove Verity from this project (skills, hooks, .verity/, VERITY.md)").option("--dry-run", "Show what would be removed without doing it").option("--purge-global", "Also remove ~/.verity/ (deletes saved tokens \u2014 reconnect requires re-registration)").option("--keep-verity-md", "Keep the project root VERITY.md file").action(async (opts) => {
|
|
42938
42652
|
const dryRun = opts.dryRun ?? false;
|
|
@@ -42941,11 +42655,11 @@ function registerUninstallCommand(program2) {
|
|
|
42941
42655
|
const actions = [];
|
|
42942
42656
|
const skillsRoot = projectPath(".claude/skills");
|
|
42943
42657
|
for (const name2 of PROJECT_SKILL_NAMES) {
|
|
42944
|
-
const dir = (0,
|
|
42945
|
-
if ((0,
|
|
42658
|
+
const dir = (0, import_node_path65.join)(skillsRoot, name2);
|
|
42659
|
+
if ((0, import_node_fs78.existsSync)(dir)) {
|
|
42946
42660
|
actions.push({
|
|
42947
42661
|
label: `Remove .claude/skills/${name2}/`,
|
|
42948
|
-
apply: () => (0,
|
|
42662
|
+
apply: () => (0, import_node_fs78.rmSync)(dir, { recursive: true, force: true })
|
|
42949
42663
|
});
|
|
42950
42664
|
}
|
|
42951
42665
|
}
|
|
@@ -42959,24 +42673,24 @@ function registerUninstallCommand(program2) {
|
|
|
42959
42673
|
});
|
|
42960
42674
|
}
|
|
42961
42675
|
const verityDir = projectPath(VERITY_DIR);
|
|
42962
|
-
if ((0,
|
|
42676
|
+
if ((0, import_node_fs78.existsSync)(verityDir)) {
|
|
42963
42677
|
actions.push({
|
|
42964
42678
|
label: `Remove ${VERITY_DIR}/`,
|
|
42965
|
-
apply: () => (0,
|
|
42679
|
+
apply: () => (0, import_node_fs78.rmSync)(verityDir, { recursive: true, force: true })
|
|
42966
42680
|
});
|
|
42967
42681
|
}
|
|
42968
42682
|
if (!keepVerityMd) {
|
|
42969
42683
|
const verityMd = projectPath(VERITY_MD_FILE);
|
|
42970
|
-
if ((0,
|
|
42684
|
+
if ((0, import_node_fs78.existsSync)(verityMd)) {
|
|
42971
42685
|
actions.push({
|
|
42972
42686
|
label: `Remove ${VERITY_MD_FILE}`,
|
|
42973
|
-
apply: () => (0,
|
|
42687
|
+
apply: () => (0, import_node_fs78.rmSync)(verityMd, { force: true })
|
|
42974
42688
|
});
|
|
42975
42689
|
}
|
|
42976
42690
|
}
|
|
42977
42691
|
const cleanupEmptyDir = (path) => {
|
|
42978
|
-
if ((0,
|
|
42979
|
-
(0,
|
|
42692
|
+
if ((0, import_node_fs78.existsSync)(path) && (0, import_node_fs78.statSync)(path).isDirectory() && (0, import_node_fs78.readdirSync)(path).length === 0) {
|
|
42693
|
+
(0, import_node_fs78.rmdirSync)(path);
|
|
42980
42694
|
}
|
|
42981
42695
|
};
|
|
42982
42696
|
actions.push({
|
|
@@ -42987,11 +42701,11 @@ function registerUninstallCommand(program2) {
|
|
|
42987
42701
|
}
|
|
42988
42702
|
});
|
|
42989
42703
|
const home = process.env.HOME ?? "";
|
|
42990
|
-
const globalVerityDir = (0,
|
|
42991
|
-
if (purgeGlobal && (0,
|
|
42704
|
+
const globalVerityDir = (0, import_node_path65.join)(home, ".verity");
|
|
42705
|
+
if (purgeGlobal && (0, import_node_fs78.existsSync)(globalVerityDir)) {
|
|
42992
42706
|
actions.push({
|
|
42993
42707
|
label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
|
|
42994
|
-
apply: () => (0,
|
|
42708
|
+
apply: () => (0, import_node_fs78.rmSync)(globalVerityDir, { recursive: true, force: true })
|
|
42995
42709
|
});
|
|
42996
42710
|
}
|
|
42997
42711
|
if (actions.length === 0) {
|
|
@@ -43210,8 +42924,8 @@ function registerTaskCommands(program2) {
|
|
|
43210
42924
|
}
|
|
43211
42925
|
|
|
43212
42926
|
// src/commands/reset.ts
|
|
43213
|
-
var
|
|
43214
|
-
var
|
|
42927
|
+
var import_node_fs79 = require("node:fs");
|
|
42928
|
+
var import_node_path66 = require("node:path");
|
|
43215
42929
|
function registerResetCommand(program2) {
|
|
43216
42930
|
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) => {
|
|
43217
42931
|
const globals = program2.opts();
|
|
@@ -43251,18 +42965,18 @@ function registerResetCommand(program2) {
|
|
|
43251
42965
|
} catch {
|
|
43252
42966
|
}
|
|
43253
42967
|
}
|
|
43254
|
-
const purged = purgeVerityDir((0,
|
|
42968
|
+
const purged = purgeVerityDir((0, import_node_path66.basename)(CACHE_DIR), (name2) => name2.startsWith("pending-"));
|
|
43255
42969
|
if (purged === null) printError(`Did not purge ${CACHE_DIR}: it is not a directory inside ${VERITY_DIR}.`);
|
|
43256
42970
|
const stateDir2 = projectPath(VERITY_DIR);
|
|
43257
42971
|
const filesToClear = [
|
|
43258
|
-
(0,
|
|
43259
|
-
(0,
|
|
43260
|
-
(0,
|
|
42972
|
+
(0, import_node_path66.basename)(INTENT_FILE),
|
|
42973
|
+
(0, import_node_path66.basename)(HASH_FILE),
|
|
42974
|
+
(0, import_node_path66.basename)(BASELINE_SHA_FILE),
|
|
43261
42975
|
".iteration-count",
|
|
43262
42976
|
".last-analysis"
|
|
43263
42977
|
];
|
|
43264
42978
|
for (const name2 of filesToClear) {
|
|
43265
|
-
if (lstatOrNull((0,
|
|
42979
|
+
if (lstatOrNull((0, import_node_path66.join)(stateDir2, name2)) && !replaceInRoot(stateDir2, name2, "")) {
|
|
43266
42980
|
printError(`Did not clear ${VERITY_DIR}/${name2}: it is not a regular file inside ${VERITY_DIR}.`);
|
|
43267
42981
|
}
|
|
43268
42982
|
}
|
|
@@ -43277,29 +42991,29 @@ function registerResetCommand(program2) {
|
|
|
43277
42991
|
}
|
|
43278
42992
|
function lstatOrNull(path) {
|
|
43279
42993
|
try {
|
|
43280
|
-
return (0,
|
|
42994
|
+
return (0, import_node_fs79.lstatSync)(path);
|
|
43281
42995
|
} catch {
|
|
43282
42996
|
return null;
|
|
43283
42997
|
}
|
|
43284
42998
|
}
|
|
43285
42999
|
function purgeVerityDir(child, select) {
|
|
43286
43000
|
const verityDir = projectPath(VERITY_DIR);
|
|
43287
|
-
const dir = (0,
|
|
43001
|
+
const dir = (0, import_node_path66.join)(verityDir, child);
|
|
43288
43002
|
const parent = lstatOrNull(verityDir);
|
|
43289
43003
|
const st = lstatOrNull(dir);
|
|
43290
43004
|
if (!st) return 0;
|
|
43291
43005
|
if (!parent || parent.isSymbolicLink() || !parent.isDirectory()) return null;
|
|
43292
43006
|
if (st.isSymbolicLink() || !st.isDirectory()) return null;
|
|
43293
43007
|
try {
|
|
43294
|
-
if ((0,
|
|
43008
|
+
if ((0, import_node_fs79.realpathSync)(dir) !== (0, import_node_path66.join)((0, import_node_fs79.realpathSync)(verityDir), child)) return null;
|
|
43295
43009
|
} catch {
|
|
43296
43010
|
return null;
|
|
43297
43011
|
}
|
|
43298
43012
|
let removed = 0;
|
|
43299
|
-
for (const entry of (0,
|
|
43013
|
+
for (const entry of (0, import_node_fs79.readdirSync)(dir, { withFileTypes: true })) {
|
|
43300
43014
|
if (entry.isDirectory() || !select(entry.name)) continue;
|
|
43301
43015
|
try {
|
|
43302
|
-
(0,
|
|
43016
|
+
(0, import_node_fs79.unlinkSync)((0, import_node_path66.join)(dir, entry.name));
|
|
43303
43017
|
removed++;
|
|
43304
43018
|
} catch {
|
|
43305
43019
|
}
|
|
@@ -43308,7 +43022,7 @@ function purgeVerityDir(child, select) {
|
|
|
43308
43022
|
}
|
|
43309
43023
|
|
|
43310
43024
|
// src/commands/reflect.ts
|
|
43311
|
-
var
|
|
43025
|
+
var import_node_fs80 = require("node:fs");
|
|
43312
43026
|
|
|
43313
43027
|
// src/lib/reflection-globs.ts
|
|
43314
43028
|
var MAX_GLOBS = 6;
|
|
@@ -43346,7 +43060,7 @@ async function writeNodeToDisk(args2) {
|
|
|
43346
43060
|
{ treePaths: listTrackedFiles(), recordBaseline: false }
|
|
43347
43061
|
);
|
|
43348
43062
|
if (written === 0) return false;
|
|
43349
|
-
return (0,
|
|
43063
|
+
return (0, import_node_fs80.existsSync)(projectPath(`${VERITY_DIR}/memory/${args2.filePath}`));
|
|
43350
43064
|
} catch {
|
|
43351
43065
|
return false;
|
|
43352
43066
|
}
|
|
@@ -43783,7 +43497,7 @@ function registerRunCommand(program2) {
|
|
|
43783
43497
|
}
|
|
43784
43498
|
|
|
43785
43499
|
// src/commands/telemetry.ts
|
|
43786
|
-
var
|
|
43500
|
+
var import_node_fs81 = require("node:fs");
|
|
43787
43501
|
var SCOPE = "every Claude Code session on this machine";
|
|
43788
43502
|
function declineHeaders(why) {
|
|
43789
43503
|
process.stderr.write(`Verity telemetry is off here: ${why} Nothing is exported from this session.
|
|
@@ -43791,7 +43505,7 @@ function declineHeaders(why) {
|
|
|
43791
43505
|
process.exitCode = 1;
|
|
43792
43506
|
}
|
|
43793
43507
|
async function recordTelemetryChoice(choice) {
|
|
43794
|
-
if (!(0,
|
|
43508
|
+
if (!(0, import_node_fs81.existsSync)(projectPath(SETUP_STATE_FILE))) return;
|
|
43795
43509
|
try {
|
|
43796
43510
|
if ((await readSetupState())?.telemetry === choice) return;
|
|
43797
43511
|
await writeSetupState({ telemetry: choice });
|
|
@@ -44041,8 +43755,8 @@ function registerUsageCommand(program2) {
|
|
|
44041
43755
|
}
|
|
44042
43756
|
|
|
44043
43757
|
// src/commands/prove.ts
|
|
44044
|
-
var
|
|
44045
|
-
var
|
|
43758
|
+
var import_node_fs82 = require("node:fs");
|
|
43759
|
+
var import_node_path67 = require("node:path");
|
|
44046
43760
|
|
|
44047
43761
|
// src/lib/prove/hint-pool.ts
|
|
44048
43762
|
var STOP2 = /* @__PURE__ */ new Set([
|
|
@@ -44184,7 +43898,7 @@ function registerProveCommand(program2) {
|
|
|
44184
43898
|
process.exit(2);
|
|
44185
43899
|
}
|
|
44186
43900
|
const limit = Math.max(1, Math.min(50, Number(opts.limit) || 10));
|
|
44187
|
-
let signals = collectRiskSignals(root, { since: opts.since, limit, paths: paths.map((p) => (0,
|
|
43901
|
+
let signals = collectRiskSignals(root, { since: opts.since, limit, paths: paths.map((p) => (0, import_node_path67.relative)(root, (0, import_node_path67.resolve)(p)) || ".") });
|
|
44188
43902
|
if (opts.hint && paths.length === 0) {
|
|
44189
43903
|
const hit = hintMatches(root, opts.hint);
|
|
44190
43904
|
if (hit.files.length > 0) {
|
|
@@ -44238,7 +43952,7 @@ function registerProveCommand(program2) {
|
|
|
44238
43952
|
});
|
|
44239
43953
|
prove.command("check [model]").description("Elaborate a model (or every model) with the local Lean toolchain and report each property").option("--timeout <ms>", "Per-model timeout in ms", "120000").option("--json", "Machine-readable output").action((model, opts) => {
|
|
44240
43954
|
const root = repoRoot();
|
|
44241
|
-
if (!(0,
|
|
43955
|
+
if (!(0, import_node_fs82.existsSync)((0, import_node_path67.join)(root, PROOFS_DIR, "lakefile.toml"))) {
|
|
44242
43956
|
printError(`No ${PROOFS_DIR}/ project here. Run \`verity prove init\` first.`);
|
|
44243
43957
|
process.exit(2);
|
|
44244
43958
|
}
|
|
@@ -44262,7 +43976,7 @@ ${build.stderr.split("\n").filter((l) => !l.startsWith("trace:")).slice(0, 20).j
|
|
|
44262
43976
|
const reports = [];
|
|
44263
43977
|
for (const f of files) {
|
|
44264
43978
|
const report = checkModel(tc, root, f, { timeoutMs: Math.max(1e3, Number(opts.timeout) || 12e4) });
|
|
44265
|
-
report.file = (0,
|
|
43979
|
+
report.file = (0, import_node_path67.relative)(root, f);
|
|
44266
43980
|
reports.push(report);
|
|
44267
43981
|
try {
|
|
44268
43982
|
writeResult(root, report);
|
|
@@ -44300,7 +44014,7 @@ ${build.stderr.split("\n").filter((l) => !l.startsWith("trace:")).slice(0, 20).j
|
|
|
44300
44014
|
printError("Not logged in \u2014 replays run on the Verity service. Run `verity login` first.");
|
|
44301
44015
|
process.exit(2);
|
|
44302
44016
|
}
|
|
44303
|
-
if (!(0,
|
|
44017
|
+
if (!(0, import_node_fs82.existsSync)((0, import_node_path67.join)(root, PROOFS_DIR, "lakefile.toml"))) {
|
|
44304
44018
|
printError(`No ${PROOFS_DIR}/ project here. Run \`verity prove init\` first.`);
|
|
44305
44019
|
process.exit(2);
|
|
44306
44020
|
}
|
|
@@ -44331,7 +44045,7 @@ ${report.error ?? ""}`);
|
|
|
44331
44045
|
printInfo(opts.property ? `${model} ${opts.property} has no counterexample to replay` : `${model} has no counterexample to replay`);
|
|
44332
44046
|
return;
|
|
44333
44047
|
}
|
|
44334
|
-
const modelSource = readRegularFileInRoot(root, (0,
|
|
44048
|
+
const modelSource = readRegularFileInRoot(root, (0, import_node_path67.relative)(root, file), 512 * 1024);
|
|
44335
44049
|
if (!opts.json) printInfo(`replaying ${targets.length} counterexample(s) of ${model} in network-less containers (${Math.min(targets.length, REPLAY_CONCURRENCY)} at a time)\u2026`);
|
|
44336
44050
|
const outcomes = await mapPool(targets, REPLAY_CONCURRENCY, async (p) => {
|
|
44337
44051
|
const brief = buildReplayBrief(root, report, p, modelSource.ok ? modelSource.content : "");
|
|
@@ -44351,7 +44065,7 @@ ${report.error ?? ""}`);
|
|
|
44351
44065
|
});
|
|
44352
44066
|
prove.command("auto [files...]").description("The formal phase the pre-push guard runs: select one risky symbol among changed files, generate a model, check it, replay counterexamples (default files: the commits ahead of upstream)").option("--deadline <ms>", "Budget for the whole phase", "170000").option("--json", "Machine-readable output").action(async (paths, opts) => {
|
|
44353
44067
|
const root = repoRoot();
|
|
44354
|
-
let files = paths.map((p) => (0,
|
|
44068
|
+
let files = paths.map((p) => (0, import_node_path67.relative)(root, (0, import_node_path67.resolve)(p)));
|
|
44355
44069
|
if (files.length === 0) {
|
|
44356
44070
|
const ahead = runGitSync(root, ["diff", "--name-only", "@{upstream}...HEAD"]);
|
|
44357
44071
|
const fallback = ahead.ok ? ahead : runGitSync(root, ["diff", "--name-only", "HEAD~1..HEAD"]);
|
|
@@ -44425,7 +44139,7 @@ ${f.reason ? ` ${terminalText(f.reason, 2e3)}
|
|
|
44425
44139
|
let revise;
|
|
44426
44140
|
if (opts.revise) {
|
|
44427
44141
|
const existing = resolveModelFile(root, opts.revise);
|
|
44428
|
-
const src = existing ? readRegularFileInRoot(root, (0,
|
|
44142
|
+
const src = existing ? readRegularFileInRoot(root, (0, import_node_path67.relative)(root, existing), 64 * 1024) : null;
|
|
44429
44143
|
if (!src || !src.ok || !opts.note) {
|
|
44430
44144
|
printError("--revise needs an existing model under Models/ and --note - with what the replay showed on stdin");
|
|
44431
44145
|
process.exit(2);
|
|
@@ -44465,8 +44179,8 @@ ${f.reason ? ` ${terminalText(f.reason, 2e3)}
|
|
|
44465
44179
|
});
|
|
44466
44180
|
prove.command("waive <file>").description("Dismiss the remembered formal finding(s) on a file for its current bytes \u2014 the accepted-risk path for a formal block").requiredOption("--reason <text>", "The human disposition this records (reviewer finding, ADR, \u2026)").action((file, opts) => {
|
|
44467
44181
|
const root = repoRoot();
|
|
44468
|
-
const rel0 = (0,
|
|
44469
|
-
const rel = rel0.startsWith("..") || (0,
|
|
44182
|
+
const rel0 = (0, import_node_path67.relative)(root, (0, import_node_path67.isAbsolute)(file) ? file : (0, import_node_path67.join)(root, file)).replace(/\\/g, "/");
|
|
44183
|
+
const rel = rel0.startsWith("..") || (0, import_node_path67.isAbsolute)(rel0) ? null : rel0;
|
|
44470
44184
|
if (!rel) {
|
|
44471
44185
|
printError(`${file} is not inside this repository`);
|
|
44472
44186
|
process.exit(2);
|
|
@@ -44482,8 +44196,8 @@ ${f.reason ? ` ${terminalText(f.reason, 2e3)}
|
|
|
44482
44196
|
});
|
|
44483
44197
|
prove.command("hash <file>").description("Print the sha256 of a file, for a model header (Verity.model \u2026 sha256). Path is REPO-relative, as in the header").action((file) => {
|
|
44484
44198
|
const root = repoRoot();
|
|
44485
|
-
const rel = (0,
|
|
44486
|
-
const digest = rel.startsWith("..") || (0,
|
|
44199
|
+
const rel = (0, import_node_path67.relative)(root, (0, import_node_path67.isAbsolute)(file) ? file : (0, import_node_path67.join)(root, file));
|
|
44200
|
+
const digest = rel.startsWith("..") || (0, import_node_path67.isAbsolute)(rel) ? null : sha256InRoot(root, rel);
|
|
44487
44201
|
if (!digest) {
|
|
44488
44202
|
printError(`Cannot hash ${file}: it must be a regular file inside this repository (not a symlink)`);
|
|
44489
44203
|
process.exit(2);
|
|
@@ -44517,10 +44231,10 @@ function refreshScaffold(root) {
|
|
|
44517
44231
|
}
|
|
44518
44232
|
|
|
44519
44233
|
// src/commands/lean.ts
|
|
44520
|
-
var
|
|
44521
|
-
var
|
|
44522
|
-
var
|
|
44523
|
-
var
|
|
44234
|
+
var import_node_fs83 = require("node:fs");
|
|
44235
|
+
var import_node_child_process23 = require("node:child_process");
|
|
44236
|
+
var import_node_os14 = require("node:os");
|
|
44237
|
+
var import_node_path68 = require("node:path");
|
|
44524
44238
|
function registerLeanCommand(program2) {
|
|
44525
44239
|
const lean = program2.command("lean").description("The Lean 4 toolchain formal verification needs (managed download, pinned by this CLI)");
|
|
44526
44240
|
lean.command("status").option("--json", "Machine-readable output").description("Which toolchain would run, and whether it matches the pinned version").action((opts) => {
|
|
@@ -44528,7 +44242,7 @@ function registerLeanCommand(program2) {
|
|
|
44528
44242
|
let version = null;
|
|
44529
44243
|
if (tc) {
|
|
44530
44244
|
try {
|
|
44531
|
-
version = (0,
|
|
44245
|
+
version = (0, import_node_child_process23.execFileSync)(tc.lake.replace(/lake(\.exe)?$/, "lean$1"), ["--version"], { env: tc.env, encoding: "utf-8", timeout: 1e4 }).trim();
|
|
44532
44246
|
} catch {
|
|
44533
44247
|
version = null;
|
|
44534
44248
|
}
|
|
@@ -44539,7 +44253,7 @@ function registerLeanCommand(program2) {
|
|
|
44539
44253
|
const bg = readInstallState();
|
|
44540
44254
|
const background = bg ? { ...bg, running: bg.state === "running" && installRunning() } : null;
|
|
44541
44255
|
if (opts.json) {
|
|
44542
|
-
printJson({ pinned: LEAN_TOOLCHAIN, found: tc?.lake ?? null, version, matches_pin: matches, managed_dir: managed, managed_installed: (0,
|
|
44256
|
+
printJson({ pinned: LEAN_TOOLCHAIN, found: tc?.lake ?? null, version, matches_pin: matches, managed_dir: managed, managed_installed: (0, import_node_fs83.existsSync)((0, import_node_path68.join)(managed, "bin")), asset: leanAssetName(), background });
|
|
44543
44257
|
return;
|
|
44544
44258
|
}
|
|
44545
44259
|
printInfo(`pinned: ${LEAN_TOOLCHAIN}`);
|
|
@@ -44556,11 +44270,11 @@ function registerLeanCommand(program2) {
|
|
|
44556
44270
|
if (opts.background) process.exit(await runBackgroundInstall());
|
|
44557
44271
|
const asset = leanAssetName();
|
|
44558
44272
|
if (!asset) {
|
|
44559
|
-
printError(`no Lean release for ${(0,
|
|
44273
|
+
printError(`no Lean release for ${(0, import_node_os14.platform)()}/${(0, import_node_os14.arch)()}`);
|
|
44560
44274
|
process.exit(2);
|
|
44561
44275
|
}
|
|
44562
44276
|
const dir = pinnedVersionDir();
|
|
44563
|
-
if ((0,
|
|
44277
|
+
if ((0, import_node_fs83.existsSync)((0, import_node_path68.join)(dir, "bin", "lake")) && !opts.force) {
|
|
44564
44278
|
printInfo(`already installed at ${dir}`);
|
|
44565
44279
|
return;
|
|
44566
44280
|
}
|
|
@@ -44596,20 +44310,20 @@ function registerLeanCommand(program2) {
|
|
|
44596
44310
|
});
|
|
44597
44311
|
lean.command("remove").description("Delete the Verity-managed toolchain for the pinned version").action(() => {
|
|
44598
44312
|
const dir = pinnedVersionDir();
|
|
44599
|
-
if (!(0,
|
|
44313
|
+
if (!(0, import_node_fs83.existsSync)(dir)) {
|
|
44600
44314
|
printInfo("nothing to remove");
|
|
44601
44315
|
return;
|
|
44602
44316
|
}
|
|
44603
|
-
(0,
|
|
44317
|
+
(0, import_node_fs83.rmSync)(dir, { recursive: true, force: true });
|
|
44604
44318
|
printInfo(`removed ${dir}`);
|
|
44605
44319
|
});
|
|
44606
44320
|
}
|
|
44607
44321
|
|
|
44608
44322
|
// src/cli.ts
|
|
44609
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.35.0-experimental.
|
|
44323
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.35.0-experimental.7c5e75b").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) => {
|
|
44610
44324
|
const isTelemetryHelper = actionCommand.name() === "headers" && actionCommand.parent?.name() === "telemetry";
|
|
44611
44325
|
if (!isTelemetryHelper) {
|
|
44612
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.35.0-experimental.
|
|
44326
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.35.0-experimental.7c5e75b");
|
|
44613
44327
|
}
|
|
44614
44328
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
44615
44329
|
try {
|