@codacy/verity-cli 0.28.0 → 0.28.1-experimental.9155758
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/verity.js +395 -244
- package/data/skills/verity-setup/SKILL.md +16 -10
- package/package.json +1 -5
package/bin/verity.js
CHANGED
|
@@ -10327,10 +10327,6 @@ var {
|
|
|
10327
10327
|
// src/commands/auth.ts
|
|
10328
10328
|
var import_node_child_process4 = require("node:child_process");
|
|
10329
10329
|
|
|
10330
|
-
// src/lib/auth.ts
|
|
10331
|
-
var import_promises2 = require("node:fs/promises");
|
|
10332
|
-
var import_node_child_process2 = require("node:child_process");
|
|
10333
|
-
|
|
10334
10330
|
// src/constants.ts
|
|
10335
10331
|
var import_node_child_process = require("node:child_process");
|
|
10336
10332
|
var import_node_path = require("node:path");
|
|
@@ -10347,7 +10343,6 @@ function repoRoot() {
|
|
|
10347
10343
|
}
|
|
10348
10344
|
var VERITY_DIR = ".verity";
|
|
10349
10345
|
var CREDENTIALS_FILE = `${VERITY_DIR}/credentials`;
|
|
10350
|
-
var GLOBAL_CREDENTIALS_FILE = `${process.env.HOME}/.verity/credentials`;
|
|
10351
10346
|
var DEBOUNCE_FILE = `${VERITY_DIR}/.last-analysis`;
|
|
10352
10347
|
var ITERATION_FILE = `${VERITY_DIR}/.iteration-count`;
|
|
10353
10348
|
var HASH_FILE = `${VERITY_DIR}/.last-pass-hash`;
|
|
@@ -10473,7 +10468,7 @@ var SECURITY_PATTERNS = [
|
|
|
10473
10468
|
/Dockerfile/
|
|
10474
10469
|
];
|
|
10475
10470
|
var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
|
|
10476
|
-
var DEFAULT_SERVICE_URL = "".length > 0 ? "" : PROD_SERVICE_URL;
|
|
10471
|
+
var DEFAULT_SERVICE_URL = "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1".length > 0 ? "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1" : PROD_SERVICE_URL;
|
|
10477
10472
|
var GITHUB_CLIENT_ID = "Iv23li88HxAi3ZrbYzWh";
|
|
10478
10473
|
var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
10479
10474
|
var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
@@ -10673,19 +10668,174 @@ function analyzeRequest(options) {
|
|
|
10673
10668
|
}
|
|
10674
10669
|
|
|
10675
10670
|
// src/lib/service-url.ts
|
|
10671
|
+
var import_promises2 = require("node:fs/promises");
|
|
10672
|
+
|
|
10673
|
+
// src/lib/credentials.ts
|
|
10676
10674
|
var import_promises = require("node:fs/promises");
|
|
10677
|
-
|
|
10675
|
+
var import_node_path2 = require("node:path");
|
|
10676
|
+
var import_node_child_process2 = require("node:child_process");
|
|
10677
|
+
function globalCredentialsPath() {
|
|
10678
|
+
return `${process.env.HOME}/.verity/credentials`;
|
|
10679
|
+
}
|
|
10680
|
+
function currentRemote() {
|
|
10681
|
+
try {
|
|
10682
|
+
return (0, import_node_child_process2.execSync)("git remote get-url origin", {
|
|
10683
|
+
encoding: "utf-8",
|
|
10684
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
10685
|
+
}).trim();
|
|
10686
|
+
} catch {
|
|
10687
|
+
return "";
|
|
10688
|
+
}
|
|
10689
|
+
}
|
|
10690
|
+
var TOKEN_RE = /token:\s*((?:gate_|verity_)[a-f0-9]+)/;
|
|
10691
|
+
function parseCredentialLine(line) {
|
|
10692
|
+
const idx = line.indexOf(" token:");
|
|
10693
|
+
const remote = idx === -1 ? "" : line.slice(0, idx).trim();
|
|
10694
|
+
const rest = idx === -1 ? line : line.slice(idx);
|
|
10695
|
+
const tokenMatch = rest.match(TOKEN_RE);
|
|
10696
|
+
if (!tokenMatch) return null;
|
|
10697
|
+
const serviceUrl = rest.match(/service_url:\s*(https?:\/\/\S+)/)?.[1];
|
|
10698
|
+
const userIdRaw = rest.match(/user_id:\s*(\d+)/)?.[1];
|
|
10699
|
+
const email = rest.match(/email:\s*(\S+)/)?.[1];
|
|
10700
|
+
return {
|
|
10701
|
+
remote,
|
|
10702
|
+
rec: {
|
|
10703
|
+
token: tokenMatch[1],
|
|
10704
|
+
serviceUrl,
|
|
10705
|
+
userId: userIdRaw != null ? Number(userIdRaw) : void 0,
|
|
10706
|
+
email
|
|
10707
|
+
}
|
|
10708
|
+
};
|
|
10709
|
+
}
|
|
10710
|
+
function encodeRemoteKey(remote) {
|
|
10711
|
+
return remote.replace(
|
|
10712
|
+
/[%\x00-\x20\x7f]/g,
|
|
10713
|
+
(c) => "%" + c.charCodeAt(0).toString(16).padStart(2, "0").toUpperCase()
|
|
10714
|
+
);
|
|
10715
|
+
}
|
|
10716
|
+
function serializeCredential(remote, rec) {
|
|
10717
|
+
const key = encodeRemoteKey(remote);
|
|
10718
|
+
const out = [key ? `${key} token: ${rec.token}` : `token: ${rec.token}`];
|
|
10719
|
+
if (rec.serviceUrl) out.push(`service_url: ${rec.serviceUrl}`);
|
|
10720
|
+
if (rec.userId != null) out.push(`user_id: ${rec.userId}`);
|
|
10721
|
+
if (rec.email) out.push(`email: ${rec.email}`);
|
|
10722
|
+
return out.join(" ");
|
|
10723
|
+
}
|
|
10724
|
+
async function readGlobalCredential(remote) {
|
|
10725
|
+
let content;
|
|
10678
10726
|
try {
|
|
10679
|
-
|
|
10680
|
-
const match = creds.match(/service_url:\s*(https?:\/\/[^\s]+)/);
|
|
10681
|
-
return match ? match[1] : null;
|
|
10727
|
+
content = await (0, import_promises.readFile)(globalCredentialsPath(), "utf-8");
|
|
10682
10728
|
} catch {
|
|
10683
10729
|
return null;
|
|
10684
10730
|
}
|
|
10731
|
+
const lines = content.split("\n");
|
|
10732
|
+
const key = encodeRemoteKey(remote);
|
|
10733
|
+
if (key) {
|
|
10734
|
+
let last = null;
|
|
10735
|
+
for (const line of lines) {
|
|
10736
|
+
const parsed = parseCredentialLine(line);
|
|
10737
|
+
if (parsed && parsed.remote === key) last = parsed.rec;
|
|
10738
|
+
}
|
|
10739
|
+
if (last) return last;
|
|
10740
|
+
}
|
|
10741
|
+
let plain = null;
|
|
10742
|
+
for (const line of lines) {
|
|
10743
|
+
const parsed = parseCredentialLine(line);
|
|
10744
|
+
if (parsed && parsed.remote === "") plain = parsed.rec;
|
|
10745
|
+
}
|
|
10746
|
+
return plain;
|
|
10747
|
+
}
|
|
10748
|
+
async function upsertGlobalCredential(remote, rec) {
|
|
10749
|
+
const path = globalCredentialsPath();
|
|
10750
|
+
await (0, import_promises.mkdir)((0, import_node_path2.dirname)(path), { recursive: true });
|
|
10751
|
+
let content = "";
|
|
10752
|
+
try {
|
|
10753
|
+
content = await (0, import_promises.readFile)(path, "utf-8");
|
|
10754
|
+
} catch {
|
|
10755
|
+
}
|
|
10756
|
+
const line = serializeCredential(remote, rec);
|
|
10757
|
+
const key = encodeRemoteKey(remote);
|
|
10758
|
+
const kept = [];
|
|
10759
|
+
let replaced = false;
|
|
10760
|
+
for (const existing of content ? content.split("\n") : []) {
|
|
10761
|
+
const parsed = parseCredentialLine(existing);
|
|
10762
|
+
const matches = parsed !== null && parsed.remote === key;
|
|
10763
|
+
if (matches) {
|
|
10764
|
+
if (!replaced) {
|
|
10765
|
+
kept.push(line);
|
|
10766
|
+
replaced = true;
|
|
10767
|
+
}
|
|
10768
|
+
continue;
|
|
10769
|
+
}
|
|
10770
|
+
kept.push(existing);
|
|
10771
|
+
}
|
|
10772
|
+
while (kept.length && kept[kept.length - 1].trim() === "") kept.pop();
|
|
10773
|
+
if (!replaced) kept.push(line);
|
|
10774
|
+
const body = kept.join("\n") + "\n";
|
|
10775
|
+
await (0, import_promises.writeFile)(path, body, { mode: 384 });
|
|
10776
|
+
await (0, import_promises.chmod)(path, 384).catch(() => {
|
|
10777
|
+
});
|
|
10778
|
+
}
|
|
10779
|
+
function parseLocalCredentialFile(content) {
|
|
10780
|
+
const tokenMatch = content.match(/token:\s*((?:gate_|verity_)[a-f0-9]+)/);
|
|
10781
|
+
if (!tokenMatch) return null;
|
|
10782
|
+
const userIdMatch = content.match(/^user_id:\s*(\d+)/m);
|
|
10783
|
+
return {
|
|
10784
|
+
token: tokenMatch[1],
|
|
10785
|
+
serviceUrl: content.match(/service_url:\s*(https?:\/\/\S+)/)?.[1],
|
|
10786
|
+
userId: userIdMatch ? Number(userIdMatch[1]) : void 0,
|
|
10787
|
+
email: content.match(/^email:\s*(\S+)/m)?.[1]
|
|
10788
|
+
};
|
|
10789
|
+
}
|
|
10790
|
+
async function readLegacyLocalCredential() {
|
|
10791
|
+
try {
|
|
10792
|
+
return parseLocalCredentialFile(await (0, import_promises.readFile)(projectPath(CREDENTIALS_FILE), "utf-8"));
|
|
10793
|
+
} catch {
|
|
10794
|
+
return null;
|
|
10795
|
+
}
|
|
10796
|
+
}
|
|
10797
|
+
async function foldLegacyLocalCredential(remoteArg) {
|
|
10798
|
+
let content;
|
|
10799
|
+
try {
|
|
10800
|
+
content = await (0, import_promises.readFile)(projectPath(CREDENTIALS_FILE), "utf-8");
|
|
10801
|
+
} catch {
|
|
10802
|
+
return false;
|
|
10803
|
+
}
|
|
10804
|
+
const local = parseLocalCredentialFile(content);
|
|
10805
|
+
if (!local) {
|
|
10806
|
+
await (0, import_promises.unlink)(projectPath(CREDENTIALS_FILE)).catch(() => {
|
|
10807
|
+
});
|
|
10808
|
+
return false;
|
|
10809
|
+
}
|
|
10810
|
+
const remote = remoteArg ?? currentRemote();
|
|
10811
|
+
if (!remote) {
|
|
10812
|
+
return false;
|
|
10813
|
+
}
|
|
10814
|
+
const existing = await readGlobalCredential(remote);
|
|
10815
|
+
const merged = {
|
|
10816
|
+
token: existing?.token ?? local.token,
|
|
10817
|
+
serviceUrl: existing?.serviceUrl ?? local.serviceUrl,
|
|
10818
|
+
userId: existing?.userId ?? local.userId,
|
|
10819
|
+
email: existing?.email ?? local.email
|
|
10820
|
+
};
|
|
10821
|
+
try {
|
|
10822
|
+
await upsertGlobalCredential(remote, merged);
|
|
10823
|
+
} catch {
|
|
10824
|
+
return false;
|
|
10825
|
+
}
|
|
10826
|
+
await (0, import_promises.unlink)(projectPath(CREDENTIALS_FILE)).catch(() => {
|
|
10827
|
+
});
|
|
10828
|
+
return true;
|
|
10829
|
+
}
|
|
10830
|
+
|
|
10831
|
+
// src/lib/service-url.ts
|
|
10832
|
+
async function serviceUrlFromCredentials() {
|
|
10833
|
+
const rec = await readGlobalCredential(currentRemote());
|
|
10834
|
+
return rec?.serviceUrl ?? null;
|
|
10685
10835
|
}
|
|
10686
10836
|
async function serviceUrlFromVerityMd() {
|
|
10687
10837
|
try {
|
|
10688
|
-
const content = await (0,
|
|
10838
|
+
const content = await (0, import_promises2.readFile)(projectPath(VERITY_MD_FILE), "utf-8");
|
|
10689
10839
|
const boldLine = content.split("\n").find((l) => /\*\*url\*\*/i.test(l));
|
|
10690
10840
|
if (boldLine) {
|
|
10691
10841
|
const urlMatch = boldLine.match(/https:\/\/[^\s]+/);
|
|
@@ -10727,14 +10877,6 @@ function isHealCandidate(resolved) {
|
|
|
10727
10877
|
}
|
|
10728
10878
|
|
|
10729
10879
|
// src/lib/auth.ts
|
|
10730
|
-
function parseIdentity(content) {
|
|
10731
|
-
const idMatch = content.match(/^user_id:\s*(\d+)/m);
|
|
10732
|
-
const emailMatch = content.match(/^email:\s*(\S+)/m);
|
|
10733
|
-
return {
|
|
10734
|
-
userId: idMatch ? Number(idMatch[1]) : void 0,
|
|
10735
|
-
email: emailMatch ? emailMatch[1] : void 0
|
|
10736
|
-
};
|
|
10737
|
-
}
|
|
10738
10880
|
async function resolveToken(flagToken) {
|
|
10739
10881
|
if (flagToken) {
|
|
10740
10882
|
return { ok: true, data: { token: flagToken, source: "flag" } };
|
|
@@ -10743,36 +10885,19 @@ async function resolveToken(flagToken) {
|
|
|
10743
10885
|
if (envToken) {
|
|
10744
10886
|
return { ok: true, data: { token: envToken, source: "env" } };
|
|
10745
10887
|
}
|
|
10746
|
-
|
|
10747
|
-
|
|
10748
|
-
|
|
10749
|
-
|
|
10750
|
-
|
|
10751
|
-
}
|
|
10752
|
-
} catch {
|
|
10888
|
+
const rec = await readGlobalCredential(currentRemote());
|
|
10889
|
+
if (rec) {
|
|
10890
|
+
return {
|
|
10891
|
+
ok: true,
|
|
10892
|
+
data: { token: rec.token, source: "global", userId: rec.userId, email: rec.email }
|
|
10893
|
+
};
|
|
10753
10894
|
}
|
|
10754
|
-
const
|
|
10755
|
-
|
|
10756
|
-
|
|
10757
|
-
|
|
10758
|
-
|
|
10759
|
-
|
|
10760
|
-
} catch {
|
|
10761
|
-
}
|
|
10762
|
-
if (remote) {
|
|
10763
|
-
const remoteLine = content.split("\n").find((l) => l.includes(remote));
|
|
10764
|
-
if (remoteLine) {
|
|
10765
|
-
const match = remoteLine.match(/token:\s*((?:gate_|verity_)[a-f0-9]+)/);
|
|
10766
|
-
if (match) {
|
|
10767
|
-
return { ok: true, data: { token: match[1], source: "global" } };
|
|
10768
|
-
}
|
|
10769
|
-
}
|
|
10770
|
-
}
|
|
10771
|
-
const plainMatch = content.match(/^token:\s*((?:gate_|verity_)[a-f0-9]+)/m);
|
|
10772
|
-
if (plainMatch) {
|
|
10773
|
-
return { ok: true, data: { token: plainMatch[1], source: "global" } };
|
|
10774
|
-
}
|
|
10775
|
-
} catch {
|
|
10895
|
+
const local = await readLegacyLocalCredential();
|
|
10896
|
+
if (local) {
|
|
10897
|
+
return {
|
|
10898
|
+
ok: true,
|
|
10899
|
+
data: { token: local.token, source: "local", userId: local.userId, email: local.email }
|
|
10900
|
+
};
|
|
10776
10901
|
}
|
|
10777
10902
|
return { ok: false, error: "No Verity token found. Run /verity-setup to configure." };
|
|
10778
10903
|
}
|
|
@@ -10806,7 +10931,7 @@ async function maybeHealServiceUrl(resolution, verbose) {
|
|
|
10806
10931
|
if (probe.reachable) {
|
|
10807
10932
|
return { serviceUrl: resolution.url, healed: false };
|
|
10808
10933
|
}
|
|
10809
|
-
const from = resolution.source === "credentials" ? "
|
|
10934
|
+
const from = resolution.source === "credentials" ? "~/.verity/credentials" : "VERITY.md";
|
|
10810
10935
|
printWarn(`Your configured Verity service URL is unreachable: ${resolution.url}`);
|
|
10811
10936
|
printInfo(` (${probe.error})`);
|
|
10812
10937
|
if (probe.dnsDead && (await probeService(DEFAULT_SERVICE_URL, verbose)).reachable) {
|
|
@@ -10823,8 +10948,6 @@ async function maybeHealServiceUrl(resolution, verbose) {
|
|
|
10823
10948
|
}
|
|
10824
10949
|
|
|
10825
10950
|
// src/lib/register.ts
|
|
10826
|
-
var import_promises3 = require("node:fs/promises");
|
|
10827
|
-
var import_node_path3 = require("node:path");
|
|
10828
10951
|
var readline = __toESM(require("node:readline/promises"));
|
|
10829
10952
|
|
|
10830
10953
|
// src/lib/provider-auth.ts
|
|
@@ -10934,7 +11057,7 @@ async function githubDeviceFlow() {
|
|
|
10934
11057
|
// src/lib/git.ts
|
|
10935
11058
|
var import_node_child_process3 = require("node:child_process");
|
|
10936
11059
|
var import_node_fs2 = require("node:fs");
|
|
10937
|
-
var
|
|
11060
|
+
var import_node_path3 = require("node:path");
|
|
10938
11061
|
function resolveFile(relpath) {
|
|
10939
11062
|
if ((0, import_node_fs2.existsSync)(relpath)) return relpath;
|
|
10940
11063
|
if ((0, import_node_fs2.existsSync)(".claude/worktrees")) {
|
|
@@ -10942,7 +11065,7 @@ function resolveFile(relpath) {
|
|
|
10942
11065
|
const entries = (0, import_node_fs2.readdirSync)(".claude/worktrees", { withFileTypes: true });
|
|
10943
11066
|
for (const entry of entries) {
|
|
10944
11067
|
if (!entry.isDirectory()) continue;
|
|
10945
|
-
const candidate = (0,
|
|
11068
|
+
const candidate = (0, import_node_path3.join)(".claude/worktrees", entry.name, relpath);
|
|
10946
11069
|
if ((0, import_node_fs2.existsSync)(candidate)) return candidate;
|
|
10947
11070
|
}
|
|
10948
11071
|
} catch {
|
|
@@ -10983,7 +11106,7 @@ function readBaselineSha() {
|
|
|
10983
11106
|
function writeBaselineSha(sha) {
|
|
10984
11107
|
if (!SHA_RE.test(sha)) return;
|
|
10985
11108
|
try {
|
|
10986
|
-
(0, import_node_fs2.mkdirSync)((0,
|
|
11109
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path3.dirname)(BASELINE_SHA_FILE), { recursive: true });
|
|
10987
11110
|
(0, import_node_fs2.writeFileSync)(BASELINE_SHA_FILE, sha);
|
|
10988
11111
|
} catch {
|
|
10989
11112
|
}
|
|
@@ -11077,7 +11200,7 @@ function getWorktreeFiles() {
|
|
|
11077
11200
|
const entries = (0, import_node_fs2.readdirSync)(worktreeDir, { withFileTypes: true });
|
|
11078
11201
|
for (const entry of entries) {
|
|
11079
11202
|
if (!entry.isDirectory()) continue;
|
|
11080
|
-
const wtDir = (0,
|
|
11203
|
+
const wtDir = (0, import_node_path3.join)(worktreeDir, entry.name);
|
|
11081
11204
|
scanDir(wtDir, wtDir, fiveMinAgo, result);
|
|
11082
11205
|
}
|
|
11083
11206
|
} catch {
|
|
@@ -11088,12 +11211,12 @@ function scanDir(baseDir, dir, minMtime, result) {
|
|
|
11088
11211
|
try {
|
|
11089
11212
|
const entries = (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true });
|
|
11090
11213
|
for (const entry of entries) {
|
|
11091
|
-
const fullPath = (0,
|
|
11214
|
+
const fullPath = (0, import_node_path3.join)(dir, entry.name);
|
|
11092
11215
|
if (entry.isDirectory()) {
|
|
11093
11216
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
11094
11217
|
scanDir(baseDir, fullPath, minMtime, result);
|
|
11095
11218
|
} else if (entry.isFile()) {
|
|
11096
|
-
const ext = (0,
|
|
11219
|
+
const ext = (0, import_node_path3.extname)(entry.name).slice(1);
|
|
11097
11220
|
if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
|
|
11098
11221
|
try {
|
|
11099
11222
|
const stat3 = (0, import_node_fs2.statSync)(fullPath);
|
|
@@ -11110,13 +11233,13 @@ function scanDir(baseDir, dir, minMtime, result) {
|
|
|
11110
11233
|
}
|
|
11111
11234
|
function filterAnalyzable(files) {
|
|
11112
11235
|
return files.filter((f) => {
|
|
11113
|
-
const ext = (0,
|
|
11236
|
+
const ext = (0, import_node_path3.extname)(f).slice(1);
|
|
11114
11237
|
return ANALYZABLE_EXTENSIONS.has(ext);
|
|
11115
11238
|
});
|
|
11116
11239
|
}
|
|
11117
11240
|
function filterReviewable(files) {
|
|
11118
11241
|
return files.filter((f) => {
|
|
11119
|
-
const ext = (0,
|
|
11242
|
+
const ext = (0, import_node_path3.extname)(f).slice(1);
|
|
11120
11243
|
if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
|
|
11121
11244
|
if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
|
|
11122
11245
|
const basename4 = f.split("/").pop() ?? "";
|
|
@@ -11249,38 +11372,19 @@ async function registerProject(opts) {
|
|
|
11249
11372
|
const { project_id, token, service_url, user } = result.data;
|
|
11250
11373
|
const userId = result.data.user_id ?? user?.id;
|
|
11251
11374
|
const email = user?.email;
|
|
11252
|
-
const identityLines = (userId != null ? `user_id: ${userId}
|
|
11253
|
-
` : "") + (email ? `email: ${email}
|
|
11254
|
-
` : "");
|
|
11255
11375
|
try {
|
|
11256
|
-
await (
|
|
11257
|
-
|
|
11258
|
-
|
|
11259
|
-
|
|
11260
|
-
|
|
11261
|
-
${identityLines}`,
|
|
11262
|
-
{ mode: 384 }
|
|
11263
|
-
);
|
|
11264
|
-
await (0, import_promises3.chmod)(CREDENTIALS_FILE, 384).catch(() => {
|
|
11376
|
+
await upsertGlobalCredential(opts.remote, {
|
|
11377
|
+
token,
|
|
11378
|
+
serviceUrl: service_url,
|
|
11379
|
+
userId: userId ?? void 0,
|
|
11380
|
+
email
|
|
11265
11381
|
});
|
|
11266
11382
|
} catch (err) {
|
|
11267
11383
|
return {
|
|
11268
11384
|
ok: false,
|
|
11269
|
-
error: `Registered with the Verity service, but could not save credentials to
|
|
11385
|
+
error: `Registered with the Verity service, but could not save credentials to ~/.verity/credentials: ${err.message}. Check filesystem permissions and re-run "verity auth register".`
|
|
11270
11386
|
};
|
|
11271
11387
|
}
|
|
11272
|
-
try {
|
|
11273
|
-
await (0, import_promises3.mkdir)((0, import_node_path3.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
|
|
11274
|
-
await (0, import_promises3.appendFile)(
|
|
11275
|
-
GLOBAL_CREDENTIALS_FILE,
|
|
11276
|
-
`${opts.remote} token: ${token}
|
|
11277
|
-
`,
|
|
11278
|
-
{ mode: 384 }
|
|
11279
|
-
);
|
|
11280
|
-
await (0, import_promises3.chmod)(GLOBAL_CREDENTIALS_FILE, 384).catch(() => {
|
|
11281
|
-
});
|
|
11282
|
-
} catch {
|
|
11283
|
-
}
|
|
11284
11388
|
return { ok: true, data: { projectId: project_id, serviceUrl: service_url, email, userId } };
|
|
11285
11389
|
}
|
|
11286
11390
|
|
|
@@ -11385,7 +11489,7 @@ function registerLoginCommand(program2) {
|
|
|
11385
11489
|
const heal = await maybeHealServiceUrl(urlResult.data, globals.verbose);
|
|
11386
11490
|
const serviceUrl = heal.serviceUrl;
|
|
11387
11491
|
if (heal.healed) {
|
|
11388
|
-
printInfo(" Completing login re-registers this project and updates
|
|
11492
|
+
printInfo(" Completing login re-registers this project and updates ~/.verity/credentials.");
|
|
11389
11493
|
}
|
|
11390
11494
|
const existing = await resolveToken(globals.token);
|
|
11391
11495
|
if (existing.ok && !opts.force && !heal.healed) {
|
|
@@ -11433,11 +11537,11 @@ function registerLoginCommand(program2) {
|
|
|
11433
11537
|
}
|
|
11434
11538
|
|
|
11435
11539
|
// src/lib/hooks.ts
|
|
11436
|
-
var
|
|
11540
|
+
var import_promises4 = require("node:fs/promises");
|
|
11437
11541
|
var import_node_path6 = require("node:path");
|
|
11438
11542
|
|
|
11439
11543
|
// src/lib/json-file.ts
|
|
11440
|
-
var
|
|
11544
|
+
var import_promises3 = require("node:fs/promises");
|
|
11441
11545
|
var import_node_path5 = require("node:path");
|
|
11442
11546
|
function jsonSemanticEqual(a, b) {
|
|
11443
11547
|
if (a === b) return true;
|
|
@@ -11471,7 +11575,7 @@ function detectJsonIndent(raw) {
|
|
|
11471
11575
|
async function writeJsonFilePreservingStyle(file, value) {
|
|
11472
11576
|
let currentRaw = null;
|
|
11473
11577
|
try {
|
|
11474
|
-
currentRaw = await (0,
|
|
11578
|
+
currentRaw = await (0, import_promises3.readFile)(file, "utf-8");
|
|
11475
11579
|
} catch {
|
|
11476
11580
|
currentRaw = null;
|
|
11477
11581
|
}
|
|
@@ -11484,8 +11588,8 @@ async function writeJsonFilePreservingStyle(file, value) {
|
|
|
11484
11588
|
const indent = currentRaw !== null ? detectJsonIndent(currentRaw) : 2;
|
|
11485
11589
|
const next = JSON.stringify(value, null, indent) + "\n";
|
|
11486
11590
|
if (next === currentRaw) return false;
|
|
11487
|
-
await (0,
|
|
11488
|
-
await (0,
|
|
11591
|
+
await (0, import_promises3.mkdir)((0, import_node_path5.dirname)(file), { recursive: true });
|
|
11592
|
+
await (0, import_promises3.writeFile)(file, next);
|
|
11489
11593
|
return true;
|
|
11490
11594
|
}
|
|
11491
11595
|
|
|
@@ -11567,7 +11671,7 @@ function globalSettingsFile() {
|
|
|
11567
11671
|
}
|
|
11568
11672
|
async function readSettings() {
|
|
11569
11673
|
try {
|
|
11570
|
-
const content = await (0,
|
|
11674
|
+
const content = await (0, import_promises4.readFile)(CLAUDE_SETTINGS_FILE, "utf-8");
|
|
11571
11675
|
return JSON.parse(content);
|
|
11572
11676
|
} catch {
|
|
11573
11677
|
return {};
|
|
@@ -11578,7 +11682,7 @@ async function readAllSettings() {
|
|
|
11578
11682
|
const out = [];
|
|
11579
11683
|
for (const f of files) {
|
|
11580
11684
|
try {
|
|
11581
|
-
out.push(JSON.parse(await (0,
|
|
11685
|
+
out.push(JSON.parse(await (0, import_promises4.readFile)(f, "utf-8")));
|
|
11582
11686
|
} catch {
|
|
11583
11687
|
}
|
|
11584
11688
|
}
|
|
@@ -11607,7 +11711,7 @@ async function checkExternalVerityHooks() {
|
|
|
11607
11711
|
for (const f of [SETTINGS_LOCAL_FILE, globalSettingsFile()]) {
|
|
11608
11712
|
let settings;
|
|
11609
11713
|
try {
|
|
11610
|
-
settings = JSON.parse(await (0,
|
|
11714
|
+
settings = JSON.parse(await (0, import_promises4.readFile)(f, "utf-8"));
|
|
11611
11715
|
} catch {
|
|
11612
11716
|
continue;
|
|
11613
11717
|
}
|
|
@@ -11645,7 +11749,7 @@ async function writeSettings(settings) {
|
|
|
11645
11749
|
}
|
|
11646
11750
|
async function readSettingsAt(root) {
|
|
11647
11751
|
try {
|
|
11648
|
-
return JSON.parse(await (0,
|
|
11752
|
+
return JSON.parse(await (0, import_promises4.readFile)((0, import_node_path6.join)(root, CLAUDE_SETTINGS_FILE), "utf-8"));
|
|
11649
11753
|
} catch {
|
|
11650
11754
|
return {};
|
|
11651
11755
|
}
|
|
@@ -11890,7 +11994,7 @@ function registerHooksCommands(program2) {
|
|
|
11890
11994
|
var import_node_crypto4 = require("node:crypto");
|
|
11891
11995
|
|
|
11892
11996
|
// src/lib/conversation-buffer.ts
|
|
11893
|
-
var
|
|
11997
|
+
var import_promises5 = require("node:fs/promises");
|
|
11894
11998
|
var import_node_fs3 = require("node:fs");
|
|
11895
11999
|
var import_node_child_process6 = require("node:child_process");
|
|
11896
12000
|
var import_node_crypto = require("node:crypto");
|
|
@@ -11902,7 +12006,7 @@ function bufferTmpPath() {
|
|
|
11902
12006
|
}
|
|
11903
12007
|
async function appendToConversationBuffer(prompt, sessionId) {
|
|
11904
12008
|
try {
|
|
11905
|
-
await (0,
|
|
12009
|
+
await (0, import_promises5.mkdir)(VERITY_DIR, { recursive: true });
|
|
11906
12010
|
let sanitized = prompt.length > MAX_INTENT_CHARS ? prompt.slice(0, MAX_INTENT_CHARS) : prompt;
|
|
11907
12011
|
sanitized = stripImageReferences(sanitized);
|
|
11908
12012
|
const entry = {
|
|
@@ -11920,8 +12024,8 @@ async function appendToConversationBuffer(prompt, sessionId) {
|
|
|
11920
12024
|
const capped = recent.slice(-CONVERSATION_MAX_ENTRIES);
|
|
11921
12025
|
const content = capped.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
11922
12026
|
const tmpFile = bufferTmpPath();
|
|
11923
|
-
await (0,
|
|
11924
|
-
await (0,
|
|
12027
|
+
await (0, import_promises5.writeFile)(tmpFile, content);
|
|
12028
|
+
await (0, import_promises5.rename)(tmpFile, CONVERSATION_BUFFER_FILE);
|
|
11925
12029
|
} catch {
|
|
11926
12030
|
}
|
|
11927
12031
|
}
|
|
@@ -11938,10 +12042,10 @@ async function readAndClearConversationBuffer(currentSessionId) {
|
|
|
11938
12042
|
if (others.length > 0) {
|
|
11939
12043
|
const remaining = others.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
11940
12044
|
const tmpFile = bufferTmpPath();
|
|
11941
|
-
await (0,
|
|
11942
|
-
await (0,
|
|
12045
|
+
await (0, import_promises5.writeFile)(tmpFile, remaining);
|
|
12046
|
+
await (0, import_promises5.rename)(tmpFile, CONVERSATION_BUFFER_FILE);
|
|
11943
12047
|
} else {
|
|
11944
|
-
await (0,
|
|
12048
|
+
await (0, import_promises5.unlink)(CONVERSATION_BUFFER_FILE).catch(() => {
|
|
11945
12049
|
});
|
|
11946
12050
|
}
|
|
11947
12051
|
if (mine.length > 0) {
|
|
@@ -11953,8 +12057,8 @@ async function readAndClearConversationBuffer(currentSessionId) {
|
|
|
11953
12057
|
}
|
|
11954
12058
|
if ((0, import_node_fs3.existsSync)(INTENT_FILE)) {
|
|
11955
12059
|
try {
|
|
11956
|
-
const content = await (0,
|
|
11957
|
-
await (0,
|
|
12060
|
+
const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
|
|
12061
|
+
await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
|
|
11958
12062
|
});
|
|
11959
12063
|
const data = JSON.parse(content);
|
|
11960
12064
|
if (data.prompt) {
|
|
@@ -11977,7 +12081,7 @@ async function readAndClearConversationBuffer(currentSessionId) {
|
|
|
11977
12081
|
}
|
|
11978
12082
|
async function readBufferEntries() {
|
|
11979
12083
|
try {
|
|
11980
|
-
const content = await (0,
|
|
12084
|
+
const content = await (0, import_promises5.readFile)(CONVERSATION_BUFFER_FILE, "utf-8");
|
|
11981
12085
|
const entries = [];
|
|
11982
12086
|
for (const line of content.split("\n")) {
|
|
11983
12087
|
const trimmed = line.trim();
|
|
@@ -12020,7 +12124,7 @@ function sessionScopeKey(token, sessionId) {
|
|
|
12020
12124
|
}
|
|
12021
12125
|
|
|
12022
12126
|
// src/lib/task-context-buffer.ts
|
|
12023
|
-
var
|
|
12127
|
+
var import_promises6 = require("node:fs/promises");
|
|
12024
12128
|
var import_node_fs4 = require("node:fs");
|
|
12025
12129
|
var import_node_path7 = require("node:path");
|
|
12026
12130
|
var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
|
|
@@ -12063,7 +12167,7 @@ async function readTaskContextBuffer(taskId) {
|
|
|
12063
12167
|
const filePath = bufferPath(taskId);
|
|
12064
12168
|
if (!(0, import_node_fs4.existsSync)(filePath)) return null;
|
|
12065
12169
|
try {
|
|
12066
|
-
const content = await (0,
|
|
12170
|
+
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
12067
12171
|
if (!content.trim()) return null;
|
|
12068
12172
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
12069
12173
|
const formatted = [];
|
|
@@ -12096,15 +12200,15 @@ async function readTaskContextBuffer(taskId) {
|
|
|
12096
12200
|
async function cleanupTaskContextBuffers() {
|
|
12097
12201
|
try {
|
|
12098
12202
|
if (!(0, import_node_fs4.existsSync)(TASK_CONTEXT_DIR)) return;
|
|
12099
|
-
const files = await (0,
|
|
12203
|
+
const files = await (0, import_promises6.readdir)(TASK_CONTEXT_DIR);
|
|
12100
12204
|
const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
|
|
12101
12205
|
for (const file of files) {
|
|
12102
12206
|
if (!file.endsWith(".jsonl")) continue;
|
|
12103
12207
|
const filePath = (0, import_node_path7.join)(TASK_CONTEXT_DIR, file);
|
|
12104
12208
|
try {
|
|
12105
|
-
const stats = await (0,
|
|
12209
|
+
const stats = await (0, import_promises6.stat)(filePath);
|
|
12106
12210
|
if (stats.mtimeMs < cutoffMs) {
|
|
12107
|
-
await (0,
|
|
12211
|
+
await (0, import_promises6.unlink)(filePath);
|
|
12108
12212
|
}
|
|
12109
12213
|
} catch {
|
|
12110
12214
|
}
|
|
@@ -12118,27 +12222,27 @@ function bufferPath(taskId) {
|
|
|
12118
12222
|
}
|
|
12119
12223
|
async function appendEntry(taskId, entry) {
|
|
12120
12224
|
try {
|
|
12121
|
-
await (0,
|
|
12225
|
+
await (0, import_promises6.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
|
|
12122
12226
|
const filePath = bufferPath(taskId);
|
|
12123
12227
|
if ((0, import_node_fs4.existsSync)(filePath)) {
|
|
12124
|
-
const stats = await (0,
|
|
12228
|
+
const stats = await (0, import_promises6.stat)(filePath);
|
|
12125
12229
|
if (stats.size >= MAX_BUFFER_BYTES) {
|
|
12126
|
-
const content = await (0,
|
|
12230
|
+
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
12127
12231
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
12128
12232
|
const keepFrom = Math.floor(lines.length * 0.25);
|
|
12129
12233
|
const pruned = lines.slice(keepFrom).join("\n") + "\n";
|
|
12130
|
-
await (0,
|
|
12234
|
+
await (0, import_promises6.writeFile)(filePath, pruned);
|
|
12131
12235
|
}
|
|
12132
12236
|
}
|
|
12133
12237
|
const line = JSON.stringify(entry) + "\n";
|
|
12134
|
-
const existing = (0, import_node_fs4.existsSync)(filePath) ? await (0,
|
|
12135
|
-
await (0,
|
|
12238
|
+
const existing = (0, import_node_fs4.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
|
|
12239
|
+
await (0, import_promises6.writeFile)(filePath, existing + line);
|
|
12136
12240
|
} catch {
|
|
12137
12241
|
}
|
|
12138
12242
|
}
|
|
12139
12243
|
|
|
12140
12244
|
// src/lib/memory-retrieval.ts
|
|
12141
|
-
var
|
|
12245
|
+
var import_promises7 = require("node:fs/promises");
|
|
12142
12246
|
var import_node_fs5 = require("node:fs");
|
|
12143
12247
|
var import_node_path8 = require("node:path");
|
|
12144
12248
|
var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
|
|
@@ -12241,11 +12345,11 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
|
|
|
12241
12345
|
const domainDir = (0, import_node_path8.join)(memoryDir(), domain);
|
|
12242
12346
|
if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
|
|
12243
12347
|
try {
|
|
12244
|
-
const files = await (0,
|
|
12348
|
+
const files = await (0, import_promises7.readdir)(domainDir);
|
|
12245
12349
|
for (const file of files) {
|
|
12246
12350
|
if (!file.endsWith(".md")) continue;
|
|
12247
12351
|
try {
|
|
12248
|
-
const content = await (0,
|
|
12352
|
+
const content = await (0, import_promises7.readFile)((0, import_node_path8.join)(domainDir, file), "utf-8");
|
|
12249
12353
|
const { fm, body } = parseFrontmatter(content);
|
|
12250
12354
|
if (fm.status && fm.status !== "active") continue;
|
|
12251
12355
|
nodes.push({
|
|
@@ -12303,7 +12407,7 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
|
|
|
12303
12407
|
}
|
|
12304
12408
|
|
|
12305
12409
|
// src/lib/memory-sync.ts
|
|
12306
|
-
var
|
|
12410
|
+
var import_promises8 = require("node:fs/promises");
|
|
12307
12411
|
var import_node_fs6 = require("node:fs");
|
|
12308
12412
|
var import_node_path9 = require("node:path");
|
|
12309
12413
|
var import_node_crypto3 = require("node:crypto");
|
|
@@ -12374,18 +12478,18 @@ var memoryDir2 = () => projectPath(`${VERITY_DIR}/memory`);
|
|
|
12374
12478
|
var DOMAINS2 = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations", "_archive"];
|
|
12375
12479
|
var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
|
|
12376
12480
|
async function ensureMemoryDir() {
|
|
12377
|
-
await (0,
|
|
12481
|
+
await (0, import_promises8.mkdir)(memoryDir2(), { recursive: true });
|
|
12378
12482
|
for (const domain of DOMAINS2) {
|
|
12379
|
-
await (0,
|
|
12483
|
+
await (0, import_promises8.mkdir)((0, import_node_path9.join)(memoryDir2(), domain), { recursive: true });
|
|
12380
12484
|
}
|
|
12381
12485
|
if (!(0, import_node_fs6.existsSync)((0, import_node_path9.join)(memoryDir2(), "SCHEMA.md"))) {
|
|
12382
|
-
await (0,
|
|
12486
|
+
await (0, import_promises8.writeFile)((0, import_node_path9.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
|
|
12383
12487
|
}
|
|
12384
12488
|
if (!(0, import_node_fs6.existsSync)((0, import_node_path9.join)(memoryDir2(), "index.md"))) {
|
|
12385
|
-
await (0,
|
|
12489
|
+
await (0, import_promises8.writeFile)((0, import_node_path9.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
|
|
12386
12490
|
}
|
|
12387
12491
|
if (!(0, import_node_fs6.existsSync)((0, import_node_path9.join)(memoryDir2(), "log.md"))) {
|
|
12388
|
-
await (0,
|
|
12492
|
+
await (0, import_promises8.writeFile)((0, import_node_path9.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
|
|
12389
12493
|
}
|
|
12390
12494
|
}
|
|
12391
12495
|
async function buildManifest() {
|
|
@@ -12397,13 +12501,13 @@ async function buildManifest() {
|
|
|
12397
12501
|
const domainDir = (0, import_node_path9.join)(memoryDir2(), domain);
|
|
12398
12502
|
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
12399
12503
|
try {
|
|
12400
|
-
const files = await (0,
|
|
12504
|
+
const files = await (0, import_promises8.readdir)(domainDir);
|
|
12401
12505
|
for (const file of files) {
|
|
12402
12506
|
if (!file.endsWith(".md")) continue;
|
|
12403
12507
|
const filePath = `${domain}/${file}`;
|
|
12404
12508
|
const fullPath = (0, import_node_path9.join)(memoryDir2(), filePath);
|
|
12405
12509
|
try {
|
|
12406
|
-
const content = await (0,
|
|
12510
|
+
const content = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
12407
12511
|
const hash = (0, import_node_crypto3.createHash)("sha256").update(content).digest("hex").slice(0, 16);
|
|
12408
12512
|
nodes.push({ path: filePath, content_hash: `sha256:${hash}` });
|
|
12409
12513
|
} catch {
|
|
@@ -12414,13 +12518,13 @@ async function buildManifest() {
|
|
|
12414
12518
|
}
|
|
12415
12519
|
let indexHash = null;
|
|
12416
12520
|
try {
|
|
12417
|
-
const indexContent = await (0,
|
|
12521
|
+
const indexContent = await (0, import_promises8.readFile)((0, import_node_path9.join)(memoryDir2(), "index.md"), "utf-8");
|
|
12418
12522
|
indexHash = `sha256:${(0, import_node_crypto3.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
|
|
12419
12523
|
} catch {
|
|
12420
12524
|
}
|
|
12421
12525
|
let logLength = 0;
|
|
12422
12526
|
try {
|
|
12423
|
-
const logContent = await (0,
|
|
12527
|
+
const logContent = await (0, import_promises8.readFile)((0, import_node_path9.join)(memoryDir2(), "log.md"), "utf-8");
|
|
12424
12528
|
logLength = logContent.split("\n").length;
|
|
12425
12529
|
} catch {
|
|
12426
12530
|
}
|
|
@@ -12436,10 +12540,10 @@ async function readOnDiskNodes() {
|
|
|
12436
12540
|
const domainDir = (0, import_node_path9.join)(memoryDir2(), domain);
|
|
12437
12541
|
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
12438
12542
|
try {
|
|
12439
|
-
for (const file of await (0,
|
|
12543
|
+
for (const file of await (0, import_promises8.readdir)(domainDir)) {
|
|
12440
12544
|
if (!file.endsWith(".md")) continue;
|
|
12441
12545
|
try {
|
|
12442
|
-
out.set(`${domain}/${file}`, hashContent(await (0,
|
|
12546
|
+
out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path9.join)(domainDir, file), "utf-8")));
|
|
12443
12547
|
} catch {
|
|
12444
12548
|
}
|
|
12445
12549
|
}
|
|
@@ -12451,7 +12555,7 @@ async function readOnDiskNodes() {
|
|
|
12451
12555
|
async function readSyncBaseline() {
|
|
12452
12556
|
const out = /* @__PURE__ */ new Map();
|
|
12453
12557
|
try {
|
|
12454
|
-
const parsed = JSON.parse(await (0,
|
|
12558
|
+
const parsed = JSON.parse(await (0, import_promises8.readFile)(syncStateFile(), "utf-8"));
|
|
12455
12559
|
if (Array.isArray(parsed?.nodes)) {
|
|
12456
12560
|
for (const n of parsed.nodes) if (n?.path) out.set(n.path, n.hash ?? null);
|
|
12457
12561
|
} else if (Array.isArray(parsed?.paths)) {
|
|
@@ -12467,12 +12571,12 @@ async function recordSyncedNodePaths() {
|
|
|
12467
12571
|
const next = JSON.stringify({ schema: 2, nodes }) + "\n";
|
|
12468
12572
|
let existing = "";
|
|
12469
12573
|
try {
|
|
12470
|
-
existing = await (0,
|
|
12574
|
+
existing = await (0, import_promises8.readFile)(syncStateFile(), "utf-8");
|
|
12471
12575
|
} catch {
|
|
12472
12576
|
}
|
|
12473
12577
|
if (existing === next) return;
|
|
12474
|
-
await (0,
|
|
12475
|
-
await (0,
|
|
12578
|
+
await (0, import_promises8.mkdir)(projectPath(VERITY_DIR), { recursive: true });
|
|
12579
|
+
await (0, import_promises8.writeFile)(syncStateFile(), next);
|
|
12476
12580
|
} catch {
|
|
12477
12581
|
}
|
|
12478
12582
|
}
|
|
@@ -12489,7 +12593,7 @@ async function computeEditedNodeUploads() {
|
|
|
12489
12593
|
if (!(0, import_node_fs6.existsSync)(full)) continue;
|
|
12490
12594
|
let content;
|
|
12491
12595
|
try {
|
|
12492
|
-
content = await (0,
|
|
12596
|
+
content = await (0, import_promises8.readFile)(full, "utf-8");
|
|
12493
12597
|
} catch {
|
|
12494
12598
|
continue;
|
|
12495
12599
|
}
|
|
@@ -12522,8 +12626,8 @@ async function applyMemoryWrites(writes, opts = {}) {
|
|
|
12522
12626
|
const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
|
|
12523
12627
|
for (const n of notes) logLines.push(` - ${n}`);
|
|
12524
12628
|
try {
|
|
12525
|
-
const existing = (0, import_node_fs6.existsSync)((0, import_node_path9.join)(memoryDir2(), "log.md")) ? await (0,
|
|
12526
|
-
await (0,
|
|
12629
|
+
const existing = (0, import_node_fs6.existsSync)((0, import_node_path9.join)(memoryDir2(), "log.md")) ? await (0, import_promises8.readFile)((0, import_node_path9.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
|
|
12630
|
+
await (0, import_promises8.writeFile)((0, import_node_path9.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
|
|
12527
12631
|
} catch {
|
|
12528
12632
|
}
|
|
12529
12633
|
await recordSyncedNodePaths();
|
|
@@ -12543,7 +12647,7 @@ async function applyOneWrite(write, treePaths) {
|
|
|
12543
12647
|
if ((0, import_node_fs6.existsSync)(fullPath)) {
|
|
12544
12648
|
let existing = "";
|
|
12545
12649
|
try {
|
|
12546
|
-
existing = await (0,
|
|
12650
|
+
existing = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
12547
12651
|
} catch {
|
|
12548
12652
|
}
|
|
12549
12653
|
if (existing === content) return { written: false, notes };
|
|
@@ -12552,8 +12656,8 @@ async function applyOneWrite(write, treePaths) {
|
|
|
12552
12656
|
return { written: false, notes };
|
|
12553
12657
|
}
|
|
12554
12658
|
}
|
|
12555
|
-
await (0,
|
|
12556
|
-
await (0,
|
|
12659
|
+
await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(fullPath), { recursive: true });
|
|
12660
|
+
await (0, import_promises8.writeFile)(fullPath, content);
|
|
12557
12661
|
return { written: true, notes };
|
|
12558
12662
|
}
|
|
12559
12663
|
function groundFileGlobs(content, treePaths) {
|
|
@@ -12596,7 +12700,7 @@ async function regenerateIndex() {
|
|
|
12596
12700
|
const domainDir = (0, import_node_path9.join)(memoryDir2(), domain);
|
|
12597
12701
|
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
12598
12702
|
try {
|
|
12599
|
-
const files = await (0,
|
|
12703
|
+
const files = await (0, import_promises8.readdir)(domainDir);
|
|
12600
12704
|
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
12601
12705
|
if (mdFiles.length === 0) continue;
|
|
12602
12706
|
lines.push(`## ${domain}/ (${mdFiles.length})`);
|
|
@@ -12604,7 +12708,7 @@ async function regenerateIndex() {
|
|
|
12604
12708
|
for (const file of mdFiles.sort()) {
|
|
12605
12709
|
const slug = file.replace(/\.md$/, "");
|
|
12606
12710
|
try {
|
|
12607
|
-
const content = await (0,
|
|
12711
|
+
const content = await (0, import_promises8.readFile)((0, import_node_path9.join)(domainDir, file), "utf-8");
|
|
12608
12712
|
const title = pickFrontmatter(content, "title") ?? slug;
|
|
12609
12713
|
const kind = pickFrontmatter(content, "kind") ?? "-";
|
|
12610
12714
|
const confidence = pickFrontmatter(content, "confidence");
|
|
@@ -12631,11 +12735,11 @@ async function regenerateIndex() {
|
|
|
12631
12735
|
const indexPath = (0, import_node_path9.join)(memoryDir2(), "index.md");
|
|
12632
12736
|
let existing = null;
|
|
12633
12737
|
try {
|
|
12634
|
-
existing = await (0,
|
|
12738
|
+
existing = await (0, import_promises8.readFile)(indexPath, "utf-8");
|
|
12635
12739
|
} catch {
|
|
12636
12740
|
}
|
|
12637
12741
|
if (existing === next) return;
|
|
12638
|
-
await (0,
|
|
12742
|
+
await (0, import_promises8.writeFile)(indexPath, next);
|
|
12639
12743
|
}
|
|
12640
12744
|
function pickFrontmatter(content, key) {
|
|
12641
12745
|
const re = new RegExp(`^${key}:\\s*"?([^"\\n]+?)"?\\s*$`, "m");
|
|
@@ -12713,7 +12817,7 @@ async function ensureClaudeMdPointer(cwd = repoRoot()) {
|
|
|
12713
12817
|
const claudeMdPath = (0, import_node_path9.join)(cwd, "CLAUDE.md");
|
|
12714
12818
|
let existing = "";
|
|
12715
12819
|
if ((0, import_node_fs6.existsSync)(claudeMdPath)) {
|
|
12716
|
-
existing = await (0,
|
|
12820
|
+
existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
|
|
12717
12821
|
}
|
|
12718
12822
|
let startTag = CLAUDE_MD_START;
|
|
12719
12823
|
let endTag = CLAUDE_MD_END;
|
|
@@ -12769,7 +12873,7 @@ async function ensureClaudeMdPointer(cwd = repoRoot()) {
|
|
|
12769
12873
|
next = existing.replace(/\n*$/, "") + "\n\n" + block + "\n";
|
|
12770
12874
|
}
|
|
12771
12875
|
if (next === existing) return;
|
|
12772
|
-
await (0,
|
|
12876
|
+
await (0, import_promises8.writeFile)(claudeMdPath, next);
|
|
12773
12877
|
}
|
|
12774
12878
|
function extractPreserveContent(interior) {
|
|
12775
12879
|
for (const [start, end] of [
|
|
@@ -12955,7 +13059,7 @@ async function fireClassify(prompt, sessionId) {
|
|
|
12955
13059
|
}
|
|
12956
13060
|
|
|
12957
13061
|
// src/commands/standard.ts
|
|
12958
|
-
var
|
|
13062
|
+
var import_promises9 = require("node:fs/promises");
|
|
12959
13063
|
var import_yaml = __toESM(require_dist());
|
|
12960
13064
|
function registerStandardCommands(program2) {
|
|
12961
13065
|
const standard = program2.command("standard").description("Manage the project Standard");
|
|
@@ -12973,7 +13077,7 @@ function registerStandardCommands(program2) {
|
|
|
12973
13077
|
}
|
|
12974
13078
|
let yamlContent;
|
|
12975
13079
|
try {
|
|
12976
|
-
yamlContent = await (0,
|
|
13080
|
+
yamlContent = await (0, import_promises9.readFile)(opts.file, "utf-8");
|
|
12977
13081
|
} catch {
|
|
12978
13082
|
printError(`Cannot read ${opts.file}`);
|
|
12979
13083
|
process.exit(1);
|
|
@@ -13064,7 +13168,7 @@ function registerStandardCommands(program2) {
|
|
|
13064
13168
|
}
|
|
13065
13169
|
|
|
13066
13170
|
// src/commands/config.ts
|
|
13067
|
-
var
|
|
13171
|
+
var import_promises10 = require("node:fs/promises");
|
|
13068
13172
|
function registerConfigCommands(program2) {
|
|
13069
13173
|
const config = program2.command("config").description("Manage analysis configuration");
|
|
13070
13174
|
config.command("push").description("Upload the analysis config to the service").option("--file <path>", "Path to config file", CODACY_CONFIG_FILE).action(async (opts) => {
|
|
@@ -13081,7 +13185,7 @@ function registerConfigCommands(program2) {
|
|
|
13081
13185
|
}
|
|
13082
13186
|
let content;
|
|
13083
13187
|
try {
|
|
13084
|
-
const raw = await (0,
|
|
13188
|
+
const raw = await (0, import_promises10.readFile)(opts.file, "utf-8");
|
|
13085
13189
|
content = JSON.parse(raw);
|
|
13086
13190
|
} catch {
|
|
13087
13191
|
printError(`Cannot read or parse ${opts.file}`);
|
|
@@ -14773,7 +14877,7 @@ function isExplicitlyAutonomous(env = process.env) {
|
|
|
14773
14877
|
}
|
|
14774
14878
|
|
|
14775
14879
|
// src/lib/seed-runner.ts
|
|
14776
|
-
var
|
|
14880
|
+
var import_promises11 = require("node:fs/promises");
|
|
14777
14881
|
var import_node_fs18 = require("node:fs");
|
|
14778
14882
|
var import_node_path15 = require("node:path");
|
|
14779
14883
|
var import_yaml2 = __toESM(require_dist());
|
|
@@ -15019,7 +15123,7 @@ async function runSeed(opts) {
|
|
|
15019
15123
|
}
|
|
15020
15124
|
let standardDoc;
|
|
15021
15125
|
try {
|
|
15022
|
-
const raw = await (0,
|
|
15126
|
+
const raw = await (0, import_promises11.readFile)(STANDARD_FILE, "utf-8");
|
|
15023
15127
|
standardDoc = (0, import_yaml2.parse)(raw);
|
|
15024
15128
|
} catch {
|
|
15025
15129
|
return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
@@ -15028,7 +15132,7 @@ async function runSeed(opts) {
|
|
|
15028
15132
|
let readmeContent;
|
|
15029
15133
|
if ((0, import_node_fs18.existsSync)("README.md")) {
|
|
15030
15134
|
try {
|
|
15031
|
-
readmeContent = await (0,
|
|
15135
|
+
readmeContent = await (0, import_promises11.readFile)("README.md", "utf-8");
|
|
15032
15136
|
} catch {
|
|
15033
15137
|
}
|
|
15034
15138
|
}
|
|
@@ -15036,7 +15140,7 @@ async function runSeed(opts) {
|
|
|
15036
15140
|
for (const p of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
|
|
15037
15141
|
if ((0, import_node_fs18.existsSync)(p)) {
|
|
15038
15142
|
try {
|
|
15039
|
-
claudeMdContent = await (0,
|
|
15143
|
+
claudeMdContent = await (0, import_promises11.readFile)(p, "utf-8");
|
|
15040
15144
|
break;
|
|
15041
15145
|
} catch {
|
|
15042
15146
|
}
|
|
@@ -15095,8 +15199,8 @@ async function runSeed(opts) {
|
|
|
15095
15199
|
const filePathRel = res.data.file_path;
|
|
15096
15200
|
const targetPath = (0, import_node_path15.join)(MEMORY_DIR, filePathRel);
|
|
15097
15201
|
try {
|
|
15098
|
-
await (0,
|
|
15099
|
-
await (0,
|
|
15202
|
+
await (0, import_promises11.mkdir)((0, import_node_path15.dirname)(targetPath), { recursive: true });
|
|
15203
|
+
await (0, import_promises11.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
|
|
15100
15204
|
created++;
|
|
15101
15205
|
opts.onCreated?.(nodeId, filePathRel, c);
|
|
15102
15206
|
} catch (err) {
|
|
@@ -16217,6 +16321,98 @@ var readline2 = __toESM(require("node:readline/promises"));
|
|
|
16217
16321
|
var import_node_fs23 = require("node:fs");
|
|
16218
16322
|
var import_node_path18 = require("node:path");
|
|
16219
16323
|
var import_node_child_process10 = require("node:child_process");
|
|
16324
|
+
|
|
16325
|
+
// src/lib/telemetry.ts
|
|
16326
|
+
var import_promises12 = require("node:fs/promises");
|
|
16327
|
+
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
16328
|
+
var GITIGNORE_FILE = ".gitignore";
|
|
16329
|
+
var GITIGNORE_ENTRY = ".claude/settings.local.json";
|
|
16330
|
+
var OTEL_HEADERS_HELPER_CMD = "verity telemetry headers";
|
|
16331
|
+
var LEGACY_TELEMETRY_ENV_KEYS = ["OTEL_EXPORTER_OTLP_HEADERS"];
|
|
16332
|
+
function deriveOtlpEndpoint(serviceUrl) {
|
|
16333
|
+
return serviceUrl.replace(/\/+$/, "").replace(/\/v1$/, "") + "/v1/otlp";
|
|
16334
|
+
}
|
|
16335
|
+
function buildTelemetryEnv(serviceUrl) {
|
|
16336
|
+
return {
|
|
16337
|
+
CLAUDE_CODE_ENABLE_TELEMETRY: "1",
|
|
16338
|
+
OTEL_METRICS_EXPORTER: "otlp",
|
|
16339
|
+
OTEL_TRACES_EXPORTER: "otlp",
|
|
16340
|
+
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA: "1",
|
|
16341
|
+
OTEL_EXPORTER_OTLP_PROTOCOL: "http/json",
|
|
16342
|
+
OTEL_EXPORTER_OTLP_ENDPOINT: deriveOtlpEndpoint(serviceUrl),
|
|
16343
|
+
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: "delta",
|
|
16344
|
+
OTEL_LOG_USER_PROMPTS: "0",
|
|
16345
|
+
OTEL_LOG_TOOL_DETAILS: "0",
|
|
16346
|
+
OTEL_LOG_TOOL_CONTENT: "0",
|
|
16347
|
+
OTEL_METRICS_INCLUDE_SESSION_ID: "true",
|
|
16348
|
+
OTEL_METRICS_INCLUDE_ACCOUNT_UUID: "false",
|
|
16349
|
+
OTEL_METRICS_INCLUDE_VERSION: "false",
|
|
16350
|
+
OTEL_METRIC_EXPORT_INTERVAL: "60000"
|
|
16351
|
+
};
|
|
16352
|
+
}
|
|
16353
|
+
var VERITY_TELEMETRY_KEYS = Object.keys(buildTelemetryEnv(""));
|
|
16354
|
+
async function readSettingsLocal() {
|
|
16355
|
+
try {
|
|
16356
|
+
return JSON.parse(await (0, import_promises12.readFile)(projectPath(SETTINGS_LOCAL_FILE2), "utf-8"));
|
|
16357
|
+
} catch {
|
|
16358
|
+
return {};
|
|
16359
|
+
}
|
|
16360
|
+
}
|
|
16361
|
+
async function writeSettingsLocal(settings) {
|
|
16362
|
+
await writeJsonFilePreservingStyle(projectPath(SETTINGS_LOCAL_FILE2), settings);
|
|
16363
|
+
}
|
|
16364
|
+
async function ensureGitignore() {
|
|
16365
|
+
const file = projectPath(GITIGNORE_FILE);
|
|
16366
|
+
let content = "";
|
|
16367
|
+
try {
|
|
16368
|
+
content = await (0, import_promises12.readFile)(file, "utf-8");
|
|
16369
|
+
} catch {
|
|
16370
|
+
}
|
|
16371
|
+
const lines = content.split("\n").map((l) => l.trim());
|
|
16372
|
+
if (lines.includes(GITIGNORE_ENTRY) || lines.includes(".claude/") || lines.includes(".claude")) {
|
|
16373
|
+
return;
|
|
16374
|
+
}
|
|
16375
|
+
const block = "# Verity telemetry \u2014 machine-local Claude Code settings\n" + GITIGNORE_ENTRY + "\n";
|
|
16376
|
+
const next = content ? content + (content.endsWith("\n") ? "" : "\n") + "\n" + block : block;
|
|
16377
|
+
await (0, import_promises12.writeFile)(file, next);
|
|
16378
|
+
}
|
|
16379
|
+
async function installTelemetry(serviceUrl) {
|
|
16380
|
+
const env = buildTelemetryEnv(serviceUrl);
|
|
16381
|
+
const settings = await readSettingsLocal();
|
|
16382
|
+
settings.env = { ...settings.env ?? {}, ...env };
|
|
16383
|
+
for (const key of LEGACY_TELEMETRY_ENV_KEYS) delete settings.env[key];
|
|
16384
|
+
settings.otelHeadersHelper = OTEL_HEADERS_HELPER_CMD;
|
|
16385
|
+
await writeSettingsLocal(settings);
|
|
16386
|
+
await ensureGitignore();
|
|
16387
|
+
return { ok: true, data: { endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT } };
|
|
16388
|
+
}
|
|
16389
|
+
async function checkTelemetry() {
|
|
16390
|
+
const env = (await readSettingsLocal()).env ?? {};
|
|
16391
|
+
const enabled = env.CLAUDE_CODE_ENABLE_TELEMETRY === "1" && !!env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
16392
|
+
return { enabled, endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT ?? null, settingsPath: SETTINGS_LOCAL_FILE2 };
|
|
16393
|
+
}
|
|
16394
|
+
async function uninstallTelemetry() {
|
|
16395
|
+
const settings = await readSettingsLocal();
|
|
16396
|
+
const hadHelper = settings.otelHeadersHelper === OTEL_HEADERS_HELPER_CMD;
|
|
16397
|
+
if (hadHelper) delete settings.otelHeadersHelper;
|
|
16398
|
+
if (!settings.env) {
|
|
16399
|
+
if (hadHelper) await writeSettingsLocal(settings);
|
|
16400
|
+
return { ok: true, data: { removed: 0 } };
|
|
16401
|
+
}
|
|
16402
|
+
let removed = 0;
|
|
16403
|
+
for (const key of VERITY_TELEMETRY_KEYS) {
|
|
16404
|
+
if (key in settings.env) {
|
|
16405
|
+
delete settings.env[key];
|
|
16406
|
+
removed++;
|
|
16407
|
+
}
|
|
16408
|
+
}
|
|
16409
|
+
for (const key of LEGACY_TELEMETRY_ENV_KEYS) delete settings.env[key];
|
|
16410
|
+
if (Object.keys(settings.env).length === 0) delete settings.env;
|
|
16411
|
+
await writeSettingsLocal(settings);
|
|
16412
|
+
return { ok: true, data: { removed } };
|
|
16413
|
+
}
|
|
16414
|
+
|
|
16415
|
+
// src/commands/migrate.ts
|
|
16220
16416
|
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
16221
16417
|
function defaultNpmRemover(pkg) {
|
|
16222
16418
|
(0, import_node_child_process10.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
|
|
@@ -16247,6 +16443,7 @@ async function runMigration(opts = {}) {
|
|
|
16247
16443
|
await migrateLegacyHooks(root, actions);
|
|
16248
16444
|
await migrateClaudeMd(root, actions);
|
|
16249
16445
|
migrateStandardFile(root, actions);
|
|
16446
|
+
await migrateTelemetryHeaders(root, actions);
|
|
16250
16447
|
removeLegacyPackage(movedProjectDir, npmRemover, actions);
|
|
16251
16448
|
return { actions, migrated: actions.length > 0 };
|
|
16252
16449
|
}
|
|
@@ -16360,6 +16557,25 @@ function migrateStandardFile(root, actions) {
|
|
|
16360
16557
|
if (refreshed !== content) (0, import_node_fs23.writeFileSync)(verityMd, refreshed);
|
|
16361
16558
|
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
16362
16559
|
}
|
|
16560
|
+
async function migrateTelemetryHeaders(root, actions) {
|
|
16561
|
+
const file = (0, import_node_path18.join)(root, ".claude", "settings.local.json");
|
|
16562
|
+
if (!(0, import_node_fs23.existsSync)(file)) return;
|
|
16563
|
+
let settings;
|
|
16564
|
+
try {
|
|
16565
|
+
settings = JSON.parse(readFileSyncSafe(file) || "{}");
|
|
16566
|
+
} catch {
|
|
16567
|
+
return;
|
|
16568
|
+
}
|
|
16569
|
+
const env = settings.env;
|
|
16570
|
+
const hasInlineHeader = !!env && "OTEL_EXPORTER_OTLP_HEADERS" in env;
|
|
16571
|
+
const telemetryEnabled = env?.CLAUDE_CODE_ENABLE_TELEMETRY === "1";
|
|
16572
|
+
const needsHelper = telemetryEnabled && settings.otelHeadersHelper !== OTEL_HEADERS_HELPER_CMD;
|
|
16573
|
+
if (!hasInlineHeader && !needsHelper) return;
|
|
16574
|
+
if (hasInlineHeader && env) delete env["OTEL_EXPORTER_OTLP_HEADERS"];
|
|
16575
|
+
if (telemetryEnabled) settings.otelHeadersHelper = OTEL_HEADERS_HELPER_CMD;
|
|
16576
|
+
await writeJsonFilePreservingStyle(file, settings);
|
|
16577
|
+
actions.push("Moved the telemetry token out of .claude/settings.local.json (now resolved at runtime)");
|
|
16578
|
+
}
|
|
16363
16579
|
function removeLegacyPackage(movedProjectDir, npmRemover, actions) {
|
|
16364
16580
|
if (!movedProjectDir) return;
|
|
16365
16581
|
try {
|
|
@@ -16532,7 +16748,7 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
16532
16748
|
serviceUrl = heal.serviceUrl;
|
|
16533
16749
|
healed = heal.healed;
|
|
16534
16750
|
if (healed) {
|
|
16535
|
-
printInfo(" Log in below to re-register this project and repair
|
|
16751
|
+
printInfo(" Log in below to re-register this project and repair ~/.verity/credentials.");
|
|
16536
16752
|
}
|
|
16537
16753
|
}
|
|
16538
16754
|
if (!healed) {
|
|
@@ -17330,89 +17546,6 @@ function registerRunCommand(program2) {
|
|
|
17330
17546
|
});
|
|
17331
17547
|
}
|
|
17332
17548
|
|
|
17333
|
-
// src/lib/telemetry.ts
|
|
17334
|
-
var import_promises14 = require("node:fs/promises");
|
|
17335
|
-
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
17336
|
-
var GITIGNORE_FILE = ".gitignore";
|
|
17337
|
-
var GITIGNORE_ENTRY = ".claude/settings.local.json";
|
|
17338
|
-
function deriveOtlpEndpoint(serviceUrl) {
|
|
17339
|
-
return serviceUrl.replace(/\/+$/, "").replace(/\/v1$/, "") + "/v1/otlp";
|
|
17340
|
-
}
|
|
17341
|
-
function buildTelemetryEnv(serviceUrl, token) {
|
|
17342
|
-
return {
|
|
17343
|
-
CLAUDE_CODE_ENABLE_TELEMETRY: "1",
|
|
17344
|
-
OTEL_METRICS_EXPORTER: "otlp",
|
|
17345
|
-
OTEL_TRACES_EXPORTER: "otlp",
|
|
17346
|
-
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA: "1",
|
|
17347
|
-
OTEL_EXPORTER_OTLP_PROTOCOL: "http/json",
|
|
17348
|
-
OTEL_EXPORTER_OTLP_ENDPOINT: deriveOtlpEndpoint(serviceUrl),
|
|
17349
|
-
// <token> is whatever is in .verity/credentials — verity_ OR legacy gate_.
|
|
17350
|
-
// The server hashes the raw token regardless of prefix.
|
|
17351
|
-
OTEL_EXPORTER_OTLP_HEADERS: `Authorization=Bearer ${token}`,
|
|
17352
|
-
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: "delta",
|
|
17353
|
-
OTEL_LOG_USER_PROMPTS: "0",
|
|
17354
|
-
OTEL_LOG_TOOL_DETAILS: "0",
|
|
17355
|
-
OTEL_LOG_TOOL_CONTENT: "0",
|
|
17356
|
-
OTEL_METRICS_INCLUDE_SESSION_ID: "true",
|
|
17357
|
-
OTEL_METRICS_INCLUDE_ACCOUNT_UUID: "false",
|
|
17358
|
-
OTEL_METRICS_INCLUDE_VERSION: "false",
|
|
17359
|
-
OTEL_METRIC_EXPORT_INTERVAL: "60000"
|
|
17360
|
-
};
|
|
17361
|
-
}
|
|
17362
|
-
var VERITY_TELEMETRY_KEYS = Object.keys(buildTelemetryEnv("", ""));
|
|
17363
|
-
async function readSettingsLocal() {
|
|
17364
|
-
try {
|
|
17365
|
-
return JSON.parse(await (0, import_promises14.readFile)(projectPath(SETTINGS_LOCAL_FILE2), "utf-8"));
|
|
17366
|
-
} catch {
|
|
17367
|
-
return {};
|
|
17368
|
-
}
|
|
17369
|
-
}
|
|
17370
|
-
async function writeSettingsLocal(settings) {
|
|
17371
|
-
await writeJsonFilePreservingStyle(projectPath(SETTINGS_LOCAL_FILE2), settings);
|
|
17372
|
-
}
|
|
17373
|
-
async function ensureGitignore() {
|
|
17374
|
-
const file = projectPath(GITIGNORE_FILE);
|
|
17375
|
-
let content = "";
|
|
17376
|
-
try {
|
|
17377
|
-
content = await (0, import_promises14.readFile)(file, "utf-8");
|
|
17378
|
-
} catch {
|
|
17379
|
-
}
|
|
17380
|
-
const lines = content.split("\n").map((l) => l.trim());
|
|
17381
|
-
if (lines.includes(GITIGNORE_ENTRY) || lines.includes(".claude/") || lines.includes(".claude")) {
|
|
17382
|
-
return;
|
|
17383
|
-
}
|
|
17384
|
-
const block = "# Verity telemetry \u2014 holds your project token\n" + GITIGNORE_ENTRY + "\n";
|
|
17385
|
-
const next = content ? content + (content.endsWith("\n") ? "" : "\n") + "\n" + block : block;
|
|
17386
|
-
await (0, import_promises14.writeFile)(file, next);
|
|
17387
|
-
}
|
|
17388
|
-
async function installTelemetry(serviceUrl, token) {
|
|
17389
|
-
const env = buildTelemetryEnv(serviceUrl, token);
|
|
17390
|
-
const settings = await readSettingsLocal();
|
|
17391
|
-
settings.env = { ...settings.env ?? {}, ...env };
|
|
17392
|
-
await writeSettingsLocal(settings);
|
|
17393
|
-
await ensureGitignore();
|
|
17394
|
-
return { ok: true, data: { endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT } };
|
|
17395
|
-
}
|
|
17396
|
-
async function checkTelemetry() {
|
|
17397
|
-
const env = (await readSettingsLocal()).env ?? {};
|
|
17398
|
-
const enabled = env.CLAUDE_CODE_ENABLE_TELEMETRY === "1" && !!env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
17399
|
-
return { enabled, endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT ?? null, settingsPath: SETTINGS_LOCAL_FILE2 };
|
|
17400
|
-
}
|
|
17401
|
-
async function uninstallTelemetry() {
|
|
17402
|
-
const settings = await readSettingsLocal();
|
|
17403
|
-
if (!settings.env) return { ok: true, data: { removed: 0 } };
|
|
17404
|
-
let removed = 0;
|
|
17405
|
-
for (const key of VERITY_TELEMETRY_KEYS) {
|
|
17406
|
-
if (key in settings.env) {
|
|
17407
|
-
delete settings.env[key];
|
|
17408
|
-
removed++;
|
|
17409
|
-
}
|
|
17410
|
-
}
|
|
17411
|
-
if (Object.keys(settings.env).length === 0) delete settings.env;
|
|
17412
|
-
await writeSettingsLocal(settings);
|
|
17413
|
-
return { ok: true, data: { removed } };
|
|
17414
|
-
}
|
|
17415
|
-
|
|
17416
17549
|
// src/commands/telemetry.ts
|
|
17417
17550
|
function registerTelemetryCommands(program2) {
|
|
17418
17551
|
const telemetry = program2.command("telemetry").description("Manage Claude Code OpenTelemetry export to Verity (cost & usage)");
|
|
@@ -17428,16 +17561,25 @@ function registerTelemetryCommands(program2) {
|
|
|
17428
17561
|
printError(urlResult.error);
|
|
17429
17562
|
process.exit(1);
|
|
17430
17563
|
}
|
|
17431
|
-
const result = await installTelemetry(urlResult.data
|
|
17564
|
+
const result = await installTelemetry(urlResult.data);
|
|
17432
17565
|
if (!result.ok) {
|
|
17433
17566
|
printError(result.error);
|
|
17434
17567
|
process.exit(1);
|
|
17435
17568
|
}
|
|
17436
17569
|
printInfo(`Telemetry enabled \u2192 ${result.data.endpoint}`);
|
|
17437
|
-
printInfo(` wrote ${SETTINGS_LOCAL_FILE2} (
|
|
17570
|
+
printInfo(` wrote ${SETTINGS_LOCAL_FILE2} (no token stored \u2014 resolved at runtime via otelHeadersHelper)`);
|
|
17438
17571
|
printInfo(" takes effect on your NEXT Claude Code session; first metrics appear within ~60s of activity");
|
|
17439
17572
|
printInfo(" view cost & usage at /usage");
|
|
17440
17573
|
});
|
|
17574
|
+
telemetry.command("headers").description("Print the OTLP Authorization header as JSON (used by Claude Code otelHeadersHelper)").action(async () => {
|
|
17575
|
+
const globals = program2.opts();
|
|
17576
|
+
const tokenResult = await resolveToken(globals.token);
|
|
17577
|
+
if (tokenResult.ok) {
|
|
17578
|
+
printJsonCompact({ Authorization: `Bearer ${tokenResult.data.token}` });
|
|
17579
|
+
} else {
|
|
17580
|
+
printJsonCompact({});
|
|
17581
|
+
}
|
|
17582
|
+
});
|
|
17441
17583
|
telemetry.command("check").description("Show whether Claude Code telemetry export to Verity is enabled").option("--json", "Output status as JSON").action(async (opts) => {
|
|
17442
17584
|
const status = await checkTelemetry();
|
|
17443
17585
|
if (opts.json) {
|
|
@@ -17463,7 +17605,12 @@ function registerTelemetryCommands(program2) {
|
|
|
17463
17605
|
}
|
|
17464
17606
|
|
|
17465
17607
|
// src/cli.ts
|
|
17466
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.28.
|
|
17608
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.28.1-experimental.9155758").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async () => {
|
|
17609
|
+
try {
|
|
17610
|
+
await foldLegacyLocalCredential();
|
|
17611
|
+
} catch {
|
|
17612
|
+
}
|
|
17613
|
+
});
|
|
17467
17614
|
registerAuthCommands(program);
|
|
17468
17615
|
registerLoginCommand(program);
|
|
17469
17616
|
registerHooksCommands(program);
|
|
@@ -17485,4 +17632,8 @@ registerMemoryCommand(program);
|
|
|
17485
17632
|
registerRunCommand(program);
|
|
17486
17633
|
registerMigrateCommand(program);
|
|
17487
17634
|
registerTelemetryCommands(program);
|
|
17488
|
-
program.
|
|
17635
|
+
program.parseAsync().catch((err) => {
|
|
17636
|
+
process.stderr.write(`[verity] ${err instanceof Error ? err.message : String(err)}
|
|
17637
|
+
`);
|
|
17638
|
+
process.exit(1);
|
|
17639
|
+
});
|
|
@@ -353,8 +353,9 @@ verity auth verify
|
|
|
353
353
|
(Metadata + Email addresses); the resulting token is used once server-side to
|
|
354
354
|
check repo access and is never persisted.
|
|
355
355
|
|
|
356
|
-
When authenticated,
|
|
357
|
-
|
|
356
|
+
When authenticated, the project's `token` and `service_url` are stored in the
|
|
357
|
+
single global credentials file `~/.verity/credentials` (keyed by git remote) —
|
|
358
|
+
nothing secret is written into the repo. All subsequent `verity` upload commands work.
|
|
358
359
|
|
|
359
360
|
---
|
|
360
361
|
|
|
@@ -407,10 +408,12 @@ now (the token from Step 6 must already exist):
|
|
|
407
408
|
verity telemetry install
|
|
408
409
|
```
|
|
409
410
|
|
|
410
|
-
This writes the `OTEL_*` env block
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
411
|
+
This writes the `OTEL_*` env block plus an `otelHeadersHelper` to `.claude/settings.local.json`
|
|
412
|
+
and points Claude Code's OpenTelemetry exporter at Verity's OTLP endpoint. No token is written to
|
|
413
|
+
the file — the helper (`verity telemetry headers`) resolves the project token from
|
|
414
|
+
`~/.verity/credentials` at session start, so the secret never lands in the repo. Tell the user it
|
|
415
|
+
takes effect on their **next** Claude Code session, that first metrics appear within ~60s of
|
|
416
|
+
activity, and point them at `/usage`.
|
|
414
417
|
|
|
415
418
|
If the user declined, skip this step and note that `/usage` will stay empty until they run
|
|
416
419
|
`verity telemetry install`.
|
|
@@ -534,7 +537,6 @@ Add these entries to `.gitignore` (create it if it doesn't exist, append if it d
|
|
|
534
537
|
|
|
535
538
|
```
|
|
536
539
|
# Verity
|
|
537
|
-
.verity/credentials
|
|
538
540
|
.verity/.cache/
|
|
539
541
|
.verity/.logs/
|
|
540
542
|
.verity/.last-analysis
|
|
@@ -546,14 +548,18 @@ Add these entries to `.gitignore` (create it if it doesn't exist, append if it d
|
|
|
546
548
|
.claude/settings.local.json
|
|
547
549
|
```
|
|
548
550
|
|
|
549
|
-
`.claude/settings.local.json`
|
|
550
|
-
|
|
551
|
+
`.claude/settings.local.json` is machine-local Claude Code config (telemetry endpoint + an
|
|
552
|
+
`otelHeadersHelper` reference — no token). `verity telemetry install` adds this entry
|
|
553
|
+
automatically.
|
|
551
554
|
|
|
552
555
|
Do NOT gitignore `.verity/standard.yaml` or `VERITY.md` — those should be committed.
|
|
553
556
|
|
|
554
557
|
**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`.
|
|
555
558
|
|
|
556
|
-
|
|
559
|
+
**No secrets live in the repo anymore.** The project token is stored only in the single global
|
|
560
|
+
file `~/.verity/credentials` (outside the repo, keyed by git remote), so there is no
|
|
561
|
+
`.verity/credentials` to ignore. If an older install left one behind, the CLI folds it into the
|
|
562
|
+
global store and removes it automatically on the next `verity` command.
|
|
557
563
|
|
|
558
564
|
---
|
|
559
565
|
|
package/package.json
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codacy/verity-cli",
|
|
3
|
-
"version": "0.28.
|
|
3
|
+
"version": "0.28.1-experimental.9155758",
|
|
4
4
|
"description": "CLI for Verity quality gate service",
|
|
5
5
|
"homepage": "https://verity.md",
|
|
6
|
-
"repository": {
|
|
7
|
-
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/codacy/verity.git"
|
|
9
|
-
},
|
|
10
6
|
"bugs": {
|
|
11
7
|
"url": "https://github.com/codacy/verity/issues"
|
|
12
8
|
},
|