@codacy/verity-cli 0.31.3 → 0.32.0-experimental.68ae2ee

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
@@ -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;
@@ -10509,7 +10509,7 @@ var SECURITY_PATTERNS = [
10509
10509
  /Dockerfile/
10510
10510
  ];
10511
10511
  var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
10512
- var DEFAULT_SERVICE_URL = "".length > 0 ? "" : PROD_SERVICE_URL;
10512
+ var DEFAULT_SERVICE_URL = "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1".length > 0 ? "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1" : PROD_SERVICE_URL;
10513
10513
  var GITHUB_CLIENT_ID = "Iv23li88HxAi3ZrbYzWh";
10514
10514
  var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
10515
10515
  var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
@@ -10955,8 +10955,8 @@ function filterReviewable(files) {
10955
10955
  const ext = (0, import_node_path3.extname)(f).slice(1);
10956
10956
  if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
10957
10957
  if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
10958
- const basename3 = f.split("/").pop() ?? "";
10959
- if (REVIEWABLE_FILENAMES.has(basename3)) return true;
10958
+ const basename4 = f.split("/").pop() ?? "";
10959
+ if (REVIEWABLE_FILENAMES.has(basename4)) return true;
10960
10960
  if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
10961
10961
  return false;
10962
10962
  });
@@ -16758,12 +16758,862 @@ async function readHookStdin() {
16758
16758
  }
16759
16759
 
16760
16760
  // src/commands/standard.ts
16761
+ var import_promises12 = require("node:fs/promises");
16762
+ var import_node_fs28 = require("node:fs");
16763
+ var import_yaml3 = __toESM(require_dist());
16764
+
16765
+ // src/lib/synthesize.ts
16766
+ var import_node_child_process8 = require("node:child_process");
16767
+ var import_node_fs24 = require("node:fs");
16761
16768
  var import_promises9 = require("node:fs/promises");
16762
- var import_node_fs23 = require("node:fs");
16769
+ var import_node_path20 = require("node:path");
16763
16770
  var import_yaml = __toESM(require_dist());
16764
16771
 
16765
- // src/lib/verityignore.ts
16772
+ // src/lib/data-dir.ts
16766
16773
  var import_node_fs22 = require("node:fs");
