@codacy/verity-cli 0.26.0 → 0.27.0-experimental.1106a16

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 CHANGED
@@ -10325,9 +10325,7 @@ var {
10325
10325
  } = import_index.default;
10326
10326
 
10327
10327
  // src/commands/auth.ts
10328
- var import_promises3 = require("node:fs/promises");
10329
- var import_node_child_process3 = require("node:child_process");
10330
- var import_node_path2 = require("node:path");
10328
+ var import_node_child_process4 = require("node:child_process");
10331
10329
 
10332
10330
  // src/lib/auth.ts
10333
10331
  var import_promises = require("node:fs/promises");
@@ -10475,7 +10473,15 @@ var SECURITY_PATTERNS = [
10475
10473
  /Dockerfile/
10476
10474
  ];
10477
10475
  var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
10478
- var DEFAULT_SERVICE_URL = "".length > 0 ? "" : PROD_SERVICE_URL;
10476
+ var DEFAULT_SERVICE_URL = "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1".length > 0 ? "https://yyfaqvcgslcrzvrbvqik.supabase.co/functions/v1" : PROD_SERVICE_URL;
10477
+ var GITHUB_CLIENT_ID = "Iv23li88HxAi3ZrbYzWh";
10478
+ var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
10479
+ var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
10480
+ var GITHUB_APP_SLUG = "verity-auth";
10481
+ var GITHUB_APP_INSTALL_URL = `https://github.com/apps/${GITHUB_APP_SLUG}/installations/new`;
10482
+ function githubAppInstallUrl(accountId) {
10483
+ return accountId != null ? `https://github.com/apps/${GITHUB_APP_SLUG}/installations/new/permissions?target_id=${accountId}` : GITHUB_APP_INSTALL_URL;
10484
+ }
10479
10485
 
10480
10486
  // src/lib/auth.ts
