@codacy/verity-cli 0.20.1 → 0.21.0
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/README.md +14 -2
- package/bin/verity.js +159 -1
- package/data/skills/verity-setup/SKILL.md +55 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,13 +24,25 @@ source ~/.zshrc
|
|
|
24
24
|
npm install -g @codacy/verity-cli
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
## Upgrade
|
|
27
|
+
## Upgrade
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
30
|
npm install -g @codacy/verity-cli@latest
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
Existing projects
|
|
33
|
+
Existing projects keep working — new compound features (knowledge extraction, graph memory, pre-work injection) activate automatically.
|
|
34
|
+
|
|
35
|
+
### Coming from GATE.md (`@codacy/gate-cli`)?
|
|
36
|
+
|
|
37
|
+
GATE.md is now Verity. One-time switch:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm rm -g @codacy/gate-cli
|
|
41
|
+
npm install -g @codacy/verity-cli
|
|
42
|
+
verity init # per project — moves .gate→.verity, keeps your token, Standard & memory
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
No re-setup needed: your token, Standard, and run history all carry over.
|
|
34
46
|
|
|
35
47
|
## Quick reference
|
|
36
48
|
|
package/bin/verity.js
CHANGED
|
@@ -14824,6 +14824,7 @@ async function runMigration(opts = {}) {
|
|
|
14824
14824
|
migrateGlobalCredentials(home, actions);
|
|
14825
14825
|
await migrateLegacyHooks(root, actions);
|
|
14826
14826
|
await migrateClaudeMd(root, actions);
|
|
14827
|
+
migrateStandardFile(root, actions);
|
|
14827
14828
|
removeLegacyPackage(movedProjectDir, npmRemover, actions);
|
|
14828
14829
|
return { actions, migrated: actions.length > 0 };
|
|
14829
14830
|
}
|
|
@@ -14919,6 +14920,24 @@ async function migrateClaudeMd(root, actions) {
|
|
|
14919
14920
|
printWarn(`Could not rewrite CLAUDE.md block: ${err.message}`);
|
|
14920
14921
|
}
|
|
14921
14922
|
}
|
|
14923
|
+
function migrateStandardFile(root, actions) {
|
|
14924
|
+
const gateMd = (0, import_node_path14.join)(root, "GATE.md");
|
|
14925
|
+
const verityMd = (0, import_node_path14.join)(root, "VERITY.md");
|
|
14926
|
+
if (!(0, import_node_fs20.existsSync)(gateMd) || (0, import_node_fs20.existsSync)(verityMd)) return;
|
|
14927
|
+
let moved = false;
|
|
14928
|
+
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
14929
|
+
try {
|
|
14930
|
+
(0, import_node_child_process8.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
|
|
14931
|
+
moved = true;
|
|
14932
|
+
} catch {
|
|
14933
|
+
}
|
|
14934
|
+
}
|
|
14935
|
+
if (!moved) moveFile(gateMd, verityMd);
|
|
14936
|
+
const content = readFileSyncSafe(verityMd);
|
|
14937
|
+
const refreshed = content.split("GATE.md").join("VERITY.md");
|
|
14938
|
+
if (refreshed !== content) (0, import_node_fs20.writeFileSync)(verityMd, refreshed);
|
|
14939
|
+
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
14940
|
+
}
|
|
14922
14941
|
function removeLegacyPackage(movedProjectDir, npmRemover, actions) {
|
|
14923
14942
|
if (!movedProjectDir) return;
|
|
14924
14943
|
try {
|
|
@@ -15024,6 +15043,9 @@ async function needsMigration(root = repoRoot()) {
|
|
|
15024
15043
|
if ((0, import_node_fs20.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
15025
15044
|
return true;
|
|
15026
15045
|
}
|
|
15046
|
+
if ((0, import_node_fs20.existsSync)((0, import_node_path14.join)(root, "GATE.md")) && !(0, import_node_fs20.existsSync)((0, import_node_path14.join)(root, "VERITY.md"))) {
|
|
15047
|
+
return true;
|
|
15048
|
+
}
|
|
15027
15049
|
if (await hasLegacyHooksAt(root)) return true;
|
|
15028
15050
|
return false;
|
|
15029
15051
|
}
|
|
@@ -15778,8 +15800,143 @@ function registerRunCommand(program2) {
|
|
|
15778
15800
|
});
|
|
15779
15801
|
}
|
|
15780
15802
|
|
|
15803
|
+
// src/lib/telemetry.ts
|
|
15804
|
+
var import_promises13 = require("node:fs/promises");
|
|
15805
|
+
var import_node_path18 = require("node:path");
|
|
15806
|
+
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
15807
|
+
var GITIGNORE_FILE = ".gitignore";
|
|
15808
|
+
var GITIGNORE_ENTRY = ".claude/settings.local.json";
|
|
15809
|
+
function deriveOtlpEndpoint(serviceUrl) {
|
|
15810
|
+
return serviceUrl.replace(/\/+$/, "").replace(/\/v1$/, "") + "/v1/otlp";
|
|
15811
|
+
}
|
|
15812
|
+
function buildTelemetryEnv(serviceUrl, token) {
|
|
15813
|
+
return {
|
|
15814
|
+
CLAUDE_CODE_ENABLE_TELEMETRY: "1",
|
|
15815
|
+
OTEL_METRICS_EXPORTER: "otlp",
|
|
15816
|
+
OTEL_TRACES_EXPORTER: "otlp",
|
|
15817
|
+
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA: "1",
|
|
15818
|
+
OTEL_EXPORTER_OTLP_PROTOCOL: "http/json",
|
|
15819
|
+
OTEL_EXPORTER_OTLP_ENDPOINT: deriveOtlpEndpoint(serviceUrl),
|
|
15820
|
+
// <token> is whatever is in .verity/credentials — verity_ OR legacy gate_.
|
|
15821
|
+
// The server hashes the raw token regardless of prefix.
|
|
15822
|
+
OTEL_EXPORTER_OTLP_HEADERS: `Authorization=Bearer ${token}`,
|
|
15823
|
+
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: "delta",
|
|
15824
|
+
OTEL_LOG_USER_PROMPTS: "0",
|
|
15825
|
+
OTEL_LOG_TOOL_DETAILS: "0",
|
|
15826
|
+
OTEL_LOG_TOOL_CONTENT: "0",
|
|
15827
|
+
OTEL_METRICS_INCLUDE_SESSION_ID: "true",
|
|
15828
|
+
OTEL_METRICS_INCLUDE_ACCOUNT_UUID: "false",
|
|
15829
|
+
OTEL_METRICS_INCLUDE_VERSION: "false",
|
|
15830
|
+
OTEL_METRIC_EXPORT_INTERVAL: "60000"
|
|
15831
|
+
};
|
|
15832
|
+
}
|
|
15833
|
+
var VERITY_TELEMETRY_KEYS = Object.keys(buildTelemetryEnv("", ""));
|
|
15834
|
+
async function readSettingsLocal() {
|
|
15835
|
+
try {
|
|
15836
|
+
return JSON.parse(await (0, import_promises13.readFile)(projectPath(SETTINGS_LOCAL_FILE2), "utf-8"));
|
|
15837
|
+
} catch {
|
|
15838
|
+
return {};
|
|
15839
|
+
}
|
|
15840
|
+
}
|
|
15841
|
+
async function writeSettingsLocal(settings) {
|
|
15842
|
+
const file = projectPath(SETTINGS_LOCAL_FILE2);
|
|
15843
|
+
await (0, import_promises13.mkdir)((0, import_node_path18.dirname)(file), { recursive: true });
|
|
15844
|
+
await (0, import_promises13.writeFile)(file, JSON.stringify(settings, null, 2) + "\n");
|
|
15845
|
+
}
|
|
15846
|
+
async function ensureGitignore() {
|
|
15847
|
+
const file = projectPath(GITIGNORE_FILE);
|
|
15848
|
+
let content = "";
|
|
15849
|
+
try {
|
|
15850
|
+
content = await (0, import_promises13.readFile)(file, "utf-8");
|
|
15851
|
+
} catch {
|
|
15852
|
+
}
|
|
15853
|
+
const lines = content.split("\n").map((l) => l.trim());
|
|
15854
|
+
if (lines.includes(GITIGNORE_ENTRY) || lines.includes(".claude/") || lines.includes(".claude")) {
|
|
15855
|
+
return;
|
|
15856
|
+
}
|
|
15857
|
+
const block = "# Verity telemetry \u2014 holds your project token\n" + GITIGNORE_ENTRY + "\n";
|
|
15858
|
+
const next = content ? content + (content.endsWith("\n") ? "" : "\n") + "\n" + block : block;
|
|
15859
|
+
await (0, import_promises13.writeFile)(file, next);
|
|
15860
|
+
}
|
|
15861
|
+
async function installTelemetry(serviceUrl, token) {
|
|
15862
|
+
const env = buildTelemetryEnv(serviceUrl, token);
|
|
15863
|
+
const settings = await readSettingsLocal();
|
|
15864
|
+
settings.env = { ...settings.env ?? {}, ...env };
|
|
15865
|
+
await writeSettingsLocal(settings);
|
|
15866
|
+
await ensureGitignore();
|
|
15867
|
+
return { ok: true, data: { endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT } };
|
|
15868
|
+
}
|
|
15869
|
+
async function checkTelemetry() {
|
|
15870
|
+
const env = (await readSettingsLocal()).env ?? {};
|
|
15871
|
+
const enabled = env.CLAUDE_CODE_ENABLE_TELEMETRY === "1" && !!env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
15872
|
+
return { enabled, endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT ?? null, settingsPath: SETTINGS_LOCAL_FILE2 };
|
|
15873
|
+
}
|
|
15874
|
+
async function uninstallTelemetry() {
|
|
15875
|
+
const settings = await readSettingsLocal();
|
|
15876
|
+
if (!settings.env) return { ok: true, data: { removed: 0 } };
|
|
15877
|
+
let removed = 0;
|
|
15878
|
+
for (const key of VERITY_TELEMETRY_KEYS) {
|
|
15879
|
+
if (key in settings.env) {
|
|
15880
|
+
delete settings.env[key];
|
|
15881
|
+
removed++;
|
|
15882
|
+
}
|
|
15883
|
+
}
|
|
15884
|
+
if (Object.keys(settings.env).length === 0) delete settings.env;
|
|
15885
|
+
await writeSettingsLocal(settings);
|
|
15886
|
+
return { ok: true, data: { removed } };
|
|
15887
|
+
}
|
|
15888
|
+
|
|
15889
|
+
// src/commands/telemetry.ts
|
|
15890
|
+
function registerTelemetryCommands(program2) {
|
|
15891
|
+
const telemetry = program2.command("telemetry").description("Manage Claude Code OpenTelemetry export to Verity (cost & usage)");
|
|
15892
|
+
telemetry.command("install").description("Enable Claude Code telemetry export to Verity (writes .claude/settings.local.json)").action(async () => {
|
|
15893
|
+
const globals = program2.opts();
|
|
15894
|
+
const tokenResult = await resolveToken(globals.token);
|
|
15895
|
+
if (!tokenResult.ok) {
|
|
15896
|
+
printError(tokenResult.error);
|
|
15897
|
+
process.exit(1);
|
|
15898
|
+
}
|
|
15899
|
+
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
15900
|
+
if (!urlResult.ok) {
|
|
15901
|
+
printError(urlResult.error);
|
|
15902
|
+
process.exit(1);
|
|
15903
|
+
}
|
|
15904
|
+
const result = await installTelemetry(urlResult.data, tokenResult.data.token);
|
|
15905
|
+
if (!result.ok) {
|
|
15906
|
+
printError(result.error);
|
|
15907
|
+
process.exit(1);
|
|
15908
|
+
}
|
|
15909
|
+
printInfo(`Telemetry enabled \u2192 ${result.data.endpoint}`);
|
|
15910
|
+
printInfo(` wrote ${SETTINGS_LOCAL_FILE2} (gitignored \u2014 it holds your project token)`);
|
|
15911
|
+
printInfo(" takes effect on your NEXT Claude Code session; first metrics appear within ~60s of activity");
|
|
15912
|
+
printInfo(" view cost & usage at /usage");
|
|
15913
|
+
});
|
|
15914
|
+
telemetry.command("check").description("Show whether Claude Code telemetry export to Verity is enabled").option("--json", "Output status as JSON").action(async (opts) => {
|
|
15915
|
+
const status = await checkTelemetry();
|
|
15916
|
+
if (opts.json) {
|
|
15917
|
+
printJson(status);
|
|
15918
|
+
return;
|
|
15919
|
+
}
|
|
15920
|
+
if (status.enabled) {
|
|
15921
|
+
printInfo(`Telemetry: enabled \u2192 ${status.endpoint}`);
|
|
15922
|
+
} else {
|
|
15923
|
+
printWarn('Telemetry: disabled. Run "verity telemetry install" to enable cost & usage tracking.');
|
|
15924
|
+
}
|
|
15925
|
+
});
|
|
15926
|
+
telemetry.command("uninstall").description("Disable Claude Code telemetry export to Verity").action(async () => {
|
|
15927
|
+
const result = await uninstallTelemetry();
|
|
15928
|
+
if (!result.ok) {
|
|
15929
|
+
printError(result.error);
|
|
15930
|
+
process.exit(1);
|
|
15931
|
+
}
|
|
15932
|
+
printInfo(
|
|
15933
|
+
result.data.removed > 0 ? `Telemetry disabled (${result.data.removed} keys removed from ${SETTINGS_LOCAL_FILE2})` : "Telemetry was not enabled \u2014 nothing to remove."
|
|
15934
|
+
);
|
|
15935
|
+
});
|
|
15936
|
+
}
|
|
15937
|
+
|
|
15781
15938
|
// src/cli.ts
|
|
15782
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.
|
|
15939
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.21.0").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
|
|
15783
15940
|
registerAuthCommands(program);
|
|
15784
15941
|
registerHooksCommands(program);
|
|
15785
15942
|
registerIntentCommands(program);
|
|
@@ -15797,4 +15954,5 @@ registerReflectCommand(program);
|
|
|
15797
15954
|
registerMemoryCommand(program);
|
|
15798
15955
|
registerRunCommand(program);
|
|
15799
15956
|
registerMigrateCommand(program);
|
|
15957
|
+
registerTelemetryCommands(program);
|
|
15800
15958
|
program.parse();
|
|
@@ -75,6 +75,36 @@ Default to `balanced` if the user says "default" or doesn't specify a preference
|
|
|
75
75
|
|
|
76
76
|
---
|
|
77
77
|
|
|
78
|
+
## Step 3b: Ask about cost & usage telemetry (opt-in)
|
|
79
|
+
|
|
80
|
+
Cost & usage observability is **opt-in** and **required** for the `/usage` dashboard — all
|
|
81
|
+
cost and token data comes from Claude Code's own OpenTelemetry export, so with telemetry off
|
|
82
|
+
the dashboard stays empty.
|
|
83
|
+
|
|
84
|
+
First check the current state (so you don't re-ask if it's already on):
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
verity telemetry check
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
- If it reports **enabled**, don't re-ask — tell the user telemetry is already on and continue
|
|
91
|
+
(mention `verity telemetry uninstall` only if they want to turn it off).
|
|
92
|
+
- If **disabled**, use the **AskUserQuestion** tool:
|
|
93
|
+
|
|
94
|
+
> **Enable Claude Code cost & usage telemetry for this project?**
|
|
95
|
+
> Verity can show cost, tokens, a per-agent / per-session / model breakdown, and a fleet cost
|
|
96
|
+
> tree — by receiving Claude Code's built-in **OpenTelemetry** export. This sends usage
|
|
97
|
+
> **metrics and traces only** (model names, token counts, USD cost, agent types, session IDs).
|
|
98
|
+
> It does **NOT** send your prompts, code, or tool input/output. Without it, the `/usage`
|
|
99
|
+
> dashboard stays empty.
|
|
100
|
+
> - **Yes** (recommended) — enable telemetry
|
|
101
|
+
> - **No** — skip (you can enable later with `verity telemetry install`)
|
|
102
|
+
|
|
103
|
+
Remember the answer; you act on it in Step 7b (the token must exist first). **Declining is
|
|
104
|
+
fine and reversible** — nothing is written, and the next `/verity-setup` re-offers it.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
78
108
|
## Step 4: Synthesize the Standard
|
|
79
109
|
|
|
80
110
|
Read `.claude/skills/verity-setup/patterns-reference.yaml` and `.claude/skills/verity-setup/standard-template.yaml` from the project root.
|
|
@@ -301,6 +331,25 @@ This derives a small set of descriptive memory nodes from what you already analy
|
|
|
301
331
|
|
|
302
332
|
---
|
|
303
333
|
|
|
334
|
+
## Step 7b: Enable telemetry (only if the user opted in at Step 3b)
|
|
335
|
+
|
|
336
|
+
If — and only if — the user said **Yes** in Step 3b, enable the Claude Code telemetry export
|
|
337
|
+
now (the token from Step 6 must already exist):
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
verity telemetry install
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
This writes the `OTEL_*` env block to the gitignored `.claude/settings.local.json` (which
|
|
344
|
+
holds your token) and points Claude Code's OpenTelemetry exporter at Verity's OTLP endpoint.
|
|
345
|
+
Tell the user it takes effect on their **next** Claude Code session, that first metrics appear
|
|
346
|
+
within ~60s of activity, and point them at `/usage`.
|
|
347
|
+
|
|
348
|
+
If the user declined, skip this step and note that `/usage` will stay empty until they run
|
|
349
|
+
`verity telemetry install`.
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
304
353
|
## Step 8: Generate VERITY.md file
|
|
305
354
|
|
|
306
355
|
Create `VERITY.md` at the project root with this content:
|
|
@@ -404,8 +453,12 @@ Add these entries to `.gitignore` (create it if it doesn't exist, append if it d
|
|
|
404
453
|
.verity/.last-intent
|
|
405
454
|
.verity/.memory-sync-state.json
|
|
406
455
|
.verity/memory/log.md
|
|
456
|
+
.claude/settings.local.json
|
|
407
457
|
```
|
|
408
458
|
|
|
459
|
+
`.claude/settings.local.json` holds the telemetry env block **including your token**, so it
|
|
460
|
+
must never be committed (`verity telemetry install` also adds this entry automatically).
|
|
461
|
+
|
|
409
462
|
Do NOT gitignore `.verity/standard.yaml` or `VERITY.md` — those should be committed.
|
|
410
463
|
|
|
411
464
|
**Commit the knowledge graph, but not its log.** The nodes under `.verity/memory/<domain>/` and `.verity/memory/index.md` are durable project knowledge meant to be committed and reviewed. But `.verity/memory/log.md` is an append-only, per-run timestamped activity log — it churns on every analysis and carries no reviewable content, so it is gitignored above. If a project already committed it, untrack it once with `git rm --cached .verity/memory/log.md`.
|
|
@@ -431,12 +484,14 @@ Standard: v1 (4 quality, 7 security, N custom patterns)
|
|
|
431
484
|
Tools: ${TOOL_LIST}
|
|
432
485
|
Service: registered (project_id: ${PROJECT_ID})
|
|
433
486
|
Hooks: installed (verity analyze + verity intent capture)
|
|
487
|
+
Telemetry: ${TELEMETRY_STATUS} (enabled → cost+usage at /usage, or disabled)
|
|
434
488
|
|
|
435
489
|
Files created:
|
|
436
490
|
.verity/standard.yaml — Quality standard
|
|
437
491
|
.codacy/codacy.config.json — Analysis CLI config
|
|
438
492
|
VERITY.md — Project quality overview
|
|
439
493
|
.claude/settings.json — Hook configuration (verified)
|
|
494
|
+
.claude/settings.local.json — Telemetry env (only if enabled; gitignored)
|
|
440
495
|
.gitignore — Updated with Verity entries
|
|
441
496
|
|
|
442
497
|
Next: You should now be seeing the first analysis happening below. The hook will fire automatically every time your agent stops working on something.
|