@codacy/verity-cli 0.32.1-experimental.f2cf829 → 0.32.2-experimental.8843567

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 +39 -2
  2. package/bin/verity.js +116 -39
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -10,7 +10,7 @@ All notable changes to Verity are documented here. This project follows
10
10
  into its own heading when it is ready to be announced. -->
11
11
 
12
12
  **Verity ships as a Claude Code plugin.** `/plugin marketplace add codacy/verity`
13
- then `/plugin install verity@codacy` gives a working gate with nothing else to
13
+ then `/plugin install verity@verity` gives a working gate with nothing else to
14
14
  install — the plugin carries the CLI as a pinned dependency, and Claude Code
15
15
  installs and updates it for you.
16
16
 
@@ -47,7 +47,10 @@ installs are untouched.
47
47
  - **A repository can no longer switch the gate off by shipping a file.**
48
48
  Ownership was claimed by `.verity/.plugin-active`, a file inside the project,
49
49
  and one naming any directory that happened to exist disabled every hook there.
50
- An install Claude Code cannot vouch for is ignored.
50
+ An install Claude Code cannot vouch for is ignored — and "vouch" now means
51
+ *for this checkout*: the cache path a real install lives at is predictable, so
52
+ a marker naming a plugin installed for **somebody else's project** used to be
53
+ believed here.
51
54
  - **A plugin installed from a local marketplace directory is recognised.**
52
55
  Claude Code runs a marketplace served from a directory out of that directory
53
56
  while recording the cache copy it made, so the two paths disagreed and a live,
@@ -61,6 +64,40 @@ installs are untouched.
61
64
  which is the ordinary case for someone whose first Verity install *is* the
62
65
  plugin.
63
66
 
67
+ ## [0.32.2] — 2026-09-10
68
+
69
+ **A patch for one bug, which stopped people before Verity was installed at all.**
70
+
71
+ ### 🔌 The plugin marketplace is named `verity`
72
+
73
+ `/plugin install verity@codacy` is now **`/plugin install verity@verity`**. The
74
+ `marketplace add` line is unchanged — `codacy/verity` is the repository, not the
75
+ name.
76
+
77
+ Marketplace names are a flat, global namespace on your machine: no per-org
78
+ scoping, so two repositories that declare the same `name` compete for one slot.
79
+ Verity published as `codacy`, which `codacy/codacy-skills` already had — so
80
+ anyone who used that marketplace got
81
+
82
+ ```
83
+ Cannot add marketplace "codacy": its network source differs from the one
84
+ declared for it in settings …
85
+ ```
86
+
87
+ and could never install Verity. The error names neither the file nor the fix.
88
+
89
+ **If you installed before this release**, the old plugin still runs but will
90
+ never update again:
91
+
92
+ ```
93
+ /plugin uninstall verity@codacy
94
+ /plugin marketplace add codacy/verity
95
+ /plugin install verity@verity
96
+ ```
97
+
98
+ `verity doctor` now detects both cases — a taken marketplace name, and a plugin
99
+ installed from the old one — and prints the file to edit and the commands to
100
+ run, so the next collision is a diagnosis rather than a dead end.
64
101
 
65
102
  ## [0.32.1] — 2026-09-09
66
103
 
package/bin/verity.js CHANGED
@@ -12917,6 +12917,52 @@ function marketplaceLocations() {
12917
12917
  }
12918
12918
  return out;
12919
12919
  }
