@codacy/verity-cli 0.35.2 → 0.35.3

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/bin/verity.js +237 -44
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -64,6 +64,32 @@ installs are untouched.
64
64
  which is the ordinary case for someone whose first Verity install *is* the
65
65
  plugin.
66
66
 
67
+ ## [0.35.3] — 2026-11-05
68
+
69
+ ### 🔧 Fixes
70
+
71
+ - **`verity init` on a repository the Verity GitHub App cannot see gives it
72
+ access in the same run.** Init offers a GitHub sign-in that opens the page
73
+ where you add the repository to the Verity app, waits for it, and then
74
+ continues. Init says why a second sign-in is needed: Verity reads your
75
+ repository access from GitHub only when you sign in. With `--yes` or no
76
+ terminal, init names the one action instead.
77
+ - **Init states a refusal as a refusal.** When the service refuses access to
78
+ the repository, init writes nothing, says that Verity cannot access the
79
+ repository yet, names the one action, and exits 1. Before, it said the
80
+ service did not answer and told you to run `verity init` again.
81
+ - **A turn with nothing new to review no longer prints a Verity line.**
82
+ Before, a line such as "No files modified since last analysis" showed under
83
+ every message. `verity config verbose on` brings the line back. Turns that
84
+ wrote code still get their review, and a change that nobody reviewed is
85
+ still reported.
86
+ - **The upgrade link in a plan-limit message is clickable.** It keeps its
87
+ `https://`.
88
+ - **`verity usage` says "your plan" for the plan you own, and `verity status`
89
+ no longer lists that plan under "member of".** A team you belong to is still
90
+ listed as a team.
91
+ - Includes security fixes.
92
+
67
93
  ## [0.35.2] — 2026-10-30
68
94
 
69
95
  ### 🔧 Fixes
package/bin/verity.js CHANGED
@@ -15325,7 +15325,7 @@ function pinRefusalMessage(verdict, targetUrl) {
15325
15325
  // src/lib/cli-version.ts
15326
15326
  function cliVersion() {
15327
15327
  try {
15328
- return true ? "0.35.2" : "dev";
15328
+ return true ? "0.35.3" : "dev";
15329
15329
  } catch {
15330
15330
  return "dev";
15331
15331
  }
@@ -19207,13 +19207,21 @@ function readConfigFile(path) {
19207
19207
  git_moments: [...new Set(moments)],
19208
19208
  ...source2 ? { git_moments_source: source2 } : {},
19209
19209
  ...raw.formal === "push" || raw.formal === "off" ? { formal: raw.formal } : {},
19210
- ...raw.turn_review === "on" || raw.turn_review === "off" ? { turn_review: raw.turn_review } : {}
19210
+ ...raw.turn_review === "on" || raw.turn_review === "off" ? { turn_review: raw.turn_review } : {},
19211
+ ...raw.verbose === "on" || raw.verbose === "off" ? { verbose: raw.verbose } : {}
19211
19212
  }
19212
19213
  };
19213
19214
  } catch {
19214
19215
  return { cfg: DEFAULTS };
19215
19216
  }
19216
19217
  }
