@codacy/verity-cli 0.26.0 → 0.27.0-experimental.8f12b5a

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,462 @@ 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) {
11125
+ let installUrl = null;
11126
+ const maxAttempts = 3;
11127
+ for (let attempt = 1; ; attempt++) {
11128
+ const visible = await githubCanSeeRepo(owner, repo, token);
11129
+ if (!visible.ok) return { ok: true, data: void 0 };
11130
+ if (visible.data) return { ok: true, data: void 0 };
11131
+ if (installUrl === null) installUrl = githubAppInstallUrl(await githubAccountId(owner));
11132
+ if (!isInteractive() || attempt >= maxAttempts) {
11133
+ return {
11134
+ ok: false,
11135
+ error: `The Verity GitHub App can't see ${owner}/${repo}. Install it on "${owner}" and grant access to this repository, then re-run:
11136
+ ${installUrl}`
11137
+ };
11138
+ }
11139
+ printWarn(`The Verity GitHub App can't see ${owner}/${repo} yet.`);
11140
+ printInfo(`Install it on "${owner}" (grant access to this repo):`);
11141
+ printInfo(` ${installUrl}`);
11142
+ await waitForEnter("Press Enter once installed to retry\u2026 ");
11143
+ }
11144
+ }
11145
+ async function registerProject(opts) {
11146
+ const parsed = parseRemote(opts.remote);
11147
+ if (!parsed) {
11148
+ return { ok: false, error: `Could not parse git remote: ${opts.remote}` };
11149
+ }
11150
+ if (parsed.provider !== "github") {
11151
+ return { ok: false, error: `Provider '${parsed.provider}' is not supported yet \u2014 GitHub only for now.` };
11152
+ }
11153
+ const usingTokenOverride = Boolean(process.env.VERITY_PROVIDER_TOKEN);
11154
+ const providerAuth = await githubDeviceFlow();
11155
+ if (!providerAuth.ok) {
11156
+ return { ok: false, error: providerAuth.error };
11157
+ }
11158
+ const providerToken = providerAuth.data;
11159
+ if (!usingTokenOverride) {
11160
+ const installed = await ensureAppInstalled(parsed.owner, parsed.repo, providerToken);
11161
+ if (!installed.ok) {
11162
+ return { ok: false, error: installed.error };
11163
+ }
11164
+ }
11165
+ const result = await apiRequest({
11166
+ method: "POST",
11167
+ path: "/auth/register",
11168
+ serviceUrl: opts.serviceUrl,
11169
+ body: { project_name: opts.projectName, git_remote_url: opts.remote },
11170
+ extraHeaders: { "X-Provider-Token": providerToken },
11171
+ verbose: opts.verbose
11172
+ });
11173
+ if (!result.ok) {
11174
+ return { ok: false, error: result.error };
11175
+ }
11176
+ const { project_id, token, service_url, user } = result.data;
11177
+ try {
11178
+ await (0, import_promises3.mkdir)(VERITY_DIR, { recursive: true });
11179
+ await (0, import_promises3.writeFile)(
11180
+ CREDENTIALS_FILE,
11181
+ `token: ${token}
11182
+ service_url: ${service_url}
11183
+ `,
11184
+ { mode: 384 }
11185
+ );
11186
+ await (0, import_promises3.chmod)(CREDENTIALS_FILE, 384).catch(() => {
11187
+ });
11188
+ } catch (err) {
11189
+ return {
11190
+ ok: false,
11191
+ 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".`
11192
+ };
11193
+ }
11194
+ try {
11195
+ await (0, import_promises3.mkdir)((0, import_node_path3.dirname)(GLOBAL_CREDENTIALS_FILE), { recursive: true });
11196
+ await (0, import_promises3.appendFile)(
11197
+ GLOBAL_CREDENTIALS_FILE,
11198
+ `${opts.remote} token: ${token}
11199
+ `,
11200
+ { mode: 384 }
11201
+ );
11202
+ await (0, import_promises3.chmod)(GLOBAL_CREDENTIALS_FILE, 384).catch(() => {
11203
+ });
11204
+ } catch {
11205
+ }
11206
+ return { ok: true, data: { projectId: project_id, serviceUrl: service_url, email: user?.email } };
11207
+ }
11208
+
10743
11209
  // src/commands/auth.ts
