@codacy/verity-cli 0.27.1 → 0.27.2-experimental.5fb996a

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.
Files changed (2) hide show
  1. package/bin/verity.js +454 -261
  2. package/package.json +1 -1
package/bin/verity.js CHANGED
@@ -10473,7 +10473,7 @@ var SECURITY_PATTERNS = [
10473
10473
  /Dockerfile/
10474
10474
  ];
10475
10475
  var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
10476
- var DEFAULT_SERVICE_URL = "".length > 0 ? "" : PROD_SERVICE_URL;
10476
+ var DEFAULT_SERVICE_URL = "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1".length > 0 ? "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1" : PROD_SERVICE_URL;
10477
10477
  var GITHUB_CLIENT_ID = "Iv23li88HxAi3ZrbYzWh";
10478
10478
  var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
10479
10479
  var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
@@ -10483,91 +10483,6 @@ function githubAppInstallUrl(accountId) {
10483
10483
  return accountId != null ? `https://github.com/apps/${GITHUB_APP_SLUG}/installations/new/permissions?target_id=${accountId}` : GITHUB_APP_INSTALL_URL;
10484
10484
  }
10485
10485
 
10486
- // src/lib/auth.ts
10487
- async function resolveToken(flagToken) {
10488
- if (flagToken) {
10489
- return { ok: true, data: { token: flagToken, source: "flag" } };
10490
- }
10491
- const envToken = process.env.VERITY_TOKEN;
10492
- if (envToken) {
10493
- return { ok: true, data: { token: envToken, source: "env" } };
10494
- }
10495
- try {
10496
- const content = await (0, import_promises.readFile)(projectPath(CREDENTIALS_FILE), "utf-8");
10497
- const match = content.match(/token:\s*((?:gate_|verity_)[a-f0-9]+)/);
10498
- if (match) {
10499
- return { ok: true, data: { token: match[1], source: "local" } };
10500
- }
10501
- } catch {
10502
- }
10503
- const globalCredentials = `${process.env.HOME}/.verity/credentials`;
10504
- try {
10505
- const content = await (0, import_promises.readFile)(globalCredentials, "utf-8");
10506
- let remote = "";
10507
- try {
10508
- remote = (0, import_node_child_process2.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
10509
- } catch {
10510
- }
10511
- if (remote) {
10512
- const remoteLine = content.split("\n").find((l) => l.includes(remote));
10513
- if (remoteLine) {
10514
- const match = remoteLine.match(/token:\s*((?:gate_|verity_)[a-f0-9]+)/);
10515
- if (match) {
10516
- return { ok: true, data: { token: match[1], source: "global" } };
10517
- }
10518
- }
10519
- }
10520
- const plainMatch = content.match(/^token:\s*((?:gate_|verity_)[a-f0-9]+)/m);
10521
- if (plainMatch) {
10522
- return { ok: true, data: { token: plainMatch[1], source: "global" } };
10523
- }
10524
- } catch {
10525
- }
10526
- return { ok: false, error: "No Verity token found. Run /verity-setup to configure." };
10527
- }
10528
-
10529
- // src/lib/service-url.ts
10530
- var import_promises2 = require("node:fs/promises");
10531
- async function resolveServiceUrl(flagUrl) {
10532
- if (flagUrl) {
10533
- return { ok: true, data: flagUrl };
10534
- }
10535
- const envUrl = process.env.VERITY_SERVICE_URL;
10536
- if (envUrl) {
10537
- return { ok: true, data: envUrl };
10538
- }
10539
- try {
10540
- const creds = await (0, import_promises2.readFile)(projectPath(CREDENTIALS_FILE), "utf-8");
10541
- const match = creds.match(/service_url:\s*(https?:\/\/[^\s]+)/);
10542
- if (match) {
10543
- return { ok: true, data: match[1] };
10544
- }
10545
- } catch {
10546
- }
10547
- try {
10548
- const content = await (0, import_promises2.readFile)(projectPath(VERITY_MD_FILE), "utf-8");
10549
- const boldMatch = content.match(/\*\*url\*\*/i);
10550
- if (boldMatch) {
10551
- const lineMatch = content.split("\n").find((l) => /\*\*url\*\*/i.test(l));
10552
- if (lineMatch) {
10553
- const urlMatch = lineMatch.match(/https:\/\/[^\s]+/);
10554
- if (urlMatch) {
10555
- return { ok: true, data: urlMatch[0] };
10556
- }
10557
- }
10558
- }
10559
- const plainLine = content.split("\n").find((l) => /(?:url|service)\s*:/i.test(l));
10560
- if (plainLine) {
10561
- const urlMatch = plainLine.match(/https:\/\/[^\s]+/);
10562
- if (urlMatch) {
10563
- return { ok: true, data: urlMatch[0] };
10564
- }
10565
- }
10566
- } catch {
10567
- }
10568
- return { ok: false, error: "No Verity service URL found. Run /verity-setup to configure." };
10569
- }
10570
-
10571
10486
  // src/lib/output.ts
10572
10487
  var RED = "\x1B[0;31m";
10573
10488
  var YELLOW = "\x1B[1;33m";
@@ -10750,6 +10665,108 @@ function analyzeRequest(options) {
10750
10665
  });
10751
10666
  }
10752
10667
 
10668
+ // src/lib/auth.ts
10669
+ function parseIdentity(content) {
10670
+ const idMatch = content.match(/^user_id:\s*(\d+)/m);
10671
+ const emailMatch = content.match(/^email:\s*(\S+)/m);
10672
+ return {
10673
+ userId: idMatch ? Number(idMatch[1]) : void 0,
10674
+ email: emailMatch ? emailMatch[1] : void 0
10675
+ };
10676
+ }
10677
+ async function resolveToken(flagToken) {
10678
+ if (flagToken) {
10679
+ return { ok: true, data: { token: flagToken, source: "flag" } };
10680
+ }
10681
+ const envToken = process.env.VERITY_TOKEN;
10682
+ if (envToken) {
10683
+ return { ok: true, data: { token: envToken, source: "env" } };
10684
+ }
10685
+ try {
10686
+ const content = await (0, import_promises.readFile)(projectPath(CREDENTIALS_FILE), "utf-8");
10687
+ const match = content.match(/token:\s*((?:gate_|verity_)[a-f0-9]+)/);
10688
+ if (match) {
10689
+ return { ok: true, data: { token: match[1], source: "local", ...parseIdentity(content) } };
10690
+ }
10691
+ } catch {
10692
+ }
10693
+ const globalCredentials = `${process.env.HOME}/.verity/credentials`;
10694
+ try {
10695
+ const content = await (0, import_promises.readFile)(globalCredentials, "utf-8");
10696
+ let remote = "";
10697
+ try {
10698
+ remote = (0, import_node_child_process2.execSync)("git remote get-url origin", { encoding: "utf-8" }).trim();
10699
+ } catch {
10700
+ }
10701
+ if (remote) {
10702
+ const remoteLine = content.split("\n").find((l) => l.includes(remote));
10703
+ if (remoteLine) {
10704
+ const match = remoteLine.match(/token:\s*((?:gate_|verity_)[a-f0-9]+)/);
10705
+ if (match) {
10706
+ return { ok: true, data: { token: match[1], source: "global" } };
10707
+ }
10708
+ }
10709
+ }
10710
+ const plainMatch = content.match(/^token:\s*((?:gate_|verity_)[a-f0-9]+)/m);
10711
+ if (plainMatch) {
10712
+ return { ok: true, data: { token: plainMatch[1], source: "global" } };
10713
+ }
10714
+ } catch {
10715
+ }
10716
+ return { ok: false, error: "No Verity token found. Run /verity-setup to configure." };
10717
+ }
10718
+ async function whoami(token, serviceUrl, verbose) {
10719
+ return apiRequest({
10720
+ method: "GET",
10721
+ path: "/auth/whoami",
10722
+ serviceUrl,
10723
+ token,
10724
+ verbose
10725
+ });
10726
+ }
10727
+
10728
+ // src/lib/service-url.ts
10729
+ var import_promises2 = require("node:fs/promises");
10730
+ async function resolveServiceUrl(flagUrl) {
10731
+ if (flagUrl) {
10732
+ return { ok: true, data: flagUrl };
10733
+ }
10734
+ const envUrl = process.env.VERITY_SERVICE_URL;
10735
+ if (envUrl) {
10736
+ return { ok: true, data: envUrl };
10737
+ }
10738
+ try {
10739
+ const creds = await (0, import_promises2.readFile)(projectPath(CREDENTIALS_FILE), "utf-8");
10740
+ const match = creds.match(/service_url:\s*(https?:\/\/[^\s]+)/);
10741
+ if (match) {
10742
+ return { ok: true, data: match[1] };
10743
+ }
10744
+ } catch {
10745
+ }
10746
+ try {
10747
+ const content = await (0, import_promises2.readFile)(projectPath(VERITY_MD_FILE), "utf-8");
10748
+ const boldMatch = content.match(/\*\*url\*\*/i);
10749
+ if (boldMatch) {
10750
+ const lineMatch = content.split("\n").find((l) => /\*\*url\*\*/i.test(l));
10751
+ if (lineMatch) {
10752
+ const urlMatch = lineMatch.match(/https:\/\/[^\s]+/);
10753
+ if (urlMatch) {
10754
+ return { ok: true, data: urlMatch[0] };
10755
+ }
10756
+ }
10757
+ }
10758
+ const plainLine = content.split("\n").find((l) => /(?:url|service)\s*:/i.test(l));
10759
+ if (plainLine) {
10760
+ const urlMatch = plainLine.match(/https:\/\/[^\s]+/);
10761
+ if (urlMatch) {
10762
+ return { ok: true, data: urlMatch[0] };
10763
+ }
10764
+ }
10765
+ } catch {
10766
+ }
10767
+ return { ok: false, error: "No Verity service URL found. Run /verity-setup to configure." };
10768
+ }
10769
+
10753
10770
  // src/lib/register.ts
10754
10771
  var import_promises3 = require("node:fs/promises");
10755
10772
  var import_node_path3 = require("node:path");
@@ -11047,8 +11064,8 @@ function filterReviewable(files) {
11047
11064
  const ext = (0, import_node_path2.extname)(f).slice(1);
11048
11065
  if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
11049
11066
  if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
11050
- const basename3 = f.split("/").pop() ?? "";
11051
- if (REVIEWABLE_FILENAMES.has(basename3)) return true;
11067
+ const basename4 = f.split("/").pop() ?? "";
11068
+ if (REVIEWABLE_FILENAMES.has(basename4)) return true;
11052
11069
  if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
11053
11070
  return false;
11054
11071
  });
@@ -11174,13 +11191,18 @@ async function registerProject(opts) {
11174
11191
  return { ok: false, error: result.error };
11175
11192
  }
11176
11193
  const { project_id, token, service_url, user } = result.data;