10481
10487
  async function resolveToken(flagToken) {
@@ -10641,7 +10647,8 @@ async function apiRequest(options) {
10641
10647
  timeout = 9e4,
10642
10648
  cmd = "unknown",
10643
10649
  retry = false,
10644
- encodeBody = false
10650
+ encodeBody = false,
10651
+ extraHeaders
10645
10652
  } = options;
10646
10653
  const url = `${serviceUrl}${path}`;
10647
10654
  const headers = {
@@ -10650,6 +10657,9 @@ async function apiRequest(options) {
10650
10657
  if (token) {
10651
10658
  headers["Authorization"] = `Bearer ${token}`;
10652
10659
  }
10660
+ if (extraHeaders) {
10661
+ Object.assign(headers, extraHeaders);
10662
+ }
10653
10663
  const testMockScenario = process.env.VERITY_TEST_MOCK_SCENARIO;
10654
10664
  if (testMockScenario) {
10655
10665
  headers["X-Verity-Mock-Scenario"] = testMockScenario;
@@ -10740,6 +10750,468 @@ function analyzeRequest(options) {
10740
10750
  });
10741
10751
  }
10742
10752
 
10753
+ // src/lib/register.ts
10754
+ var import_promises3 = require("node:fs/promises");
10755
+ var import_node_path3 = require("node:path");
10756
+ var readline = __toESM(require("node:readline/promises"));
10757
+
10758
+ // src/lib/provider-auth.ts
10759
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
10760
+ var form = (fields) => new URLSearchParams(fields).toString();
10761
+ async function githubAccountId(owner) {
10762
+ try {
10763
+ const res = await fetch(`https://api.github.com/users/${encodeURIComponent(owner)}`, {
10764
+ headers: {
10765
+ Accept: "application/vnd.github+json",
10766
+ "X-GitHub-Api-Version": "2022-11-28",
10767
+ "User-Agent": "verity-cli"
10768
+ }
10769
+ });
10770
+ if (!res.ok) return null;
10771
+ const body = await res.json();
10772
+ return typeof body.id === "number" ? body.id : null;
10773
+ } catch {
10774
+ return null;
10775
+ }
10776
+ }
10777
+ async function githubCanSeeRepo(owner, repo, token) {
10778
+ let res;
10779
+ try {
10780
+ res = await fetch(
10781
+ `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`,
10782
+ {
10783
+ headers: {
10784
+ Authorization: `Bearer ${token}`,
10785
+ Accept: "application/vnd.github+json",
10786
+ "X-GitHub-Api-Version": "2022-11-28",
10787
+ "User-Agent": "verity-cli"
10788
+ }
10789
+ }
10790
+ );
10791
+ } catch (err) {
10792
+ return { ok: false, error: `Network error contacting GitHub: ${err.message}` };
10793
+ }
10794
+ if (res.status === 404) return { ok: true, data: false };
10795
+ if (res.ok) return { ok: true, data: true };
10796
+ return { ok: false, error: `GitHub repo lookup failed (HTTP ${res.status})` };
10797
+ }
10798
+ async function githubDeviceFlow() {
10799
+ const override = process.env.VERITY_PROVIDER_TOKEN;
10800
+ if (override) return { ok: true, data: override };
10801
+ let dc;
10802
+ try {
10803
+ const res = await fetch(GITHUB_DEVICE_CODE_URL, {
10804
+ method: "POST",
10805
+ headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
10806
+ body: form({ client_id: GITHUB_CLIENT_ID })
10807
+ });
10808
+ if (!res.ok) {
10809
+ return { ok: false, error: `GitHub device-code request failed (HTTP ${res.status})` };
10810
+ }
10811
+ dc = await res.json();
10812
+ } catch (err) {
10813
+ return { ok: false, error: `Network error contacting GitHub: ${err.message}` };
10814
+ }
10815
+ if (!dc.device_code || !dc.user_code) {
10816
+ return {
10817
+ ok: false,
10818
+ error: "GitHub did not return a device code (is Device Flow enabled on the OAuth app?)"
10819
+ };
10820
+ }
10821
+ printInfo("");
10822
+ printInfo(`To authorize Verity, open: ${dc.verification_uri}`);
10823
+ printInfo(`And enter the code: ${dc.user_code}`);
10824
+ printInfo("Waiting for authorization\u2026");
10825
+ const deadline = Date.now() + (dc.expires_in || 900) * 1e3;
10826
+ let interval = dc.interval || 5;
10827
+ while (Date.now() < deadline) {
10828
+ await sleep(interval * 1e3);
10829
+ let data;
10830
+ try {
10831
+ const res = await fetch(GITHUB_ACCESS_TOKEN_URL, {
10832
+ method: "POST",
10833
+ headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
10834
+ body: form({
10835
+ client_id: GITHUB_CLIENT_ID,
10836
+ device_code: dc.device_code,
10837
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
10838
+ })
10839
+ });
10840
+ data = await res.json().catch(() => ({}));
10841
+ } catch {
10842
+ continue;
10843
+ }
10844
+ if (data.access_token) return { ok: true, data: data.access_token };
10845
+ switch (data.error) {
10846
+ case "authorization_pending":
10847
+ break;
10848
+ case "slow_down":
10849
+ interval += 5;
10850
+ break;
10851
+ case "access_denied":
10852
+ return { ok: false, error: "Authorization was denied on GitHub." };
10853
+ case "expired_token":
10854
+ return { ok: false, error: "The authorization code expired. Re-run register." };
10855
+ default:
10856
+ if (data.error) return { ok: false, error: `GitHub auth error: ${data.error}` };
10857
+ }
10858
+ }
10859
+ return { ok: false, error: "Timed out waiting for GitHub authorization." };
10860
+ }
10861
+
10862
+ // src/lib/git.ts
10863
+ var import_node_child_process3 = require("node:child_process");
10864
+ var import_node_fs2 = require("node:fs");
10865
+ var import_node_path2 = require("node:path");
10866
+ function resolveFile(relpath) {
10867
+ if ((0, import_node_fs2.existsSync)(relpath)) return relpath;
10868
+ if ((0, import_node_fs2.existsSync)(".claude/worktrees")) {
10869
+ try {
10870
+ const entries = (0, import_node_fs2.readdirSync)(".claude/worktrees", { withFileTypes: true });
10871
+ for (const entry of entries) {
10872
+ if (!entry.isDirectory()) continue;
10873
+ const candidate = (0, import_node_path2.join)(".claude/worktrees", entry.name, relpath);
10874
+ if ((0, import_node_fs2.existsSync)(candidate)) return candidate;
10875
+ }
10876
+ } catch {
10877
+ }
10878
+ }
10879
+ return null;
10880
+ }
10881
+ function execGit(cmd) {
10882
+ try {
10883
+ return (0, import_node_child_process3.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
10884
+ } catch {
10885
+ return "";
10886
+ }
10887
+ }
10888
+ function splitLines(s) {
10889
+ return s.split("\n").filter((l) => l.length > 0);
10890
+ }
10891
+ var SHA_RE = /^[0-9a-f]{40}$/;
10892
+ function readBaselineSha() {
10893
+ if (!(0, import_node_fs2.existsSync)(BASELINE_SHA_FILE)) return null;
10894
+ let sha;
10895
+ try {
10896
+ sha = (0, import_node_fs2.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
10897
+ } catch {
10898
+ return null;
10899
+ }
10900
+ if (!SHA_RE.test(sha)) return null;
10901
+ const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
10902
+ if (!reachable) {
10903
+ try {
10904
+ (0, import_node_fs2.unlinkSync)(BASELINE_SHA_FILE);
10905
+ } catch {
10906
+ }
10907
+ return null;
10908
+ }
10909
+ return sha;
10910
+ }
10911
+ function writeBaselineSha(sha) {
10912
+ if (!SHA_RE.test(sha)) return;
10913
+ try {
10914
+ (0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(BASELINE_SHA_FILE), { recursive: true });
10915
+ (0, import_node_fs2.writeFileSync)(BASELINE_SHA_FILE, sha);
10916
+ } catch {
10917
+ }
10918
+ }
10919
+ function getChangedFiles() {
10920
+ const sets = /* @__PURE__ */ new Set();
10921
+ let hasRecentCommitFiles = false;
10922
+ for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
10923
+ for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
10924
+ for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
10925
+ const baseline = readBaselineSha();
10926
+ if (baseline) {
10927
+ const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
10928
+ if (committed.length > 0) {
10929
+ hasRecentCommitFiles = true;
10930
+ for (const f of committed) sets.add(f);
10931
+ }
10932
+ } else {
10933
+ const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
10934
+ const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
10935
+ const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
10936
+ const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
10937
+ if (commitAge < 120 && !hasUnstaged && !hasStaged) {
10938
+ const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
10939
+ if (recentFiles.length > 0) {
10940
+ hasRecentCommitFiles = true;
10941
+ for (const f of recentFiles) sets.add(f);
10942
+ }
10943
+ }
10944
+ }
10945
+ for (const f of getWorktreeFiles()) sets.add(f);
10946
+ const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
10947
+ return { files: filtered, hasRecentCommitFiles };
10948
+ }
10949
+ function getStagedFiles() {
10950
+ return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
10951
+ }
10952
+ function getDirtyFiles() {
10953
+ const set = /* @__PURE__ */ new Set();
10954
+ for (const f of splitLines(execGit("git diff --name-only HEAD"))) set.add(f);
10955
+ for (const f of splitLines(execGit("git diff --name-only --cached"))) set.add(f);
10956
+ for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
10957
+ return Array.from(set).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
10958
+ }
10959
+ function showContentAtRef(ref, repoRelPath) {
10960
+ if (!ref || ref === "no-git") return null;
10961
+ const normalizedPath = repoRelPath.replace(/\\/g, "/");
10962
+ try {
10963
+ return (0, import_node_child_process3.execFileSync)("git", ["show", `${ref}:${normalizedPath}`], {
10964
+ encoding: "utf-8",
10965
+ maxBuffer: 64 * 1024 * 1024,
10966
+ stdio: ["pipe", "pipe", "pipe"]
10967
+ });
10968
+ } catch {
10969
+ return null;
10970
+ }
10971
+ }
10972
+ function getPushRangeFiles() {
10973
+ const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
10974
+ const resolvers = [
10975
+ () => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
10976
+ () => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
10977
+ () => {
10978
+ const branch = execGit("git rev-parse --abbrev-ref HEAD");
10979
+ return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
10980
+ }
10981
+ ];
10982
+ for (const resolve of resolvers) {
10983
+ const range = resolve();
10984
+ if (range) return { files: diff(range), range };
10985
+ }
10986
+ const baseline = readBaselineSha();
10987
+ if (baseline) {
10988
+ const files = diff(`${baseline}..HEAD`);
10989
+ if (files.length > 0) return { files, range: `${baseline}..HEAD` };
10990
+ }
10991
+ const last = diff("HEAD~1..HEAD");
10992
+ return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
10993
+ }
10994
+ function getPushRangeMessages() {
10995
+ const { range } = getPushRangeFiles();
10996
+ if (!range) return "";
10997
+ return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
10998
+ }
10999
+ function getWorktreeFiles() {
11000
+ const result = [];
11001
+ const worktreeDir = ".claude/worktrees";
11002
+ if (!(0, import_node_fs2.existsSync)(worktreeDir)) return result;
11003
+ try {
11004
+ const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
11005
+ const entries = (0, import_node_fs2.readdirSync)(worktreeDir, { withFileTypes: true });
11006
+ for (const entry of entries) {
11007
+ if (!entry.isDirectory()) continue;
11008
+ const wtDir = (0, import_node_path2.join)(worktreeDir, entry.name);
11009
+ scanDir(wtDir, wtDir, fiveMinAgo, result);
11010
+ }
11011
+ } catch {
11012
+ }
11013
+ return result;
11014
+ }
11015
+ function scanDir(baseDir, dir, minMtime, result) {
11016
+ try {
11017
+ const entries = (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true });
11018
+ for (const entry of entries) {
11019
+ const fullPath = (0, import_node_path2.join)(dir, entry.name);
11020
+ if (entry.isDirectory()) {
11021
+ if (entry.name === "node_modules" || entry.name === ".git") continue;
11022
+ scanDir(baseDir, fullPath, minMtime, result);
11023
+ } else if (entry.isFile()) {
11024
+ const ext = (0, import_node_path2.extname)(entry.name).slice(1);
11025
+ if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
11026
+ try {
11027
+ const stat3 = (0, import_node_fs2.statSync)(fullPath);
11028
+ if (stat3.mtimeMs >= minMtime) {
11029
+ const relPath = fullPath.slice(baseDir.length + 1);
11030
+ result.push(relPath);
11031
+ }
11032
+ } catch {
11033
+ }
11034
+ }
11035
+ }
11036
+ } catch {
11037
+ }
11038
+ }
11039
+ function filterAnalyzable(files) {
11040
+ return files.filter((f) => {
11041
+ const ext = (0, import_node_path2.extname)(f).slice(1);
11042
+ return ANALYZABLE_EXTENSIONS.has(ext);
11043
+ });
11044
+ }
11045
+ function filterReviewable(files) {
11046
+ return files.filter((f) => {
11047
+ const ext = (0, import_node_path2.extname)(f).slice(1);
11048
+ if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
11049
+ if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
11050
+ const basename3 = f.split("/").pop() ?? "";
11051
+ if (REVIEWABLE_FILENAMES.has(basename3)) return true;
11052
+ if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
11053
+ return false;
11054
+ });
11055
+ }
11056
+ function filterSecurity(files) {
11057
+ return files.filter(
11058
+ (f) => SECURITY_PATTERNS.some((p) => p.test(f))
11059
+ );
11060
+ }
11061
+ function getCurrentCommit() {
11062
+ return execGit("git rev-parse HEAD") || "no-git";
11063
+ }
11064
+ function detectProvider(host) {
11065
+ const h = host.toLowerCase();
11066
+ if (h.includes("github")) return "github";
11067
+ if (h.includes("gitlab")) return "gitlab";
11068
+ if (h.includes("bitbucket")) return "bitbucket";
11069
+ return "unknown";
11070
+ }
11071
+ function parseRemote(raw) {
11072
+ if (!raw || typeof raw !== "string") return null;
11073
+ let s = raw.trim();
11074
+ if (!s) return null;
11075
+ let host = "";
11076
+ let path = "";
11077
+ const scp = s.match(/^[^/@]+@([^:/]+):(.+)$/);
11078
+ if (scp) {
11079
+ host = scp[1];
11080
+ path = scp[2];
11081
+ } else {
11082
+ s = s.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//, "");
11083
+ s = s.replace(/^[^/@]+@/, "");
11084
+ const slash = s.indexOf("/");
11085
+ if (slash === -1) return null;
11086
+ host = s.slice(0, slash);
11087
+ path = s.slice(slash + 1);
11088
+ }
11089
+ host = host.toLowerCase().trim();
11090
+ path = path.replace(/\/+$/, "").replace(/\.git$/, "");
11091
+ if (!host || !path) return null;
11092
+ const segments = path.split("/").filter(Boolean);
11093
+ if (segments.length < 2) return null;
11094
+ const owner = segments[0];
11095
+ const repo = segments[segments.length - 1];
11096
+ if (!owner || !repo) return null;
11097
+ return {
11098
+ host,
11099
+ owner,
11100
+ repo,
11101
+ provider: detectProvider(host),
11102
+ orgUrl: `https://${host}/${owner}`,
11103
+ orgName: owner
11104
+ };
11105
+ }
11106
+ function listTrackedFiles() {
11107
+ const set = /* @__PURE__ */ new Set();
11108
+ for (const f of splitLines(execGit("git ls-files"))) set.add(f);
11109
+ for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
11110
+ return Array.from(set);
11111
+ }
11112
+
11113
+ // src/lib/register.ts
11114
+ var isInteractive = () => Boolean(process.stdin.isTTY && process.stdout.isTTY);
11115
+ async function waitForEnter(prompt) {
11116
+ if (!isInteractive()) return;
11117
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
11118
+ try {
11119
+ await rl.question(prompt);
11120
+ } finally {
11121
+ rl.close();
11122
+ }
11123
+ }
11124
+ async function ensureAppInstalled(owner, repo, token, installUrl) {
11125
+ const notInstalledError = {
11126
+ ok: false,
11127
+ error: `The Verity GitHub App can't see ${owner}/${repo}. Install it on "${owner}" and grant access to this repository, then re-run:
11128
+ ${installUrl}`
11129
+ };
11130
+ const maxAttempts = 3;
11131
+ for (let attempt = 1; ; attempt++) {
11132
+ const visible = await githubCanSeeRepo(owner, repo, token);
11133
+ if (!visible.ok) return { ok: true, data: void 0 };
11134
+ if (visible.data) return { ok: true, data: void 0 };
11135
+ if (!isInteractive() || attempt >= maxAttempts) return notInstalledError;
11136
+ printWarn(`The Verity GitHub App can't see ${owner}/${repo} yet.`);
11137
+ printInfo(`Install it on "${owner}" (grant access to this repo):`);
11138
+ printInfo(` ${installUrl}`);
11139
+ await waitForEnter("Press Enter once installed to retry\u2026 ");
11140
+ }
11141
+ }
11142
+ async function registerProject(opts) {
11143
+ const parsed = parseRemote(opts.remote);
11144
+ if (!parsed) {
11145
+ return { ok: false, error: `Could not parse git remote: ${opts.remote}` };
11146
+ }
11147
+ if (parsed.provider !== "github") {
11148
+ return { ok: false, error: `Provider '${parsed.provider}' is not supported yet \u2014 GitHub only for now.` };
11149
+ }
11150
+ const usingTokenOverride = Boolean(process.env.VERITY_PROVIDER_TOKEN);
11151
+ const ownerId = usingTokenOverride ? null : await githubAccountId(parsed.owner);
11152
+ const installUrl = githubAppInstallUrl(ownerId);
11153
+ if (!usingTokenOverride && isInteractive()) {
11154
+ printInfo("");
11155
+ printInfo(`Verity uses a read-only GitHub App to verify write access to ${parsed.owner}/${parsed.repo}.`);
11156
+ printInfo(`Install it on "${parsed.owner}" (grant access to this repo) if you haven't already:`);
11157
+ printInfo(` ${installUrl}`);
11158
+ await waitForEnter("Press Enter to continue to GitHub authorization\u2026 ");
11159
+ }
11160
+ const providerAuth = await githubDeviceFlow();
11161
+ if (!providerAuth.ok) {
11162
+ return { ok: false, error: providerAuth.error };
11163
+ }
11164
+ const providerToken = providerAuth.data;
11165
+ if (!usingTokenOverride) {
11166
+ const installed = await ensureAppInstalled(parsed.owner, parsed.repo, providerToken, installUrl);
11167
+ if (!installed.ok) {
11168
+ return { ok: false, error: installed.error };
11169
+ }
11170
+ }
11171
+ const result = await apiRequest({
11172
+ method: "POST",
11173
+ path: "/auth/register",
11174
+ serviceUrl: opts.serviceUrl,
11175
+ body: { project_name: opts.projectName, git_remote_url: opts.remote },
11176
+ extraHeaders: { "X-Provider-Token": providerToken },
11177
+ verbose: opts.verbose
11178
+ });
11179
+ if (!result.ok) {
11180
+ return { ok: false, error: result.error };
11181
+ }
11182
+ const { project_id, token, service_url, user } = result.data;
11183
+ try {
11184
+ await (0, import_promises3.mkdir)(VERITY_DIR, { recursive: true });
11185
+ await (0, import_promises3.writeFile)(
11186
+ CREDENTIALS_FILE,
11187
+ `token: ${token}
11188
+ service_url: ${service_url}
11189
+ `,
11190
+ { mode: 384 }
11191
+ );
11192
+ await (0, import_promises3.chmod)(CREDENTIALS_FILE, 384).catch(() => {
11193
+ });
11194
+ } catch (err) {
11195
+ return {
11196
+ ok: false,
11197
+ error: `Registered with the Verity service, but could not save credentials to ${CREDENTIALS_FILE}: ${err.message}. Check filesystem permissions and re-run "verity auth register".`
11198
+ };
11199
+ }
11200
+ try {
11201
+ await (0, import_promises3.mkdir)((0, import_node_path3.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
11202
+ await (0, import_promises3.appendFile)(
11203
+ GLOBAL_CREDENTIALS_FILE,
11204
+ `${opts.remote} token: ${token}
11205
+ `,
11206
+ { mode: 384 }
11207
+ );
11208
+ await (0, import_promises3.chmod)(GLOBAL_CREDENTIALS_FILE, 384).catch(() => {
11209
+ });
11210
+ } catch {
11211
+ }
11212
+ return { ok: true, data: { projectId: project_id, serviceUrl: service_url, email: user?.email } };
11213
+ }
11214
+
10743
11215
  // src/commands/auth.ts
10744
11216
  function registerAuthCommands(program2) {
10745
11217
  const auth = program2.command("auth").description("Manage project authentication");
@@ -10749,36 +11221,26 @@ function registerAuthCommands(program2) {
10749
11221
  let remote = opts.remote;
10750
11222
  if (!remote) {
10751
11223
  try {
10752
- remote = (0, import_node_child_process3.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
11224
+ remote = (0, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
10753
11225
  } catch {
10754
11226
  printError("No git remote found. Use --remote to specify one.");
10755
11227
  process.exit(1);
10756
11228
  }
10757
11229
  }
10758
- const result = await apiRequest({
10759
- method: "POST",
10760
- path: "/auth/register",
11230
+ const result = await registerProject({
11231
+ projectName: opts.project,
11232
+ remote,
10761
11233
  serviceUrl,
10762
- body: { project_name: opts.project, git_remote_url: remote },
10763
11234
  verbose: globals.verbose
10764
11235
  });
10765
- if (!result.ok) {
10766
- printError(result.error);
10767
- process.exit(1);
10768
- }
10769
- const { project_id, token, service_url } = result.data;
10770
- await (0, import_promises3.mkdir)(VERITY_DIR, { recursive: true });
10771
- await (0, import_promises3.writeFile)(CREDENTIALS_FILE, `token: ${token}
10772
- service_url: ${service_url}
10773
- `);
10774
- try {
10775
- await (0, import_promises3.mkdir)((0, import_node_path2.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
10776
- await (0, import_promises3.appendFile)(GLOBAL_CREDENTIALS_FILE, `${remote} token: ${token}
10777
- `);
10778
- } catch {
11236
+ if (!result.ok) {
11237
+ printError(result.error);
11238
+ process.exit(1);
10779
11239
  }
10780
- printInfo(`Project registered: ${project_id}`);
10781
- printJson({ project_id, service_url });
11240
+ const { projectId, serviceUrl: resolvedUrl, email } = result.data;
11241
+ printInfo(`Project registered: ${projectId}`);
11242
+ if (email) printInfo(`Authenticated as: ${email}`);
11243
+ printJson({ project_id: projectId, service_url: resolvedUrl });
10782
11244
  });
10783
11245
  auth.command("verify").description("Verify the current token is valid").action(async () => {
10784
11246
  const globals = program2.opts();
@@ -10811,7 +11273,7 @@ service_url: ${service_url}
10811
11273
  let remote = opts.remote;
10812
11274
  if (!remote) {
10813
11275
  try {
10814
- remote = (0, import_node_child_process3.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
11276
+ remote = (0, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
10815
11277
  } catch {
10816
11278
  printError("No git remote found. Use --remote to specify one.");
10817
11279
  process.exit(1);
@@ -10839,11 +11301,11 @@ service_url: ${service_url}
10839
11301
 
10840
11302
  // src/lib/hooks.ts
10841
11303
  var import_promises5 = require("node:fs/promises");
10842
- var import_node_path4 = require("node:path");
11304
+ var import_node_path5 = require("node:path");
10843
11305
 
10844
11306
  // src/lib/json-file.ts
10845
11307
  var import_promises4 = require("node:fs/promises");
10846
- var import_node_path3 = require("node:path");
11308
+ var import_node_path4 = require("node:path");
10847
11309
  function jsonSemanticEqual(a, b) {
10848
11310
  if (a === b) return true;
10849
11311
  if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) {
@@ -10889,7 +11351,7 @@ async function writeJsonFilePreservingStyle(file, value) {
10889
11351
  const indent = currentRaw !== null ? detectJsonIndent(currentRaw) : 2;
10890
11352
  const next = JSON.stringify(value, null, indent) + "\n";
10891
11353
  if (next === currentRaw) return false;
10892
- await (0, import_promises4.mkdir)((0, import_node_path3.dirname)(file), { recursive: true });
11354
+ await (0, import_promises4.mkdir)((0, import_node_path4.dirname)(file), { recursive: true });
10893
11355
  await (0, import_promises4.writeFile)(file, next);
10894
11356
  return true;
10895
11357
  }
@@ -11050,13 +11512,13 @@ async function writeSettings(settings) {
11050
11512
  }
11051
11513
  async function readSettingsAt(root) {
11052
11514
  try {
11053
- return JSON.parse(await (0, import_promises5.readFile)((0, import_node_path4.join)(root, CLAUDE_SETTINGS_FILE), "utf-8"));
11515
+ return JSON.parse(await (0, import_promises5.readFile)((0, import_node_path5.join)(root, CLAUDE_SETTINGS_FILE), "utf-8"));
11054
11516
  } catch {
11055
11517
  return {};
11056
11518
  }
11057
11519
  }
11058
11520
  async function writeSettingsAt(root, settings) {
11059
- await writeJsonFilePreservingStyle((0, import_node_path4.join)(root, CLAUDE_SETTINGS_FILE), settings);
11521
+ await writeJsonFilePreservingStyle((0, import_node_path5.join)(root, CLAUDE_SETTINGS_FILE), settings);
11060
11522
  }
11061
11523
  async function hasLegacyHooksAt(root) {
11062
11524
  const settings = await readSettingsAt(root);
@@ -11296,8 +11758,8 @@ var import_node_crypto3 = require("node:crypto");
11296
11758
 
11297
11759
  // src/lib/conversation-buffer.ts
11298
11760
  var import_promises6 = require("node:fs/promises");
11299
- var import_node_fs2 = require("node:fs");
11300
- var import_node_child_process4 = require("node:child_process");
11761
+ var import_node_fs3 = require("node:fs");
11762
+ var import_node_child_process5 = require("node:child_process");
11301
11763
  var import_node_crypto = require("node:crypto");
11302
11764
  function stripImageReferences(text) {
11303
11765
  return text.replace(/\[Image #\d+\]/g, "[screenshot \u2014 not available for review]");
@@ -11332,7 +11794,7 @@ async function appendToConversationBuffer(prompt, sessionId) {
11332
11794
  }
11333
11795
  async function readAndClearConversationBuffer(currentSessionId) {
11334
11796
  try {
11335
- if ((0, import_node_fs2.existsSync)(CONVERSATION_BUFFER_FILE)) {
11797
+ if ((0, import_node_fs3.existsSync)(CONVERSATION_BUFFER_FILE)) {
11336
11798
  const entries = await readBufferEntries();
11337
11799
  let mine = entries;
11338
11800
  let others = [];
@@ -11356,7 +11818,7 @@ async function readAndClearConversationBuffer(currentSessionId) {
11356
11818
  };
11357
11819
  }
11358
11820
  }
11359
- if ((0, import_node_fs2.existsSync)(INTENT_FILE)) {
11821
+ if ((0, import_node_fs3.existsSync)(INTENT_FILE)) {
11360
11822
  try {
11361
11823
  const content = await (0, import_promises6.readFile)(INTENT_FILE, "utf-8");
11362
11824
  await (0, import_promises6.unlink)(INTENT_FILE).catch(() => {
@@ -11400,7 +11862,7 @@ async function readBufferEntries() {
11400
11862
  }
11401
11863
  function getRecentCommitMessages() {
11402
11864
  try {
11403
- const output = (0, import_node_child_process4.execSync)(
11865
+ const output = (0, import_node_child_process5.execSync)(
11404
11866
  'git log --since="30 minutes ago" --format="%s" -5',
11405
11867
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
11406
11868
  ).trim();
@@ -11413,8 +11875,8 @@ function getRecentCommitMessages() {
11413
11875
 
11414
11876
  // src/lib/task-context-buffer.ts
11415
11877
  var import_promises7 = require("node:fs/promises");
11416
- var import_node_fs3 = require("node:fs");
11417
- var import_node_path5 = require("node:path");
11878
+ var import_node_fs4 = require("node:fs");
11879
+ var import_node_path6 = require("node:path");
11418
11880
  var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
11419
11881
  var MAX_BUFFER_BYTES = 500 * 1024;
11420
11882
  var MAX_PROMPT_CHARS = 2e3;
@@ -11453,7 +11915,7 @@ async function appendResponseToTaskBuffer(taskId, assistantResponse, actionSumma
11453
11915
  }
11454
11916
  async function readTaskContextBuffer(taskId) {
11455
11917
  const filePath = bufferPath(taskId);
11456
- if (!(0, import_node_fs3.existsSync)(filePath)) return null;
11918
+ if (!(0, import_node_fs4.existsSync)(filePath)) return null;
11457
11919
  try {
11458
11920
  const content = await (0, import_promises7.readFile)(filePath, "utf-8");
11459
11921
  if (!content.trim()) return null;
@@ -11487,12 +11949,12 @@ async function readTaskContextBuffer(taskId) {
11487
11949
  }
11488
11950
  async function cleanupTaskContextBuffers() {
11489
11951
  try {
11490
- if (!(0, import_node_fs3.existsSync)(TASK_CONTEXT_DIR)) return;
11952
+ if (!(0, import_node_fs4.existsSync)(TASK_CONTEXT_DIR)) return;
11491
11953
  const files = await (0, import_promises7.readdir)(TASK_CONTEXT_DIR);
11492
11954
  const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
11493
11955
  for (const file of files) {
11494
11956
  if (!file.endsWith(".jsonl")) continue;
11495
- const filePath = (0, import_node_path5.join)(TASK_CONTEXT_DIR, file);
11957
+ const filePath = (0, import_node_path6.join)(TASK_CONTEXT_DIR, file);
11496
11958
  try {
11497
11959
  const stats = await (0, import_promises7.stat)(filePath);
11498
11960
  if (stats.mtimeMs < cutoffMs) {
@@ -11506,13 +11968,13 @@ async function cleanupTaskContextBuffers() {
11506
11968
  }
11507
11969
  function bufferPath(taskId) {
11508
11970
  const safe = taskId.replace(/[^a-zA-Z0-9_-]/g, "");
11509
- return (0, import_node_path5.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
11971
+ return (0, import_node_path6.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
11510
11972
  }
11511
11973
  async function appendEntry(taskId, entry) {
11512
11974
  try {
11513
11975
  await (0, import_promises7.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
11514
11976
  const filePath = bufferPath(taskId);
11515
- if ((0, import_node_fs3.existsSync)(filePath)) {
11977
+ if ((0, import_node_fs4.existsSync)(filePath)) {
11516
11978
  const stats = await (0, import_promises7.stat)(filePath);
11517
11979
  if (stats.size >= MAX_BUFFER_BYTES) {
11518
11980
  const content = await (0, import_promises7.readFile)(filePath, "utf-8");
@@ -11523,7 +11985,7 @@ async function appendEntry(taskId, entry) {
11523
11985
  }
11524
11986
  }
11525
11987
  const line = JSON.stringify(entry) + "\n";
11526
- const existing = (0, import_node_fs3.existsSync)(filePath) ? await (0, import_promises7.readFile)(filePath, "utf-8") : "";
11988
+ const existing = (0, import_node_fs4.existsSync)(filePath) ? await (0, import_promises7.readFile)(filePath, "utf-8") : "";
11527
11989
  await (0, import_promises7.writeFile)(filePath, existing + line);
11528
11990
  } catch {
11529
11991
  }
@@ -11531,8 +11993,8 @@ async function appendEntry(taskId, entry) {
11531
11993
 
11532
11994
  // src/lib/memory-retrieval.ts
11533
11995
  var import_promises8 = require("node:fs/promises");
11534
- var import_node_fs4 = require("node:fs");
11535
- var import_node_path6 = require("node:path");
11996
+ var import_node_fs5 = require("node:fs");
11997
+ var import_node_path7 = require("node:path");
11536
11998
  var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
11537
11999
  var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
11538
12000
  var DEFAULT_BUDGET_TOKENS = 2e3;
@@ -11625,19 +12087,19 @@ function parseFrontmatter(content) {
11625
12087
  return { fm, body: match[2].trim() };
11626
12088
  }
11627
12089
  async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = DEFAULT_BUDGET_TOKENS) {
11628
- if (!(0, import_node_fs4.existsSync)(memoryDir())) return null;
12090
+ if (!(0, import_node_fs5.existsSync)(memoryDir())) return null;
11629
12091
  const budget = Math.min(budgetTokens, MAX_BUDGET_TOKENS);
11630
12092
  const promptTokens = tokenize(promptText);
11631
12093
  const nodes = [];
11632
12094
  for (const domain of DOMAINS) {
11633
- const domainDir = (0, import_node_path6.join)(memoryDir(), domain);
11634
- if (!(0, import_node_fs4.existsSync)(domainDir)) continue;
12095
+ const domainDir = (0, import_node_path7.join)(memoryDir(), domain);
12096
+ if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
11635
12097
  try {
11636
12098
  const files = await (0, import_promises8.readdir)(domainDir);
11637
12099
  for (const file of files) {
11638
12100
  if (!file.endsWith(".md")) continue;
11639
12101
  try {
11640
- const content = await (0, import_promises8.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8");
12102
+ const content = await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8");
11641
12103
  const { fm, body } = parseFrontmatter(content);
11642
12104
  if (fm.status && fm.status !== "active") continue;
11643
12105
  nodes.push({
@@ -11696,8 +12158,8 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
11696
12158
 
11697
12159
  // src/lib/memory-sync.ts
11698
12160
  var import_promises9 = require("node:fs/promises");
11699
- var import_node_fs5 = require("node:fs");
11700
- var import_node_path7 = require("node:path");
12161
+ var import_node_fs6 = require("node:fs");
12162
+ var import_node_path8 = require("node:path");
11701
12163
  var import_node_crypto2 = require("node:crypto");
11702
12164
 
11703
12165
  // src/lib/glob-match.ts
@@ -11768,32 +12230,32 @@ var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
11768
12230
  async function ensureMemoryDir() {
11769
12231
  await (0, import_promises9.mkdir)(memoryDir2(), { recursive: true });
11770
12232
  for (const domain of DOMAINS2) {
11771
- await (0, import_promises9.mkdir)((0, import_node_path7.join)(memoryDir2(), domain), { recursive: true });
12233
+ await (0, import_promises9.mkdir)((0, import_node_path8.join)(memoryDir2(), domain), { recursive: true });
11772
12234
  }
11773
- if (!(0, import_node_fs5.existsSync)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"))) {
11774
- await (0, import_promises9.writeFile)((0, import_node_path7.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
12235
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path8.join)(memoryDir2(), "SCHEMA.md"))) {
12236
+ await (0, import_promises9.writeFile)((0, import_node_path8.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
11775
12237
  }
11776
- if (!(0, import_node_fs5.existsSync)((0, import_node_path7.join)(memoryDir2(), "index.md"))) {
11777
- await (0, import_promises9.writeFile)((0, import_node_path7.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
12238
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path8.join)(memoryDir2(), "index.md"))) {
12239
+ await (0, import_promises9.writeFile)((0, import_node_path8.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
11778
12240
  }
11779
- if (!(0, import_node_fs5.existsSync)((0, import_node_path7.join)(memoryDir2(), "log.md"))) {
11780
- await (0, import_promises9.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
12241
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path8.join)(memoryDir2(), "log.md"))) {
12242
+ await (0, import_promises9.writeFile)((0, import_node_path8.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
11781
12243
  }
11782
12244
  }
11783
12245
  async function buildManifest() {
11784
- if (!(0, import_node_fs5.existsSync)(memoryDir2())) {
12246
+ if (!(0, import_node_fs6.existsSync)(memoryDir2())) {
11785
12247
  return { schema_version: 1, nodes: [], index_hash: null, log_length: 0 };
11786
12248
  }
11787
12249
  const nodes = [];
11788
12250
  for (const domain of DOMAINS2) {
11789
- const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
11790
- if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
12251
+ const domainDir = (0, import_node_path8.join)(memoryDir2(), domain);
12252
+ if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
11791
12253
  try {
11792
12254
  const files = await (0, import_promises9.readdir)(domainDir);
11793
12255
  for (const file of files) {
11794
12256
  if (!file.endsWith(".md")) continue;
11795
12257
  const filePath = `${domain}/${file}`;
11796
- const fullPath = (0, import_node_path7.join)(memoryDir2(), filePath);
12258
+ const fullPath = (0, import_node_path8.join)(memoryDir2(), filePath);
11797
12259
  try {
11798
12260
  const content = await (0, import_promises9.readFile)(fullPath, "utf-8");
11799
12261
  const hash = (0, import_node_crypto2.createHash)("sha256").update(content).digest("hex").slice(0, 16);
@@ -11806,13 +12268,13 @@ async function buildManifest() {
11806
12268
  }
11807
12269
  let indexHash = null;
11808
12270
  try {
11809
- const indexContent = await (0, import_promises9.readFile)((0, import_node_path7.join)(memoryDir2(), "index.md"), "utf-8");
12271
+ const indexContent = await (0, import_promises9.readFile)((0, import_node_path8.join)(memoryDir2(), "index.md"), "utf-8");
11810
12272
  indexHash = `sha256:${(0, import_node_crypto2.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
11811
12273
  } catch {
11812
12274
  }
11813
12275
  let logLength = 0;
11814
12276
  try {
11815
- const logContent = await (0, import_promises9.readFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "utf-8");
12277
+ const logContent = await (0, import_promises9.readFile)((0, import_node_path8.join)(memoryDir2(), "log.md"), "utf-8");
11816
12278
  logLength = logContent.split("\n").length;
11817
12279
  } catch {
11818
12280
  }
@@ -11823,15 +12285,15 @@ function hashContent(content) {
11823
12285
  }
11824
12286
  async function readOnDiskNodes() {
11825
12287
  const out = /* @__PURE__ */ new Map();
11826
- if (!(0, import_node_fs5.existsSync)(memoryDir2())) return out;
12288
+ if (!(0, import_node_fs6.existsSync)(memoryDir2())) return out;
11827
12289
  for (const domain of DOMAINS2) {
11828
- const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
11829
- if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
12290
+ const domainDir = (0, import_node_path8.join)(memoryDir2(), domain);
12291
+ if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
11830
12292
  try {
11831
12293
  for (const file of await (0, import_promises9.readdir)(domainDir)) {
11832
12294
  if (!file.endsWith(".md")) continue;
11833
12295
  try {
11834
- out.set(`${domain}/${file}`, hashContent(await (0, import_promises9.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8")));
12296
+ out.set(`${domain}/${file}`, hashContent(await (0, import_promises9.readFile)((0, import_node_path8.join)(domainDir, file), "utf-8")));
11835
12297
  } catch {
11836
12298
  }
11837
12299
  }
@@ -11877,8 +12339,8 @@ async function computeEditedNodeUploads() {
11877
12339
  const uploads = [];
11878
12340
  for (const [path, prevHash] of prev) {
11879
12341
  if (prevHash == null) continue;
11880
- const full = (0, import_node_path7.join)(memoryDir2(), path);
11881
- if (!(0, import_node_fs5.existsSync)(full)) continue;
12342
+ const full = (0, import_node_path8.join)(memoryDir2(), path);
12343
+ if (!(0, import_node_fs6.existsSync)(full)) continue;
11882
12344
  let content;
11883
12345
  try {
11884
12346
  content = await (0, import_promises9.readFile)(full, "utf-8");
@@ -11914,15 +12376,15 @@ async function applyMemoryWrites(writes, opts = {}) {
11914
12376
  const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
11915
12377
  for (const n of notes) logLines.push(` - ${n}`);
11916
12378
  try {
11917
- const existing = (0, import_node_fs5.existsSync)((0, import_node_path7.join)(memoryDir2(), "log.md")) ? await (0, import_promises9.readFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
11918
- await (0, import_promises9.writeFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
12379
+ const existing = (0, import_node_fs6.existsSync)((0, import_node_path8.join)(memoryDir2(), "log.md")) ? await (0, import_promises9.readFile)((0, import_node_path8.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
12380
+ await (0, import_promises9.writeFile)((0, import_node_path8.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
11919
12381
  } catch {
11920
12382
  }
11921
12383
  await recordSyncedNodePaths();
11922
12384
  return count;
11923
12385
  }
11924
12386
  async function applyOneWrite(write, treePaths) {
11925
- const fullPath = (0, import_node_path7.join)(memoryDir2(), write.path);
12387
+ const fullPath = (0, import_node_path8.join)(memoryDir2(), write.path);
11926
12388
  const notes = [];
11927
12389
  let content = write.content;
11928
12390
  if (treePaths && treePaths.length > 0) {
@@ -11932,7 +12394,7 @@ async function applyOneWrite(write, treePaths) {
11932
12394
  notes.push(`${write.path}: dropped unmatched file_globs [${grounded.dropped.join(", ")}]`);
11933
12395
  }
11934
12396
  }
11935
- if ((0, import_node_fs5.existsSync)(fullPath)) {
12397
+ if ((0, import_node_fs6.existsSync)(fullPath)) {
11936
12398
  let existing = "";
11937
12399
  try {
11938
12400
  existing = await (0, import_promises9.readFile)(fullPath, "utf-8");
@@ -11944,7 +12406,7 @@ async function applyOneWrite(write, treePaths) {
11944
12406
  return { written: false, notes };
11945
12407
  }
11946
12408
  }
11947
- await (0, import_promises9.mkdir)((0, import_node_path7.dirname)(fullPath), { recursive: true });
12409
+ await (0, import_promises9.mkdir)((0, import_node_path8.dirname)(fullPath), { recursive: true });
11948
12410
  await (0, import_promises9.writeFile)(fullPath, content);
11949
12411
  return { written: true, notes };
11950
12412
  }
@@ -11985,8 +12447,8 @@ async function regenerateIndex() {
11985
12447
  ];
11986
12448
  let totalNodes = 0;
11987
12449
  for (const domain of DOMAINS2.filter((d) => d !== "_archive")) {
11988
- const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
11989
- if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
12450
+ const domainDir = (0, import_node_path8.join)(memoryDir2(), domain);
12451
+ if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
11990
12452
  try {
11991
12453
  const files = await (0, import_promises9.readdir)(domainDir);
11992
12454
  const mdFiles = files.filter((f) => f.endsWith(".md"));
@@ -11996,7 +12458,7 @@ async function regenerateIndex() {
11996
12458
  for (const file of mdFiles.sort()) {
11997
12459
  const slug = file.replace(/\.md$/, "");
11998
12460
  try {
11999
- const content = await (0, import_promises9.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8");
12461
+ const content = await (0, import_promises9.readFile)((0, import_node_path8.join)(domainDir, file), "utf-8");
12000
12462
  const title = pickFrontmatter(content, "title") ?? slug;
12001
12463
  const kind = pickFrontmatter(content, "kind") ?? "-";
12002
12464
  const confidence = pickFrontmatter(content, "confidence");
@@ -12020,7 +12482,7 @@ async function regenerateIndex() {
12020
12482
  lines.push("No nodes yet. Run an analysis to start building the knowledge graph.");
12021
12483
  }
12022
12484
  const next = lines.join("\n") + "\n";
12023
- const indexPath = (0, import_node_path7.join)(memoryDir2(), "index.md");
12485
+ const indexPath = (0, import_node_path8.join)(memoryDir2(), "index.md");
12024
12486
  let existing = null;
12025
12487
  try {
12026
12488
  existing = await (0, import_promises9.readFile)(indexPath, "utf-8");
@@ -12102,9 +12564,9 @@ function hasLegacyMemoryBlock(text) {
12102
12564
  return findMarker(text, LEGACY_MD_START) !== -1;
12103
12565
  }
12104
12566
  async function ensureClaudeMdPointer(cwd = repoRoot()) {
12105
- const claudeMdPath = (0, import_node_path7.join)(cwd, "CLAUDE.md");
12567
+ const claudeMdPath = (0, import_node_path8.join)(cwd, "CLAUDE.md");
12106
12568
  let existing = "";
12107
- if ((0, import_node_fs5.existsSync)(claudeMdPath)) {
12569
+ if ((0, import_node_fs6.existsSync)(claudeMdPath)) {
12108
12570
  existing = await (0, import_promises9.readFile)(claudeMdPath, "utf-8");
12109
12571
  }
12110
12572
  let startTag = CLAUDE_MD_START;
@@ -12234,7 +12696,7 @@ Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
12234
12696
  `;
12235
12697
 
12236
12698
  // src/commands/intent.ts
12237
- var import_node_fs6 = require("node:fs");
12699
+ var import_node_fs7 = require("node:fs");
12238
12700
  function registerIntentCommands(program2) {
12239
12701
  const intent = program2.command("intent").description("Manage intent capture");
12240
12702
  intent.command("capture").description("Capture user intent from stdin (used by UserPromptSubmit hook)").action(async () => {
@@ -12243,7 +12705,7 @@ function registerIntentCommands(program2) {
12243
12705
  process.chdir(repoRoot());
12244
12706
  } catch {
12245
12707
  }
12246
- if (!(0, import_node_fs6.existsSync)(VERITY_DIR)) {
12708
+ if (!(0, import_node_fs7.existsSync)(VERITY_DIR)) {
12247
12709
  process.exit(0);
12248
12710
  }
12249
12711
  const chunks = [];
@@ -12822,215 +13284,6 @@ async function sendGeneralFeedback(message, opts, globals) {
12822
13284
  var import_node_fs19 = require("node:fs");
12823
13285
  var import_node_path15 = require("node:path");
12824
13286
 
12825
- // src/lib/git.ts
12826
- var import_node_child_process5 = require("node:child_process");
12827
- var import_node_fs7 = require("node:fs");
12828
- var import_node_path8 = require("node:path");
12829
- function resolveFile(relpath) {
12830
- if ((0, import_node_fs7.existsSync)(relpath)) return relpath;
12831
- if ((0, import_node_fs7.existsSync)(".claude/worktrees")) {
12832
- try {
12833
- const entries = (0, import_node_fs7.readdirSync)(".claude/worktrees", { withFileTypes: true });
12834
- for (const entry of entries) {
12835
- if (!entry.isDirectory()) continue;
12836
- const candidate = (0, import_node_path8.join)(".claude/worktrees", entry.name, relpath);
12837
- if ((0, import_node_fs7.existsSync)(candidate)) return candidate;
12838
- }
12839
- } catch {
12840
- }
12841
- }
12842
- return null;
12843
- }
12844
- function execGit(cmd) {
12845
- try {
12846
- return (0, import_node_child_process5.execSync)(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
12847
- } catch {
12848
- return "";
12849
- }
12850
- }
12851
- function splitLines(s) {
12852
- return s.split("\n").filter((l) => l.length > 0);
12853
- }
12854
- var SHA_RE = /^[0-9a-f]{40}$/;
12855
- function readBaselineSha() {
12856
- if (!(0, import_node_fs7.existsSync)(BASELINE_SHA_FILE)) return null;
12857
- let sha;
12858
- try {
12859
- sha = (0, import_node_fs7.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
12860
- } catch {
12861
- return null;
12862
- }
12863
- if (!SHA_RE.test(sha)) return null;
12864
- const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
12865
- if (!reachable) {
12866
- try {
12867
- (0, import_node_fs7.unlinkSync)(BASELINE_SHA_FILE);
12868
- } catch {
12869
- }
12870
- return null;
12871
- }
12872
- return sha;
12873
- }
12874
- function writeBaselineSha(sha) {
12875
- if (!SHA_RE.test(sha)) return;
12876
- try {
12877
- (0, import_node_fs7.mkdirSync)((0, import_node_path8.dirname)(BASELINE_SHA_FILE), { recursive: true });
12878
- (0, import_node_fs7.writeFileSync)(BASELINE_SHA_FILE, sha);
12879
- } catch {
12880
- }
12881
- }
12882
- function getChangedFiles() {
12883
- const sets = /* @__PURE__ */ new Set();
12884
- let hasRecentCommitFiles = false;
12885
- for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
12886
- for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
12887
- for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
12888
- const baseline = readBaselineSha();
12889
- if (baseline) {
12890
- const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
12891
- if (committed.length > 0) {
12892
- hasRecentCommitFiles = true;
12893
- for (const f of committed) sets.add(f);
12894
- }
12895
- } else {
12896
- const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
12897
- const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
12898
- const hasUnstaged = splitLines(execGit("git diff --name-only HEAD")).length > 0;
12899
- const hasStaged = splitLines(execGit("git diff --name-only --cached")).length > 0;
12900
- if (commitAge < 120 && !hasUnstaged && !hasStaged) {
12901
- const recentFiles = splitLines(execGit("git diff --name-only HEAD~1..HEAD"));
12902
- if (recentFiles.length > 0) {
12903
- hasRecentCommitFiles = true;
12904
- for (const f of recentFiles) sets.add(f);
12905
- }
12906
- }
12907
- }
12908
- for (const f of getWorktreeFiles()) sets.add(f);
12909
- const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
12910
- return { files: filtered, hasRecentCommitFiles };
12911
- }
12912
- function getStagedFiles() {
12913
- return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
12914
- }
12915
- function getDirtyFiles() {
12916
- const set = /* @__PURE__ */ new Set();
12917
- for (const f of splitLines(execGit("git diff --name-only HEAD"))) set.add(f);
12918
- for (const f of splitLines(execGit("git diff --name-only --cached"))) set.add(f);
12919
- for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
12920
- return Array.from(set).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
12921
- }
12922
- function showContentAtRef(ref, repoRelPath) {
12923
- if (!ref || ref === "no-git") return null;
12924
- const normalizedPath = repoRelPath.replace(/\\/g, "/");
12925
- try {
12926
- return (0, import_node_child_process5.execFileSync)("git", ["show", `${ref}:${normalizedPath}`], {
12927
- encoding: "utf-8",
12928
- maxBuffer: 64 * 1024 * 1024,
12929
- stdio: ["pipe", "pipe", "pipe"]
12930
- });
12931
- } catch {
12932
- return null;
12933
- }
12934
- }
12935
- function getPushRangeFiles() {
12936
- const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
12937
- const resolvers = [
12938
- () => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
12939
- () => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
12940
- () => {
12941
- const branch = execGit("git rev-parse --abbrev-ref HEAD");
12942
- return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
12943
- }
12944
- ];
12945
- for (const resolve of resolvers) {
12946
- const range = resolve();
12947
- if (range) return { files: diff(range), range };
12948
- }
12949
- const baseline = readBaselineSha();
12950
- if (baseline) {
12951
- const files = diff(`${baseline}..HEAD`);
12952
- if (files.length > 0) return { files, range: `${baseline}..HEAD` };
12953
- }
12954
- const last = diff("HEAD~1..HEAD");
12955
- return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
12956
- }
12957
- function getPushRangeMessages() {
12958
- const { range } = getPushRangeFiles();
12959
- if (!range) return "";
12960
- return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
12961
- }
12962
- function getWorktreeFiles() {
12963
- const result = [];
12964
- const worktreeDir = ".claude/worktrees";
12965
- if (!(0, import_node_fs7.existsSync)(worktreeDir)) return result;
12966
- try {
12967
- const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
12968
- const entries = (0, import_node_fs7.readdirSync)(worktreeDir, { withFileTypes: true });
12969
- for (const entry of entries) {
12970
- if (!entry.isDirectory()) continue;
12971
- const wtDir = (0, import_node_path8.join)(worktreeDir, entry.name);
12972
- scanDir(wtDir, wtDir, fiveMinAgo, result);
12973
- }
12974
- } catch {
12975
- }
12976
- return result;
12977
- }
12978
- function scanDir(baseDir, dir, minMtime, result) {
12979
- try {
12980
- const entries = (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true });
12981
- for (const entry of entries) {
12982
- const fullPath = (0, import_node_path8.join)(dir, entry.name);
12983
- if (entry.isDirectory()) {
12984
- if (entry.name === "node_modules" || entry.name === ".git") continue;
12985
- scanDir(baseDir, fullPath, minMtime, result);
12986
- } else if (entry.isFile()) {
12987
- const ext = (0, import_node_path8.extname)(entry.name).slice(1);
12988
- if (!ANALYZABLE_EXTENSIONS.has(ext)) continue;
12989
- try {
12990
- const stat3 = (0, import_node_fs7.statSync)(fullPath);
12991
- if (stat3.mtimeMs >= minMtime) {
12992
- const relPath = fullPath.slice(baseDir.length + 1);
12993
- result.push(relPath);
12994
- }
12995
- } catch {
12996
- }
12997
- }
12998
- }
12999
- } catch {
13000
- }
13001
- }
13002
- function filterAnalyzable(files) {
13003
- return files.filter((f) => {
13004
- const ext = (0, import_node_path8.extname)(f).slice(1);
13005
- return ANALYZABLE_EXTENSIONS.has(ext);
13006
- });
13007
- }
13008
- function filterReviewable(files) {
13009
- return files.filter((f) => {
13010
- const ext = (0, import_node_path8.extname)(f).slice(1);
13011
- if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
13012
- if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
13013
- const basename2 = f.split("/").pop() ?? "";
13014
- if (REVIEWABLE_FILENAMES.has(basename2)) return true;
13015
- if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
13016
- return false;
13017
- });
13018
- }
13019
- function filterSecurity(files) {
13020
- return files.filter(
13021
- (f) => SECURITY_PATTERNS.some((p) => p.test(f))
13022
- );
13023
- }
13024
- function getCurrentCommit() {
13025
- return execGit("git rev-parse HEAD") || "no-git";
13026
- }
13027
- function listTrackedFiles() {
13028
- const set = /* @__PURE__ */ new Set();
13029
- for (const f of splitLines(execGit("git ls-files"))) set.add(f);
13030
- for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
13031
- return Array.from(set);
13032
- }
13033
-
13034
13287
  // src/lib/files.ts
13035
13288
  var import_node_fs8 = require("node:fs");
13036
13289
  var import_node_path9 = require("node:path");
@@ -14638,6 +14891,27 @@ function passAndExit(reason) {
14638
14891
  printJsonCompact({ gate_decision: "PASS", systemMessage: `Verity: ${reason}` });
14639
14892
  process.exit(0);
14640
14893
  }
14894
+ var EMPTY_STATIC = {
14895
+ tool: "@codacy/analysis-cli",
14896
+ findings: [],
14897
+ summary: { total_findings: 0, by_severity: {}, tools_run: [] }
14898
+ };
14899
+ function runLocalStatic(analyzable, securityFiles, baseline, skipStatic) {
14900
+ if (skipStatic || !isCodacyAvailable()) return EMPTY_STATIC;
14901
+ let scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
14902
+ if (baseline) scannable = scannable.filter((f) => changedSinceBaseline(f, baseline));
14903
+ if (scannable.length === 0) return EMPTY_STATIC;
14904
+ return runCodacyAnalysis(scannable);
14905
+ }
14906
+ function localOnlyAndExit(staticResults) {
14907
+ printJsonCompact({
14908
+ gate_decision: "PASS",
14909
+ systemMessage: "Verity: not authenticated \u2014 ran a local static-only check (no deep review, no upload). Run `verity init` to authenticate and enable the full quality gate.",
14910
+ unauthenticated: true,
14911
+ static_results: staticResults
14912
+ });
14913
+ process.exit(0);
14914
+ }
14641
14915
  function registerAnalyzeCommand(program2) {
14642
14916
  program2.command("analyze").description("Run Verity analysis on changed files (stop hook)").option("--debounce <seconds>", "Skip if last analysis was within N seconds", "30").option("--max-iterations <n>", "Force PASS after N FAIL cycles", "2").option("--max-files <n>", "Max files to send for review", "20").option("--max-file-size <bytes>", "Skip files larger than N bytes", "51200").option("--max-total-size <bytes>", "Stop collecting files at N total bytes", "194560").option("--skip-static", "Skip codacy-analysis").option("--mode <mode>", "Force analysis mode (standard|plan|debug|skip)").option("--json", "Output raw JSON response").action(async (opts) => {
14643
14917
  const globals = program2.opts();
@@ -14688,12 +14962,9 @@ async function runAnalyze(opts, globals) {
14688
14962
  passAndExit("Reflection-prompt turn \u2014 skipping analysis");
14689
14963
  }
14690
14964
  const tokenResult = await resolveToken(globals.token);
14691
- if (!tokenResult.ok) {
14692
- passAndExit("Not configured yet \u2014 run /verity-setup in Claude Code to enable quality gates.");
14693
- }
14694
14965
  const urlResult = await resolveServiceUrl(globals.serviceUrl);
14695
- if (!urlResult.ok) {
14696
- passAndExit("Not configured yet \u2014 run /verity-setup in Claude Code to enable quality gates.");
14966
+ if (!tokenResult.ok || !urlResult.ok) {
14967
+ localOnlyAndExit(runLocalStatic(analyzable, securityFiles, baseline, !!opts.skipStatic));
14697
14968
  }
14698
14969
  const sessionIdForMemory = sessionId || process.env.CLAUDE_SESSION_ID || "";
14699
14970
  let contextFilePaths = [];
@@ -15267,13 +15538,14 @@ async function runReview(opts, globals) {
15267
15538
  }
15268
15539
  const codeDelta = collectCodeDelta(allFiles);
15269
15540
  const tokenResult = await resolveToken(globals.token);
15270
- if (!tokenResult.ok) {
15271
- printError("Not configured yet \u2014 run /verity-setup in Claude Code to enable quality gates.");
15272
- process.exit(0);
15273
- }
15274
15541
  const urlResult = await resolveServiceUrl(globals.serviceUrl);
15275
- if (!urlResult.ok) {
15276
- printError("Not configured yet \u2014 run /verity-setup in Claude Code to enable quality gates.");
15542
+ if (!tokenResult.ok || !urlResult.ok) {
15543
+ printJsonCompact({
15544
+ gate_decision: "PASS",
15545
+ systemMessage: "Verity: not authenticated \u2014 showing local static results only (no deep review, no upload). Run `verity init` to authenticate and unlock the full review.",
15546
+ unauthenticated: true,
15547
+ static_results: staticResults
15548
+ });
15277
15549
  process.exit(0);
15278
15550
  }
15279
15551
  let specs;
@@ -15662,6 +15934,7 @@ var import_node_fs24 = require("node:fs");
15662
15934
  var import_promises13 = require("node:fs/promises");
15663
15935
  var import_node_path18 = require("node:path");
15664
15936
  var import_node_child_process9 = require("node:child_process");
15937
+ var readline2 = __toESM(require("node:readline/promises"));
15665
15938
 
15666
15939
  // src/commands/migrate.ts
15667
15940
  var import_node_fs23 = require("node:fs");
@@ -15941,6 +16214,66 @@ function registerMigrateCommand(program2) {
15941
16214
  }
15942
16215
 
15943
16216
  // src/commands/init.ts
16217
+ async function promptYes(question) {
16218
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
16219
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
16220
+ try {
16221
+ const answer = (await rl.question(question)).trim().toLowerCase();
16222
+ return answer === "" || answer === "y" || answer === "yes";
16223
+ } finally {
16224
+ rl.close();
16225
+ }
16226
+ }
16227
+ async function runOptionalAuth() {
16228
+ const existing = await resolveToken();
16229
+ if (existing.ok) {
16230
+ printInfo("Already authenticated \u2014 results will upload to the Verity service. \u2713");
16231
+ return;
16232
+ }
16233
+ let remote = "";
16234
+ try {
16235
+ remote = (0, import_node_child_process9.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
16236
+ } catch {
16237
+ }
16238
+ const localOnlyNote = () => {
16239
+ printInfo("Verity runs in local-only mode: the gate still runs and shows static findings, but nothing uploads.");
16240
+ printInfo(' Authenticate anytime: run "verity init" again, or "verity auth register".');
16241
+ };
16242
+ if (process.stdin.isTTY && process.stdout.isTTY) {
16243
+ console.log("");
16244
+ console.log(" Signing in is optional. What it does:");
16245
+ console.log(" - Confirms you have write access to this repository. The GitHub token");
16246
+ console.log(" is used once to verify that, then discarded \u2014 Verity never stores it.");
16247
+ console.log(" - It does NOT give Verity access to your code. Code checked by the gate");
16248
+ console.log(" is analyzed in memory and discarded \u2014 we never store your code.");
16249
+ console.log(" - It is required to store and access run history for this repo");
16250
+ console.log(" (past results, trends, and shareable reports).");
16251
+ console.log(" - Skip and Verity still works fully locally: the gate runs and shows");
16252
+ console.log(" findings, but nothing is uploaded.");
16253
+ console.log("");
16254
+ }
16255
+ const wantsAuth = await promptYes("Authenticate with GitHub now to upload results to Verity? [Y/skip] ");
16256
+ if (!wantsAuth) {
16257
+ printInfo("Skipped authentication.");
16258
+ localOnlyNote();
16259
+ return;
16260
+ }
16261
+ if (!remote) {
16262
+ printWarn("No git remote found \u2014 cannot authenticate yet.");
16263
+ localOnlyNote();
16264
+ return;
16265
+ }
16266
+ const projectName = parseRemote(remote)?.repo ?? (0, import_node_path18.basename)(process.cwd());
16267
+ printInfo("Authenticating with GitHub\u2026");
16268
+ const result = await registerProject({ projectName, remote, serviceUrl: DEFAULT_SERVICE_URL });
16269
+ if (result.ok) {
16270
+ printInfo(`Project registered: ${result.data.projectId} \u2713`);
16271
+ if (result.data.email) printInfo(` Authenticated as: ${result.data.email}`);
16272
+ } else {
16273
+ printWarn(`Authentication did not complete: ${result.error}`);
16274
+ localOnlyNote();
16275
+ }
16276
+ }
15944
16277
  function resolveDataDir() {
15945
16278
  const candidates = [
15946
16279
  (0, import_node_path18.join)(__dirname, "..", "data"),
@@ -16084,6 +16417,12 @@ function registerInitCommand(program2) {
16084
16417
  const globalVerityDir = (0, import_node_path18.join)(process.env.HOME ?? "", ".verity");
16085
16418
  await (0, import_promises13.mkdir)(globalVerityDir, { recursive: true });
16086
16419
  console.log("");
16420
+ try {
16421
+ await runOptionalAuth();
16422
+ } catch (err) {
16423
+ printWarn(`Authentication step skipped: ${err.message}`);
16424
+ }
16425
+ console.log("");
16087
16426
  printInfo("Verity initialized!");
16088
16427
  console.log("");
16089
16428
  console.log(" Installed:");
@@ -16099,6 +16438,7 @@ function registerInitCommand(program2) {
16099
16438
  console.log(" .verity/memory/ \u2014 knowledge base (8 domains, commit to git)");
16100
16439
  console.log("");
16101
16440
  console.log(" Next step: open this project in Claude Code and run /verity-setup");
16441
+ console.log(' (Not authenticated? Verity runs in local-only mode until you run "verity auth register".)');
16102
16442
  console.log("");
16103
16443
  });
16104
16444
  }
@@ -16827,7 +17167,7 @@ function registerTelemetryCommands(program2) {
16827
17167
  }
16828
17168
 
16829
17169
  // src/cli.ts
16830
- program.name("verity").description("CLI for Verity quality gate service").version("0.26.0").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
17170
+ program.name("verity").description("CLI for Verity quality gate service").version("0.27.0-experimental.1106a16").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
16831
17171
  registerAuthCommands(program);
16832
17172
  registerHooksCommands(program);
16833
17173
  registerIntentCommands(program);