@codacy/verity-cli 0.27.2 → 0.28.1-experimental.708d3ae
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 +693 -352
- 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_promises = 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";
|
|
@@ -10551,6 +10546,13 @@ function rotateIfNeeded(file) {
|
|
|
10551
10546
|
}
|
|
10552
10547
|
|
|
10553
10548
|
// src/lib/api-client.ts
|
|
10549
|
+
function describeFetchError(err, url) {
|
|
10550
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10551
|
+
const cause = err?.cause;
|
|
10552
|
+
const causeBits = [cause?.code, cause?.hostname].filter(Boolean).join(" ");
|
|
10553
|
+
const detail = causeBits || (cause?.message && cause.message !== message ? cause.message : "");
|
|
10554
|
+
return `${message}${detail ? ` (${detail})` : ""} \u2014 could not reach ${url}`;
|
|
10555
|
+
}
|
|
10554
10556
|
async function apiRequest(options) {
|
|
10555
10557
|
const {
|
|
10556
10558
|
method,
|
|
@@ -10606,7 +10608,7 @@ async function apiRequest(options) {
|
|
|
10606
10608
|
const duration2 = Date.now() - startedAt;
|
|
10607
10609
|
const isTimeout = err instanceof DOMException && err.name === "TimeoutError";
|
|
10608
10610
|
const category = isTimeout ? "timeout" : "network";
|
|
10609
|
-
const error = isTimeout ? `Request timed out after ${timeout}ms` : `Network error: ${err
|
|
10611
|
+
const error = isTimeout ? `Request timed out after ${timeout}ms (${url})` : `Network error: ${describeFetchError(err, url)}`;
|
|
10610
10612
|
logHttpCall({ ...logBase, duration_ms: duration2, http_status: null, category, error });
|
|
10611
10613
|
return { ok: false, error, category, http_status: null };
|
|
10612
10614
|
}
|
|
@@ -10665,111 +10667,287 @@ function analyzeRequest(options) {
|
|
|
10665
10667
|
});
|
|
10666
10668
|
}
|
|
10667
10669
|
|
|
10668
|
-
// src/lib/
|
|
10669
|
-
|
|
10670
|
-
|
|
10671
|
-
|
|
10670
|
+
// src/lib/service-url.ts
|
|
10671
|
+
var import_promises2 = require("node:fs/promises");
|
|
10672
|
+
|
|
10673
|
+
// src/lib/credentials.ts
|
|
10674
|
+
var import_promises = require("node:fs/promises");
|
|
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];
|
|
10672
10700
|
return {
|
|
10673
|
-
|
|
10674
|
-
|
|
10701
|
+
remote,
|
|
10702
|
+
rec: {
|
|
10703
|
+
token: tokenMatch[1],
|
|
10704
|
+
serviceUrl,
|
|
10705
|
+
userId: userIdRaw != null ? Number(userIdRaw) : void 0,
|
|
10706
|
+
email
|
|
10707
|
+
}
|
|
10675
10708
|
};
|
|
10676
10709
|
}
|
|
10677
|
-
|
|
10678
|
-
|
|
10679
|
-
|
|
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;
|
|
10726
|
+
try {
|
|
10727
|
+
content = await (0, import_promises.readFile)(globalCredentialsPath(), "utf-8");
|
|
10728
|
+
} catch {
|
|
10729
|
+
return null;
|
|
10680
10730
|
}
|
|
10681
|
-
const
|
|
10682
|
-
|
|
10683
|
-
|
|
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;
|
|
10684
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 = "";
|
|
10685
10752
|
try {
|
|
10686
|
-
|
|
10687
|
-
const match = content.match(/token:\s*((?:gate_|verity_)[a-f0-9]+)/);
|
|
10688
|
-
if (match) {
|
|
10689
|
-
return { ok: true, data: { token: match[1], source: "local", ...parseIdentity(content) } };
|
|
10690
|
-
}
|
|
10753
|
+
content = await (0, import_promises.readFile)(path, "utf-8");
|
|
10691
10754
|
} catch {
|
|
10692
10755
|
}
|
|
10693
|
-
const
|
|
10694
|
-
|
|
10695
|
-
|
|
10696
|
-
|
|
10697
|
-
|
|
10698
|
-
|
|
10699
|
-
|
|
10700
|
-
|
|
10701
|
-
|
|
10702
|
-
|
|
10703
|
-
|
|
10704
|
-
const match = remoteLine.match(/token:\s*((?:gate_|verity_)[a-f0-9]+)/);
|
|
10705
|
-
if (match) {
|
|
10706
|
-
return { ok: true, data: { token: match[1], source: "global" } };
|
|
10707
|
-
}
|
|
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;
|
|
10708
10767
|
}
|
|
10768
|
+
continue;
|
|
10709
10769
|
}
|
|
10710
|
-
|
|
10711
|
-
if (plainMatch) {
|
|
10712
|
-
return { ok: true, data: { token: plainMatch[1], source: "global" } };
|
|
10713
|
-
}
|
|
10714
|
-
} catch {
|
|
10770
|
+
kept.push(existing);
|
|
10715
10771
|
}
|
|
10716
|
-
|
|
10717
|
-
|
|
10718
|
-
|
|
10719
|
-
|
|
10720
|
-
|
|
10721
|
-
path: "/auth/whoami",
|
|
10722
|
-
serviceUrl,
|
|
10723
|
-
token,
|
|
10724
|
-
verbose
|
|
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(() => {
|
|
10725
10777
|
});
|
|
10726
10778
|
}
|
|
10727
|
-
|
|
10728
|
-
|
|
10729
|
-
|
|
10730
|
-
|
|
10731
|
-
|
|
10732
|
-
|
|
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;
|
|
10733
10795
|
}
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
|
|
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;
|
|
10737
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
|
+
};
|
|
10738
10821
|
try {
|
|
10739
|
-
|
|
10740
|
-
const match = creds.match(/service_url:\s*(https?:\/\/[^\s]+)/);
|
|
10741
|
-
if (match) {
|
|
10742
|
-
return { ok: true, data: match[1] };
|
|
10743
|
-
}
|
|
10822
|
+
await upsertGlobalCredential(remote, merged);
|
|
10744
10823
|
} catch {
|
|
10824
|
+
return false;
|
|
10745
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;
|
|
10835
|
+
}
|
|
10836
|
+
async function serviceUrlFromVerityMd() {
|
|
10746
10837
|
try {
|
|
10747
10838
|
const content = await (0, import_promises2.readFile)(projectPath(VERITY_MD_FILE), "utf-8");
|
|
10748
|
-
const
|
|
10749
|
-
if (
|
|
10750
|
-
const
|
|
10751
|
-
if (
|
|
10752
|
-
const urlMatch = lineMatch.match(/https:\/\/[^\s]+/);
|
|
10753
|
-
if (urlMatch) {
|
|
10754
|
-
return { ok: true, data: urlMatch[0] };
|
|
10755
|
-
}
|
|
10756
|
-
}
|
|
10839
|
+
const boldLine = content.split("\n").find((l) => /\*\*url\*\*/i.test(l));
|
|
10840
|
+
if (boldLine) {
|
|
10841
|
+
const urlMatch = boldLine.match(/https:\/\/[^\s]+/);
|
|
10842
|
+
if (urlMatch) return urlMatch[0];
|
|
10757
10843
|
}
|
|
10758
10844
|
const plainLine = content.split("\n").find((l) => /(?:url|service)\s*:/i.test(l));
|
|
10759
10845
|
if (plainLine) {
|
|
10760
10846
|
const urlMatch = plainLine.match(/https:\/\/[^\s]+/);
|
|
10761
|
-
if (urlMatch)
|
|
10762
|
-
return { ok: true, data: urlMatch[0] };
|
|
10763
|
-
}
|
|
10847
|
+
if (urlMatch) return urlMatch[0];
|
|
10764
10848
|
}
|
|
10765
10849
|
} catch {
|
|
10766
10850
|
}
|
|
10851
|
+
return null;
|
|
10852
|
+
}
|
|
10853
|
+
async function resolveServiceUrlDetailed(flagUrl) {
|
|
10854
|
+
if (flagUrl) {
|
|
10855
|
+
return { ok: true, data: { url: flagUrl, source: "flag" } };
|
|
10856
|
+
}
|
|
10857
|
+
const envUrl = process.env.VERITY_SERVICE_URL;
|
|
10858
|
+
if (envUrl) {
|
|
10859
|
+
return { ok: true, data: { url: envUrl, source: "env" } };
|
|
10860
|
+
}
|
|
10861
|
+
const credsUrl = await serviceUrlFromCredentials();
|
|
10862
|
+
if (credsUrl) {
|
|
10863
|
+
return { ok: true, data: { url: credsUrl, source: "credentials" } };
|
|
10864
|
+
}
|
|
10865
|
+
const mdUrl = await serviceUrlFromVerityMd();
|
|
10866
|
+
if (mdUrl) {
|
|
10867
|
+
return { ok: true, data: { url: mdUrl, source: "verity_md" } };
|
|
10868
|
+
}
|
|
10767
10869
|
return { ok: false, error: "No Verity service URL found. Run /verity-setup to configure." };
|
|
10768
10870
|
}
|
|
10871
|
+
async function resolveServiceUrl(flagUrl) {
|
|
10872
|
+
const result = await resolveServiceUrlDetailed(flagUrl);
|
|
10873
|
+
return result.ok ? { ok: true, data: result.data.url } : result;
|
|
10874
|
+
}
|
|
10875
|
+
function isHealCandidate(resolved) {
|
|
10876
|
+
return (resolved.source === "credentials" || resolved.source === "verity_md") && resolved.url !== DEFAULT_SERVICE_URL;
|
|
10877
|
+
}
|
|
10878
|
+
|
|
10879
|
+
// src/lib/auth.ts
|
|
10880
|
+
async function resolveToken(flagToken) {
|
|
10881
|
+
if (flagToken) {
|
|
10882
|
+
return { ok: true, data: { token: flagToken, source: "flag" } };
|
|
10883
|
+
}
|
|
10884
|
+
const envToken = process.env.VERITY_TOKEN;
|
|
10885
|
+
if (envToken) {
|
|
10886
|
+
return { ok: true, data: { token: envToken, source: "env" } };
|
|
10887
|
+
}
|
|
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
|
+
};
|
|
10894
|
+
}
|
|
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
|
+
};
|
|
10901
|
+
}
|
|
10902
|
+
return { ok: false, error: "No Verity token found. Run /verity-setup to configure." };
|
|
10903
|
+
}
|
|
10904
|
+
async function whoami(token, serviceUrl, verbose) {
|
|
10905
|
+
return apiRequest({
|
|
10906
|
+
method: "GET",
|
|
10907
|
+
path: "/auth/whoami",
|
|
10908
|
+
serviceUrl,
|
|
10909
|
+
token,
|
|
10910
|
+
verbose,
|
|
10911
|
+
cmd: "whoami"
|
|
10912
|
+
});
|
|
10913
|
+
}
|
|
10914
|
+
async function probeService(serviceUrl, verbose) {
|
|
10915
|
+
const res = await apiRequest({
|
|
10916
|
+
method: "GET",
|
|
10917
|
+
path: "/auth/whoami",
|
|
10918
|
+
serviceUrl,
|
|
10919
|
+
verbose,
|
|
10920
|
+
timeout: 5e3,
|
|
10921
|
+
cmd: "probe"
|
|
10922
|
+
});
|
|
10923
|
+
if (res.ok || res.http_status != null) return { reachable: true };
|
|
10924
|
+
return { reachable: false, dnsDead: /\bENOTFOUND\b/.test(res.error), error: res.error };
|
|
10925
|
+
}
|
|
10926
|
+
async function maybeHealServiceUrl(resolution, verbose) {
|
|
10927
|
+
if (!isHealCandidate(resolution)) {
|
|
10928
|
+
return { serviceUrl: resolution.url, healed: false };
|
|
10929
|
+
}
|
|
10930
|
+
const probe = await probeService(resolution.url, verbose);
|
|
10931
|
+
if (probe.reachable) {
|
|
10932
|
+
return { serviceUrl: resolution.url, healed: false };
|
|
10933
|
+
}
|
|
10934
|
+
const from = resolution.source === "credentials" ? "~/.verity/credentials" : "VERITY.md";
|
|
10935
|
+
printWarn(`Your configured Verity service URL is unreachable: ${resolution.url}`);
|
|
10936
|
+
printInfo(` (${probe.error})`);
|
|
10937
|
+
if (probe.dnsDead && (await probeService(DEFAULT_SERVICE_URL, verbose)).reachable) {
|
|
10938
|
+
printInfo(` The hostname no longer exists \u2014 the URL in ${from} is stale (e.g. a retired preview backend).`);
|
|
10939
|
+
printInfo(` Falling back to the default Verity service: ${DEFAULT_SERVICE_URL}`);
|
|
10940
|
+
if (resolution.source === "verity_md") {
|
|
10941
|
+
printWarn(` Note: VERITY.md still contains the stale URL \u2014 update it to ${DEFAULT_SERVICE_URL} and commit.`);
|
|
10942
|
+
}
|
|
10943
|
+
return { serviceUrl: DEFAULT_SERVICE_URL, healed: true };
|
|
10944
|
+
}
|
|
10945
|
+
printInfo(" Continuing against the configured URL. If it is stale, log in against the default with:");
|
|
10946
|
+
printInfo(` VERITY_SERVICE_URL=${DEFAULT_SERVICE_URL} verity login`);
|
|
10947
|
+
return { serviceUrl: resolution.url, healed: false };
|
|
10948
|
+
}
|
|
10769
10949
|
|
|
10770
10950
|
// src/lib/register.ts
|
|
10771
|
-
var import_promises3 = require("node:fs/promises");
|
|
10772
|
-
var import_node_path3 = require("node:path");
|
|
10773
10951
|
var readline = __toESM(require("node:readline/promises"));
|
|
10774
10952
|
|
|
10775
10953
|
// src/lib/provider-auth.ts
|
|
@@ -10879,7 +11057,7 @@ async function githubDeviceFlow() {
|
|
|
10879
11057
|
// src/lib/git.ts
|
|
10880
11058
|
var import_node_child_process3 = require("node:child_process");
|
|
10881
11059
|
var import_node_fs2 = require("node:fs");
|
|
10882
|
-
var
|
|
11060
|
+
var import_node_path3 = require("node:path");
|
|
10883
11061
|
function resolveFile(relpath) {
|
|
10884
11062
|
if ((0, import_node_fs2.existsSync)(relpath)) return relpath;
|
|
10885
11063
|
if ((0, import_node_fs2.existsSync)(".claude/worktrees")) {
|
|
@@ -10887,7 +11065,7 @@ function resolveFile(relpath) {
|
|
|
10887
11065
|
const entries = (0, import_node_fs2.readdirSync)(".claude/worktrees", { withFileTypes: true });
|
|
10888
11066
|
for (const entry of entries) {
|
|
10889
11067
|
if (!entry.isDirectory()) continue;
|
|
10890
|
-
const candidate = (0,
|
|
11068
|
+
const candidate = (0, import_node_path3.join)(".claude/worktrees", entry.name, relpath);
|
|
10891
11069
|
if ((0, import_node_fs2.existsSync)(candidate)) return candidate;
|
|
10892
11070
|
}
|
|
10893
11071
|
} catch {
|
|
@@ -10928,7 +11106,7 @@ function readBaselineSha() {
|
|
|
10928
11106
|
function writeBaselineSha(sha) {
|
|
10929
11107
|
if (!SHA_RE.test(sha)) return;
|
|
10930
11108
|
try {
|
|
10931
|
-
(0, import_node_fs2.mkdirSync)((0,
|
|
11109
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path3.dirname)(BASELINE_SHA_FILE), { recursive: true });
|
|
10932
11110
|
(0, import_node_fs2.writeFileSync)(BASELINE_SHA_FILE, sha);
|
|
10933
11111
|
} catch {
|
|
10934
11112
|
}
|
|
@@ -11022,7 +11200,7 @@ function getWorktreeFiles() {
|
|
|
11022
11200
|
const entries = (0, import_node_fs2.readdirSync)(worktreeDir, { withFileTypes: true });
|
|
11023
11201
|
for (const entry of entries) {
|
|
11024
11202
|
if (!entry.isDirectory()) continue;
|
|
11025
|
-
const wtDir = (0,
|
|
11203
|
+
const wtDir = (0, import_node_path3.join)(worktreeDir, entry.name);
|
|
11026
11204
|
scanDir(wtDir, wtDir, fiveMinAgo, result);
|
|
11027
11205
|
}
|
|
11028
11206
|
} catch {
|
|
@@ -11033,12 +11211,12 @@ function scanDir(baseDir, dir, minMtime, result) {
|
|
|
11033
11211
|
try {
|
|
11034
11212
|
const entries = (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true });
|
|
11035
11213
|
for (const entry of entries) {
|
|
11036
|
-
const fullPath = (0,
|
|
11214
|
+
const fullPath = (0, import_node_path3.join)(dir, entry.name);
|
|
11037
11215
|
if (entry.isDirectory()) {
|
|
11038
11216
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
11039
11217
|
scanDir(baseDir, fullPath, minMtime, result);
|
|
11040
11218
|
} else if (entry.isFile()) {
|
|
11041
|
-
const ext = (0,
|
|
11219
|
+
const ext = (0, import_node_path3.extname)(entry.name).slice(1);
|
|
11042
11220
|
if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
|
|
11043
11221
|
try {
|
|
11044
11222
|
const stat3 = (0, import_node_fs2.statSync)(fullPath);
|
|
@@ -11055,13 +11233,13 @@ function scanDir(baseDir, dir, minMtime, result) {
|
|
|
11055
11233
|
}
|
|
11056
11234
|
function filterAnalyzable(files) {
|
|
11057
11235
|
return files.filter((f) => {
|
|
11058
|
-
const ext = (0,
|
|
11236
|
+
const ext = (0, import_node_path3.extname)(f).slice(1);
|
|
11059
11237
|
return ANALYZABLE_EXTENSIONS.has(ext);
|
|
11060
11238
|
});
|
|
11061
11239
|
}
|
|
11062
11240
|
function filterReviewable(files) {
|
|
11063
11241
|
return files.filter((f) => {
|
|
11064
|
-
const ext = (0,
|
|
11242
|
+
const ext = (0, import_node_path3.extname)(f).slice(1);
|
|
11065
11243
|
if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
|
|
11066
11244
|
if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
|
|
11067
11245
|
const basename4 = f.split("/").pop() ?? "";
|
|
@@ -11185,7 +11363,8 @@ async function registerProject(opts) {
|
|
|
11185
11363
|
serviceUrl: opts.serviceUrl,
|
|
11186
11364
|
body: { project_name: opts.projectName, git_remote_url: opts.remote },
|
|
11187
11365
|
extraHeaders: { "X-Provider-Token": providerToken },
|
|
11188
|
-
verbose: opts.verbose
|
|
11366
|
+
verbose: opts.verbose,
|
|
11367
|
+
cmd: "register"
|
|
11189
11368
|
});
|
|
11190
11369
|
if (!result.ok) {
|
|
11191
11370
|
return { ok: false, error: result.error };
|
|
@@ -11193,38 +11372,19 @@ async function registerProject(opts) {
|
|
|
11193
11372
|
const { project_id, token, service_url, user } = result.data;
|
|
11194
11373
|
const userId = result.data.user_id ?? user?.id;
|
|
11195
11374
|
const email = user?.email;
|
|
11196
|
-
const identityLines = (userId != null ? `user_id: ${userId}
|
|
11197
|
-
` : "") + (email ? `email: ${email}
|
|
11198
|
-
` : "");
|
|
11199
11375
|
try {
|
|
11200
|
-
await (
|
|
11201
|
-
|
|
11202
|
-
|
|
11203
|
-
|
|
11204
|
-
|
|
11205
|
-
${identityLines}`,
|
|
11206
|
-
{ mode: 384 }
|
|
11207
|
-
);
|
|
11208
|
-
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
|
|
11209
11381
|
});
|
|
11210
11382
|
} catch (err) {
|
|
11211
11383
|
return {
|
|
11212
11384
|
ok: false,
|
|
11213
|
-
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".`
|
|
11214
11386
|
};
|
|
11215
11387
|
}
|
|
11216
|
-
try {
|
|
11217
|
-
await (0, import_promises3.mkdir)((0, import_node_path3.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
|
|
11218
|
-
await (0, import_promises3.appendFile)(
|
|
11219
|
-
GLOBAL_CREDENTIALS_FILE,
|
|
11220
|
-
`${opts.remote} token: ${token}
|
|
11221
|
-
`,
|
|
11222
|
-
{ mode: 384 }
|
|
11223
|
-
);
|
|
11224
|
-
await (0, import_promises3.chmod)(GLOBAL_CREDENTIALS_FILE, 384).catch(() => {
|
|
11225
|
-
});
|
|
11226
|
-
} catch {
|
|
11227
|
-
}
|
|
11228
11388
|
return { ok: true, data: { projectId: project_id, serviceUrl: service_url, email, userId } };
|
|
11229
11389
|
}
|
|
11230
11390
|
|
|
@@ -11321,14 +11481,18 @@ var import_node_path4 = require("node:path");
|
|
|
11321
11481
|
function registerLoginCommand(program2) {
|
|
11322
11482
|
program2.command("login").description("Log in to Verity (link your GitHub identity so runs and memory are saved)").option("--force", "Re-authenticate even if already logged in").action(async (opts) => {
|
|
11323
11483
|
const globals = program2.opts();
|
|
11324
|
-
const urlResult = await
|
|
11484
|
+
const urlResult = await resolveServiceUrlDetailed(globals.serviceUrl);
|
|
11325
11485
|
if (!urlResult.ok) {
|
|
11326
11486
|
printError(urlResult.error);
|
|
11327
11487
|
process.exit(1);
|
|
11328
11488
|
}
|
|
11329
|
-
const
|
|
11489
|
+
const heal = await maybeHealServiceUrl(urlResult.data, globals.verbose);
|
|
11490
|
+
const serviceUrl = heal.serviceUrl;
|
|
11491
|
+
if (heal.healed) {
|
|
11492
|
+
printInfo(" Completing login re-registers this project and updates ~/.verity/credentials.");
|
|
11493
|
+
}
|
|
11330
11494
|
const existing = await resolveToken(globals.token);
|
|
11331
|
-
if (existing.ok && !opts.force) {
|
|
11495
|
+
if (existing.ok && !opts.force && !heal.healed) {
|
|
11332
11496
|
if (existing.data.userId != null) {
|
|
11333
11497
|
printInfo(`Already logged in as ${existing.data.email ?? `user #${existing.data.userId}`}. \u2713`);
|
|
11334
11498
|
printInfo(" Re-authenticate with: verity login --force");
|
|
@@ -11373,11 +11537,11 @@ function registerLoginCommand(program2) {
|
|
|
11373
11537
|
}
|
|
11374
11538
|
|
|
11375
11539
|
// src/lib/hooks.ts
|
|
11376
|
-
var
|
|
11540
|
+
var import_promises4 = require("node:fs/promises");
|
|
11377
11541
|
var import_node_path6 = require("node:path");
|
|
11378
11542
|
|
|
11379
11543
|
// src/lib/json-file.ts
|
|
11380
|
-
var
|
|
11544
|
+
var import_promises3 = require("node:fs/promises");
|
|
11381
11545
|
var import_node_path5 = require("node:path");
|
|
11382
11546
|
function jsonSemanticEqual(a, b) {
|
|
11383
11547
|
if (a === b) return true;
|
|
@@ -11411,7 +11575,7 @@ function detectJsonIndent(raw) {
|
|
|
11411
11575
|
async function writeJsonFilePreservingStyle(file, value) {
|
|
11412
11576
|
let currentRaw = null;
|
|
11413
11577
|
try {
|
|
11414
|
-
currentRaw = await (0,
|
|
11578
|
+
currentRaw = await (0, import_promises3.readFile)(file, "utf-8");
|
|
11415
11579
|
} catch {
|
|
11416
11580
|
currentRaw = null;
|
|
11417
11581
|
}
|
|
@@ -11424,8 +11588,8 @@ async function writeJsonFilePreservingStyle(file, value) {
|
|
|
11424
11588
|
const indent = currentRaw !== null ? detectJsonIndent(currentRaw) : 2;
|
|
11425
11589
|
const next = JSON.stringify(value, null, indent) + "\n";
|
|
11426
11590
|
if (next === currentRaw) return false;
|
|
11427
|
-
await (0,
|
|
11428
|
-
await (0,
|
|
11591
|
+
await (0, import_promises3.mkdir)((0, import_node_path5.dirname)(file), { recursive: true });
|
|
11592
|
+
await (0, import_promises3.writeFile)(file, next);
|
|
11429
11593
|
return true;
|
|
11430
11594
|
}
|
|
11431
11595
|
|
|
@@ -11507,7 +11671,7 @@ function globalSettingsFile() {
|
|
|
11507
11671
|
}
|
|
11508
11672
|
async function readSettings() {
|
|
11509
11673
|
try {
|
|
11510
|
-
const content = await (0,
|
|
11674
|
+
const content = await (0, import_promises4.readFile)(CLAUDE_SETTINGS_FILE, "utf-8");
|
|
11511
11675
|
return JSON.parse(content);
|
|
11512
11676
|
} catch {
|
|
11513
11677
|
return {};
|
|
@@ -11518,7 +11682,7 @@ async function readAllSettings() {
|
|
|
11518
11682
|
const out = [];
|
|
11519
11683
|
for (const f of files) {
|
|
11520
11684
|
try {
|
|
11521
|
-
out.push(JSON.parse(await (0,
|
|
11685
|
+
out.push(JSON.parse(await (0, import_promises4.readFile)(f, "utf-8")));
|
|
11522
11686
|
} catch {
|
|
11523
11687
|
}
|
|
11524
11688
|
}
|
|
@@ -11547,7 +11711,7 @@ async function checkExternalVerityHooks() {
|
|
|
11547
11711
|
for (const f of [SETTINGS_LOCAL_FILE, globalSettingsFile()]) {
|
|
11548
11712
|
let settings;
|
|
11549
11713
|
try {
|
|
11550
|
-
settings = JSON.parse(await (0,
|
|
11714
|
+
settings = JSON.parse(await (0, import_promises4.readFile)(f, "utf-8"));
|
|
11551
11715
|
} catch {
|
|
11552
11716
|
continue;
|
|
11553
11717
|
}
|
|
@@ -11585,7 +11749,7 @@ async function writeSettings(settings) {
|
|
|
11585
11749
|
}
|
|
11586
11750
|
async function readSettingsAt(root) {
|
|
11587
11751
|
try {
|
|
11588
|
-
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"));
|
|
11589
11753
|
} catch {
|
|
11590
11754
|
return {};
|
|
11591
11755
|
}
|
|
@@ -11827,10 +11991,10 @@ function registerHooksCommands(program2) {
|
|
|
11827
11991
|
}
|
|
11828
11992
|
|
|
11829
11993
|
// src/commands/intent.ts
|
|
11830
|
-
var
|
|
11994
|
+
var import_node_crypto4 = require("node:crypto");
|
|
11831
11995
|
|
|
11832
11996
|
// src/lib/conversation-buffer.ts
|
|
11833
|
-
var
|
|
11997
|
+
var import_promises5 = require("node:fs/promises");
|
|
11834
11998
|
var import_node_fs3 = require("node:fs");
|
|
11835
11999
|
var import_node_child_process6 = require("node:child_process");
|
|
11836
12000
|
var import_node_crypto = require("node:crypto");
|
|
@@ -11842,7 +12006,7 @@ function bufferTmpPath() {
|
|
|
11842
12006
|
}
|
|
11843
12007
|
async function appendToConversationBuffer(prompt, sessionId) {
|
|
11844
12008
|
try {
|
|
11845
|
-
await (0,
|
|
12009
|
+
await (0, import_promises5.mkdir)(VERITY_DIR, { recursive: true });
|
|
11846
12010
|
let sanitized = prompt.length > MAX_INTENT_CHARS ? prompt.slice(0, MAX_INTENT_CHARS) : prompt;
|
|
11847
12011
|
sanitized = stripImageReferences(sanitized);
|
|
11848
12012
|
const entry = {
|
|
@@ -11860,8 +12024,8 @@ async function appendToConversationBuffer(prompt, sessionId) {
|
|
|
11860
12024
|
const capped = recent.slice(-CONVERSATION_MAX_ENTRIES);
|
|
11861
12025
|
const content = capped.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
11862
12026
|
const tmpFile = bufferTmpPath();
|
|
11863
|
-
await (0,
|
|
11864
|
-
await (0,
|
|
12027
|
+
await (0, import_promises5.writeFile)(tmpFile, content);
|
|
12028
|
+
await (0, import_promises5.rename)(tmpFile, CONVERSATION_BUFFER_FILE);
|
|
11865
12029
|
} catch {
|
|
11866
12030
|
}
|
|
11867
12031
|
}
|
|
@@ -11878,10 +12042,10 @@ async function readAndClearConversationBuffer(currentSessionId) {
|
|
|
11878
12042
|
if (others.length > 0) {
|
|
11879
12043
|
const remaining = others.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
11880
12044
|
const tmpFile = bufferTmpPath();
|
|
11881
|
-
await (0,
|
|
11882
|
-
await (0,
|
|
12045
|
+
await (0, import_promises5.writeFile)(tmpFile, remaining);
|
|
12046
|
+
await (0, import_promises5.rename)(tmpFile, CONVERSATION_BUFFER_FILE);
|
|
11883
12047
|
} else {
|
|
11884
|
-
await (0,
|
|
12048
|
+
await (0, import_promises5.unlink)(CONVERSATION_BUFFER_FILE).catch(() => {
|
|
11885
12049
|
});
|
|
11886
12050
|
}
|
|
11887
12051
|
if (mine.length > 0) {
|
|
@@ -11893,8 +12057,8 @@ async function readAndClearConversationBuffer(currentSessionId) {
|
|
|
11893
12057
|
}
|
|
11894
12058
|
if ((0, import_node_fs3.existsSync)(INTENT_FILE)) {
|
|
11895
12059
|
try {
|
|
11896
|
-
const content = await (0,
|
|
11897
|
-
await (0,
|
|
12060
|
+
const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
|
|
12061
|
+
await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
|
|
11898
12062
|
});
|
|
11899
12063
|
const data = JSON.parse(content);
|
|
11900
12064
|
if (data.prompt) {
|
|
@@ -11917,7 +12081,7 @@ async function readAndClearConversationBuffer(currentSessionId) {
|
|
|
11917
12081
|
}
|
|
11918
12082
|
async function readBufferEntries() {
|
|
11919
12083
|
try {
|
|
11920
|
-
const content = await (0,
|
|
12084
|
+
const content = await (0, import_promises5.readFile)(CONVERSATION_BUFFER_FILE, "utf-8");
|
|
11921
12085
|
const entries = [];
|
|
11922
12086
|
for (const line of content.split("\n")) {
|
|
11923
12087
|
const trimmed = line.trim();
|
|
@@ -11946,8 +12110,21 @@ function getRecentCommitMessages() {
|
|
|
11946
12110
|
}
|
|
11947
12111
|
}
|
|
11948
12112
|
|
|
12113
|
+
// src/lib/context-identity.ts
|
|
12114
|
+
var import_node_crypto2 = require("node:crypto");
|
|
12115
|
+
function contextIdentity(token, sessionId) {
|
|
12116
|
+
const t = (token ?? "").trim();
|
|
12117
|
+
const s = (sessionId ?? "").trim();
|
|
12118
|
+
const userKey = t.length > 0 ? (0, import_node_crypto2.createHash)("sha256").update(t).digest("hex").slice(0, 12) : "anon";
|
|
12119
|
+
const sessionKey2 = s.length > 0 ? (0, import_node_crypto2.createHash)("sha256").update(s).digest("hex").slice(0, 16) : "_default";
|
|
12120
|
+
return { userKey, sessionKey: sessionKey2, bucket: `${userKey}/${sessionKey2}` };
|
|
12121
|
+
}
|
|
12122
|
+
function sessionScopeKey(token, sessionId) {
|
|
12123
|
+
return contextIdentity(token, sessionId).bucket;
|
|
12124
|
+
}
|
|
12125
|
+
|
|
11949
12126
|
// src/lib/task-context-buffer.ts
|
|
11950
|
-
var
|
|
12127
|
+
var import_promises6 = require("node:fs/promises");
|
|
11951
12128
|
var import_node_fs4 = require("node:fs");
|
|
11952
12129
|
var import_node_path7 = require("node:path");
|
|
11953
12130
|
var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
|
|
@@ -11990,7 +12167,7 @@ async function readTaskContextBuffer(taskId) {
|
|
|
11990
12167
|
const filePath = bufferPath(taskId);
|
|
11991
12168
|
if (!(0, import_node_fs4.existsSync)(filePath)) return null;
|
|
11992
12169
|
try {
|
|
11993
|
-
const content = await (0,
|
|
12170
|
+
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
11994
12171
|
if (!content.trim()) return null;
|
|
11995
12172
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
11996
12173
|
const formatted = [];
|
|
@@ -12023,15 +12200,15 @@ async function readTaskContextBuffer(taskId) {
|
|
|
12023
12200
|
async function cleanupTaskContextBuffers() {
|
|
12024
12201
|
try {
|
|
12025
12202
|
if (!(0, import_node_fs4.existsSync)(TASK_CONTEXT_DIR)) return;
|
|
12026
|
-
const files = await (0,
|
|
12203
|
+
const files = await (0, import_promises6.readdir)(TASK_CONTEXT_DIR);
|
|
12027
12204
|
const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
|
|
12028
12205
|
for (const file of files) {
|
|
12029
12206
|
if (!file.endsWith(".jsonl")) continue;
|
|
12030
12207
|
const filePath = (0, import_node_path7.join)(TASK_CONTEXT_DIR, file);
|
|
12031
12208
|
try {
|
|
12032
|
-
const stats = await (0,
|
|
12209
|
+
const stats = await (0, import_promises6.stat)(filePath);
|
|
12033
12210
|
if (stats.mtimeMs < cutoffMs) {
|
|
12034
|
-
await (0,
|
|
12211
|
+
await (0, import_promises6.unlink)(filePath);
|
|
12035
12212
|
}
|
|
12036
12213
|
} catch {
|
|
12037
12214
|
}
|
|
@@ -12045,27 +12222,27 @@ function bufferPath(taskId) {
|
|
|
12045
12222
|
}
|
|
12046
12223
|
async function appendEntry(taskId, entry) {
|
|
12047
12224
|
try {
|
|
12048
|
-
await (0,
|
|
12225
|
+
await (0, import_promises6.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
|
|
12049
12226
|
const filePath = bufferPath(taskId);
|
|
12050
12227
|
if ((0, import_node_fs4.existsSync)(filePath)) {
|
|
12051
|
-
const stats = await (0,
|
|
12228
|
+
const stats = await (0, import_promises6.stat)(filePath);
|
|
12052
12229
|
if (stats.size >= MAX_BUFFER_BYTES) {
|
|
12053
|
-
const content = await (0,
|
|
12230
|
+
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
12054
12231
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
12055
12232
|
const keepFrom = Math.floor(lines.length * 0.25);
|
|
12056
12233
|
const pruned = lines.slice(keepFrom).join("\n") + "\n";
|
|
12057
|
-
await (0,
|
|
12234
|
+
await (0, import_promises6.writeFile)(filePath, pruned);
|
|
12058
12235
|
}
|
|
12059
12236
|
}
|
|
12060
12237
|
const line = JSON.stringify(entry) + "\n";
|
|
12061
|
-
const existing = (0, import_node_fs4.existsSync)(filePath) ? await (0,
|
|
12062
|
-
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);
|
|
12063
12240
|
} catch {
|
|
12064
12241
|
}
|
|
12065
12242
|
}
|
|
12066
12243
|
|
|
12067
12244
|
// src/lib/memory-retrieval.ts
|
|
12068
|
-
var
|
|
12245
|
+
var import_promises7 = require("node:fs/promises");
|
|
12069
12246
|
var import_node_fs5 = require("node:fs");
|
|
12070
12247
|
var import_node_path8 = require("node:path");
|
|
12071
12248
|
var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
|
|
@@ -12168,11 +12345,11 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
|
|
|
12168
12345
|
const domainDir = (0, import_node_path8.join)(memoryDir(), domain);
|
|
12169
12346
|
if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
|
|
12170
12347
|
try {
|
|
12171
|
-
const files = await (0,
|
|
12348
|
+
const files = await (0, import_promises7.readdir)(domainDir);
|
|
12172
12349
|
for (const file of files) {
|
|
12173
12350
|
if (!file.endsWith(".md")) continue;
|
|
12174
12351
|
try {
|
|
12175
|
-
const content = await (0,
|
|
12352
|
+
const content = await (0, import_promises7.readFile)((0, import_node_path8.join)(domainDir, file), "utf-8");
|
|
12176
12353
|
const { fm, body } = parseFrontmatter(content);
|
|
12177
12354
|
if (fm.status && fm.status !== "active") continue;
|
|
12178
12355
|
nodes.push({
|
|
@@ -12230,10 +12407,10 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
|
|
|
12230
12407
|
}
|
|
12231
12408
|
|
|
12232
12409
|
// src/lib/memory-sync.ts
|
|
12233
|
-
var
|
|
12410
|
+
var import_promises8 = require("node:fs/promises");
|
|
12234
12411
|
var import_node_fs6 = require("node:fs");
|
|
12235
12412
|
var import_node_path9 = require("node:path");
|
|
12236
|
-
var
|
|
12413
|
+
var import_node_crypto3 = require("node:crypto");
|
|
12237
12414
|
|
|
12238
12415
|
// src/lib/glob-match.ts
|
|
12239
12416
|
function globToRegex(glob) {
|
|
@@ -12301,18 +12478,18 @@ var memoryDir2 = () => projectPath(`${VERITY_DIR}/memory`);
|
|
|
12301
12478
|
var DOMAINS2 = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations", "_archive"];
|
|
12302
12479
|
var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
|
|
12303
12480
|
async function ensureMemoryDir() {
|
|
12304
|
-
await (0,
|
|
12481
|
+
await (0, import_promises8.mkdir)(memoryDir2(), { recursive: true });
|
|
12305
12482
|
for (const domain of DOMAINS2) {
|
|
12306
|
-
await (0,
|
|
12483
|
+
await (0, import_promises8.mkdir)((0, import_node_path9.join)(memoryDir2(), domain), { recursive: true });
|
|
12307
12484
|
}
|
|
12308
12485
|
if (!(0, import_node_fs6.existsSync)((0, import_node_path9.join)(memoryDir2(), "SCHEMA.md"))) {
|
|
12309
|
-
await (0,
|
|
12486
|
+
await (0, import_promises8.writeFile)((0, import_node_path9.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
|
|
12310
12487
|
}
|
|
12311
12488
|
if (!(0, import_node_fs6.existsSync)((0, import_node_path9.join)(memoryDir2(), "index.md"))) {
|
|
12312
|
-
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");
|
|
12313
12490
|
}
|
|
12314
12491
|
if (!(0, import_node_fs6.existsSync)((0, import_node_path9.join)(memoryDir2(), "log.md"))) {
|
|
12315
|
-
await (0,
|
|
12492
|
+
await (0, import_promises8.writeFile)((0, import_node_path9.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
|
|
12316
12493
|
}
|
|
12317
12494
|
}
|
|
12318
12495
|
async function buildManifest() {
|
|
@@ -12324,14 +12501,14 @@ async function buildManifest() {
|
|
|
12324
12501
|
const domainDir = (0, import_node_path9.join)(memoryDir2(), domain);
|
|
12325
12502
|
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
12326
12503
|
try {
|
|
12327
|
-
const files = await (0,
|
|
12504
|
+
const files = await (0, import_promises8.readdir)(domainDir);
|
|
12328
12505
|
for (const file of files) {
|
|
12329
12506
|
if (!file.endsWith(".md")) continue;
|
|
12330
12507
|
const filePath = `${domain}/${file}`;
|
|
12331
12508
|
const fullPath = (0, import_node_path9.join)(memoryDir2(), filePath);
|
|
12332
12509
|
try {
|
|
12333
|
-
const content = await (0,
|
|
12334
|
-
const hash = (0,
|
|
12510
|
+
const content = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
12511
|
+
const hash = (0, import_node_crypto3.createHash)("sha256").update(content).digest("hex").slice(0, 16);
|
|
12335
12512
|
nodes.push({ path: filePath, content_hash: `sha256:${hash}` });
|
|
12336
12513
|
} catch {
|
|
12337
12514
|
}
|
|
@@ -12341,20 +12518,20 @@ async function buildManifest() {
|
|
|
12341
12518
|
}
|
|
12342
12519
|
let indexHash = null;
|
|
12343
12520
|
try {
|
|
12344
|
-
const indexContent = await (0,
|
|
12345
|
-
indexHash = `sha256:${(0,
|
|
12521
|
+
const indexContent = await (0, import_promises8.readFile)((0, import_node_path9.join)(memoryDir2(), "index.md"), "utf-8");
|
|
12522
|
+
indexHash = `sha256:${(0, import_node_crypto3.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
|
|
12346
12523
|
} catch {
|
|
12347
12524
|
}
|
|
12348
12525
|
let logLength = 0;
|
|
12349
12526
|
try {
|
|
12350
|
-
const logContent = await (0,
|
|
12527
|
+
const logContent = await (0, import_promises8.readFile)((0, import_node_path9.join)(memoryDir2(), "log.md"), "utf-8");
|
|
12351
12528
|
logLength = logContent.split("\n").length;
|
|
12352
12529
|
} catch {
|
|
12353
12530
|
}
|
|
12354
12531
|
return { schema_version: 1, nodes, index_hash: indexHash, log_length: logLength };
|
|
12355
12532
|
}
|
|
12356
12533
|
function hashContent(content) {
|
|
12357
|
-
return `sha256:${(0,
|
|
12534
|
+
return `sha256:${(0, import_node_crypto3.createHash)("sha256").update(content).digest("hex").slice(0, 16)}`;
|
|
12358
12535
|
}
|
|
12359
12536
|
async function readOnDiskNodes() {
|
|
12360
12537
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -12363,10 +12540,10 @@ async function readOnDiskNodes() {
|
|
|
12363
12540
|
const domainDir = (0, import_node_path9.join)(memoryDir2(), domain);
|
|
12364
12541
|
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
12365
12542
|
try {
|
|
12366
|
-
for (const file of await (0,
|
|
12543
|
+
for (const file of await (0, import_promises8.readdir)(domainDir)) {
|
|
12367
12544
|
if (!file.endsWith(".md")) continue;
|
|
12368
12545
|
try {
|
|
12369
|
-
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")));
|
|
12370
12547
|
} catch {
|
|
12371
12548
|
}
|
|
12372
12549
|
}
|
|
@@ -12378,7 +12555,7 @@ async function readOnDiskNodes() {
|
|
|
12378
12555
|
async function readSyncBaseline() {
|
|
12379
12556
|
const out = /* @__PURE__ */ new Map();
|
|
12380
12557
|
try {
|
|
12381
|
-
const parsed = JSON.parse(await (0,
|
|
12558
|
+
const parsed = JSON.parse(await (0, import_promises8.readFile)(syncStateFile(), "utf-8"));
|
|
12382
12559
|
if (Array.isArray(parsed?.nodes)) {
|
|
12383
12560
|
for (const n of parsed.nodes) if (n?.path) out.set(n.path, n.hash ?? null);
|
|
12384
12561
|
} else if (Array.isArray(parsed?.paths)) {
|
|
@@ -12394,12 +12571,12 @@ async function recordSyncedNodePaths() {
|
|
|
12394
12571
|
const next = JSON.stringify({ schema: 2, nodes }) + "\n";
|
|
12395
12572
|
let existing = "";
|
|
12396
12573
|
try {
|
|
12397
|
-
existing = await (0,
|
|
12574
|
+
existing = await (0, import_promises8.readFile)(syncStateFile(), "utf-8");
|
|
12398
12575
|
} catch {
|
|
12399
12576
|
}
|
|
12400
12577
|
if (existing === next) return;
|
|
12401
|
-
await (0,
|
|
12402
|
-
await (0,
|
|
12578
|
+
await (0, import_promises8.mkdir)(projectPath(VERITY_DIR), { recursive: true });
|
|
12579
|
+
await (0, import_promises8.writeFile)(syncStateFile(), next);
|
|
12403
12580
|
} catch {
|
|
12404
12581
|
}
|
|
12405
12582
|
}
|
|
@@ -12416,7 +12593,7 @@ async function computeEditedNodeUploads() {
|
|
|
12416
12593
|
if (!(0, import_node_fs6.existsSync)(full)) continue;
|
|
12417
12594
|
let content;
|
|
12418
12595
|
try {
|
|
12419
|
-
content = await (0,
|
|
12596
|
+
content = await (0, import_promises8.readFile)(full, "utf-8");
|
|
12420
12597
|
} catch {
|
|
12421
12598
|
continue;
|
|
12422
12599
|
}
|
|
@@ -12449,8 +12626,8 @@ async function applyMemoryWrites(writes, opts = {}) {
|
|
|
12449
12626
|
const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
|
|
12450
12627
|
for (const n of notes) logLines.push(` - ${n}`);
|
|
12451
12628
|
try {
|
|
12452
|
-
const existing = (0, import_node_fs6.existsSync)((0, import_node_path9.join)(memoryDir2(), "log.md")) ? await (0,
|
|
12453
|
-
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");
|
|
12454
12631
|
} catch {
|
|
12455
12632
|
}
|
|
12456
12633
|
await recordSyncedNodePaths();
|
|
@@ -12470,7 +12647,7 @@ async function applyOneWrite(write, treePaths) {
|
|
|
12470
12647
|
if ((0, import_node_fs6.existsSync)(fullPath)) {
|
|
12471
12648
|
let existing = "";
|
|
12472
12649
|
try {
|
|
12473
|
-
existing = await (0,
|
|
12650
|
+
existing = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
12474
12651
|
} catch {
|
|
12475
12652
|
}
|
|
12476
12653
|
if (existing === content) return { written: false, notes };
|
|
@@ -12479,8 +12656,8 @@ async function applyOneWrite(write, treePaths) {
|
|
|
12479
12656
|
return { written: false, notes };
|
|
12480
12657
|
}
|
|
12481
12658
|
}
|
|
12482
|
-
await (0,
|
|
12483
|
-
await (0,
|
|
12659
|
+
await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(fullPath), { recursive: true });
|
|
12660
|
+
await (0, import_promises8.writeFile)(fullPath, content);
|
|
12484
12661
|
return { written: true, notes };
|
|
12485
12662
|
}
|
|
12486
12663
|
function groundFileGlobs(content, treePaths) {
|
|
@@ -12523,7 +12700,7 @@ async function regenerateIndex() {
|
|
|
12523
12700
|
const domainDir = (0, import_node_path9.join)(memoryDir2(), domain);
|
|
12524
12701
|
if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
|
|
12525
12702
|
try {
|
|
12526
|
-
const files = await (0,
|
|
12703
|
+
const files = await (0, import_promises8.readdir)(domainDir);
|
|
12527
12704
|
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
12528
12705
|
if (mdFiles.length === 0) continue;
|
|
12529
12706
|
lines.push(`## ${domain}/ (${mdFiles.length})`);
|
|
@@ -12531,7 +12708,7 @@ async function regenerateIndex() {
|
|
|
12531
12708
|
for (const file of mdFiles.sort()) {
|
|
12532
12709
|
const slug = file.replace(/\.md$/, "");
|
|
12533
12710
|
try {
|
|
12534
|
-
const content = await (0,
|
|
12711
|
+
const content = await (0, import_promises8.readFile)((0, import_node_path9.join)(domainDir, file), "utf-8");
|
|
12535
12712
|
const title = pickFrontmatter(content, "title") ?? slug;
|
|
12536
12713
|
const kind = pickFrontmatter(content, "kind") ?? "-";
|
|
12537
12714
|
const confidence = pickFrontmatter(content, "confidence");
|
|
@@ -12558,11 +12735,11 @@ async function regenerateIndex() {
|
|
|
12558
12735
|
const indexPath = (0, import_node_path9.join)(memoryDir2(), "index.md");
|
|
12559
12736
|
let existing = null;
|
|
12560
12737
|
try {
|
|
12561
|
-
existing = await (0,
|
|
12738
|
+
existing = await (0, import_promises8.readFile)(indexPath, "utf-8");
|
|
12562
12739
|
} catch {
|
|
12563
12740
|
}
|
|
12564
12741
|
if (existing === next) return;
|
|
12565
|
-
await (0,
|
|
12742
|
+
await (0, import_promises8.writeFile)(indexPath, next);
|
|
12566
12743
|
}
|
|
12567
12744
|
function pickFrontmatter(content, key) {
|
|
12568
12745
|
const re = new RegExp(`^${key}:\\s*"?([^"\\n]+?)"?\\s*$`, "m");
|
|
@@ -12640,7 +12817,7 @@ async function ensureClaudeMdPointer(cwd = repoRoot()) {
|
|
|
12640
12817
|
const claudeMdPath = (0, import_node_path9.join)(cwd, "CLAUDE.md");
|
|
12641
12818
|
let existing = "";
|
|
12642
12819
|
if ((0, import_node_fs6.existsSync)(claudeMdPath)) {
|
|
12643
|
-
existing = await (0,
|
|
12820
|
+
existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
|
|
12644
12821
|
}
|
|
12645
12822
|
let startTag = CLAUDE_MD_START;
|
|
12646
12823
|
let endTag = CLAUDE_MD_END;
|
|
@@ -12696,7 +12873,7 @@ async function ensureClaudeMdPointer(cwd = repoRoot()) {
|
|
|
12696
12873
|
next = existing.replace(/\n*$/, "") + "\n\n" + block + "\n";
|
|
12697
12874
|
}
|
|
12698
12875
|
if (next === existing) return;
|
|
12699
|
-
await (0,
|
|
12876
|
+
await (0, import_promises8.writeFile)(claudeMdPath, next);
|
|
12700
12877
|
}
|
|
12701
12878
|
function extractPreserveContent(interior) {
|
|
12702
12879
|
for (const [start, end] of [
|
|
@@ -12799,7 +12976,10 @@ function registerIntentCommands(program2) {
|
|
|
12799
12976
|
if (!prompt) {
|
|
12800
12977
|
process.exit(0);
|
|
12801
12978
|
}
|
|
12802
|
-
await
|
|
12979
|
+
const authForScope = await resolveToken(program2.opts().token);
|
|
12980
|
+
const scopeToken = authForScope.ok ? authForScope.data.token : void 0;
|
|
12981
|
+
const scopeSession = event.session_id || process.env.CLAUDE_SESSION_ID || "";
|
|
12982
|
+
await appendToConversationBuffer(prompt, sessionScopeKey(scopeToken, scopeSession));
|
|
12803
12983
|
try {
|
|
12804
12984
|
await ensureMemoryDir();
|
|
12805
12985
|
const injection = await retrieveForInjection(prompt);
|
|
@@ -12841,7 +13021,7 @@ async function fireClassify(prompt, sessionId) {
|
|
|
12841
13021
|
logEvent("classify_skipped", { reason: "no_service_url", detail: urlResult.error });
|
|
12842
13022
|
return;
|
|
12843
13023
|
}
|
|
12844
|
-
const promptHash = (0,
|
|
13024
|
+
const promptHash = (0, import_node_crypto4.createHash)("sha256").update(prompt).digest("hex");
|
|
12845
13025
|
const result = await apiRequest({
|
|
12846
13026
|
method: "POST",
|
|
12847
13027
|
path: "/classify-task",
|
|
@@ -12879,7 +13059,7 @@ async function fireClassify(prompt, sessionId) {
|
|
|
12879
13059
|
}
|
|
12880
13060
|
|
|
12881
13061
|
// src/commands/standard.ts
|
|
12882
|
-
var
|
|
13062
|
+
var import_promises9 = require("node:fs/promises");
|
|
12883
13063
|
var import_yaml = __toESM(require_dist());
|
|
12884
13064
|
function registerStandardCommands(program2) {
|
|
12885
13065
|
const standard = program2.command("standard").description("Manage the project Standard");
|
|
@@ -12897,7 +13077,7 @@ function registerStandardCommands(program2) {
|
|
|
12897
13077
|
}
|
|
12898
13078
|
let yamlContent;
|
|
12899
13079
|
try {
|
|
12900
|
-
yamlContent = await (0,
|
|
13080
|
+
yamlContent = await (0, import_promises9.readFile)(opts.file, "utf-8");
|
|
12901
13081
|
} catch {
|
|
12902
13082
|
printError(`Cannot read ${opts.file}`);
|
|
12903
13083
|
process.exit(1);
|
|
@@ -12988,7 +13168,7 @@ function registerStandardCommands(program2) {
|
|
|
12988
13168
|
}
|
|
12989
13169
|
|
|
12990
13170
|
// src/commands/config.ts
|
|
12991
|
-
var
|
|
13171
|
+
var import_promises10 = require("node:fs/promises");
|
|
12992
13172
|
function registerConfigCommands(program2) {
|
|
12993
13173
|
const config = program2.command("config").description("Manage analysis configuration");
|
|
12994
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) => {
|
|
@@ -13005,7 +13185,7 @@ function registerConfigCommands(program2) {
|
|
|
13005
13185
|
}
|
|
13006
13186
|
let content;
|
|
13007
13187
|
try {
|
|
13008
|
-
const raw = await (0,
|
|
13188
|
+
const raw = await (0, import_promises10.readFile)(opts.file, "utf-8");
|
|
13009
13189
|
content = JSON.parse(raw);
|
|
13010
13190
|
} catch {
|
|
13011
13191
|
printError(`Cannot read or parse ${opts.file}`);
|
|
@@ -13499,10 +13679,10 @@ function collectCodeDelta(files, opts) {
|
|
|
13499
13679
|
|
|
13500
13680
|
// src/lib/debounce.ts
|
|
13501
13681
|
var import_node_fs9 = require("node:fs");
|
|
13502
|
-
var
|
|
13682
|
+
var import_node_crypto5 = require("node:crypto");
|
|
13503
13683
|
function scopedFile(base, sessionId) {
|
|
13504
13684
|
if (!sessionId) return base;
|
|
13505
|
-
return `${base}.${(0,
|
|
13685
|
+
return `${base}.${(0, import_node_crypto5.createHash)("sha1").update(sessionId).digest("hex").slice(0, 12)}`;
|
|
13506
13686
|
}
|
|
13507
13687
|
function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
|
|
13508
13688
|
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
@@ -13543,7 +13723,7 @@ function checkMtime(files, bypassForRecentCommits, sessionId) {
|
|
|
13543
13723
|
return "No files modified since last analysis";
|
|
13544
13724
|
}
|
|
13545
13725
|
function computeContentHash(files) {
|
|
13546
|
-
const hash = (0,
|
|
13726
|
+
const hash = (0, import_node_crypto5.createHash)("sha1");
|
|
13547
13727
|
const sorted = [...files].sort();
|
|
13548
13728
|
for (const f of sorted) {
|
|
13549
13729
|
const resolved = resolveFile(f) ?? f;
|
|
@@ -13931,14 +14111,14 @@ function cleanStaleSnapshots(dir, keepSet) {
|
|
|
13931
14111
|
// src/lib/baseline.ts
|
|
13932
14112
|
var import_node_fs13 = require("node:fs");
|
|
13933
14113
|
var import_node_path13 = require("node:path");
|
|
13934
|
-
var
|
|
14114
|
+
var import_node_crypto6 = require("node:crypto");
|
|
13935
14115
|
var BASELINE_VERSION = 1;
|
|
13936
14116
|
var BASELINE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
13937
14117
|
var MIRROR_MAX_BYTES = 2 * 1024 * 1024;
|
|
13938
14118
|
var DEFAULT_SESSION_KEY = "_default";
|
|
13939
14119
|
function sessionKey(sessionId) {
|
|
13940
14120
|
if (!sessionId) return DEFAULT_SESSION_KEY;
|
|
13941
|
-
return (0,
|
|
14121
|
+
return (0, import_node_crypto6.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
|
|
13942
14122
|
}
|
|
13943
14123
|
function sessionDir(key) {
|
|
13944
14124
|
return (0, import_node_path13.join)(projectPath(BASELINE_DIR), key);
|
|
@@ -14120,13 +14300,57 @@ function pruneOldBaselines() {
|
|
|
14120
14300
|
}
|
|
14121
14301
|
}
|
|
14122
14302
|
|
|
14303
|
+
// src/lib/task-context.ts
|
|
14304
|
+
var import_node_child_process9 = require("node:child_process");
|
|
14305
|
+
var CLOSING_RE = /\b(close[sd]?|fix(?:e[sd])?|resolve[sd]?)\b[\s:]*#(\d+)/i;
|
|
14306
|
+
var BRANCH_RE = /(?:^|[/_-])(?:issue|gh|fix)[-_/]?(\d+)\b/i;
|
|
14307
|
+
function parseLinkedIssue(sources) {
|
|
14308
|
+
for (const c of sources.commits ?? []) {
|
|
14309
|
+
const m = CLOSING_RE.exec(c);
|
|
14310
|
+
if (m) return { issue: parseInt(m[2], 10), via: `commit:${m[1].toLowerCase()}` };
|
|
14311
|
+
}
|
|
14312
|
+
if (sources.branch) {
|
|
14313
|
+
const m = BRANCH_RE.exec(sources.branch);
|
|
14314
|
+
if (m) return { issue: parseInt(m[1], 10), via: "branch" };
|
|
14315
|
+
}
|
|
14316
|
+
return null;
|
|
14317
|
+
}
|
|
14318
|
+
function safeExec(cmd, timeout) {
|
|
14319
|
+
try {
|
|
14320
|
+
return (0, import_node_child_process9.execSync)(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout }).trim();
|
|
14321
|
+
} catch {
|
|
14322
|
+
return "";
|
|
14323
|
+
}
|
|
14324
|
+
}
|
|
14325
|
+
function defaultGhFetch(issue) {
|
|
14326
|
+
const raw = safeExec(`gh issue view ${issue} --json title,body`, 5e3);
|
|
14327
|
+
if (!raw) return null;
|
|
14328
|
+
try {
|
|
14329
|
+
const j = JSON.parse(raw);
|
|
14330
|
+
return j.title ? { title: j.title, body: j.body ?? "" } : null;
|
|
14331
|
+
} catch {
|
|
14332
|
+
return null;
|
|
14333
|
+
}
|
|
14334
|
+
}
|
|
14335
|
+
function resolveTaskContext(opts) {
|
|
14336
|
+
const branch = opts?.branch ?? safeExec("git rev-parse --abbrev-ref HEAD", 3e3);
|
|
14337
|
+
const commits = opts?.commits ?? safeExec("git log -5 --format=%s%n%b", 3e3).split("\n").map((l) => l.trim()).filter(Boolean);
|
|
14338
|
+
const linked = parseLinkedIssue({ branch, commits });
|
|
14339
|
+
if (!linked) return null;
|
|
14340
|
+
const issue = (opts?.ghFetch ?? defaultGhFetch)(linked.issue);
|
|
14341
|
+
if (!issue) return null;
|
|
14342
|
+
const body = (issue.body ?? "").slice(0, 4e3).trim();
|
|
14343
|
+
const goal = `[#${linked.issue}] ${issue.title}${body ? "\n\n" + body : ""}`;
|
|
14344
|
+
return { number: linked.issue, title: issue.title, goal, via: linked.via };
|
|
14345
|
+
}
|
|
14346
|
+
|
|
14123
14347
|
// src/lib/offline.ts
|
|
14124
14348
|
var import_node_fs14 = require("node:fs");
|
|
14125
|
-
var
|
|
14349
|
+
var import_node_crypto7 = require("node:crypto");
|
|
14126
14350
|
function cacheRequest(body) {
|
|
14127
14351
|
try {
|
|
14128
14352
|
(0, import_node_fs14.mkdirSync)(CACHE_DIR, { recursive: true });
|
|
14129
|
-
const suffix = (0,
|
|
14353
|
+
const suffix = (0, import_node_crypto7.randomBytes)(4).toString("hex");
|
|
14130
14354
|
const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
|
|
14131
14355
|
(0, import_node_fs14.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(body));
|
|
14132
14356
|
} catch {
|
|
@@ -14331,6 +14555,30 @@ function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPromp
|
|
|
14331
14555
|
}
|
|
14332
14556
|
return "standard";
|
|
14333
14557
|
}
|
|
14558
|
+
var FILE_MUTATE_RE = /(?:^|[\s|&;(`])(?:sed\s+-i|perl\s+-i|awk\b|tee\b|dd\b|cp\b|mv\b|ln\b|install\b|touch\b|patch\b|git\s+(?:apply|am)\b|cargo\s+build|go\s+generate|make\b|--write\b|--fix\b|--in-place\b)|>>?(?![&>])/i;
|
|
14559
|
+
var GIT_PLUMBING_RE = /^\s*git\s+(?:merge|rebase|stash|cherry-pick|revert|pull|fetch|checkout|switch|reset|restore|clean)\b/i;
|
|
14560
|
+
var READ_ONLY_RE = /^\s*(?:git\s+(?:status|diff|log|show|branch|remote|config|rev-parse|ls-files|blame|describe)|ls|cat|head|tail|less|grep|rg|find|pwd|echo|printf|wc|which|type|tree|stat|file|env|printenv|date|whoami)\b/i;
|
|
14561
|
+
var CHAIN_RE = /&&|\||;|\$\(|\x60/;
|
|
14562
|
+
function hasNonEditAuthorship(actionSummary, sessionAuthoredCode) {
|
|
14563
|
+
if (!actionSummary) return sessionAuthoredCode;
|
|
14564
|
+
if ((actionSummary.subagents ?? 0) > 0) return true;
|
|
14565
|
+
if (Object.keys(actionSummary.tool_counts ?? {}).some((t) => t.startsWith("mcp__"))) return true;
|
|
14566
|
+
const commands = actionSummary.commands ?? [];
|
|
14567
|
+
if (commands.some((c) => FILE_MUTATE_RE.test(c))) return true;
|
|
14568
|
+
if (sessionAuthoredCode) {
|
|
14569
|
+
const allSafe = commands.length > 0 && commands.every(
|
|
14570
|
+
(c) => !CHAIN_RE.test(c) && (GIT_PLUMBING_RE.test(c) || READ_ONLY_RE.test(c))
|
|
14571
|
+
);
|
|
14572
|
+
if (!allSafe) return true;
|
|
14573
|
+
}
|
|
14574
|
+
return false;
|
|
14575
|
+
}
|
|
14576
|
+
function scopeToAuthored(files, actionSummary) {
|
|
14577
|
+
if (!actionSummary) return { files, signal: "no-transcript" };
|
|
14578
|
+
const touched = [...actionSummary.files_edited ?? [], ...actionSummary.files_created ?? []];
|
|
14579
|
+
if (touched.length === 0) return { files: [], signal: "none-authored" };
|
|
14580
|
+
return { files: narrowToAgentAuthored(files, actionSummary), signal: "authored" };
|
|
14581
|
+
}
|
|
14334
14582
|
function narrowToAgentAuthored(files, actionSummary) {
|
|
14335
14583
|
if (!actionSummary) return files;
|
|
14336
14584
|
const touched = [
|
|
@@ -14533,6 +14781,7 @@ function buildSummary(lines) {
|
|
|
14533
14781
|
searches++;
|
|
14534
14782
|
break;
|
|
14535
14783
|
case "Agent":
|
|
14784
|
+
case "Task":
|
|
14536
14785
|
subagents++;
|
|
14537
14786
|
break;
|
|
14538
14787
|
case "WebFetch":
|
|
@@ -14585,21 +14834,50 @@ function addPath(set, rawPath) {
|
|
|
14585
14834
|
function sanitizeCommand(rawCmd) {
|
|
14586
14835
|
if (typeof rawCmd !== "string" || !rawCmd) return null;
|
|
14587
14836
|
let cmd = rawCmd.split("\n")[0];
|
|
14837
|
+
let cut = -1;
|
|
14838
|
+
let marker = "";
|
|
14588
14839
|
for (const sep of [" | ", " > ", " >> ", " 2>", " && ", " ; "]) {
|
|
14589
14840
|
const idx = cmd.indexOf(sep);
|
|
14590
|
-
if (idx > 0
|
|
14841
|
+
if (idx > 0 && (cut === -1 || idx < cut)) {
|
|
14842
|
+
cut = idx;
|
|
14843
|
+
marker = sep.trim();
|
|
14844
|
+
}
|
|
14591
14845
|
}
|
|
14846
|
+
if (cut > -1) cmd = cmd.slice(0, cut);
|
|
14592
14847
|
if (cmd.length > MAX_COMMAND_CHARS) {
|
|
14593
14848
|
cmd = cmd.slice(0, MAX_COMMAND_CHARS);
|
|
14594
14849
|
}
|
|
14595
|
-
|
|
14850
|
+
cmd = cmd.trim();
|
|
14851
|
+
if (marker) cmd = cmd ? `${cmd} ${marker}` : marker;
|
|
14852
|
+
return cmd || null;
|
|
14596
14853
|
}
|
|
14597
14854
|
function capArray(set, max) {
|
|
14598
14855
|
return Array.from(set).slice(0, max);
|
|
14599
14856
|
}
|
|
14600
14857
|
|
|
14858
|
+
// src/lib/run-mode.ts
|
|
14859
|
+
function parseAutonomousEnv(raw) {
|
|
14860
|
+
if (raw === void 0) return void 0;
|
|
14861
|
+
const v = raw.trim().toLowerCase();
|
|
14862
|
+
if (v === "") return void 0;
|
|
14863
|
+
if (v === "0" || v === "false" || v === "off" || v === "no") return false;
|
|
14864
|
+
return true;
|
|
14865
|
+
}
|
|
14866
|
+
function resolveRunMode(inputs = {}) {
|
|
14867
|
+
if (inputs.autonomousFlag === true) return "autonomous";
|
|
14868
|
+
if (inputs.autonomousFlag === false) return "interactive";
|
|
14869
|
+
const env = inputs.env ?? process.env;
|
|
14870
|
+
const envDecision = parseAutonomousEnv(env.VERITY_AUTONOMOUS);
|
|
14871
|
+
if (envDecision !== void 0) return envDecision ? "autonomous" : "interactive";
|
|
14872
|
+
const isTTY = inputs.isTTY ?? Boolean(process.stdin?.isTTY);
|
|
14873
|
+
return isTTY ? "interactive" : "autonomous";
|
|
14874
|
+
}
|
|
14875
|
+
function isExplicitlyAutonomous(env = process.env) {
|
|
14876
|
+
return parseAutonomousEnv(env.VERITY_AUTONOMOUS) === true || parseAutonomousEnv(env.CI) === true || parseAutonomousEnv(env.GITHUB_ACTIONS) === true;
|
|
14877
|
+
}
|
|
14878
|
+
|
|
14601
14879
|
// src/lib/seed-runner.ts
|
|
14602
|
-
var
|
|
14880
|
+
var import_promises11 = require("node:fs/promises");
|
|
14603
14881
|
var import_node_fs18 = require("node:fs");
|
|
14604
14882
|
var import_node_path15 = require("node:path");
|
|
14605
14883
|
var import_yaml2 = __toESM(require_dist());
|
|
@@ -14845,7 +15123,7 @@ async function runSeed(opts) {
|
|
|
14845
15123
|
}
|
|
14846
15124
|
let standardDoc;
|
|
14847
15125
|
try {
|
|
14848
|
-
const raw = await (0,
|
|
15126
|
+
const raw = await (0, import_promises11.readFile)(STANDARD_FILE, "utf-8");
|
|
14849
15127
|
standardDoc = (0, import_yaml2.parse)(raw);
|
|
14850
15128
|
} catch {
|
|
14851
15129
|
return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
@@ -14854,7 +15132,7 @@ async function runSeed(opts) {
|
|
|
14854
15132
|
let readmeContent;
|
|
14855
15133
|
if ((0, import_node_fs18.existsSync)("README.md")) {
|
|
14856
15134
|
try {
|
|
14857
|
-
readmeContent = await (0,
|
|
15135
|
+
readmeContent = await (0, import_promises11.readFile)("README.md", "utf-8");
|
|
14858
15136
|
} catch {
|
|
14859
15137
|
}
|
|
14860
15138
|
}
|
|
@@ -14862,7 +15140,7 @@ async function runSeed(opts) {
|
|
|
14862
15140
|
for (const p of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
|
|
14863
15141
|
if ((0, import_node_fs18.existsSync)(p)) {
|
|
14864
15142
|
try {
|
|
14865
|
-
claudeMdContent = await (0,
|
|
15143
|
+
claudeMdContent = await (0, import_promises11.readFile)(p, "utf-8");
|
|
14866
15144
|
break;
|
|
14867
15145
|
} catch {
|
|
14868
15146
|
}
|
|
@@ -14921,8 +15199,8 @@ async function runSeed(opts) {
|
|
|
14921
15199
|
const filePathRel = res.data.file_path;
|
|
14922
15200
|
const targetPath = (0, import_node_path15.join)(MEMORY_DIR, filePathRel);
|
|
14923
15201
|
try {
|
|
14924
|
-
await (0,
|
|
14925
|
-
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));
|
|
14926
15204
|
created++;
|
|
14927
15205
|
opts.onCreated?.(nodeId, filePathRel, c);
|
|
14928
15206
|
} catch (err) {
|
|
@@ -15012,7 +15290,10 @@ async function runAnalyze(opts, globals) {
|
|
|
15012
15290
|
}
|
|
15013
15291
|
const { assistantMessage: assistantResponse, stopReason, transcriptPath, sessionId } = await readStopHookStdin();
|
|
15014
15292
|
const actionSummary = transcriptPath ? await extractActionSummary(transcriptPath) : null;
|
|
15015
|
-
const
|
|
15293
|
+
const tokenResult = await resolveToken(globals.token);
|
|
15294
|
+
const scopeToken = tokenResult.ok ? tokenResult.data.token : void 0;
|
|
15295
|
+
const rawSessionId = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
|
|
15296
|
+
const baselineSessionId = sessionScopeKey(scopeToken, rawSessionId);
|
|
15016
15297
|
const baseline = readBaseline(baselineSessionId);
|
|
15017
15298
|
if (baseline) {
|
|
15018
15299
|
logEvent("baseline_loaded", {
|
|
@@ -15030,7 +15311,7 @@ async function runAnalyze(opts, globals) {
|
|
|
15030
15311
|
passAndExit("No analyzable files changed");
|
|
15031
15312
|
}
|
|
15032
15313
|
const allForReview = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable]));
|
|
15033
|
-
const conversation = await readAndClearConversationBuffer(
|
|
15314
|
+
const conversation = await readAndClearConversationBuffer(baselineSessionId);
|
|
15034
15315
|
const specs = discoverSpecs();
|
|
15035
15316
|
const plans = discoverPlans();
|
|
15036
15317
|
const latestPrompt = conversation?.prompts?.[conversation.prompts.length - 1]?.prompt ?? "";
|
|
@@ -15044,7 +15325,6 @@ async function runAnalyze(opts, globals) {
|
|
|
15044
15325
|
if (isReflectionQuestion(assistantResponse) && !agentAuthoredCodeThisTurn) {
|
|
15045
15326
|
passAndExit("Reflection-prompt turn \u2014 skipping analysis");
|
|
15046
15327
|
}
|
|
15047
|
-
const tokenResult = await resolveToken(globals.token);
|
|
15048
15328
|
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
15049
15329
|
if (!tokenResult.ok || !urlResult.ok) {
|
|
15050
15330
|
localOnlyAndExit(runLocalStatic(analyzable, securityFiles, baseline, !!opts.skipStatic));
|
|
@@ -15138,8 +15418,11 @@ async function runAnalyze(opts, globals) {
|
|
|
15138
15418
|
}
|
|
15139
15419
|
contentHash = hashResult.hash;
|
|
15140
15420
|
if (analysisMode !== "plan") {
|
|
15141
|
-
const
|
|
15142
|
-
|
|
15421
|
+
const scoped = scopeToAuthored(allForReview, actionSummary);
|
|
15422
|
+
if (scoped.signal === "none-authored" && !hasNonEditAuthorship(actionSummary, sessionAuthoredCode)) {
|
|
15423
|
+
passAndExit("No agent-authored code this turn \u2014 working-tree changes were not authored by this session");
|
|
15424
|
+
}
|
|
15425
|
+
const baseForReview = scoped.signal === "authored" && scoped.files.length > 0 ? scoped.files : allForReview;
|
|
15143
15426
|
const recentForReview = narrowToRecent(baseForReview, baselineSessionId);
|
|
15144
15427
|
if (!opts.skipStatic && isCodacyAvailable()) {
|
|
15145
15428
|
let allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
|
|
@@ -15269,7 +15552,9 @@ async function runAnalyze(opts, globals) {
|
|
|
15269
15552
|
if (snapshotResult.has_snapshots && snapshotResult.diffs.length > 0) {
|
|
15270
15553
|
requestBody.snapshot_diffs = snapshotResult.diffs;
|
|
15271
15554
|
}
|
|
15272
|
-
const
|
|
15555
|
+
const noHumanPrompt = (conversation?.prompts?.length ?? 0) === 0;
|
|
15556
|
+
const w4Task = noHumanPrompt && isExplicitlyAutonomous() ? resolveTaskContext() : null;
|
|
15557
|
+
const hasIntent = (conversation?.prompts?.length ?? 0) > 0 || specs.length > 0 || plans.length > 0 || !!assistantResponse || !!w4Task;
|
|
15273
15558
|
if (hasIntent) {
|
|
15274
15559
|
const intentContext = {};
|
|
15275
15560
|
if (conversation && conversation.prompts.length > 0) {
|
|
@@ -15284,6 +15569,10 @@ async function runAnalyze(opts, globals) {
|
|
|
15284
15569
|
intentContext.recent_commits = conversation.recent_commits;
|
|
15285
15570
|
}
|
|
15286
15571
|
}
|
|
15572
|
+
if (w4Task && !intentContext.user_prompt) {
|
|
15573
|
+
intentContext.user_prompt = w4Task.goal;
|
|
15574
|
+
logEvent("w4_issue_anchor", { issue: w4Task.number, via: w4Task.via });
|
|
15575
|
+
}
|
|
15287
15576
|
if (assistantResponse) {
|
|
15288
15577
|
const cap = analysisMode === "plan" ? MAX_ASSISTANT_RESPONSE_CHARS_PLAN : MAX_ASSISTANT_RESPONSE_CHARS_DEFAULT;
|
|
15289
15578
|
intentContext.assistant_response = assistantResponse.length > cap ? assistantResponse.slice(0, cap) : assistantResponse;
|
|
@@ -15576,7 +15865,10 @@ function registerBaselineCommands(program2) {
|
|
|
15576
15865
|
}
|
|
15577
15866
|
}
|
|
15578
15867
|
}
|
|
15579
|
-
const
|
|
15868
|
+
const authForScope = await resolveToken(program2.opts().token);
|
|
15869
|
+
const scopeToken = authForScope.ok ? authForScope.data.token : void 0;
|
|
15870
|
+
const scopeSession = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
|
|
15871
|
+
const result = captureBaseline({ sessionId: sessionScopeKey(scopeToken, scopeSession), source });
|
|
15580
15872
|
logEvent("baseline_capture", {
|
|
15581
15873
|
created: result.created,
|
|
15582
15874
|
source: source ?? null,
|
|
@@ -16022,20 +16314,112 @@ function writeBlockMessage(moment, response) {
|
|
|
16022
16314
|
var import_node_fs24 = require("node:fs");
|
|
16023
16315
|
var import_promises13 = require("node:fs/promises");
|
|
16024
16316
|
var import_node_path19 = require("node:path");
|
|
16025
|
-
var
|
|
16317
|
+
var import_node_child_process11 = require("node:child_process");
|
|
16026
16318
|
var readline2 = __toESM(require("node:readline/promises"));
|
|
16027
16319
|
|
|
16028
16320
|
// src/commands/migrate.ts
|
|
16029
16321
|
var import_node_fs23 = require("node:fs");
|
|
16030
16322
|
var import_node_path18 = require("node:path");
|
|
16031
|
-
var
|
|
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
|
|
16032
16416
|
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
16033
16417
|
function defaultNpmRemover(pkg) {
|
|
16034
|
-
(0,
|
|
16418
|
+
(0, import_node_child_process10.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
|
|
16035
16419
|
}
|
|
16036
16420
|
function isGitTracked(cwd, relPath) {
|
|
16037
16421
|
try {
|
|
16038
|
-
(0,
|
|
16422
|
+
(0, import_node_child_process10.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
|
|
16039
16423
|
return true;
|
|
16040
16424
|
} catch {
|
|
16041
16425
|
return false;
|
|
@@ -16043,7 +16427,7 @@ function isGitTracked(cwd, relPath) {
|
|
|
16043
16427
|
}
|
|
16044
16428
|
function isGitRepo(cwd) {
|
|
16045
16429
|
try {
|
|
16046
|
-
(0,
|
|
16430
|
+
(0, import_node_child_process10.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
|
|
16047
16431
|
return true;
|
|
16048
16432
|
} catch {
|
|
16049
16433
|
return false;
|
|
@@ -16059,6 +16443,7 @@ async function runMigration(opts = {}) {
|
|
|
16059
16443
|
await migrateLegacyHooks(root, actions);
|
|
16060
16444
|
await migrateClaudeMd(root, actions);
|
|
16061
16445
|
migrateStandardFile(root, actions);
|
|
16446
|
+
await migrateTelemetryHeaders(root, actions);
|
|
16062
16447
|
removeLegacyPackage(movedProjectDir, npmRemover, actions);
|
|
16063
16448
|
return { actions, migrated: actions.length > 0 };
|
|
16064
16449
|
}
|
|
@@ -16082,7 +16467,7 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
|
|
|
16082
16467
|
);
|
|
16083
16468
|
}
|
|
16084
16469
|
try {
|
|
16085
|
-
(0,
|
|
16470
|
+
(0, import_node_child_process10.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
|
|
16086
16471
|
actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
|
|
16087
16472
|
moved = true;
|
|
16088
16473
|
} catch {
|
|
@@ -16161,7 +16546,7 @@ function migrateStandardFile(root, actions) {
|
|
|
16161
16546
|
let moved = false;
|
|
16162
16547
|
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
16163
16548
|
try {
|
|
16164
|
-
(0,
|
|
16549
|
+
(0, import_node_child_process10.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
|
|
16165
16550
|
moved = true;
|
|
16166
16551
|
} catch {
|
|
16167
16552
|
}
|
|
@@ -16172,6 +16557,25 @@ function migrateStandardFile(root, actions) {
|
|
|
16172
16557
|
if (refreshed !== content) (0, import_node_fs23.writeFileSync)(verityMd, refreshed);
|
|
16173
16558
|
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
16174
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
|
+
}
|
|
16175
16579
|
function removeLegacyPackage(movedProjectDir, npmRemover, actions) {
|
|
16176
16580
|
if (!movedProjectDir) return;
|
|
16177
16581
|
try {
|
|
@@ -16217,7 +16621,7 @@ function readFileSyncSafe(path) {
|
|
|
16217
16621
|
}
|
|
16218
16622
|
function hasStagedChanges(root) {
|
|
16219
16623
|
try {
|
|
16220
|
-
(0,
|
|
16624
|
+
(0, import_node_child_process10.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
|
|
16221
16625
|
return false;
|
|
16222
16626
|
} catch {
|
|
16223
16627
|
return true;
|
|
@@ -16313,30 +16717,47 @@ async function promptYes(question) {
|
|
|
16313
16717
|
rl.close();
|
|
16314
16718
|
}
|
|
16315
16719
|
}
|
|
16316
|
-
async function
|
|
16317
|
-
const existing = await resolveToken();
|
|
16318
|
-
if (existing.ok)
|
|
16319
|
-
|
|
16320
|
-
|
|
16321
|
-
|
|
16322
|
-
|
|
16720
|
+
async function confirmExistingLogin(serviceUrl, opts) {
|
|
16721
|
+
const existing = await resolveToken(opts.token);
|
|
16722
|
+
if (!existing.ok) return "drive-login";
|
|
16723
|
+
const who = await whoami(existing.data.token, serviceUrl, opts.verbose);
|
|
16724
|
+
if (who.ok && who.data.logged_in) {
|
|
16725
|
+
printInfo(`Logged in as ${who.data.email ?? `user #${who.data.user_id}`} \u2713 \u2014 runs & memory sync to Verity.`);
|
|
16726
|
+
return "handled";
|
|
16727
|
+
}
|
|
16728
|
+
if (!who.ok) {
|
|
16729
|
+
if (existing.data.userId != null) {
|
|
16730
|
+
printInfo(`Logged in as ${existing.data.email ?? `user #${existing.data.userId}`} (cached \u2014 could not reach the Verity service). \u2713`);
|
|
16731
|
+
} else {
|
|
16732
|
+
printInfo("Could not confirm your login state with the service; continuing with your existing token.");
|
|
16733
|
+
printInfo(` (${who.error})`);
|
|
16323
16734
|
}
|
|
16324
|
-
|
|
16325
|
-
|
|
16326
|
-
|
|
16327
|
-
|
|
16328
|
-
|
|
16329
|
-
|
|
16330
|
-
|
|
16735
|
+
return "handled";
|
|
16736
|
+
}
|
|
16737
|
+
console.log("");
|
|
16738
|
+
printWarn("You are NOT logged in \u2014 this project has only an anonymous token.");
|
|
16739
|
+
printInfo(" The gate still runs, but no runs are saved and Verity keeps no memory of this project.");
|
|
16740
|
+
printInfo(" Log in below to unlock run history, trends, and cloud memory (strongly recommended).");
|
|
16741
|
+
return "drive-login";
|
|
16742
|
+
}
|
|
16743
|
+
async function runOptionalAuth(resolution, opts = {}) {
|
|
16744
|
+
let serviceUrl = resolution?.url ?? DEFAULT_SERVICE_URL;
|
|
16745
|
+
let healed = false;
|
|
16746
|
+
if (resolution) {
|
|
16747
|
+
const heal = await maybeHealServiceUrl(resolution, opts.verbose);
|
|
16748
|
+
serviceUrl = heal.serviceUrl;
|
|
16749
|
+
healed = heal.healed;
|
|
16750
|
+
if (healed) {
|
|
16751
|
+
printInfo(" Log in below to re-register this project and repair ~/.verity/credentials.");
|
|
16331
16752
|
}
|
|
16332
|
-
|
|
16333
|
-
|
|
16334
|
-
|
|
16335
|
-
|
|
16753
|
+
}
|
|
16754
|
+
if (!healed) {
|
|
16755
|
+
const state = await confirmExistingLogin(serviceUrl, opts);
|
|
16756
|
+
if (state === "handled") return;
|
|
16336
16757
|
}
|
|
16337
16758
|
let remote = "";
|
|
16338
16759
|
try {
|
|
16339
|
-
remote = (0,
|
|
16760
|
+
remote = (0, import_node_child_process11.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
16340
16761
|
} catch {
|
|
16341
16762
|
}
|
|
16342
16763
|
const localOnlyNote = () => {
|
|
@@ -16369,7 +16790,7 @@ async function runOptionalAuth(serviceUrl) {
|
|
|
16369
16790
|
}
|
|
16370
16791
|
const projectName = parseRemote(remote)?.repo ?? (0, import_node_path19.basename)(process.cwd());
|
|
16371
16792
|
printInfo("Authenticating with GitHub\u2026");
|
|
16372
|
-
const result = await registerProject({ projectName, remote, serviceUrl });
|
|
16793
|
+
const result = await registerProject({ projectName, remote, serviceUrl, verbose: opts.verbose });
|
|
16373
16794
|
if (result.ok) {
|
|
16374
16795
|
const who = result.data.email ?? (result.data.userId != null ? `user #${result.data.userId}` : null);
|
|
16375
16796
|
printInfo(`Logged in${who ? ` as ${who}` : ""} \u2713 \u2014 runs, history, and cloud memory now sync to Verity.`);
|
|
@@ -16432,30 +16853,30 @@ function registerInitCommand(program2) {
|
|
|
16432
16853
|
}
|
|
16433
16854
|
printInfo(` Node.js ${nodeVersion} \u2713`);
|
|
16434
16855
|
try {
|
|
16435
|
-
const gitVersion = (0,
|
|
16856
|
+
const gitVersion = (0, import_node_child_process11.execSync)("git --version", { encoding: "utf-8" }).trim();
|
|
16436
16857
|
printInfo(` ${gitVersion} \u2713`);
|
|
16437
16858
|
} catch {
|
|
16438
16859
|
printError("git is required but not installed. Install from https://git-scm.com");
|
|
16439
16860
|
process.exit(1);
|
|
16440
16861
|
}
|
|
16441
16862
|
try {
|
|
16442
|
-
(0,
|
|
16863
|
+
(0, import_node_child_process11.execSync)("which claude", { encoding: "utf-8" });
|
|
16443
16864
|
printInfo(" Claude Code \u2713");
|
|
16444
16865
|
} catch {
|
|
16445
16866
|
printWarn(" Claude Code not found \u2014 hooks will be configured but need Claude Code to run.");
|
|
16446
16867
|
}
|
|
16447
16868
|
try {
|
|
16448
|
-
(0,
|
|
16869
|
+
(0, import_node_child_process11.execSync)("which codacy-analysis", { encoding: "utf-8", stdio: "pipe" });
|
|
16449
16870
|
printInfo(" @codacy/analysis-cli \u2713");
|
|
16450
16871
|
} catch {
|
|
16451
16872
|
printInfo(" Installing @codacy/analysis-cli...");
|
|
16452
16873
|
try {
|
|
16453
|
-
(0,
|
|
16874
|
+
(0, import_node_child_process11.execSync)("npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "pipe", timeout: 12e4 });
|
|
16454
16875
|
printInfo(" @codacy/analysis-cli installed \u2713");
|
|
16455
16876
|
} catch {
|
|
16456
16877
|
try {
|
|
16457
16878
|
printWarn(" Retrying with sudo...");
|
|
16458
|
-
(0,
|
|
16879
|
+
(0, import_node_child_process11.execSync)("sudo npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
|
|
16459
16880
|
printInfo(" @codacy/analysis-cli installed \u2713");
|
|
16460
16881
|
} catch {
|
|
16461
16882
|
printWarn(" Could not install @codacy/analysis-cli automatically.");
|
|
@@ -16523,8 +16944,11 @@ function registerInitCommand(program2) {
|
|
|
16523
16944
|
console.log("");
|
|
16524
16945
|
try {
|
|
16525
16946
|
const globals = program2.opts();
|
|
16526
|
-
const urlResult = await
|
|
16527
|
-
await runOptionalAuth(urlResult.ok ? urlResult.data :
|
|
16947
|
+
const urlResult = await resolveServiceUrlDetailed(globals.serviceUrl);
|
|
16948
|
+
await runOptionalAuth(urlResult.ok ? urlResult.data : null, {
|
|
16949
|
+
token: globals.token,
|
|
16950
|
+
verbose: globals.verbose
|
|
16951
|
+
});
|
|
16528
16952
|
} catch (err) {
|
|
16529
16953
|
printWarn(`Authentication step skipped: ${err.message}`);
|
|
16530
16954
|
}
|
|
@@ -16894,24 +17318,6 @@ function registerResetCommand(program2) {
|
|
|
16894
17318
|
});
|
|
16895
17319
|
}
|
|
16896
17320
|
|
|
16897
|
-
// src/lib/run-mode.ts
|
|
16898
|
-
function parseAutonomousEnv(raw) {
|
|
16899
|
-
if (raw === void 0) return void 0;
|
|
16900
|
-
const v = raw.trim().toLowerCase();
|
|
16901
|
-
if (v === "") return void 0;
|
|
16902
|
-
if (v === "0" || v === "false" || v === "off" || v === "no") return false;
|
|
16903
|
-
return true;
|
|
16904
|
-
}
|
|
16905
|
-
function resolveRunMode(inputs = {}) {
|
|
16906
|
-
if (inputs.autonomousFlag === true) return "autonomous";
|
|
16907
|
-
if (inputs.autonomousFlag === false) return "interactive";
|
|
16908
|
-
const env = inputs.env ?? process.env;
|
|
16909
|
-
const envDecision = parseAutonomousEnv(env.VERITY_AUTONOMOUS);
|
|
16910
|
-
if (envDecision !== void 0) return envDecision ? "autonomous" : "interactive";
|
|
16911
|
-
const isTTY = inputs.isTTY ?? Boolean(process.stdin?.isTTY);
|
|
16912
|
-
return isTTY ? "interactive" : "autonomous";
|
|
16913
|
-
}
|
|
16914
|
-
|
|
16915
17321
|
// src/commands/reflect.ts
|
|
16916
17322
|
function registerReflectCommand(program2) {
|
|
16917
17323
|
program2.command("reflect").description("Capture learnings \u2014 auto-extract or submit a human reflection").option("--user-input <text>", "The reflection to record (the agent-drafted or user-confirmed text)").option("--kind <kind>", "Node kind (decision, gotcha, pattern, security, quality, intent, domain, integration)", "gotcha").option("--task-id <id>", "Task to reflect on (defaults to current task)").option("--autonomous", "Record the drafted reflection without a confirm step (auto-detected from TTY / VERITY_AUTONOMOUS when omitted)").action(async (opts) => {
|
|
@@ -17140,89 +17546,6 @@ function registerRunCommand(program2) {
|
|
|
17140
17546
|
});
|
|
17141
17547
|
}
|
|
17142
17548
|
|
|
17143
|
-
// src/lib/telemetry.ts
|
|
17144
|
-
var import_promises14 = require("node:fs/promises");
|
|
17145
|
-
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
17146
|
-
var GITIGNORE_FILE = ".gitignore";
|
|
17147
|
-
var GITIGNORE_ENTRY = ".claude/settings.local.json";
|
|
17148
|
-
function deriveOtlpEndpoint(serviceUrl) {
|
|
17149
|
-
return serviceUrl.replace(/\/+$/, "").replace(/\/v1$/, "") + "/v1/otlp";
|
|
17150
|
-
}
|
|
17151
|
-
function buildTelemetryEnv(serviceUrl, token) {
|
|
17152
|
-
return {
|
|
17153
|
-
CLAUDE_CODE_ENABLE_TELEMETRY: "1",
|
|
17154
|
-
OTEL_METRICS_EXPORTER: "otlp",
|
|
17155
|
-
OTEL_TRACES_EXPORTER: "otlp",
|
|
17156
|
-
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA: "1",
|
|
17157
|
-
OTEL_EXPORTER_OTLP_PROTOCOL: "http/json",
|
|
17158
|
-
OTEL_EXPORTER_OTLP_ENDPOINT: deriveOtlpEndpoint(serviceUrl),
|
|
17159
|
-
// <token> is whatever is in .verity/credentials — verity_ OR legacy gate_.
|
|
17160
|
-
// The server hashes the raw token regardless of prefix.
|
|
17161
|
-
OTEL_EXPORTER_OTLP_HEADERS: `Authorization=Bearer ${token}`,
|
|
17162
|
-
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: "delta",
|
|
17163
|
-
OTEL_LOG_USER_PROMPTS: "0",
|
|
17164
|
-
OTEL_LOG_TOOL_DETAILS: "0",
|
|
17165
|
-
OTEL_LOG_TOOL_CONTENT: "0",
|
|
17166
|
-
OTEL_METRICS_INCLUDE_SESSION_ID: "true",
|
|
17167
|
-
OTEL_METRICS_INCLUDE_ACCOUNT_UUID: "false",
|
|
17168
|
-
OTEL_METRICS_INCLUDE_VERSION: "false",
|
|
17169
|
-
OTEL_METRIC_EXPORT_INTERVAL: "60000"
|
|
17170
|
-
};
|
|
17171
|
-
}
|
|
17172
|
-
var VERITY_TELEMETRY_KEYS = Object.keys(buildTelemetryEnv("", ""));
|
|
17173
|
-
async function readSettingsLocal() {
|
|
17174
|
-
try {
|
|
17175
|
-
return JSON.parse(await (0, import_promises14.readFile)(projectPath(SETTINGS_LOCAL_FILE2), "utf-8"));
|
|
17176
|
-
} catch {
|
|
17177
|
-
return {};
|
|
17178
|
-
}
|
|
17179
|
-
}
|
|
17180
|
-
async function writeSettingsLocal(settings) {
|
|
17181
|
-
await writeJsonFilePreservingStyle(projectPath(SETTINGS_LOCAL_FILE2), settings);
|
|
17182
|
-
}
|
|
17183
|
-
async function ensureGitignore() {
|
|
17184
|
-
const file = projectPath(GITIGNORE_FILE);
|
|
17185
|
-
let content = "";
|
|
17186
|
-
try {
|
|
17187
|
-
content = await (0, import_promises14.readFile)(file, "utf-8");
|
|
17188
|
-
} catch {
|
|
17189
|
-
}
|
|
17190
|
-
const lines = content.split("\n").map((l) => l.trim());
|
|
17191
|
-
if (lines.includes(GITIGNORE_ENTRY) || lines.includes(".claude/") || lines.includes(".claude")) {
|
|
17192
|
-
return;
|
|
17193
|
-
}
|
|
17194
|
-
const block = "# Verity telemetry \u2014 holds your project token\n" + GITIGNORE_ENTRY + "\n";
|
|
17195
|
-
const next = content ? content + (content.endsWith("\n") ? "" : "\n") + "\n" + block : block;
|
|
17196
|
-
await (0, import_promises14.writeFile)(file, next);
|
|
17197
|
-
}
|
|
17198
|
-
async function installTelemetry(serviceUrl, token) {
|
|
17199
|
-
const env = buildTelemetryEnv(serviceUrl, token);
|
|
17200
|
-
const settings = await readSettingsLocal();
|
|
17201
|
-
settings.env = { ...settings.env ?? {}, ...env };
|
|
17202
|
-
await writeSettingsLocal(settings);
|
|
17203
|
-
await ensureGitignore();
|
|
17204
|
-
return { ok: true, data: { endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT } };
|
|
17205
|
-
}
|
|
17206
|
-
async function checkTelemetry() {
|
|
17207
|
-
const env = (await readSettingsLocal()).env ?? {};
|
|
17208
|
-
const enabled = env.CLAUDE_CODE_ENABLE_TELEMETRY === "1" && !!env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
17209
|
-
return { enabled, endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT ?? null, settingsPath: SETTINGS_LOCAL_FILE2 };
|
|
17210
|
-
}
|
|
17211
|
-
async function uninstallTelemetry() {
|
|
17212
|
-
const settings = await readSettingsLocal();
|
|
17213
|
-
if (!settings.env) return { ok: true, data: { removed: 0 } };
|
|
17214
|
-
let removed = 0;
|
|
17215
|
-
for (const key of VERITY_TELEMETRY_KEYS) {
|
|
17216
|
-
if (key in settings.env) {
|
|
17217
|
-
delete settings.env[key];
|
|
17218
|
-
removed++;
|
|
17219
|
-
}
|
|
17220
|
-
}
|
|
17221
|
-
if (Object.keys(settings.env).length === 0) delete settings.env;
|
|
17222
|
-
await writeSettingsLocal(settings);
|
|
17223
|
-
return { ok: true, data: { removed } };
|
|
17224
|
-
}
|
|
17225
|
-
|
|
17226
17549
|
// src/commands/telemetry.ts
|
|
17227
17550
|
function registerTelemetryCommands(program2) {
|
|
17228
17551
|
const telemetry = program2.command("telemetry").description("Manage Claude Code OpenTelemetry export to Verity (cost & usage)");
|
|
@@ -17238,16 +17561,25 @@ function registerTelemetryCommands(program2) {
|
|
|
17238
17561
|
printError(urlResult.error);
|
|
17239
17562
|
process.exit(1);
|
|
17240
17563
|
}
|
|
17241
|
-
const result = await installTelemetry(urlResult.data
|
|
17564
|
+
const result = await installTelemetry(urlResult.data);
|
|
17242
17565
|
if (!result.ok) {
|
|
17243
17566
|
printError(result.error);
|
|
17244
17567
|
process.exit(1);
|
|
17245
17568
|
}
|
|
17246
17569
|
printInfo(`Telemetry enabled \u2192 ${result.data.endpoint}`);
|
|
17247
|
-
printInfo(` wrote ${SETTINGS_LOCAL_FILE2} (
|
|
17570
|
+
printInfo(` wrote ${SETTINGS_LOCAL_FILE2} (no token stored \u2014 resolved at runtime via otelHeadersHelper)`);
|
|
17248
17571
|
printInfo(" takes effect on your NEXT Claude Code session; first metrics appear within ~60s of activity");
|
|
17249
17572
|
printInfo(" view cost & usage at /usage");
|
|
17250
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
|
+
});
|
|
17251
17583
|
telemetry.command("check").description("Show whether Claude Code telemetry export to Verity is enabled").option("--json", "Output status as JSON").action(async (opts) => {
|
|
17252
17584
|
const status = await checkTelemetry();
|
|
17253
17585
|
if (opts.json) {
|
|
@@ -17273,7 +17605,12 @@ function registerTelemetryCommands(program2) {
|
|
|
17273
17605
|
}
|
|
17274
17606
|
|
|
17275
17607
|
// src/cli.ts
|
|
17276
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.
|
|
17608
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.28.1-experimental.708d3ae").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
|
+
});
|
|
17277
17614
|
registerAuthCommands(program);
|
|
17278
17615
|
registerLoginCommand(program);
|
|
17279
17616
|
registerHooksCommands(program);
|
|
@@ -17295,4 +17632,8 @@ registerMemoryCommand(program);
|
|
|
17295
17632
|
registerRunCommand(program);
|
|
17296
17633
|
registerMigrateCommand(program);
|
|
17297
17634
|
registerTelemetryCommands(program);
|
|
17298
|
-
program.
|
|
17635
|
+
program.parseAsync().catch((err) => {
|
|
17636
|
+
process.stderr.write(`[verity] ${err instanceof Error ? err.message : String(err)}
|
|
17637
|
+
`);
|
|
17638
|
+
process.exit(1);
|
|
17639
|
+
});
|