11194
+ const userId = result.data.user_id ?? user?.id;
11195
+ const email = user?.email;
11196
+ const identityLines = (userId != null ? `user_id: ${userId}
11197
+ ` : "") + (email ? `email: ${email}
11198
+ ` : "");
11177
11199
  try {
11178
11200
  await (0, import_promises3.mkdir)(VERITY_DIR, { recursive: true });
11179
11201
  await (0, import_promises3.writeFile)(
11180
11202
  CREDENTIALS_FILE,
11181
11203
  `token: ${token}
11182
11204
  service_url: ${service_url}
11183
- `,
11205
+ ${identityLines}`,
11184
11206
  { mode: 384 }
11185
11207
  );
11186
11208
  await (0, import_promises3.chmod)(CREDENTIALS_FILE, 384).catch(() => {
@@ -11203,7 +11225,7 @@ service_url: ${service_url}
11203
11225
  });
11204
11226
  } catch {
11205
11227
  }
11206
- return { ok: true, data: { projectId: project_id, serviceUrl: service_url, email: user?.email } };
11228
+ return { ok: true, data: { projectId: project_id, serviceUrl: service_url, email, userId } };
11207
11229
  }
11208
11230
 
11209
11231
  // src/commands/auth.ts
@@ -11293,13 +11315,70 @@ function registerAuthCommands(program2) {
11293
11315
  });
11294
11316
  }
11295
11317
 
11318
+ // src/commands/login.ts
11319
+ var import_node_child_process5 = require("node:child_process");
11320
+ var import_node_path4 = require("node:path");
11321
+ function registerLoginCommand(program2) {
11322
+ program2.command("login").description("Log in to Verity (link your GitHub identity so runs and memory are saved)").option("--force", "Re-authenticate even if already logged in").action(async (opts) => {
11323
+ const globals = program2.opts();
11324
+ const urlResult = await resolveServiceUrl(globals.serviceUrl);
11325
+ if (!urlResult.ok) {
11326
+ printError(urlResult.error);
11327
+ process.exit(1);
11328
+ }
11329
+ const serviceUrl = urlResult.data;
11330
+ const existing = await resolveToken(globals.token);
11331
+ if (existing.ok && !opts.force) {
11332
+ if (existing.data.userId != null) {
11333
+ printInfo(`Already logged in as ${existing.data.email ?? `user #${existing.data.userId}`}. \u2713`);
11334
+ printInfo(" Re-authenticate with: verity login --force");
11335
+ return;
11336
+ }
11337
+ const who2 = await whoami(existing.data.token, serviceUrl, globals.verbose);
11338
+ if (who2.ok && who2.data.logged_in) {
11339
+ printInfo(`Already logged in as ${who2.data.email ?? `user #${who2.data.user_id}`}. \u2713`);
11340
+ printInfo(" Re-authenticate with: verity login --force");
11341
+ return;
11342
+ }
11343
+ if (who2.ok && who2.data.anonymous) {
11344
+ printInfo("You have an anonymous token (the gate runs, but nothing is saved). Logging you in\u2026");
11345
+ } else if (!who2.ok) {
11346
+ printWarn("Could not confirm your current login state with the service \u2014 proceeding to log in.");
11347
+ }
11348
+ }
11349
+ let remote = "";
11350
+ try {
11351
+ remote = (0, import_node_child_process5.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
11352
+ } catch {
11353
+ }
11354
+ if (!remote) {
11355
+ printError("verity login needs a git remote (origin) to link this repository.");
11356
+ printInfo(" Add one with: git remote add origin <url>, then re-run verity login.");
11357
+ process.exit(1);
11358
+ }
11359
+ const projectName = parseRemote(remote)?.repo ?? (0, import_node_path4.basename)(process.cwd());
11360
+ printInfo("Authenticating with GitHub\u2026");
11361
+ const result = await registerProject({ projectName, remote, serviceUrl, verbose: globals.verbose });
11362
+ if (!result.ok) {
11363
+ printError(`Login failed: ${result.error}`);
11364
+ process.exit(1);
11365
+ }
11366
+ const who = result.data.email ?? (result.data.userId != null ? `user #${result.data.userId}` : "your account");
11367
+ printInfo(`Logged in as ${who}. \u2713`);
11368
+ printInfo(" Runs, history, and cloud memory now sync to Verity for this project.");
11369
+ if (result.data.userId == null) {
11370
+ printWarn(" (Server did not return a user id \u2014 update the CLI if this persists.)");
11371
+ }
11372
+ });
11373
+ }
11374
+
11296
11375
  // src/lib/hooks.ts
11297
11376
  var import_promises5 = require("node:fs/promises");
11298
- var import_node_path5 = require("node:path");
11377
+ var import_node_path6 = require("node:path");
11299
11378
 
11300
11379
  // src/lib/json-file.ts
11301
11380
  var import_promises4 = require("node:fs/promises");
