@codacy/verity-cli 0.32.6-experimental.df0a578 → 0.32.7

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
@@ -6975,10 +6975,10 @@ var require_resolve_block_map = __commonJS({
6975
6975
  let offset = bm.offset;
6976
6976
  let commentEnd = null;
6977
6977
  for (const collItem of bm.items) {
6978
- const { start, key, sep: sep4, value } = collItem;
6978
+ const { start, key, sep: sep3, value } = collItem;
6979
6979
  const keyProps = resolveProps.resolveProps(start, {
6980
6980
  indicator: "explicit-key-ind",
6981
- next: key ?? sep4?.[0],
6981
+ next: key ?? sep3?.[0],
6982
6982
  offset,
6983
6983
  onError,
6984
6984
  parentIndent: bm.indent,
@@ -6992,7 +6992,7 @@ var require_resolve_block_map = __commonJS({
6992
6992
  else if ("indent" in key && key.indent !== bm.indent)
6993
6993
  onError(offset, "BAD_INDENT", startColMsg);
6994
6994
  }
6995
- if (!keyProps.anchor && !keyProps.tag && !sep4) {
6995
+ if (!keyProps.anchor && !keyProps.tag && !sep3) {
6996
6996
  commentEnd = keyProps.end;
6997
6997
  if (keyProps.comment) {
6998
6998
  if (map.comment)
@@ -7016,7 +7016,7 @@ var require_resolve_block_map = __commonJS({
7016
7016
  ctx.atKey = false;
7017
7017
  if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
7018
7018
  onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
7019
- const valueProps = resolveProps.resolveProps(sep4 ?? [], {
7019
+ const valueProps = resolveProps.resolveProps(sep3 ?? [], {
7020
7020
  indicator: "map-value-ind",
7021
7021
  next: value,
7022
7022
  offset: keyNode.range[2],
@@ -7032,7 +7032,7 @@ var require_resolve_block_map = __commonJS({
7032
7032
  if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
7033
7033
  onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
7034
7034
  }
7035
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep4, null, valueProps, onError);
7035
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep3, null, valueProps, onError);
7036
7036
  if (ctx.schema.compat)
7037
7037
  utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
7038
7038
  offset = valueNode.range[2];
@@ -7123,7 +7123,7 @@ var require_resolve_end = __commonJS({
7123
7123
  let comment = "";
7124
7124
  if (end) {
7125
7125
  let hasSpace = false;
7126
- let sep4 = "";
7126
+ let sep3 = "";
7127
7127
  for (const token of end) {
7128
7128
  const { source, type } = token;
7129
7129
  switch (type) {
@@ -7137,13 +7137,13 @@ var require_resolve_end = __commonJS({
7137
7137
  if (!comment)
7138
7138
  comment = cb;
7139
7139
  else
7140
- comment += sep4 + cb;
7141
- sep4 = "";
7140
+ comment += sep3 + cb;
7141
+ sep3 = "";
7142
7142
  break;
7143
7143
  }
7144
7144
  case "newline":
7145
7145
  if (comment)
7146
- sep4 += source;
7146
+ sep3 += source;
7147
7147
  hasSpace = true;
7148
7148
  break;
7149
7149
  default:
@@ -7186,18 +7186,18 @@ var require_resolve_flow_collection = __commonJS({
7186
7186
  let offset = fc.offset + fc.start.source.length;
7187
7187
  for (let i = 0; i < fc.items.length; ++i) {
7188
7188
  const collItem = fc.items[i];
7189
- const { start, key, sep: sep4, value } = collItem;
7189
+ const { start, key, sep: sep3, value } = collItem;
7190
7190
  const props = resolveProps.resolveProps(start, {
7191
7191
  flow: fcName,
7192
7192
  indicator: "explicit-key-ind",
7193
- next: key ?? sep4?.[0],
7193
+ next: key ?? sep3?.[0],
7194
7194
  offset,
7195
7195
  onError,
7196
7196
  parentIndent: fc.indent,
7197
7197
  startOnNewline: false
7198
7198
  });
7199
7199
  if (!props.found) {
7200
- if (!props.anchor && !props.tag && !sep4 && !value) {
7200
+ if (!props.anchor && !props.tag && !sep3 && !value) {
7201
7201
  if (i === 0 && props.comma)
7202
7202
  onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
7203
7203
  else if (i < fc.items.length - 1)
@@ -7251,8 +7251,8 @@ var require_resolve_flow_collection = __commonJS({
7251
7251
  }
7252
7252
  }
7253
7253
  }
7254
- if (!isMap && !sep4 && !props.found) {
7255
- const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep4, null, props, onError);
7254
+ if (!isMap && !sep3 && !props.found) {
7255
+ const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep3, null, props, onError);
7256
7256
  coll.items.push(valueNode);
7257
7257
  offset = valueNode.range[2];
7258
7258
  if (isBlock(value))
@@ -7264,7 +7264,7 @@ var require_resolve_flow_collection = __commonJS({
7264
7264
  if (isBlock(key))
7265
7265
  onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
7266
7266
  ctx.atKey = false;
7267
- const valueProps = resolveProps.resolveProps(sep4 ?? [], {
7267
+ const valueProps = resolveProps.resolveProps(sep3 ?? [], {
7268
7268
  flow: fcName,
7269
7269
  indicator: "map-value-ind",
7270
7270
  next: value,
@@ -7275,8 +7275,8 @@ var require_resolve_flow_collection = __commonJS({
7275
7275
  });
7276
7276
  if (valueProps.found) {
7277
7277
  if (!isMap && !props.found && ctx.options.strict) {
7278
- if (sep4)
7279
- for (const st of sep4) {
7278
+ if (sep3)
7279
+ for (const st of sep3) {
7280
7280
  if (st === valueProps.found)
7281
7281
  break;
7282
7282
  if (st.type === "newline") {
@@ -7293,7 +7293,7 @@ var require_resolve_flow_collection = __commonJS({
7293
7293
  else
7294
7294
  onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
7295
7295
  }
7296
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep4, null, valueProps, onError) : null;
7296
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep3, null, valueProps, onError) : null;
7297
7297
  if (valueNode) {
7298
7298
  if (isBlock(value))
7299
7299
  onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
@@ -7473,7 +7473,7 @@ var require_resolve_block_scalar = __commonJS({
7473
7473
  chompStart = i + 1;
7474
7474
  }
7475
7475
  let value = "";
7476
- let sep4 = "";
7476
+ let sep3 = "";
7477
7477
  let prevMoreIndented = false;
7478
7478
  for (let i = 0; i < contentStart; ++i)
7479
7479
  value += lines[i][0].slice(trimIndent) + "\n";
@@ -7490,24 +7490,24 @@ var require_resolve_block_scalar = __commonJS({
7490
7490
  indent = "";
7491
7491
  }
7492
7492
  if (type === Scalar.Scalar.BLOCK_LITERAL) {
7493
- value += sep4 + indent.slice(trimIndent) + content;
7494
- sep4 = "\n";
7493
+ value += sep3 + indent.slice(trimIndent) + content;
7494
+ sep3 = "\n";
7495
7495
  } else if (indent.length > trimIndent || content[0] === " ") {
7496
- if (sep4 === " ")
7497
- sep4 = "\n";
7498
- else if (!prevMoreIndented && sep4 === "\n")
7499
- sep4 = "\n\n";
7500
- value += sep4 + indent.slice(trimIndent) + content;
7501
- sep4 = "\n";
7496
+ if (sep3 === " ")
7497
+ sep3 = "\n";
7498
+ else if (!prevMoreIndented && sep3 === "\n")
7499
+ sep3 = "\n\n";
7500
+ value += sep3 + indent.slice(trimIndent) + content;
7501
+ sep3 = "\n";
7502
7502
  prevMoreIndented = true;
7503
7503
  } else if (content === "") {
7504
- if (sep4 === "\n")
7504
+ if (sep3 === "\n")
7505
7505
  value += "\n";
7506
7506
  else
7507
- sep4 = "\n";
7507
+ sep3 = "\n";
7508
7508
  } else {
7509
- value += sep4 + content;
7510
- sep4 = " ";
7509
+ value += sep3 + content;
7510
+ sep3 = " ";
7511
7511
  prevMoreIndented = false;
7512
7512
  }
7513
7513
  }
@@ -7689,25 +7689,25 @@ var require_resolve_flow_scalar = __commonJS({
7689
7689
  if (!match)
7690
7690
  return source;
7691
7691
  let res = match[1];
7692
- let sep4 = " ";
7692
+ let sep3 = " ";
7693
7693
  let pos = first.lastIndex;
7694
7694
  line.lastIndex = pos;
7695
7695
  while (match = line.exec(source)) {
7696
7696
  if (match[1] === "") {
7697
- if (sep4 === "\n")
7698
- res += sep4;
7697
+ if (sep3 === "\n")
7698
+ res += sep3;
7699
7699
  else
7700
- sep4 = "\n";
7700
+ sep3 = "\n";
7701
7701
  } else {
7702
- res += sep4 + match[1];
7703
- sep4 = " ";
7702
+ res += sep3 + match[1];
7703
+ sep3 = " ";
7704
7704
  }
7705
7705
  pos = line.lastIndex;
7706
7706
  }
7707
7707
  const last = /[ \t]*(.*)/sy;
7708
7708
  last.lastIndex = pos;
7709
7709
  match = last.exec(source);
7710
- return res + sep4 + (match?.[1] ?? "");
7710
+ return res + sep3 + (match?.[1] ?? "");
7711
7711
  }
7712
7712
  function doubleQuotedValue(source, onError) {
7713
7713
  let res = "";
@@ -8514,14 +8514,14 @@ var require_cst_stringify = __commonJS({
8514
8514
  }
8515
8515
  }
8516
8516
  }
8517
- function stringifyItem({ start, key, sep: sep4, value }) {
8517
+ function stringifyItem({ start, key, sep: sep3, value }) {
8518
8518
  let res = "";
8519
8519
  for (const st of start)
8520
8520
  res += st.source;
8521
8521
  if (key)
8522
8522
  res += stringifyToken(key);
8523
- if (sep4)
8524
- for (const st of sep4)
8523
+ if (sep3)
8524
+ for (const st of sep3)
8525
8525
  res += st.source;
8526
8526
  if (value)
8527
8527
  res += stringifyToken(value);
@@ -9671,18 +9671,18 @@ var require_parser = __commonJS({
9671
9671
  if (this.type === "map-value-ind") {
9672
9672
  const prev = getPrevProps(this.peek(2));
9673
9673
  const start = getFirstKeyStartProps(prev);
9674
- let sep4;
9674
+ let sep3;
9675
9675
  if (scalar.end) {
9676
- sep4 = scalar.end;
9677
- sep4.push(this.sourceToken);
9676
+ sep3 = scalar.end;
9677
+ sep3.push(this.sourceToken);
9678
9678
  delete scalar.end;
9679
9679
  } else
9680
- sep4 = [this.sourceToken];
9680
+ sep3 = [this.sourceToken];
9681
9681
  const map = {
9682
9682
  type: "block-map",
9683
9683
  offset: scalar.offset,
9684
9684
  indent: scalar.indent,
9685
- items: [{ start, key: scalar, sep: sep4 }]
9685
+ items: [{ start, key: scalar, sep: sep3 }]
9686
9686
  };
9687
9687
  this.onKeyLine = true;
9688
9688
  this.stack[this.stack.length - 1] = map;
@@ -9835,15 +9835,15 @@ var require_parser = __commonJS({
9835
9835
  } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
9836
9836
  const start2 = getFirstKeyStartProps(it.start);
9837
9837
  const key = it.key;
9838
- const sep4 = it.sep;
9839
- sep4.push(this.sourceToken);
9838
+ const sep3 = it.sep;
9839
+ sep3.push(this.sourceToken);
9840
9840
  delete it.key;
9841
9841
  delete it.sep;
9842
9842
  this.stack.push({
9843
9843
  type: "block-map",
9844
9844
  offset: this.offset,
9845
9845
  indent: this.indent,
9846
- items: [{ start: start2, key, sep: sep4 }]
9846
+ items: [{ start: start2, key, sep: sep3 }]
9847
9847
  });
9848
9848
  } else if (start.length > 0) {
9849
9849
  it.sep = it.sep.concat(start, this.sourceToken);
@@ -10037,13 +10037,13 @@ var require_parser = __commonJS({
10037
10037
  const prev = getPrevProps(parent);
10038
10038
  const start = getFirstKeyStartProps(prev);
10039
10039
  fixFlowSeqItems(fc);
10040
- const sep4 = fc.end.splice(1, fc.end.length);
10041
- sep4.push(this.sourceToken);
10040
+ const sep3 = fc.end.splice(1, fc.end.length);
10041
+ sep3.push(this.sourceToken);
10042
10042
  const map = {
10043
10043
  type: "block-map",
10044
10044
  offset: fc.offset,
10045
10045
  indent: fc.indent,
10046
- items: [{ start, key: fc, sep: sep4 }]
10046
+ items: [{ start, key: fc, sep: sep3 }]
10047
10047
  };
10048
10048
  this.onKeyLine = true;
10049
10049
  this.stack[this.stack.length - 1] = map;
@@ -10413,6 +10413,7 @@ var MAX_ITERATIONS = 2;
10413
10413
  var MAX_SPEC_FILES = 6;
10414
10414
  var MAX_SPEC_FILE_BYTES = 512e3;
10415
10415
  var MAX_TOTAL_SPEC_BYTES = 512e3;
10416
+ var MAX_EXPLICIT_SPEC_FILE_BYTES = 10240;
10416
10417
  var MAX_PLAN_FILES = 3;
10417
10418
  var MAX_PLAN_FILE_BYTES = 512e3;
10418
10419
  var MAX_INTENT_CHARS = 2e3;
@@ -10523,7 +10524,7 @@ var SECURITY_PATTERNS = [
10523
10524
  /Dockerfile/
10524
10525
  ];
10525
10526
  var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
10526
- var DEFAULT_SERVICE_URL = "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1".length > 0 ? "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1" : PROD_SERVICE_URL;
10527
+ var DEFAULT_SERVICE_URL = "".length > 0 ? "" : PROD_SERVICE_URL;
10527
10528
  var GITHUB_CLIENT_ID = "Iv23li88HxAi3ZrbYzWh";
10528
10529
  var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
10529
10530
  var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
@@ -10969,8 +10970,8 @@ function filterReviewable(files) {
10969
10970
  const ext = (0, import_node_path3.extname)(f).slice(1);
10970
10971
  if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
10971
10972
  if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
10972
- const basename4 = f.split("/").pop() ?? "";
10973
- if (REVIEWABLE_FILENAMES.has(basename4)) return true;
10973
+ const basename5 = f.split("/").pop() ?? "";
10974
+ if (REVIEWABLE_FILENAMES.has(basename5)) return true;
10974
10975
  if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
10975
10976
  return false;
10976
10977
  });
@@ -11533,7 +11534,7 @@ function startSpinner(label2, opts = {}) {
11533
11534
  }
11534
11535
 
11535
11536
  // src/lib/provider-auth.ts
11536
- var sleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
11537
+ var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
11537
11538
  var form = (fields) => new URLSearchParams(fields).toString();
11538
11539
  async function githubAccountId(owner) {
11539
11540
  try {
@@ -12800,12 +12801,50 @@ function resolveGuardMoments(explicit) {
12800
12801
  }
12801
12802
 
12802
12803
  // src/lib/plugin-ownership.ts
12803
- var import_node_fs6 = require("node:fs");
12804
- var import_node_os2 = require("node:os");
12804
+ var import_node_fs7 = require("node:fs");
12805
+
12806
+ // src/lib/which.ts
12807
+ var import_node_fs5 = require("node:fs");
12805
12808
  var import_node_path7 = require("node:path");
12809
+ function executableExtensions(platform, pathext) {
12810
+ if (platform !== "win32") return [""];
12811
+ const raw = (pathext ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
12812
+ const out = [];
12813
+ for (const ext of raw) {
12814
+ const lower = ext.toLowerCase();
12815
+ if (!out.includes(lower)) out.push(lower);
12816
+ if (!out.includes(ext)) out.push(ext);
12817
+ }
12818
+ return out;
12819
+ }
12820
+ function whichSync(bin, opts = {}) {
12821
+ const platform = opts.platform ?? process.platform;
12822
+ const rawPath = opts.path ?? process.env.PATH ?? "";
12823
+ if (!rawPath) return null;
12824
+ const exts = executableExtensions(platform, opts.pathext ?? process.env.PATHEXT);
12825
+ const real = opts.real ?? true;
12826
+ for (const dir of rawPath.split(import_node_path7.delimiter)) {
12827
+ if (!dir) continue;
12828
+ for (const ext of exts) {
12829
+ const candidate = (0, import_node_path7.join)(dir, bin + ext);
12830
+ try {
12831
+ if (!(0, import_node_fs5.statSync)(candidate).isFile()) continue;
12832
+ if (platform !== "win32") (0, import_node_fs5.accessSync)(candidate, import_node_fs5.constants.X_OK);
12833
+ return real ? (0, import_node_fs5.realpathSync)(candidate) : candidate;
12834
+ } catch {
12835
+ continue;
12836
+ }
12837
+ }
12838
+ }
12839
+ return null;
12840
+ }
12841
+
12842
+ // src/lib/plugin-ownership.ts
12843
+ var import_node_os2 = require("node:os");
12844
+ var import_node_path8 = require("node:path");
12806
12845
 
12807
12846
  // src/lib/stderr-log.ts
12808
- var import_node_fs5 = require("node:fs");
12847
+ var import_node_fs6 = require("node:fs");
12809
12848
  var TOKEN_RE2 = /verity_[0-9a-f]{16,}/g;
12810
12849
  var ANSI_RE = /\u001b\[[0-?]*[ -/]*[@-~]/g;
12811
12850
  function scrub(s) {
@@ -12818,9 +12857,9 @@ function append(text) {
12818
12857
  try {
12819
12858
  const dir = projectPath(DEBUG_LOG_DIR);
12820
12859
  const file = projectPath(STDERR_LOG_FILE);
12821
- (0, import_node_fs5.mkdirSync)(dir, { recursive: true });
12860
+ (0, import_node_fs6.mkdirSync)(dir, { recursive: true });
12822
12861
  rotateIfNeeded(file);
12823
- (0, import_node_fs5.appendFileSync)(file, text);
12862
+ (0, import_node_fs6.appendFileSync)(file, text);
12824
12863
  } catch {
12825
12864
  }
12826
12865
  }
@@ -12868,8 +12907,8 @@ function markerPath() {
12868
12907
  }
12869
12908
  function readMarker() {
12870
12909
  try {
12871
- if (!(0, import_node_fs6.existsSync)(markerPath())) return null;
12872
- const raw = JSON.parse((0, import_node_fs6.readFileSync)(markerPath(), "utf-8"));
12910
+ if (!(0, import_node_fs7.existsSync)(markerPath())) return null;
12911
+ const raw = JSON.parse((0, import_node_fs7.readFileSync)(markerPath(), "utf-8"));
12873
12912
  const pluginRoot = typeof raw.plugin_root === "string" ? raw.plugin_root : "";
12874
12913
  if (!pluginRoot || CONTROL_CHARS.test(pluginRoot)) return null;
12875
12914
  return {
@@ -12886,23 +12925,23 @@ function recordPluginOwnership(sessionId) {
12886
12925
  const pluginRoot = process.env.VERITY_PLUGIN_ROOT;
12887
12926
  if (!pluginRoot) return;
12888
12927
  try {
12889
- (0, import_node_fs6.mkdirSync)(projectPath(VERITY_DIR), { recursive: true });
12928
+ (0, import_node_fs7.mkdirSync)(projectPath(VERITY_DIR), { recursive: true });
12890
12929
  const marker = {
12891
12930
  session_id: sessionId,
12892
12931
  plugin_root: pluginRoot,
12893
12932
  version: process.env.VERITY_PLUGIN_VERSION || null,
12894
12933
  ts: Math.floor(Date.now() / 1e3)
12895
12934
  };
12896
- (0, import_node_fs6.writeFileSync)(markerPath(), JSON.stringify(marker));
12935
+ (0, import_node_fs7.writeFileSync)(markerPath(), JSON.stringify(marker));
12897
12936
  } catch {
12898
12937
  }
12899
12938
  }
12900
12939
  function claudeConfigDir() {
12901
- return process.env.CLAUDE_CONFIG_DIR || (0, import_node_path7.join)((0, import_node_os2.homedir)(), ".claude");
12940
+ return process.env.CLAUDE_CONFIG_DIR || (0, import_node_path8.join)((0, import_node_os2.homedir)(), ".claude");
12902
12941
  }
12903
12942
  function readJsonFile(path) {
12904
12943
  try {
12905
- const parsed = JSON.parse((0, import_node_fs6.readFileSync)(path, "utf-8"));
12944
+ const parsed = JSON.parse((0, import_node_fs7.readFileSync)(path, "utf-8"));
12906
12945
  return parsed && typeof parsed === "object" ? parsed : null;
12907
12946
  } catch {
12908
12947
  return null;
@@ -12910,9 +12949,9 @@ function readJsonFile(path) {
12910
12949
  }
12911
12950
  function enabledPluginSetting(key) {
12912
12951
  const files = [
12913
- projectPath((0, import_node_path7.join)(".claude", "settings.local.json")),
12914
- projectPath((0, import_node_path7.join)(".claude", "settings.json")),
12915
- (0, import_node_path7.join)(claudeConfigDir(), "settings.json")
12952
+ projectPath((0, import_node_path8.join)(".claude", "settings.local.json")),
12953
+ projectPath((0, import_node_path8.join)(".claude", "settings.json")),
12954
+ (0, import_node_path8.join)(claudeConfigDir(), "settings.json")
12916
12955
  ];
12917
12956
  for (const file of files) {
12918
12957
  const map = readJsonFile(file)?.enabledPlugins;
@@ -12924,27 +12963,16 @@ function enabledPluginSetting(key) {
12924
12963
  }
12925
12964
  function marketplaceLocations() {
12926
12965
  const out = /* @__PURE__ */ new Map();
12927
- const known = readJsonFile((0, import_node_path7.join)(claudeConfigDir(), "plugins", "known_marketplaces.json"));
12966
+ const known = readJsonFile((0, import_node_path8.join)(claudeConfigDir(), "plugins", "known_marketplaces.json"));
12928
12967
  if (!known) return out;
12929
12968
  for (const [name, entry] of Object.entries(known)) {
12930
12969
  const loc2 = entry?.installLocation;
12931
- if (typeof loc2 === "string" && loc2) out.set(name, (0, import_node_path7.resolve)(loc2));
12970
+ if (typeof loc2 === "string" && loc2) out.set(name, (0, import_node_path8.resolve)(loc2));
12932
12971
  }
12933
12972
  return out;
12934
12973
  }
12935
12974
  function verityPathEntry() {
12936
- const path = process.env.PATH;
12937
- if (!path) return null;
12938
- for (const dir of path.split(import_node_path7.delimiter)) {
12939
- if (!dir) continue;
12940
- const candidate = (0, import_node_path7.join)(dir, "verity");
12941
- try {
12942
- (0, import_node_fs6.accessSync)(candidate, import_node_fs6.constants.X_OK);
12943
- return candidate;
12944
- } catch {
12945
- }
12946
- }
12947
- return null;
12975
+ return whichSync("verity", { real: false });
12948
12976
  }
12949
12977
  function verityOnPath() {
12950
12978
  return verityPathEntry() !== null;
@@ -12953,7 +12981,7 @@ function globalVerityVersion() {
12953
12981
  const entry = verityPathEntry();
12954
12982
  if (!entry) return null;
12955
12983
  try {
12956
- const pkg = readJsonFile((0, import_node_path7.join)((0, import_node_path7.dirname)((0, import_node_fs6.realpathSync)(entry)), "..", "package.json"));
12984
+ const pkg = readJsonFile((0, import_node_path8.join)((0, import_node_path8.dirname)((0, import_node_fs7.realpathSync)(entry)), "..", "package.json"));
12957
12985
  if (pkg?.name !== "@codacy/verity-cli" || typeof pkg.version !== "string") return null;
12958
12986
  return pkg.version;
12959
12987
  } catch {
@@ -12965,7 +12993,7 @@ function pluginCliInvocation() {
12965
12993
  if (!root) return null;
12966
12994
  const onPath = globalVerityVersion();
12967
12995
  if (onPath !== null && onPath === activePluginVersion()) return null;
12968
- return `node ${JSON.stringify((0, import_node_path7.join)(root, "scripts", "verity.mjs"))}`;
12996
+ return `node ${JSON.stringify((0, import_node_path8.join)(root, "scripts", "verity.mjs"))}`;
12969
12997
  }
12970
12998
  function cliVersionSkew() {
12971
12999
  if (!process.env.VERITY_PLUGIN_ROOT) return null;
@@ -12984,7 +13012,7 @@ var VERITY_MARKETPLACE_REPO = "codacy/verity";
12984
13012
  function marketplaceConflict() {
12985
13013
  const wanted = VERITY_MARKETPLACE;
12986
13014
  for (const file of ["settings.json", "settings.local.json"]) {
12987
- const path = (0, import_node_path7.join)(claudeConfigDir(), file);
13015
+ const path = (0, import_node_path8.join)(claudeConfigDir(), file);
12988
13016
  const declared = readJsonFile(path)?.extraKnownMarketplaces ?? null;
12989
13017
  const entry2 = declared && typeof declared === "object" ? declared[wanted] : void 0;
12990
13018
  if (entry2 && typeof entry2 === "object") {
@@ -12994,7 +13022,7 @@ function marketplaceConflict() {
12994
13022
  }
12995
13023
  }
12996
13024
  }
12997
- const known = readJsonFile((0, import_node_path7.join)(claudeConfigDir(), "plugins", "known_marketplaces.json"));
13025
+ const known = readJsonFile((0, import_node_path8.join)(claudeConfigDir(), "plugins", "known_marketplaces.json"));
12998
13026
  const entry = known?.[wanted];
12999
13027
  if (entry && typeof entry === "object" && !pointsAtVerity(entry.source)) {
13000
13028
  return { name: wanted, declaredAs: describeSource(entry.source) };
@@ -13015,7 +13043,7 @@ function describeSource(src) {
13015
13043
  return target ? `${kind} \u2192 ${target}` : kind;
13016
13044
  }
13017
13045
  function legacyMarketplaceInstall() {
13018
- const plugins = readJsonFile((0, import_node_path7.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
13046
+ const plugins = readJsonFile((0, import_node_path8.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
13019
13047
  if (!plugins || typeof plugins !== "object") return null;
13020
13048
  for (const key of Object.keys(plugins)) {
13021
13049
  const at = key.lastIndexOf("@");
@@ -13027,13 +13055,13 @@ function legacyMarketplaceInstall() {
13027
13055
  }
13028
13056
  function realpathOr(p) {
13029
13057
  try {
13030
- return import_node_fs6.realpathSync.native(p);
13058
+ return import_node_fs7.realpathSync.native(p);
13031
13059
  } catch {
13032
- return (0, import_node_path7.resolve)(p);
13060
+ return (0, import_node_path8.resolve)(p);
13033
13061
  }
13034
13062
  }
13035
13063
  function isWithin(want, dir) {
13036
- return want === dir || want.startsWith(dir + import_node_path7.sep);
13064
+ return want === dir || want.startsWith(dir + import_node_path8.sep);
13037
13065
  }
13038
13066
  function entryAppliesHere(entry, here) {
13039
13067
  const e = entry;
@@ -13043,9 +13071,9 @@ function entryAppliesHere(entry, here) {
13043
13071
  return forProject === here;
13044
13072
  }
13045
13073
  function registrySays(pluginRoot) {
13046
- const plugins = readJsonFile((0, import_node_path7.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
13074
+ const plugins = readJsonFile((0, import_node_path8.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
13047
13075
  if (!plugins || typeof plugins !== "object") return "unverified";
13048
- const want = (0, import_node_path7.resolve)(pluginRoot);
13076
+ const want = (0, import_node_path8.resolve)(pluginRoot);
13049
13077
  const here = realpathOr(repoRoot());
13050
13078
  const markets = marketplaceLocations();
13051
13079
  for (const [key, value] of Object.entries(plugins)) {
@@ -13054,15 +13082,15 @@ function registrySays(pluginRoot) {
13054
13082
  const applicable = (Array.isArray(value) ? value : []).filter((entry) => entryAppliesHere(entry, here));
13055
13083
  const claims = applicable.some((entry) => {
13056
13084
  const installPath = entry?.installPath;
13057
- return typeof installPath === "string" && (0, import_node_path7.resolve)(installPath) === want;
13085
+ return typeof installPath === "string" && (0, import_node_path8.resolve)(installPath) === want;
13058
13086
  }) || source !== void 0 && applicable.length > 0 && isWithin(want, source);
13059
13087
  if (claims) {
13060
13088
  if (enabledPluginSetting(key) === false) return "gone";
13061
- return (0, import_node_fs6.existsSync)(pluginRoot) ? "live" : "gone";
13089
+ return (0, import_node_fs7.existsSync)(pluginRoot) ? "live" : "gone";
13062
13090
  }
13063
13091
  }
13064
- const managed = (0, import_node_path7.resolve)((0, import_node_path7.join)(claudeConfigDir(), "plugins", "cache"));
13065
- return want === managed || want.startsWith(managed + import_node_path7.sep) ? "gone" : "unverified";
13092
+ const managed = (0, import_node_path8.resolve)((0, import_node_path8.join)(claudeConfigDir(), "plugins", "cache"));
13093
+ return want === managed || want.startsWith(managed + import_node_path8.sep) ? "gone" : "unverified";
13066
13094
  }
13067
13095
  var _live = /* @__PURE__ */ new Map();
13068
13096
  function pluginLiveness(pluginRoot) {
@@ -13076,7 +13104,7 @@ function clearStalePluginMarker() {
13076
13104
  const marker = readMarker();
13077
13105
  if (!marker || pluginLiveness(marker.plugin_root) !== "gone") return null;
13078
13106
  try {
13079
- (0, import_node_fs6.rmSync)(markerPath(), { force: true });
13107
+ (0, import_node_fs7.rmSync)(markerPath(), { force: true });
13080
13108
  } catch {
13081
13109
  return null;
13082
13110
  }
@@ -13104,7 +13132,7 @@ function pluginActiveHere() {
13104
13132
  return activePluginInstall() !== null;
13105
13133
  }
13106
13134
  function registeredVerityPlugin() {
13107
- const plugins = readJsonFile((0, import_node_path7.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
13135
+ const plugins = readJsonFile((0, import_node_path8.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
13108
13136
  if (!plugins || typeof plugins !== "object") return null;
13109
13137
  const here = realpathOr(repoRoot());
13110
13138
  for (const [key, value] of Object.entries(plugins)) {
@@ -13114,7 +13142,7 @@ function registeredVerityPlugin() {
13114
13142
  for (const entry of Array.isArray(value) ? value : []) {
13115
13143
  const e = entry;
13116
13144
  const installPath = typeof e.installPath === "string" ? e.installPath : "";
13117
- if (!installPath || !(0, import_node_fs6.existsSync)(installPath)) continue;
13145
+ if (!installPath || !(0, import_node_fs7.existsSync)(installPath)) continue;
13118
13146
  if (!entryAppliesHere(entry, here)) continue;
13119
13147
  return { pluginRoot: installPath, version: typeof e.version === "string" ? e.version : null };
13120
13148
  }
@@ -13299,7 +13327,7 @@ var import_node_crypto8 = require("node:crypto");
13299
13327
 
13300
13328
  // src/lib/conversation-buffer.ts
13301
13329
  var import_promises5 = require("node:fs/promises");
13302
- var import_node_fs7 = require("node:fs");
13330
+ var import_node_fs8 = require("node:fs");
13303
13331
  var import_node_child_process5 = require("node:child_process");
13304
13332
  var import_node_crypto = require("node:crypto");
13305
13333
  function stripImageReferences(text) {
@@ -13335,7 +13363,7 @@ async function appendToConversationBuffer(prompt, sessionId) {
13335
13363
  }
13336
13364
  async function readAndClearConversationBuffer(currentSessionId) {
13337
13365
  try {
13338
- if ((0, import_node_fs7.existsSync)(CONVERSATION_BUFFER_FILE)) {
13366
+ if ((0, import_node_fs8.existsSync)(CONVERSATION_BUFFER_FILE)) {
13339
13367
  const entries = await readBufferEntries();
13340
13368
  let mine = entries;
13341
13369
  let others = [];
@@ -13359,7 +13387,7 @@ async function readAndClearConversationBuffer(currentSessionId) {
13359
13387
  };
13360
13388
  }
13361
13389
  }
13362
- if ((0, import_node_fs7.existsSync)(INTENT_FILE)) {
13390
+ if ((0, import_node_fs8.existsSync)(INTENT_FILE)) {
13363
13391
  try {
13364
13392
  const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
13365
13393
  await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
@@ -13652,9 +13680,9 @@ function isCommandOnlyTurn(input) {
13652
13680
 
13653
13681
  // src/lib/context-identity.ts
13654
13682
  var import_node_crypto2 = require("node:crypto");
13655
- var import_node_fs8 = require("node:fs");
13683
+ var import_node_fs9 = require("node:fs");
13656
13684
  var import_node_os3 = require("node:os");
13657
- var import_node_path8 = require("node:path");
13685
+ var import_node_path9 = require("node:path");
13658
13686
  var SHARED_SENTINELS = /* @__PURE__ */ new Set([
13659
13687
  "",
13660
13688
  "-",
@@ -13692,7 +13720,7 @@ function contextIdentity(input) {
13692
13720
  if (rawTree && !isSharedSentinel(rawTree)) {
13693
13721
  let resolved = rawTree;
13694
13722
  try {
13695
- resolved = import_node_fs8.realpathSync.native(rawTree);
13723
+ resolved = import_node_fs9.realpathSync.native(rawTree);
13696
13724
  } catch {
13697
13725
  }
13698
13726
  treeKey = (0, import_node_crypto2.createHash)("sha256").update(resolved).digest("hex").slice(0, 12);
@@ -13708,13 +13736,13 @@ function contextIdentity(input) {
13708
13736
  }
13709
13737
  function verityHome() {
13710
13738
  const override = process.env.VERITY_HOME;
13711
- return override && override.trim() ? (0, import_node_path8.resolve)(override) : (0, import_node_path8.join)((0, import_node_os3.homedir)(), ".verity");
13739
+ return override && override.trim() ? (0, import_node_path9.resolve)(override) : (0, import_node_path9.join)((0, import_node_os3.homedir)(), ".verity");
13712
13740
  }
13713
13741
  function dossierDir(identity) {
13714
- return (0, import_node_path8.join)(verityHome(), "sessions", identity.userKey, identity.treeKey, identity.sessionKey);
13742
+ return (0, import_node_path9.join)(verityHome(), "sessions", identity.userKey, identity.treeKey, identity.sessionKey);
13715
13743
  }
13716
13744
  function treeDir(identity) {
13717
- return (0, import_node_path8.join)(verityHome(), "sessions", identity.userKey, identity.treeKey);
13745
+ return (0, import_node_path9.join)(verityHome(), "sessions", identity.userKey, identity.treeKey);
13718
13746
  }
13719
13747
  function scopeIdentity(token, sessionId) {
13720
13748
  const t = (token ?? "").trim();
@@ -13729,8 +13757,8 @@ function sessionScopeKey(token, sessionId) {
13729
13757
 
13730
13758
  // src/lib/task-context-buffer.ts
13731
13759
  var import_promises6 = require("node:fs/promises");
13732
- var import_node_fs9 = require("node:fs");
13733
- var import_node_path9 = require("node:path");
13760
+ var import_node_fs10 = require("node:fs");
13761
+ var import_node_path10 = require("node:path");
13734
13762
  var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
13735
13763
  var MAX_BUFFER_BYTES = 500 * 1024;
13736
13764
  var MAX_PROMPT_CHARS = 2e3;
@@ -13769,7 +13797,7 @@ async function appendResponseToTaskBuffer(taskId, assistantResponse, actionSumma
13769
13797
  }
13770
13798
  async function readTaskContextBuffer(taskId) {
13771
13799
  const filePath = bufferPath(taskId);
13772
- if (!(0, import_node_fs9.existsSync)(filePath)) return null;
13800
+ if (!(0, import_node_fs10.existsSync)(filePath)) return null;
13773
13801
  try {
13774
13802
  const content = await (0, import_promises6.readFile)(filePath, "utf-8");
13775
13803
  if (!content.trim()) return null;
@@ -13803,12 +13831,12 @@ async function readTaskContextBuffer(taskId) {
13803
13831
  }
13804
13832
  async function cleanupTaskContextBuffers() {
13805
13833
  try {
13806
- if (!(0, import_node_fs9.existsSync)(TASK_CONTEXT_DIR)) return;
13834
+ if (!(0, import_node_fs10.existsSync)(TASK_CONTEXT_DIR)) return;
13807
13835
  const files = await (0, import_promises6.readdir)(TASK_CONTEXT_DIR);
13808
13836
  const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
13809
13837
  for (const file of files) {
13810
13838
  if (!file.endsWith(".jsonl")) continue;
13811
- const filePath = (0, import_node_path9.join)(TASK_CONTEXT_DIR, file);
13839
+ const filePath = (0, import_node_path10.join)(TASK_CONTEXT_DIR, file);
13812
13840
  try {
13813
13841
  const stats = await (0, import_promises6.stat)(filePath);
13814
13842
  if (stats.mtimeMs < cutoffMs) {
@@ -13822,13 +13850,13 @@ async function cleanupTaskContextBuffers() {
13822
13850
  }
13823
13851
  function bufferPath(taskId) {
13824
13852
  const safe = taskId.replace(/[^a-zA-Z0-9_-]/g, "");
13825
- return (0, import_node_path9.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
13853
+ return (0, import_node_path10.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
13826
13854
  }
13827
13855
  async function appendEntry(taskId, entry) {
13828
13856
  try {
13829
13857
  await (0, import_promises6.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
13830
13858
  const filePath = bufferPath(taskId);
13831
- if ((0, import_node_fs9.existsSync)(filePath)) {
13859
+ if ((0, import_node_fs10.existsSync)(filePath)) {
13832
13860
  const stats = await (0, import_promises6.stat)(filePath);
13833
13861
  if (stats.size >= MAX_BUFFER_BYTES) {
13834
13862
  const content = await (0, import_promises6.readFile)(filePath, "utf-8");
@@ -13839,213 +13867,21 @@ async function appendEntry(taskId, entry) {
13839
13867
  }
13840
13868
  }
13841
13869
  const line = JSON.stringify(entry) + "\n";
13842
- const existing = (0, import_node_fs9.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
13870
+ const existing = (0, import_node_fs10.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
13843
13871
  await (0, import_promises6.writeFile)(filePath, existing + line);
13844
13872
  } catch {
13845
13873
  }
13846
13874
  }
13847
13875
 
13848
13876
  // src/lib/memory-retrieval.ts
13849
- var import_promises8 = require("node:fs/promises");
13877
+ var import_promises7 = require("node:fs/promises");
13850
13878
  var import_node_fs11 = require("node:fs");
13851
13879
  var import_node_path11 = require("node:path");
13852
-
13853
- // src/lib/org-mirror.ts
13854
- var import_node_fs10 = require("node:fs");
13855
- var import_promises7 = require("node:fs/promises");
13856
- var import_node_path10 = require("node:path");
13857
- var ORG_KINDS = ["decision", "security", "gotcha", "pattern", "domain", "integration"];
13858
- var STATE_FILE = ".org-pull-state.json";
13859
- var BODY_MAX = 8192;
13860
- function orgMirrorDir(remote = requestRemote()) {
13861
- const parsed = parseRemote(remote);
13862
- if (!parsed) return null;
13863
- return (0, import_node_path10.join)(verityHome(), "orgs", parsed.host, parsed.owner.toLowerCase(), "memory");
13864
- }
13865
- function slugify(s) {
13866
- return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
13867
- }
13868
- function orgNodePath(node) {
13869
- const kind = ORG_KINDS.includes(node.kind) ? node.kind : "domain";
13870
- return `${kind}/${slugify(node.title) || "claim"}-${node.tag_id.replace(/-/g, "").slice(0, 8)}.md`;
13871
- }
13872
- function renderOrgNode(node, orgName) {
13873
- const fm = [
13874
- "---",
13875
- `id: ${JSON.stringify(node.tag_id)}`,
13876
- `node_id: ${JSON.stringify(node.node_id)}`,
13877
- 'scope: "org"',
13878
- `org: ${JSON.stringify(orgName)}`,
13879
- `kind: ${JSON.stringify(node.kind)}`,
13880
- `title: ${JSON.stringify(node.title)}`,
13881
- `origin: ${JSON.stringify(node.origin)}`,
13882
- `signal: ${JSON.stringify(node.signal)}`,
13883
- `tier: ${node.tier === null ? "null" : JSON.stringify(node.tier)}`,
13884
- `applies_to: ${JSON.stringify(node.applies_to)}`,
13885
- `source_repo_count: ${JSON.stringify(node.source_repo_count)}`,
13886
- `promoted_at: ${JSON.stringify(node.promoted_at)}`,
13887
- 'status: "active"',
13888
- "---"
13889
- ];
13890
- return `${fm.join("\n")}
13891
-
13892
- # ${node.title}
13893
-
13894
- ${node.body.trim()}
13895
- `;
13896
- }
13897
- function parseOrgNode(content) {
13898
- const m = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
13899
- if (!m) return null;
13900
- const fm = {};
13901
- for (const line of m[1].split("\n")) {
13902
- const kv = line.match(/^([a-z_]+):\s*(.*)$/);
13903
- if (!kv) continue;
13904
- try {
13905
- fm[kv[1]] = JSON.parse(kv[2]);
13906
- } catch {
13907
- fm[kv[1]] = kv[2];
13908
- }
13909
- }
13910
- if (fm.scope !== "org" || typeof fm.id !== "string" || typeof fm.title !== "string") return null;
13911
- if (fm.status && fm.status !== "active") return null;
13912
- const body = m[2].replace(/^\s*#[^\n]*\n/, "").trim();
13913
- return {
13914
- tag_id: fm.id,
13915
- node_id: typeof fm.node_id === "string" ? fm.node_id : "",
13916
- kind: typeof fm.kind === "string" ? fm.kind : "domain",
13917
- title: fm.title,
13918
- body,
13919
- origin: typeof fm.origin === "string" ? fm.origin : "",
13920
- signal: typeof fm.signal === "string" ? fm.signal : "",
13921
- tier: typeof fm.tier === "number" ? fm.tier : null,
13922
- applies_to: Array.isArray(fm.applies_to) ? fm.applies_to.filter((v) => typeof v === "string") : [],
13923
- source_repo_count: typeof fm.source_repo_count === "number" ? fm.source_repo_count : 1,
13924
- promoted_at: typeof fm.promoted_at === "string" ? fm.promoted_at : ""
13925
- };
13926
- }
13927
- async function readState(dir) {
13928
- try {
13929
- const parsed = JSON.parse(await (0, import_promises7.readFile)((0, import_node_path10.join)(dir, STATE_FILE), "utf-8"));
13930
- return {
13931
- version: typeof parsed?.version === "string" ? parsed.version : null,
13932
- organization: parsed?.organization && typeof parsed.organization.id === "string" ? parsed.organization : null,
13933
- files: Array.isArray(parsed?.files) ? parsed.files.filter((f) => typeof f === "string") : []
13934
- };
13935
- } catch {
13936
- return { version: null, organization: null, files: [] };
13937
- }
13938
- }
13939
- async function writeState(dir, state) {
13940
- await (0, import_promises7.mkdir)(dir, { recursive: true });
13941
- await (0, import_promises7.writeFile)((0, import_node_path10.join)(dir, STATE_FILE), JSON.stringify({
13942
- ...state.version ? { version: state.version } : {},
13943
- organization: state.organization,
13944
- pulled_at: (/* @__PURE__ */ new Date()).toISOString(),
13945
- files: state.files
13946
- }, null, 2) + "\n");
13947
- }
13948
- function renderIndex(orgName, nodes) {
13949
- const lines = [
13950
- `# Org Knowledge \u2014 ${orgName}`,
13951
- "",
13952
- "*Auto-generated by verity CLI from what this organization's repositories learned. Do not hand-edit; `verity memory pull` overwrites it.*",
13953
- "",
13954
- '> These claims hold across the organization\'s repositories, not only this one. Each names where it came from. A claim that is wrong for this repository is demoted with `verity memory demote <id> --reason "\u2026"`, for everyone.',
13955
- ""
13956
- ];
13957
- const byKind = /* @__PURE__ */ new Map();
13958
- for (const n of nodes) byKind.set(n.kind, [...byKind.get(n.kind) ?? [], n]);
13959
- for (const kind of ORG_KINDS) {
13960
- const list2 = byKind.get(kind);
13961
- if (!list2?.length) continue;
13962
- lines.push(`## ${kind}/ (${list2.length})`);
13963
- for (const n of list2) lines.push(`- [[${orgNodePath(n).split("/")[1].replace(/\.md$/, "")}]] \u2014 ${n.title} \xB7 ${n.origin}`);
13964
- lines.push("");
13965
- }
13966
- if (nodes.length === 0) lines.push("No org knowledge yet.", "");
13967
- return lines.join("\n");
13968
- }
13969
- async function pullOrgKnowledge(opts) {
13970
- const dir = orgMirrorDir(opts.remote ?? requestRemote());
13971
- if (!dir) return { ok: true, status: "no_remote", received: 0, dir: null };
13972
- const state = await readState(dir);
13973
- const params = new URLSearchParams({ for: "agent" });
13974
- if (!opts.force && state.version) params.set("if_version", state.version);
13975
- const res = await apiRequest({
13976
- method: "GET",
13977
- path: `/memory/org?${params.toString()}`,
13978
- serviceUrl: opts.serviceUrl,
13979
- token: opts.token,
13980
- verbose: opts.verbose,
13981
- timeout: opts.timeoutMs ?? 3e4,
13982
- cmd: "memory-org-pull"
13983
- });
13984
- if (!res.ok) return { ok: false, error: res.error, category: res.category };
13985
- const page = res.data;
13986
- if (page.unchanged) return { ok: true, status: "unchanged", received: 0, dir };
13987
- const org = page.organization;
13988
- const nodes = org ? (page.nodes ?? []).flatMap((n) => typeof n.tag_id === "string" && typeof n.title === "string" && typeof n.body === "string" ? [{
13989
- tag_id: n.tag_id,
13990
- node_id: typeof n.node_id === "string" ? n.node_id : "",
13991
- kind: typeof n.kind === "string" ? n.kind : "domain",
13992
- title: n.title,
13993
- body: n.body.slice(0, BODY_MAX),
13994
- origin: typeof n.origin === "string" ? n.origin : "",
13995
- signal: typeof n.signal === "string" ? n.signal : "",
13996
- tier: typeof n.tier === "number" ? n.tier : null,
13997
- applies_to: Array.isArray(n.applies_to) ? n.applies_to.filter((v) => typeof v === "string") : [],
13998
- source_repo_count: typeof n.source_repo_count === "number" ? n.source_repo_count : 1,
13999
- promoted_at: typeof n.promoted_at === "string" ? n.promoted_at : ""
14000
- }] : []) : [];
14001
- const written = /* @__PURE__ */ new Set();
14002
- await (0, import_promises7.mkdir)(dir, { recursive: true });
14003
- for (const n of nodes) {
14004
- const rel = orgNodePath(n);
14005
- if (written.has(rel)) continue;
14006
- await (0, import_promises7.mkdir)((0, import_node_path10.join)(dir, rel.split("/")[0]), { recursive: true });
14007
- await (0, import_promises7.writeFile)((0, import_node_path10.join)(dir, rel), renderOrgNode(n, org?.name ?? ""));
14008
- written.add(rel);
14009
- }
14010
- for (const rel of state.files) {
14011
- if (!written.has(rel)) await (0, import_promises7.rm)((0, import_node_path10.join)(dir, rel), { force: true });
14012
- }
14013
- await (0, import_promises7.writeFile)((0, import_node_path10.join)(dir, "index.md"), renderIndex(org?.name ?? "no organization", nodes));
14014
- await writeState(dir, { version: org ? page.version : null, organization: org, files: [...written].sort() });
14015
- return { ok: true, status: org ? "pulled" : "none", received: nodes.length, dir };
14016
- }
14017
- async function readOrgMirror(remote = requestRemote()) {
14018
- const dir = orgMirrorDir(remote);
14019
- if (!dir || !(0, import_node_fs10.existsSync)(dir)) return [];
14020
- const out = [];
14021
- for (const kind of ORG_KINDS) {
14022
- const kindDir = (0, import_node_path10.join)(dir, kind);
14023
- if (!(0, import_node_fs10.existsSync)(kindDir)) continue;
14024
- let files;
14025
- try {
14026
- files = await (0, import_promises7.readdir)(kindDir);
14027
- } catch {
14028
- continue;
14029
- }
14030
- for (const file of files) {
14031
- if (!file.endsWith(".md")) continue;
14032
- try {
14033
- const node = parseOrgNode(await (0, import_promises7.readFile)((0, import_node_path10.join)(kindDir, file), "utf-8"));
14034
- if (node) out.push(node);
14035
- } catch {
14036
- }
14037
- }
14038
- }
14039
- return out;
14040
- }
14041
-
14042
- // src/lib/memory-retrieval.ts
14043
13880
  var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
14044
13881
  var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
14045
13882
  var DEFAULT_BUDGET_TOKENS = 2e3;
14046
13883
  var MAX_BUDGET_TOKENS = 4e3;
14047
13884
  var MIN_SCORE = 0.5;
14048
- var ORG_MIN_SHARED_TERMS = 2;
14049
13885
  function tokenize(text) {
14050
13886
  return new Set(
14051
13887
  text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((w) => w.length > 2)
@@ -14058,26 +13894,6 @@ function jaccardKeywords(a, b) {
14058
13894
  const union = a.size + b.size - inter;
14059
13895
  return union === 0 ? 0 : inter / union;
14060
13896
  }
14061
- function sharedTerms(a, b) {
14062
- let n = 0;
14063
- for (const w of a) if (b.has(w)) n++;
14064
- return n;
14065
- }
14066
- function orgNodeToLocal(n) {
14067
- return {
14068
- path: `org:${n.tag_id}`,
14069
- nodeId: n.tag_id,
14070
- kind: n.kind,
14071
- title: n.title,
14072
- body: n.body.slice(0, 2e3),
14073
- fileGlobs: [],
14074
- confidence: 0.5,
14075
- citedCount: 0,
14076
- tokenEstimate: Math.ceil((n.title.length + n.body.length) / 4) + 20,
14077
- scope: "org",
14078
- origin: n.origin
14079
- };
14080
- }
14081
13897
  function globMatch(glob, filePath) {
14082
13898
  if (glob === filePath) return true;
14083
13899
  const suffixMatch = glob.match(/^\*\*\/\*(.+)$/);
@@ -14153,19 +13969,19 @@ function parseFrontmatter(content) {
14153
13969
  return { fm, body: match[2].trim() };
14154
13970
  }
14155
13971
  async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = DEFAULT_BUDGET_TOKENS) {
14156
- const hasRepoGraph = (0, import_node_fs11.existsSync)(memoryDir());
13972
+ if (!(0, import_node_fs11.existsSync)(memoryDir())) return null;
14157
13973
  const budget = Math.min(budgetTokens, MAX_BUDGET_TOKENS);
14158
13974
  const promptTokens = tokenize(promptText);
14159
13975
  const nodes = [];
14160
- for (const domain of hasRepoGraph ? DOMAINS : []) {
13976
+ for (const domain of DOMAINS) {
14161
13977
  const domainDir = (0, import_node_path11.join)(memoryDir(), domain);
14162
13978
  if (!(0, import_node_fs11.existsSync)(domainDir)) continue;
14163
13979
  try {
14164
- const files = await (0, import_promises8.readdir)(domainDir);
13980
+ const files = await (0, import_promises7.readdir)(domainDir);
14165
13981
  for (const file of files) {
14166
13982
  if (!file.endsWith(".md")) continue;
14167
13983
  try {
14168
- const content = await (0, import_promises8.readFile)((0, import_node_path11.join)(domainDir, file), "utf-8");
13984
+ const content = await (0, import_promises7.readFile)((0, import_node_path11.join)(domainDir, file), "utf-8");
14169
13985
  const { fm, body } = parseFrontmatter(content);
14170
13986
  if (fm.status && fm.status !== "active") continue;
14171
13987
  nodes.push({
@@ -14185,13 +14001,6 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
14185
14001
  } catch {
14186
14002
  }
14187
14003
  }
14188
- try {
14189
- for (const claim of await readOrgMirror()) {
14190
- const local = orgNodeToLocal(claim);
14191
- if (sharedTerms(tokenize(`${local.title} ${local.body}`), promptTokens) >= ORG_MIN_SHARED_TERMS) nodes.push(local);
14192
- }
14193
- } catch {
14194
- }
14195
14004
  if (nodes.length === 0) return null;
14196
14005
  const scored = nodes.map((n) => ({
14197
14006
  ...n,
@@ -14210,13 +14019,12 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
14210
14019
  const lines = [
14211
14020
  "## Project Knowledge (auto-injected)",
14212
14021
  "",
14213
- `*${selected.length} node(s) relevant to your current task. See \`.verity/memory/index.md\` for the full knowledge base${selected.some((n) => n.scope === "org") ? ", and `~/.verity/orgs/` for what the organization's other repositories learned" : ""}.*`,
14022
+ `*${selected.length} node(s) relevant to your current task. See \`.verity/memory/index.md\` for the full knowledge base.*`,
14214
14023
  ""
14215
14024
  ];
14216
14025
  for (const node of selected) {
14217
14026
  lines.push(`### ${node.title}`);
14218
- if (node.scope === "org") lines.push(`*org \xB7 ${node.origin}* \xB7 ${node.kind}`);
14219
- else lines.push(`*${node.kind}* \xB7 confidence ${Math.round(node.confidence * 100)}%`);
14027
+ lines.push(`*${node.kind}* \xB7 confidence ${Math.round(node.confidence * 100)}%`);
14220
14028
  lines.push("");
14221
14029
  lines.push(node.body.slice(0, 800));
14222
14030
  lines.push("");
@@ -14231,7 +14039,7 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
14231
14039
  }
14232
14040
 
14233
14041
  // src/lib/memory-sync.ts
14234
- var import_promises9 = require("node:fs/promises");
14042
+ var import_promises8 = require("node:fs/promises");
14235
14043
  var import_node_fs14 = require("node:fs");
14236
14044
  var import_node_path13 = require("node:path");
14237
14045
  var import_node_crypto3 = require("node:crypto");
@@ -14346,8 +14154,8 @@ function ensureVerityGitignore() {
14346
14154
  }
14347
14155
  let text = next.join("\n");
14348
14156
  if (!hasMarker) {
14349
- const sep4 = text === "" ? "" : text.endsWith("\n") ? "\n" : "\n\n";
14350
- text = text + sep4 + VERITY_GITIGNORE_BLOCK;
14157
+ const sep3 = text === "" ? "" : text.endsWith("\n") ? "\n" : "\n\n";
14158
+ text = text + sep3 + VERITY_GITIGNORE_BLOCK;
14351
14159
  }
14352
14160
  if (text !== content) (0, import_node_fs12.writeFileSync)(".gitignore", text);
14353
14161
  return verified(
@@ -14377,8 +14185,8 @@ function writeFencedBlock() {
14377
14185
  lines = fenceMemoryLines(lines);
14378
14186
  let text = lines.join("\n");
14379
14187
  if (!hasMarker) {
14380
- const sep4 = text === "" ? "" : text.endsWith("\n") ? "\n" : "\n\n";
14381
- text = text + sep4 + VERITY_GITIGNORE_BLOCK;
14188
+ const sep3 = text === "" ? "" : text.endsWith("\n") ? "\n" : "\n\n";
14189
+ text = text + sep3 + VERITY_GITIGNORE_BLOCK;
14382
14190
  }
14383
14191
  try {
14384
14192
  (0, import_node_fs12.writeFileSync)(".gitignore", text);
@@ -14428,8 +14236,8 @@ function keepMemoryTracked() {
14428
14236
  }
14429
14237
  if (content.includes(MEMORY_OPT_OUT_MARKER)) return "already";
14430
14238
  try {
14431
- const sep4 = content === "" ? "" : content.endsWith("\n") ? "\n" : "\n\n";
14432
- (0, import_node_fs12.writeFileSync)(".gitignore", content + sep4 + MEMORY_OPT_OUT_STANZA);
14239
+ const sep3 = content === "" ? "" : content.endsWith("\n") ? "\n" : "\n\n";
14240
+ (0, import_node_fs12.writeFileSync)(".gitignore", content + sep3 + MEMORY_OPT_OUT_STANZA);
14433
14241
  } catch {
14434
14242
  return "failed";
14435
14243
  }
@@ -14491,6 +14299,42 @@ function resolveInside(baseDir, candidate) {
14491
14299
  }
14492
14300
  return full;
14493
14301
  }
14302
+ var O_NOFOLLOW = typeof import_node_fs13.constants.O_NOFOLLOW === "number" ? import_node_fs13.constants.O_NOFOLLOW : 0;
14303
+ function readFileInside(baseDir, candidate, maxBytes) {
14304
+ const full = resolveInside(baseDir, candidate);
14305
+ if (!full) return null;
14306
+ let realParent;
14307
+ let realBase;
14308
+ try {
14309
+ realParent = (0, import_node_fs13.realpathSync)((0, import_node_path12.dirname)(full));
14310
+ realBase = (0, import_node_fs13.realpathSync)((0, import_node_path12.resolve)(baseDir));
14311
+ } catch {
14312
+ return null;
14313
+ }
14314
+ const realBaseSep = realBase.endsWith(import_node_path12.sep) ? realBase : realBase + import_node_path12.sep;
14315
+ if (realParent !== realBase && !realParent.startsWith(realBaseSep)) return null;
14316
+ const target = (0, import_node_path12.join)(realParent, (0, import_node_path12.basename)(full));
14317
+ let fd = null;
14318
+ try {
14319
+ fd = (0, import_node_fs13.openSync)(target, import_node_fs13.constants.O_RDONLY | O_NOFOLLOW);
14320
+ const st = (0, import_node_fs13.fstatSync)(fd);
14321
+ if (!st.isFile()) return null;
14322
+ const cap = Math.min(maxBytes, st.size);
14323
+ if (cap <= 0) return "";
14324
+ const buf = Buffer.alloc(cap);
14325
+ const bytesRead = (0, import_node_fs13.readSync)(fd, buf, 0, cap, 0);
14326
+ return buf.subarray(0, bytesRead).toString("utf-8");
14327
+ } catch {
14328
+ return null;
14329
+ } finally {
14330
+ if (fd !== null) {
14331
+ try {
14332
+ (0, import_node_fs13.closeSync)(fd);
14333
+ } catch {
14334
+ }
14335
+ }
14336
+ }
14337
+ }
14494
14338
 
14495
14339
  // src/lib/glob-match.ts
14496
14340
  function globToRegex(glob) {
@@ -14540,14 +14384,6 @@ function globToRegex(glob) {
14540
14384
  out = out.replace(/(?:\.\*)+/g, ".*");
14541
14385
  return new RegExp(`^${out}$`);
14542
14386
  }
14543
- function globMatch2(glob, filePath) {
14544
- if (glob === filePath) return true;
14545
- try {
14546
- return globToRegex(glob).test(filePath);
14547
- } catch {
14548
- return false;
14549
- }
14550
- }
14551
14387
  function anyPathMatches(glob, treePaths) {
14552
14388
  let regex;
14553
14389
  try {
@@ -14566,18 +14402,18 @@ var memoryDir2 = () => projectPath(`${VERITY_DIR}/memory`);
14566
14402
  var DOMAINS2 = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations", "_archive"];
14567
14403
  var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
14568
14404
  async function ensureMemoryDir() {
14569
- await (0, import_promises9.mkdir)(memoryDir2(), { recursive: true });
14405
+ await (0, import_promises8.mkdir)(memoryDir2(), { recursive: true });
14570
14406
  for (const domain of DOMAINS2) {
14571
- await (0, import_promises9.mkdir)((0, import_node_path13.join)(memoryDir2(), domain), { recursive: true });
14407
+ await (0, import_promises8.mkdir)((0, import_node_path13.join)(memoryDir2(), domain), { recursive: true });
14572
14408
  }
14573
14409
  if (!(0, import_node_fs14.existsSync)((0, import_node_path13.join)(memoryDir2(), "SCHEMA.md"))) {
14574
- await (0, import_promises9.writeFile)((0, import_node_path13.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
14410
+ await (0, import_promises8.writeFile)((0, import_node_path13.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
14575
14411
  }
14576
14412
  if (!(0, import_node_fs14.existsSync)((0, import_node_path13.join)(memoryDir2(), "index.md"))) {
14577
- await (0, import_promises9.writeFile)((0, import_node_path13.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
14413
+ await (0, import_promises8.writeFile)((0, import_node_path13.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
14578
14414
  }
14579
14415
  if (!(0, import_node_fs14.existsSync)((0, import_node_path13.join)(memoryDir2(), "log.md"))) {
14580
- await (0, import_promises9.writeFile)((0, import_node_path13.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
14416
+ await (0, import_promises8.writeFile)((0, import_node_path13.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
14581
14417
  }
14582
14418
  }
14583
14419
  async function buildManifest() {
@@ -14589,13 +14425,13 @@ async function buildManifest() {
14589
14425
  const domainDir = (0, import_node_path13.join)(memoryDir2(), domain);
14590
14426
  if (!(0, import_node_fs14.existsSync)(domainDir)) continue;
14591
14427
  try {
14592
- const files = await (0, import_promises9.readdir)(domainDir);
14428
+ const files = await (0, import_promises8.readdir)(domainDir);
14593
14429
  for (const file of files) {
14594
14430
  if (!file.endsWith(".md")) continue;
14595
14431
  const filePath = `${domain}/${file}`;
14596
14432
  const fullPath = (0, import_node_path13.join)(memoryDir2(), filePath);
14597
14433
  try {
14598
- const content = await (0, import_promises9.readFile)(fullPath, "utf-8");
14434
+ const content = await (0, import_promises8.readFile)(fullPath, "utf-8");
14599
14435
  const hash = (0, import_node_crypto3.createHash)("sha256").update(content).digest("hex").slice(0, 16);
14600
14436
  nodes.push({ path: filePath, content_hash: `sha256:${hash}` });
14601
14437
  } catch {
@@ -14606,13 +14442,13 @@ async function buildManifest() {
14606
14442
  }
14607
14443
  let indexHash = null;
14608
14444
  try {
14609
- const indexContent = await (0, import_promises9.readFile)((0, import_node_path13.join)(memoryDir2(), "index.md"), "utf-8");
14445
+ const indexContent = await (0, import_promises8.readFile)((0, import_node_path13.join)(memoryDir2(), "index.md"), "utf-8");
14610
14446
  indexHash = `sha256:${(0, import_node_crypto3.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
14611
14447
  } catch {
14612
14448
  }
14613
14449
  let logLength = 0;
14614
14450
  try {
14615
- const logContent = await (0, import_promises9.readFile)((0, import_node_path13.join)(memoryDir2(), "log.md"), "utf-8");
14451
+ const logContent = await (0, import_promises8.readFile)((0, import_node_path13.join)(memoryDir2(), "log.md"), "utf-8");
14616
14452
  logLength = logContent.split("\n").length;
14617
14453
  } catch {
14618
14454
  }
@@ -14628,10 +14464,10 @@ async function readOnDiskNodes() {
14628
14464
  const domainDir = (0, import_node_path13.join)(memoryDir2(), domain);
14629
14465
  if (!(0, import_node_fs14.existsSync)(domainDir)) continue;
14630
14466
  try {
14631
- for (const file of await (0, import_promises9.readdir)(domainDir)) {
14467
+ for (const file of await (0, import_promises8.readdir)(domainDir)) {
14632
14468
  if (!file.endsWith(".md")) continue;
14633
14469
  try {
14634
- out.set(`${domain}/${file}`, hashContent(await (0, import_promises9.readFile)((0, import_node_path13.join)(domainDir, file), "utf-8")));
14470
+ out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path13.join)(domainDir, file), "utf-8")));
14635
14471
  } catch {
14636
14472
  }
14637
14473
  }
@@ -14643,7 +14479,7 @@ async function readOnDiskNodes() {
14643
14479
  async function readSyncBaseline() {
14644
14480
  const out = /* @__PURE__ */ new Map();
14645
14481
  try {
14646
- const parsed = JSON.parse(await (0, import_promises9.readFile)(syncStateFile(), "utf-8"));
14482
+ const parsed = JSON.parse(await (0, import_promises8.readFile)(syncStateFile(), "utf-8"));
14647
14483
  if (Array.isArray(parsed?.nodes)) {
14648
14484
  for (const n of parsed.nodes) if (n?.path) out.set(n.path, n.hash ?? null);
14649
14485
  } else if (Array.isArray(parsed?.paths)) {
@@ -14659,20 +14495,15 @@ async function recordSyncedNodePaths() {
14659
14495
  const next = JSON.stringify({ schema: 2, nodes }) + "\n";
14660
14496
  let existing = "";
14661
14497
  try {
14662
- existing = await (0, import_promises9.readFile)(syncStateFile(), "utf-8");
14498
+ existing = await (0, import_promises8.readFile)(syncStateFile(), "utf-8");
14663
14499
  } catch {
14664
14500
  }
14665
14501
  if (existing === next) return;
14666
- await writeFileAtomic(syncStateFile(), next);
14502
+ await (0, import_promises8.mkdir)(projectPath(VERITY_DIR), { recursive: true });
14503
+ await (0, import_promises8.writeFile)(syncStateFile(), next);
14667
14504
  } catch {
14668
14505
  }
14669
14506
  }
14670
- async function writeFileAtomic(path, content) {
14671
- await (0, import_promises9.mkdir)((0, import_node_path13.dirname)(path), { recursive: true });
14672
- const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
14673
- await (0, import_promises9.writeFile)(tmp, content);
14674
- await (0, import_promises9.rename)(tmp, path);
14675
- }
14676
14507
  async function recordSyncBaseline() {
14677
14508
  await recordSyncedNodePaths();
14678
14509
  }
@@ -14686,7 +14517,7 @@ async function computeEditedNodeUploads() {
14686
14517
  if (!(0, import_node_fs14.existsSync)(full)) continue;
14687
14518
  let content;
14688
14519
  try {
14689
- content = await (0, import_promises9.readFile)(full, "utf-8");
14520
+ content = await (0, import_promises8.readFile)(full, "utf-8");
14690
14521
  } catch {
14691
14522
  continue;
14692
14523
  }
@@ -14707,56 +14538,31 @@ async function applyMemoryWrites(writes, opts = {}) {
14707
14538
  let count = 0;
14708
14539
  const notes = [];
14709
14540
  const treePaths = opts.treePaths;
14710
- const synced = /* @__PURE__ */ new Map();
14711
14541
  for (const write of writes) {
14712
- const outcome = await applyOneWrite(write, treePaths, opts.lastServed);
14713
- if (outcome.written) count++;
14714
- if (outcome.syncedHash) {
14715
- synced.set(write.path, outcome.syncedHash);
14716
- opts.served?.set(write.path, outcome.syncedHash);
14717
- }
14718
- notes.push(...outcome.notes);
14542
+ const { written, notes: writeNotes } = await applyOneWrite(write, treePaths);
14543
+ if (written) count++;
14544
+ notes.push(...writeNotes);
14719
14545
  }
14720
14546
  await regenerateIndex();
14721
- if (opts.claudeMdPointer !== false) {
14722
- try {
14723
- await ensureClaudeMdPointer();
14724
- } catch {
14725
- }
14547
+ try {
14548
+ await ensureClaudeMdPointer();
14549
+ } catch {
14726
14550
  }
14727
14551
  const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
14728
14552
  for (const n of notes) logLines.push(` - ${n}`);
14729
14553
  try {
14730
- const existing = (0, import_node_fs14.existsSync)((0, import_node_path13.join)(memoryDir2(), "log.md")) ? await (0, import_promises9.readFile)((0, import_node_path13.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
14731
- await (0, import_promises9.writeFile)((0, import_node_path13.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
14554
+ const existing = (0, import_node_fs14.existsSync)((0, import_node_path13.join)(memoryDir2(), "log.md")) ? await (0, import_promises8.readFile)((0, import_node_path13.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
14555
+ await (0, import_promises8.writeFile)((0, import_node_path13.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
14732
14556
  } catch {
14733
14557
  }
14734
- if (opts.baseline === "written") await mergeSyncBaseline(synced);
14735
- else if (opts.recordBaseline !== false) await recordSyncedNodePaths();
14558
+ await recordSyncedNodePaths();
14736
14559
  return count;
14737
14560
  }
14738
- async function mergeSyncBaseline(entries) {
14739
- if (entries.size === 0) return;
14740
- try {
14741
- if ((0, import_node_fs14.existsSync)(syncStateFile())) {
14742
- try {
14743
- JSON.parse(await (0, import_promises9.readFile)(syncStateFile(), "utf-8"));
14744
- } catch {
14745
- return;
14746
- }
14747
- }
14748
- const merged = await readSyncBaseline();
14749
- for (const [path, hash] of entries) merged.set(path, hash);
14750
- const nodes = [...merged].map(([path, hash]) => ({ path, hash }));
14751
- await writeFileAtomic(syncStateFile(), JSON.stringify({ schema: 2, nodes }) + "\n");
14752
- } catch {
14753
- }
14754
- }
14755
- async function applyOneWrite(write, treePaths, lastServed) {
14561
+ async function applyOneWrite(write, treePaths) {
14756
14562
  const fullPath = resolveInside(memoryDir2(), write.path);
14757
14563
  const notes = [];
14758
14564
  if (!fullPath) {
14759
- return { written: false, syncedHash: null, notes: [`${String(write.path)}: rejected \u2014 path escapes the memory directory`] };
14565
+ return { written: false, notes: [`${String(write.path)}: rejected \u2014 path escapes the memory directory`] };
14760
14566
  }
14761
14567
  let content = write.content;
14762
14568
  if (treePaths && treePaths.length > 0) {
@@ -14769,22 +14575,18 @@ async function applyOneWrite(write, treePaths, lastServed) {
14769
14575
  if ((0, import_node_fs14.existsSync)(fullPath)) {
14770
14576
  let existing = "";
14771
14577
  try {
14772
- existing = await (0, import_promises9.readFile)(fullPath, "utf-8");
14578
+ existing = await (0, import_promises8.readFile)(fullPath, "utf-8");
14773
14579
  } catch {
14774
14580
  }
14775
- if (existing === content) return { written: false, syncedHash: hashContent(content), notes };
14581
+ if (existing === content) return { written: false, notes };
14776
14582
  if (existing !== "") {
14777
- const served = lastServed?.get(write.path);
14778
- if (served == null || served !== hashContent(existing)) {
14779
- notes.push(`${write.path}: kept local edit (differs from server)`);
14780
- return { written: false, syncedHash: null, notes };
14781
- }
14782
- notes.push(`${write.path}: updated from server (untouched here since the server delivered it)`);
14583
+ notes.push(`${write.path}: kept local edit (differs from server)`);
14584
+ return { written: false, notes };
14783
14585
  }
14784
14586
  }
14785
- await (0, import_promises9.mkdir)((0, import_node_path13.dirname)(fullPath), { recursive: true });
14786
- await (0, import_promises9.writeFile)(fullPath, content);
14787
- return { written: true, syncedHash: hashContent(content), notes };
14587
+ await (0, import_promises8.mkdir)((0, import_node_path13.dirname)(fullPath), { recursive: true });
14588
+ await (0, import_promises8.writeFile)(fullPath, content);
14589
+ return { written: true, notes };
14788
14590
  }
14789
14591
  function groundFileGlobs(content, treePaths) {
14790
14592
  const globs = pickFrontmatterArray(content, "file_globs");
@@ -14826,7 +14628,7 @@ async function regenerateIndex() {
14826
14628
  const domainDir = (0, import_node_path13.join)(memoryDir2(), domain);
14827
14629
  if (!(0, import_node_fs14.existsSync)(domainDir)) continue;
14828
14630
  try {
14829
- const files = await (0, import_promises9.readdir)(domainDir);
14631
+ const files = await (0, import_promises8.readdir)(domainDir);
14830
14632
  const mdFiles = files.filter((f) => f.endsWith(".md"));
14831
14633
  if (mdFiles.length === 0) continue;
14832
14634
  lines.push(`## ${domain}/ (${mdFiles.length})`);
@@ -14834,7 +14636,7 @@ async function regenerateIndex() {
14834
14636
  for (const file of mdFiles.sort()) {
14835
14637
  const slug = file.replace(/\.md$/, "");
14836
14638
  try {
14837
- const content = await (0, import_promises9.readFile)((0, import_node_path13.join)(domainDir, file), "utf-8");
14639
+ const content = await (0, import_promises8.readFile)((0, import_node_path13.join)(domainDir, file), "utf-8");
14838
14640
  const title = pickFrontmatter(content, "title") ?? slug;
14839
14641
  const kind = pickFrontmatter(content, "kind") ?? "-";
14840
14642
  const confidence = pickFrontmatter(content, "confidence");
@@ -14861,11 +14663,11 @@ async function regenerateIndex() {
14861
14663
  const indexPath = (0, import_node_path13.join)(memoryDir2(), "index.md");
14862
14664
  let existing = null;
14863
14665
  try {
14864
- existing = await (0, import_promises9.readFile)(indexPath, "utf-8");
14666
+ existing = await (0, import_promises8.readFile)(indexPath, "utf-8");
14865
14667
  } catch {
14866
14668
  }
14867
14669
  if (existing === next) return;
14868
- await (0, import_promises9.writeFile)(indexPath, next);
14670
+ await (0, import_promises8.writeFile)(indexPath, next);
14869
14671
  }
14870
14672
  function pickFrontmatter(content, key) {
14871
14673
  const re = new RegExp(`^${key}:\\s*"?([^"\\n]+?)"?\\s*$`, "m");
@@ -14892,169 +14694,6 @@ var LEGACY_MD_END = "<!-- gate-memory:end -->";
14892
14694
  var LEGACY_PRESERVE_START = "<!-- gate-memory:preserve -->";
14893
14695
  var LEGACY_PRESERVE_END = "<!-- /gate-memory:preserve -->";
14894
14696
  var CLAUDE_MD_PROSE = [
14895
- "## Project Memory",
14896
- "",
14897
- "This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
14898
- "non-trivial work, scan `.verity/memory/index.md` for decisions, gotchas, and patterns",
14899
- "that may apply to the change you are about to make. Open specific node files via",
14900
- "the Read tool when the title or scope suggests relevance.",
14901
- "",
14902
- "The graph is auto-maintained by Verity. Files at `.verity/memory/_archive/` are",
14903
- "superseded \u2014 ignore them unless investigating history.",
14904
- "",
14905
- "Knowledge the organization's other repositories learned is mirrored outside the",
14906
- "repo at `~/.verity/orgs/<host>/<owner>/memory/` (`verity memory org` lists it).",
14907
- "Each claim says where it came from; one that is wrong here is demoted for",
14908
- 'everyone with `verity memory demote <id> --reason "\u2026"`.',
14909
- "",
14910
- "## Quality gate: accepted risks",
14911
- "",
14912
- "When the Verity pre-commit/pre-push gate FAILs, fix the findings \u2014 that is the",
14913
- 'default. Use `verity waive <pattern-id> --file <path> --reason "\u2026"` ONLY to relay',
14914
- "a risk a human has explicitly accepted: a named code-review finding, an ADR, or",
14915
- "the user saying so in this conversation. The --reason must cite that source.",
14916
- "",
14917
- "Never waive on your own judgment, to get past a block, or pre-emptively. A waive",
14918
- "binds to the file's current bytes and voids automatically when the file changes,",
14919
- "and every waive is recorded in the run ledger. For a pattern-level false positive",
14920
- "use `verity feedback finding <run-id> <pattern-id> false_positive` instead.",
14921
- "",
14922
- "## Post-task reflection",
14923
- "",
14924
- `When a task is complete (you've created a PR, the user says "done" or "ship it",`,
14925
- "or the work is clearly finished), **draft the reflection yourself** \u2014 1\u20133",
14926
- "concrete things worth remembering (a decision, a gotcha, or a pattern), each",
14927
- "cited with the files / PR / commands / error-signatures it came from. Skip",
14928
- "entirely if nothing non-obvious happened \u2014 that judgement is the ONLY filter,",
14929
- "because nothing reviews the reflection before it lands.",
14930
- "",
14931
- "Then record it straight away. There is no confirm step, in any environment:",
14932
- "",
14933
- "```bash",
14934
- 'verity reflect --user-input "<your draft>" --kind <kind>',
14935
- "```",
14936
- "",
14937
- "Add `--confirmed` ONLY when the user authored or dictated the words. Without",
14938
- "it the node is stored as `source: agent` \u2014 Verity thought this, nobody checked",
14939
- "it. With it, `source: user` at full confidence \u2014 a person stands behind this.",
14940
- "Never claim the second for your own draft, however good it is.",
14941
- "",
14942
- "**Name the files in the text.** Verity scopes the reflection to the paths it",
14943
- "cites, and a reflection that names no file in this repo is never retrieved for",
14944
- "a later review \u2014 it is recorded and then invisible. The command says so when it",
14945
- 'happens; `--file-globs "<path or glob>"` is the fix when the prose cannot carry',
14946
- "the paths.",
14947
- "",
14948
- "Then tell the user, in one line, what you recorded and where: the command",
14949
- "prints the node id, the path under `.verity/memory/`, and a dashboard link.",
14950
- "They did not agree to it in advance, so say it happened \u2014 editing or deleting",
14951
- "that file is how they correct it.",
14952
- "",
14953
- "> Durable, hand-curated guidance goes in the preserve region below (it survives",
14954
- "> regeneration) or anywhere OUTSIDE these markers. Everything else between the",
14955
- "> markers is tool-owned and overwritten on each run.",
14956
- "",
14957
- "## Housekeeping Turns",
14958
- "",
14959
- "When a turn will be pure housekeeping \u2014 pulling, installing dependencies,",
14960
- "rebasing, a formatting sweep you are not authoring \u2014 declare it BEFORE doing it:",
14961
- "",
14962
- "```bash",
14963
- 'verity ignore --turn --agent --reason "pulling latest before starting"',
14964
- "```",
14965
- "",
14966
- "This skips the review for that turn, which saves the turn Verity would",
14967
- "otherwise spend saying it had nothing to say. Use `--for 30m` instead of",
14968
- "`--turn` when a single piece of housekeeping spans several turns.",
14969
- "",
14970
- "**It is a claim about the turn, not a way to silence review.** The declaration",
14971
- "is checked against what the turn actually did: if anything is authored \u2014 by you,",
14972
- "by a subagent, or by a shell command that can write files \u2014 it voids, the review",
14973
- "runs anyway, and the broken declaration is reported. So declare housekeeping you",
14974
- "are about to do, never work you have already done, and never as a way to get past",
14975
- "a finding. Declarations are budgeted per session and every one is recorded with",
14976
- "its reason."
14977
- ].join("\n");
14978
- var CLAUDE_MD_PROSE_PRE_ORG = [
14979
- "## Project Memory",
14980
- "",
14981
- "This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
14982
- "non-trivial work, scan `.verity/memory/index.md` for decisions, gotchas, and patterns",
14983
- "that may apply to the change you are about to make. Open specific node files via",
14984
- "the Read tool when the title or scope suggests relevance.",
14985
- "",
14986
- "The graph is auto-maintained by Verity. Files at `.verity/memory/_archive/` are",
14987
- "superseded \u2014 ignore them unless investigating history.",
14988
- "",
14989
- "## Quality gate: accepted risks",
14990
- "",
14991
- "When the Verity pre-commit/pre-push gate FAILs, fix the findings \u2014 that is the",
14992
- 'default. Use `verity waive <pattern-id> --file <path> --reason "\u2026"` ONLY to relay',
14993
- "a risk a human has explicitly accepted: a named code-review finding, an ADR, or",
14994
- "the user saying so in this conversation. The --reason must cite that source.",
14995
- "",
14996
- "Never waive on your own judgment, to get past a block, or pre-emptively. A waive",
14997
- "binds to the file's current bytes and voids automatically when the file changes,",
14998
- "and every waive is recorded in the run ledger. For a pattern-level false positive",
14999
- "use `verity feedback finding <run-id> <pattern-id> false_positive` instead.",
15000
- "",
15001
- "## Post-task reflection",
15002
- "",
15003
- `When a task is complete (you've created a PR, the user says "done" or "ship it",`,
15004
- "or the work is clearly finished), **draft the reflection yourself** \u2014 1\u20133",
15005
- "concrete things worth remembering (a decision, a gotcha, or a pattern), each",
15006
- "cited with the files / PR / commands / error-signatures it came from. Skip",
15007
- "entirely if nothing non-obvious happened \u2014 that judgement is the ONLY filter,",
15008
- "because nothing reviews the reflection before it lands.",
15009
- "",
15010
- "Then record it straight away. There is no confirm step, in any environment:",
15011
- "",
15012
- "```bash",
15013
- 'verity reflect --user-input "<your draft>" --kind <kind>',
15014
- "```",
15015
- "",
15016
- "Add `--confirmed` ONLY when the user authored or dictated the words. Without",
15017
- "it the node is stored as `source: agent` \u2014 Verity thought this, nobody checked",
15018
- "it. With it, `source: user` at full confidence \u2014 a person stands behind this.",
15019
- "Never claim the second for your own draft, however good it is.",
15020
- "",
15021
- "**Name the files in the text.** Verity scopes the reflection to the paths it",
15022
- "cites, and a reflection that names no file in this repo is never retrieved for",
15023
- "a later review \u2014 it is recorded and then invisible. The command says so when it",
15024
- 'happens; `--file-globs "<path or glob>"` is the fix when the prose cannot carry',
15025
- "the paths.",
15026
- "",
15027
- "Then tell the user, in one line, what you recorded and where: the command",
15028
- "prints the node id, the path under `.verity/memory/`, and a dashboard link.",
15029
- "They did not agree to it in advance, so say it happened \u2014 editing or deleting",
15030
- "that file is how they correct it.",
15031
- "",
15032
- "> Durable, hand-curated guidance goes in the preserve region below (it survives",
15033
- "> regeneration) or anywhere OUTSIDE these markers. Everything else between the",
15034
- "> markers is tool-owned and overwritten on each run.",
15035
- "",
15036
- "## Housekeeping Turns",
15037
- "",
15038
- "When a turn will be pure housekeeping \u2014 pulling, installing dependencies,",
15039
- "rebasing, a formatting sweep you are not authoring \u2014 declare it BEFORE doing it:",
15040
- "",
15041
- "```bash",
15042
- 'verity ignore --turn --agent --reason "pulling latest before starting"',
15043
- "```",
15044
- "",
15045
- "This skips the review for that turn, which saves the turn Verity would",
15046
- "otherwise spend saying it had nothing to say. Use `--for 30m` instead of",
15047
- "`--turn` when a single piece of housekeeping spans several turns.",
15048
- "",
15049
- "**It is a claim about the turn, not a way to silence review.** The declaration",
15050
- "is checked against what the turn actually did: if anything is authored \u2014 by you,",
15051
- "by a subagent, or by a shell command that can write files \u2014 it voids, the review",
15052
- "runs anyway, and the broken declaration is reported. So declare housekeeping you",
15053
- "are about to do, never work you have already done, and never as a way to get past",
15054
- "a finding. Declarations are budgeted per session and every one is recorded with",
15055
- "its reason."
15056
- ].join("\n");
15057
- var CLAUDE_MD_PROSE_PRE_AUTORECORD = [
15058
14697
  "## Project Memory",
15059
14698
  "",
15060
14699
  "This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
@@ -15258,7 +14897,7 @@ async function ensureClaudeMdPointer(cwd = repoRoot()) {
15258
14897
  const claudeMdPath = (0, import_node_path13.join)(cwd, "CLAUDE.md");
15259
14898
  let existing = "";
15260
14899
  if ((0, import_node_fs14.existsSync)(claudeMdPath)) {
15261
- existing = await (0, import_promises9.readFile)(claudeMdPath, "utf-8");
14900
+ existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
15262
14901
  }
15263
14902
  let startTag = CLAUDE_MD_START;
15264
14903
  let endTag = CLAUDE_MD_END;
@@ -15314,7 +14953,7 @@ async function ensureClaudeMdPointer(cwd = repoRoot()) {
15314
14953
  next = existing.replace(/\n*$/, "") + "\n\n" + block + "\n";
15315
14954
  }
15316
14955
  if (next === existing) return;
15317
- await (0, import_promises9.writeFile)(claudeMdPath, next);
14956
+ await (0, import_promises8.writeFile)(claudeMdPath, next);
15318
14957
  }
15319
14958
  function extractPreserveContent(interior) {
15320
14959
  for (const [start, end] of [
@@ -15333,8 +14972,6 @@ function stripKnownProse(interior) {
15333
14972
  const trimmed = interior.replace(/^\n+/, "");
15334
14973
  for (const prose of [
15335
14974
  CLAUDE_MD_PROSE,
15336
- CLAUDE_MD_PROSE_PRE_ORG,
15337
- CLAUDE_MD_PROSE_PRE_AUTORECORD,
15338
14975
  CLAUDE_MD_PROSE_PRE_REFLECT,
15339
14976
  CLAUDE_MD_PROSE_PRE_WAIVE,
15340
14977
  CLAUDE_MD_PROSE_PRE_IGNORE,
@@ -16954,112 +16591,21 @@ async function fireClassify(prompt, sessionId, globals) {
16954
16591
 
16955
16592
  // src/commands/lifecycle.ts
16956
16593
  var import_node_fs23 = require("node:fs");
16957
- var import_node_path21 = require("node:path");
16594
+ var import_node_path20 = require("node:path");
16958
16595
 
16959
16596
  // src/lib/baseline.ts
16960
16597
  var import_node_fs22 = require("node:fs");
16961
- var import_node_path20 = require("node:path");
16598
+ var import_node_path19 = require("node:path");
16962
16599
  var import_node_crypto9 = require("node:crypto");
16963
16600
 
16964
16601
  // src/lib/snapshot.ts
16965
16602
  var import_node_fs21 = require("node:fs");
16966
- var import_node_path19 = require("node:path");
16603
+ var import_node_path18 = require("node:path");
16967
16604
  var import_node_child_process7 = require("node:child_process");
16968
16605
 
16969
16606
  // src/lib/files.ts
16970
- var import_node_path18 = require("node:path");
16971
-
16972
- // src/lib/safe-read.ts
16973
16607
  var import_node_fs20 = require("node:fs");
16974
16608
  var import_node_path17 = require("node:path");
16975
- var FLAGS = import_node_fs20.constants;
16976
- var NOFOLLOW = FLAGS.O_NOFOLLOW;
16977
- var NONBLOCK = FLAGS.O_NONBLOCK;
16978
- function closeOpened(fd) {
16979
- try {
16980
- (0, import_node_fs20.closeSync)(fd);
16981
- } catch {
16982
- }
16983
- }
16984
- function isInsideRoot(realRoot, realPath) {
16985
- return realPath === realRoot || realPath.startsWith(realRoot.endsWith(import_node_path17.sep) ? realRoot : realRoot + import_node_path17.sep);
16986
- }
16987
- function sameOpenedFile(opened, current, nofollowAvailable) {
16988
- if (!nofollowAvailable && (opened.ino === 0n || current.ino === 0n)) return false;
16989
- return opened.ino === current.ino && opened.dev === current.dev;
16990
- }
16991
- function openRegularInRoot(root, path) {
16992
- const full = (0, import_node_path17.isAbsolute)(path) ? path : (0, import_node_path17.join)(root, path);
16993
- let fd;
16994
- try {
16995
- fd = (0, import_node_fs20.openSync)(full, import_node_fs20.constants.O_RDONLY | (NOFOLLOW ?? 0) | (NONBLOCK ?? 0));
16996
- } catch (err) {
16997
- const code = err.code;
16998
- if (code === "ELOOP" || code === "EMLINK" || code === "EFTYPE") return { ok: false, reason: "symlink" };
16999
- if (code === "ENOENT" || code === "ENOTDIR") return { ok: false, reason: "missing" };
17000
- return { ok: false, reason: "unreadable" };
17001
- }
17002
- try {
17003
- const opened = (0, import_node_fs20.fstatSync)(fd, { bigint: true });
17004
- if (!opened.isFile()) {
17005
- closeOpened(fd);
17006
- return { ok: false, reason: "not-regular" };
17007
- }
17008
- const realRoot = import_node_fs20.realpathSync.native(root);
17009
- const realPath = import_node_fs20.realpathSync.native(full);
17010
- if (!isInsideRoot(realRoot, realPath)) {
17011
- closeOpened(fd);
17012
- return { ok: false, reason: "outside-root" };
17013
- }
17014
- const current = (0, import_node_fs20.statSync)(realPath, { bigint: true });
17015
- if (!sameOpenedFile(opened, current, NOFOLLOW !== void 0)) {
17016
- closeOpened(fd);
17017
- return { ok: false, reason: NOFOLLOW === void 0 ? "unreadable" : "outside-root" };
17018
- }
17019
- return { ok: true, fd, size: Number(opened.size), mtimeMs: Number(opened.mtimeMs) };
17020
- } catch {
17021
- closeOpened(fd);
17022
- return { ok: false, reason: "unreadable" };
17023
- }
17024
- }
17025
- function statRegularInRoot(root, path) {
17026
- const opened = openRegularInRoot(root, path);
17027
- if (!opened.ok) return opened;
17028
- closeOpened(opened.fd);
17029
- return { ok: true, size: opened.size, mtimeMs: opened.mtimeMs };
17030
- }
17031
- function readOpened(fd, size) {
17032
- try {
17033
- const buffer = Buffer.alloc(size);
17034
- let offset = 0;
17035
- while (offset < size) {
17036
- const n = (0, import_node_fs20.readSync)(fd, buffer, offset, size - offset, offset);
17037
- if (n === 0) break;
17038
- offset += n;
17039
- }
17040
- return buffer.subarray(0, offset);
17041
- } finally {
17042
- closeOpened(fd);
17043
- }
17044
- }
17045
- function readOpenedUtf8(fd, size) {
17046
- return readOpened(fd, size).toString("utf-8");
17047
- }
17048
- function readRegularFileInRoot(root, path, maxBytes) {
17049
- const opened = openRegularInRoot(root, path);
17050
- if (!opened.ok) return opened;
17051
- if (opened.size > maxBytes) {
17052
- closeOpened(opened.fd);
17053
- return { ok: false, reason: "too-large", size: opened.size };
17054
- }
17055
- try {
17056
- return { ok: true, content: readOpenedUtf8(opened.fd, opened.size), size: opened.size };
17057
- } catch {
17058
- return { ok: false, reason: "unreadable" };
17059
- }
17060
- }
17061
-
17062
- // src/lib/files.ts
17063
16609
  var LANG_MAP = {
17064
16610
  // Analyzable (static analysis + Gemini)
17065
16611
  ts: "typescript",
@@ -17127,14 +16673,19 @@ var LANG_MAP = {
17127
16673
  mk: "make"
17128
16674
  };
17129
16675
  function detectLanguage(filepath) {
17130
- const ext = (0, import_node_path18.extname)(filepath).slice(1);
16676
+ const ext = (0, import_node_path17.extname)(filepath).slice(1);
17131
16677
  return LANG_MAP[ext] ?? ext;
17132
16678
  }
17133
16679
  function sortByMtime(files) {
17134
16680
  const withMtime = files.map((f) => {
17135
- const stat3 = statRegularInRoot(process.cwd(), f);
17136
- if (stat3.ok) return { path: f, mtime: stat3.mtimeMs };
17137
- return stat3.reason === "missing" ? null : { path: f, mtime: Number.NEGATIVE_INFINITY };
16681
+ const resolved = resolveFile(f);
16682
+ if (!resolved) return null;
16683
+ try {
16684
+ const stat3 = (0, import_node_fs20.statSync)(resolved);
16685
+ return { path: f, resolved, mtime: stat3.mtimeMs };
16686
+ } catch {
16687
+ return null;
16688
+ }
17138
16689
  }).filter((x) => x !== null);
17139
16690
  withMtime.sort((a, b) => b.mtime - a.mtime);
17140
16691
  return withMtime.map((x) => x.path);
@@ -17156,22 +16707,23 @@ function collectCodeDelta(files, opts) {
17156
16707
  droppedPaths.push(filepath);
17157
16708
  continue;
17158
16709
  }
17159
- const opened = openRegularInRoot(process.cwd(), filepath);
17160
- if (!opened.ok) {
17161
- exclude(
17162
- filepath,
17163
- opened.reason === "symlink" || opened.reason === "outside-root" ? "symlink" : opened.reason === "missing" ? "path-not-resolvable" : "not-stattable"
17164
- );
16710
+ const resolved = resolveFile(filepath);
16711
+ if (!resolved) {
16712
+ exclude(filepath, "path-not-resolvable");
16713
+ continue;
16714
+ }
16715
+ let size;
16716
+ try {
16717
+ size = (0, import_node_fs20.statSync)(resolved).size;
16718
+ } catch {
16719
+ exclude(filepath, "not-stattable");
17165
16720
  continue;
17166
16721
  }
17167
- const size = opened.size;
17168
16722
  if (size > maxFileBytes) {
17169
- closeOpened(opened.fd);
17170
16723
  exclude(filepath, `over-file-size-limit-${maxFileBytes}b`);
17171
16724
  continue;
17172
16725
  }
17173
16726
  if (totalSize + size > maxTotalBytes) {
17174
- closeOpened(opened.fd);
17175
16727
  truncationReason ??= "max_total_bytes";
17176
16728
  const idx = sorted.indexOf(filepath);
17177
16729
  droppedPaths.push(...sorted.slice(idx));
@@ -17179,7 +16731,7 @@ function collectCodeDelta(files, opts) {
17179
16731
  }
17180
16732
  let content;
17181
16733
  try {
17182
- content = readOpenedUtf8(opened.fd, size);
16734
+ content = (0, import_node_fs20.readFileSync)(resolved, "utf-8");
17183
16735
  } catch {
17184
16736
  exclude(filepath, "not-readable");
17185
16737
  continue;
@@ -17224,7 +16776,7 @@ function generateSnapshotDiffs(files) {
17224
16776
  const diffs = [];
17225
16777
  for (const file of files) {
17226
16778
  if (!resolveInside(SNAPSHOT_DIR, file.path)) continue;
17227
- const snapshotPath = (0, import_node_path19.join)(SNAPSHOT_DIR, file.path);
16779
+ const snapshotPath = (0, import_node_path18.join)(SNAPSHOT_DIR, file.path);
17228
16780
  const language = file.language ?? detectLanguage(file.path);
17229
16781
  if ((0, import_node_fs21.existsSync)(snapshotPath)) {
17230
16782
  const oldContent = (0, import_node_fs21.readFileSync)(snapshotPath, "utf-8");
@@ -17252,16 +16804,16 @@ function saveSnapshots(files) {
17252
16804
  const snapshotPaths = /* @__PURE__ */ new Set();
17253
16805
  for (const file of files) {
17254
16806
  if (!resolveInside(SNAPSHOT_DIR, file.path)) continue;
17255
- const snapshotPath = (0, import_node_path19.join)(SNAPSHOT_DIR, file.path);
16807
+ const snapshotPath = (0, import_node_path18.join)(SNAPSHOT_DIR, file.path);
17256
16808
  snapshotPaths.add(snapshotPath);
17257
- (0, import_node_fs21.mkdirSync)((0, import_node_path19.dirname)(snapshotPath), { recursive: true });
16809
+ (0, import_node_fs21.mkdirSync)((0, import_node_path18.dirname)(snapshotPath), { recursive: true });
17258
16810
  (0, import_node_fs21.writeFileSync)(snapshotPath, file.content);
17259
16811
  }
17260
16812
  cleanStaleSnapshots(SNAPSHOT_DIR, snapshotPaths);
17261
16813
  }
17262
16814
  function computeDiff(oldContent, newContent, filePath) {
17263
- const tmpOld = (0, import_node_path19.join)(SNAPSHOT_DIR, ".diff-old.tmp");
17264
- const tmpNew = (0, import_node_path19.join)(SNAPSHOT_DIR, ".diff-new.tmp");
16815
+ const tmpOld = (0, import_node_path18.join)(SNAPSHOT_DIR, ".diff-old.tmp");
16816
+ const tmpNew = (0, import_node_path18.join)(SNAPSHOT_DIR, ".diff-new.tmp");
17265
16817
  try {
17266
16818
  (0, import_node_fs21.mkdirSync)(SNAPSHOT_DIR, { recursive: true });
17267
16819
  (0, import_node_fs21.writeFileSync)(tmpOld, oldContent);
@@ -17294,7 +16846,7 @@ function cleanStaleSnapshots(dir, keepSet) {
17294
16846
  const entries = (0, import_node_fs21.readdirSync)(dir, { withFileTypes: true });
17295
16847
  for (const entry of entries) {
17296
16848
  if (dir === SNAPSHOT_DIR && (entry.name === ".diff-old.tmp" || entry.name === ".diff-new.tmp")) continue;
17297
- const fullPath = (0, import_node_path19.join)(dir, entry.name);
16849
+ const fullPath = (0, import_node_path18.join)(dir, entry.name);
17298
16850
  if (entry.isDirectory()) {
17299
16851
  cleanStaleSnapshots(fullPath, keepSet);
17300
16852
  try {
@@ -17323,13 +16875,13 @@ function sessionKey(sessionId) {
17323
16875
  return (0, import_node_crypto9.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
17324
16876
  }
17325
16877
  function sessionDir(key) {
17326
- return (0, import_node_path20.join)(projectPath(BASELINE_DIR), key);
16878
+ return (0, import_node_path19.join)(projectPath(BASELINE_DIR), key);
17327
16879
  }
17328
16880
  function manifestPath(dir) {
17329
- return (0, import_node_path20.join)(dir, "manifest.json");
16881
+ return (0, import_node_path19.join)(dir, "manifest.json");
17330
16882
  }
17331
16883
  function mirrorPath(dir, repoRelPath) {
17332
- return (0, import_node_path20.join)(dir, "files", repoRelPath);
16884
+ return (0, import_node_path19.join)(dir, "files", repoRelPath);
17333
16885
  }
17334
16886
  var CARRY_FILE = `${BASELINE_DIR}/.carry`;
17335
16887
  var CARRY_WINDOW_MS = 12e4;
@@ -17391,7 +16943,7 @@ function captureBaseline(opts = {}) {
17391
16943
  (0, import_node_fs22.rmSync)(dir, { recursive: true, force: true });
17392
16944
  } catch {
17393
16945
  }
17394
- const filesDir = (0, import_node_path20.join)(dir, "files");
16946
+ const filesDir = (0, import_node_path19.join)(dir, "files");
17395
16947
  const mirrored = [];
17396
16948
  try {
17397
16949
  (0, import_node_fs22.mkdirSync)(filesDir, { recursive: true });
@@ -17401,7 +16953,7 @@ function captureBaseline(opts = {}) {
17401
16953
  if (content === null) continue;
17402
16954
  const dest = mirrorPath(dir, p);
17403
16955
  try {
17404
- (0, import_node_fs22.mkdirSync)((0, import_node_path20.dirname)(dest), { recursive: true });
16956
+ (0, import_node_fs22.mkdirSync)((0, import_node_path19.dirname)(dest), { recursive: true });
17405
16957
  (0, import_node_fs22.writeFileSync)(dest, content);
17406
16958
  mirrored.push(p);
17407
16959
  } catch {
@@ -17509,7 +17061,7 @@ function absorbIntoBaseline(paths, sessionId) {
17509
17061
  const content = safeReadForMirror(projectPath(p));
17510
17062
  if (content === null) continue;
17511
17063
  const dest = mirrorPath(dir, p);
17512
- (0, import_node_fs22.mkdirSync)((0, import_node_path20.dirname)(dest), { recursive: true });
17064
+ (0, import_node_fs22.mkdirSync)((0, import_node_path19.dirname)(dest), { recursive: true });
17513
17065
  (0, import_node_fs22.writeFileSync)(dest, content);
17514
17066
  dirty.add(p);
17515
17067
  adopted++;
@@ -17557,7 +17109,7 @@ function pruneOldBaselines() {
17557
17109
  }
17558
17110
  const now = Date.now();
17559
17111
  for (const name of entries) {
17560
- const dir = (0, import_node_path20.join)(root, name);
17112
+ const dir = (0, import_node_path19.join)(root, name);
17561
17113
  const manifest = readManifest(dir);
17562
17114
  if (!manifest) {
17563
17115
  try {
@@ -17745,7 +17297,7 @@ function buildCompactionContext(session) {
17745
17297
  commitsSince: commitsSincePaths(watermark, (state.authored ?? []).map((a) => a.path)),
17746
17298
  readFileLines: (file) => {
17747
17299
  try {
17748
- const abs = (0, import_node_path21.join)(root, file);
17300
+ const abs = (0, import_node_path20.join)(root, file);
17749
17301
  return (0, import_node_fs23.existsSync)(abs) ? (0, import_node_fs23.readFileSync)(abs, "utf8").split("\n") : null;
17750
17302
  } catch {
17751
17303
  return null;
@@ -17782,17 +17334,17 @@ async function readHookStdin() {
17782
17334
  try {
17783
17335
  if (process.stdin.isTTY) return {};
17784
17336
  const chunks = [];
17785
- const timeout = new Promise((resolve6) => setTimeout(() => resolve6({}), 500));
17786
- const read = new Promise((resolve6) => {
17337
+ const timeout = new Promise((resolve5) => setTimeout(() => resolve5({}), 500));
17338
+ const read = new Promise((resolve5) => {
17787
17339
  process.stdin.on("data", (c) => chunks.push(c));
17788
17340
  process.stdin.on("end", () => {
17789
17341
  try {
17790
- resolve6(JSON.parse(Buffer.concat(chunks).toString("utf-8").trim() || "{}"));
17342
+ resolve5(JSON.parse(Buffer.concat(chunks).toString("utf-8").trim() || "{}"));
17791
17343
  } catch {
17792
- resolve6({});
17344
+ resolve5({});
17793
17345
  }
17794
17346
  });
17795
- process.stdin.on("error", () => resolve6({}));
17347
+ process.stdin.on("error", () => resolve5({}));
17796
17348
  process.stdin.resume();
17797
17349
  });
17798
17350
  return await Promise.race([read, timeout]);
@@ -17802,25 +17354,25 @@ async function readHookStdin() {
17802
17354
  }
17803
17355
 
17804
17356
  // src/commands/standard.ts
17805
- var import_promises13 = require("node:fs/promises");
17357
+ var import_promises12 = require("node:fs/promises");
17806
17358
  var import_node_fs30 = require("node:fs");
17807
17359
  var import_yaml3 = __toESM(require_dist());
17808
17360
 
17809
17361
  // src/lib/synthesize.ts
17810
17362
  var import_node_child_process9 = require("node:child_process");
17811
17363
  var import_node_fs26 = require("node:fs");
17812
- var import_promises10 = require("node:fs/promises");
17813
- var import_node_path24 = require("node:path");
17364
+ var import_promises9 = require("node:fs/promises");
17365
+ var import_node_path23 = require("node:path");
17814
17366
  var import_yaml = __toESM(require_dist());
17815
17367
 
17816
17368
  // src/lib/data-dir.ts
17817
17369
  var import_node_fs24 = require("node:fs");
17818
- var import_node_path22 = require("node:path");
17370
+ var import_node_path21 = require("node:path");
17819
17371
  function resolveDataDir() {
17820
17372
  const candidates2 = [
17821
- (0, import_node_path22.join)(__dirname, "..", "data"),
17373
+ (0, import_node_path21.join)(__dirname, "..", "data"),
17822
17374
  // installed: node_modules/@codacy/verity-cli/data
17823
- (0, import_node_path22.join)(__dirname, "..", "..", "data"),
17375
+ (0, import_node_path21.join)(__dirname, "..", "..", "data"),
17824
17376
  // edge case: nested resolution
17825
17377
  // THE COMMITTED SOURCE, for a source checkout that has not been built.
17826
17378
  // cli/data/skills/ is a BUILD ARTIFACT (scripts/build.js copies client/skills
@@ -17829,14 +17381,14 @@ function resolveDataDir() {
17829
17381
  // without this the synthesizer throws "Could not find Verity skill data"
17830
17382
  // for every test and every `verity` run from source. Resolved from this
17831
17383
  // module's own location, never the cwd: see the warning below.
17832
- (0, import_node_path22.join)(__dirname, "..", "..", "client"),
17384
+ (0, import_node_path21.join)(__dirname, "..", "..", "client"),
17833
17385
  // bundled: cli/bin/ → ../../client
17834
- (0, import_node_path22.join)(__dirname, "..", "..", "..", "client"),
17386
+ (0, import_node_path21.join)(__dirname, "..", "..", "..", "client"),
17835
17387
  // tsx: cli/src/lib/ → ../../../client
17836
17388
  ...process.env.VERITY_DEV_DATA_DIR ? [process.env.VERITY_DEV_DATA_DIR] : []
17837
17389
  ];
17838
17390
  for (const candidate of candidates2) {
17839
- if ((0, import_node_fs24.existsSync)((0, import_node_path22.join)(candidate, "skills"))) {
17391
+ if ((0, import_node_fs24.existsSync)((0, import_node_path21.join)(candidate, "skills"))) {
17840
17392
  return candidate;
17841
17393
  }
17842
17394
  }
@@ -17845,13 +17397,13 @@ function resolveDataDir() {
17845
17397
  );
17846
17398
  }
17847
17399
  function setupDataPath(file) {
17848
- return (0, import_node_path22.join)(resolveDataDir(), "skills", "verity-setup", file);
17400
+ return (0, import_node_path21.join)(resolveDataDir(), "skills", "verity-setup", file);
17849
17401
  }
17850
17402
 
17851
17403
  // src/lib/detect.ts
17852
17404
  var import_node_child_process8 = require("node:child_process");
17853
17405
  var import_node_fs25 = require("node:fs");
17854
- var import_node_path23 = require("node:path");
17406
+ var import_node_path22 = require("node:path");
17855
17407
  var TOOLED_LANGUAGES = /* @__PURE__ */ new Set([
17856
17408
  "typescript",
17857
17409
  "javascript",
@@ -17915,18 +17467,18 @@ function walk(root) {
17915
17467
  for (const entry of entries) {
17916
17468
  if (found.length >= WALK_MAX_FILES) return;
17917
17469
  if (IGNORED_SEGMENTS.includes(entry.name)) continue;
17918
- const full = (0, import_node_path23.join)(dir, entry.name);
17470
+ const full = (0, import_node_path22.join)(dir, entry.name);
17919
17471
  if (entry.isDirectory()) visit(full, depth + 1);
17920
- else if (entry.isFile()) found.push((0, import_node_path23.relative)(root, full));
17472
+ else if (entry.isFile()) found.push((0, import_node_path22.relative)(root, full));
17921
17473
  }
17922
17474
  };
17923
17475
  visit(root, 0);
17924
17476
  return found;
17925
17477
  }
17926
17478
  function languageOf(path) {
17927
- const name = (0, import_node_path23.basename)(path);
17479
+ const name = (0, import_node_path22.basename)(path);
17928
17480
  if (/^Dockerfile(\..+)?$/i.test(name)) return "dockerfile";
17929
- if (!(0, import_node_path23.extname)(name)) return null;
17481
+ if (!(0, import_node_path22.extname)(name)) return null;
17930
17482
  const lang = detectLanguage(path);
17931
17483
  return lang || null;
17932
17484
  }
@@ -18008,12 +17560,12 @@ function declaredDependencies(root, files) {
18008
17560
  if (deps && typeof deps === "object") names2.push(...Object.keys(deps));
18009
17561
  }
18010
17562
  };
18011
- readPackageJson((0, import_node_path23.join)(root, "package.json"));
18012
- const nested = files.filter((f) => f.includes("/") && (0, import_node_path23.basename)(f) === "package.json").slice(0, NESTED_MANIFEST_LIMIT);
18013
- for (const rel of nested) readPackageJson((0, import_node_path23.join)(root, rel));
17563
+ readPackageJson((0, import_node_path22.join)(root, "package.json"));
17564
+ const nested = files.filter((f) => f.includes("/") && (0, import_node_path22.basename)(f) === "package.json").slice(0, NESTED_MANIFEST_LIMIT);
17565
+ for (const rel of nested) readPackageJson((0, import_node_path22.join)(root, rel));
18014
17566
  const pythonManifests = [
18015
- ...["pyproject.toml", "requirements.txt", "Pipfile", "setup.py"].map((f) => (0, import_node_path23.join)(root, f)),
18016
- ...files.filter((f) => f.includes("/") && /(^|\/)(pyproject\.toml|requirements\.txt)$/.test(f)).slice(0, NESTED_MANIFEST_LIMIT).map((f) => (0, import_node_path23.join)(root, f))
17567
+ ...["pyproject.toml", "requirements.txt", "Pipfile", "setup.py"].map((f) => (0, import_node_path22.join)(root, f)),
17568
+ ...files.filter((f) => f.includes("/") && /(^|\/)(pyproject\.toml|requirements\.txt)$/.test(f)).slice(0, NESTED_MANIFEST_LIMIT).map((f) => (0, import_node_path22.join)(root, f))
18017
17569
  ];
18018
17570
  for (const path of pythonManifests) {
18019
17571
  if (!(0, import_node_fs25.existsSync)(path)) continue;
@@ -18028,8 +17580,8 @@ function declaredDependencies(root, files) {
18028
17580
  }
18029
17581
  }
18030
17582
  const goMods = [
18031
- (0, import_node_path23.join)(root, "go.mod"),
18032
- ...files.filter((f) => f.includes("/") && (0, import_node_path23.basename)(f) === "go.mod").slice(0, NESTED_MANIFEST_LIMIT).map((f) => (0, import_node_path23.join)(root, f))
17583
+ (0, import_node_path22.join)(root, "go.mod"),
17584
+ ...files.filter((f) => f.includes("/") && (0, import_node_path22.basename)(f) === "go.mod").slice(0, NESTED_MANIFEST_LIMIT).map((f) => (0, import_node_path22.join)(root, f))
18033
17585
  ];
18034
17586
  for (const path of goMods) {
18035
17587
  if (!(0, import_node_fs25.existsSync)(path)) continue;
@@ -18042,7 +17594,7 @@ function declaredDependencies(root, files) {
18042
17594
  }
18043
17595
  }
18044
17596
  for (const file of ["pom.xml", "build.gradle", "build.gradle.kts", "Gemfile", "Cargo.toml"]) {
18045
- const path = (0, import_node_path23.join)(root, file);
17597
+ const path = (0, import_node_path22.join)(root, file);
18046
17598
  if (!(0, import_node_fs25.existsSync)(path)) continue;
18047
17599
  try {
18048
17600
  const text = (0, import_node_fs25.readFileSync)(path, "utf-8");
@@ -18053,7 +17605,7 @@ function declaredDependencies(root, files) {
18053
17605
  return names2;
18054
17606
  }
18055
17607
  function detectBuildSystem(root, files) {
18056
- const has = (f) => (0, import_node_fs25.existsSync)((0, import_node_path23.join)(root, f)) || files.some((p) => (0, import_node_path23.basename)(p) === f);
17608
+ const has = (f) => (0, import_node_fs25.existsSync)((0, import_node_path22.join)(root, f)) || files.some((p) => (0, import_node_path22.basename)(p) === f);
18057
17609
  if (has("pnpm-lock.yaml")) return "pnpm";
18058
17610
  if (has("yarn.lock")) return "yarn";
18059
17611
  if (has("bun.lock") || has("bun.lockb")) return "bun";
@@ -18070,8 +17622,8 @@ function detectBuildSystem(root, files) {
18070
17622
  }
18071
17623
  function detectArchitecture(root, files) {
18072
17624
  const workspaceMarkers = ["lerna.json", "pnpm-workspace.yaml", "nx.json", "turbo.json", "rush.json"];
18073
- if (workspaceMarkers.some((m) => (0, import_node_fs25.existsSync)((0, import_node_path23.join)(root, m)))) return "monorepo";
18074
- const pkg = readJson((0, import_node_path23.join)(root, "package.json"));
17625
+ if (workspaceMarkers.some((m) => (0, import_node_fs25.existsSync)((0, import_node_path22.join)(root, m)))) return "monorepo";
17626
+ const pkg = readJson((0, import_node_path22.join)(root, "package.json"));
18075
17627
  if (pkg && "workspaces" in pkg) return "monorepo";
18076
17628
  const manifests = files.filter((f) => /(^|\/)(package\.json|go\.mod|pyproject\.toml|Cargo\.toml|pom\.xml)$/.test(f));
18077
17629
  const nested = manifests.filter((f) => f.includes("/"));
@@ -18093,7 +17645,7 @@ function measureAvgFileLength(root, files, languages) {
18093
17645
  let total = 0;
18094
17646
  let counted = 0;
18095
17647
  for (let i = 0; i < candidates2.length; i += stride) {
18096
- const path = (0, import_node_path23.join)(root, candidates2[i]);
17648
+ const path = (0, import_node_path22.join)(root, candidates2[i]);
18097
17649
  try {
18098
17650
  if ((0, import_node_fs25.statSync)(path).size > 2 * 1024 * 1024) continue;
18099
17651
  total += (0, import_node_fs25.readFileSync)(path, "utf-8").split("\n").length;
@@ -18126,14 +17678,14 @@ function detectProject(root = repoRoot()) {
18126
17678
  const existingToolConfigs = [];
18127
17679
  for (const [tool, markers] of TOOL_CONFIG_MARKERS) {
18128
17680
  for (const marker of markers) {
18129
- if ((0, import_node_fs25.existsSync)((0, import_node_path23.join)(root, marker))) {
17681
+ if ((0, import_node_fs25.existsSync)((0, import_node_path22.join)(root, marker))) {
18130
17682
  existingToolConfigs.push({ tool, path: `./${marker}` });
18131
17683
  break;
18132
17684
  }
18133
17685
  }
18134
17686
  }
18135
17687
  return {
18136
- projectName: (0, import_node_path23.basename)(root),
17688
+ projectName: (0, import_node_path22.basename)(root),
18137
17689
  languages,
18138
17690
  languageCounts,
18139
17691
  frameworks: matchAll(dependencies, FRAMEWORK_BY_DEPENDENCY),
@@ -18577,10 +18129,10 @@ async function correctVerityMdVersion(opts) {
18577
18129
  origin: { kind: "synthesized", tools: opts.tools }
18578
18130
  }));
18579
18131
  }
18580
- async function writeFileTo(relative2, body) {
18581
- const target = projectPath(relative2);
18582
- await (0, import_promises10.mkdir)((0, import_node_path24.dirname)(target), { recursive: true });
18583
- await (0, import_promises10.writeFile)(target, body);
18132
+ async function writeFileTo(relative3, body) {
18133
+ const target = projectPath(relative3);
18134
+ await (0, import_promises9.mkdir)((0, import_node_path23.dirname)(target), { recursive: true });
18135
+ await (0, import_promises9.writeFile)(target, body);
18584
18136
  }
18585
18137
  async function deriveConfigForStandard(standard) {
18586
18138
  const spec = standard.knowledge_spec ?? {};
@@ -18637,14 +18189,14 @@ ${validation.detail}`);
18637
18189
  }
18638
18190
 
18639
18191
  // src/lib/setup-state.ts
18640
- var import_promises11 = require("node:fs/promises");
18192
+ var import_promises10 = require("node:fs/promises");
18641
18193
  var import_node_fs27 = require("node:fs");
18642
18194
  var SETUP_STATE_FILE = `${VERITY_DIR}/setup.json`;
18643
18195
  async function readSetupState() {
18644
18196
  const path = projectPath(SETUP_STATE_FILE);
18645
18197
  if (!(0, import_node_fs27.existsSync)(path)) return null;
18646
18198
  try {
18647
- const parsed = JSON.parse(await (0, import_promises11.readFile)(path, "utf-8"));
18199
+ const parsed = JSON.parse(await (0, import_promises10.readFile)(path, "utf-8"));
18648
18200
  return parsed && typeof parsed === "object" ? parsed : null;
18649
18201
  } catch {
18650
18202
  return null;
@@ -18659,7 +18211,7 @@ async function writeSetupState(patch) {
18659
18211
 
18660
18212
  // src/lib/push-setup.ts
18661
18213
  var import_node_fs29 = require("node:fs");
18662
- var import_promises12 = require("node:fs/promises");
18214
+ var import_promises11 = require("node:fs/promises");
18663
18215
  var import_yaml2 = __toESM(require_dist());
18664
18216
 
18665
18217
  // src/lib/verityignore.ts
@@ -18828,7 +18380,7 @@ async function pushStandardAndConfig(globals, what = {}) {
18828
18380
  const standardPath = projectPath(STANDARD_FILE);
18829
18381
  if (pushStandard && (0, import_node_fs29.existsSync)(standardPath)) {
18830
18382
  try {
18831
- const content = (0, import_yaml2.parse)(await (0, import_promises12.readFile)(standardPath, "utf-8"));
18383
+ const content = (0, import_yaml2.parse)(await (0, import_promises11.readFile)(standardPath, "utf-8"));
18832
18384
  const upload = buildStandardUpload(content, readVerityIgnoreRaw());
18833
18385
  if (upload.warning) lines.push(upload.warning);
18834
18386
  const result = await apiRequest({
@@ -18853,7 +18405,7 @@ async function pushStandardAndConfig(globals, what = {}) {
18853
18405
  const configPath = projectPath(CODACY_CONFIG_FILE);
18854
18406
  if (pushConfig && (0, import_node_fs29.existsSync)(configPath)) {
18855
18407
  try {
18856
- const content = JSON.parse(await (0, import_promises12.readFile)(configPath, "utf-8"));
18408
+ const content = JSON.parse(await (0, import_promises11.readFile)(configPath, "utf-8"));
18857
18409
  const result = await apiRequest({
18858
18410
  method: "POST",
18859
18411
  path: "/analysis-configs",
@@ -18887,7 +18439,7 @@ function registerStandardCommands(program2) {
18887
18439
  printError(`No ${STANDARD_FILE} here \u2014 run "verity standard synthesize" to create one.`);
18888
18440
  process.exit(1);
18889
18441
  }
18890
- const content = (0, import_yaml3.parse)(await (0, import_promises13.readFile)(standardPath, "utf-8"));
18442
+ const content = (0, import_yaml3.parse)(await (0, import_promises12.readFile)(standardPath, "utf-8"));
18891
18443
  const derived = await deriveConfigForStandard(content);
18892
18444
  for (const path of derived.written) printInfo(` ${path} \u2713`);
18893
18445
  for (const note of derived.notes) printWarn(` ${note}`);
@@ -18943,7 +18495,7 @@ function registerStandardCommands(program2) {
18943
18495
  }
18944
18496
  let yamlContent;
18945
18497
  try {
18946
- yamlContent = await (0, import_promises13.readFile)(opts.file, "utf-8");
18498
+ yamlContent = await (0, import_promises12.readFile)(opts.file, "utf-8");
18947
18499
  } catch {
18948
18500
  printError(`Cannot read ${opts.file}`);
18949
18501
  process.exit(1);
@@ -19040,7 +18592,7 @@ function parseIntensity(value) {
19040
18592
  }
19041
18593
 
19042
18594
  // src/commands/config.ts
19043
- var import_promises14 = require("node:fs/promises");
18595
+ var import_promises13 = require("node:fs/promises");
19044
18596
  function registerConfigCommands(program2) {
19045
18597
  const config = program2.command("config").description("Manage analysis configuration");
19046
18598
  config.command("service-url").description("Print the resolved service URL (used by the shipped skills)").action(async () => {
@@ -19086,7 +18638,7 @@ function registerConfigCommands(program2) {
19086
18638
  }
19087
18639
  let content;
19088
18640
  try {
19089
- const raw = await (0, import_promises14.readFile)(opts.file, "utf-8");
18641
+ const raw = await (0, import_promises13.readFile)(opts.file, "utf-8");
19090
18642
  content = JSON.parse(raw);
19091
18643
  } catch {
19092
18644
  printError(`Cannot read or parse ${opts.file}`);
@@ -19895,10 +19447,6 @@ function rgInvocations(env = process.env) {
19895
19447
  out.push({ cmd: `${(0, import_node_os4.homedir)()}/.local/bin/claude`, argv0: "rg" });
19896
19448
  return out;
19897
19449
  }
19898
- function ripgrepEnv(base = process.env) {
19899
- const { RIPGREP_CONFIG_PATH: _dropped, ...rest } = base;
19900
- return rest;
19901
- }
19902
19450
  var MAX_SYMBOLS = 12;
19903
19451
  var MAX_SITES = 24;
19904
19452
  var MAX_SITES_PER_FILE = 3;
@@ -20438,9 +19986,6 @@ function buildRepoContext(input) {
20438
19986
  };
20439
19987
  }
20440
19988
  const args = [
20441
- // `--no-config` + RIPGREP_CONFIG_PATH scrubbed below: a config file can add
20442
- // `--pre=<command>`, which runs a program per searched file.
20443
- "--no-config",
20444
19989
  "-n",
20445
19990
  "-w",
20446
19991
  "-F",
@@ -20473,7 +20018,6 @@ function buildRepoContext(input) {
20473
20018
  res = (0, import_node_child_process10.spawnSync)(inv.cmd, args, {
20474
20019
  ...inv.argv0 ? { argv0: inv.argv0 } : {},
20475
20020
  cwd: input.cwd ?? process.cwd(),
20476
- env: ripgrepEnv(),
20477
20021
  timeout: input.timeoutMs ?? RG_TIMEOUT_MS,
20478
20022
  maxBuffer: 4 * 1024 * 1024,
20479
20023
  encoding: "utf8"
@@ -20712,85 +20256,11 @@ function installRunEvidence(run2) {
20712
20256
  }
20713
20257
 
20714
20258
  // src/lib/git-frame.ts
20259
+ var import_node_child_process11 = require("node:child_process");
20715
20260
  var import_node_fs33 = require("node:fs");
20716
20261
  var import_node_os5 = require("node:os");
20262
+ var import_node_path24 = require("node:path");
20717
20263
  var import_node_path25 = require("node:path");
20718
- var import_node_path26 = require("node:path");
20719
-
20720
- // src/lib/hardened-git.ts
20721
- var import_node_child_process11 = require("node:child_process");
20722
- var HARDENED_CONFIG_ARGS = [
20723
- "-c",
20724
- "core.fsmonitor=false",
20725
- "-c",
20726
- "core.pager=cat",
20727
- "-c",
20728
- "log.showSignature=false"
20729
- ];
20730
- var DIFF_READ_FLAGS = ["--no-ext-diff", "--no-textconv"];
20731
- var DEFAULT_GIT_TIMEOUT_MS = 6e4;
20732
- var DEFAULT_GIT_MAX_BUFFER = 64 * 1024 * 1024;
20733
- function hardenedGitEnv(base = process.env) {
20734
- const env = {};
20735
- for (const [key, value] of Object.entries(base)) {
20736
- if (key.startsWith("GIT_")) continue;
20737
- env[key] = value;
20738
- }
20739
- env.GIT_OPTIONAL_LOCKS = "0";
20740
- env.GIT_TERMINAL_PROMPT = "0";
20741
- return env;
20742
- }
20743
- function hardenedGitArgv(args) {
20744
- return [...HARDENED_CONFIG_ARGS, ...args];
20745
- }
20746
- function classifyError(err) {
20747
- const e = err;
20748
- const stderr = typeof e?.stderr === "string" ? e.stderr : e?.stderr ? e.stderr.toString("utf-8") : "";
20749
- if (e?.code === "ETIMEDOUT" || e?.killed && e?.signal === "SIGTERM") return { ok: false, error: "timeout", stderr };
20750
- if (e?.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") return { ok: false, error: "too_large", stderr };
20751
- return { ok: false, error: "failed", stderr };
20752
- }
20753
- function runGitSync(cwd, args, opts = {}) {
20754
- try {
20755
- const stdout = (0, import_node_child_process11.execFileSync)("git", hardenedGitArgv(args), {
20756
- cwd,
20757
- env: hardenedGitEnv(),
20758
- encoding: "utf-8",
20759
- stdio: ["ignore", "pipe", "pipe"],
20760
- timeout: opts.timeoutMs ?? DEFAULT_GIT_TIMEOUT_MS,
20761
- maxBuffer: opts.maxBuffer ?? DEFAULT_GIT_MAX_BUFFER
20762
- });
20763
- return { ok: true, stdout };
20764
- } catch (err) {
20765
- return classifyError(err);
20766
- }
20767
- }
20768
- function runGit(cwd, args, opts = {}) {
20769
- return new Promise((resolvePromise) => {
20770
- (0, import_node_child_process11.execFile)(
20771
- "git",
20772
- hardenedGitArgv(args),
20773
- {
20774
- cwd,
20775
- env: hardenedGitEnv(),
20776
- encoding: "utf-8",
20777
- timeout: opts.timeoutMs ?? DEFAULT_GIT_TIMEOUT_MS,
20778
- maxBuffer: opts.maxBuffer ?? DEFAULT_GIT_MAX_BUFFER
20779
- },
20780
- (err, stdout, stderr) => {
20781
- if (err) {
20782
- const e = err;
20783
- e.stderr = stderr;
20784
- resolvePromise(classifyError(e));
20785
- return;
20786
- }
20787
- resolvePromise({ ok: true, stdout });
20788
- }
20789
- );
20790
- });
20791
- }
20792
-
20793
- // src/lib/git-frame.ts
20794
20264
  var VALUE_TOKEN = `(?:'[^']*'|"[^"]*"|\\S+)`;
20795
20265
  var GIT_GLOBAL_OPTS = `(?:\\s+(?:-[Cc]\\s+${VALUE_TOKEN}|--?[\\w-]+(?:=\\S+)?))*`;
20796
20266
  var COMMIT_HEAD = `git${GIT_GLOBAL_OPTS}\\s+commit(?![\\w-])`;
@@ -20800,41 +20270,7 @@ var COMMIT_RE = new RegExp(`(?:^|[\\s;&|(])${COMMIT_HEAD}|(?:^|[;&|(])\\s*[^\\s;
20800
20270
  var PUSH_RE = new RegExp(`(?:^|[\\s;&|(])${PUSH_HEAD}|(?:^|[;&|(])\\s*[^\\s;&|()'"]*\\/${PUSH_HEAD}`);
20801
20271
  var GH_PR_RE = new RegExp(`(?:^|[\\s;&|(])${GH_PR_HEAD}|(?:^|[;&|(])\\s*[^\\s;&|()'"]*\\/${GH_PR_HEAD}`);
20802
20272
  function splitSegments(command) {
20803
- return stripHeredocBodies(command ?? "").split(/&&|\|\||;|\n/);
20804
- }
20805
- var HEREDOC_OPERATOR = /(^|[^<])<<(-?)[ \t]*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][A-Za-z0-9_]*))/g;
20806
- var SHELL_CONSUMER = /(^|[\s;&|(])(bash|sh|zsh|dash|ksh|fish|ssh|eval|exec|sudo|su|xargs|env|nohup|time|docker|kubectl|podman)(\s|$)/;
20807
- function stripHeredocBodies(command) {
20808
- const lines = command.split("\n");
20809
- const out = [];
20810
- let i = 0;
20811
- while (i < lines.length) {
20812
- const line = lines[i];
20813
- out.push(line);
20814
- i++;
20815
- const words = [];
20816
- HEREDOC_OPERATOR.lastIndex = 0;
20817
- let m;
20818
- while ((m = HEREDOC_OPERATOR.exec(line)) !== null) {
20819
- words.push({ word: m[3] ?? m[4] ?? m[5], dash: m[2] === "-" });
20820
- }
20821
- if (words.length === 0 || SHELL_CONSUMER.test(line)) continue;
20822
- let cursor = i;
20823
- let end = -1;
20824
- for (const { word, dash } of words) {
20825
- let k = cursor;
20826
- while (k < lines.length && (dash ? lines[k].replace(/^\t+/, "") : lines[k]) !== word) k++;
20827
- if (k >= lines.length) {
20828
- end = -1;
20829
- break;
20830
- }
20831
- end = k;
20832
- cursor = k + 1;
20833
- }
20834
- if (end === -1) continue;
20835
- i = end + 1;
20836
- }
20837
- return out.join("\n");
20273
+ return (command ?? "").split(/&&|\|\||;|\n/);
20838
20274
  }
20839
20275
  function findMomentSegment(command, on) {
20840
20276
  const segments = splitSegments(command);
@@ -20877,8 +20313,8 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
20877
20313
  if (SHELL_DYNAMIC.test(raw) || raw === "-") {
20878
20314
  return { dir: null, named: true, unresolvable: `cd target not statically resolvable: ${raw}` };
20879
20315
  }
20880
- const expanded = raw === "~" ? (0, import_node_os5.homedir)() : raw.startsWith("~/") ? (0, import_node_path26.join)((0, import_node_os5.homedir)(), raw.slice(2)) : raw;
20881
- dir = (0, import_node_path25.isAbsolute)(expanded) ? expanded : (0, import_node_path25.resolve)(dir, expanded);
20316
+ const expanded = raw === "~" ? (0, import_node_os5.homedir)() : raw.startsWith("~/") ? (0, import_node_path25.join)((0, import_node_os5.homedir)(), raw.slice(2)) : raw;
20317
+ dir = (0, import_node_path24.isAbsolute)(expanded) ? expanded : (0, import_node_path24.resolve)(dir, expanded);
20882
20318
  }
20883
20319
  const seg = segments[segmentIndex];
20884
20320
  const overrideMatch = /--(?:git-dir|work-tree)(?:=|\s)|\bGIT_(?:DIR|WORK_TREE|INDEX_FILE)=/.exec(seg);
@@ -20896,8 +20332,8 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
20896
20332
  if (SHELL_DYNAMIC.test(raw)) {
20897
20333
  return { dir: null, named: true, unresolvable: `-C target not statically resolvable: ${raw}` };
20898
20334
  }
20899
- const expanded = raw === "~" ? (0, import_node_os5.homedir)() : raw.startsWith("~/") ? (0, import_node_path26.join)((0, import_node_os5.homedir)(), raw.slice(2)) : raw;
20900
- dir = (0, import_node_path25.isAbsolute)(expanded) ? expanded : (0, import_node_path25.resolve)(dir, expanded);
20335
+ const expanded = raw === "~" ? (0, import_node_os5.homedir)() : raw.startsWith("~/") ? (0, import_node_path25.join)((0, import_node_os5.homedir)(), raw.slice(2)) : raw;
20336
+ dir = (0, import_node_path24.isAbsolute)(expanded) ? expanded : (0, import_node_path24.resolve)(dir, expanded);
20901
20337
  }
20902
20338
  }
20903
20339
  return { dir, named, unresolvable: null };
@@ -20952,14 +20388,17 @@ function parsePushTarget(segment) {
20952
20388
  return { remote, srcRef: src, dstRef: dst || null, isDelete };
20953
20389
  }
20954
20390
  function gitAt(dir, args) {
20955
- const res = runGitSync(dir, args);
20956
- return res.ok ? res.stdout.trim() : "";
20391
+ try {
20392
+ return (0, import_node_child_process11.execFileSync)("git", args, { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
20393
+ } catch {
20394
+ return "";
20395
+ }
20957
20396
  }
20958
20397
  function realpathOr2(p) {
20959
20398
  try {
20960
20399
  return import_node_fs33.realpathSync.native(p);
20961
20400
  } catch {
20962
- return (0, import_node_path25.resolve)(p);
20401
+ return (0, import_node_path24.resolve)(p);
20963
20402
  }
20964
20403
  }
20965
20404
  function resolveFrame(input) {
@@ -20998,7 +20437,7 @@ function resolveFrame(input) {
20998
20437
  const gitDirRaw = gitAt(dir, ["rev-parse", "--absolute-git-dir"]);
20999
20438
  const commonRaw = gitAt(dir, ["rev-parse", "--git-common-dir"]);
21000
20439
  const gitDir = gitDirRaw ? realpathOr2(gitDirRaw) : null;
21001
- const commonDir = commonRaw ? realpathOr2((0, import_node_path25.isAbsolute)(commonRaw) ? commonRaw : (0, import_node_path25.resolve)(dir, commonRaw)) : null;
20440
+ const commonDir = commonRaw ? realpathOr2((0, import_node_path24.isAbsolute)(commonRaw) ? commonRaw : (0, import_node_path24.resolve)(dir, commonRaw)) : null;
21002
20441
  const branchRaw = gitAt(dir, ["rev-parse", "--abbrev-ref", "HEAD"]);
21003
20442
  return {
21004
20443
  moment: found?.moment ?? null,
@@ -21173,7 +20612,7 @@ function stagedRange(frame, command) {
21173
20612
  if (plan.kind === "unpredictable") {
21174
20613
  return { kind: "staged", base: "HEAD", head: "INDEX", via: "staged-in-command", refusal: plan.reason };
21175
20614
  }
21176
- const mergeHead = frame.gitDir ? (0, import_node_path26.join)(frame.gitDir, "MERGE_HEAD") : null;
20615
+ const mergeHead = frame.gitDir ? (0, import_node_path25.join)(frame.gitDir, "MERGE_HEAD") : null;
21177
20616
  if (mergeHead && (0, import_node_fs33.existsSync)(mergeHead)) {
21178
20617
  const vsHead = new Set(frameGit(frame, ["diff", "--cached", "--name-only", "HEAD"]).split("\n").filter(Boolean));
21179
20618
  const vsMerge = new Set(frameGit(frame, ["diff", "--cached", "--name-only", "MERGE_HEAD"]).split("\n").filter(Boolean));
@@ -21226,7 +20665,7 @@ function rangeChangeSignals(frame, range, paths) {
21226
20665
  const out = /* @__PURE__ */ new Map();
21227
20666
  if (range.kind === "nothing" || paths.length === 0) return out;
21228
20667
  if (range.kind === "push" && !range.base) return out;
21229
- const args = range.kind === "staged" || range.kind === "merge" ? ["diff", ...DIFF_READ_FLAGS, "--cached", "--unified=0"] : ["diff", ...DIFF_READ_FLAGS, "--unified=0", range.base, range.head === "INDEX" ? "HEAD" : range.head];
20668
+ const args = range.kind === "staged" || range.kind === "merge" ? ["diff", "--cached", "--unified=0"] : ["diff", "--unified=0", range.base, range.head === "INDEX" ? "HEAD" : range.head];
21230
20669
  const diff = frameGit(frame, [...args, "--", ...paths]);
21231
20670
  let current = null;
21232
20671
  let oldSide = null;
@@ -21589,7 +21028,7 @@ function sanitizeCommandWithLoss(rawCmd) {
21589
21028
  const lost = lines.slice(1).some((l) => l.trim().length > 0);
21590
21029
  const first = lines[0];
21591
21030
  const cmd = sanitizeCommand(first);
21592
- const hadSeparator = SEPARATORS.some((sep4) => first.indexOf(sep4) > 0);
21031
+ const hadSeparator = SEPARATORS.some((sep3) => first.indexOf(sep3) > 0);
21593
21032
  return { cmd, lost: lost || !hadSeparator && first.length > MAX_COMMAND_CHARS };
21594
21033
  }
21595
21034
  var SEPARATORS = [" | ", " > ", " >> ", " 2>", " && ", " ; "];
@@ -21598,11 +21037,11 @@ function sanitizeCommand(rawCmd) {
21598
21037
  let cmd = rawCmd.split("\n")[0];
21599
21038
  let cut = -1;
21600
21039
  let marker = "";
21601
- for (const sep4 of SEPARATORS) {
21602
- const idx = cmd.indexOf(sep4);
21040
+ for (const sep3 of SEPARATORS) {
21041
+ const idx = cmd.indexOf(sep3);
21603
21042
  if (idx > 0 && (cut === -1 || idx < cut)) {
21604
21043
  cut = idx;
21605
- marker = sep4.trim();
21044
+ marker = sep3.trim();
21606
21045
  }
21607
21046
  }
21608
21047
  if (cut > -1) cmd = cmd.slice(0, cut);
@@ -21626,28 +21065,28 @@ async function readStopHookStdin() {
21626
21065
  try {
21627
21066
  if (process.stdin.isTTY) return empty;
21628
21067
  const chunks = [];
21629
- const timeout = new Promise((resolve6) => setTimeout(() => resolve6(empty), 500));
21630
- const read = new Promise((resolve6) => {
21068
+ const timeout = new Promise((resolve5) => setTimeout(() => resolve5(empty), 500));
21069
+ const read = new Promise((resolve5) => {
21631
21070
  process.stdin.on("data", (chunk) => chunks.push(chunk));
21632
21071
  process.stdin.on("end", () => {
21633
21072
  const raw = Buffer.concat(chunks).toString("utf-8").trim();
21634
21073
  if (!raw) {
21635
- resolve6(empty);
21074
+ resolve5(empty);
21636
21075
  return;
21637
21076
  }
21638
21077
  try {
21639
21078
  const data = JSON.parse(raw);
21640
- resolve6({
21079
+ resolve5({
21641
21080
  assistantMessage: typeof data.last_assistant_message === "string" ? data.last_assistant_message : null,
21642
21081
  stopReason: typeof data.stop_reason === "string" ? data.stop_reason : null,
21643
21082
  transcriptPath: typeof data.transcript_path === "string" ? data.transcript_path : null,
21644
21083
  sessionId: typeof data.session_id === "string" ? data.session_id : null
21645
21084
  });
21646
21085
  } catch {
21647
- resolve6(empty);
21086
+ resolve5(empty);
21648
21087
  }
21649
21088
  });
21650
- process.stdin.on("error", () => resolve6(empty));
21089
+ process.stdin.on("error", () => resolve5(empty));
21651
21090
  process.stdin.resume();
21652
21091
  });
21653
21092
  return await Promise.race([read, timeout]);
@@ -21851,7 +21290,7 @@ function channelSilence(input) {
21851
21290
  // src/lib/cli-version.ts
21852
21291
  function cliVersion() {
21853
21292
  try {
21854
- return true ? "0.32.6-experimental.df0a578" : "dev";
21293
+ return true ? "0.32.7" : "dev";
21855
21294
  } catch {
21856
21295
  return "dev";
21857
21296
  }
@@ -21903,12 +21342,10 @@ var SEVERITY_ORDER = {
21903
21342
  Low: 3
21904
21343
  };
21905
21344
  function isCodacyAvailable() {
21906
- try {
21907
- (0, import_node_child_process12.execSync)("which codacy-analysis", { stdio: "pipe" });
21908
- return true;
21909
- } catch {
21910
- return false;
21911
- }
21345
+ return codacyAnalysisPath() !== null;
21346
+ }
21347
+ function codacyAnalysisPath() {
21348
+ return whichSync("codacy-analysis");
21912
21349
  }
21913
21350
  function buildAnalyzerArgv(files) {
21914
21351
  return [
@@ -21935,6 +21372,16 @@ function withFailure(kind, detail) {
21935
21372
  summary: { ...EMPTY_RESULT.summary, failure: { kind, detail: detail.slice(0, 300) } }
21936
21373
  };
21937
21374
  }
21375
+ function runCodacyAnalysisIfAvailable(files) {
21376
+ if (files.length === 0) return EMPTY_RESULT;
21377
+ if (!isCodacyAvailable()) {
21378
+ return withFailure(
21379
+ "analyzer_unavailable",
21380
+ "@codacy/analysis-cli was not found on PATH \u2014 no static analysis ran"
21381
+ );
21382
+ }
21383
+ return runCodacyAnalysis(files);
21384
+ }
21938
21385
  function runCodacyAnalysis(files) {
21939
21386
  const empty = EMPTY_RESULT;
21940
21387
  if (files.length === 0) return empty;
@@ -21946,7 +21393,8 @@ function runCodacyAnalysis(files) {
21946
21393
  }
21947
21394
  });
21948
21395
  if (existingFiles.length === 0) return empty;
21949
- const proc = (0, import_node_child_process12.spawnSync)("codacy-analysis", buildAnalyzerArgv(existingFiles), {
21396
+ const analyzer = codacyAnalysisPath() ?? "codacy-analysis";
21397
+ const proc = (0, import_node_child_process12.spawnSync)(analyzer, buildAnalyzerArgv(existingFiles), {
21950
21398
  encoding: "utf-8",
21951
21399
  maxBuffer: 10 * 1024 * 1024
21952
21400
  });
@@ -22114,11 +21562,10 @@ var EMPTY_STATIC = {
22114
21562
  summary: { total_findings: 0, by_severity: {}, tools_run: [] }
22115
21563
  };
22116
21564
  function runLocalStatic(analyzable, securityFiles, baseline, skipStatic) {
22117
- if (skipStatic || !isCodacyAvailable()) return EMPTY_STATIC;
21565
+ if (skipStatic) return EMPTY_STATIC;
22118
21566
  let scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
22119
21567
  if (baseline) scannable = scannable.filter((f) => changedSinceBaseline(f, baseline));
22120
- if (scannable.length === 0) return EMPTY_STATIC;
22121
- return runCodacyAnalysis(scannable);
21568
+ return runCodacyAnalysisIfAvailable(scannable);
22122
21569
  }
22123
21570
  function localOnlyAndExit(staticResults) {
22124
21571
  printJsonCompact({
@@ -22210,7 +21657,7 @@ async function scope(run2) {
22210
21657
 
22211
21658
  // src/lib/specs.ts
22212
21659
  var import_node_fs36 = require("node:fs");
22213
- var import_node_path27 = require("node:path");
21660
+ var import_node_path26 = require("node:path");
22214
21661
  var SPEC_CANDIDATES = [
22215
21662
  "CLAUDE.md",
22216
21663
  "AGENTS.md",
@@ -22229,6 +21676,16 @@ var SPEC_CANDIDATES = [
22229
21676
  var DOC_EXT = /\.(md|mdx|ya?ml|txt|rst|adoc)$/i;
22230
21677
  var UNCONSULTED_FILE_BYTES = 10240;
22231
21678
  var UNCONSULTED_TOTAL_BYTES = 30720;
21679
+ function readSpecFiles(specsOpt, root) {
21680
+ const out = [];
21681
+ for (const raw of specsOpt.split(",").map((f) => f.trim()).filter(Boolean)) {
21682
+ const candidate = (0, import_node_path26.isAbsolute)(raw) ? (0, import_node_path26.relative)(root, raw) : raw;
21683
+ const content = readFileInside(root, candidate, MAX_EXPLICIT_SPEC_FILE_BYTES);
21684
+ if (content === null) continue;
21685
+ out.push({ path: raw, content });
21686
+ }
21687
+ return out;
21688
+ }
22232
21689
  function discoverSpecs(consulted = []) {
22233
21690
  const result = [];
22234
21691
  const seen = /* @__PURE__ */ new Set();
@@ -22246,13 +21703,15 @@ function discoverSpecs(consulted = []) {
22246
21703
  const remaining = totalCap - totalBytes;
22247
21704
  const fileCap = relevant ? MAX_SPEC_FILE_BYTES : UNCONSULTED_FILE_BYTES;
22248
21705
  const readBytes = Math.min(fileCap, remaining);
22249
- const opened = openRegularInRoot(process.cwd(), specPath);
22250
- if (!opened.ok) return true;
22251
21706
  try {
22252
- const bytes = readOpened(opened.fd, Math.min(opened.size, readBytes));
22253
- if (bytes.length === 0) return true;
22254
- result.push({ path: specPath, content: bytes.toString("utf-8") });
22255
- totalBytes += bytes.length;
21707
+ const buf = Buffer.alloc(readBytes);
21708
+ const fd = (0, import_node_fs36.openSync)(specPath, "r");
21709
+ const bytesRead = (0, import_node_fs36.readSync)(fd, buf, 0, readBytes, 0);
21710
+ (0, import_node_fs36.closeSync)(fd);
21711
+ const content = buf.slice(0, bytesRead).toString("utf-8");
21712
+ if (!content) return true;
21713
+ result.push({ path: specPath, content });
21714
+ totalBytes += content.length;
22256
21715
  } catch {
22257
21716
  }
22258
21717
  return true;
@@ -22281,7 +21740,7 @@ function findMdFiles(dir, maxDepth, depth = 0) {
22281
21740
  try {
22282
21741
  const entries = (0, import_node_fs36.readdirSync)(dir, { withFileTypes: true });
22283
21742
  for (const entry of entries) {
22284
- const fullPath = (0, import_node_path27.join)(dir, entry.name);
21743
+ const fullPath = (0, import_node_path26.join)(dir, entry.name);
22285
21744
  if (entry.isFile() && entry.name.endsWith(".md")) {
22286
21745
  result.push(fullPath);
22287
21746
  } else if (entry.isDirectory() && depth < maxDepth - 1) {
@@ -22293,51 +21752,37 @@ function findMdFiles(dir, maxDepth, depth = 0) {
22293
21752
  return result;
22294
21753
  }
22295
21754
  function discoverPlans() {
22296
- const home = process.env.HOME ?? "";
22297
- const homePlansDir = (0, import_node_path27.join)(home, ".claude", "plans");
22298
- const sources = [];
22299
- if (isRealDirectoryChain(process.cwd(), [".claude", "plans"])) {
22300
- sources.push({ root: process.cwd(), prefix: (0, import_node_path27.join)(".claude", "plans") });
22301
- }
22302
- if (home && (0, import_node_fs36.existsSync)(homePlansDir)) sources.push({ root: homePlansDir, prefix: "" });
21755
+ const homePlansDir = (0, import_node_path26.join)(process.env.HOME ?? "", ".claude", "plans");
21756
+ const localPlansDir = ".claude/plans";
22303
21757
  const candidates2 = [];
22304
21758
  const seen = /* @__PURE__ */ new Set();
22305
- for (const source of sources) {
22306
- let names2;
21759
+ for (const plansDir of [localPlansDir, homePlansDir]) {
21760
+ if (!(0, import_node_fs36.existsSync)(plansDir)) continue;
22307
21761
  try {
22308
- names2 = (0, import_node_fs36.readdirSync)(source.prefix ? (0, import_node_path27.join)(source.root, source.prefix) : source.root);
21762
+ for (const f of (0, import_node_fs36.readdirSync)(plansDir)) {
21763
+ if (!f.endsWith(".md") || seen.has(f)) continue;
21764
+ seen.add(f);
21765
+ const fullPath = (0, import_node_path26.join)(plansDir, f);
21766
+ try {
21767
+ const stat3 = (0, import_node_fs36.statSync)(fullPath);
21768
+ candidates2.push({ name: f, path: fullPath, mtime: stat3.mtimeMs, size: stat3.size });
21769
+ } catch {
21770
+ }
21771
+ }
22309
21772
  } catch {
22310
- continue;
22311
- }
22312
- for (const f of names2) {
22313
- if (!f.endsWith(".md") || seen.has(f)) continue;
22314
- seen.add(f);
22315
- const relPath = source.prefix ? (0, import_node_path27.join)(source.prefix, f) : f;
22316
- const stat3 = statRegularInRoot(source.root, relPath);
22317
- if (!stat3.ok) continue;
22318
- candidates2.push({ name: f, root: source.root, relPath, mtime: stat3.mtimeMs, size: stat3.size });
22319
21773
  }
22320
21774
  }
22321
21775
  candidates2.sort((a, b) => b.mtime - a.mtime);
22322
21776
  const result = [];
22323
21777
  for (const entry of candidates2.slice(0, MAX_PLAN_FILES)) {
22324
21778
  if (entry.size > MAX_PLAN_FILE_BYTES) continue;
22325
- const read = readRegularFileInRoot(entry.root, entry.relPath, MAX_PLAN_FILE_BYTES);
22326
- if (read.ok) result.push({ name: entry.name, content: read.content });
22327
- }
22328
- return result;
22329
- }
22330
- function isRealDirectoryChain(root, parts) {
22331
- let current = root;
22332
- for (const part of parts) {
22333
- current = (0, import_node_path27.join)(current, part);
22334
21779
  try {
22335
- if (!(0, import_node_fs36.lstatSync)(current).isDirectory()) return false;
21780
+ const content = (0, import_node_fs36.readFileSync)(entry.path, "utf-8");
21781
+ result.push({ name: entry.name, content });
22336
21782
  } catch {
22337
- return false;
22338
21783
  }
22339
21784
  }
22340
- return true;
21785
+ return result;
22341
21786
  }
22342
21787
  var GUARD_DOC_EXT = /\.(md|mdx|txt|rst|adoc)$/i;
22343
21788
  function discoverGuardDocs(rangeFiles2) {
@@ -22348,21 +21793,16 @@ function discoverGuardDocs(rangeFiles2) {
22348
21793
  if (!GUARD_DOC_EXT.test(path)) continue;
22349
21794
  if (path.startsWith("/") || path.includes("..")) continue;
22350
21795
  if (!(0, import_node_fs36.existsSync)(path)) continue;
22351
- const opened = openRegularInRoot(process.cwd(), path);
22352
- if (!opened.ok) continue;
22353
- if (opened.size > MAX_PLAN_FILE_BYTES || totalBytes + opened.size > MAX_TOTAL_SPEC_BYTES) {
22354
- closeOpened(opened.fd);
22355
- continue;
22356
- }
22357
- let bytes;
22358
21796
  try {
22359
- bytes = readOpened(opened.fd, opened.size);
21797
+ const stat3 = (0, import_node_fs36.statSync)(path);
21798
+ if (stat3.size > MAX_PLAN_FILE_BYTES) continue;
21799
+ if (totalBytes + stat3.size > MAX_TOTAL_SPEC_BYTES) continue;
21800
+ const content = (0, import_node_fs36.readFileSync)(path, "utf-8");
21801
+ if (!content) continue;
21802
+ result.push({ name: path, content });
21803
+ totalBytes += content.length;
22360
21804
  } catch {
22361
- continue;
22362
21805
  }
22363
- if (bytes.length === 0) continue;
22364
- result.push({ name: path, content: bytes.toString("utf-8") });
22365
- totalBytes += bytes.length;
22366
21806
  }
22367
21807
  return result;
22368
21808
  }
@@ -22534,7 +21974,7 @@ async function mode(run2) {
22534
21974
 
22535
21975
  // src/lib/fold.ts
22536
21976
  var import_node_fs37 = require("node:fs");
22537
- var import_node_path28 = require("node:path");
21977
+ var import_node_path27 = require("node:path");
22538
21978
  var KNOWN_RECORD_TYPES = /* @__PURE__ */ new Set([
22539
21979
  "user",
22540
21980
  "assistant",
@@ -22766,9 +22206,9 @@ function fold(transcriptPath, opts = {}) {
22766
22206
  return result;
22767
22207
  }
22768
22208
  try {
22769
- const sidecarDir = (0, import_node_path28.join)(
22770
- (0, import_node_path28.dirname)(transcriptPath),
22771
- (0, import_node_path28.basename)(transcriptPath).replace(/\.jsonl$/, ""),
22209
+ const sidecarDir = (0, import_node_path27.join)(
22210
+ (0, import_node_path27.dirname)(transcriptPath),
22211
+ (0, import_node_path27.basename)(transcriptPath).replace(/\.jsonl$/, ""),
22772
22212
  "subagents"
22773
22213
  );
22774
22214
  if ((0, import_node_fs37.existsSync)(sidecarDir)) {
@@ -22778,7 +22218,7 @@ function fold(transcriptPath, opts = {}) {
22778
22218
  const walk2 = (d, depth) => {
22779
22219
  if (depth > 4) return;
22780
22220
  for (const e of (0, import_node_fs37.readdirSync)(d, { withFileTypes: true })) {
22781
- const p = (0, import_node_path28.join)(d, e.name);
22221
+ const p = (0, import_node_path27.join)(d, e.name);
22782
22222
  if (e.isDirectory()) {
22783
22223
  walk2(p, depth + 1);
22784
22224
  } else if (e.name.startsWith("agent-") && e.name.endsWith(".jsonl")) {
@@ -23102,14 +22542,12 @@ async function evidence(run2) {
23102
22542
  authorshipWasObservable
23103
22543
  });
23104
22544
  const recentForReview = narrowToRecent(baseForReview, baselineSessionId);
23105
- if (!opts.skipStatic && isCodacyAvailable()) {
22545
+ if (!opts.skipStatic) {
23106
22546
  let allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
23107
22547
  if (baseline) {
23108
22548
  allScannable = allScannable.filter((f) => changedSinceBaseline(f, baseline));
23109
22549
  }
23110
- if (allScannable.length > 0) {
23111
- staticResults = runCodacyAnalysis(allScannable);
23112
- }
22550
+ staticResults = runCodacyAnalysisIfAvailable(allScannable);
23113
22551
  }
23114
22552
  const deltaSet = baseline ? recentForReview.filter((f) => changedSinceBaseline(f, baseline)) : recentForReview;
23115
22553
  codeDelta = collectCodeDelta(deltaSet, {
@@ -23153,7 +22591,7 @@ async function evidence(run2) {
23153
22591
 
23154
22592
  // src/lib/cache-cleanup.ts
23155
22593
  var import_node_fs38 = require("node:fs");
23156
- var import_node_path29 = require("node:path");
22594
+ var import_node_path28 = require("node:path");
23157
22595
  var CACHE_TTL_DAYS = 7;
23158
22596
  function pruneStaleCache() {
23159
22597
  try {
@@ -23161,7 +22599,7 @@ function pruneStaleCache() {
23161
22599
  const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
23162
22600
  for (const entry of (0, import_node_fs38.readdirSync)(dir)) {
23163
22601
  if (!entry.startsWith("pending-")) continue;
23164
- const path = (0, import_node_path29.join)(dir, entry);
22602
+ const path = (0, import_node_path28.join)(dir, entry);
23165
22603
  try {
23166
22604
  const stat3 = (0, import_node_fs38.statSync)(path);
23167
22605
  if (stat3.mtimeMs < cutoff) {
@@ -23327,9 +22765,9 @@ async function repoContext(run2) {
23327
22765
  }
23328
22766
 
23329
22767
  // src/lib/seed-runner.ts
23330
- var import_promises15 = require("node:fs/promises");
22768
+ var import_promises14 = require("node:fs/promises");
23331
22769
  var import_node_fs40 = require("node:fs");
23332
- var import_node_path30 = require("node:path");
22770
+ var import_node_path29 = require("node:path");
23333
22771
  var import_yaml4 = __toESM(require_dist());
23334
22772
 
23335
22773
  // src/lib/seed.ts
@@ -23405,7 +22843,7 @@ function classifyClaudeSection(heading) {
23405
22843
  if (hasAny(["integration", "webhook", "third-party", "third party", "provider"])) return "integration";
23406
22844
  return "domain";
23407
22845
  }
23408
- function slugify2(s) {
22846
+ function slugify(s) {
23409
22847
  return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
23410
22848
  }
23411
22849
  var FRAMEWORK_GLOBS = [
@@ -23502,7 +22940,7 @@ _Extracted from README on setup. Update when project direction changes._`,
23502
22940
  for (const fwRaw of frameworks) {
23503
22941
  const fw = fwRaw.trim();
23504
22942
  if (!fw) continue;
23505
- const slug = slugify2(fw);
22943
+ const slug = slugify(fw);
23506
22944
  if (!slug || seenFrameworkSlugs.has(slug)) continue;
23507
22945
  seenFrameworkSlugs.add(slug);
23508
22946
  out.push({
@@ -23573,7 +23011,7 @@ async function runSeed(opts) {
23573
23011
  }
23574
23012
  let standardDoc;
23575
23013
  try {
23576
- const raw = await (0, import_promises15.readFile)(STANDARD_FILE, "utf-8");
23014
+ const raw = await (0, import_promises14.readFile)(STANDARD_FILE, "utf-8");
23577
23015
  standardDoc = (0, import_yaml4.parse)(raw);
23578
23016
  } catch {
23579
23017
  return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
@@ -23582,7 +23020,7 @@ async function runSeed(opts) {
23582
23020
  let readmeContent;
23583
23021
  if ((0, import_node_fs40.existsSync)("README.md")) {
23584
23022
  try {
23585
- readmeContent = await (0, import_promises15.readFile)("README.md", "utf-8");
23023
+ readmeContent = await (0, import_promises14.readFile)("README.md", "utf-8");
23586
23024
  } catch {
23587
23025
  }
23588
23026
  }
@@ -23590,7 +23028,7 @@ async function runSeed(opts) {
23590
23028
  for (const p of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
23591
23029
  if ((0, import_node_fs40.existsSync)(p)) {
23592
23030
  try {
23593
- claudeMdContent = await (0, import_promises15.readFile)(p, "utf-8");
23031
+ claudeMdContent = await (0, import_promises14.readFile)(p, "utf-8");
23594
23032
  break;
23595
23033
  } catch {
23596
23034
  }
@@ -23611,7 +23049,7 @@ async function runSeed(opts) {
23611
23049
  if (candidates2.length === 0) {
23612
23050
  return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
23613
23051
  }
23614
- const overviewPath = (0, import_node_path30.join)(MEMORY_DIR, "domain", "project-overview.md");
23052
+ const overviewPath = (0, import_node_path29.join)(MEMORY_DIR, "domain", "project-overview.md");
23615
23053
  if ((0, import_node_fs40.existsSync)(overviewPath) && !opts.force) {
23616
23054
  return { created: 0, failed: 0, skipped: "already_seeded", candidates: candidates2 };
23617
23055
  }
@@ -23654,8 +23092,8 @@ async function runSeed(opts) {
23654
23092
  continue;
23655
23093
  }
23656
23094
  try {
23657
- await (0, import_promises15.mkdir)((0, import_node_path30.dirname)(targetPath), { recursive: true });
23658
- await (0, import_promises15.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
23095
+ await (0, import_promises14.mkdir)((0, import_node_path29.dirname)(targetPath), { recursive: true });
23096
+ await (0, import_promises14.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
23659
23097
  created++;
23660
23098
  opts.onCreated?.(nodeId, filePathRel, c);
23661
23099
  } catch (err) {
@@ -23668,7 +23106,7 @@ async function runSeed(opts) {
23668
23106
 
23669
23107
  // src/commands/analyze/phases/08-memory-manifest.ts
23670
23108
  var import_node_fs41 = require("node:fs");
23671
- var import_node_path31 = require("node:path");
23109
+ var import_node_path30 = require("node:path");
23672
23110
  async function memoryManifest(run2) {
23673
23111
  const { globals } = run2;
23674
23112
  const { serviceUrl, token } = run2;
@@ -23678,7 +23116,7 @@ async function memoryManifest(run2) {
23678
23116
  let autoSeedNotice = null;
23679
23117
  try {
23680
23118
  await ensureMemoryDir();
23681
- const seedMarker = (0, import_node_path31.join)(VERITY_DIR, ".seeded");
23119
+ const seedMarker = (0, import_node_path30.join)(VERITY_DIR, ".seeded");
23682
23120
  const hasStandard = (0, import_node_fs41.existsSync)(STANDARD_FILE);
23683
23121
  const alreadyTried = (0, import_node_fs41.existsSync)(seedMarker);
23684
23122
  if (hasStandard && !alreadyTried) {
@@ -23796,7 +23234,7 @@ function computeIncrement(reviewedPaths, hashOf, priorAuthored) {
23796
23234
  }
23797
23235
 
23798
23236
  // src/commands/analyze/phases/10-working-memory.ts
23799
- var import_node_path32 = require("node:path");
23237
+ var import_node_path31 = require("node:path");
23800
23238
  async function workingMemory(run2) {
23801
23239
  const { opts } = run2;
23802
23240
  const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } = run2;
@@ -23808,7 +23246,7 @@ async function workingMemory(run2) {
23808
23246
  const priorState = foldForMarks(memorySession.d);
23809
23247
  incrementReport = computeIncrement(
23810
23248
  allForReview,
23811
- (p) => fileHash((0, import_node_path32.join)(repoRoot(), p)),
23249
+ (p) => fileHash((0, import_node_path31.join)(repoRoot(), p)),
23812
23250
  priorState.authored_all.map((a) => ({
23813
23251
  path: a.path,
23814
23252
  hash_at_last_verdict: a.hash_at_last_verdict
@@ -23945,6 +23383,15 @@ function parseAutonomousEnv(raw) {
23945
23383
  if (v === "0" || v === "false" || v === "off" || v === "no") return false;
23946
23384
  return true;
23947
23385
  }
23386
+ function resolveRunMode(inputs = {}) {
23387
+ if (inputs.autonomousFlag === true) return "autonomous";
23388
+ if (inputs.autonomousFlag === false) return "interactive";
23389
+ const env = inputs.env ?? process.env;
23390
+ const envDecision = parseAutonomousEnv(env.VERITY_AUTONOMOUS);
23391
+ if (envDecision !== void 0) return envDecision ? "autonomous" : "interactive";
23392
+ const isTTY = inputs.isTTY ?? Boolean(process.stdin?.isTTY);
23393
+ return isTTY ? "interactive" : "autonomous";
23394
+ }
23948
23395
  function isExplicitlyAutonomous(env = process.env) {
23949
23396
  return parseAutonomousEnv(env.VERITY_AUTONOMOUS) === true || parseAutonomousEnv(env.CI) === true || parseAutonomousEnv(env.GITHUB_ACTIONS) === true;
23950
23397
  }
@@ -24364,7 +23811,7 @@ async function transmit(run2) {
24364
23811
 
24365
23812
  // src/commands/analyze/phases/13-reconcile.ts
24366
23813
  var import_node_fs44 = require("node:fs");
24367
- var import_node_path33 = require("node:path");
23814
+ var import_node_path32 = require("node:path");
24368
23815
  async function reconcile(run2) {
24369
23816
  const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run2;
24370
23817
  const sentPaths = codeDelta.files.map((f) => f.path);
@@ -24393,7 +23840,7 @@ async function reconcile(run2) {
24393
23840
  const st = foldDossier(memorySession.d);
24394
23841
  openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line) => {
24395
23842
  try {
24396
- const src = (0, import_node_fs44.readFileSync)((0, import_node_path33.join)(repoRoot(), file), "utf8").split("\n");
23843
+ const src = (0, import_node_fs44.readFileSync)((0, import_node_path32.join)(repoRoot(), file), "utf8").split("\n");
24397
23844
  const at = src[line - 1];
24398
23845
  return at === void 0 ? null : lineSha(at);
24399
23846
  } catch {
@@ -24656,146 +24103,6 @@ function describeRelease(release, input) {
24656
24103
  }
24657
24104
  }
24658
24105
 
24659
- // src/lib/memory-pull.ts
24660
- var import_node_child_process14 = require("node:child_process");
24661
- var import_promises16 = require("node:fs/promises");
24662
- var pullStateFile = () => projectPath(`${VERITY_DIR}/.memory-pull-state.json`);
24663
- var PAGE_SIZE = 200;
24664
- var MAX_PAGES = 500;
24665
- var memoryIsTracked = () => committedMemoryFiles().length > 0;
24666
- async function pullRepoMemory(opts) {
24667
- if (memoryIsTracked()) return { ok: true, status: "tracked", received: 0, written: 0 };
24668
- const state = await readPullState();
24669
- const known = opts.force ? null : state.version;
24670
- let treePaths;
24671
- try {
24672
- treePaths = listTrackedFiles();
24673
- } catch {
24674
- treePaths = void 0;
24675
- }
24676
- const served = /* @__PURE__ */ new Map();
24677
- let received = 0;
24678
- let written = 0;
24679
- let version = null;
24680
- let cursor = null;
24681
- let pages = 0;
24682
- const remember = (complete) => writePullState({
24683
- version: complete ? version : null,
24684
- // A complete pull saw every file the server holds, so its ledger replaces the
24685
- // old one (a path that stopped arriving was archived). A partial one only adds.
24686
- served: complete ? served : new Map([...state.served, ...served])
24687
- });
24688
- do {
24689
- const params = new URLSearchParams({ limit: String(PAGE_SIZE) });
24690
- if (cursor) params.set("cursor", cursor);
24691
- else if (known) params.set("if_version", known);
24692
- const res = await apiRequest({
24693
- method: "GET",
24694
- path: `/memory/files?${params.toString()}`,
24695
- serviceUrl: opts.serviceUrl,
24696
- token: opts.token,
24697
- verbose: opts.verbose,
24698
- timeout: opts.timeoutMs ?? 3e4,
24699
- cmd: "memory-pull"
24700
- });
24701
- if (!res.ok) {
24702
- if (pages > 0) await remember(false);
24703
- return { ok: false, error: res.error, category: res.category };
24704
- }
24705
- const page = res.data;
24706
- if (page.unchanged) return { ok: true, status: "unchanged", received: 0, written: 0 };
24707
- if (pages === 0) version = page.version;
24708
- else if (page.version !== version) version = null;
24709
- const files = (page.files ?? []).map((f) => ({
24710
- path: f.path,
24711
- content: f.content,
24712
- node_id: f.node_id,
24713
- op: "create"
24714
- }));
24715
- received += files.length;
24716
- try {
24717
- written += await applyMemoryWrites(files, {
24718
- treePaths,
24719
- baseline: "written",
24720
- lastServed: state.served,
24721
- served,
24722
- claudeMdPointer: false
24723
- });
24724
- } catch (err) {
24725
- await remember(false);
24726
- throw err;
24727
- }
24728
- cursor = page.next_cursor;
24729
- pages++;
24730
- } while (cursor && pages < MAX_PAGES);
24731
- await remember(!cursor);
24732
- return { ok: true, status: "pulled", received, written };
24733
- }
24734
- async function readServedLedger() {
24735
- return (await readPullState()).served;
24736
- }
24737
- async function recordServedFiles(served) {
24738
- if (served.size === 0) return;
24739
- const state = await readPullState();
24740
- for (const [path, hash] of served) state.served.set(path, hash);
24741
- await writePullState(state);
24742
- }
24743
- async function readPullState() {
24744
- try {
24745
- const parsed = JSON.parse(await (0, import_promises16.readFile)(pullStateFile(), "utf-8"));
24746
- const served = /* @__PURE__ */ new Map();
24747
- if (parsed?.served && typeof parsed.served === "object") {
24748
- for (const [path, hash] of Object.entries(parsed.served)) {
24749
- if (typeof hash === "string") served.set(path, hash);
24750
- }
24751
- }
24752
- return { version: typeof parsed?.version === "string" ? parsed.version : null, served };
24753
- } catch {
24754
- return { version: null, served: /* @__PURE__ */ new Map() };
24755
- }
24756
- }
24757
- async function writePullState(state) {
24758
- try {
24759
- await (0, import_promises16.mkdir)(projectPath(VERITY_DIR), { recursive: true });
24760
- await (0, import_promises16.writeFile)(pullStateFile(), JSON.stringify({
24761
- ...state.version ? { version: state.version } : {},
24762
- pulled_at: (/* @__PURE__ */ new Date()).toISOString(),
24763
- served: Object.fromEntries(state.served)
24764
- }) + "\n");
24765
- } catch {
24766
- }
24767
- }
24768
- function pullsAtSessionStart(auth2, source) {
24769
- if (source === "compact") return false;
24770
- if (auth2.source === "env" || auth2.source === "flag") return true;
24771
- return auth2.userId != null;
24772
- }
24773
- function spawnBackgroundPull(deps = {}) {
24774
- const d = {
24775
- spawn: import_node_child_process14.spawn,
24776
- execPath: process.execPath,
24777
- script: process.argv[1],
24778
- cwd: process.cwd(),
24779
- env: process.env,
24780
- ...deps
24781
- };
24782
- if (d.env.VERITY_NO_BACKGROUND_PULL === "1" || !d.script) return false;
24783
- try {
24784
- const child = d.spawn(d.execPath, [d.script, "memory", "pull", "--quiet"], {
24785
- cwd: d.cwd,
24786
- env: d.env,
24787
- detached: true,
24788
- stdio: "ignore"
24789
- });
24790
- child.on("error", () => {
24791
- });
24792
- child.unref();
24793
- return true;
24794
- } catch {
24795
- return false;
24796
- }
24797
- }
24798
-
24799
24106
  // src/lib/remediation-guard.ts
24800
24107
  var TOOL_CONFIG_PATTERNS = [
24801
24108
  /(^|\/)\.codacy\//,
@@ -24894,9 +24201,7 @@ async function render(run2) {
24894
24201
  if (memoryWritesRaw && memoryWritesRaw.length > 0) {
24895
24202
  try {
24896
24203
  const treePaths = listTrackedFiles();
24897
- const served = /* @__PURE__ */ new Map();
24898
- await applyMemoryWrites(memoryWritesRaw, { treePaths, lastServed: await readServedLedger(), served });
24899
- await recordServedFiles(served);
24204
+ await applyMemoryWrites(memoryWritesRaw, { treePaths });
24900
24205
  } catch (err) {
24901
24206
  process.stderr.write(`verity: memory sync write failed: ${err.message}
24902
24207
  `);
@@ -25225,7 +24530,7 @@ var import_node_fs47 = require("node:fs");
25225
24530
 
25226
24531
  // src/lib/project-skills.ts
25227
24532
  var import_node_fs46 = require("node:fs");
25228
- var import_node_path34 = require("node:path");
24533
+ var import_node_path33 = require("node:path");
25229
24534
  var PROJECT_SKILL_NAMES = [
25230
24535
  "verity-setup",
25231
24536
  "verity-analyze",
@@ -25251,13 +24556,13 @@ var ALL = [...PROJECT_SKILL_NAMES, ...LEGACY_SKILL_NAMES];
25251
24556
  function staleProjectSkills() {
25252
24557
  const root = projectPath(".claude/skills");
25253
24558
  if (!(0, import_node_fs46.existsSync)(root)) return [];
25254
- return ALL.filter((name) => (0, import_node_fs46.existsSync)((0, import_node_path34.join)(root, name)));
24559
+ return ALL.filter((name) => (0, import_node_fs46.existsSync)((0, import_node_path33.join)(root, name)));
25255
24560
  }
25256
24561
  function removeProjectSkills() {
25257
24562
  const root = projectPath(".claude/skills");
25258
24563
  const removed = [];
25259
24564
  for (const name of staleProjectSkills()) {
25260
- (0, import_node_fs46.rmSync)((0, import_node_path34.join)(root, name), { recursive: true, force: true });
24565
+ (0, import_node_fs46.rmSync)((0, import_node_path33.join)(root, name), { recursive: true, force: true });
25261
24566
  removed.push(name);
25262
24567
  }
25263
24568
  return removed;
@@ -25383,7 +24688,6 @@ function registerBaselineCommands(program2) {
25383
24688
  }
25384
24689
  const authForScope = await resolveToken(program2.opts().token);
25385
24690
  const scopeToken = authForScope.ok ? authForScope.data.token : void 0;
25386
- if (authForScope.ok && pullsAtSessionStart(authForScope.data, source)) spawnBackgroundPull();
25387
24691
  const scopeSession = session;
25388
24692
  const result = captureBaseline({ sessionId: sessionScopeKey(scopeToken, scopeSession), source });
25389
24693
  logEvent("baseline_capture", {
@@ -25446,45 +24750,22 @@ async function runReview(opts, globals) {
25446
24750
  const changedFiles = opts.changed ? opts.changed.split(",").map((f) => f.trim()).filter(Boolean) : allFiles;
25447
24751
  const analyzable = filterAnalyzable(allFiles);
25448
24752
  const securityFiles = filterSecurity(allFiles);
25449
- let staticResults;
25450
- if (isCodacyAvailable()) {
25451
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs48.existsSync)(f) || resolveFile(f) !== null);
25452
- staticResults = runCodacyAnalysis(scannable);
25453
- } else {
25454
- staticResults = {
25455
- tool: "@codacy/analysis-cli",
25456
- findings: [],
25457
- summary: { total_findings: 0, by_severity: {}, tools_run: [] }
25458
- };
25459
- }
24753
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs48.existsSync)(f) || resolveFile(f) !== null);
24754
+ const staticResults = runCodacyAnalysisIfAvailable(scannable);
25460
24755
  const codeDelta = collectCodeDelta(allFiles);
25461
24756
  const tokenResult = await resolveToken(globals.token);
25462
24757
  const urlResult = await resolveServiceUrl(globals.serviceUrl);
25463
24758
  if (!tokenResult.ok || !urlResult.ok) {
24759
+ const staticFailure = staticResults.summary.failure;
25464
24760
  printJsonCompact({
25465
- gate_decision: "PASS",
25466
- 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.",
24761
+ gate_decision: "WARN",
24762
+ systemMessage: staticFailure ? `Verity: not authenticated, and local static analysis did not run (${staticFailure.kind}) \u2014 nothing was examined. Run \`verity init\` to authenticate and unlock the full review.` : "Verity: not authenticated \u2014 showing local static results only (no deep review, no upload). Run `verity init` to authenticate and unlock the full review.",
25467
24763
  unauthenticated: true,
25468
24764
  static_results: staticResults
25469
24765
  });
25470
24766
  process.exit(0);
25471
24767
  }
25472
- let specs;
25473
- if (opts.specs) {
25474
- const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
25475
- specs = [];
25476
- for (const p of specPaths) {
25477
- if (!(0, import_node_fs48.existsSync)(p)) continue;
25478
- try {
25479
- const { readFileSync: readFileSync26 } = await import("node:fs");
25480
- const content = readFileSync26(p, "utf-8");
25481
- specs.push({ path: p, content: content.slice(0, 10240) });
25482
- } catch {
25483
- }
25484
- }
25485
- } else {
25486
- specs = discoverSpecs();
25487
- }
24768
+ const specs = opts.specs ? readSpecFiles(opts.specs, repoRoot()) : discoverSpecs();
25488
24769
  const plans = discoverPlans();
25489
24770
  const requestBody = {
25490
24771
  static_results: staticResults,
@@ -25517,9 +24798,10 @@ async function runReview(opts, globals) {
25517
24798
  });
25518
24799
  if (!result.ok) {
25519
24800
  printError(`Service error: ${result.error}`);
24801
+ const staticFailure = staticResults.summary.failure;
25520
24802
  printJsonCompact({
25521
- gate_decision: "PASS",
25522
- systemMessage: "Verity: Service unavailable \u2014 showing static results only",
24803
+ gate_decision: "WARN",
24804
+ systemMessage: staticFailure ? `Verity: service unavailable, and local static analysis did not run (${staticFailure.kind}) \u2014 nothing was examined` : "Verity: service unavailable \u2014 showing local static results only",
25523
24805
  offline: true,
25524
24806
  static_results: staticResults
25525
24807
  });
@@ -25535,387 +24817,14 @@ async function runReview(opts, globals) {
25535
24817
 
25536
24818
  // src/commands/guard.ts
25537
24819
  var import_node_fs49 = require("node:fs");
25538
- var import_node_path35 = require("node:path");
25539
-
25540
- // src/lib/terminal-text.ts
25541
- var CONTROL = /[\x00-\x08\x0b-\x1f\x7f-\x9f\u061c\u200e\u200f\u2028-\u202e\u2066-\u2069]/g;
25542
- function terminalText(value, max = 4e3) {
25543
- if (typeof value !== "string") return "";
25544
- const clean2 = value.replace(CONTROL, "");
25545
- return clean2.length > max ? `${clean2.slice(0, max - 1)}\u2026` : clean2;
25546
- }
25547
- function terminalUrl(value) {
25548
- const clean2 = terminalText(value, 2e3);
25549
- return /^https?:\/\/\S+$/.test(clean2) ? clean2 : "";
25550
- }
25551
-
25552
- // src/lib/agent-driver.ts
25553
- var AGENT_DEADLINE_MS = {
25554
- "pre-commit": 9e4,
25555
- "pre-push": 18e4
25556
- };
25557
- var MIN_AGENT_START_MS = 25e3;
25558
- var MIN_TURN_MS = 12e3;
25559
- var FINISH_TIMEOUT_MS = 15e3;
25560
- var CANCEL_TIMEOUT_MS = 3e3;
25561
- var MAX_TURNS = 16;
25562
- var ONE_SHOT_FINDING_FIELDS = ["severity", "category", "file", "line", "title", "description", "scope", "pattern_id"];
25563
- function summarizeFindings(response) {
25564
- const findings = Array.isArray(response.findings) ? response.findings : [];
25565
- return findings.slice(0, 40).map((f) => {
25566
- const out = {};
25567
- const finding = f ?? {};
25568
- for (const key of ONE_SHOT_FINDING_FIELDS) if (finding[key] !== void 0) out[key] = finding[key];
25569
- return out;
25570
- });
25571
- }
25572
- function finishOneShot(settled) {
25573
- if (!settled) return { status: "pending" };
25574
- if (!settled.ok) return { status: "failed" };
25575
- const r = settled.response;
25576
- return {
25577
- status: "arrived",
25578
- ...typeof r.agent_attestation === "string" ? { attestation: r.agent_attestation } : {},
25579
- ...typeof r.run_id === "string" ? { run_id: r.run_id } : {},
25580
- ...typeof r.gate_decision === "string" ? { gate_decision: r.gate_decision } : {}
25581
- };
25582
- }
25583
- async function runAgentReview(options) {
25584
- const now = options.now ?? Date.now;
25585
- const remaining = () => options.deadlineMs - (now() - options.startedAt);
25586
- let settled = null;
25587
- const oneShotDone = options.oneShot.then(
25588
- (outcome) => {
25589
- settled = outcome;
25590
- },
25591
- () => {
25592
- settled = { ok: false };
25593
- }
25594
- );
25595
- if (remaining() < MIN_AGENT_START_MS) return { kind: "skipped", reason: "not enough time left after prep" };
25596
- const started = await options.http.post(
25597
- "/agent-review/start",
25598
- { ...options.start, remaining_ms: remaining() },
25599
- Math.max(1, remaining())
25600
- );
25601
- if (!started.ok) return { kind: "failed", reason: `start: ${started.error}` };
25602
- if (!started.data.enabled) return { kind: "disabled", reason: started.data.reason };
25603
- let state = started.data.state;
25604
- let calls = started.data.calls ?? [];
25605
- let done = started.data.done;
25606
- let sentOneShot = "pending";
25607
- const cancel = async () => {
25608
- await options.http.post("/agent-review/cancel", { state }, CANCEL_TIMEOUT_MS);
25609
- };
25610
- for (let turn = 0; !done; turn++) {
25611
- if (turn >= MAX_TURNS || remaining() < MIN_TURN_MS) break;
25612
- const results = await options.executeTools(calls);
25613
- let oneShot;
25614
- const current = settled;
25615
- if (current && sentOneShot === "pending") {
25616
- oneShot = current.ok ? { status: "arrived", findings: summarizeFindings(current.response) } : { status: "failed" };
25617
- sentOneShot = oneShot.status;
25618
- }
25619
- const turnRequest = { state, remaining_ms: remaining(), results, ...oneShot ? { one_shot: oneShot } : {} };
25620
- const next = await options.http.post("/agent-review/turn", turnRequest, Math.max(1, remaining()));
25621
- if (!next.ok) {
25622
- await cancel();
25623
- return { kind: "failed", reason: `turn: ${next.error}` };
25624
- }
25625
- state = next.data.state;
25626
- calls = next.data.calls ?? [];
25627
- done = next.data.done;
25628
- }
25629
- if (!settled) {
25630
- const wait = Math.max(0, remaining() - FINISH_TIMEOUT_MS);
25631
- await Promise.race([oneShotDone, new Promise((resolve6) => setTimeout(resolve6, wait))]);
25632
- }
25633
- const finished = await options.http.post(
25634
- "/agent-review/finish",
25635
- { state, session_id: options.sessionId, one_shot: finishOneShot(settled) },
25636
- FINISH_TIMEOUT_MS
25637
- );
25638
- if (!finished.ok) {
25639
- await cancel();
25640
- return { kind: "failed", reason: `finish: ${finished.error}` };
25641
- }
25642
- return { kind: "finished", response: finished.data };
25643
- }
25644
-
25645
- // src/lib/agent-tools.ts
25646
- var TOOL_LIMITS = {
25647
- outputChars: 16e3,
25648
- readLines: 400,
25649
- blameLines: 200,
25650
- searchHits: 50,
25651
- searchLineChars: 300,
25652
- listEntries: 200,
25653
- logCommits: 8,
25654
- textChars: 200,
25655
- pathChars: 400,
25656
- fileBytes: 4 * 1024 * 1024,
25657
- gitTimeoutMs: 2e3,
25658
- diffTimeoutMs: 1e4,
25659
- concurrency: 4
25660
- };
25661
- var DENIED_PATHS = [
25662
- /(^|\/)\.env(\.[^/]*)?$/i,
25663
- /\.(pem|key|p12|pfx|jks|keystore|asc|gpg|kdbx)$/i,
25664
- /(^|\/)id_(rsa|dsa|ecdsa|ed25519)(\.pub)?$/i,
25665
- /(^|\/)\.(npmrc|netrc|pypirc|pgpass|git-credentials)$/i,
25666
- /(^|\/)(credentials|secrets?)(\.[^/]*)?$/i,
25667
- /(^|\/)\.(ssh|aws|gnupg|docker)(\/|$)/i,
25668
- /(^|\/)\.git(\/|$)/
25669
- ];
25670
- function isDeniedPath(path) {
25671
- return DENIED_PATHS.some((pattern) => pattern.test(path)) || isVerityOwnedPath(path);
25672
- }
25673
- function validateRepoPath(value, { allowRoot = false } = {}) {
25674
- if (typeof value !== "string") return { ok: false, error: "path must be a string" };
25675
- let path = value.replace(/\\/g, "/").replace(/^\.\/+/, "");
25676
- if (allowRoot && (path === "" || path === ".")) return { ok: true, value: "" };
25677
- path = path.replace(/\/+$/, "");
25678
- if (!path || path.length > TOOL_LIMITS.pathChars) return { ok: false, error: "path is empty or too long" };
25679
- if (/[-]/.test(path)) return { ok: false, error: "path contains control characters" };
25680
- if (path.startsWith("/") || /^[A-Za-z]:/.test(path)) return { ok: false, error: "path must be relative to the repository root" };
25681
- if (path.split("/").some((segment) => segment === "..")) return { ok: false, error: "path must not leave the repository" };
25682
- if (path.startsWith("-") || path.startsWith(":")) return { ok: false, error: 'path must not start with "-" or ":"' };
25683
- if (isDeniedPath(path)) return { ok: false, error: "reading this path is not allowed" };
25684
- return { ok: true, value: path };
25685
- }
25686
- function lineNumber(value, fallback) {
25687
- return typeof value === "number" && Number.isInteger(value) && value >= 1 ? value : fallback;
25688
- }
25689
- function capOutput(text) {
25690
- return text.length > TOOL_LIMITS.outputChars ? `${text.slice(0, TOOL_LIMITS.outputChars)}
25691
- [truncated at ${TOOL_LIMITS.outputChars} characters]` : text;
25692
- }
25693
- function git(ctx, args, opts = {}) {
25694
- return runGit(ctx.root, args, { timeoutMs: opts.timeoutMs ?? TOOL_LIMITS.gitTimeoutMs, maxBuffer: opts.maxBuffer ?? TOOL_LIMITS.fileBytes });
25695
- }
25696
- function gitFailure(res, what) {
25697
- if (res.error === "timeout") return `${what} timed out`;
25698
- if (res.error === "too_large") return `${what} is too large`;
25699
- const detail = res.stderr.trim().split("\n")[0]?.slice(0, 160);
25700
- return detail ? `${what} failed: ${detail}` : `${what} failed`;
25701
- }
25702
- var INDEX = Symbol("index");
25703
- function changeRevision(range) {
25704
- return range.kind === "push" ? range.head : INDEX;
25705
- }
25706
- function baseRevision(range) {
25707
- return range.kind === "push" ? range.base : "HEAD";
25708
- }
25709
- function objectSpec(revision, path) {
25710
- return revision === INDEX ? `:${path}` : `${revision}:${path}`;
25711
- }
25712
- async function readBlob(ctx, revision, path) {
25713
- const res = await git(ctx, ["cat-file", "blob", objectSpec(revision, path)]);
25714
- if (!res.ok) {
25715
- if (res.error === "failed") return { ok: false, error: `${path} is not a file in ${revision === INDEX ? "the staged version" : "that version"}` };
25716
- return { ok: false, error: gitFailure(res, `reading ${path}`) };
25717
- }
25718
- if (res.stdout.slice(0, 8e3).includes("\0")) return { ok: false, error: `${path} is a binary file` };
25719
- return { ok: true, value: res.stdout };
25720
- }
25721
- function numberedRange(content, startLine, endLine, maxLines) {
25722
- const lines = content.split("\n");
25723
- if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
25724
- const total = lines.length;
25725
- const from = Math.min(Math.max(1, startLine), Math.max(1, total));
25726
- const to = Math.min(total, Math.max(from, endLine), from + maxLines - 1);
25727
- const width = String(to).length;
25728
- const body = lines.slice(from - 1, to).map((line, i) => `${String(from + i).padStart(width, " ")}| ${line}`).join("\n");
25729
- return `lines ${total === 0 ? 0 : from}-${to} of ${total}
25730
- ${body}`;
25731
- }
25732
- async function resolveRevision(ctx, value) {
25733
- if (value === "head") return { ok: true, value: changeRevision(ctx.range) };
25734
- if (value === "base") {
25735
- const base = baseRevision(ctx.range);
25736
- return base ? { ok: true, value: base } : { ok: false, error: "this change has no base version" };
25737
- }
25738
- if (typeof value === "string" && /^[0-9a-f]{7,40}$/.test(value)) {
25739
- const res = await git(ctx, ["rev-parse", "--verify", "--quiet", "--end-of-options", `${value}^{commit}`]);
25740
- if (res.ok && res.stdout.trim()) return { ok: true, value: res.stdout.trim() };
25741
- return { ok: false, error: `unknown commit ${value}` };
25742
- }
25743
- return { ok: false, error: 'revision must be "base", "head", or a commit hash from git_log' };
25744
- }
25745
- var readFileTool = async (ctx, args) => {
25746
- const path = validateRepoPath(args.path);
25747
- if (!path.ok) return path;
25748
- const blob = await readBlob(ctx, changeRevision(ctx.range), path.value);
25749
- if (!blob.ok) return blob;
25750
- const start = lineNumber(args.start_line, 1);
25751
- const end = lineNumber(args.end_line, start + TOOL_LIMITS.readLines - 1);
25752
- return { ok: true, value: `${path.value} (${ctx.range.kind === "push" ? "pushed version" : "staged version"}), ${numberedRange(blob.value, start, end, TOOL_LIMITS.readLines)}` };
25753
- };
25754
- var gitShowTool = async (ctx, args) => {
25755
- const path = validateRepoPath(args.path);
25756
- if (!path.ok) return path;
25757
- const revision = await resolveRevision(ctx, args.revision);
25758
- if (!revision.ok) return revision;
25759
- const blob = await readBlob(ctx, revision.value, path.value);
25760
- if (!blob.ok) return blob;
25761
- const start = lineNumber(args.start_line, 1);
25762
- const end = lineNumber(args.end_line, start + TOOL_LIMITS.readLines - 1);
25763
- return { ok: true, value: `${path.value} at ${String(args.revision)}, ${numberedRange(blob.value, start, end, TOOL_LIMITS.readLines)}` };
25764
- };
25765
- var gitDiffTool = async (ctx, args) => {
25766
- const path = validateRepoPath(args.path);
25767
- if (!path.ok) return path;
25768
- const range = ctx.range;
25769
- let diffArgs;
25770
- if (range.kind === "push") {
25771
- if (!range.base) return { ok: false, error: "this push has no base to diff against" };
25772
- diffArgs = ["diff", ...DIFF_READ_FLAGS, "--no-color", "-U10", range.base, range.head];
25773
- } else {
25774
- diffArgs = ["diff", ...DIFF_READ_FLAGS, "--no-color", "-U10", "--cached"];
25775
- }
25776
- const res = await git(ctx, ["--literal-pathspecs", ...diffArgs, "--", path.value]);
25777
- if (!res.ok) return { ok: false, error: gitFailure(res, "diff") };
25778
- return { ok: true, value: res.stdout.trim() ? capOutput(res.stdout) : `no changes to ${path.value} in the change under review` };
25779
- };
25780
- var gitLogTool = async (ctx, args) => {
25781
- const path = validateRepoPath(args.path);
25782
- if (!path.ok) return path;
25783
- const count = Math.min(TOOL_LIMITS.logCommits, lineNumber(args.max_count, TOOL_LIMITS.logCommits));
25784
- const revision = ctx.range.kind === "push" ? ctx.range.head : "HEAD";
25785
- const res = await git(ctx, [
25786
- "--literal-pathspecs",
25787
- "log",
25788
- "--no-show-signature",
25789
- "--no-color",
25790
- "-n",
25791
- String(count),
25792
- "--date=short",
25793
- "--format=%h %ad %s",
25794
- revision,
25795
- "--",
25796
- path.value
25797
- ]);
25798
- if (!res.ok) return { ok: false, error: gitFailure(res, "log") };
25799
- return { ok: true, value: res.stdout.trim() ? capOutput(res.stdout.trim()) : `no commits touch ${path.value}` };
25800
- };
25801
- var gitBlameTool = async (ctx, args) => {
25802
- const path = validateRepoPath(args.path);
25803
- if (!path.ok) return path;
25804
- const start = lineNumber(args.start_line, 1);
25805
- const end = Math.min(lineNumber(args.end_line, start), start + TOOL_LIMITS.blameLines - 1);
25806
- const revision = ctx.range.kind === "push" ? ctx.range.head : "HEAD";
25807
- const res = await git(ctx, ["blame", "--no-textconv", "-s", "-L", `${start},${Math.max(start, end)}`, revision, "--", path.value]);
25808
- if (!res.ok) return { ok: false, error: gitFailure(res, "blame") };
25809
- return { ok: true, value: capOutput(res.stdout.trimEnd()) };
25810
- };
25811
- var listFilesTool = async (ctx, args) => {
25812
- const dir = validateRepoPath(args.dir, { allowRoot: true });
25813
- if (!dir.ok) return dir;
25814
- const glob = typeof args.glob === "string" && args.glob.length > 0 && args.glob.length <= 200 ? args.glob : null;
25815
- const scope2 = dir.value || ".";
25816
- const res = ctx.range.kind === "push" ? await git(ctx, ["--literal-pathspecs", "ls-tree", "-r", "--name-only", "-z", ctx.range.head, "--", scope2]) : await git(ctx, ["--literal-pathspecs", "ls-files", "-z", "--cached", "--", scope2]);
25817
- if (!res.ok) return { ok: false, error: gitFailure(res, "listing") };
25818
- const paths = res.stdout.split("\0").filter((p) => p && !isDeniedPath(p) && (!glob || globMatch2(glob, p)));
25819
- const shown = paths.slice(0, TOOL_LIMITS.listEntries);
25820
- const more = paths.length - shown.length;
25821
- return { ok: true, value: shown.length ? `${shown.join("\n")}${more > 0 ? `
25822
- \u2026 ${more} more` : ""}` : "no matching files" };
25823
- };
25824
- var searchTool = async (ctx, args) => {
25825
- const text = args.text;
25826
- if (typeof text !== "string" || text.length === 0 || text.length > TOOL_LIMITS.textChars || /[\n\r]/.test(text)) {
25827
- return { ok: false, error: `text must be 1-${TOOL_LIMITS.textChars} characters on one line` };
25828
- }
25829
- const glob = typeof args.path_glob === "string" && args.path_glob.length > 0 ? args.path_glob : null;
25830
- if (glob && (glob.length > 200 || /[-]/.test(glob) || glob.startsWith("-"))) {
25831
- return { ok: false, error: "path_glob is not a valid glob" };
25832
- }
25833
- const pathspec = glob ? [`:(glob)${glob}`] : [];
25834
- const revision = ctx.range.kind === "push" ? [ctx.range.head] : ["--cached"];
25835
- const res = await git(ctx, ["grep", "-n", "-I", "-F", "--no-color", ...revision.filter((r) => r === "--cached"), "-e", text, ...revision.filter((r) => r !== "--cached"), "--", ...pathspec]);
25836
- if (!res.ok) {
25837
- if (res.error === "failed" && res.stderr.trim() === "") return { ok: true, value: "no matches" };
25838
- return { ok: false, error: gitFailure(res, "search") };
25839
- }
25840
- const prefix = ctx.range.kind === "push" ? `${ctx.range.head}:` : "";
25841
- const hits = [];
25842
- for (const raw of res.stdout.split("\n")) {
25843
- if (!raw) continue;
25844
- const line = prefix && raw.startsWith(prefix) ? raw.slice(prefix.length) : raw;
25845
- const file = line.slice(0, line.indexOf(":"));
25846
- if (isDeniedPath(file)) continue;
25847
- hits.push(line.length > TOOL_LIMITS.searchLineChars ? `${line.slice(0, TOOL_LIMITS.searchLineChars)}\u2026` : line);
25848
- if (hits.length >= TOOL_LIMITS.searchHits) break;
25849
- }
25850
- return { ok: true, value: hits.length ? hits.join("\n") : "no matches" };
25851
- };
25852
- var TOOLS = {
25853
- read_file: readFileTool,
25854
- search: searchTool,
25855
- list_files: listFilesTool,
25856
- git_log: gitLogTool,
25857
- git_blame: gitBlameTool,
25858
- git_show: gitShowTool,
25859
- git_diff: gitDiffTool
25860
- };
25861
- function makeToolExecutor(ctx) {
25862
- return async (calls) => {
25863
- const results = new Array(calls.length);
25864
- let next = 0;
25865
- const worker = async () => {
25866
- while (next < calls.length) {
25867
- const index = next++;
25868
- const call = calls[index];
25869
- const tool = TOOLS[call.name];
25870
- if (!tool) {
25871
- results[index] = { call_id: call.call_id, ok: false, error: `unknown tool ${call.name}` };
25872
- continue;
25873
- }
25874
- try {
25875
- const outcome = await tool(ctx, call.arguments ?? {});
25876
- results[index] = outcome.ok ? { call_id: call.call_id, ok: true, output: capOutput(outcome.value) } : { call_id: call.call_id, ok: false, error: outcome.error };
25877
- } catch (err) {
25878
- results[index] = { call_id: call.call_id, ok: false, error: `tool failed: ${String(err?.message ?? err).slice(0, 160)}` };
25879
- }
25880
- }
25881
- };
25882
- await Promise.all(Array.from({ length: Math.min(TOOL_LIMITS.concurrency, calls.length) }, worker));
25883
- return results;
25884
- };
25885
- }
25886
- async function buildAgentDiffs(ctx, paths, limits = { perFileChars: 24e3, totalChars: 12e4 }) {
25887
- const valid = paths.filter((p) => validateRepoPath(p).ok);
25888
- if (valid.length === 0 || ctx.range.kind === "nothing") return [];
25889
- if (ctx.range.kind === "push" && !ctx.range.base) return [];
25890
- const args = ctx.range.kind === "push" ? ["diff", ...DIFF_READ_FLAGS, "--no-color", "-U10", ctx.range.base, ctx.range.head] : ["diff", ...DIFF_READ_FLAGS, "--no-color", "-U10", "--cached"];
25891
- const res = await git(ctx, ["--literal-pathspecs", ...args, "--", ...valid], { timeoutMs: TOOL_LIMITS.diffTimeoutMs, maxBuffer: 16 * 1024 * 1024 });
25892
- if (!res.ok) return [];
25893
- const out = [];
25894
- let total = 0;
25895
- for (const chunk of res.stdout.split(/^(?=diff --git )/m)) {
25896
- if (!chunk.startsWith("diff --git ")) continue;
25897
- const plus = /^\+\+\+ b\/(.+)$/m.exec(chunk);
25898
- const minus = /^--- a\/(.+)$/m.exec(chunk);
25899
- const path = plus?.[1] ?? minus?.[1] ?? /^diff --git a\/(\S+)/.exec(chunk)?.[1];
25900
- if (!path || isDeniedPath(path)) continue;
25901
- const room = Math.min(limits.perFileChars, limits.totalChars - total);
25902
- if (room <= 0) break;
25903
- const diff = chunk.length > room ? chunk.slice(0, room) : chunk;
25904
- total += diff.length;
25905
- out.push({ path, diff });
25906
- }
25907
- return out;
25908
- }
25909
-
25910
- // src/commands/guard.ts
25911
- var EXCERPT_SOURCE_MAX_BYTES = 2 * 1024 * 1024;
24820
+ var import_node_path34 = require("node:path");
25912
24821
  var GUARD_BLOCK_CAP = 2;
25913
- var GUARD_ITER_FILE = (0, import_node_path35.join)(VERITY_DIR, ".guard-iteration");
24822
+ var GUARD_ITER_FILE = (0, import_node_path34.join)(VERITY_DIR, ".guard-iteration");
25914
24823
  function readPreToolUseStdin() {
25915
24824
  const empty = { command: "", cwd: null, sessionId: null };
25916
- return new Promise((resolve6) => {
24825
+ return new Promise((resolve5) => {
25917
24826
  try {
25918
- if (process.stdin.isTTY) return resolve6(empty);
24827
+ if (process.stdin.isTTY) return resolve5(empty);
25919
24828
  const chunks = [];
25920
24829
  let timer;
25921
24830
  let settled = false;
@@ -25928,7 +24837,7 @@ function readPreToolUseStdin() {
25928
24837
  process.stdin.removeListener("end", onEnd);
25929
24838
  process.stdin.removeListener("error", onError);
25930
24839
  process.stdin.pause();
25931
- resolve6(value);
24840
+ resolve5(value);
25932
24841
  };
25933
24842
  const onEnd = () => {
25934
24843
  try {
@@ -25949,7 +24858,7 @@ function readPreToolUseStdin() {
25949
24858
  process.stdin.on("error", onError);
25950
24859
  process.stdin.resume();
25951
24860
  } catch {
25952
- resolve6(empty);
24861
+ resolve5(empty);
25953
24862
  }
25954
24863
  });
25955
24864
  }
@@ -26061,13 +24970,8 @@ function hasBlockingFinding(response, sentFiles) {
26061
24970
  function buildGuardRequest(moment, files, codeDelta, iter, sessionId, statedIntent, coverageTelemetry) {
26062
24971
  const analyzable = filterAnalyzable(files);
26063
24972
  const securityFiles = filterSecurity(files);
26064
- let staticResults;
26065
- if (isCodacyAvailable()) {
26066
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs49.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
26067
- staticResults = runCodacyAnalysis(scannable);
26068
- } else {
26069
- staticResults = { tool: "@codacy/analysis-cli", findings: [], summary: { total_findings: 0, by_severity: {}, tools_run: [] } };
26070
- }
24973
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs49.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
24974
+ const staticResults = runCodacyAnalysisIfAvailable(scannable);
26071
24975
  const trigger = moment === "pre-commit" ? "hook:pre-commit" : "hook:pre-push";
26072
24976
  const requestBody = {
26073
24977
  static_results: staticResults,
@@ -26130,100 +25034,12 @@ function coverageBlock(c) {
26130
25034
  }
26131
25035
  function emitAllowNotice(userMsg, agentMsg) {
26132
25036
  process.stdout.write(JSON.stringify({
26133
- systemMessage: terminalText(userMsg, 8e3),
26134
- hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: terminalText(agentMsg, 4e4) }
25037
+ systemMessage: userMsg,
25038
+ hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: agentMsg }
26135
25039
  }) + "\n");
26136
25040
  process.exit(0);
26137
25041
  }
26138
- var GATES = ["PASS", "WARN", "FAIL"];
26139
- function worstGate(...gates) {
26140
- const rank2 = (gate) => {
26141
- const index = GATES.indexOf(String(gate ?? "").toUpperCase());
26142
- return index === -1 ? 1 : index;
26143
- };
26144
- return GATES[Math.max(...gates.map(rank2))];
26145
- }
26146
- function withAgentVerdict(oneShot, agent) {
26147
- if (agent.one_shot_source === "none" && typeof oneShot.gate_decision === "string") {
26148
- const added = agent.findings.filter((f) => f.agent_added === true);
26149
- return {
26150
- ...oneShot,
26151
- gate_decision: worstGate(oneShot.gate_decision, agent.gate_decision ?? "WARN", "WARN"),
26152
- findings: [...Array.isArray(oneShot.findings) ? oneShot.findings : [], ...added],
26153
- agent_review: {
26154
- ...agent.agent_review,
26155
- gate_decision_before: oneShot.gate_decision,
26156
- one_shot_source: "none",
26157
- cleared: [],
26158
- downgraded: []
26159
- }
26160
- };
26161
- }
26162
- return {
26163
- ...oneShot,
26164
- gate_decision: agent.gate_decision,
26165
- findings: agent.findings,
26166
- agent_review: {
26167
- ...agent.agent_review,
26168
- gate_decision_before: agent.gate_decision_before,
26169
- one_shot_source: agent.one_shot_source,
26170
- cleared: agent.cleared,
26171
- downgraded: agent.downgraded ?? []
26172
- }
26173
- };
26174
- }
26175
- function describeAgentOutcome(outcome) {
26176
- if (!outcome || outcome.kind === "disabled") return null;
26177
- if (outcome.kind !== "finished") return `Agent review: not used (${outcome.reason})`;
26178
- const { agent_review: r, gate_decision: after, gate_decision_before: before } = outcome.response;
26179
- const checks = Number(r.code_runtime_checks ?? 0) + Number(r.container_checks ?? 0);
26180
- const verdict = before && after && before !== after ? `, verdict changed from ${before} to ${after}` : "";
26181
- return `Agent review: ${r.outcome}, ${r.turns} turn(s), ${r.local_calls} read(s), ${checks} code check(s), ${r.cleared} cleared, ${Number(r.downgraded ?? 0)} downgraded, ${r.added} added${verdict}`;
26182
- }
26183
- function startAgentReview(input) {
26184
- if (process.env.VERITY_GIT_MOMENT_AGENT === "off") return null;
26185
- const ctx = { root: input.root, range: input.range };
26186
- const http = {
26187
- async post(path, body, timeoutMs) {
26188
- const res = await apiRequest({
26189
- method: "POST",
26190
- path,
26191
- serviceUrl: input.serviceUrl,
26192
- token: input.token,
26193
- body,
26194
- timeout: timeoutMs,
26195
- cmd: "guard-agent",
26196
- verbose: input.verbose,
26197
- encodeBody: true
26198
- });
26199
- return res.ok ? { ok: true, data: res.data } : { ok: false, error: res.error };
26200
- }
26201
- };
26202
- const sentPaths = input.codeDelta.files.map((f) => f.path);
26203
- const run2 = async () => runAgentReview({
26204
- http,
26205
- executeTools: makeToolExecutor(ctx),
26206
- startedAt: input.hookStartedAt,
26207
- deadlineMs: AGENT_DEADLINE_MS[input.moment],
26208
- start: {
26209
- moment: input.moment,
26210
- iteration: input.iteration,
26211
- session_id: input.sessionId,
26212
- stated_intent: input.statedIntent,
26213
- changed_files: input.files,
26214
- sent_paths: sentPaths,
26215
- diffs: await buildAgentDiffs(ctx, sentPaths),
26216
- repo_context: input.requestBody.repo_context,
26217
- static_results: input.requestBody.static_results,
26218
- one_shot_chars: input.codeDelta.files.reduce((n, f) => n + f.content.length, 0)
26219
- },
26220
- oneShot: input.oneShot.then((r) => r.ok ? { ok: true, response: r.data } : { ok: false }),
26221
- sessionId: input.sessionId
26222
- });
26223
- return run2().catch((err) => ({ kind: "failed", reason: String(err?.message ?? err) }));
26224
- }
26225
25042
  async function runGuard(opts, globals) {
26226
- const hookStartedAt = Date.now();
26227
25043
  const on = resolveGuardMoments(opts.on);
26228
25044
  if (on.length === 0) process.exit(0);
26229
25045
  const { command, cwd, sessionId } = await readPreToolUseStdin();
@@ -26295,12 +25111,12 @@ async function runGuard(opts, globals) {
26295
25111
  cwd: frame.worktreeRoot ?? process.cwd()
26296
25112
  });
26297
25113
  upgradeToExcerpts(repoContext2, {
26298
- // Rooted, no-follow read from the validated descriptor (safe-read.ts):
26299
- // site paths come from a search, and a site under a symlinked directory
26300
- // must not pull a file from outside the tree into the request.
26301
25114
  readFile: (rel) => {
26302
- const read = readRegularFileInRoot(frame.worktreeRoot ?? process.cwd(), rel, EXCERPT_SOURCE_MAX_BYTES);
26303
- return read.ok ? read.content : null;
25115
+ try {
25116
+ return (0, import_node_fs49.readFileSync)((0, import_node_path34.join)(frame.worktreeRoot ?? process.cwd(), rel), "utf8");
25117
+ } catch {
25118
+ return null;
25119
+ }
26304
25120
  }
26305
25121
  });
26306
25122
  requestBody.repo_context = repoContext2;
@@ -26329,7 +25145,7 @@ async function runGuard(opts, globals) {
26329
25145
  };
26330
25146
  logToFileOnly(coverageBlock(coverage));
26331
25147
  const reviewStart = Date.now();
26332
- const oneShot = analyzeRequest({
25148
+ const result = await analyzeRequest({
26333
25149
  serviceUrl: urlResult.data,
26334
25150
  token: tokenResult.data.token,
26335
25151
  body: requestBody,
@@ -26337,45 +25153,22 @@ async function runGuard(opts, globals) {
26337
25153
  timeout: 29e4,
26338
25154
  cmd: "guard"
26339
25155
  });
26340
- const agent = startAgentReview({
26341
- serviceUrl: urlResult.data,
26342
- token: tokenResult.data.token,
26343
- verbose: globals.verbose,
26344
- hookStartedAt,
26345
- moment,
26346
- root: frame.worktreeRoot,
26347
- range,
26348
- iteration: iter + 1,
26349
- sessionId,
26350
- statedIntent,
26351
- files,
26352
- codeDelta,
26353
- requestBody,
26354
- oneShot
26355
- });
26356
- const result = await oneShot;
26357
- const agentOutcome = agent ? await agent : null;
26358
- const agentVerdict = agentOutcome?.kind === "finished" && agentOutcome.response.gate_decision ? agentOutcome.response : null;
26359
25156
  const reviewSecs = Math.max(1, Math.round((Date.now() - reviewStart) / 1e3));
26360
- if (!result.ok && !agentVerdict) {
25157
+ if (!result.ok) {
26361
25158
  const authRemedy = result.error.startsWith("STALE_VERIFICATION") ? " Your GitHub verification expired \u2014 run `verity login` to re-verify." : result.error.startsWith("FORBIDDEN") ? " No access grant for this repository \u2014 run `verity login` to refresh your grants." : result.error.startsWith("INVALID_TOKEN") ? " Your Verity login expired or was revoked \u2014 run `verity login` to sign in again." : "";
26362
25159
  emitAllowNotice(
26363
25160
  `\u26A0 Verity ${moment}: ${authRemedy ? "not authorized" : "service offline"} \u2014 ${verb}ed WITHOUT review${authRemedy}`,
26364
25161
  `Verity ${moment}: ${authRemedy ? "not authorized" : "service unavailable"} (${result.error}); the ${verb} was allowed WITHOUT a Verity review.${authRemedy}`
26365
25162
  );
26366
25163
  }
26367
- const oneShotResponse = result.ok ? result.data : {};
26368
- const response = agentVerdict ? withAgentVerdict(oneShotResponse, agentVerdict) : oneShotResponse;
26369
- if (opts.json) process.stderr.write(JSON.stringify(response) + "\n");
25164
+ if (opts.json) process.stderr.write(JSON.stringify(result.data) + "\n");
25165
+ const response = result.data;
26370
25166
  const decision = response.gate_decision ?? "(unrecognised)";
26371
25167
  const viewUrl = response.view_url ?? "";
26372
25168
  const link = viewUrl ? ` \u2014 ${viewUrl}` : "";
26373
25169
  const covLine = `${coverageSummary(coverage)} in ${reviewSecs}s`;
26374
- const agentLine = describeAgentOutcome(agentOutcome);
26375
- const covDetail = agentLine ? `${coverageBlock(coverage)}
26376
- ${agentLine}` : coverageBlock(coverage);
26377
- const witnessed = [...codeDelta.files.map((f) => f.path), ...agentVerdict?.witnessed_files ?? []];
26378
- if (decision === "FAIL" && hasBlockingFinding(response, witnessed)) {
25170
+ const covDetail = coverageBlock(coverage);
25171
+ if (decision === "FAIL" && hasBlockingFinding(response, codeDelta.files.map((f) => f.path))) {
26379
25172
  writeIter(moment, iter + 1);
26380
25173
  writeBlockMessage(moment, response, covDetail);
26381
25174
  process.exit(2);
@@ -26394,16 +25187,12 @@ async function runGuard(opts, globals) {
26394
25187
  emitAllowNotice(notice.user, notice.agent);
26395
25188
  }
26396
25189
  function verdictNotice(ctx) {
26397
- const { moment, verb } = ctx;
26398
- const covLine = terminalText(ctx.covLine, 500);
26399
- const covDetail = terminalText(ctx.covDetail, 2e4);
26400
- const link = terminalText(ctx.link, 2100);
26401
- const viewUrl = terminalUrl(ctx.viewUrl);
26402
- const decision = ctx.decision == null ? "(unrecognised)" : terminalText(ctx.decision, 60);
25190
+ const { moment, verb, covLine, covDetail, link, viewUrl } = ctx;
25191
+ const decision = ctx.decision ?? "(unrecognised)";
26403
25192
  const report = viewUrl ? `
26404
25193
  Report: ${viewUrl}` : "";
26405
25194
  if (decision === "FAIL") {
26406
- const narrative = terminalText(ctx.narrative, 4e3);
25195
+ const narrative = ctx.narrative ?? "";
26407
25196
  return {
26408
25197
  user: `\u26A0 Verity ${moment}: the ${verb} may not match its stated purpose \u2014 proceeding (${covLine})${link}`,
26409
25198
  agent: `Verity ${moment}: intent-alignment WARNING (not blocked).${narrative ? " " + narrative : ""}
@@ -26434,9 +25223,9 @@ function writeBlockMessage(moment, response, covDetail) {
26434
25223
  const label2 = moment === "pre-commit" ? "pre-commit" : "pre-push";
26435
25224
  const verb = moment === "pre-commit" ? "commit" : "push";
26436
25225
  const assessment = response.assessment;
26437
- const narrative = terminalText(assessment?.narrative, 4e3);
26438
- const findings = Array.isArray(response.findings) ? response.findings : [];
26439
- const viewUrl = terminalUrl(response.view_url);
25226
+ const narrative = assessment?.narrative ?? "";
25227
+ const findings = response.findings ?? [];
25228
+ const viewUrl = response.view_url ?? "";
26440
25229
  process.stderr.write(`${RED}${BOLD}\u2501\u2501\u2501 Verity ${label2} gate: FAIL \u2501\u2501\u2501${NC}
26441
25230
 
26442
25231
  `);
@@ -26449,18 +25238,18 @@ function writeBlockMessage(moment, response, covDetail) {
26449
25238
 
26450
25239
  `);
26451
25240
  for (const f of agentFindings) {
26452
- const severity = terminalText(f.severity, 20).toUpperCase();
25241
+ const severity = (f.severity ?? "").toUpperCase();
26453
25242
  const fix = f.fix;
26454
- process.stderr.write(`[${severity}] ${terminalText(f.title, 300)}
25243
+ process.stderr.write(`[${severity}] ${f.title ?? ""}
26455
25244
  `);
26456
- process.stderr.write(` File: ${terminalText(f.file, 400)}:${terminalText(String(f.line ?? ""), 12)}
25245
+ process.stderr.write(` File: ${f.file ?? ""}:${f.line ?? ""}
26457
25246
  `);
26458
- process.stderr.write(` Fix: ${terminalText(fix?.description, 2e3) || "See description"}
25247
+ process.stderr.write(` Fix: ${fix?.description ?? "See description"}
26459
25248
 
26460
25249
  `);
26461
25250
  }
26462
25251
  }
26463
- process.stderr.write(`${DIM}${terminalText(covDetail, 2e4)}${NC}
25252
+ process.stderr.write(`${DIM}${covDetail}${NC}
26464
25253
 
26465
25254
  `);
26466
25255
  if (viewUrl) process.stderr.write(`${CYAN}Full report: ${viewUrl}${NC}
@@ -26621,10 +25410,10 @@ function registerWaiveCommand(program2) {
26621
25410
 
26622
25411
  // src/commands/init.ts
26623
25412
  var import_node_fs54 = require("node:fs");
26624
- var import_promises19 = require("node:fs/promises");
25413
+ var import_promises17 = require("node:fs/promises");
26625
25414
  var import_yaml6 = __toESM(require_dist());
26626
- var import_node_path38 = require("node:path");
26627
- var import_node_child_process17 = require("node:child_process");
25415
+ var import_node_path37 = require("node:path");
25416
+ var import_node_child_process16 = require("node:child_process");
26628
25417
 
26629
25418
  // src/lib/banner.ts
26630
25419
  var WORDMARK = [
@@ -26705,15 +25494,10 @@ function printPhase(n, of, title, subtitle) {
26705
25494
  var import_node_fs51 = require("node:fs");
26706
25495
 
26707
25496
  // src/lib/prereqs.ts
26708
- var import_node_child_process15 = require("node:child_process");
25497
+ var import_node_child_process14 = require("node:child_process");
26709
25498
  var MIN_NODE_MAJOR = 20;
26710
25499
  function which(bin) {
26711
- try {
26712
- const out = (0, import_node_child_process15.execSync)(`command -v ${bin}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
26713
- return out || null;
26714
- } catch {
26715
- return null;
26716
- }
25500
+ return whichSync(bin, { real: false });
26717
25501
  }
26718
25502
  function checkNode() {
26719
25503
  const version = process.version;
@@ -26731,7 +25515,7 @@ function checkNode() {
26731
25515
  function checkGit() {
26732
25516
  let detail = "";
26733
25517
  try {
26734
- detail = (0, import_node_child_process15.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
25518
+ detail = (0, import_node_child_process14.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
26735
25519
  } catch {
26736
25520
  return {
26737
25521
  id: "git",
@@ -26768,13 +25552,13 @@ function checkAnalysisCli() {
26768
25552
  }
26769
25553
  var INSTALL_TIMEOUT_MS = 12e4;
26770
25554
  function run(command, args, opts = {}) {
26771
- return new Promise((resolve6) => {
26772
- const child = (0, import_node_child_process15.spawn)(command, args, {
25555
+ return new Promise((resolve5) => {
25556
+ const child = (0, import_node_child_process14.spawn)(command, args, {
26773
25557
  stdio: opts.inherit ? "inherit" : "pipe",
26774
25558
  timeout: INSTALL_TIMEOUT_MS
26775
25559
  });
26776
- child.on("error", () => resolve6(false));
26777
- child.on("close", (code) => resolve6(code === 0));
25560
+ child.on("error", () => resolve5(false));
25561
+ child.on("close", (code) => resolve5(code === 0));
26778
25562
  });
26779
25563
  }
26780
25564
  async function installAnalysisCli() {
@@ -26818,7 +25602,7 @@ async function checkPrereqs(opts = {}) {
26818
25602
  }
26819
25603
 
26820
25604
  // src/lib/telemetry.ts
26821
- var import_promises17 = require("node:fs/promises");
25605
+ var import_promises15 = require("node:fs/promises");
26822
25606
  var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
26823
25607
  var GITIGNORE_FILE = ".gitignore";
26824
25608
  var GITIGNORE_ENTRY = SETTINGS_LOCAL_IGNORE_ENTRY;
@@ -26848,7 +25632,7 @@ function buildTelemetryEnv(serviceUrl) {
26848
25632
  var VERITY_TELEMETRY_KEYS = Object.keys(buildTelemetryEnv(""));
26849
25633
  async function readSettingsLocal() {
26850
25634
  try {
26851
- return JSON.parse(await (0, import_promises17.readFile)(projectPath(SETTINGS_LOCAL_FILE2), "utf-8"));
25635
+ return JSON.parse(await (0, import_promises15.readFile)(projectPath(SETTINGS_LOCAL_FILE2), "utf-8"));
26852
25636
  } catch {
26853
25637
  return {};
26854
25638
  }
@@ -26860,7 +25644,7 @@ async function ensureGitignore() {
26860
25644
  const file = projectPath(GITIGNORE_FILE);
26861
25645
  let content = "";
26862
25646
  try {
26863
- content = await (0, import_promises17.readFile)(file, "utf-8");
25647
+ content = await (0, import_promises15.readFile)(file, "utf-8");
26864
25648
  } catch {
26865
25649
  }
26866
25650
  const lines = content.split("\n").map((l) => l.trim());
@@ -26869,7 +25653,7 @@ async function ensureGitignore() {
26869
25653
  }
26870
25654
  const block = "# Verity telemetry \u2014 machine-local Claude Code settings\n" + GITIGNORE_ENTRY + "\n";
26871
25655
  const next = content ? content + (content.endsWith("\n") ? "" : "\n") + "\n" + block : block;
26872
- await (0, import_promises17.writeFile)(file, next);
25656
+ await (0, import_promises15.writeFile)(file, next);
26873
25657
  }
26874
25658
  async function installTelemetry(serviceUrl) {
26875
25659
  const env = buildTelemetryEnv(serviceUrl);
@@ -27050,15 +25834,15 @@ function registerDoctorCommand(program2) {
27050
25834
 
27051
25835
  // src/commands/migrate.ts
27052
25836
  var import_node_fs52 = require("node:fs");
27053
- var import_node_path36 = require("node:path");
27054
- var import_node_child_process16 = require("node:child_process");
25837
+ var import_node_path35 = require("node:path");
25838
+ var import_node_child_process15 = require("node:child_process");
27055
25839
  var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
27056
25840
  function defaultNpmRemover(pkg) {
27057
- (0, import_node_child_process16.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
25841
+ (0, import_node_child_process15.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
27058
25842
  }
27059
25843
  function isGitTracked(cwd, relPath) {
27060
25844
  try {
27061
- (0, import_node_child_process16.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
25845
+ (0, import_node_child_process15.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
27062
25846
  return true;
27063
25847
  } catch {
27064
25848
  return false;
@@ -27066,7 +25850,7 @@ function isGitTracked(cwd, relPath) {
27066
25850
  }
27067
25851
  function isGitRepo(cwd) {
27068
25852
  try {
27069
- (0, import_node_child_process16.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
25853
+ (0, import_node_child_process15.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
27070
25854
  return true;
27071
25855
  } catch {
27072
25856
  return false;
@@ -27087,8 +25871,8 @@ async function runMigration(opts = {}) {
27087
25871
  return { actions, migrated: actions.length > 0 };
27088
25872
  }
27089
25873
  function migrateProjectDir(root, actions) {
27090
- const gateDir = (0, import_node_path36.join)(root, ".gate");
27091
- const verityDir = (0, import_node_path36.join)(root, ".verity");
25874
+ const gateDir = (0, import_node_path35.join)(root, ".gate");
25875
+ const verityDir = (0, import_node_path35.join)(root, ".verity");
27092
25876
  if ((0, import_node_fs52.existsSync)(gateDir) && !(0, import_node_fs52.existsSync)(verityDir)) {
27093
25877
  return migrateProjectDirRename(root, gateDir, verityDir, actions);
27094
25878
  }
@@ -27106,7 +25890,7 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
27106
25890
  );
27107
25891
  }
27108
25892
  try {
27109
- (0, import_node_child_process16.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
25893
+ (0, import_node_child_process15.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
27110
25894
  actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
27111
25895
  moved = true;
27112
25896
  } catch {
@@ -27142,11 +25926,11 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
27142
25926
  }
27143
25927
  function migrateGlobalCredentials(home, actions) {
27144
25928
  if (!home) return;
27145
- const gateCreds = (0, import_node_path36.join)(home, ".gate", "credentials");
27146
- const verityCreds = (0, import_node_path36.join)(home, ".verity", "credentials");
25929
+ const gateCreds = (0, import_node_path35.join)(home, ".gate", "credentials");
25930
+ const verityCreds = (0, import_node_path35.join)(home, ".verity", "credentials");
27147
25931
  if (!(0, import_node_fs52.existsSync)(gateCreds)) return;
27148
25932
  if (!(0, import_node_fs52.existsSync)(verityCreds)) {
27149
- (0, import_node_fs52.mkdirSync)((0, import_node_path36.join)(home, ".verity"), { recursive: true });
25933
+ (0, import_node_fs52.mkdirSync)((0, import_node_path35.join)(home, ".verity"), { recursive: true });
27150
25934
  moveFile(gateCreds, verityCreds);
27151
25935
  actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
27152
25936
  return;
@@ -27168,7 +25952,7 @@ async function migrateLegacyHooks(root, actions) {
27168
25952
  }
27169
25953
  }
27170
25954
  async function migrateClaudeMd(root, actions) {
27171
- const claudeMd = (0, import_node_path36.join)(root, "CLAUDE.md");
25955
+ const claudeMd = (0, import_node_path35.join)(root, "CLAUDE.md");
27172
25956
  const hadLegacyBlock = (0, import_node_fs52.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
27173
25957
  if (!hadLegacyBlock) return;
27174
25958
  try {
@@ -27179,13 +25963,13 @@ async function migrateClaudeMd(root, actions) {
27179
25963
  }
27180
25964
  }
27181
25965
  function migrateStandardFile(root, actions) {
27182
- const gateMd = (0, import_node_path36.join)(root, "GATE.md");
27183
- const verityMd = (0, import_node_path36.join)(root, "VERITY.md");
25966
+ const gateMd = (0, import_node_path35.join)(root, "GATE.md");
25967
+ const verityMd = (0, import_node_path35.join)(root, "VERITY.md");
27184
25968
  if (!(0, import_node_fs52.existsSync)(gateMd) || (0, import_node_fs52.existsSync)(verityMd)) return;
27185
25969
  let moved = false;
27186
25970
  if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
27187
25971
  try {
27188
- (0, import_node_child_process16.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
25972
+ (0, import_node_child_process15.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
27189
25973
  moved = true;
27190
25974
  } catch {
27191
25975
  }
@@ -27197,7 +25981,7 @@ function migrateStandardFile(root, actions) {
27197
25981
  actions.push("Renamed GATE.md \u2192 VERITY.md");
27198
25982
  }
27199
25983
  async function migrateTelemetryHeaders(root, actions) {
27200
- const file = (0, import_node_path36.join)(root, ".claude", "settings.local.json");
25984
+ const file = (0, import_node_path35.join)(root, ".claude", "settings.local.json");
27201
25985
  if (!(0, import_node_fs52.existsSync)(file)) return;
27202
25986
  let settings;
27203
25987
  try {
@@ -27245,8 +26029,8 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
27245
26029
  toAppend.push(line.replace(/\r$/, ""));
27246
26030
  }
27247
26031
  if (toAppend.length > 0) {
27248
- const sep4 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
27249
- (0, import_node_fs52.writeFileSync)(verityCreds, verityContent + sep4 + toAppend.join("\n") + "\n");
26032
+ const sep3 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
26033
+ (0, import_node_fs52.writeFileSync)(verityCreds, verityContent + sep3 + toAppend.join("\n") + "\n");
27250
26034
  }
27251
26035
  (0, import_node_fs52.rmSync)(gateCreds, { force: true });
27252
26036
  return toAppend.length;
@@ -27260,7 +26044,7 @@ function readFileSyncSafe(path) {
27260
26044
  }
27261
26045
  function hasStagedChanges(root) {
27262
26046
  try {
27263
- (0, import_node_child_process16.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
26047
+ (0, import_node_child_process15.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
27264
26048
  return false;
27265
26049
  } catch {
27266
26050
  return true;
@@ -27287,15 +26071,15 @@ function moveFile(from, to) {
27287
26071
  function carryLegacyContents(gateDir, verityDir) {
27288
26072
  let copied = 0;
27289
26073
  const walk2 = (relDir) => {
27290
- const srcDir = (0, import_node_path36.join)(gateDir, relDir);
26074
+ const srcDir = (0, import_node_path35.join)(gateDir, relDir);
27291
26075
  for (const entry of (0, import_node_fs52.readdirSync)(srcDir)) {
27292
- const rel = relDir ? (0, import_node_path36.join)(relDir, entry) : entry;
27293
- const src = (0, import_node_path36.join)(gateDir, rel);
27294
- const dest = (0, import_node_path36.join)(verityDir, rel);
26076
+ const rel = relDir ? (0, import_node_path35.join)(relDir, entry) : entry;
26077
+ const src = (0, import_node_path35.join)(gateDir, rel);
26078
+ const dest = (0, import_node_path35.join)(verityDir, rel);
27295
26079
  if ((0, import_node_fs52.statSync)(src).isDirectory()) {
27296
26080
  walk2(rel);
27297
26081
  } else if (!(0, import_node_fs52.existsSync)(dest)) {
27298
- (0, import_node_fs52.mkdirSync)((0, import_node_path36.dirname)(dest), { recursive: true });
26082
+ (0, import_node_fs52.mkdirSync)((0, import_node_path35.dirname)(dest), { recursive: true });
27299
26083
  (0, import_node_fs52.cpSync)(src, dest);
27300
26084
  copied++;
27301
26085
  }
@@ -27305,22 +26089,22 @@ function carryLegacyContents(gateDir, verityDir) {
27305
26089
  return copied;
27306
26090
  }
27307
26091
  async function needsMigration(root = repoRoot()) {
27308
- const gateDir = (0, import_node_path36.join)(root, ".gate");
27309
- const verityDir = (0, import_node_path36.join)(root, ".verity");
26092
+ const gateDir = (0, import_node_path35.join)(root, ".gate");
26093
+ const verityDir = (0, import_node_path35.join)(root, ".verity");
27310
26094
  if ((0, import_node_fs52.existsSync)(gateDir) && !(0, import_node_fs52.existsSync)(verityDir)) return true;
27311
26095
  if ((0, import_node_fs52.existsSync)(gateDir) && (0, import_node_fs52.existsSync)(verityDir)) {
27312
- if ((0, import_node_fs52.existsSync)((0, import_node_path36.join)(gateDir, "credentials")) && !(0, import_node_fs52.existsSync)((0, import_node_path36.join)(verityDir, "credentials"))) {
26096
+ if ((0, import_node_fs52.existsSync)((0, import_node_path35.join)(gateDir, "credentials")) && !(0, import_node_fs52.existsSync)((0, import_node_path35.join)(verityDir, "credentials"))) {
27313
26097
  return true;
27314
26098
  }
27315
- if ((0, import_node_fs52.existsSync)((0, import_node_path36.join)(gateDir, "memory")) && !(0, import_node_fs52.existsSync)((0, import_node_path36.join)(verityDir, "memory"))) {
26099
+ if ((0, import_node_fs52.existsSync)((0, import_node_path35.join)(gateDir, "memory")) && !(0, import_node_fs52.existsSync)((0, import_node_path35.join)(verityDir, "memory"))) {
27316
26100
  return true;
27317
26101
  }
27318
26102
  }
27319
- const claudeMd = (0, import_node_path36.join)(root, "CLAUDE.md");
26103
+ const claudeMd = (0, import_node_path35.join)(root, "CLAUDE.md");
27320
26104
  if ((0, import_node_fs52.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
27321
26105
  return true;
27322
26106
  }
27323
- if ((0, import_node_fs52.existsSync)((0, import_node_path36.join)(root, "GATE.md")) && !(0, import_node_fs52.existsSync)((0, import_node_path36.join)(root, "VERITY.md"))) {
26107
+ if ((0, import_node_fs52.existsSync)((0, import_node_path35.join)(root, "GATE.md")) && !(0, import_node_fs52.existsSync)((0, import_node_path35.join)(root, "VERITY.md"))) {
27324
26108
  return true;
27325
26109
  }
27326
26110
  if (await hasLegacyHooksAt(root)) return true;
@@ -27449,7 +26233,7 @@ function runSelect(opts) {
27449
26233
  cursor: initialIdx[0] ?? 0,
27450
26234
  chosen: new Set(mode2 === "single" ? [initialIdx[0] ?? 0] : initialIdx)
27451
26235
  };
27452
- return new Promise((resolve6) => {
26236
+ return new Promise((resolve5) => {
27453
26237
  let painted = 0;
27454
26238
  let settled = false;
27455
26239
  const draw = () => {
@@ -27497,7 +26281,7 @@ function runSelect(opts) {
27497
26281
  if (settled) return;
27498
26282
  settled = true;
27499
26283
  restore();
27500
- resolve6(value);
26284
+ resolve5(value);
27501
26285
  };
27502
26286
  readline2.emitKeypressEvents(input);
27503
26287
  input.setRawMode(true);
@@ -27518,9 +26302,9 @@ async function askLine(question, io = {}) {
27518
26302
  output: io.output ?? process.stdout
27519
26303
  });
27520
26304
  try {
27521
- return await new Promise((resolve6) => {
27522
- rl.question(question).then((a) => resolve6(a.trim())).catch(() => resolve6(null));
27523
- rl.once("close", () => setImmediate(() => resolve6(null)));
26305
+ return await new Promise((resolve5) => {
26306
+ rl.question(question).then((a) => resolve5(a.trim())).catch(() => resolve5(null));
26307
+ rl.once("close", () => setImmediate(() => resolve5(null)));
27524
26308
  });
27525
26309
  } finally {
27526
26310
  rl.close();
@@ -27602,8 +26386,8 @@ async function promptMultiSelect(question, choices, fallback) {
27602
26386
 
27603
26387
  // src/lib/remote-config.ts
27604
26388
  var import_node_fs53 = require("node:fs");
27605
- var import_promises18 = require("node:fs/promises");
27606
- var import_node_path37 = require("node:path");
26389
+ var import_promises16 = require("node:fs/promises");
26390
+ var import_node_path36 = require("node:path");
27607
26391
  var import_yaml5 = __toESM(require_dist());
27608
26392
  var IGNORE_RIDER = "verityignore";
27609
26393
  async function fetchRemoteSetup(opts) {
@@ -27652,7 +26436,7 @@ async function adoptRemoteSetup(found, opts) {
27652
26436
  await writeOut(VERITYIGNORE_FILE, rider);
27653
26437
  written.push(VERITYIGNORE_FILE);
27654
26438
  } else {
27655
- const local = await (0, import_promises18.readFile)(localIgnore, "utf-8").catch(() => null);
26439
+ const local = await (0, import_promises16.readFile)(localIgnore, "utf-8").catch(() => null);
27656
26440
  if (local !== null && local !== rider) {
27657
26441
  notes.push(`${VERITYIGNORE_FILE} already exists here and differs from the pushed copy \u2014 kept yours.`);
27658
26442
  }
@@ -27679,10 +26463,10 @@ async function adoptRemoteSetup(found, opts) {
27679
26463
  written.push(VERITY_MD_FILE);
27680
26464
  return { written, notes };
27681
26465
  }
27682
- async function writeOut(relative2, body) {
27683
- const target = projectPath(relative2);
27684
- await (0, import_promises18.mkdir)((0, import_node_path37.dirname)(target), { recursive: true });
27685
- await (0, import_promises18.writeFile)(target, body);
26466
+ async function writeOut(relative3, body) {
26467
+ const target = projectPath(relative3);
26468
+ await (0, import_promises16.mkdir)((0, import_node_path36.dirname)(target), { recursive: true });
26469
+ await (0, import_promises16.writeFile)(target, body);
27686
26470
  }
27687
26471
  function describeRemote(found) {
27688
26472
  const when = found.standard.createdAt.slice(0, 10);
@@ -27752,7 +26536,7 @@ async function runOptionalAuth(resolution, opts = {}) {
27752
26536
  }
27753
26537
  let remote = "";
27754
26538
  try {
27755
- remote = (0, import_node_child_process17.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
26539
+ remote = (0, import_node_child_process16.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
27756
26540
  } catch {
27757
26541
  }
27758
26542
  if (!healed) {
@@ -27798,15 +26582,15 @@ async function runOptionalAuth(resolution, opts = {}) {
27798
26582
  }
27799
26583
  function resolveDataDir2() {
27800
26584
  const candidates2 = [
27801
- (0, import_node_path38.join)(__dirname, "..", "data"),
26585
+ (0, import_node_path37.join)(__dirname, "..", "data"),
27802
26586
  // installed: node_modules/@codacy/verity-cli/data
27803
- (0, import_node_path38.join)(__dirname, "..", "..", "data"),
26587
+ (0, import_node_path37.join)(__dirname, "..", "..", "data"),
27804
26588
  // edge case: nested resolution
27805
- (0, import_node_path38.join)(process.cwd(), "cli", "data")
26589
+ (0, import_node_path37.join)(process.cwd(), "cli", "data")
27806
26590
  // local dev: running from repo root
27807
26591
  ];
27808
26592
  for (const candidate of candidates2) {
27809
- if ((0, import_node_fs54.existsSync)((0, import_node_path38.join)(candidate, "skills"))) {
26593
+ if ((0, import_node_fs54.existsSync)((0, import_node_path37.join)(candidate, "skills"))) {
27810
26594
  return candidate;
27811
26595
  }
27812
26596
  }
@@ -27815,8 +26599,8 @@ function resolveDataDir2() {
27815
26599
  );
27816
26600
  }
27817
26601
  async function copyDir(src, dest) {
27818
- await (0, import_promises19.mkdir)(dest, { recursive: true });
27819
- await (0, import_promises19.cp)(src, dest, { recursive: true, force: true });
26602
+ await (0, import_promises17.mkdir)(dest, { recursive: true });
26603
+ await (0, import_promises17.cp)(src, dest, { recursive: true, force: true });
27820
26604
  }
27821
26605
  async function skillIsCurrent(src, dest) {
27822
26606
  const list2 = (dir) => {
@@ -27824,7 +26608,7 @@ async function skillIsCurrent(src, dest) {
27824
26608
  const walk2 = (d, prefix) => {
27825
26609
  for (const e of (0, import_node_fs54.readdirSync)(d, { withFileTypes: true })) {
27826
26610
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
27827
- if (e.isDirectory()) walk2((0, import_node_path38.join)(d, e.name), rel);
26611
+ if (e.isDirectory()) walk2((0, import_node_path37.join)(d, e.name), rel);
27828
26612
  else if (e.isFile()) out.push(rel);
27829
26613
  }
27830
26614
  };
@@ -27835,8 +26619,8 @@ async function skillIsCurrent(src, dest) {
27835
26619
  const shipped = list2(src);
27836
26620
  if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
27837
26621
  for (const rel of shipped) {
27838
- const a = await (0, import_promises19.readFile)((0, import_node_path38.join)(src, rel), "utf-8");
27839
- const b = await (0, import_promises19.readFile)((0, import_node_path38.join)(dest, rel), "utf-8");
26622
+ const a = await (0, import_promises17.readFile)((0, import_node_path37.join)(src, rel), "utf-8");
26623
+ const b = await (0, import_promises17.readFile)((0, import_node_path37.join)(dest, rel), "utf-8");
27840
26624
  if (a !== b) return false;
27841
26625
  }
27842
26626
  return true;
@@ -28023,7 +26807,7 @@ async function healStaleAnalysisConfig(globals) {
28023
26807
  printWarn(" Your analysis config names pattern ids that no longer resolve \u2014 those tools were");
28024
26808
  printWarn(" running silently with nothing enabled. Re-deriving it from your Standard\u2026");
28025
26809
  try {
28026
- const content = (0, import_yaml6.parse)(await (0, import_promises19.readFile)(standardPath, "utf-8"));
26810
+ const content = (0, import_yaml6.parse)(await (0, import_promises17.readFile)(standardPath, "utf-8"));
28027
26811
  const derived = await deriveConfigForStandard(content);
28028
26812
  for (const path of derived.written) printInfo(` ${path} \u2713 (re-derived)`);
28029
26813
  for (const note of derived.notes) printWarn(` ${note}`);
@@ -28104,7 +26888,7 @@ async function handoffToSetup(enabled, claudeInstalled) {
28104
26888
  console.log(" Usually a minute or two. Quit any time \u2014 re-running /verity-setup resumes.");
28105
26889
  console.log("");
28106
26890
  const startedAt = Date.now();
28107
- const run2 = (0, import_node_child_process17.spawnSync)("claude", ["/verity-setup"], { stdio: "inherit" });
26891
+ const run2 = (0, import_node_child_process16.spawnSync)("claude", ["/verity-setup"], { stdio: "inherit" });
28108
26892
  if (run2.error) {
28109
26893
  printWarn(`Could not start Claude Code: ${run2.error.message}`);
28110
26894
  return instruct("start it yourself and run the skill there");
@@ -28114,12 +26898,12 @@ async function handoffToSetup(enabled, claudeInstalled) {
28114
26898
  async function installSkills(force, step) {
28115
26899
  step("Installing skills");
28116
26900
  const dataDir = resolveDataDir2();
28117
- const skillsSource = (0, import_node_path38.join)(dataDir, "skills");
26901
+ const skillsSource = (0, import_node_path37.join)(dataDir, "skills");
28118
26902
  const skillsDest = ".claude/skills";
28119
26903
  let skillsInstalled = 0;
28120
26904
  for (const skill of SKILLS) {
28121
- const src = (0, import_node_path38.join)(skillsSource, skill);
28122
- const dest = (0, import_node_path38.join)(skillsDest, skill);
26905
+ const src = (0, import_node_path37.join)(skillsSource, skill);
26906
+ const dest = (0, import_node_path37.join)(skillsDest, skill);
28123
26907
  if (!(0, import_node_fs54.existsSync)(src)) {
28124
26908
  printWarn(` Skill data not found: ${skill}`);
28125
26909
  continue;
@@ -28188,7 +26972,7 @@ async function checkPrerequisites(step) {
28188
26972
  }
28189
26973
  async function scaffoldProject(step, defaultsOnly) {
28190
26974
  step("Knowledge base, .gitignore and CLAUDE.md");
28191
- await (0, import_promises19.mkdir)(VERITY_DIR, { recursive: true });
26975
+ await (0, import_promises17.mkdir)(VERITY_DIR, { recursive: true });
28192
26976
  await ensureMemoryDir();
28193
26977
  const ignoreResult = ensureVerityGitignore();
28194
26978
  if (ignoreResult === "failed") {
@@ -28353,8 +27137,8 @@ function registerInitCommand(program2) {
28353
27137
  printInfo(` intensity: ${intensity} \xB7 moments: ${moments.join(", ") || "none"} (no questions asked)`);
28354
27138
  }
28355
27139
  await scaffoldProject(step, defaultsOnly);
28356
- const globalVerityDir = (0, import_node_path38.join)(process.env.HOME ?? "", ".verity");
28357
- await (0, import_promises19.mkdir)(globalVerityDir, { recursive: true });
27140
+ const globalVerityDir = (0, import_node_path37.join)(process.env.HOME ?? "", ".verity");
27141
+ await (0, import_promises17.mkdir)(globalVerityDir, { recursive: true });
28358
27142
  console.log("");
28359
27143
  step("Wiring Claude Code hooks");
28360
27144
  const gitMoments = [
@@ -28452,7 +27236,7 @@ function registerInitCommand(program2) {
28452
27236
  ...telemetryChoice ? { telemetry: telemetryChoice } : {},
28453
27237
  init: {
28454
27238
  completed_at: (/* @__PURE__ */ new Date()).toISOString(),
28455
- cli_version: true ? "0.32.6-experimental.df0a578" : "dev"
27239
+ cli_version: true ? "0.32.7" : "dev"
28456
27240
  }
28457
27241
  });
28458
27242
  } catch (err) {
@@ -28503,7 +27287,7 @@ function registerInitCommand(program2) {
28503
27287
 
28504
27288
  // src/commands/uninstall.ts
28505
27289
  var import_node_fs55 = require("node:fs");
28506
- var import_node_path39 = require("node:path");
27290
+ var import_node_path38 = require("node:path");
28507
27291
  function registerUninstallCommand(program2) {
28508
27292
  program2.command("uninstall").description("Remove Verity from this project (skills, hooks, .verity/, VERITY.md)").option("--dry-run", "Show what would be removed without doing it").option("--purge-global", "Also remove ~/.verity/ (deletes saved tokens \u2014 reconnect requires re-registration)").option("--keep-verity-md", "Keep the project root VERITY.md file").action(async (opts) => {
28509
27293
  const dryRun = opts.dryRun ?? false;
@@ -28512,7 +27296,7 @@ function registerUninstallCommand(program2) {
28512
27296
  const actions = [];
28513
27297
  const skillsRoot = projectPath(".claude/skills");
28514
27298
  for (const name of PROJECT_SKILL_NAMES) {
28515
- const dir = (0, import_node_path39.join)(skillsRoot, name);
27299
+ const dir = (0, import_node_path38.join)(skillsRoot, name);
28516
27300
  if ((0, import_node_fs55.existsSync)(dir)) {
28517
27301
  actions.push({
28518
27302
  label: `Remove .claude/skills/${name}/`,
@@ -28558,7 +27342,7 @@ function registerUninstallCommand(program2) {
28558
27342
  }
28559
27343
  });
28560
27344
  const home = process.env.HOME ?? "";
28561
- const globalVerityDir = (0, import_node_path39.join)(home, ".verity");
27345
+ const globalVerityDir = (0, import_node_path38.join)(home, ".verity");
28562
27346
  if (purgeGlobal && (0, import_node_fs55.existsSync)(globalVerityDir)) {
28563
27347
  actions.push({
28564
27348
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
@@ -28757,7 +27541,7 @@ function registerTaskCommands(program2) {
28757
27541
 
28758
27542
  // src/commands/reset.ts
28759
27543
  var import_node_fs56 = require("node:fs");
28760
- var import_node_path40 = require("node:path");
27544
+ var import_node_path39 = require("node:path");
28761
27545
  function registerResetCommand(program2) {
28762
27546
  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) => {
28763
27547
  const globals = program2.opts();
@@ -28798,7 +27582,7 @@ function registerResetCommand(program2) {
28798
27582
  for (const entry of (0, import_node_fs56.readdirSync)(cacheDir)) {
28799
27583
  if (entry.startsWith("pending-")) {
28800
27584
  try {
28801
- (0, import_node_fs56.unlinkSync)((0, import_node_path40.join)(cacheDir, entry));
27585
+ (0, import_node_fs56.unlinkSync)((0, import_node_path39.join)(cacheDir, entry));
28802
27586
  purged++;
28803
27587
  } catch {
28804
27588
  }
@@ -28825,7 +27609,7 @@ function registerResetCommand(program2) {
28825
27609
  if ((0, import_node_fs56.existsSync)(logsDir)) {
28826
27610
  for (const entry of (0, import_node_fs56.readdirSync)(logsDir)) {
28827
27611
  try {
28828
- (0, import_node_fs56.unlinkSync)((0, import_node_path40.join)(logsDir, entry));
27612
+ (0, import_node_fs56.unlinkSync)((0, import_node_path39.join)(logsDir, entry));
28829
27613
  } catch {
28830
27614
  }
28831
27615
  }
@@ -28837,52 +27621,10 @@ function registerResetCommand(program2) {
28837
27621
  }
28838
27622
 
28839
27623
  // src/commands/reflect.ts
28840
- var import_node_fs57 = require("node:fs");
28841
-
28842
- // src/lib/reflection-globs.ts
28843
- var MAX_GLOBS = 6;
28844
- var PATH_TOKEN = /[A-Za-z0-9_./-]*[A-Za-z0-9_-]\.[A-Za-z0-9]{1,8}(?::\d+(?::\d+)?)?/g;
28845
- function clean(token) {
28846
- return token.replace(/:\d+(?::\d+)?$/, "").replace(/^[`'"(\[]+/, "").replace(/[`'"),\].;!?]+$/, "").replace(/^\.\//, "");
28847
- }
28848
- function resolve5(token, treePaths) {
28849
- if (treePaths.includes(token)) return token;
28850
- const suffix = token.startsWith("/") ? token : `/${token}`;
28851
- const matches = treePaths.filter((p) => p.endsWith(suffix));
28852
- return matches.length === 1 ? matches[0] : null;
28853
- }
28854
- function deriveFileGlobs(text, treePaths) {
28855
- if (!text || treePaths.length === 0) return [];
28856
- const out = [];
28857
- const seen = /* @__PURE__ */ new Set();
28858
- for (const raw of text.match(PATH_TOKEN) ?? []) {
28859
- const token = clean(raw);
28860
- if (!token) continue;
28861
- const path = resolve5(token, treePaths);
28862
- if (!path || seen.has(path)) continue;
28863
- seen.add(path);
28864
- out.push(path);
28865
- if (out.length >= MAX_GLOBS) break;
28866
- }
28867
- return out;
28868
- }
28869
-
28870
- // src/commands/reflect.ts
28871
- async function writeNodeToDisk(args) {
28872
- try {
28873
- const written = await applyMemoryWrites(
28874
- [{ path: args.filePath, content: args.content, node_id: args.nodeId, op: "create" }],
28875
- { treePaths: listTrackedFiles(), recordBaseline: false }
28876
- );
28877
- if (written === 0) return false;
28878
- return (0, import_node_fs57.existsSync)(projectPath(`${VERITY_DIR}/memory/${args.filePath}`));
28879
- } catch {
28880
- return false;
28881
- }
28882
- }
28883
27624
  function registerReflectCommand(program2) {
28884
- program2.command("reflect").description("Capture learnings \u2014 auto-extract or record a reflection").option("--user-input <text>", "The reflection to record (the agent-drafted or user-dictated 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("--confirmed", "The USER authored or confirmed these exact words \u2014 records as human-authored (source: user, confidence 1.0)").option("--file-globs <glob...>", "Files this reflection is about. Without any, the paths cited in the text are used; a reflection matching no file never surfaces in a review").option("--autonomous", "(deprecated, ignored) reflections are always recorded without a confirm step").action(async (opts) => {
27625
+ 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) => {
28885
27626
  const globals = program2.opts();
27627
+ const mode2 = resolveRunMode({ autonomousFlag: opts.autonomous });
28886
27628
  await ensureMemoryDir();
28887
27629
  const tokenResult = await resolveToken(globals.token);
28888
27630
  if (!tokenResult.ok) {
@@ -28947,11 +27689,6 @@ function registerReflectCommand(program2) {
28947
27689
  if (userInput) {
28948
27690
  const validKinds = ["decision", "gotcha", "pattern", "security", "quality", "intent", "domain", "integration"];
28949
27691
  const kind = validKinds.includes(opts.kind) ? opts.kind : "gotcha";
28950
- const confirmed = opts.confirmed === true;
28951
- const source = confirmed ? "user" : "agent";
28952
- const explicitGlobs = Array.isArray(opts.fileGlobs) ? opts.fileGlobs.map((g) => g.trim()).filter(Boolean) : [];
28953
- const derivedGlobs = explicitGlobs.length > 0 ? [] : deriveFileGlobs(userInput, listTrackedFiles());
28954
- const fileGlobs = explicitGlobs.length > 0 ? explicitGlobs : derivedGlobs.length > 0 ? derivedGlobs : inheritedGlobs;
28955
27692
  const result = await apiRequest({
28956
27693
  method: "POST",
28957
27694
  path: "/compound/memory/nodes",
@@ -28962,23 +27699,14 @@ function registerReflectCommand(program2) {
28962
27699
  title: userInput.slice(0, 200),
28963
27700
  body: userInput,
28964
27701
  domains: [],
28965
- file_globs: fileGlobs,
28966
- source,
28967
- created_by: "reflect",
28968
- // The server VERIFIES this against the project before writing it
28969
- // an id it cannot verify is dropped, never trusted and never an
28970
- // error. Sending it is what lets an auto-recorded reflection carry
28971
- // the same attribution a dashboard-written one does; without it the
28972
- // unreviewed node is the one that cannot say where it came from.
28973
- ...taskId && { task_id: taskId },
28974
- // Server-side default for each source; sent explicitly so an older
28975
- // server (which knows 'user'/'extractor'/'linter' only, and would
28976
- // silently coerce an unknown source to 'user' at 1.0) still records
28977
- // an honest confidence for a draft nobody confirmed.
28978
- confidence: confirmed ? 1 : 0.7
27702
+ // Inherit globs from the current task's context_files so the
27703
+ // reflection is actually reachable by reviewer injection.
27704
+ // Falls back to [] when there's no task context — user can
27705
+ // edit the markdown later to tighten retrieval.
27706
+ file_globs: inheritedGlobs
28979
27707
  },
28980
27708
  verbose: globals.verbose,
28981
- cmd: confirmed ? "reflect_user" : "reflect_agent"
27709
+ cmd: "reflect_user"
28982
27710
  });
28983
27711
  if (!result.ok) {
28984
27712
  printError(`Failed to save reflection: ${result.error}`);
@@ -28986,23 +27714,12 @@ function registerReflectCommand(program2) {
28986
27714
  }
28987
27715
  const nodeId = result.data.node_id;
28988
27716
  const filePath = result.data.file_path;
28989
- const viewUrl = typeof result.data.view_url === "string" ? result.data.view_url : null;
28990
- const content = typeof result.data.content === "string" ? result.data.content : null;
28991
- const synced = content !== null && await writeNodeToDisk({ filePath, content, nodeId });
28992
- printInfo(
28993
- synced ? `Recorded ${nodeId} \u2192 .verity/memory/${filePath}` : `Recorded ${nodeId} (syncs to .verity/memory/${filePath} on the next analysis).`
28994
- );
28995
- if (viewUrl) printInfo(` ${viewUrl}`);
27717
+ printInfo(`Queued: ${nodeId} (will sync to .verity/memory/${filePath} on next analysis).`);
28996
27718
  printInfo(
28997
- confirmed ? " Recorded as yours (source: user)." : " Auto-recorded from this task \u2014 nobody reviewed it. Edit or delete the file if it is wrong."
27719
+ mode2 === "autonomous" ? "Recorded autonomously (no confirm step) into the project knowledge base." : "Recorded your confirmed reflection into the project knowledge base."
28998
27720
  );
28999
- if (fileGlobs.length === 0) {
29000
- printWarn(
29001
- 'No files matched \u2014 this reflection will not surface in future reviews. Cite a path in the text, or pass --file-globs "<path or glob>".'
29002
- );
29003
- }
29004
27721
  if (taskResolution === "none") {
29005
- printInfo(" (No task context \u2014 reflection lives at the project level.)");
27722
+ printInfo("(No task context \u2014 reflection lives at the project level.)");
29006
27723
  }
29007
27724
  } else {
29008
27725
  if (!taskId) {
@@ -29089,121 +27806,6 @@ function registerMemoryCommand(program2) {
29089
27806
  }
29090
27807
  process.exit(result.failed === 0 ? 0 : 1);
29091
27808
  });
29092
- memory.command("pull").description("Download this repo's knowledge graph into .verity/memory/ (the session-start hook runs it)").option("--force", "Fetch every node even if nothing changed since the last pull").option("--quiet", "Print nothing and always exit 0 (for the session-start hook)").action(async (opts) => {
29093
- const globals = program2.opts();
29094
- const quiet = !!opts.quiet;
29095
- const fail = (message) => {
29096
- if (!quiet) printError(message);
29097
- process.exit(quiet ? 0 : 1);
29098
- };
29099
- try {
29100
- process.chdir(repoRoot());
29101
- } catch {
29102
- }
29103
- const tokenResult = await resolveToken(globals.token);
29104
- if (!tokenResult.ok) return fail(tokenResult.error);
29105
- const urlResult = await resolveServiceUrl(globals.serviceUrl);
29106
- if (!urlResult.ok) return fail(urlResult.error);
29107
- let result;
29108
- try {
29109
- result = await pullRepoMemory({
29110
- serviceUrl: urlResult.data,
29111
- token: tokenResult.data.token,
29112
- verbose: globals.verbose,
29113
- force: !!opts.force
29114
- });
29115
- } catch (err) {
29116
- return fail(`Could not pull the knowledge graph: ${err.message}`);
29117
- }
29118
- logEvent("memory_pull", result.ok ? { status: result.status, received: result.received, written: result.written } : { status: "failed", category: result.category ?? null });
29119
- if (!result.ok) return fail(`Could not pull the knowledge graph: ${result.error}`);
29120
- if (!quiet) {
29121
- printInfo(result.status === "unchanged" ? "Knowledge graph is up to date." : result.status === "tracked" ? "Skipped: .verity/memory/ is tracked by git here, and the pull never rewrites tracked files. Run `verity memory untrack` to make the graph machine-local." : `Pulled ${result.received} node(s); ${result.written} new or updated file(s) written to .verity/memory/.`);
29122
- }
29123
- let org;
29124
- try {
29125
- org = await pullOrgKnowledge({
29126
- serviceUrl: urlResult.data,
29127
- token: tokenResult.data.token,
29128
- verbose: globals.verbose,
29129
- force: !!opts.force
29130
- });
29131
- } catch (err) {
29132
- org = { ok: false, error: err.message };
29133
- }
29134
- logEvent("memory_org_pull", org.ok ? { status: org.status, received: org.received } : { status: "failed", category: org.category ?? null });
29135
- if (!quiet) {
29136
- if (!org.ok) printWarn(`Could not refresh the org knowledge mirror: ${org.error}`);
29137
- else if (org.status === "pulled") printInfo(`Org knowledge: ${org.received} claim(s) mirrored at ${org.dir}.`);
29138
- else if (org.status === "unchanged") printInfo("Org knowledge mirror is up to date.");
29139
- }
29140
- process.exit(0);
29141
- });
29142
- memory.command("org").description("List the org knowledge mirrored for this repository's organization").action(async () => {
29143
- try {
29144
- process.chdir(repoRoot());
29145
- } catch {
29146
- }
29147
- const dir = orgMirrorDir();
29148
- if (!dir) {
29149
- printError("Not a repository with an origin remote, so there is no organization to list.");
29150
- process.exit(1);
29151
- }
29152
- const claims = await readOrgMirror();
29153
- if (claims.length === 0) {
29154
- printInfo(`No org knowledge mirrored yet (${dir}). Run \`verity memory pull\`.`);
29155
- process.exit(0);
29156
- }
29157
- for (const c of claims) console.log(`${c.tag_id} ${c.kind.padEnd(11)} ${c.title} \xB7 ${c.origin}`);
29158
- printInfo(`${claims.length} claim(s) at ${dir}. Demote one with \`verity memory demote <id> --reason "\u2026"\`.`);
29159
- process.exit(0);
29160
- });
29161
- memory.command("demote <claim-id>").description("Demote an org knowledge claim for the whole organization, with a reason").requiredOption("--reason <text>", "Why this claim should no longer be shared (1\u2013500 characters)").action(async (claimId, opts) => {
29162
- const globals = program2.opts();
29163
- try {
29164
- process.chdir(repoRoot());
29165
- } catch {
29166
- }
29167
- if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(claimId)) {
29168
- printError("The claim id is the `id` in the mirrored file, a uuid. `verity memory org` lists them.");
29169
- process.exit(1);
29170
- }
29171
- const reason = opts.reason.trim();
29172
- if (reason.length === 0 || reason.length > 500) {
29173
- printError("--reason is 1 to 500 characters.");
29174
- process.exit(1);
29175
- }
29176
- const tokenResult = await resolveToken(globals.token);
29177
- if (!tokenResult.ok) {
29178
- printError(tokenResult.error);
29179
- process.exit(1);
29180
- }
29181
- const urlResult = await resolveServiceUrl(globals.serviceUrl);
29182
- if (!urlResult.ok) {
29183
- printError(urlResult.error);
29184
- process.exit(1);
29185
- }
29186
- const res = await apiRequest({
29187
- method: "POST",
29188
- path: `/memory/org/claims/${claimId}/demote`,
29189
- serviceUrl: urlResult.data,
29190
- token: tokenResult.data.token,
29191
- body: { reason },
29192
- verbose: globals.verbose,
29193
- cmd: "memory-demote",
29194
- extraHeaders: { "X-Verity-Via": "cli" }
29195
- });
29196
- if (!res.ok) {
29197
- printError(`Could not demote the claim: ${res.error}`);
29198
- process.exit(1);
29199
- }
29200
- printInfo(`Demoted ${claimId} for the whole organization. Restore it from the dashboard if that was wrong.`);
29201
- try {
29202
- await pullOrgKnowledge({ serviceUrl: urlResult.data, token: tokenResult.data.token, verbose: globals.verbose, force: true });
29203
- } catch {
29204
- }
29205
- process.exit(0);
29206
- });
29207
27809
  memory.command("untrack").description("Stop committing .verity/memory/ \u2014 the graph stays on disk and leaves your diffs").action(() => {
29208
27810
  try {
29209
27811
  process.chdir(repoRoot());
@@ -29367,8 +27969,8 @@ function registerTelemetryCommands(program2) {
29367
27969
  }
29368
27970
 
29369
27971
  // src/cli.ts
29370
- program.name("verity").description("CLI for Verity quality gate service").version("0.32.6-experimental.df0a578").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
29371
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.32.6-experimental.df0a578");
27972
+ program.name("verity").description("CLI for Verity quality gate service").version("0.32.7").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
27973
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.32.7");
29372
27974
  setUserNamedServiceUrl(program.opts().serviceUrl);
29373
27975
  try {
29374
27976
  await foldLegacyLocalCredential();