@codacy/verity-cli 0.31.1-experimental.83a8619 โ†’ 0.31.1-experimental.b9b3862

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,50 @@ All notable changes to Verity are documented here. This project follows
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ### ๐Ÿค Joining a project that already has a Standard
9
+
10
+ - **`verity init` now offers the Standard the service already holds** for this
11
+ repository, instead of asking a model to invent a second one. Everyone after the
12
+ first person on a project โ€” a teammate cloning the repo, the same person on
13
+ another machine, a CI box โ€” gets the team's actual configuration
14
+ (`standard.yaml`, the analysis config, `.verityignore`, `VERITY.md`) written
15
+ straight to disk, and the model phase is skipped entirely.
16
+ - **This is a correctness fix as much as a speed one.** `verity standard push`
17
+ auto-versions, so a re-synthesis does not produce a private copy โ€” it produces
18
+ v4, and v4 is what the team is held to from then on.
19
+ - **Nothing about it can block a setup.** No token, offline, a 404, a service that
20
+ errors: every one of those falls through to the regular flow. An unreliable
21
+ answer is never reported as "no Standard stored" โ€” that would offer a fresh
22
+ synthesis over one the team already agreed.
23
+ - Unattended runs (`--yes`, CI) adopt without asking, for the same reason: a CI
24
+ box quietly authoring a new version of the team's Standard is not an outcome
25
+ anyone would choose on purpose.
26
+ - **`--no-adopt` opts out**, for a script that wants a fresh synthesis on purpose.
27
+ It skips the request as well as the write: asking a question whose answer is
28
+ discarded is a network call the user declined, and in a locked-down CI it is one
29
+ that can fail loudly for no reason.
30
+ - **Declining is remembered per VERSION, never as a mute.** Say no and init stops
31
+ asking about *that* Standard โ€” and offers the next one, because a new version is
32
+ new information. A boolean "never again" would have been the familiar
33
+ silent-forever failure: decline on Monday meaning to synthesize later, forget,
34
+ and every later run stays quiet while the project has no local config at all.
35
+
36
+ ### ๐Ÿ”ง Fixed
37
+
38
+ - **An upgrade no longer removes the gates you chose.** `.verity/setup.json` only
39
+ exists from this release, so every project configured before it had no recorded
40
+ answer โ€” including everyone who picked commit/push gates through the old
41
+ `/verity-setup`. The first upgraded `verity init` fell back to the shipped
42
+ default and reconciled those gates away, turning Stop back on. What you chose is
43
+ still legible in `.claude/settings.json`, so init reads it.
44
+ - `verity doctor` names the artifact that is actually missing instead of telling
45
+ you to run a phase that will decline: with a Standard adopted and no analysis
46
+ config, `/verity-setup` would have answered "already configured".
47
+ - The setup skill learned the same case โ€” Standard present, analysis config
48
+ missing โ€” and writes only the config rather than re-synthesizing the Standard.
49
+
50
+ ## [Unreleased]
51
+
8
52
  **Setup is one command.** `verity init` and `/verity-setup` were two half-flows
9
53
  that overlapped โ€” and the overlap was not harmless: init wired the Stop hook,
10
54
  then the skill reconciled the hooks to a selection it asked for afterwards,
package/README.md CHANGED
@@ -12,8 +12,29 @@ verity init
12
12
 
13
13
  `verity init` is the whole setup. It asks how deeply to review and when
14
14
  (on stop / before commit / before push), wires everything, offers the optional
15
- GitHub login โ€” then launches Claude Code to finish the half that needs a model:
16
- reading your codebase and synthesizing the Standard (`/verity-setup`).
15
+ GitHub login โ€” then either **adopts the Standard your team already has**, or
16
+ launches Claude Code to synthesize one from your codebase (`/verity-setup`).
17
+
18
+ ### Joining a project that already uses Verity
19
+
20
+ A Standard belongs to the project, not to your machine. So once you are signed in,
21
+ init asks the service whether this repository already has one:
22
+
23
+ ```
24
+ This repository already has a Standard on Verity: version 3, pushed by claude-code
25
+ on 2026-08-12 ยท with analysis config and .verityignore
26
+ Use the existing Standard? [Y/n]
27
+ ```
28
+
29
+ Say yes and it writes `.verity/standard.yaml`, `.codacy/codacy.config.json`,
30
+ `.verityignore` and `VERITY.md` straight from the service โ€” no model phase, and no
31
+ second version of your team's Standard (`verity standard push` auto-versions, so a
32
+ fresh synthesis would become the version everyone is held to). Say no and it stops asking about
33
+ that version โ€” and offers the next one, since a new version is new information.
34
+ With no Standard stored yet, the regular flow runs.
35
+ `--no-adopt` opts out of the whole check, including the request. Offline, signed out, or a service
36
+ that cannot answer: also the regular flow โ€” setup has never needed the network and
37
+ still doesn't.
17
38
 
18
39
  If Claude Code isn't on your PATH, or you're already inside a session, init prints
19
40
  the one command to run instead. `verity setup` is an alias for the same command.
@@ -22,6 +43,7 @@ the one command to run instead. `verity setup` is an alias for the same command.
22
43
  |------|-----|