11302
- var import_node_path4 = require("node:path");
11381
+ var import_node_path5 = require("node:path");
11303
11382
  function jsonSemanticEqual(a, b) {
11304
11383
  if (a === b) return true;
11305
11384
  if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) {
@@ -11345,7 +11424,7 @@ async function writeJsonFilePreservingStyle(file, value) {
11345
11424
  const indent = currentRaw !== null ? detectJsonIndent(currentRaw) : 2;
11346
11425
  const next = JSON.stringify(value, null, indent) + "\n";
11347
11426
  if (next === currentRaw) return false;
11348
- await (0, import_promises4.mkdir)((0, import_node_path4.dirname)(file), { recursive: true });
11427
+ await (0, import_promises4.mkdir)((0, import_node_path5.dirname)(file), { recursive: true });
11349
11428
  await (0, import_promises4.writeFile)(file, next);
11350
11429
  return true;
11351
11430
  }
@@ -11506,13 +11585,13 @@ async function writeSettings(settings) {
11506
11585
  }
11507
11586
  async function readSettingsAt(root) {
11508
11587
  try {
11509
- return JSON.parse(await (0, import_promises5.readFile)((0, import_node_path5.join)(root, CLAUDE_SETTINGS_FILE), "utf-8"));
11588
+ return JSON.parse(await (0, import_promises5.readFile)((0, import_node_path6.join)(root, CLAUDE_SETTINGS_FILE), "utf-8"));
11510
11589
  } catch {
11511
11590
  return {};
11512
11591
  }
11513
11592
  }
11514
11593
  async function writeSettingsAt(root, settings) {
11515
- await writeJsonFilePreservingStyle((0, import_node_path5.join)(root, CLAUDE_SETTINGS_FILE), settings);
11594
+ await writeJsonFilePreservingStyle((0, import_node_path6.join)(root, CLAUDE_SETTINGS_FILE), settings);
11516
11595
  }
11517
11596
  async function hasLegacyHooksAt(root) {
11518
11597
  const settings = await readSettingsAt(root);
@@ -11748,12 +11827,12 @@ function registerHooksCommands(program2) {
11748
11827
  }
11749
11828
 
11750
11829
  // src/commands/intent.ts
11751
- var import_node_crypto3 = require("node:crypto");
11830
+ var import_node_crypto4 = require("node:crypto");
11752
11831
 
11753
11832
  // src/lib/conversation-buffer.ts
11754
11833
  var import_promises6 = require("node:fs/promises");
11755
11834
  var import_node_fs3 = require("node:fs");
11756
- var import_node_child_process5 = require("node:child_process");
11835
+ var import_node_child_process6 = require("node:child_process");
11757
11836
  var import_node_crypto = require("node:crypto");
11758
11837
  function stripImageReferences(text) {
11759
11838
  return text.replace(/\[Image #\d+\]/g, "[screenshot \u2014 not available for review]");
@@ -11856,7 +11935,7 @@ async function readBufferEntries() {
11856
11935
  }
11857
11936
  function getRecentCommitMessages() {
11858
11937
  try {
11859
- const output = (0, import_node_child_process5.execSync)(
11938
+ const output = (0, import_node_child_process6.execSync)(
11860
11939
  'git log --since="30 minutes ago" --format="%s" -5',
11861
11940
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
11862
11941
  ).trim();
@@ -11867,10 +11946,23 @@ function getRecentCommitMessages() {
11867
11946
  }
11868
11947
  }
11869
11948
 
11949
+ // src/lib/context-identity.ts
11950
+ var import_node_crypto2 = require("node:crypto");
11951
+ function contextIdentity(token, sessionId) {
11952
+ const t = (token ?? "").trim();
11953
+ const s = (sessionId ?? "").trim();
11954
+ const userKey = t.length > 0 ? (0, import_node_crypto2.createHash)("sha256").update(t).digest("hex").slice(0, 12) : "anon";
11955
+ const sessionKey2 = s.length > 0 ? (0, import_node_crypto2.createHash)("sha256").update(s).digest("hex").slice(0, 16) : "_default";
11956
+ return { userKey, sessionKey: sessionKey2, bucket: `${userKey}/${sessionKey2}` };
11957
+ }
11958
+ function sessionScopeKey(token, sessionId) {
11959
+ return contextIdentity(token, sessionId).bucket;
11960
+ }
11961
+
11870
11962
  // src/lib/task-context-buffer.ts
11871
11963
  var import_promises7 = require("node:fs/promises");
11872
11964
  var import_node_fs4 = require("node:fs");
11873
- var import_node_path6 = require("node:path");
11965
+ var import_node_path7 = require("node:path");
11874
11966
  var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
11875
11967
  var MAX_BUFFER_BYTES = 500 * 1024;
11876
11968
  var MAX_PROMPT_CHARS = 2e3;
@@ -11948,7 +12040,7 @@ async function cleanupTaskContextBuffers() {
11948
12040
  const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
11949
12041
  for (const file of files) {
11950
12042
  if (!file.endsWith(".jsonl")) continue;
11951
- const filePath = (0, import_node_path6.join)(TASK_CONTEXT_DIR, file);
12043
+ const filePath = (0, import_node_path7.join)(TASK_CONTEXT_DIR, file);
11952
12044
  try {
11953
12045
  const stats = await (0, import_promises7.stat)(filePath);
11954
12046
  if (stats.mtimeMs < cutoffMs) {
@@ -11962,7 +12054,7 @@ async function cleanupTaskContextBuffers() {
11962
12054
  }
11963
12055
  function bufferPath(taskId) {
11964
12056
  const safe = taskId.replace(/[^a-zA-Z0-9_-]/g, "");
11965
- return (0, import_node_path6.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
12057
+ return (0, import_node_path7.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
11966
12058
  }
11967
12059
  async function appendEntry(taskId, entry) {
11968
12060
  try {
@@ -11988,7 +12080,7 @@ async function appendEntry(taskId, entry) {
11988
12080
  // src/lib/memory-retrieval.ts
11989
12081
  var import_promises8 = require("node:fs/promises");
11990
12082
  var import_node_fs5 = require("node:fs");
11991
- var import_node_path7 = require("node:path");
12083
+ var import_node_path8 = require("node:path");
11992
12084
  var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
11993
12085
  var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
11994
12086
  var DEFAULT_BUDGET_TOKENS = 2e3;
@@ -12086,14 +12178,14 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
12086
12178
  const promptTokens = tokenize(promptText);
12087
12179
  const nodes = [];
12088
12180
  for (const domain of DOMAINS) {
12089
- const domainDir = (0, import_node_path7.join)(memoryDir(), domain);
12181
+ const domainDir = (0, import_node_path8.join)(memoryDir(), domain);
12090
12182
  if (!(0, import_node_fs5.existsSync)(domainDir)) continue;
12091
12183
  try {
12092
12184
  const files = await (0, import_promises8.readdir)(domainDir);
12093
12185
  for (const file of files) {
12094
12186
  if (!file.endsWith(".md")) continue;
12095
12187
  try {
12096
- const content = await (0, import_promises8.readFile)((0, import_node_path7.join)(domainDir, file), "utf-8");
12188
+ const content = await (0, import_promises8.readFile)((0, import_node_path8.join)(domainDir, file), "utf-8");
12097
12189
  const { fm, body } = parseFrontmatter(content);
12098
12190
  if (fm.status && fm.status !== "active") continue;
12099
12191
  nodes.push({
@@ -12153,8 +12245,8 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
12153
12245
  // src/lib/memory-sync.ts
12154
12246
  var import_promises9 = require("node:fs/promises");
12155
12247
  var import_node_fs6 = require("node:fs");
12156
- var import_node_path8 = require("node:path");
12157
- var import_node_crypto2 = require("node:crypto");
12248
+ var import_node_path9 = require("node:path");
12249
+ var import_node_crypto3 = require("node:crypto");
12158
12250
 
12159
12251
  // src/lib/glob-match.ts
12160
12252
  function globToRegex(glob) {
@@ -12224,16 +12316,16 @@ var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
12224
12316
  async function ensureMemoryDir() {
12225
12317
  await (0, import_promises9.mkdir)(memoryDir2(), { recursive: true });
12226
12318
  for (const domain of DOMAINS2) {
12227
- await (0, import_promises9.mkdir)((0, import_node_path8.join)(memoryDir2(), domain), { recursive: true });
12319
+ await (0, import_promises9.mkdir)((0, import_node_path9.join)(memoryDir2(), domain), { recursive: true });
12228
12320
  }
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);
12321
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path9.join)(memoryDir2(), "SCHEMA.md"))) {
12322
+ await (0, import_promises9.writeFile)((0, import_node_path9.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
12231
12323
  }
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");
12324
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path9.join)(memoryDir2(), "index.md"))) {
12325
+ await (0, import_promises9.writeFile)((0, import_node_path9.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
12234
12326
  }
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");
12327
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path9.join)(memoryDir2(), "log.md"))) {
12328
+ await (0, import_promises9.writeFile)((0, import_node_path9.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
12237
12329
  }
12238
12330
  }
12239
12331
  async function buildManifest() {
@@ -12242,17 +12334,17 @@ async function buildManifest() {
12242
12334
  }
12243
12335
  const nodes = [];
12244
12336
  for (const domain of DOMAINS2) {
12245
- const domainDir = (0, import_node_path8.join)(memoryDir2(), domain);
12337
+ const domainDir = (0, import_node_path9.join)(memoryDir2(), domain);
12246
12338
  if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
12247
12339
  try {
12248
12340
  const files = await (0, import_promises9.readdir)(domainDir);
12249
12341
  for (const file of files) {
12250
12342
  if (!file.endsWith(".md")) continue;
12251
12343
  const filePath = `${domain}/${file}`;
12252
- const fullPath = (0, import_node_path8.join)(memoryDir2(), filePath);
12344
+ const fullPath = (0, import_node_path9.join)(memoryDir2(), filePath);
12253
12345
  try {
12254
12346
  const content = await (0, import_promises9.readFile)(fullPath, "utf-8");
12255
- const hash = (0, import_node_crypto2.createHash)("sha256").update(content).digest("hex").slice(0, 16);
12347
+ const hash = (0, import_node_crypto3.createHash)("sha256").update(content).digest("hex").slice(0, 16);
12256
12348
  nodes.push({ path: filePath, content_hash: `sha256:${hash}` });
12257
12349
  } catch {
12258
12350
  }
@@ -12262,32 +12354,32 @@ async function buildManifest() {
12262
12354
  }
12263
12355
  let indexHash = null;
12264
12356
  try {
12265
- const indexContent = await (0, import_promises9.readFile)((0, import_node_path8.join)(memoryDir2(), "index.md"), "utf-8");
12266
- indexHash = `sha256:${(0, import_node_crypto2.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
12357
+ const indexContent = await (0, import_promises9.readFile)((0, import_node_path9.join)(memoryDir2(), "index.md"), "utf-8");
12358
+ indexHash = `sha256:${(0, import_node_crypto3.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
12267
12359
  } catch {
12268
12360
  }
12269
12361
  let logLength = 0;
12270
12362
  try {
12271
- const logContent = await (0, import_promises9.readFile)((0, import_node_path8.join)(memoryDir2(), "log.md"), "utf-8");
12363
+ const logContent = await (0, import_promises9.readFile)((0, import_node_path9.join)(memoryDir2(), "log.md"), "utf-8");
12272
12364
  logLength = logContent.split("\n").length;
12273
12365
  } catch {
12274
12366
  }
12275
12367
  return { schema_version: 1, nodes, index_hash: indexHash, log_length: logLength };
12276
12368
  }
12277
12369
  function hashContent(content) {
12278
- return `sha256:${(0, import_node_crypto2.createHash)("sha256").update(content).digest("hex").slice(0, 16)}`;
12370
+ return `sha256:${(0, import_node_crypto3.createHash)("sha256").update(content).digest("hex").slice(0, 16)}`;
12279
12371
  }
12280
12372
  async function readOnDiskNodes() {
12281
12373
  const out = /* @__PURE__ */ new Map();
12282
12374
  if (!(0, import_node_fs6.existsSync)(memoryDir2())) return out;
12283
12375
  for (const domain of DOMAINS2) {
12284
- const domainDir = (0, import_node_path8.join)(memoryDir2(), domain);
12376
+ const domainDir = (0, import_node_path9.join)(memoryDir2(), domain);
12285
12377
  if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
12286
12378
  try {
12287
12379
  for (const file of await (0, import_promises9.readdir)(domainDir)) {
12288
12380
  if (!file.endsWith(".md")) continue;
12289
12381
  try {
12290
- out.set(`${domain}/${file}`, hashContent(await (0, import_promises9.readFile)((0, import_node_path8.join)(domainDir, file), "utf-8")));
12382
+ out.set(`${domain}/${file}`, hashContent(await (0, import_promises9.readFile)((0, import_node_path9.join)(domainDir, file), "utf-8")));
12291
12383
  } catch {
12292
12384
  }
12293
12385
  }
@@ -12333,7 +12425,7 @@ async function computeEditedNodeUploads() {
12333
12425
  const uploads = [];
12334
12426
  for (const [path, prevHash] of prev) {
12335
12427
  if (prevHash == null) continue;
12336
- const full = (0, import_node_path8.join)(memoryDir2(), path);
12428
+ const full = (0, import_node_path9.join)(memoryDir2(), path);
12337
12429
  if (!(0, import_node_fs6.existsSync)(full)) continue;
12338
12430
  let content;
12339
12431
  try {
@@ -12370,15 +12462,15 @@ async function applyMemoryWrites(writes, opts = {}) {
12370
12462
  const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
12371
12463
  for (const n of notes) logLines.push(` - ${n}`);
12372
12464
  try {
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");
12465
+ const existing = (0, import_node_fs6.existsSync)((0, import_node_path9.join)(memoryDir2(), "log.md")) ? await (0, import_promises9.readFile)((0, import_node_path9.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
12466
+ await (0, import_promises9.writeFile)((0, import_node_path9.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
12375
12467
  } catch {
12376
12468
  }
12377
12469
  await recordSyncedNodePaths();
12378
12470
  return count;
12379
12471
  }
12380
12472
  async function applyOneWrite(write, treePaths) {
12381
- const fullPath = (0, import_node_path8.join)(memoryDir2(), write.path);
12473
+ const fullPath = (0, import_node_path9.join)(memoryDir2(), write.path);
12382
12474
  const notes = [];
12383
12475
  let content = write.content;
12384
12476
  if (treePaths && treePaths.length > 0) {
@@ -12400,7 +12492,7 @@ async function applyOneWrite(write, treePaths) {
12400
12492
  return { written: false, notes };
12401
12493
  }
12402
12494
  }
12403
- await (0, import_promises9.mkdir)((0, import_node_path8.dirname)(fullPath), { recursive: true });
12495
+ await (0, import_promises9.mkdir)((0, import_node_path9.dirname)(fullPath), { recursive: true });
12404
12496
  await (0, import_promises9.writeFile)(fullPath, content);
12405
12497
  return { written: true, notes };
12406
12498
  }
@@ -12441,7 +12533,7 @@ async function regenerateIndex() {
12441
12533
  ];
12442
12534
  let totalNodes = 0;
12443
12535
  for (const domain of DOMAINS2.filter((d) => d !== "_archive")) {
12444
- const domainDir = (0, import_node_path8.join)(memoryDir2(), domain);
12536
+ const domainDir = (0, import_node_path9.join)(memoryDir2(), domain);
12445
12537
  if (!(0, import_node_fs6.existsSync)(domainDir)) continue;
12446
12538
  try {
12447
12539
  const files = await (0, import_promises9.readdir)(domainDir);
@@ -12452,7 +12544,7 @@ async function regenerateIndex() {
12452
12544
  for (const file of mdFiles.sort()) {
12453
12545
  const slug = file.replace(/\.md$/, "");
12454
12546
  try {
12455
- const content = await (0, import_promises9.readFile)((0, import_node_path8.join)(domainDir, file), "utf-8");
12547
+ const content = await (0, import_promises9.readFile)((0, import_node_path9.join)(domainDir, file), "utf-8");
12456
12548
  const title = pickFrontmatter(content, "title") ?? slug;
12457
12549
  const kind = pickFrontmatter(content, "kind") ?? "-";
12458
12550
  const confidence = pickFrontmatter(content, "confidence");
@@ -12476,7 +12568,7 @@ async function regenerateIndex() {
12476
12568
  lines.push("No nodes yet. Run an analysis to start building the knowledge graph.");
12477
12569
  }
12478
12570
  const next = lines.join("\n") + "\n";
12479
- const indexPath = (0, import_node_path8.join)(memoryDir2(), "index.md");
12571
+ const indexPath = (0, import_node_path9.join)(memoryDir2(), "index.md");
12480
12572
  let existing = null;
12481
12573
  try {
12482
12574
  existing = await (0, import_promises9.readFile)(indexPath, "utf-8");
@@ -12558,7 +12650,7 @@ function hasLegacyMemoryBlock(text) {
12558
12650
  return findMarker(text, LEGACY_MD_START) !== -1;
12559
12651
  }
12560
12652
  async function ensureClaudeMdPointer(cwd = repoRoot()) {
12561
- const claudeMdPath = (0, import_node_path8.join)(cwd, "CLAUDE.md");
12653
+ const claudeMdPath = (0, import_node_path9.join)(cwd, "CLAUDE.md");
12562
12654
  let existing = "";
12563
12655
  if ((0, import_node_fs6.existsSync)(claudeMdPath)) {
12564
12656
  existing = await (0, import_promises9.readFile)(claudeMdPath, "utf-8");
@@ -12720,7 +12812,10 @@ function registerIntentCommands(program2) {
12720
12812
  if (!prompt) {
12721
12813
  process.exit(0);
12722
12814
  }
12723
- await appendToConversationBuffer(prompt, event.session_id ?? "");
12815
+ const authForScope = await resolveToken(program2.opts().token);
12816
+ const scopeToken = authForScope.ok ? authForScope.data.token : void 0;
12817
+ const scopeSession = event.session_id || process.env.CLAUDE_SESSION_ID || "";
12818
+ await appendToConversationBuffer(prompt, sessionScopeKey(scopeToken, scopeSession));
12724
12819
  try {
12725
12820
  await ensureMemoryDir();
12726
12821
  const injection = await retrieveForInjection(prompt);
@@ -12762,7 +12857,7 @@ async function fireClassify(prompt, sessionId) {
12762
12857
  logEvent("classify_skipped", { reason: "no_service_url", detail: urlResult.error });
12763
12858
  return;
12764
12859
  }
12765
- const promptHash = (0, import_node_crypto3.createHash)("sha256").update(prompt).digest("hex");
12860
+ const promptHash = (0, import_node_crypto4.createHash)("sha256").update(prompt).digest("hex");
12766
12861
  const result = await apiRequest({
12767
12862
  method: "POST",
12768
12863
  path: "/classify-task",
@@ -13113,6 +13208,16 @@ function registerStatusCommand(program2) {
13113
13208
  return;
13114
13209
  }
13115
13210
  printInfo("=== Verity Status ===");
13211
+ if (tokenResult.data.userId != null) {
13212
+ printInfo(`Account: Logged in as ${tokenResult.data.email ?? `user #${tokenResult.data.userId}`} \u2713`);
13213
+ } else {
13214
+ const who = await whoami(token, serviceUrl, globals.verbose);
13215
+ if (who.ok && who.data.logged_in) {
13216
+ printInfo(`Account: Logged in as ${who.data.email ?? `user #${who.data.user_id}`} \u2713`);
13217
+ } else if (who.ok && who.data.anonymous) {
13218
+ printInfo('Account: Anonymous \u2014 runs not saved, no cloud memory. Run "verity login".');
13219
+ }
13220
+ }
13116
13221
  if (mem.project_name) printInfo(`Project: ${mem.project_name}`);
13117
13222
  if (mem.standard) {
13118
13223
  const s = mem.standard;
@@ -13276,11 +13381,11 @@ async function sendGeneralFeedback(message, opts, globals) {
13276
13381
 
13277
13382
  // src/commands/analyze.ts
13278
13383
  var import_node_fs19 = require("node:fs");
13279
- var import_node_path15 = require("node:path");
13384
+ var import_node_path16 = require("node:path");
13280
13385
 
13281
13386
  // src/lib/files.ts
13282
13387
  var import_node_fs8 = require("node:fs");
13283
- var import_node_path9 = require("node:path");
13388
+ var import_node_path10 = require("node:path");
13284
13389
  var LANG_MAP = {
13285
13390
  // Analyzable (static analysis + Gemini)
13286
13391
  ts: "typescript",
@@ -13348,7 +13453,7 @@ var LANG_MAP = {
13348
13453
  mk: "make"
13349
13454
  };
13350
13455
  function detectLanguage(filepath) {
13351
- const ext = (0, import_node_path9.extname)(filepath).slice(1);
13456
+ const ext = (0, import_node_path10.extname)(filepath).slice(1);
13352
13457
  return LANG_MAP[ext] ?? ext;
13353
13458
  }
13354
13459
  function sortByMtime(files) {
@@ -13410,10 +13515,10 @@ function collectCodeDelta(files, opts) {
13410
13515
 
13411
13516
  // src/lib/debounce.ts
13412
13517
  var import_node_fs9 = require("node:fs");
13413
- var import_node_crypto4 = require("node:crypto");
13518
+ var import_node_crypto5 = require("node:crypto");
13414
13519
  function scopedFile(base, sessionId) {
13415
13520
  if (!sessionId) return base;
13416
- return `${base}.${(0, import_node_crypto4.createHash)("sha1").update(sessionId).digest("hex").slice(0, 12)}`;
13521
+ return `${base}.${(0, import_node_crypto5.createHash)("sha1").update(sessionId).digest("hex").slice(0, 12)}`;
13417
13522
  }
13418
13523
  function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
13419
13524
  const file = scopedFile(DEBOUNCE_FILE, sessionId);
@@ -13454,7 +13559,7 @@ function checkMtime(files, bypassForRecentCommits, sessionId) {
13454
13559
  return "No files modified since last analysis";
13455
13560
  }
13456
13561
  function computeContentHash(files) {
13457
- const hash = (0, import_node_crypto4.createHash)("sha1");
13562
+ const hash = (0, import_node_crypto5.createHash)("sha1");
13458
13563
  const sorted = [...files].sort();
13459
13564
  for (const f of sorted) {
13460
13565
  const resolved = resolveFile(f) ?? f;
@@ -13543,7 +13648,7 @@ function writeIteration(iteration, commit, _contentHash) {
13543
13648
  }
13544
13649
 
13545
13650
  // src/lib/static-analysis.ts
13546
- var import_node_child_process6 = require("node:child_process");
13651
+ var import_node_child_process7 = require("node:child_process");
13547
13652
  var import_node_fs10 = require("node:fs");
13548
13653
  var SEVERITY_ORDER = {
13549
13654
  Error: 0,
@@ -13556,7 +13661,7 @@ var SEVERITY_ORDER = {
13556
13661
  };
13557
13662
  function isCodacyAvailable() {
13558
13663
  try {
13559
- (0, import_node_child_process6.execSync)("which codacy-analysis", { stdio: "pipe" });
13664
+ (0, import_node_child_process7.execSync)("which codacy-analysis", { stdio: "pipe" });
13560
13665
  return true;
13561
13666
  } catch {
13562
13667
  return false;
@@ -13580,7 +13685,7 @@ function runCodacyAnalysis(files) {
13580
13685
  const fileArgs = existingFiles.join(" ");
13581
13686
  let output;
13582
13687
  try {
13583
- output = (0, import_node_child_process6.execSync)(
13688
+ output = (0, import_node_child_process7.execSync)(
13584
13689
  `codacy-analysis analyze --install-dependencies --files ${fileArgs} --output-format json --log-level error --parallel-tools 3`,
13585
13690
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], maxBuffer: 10 * 1024 * 1024 }
13586
13691
  );
@@ -13634,7 +13739,7 @@ function runCodacyAnalysis(files) {
13634
13739
 
13635
13740
  // src/lib/specs.ts
13636
13741
  var import_node_fs11 = require("node:fs");
13637
- var import_node_path10 = require("node:path");
13742
+ var import_node_path11 = require("node:path");
13638
13743
  var SPEC_CANDIDATES = [
13639
13744
  "CLAUDE.md",
13640
13745
  "AGENTS.md",
@@ -13696,7 +13801,7 @@ function findMdFiles(dir, maxDepth, depth = 0) {
13696
13801
  try {
13697
13802
  const entries = (0, import_node_fs11.readdirSync)(dir, { withFileTypes: true });
13698
13803
  for (const entry of entries) {
13699
- const fullPath = (0, import_node_path10.join)(dir, entry.name);
13804
+ const fullPath = (0, import_node_path11.join)(dir, entry.name);
13700
13805
  if (entry.isFile() && entry.name.endsWith(".md")) {
13701
13806
  result.push(fullPath);
13702
13807
  } else if (entry.isDirectory() && depth < maxDepth - 1) {
@@ -13708,7 +13813,7 @@ function findMdFiles(dir, maxDepth, depth = 0) {
13708
13813
  return result;
13709
13814
  }
13710
13815
  function discoverPlans() {
13711
- const homePlansDir = (0, import_node_path10.join)(process.env.HOME ?? "", ".claude", "plans");
13816
+ const homePlansDir = (0, import_node_path11.join)(process.env.HOME ?? "", ".claude", "plans");
13712
13817
  const localPlansDir = ".claude/plans";
13713
13818
  const candidates = [];
13714
13819
  const seen = /* @__PURE__ */ new Set();
@@ -13718,7 +13823,7 @@ function discoverPlans() {
13718
13823
  for (const f of (0, import_node_fs11.readdirSync)(plansDir)) {
13719
13824
  if (!f.endsWith(".md") || seen.has(f)) continue;
13720
13825
  seen.add(f);
13721
- const fullPath = (0, import_node_path10.join)(plansDir, f);
13826
+ const fullPath = (0, import_node_path11.join)(plansDir, f);
13722
13827
  try {
13723
13828
  const stat3 = (0, import_node_fs11.statSync)(fullPath);
13724
13829
  candidates.push({ name: f, path: fullPath, mtime: stat3.mtimeMs, size: stat3.size });
@@ -13743,15 +13848,15 @@ function discoverPlans() {
13743
13848
 
13744
13849
  // src/lib/snapshot.ts
13745
13850
  var import_node_fs12 = require("node:fs");
13746
- var import_node_path11 = require("node:path");
13747
- var import_node_child_process7 = require("node:child_process");
13851
+ var import_node_path12 = require("node:path");
13852
+ var import_node_child_process8 = require("node:child_process");
13748
13853
  function generateSnapshotDiffs(files) {
13749
13854
  if (!(0, import_node_fs12.existsSync)(SNAPSHOT_DIR)) {
13750
13855
  return { diffs: [], has_snapshots: false };
13751
13856
  }
13752
13857
  const diffs = [];
13753
13858
  for (const file of files) {
13754
- const snapshotPath = (0, import_node_path11.join)(SNAPSHOT_DIR, file.path);
13859
+ const snapshotPath = (0, import_node_path12.join)(SNAPSHOT_DIR, file.path);
13755
13860
  const language = file.language ?? detectLanguage(file.path);
13756
13861
  if ((0, import_node_fs12.existsSync)(snapshotPath)) {
13757
13862
  const oldContent = (0, import_node_fs12.readFileSync)(snapshotPath, "utf-8");
@@ -13778,21 +13883,21 @@ ${addedLines}`,
13778
13883
  function saveSnapshots(files) {
13779
13884
  const snapshotPaths = /* @__PURE__ */ new Set();
13780
13885
  for (const file of files) {
13781
- const snapshotPath = (0, import_node_path11.join)(SNAPSHOT_DIR, file.path);
13886
+ const snapshotPath = (0, import_node_path12.join)(SNAPSHOT_DIR, file.path);
13782
13887
  snapshotPaths.add(snapshotPath);
13783
- (0, import_node_fs12.mkdirSync)((0, import_node_path11.dirname)(snapshotPath), { recursive: true });
13888
+ (0, import_node_fs12.mkdirSync)((0, import_node_path12.dirname)(snapshotPath), { recursive: true });
13784
13889
  (0, import_node_fs12.writeFileSync)(snapshotPath, file.content);
13785
13890
  }
13786
13891
  cleanStaleSnapshots(SNAPSHOT_DIR, snapshotPaths);
13787
13892
  }
13788
13893
  function computeDiff(oldContent, newContent, filePath) {
13789
- const tmpOld = (0, import_node_path11.join)(SNAPSHOT_DIR, ".diff-old.tmp");
13790
- const tmpNew = (0, import_node_path11.join)(SNAPSHOT_DIR, ".diff-new.tmp");
13894
+ const tmpOld = (0, import_node_path12.join)(SNAPSHOT_DIR, ".diff-old.tmp");
13895
+ const tmpNew = (0, import_node_path12.join)(SNAPSHOT_DIR, ".diff-new.tmp");
13791
13896
  try {
13792
13897
  (0, import_node_fs12.mkdirSync)(SNAPSHOT_DIR, { recursive: true });
13793
13898
  (0, import_node_fs12.writeFileSync)(tmpOld, oldContent);
13794
13899
  (0, import_node_fs12.writeFileSync)(tmpNew, newContent);
13795
- const result = (0, import_node_child_process7.execSync)(
13900
+ const result = (0, import_node_child_process8.execSync)(
13796
13901
  `git diff --no-index --unified=10 -- "${tmpOld}" "${tmpNew}"`,
13797
13902
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
13798
13903
  );
@@ -13820,7 +13925,7 @@ function cleanStaleSnapshots(dir, keepSet) {
13820
13925
  const entries = (0, import_node_fs12.readdirSync)(dir, { withFileTypes: true });
13821
13926
  for (const entry of entries) {
13822
13927
  if (entry.name.startsWith(".")) continue;
13823
- const fullPath = (0, import_node_path11.join)(dir, entry.name);
13928
+ const fullPath = (0, import_node_path12.join)(dir, entry.name);
13824
13929
  if (entry.isDirectory()) {
13825
13930
  cleanStaleSnapshots(fullPath, keepSet);
13826
13931
  try {
@@ -13841,24 +13946,24 @@ function cleanStaleSnapshots(dir, keepSet) {
13841
13946
 
13842
13947
  // src/lib/baseline.ts
13843
13948
  var import_node_fs13 = require("node:fs");
13844
- var import_node_path12 = require("node:path");
13845
- var import_node_crypto5 = require("node:crypto");
13949
+ var import_node_path13 = require("node:path");
13950
+ var import_node_crypto6 = require("node:crypto");
13846
13951
  var BASELINE_VERSION = 1;
13847
13952
  var BASELINE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
13848
13953
  var MIRROR_MAX_BYTES = 2 * 1024 * 1024;
13849
13954
  var DEFAULT_SESSION_KEY = "_default";
13850
13955
  function sessionKey(sessionId) {
13851
13956
  if (!sessionId) return DEFAULT_SESSION_KEY;
13852
- return (0, import_node_crypto5.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
13957
+ return (0, import_node_crypto6.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
13853
13958
  }
13854
13959
  function sessionDir(key) {
13855
- return (0, import_node_path12.join)(projectPath(BASELINE_DIR), key);
13960
+ return (0, import_node_path13.join)(projectPath(BASELINE_DIR), key);
13856
13961
  }
13857
13962
  function manifestPath(dir) {
13858
- return (0, import_node_path12.join)(dir, "manifest.json");
13963
+ return (0, import_node_path13.join)(dir, "manifest.json");
13859
13964
  }
13860
13965
  function mirrorPath(dir, repoRelPath) {
13861
- return (0, import_node_path12.join)(dir, "files", repoRelPath);
13966
+ return (0, import_node_path13.join)(dir, "files", repoRelPath);
13862
13967
  }
13863
13968
  function captureBaseline(opts = {}) {
13864
13969
  const key = sessionKey(opts.sessionId);
@@ -13874,7 +13979,7 @@ function captureBaseline(opts = {}) {
13874
13979
  (0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
13875
13980
  } catch {
13876
13981
  }
13877
- const filesDir = (0, import_node_path12.join)(dir, "files");
13982
+ const filesDir = (0, import_node_path13.join)(dir, "files");
13878
13983
  const mirrored = [];
13879
13984
  try {
13880
13985
  (0, import_node_fs13.mkdirSync)(filesDir, { recursive: true });
@@ -13884,7 +13989,7 @@ function captureBaseline(opts = {}) {
13884
13989
  if (content === null) continue;
13885
13990
  const dest = mirrorPath(dir, p);
13886
13991
  try {
13887
- (0, import_node_fs13.mkdirSync)((0, import_node_path12.dirname)(dest), { recursive: true });
13992
+ (0, import_node_fs13.mkdirSync)((0, import_node_path13.dirname)(dest), { recursive: true });
13888
13993
  (0, import_node_fs13.writeFileSync)(dest, content);
13889
13994
  mirrored.push(p);
13890
13995
  } catch {
@@ -14012,7 +14117,7 @@ function pruneOldBaselines() {
14012
14117
  }
14013
14118
  const now = Date.now();
14014
14119
  for (const name of entries) {
14015
- const dir = (0, import_node_path12.join)(root, name);
14120
+ const dir = (0, import_node_path13.join)(root, name);
14016
14121
  const manifest = readManifest(dir);
14017
14122
  if (!manifest) {
14018
14123
  try {
@@ -14031,13 +14136,75 @@ function pruneOldBaselines() {
14031
14136
  }
14032
14137
  }
14033
14138
 
14139
+ // src/lib/task-context.ts
14140
+ var import_node_child_process9 = require("node:child_process");
14141
+ var CLOSING_RE = /\b(close[sd]?|fix(?:e[sd])?|resolve[sd]?)\b[\s:]*#(\d+)/i;
14142
+ var BRANCH_RE = /(?:^|[/_-])(?:issue|gh|fix)[-_/]?(\d+)\b/i;
14143
+ function parseLinkedIssue(sources) {
14144
+ for (const c of sources.commits ?? []) {
14145
+ const m = CLOSING_RE.exec(c);
14146
+ if (m) return { issue: parseInt(m[2], 10), via: `commit:${m[1].toLowerCase()}` };
14147
+ }
14148
+ if (sources.branch) {
14149
+ const m = BRANCH_RE.exec(sources.branch);
14150
+ if (m) return { issue: parseInt(m[1], 10), via: "branch" };
14151
+ }
14152
+ return null;
14153
+ }
14154
+ function safeExec(cmd, timeout) {
14155
+ try {
14156
+ return (0, import_node_child_process9.execSync)(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout }).trim();
14157
+ } catch {
14158
+ return "";
14159
+ }
14160
+ }
14161
+ function defaultGhFetch(issue) {
14162
+ const raw = safeExec(`gh issue view ${issue} --json title,body`, 5e3);
14163
+ if (!raw) return null;
14164
+ try {
14165
+ const j = JSON.parse(raw);
14166
+ return j.title ? { title: j.title, body: j.body ?? "" } : null;
14167
+ } catch {
14168
+ return null;
14169
+ }
14170
+ }
14171
+ function resolveTaskContext(opts) {
14172
+ const branch = opts?.branch ?? safeExec("git rev-parse --abbrev-ref HEAD", 3e3);
14173
+ const commits = opts?.commits ?? safeExec("git log -5 --format=%s%n%b", 3e3).split("\n").map((l) => l.trim()).filter(Boolean);
14174
+ const linked = parseLinkedIssue({ branch, commits });
14175
+ if (!linked) return null;
14176
+ const issue = (opts?.ghFetch ?? defaultGhFetch)(linked.issue);
14177
+ if (!issue) return null;
14178
+ const body = (issue.body ?? "").slice(0, 4e3).trim();
14179
+ const goal = `[#${linked.issue}] ${issue.title}${body ? "\n\n" + body : ""}`;
14180
+ return { number: linked.issue, title: issue.title, goal, via: linked.via };
14181
+ }
14182
+
14183
+ // src/lib/run-mode.ts
14184
+ function parseAutonomousEnv(raw) {
14185
+ if (raw === void 0) return void 0;
14186
+ const v = raw.trim().toLowerCase();
14187
+ if (v === "") return void 0;
14188
+ if (v === "0" || v === "false" || v === "off" || v === "no") return false;
14189
+ return true;
14190
+ }
14191
+ function resolveRunMode(inputs = {}) {
14192
+ if (inputs.autonomousFlag === true) return "autonomous";
14193
+ if (inputs.autonomousFlag === false) return "interactive";
14194
+ const env = inputs.env ?? process.env;
14195
+ const envDecision = parseAutonomousEnv(env.VERITY_AUTONOMOUS);
14196
+ if (envDecision !== void 0) return envDecision ? "autonomous" : "interactive";
14197
+ const isTTY = inputs.isTTY ?? Boolean(process.stdin?.isTTY);
14198
+ return isTTY ? "interactive" : "autonomous";
14199
+ }
14200
+
14034
14201
  // src/lib/offline.ts
14035
14202
  var import_node_fs14 = require("node:fs");
14036
- var import_node_crypto6 = require("node:crypto");
14203
+ var import_node_crypto7 = require("node:crypto");
14037
14204
  function cacheRequest(body) {
14038
14205
  try {
14039
14206
  (0, import_node_fs14.mkdirSync)(CACHE_DIR, { recursive: true });
14040
- const suffix = (0, import_node_crypto6.randomBytes)(4).toString("hex");
14207
+ const suffix = (0, import_node_crypto7.randomBytes)(4).toString("hex");
14041
14208
  const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
14042
14209
  (0, import_node_fs14.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(body));
14043
14210
  } catch {
@@ -14111,7 +14278,7 @@ function gatherContextFiles(contextPaths, deltaFiles) {
14111
14278
 
14112
14279
  // src/lib/cache-cleanup.ts
14113
14280
  var import_node_fs16 = require("node:fs");
14114
- var import_node_path13 = require("node:path");
14281
+ var import_node_path14 = require("node:path");
14115
14282
  var CACHE_TTL_DAYS = 7;
14116
14283
  function pruneStaleCache() {
14117
14284
  try {
@@ -14119,7 +14286,7 @@ function pruneStaleCache() {
14119
14286
  const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
14120
14287
  for (const entry of (0, import_node_fs16.readdirSync)(dir)) {
14121
14288
  if (!entry.startsWith("pending-")) continue;
14122
- const path = (0, import_node_path13.join)(dir, entry);
14289
+ const path = (0, import_node_path14.join)(dir, entry);
14123
14290
  try {
14124
14291
  const stat3 = (0, import_node_fs16.statSync)(path);
14125
14292
  if (stat3.mtimeMs < cutoff) {
@@ -14242,6 +14409,12 @@ function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPromp
14242
14409
  }
14243
14410
  return "standard";
14244
14411
  }
14412
+ function scopeToAuthored(files, actionSummary) {
14413
+ if (!actionSummary) return { files, signal: "no-transcript" };
14414
+ const touched = [...actionSummary.files_edited ?? [], ...actionSummary.files_created ?? []];
14415
+ if (touched.length === 0) return { files: [], signal: "none-authored" };
14416
+ return { files: narrowToAgentAuthored(files, actionSummary), signal: "authored" };
14417
+ }
14245
14418
  function narrowToAgentAuthored(files, actionSummary) {
14246
14419
  if (!actionSummary) return files;
14247
14420
  const touched = [
@@ -14512,7 +14685,7 @@ function capArray(set, max) {
14512
14685
  // src/lib/seed-runner.ts
14513
14686
  var import_promises12 = require("node:fs/promises");
14514
14687
  var import_node_fs18 = require("node:fs");
14515
- var import_node_path14 = require("node:path");
14688
+ var import_node_path15 = require("node:path");
14516
14689
  var import_yaml2 = __toESM(require_dist());
14517
14690
 
14518
14691
  // src/lib/seed.ts
@@ -14794,7 +14967,7 @@ async function runSeed(opts) {
14794
14967
  if (candidates.length === 0) {
14795
14968
  return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
14796
14969
  }
14797
- const overviewPath = (0, import_node_path14.join)(MEMORY_DIR, "domain", "project-overview.md");
14970
+ const overviewPath = (0, import_node_path15.join)(MEMORY_DIR, "domain", "project-overview.md");
14798
14971
  if ((0, import_node_fs18.existsSync)(overviewPath) && !opts.force) {
14799
14972
  return { created: 0, failed: 0, skipped: "already_seeded", candidates };
14800
14973
  }
@@ -14830,9 +15003,9 @@ async function runSeed(opts) {
14830
15003
  }
14831
15004
  const nodeId = res.data.node_id;
14832
15005
  const filePathRel = res.data.file_path;
14833
- const targetPath = (0, import_node_path14.join)(MEMORY_DIR, filePathRel);
15006
+ const targetPath = (0, import_node_path15.join)(MEMORY_DIR, filePathRel);
14834
15007
  try {
14835
- await (0, import_promises12.mkdir)((0, import_node_path14.dirname)(targetPath), { recursive: true });
15008
+ await (0, import_promises12.mkdir)((0, import_node_path15.dirname)(targetPath), { recursive: true });
14836
15009
  await (0, import_promises12.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
14837
15010
  created++;
14838
15011
  opts.onCreated?.(nodeId, filePathRel, c);
@@ -14923,7 +15096,10 @@ async function runAnalyze(opts, globals) {
14923
15096
  }
14924
15097
  const { assistantMessage: assistantResponse, stopReason, transcriptPath, sessionId } = await readStopHookStdin();
14925
15098
  const actionSummary = transcriptPath ? await extractActionSummary(transcriptPath) : null;
14926
- const baselineSessionId = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
15099
+ const tokenResult = await resolveToken(globals.token);
15100
+ const scopeToken = tokenResult.ok ? tokenResult.data.token : void 0;
15101
+ const rawSessionId = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
15102
+ const baselineSessionId = sessionScopeKey(scopeToken, rawSessionId);
14927
15103
  const baseline = readBaseline(baselineSessionId);
14928
15104
  if (baseline) {
14929
15105
  logEvent("baseline_loaded", {
@@ -14941,7 +15117,7 @@ async function runAnalyze(opts, globals) {
14941
15117
  passAndExit("No analyzable files changed");
14942
15118
  }
14943
15119
  const allForReview = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable]));
14944
- const conversation = await readAndClearConversationBuffer(sessionId ?? void 0);
15120
+ const conversation = await readAndClearConversationBuffer(baselineSessionId);
14945
15121
  const specs = discoverSpecs();
14946
15122
  const plans = discoverPlans();
14947
15123
  const latestPrompt = conversation?.prompts?.[conversation.prompts.length - 1]?.prompt ?? "";
@@ -14955,7 +15131,6 @@ async function runAnalyze(opts, globals) {
14955
15131
  if (isReflectionQuestion(assistantResponse) && !agentAuthoredCodeThisTurn) {
14956
15132
  passAndExit("Reflection-prompt turn \u2014 skipping analysis");
14957
15133
  }
14958
- const tokenResult = await resolveToken(globals.token);
14959
15134
  const urlResult = await resolveServiceUrl(globals.serviceUrl);
14960
15135
  if (!tokenResult.ok || !urlResult.ok) {
14961
15136
  localOnlyAndExit(runLocalStatic(analyzable, securityFiles, baseline, !!opts.skipStatic));
@@ -15049,8 +15224,11 @@ async function runAnalyze(opts, globals) {
15049
15224
  }
15050
15225
  contentHash = hashResult.hash;
15051
15226
  if (analysisMode !== "plan") {
15052
- const agentNarrowed = narrowToAgentAuthored(allForReview, actionSummary);
15053
- const baseForReview = agentNarrowed.length > 0 ? agentNarrowed : allForReview;
15227
+ const scoped = scopeToAuthored(allForReview, actionSummary);
15228
+ if (scoped.signal === "none-authored" && (actionSummary?.subagents ?? 0) === 0) {
15229
+ passAndExit("No agent-authored code this turn \u2014 working-tree changes were not authored by this session");
15230
+ }
15231
+ const baseForReview = scoped.signal === "authored" && scoped.files.length > 0 ? scoped.files : allForReview;
15054
15232
  const recentForReview = narrowToRecent(baseForReview, baselineSessionId);
15055
15233
  if (!opts.skipStatic && isCodacyAvailable()) {
15056
15234
  let allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
@@ -15114,7 +15292,7 @@ async function runAnalyze(opts, globals) {
15114
15292
  let autoSeedNotice = null;
15115
15293
  try {
15116
15294
  await ensureMemoryDir();
15117
- const seedMarker = (0, import_node_path15.join)(VERITY_DIR, ".seeded");
15295
+ const seedMarker = (0, import_node_path16.join)(VERITY_DIR, ".seeded");
15118
15296
  const hasStandard = (0, import_node_fs19.existsSync)(STANDARD_FILE);
15119
15297
  const alreadyTried = (0, import_node_fs19.existsSync)(seedMarker);
15120
15298
  if (hasStandard && !alreadyTried) {
@@ -15180,7 +15358,9 @@ async function runAnalyze(opts, globals) {
15180
15358
  if (snapshotResult.has_snapshots && snapshotResult.diffs.length > 0) {
15181
15359
  requestBody.snapshot_diffs = snapshotResult.diffs;
15182
15360
  }
15183
- const hasIntent = (conversation?.prompts?.length ?? 0) > 0 || specs.length > 0 || plans.length > 0 || !!assistantResponse;
15361
+ const noHumanPrompt = (conversation?.prompts?.length ?? 0) === 0;
15362
+ const w4Task = noHumanPrompt && resolveRunMode() === "autonomous" ? resolveTaskContext() : null;
15363
+ const hasIntent = (conversation?.prompts?.length ?? 0) > 0 || specs.length > 0 || plans.length > 0 || !!assistantResponse || !!w4Task;
15184
15364
  if (hasIntent) {
15185
15365
  const intentContext = {};
15186
15366
  if (conversation && conversation.prompts.length > 0) {
@@ -15195,6 +15375,10 @@ async function runAnalyze(opts, globals) {
15195
15375
  intentContext.recent_commits = conversation.recent_commits;
15196
15376
  }
15197
15377
  }
15378
+ if (w4Task && !intentContext.user_prompt) {
15379
+ intentContext.user_prompt = w4Task.goal;
15380
+ logEvent("w4_issue_anchor", { issue: w4Task.number, via: w4Task.via });
15381
+ }
15198
15382
  if (assistantResponse) {
15199
15383
  const cap = analysisMode === "plan" ? MAX_ASSISTANT_RESPONSE_CHARS_PLAN : MAX_ASSISTANT_RESPONSE_CHARS_DEFAULT;
15200
15384
  intentContext.assistant_response = assistantResponse.length > cap ? assistantResponse.slice(0, cap) : assistantResponse;
@@ -15348,6 +15532,7 @@ async function runAnalyze(opts, globals) {
15348
15532
  }
15349
15533
  }
15350
15534
  saveSnapshots(codeDelta.files.map((f) => ({ path: f.path, content: f.content })));
15535
+ const loginNudge = response.persisted === false ? " \u26A0\uFE0F Not logged in \u2014 this run was NOT saved and Verity has no memory of your project. Run `verity login` to unlock run history, trends, and cloud memory." : "";
15351
15536
  switch (decision) {
15352
15537
  case "FAIL": {
15353
15538
  writeIteration(iteration + 1, currentCommit, contentHash ?? void 0);
@@ -15420,6 +15605,9 @@ async function runAnalyze(opts, globals) {
15420
15605
  `);
15421
15606
  }
15422
15607
  process.stderr.write(`${BOLD}Fix these issues, then I will re-analyze automatically.${NC}
15608
+ `);
15609
+ if (loginNudge) process.stderr.write(`
15610
+ ${YELLOW}${loginNudge.trim()}${NC}
15423
15611
  `);
15424
15612
  process.exit(2);
15425
15613
  break;
@@ -15432,6 +15620,7 @@ async function runAnalyze(opts, globals) {
15432
15620
  const viewUrl = response.view_url ?? "";
15433
15621
  if (viewUrl) userSummary += ` Report: ${viewUrl}`;
15434
15622
  if (autoSeedNotice) userSummary = `${autoSeedNotice} ${userSummary}`;
15623
+ userSummary += loginNudge;
15435
15624
  printJsonCompact({ gate_decision: "PASS", systemMessage: userSummary });
15436
15625
  process.exit(0);
15437
15626
  break;
@@ -15443,12 +15632,13 @@ async function runAnalyze(opts, globals) {
15443
15632
  const viewUrl = response.view_url ?? "";
15444
15633
  if (viewUrl) userSummary += ` Report: ${viewUrl}`;
15445
15634
  if (autoSeedNotice) userSummary = `${autoSeedNotice} ${userSummary}`;
15635
+ userSummary += loginNudge;
15446
15636
  printJsonCompact({ gate_decision: "WARN", systemMessage: userSummary });
15447
15637
  process.exit(0);
15448
15638
  break;
15449
15639
  }
15450
15640
  default: {
15451
- const msg = autoSeedNotice ? `${autoSeedNotice} Verity: Analysis complete` : "Verity: Analysis complete";
15641
+ const msg = (autoSeedNotice ? `${autoSeedNotice} Verity: Analysis complete` : "Verity: Analysis complete") + loginNudge;
15452
15642
  printJsonCompact({ gate_decision: "PASS", systemMessage: msg });
15453
15643
  process.exit(0);
15454
15644
  }
@@ -15481,7 +15671,10 @@ function registerBaselineCommands(program2) {
15481
15671
  }
15482
15672
  }
15483
15673
  }
15484
- const result = captureBaseline({ sessionId, source });
15674
+ const authForScope = await resolveToken(program2.opts().token);
15675
+ const scopeToken = authForScope.ok ? authForScope.data.token : void 0;
15676
+ const scopeSession = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
15677
+ const result = captureBaseline({ sessionId: sessionScopeKey(scopeToken, scopeSession), source });
15485
15678
  logEvent("baseline_capture", {
15486
15679
  created: result.created,
15487
15680
  source: source ?? null,
@@ -15608,9 +15801,9 @@ async function runReview(opts, globals) {
15608
15801
 
15609
15802
  // src/commands/guard.ts
15610
15803
  var import_node_fs22 = require("node:fs");
15611
- var import_node_path16 = require("node:path");
15804
+ var import_node_path17 = require("node:path");
15612
15805
  var GUARD_BLOCK_CAP = 2;
15613
- var GUARD_ITER_FILE = (0, import_node_path16.join)(VERITY_DIR, ".guard-iteration");
15806
+ var GUARD_ITER_FILE = (0, import_node_path17.join)(VERITY_DIR, ".guard-iteration");
15614
15807
  function readPreToolUseStdin() {
15615
15808
  const empty = { command: "", cwd: null, sessionId: null };
15616
15809
  return new Promise((resolve) => {
@@ -15926,21 +16119,21 @@ function writeBlockMessage(moment, response) {
15926
16119
  // src/commands/init.ts
15927
16120
  var import_node_fs24 = require("node:fs");
15928
16121
  var import_promises13 = require("node:fs/promises");
15929
- var import_node_path18 = require("node:path");
15930
- var import_node_child_process9 = require("node:child_process");
16122
+ var import_node_path19 = require("node:path");
16123
+ var import_node_child_process11 = require("node:child_process");
15931
16124
  var readline2 = __toESM(require("node:readline/promises"));
15932
16125
 
15933
16126
  // src/commands/migrate.ts
15934
16127
  var import_node_fs23 = require("node:fs");
15935
- var import_node_path17 = require("node:path");
15936
- var import_node_child_process8 = require("node:child_process");
16128
+ var import_node_path18 = require("node:path");
16129
+ var import_node_child_process10 = require("node:child_process");
15937
16130
  var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
15938
16131
  function defaultNpmRemover(pkg) {
15939
- (0, import_node_child_process8.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
16132
+ (0, import_node_child_process10.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
15940
16133
  }
15941
16134
  function isGitTracked(cwd, relPath) {
15942
16135
  try {
15943
- (0, import_node_child_process8.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
16136
+ (0, import_node_child_process10.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
15944
16137
  return true;
15945
16138
  } catch {
15946
16139
  return false;
@@ -15948,7 +16141,7 @@ function isGitTracked(cwd, relPath) {
15948
16141
  }
15949
16142
  function isGitRepo(cwd) {
15950
16143
  try {
15951
- (0, import_node_child_process8.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
16144
+ (0, import_node_child_process10.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
15952
16145
  return true;
15953
16146
  } catch {
15954
16147
  return false;
@@ -15968,8 +16161,8 @@ async function runMigration(opts = {}) {
15968
16161
  return { actions, migrated: actions.length > 0 };
15969
16162
  }
15970
16163
  function migrateProjectDir(root, actions) {
15971
- const gateDir = (0, import_node_path17.join)(root, ".gate");
15972
- const verityDir = (0, import_node_path17.join)(root, ".verity");
16164
+ const gateDir = (0, import_node_path18.join)(root, ".gate");
16165
+ const verityDir = (0, import_node_path18.join)(root, ".verity");
15973
16166
  if ((0, import_node_fs23.existsSync)(gateDir) && !(0, import_node_fs23.existsSync)(verityDir)) {
15974
16167
  return migrateProjectDirRename(root, gateDir, verityDir, actions);
15975
16168
  }
@@ -15987,7 +16180,7 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
15987
16180
  );
15988
16181
  }
15989
16182
  try {
15990
- (0, import_node_child_process8.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
16183
+ (0, import_node_child_process10.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
15991
16184
  actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
15992
16185
  moved = true;
15993
16186
  } catch {
@@ -16023,11 +16216,11 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
16023
16216
  }
16024
16217
  function migrateGlobalCredentials(home, actions) {
16025
16218
  if (!home) return;
16026
- const gateCreds = (0, import_node_path17.join)(home, ".gate", "credentials");
16027
- const verityCreds = (0, import_node_path17.join)(home, ".verity", "credentials");
16219
+ const gateCreds = (0, import_node_path18.join)(home, ".gate", "credentials");
16220
+ const verityCreds = (0, import_node_path18.join)(home, ".verity", "credentials");
16028
16221
  if (!(0, import_node_fs23.existsSync)(gateCreds)) return;
16029
16222
  if (!(0, import_node_fs23.existsSync)(verityCreds)) {
16030
- (0, import_node_fs23.mkdirSync)((0, import_node_path17.join)(home, ".verity"), { recursive: true });
16223
+ (0, import_node_fs23.mkdirSync)((0, import_node_path18.join)(home, ".verity"), { recursive: true });
16031
16224
  moveFile(gateCreds, verityCreds);
16032
16225
  actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
16033
16226
  return;
@@ -16049,7 +16242,7 @@ async function migrateLegacyHooks(root, actions) {
16049
16242
  }
16050
16243
  }
16051
16244
  async function migrateClaudeMd(root, actions) {
16052
- const claudeMd = (0, import_node_path17.join)(root, "CLAUDE.md");
16245
+ const claudeMd = (0, import_node_path18.join)(root, "CLAUDE.md");
16053
16246
  const hadLegacyBlock = (0, import_node_fs23.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
16054
16247
  if (!hadLegacyBlock) return;
16055
16248
  try {
@@ -16060,13 +16253,13 @@ async function migrateClaudeMd(root, actions) {
16060
16253
  }
16061
16254
  }
16062
16255
  function migrateStandardFile(root, actions) {
16063
- const gateMd = (0, import_node_path17.join)(root, "GATE.md");
16064
- const verityMd = (0, import_node_path17.join)(root, "VERITY.md");
16256
+ const gateMd = (0, import_node_path18.join)(root, "GATE.md");
16257
+ const verityMd = (0, import_node_path18.join)(root, "VERITY.md");
16065
16258
  if (!(0, import_node_fs23.existsSync)(gateMd) || (0, import_node_fs23.existsSync)(verityMd)) return;
16066
16259
  let moved = false;
16067
16260
  if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
16068
16261
  try {
16069
- (0, import_node_child_process8.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
16262
+ (0, import_node_child_process10.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
16070
16263
  moved = true;
16071
16264
  } catch {
16072
16265
  }
@@ -16122,7 +16315,7 @@ function readFileSyncSafe(path) {
16122
16315
  }
16123
16316
  function hasStagedChanges(root) {
16124
16317
  try {
16125
- (0, import_node_child_process8.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
16318
+ (0, import_node_child_process10.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
16126
16319
  return false;
16127
16320
  } catch {
16128
16321
  return true;
@@ -16149,15 +16342,15 @@ function moveFile(from, to) {
16149
16342
  function carryLegacyContents(gateDir, verityDir) {
16150
16343
  let copied = 0;
16151
16344
  const walk = (relDir) => {
16152
- const srcDir = (0, import_node_path17.join)(gateDir, relDir);
16345
+ const srcDir = (0, import_node_path18.join)(gateDir, relDir);
16153
16346
  for (const entry of (0, import_node_fs23.readdirSync)(srcDir)) {
16154
- const rel = relDir ? (0, import_node_path17.join)(relDir, entry) : entry;
16155
- const src = (0, import_node_path17.join)(gateDir, rel);
16156
- const dest = (0, import_node_path17.join)(verityDir, rel);
16347
+ const rel = relDir ? (0, import_node_path18.join)(relDir, entry) : entry;
16348
+ const src = (0, import_node_path18.join)(gateDir, rel);
16349
+ const dest = (0, import_node_path18.join)(verityDir, rel);
16157
16350
  if ((0, import_node_fs23.statSync)(src).isDirectory()) {
16158
16351
  walk(rel);
16159
16352
  } else if (!(0, import_node_fs23.existsSync)(dest)) {
16160
- (0, import_node_fs23.mkdirSync)((0, import_node_path17.dirname)(dest), { recursive: true });
16353
+ (0, import_node_fs23.mkdirSync)((0, import_node_path18.dirname)(dest), { recursive: true });
16161
16354
  (0, import_node_fs23.cpSync)(src, dest);
16162
16355
  copied++;
16163
16356
  }
@@ -16167,22 +16360,22 @@ function carryLegacyContents(gateDir, verityDir) {
16167
16360
  return copied;
16168
16361
  }
16169
16362
  async function needsMigration(root = repoRoot()) {
16170
- const gateDir = (0, import_node_path17.join)(root, ".gate");
16171
- const verityDir = (0, import_node_path17.join)(root, ".verity");
16363
+ const gateDir = (0, import_node_path18.join)(root, ".gate");
16364
+ const verityDir = (0, import_node_path18.join)(root, ".verity");
16172
16365
  if ((0, import_node_fs23.existsSync)(gateDir) && !(0, import_node_fs23.existsSync)(verityDir)) return true;
16173
16366
  if ((0, import_node_fs23.existsSync)(gateDir) && (0, import_node_fs23.existsSync)(verityDir)) {
16174
- if ((0, import_node_fs23.existsSync)((0, import_node_path17.join)(gateDir, "credentials")) && !(0, import_node_fs23.existsSync)((0, import_node_path17.join)(verityDir, "credentials"))) {
16367
+ if ((0, import_node_fs23.existsSync)((0, import_node_path18.join)(gateDir, "credentials")) && !(0, import_node_fs23.existsSync)((0, import_node_path18.join)(verityDir, "credentials"))) {
16175
16368
  return true;
16176
16369
  }
16177
- if ((0, import_node_fs23.existsSync)((0, import_node_path17.join)(gateDir, "memory")) && !(0, import_node_fs23.existsSync)((0, import_node_path17.join)(verityDir, "memory"))) {
16370
+ if ((0, import_node_fs23.existsSync)((0, import_node_path18.join)(gateDir, "memory")) && !(0, import_node_fs23.existsSync)((0, import_node_path18.join)(verityDir, "memory"))) {
16178
16371
  return true;
16179
16372
  }
16180
16373
  }
16181
- const claudeMd = (0, import_node_path17.join)(root, "CLAUDE.md");
16374
+ const claudeMd = (0, import_node_path18.join)(root, "CLAUDE.md");
16182
16375
  if ((0, import_node_fs23.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
16183
16376
  return true;
16184
16377
  }
16185
- if ((0, import_node_fs23.existsSync)((0, import_node_path17.join)(root, "GATE.md")) && !(0, import_node_fs23.existsSync)((0, import_node_path17.join)(root, "VERITY.md"))) {
16378
+ if ((0, import_node_fs23.existsSync)((0, import_node_path18.join)(root, "GATE.md")) && !(0, import_node_fs23.existsSync)((0, import_node_path18.join)(root, "VERITY.md"))) {
16186
16379
  return true;
16187
16380
  }
16188
16381
  if (await hasLegacyHooksAt(root)) return true;
@@ -16218,15 +16411,30 @@ async function promptYes(question) {
16218
16411
  rl.close();
16219
16412
  }
16220
16413
  }
16221
- async function runOptionalAuth() {
16414
+ async function runOptionalAuth(serviceUrl) {
16222
16415
  const existing = await resolveToken();
16223
16416
  if (existing.ok) {
16224
- printInfo("Already authenticated \u2014 results will upload to the Verity service. \u2713");
16225
- return;
16417
+ const who = await whoami(existing.data.token, serviceUrl);
16418
+ if (who.ok && who.data.logged_in) {
16419
+ printInfo(`Logged in as ${who.data.email ?? `user #${who.data.user_id}`} \u2713 \u2014 runs & memory sync to Verity.`);
16420
+ return;
16421
+ }
16422
+ if (!who.ok) {
16423
+ if (existing.data.userId != null) {
16424
+ printInfo(`Logged in as ${existing.data.email ?? `user #${existing.data.userId}`} (cached \u2014 could not reach the Verity service). \u2713`);
16425
+ } else {
16426
+ printInfo("Could not confirm your login state with the service; continuing with your existing token.");
16427
+ }
16428
+ return;
16429
+ }
16430
+ console.log("");
16431
+ printWarn("You are NOT logged in \u2014 this project has only an anonymous token.");
16432
+ printInfo(" The gate still runs, but no runs are saved and Verity keeps no memory of this project.");
16433
+ printInfo(" Log in below to unlock run history, trends, and cloud memory (strongly recommended).");
16226
16434
  }
16227
16435
  let remote = "";
16228
16436
  try {
16229
- remote = (0, import_node_child_process9.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
16437
+ remote = (0, import_node_child_process11.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
16230
16438
  } catch {
16231
16439
  }
16232
16440
  const localOnlyNote = () => {
@@ -16257,12 +16465,12 @@ async function runOptionalAuth() {
16257
16465
  localOnlyNote();
16258
16466
  return;
16259
16467
  }
16260
- const projectName = parseRemote(remote)?.repo ?? (0, import_node_path18.basename)(process.cwd());
16468
+ const projectName = parseRemote(remote)?.repo ?? (0, import_node_path19.basename)(process.cwd());
16261
16469
  printInfo("Authenticating with GitHub\u2026");
16262
- const result = await registerProject({ projectName, remote, serviceUrl: DEFAULT_SERVICE_URL });
16470
+ const result = await registerProject({ projectName, remote, serviceUrl });
16263
16471
  if (result.ok) {
16264
- printInfo(`Project registered: ${result.data.projectId} \u2713`);
16265
- if (result.data.email) printInfo(` Authenticated as: ${result.data.email}`);
16472
+ const who = result.data.email ?? (result.data.userId != null ? `user #${result.data.userId}` : null);
16473
+ printInfo(`Logged in${who ? ` as ${who}` : ""} \u2713 \u2014 runs, history, and cloud memory now sync to Verity.`);
16266
16474
  } else {
16267
16475
  printWarn(`Authentication did not complete: ${result.error}`);
16268
16476
  localOnlyNote();
@@ -16270,15 +16478,15 @@ async function runOptionalAuth() {
16270
16478
  }
16271
16479
  function resolveDataDir() {
16272
16480
  const candidates = [
16273
- (0, import_node_path18.join)(__dirname, "..", "data"),
16481
+ (0, import_node_path19.join)(__dirname, "..", "data"),
16274
16482
  // installed: node_modules/@codacy/verity-cli/data
16275
- (0, import_node_path18.join)(__dirname, "..", "..", "data"),
16483
+ (0, import_node_path19.join)(__dirname, "..", "..", "data"),
16276
16484
  // edge case: nested resolution
16277
- (0, import_node_path18.join)(process.cwd(), "cli", "data")
16485
+ (0, import_node_path19.join)(process.cwd(), "cli", "data")
16278
16486
  // local dev: running from repo root
16279
16487
  ];
16280
16488
  for (const candidate of candidates) {
16281
- if ((0, import_node_fs24.existsSync)((0, import_node_path18.join)(candidate, "skills"))) {
16489
+ if ((0, import_node_fs24.existsSync)((0, import_node_path19.join)(candidate, "skills"))) {
16282
16490
  return candidate;
16283
16491
  }
16284
16492
  }
@@ -16322,30 +16530,30 @@ function registerInitCommand(program2) {
16322
16530
  }
16323
16531
  printInfo(` Node.js ${nodeVersion} \u2713`);
16324
16532
  try {
16325
- const gitVersion = (0, import_node_child_process9.execSync)("git --version", { encoding: "utf-8" }).trim();
16533
+ const gitVersion = (0, import_node_child_process11.execSync)("git --version", { encoding: "utf-8" }).trim();
16326
16534
  printInfo(` ${gitVersion} \u2713`);
16327
16535
  } catch {
16328
16536
  printError("git is required but not installed. Install from https://git-scm.com");
16329
16537
  process.exit(1);
16330
16538
  }
16331
16539
  try {
16332
- (0, import_node_child_process9.execSync)("which claude", { encoding: "utf-8" });
16540
+ (0, import_node_child_process11.execSync)("which claude", { encoding: "utf-8" });
16333
16541
  printInfo(" Claude Code \u2713");
16334
16542
  } catch {
16335
16543
  printWarn(" Claude Code not found \u2014 hooks will be configured but need Claude Code to run.");
16336
16544
  }
16337
16545
  try {
16338
- (0, import_node_child_process9.execSync)("which codacy-analysis", { encoding: "utf-8", stdio: "pipe" });
16546
+ (0, import_node_child_process11.execSync)("which codacy-analysis", { encoding: "utf-8", stdio: "pipe" });
16339
16547
  printInfo(" @codacy/analysis-cli \u2713");
16340
16548
  } catch {
16341
16549
  printInfo(" Installing @codacy/analysis-cli...");
16342
16550
  try {
16343
- (0, import_node_child_process9.execSync)("npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "pipe", timeout: 12e4 });
16551
+ (0, import_node_child_process11.execSync)("npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "pipe", timeout: 12e4 });
16344
16552
  printInfo(" @codacy/analysis-cli installed \u2713");
16345
16553
  } catch {
16346
16554
  try {
16347
16555
  printWarn(" Retrying with sudo...");
16348
- (0, import_node_child_process9.execSync)("sudo npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
16556
+ (0, import_node_child_process11.execSync)("sudo npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
16349
16557
  printInfo(" @codacy/analysis-cli installed \u2713");
16350
16558
  } catch {
16351
16559
  printWarn(" Could not install @codacy/analysis-cli automatically.");
@@ -16357,20 +16565,20 @@ function registerInitCommand(program2) {
16357
16565
  console.log("");
16358
16566
  printInfo("Installing skills...");
16359
16567
  const dataDir = resolveDataDir();
16360
- const skillsSource = (0, import_node_path18.join)(dataDir, "skills");
16568
+ const skillsSource = (0, import_node_path19.join)(dataDir, "skills");
16361
16569
  const skillsDest = ".claude/skills";
16362
16570
  const skills = ["verity-setup", "verity-analyze", "verity-status", "verity-feedback", "verity-learn", "verity-memory", "verity-insights", "verity-reflect"];
16363
16571
  let skillsInstalled = 0;
16364
16572
  for (const skill of skills) {
16365
- const src = (0, import_node_path18.join)(skillsSource, skill);
16366
- const dest = (0, import_node_path18.join)(skillsDest, skill);
16573
+ const src = (0, import_node_path19.join)(skillsSource, skill);
16574
+ const dest = (0, import_node_path19.join)(skillsDest, skill);
16367
16575
  if (!(0, import_node_fs24.existsSync)(src)) {
16368
16576
  printWarn(` Skill data not found: ${skill}`);
16369
16577
  continue;
16370
16578
  }
16371
16579
  if ((0, import_node_fs24.existsSync)(dest) && !force) {
16372
- const srcSkill = (0, import_node_path18.join)(src, "SKILL.md");
16373
- const destSkill = (0, import_node_path18.join)(dest, "SKILL.md");
16580
+ const srcSkill = (0, import_node_path19.join)(src, "SKILL.md");
16581
+ const destSkill = (0, import_node_path19.join)(dest, "SKILL.md");
16374
16582
  if ((0, import_node_fs24.existsSync)(destSkill)) {
16375
16583
  try {
16376
16584
  const srcContent = await (0, import_promises13.readFile)(srcSkill, "utf-8");
@@ -16408,11 +16616,13 @@ function registerInitCommand(program2) {
16408
16616
  } catch (err) {
16409
16617
  printWarn(` Could not update CLAUDE.md: ${err.message}`);
16410
16618
  }
16411
- const globalVerityDir = (0, import_node_path18.join)(process.env.HOME ?? "", ".verity");
16619
+ const globalVerityDir = (0, import_node_path19.join)(process.env.HOME ?? "", ".verity");
16412
16620
  await (0, import_promises13.mkdir)(globalVerityDir, { recursive: true });
16413
16621
  console.log("");
16414
16622
  try {
16415
- await runOptionalAuth();
16623
+ const globals = program2.opts();
16624
+ const urlResult = await resolveServiceUrl(globals.serviceUrl);
16625
+ await runOptionalAuth(urlResult.ok ? urlResult.data : DEFAULT_SERVICE_URL);
16416
16626
  } catch (err) {
16417
16627
  printWarn(`Authentication step skipped: ${err.message}`);
16418
16628
  }
@@ -16439,7 +16649,7 @@ function registerInitCommand(program2) {
16439
16649
 
16440
16650
  // src/commands/uninstall.ts
16441
16651
  var import_node_fs25 = require("node:fs");
16442
- var import_node_path19 = require("node:path");
16652
+ var import_node_path20 = require("node:path");
16443
16653
  var SKILL_NAMES = [
16444
16654
  "verity-setup",
16445
16655
  "verity-analyze",
@@ -16458,7 +16668,7 @@ function registerUninstallCommand(program2) {
16458
16668
  const actions = [];
16459
16669
  const skillsRoot = projectPath(".claude/skills");
16460
16670
  for (const name of SKILL_NAMES) {
16461
- const dir = (0, import_node_path19.join)(skillsRoot, name);
16671
+ const dir = (0, import_node_path20.join)(skillsRoot, name);
16462
16672
  if ((0, import_node_fs25.existsSync)(dir)) {
16463
16673
  actions.push({
16464
16674
  label: `Remove .claude/skills/${name}/`,
@@ -16504,7 +16714,7 @@ function registerUninstallCommand(program2) {
16504
16714
  }
16505
16715
  });
16506
16716
  const home = process.env.HOME ?? "";
16507
- const globalVerityDir = (0, import_node_path19.join)(home, ".verity");
16717
+ const globalVerityDir = (0, import_node_path20.join)(home, ".verity");
16508
16718
  if (purgeGlobal && (0, import_node_fs25.existsSync)(globalVerityDir)) {
16509
16719
  actions.push({
16510
16720
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
@@ -16703,7 +16913,7 @@ function registerTaskCommands(program2) {
16703
16913
 
16704
16914
  // src/commands/reset.ts
16705
16915
  var import_node_fs26 = require("node:fs");
16706
- var import_node_path20 = require("node:path");
16916
+ var import_node_path21 = require("node:path");
16707
16917
  function registerResetCommand(program2) {
16708
16918
  program2.command("reset").description("Close the current task and clear transient state").option("--keep-task", "Only purge caches; leave the current task open").option("--all", "Also purge diagnostic logs (.verity/.logs/)").action(async (opts) => {
16709
16919
  const globals = program2.opts();
@@ -16744,7 +16954,7 @@ function registerResetCommand(program2) {
16744
16954
  for (const entry of (0, import_node_fs26.readdirSync)(cacheDir)) {
16745
16955
  if (entry.startsWith("pending-")) {
16746
16956
  try {
16747
- (0, import_node_fs26.unlinkSync)((0, import_node_path20.join)(cacheDir, entry));
16957
+ (0, import_node_fs26.unlinkSync)((0, import_node_path21.join)(cacheDir, entry));
16748
16958
  purged++;
16749
16959
  } catch {
16750
16960
  }
@@ -16771,7 +16981,7 @@ function registerResetCommand(program2) {
16771
16981
  if ((0, import_node_fs26.existsSync)(logsDir)) {
16772
16982
  for (const entry of (0, import_node_fs26.readdirSync)(logsDir)) {
16773
16983
  try {
16774
- (0, import_node_fs26.unlinkSync)((0, import_node_path20.join)(logsDir, entry));
16984
+ (0, import_node_fs26.unlinkSync)((0, import_node_path21.join)(logsDir, entry));
16775
16985
  } catch {
16776
16986
  }
16777
16987
  }
@@ -16782,24 +16992,6 @@ function registerResetCommand(program2) {
16782
16992
  });
16783
16993
  }
16784
16994
 
16785
- // src/lib/run-mode.ts
16786
- function parseAutonomousEnv(raw) {
16787
- if (raw === void 0) return void 0;
16788
- const v = raw.trim().toLowerCase();
16789
- if (v === "") return void 0;
16790
- if (v === "0" || v === "false" || v === "off" || v === "no") return false;
16791
- return true;
16792
- }
16793
- function resolveRunMode(inputs = {}) {
16794
- if (inputs.autonomousFlag === true) return "autonomous";
16795
- if (inputs.autonomousFlag === false) return "interactive";
16796
- const env = inputs.env ?? process.env;
16797
- const envDecision = parseAutonomousEnv(env.VERITY_AUTONOMOUS);
16798
- if (envDecision !== void 0) return envDecision ? "autonomous" : "interactive";
16799
- const isTTY = inputs.isTTY ?? Boolean(process.stdin?.isTTY);
16800
- return isTTY ? "interactive" : "autonomous";
16801
- }
16802
-
16803
16995
  // src/commands/reflect.ts
16804
16996
  function registerReflectCommand(program2) {
16805
16997
  program2.command("reflect").description("Capture learnings \u2014 auto-extract or submit a human reflection").option("--user-input <text>", "The reflection to record (the agent-drafted or user-confirmed text)").option("--kind <kind>", "Node kind (decision, gotcha, pattern, security, quality, intent, domain, integration)", "gotcha").option("--task-id <id>", "Task to reflect on (defaults to current task)").option("--autonomous", "Record the drafted reflection without a confirm step (auto-detected from TTY / VERITY_AUTONOMOUS when omitted)").action(async (opts) => {
@@ -17161,8 +17353,9 @@ function registerTelemetryCommands(program2) {
17161
17353
  }
17162
17354
 
17163
17355
  // src/cli.ts
17164
- program.name("verity").description("CLI for Verity quality gate service").version("0.27.1").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
17356
+ program.name("verity").description("CLI for Verity quality gate service").version("0.27.2-experimental.5fb996a").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
17165
17357
  registerAuthCommands(program);
17358
+ registerLoginCommand(program);
17166
17359
  registerHooksCommands(program);
17167
17360
  registerIntentCommands(program);
17168
17361
  registerStandardCommands(program);