10744
11210
  function registerAuthCommands(program2) {
10745
11211
  const auth = program2.command("auth").description("Manage project authentication");
@@ -10749,36 +11215,26 @@ function registerAuthCommands(program2) {
10749
11215
  let remote = opts.remote;
10750
11216
  if (!remote) {
10751
11217
  try {
10752
- remote = (0, import_node_child_process3.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
11218
+ remote = (0, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
10753
11219
  } catch {
10754
11220
  printError("No git remote found. Use --remote to specify one.");
10755
11221
  process.exit(1);
10756
11222
  }
10757
11223
  }
10758
- const result = await apiRequest({
10759
- method: "POST",
10760
- path: "/auth/register",
11224
+ const result = await registerProject({
11225
+ projectName: opts.project,
11226
+ remote,
10761
11227
  serviceUrl,
10762
- body: { project_name: opts.project, git_remote_url: remote },
10763
11228
  verbose: globals.verbose
10764
11229
  });
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 {
11230
+ if (!result.ok) {
11231
+ printError(result.error);
11232
+ process.exit(1);
10779
11233
  }
10780
- printInfo(`Project registered: ${project_id}`);
10781
- printJson({ project_id, service_url });
11234
+ const { projectId, serviceUrl: resolvedUrl, email } = result.data;
11235
+ printInfo(`Project registered: ${projectId}`);
11236
+ if (email) printInfo(`Authenticated as: ${email}`);
11237
+ printJson({ project_id: projectId, service_url: resolvedUrl });
10782
11238
  });
10783
11239
  auth.command("verify").description("Verify the current token is valid").action(async () => {
10784
11240
  const globals = program2.opts();
@@ -10811,7 +11267,7 @@ service_url: ${service_url}
10811
11267
  let remote = opts.remote;
10812
11268
  if (!remote) {
10813
11269
  try {
10814
- remote = (0, import_node_child_process3.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
11270
+ remote = (0, import_node_child_process4.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
10815
11271
  } catch {
10816
11272
  printError("No git remote found. Use --remote to specify one.");
10817
11273
  process.exit(1);
@@ -10839,11 +11295,11 @@ service_url: ${service_url}
10839
11295
 
10840
11296
  // src/lib/hooks.ts
10841
11297
  var import_promises5 = require("node:fs/promises");
10842
- var import_node_path4 = require("node:path");
11298
+ var import_node_path5 = require("node:path");
10843
11299
 
10844
11300
  // src/lib/json-file.ts
10845
11301
  var import_promises4 = require("node:fs/promises");
10846
- var import_node_path3 = require("node:path");
11302
+ var import_node_path4 = require("node:path");
10847
11303
  function jsonSemanticEqual(a, b) {
10848
11304
  if (a === b) return true;
10849
11305
  if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) {
@@ -10889,7 +11345,7 @@ async function writeJsonFilePreservingStyle(file, value) {
10889
11345
  const indent = currentRaw !== null ? detectJsonIndent(currentRaw) : 2;
10890
11346
  const next = JSON.stringify(value, null, indent) + "\n";
10891
11347
  if (next === currentRaw) return false;
10892
- await (0, import_promises4.mkdir)((0, import_node_path3.dirname)(file), { recursive: true });
11348
+ await (0, import_promises4.mkdir)((0, import_node_path4.dirname)(file), { recursive: true });
10893
11349
  await (0, import_promises4.writeFile)(file, next);
10894
11350
  return true;
10895
11351
  }
@@ -11050,13 +11506,13 @@ async function writeSettings(settings) {
11050
11506
  }
11051
11507
  async function readSettingsAt(root) {
11052
11508
  try {
11053
- return JSON.parse(await (0, import_promises5.readFile)((0, import_node_path4.join)(root, CLAUDE_SETTINGS_FILE), "utf-8"));
11509
+ return JSON.parse(await (0, import_promises5.readFile)((0, import_node_path5.join)(root, CLAUDE_SETTINGS_FILE), "utf-8"));
11054
11510
  } catch {
11055
11511
  return {};
11056
11512
  }
11057
11513
  }
11058
11514
  async function writeSettingsAt(root, settings) {
11059
- await writeJsonFilePreservingStyle((0, import_node_path4.join)(root, CLAUDE_SETTINGS_FILE), settings);
11515
+ await writeJsonFilePreservingStyle((0, import_node_path5.join)(root, CLAUDE_SETTINGS_FILE), settings);
11060
11516
  }
11061
11517
  async function hasLegacyHooksAt(root) {
11062
11518
  const settings = await readSettingsAt(root);
@@ -11296,8 +11752,8 @@ var import_node_crypto3 = require("node:crypto");
11296
11752
 
11297
11753
  // src/lib/conversation-buffer.ts
11298
11754
  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");
11755
+ var import_node_fs3 = require("node:fs");
11756
+ var import_node_child_process5 = require("node:child_process");
11301
11757
  var import_node_crypto = require("node:crypto");
11302
11758
  function stripImageReferences(text) {
11303
11759
  return text.replace(/\[Image #\d+\]/g, "[screenshot \u2014 not available for review]");
@@ -11332,7 +11788,7 @@ async function appendToConversationBuffer(prompt, sessionId) {
11332
11788
  }
11333
11789
  async function readAndClearConversationBuffer(currentSessionId) {
11334
11790
  try {
11335
- if ((0, import_node_fs2.existsSync)(CONVERSATION_BUFFER_FILE)) {
11791
+ if ((0, import_node_fs3.existsSync)(CONVERSATION_BUFFER_FILE)) {
11336
11792
  const entries = await readBufferEntries();
11337
11793
  let mine = entries;
11338
11794
  let others = [];
@@ -11356,7 +11812,7 @@ async function readAndClearConversationBuffer(currentSessionId) {
11356
11812
  };
11357
11813
  }
11358
11814
  }
11359
- if ((0, import_node_fs2.existsSync)(INTENT_FILE)) {
11815
+ if ((0, import_node_fs3.existsSync)(INTENT_FILE)) {
11360
11816
  try {
11361
11817
  const content = await (0, import_promises6.readFile)(INTENT_FILE, "utf-8");
11362
11818
  await (0, import_promises6.unlink)(INTENT_FILE).catch(() => {
@@ -11400,7 +11856,7 @@ async function readBufferEntries() {
11400
11856
  }
11401
11857
  function getRecentCommitMessages() {
11402
11858
  try {
11403
- const output = (0, import_node_child_process4.execSync)(
11859
+ const output = (0, import_node_child_process5.execSync)(
11404
11860
  'git log --since="30 minutes ago" --format="%s" -5',
11405
11861
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
11406
11862
  ).trim();
@@ -11413,8 +11869,8 @@ function getRecentCommitMessages() {
11413
11869
 
11414
11870
  // src/lib/task-context-buffer.ts
11415
11871
  var import_promises7 = require("node:fs/promises");
11416
- var import_node_fs3 = require("node:fs");
11417
- var import_node_path5 = require("node:path");
11872
+ var import_node_fs4 = require("node:fs");
11873
+ var import_node_path6 = require("node:path");
11418
11874
  var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
11419
11875
  var MAX_BUFFER_BYTES = 500 * 1024;
11420
11876
  var MAX_PROMPT_CHARS = 2e3;
@@ -11453,7 +11909,7 @@ async function appendResponseToTaskBuffer(taskId, assistantResponse, actionSumma
11453
11909
  }
11454
11910
  async function readTaskContextBuffer(taskId) {
11455
11911
  const filePath = bufferPath(taskId);
11456
- if (!(0, import_node_fs3.existsSync)(filePath)) return null;
11912
+ if (!(0, import_node_fs4.existsSync)(filePath)) return null;
11457
11913
  try {
11458
11914
  const content = await (0, import_promises7.readFile)(filePath, "utf-8");
11459
11915
  if (!content.trim()) return null;
@@ -11487,12 +11943,12 @@ async function readTaskContextBuffer(taskId) {
11487
11943
  }
11488
11944
  async function cleanupTaskContextBuffers() {
11489
11945
  try {
11490
- if (!(0, import_node_fs3.existsSync)(TASK_CONTEXT_DIR)) return;
11946
+ if (!(0, import_node_fs4.existsSync)(TASK_CONTEXT_DIR)) return;
11491
11947
  const files = await (0, import_promises7.readdir)(TASK_CONTEXT_DIR);
11492
11948
  const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
11493
11949
  for (const file of files) {
11494
11950
  if (!file.endsWith(".jsonl")) continue;
11495
- const filePath = (0, import_node_path5.join)(TASK_CONTEXT_DIR, file);
11951
+ const filePath = (0, import_node_path6.join)(TASK_CONTEXT_DIR, file);
11496
11952
  try {
11497
11953
  const stats = await (0, import_promises7.stat)(filePath);
11498
11954
  if (stats.mtimeMs < cutoffMs) {
@@ -11506,13 +11962,13 @@ async function cleanupTaskContextBuffers() {
11506
11962
  }
11507
11963
  function bufferPath(taskId) {
11508
11964
  const safe = taskId.replace(/[^a-zA-Z0-9_-]/g, "");
11509
- return (0, import_node_path5.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
11965
+ return (0, import_node_path6.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
11510
11966
  }
11511
11967
  async function appendEntry(taskId, entry) {
11512
11968
  try {
11513
11969
  await (0, import_promises7.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
11514
11970
  const filePath = bufferPath(taskId);
11515
- if ((0, import_node_fs3.existsSync)(filePath)) {
11971
+ if ((0, import_node_fs4.existsSync)(filePath)) {
11516
11972
  const stats = await (0, import_promises7.stat)(filePath);
11517
11973
  if (stats.size >= MAX_BUFFER_BYTES) {
11518
11974
  const content = await (0, import_promises7.readFile)(filePath, "utf-8");
@@ -11523,7 +11979,7 @@ async function appendEntry(taskId, entry) {
11523
11979
  }
11524
11980
  }
11525
11981
  const line = JSON.stringify(entry) + "\n";
11526
- const existing = (0, import_node_fs3.existsSync)(filePath) ? await (0, import_promises7.readFile)(filePath, "utf-8") : "";
11982
+ const existing = (0, import_node_fs4.existsSync)(filePath) ? await (0, import_promises7.readFile)(filePath, "utf-8") : "";
11527
11983
  await (0, import_promises7.writeFile)(filePath, existing + line);
11528
11984
  } catch {
11529
11985
  }
@@ -11531,8 +11987,8 @@ async function appendEntry(taskId, entry) {
11531
11987
 
11532
11988
  // src/lib/memory-retrieval.ts
11533
11989
  var import_promises8 = require("node:fs/promises");
11534
- var import_node_fs4 = require("node:fs");
11535
- var import_node_path6 = require("node:path");
11990
+ var import_node_fs5 = require("node:fs");
11991
+ var import_node_path7 = require("node:path");
11536
11992
  var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
11537
11993
  var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
11538
11994
  var DEFAULT_BUDGET_TOKENS = 2e3;
@@ -11625,19 +12081,19 @@ function parseFrontmatter(content) {
11625
12081
  return { fm, body: match[2].trim() };
11626
12082
  }
11627
12083
  async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = DEFAULT_BUDGET_TOKENS) {
11628
- if (!(0, import_node_fs4.existsSync)(memoryDir())) return null;
12084
+ if (!(0, import_node_fs5.existsSync)(memoryDir())) return null;
11629
12085
  const budget = Math.min(budgetTokens, MAX_BUDGET_TOKENS);
11630
12086
  const promptTokens = tokenize(promptText);
11631
12087
  const nodes = [];
11632
12088
  for (const domain of DOMAINS) {
11633
- const domainDir = (0, import_node_path6.join)(memoryDir(), domain);
11634
- if (!(0, import_node_fs4.existsSync)(domainDir)) continue;
12089
+ const domainDir = (0, import_node_path7.join)(memoryDir(), domain);
12090
+ if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
11635
12091
  try {
11636
12092
  const files = await (0, import_promises8.readdir)(domainDir);
11637
12093
  for (const file of files) {
11638
12094
  if (!file.endsWith(".md")) continue;
11639
12095
  try {
11640
- const content = await (0, import_promises8.readFile)((0, import_node_path6.join)(domainDir, file), "utf-8");
12096
+ const content = await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8");
11641
12097
  const { fm, body } = parseFrontmatter(content);
11642
12098
  if (fm.status && fm.status !== "active") continue;
11643
12099
  nodes.push({
@@ -11696,8 +12152,8 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
11696
12152
 
11697
12153
  // src/lib/memory-sync.ts
11698
12154
  var import_promises9 = require("node:fs/promises");
11699
- var import_node_fs5 = require("node:fs");
11700
- var import_node_path7 = require("node:path");
12155
+ var import_node_fs6 = require("node:fs");
12156
+ var import_node_path8 = require("node:path");
11701
12157
  var import_node_crypto2 = require("node:crypto");
11702
12158
 
11703
12159
  // src/lib/glob-match.ts
@@ -11768,32 +12224,32 @@ var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
11768
12224
  async function ensureMemoryDir() {
11769
12225
  await (0, import_promises9.mkdir)(memoryDir2(), { recursive: true });
11770
12226
  for (const domain of DOMAINS2) {
11771
- await (0, import_promises9.mkdir)((0, import_node_path7.join)(memoryDir2(), domain), { recursive: true });
12227
+ await (0, import_promises9.mkdir)((0, import_node_path8.join)(memoryDir2(), domain), { recursive: true });
11772
12228
  }
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);
12229
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path8.join)(memoryDir2(), "SCHEMA.md"))) {
12230
+ await (0, import_promises9.writeFile)((0, import_node_path8.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
11775
12231
  }
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");
12232
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path8.join)(memoryDir2(), "index.md"))) {
12233
+ 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
12234
  }
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");
12235
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path8.join)(memoryDir2(), "log.md"))) {
12236
+ await (0, import_promises9.writeFile)((0, import_node_path8.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
11781
12237
  }
11782
12238
  }
11783
12239
  async function buildManifest() {
11784
- if (!(0, import_node_fs5.existsSync)(memoryDir2())) {
12240
+ if (!(0, import_node_fs6.existsSync)(memoryDir2())) {
11785
12241
  return { schema_version: 1, nodes: [], index_hash: null, log_length: 0 };
11786
12242
  }
11787
12243
  const nodes = [];
11788
12244
  for (const domain of DOMAINS2) {
11789
- const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
11790
- if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
12245
+ const domainDir = (0, import_node_path8.join)(memoryDir2(), domain);
12246
+ if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
11791
12247
  try {
11792
12248
  const files = await (0, import_promises9.readdir)(domainDir);
11793
12249
  for (const file of files) {
11794
12250
  if (!file.endsWith(".md")) continue;
11795
12251
  const filePath = `${domain}/${file}`;
11796
- const fullPath = (0, import_node_path7.join)(memoryDir2(), filePath);
12252
+ const fullPath = (0, import_node_path8.join)(memoryDir2(), filePath);
11797
12253
  try {
11798
12254
  const content = await (0, import_promises9.readFile)(fullPath, "utf-8");
11799
12255
  const hash = (0, import_node_crypto2.createHash)("sha256").update(content).digest("hex").slice(0, 16);
@@ -11806,13 +12262,13 @@ async function buildManifest() {
11806
12262
  }
11807
12263
  let indexHash = null;
11808
12264
  try {
11809
- const indexContent = await (0, import_promises9.readFile)((0, import_node_path7.join)(memoryDir2(), "index.md"), "utf-8");
12265
+ const indexContent = await (0, import_promises9.readFile)((0, import_node_path8.join)(memoryDir2(), "index.md"), "utf-8");
11810
12266
  indexHash = `sha256:${(0, import_node_crypto2.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
11811
12267
  } catch {
11812
12268
  }
11813
12269
  let logLength = 0;
11814
12270
  try {
11815
- const logContent = await (0, import_promises9.readFile)((0, import_node_path7.join)(memoryDir2(), "log.md"), "utf-8");
12271
+ const logContent = await (0, import_promises9.readFile)((0, import_node_path8.join)(memoryDir2(), "log.md"), "utf-8");
11816
12272
  logLength = logContent.split("\n").length;
11817
12273
  } catch {
11818
12274
  }
@@ -11823,15 +12279,15 @@ function hashContent(content) {
11823
12279
  }
11824
12280
  async function readOnDiskNodes() {
11825
12281
  const out = /* @__PURE__ */ new Map();
11826
- if (!(0, import_node_fs5.existsSync)(memoryDir2())) return out;
12282
+ if (!(0, import_node_fs6.existsSync)(memoryDir2())) return out;
11827
12283
  for (const domain of DOMAINS2) {
11828
- const domainDir = (0, import_node_path7.join)(memoryDir2(), domain);
11829
- if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
12284
+ const domainDir = (0, import_node_path8.join)(memoryDir2(), domain);
12285
+ if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
11830
12286
  try {
11831
12287
  for (const file of await (0, import_promises9.readdir)(domainDir)) {
11832
12288
  if (!file.endsWith(".md")) continue;
11833
12289
  try {
11834
- out.set(`${domain}/${file}`, hashContent(await (0, import_promises9.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8")));
12290
+ out.set(`${domain}/${file}`, hashContent(await (0, import_promises9.readFile)((0, import_node_path8.join)(domainDir, file), "utf-8")));
11835
12291
  } catch {
11836
12292
  }
11837
12293
  }
@@ -11877,8 +12333,8 @@ async function computeEditedNodeUploads() {
11877
12333
  const uploads = [];
11878
12334
  for (const [path, prevHash] of prev) {
11879
12335
  if (prevHash == null) continue;
11880
- const full = (0, import_node_path7.join)(memoryDir2(), path);
11881
- if (!(0, import_node_fs5.existsSync)(full)) continue;
12336
+ const full = (0, import_node_path8.join)(memoryDir2(), path);
12337
+ if (!(0, import_node_fs6.existsSync)(full)) continue;
11882
12338
  let content;
11883
12339
  try {
11884
12340
  content = await (0, import_promises9.readFile)(full, "utf-8");
@@ -11914,15 +12370,15 @@ async function applyMemoryWrites(writes, opts = {}) {
11914
12370
  const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
11915
12371
  for (const n of notes) logLines.push(` - ${n}`);
11916
12372
  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");
12373
+ 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";
12374
+ await (0, import_promises9.writeFile)((0, import_node_path8.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
11919
12375
  } catch {
11920
12376
  }
11921
12377
  await recordSyncedNodePaths();
11922
12378
  return count;
11923
12379
  }
11924
12380
  async function applyOneWrite(write, treePaths) {
11925
- const fullPath = (0, import_node_path7.join)(memoryDir2(), write.path);
12381
+ const fullPath = (0, import_node_path8.join)(memoryDir2(), write.path);
11926
12382
  const notes = [];
11927
12383
  let content = write.content;
11928
12384
  if (treePaths && treePaths.length > 0) {
@@ -11932,7 +12388,7 @@ async function applyOneWrite(write, treePaths) {
11932
12388
  notes.push(`${write.path}: dropped unmatched file_globs [${grounded.dropped.join(", ")}]`);
11933
12389
  }
11934
12390
  }
11935
- if ((0, import_node_fs5.existsSync)(fullPath)) {
12391
+ if ((0, import_node_fs6.existsSync)(fullPath)) {
11936
12392
  let existing = "";
11937
12393
  try {
11938
12394
  existing = await (0, import_promises9.readFile)(fullPath, "utf-8");
@@ -11944,7 +12400,7 @@ async function applyOneWrite(write, treePaths) {
11944
12400
  return { written: false, notes };
11945
12401
  }
11946
12402
  }
11947
- await (0, import_promises9.mkdir)((0, import_node_path7.dirname)(fullPath), { recursive: true });
12403
+ await (0, import_promises9.mkdir)((0, import_node_path8.dirname)(fullPath), { recursive: true });
11948
12404
  await (0, import_promises9.writeFile)(fullPath, content);
11949
12405
  return { written: true, notes };
11950
12406
  }
@@ -11985,8 +12441,8 @@ async function regenerateIndex() {
11985
12441
  ];
11986
12442
  let totalNodes = 0;
11987
12443
  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;
12444
+ const domainDir = (0, import_node_path8.join)(memoryDir2(), domain);
12445
+ if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
11990
12446
  try {
11991
12447
  const files = await (0, import_promises9.readdir)(domainDir);
11992
12448
  const mdFiles = files.filter((f) => f.endsWith(".md"));
@@ -11996,7 +12452,7 @@ async function regenerateIndex() {
11996
12452
  for (const file of mdFiles.sort()) {
11997
12453
  const slug = file.replace(/\.md$/, "");
11998
12454
  try {
11999
- const content = await (0, import_promises9.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8");
12455
+ const content = await (0, import_promises9.readFile)((0, import_node_path8.join)(domainDir, file), "utf-8");
12000
12456
  const title = pickFrontmatter(content, "title") ?? slug;
12001
12457
  const kind = pickFrontmatter(content, "kind") ?? "-";
12002
12458
  const confidence = pickFrontmatter(content, "confidence");
@@ -12020,7 +12476,7 @@ async function regenerateIndex() {
12020
12476
  lines.push("No nodes yet. Run an analysis to start building the knowledge graph.");
12021
12477
  }
12022
12478
  const next = lines.join("\n") + "\n";
12023
- const indexPath = (0, import_node_path7.join)(memoryDir2(), "index.md");
12479
+ const indexPath = (0, import_node_path8.join)(memoryDir2(), "index.md");
12024
12480
  let existing = null;
12025
12481
  try {
12026
12482
  existing = await (0, import_promises9.readFile)(indexPath, "utf-8");
@@ -12102,9 +12558,9 @@ function hasLegacyMemoryBlock(text) {
12102
12558
  return findMarker(text, LEGACY_MD_START) !== -1;
12103
12559
  }
12104
12560
  async function ensureClaudeMdPointer(cwd = repoRoot()) {
12105
- const claudeMdPath = (0, import_node_path7.join)(cwd, "CLAUDE.md");
12561
+ const claudeMdPath = (0, import_node_path8.join)(cwd, "CLAUDE.md");
12106
12562
  let existing = "";
12107
- if ((0, import_node_fs5.existsSync)(claudeMdPath)) {
12563
+ if ((0, import_node_fs6.existsSync)(claudeMdPath)) {
12108
12564
  existing = await (0, import_promises9.readFile)(claudeMdPath, "utf-8");
12109
12565
  }
12110
12566
  let startTag = CLAUDE_MD_START;
@@ -12234,7 +12690,7 @@ Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
12234
12690
  `;
12235
12691
 
12236
12692
  // src/commands/intent.ts
12237
- var import_node_fs6 = require("node:fs");
12693
+ var import_node_fs7 = require("node:fs");
12238
12694
  function registerIntentCommands(program2) {
12239
12695
  const intent = program2.command("intent").description("Manage intent capture");
12240
12696
  intent.command("capture").description("Capture user intent from stdin (used by UserPromptSubmit hook)").action(async () => {
@@ -12243,7 +12699,7 @@ function registerIntentCommands(program2) {
12243
12699
  process.chdir(repoRoot());
12244
12700
  } catch {
12245
12701
  }
12246
- if (!(0, import_node_fs6.existsSync)(VERITY_DIR)) {
12702
+ if (!(0, import_node_fs7.existsSync)(VERITY_DIR)) {
12247
12703
  process.exit(0);
12248
12704
  }
12249
12705
  const chunks = [];
@@ -12822,215 +13278,6 @@ async function sendGeneralFeedback(message, opts, globals) {
12822
13278
  var import_node_fs19 = require("node:fs");
12823
13279
  var import_node_path15 = require("node:path");
12824
13280
 
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
13281
  // src/lib/files.ts
13035
13282
  var import_node_fs8 = require("node:fs");
13036
13283
  var import_node_path9 = require("node:path");
@@ -14638,6 +14885,27 @@ function passAndExit(reason) {
14638
14885
  printJsonCompact({ gate_decision: "PASS", systemMessage: `Verity: ${reason}` });
14639
14886
  process.exit(0);
14640
14887
  }
14888
+ var EMPTY_STATIC = {
14889
+ tool: "@codacy/analysis-cli",
14890
+ findings: [],
14891
+ summary: { total_findings: 0, by_severity: {}, tools_run: [] }
14892
+ };
14893
+ function runLocalStatic(analyzable, securityFiles, baseline, skipStatic) {
14894
+ if (skipStatic || !isCodacyAvailable()) return EMPTY_STATIC;
14895
+ let scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
14896
+ if (baseline) scannable = scannable.filter((f) => changedSinceBaseline(f, baseline));
14897
+ if (scannable.length === 0) return EMPTY_STATIC;
14898
+ return runCodacyAnalysis(scannable);
14899
+ }
14900
+ function localOnlyAndExit(staticResults) {
14901
+ printJsonCompact({
14902
+ gate_decision: "PASS",
14903
+ 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.",
14904
+ unauthenticated: true,
14905
+ static_results: staticResults
14906
+ });
14907
+ process.exit(0);
14908
+ }
14641
14909
  function registerAnalyzeCommand(program2) {
14642
14910
  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
14911
  const globals = program2.opts();
@@ -14688,12 +14956,9 @@ async function runAnalyze(opts, globals) {
14688
14956
  passAndExit("Reflection-prompt turn \u2014 skipping analysis");
14689
14957
  }
14690
14958
  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
14959
  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.");
14960
+ if (!tokenResult.ok || !urlResult.ok) {
14961
+ localOnlyAndExit(runLocalStatic(analyzable, securityFiles, baseline, !!opts.skipStatic));
14697
14962
  }
14698
14963
  const sessionIdForMemory = sessionId || process.env.CLAUDE_SESSION_ID || "";
14699
14964
  let contextFilePaths = [];
@@ -15267,13 +15532,14 @@ async function runReview(opts, globals) {
15267
15532
  }
15268
15533
  const codeDelta = collectCodeDelta(allFiles);
15269
15534
  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
15535
  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.");
15536
+ if (!tokenResult.ok || !urlResult.ok) {
15537
+ printJsonCompact({
15538
+ gate_decision: "PASS",
15539
+ 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.",
15540
+ unauthenticated: true,
15541
+ static_results: staticResults
15542
+ });
15277
15543
  process.exit(0);
15278
15544
  }
15279
15545
  let specs;
@@ -15662,6 +15928,7 @@ var import_node_fs24 = require("node:fs");
15662
15928
  var import_promises13 = require("node:fs/promises");
15663
15929
  var import_node_path18 = require("node:path");
15664
15930
  var import_node_child_process9 = require("node:child_process");
15931
+ var readline2 = __toESM(require("node:readline/promises"));
15665
15932
 
15666
15933
  // src/commands/migrate.ts
15667
15934
  var import_node_fs23 = require("node:fs");
@@ -15941,6 +16208,66 @@ function registerMigrateCommand(program2) {
15941
16208
  }
15942
16209
 
15943
16210
  // src/commands/init.ts
16211
+ async function promptYes(question) {
16212
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
16213
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
16214
+ try {
16215
+ const answer = (await rl.question(question)).trim().toLowerCase();
16216
+ return answer === "" || answer === "y" || answer === "yes";
16217
+ } finally {
16218
+ rl.close();
16219
+ }
16220
+ }
16221
+ async function runOptionalAuth() {
16222
+ const existing = await resolveToken();
16223
+ if (existing.ok) {
16224
+ printInfo("Already authenticated \u2014 results will upload to the Verity service. \u2713");
16225
+ return;
16226
+ }
16227
+ let remote = "";
16228
+ try {
16229
+ remote = (0, import_node_child_process9.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
16230
+ } catch {
16231
+ }
16232
+ const localOnlyNote = () => {
16233
+ printInfo("Verity runs in local-only mode: the gate still runs and shows static findings, but nothing uploads.");
16234
+ printInfo(' Authenticate anytime: run "verity init" again, or "verity auth register".');
16235
+ };
16236
+ if (process.stdin.isTTY && process.stdout.isTTY) {
16237
+ console.log("");
16238
+ console.log(" Signing in is optional. What it does:");
16239
+ console.log(" - Confirms you have write access to this repository. The GitHub token");
16240
+ console.log(" is used once to verify that, then discarded \u2014 Verity never stores it.");
16241
+ console.log(" - It does NOT give Verity access to your code. Code checked by the gate");
16242
+ console.log(" is analyzed in memory and discarded \u2014 we never store your code.");
16243
+ console.log(" - It is required to store and access run history for this repo");
16244
+ console.log(" (past results, trends, and shareable reports).");
16245
+ console.log(" - Skip and Verity still works fully locally: the gate runs and shows");
16246
+ console.log(" findings, but nothing is uploaded.");
16247
+ console.log("");
16248
+ }
16249
+ const wantsAuth = await promptYes("Authenticate with GitHub now to upload results to Verity? [Y/skip] ");
16250
+ if (!wantsAuth) {
16251
+ printInfo("Skipped authentication.");
16252
+ localOnlyNote();
16253
+ return;
16254
+ }
16255
+ if (!remote) {
16256
+ printWarn("No git remote found \u2014 cannot authenticate yet.");
16257
+ localOnlyNote();
16258
+ return;
16259
+ }
16260
+ const projectName = parseRemote(remote)?.repo ?? (0, import_node_path18.basename)(process.cwd());
16261
+ printInfo("Authenticating with GitHub\u2026");
16262
+ const result = await registerProject({ projectName, remote, serviceUrl: DEFAULT_SERVICE_URL });
16263
+ if (result.ok) {
16264
+ printInfo(`Project registered: ${result.data.projectId} \u2713`);
16265
+ if (result.data.email) printInfo(` Authenticated as: ${result.data.email}`);
16266
+ } else {
16267
+ printWarn(`Authentication did not complete: ${result.error}`);
16268
+ localOnlyNote();
16269
+ }
16270
+ }
15944
16271
  function resolveDataDir() {
15945
16272
  const candidates = [
15946
16273
  (0, import_node_path18.join)(__dirname, "..", "data"),
@@ -16084,6 +16411,12 @@ function registerInitCommand(program2) {
16084
16411
  const globalVerityDir = (0, import_node_path18.join)(process.env.HOME ?? "", ".verity");
16085
16412
  await (0, import_promises13.mkdir)(globalVerityDir, { recursive: true });
16086
16413
  console.log("");
16414
+ try {
16415
+ await runOptionalAuth();
16416
+ } catch (err) {
16417
+ printWarn(`Authentication step skipped: ${err.message}`);
16418
+ }
16419
+ console.log("");
16087
16420
  printInfo("Verity initialized!");
16088
16421
  console.log("");
16089
16422
  console.log(" Installed:");
@@ -16099,6 +16432,7 @@ function registerInitCommand(program2) {
16099
16432
  console.log(" .verity/memory/ \u2014 knowledge base (8 domains, commit to git)");
16100
16433
  console.log("");
16101
16434
  console.log(" Next step: open this project in Claude Code and run /verity-setup");
16435
+ console.log(' (Not authenticated? Verity runs in local-only mode until you run "verity auth register".)');
16102
16436
  console.log("");
16103
16437
  });
16104
16438
  }
@@ -16827,7 +17161,7 @@ function registerTelemetryCommands(program2) {
16827
17161
  }
16828
17162
 
16829
17163
  // 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");
17164
+ program.name("verity").description("CLI for Verity quality gate service").version("0.27.0-experimental.8f12b5a").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
16831
17165
  registerAuthCommands(program);
16832
17166
  registerHooksCommands(program);
16833
17167
  registerIntentCommands(program);