@codacy/verity-cli 0.25.0-experimental.e3da48a → 0.25.0-experimental.ed0c319

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 CHANGED
@@ -10325,9 +10325,7 @@ var {
10325
10325
  } = import_index.default;
10326
10326
 
10327
10327
  // src/commands/auth.ts
10328
- var import_promises3 = require("node:fs/promises");
10329
10328
  var import_node_child_process4 = require("node:child_process");
10330
- var import_node_path3 = require("node:path");
10331
10329
 
10332
10330
  // src/lib/auth.ts
10333
10331
  var import_promises = require("node:fs/promises");
@@ -10748,6 +10746,10 @@ function analyzeRequest(options) {
10748
10746
  });
10749
10747
  }
10750
10748
 
10749
+ // src/lib/register.ts
10750
+ var import_promises3 = require("node:fs/promises");
10751
+ var import_node_path3 = require("node:path");
10752
+
10751
10753
  // src/lib/provider-auth.ts
10752
10754
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
10753
10755
  var form = (fields) => new URLSearchParams(fields).toString();
@@ -11003,8 +11005,8 @@ function filterReviewable(files) {
11003
11005
  const ext = (0, import_node_path2.extname)(f).slice(1);
11004
11006
  if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
11005
11007
  if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
11006
- const basename2 = f.split("/").pop() ?? "";
11007
- if (REVIEWABLE_FILENAMES.has(basename2)) return true;
11008
+ const basename3 = f.split("/").pop() ?? "";
11009
+ if (REVIEWABLE_FILENAMES.has(basename3)) return true;
11008
11010
  if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
11009
11011
  return false;
11010
11012
  });
@@ -11066,6 +11068,66 @@ function listTrackedFiles() {
11066
11068
  return Array.from(set);
11067
11069
  }
11068
11070
 