16774
+ var import_node_path18 = require("node:path");
16775
+ function resolveDataDir() {
16776
+ const candidates = [
16777
+ (0, import_node_path18.join)(__dirname, "..", "data"),
16778
+ // installed: node_modules/@codacy/verity-cli/data
16779
+ (0, import_node_path18.join)(__dirname, "..", "..", "data"),
16780
+ // edge case: nested resolution
16781
+ // THE COMMITTED SOURCE, for a source checkout that has not been built.
16782
+ // cli/data/skills/ is a BUILD ARTIFACT (scripts/build.js copies client/skills
16783
+ // into it) and is gitignored, because skills have one committed source — so
16784
+ // in a fresh clone the packaged candidates above do not exist at all, and
16785
+ // without this the synthesizer throws "Could not find Verity skill data"
16786
+ // for every test and every `verity` run from source. Resolved from this
16787
+ // module's own location, never the cwd: see the warning below.
16788
+ (0, import_node_path18.join)(__dirname, "..", "..", "client"),
16789
+ // bundled: cli/bin/ → ../../client
16790
+ (0, import_node_path18.join)(__dirname, "..", "..", "..", "client"),
16791
+ // tsx: cli/src/lib/ → ../../../client
16792
+ ...process.env.VERITY_DEV_DATA_DIR ? [process.env.VERITY_DEV_DATA_DIR] : []
16793
+ ];
16794
+ for (const candidate of candidates) {
16795
+ if ((0, import_node_fs22.existsSync)((0, import_node_path18.join)(candidate, "skills"))) {
16796
+ return candidate;
16797
+ }
16798
+ }
16799
+ throw new Error(
16800
+ "Could not find Verity skill data. Reinstall: npm install -g @codacy/verity-cli"
16801
+ );
16802
+ }
16803
+ function setupDataPath(file) {
16804
+ return (0, import_node_path18.join)(resolveDataDir(), "skills", "verity-setup", file);
16805
+ }
16806
+
16807
+ // src/lib/detect.ts
16808
+ var import_node_child_process7 = require("node:child_process");
16809
+ var import_node_fs23 = require("node:fs");
16810
+ var import_node_path19 = require("node:path");
16811
+ var TOOLED_LANGUAGES = /* @__PURE__ */ new Set([
16812
+ "typescript",
16813
+ "javascript",
16814
+ "python",
16815
+ "go",
16816
+ "java",
16817
+ "kotlin",
16818
+ "ruby",
16819
+ "rust",
16820
+ "c",
16821
+ "cpp",
16822
+ "shell",
16823
+ "dockerfile"
16824
+ ]);
16825
+ var MIN_FILES_FOR_LANGUAGE = 3;
16826
+ var IGNORED_SEGMENTS = [
16827
+ "node_modules",
16828
+ "dist",
16829
+ "build",
16830
+ "vendor",
16831
+ "coverage",
16832
+ ".git",
16833
+ "__pycache__",
16834
+ ".next",
16835
+ ".venv",
16836
+ "target",
16837
+ ".verity",
16838
+ ".codacy"
16839
+ ];
16840
+ function isIgnored(path) {
16841
+ const parts = path.split("/");
16842
+ return parts.some((p) => IGNORED_SEGMENTS.includes(p));
16843
+ }
16844
+ function listProjectFiles(root) {
16845
+ try {
16846
+ const out = (0, import_node_child_process7.execSync)("git ls-files -z", {
16847
+ cwd: root,
16848
+ encoding: "utf-8",
16849
+ maxBuffer: 32 * 1024 * 1024,
16850
+ stdio: ["pipe", "pipe", "pipe"]
16851
+ });
16852
+ const tracked = out.split("\0").filter(Boolean).filter((p) => !isIgnored(p));
16853
+ if (tracked.length > 0) return tracked;
16854
+ } catch {
16855
+ }
16856
+ return walk(root);
16857
+ }
16858
+ var WALK_MAX_FILES = 2e4;
16859
+ var WALK_MAX_DEPTH = 8;
16860
+ function walk(root) {
16861
+ const found = [];
16862
+ const visit = (dir, depth) => {
16863
+ if (depth > WALK_MAX_DEPTH || found.length >= WALK_MAX_FILES) return;
16864
+ let entries;
16865
+ try {
16866
+ entries = (0, import_node_fs23.readdirSync)(dir, { withFileTypes: true });
16867
+ } catch {
16868
+ return;
16869
+ }
16870
+ for (const entry of entries) {
16871
+ if (found.length >= WALK_MAX_FILES) return;
16872
+ if (IGNORED_SEGMENTS.includes(entry.name)) continue;
16873
+ const full = (0, import_node_path19.join)(dir, entry.name);
16874
+ if (entry.isDirectory()) visit(full, depth + 1);
16875
+ else if (entry.isFile()) found.push((0, import_node_path19.relative)(root, full));
16876
+ }
16877
+ };
16878
+ visit(root, 0);
16879
+ return found;
16880
+ }
16881
+ function languageOf(path) {
16882
+ const name = (0, import_node_path19.basename)(path);
16883
+ if (/^Dockerfile(\..+)?$/i.test(name)) return "dockerfile";
16884
+ if (!(0, import_node_path19.extname)(name)) return null;
16885
+ const lang = detectLanguage(path);
16886
+ return lang || null;
16887
+ }
16888
+ var FRAMEWORK_BY_DEPENDENCY = [
16889
+ [/^react$/, "react"],
16890
+ [/^react-native$/, "react-native"],
16891
+ [/^next$/, "next.js"],
16892
+ [/^vue$/, "vue"],
16893
+ [/^@angular\/core$/, "angular"],
16894
+ [/^svelte$/, "svelte"],
16895
+ [/^express$/, "express"],
16896
+ [/^fastify$/, "fastify"],
16897
+ [/^@nestjs\/core$/, "nestjs"],
16898
+ [/^koa$/, "koa"],
16899
+ [/^hono$/, "hono"],
16900
+ [/^@supabase\/supabase-js$/, "supabase"],
16901
+ [/^prisma$|^@prisma\/client$/, "prisma"],
16902
+ [/^drizzle-orm$/, "drizzle"],
16903
+ [/^django$/i, "django"],
16904
+ [/^flask$/i, "flask"],
16905
+ [/^fastapi$/i, "fastapi"],
16906
+ [/^sqlalchemy$/i, "sqlalchemy"],
16907
+ [/^pydantic$/i, "pydantic"],
16908
+ [/^celery$/i, "celery"],
16909
+ [/^gin-gonic\/gin$/, "gin"],
16910
+ [/^labstack\/echo/, "echo"],
16911
+ [/^gofiber\/fiber/, "fiber"],
16912
+ [/^spring-boot|springframework/, "spring"],
16913
+ [/^rails$/, "rails"],
16914
+ [/^sinatra$/, "sinatra"],
16915
+ [/^actix-web$/, "actix"],
16916
+ [/^axum$/, "axum"],
16917
+ [/^rocket$/, "rocket"]
16918
+ ];
16919
+ var TEST_FRAMEWORK_BY_DEPENDENCY = [
16920
+ [/^vitest$/, "vitest"],
16921
+ [/^jest$/, "jest"],
16922
+ [/^mocha$/, "mocha"],
16923
+ [/^@playwright\/test$/, "playwright"],
16924
+ [/^cypress$/, "cypress"],
16925
+ [/^pytest$/i, "pytest"],
16926
+ [/^unittest2$/i, "unittest"],
16927
+ [/^rspec$/, "rspec"],
16928
+ [/^junit$|^junit-jupiter/, "junit"]
16929
+ ];
16930
+ var TOOL_CONFIG_MARKERS = [
16931
+ ["ESLint9", ["eslint.config.js", "eslint.config.mjs", "eslint.config.cjs", ".eslintrc", ".eslintrc.js", ".eslintrc.json", ".eslintrc.yml"]],
16932
+ ["Semgrep", [".semgrep.yml", ".semgrep.yaml", "semgrep.yml", "semgrep.yaml"]],
16933
+ ["Trivy", ["trivy.yaml", "trivy.yml"]],
16934
+ ["Ruff", ["ruff.toml", ".ruff.toml"]],
16935
+ ["PMD7", ["pmd.xml", "ruleset.xml"]],
16936
+ ["detekt", ["detekt.yml", "detekt.yaml"]],
16937
+ ["Hadolint", [".hadolint.yaml", ".hadolint.yml"]]
16938
+ ];
16939
+ function readJson(path) {
16940
+ try {
16941
+ return JSON.parse((0, import_node_fs23.readFileSync)(path, "utf-8"));
16942
+ } catch {
16943
+ return null;
16944
+ }
16945
+ }
16946
+ function matchAll(names2, table) {
16947
+ const hits = /* @__PURE__ */ new Set();
16948
+ for (const name of names2) {
16949
+ for (const [pattern, label2] of table) {
16950
+ if (pattern.test(name)) hits.add(label2);
16951
+ }
16952
+ }
16953
+ return [...hits];
16954
+ }
16955
+ var NESTED_MANIFEST_LIMIT = 25;
16956
+ function declaredDependencies(root, files) {
16957
+ const names2 = [];
16958
+ const readPackageJson = (path) => {
16959
+ const pkg = readJson(path);
16960
+ if (!pkg) return;
16961
+ for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
16962
+ const deps = pkg[field];
16963
+ if (deps && typeof deps === "object") names2.push(...Object.keys(deps));
16964
+ }
16965
+ };
16966
+ readPackageJson((0, import_node_path19.join)(root, "package.json"));
16967
+ const nested = files.filter((f) => f.includes("/") && (0, import_node_path19.basename)(f) === "package.json").slice(0, NESTED_MANIFEST_LIMIT);
16968
+ for (const rel of nested) readPackageJson((0, import_node_path19.join)(root, rel));
16969
+ const pythonManifests = [
16970
+ ...["pyproject.toml", "requirements.txt", "Pipfile", "setup.py"].map((f) => (0, import_node_path19.join)(root, f)),
16971
+ ...files.filter((f) => f.includes("/") && /(^|\/)(pyproject\.toml|requirements\.txt)$/.test(f)).slice(0, NESTED_MANIFEST_LIMIT).map((f) => (0, import_node_path19.join)(root, f))
16972
+ ];
16973
+ for (const path of pythonManifests) {
16974
+ if (!(0, import_node_fs23.existsSync)(path)) continue;
16975
+ try {
16976
+ const text = (0, import_node_fs23.readFileSync)(path, "utf-8");
16977
+ for (const m of text.matchAll(/^\s*["']?([A-Za-z][A-Za-z0-9._-]+)/gm)) names2.push(m[1]);
16978
+ for (const line of text.split("\n")) {
16979
+ if (!/dependencies\s*=/.test(line)) continue;
16980
+ for (const m of line.matchAll(/["']([A-Za-z][A-Za-z0-9._-]+)/g)) names2.push(m[1]);
16981
+ }
16982
+ } catch {
16983
+ }
16984
+ }
16985
+ const goMods = [
16986
+ (0, import_node_path19.join)(root, "go.mod"),
16987
+ ...files.filter((f) => f.includes("/") && (0, import_node_path19.basename)(f) === "go.mod").slice(0, NESTED_MANIFEST_LIMIT).map((f) => (0, import_node_path19.join)(root, f))
16988
+ ];
16989
+ for (const path of goMods) {
16990
+ if (!(0, import_node_fs23.existsSync)(path)) continue;
16991
+ try {
16992
+ const text = (0, import_node_fs23.readFileSync)(path, "utf-8");
16993
+ for (const m of text.matchAll(/^\s+([\w.-]+\/[\w./-]+)\s+v/gm)) {
16994
+ names2.push(m[1].replace(/^github\.com\//, ""));
16995
+ }
16996
+ } catch {
16997
+ }
16998
+ }
16999
+ for (const file of ["pom.xml", "build.gradle", "build.gradle.kts", "Gemfile", "Cargo.toml"]) {
17000
+ const path = (0, import_node_path19.join)(root, file);
17001
+ if (!(0, import_node_fs23.existsSync)(path)) continue;
17002
+ try {
17003
+ const text = (0, import_node_fs23.readFileSync)(path, "utf-8");
17004
+ for (const m of text.matchAll(/["'<]([A-Za-z][A-Za-z0-9._-]{2,})["'>]/g)) names2.push(m[1]);
17005
+ } catch {
17006
+ }
17007
+ }
17008
+ return names2;
17009
+ }
17010
+ function detectBuildSystem(root, files) {
17011
+ const has = (f) => (0, import_node_fs23.existsSync)((0, import_node_path19.join)(root, f)) || files.some((p) => (0, import_node_path19.basename)(p) === f);
17012
+ if (has("pnpm-lock.yaml")) return "pnpm";
17013
+ if (has("yarn.lock")) return "yarn";
17014
+ if (has("bun.lock") || has("bun.lockb")) return "bun";
17015
+ if (has("package-lock.json") || has("package.json")) return "npm";
17016
+ if (has("poetry.lock")) return "poetry";
17017
+ if (has("uv.lock")) return "uv";
17018
+ if (has("pyproject.toml") || has("requirements.txt")) return "pip";
17019
+ if (has("go.mod")) return "go";
17020
+ if (has("pom.xml")) return "maven";
17021
+ if (has("build.gradle") || has("build.gradle.kts")) return "gradle";
17022
+ if (has("Cargo.toml")) return "cargo";
17023
+ if (has("Gemfile")) return "bundler";
17024
+ return null;
17025
+ }
17026
+ function detectArchitecture(root, files) {
17027
+ const workspaceMarkers = ["lerna.json", "pnpm-workspace.yaml", "nx.json", "turbo.json", "rush.json"];
17028
+ if (workspaceMarkers.some((m) => (0, import_node_fs23.existsSync)((0, import_node_path19.join)(root, m)))) return "monorepo";
17029
+ const pkg = readJson((0, import_node_path19.join)(root, "package.json"));
17030
+ if (pkg && "workspaces" in pkg) return "monorepo";
17031
+ const manifests = files.filter((f) => /(^|\/)(package\.json|go\.mod|pyproject\.toml|Cargo\.toml|pom\.xml)$/.test(f));
17032
+ const nested = manifests.filter((f) => f.includes("/"));
17033
+ if (nested.length >= 2) {
17034
+ const underServices = nested.filter((f) => /^(services|apps)\//.test(f));
17035
+ return underServices.length >= 2 ? "microservices" : "monorepo";
17036
+ }
17037
+ return "monolith";
17038
+ }
17039
+ var SAMPLE_LIMIT = 300;
17040
+ function measureAvgFileLength(root, files, languages) {
17041
+ const primary = new Set(languages.slice(0, 2));
17042
+ const candidates = files.filter((f) => {
17043
+ const lang = languageOf(f);
17044
+ return lang !== null && primary.has(lang);
17045
+ });
17046
+ if (candidates.length === 0) return null;
17047
+ const stride = Math.max(1, Math.floor(candidates.length / SAMPLE_LIMIT));
17048
+ let total = 0;
17049
+ let counted = 0;
17050
+ for (let i = 0; i < candidates.length; i += stride) {
17051
+ const path = (0, import_node_path19.join)(root, candidates[i]);
17052
+ try {
17053
+ if ((0, import_node_fs23.statSync)(path).size > 2 * 1024 * 1024) continue;
17054
+ total += (0, import_node_fs23.readFileSync)(path, "utf-8").split("\n").length;
17055
+ counted++;
17056
+ } catch {
17057
+ }
17058
+ }
17059
+ return counted > 0 ? Math.round(total / counted) : null;
17060
+ }
17061
+ function tunedFileLengthThreshold(avgFileLength, floor = 300) {
17062
+ if (avgFileLength === null || avgFileLength <= 0) return floor;
17063
+ const scaled = Math.round(avgFileLength * 0.8 / 50) * 50;
17064
+ return Math.max(floor, scaled);
17065
+ }
17066
+ function detectProject(root = repoRoot()) {
17067
+ const files = listProjectFiles(root);
17068
+ const languageCounts = {};
17069
+ for (const file of files) {
17070
+ const lang = languageOf(file);
17071
+ if (!lang) continue;
17072
+ languageCounts[lang] = (languageCounts[lang] ?? 0) + 1;
17073
+ }
17074
+ const languages = Object.entries(languageCounts).filter(([lang, count]) => TOOLED_LANGUAGES.has(lang) && count >= MIN_FILES_FOR_LANGUAGE).sort((a, b) => b[1] - a[1]).map(([lang]) => lang);
17075
+ const dependencies = declaredDependencies(root, files);
17076
+ const existingToolConfigs = [];
17077
+ for (const [tool, markers] of TOOL_CONFIG_MARKERS) {
17078
+ for (const marker of markers) {
17079
+ if ((0, import_node_fs23.existsSync)((0, import_node_path19.join)(root, marker))) {
17080
+ existingToolConfigs.push({ tool, path: `./${marker}` });
17081
+ break;
17082
+ }
17083
+ }
17084
+ }
17085
+ return {
17086
+ projectName: (0, import_node_path19.basename)(root),
17087
+ languages,
17088
+ languageCounts,
17089
+ frameworks: matchAll(dependencies, FRAMEWORK_BY_DEPENDENCY),
17090
+ architecture: detectArchitecture(root, files),
17091
+ buildSystem: detectBuildSystem(root, files),
17092
+ testFramework: matchAll(dependencies, TEST_FRAMEWORK_BY_DEPENDENCY)[0] ?? null,
17093
+ existingToolConfigs,
17094
+ avgFileLength: measureAvgFileLength(root, files, languages),
17095
+ filesScanned: files.length
17096
+ };
17097
+ }
17098
+ function describeDetected(d) {
17099
+ const langs = d.languages.length > 0 ? d.languages.map((l) => `${l} (${d.languageCounts[l]} files)`).join(", ") : "no code languages detected";
17100
+ const parts = [langs];
17101
+ if (d.frameworks.length > 0) parts.push(d.frameworks.join(", "));
17102
+ parts.push(d.architecture);
17103
+ if (d.buildSystem) parts.push(d.buildSystem);
17104
+ if (d.testFramework) parts.push(d.testFramework);
17105
+ return parts.join(" \xB7 ");
17106
+ }
17107
+
17108
+ // src/lib/verity-md.ts
17109
+ function names(content, key) {
17110
+ const raw = content[key];
17111
+ if (!Array.isArray(raw)) return [];
17112
+ return raw.map((entry) => {
17113
+ if (typeof entry === "string") return entry;
17114
+ if (entry && typeof entry === "object") {
17115
+ const rec = entry;
17116
+ const label2 = rec.name ?? rec.id ?? rec.dimension ?? rec.pattern;
17117
+ return typeof label2 === "string" ? label2 : null;
17118
+ }
17119
+ return null;
17120
+ }).filter((x) => !!x);
17121
+ }
17122
+ var DEFAULT_DIMENSIONS = ["comprehensibility", "modularity", "type_safety", "test_adequacy"];
17123
+ var DEFAULT_SECURITY = [
17124
+ "No hardcoded secrets (CWE-798)",
17125
+ "Input sanitization (CWE-20)",
17126
+ "Parameterized queries (CWE-89)",
17127
+ "Dependency verification (CWE-1395)",
17128
+ "No unsafe deserialization (CWE-502)",
17129
+ "Access control checks (CWE-639)",
17130
+ "Config file integrity (CWE-15)"
17131
+ ];
17132
+ function describeOrigin(origin) {
17133
+ if (origin.kind === "adopted") {
17134
+ return `adopted from the service${origin.createdBy ? `, created by ${origin.createdBy}` : ""}`;
17135
+ }
17136
+ return `derived from this codebase${origin.tools.length > 0 ? ` \xB7 ${origin.tools.join(", ")}` : ""}`;
17137
+ }
17138
+ function closingNote(origin) {
17139
+ if (origin.kind === "adopted") {
17140
+ return `_This Standard came from the service \u2014 the team's shared version. To change it,
17141
+ edit \`.verity/standard.yaml\` and run \`verity standard push\`, or re-synthesize
17142
+ from the codebase with \`verity standard synthesize --force\`._`;
17143
+ }
17144
+ return `_This Standard was derived from the codebase: languages, frameworks and
17145
+ architecture were detected, and the rules come from Verity's research-backed
17146
+ patterns reference. Project-specific patterns are not part of it yet \u2014 add them
17147
+ to \`custom_patterns\` in \`.verity/standard.yaml\` and run \`verity standard push\`._`;
17148
+ }
17149
+ function renderVerityMd(input) {
17150
+ const dimensions = names(input.content, "quality_dimensions");
17151
+ const patterns = names(input.content, "security_patterns");
17152
+ const custom = names(input.content, "custom_patterns");
17153
+ const list2 = (items, fallback) => (items.length > 0 ? items : fallback).map((i) => `- ${i}`).join("\n");
17154
+ return `# VERITY.md \u2014 Quality Gate
17155
+
17156
+ > This project uses [Verity](https://verity.md) to enforce quality and security standards on AI-generated code.
17157
+
17158
+ **URL:** ${input.serviceUrl}
17159
+ **Project:** ${input.projectId ?? "this repository"}
17160
+ **Standard:** v${input.version} (${describeOrigin(input.origin)})
17161
+
17162
+ ## Quality Dimensions
17163
+ ${list2(dimensions, DEFAULT_DIMENSIONS)}
17164
+
17165
+ ## Security Patterns
17166
+ ${list2(patterns, DEFAULT_SECURITY)}
17167
+ ${custom.length > 0 ? `
17168
+ ## Project Patterns
17169
+ ${list2(custom, [])}
17170
+ ` : ""}
17171
+ ## How It Works
17172
+ Every time the coding agent stops, the Verity hook:
17173
+ 1. Runs static analysis via @codacy/analysis-cli
17174
+ 2. Sends results + code to the Verity service
17175
+ 3. An independent model reviews the code
17176
+ 4. Returns PASS / WARN / FAIL with actionable findings
17177
+
17178
+ ${closingNote(input.origin)}
17179
+ `;
17180
+ }
17181
+
17182
+ // src/lib/synthesize.ts
17183
+ function loadCatalog() {
17184
+ const catalog = (0, import_yaml.parse)((0, import_node_fs24.readFileSync)(setupDataPath("patterns-reference.yaml"), "utf-8"));
17185
+ const template = (0, import_yaml.parse)((0, import_node_fs24.readFileSync)(setupDataPath("standard-template.yaml"), "utf-8"));
17186
+ return { catalog, template };
17187
+ }
17188
+ function selectTools(languages, intensity, catalog) {
17189
+ const recommended = /* @__PURE__ */ new Set();
17190
+ for (const language of languages) {
17191
+ for (const tool of catalog.tool_recommendations?.[language]?.[intensity] ?? []) {
17192
+ recommended.add(tool);
17193
+ }
17194
+ }
17195
+ const tools = [];
17196
+ const skipped = [];
17197
+ for (const tool of recommended) {
17198
+ if (tool === "Trivy") {
17199
+ tools.push(tool);
17200
+ continue;
17201
+ }
17202
+ const curated = catalog.curated_patterns?.[tool]?.patterns;
17203
+ if (curated && curated.length > 0) tools.push(tool);
17204
+ else skipped.push(tool);
17205
+ }
17206
+ return { tools: tools.sort(), skipped: skipped.sort() };
17207
+ }
17208
+ var TRIVY_BY_INTENSITY = {
17209
+ lightweight: {
17210
+ emit: "^Trivy_(secret|vulnerability_(critical|high))$",
17211
+ fallback: ["Trivy_secret", "Trivy_vulnerability_critical", "Trivy_vulnerability_high"]
17212
+ },
17213
+ balanced: {
17214
+ emit: "^Trivy_(secret|malicious_packages|vulnerability_(critical|high))$",
17215
+ fallback: [
17216
+ "Trivy_secret",
17217
+ "Trivy_malicious_packages",
17218
+ "Trivy_vulnerability_critical",
17219
+ "Trivy_vulnerability_high"
17220
+ ]
17221
+ },
17222
+ thorough: {
17223
+ // `medium` is emitted when the adapter has it; the fallback stays at the
17224
+ // four ids that are known to exist rather than guessing a fifth.
17225
+ emit: "^Trivy_(secret|malicious_packages|vulnerability_(critical|high|medium))$",
17226
+ fallback: [
17227
+ "Trivy_secret",
17228
+ "Trivy_malicious_packages",
17229
+ "Trivy_vulnerability_critical",
17230
+ "Trivy_vulnerability_high"
17231
+ ]
17232
+ }
17233
+ };
17234
+ function adapterPatternIds(toolId) {
17235
+ try {
17236
+ const out = (0, import_node_child_process8.execFileSync)(
17237
+ process.execPath,
17238
+ [setupDataPath("validate-patterns.mjs"), "--list", toolId],
17239
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
17240
+ );
17241
+ const ids = out.split("\n").map((l) => l.trim()).filter(Boolean);
17242
+ return ids.length > 0 ? new Set(ids) : null;
17243
+ } catch {
17244
+ return null;
17245
+ }
17246
+ }
17247
+ function resolvePatternId(toolId, curatedId, adapterIds) {
17248
+ if (adapterIds.has(curatedId)) return { id: curatedId, resolved: false };
17249
+ const prefixed = `${toolId}_${curatedId}`;
17250
+ if (adapterIds.has(prefixed)) return { id: prefixed, resolved: true };
17251
+ for (const base of [curatedId, prefixed]) {
17252
+ const matches = [...adapterIds].filter((id) => id.startsWith(`${base}_`));
17253
+ if (matches.length === 1) return { id: matches[0], resolved: true };
17254
+ if (matches.length > 1) return null;
17255
+ }
17256
+ const segments = curatedId.split("_");
17257
+ if (segments.length >= 3) {
17258
+ const head = segments.slice(0, 2).join("_");
17259
+ const matches = [...adapterIds].filter((id) => id.startsWith(`${head}_`));
17260
+ if (matches.length === 1) return { id: matches[0], resolved: true };
17261
+ }
17262
+ return null;
17263
+ }
17264
+ function trivyPatterns(intensity) {
17265
+ try {
17266
+ const out = (0, import_node_child_process8.execFileSync)(
17267
+ process.execPath,
17268
+ [setupDataPath("validate-patterns.mjs"), "--emit", "Trivy", TRIVY_BY_INTENSITY[intensity].emit],
17269
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
17270
+ );
17271
+ const parsed = JSON.parse(out);
17272
+ const patterns = parsed.map((p) => p.patternId).filter(Boolean);
17273
+ if (patterns.length > 0) return { patterns, derived: true };
17274
+ } catch {
17275
+ }
17276
+ return { patterns: [...TRIVY_BY_INTENSITY[intensity].fallback], derived: false };
17277
+ }
17278
+ function buildStandard(input, loaded) {
17279
+ const { detected, intensity, tools } = input;
17280
+ const { catalog, template } = loaded;
17281
+ const standard = structuredClone(template);
17282
+ const selected = new Set(tools);
17283
+ standard.created_by = input.createdBy ?? "verity-cli";
17284
+ standard.knowledge_spec = {
17285
+ project_name: detected.projectName,
17286
+ languages: detected.languages,
17287
+ frameworks: detected.frameworks,
17288
+ architecture: detected.architecture,
17289
+ build_system: detected.buildSystem ?? "",
17290
+ test_framework: detected.testFramework ?? ""
17291
+ };
17292
+ const dimensions = Array.isArray(standard.quality_dimensions) ? standard.quality_dimensions : [];
17293
+ for (const dimension of dimensions) {
17294
+ if (dimension.id === "comprehensibility") {
17295
+ for (const signal of dimension.signals ?? []) {
17296
+ if (signal.metric === "file_length") {
17297
+ signal.threshold = tunedFileLengthThreshold(detected.avgFileLength);
17298
+ if (detected.avgFileLength !== null) {
17299
+ signal.measured_project_average = detected.avgFileLength;
17300
+ }
17301
+ }
17302
+ }
17303
+ }
17304
+ if (dimension.id === "type_safety") {
17305
+ dimension.signals = typeSafetySignals(detected.languages, catalog);
17306
+ }
17307
+ }
17308
+ const patterns = Array.isArray(standard.security_patterns) ? standard.security_patterns : [];
17309
+ const catalogById = new Map((catalog.security_patterns ?? []).map((p) => [p.id, p]));
17310
+ for (const pattern of patterns) {
17311
+ const fromCatalog = catalogById.get(String(pattern.id));
17312
+ const enforcers = (fromCatalog?.enforced_by ?? []).filter((e) => e.tool && selected.has(e.tool)).map((e) => ({ tool: e.tool }));
17313
+ pattern.enforced_by = enforcers;
17314
+ }
17315
+ standard.custom_patterns = [];
17316
+ const constraints = standard.process_constraints ?? {};
17317
+ constraints.analysis_mode = intensity;
17318
+ standard.process_constraints = constraints;
17319
+ standard.tool_configuration = Object.fromEntries(
17320
+ tools.map((tool) => {
17321
+ const existing = detected.existingToolConfigs.find((c) => c.tool === tool);
17322
+ return [tool, existing ? { enabled: true, project_config_detected: existing.path } : { enabled: true }];
17323
+ })
17324
+ );
17325
+ return standard;
17326
+ }
17327
+ function typeSafetySignals(languages, catalog) {
17328
+ const template = (catalog.quality_dimensions ?? []).find((d) => d.id === "type_safety");
17329
+ const signals = [];
17330
+ for (const signal of template?.signals ?? []) {
17331
+ const byLanguage = signal.rules_by_language;
17332
+ if (!byLanguage) continue;
17333
+ for (const language of languages) {
17334
+ const entry = byLanguage[language];
17335
+ if (!entry) continue;
17336
+ signals.push({
17337
+ metric: signal.metric,
17338
+ language,
17339
+ tool: entry.tool,
17340
+ rules: entry.rules ?? [],
17341
+ rationale: signal.rationale
17342
+ });
17343
+ }
17344
+ }
17345
+ return signals;
17346
+ }
17347
+ function standardToYaml(standard) {
17348
+ return (0, import_yaml.stringify)(standard, { lineWidth: 100 });
17349
+ }
17350
+ var VERITY_ESLINT_CONFIG_PATH = "./.codacy/tools-configs/eslint.config.mjs";
17351
+ function resolveToolPatterns(toolId, curated) {
17352
+ const real = adapterPatternIds(toolId);
17353
+ if (!real) return { patterns: curated, notes: [] };
17354
+ const notes = [];
17355
+ const kept = [];
17356
+ const lost = [];
17357
+ let renamed = 0;
17358
+ for (const { patternId } of curated) {
17359
+ const match = resolvePatternId(toolId, patternId, real);
17360
+ if (!match) {
17361
+ lost.push(patternId);
17362
+ continue;
17363
+ }
17364
+ if (match.resolved) renamed++;
17365
+ kept.push({ patternId: match.id });
17366
+ }
17367
+ if (renamed > 0) {
17368
+ notes.push(`${toolId}: ${renamed} of ${curated.length} pattern ids resolved to your adapter's spelling.`);
17369
+ }
17370
+ if (lost.length > 0) {
17371
+ notes.push(
17372
+ `${toolId}: ${lost.length} of ${curated.length} curated pattern ids match nothing in your installed adapter and were dropped${kept.length === 0 ? ` \u2014 ${toolId} is not configured at all` : ""} (${lost.slice(0, 3).join(", ")}${lost.length > 3 ? ", \u2026" : ""}). Re-derive them with: validate-patterns.mjs --list ${toolId}`
17373
+ );
17374
+ }
17375
+ return { patterns: kept, notes };
17376
+ }
17377
+ function buildCodacyConfig(input, loaded, trivy, verifyAgainstAdapter = true) {
17378
+ const tools = [];
17379
+ const notes = [];
17380
+ for (const toolId of input.tools) {
17381
+ const curated = toolId === "Trivy" ? trivy.patterns.map((patternId) => ({ patternId })) : (loaded.catalog.curated_patterns?.[toolId]?.patterns ?? []).map((p) => ({ patternId: p.patternId }));
17382
+ const resolved = verifyAgainstAdapter && toolId !== "Trivy" ? resolveToolPatterns(toolId, curated) : { patterns: curated, notes: [] };
17383
+ const patterns = resolved.patterns;
17384
+ notes.push(...resolved.notes);
17385
+ if (patterns.length === 0) continue;
17386
+ const tool = { toolId, patterns };
17387
+ if (toolId === "ESLint9") {
17388
+ tool.useLocalConfigurationFile = false;
17389
+ tool.localConfigurationFile = VERITY_ESLINT_CONFIG_PATH;
17390
+ }
17391
+ tools.push(tool);
17392
+ }
17393
+ return {
17394
+ config: {
17395
+ version: 1,
17396
+ metadata: {
17397
+ source: "local",
17398
+ languages: input.detected.languages.map(capitalizeLanguage)
17399
+ },
17400
+ tools
17401
+ },
17402
+ notes
17403
+ };
17404
+ }
17405
+ function capitalizeLanguage(language) {
17406
+ const special = {
17407
+ typescript: "TypeScript",
17408
+ javascript: "JavaScript",
17409
+ cpp: "CPP",
17410
+ c: "C",
17411
+ csharp: "CSharp",
17412
+ php: "PHP",
17413
+ shell: "Shell",
17414
+ dockerfile: "Dockerfile"
17415
+ };
17416
+ return special[language] ?? language.charAt(0).toUpperCase() + language.slice(1);
17417
+ }
17418
+ var VERITY_ESLINT_CONFIG = `// Verity-owned ESLint config for the quality gate. Generated by \`verity init\`.
17419
+ //
17420
+ // Deliberately SEPARATE from the project's ./eslint.config.js. Pointing the gate at
17421
+ // the project config makes Verity's pattern list a FILTER over rules the project
17422
+ // enabled, so any rule the project disables becomes unreachable (VRT-108).
17423
+ //
17424
+ // The enabled rules come from .codacy/codacy.config.json; this file supplies only
17425
+ // the parser, the globals and the ignores ESLint needs to run at all.
17426
+ import js from "@eslint/js";
17427
+ import globals from "globals";
17428
+ import tseslint from "typescript-eslint";
17429
+
17430
+ export default tseslint.config(
17431
+ { ignores: ["dist/**", "build/**", "node_modules/**", "coverage/**", ".codacy/**"] },
17432
+ {
17433
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
17434
+ files: ["**/*.{ts,tsx,js,jsx}"],
17435
+ languageOptions: {
17436
+ ecmaVersion: 2022,
17437
+ sourceType: "module",
17438
+ globals: { ...globals.browser, ...globals.node },
17439
+ },
17440
+ },
17441
+ );
17442
+ `;
17443
+ function validatePatternIds() {
17444
+ const configPath = projectPath(CODACY_CONFIG_FILE);
17445
+ try {
17446
+ (0, import_node_child_process8.execFileSync)(process.execPath, [setupDataPath("validate-patterns.mjs"), configPath], {
17447
+ encoding: "utf-8",
17448
+ stdio: ["pipe", "pipe", "pipe"],
17449
+ timeout: 6e4
17450
+ });
17451
+ return { status: "valid" };
17452
+ } catch (err) {
17453
+ const e = err;
17454
+ const raw = (e.stderr || e.stdout || e.message || "").trim();
17455
+ const detail = raw.split("\n").slice(0, 6).join("\n");
17456
+ if (/^\s*SKIP:/m.test(raw)) return { status: "unchecked", detail };
17457
+ if (/^\s*FAIL:/m.test(raw)) return { status: "invalid", detail };
17458
+ return { status: "unchecked", detail };
17459
+ }
17460
+ }
17461
+ async function runSynthesis(opts) {
17462
+ const standardPath = projectPath(STANDARD_FILE);
17463
+ if ((0, import_node_fs24.existsSync)(standardPath) && !opts.force) {
17464
+ return { refused: `${STANDARD_FILE} already exists \u2014 pass --force to replace it.` };
17465
+ }
17466
+ const detected = opts.detected ?? detectProject();
17467
+ const loaded = loadCatalog();
17468
+ const { tools, skipped } = selectTools(detected.languages, opts.intensity, loaded.catalog);
17469
+ const trivy = trivyPatterns(opts.intensity);
17470
+ const input = { detected, intensity: opts.intensity, tools };
17471
+ const built = buildCodacyConfig(input, loaded, trivy);
17472
+ const config = built.config;
17473
+ const configuredTools = config.tools.map((t) => t.toolId);
17474
+ const standard = buildStandard({ ...input, tools: configuredTools }, loaded);
17475
+ const written = [];
17476
+ const notes = [...built.notes];
17477
+ await writeFileTo(STANDARD_FILE, standardToYaml(standard));
17478
+ written.push(STANDARD_FILE);
17479
+ if (config.tools.length > 0) {
17480
+ await writeFileTo(CODACY_CONFIG_FILE, JSON.stringify(config, null, 2) + "\n");
17481
+ written.push(CODACY_CONFIG_FILE);
17482
+ if (config.tools.some((t) => t.toolId === "ESLint9")) {
17483
+ await writeFileTo(VERITY_ESLINT_CONFIG_PATH.replace(/^\.\//, ""), VERITY_ESLINT_CONFIG);
17484
+ written.push(VERITY_ESLINT_CONFIG_PATH.replace(/^\.\//, ""));
17485
+ }
17486
+ } else {
17487
+ notes.push(
17488
+ "No static-analysis tool matched this codebase, so the gate runs on AI review alone. Static findings would need a supported language (TypeScript, JavaScript, Python, Go, Java, Kotlin, Ruby, Rust, C/C++, shell, Dockerfile)."
17489
+ );
17490
+ }
17491
+ await writeFileTo(VERITY_MD_FILE, renderVerityMd({
17492
+ content: standard,
17493
+ version: 1,
17494
+ serviceUrl: opts.serviceUrl ?? "https://verity.md",
17495
+ projectId: opts.projectId,
17496
+ origin: { kind: "synthesized", tools }
17497
+ }));
17498
+ written.push(VERITY_MD_FILE);
17499
+ if (skipped.length > 0) {
17500
+ notes.push(
17501
+ `Not configured: ${skipped.join(", ")} \u2014 the patterns reference has no derived rule ids for ${skipped.length === 1 ? "it" : "them"} yet, and a wrong id disables a tool silently.`
17502
+ );
17503
+ }
17504
+ if (!trivy.derived && config.tools.some((t) => t.toolId === "Trivy")) {
17505
+ notes.push(
17506
+ "Trivy pattern ids came from the documented fallback, not from your installed adapter (@codacy/analysis-cli was not found). Re-run after installing it to derive them."
17507
+ );
17508
+ }
17509
+ if (config.tools.length > 0) {
17510
+ const validation = validatePatternIds();
17511
+ if (validation.status === "invalid") {
17512
+ notes.push(`\u26A0 Pattern-id validation FAILED \u2014 a tool may be silently disabled:
17513
+ ${validation.detail}`);
17514
+ } else if (validation.status === "unchecked") {
17515
+ notes.push("Pattern ids could not be validated (no analysis adapter installed on this machine).");
17516
+ }
17517
+ }
17518
+ return { written, notes, standard, tools, intensity: opts.intensity, detected };
17519
+ }
17520
+ async function correctVerityMdVersion(opts) {
17521
+ if (opts.version === 1) return;
17522
+ await writeFileTo(VERITY_MD_FILE, renderVerityMd({
17523
+ content: opts.standard,
17524
+ version: opts.version,
17525
+ serviceUrl: opts.serviceUrl ?? "https://verity.md",
17526
+ projectId: opts.projectId,
17527
+ origin: { kind: "synthesized", tools: opts.tools }
17528
+ }));
17529
+ }
17530
+ async function writeFileTo(relative2, body) {
17531
+ const target = projectPath(relative2);
17532
+ await (0, import_promises9.mkdir)((0, import_node_path20.dirname)(target), { recursive: true });
17533
+ await (0, import_promises9.writeFile)(target, body);
17534
+ }
17535
+ async function deriveConfigForStandard(standard) {
17536
+ const spec = standard.knowledge_spec ?? {};
17537
+ const constraints = standard.process_constraints ?? {};
17538
+ const languages = Array.isArray(spec.languages) ? spec.languages.filter((l) => typeof l === "string").map((l) => l.toLowerCase()) : [];
17539
+ const mode2 = constraints.analysis_mode;
17540
+ const intensity = mode2 === "lightweight" || mode2 === "thorough" ? mode2 : "balanced";
17541
+ const written = [];
17542
+ const notes = [];
17543
+ if (languages.length === 0) {
17544
+ notes.push(
17545
+ "The adopted Standard names no languages, so no analysis config could be derived from it. Static analysis will run nothing until one exists."
17546
+ );
17547
+ return { written, notes, tools: [] };
17548
+ }
17549
+ const loaded = loadCatalog();
17550
+ const { tools, skipped } = selectTools(languages, intensity, loaded.catalog);
17551
+ const trivy = trivyPatterns(intensity);
17552
+ const stub = {
17553
+ projectName: "",
17554
+ languages,
17555
+ languageCounts: {},
17556
+ frameworks: [],
17557
+ architecture: "monolith",
17558
+ buildSystem: null,
17559
+ testFramework: null,
17560
+ existingToolConfigs: [],
17561
+ avgFileLength: null,
17562
+ filesScanned: 0
17563
+ };
17564
+ const built = buildCodacyConfig({ detected: stub, intensity, tools }, loaded, trivy);
17565
+ const config = built.config;
17566
+ notes.push(...built.notes);
17567
+ if (config.tools.length === 0) {
17568
+ notes.push("No static-analysis tool matches the adopted Standard's languages \u2014 the gate runs on AI review alone.");
17569
+ return { written, notes, tools: [] };
17570
+ }
17571
+ await writeFileTo(CODACY_CONFIG_FILE, JSON.stringify(config, null, 2) + "\n");
17572
+ written.push(CODACY_CONFIG_FILE);
17573
+ if (config.tools.some((t) => t.toolId === "ESLint9")) {
17574
+ const path = VERITY_ESLINT_CONFIG_PATH.replace(/^\.\//, "");
17575
+ await writeFileTo(path, VERITY_ESLINT_CONFIG);
17576
+ written.push(path);
17577
+ }
17578
+ if (skipped.length > 0) {
17579
+ notes.push(`Not configured: ${skipped.join(", ")} \u2014 no derived rule ids for ${skipped.length === 1 ? "it" : "them"} yet.`);
17580
+ }
17581
+ const validation = validatePatternIds();
17582
+ if (validation.status === "invalid") {
17583
+ notes.push(`\u26A0 Pattern-id validation FAILED \u2014 a tool may be silently disabled:
17584
+ ${validation.detail}`);
17585
+ }
17586
+ return { written, notes, tools };
17587
+ }
17588
+
17589
+ // src/lib/setup-state.ts
17590
+ var import_promises10 = require("node:fs/promises");
17591
+ var import_node_fs25 = require("node:fs");
17592
+ var SETUP_STATE_FILE = `${VERITY_DIR}/setup.json`;
17593
+ async function readSetupState() {
17594
+ const path = projectPath(SETUP_STATE_FILE);
17595
+ if (!(0, import_node_fs25.existsSync)(path)) return null;
17596
+ try {
17597
+ const parsed = JSON.parse(await (0, import_promises10.readFile)(path, "utf-8"));
17598
+ return parsed && typeof parsed === "object" ? parsed : null;
17599
+ } catch {
17600
+ return null;
17601
+ }
17602
+ }
17603
+ async function writeSetupState(patch) {
17604
+ const current = await readSetupState() ?? { version: 1 };
17605
+ const next = { ...current, ...patch, version: 1 };
17606
+ await writeJsonFilePreservingStyle(projectPath(SETUP_STATE_FILE), next);
17607
+ return next;
17608
+ }
17609
+
17610
+ // src/lib/push-setup.ts
17611
+ var import_node_fs27 = require("node:fs");
17612
+ var import_promises11 = require("node:fs/promises");
17613
+ var import_yaml2 = __toESM(require_dist());
17614
+
17615
+ // src/lib/verityignore.ts
17616
+ var import_node_fs26 = require("node:fs");
16767
17617
  var EMPTY = { rules: [], securityOverlap: [], problems: [] };
16768
17618
  var SECURITY_PROBES = [
16769
17619
  ".env",
@@ -16851,14 +17701,14 @@ function decide(rules, path) {
16851
17701
  }
16852
17702
  return excluded;
16853
17703
  }
16854
- function isIgnored(ig, path) {
17704
+ function isIgnored2(ig, path) {
16855
17705
  return decide(ig.rules, path);
16856
17706
  }
16857
17707
  function loadVerityIgnore() {
16858
17708
  const file = projectPath(VERITYIGNORE_FILE);
16859
- if (!(0, import_node_fs22.existsSync)(file)) return EMPTY;
17709
+ if (!(0, import_node_fs26.existsSync)(file)) return EMPTY;
16860
17710
  try {
16861
- return parseVerityIgnore((0, import_node_fs22.readFileSync)(file, "utf-8"));
17711
+ return parseVerityIgnore((0, import_node_fs26.readFileSync)(file, "utf-8"));
16862
17712
  } catch {
16863
17713
  return EMPTY;
16864
17714
  }
@@ -16871,7 +17721,7 @@ function partitionIgnored(paths, ig) {
16871
17721
  const kept = [];
16872
17722
  const ignored = [];
16873
17723
  for (const p of paths) {
16874
- if (p !== VERITYIGNORE_FILE && isIgnored(ig, p)) ignored.push(p);
17724
+ if (p !== VERITYIGNORE_FILE && isIgnored2(ig, p)) ignored.push(p);
16875
17725
  else kept.push(p);
16876
17726
  }
16877
17727
  return {
@@ -16887,6 +17737,22 @@ function ignoreShare(kept, ignored) {
16887
17737
  if (total === 0) return 0;
16888
17738
  return Math.round(ignored / total * 1e3) / 1e3;
16889
17739
  }
17740
+ function buildStandardUpload(standard, ignoreRaw) {
17741
+ if (ignoreRaw === null) return { content: standard, warning: null };
17742
+ return {
17743
+ content: { ...standard, verityignore: ignoreRaw },
17744
+ warning: describeSecurityOverlap(parseVerityIgnore(ignoreRaw))
17745
+ };
17746
+ }
17747
+ function readVerityIgnoreRaw() {
17748
+ const file = projectPath(VERITYIGNORE_FILE);
17749
+ if (!(0, import_node_fs26.existsSync)(file)) return null;
17750
+ try {
17751
+ return (0, import_node_fs26.readFileSync)(file, "utf-8");
17752
+ } catch {
17753
+ return null;
17754
+ }
17755
+ }
16890
17756
  function describeSecurityOverlap(ig) {
16891
17757
  if (ig.securityOverlap.length === 0) return null;
16892
17758
  const lines = ig.securityOverlap.map(
@@ -16897,9 +17763,122 @@ ${lines.join("\n")}
16897
17763
  These are still excluded \u2014 this is a warning, not a refusal. Add a \`!\` rule to keep one in scope, e.g. \`!.env*\`.`;
16898
17764
  }
16899
17765
 
17766
+ // src/lib/push-setup.ts
17767
+ async function pushStandardAndConfig(globals, what = {}) {
17768
+ const pushStandard = what.standard ?? true;
17769
+ const pushConfig = what.config ?? true;
17770
+ const lines = [];
17771
+ const token = await resolveToken(globals.token);
17772
+ const url = await resolveServiceUrl(globals.serviceUrl);
17773
+ if (!token.ok || !url.ok) {
17774
+ lines.push('Not signed in \u2014 the Standard stays local until you run "verity login" and "verity standard push".');
17775
+ return { lines, standardVersion: null, configPushed: false };
17776
+ }
17777
+ let standardVersion = null;
17778
+ const standardPath = projectPath(STANDARD_FILE);
17779
+ if (pushStandard && (0, import_node_fs27.existsSync)(standardPath)) {
17780
+ try {
17781
+ const content = (0, import_yaml2.parse)(await (0, import_promises11.readFile)(standardPath, "utf-8"));
17782
+ const upload = buildStandardUpload(content, readVerityIgnoreRaw());
17783
+ if (upload.warning) lines.push(upload.warning);
17784
+ const result = await apiRequest({
17785
+ method: "POST",
17786
+ path: "/standards",
17787
+ serviceUrl: url.data,
17788
+ token: token.data.token,
17789
+ body: { content: upload.content, created_by: "verity-cli" },
17790
+ verbose: globals.verbose
17791
+ });
17792
+ if (result.ok) {
17793
+ standardVersion = result.data.version;
17794
+ lines.push(`Standard pushed to Verity (v${result.data.version}) \u2713`);
17795
+ } else {
17796
+ lines.push(`Could not push the Standard: ${result.error} \u2014 run "verity standard push" later.`);
17797
+ }
17798
+ } catch (err) {
17799
+ lines.push(`Could not read ${STANDARD_FILE}: ${err.message}`);
17800
+ }
17801
+ }
17802
+ let configPushed = false;
17803
+ const configPath = projectPath(CODACY_CONFIG_FILE);
17804
+ if (pushConfig && (0, import_node_fs27.existsSync)(configPath)) {
17805
+ try {
17806
+ const content = JSON.parse(await (0, import_promises11.readFile)(configPath, "utf-8"));
17807
+ const result = await apiRequest({
17808
+ method: "POST",
17809
+ path: "/analysis-configs",
17810
+ serviceUrl: url.data,
17811
+ token: token.data.token,
17812
+ body: { content },
17813
+ verbose: globals.verbose
17814
+ });
17815
+ if (result.ok) {
17816
+ configPushed = true;
17817
+ lines.push("Analysis config pushed \u2713");
17818
+ } else {
17819
+ lines.push(`Could not push the analysis config: ${result.error} \u2014 run "verity config push" later.`);
17820
+ }
17821
+ } catch (err) {
17822
+ lines.push(`Could not read ${CODACY_CONFIG_FILE}: ${err.message}`);
17823
+ }
17824
+ }
17825
+ return { lines, standardVersion, configPushed };
17826
+ }
17827
+
16900
17828
  // src/commands/standard.ts
16901
17829
  function registerStandardCommands(program2) {
16902
17830
  const standard = program2.command("standard").description("Manage the project Standard");
17831
+ standard.command("synthesize").description("Derive the Standard and analysis config from this codebase (no model required)").option("--intensity <mode>", "lightweight | balanced | thorough (default: your recorded answer)").option("--force", "Replace an existing .verity/standard.yaml").option("--config-only", "Rewrite only the analysis config, from the Standard already here").option("--push", "Upload the Standard and config after writing them").action(async (opts) => {
17832
+ const globals = program2.opts();
17833
+ const state = await readSetupState();
17834
+ if (opts.configOnly) {
17835
+ const standardPath = projectPath(STANDARD_FILE);
17836
+ if (!(0, import_node_fs28.existsSync)(standardPath)) {
17837
+ printError(`No ${STANDARD_FILE} here \u2014 run "verity standard synthesize" to create one.`);
17838
+ process.exit(1);
17839
+ }
17840
+ const content = (0, import_yaml3.parse)(await (0, import_promises12.readFile)(standardPath, "utf-8"));
17841
+ const derived = await deriveConfigForStandard(content);
17842
+ for (const path of derived.written) printInfo(` ${path} \u2713`);
17843
+ for (const note of derived.notes) printWarn(` ${note}`);
17844
+ if (derived.written.length === 0) printWarn(" Nothing written.");
17845
+ if (opts.push) {
17846
+ const pushed = await pushStandardAndConfig(globals, { standard: false });
17847
+ for (const line of pushed.lines) printInfo(` ${line}`);
17848
+ }
17849
+ return;
17850
+ }
17851
+ if (opts.intensity && !parseIntensity(opts.intensity)) {
17852
+ printError(`Unknown intensity "${opts.intensity}" \u2014 use lightweight, balanced or thorough.`);
17853
+ process.exit(1);
17854
+ }
17855
+ const intensity = parseIntensity(opts.intensity) ?? state?.intensity ?? "balanced";
17856
+ const urlResult = await resolveServiceUrl(globals.serviceUrl);
17857
+ const result = await runSynthesis({
17858
+ intensity,
17859
+ force: opts.force ?? false,
17860
+ serviceUrl: urlResult.ok ? urlResult.data : void 0
17861
+ });
17862
+ if ("refused" in result) {
17863
+ printWarn(result.refused);
17864
+ process.exit(1);
17865
+ }
17866
+ printInfo(` ${describeDetected(result.detected)}`);
17867
+ for (const path of result.written) printInfo(` ${path} \u2713`);
17868
+ for (const note of result.notes) printWarn(` ${note}`);
17869
+ if (opts.push) {
17870
+ const pushed = await pushStandardAndConfig(globals);
17871
+ for (const line of pushed.lines) printInfo(` ${line}`);
17872
+ if (pushed.standardVersion !== null) {
17873
+ await correctVerityMdVersion({
17874
+ standard: result.standard,
17875
+ version: pushed.standardVersion,
17876
+ tools: result.tools,
17877
+ serviceUrl: urlResult.ok ? urlResult.data : void 0
17878
+ });
17879
+ }
17880
+ }
17881
+ });
16903
17882
  standard.command("push").description("Upload the project Standard to the service").option("--file <path>", "Path to standard YAML file", STANDARD_FILE).option("--created-by <name>", "Who created this version", "claude-code").action(async (opts) => {
16904
17883
  const globals = program2.opts();
16905
17884
  const tokenResult = await resolveToken(globals.token);
@@ -16914,37 +17893,26 @@ function registerStandardCommands(program2) {
16914
17893
  }
16915
17894
  let yamlContent;
16916
17895
  try {
16917
- yamlContent = await (0, import_promises9.readFile)(opts.file, "utf-8");
17896
+ yamlContent = await (0, import_promises12.readFile)(opts.file, "utf-8");
16918
17897
  } catch {
16919
17898
  printError(`Cannot read ${opts.file}`);
16920
17899
  process.exit(1);
16921
17900
  }
16922
17901
  let content;
16923
17902
  try {
16924
- content = (0, import_yaml.parse)(yamlContent);
17903
+ content = (0, import_yaml3.parse)(yamlContent);
16925
17904
  } catch (err) {
16926
17905
  printError(`Invalid YAML in ${opts.file}: ${err.message}`);
16927
17906
  process.exit(1);
16928
17907
  }
16929
- let ignoreRaw = null;
16930
- const ignorePath = projectPath(VERITYIGNORE_FILE);
16931
- if ((0, import_node_fs23.existsSync)(ignorePath)) {
16932
- try {
16933
- ignoreRaw = (0, import_node_fs23.readFileSync)(ignorePath, "utf-8");
16934
- const overlap = describeSecurityOverlap(parseVerityIgnore(ignoreRaw));
16935
- if (overlap) printWarn(overlap);
16936
- } catch {
16937
- }
16938
- }
17908
+ const upload = buildStandardUpload(content, readVerityIgnoreRaw());
17909
+ if (upload.warning) printWarn(upload.warning);
16939
17910
  const result = await apiRequest({
16940
17911
  method: "POST",
16941
17912
  path: "/standards",
16942
17913
  serviceUrl: urlResult.data,
16943
17914
  token: tokenResult.data.token,
16944
- body: {
16945
- content: ignoreRaw === null ? content : { ...content, verityignore: ignoreRaw },
16946
- created_by: opts.createdBy
16947
- },
17915
+ body: { content: upload.content, created_by: opts.createdBy },
16948
17916
  verbose: globals.verbose
16949
17917
  });
16950
17918
  if (!result.ok) {
@@ -17016,9 +17984,13 @@ function registerStandardCommands(program2) {
17016
17984
  }
17017
17985
  });
17018
17986
  }
17987
+ function parseIntensity(value) {
17988
+ if (value === "lightweight" || value === "balanced" || value === "thorough") return value;
17989
+ return null;
17990
+ }
17019
17991
 
17020
17992
  // src/commands/config.ts
17021
- var import_promises10 = require("node:fs/promises");
17993
+ var import_promises13 = require("node:fs/promises");
17022
17994
  function registerConfigCommands(program2) {
17023
17995
  const config = program2.command("config").description("Manage analysis configuration");
17024
17996
  config.command("service-url").description("Print the resolved service URL (used by the shipped skills)").action(async () => {
@@ -17060,7 +18032,7 @@ function registerConfigCommands(program2) {
17060
18032
  }
17061
18033
  let content;
17062
18034
  try {
17063
- const raw = await (0, import_promises10.readFile)(opts.file, "utf-8");
18035
+ const raw = await (0, import_promises13.readFile)(opts.file, "utf-8");
17064
18036
  content = JSON.parse(raw);
17065
18037
  } catch {
17066
18038
  printError(`Cannot read or parse ${opts.file}`);
@@ -17188,10 +18160,10 @@ function formatRunDetail(run2) {
17188
18160
  }
17189
18161
 
17190
18162
  // src/lib/ignore-declaration.ts
17191
- var import_node_fs25 = require("node:fs");
18163
+ var import_node_fs30 = require("node:fs");
17192
18164
 
17193
18165
  // src/lib/debounce.ts
17194
- var import_node_fs24 = require("node:fs");
18166
+ var import_node_fs29 = require("node:fs");
17195
18167
  var import_node_crypto10 = require("node:crypto");
17196
18168
  function scopedFile(base, sessionId) {
17197
18169
  if (!sessionId) return base;
@@ -17199,9 +18171,9 @@ function scopedFile(base, sessionId) {
17199
18171
  }
17200
18172
  function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
17201
18173
  const file = scopedFile(DEBOUNCE_FILE, sessionId);
17202
- if (!(0, import_node_fs24.existsSync)(file)) return null;
18174
+ if (!(0, import_node_fs29.existsSync)(file)) return null;
17203
18175
  try {
17204
- const lastTs = parseInt((0, import_node_fs24.readFileSync)(file, "utf-8").trim(), 10);
18176
+ const lastTs = parseInt((0, import_node_fs29.readFileSync)(file, "utf-8").trim(), 10);
17205
18177
  const nowTs = Math.floor(Date.now() / 1e3);
17206
18178
  const elapsed = nowTs - lastTs;
17207
18179
  if (elapsed < debounceSeconds) {
@@ -17214,10 +18186,10 @@ function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
17214
18186
  function checkMtime(files, bypassForRecentCommits, sessionId) {
17215
18187
  if (bypassForRecentCommits) return null;
17216
18188
  const file = scopedFile(DEBOUNCE_FILE, sessionId);
17217
- if (!(0, import_node_fs24.existsSync)(file)) return null;
18189
+ if (!(0, import_node_fs29.existsSync)(file)) return null;
17218
18190
  let debounceTime;
17219
18191
  try {
17220
- debounceTime = (0, import_node_fs24.statSync)(file).mtimeMs;
18192
+ debounceTime = (0, import_node_fs29.statSync)(file).mtimeMs;
17221
18193
  } catch {
17222
18194
  return null;
17223
18195
  }
@@ -17225,7 +18197,7 @@ function checkMtime(files, bypassForRecentCommits, sessionId) {
17225
18197
  const resolved = resolveFile(f);
17226
18198
  if (!resolved) continue;
17227
18199
  try {
17228
- const stat3 = (0, import_node_fs24.statSync)(resolved);
18200
+ const stat3 = (0, import_node_fs29.statSync)(resolved);
17229
18201
  if (stat3.mtimeMs > debounceTime) {
17230
18202
  return null;
17231
18203
  }
@@ -17241,8 +18213,8 @@ function computeContentHash(files) {
17241
18213
  for (const f of sorted) {
17242
18214
  const resolved = resolveFile(f) ?? f;
17243
18215
  try {
17244
- if ((0, import_node_fs24.existsSync)(resolved)) {
17245
- hash.update((0, import_node_fs24.readFileSync)(resolved));
18216
+ if ((0, import_node_fs29.existsSync)(resolved)) {
18217
+ hash.update((0, import_node_fs29.readFileSync)(resolved));
17246
18218
  }
17247
18219
  } catch {
17248
18220
  }
@@ -17252,9 +18224,9 @@ function computeContentHash(files) {
17252
18224
  function checkContentHash(files, sessionId) {
17253
18225
  const hash = computeContentHash(files);
17254
18226
  const file = scopedFile(HASH_FILE, sessionId);
17255
- if ((0, import_node_fs24.existsSync)(file)) {
18227
+ if ((0, import_node_fs29.existsSync)(file)) {
17256
18228
  try {
17257
- const storedHash = (0, import_node_fs24.readFileSync)(file, "utf-8").trim();
18229
+ const storedHash = (0, import_node_fs29.readFileSync)(file, "utf-8").trim();
17258
18230
  if (hash === storedHash) {
17259
18231
  return { skip: "No source changes since last analysis", hash };
17260
18232
  }
@@ -17264,24 +18236,24 @@ function checkContentHash(files, sessionId) {
17264
18236
  return { skip: null, hash };
17265
18237
  }
17266
18238
  function recordAnalysisStart(sessionId) {
17267
- (0, import_node_fs24.mkdirSync)(VERITY_DIR, { recursive: true });
17268
- (0, import_node_fs24.writeFileSync)(scopedFile(DEBOUNCE_FILE, sessionId), String(Math.floor(Date.now() / 1e3)));
18239
+ (0, import_node_fs29.mkdirSync)(VERITY_DIR, { recursive: true });
18240
+ (0, import_node_fs29.writeFileSync)(scopedFile(DEBOUNCE_FILE, sessionId), String(Math.floor(Date.now() / 1e3)));
17269
18241
  }
17270
18242
  function recordPassHash(hash, sessionId) {
17271
- (0, import_node_fs24.writeFileSync)(scopedFile(HASH_FILE, sessionId), hash);
18243
+ (0, import_node_fs29.writeFileSync)(scopedFile(HASH_FILE, sessionId), hash);
17272
18244
  }
17273
18245
  function narrowToRecent(files, sessionId) {
17274
18246
  const file = scopedFile(DEBOUNCE_FILE, sessionId);
17275
- if (!(0, import_node_fs24.existsSync)(file)) return files;
18247
+ if (!(0, import_node_fs29.existsSync)(file)) return files;
17276
18248
  let debounceTime;
17277
18249
  try {
17278
- debounceTime = (0, import_node_fs24.statSync)(file).mtimeMs;
18250
+ debounceTime = (0, import_node_fs29.statSync)(file).mtimeMs;
17279
18251
  } catch {
17280
18252
  return files;
17281
18253
  }
17282
18254
  const recent = files.filter((f) => {
17283
18255
  try {
17284
- return (0, import_node_fs24.existsSync)(f) && (0, import_node_fs24.statSync)(f).mtimeMs > debounceTime;
18256
+ return (0, import_node_fs29.existsSync)(f) && (0, import_node_fs29.statSync)(f).mtimeMs > debounceTime;
17285
18257
  } catch {
17286
18258
  return false;
17287
18259
  }
@@ -17294,9 +18266,9 @@ function readIteration(currentCommit, _contentHash) {
17294
18266
  var NO_BLOCKS = { attempts: 0, blocks: 0, fingerprint: null };
17295
18267
  function readBlockState(currentCommit, opts) {
17296
18268
  if (opts?.newUserPrompt) return NO_BLOCKS;
17297
- if (!(0, import_node_fs24.existsSync)(ITERATION_FILE)) return NO_BLOCKS;
18269
+ if (!(0, import_node_fs29.existsSync)(ITERATION_FILE)) return NO_BLOCKS;
17298
18270
  try {
17299
- const stored = (0, import_node_fs24.readFileSync)(ITERATION_FILE, "utf-8").trim();
18271
+ const stored = (0, import_node_fs29.readFileSync)(ITERATION_FILE, "utf-8").trim();
17300
18272
  const parsed = stored.startsWith("{") ? parseJsonState(stored) : parseLegacyState(stored);
17301
18273
  if (!parsed) return NO_BLOCKS;
17302
18274
  if (parsed.commit !== currentCommit) return NO_BLOCKS;
@@ -17342,8 +18314,8 @@ function isSameProblem(previous, current) {
17342
18314
  return current.split(",").some((k) => prev.has(k));
17343
18315
  }
17344
18316
  function writeBlockState(commit, state) {
17345
- (0, import_node_fs24.mkdirSync)(VERITY_DIR, { recursive: true });
17346
- (0, import_node_fs24.writeFileSync)(
18317
+ (0, import_node_fs29.mkdirSync)(VERITY_DIR, { recursive: true });
18318
+ (0, import_node_fs29.writeFileSync)(
17347
18319
  ITERATION_FILE,
17348
18320
  JSON.stringify({
17349
18321
  v: 2,
@@ -17448,9 +18420,9 @@ function resolveIgnoreState(keys) {
17448
18420
  }
17449
18421
  function readIgnoreState(sessionId) {
17450
18422
  const file = stateFile(sessionId);
17451
- if (!(0, import_node_fs25.existsSync)(file)) return null;
18423
+ if (!(0, import_node_fs30.existsSync)(file)) return null;
17452
18424
  try {
17453
- const o = JSON.parse((0, import_node_fs25.readFileSync)(file, "utf-8")) ?? {};
18425
+ const o = JSON.parse((0, import_node_fs30.readFileSync)(file, "utf-8")) ?? {};
17454
18426
  const spent = typeof o.spent === "number" ? o.spent : 0;
17455
18427
  const raw = o.active;
17456
18428
  let active = null;
@@ -17474,8 +18446,8 @@ function readIgnoreState(sessionId) {
17474
18446
  }
17475
18447
  function writeIgnoreState(state, sessionId) {
17476
18448
  try {
17477
- (0, import_node_fs25.mkdirSync)(projectPath(VERITY_DIR), { recursive: true });
17478
- (0, import_node_fs25.writeFileSync)(stateFile(sessionId), JSON.stringify({ v: 1, active: state.active, spent: state.spent }));
18449
+ (0, import_node_fs30.mkdirSync)(projectPath(VERITY_DIR), { recursive: true });
18450
+ (0, import_node_fs30.writeFileSync)(stateFile(sessionId), JSON.stringify({ v: 1, active: state.active, spent: state.spent }));
17479
18451
  } catch {
17480
18452
  }
17481
18453
  }
@@ -17849,8 +18821,11 @@ function createRun(opts, globals) {
17849
18821
  };
17850
18822
  }
17851
18823
 
18824
+ // src/commands/analyze/index.ts
18825
+ var import_node_fs43 = require("node:fs");
18826
+
17852
18827
  // src/lib/repo-context.ts
17853
- var import_node_child_process7 = require("node:child_process");
18828
+ var import_node_child_process9 = require("node:child_process");
17854
18829
  var import_node_os3 = require("node:os");
17855
18830
  function rgInvocations(env = process.env) {
17856
18831
  const out = [{ cmd: "rg" }];
@@ -18426,7 +19401,7 @@ function buildRepoContext(input) {
18426
19401
  ];
18427
19402
  let res = null;
18428
19403
  for (const inv of rgInvocations()) {
18429
- res = (0, import_node_child_process7.spawnSync)(inv.cmd, args, {
19404
+ res = (0, import_node_child_process9.spawnSync)(inv.cmd, args, {
18430
19405
  ...inv.argv0 ? { argv0: inv.argv0 } : {},
18431
19406
  cwd: input.cwd ?? process.cwd(),
18432
19407
  timeout: input.timeoutMs ?? RG_TIMEOUT_MS,
@@ -18667,11 +19642,11 @@ function installRunEvidence(run2) {
18667
19642
  }
18668
19643
 
18669
19644
  // src/lib/git-frame.ts
18670
- var import_node_child_process8 = require("node:child_process");
18671
- var import_node_fs26 = require("node:fs");
19645
+ var import_node_child_process10 = require("node:child_process");
19646
+ var import_node_fs31 = require("node:fs");
18672
19647
  var import_node_os4 = require("node:os");
18673
- var import_node_path18 = require("node:path");
18674
- var import_node_path19 = require("node:path");
19648
+ var import_node_path21 = require("node:path");
19649
+ var import_node_path22 = require("node:path");
18675
19650
  var VALUE_TOKEN = `(?:'[^']*'|"[^"]*"|\\S+)`;
18676
19651
  var GIT_GLOBAL_OPTS = `(?:\\s+(?:-[Cc]\\s+${VALUE_TOKEN}|--?[\\w-]+(?:=\\S+)?))*`;
18677
19652
  var COMMIT_HEAD = `git${GIT_GLOBAL_OPTS}\\s+commit(?![\\w-])`;
@@ -18724,8 +19699,8 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
18724
19699
  if (SHELL_DYNAMIC.test(raw) || raw === "-") {
18725
19700
  return { dir: null, named: true, unresolvable: `cd target not statically resolvable: ${raw}` };
18726
19701
  }
18727
- const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
18728
- dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
19702
+ const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path22.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
19703
+ dir = (0, import_node_path21.isAbsolute)(expanded) ? expanded : (0, import_node_path21.resolve)(dir, expanded);
18729
19704
  }
18730
19705
  const seg = segments[segmentIndex];
18731
19706
  const overrideMatch = /--(?:git-dir|work-tree)(?:=|\s)|\bGIT_(?:DIR|WORK_TREE|INDEX_FILE)=/.exec(seg);
@@ -18743,8 +19718,8 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
18743
19718
  if (SHELL_DYNAMIC.test(raw)) {
18744
19719
  return { dir: null, named: true, unresolvable: `-C target not statically resolvable: ${raw}` };
18745
19720
  }
18746
- const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
18747
- dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
19721
+ const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path22.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
19722
+ dir = (0, import_node_path21.isAbsolute)(expanded) ? expanded : (0, import_node_path21.resolve)(dir, expanded);
18748
19723
  }
18749
19724
  }
18750
19725
  return { dir, named, unresolvable: null };
@@ -18800,21 +19775,21 @@ function parsePushTarget(segment) {
18800
19775
  }
18801
19776
  function gitAt(dir, args) {
18802
19777
  try {
18803
- return (0, import_node_child_process8.execFileSync)("git", args, { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
19778
+ return (0, import_node_child_process10.execFileSync)("git", args, { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
18804
19779
  } catch {
18805
19780
  return "";
18806
19781
  }
18807
19782
  }
18808
19783
  function realpathOr(p) {
18809
19784
  try {
18810
- return import_node_fs26.realpathSync.native(p);
19785
+ return import_node_fs31.realpathSync.native(p);
18811
19786
  } catch {
18812
- return (0, import_node_path18.resolve)(p);
19787
+ return (0, import_node_path21.resolve)(p);
18813
19788
  }
18814
19789
  }
18815
19790
  function resolveFrame(input) {
18816
19791
  const found = findMomentSegment(input.command, input.on);
18817
- const hookDirUsable = !!input.hookCwd && (0, import_node_fs26.existsSync)(input.hookCwd);
19792
+ const hookDirUsable = !!input.hookCwd && (0, import_node_fs31.existsSync)(input.hookCwd);
18818
19793
  const baseDir = hookDirUsable ? input.hookCwd : process.cwd();
18819
19794
  let anchor = hookDirUsable ? "hook-cwd" : "process-cwd";
18820
19795
  const refuse = (refusal) => ({
@@ -18837,7 +19812,7 @@ function resolveFrame(input) {
18837
19812
  if (dirs.size > 1) return refuse(`target:multiple ${found.moment} targets in one command`);
18838
19813
  const targetDir = dirs.size === 1 ? [...dirs][0] : baseDir;
18839
19814
  if (targetDir !== baseDir) {
18840
- if (!(0, import_node_fs26.existsSync)(targetDir)) return refuse(`target:directory does not exist: ${targetDir}`);
19815
+ if (!(0, import_node_fs31.existsSync)(targetDir)) return refuse(`target:directory does not exist: ${targetDir}`);
18841
19816
  dir = targetDir;
18842
19817
  }
18843
19818
  }
@@ -18848,7 +19823,7 @@ function resolveFrame(input) {
18848
19823
  const gitDirRaw = gitAt(dir, ["rev-parse", "--absolute-git-dir"]);
18849
19824
  const commonRaw = gitAt(dir, ["rev-parse", "--git-common-dir"]);
18850
19825
  const gitDir = gitDirRaw ? realpathOr(gitDirRaw) : null;
18851
- const commonDir = commonRaw ? realpathOr((0, import_node_path18.isAbsolute)(commonRaw) ? commonRaw : (0, import_node_path18.resolve)(dir, commonRaw)) : null;
19826
+ const commonDir = commonRaw ? realpathOr((0, import_node_path21.isAbsolute)(commonRaw) ? commonRaw : (0, import_node_path21.resolve)(dir, commonRaw)) : null;
18852
19827
  const branchRaw = gitAt(dir, ["rev-parse", "--abbrev-ref", "HEAD"]);
18853
19828
  return {
18854
19829
  moment: found?.moment ?? null,
@@ -18875,8 +19850,8 @@ function refResolves(frame, ref) {
18875
19850
  var SHA_RE2 = /^[0-9a-f]{40}$/;
18876
19851
  function stagedRange(frame) {
18877
19852
  if (!frame.worktreeRoot) return { kind: "nothing", base: null, head: "INDEX", via: "refused" };
18878
- const mergeHead = frame.gitDir ? (0, import_node_path19.join)(frame.gitDir, "MERGE_HEAD") : null;
18879
- if (mergeHead && (0, import_node_fs26.existsSync)(mergeHead)) {
19853
+ const mergeHead = frame.gitDir ? (0, import_node_path22.join)(frame.gitDir, "MERGE_HEAD") : null;
19854
+ if (mergeHead && (0, import_node_fs31.existsSync)(mergeHead)) {
18880
19855
  const vsHead = new Set(frameGit(frame, ["diff", "--cached", "--name-only", "HEAD"]).split("\n").filter(Boolean));
18881
19856
  const vsMerge = new Set(frameGit(frame, ["diff", "--cached", "--name-only", "MERGE_HEAD"]).split("\n").filter(Boolean));
18882
19857
  const resolutions = [...vsHead].filter((f) => vsMerge.has(f) && !isVerityOwnedPath(f));
@@ -19000,7 +19975,7 @@ function truthy(v) {
19000
19975
  }
19001
19976
 
19002
19977
  // src/lib/transcript.ts
19003
- var import_node_fs27 = require("node:fs");
19978
+ var import_node_fs32 = require("node:fs");
19004
19979
  var MAX_READ_BYTES = 256 * 1024;
19005
19980
  var SMALL_FILE_BYTES = 64 * 1024;
19006
19981
  var MAX_FILES_LIST = 20;
@@ -19027,7 +20002,7 @@ async function extractActionSummary(transcriptPath) {
19027
20002
  function readTurnLines(transcriptPath) {
19028
20003
  let size;
19029
20004
  try {
19030
- size = (0, import_node_fs27.statSync)(transcriptPath).size;
20005
+ size = (0, import_node_fs32.statSync)(transcriptPath).size;
19031
20006
  } catch {
19032
20007
  return null;
19033
20008
  }
@@ -19035,7 +20010,7 @@ function readTurnLines(transcriptPath) {
19035
20010
  let raw;
19036
20011
  let windowed = false;
19037
20012
  if (size <= SMALL_FILE_BYTES) {
19038
- raw = (0, import_node_fs27.readFileSync)(transcriptPath, "utf-8");
20013
+ raw = (0, import_node_fs32.readFileSync)(transcriptPath, "utf-8");
19039
20014
  } else {
19040
20015
  windowed = true;
19041
20016
  const buf = Buffer.alloc(Math.min(MAX_READ_BYTES, size));
@@ -19534,7 +20509,7 @@ function channelSilence(input) {
19534
20509
  // src/lib/cli-version.ts
19535
20510
  function cliVersion() {
19536
20511
  try {
19537
- return true ? "0.31.3" : "dev";
20512
+ return true ? "0.32.0-experimental.68ae2ee" : "dev";
19538
20513
  } catch {
19539
20514
  return "dev";
19540
20515
  }
@@ -19574,8 +20549,8 @@ async function sendSkipBeacon(ctx, reason) {
19574
20549
  }
19575
20550
 
19576
20551
  // src/lib/static-analysis.ts
19577
- var import_node_child_process9 = require("node:child_process");
19578
- var import_node_fs28 = require("node:fs");
20552
+ var import_node_child_process11 = require("node:child_process");
20553
+ var import_node_fs33 = require("node:fs");
19579
20554
  var SEVERITY_ORDER = {
19580
20555
  Error: 0,
19581
20556
  Critical: 0,
@@ -19587,7 +20562,7 @@ var SEVERITY_ORDER = {
19587
20562
  };
19588
20563
  function isCodacyAvailable() {
19589
20564
  try {
19590
- (0, import_node_child_process9.execSync)("which codacy-analysis", { stdio: "pipe" });
20565
+ (0, import_node_child_process11.execSync)("which codacy-analysis", { stdio: "pipe" });
19591
20566
  return true;
19592
20567
  } catch {
19593
20568
  return false;
@@ -19623,13 +20598,13 @@ function runCodacyAnalysis(files) {
19623
20598
  if (files.length === 0) return empty;
19624
20599
  const existingFiles = files.filter((f) => {
19625
20600
  try {
19626
- return (0, import_node_fs28.existsSync)(f);
20601
+ return (0, import_node_fs33.existsSync)(f);
19627
20602
  } catch {
19628
20603
  return false;
19629
20604
  }
19630
20605
  });
19631
20606
  if (existingFiles.length === 0) return empty;
19632
- const proc = (0, import_node_child_process9.spawnSync)("codacy-analysis", buildAnalyzerArgv(existingFiles), {
20607
+ const proc = (0, import_node_child_process11.spawnSync)("codacy-analysis", buildAnalyzerArgv(existingFiles), {
19633
20608
  encoding: "utf-8",
19634
20609
  maxBuffer: 10 * 1024 * 1024
19635
20610
  });
@@ -19892,8 +20867,8 @@ async function scope(run2) {
19892
20867
  }
19893
20868
 
19894
20869
  // src/lib/specs.ts
19895
- var import_node_fs29 = require("node:fs");
19896
- var import_node_path20 = require("node:path");
20870
+ var import_node_fs34 = require("node:fs");
20871
+ var import_node_path23 = require("node:path");
19897
20872
  var SPEC_CANDIDATES = [
19898
20873
  "CLAUDE.md",
19899
20874
  "AGENTS.md",
@@ -19924,16 +20899,16 @@ function discoverSpecs(consulted = []) {
19924
20899
  const totalCap = relevant ? MAX_TOTAL_SPEC_BYTES : UNCONSULTED_TOTAL_BYTES;
19925
20900
  if (totalBytes >= totalCap) return false;
19926
20901
  if (seen.has(specPath)) return true;
19927
- if (!(0, import_node_fs29.existsSync)(specPath)) return true;
20902
+ if (!(0, import_node_fs34.existsSync)(specPath)) return true;
19928
20903
  seen.add(specPath);
19929
20904
  const remaining = totalCap - totalBytes;
19930
20905
  const fileCap = relevant ? MAX_SPEC_FILE_BYTES : UNCONSULTED_FILE_BYTES;
19931
20906
  const readBytes = Math.min(fileCap, remaining);
19932
20907
  try {
19933
20908
  const buf = Buffer.alloc(readBytes);
19934
- const fd = (0, import_node_fs29.openSync)(specPath, "r");
19935
- const bytesRead = (0, import_node_fs29.readSync)(fd, buf, 0, readBytes, 0);
19936
- (0, import_node_fs29.closeSync)(fd);
20909
+ const fd = (0, import_node_fs34.openSync)(specPath, "r");
20910
+ const bytesRead = (0, import_node_fs34.readSync)(fd, buf, 0, readBytes, 0);
20911
+ (0, import_node_fs34.closeSync)(fd);
19937
20912
  const content = buf.slice(0, bytesRead).toString("utf-8");
19938
20913
  if (!content) return true;
19939
20914
  result.push({ path: specPath, content });
@@ -19949,7 +20924,7 @@ function discoverSpecs(consulted = []) {
19949
20924
  if (!addSpec(candidate)) break;
19950
20925
  }
19951
20926
  for (const dir of ["spec", "docs"]) {
19952
- if (!(0, import_node_fs29.existsSync)(dir)) continue;
20927
+ if (!(0, import_node_fs34.existsSync)(dir)) continue;
19953
20928
  try {
19954
20929
  const mdFiles = findMdFiles(dir, 2).sort();
19955
20930
  for (const mdFile of mdFiles) {
@@ -19964,9 +20939,9 @@ function findMdFiles(dir, maxDepth, depth = 0) {
19964
20939
  if (depth >= maxDepth) return [];
19965
20940
  const result = [];
19966
20941
  try {
19967
- const entries = (0, import_node_fs29.readdirSync)(dir, { withFileTypes: true });
20942
+ const entries = (0, import_node_fs34.readdirSync)(dir, { withFileTypes: true });
19968
20943
  for (const entry of entries) {
19969
- const fullPath = (0, import_node_path20.join)(dir, entry.name);
20944
+ const fullPath = (0, import_node_path23.join)(dir, entry.name);
19970
20945
  if (entry.isFile() && entry.name.endsWith(".md")) {
19971
20946
  result.push(fullPath);
19972
20947
  } else if (entry.isDirectory() && depth < maxDepth - 1) {
@@ -19978,19 +20953,19 @@ function findMdFiles(dir, maxDepth, depth = 0) {
19978
20953
  return result;
19979
20954
  }
19980
20955
  function discoverPlans() {
19981
- const homePlansDir = (0, import_node_path20.join)(process.env.HOME ?? "", ".claude", "plans");
20956
+ const homePlansDir = (0, import_node_path23.join)(process.env.HOME ?? "", ".claude", "plans");
19982
20957
  const localPlansDir = ".claude/plans";
19983
20958
  const candidates = [];
19984
20959
  const seen = /* @__PURE__ */ new Set();
19985
20960
  for (const plansDir of [localPlansDir, homePlansDir]) {
19986
- if (!(0, import_node_fs29.existsSync)(plansDir)) continue;
20961
+ if (!(0, import_node_fs34.existsSync)(plansDir)) continue;
19987
20962
  try {
19988
- for (const f of (0, import_node_fs29.readdirSync)(plansDir)) {
20963
+ for (const f of (0, import_node_fs34.readdirSync)(plansDir)) {
19989
20964
  if (!f.endsWith(".md") || seen.has(f)) continue;
19990
20965
  seen.add(f);
19991
- const fullPath = (0, import_node_path20.join)(plansDir, f);
20966
+ const fullPath = (0, import_node_path23.join)(plansDir, f);
19992
20967
  try {
19993
- const stat3 = (0, import_node_fs29.statSync)(fullPath);
20968
+ const stat3 = (0, import_node_fs34.statSync)(fullPath);
19994
20969
  candidates.push({ name: f, path: fullPath, mtime: stat3.mtimeMs, size: stat3.size });
19995
20970
  } catch {
19996
20971
  }
@@ -20003,7 +20978,7 @@ function discoverPlans() {
20003
20978
  for (const entry of candidates.slice(0, MAX_PLAN_FILES)) {
20004
20979
  if (entry.size > MAX_PLAN_FILE_BYTES) continue;
20005
20980
  try {
20006
- const content = (0, import_node_fs29.readFileSync)(entry.path, "utf-8");
20981
+ const content = (0, import_node_fs34.readFileSync)(entry.path, "utf-8");
20007
20982
  result.push({ name: entry.name, content });
20008
20983
  } catch {
20009
20984
  }
@@ -20018,12 +20993,12 @@ function discoverGuardDocs(rangeFiles2) {
20018
20993
  if (result.length >= MAX_SPEC_FILES) break;
20019
20994
  if (!GUARD_DOC_EXT.test(path)) continue;
20020
20995
  if (path.startsWith("/") || path.includes("..")) continue;
20021
- if (!(0, import_node_fs29.existsSync)(path)) continue;
20996
+ if (!(0, import_node_fs34.existsSync)(path)) continue;
20022
20997
  try {
20023
- const stat3 = (0, import_node_fs29.statSync)(path);
20998
+ const stat3 = (0, import_node_fs34.statSync)(path);
20024
20999
  if (stat3.size > MAX_PLAN_FILE_BYTES) continue;
20025
21000
  if (totalBytes + stat3.size > MAX_TOTAL_SPEC_BYTES) continue;
20026
- const content = (0, import_node_fs29.readFileSync)(path, "utf-8");
21001
+ const content = (0, import_node_fs34.readFileSync)(path, "utf-8");
20027
21002
  if (!content) continue;
20028
21003
  result.push({ name: path, content });
20029
21004
  totalBytes += content.length;
@@ -20199,8 +21174,8 @@ async function mode(run2) {
20199
21174
  }
20200
21175
 
20201
21176
  // src/lib/fold.ts
20202
- var import_node_fs30 = require("node:fs");
20203
- var import_node_path21 = require("node:path");
21177
+ var import_node_fs35 = require("node:fs");
21178
+ var import_node_path24 = require("node:path");
20204
21179
  var KNOWN_RECORD_TYPES = /* @__PURE__ */ new Set([
20205
21180
  "user",
20206
21181
  "assistant",
@@ -20337,7 +21312,7 @@ function candidateRoots(repoRoot2) {
20337
21312
  const norm = repoRoot2.replace(/\\/g, "/").replace(/\/+$/, "");
20338
21313
  const out = [norm];
20339
21314
  try {
20340
- const real = import_node_fs30.realpathSync.native(norm).replace(/\\/g, "/").replace(/\/+$/, "");
21315
+ const real = import_node_fs35.realpathSync.native(norm).replace(/\\/g, "/").replace(/\/+$/, "");
20341
21316
  if (real !== norm) out.push(real);
20342
21317
  } catch {
20343
21318
  }
@@ -20425,31 +21400,31 @@ function fold(transcriptPath, opts = {}) {
20425
21400
  }
20426
21401
  };
20427
21402
  try {
20428
- if (!(0, import_node_fs30.existsSync)(transcriptPath)) return result;
20429
- ingest((0, import_node_fs30.readFileSync)(transcriptPath, "utf8"), "agent");
21403
+ if (!(0, import_node_fs35.existsSync)(transcriptPath)) return result;
21404
+ ingest((0, import_node_fs35.readFileSync)(transcriptPath, "utf8"), "agent");
20430
21405
  result.coverage.complete = true;
20431
21406
  } catch {
20432
21407
  return result;
20433
21408
  }
20434
21409
  try {
20435
- const sidecarDir = (0, import_node_path21.join)(
20436
- (0, import_node_path21.dirname)(transcriptPath),
20437
- (0, import_node_path21.basename)(transcriptPath).replace(/\.jsonl$/, ""),
21410
+ const sidecarDir = (0, import_node_path24.join)(
21411
+ (0, import_node_path24.dirname)(transcriptPath),
21412
+ (0, import_node_path24.basename)(transcriptPath).replace(/\.jsonl$/, ""),
20438
21413
  "subagents"
20439
21414
  );
20440
- if ((0, import_node_fs30.existsSync)(sidecarDir)) {
21415
+ if ((0, import_node_fs35.existsSync)(sidecarDir)) {
20441
21416
  const maxFiles = opts.maxSidecars ?? 200;
20442
21417
  const maxBytes = opts.maxSidecarBytes ?? 16 * 1024 * 1024;
20443
21418
  const found = [];
20444
- const walk = (d, depth) => {
21419
+ const walk2 = (d, depth) => {
20445
21420
  if (depth > 4) return;
20446
- for (const e of (0, import_node_fs30.readdirSync)(d, { withFileTypes: true })) {
20447
- const p = (0, import_node_path21.join)(d, e.name);
21421
+ for (const e of (0, import_node_fs35.readdirSync)(d, { withFileTypes: true })) {
21422
+ const p = (0, import_node_path24.join)(d, e.name);
20448
21423
  if (e.isDirectory()) {
20449
- walk(p, depth + 1);
21424
+ walk2(p, depth + 1);
20450
21425
  } else if (e.name.startsWith("agent-") && e.name.endsWith(".jsonl")) {
20451
21426
  try {
20452
- const st = (0, import_node_fs30.statSync)(p);
21427
+ const st = (0, import_node_fs35.statSync)(p);
20453
21428
  found.push({ path: p, size: st.size, mtimeMs: st.mtimeMs });
20454
21429
  } catch {
20455
21430
  result.coverage.malformed++;
@@ -20457,7 +21432,7 @@ function fold(transcriptPath, opts = {}) {
20457
21432
  }
20458
21433
  }
20459
21434
  };
20460
- walk(sidecarDir, 0);
21435
+ walk2(sidecarDir, 0);
20461
21436
  found.sort((a, b) => b.mtimeMs - a.mtimeMs);
20462
21437
  let bytes = 0;
20463
21438
  for (const f of found) {
@@ -20466,7 +21441,7 @@ function fold(transcriptPath, opts = {}) {
20466
21441
  continue;
20467
21442
  }
20468
21443
  try {
20469
- ingest((0, import_node_fs30.readFileSync)(f.path, "utf8"), "subagent");
21444
+ ingest((0, import_node_fs35.readFileSync)(f.path, "utf8"), "subagent");
20470
21445
  bytes += f.size;
20471
21446
  result.coverage.subagentFiles++;
20472
21447
  } catch {
@@ -20501,7 +21476,7 @@ function fold(transcriptPath, opts = {}) {
20501
21476
  }
20502
21477
  function classifyUnobserved(path) {
20503
21478
  try {
20504
- const st = (0, import_node_fs30.statSync)(path);
21479
+ const st = (0, import_node_fs35.statSync)(path);
20505
21480
  if (!st.isFile()) return "unreadable";
20506
21481
  } catch {
20507
21482
  return "unreadable";
@@ -20818,20 +21793,20 @@ async function evidence(run2) {
20818
21793
  }
20819
21794
 
20820
21795
  // src/lib/cache-cleanup.ts
20821
- var import_node_fs31 = require("node:fs");
20822
- var import_node_path22 = require("node:path");
21796
+ var import_node_fs36 = require("node:fs");
21797
+ var import_node_path25 = require("node:path");
20823
21798
  var CACHE_TTL_DAYS = 7;
20824
21799
  function pruneStaleCache() {
20825
21800
  try {
20826
21801
  const dir = projectPath(CACHE_DIR);
20827
21802
  const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
20828
- for (const entry of (0, import_node_fs31.readdirSync)(dir)) {
21803
+ for (const entry of (0, import_node_fs36.readdirSync)(dir)) {
20829
21804
  if (!entry.startsWith("pending-")) continue;
20830
- const path = (0, import_node_path22.join)(dir, entry);
21805
+ const path = (0, import_node_path25.join)(dir, entry);
20831
21806
  try {
20832
- const stat3 = (0, import_node_fs31.statSync)(path);
21807
+ const stat3 = (0, import_node_fs36.statSync)(path);
20833
21808
  if (stat3.mtimeMs < cutoff) {
20834
- (0, import_node_fs31.unlinkSync)(path);
21809
+ (0, import_node_fs36.unlinkSync)(path);
20835
21810
  logEvent("cache_entry_pruned", {
20836
21811
  path: entry,
20837
21812
  age_days: Math.round((Date.now() - stat3.mtimeMs) / 864e5)
@@ -20845,7 +21820,7 @@ function pruneStaleCache() {
20845
21820
  }
20846
21821
 
20847
21822
  // src/lib/context-files.ts
20848
- var import_node_fs32 = require("node:fs");
21823
+ var import_node_fs37 = require("node:fs");
20849
21824
  var import_node_os5 = require("node:os");
20850
21825
  var MAX_CONTEXT_FILES = 10;
20851
21826
  var MAX_CONTEXT_FILE_BYTES = 10240;
@@ -20903,7 +21878,7 @@ function gatherContextFiles(contextPaths, deltaFiles, opts) {
20903
21878
  continue;
20904
21879
  }
20905
21880
  try {
20906
- const content = (0, import_node_fs32.readFileSync)(safePath, "utf8");
21881
+ const content = (0, import_node_fs37.readFileSync)(safePath, "utf8");
20907
21882
  const bytes = Buffer.byteLength(content);
20908
21883
  if (bytes > MAX_CONTEXT_FILE_BYTES) {
20909
21884
  logEvent("context_file_skipped", { path: filePath, reason: "too_large", bytes });
@@ -20953,7 +21928,7 @@ async function contextFiles(run2) {
20953
21928
  const readSet = readSetContextPaths(run2.actionSummary, codeDelta.files);
20954
21929
  const { kept: externalContext } = partitionVerityOwned(readSet);
20955
21930
  const ig = loadVerityIgnore();
20956
- const unfenced = run2.verityIgnored.suspended ? externalContext : externalContext.filter((p) => !isIgnored(ig, p));
21931
+ const unfenced = run2.verityIgnored.suspended ? externalContext : externalContext.filter((p) => !isIgnored2(ig, p));
20957
21932
  const contextFiles2 = gatherContextFiles(unfenced, codeDelta.files, {
20958
21933
  maxFiles: MAX_FILES - codeDelta.files.length
20959
21934
  });
@@ -20974,7 +21949,7 @@ async function repoContext(run2) {
20974
21949
  const deltaFiles = codeDelta.files.filter((f) => f.role !== "context");
20975
21950
  const sentPaths = new Set(codeDelta.files.map((f) => f.path));
20976
21951
  const ig = loadVerityIgnore();
20977
- const isExcluded = (p) => isVerityOwnedPath(p) || !run2.verityIgnored.suspended && isIgnored(ig, p);
21952
+ const isExcluded = (p) => isVerityOwnedPath(p) || !run2.verityIgnored.suspended && isIgnored2(ig, p);
20978
21953
  run2.repoContext = buildRepoContext({
20979
21954
  deltaFiles,
20980
21955
  diffs: snapshotResult.diffs,
@@ -20993,10 +21968,10 @@ async function repoContext(run2) {
20993
21968
  }
20994
21969
 
20995
21970
  // src/lib/seed-runner.ts
20996
- var import_promises11 = require("node:fs/promises");
20997
- var import_node_fs33 = require("node:fs");
20998
- var import_node_path23 = require("node:path");
20999
- var import_yaml2 = __toESM(require_dist());
21971
+ var import_promises14 = require("node:fs/promises");
21972
+ var import_node_fs38 = require("node:fs");
21973
+ var import_node_path26 = require("node:path");
21974
+ var import_yaml4 = __toESM(require_dist());
21000
21975
 
21001
21976
  // src/lib/seed.ts
21002
21977
  function extractReadmeIntro(content) {
@@ -21234,29 +22209,29 @@ function renderNodeMarkdown(candidate, nodeId, createdAt) {
21234
22209
  return fm;
21235
22210
  }
21236
22211
  async function runSeed(opts) {
21237
- if (!(0, import_node_fs33.existsSync)(STANDARD_FILE)) {
22212
+ if (!(0, import_node_fs38.existsSync)(STANDARD_FILE)) {
21238
22213
  return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
21239
22214
  }
21240
22215
  let standardDoc;
21241
22216
  try {
21242
- const raw = await (0, import_promises11.readFile)(STANDARD_FILE, "utf-8");
21243
- standardDoc = (0, import_yaml2.parse)(raw);
22217
+ const raw = await (0, import_promises14.readFile)(STANDARD_FILE, "utf-8");
22218
+ standardDoc = (0, import_yaml4.parse)(raw);
21244
22219
  } catch {
21245
22220
  return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
21246
22221
  }
21247
22222
  const knowledgeSpec = standardDoc.knowledge_spec ?? {};
21248
22223
  let readmeContent;
21249
- if ((0, import_node_fs33.existsSync)("README.md")) {
22224
+ if ((0, import_node_fs38.existsSync)("README.md")) {
21250
22225
  try {
21251
- readmeContent = await (0, import_promises11.readFile)("README.md", "utf-8");
22226
+ readmeContent = await (0, import_promises14.readFile)("README.md", "utf-8");
21252
22227
  } catch {
21253
22228
  }
21254
22229
  }
21255
22230
  let claudeMdContent;
21256
22231
  for (const p of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
21257
- if ((0, import_node_fs33.existsSync)(p)) {
22232
+ if ((0, import_node_fs38.existsSync)(p)) {
21258
22233
  try {
21259
- claudeMdContent = await (0, import_promises11.readFile)(p, "utf-8");
22234
+ claudeMdContent = await (0, import_promises14.readFile)(p, "utf-8");
21260
22235
  break;
21261
22236
  } catch {
21262
22237
  }
@@ -21277,8 +22252,8 @@ async function runSeed(opts) {
21277
22252
  if (candidates.length === 0) {
21278
22253
  return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
21279
22254
  }
21280
- const overviewPath = (0, import_node_path23.join)(MEMORY_DIR, "domain", "project-overview.md");
21281
- if ((0, import_node_fs33.existsSync)(overviewPath) && !opts.force) {
22255
+ const overviewPath = (0, import_node_path26.join)(MEMORY_DIR, "domain", "project-overview.md");
22256
+ if ((0, import_node_fs38.existsSync)(overviewPath) && !opts.force) {
21282
22257
  return { created: 0, failed: 0, skipped: "already_seeded", candidates };
21283
22258
  }
21284
22259
  if (opts.dryRun) {
@@ -21320,8 +22295,8 @@ async function runSeed(opts) {
21320
22295
  continue;
21321
22296
  }
21322
22297
  try {
21323
- await (0, import_promises11.mkdir)((0, import_node_path23.dirname)(targetPath), { recursive: true });
21324
- await (0, import_promises11.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
22298
+ await (0, import_promises14.mkdir)((0, import_node_path26.dirname)(targetPath), { recursive: true });
22299
+ await (0, import_promises14.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
21325
22300
  created++;
21326
22301
  opts.onCreated?.(nodeId, filePathRel, c);
21327
22302
  } catch (err) {
@@ -21333,8 +22308,8 @@ async function runSeed(opts) {
21333
22308
  }
21334
22309
 
21335
22310
  // src/commands/analyze/phases/08-memory-manifest.ts
21336
- var import_node_fs34 = require("node:fs");
21337
- var import_node_path24 = require("node:path");
22311
+ var import_node_fs39 = require("node:fs");
22312
+ var import_node_path27 = require("node:path");
21338
22313
  async function memoryManifest(run2) {
21339
22314
  const { globals } = run2;
21340
22315
  const { serviceUrl, token } = run2;
@@ -21344,9 +22319,9 @@ async function memoryManifest(run2) {
21344
22319
  let autoSeedNotice = null;
21345
22320
  try {
21346
22321
  await ensureMemoryDir();
21347
- const seedMarker = (0, import_node_path24.join)(VERITY_DIR, ".seeded");
21348
- const hasStandard = (0, import_node_fs34.existsSync)(STANDARD_FILE);
21349
- const alreadyTried = (0, import_node_fs34.existsSync)(seedMarker);
22322
+ const seedMarker = (0, import_node_path27.join)(VERITY_DIR, ".seeded");
22323
+ const hasStandard = (0, import_node_fs39.existsSync)(STANDARD_FILE);
22324
+ const alreadyTried = (0, import_node_fs39.existsSync)(seedMarker);
21350
22325
  if (hasStandard && !alreadyTried) {
21351
22326
  const preManifest = await buildManifest();
21352
22327
  if (preManifest.nodes.length === 0) {
@@ -21359,7 +22334,7 @@ async function memoryManifest(run2) {
21359
22334
  dryRun: false
21360
22335
  });
21361
22336
  if (seedResult.created > 0) {
21362
- (0, import_node_fs34.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created}
22337
+ (0, import_node_fs39.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created}
21363
22338
  `);
21364
22339
  autoSeedNotice = `Seeded ${seedResult.created} knowledge node(s) from your existing Standard (one-time).`;
21365
22340
  logEvent("auto_seed_ran", {
@@ -21367,7 +22342,7 @@ async function memoryManifest(run2) {
21367
22342
  failed: seedResult.failed
21368
22343
  });
21369
22344
  } else if (seedResult.skipped === "already_seeded") {
21370
- (0, import_node_fs34.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} skipped=already_seeded
22345
+ (0, import_node_fs39.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} skipped=already_seeded
21371
22346
  `);
21372
22347
  } else {
21373
22348
  logEvent("auto_seed_noop", {
@@ -21462,7 +22437,7 @@ function computeIncrement(reviewedPaths, hashOf, priorAuthored) {
21462
22437
  }
21463
22438
 
21464
22439
  // src/commands/analyze/phases/10-working-memory.ts
21465
- var import_node_path25 = require("node:path");
22440
+ var import_node_path28 = require("node:path");
21466
22441
  async function workingMemory(run2) {
21467
22442
  const { opts } = run2;
21468
22443
  const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } = run2;
@@ -21474,7 +22449,7 @@ async function workingMemory(run2) {
21474
22449
  const priorState = foldForMarks(memorySession.d);
21475
22450
  incrementReport = computeIncrement(
21476
22451
  allForReview,
21477
- (p) => fileHash((0, import_node_path25.join)(repoRoot(), p)),
22452
+ (p) => fileHash((0, import_node_path28.join)(repoRoot(), p)),
21478
22453
  priorState.authored_all.map((a) => ({
21479
22454
  path: a.path,
21480
22455
  hash_at_last_verdict: a.hash_at_last_verdict
@@ -21556,7 +22531,7 @@ async function workingMemory(run2) {
21556
22531
  }
21557
22532
 
21558
22533
  // src/lib/note-budget.ts
21559
- var import_node_fs35 = require("node:fs");
22534
+ var import_node_fs40 = require("node:fs");
21560
22535
  var ADVISORY_BUDGET = { PASS: 1, WARN: 2 };
21561
22536
  var EPISODE_STALE_SECONDS = 30 * 60;
21562
22537
  var FRESH = { delivered: 0, tasksCompleted: 0, ts: 0 };
@@ -21578,9 +22553,9 @@ function advisoryBudgetSpent(episode, rawDecision) {
21578
22553
  }
21579
22554
  function readAdvisoryEpisode(sessionId) {
21580
22555
  const file = scopedFile(ADVISORY_EPISODE_FILE, sessionId);
21581
- if (!(0, import_node_fs35.existsSync)(file)) return null;
22556
+ if (!(0, import_node_fs40.existsSync)(file)) return null;
21582
22557
  try {
21583
- const o = JSON.parse((0, import_node_fs35.readFileSync)(file, "utf-8")) ?? {};
22558
+ const o = JSON.parse((0, import_node_fs40.readFileSync)(file, "utf-8")) ?? {};
21584
22559
  const delivered = typeof o.delivered === "number" ? o.delivered : NaN;
21585
22560
  if (isNaN(delivered)) return null;
21586
22561
  return {
@@ -21594,8 +22569,8 @@ function readAdvisoryEpisode(sessionId) {
21594
22569
  }
21595
22570
  function writeAdvisoryEpisode(episode, sessionId) {
21596
22571
  try {
21597
- (0, import_node_fs35.mkdirSync)(VERITY_DIR, { recursive: true });
21598
- (0, import_node_fs35.writeFileSync)(
22572
+ (0, import_node_fs40.mkdirSync)(VERITY_DIR, { recursive: true });
22573
+ (0, import_node_fs40.writeFileSync)(
21599
22574
  scopedFile(ADVISORY_EPISODE_FILE, sessionId),
21600
22575
  JSON.stringify({ v: 1, ...episode })
21601
22576
  );
@@ -21625,7 +22600,7 @@ function isExplicitlyAutonomous(env = process.env) {
21625
22600
  }
21626
22601
 
21627
22602
  // src/lib/task-context.ts
21628
- var import_node_child_process10 = require("node:child_process");
22603
+ var import_node_child_process12 = require("node:child_process");
21629
22604
  var CLOSING_RE = /\b(close[sd]?|fix(?:e[sd])?|resolve[sd]?)\b[\s:]*#(\d+)/i;
21630
22605
  var BRANCH_RE = /(?:^|[/_-])(?:issue|gh|fix)[-_/]?(\d+)\b/i;
21631
22606
  function parseLinkedIssue(sources) {
@@ -21641,7 +22616,7 @@ function parseLinkedIssue(sources) {
21641
22616
  }
21642
22617
  function safeExec(cmd, timeout) {
21643
22618
  try {
21644
- return (0, import_node_child_process10.execSync)(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout }).trim();
22619
+ return (0, import_node_child_process12.execSync)(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout }).trim();
21645
22620
  } catch {
21646
22621
  return "";
21647
22622
  }
@@ -21912,14 +22887,14 @@ async function buildRequest(run2) {
21912
22887
  }
21913
22888
 
21914
22889
  // src/lib/offline.ts
21915
- var import_node_fs36 = require("node:fs");
22890
+ var import_node_fs41 = require("node:fs");
21916
22891
  var import_node_crypto11 = require("node:crypto");
21917
22892
  function cacheRequest(body) {
21918
22893
  try {
21919
- (0, import_node_fs36.mkdirSync)(CACHE_DIR, { recursive: true });
22894
+ (0, import_node_fs41.mkdirSync)(CACHE_DIR, { recursive: true });
21920
22895
  const suffix = (0, import_node_crypto11.randomBytes)(4).toString("hex");
21921
22896
  const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
21922
- (0, import_node_fs36.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(redactRequest(body)));
22897
+ (0, import_node_fs41.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(redactRequest(body)));
21923
22898
  } catch {
21924
22899
  }
21925
22900
  }
@@ -22038,8 +23013,8 @@ async function transmit(run2) {
22038
23013
  }
22039
23014
 
22040
23015
  // src/commands/analyze/phases/13-reconcile.ts
22041
- var import_node_fs37 = require("node:fs");
22042
- var import_node_path26 = require("node:path");
23016
+ var import_node_fs42 = require("node:fs");
23017
+ var import_node_path29 = require("node:path");
22043
23018
  async function reconcile(run2) {
22044
23019
  const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run2;
22045
23020
  const sentPaths = codeDelta.files.map((f) => f.path);
@@ -22068,7 +23043,7 @@ async function reconcile(run2) {
22068
23043
  const st = foldDossier(memorySession.d);
22069
23044
  openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line) => {
22070
23045
  try {
22071
- const src = (0, import_node_fs37.readFileSync)((0, import_node_path26.join)(repoRoot(), file), "utf8").split("\n");
23046
+ const src = (0, import_node_fs42.readFileSync)((0, import_node_path29.join)(repoRoot(), file), "utf8").split("\n");
22072
23047
  const at = src[line - 1];
22073
23048
  return at === void 0 ? null : lineSha(at);
22074
23049
  } catch {
@@ -22732,6 +23707,10 @@ function registerAnalyzeCommand(program2) {
22732
23707
  }
22733
23708
  var tracing = () => process.env.VERITY_TRACE_PHASES === "1";
22734
23709
  async function runAnalyze(opts, globals) {
23710
+ if (!verityConfigured()) {
23711
+ (0, import_node_fs43.writeSync)(2, '[verity] not set up in this project \u2014 run "verity init" first.\n');
23712
+ process.exit(0);
23713
+ }
22735
23714
  const run2 = createRun(opts, globals);
22736
23715
  installRunEvidence(run2);
22737
23716
  for (const [name, phase] of PIPELINE) {
@@ -22750,7 +23729,6 @@ async function runAnalyze(opts, globals) {
22750
23729
  }
22751
23730
 
22752
23731
  // src/commands/baseline.ts
22753
- var import_node_fs38 = require("node:fs");
22754
23732
  function registerBaselineCommands(program2) {
22755
23733
  const baseline = program2.command("baseline").description("Manage the task-start working-tree baseline");
22756
23734
  baseline.command("capture").description("Snapshot the working tree at task start (used by SessionStart hook)").option("--session-id <id>", "Session id (overrides any value from stdin)").option("--source <source>", "Lifecycle hint: startup|resume|clear|compact").action(async (opts) => {
@@ -22759,7 +23737,16 @@ function registerBaselineCommands(program2) {
22759
23737
  process.chdir(repoRoot());
22760
23738
  } catch {
22761
23739
  }
22762
- if (!(0, import_node_fs38.existsSync)(VERITY_DIR)) {
23740
+ if (!verityConfigured()) {
23741
+ const how = isPluginInvocation() ? "Offer to run /verity:setup for the user." : "Offer to run `verity init` (or /verity-setup) for the user.";
23742
+ process.stdout.write(
23743
+ JSON.stringify({
23744
+ hookSpecificOutput: {
23745
+ hookEventName: "SessionStart",
23746
+ additionalContext: `Verity is installed but this project is not set up yet, so the quality gate will not review anything here. ${how}`
23747
+ }
23748
+ }) + "\n"
23749
+ );
22763
23750
  process.exit(0);
22764
23751
  }
22765
23752
  let sessionId = opts.sessionId;
@@ -22802,7 +23789,7 @@ async function readStdin() {
22802
23789
  }
22803
23790
 
22804
23791
  // src/commands/review.ts
22805
- var import_node_fs39 = require("node:fs");
23792
+ var import_node_fs44 = require("node:fs");
22806
23793
  function registerReviewCommand(program2) {
22807
23794
  program2.command("review").description("Run on-demand Verity analysis (advisory, never blocks)").requiredOption("--files <paths>", "Comma-separated file list").option("--changed <paths>", "Subset of --files that were modified").option("--intent <text>", "User intent description (max 2000 chars)").option("--specs <paths>", "Comma-separated spec file paths").option("--json", "Output raw JSON response").action(async (opts) => {
22808
23795
  const globals = program2.opts();
@@ -22821,7 +23808,7 @@ async function runReview(opts, globals) {
22821
23808
  const securityFiles = filterSecurity(allFiles);
22822
23809
  let staticResults;
22823
23810
  if (isCodacyAvailable()) {
22824
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs39.existsSync)(f) || resolveFile(f) !== null);
23811
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs44.existsSync)(f) || resolveFile(f) !== null);
22825
23812
  staticResults = runCodacyAnalysis(scannable);
22826
23813
  } else {
22827
23814
  staticResults = {
@@ -22847,10 +23834,10 @@ async function runReview(opts, globals) {
22847
23834
  const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
22848
23835
  specs = [];
22849
23836
  for (const p of specPaths) {
22850
- if (!(0, import_node_fs39.existsSync)(p)) continue;
23837
+ if (!(0, import_node_fs44.existsSync)(p)) continue;
22851
23838
  try {
22852
- const { readFileSync: readFileSync26 } = await import("node:fs");
22853
- const content = readFileSync26(p, "utf-8");
23839
+ const { readFileSync: readFileSync27 } = await import("node:fs");
23840
+ const content = readFileSync27(p, "utf-8");
22854
23841
  specs.push({ path: p, content: content.slice(0, 10240) });
22855
23842
  } catch {
22856
23843
  }
@@ -22907,10 +23894,10 @@ async function runReview(opts, globals) {
22907
23894
  }
22908
23895
 
22909
23896
  // src/commands/guard.ts
22910
- var import_node_fs40 = require("node:fs");
22911
- var import_node_path27 = require("node:path");
23897
+ var import_node_fs45 = require("node:fs");
23898
+ var import_node_path30 = require("node:path");
22912
23899
  var GUARD_BLOCK_CAP = 2;
22913
- var GUARD_ITER_FILE = (0, import_node_path27.join)(VERITY_DIR, ".guard-iteration");
23900
+ var GUARD_ITER_FILE = (0, import_node_path30.join)(VERITY_DIR, ".guard-iteration");
22914
23901
  function readPreToolUseStdin() {
22915
23902
  const empty = { command: "", cwd: null, sessionId: null };
22916
23903
  return new Promise((resolve4) => {
@@ -22955,7 +23942,7 @@ function readPreToolUseStdin() {
22955
23942
  }
22956
23943
  function readIterMap() {
22957
23944
  try {
22958
- const raw = JSON.parse((0, import_node_fs40.readFileSync)(GUARD_ITER_FILE, "utf-8"));
23945
+ const raw = JSON.parse((0, import_node_fs45.readFileSync)(GUARD_ITER_FILE, "utf-8"));
22959
23946
  if (raw && typeof raw === "object") {
22960
23947
  if (typeof raw.moment === "string" && typeof raw.count === "number") {
22961
23948
  return { [raw.moment]: raw.count };
@@ -22975,10 +23962,10 @@ function readIter(moment) {
22975
23962
  }
22976
23963
  function writeIter(moment, count) {
22977
23964
  try {
22978
- (0, import_node_fs40.mkdirSync)(VERITY_DIR, { recursive: true });
23965
+ (0, import_node_fs45.mkdirSync)(VERITY_DIR, { recursive: true });
22979
23966
  const map = readIterMap();
22980
23967
  map[moment] = count;
22981
- (0, import_node_fs40.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
23968
+ (0, import_node_fs45.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
22982
23969
  } catch {
22983
23970
  }
22984
23971
  }
@@ -22988,10 +23975,10 @@ function resetIter(moment) {
22988
23975
  if (!(moment in map)) return;
22989
23976
  delete map[moment];
22990
23977
  if (Object.keys(map).length === 0) {
22991
- if ((0, import_node_fs40.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs40.unlinkSync)(GUARD_ITER_FILE);
23978
+ if ((0, import_node_fs45.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs45.unlinkSync)(GUARD_ITER_FILE);
22992
23979
  } else {
22993
- (0, import_node_fs40.mkdirSync)(VERITY_DIR, { recursive: true });
22994
- (0, import_node_fs40.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
23980
+ (0, import_node_fs45.mkdirSync)(VERITY_DIR, { recursive: true });
23981
+ (0, import_node_fs45.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
22995
23982
  }
22996
23983
  } catch {
22997
23984
  }
@@ -23063,7 +24050,7 @@ function buildGuardRequest(moment, files, codeDelta, iter, sessionId, statedInte
23063
24050
  const securityFiles = filterSecurity(files);
23064
24051
  let staticResults;
23065
24052
  if (isCodacyAvailable()) {
23066
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs40.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
24053
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs45.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
23067
24054
  staticResults = runCodacyAnalysis(scannable);
23068
24055
  } else {
23069
24056
  staticResults = { tool: "@codacy/analysis-cli", findings: [], summary: { total_findings: 0, by_severity: {}, tools_run: [] } };
@@ -23196,13 +24183,13 @@ async function runGuard(opts, globals) {
23196
24183
  diffs: [],
23197
24184
  signalsByPath: rangeChangeSignals(frame, range, codeDelta.files.map((f) => f.path)),
23198
24185
  sentPaths: new Set(codeDelta.files.map((f) => f.path)),
23199
- isExcluded: (p) => isVerityOwnedPath(p) || isIgnored(ig, p),
24186
+ isExcluded: (p) => isVerityOwnedPath(p) || isIgnored2(ig, p),
23200
24187
  cwd: frame.worktreeRoot ?? process.cwd()
23201
24188
  });
23202
24189
  upgradeToExcerpts(repoContext2, {
23203
24190
  readFile: (rel) => {
23204
24191
  try {
23205
- return (0, import_node_fs40.readFileSync)((0, import_node_path27.join)(frame.worktreeRoot ?? process.cwd(), rel), "utf8");
24192
+ return (0, import_node_fs45.readFileSync)((0, import_node_path30.join)(frame.worktreeRoot ?? process.cwd(), rel), "utf8");
23206
24193
  } catch {
23207
24194
  return null;
23208
24195
  }
@@ -23423,7 +24410,7 @@ function registerIgnoreCommand(program2) {
23423
24410
 
23424
24411
  // src/commands/waive.ts
23425
24412
  var import_node_crypto12 = require("node:crypto");
23426
- var import_node_fs41 = require("node:fs");
24413
+ var import_node_fs46 = require("node:fs");
23427
24414
  function registerWaiveCommand(program2) {
23428
24415
  program2.command("waive <pattern-id>").description("Record an accepted-risk disposition for an open finding (voids when the file changes)").option("--file <path>", "File the finding is anchored to, REPO-RELATIVE (recommended \u2014 narrows the waive)").requiredOption("--reason <text>", "The human disposition this records (reviewer finding, ADR, \u2026)").action(async (patternId, opts) => {
23429
24416
  const globals = program2.opts();
@@ -23452,7 +24439,7 @@ function registerWaiveCommand(program2) {
23452
24439
  if (opts.file) {
23453
24440
  body.file = opts.file;
23454
24441
  try {
23455
- body.file_sha256 = (0, import_node_crypto12.createHash)("sha256").update((0, import_node_fs41.readFileSync)(opts.file)).digest("hex");
24442
+ body.file_sha256 = (0, import_node_crypto12.createHash)("sha256").update((0, import_node_fs46.readFileSync)(opts.file)).digest("hex");
23456
24443
  } catch {
23457
24444
  printError(`Cannot read ${opts.file} \u2014 run from the repo root, or omit --file to waive by pattern.`);
23458
24445
  process.exit(1);
@@ -23477,10 +24464,11 @@ function registerWaiveCommand(program2) {
23477
24464
  }
23478
24465
 
23479
24466
  // src/commands/init.ts
23480
- var import_node_fs46 = require("node:fs");
23481
- var import_promises14 = require("node:fs/promises");
23482
- var import_node_path29 = require("node:path");
23483
- var import_node_child_process14 = require("node:child_process");
24467
+ var import_node_fs51 = require("node:fs");
24468
+ var import_promises17 = require("node:fs/promises");
24469
+ var import_yaml6 = __toESM(require_dist());
24470
+ var import_node_path33 = require("node:path");
24471
+ var import_node_child_process16 = require("node:child_process");
23484
24472
 
23485
24473
  // src/lib/banner.ts
23486
24474
  var WORDMARK = [
@@ -23558,14 +24546,14 @@ function printPhase(n, of, title, subtitle) {
23558
24546
  }
23559
24547
 
23560
24548
  // src/commands/doctor.ts
23561
- var import_node_fs44 = require("node:fs");
24549
+ var import_node_fs48 = require("node:fs");
23562
24550
 
23563
24551
  // src/lib/prereqs.ts
23564
- var import_node_child_process11 = require("node:child_process");
24552
+ var import_node_child_process13 = require("node:child_process");
23565
24553
  var MIN_NODE_MAJOR = 20;
23566
24554
  function which(bin) {
23567
24555
  try {
23568
- const out = (0, import_node_child_process11.execSync)(`command -v ${bin}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
24556
+ const out = (0, import_node_child_process13.execSync)(`command -v ${bin}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
23569
24557
  return out || null;
23570
24558
  } catch {
23571
24559
  return null;
@@ -23587,7 +24575,7 @@ function checkNode() {
23587
24575
  function checkGit() {
23588
24576
  let detail = "";
23589
24577
  try {
23590
- detail = (0, import_node_child_process11.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
24578
+ detail = (0, import_node_child_process13.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
23591
24579
  } catch {
23592
24580
  return {
23593
24581
  id: "git",
@@ -23625,7 +24613,7 @@ function checkAnalysisCli() {
23625
24613
  var INSTALL_TIMEOUT_MS = 12e4;
23626
24614
  function run(command, args, opts = {}) {
23627
24615
  return new Promise((resolve4) => {
23628
- const child = (0, import_node_child_process11.spawn)(command, args, {
24616
+ const child = (0, import_node_child_process13.spawn)(command, args, {
23629
24617
  stdio: opts.inherit ? "inherit" : "pipe",
23630
24618
  timeout: INSTALL_TIMEOUT_MS
23631
24619
  });
@@ -23674,11 +24662,11 @@ async function checkPrereqs(opts = {}) {
23674
24662
  }
23675
24663
 
23676
24664
  // src/lib/telemetry.ts
23677
- var import_promises12 = require("node:fs/promises");
24665
+ var import_promises15 = require("node:fs/promises");
23678
24666
 
23679
24667
  // src/lib/gitignore.ts
23680
- var import_node_child_process12 = require("node:child_process");
23681
- var import_node_fs42 = require("node:fs");
24668
+ var import_node_child_process14 = require("node:child_process");
24669
+ var import_node_fs47 = require("node:fs");
23682
24670
  var VERITY_GITIGNORE_MARKER = "# Verity \u2014 machine-local state.";
23683
24671
  var SETTINGS_LOCAL_IGNORE_ENTRY = ".claude/settings.local.json";
23684
24672
  var VERITY_GITIGNORE_BLOCK = [
@@ -23692,26 +24680,26 @@ var VERITY_GITIGNORE_BLOCK = [
23692
24680
  ""
23693
24681
  ].join("\n");
23694
24682
  var BREAKING_ENTRIES = /* @__PURE__ */ new Set([".verity/", ".verity"]);
23695
- function isIgnored2(path) {
24683
+ function isIgnored3(path) {
23696
24684
  try {
23697
- (0, import_node_child_process12.execSync)(`git check-ignore -q -- "${path}"`, { stdio: "pipe" });
24685
+ (0, import_node_child_process14.execSync)(`git check-ignore -q -- "${path}"`, { stdio: "pipe" });
23698
24686
  return true;
23699
24687
  } catch (err) {
23700
24688
  return err.status === 1 ? false : null;
23701
24689
  }
23702
24690
  }
23703
24691
  function semanticsHold() {
23704
- const snapshot = isIgnored2(".verity/.snapshot/__probe__");
23705
- const standard = isIgnored2(".verity/standard.yaml");
23706
- const node = isIgnored2(".verity/memory/domain/__probe__.md");
23707
- const futureState = isIgnored2(".verity/.__probe-future-state__");
24692
+ const snapshot = isIgnored3(".verity/.snapshot/__probe__");
24693
+ const standard = isIgnored3(".verity/standard.yaml");
24694
+ const node = isIgnored3(".verity/memory/domain/__probe__.md");
24695
+ const futureState = isIgnored3(".verity/.__probe-future-state__");
23708
24696
  if (snapshot === null || standard === null || node === null || futureState === null) return null;
23709
24697
  return snapshot === true && futureState === true && standard === false && node === false;
23710
24698
  }
23711
24699
  function ensureVerityGitignore() {
23712
24700
  let content = "";
23713
24701
  try {
23714
- content = (0, import_node_fs42.readFileSync)(".gitignore", "utf-8");
24702
+ content = (0, import_node_fs47.readFileSync)(".gitignore", "utf-8");
23715
24703
  } catch {
23716
24704
  }
23717
24705
  const hasMarker = content.includes(VERITY_GITIGNORE_MARKER);
@@ -23732,7 +24720,7 @@ function ensureVerityGitignore() {
23732
24720
  const sep2 = next === "" ? "" : next.endsWith("\n") ? "\n" : "\n\n";
23733
24721
  next = next + sep2 + VERITY_GITIGNORE_BLOCK;
23734
24722
  }
23735
- (0, import_node_fs42.writeFileSync)(".gitignore", next);
24723
+ (0, import_node_fs47.writeFileSync)(".gitignore", next);
23736
24724
  return verified(needsRepair ? "repaired" : "added");
23737
24725
  } catch {
23738
24726
  return "failed";
@@ -23741,7 +24729,7 @@ function ensureVerityGitignore() {
23741
24729
  function committedVerityState() {
23742
24730
  let out = "";
23743
24731
  try {
23744
- out = (0, import_node_child_process12.execSync)("git ls-files -z -- .verity", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
24732
+ out = (0, import_node_child_process14.execSync)("git ls-files -z -- .verity", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
23745
24733
  } catch {
23746
24734
  return [];
23747
24735
  }
@@ -23751,10 +24739,10 @@ function untrackVerityState() {
23751
24739
  const tracked = committedVerityState();
23752
24740
  if (tracked.length === 0) return "none";
23753
24741
  try {
23754
- (0, import_node_child_process12.execSync)("git rm -r --cached --quiet -- .verity", { stdio: "pipe" });
24742
+ (0, import_node_child_process14.execSync)("git rm -r --cached --quiet -- .verity", { stdio: "pipe" });
23755
24743
  for (const keep of [".verity/standard.yaml", ".verity/memory"]) {
23756
24744
  try {
23757
- (0, import_node_child_process12.execSync)(`git add -- "${keep}"`, { stdio: "pipe" });
24745
+ (0, import_node_child_process14.execSync)(`git add -- "${keep}"`, { stdio: "pipe" });
23758
24746
  } catch {
23759
24747
  }
23760
24748
  }
@@ -23794,7 +24782,7 @@ function buildTelemetryEnv(serviceUrl) {
23794
24782
  var VERITY_TELEMETRY_KEYS = Object.keys(buildTelemetryEnv(""));
23795
24783
  async function readSettingsLocal() {
23796
24784
  try {
23797
- return JSON.parse(await (0, import_promises12.readFile)(projectPath(SETTINGS_LOCAL_FILE2), "utf-8"));
24785
+ return JSON.parse(await (0, import_promises15.readFile)(projectPath(SETTINGS_LOCAL_FILE2), "utf-8"));
23798
24786
  } catch {
23799
24787
  return {};
23800
24788
  }
@@ -23806,7 +24794,7 @@ async function ensureGitignore() {
23806
24794
  const file = projectPath(GITIGNORE_FILE);
23807
24795
  let content = "";
23808
24796
  try {
23809
- content = await (0, import_promises12.readFile)(file, "utf-8");
24797
+ content = await (0, import_promises15.readFile)(file, "utf-8");
23810
24798
  } catch {
23811
24799
  }
23812
24800
  const lines = content.split("\n").map((l) => l.trim());
@@ -23815,7 +24803,7 @@ async function ensureGitignore() {
23815
24803
  }
23816
24804
  const block = "# Verity telemetry \u2014 machine-local Claude Code settings\n" + GITIGNORE_ENTRY + "\n";
23817
24805
  const next = content ? content + (content.endsWith("\n") ? "" : "\n") + "\n" + block : block;
23818
- await (0, import_promises12.writeFile)(file, next);
24806
+ await (0, import_promises15.writeFile)(file, next);
23819
24807
  }
23820
24808
  async function installTelemetry(serviceUrl) {
23821
24809
  const env = buildTelemetryEnv(serviceUrl);
@@ -23853,50 +24841,49 @@ async function uninstallTelemetry() {
23853
24841
  return { ok: true, data: { removed } };
23854
24842
  }
23855
24843
 
23856
- // src/lib/setup-state.ts
23857
- var import_promises13 = require("node:fs/promises");
23858
- var import_node_fs43 = require("node:fs");
23859
- var SETUP_STATE_FILE = `${VERITY_DIR}/setup.json`;
23860
- async function readSetupState() {
23861
- const path = projectPath(SETUP_STATE_FILE);
23862
- if (!(0, import_node_fs43.existsSync)(path)) return null;
23863
- try {
23864
- const parsed = JSON.parse(await (0, import_promises13.readFile)(path, "utf-8"));
23865
- return parsed && typeof parsed === "object" ? parsed : null;
23866
- } catch {
23867
- return null;
23868
- }
23869
- }
23870
- async function writeSetupState(patch) {
23871
- const current = await readSetupState() ?? { version: 1 };
23872
- const next = { ...current, ...patch, version: 1 };
23873
- await writeJsonFilePreservingStyle(projectPath(SETUP_STATE_FILE), next);
23874
- return next;
23875
- }
23876
-
23877
24844
  // src/commands/doctor.ts
23878
24845
  async function buildReport() {
23879
24846
  const prereqs = await checkPrereqs({ install: false });
23880
24847
  const state = await readSetupState();
23881
24848
  const hooks = await checkAllVerityHooks();
23882
24849
  const telemetry = await checkTelemetry();
24850
+ const hasConfig = (0, import_node_fs48.existsSync)(projectPath(CODACY_CONFIG_FILE));
23883
24851
  const artifacts = {
23884
- standard: (0, import_node_fs44.existsSync)(projectPath(STANDARD_FILE)),
23885
- analysisConfig: (0, import_node_fs44.existsSync)(projectPath(CODACY_CONFIG_FILE)),
23886
- verityMd: (0, import_node_fs44.existsSync)(projectPath(VERITY_MD_FILE))
24852
+ standard: (0, import_node_fs48.existsSync)(projectPath(STANDARD_FILE)),
24853
+ analysisConfig: hasConfig,
24854
+ verityMd: (0, import_node_fs48.existsSync)(projectPath(VERITY_MD_FILE)),
24855
+ analysisConfigIds: hasConfig ? validatePatternIds().status : "absent"
23887
24856
  };
23888
24857
  const next = [];
23889
24858
  for (const c of prereqs.checks) {
23890
24859
  if (c.status !== "ok" && c.remedy) next.push(c.remedy);
23891
24860
  }
23892
24861
  if (!state?.init) next.push('Run "verity init" \u2014 the deterministic setup phase has not completed here.');
23893
- if (!artifacts.standard || !artifacts.analysisConfig || !artifacts.verityMd) {
23894
- next.push("Run /verity-setup in Claude Code \u2014 the Standard, analysis config, and VERITY.md are synthesized there.");
24862
+ const missing = [
24863
+ !artifacts.standard && ".verity/standard.yaml",
24864
+ !artifacts.analysisConfig && ".codacy/codacy.config.json",
24865
+ !artifacts.verityMd && "VERITY.md"
24866
+ ].filter((x) => !!x);
24867
+ if (missing.length === 3) {
24868
+ next.push('Run "verity standard synthesize --push" \u2014 it derives the Standard, the analysis config and VERITY.md from this codebase.');
24869
+ } else if (missing.length === 1 && !artifacts.analysisConfig) {
24870
+ next.push(
24871
+ 'No analysis config, so static analysis runs nothing. Run "verity standard synthesize --config-only" to derive one from the Standard you already have (--force would REPLACE that Standard), or have a teammate run "verity config push".'
24872
+ );
24873
+ } else if (missing.length > 0) {
24874
+ next.push(
24875
+ `Missing ${missing.join(" and ")}. Run "verity standard synthesize --force" to regenerate \u2014 note that replaces the Standard; use --config-only if only the analysis config is missing.`
24876
+ );
23895
24877
  }
23896
24878
  const noAnalysisMoment = !hooks.stop && hooks.guardOn.length === 0;
23897
24879
  if (noAnalysisMoment) {
23898
24880
  next.push('No analysis moment is active \u2014 run "verity hooks install --moments stop" or re-run "verity init".');
23899
24881
  }
24882
+ if (artifacts.analysisConfigIds === "invalid") {
24883
+ next.push(
24884
+ 'Your analysis config names pattern ids that no longer resolve, so those tools run with nothing enabled and report zero issues. Fix: "verity standard synthesize --config-only" (or re-run "verity init").'
24885
+ );
24886
+ }
23900
24887
  if (state?.telemetry === "deferred") {
23901
24888
  next.push('Telemetry was requested but needs a token \u2014 run "verity login", then "verity telemetry install".');
23902
24889
  }
@@ -23942,7 +24929,9 @@ function registerDoctorCommand(program2) {
23942
24929
  printInfo(`Telemetry: ${report.telemetry.enabled ? `enabled \u2192 ${report.telemetry.endpoint}` : "disabled"}`);
23943
24930
  printInfo("Artifacts:");
23944
24931
  printInfo(` .verity/standard.yaml: ${report.artifacts.standard ? "\u2713" : "missing"}`);
23945
- printInfo(` .codacy/codacy.config.json: ${report.artifacts.analysisConfig ? "\u2713" : "missing"}`);
24932
+ const idState = report.artifacts.analysisConfigIds;
24933
+ const idNote = idState === "valid" ? "\u2713" : idState === "invalid" ? "\u26A0 ids do not resolve" : idState === "unchecked" ? "\u2713 (ids unverified \u2014 no adapter)" : "";
24934
+ printInfo(` .codacy/codacy.config.json: ${report.artifacts.analysisConfig ? idNote : "missing"}`);
23946
24935
  printInfo(` VERITY.md: ${report.artifacts.verityMd ? "\u2713" : "missing"}`);
23947
24936
  if (report.next.length > 0) {
23948
24937
  console.log("");
@@ -23956,16 +24945,16 @@ function registerDoctorCommand(program2) {
23956
24945
  }
23957
24946
 
23958
24947
  // src/commands/migrate.ts
23959
- var import_node_fs45 = require("node:fs");
23960
- var import_node_path28 = require("node:path");
23961
- var import_node_child_process13 = require("node:child_process");
24948
+ var import_node_fs49 = require("node:fs");
24949
+ var import_node_path31 = require("node:path");
24950
+ var import_node_child_process15 = require("node:child_process");
23962
24951
  var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
23963
24952
  function defaultNpmRemover(pkg) {
23964
- (0, import_node_child_process13.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
24953
+ (0, import_node_child_process15.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
23965
24954
  }
23966
24955
  function isGitTracked(cwd, relPath) {
23967
24956
  try {
23968
- (0, import_node_child_process13.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
24957
+ (0, import_node_child_process15.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
23969
24958
  return true;
23970
24959
  } catch {
23971
24960
  return false;
@@ -23973,7 +24962,7 @@ function isGitTracked(cwd, relPath) {
23973
24962
  }
23974
24963
  function isGitRepo(cwd) {
23975
24964
  try {
23976
- (0, import_node_child_process13.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
24965
+ (0, import_node_child_process15.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
23977
24966
  return true;
23978
24967
  } catch {
23979
24968
  return false;
@@ -23994,12 +24983,12 @@ async function runMigration(opts = {}) {
23994
24983
  return { actions, migrated: actions.length > 0 };
23995
24984
  }
23996
24985
  function migrateProjectDir(root, actions) {
23997
- const gateDir = (0, import_node_path28.join)(root, ".gate");
23998
- const verityDir = (0, import_node_path28.join)(root, ".verity");
23999
- if ((0, import_node_fs45.existsSync)(gateDir) && !(0, import_node_fs45.existsSync)(verityDir)) {
24986
+ const gateDir = (0, import_node_path31.join)(root, ".gate");
24987
+ const verityDir = (0, import_node_path31.join)(root, ".verity");
24988
+ if ((0, import_node_fs49.existsSync)(gateDir) && !(0, import_node_fs49.existsSync)(verityDir)) {
24000
24989
  return migrateProjectDirRename(root, gateDir, verityDir, actions);
24001
24990
  }
24002
- if ((0, import_node_fs45.existsSync)(gateDir) && (0, import_node_fs45.existsSync)(verityDir)) {
24991
+ if ((0, import_node_fs49.existsSync)(gateDir) && (0, import_node_fs49.existsSync)(verityDir)) {
24003
24992
  return migrateProjectDirCarry(gateDir, verityDir, actions);
24004
24993
  }
24005
24994
  return false;
@@ -24013,20 +25002,20 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
24013
25002
  );
24014
25003
  }
24015
25004
  try {
24016
- (0, import_node_child_process13.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
25005
+ (0, import_node_child_process15.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
24017
25006
  actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
24018
25007
  moved = true;
24019
25008
  } catch {
24020
25009
  }
24021
25010
  }
24022
25011
  if (moved) {
24023
- if ((0, import_node_fs45.existsSync)(gateDir)) {
25012
+ if ((0, import_node_fs49.existsSync)(gateDir)) {
24024
25013
  const carried = carryLegacyContents(gateDir, verityDir);
24025
25014
  if (carried > 0) {
24026
25015
  actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
24027
25016
  }
24028
25017
  try {
24029
- (0, import_node_fs45.rmSync)(gateDir, { recursive: true, force: true });
25018
+ (0, import_node_fs49.rmSync)(gateDir, { recursive: true, force: true });
24030
25019
  } catch {
24031
25020
  }
24032
25021
  }
@@ -24042,18 +25031,18 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
24042
25031
  actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
24043
25032
  }
24044
25033
  try {
24045
- (0, import_node_fs45.rmSync)(gateDir, { recursive: true, force: true });
25034
+ (0, import_node_fs49.rmSync)(gateDir, { recursive: true, force: true });
24046
25035
  } catch {
24047
25036
  }
24048
25037
  return carried > 0;
24049
25038
  }
24050
25039
  function migrateGlobalCredentials(home, actions) {
24051
25040
  if (!home) return;
24052
- const gateCreds = (0, import_node_path28.join)(home, ".gate", "credentials");
24053
- const verityCreds = (0, import_node_path28.join)(home, ".verity", "credentials");
24054
- if (!(0, import_node_fs45.existsSync)(gateCreds)) return;
24055
- if (!(0, import_node_fs45.existsSync)(verityCreds)) {
24056
- (0, import_node_fs45.mkdirSync)((0, import_node_path28.join)(home, ".verity"), { recursive: true });
25041
+ const gateCreds = (0, import_node_path31.join)(home, ".gate", "credentials");
25042
+ const verityCreds = (0, import_node_path31.join)(home, ".verity", "credentials");
25043
+ if (!(0, import_node_fs49.existsSync)(gateCreds)) return;
25044
+ if (!(0, import_node_fs49.existsSync)(verityCreds)) {
25045
+ (0, import_node_fs49.mkdirSync)((0, import_node_path31.join)(home, ".verity"), { recursive: true });
24057
25046
  moveFile(gateCreds, verityCreds);
24058
25047
  actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
24059
25048
  return;
@@ -24075,8 +25064,8 @@ async function migrateLegacyHooks(root, actions) {
24075
25064
  }
24076
25065
  }
24077
25066
  async function migrateClaudeMd(root, actions) {
24078
- const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
24079
- const hadLegacyBlock = (0, import_node_fs45.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
25067
+ const claudeMd = (0, import_node_path31.join)(root, "CLAUDE.md");
25068
+ const hadLegacyBlock = (0, import_node_fs49.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
24080
25069
  if (!hadLegacyBlock) return;
24081
25070
  try {
24082
25071
  await ensureClaudeMdPointer(root);
@@ -24086,13 +25075,13 @@ async function migrateClaudeMd(root, actions) {
24086
25075
  }
24087
25076
  }
24088
25077
  function migrateStandardFile(root, actions) {
24089
- const gateMd = (0, import_node_path28.join)(root, "GATE.md");
24090
- const verityMd = (0, import_node_path28.join)(root, "VERITY.md");
24091
- if (!(0, import_node_fs45.existsSync)(gateMd) || (0, import_node_fs45.existsSync)(verityMd)) return;
25078
+ const gateMd = (0, import_node_path31.join)(root, "GATE.md");
25079
+ const verityMd = (0, import_node_path31.join)(root, "VERITY.md");
25080
+ if (!(0, import_node_fs49.existsSync)(gateMd) || (0, import_node_fs49.existsSync)(verityMd)) return;
24092
25081
  let moved = false;
24093
25082
  if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
24094
25083
  try {
24095
- (0, import_node_child_process13.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
25084
+ (0, import_node_child_process15.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
24096
25085
  moved = true;
24097
25086
  } catch {
24098
25087
  }
@@ -24100,12 +25089,12 @@ function migrateStandardFile(root, actions) {
24100
25089
  if (!moved) moveFile(gateMd, verityMd);
24101
25090
  const content = readFileSyncSafe(verityMd);
24102
25091
  const refreshed = content.split("GATE.md").join("VERITY.md");
24103
- if (refreshed !== content) (0, import_node_fs45.writeFileSync)(verityMd, refreshed);
25092
+ if (refreshed !== content) (0, import_node_fs49.writeFileSync)(verityMd, refreshed);
24104
25093
  actions.push("Renamed GATE.md \u2192 VERITY.md");
24105
25094
  }
24106
25095
  async function migrateTelemetryHeaders(root, actions) {
24107
- const file = (0, import_node_path28.join)(root, ".claude", "settings.local.json");
24108
- if (!(0, import_node_fs45.existsSync)(file)) return;
25096
+ const file = (0, import_node_path31.join)(root, ".claude", "settings.local.json");
25097
+ if (!(0, import_node_fs49.existsSync)(file)) return;
24109
25098
  let settings;
24110
25099
  try {
24111
25100
  settings = JSON.parse(readFileSyncSafe(file) || "{}");
@@ -24153,21 +25142,21 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
24153
25142
  }
24154
25143
  if (toAppend.length > 0) {
24155
25144
  const sep2 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
24156
- (0, import_node_fs45.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
25145
+ (0, import_node_fs49.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
24157
25146
  }
24158
- (0, import_node_fs45.rmSync)(gateCreds, { force: true });
25147
+ (0, import_node_fs49.rmSync)(gateCreds, { force: true });
24159
25148
  return toAppend.length;
24160
25149
  }
24161
25150
  function readFileSyncSafe(path) {
24162
25151
  try {
24163
- return (0, import_node_fs45.readFileSync)(path, "utf-8");
25152
+ return (0, import_node_fs49.readFileSync)(path, "utf-8");
24164
25153
  } catch {
24165
25154
  return "";
24166
25155
  }
24167
25156
  }
24168
25157
  function hasStagedChanges(root) {
24169
25158
  try {
24170
- (0, import_node_child_process13.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
25159
+ (0, import_node_child_process15.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
24171
25160
  return false;
24172
25161
  } catch {
24173
25162
  return true;
@@ -24175,59 +25164,59 @@ function hasStagedChanges(root) {
24175
25164
  }
24176
25165
  function moveDir(from, to) {
24177
25166
  try {
24178
- (0, import_node_fs45.renameSync)(from, to);
25167
+ (0, import_node_fs49.renameSync)(from, to);
24179
25168
  } catch (err) {
24180
25169
  if (err.code !== "EXDEV") throw err;
24181
- (0, import_node_fs45.cpSync)(from, to, { recursive: true });
24182
- (0, import_node_fs45.rmSync)(from, { recursive: true, force: true });
25170
+ (0, import_node_fs49.cpSync)(from, to, { recursive: true });
25171
+ (0, import_node_fs49.rmSync)(from, { recursive: true, force: true });
24183
25172
  }
24184
25173
  }
24185
25174
  function moveFile(from, to) {
24186
25175
  try {
24187
- (0, import_node_fs45.renameSync)(from, to);
25176
+ (0, import_node_fs49.renameSync)(from, to);
24188
25177
  } catch (err) {
24189
25178
  if (err.code !== "EXDEV") throw err;
24190
- (0, import_node_fs45.cpSync)(from, to);
24191
- (0, import_node_fs45.rmSync)(from, { force: true });
25179
+ (0, import_node_fs49.cpSync)(from, to);
25180
+ (0, import_node_fs49.rmSync)(from, { force: true });
24192
25181
  }
24193
25182
  }
24194
25183
  function carryLegacyContents(gateDir, verityDir) {
24195
25184
  let copied = 0;
24196
- const walk = (relDir) => {
24197
- const srcDir = (0, import_node_path28.join)(gateDir, relDir);
24198
- for (const entry of (0, import_node_fs45.readdirSync)(srcDir)) {
24199
- const rel = relDir ? (0, import_node_path28.join)(relDir, entry) : entry;
24200
- const src = (0, import_node_path28.join)(gateDir, rel);
24201
- const dest = (0, import_node_path28.join)(verityDir, rel);
24202
- if ((0, import_node_fs45.statSync)(src).isDirectory()) {
24203
- walk(rel);
24204
- } else if (!(0, import_node_fs45.existsSync)(dest)) {
24205
- (0, import_node_fs45.mkdirSync)((0, import_node_path28.dirname)(dest), { recursive: true });
24206
- (0, import_node_fs45.cpSync)(src, dest);
25185
+ const walk2 = (relDir) => {
25186
+ const srcDir = (0, import_node_path31.join)(gateDir, relDir);
25187
+ for (const entry of (0, import_node_fs49.readdirSync)(srcDir)) {
25188
+ const rel = relDir ? (0, import_node_path31.join)(relDir, entry) : entry;
25189
+ const src = (0, import_node_path31.join)(gateDir, rel);
25190
+ const dest = (0, import_node_path31.join)(verityDir, rel);
25191
+ if ((0, import_node_fs49.statSync)(src).isDirectory()) {
25192
+ walk2(rel);
25193
+ } else if (!(0, import_node_fs49.existsSync)(dest)) {
25194
+ (0, import_node_fs49.mkdirSync)((0, import_node_path31.dirname)(dest), { recursive: true });
25195
+ (0, import_node_fs49.cpSync)(src, dest);
24207
25196
  copied++;
24208
25197
  }
24209
25198
  }
24210
25199
  };
24211
- walk("");
25200
+ walk2("");
24212
25201
  return copied;
24213
25202
  }
24214
25203
  async function needsMigration(root = repoRoot()) {
24215
- const gateDir = (0, import_node_path28.join)(root, ".gate");
24216
- const verityDir = (0, import_node_path28.join)(root, ".verity");
24217
- if ((0, import_node_fs45.existsSync)(gateDir) && !(0, import_node_fs45.existsSync)(verityDir)) return true;
24218
- if ((0, import_node_fs45.existsSync)(gateDir) && (0, import_node_fs45.existsSync)(verityDir)) {
24219
- if ((0, import_node_fs45.existsSync)((0, import_node_path28.join)(gateDir, "credentials")) && !(0, import_node_fs45.existsSync)((0, import_node_path28.join)(verityDir, "credentials"))) {
25204
+ const gateDir = (0, import_node_path31.join)(root, ".gate");
25205
+ const verityDir = (0, import_node_path31.join)(root, ".verity");
25206
+ if ((0, import_node_fs49.existsSync)(gateDir) && !(0, import_node_fs49.existsSync)(verityDir)) return true;
25207
+ if ((0, import_node_fs49.existsSync)(gateDir) && (0, import_node_fs49.existsSync)(verityDir)) {
25208
+ if ((0, import_node_fs49.existsSync)((0, import_node_path31.join)(gateDir, "credentials")) && !(0, import_node_fs49.existsSync)((0, import_node_path31.join)(verityDir, "credentials"))) {
24220
25209
  return true;
24221
25210
  }
24222
- if ((0, import_node_fs45.existsSync)((0, import_node_path28.join)(gateDir, "memory")) && !(0, import_node_fs45.existsSync)((0, import_node_path28.join)(verityDir, "memory"))) {
25211
+ if ((0, import_node_fs49.existsSync)((0, import_node_path31.join)(gateDir, "memory")) && !(0, import_node_fs49.existsSync)((0, import_node_path31.join)(verityDir, "memory"))) {
24223
25212
  return true;
24224
25213
  }
24225
25214
  }
24226
- const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
24227
- if ((0, import_node_fs45.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
25215
+ const claudeMd = (0, import_node_path31.join)(root, "CLAUDE.md");
25216
+ if ((0, import_node_fs49.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
24228
25217
  return true;
24229
25218
  }
24230
- if ((0, import_node_fs45.existsSync)((0, import_node_path28.join)(root, "GATE.md")) && !(0, import_node_fs45.existsSync)((0, import_node_path28.join)(root, "VERITY.md"))) {
25219
+ if ((0, import_node_fs49.existsSync)((0, import_node_path31.join)(root, "GATE.md")) && !(0, import_node_fs49.existsSync)((0, import_node_path31.join)(root, "VERITY.md"))) {
24231
25220
  return true;
24232
25221
  }
24233
25222
  if (await hasLegacyHooksAt(root)) return true;
@@ -24253,12 +25242,168 @@ function registerMigrateCommand(program2) {
24253
25242
  }
24254
25243
 
24255
25244
  // src/lib/prompt.ts
24256
- var readline2 = __toESM(require("node:readline/promises"));
25245
+ var readline3 = __toESM(require("node:readline/promises"));
25246
+
25247
+ // src/lib/select.ts
25248
+ var readline2 = __toESM(require("node:readline"));
25249
+ function keyToAction(str, key) {
25250
+ const name = key?.name;
25251
+ if (key?.ctrl && (name === "c" || str === "")) return { type: "interrupt" };
25252
+ if (key?.ctrl && (name === "d" || str === "")) return { type: "eof" };
25253
+ switch (name) {
25254
+ case "up":
25255
+ return { type: "move", delta: -1 };
25256
+ case "down":
25257
+ return { type: "move", delta: 1 };
25258
+ case "k":
25259
+ return key?.ctrl ? null : { type: "move", delta: -1 };
25260
+ case "j":
25261
+ return key?.ctrl ? null : { type: "move", delta: 1 };
25262
+ case "space":
25263
+ return { type: "toggle" };
25264
+ case "return":
25265
+ case "enter":
25266
+ return { type: "confirm" };
25267
+ default:
25268
+ break;
25269
+ }
25270
+ if (str === " ") return { type: "toggle" };
25271
+ if (str === "\r" || str === "\n") return { type: "confirm" };
25272
+ if (str && /^[1-9]$/.test(str)) return { type: "jump", index: Number(str) - 1 };
25273
+ return null;
25274
+ }
25275
+ function applyKey(state, action, count, mode2) {
25276
+ switch (action.type) {
25277
+ case "move": {
25278
+ const cursor = (state.cursor + action.delta + count) % count;
25279
+ return { status: "open", state: { cursor, chosen: state.chosen } };
25280
+ }
25281
+ case "jump": {
25282
+ if (action.index >= count) return { status: "open", state };
25283
+ return { status: "open", state: { cursor: action.index, chosen: state.chosen } };
25284
+ }
25285
+ case "toggle": {
25286
+ if (mode2 === "single") {
25287
+ return { status: "open", state: { cursor: state.cursor, chosen: /* @__PURE__ */ new Set([state.cursor]) } };
25288
+ }
25289
+ const chosen = new Set(state.chosen);
25290
+ if (chosen.has(state.cursor)) chosen.delete(state.cursor);
25291
+ else chosen.add(state.cursor);
25292
+ return { status: "open", state: { cursor: state.cursor, chosen } };
25293
+ }
25294
+ case "confirm": {
25295
+ if (mode2 === "single") return { status: "confirmed", indices: [state.cursor] };
25296
+ if (state.chosen.size === 0) {
25297
+ return {
25298
+ status: "open",
25299
+ state: { ...state, hint: "Select at least one \u2014 space toggles the option under the cursor." }
25300
+ };
25301
+ }
25302
+ return { status: "confirmed", indices: [...state.chosen].sort((a, b) => a - b) };
25303
+ }
25304
+ case "interrupt":
25305
+ return { status: "interrupt" };
25306
+ case "eof":
25307
+ return { status: "eof" };
25308
+ }
25309
+ }
25310
+ var GREEN3 = "\x1B[0;32m";
25311
+ var DIM4 = "\x1B[2m";
25312
+ var BOLD2 = "\x1B[1m";
25313
+ var RESET3 = "\x1B[0m";
25314
+ function renderSelect(question, choices, state, mode2, color = colorEnabled()) {
25315
+ const paint = (text, code) => color ? `${code}${text}${RESET3}` : text;
25316
+ const lines = ["", ` ${question}`];
25317
+ const labelOf = (c) => `${c.label}${c.recommended ? " (recommended)" : ""}`;
25318
+ const gutter = Math.max(...choices.map((c) => labelOf(c).length));
25319
+ choices.forEach((choice, i) => {
25320
+ const here = i === state.cursor;
25321
+ const marker = state.chosen.has(i) ? "\u25C9" : "\u25CB";
25322
+ const cursor = here ? "\u276F" : " ";
25323
+ const label2 = labelOf(choice);
25324
+ const pad = choice.hint ? " ".repeat(gutter - label2.length) : "";
25325
+ const hint = choice.hint ? `${pad} ${paint(choice.hint, DIM4)}` : "";
25326
+ lines.push(` ${cursor} ${marker} ${here ? paint(label2, BOLD2) : label2}${hint}`);
25327
+ });
25328
+ const keys = mode2 === "multi" ? "\u2191\u2193 move \xB7 space toggle \xB7 enter confirm" : "\u2191\u2193 move \xB7 enter confirm";
25329
+ lines.push(` ${paint(state.hint ?? keys, state.hint ? GREEN3 : DIM4)}`);
25330
+ return lines;
25331
+ }
25332
+ function runSelect(opts) {
25333
+ const input = opts.input ?? process.stdin;
25334
+ const output = opts.output ?? process.stdout;
25335
+ const { choices, mode: mode2 } = opts;
25336
+ if (typeof input.setRawMode !== "function" || !input.isTTY) return Promise.resolve(null);
25337
+ const initialIdx = choices.map((c, i) => opts.initial.includes(c.id) ? i : -1).filter((i) => i >= 0);
25338
+ let state = {
25339
+ cursor: initialIdx[0] ?? 0,
25340
+ chosen: new Set(mode2 === "single" ? [initialIdx[0] ?? 0] : initialIdx)
25341
+ };
25342
+ return new Promise((resolve4) => {
25343
+ let painted = 0;
25344
+ let settled = false;
25345
+ const draw = () => {
25346
+ if (painted > 0) {
25347
+ readline2.moveCursor(output, 0, -painted);
25348
+ readline2.cursorTo(output, 0);
25349
+ readline2.clearScreenDown(output);
25350
+ }
25351
+ const lines = renderSelect(opts.question, choices, state, mode2);
25352
+ output.write(lines.join("\n") + "\n");
25353
+ painted = lines.length;
25354
+ };
25355
+ const onKey = (str, key) => {
25356
+ const action = keyToAction(str, key);
25357
+ if (!action) return;
25358
+ const outcome = applyKey(state, action, choices.length, mode2);
25359
+ if (outcome.status === "open") {
25360
+ state = outcome.state;
25361
+ draw();
25362
+ return;
25363
+ }
25364
+ if (outcome.status === "confirmed") {
25365
+ state = { ...state, hint: void 0 };
25366
+ draw();
25367
+ finish(outcome.indices.map((i) => choices[i].id));
25368
+ return;
25369
+ }
25370
+ if (outcome.status === "eof") {
25371
+ finish(null);
25372
+ return;
25373
+ }
25374
+ restore();
25375
+ output.write("\n");
25376
+ process.exit(130);
25377
+ };
25378
+ const restore = () => {
25379
+ input.removeListener("keypress", onKey);
25380
+ try {
25381
+ input.setRawMode(false);
25382
+ } catch {
25383
+ }
25384
+ input.pause();
25385
+ };
25386
+ const finish = (value) => {
25387
+ if (settled) return;
25388
+ settled = true;
25389
+ restore();
25390
+ resolve4(value);
25391
+ };
25392
+ readline2.emitKeypressEvents(input);
25393
+ input.setRawMode(true);
25394
+ input.resume();
25395
+ input.on("keypress", onKey);
25396
+ input.once("end", () => finish(null));
25397
+ draw();
25398
+ });
25399
+ }
25400
+
25401
+ // src/lib/prompt.ts
24257
25402
  function interactive() {
24258
25403
  return !!process.stdin.isTTY && !!process.stdout.isTTY;
24259
25404
  }
24260
25405
  async function askLine(question, io = {}) {
24261
- const rl = readline2.createInterface({
25406
+ const rl = readline3.createInterface({
24262
25407
  input: io.input ?? process.stdin,
24263
25408
  output: io.output ?? process.stdout
24264
25409
  });
@@ -24316,6 +25461,8 @@ function printOptions(question, choices) {
24316
25461
  }
24317
25462
  async function promptChoice(question, choices, fallback) {
24318
25463
  if (!interactive()) return fallback;
25464
+ const picked = await runSelect({ question, choices, initial: [fallback], mode: "single" });
25465
+ if (picked !== null) return picked[0] ?? fallback;
24319
25466
  printOptions(question, choices);
24320
25467
  const defaultIdx = choices.findIndex((c) => c.id === fallback);
24321
25468
  const answer = await ask(` Choose [${defaultIdx + 1}]: `);
@@ -24329,6 +25476,8 @@ async function promptChoice(question, choices, fallback) {
24329
25476
  }
24330
25477
  async function promptMultiSelect(question, choices, fallback) {
24331
25478
  if (!interactive()) return [...fallback];
25479
+ const picked = await runSelect({ question, choices, initial: fallback, mode: "multi" });
25480
+ if (picked !== null) return picked.length > 0 ? picked : [...fallback];
24332
25481
  printOptions(question, choices);
24333
25482
  const defaultLabel = choices.map((c, i) => fallback.includes(c.id) ? String(i + 1) : null).filter(Boolean).join(",");
24334
25483
  const answer = await ask(` Choose one or more, comma-separated [${defaultLabel}]: `);
@@ -24341,6 +25490,100 @@ async function promptMultiSelect(question, choices, fallback) {
24341
25490
  return parsed.ids;
24342
25491
  }
24343
25492
 
25493
+ // src/lib/remote-config.ts
25494
+ var import_node_fs50 = require("node:fs");
25495
+ var import_promises16 = require("node:fs/promises");
25496
+ var import_node_path32 = require("node:path");
25497
+ var import_yaml5 = __toESM(require_dist());
25498
+ var IGNORE_RIDER = "verityignore";
25499
+ async function fetchRemoteSetup(opts) {
25500
+ const standard = await apiRequest({
25501
+ method: "GET",
25502
+ path: "/standards/latest",
25503
+ serviceUrl: opts.serviceUrl,
25504
+ token: opts.token,
25505
+ verbose: opts.verbose
25506
+ });
25507
+ if (!standard.ok) {
25508
+ const missing = /STANDARD_NOT_FOUND|not found|404/i.test(standard.error);
25509
+ return missing ? { status: "none" } : { status: "unavailable", reason: standard.error };
25510
+ }
25511
+ const config = await apiRequest({
25512
+ method: "GET",
25513
+ path: "/analysis-configs",
25514
+ serviceUrl: opts.serviceUrl,
25515
+ token: opts.token,
25516
+ verbose: opts.verbose
25517
+ });
25518
+ return {
25519
+ status: "found",
25520
+ standard: {
25521
+ version: standard.data.version,
25522
+ content: standard.data.content ?? {},
25523
+ createdBy: standard.data.created_by,
25524
+ createdAt: standard.data.created_at
25525
+ },
25526
+ // Absent is normal and not an error: the two are stored independently, and a
25527
+ // project can have a Standard with no analysis config.
25528
+ analysisConfig: config.ok ? config.data.content ?? null : null
25529
+ };
25530
+ }
25531
+ async function adoptRemoteSetup(found, opts) {
25532
+ const written = [];
25533
+ const notes = [];
25534
+ const content = { ...found.standard.content };
25535
+ const rider = typeof content[IGNORE_RIDER] === "string" ? content[IGNORE_RIDER] : null;
25536
+ delete content[IGNORE_RIDER];
25537
+ await writeOut(STANDARD_FILE, (0, import_yaml5.stringify)(content));
25538
+ written.push(STANDARD_FILE);
25539
+ if (rider !== null) {
25540
+ const localIgnore = projectPath(VERITYIGNORE_FILE);
25541
+ if (!(0, import_node_fs50.existsSync)(localIgnore)) {
25542
+ await writeOut(VERITYIGNORE_FILE, rider);
25543
+ written.push(VERITYIGNORE_FILE);
25544
+ } else {
25545
+ const local = await (0, import_promises16.readFile)(localIgnore, "utf-8").catch(() => null);
25546
+ if (local !== null && local !== rider) {
25547
+ notes.push(`${VERITYIGNORE_FILE} already exists here and differs from the pushed copy \u2014 kept yours.`);
25548
+ }
25549
+ }
25550
+ }
25551
+ if (found.analysisConfig) {
25552
+ await writeOut(CODACY_CONFIG_FILE, JSON.stringify(found.analysisConfig, null, 2) + "\n");
25553
+ written.push(CODACY_CONFIG_FILE);
25554
+ } else {
25555
+ const derived = await deriveConfigForStandard(content);
25556
+ written.push(...derived.written);
25557
+ notes.push(...derived.notes);
25558
+ if (derived.written.length > 0) {
25559
+ notes.push("No analysis config was stored for this repository \u2014 derived one from the adopted Standard.");
25560
+ }
25561
+ }
25562
+ await writeOut(VERITY_MD_FILE, renderVerityMd({
25563
+ content,
25564
+ version: found.standard.version,
25565
+ serviceUrl: opts.serviceUrl,
25566
+ projectId: opts.projectId,
25567
+ origin: { kind: "adopted", createdBy: found.standard.createdBy }
25568
+ }));
25569
+ written.push(VERITY_MD_FILE);
25570
+ return { written, notes };
25571
+ }
25572
+ async function writeOut(relative2, body) {
25573
+ const target = projectPath(relative2);
25574
+ await (0, import_promises16.mkdir)((0, import_node_path32.dirname)(target), { recursive: true });
25575
+ await (0, import_promises16.writeFile)(target, body);
25576
+ }
25577
+ function describeRemote(found) {
25578
+ const when = found.standard.createdAt.slice(0, 10);
25579
+ const who = found.standard.createdBy ? ` by ${found.standard.createdBy}` : "";
25580
+ const extras = [];
25581
+ if (found.analysisConfig) extras.push("analysis config");
25582
+ if (typeof found.standard.content[IGNORE_RIDER] === "string") extras.push(".verityignore");
25583
+ const also = extras.length > 0 ? ` \xB7 with ${extras.join(" and ")}` : "";
25584
+ return `version ${found.standard.version}, pushed${who} on ${when}${also}`;
25585
+ }
25586
+
24344
25587
  // src/commands/init.ts
24345
25588
  async function confirmExistingLogin(serviceUrl, remote, opts) {
24346
25589
  const existing = await resolveToken(opts.token);
@@ -24399,7 +25642,7 @@ async function runOptionalAuth(resolution, opts = {}) {
24399
25642
  }
24400
25643
  let remote = "";
24401
25644
  try {
24402
- remote = (0, import_node_child_process14.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
25645
+ remote = (0, import_node_child_process16.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
24403
25646
  } catch {
24404
25647
  }
24405
25648
  if (!healed) {
@@ -24443,17 +25686,17 @@ async function runOptionalAuth(resolution, opts = {}) {
24443
25686
  localOnlyNote();
24444
25687
  }
24445
25688
  }
24446
- function resolveDataDir() {
25689
+ function resolveDataDir2() {
24447
25690
  const candidates = [
24448
- (0, import_node_path29.join)(__dirname, "..", "data"),
25691
+ (0, import_node_path33.join)(__dirname, "..", "data"),
24449
25692
  // installed: node_modules/@codacy/verity-cli/data
24450
- (0, import_node_path29.join)(__dirname, "..", "..", "data"),
25693
+ (0, import_node_path33.join)(__dirname, "..", "..", "data"),
24451
25694
  // edge case: nested resolution
24452
- (0, import_node_path29.join)(process.cwd(), "cli", "data")
25695
+ (0, import_node_path33.join)(process.cwd(), "cli", "data")
24453
25696
  // local dev: running from repo root
24454
25697
  ];
24455
25698
  for (const candidate of candidates) {
24456
- if ((0, import_node_fs46.existsSync)((0, import_node_path29.join)(candidate, "skills"))) {
25699
+ if ((0, import_node_fs51.existsSync)((0, import_node_path33.join)(candidate, "skills"))) {
24457
25700
  return candidate;
24458
25701
  }
24459
25702
  }
@@ -24462,28 +25705,28 @@ function resolveDataDir() {
24462
25705
  );
24463
25706
  }
24464
25707
  async function copyDir(src, dest) {
24465
- await (0, import_promises14.mkdir)(dest, { recursive: true });
24466
- await (0, import_promises14.cp)(src, dest, { recursive: true, force: true });
25708
+ await (0, import_promises17.mkdir)(dest, { recursive: true });
25709
+ await (0, import_promises17.cp)(src, dest, { recursive: true, force: true });
24467
25710
  }
24468
25711
  async function skillIsCurrent(src, dest) {
24469
25712
  const list2 = (dir) => {
24470
25713
  const out = [];
24471
- const walk = (d, prefix) => {
24472
- for (const e of (0, import_node_fs46.readdirSync)(d, { withFileTypes: true })) {
25714
+ const walk2 = (d, prefix) => {
25715
+ for (const e of (0, import_node_fs51.readdirSync)(d, { withFileTypes: true })) {
24473
25716
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
24474
- if (e.isDirectory()) walk((0, import_node_path29.join)(d, e.name), rel);
25717
+ if (e.isDirectory()) walk2((0, import_node_path33.join)(d, e.name), rel);
24475
25718
  else if (e.isFile()) out.push(rel);
24476
25719
  }
24477
25720
  };
24478
- walk(dir, "");
25721
+ walk2(dir, "");
24479
25722
  return out.sort();
24480
25723
  };
24481
25724
  try {
24482
25725
  const shipped = list2(src);
24483
25726
  if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
24484
25727
  for (const rel of shipped) {
24485
- const a = await (0, import_promises14.readFile)((0, import_node_path29.join)(src, rel), "utf-8");
24486
- const b = await (0, import_promises14.readFile)((0, import_node_path29.join)(dest, rel), "utf-8");
25728
+ const a = await (0, import_promises17.readFile)((0, import_node_path33.join)(src, rel), "utf-8");
25729
+ const b = await (0, import_promises17.readFile)((0, import_node_path33.join)(dest, rel), "utf-8");
24487
25730
  if (a !== b) return false;
24488
25731
  }
24489
25732
  return true;
@@ -24512,9 +25755,17 @@ var MOMENT_CHOICES = [
24512
25755
  { id: "pre-push", label: "Before push / PR", hint: "reviews the to-be-pushed commits, blocks on FAIL" }
24513
25756
  ];
24514
25757
  var DEFAULT_MOMENTS = ["stop"];
25758
+ async function momentsFromInstalledHooks() {
25759
+ const status = await checkAllVerityHooks();
25760
+ const wired = [];
25761
+ if (status.stop) wired.push("stop");
25762
+ if (status.guardOn.includes("commit")) wired.push("pre-commit");
25763
+ if (status.guardOn.includes("push")) wired.push("pre-push");
25764
+ return wired.length > 0 ? wired : DEFAULT_MOMENTS;
25765
+ }
24515
25766
  async function askSetupQuestions(defaultsOnly, previous) {
24516
25767
  const intensityDefault = previous?.intensity ?? "balanced";
24517
- const momentsDefault = previous?.moments?.length ? previous.moments : DEFAULT_MOMENTS;
25768
+ const momentsDefault = previous?.moments?.length ? previous.moments : await momentsFromInstalledHooks();
24518
25769
  if (defaultsOnly) {
24519
25770
  return { intensity: intensityDefault, moments: momentsDefault, telemetry: "not-asked" };
24520
25771
  }
@@ -24551,6 +25802,127 @@ function insideClaudeCode() {
24551
25802
  function resumeDeferredPending(previous) {
24552
25803
  return previous?.telemetry === "deferred";
24553
25804
  }
25805
+ async function maybeAdoptRemoteSetup(opts) {
25806
+ if (!opts.adopt) {
25807
+ printInfo(" Skipped (--no-adopt) \u2014 the setup below runs from this codebase.");
25808
+ return false;
25809
+ }
25810
+ const token = await resolveToken(opts.globals.token);
25811
+ const url = await resolveServiceUrl(opts.globals.serviceUrl);
25812
+ if (!token.ok || !url.ok) {
25813
+ printInfo(" Not signed in, so there is nothing to fetch \u2014 the setup below runs from your codebase.");
25814
+ return false;
25815
+ }
25816
+ const found = await fetchRemoteSetup({
25817
+ token: token.data.token,
25818
+ serviceUrl: url.data,
25819
+ verbose: opts.globals.verbose
25820
+ });
25821
+ if (found.status === "unavailable") {
25822
+ printWarn(` Could not ask the service (${found.reason}) \u2014 continuing with the regular setup.`);
25823
+ return false;
25824
+ }
25825
+ if (found.status === "none") {
25826
+ printInfo(" No Standard stored for this repository yet \u2014 this setup will create the first one.");
25827
+ return false;
25828
+ }
25829
+ if (opts.previous?.declinedStandardVersion === found.standard.version) {
25830
+ printInfo(` Standard v${found.standard.version} is available; you declined this version before \u2014 not asking again.`);
25831
+ printInfo(" A newer version will be offered. To take it now: verity standard get, or /verity-setup.");
25832
+ return false;
25833
+ }
25834
+ console.log("");
25835
+ console.log(` This repository already has a Standard on Verity: ${describeRemote(found)}`);
25836
+ console.log(" Using it keeps you consistent with everyone else on the project.");
25837
+ console.log(" Synthesizing a new one from this codebase would push a competing version.");
25838
+ console.log("");
25839
+ const use = opts.defaultsOnly ? true : await promptYes(" Use the existing Standard? [Y/n] ", { nonInteractive: true });
25840
+ if (!use) {
25841
+ printInfo(" Keeping the existing version untouched; /verity-setup will synthesize a new one.");
25842
+ await writeSetupState({ declinedStandardVersion: found.standard.version }).catch(() => {
25843
+ });
25844
+ return false;
25845
+ }
25846
+ try {
25847
+ const outcome = await adoptRemoteSetup(found, {
25848
+ serviceUrl: url.data,
25849
+ projectId: await projectLabel(token.data.token, url.data, opts.globals.verbose)
25850
+ });
25851
+ for (const path of outcome.written) printInfo(` ${path} \u2713`);
25852
+ for (const note of outcome.notes) printWarn(` ${note}`);
25853
+ if (opts.previous?.declinedStandardVersion != null) {
25854
+ await writeSetupState({ declinedStandardVersion: void 0 }).catch(() => {
25855
+ });
25856
+ }
25857
+ return true;
25858
+ } catch (err) {
25859
+ printWarn(` Could not write the fetched configuration: ${err.message}`);
25860
+ printInfo(" Falling back to the regular setup.");
25861
+ return false;
25862
+ }
25863
+ }
25864
+ async function projectLabel(token, serviceUrl, verbose) {
25865
+ try {
25866
+ const who = await whoami(token, serviceUrl, verbose);
25867
+ return who.ok ? who.data.project_id : null;
25868
+ } catch {
25869
+ return null;
25870
+ }
25871
+ }
25872
+ async function synthesizeLocally(opts) {
25873
+ printInfo(" Building a Standard from your codebase\u2026");
25874
+ try {
25875
+ const url = await resolveServiceUrl(opts.globals.serviceUrl);
25876
+ const result = await runSynthesis({
25877
+ intensity: opts.intensity,
25878
+ serviceUrl: url.ok ? url.data : void 0
25879
+ });
25880
+ if ("refused" in result) {
25881
+ printWarn(` ${result.refused}`);
25882
+ return false;
25883
+ }
25884
+ printInfo(` ${describeDetected(result.detected)}`);
25885
+ for (const path of result.written) printInfo(` ${path} \u2713`);
25886
+ for (const note of result.notes) printWarn(` ${note}`);
25887
+ const pushed = await pushStandardAndConfig(opts.globals);
25888
+ for (const line of pushed.lines) printInfo(` ${line}`);
25889
+ if (pushed.standardVersion !== null) {
25890
+ await correctVerityMdVersion({
25891
+ standard: result.standard,
25892
+ version: pushed.standardVersion,
25893
+ tools: result.tools,
25894
+ serviceUrl: url.ok ? url.data : void 0
25895
+ });
25896
+ }
25897
+ return true;
25898
+ } catch (err) {
25899
+ printWarn(` Could not derive a Standard: ${err.message}`);
25900
+ printInfo(" Run /verity-setup in Claude Code to synthesize one instead.");
25901
+ return false;
25902
+ }
25903
+ }
25904
+ async function healStaleAnalysisConfig(globals) {
25905
+ const configPath = projectPath(CODACY_CONFIG_FILE);
25906
+ const standardPath = projectPath(STANDARD_FILE);
25907
+ if (!(0, import_node_fs51.existsSync)(configPath) || !(0, import_node_fs51.existsSync)(standardPath)) return;
25908
+ const validation = validatePatternIds();
25909
+ if (validation.status !== "invalid") return;
25910
+ printWarn(" Your analysis config names pattern ids that no longer resolve \u2014 those tools were");
25911
+ printWarn(" running silently with nothing enabled. Re-deriving it from your Standard\u2026");
25912
+ try {
25913
+ const content = (0, import_yaml6.parse)(await (0, import_promises17.readFile)(standardPath, "utf-8"));
25914
+ const derived = await deriveConfigForStandard(content);
25915
+ for (const path of derived.written) printInfo(` ${path} \u2713 (re-derived)`);
25916
+ for (const note of derived.notes) printWarn(` ${note}`);
25917
+ if (derived.written.length > 0) {
25918
+ const pushed = await pushStandardAndConfig(globals, { standard: false });
25919
+ for (const line of pushed.lines) printInfo(` ${line}`);
25920
+ }
25921
+ } catch (err) {
25922
+ printWarn(` Could not re-derive it: ${err.message}`);
25923
+ printInfo(" Fix it with: verity standard synthesize --config-only");
25924
+ }
25925
+ }
24554
25926
  var PHASE_TWO_ARTIFACTS = [
24555
25927
  {
24556
25928
  path: ".verity/standard.yaml",
@@ -24619,7 +25991,7 @@ async function handoffToSetup(enabled, claudeInstalled) {
24619
25991
  console.log(" Usually a minute or two. Quit any time \u2014 re-running /verity-setup resumes.");
24620
25992
  console.log("");
24621
25993
  const startedAt = Date.now();
24622
- const run2 = (0, import_node_child_process14.spawnSync)("claude", ["/verity-setup"], { stdio: "inherit" });
25994
+ const run2 = (0, import_node_child_process16.spawnSync)("claude", ["/verity-setup"], { stdio: "inherit" });
24623
25995
  if (run2.error) {
24624
25996
  printWarn(`Could not start Claude Code: ${run2.error.message}`);
24625
25997
  return instruct("start it yourself and run the skill there");
@@ -24628,18 +26000,18 @@ async function handoffToSetup(enabled, claudeInstalled) {
24628
26000
  }
24629
26001
  async function installSkills(force, step) {
24630
26002
  step("Installing skills");
24631
- const dataDir = resolveDataDir();
24632
- const skillsSource = (0, import_node_path29.join)(dataDir, "skills");
26003
+ const dataDir = resolveDataDir2();
26004
+ const skillsSource = (0, import_node_path33.join)(dataDir, "skills");
24633
26005
  const skillsDest = ".claude/skills";
24634
26006
  let skillsInstalled = 0;
24635
26007
  for (const skill of SKILLS) {
24636
- const src = (0, import_node_path29.join)(skillsSource, skill);
24637
- const dest = (0, import_node_path29.join)(skillsDest, skill);
24638
- if (!(0, import_node_fs46.existsSync)(src)) {
26008
+ const src = (0, import_node_path33.join)(skillsSource, skill);
26009
+ const dest = (0, import_node_path33.join)(skillsDest, skill);
26010
+ if (!(0, import_node_fs51.existsSync)(src)) {
24639
26011
  printWarn(` Skill data not found: ${skill}`);
24640
26012
  continue;
24641
26013
  }
24642
- if ((0, import_node_fs46.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) {
26014
+ if ((0, import_node_fs51.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) {
24643
26015
  skillsInstalled++;
24644
26016
  continue;
24645
26017
  }
@@ -24678,17 +26050,73 @@ async function reconcileOwnWiring(moments) {
24678
26050
  printWarn(" Enable one: verity hooks install --moments stop");
24679
26051
  }
24680
26052
  }
26053
+ async function checkPrerequisites(step) {
26054
+ step("Checking prerequisites");
26055
+ const prereqs = await checkPrereqs({ install: true });
26056
+ for (const c of prereqs.checks) {
26057
+ if (c.status === "ok") {
26058
+ if (c.justInstalled) continue;
26059
+ printInfo(` ${c.label} ${c.detail} \u2713`);
26060
+ } else {
26061
+ printWarn(` ${c.label}: ${c.detail}`);
26062
+ if (c.remedy) printWarn(` ${c.remedy}`);
26063
+ }
26064
+ }
26065
+ if (prereqs.blocked) {
26066
+ printError("A required prerequisite is missing \u2014 cannot continue.");
26067
+ process.exit(1);
26068
+ }
26069
+ return prereqs.checks.some((c) => c.id === "claude" && c.status === "ok");
26070
+ }
26071
+ async function scaffoldProject(step, defaultsOnly) {
26072
+ step("Knowledge base, .gitignore and CLAUDE.md");
26073
+ await (0, import_promises17.mkdir)(VERITY_DIR, { recursive: true });
26074
+ await ensureMemoryDir();
26075
+ const ignoreResult = ensureVerityGitignore();
26076
+ if (ignoreResult === "failed") {
26077
+ printWarn(" .gitignore: could not write the Verity block \u2014 add it manually");
26078
+ printWarn(" (.verity/ holds copies of analyzed files, including any secret the gate flagged)");
26079
+ } else if (ignoreResult === "conflict") {
26080
+ printWarn(" .gitignore: the Verity block is in place but git still ignores .verity/standard.yaml");
26081
+ printWarn(" Something outside this file covers it \u2014 a global (~/.gitignore) or nested");
26082
+ printWarn(" .gitignore, or a pattern we do not recognise. Check: git check-ignore -v .verity/standard.yaml");
26083
+ printWarn(" Until it is fixed, the Standard and the knowledge graph cannot be committed.");
26084
+ } else if (ignoreResult === "repaired") {
26085
+ printInfo(" .gitignore: rewrote `.verity/` to `.verity/*` so the standard stays committable \u2713");
26086
+ } else {
26087
+ printInfo(` .gitignore: Verity block ${ignoreResult === "added" ? "added" : "already covered"} \u2713`);
26088
+ }
26089
+ const tracked = committedVerityState();
26090
+ if (tracked.length > 0) {
26091
+ printWarn(` ${tracked.length} Verity state file(s) are tracked in git (e.g. ${tracked[0]}).`);
26092
+ const untrack = defaultsOnly ? false : await promptYes(" Untrack them now (files stay on disk)? [Y/n] ", { nonInteractive: false });
26093
+ if (untrack) {
26094
+ const result = untrackVerityState();
26095
+ if (result === "untracked") printInfo(" Untracked (staged) \u2014 commit to finish \u2713");
26096
+ else if (result === "failed") printWarn(' Could not untrack \u2014 run "git rm -r --cached .verity" manually');
26097
+ } else {
26098
+ printWarn(" Left tracked. Fix with: git rm -r --cached .verity && git add .verity/standard.yaml .verity/memory");
26099
+ }
26100
+ }
26101
+ try {
26102
+ await ensureClaudeMdPointer();
26103
+ printInfo(" CLAUDE.md instructions \u2713");
26104
+ } catch (err) {
26105
+ printWarn(` Could not update CLAUDE.md: ${err.message}`);
26106
+ }
26107
+ }
24681
26108
  function registerInitCommand(program2) {
24682
26109
  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(
24683
26110
  "--plugin-mode",
24684
26111
  "The Claude Code plugin owns the skills and hooks: install neither, and remove any this project already has"
24685
- ).action(async (opts) => {
26112
+ ).option("--no-adopt", "Don't offer this repository's existing Standard from the service; synthesize a new one").action(async (opts) => {
24686
26113
  const force = opts.force ?? false;
24687
26114
  const wantsHandoff = opts.setup !== false;
26115
+ const wantsAdopt = opts.adopt !== false;
24688
26116
  const defaultsOnly = (opts.yes ?? false) || !interactive();
24689
26117
  const pluginMode = opts.pluginMode ?? pluginActiveHere();
24690
26118
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
24691
- const isProject = projectMarkers.some((m) => (0, import_node_fs46.existsSync)(m));
26119
+ const isProject = projectMarkers.some((m) => (0, import_node_fs51.existsSync)(m));
24692
26120
  if (!isProject) {
24693
26121
  printError("No project detected in the current directory.");
24694
26122
  printInfo('Run "verity init" from your project root.');
@@ -24703,7 +26131,7 @@ function registerInitCommand(program2) {
24703
26131
  } else {
24704
26132
  printPhase(1, 2, "this machine", "prerequisites \xB7 skills \xB7 hooks \xB7 sign-in");
24705
26133
  }
24706
- const TOTAL_STEPS = 8;
26134
+ const TOTAL_STEPS = 9;
24707
26135
  let stepNo = 0;
24708
26136
  const step = (label2) => {
24709
26137
  stepNo++;
@@ -24719,22 +26147,7 @@ function registerInitCommand(program2) {
24719
26147
  }
24720
26148
  console.log("");
24721
26149
  }
24722
- step("Checking prerequisites");
24723
- const prereqs = await checkPrereqs({ install: true });
24724
- for (const c of prereqs.checks) {
24725
- if (c.status === "ok") {
24726
- if (c.justInstalled) continue;
24727
- printInfo(` ${c.label} ${c.detail} \u2713`);
24728
- } else {
24729
- printWarn(` ${c.label}: ${c.detail}`);
24730
- if (c.remedy) printWarn(` ${c.remedy}`);
24731
- }
24732
- }
24733
- if (prereqs.blocked) {
24734
- printError("A required prerequisite is missing \u2014 cannot continue.");
24735
- process.exit(1);
24736
- }
24737
- const claudeInstalled = prereqs.checks.some((c) => c.id === "claude" && c.status === "ok");
26150
+ const claudeInstalled = await checkPrerequisites(step);
24738
26151
  console.log("");
24739
26152
  if (pluginMode) {
24740
26153
  printInfo("Skipping skills \u2014 the Verity plugin provides them, namespaced as /verity:<name>.");
@@ -24748,43 +26161,9 @@ function registerInitCommand(program2) {
24748
26161
  if (defaultsOnly) {
24749
26162
  printInfo(` intensity: ${intensity} \xB7 moments: ${moments.join(", ") || "none"} (no questions asked)`);
24750
26163
  }
24751
- step("Knowledge base, .gitignore and CLAUDE.md");
24752
- await (0, import_promises14.mkdir)(VERITY_DIR, { recursive: true });
24753
- await ensureMemoryDir();
24754
- const ignoreResult = ensureVerityGitignore();
24755
- if (ignoreResult === "failed") {
24756
- printWarn(" .gitignore: could not write the Verity block \u2014 add it manually");
24757
- printWarn(" (.verity/ holds copies of analyzed files, including any secret the gate flagged)");
24758
- } else if (ignoreResult === "conflict") {
24759
- printWarn(" .gitignore: the Verity block is in place but git still ignores .verity/standard.yaml");
24760
- printWarn(" Something outside this file covers it \u2014 a global (~/.gitignore) or nested");
24761
- printWarn(" .gitignore, or a pattern we do not recognise. Check: git check-ignore -v .verity/standard.yaml");
24762
- printWarn(" Until it is fixed, the Standard and the knowledge graph cannot be committed.");
24763
- } else if (ignoreResult === "repaired") {
24764
- printInfo(" .gitignore: rewrote `.verity/` to `.verity/*` so the standard stays committable \u2713");
24765
- } else {
24766
- printInfo(` .gitignore: Verity block ${ignoreResult === "added" ? "added" : "already covered"} \u2713`);
24767
- }
24768
- const tracked = committedVerityState();
24769
- if (tracked.length > 0) {
24770
- printWarn(` ${tracked.length} Verity state file(s) are tracked in git (e.g. ${tracked[0]}).`);
24771
- const untrack = defaultsOnly ? false : await promptYes(" Untrack them now (files stay on disk)? [Y/n] ", { nonInteractive: false });
24772
- if (untrack) {
24773
- const result = untrackVerityState();
24774
- if (result === "untracked") printInfo(" Untracked (staged) \u2014 commit to finish \u2713");
24775
- else if (result === "failed") printWarn(' Could not untrack \u2014 run "git rm -r --cached .verity" manually');
24776
- } else {
24777
- printWarn(" Left tracked. Fix with: git rm -r --cached .verity && git add .verity/standard.yaml .verity/memory");
24778
- }
24779
- }
24780
- try {
24781
- await ensureClaudeMdPointer();
24782
- printInfo(" CLAUDE.md instructions \u2713");
24783
- } catch (err) {
24784
- printWarn(` Could not update CLAUDE.md: ${err.message}`);
24785
- }
24786
- const globalVerityDir = (0, import_node_path29.join)(process.env.HOME ?? "", ".verity");
24787
- await (0, import_promises14.mkdir)(globalVerityDir, { recursive: true });
26164
+ await scaffoldProject(step, defaultsOnly);
26165
+ const globalVerityDir = (0, import_node_path33.join)(process.env.HOME ?? "", ".verity");
26166
+ await (0, import_promises17.mkdir)(globalVerityDir, { recursive: true });
24788
26167
  console.log("");
24789
26168
  step("Wiring Claude Code hooks");
24790
26169
  const gitMoments = [
@@ -24836,6 +26215,27 @@ function registerInitCommand(program2) {
24836
26215
  printWarn('Telemetry needs a Verity token \u2014 run "verity login", then "verity telemetry install".');
24837
26216
  }
24838
26217
  }
26218
+ step("Your project's Standard");
26219
+ let haveStandard = false;
26220
+ if ((0, import_node_fs51.existsSync)(projectPath(STANDARD_FILE))) {
26221
+ printInfo(" This project already has .verity/standard.yaml \u2014 keeping it.");
26222
+ haveStandard = true;
26223
+ } else {
26224
+ haveStandard = await maybeAdoptRemoteSetup({
26225
+ globals: program2.opts(),
26226
+ defaultsOnly,
26227
+ previous,
26228
+ adopt: wantsAdopt
26229
+ });
26230
+ }
26231
+ if (!haveStandard) {
26232
+ haveStandard = await synthesizeLocally({
26233
+ globals: program2.opts(),
26234
+ intensity
26235
+ });
26236
+ } else {
26237
+ await healStaleAnalysisConfig(program2.opts());
26238
+ }
24839
26239
  step("Recording your answers");
24840
26240
  try {
24841
26241
  await writeSetupState({
@@ -24844,7 +26244,7 @@ function registerInitCommand(program2) {
24844
26244
  ...telemetryChoice ? { telemetry: telemetryChoice } : {},
24845
26245
  init: {
24846
26246
  completed_at: (/* @__PURE__ */ new Date()).toISOString(),
24847
- cli_version: true ? "0.31.3" : "dev"
26247
+ cli_version: true ? "0.32.0-experimental.68ae2ee" : "dev"
24848
26248
  }
24849
26249
  });
24850
26250
  } catch (err) {
@@ -24861,19 +26261,30 @@ function registerInitCommand(program2) {
24861
26261
  console.log(" .claude/settings.json hooks, reconciled to your chosen moments");
24862
26262
  }
24863
26263
  console.log(" .verity/memory/ knowledge base (commit to git)");
26264
+ console.log(" .verity/standard.yaml the Standard the gate enforces");
26265
+ console.log(" .codacy/codacy.config.json static-analysis patterns (validated)");
26266
+ console.log(" VERITY.md project quality overview");
24864
26267
  console.log(" .verity/setup.json your answers, read by /verity-setup");
24865
26268
  console.log(" .gitignore Verity block (whitelist form)");
24866
26269
  console.log(" CLAUDE.md memory pointer, waive policy, reflection");
24867
26270
  console.log("");
24868
26271
  console.log(` Intensity: ${intensity} Moments: ${moments.join(", ") || "none"}`);
24869
- await handoffToSetup(wantsHandoff, claudeInstalled);
26272
+ console.log("");
26273
+ if (haveStandard) {
26274
+ printInfo("Setup complete \u2014 the gate is live on your next Claude Code session.");
26275
+ printInfo(' Add project-specific rules any time: edit .verity/standard.yaml, then "verity standard push".');
26276
+ printInfo(" Or have a model propose them: /verity-setup --force in Claude Code.");
26277
+ } else {
26278
+ printWarn("No Standard for this project yet \u2014 the gate cannot review until there is one.");
26279
+ await handoffToSetup(wantsHandoff, claudeInstalled);
26280
+ }
24870
26281
  console.log("");
24871
26282
  });
24872
26283
  }
24873
26284
 
24874
26285
  // src/commands/uninstall.ts
24875
- var import_node_fs47 = require("node:fs");
24876
- var import_node_path30 = require("node:path");
26286
+ var import_node_fs52 = require("node:fs");
26287
+ var import_node_path34 = require("node:path");
24877
26288
  var SKILL_NAMES = [
24878
26289
  "verity-setup",
24879
26290
  "verity-analyze",
@@ -24892,11 +26303,11 @@ function registerUninstallCommand(program2) {
24892
26303
  const actions = [];
24893
26304
  const skillsRoot = projectPath(".claude/skills");
24894
26305
  for (const name of SKILL_NAMES) {
24895
- const dir = (0, import_node_path30.join)(skillsRoot, name);
24896
- if ((0, import_node_fs47.existsSync)(dir)) {
26306
+ const dir = (0, import_node_path34.join)(skillsRoot, name);
26307
+ if ((0, import_node_fs52.existsSync)(dir)) {
24897
26308
  actions.push({
24898
26309
  label: `Remove .claude/skills/${name}/`,
24899
- apply: () => (0, import_node_fs47.rmSync)(dir, { recursive: true, force: true })
26310
+ apply: () => (0, import_node_fs52.rmSync)(dir, { recursive: true, force: true })
24900
26311
  });
24901
26312
  }
24902
26313
  }
@@ -24910,24 +26321,24 @@ function registerUninstallCommand(program2) {
24910
26321
  });
24911
26322
  }
24912
26323
  const verityDir = projectPath(VERITY_DIR);
24913
- if ((0, import_node_fs47.existsSync)(verityDir)) {
26324
+ if ((0, import_node_fs52.existsSync)(verityDir)) {
24914
26325
  actions.push({
24915
26326
  label: `Remove ${VERITY_DIR}/`,
24916
- apply: () => (0, import_node_fs47.rmSync)(verityDir, { recursive: true, force: true })
26327
+ apply: () => (0, import_node_fs52.rmSync)(verityDir, { recursive: true, force: true })
24917
26328
  });
24918
26329
  }
24919
26330
  if (!keepVerityMd) {
24920
26331
  const verityMd = projectPath(VERITY_MD_FILE);
24921
- if ((0, import_node_fs47.existsSync)(verityMd)) {
26332
+ if ((0, import_node_fs52.existsSync)(verityMd)) {
24922
26333
  actions.push({
24923
26334
  label: `Remove ${VERITY_MD_FILE}`,
24924
- apply: () => (0, import_node_fs47.rmSync)(verityMd, { force: true })
26335
+ apply: () => (0, import_node_fs52.rmSync)(verityMd, { force: true })
24925
26336
  });
24926
26337
  }
24927
26338
  }
24928
26339
  const cleanupEmptyDir = (path) => {
24929
- if ((0, import_node_fs47.existsSync)(path) && (0, import_node_fs47.statSync)(path).isDirectory() && (0, import_node_fs47.readdirSync)(path).length === 0) {
24930
- (0, import_node_fs47.rmdirSync)(path);
26340
+ if ((0, import_node_fs52.existsSync)(path) && (0, import_node_fs52.statSync)(path).isDirectory() && (0, import_node_fs52.readdirSync)(path).length === 0) {
26341
+ (0, import_node_fs52.rmdirSync)(path);
24931
26342
  }
24932
26343
  };
24933
26344
  actions.push({
@@ -24938,11 +26349,11 @@ function registerUninstallCommand(program2) {
24938
26349
  }
24939
26350
  });
24940
26351
  const home = process.env.HOME ?? "";
24941
- const globalVerityDir = (0, import_node_path30.join)(home, ".verity");
24942
- if (purgeGlobal && (0, import_node_fs47.existsSync)(globalVerityDir)) {
26352
+ const globalVerityDir = (0, import_node_path34.join)(home, ".verity");
26353
+ if (purgeGlobal && (0, import_node_fs52.existsSync)(globalVerityDir)) {
24943
26354
  actions.push({
24944
26355
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
24945
- apply: () => (0, import_node_fs47.rmSync)(globalVerityDir, { recursive: true, force: true })
26356
+ apply: () => (0, import_node_fs52.rmSync)(globalVerityDir, { recursive: true, force: true })
24946
26357
  });
24947
26358
  }
24948
26359
  if (actions.length === 0) {
@@ -25136,8 +26547,8 @@ function registerTaskCommands(program2) {
25136
26547
  }
25137
26548
 
25138
26549
  // src/commands/reset.ts
25139
- var import_node_fs48 = require("node:fs");
25140
- var import_node_path31 = require("node:path");
26550
+ var import_node_fs53 = require("node:fs");
26551
+ var import_node_path35 = require("node:path");
25141
26552
  function registerResetCommand(program2) {
25142
26553
  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) => {
25143
26554
  const globals = program2.opts();
@@ -25174,11 +26585,11 @@ function registerResetCommand(program2) {
25174
26585
  }
25175
26586
  const cacheDir = projectPath(CACHE_DIR);
25176
26587
  let purged = 0;
25177
- if ((0, import_node_fs48.existsSync)(cacheDir)) {
25178
- for (const entry of (0, import_node_fs48.readdirSync)(cacheDir)) {
26588
+ if ((0, import_node_fs53.existsSync)(cacheDir)) {
26589
+ for (const entry of (0, import_node_fs53.readdirSync)(cacheDir)) {
25179
26590
  if (entry.startsWith("pending-")) {
25180
26591
  try {
25181
- (0, import_node_fs48.unlinkSync)((0, import_node_path31.join)(cacheDir, entry));
26592
+ (0, import_node_fs53.unlinkSync)((0, import_node_path35.join)(cacheDir, entry));
25182
26593
  purged++;
25183
26594
  } catch {
25184
26595
  }
@@ -25193,19 +26604,19 @@ function registerResetCommand(program2) {
25193
26604
  projectPath(`${VERITY_DIR}/.last-analysis`)
25194
26605
  ];
25195
26606
  for (const file of filesToClear) {
25196
- if ((0, import_node_fs48.existsSync)(file)) {
26607
+ if ((0, import_node_fs53.existsSync)(file)) {
25197
26608
  try {
25198
- (0, import_node_fs48.writeFileSync)(file, "");
26609
+ (0, import_node_fs53.writeFileSync)(file, "");
25199
26610
  } catch {
25200
26611
  }
25201
26612
  }
25202
26613
  }
25203
26614
  if (opts.all) {
25204
26615
  const logsDir = projectPath(`${VERITY_DIR}/.logs`);
25205
- if ((0, import_node_fs48.existsSync)(logsDir)) {
25206
- for (const entry of (0, import_node_fs48.readdirSync)(logsDir)) {
26616
+ if ((0, import_node_fs53.existsSync)(logsDir)) {
26617
+ for (const entry of (0, import_node_fs53.readdirSync)(logsDir)) {
25207
26618
  try {
25208
- (0, import_node_fs48.unlinkSync)((0, import_node_path31.join)(logsDir, entry));
26619
+ (0, import_node_fs53.unlinkSync)((0, import_node_path35.join)(logsDir, entry));
25209
26620
  } catch {
25210
26621
  }
25211
26622
  }
@@ -25513,8 +26924,8 @@ function registerTelemetryCommands(program2) {
25513
26924
  }
25514
26925
 
25515
26926
  // src/cli.ts
25516
- program.name("verity").description("CLI for Verity quality gate service").version("0.31.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) => {
25517
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.3");
26927
+ program.name("verity").description("CLI for Verity quality gate service").version("0.32.0-experimental.68ae2ee").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) => {
26928
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.32.0-experimental.68ae2ee");
25518
26929
  setUserNamedServiceUrl(program.opts().serviceUrl);
25519
26930
  try {
25520
26931
  await foldLegacyLocalCredential();