@codacy/verity-cli 0.33.2 → 0.33.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/bin/verity.js +116 -22
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -64,6 +64,32 @@ installs are untouched.
|
|
|
64
64
|
which is the ordinary case for someone whose first Verity install *is* the
|
|
65
65
|
plugin.
|
|
66
66
|
|
|
67
|
+
## [0.33.3] — 2026-09-24
|
|
68
|
+
|
|
69
|
+
**The knowledge seed runs once per project, not once per checkout.**
|
|
70
|
+
|
|
71
|
+
- **`verity memory seed` and the analyze auto-seed no longer re-seed a project
|
|
72
|
+
that is already seeded.** The old "already seeded" check looked for a file the
|
|
73
|
+
server never writes (`domain/project-overview.md`; nodes are named
|
|
74
|
+
`n{seq}-{slug}.md`), so it never matched, and an empty, not-yet-pulled
|
|
75
|
+
`.verity/memory/` in a fresh clone or worktree read as "never seeded". Every
|
|
76
|
+
new checkout posted the whole batch again and the server recorded every copy.
|
|
77
|
+
One project reached 13,497 nodes, 11,214 of them copies of 38 seed titles, and
|
|
78
|
+
every analyze for it died on request size (HTTP 546, VRT-243). The CLI now
|
|
79
|
+
checks the frontmatter of what is on disk, then asks the server which seed
|
|
80
|
+
titles the project has, and posts only the titles that are missing — so a
|
|
81
|
+
seed that only half landed is completed, and a seeded project is never
|
|
82
|
+
re-posted.
|
|
83
|
+
- **The server refuses to record the same seed twice.** Posting a seed node whose
|
|
84
|
+
title and body the project already carries returns the existing node
|
|
85
|
+
(`200`, `deduplicated: true`) instead of minting another. This holds for old
|
|
86
|
+
CLIs, cleared markers and parallel worktrees alike. A seed whose body changed
|
|
87
|
+
(the README was edited) is still a new node, and an archived seed can be
|
|
88
|
+
recorded again.
|
|
89
|
+
- `verity memory seed` reports nodes that already existed apart from nodes it
|
|
90
|
+
created, and `--force` re-materializes the existing nodes locally rather than
|
|
91
|
+
duplicating them.
|
|
92
|
+
|
|
67
93
|
## [0.33.2] — 2026-09-23
|
|
68
94
|
|
|
69
95
|
**Which build is gating, said out loud.**
|
package/bin/verity.js
CHANGED
|
@@ -22159,7 +22159,7 @@ function channelSilence(input) {
|
|
|
22159
22159
|
// src/lib/cli-version.ts
|
|
22160
22160
|
function cliVersion() {
|
|
22161
22161
|
try {
|
|
22162
|
-
return true ? "0.33.
|
|
22162
|
+
return true ? "0.33.3" : "dev";
|
|
22163
22163
|
} catch {
|
|
22164
22164
|
return "dev";
|
|
22165
22165
|
}
|
|
@@ -23870,6 +23870,73 @@ _Seeded from CLAUDE.md. Edit or archive if outdated._`,
|
|
|
23870
23870
|
}
|
|
23871
23871
|
|
|
23872
23872
|
// src/lib/seed-runner.ts
|
|
23873
|
+
var NODE_DIRS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
|
|
23874
|
+
function normalizeTitle(title) {
|
|
23875
|
+
return title.trim().replace(/\s+/g, " ").toLowerCase();
|
|
23876
|
+
}
|
|
23877
|
+
async function localSeedTitles(memoryDir3 = MEMORY_DIR) {
|
|
23878
|
+
const titles = /* @__PURE__ */ new Set();
|
|
23879
|
+
for (const dir of NODE_DIRS) {
|
|
23880
|
+
const full = (0, import_node_path32.join)(memoryDir3, dir);
|
|
23881
|
+
if (!(0, import_node_fs42.existsSync)(full)) continue;
|
|
23882
|
+
let files;
|
|
23883
|
+
try {
|
|
23884
|
+
files = await (0, import_promises15.readdir)(full);
|
|
23885
|
+
} catch {
|
|
23886
|
+
continue;
|
|
23887
|
+
}
|
|
23888
|
+
for (const file of files) {
|
|
23889
|
+
if (!file.endsWith(".md")) continue;
|
|
23890
|
+
let content;
|
|
23891
|
+
try {
|
|
23892
|
+
content = await (0, import_promises15.readFile)((0, import_node_path32.join)(full, file), "utf-8");
|
|
23893
|
+
} catch {
|
|
23894
|
+
continue;
|
|
23895
|
+
}
|
|
23896
|
+
if (!isSeedNodeMarkdown(content)) continue;
|
|
23897
|
+
const title = pickFrontmatterTitle(content);
|
|
23898
|
+
if (title) titles.add(normalizeTitle(title));
|
|
23899
|
+
}
|
|
23900
|
+
}
|
|
23901
|
+
return titles;
|
|
23902
|
+
}
|
|
23903
|
+
function frontmatterOf(content) {
|
|
23904
|
+
if (!content.startsWith("---")) return null;
|
|
23905
|
+
const end = content.indexOf("\n---", 3);
|
|
23906
|
+
return end === -1 ? content : content.slice(0, end);
|
|
23907
|
+
}
|
|
23908
|
+
function isSeedNodeMarkdown(content) {
|
|
23909
|
+
const frontmatter = frontmatterOf(content);
|
|
23910
|
+
return frontmatter !== null && /^created_by:\s*["']?seed["']?\s*$/m.test(frontmatter);
|
|
23911
|
+
}
|
|
23912
|
+
function pickFrontmatterTitle(content) {
|
|
23913
|
+
const frontmatter = frontmatterOf(content);
|
|
23914
|
+
const match = frontmatter?.match(/^title:[ \t]*(.*)$/m);
|
|
23915
|
+
if (!match) return null;
|
|
23916
|
+
const raw = match[1].trim();
|
|
23917
|
+
if (raw.startsWith('"')) {
|
|
23918
|
+
try {
|
|
23919
|
+
return String(JSON.parse(raw));
|
|
23920
|
+
} catch {
|
|
23921
|
+
return raw.slice(1, raw.endsWith('"') ? -1 : void 0).replace(/\\"/g, '"');
|
|
23922
|
+
}
|
|
23923
|
+
}
|
|
23924
|
+
return raw;
|
|
23925
|
+
}
|
|
23926
|
+
async function serverSeedTitles(opts) {
|
|
23927
|
+
const res = await apiRequest({
|
|
23928
|
+
method: "GET",
|
|
23929
|
+
path: "/compound/memory/nodes?created_by=seed&status=active&limit=100",
|
|
23930
|
+
serviceUrl: opts.serviceUrl,
|
|
23931
|
+
token: opts.token,
|
|
23932
|
+
verbose: opts.verbose,
|
|
23933
|
+
cmd: "memory_seed"
|
|
23934
|
+
});
|
|
23935
|
+
if (!res.ok || !Array.isArray(res.data.nodes)) return null;
|
|
23936
|
+
const titles = /* @__PURE__ */ new Set();
|
|
23937
|
+
for (const n of res.data.nodes) if (typeof n?.title === "string") titles.add(normalizeTitle(n.title));
|
|
23938
|
+
return titles;
|
|
23939
|
+
}
|
|
23873
23940
|
function renderNodeMarkdown(candidate, nodeId, createdAt) {
|
|
23874
23941
|
const fm = [
|
|
23875
23942
|
"---",
|
|
@@ -23894,14 +23961,14 @@ function renderNodeMarkdown(candidate, nodeId, createdAt) {
|
|
|
23894
23961
|
}
|
|
23895
23962
|
async function runSeed(opts) {
|
|
23896
23963
|
if (!(0, import_node_fs42.existsSync)(STANDARD_FILE)) {
|
|
23897
|
-
return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
23964
|
+
return { created: 0, existing: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
23898
23965
|
}
|
|
23899
23966
|
let standardDoc;
|
|
23900
23967
|
try {
|
|
23901
23968
|
const raw = await (0, import_promises15.readFile)(STANDARD_FILE, "utf-8");
|
|
23902
23969
|
standardDoc = (0, import_yaml4.parse)(raw);
|
|
23903
23970
|
} catch {
|
|
23904
|
-
return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
23971
|
+
return { created: 0, existing: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
23905
23972
|
}
|
|
23906
23973
|
const knowledgeSpec = standardDoc.knowledge_spec ?? {};
|
|
23907
23974
|
let readmeContent;
|
|
@@ -23934,19 +24001,33 @@ async function runSeed(opts) {
|
|
|
23934
24001
|
claudeMdContent
|
|
23935
24002
|
});
|
|
23936
24003
|
if (candidates2.length === 0) {
|
|
23937
|
-
return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
|
|
24004
|
+
return { created: 0, existing: 0, failed: 0, skipped: "no_candidates", candidates: [] };
|
|
23938
24005
|
}
|
|
23939
|
-
|
|
23940
|
-
if (
|
|
23941
|
-
|
|
24006
|
+
let toPost = candidates2;
|
|
24007
|
+
if (!opts.force) {
|
|
24008
|
+
const local = await localSeedTitles();
|
|
24009
|
+
toPost = toPost.filter((c) => !local.has(normalizeTitle(c.title)));
|
|
24010
|
+
if (toPost.length === 0) {
|
|
24011
|
+
return { created: 0, existing: 0, failed: 0, skipped: "already_seeded", candidates: candidates2 };
|
|
24012
|
+
}
|
|
24013
|
+
if (!opts.dryRun) {
|
|
24014
|
+
const remote = await serverSeedTitles(opts);
|
|
24015
|
+
if (remote) {
|
|
24016
|
+
toPost = toPost.filter((c) => !remote.has(normalizeTitle(c.title)));
|
|
24017
|
+
if (toPost.length === 0) {
|
|
24018
|
+
return { created: 0, existing: 0, failed: 0, skipped: "already_seeded", candidates: candidates2 };
|
|
24019
|
+
}
|
|
24020
|
+
}
|
|
24021
|
+
}
|
|
23942
24022
|
}
|
|
23943
24023
|
if (opts.dryRun) {
|
|
23944
|
-
return { created: 0, failed: 0, skipped: null, candidates: candidates2 };
|
|
24024
|
+
return { created: 0, existing: 0, failed: 0, skipped: null, candidates: candidates2 };
|
|
23945
24025
|
}
|
|
23946
24026
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
23947
24027
|
let created = 0;
|
|
24028
|
+
let existing = 0;
|
|
23948
24029
|
let failed = 0;
|
|
23949
|
-
for (const c of
|
|
24030
|
+
for (const c of toPost) {
|
|
23950
24031
|
const res = await apiRequest({
|
|
23951
24032
|
method: "POST",
|
|
23952
24033
|
path: "/compound/memory/nodes",
|
|
@@ -23978,17 +24059,26 @@ async function runSeed(opts) {
|
|
|
23978
24059
|
failed++;
|
|
23979
24060
|
continue;
|
|
23980
24061
|
}
|
|
24062
|
+
const deduplicated = res.data.deduplicated === true;
|
|
24063
|
+
if (deduplicated && (0, import_node_fs42.existsSync)(targetPath)) {
|
|
24064
|
+
existing++;
|
|
24065
|
+
continue;
|
|
24066
|
+
}
|
|
24067
|
+
const content = typeof res.data.content === "string" && res.data.content.length > 0 ? res.data.content : renderNodeMarkdown(c, nodeId, createdAt);
|
|
23981
24068
|
try {
|
|
23982
24069
|
await (0, import_promises15.mkdir)((0, import_node_path32.dirname)(targetPath), { recursive: true });
|
|
23983
|
-
await (0, import_promises15.writeFile)(targetPath,
|
|
23984
|
-
|
|
23985
|
-
|
|
24070
|
+
await (0, import_promises15.writeFile)(targetPath, content);
|
|
24071
|
+
if (deduplicated) existing++;
|
|
24072
|
+
else {
|
|
24073
|
+
created++;
|
|
24074
|
+
opts.onCreated?.(nodeId, filePathRel, c);
|
|
24075
|
+
}
|
|
23986
24076
|
} catch (err) {
|
|
23987
24077
|
opts.onFailed?.(c, `Server accepted "${c.title}" but couldn't write local file: ${err.message}`);
|
|
23988
24078
|
failed++;
|
|
23989
24079
|
}
|
|
23990
24080
|
}
|
|
23991
|
-
return { created, failed, skipped: null, candidates: candidates2 };
|
|
24081
|
+
return { created, existing, failed, skipped: null, candidates: candidates2 };
|
|
23992
24082
|
}
|
|
23993
24083
|
|
|
23994
24084
|
// src/commands/analyze/phases/08-memory-manifest.ts
|
|
@@ -24017,12 +24107,15 @@ async function memoryManifest(run2) {
|
|
|
24017
24107
|
force: false,
|
|
24018
24108
|
dryRun: false
|
|
24019
24109
|
});
|
|
24020
|
-
if (seedResult.created > 0) {
|
|
24021
|
-
(0, import_node_fs43.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created}
|
|
24110
|
+
if (seedResult.created > 0 || seedResult.existing > 0) {
|
|
24111
|
+
(0, import_node_fs43.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created} existing=${seedResult.existing}
|
|
24022
24112
|
`);
|
|
24023
|
-
|
|
24113
|
+
if (seedResult.created > 0) {
|
|
24114
|
+
autoSeedNotice = `Seeded ${seedResult.created} knowledge node(s) from your existing Standard (one-time).`;
|
|
24115
|
+
}
|
|
24024
24116
|
logEvent("auto_seed_ran", {
|
|
24025
24117
|
created: seedResult.created,
|
|
24118
|
+
existing: seedResult.existing,
|
|
24026
24119
|
failed: seedResult.failed
|
|
24027
24120
|
});
|
|
24028
24121
|
} else if (seedResult.skipped === "already_seeded") {
|
|
@@ -28774,7 +28867,7 @@ function registerInitCommand(program2) {
|
|
|
28774
28867
|
...telemetryChoice ? { telemetry: telemetryChoice } : {},
|
|
28775
28868
|
init: {
|
|
28776
28869
|
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28777
|
-
cli_version: true ? "0.33.
|
|
28870
|
+
cli_version: true ? "0.33.3" : "dev"
|
|
28778
28871
|
}
|
|
28779
28872
|
});
|
|
28780
28873
|
} catch (err) {
|
|
@@ -29400,7 +29493,7 @@ function registerMemoryCommand(program2) {
|
|
|
29400
29493
|
process.exit(0);
|
|
29401
29494
|
}
|
|
29402
29495
|
if (result.skipped === "already_seeded") {
|
|
29403
|
-
printInfo("Memory graph already seeded
|
|
29496
|
+
printInfo("Memory graph already seeded \u2014 this project has seed nodes (locally or on the server). Use --force to re-materialize them.");
|
|
29404
29497
|
process.exit(0);
|
|
29405
29498
|
}
|
|
29406
29499
|
if (opts.dryRun) {
|
|
@@ -29411,10 +29504,11 @@ function registerMemoryCommand(program2) {
|
|
|
29411
29504
|
process.exit(0);
|
|
29412
29505
|
}
|
|
29413
29506
|
console.log("");
|
|
29507
|
+
const existingNote = result.existing > 0 ? ` ${result.existing} already existed on the server and were not duplicated.` : "";
|
|
29414
29508
|
if (result.failed === 0) {
|
|
29415
|
-
printInfo(`Seeded ${result.created} knowledge node(s) from the Standard into .verity/memory
|
|
29509
|
+
printInfo(`Seeded ${result.created} knowledge node(s) from the Standard into .verity/memory/.${existingNote}`);
|
|
29416
29510
|
} else {
|
|
29417
|
-
printWarn(`Seeded ${result.created} node(s); ${result.failed} failed
|
|
29511
|
+
printWarn(`Seeded ${result.created} node(s); ${result.failed} failed.${existingNote} See messages above.`);
|
|
29418
29512
|
}
|
|
29419
29513
|
process.exit(result.failed === 0 ? 0 : 1);
|
|
29420
29514
|
});
|
|
@@ -29696,8 +29790,8 @@ function registerTelemetryCommands(program2) {
|
|
|
29696
29790
|
}
|
|
29697
29791
|
|
|
29698
29792
|
// src/cli.ts
|
|
29699
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.33.
|
|
29700
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.33.
|
|
29793
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.33.3").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
|
|
29794
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.33.3");
|
|
29701
29795
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
29702
29796
|
try {
|
|
29703
29797
|
await foldLegacyLocalCredential();
|