11071
+ // src/lib/register.ts
11072
+ async function registerProject(opts) {
11073
+ const parsed = parseRemote(opts.remote);
11074
+ if (!parsed) {
11075
+ return { ok: false, error: `Could not parse git remote: ${opts.remote}` };
11076
+ }
11077
+ if (parsed.provider !== "github") {
11078
+ return { ok: false, error: `Provider '${parsed.provider}' is not supported yet \u2014 GitHub only for now.` };
11079
+ }
11080
+ const providerAuth = await githubDeviceFlow();
11081
+ if (!providerAuth.ok) {
11082
+ return { ok: false, error: providerAuth.error };
11083
+ }
11084
+ const providerToken = providerAuth.data;
11085
+ const result = await apiRequest({
11086
+ method: "POST",
11087
+ path: "/auth/register",
11088
+ serviceUrl: opts.serviceUrl,
11089
+ body: { project_name: opts.projectName, git_remote_url: opts.remote },
11090
+ extraHeaders: { "X-Provider-Token": providerToken },
11091
+ verbose: opts.verbose
11092
+ });
11093
+ if (!result.ok) {
11094
+ return { ok: false, error: result.error };
11095
+ }
11096
+ const { project_id, token, service_url, user } = result.data;
11097
+ try {
11098
+ await (0, import_promises3.mkdir)(VERITY_DIR, { recursive: true });
11099
+ await (0, import_promises3.writeFile)(
11100
+ CREDENTIALS_FILE,
11101
+ `token: ${token}
11102
+ service_url: ${service_url}
11103
+ provider_token: ${providerToken}
11104
+ `,
11105
+ { mode: 384 }
11106
+ );
11107
+ await (0, import_promises3.chmod)(CREDENTIALS_FILE, 384).catch(() => {
11108
+ });
11109
+ } catch (err) {
11110
+ return {
11111
+ ok: false,
11112
+ error: `Registered with the Verity service, but could not save credentials to ${CREDENTIALS_FILE}: ${err.message}. Check filesystem permissions and re-run "verity auth register".`
11113
+ };
11114
+ }
11115
+ try {
11116
+ await (0, import_promises3.mkdir)((0, import_node_path3.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
11117
+ await (0, import_promises3.appendFile)(
11118
+ GLOBAL_CREDENTIALS_FILE,
11119
+ `${opts.remote} token: ${token}
11120
+ ${opts.remote} provider_token: ${providerToken}
11121
+ `,
11122
+ { mode: 384 }
11123
+ );
11124
+ await (0, import_promises3.chmod)(GLOBAL_CREDENTIALS_FILE, 384).catch(() => {
11125
+ });
11126
+ } catch {
11127
+ }
11128
+ return { ok: true, data: { projectId: project_id, serviceUrl: service_url, email: user?.email } };
11129
+ }
11130
+
11069
11131
  // src/commands/auth.ts
11070
11132
  function registerAuthCommands(program2) {
11071
11133
  const auth = program2.command("auth").description("Manage project authentication");
@@ -11081,60 +11143,20 @@ function registerAuthCommands(program2) {
11081
11143
  process.exit(1);
11082
11144
  }
11083
11145
  }
11084
- const parsed = parseRemote(remote);
11085
- if (!parsed) {
11086
- printError(`Could not parse git remote: ${remote}`);
11087
- process.exit(1);
11088
- }
11089
- if (parsed.provider !== "github") {
11090
- printError(`Provider '${parsed.provider}' is not supported yet \u2014 GitHub only for now.`);
11091
- process.exit(1);
11092
- }
11093
- const providerAuth = await githubDeviceFlow();
11094
- if (!providerAuth.ok) {
11095
- printError(providerAuth.error);
11096
- process.exit(1);
11097
- }
11098
- const providerToken = providerAuth.data;
11099
- const result = await apiRequest({
11100
- method: "POST",
11101
- path: "/auth/register",
11146
+ const result = await registerProject({
11147
+ projectName: opts.project,
11148
+ remote,
11102
11149
  serviceUrl,
11103
- body: { project_name: opts.project, git_remote_url: remote },
11104
- extraHeaders: { "X-Provider-Token": providerToken },
11105
11150
  verbose: globals.verbose
11106
11151
  });
11107
11152
  if (!result.ok) {
11108
11153
  printError(result.error);
11109
11154
  process.exit(1);
11110
11155
  }
11111
- const { project_id, token, service_url, user } = result.data;
11112
- await (0, import_promises3.mkdir)(VERITY_DIR, { recursive: true });
11113
- await (0, import_promises3.writeFile)(
11114
- CREDENTIALS_FILE,
11115
- `token: ${token}
11116
- service_url: ${service_url}
11117
- provider_token: ${providerToken}
11118
- `,
11119
- { mode: 384 }
11120
- );
11121
- await (0, import_promises3.chmod)(CREDENTIALS_FILE, 384).catch(() => {
11122
- });
11123
- try {
11124
- await (0, import_promises3.mkdir)((0, import_node_path3.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
11125
- await (0, import_promises3.appendFile)(
11126
- GLOBAL_CREDENTIALS_FILE,
11127
- `${remote} token: ${token}
11128
- ${remote} provider_token: ${providerToken}
11129
- `
11130
- );
11131
- await (0, import_promises3.chmod)(GLOBAL_CREDENTIALS_FILE, 384).catch(() => {
11132
- });
11133
- } catch {
11134
- }
11135
- printInfo(`Project registered: ${project_id}`);
11136
- if (user?.email) printInfo(`Authenticated as: ${user.email}`);
11137
- printJson({ project_id, service_url });
11156
+ const { projectId, serviceUrl: resolvedUrl, email } = result.data;
11157
+ printInfo(`Project registered: ${projectId}`);
11158
+ if (email) printInfo(`Authenticated as: ${email}`);
11159
+ printJson({ project_id: projectId, service_url: resolvedUrl });
11138
11160
  });
11139
11161
  auth.command("verify").description("Verify the current token is valid").action(async () => {
11140
11162
  const globals = program2.opts();
@@ -14733,6 +14755,27 @@ function passAndExit(reason) {
14733
14755
  printJsonCompact({ gate_decision: "PASS", systemMessage: `Verity: ${reason}` });
14734
14756
  process.exit(0);
14735
14757
  }
14758
+ var EMPTY_STATIC = {
14759
+ tool: "@codacy/analysis-cli",
14760
+ findings: [],
14761
+ summary: { total_findings: 0, by_severity: {}, tools_run: [] }
14762
+ };
14763
+ function runLocalStatic(analyzable, securityFiles, baseline, skipStatic) {
14764
+ if (skipStatic || !isCodacyAvailable()) return EMPTY_STATIC;
14765
+ let scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
14766
+ if (baseline) scannable = scannable.filter((f) => changedSinceBaseline(f, baseline));
14767
+ if (scannable.length === 0) return EMPTY_STATIC;
14768
+ return runCodacyAnalysis(scannable);
14769
+ }
14770
+ function localOnlyAndExit(staticResults) {
14771
+ printJsonCompact({
14772
+ gate_decision: "PASS",
14773
+ systemMessage: "Verity: not authenticated \u2014 ran a local static-only check (no deep review, no upload). Run `verity init` to authenticate and enable the full quality gate.",
14774
+ unauthenticated: true,
14775
+ static_results: staticResults
14776
+ });
14777
+ process.exit(0);
14778
+ }
14736
14779
  function registerAnalyzeCommand(program2) {
14737
14780
  program2.command("analyze").description("Run Verity analysis on changed files (stop hook)").option("--debounce <seconds>", "Skip if last analysis was within N seconds", "30").option("--max-iterations <n>", "Force PASS after N FAIL cycles", "2").option("--max-files <n>", "Max files to send for review", "20").option("--max-file-size <bytes>", "Skip files larger than N bytes", "51200").option("--max-total-size <bytes>", "Stop collecting files at N total bytes", "194560").option("--skip-static", "Skip codacy-analysis").option("--mode <mode>", "Force analysis mode (standard|plan|debug|skip)").option("--json", "Output raw JSON response").action(async (opts) => {
14738
14781
  const globals = program2.opts();
@@ -14783,12 +14826,9 @@ async function runAnalyze(opts, globals) {
14783
14826
  passAndExit("Reflection-prompt turn \u2014 skipping analysis");
14784
14827
  }
14785
14828
  const tokenResult = await resolveToken(globals.token);
14786
- if (!tokenResult.ok) {
14787
- passAndExit("Not configured yet \u2014 run /verity-setup in Claude Code to enable quality gates.");
14788
- }
14789
14829
  const urlResult = await resolveServiceUrl(globals.serviceUrl);
14790
- if (!urlResult.ok) {
14791
- passAndExit("Not configured yet \u2014 run /verity-setup in Claude Code to enable quality gates.");
14830
+ if (!tokenResult.ok || !urlResult.ok) {
14831
+ localOnlyAndExit(runLocalStatic(analyzable, securityFiles, baseline, !!opts.skipStatic));
14792
14832
  }
14793
14833
  const sessionIdForMemory = sessionId || process.env.CLAUDE_SESSION_ID || "";
14794
14834
  let contextFilePaths = [];
@@ -15362,13 +15402,14 @@ async function runReview(opts, globals) {
15362
15402
  }
15363
15403
  const codeDelta = collectCodeDelta(allFiles);
15364
15404
  const tokenResult = await resolveToken(globals.token);
15365
- if (!tokenResult.ok) {
15366
- printError("Not configured yet \u2014 run /verity-setup in Claude Code to enable quality gates.");
15367
- process.exit(0);
15368
- }
15369
15405
  const urlResult = await resolveServiceUrl(globals.serviceUrl);
15370
- if (!urlResult.ok) {
15371
- printError("Not configured yet \u2014 run /verity-setup in Claude Code to enable quality gates.");
15406
+ if (!tokenResult.ok || !urlResult.ok) {
15407
+ printJsonCompact({
15408
+ gate_decision: "PASS",
15409
+ systemMessage: "Verity: not authenticated \u2014 showing local static results only (no deep review, no upload). Run `verity init` to authenticate and unlock the full review.",
15410
+ unauthenticated: true,
15411
+ static_results: staticResults
15412
+ });
15372
15413
  process.exit(0);
15373
15414
  }
15374
15415
  let specs;
@@ -15757,6 +15798,7 @@ var import_node_fs24 = require("node:fs");
15757
15798
  var import_promises12 = require("node:fs/promises");
15758
15799
  var import_node_path17 = require("node:path");
15759
15800
  var import_node_child_process9 = require("node:child_process");
15801
+ var readline = __toESM(require("node:readline/promises"));
15760
15802
 
15761
15803
  // src/commands/migrate.ts
15762
15804
  var import_node_fs23 = require("node:fs");
@@ -16036,6 +16078,66 @@ function registerMigrateCommand(program2) {
16036
16078
  }
16037
16079
 
16038
16080
  // src/commands/init.ts
16081
+ async function promptYes(question) {
16082
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
16083
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
16084
+ try {
16085
+ const answer = (await rl.question(question)).trim().toLowerCase();
16086
+ return answer === "" || answer === "y" || answer === "yes";
16087
+ } finally {
16088
+ rl.close();
16089
+ }
16090
+ }
16091
+ async function runOptionalAuth() {
16092
+ const existing = await resolveToken();
16093
+ if (existing.ok) {
16094
+ printInfo("Already authenticated \u2014 results will upload to the Verity service. \u2713");
16095
+ return;
16096
+ }
16097
+ let remote = "";
16098
+ try {
16099
+ remote = (0, import_node_child_process9.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
16100
+ } catch {
16101
+ }
16102
+ const localOnlyNote = () => {
16103
+ printInfo("Verity runs in local-only mode: the gate still runs and shows static findings, but nothing uploads.");
16104
+ printInfo(' Authenticate anytime: run "verity init" again, or "verity auth register".');
16105
+ };
16106
+ if (process.stdin.isTTY && process.stdout.isTTY) {
16107
+ console.log("");
16108
+ console.log(" Signing in is optional. What it does:");
16109
+ console.log(" - Confirms you have write access to this repository. The GitHub token");
16110
+ console.log(" is used once to verify that, then discarded \u2014 Verity never stores it.");
16111
+ console.log(" - It does NOT give Verity access to your code. Code checked by the gate");
16112
+ console.log(" is analyzed in memory and discarded \u2014 we never store your code.");
16113
+ console.log(" - It is required to store and access run history for this repo");
16114
+ console.log(" (past results, trends, and shareable reports).");
16115
+ console.log(" - Skip and Verity still works fully locally: the gate runs and shows");
16116
+ console.log(" findings, but nothing is uploaded.");
16117
+ console.log("");
16118
+ }
16119
+ const wantsAuth = await promptYes("Authenticate with GitHub now to upload results to Verity? [Y/skip] ");
16120
+ if (!wantsAuth) {
16121
+ printInfo("Skipped authentication.");
16122
+ localOnlyNote();
16123
+ return;
16124
+ }
16125
+ if (!remote) {
16126
+ printWarn("No git remote found \u2014 cannot authenticate yet.");
16127
+ localOnlyNote();
16128
+ return;
16129
+ }
16130
+ const projectName = parseRemote(remote)?.repo ?? (0, import_node_path17.basename)(process.cwd());
16131
+ printInfo("Authenticating with GitHub\u2026");
16132
+ const result = await registerProject({ projectName, remote, serviceUrl: DEFAULT_SERVICE_URL });
16133
+ if (result.ok) {
16134
+ printInfo(`Project registered: ${result.data.projectId} \u2713`);
16135
+ if (result.data.email) printInfo(` Authenticated as: ${result.data.email}`);
16136
+ } else {
16137
+ printWarn(`Authentication did not complete: ${result.error}`);
16138
+ localOnlyNote();
16139
+ }
16140
+ }
16039
16141
  function resolveDataDir() {
16040
16142
  const candidates = [
16041
16143
  (0, import_node_path17.join)(__dirname, "..", "data"),
@@ -16179,6 +16281,12 @@ function registerInitCommand(program2) {
16179
16281
  const globalVerityDir = (0, import_node_path17.join)(process.env.HOME ?? "", ".verity");
16180
16282
  await (0, import_promises12.mkdir)(globalVerityDir, { recursive: true });
16181
16283
  console.log("");
16284
+ try {
16285
+ await runOptionalAuth();
16286
+ } catch (err) {
16287
+ printWarn(`Authentication step skipped: ${err.message}`);
16288
+ }
16289
+ console.log("");
16182
16290
  printInfo("Verity initialized!");
16183
16291
  console.log("");
16184
16292
  console.log(" Installed:");
@@ -16194,6 +16302,7 @@ function registerInitCommand(program2) {
16194
16302
  console.log(" .verity/memory/ \u2014 knowledge base (8 domains, commit to git)");
16195
16303
  console.log("");
16196
16304
  console.log(" Next step: open this project in Claude Code and run /verity-setup");
16305
+ console.log(' (Not authenticated? Verity runs in local-only mode until you run "verity auth register".)');
16197
16306
  console.log("");
16198
16307
  });
16199
16308
  }
@@ -16925,7 +17034,7 @@ function registerTelemetryCommands(program2) {
16925
17034
  }
16926
17035
 
16927
17036
  // src/cli.ts
16928
- program.name("verity").description("CLI for Verity quality gate service").version("0.25.0-experimental.e3da48a").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
17037
+ program.name("verity").description("CLI for Verity quality gate service").version("0.25.0-experimental.ed0c319").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
16929
17038
  registerAuthCommands(program);
16930
17039
  registerHooksCommands(program);
16931
17040
  registerIntentCommands(program);
@@ -15,7 +15,13 @@ You are running an on-demand Verity analysis. This is like a "second opinion"
15
15
 
16
16
  1. Verify `.verity/standard.yaml` exists. If not: "Run `/verity-setup` first."
17
17
  2. Verify `verity` CLI is available: `which verity`. If not: "Re-run the Verity installer: `curl -fsSL https://raw.githubusercontent.com/codacy/verity/main/install.sh | bash`"
18
- 3. Run `verity auth verify` to check token is valid. If it fails: "Verity not configured. Run `/verity-setup` first."
18
+ 3. Run `verity auth verify` to check the token is valid.
19
+ - **Valid**: proceed with the full deep review.
20
+ - **Fails / not authenticated**: don't stop. Tell the user Verity is in
21
+ **local-only mode**, so the review will run static analysis and show findings
22
+ but won't perform the deep (LLM) review or upload anything. To unlock the deep
23
+ review they can authenticate with `verity init` (or `verity auth register`).
24
+ Continue — `verity review` degrades to a local static-only report on its own.
19
25
 
20
26
  ---
21
27
 
@@ -310,32 +310,60 @@ Expected: single-digit findings per file, not hundreds. If you see 50+ issues fr
310
310
 
311
311
  ---
312
312
 
313
- ## Step 6: Register with Verity service
314
-
315
- Use the `verity` CLI to register. It handles provider auth, credential storage, and the service URL automatically.
313
+ ## Step 6: Verify authentication
314
+
315
+ Login now happens in `verity init` (an optional, skippable step), **not** here.
316
+ This step only checks whether the user authenticated during init, and branches
317
+ the rest of setup accordingly.
318
+
319
+ **If the user asks what signing in does or why it matters, tell them:**
320
+ - It confirms they have **write access to this repository** — the GitHub token is
321
+ used **once** to verify that, then discarded. Verity never stores it.
322
+ - It does **not** give Verity access to their code. Code checked by the gate is
323
+ analyzed **in memory and discarded** — Verity never stores their code.
324
+ - It is **required to store and access run history** for the repo (past results,
325
+ trends, and shareable reports).
326
+ - Skipping keeps Verity fully **local-only**: the gate still runs and shows
327
+ findings, but nothing is uploaded.
316
328
 
317
329
  ```bash
318
- verity auth register --project "PROJECT_NAME" --remote "GIT_REMOTE_URL"
330
+ verity auth verify
319
331
  ```
320
332
 
321
- Registration is **provider-gated** (GitHub today): the CLI runs a GitHub OAuth
322
- **device flow** and prints something like *"open https://github.com/login/device
323
- and enter code WXYZ-1234"*. The user approves in the browser; the CLI then proves
324
- the user has **write access** to the repo before the service issues a token. Tell
325
- the user to expect this prompt and to complete it in their browser.
326
-
327
- This command:
328
- - **Write access confirmed**: Registers, stores the verity token + service URL + provider token in `.verity/credentials` (perms 600), prints `project_id` and the authenticated email. Continue.
329
- - **No write access** (`403 NO_WRITE_ACCESS`): The user lacks push rights on the repo — they can't register it. Verity still runs locally as an anonymous gate, but no history/org/repo data is stored. Stop.
330
- - **Non-GitHub remote**: Only GitHub is supported for now; show the error and stop.
331
- - **Other errors**: Show the error and stop.
332
-
333
- After this step, `.verity/credentials` will contain `token`, `service_url`, and `provider_token` — all subsequent `verity` commands will work.
333
+ - **Token valid** (prints the project name): The user authenticated during
334
+ `verity init`. Continue to Step 7 the Standard, config, and knowledge base
335
+ will upload.
336
+ - **Not authenticated / no token**: The user skipped login in `verity init` (or
337
+ lacks write access). Verity runs in **local-only mode** the gate still runs on
338
+ every stop and surfaces static findings, but nothing uploads and no
339
+ history/org/repo data is stored. **Skip Steps 7 and 7b** (they require a token)
340
+ and continue to Step 8. Tell the user they can authenticate anytime to unlock
341
+ deep review, history, and shareable reports by running:
342
+
343
+ ```bash
344
+ verity init # re-runs init; offers the auth prompt again
345
+ # or, directly:
346
+ verity auth register --project "PROJECT_NAME" --remote "GIT_REMOTE_URL"
347
+ ```
348
+
349
+ Registration is **provider-gated** (GitHub today): the CLI runs a GitHub OAuth
350
+ **device flow** ("open https://github.com/login/device and enter code
351
+ WXYZ-1234"), proving the user has **write access** to the repo before the
352
+ service issues a token.
353
+
354
+ When authenticated, `.verity/credentials` contains `token`, `service_url`, and
355
+ `provider_token` — all subsequent `verity` upload commands work.
334
356
 
335
357
  ---
336
358
 
337
359
  ## Step 7: Upload Standard and config
338
360
 
361
+ > **Skip this entire step if the user is not authenticated** (Step 6 reported
362
+ > local-only mode). These commands require a token and will fail without one. The
363
+ > `.verity/standard.yaml` and `.codacy/codacy.config.json` you generated locally
364
+ > still drive the gate; they'll upload the next time the user authenticates and
365
+ > re-runs setup.
366
+
339
367
  ### Upload the Standard
340
368
 
341
369
  The `verity` CLI handles YAML→JSON conversion automatically:
@@ -366,6 +394,10 @@ This derives a small set of descriptive memory nodes from what you already analy
366
394
 
367
395
  ## Step 7b: Enable telemetry (only if the user opted in at Step 3b)
368
396
 
397
+ > **Skip this step if the user is not authenticated** (local-only mode) — telemetry
398
+ > export requires the token. Note that `/usage` stays empty until they authenticate
399
+ > and run `verity telemetry install`.
400
+
369
401
  If — and only if — the user said **Yes** in Step 3b, enable the Claude Code telemetry export
370
402
  now (the token from Step 6 must already exist):
371
403
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/verity-cli",
3
- "version": "0.25.0-experimental.e3da48a",
3
+ "version": "0.25.0-experimental.ed0c319",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "homepage": "https://verity.md",
6
6
  "repository": {