12920
+ var VERITY_MARKETPLACE = "verity";
12921
+ var VERITY_MARKETPLACE_REPO = "codacy/verity";
12922
+ function marketplaceConflict() {
12923
+ const wanted = VERITY_MARKETPLACE;
12924
+ for (const file of ["settings.json", "settings.local.json"]) {
12925
+ const path = (0, import_node_path7.join)(claudeConfigDir(), file);
12926
+ const declared = readJsonFile(path)?.extraKnownMarketplaces ?? null;
12927
+ const entry2 = declared && typeof declared === "object" ? declared[wanted] : void 0;
12928
+ if (entry2 && typeof entry2 === "object") {
12929
+ const src = entry2.source;
12930
+ if (!pointsAtVerity(src)) {
12931
+ return { name: wanted, declaredAs: describeSource(src), settingsFile: path };
12932
+ }
12933
+ }
12934
+ }
12935
+ const known = readJsonFile((0, import_node_path7.join)(claudeConfigDir(), "plugins", "known_marketplaces.json"));
12936
+ const entry = known?.[wanted];
12937
+ if (entry && typeof entry === "object" && !pointsAtVerity(entry.source)) {
12938
+ return { name: wanted, declaredAs: describeSource(entry.source) };
12939
+ }
12940
+ return null;
12941
+ }
12942
+ function pointsAtVerity(src) {
12943
+ if (!src || typeof src !== "object") return false;
12944
+ const repo = typeof src.repo === "string" ? src.repo : "";
12945
+ if (repo.toLowerCase() === VERITY_MARKETPLACE_REPO) return true;
12946
+ const url = typeof src.url === "string" ? src.url : "";
12947
+ return /github\.com[/:]codacy\/verity(\.git)?\/?$/i.test(url);
12948
+ }
12949
+ function describeSource(src) {
12950
+ if (!src || typeof src !== "object") return "an unrecognised source";
12951
+ const kind = typeof src.source === "string" ? src.source : "unknown";
12952
+ const target = typeof src.repo === "string" && src.repo || typeof src.url === "string" && src.url || typeof src.path === "string" && src.path || "";
12953
+ return target ? `${kind} \u2192 ${target}` : kind;
12954
+ }
12955
+ function legacyMarketplaceInstall() {
12956
+ const plugins = readJsonFile((0, import_node_path7.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
12957
+ if (!plugins || typeof plugins !== "object") return null;
12958
+ for (const key of Object.keys(plugins)) {
12959
+ const at = key.lastIndexOf("@");
12960
+ if (at <= 0) continue;
12961
+ if (key.slice(0, at) !== "verity") continue;
12962
+ if (key.slice(at + 1) !== VERITY_MARKETPLACE) return key;
12963
+ }
12964
+ return null;
12965
+ }
12920
12966
  function realpathOr(p) {
12921
12967
  try {
12922
12968
  return import_node_fs6.realpathSync.native(p);
@@ -12927,18 +12973,27 @@ function realpathOr(p) {
12927
12973
  function isWithin(want, dir) {
12928
12974
  return want === dir || want.startsWith(dir + import_node_path7.sep);
12929
12975
  }
12976
+ function entryAppliesHere(entry, here) {
12977
+ const e = entry;
12978
+ const scope2 = typeof e?.scope === "string" ? e.scope : "user";
12979
+ if (scope2 === "user") return true;
12980
+ const forProject = typeof e?.projectPath === "string" ? realpathOr(e.projectPath) : "";
12981
+ return forProject === here;
12982
+ }
12930
12983
  function registrySays(pluginRoot) {
12931
12984
  const plugins = readJsonFile((0, import_node_path7.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
12932
12985
  if (!plugins || typeof plugins !== "object") return "unverified";
12933
12986
  const want = (0, import_node_path7.resolve)(pluginRoot);
12987
+ const here = realpathOr(repoRoot());
12934
12988
  const markets = marketplaceLocations();
12935
12989
  for (const [key, value] of Object.entries(plugins)) {
12936
12990
  const at = key.lastIndexOf("@");
12937
12991
  const source = at > 0 ? markets.get(key.slice(at + 1)) : void 0;
12938
- const claims = (Array.isArray(value) ? value : []).some((entry) => {
12992
+ const applicable = (Array.isArray(value) ? value : []).filter((entry) => entryAppliesHere(entry, here));
12993
+ const claims = applicable.some((entry) => {
12939
12994
  const installPath = entry?.installPath;
12940
12995
  return typeof installPath === "string" && (0, import_node_path7.resolve)(installPath) === want;
12941
- }) || source !== void 0 && isWithin(want, source);
12996
+ }) || source !== void 0 && applicable.length > 0 && isWithin(want, source);
12942
12997
  if (claims) {
12943
12998
  if (enabledPluginSetting(key) === false) return "gone";
12944
12999
  return (0, import_node_fs6.existsSync)(pluginRoot) ? "live" : "gone";
@@ -12998,11 +13053,7 @@ function registeredVerityPlugin() {
12998
13053
  const e = entry;
12999
13054
  const installPath = typeof e.installPath === "string" ? e.installPath : "";
13000
13055
  if (!installPath || !(0, import_node_fs6.existsSync)(installPath)) continue;
13001
- const scope2 = typeof e.scope === "string" ? e.scope : "user";
13002
- if (scope2 !== "user") {
13003
- const forProject = typeof e.projectPath === "string" ? realpathOr(e.projectPath) : "";
13004
- if (forProject !== here) continue;
13005
- }
13056
+ if (!entryAppliesHere(entry, here)) continue;
13006
13057
  return { pluginRoot: installPath, version: typeof e.version === "string" ? e.version : null };
13007
13058
  }
13008
13059
  }
@@ -20867,7 +20918,7 @@ function channelSilence(input) {
20867
20918
  // src/lib/cli-version.ts
20868
20919
  function cliVersion() {
20869
20920
  try {
20870
- return true ? "0.32.1-experimental.f2cf829" : "dev";
20921
+ return true ? "0.32.2-experimental.8843567" : "dev";
20871
20922
  } catch {
20872
20923
  return "dev";
20873
20924
  }
@@ -24615,36 +24666,50 @@ async function runGuard(opts, globals) {
24615
24666
  process.exit(2);
24616
24667
  }
24617
24668
  resetIter(moment);
24669
+ const notice = verdictNotice({
24670
+ decision,
24671
+ moment,
24672
+ verb,
24673
+ covLine,
24674
+ covDetail,
24675
+ link,
24676
+ viewUrl,
24677
+ narrative: response.assessment?.narrative ?? ""
24678
+ });
24679
+ emitAllowNotice(notice.user, notice.agent);
24680
+ }
24681
+ function verdictNotice(ctx) {
24682
+ const { moment, verb, covLine, covDetail, link, viewUrl } = ctx;
24683
+ const decision = ctx.decision ?? "(unrecognised)";
24684
+ const report = viewUrl ? `
24685
+ Report: ${viewUrl}` : "";
24618
24686
  if (decision === "FAIL") {
24619
- const narrative = response.assessment?.narrative ?? "";
24620
- emitAllowNotice(
24621
- `\u26A0 Verity ${moment}: the ${verb} may not match its stated purpose \u2014 proceeding (${covLine})${link}`,
24622
- `Verity ${moment}: intent-alignment WARNING (not blocked).${narrative ? " " + narrative : ""}
24623
- ${covDetail}${viewUrl ? `
24624
- Report: ${viewUrl}` : ""}`
24625
- );
24626
- } else if (decision === "WARN") {
24627
- emitAllowNotice(
24628
- `\u26A0 Verity ${moment}: WARN \u2014 proceeding (${covLine})${link}`,
24629
- `Verity ${moment} review: WARN (proceeding).
24630
- ${covDetail}${viewUrl ? `
24631
- Report: ${viewUrl}` : ""}`
24632
- );
24633
- } else if (decision === "PASS") {
24634
- emitAllowNotice(
24635
- `\u2713 Verity ${moment}: PASS (${covLine})${link}`,
24636
- `Verity ${moment} review: PASS.
24637
- ${covDetail}${viewUrl ? `
24638
- Report: ${viewUrl}` : ""}`
24639
- );
24640
- } else {
24641
- emitAllowNotice(
24642
- `\u26A0 Verity ${moment}: no verdict came back \u2014 ${verb === "commit" ? "committed" : "pushed"} WITHOUT a usable review (${covLine})${link}`,
24643
- `Verity ${moment}: the service answered with no recognisable gate decision (${decision}); the ${verb} was allowed, but nothing reviewed it. Treat this as unreviewed, not as a pass.
24644
- ${covDetail}${viewUrl ? `
24645
- Report: ${viewUrl}` : ""}`
24646
- );
24687
+ const narrative = ctx.narrative ?? "";
24688
+ return {
24689
+ user: `\u26A0 Verity ${moment}: the ${verb} may not match its stated purpose \u2014 proceeding (${covLine})${link}`,
24690
+ agent: `Verity ${moment}: intent-alignment WARNING (not blocked).${narrative ? " " + narrative : ""}
24691
+ ${covDetail}${report}`
24692
+ };
24693
+ }
24694
+ if (decision === "WARN") {
24695
+ return {
24696
+ user: `\u26A0 Verity ${moment}: WARN \u2014 proceeding (${covLine})${link}`,
24697
+ agent: `Verity ${moment} review: WARN (proceeding).
24698
+ ${covDetail}${report}`
24699
+ };
24647
24700
  }
24701
+ if (decision === "PASS") {
24702
+ return {
24703
+ user: `\u2713 Verity ${moment}: PASS (${covLine})${link}`,
24704
+ agent: `Verity ${moment} review: PASS.
24705
+ ${covDetail}${report}`
24706
+ };
24707
+ }
24708
+ return {
24709
+ user: `\u26A0 Verity ${moment}: no verdict came back \u2014 ${verb === "commit" ? "committed" : "pushed"} WITHOUT a usable review (${covLine})${link}`,
24710
+ agent: `Verity ${moment}: the service answered with no recognisable gate decision (${decision}); the ${verb} was allowed, but nothing reviewed it. Treat this as unreviewed, not as a pass.
24711
+ ${covDetail}${report}`
24712
+ };
24648
24713
  }
24649
24714
  function writeBlockMessage(moment, response, covDetail) {
24650
24715
  const label2 = moment === "pre-commit" ? "pre-commit" : "pre-push";
@@ -25265,6 +25330,18 @@ async function buildReport() {
25265
25330
  if (state?.telemetry === "deferred") {
25266
25331
  next.push('Telemetry was requested but needs a token \u2014 run "verity login", then "verity telemetry install".');
25267
25332
  }
25333
+ const conflict = marketplaceConflict();
25334
+ if (conflict) {
25335
+ next.push(
25336
+ `The marketplace name "${conflict.name}" is already taken on this machine by ${conflict.declaredAs}, so "/plugin marketplace add codacy/verity" will be refused. ` + (conflict.settingsFile ? `Remove the "${conflict.name}" entry from extraKnownMarketplaces in ${conflict.settingsFile} ` : `Run "/plugin marketplace remove ${conflict.name}" `) + "and restart Claude Code, then add it again."
25337
+ );
25338
+ }
25339
+ const legacy = legacyMarketplaceInstall();
25340
+ if (legacy) {
25341
+ next.push(
25342
+ `The Verity plugin is installed as "${legacy}", from a marketplace name Verity no longer publishes under \u2014 it still runs, but it will never see another update. Run "/plugin uninstall ${legacy}", then "/plugin marketplace add codacy/verity" and "/plugin install verity@verity".`
25343
+ );
25344
+ }
25268
25345
  return {
25269
25346
  prerequisites: prereqs.checks,
25270
25347
  blocked: prereqs.blocked,
@@ -26667,7 +26744,7 @@ function registerInitCommand(program2) {
26667
26744
  ...telemetryChoice ? { telemetry: telemetryChoice } : {},
26668
26745
  init: {
26669
26746
  completed_at: (/* @__PURE__ */ new Date()).toISOString(),
26670
- cli_version: true ? "0.32.1-experimental.f2cf829" : "dev"
26747
+ cli_version: true ? "0.32.2-experimental.8843567" : "dev"
26671
26748
  }
26672
26749
  });
26673
26750
  } catch (err) {
@@ -27347,8 +27424,8 @@ function registerTelemetryCommands(program2) {
27347
27424
  }
27348
27425
 
27349
27426
  // src/cli.ts
27350
- program.name("verity").description("CLI for Verity quality gate service").version("0.32.1-experimental.f2cf829").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) => {
27351
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.32.1-experimental.f2cf829");
27427
+ program.name("verity").description("CLI for Verity quality gate service").version("0.32.2-experimental.8843567").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) => {
27428
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.32.2-experimental.8843567");
27352
27429
  setUserNamedServiceUrl(program.opts().serviceUrl);
27353
27430
  try {
27354
27431
  await foldLegacyLocalCredential();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/verity-cli",
3
- "version": "0.32.1-experimental.f2cf829",
3
+ "version": "0.32.2-experimental.8843567",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://verity.md",