23
44
  | `--yes` | Take the recommended answer for every question โ€” no prompts at all (CI, scripts, agents). Includes the GitHub login, whose unattended answer is **skip**: the project stays local-only until you run `verity login`. |
24
45
  | `--no-setup` | Stop after the deterministic phase; don't launch Claude Code |
46
+ | `--no-adopt` | Don't offer the Standard the service already holds for this repo โ€” synthesize a fresh one. Skips the request entirely |
25
47
  | `--force` | Overwrite the installed skills even when they differ |
26
48
 
27
49
  Run `verity doctor` any time to see prerequisites, which phase is done, the hook
package/bin/verity.js CHANGED
@@ -675,8 +675,8 @@ var require_option = __commonJS({
675
675
  * @param {(string | string[])} names
676
676
  * @return {Option}
677
677
  */
678
- conflicts(names) {
679
- this.conflictsWith = this.conflictsWith.concat(names);
678
+ conflicts(names2) {
679
+ this.conflictsWith = this.conflictsWith.concat(names2);
680
680
  return this;
681
681
  }
682
682
  /**
@@ -1249,8 +1249,8 @@ var require_command = __commonJS({
1249
1249
  * @param {string} names
1250
1250
  * @return {Command} `this` command for chaining
1251
1251
  */
1252
- arguments(names) {
1253
- names.trim().split(/ +/).forEach((detail) => {
1252
+ arguments(names2) {
1253
+ names2.trim().split(/ +/).forEach((detail) => {
1254
1254
  this.argument(detail);
1255
1255
  });
1256
1256
  return this;
@@ -19364,7 +19364,7 @@ function channelSilence(input) {
19364
19364
  // src/lib/cli-version.ts
19365
19365
  function cliVersion() {
19366
19366
  try {
19367
- return true ? "0.31.1-experimental.83a8619" : "dev";
19367
+ return true ? "0.31.1-experimental.b9b3862" : "dev";
19368
19368
  } catch {
19369
19369
  return "dev";
19370
19370
  }
@@ -23288,9 +23288,9 @@ function registerWaiveCommand(program2) {
23288
23288
  }
23289
23289
 
23290
23290
  // src/commands/init.ts
23291
- var import_node_fs44 = require("node:fs");
23292
- var import_promises14 = require("node:fs/promises");
23293
- var import_node_path29 = require("node:path");
23291
+ var import_node_fs45 = require("node:fs");
23292
+ var import_promises15 = require("node:fs/promises");
23293
+ var import_node_path30 = require("node:path");
23294
23294
  var import_node_child_process14 = require("node:child_process");
23295
23295
 
23296
23296
  // src/lib/banner.ts
@@ -23701,8 +23701,19 @@ async function buildReport() {
23701
23701
  if (c.status !== "ok" && c.remedy) next.push(c.remedy);
23702
23702
  }
23703
23703
  if (!state?.init) next.push('Run "verity init" \u2014 the deterministic setup phase has not completed here.');
23704
- if (!artifacts.standard || !artifacts.analysisConfig || !artifacts.verityMd) {
23705
- next.push("Run /verity-setup in Claude Code \u2014 the Standard, analysis config, and VERITY.md are synthesized there.");
23704
+ const missing = [
23705
+ !artifacts.standard && ".verity/standard.yaml",
23706
+ !artifacts.analysisConfig && ".codacy/codacy.config.json",
23707
+ !artifacts.verityMd && "VERITY.md"
23708
+ ].filter((x) => !!x);
23709
+ if (missing.length === 3) {
23710
+ next.push("Run /verity-setup in Claude Code \u2014 it synthesizes the Standard, the analysis config, and VERITY.md.");
23711
+ } else if (missing.length === 1 && !artifacts.analysisConfig) {
23712
+ next.push(
23713
+ 'No analysis config, so static analysis runs nothing. Run /verity-setup in Claude Code to write one (it keeps the Standard you already have), or have a teammate run "verity config push".'
23714
+ );
23715
+ } else if (missing.length > 0) {
23716
+ next.push(`Missing ${missing.join(" and ")}. Run "/verity-setup --force" in Claude Code to regenerate.`);
23706
23717
  }
23707
23718
  const noAnalysisMoment = !hooks.stop && hooks.guardOn.length === 0;
23708
23719
  if (noAnalysisMoment) {
@@ -24152,6 +24163,146 @@ async function promptMultiSelect(question, choices, fallback) {
24152
24163
  return parsed.ids;
24153
24164
  }
24154
24165
 
24166
+ // src/lib/remote-config.ts
24167
+ var import_node_fs44 = require("node:fs");
24168
+ var import_promises14 = require("node:fs/promises");
24169
+ var import_node_path29 = require("node:path");
24170
+ var import_yaml3 = __toESM(require_dist());
24171
+ var IGNORE_RIDER = "verityignore";
24172
+ async function fetchRemoteSetup(opts) {
24173
+ const standard = await apiRequest({
24174
+ method: "GET",
24175
+ path: "/standards/latest",
24176
+ serviceUrl: opts.serviceUrl,
24177
+ token: opts.token,
24178
+ verbose: opts.verbose
24179
+ });
24180
+ if (!standard.ok) {
24181
+ const missing = /STANDARD_NOT_FOUND|not found|404/i.test(standard.error);
24182
+ return missing ? { status: "none" } : { status: "unavailable", reason: standard.error };
24183
+ }
24184
+ const config = await apiRequest({
24185
+ method: "GET",
24186
+ path: "/analysis-configs",
24187
+ serviceUrl: opts.serviceUrl,
24188
+ token: opts.token,
24189
+ verbose: opts.verbose
24190
+ });
24191
+ return {
24192
+ status: "found",
24193
+ standard: {
24194
+ version: standard.data.version,
24195
+ content: standard.data.content ?? {},
24196
+ createdBy: standard.data.created_by,
24197
+ createdAt: standard.data.created_at
24198
+ },
24199
+ // Absent is normal and not an error: the two are stored independently, and a
24200
+ // project can have a Standard with no analysis config.
24201
+ analysisConfig: config.ok ? config.data.content ?? null : null
24202
+ };
24203
+ }
24204
+ async function adoptRemoteSetup(found, opts) {
24205
+ const written = [];
24206
+ const notes = [];
24207
+ const content = { ...found.standard.content };
24208
+ const rider = typeof content[IGNORE_RIDER] === "string" ? content[IGNORE_RIDER] : null;
24209
+ delete content[IGNORE_RIDER];
24210
+ await writeOut(STANDARD_FILE, (0, import_yaml3.stringify)(content));
24211
+ written.push(STANDARD_FILE);
24212
+ if (rider !== null) {
24213
+ const localIgnore = projectPath(VERITYIGNORE_FILE);
24214
+ if (!(0, import_node_fs44.existsSync)(localIgnore)) {
24215
+ await writeOut(VERITYIGNORE_FILE, rider);
24216
+ written.push(VERITYIGNORE_FILE);
24217
+ } else {
24218
+ const local = await (0, import_promises14.readFile)(localIgnore, "utf-8").catch(() => null);
24219
+ if (local !== null && local !== rider) {
24220
+ notes.push(`${VERITYIGNORE_FILE} already exists here and differs from the pushed copy \u2014 kept yours.`);
24221
+ }
24222
+ }
24223
+ }
24224
+ if (found.analysisConfig) {
24225
+ await writeOut(CODACY_CONFIG_FILE, JSON.stringify(found.analysisConfig, null, 2) + "\n");
24226
+ written.push(CODACY_CONFIG_FILE);
24227
+ } else {
24228
+ notes.push(
24229
+ "No analysis config is stored for this repository, so static analysis has nothing to run. Run /verity-setup in Claude Code to generate one."
24230
+ );
24231
+ }
24232
+ await writeOut(VERITY_MD_FILE, renderVerityMd(found.standard, opts));
24233
+ written.push(VERITY_MD_FILE);
24234
+ return { written, notes };
24235
+ }
24236
+ async function writeOut(relative, body) {
24237
+ const target = projectPath(relative);
24238
+ await (0, import_promises14.mkdir)((0, import_node_path29.dirname)(target), { recursive: true });
24239
+ await (0, import_promises14.writeFile)(target, body);
24240
+ }
24241
+ function names(content, key) {
24242
+ const raw = content[key];
24243
+ if (!Array.isArray(raw)) return [];
24244
+ return raw.map((entry) => {
24245
+ if (typeof entry === "string") return entry;
24246
+ if (entry && typeof entry === "object") {
24247
+ const rec = entry;
24248
+ const label2 = rec.name ?? rec.id ?? rec.dimension ?? rec.pattern;
24249
+ return typeof label2 === "string" ? label2 : null;
24250
+ }
24251
+ return null;
24252
+ }).filter((x) => !!x);
24253
+ }
24254
+ function renderVerityMd(standard, opts) {
24255
+ const dimensions = names(standard.content, "quality_dimensions");
24256
+ const patterns = names(standard.content, "security_patterns");
24257
+ const custom = names(standard.content, "custom_patterns");
24258
+ const list2 = (items, fallback) => (items.length > 0 ? items : fallback).map((i) => `- ${i}`).join("\n");
24259
+ return `# VERITY.md \u2014 Quality Gate
24260
+
24261
+ > This project uses [Verity](https://verity.md) to enforce quality and security standards on AI-generated code.
24262
+
24263
+ **URL:** ${opts.serviceUrl}
24264
+ **Project:** ${opts.projectId ?? "this repository"}
24265
+ **Standard:** v${standard.version} (adopted from the service${standard.createdBy ? `, created by ${standard.createdBy}` : ""})
24266
+
24267
+ ## Quality Dimensions
24268
+ ${list2(dimensions, ["comprehensibility", "modularity", "type_safety", "test_adequacy"])}
24269
+
24270
+ ## Security Patterns
24271
+ ${list2(patterns, [
24272
+ "No hardcoded secrets (CWE-798)",
24273
+ "Input sanitization (CWE-20)",
24274
+ "Parameterized queries (CWE-89)",
24275
+ "Dependency verification (CWE-1395)",
24276
+ "No unsafe deserialization (CWE-502)",
24277
+ "Access control checks (CWE-639)",
24278
+ "Config file integrity (CWE-15)"
24279
+ ])}
24280
+ ${custom.length > 0 ? `
24281
+ ## Project Patterns
24282
+ ${list2(custom, [])}
24283
+ ` : ""}
24284
+ ## How It Works
24285
+ Every time the coding agent stops, the Verity hook:
24286
+ 1. Runs static analysis via @codacy/analysis-cli
24287
+ 2. Sends results + code to the Verity service
24288
+ 3. An independent model reviews the code
24289
+ 4. Returns PASS / WARN / FAIL with actionable findings
24290
+
24291
+ _This Standard came from the service \u2014 the team's shared version. To change it,
24292
+ edit \`.verity/standard.yaml\` and run \`verity standard push\`, or re-synthesize
24293
+ from the codebase with \`/verity-setup --force\`._
24294
+ `;
24295
+ }
24296
+ function describeRemote(found) {
24297
+ const when = found.standard.createdAt.slice(0, 10);
24298
+ const who = found.standard.createdBy ? ` by ${found.standard.createdBy}` : "";
24299
+ const extras = [];
24300
+ if (found.analysisConfig) extras.push("analysis config");
24301
+ if (typeof found.standard.content[IGNORE_RIDER] === "string") extras.push(".verityignore");
24302
+ const also = extras.length > 0 ? ` \xB7 with ${extras.join(" and ")}` : "";
24303
+ return `version ${found.standard.version}, pushed${who} on ${when}${also}`;
24304
+ }
24305
+
24155
24306
  // src/commands/init.ts
24156
24307
  async function confirmExistingLogin(serviceUrl, remote, opts) {
24157
24308
  const existing = await resolveToken(opts.token);
@@ -24256,15 +24407,15 @@ async function runOptionalAuth(resolution, opts = {}) {
24256
24407
  }
24257
24408
  function resolveDataDir() {
24258
24409
  const candidates = [
24259
- (0, import_node_path29.join)(__dirname, "..", "data"),
24410
+ (0, import_node_path30.join)(__dirname, "..", "data"),
24260
24411
  // installed: node_modules/@codacy/verity-cli/data
24261
- (0, import_node_path29.join)(__dirname, "..", "..", "data"),
24412
+ (0, import_node_path30.join)(__dirname, "..", "..", "data"),
24262
24413
  // edge case: nested resolution
24263
- (0, import_node_path29.join)(process.cwd(), "cli", "data")
24414
+ (0, import_node_path30.join)(process.cwd(), "cli", "data")
24264
24415
  // local dev: running from repo root
24265
24416
  ];
24266
24417
  for (const candidate of candidates) {
24267
- if ((0, import_node_fs44.existsSync)((0, import_node_path29.join)(candidate, "skills"))) {
24418
+ if ((0, import_node_fs45.existsSync)((0, import_node_path30.join)(candidate, "skills"))) {
24268
24419
  return candidate;
24269
24420
  }
24270
24421
  }
@@ -24273,16 +24424,16 @@ function resolveDataDir() {
24273
24424
  );
24274
24425
  }
24275
24426
  async function copyDir(src, dest) {
24276
- await (0, import_promises14.mkdir)(dest, { recursive: true });
24277
- await (0, import_promises14.cp)(src, dest, { recursive: true, force: true });
24427
+ await (0, import_promises15.mkdir)(dest, { recursive: true });
24428
+ await (0, import_promises15.cp)(src, dest, { recursive: true, force: true });
24278
24429
  }
24279
24430
  async function skillIsCurrent(src, dest) {
24280
24431
  const list2 = (dir) => {
24281
24432
  const out = [];
24282
24433
  const walk = (d, prefix) => {
24283
- for (const e of (0, import_node_fs44.readdirSync)(d, { withFileTypes: true })) {
24434
+ for (const e of (0, import_node_fs45.readdirSync)(d, { withFileTypes: true })) {
24284
24435
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
24285
- if (e.isDirectory()) walk((0, import_node_path29.join)(d, e.name), rel);
24436
+ if (e.isDirectory()) walk((0, import_node_path30.join)(d, e.name), rel);
24286
24437
  else if (e.isFile()) out.push(rel);
24287
24438
  }
24288
24439
  };
@@ -24293,8 +24444,8 @@ async function skillIsCurrent(src, dest) {
24293
24444
  const shipped = list2(src);
24294
24445
  if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
24295
24446
  for (const rel of shipped) {
24296
- const a = await (0, import_promises14.readFile)((0, import_node_path29.join)(src, rel), "utf-8");
24297
- const b = await (0, import_promises14.readFile)((0, import_node_path29.join)(dest, rel), "utf-8");
24447
+ const a = await (0, import_promises15.readFile)((0, import_node_path30.join)(src, rel), "utf-8");
24448
+ const b = await (0, import_promises15.readFile)((0, import_node_path30.join)(dest, rel), "utf-8");
24298
24449
  if (a !== b) return false;
24299
24450
  }
24300
24451
  return true;
@@ -24323,9 +24474,17 @@ var MOMENT_CHOICES = [
24323
24474
  { id: "pre-push", label: "Before push / PR", hint: "reviews the to-be-pushed commits, blocks on FAIL" }
24324
24475
  ];
24325
24476
  var DEFAULT_MOMENTS = ["stop"];
24477
+ async function momentsFromInstalledHooks() {
24478
+ const status = await checkAllVerityHooks();
24479
+ const wired = [];
24480
+ if (status.stop) wired.push("stop");
24481
+ if (status.guardOn.includes("commit")) wired.push("pre-commit");
24482
+ if (status.guardOn.includes("push")) wired.push("pre-push");
24483
+ return wired.length > 0 ? wired : DEFAULT_MOMENTS;
24484
+ }
24326
24485
  async function askSetupQuestions(defaultsOnly, previous) {
24327
24486
  const intensityDefault = previous?.intensity ?? "balanced";
24328
- const momentsDefault = previous?.moments?.length ? previous.moments : DEFAULT_MOMENTS;
24487
+ const momentsDefault = previous?.moments?.length ? previous.moments : await momentsFromInstalledHooks();
24329
24488
  if (defaultsOnly) {
24330
24489
  return { intensity: intensityDefault, moments: momentsDefault, telemetry: "not-asked" };
24331
24490
  }
@@ -24362,6 +24521,73 @@ function insideClaudeCode() {
24362
24521
  function resumeDeferredPending(previous) {
24363
24522
  return previous?.telemetry === "deferred";
24364
24523
  }
24524
+ async function maybeAdoptRemoteSetup(opts) {
24525
+ if (!opts.adopt) {
24526
+ printInfo(" Skipped (--no-adopt) \u2014 the setup below runs from this codebase.");
24527
+ return false;
24528
+ }
24529
+ const token = await resolveToken(opts.globals.token);
24530
+ const url = await resolveServiceUrl(opts.globals.serviceUrl);
24531
+ if (!token.ok || !url.ok) {
24532
+ printInfo(" Not signed in, so there is nothing to fetch \u2014 the setup below runs from your codebase.");
24533
+ return false;
24534
+ }
24535
+ const found = await fetchRemoteSetup({
24536
+ token: token.data.token,
24537
+ serviceUrl: url.data,
24538
+ verbose: opts.globals.verbose
24539
+ });
24540
+ if (found.status === "unavailable") {
24541
+ printWarn(` Could not ask the service (${found.reason}) \u2014 continuing with the regular setup.`);
24542
+ return false;
24543
+ }
24544
+ if (found.status === "none") {
24545
+ printInfo(" No Standard stored for this repository yet \u2014 this setup will create the first one.");
24546
+ return false;
24547
+ }
24548
+ if (opts.previous?.declinedStandardVersion === found.standard.version) {
24549
+ printInfo(` Standard v${found.standard.version} is available; you declined this version before \u2014 not asking again.`);
24550
+ printInfo(" A newer version will be offered. To take it now: verity standard get, or /verity-setup.");
24551
+ return false;
24552
+ }
24553
+ console.log("");
24554
+ console.log(` This repository already has a Standard on Verity: ${describeRemote(found)}`);
24555
+ console.log(" Using it keeps you consistent with everyone else on the project.");
24556
+ console.log(" Synthesizing a new one from this codebase would push a competing version.");
24557
+ console.log("");
24558
+ const use = opts.defaultsOnly ? true : await promptYes(" Use the existing Standard? [Y/n] ", { nonInteractive: true });
24559
+ if (!use) {
24560
+ printInfo(" Keeping the existing version untouched; /verity-setup will synthesize a new one.");
24561
+ await writeSetupState({ declinedStandardVersion: found.standard.version }).catch(() => {
24562
+ });
24563
+ return false;
24564
+ }
24565
+ try {
24566
+ const outcome = await adoptRemoteSetup(found, {
24567
+ serviceUrl: url.data,
24568
+ projectId: await projectLabel(token.data.token, url.data, opts.globals.verbose)
24569
+ });
24570
+ for (const path of outcome.written) printInfo(` ${path} \u2713`);
24571
+ for (const note of outcome.notes) printWarn(` ${note}`);
24572
+ if (opts.previous?.declinedStandardVersion != null) {
24573
+ await writeSetupState({ declinedStandardVersion: void 0 }).catch(() => {
24574
+ });
24575
+ }
24576
+ return true;
24577
+ } catch (err) {
24578
+ printWarn(` Could not write the fetched configuration: ${err.message}`);
24579
+ printInfo(" Falling back to the regular setup.");
24580
+ return false;
24581
+ }
24582
+ }
24583
+ async function projectLabel(token, serviceUrl, verbose) {
24584
+ try {
24585
+ const who = await whoami(token, serviceUrl, verbose);
24586
+ return who.ok ? who.data.project_id : null;
24587
+ } catch {
24588
+ return null;
24589
+ }
24590
+ }
24365
24591
  var PHASE_TWO_ARTIFACTS = [
24366
24592
  {
24367
24593
  path: ".verity/standard.yaml",
@@ -24438,12 +24664,13 @@ async function handoffToSetup(enabled, claudeInstalled) {
24438
24664
  await reportPhaseTwo(startedAt);
24439
24665
  }
24440
24666
  function registerInitCommand(program2) {
24441
- program2.command("init").alias("setup").description("Set up Verity in the current project (asks the setup questions, then hands off to /verity-setup)").option("--force", "Reinstall the skills even when they are already up to date (hooks are always reconciled)").option("-y, --yes", "Take the recommended answer for every question (no prompts)").option("--no-setup", "Skip the /verity-setup handoff at the end").action(async (opts) => {
24667
+ program2.command("init").alias("setup").description("Set up Verity in the current project (asks the setup questions, then hands off to /verity-setup)").option("--force", "Reinstall the skills even when they are already up to date (hooks are always reconciled)").option("-y, --yes", "Take the recommended answer for every question (no prompts)").option("--no-setup", "Skip the /verity-setup handoff at the end").option("--no-adopt", "Don't offer this repository's existing Standard from the service; synthesize a new one").action(async (opts) => {
24442
24668
  const force = opts.force ?? false;
24443
24669
  const wantsHandoff = opts.setup !== false;
24670
+ const wantsAdopt = opts.adopt !== false;
24444
24671
  const defaultsOnly = (opts.yes ?? false) || !interactive();
24445
24672
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
24446
- const isProject = projectMarkers.some((m) => (0, import_node_fs44.existsSync)(m));
24673
+ const isProject = projectMarkers.some((m) => (0, import_node_fs45.existsSync)(m));
24447
24674
  if (!isProject) {
24448
24675
  printError("No project detected in the current directory.");
24449
24676
  printInfo('Run "verity init" from your project root.');
@@ -24458,7 +24685,7 @@ function registerInitCommand(program2) {
24458
24685
  } else {
24459
24686
  printPhase(1, 2, "this machine", "prerequisites \xB7 skills \xB7 hooks \xB7 sign-in");
24460
24687
  }
24461
- const TOTAL_STEPS = 8;
24688
+ const TOTAL_STEPS = 9;
24462
24689
  let stepNo = 0;
24463
24690
  const step = (label2) => {
24464
24691
  stepNo++;
@@ -24493,17 +24720,17 @@ function registerInitCommand(program2) {
24493
24720
  console.log("");
24494
24721
  step("Installing skills");
24495
24722
  const dataDir = resolveDataDir();
24496
- const skillsSource = (0, import_node_path29.join)(dataDir, "skills");
24723
+ const skillsSource = (0, import_node_path30.join)(dataDir, "skills");
24497
24724
  const skillsDest = ".claude/skills";
24498
24725
  let skillsInstalled = 0;
24499
24726
  for (const skill of SKILLS) {
24500
- const src = (0, import_node_path29.join)(skillsSource, skill);
24501
- const dest = (0, import_node_path29.join)(skillsDest, skill);
24502
- if (!(0, import_node_fs44.existsSync)(src)) {
24727
+ const src = (0, import_node_path30.join)(skillsSource, skill);
24728
+ const dest = (0, import_node_path30.join)(skillsDest, skill);
24729
+ if (!(0, import_node_fs45.existsSync)(src)) {
24503
24730
  printWarn(` Skill data not found: ${skill}`);
24504
24731
  continue;
24505
24732
  }
24506
- if ((0, import_node_fs44.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) {
24733
+ if ((0, import_node_fs45.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) {
24507
24734
  skillsInstalled++;
24508
24735
  continue;
24509
24736
  }
@@ -24519,7 +24746,7 @@ function registerInitCommand(program2) {
24519
24746
  printInfo(` intensity: ${intensity} \xB7 moments: ${moments.join(", ") || "none"} (no questions asked)`);
24520
24747
  }
24521
24748
  step("Knowledge base, .gitignore and CLAUDE.md");
24522
- await (0, import_promises14.mkdir)(VERITY_DIR, { recursive: true });
24749
+ await (0, import_promises15.mkdir)(VERITY_DIR, { recursive: true });
24523
24750
  await ensureMemoryDir();
24524
24751
  const ignoreResult = ensureVerityGitignore();
24525
24752
  if (ignoreResult === "failed") {
@@ -24553,8 +24780,8 @@ function registerInitCommand(program2) {
24553
24780
  } catch (err) {
24554
24781
  printWarn(` Could not update CLAUDE.md: ${err.message}`);
24555
24782
  }
24556
- const globalVerityDir = (0, import_node_path29.join)(process.env.HOME ?? "", ".verity");
24557
- await (0, import_promises14.mkdir)(globalVerityDir, { recursive: true });
24783
+ const globalVerityDir = (0, import_node_path30.join)(process.env.HOME ?? "", ".verity");
24784
+ await (0, import_promises15.mkdir)(globalVerityDir, { recursive: true });
24558
24785
  console.log("");
24559
24786
  step("Wiring Claude Code hooks");
24560
24787
  await applyMomentSelection(moments);
@@ -24606,6 +24833,18 @@ function registerInitCommand(program2) {
24606
24833
  printWarn('Telemetry needs a Verity token \u2014 run "verity login", then "verity telemetry install".');
24607
24834
  }
24608
24835
  }
24836
+ step("Checking Verity for an existing setup");
24837
+ let adopted = false;
24838
+ if ((0, import_node_fs45.existsSync)(projectPath(STANDARD_FILE))) {
24839
+ printInfo(" This project already has .verity/standard.yaml \u2014 keeping it.");
24840
+ } else {
24841
+ adopted = await maybeAdoptRemoteSetup({
24842
+ globals: program2.opts(),
24843
+ defaultsOnly,
24844
+ previous,
24845
+ adopt: wantsAdopt
24846
+ });
24847
+ }
24609
24848
  step("Recording your answers");
24610
24849
  try {
24611
24850
  await writeSetupState({
@@ -24614,7 +24853,7 @@ function registerInitCommand(program2) {
24614
24853
  ...telemetryChoice ? { telemetry: telemetryChoice } : {},
24615
24854
  init: {
24616
24855
  completed_at: (/* @__PURE__ */ new Date()).toISOString(),
24617
- cli_version: true ? "0.31.1-experimental.83a8619" : "dev"
24856
+ cli_version: true ? "0.31.1-experimental.b9b3862" : "dev"
24618
24857
  }
24619
24858
  });
24620
24859
  } catch (err) {
@@ -24632,14 +24871,20 @@ function registerInitCommand(program2) {
24632
24871
  console.log(" CLAUDE.md memory pointer, waive policy, reflection");
24633
24872
  console.log("");
24634
24873
  console.log(` Intensity: ${intensity} Moments: ${moments.join(", ") || "none"}`);
24635
- await handoffToSetup(wantsHandoff, claudeInstalled);
24874
+ if (adopted) {
24875
+ console.log("");
24876
+ printInfo("Setup complete \u2014 this project uses your team's Standard.");
24877
+ printInfo(" Re-synthesize from this codebase instead: /verity-setup --force");
24878
+ } else {
24879
+ await handoffToSetup(wantsHandoff, claudeInstalled);
24880
+ }
24636
24881
  console.log("");
24637
24882
  });
24638
24883
  }
24639
24884
 
24640
24885
  // src/commands/uninstall.ts
24641
- var import_node_fs45 = require("node:fs");
24642
- var import_node_path30 = require("node:path");
24886
+ var import_node_fs46 = require("node:fs");
24887
+ var import_node_path31 = require("node:path");
24643
24888
  var SKILL_NAMES = [
24644
24889
  "verity-setup",
24645
24890
  "verity-analyze",
@@ -24658,11 +24903,11 @@ function registerUninstallCommand(program2) {
24658
24903
  const actions = [];
24659
24904
  const skillsRoot = projectPath(".claude/skills");
24660
24905
  for (const name of SKILL_NAMES) {
24661
- const dir = (0, import_node_path30.join)(skillsRoot, name);
24662
- if ((0, import_node_fs45.existsSync)(dir)) {
24906
+ const dir = (0, import_node_path31.join)(skillsRoot, name);
24907
+ if ((0, import_node_fs46.existsSync)(dir)) {
24663
24908
  actions.push({
24664
24909
  label: `Remove .claude/skills/${name}/`,
24665
- apply: () => (0, import_node_fs45.rmSync)(dir, { recursive: true, force: true })
24910
+ apply: () => (0, import_node_fs46.rmSync)(dir, { recursive: true, force: true })
24666
24911
  });
24667
24912
  }
24668
24913
  }
@@ -24676,24 +24921,24 @@ function registerUninstallCommand(program2) {
24676
24921
  });
24677
24922
  }
24678
24923
  const verityDir = projectPath(VERITY_DIR);
24679
- if ((0, import_node_fs45.existsSync)(verityDir)) {
24924
+ if ((0, import_node_fs46.existsSync)(verityDir)) {
24680
24925
  actions.push({
24681
24926
  label: `Remove ${VERITY_DIR}/`,
24682
- apply: () => (0, import_node_fs45.rmSync)(verityDir, { recursive: true, force: true })
24927
+ apply: () => (0, import_node_fs46.rmSync)(verityDir, { recursive: true, force: true })
24683
24928
  });
24684
24929
  }
24685
24930
  if (!keepVerityMd) {
24686
24931
  const verityMd = projectPath(VERITY_MD_FILE);
24687
- if ((0, import_node_fs45.existsSync)(verityMd)) {
24932
+ if ((0, import_node_fs46.existsSync)(verityMd)) {
24688
24933
  actions.push({
24689
24934
  label: `Remove ${VERITY_MD_FILE}`,
24690
- apply: () => (0, import_node_fs45.rmSync)(verityMd, { force: true })
24935
+ apply: () => (0, import_node_fs46.rmSync)(verityMd, { force: true })
24691
24936
  });
24692
24937
  }
24693
24938
  }
24694
24939
  const cleanupEmptyDir = (path) => {
24695
- if ((0, import_node_fs45.existsSync)(path) && (0, import_node_fs45.statSync)(path).isDirectory() && (0, import_node_fs45.readdirSync)(path).length === 0) {
24696
- (0, import_node_fs45.rmdirSync)(path);
24940
+ if ((0, import_node_fs46.existsSync)(path) && (0, import_node_fs46.statSync)(path).isDirectory() && (0, import_node_fs46.readdirSync)(path).length === 0) {
24941
+ (0, import_node_fs46.rmdirSync)(path);
24697
24942
  }
24698
24943
  };
24699
24944
  actions.push({
@@ -24704,11 +24949,11 @@ function registerUninstallCommand(program2) {
24704
24949
  }
24705
24950
  });
24706
24951
  const home = process.env.HOME ?? "";
24707
- const globalVerityDir = (0, import_node_path30.join)(home, ".verity");
24708
- if (purgeGlobal && (0, import_node_fs45.existsSync)(globalVerityDir)) {
24952
+ const globalVerityDir = (0, import_node_path31.join)(home, ".verity");
24953
+ if (purgeGlobal && (0, import_node_fs46.existsSync)(globalVerityDir)) {
24709
24954
  actions.push({
24710
24955
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
24711
- apply: () => (0, import_node_fs45.rmSync)(globalVerityDir, { recursive: true, force: true })
24956
+ apply: () => (0, import_node_fs46.rmSync)(globalVerityDir, { recursive: true, force: true })
24712
24957
  });
24713
24958
  }
24714
24959
  if (actions.length === 0) {
@@ -24902,8 +25147,8 @@ function registerTaskCommands(program2) {
24902
25147
  }
24903
25148
 
24904
25149
  // src/commands/reset.ts
24905
- var import_node_fs46 = require("node:fs");
24906
- var import_node_path31 = require("node:path");
25150
+ var import_node_fs47 = require("node:fs");
25151
+ var import_node_path32 = require("node:path");
24907
25152
  function registerResetCommand(program2) {
24908
25153
  program2.command("reset").description("Close the current task and clear transient state").option("--keep-task", "Only purge caches; leave the current task open").option("--all", "Also purge diagnostic logs (.verity/.logs/)").action(async (opts) => {
24909
25154
  const globals = program2.opts();
@@ -24940,11 +25185,11 @@ function registerResetCommand(program2) {
24940
25185
  }
24941
25186
  const cacheDir = projectPath(CACHE_DIR);
24942
25187
  let purged = 0;
24943
- if ((0, import_node_fs46.existsSync)(cacheDir)) {
24944
- for (const entry of (0, import_node_fs46.readdirSync)(cacheDir)) {
25188
+ if ((0, import_node_fs47.existsSync)(cacheDir)) {
25189
+ for (const entry of (0, import_node_fs47.readdirSync)(cacheDir)) {
24945
25190
  if (entry.startsWith("pending-")) {
24946
25191
  try {
24947
- (0, import_node_fs46.unlinkSync)((0, import_node_path31.join)(cacheDir, entry));
25192
+ (0, import_node_fs47.unlinkSync)((0, import_node_path32.join)(cacheDir, entry));
24948
25193
  purged++;
24949
25194
  } catch {
24950
25195
  }
@@ -24959,19 +25204,19 @@ function registerResetCommand(program2) {
24959
25204
  projectPath(`${VERITY_DIR}/.last-analysis`)
24960
25205
  ];
24961
25206
  for (const file of filesToClear) {
24962
- if ((0, import_node_fs46.existsSync)(file)) {
25207
+ if ((0, import_node_fs47.existsSync)(file)) {
24963
25208
  try {
24964
- (0, import_node_fs46.writeFileSync)(file, "");
25209
+ (0, import_node_fs47.writeFileSync)(file, "");
24965
25210
  } catch {
24966
25211
  }
24967
25212
  }
24968
25213
  }
24969
25214
  if (opts.all) {
24970
25215
  const logsDir = projectPath(`${VERITY_DIR}/.logs`);
24971
- if ((0, import_node_fs46.existsSync)(logsDir)) {
24972
- for (const entry of (0, import_node_fs46.readdirSync)(logsDir)) {
25216
+ if ((0, import_node_fs47.existsSync)(logsDir)) {
25217
+ for (const entry of (0, import_node_fs47.readdirSync)(logsDir)) {
24973
25218
  try {
24974
- (0, import_node_fs46.unlinkSync)((0, import_node_path31.join)(logsDir, entry));
25219
+ (0, import_node_fs47.unlinkSync)((0, import_node_path32.join)(logsDir, entry));
24975
25220
  } catch {
24976
25221
  }
24977
25222
  }
@@ -25279,8 +25524,8 @@ function registerTelemetryCommands(program2) {
25279
25524
  }
25280
25525
 
25281
25526
  // src/cli.ts
25282
- program.name("verity").description("CLI for Verity quality gate service").version("0.31.1-experimental.83a8619").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
25283
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.1-experimental.83a8619");
25527
+ program.name("verity").description("CLI for Verity quality gate service").version("0.31.1-experimental.b9b3862").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
25528
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.1-experimental.b9b3862");
25284
25529
  setUserNamedServiceUrl(program.opts().serviceUrl);
25285
25530
  try {
25286
25531
  await foldLegacyLocalCredential();
@@ -46,6 +46,13 @@ answers, the hook wiring, and a `next` list. Branch on it:
46
46
  to launch a second Claude Code session on top of this one.
47
47
  - **`blocked` is true** โ†’ a required prerequisite is missing. Show the
48
48
  `prerequisites[].remedy` lines and stop; nothing below can work.
49
+ - **`artifacts.standard` is true but `artifacts.analysisConfig` is false** โ†’ this
50
+ project adopted a Standard rather than synthesizing one (`verity init` can pull
51
+ the team's Standard straight from the service), and the only missing piece is
52
+ the analysis config. **Do Step 2 and Step 5 only**, then `verity config push`,
53
+ then stop. Do NOT re-synthesize the Standard: the team already agreed one, and
54
+ `verity standard push` auto-versions, so writing a new one lands as the version
55
+ everybody is then held to.
49
56
  - **`artifacts.verityMd` is true and the user did not pass `--force`** โ†’ already
50
57
  configured. Say "Already configured. Run `/verity-setup --force` to
51
58
  reconfigure." and stop.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/verity-cli",
3
- "version": "0.31.1-experimental.83a8619",
3
+ "version": "0.31.1-experimental.b9b3862",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://verity.md",