@codacy/verity-cli 0.32.0 → 0.32.1-experimental.f2cf829

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +232 -52
  2. package/bin/verity.js +782 -359
  3. package/package.json +1 -1
package/bin/verity.js CHANGED
@@ -911,18 +911,18 @@ var require_suggestSimilar = __commonJS({
911
911
  }
912
912
  return d[a.length][b.length];
913
913
  }
914
- function suggestSimilar(word, candidates) {
915
- if (!candidates || candidates.length === 0) return "";
916
- candidates = Array.from(new Set(candidates));
914
+ function suggestSimilar(word, candidates2) {
915
+ if (!candidates2 || candidates2.length === 0) return "";
916
+ candidates2 = Array.from(new Set(candidates2));
917
917
  const searchingOptions = word.startsWith("--");
918
918
  if (searchingOptions) {
919
919
  word = word.slice(2);
920
- candidates = candidates.map((candidate) => candidate.slice(2));
920
+ candidates2 = candidates2.map((candidate) => candidate.slice(2));
921
921
  }
922
922
  let similar = [];
923
923
  let bestDistance = maxDistance;
924
924
  const minSimilarity = 0.4;
925
- candidates.forEach((candidate) => {
925
+ candidates2.forEach((candidate) => {
926
926
  if (candidate.length <= 1) return;
927
927
  const distance = editDistance(word, candidate);
928
928
  const length = Math.max(word.length, candidate.length);
@@ -6974,10 +6974,10 @@ var require_resolve_block_map = __commonJS({
6974
6974
  let offset = bm.offset;
6975
6975
  let commentEnd = null;
6976
6976
  for (const collItem of bm.items) {
6977
- const { start, key, sep: sep2, value } = collItem;
6977
+ const { start, key, sep: sep3, value } = collItem;
6978
6978
  const keyProps = resolveProps.resolveProps(start, {
6979
6979
  indicator: "explicit-key-ind",
6980
- next: key ?? sep2?.[0],
6980
+ next: key ?? sep3?.[0],
6981
6981
  offset,
6982
6982
  onError,
6983
6983
  parentIndent: bm.indent,
@@ -6991,7 +6991,7 @@ var require_resolve_block_map = __commonJS({
6991
6991
  else if ("indent" in key && key.indent !== bm.indent)
6992
6992
  onError(offset, "BAD_INDENT", startColMsg);
6993
6993
  }
6994
- if (!keyProps.anchor && !keyProps.tag && !sep2) {
6994
+ if (!keyProps.anchor && !keyProps.tag && !sep3) {
6995
6995
  commentEnd = keyProps.end;
6996
6996
  if (keyProps.comment) {
6997
6997
  if (map.comment)
@@ -7015,7 +7015,7 @@ var require_resolve_block_map = __commonJS({
7015
7015
  ctx.atKey = false;
7016
7016
  if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
7017
7017
  onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
7018
- const valueProps = resolveProps.resolveProps(sep2 ?? [], {
7018
+ const valueProps = resolveProps.resolveProps(sep3 ?? [], {
7019
7019
  indicator: "map-value-ind",
7020
7020
  next: value,
7021
7021
  offset: keyNode.range[2],
@@ -7031,7 +7031,7 @@ var require_resolve_block_map = __commonJS({
7031
7031
  if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
7032
7032
  onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
7033
7033
  }
7034
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep2, null, valueProps, onError);
7034
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep3, null, valueProps, onError);
7035
7035
  if (ctx.schema.compat)
7036
7036
  utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
7037
7037
  offset = valueNode.range[2];
@@ -7122,7 +7122,7 @@ var require_resolve_end = __commonJS({
7122
7122
  let comment = "";
7123
7123
  if (end) {
7124
7124
  let hasSpace = false;
7125
- let sep2 = "";
7125
+ let sep3 = "";
7126
7126
  for (const token of end) {
7127
7127
  const { source, type } = token;
7128
7128
  switch (type) {
@@ -7136,13 +7136,13 @@ var require_resolve_end = __commonJS({
7136
7136
  if (!comment)
7137
7137
  comment = cb;
7138
7138
  else
7139
- comment += sep2 + cb;
7140
- sep2 = "";
7139
+ comment += sep3 + cb;
7140
+ sep3 = "";
7141
7141
  break;
7142
7142
  }
7143
7143
  case "newline":
7144
7144
  if (comment)
7145
- sep2 += source;
7145
+ sep3 += source;
7146
7146
  hasSpace = true;
7147
7147
  break;
7148
7148
  default:
@@ -7185,18 +7185,18 @@ var require_resolve_flow_collection = __commonJS({
7185
7185
  let offset = fc.offset + fc.start.source.length;
7186
7186
  for (let i = 0; i < fc.items.length; ++i) {
7187
7187
  const collItem = fc.items[i];
7188
- const { start, key, sep: sep2, value } = collItem;
7188
+ const { start, key, sep: sep3, value } = collItem;
7189
7189
  const props = resolveProps.resolveProps(start, {
7190
7190
  flow: fcName,
7191
7191
  indicator: "explicit-key-ind",
7192
- next: key ?? sep2?.[0],
7192
+ next: key ?? sep3?.[0],
7193
7193
  offset,
7194
7194
  onError,
7195
7195
  parentIndent: fc.indent,
7196
7196
  startOnNewline: false
7197
7197
  });
7198
7198
  if (!props.found) {
7199
- if (!props.anchor && !props.tag && !sep2 && !value) {
7199
+ if (!props.anchor && !props.tag && !sep3 && !value) {
7200
7200
  if (i === 0 && props.comma)
7201
7201
  onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
7202
7202
  else if (i < fc.items.length - 1)
@@ -7250,8 +7250,8 @@ var require_resolve_flow_collection = __commonJS({
7250
7250
  }
7251
7251
  }
7252
7252
  }
7253
- if (!isMap && !sep2 && !props.found) {
7254
- const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep2, null, props, onError);
7253
+ if (!isMap && !sep3 && !props.found) {
7254
+ const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep3, null, props, onError);
7255
7255
  coll.items.push(valueNode);
7256
7256
  offset = valueNode.range[2];
7257
7257
  if (isBlock(value))
@@ -7263,7 +7263,7 @@ var require_resolve_flow_collection = __commonJS({
7263
7263
  if (isBlock(key))
7264
7264
  onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
7265
7265
  ctx.atKey = false;
7266
- const valueProps = resolveProps.resolveProps(sep2 ?? [], {
7266
+ const valueProps = resolveProps.resolveProps(sep3 ?? [], {
7267
7267
  flow: fcName,
7268
7268
  indicator: "map-value-ind",
7269
7269
  next: value,
@@ -7274,8 +7274,8 @@ var require_resolve_flow_collection = __commonJS({
7274
7274
  });
7275
7275
  if (valueProps.found) {
7276
7276
  if (!isMap && !props.found && ctx.options.strict) {
7277
- if (sep2)
7278
- for (const st of sep2) {
7277
+ if (sep3)
7278
+ for (const st of sep3) {
7279
7279
  if (st === valueProps.found)
7280
7280
  break;
7281
7281
  if (st.type === "newline") {
@@ -7292,7 +7292,7 @@ var require_resolve_flow_collection = __commonJS({
7292
7292
  else
7293
7293
  onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
7294
7294
  }
7295
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep2, null, valueProps, onError) : null;
7295
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep3, null, valueProps, onError) : null;
7296
7296
  if (valueNode) {
7297
7297
  if (isBlock(value))
7298
7298
  onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
@@ -7472,7 +7472,7 @@ var require_resolve_block_scalar = __commonJS({
7472
7472
  chompStart = i + 1;
7473
7473
  }
7474
7474
  let value = "";
7475
- let sep2 = "";
7475
+ let sep3 = "";
7476
7476
  let prevMoreIndented = false;
7477
7477
  for (let i = 0; i < contentStart; ++i)
7478
7478
  value += lines[i][0].slice(trimIndent) + "\n";
@@ -7489,24 +7489,24 @@ var require_resolve_block_scalar = __commonJS({
7489
7489
  indent = "";
7490
7490
  }
7491
7491
  if (type === Scalar.Scalar.BLOCK_LITERAL) {
7492
- value += sep2 + indent.slice(trimIndent) + content;
7493
- sep2 = "\n";
7492
+ value += sep3 + indent.slice(trimIndent) + content;
7493
+ sep3 = "\n";
7494
7494
  } else if (indent.length > trimIndent || content[0] === " ") {
7495
- if (sep2 === " ")
7496
- sep2 = "\n";
7497
- else if (!prevMoreIndented && sep2 === "\n")
7498
- sep2 = "\n\n";
7499
- value += sep2 + indent.slice(trimIndent) + content;
7500
- sep2 = "\n";
7495
+ if (sep3 === " ")
7496
+ sep3 = "\n";
7497
+ else if (!prevMoreIndented && sep3 === "\n")
7498
+ sep3 = "\n\n";
7499
+ value += sep3 + indent.slice(trimIndent) + content;
7500
+ sep3 = "\n";
7501
7501
  prevMoreIndented = true;
7502
7502
  } else if (content === "") {
7503
- if (sep2 === "\n")
7503
+ if (sep3 === "\n")
7504
7504
  value += "\n";
7505
7505
  else
7506
- sep2 = "\n";
7506
+ sep3 = "\n";
7507
7507
  } else {
7508
- value += sep2 + content;
7509
- sep2 = " ";
7508
+ value += sep3 + content;
7509
+ sep3 = " ";
7510
7510
  prevMoreIndented = false;
7511
7511
  }
7512
7512
  }
@@ -7684,25 +7684,25 @@ var require_resolve_flow_scalar = __commonJS({
7684
7684
  if (!match)
7685
7685
  return source;
7686
7686
  let res = match[1];
7687
- let sep2 = " ";
7687
+ let sep3 = " ";
7688
7688
  let pos = first.lastIndex;
7689
7689
  line.lastIndex = pos;
7690
7690
  while (match = line.exec(source)) {
7691
7691
  if (match[1] === "") {
7692
- if (sep2 === "\n")
7693
- res += sep2;
7692
+ if (sep3 === "\n")
7693
+ res += sep3;
7694
7694
  else
7695
- sep2 = "\n";
7695
+ sep3 = "\n";
7696
7696
  } else {
7697
- res += sep2 + match[1];
7698
- sep2 = " ";
7697
+ res += sep3 + match[1];
7698
+ sep3 = " ";
7699
7699
  }
7700
7700
  pos = line.lastIndex;
7701
7701
  }
7702
7702
  const last = /[ \t]*(.*)/sy;
7703
7703
  last.lastIndex = pos;
7704
7704
  match = last.exec(source);
7705
- return res + sep2 + (match?.[1] ?? "");
7705
+ return res + sep3 + (match?.[1] ?? "");
7706
7706
  }
7707
7707
  function doubleQuotedValue(source, onError) {
7708
7708
  let res = "";
@@ -8509,14 +8509,14 @@ var require_cst_stringify = __commonJS({
8509
8509
  }
8510
8510
  }
8511
8511
  }
8512
- function stringifyItem({ start, key, sep: sep2, value }) {
8512
+ function stringifyItem({ start, key, sep: sep3, value }) {
8513
8513
  let res = "";
8514
8514
  for (const st of start)
8515
8515
  res += st.source;
8516
8516
  if (key)
8517
8517
  res += stringifyToken(key);
8518
- if (sep2)
8519
- for (const st of sep2)
8518
+ if (sep3)
8519
+ for (const st of sep3)
8520
8520
  res += st.source;
8521
8521
  if (value)
8522
8522
  res += stringifyToken(value);
@@ -9659,18 +9659,18 @@ var require_parser = __commonJS({
9659
9659
  if (this.type === "map-value-ind") {
9660
9660
  const prev = getPrevProps(this.peek(2));
9661
9661
  const start = getFirstKeyStartProps(prev);
9662
- let sep2;
9662
+ let sep3;
9663
9663
  if (scalar.end) {
9664
- sep2 = scalar.end;
9665
- sep2.push(this.sourceToken);
9664
+ sep3 = scalar.end;
9665
+ sep3.push(this.sourceToken);
9666
9666
  delete scalar.end;
9667
9667
  } else
9668
- sep2 = [this.sourceToken];
9668
+ sep3 = [this.sourceToken];
9669
9669
  const map = {
9670
9670
  type: "block-map",
9671
9671
  offset: scalar.offset,
9672
9672
  indent: scalar.indent,
9673
- items: [{ start, key: scalar, sep: sep2 }]
9673
+ items: [{ start, key: scalar, sep: sep3 }]
9674
9674
  };
9675
9675
  this.onKeyLine = true;
9676
9676
  this.stack[this.stack.length - 1] = map;
@@ -9822,15 +9822,15 @@ var require_parser = __commonJS({
9822
9822
  } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
9823
9823
  const start2 = getFirstKeyStartProps(it.start);
9824
9824
  const key = it.key;
9825
- const sep2 = it.sep;
9826
- sep2.push(this.sourceToken);
9825
+ const sep3 = it.sep;
9826
+ sep3.push(this.sourceToken);
9827
9827
  delete it.key;
9828
9828
  delete it.sep;
9829
9829
  this.stack.push({
9830
9830
  type: "block-map",
9831
9831
  offset: this.offset,
9832
9832
  indent: this.indent,
9833
- items: [{ start: start2, key, sep: sep2 }]
9833
+ items: [{ start: start2, key, sep: sep3 }]
9834
9834
  });
9835
9835
  } else if (start.length > 0) {
9836
9836
  it.sep = it.sep.concat(start, this.sourceToken);
@@ -10024,13 +10024,13 @@ var require_parser = __commonJS({
10024
10024
  const prev = getPrevProps(parent);
10025
10025
  const start = getFirstKeyStartProps(prev);
10026
10026
  fixFlowSeqItems(fc);
10027
- const sep2 = fc.end.splice(1, fc.end.length);
10028
- sep2.push(this.sourceToken);
10027
+ const sep3 = fc.end.splice(1, fc.end.length);
10028
+ sep3.push(this.sourceToken);
10029
10029
  const map = {
10030
10030
  type: "block-map",
10031
10031
  offset: fc.offset,
10032
10032
  indent: fc.indent,
10033
- items: [{ start, key: fc, sep: sep2 }]
10033
+ items: [{ start, key: fc, sep: sep3 }]
10034
10034
  };
10035
10035
  this.onKeyLine = true;
10036
10036
  this.stack[this.stack.length - 1] = map;
@@ -10509,7 +10509,7 @@ var SECURITY_PATTERNS = [
10509
10509
  /Dockerfile/
10510
10510
  ];
10511
10511
  var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
10512
- var DEFAULT_SERVICE_URL = "".length > 0 ? "" : PROD_SERVICE_URL;
10512
+ var DEFAULT_SERVICE_URL = "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1".length > 0 ? "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1" : PROD_SERVICE_URL;
10513
10513
  var GITHUB_CLIENT_ID = "Iv23li88HxAi3ZrbYzWh";
10514
10514
  var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
10515
10515
  var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
@@ -11519,7 +11519,7 @@ function startSpinner(label2, opts = {}) {
11519
11519
  }
11520
11520
 
11521
11521
  // src/lib/provider-auth.ts
11522
- var sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
11522
+ var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
11523
11523
  var form = (fields) => new URLSearchParams(fields).toString();
11524
11524
  async function githubAccountId(owner) {
11525
11525
  try {
@@ -12735,6 +12735,7 @@ async function applyMomentSelection(moments) {
12735
12735
 
12736
12736
  // src/lib/project-config.ts
12737
12737
  var import_node_fs4 = require("node:fs");
12738
+ var import_node_path6 = require("node:path");
12738
12739
  var DEFAULTS = { git_moments: [] };
12739
12740
  function isMoment(value) {
12740
12741
  return value === "commit" || value === "push";
@@ -12742,20 +12743,40 @@ function isMoment(value) {
12742
12743
  function parseMoments(raw) {
12743
12744
  return [...new Set(raw.split(",").map((s) => s.trim()).filter(isMoment))];
12744
12745
  }
12745
- function readProjectConfig() {
12746
+ function readConfigFile(path) {
12746
12747
  try {
12747
- if (!(0, import_node_fs4.existsSync)(PROJECT_CONFIG_FILE)) return DEFAULTS;
12748
- const raw = JSON.parse((0, import_node_fs4.readFileSync)(PROJECT_CONFIG_FILE, "utf-8"));
12748
+ if (!(0, import_node_fs4.existsSync)(path)) return null;
12749
+ const raw = JSON.parse((0, import_node_fs4.readFileSync)(path, "utf-8"));
12749
12750
  const moments = Array.isArray(raw.git_moments) ? raw.git_moments.filter(isMoment) : [];
12750
- return { git_moments: [...new Set(moments)] };
12751
+ const source = raw.git_moments_source === "user" || raw.git_moments_source === "init" ? raw.git_moments_source : void 0;
12752
+ return { git_moments: [...new Set(moments)], ...source ? { git_moments_source: source } : {} };
12751
12753
  } catch {
12752
12754
  return DEFAULTS;
12753
12755
  }
12754
12756
  }
12757
+ function candidates() {
12758
+ const local = projectPath(PROJECT_CONFIG_FILE);
12759
+ const out = [{ path: local, source: "local" }];
12760
+ const main = mainWorktreeRoot();
12761
+ if (main) {
12762
+ const inherited = (0, import_node_path6.join)(main, PROJECT_CONFIG_FILE);
12763
+ if (inherited !== local) out.push({ path: inherited, source: "main-worktree" });
12764
+ }
12765
+ return out;
12766
+ }
12767
+ function readProjectConfig() {
12768
+ for (const { path, source } of candidates()) {
12769
+ const cfg = readConfigFile(path);
12770
+ if (cfg) return { ...cfg, source, path };
12771
+ }
12772
+ return { ...DEFAULTS, source: "default" };
12773
+ }
12755
12774
  function writeProjectConfig(patch) {
12756
- const next = { ...readProjectConfig(), ...patch };
12757
- (0, import_node_fs4.mkdirSync)(VERITY_DIR, { recursive: true });
12758
- (0, import_node_fs4.writeFileSync)(PROJECT_CONFIG_FILE, JSON.stringify(next, null, 2) + "\n");
12775
+ const { git_moments, git_moments_source } = readProjectConfig();
12776
+ const next = { git_moments, ...git_moments_source ? { git_moments_source } : {}, ...patch };
12777
+ const target = projectPath(PROJECT_CONFIG_FILE);
12778
+ (0, import_node_fs4.mkdirSync)((0, import_node_path6.dirname)(target), { recursive: true });
12779
+ (0, import_node_fs4.writeFileSync)(target, JSON.stringify(next, null, 2) + "\n");
12759
12780
  return next;
12760
12781
  }
12761
12782
  function resolveGuardMoments(explicit) {
@@ -12765,6 +12786,8 @@ function resolveGuardMoments(explicit) {
12765
12786
 
12766
12787
  // src/lib/plugin-ownership.ts
12767
12788
  var import_node_fs6 = require("node:fs");
12789
+ var import_node_os2 = require("node:os");
12790
+ var import_node_path7 = require("node:path");
12768
12791
 
12769
12792
  // src/lib/stderr-log.ts
12770
12793
  var import_node_fs5 = require("node:fs");
@@ -12821,16 +12844,19 @@ function logToFileOnly(text) {
12821
12844
  }
12822
12845
 
12823
12846
  // src/lib/plugin-ownership.ts
12824
- var MARKER_TTL_SECONDS = 24 * 60 * 60;
12847
+ var CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
12825
12848
  function isPluginInvocation() {
12826
12849
  return !!process.env.VERITY_PLUGIN_ROOT;
12827
12850
  }
12851
+ function markerPath() {
12852
+ return projectPath(PLUGIN_MARKER_FILE);
12853
+ }
12828
12854
  function readMarker() {
12829
12855
  try {
12830
- if (!(0, import_node_fs6.existsSync)(PLUGIN_MARKER_FILE)) return null;
12831
- const raw = JSON.parse((0, import_node_fs6.readFileSync)(PLUGIN_MARKER_FILE, "utf-8"));
12856
+ if (!(0, import_node_fs6.existsSync)(markerPath())) return null;
12857
+ const raw = JSON.parse((0, import_node_fs6.readFileSync)(markerPath(), "utf-8"));
12832
12858
  const pluginRoot = typeof raw.plugin_root === "string" ? raw.plugin_root : "";
12833
- if (!pluginRoot) return null;
12859
+ if (!pluginRoot || CONTROL_CHARS.test(pluginRoot)) return null;
12834
12860
  return {
12835
12861
  session_id: typeof raw.session_id === "string" ? raw.session_id : null,
12836
12862
  plugin_root: pluginRoot,
@@ -12845,16 +12871,99 @@ function recordPluginOwnership(sessionId) {
12845
12871
  const pluginRoot = process.env.VERITY_PLUGIN_ROOT;
12846
12872
  if (!pluginRoot) return;
12847
12873
  try {
12848
- (0, import_node_fs6.mkdirSync)(VERITY_DIR, { recursive: true });
12874
+ (0, import_node_fs6.mkdirSync)(projectPath(VERITY_DIR), { recursive: true });
12849
12875
  const marker = {
12850
12876
  session_id: sessionId,
12851
12877
  plugin_root: pluginRoot,
12852
12878
  version: process.env.VERITY_PLUGIN_VERSION || null,
12853
12879
  ts: Math.floor(Date.now() / 1e3)
12854
12880
  };
12855
- (0, import_node_fs6.writeFileSync)(PLUGIN_MARKER_FILE, JSON.stringify(marker));
12881
+ (0, import_node_fs6.writeFileSync)(markerPath(), JSON.stringify(marker));
12882
+ } catch {
12883
+ }
12884
+ }
12885
+ function claudeConfigDir() {
12886
+ return process.env.CLAUDE_CONFIG_DIR || (0, import_node_path7.join)((0, import_node_os2.homedir)(), ".claude");
12887
+ }
12888
+ function readJsonFile(path) {
12889
+ try {
12890
+ const parsed = JSON.parse((0, import_node_fs6.readFileSync)(path, "utf-8"));
12891
+ return parsed && typeof parsed === "object" ? parsed : null;
12892
+ } catch {
12893
+ return null;
12894
+ }
12895
+ }
12896
+ function enabledPluginSetting(key) {
12897
+ const files = [
12898
+ projectPath((0, import_node_path7.join)(".claude", "settings.local.json")),
12899
+ projectPath((0, import_node_path7.join)(".claude", "settings.json")),
12900
+ (0, import_node_path7.join)(claudeConfigDir(), "settings.json")
12901
+ ];
12902
+ for (const file of files) {
12903
+ const map = readJsonFile(file)?.enabledPlugins;
12904
+ if (map && typeof map === "object" && key in map) {
12905
+ return map[key] !== false;
12906
+ }
12907
+ }
12908
+ return void 0;
12909
+ }
12910
+ function marketplaceLocations() {
12911
+ const out = /* @__PURE__ */ new Map();
12912
+ const known = readJsonFile((0, import_node_path7.join)(claudeConfigDir(), "plugins", "known_marketplaces.json"));
12913
+ if (!known) return out;
12914
+ for (const [name, entry] of Object.entries(known)) {
12915
+ const loc2 = entry?.installLocation;
12916
+ if (typeof loc2 === "string" && loc2) out.set(name, (0, import_node_path7.resolve)(loc2));
12917
+ }
12918
+ return out;
12919
+ }
12920
+ function realpathOr(p) {
12921
+ try {
12922
+ return import_node_fs6.realpathSync.native(p);
12923
+ } catch {
12924
+ return (0, import_node_path7.resolve)(p);
12925
+ }
12926
+ }
12927
+ function isWithin(want, dir) {
12928
+ return want === dir || want.startsWith(dir + import_node_path7.sep);
12929
+ }
12930
+ function registrySays(pluginRoot) {
12931
+ const plugins = readJsonFile((0, import_node_path7.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
12932
+ if (!plugins || typeof plugins !== "object") return "unverified";
12933
+ const want = (0, import_node_path7.resolve)(pluginRoot);
12934
+ const markets = marketplaceLocations();
12935
+ for (const [key, value] of Object.entries(plugins)) {
12936
+ const at = key.lastIndexOf("@");
12937
+ const source = at > 0 ? markets.get(key.slice(at + 1)) : void 0;
12938
+ const claims = (Array.isArray(value) ? value : []).some((entry) => {
12939
+ const installPath = entry?.installPath;
12940
+ return typeof installPath === "string" && (0, import_node_path7.resolve)(installPath) === want;
12941
+ }) || source !== void 0 && isWithin(want, source);
12942
+ if (claims) {
12943
+ if (enabledPluginSetting(key) === false) return "gone";
12944
+ return (0, import_node_fs6.existsSync)(pluginRoot) ? "live" : "gone";
12945
+ }
12946
+ }
12947
+ const managed = (0, import_node_path7.resolve)((0, import_node_path7.join)(claudeConfigDir(), "plugins", "cache"));
12948
+ return want === managed || want.startsWith(managed + import_node_path7.sep) ? "gone" : "unverified";
12949
+ }
12950
+ var _live = /* @__PURE__ */ new Map();
12951
+ function pluginLiveness(pluginRoot) {
12952
+ const cached2 = _live.get(pluginRoot);
12953
+ if (cached2 !== void 0) return cached2;
12954
+ const said = registrySays(pluginRoot);
12955
+ _live.set(pluginRoot, said);
12956
+ return said;
12957
+ }
12958
+ function clearStalePluginMarker() {
12959
+ const marker = readMarker();
12960
+ if (!marker || pluginLiveness(marker.plugin_root) !== "gone") return null;
12961
+ try {
12962
+ (0, import_node_fs6.rmSync)(markerPath(), { force: true });
12856
12963
  } catch {
12964
+ return null;
12857
12965
  }
12966
+ return { pluginRoot: marker.plugin_root, version: marker.version };
12858
12967
  }
12859
12968
  function shouldDeferToPlugin(sessionId) {
12860
12969
  if (isPluginInvocation()) {
@@ -12863,13 +12972,48 @@ function shouldDeferToPlugin(sessionId) {
12863
12972
  }
12864
12973
  const marker = readMarker();
12865
12974
  if (!marker) return false;
12866
- if (!(0, import_node_fs6.existsSync)(marker.plugin_root)) return false;
12975
+ const liveness = pluginLiveness(marker.plugin_root);
12976
+ if (liveness === "gone") return false;
12867
12977
  if (sessionId && marker.session_id) return sessionId === marker.session_id;
12868
- return Math.floor(Date.now() / 1e3) - marker.ts < MARKER_TTL_SECONDS;
12978
+ logToFileOnly(
12979
+ // JSON-encoded: `plugin_root` is repository-controlled, and raw it could
12980
+ // write forged lines into the one place someone looks to understand why
12981
+ // the gate did what it did. (`readMarker` also refuses control characters.)
12982
+ `plugin ownership: no session id to match against the marker for ${JSON.stringify(marker.plugin_root)} \u2014 running rather than standing down, because only an exact session match is evidence a repository cannot write.`
12983
+ );
12984
+ return false;
12869
12985
  }
12870
12986
  function pluginActiveHere() {
12987
+ return activePluginInstall() !== null;
12988
+ }
12989
+ function registeredVerityPlugin() {
12990
+ const plugins = readJsonFile((0, import_node_path7.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
12991
+ if (!plugins || typeof plugins !== "object") return null;
12992
+ const here = realpathOr(repoRoot());
12993
+ for (const [key, value] of Object.entries(plugins)) {
12994
+ const at = key.lastIndexOf("@");
12995
+ if ((at > 0 ? key.slice(0, at) : key) !== "verity") continue;
12996
+ if (enabledPluginSetting(key) === false) continue;
12997
+ for (const entry of Array.isArray(value) ? value : []) {
12998
+ const e = entry;
12999
+ const installPath = typeof e.installPath === "string" ? e.installPath : "";
13000
+ if (!installPath || !(0, import_node_fs6.existsSync)(installPath)) continue;
13001
+ const scope2 = typeof e.scope === "string" ? e.scope : "user";
13002
+ if (scope2 !== "user") {
13003
+ const forProject = typeof e.projectPath === "string" ? realpathOr(e.projectPath) : "";
13004
+ if (forProject !== here) continue;
13005
+ }
13006
+ return { pluginRoot: installPath, version: typeof e.version === "string" ? e.version : null };
13007
+ }
13008
+ }
13009
+ return null;
13010
+ }
13011
+ function activePluginInstall() {
12871
13012
  const marker = readMarker();
12872
- return !!marker && (0, import_node_fs6.existsSync)(marker.plugin_root);
13013
+ if (marker && pluginLiveness(marker.plugin_root) === "live") {
13014
+ return { pluginRoot: marker.plugin_root, version: marker.version };
13015
+ }
13016
+ return registeredVerityPlugin();
12873
13017
  }
12874
13018
  function deferredToPlugin(command, sessionId) {
12875
13019
  if (!shouldDeferToPlugin(sessionId)) return false;
@@ -12879,21 +13023,63 @@ function deferredToPlugin(command, sessionId) {
12879
13023
  return true;
12880
13024
  }
12881
13025
 
13026
+ // src/lib/hook-wiring.ts
13027
+ function carriesAnyVerityHook(s) {
13028
+ return s.stop || s.intent || s.baseline || s.compact || s.sessionEnd || s.guard;
13029
+ }
13030
+ async function resolveHookWiring() {
13031
+ const settings = await checkAllVerityHooks();
13032
+ const config = readProjectConfig();
13033
+ const gitMoments = { source: config.source, path: config.path };
13034
+ if (!pluginActiveHere()) {
13035
+ return { source: "settings", status: settings, settings, duplicateSettingsHooks: false, gitMoments };
13036
+ }
13037
+ const status = {
13038
+ stop: true,
13039
+ intent: true,
13040
+ baseline: true,
13041
+ compact: true,
13042
+ sessionEnd: true,
13043
+ guard: true,
13044
+ guardOn: config.git_moments
13045
+ };
13046
+ return {
13047
+ source: "plugin",
13048
+ status,
13049
+ settings,
13050
+ duplicateSettingsHooks: carriesAnyVerityHook(settings),
13051
+ gitMoments
13052
+ };
13053
+ }
13054
+
12882
13055
  // src/commands/hooks.ts
12883
13056
  var ALL_MOMENTS = ["stop", "pre-commit", "pre-push"];
12884
- function parseMoments2(raw) {
12885
- const seen = /* @__PURE__ */ new Set();
12886
- for (const part of raw.split(",").map((s) => s.trim()).filter(Boolean)) {
12887
- if (ALL_MOMENTS.includes(part)) seen.add(part);
13057
+ function parseMomentSelection(raw) {
13058
+ const parts = raw.split(",").map((s) => s.trim()).filter(Boolean);
13059
+ if (parts.length === 1 && parts[0].toLowerCase() === "none") return { moments: [] };
13060
+ if (parts.length === 0) {
13061
+ return { error: `--moments was empty. Pass one or more of ${ALL_MOMENTS.join(", ")} \u2014 or "none" to gate nothing.` };
13062
+ }
13063
+ const unknown = parts.filter((p) => !ALL_MOMENTS.includes(p));
13064
+ if (unknown.length > 0) {
13065
+ return {
13066
+ error: `--moments: unrecognised ${unknown.length === 1 ? "moment" : "moments"} ${unknown.map((u) => `"${u}"`).join(", ")}. Valid: ${ALL_MOMENTS.join(", ")} \u2014 or "none" to gate nothing.`
13067
+ };
12888
13068
  }
12889
- return ALL_MOMENTS.filter((m) => seen.has(m));
13069
+ const seen = new Set(parts);
13070
+ return { moments: ALL_MOMENTS.filter((m) => seen.has(m)) };
12890
13071
  }
12891
13072
  function registerHooksCommands(program2) {
12892
13073
  const hooks = program2.command("hooks").description("Manage Claude Code hook wiring");
12893
13074
  hooks.command("install").description("Install Verity hooks into Claude Code settings").option("--force", "Overwrite existing Verity hooks").option("--moments <list>", "Reconcile to exactly these moments: stop,pre-commit,pre-push").action(async (opts) => {
12894
13075
  const force = opts.force ?? false;
12895
13076
  if (opts.moments != null) {
12896
- const moments = parseMoments2(opts.moments);
13077
+ const parsed = parseMomentSelection(opts.moments);
13078
+ if ("error" in parsed) {
13079
+ printError(parsed.error);
13080
+ process.exit(1);
13081
+ }
13082
+ const moments = parsed.moments;
12897
13083
  const gitMoments = [
12898
13084
  ...moments.includes("pre-commit") ? ["commit"] : [],
12899
13085
  ...moments.includes("pre-push") ? ["push"] : []
@@ -12954,20 +13140,22 @@ function registerHooksCommands(program2) {
12954
13140
  printInfo(" SessionStart hook: verity baseline capture");
12955
13141
  });
12956
13142
  hooks.command("check").description("Check if Verity hooks are installed").action(async () => {
12957
- const status = await checkAllVerityHooks();
12958
- if (pluginActiveHere()) {
12959
- const moments = readProjectConfig().git_moments;
13143
+ const wiring = await resolveHookWiring();
13144
+ const status = wiring.status;
13145
+ if (wiring.source === "plugin") {
13146
+ const moments = status.guardOn;
13147
+ const inherited = wiring.gitMoments.source === "main-worktree" && wiring.gitMoments.path ? ` (inherited from the main worktree: ${wiring.gitMoments.path})` : "";
12960
13148
  printInfo("Wired by the Verity Claude Code plugin (not .claude/settings.json):");
12961
13149
  printInfo(" Stop hook (verity analyze): installed");
12962
13150
  printInfo(" Intent hook (verity intent capture): installed");
12963
13151
  printInfo(" Baseline hook (verity baseline capture): installed");
12964
13152
  printInfo(
12965
- ` Git-moment gate (verity guard): ${moments.length ? `installed [${moments.join(", ")}]` : "wired but gating nothing"}`
13153
+ ` Git-moment gate (verity guard): ${moments.length ? `installed [${moments.join(", ")}]${inherited}` : "wired but gating nothing"}`
12966
13154
  );
12967
13155
  if (!moments.length) {
12968
13156
  printInfo(' Enable it with "verity config git-moments commit,push".');
12969
13157
  }
12970
- if (status.stop || status.intent || status.baseline || status.guard) {
13158
+ if (wiring.duplicateSettingsHooks) {
12971
13159
  printWarn(" Duplicate hooks also exist in .claude/settings.json. They stand down at run time,");
12972
13160
  printWarn(' but remove them with "verity init --plugin-mode" so the wiring says what it does.');
12973
13161
  }
@@ -13352,8 +13540,8 @@ function isCommandOnlyTurn(input) {
13352
13540
  // src/lib/context-identity.ts
13353
13541
  var import_node_crypto2 = require("node:crypto");
13354
13542
  var import_node_fs8 = require("node:fs");
13355
- var import_node_os2 = require("node:os");
13356
- var import_node_path6 = require("node:path");
13543
+ var import_node_os3 = require("node:os");
13544
+ var import_node_path8 = require("node:path");
13357
13545
  var SHARED_SENTINELS = /* @__PURE__ */ new Set([
13358
13546
  "",
13359
13547
  "-",
@@ -13407,13 +13595,13 @@ function contextIdentity(input) {
13407
13595
  }
13408
13596
  function verityHome() {
13409
13597
  const override = process.env.VERITY_HOME;
13410
- return override && override.trim() ? (0, import_node_path6.resolve)(override) : (0, import_node_path6.join)((0, import_node_os2.homedir)(), ".verity");
13598
+ return override && override.trim() ? (0, import_node_path8.resolve)(override) : (0, import_node_path8.join)((0, import_node_os3.homedir)(), ".verity");
13411
13599
  }
13412
13600
  function dossierDir(identity) {
13413
- return (0, import_node_path6.join)(verityHome(), "sessions", identity.userKey, identity.treeKey, identity.sessionKey);
13601
+ return (0, import_node_path8.join)(verityHome(), "sessions", identity.userKey, identity.treeKey, identity.sessionKey);
13414
13602
  }
13415
13603
  function treeDir(identity) {
13416
- return (0, import_node_path6.join)(verityHome(), "sessions", identity.userKey, identity.treeKey);
13604
+ return (0, import_node_path8.join)(verityHome(), "sessions", identity.userKey, identity.treeKey);
13417
13605
  }
13418
13606
  function scopeIdentity(token, sessionId) {
13419
13607
  const t = (token ?? "").trim();
@@ -13429,7 +13617,7 @@ function sessionScopeKey(token, sessionId) {
13429
13617
  // src/lib/task-context-buffer.ts
13430
13618
  var import_promises6 = require("node:fs/promises");
13431
13619
  var import_node_fs9 = require("node:fs");
13432
- var import_node_path7 = require("node:path");
13620
+ var import_node_path9 = require("node:path");
13433
13621
  var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
13434
13622
  var MAX_BUFFER_BYTES = 500 * 1024;
13435
13623
  var MAX_PROMPT_CHARS = 2e3;
@@ -13507,7 +13695,7 @@ async function cleanupTaskContextBuffers() {
13507
13695
  const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
13508
13696
  for (const file of files) {
13509
13697
  if (!file.endsWith(".jsonl")) continue;
13510
- const filePath = (0, import_node_path7.join)(TASK_CONTEXT_DIR, file);
13698
+ const filePath = (0, import_node_path9.join)(TASK_CONTEXT_DIR, file);
13511
13699
  try {
13512
13700
  const stats = await (0, import_promises6.stat)(filePath);
13513
13701
  if (stats.mtimeMs < cutoffMs) {
@@ -13521,7 +13709,7 @@ async function cleanupTaskContextBuffers() {
13521
13709
  }
13522
13710
  function bufferPath(taskId) {
13523
13711
  const safe = taskId.replace(/[^a-zA-Z0-9_-]/g, "");
13524
- return (0, import_node_path7.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
13712
+ return (0, import_node_path9.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
13525
13713
  }
13526
13714
  async function appendEntry(taskId, entry) {
13527
13715
  try {
@@ -13547,7 +13735,7 @@ async function appendEntry(taskId, entry) {
13547
13735
  // src/lib/memory-retrieval.ts
13548
13736
  var import_promises7 = require("node:fs/promises");
13549
13737
  var import_node_fs10 = require("node:fs");
13550
- var import_node_path8 = require("node:path");
13738
+ var import_node_path10 = require("node:path");
13551
13739
  var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
13552
13740
  var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
13553
13741
  var DEFAULT_BUDGET_TOKENS = 2e3;
@@ -13645,14 +13833,14 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
13645
13833
  const promptTokens = tokenize(promptText);
13646
13834
  const nodes = [];
13647
13835
  for (const domain of DOMAINS) {
13648
- const domainDir = (0, import_node_path8.join)(memoryDir(), domain);
13836
+ const domainDir = (0, import_node_path10.join)(memoryDir(), domain);
13649
13837
  if (!(0, import_node_fs10.existsSync)(domainDir)) continue;
13650
13838
  try {
13651
13839
  const files = await (0, import_promises7.readdir)(domainDir);
13652
13840
  for (const file of files) {
13653
13841
  if (!file.endsWith(".md")) continue;
13654
13842
  try {
13655
- const content = await (0, import_promises7.readFile)((0, import_node_path8.join)(domainDir, file), "utf-8");
13843
+ const content = await (0, import_promises7.readFile)((0, import_node_path10.join)(domainDir, file), "utf-8");
13656
13844
  const { fm, body } = parseFrontmatter(content);
13657
13845
  if (fm.status && fm.status !== "active") continue;
13658
13846
  nodes.push({
@@ -13712,26 +13900,26 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
13712
13900
  // src/lib/memory-sync.ts
13713
13901
  var import_promises8 = require("node:fs/promises");
13714
13902
  var import_node_fs12 = require("node:fs");
13715
- var import_node_path10 = require("node:path");
13903
+ var import_node_path12 = require("node:path");
13716
13904
  var import_node_crypto3 = require("node:crypto");
13717
13905
 
13718
13906
  // src/lib/safe-path.ts
13719
13907
  var import_node_fs11 = require("node:fs");
13720
- var import_node_path9 = require("node:path");
13908
+ var import_node_path11 = require("node:path");
13721
13909
  function resolveInside(baseDir, candidate) {
13722
13910
  if (typeof candidate !== "string" || candidate.length === 0) return null;
13723
- if ((0, import_node_path9.isAbsolute)(candidate)) return null;
13724
- const baseAbs = (0, import_node_path9.resolve)(baseDir);
13725
- const full = (0, import_node_path9.resolve)(baseAbs, candidate);
13726
- const baseSep = baseAbs.endsWith(import_node_path9.sep) ? baseAbs : baseAbs + import_node_path9.sep;
13911
+ if ((0, import_node_path11.isAbsolute)(candidate)) return null;
13912
+ const baseAbs = (0, import_node_path11.resolve)(baseDir);
13913
+ const full = (0, import_node_path11.resolve)(baseAbs, candidate);
13914
+ const baseSep = baseAbs.endsWith(import_node_path11.sep) ? baseAbs : baseAbs + import_node_path11.sep;
13727
13915
  if (full !== baseAbs && !full.startsWith(baseSep)) return null;
13728
13916
  try {
13729
13917
  if ((0, import_node_fs11.existsSync)(baseAbs)) {
13730
13918
  const realBase = (0, import_node_fs11.realpathSync)(baseAbs);
13731
- const realBaseSep = realBase.endsWith(import_node_path9.sep) ? realBase : realBase + import_node_path9.sep;
13919
+ const realBaseSep = realBase.endsWith(import_node_path11.sep) ? realBase : realBase + import_node_path11.sep;
13732
13920
  let probe = full;
13733
13921
  while (!(0, import_node_fs11.existsSync)(probe)) {
13734
- const parent = (0, import_node_path9.dirname)(probe);
13922
+ const parent = (0, import_node_path11.dirname)(probe);
13735
13923
  if (parent === probe) break;
13736
13924
  probe = parent;
13737
13925
  }
@@ -13812,16 +14000,16 @@ var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
13812
14000
  async function ensureMemoryDir() {
13813
14001
  await (0, import_promises8.mkdir)(memoryDir2(), { recursive: true });
13814
14002
  for (const domain of DOMAINS2) {
13815
- await (0, import_promises8.mkdir)((0, import_node_path10.join)(memoryDir2(), domain), { recursive: true });
14003
+ await (0, import_promises8.mkdir)((0, import_node_path12.join)(memoryDir2(), domain), { recursive: true });
13816
14004
  }
13817
- if (!(0, import_node_fs12.existsSync)((0, import_node_path10.join)(memoryDir2(), "SCHEMA.md"))) {
13818
- await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
14005
+ if (!(0, import_node_fs12.existsSync)((0, import_node_path12.join)(memoryDir2(), "SCHEMA.md"))) {
14006
+ await (0, import_promises8.writeFile)((0, import_node_path12.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
13819
14007
  }
13820
- if (!(0, import_node_fs12.existsSync)((0, import_node_path10.join)(memoryDir2(), "index.md"))) {
13821
- await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
14008
+ if (!(0, import_node_fs12.existsSync)((0, import_node_path12.join)(memoryDir2(), "index.md"))) {
14009
+ await (0, import_promises8.writeFile)((0, import_node_path12.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
13822
14010
  }
13823
- if (!(0, import_node_fs12.existsSync)((0, import_node_path10.join)(memoryDir2(), "log.md"))) {
13824
- await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
14011
+ if (!(0, import_node_fs12.existsSync)((0, import_node_path12.join)(memoryDir2(), "log.md"))) {
14012
+ await (0, import_promises8.writeFile)((0, import_node_path12.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
13825
14013
  }
13826
14014
  }
13827
14015
  async function buildManifest() {
@@ -13830,14 +14018,14 @@ async function buildManifest() {
13830
14018
  }
13831
14019
  const nodes = [];
13832
14020
  for (const domain of DOMAINS2) {
13833
- const domainDir = (0, import_node_path10.join)(memoryDir2(), domain);
14021
+ const domainDir = (0, import_node_path12.join)(memoryDir2(), domain);
13834
14022
  if (!(0, import_node_fs12.existsSync)(domainDir)) continue;
13835
14023
  try {
13836
14024
  const files = await (0, import_promises8.readdir)(domainDir);
13837
14025
  for (const file of files) {
13838
14026
  if (!file.endsWith(".md")) continue;
13839
14027
  const filePath = `${domain}/${file}`;
13840
- const fullPath = (0, import_node_path10.join)(memoryDir2(), filePath);
14028
+ const fullPath = (0, import_node_path12.join)(memoryDir2(), filePath);
13841
14029
  try {
13842
14030
  const content = await (0, import_promises8.readFile)(fullPath, "utf-8");
13843
14031
  const hash = (0, import_node_crypto3.createHash)("sha256").update(content).digest("hex").slice(0, 16);
@@ -13850,13 +14038,13 @@ async function buildManifest() {
13850
14038
  }
13851
14039
  let indexHash = null;
13852
14040
  try {
13853
- const indexContent = await (0, import_promises8.readFile)((0, import_node_path10.join)(memoryDir2(), "index.md"), "utf-8");
14041
+ const indexContent = await (0, import_promises8.readFile)((0, import_node_path12.join)(memoryDir2(), "index.md"), "utf-8");
13854
14042
  indexHash = `sha256:${(0, import_node_crypto3.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
13855
14043
  } catch {
13856
14044
  }
13857
14045
  let logLength = 0;
13858
14046
  try {
13859
- const logContent = await (0, import_promises8.readFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), "utf-8");
14047
+ const logContent = await (0, import_promises8.readFile)((0, import_node_path12.join)(memoryDir2(), "log.md"), "utf-8");
13860
14048
  logLength = logContent.split("\n").length;
13861
14049
  } catch {
13862
14050
  }
@@ -13869,13 +14057,13 @@ async function readOnDiskNodes() {
13869
14057
  const out = /* @__PURE__ */ new Map();
13870
14058
  if (!(0, import_node_fs12.existsSync)(memoryDir2())) return out;
13871
14059
  for (const domain of DOMAINS2) {
13872
- const domainDir = (0, import_node_path10.join)(memoryDir2(), domain);
14060
+ const domainDir = (0, import_node_path12.join)(memoryDir2(), domain);
13873
14061
  if (!(0, import_node_fs12.existsSync)(domainDir)) continue;
13874
14062
  try {
13875
14063
  for (const file of await (0, import_promises8.readdir)(domainDir)) {
13876
14064
  if (!file.endsWith(".md")) continue;
13877
14065
  try {
13878
- out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path10.join)(domainDir, file), "utf-8")));
14066
+ out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path12.join)(domainDir, file), "utf-8")));
13879
14067
  } catch {
13880
14068
  }
13881
14069
  }
@@ -13921,7 +14109,7 @@ async function computeEditedNodeUploads() {
13921
14109
  const uploads = [];
13922
14110
  for (const [path, prevHash] of prev) {
13923
14111
  if (prevHash == null) continue;
13924
- const full = (0, import_node_path10.join)(memoryDir2(), path);
14112
+ const full = (0, import_node_path12.join)(memoryDir2(), path);
13925
14113
  if (!(0, import_node_fs12.existsSync)(full)) continue;
13926
14114
  let content;
13927
14115
  try {
@@ -13958,8 +14146,8 @@ async function applyMemoryWrites(writes, opts = {}) {
13958
14146
  const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
13959
14147
  for (const n of notes) logLines.push(` - ${n}`);
13960
14148
  try {
13961
- const existing = (0, import_node_fs12.existsSync)((0, import_node_path10.join)(memoryDir2(), "log.md")) ? await (0, import_promises8.readFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
13962
- await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
14149
+ const existing = (0, import_node_fs12.existsSync)((0, import_node_path12.join)(memoryDir2(), "log.md")) ? await (0, import_promises8.readFile)((0, import_node_path12.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
14150
+ await (0, import_promises8.writeFile)((0, import_node_path12.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
13963
14151
  } catch {
13964
14152
  }
13965
14153
  await recordSyncedNodePaths();
@@ -13991,7 +14179,7 @@ async function applyOneWrite(write, treePaths) {
13991
14179
  return { written: false, notes };
13992
14180
  }
13993
14181
  }
13994
- await (0, import_promises8.mkdir)((0, import_node_path10.dirname)(fullPath), { recursive: true });
14182
+ await (0, import_promises8.mkdir)((0, import_node_path12.dirname)(fullPath), { recursive: true });
13995
14183
  await (0, import_promises8.writeFile)(fullPath, content);
13996
14184
  return { written: true, notes };
13997
14185
  }
@@ -14032,7 +14220,7 @@ async function regenerateIndex() {
14032
14220
  ];
14033
14221
  let totalNodes = 0;
14034
14222
  for (const domain of DOMAINS2.filter((d) => d !== "_archive")) {
14035
- const domainDir = (0, import_node_path10.join)(memoryDir2(), domain);
14223
+ const domainDir = (0, import_node_path12.join)(memoryDir2(), domain);
14036
14224
  if (!(0, import_node_fs12.existsSync)(domainDir)) continue;
14037
14225
  try {
14038
14226
  const files = await (0, import_promises8.readdir)(domainDir);
@@ -14043,7 +14231,7 @@ async function regenerateIndex() {
14043
14231
  for (const file of mdFiles.sort()) {
14044
14232
  const slug = file.replace(/\.md$/, "");
14045
14233
  try {
14046
- const content = await (0, import_promises8.readFile)((0, import_node_path10.join)(domainDir, file), "utf-8");
14234
+ const content = await (0, import_promises8.readFile)((0, import_node_path12.join)(domainDir, file), "utf-8");
14047
14235
  const title = pickFrontmatter(content, "title") ?? slug;
14048
14236
  const kind = pickFrontmatter(content, "kind") ?? "-";
14049
14237
  const confidence = pickFrontmatter(content, "confidence");
@@ -14067,7 +14255,7 @@ async function regenerateIndex() {
14067
14255
  lines.push("No nodes yet. Run an analysis to start building the knowledge graph.");
14068
14256
  }
14069
14257
  const next = lines.join("\n") + "\n";
14070
- const indexPath = (0, import_node_path10.join)(memoryDir2(), "index.md");
14258
+ const indexPath = (0, import_node_path12.join)(memoryDir2(), "index.md");
14071
14259
  let existing = null;
14072
14260
  try {
14073
14261
  existing = await (0, import_promises8.readFile)(indexPath, "utf-8");
@@ -14301,7 +14489,7 @@ function hasLegacyMemoryBlock(text) {
14301
14489
  return findMarker(text, LEGACY_MD_START) !== -1;
14302
14490
  }
14303
14491
  async function ensureClaudeMdPointer(cwd = repoRoot()) {
14304
- const claudeMdPath = (0, import_node_path10.join)(cwd, "CLAUDE.md");
14492
+ const claudeMdPath = (0, import_node_path12.join)(cwd, "CLAUDE.md");
14305
14493
  let existing = "";
14306
14494
  if ((0, import_node_fs12.existsSync)(claudeMdPath)) {
14307
14495
  existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
@@ -14441,7 +14629,7 @@ Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
14441
14629
  // src/lib/dossier-session.ts
14442
14630
  var import_node_fs17 = require("node:fs");
14443
14631
  var import_node_crypto7 = require("node:crypto");
14444
- var import_node_path13 = require("node:path");
14632
+ var import_node_path15 = require("node:path");
14445
14633
 
14446
14634
  // src/lib/pending-repeat.ts
14447
14635
  var STOP = /* @__PURE__ */ new Set([
@@ -14547,7 +14735,7 @@ function statementAnchorKey(file, patternId) {
14547
14735
  // src/lib/dossier/log.ts
14548
14736
  var import_node_crypto4 = require("node:crypto");
14549
14737
  var import_node_fs13 = require("node:fs");
14550
- var import_node_path11 = require("node:path");
14738
+ var import_node_path13 = require("node:path");
14551
14739
  var CRC_TABLE = (() => {
14552
14740
  const t = new Int32Array(256);
14553
14741
  for (let i = 0; i < 256; i++) {
@@ -14570,9 +14758,9 @@ function openDossier(identity) {
14570
14758
  return {
14571
14759
  dir,
14572
14760
  identity,
14573
- eventsPath: (0, import_node_path11.join)(dir, "events.jsonl"),
14574
- foldPath: (0, import_node_path11.join)(dir, "fold.json"),
14575
- rotatedDir: (0, import_node_path11.join)(dir, "rotated")
14761
+ eventsPath: (0, import_node_path13.join)(dir, "events.jsonl"),
14762
+ foldPath: (0, import_node_path13.join)(dir, "fold.json"),
14763
+ rotatedDir: (0, import_node_path13.join)(dir, "rotated")
14576
14764
  };
14577
14765
  } catch {
14578
14766
  return null;
@@ -14645,11 +14833,11 @@ function rotateIfNeeded2(d) {
14645
14833
  if (!(0, import_node_fs13.existsSync)(d.eventsPath)) return;
14646
14834
  if ((0, import_node_fs13.statSync)(d.eventsPath).size < ROTATE_BYTES) return;
14647
14835
  (0, import_node_fs13.mkdirSync)(d.rotatedDir, { recursive: true, mode: 448 });
14648
- (0, import_node_fs13.renameSync)(d.eventsPath, (0, import_node_path11.join)(d.rotatedDir, `events.${Date.now()}.jsonl`));
14836
+ (0, import_node_fs13.renameSync)(d.eventsPath, (0, import_node_path13.join)(d.rotatedDir, `events.${Date.now()}.jsonl`));
14649
14837
  const kept = (0, import_node_fs13.readdirSync)(d.rotatedDir).filter((f) => f.endsWith(".jsonl")).sort();
14650
14838
  for (const stale of kept.slice(0, Math.max(0, kept.length - ROTATE_KEEP))) {
14651
14839
  try {
14652
- (0, import_node_fs13.renameSync)((0, import_node_path11.join)(d.rotatedDir, stale), (0, import_node_path11.join)(d.rotatedDir, `${stale}.pruned`));
14840
+ (0, import_node_fs13.renameSync)((0, import_node_path13.join)(d.rotatedDir, stale), (0, import_node_path13.join)(d.rotatedDir, `${stale}.pruned`));
14653
14841
  } catch {
14654
14842
  }
14655
14843
  }
@@ -14660,7 +14848,7 @@ function rotateIfNeeded2(d) {
14660
14848
  // src/lib/dossier/fold-dossier.ts
14661
14849
  var import_node_crypto5 = require("node:crypto");
14662
14850
  var import_node_fs14 = require("node:fs");
14663
- var import_node_path12 = require("node:path");
14851
+ var import_node_path14 = require("node:path");
14664
14852
  var EMPTY_CAPABILITIES = () => ({
14665
14853
  human_reachable: { value: "unknown", tier: "unknown" },
14666
14854
  authorship_observability: { value: "unknown", tier: "unknown" },
@@ -14716,7 +14904,7 @@ function foldDossier(d, opts = {}) {
14716
14904
  state.meta.rotations = files.length;
14717
14905
  for (const f of files) {
14718
14906
  try {
14719
- ingest((0, import_node_fs14.readFileSync)((0, import_node_path12.join)(d.rotatedDir, f), "utf8"));
14907
+ ingest((0, import_node_fs14.readFileSync)((0, import_node_path14.join)(d.rotatedDir, f), "utf8"));
14720
14908
  } catch {
14721
14909
  state.meta.dropped_lines++;
14722
14910
  }
@@ -15573,16 +15761,16 @@ function foreignAuthoredPaths(identity, opts = {}) {
15573
15761
  for (const entry of (0, import_node_fs17.readdirSync)(dir, { withFileTypes: true })) {
15574
15762
  if (!entry.isDirectory()) continue;
15575
15763
  if (entry.name === identity.sessionKey) continue;
15576
- const log = (0, import_node_path13.join)(dir, entry.name, "events.jsonl");
15764
+ const log = (0, import_node_path15.join)(dir, entry.name, "events.jsonl");
15577
15765
  try {
15578
15766
  if (!(0, import_node_fs17.existsSync)(log)) continue;
15579
15767
  if (now - (0, import_node_fs17.statSync)(log).mtimeMs > windowMs) continue;
15580
15768
  const sib = {
15581
- dir: (0, import_node_path13.join)(dir, entry.name),
15769
+ dir: (0, import_node_path15.join)(dir, entry.name),
15582
15770
  identity,
15583
15771
  eventsPath: log,
15584
- foldPath: (0, import_node_path13.join)(dir, entry.name, "fold.json"),
15585
- rotatedDir: (0, import_node_path13.join)(dir, entry.name, "rotated")
15772
+ foldPath: (0, import_node_path15.join)(dir, entry.name, "fold.json"),
15773
+ rotatedDir: (0, import_node_path15.join)(dir, entry.name, "rotated")
15586
15774
  };
15587
15775
  const state = readFoldCache(sib) ?? foldDossier(sib);
15588
15776
  sessions++;
@@ -15610,22 +15798,22 @@ function pruneOldDossiers(identity, maxAgeMs = 7 * 24 * 60 * 60 * 1e3) {
15610
15798
  let removed = 0;
15611
15799
  try {
15612
15800
  const mine = dossierDir(identity);
15613
- const userDir = (0, import_node_path13.dirname)((0, import_node_path13.dirname)(mine));
15801
+ const userDir = (0, import_node_path15.dirname)((0, import_node_path15.dirname)(mine));
15614
15802
  if (!(0, import_node_fs17.existsSync)(userDir)) return 0;
15615
15803
  const cutoff = Date.now() - maxAgeMs;
15616
15804
  for (const tree of (0, import_node_fs17.readdirSync)(userDir, { withFileTypes: true })) {
15617
15805
  if (!tree.isDirectory()) continue;
15618
- const treePath = (0, import_node_path13.join)(userDir, tree.name);
15806
+ const treePath = (0, import_node_path15.join)(userDir, tree.name);
15619
15807
  let live = 0;
15620
15808
  for (const entry of (0, import_node_fs17.readdirSync)(treePath, { withFileTypes: true })) {
15621
15809
  if (!entry.isDirectory()) continue;
15622
- const dir = (0, import_node_path13.join)(treePath, entry.name);
15810
+ const dir = (0, import_node_path15.join)(treePath, entry.name);
15623
15811
  if (dir === mine) {
15624
15812
  live++;
15625
15813
  continue;
15626
15814
  }
15627
15815
  try {
15628
- const log = (0, import_node_path13.join)(dir, "events.jsonl");
15816
+ const log = (0, import_node_path15.join)(dir, "events.jsonl");
15629
15817
  const at = (0, import_node_fs17.existsSync)(log) ? (0, import_node_fs17.statSync)(log).mtimeMs : (0, import_node_fs17.statSync)(dir).mtimeMs;
15630
15818
  if (at < cutoff) {
15631
15819
  (0, import_node_fs17.rmSync)(dir, { recursive: true, force: true });
@@ -15680,7 +15868,7 @@ function recordTurn(d, t) {
15680
15868
  for (const a of t.authored) {
15681
15869
  const origin = a.owner === "subagent" ? "subagent" : "edit_tool";
15682
15870
  const prior = t.known?.authored?.get(a.p);
15683
- const hash = fileHash((0, import_node_path13.join)(root, a.p));
15871
+ const hash = fileHash((0, import_node_path15.join)(root, a.p));
15684
15872
  const hunks = Math.max(0, a.h - (prior?.hunks ?? 0));
15685
15873
  const adds = Math.max(0, a.a - (prior?.adds ?? 0));
15686
15874
  const dels = Math.max(0, a.d - (prior?.dels ?? 0));
@@ -15713,7 +15901,7 @@ function recordTurn(d, t) {
15713
15901
  }
15714
15902
  const seenDivergence = t.known?.divergence ?? /* @__PURE__ */ new Set();
15715
15903
  for (const u of t.unobserved) {
15716
- const hash = fileHash((0, import_node_path13.join)(root, u.p));
15904
+ const hash = fileHash((0, import_node_path15.join)(root, u.p));
15717
15905
  if (seenDivergence.has(divergenceKey(u.p, hash))) continue;
15718
15906
  appendEvent(d, { k: "divergence", kind: "external_mutation", path: u.p, hash });
15719
15907
  }
@@ -15748,7 +15936,7 @@ function recordVerdict(d, v) {
15748
15936
  if (!sent.has(f.file)) continue;
15749
15937
  if (!lines.has(f.file)) {
15750
15938
  try {
15751
- const abs = (0, import_node_path13.join)(root, f.file);
15939
+ const abs = (0, import_node_path15.join)(root, f.file);
15752
15940
  lines.set(f.file, (0, import_node_fs17.existsSync)(abs) ? (0, import_node_fs17.readFileSync)(abs, "utf8").split("\n") : null);
15753
15941
  } catch {
15754
15942
  lines.set(f.file, null);
@@ -15849,7 +16037,7 @@ function recallMemory(d, identity, opts) {
15849
16037
  budgetBytes: opts.budgetBytes,
15850
16038
  readFileLines: (file) => {
15851
16039
  try {
15852
- const abs = (0, import_node_path13.join)(root, file);
16040
+ const abs = (0, import_node_path15.join)(root, file);
15853
16041
  return (0, import_node_fs17.existsSync)(abs) ? (0, import_node_fs17.readFileSync)(abs, "utf8").split("\n") : null;
15854
16042
  } catch {
15855
16043
  return null;
@@ -15995,21 +16183,21 @@ async function fireClassify(prompt, sessionId, globals) {
15995
16183
 
15996
16184
  // src/commands/lifecycle.ts
15997
16185
  var import_node_fs21 = require("node:fs");
15998
- var import_node_path17 = require("node:path");
16186
+ var import_node_path19 = require("node:path");
15999
16187
 
16000
16188
  // src/lib/baseline.ts
16001
16189
  var import_node_fs20 = require("node:fs");
16002
- var import_node_path16 = require("node:path");
16190
+ var import_node_path18 = require("node:path");
16003
16191
  var import_node_crypto9 = require("node:crypto");
16004
16192
 
16005
16193
  // src/lib/snapshot.ts
16006
16194
  var import_node_fs19 = require("node:fs");
16007
- var import_node_path15 = require("node:path");
16195
+ var import_node_path17 = require("node:path");
16008
16196
  var import_node_child_process6 = require("node:child_process");
16009
16197
 
16010
16198
  // src/lib/files.ts
16011
16199
  var import_node_fs18 = require("node:fs");
16012
- var import_node_path14 = require("node:path");
16200
+ var import_node_path16 = require("node:path");
16013
16201
  var LANG_MAP = {
16014
16202
  // Analyzable (static analysis + Gemini)
16015
16203
  ts: "typescript",
@@ -16077,7 +16265,7 @@ var LANG_MAP = {
16077
16265
  mk: "make"
16078
16266
  };
16079
16267
  function detectLanguage(filepath) {
16080
- const ext = (0, import_node_path14.extname)(filepath).slice(1);
16268
+ const ext = (0, import_node_path16.extname)(filepath).slice(1);
16081
16269
  return LANG_MAP[ext] ?? ext;
16082
16270
  }
16083
16271
  function sortByMtime(files) {
@@ -16180,7 +16368,7 @@ function generateSnapshotDiffs(files) {
16180
16368
  const diffs = [];
16181
16369
  for (const file of files) {
16182
16370
  if (!resolveInside(SNAPSHOT_DIR, file.path)) continue;
16183
- const snapshotPath = (0, import_node_path15.join)(SNAPSHOT_DIR, file.path);
16371
+ const snapshotPath = (0, import_node_path17.join)(SNAPSHOT_DIR, file.path);
16184
16372
  const language = file.language ?? detectLanguage(file.path);
16185
16373
  if ((0, import_node_fs19.existsSync)(snapshotPath)) {
16186
16374
  const oldContent = (0, import_node_fs19.readFileSync)(snapshotPath, "utf-8");
@@ -16208,16 +16396,16 @@ function saveSnapshots(files) {
16208
16396
  const snapshotPaths = /* @__PURE__ */ new Set();
16209
16397
  for (const file of files) {
16210
16398
  if (!resolveInside(SNAPSHOT_DIR, file.path)) continue;
16211
- const snapshotPath = (0, import_node_path15.join)(SNAPSHOT_DIR, file.path);
16399
+ const snapshotPath = (0, import_node_path17.join)(SNAPSHOT_DIR, file.path);
16212
16400
  snapshotPaths.add(snapshotPath);
16213
- (0, import_node_fs19.mkdirSync)((0, import_node_path15.dirname)(snapshotPath), { recursive: true });
16401
+ (0, import_node_fs19.mkdirSync)((0, import_node_path17.dirname)(snapshotPath), { recursive: true });
16214
16402
  (0, import_node_fs19.writeFileSync)(snapshotPath, file.content);
16215
16403
  }
16216
16404
  cleanStaleSnapshots(SNAPSHOT_DIR, snapshotPaths);
16217
16405
  }
16218
16406
  function computeDiff(oldContent, newContent, filePath) {
16219
- const tmpOld = (0, import_node_path15.join)(SNAPSHOT_DIR, ".diff-old.tmp");
16220
- const tmpNew = (0, import_node_path15.join)(SNAPSHOT_DIR, ".diff-new.tmp");
16407
+ const tmpOld = (0, import_node_path17.join)(SNAPSHOT_DIR, ".diff-old.tmp");
16408
+ const tmpNew = (0, import_node_path17.join)(SNAPSHOT_DIR, ".diff-new.tmp");
16221
16409
  try {
16222
16410
  (0, import_node_fs19.mkdirSync)(SNAPSHOT_DIR, { recursive: true });
16223
16411
  (0, import_node_fs19.writeFileSync)(tmpOld, oldContent);
@@ -16250,7 +16438,7 @@ function cleanStaleSnapshots(dir, keepSet) {
16250
16438
  const entries = (0, import_node_fs19.readdirSync)(dir, { withFileTypes: true });
16251
16439
  for (const entry of entries) {
16252
16440
  if (dir === SNAPSHOT_DIR && (entry.name === ".diff-old.tmp" || entry.name === ".diff-new.tmp")) continue;
16253
- const fullPath = (0, import_node_path15.join)(dir, entry.name);
16441
+ const fullPath = (0, import_node_path17.join)(dir, entry.name);
16254
16442
  if (entry.isDirectory()) {
16255
16443
  cleanStaleSnapshots(fullPath, keepSet);
16256
16444
  try {
@@ -16279,13 +16467,13 @@ function sessionKey(sessionId) {
16279
16467
  return (0, import_node_crypto9.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
16280
16468
  }
16281
16469
  function sessionDir(key) {
16282
- return (0, import_node_path16.join)(projectPath(BASELINE_DIR), key);
16470
+ return (0, import_node_path18.join)(projectPath(BASELINE_DIR), key);
16283
16471
  }
16284
16472
  function manifestPath(dir) {
16285
- return (0, import_node_path16.join)(dir, "manifest.json");
16473
+ return (0, import_node_path18.join)(dir, "manifest.json");
16286
16474
  }
16287
16475
  function mirrorPath(dir, repoRelPath) {
16288
- return (0, import_node_path16.join)(dir, "files", repoRelPath);
16476
+ return (0, import_node_path18.join)(dir, "files", repoRelPath);
16289
16477
  }
16290
16478
  var CARRY_FILE = `${BASELINE_DIR}/.carry`;
16291
16479
  var CARRY_WINDOW_MS = 12e4;
@@ -16347,7 +16535,7 @@ function captureBaseline(opts = {}) {
16347
16535
  (0, import_node_fs20.rmSync)(dir, { recursive: true, force: true });
16348
16536
  } catch {
16349
16537
  }
16350
- const filesDir = (0, import_node_path16.join)(dir, "files");
16538
+ const filesDir = (0, import_node_path18.join)(dir, "files");
16351
16539
  const mirrored = [];
16352
16540
  try {
16353
16541
  (0, import_node_fs20.mkdirSync)(filesDir, { recursive: true });
@@ -16357,7 +16545,7 @@ function captureBaseline(opts = {}) {
16357
16545
  if (content === null) continue;
16358
16546
  const dest = mirrorPath(dir, p);
16359
16547
  try {
16360
- (0, import_node_fs20.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
16548
+ (0, import_node_fs20.mkdirSync)((0, import_node_path18.dirname)(dest), { recursive: true });
16361
16549
  (0, import_node_fs20.writeFileSync)(dest, content);
16362
16550
  mirrored.push(p);
16363
16551
  } catch {
@@ -16465,7 +16653,7 @@ function absorbIntoBaseline(paths, sessionId) {
16465
16653
  const content = safeReadForMirror(projectPath(p));
16466
16654
  if (content === null) continue;
16467
16655
  const dest = mirrorPath(dir, p);
16468
- (0, import_node_fs20.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
16656
+ (0, import_node_fs20.mkdirSync)((0, import_node_path18.dirname)(dest), { recursive: true });
16469
16657
  (0, import_node_fs20.writeFileSync)(dest, content);
16470
16658
  dirty.add(p);
16471
16659
  adopted++;
@@ -16513,7 +16701,7 @@ function pruneOldBaselines() {
16513
16701
  }
16514
16702
  const now = Date.now();
16515
16703
  for (const name of entries) {
16516
- const dir = (0, import_node_path16.join)(root, name);
16704
+ const dir = (0, import_node_path18.join)(root, name);
16517
16705
  const manifest = readManifest(dir);
16518
16706
  if (!manifest) {
16519
16707
  try {
@@ -16701,7 +16889,7 @@ function buildCompactionContext(session) {
16701
16889
  commitsSince: commitsSincePaths(watermark, (state.authored ?? []).map((a) => a.path)),
16702
16890
  readFileLines: (file) => {
16703
16891
  try {
16704
- const abs = (0, import_node_path17.join)(root, file);
16892
+ const abs = (0, import_node_path19.join)(root, file);
16705
16893
  return (0, import_node_fs21.existsSync)(abs) ? (0, import_node_fs21.readFileSync)(abs, "utf8").split("\n") : null;
16706
16894
  } catch {
16707
16895
  return null;
@@ -16738,17 +16926,17 @@ async function readHookStdin() {
16738
16926
  try {
16739
16927
  if (process.stdin.isTTY) return {};
16740
16928
  const chunks = [];
16741
- const timeout = new Promise((resolve4) => setTimeout(() => resolve4({}), 500));
16742
- const read = new Promise((resolve4) => {
16929
+ const timeout = new Promise((resolve5) => setTimeout(() => resolve5({}), 500));
16930
+ const read = new Promise((resolve5) => {
16743
16931
  process.stdin.on("data", (c) => chunks.push(c));
16744
16932
  process.stdin.on("end", () => {
16745
16933
  try {
16746
- resolve4(JSON.parse(Buffer.concat(chunks).toString("utf-8").trim() || "{}"));
16934
+ resolve5(JSON.parse(Buffer.concat(chunks).toString("utf-8").trim() || "{}"));
16747
16935
  } catch {
16748
- resolve4({});
16936
+ resolve5({});
16749
16937
  }
16750
16938
  });
16751
- process.stdin.on("error", () => resolve4({}));
16939
+ process.stdin.on("error", () => resolve5({}));
16752
16940
  process.stdin.resume();
16753
16941
  });
16754
16942
  return await Promise.race([read, timeout]);
@@ -16766,17 +16954,17 @@ var import_yaml3 = __toESM(require_dist());
16766
16954
  var import_node_child_process8 = require("node:child_process");
16767
16955
  var import_node_fs24 = require("node:fs");
16768
16956
  var import_promises9 = require("node:fs/promises");
16769
- var import_node_path20 = require("node:path");
16957
+ var import_node_path22 = require("node:path");
16770
16958
  var import_yaml = __toESM(require_dist());
16771
16959
 
16772
16960
  // src/lib/data-dir.ts
16773
16961
  var import_node_fs22 = require("node:fs");
16774
- var import_node_path18 = require("node:path");
16962
+ var import_node_path20 = require("node:path");
16775
16963
  function resolveDataDir() {
16776
- const candidates = [
16777
- (0, import_node_path18.join)(__dirname, "..", "data"),
16964
+ const candidates2 = [
16965
+ (0, import_node_path20.join)(__dirname, "..", "data"),
16778
16966
  // installed: node_modules/@codacy/verity-cli/data
16779
- (0, import_node_path18.join)(__dirname, "..", "..", "data"),
16967
+ (0, import_node_path20.join)(__dirname, "..", "..", "data"),
16780
16968
  // edge case: nested resolution
16781
16969
  // THE COMMITTED SOURCE, for a source checkout that has not been built.
16782
16970
  // cli/data/skills/ is a BUILD ARTIFACT (scripts/build.js copies client/skills
@@ -16785,14 +16973,14 @@ function resolveDataDir() {
16785
16973
  // without this the synthesizer throws "Could not find Verity skill data"
16786
16974
  // for every test and every `verity` run from source. Resolved from this
16787
16975
  // module's own location, never the cwd: see the warning below.
16788
- (0, import_node_path18.join)(__dirname, "..", "..", "client"),
16976
+ (0, import_node_path20.join)(__dirname, "..", "..", "client"),
16789
16977
  // bundled: cli/bin/ → ../../client
16790
- (0, import_node_path18.join)(__dirname, "..", "..", "..", "client"),
16978
+ (0, import_node_path20.join)(__dirname, "..", "..", "..", "client"),
16791
16979
  // tsx: cli/src/lib/ → ../../../client
16792
16980
  ...process.env.VERITY_DEV_DATA_DIR ? [process.env.VERITY_DEV_DATA_DIR] : []
16793
16981
  ];
16794
- for (const candidate of candidates) {
16795
- if ((0, import_node_fs22.existsSync)((0, import_node_path18.join)(candidate, "skills"))) {
16982
+ for (const candidate of candidates2) {
16983
+ if ((0, import_node_fs22.existsSync)((0, import_node_path20.join)(candidate, "skills"))) {
16796
16984
  return candidate;
16797
16985
  }
16798
16986
  }
@@ -16801,13 +16989,13 @@ function resolveDataDir() {
16801
16989
  );
16802
16990
  }
16803
16991
  function setupDataPath(file) {
16804
- return (0, import_node_path18.join)(resolveDataDir(), "skills", "verity-setup", file);
16992
+ return (0, import_node_path20.join)(resolveDataDir(), "skills", "verity-setup", file);
16805
16993
  }
16806
16994
 
16807
16995
  // src/lib/detect.ts
16808
16996
  var import_node_child_process7 = require("node:child_process");
16809
16997
  var import_node_fs23 = require("node:fs");
16810
- var import_node_path19 = require("node:path");
16998
+ var import_node_path21 = require("node:path");
16811
16999
  var TOOLED_LANGUAGES = /* @__PURE__ */ new Set([
16812
17000
  "typescript",
16813
17001
  "javascript",
@@ -16870,18 +17058,18 @@ function walk(root) {
16870
17058
  for (const entry of entries) {
16871
17059
  if (found.length >= WALK_MAX_FILES) return;
16872
17060
  if (IGNORED_SEGMENTS.includes(entry.name)) continue;
16873
- const full = (0, import_node_path19.join)(dir, entry.name);
17061
+ const full = (0, import_node_path21.join)(dir, entry.name);
16874
17062
  if (entry.isDirectory()) visit(full, depth + 1);
16875
- else if (entry.isFile()) found.push((0, import_node_path19.relative)(root, full));
17063
+ else if (entry.isFile()) found.push((0, import_node_path21.relative)(root, full));
16876
17064
  }
16877
17065
  };
16878
17066
  visit(root, 0);
16879
17067
  return found;
16880
17068
  }
16881
17069
  function languageOf(path) {
16882
- const name = (0, import_node_path19.basename)(path);
17070
+ const name = (0, import_node_path21.basename)(path);
16883
17071
  if (/^Dockerfile(\..+)?$/i.test(name)) return "dockerfile";
16884
- if (!(0, import_node_path19.extname)(name)) return null;
17072
+ if (!(0, import_node_path21.extname)(name)) return null;
16885
17073
  const lang = detectLanguage(path);
16886
17074
  return lang || null;
16887
17075
  }
@@ -16963,12 +17151,12 @@ function declaredDependencies(root, files) {
16963
17151
  if (deps && typeof deps === "object") names2.push(...Object.keys(deps));
16964
17152
  }
16965
17153
  };
16966
- readPackageJson((0, import_node_path19.join)(root, "package.json"));
16967
- const nested = files.filter((f) => f.includes("/") && (0, import_node_path19.basename)(f) === "package.json").slice(0, NESTED_MANIFEST_LIMIT);
16968
- for (const rel of nested) readPackageJson((0, import_node_path19.join)(root, rel));
17154
+ readPackageJson((0, import_node_path21.join)(root, "package.json"));
17155
+ const nested = files.filter((f) => f.includes("/") && (0, import_node_path21.basename)(f) === "package.json").slice(0, NESTED_MANIFEST_LIMIT);
17156
+ for (const rel of nested) readPackageJson((0, import_node_path21.join)(root, rel));
16969
17157
  const pythonManifests = [
16970
- ...["pyproject.toml", "requirements.txt", "Pipfile", "setup.py"].map((f) => (0, import_node_path19.join)(root, f)),
16971
- ...files.filter((f) => f.includes("/") && /(^|\/)(pyproject\.toml|requirements\.txt)$/.test(f)).slice(0, NESTED_MANIFEST_LIMIT).map((f) => (0, import_node_path19.join)(root, f))
17158
+ ...["pyproject.toml", "requirements.txt", "Pipfile", "setup.py"].map((f) => (0, import_node_path21.join)(root, f)),
17159
+ ...files.filter((f) => f.includes("/") && /(^|\/)(pyproject\.toml|requirements\.txt)$/.test(f)).slice(0, NESTED_MANIFEST_LIMIT).map((f) => (0, import_node_path21.join)(root, f))
16972
17160
  ];
16973
17161
  for (const path of pythonManifests) {
16974
17162
  if (!(0, import_node_fs23.existsSync)(path)) continue;
@@ -16983,8 +17171,8 @@ function declaredDependencies(root, files) {
16983
17171
  }
16984
17172
  }
16985
17173
  const goMods = [
16986
- (0, import_node_path19.join)(root, "go.mod"),
16987
- ...files.filter((f) => f.includes("/") && (0, import_node_path19.basename)(f) === "go.mod").slice(0, NESTED_MANIFEST_LIMIT).map((f) => (0, import_node_path19.join)(root, f))
17174
+ (0, import_node_path21.join)(root, "go.mod"),
17175
+ ...files.filter((f) => f.includes("/") && (0, import_node_path21.basename)(f) === "go.mod").slice(0, NESTED_MANIFEST_LIMIT).map((f) => (0, import_node_path21.join)(root, f))
16988
17176
  ];
16989
17177
  for (const path of goMods) {
16990
17178
  if (!(0, import_node_fs23.existsSync)(path)) continue;
@@ -16997,7 +17185,7 @@ function declaredDependencies(root, files) {
16997
17185
  }
16998
17186
  }
16999
17187
  for (const file of ["pom.xml", "build.gradle", "build.gradle.kts", "Gemfile", "Cargo.toml"]) {
17000
- const path = (0, import_node_path19.join)(root, file);
17188
+ const path = (0, import_node_path21.join)(root, file);
17001
17189
  if (!(0, import_node_fs23.existsSync)(path)) continue;
17002
17190
  try {
17003
17191
  const text = (0, import_node_fs23.readFileSync)(path, "utf-8");
@@ -17008,7 +17196,7 @@ function declaredDependencies(root, files) {
17008
17196
  return names2;
17009
17197
  }
17010
17198
  function detectBuildSystem(root, files) {
17011
- const has = (f) => (0, import_node_fs23.existsSync)((0, import_node_path19.join)(root, f)) || files.some((p) => (0, import_node_path19.basename)(p) === f);
17199
+ const has = (f) => (0, import_node_fs23.existsSync)((0, import_node_path21.join)(root, f)) || files.some((p) => (0, import_node_path21.basename)(p) === f);
17012
17200
  if (has("pnpm-lock.yaml")) return "pnpm";
17013
17201
  if (has("yarn.lock")) return "yarn";
17014
17202
  if (has("bun.lock") || has("bun.lockb")) return "bun";
@@ -17025,8 +17213,8 @@ function detectBuildSystem(root, files) {
17025
17213
  }
17026
17214
  function detectArchitecture(root, files) {
17027
17215
  const workspaceMarkers = ["lerna.json", "pnpm-workspace.yaml", "nx.json", "turbo.json", "rush.json"];
17028
- if (workspaceMarkers.some((m) => (0, import_node_fs23.existsSync)((0, import_node_path19.join)(root, m)))) return "monorepo";
17029
- const pkg = readJson((0, import_node_path19.join)(root, "package.json"));
17216
+ if (workspaceMarkers.some((m) => (0, import_node_fs23.existsSync)((0, import_node_path21.join)(root, m)))) return "monorepo";
17217
+ const pkg = readJson((0, import_node_path21.join)(root, "package.json"));
17030
17218
  if (pkg && "workspaces" in pkg) return "monorepo";
17031
17219
  const manifests = files.filter((f) => /(^|\/)(package\.json|go\.mod|pyproject\.toml|Cargo\.toml|pom\.xml)$/.test(f));
17032
17220
  const nested = manifests.filter((f) => f.includes("/"));
@@ -17039,16 +17227,16 @@ function detectArchitecture(root, files) {
17039
17227
  var SAMPLE_LIMIT = 300;
17040
17228
  function measureAvgFileLength(root, files, languages) {
17041
17229
  const primary = new Set(languages.slice(0, 2));
17042
- const candidates = files.filter((f) => {
17230
+ const candidates2 = files.filter((f) => {
17043
17231
  const lang = languageOf(f);
17044
17232
  return lang !== null && primary.has(lang);
17045
17233
  });
17046
- if (candidates.length === 0) return null;
17047
- const stride = Math.max(1, Math.floor(candidates.length / SAMPLE_LIMIT));
17234
+ if (candidates2.length === 0) return null;
17235
+ const stride = Math.max(1, Math.floor(candidates2.length / SAMPLE_LIMIT));
17048
17236
  let total = 0;
17049
17237
  let counted = 0;
17050
- for (let i = 0; i < candidates.length; i += stride) {
17051
- const path = (0, import_node_path19.join)(root, candidates[i]);
17238
+ for (let i = 0; i < candidates2.length; i += stride) {
17239
+ const path = (0, import_node_path21.join)(root, candidates2[i]);
17052
17240
  try {
17053
17241
  if ((0, import_node_fs23.statSync)(path).size > 2 * 1024 * 1024) continue;
17054
17242
  total += (0, import_node_fs23.readFileSync)(path, "utf-8").split("\n").length;
@@ -17076,14 +17264,14 @@ function detectProject(root = repoRoot()) {
17076
17264
  const existingToolConfigs = [];
17077
17265
  for (const [tool, markers] of TOOL_CONFIG_MARKERS) {
17078
17266
  for (const marker of markers) {
17079
- if ((0, import_node_fs23.existsSync)((0, import_node_path19.join)(root, marker))) {
17267
+ if ((0, import_node_fs23.existsSync)((0, import_node_path21.join)(root, marker))) {
17080
17268
  existingToolConfigs.push({ tool, path: `./${marker}` });
17081
17269
  break;
17082
17270
  }
17083
17271
  }
17084
17272
  }
17085
17273
  return {
17086
- projectName: (0, import_node_path19.basename)(root),
17274
+ projectName: (0, import_node_path21.basename)(root),
17087
17275
  languages,
17088
17276
  languageCounts,
17089
17277
  frameworks: matchAll(dependencies, FRAMEWORK_BY_DEPENDENCY),
@@ -17529,7 +17717,7 @@ async function correctVerityMdVersion(opts) {
17529
17717
  }
17530
17718
  async function writeFileTo(relative2, body) {
17531
17719
  const target = projectPath(relative2);
17532
- await (0, import_promises9.mkdir)((0, import_node_path20.dirname)(target), { recursive: true });
17720
+ await (0, import_promises9.mkdir)((0, import_node_path22.dirname)(target), { recursive: true });
17533
17721
  await (0, import_promises9.writeFile)(target, body);
17534
17722
  }
17535
17723
  async function deriveConfigForStandard(standard) {
@@ -18004,7 +18192,11 @@ function registerConfigCommands(program2) {
18004
18192
  });
18005
18193
  config.command("git-moments [moments]").description('Get or set the git moments the guard reviews: commit,push \u2014 or "none"').action((moments) => {
18006
18194
  if (moments === void 0) {
18007
- const current = readProjectConfig().git_moments;
18195
+ const config2 = readProjectConfig();
18196
+ const current = config2.git_moments;
18197
+ if (config2.source === "main-worktree" && config2.path) {
18198
+ printInfo(`Inherited from the main worktree: ${config2.path}`);
18199
+ }
18008
18200
  process.stdout.write((current.length ? current.join(",") : "none") + "\n");
18009
18201
  return;
18010
18202
  }
@@ -18013,7 +18205,7 @@ function registerConfigCommands(program2) {
18013
18205
  printError(`Unrecognised moments: ${moments}. Use "commit", "push", "commit,push", or "none".`);
18014
18206
  process.exit(1);
18015
18207
  }
18016
- writeProjectConfig({ git_moments: next });
18208
+ writeProjectConfig({ git_moments: next, git_moments_source: "user" });
18017
18209
  printInfo(
18018
18210
  next.length ? `Git-moment review enabled for: ${next.join(", ")}` : "Git-moment review disabled \u2014 commits and pushes are no longer gated."
18019
18211
  );
@@ -18474,8 +18666,14 @@ function timeAgo(isoDate) {
18474
18666
  return `${days}d ago`;
18475
18667
  }
18476
18668
  function registerStatusCommand(program2) {
18477
- program2.command("status").description("Show project quality status").option("--history", "Include recent run history").option("--limit <n>", "Number of history entries", "5").option("--json", "Output raw JSON").action(async (opts) => {
18669
+ program2.command("status").description("Show project quality status").option("--history", "Include recent run history").option("--limit <n>", "Number of history entries (1-100)", "5").option("--json", "Output raw JSON").action(async (opts) => {
18478
18670
  const globals = program2.opts();
18671
+ const rawLimit = String(opts.limit).trim();
18672
+ const limit = /^\d{1,3}$/.test(rawLimit) ? Number(rawLimit) : Number.NaN;
18673
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
18674
+ printError(`--limit must be a whole number between 1 and 100 (got "${opts.limit}").`);
18675
+ process.exit(1);
18676
+ }
18479
18677
  const tokenResult = await resolveToken(globals.token);
18480
18678
  if (!tokenResult.ok) {
18481
18679
  printError(tokenResult.error);
@@ -18513,7 +18711,7 @@ function registerStatusCommand(program2) {
18513
18711
  if (opts.history && !denial) {
18514
18712
  const runsResult = await apiRequest({
18515
18713
  method: "GET",
18516
- path: `/runs?limit=${opts.limit}`,
18714
+ path: `/runs?limit=${limit}`,
18517
18715
  serviceUrl,
18518
18716
  token,
18519
18717
  verbose: globals.verbose
@@ -18553,12 +18751,14 @@ function registerStatusCommand(program2) {
18553
18751
  printInfo(`Standard: v${s.version} (${s.quality_dimensions} quality, ${s.security_patterns} security, ${s.custom_patterns} custom)`);
18554
18752
  printInfo(`Languages: ${s.languages.join(", ")}`);
18555
18753
  }
18556
- const hookStatus = await checkAllVerityHooks();
18754
+ const wiring = await resolveHookWiring();
18755
+ const hookStatus = wiring.status;
18557
18756
  const moments = [];
18558
18757
  if (hookStatus.stop) moments.push("stop");
18559
18758
  if (hookStatus.guardOn.includes("commit")) moments.push("pre-commit");
18560
18759
  if (hookStatus.guardOn.includes("push")) moments.push("pre-push/PR");
18561
- printInfo(`Moments: ${moments.length > 0 ? moments.join(", ") : 'none (run "verity init")'}`);
18760
+ const via = wiring.source === "plugin" ? " (wired by the Verity plugin)" : "";
18761
+ printInfo(`Moments: ${moments.length > 0 ? `${moments.join(", ")}${via}` : 'none (run "verity init")'}`);
18562
18762
  if (!mem) return;
18563
18763
  if (mem.recent_runs) {
18564
18764
  const r = mem.recent_runs;
@@ -18635,7 +18835,7 @@ function registerStatusCommand(program2) {
18635
18835
  if (opts.history) {
18636
18836
  const runsResult = await apiRequest({
18637
18837
  method: "GET",
18638
- path: `/runs?limit=${opts.limit}`,
18838
+ path: `/runs?limit=${limit}`,
18639
18839
  serviceUrl,
18640
18840
  token,
18641
18841
  verbose: globals.verbose
@@ -18826,11 +19026,11 @@ var import_node_fs43 = require("node:fs");
18826
19026
 
18827
19027
  // src/lib/repo-context.ts
18828
19028
  var import_node_child_process9 = require("node:child_process");
18829
- var import_node_os3 = require("node:os");
19029
+ var import_node_os4 = require("node:os");
18830
19030
  function rgInvocations(env = process.env) {
18831
19031
  const out = [{ cmd: "rg" }];
18832
19032
  if (env.CLAUDE_CODE_EXECPATH) out.push({ cmd: env.CLAUDE_CODE_EXECPATH, argv0: "rg" });
18833
- out.push({ cmd: `${(0, import_node_os3.homedir)()}/.local/bin/claude`, argv0: "rg" });
19033
+ out.push({ cmd: `${(0, import_node_os4.homedir)()}/.local/bin/claude`, argv0: "rg" });
18834
19034
  return out;
18835
19035
  }
18836
19036
  var MAX_SYMBOLS = 12;
@@ -19644,9 +19844,9 @@ function installRunEvidence(run2) {
19644
19844
  // src/lib/git-frame.ts
19645
19845
  var import_node_child_process10 = require("node:child_process");
19646
19846
  var import_node_fs31 = require("node:fs");
19647
- var import_node_os4 = require("node:os");
19648
- var import_node_path21 = require("node:path");
19649
- var import_node_path22 = require("node:path");
19847
+ var import_node_os5 = require("node:os");
19848
+ var import_node_path23 = require("node:path");
19849
+ var import_node_path24 = require("node:path");
19650
19850
  var VALUE_TOKEN = `(?:'[^']*'|"[^"]*"|\\S+)`;
19651
19851
  var GIT_GLOBAL_OPTS = `(?:\\s+(?:-[Cc]\\s+${VALUE_TOKEN}|--?[\\w-]+(?:=\\S+)?))*`;
19652
19852
  var COMMIT_HEAD = `git${GIT_GLOBAL_OPTS}\\s+commit(?![\\w-])`;
@@ -19692,15 +19892,15 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
19692
19892
  if (!m) continue;
19693
19893
  named = true;
19694
19894
  if (m[1] === void 0) {
19695
- dir = (0, import_node_os4.homedir)();
19895
+ dir = (0, import_node_os5.homedir)();
19696
19896
  continue;
19697
19897
  }
19698
19898
  const raw = unquote(m[1]);
19699
19899
  if (SHELL_DYNAMIC.test(raw) || raw === "-") {
19700
19900
  return { dir: null, named: true, unresolvable: `cd target not statically resolvable: ${raw}` };
19701
19901
  }
19702
- const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path22.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
19703
- dir = (0, import_node_path21.isAbsolute)(expanded) ? expanded : (0, import_node_path21.resolve)(dir, expanded);
19902
+ const expanded = raw === "~" ? (0, import_node_os5.homedir)() : raw.startsWith("~/") ? (0, import_node_path24.join)((0, import_node_os5.homedir)(), raw.slice(2)) : raw;
19903
+ dir = (0, import_node_path23.isAbsolute)(expanded) ? expanded : (0, import_node_path23.resolve)(dir, expanded);
19704
19904
  }
19705
19905
  const seg = segments[segmentIndex];
19706
19906
  const overrideMatch = /--(?:git-dir|work-tree)(?:=|\s)|\bGIT_(?:DIR|WORK_TREE|INDEX_FILE)=/.exec(seg);
@@ -19718,8 +19918,8 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
19718
19918
  if (SHELL_DYNAMIC.test(raw)) {
19719
19919
  return { dir: null, named: true, unresolvable: `-C target not statically resolvable: ${raw}` };
19720
19920
  }
19721
- const expanded = raw === "~" ? (0, import_node_os4.homedir)() : raw.startsWith("~/") ? (0, import_node_path22.join)((0, import_node_os4.homedir)(), raw.slice(2)) : raw;
19722
- dir = (0, import_node_path21.isAbsolute)(expanded) ? expanded : (0, import_node_path21.resolve)(dir, expanded);
19921
+ const expanded = raw === "~" ? (0, import_node_os5.homedir)() : raw.startsWith("~/") ? (0, import_node_path24.join)((0, import_node_os5.homedir)(), raw.slice(2)) : raw;
19922
+ dir = (0, import_node_path23.isAbsolute)(expanded) ? expanded : (0, import_node_path23.resolve)(dir, expanded);
19723
19923
  }
19724
19924
  }
19725
19925
  return { dir, named, unresolvable: null };
@@ -19780,11 +19980,11 @@ function gitAt(dir, args) {
19780
19980
  return "";
19781
19981
  }
19782
19982
  }
19783
- function realpathOr(p) {
19983
+ function realpathOr2(p) {
19784
19984
  try {
19785
19985
  return import_node_fs31.realpathSync.native(p);
19786
19986
  } catch {
19787
- return (0, import_node_path21.resolve)(p);
19987
+ return (0, import_node_path23.resolve)(p);
19788
19988
  }
19789
19989
  }
19790
19990
  function resolveFrame(input) {
@@ -19822,13 +20022,13 @@ function resolveFrame(input) {
19822
20022
  }
19823
20023
  const gitDirRaw = gitAt(dir, ["rev-parse", "--absolute-git-dir"]);
19824
20024
  const commonRaw = gitAt(dir, ["rev-parse", "--git-common-dir"]);
19825
- const gitDir = gitDirRaw ? realpathOr(gitDirRaw) : null;
19826
- const commonDir = commonRaw ? realpathOr((0, import_node_path21.isAbsolute)(commonRaw) ? commonRaw : (0, import_node_path21.resolve)(dir, commonRaw)) : null;
20025
+ const gitDir = gitDirRaw ? realpathOr2(gitDirRaw) : null;
20026
+ const commonDir = commonRaw ? realpathOr2((0, import_node_path23.isAbsolute)(commonRaw) ? commonRaw : (0, import_node_path23.resolve)(dir, commonRaw)) : null;
19827
20027
  const branchRaw = gitAt(dir, ["rev-parse", "--abbrev-ref", "HEAD"]);
19828
20028
  return {
19829
20029
  moment: found?.moment ?? null,
19830
20030
  frame: {
19831
- worktreeRoot: realpathOr(toplevel),
20031
+ worktreeRoot: realpathOr2(toplevel),
19832
20032
  gitDir,
19833
20033
  commonDir,
19834
20034
  // The one honest definition: a linked worktree's own git dir differs from
@@ -19848,14 +20048,172 @@ function refResolves(frame, ref) {
19848
20048
  return frameGit(frame, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]) !== "";
19849
20049
  }
19850
20050
  var SHA_RE2 = /^[0-9a-f]{40}$/;
19851
- function stagedRange(frame) {
20051
+ function shellWords(segment) {
20052
+ return segment.match(/'[^']*'|"[^"]*"|\S+/g) ?? [];
20053
+ }
20054
+ var ADD_FLAGS_REPLAYABLE = /* @__PURE__ */ new Set([
20055
+ "-A",
20056
+ "--all",
20057
+ "--no-ignore-removal",
20058
+ "-u",
20059
+ "--update",
20060
+ "--no-all",
20061
+ "--ignore-removal",
20062
+ "-f",
20063
+ "--force",
20064
+ "-v",
20065
+ "--verbose",
20066
+ "-N",
20067
+ "--intent-to-add",
20068
+ "--renormalize",
20069
+ "--sparse",
20070
+ "--ignore-errors",
20071
+ "--refresh"
20072
+ ]);
20073
+ var ADD_FLAGS_INTERACTIVE = /* @__PURE__ */ new Set(["-i", "--interactive", "-p", "--patch", "-e", "--edit"]);
20074
+ var PATHSPEC_UNRESOLVABLE = /[$`\\*?[\]{}~]/;
20075
+ var COMMIT_VALUE_OPTS = /* @__PURE__ */ new Set([
20076
+ "-m",
20077
+ "--message",
20078
+ "-F",
20079
+ "--file",
20080
+ "-C",
20081
+ "--reuse-message",
20082
+ "-c",
20083
+ "--reedit-message",
20084
+ "--author",
20085
+ "--date",
20086
+ "-t",
20087
+ "--template",
20088
+ "--fixup",
20089
+ "--squash",
20090
+ "--trailer",
20091
+ "--cleanup"
20092
+ ]);
20093
+ var ADD_HEAD = `git${GIT_GLOBAL_OPTS}\\s+add(?![\\w-])`;
20094
+ var ADD_RE = new RegExp(`(?:^|[\\s;&|(])${ADD_HEAD}|(?:^|[;&|(])\\s*[^\\s;&|()'"]*\\/${ADD_HEAD}`);
20095
+ function pathspecOf(word) {
20096
+ const raw = unquote(word);
20097
+ if (!raw) return { reason: "an empty pathspec" };
20098
+ if (PATHSPEC_UNRESOLVABLE.test(raw)) {
20099
+ return { reason: `the shell rewrites the pathspec "${raw}" before git sees it` };
20100
+ }
20101
+ return { path: raw };
20102
+ }
20103
+ function parseAddSegment(segment) {
20104
+ const words = shellWords(segment);
20105
+ const at = words.indexOf("add");
20106
+ if (at === -1) return { reason: 'the "git add" arguments could not be read' };
20107
+ const flags = [];
20108
+ const paths = [];
20109
+ let literal = false;
20110
+ for (const word of words.slice(at + 1)) {
20111
+ if (literal || !word.startsWith("-") || word === "-") {
20112
+ if (word === "--" && !literal) {
20113
+ literal = true;
20114
+ continue;
20115
+ }
20116
+ const p = pathspecOf(word);
20117
+ if ("reason" in p) return p;
20118
+ paths.push(p.path);
20119
+ continue;
20120
+ }
20121
+ if (word === "--") {
20122
+ literal = true;
20123
+ continue;
20124
+ }
20125
+ const parts = /^-[A-Za-z]{2,}$/.test(word) ? [...word.slice(1)].map((c) => `-${c}`) : [word];
20126
+ for (const flag of parts) {
20127
+ if (ADD_FLAGS_INTERACTIVE.has(flag)) {
20128
+ return { reason: `"git add ${flag}" stages what a human picks at a prompt` };
20129
+ }
20130
+ if (!ADD_FLAGS_REPLAYABLE.has(flag.split("=")[0])) {
20131
+ return { reason: `unmodelled "git add" option ${flag}` };
20132
+ }
20133
+ flags.push(flag);
20134
+ }
20135
+ }
20136
+ return { flags, paths };
20137
+ }
20138
+ function commitStagesTrackedChanges(segment) {
20139
+ const words = shellWords(segment);
20140
+ const at = words.indexOf("commit");
20141
+ if (at === -1) return false;
20142
+ let skipValue = false;
20143
+ for (const word of words.slice(at + 1)) {
20144
+ if (skipValue) {
20145
+ skipValue = false;
20146
+ continue;
20147
+ }
20148
+ if (word === "--") break;
20149
+ if (!word.startsWith("-") || word === "-") continue;
20150
+ if (word.startsWith("--")) {
20151
+ const name = word.split("=")[0];
20152
+ if (name === "--all") return true;
20153
+ if (COMMIT_VALUE_OPTS.has(name) && !word.includes("=")) skipValue = true;
20154
+ continue;
20155
+ }
20156
+ for (let i = 1; i < word.length; i++) {
20157
+ const flag = `-${word[i]}`;
20158
+ if (flag === "-a") return true;
20159
+ if (COMMIT_VALUE_OPTS.has(flag)) {
20160
+ if (i === word.length - 1) skipValue = true;
20161
+ break;
20162
+ }
20163
+ }
20164
+ }
20165
+ return false;
20166
+ }
20167
+ function planInCommandStaging(frame, command) {
20168
+ const found = findMomentSegment(command, ["commit"]);
20169
+ if (!found) return { kind: "none" };
20170
+ const segments = splitSegments(command);
20171
+ if (commitStagesTrackedChanges(segments[found.segmentIndex])) {
20172
+ return {
20173
+ kind: "files",
20174
+ via: "index+commit-a",
20175
+ files: frameGit(frame, ["diff", "--name-only", "HEAD"]).split("\n").filter(Boolean)
20176
+ };
20177
+ }
20178
+ const addSegments = segments.slice(0, found.segmentIndex).filter((s) => ADD_RE.test(s));
20179
+ if (addSegments.length === 0) return { kind: "none" };
20180
+ const staged = new Set(
20181
+ frameGit(frame, ["diff", "--cached", "--name-only"]).split("\n").filter(Boolean)
20182
+ );
20183
+ for (const segment of addSegments) {
20184
+ const parsed = parseAddSegment(segment);
20185
+ if ("reason" in parsed) return { kind: "unpredictable", reason: parsed.reason };
20186
+ const args = ["add", "--dry-run", "--ignore-missing", ...parsed.flags];
20187
+ if (parsed.paths.length > 0) args.push("--", ...parsed.paths);
20188
+ for (const line of frameGit(frame, args).split("\n")) {
20189
+ const m = line.match(/^(?:add|remove) '(.*)'$/);
20190
+ if (m) staged.add(m[1]);
20191
+ }
20192
+ }
20193
+ return { kind: "files", via: "index+staged-in-command", files: [...staged] };
20194
+ }
20195
+ function stagedRange(frame, command) {
19852
20196
  if (!frame.worktreeRoot) return { kind: "nothing", base: null, head: "INDEX", via: "refused" };
19853
- const mergeHead = frame.gitDir ? (0, import_node_path22.join)(frame.gitDir, "MERGE_HEAD") : null;
20197
+ const plan = command ? planInCommandStaging(frame, command) : { kind: "none" };
20198
+ if (plan.kind === "unpredictable") {
20199
+ return { kind: "staged", base: "HEAD", head: "INDEX", via: "staged-in-command", refusal: plan.reason };
20200
+ }
20201
+ const mergeHead = frame.gitDir ? (0, import_node_path24.join)(frame.gitDir, "MERGE_HEAD") : null;
19854
20202
  if (mergeHead && (0, import_node_fs31.existsSync)(mergeHead)) {
19855
20203
  const vsHead = new Set(frameGit(frame, ["diff", "--cached", "--name-only", "HEAD"]).split("\n").filter(Boolean));
19856
20204
  const vsMerge = new Set(frameGit(frame, ["diff", "--cached", "--name-only", "MERGE_HEAD"]).split("\n").filter(Boolean));
19857
- const resolutions = [...vsHead].filter((f) => vsMerge.has(f) && !isVerityOwnedPath(f));
19858
- return { kind: "merge", base: "HEAD", head: "INDEX", via: "merge-resolutions", files: resolutions };
20205
+ const resolutions = new Set([...vsHead].filter((f) => vsMerge.has(f) && !isVerityOwnedPath(f)));
20206
+ if (plan.kind === "files") {
20207
+ const wtVsHead = new Set(frameGit(frame, ["diff", "--name-only", "HEAD"]).split("\n").filter(Boolean));
20208
+ const wtVsMerge = new Set(frameGit(frame, ["diff", "--name-only", "MERGE_HEAD"]).split("\n").filter(Boolean));
20209
+ for (const f of plan.files) {
20210
+ if (wtVsHead.has(f) && wtVsMerge.has(f) && !isVerityOwnedPath(f)) resolutions.add(f);
20211
+ }
20212
+ }
20213
+ return { kind: "merge", base: "HEAD", head: "INDEX", via: "merge-resolutions", files: [...resolutions] };
20214
+ }
20215
+ if (plan.kind === "files") {
20216
+ return { kind: "staged", base: "HEAD", head: "INDEX", via: plan.via, files: plan.files };
19859
20217
  }
19860
20218
  return { kind: "staged", base: "HEAD", head: "INDEX", via: "index" };
19861
20219
  }
@@ -19937,7 +20295,7 @@ function frameTelemetry(frame, range, divergence) {
19937
20295
  refusal: frame.refusal
19938
20296
  };
19939
20297
  if (divergence) {
19940
- t.root_differs = !!frame.worktreeRoot && !!divergence.actualRoot && realpathOr(frame.worktreeRoot) !== realpathOr(divergence.actualRoot);
20298
+ t.root_differs = !!frame.worktreeRoot && !!divergence.actualRoot && realpathOr2(frame.worktreeRoot) !== realpathOr2(divergence.actualRoot);
19941
20299
  const a = [...divergence.actualFiles].sort().join("\n");
19942
20300
  const b = [...divergence.frameFiles].sort().join("\n");
19943
20301
  t.files_differ = a !== b;
@@ -20247,7 +20605,7 @@ function sanitizeCommandWithLoss(rawCmd) {
20247
20605
  const lost = lines.slice(1).some((l) => l.trim().length > 0);
20248
20606
  const first = lines[0];
20249
20607
  const cmd = sanitizeCommand(first);
20250
- const hadSeparator = SEPARATORS.some((sep2) => first.indexOf(sep2) > 0);
20608
+ const hadSeparator = SEPARATORS.some((sep3) => first.indexOf(sep3) > 0);
20251
20609
  return { cmd, lost: lost || !hadSeparator && first.length > MAX_COMMAND_CHARS };
20252
20610
  }
20253
20611
  var SEPARATORS = [" | ", " > ", " >> ", " 2>", " && ", " ; "];
@@ -20256,11 +20614,11 @@ function sanitizeCommand(rawCmd) {
20256
20614
  let cmd = rawCmd.split("\n")[0];
20257
20615
  let cut = -1;
20258
20616
  let marker = "";
20259
- for (const sep2 of SEPARATORS) {
20260
- const idx = cmd.indexOf(sep2);
20617
+ for (const sep3 of SEPARATORS) {
20618
+ const idx = cmd.indexOf(sep3);
20261
20619
  if (idx > 0 && (cut === -1 || idx < cut)) {
20262
20620
  cut = idx;
20263
- marker = sep2.trim();
20621
+ marker = sep3.trim();
20264
20622
  }
20265
20623
  }
20266
20624
  if (cut > -1) cmd = cmd.slice(0, cut);
@@ -20284,28 +20642,28 @@ async function readStopHookStdin() {
20284
20642
  try {
20285
20643
  if (process.stdin.isTTY) return empty;
20286
20644
  const chunks = [];
20287
- const timeout = new Promise((resolve4) => setTimeout(() => resolve4(empty), 500));
20288
- const read = new Promise((resolve4) => {
20645
+ const timeout = new Promise((resolve5) => setTimeout(() => resolve5(empty), 500));
20646
+ const read = new Promise((resolve5) => {
20289
20647
  process.stdin.on("data", (chunk) => chunks.push(chunk));
20290
20648
  process.stdin.on("end", () => {
20291
20649
  const raw = Buffer.concat(chunks).toString("utf-8").trim();
20292
20650
  if (!raw) {
20293
- resolve4(empty);
20651
+ resolve5(empty);
20294
20652
  return;
20295
20653
  }
20296
20654
  try {
20297
20655
  const data = JSON.parse(raw);
20298
- resolve4({
20656
+ resolve5({
20299
20657
  assistantMessage: typeof data.last_assistant_message === "string" ? data.last_assistant_message : null,
20300
20658
  stopReason: typeof data.stop_reason === "string" ? data.stop_reason : null,
20301
20659
  transcriptPath: typeof data.transcript_path === "string" ? data.transcript_path : null,
20302
20660
  sessionId: typeof data.session_id === "string" ? data.session_id : null
20303
20661
  });
20304
20662
  } catch {
20305
- resolve4(empty);
20663
+ resolve5(empty);
20306
20664
  }
20307
20665
  });
20308
- process.stdin.on("error", () => resolve4(empty));
20666
+ process.stdin.on("error", () => resolve5(empty));
20309
20667
  process.stdin.resume();
20310
20668
  });
20311
20669
  return await Promise.race([read, timeout]);
@@ -20509,7 +20867,7 @@ function channelSilence(input) {
20509
20867
  // src/lib/cli-version.ts
20510
20868
  function cliVersion() {
20511
20869
  try {
20512
- return true ? "0.32.0" : "dev";
20870
+ return true ? "0.32.1-experimental.f2cf829" : "dev";
20513
20871
  } catch {
20514
20872
  return "dev";
20515
20873
  }
@@ -20868,7 +21226,7 @@ async function scope(run2) {
20868
21226
 
20869
21227
  // src/lib/specs.ts
20870
21228
  var import_node_fs34 = require("node:fs");
20871
- var import_node_path23 = require("node:path");
21229
+ var import_node_path25 = require("node:path");
20872
21230
  var SPEC_CANDIDATES = [
20873
21231
  "CLAUDE.md",
20874
21232
  "AGENTS.md",
@@ -20941,7 +21299,7 @@ function findMdFiles(dir, maxDepth, depth = 0) {
20941
21299
  try {
20942
21300
  const entries = (0, import_node_fs34.readdirSync)(dir, { withFileTypes: true });
20943
21301
  for (const entry of entries) {
20944
- const fullPath = (0, import_node_path23.join)(dir, entry.name);
21302
+ const fullPath = (0, import_node_path25.join)(dir, entry.name);
20945
21303
  if (entry.isFile() && entry.name.endsWith(".md")) {
20946
21304
  result.push(fullPath);
20947
21305
  } else if (entry.isDirectory() && depth < maxDepth - 1) {
@@ -20953,9 +21311,9 @@ function findMdFiles(dir, maxDepth, depth = 0) {
20953
21311
  return result;
20954
21312
  }
20955
21313
  function discoverPlans() {
20956
- const homePlansDir = (0, import_node_path23.join)(process.env.HOME ?? "", ".claude", "plans");
21314
+ const homePlansDir = (0, import_node_path25.join)(process.env.HOME ?? "", ".claude", "plans");
20957
21315
  const localPlansDir = ".claude/plans";
20958
- const candidates = [];
21316
+ const candidates2 = [];
20959
21317
  const seen = /* @__PURE__ */ new Set();
20960
21318
  for (const plansDir of [localPlansDir, homePlansDir]) {
20961
21319
  if (!(0, import_node_fs34.existsSync)(plansDir)) continue;
@@ -20963,19 +21321,19 @@ function discoverPlans() {
20963
21321
  for (const f of (0, import_node_fs34.readdirSync)(plansDir)) {
20964
21322
  if (!f.endsWith(".md") || seen.has(f)) continue;
20965
21323
  seen.add(f);
20966
- const fullPath = (0, import_node_path23.join)(plansDir, f);
21324
+ const fullPath = (0, import_node_path25.join)(plansDir, f);
20967
21325
  try {
20968
21326
  const stat3 = (0, import_node_fs34.statSync)(fullPath);
20969
- candidates.push({ name: f, path: fullPath, mtime: stat3.mtimeMs, size: stat3.size });
21327
+ candidates2.push({ name: f, path: fullPath, mtime: stat3.mtimeMs, size: stat3.size });
20970
21328
  } catch {
20971
21329
  }
20972
21330
  }
20973
21331
  } catch {
20974
21332
  }
20975
21333
  }
20976
- candidates.sort((a, b) => b.mtime - a.mtime);
21334
+ candidates2.sort((a, b) => b.mtime - a.mtime);
20977
21335
  const result = [];
20978
- for (const entry of candidates.slice(0, MAX_PLAN_FILES)) {
21336
+ for (const entry of candidates2.slice(0, MAX_PLAN_FILES)) {
20979
21337
  if (entry.size > MAX_PLAN_FILE_BYTES) continue;
20980
21338
  try {
20981
21339
  const content = (0, import_node_fs34.readFileSync)(entry.path, "utf-8");
@@ -21175,7 +21533,7 @@ async function mode(run2) {
21175
21533
 
21176
21534
  // src/lib/fold.ts
21177
21535
  var import_node_fs35 = require("node:fs");
21178
- var import_node_path24 = require("node:path");
21536
+ var import_node_path26 = require("node:path");
21179
21537
  var KNOWN_RECORD_TYPES = /* @__PURE__ */ new Set([
21180
21538
  "user",
21181
21539
  "assistant",
@@ -21407,9 +21765,9 @@ function fold(transcriptPath, opts = {}) {
21407
21765
  return result;
21408
21766
  }
21409
21767
  try {
21410
- const sidecarDir = (0, import_node_path24.join)(
21411
- (0, import_node_path24.dirname)(transcriptPath),
21412
- (0, import_node_path24.basename)(transcriptPath).replace(/\.jsonl$/, ""),
21768
+ const sidecarDir = (0, import_node_path26.join)(
21769
+ (0, import_node_path26.dirname)(transcriptPath),
21770
+ (0, import_node_path26.basename)(transcriptPath).replace(/\.jsonl$/, ""),
21413
21771
  "subagents"
21414
21772
  );
21415
21773
  if ((0, import_node_fs35.existsSync)(sidecarDir)) {
@@ -21419,7 +21777,7 @@ function fold(transcriptPath, opts = {}) {
21419
21777
  const walk2 = (d, depth) => {
21420
21778
  if (depth > 4) return;
21421
21779
  for (const e of (0, import_node_fs35.readdirSync)(d, { withFileTypes: true })) {
21422
- const p = (0, import_node_path24.join)(d, e.name);
21780
+ const p = (0, import_node_path26.join)(d, e.name);
21423
21781
  if (e.isDirectory()) {
21424
21782
  walk2(p, depth + 1);
21425
21783
  } else if (e.name.startsWith("agent-") && e.name.endsWith(".jsonl")) {
@@ -21794,7 +22152,7 @@ async function evidence(run2) {
21794
22152
 
21795
22153
  // src/lib/cache-cleanup.ts
21796
22154
  var import_node_fs36 = require("node:fs");
21797
- var import_node_path25 = require("node:path");
22155
+ var import_node_path27 = require("node:path");
21798
22156
  var CACHE_TTL_DAYS = 7;
21799
22157
  function pruneStaleCache() {
21800
22158
  try {
@@ -21802,7 +22160,7 @@ function pruneStaleCache() {
21802
22160
  const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
21803
22161
  for (const entry of (0, import_node_fs36.readdirSync)(dir)) {
21804
22162
  if (!entry.startsWith("pending-")) continue;
21805
- const path = (0, import_node_path25.join)(dir, entry);
22163
+ const path = (0, import_node_path27.join)(dir, entry);
21806
22164
  try {
21807
22165
  const stat3 = (0, import_node_fs36.statSync)(path);
21808
22166
  if (stat3.mtimeMs < cutoff) {
@@ -21821,7 +22179,7 @@ function pruneStaleCache() {
21821
22179
 
21822
22180
  // src/lib/context-files.ts
21823
22181
  var import_node_fs37 = require("node:fs");
21824
- var import_node_os5 = require("node:os");
22182
+ var import_node_os6 = require("node:os");
21825
22183
  var MAX_CONTEXT_FILES = 10;
21826
22184
  var MAX_CONTEXT_FILE_BYTES = 10240;
21827
22185
  var MAX_CONTEXT_TOTAL_BYTES = 24576;
@@ -21829,7 +22187,7 @@ function readSetContextPaths(summary, deltaFiles) {
21829
22187
  const reads = summary?.files_read ?? [];
21830
22188
  if (reads.length === 0) return [];
21831
22189
  const root = process.cwd().replace(/\/+$/, "");
21832
- const home = (0, import_node_os5.homedir)();
22190
+ const home = (0, import_node_os6.homedir)();
21833
22191
  const toRepoRelative2 = (p) => {
21834
22192
  if (!p) return null;
21835
22193
  let abs;
@@ -21970,7 +22328,7 @@ async function repoContext(run2) {
21970
22328
  // src/lib/seed-runner.ts
21971
22329
  var import_promises14 = require("node:fs/promises");
21972
22330
  var import_node_fs38 = require("node:fs");
21973
- var import_node_path26 = require("node:path");
22331
+ var import_node_path28 = require("node:path");
21974
22332
  var import_yaml4 = __toESM(require_dist());
21975
22333
 
21976
22334
  // src/lib/seed.ts
@@ -22237,7 +22595,7 @@ async function runSeed(opts) {
22237
22595
  }
22238
22596
  }
22239
22597
  }
22240
- const candidates = deriveSeedNodes({
22598
+ const candidates2 = deriveSeedNodes({
22241
22599
  knowledgeSpec: {
22242
22600
  project_name: knowledgeSpec.project_name,
22243
22601
  languages: knowledgeSpec.languages,
@@ -22249,20 +22607,20 @@ async function runSeed(opts) {
22249
22607
  readmeContent,
22250
22608
  claudeMdContent
22251
22609
  });
22252
- if (candidates.length === 0) {
22610
+ if (candidates2.length === 0) {
22253
22611
  return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
22254
22612
  }
22255
- const overviewPath = (0, import_node_path26.join)(MEMORY_DIR, "domain", "project-overview.md");
22613
+ const overviewPath = (0, import_node_path28.join)(MEMORY_DIR, "domain", "project-overview.md");
22256
22614
  if ((0, import_node_fs38.existsSync)(overviewPath) && !opts.force) {
22257
- return { created: 0, failed: 0, skipped: "already_seeded", candidates };
22615
+ return { created: 0, failed: 0, skipped: "already_seeded", candidates: candidates2 };
22258
22616
  }
22259
22617
  if (opts.dryRun) {
22260
- return { created: 0, failed: 0, skipped: null, candidates };
22618
+ return { created: 0, failed: 0, skipped: null, candidates: candidates2 };
22261
22619
  }
22262
22620
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
22263
22621
  let created = 0;
22264
22622
  let failed = 0;
22265
- for (const c of candidates) {
22623
+ for (const c of candidates2) {
22266
22624
  const res = await apiRequest({
22267
22625
  method: "POST",
22268
22626
  path: "/compound/memory/nodes",
@@ -22295,7 +22653,7 @@ async function runSeed(opts) {
22295
22653
  continue;
22296
22654
  }
22297
22655
  try {
22298
- await (0, import_promises14.mkdir)((0, import_node_path26.dirname)(targetPath), { recursive: true });
22656
+ await (0, import_promises14.mkdir)((0, import_node_path28.dirname)(targetPath), { recursive: true });
22299
22657
  await (0, import_promises14.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
22300
22658
  created++;
22301
22659
  opts.onCreated?.(nodeId, filePathRel, c);
@@ -22304,12 +22662,12 @@ async function runSeed(opts) {
22304
22662
  failed++;
22305
22663
  }
22306
22664
  }
22307
- return { created, failed, skipped: null, candidates };
22665
+ return { created, failed, skipped: null, candidates: candidates2 };
22308
22666
  }
22309
22667
 
22310
22668
  // src/commands/analyze/phases/08-memory-manifest.ts
22311
22669
  var import_node_fs39 = require("node:fs");
22312
- var import_node_path27 = require("node:path");
22670
+ var import_node_path29 = require("node:path");
22313
22671
  async function memoryManifest(run2) {
22314
22672
  const { globals } = run2;
22315
22673
  const { serviceUrl, token } = run2;
@@ -22319,7 +22677,7 @@ async function memoryManifest(run2) {
22319
22677
  let autoSeedNotice = null;
22320
22678
  try {
22321
22679
  await ensureMemoryDir();
22322
- const seedMarker = (0, import_node_path27.join)(VERITY_DIR, ".seeded");
22680
+ const seedMarker = (0, import_node_path29.join)(VERITY_DIR, ".seeded");
22323
22681
  const hasStandard = (0, import_node_fs39.existsSync)(STANDARD_FILE);
22324
22682
  const alreadyTried = (0, import_node_fs39.existsSync)(seedMarker);
22325
22683
  if (hasStandard && !alreadyTried) {
@@ -22437,7 +22795,7 @@ function computeIncrement(reviewedPaths, hashOf, priorAuthored) {
22437
22795
  }
22438
22796
 
22439
22797
  // src/commands/analyze/phases/10-working-memory.ts
22440
- var import_node_path28 = require("node:path");
22798
+ var import_node_path30 = require("node:path");
22441
22799
  async function workingMemory(run2) {
22442
22800
  const { opts } = run2;
22443
22801
  const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } = run2;
@@ -22449,7 +22807,7 @@ async function workingMemory(run2) {
22449
22807
  const priorState = foldForMarks(memorySession.d);
22450
22808
  incrementReport = computeIncrement(
22451
22809
  allForReview,
22452
- (p) => fileHash((0, import_node_path28.join)(repoRoot(), p)),
22810
+ (p) => fileHash((0, import_node_path30.join)(repoRoot(), p)),
22453
22811
  priorState.authored_all.map((a) => ({
22454
22812
  path: a.path,
22455
22813
  hash_at_last_verdict: a.hash_at_last_verdict
@@ -23014,7 +23372,7 @@ async function transmit(run2) {
23014
23372
 
23015
23373
  // src/commands/analyze/phases/13-reconcile.ts
23016
23374
  var import_node_fs42 = require("node:fs");
23017
- var import_node_path29 = require("node:path");
23375
+ var import_node_path31 = require("node:path");
23018
23376
  async function reconcile(run2) {
23019
23377
  const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run2;
23020
23378
  const sentPaths = codeDelta.files.map((f) => f.path);
@@ -23043,7 +23401,7 @@ async function reconcile(run2) {
23043
23401
  const st = foldDossier(memorySession.d);
23044
23402
  openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line) => {
23045
23403
  try {
23046
- const src = (0, import_node_fs42.readFileSync)((0, import_node_path29.join)(repoRoot(), file), "utf8").split("\n");
23404
+ const src = (0, import_node_fs42.readFileSync)((0, import_node_path31.join)(repoRoot(), file), "utf8").split("\n");
23047
23405
  const at = src[line - 1];
23048
23406
  return at === void 0 ? null : lineSha(at);
23049
23407
  } catch {
@@ -23895,14 +24253,14 @@ async function runReview(opts, globals) {
23895
24253
 
23896
24254
  // src/commands/guard.ts
23897
24255
  var import_node_fs45 = require("node:fs");
23898
- var import_node_path30 = require("node:path");
24256
+ var import_node_path32 = require("node:path");
23899
24257
  var GUARD_BLOCK_CAP = 2;
23900
- var GUARD_ITER_FILE = (0, import_node_path30.join)(VERITY_DIR, ".guard-iteration");
24258
+ var GUARD_ITER_FILE = (0, import_node_path32.join)(VERITY_DIR, ".guard-iteration");
23901
24259
  function readPreToolUseStdin() {
23902
24260
  const empty = { command: "", cwd: null, sessionId: null };
23903
- return new Promise((resolve4) => {
24261
+ return new Promise((resolve5) => {
23904
24262
  try {
23905
- if (process.stdin.isTTY) return resolve4(empty);
24263
+ if (process.stdin.isTTY) return resolve5(empty);
23906
24264
  const chunks = [];
23907
24265
  let timer;
23908
24266
  let settled = false;
@@ -23915,7 +24273,7 @@ function readPreToolUseStdin() {
23915
24273
  process.stdin.removeListener("end", onEnd);
23916
24274
  process.stdin.removeListener("error", onError);
23917
24275
  process.stdin.pause();
23918
- resolve4(value);
24276
+ resolve5(value);
23919
24277
  };
23920
24278
  const onEnd = () => {
23921
24279
  try {
@@ -23936,7 +24294,7 @@ function readPreToolUseStdin() {
23936
24294
  process.stdin.on("error", onError);
23937
24295
  process.stdin.resume();
23938
24296
  } catch {
23939
- resolve4(empty);
24297
+ resolve5(empty);
23940
24298
  }
23941
24299
  });
23942
24300
  }
@@ -23994,7 +24352,7 @@ function registerGuardCommand(program2) {
23994
24352
  });
23995
24353
  }
23996
24354
  function resolveMomentRange(moment, frame, command, on) {
23997
- return moment === "pre-commit" ? stagedRange(frame) : resolvePushRange(frame, command, on);
24355
+ return moment === "pre-commit" ? stagedRange(frame, command) : resolvePushRange(frame, command, on);
23998
24356
  }
23999
24357
  function describeRange(range) {
24000
24358
  if (range.kind === "staged") return "staged";
@@ -24154,6 +24512,13 @@ async function runGuard(opts, globals) {
24154
24512
  );
24155
24513
  }
24156
24514
  const range = resolveMomentRange(moment, frame, command, on);
24515
+ if (range.refusal) {
24516
+ logEvent("guard_frame", { moment, ...frameTelemetry(frame, range) });
24517
+ emitAllowNotice(
24518
+ `\u26A0 Verity ${moment}: could not tell what this ${verb} will include \u2014 ${verb === "commit" ? "committed" : "pushed"} WITHOUT review`,
24519
+ `Verity ${moment}: this command stages files as it runs and the resulting set could not be resolved (${range.refusal}); the ${verb} was allowed WITHOUT a Verity review. Stage first (\`git add \u2026\`) and re-run the ${verb} to have it reviewed.`
24520
+ );
24521
+ }
24157
24522
  const files = rangeFiles(frame, range);
24158
24523
  if (files.length === 0) process.exit(0);
24159
24524
  const tokenResult = await resolveToken(globals.token);
@@ -24189,7 +24554,7 @@ async function runGuard(opts, globals) {
24189
24554
  upgradeToExcerpts(repoContext2, {
24190
24555
  readFile: (rel) => {
24191
24556
  try {
24192
- return (0, import_node_fs45.readFileSync)((0, import_node_path30.join)(frame.worktreeRoot ?? process.cwd(), rel), "utf8");
24557
+ return (0, import_node_fs45.readFileSync)((0, import_node_path32.join)(frame.worktreeRoot ?? process.cwd(), rel), "utf8");
24193
24558
  } catch {
24194
24559
  return null;
24195
24560
  }
@@ -24258,21 +24623,28 @@ async function runGuard(opts, globals) {
24258
24623
  ${covDetail}${viewUrl ? `
24259
24624
  Report: ${viewUrl}` : ""}`
24260
24625
  );
24261
- }
24262
- if (decision === "WARN") {
24626
+ } else if (decision === "WARN") {
24263
24627
  emitAllowNotice(
24264
24628
  `\u26A0 Verity ${moment}: WARN \u2014 proceeding (${covLine})${link}`,
24265
24629
  `Verity ${moment} review: WARN (proceeding).
24266
24630
  ${covDetail}${viewUrl ? `
24267
24631
  Report: ${viewUrl}` : ""}`
24268
24632
  );
24269
- }
24270
- emitAllowNotice(
24271
- `\u2713 Verity ${moment}: PASS (${covLine})${link}`,
24272
- `Verity ${moment} review: PASS.
24633
+ } else if (decision === "PASS") {
24634
+ emitAllowNotice(
24635
+ `\u2713 Verity ${moment}: PASS (${covLine})${link}`,
24636
+ `Verity ${moment} review: PASS.
24273
24637
  ${covDetail}${viewUrl ? `
24274
24638
  Report: ${viewUrl}` : ""}`
24275
- );
24639
+ );
24640
+ } else {
24641
+ emitAllowNotice(
24642
+ `\u26A0 Verity ${moment}: no verdict came back \u2014 ${verb === "commit" ? "committed" : "pushed"} WITHOUT a usable review (${covLine})${link}`,
24643
+ `Verity ${moment}: the service answered with no recognisable gate decision (${decision}); the ${verb} was allowed, but nothing reviewed it. Treat this as unreviewed, not as a pass.
24644
+ ${covDetail}${viewUrl ? `
24645
+ Report: ${viewUrl}` : ""}`
24646
+ );
24647
+ }
24276
24648
  }
24277
24649
  function writeBlockMessage(moment, response, covDetail) {
24278
24650
  const label2 = moment === "pre-commit" ? "pre-commit" : "pre-push";
@@ -24467,7 +24839,7 @@ function registerWaiveCommand(program2) {
24467
24839
  var import_node_fs51 = require("node:fs");
24468
24840
  var import_promises17 = require("node:fs/promises");
24469
24841
  var import_yaml6 = __toESM(require_dist());
24470
- var import_node_path33 = require("node:path");
24842
+ var import_node_path35 = require("node:path");
24471
24843
  var import_node_child_process16 = require("node:child_process");
24472
24844
 
24473
24845
  // src/lib/banner.ts
@@ -24504,8 +24876,8 @@ function printBanner(opts) {
24504
24876
  const dim = (text) => color ? `${DIM3}${text}${RESET2}` : text;
24505
24877
  process.stderr.write("\n");
24506
24878
  if (!art) {
24507
- const candidates = [`Verity ${version}`, "Verity"];
24508
- const text = candidates.find((c) => c.length + INDENT.length <= columns);
24879
+ const candidates2 = [`Verity ${version}`, "Verity"];
24880
+ const text = candidates2.find((c) => c.length + INDENT.length <= columns);
24509
24881
  if (text) process.stderr.write(`${INDENT}${dim(text)}
24510
24882
  `);
24511
24883
  process.stderr.write("\n");
@@ -24612,13 +24984,13 @@ function checkAnalysisCli() {
24612
24984
  }
24613
24985
  var INSTALL_TIMEOUT_MS = 12e4;
24614
24986
  function run(command, args, opts = {}) {
24615
- return new Promise((resolve4) => {
24987
+ return new Promise((resolve5) => {
24616
24988
  const child = (0, import_node_child_process13.spawn)(command, args, {
24617
24989
  stdio: opts.inherit ? "inherit" : "pipe",
24618
24990
  timeout: INSTALL_TIMEOUT_MS
24619
24991
  });
24620
- child.on("error", () => resolve4(false));
24621
- child.on("close", (code) => resolve4(code === 0));
24992
+ child.on("error", () => resolve5(false));
24993
+ child.on("close", (code) => resolve5(code === 0));
24622
24994
  });
24623
24995
  }
24624
24996
  async function installAnalysisCli() {
@@ -24717,8 +25089,8 @@ function ensureVerityGitignore() {
24717
25089
  next = lines.map((l) => BREAKING_ENTRIES.has(l.trim()) ? ".verity/*" : l).join("\n");
24718
25090
  }
24719
25091
  if (!hasMarker) {
24720
- const sep2 = next === "" ? "" : next.endsWith("\n") ? "\n" : "\n\n";
24721
- next = next + sep2 + VERITY_GITIGNORE_BLOCK;
25092
+ const sep3 = next === "" ? "" : next.endsWith("\n") ? "\n" : "\n\n";
25093
+ next = next + sep3 + VERITY_GITIGNORE_BLOCK;
24722
25094
  }
24723
25095
  (0, import_node_fs47.writeFileSync)(".gitignore", next);
24724
25096
  return verified(needsRepair ? "repaired" : "added");
@@ -24845,7 +25217,8 @@ async function uninstallTelemetry() {
24845
25217
  async function buildReport() {
24846
25218
  const prereqs = await checkPrereqs({ install: false });
24847
25219
  const state = await readSetupState();
24848
- const hooks = await checkAllVerityHooks();
25220
+ const wiring = await resolveHookWiring();
25221
+ const hooks = wiring.status;
24849
25222
  const telemetry = await checkTelemetry();
24850
25223
  const hasConfig = (0, import_node_fs48.existsSync)(projectPath(CODACY_CONFIG_FILE));
24851
25224
  const artifacts = {
@@ -24884,6 +25257,11 @@ async function buildReport() {
24884
25257
  'Your analysis config names pattern ids that no longer resolve, so those tools run with nothing enabled and report zero issues. Fix: "verity standard synthesize --config-only" (or re-run "verity init").'
24885
25258
  );
24886
25259
  }
25260
+ if (wiring.duplicateSettingsHooks) {
25261
+ next.push(
25262
+ 'Duplicate Verity hooks also exist in .claude/settings.json. They stand down at run time under the plugin, but remove them with "verity init --plugin-mode" so the wiring says what it does.'
25263
+ );
25264
+ }
24887
25265
  if (state?.telemetry === "deferred") {
24888
25266
  next.push('Telemetry was requested but needs a token \u2014 run "verity login", then "verity telemetry install".');
24889
25267
  }
@@ -24899,7 +25277,12 @@ async function buildReport() {
24899
25277
  moments: state?.moments ?? null,
24900
25278
  telemetry: state?.telemetry ?? null
24901
25279
  },
24902
- hooks: { ...hooks, noAnalysisMoment },
25280
+ hooks: {
25281
+ source: wiring.source,
25282
+ ...hooks,
25283
+ duplicateSettingsHooks: wiring.duplicateSettingsHooks,
25284
+ noAnalysisMoment
25285
+ },
24903
25286
  telemetry: { enabled: telemetry.enabled, endpoint: telemetry.endpoint },
24904
25287
  artifacts,
24905
25288
  next
@@ -24922,8 +25305,9 @@ function registerDoctorCommand(program2) {
24922
25305
  printInfo(` verity init: ${report.phases.init.done ? `done ${report.phases.init.completed_at ?? ""}`.trim() : "NOT run here"}`);
24923
25306
  printInfo(` /verity-setup: ${report.phases.setup.done ? "done" : "not completed"}`);
24924
25307
  printInfo(` intensity: ${report.answers.intensity ?? "\u2014"} moments: ${report.answers.moments?.join(", ") ?? "\u2014"}`);
24925
- printInfo("Hooks:");
24926
- printInfo(` Stop (verity analyze): ${report.hooks.stop ? "on" : "off"}`);
25308
+ const plugin = report.hooks.source === "plugin";
25309
+ printInfo(plugin ? "Hooks: wired by the Verity Claude Code plugin (not .claude/settings.json)" : "Hooks:");
25310
+ printInfo(` Stop (verity analyze): ${report.hooks.stop ? plugin ? "on (plugin)" : "on" : "off"}`);
24927
25311
  printInfo(` Git-moment gate: ${report.hooks.guardOn.length ? report.hooks.guardOn.join(", ") : "off"}`);
24928
25312
  printInfo(` Infra (intent/baseline/compact/session-end): ${[report.hooks.intent, report.hooks.baseline, report.hooks.compact, report.hooks.sessionEnd].filter(Boolean).length}/4`);
24929
25313
  printInfo(`Telemetry: ${report.telemetry.enabled ? `enabled \u2192 ${report.telemetry.endpoint}` : "disabled"}`);
@@ -24946,7 +25330,7 @@ function registerDoctorCommand(program2) {
24946
25330
 
24947
25331
  // src/commands/migrate.ts
24948
25332
  var import_node_fs49 = require("node:fs");
24949
- var import_node_path31 = require("node:path");
25333
+ var import_node_path33 = require("node:path");
24950
25334
  var import_node_child_process15 = require("node:child_process");
24951
25335
  var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
24952
25336
  function defaultNpmRemover(pkg) {
@@ -24983,8 +25367,8 @@ async function runMigration(opts = {}) {
24983
25367
  return { actions, migrated: actions.length > 0 };
24984
25368
  }
24985
25369
  function migrateProjectDir(root, actions) {
24986
- const gateDir = (0, import_node_path31.join)(root, ".gate");
24987
- const verityDir = (0, import_node_path31.join)(root, ".verity");
25370
+ const gateDir = (0, import_node_path33.join)(root, ".gate");
25371
+ const verityDir = (0, import_node_path33.join)(root, ".verity");
24988
25372
  if ((0, import_node_fs49.existsSync)(gateDir) && !(0, import_node_fs49.existsSync)(verityDir)) {
24989
25373
  return migrateProjectDirRename(root, gateDir, verityDir, actions);
24990
25374
  }
@@ -25038,11 +25422,11 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
25038
25422
  }
25039
25423
  function migrateGlobalCredentials(home, actions) {
25040
25424
  if (!home) return;
25041
- const gateCreds = (0, import_node_path31.join)(home, ".gate", "credentials");
25042
- const verityCreds = (0, import_node_path31.join)(home, ".verity", "credentials");
25425
+ const gateCreds = (0, import_node_path33.join)(home, ".gate", "credentials");
25426
+ const verityCreds = (0, import_node_path33.join)(home, ".verity", "credentials");
25043
25427
  if (!(0, import_node_fs49.existsSync)(gateCreds)) return;
25044
25428
  if (!(0, import_node_fs49.existsSync)(verityCreds)) {
25045
- (0, import_node_fs49.mkdirSync)((0, import_node_path31.join)(home, ".verity"), { recursive: true });
25429
+ (0, import_node_fs49.mkdirSync)((0, import_node_path33.join)(home, ".verity"), { recursive: true });
25046
25430
  moveFile(gateCreds, verityCreds);
25047
25431
  actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
25048
25432
  return;
@@ -25064,7 +25448,7 @@ async function migrateLegacyHooks(root, actions) {
25064
25448
  }
25065
25449
  }
25066
25450
  async function migrateClaudeMd(root, actions) {
25067
- const claudeMd = (0, import_node_path31.join)(root, "CLAUDE.md");
25451
+ const claudeMd = (0, import_node_path33.join)(root, "CLAUDE.md");
25068
25452
  const hadLegacyBlock = (0, import_node_fs49.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
25069
25453
  if (!hadLegacyBlock) return;
25070
25454
  try {
@@ -25075,8 +25459,8 @@ async function migrateClaudeMd(root, actions) {
25075
25459
  }
25076
25460
  }
25077
25461
  function migrateStandardFile(root, actions) {
25078
- const gateMd = (0, import_node_path31.join)(root, "GATE.md");
25079
- const verityMd = (0, import_node_path31.join)(root, "VERITY.md");
25462
+ const gateMd = (0, import_node_path33.join)(root, "GATE.md");
25463
+ const verityMd = (0, import_node_path33.join)(root, "VERITY.md");
25080
25464
  if (!(0, import_node_fs49.existsSync)(gateMd) || (0, import_node_fs49.existsSync)(verityMd)) return;
25081
25465
  let moved = false;
25082
25466
  if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
@@ -25093,7 +25477,7 @@ function migrateStandardFile(root, actions) {
25093
25477
  actions.push("Renamed GATE.md \u2192 VERITY.md");
25094
25478
  }
25095
25479
  async function migrateTelemetryHeaders(root, actions) {
25096
- const file = (0, import_node_path31.join)(root, ".claude", "settings.local.json");
25480
+ const file = (0, import_node_path33.join)(root, ".claude", "settings.local.json");
25097
25481
  if (!(0, import_node_fs49.existsSync)(file)) return;
25098
25482
  let settings;
25099
25483
  try {
@@ -25141,8 +25525,8 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
25141
25525
  toAppend.push(line.replace(/\r$/, ""));
25142
25526
  }
25143
25527
  if (toAppend.length > 0) {
25144
- const sep2 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
25145
- (0, import_node_fs49.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
25528
+ const sep3 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
25529
+ (0, import_node_fs49.writeFileSync)(verityCreds, verityContent + sep3 + toAppend.join("\n") + "\n");
25146
25530
  }
25147
25531
  (0, import_node_fs49.rmSync)(gateCreds, { force: true });
25148
25532
  return toAppend.length;
@@ -25183,15 +25567,15 @@ function moveFile(from, to) {
25183
25567
  function carryLegacyContents(gateDir, verityDir) {
25184
25568
  let copied = 0;
25185
25569
  const walk2 = (relDir) => {
25186
- const srcDir = (0, import_node_path31.join)(gateDir, relDir);
25570
+ const srcDir = (0, import_node_path33.join)(gateDir, relDir);
25187
25571
  for (const entry of (0, import_node_fs49.readdirSync)(srcDir)) {
25188
- const rel = relDir ? (0, import_node_path31.join)(relDir, entry) : entry;
25189
- const src = (0, import_node_path31.join)(gateDir, rel);
25190
- const dest = (0, import_node_path31.join)(verityDir, rel);
25572
+ const rel = relDir ? (0, import_node_path33.join)(relDir, entry) : entry;
25573
+ const src = (0, import_node_path33.join)(gateDir, rel);
25574
+ const dest = (0, import_node_path33.join)(verityDir, rel);
25191
25575
  if ((0, import_node_fs49.statSync)(src).isDirectory()) {
25192
25576
  walk2(rel);
25193
25577
  } else if (!(0, import_node_fs49.existsSync)(dest)) {
25194
- (0, import_node_fs49.mkdirSync)((0, import_node_path31.dirname)(dest), { recursive: true });
25578
+ (0, import_node_fs49.mkdirSync)((0, import_node_path33.dirname)(dest), { recursive: true });
25195
25579
  (0, import_node_fs49.cpSync)(src, dest);
25196
25580
  copied++;
25197
25581
  }
@@ -25201,22 +25585,22 @@ function carryLegacyContents(gateDir, verityDir) {
25201
25585
  return copied;
25202
25586
  }
25203
25587
  async function needsMigration(root = repoRoot()) {
25204
- const gateDir = (0, import_node_path31.join)(root, ".gate");
25205
- const verityDir = (0, import_node_path31.join)(root, ".verity");
25588
+ const gateDir = (0, import_node_path33.join)(root, ".gate");
25589
+ const verityDir = (0, import_node_path33.join)(root, ".verity");
25206
25590
  if ((0, import_node_fs49.existsSync)(gateDir) && !(0, import_node_fs49.existsSync)(verityDir)) return true;
25207
25591
  if ((0, import_node_fs49.existsSync)(gateDir) && (0, import_node_fs49.existsSync)(verityDir)) {
25208
- if ((0, import_node_fs49.existsSync)((0, import_node_path31.join)(gateDir, "credentials")) && !(0, import_node_fs49.existsSync)((0, import_node_path31.join)(verityDir, "credentials"))) {
25592
+ if ((0, import_node_fs49.existsSync)((0, import_node_path33.join)(gateDir, "credentials")) && !(0, import_node_fs49.existsSync)((0, import_node_path33.join)(verityDir, "credentials"))) {
25209
25593
  return true;
25210
25594
  }
25211
- if ((0, import_node_fs49.existsSync)((0, import_node_path31.join)(gateDir, "memory")) && !(0, import_node_fs49.existsSync)((0, import_node_path31.join)(verityDir, "memory"))) {
25595
+ if ((0, import_node_fs49.existsSync)((0, import_node_path33.join)(gateDir, "memory")) && !(0, import_node_fs49.existsSync)((0, import_node_path33.join)(verityDir, "memory"))) {
25212
25596
  return true;
25213
25597
  }
25214
25598
  }
25215
- const claudeMd = (0, import_node_path31.join)(root, "CLAUDE.md");
25599
+ const claudeMd = (0, import_node_path33.join)(root, "CLAUDE.md");
25216
25600
  if ((0, import_node_fs49.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
25217
25601
  return true;
25218
25602
  }
25219
- if ((0, import_node_fs49.existsSync)((0, import_node_path31.join)(root, "GATE.md")) && !(0, import_node_fs49.existsSync)((0, import_node_path31.join)(root, "VERITY.md"))) {
25603
+ if ((0, import_node_fs49.existsSync)((0, import_node_path33.join)(root, "GATE.md")) && !(0, import_node_fs49.existsSync)((0, import_node_path33.join)(root, "VERITY.md"))) {
25220
25604
  return true;
25221
25605
  }
25222
25606
  if (await hasLegacyHooksAt(root)) return true;
@@ -25280,7 +25664,13 @@ function applyKey(state, action, count, mode2) {
25280
25664
  }
25281
25665
  case "jump": {
25282
25666
  if (action.index >= count) return { status: "open", state };
25283
- return { status: "open", state: { cursor: action.index, chosen: state.chosen } };
25667
+ if (mode2 === "single") {
25668
+ return { status: "open", state: { cursor: action.index, chosen: state.chosen } };
25669
+ }
25670
+ const chosen = new Set(state.chosen);
25671
+ if (chosen.has(action.index)) chosen.delete(action.index);
25672
+ else chosen.add(action.index);
25673
+ return { status: "open", state: { cursor: action.index, chosen } };
25284
25674
  }
25285
25675
  case "toggle": {
25286
25676
  if (mode2 === "single") {
@@ -25325,7 +25715,7 @@ function renderSelect(question, choices, state, mode2, color = colorEnabled()) {
25325
25715
  const hint = choice.hint ? `${pad} ${paint(choice.hint, DIM4)}` : "";
25326
25716
  lines.push(` ${cursor} ${marker} ${here ? paint(label2, BOLD2) : label2}${hint}`);
25327
25717
  });
25328
- const keys = mode2 === "multi" ? "\u2191\u2193 move \xB7 space toggle \xB7 enter confirm" : "\u2191\u2193 move \xB7 enter confirm";
25718
+ const keys = mode2 === "multi" ? "\u2191\u2193 move \xB7 space or number toggle \xB7 enter confirm" : "\u2191\u2193 move \xB7 number jumps \xB7 enter confirm";
25329
25719
  lines.push(` ${paint(state.hint ?? keys, state.hint ? GREEN3 : DIM4)}`);
25330
25720
  return lines;
25331
25721
  }
@@ -25339,7 +25729,7 @@ function runSelect(opts) {
25339
25729
  cursor: initialIdx[0] ?? 0,
25340
25730
  chosen: new Set(mode2 === "single" ? [initialIdx[0] ?? 0] : initialIdx)
25341
25731
  };
25342
- return new Promise((resolve4) => {
25732
+ return new Promise((resolve5) => {
25343
25733
  let painted = 0;
25344
25734
  let settled = false;
25345
25735
  const draw = () => {
@@ -25387,7 +25777,7 @@ function runSelect(opts) {
25387
25777
  if (settled) return;
25388
25778
  settled = true;
25389
25779
  restore();
25390
- resolve4(value);
25780
+ resolve5(value);
25391
25781
  };
25392
25782
  readline2.emitKeypressEvents(input);
25393
25783
  input.setRawMode(true);
@@ -25408,9 +25798,9 @@ async function askLine(question, io = {}) {
25408
25798
  output: io.output ?? process.stdout
25409
25799
  });
25410
25800
  try {
25411
- return await new Promise((resolve4) => {
25412
- rl.question(question).then((a) => resolve4(a.trim())).catch(() => resolve4(null));
25413
- rl.once("close", () => setImmediate(() => resolve4(null)));
25801
+ return await new Promise((resolve5) => {
25802
+ rl.question(question).then((a) => resolve5(a.trim())).catch(() => resolve5(null));
25803
+ rl.once("close", () => setImmediate(() => resolve5(null)));
25414
25804
  });
25415
25805
  } finally {
25416
25806
  rl.close();
@@ -25493,7 +25883,7 @@ async function promptMultiSelect(question, choices, fallback) {
25493
25883
  // src/lib/remote-config.ts
25494
25884
  var import_node_fs50 = require("node:fs");
25495
25885
  var import_promises16 = require("node:fs/promises");
25496
- var import_node_path32 = require("node:path");
25886
+ var import_node_path34 = require("node:path");
25497
25887
  var import_yaml5 = __toESM(require_dist());
25498
25888
  var IGNORE_RIDER = "verityignore";
25499
25889
  async function fetchRemoteSetup(opts) {
@@ -25571,7 +25961,7 @@ async function adoptRemoteSetup(found, opts) {
25571
25961
  }
25572
25962
  async function writeOut(relative2, body) {
25573
25963
  const target = projectPath(relative2);
25574
- await (0, import_promises16.mkdir)((0, import_node_path32.dirname)(target), { recursive: true });
25964
+ await (0, import_promises16.mkdir)((0, import_node_path34.dirname)(target), { recursive: true });
25575
25965
  await (0, import_promises16.writeFile)(target, body);
25576
25966
  }
25577
25967
  function describeRemote(found) {
@@ -25687,16 +26077,16 @@ async function runOptionalAuth(resolution, opts = {}) {
25687
26077
  }
25688
26078
  }
25689
26079
  function resolveDataDir2() {
25690
- const candidates = [
25691
- (0, import_node_path33.join)(__dirname, "..", "data"),
26080
+ const candidates2 = [
26081
+ (0, import_node_path35.join)(__dirname, "..", "data"),
25692
26082
  // installed: node_modules/@codacy/verity-cli/data
25693
- (0, import_node_path33.join)(__dirname, "..", "..", "data"),
26083
+ (0, import_node_path35.join)(__dirname, "..", "..", "data"),
25694
26084
  // edge case: nested resolution
25695
- (0, import_node_path33.join)(process.cwd(), "cli", "data")
26085
+ (0, import_node_path35.join)(process.cwd(), "cli", "data")
25696
26086
  // local dev: running from repo root
25697
26087
  ];
25698
- for (const candidate of candidates) {
25699
- if ((0, import_node_fs51.existsSync)((0, import_node_path33.join)(candidate, "skills"))) {
26088
+ for (const candidate of candidates2) {
26089
+ if ((0, import_node_fs51.existsSync)((0, import_node_path35.join)(candidate, "skills"))) {
25700
26090
  return candidate;
25701
26091
  }
25702
26092
  }
@@ -25714,7 +26104,7 @@ async function skillIsCurrent(src, dest) {
25714
26104
  const walk2 = (d, prefix) => {
25715
26105
  for (const e of (0, import_node_fs51.readdirSync)(d, { withFileTypes: true })) {
25716
26106
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
25717
- if (e.isDirectory()) walk2((0, import_node_path33.join)(d, e.name), rel);
26107
+ if (e.isDirectory()) walk2((0, import_node_path35.join)(d, e.name), rel);
25718
26108
  else if (e.isFile()) out.push(rel);
25719
26109
  }
25720
26110
  };
@@ -25725,8 +26115,8 @@ async function skillIsCurrent(src, dest) {
25725
26115
  const shipped = list2(src);
25726
26116
  if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
25727
26117
  for (const rel of shipped) {
25728
- const a = await (0, import_promises17.readFile)((0, import_node_path33.join)(src, rel), "utf-8");
25729
- const b = await (0, import_promises17.readFile)((0, import_node_path33.join)(dest, rel), "utf-8");
26118
+ const a = await (0, import_promises17.readFile)((0, import_node_path35.join)(src, rel), "utf-8");
26119
+ const b = await (0, import_promises17.readFile)((0, import_node_path35.join)(dest, rel), "utf-8");
25730
26120
  if (a !== b) return false;
25731
26121
  }
25732
26122
  return true;
@@ -26001,12 +26391,12 @@ async function handoffToSetup(enabled, claudeInstalled) {
26001
26391
  async function installSkills(force, step) {
26002
26392
  step("Installing skills");
26003
26393
  const dataDir = resolveDataDir2();
26004
- const skillsSource = (0, import_node_path33.join)(dataDir, "skills");
26394
+ const skillsSource = (0, import_node_path35.join)(dataDir, "skills");
26005
26395
  const skillsDest = ".claude/skills";
26006
26396
  let skillsInstalled = 0;
26007
26397
  for (const skill of SKILLS) {
26008
- const src = (0, import_node_path33.join)(skillsSource, skill);
26009
- const dest = (0, import_node_path33.join)(skillsDest, skill);
26398
+ const src = (0, import_node_path35.join)(skillsSource, skill);
26399
+ const dest = (0, import_node_path35.join)(skillsDest, skill);
26010
26400
  if (!(0, import_node_fs51.existsSync)(src)) {
26011
26401
  printWarn(` Skill data not found: ${skill}`);
26012
26402
  continue;
@@ -26109,12 +26499,16 @@ function registerInitCommand(program2) {
26109
26499
  program2.command("init").alias("setup").description("Set up Verity in the current project (asks the setup questions, then hands off to /verity-setup)").option("--force", "Reinstall the skills even when they are already up to date (hooks are always reconciled)").option("-y, --yes", "Take the recommended answer for every question (no prompts)").option("--no-setup", "Skip the /verity-setup handoff at the end").option(
26110
26500
  "--plugin-mode",
26111
26501
  "The Claude Code plugin owns the skills and hooks: install neither, and remove any this project already has"
26502
+ ).option(
26503
+ "--no-plugin",
26504
+ "Ignore any Claude Code plugin here and wire this project's own skills and hooks"
26112
26505
  ).option("--no-adopt", "Don't offer this repository's existing Standard from the service; synthesize a new one").action(async (opts) => {
26113
26506
  const force = opts.force ?? false;
26114
26507
  const wantsHandoff = opts.setup !== false;
26115
26508
  const wantsAdopt = opts.adopt !== false;
26116
26509
  const defaultsOnly = (opts.yes ?? false) || !interactive();
26117
- const pluginMode = opts.pluginMode ?? pluginActiveHere();
26510
+ const staleMarker = clearStalePluginMarker();
26511
+ const pluginMode = opts.plugin === false ? false : opts.pluginMode ?? pluginActiveHere();
26118
26512
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
26119
26513
  const isProject = projectMarkers.some((m) => (0, import_node_fs51.existsSync)(m));
26120
26514
  if (!isProject) {
@@ -26131,6 +26525,26 @@ function registerInitCommand(program2) {
26131
26525
  } else {
26132
26526
  printPhase(1, 2, "this machine", "prerequisites \xB7 skills \xB7 hooks \xB7 sign-in");
26133
26527
  }
26528
+ if (staleMarker) {
26529
+ printInfo(`Cleared a stale plugin marker \u2014 ${staleMarker.pluginRoot} is no longer installed.`);
26530
+ }
26531
+ const install = pluginMode ? activePluginInstall() : null;
26532
+ if (pluginMode) {
26533
+ const where = install ? ` \u2014 ${install.pluginRoot}${install.version ? ` (v${install.version})` : ""}` : "";
26534
+ printInfo(`Hooks here: the Verity Claude Code plugin${where}.`);
26535
+ printInfo(" Skills and hooks come from the plugin, so this run installs neither \u2014 and removes");
26536
+ printInfo(" any this project still carries from an earlier npm install.");
26537
+ printInfo(" It still does the half the plugin cannot: your Standard and analysis config, the");
26538
+ printInfo(" knowledge base, CLAUDE.md, .gitignore, the review moments, and sign-in.");
26539
+ printInfo(" Want this project to own its own copy instead? Uninstall the plugin, or use --no-plugin.");
26540
+ } else if (opts.plugin === false && pluginActiveHere()) {
26541
+ printWarn("Hooks here: this project's .claude/settings.json (--no-plugin).");
26542
+ printWarn(" The Verity plugin is active on this machine and would otherwise own them. Both sets");
26543
+ printWarn(" will fire; the settings.json copy stands down at run time so each turn is gated once.");
26544
+ } else {
26545
+ printInfo("Hooks here: this project's .claude/settings.json (no Verity Claude Code plugin is active).");
26546
+ }
26547
+ console.log("");
26134
26548
  const TOTAL_STEPS = 9;
26135
26549
  let stepNo = 0;
26136
26550
  const step = (label2) => {
@@ -26162,7 +26576,7 @@ function registerInitCommand(program2) {
26162
26576
  printInfo(` intensity: ${intensity} \xB7 moments: ${moments.join(", ") || "none"} (no questions asked)`);
26163
26577
  }
26164
26578
  await scaffoldProject(step, defaultsOnly);
26165
- const globalVerityDir = (0, import_node_path33.join)(process.env.HOME ?? "", ".verity");
26579
+ const globalVerityDir = (0, import_node_path35.join)(process.env.HOME ?? "", ".verity");
26166
26580
  await (0, import_promises17.mkdir)(globalVerityDir, { recursive: true });
26167
26581
  console.log("");
26168
26582
  step("Wiring Claude Code hooks");
@@ -26170,9 +26584,18 @@ function registerInitCommand(program2) {
26170
26584
  ...moments.includes("pre-commit") ? ["commit"] : [],
26171
26585
  ...moments.includes("pre-push") ? ["push"] : []
26172
26586
  ];
26173
- writeProjectConfig({ git_moments: gitMoments });
26587
+ const existingMoments = readProjectConfig();
26588
+ const keepExisting = defaultsOnly && existingMoments.git_moments_source === "user";
26589
+ if (keepExisting) {
26590
+ printInfo(
26591
+ ` Keeping the git moments you set: ${existingMoments.git_moments.join(", ") || "none"} (this run asked no questions, so it does not overrule them).`
26592
+ );
26593
+ } else {
26594
+ writeProjectConfig({ git_moments: gitMoments, git_moments_source: "init" });
26595
+ }
26596
+ const effectiveMoments = keepExisting ? existingMoments.git_moments : gitMoments;
26174
26597
  if (pluginMode) {
26175
- await adoptPluginWiring(gitMoments, moments);
26598
+ await adoptPluginWiring(effectiveMoments, moments);
26176
26599
  } else {
26177
26600
  await reconcileOwnWiring(moments);
26178
26601
  }
@@ -26244,7 +26667,7 @@ function registerInitCommand(program2) {
26244
26667
  ...telemetryChoice ? { telemetry: telemetryChoice } : {},
26245
26668
  init: {
26246
26669
  completed_at: (/* @__PURE__ */ new Date()).toISOString(),
26247
- cli_version: true ? "0.32.0" : "dev"
26670
+ cli_version: true ? "0.32.1-experimental.f2cf829" : "dev"
26248
26671
  }
26249
26672
  });
26250
26673
  } catch (err) {
@@ -26284,7 +26707,7 @@ function registerInitCommand(program2) {
26284
26707
 
26285
26708
  // src/commands/uninstall.ts
26286
26709
  var import_node_fs52 = require("node:fs");
26287
- var import_node_path34 = require("node:path");
26710
+ var import_node_path36 = require("node:path");
26288
26711
  var SKILL_NAMES = [
26289
26712
  "verity-setup",
26290
26713
  "verity-analyze",
@@ -26303,7 +26726,7 @@ function registerUninstallCommand(program2) {
26303
26726
  const actions = [];
26304
26727
  const skillsRoot = projectPath(".claude/skills");
26305
26728
  for (const name of SKILL_NAMES) {
26306
- const dir = (0, import_node_path34.join)(skillsRoot, name);
26729
+ const dir = (0, import_node_path36.join)(skillsRoot, name);
26307
26730
  if ((0, import_node_fs52.existsSync)(dir)) {
26308
26731
  actions.push({
26309
26732
  label: `Remove .claude/skills/${name}/`,
@@ -26349,7 +26772,7 @@ function registerUninstallCommand(program2) {
26349
26772
  }
26350
26773
  });
26351
26774
  const home = process.env.HOME ?? "";
26352
- const globalVerityDir = (0, import_node_path34.join)(home, ".verity");
26775
+ const globalVerityDir = (0, import_node_path36.join)(home, ".verity");
26353
26776
  if (purgeGlobal && (0, import_node_fs52.existsSync)(globalVerityDir)) {
26354
26777
  actions.push({
26355
26778
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
@@ -26548,7 +26971,7 @@ function registerTaskCommands(program2) {
26548
26971
 
26549
26972
  // src/commands/reset.ts
26550
26973
  var import_node_fs53 = require("node:fs");
26551
- var import_node_path35 = require("node:path");
26974
+ var import_node_path37 = require("node:path");
26552
26975
  function registerResetCommand(program2) {
26553
26976
  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) => {
26554
26977
  const globals = program2.opts();
@@ -26589,7 +27012,7 @@ function registerResetCommand(program2) {
26589
27012
  for (const entry of (0, import_node_fs53.readdirSync)(cacheDir)) {
26590
27013
  if (entry.startsWith("pending-")) {
26591
27014
  try {
26592
- (0, import_node_fs53.unlinkSync)((0, import_node_path35.join)(cacheDir, entry));
27015
+ (0, import_node_fs53.unlinkSync)((0, import_node_path37.join)(cacheDir, entry));
26593
27016
  purged++;
26594
27017
  } catch {
26595
27018
  }
@@ -26616,7 +27039,7 @@ function registerResetCommand(program2) {
26616
27039
  if ((0, import_node_fs53.existsSync)(logsDir)) {
26617
27040
  for (const entry of (0, import_node_fs53.readdirSync)(logsDir)) {
26618
27041
  try {
26619
- (0, import_node_fs53.unlinkSync)((0, import_node_path35.join)(logsDir, entry));
27042
+ (0, import_node_fs53.unlinkSync)((0, import_node_path37.join)(logsDir, entry));
26620
27043
  } catch {
26621
27044
  }
26622
27045
  }
@@ -26924,8 +27347,8 @@ function registerTelemetryCommands(program2) {
26924
27347
  }
26925
27348
 
26926
27349
  // src/cli.ts
26927
- program.name("verity").description("CLI for Verity quality gate service").version("0.32.0").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) => {
26928
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.32.0");
27350
+ program.name("verity").description("CLI for Verity quality gate service").version("0.32.1-experimental.f2cf829").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) => {
27351
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.32.1-experimental.f2cf829");
26929
27352
  setUserNamedServiceUrl(program.opts().serviceUrl);
26930
27353
  try {
26931
27354
  await foldLegacyLocalCredential();