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

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 +235 -52
  2. package/bin/verity.js +813 -371
  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,17 +12871,109 @@ 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));
12856
12882
  } catch {
12857
12883
  }
12858
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 entryAppliesHere(entry, here) {
12931
+ const e = entry;
12932
+ const scope2 = typeof e?.scope === "string" ? e.scope : "user";
12933
+ if (scope2 === "user") return true;
12934
+ const forProject = typeof e?.projectPath === "string" ? realpathOr(e.projectPath) : "";
12935
+ return forProject === here;
12936
+ }
12937
+ function registrySays(pluginRoot) {
12938
+ const plugins = readJsonFile((0, import_node_path7.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
12939
+ if (!plugins || typeof plugins !== "object") return "unverified";
12940
+ const want = (0, import_node_path7.resolve)(pluginRoot);
12941
+ const here = realpathOr(repoRoot());
12942
+ const markets = marketplaceLocations();
12943
+ for (const [key, value] of Object.entries(plugins)) {
12944
+ const at = key.lastIndexOf("@");
12945
+ const source = at > 0 ? markets.get(key.slice(at + 1)) : void 0;
12946
+ const applicable = (Array.isArray(value) ? value : []).filter((entry) => entryAppliesHere(entry, here));
12947
+ const claims = applicable.some((entry) => {
12948
+ const installPath = entry?.installPath;
12949
+ return typeof installPath === "string" && (0, import_node_path7.resolve)(installPath) === want;
12950
+ }) || source !== void 0 && applicable.length > 0 && isWithin(want, source);
12951
+ if (claims) {
12952
+ if (enabledPluginSetting(key) === false) return "gone";
12953
+ return (0, import_node_fs6.existsSync)(pluginRoot) ? "live" : "gone";
12954
+ }
12955
+ }
12956
+ const managed = (0, import_node_path7.resolve)((0, import_node_path7.join)(claudeConfigDir(), "plugins", "cache"));
12957
+ return want === managed || want.startsWith(managed + import_node_path7.sep) ? "gone" : "unverified";
12958
+ }
12959
+ var _live = /* @__PURE__ */ new Map();
12960
+ function pluginLiveness(pluginRoot) {
12961
+ const cached2 = _live.get(pluginRoot);
12962
+ if (cached2 !== void 0) return cached2;
12963
+ const said = registrySays(pluginRoot);
12964
+ _live.set(pluginRoot, said);
12965
+ return said;
12966
+ }
12967
+ function clearStalePluginMarker() {
12968
+ const marker = readMarker();
12969
+ if (!marker || pluginLiveness(marker.plugin_root) !== "gone") return null;
12970
+ try {
12971
+ (0, import_node_fs6.rmSync)(markerPath(), { force: true });
12972
+ } catch {
12973
+ return null;
12974
+ }
12975
+ return { pluginRoot: marker.plugin_root, version: marker.version };
12976
+ }
12859
12977
  function shouldDeferToPlugin(sessionId) {
12860
12978
  if (isPluginInvocation()) {
12861
12979
  recordPluginOwnership(sessionId);
@@ -12863,13 +12981,44 @@ function shouldDeferToPlugin(sessionId) {
12863
12981
  }
12864
12982
  const marker = readMarker();
12865
12983
  if (!marker) return false;
12866
- if (!(0, import_node_fs6.existsSync)(marker.plugin_root)) return false;
12984
+ const liveness = pluginLiveness(marker.plugin_root);
12985
+ if (liveness === "gone") return false;
12867
12986
  if (sessionId && marker.session_id) return sessionId === marker.session_id;
12868
- return Math.floor(Date.now() / 1e3) - marker.ts < MARKER_TTL_SECONDS;
12987
+ logToFileOnly(
12988
+ // JSON-encoded: `plugin_root` is repository-controlled, and raw it could
12989
+ // write forged lines into the one place someone looks to understand why
12990
+ // the gate did what it did. (`readMarker` also refuses control characters.)
12991
+ `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.`
12992
+ );
12993
+ return false;
12869
12994
  }
12870
12995
  function pluginActiveHere() {
12996
+ return activePluginInstall() !== null;
12997
+ }
12998
+ function registeredVerityPlugin() {
12999
+ const plugins = readJsonFile((0, import_node_path7.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
13000
+ if (!plugins || typeof plugins !== "object") return null;
13001
+ const here = realpathOr(repoRoot());
13002
+ for (const [key, value] of Object.entries(plugins)) {
13003
+ const at = key.lastIndexOf("@");
13004
+ if ((at > 0 ? key.slice(0, at) : key) !== "verity") continue;
13005
+ if (enabledPluginSetting(key) === false) continue;
13006
+ for (const entry of Array.isArray(value) ? value : []) {
13007
+ const e = entry;
13008
+ const installPath = typeof e.installPath === "string" ? e.installPath : "";
13009
+ if (!installPath || !(0, import_node_fs6.existsSync)(installPath)) continue;
13010
+ if (!entryAppliesHere(entry, here)) continue;
13011
+ return { pluginRoot: installPath, version: typeof e.version === "string" ? e.version : null };
13012
+ }
13013
+ }
13014
+ return null;
13015
+ }
13016
+ function activePluginInstall() {
12871
13017
  const marker = readMarker();
12872
- return !!marker && (0, import_node_fs6.existsSync)(marker.plugin_root);
13018
+ if (marker && pluginLiveness(marker.plugin_root) === "live") {
13019
+ return { pluginRoot: marker.plugin_root, version: marker.version };
13020
+ }
13021
+ return registeredVerityPlugin();
12873
13022
  }
12874
13023
  function deferredToPlugin(command, sessionId) {
12875
13024
  if (!shouldDeferToPlugin(sessionId)) return false;
@@ -12879,21 +13028,63 @@ function deferredToPlugin(command, sessionId) {
12879
13028
  return true;
12880
13029
  }
12881
13030
 
13031
+ // src/lib/hook-wiring.ts
13032
+ function carriesAnyVerityHook(s) {
13033
+ return s.stop || s.intent || s.baseline || s.compact || s.sessionEnd || s.guard;
13034
+ }
13035
+ async function resolveHookWiring() {
13036
+ const settings = await checkAllVerityHooks();
13037
+ const config = readProjectConfig();
13038
+ const gitMoments = { source: config.source, path: config.path };
13039
+ if (!pluginActiveHere()) {
13040
+ return { source: "settings", status: settings, settings, duplicateSettingsHooks: false, gitMoments };
13041
+ }
13042
+ const status = {
13043
+ stop: true,
13044
+ intent: true,
13045
+ baseline: true,
13046
+ compact: true,
13047
+ sessionEnd: true,
13048
+ guard: true,
13049
+ guardOn: config.git_moments
13050
+ };
13051
+ return {
13052
+ source: "plugin",
13053
+ status,
13054
+ settings,
13055
+ duplicateSettingsHooks: carriesAnyVerityHook(settings),
13056
+ gitMoments
13057
+ };
13058
+ }
13059
+
12882
13060
  // src/commands/hooks.ts
12883
13061
  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);
13062
+ function parseMomentSelection(raw) {
13063
+ const parts = raw.split(",").map((s) => s.trim()).filter(Boolean);
13064
+ if (parts.length === 1 && parts[0].toLowerCase() === "none") return { moments: [] };
13065
+ if (parts.length === 0) {
13066
+ return { error: `--moments was empty. Pass one or more of ${ALL_MOMENTS.join(", ")} \u2014 or "none" to gate nothing.` };
13067
+ }
13068
+ const unknown = parts.filter((p) => !ALL_MOMENTS.includes(p));
13069
+ if (unknown.length > 0) {
13070
+ return {
13071
+ error: `--moments: unrecognised ${unknown.length === 1 ? "moment" : "moments"} ${unknown.map((u) => `"${u}"`).join(", ")}. Valid: ${ALL_MOMENTS.join(", ")} \u2014 or "none" to gate nothing.`
13072
+ };
12888
13073
  }
12889
- return ALL_MOMENTS.filter((m) => seen.has(m));
13074
+ const seen = new Set(parts);
13075
+ return { moments: ALL_MOMENTS.filter((m) => seen.has(m)) };
12890
13076
  }
12891
13077
  function registerHooksCommands(program2) {
12892
13078
  const hooks = program2.command("hooks").description("Manage Claude Code hook wiring");
12893
13079
  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
13080
  const force = opts.force ?? false;
12895
13081
  if (opts.moments != null) {
12896
- const moments = parseMoments2(opts.moments);
13082
+ const parsed = parseMomentSelection(opts.moments);
13083
+ if ("error" in parsed) {
13084
+ printError(parsed.error);
13085
+ process.exit(1);
13086
+ }
13087
+ const moments = parsed.moments;
12897
13088
  const gitMoments = [
12898
13089
  ...moments.includes("pre-commit") ? ["commit"] : [],
12899
13090
  ...moments.includes("pre-push") ? ["push"] : []
@@ -12954,20 +13145,22 @@ function registerHooksCommands(program2) {
12954
13145
  printInfo(" SessionStart hook: verity baseline capture");
12955
13146
  });
12956
13147
  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;
13148
+ const wiring = await resolveHookWiring();
13149
+ const status = wiring.status;
13150
+ if (wiring.source === "plugin") {
13151
+ const moments = status.guardOn;
13152
+ const inherited = wiring.gitMoments.source === "main-worktree" && wiring.gitMoments.path ? ` (inherited from the main worktree: ${wiring.gitMoments.path})` : "";
12960
13153
  printInfo("Wired by the Verity Claude Code plugin (not .claude/settings.json):");
12961
13154
  printInfo(" Stop hook (verity analyze): installed");
12962
13155
  printInfo(" Intent hook (verity intent capture): installed");
12963
13156
  printInfo(" Baseline hook (verity baseline capture): installed");
12964
13157
  printInfo(
12965
- ` Git-moment gate (verity guard): ${moments.length ? `installed [${moments.join(", ")}]` : "wired but gating nothing"}`
13158
+ ` Git-moment gate (verity guard): ${moments.length ? `installed [${moments.join(", ")}]${inherited}` : "wired but gating nothing"}`
12966
13159
  );
12967
13160
  if (!moments.length) {
12968
13161
  printInfo(' Enable it with "verity config git-moments commit,push".');
12969
13162
  }
12970
- if (status.stop || status.intent || status.baseline || status.guard) {
13163
+ if (wiring.duplicateSettingsHooks) {
12971
13164
  printWarn(" Duplicate hooks also exist in .claude/settings.json. They stand down at run time,");
12972
13165
  printWarn(' but remove them with "verity init --plugin-mode" so the wiring says what it does.');
12973
13166
  }
@@ -13352,8 +13545,8 @@ function isCommandOnlyTurn(input) {
13352
13545
  // src/lib/context-identity.ts
13353
13546
  var import_node_crypto2 = require("node:crypto");
13354
13547
  var import_node_fs8 = require("node:fs");
13355
- var import_node_os2 = require("node:os");
13356
- var import_node_path6 = require("node:path");
13548
+ var import_node_os3 = require("node:os");
13549
+ var import_node_path8 = require("node:path");
13357
13550
  var SHARED_SENTINELS = /* @__PURE__ */ new Set([
13358
13551
  "",
13359
13552
  "-",
@@ -13407,13 +13600,13 @@ function contextIdentity(input) {
13407
13600
  }
13408
13601
  function verityHome() {
13409
13602
  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");
13603
+ return override && override.trim() ? (0, import_node_path8.resolve)(override) : (0, import_node_path8.join)((0, import_node_os3.homedir)(), ".verity");
13411
13604
  }
13412
13605
  function dossierDir(identity) {
13413
- return (0, import_node_path6.join)(verityHome(), "sessions", identity.userKey, identity.treeKey, identity.sessionKey);
13606
+ return (0, import_node_path8.join)(verityHome(), "sessions", identity.userKey, identity.treeKey, identity.sessionKey);
13414
13607
  }
13415
13608
  function treeDir(identity) {
13416
- return (0, import_node_path6.join)(verityHome(), "sessions", identity.userKey, identity.treeKey);
13609
+ return (0, import_node_path8.join)(verityHome(), "sessions", identity.userKey, identity.treeKey);
13417
13610
  }
13418
13611
  function scopeIdentity(token, sessionId) {
13419
13612
  const t = (token ?? "").trim();
@@ -13429,7 +13622,7 @@ function sessionScopeKey(token, sessionId) {
13429
13622
  // src/lib/task-context-buffer.ts
13430
13623
  var import_promises6 = require("node:fs/promises");
13431
13624
  var import_node_fs9 = require("node:fs");
13432
- var import_node_path7 = require("node:path");
13625
+ var import_node_path9 = require("node:path");
13433
13626
  var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
13434
13627
  var MAX_BUFFER_BYTES = 500 * 1024;
13435
13628
  var MAX_PROMPT_CHARS = 2e3;
@@ -13507,7 +13700,7 @@ async function cleanupTaskContextBuffers() {
13507
13700
  const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
13508
13701
  for (const file of files) {
13509
13702
  if (!file.endsWith(".jsonl")) continue;
13510
- const filePath = (0, import_node_path7.join)(TASK_CONTEXT_DIR, file);
13703
+ const filePath = (0, import_node_path9.join)(TASK_CONTEXT_DIR, file);
13511
13704
  try {
13512
13705
  const stats = await (0, import_promises6.stat)(filePath);
13513
13706
  if (stats.mtimeMs < cutoffMs) {
@@ -13521,7 +13714,7 @@ async function cleanupTaskContextBuffers() {
13521
13714
  }
13522
13715
  function bufferPath(taskId) {
13523
13716
  const safe = taskId.replace(/[^a-zA-Z0-9_-]/g, "");
13524
- return (0, import_node_path7.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
13717
+ return (0, import_node_path9.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
13525
13718
  }
13526
13719
  async function appendEntry(taskId, entry) {
13527
13720
  try {
@@ -13547,7 +13740,7 @@ async function appendEntry(taskId, entry) {
13547
13740
  // src/lib/memory-retrieval.ts
13548
13741
  var import_promises7 = require("node:fs/promises");
13549
13742
  var import_node_fs10 = require("node:fs");
13550
- var import_node_path8 = require("node:path");
13743
+ var import_node_path10 = require("node:path");
13551
13744
  var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
13552
13745
  var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
13553
13746
  var DEFAULT_BUDGET_TOKENS = 2e3;
@@ -13645,14 +13838,14 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
13645
13838
  const promptTokens = tokenize(promptText);
13646
13839
  const nodes = [];
13647
13840
  for (const domain of DOMAINS) {
13648
- const domainDir = (0, import_node_path8.join)(memoryDir(), domain);
13841
+ const domainDir = (0, import_node_path10.join)(memoryDir(), domain);
13649
13842
  if (!(0, import_node_fs10.existsSync)(domainDir)) continue;
13650
13843
  try {
13651
13844
  const files = await (0, import_promises7.readdir)(domainDir);
13652
13845
  for (const file of files) {
13653
13846
  if (!file.endsWith(".md")) continue;
13654
13847
  try {
13655
- const content = await (0, import_promises7.readFile)((0, import_node_path8.join)(domainDir, file), "utf-8");
13848
+ const content = await (0, import_promises7.readFile)((0, import_node_path10.join)(domainDir, file), "utf-8");
13656
13849
  const { fm, body } = parseFrontmatter(content);
13657
13850
  if (fm.status && fm.status !== "active") continue;
13658
13851
  nodes.push({
@@ -13712,26 +13905,26 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
13712
13905
  // src/lib/memory-sync.ts
13713
13906
  var import_promises8 = require("node:fs/promises");
13714
13907
  var import_node_fs12 = require("node:fs");
13715
- var import_node_path10 = require("node:path");
13908
+ var import_node_path12 = require("node:path");
13716
13909
  var import_node_crypto3 = require("node:crypto");
13717
13910
 
13718
13911
  // src/lib/safe-path.ts
13719
13912
  var import_node_fs11 = require("node:fs");
13720
- var import_node_path9 = require("node:path");
13913
+ var import_node_path11 = require("node:path");
13721
13914
  function resolveInside(baseDir, candidate) {
13722
13915
  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;
13916
+ if ((0, import_node_path11.isAbsolute)(candidate)) return null;
13917
+ const baseAbs = (0, import_node_path11.resolve)(baseDir);
13918
+ const full = (0, import_node_path11.resolve)(baseAbs, candidate);
13919
+ const baseSep = baseAbs.endsWith(import_node_path11.sep) ? baseAbs : baseAbs + import_node_path11.sep;
13727
13920
  if (full !== baseAbs && !full.startsWith(baseSep)) return null;
13728
13921
  try {
13729
13922
  if ((0, import_node_fs11.existsSync)(baseAbs)) {
13730
13923
  const realBase = (0, import_node_fs11.realpathSync)(baseAbs);
13731
- const realBaseSep = realBase.endsWith(import_node_path9.sep) ? realBase : realBase + import_node_path9.sep;
13924
+ const realBaseSep = realBase.endsWith(import_node_path11.sep) ? realBase : realBase + import_node_path11.sep;
13732
13925
  let probe = full;
13733
13926
  while (!(0, import_node_fs11.existsSync)(probe)) {
13734
- const parent = (0, import_node_path9.dirname)(probe);
13927
+ const parent = (0, import_node_path11.dirname)(probe);
13735
13928
  if (parent === probe) break;
13736
13929
  probe = parent;
13737
13930
  }
@@ -13812,16 +14005,16 @@ var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
13812
14005
  async function ensureMemoryDir() {
13813
14006
  await (0, import_promises8.mkdir)(memoryDir2(), { recursive: true });
13814
14007
  for (const domain of DOMAINS2) {
13815
- await (0, import_promises8.mkdir)((0, import_node_path10.join)(memoryDir2(), domain), { recursive: true });
14008
+ await (0, import_promises8.mkdir)((0, import_node_path12.join)(memoryDir2(), domain), { recursive: true });
13816
14009
  }
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);
14010
+ if (!(0, import_node_fs12.existsSync)((0, import_node_path12.join)(memoryDir2(), "SCHEMA.md"))) {
14011
+ await (0, import_promises8.writeFile)((0, import_node_path12.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
13819
14012
  }
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");
14013
+ if (!(0, import_node_fs12.existsSync)((0, import_node_path12.join)(memoryDir2(), "index.md"))) {
14014
+ 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
14015
  }
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");
14016
+ if (!(0, import_node_fs12.existsSync)((0, import_node_path12.join)(memoryDir2(), "log.md"))) {
14017
+ await (0, import_promises8.writeFile)((0, import_node_path12.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
13825
14018
  }
13826
14019
  }
13827
14020
  async function buildManifest() {
@@ -13830,14 +14023,14 @@ async function buildManifest() {
13830
14023
  }
13831
14024
  const nodes = [];
13832
14025
  for (const domain of DOMAINS2) {
13833
- const domainDir = (0, import_node_path10.join)(memoryDir2(), domain);
14026
+ const domainDir = (0, import_node_path12.join)(memoryDir2(), domain);
13834
14027
  if (!(0, import_node_fs12.existsSync)(domainDir)) continue;
13835
14028
  try {
13836
14029
  const files = await (0, import_promises8.readdir)(domainDir);
13837
14030
  for (const file of files) {
13838
14031
  if (!file.endsWith(".md")) continue;
13839
14032
  const filePath = `${domain}/${file}`;
13840
- const fullPath = (0, import_node_path10.join)(memoryDir2(), filePath);
14033
+ const fullPath = (0, import_node_path12.join)(memoryDir2(), filePath);
13841
14034
  try {
13842
14035
  const content = await (0, import_promises8.readFile)(fullPath, "utf-8");
13843
14036
  const hash = (0, import_node_crypto3.createHash)("sha256").update(content).digest("hex").slice(0, 16);
@@ -13850,13 +14043,13 @@ async function buildManifest() {
13850
14043
  }
13851
14044
  let indexHash = null;
13852
14045
  try {
13853
- const indexContent = await (0, import_promises8.readFile)((0, import_node_path10.join)(memoryDir2(), "index.md"), "utf-8");
14046
+ const indexContent = await (0, import_promises8.readFile)((0, import_node_path12.join)(memoryDir2(), "index.md"), "utf-8");
13854
14047
  indexHash = `sha256:${(0, import_node_crypto3.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
13855
14048
  } catch {
13856
14049
  }
13857
14050
  let logLength = 0;
13858
14051
  try {
13859
- const logContent = await (0, import_promises8.readFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), "utf-8");
14052
+ const logContent = await (0, import_promises8.readFile)((0, import_node_path12.join)(memoryDir2(), "log.md"), "utf-8");
13860
14053
  logLength = logContent.split("\n").length;
13861
14054
  } catch {
13862
14055
  }
@@ -13869,13 +14062,13 @@ async function readOnDiskNodes() {
13869
14062
  const out = /* @__PURE__ */ new Map();
13870
14063
  if (!(0, import_node_fs12.existsSync)(memoryDir2())) return out;
13871
14064
  for (const domain of DOMAINS2) {
13872
- const domainDir = (0, import_node_path10.join)(memoryDir2(), domain);
14065
+ const domainDir = (0, import_node_path12.join)(memoryDir2(), domain);
13873
14066
  if (!(0, import_node_fs12.existsSync)(domainDir)) continue;
13874
14067
  try {
13875
14068
  for (const file of await (0, import_promises8.readdir)(domainDir)) {
13876
14069
  if (!file.endsWith(".md")) continue;
13877
14070
  try {
13878
- out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path10.join)(domainDir, file), "utf-8")));
14071
+ out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path12.join)(domainDir, file), "utf-8")));
13879
14072
  } catch {
13880
14073
  }
13881
14074
  }
@@ -13921,7 +14114,7 @@ async function computeEditedNodeUploads() {
13921
14114
  const uploads = [];
13922
14115
  for (const [path, prevHash] of prev) {
13923
14116
  if (prevHash == null) continue;
13924
- const full = (0, import_node_path10.join)(memoryDir2(), path);
14117
+ const full = (0, import_node_path12.join)(memoryDir2(), path);
13925
14118
  if (!(0, import_node_fs12.existsSync)(full)) continue;
13926
14119
  let content;
13927
14120
  try {
@@ -13958,8 +14151,8 @@ async function applyMemoryWrites(writes, opts = {}) {
13958
14151
  const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
13959
14152
  for (const n of notes) logLines.push(` - ${n}`);
13960
14153
  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");
14154
+ 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";
14155
+ await (0, import_promises8.writeFile)((0, import_node_path12.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
13963
14156
  } catch {
13964
14157
  }
13965
14158
  await recordSyncedNodePaths();
@@ -13991,7 +14184,7 @@ async function applyOneWrite(write, treePaths) {
13991
14184
  return { written: false, notes };
13992
14185
  }
13993
14186
  }
13994
- await (0, import_promises8.mkdir)((0, import_node_path10.dirname)(fullPath), { recursive: true });
14187
+ await (0, import_promises8.mkdir)((0, import_node_path12.dirname)(fullPath), { recursive: true });
13995
14188
  await (0, import_promises8.writeFile)(fullPath, content);
13996
14189
  return { written: true, notes };
13997
14190
  }
@@ -14032,7 +14225,7 @@ async function regenerateIndex() {
14032
14225
  ];
14033
14226
  let totalNodes = 0;
14034
14227
  for (const domain of DOMAINS2.filter((d) => d !== "_archive")) {
14035
- const domainDir = (0, import_node_path10.join)(memoryDir2(), domain);
14228
+ const domainDir = (0, import_node_path12.join)(memoryDir2(), domain);
14036
14229
  if (!(0, import_node_fs12.existsSync)(domainDir)) continue;
14037
14230
  try {
14038
14231
  const files = await (0, import_promises8.readdir)(domainDir);
@@ -14043,7 +14236,7 @@ async function regenerateIndex() {
14043
14236
  for (const file of mdFiles.sort()) {
14044
14237
  const slug = file.replace(/\.md$/, "");
14045
14238
  try {
14046
- const content = await (0, import_promises8.readFile)((0, import_node_path10.join)(domainDir, file), "utf-8");
14239
+ const content = await (0, import_promises8.readFile)((0, import_node_path12.join)(domainDir, file), "utf-8");
14047
14240
  const title = pickFrontmatter(content, "title") ?? slug;
14048
14241
  const kind = pickFrontmatter(content, "kind") ?? "-";
14049
14242
  const confidence = pickFrontmatter(content, "confidence");
@@ -14067,7 +14260,7 @@ async function regenerateIndex() {
14067
14260
  lines.push("No nodes yet. Run an analysis to start building the knowledge graph.");
14068
14261
  }
14069
14262
  const next = lines.join("\n") + "\n";
14070
- const indexPath = (0, import_node_path10.join)(memoryDir2(), "index.md");
14263
+ const indexPath = (0, import_node_path12.join)(memoryDir2(), "index.md");
14071
14264
  let existing = null;
14072
14265
  try {
14073
14266
  existing = await (0, import_promises8.readFile)(indexPath, "utf-8");
@@ -14301,7 +14494,7 @@ function hasLegacyMemoryBlock(text) {
14301
14494
  return findMarker(text, LEGACY_MD_START) !== -1;
14302
14495
  }
14303
14496
  async function ensureClaudeMdPointer(cwd = repoRoot()) {
14304
- const claudeMdPath = (0, import_node_path10.join)(cwd, "CLAUDE.md");
14497
+ const claudeMdPath = (0, import_node_path12.join)(cwd, "CLAUDE.md");
14305
14498
  let existing = "";
14306
14499
  if ((0, import_node_fs12.existsSync)(claudeMdPath)) {
14307
14500
  existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
@@ -14441,7 +14634,7 @@ Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
14441
14634
  // src/lib/dossier-session.ts
14442
14635
  var import_node_fs17 = require("node:fs");
14443
14636
  var import_node_crypto7 = require("node:crypto");
14444
- var import_node_path13 = require("node:path");
14637
+ var import_node_path15 = require("node:path");
14445
14638
 
14446
14639
  // src/lib/pending-repeat.ts
14447
14640
  var STOP = /* @__PURE__ */ new Set([
@@ -14547,7 +14740,7 @@ function statementAnchorKey(file, patternId) {
14547
14740
  // src/lib/dossier/log.ts
14548
14741
  var import_node_crypto4 = require("node:crypto");
14549
14742
  var import_node_fs13 = require("node:fs");
14550
- var import_node_path11 = require("node:path");
14743
+ var import_node_path13 = require("node:path");
14551
14744
  var CRC_TABLE = (() => {
14552
14745
  const t = new Int32Array(256);
14553
14746
  for (let i = 0; i < 256; i++) {
@@ -14570,9 +14763,9 @@ function openDossier(identity) {
14570
14763
  return {
14571
14764
  dir,
14572
14765
  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")
14766
+ eventsPath: (0, import_node_path13.join)(dir, "events.jsonl"),
14767
+ foldPath: (0, import_node_path13.join)(dir, "fold.json"),
14768
+ rotatedDir: (0, import_node_path13.join)(dir, "rotated")
14576
14769
  };
14577
14770
  } catch {
14578
14771
  return null;
@@ -14645,11 +14838,11 @@ function rotateIfNeeded2(d) {
14645
14838
  if (!(0, import_node_fs13.existsSync)(d.eventsPath)) return;
14646
14839
  if ((0, import_node_fs13.statSync)(d.eventsPath).size < ROTATE_BYTES) return;
14647
14840
  (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`));
14841
+ (0, import_node_fs13.renameSync)(d.eventsPath, (0, import_node_path13.join)(d.rotatedDir, `events.${Date.now()}.jsonl`));
14649
14842
  const kept = (0, import_node_fs13.readdirSync)(d.rotatedDir).filter((f) => f.endsWith(".jsonl")).sort();
14650
14843
  for (const stale of kept.slice(0, Math.max(0, kept.length - ROTATE_KEEP))) {
14651
14844
  try {
14652
- (0, import_node_fs13.renameSync)((0, import_node_path11.join)(d.rotatedDir, stale), (0, import_node_path11.join)(d.rotatedDir, `${stale}.pruned`));
14845
+ (0, import_node_fs13.renameSync)((0, import_node_path13.join)(d.rotatedDir, stale), (0, import_node_path13.join)(d.rotatedDir, `${stale}.pruned`));
14653
14846
  } catch {
14654
14847
  }
14655
14848
  }
@@ -14660,7 +14853,7 @@ function rotateIfNeeded2(d) {
14660
14853
  // src/lib/dossier/fold-dossier.ts
14661
14854
  var import_node_crypto5 = require("node:crypto");
14662
14855
  var import_node_fs14 = require("node:fs");
14663
- var import_node_path12 = require("node:path");
14856
+ var import_node_path14 = require("node:path");
14664
14857
  var EMPTY_CAPABILITIES = () => ({
14665
14858
  human_reachable: { value: "unknown", tier: "unknown" },
14666
14859
  authorship_observability: { value: "unknown", tier: "unknown" },
@@ -14716,7 +14909,7 @@ function foldDossier(d, opts = {}) {
14716
14909
  state.meta.rotations = files.length;
14717
14910
  for (const f of files) {
14718
14911
  try {
14719
- ingest((0, import_node_fs14.readFileSync)((0, import_node_path12.join)(d.rotatedDir, f), "utf8"));
14912
+ ingest((0, import_node_fs14.readFileSync)((0, import_node_path14.join)(d.rotatedDir, f), "utf8"));
14720
14913
  } catch {
14721
14914
  state.meta.dropped_lines++;
14722
14915
  }
@@ -15573,16 +15766,16 @@ function foreignAuthoredPaths(identity, opts = {}) {
15573
15766
  for (const entry of (0, import_node_fs17.readdirSync)(dir, { withFileTypes: true })) {
15574
15767
  if (!entry.isDirectory()) continue;
15575
15768
  if (entry.name === identity.sessionKey) continue;
15576
- const log = (0, import_node_path13.join)(dir, entry.name, "events.jsonl");
15769
+ const log = (0, import_node_path15.join)(dir, entry.name, "events.jsonl");
15577
15770
  try {
15578
15771
  if (!(0, import_node_fs17.existsSync)(log)) continue;
15579
15772
  if (now - (0, import_node_fs17.statSync)(log).mtimeMs > windowMs) continue;
15580
15773
  const sib = {
15581
- dir: (0, import_node_path13.join)(dir, entry.name),
15774
+ dir: (0, import_node_path15.join)(dir, entry.name),
15582
15775
  identity,
15583
15776
  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")
15777
+ foldPath: (0, import_node_path15.join)(dir, entry.name, "fold.json"),
15778
+ rotatedDir: (0, import_node_path15.join)(dir, entry.name, "rotated")
15586
15779
  };
15587
15780
  const state = readFoldCache(sib) ?? foldDossier(sib);
15588
15781
  sessions++;
@@ -15610,22 +15803,22 @@ function pruneOldDossiers(identity, maxAgeMs = 7 * 24 * 60 * 60 * 1e3) {
15610
15803
  let removed = 0;
15611
15804
  try {
15612
15805
  const mine = dossierDir(identity);
15613
- const userDir = (0, import_node_path13.dirname)((0, import_node_path13.dirname)(mine));
15806
+ const userDir = (0, import_node_path15.dirname)((0, import_node_path15.dirname)(mine));
15614
15807
  if (!(0, import_node_fs17.existsSync)(userDir)) return 0;
15615
15808
  const cutoff = Date.now() - maxAgeMs;
15616
15809
  for (const tree of (0, import_node_fs17.readdirSync)(userDir, { withFileTypes: true })) {
15617
15810
  if (!tree.isDirectory()) continue;
15618
- const treePath = (0, import_node_path13.join)(userDir, tree.name);
15811
+ const treePath = (0, import_node_path15.join)(userDir, tree.name);
15619
15812
  let live = 0;
15620
15813
  for (const entry of (0, import_node_fs17.readdirSync)(treePath, { withFileTypes: true })) {
15621
15814
  if (!entry.isDirectory()) continue;
15622
- const dir = (0, import_node_path13.join)(treePath, entry.name);
15815
+ const dir = (0, import_node_path15.join)(treePath, entry.name);
15623
15816
  if (dir === mine) {
15624
15817
  live++;
15625
15818
  continue;
15626
15819
  }
15627
15820
  try {
15628
- const log = (0, import_node_path13.join)(dir, "events.jsonl");
15821
+ const log = (0, import_node_path15.join)(dir, "events.jsonl");
15629
15822
  const at = (0, import_node_fs17.existsSync)(log) ? (0, import_node_fs17.statSync)(log).mtimeMs : (0, import_node_fs17.statSync)(dir).mtimeMs;
15630
15823
  if (at < cutoff) {
15631
15824
  (0, import_node_fs17.rmSync)(dir, { recursive: true, force: true });
@@ -15680,7 +15873,7 @@ function recordTurn(d, t) {
15680
15873
  for (const a of t.authored) {
15681
15874
  const origin = a.owner === "subagent" ? "subagent" : "edit_tool";
15682
15875
  const prior = t.known?.authored?.get(a.p);
15683
- const hash = fileHash((0, import_node_path13.join)(root, a.p));
15876
+ const hash = fileHash((0, import_node_path15.join)(root, a.p));
15684
15877
  const hunks = Math.max(0, a.h - (prior?.hunks ?? 0));
15685
15878
  const adds = Math.max(0, a.a - (prior?.adds ?? 0));
15686
15879
  const dels = Math.max(0, a.d - (prior?.dels ?? 0));
@@ -15713,7 +15906,7 @@ function recordTurn(d, t) {
15713
15906
  }
15714
15907
  const seenDivergence = t.known?.divergence ?? /* @__PURE__ */ new Set();
15715
15908
  for (const u of t.unobserved) {
15716
- const hash = fileHash((0, import_node_path13.join)(root, u.p));
15909
+ const hash = fileHash((0, import_node_path15.join)(root, u.p));
15717
15910
  if (seenDivergence.has(divergenceKey(u.p, hash))) continue;
15718
15911
  appendEvent(d, { k: "divergence", kind: "external_mutation", path: u.p, hash });
15719
15912
  }
@@ -15748,7 +15941,7 @@ function recordVerdict(d, v) {
15748
15941
  if (!sent.has(f.file)) continue;
15749
15942
  if (!lines.has(f.file)) {
15750
15943
  try {
15751
- const abs = (0, import_node_path13.join)(root, f.file);
15944
+ const abs = (0, import_node_path15.join)(root, f.file);
15752
15945
  lines.set(f.file, (0, import_node_fs17.existsSync)(abs) ? (0, import_node_fs17.readFileSync)(abs, "utf8").split("\n") : null);
15753
15946
  } catch {
15754
15947
  lines.set(f.file, null);
@@ -15849,7 +16042,7 @@ function recallMemory(d, identity, opts) {
15849
16042
  budgetBytes: opts.budgetBytes,
15850
16043
  readFileLines: (file) => {
15851
16044
  try {
15852
- const abs = (0, import_node_path13.join)(root, file);
16045
+ const abs = (0, import_node_path15.join)(root, file);
15853
16046
  return (0, import_node_fs17.existsSync)(abs) ? (0, import_node_fs17.readFileSync)(abs, "utf8").split("\n") : null;
15854
16047
  } catch {
15855
16048
  return null;
@@ -15995,21 +16188,21 @@ async function fireClassify(prompt, sessionId, globals) {
15995
16188
 
15996
16189
  // src/commands/lifecycle.ts
15997
16190
  var import_node_fs21 = require("node:fs");
15998
- var import_node_path17 = require("node:path");
16191
+ var import_node_path19 = require("node:path");
15999
16192
 
16000
16193
  // src/lib/baseline.ts
16001
16194
  var import_node_fs20 = require("node:fs");
16002
- var import_node_path16 = require("node:path");
16195
+ var import_node_path18 = require("node:path");
16003
16196
  var import_node_crypto9 = require("node:crypto");
16004
16197
 
16005
16198
  // src/lib/snapshot.ts
16006
16199
  var import_node_fs19 = require("node:fs");
16007
- var import_node_path15 = require("node:path");
16200
+ var import_node_path17 = require("node:path");
16008
16201
  var import_node_child_process6 = require("node:child_process");
16009
16202
 
16010
16203
  // src/lib/files.ts
16011
16204
  var import_node_fs18 = require("node:fs");
16012
- var import_node_path14 = require("node:path");
16205
+ var import_node_path16 = require("node:path");
16013
16206
  var LANG_MAP = {
16014
16207
  // Analyzable (static analysis + Gemini)
16015
16208
  ts: "typescript",
@@ -16077,7 +16270,7 @@ var LANG_MAP = {
16077
16270
  mk: "make"
16078
16271
  };
16079
16272
  function detectLanguage(filepath) {
16080
- const ext = (0, import_node_path14.extname)(filepath).slice(1);
16273
+ const ext = (0, import_node_path16.extname)(filepath).slice(1);
16081
16274
  return LANG_MAP[ext] ?? ext;
16082
16275
  }
16083
16276
  function sortByMtime(files) {
@@ -16180,7 +16373,7 @@ function generateSnapshotDiffs(files) {
16180
16373
  const diffs = [];
16181
16374
  for (const file of files) {
16182
16375
  if (!resolveInside(SNAPSHOT_DIR, file.path)) continue;
16183
- const snapshotPath = (0, import_node_path15.join)(SNAPSHOT_DIR, file.path);
16376
+ const snapshotPath = (0, import_node_path17.join)(SNAPSHOT_DIR, file.path);
16184
16377
  const language = file.language ?? detectLanguage(file.path);
16185
16378
  if ((0, import_node_fs19.existsSync)(snapshotPath)) {
16186
16379
  const oldContent = (0, import_node_fs19.readFileSync)(snapshotPath, "utf-8");
@@ -16208,16 +16401,16 @@ function saveSnapshots(files) {
16208
16401
  const snapshotPaths = /* @__PURE__ */ new Set();
16209
16402
  for (const file of files) {
16210
16403
  if (!resolveInside(SNAPSHOT_DIR, file.path)) continue;
16211
- const snapshotPath = (0, import_node_path15.join)(SNAPSHOT_DIR, file.path);
16404
+ const snapshotPath = (0, import_node_path17.join)(SNAPSHOT_DIR, file.path);
16212
16405
  snapshotPaths.add(snapshotPath);
16213
- (0, import_node_fs19.mkdirSync)((0, import_node_path15.dirname)(snapshotPath), { recursive: true });
16406
+ (0, import_node_fs19.mkdirSync)((0, import_node_path17.dirname)(snapshotPath), { recursive: true });
16214
16407
  (0, import_node_fs19.writeFileSync)(snapshotPath, file.content);
16215
16408
  }
16216
16409
  cleanStaleSnapshots(SNAPSHOT_DIR, snapshotPaths);
16217
16410
  }
16218
16411
  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");
16412
+ const tmpOld = (0, import_node_path17.join)(SNAPSHOT_DIR, ".diff-old.tmp");
16413
+ const tmpNew = (0, import_node_path17.join)(SNAPSHOT_DIR, ".diff-new.tmp");
16221
16414
  try {
16222
16415
  (0, import_node_fs19.mkdirSync)(SNAPSHOT_DIR, { recursive: true });
16223
16416
  (0, import_node_fs19.writeFileSync)(tmpOld, oldContent);
@@ -16250,7 +16443,7 @@ function cleanStaleSnapshots(dir, keepSet) {
16250
16443
  const entries = (0, import_node_fs19.readdirSync)(dir, { withFileTypes: true });
16251
16444
  for (const entry of entries) {
16252
16445
  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);
16446
+ const fullPath = (0, import_node_path17.join)(dir, entry.name);
16254
16447
  if (entry.isDirectory()) {
16255
16448
  cleanStaleSnapshots(fullPath, keepSet);
16256
16449
  try {
@@ -16279,13 +16472,13 @@ function sessionKey(sessionId) {
16279
16472
  return (0, import_node_crypto9.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
16280
16473
  }
16281
16474
  function sessionDir(key) {
16282
- return (0, import_node_path16.join)(projectPath(BASELINE_DIR), key);
16475
+ return (0, import_node_path18.join)(projectPath(BASELINE_DIR), key);
16283
16476
  }
16284
16477
  function manifestPath(dir) {
16285
- return (0, import_node_path16.join)(dir, "manifest.json");
16478
+ return (0, import_node_path18.join)(dir, "manifest.json");
16286
16479
  }
16287
16480
  function mirrorPath(dir, repoRelPath) {
16288
- return (0, import_node_path16.join)(dir, "files", repoRelPath);
16481
+ return (0, import_node_path18.join)(dir, "files", repoRelPath);
16289
16482
  }
16290
16483
  var CARRY_FILE = `${BASELINE_DIR}/.carry`;
16291
16484
  var CARRY_WINDOW_MS = 12e4;
@@ -16347,7 +16540,7 @@ function captureBaseline(opts = {}) {
16347
16540
  (0, import_node_fs20.rmSync)(dir, { recursive: true, force: true });
16348
16541
  } catch {
16349
16542
  }
16350
- const filesDir = (0, import_node_path16.join)(dir, "files");
16543
+ const filesDir = (0, import_node_path18.join)(dir, "files");
16351
16544
  const mirrored = [];
16352
16545
  try {
16353
16546
  (0, import_node_fs20.mkdirSync)(filesDir, { recursive: true });
@@ -16357,7 +16550,7 @@ function captureBaseline(opts = {}) {
16357
16550
  if (content === null) continue;
16358
16551
  const dest = mirrorPath(dir, p);
16359
16552
  try {
16360
- (0, import_node_fs20.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
16553
+ (0, import_node_fs20.mkdirSync)((0, import_node_path18.dirname)(dest), { recursive: true });
16361
16554
  (0, import_node_fs20.writeFileSync)(dest, content);
16362
16555
  mirrored.push(p);
16363
16556
  } catch {
@@ -16465,7 +16658,7 @@ function absorbIntoBaseline(paths, sessionId) {
16465
16658
  const content = safeReadForMirror(projectPath(p));
16466
16659
  if (content === null) continue;
16467
16660
  const dest = mirrorPath(dir, p);
16468
- (0, import_node_fs20.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
16661
+ (0, import_node_fs20.mkdirSync)((0, import_node_path18.dirname)(dest), { recursive: true });
16469
16662
  (0, import_node_fs20.writeFileSync)(dest, content);
16470
16663
  dirty.add(p);
16471
16664
  adopted++;
@@ -16513,7 +16706,7 @@ function pruneOldBaselines() {
16513
16706
  }
16514
16707
  const now = Date.now();
16515
16708
  for (const name of entries) {
16516
- const dir = (0, import_node_path16.join)(root, name);
16709
+ const dir = (0, import_node_path18.join)(root, name);
16517
16710
  const manifest = readManifest(dir);
16518
16711
  if (!manifest) {
16519
16712
  try {
@@ -16701,7 +16894,7 @@ function buildCompactionContext(session) {
16701
16894
  commitsSince: commitsSincePaths(watermark, (state.authored ?? []).map((a) => a.path)),
16702
16895
  readFileLines: (file) => {
16703
16896
  try {
16704
- const abs = (0, import_node_path17.join)(root, file);
16897
+ const abs = (0, import_node_path19.join)(root, file);
16705
16898
  return (0, import_node_fs21.existsSync)(abs) ? (0, import_node_fs21.readFileSync)(abs, "utf8").split("\n") : null;
16706
16899
  } catch {
16707
16900
  return null;
@@ -16738,17 +16931,17 @@ async function readHookStdin() {
16738
16931
  try {
16739
16932
  if (process.stdin.isTTY) return {};
16740
16933
  const chunks = [];
16741
- const timeout = new Promise((resolve4) => setTimeout(() => resolve4({}), 500));
16742
- const read = new Promise((resolve4) => {
16934
+ const timeout = new Promise((resolve5) => setTimeout(() => resolve5({}), 500));
16935
+ const read = new Promise((resolve5) => {
16743
16936
  process.stdin.on("data", (c) => chunks.push(c));
16744
16937
  process.stdin.on("end", () => {
16745
16938
  try {
16746
- resolve4(JSON.parse(Buffer.concat(chunks).toString("utf-8").trim() || "{}"));
16939
+ resolve5(JSON.parse(Buffer.concat(chunks).toString("utf-8").trim() || "{}"));
16747
16940
  } catch {
16748
- resolve4({});
16941
+ resolve5({});
16749
16942
  }
16750
16943
  });
16751
- process.stdin.on("error", () => resolve4({}));
16944
+ process.stdin.on("error", () => resolve5({}));
16752
16945
  process.stdin.resume();
16753
16946
  });
16754
16947
  return await Promise.race([read, timeout]);
@@ -16766,17 +16959,17 @@ var import_yaml3 = __toESM(require_dist());
16766
16959
  var import_node_child_process8 = require("node:child_process");
16767
16960
  var import_node_fs24 = require("node:fs");
16768
16961
  var import_promises9 = require("node:fs/promises");
16769
- var import_node_path20 = require("node:path");
16962
+ var import_node_path22 = require("node:path");
16770
16963
  var import_yaml = __toESM(require_dist());
16771
16964
 
16772
16965
  // src/lib/data-dir.ts
16773
16966
  var import_node_fs22 = require("node:fs");
16774
- var import_node_path18 = require("node:path");
16967
+ var import_node_path20 = require("node:path");
16775
16968
  function resolveDataDir() {
16776
- const candidates = [
16777
- (0, import_node_path18.join)(__dirname, "..", "data"),
16969
+ const candidates2 = [
16970
+ (0, import_node_path20.join)(__dirname, "..", "data"),
16778
16971
  // installed: node_modules/@codacy/verity-cli/data
16779
- (0, import_node_path18.join)(__dirname, "..", "..", "data"),
16972
+ (0, import_node_path20.join)(__dirname, "..", "..", "data"),
16780
16973
  // edge case: nested resolution
16781
16974
  // THE COMMITTED SOURCE, for a source checkout that has not been built.
16782
16975
  // cli/data/skills/ is a BUILD ARTIFACT (scripts/build.js copies client/skills
@@ -16785,14 +16978,14 @@ function resolveDataDir() {
16785
16978
  // without this the synthesizer throws "Could not find Verity skill data"
16786
16979
  // for every test and every `verity` run from source. Resolved from this
16787
16980
  // module's own location, never the cwd: see the warning below.
16788
- (0, import_node_path18.join)(__dirname, "..", "..", "client"),
16981
+ (0, import_node_path20.join)(__dirname, "..", "..", "client"),
16789
16982
  // bundled: cli/bin/ → ../../client
16790
- (0, import_node_path18.join)(__dirname, "..", "..", "..", "client"),
16983
+ (0, import_node_path20.join)(__dirname, "..", "..", "..", "client"),
16791
16984
  // tsx: cli/src/lib/ → ../../../client
16792
16985
  ...process.env.VERITY_DEV_DATA_DIR ? [process.env.VERITY_DEV_DATA_DIR] : []
16793
16986
  ];
16794
- for (const candidate of candidates) {
16795
- if ((0, import_node_fs22.existsSync)((0, import_node_path18.join)(candidate, "skills"))) {
16987
+ for (const candidate of candidates2) {
16988
+ if ((0, import_node_fs22.existsSync)((0, import_node_path20.join)(candidate, "skills"))) {
16796
16989
  return candidate;
16797
16990
  }
16798
16991
  }
@@ -16801,13 +16994,13 @@ function resolveDataDir() {
16801
16994
  );
16802
16995
  }
16803
16996
  function setupDataPath(file) {
16804
- return (0, import_node_path18.join)(resolveDataDir(), "skills", "verity-setup", file);
16997
+ return (0, import_node_path20.join)(resolveDataDir(), "skills", "verity-setup", file);
16805
16998
  }
16806
16999
 
16807
17000
  // src/lib/detect.ts
16808
17001
  var import_node_child_process7 = require("node:child_process");
16809
17002
  var import_node_fs23 = require("node:fs");
16810
- var import_node_path19 = require("node:path");
17003
+ var import_node_path21 = require("node:path");
16811
17004
  var TOOLED_LANGUAGES = /* @__PURE__ */ new Set([
16812
17005
  "typescript",
16813
17006
  "javascript",
@@ -16870,18 +17063,18 @@ function walk(root) {
16870
17063
  for (const entry of entries) {
16871
17064
  if (found.length >= WALK_MAX_FILES) return;
16872
17065
  if (IGNORED_SEGMENTS.includes(entry.name)) continue;
16873
- const full = (0, import_node_path19.join)(dir, entry.name);
17066
+ const full = (0, import_node_path21.join)(dir, entry.name);
16874
17067
  if (entry.isDirectory()) visit(full, depth + 1);
16875
- else if (entry.isFile()) found.push((0, import_node_path19.relative)(root, full));
17068
+ else if (entry.isFile()) found.push((0, import_node_path21.relative)(root, full));
16876
17069
  }
16877
17070
  };
16878
17071
  visit(root, 0);
16879
17072
  return found;
16880
17073
  }
16881
17074
  function languageOf(path) {
16882
- const name = (0, import_node_path19.basename)(path);
17075
+ const name = (0, import_node_path21.basename)(path);
16883
17076
  if (/^Dockerfile(\..+)?$/i.test(name)) return "dockerfile";
16884
- if (!(0, import_node_path19.extname)(name)) return null;
17077
+ if (!(0, import_node_path21.extname)(name)) return null;
16885
17078
  const lang = detectLanguage(path);
16886
17079
  return lang || null;
16887
17080
  }
@@ -16963,12 +17156,12 @@ function declaredDependencies(root, files) {
16963
17156
  if (deps && typeof deps === "object") names2.push(...Object.keys(deps));
16964
17157
  }
16965
17158
  };
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));
17159
+ readPackageJson((0, import_node_path21.join)(root, "package.json"));
17160
+ const nested = files.filter((f) => f.includes("/") && (0, import_node_path21.basename)(f) === "package.json").slice(0, NESTED_MANIFEST_LIMIT);
17161
+ for (const rel of nested) readPackageJson((0, import_node_path21.join)(root, rel));
16969
17162
  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))
17163
+ ...["pyproject.toml", "requirements.txt", "Pipfile", "setup.py"].map((f) => (0, import_node_path21.join)(root, f)),
17164
+ ...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
17165
  ];
16973
17166
  for (const path of pythonManifests) {
16974
17167
  if (!(0, import_node_fs23.existsSync)(path)) continue;
@@ -16983,8 +17176,8 @@ function declaredDependencies(root, files) {
16983
17176
  }
16984
17177
  }
16985
17178
  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))
17179
+ (0, import_node_path21.join)(root, "go.mod"),
17180
+ ...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
17181
  ];
16989
17182
  for (const path of goMods) {
16990
17183
  if (!(0, import_node_fs23.existsSync)(path)) continue;
@@ -16997,7 +17190,7 @@ function declaredDependencies(root, files) {
16997
17190
  }
16998
17191
  }
16999
17192
  for (const file of ["pom.xml", "build.gradle", "build.gradle.kts", "Gemfile", "Cargo.toml"]) {
17000
- const path = (0, import_node_path19.join)(root, file);
17193
+ const path = (0, import_node_path21.join)(root, file);
17001
17194
  if (!(0, import_node_fs23.existsSync)(path)) continue;
17002
17195
  try {
17003
17196
  const text = (0, import_node_fs23.readFileSync)(path, "utf-8");
@@ -17008,7 +17201,7 @@ function declaredDependencies(root, files) {
17008
17201
  return names2;
17009
17202
  }
17010
17203
  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);
17204
+ 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
17205
  if (has("pnpm-lock.yaml")) return "pnpm";
17013
17206
  if (has("yarn.lock")) return "yarn";
17014
17207
  if (has("bun.lock") || has("bun.lockb")) return "bun";
@@ -17025,8 +17218,8 @@ function detectBuildSystem(root, files) {
17025
17218
  }
17026
17219
  function detectArchitecture(root, files) {
17027
17220
  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"));
17221
+ if (workspaceMarkers.some((m) => (0, import_node_fs23.existsSync)((0, import_node_path21.join)(root, m)))) return "monorepo";
17222
+ const pkg = readJson((0, import_node_path21.join)(root, "package.json"));
17030
17223
  if (pkg && "workspaces" in pkg) return "monorepo";
17031
17224
  const manifests = files.filter((f) => /(^|\/)(package\.json|go\.mod|pyproject\.toml|Cargo\.toml|pom\.xml)$/.test(f));
17032
17225
  const nested = manifests.filter((f) => f.includes("/"));
@@ -17039,16 +17232,16 @@ function detectArchitecture(root, files) {
17039
17232
  var SAMPLE_LIMIT = 300;
17040
17233
  function measureAvgFileLength(root, files, languages) {
17041
17234
  const primary = new Set(languages.slice(0, 2));
17042
- const candidates = files.filter((f) => {
17235
+ const candidates2 = files.filter((f) => {
17043
17236
  const lang = languageOf(f);
17044
17237
  return lang !== null && primary.has(lang);
17045
17238
  });
17046
- if (candidates.length === 0) return null;
17047
- const stride = Math.max(1, Math.floor(candidates.length / SAMPLE_LIMIT));
17239
+ if (candidates2.length === 0) return null;
17240
+ const stride = Math.max(1, Math.floor(candidates2.length / SAMPLE_LIMIT));
17048
17241
  let total = 0;
17049
17242
  let counted = 0;
17050
- for (let i = 0; i < candidates.length; i += stride) {
17051
- const path = (0, import_node_path19.join)(root, candidates[i]);
17243
+ for (let i = 0; i < candidates2.length; i += stride) {
17244
+ const path = (0, import_node_path21.join)(root, candidates2[i]);
17052
17245
  try {
17053
17246
  if ((0, import_node_fs23.statSync)(path).size > 2 * 1024 * 1024) continue;
17054
17247
  total += (0, import_node_fs23.readFileSync)(path, "utf-8").split("\n").length;
@@ -17076,14 +17269,14 @@ function detectProject(root = repoRoot()) {
17076
17269
  const existingToolConfigs = [];
17077
17270
  for (const [tool, markers] of TOOL_CONFIG_MARKERS) {
17078
17271
  for (const marker of markers) {
17079
- if ((0, import_node_fs23.existsSync)((0, import_node_path19.join)(root, marker))) {
17272
+ if ((0, import_node_fs23.existsSync)((0, import_node_path21.join)(root, marker))) {
17080
17273
  existingToolConfigs.push({ tool, path: `./${marker}` });
17081
17274
  break;
17082
17275
  }
17083
17276
  }
17084
17277
  }
17085
17278
  return {
17086
- projectName: (0, import_node_path19.basename)(root),
17279
+ projectName: (0, import_node_path21.basename)(root),
17087
17280
  languages,
17088
17281
  languageCounts,
17089
17282
  frameworks: matchAll(dependencies, FRAMEWORK_BY_DEPENDENCY),
@@ -17529,7 +17722,7 @@ async function correctVerityMdVersion(opts) {
17529
17722
  }
17530
17723
  async function writeFileTo(relative2, body) {
17531
17724
  const target = projectPath(relative2);
17532
- await (0, import_promises9.mkdir)((0, import_node_path20.dirname)(target), { recursive: true });
17725
+ await (0, import_promises9.mkdir)((0, import_node_path22.dirname)(target), { recursive: true });
17533
17726
  await (0, import_promises9.writeFile)(target, body);
17534
17727
  }
17535
17728
  async function deriveConfigForStandard(standard) {
@@ -18004,7 +18197,11 @@ function registerConfigCommands(program2) {
18004
18197
  });
18005
18198
  config.command("git-moments [moments]").description('Get or set the git moments the guard reviews: commit,push \u2014 or "none"').action((moments) => {
18006
18199
  if (moments === void 0) {
18007
- const current = readProjectConfig().git_moments;
18200
+ const config2 = readProjectConfig();
18201
+ const current = config2.git_moments;
18202
+ if (config2.source === "main-worktree" && config2.path) {
18203
+ printInfo(`Inherited from the main worktree: ${config2.path}`);
18204
+ }
18008
18205
  process.stdout.write((current.length ? current.join(",") : "none") + "\n");
18009
18206
  return;
18010
18207
  }
@@ -18013,7 +18210,7 @@ function registerConfigCommands(program2) {
18013
18210
  printError(`Unrecognised moments: ${moments}. Use "commit", "push", "commit,push", or "none".`);
18014
18211
  process.exit(1);
18015
18212
  }
18016
- writeProjectConfig({ git_moments: next });
18213
+ writeProjectConfig({ git_moments: next, git_moments_source: "user" });
18017
18214
  printInfo(
18018
18215
  next.length ? `Git-moment review enabled for: ${next.join(", ")}` : "Git-moment review disabled \u2014 commits and pushes are no longer gated."
18019
18216
  );
@@ -18474,8 +18671,14 @@ function timeAgo(isoDate) {
18474
18671
  return `${days}d ago`;
18475
18672
  }
18476
18673
  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) => {
18674
+ 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
18675
  const globals = program2.opts();
18676
+ const rawLimit = String(opts.limit).trim();
18677
+ const limit = /^\d{1,3}$/.test(rawLimit) ? Number(rawLimit) : Number.NaN;
18678
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
18679
+ printError(`--limit must be a whole number between 1 and 100 (got "${opts.limit}").`);
18680
+ process.exit(1);
18681
+ }
18479
18682
  const tokenResult = await resolveToken(globals.token);
18480
18683
  if (!tokenResult.ok) {
18481
18684
  printError(tokenResult.error);
@@ -18513,7 +18716,7 @@ function registerStatusCommand(program2) {
18513
18716
  if (opts.history && !denial) {
18514
18717
  const runsResult = await apiRequest({
18515
18718
  method: "GET",
18516
- path: `/runs?limit=${opts.limit}`,
18719
+ path: `/runs?limit=${limit}`,
18517
18720
  serviceUrl,
18518
18721
  token,
18519
18722
  verbose: globals.verbose
@@ -18553,12 +18756,14 @@ function registerStatusCommand(program2) {
18553
18756
  printInfo(`Standard: v${s.version} (${s.quality_dimensions} quality, ${s.security_patterns} security, ${s.custom_patterns} custom)`);
18554
18757
  printInfo(`Languages: ${s.languages.join(", ")}`);
18555
18758
  }
18556
- const hookStatus = await checkAllVerityHooks();
18759
+ const wiring = await resolveHookWiring();
18760
+ const hookStatus = wiring.status;
18557
18761
  const moments = [];
18558
18762
  if (hookStatus.stop) moments.push("stop");
18559
18763
  if (hookStatus.guardOn.includes("commit")) moments.push("pre-commit");
18560
18764
  if (hookStatus.guardOn.includes("push")) moments.push("pre-push/PR");
18561
- printInfo(`Moments: ${moments.length > 0 ? moments.join(", ") : 'none (run "verity init")'}`);
18765
+ const via = wiring.source === "plugin" ? " (wired by the Verity plugin)" : "";
18766
+ printInfo(`Moments: ${moments.length > 0 ? `${moments.join(", ")}${via}` : 'none (run "verity init")'}`);
18562
18767
  if (!mem) return;
18563
18768
  if (mem.recent_runs) {
18564
18769
  const r = mem.recent_runs;
@@ -18635,7 +18840,7 @@ function registerStatusCommand(program2) {
18635
18840
  if (opts.history) {
18636
18841
  const runsResult = await apiRequest({
18637
18842
  method: "GET",
18638
- path: `/runs?limit=${opts.limit}`,
18843
+ path: `/runs?limit=${limit}`,
18639
18844
  serviceUrl,
18640
18845
  token,
18641
18846
  verbose: globals.verbose
@@ -18826,11 +19031,11 @@ var import_node_fs43 = require("node:fs");
18826
19031
 
18827
19032
  // src/lib/repo-context.ts
18828
19033
  var import_node_child_process9 = require("node:child_process");
18829
- var import_node_os3 = require("node:os");
19034
+ var import_node_os4 = require("node:os");
18830
19035
  function rgInvocations(env = process.env) {
18831
19036
  const out = [{ cmd: "rg" }];
18832
19037
  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" });
19038
+ out.push({ cmd: `${(0, import_node_os4.homedir)()}/.local/bin/claude`, argv0: "rg" });
18834
19039
  return out;
18835
19040
  }
18836
19041
  var MAX_SYMBOLS = 12;
@@ -19644,9 +19849,9 @@ function installRunEvidence(run2) {
19644
19849
  // src/lib/git-frame.ts
19645
19850
  var import_node_child_process10 = require("node:child_process");
19646
19851
  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");
19852
+ var import_node_os5 = require("node:os");
19853
+ var import_node_path23 = require("node:path");
19854
+ var import_node_path24 = require("node:path");
19650
19855
  var VALUE_TOKEN = `(?:'[^']*'|"[^"]*"|\\S+)`;
19651
19856
  var GIT_GLOBAL_OPTS = `(?:\\s+(?:-[Cc]\\s+${VALUE_TOKEN}|--?[\\w-]+(?:=\\S+)?))*`;
19652
19857
  var COMMIT_HEAD = `git${GIT_GLOBAL_OPTS}\\s+commit(?![\\w-])`;
@@ -19692,15 +19897,15 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
19692
19897
  if (!m) continue;
19693
19898
  named = true;
19694
19899
  if (m[1] === void 0) {
19695
- dir = (0, import_node_os4.homedir)();
19900
+ dir = (0, import_node_os5.homedir)();
19696
19901
  continue;
19697
19902
  }
19698
19903
  const raw = unquote(m[1]);
19699
19904
  if (SHELL_DYNAMIC.test(raw) || raw === "-") {
19700
19905
  return { dir: null, named: true, unresolvable: `cd target not statically resolvable: ${raw}` };
19701
19906
  }
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);
19907
+ const expanded = raw === "~" ? (0, import_node_os5.homedir)() : raw.startsWith("~/") ? (0, import_node_path24.join)((0, import_node_os5.homedir)(), raw.slice(2)) : raw;
19908
+ dir = (0, import_node_path23.isAbsolute)(expanded) ? expanded : (0, import_node_path23.resolve)(dir, expanded);
19704
19909
  }
19705
19910
  const seg = segments[segmentIndex];
19706
19911
  const overrideMatch = /--(?:git-dir|work-tree)(?:=|\s)|\bGIT_(?:DIR|WORK_TREE|INDEX_FILE)=/.exec(seg);
@@ -19718,8 +19923,8 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
19718
19923
  if (SHELL_DYNAMIC.test(raw)) {
19719
19924
  return { dir: null, named: true, unresolvable: `-C target not statically resolvable: ${raw}` };
19720
19925
  }
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);
19926
+ const expanded = raw === "~" ? (0, import_node_os5.homedir)() : raw.startsWith("~/") ? (0, import_node_path24.join)((0, import_node_os5.homedir)(), raw.slice(2)) : raw;
19927
+ dir = (0, import_node_path23.isAbsolute)(expanded) ? expanded : (0, import_node_path23.resolve)(dir, expanded);
19723
19928
  }
19724
19929
  }
19725
19930
  return { dir, named, unresolvable: null };
@@ -19780,11 +19985,11 @@ function gitAt(dir, args) {
19780
19985
  return "";
19781
19986
  }
19782
19987
  }
19783
- function realpathOr(p) {
19988
+ function realpathOr2(p) {
19784
19989
  try {
19785
19990
  return import_node_fs31.realpathSync.native(p);
19786
19991
  } catch {
19787
- return (0, import_node_path21.resolve)(p);
19992
+ return (0, import_node_path23.resolve)(p);
19788
19993
  }
19789
19994
  }
19790
19995
  function resolveFrame(input) {
@@ -19822,13 +20027,13 @@ function resolveFrame(input) {
19822
20027
  }
19823
20028
  const gitDirRaw = gitAt(dir, ["rev-parse", "--absolute-git-dir"]);
19824
20029
  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;
20030
+ const gitDir = gitDirRaw ? realpathOr2(gitDirRaw) : null;
20031
+ const commonDir = commonRaw ? realpathOr2((0, import_node_path23.isAbsolute)(commonRaw) ? commonRaw : (0, import_node_path23.resolve)(dir, commonRaw)) : null;
19827
20032
  const branchRaw = gitAt(dir, ["rev-parse", "--abbrev-ref", "HEAD"]);
19828
20033
  return {
19829
20034
  moment: found?.moment ?? null,
19830
20035
  frame: {
19831
- worktreeRoot: realpathOr(toplevel),
20036
+ worktreeRoot: realpathOr2(toplevel),
19832
20037
  gitDir,
19833
20038
  commonDir,
19834
20039
  // The one honest definition: a linked worktree's own git dir differs from
@@ -19848,14 +20053,172 @@ function refResolves(frame, ref) {
19848
20053
  return frameGit(frame, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]) !== "";
19849
20054
  }
19850
20055
  var SHA_RE2 = /^[0-9a-f]{40}$/;
19851
- function stagedRange(frame) {
20056
+ function shellWords(segment) {
20057
+ return segment.match(/'[^']*'|"[^"]*"|\S+/g) ?? [];
20058
+ }
20059
+ var ADD_FLAGS_REPLAYABLE = /* @__PURE__ */ new Set([
20060
+ "-A",
20061
+ "--all",
20062
+ "--no-ignore-removal",
20063
+ "-u",
20064
+ "--update",
20065
+ "--no-all",
20066
+ "--ignore-removal",
20067
+ "-f",
20068
+ "--force",
20069
+ "-v",
20070
+ "--verbose",
20071
+ "-N",
20072
+ "--intent-to-add",
20073
+ "--renormalize",
20074
+ "--sparse",
20075
+ "--ignore-errors",
20076
+ "--refresh"
20077
+ ]);
20078
+ var ADD_FLAGS_INTERACTIVE = /* @__PURE__ */ new Set(["-i", "--interactive", "-p", "--patch", "-e", "--edit"]);
20079
+ var PATHSPEC_UNRESOLVABLE = /[$`\\*?[\]{}~]/;
20080
+ var COMMIT_VALUE_OPTS = /* @__PURE__ */ new Set([
20081
+ "-m",
20082
+ "--message",
20083
+ "-F",
20084
+ "--file",
20085
+ "-C",
20086
+ "--reuse-message",
20087
+ "-c",
20088
+ "--reedit-message",
20089
+ "--author",
20090
+ "--date",
20091
+ "-t",
20092
+ "--template",
20093
+ "--fixup",
20094
+ "--squash",
20095
+ "--trailer",
20096
+ "--cleanup"
20097
+ ]);
20098
+ var ADD_HEAD = `git${GIT_GLOBAL_OPTS}\\s+add(?![\\w-])`;
20099
+ var ADD_RE = new RegExp(`(?:^|[\\s;&|(])${ADD_HEAD}|(?:^|[;&|(])\\s*[^\\s;&|()'"]*\\/${ADD_HEAD}`);
20100
+ function pathspecOf(word) {
20101
+ const raw = unquote(word);
20102
+ if (!raw) return { reason: "an empty pathspec" };
20103
+ if (PATHSPEC_UNRESOLVABLE.test(raw)) {
20104
+ return { reason: `the shell rewrites the pathspec "${raw}" before git sees it` };
20105
+ }
20106
+ return { path: raw };
20107
+ }
20108
+ function parseAddSegment(segment) {
20109
+ const words = shellWords(segment);
20110
+ const at = words.indexOf("add");
20111
+ if (at === -1) return { reason: 'the "git add" arguments could not be read' };
20112
+ const flags = [];
20113
+ const paths = [];
20114
+ let literal = false;
20115
+ for (const word of words.slice(at + 1)) {
20116
+ if (literal || !word.startsWith("-") || word === "-") {
20117
+ if (word === "--" && !literal) {
20118
+ literal = true;
20119
+ continue;
20120
+ }
20121
+ const p = pathspecOf(word);
20122
+ if ("reason" in p) return p;
20123
+ paths.push(p.path);
20124
+ continue;
20125
+ }
20126
+ if (word === "--") {
20127
+ literal = true;
20128
+ continue;
20129
+ }
20130
+ const parts = /^-[A-Za-z]{2,}$/.test(word) ? [...word.slice(1)].map((c) => `-${c}`) : [word];
20131
+ for (const flag of parts) {
20132
+ if (ADD_FLAGS_INTERACTIVE.has(flag)) {
20133
+ return { reason: `"git add ${flag}" stages what a human picks at a prompt` };
20134
+ }
20135
+ if (!ADD_FLAGS_REPLAYABLE.has(flag.split("=")[0])) {
20136
+ return { reason: `unmodelled "git add" option ${flag}` };
20137
+ }
20138
+ flags.push(flag);
20139
+ }
20140
+ }
20141
+ return { flags, paths };
20142
+ }
20143
+ function commitStagesTrackedChanges(segment) {
20144
+ const words = shellWords(segment);
20145
+ const at = words.indexOf("commit");
20146
+ if (at === -1) return false;
20147
+ let skipValue = false;
20148
+ for (const word of words.slice(at + 1)) {
20149
+ if (skipValue) {
20150
+ skipValue = false;
20151
+ continue;
20152
+ }
20153
+ if (word === "--") break;
20154
+ if (!word.startsWith("-") || word === "-") continue;
20155
+ if (word.startsWith("--")) {
20156
+ const name = word.split("=")[0];
20157
+ if (name === "--all") return true;
20158
+ if (COMMIT_VALUE_OPTS.has(name) && !word.includes("=")) skipValue = true;
20159
+ continue;
20160
+ }
20161
+ for (let i = 1; i < word.length; i++) {
20162
+ const flag = `-${word[i]}`;
20163
+ if (flag === "-a") return true;
20164
+ if (COMMIT_VALUE_OPTS.has(flag)) {
20165
+ if (i === word.length - 1) skipValue = true;
20166
+ break;
20167
+ }
20168
+ }
20169
+ }
20170
+ return false;
20171
+ }
20172
+ function planInCommandStaging(frame, command) {
20173
+ const found = findMomentSegment(command, ["commit"]);
20174
+ if (!found) return { kind: "none" };
20175
+ const segments = splitSegments(command);
20176
+ if (commitStagesTrackedChanges(segments[found.segmentIndex])) {
20177
+ return {
20178
+ kind: "files",
20179
+ via: "index+commit-a",
20180
+ files: frameGit(frame, ["diff", "--name-only", "HEAD"]).split("\n").filter(Boolean)
20181
+ };
20182
+ }
20183
+ const addSegments = segments.slice(0, found.segmentIndex).filter((s) => ADD_RE.test(s));
20184
+ if (addSegments.length === 0) return { kind: "none" };
20185
+ const staged = new Set(
20186
+ frameGit(frame, ["diff", "--cached", "--name-only"]).split("\n").filter(Boolean)
20187
+ );
20188
+ for (const segment of addSegments) {
20189
+ const parsed = parseAddSegment(segment);
20190
+ if ("reason" in parsed) return { kind: "unpredictable", reason: parsed.reason };
20191
+ const args = ["add", "--dry-run", "--ignore-missing", ...parsed.flags];
20192
+ if (parsed.paths.length > 0) args.push("--", ...parsed.paths);
20193
+ for (const line of frameGit(frame, args).split("\n")) {
20194
+ const m = line.match(/^(?:add|remove) '(.*)'$/);
20195
+ if (m) staged.add(m[1]);
20196
+ }
20197
+ }
20198
+ return { kind: "files", via: "index+staged-in-command", files: [...staged] };
20199
+ }
20200
+ function stagedRange(frame, command) {
19852
20201
  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;
20202
+ const plan = command ? planInCommandStaging(frame, command) : { kind: "none" };
20203
+ if (plan.kind === "unpredictable") {
20204
+ return { kind: "staged", base: "HEAD", head: "INDEX", via: "staged-in-command", refusal: plan.reason };
20205
+ }
20206
+ const mergeHead = frame.gitDir ? (0, import_node_path24.join)(frame.gitDir, "MERGE_HEAD") : null;
19854
20207
  if (mergeHead && (0, import_node_fs31.existsSync)(mergeHead)) {
19855
20208
  const vsHead = new Set(frameGit(frame, ["diff", "--cached", "--name-only", "HEAD"]).split("\n").filter(Boolean));
19856
20209
  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 };
20210
+ const resolutions = new Set([...vsHead].filter((f) => vsMerge.has(f) && !isVerityOwnedPath(f)));
20211
+ if (plan.kind === "files") {
20212
+ const wtVsHead = new Set(frameGit(frame, ["diff", "--name-only", "HEAD"]).split("\n").filter(Boolean));
20213
+ const wtVsMerge = new Set(frameGit(frame, ["diff", "--name-only", "MERGE_HEAD"]).split("\n").filter(Boolean));
20214
+ for (const f of plan.files) {
20215
+ if (wtVsHead.has(f) && wtVsMerge.has(f) && !isVerityOwnedPath(f)) resolutions.add(f);
20216
+ }
20217
+ }
20218
+ return { kind: "merge", base: "HEAD", head: "INDEX", via: "merge-resolutions", files: [...resolutions] };
20219
+ }
20220
+ if (plan.kind === "files") {
20221
+ return { kind: "staged", base: "HEAD", head: "INDEX", via: plan.via, files: plan.files };
19859
20222
  }
19860
20223
  return { kind: "staged", base: "HEAD", head: "INDEX", via: "index" };
19861
20224
  }
@@ -19937,7 +20300,7 @@ function frameTelemetry(frame, range, divergence) {
19937
20300
  refusal: frame.refusal
19938
20301
  };
19939
20302
  if (divergence) {
19940
- t.root_differs = !!frame.worktreeRoot && !!divergence.actualRoot && realpathOr(frame.worktreeRoot) !== realpathOr(divergence.actualRoot);
20303
+ t.root_differs = !!frame.worktreeRoot && !!divergence.actualRoot && realpathOr2(frame.worktreeRoot) !== realpathOr2(divergence.actualRoot);
19941
20304
  const a = [...divergence.actualFiles].sort().join("\n");
19942
20305
  const b = [...divergence.frameFiles].sort().join("\n");
19943
20306
  t.files_differ = a !== b;
@@ -20247,7 +20610,7 @@ function sanitizeCommandWithLoss(rawCmd) {
20247
20610
  const lost = lines.slice(1).some((l) => l.trim().length > 0);
20248
20611
  const first = lines[0];
20249
20612
  const cmd = sanitizeCommand(first);
20250
- const hadSeparator = SEPARATORS.some((sep2) => first.indexOf(sep2) > 0);
20613
+ const hadSeparator = SEPARATORS.some((sep3) => first.indexOf(sep3) > 0);
20251
20614
  return { cmd, lost: lost || !hadSeparator && first.length > MAX_COMMAND_CHARS };
20252
20615
  }
20253
20616
  var SEPARATORS = [" | ", " > ", " >> ", " 2>", " && ", " ; "];
@@ -20256,11 +20619,11 @@ function sanitizeCommand(rawCmd) {
20256
20619
  let cmd = rawCmd.split("\n")[0];
20257
20620
  let cut = -1;
20258
20621
  let marker = "";
20259
- for (const sep2 of SEPARATORS) {
20260
- const idx = cmd.indexOf(sep2);
20622
+ for (const sep3 of SEPARATORS) {
20623
+ const idx = cmd.indexOf(sep3);
20261
20624
  if (idx > 0 && (cut === -1 || idx < cut)) {
20262
20625
  cut = idx;
20263
- marker = sep2.trim();
20626
+ marker = sep3.trim();
20264
20627
  }
20265
20628
  }
20266
20629
  if (cut > -1) cmd = cmd.slice(0, cut);
@@ -20284,28 +20647,28 @@ async function readStopHookStdin() {
20284
20647
  try {
20285
20648
  if (process.stdin.isTTY) return empty;
20286
20649
  const chunks = [];
20287
- const timeout = new Promise((resolve4) => setTimeout(() => resolve4(empty), 500));
20288
- const read = new Promise((resolve4) => {
20650
+ const timeout = new Promise((resolve5) => setTimeout(() => resolve5(empty), 500));
20651
+ const read = new Promise((resolve5) => {
20289
20652
  process.stdin.on("data", (chunk) => chunks.push(chunk));
20290
20653
  process.stdin.on("end", () => {
20291
20654
  const raw = Buffer.concat(chunks).toString("utf-8").trim();
20292
20655
  if (!raw) {
20293
- resolve4(empty);
20656
+ resolve5(empty);
20294
20657
  return;
20295
20658
  }
20296
20659
  try {
20297
20660
  const data = JSON.parse(raw);
20298
- resolve4({
20661
+ resolve5({
20299
20662
  assistantMessage: typeof data.last_assistant_message === "string" ? data.last_assistant_message : null,
20300
20663
  stopReason: typeof data.stop_reason === "string" ? data.stop_reason : null,
20301
20664
  transcriptPath: typeof data.transcript_path === "string" ? data.transcript_path : null,
20302
20665
  sessionId: typeof data.session_id === "string" ? data.session_id : null
20303
20666
  });
20304
20667
  } catch {
20305
- resolve4(empty);
20668
+ resolve5(empty);
20306
20669
  }
20307
20670
  });
20308
- process.stdin.on("error", () => resolve4(empty));
20671
+ process.stdin.on("error", () => resolve5(empty));
20309
20672
  process.stdin.resume();
20310
20673
  });
20311
20674
  return await Promise.race([read, timeout]);
@@ -20509,7 +20872,7 @@ function channelSilence(input) {
20509
20872
  // src/lib/cli-version.ts
20510
20873
  function cliVersion() {
20511
20874
  try {
20512
- return true ? "0.32.0" : "dev";
20875
+ return true ? "0.32.1-experimental.c640abd" : "dev";
20513
20876
  } catch {
20514
20877
  return "dev";
20515
20878
  }
@@ -20868,7 +21231,7 @@ async function scope(run2) {
20868
21231
 
20869
21232
  // src/lib/specs.ts
20870
21233
  var import_node_fs34 = require("node:fs");
20871
- var import_node_path23 = require("node:path");
21234
+ var import_node_path25 = require("node:path");
20872
21235
  var SPEC_CANDIDATES = [
20873
21236
  "CLAUDE.md",
20874
21237
  "AGENTS.md",
@@ -20941,7 +21304,7 @@ function findMdFiles(dir, maxDepth, depth = 0) {
20941
21304
  try {
20942
21305
  const entries = (0, import_node_fs34.readdirSync)(dir, { withFileTypes: true });
20943
21306
  for (const entry of entries) {
20944
- const fullPath = (0, import_node_path23.join)(dir, entry.name);
21307
+ const fullPath = (0, import_node_path25.join)(dir, entry.name);
20945
21308
  if (entry.isFile() && entry.name.endsWith(".md")) {
20946
21309
  result.push(fullPath);
20947
21310
  } else if (entry.isDirectory() && depth < maxDepth - 1) {
@@ -20953,9 +21316,9 @@ function findMdFiles(dir, maxDepth, depth = 0) {
20953
21316
  return result;
20954
21317
  }
20955
21318
  function discoverPlans() {
20956
- const homePlansDir = (0, import_node_path23.join)(process.env.HOME ?? "", ".claude", "plans");
21319
+ const homePlansDir = (0, import_node_path25.join)(process.env.HOME ?? "", ".claude", "plans");
20957
21320
  const localPlansDir = ".claude/plans";
20958
- const candidates = [];
21321
+ const candidates2 = [];
20959
21322
  const seen = /* @__PURE__ */ new Set();
20960
21323
  for (const plansDir of [localPlansDir, homePlansDir]) {
20961
21324
  if (!(0, import_node_fs34.existsSync)(plansDir)) continue;
@@ -20963,19 +21326,19 @@ function discoverPlans() {
20963
21326
  for (const f of (0, import_node_fs34.readdirSync)(plansDir)) {
20964
21327
  if (!f.endsWith(".md") || seen.has(f)) continue;
20965
21328
  seen.add(f);
20966
- const fullPath = (0, import_node_path23.join)(plansDir, f);
21329
+ const fullPath = (0, import_node_path25.join)(plansDir, f);
20967
21330
  try {
20968
21331
  const stat3 = (0, import_node_fs34.statSync)(fullPath);
20969
- candidates.push({ name: f, path: fullPath, mtime: stat3.mtimeMs, size: stat3.size });
21332
+ candidates2.push({ name: f, path: fullPath, mtime: stat3.mtimeMs, size: stat3.size });
20970
21333
  } catch {
20971
21334
  }
20972
21335
  }
20973
21336
  } catch {
20974
21337
  }
20975
21338
  }
20976
- candidates.sort((a, b) => b.mtime - a.mtime);
21339
+ candidates2.sort((a, b) => b.mtime - a.mtime);
20977
21340
  const result = [];
20978
- for (const entry of candidates.slice(0, MAX_PLAN_FILES)) {
21341
+ for (const entry of candidates2.slice(0, MAX_PLAN_FILES)) {
20979
21342
  if (entry.size > MAX_PLAN_FILE_BYTES) continue;
20980
21343
  try {
20981
21344
  const content = (0, import_node_fs34.readFileSync)(entry.path, "utf-8");
@@ -21175,7 +21538,7 @@ async function mode(run2) {
21175
21538
 
21176
21539
  // src/lib/fold.ts
21177
21540
  var import_node_fs35 = require("node:fs");
21178
- var import_node_path24 = require("node:path");
21541
+ var import_node_path26 = require("node:path");
21179
21542
  var KNOWN_RECORD_TYPES = /* @__PURE__ */ new Set([
21180
21543
  "user",
21181
21544
  "assistant",
@@ -21407,9 +21770,9 @@ function fold(transcriptPath, opts = {}) {
21407
21770
  return result;
21408
21771
  }
21409
21772
  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$/, ""),
21773
+ const sidecarDir = (0, import_node_path26.join)(
21774
+ (0, import_node_path26.dirname)(transcriptPath),
21775
+ (0, import_node_path26.basename)(transcriptPath).replace(/\.jsonl$/, ""),
21413
21776
  "subagents"
21414
21777
  );
21415
21778
  if ((0, import_node_fs35.existsSync)(sidecarDir)) {
@@ -21419,7 +21782,7 @@ function fold(transcriptPath, opts = {}) {
21419
21782
  const walk2 = (d, depth) => {
21420
21783
  if (depth > 4) return;
21421
21784
  for (const e of (0, import_node_fs35.readdirSync)(d, { withFileTypes: true })) {
21422
- const p = (0, import_node_path24.join)(d, e.name);
21785
+ const p = (0, import_node_path26.join)(d, e.name);
21423
21786
  if (e.isDirectory()) {
21424
21787
  walk2(p, depth + 1);
21425
21788
  } else if (e.name.startsWith("agent-") && e.name.endsWith(".jsonl")) {
@@ -21794,7 +22157,7 @@ async function evidence(run2) {
21794
22157
 
21795
22158
  // src/lib/cache-cleanup.ts
21796
22159
  var import_node_fs36 = require("node:fs");
21797
- var import_node_path25 = require("node:path");
22160
+ var import_node_path27 = require("node:path");
21798
22161
  var CACHE_TTL_DAYS = 7;
21799
22162
  function pruneStaleCache() {
21800
22163
  try {
@@ -21802,7 +22165,7 @@ function pruneStaleCache() {
21802
22165
  const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
21803
22166
  for (const entry of (0, import_node_fs36.readdirSync)(dir)) {
21804
22167
  if (!entry.startsWith("pending-")) continue;
21805
- const path = (0, import_node_path25.join)(dir, entry);
22168
+ const path = (0, import_node_path27.join)(dir, entry);
21806
22169
  try {
21807
22170
  const stat3 = (0, import_node_fs36.statSync)(path);
21808
22171
  if (stat3.mtimeMs < cutoff) {
@@ -21821,7 +22184,7 @@ function pruneStaleCache() {
21821
22184
 
21822
22185
  // src/lib/context-files.ts
21823
22186
  var import_node_fs37 = require("node:fs");
21824
- var import_node_os5 = require("node:os");
22187
+ var import_node_os6 = require("node:os");
21825
22188
  var MAX_CONTEXT_FILES = 10;
21826
22189
  var MAX_CONTEXT_FILE_BYTES = 10240;
21827
22190
  var MAX_CONTEXT_TOTAL_BYTES = 24576;
@@ -21829,7 +22192,7 @@ function readSetContextPaths(summary, deltaFiles) {
21829
22192
  const reads = summary?.files_read ?? [];
21830
22193
  if (reads.length === 0) return [];
21831
22194
  const root = process.cwd().replace(/\/+$/, "");
21832
- const home = (0, import_node_os5.homedir)();
22195
+ const home = (0, import_node_os6.homedir)();
21833
22196
  const toRepoRelative2 = (p) => {
21834
22197
  if (!p) return null;
21835
22198
  let abs;
@@ -21970,7 +22333,7 @@ async function repoContext(run2) {
21970
22333
  // src/lib/seed-runner.ts
21971
22334
  var import_promises14 = require("node:fs/promises");
21972
22335
  var import_node_fs38 = require("node:fs");
21973
- var import_node_path26 = require("node:path");
22336
+ var import_node_path28 = require("node:path");
21974
22337
  var import_yaml4 = __toESM(require_dist());
21975
22338
 
21976
22339
  // src/lib/seed.ts
@@ -22237,7 +22600,7 @@ async function runSeed(opts) {
22237
22600
  }
22238
22601
  }
22239
22602
  }
22240
- const candidates = deriveSeedNodes({
22603
+ const candidates2 = deriveSeedNodes({
22241
22604
  knowledgeSpec: {
22242
22605
  project_name: knowledgeSpec.project_name,
22243
22606
  languages: knowledgeSpec.languages,
@@ -22249,20 +22612,20 @@ async function runSeed(opts) {
22249
22612
  readmeContent,
22250
22613
  claudeMdContent
22251
22614
  });
22252
- if (candidates.length === 0) {
22615
+ if (candidates2.length === 0) {
22253
22616
  return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
22254
22617
  }
22255
- const overviewPath = (0, import_node_path26.join)(MEMORY_DIR, "domain", "project-overview.md");
22618
+ const overviewPath = (0, import_node_path28.join)(MEMORY_DIR, "domain", "project-overview.md");
22256
22619
  if ((0, import_node_fs38.existsSync)(overviewPath) && !opts.force) {
22257
- return { created: 0, failed: 0, skipped: "already_seeded", candidates };
22620
+ return { created: 0, failed: 0, skipped: "already_seeded", candidates: candidates2 };
22258
22621
  }
22259
22622
  if (opts.dryRun) {
22260
- return { created: 0, failed: 0, skipped: null, candidates };
22623
+ return { created: 0, failed: 0, skipped: null, candidates: candidates2 };
22261
22624
  }
22262
22625
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
22263
22626
  let created = 0;
22264
22627
  let failed = 0;
22265
- for (const c of candidates) {
22628
+ for (const c of candidates2) {
22266
22629
  const res = await apiRequest({
22267
22630
  method: "POST",
22268
22631
  path: "/compound/memory/nodes",
@@ -22295,7 +22658,7 @@ async function runSeed(opts) {
22295
22658
  continue;
22296
22659
  }
22297
22660
  try {
22298
- await (0, import_promises14.mkdir)((0, import_node_path26.dirname)(targetPath), { recursive: true });
22661
+ await (0, import_promises14.mkdir)((0, import_node_path28.dirname)(targetPath), { recursive: true });
22299
22662
  await (0, import_promises14.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
22300
22663
  created++;
22301
22664
  opts.onCreated?.(nodeId, filePathRel, c);
@@ -22304,12 +22667,12 @@ async function runSeed(opts) {
22304
22667
  failed++;
22305
22668
  }
22306
22669
  }
22307
- return { created, failed, skipped: null, candidates };
22670
+ return { created, failed, skipped: null, candidates: candidates2 };
22308
22671
  }
22309
22672
 
22310
22673
  // src/commands/analyze/phases/08-memory-manifest.ts
22311
22674
  var import_node_fs39 = require("node:fs");
22312
- var import_node_path27 = require("node:path");
22675
+ var import_node_path29 = require("node:path");
22313
22676
  async function memoryManifest(run2) {
22314
22677
  const { globals } = run2;
22315
22678
  const { serviceUrl, token } = run2;
@@ -22319,7 +22682,7 @@ async function memoryManifest(run2) {
22319
22682
  let autoSeedNotice = null;
22320
22683
  try {
22321
22684
  await ensureMemoryDir();
22322
- const seedMarker = (0, import_node_path27.join)(VERITY_DIR, ".seeded");
22685
+ const seedMarker = (0, import_node_path29.join)(VERITY_DIR, ".seeded");
22323
22686
  const hasStandard = (0, import_node_fs39.existsSync)(STANDARD_FILE);
22324
22687
  const alreadyTried = (0, import_node_fs39.existsSync)(seedMarker);
22325
22688
  if (hasStandard && !alreadyTried) {
@@ -22437,7 +22800,7 @@ function computeIncrement(reviewedPaths, hashOf, priorAuthored) {
22437
22800
  }
22438
22801
 
22439
22802
  // src/commands/analyze/phases/10-working-memory.ts
22440
- var import_node_path28 = require("node:path");
22803
+ var import_node_path30 = require("node:path");
22441
22804
  async function workingMemory(run2) {
22442
22805
  const { opts } = run2;
22443
22806
  const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } = run2;
@@ -22449,7 +22812,7 @@ async function workingMemory(run2) {
22449
22812
  const priorState = foldForMarks(memorySession.d);
22450
22813
  incrementReport = computeIncrement(
22451
22814
  allForReview,
22452
- (p) => fileHash((0, import_node_path28.join)(repoRoot(), p)),
22815
+ (p) => fileHash((0, import_node_path30.join)(repoRoot(), p)),
22453
22816
  priorState.authored_all.map((a) => ({
22454
22817
  path: a.path,
22455
22818
  hash_at_last_verdict: a.hash_at_last_verdict
@@ -23014,7 +23377,7 @@ async function transmit(run2) {
23014
23377
 
23015
23378
  // src/commands/analyze/phases/13-reconcile.ts
23016
23379
  var import_node_fs42 = require("node:fs");
23017
- var import_node_path29 = require("node:path");
23380
+ var import_node_path31 = require("node:path");
23018
23381
  async function reconcile(run2) {
23019
23382
  const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run2;
23020
23383
  const sentPaths = codeDelta.files.map((f) => f.path);
@@ -23043,7 +23406,7 @@ async function reconcile(run2) {
23043
23406
  const st = foldDossier(memorySession.d);
23044
23407
  openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line) => {
23045
23408
  try {
23046
- const src = (0, import_node_fs42.readFileSync)((0, import_node_path29.join)(repoRoot(), file), "utf8").split("\n");
23409
+ const src = (0, import_node_fs42.readFileSync)((0, import_node_path31.join)(repoRoot(), file), "utf8").split("\n");
23047
23410
  const at = src[line - 1];
23048
23411
  return at === void 0 ? null : lineSha(at);
23049
23412
  } catch {
@@ -23895,14 +24258,14 @@ async function runReview(opts, globals) {
23895
24258
 
23896
24259
  // src/commands/guard.ts
23897
24260
  var import_node_fs45 = require("node:fs");
23898
- var import_node_path30 = require("node:path");
24261
+ var import_node_path32 = require("node:path");
23899
24262
  var GUARD_BLOCK_CAP = 2;
23900
- var GUARD_ITER_FILE = (0, import_node_path30.join)(VERITY_DIR, ".guard-iteration");
24263
+ var GUARD_ITER_FILE = (0, import_node_path32.join)(VERITY_DIR, ".guard-iteration");
23901
24264
  function readPreToolUseStdin() {
23902
24265
  const empty = { command: "", cwd: null, sessionId: null };
23903
- return new Promise((resolve4) => {
24266
+ return new Promise((resolve5) => {
23904
24267
  try {
23905
- if (process.stdin.isTTY) return resolve4(empty);
24268
+ if (process.stdin.isTTY) return resolve5(empty);
23906
24269
  const chunks = [];
23907
24270
  let timer;
23908
24271
  let settled = false;
@@ -23915,7 +24278,7 @@ function readPreToolUseStdin() {
23915
24278
  process.stdin.removeListener("end", onEnd);
23916
24279
  process.stdin.removeListener("error", onError);
23917
24280
  process.stdin.pause();
23918
- resolve4(value);
24281
+ resolve5(value);
23919
24282
  };
23920
24283
  const onEnd = () => {
23921
24284
  try {
@@ -23936,7 +24299,7 @@ function readPreToolUseStdin() {
23936
24299
  process.stdin.on("error", onError);
23937
24300
  process.stdin.resume();
23938
24301
  } catch {
23939
- resolve4(empty);
24302
+ resolve5(empty);
23940
24303
  }
23941
24304
  });
23942
24305
  }
@@ -23994,7 +24357,7 @@ function registerGuardCommand(program2) {
23994
24357
  });
23995
24358
  }
23996
24359
  function resolveMomentRange(moment, frame, command, on) {
23997
- return moment === "pre-commit" ? stagedRange(frame) : resolvePushRange(frame, command, on);
24360
+ return moment === "pre-commit" ? stagedRange(frame, command) : resolvePushRange(frame, command, on);
23998
24361
  }
23999
24362
  function describeRange(range) {
24000
24363
  if (range.kind === "staged") return "staged";
@@ -24154,6 +24517,13 @@ async function runGuard(opts, globals) {
24154
24517
  );
24155
24518
  }
24156
24519
  const range = resolveMomentRange(moment, frame, command, on);
24520
+ if (range.refusal) {
24521
+ logEvent("guard_frame", { moment, ...frameTelemetry(frame, range) });
24522
+ emitAllowNotice(
24523
+ `\u26A0 Verity ${moment}: could not tell what this ${verb} will include \u2014 ${verb === "commit" ? "committed" : "pushed"} WITHOUT review`,
24524
+ `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.`
24525
+ );
24526
+ }
24157
24527
  const files = rangeFiles(frame, range);
24158
24528
  if (files.length === 0) process.exit(0);
24159
24529
  const tokenResult = await resolveToken(globals.token);
@@ -24189,7 +24559,7 @@ async function runGuard(opts, globals) {
24189
24559
  upgradeToExcerpts(repoContext2, {
24190
24560
  readFile: (rel) => {
24191
24561
  try {
24192
- return (0, import_node_fs45.readFileSync)((0, import_node_path30.join)(frame.worktreeRoot ?? process.cwd(), rel), "utf8");
24562
+ return (0, import_node_fs45.readFileSync)((0, import_node_path32.join)(frame.worktreeRoot ?? process.cwd(), rel), "utf8");
24193
24563
  } catch {
24194
24564
  return null;
24195
24565
  }
@@ -24250,29 +24620,50 @@ async function runGuard(opts, globals) {
24250
24620
  process.exit(2);
24251
24621
  }
24252
24622
  resetIter(moment);
24623
+ const notice = verdictNotice({
24624
+ decision,
24625
+ moment,
24626
+ verb,
24627
+ covLine,
24628
+ covDetail,
24629
+ link,
24630
+ viewUrl,
24631
+ narrative: response.assessment?.narrative ?? ""
24632
+ });
24633
+ emitAllowNotice(notice.user, notice.agent);
24634
+ }
24635
+ function verdictNotice(ctx) {
24636
+ const { moment, verb, covLine, covDetail, link, viewUrl } = ctx;
24637
+ const decision = ctx.decision ?? "(unrecognised)";
24638
+ const report = viewUrl ? `
24639
+ Report: ${viewUrl}` : "";
24253
24640
  if (decision === "FAIL") {
24254
- const narrative = response.assessment?.narrative ?? "";
24255
- emitAllowNotice(
24256
- `\u26A0 Verity ${moment}: the ${verb} may not match its stated purpose \u2014 proceeding (${covLine})${link}`,
24257
- `Verity ${moment}: intent-alignment WARNING (not blocked).${narrative ? " " + narrative : ""}
24258
- ${covDetail}${viewUrl ? `
24259
- Report: ${viewUrl}` : ""}`
24260
- );
24641
+ const narrative = ctx.narrative ?? "";
24642
+ return {
24643
+ user: `\u26A0 Verity ${moment}: the ${verb} may not match its stated purpose \u2014 proceeding (${covLine})${link}`,
24644
+ agent: `Verity ${moment}: intent-alignment WARNING (not blocked).${narrative ? " " + narrative : ""}
24645
+ ${covDetail}${report}`
24646
+ };
24261
24647
  }
24262
24648
  if (decision === "WARN") {
24263
- emitAllowNotice(
24264
- `\u26A0 Verity ${moment}: WARN \u2014 proceeding (${covLine})${link}`,
24265
- `Verity ${moment} review: WARN (proceeding).
24266
- ${covDetail}${viewUrl ? `
24267
- Report: ${viewUrl}` : ""}`
24268
- );
24649
+ return {
24650
+ user: `\u26A0 Verity ${moment}: WARN \u2014 proceeding (${covLine})${link}`,
24651
+ agent: `Verity ${moment} review: WARN (proceeding).
24652
+ ${covDetail}${report}`
24653
+ };
24269
24654
  }
24270
- emitAllowNotice(
24271
- `\u2713 Verity ${moment}: PASS (${covLine})${link}`,
24272
- `Verity ${moment} review: PASS.
24273
- ${covDetail}${viewUrl ? `
24274
- Report: ${viewUrl}` : ""}`
24275
- );
24655
+ if (decision === "PASS") {
24656
+ return {
24657
+ user: `\u2713 Verity ${moment}: PASS (${covLine})${link}`,
24658
+ agent: `Verity ${moment} review: PASS.
24659
+ ${covDetail}${report}`
24660
+ };
24661
+ }
24662
+ return {
24663
+ user: `\u26A0 Verity ${moment}: no verdict came back \u2014 ${verb === "commit" ? "committed" : "pushed"} WITHOUT a usable review (${covLine})${link}`,
24664
+ agent: `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.
24665
+ ${covDetail}${report}`
24666
+ };
24276
24667
  }
24277
24668
  function writeBlockMessage(moment, response, covDetail) {
24278
24669
  const label2 = moment === "pre-commit" ? "pre-commit" : "pre-push";
@@ -24467,7 +24858,7 @@ function registerWaiveCommand(program2) {
24467
24858
  var import_node_fs51 = require("node:fs");
24468
24859
  var import_promises17 = require("node:fs/promises");
24469
24860
  var import_yaml6 = __toESM(require_dist());
24470
- var import_node_path33 = require("node:path");
24861
+ var import_node_path35 = require("node:path");
24471
24862
  var import_node_child_process16 = require("node:child_process");
24472
24863
 
24473
24864
  // src/lib/banner.ts
@@ -24504,8 +24895,8 @@ function printBanner(opts) {
24504
24895
  const dim = (text) => color ? `${DIM3}${text}${RESET2}` : text;
24505
24896
  process.stderr.write("\n");
24506
24897
  if (!art) {
24507
- const candidates = [`Verity ${version}`, "Verity"];
24508
- const text = candidates.find((c) => c.length + INDENT.length <= columns);
24898
+ const candidates2 = [`Verity ${version}`, "Verity"];
24899
+ const text = candidates2.find((c) => c.length + INDENT.length <= columns);
24509
24900
  if (text) process.stderr.write(`${INDENT}${dim(text)}
24510
24901
  `);
24511
24902
  process.stderr.write("\n");
@@ -24612,13 +25003,13 @@ function checkAnalysisCli() {
24612
25003
  }
24613
25004
  var INSTALL_TIMEOUT_MS = 12e4;
24614
25005
  function run(command, args, opts = {}) {
24615
- return new Promise((resolve4) => {
25006
+ return new Promise((resolve5) => {
24616
25007
  const child = (0, import_node_child_process13.spawn)(command, args, {
24617
25008
  stdio: opts.inherit ? "inherit" : "pipe",
24618
25009
  timeout: INSTALL_TIMEOUT_MS
24619
25010
  });
24620
- child.on("error", () => resolve4(false));
24621
- child.on("close", (code) => resolve4(code === 0));
25011
+ child.on("error", () => resolve5(false));
25012
+ child.on("close", (code) => resolve5(code === 0));
24622
25013
  });
24623
25014
  }
24624
25015
  async function installAnalysisCli() {
@@ -24717,8 +25108,8 @@ function ensureVerityGitignore() {
24717
25108
  next = lines.map((l) => BREAKING_ENTRIES.has(l.trim()) ? ".verity/*" : l).join("\n");
24718
25109
  }
24719
25110
  if (!hasMarker) {
24720
- const sep2 = next === "" ? "" : next.endsWith("\n") ? "\n" : "\n\n";
24721
- next = next + sep2 + VERITY_GITIGNORE_BLOCK;
25111
+ const sep3 = next === "" ? "" : next.endsWith("\n") ? "\n" : "\n\n";
25112
+ next = next + sep3 + VERITY_GITIGNORE_BLOCK;
24722
25113
  }
24723
25114
  (0, import_node_fs47.writeFileSync)(".gitignore", next);
24724
25115
  return verified(needsRepair ? "repaired" : "added");
@@ -24845,7 +25236,8 @@ async function uninstallTelemetry() {
24845
25236
  async function buildReport() {
24846
25237
  const prereqs = await checkPrereqs({ install: false });
24847
25238
  const state = await readSetupState();
24848
- const hooks = await checkAllVerityHooks();
25239
+ const wiring = await resolveHookWiring();
25240
+ const hooks = wiring.status;
24849
25241
  const telemetry = await checkTelemetry();
24850
25242
  const hasConfig = (0, import_node_fs48.existsSync)(projectPath(CODACY_CONFIG_FILE));
24851
25243
  const artifacts = {
@@ -24884,6 +25276,11 @@ async function buildReport() {
24884
25276
  '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
25277
  );
24886
25278
  }
25279
+ if (wiring.duplicateSettingsHooks) {
25280
+ next.push(
25281
+ '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.'
25282
+ );
25283
+ }
24887
25284
  if (state?.telemetry === "deferred") {
24888
25285
  next.push('Telemetry was requested but needs a token \u2014 run "verity login", then "verity telemetry install".');
24889
25286
  }
@@ -24899,7 +25296,12 @@ async function buildReport() {
24899
25296
  moments: state?.moments ?? null,
24900
25297
  telemetry: state?.telemetry ?? null
24901
25298
  },
24902
- hooks: { ...hooks, noAnalysisMoment },
25299
+ hooks: {
25300
+ source: wiring.source,
25301
+ ...hooks,
25302
+ duplicateSettingsHooks: wiring.duplicateSettingsHooks,
25303
+ noAnalysisMoment
25304
+ },
24903
25305
  telemetry: { enabled: telemetry.enabled, endpoint: telemetry.endpoint },
24904
25306
  artifacts,
24905
25307
  next
@@ -24922,8 +25324,9 @@ function registerDoctorCommand(program2) {
24922
25324
  printInfo(` verity init: ${report.phases.init.done ? `done ${report.phases.init.completed_at ?? ""}`.trim() : "NOT run here"}`);
24923
25325
  printInfo(` /verity-setup: ${report.phases.setup.done ? "done" : "not completed"}`);
24924
25326
  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"}`);
25327
+ const plugin = report.hooks.source === "plugin";
25328
+ printInfo(plugin ? "Hooks: wired by the Verity Claude Code plugin (not .claude/settings.json)" : "Hooks:");
25329
+ printInfo(` Stop (verity analyze): ${report.hooks.stop ? plugin ? "on (plugin)" : "on" : "off"}`);
24927
25330
  printInfo(` Git-moment gate: ${report.hooks.guardOn.length ? report.hooks.guardOn.join(", ") : "off"}`);
24928
25331
  printInfo(` Infra (intent/baseline/compact/session-end): ${[report.hooks.intent, report.hooks.baseline, report.hooks.compact, report.hooks.sessionEnd].filter(Boolean).length}/4`);
24929
25332
  printInfo(`Telemetry: ${report.telemetry.enabled ? `enabled \u2192 ${report.telemetry.endpoint}` : "disabled"}`);
@@ -24946,7 +25349,7 @@ function registerDoctorCommand(program2) {
24946
25349
 
24947
25350
  // src/commands/migrate.ts
24948
25351
  var import_node_fs49 = require("node:fs");
24949
- var import_node_path31 = require("node:path");
25352
+ var import_node_path33 = require("node:path");
24950
25353
  var import_node_child_process15 = require("node:child_process");
24951
25354
  var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
24952
25355
  function defaultNpmRemover(pkg) {
@@ -24983,8 +25386,8 @@ async function runMigration(opts = {}) {
24983
25386
  return { actions, migrated: actions.length > 0 };
24984
25387
  }
24985
25388
  function migrateProjectDir(root, actions) {
24986
- const gateDir = (0, import_node_path31.join)(root, ".gate");
24987
- const verityDir = (0, import_node_path31.join)(root, ".verity");
25389
+ const gateDir = (0, import_node_path33.join)(root, ".gate");
25390
+ const verityDir = (0, import_node_path33.join)(root, ".verity");
24988
25391
  if ((0, import_node_fs49.existsSync)(gateDir) && !(0, import_node_fs49.existsSync)(verityDir)) {
24989
25392
  return migrateProjectDirRename(root, gateDir, verityDir, actions);
24990
25393
  }
@@ -25038,11 +25441,11 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
25038
25441
  }
25039
25442
  function migrateGlobalCredentials(home, actions) {
25040
25443
  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");
25444
+ const gateCreds = (0, import_node_path33.join)(home, ".gate", "credentials");
25445
+ const verityCreds = (0, import_node_path33.join)(home, ".verity", "credentials");
25043
25446
  if (!(0, import_node_fs49.existsSync)(gateCreds)) return;
25044
25447
  if (!(0, import_node_fs49.existsSync)(verityCreds)) {
25045
- (0, import_node_fs49.mkdirSync)((0, import_node_path31.join)(home, ".verity"), { recursive: true });
25448
+ (0, import_node_fs49.mkdirSync)((0, import_node_path33.join)(home, ".verity"), { recursive: true });
25046
25449
  moveFile(gateCreds, verityCreds);
25047
25450
  actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
25048
25451
  return;
@@ -25064,7 +25467,7 @@ async function migrateLegacyHooks(root, actions) {
25064
25467
  }
25065
25468
  }
25066
25469
  async function migrateClaudeMd(root, actions) {
25067
- const claudeMd = (0, import_node_path31.join)(root, "CLAUDE.md");
25470
+ const claudeMd = (0, import_node_path33.join)(root, "CLAUDE.md");
25068
25471
  const hadLegacyBlock = (0, import_node_fs49.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
25069
25472
  if (!hadLegacyBlock) return;
25070
25473
  try {
@@ -25075,8 +25478,8 @@ async function migrateClaudeMd(root, actions) {
25075
25478
  }
25076
25479
  }
25077
25480
  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");
25481
+ const gateMd = (0, import_node_path33.join)(root, "GATE.md");
25482
+ const verityMd = (0, import_node_path33.join)(root, "VERITY.md");
25080
25483
  if (!(0, import_node_fs49.existsSync)(gateMd) || (0, import_node_fs49.existsSync)(verityMd)) return;
25081
25484
  let moved = false;
25082
25485
  if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
@@ -25093,7 +25496,7 @@ function migrateStandardFile(root, actions) {
25093
25496
  actions.push("Renamed GATE.md \u2192 VERITY.md");
25094
25497
  }
25095
25498
  async function migrateTelemetryHeaders(root, actions) {
25096
- const file = (0, import_node_path31.join)(root, ".claude", "settings.local.json");
25499
+ const file = (0, import_node_path33.join)(root, ".claude", "settings.local.json");
25097
25500
  if (!(0, import_node_fs49.existsSync)(file)) return;
25098
25501
  let settings;
25099
25502
  try {
@@ -25141,8 +25544,8 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
25141
25544
  toAppend.push(line.replace(/\r$/, ""));
25142
25545
  }
25143
25546
  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");
25547
+ const sep3 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
25548
+ (0, import_node_fs49.writeFileSync)(verityCreds, verityContent + sep3 + toAppend.join("\n") + "\n");
25146
25549
  }
25147
25550
  (0, import_node_fs49.rmSync)(gateCreds, { force: true });
25148
25551
  return toAppend.length;
@@ -25183,15 +25586,15 @@ function moveFile(from, to) {
25183
25586
  function carryLegacyContents(gateDir, verityDir) {
25184
25587
  let copied = 0;
25185
25588
  const walk2 = (relDir) => {
25186
- const srcDir = (0, import_node_path31.join)(gateDir, relDir);
25589
+ const srcDir = (0, import_node_path33.join)(gateDir, relDir);
25187
25590
  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);
25591
+ const rel = relDir ? (0, import_node_path33.join)(relDir, entry) : entry;
25592
+ const src = (0, import_node_path33.join)(gateDir, rel);
25593
+ const dest = (0, import_node_path33.join)(verityDir, rel);
25191
25594
  if ((0, import_node_fs49.statSync)(src).isDirectory()) {
25192
25595
  walk2(rel);
25193
25596
  } else if (!(0, import_node_fs49.existsSync)(dest)) {
25194
- (0, import_node_fs49.mkdirSync)((0, import_node_path31.dirname)(dest), { recursive: true });
25597
+ (0, import_node_fs49.mkdirSync)((0, import_node_path33.dirname)(dest), { recursive: true });
25195
25598
  (0, import_node_fs49.cpSync)(src, dest);
25196
25599
  copied++;
25197
25600
  }
@@ -25201,22 +25604,22 @@ function carryLegacyContents(gateDir, verityDir) {
25201
25604
  return copied;
25202
25605
  }
25203
25606
  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");
25607
+ const gateDir = (0, import_node_path33.join)(root, ".gate");
25608
+ const verityDir = (0, import_node_path33.join)(root, ".verity");
25206
25609
  if ((0, import_node_fs49.existsSync)(gateDir) && !(0, import_node_fs49.existsSync)(verityDir)) return true;
25207
25610
  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"))) {
25611
+ 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
25612
  return true;
25210
25613
  }
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"))) {
25614
+ 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
25615
  return true;
25213
25616
  }
25214
25617
  }
25215
- const claudeMd = (0, import_node_path31.join)(root, "CLAUDE.md");
25618
+ const claudeMd = (0, import_node_path33.join)(root, "CLAUDE.md");
25216
25619
  if ((0, import_node_fs49.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
25217
25620
  return true;
25218
25621
  }
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"))) {
25622
+ 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
25623
  return true;
25221
25624
  }
25222
25625
  if (await hasLegacyHooksAt(root)) return true;
@@ -25280,7 +25683,13 @@ function applyKey(state, action, count, mode2) {
25280
25683
  }
25281
25684
  case "jump": {
25282
25685
  if (action.index >= count) return { status: "open", state };
25283
- return { status: "open", state: { cursor: action.index, chosen: state.chosen } };
25686
+ if (mode2 === "single") {
25687
+ return { status: "open", state: { cursor: action.index, chosen: state.chosen } };
25688
+ }
25689
+ const chosen = new Set(state.chosen);
25690
+ if (chosen.has(action.index)) chosen.delete(action.index);
25691
+ else chosen.add(action.index);
25692
+ return { status: "open", state: { cursor: action.index, chosen } };
25284
25693
  }
25285
25694
  case "toggle": {
25286
25695
  if (mode2 === "single") {
@@ -25325,7 +25734,7 @@ function renderSelect(question, choices, state, mode2, color = colorEnabled()) {
25325
25734
  const hint = choice.hint ? `${pad} ${paint(choice.hint, DIM4)}` : "";
25326
25735
  lines.push(` ${cursor} ${marker} ${here ? paint(label2, BOLD2) : label2}${hint}`);
25327
25736
  });
25328
- const keys = mode2 === "multi" ? "\u2191\u2193 move \xB7 space toggle \xB7 enter confirm" : "\u2191\u2193 move \xB7 enter confirm";
25737
+ 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
25738
  lines.push(` ${paint(state.hint ?? keys, state.hint ? GREEN3 : DIM4)}`);
25330
25739
  return lines;
25331
25740
  }
@@ -25339,7 +25748,7 @@ function runSelect(opts) {
25339
25748
  cursor: initialIdx[0] ?? 0,
25340
25749
  chosen: new Set(mode2 === "single" ? [initialIdx[0] ?? 0] : initialIdx)
25341
25750
  };
25342
- return new Promise((resolve4) => {
25751
+ return new Promise((resolve5) => {
25343
25752
  let painted = 0;
25344
25753
  let settled = false;
25345
25754
  const draw = () => {
@@ -25387,7 +25796,7 @@ function runSelect(opts) {
25387
25796
  if (settled) return;
25388
25797
  settled = true;
25389
25798
  restore();
25390
- resolve4(value);
25799
+ resolve5(value);
25391
25800
  };
25392
25801
  readline2.emitKeypressEvents(input);
25393
25802
  input.setRawMode(true);
@@ -25408,9 +25817,9 @@ async function askLine(question, io = {}) {
25408
25817
  output: io.output ?? process.stdout
25409
25818
  });
25410
25819
  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)));
25820
+ return await new Promise((resolve5) => {
25821
+ rl.question(question).then((a) => resolve5(a.trim())).catch(() => resolve5(null));
25822
+ rl.once("close", () => setImmediate(() => resolve5(null)));
25414
25823
  });
25415
25824
  } finally {
25416
25825
  rl.close();
@@ -25493,7 +25902,7 @@ async function promptMultiSelect(question, choices, fallback) {
25493
25902
  // src/lib/remote-config.ts
25494
25903
  var import_node_fs50 = require("node:fs");
25495
25904
  var import_promises16 = require("node:fs/promises");
25496
- var import_node_path32 = require("node:path");
25905
+ var import_node_path34 = require("node:path");
25497
25906
  var import_yaml5 = __toESM(require_dist());
25498
25907
  var IGNORE_RIDER = "verityignore";
25499
25908
  async function fetchRemoteSetup(opts) {
@@ -25571,7 +25980,7 @@ async function adoptRemoteSetup(found, opts) {
25571
25980
  }
25572
25981
  async function writeOut(relative2, body) {
25573
25982
  const target = projectPath(relative2);
25574
- await (0, import_promises16.mkdir)((0, import_node_path32.dirname)(target), { recursive: true });
25983
+ await (0, import_promises16.mkdir)((0, import_node_path34.dirname)(target), { recursive: true });
25575
25984
  await (0, import_promises16.writeFile)(target, body);
25576
25985
  }
25577
25986
  function describeRemote(found) {
@@ -25687,16 +26096,16 @@ async function runOptionalAuth(resolution, opts = {}) {
25687
26096
  }
25688
26097
  }
25689
26098
  function resolveDataDir2() {
25690
- const candidates = [
25691
- (0, import_node_path33.join)(__dirname, "..", "data"),
26099
+ const candidates2 = [
26100
+ (0, import_node_path35.join)(__dirname, "..", "data"),
25692
26101
  // installed: node_modules/@codacy/verity-cli/data
25693
- (0, import_node_path33.join)(__dirname, "..", "..", "data"),
26102
+ (0, import_node_path35.join)(__dirname, "..", "..", "data"),
25694
26103
  // edge case: nested resolution
25695
- (0, import_node_path33.join)(process.cwd(), "cli", "data")
26104
+ (0, import_node_path35.join)(process.cwd(), "cli", "data")
25696
26105
  // local dev: running from repo root
25697
26106
  ];
25698
- for (const candidate of candidates) {
25699
- if ((0, import_node_fs51.existsSync)((0, import_node_path33.join)(candidate, "skills"))) {
26107
+ for (const candidate of candidates2) {
26108
+ if ((0, import_node_fs51.existsSync)((0, import_node_path35.join)(candidate, "skills"))) {
25700
26109
  return candidate;
25701
26110
  }
25702
26111
  }
@@ -25714,7 +26123,7 @@ async function skillIsCurrent(src, dest) {
25714
26123
  const walk2 = (d, prefix) => {
25715
26124
  for (const e of (0, import_node_fs51.readdirSync)(d, { withFileTypes: true })) {
25716
26125
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
25717
- if (e.isDirectory()) walk2((0, import_node_path33.join)(d, e.name), rel);
26126
+ if (e.isDirectory()) walk2((0, import_node_path35.join)(d, e.name), rel);
25718
26127
  else if (e.isFile()) out.push(rel);
25719
26128
  }
25720
26129
  };
@@ -25725,8 +26134,8 @@ async function skillIsCurrent(src, dest) {
25725
26134
  const shipped = list2(src);
25726
26135
  if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
25727
26136
  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");
26137
+ const a = await (0, import_promises17.readFile)((0, import_node_path35.join)(src, rel), "utf-8");
26138
+ const b = await (0, import_promises17.readFile)((0, import_node_path35.join)(dest, rel), "utf-8");
25730
26139
  if (a !== b) return false;
25731
26140
  }
25732
26141
  return true;
@@ -26001,12 +26410,12 @@ async function handoffToSetup(enabled, claudeInstalled) {
26001
26410
  async function installSkills(force, step) {
26002
26411
  step("Installing skills");
26003
26412
  const dataDir = resolveDataDir2();
26004
- const skillsSource = (0, import_node_path33.join)(dataDir, "skills");
26413
+ const skillsSource = (0, import_node_path35.join)(dataDir, "skills");
26005
26414
  const skillsDest = ".claude/skills";
26006
26415
  let skillsInstalled = 0;
26007
26416
  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);
26417
+ const src = (0, import_node_path35.join)(skillsSource, skill);
26418
+ const dest = (0, import_node_path35.join)(skillsDest, skill);
26010
26419
  if (!(0, import_node_fs51.existsSync)(src)) {
26011
26420
  printWarn(` Skill data not found: ${skill}`);
26012
26421
  continue;
@@ -26109,12 +26518,16 @@ function registerInitCommand(program2) {
26109
26518
  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
26519
  "--plugin-mode",
26111
26520
  "The Claude Code plugin owns the skills and hooks: install neither, and remove any this project already has"
26521
+ ).option(
26522
+ "--no-plugin",
26523
+ "Ignore any Claude Code plugin here and wire this project's own skills and hooks"
26112
26524
  ).option("--no-adopt", "Don't offer this repository's existing Standard from the service; synthesize a new one").action(async (opts) => {
26113
26525
  const force = opts.force ?? false;
26114
26526
  const wantsHandoff = opts.setup !== false;
26115
26527
  const wantsAdopt = opts.adopt !== false;
26116
26528
  const defaultsOnly = (opts.yes ?? false) || !interactive();
26117
- const pluginMode = opts.pluginMode ?? pluginActiveHere();
26529
+ const staleMarker = clearStalePluginMarker();
26530
+ const pluginMode = opts.plugin === false ? false : opts.pluginMode ?? pluginActiveHere();
26118
26531
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
26119
26532
  const isProject = projectMarkers.some((m) => (0, import_node_fs51.existsSync)(m));
26120
26533
  if (!isProject) {
@@ -26131,6 +26544,26 @@ function registerInitCommand(program2) {
26131
26544
  } else {
26132
26545
  printPhase(1, 2, "this machine", "prerequisites \xB7 skills \xB7 hooks \xB7 sign-in");
26133
26546
  }
26547
+ if (staleMarker) {
26548
+ printInfo(`Cleared a stale plugin marker \u2014 ${staleMarker.pluginRoot} is no longer installed.`);
26549
+ }
26550
+ const install = pluginMode ? activePluginInstall() : null;
26551
+ if (pluginMode) {
26552
+ const where = install ? ` \u2014 ${install.pluginRoot}${install.version ? ` (v${install.version})` : ""}` : "";
26553
+ printInfo(`Hooks here: the Verity Claude Code plugin${where}.`);
26554
+ printInfo(" Skills and hooks come from the plugin, so this run installs neither \u2014 and removes");
26555
+ printInfo(" any this project still carries from an earlier npm install.");
26556
+ printInfo(" It still does the half the plugin cannot: your Standard and analysis config, the");
26557
+ printInfo(" knowledge base, CLAUDE.md, .gitignore, the review moments, and sign-in.");
26558
+ printInfo(" Want this project to own its own copy instead? Uninstall the plugin, or use --no-plugin.");
26559
+ } else if (opts.plugin === false && pluginActiveHere()) {
26560
+ printWarn("Hooks here: this project's .claude/settings.json (--no-plugin).");
26561
+ printWarn(" The Verity plugin is active on this machine and would otherwise own them. Both sets");
26562
+ printWarn(" will fire; the settings.json copy stands down at run time so each turn is gated once.");
26563
+ } else {
26564
+ printInfo("Hooks here: this project's .claude/settings.json (no Verity Claude Code plugin is active).");
26565
+ }
26566
+ console.log("");
26134
26567
  const TOTAL_STEPS = 9;
26135
26568
  let stepNo = 0;
26136
26569
  const step = (label2) => {
@@ -26162,7 +26595,7 @@ function registerInitCommand(program2) {
26162
26595
  printInfo(` intensity: ${intensity} \xB7 moments: ${moments.join(", ") || "none"} (no questions asked)`);
26163
26596
  }
26164
26597
  await scaffoldProject(step, defaultsOnly);
26165
- const globalVerityDir = (0, import_node_path33.join)(process.env.HOME ?? "", ".verity");
26598
+ const globalVerityDir = (0, import_node_path35.join)(process.env.HOME ?? "", ".verity");
26166
26599
  await (0, import_promises17.mkdir)(globalVerityDir, { recursive: true });
26167
26600
  console.log("");
26168
26601
  step("Wiring Claude Code hooks");
@@ -26170,9 +26603,18 @@ function registerInitCommand(program2) {
26170
26603
  ...moments.includes("pre-commit") ? ["commit"] : [],
26171
26604
  ...moments.includes("pre-push") ? ["push"] : []
26172
26605
  ];
26173
- writeProjectConfig({ git_moments: gitMoments });
26606
+ const existingMoments = readProjectConfig();
26607
+ const keepExisting = defaultsOnly && existingMoments.git_moments_source === "user";
26608
+ if (keepExisting) {
26609
+ printInfo(
26610
+ ` Keeping the git moments you set: ${existingMoments.git_moments.join(", ") || "none"} (this run asked no questions, so it does not overrule them).`
26611
+ );
26612
+ } else {
26613
+ writeProjectConfig({ git_moments: gitMoments, git_moments_source: "init" });
26614
+ }
26615
+ const effectiveMoments = keepExisting ? existingMoments.git_moments : gitMoments;
26174
26616
  if (pluginMode) {
26175
- await adoptPluginWiring(gitMoments, moments);
26617
+ await adoptPluginWiring(effectiveMoments, moments);
26176
26618
  } else {
26177
26619
  await reconcileOwnWiring(moments);
26178
26620
  }
@@ -26244,7 +26686,7 @@ function registerInitCommand(program2) {
26244
26686
  ...telemetryChoice ? { telemetry: telemetryChoice } : {},
26245
26687
  init: {
26246
26688
  completed_at: (/* @__PURE__ */ new Date()).toISOString(),
26247
- cli_version: true ? "0.32.0" : "dev"
26689
+ cli_version: true ? "0.32.1-experimental.c640abd" : "dev"
26248
26690
  }
26249
26691
  });
26250
26692
  } catch (err) {
@@ -26284,7 +26726,7 @@ function registerInitCommand(program2) {
26284
26726
 
26285
26727
  // src/commands/uninstall.ts
26286
26728
  var import_node_fs52 = require("node:fs");
26287
- var import_node_path34 = require("node:path");
26729
+ var import_node_path36 = require("node:path");
26288
26730
  var SKILL_NAMES = [
26289
26731
  "verity-setup",
26290
26732
  "verity-analyze",
@@ -26303,7 +26745,7 @@ function registerUninstallCommand(program2) {
26303
26745
  const actions = [];
26304
26746
  const skillsRoot = projectPath(".claude/skills");
26305
26747
  for (const name of SKILL_NAMES) {
26306
- const dir = (0, import_node_path34.join)(skillsRoot, name);
26748
+ const dir = (0, import_node_path36.join)(skillsRoot, name);
26307
26749
  if ((0, import_node_fs52.existsSync)(dir)) {
26308
26750
  actions.push({
26309
26751
  label: `Remove .claude/skills/${name}/`,
@@ -26349,7 +26791,7 @@ function registerUninstallCommand(program2) {
26349
26791
  }
26350
26792
  });
26351
26793
  const home = process.env.HOME ?? "";
26352
- const globalVerityDir = (0, import_node_path34.join)(home, ".verity");
26794
+ const globalVerityDir = (0, import_node_path36.join)(home, ".verity");
26353
26795
  if (purgeGlobal && (0, import_node_fs52.existsSync)(globalVerityDir)) {
26354
26796
  actions.push({
26355
26797
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
@@ -26548,7 +26990,7 @@ function registerTaskCommands(program2) {
26548
26990
 
26549
26991
  // src/commands/reset.ts
26550
26992
  var import_node_fs53 = require("node:fs");
26551
- var import_node_path35 = require("node:path");
26993
+ var import_node_path37 = require("node:path");
26552
26994
  function registerResetCommand(program2) {
26553
26995
  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
26996
  const globals = program2.opts();
@@ -26589,7 +27031,7 @@ function registerResetCommand(program2) {
26589
27031
  for (const entry of (0, import_node_fs53.readdirSync)(cacheDir)) {
26590
27032
  if (entry.startsWith("pending-")) {
26591
27033
  try {
26592
- (0, import_node_fs53.unlinkSync)((0, import_node_path35.join)(cacheDir, entry));
27034
+ (0, import_node_fs53.unlinkSync)((0, import_node_path37.join)(cacheDir, entry));
26593
27035
  purged++;
26594
27036
  } catch {
26595
27037
  }
@@ -26616,7 +27058,7 @@ function registerResetCommand(program2) {
26616
27058
  if ((0, import_node_fs53.existsSync)(logsDir)) {
26617
27059
  for (const entry of (0, import_node_fs53.readdirSync)(logsDir)) {
26618
27060
  try {
26619
- (0, import_node_fs53.unlinkSync)((0, import_node_path35.join)(logsDir, entry));
27061
+ (0, import_node_fs53.unlinkSync)((0, import_node_path37.join)(logsDir, entry));
26620
27062
  } catch {
26621
27063
  }
26622
27064
  }
@@ -26924,8 +27366,8 @@ function registerTelemetryCommands(program2) {
26924
27366
  }
26925
27367
 
26926
27368
  // 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");
27369
+ program.name("verity").description("CLI for Verity quality gate service").version("0.32.1-experimental.c640abd").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) => {
27370
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.32.1-experimental.c640abd");
26929
27371
  setUserNamedServiceUrl(program.opts().serviceUrl);
26930
27372
  try {
26931
27373
  await foldLegacyLocalCredential();