@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/CHANGELOG.md +146 -0
- package/README.md +50 -5
- package/bin/verity.js +1817 -406
- package/data/skills/verity-setup/SKILL.md +24 -1
- package/data/skills/verity-setup/patterns-reference.yaml +79 -54
- package/package.json +1 -1
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(
|
|
679
|
-
this.conflictsWith = this.conflictsWith.concat(
|
|
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(
|
|
1253
|
-
|
|
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
|
|
10959
|
-
if (REVIEWABLE_FILENAMES.has(
|
|
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
|
|
16769
|
+
var import_node_path20 = require("node:path");
|
|
16763
16770
|
var import_yaml = __toESM(require_dist());
|
|
16764
16771
|
|
|
16765
|
-
// src/lib/
|
|
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
|
|
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,
|
|
17709
|
+
if (!(0, import_node_fs26.existsSync)(file)) return EMPTY;
|
|
16860
17710
|
try {
|
|
16861
|
-
return parseVerityIgnore((0,
|
|
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 &&
|
|
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,
|
|
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,
|
|
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
|
-
|
|
16930
|
-
|
|
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
|
|
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,
|
|
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
|
|
18163
|
+
var import_node_fs30 = require("node:fs");
|
|
17192
18164
|
|
|
17193
18165
|
// src/lib/debounce.ts
|
|
17194
|
-
var
|
|
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,
|
|
18174
|
+
if (!(0, import_node_fs29.existsSync)(file)) return null;
|
|
17203
18175
|
try {
|
|
17204
|
-
const lastTs = parseInt((0,
|
|
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,
|
|
18189
|
+
if (!(0, import_node_fs29.existsSync)(file)) return null;
|
|
17218
18190
|
let debounceTime;
|
|
17219
18191
|
try {
|
|
17220
|
-
debounceTime = (0,
|
|
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,
|
|
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,
|
|
17245
|
-
hash.update((0,
|
|
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,
|
|
18227
|
+
if ((0, import_node_fs29.existsSync)(file)) {
|
|
17256
18228
|
try {
|
|
17257
|
-
const storedHash = (0,
|
|
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,
|
|
17268
|
-
(0,
|
|
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,
|
|
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,
|
|
18247
|
+
if (!(0, import_node_fs29.existsSync)(file)) return files;
|
|
17276
18248
|
let debounceTime;
|
|
17277
18249
|
try {
|
|
17278
|
-
debounceTime = (0,
|
|
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,
|
|
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,
|
|
18269
|
+
if (!(0, import_node_fs29.existsSync)(ITERATION_FILE)) return NO_BLOCKS;
|
|
17298
18270
|
try {
|
|
17299
|
-
const stored = (0,
|
|
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,
|
|
17346
|
-
(0,
|
|
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,
|
|
18423
|
+
if (!(0, import_node_fs30.existsSync)(file)) return null;
|
|
17452
18424
|
try {
|
|
17453
|
-
const o = JSON.parse((0,
|
|
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,
|
|
17478
|
-
(0,
|
|
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
|
|
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,
|
|
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
|
|
18671
|
-
var
|
|
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
|
|
18674
|
-
var
|
|
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,
|
|
18728
|
-
dir = (0,
|
|
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,
|
|
18747
|
-
dir = (0,
|
|
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,
|
|
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
|
|
19785
|
+
return import_node_fs31.realpathSync.native(p);
|
|
18811
19786
|
} catch {
|
|
18812
|
-
return (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
18879
|
-
if (mergeHead && (0,
|
|
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
|
|
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,
|
|
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,
|
|
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.
|
|
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
|
|
19578
|
-
var
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
19896
|
-
var
|
|
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,
|
|
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,
|
|
19935
|
-
const bytesRead = (0,
|
|
19936
|
-
(0,
|
|
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,
|
|
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,
|
|
20942
|
+
const entries = (0, import_node_fs34.readdirSync)(dir, { withFileTypes: true });
|
|
19968
20943
|
for (const entry of entries) {
|
|
19969
|
-
const fullPath = (0,
|
|
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,
|
|
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,
|
|
20961
|
+
if (!(0, import_node_fs34.existsSync)(plansDir)) continue;
|
|
19987
20962
|
try {
|
|
19988
|
-
for (const f of (0,
|
|
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,
|
|
20966
|
+
const fullPath = (0, import_node_path23.join)(plansDir, f);
|
|
19992
20967
|
try {
|
|
19993
|
-
const stat3 = (0,
|
|
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,
|
|
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,
|
|
20996
|
+
if (!(0, import_node_fs34.existsSync)(path)) continue;
|
|
20022
20997
|
try {
|
|
20023
|
-
const stat3 = (0,
|
|
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,
|
|
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
|
|
20203
|
-
var
|
|
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 =
|
|
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,
|
|
20429
|
-
ingest((0,
|
|
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,
|
|
20436
|
-
(0,
|
|
20437
|
-
(0,
|
|
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,
|
|
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
|
|
21419
|
+
const walk2 = (d, depth) => {
|
|
20445
21420
|
if (depth > 4) return;
|
|
20446
|
-
for (const e of (0,
|
|
20447
|
-
const p = (0,
|
|
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
|
-
|
|
21424
|
+
walk2(p, depth + 1);
|
|
20450
21425
|
} else if (e.name.startsWith("agent-") && e.name.endsWith(".jsonl")) {
|
|
20451
21426
|
try {
|
|
20452
|
-
const st = (0,
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
|
20822
|
-
var
|
|
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,
|
|
21803
|
+
for (const entry of (0, import_node_fs36.readdirSync)(dir)) {
|
|
20829
21804
|
if (!entry.startsWith("pending-")) continue;
|
|
20830
|
-
const path = (0,
|
|
21805
|
+
const path = (0, import_node_path25.join)(dir, entry);
|
|
20831
21806
|
try {
|
|
20832
|
-
const stat3 = (0,
|
|
21807
|
+
const stat3 = (0, import_node_fs36.statSync)(path);
|
|
20833
21808
|
if (stat3.mtimeMs < cutoff) {
|
|
20834
|
-
(0,
|
|
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
|
|
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,
|
|
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) => !
|
|
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 &&
|
|
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
|
|
20997
|
-
var
|
|
20998
|
-
var
|
|
20999
|
-
var
|
|
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,
|
|
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,
|
|
21243
|
-
standardDoc = (0,
|
|
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,
|
|
22224
|
+
if ((0, import_node_fs38.existsSync)("README.md")) {
|
|
21250
22225
|
try {
|
|
21251
|
-
readmeContent = await (0,
|
|
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,
|
|
22232
|
+
if ((0, import_node_fs38.existsSync)(p)) {
|
|
21258
22233
|
try {
|
|
21259
|
-
claudeMdContent = await (0,
|
|
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,
|
|
21281
|
-
if ((0,
|
|
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,
|
|
21324
|
-
await (0,
|
|
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
|
|
21337
|
-
var
|
|
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,
|
|
21348
|
-
const hasStandard = (0,
|
|
21349
|
-
const alreadyTried = (0,
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
22556
|
+
if (!(0, import_node_fs40.existsSync)(file)) return null;
|
|
21582
22557
|
try {
|
|
21583
|
-
const o = JSON.parse((0,
|
|
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,
|
|
21598
|
-
(0,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
22042
|
-
var
|
|
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,
|
|
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 (!(
|
|
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
|
|
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,
|
|
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,
|
|
23837
|
+
if (!(0, import_node_fs44.existsSync)(p)) continue;
|
|
22851
23838
|
try {
|
|
22852
|
-
const { readFileSync:
|
|
22853
|
-
const content =
|
|
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
|
|
22911
|
-
var
|
|
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,
|
|
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,
|
|
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,
|
|
23965
|
+
(0, import_node_fs45.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
22979
23966
|
const map = readIterMap();
|
|
22980
23967
|
map[moment] = count;
|
|
22981
|
-
(0,
|
|
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,
|
|
23978
|
+
if ((0, import_node_fs45.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs45.unlinkSync)(GUARD_ITER_FILE);
|
|
22992
23979
|
} else {
|
|
22993
|
-
(0,
|
|
22994
|
-
(0,
|
|
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,
|
|
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) ||
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
23481
|
-
var
|
|
23482
|
-
var
|
|
23483
|
-
var
|
|
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
|
|
24549
|
+
var import_node_fs48 = require("node:fs");
|
|
23562
24550
|
|
|
23563
24551
|
// src/lib/prereqs.ts
|
|
23564
|
-
var
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
24665
|
+
var import_promises15 = require("node:fs/promises");
|
|
23678
24666
|
|
|
23679
24667
|
// src/lib/gitignore.ts
|
|
23680
|
-
var
|
|
23681
|
-
var
|
|
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
|
|
24683
|
+
function isIgnored3(path) {
|
|
23696
24684
|
try {
|
|
23697
|
-
(0,
|
|
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 =
|
|
23705
|
-
const standard =
|
|
23706
|
-
const node =
|
|
23707
|
-
const futureState =
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
23885
|
-
analysisConfig:
|
|
23886
|
-
verityMd: (0,
|
|
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
|
-
|
|
23894
|
-
|
|
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
|
-
|
|
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
|
|
23960
|
-
var
|
|
23961
|
-
var
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
23998
|
-
const verityDir = (0,
|
|
23999
|
-
if ((0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
24053
|
-
const verityCreds = (0,
|
|
24054
|
-
if (!(0,
|
|
24055
|
-
if (!(0,
|
|
24056
|
-
(0,
|
|
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,
|
|
24079
|
-
const hadLegacyBlock = (0,
|
|
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,
|
|
24090
|
-
const verityMd = (0,
|
|
24091
|
-
if (!(0,
|
|
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,
|
|
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,
|
|
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,
|
|
24108
|
-
if (!(0,
|
|
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,
|
|
25145
|
+
(0, import_node_fs49.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
|
|
24157
25146
|
}
|
|
24158
|
-
(0,
|
|
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,
|
|
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,
|
|
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,
|
|
25167
|
+
(0, import_node_fs49.renameSync)(from, to);
|
|
24179
25168
|
} catch (err) {
|
|
24180
25169
|
if (err.code !== "EXDEV") throw err;
|
|
24181
|
-
(0,
|
|
24182
|
-
(0,
|
|
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,
|
|
25176
|
+
(0, import_node_fs49.renameSync)(from, to);
|
|
24188
25177
|
} catch (err) {
|
|
24189
25178
|
if (err.code !== "EXDEV") throw err;
|
|
24190
|
-
(0,
|
|
24191
|
-
(0,
|
|
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
|
|
24197
|
-
const srcDir = (0,
|
|
24198
|
-
for (const entry of (0,
|
|
24199
|
-
const rel = relDir ? (0,
|
|
24200
|
-
const src = (0,
|
|
24201
|
-
const dest = (0,
|
|
24202
|
-
if ((0,
|
|
24203
|
-
|
|
24204
|
-
} else if (!(0,
|
|
24205
|
-
(0,
|
|
24206
|
-
(0,
|
|
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
|
-
|
|
25200
|
+
walk2("");
|
|
24212
25201
|
return copied;
|
|
24213
25202
|
}
|
|
24214
25203
|
async function needsMigration(root = repoRoot()) {
|
|
24215
|
-
const gateDir = (0,
|
|
24216
|
-
const verityDir = (0,
|
|
24217
|
-
if ((0,
|
|
24218
|
-
if ((0,
|
|
24219
|
-
if ((0,
|
|
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,
|
|
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,
|
|
24227
|
-
if ((0,
|
|
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,
|
|
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
|
|
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 =
|
|
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,
|
|
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
|
|
25689
|
+
function resolveDataDir2() {
|
|
24447
25690
|
const candidates = [
|
|
24448
|
-
(0,
|
|
25691
|
+
(0, import_node_path33.join)(__dirname, "..", "data"),
|
|
24449
25692
|
// installed: node_modules/@codacy/verity-cli/data
|
|
24450
|
-
(0,
|
|
25693
|
+
(0, import_node_path33.join)(__dirname, "..", "..", "data"),
|
|
24451
25694
|
// edge case: nested resolution
|
|
24452
|
-
(0,
|
|
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,
|
|
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,
|
|
24466
|
-
await (0,
|
|
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
|
|
24472
|
-
for (const e of (0,
|
|
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())
|
|
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
|
-
|
|
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,
|
|
24486
|
-
const b = await (0,
|
|
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 :
|
|
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,
|
|
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 =
|
|
24632
|
-
const skillsSource = (0,
|
|
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,
|
|
24637
|
-
const dest = (0,
|
|
24638
|
-
if (!(0,
|
|
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,
|
|
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,
|
|
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 =
|
|
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
|
|
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
|
|
24752
|
-
|
|
24753
|
-
await
|
|
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.
|
|
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
|
-
|
|
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
|
|
24876
|
-
var
|
|
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,
|
|
24896
|
-
if ((0,
|
|
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,
|
|
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,
|
|
26324
|
+
if ((0, import_node_fs52.existsSync)(verityDir)) {
|
|
24914
26325
|
actions.push({
|
|
24915
26326
|
label: `Remove ${VERITY_DIR}/`,
|
|
24916
|
-
apply: () => (0,
|
|
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,
|
|
26332
|
+
if ((0, import_node_fs52.existsSync)(verityMd)) {
|
|
24922
26333
|
actions.push({
|
|
24923
26334
|
label: `Remove ${VERITY_MD_FILE}`,
|
|
24924
|
-
apply: () => (0,
|
|
26335
|
+
apply: () => (0, import_node_fs52.rmSync)(verityMd, { force: true })
|
|
24925
26336
|
});
|
|
24926
26337
|
}
|
|
24927
26338
|
}
|
|
24928
26339
|
const cleanupEmptyDir = (path) => {
|
|
24929
|
-
if ((0,
|
|
24930
|
-
(0,
|
|
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,
|
|
24942
|
-
if (purgeGlobal && (0,
|
|
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,
|
|
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
|
|
25140
|
-
var
|
|
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,
|
|
25178
|
-
for (const entry of (0,
|
|
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,
|
|
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,
|
|
26607
|
+
if ((0, import_node_fs53.existsSync)(file)) {
|
|
25197
26608
|
try {
|
|
25198
|
-
(0,
|
|
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,
|
|
25206
|
-
for (const entry of (0,
|
|
26616
|
+
if ((0, import_node_fs53.existsSync)(logsDir)) {
|
|
26617
|
+
for (const entry of (0, import_node_fs53.readdirSync)(logsDir)) {
|
|
25207
26618
|
try {
|
|
25208
|
-
(0,
|
|
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.
|
|
25517
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.
|
|
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();
|