@codacy/verity-cli 0.20.2 → 0.22.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 +143 -6
- 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
|
@@ -14107,7 +14107,7 @@ async function runSeed(opts) {
|
|
|
14107
14107
|
var MAX_ASSISTANT_RESPONSE_CHARS_PLAN = 32768;
|
|
14108
14108
|
var MAX_ASSISTANT_RESPONSE_CHARS_DEFAULT = 8e3;
|
|
14109
14109
|
async function readStopHookStdin() {
|
|
14110
|
-
const empty = { assistantMessage: null, stopReason: null, transcriptPath: null };
|
|
14110
|
+
const empty = { assistantMessage: null, stopReason: null, transcriptPath: null, sessionId: null };
|
|
14111
14111
|
try {
|
|
14112
14112
|
if (process.stdin.isTTY) return empty;
|
|
14113
14113
|
const chunks = [];
|
|
@@ -14125,7 +14125,8 @@ async function readStopHookStdin() {
|
|
|
14125
14125
|
resolve({
|
|
14126
14126
|
assistantMessage: typeof data.last_assistant_message === "string" ? data.last_assistant_message : null,
|
|
14127
14127
|
stopReason: typeof data.stop_reason === "string" ? data.stop_reason : null,
|
|
14128
|
-
transcriptPath: typeof data.transcript_path === "string" ? data.transcript_path : null
|
|
14128
|
+
transcriptPath: typeof data.transcript_path === "string" ? data.transcript_path : null,
|
|
14129
|
+
sessionId: typeof data.session_id === "string" ? data.session_id : null
|
|
14129
14130
|
});
|
|
14130
14131
|
} catch {
|
|
14131
14132
|
resolve(empty);
|
|
@@ -14158,7 +14159,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14158
14159
|
process.chdir(repoRoot());
|
|
14159
14160
|
} catch {
|
|
14160
14161
|
}
|
|
14161
|
-
const { assistantMessage: assistantResponse, stopReason, transcriptPath } = await readStopHookStdin();
|
|
14162
|
+
const { assistantMessage: assistantResponse, stopReason, transcriptPath, sessionId } = await readStopHookStdin();
|
|
14162
14163
|
const actionSummary = transcriptPath ? await extractActionSummary(transcriptPath) : null;
|
|
14163
14164
|
const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
|
|
14164
14165
|
const analyzable = filterAnalyzable(allChanged);
|
|
@@ -14191,7 +14192,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14191
14192
|
if (!urlResult.ok) {
|
|
14192
14193
|
passAndExit("Not configured yet \u2014 run /verity-setup in Claude Code to enable quality gates.");
|
|
14193
14194
|
}
|
|
14194
|
-
const sessionIdForMemory = process.env.CLAUDE_SESSION_ID
|
|
14195
|
+
const sessionIdForMemory = sessionId || process.env.CLAUDE_SESSION_ID || "";
|
|
14195
14196
|
let contextFilePaths = [];
|
|
14196
14197
|
let predictedMode;
|
|
14197
14198
|
try {
|
|
@@ -14389,7 +14390,7 @@ async function runAnalyze(opts, globals) {
|
|
|
14389
14390
|
context: {
|
|
14390
14391
|
agent_model: process.env.CLAUDE_MODEL ?? "unknown",
|
|
14391
14392
|
agent_harness: "claude-code",
|
|
14392
|
-
session_id: process.env.CLAUDE_SESSION_ID
|
|
14393
|
+
session_id: sessionId || process.env.CLAUDE_SESSION_ID || "unknown",
|
|
14393
14394
|
iteration
|
|
14394
14395
|
},
|
|
14395
14396
|
...memoryManifest && { memory_manifest: memoryManifest },
|
|
@@ -15800,8 +15801,143 @@ function registerRunCommand(program2) {
|
|
|
15800
15801
|
});
|
|
15801
15802
|
}
|
|
15802
15803
|
|
|
15804
|
+
// src/lib/telemetry.ts
|
|
15805
|
+
var import_promises13 = require("node:fs/promises");
|
|
15806
|
+
var import_node_path18 = require("node:path");
|
|
15807
|
+
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
15808
|
+
var GITIGNORE_FILE = ".gitignore";
|
|
15809
|
+
var GITIGNORE_ENTRY = ".claude/settings.local.json";
|
|
15810
|
+
function deriveOtlpEndpoint(serviceUrl) {
|
|
15811
|
+
return serviceUrl.replace(/\/+$/, "").replace(/\/v1$/, "") + "/v1/otlp";
|
|
15812
|
+
}
|
|
15813
|
+
function buildTelemetryEnv(serviceUrl, token) {
|
|
15814
|
+
return {
|
|
15815
|
+
CLAUDE_CODE_ENABLE_TELEMETRY: "1",
|
|
15816
|
+
OTEL_METRICS_EXPORTER: "otlp",
|
|
15817
|
+
OTEL_TRACES_EXPORTER: "otlp",
|
|
15818
|
+
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA: "1",
|
|
15819
|
+
OTEL_EXPORTER_OTLP_PROTOCOL: "http/json",
|
|
15820
|
+
OTEL_EXPORTER_OTLP_ENDPOINT: deriveOtlpEndpoint(serviceUrl),
|
|
15821
|
+
// <token> is whatever is in .verity/credentials — verity_ OR legacy gate_.
|
|
15822
|
+
// The server hashes the raw token regardless of prefix.
|
|
15823
|
+
OTEL_EXPORTER_OTLP_HEADERS: `Authorization=Bearer ${token}`,
|
|
15824
|
+
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: "delta",
|
|
15825
|
+
OTEL_LOG_USER_PROMPTS: "0",
|
|
15826
|
+
OTEL_LOG_TOOL_DETAILS: "0",
|
|
15827
|
+
OTEL_LOG_TOOL_CONTENT: "0",
|
|
15828
|
+
OTEL_METRICS_INCLUDE_SESSION_ID: "true",
|
|
15829
|
+
OTEL_METRICS_INCLUDE_ACCOUNT_UUID: "false",
|
|
15830
|
+
OTEL_METRICS_INCLUDE_VERSION: "false",
|
|
15831
|
+
OTEL_METRIC_EXPORT_INTERVAL: "60000"
|
|
15832
|
+
};
|
|
15833
|
+
}
|
|
15834
|
+
var VERITY_TELEMETRY_KEYS = Object.keys(buildTelemetryEnv("", ""));
|
|
15835
|
+
async function readSettingsLocal() {
|
|
15836
|
+
try {
|
|
15837
|
+
return JSON.parse(await (0, import_promises13.readFile)(projectPath(SETTINGS_LOCAL_FILE2), "utf-8"));
|
|
15838
|
+
} catch {
|
|
15839
|
+
return {};
|
|
15840
|
+
}
|
|
15841
|
+
}
|
|
15842
|
+
async function writeSettingsLocal(settings) {
|
|
15843
|
+
const file = projectPath(SETTINGS_LOCAL_FILE2);
|
|
15844
|
+
await (0, import_promises13.mkdir)((0, import_node_path18.dirname)(file), { recursive: true });
|
|
15845
|
+
await (0, import_promises13.writeFile)(file, JSON.stringify(settings, null, 2) + "\n");
|
|
15846
|
+
}
|
|
15847
|
+
async function ensureGitignore() {
|
|
15848
|
+
const file = projectPath(GITIGNORE_FILE);
|
|
15849
|
+
let content = "";
|
|
15850
|
+
try {
|
|
15851
|
+
content = await (0, import_promises13.readFile)(file, "utf-8");
|
|
15852
|
+
} catch {
|
|
15853
|
+
}
|
|
15854
|
+
const lines = content.split("\n").map((l) => l.trim());
|
|
15855
|
+
if (lines.includes(GITIGNORE_ENTRY) || lines.includes(".claude/") || lines.includes(".claude")) {
|
|
15856
|
+
return;
|
|
15857
|
+
}
|
|
15858
|
+
const block = "# Verity telemetry \u2014 holds your project token\n" + GITIGNORE_ENTRY + "\n";
|
|
15859
|
+
const next = content ? content + (content.endsWith("\n") ? "" : "\n") + "\n" + block : block;
|
|
15860
|
+
await (0, import_promises13.writeFile)(file, next);
|
|
15861
|
+
}
|
|
15862
|
+
async function installTelemetry(serviceUrl, token) {
|
|
15863
|
+
const env = buildTelemetryEnv(serviceUrl, token);
|
|
15864
|
+
const settings = await readSettingsLocal();
|
|
15865
|
+
settings.env = { ...settings.env ?? {}, ...env };
|
|
15866
|
+
await writeSettingsLocal(settings);
|
|
15867
|
+
await ensureGitignore();
|
|
15868
|
+
return { ok: true, data: { endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT } };
|
|
15869
|
+
}
|
|
15870
|
+
async function checkTelemetry() {
|
|
15871
|
+
const env = (await readSettingsLocal()).env ?? {};
|
|
15872
|
+
const enabled = env.CLAUDE_CODE_ENABLE_TELEMETRY === "1" && !!env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
15873
|
+
return { enabled, endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT ?? null, settingsPath: SETTINGS_LOCAL_FILE2 };
|
|
15874
|
+
}
|
|
15875
|
+
async function uninstallTelemetry() {
|
|
15876
|
+
const settings = await readSettingsLocal();
|
|
15877
|
+
if (!settings.env) return { ok: true, data: { removed: 0 } };
|
|
15878
|
+
let removed = 0;
|
|
15879
|
+
for (const key of VERITY_TELEMETRY_KEYS) {
|
|
15880
|
+
if (key in settings.env) {
|
|
15881
|
+
delete settings.env[key];
|
|
15882
|
+
removed++;
|
|
15883
|
+
}
|
|
15884
|
+
}
|
|
15885
|
+
if (Object.keys(settings.env).length === 0) delete settings.env;
|
|
15886
|
+
await writeSettingsLocal(settings);
|
|
15887
|
+
return { ok: true, data: { removed } };
|
|
15888
|
+
}
|
|
15889
|
+
|
|
15890
|
+
// src/commands/telemetry.ts
|
|
15891
|
+
function registerTelemetryCommands(program2) {
|
|
15892
|
+
const telemetry = program2.command("telemetry").description("Manage Claude Code OpenTelemetry export to Verity (cost & usage)");
|
|
15893
|
+
telemetry.command("install").description("Enable Claude Code telemetry export to Verity (writes .claude/settings.local.json)").action(async () => {
|
|
15894
|
+
const globals = program2.opts();
|
|
15895
|
+
const tokenResult = await resolveToken(globals.token);
|
|
15896
|
+
if (!tokenResult.ok) {
|
|
15897
|
+
printError(tokenResult.error);
|
|
15898
|
+
process.exit(1);
|
|
15899
|
+
}
|
|
15900
|
+
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
15901
|
+
if (!urlResult.ok) {
|
|
15902
|
+
printError(urlResult.error);
|
|
15903
|
+
process.exit(1);
|
|
15904
|
+
}
|
|
15905
|
+
const result = await installTelemetry(urlResult.data, tokenResult.data.token);
|
|
15906
|
+
if (!result.ok) {
|
|
15907
|
+
printError(result.error);
|
|
15908
|
+
process.exit(1);
|
|
15909
|
+
}
|
|
15910
|
+
printInfo(`Telemetry enabled \u2192 ${result.data.endpoint}`);
|
|
15911
|
+
printInfo(` wrote ${SETTINGS_LOCAL_FILE2} (gitignored \u2014 it holds your project token)`);
|
|
15912
|
+
printInfo(" takes effect on your NEXT Claude Code session; first metrics appear within ~60s of activity");
|
|
15913
|
+
printInfo(" view cost & usage at /usage");
|
|
15914
|
+
});
|
|
15915
|
+
telemetry.command("check").description("Show whether Claude Code telemetry export to Verity is enabled").option("--json", "Output status as JSON").action(async (opts) => {
|
|
15916
|
+
const status = await checkTelemetry();
|
|
15917
|
+
if (opts.json) {
|
|
15918
|
+
printJson(status);
|
|
15919
|
+
return;
|
|
15920
|
+
}
|
|
15921
|
+
if (status.enabled) {
|
|
15922
|
+
printInfo(`Telemetry: enabled \u2192 ${status.endpoint}`);
|
|
15923
|
+
} else {
|
|
15924
|
+
printWarn('Telemetry: disabled. Run "verity telemetry install" to enable cost & usage tracking.');
|
|
15925
|
+
}
|
|
15926
|
+
});
|
|
15927
|
+
telemetry.command("uninstall").description("Disable Claude Code telemetry export to Verity").action(async () => {
|
|
15928
|
+
const result = await uninstallTelemetry();
|
|
15929
|
+
if (!result.ok) {
|
|
15930
|
+
printError(result.error);
|
|
15931
|
+
process.exit(1);
|
|
15932
|
+
}
|
|
15933
|
+
printInfo(
|
|
15934
|
+
result.data.removed > 0 ? `Telemetry disabled (${result.data.removed} keys removed from ${SETTINGS_LOCAL_FILE2})` : "Telemetry was not enabled \u2014 nothing to remove."
|
|
15935
|
+
);
|
|
15936
|
+
});
|
|
15937
|
+
}
|
|
15938
|
+
|
|
15803
15939
|
// src/cli.ts
|
|
15804
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.
|
|
15940
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.22.0").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
|
|
15805
15941
|
registerAuthCommands(program);
|
|
15806
15942
|
registerHooksCommands(program);
|
|
15807
15943
|
registerIntentCommands(program);
|
|
@@ -15819,4 +15955,5 @@ registerReflectCommand(program);
|
|
|
15819
15955
|
registerMemoryCommand(program);
|
|
15820
15956
|
registerRunCommand(program);
|
|
15821
15957
|
registerMigrateCommand(program);
|
|
15958
|
+
registerTelemetryCommands(program);
|
|
15822
15959
|
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.
|