19218
+ function readVerbose() {
19219
+ try {
19220
+ return readProjectConfigRaw().verbose === "on";
19221
+ } catch {
19222
+ return false;
19223
+ }
19224
+ }
19217
19225
  function configRefusalMessage(reason) {
19218
19226
  switch (reason) {
19219
19227
  case "link":
@@ -19285,12 +19293,13 @@ function formalRefusalMessage(reason) {
19285
19293
  }
19286
19294
  }
19287
19295
  function writeProjectConfig(patch) {
19288
- const { git_moments, git_moments_source, formal, turn_review } = readProjectConfigRaw();
19296
+ const { git_moments, git_moments_source, formal, turn_review, verbose } = readProjectConfigRaw();
19289
19297
  const next = {
19290
19298
  git_moments,
19291
19299
  ...git_moments_source ? { git_moments_source } : {},
19292
19300
  ...formal ? { formal } : {},
19293
19301
  ...turn_review ? { turn_review } : {},
19302
+ ...verbose ? { verbose } : {},
19294
19303
  ...patch
19295
19304
  };
19296
19305
  writeRepoFile(PROJECT_CONFIG_FILE, JSON.stringify(next, null, 2) + "\n");
@@ -21715,6 +21724,112 @@ var LEGACY_MD_END = "<!-- gate-memory:end -->";
21715
21724
  var LEGACY_PRESERVE_START = "<!-- gate-memory:preserve -->";
21716
21725
  var LEGACY_PRESERVE_END = "<!-- /gate-memory:preserve -->";
21717
21726
  var CLAUDE_MD_PROSE = [
21727
+ "## Project Memory",
21728
+ "",
21729
+ "This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
21730
+ "non-trivial work, scan `.verity/memory/index.md` for decisions, gotchas, and patterns",
21731
+ "that may apply to the change you are about to make. Open specific node files via",
21732
+ "the Read tool when the title or scope suggests relevance.",
21733
+ "",
21734
+ "The graph is auto-maintained by Verity. Files at `.verity/memory/_archive/` are",
21735
+ "superseded \u2014 ignore them unless investigating history.",
21736
+ "",
21737
+ "Knowledge the organization's other repositories learned is mirrored outside the",
21738
+ "repo at `~/.verity/orgs/<host>/<owner>/memory/` (`verity memory org` lists it).",
21739
+ "Each claim says where it came from; one that is wrong here is demoted for",
21740
+ 'everyone with `verity memory demote <id> --reason "\u2026"`.',
21741
+ "",
21742
+ "## Quality gate: accepted risks",
21743
+ "",
21744
+ "When the Verity pre-commit/pre-push gate FAILs, fix the findings \u2014 that is the",
21745
+ 'default. Use `verity waive <pattern-id> --file <path> --reason "\u2026"` ONLY to relay',
21746
+ "a risk a human has explicitly accepted: a named code-review finding, an ADR, or",
21747
+ "the user saying so in this conversation. The --reason must cite that source.",
21748
+ "",
21749
+ "Never waive on your own judgment, to get past a block, or pre-emptively. A waive",
21750
+ "binds to the file's current bytes and voids automatically when the file changes,",
21751
+ "and every waive is recorded in the run ledger. For a pattern-level false positive",
21752
+ "use `verity feedback finding <run-id> <pattern-id> false_positive` instead.",
21753
+ "",
21754
+ "## Post-task reflection",
21755
+ "",
21756
+ `When a task is complete (you've created a PR, the user says "done" or "ship it",`,
21757
+ "or the work is clearly finished), **draft the reflection yourself** \u2014 1\u20133",
21758
+ "concrete things worth remembering (a decision, a gotcha, or a pattern), each",
21759
+ "cited with the files / PR / commands / error-signatures it came from. Skip",
21760
+ "entirely if nothing non-obvious happened \u2014 that judgement is the ONLY filter,",
21761
+ "because nothing reviews the reflection before it lands.",
21762
+ "",
21763
+ "Then record it straight away. There is no confirm step, in any environment:",
21764
+ "",
21765
+ "```bash",
21766
+ "verity reflect --user-input - --kind <kind> <<'VERITY_EOF_<random>'",
21767
+ "<your draft>",
21768
+ "VERITY_EOF_<random>",
21769
+ "```",
21770
+ "",
21771
+ 'The draft goes through the quoted heredoc, never inside `"\u2026"` on the command',
21772
+ 'line, where a `"` or `$(\u2026)` in it would run in the shell. Replace `<random>`',
21773
+ "with 8 random letters and digits that you pick for this call, the same at both",
21774
+ "ends. Text written before you picked them cannot end the heredoc early.",
21775
+ "",
21776
+ "Add `--confirmed` ONLY when the user authored or dictated the words. Without",
21777
+ "it the node is stored as `source: agent` \u2014 Verity thought this, nobody checked",
21778
+ "it. With it, `source: user` at full confidence \u2014 a person stands behind this.",
21779
+ "Never claim the second for your own draft, however good it is.",
21780
+ "",
21781
+ "**Name the files in the text.** Verity scopes the reflection to the paths it",
21782
+ "cites, and a reflection that names no file in this repo is never retrieved for",
21783
+ "a later review \u2014 it is recorded and then invisible. The command says so when it",
21784
+ "happens; `--file-globs '<path or glob>'` is the fix when the prose cannot carry",
21785
+ "the paths.",
21786
+ "",
21787
+ "Then tell the user, in one line, what you recorded and where: the command",
21788
+ "prints the node id, the path under `.verity/memory/`, and a dashboard link.",
21789
+ "They did not agree to it in advance, so say it happened \u2014 editing or deleting",
21790
+ "that file is how they correct it.",
21791
+ "",
21792
+ "> Durable, hand-curated guidance goes in the preserve region below (it survives",
21793
+ "> regeneration) or anywhere OUTSIDE these markers. Everything else between the",
21794
+ "> markers is tool-owned and overwritten on each run.",
21795
+ "",
21796
+ "## Housekeeping Turns",
21797
+ "",
21798
+ "When a turn will be pure housekeeping \u2014 pulling, installing dependencies,",
21799
+ "rebasing, a formatting sweep you are not authoring \u2014 declare it BEFORE doing it:",
21800
+ "",
21801
+ "```bash",
21802
+ 'verity ignore --turn --agent --reason "pulling latest before starting"',
21803
+ "```",
21804
+ "",
21805
+ "This skips the review for that turn, which saves the turn Verity would",
21806
+ "otherwise spend saying it had nothing to say. Use `--for 30m` instead of",
21807
+ "`--turn` when a single piece of housekeeping spans several turns.",
21808
+ "",
21809
+ "**It is a claim about the turn, not a way to silence review.** The declaration",
21810
+ "is checked against what the turn actually did: if anything is authored \u2014 by you,",
21811
+ "by a subagent, or by a shell command that can write files \u2014 it voids, the review",
21812
+ "runs anyway, and the broken declaration is reported. So declare housekeeping you",
21813
+ "are about to do, never work you have already done, and never as a way to get past",
21814
+ "a finding. Declarations are budgeted per session and every one is recorded with",
21815
+ "its reason.",
21816
+ "",
21817
+ "## Verity settings",
21818
+ "",
21819
+ "Change these only when the user asks. Each command prints what it changed.",
21820
+ "",
21821
+ "- When Verity reviews: `verity config moments turn,commit,push`. Give any of the",
21822
+ " three, or `none`. With no list, it prints the current setting.",
21823
+ "- Formal verification at push: `verity config formal push`, or `verity config formal off`.",
21824
+ "- The line Verity prints when a turn has nothing new to review: `verity config verbose on`,",
21825
+ " or `verity config verbose off` (the default).",
21826
+ "- Project rules, written by Verity from this repository's history and docs:",
21827
+ " `verity standard generate`. It works only for a project with no Standard on the",
21828
+ " service. It writes `.verity/repo-profile.json` and sends nothing without consent.",
21829
+ " Show the user that file. Run the command again with `--yes` only after they agree.",
21830
+ "- The rules themselves: edit `.verity/standard.yaml`, then run `verity standard push`."
21831
+ ].join("\n");
21832
+ var CLAUDE_MD_PROSE_PRE_VERBOSE = [
21718
21833
  "## Project Memory",
21719
21834
  "",
21720
21835
  "This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
@@ -22351,6 +22466,7 @@ function stripKnownProse(interior) {
22351
22466
  const trimmed = interior.replace(/^\n+/, "");
22352
22467
  for (const prose of [
22353
22468
  CLAUDE_MD_PROSE,
22469
+ CLAUDE_MD_PROSE_PRE_VERBOSE,
22354
22470
  CLAUDE_MD_PROSE_PRE_SETTINGS,
22355
22471
  CLAUDE_MD_PROSE_PRE_STDIN,
22356
22472
  CLAUDE_MD_PROSE_PRE_ORG,
@@ -26343,6 +26459,18 @@ function registerConfigCommands(program2) {
26343
26459
  printInfo(state === "push" ? "Formal verification at push: on for this checkout (Lean must be installed \u2014 verity lean status)" : "Formal verification at push: off");
26344
26460
  if (state === "push") printInfo("The phase runs with your Verity login (a push without one is skipped and says so: not-logged-in)");
26345
26461
  });
26462
+ config.command("verbose [state]").description('Get or set the line Verity prints when a turn has nothing new to review: "on" or "off" (the default)').action((state) => {
26463
+ if (state === void 0) {
26464
+ process.stdout.write((readProjectConfig().verbose === "on" ? "on" : "off") + "\n");
26465
+ return;
26466
+ }
26467
+ if (state !== "on" && state !== "off") {
26468
+ printError(`Unrecognised state: ${state}. Use "on" or "off".`);
26469
+ process.exit(1);
26470
+ }
26471
+ writeProjectConfig({ verbose: state });
26472
+ printInfo(state === "on" ? "Verbose: on. A turn with nothing new to review now says so." : "Verbose: off. A turn with nothing new to review says nothing.");
26473
+ });
26346
26474
  config.command("push").description("Upload .codacy/codacy.config.json to the service (not used by the gate, which runs Trivy only)").option("--file <path>", "Path to config file", CODACY_CONFIG_FILE).action(async (opts) => {
26347
26475
  const globals = program2.opts();
26348
26476
  const tokenResult = await resolveToken(globals.token);
@@ -26818,8 +26946,8 @@ function timeAgo(isoDate) {
26818
26946
  }
26819
26947
  var PLAN_NAME = { free: "Free", pro: "Pro", fleet: "Fleet", enterprise: "Enterprise" };
26820
26948
  function planLine(plan, accounts) {
26821
- const teams = accounts.filter((a) => a.kind !== "personal").map((a) => `${a.name} (${PLAN_NAME[a.plan] ?? a.plan})`);
26822
- return `${PLAN_NAME[plan] ?? plan}${teams.length ? ` \xB7 member of ${teams.join(", ")}` : ""} \u2014 limits: "verity usage"`;
26949
+ const teams = accounts.filter((a) => !(a.is_owner ?? a.kind === "personal")).map((a) => `${terminalText(a.name)} (${PLAN_NAME[a.plan] ?? terminalText(a.plan)})`);
26950
+ return `${PLAN_NAME[plan] ?? terminalText(plan)}${teams.length ? ` \xB7 member of ${teams.join(", ")}` : ""} \u2014 limits: "verity usage"`;
26823
26951
  }
26824
26952
  function registerStatusCommand(program2) {
26825
26953
  program2.command("status").description("Show project quality status").option("--history", "Include recent run history").option("--limit <n>", "Number of history entries (1-100)", "5").option("--json", "Output raw JSON").action(async (opts) => {
@@ -27070,7 +27198,7 @@ function dollars(cents2) {
27070
27198
  return `$${((cents2 ?? 0) / 100).toFixed(2)}`;
27071
27199
  }
27072
27200
  function link(l) {
27073
- return (l.upgrade_url ?? "https://verity.md/upgrade").replace(/^https?:\/\//, "");
27201
+ return terminalUrl(l.upgrade_url) || "https://verity.md/upgrade";
27074
27202
  }
27075
27203
  function detail(l, now) {
27076
27204
  const reset = resetPhrase(l, now, true);
@@ -27081,7 +27209,8 @@ function detail(l, now) {
27081
27209
  return `Extra usage reached this month's ${dollars(l.limit)} cap. ${Reset}. ${way2}`;
27082
27210
  }
27083
27211
  if (l.period === "week") {
27084
- const who = shared && l.account ? `${l.account} used its` : "You used your";
27212
+ const team = shared ? terminalText(l.account) : "";
27213
+ const who = team ? `${team} used its` : "You used your";
27085
27214
  const way2 = l.budget === "free" ? `Upgrade: ${link(l)}` : shared ? `An admin can turn on extra usage, or upgrade: ${link(l)}` : `Turn on extra usage or upgrade: ${link(l)}`;
27086
27215
  return `${who} ${l.limit} ${METER[l.meter]} this week. ${Reset}. ${way2}`;
27087
27216
  }
@@ -27160,8 +27289,11 @@ function windowText(w, resetsAt, now, weekly) {
27160
27289
  function cents(n2) {
27161
27290
  return n2 === null ? "\u2014" : n2 < 100 ? `${n2}\xA2` : `$${(n2 / 100).toFixed(2)}`;
27162
27291
  }
27292
+ function ownsAccount(b) {
27293
+ return b?.account?.is_owner ?? b?.account?.kind === "personal";
27294
+ }
27163
27295
  function extraOffText(b, ui) {
27164
- if (b.account?.kind === "personal") return `off \u2014 turn it on at ${ui}/account/billing`;
27296
+ if (ownsAccount(b)) return `off \u2014 turn it on at ${ui}/account/billing`;
27165
27297
  return "off \u2014 a team admin can turn it on";
27166
27298
  }
27167
27299
  function renderBudget(b, now = /* @__PURE__ */ new Date(), ui = uiBase()) {
@@ -27177,12 +27309,14 @@ function renderBudget(b, now = /* @__PURE__ */ new Date(), ui = uiBase()) {
27177
27309
  return lines;
27178
27310
  }
27179
27311
  function budgetName(b) {
27180
- return b.account ? `${PLAN2[b.plan]} \xB7 ${b.account.name} (${b.account.kind})` : PLAN2[b.plan];
27312
+ if (!b.account) return PLAN2[b.plan];
27313
+ const name2 = terminalText(b.account.name);
27314
+ return ownsAccount(b) ? `${PLAN2[b.plan]} \xB7 ${name2} (your plan)` : `${PLAN2[b.plan]} \xB7 ${name2}`;
27181
27315
  }
27182
27316
  function payerText(b) {
27183
27317
  if (!b || !b.account) return "your Free plan";
27184
- if (b.account.kind === "personal") return `your personal ${PLAN2[b.plan]} plan`;
27185
- return `the ${PLAN2[b.plan]} plan of ${b.account.name}`;
27318
+ if (ownsAccount(b)) return `your ${PLAN2[b.plan]} plan`;
27319
+ return `the ${PLAN2[b.plan]} plan of ${terminalText(b.account.name)}`;
27186
27320
  }
27187
27321
  function renderLimits(d, now = /* @__PURE__ */ new Date()) {
27188
27322
  const ui = d.ui_url ? d.ui_url.replace(/\/+$/, "") : uiBase();
@@ -27190,10 +27324,10 @@ function renderLimits(d, now = /* @__PURE__ */ new Date()) {
27190
27324
  if (!d.enforced) lines.push(d.mode === "shadow" ? "Limits are counted, not yet enforced." : "Limits are not active.");
27191
27325
  const payer = d.budgets.find((b) => b.applies_here);
27192
27326
  if (d.team_here && !d.team_here.member) {
27193
- lines.push(d.team_here.auto_join ? `${d.team_here.name} pays for Verity in this repository. Your first review here makes you a member (${d.team_here.default_plan}).` : `${d.team_here.name} pays for Verity here for its members. An admin approves new members. Until an admin adds you, ${payerText(payer)} pays for your reviews here.`);
27327
+ lines.push(d.team_here.auto_join ? `${terminalText(d.team_here.name)} pays for Verity in this repository. Your first review here makes you a member (${terminalText(d.team_here.default_plan)}).` : `${terminalText(d.team_here.name)} pays for Verity here for its members. An admin approves new members. Until an admin adds you, ${payerText(payer)} pays for your reviews here.`);
27194
27328
  }
27195
- const hasPersonal = d.budgets.some((b) => b.account?.kind === "personal");
27196
- const shown = d.budgets.filter((b) => !(hasPersonal && b.budget === "free"));
27329
+ const hasOwn = d.budgets.some((b) => ownsAccount(b));
27330
+ const shown = d.budgets.filter((b) => !(hasOwn && b.budget === "free"));
27197
27331
  const full = payer ? shown.filter((b) => b === payer) : shown;
27198
27332
  for (const b of full) {
27199
27333
  lines.push("");
@@ -30244,7 +30378,13 @@ async function passAndExit(run4, reason, skip, kindOverride) {
30244
30378
  const AGENT_SILENT_SKIPS = /* @__PURE__ */ new Set([]);
30245
30379
  const agentNote = AGENT_SILENT_SKIPS.has(skip) ? null : note;
30246
30380
  const HUMAN_SILENT_SKIPS = /* @__PURE__ */ new Set(["command-only-turn"]);
30247
- const humanNote = HUMAN_SILENT_SKIPS.has(skip) ? null : `Verity: ${reason}`;
30381
+ const quiet = quietSkip({
30382
+ skip,
30383
+ kind: skipKind,
30384
+ nothingChanged: nothingChanged(run4),
30385
+ verbose: readVerbose()
30386
+ });
30387
+ const humanNote = HUMAN_SILENT_SKIPS.has(skip) || quiet ? null : `Verity: ${reason}`;
30248
30388
  printJsonCompact(
30249
30389
  buildHookOutput(
30250
30390
  verdict,
@@ -30257,6 +30397,22 @@ async function passAndExit(run4, reason, skip, kindOverride) {
30257
30397
  );
30258
30398
  process.exit(0);
30259
30399
  }
30400
+ var NOTHING_NEW = /* @__PURE__ */ new Set(["no-delta-since-last-review", "no-analyzable-files"]);
30401
+ var QUIET_ON_CLEAN_TREE = /* @__PURE__ */ new Set([
30402
+ "debounce",
30403
+ "verity-command",
30404
+ "bare-acknowledgment",
30405
+ "reflection-prompt",
30406
+ "skip-mode",
30407
+ "zero-increment"
30408
+ ]);
30409
+ function quietSkip(s) {
30410
+ if (s.verbose || s.kind !== "policy") return false;
30411
+ return NOTHING_NEW.has(s.skip) || QUIET_ON_CLEAN_TREE.has(s.skip) && s.nothingChanged;
30412
+ }
30413
+ function nothingChanged(run4) {
30414
+ return partitionVerityOwned(run4.changedUniverse).kept.length === 0;
30415
+ }
30260
30416
 
30261
30417
  // src/commands/analyze/phases/02-scope.ts
30262
30418
  async function scope(run4) {
@@ -31148,7 +31304,8 @@ async function evidence(run4) {
31148
31304
  const scoped = scopeToAuthored(allForReview, actionSummary);
31149
31305
  const canTrustNoneAuthored = scoped.signal === "none-authored" && authorshipIsObservable;
31150
31306
  if (canTrustNoneAuthored && !hasNonEditAuthorship(actionSummary, sessionAuthoredCode)) {
31151
- await passAndExit(run4, "No agent-authored code this turn \u2014 working-tree changes were not authored by this session", "zero-increment");
31307
+ const why = nothingChanged(run4) ? "No code changed this turn" : "No agent-authored code this turn \u2014 working-tree changes were not authored by this session";
31308
+ await passAndExit(run4, why, "zero-increment");
31152
31309
  }
31153
31310
  if (scoped.signal === "none-authored" && !authorshipIsObservable) {
31154
31311
  logEvent("none_authored_unverifiable", {
@@ -43499,13 +43656,20 @@ function sayPacks(ui, plan) {
43499
43656
  }
43500
43657
 
43501
43658
  // src/commands/init/team-standard.ts
43659
+ function refusalOutcome(code) {
43660
+ return code === "FORBIDDEN" ? "denied" : "sign-in-needed";
43661
+ }
43502
43662
  async function lookUpTeamStandard(opts) {
43503
43663
  if (!opts.adopt) return { status: "skipped", why: "no-adopt" };
43504
43664
  const token = await resolveToken(opts.globals.token);
43505
43665
  const url = await resolveServiceUrl(opts.globals.serviceUrl);
43506
43666
  if (!token.ok || !url.ok) return { status: "skipped", why: "not-signed-in" };
43507
43667
  const found = await fetchRemoteSetup({ token: token.data.token, serviceUrl: url.data, verbose: opts.globals.verbose });
43508
- if (found.status === "unavailable") return { status: "unavailable", reason: terminalText(found.reason, 300) };
43668
+ if (found.status === "unavailable") {
43669
+ const denial = authDenialRemedy(found.reason);
43670
+ if (denial) return { status: "denied", code: denial.code, reason: terminalText(found.reason, 300) };
43671
+ return { status: "unavailable", reason: terminalText(found.reason, 300) };
43672
+ }
43509
43673
  if (found.status === "none") return { status: "none" };
43510
43674
  if (opts.previous?.declinedStandardVersion === found.standard.version) {
43511
43675
  return { status: "declined-before", version: found.standard.version, found };
@@ -43679,12 +43843,12 @@ async function standardStep(ui, ctx) {
43679
43843
  return end({ source: "adopted", version: again.version, profile: profile2 });
43680
43844
  }
43681
43845
  removeLocalStandard();
43682
- if (again.status === "write-failed" || again.status === "unavailable") rail.line(terminalText(again.reason, 300));
43683
- return { ...leaveTeamStandard(again.status === "write-failed" ? "write-failed" : "unavailable", null), profile: profile2 };
43846
+ if (again.status === "write-failed" || again.status === "unavailable" || again.status === "denied") rail.line(terminalText(again.reason, 300));
43847
+ return { ...leaveTeamStandard(again.status === "write-failed" ? "write-failed" : again.status === "denied" ? refusalOutcome(again.code) : "unavailable", null), profile: profile2 };
43684
43848
  };
43685
43849
  const leaveTeamStandard = (why, version) => {
43686
- rail.line(why === "write-failed" ? `The team's Standard${version !== null ? ` v${version}` : ""} stays in effect for reviews. Nothing was sent to the service.` : "If the team has a Standard on the service, it stays in effect for reviews. Nothing was written here or to the service.");
43687
- rail.line(why === "unavailable" ? "Run verity init again once the Verity service answers." : "Fix the cause above, then run verity init again.");
43850
+ rail.line(why === "write-failed" ? `The team's Standard${version !== null ? ` v${version}` : ""} stays in effect for reviews. Nothing was sent to the service.` : why === "denied" ? "Verity has no access to this repository, so nothing was read or written here or on the service." : why === "sign-in-needed" ? "The Verity service did not accept your sign-in, so nothing was read or written here or on the service." : "If the team has a Standard on the service, it stays in effect for reviews. Nothing was written here or to the service.");
43851
+ rail.line(why === "unavailable" ? "Run verity init again once the Verity service answers." : why === "denied" ? "Give the Verity GitHub App access to this repository (the line above says how), then run: verity login, and verity init. Verity reads your repository access from GitHub only when you sign in." : why === "sign-in-needed" ? "Sign in again: run verity login, then verity init." : "Fix the cause above, then run verity init again.");
43688
43852
  return end({ source: "none", version: null, profile: notAsked, teamInEffect: why });
43689
43853
  };
43690
43854
  const useTeam = async (team2) => {
@@ -43706,7 +43870,7 @@ async function standardStep(ui, ctx) {
43706
43870
  rail.line(`Reviews use the team's Standard v${team2.version} on the service. Fix ${STANDARD_FILE}, or replace it with the team's: verity standard pull`);
43707
43871
  return end({ source: "kept", version: team2.version, profile: notAsked });
43708
43872
  }
43709
- rail.line(team2.status === "none" ? `There is no Standard on the service either, so reviews have no rules. Fix ${STANDARD_FILE}, or delete it, then run verity init again.` : team2.status === "unavailable" ? `The Verity service did not answer (${terminalText(team2.reason, 200)}), so no Standard is confirmed. Fix ${STANDARD_FILE}, then run verity init again.` : `No Standard on the service was looked up. Fix ${STANDARD_FILE}, or sign in, then run verity init again.`);
43873
+ rail.line(team2.status === "none" ? `There is no Standard on the service either, so reviews have no rules. Fix ${STANDARD_FILE}, or delete it, then run verity init again.` : team2.status === "unavailable" ? `The Verity service did not answer (${terminalText(team2.reason, 200)}), so no Standard is confirmed. Fix ${STANDARD_FILE}, then run verity init again.` : team2.status === "denied" && refusalOutcome(team2.code) === "denied" ? `Verity has no access to this repository (${terminalText(team2.reason, 200)}). Fix ${STANDARD_FILE}, give the Verity GitHub App access, then run verity login and verity init.` : team2.status === "denied" ? `The Verity service did not accept your sign-in (${terminalText(team2.reason, 200)}). Fix ${STANDARD_FILE}, then run verity login and verity init.` : `No Standard on the service was looked up. Fix ${STANDARD_FILE}, or sign in, then run verity init again.`);
43710
43874
  return end({ source: "none", version: null, profile: notAsked });
43711
43875
  };
43712
43876
  if (ctx.standard === "new" && (0, import_node_fs77.lstatSync)(projectPath(STANDARD_FILE), { throwIfNoEntry: false })) {
@@ -43761,6 +43925,12 @@ async function standardStep(ui, ctx) {
43761
43925
  rail.line(terminalText(team.reason, 300));
43762
43926
  return leaveTeamStandard("unavailable", null);
43763
43927
  }
43928
+ if (team.status === "denied") {
43929
+ const refused2 = refusalOutcome(team.code);
43930
+ rail.warn(refused2 === "denied" ? "Verity cannot access this repository yet" : "The Verity service did not accept your sign-in");
43931
+ rail.line(terminalText(team.reason, 300));
43932
+ return leaveTeamStandard(refused2, null);
43933
+ }
43764
43934
  if (team.status === "skipped") return derived(notAsked);
43765
43935
  if (!await rulesOffered(ui, ctx.globals)) return derived(notAsked);
43766
43936
  const profile = buildRepoProfile();
@@ -43811,10 +43981,10 @@ async function standardStep(ui, ctx) {
43811
43981
  sayAdopted(ui, again);
43812
43982
  return end({ source: "adopted", version: again.version, profile: { outcome: "ok", detail: detail2 } });
43813
43983
  }
43814
- if (again.status === "unavailable" || again.status === "write-failed") {
43815
- rail.warn(again.status === "unavailable" ? "Could not read the team's new Standard from the Verity service" : "Could not write the team's Standard here");
43984
+ if (again.status === "unavailable" || again.status === "write-failed" || again.status === "denied") {
43985
+ rail.warn(again.status === "write-failed" ? "Could not write the team's Standard here" : "Could not read the team's new Standard from the Verity service");
43816
43986
  rail.line(terminalText(again.reason, 300));
43817
- return { ...leaveTeamStandard(again.status, null), profile: { outcome: "ok", detail: detail2 } };
43987
+ return { ...leaveTeamStandard(again.status === "denied" ? refusalOutcome(again.code) : again.status, null), profile: { outcome: "ok", detail: detail2 } };
43818
43988
  }
43819
43989
  }
43820
43990
  return derived({ outcome: "ok", detail: detail2 });
@@ -43830,8 +44000,8 @@ async function finishRun(ui, ctx) {
43830
44000
  sayChanged(ui, ctx.before);
43831
44001
  if (ctx.teamInEffect) {
43832
44002
  rail.end(
43833
- ctx.teamInEffect === "unavailable" ? `${s.yellow}Not finished:${s.reset} the Verity service did not answer, so this machine has no Standard yet.` : `${s.yellow}Not finished:${s.reset} the team's Standard could not be written on this machine. Reviews still use it on the service.`,
43834
- ctx.teamInEffect === "unavailable" ? "Run verity init again once the Verity service answers." : "Fix the error above, then run verity init again."
44003
+ ctx.teamInEffect === "unavailable" ? `${s.yellow}Not finished:${s.reset} the Verity service did not answer, so this machine has no Standard yet.` : ctx.teamInEffect === "denied" ? `${s.yellow}Not finished:${s.reset} Verity has no access to this repository yet, so it cannot review it.` : ctx.teamInEffect === "sign-in-needed" ? `${s.yellow}Not finished:${s.reset} the Verity service did not accept your sign-in, so this machine has no Standard yet.` : `${s.yellow}Not finished:${s.reset} the team's Standard could not be written on this machine. Reviews still use it on the service.`,
44004
+ ctx.teamInEffect === "unavailable" ? "Run verity init again once the Verity service answers." : ctx.teamInEffect === "denied" ? "Give the Verity GitHub App access to this repository, then run: verity login, and verity init. Verity reads your repository access from GitHub only when you sign in." : ctx.teamInEffect === "sign-in-needed" ? "Sign in again: run verity login, then verity init." : "Fix the error above, then run verity init again."
43835
44005
  );
43836
44006
  return;
43837
44007
  }
@@ -44227,13 +44397,14 @@ async function classifyExistingLogin(serviceUrl, remote, opts) {
44227
44397
  if (!existing.ok) return { kind: "none" };
44228
44398
  const who = await whoami(existing.data.token, serviceUrl, opts.verbose);
44229
44399
  if (who.ok && who.data.logged_in) {
44400
+ const nudge = reverifyNudge(who.data);
44230
44401
  return {
44231
44402
  kind: "signed-in",
44232
44403
  identity: terminalText(who.data.email ?? `user #${who.data.user_id}`, 120),
44233
44404
  // Being logged in does not mean THIS repo is covered (VRT-83): grants come
44234
44405
  // from the login sweep. grant_status is non-null only when a grant exists.
44235
44406
  covered: remote ? who.data.grant_status != null : null,
44236
- nudge: reverifyNudge(who.data)
44407
+ nudge: nudge && terminalText(nudge, 200)
44237
44408
  };
44238
44409
  }
44239
44410
  if (!who.ok) {
@@ -44263,6 +44434,7 @@ function coverageLines(ui, repo, covered) {
44263
44434
  } else {
44264
44435
  ui.rail.line(`${repo} is not covered by your Verity access, so nothing syncs.`, "warn");
44265
44436
  ui.rail.line("Give the Verity GitHub App access to it, then run: verity login");
44437
+ ui.rail.line("Verity reads your repository access from GitHub only when you sign in. The new sign-in is how Verity sees the repository.", "muted");
44266
44438
  }
44267
44439
  }
44268
44440
  async function signInStep(ui, resolution, opts) {
@@ -44283,6 +44455,7 @@ async function signInStep(ui, resolution, opts) {
44283
44455
  const heal = await maybeHealServiceUrl(resolution, opts.verbose);
44284
44456
  const serviceUrl = heal.serviceUrl;
44285
44457
  const remote = gitRemote();
44458
+ let grantAccess = false;
44286
44459
  if (heal.healed) {
44287
44460
  rail.warn("The saved Verity service no longer answers");
44288
44461
  rail.line("Sign in again to repair ~/.verity/credentials.");
@@ -44290,9 +44463,14 @@ async function signInStep(ui, resolution, opts) {
44290
44463
  const state = await classifyExistingLogin(serviceUrl, remote, opts);
44291
44464
  if (state.kind === "signed-in") {
44292
44465
  rail.done(`Signed in as ${s.bold}${state.identity}${s.reset}`);
44293
- coverageLines(ui, opts.repo, state.covered);
44294
44466
  if (state.nudge) rail.line(state.nudge, "warn");
44295
- return end("already");
44467
+ if (state.covered !== false || !ui.asking) {
44468
+ coverageLines(ui, opts.repo, state.covered);
44469
+ return end("already");
44470
+ }
44471
+ rail.line(`Verity can't see ${opts.repo} yet: the Verity GitHub App has no access to it.`, "warn");
44472
+ grantAccess = true;
44473
+ rail.settle();
44296
44474
  }
44297
44475
  if (state.kind === "cached") {
44298
44476
  rail.done(state.identity ? `Signed in as ${s.bold}${state.identity}${s.reset} (cached)` : "Using your existing Verity token");
@@ -44313,16 +44491,28 @@ async function signInStep(ui, resolution, opts) {
44313
44491
  sayWhatASkipCosts(ui);
44314
44492
  return end("unattended");
44315
44493
  }
44316
- rail.active("Sign in with GitHub");
44317
- rail.line("One sign-in covers every repository on this machine.");
44318
- rail.line("Signing in does not give Verity access to the contents of your repositories.");
44494
+ if (grantAccess) {
44495
+ rail.active(`Give Verity access to ${opts.repo}`);
44496
+ rail.line("Verity reads your repository access from GitHub only when you sign in.");
44497
+ rail.line("So sign in again: Verity opens the page where you add this repository to its app, and waits.");
44498
+ rail.line("When the repository is added, Verity can see it, and this run continues.");
44499
+ } else {
44500
+ rail.active("Sign in with GitHub");
44501
+ rail.line("One sign-in covers every repository on this machine.");
44502
+ rail.line("Signing in does not give Verity access to the contents of your repositories.");
44503
+ }
44319
44504
  rail.gap();
44320
44505
  rail.keys([["Enter", "open the browser"], ["s", "skip"]]);
44321
44506
  const choice = await screen.key(["enter", "s"], "s");
44322
44507
  if (choice === "s") {
44323
44508
  rail.collapse();
44324
- rail.warn("Sign-in skipped");
44325
- sayWhatASkipCosts(ui);
44509
+ rail.warn(grantAccess ? "Access skipped" : "Sign-in skipped");
44510
+ if (grantAccess) {
44511
+ rail.line(`Verity can't review ${opts.repo} until its GitHub App has access to it.`);
44512
+ rail.line("Later: add the repository to the Verity app, then run verity login. Verity reads your repository access from GitHub only when you sign in.");
44513
+ } else {
44514
+ sayWhatASkipCosts(ui);
44515
+ }
44326
44516
  return end("declined");
44327
44517
  }
44328
44518
  const failed = (why) => {
@@ -44338,20 +44528,23 @@ async function signInStep(ui, resolution, opts) {
44338
44528
  const dc = code.data;
44339
44529
  const copied = copyToClipboard(dc.user_code);
44340
44530
  const opened = openInBrowser(dc.verification_uri);
44341
- 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");
44531
+ const where2 = terminalText(dc.verification_uri, 200);
44532
+ const userCode = terminalText(dc.user_code, 40);
44533
+ rail.line(opened ? `Opened ${where2} in your browser. If no window appeared, open it and enter the code.` : `Open ${where2} and enter the code.`, opened ? "muted" : "plain");
44342
44534
  const prefix = `${s.gray}\u2502${s.reset} `;
44343
44535
  const polled = await screen.breathe(
44344
44536
  prefix,
44345
- () => `Waiting for you in the browser \xB7 code ${s.bold}${dc.user_code}${s.reset}${copied ? ` ${s.gray}(copied)${s.reset}` : ""}`,
44537
+ () => `Waiting for you in the browser \xB7 code ${s.bold}${userCode}${s.reset}${copied ? ` ${s.gray}(copied)${s.reset}` : ""}`,
44346
44538
  pollDeviceToken(dc, { codeExpiresAt: Date.now() + dc.expires_in * 1e3, quiet: true })
44347
44539
  );
44348
44540
  if (!polled.ok) return failed(polled.error);
44349
44541
  let gaveUp = null;
44350
44542
  const appInstallUi = {
44351
44543
  wait: async (ctx, visible) => {
44352
- rail.line(`The Verity GitHub App cannot see ${ctx.owner}/${ctx.repo} yet. Install it${ctx.opened ? " (opened in your browser)" : ""}:`, "warn");
44353
- rail.line(ctx.installUrl, "plain");
44354
- return screen.breathe(prefix, () => `Waiting for the install on ${ctx.owner}`, visible);
44544
+ const owner = terminalText(ctx.owner, 100);
44545
+ rail.line(`The Verity GitHub App cannot see ${owner}/${terminalText(ctx.repo, 100)} yet. Install it${ctx.opened ? " (opened in your browser)" : ""}:`, "warn");
44546
+ rail.line(terminalText(ctx.installUrl, 300), "plain");
44547
+ return screen.breathe(prefix, () => `Waiting for the install on ${owner}`, visible);
44355
44548
  },
44356
44549
  gaveUp: (message) => {
44357
44550
  gaveUp = message;
@@ -44372,7 +44565,7 @@ async function signInStep(ui, resolution, opts) {
44372
44565
  if (gaveUp) rail.line(terminalText(gaveUp, 300), "warn");
44373
44566
  for (const line2 of report.slice(1)) {
44374
44567
  if (line2.level === "verbose") continue;
44375
- rail.line(line2.text.trim().replace(/^✓\s*/, ""), line2.level === "warn" ? "warn" : "muted");
44568
+ rail.line(terminalText(line2.text.trim().replace(/^✓\s*/, ""), 300), line2.level === "warn" ? "warn" : "muted");
44376
44569
  }
44377
44570
  return end("new");
44378
44571
  }
@@ -44412,7 +44605,7 @@ async function recordAnswers(ui, momentIds, telemetry, completed) {
44412
44605
  moments: momentIds,
44413
44606
  ...telemetry ? { telemetry } : {},
44414
44607
  // A failed run also clears an earlier stamp: "done" describes this checkout now.
44415
- init: completed ? { completed_at: (/* @__PURE__ */ new Date()).toISOString(), cli_version: true ? "0.35.2" : "dev" } : void 0
44608
+ init: completed ? { completed_at: (/* @__PURE__ */ new Date()).toISOString(), cli_version: true ? "0.35.3" : "dev" } : void 0
44416
44609
  });
44417
44610
  } catch (err2) {
44418
44611
  ui.rail.line(`Could not record the setup answers: ${terminalText(err2.message, 200)}`, "warn");
@@ -46335,10 +46528,10 @@ function registerLeanCommand(program2) {
46335
46528
  }
46336
46529
 
46337
46530
  // src/cli.ts
46338
- program.name("verity").description("CLI for Verity quality gate service").version("0.35.2").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) => {
46531
+ program.name("verity").description("CLI for Verity quality gate service").version("0.35.3").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) => {
46339
46532
  const isTelemetryHelper = actionCommand.name() === "headers" && actionCommand.parent?.name() === "telemetry";
46340
46533
  if (!isTelemetryHelper) {
46341
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.35.2");
46534
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.35.3");
46342
46535
  }
46343
46536
  setUserNamedServiceUrl(program.opts().serviceUrl);
46344
46537
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/verity-cli",
3
- "version": "0.35.2",
3
+ "version": "0.35.3",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://verity.md",