@yawlabs/ctxlint 0.18.7 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1330,8 +1330,8 @@ var require_semaphore = __commonJS({
1330
1330
  this._waiting = [];
1331
1331
  }
1332
1332
  lock(thunk) {
1333
- return new Promise((resolve18, reject) => {
1334
- this._waiting.push({ thunk, resolve: resolve18, reject });
1333
+ return new Promise((resolve19, reject) => {
1334
+ this._waiting.push({ thunk, resolve: resolve19, reject });
1335
1335
  this.runNext();
1336
1336
  });
1337
1337
  }
@@ -2960,9 +2960,9 @@ ${JSON.stringify(message, null, 4)}`);
2960
2960
  if (typeof cancellationStrategy.sender.enableCancellation === "function") {
2961
2961
  cancellationStrategy.sender.enableCancellation(requestMessage);
2962
2962
  }
2963
- return new Promise(async (resolve18, reject) => {
2963
+ return new Promise(async (resolve19, reject) => {
2964
2964
  const resolveWithCleanup = (r2) => {
2965
- resolve18(r2);
2965
+ resolve19(r2);
2966
2966
  cancellationStrategy.sender.cleanup(id);
2967
2967
  disposable?.dispose();
2968
2968
  };
@@ -8089,7 +8089,7 @@ var require_server = __commonJS({
8089
8089
  exports.createConnection = createConnection2;
8090
8090
  var vscode_languageserver_protocol_1 = require_api2();
8091
8091
  var Is3 = __importStar(require_is());
8092
- var UUID = __importStar(require_uuid());
8092
+ var UUID2 = __importStar(require_uuid());
8093
8093
  var progress_1 = require_progress();
8094
8094
  var configuration_1 = require_configuration();
8095
8095
  var workspaceFolder_1 = require_workspaceFolder();
@@ -8231,7 +8231,7 @@ var require_server = __commonJS({
8231
8231
  if (this._registered.has(method)) {
8232
8232
  throw new Error(`${method} is already added to this registration`);
8233
8233
  }
8234
- const id = UUID.generateUuid();
8234
+ const id = UUID2.generateUuid();
8235
8235
  this._registrations.push({
8236
8236
  id,
8237
8237
  method,
@@ -8325,7 +8325,7 @@ var require_server = __commonJS({
8325
8325
  }
8326
8326
  registerSingle1(unregistration, type, registerOptions) {
8327
8327
  const method = Is3.string(type) ? type : type.method;
8328
- const id = UUID.generateUuid();
8328
+ const id = UUID2.generateUuid();
8329
8329
  const params = {
8330
8330
  registrations: [{ id, method, registerOptions: registerOptions || {} }]
8331
8331
  };
@@ -8342,7 +8342,7 @@ var require_server = __commonJS({
8342
8342
  }
8343
8343
  registerSingle2(type, registerOptions) {
8344
8344
  const method = Is3.string(type) ? type : type.method;
8345
- const id = UUID.generateUuid();
8345
+ const id = UUID2.generateUuid();
8346
8346
  const params = {
8347
8347
  registrations: [{ id, method, registerOptions: registerOptions || {} }]
8348
8348
  };
@@ -8590,8 +8590,8 @@ var require_server = __commonJS({
8590
8590
  if (value instanceof Promise) {
8591
8591
  return value;
8592
8592
  } else if (Is3.thenable(value)) {
8593
- return new Promise((resolve18, reject) => {
8594
- value.then((resolved) => resolve18(resolved), (error52) => reject(error52));
8593
+ return new Promise((resolve19, reject) => {
8594
+ value.then((resolved) => resolve19(resolved), (error52) => reject(error52));
8595
8595
  });
8596
8596
  } else {
8597
8597
  return Promise.resolve(value);
@@ -8873,13 +8873,13 @@ var require_files = __commonJS({
8873
8873
  Object.defineProperty(exports, "__esModule", { value: true });
8874
8874
  exports.FileSystem = void 0;
8875
8875
  exports.uriToFilePath = uriToFilePath;
8876
- exports.resolve = resolve18;
8876
+ exports.resolve = resolve19;
8877
8877
  exports.resolveGlobalNodePath = resolveGlobalNodePath;
8878
8878
  exports.resolveGlobalYarnPath = resolveGlobalYarnPath;
8879
8879
  exports.resolveModulePath = resolveModulePath;
8880
8880
  var url2 = __importStar(__require("url"));
8881
- var path20 = __importStar(__require("path"));
8882
- var fs15 = __importStar(__require("fs"));
8881
+ var path21 = __importStar(__require("path"));
8882
+ var fs16 = __importStar(__require("fs"));
8883
8883
  var child_process_1 = __require("child_process");
8884
8884
  function uriToFilePath(uri) {
8885
8885
  const parsed = url2.parse(uri);
@@ -8897,12 +8897,12 @@ var require_files = __commonJS({
8897
8897
  segments.shift();
8898
8898
  }
8899
8899
  }
8900
- return path20.normalize(segments.join("/"));
8900
+ return path21.normalize(segments.join("/"));
8901
8901
  }
8902
8902
  function isWindows() {
8903
8903
  return process.platform === "win32";
8904
8904
  }
8905
- function resolve18(moduleName, nodePath, cwd, tracer) {
8905
+ function resolve19(moduleName, nodePath, cwd, tracer) {
8906
8906
  const nodePathKey = "NODE_PATH";
8907
8907
  const app = [
8908
8908
  "var p = process;",
@@ -8921,13 +8921,13 @@ var require_files = __commonJS({
8921
8921
  "}",
8922
8922
  "});"
8923
8923
  ].join("");
8924
- return new Promise((resolve19, reject) => {
8924
+ return new Promise((resolve20, reject) => {
8925
8925
  const env2 = process.env;
8926
8926
  const newEnv = /* @__PURE__ */ Object.create(null);
8927
8927
  Object.keys(env2).forEach((key) => newEnv[key] = env2[key]);
8928
- if (nodePath && fs15.existsSync(nodePath)) {
8928
+ if (nodePath && fs16.existsSync(nodePath)) {
8929
8929
  if (newEnv[nodePathKey]) {
8930
- newEnv[nodePathKey] = nodePath + path20.delimiter + newEnv[nodePathKey];
8930
+ newEnv[nodePathKey] = nodePath + path21.delimiter + newEnv[nodePathKey];
8931
8931
  } else {
8932
8932
  newEnv[nodePathKey] = nodePath;
8933
8933
  }
@@ -8953,7 +8953,7 @@ var require_files = __commonJS({
8953
8953
  if (message2.c === "r") {
8954
8954
  cp.send({ c: "e" });
8955
8955
  if (message2.s) {
8956
- resolve19(message2.r);
8956
+ resolve20(message2.r);
8957
8957
  } else {
8958
8958
  reject(new Error(`Failed to resolve module: ${moduleName}`));
8959
8959
  }
@@ -8999,9 +8999,9 @@ var require_files = __commonJS({
8999
8999
  }
9000
9000
  if (prefix.length > 0) {
9001
9001
  if (isWindows()) {
9002
- return path20.join(prefix, "node_modules");
9002
+ return path21.join(prefix, "node_modules");
9003
9003
  } else {
9004
- return path20.join(prefix, "lib", "node_modules");
9004
+ return path21.join(prefix, "lib", "node_modules");
9005
9005
  }
9006
9006
  }
9007
9007
  return void 0;
@@ -9040,7 +9040,7 @@ var require_files = __commonJS({
9040
9040
  try {
9041
9041
  const yarn = JSON.parse(line);
9042
9042
  if (yarn.type === "log") {
9043
- return path20.join(yarn.data, "node_modules");
9043
+ return path21.join(yarn.data, "node_modules");
9044
9044
  }
9045
9045
  } catch (e) {
9046
9046
  }
@@ -9062,36 +9062,36 @@ var require_files = __commonJS({
9062
9062
  if (process.platform === "win32") {
9063
9063
  _isCaseSensitive = false;
9064
9064
  } else {
9065
- _isCaseSensitive = !fs15.existsSync(__filename.toUpperCase()) || !fs15.existsSync(__filename.toLowerCase());
9065
+ _isCaseSensitive = !fs16.existsSync(__filename.toUpperCase()) || !fs16.existsSync(__filename.toLowerCase());
9066
9066
  }
9067
9067
  return _isCaseSensitive;
9068
9068
  }
9069
9069
  FileSystem2.isCaseSensitive = isCaseSensitive;
9070
9070
  function isParent(parent, child) {
9071
9071
  if (isCaseSensitive()) {
9072
- return path20.normalize(child).indexOf(path20.normalize(parent)) === 0;
9072
+ return path21.normalize(child).indexOf(path21.normalize(parent)) === 0;
9073
9073
  } else {
9074
- return path20.normalize(child).toLowerCase().indexOf(path20.normalize(parent).toLowerCase()) === 0;
9074
+ return path21.normalize(child).toLowerCase().indexOf(path21.normalize(parent).toLowerCase()) === 0;
9075
9075
  }
9076
9076
  }
9077
9077
  FileSystem2.isParent = isParent;
9078
9078
  })(FileSystem || (exports.FileSystem = FileSystem = {}));
9079
9079
  function resolveModulePath(workspaceRoot, moduleName, nodePath, tracer) {
9080
9080
  if (nodePath) {
9081
- if (!path20.isAbsolute(nodePath)) {
9082
- nodePath = path20.join(workspaceRoot, nodePath);
9081
+ if (!path21.isAbsolute(nodePath)) {
9082
+ nodePath = path21.join(workspaceRoot, nodePath);
9083
9083
  }
9084
- return resolve18(moduleName, nodePath, nodePath, tracer).then((value) => {
9084
+ return resolve19(moduleName, nodePath, nodePath, tracer).then((value) => {
9085
9085
  if (FileSystem.isParent(nodePath, value)) {
9086
9086
  return value;
9087
9087
  } else {
9088
9088
  return Promise.reject(new Error(`Failed to load ${moduleName} from node path location.`));
9089
9089
  }
9090
9090
  }).then(void 0, (_error) => {
9091
- return resolve18(moduleName, resolveGlobalNodePath(tracer), workspaceRoot, tracer);
9091
+ return resolve19(moduleName, resolveGlobalNodePath(tracer), workspaceRoot, tracer);
9092
9092
  });
9093
9093
  } else {
9094
- return resolve18(moduleName, resolveGlobalNodePath(tracer), workspaceRoot, tracer);
9094
+ return resolve19(moduleName, resolveGlobalNodePath(tracer), workspaceRoot, tracer);
9095
9095
  }
9096
9096
  }
9097
9097
  }
@@ -9174,10 +9174,10 @@ var require_ril = __commonJS({
9174
9174
  return api_1.Disposable.create(() => this.stream.off("end", listener));
9175
9175
  }
9176
9176
  write(data, encoding) {
9177
- return new Promise((resolve18, reject) => {
9177
+ return new Promise((resolve19, reject) => {
9178
9178
  const callback = (error52) => {
9179
9179
  if (error52 === void 0 || error52 === null) {
9180
- resolve18();
9180
+ resolve19();
9181
9181
  } else {
9182
9182
  reject(error52);
9183
9183
  }
@@ -9314,9 +9314,9 @@ var require_main = __commonJS({
9314
9314
  exports.createMessageConnection = createMessageConnection;
9315
9315
  var ril_1 = __importDefault(require_ril());
9316
9316
  ril_1.default.install();
9317
- var path20 = __importStar(__require("path"));
9317
+ var path21 = __importStar(__require("path"));
9318
9318
  var os7 = __importStar(__require("os"));
9319
- var fs15 = __importStar(__require("fs"));
9319
+ var fs16 = __importStar(__require("fs"));
9320
9320
  var crypto_1 = __require("crypto");
9321
9321
  var net_1 = __require("net");
9322
9322
  var api_1 = require_api();
@@ -9457,7 +9457,7 @@ var require_main = __commonJS({
9457
9457
  }
9458
9458
  let randomLength = 32;
9459
9459
  const fixedLength = "/lsp-.sock".length;
9460
- const tmpDir = fs15.realpathSync(XDG_RUNTIME_DIR ?? os7.tmpdir());
9460
+ const tmpDir = fs16.realpathSync(XDG_RUNTIME_DIR ?? os7.tmpdir());
9461
9461
  const limit = safeIpcPathLengths.get(process.platform);
9462
9462
  if (limit !== void 0) {
9463
9463
  randomLength = Math.min(limit - tmpDir.length - fixedLength, randomLength);
@@ -9466,14 +9466,14 @@ var require_main = __commonJS({
9466
9466
  throw new Error(`Unable to generate a random pipe name with ${randomLength} characters.`);
9467
9467
  }
9468
9468
  const randomSuffix = (0, crypto_1.randomBytes)(Math.floor(randomLength / 2)).toString("hex");
9469
- return path20.join(tmpDir, `lsp-${randomSuffix}.sock`);
9469
+ return path21.join(tmpDir, `lsp-${randomSuffix}.sock`);
9470
9470
  }
9471
9471
  function createClientPipeTransport(pipeName, encoding = "utf-8") {
9472
9472
  let connectResolve;
9473
- const connected = new Promise((resolve18, _reject) => {
9474
- connectResolve = resolve18;
9473
+ const connected = new Promise((resolve19, _reject) => {
9474
+ connectResolve = resolve19;
9475
9475
  });
9476
- return new Promise((resolve18, reject) => {
9476
+ return new Promise((resolve19, reject) => {
9477
9477
  const server2 = (0, net_1.createServer)((socket) => {
9478
9478
  server2.close();
9479
9479
  connectResolve([
@@ -9484,7 +9484,7 @@ var require_main = __commonJS({
9484
9484
  server2.on("error", reject);
9485
9485
  server2.listen(pipeName, () => {
9486
9486
  server2.removeListener("error", reject);
9487
- resolve18({
9487
+ resolve19({
9488
9488
  onConnected: () => {
9489
9489
  return connected;
9490
9490
  }
@@ -9501,10 +9501,10 @@ var require_main = __commonJS({
9501
9501
  }
9502
9502
  function createClientSocketTransport(port, encoding = "utf-8") {
9503
9503
  let connectResolve;
9504
- const connected = new Promise((resolve18, _reject) => {
9505
- connectResolve = resolve18;
9504
+ const connected = new Promise((resolve19, _reject) => {
9505
+ connectResolve = resolve19;
9506
9506
  });
9507
- return new Promise((resolve18, reject) => {
9507
+ return new Promise((resolve19, reject) => {
9508
9508
  const server2 = (0, net_1.createServer)((socket) => {
9509
9509
  server2.close();
9510
9510
  connectResolve([
@@ -9515,7 +9515,7 @@ var require_main = __commonJS({
9515
9515
  server2.on("error", reject);
9516
9516
  server2.listen(port, "127.0.0.1", () => {
9517
9517
  server2.removeListener("error", reject);
9518
- resolve18({
9518
+ resolve19({
9519
9519
  onConnected: () => {
9520
9520
  return connected;
9521
9521
  }
@@ -13770,8 +13770,8 @@ function extractPathReferences(lines, sections) {
13770
13770
  if (inCodeBlock && codeBlockLang === "" && /^\s/.test(line) && /^[\w@.-]+\/$/.test(cleanValue)) {
13771
13771
  continue;
13772
13772
  }
13773
- const lowerProse = /^([a-z][\w-]*)\/[a-z][\w-]*$/.exec(cleanValue);
13774
- if (lowerProse && !PATH_FIRST_SEGMENTS.has(lowerProse[1])) {
13773
+ const twoSegmentProse = /^([A-Za-z][\w-]*)\/[A-Za-z][\w-]*$/.exec(cleanValue);
13774
+ if (twoSegmentProse && !PATH_FIRST_SEGMENTS.has(twoSegmentProse[1].toLowerCase())) {
13775
13775
  continue;
13776
13776
  }
13777
13777
  if (/^[a-z][\w-]*(?:\/[a-z][\w-]*){2,}$/.test(cleanValue)) continue;
@@ -14733,10 +14733,10 @@ var require_src2 = __commonJS({
14733
14733
  var fs_1 = __require("fs");
14734
14734
  var debug_1 = __importDefault(require_src());
14735
14735
  var log = debug_1.default("@kwsites/file-exists");
14736
- function check2(path20, isFile, isDirectory2) {
14737
- log(`checking %s`, path20);
14736
+ function check2(path21, isFile, isDirectory2) {
14737
+ log(`checking %s`, path21);
14738
14738
  try {
14739
- const stat3 = fs_1.statSync(path20);
14739
+ const stat3 = fs_1.statSync(path21);
14740
14740
  if (stat3.isFile() && isFile) {
14741
14741
  log(`[OK] path represents a file`);
14742
14742
  return true;
@@ -14756,8 +14756,8 @@ var require_src2 = __commonJS({
14756
14756
  throw e;
14757
14757
  }
14758
14758
  }
14759
- function exists2(path20, type = exports.READABLE) {
14760
- return check2(path20, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
14759
+ function exists2(path21, type = exports.READABLE) {
14760
+ return check2(path21, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
14761
14761
  }
14762
14762
  exports.exists = exists2;
14763
14763
  exports.FILE = 1;
@@ -15355,8 +15355,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
15355
15355
  function forEachLineWithContent(input, callback) {
15356
15356
  return toLinesWithContent(input, true).map((line) => callback(line));
15357
15357
  }
15358
- function folderExists(path20) {
15359
- return (0, import_file_exists.exists)(path20, import_file_exists.FOLDER);
15358
+ function folderExists(path21) {
15359
+ return (0, import_file_exists.exists)(path21, import_file_exists.FOLDER);
15360
15360
  }
15361
15361
  function append(target, item) {
15362
15362
  if (Array.isArray(target)) {
@@ -15547,8 +15547,8 @@ function checkIsRepoRootTask() {
15547
15547
  commands,
15548
15548
  format: "utf-8",
15549
15549
  onError,
15550
- parser(path20) {
15551
- return /^\.(git)?$/.test(path20.trim());
15550
+ parser(path21) {
15551
+ return /^\.(git)?$/.test(path21.trim());
15552
15552
  }
15553
15553
  };
15554
15554
  }
@@ -15804,11 +15804,11 @@ function parseGrep(grep) {
15804
15804
  const paths = /* @__PURE__ */ new Set();
15805
15805
  const results = {};
15806
15806
  forEachLineWithContent(grep, (input) => {
15807
- const [path20, line, preview] = input.split(NULL);
15808
- paths.add(path20);
15809
- (results[path20] = results[path20] || []).push({
15807
+ const [path21, line, preview] = input.split(NULL);
15808
+ paths.add(path21);
15809
+ (results[path21] = results[path21] || []).push({
15810
15810
  line: asNumber(line),
15811
- path: path20,
15811
+ path: path21,
15812
15812
  preview
15813
15813
  });
15814
15814
  });
@@ -16105,14 +16105,14 @@ function hashObjectTask(filePath, write) {
16105
16105
  }
16106
16106
  return straightThroughStringTask(commands, true);
16107
16107
  }
16108
- function parseInit(bare, path20, text) {
16108
+ function parseInit(bare, path21, text) {
16109
16109
  const response = String(text).trim();
16110
16110
  let result;
16111
16111
  if (result = initResponseRegex.exec(response)) {
16112
- return new InitSummary(bare, path20, false, result[1]);
16112
+ return new InitSummary(bare, path21, false, result[1]);
16113
16113
  }
16114
16114
  if (result = reInitResponseRegex.exec(response)) {
16115
- return new InitSummary(bare, path20, true, result[1]);
16115
+ return new InitSummary(bare, path21, true, result[1]);
16116
16116
  }
16117
16117
  let gitDir = "";
16118
16118
  const tokens = response.split(" ");
@@ -16123,12 +16123,12 @@ function parseInit(bare, path20, text) {
16123
16123
  break;
16124
16124
  }
16125
16125
  }
16126
- return new InitSummary(bare, path20, /^re/i.test(response), gitDir);
16126
+ return new InitSummary(bare, path21, /^re/i.test(response), gitDir);
16127
16127
  }
16128
16128
  function hasBareCommand(command) {
16129
16129
  return command.includes(bareCommand);
16130
16130
  }
16131
- function initTask(bare = false, path20, customArgs) {
16131
+ function initTask(bare = false, path21, customArgs) {
16132
16132
  const commands = ["init", ...customArgs];
16133
16133
  if (bare && !hasBareCommand(commands)) {
16134
16134
  commands.splice(1, 0, bareCommand);
@@ -16137,7 +16137,7 @@ function initTask(bare = false, path20, customArgs) {
16137
16137
  commands,
16138
16138
  format: "utf-8",
16139
16139
  parser(text) {
16140
- return parseInit(commands.includes("--bare"), path20, text);
16140
+ return parseInit(commands.includes("--bare"), path21, text);
16141
16141
  }
16142
16142
  };
16143
16143
  }
@@ -16416,14 +16416,14 @@ function splitLine(result, lineStr) {
16416
16416
  default:
16417
16417
  return;
16418
16418
  }
16419
- function data(index, workingDir, path20) {
16419
+ function data(index, workingDir, path21) {
16420
16420
  const raw = `${index}${workingDir}`;
16421
16421
  const handler = parsers6.get(raw);
16422
16422
  if (handler) {
16423
- handler(result, path20);
16423
+ handler(result, path21);
16424
16424
  }
16425
16425
  if (raw !== "##" && raw !== "!!") {
16426
- result.files.push(new FileStatusSummary(path20, index, workingDir));
16426
+ result.files.push(new FileStatusSummary(path21, index, workingDir));
16427
16427
  }
16428
16428
  }
16429
16429
  }
@@ -16610,8 +16610,8 @@ function deleteBranchTask(branch, forceDelete = false) {
16610
16610
  return task;
16611
16611
  }
16612
16612
  function toPath(input) {
16613
- const path20 = input.trim().replace(/^["']|["']$/g, "");
16614
- return path20 && normalize2(path20);
16613
+ const path21 = input.trim().replace(/^["']|["']$/g, "");
16614
+ return path21 && normalize2(path21);
16615
16615
  }
16616
16616
  function checkIgnoreTask(paths) {
16617
16617
  return {
@@ -16750,8 +16750,8 @@ function stashListTask(opt = {}, customArgs) {
16750
16750
  parser: parser4
16751
16751
  };
16752
16752
  }
16753
- function addSubModuleTask(repo, path20) {
16754
- return subModuleTask(["add", repo, path20]);
16753
+ function addSubModuleTask(repo, path21) {
16754
+ return subModuleTask(["add", repo, path21]);
16755
16755
  }
16756
16756
  function initSubModuleTask(customArgs) {
16757
16757
  return subModuleTask(["init", ...customArgs]);
@@ -18014,9 +18014,9 @@ var init_esm = __esm({
18014
18014
  "src/lib/responses/InitSummary.ts"() {
18015
18015
  "use strict";
18016
18016
  InitSummary = class {
18017
- constructor(bare, path20, existing, gitDir) {
18017
+ constructor(bare, path21, existing, gitDir) {
18018
18018
  this.bare = bare;
18019
- this.path = path20;
18019
+ this.path = path21;
18020
18020
  this.existing = existing;
18021
18021
  this.gitDir = gitDir;
18022
18022
  }
@@ -18549,12 +18549,12 @@ var init_esm = __esm({
18549
18549
  "use strict";
18550
18550
  fromPathRegex = /^(.+)\0(.+)$/;
18551
18551
  FileStatusSummary = class {
18552
- constructor(path20, index, working_dir) {
18553
- this.path = path20;
18552
+ constructor(path21, index, working_dir) {
18553
+ this.path = path21;
18554
18554
  this.index = index;
18555
18555
  this.working_dir = working_dir;
18556
18556
  if (index === "R" || working_dir === "R") {
18557
- const detail = fromPathRegex.exec(path20) || [null, path20, path20];
18557
+ const detail = fromPathRegex.exec(path21) || [null, path21, path21];
18558
18558
  this.from = detail[2] || "";
18559
18559
  this.path = detail[1] || "";
18560
18560
  }
@@ -18816,9 +18816,9 @@ var init_esm = __esm({
18816
18816
  next
18817
18817
  );
18818
18818
  }
18819
- hashObject(path20, write) {
18819
+ hashObject(path21, write) {
18820
18820
  return this._runTask(
18821
- hashObjectTask(path20, write === true),
18821
+ hashObjectTask(path21, write === true),
18822
18822
  trailingFunctionArgument(arguments)
18823
18823
  );
18824
18824
  }
@@ -19455,8 +19455,8 @@ var init_esm = __esm({
19455
19455
  }
19456
19456
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
19457
19457
  };
19458
- Git2.prototype.submoduleAdd = function(repo, path20, then) {
19459
- return this._runTask(addSubModuleTask2(repo, path20), trailingFunctionArgument2(arguments));
19458
+ Git2.prototype.submoduleAdd = function(repo, path21, then) {
19459
+ return this._runTask(addSubModuleTask2(repo, path21), trailingFunctionArgument2(arguments));
19460
19460
  };
19461
19461
  Git2.prototype.submoduleUpdate = function(args2, then) {
19462
19462
  return this._runTask(
@@ -19772,7 +19772,7 @@ async function getCommitsSinceBatch(projectRoot, paths, since) {
19772
19772
  }
19773
19773
  try {
19774
19774
  const git = getGit(projectRoot);
19775
- const SENTINEL = "___CTXLINT_COMMIT___";
19775
+ const SENTINEL2 = "___CTXLINT_COMMIT___";
19776
19776
  const pathspecs = [];
19777
19777
  for (const p2 of paths) {
19778
19778
  const spec = toGitPathspec(p2, projectRoot);
@@ -19791,7 +19791,7 @@ async function getCommitsSinceBatch(projectRoot, paths, since) {
19791
19791
  // %n emits a newline; prefixing with a valid format placeholder is
19792
19792
  // what keeps git from rejecting the sentinel as an invalid --pretty
19793
19793
  // format ("fatal: invalid --pretty format: ___CTXLINT_COMMIT___").
19794
- `--format=%n${SENTINEL}`
19794
+ `--format=%n${SENTINEL2}`
19795
19795
  ];
19796
19796
  if (pathspecs.length > 0) {
19797
19797
  logArgs.push("--", ...pathspecs);
@@ -19808,7 +19808,7 @@ async function getCommitsSinceBatch(projectRoot, paths, since) {
19808
19808
  seenThisCommit.clear();
19809
19809
  };
19810
19810
  for (const line of lines) {
19811
- if (line === SENTINEL) {
19811
+ if (line === SENTINEL2) {
19812
19812
  if (inCommit) flush();
19813
19813
  inCommit = true;
19814
19814
  continue;
@@ -19966,8 +19966,8 @@ async function findRenames(projectRoot, filePath) {
19966
19966
  async function findRenamesBatch(projectRoot, relPaths) {
19967
19967
  const result = /* @__PURE__ */ new Map();
19968
19968
  if (relPaths.length === 0) return result;
19969
- let cache = renameCache.get(projectRoot);
19970
- if (!cache) {
19969
+ let cache2 = renameCache.get(projectRoot);
19970
+ if (!cache2) {
19971
19971
  try {
19972
19972
  const git = getGit(projectRoot);
19973
19973
  const rawOutput = await git.raw([
@@ -19985,14 +19985,14 @@ async function findRenamesBatch(projectRoot, relPaths) {
19985
19985
  } catch {
19986
19986
  }
19987
19987
  const { allRenames: allRenames2, basenameBuckets: basenameBuckets2 } = parseRenameLogAll(rawOutput);
19988
- cache = { allRenames: allRenames2, basenameBuckets: basenameBuckets2, prefix: prefix2 };
19989
- renameCache.set(projectRoot, cache);
19988
+ cache2 = { allRenames: allRenames2, basenameBuckets: basenameBuckets2, prefix: prefix2 };
19989
+ renameCache.set(projectRoot, cache2);
19990
19990
  } catch {
19991
19991
  for (const p2 of relPaths) result.set(p2, null);
19992
19992
  return result;
19993
19993
  }
19994
19994
  }
19995
- const { allRenames, basenameBuckets, prefix } = cache;
19995
+ const { allRenames, basenameBuckets, prefix } = cache2;
19996
19996
  for (const relPath of relPaths) {
19997
19997
  const rel = relPath.replace(/\\/g, "/");
19998
19998
  const target = prefix + rel;
@@ -21258,12 +21258,12 @@ function parseTree(text, errors = [], options = ParseOptions.DEFAULT) {
21258
21258
  }
21259
21259
  return result;
21260
21260
  }
21261
- function findNodeAtLocation(root, path20) {
21261
+ function findNodeAtLocation(root, path21) {
21262
21262
  if (!root) {
21263
21263
  return void 0;
21264
21264
  }
21265
21265
  let node = root;
21266
- for (let segment of path20) {
21266
+ for (let segment of path21) {
21267
21267
  if (typeof segment === "string") {
21268
21268
  if (node.type !== "object" || !Array.isArray(node.children)) {
21269
21269
  return void 0;
@@ -21760,9 +21760,495 @@ var init_main3 = __esm({
21760
21760
  }
21761
21761
  });
21762
21762
 
21763
- // src/core/checks/commands.ts
21763
+ // src/core/checks/exit-status.ts
21764
+ function splitSegments(cmd) {
21765
+ const out = [];
21766
+ let buf = "";
21767
+ let op = "";
21768
+ let quote = null;
21769
+ for (let i2 = 0; i2 < cmd.length; i2++) {
21770
+ const ch = cmd[i2];
21771
+ if (quote) {
21772
+ buf += ch;
21773
+ if (ch === "\\" && quote !== "'") {
21774
+ if (i2 + 1 < cmd.length) buf += cmd[++i2];
21775
+ continue;
21776
+ }
21777
+ if (ch === quote) quote = null;
21778
+ continue;
21779
+ }
21780
+ if (ch === '"' || ch === "'" || ch === "`") {
21781
+ quote = ch;
21782
+ buf += ch;
21783
+ continue;
21784
+ }
21785
+ if (ch === "\\" && i2 + 1 < cmd.length) {
21786
+ buf += ch + cmd[++i2];
21787
+ continue;
21788
+ }
21789
+ if (ch === "&" && cmd[i2 + 1] === "&") {
21790
+ out.push({ op, text: buf.trim() });
21791
+ op = "&&";
21792
+ buf = "";
21793
+ i2++;
21794
+ continue;
21795
+ }
21796
+ if (ch === "|" && cmd[i2 + 1] === "|") {
21797
+ out.push({ op, text: buf.trim() });
21798
+ op = "||";
21799
+ buf = "";
21800
+ i2++;
21801
+ continue;
21802
+ }
21803
+ if (ch === ";") {
21804
+ out.push({ op, text: buf.trim() });
21805
+ op = ";";
21806
+ buf = "";
21807
+ continue;
21808
+ }
21809
+ buf += ch;
21810
+ }
21811
+ out.push({ op, text: buf.trim() });
21812
+ return out.filter((s) => s.text.length > 0);
21813
+ }
21814
+ function splitPipes(segment) {
21815
+ const out = [];
21816
+ let buf = "";
21817
+ let quote = null;
21818
+ for (let i2 = 0; i2 < segment.length; i2++) {
21819
+ const ch = segment[i2];
21820
+ if (quote) {
21821
+ buf += ch;
21822
+ if (ch === "\\" && quote !== "'") {
21823
+ if (i2 + 1 < segment.length) buf += segment[++i2];
21824
+ continue;
21825
+ }
21826
+ if (ch === quote) quote = null;
21827
+ continue;
21828
+ }
21829
+ if (ch === '"' || ch === "'" || ch === "`") {
21830
+ quote = ch;
21831
+ buf += ch;
21832
+ continue;
21833
+ }
21834
+ if (ch === "\\" && i2 + 1 < segment.length) {
21835
+ buf += ch + segment[++i2];
21836
+ continue;
21837
+ }
21838
+ if (ch === "|") {
21839
+ out.push(buf.trim());
21840
+ buf = "";
21841
+ continue;
21842
+ }
21843
+ buf += ch;
21844
+ }
21845
+ out.push(buf.trim());
21846
+ return out.map((s) => s.trim());
21847
+ }
21848
+ function unwrap(part) {
21849
+ let s = part.trim();
21850
+ s = s.replace(/^(?:[A-Za-z_][A-Za-z0-9_]*=(?:"[^"]*"|'[^']*'|\S*)\s+)+/, "");
21851
+ const execMatch = s.match(EXEC_WRAPPER_PATTERN);
21852
+ if (execMatch) s = s.slice(execMatch[0].length).trim();
21853
+ while (WRAPPER_PATTERN.test(s)) {
21854
+ const tokens = s.split(/\s+/).slice(1);
21855
+ let i2 = 0;
21856
+ while (i2 < tokens.length && tokens[i2].startsWith("-")) {
21857
+ if (tokens[i2] === "-p" || tokens[i2] === "--package") i2++;
21858
+ i2++;
21859
+ }
21860
+ const next = tokens.slice(i2).join(" ");
21861
+ if (!next || next === s) break;
21862
+ s = next;
21863
+ }
21864
+ return s.trim();
21865
+ }
21866
+ function verifierName(part, scripts) {
21867
+ const s = unwrap(part);
21868
+ if (!s) return null;
21869
+ for (const re2 of VERIFIER_PAIRS) {
21870
+ const m = s.match(re2);
21871
+ if (m) return m[0].replace(/\s+/g, " ");
21872
+ }
21873
+ const direct = s.match(VERIFIER_PATTERN);
21874
+ if (direct) return direct[1];
21875
+ const scriptName = (s.match(SCRIPT_RUNNER) ?? s.match(SCRIPT_SHORTHAND))?.[1];
21876
+ if (scriptName && scripts) {
21877
+ const body = scripts[scriptName];
21878
+ if (typeof body === "string") {
21879
+ const first2 = splitPipes(splitSegments(body)[0]?.text ?? "")[0] ?? "";
21880
+ const inner = unwrap(first2);
21881
+ for (const re2 of VERIFIER_PAIRS) {
21882
+ const m = inner.match(re2);
21883
+ if (m) return m[0].replace(/\s+/g, " ");
21884
+ }
21885
+ const d = inner.match(VERIFIER_PATTERN);
21886
+ if (d) return d[1];
21887
+ }
21888
+ }
21889
+ return null;
21890
+ }
21891
+ function filterName(part) {
21892
+ const m = unwrap(part).match(FILTER_PATTERN);
21893
+ return m ? m[1] : null;
21894
+ }
21895
+ function isSuccessClaim(segment) {
21896
+ if (!/^(echo|printf)\b/.test(unwrap(segment))) return false;
21897
+ return SUCCESS_WORD.test(segment);
21898
+ }
21899
+ function isStatusEcho(segment) {
21900
+ if (!/^(echo|printf)\b/.test(unwrap(segment))) return false;
21901
+ if (/PIPESTATUS/.test(segment)) return false;
21902
+ return /\$\?/.test(segment);
21903
+ }
21904
+ function analyzeMaskedExitStatus(cmd, scripts) {
21905
+ const segments = splitSegments(cmd);
21906
+ if (segments.length < 2) return null;
21907
+ for (let i2 = 0; i2 < segments.length - 1; i2++) {
21908
+ if (segments.slice(0, i2 + 1).some((s) => PIPEFAIL.test(s.text))) return null;
21909
+ const parts = splitPipes(segments[i2].text);
21910
+ if (parts.length < 2) continue;
21911
+ const verifier = verifierName(parts[0], scripts);
21912
+ if (!verifier) continue;
21913
+ const filter = filterName(parts[parts.length - 1]);
21914
+ if (!filter) continue;
21915
+ const next = segments[i2 + 1];
21916
+ if (next.op !== "&&" && next.op !== ";") continue;
21917
+ if (next.op === "&&" && isSuccessClaim(next.text)) {
21918
+ return { verifier, filter, claim: next.text, kind: "success-claim" };
21919
+ }
21920
+ if (isStatusEcho(next.text)) {
21921
+ return { verifier, filter, claim: next.text, kind: "status-echo" };
21922
+ }
21923
+ }
21924
+ return null;
21925
+ }
21926
+ function pipefailInScope(content, line) {
21927
+ const lines = content.split("\n");
21928
+ const idx = line - 1;
21929
+ if (idx < 0 || idx >= lines.length) return false;
21930
+ let fenceStart = -1;
21931
+ let open = false;
21932
+ for (let i2 = 0; i2 < idx; i2++) {
21933
+ if (/^\s*```/.test(lines[i2])) {
21934
+ open = !open;
21935
+ fenceStart = open ? i2 + 1 : -1;
21936
+ }
21937
+ }
21938
+ if (!open || fenceStart < 0) return false;
21939
+ for (let i2 = fenceStart; i2 < idx; i2++) {
21940
+ if (PIPEFAIL.test(lines[i2])) return true;
21941
+ }
21942
+ return false;
21943
+ }
21944
+ var VERIFIER_PATTERN, VERIFIER_PAIRS, FILTER_PATTERN, SCRIPT_RUNNER, SCRIPT_SHORTHAND, WRAPPER_PATTERN, EXEC_WRAPPER_PATTERN, SUCCESS_WORD, PIPEFAIL;
21945
+ var init_exit_status = __esm({
21946
+ "src/core/checks/exit-status.ts"() {
21947
+ "use strict";
21948
+ init_define_WEB_FIRST_SEGMENTS();
21949
+ VERIFIER_PATTERN = /^(tsc|eslint|biome|vitest|jest|pytest|mypy|ruff|oxlint)\b/;
21950
+ VERIFIER_PAIRS = [/^cargo\s+(test|clippy|check)\b/, /^go\s+(test|vet|build)\b/];
21951
+ FILTER_PATTERN = /^(head|tail|grep|egrep|fgrep|sed|awk|less|more|cat|tee|wc)\b/;
21952
+ SCRIPT_RUNNER = /^(?:npm\s+run|(?:pnpm|yarn|bun)(?:\s+run)?)\s+(\S+)/;
21953
+ SCRIPT_SHORTHAND = /^(?:npm|pnpm|yarn|bun)\s+(test|start|build|dev|lint|format|check|typecheck|type-check|clean|serve|preview|e2e)\b/;
21954
+ WRAPPER_PATTERN = /^(npx|bunx|sudo|time|command|nice)\b/;
21955
+ EXEC_WRAPPER_PATTERN = /^(?:pnpm|npm|yarn|bun)\s+(?:exec|dlx)\b/;
21956
+ SUCCESS_WORD = /\b(ok|okay|clean|pass|passed|passes|passing|green|success|successful|done|all\s+good|no\s+errors|looks\s+good)\b/i;
21957
+ PIPEFAIL = /\bset\s+[-\w\s]*\bpipefail\b/;
21958
+ }
21959
+ });
21960
+
21961
+ // src/core/checks/cli-subcommands.ts
21764
21962
  import * as fs4 from "node:fs";
21765
21963
  import * as path5 from "node:path";
21964
+ function ownedBins(pkgJson) {
21965
+ if (!pkgJson || !pkgJson.bin) return [];
21966
+ const out = [];
21967
+ if (typeof pkgJson.bin === "string") {
21968
+ const raw = pkgJson.name ?? "";
21969
+ const name = raw.includes("/") ? raw.slice(raw.lastIndexOf("/") + 1) : raw;
21970
+ if (name) out.push({ name, entry: pkgJson.bin });
21971
+ return out;
21972
+ }
21973
+ for (const [name, entry] of Object.entries(pkgJson.bin)) {
21974
+ if (typeof entry === "string" && name) out.push({ name, entry });
21975
+ }
21976
+ return out;
21977
+ }
21978
+ function commandNameOf(token) {
21979
+ let t2 = token.trim().replace(/^["']|["']$/g, "");
21980
+ if (!t2) return null;
21981
+ const cut = Math.max(t2.lastIndexOf("/"), t2.lastIndexOf("\\"));
21982
+ if (cut >= 0) t2 = t2.slice(cut + 1);
21983
+ t2 = t2.replace(BIN_EXT, "");
21984
+ return t2 || null;
21985
+ }
21986
+ function resolveEntry(projectRoot, entry) {
21987
+ const base = path5.resolve(projectRoot, entry);
21988
+ const candidates = [base, `${base}.js`, `${base}.mjs`, `${base}.cjs`, `${base}.ts`];
21989
+ for (const c3 of candidates) {
21990
+ try {
21991
+ if (fs4.statSync(c3).isFile()) return c3;
21992
+ } catch {
21993
+ }
21994
+ }
21995
+ return null;
21996
+ }
21997
+ function readEntry(abs) {
21998
+ let src;
21999
+ try {
22000
+ const stat3 = fs4.statSync(abs);
22001
+ if (stat3.size > MAX_ENTRY_BYTES) return null;
22002
+ src = stripBom(fs4.readFileSync(abs, "utf-8"));
22003
+ } catch {
22004
+ return null;
22005
+ }
22006
+ for (const line of src.split("\n")) {
22007
+ if (line.length > MAX_ENTRY_LINE) return null;
22008
+ }
22009
+ return src;
22010
+ }
22011
+ function shimTarget(src) {
22012
+ const meaningful = src.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#!") && !l.startsWith("//") && !l.startsWith("*"));
22013
+ if (meaningful.length > 12) return null;
22014
+ const specs = /* @__PURE__ */ new Set();
22015
+ for (const m of src.matchAll(/(?:^|\s)(?:import|require)\s*\(?\s*['"](\.[^'"]+)['"]/g)) {
22016
+ specs.add(m[1]);
22017
+ }
22018
+ for (const m of src.matchAll(/^\s*import\s+[^'"]*from\s*['"](\.[^'"]+)['"]/gm)) {
22019
+ specs.add(m[1]);
22020
+ }
22021
+ return specs.size === 1 ? [...specs][0] : null;
22022
+ }
22023
+ function stripComments2(src) {
22024
+ let code = "";
22025
+ let literals = "";
22026
+ let i2 = 0;
22027
+ const push = (ch, inString) => {
22028
+ literals += ch;
22029
+ code += inString && ch !== "\n" ? " " : ch;
22030
+ };
22031
+ while (i2 < src.length) {
22032
+ const ch = src[i2];
22033
+ const next = src[i2 + 1];
22034
+ if (ch === "/" && next === "/") {
22035
+ while (i2 < src.length && src[i2] !== "\n") i2++;
22036
+ continue;
22037
+ }
22038
+ if (ch === "/" && next === "*") {
22039
+ i2 += 2;
22040
+ while (i2 < src.length && !(src[i2] === "*" && src[i2 + 1] === "/")) {
22041
+ if (src[i2] === "\n") push("\n", false);
22042
+ i2++;
22043
+ }
22044
+ i2 += 2;
22045
+ continue;
22046
+ }
22047
+ if (ch === '"' || ch === "'" || ch === "`") {
22048
+ const quote = ch;
22049
+ push(ch, false);
22050
+ i2++;
22051
+ while (i2 < src.length) {
22052
+ if (src[i2] === "\\") {
22053
+ push(src[i2], true);
22054
+ if (i2 + 1 < src.length) push(src[i2 + 1], true);
22055
+ i2 += 2;
22056
+ continue;
22057
+ }
22058
+ if (src[i2] === quote) break;
22059
+ push(src[i2], true);
22060
+ i2++;
22061
+ }
22062
+ if (i2 < src.length) push(src[i2], false);
22063
+ i2++;
22064
+ continue;
22065
+ }
22066
+ push(ch, false);
22067
+ i2++;
22068
+ }
22069
+ return { code, literals };
22070
+ }
22071
+ function commanderIsOpen(code) {
22072
+ return /\.command\(\s*[^'"`\s)]/.test(code);
22073
+ }
22074
+ function commanderSubcommands(code, literals) {
22075
+ if (commanderIsOpen(code)) return null;
22076
+ const found = /* @__PURE__ */ new Set();
22077
+ for (const m of literals.matchAll(/\.command\(\s*(['"`])([^'"`\n]+)\1/g)) {
22078
+ const name = m[2].trim().split(/\s+/)[0];
22079
+ if (name && !name.startsWith("<") && !name.startsWith("[")) found.add(name);
22080
+ }
22081
+ return found.size > 0 ? found : null;
22082
+ }
22083
+ function argvSubcommands(code, literals) {
22084
+ const aliases = /* @__PURE__ */ new Set();
22085
+ for (const m of code.matchAll(
22086
+ /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:process\.)?argv\[2\]/g
22087
+ )) {
22088
+ aliases.add(m[1]);
22089
+ }
22090
+ for (const m of code.matchAll(
22091
+ /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:process\.)?argv\.slice\(\s*2\s*\)\s*\[\s*0\s*\]/g
22092
+ )) {
22093
+ aliases.add(m[1]);
22094
+ }
22095
+ for (const m of code.matchAll(
22096
+ /\b(?:const|let|var)\s*\[\s*,\s*,\s*([A-Za-z_$][\w$]*)\s*\]\s*=\s*process\.argv/g
22097
+ )) {
22098
+ aliases.add(m[1]);
22099
+ }
22100
+ const tokens = [...aliases].map((a) => `\\b${a}\\b`);
22101
+ if (/(?:process\.)?argv\[2\]/.test(code)) tokens.push("(?:process\\.)?argv\\[2\\]");
22102
+ if (tokens.length === 0) return null;
22103
+ const tok = `(?:${tokens.join("|")})`;
22104
+ const cmpIdent = new RegExp(`${tok}\\s*[=!]==?\\s*([A-Za-z_$][\\w$.]*)`, "g");
22105
+ for (const m of code.matchAll(cmpIdent)) {
22106
+ if (!SENTINEL.test(m[1])) return null;
22107
+ }
22108
+ const identCmp = new RegExp(`([A-Za-z_$][\\w$.]*)\\s*[=!]==?\\s*${tok}`, "g");
22109
+ for (const m of code.matchAll(identCmp)) {
22110
+ if (!SENTINEL.test(m[1])) return null;
22111
+ }
22112
+ if (new RegExp(`\\[\\s*${tok}\\s*\\]`).test(code)) return null;
22113
+ if (new RegExp(`Object\\.(keys|values|entries)\\([^)]*\\)[^\\n]*${tok}`).test(code)) return null;
22114
+ if (new RegExp(`${tok}\\s+in\\s+`).test(code)) return null;
22115
+ if (new RegExp(`\\bhasOwnProperty\\(\\s*${tok}`).test(code)) return null;
22116
+ const includesRe = new RegExp(`(\\S*)\\.includes\\(\\s*${tok}\\s*\\)`, "g");
22117
+ for (const m of code.matchAll(includesRe)) {
22118
+ if (!m[1].endsWith("]")) return null;
22119
+ }
22120
+ const found = /* @__PURE__ */ new Set();
22121
+ const eqLit = new RegExp(`${tok}\\s*[=!]==?\\s*(['"\`])([^'"\`\\n]*)\\1`, "g");
22122
+ for (const m of literals.matchAll(eqLit)) if (m[2]) found.add(m[2]);
22123
+ const litEq = new RegExp(`(['"\`])([^'"\`\\n]*)\\1\\s*[=!]==?\\s*${tok}`, "g");
22124
+ for (const m of literals.matchAll(litEq)) if (m[2]) found.add(m[2]);
22125
+ const switchRe = new RegExp(`switch\\s*\\(\\s*${tok}\\s*\\)\\s*\\{`, "g");
22126
+ for (const m of literals.matchAll(switchRe)) {
22127
+ const body = literals.slice((m.index ?? 0) + m[0].length);
22128
+ const end = body.indexOf("\n}");
22129
+ const block = end >= 0 ? body.slice(0, end) : body;
22130
+ for (const c3 of block.matchAll(/\bcase\s+(['"`])([^'"`\n]*)\1/g)) {
22131
+ if (c3[2]) found.add(c3[2]);
22132
+ }
22133
+ }
22134
+ const arrIncludes = new RegExp(`\\[([^\\]\\n]*)\\]\\.includes\\(\\s*${tok}\\s*\\)`, "g");
22135
+ for (const m of literals.matchAll(arrIncludes)) {
22136
+ for (const lit of m[1].matchAll(/(['"`])([^'"`]*)\1/g)) {
22137
+ if (lit[2]) found.add(lit[2]);
22138
+ }
22139
+ }
22140
+ return found.size > 0 ? found : null;
22141
+ }
22142
+ function knownSubcommands(projectRoot, entry) {
22143
+ let abs = resolveEntry(projectRoot, entry);
22144
+ for (let hop = 0; abs && hop <= MAX_FOLLOW; hop++) {
22145
+ const src = readEntry(abs);
22146
+ if (src === null) return null;
22147
+ const { code, literals } = stripComments2(src);
22148
+ const commander = commanderSubcommands(code, literals);
22149
+ if (commander) return commander;
22150
+ if (commanderIsOpen(code)) return null;
22151
+ const argv = argvSubcommands(code, literals);
22152
+ if (argv) return argv;
22153
+ if (hop === MAX_FOLLOW) break;
22154
+ const target = shimTarget(src);
22155
+ if (!target) break;
22156
+ abs = resolveEntry(path5.dirname(abs), target);
22157
+ }
22158
+ return null;
22159
+ }
22160
+ function findBinInvocations(content, binNames) {
22161
+ if (binNames.size === 0) return [];
22162
+ const out = [];
22163
+ const lines = content.split("\n");
22164
+ let inFence = false;
22165
+ let fenceLang = "";
22166
+ let inHtmlComment = false;
22167
+ for (let i2 = 0; i2 < lines.length; i2++) {
22168
+ const line = lines[i2];
22169
+ const trimmed2 = line.trim();
22170
+ if (!inHtmlComment && trimmed2.includes("<!--")) {
22171
+ inHtmlComment = !trimmed2.includes("-->");
22172
+ continue;
22173
+ }
22174
+ if (inHtmlComment) {
22175
+ if (trimmed2.includes("-->")) inHtmlComment = false;
22176
+ continue;
22177
+ }
22178
+ if (trimmed2.startsWith("```")) {
22179
+ if (!inFence) {
22180
+ inFence = true;
22181
+ fenceLang = trimmed2.slice(3).trim().toLowerCase();
22182
+ } else {
22183
+ inFence = false;
22184
+ fenceLang = "";
22185
+ }
22186
+ continue;
22187
+ }
22188
+ const candidates = [];
22189
+ if (inFence) {
22190
+ if (["bash", "sh", "shell", "zsh", "console", ""].includes(fenceLang)) {
22191
+ if (trimmed2 && !trimmed2.startsWith("#") && !trimmed2.startsWith("//")) {
22192
+ candidates.push(trimmed2.replace(/^[$>]\s*/, ""));
22193
+ }
22194
+ }
22195
+ } else {
22196
+ if (/^[$>]\s+/.test(trimmed2)) candidates.push(trimmed2.replace(/^[$>]\s*/, ""));
22197
+ for (const m of line.matchAll(/`([^`]+)`/g)) candidates.push(m[1].trim());
22198
+ }
22199
+ for (const candidate of candidates) {
22200
+ const hit = matchBinInvocation(candidate, binNames);
22201
+ if (hit) out.push({ ...hit, cmd: candidate, line: i2 + 1 });
22202
+ }
22203
+ }
22204
+ return out;
22205
+ }
22206
+ function matchBinInvocation(candidate, binNames) {
22207
+ let s = candidate.trim();
22208
+ const stop = s.search(/&&|\|\||[;|]/);
22209
+ if (stop >= 0) s = s.slice(0, stop).trim();
22210
+ s = s.replace(TWO_TOKEN_DELEGATORS, "");
22211
+ let tokens = s.split(/\s+/).filter(Boolean);
22212
+ while (tokens.length > 0) {
22213
+ const head = commandNameOf(tokens[0]);
22214
+ if (head && DELEGATORS.has(head)) {
22215
+ tokens = tokens.slice(1);
22216
+ while (tokens.length > 0 && tokens[0].startsWith("-")) tokens = tokens.slice(1);
22217
+ continue;
22218
+ }
22219
+ break;
22220
+ }
22221
+ if (tokens.length < 2) return null;
22222
+ const bin = commandNameOf(tokens[0]);
22223
+ if (!bin || !binNames.has(bin)) return null;
22224
+ for (const raw of tokens.slice(1)) {
22225
+ if (raw.startsWith("-")) continue;
22226
+ const t2 = raw.replace(/^["']|["']$/g, "");
22227
+ if (!SUBCOMMAND_SHAPE.test(t2)) return null;
22228
+ return { bin, sub: t2 };
22229
+ }
22230
+ return null;
22231
+ }
22232
+ var BIN_EXT, MAX_ENTRY_BYTES, MAX_ENTRY_LINE, MAX_FOLLOW, SENTINEL, DELEGATORS, TWO_TOKEN_DELEGATORS, SUBCOMMAND_SHAPE;
22233
+ var init_cli_subcommands = __esm({
22234
+ "src/core/checks/cli-subcommands.ts"() {
22235
+ "use strict";
22236
+ init_define_WEB_FIRST_SEGMENTS();
22237
+ init_fs();
22238
+ BIN_EXT = /\.(exe|cmd|bat|ps1|js|mjs|cjs|ts)$/i;
22239
+ MAX_ENTRY_BYTES = 15e4;
22240
+ MAX_ENTRY_LINE = 500;
22241
+ MAX_FOLLOW = 1;
22242
+ SENTINEL = /^(undefined|null|NaN|void)$/;
22243
+ DELEGATORS = /* @__PURE__ */ new Set(["npx", "bunx", "sudo", "time"]);
22244
+ TWO_TOKEN_DELEGATORS = /^(?:pnpm|npm|yarn|bun)\s+(?:exec|dlx)\s+/;
22245
+ SUBCOMMAND_SHAPE = /^[a-z][a-z0-9:_-]*$/i;
22246
+ }
22247
+ });
22248
+
22249
+ // src/core/checks/commands.ts
22250
+ import * as fs5 from "node:fs";
22251
+ import * as path6 from "node:path";
21766
22252
  function isManagerBuiltin(manager, sub) {
21767
22253
  return PM_MANAGER_BUILTINS[manager]?.has(sub) ?? false;
21768
22254
  }
@@ -21803,7 +22289,7 @@ function loadDeniedCommandPrefixes(projectRoot) {
21803
22289
  for (const rel of ["settings.json", "settings.local.json"]) {
21804
22290
  let content;
21805
22291
  try {
21806
- content = stripBom(fs4.readFileSync(path5.join(projectRoot, ".claude", rel), "utf-8"));
22292
+ content = stripBom(fs5.readFileSync(path6.join(projectRoot, ".claude", rel), "utf-8"));
21807
22293
  } catch {
21808
22294
  continue;
21809
22295
  }
@@ -21840,8 +22326,21 @@ async function checkCommands(file2, projectRoot) {
21840
22326
  });
21841
22327
  }
21842
22328
  }
22329
+ issues.push(...checkUnknownSubcommand(file2, projectRoot, pkgJson));
21843
22330
  for (const ref of file2.references.commands) {
21844
22331
  const cmd = ref.value;
22332
+ const masked = analyzeMaskedExitStatus(cmd, pkgJson?.scripts);
22333
+ if (masked && !pipefailInScope(file2.content, ref.line)) {
22334
+ const why = masked.kind === "success-claim" ? "the success claim cannot fail" : "`$?` reports the filter, not the verifier";
22335
+ issues.push({
22336
+ severity: "warning",
22337
+ check: "commands",
22338
+ ruleId: "commands/exit-status-masked",
22339
+ line: ref.line,
22340
+ message: `"${cmd}" \u2014 exit status comes from "${masked.filter}", not "${masked.verifier}"; ${why}`,
22341
+ suggestion: "Add `set -o pipefail` before the pipeline, drop the filter, or read `${PIPESTATUS[0]}` instead of `$?`."
22342
+ });
22343
+ }
21845
22344
  const scriptMatch = cmd.match(NPM_SCRIPT_PATTERN);
21846
22345
  if (scriptMatch && pkgJson) {
21847
22346
  const scriptName = scriptNameFromMatch(scriptMatch);
@@ -21885,9 +22384,9 @@ async function checkCommands(file2, projectRoot) {
21885
22384
  };
21886
22385
  if (!(pkgName in allDeps)) {
21887
22386
  if (isDeniedCommand(cmd, deniedPrefixes)) continue;
21888
- const binPath = path5.join(projectRoot, "node_modules", ".bin", pkgName);
22387
+ const binPath = path6.join(projectRoot, "node_modules", ".bin", pkgName);
21889
22388
  try {
21890
- fs4.accessSync(binPath);
22389
+ fs5.accessSync(binPath);
21891
22390
  } catch {
21892
22391
  issues.push({
21893
22392
  severity: "warning",
@@ -21935,9 +22434,9 @@ async function checkCommands(file2, projectRoot) {
21935
22434
  ...pkgJson.optionalDependencies
21936
22435
  };
21937
22436
  if (!(pkgName in allDeps)) {
21938
- const binPath = path5.join(projectRoot, "node_modules", ".bin", tool);
22437
+ const binPath = path6.join(projectRoot, "node_modules", ".bin", tool);
21939
22438
  try {
21940
- fs4.accessSync(binPath);
22439
+ fs5.accessSync(binPath);
21941
22440
  } catch {
21942
22441
  issues.push({
21943
22442
  severity: "warning",
@@ -21952,9 +22451,36 @@ async function checkCommands(file2, projectRoot) {
21952
22451
  }
21953
22452
  return issues;
21954
22453
  }
22454
+ function checkUnknownSubcommand(file2, projectRoot, pkgJson) {
22455
+ const bins = ownedBins(pkgJson);
22456
+ if (bins.length === 0) return [];
22457
+ const invocations = findBinInvocations(file2.content, new Set(bins.map((b2) => b2.name)));
22458
+ if (invocations.length === 0) return [];
22459
+ const resolved = /* @__PURE__ */ new Map();
22460
+ const issues = [];
22461
+ for (const inv of invocations) {
22462
+ if (!resolved.has(inv.bin)) {
22463
+ const bin = bins.find((b2) => b2.name === inv.bin);
22464
+ resolved.set(inv.bin, bin ? knownSubcommands(projectRoot, bin.entry) : null);
22465
+ }
22466
+ const known = resolved.get(inv.bin);
22467
+ if (!known || known.size === 0) continue;
22468
+ if (known.has(inv.sub)) continue;
22469
+ const listed = [...known].sort().join(", ");
22470
+ issues.push({
22471
+ severity: "error",
22472
+ check: "commands",
22473
+ ruleId: "commands/unknown-subcommand",
22474
+ line: inv.line,
22475
+ message: `"${inv.cmd}" \u2014 "${inv.sub}" is not a subcommand of ${inv.bin} (known: ${listed})`,
22476
+ suggestion: `Use one of the subcommands ${inv.bin} implements, or remove the invocation.`
22477
+ });
22478
+ }
22479
+ return issues;
22480
+ }
21955
22481
  function loadMakefile(projectRoot) {
21956
22482
  try {
21957
- return stripBom(fs4.readFileSync(path5.join(projectRoot, "Makefile"), "utf-8"));
22483
+ return stripBom(fs5.readFileSync(path6.join(projectRoot, "Makefile"), "utf-8"));
21958
22484
  } catch {
21959
22485
  return null;
21960
22486
  }
@@ -21979,6 +22505,8 @@ var init_commands = __esm({
21979
22505
  init_define_WEB_FIRST_SEGMENTS();
21980
22506
  init_main3();
21981
22507
  init_fs();
22508
+ init_exit_status();
22509
+ init_cli_subcommands();
21982
22510
  NPM_SCRIPT_PATTERN = /^(?:npm\s+run|(pnpm|yarn|bun)(?:\s+(run))?)\s+(\S+)/;
21983
22511
  MAKE_PATTERN = /^make\s+\S/;
21984
22512
  PM_BUILTIN_SUBCOMMANDS = /* @__PURE__ */ new Set([
@@ -22052,13 +22580,13 @@ var init_commands = __esm({
22052
22580
  });
22053
22581
 
22054
22582
  // src/core/checks/staleness.ts
22055
- import * as path6 from "node:path";
22583
+ import * as path7 from "node:path";
22056
22584
  async function checkStaleness(file2, projectRoot) {
22057
22585
  const issues = [];
22058
22586
  if (!await isGitRepo(projectRoot)) {
22059
22587
  return issues;
22060
22588
  }
22061
- const relativePath = path6.relative(projectRoot, file2.filePath).replace(/\\/g, "/");
22589
+ const relativePath = path7.relative(projectRoot, file2.filePath).replace(/\\/g, "/");
22062
22590
  const lastModified = await getFileLastModified(projectRoot, relativePath);
22063
22591
  if (!lastModified || isNaN(lastModified.getTime())) {
22064
22592
  return issues;
@@ -22130,6 +22658,50 @@ var init_staleness = __esm({
22130
22658
  }
22131
22659
  });
22132
22660
 
22661
+ // src/core/suppressions.ts
22662
+ function collectSuppressions(content) {
22663
+ const suppressions = /* @__PURE__ */ new Map();
22664
+ const lines = content.split("\n");
22665
+ const add = (lineNumber, checks) => {
22666
+ const existing = suppressions.get(lineNumber);
22667
+ if (existing === null) return;
22668
+ if (checks.length === 0) {
22669
+ suppressions.set(lineNumber, null);
22670
+ return;
22671
+ }
22672
+ const merged = existing ?? /* @__PURE__ */ new Set();
22673
+ for (const check2 of checks) merged.add(check2);
22674
+ suppressions.set(lineNumber, merged);
22675
+ };
22676
+ for (let i2 = 0; i2 < lines.length; i2++) {
22677
+ const line = lines[i2];
22678
+ if (!line.includes("ctxlint-ignore-")) continue;
22679
+ DIRECTIVE_RE.lastIndex = 0;
22680
+ let match;
22681
+ while ((match = DIRECTIVE_RE.exec(line)) !== null) {
22682
+ const kind = match[1];
22683
+ const checks = (match[2] ?? "").trim().split(/\s+/).filter(Boolean);
22684
+ add(kind === "next-line" ? i2 + 2 : i2 + 1, checks);
22685
+ }
22686
+ }
22687
+ return suppressions;
22688
+ }
22689
+ function isSuppressed(suppressions, issue2) {
22690
+ if (suppressions.size === 0) return false;
22691
+ if (!suppressions.has(issue2.line)) return false;
22692
+ const checks = suppressions.get(issue2.line);
22693
+ if (checks === null) return true;
22694
+ return checks.has(issue2.check);
22695
+ }
22696
+ var DIRECTIVE_RE;
22697
+ var init_suppressions = __esm({
22698
+ "src/core/suppressions.ts"() {
22699
+ "use strict";
22700
+ init_define_WEB_FIRST_SEGMENTS();
22701
+ DIRECTIVE_RE = /<!--\s*ctxlint-ignore-(next-line|line)((?:\s+[\w-]+)*)\s*-->/g;
22702
+ }
22703
+ });
22704
+
22133
22705
  // src/core/checks/tokens.ts
22134
22706
  function resolveTokenThresholds(overrides) {
22135
22707
  if (!overrides) return DEFAULT_TOKEN_THRESHOLDS;
@@ -22205,9 +22777,9 @@ var init_tokens2 = __esm({
22205
22777
  });
22206
22778
 
22207
22779
  // src/core/checks/tier-tokens.ts
22208
- import * as fs5 from "node:fs";
22780
+ import * as fs6 from "node:fs";
22209
22781
  import * as os2 from "node:os";
22210
- import * as path7 from "node:path";
22782
+ import * as path8 from "node:path";
22211
22783
  function hasFrontmatterList(content, field) {
22212
22784
  const lines = content.split("\n");
22213
22785
  if (lines[0]?.trim() !== "---") return false;
@@ -22277,7 +22849,7 @@ function computeSectionCosts(file2) {
22277
22849
  function fingerprintFiles(paths) {
22278
22850
  return paths.map((p2) => {
22279
22851
  try {
22280
- const st2 = fs5.statSync(p2);
22852
+ const st2 = fs6.statSync(p2);
22281
22853
  return `${st2.mtimeMs}:${st2.size}`;
22282
22854
  } catch {
22283
22855
  return "absent";
@@ -22286,11 +22858,11 @@ function fingerprintFiles(paths) {
22286
22858
  }
22287
22859
  function loadSettingsSources(projectRoot, includeGlobal) {
22288
22860
  const candidates = [
22289
- path7.join(projectRoot, ".claude", "settings.json"),
22290
- path7.join(projectRoot, ".claude", "settings.local.json")
22861
+ path8.join(projectRoot, ".claude", "settings.json"),
22862
+ path8.join(projectRoot, ".claude", "settings.local.json")
22291
22863
  ];
22292
22864
  if (includeGlobal) {
22293
- candidates.push(path7.join(os2.homedir(), ".claude", "settings.json"));
22865
+ candidates.push(path8.join(os2.homedir(), ".claude", "settings.json"));
22294
22866
  }
22295
22867
  const fingerprint = fingerprintFiles(candidates);
22296
22868
  if (settingsCache?.root === projectRoot && settingsCache.includeGlobal === includeGlobal && settingsCache.fingerprint === fingerprint) {
@@ -22300,7 +22872,7 @@ function loadSettingsSources(projectRoot, includeGlobal) {
22300
22872
  for (const p2 of candidates) {
22301
22873
  let content;
22302
22874
  try {
22303
- content = stripBom(fs5.readFileSync(p2, "utf-8"));
22875
+ content = stripBom(fs6.readFileSync(p2, "utf-8"));
22304
22876
  } catch {
22305
22877
  continue;
22306
22878
  }
@@ -22474,7 +23046,7 @@ var init_similarity = __esm({
22474
23046
  });
22475
23047
 
22476
23048
  // src/core/checks/redundancy.ts
22477
- import * as path8 from "node:path";
23049
+ import * as path9 from "node:path";
22478
23050
  function compilePatterns(allDeps) {
22479
23051
  const compiled = [];
22480
23052
  for (const [pkg, mentions] of Object.entries(PACKAGE_TECH_MAP)) {
@@ -22554,8 +23126,8 @@ async function checkRedundancy(file2, projectRoot) {
22554
23126
  );
22555
23127
  if (dirMatch) {
22556
23128
  const dir = dirMatch[1].replace(/[`"]/g, "");
22557
- const baseDir = dir.startsWith("./") || dir.startsWith("../") ? path8.dirname(file2.filePath) : projectRoot;
22558
- const fullPath = path8.resolve(baseDir, dir);
23129
+ const baseDir = dir.startsWith("./") || dir.startsWith("../") ? path9.dirname(file2.filePath) : projectRoot;
23130
+ const fullPath = path9.resolve(baseDir, dir);
22559
23131
  if (isDirectory(fullPath)) {
22560
23132
  issues.push({
22561
23133
  severity: "info",
@@ -23583,8 +24155,8 @@ var init_security = __esm({
23583
24155
  });
23584
24156
 
23585
24157
  // src/core/checks/mcp/commands.ts
23586
- import * as fs6 from "node:fs";
23587
- import * as path9 from "node:path";
24158
+ import * as fs7 from "node:fs";
24159
+ import * as path10 from "node:path";
23588
24160
  async function checkMcpCommands(config2, projectRoot) {
23589
24161
  const issues = [];
23590
24162
  if (config2.parseErrors.length > 0) return issues;
@@ -23601,7 +24173,7 @@ async function checkMcpCommands(config2, projectRoot) {
23601
24173
  });
23602
24174
  }
23603
24175
  if (config2.scope === "project" && LOCAL_PATH_PATTERN.test(server2.command)) {
23604
- const resolved = path9.resolve(projectRoot, server2.command);
24176
+ const resolved = path10.resolve(projectRoot, server2.command);
23605
24177
  if (!fileExistsSafe(resolved)) {
23606
24178
  issues.push({
23607
24179
  severity: "warning",
@@ -23616,8 +24188,8 @@ async function checkMcpCommands(config2, projectRoot) {
23616
24188
  for (const arg of server2.args) {
23617
24189
  if (URL_PREFIX.test(arg)) continue;
23618
24190
  if (!LOCAL_PATH_PATTERN.test(arg) && !FILE_PATH_PATTERN.test(arg)) continue;
23619
- if (config2.scope !== "project" && !path9.isAbsolute(arg)) continue;
23620
- const resolved = path9.resolve(projectRoot, arg);
24191
+ if (config2.scope !== "project" && !path10.isAbsolute(arg)) continue;
24192
+ const resolved = path10.resolve(projectRoot, arg);
23621
24193
  if (!fileExistsSafe(resolved)) {
23622
24194
  issues.push({
23623
24195
  severity: "warning",
@@ -23634,7 +24206,7 @@ async function checkMcpCommands(config2, projectRoot) {
23634
24206
  }
23635
24207
  function fileExistsSafe(filePath) {
23636
24208
  try {
23637
- fs6.accessSync(filePath);
24209
+ fs7.accessSync(filePath);
23638
24210
  return true;
23639
24211
  } catch {
23640
24212
  return false;
@@ -24065,22 +24637,27 @@ var init_redundancy2 = __esm({
24065
24637
 
24066
24638
  // src/core/session-parser.ts
24067
24639
  import { readFile } from "node:fs/promises";
24068
- import { readFileSync as readFileSync4 } from "node:fs";
24069
- import { dirname as dirname3, resolve as resolve6 } from "node:path";
24640
+ import { readFileSync as readFileSync5 } from "node:fs";
24641
+ import { dirname as dirname4, resolve as resolve7 } from "node:path";
24070
24642
  import { fileURLToPath } from "node:url";
24071
24643
  function loadWebFirstSegments() {
24072
24644
  if (typeof define_WEB_FIRST_SEGMENTS_default !== "undefined") {
24073
24645
  return new Set(define_WEB_FIRST_SEGMENTS_default);
24074
24646
  }
24075
- const __dir = dirname3(fileURLToPath(import.meta.url));
24076
- const fixturePath = resolve6(__dir, "../../tests/fixtures/web-first-segments.json");
24077
- const raw = JSON.parse(readFileSync4(fixturePath, "utf-8"));
24647
+ const __dir = dirname4(fileURLToPath(import.meta.url));
24648
+ const fixturePath = resolve7(__dir, "../../tests/fixtures/web-first-segments.json");
24649
+ const raw = JSON.parse(readFileSync5(fixturePath, "utf-8"));
24078
24650
  const list = Array.isArray(raw) ? raw : raw.segments;
24079
24651
  return new Set(list);
24080
24652
  }
24081
24653
  function encodeProjectDir(fsPath) {
24082
24654
  return fsPath.replace(/[:\\/\.]/g, "-");
24083
24655
  }
24656
+ function projectDirCandidates(fsPath) {
24657
+ const encoded = encodeProjectDir(fsPath);
24658
+ const folded = encoded.replace(/_/g, "-");
24659
+ return folded === encoded ? [encoded] : [encoded, folded];
24660
+ }
24084
24661
  function projectDirMatchesPath(encodedDir, fsPath) {
24085
24662
  const normalized = fsPath.replace(/\\/g, "/");
24086
24663
  return encodedDir === encodeProjectDir(normalized);
@@ -24191,7 +24768,7 @@ import { readdir, stat } from "node:fs/promises";
24191
24768
  import { createReadStream } from "node:fs";
24192
24769
  import { homedir as homedir3 } from "node:os";
24193
24770
  import { createInterface } from "node:readline";
24194
- import { dirname as dirname4, join as join5, resolve as resolve7 } from "node:path";
24771
+ import { dirname as dirname5, join as join5, resolve as resolve8 } from "node:path";
24195
24772
  import { existsSync } from "node:fs";
24196
24773
  function detectProviders() {
24197
24774
  if (!home) return [];
@@ -24306,7 +24883,7 @@ async function mapWithConcurrency(items, limit, fn) {
24306
24883
  return results;
24307
24884
  }
24308
24885
  async function detectSiblings(projectRoot) {
24309
- const parentDir = dirname4(resolve7(projectRoot));
24886
+ const parentDir = dirname5(resolve8(projectRoot));
24310
24887
  let entries;
24311
24888
  try {
24312
24889
  entries = await readdir(parentDir);
@@ -24317,8 +24894,8 @@ async function detectSiblings(projectRoot) {
24317
24894
  for (const entry of entries) {
24318
24895
  if (entry.startsWith(".") || entry === "node_modules") continue;
24319
24896
  const fullPath = join5(parentDir, entry);
24320
- const entryPath = resolve7(fullPath);
24321
- if (entryPath === resolve7(projectRoot)) continue;
24897
+ const entryPath = resolve8(fullPath);
24898
+ if (entryPath === resolve8(projectRoot)) continue;
24322
24899
  try {
24323
24900
  const s = await stat(fullPath);
24324
24901
  if (!s.isDirectory()) continue;
@@ -24385,7 +24962,7 @@ async function scanSessionData(projectRoot) {
24385
24962
  history: histories,
24386
24963
  memories,
24387
24964
  siblings,
24388
- currentProject: resolve7(projectRoot).replace(/\\/g, "/"),
24965
+ currentProject: resolve8(projectRoot).replace(/\\/g, "/"),
24389
24966
  providers
24390
24967
  };
24391
24968
  }
@@ -24426,9 +25003,9 @@ var init_session_scanner = __esm({
24426
25003
  });
24427
25004
 
24428
25005
  // src/core/checks/session/missing-secret.ts
24429
- import { resolve as resolve8, basename as basename3 } from "node:path";
25006
+ import { resolve as resolve9, basename as basename3 } from "node:path";
24430
25007
  function normalizePath2(p2) {
24431
- return resolve8(p2).replace(/\\/g, "/").toLowerCase();
25008
+ return resolve9(p2).replace(/\\/g, "/").toLowerCase();
24432
25009
  }
24433
25010
  function extractSecretName(display) {
24434
25011
  const prefixMatch = display.match(SECRET_SET_PREFIX);
@@ -24546,7 +25123,7 @@ var init_missing_secret = __esm({
24546
25123
 
24547
25124
  // src/core/checks/session/diverged-file.ts
24548
25125
  import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
24549
- import { join as join6, resolve as resolve9 } from "node:path";
25126
+ import { join as join6, resolve as resolve10 } from "node:path";
24550
25127
  import { existsSync as existsSync2 } from "node:fs";
24551
25128
  async function loadLineSet(absPath) {
24552
25129
  let mtimeMs;
@@ -24598,7 +25175,7 @@ async function checkDivergedFile(ctx) {
24598
25175
  diverged.push({
24599
25176
  sibling: sib.name,
24600
25177
  overlap: Math.round(overlap * 100),
24601
- path: resolve9(sibPath)
25178
+ path: resolve10(sibPath)
24602
25179
  });
24603
25180
  }
24604
25181
  }
@@ -24613,7 +25190,7 @@ async function checkDivergedFile(ctx) {
24613
25190
  line: 0,
24614
25191
  message: `${fileName} has diverged from sibling repos: ${details}`,
24615
25192
  suggestion: `Compare with sibling versions to identify unintentional drift`,
24616
- detail: `File: ${resolve9(currentPath)}
25193
+ detail: `File: ${resolve10(currentPath)}
24617
25194
  Diverged siblings:
24618
25195
  ${sibPaths}`
24619
25196
  });
@@ -24703,15 +25280,15 @@ var init_missing_workflow = __esm({
24703
25280
  // src/core/checks/session/stale-memory.ts
24704
25281
  import { existsSync as existsSync4 } from "node:fs";
24705
25282
  import { homedir as homedir4 } from "node:os";
24706
- import { resolve as resolve10, isAbsolute as isAbsolute5 } from "node:path";
25283
+ import { resolve as resolve11, isAbsolute as isAbsolute5 } from "node:path";
24707
25284
  function resolveRef(ref, projectRoot) {
24708
25285
  if (ref.startsWith("~/") || ref === "~") {
24709
25286
  const home2 = process.env.HOME || process.env.USERPROFILE || homedir4();
24710
25287
  if (!home2) return null;
24711
- return ref === "~" ? home2 : resolve10(home2, ref.slice(2));
25288
+ return ref === "~" ? home2 : resolve11(home2, ref.slice(2));
24712
25289
  }
24713
25290
  if (isAbsolute5(ref)) return ref;
24714
- return resolve10(projectRoot, ref);
25291
+ return resolve11(projectRoot, ref);
24715
25292
  }
24716
25293
  async function checkStaleMemory(ctx) {
24717
25294
  const issues = [];
@@ -24808,9 +25385,9 @@ var init_duplicate_memory = __esm({
24808
25385
  });
24809
25386
 
24810
25387
  // src/core/checks/session/loop-detection.ts
24811
- import { resolve as resolve11 } from "node:path";
25388
+ import { resolve as resolve12 } from "node:path";
24812
25389
  function normalizeProject(p2) {
24813
- return resolve11(p2).replace(/\\/g, "/");
25390
+ return resolve12(p2).replace(/\\/g, "/");
24814
25391
  }
24815
25392
  function findConsecutiveRepeats(displays) {
24816
25393
  const results = [];
@@ -24967,13 +25544,19 @@ import { join as join8 } from "node:path";
24967
25544
  async function checkMemoryIndexOverflow(ctx) {
24968
25545
  const home2 = process.env.HOME || process.env.USERPROFILE || homedir5();
24969
25546
  if (!home2) return [];
24970
- const encoded = encodeProjectDir(ctx.currentProject);
24971
- const memoryFile = join8(home2, ".claude", "projects", encoded, "memory", "MEMORY.md");
24972
- let content;
24973
- try {
24974
- content = stripBom(await readFile3(memoryFile, "utf-8"));
24975
- } catch {
24976
- return [];
25547
+ let content = "";
25548
+ let memoryFile = "";
25549
+ for (const encoded of projectDirCandidates(ctx.currentProject)) {
25550
+ const candidate = join8(home2, ".claude", "projects", encoded, "memory", "MEMORY.md");
25551
+ try {
25552
+ content = stripBom(await readFile3(candidate, "utf-8"));
25553
+ } catch {
25554
+ continue;
25555
+ }
25556
+ if (content) {
25557
+ memoryFile = candidate;
25558
+ break;
25559
+ }
24977
25560
  }
24978
25561
  if (!content) return [];
24979
25562
  const lines = content.split("\n");
@@ -25018,16 +25601,529 @@ var init_memory_index_overflow = __esm({
25018
25601
  }
25019
25602
  });
25020
25603
 
25021
- // src/core/checks/ci-coverage.ts
25022
- import { readdir as readdir3, readFile as readFile4 } from "node:fs/promises";
25604
+ // src/core/transcript.ts
25605
+ import { createReadStream as createReadStream2, existsSync as existsSync5, statSync as statSync5 } from "node:fs";
25606
+ import { readdir as readdir3 } from "node:fs/promises";
25607
+ import { homedir as homedir6 } from "node:os";
25023
25608
  import { join as join9 } from "node:path";
25024
- import { existsSync as existsSync5 } from "node:fs";
25609
+ import { createInterface as createInterface2 } from "node:readline";
25610
+ function asString(v2) {
25611
+ return typeof v2 === "string" ? v2 : "";
25612
+ }
25613
+ function resultText(content) {
25614
+ if (typeof content === "string") return content;
25615
+ if (!Array.isArray(content)) return "";
25616
+ return content.map((b2) => b2 && typeof b2 === "object" ? asString(b2.text) : "").join("");
25617
+ }
25618
+ function resolveHome() {
25619
+ return process.env.HOME || process.env.USERPROFILE || homedir6();
25620
+ }
25621
+ function readProjectTranscript(project, home2 = resolveHome()) {
25622
+ const key = `${home2} ${project}`;
25623
+ let hit = cache.get(key);
25624
+ if (!hit) {
25625
+ hit = readUncached(project, home2);
25626
+ cache.set(key, hit);
25627
+ }
25628
+ return hit;
25629
+ }
25630
+ function candidateDirs(project, home2) {
25631
+ const root = join9(home2, ".claude", "projects");
25632
+ return projectDirCandidates(project).map((n7) => join9(root, n7)).filter((d) => existsSync5(d));
25633
+ }
25634
+ async function readUncached(project, home2) {
25635
+ if (!home2 || !project) return EMPTY;
25636
+ const dirs = candidateDirs(project, home2);
25637
+ if (dirs.length === 0) return EMPTY;
25638
+ const names = [];
25639
+ for (const dir of dirs) {
25640
+ for (const name of await readdir3(dir).catch(() => [])) {
25641
+ names.push({ dir, name });
25642
+ }
25643
+ }
25644
+ const files = names.filter(({ name }) => name.endsWith(".jsonl")).map(({ dir, name }) => {
25645
+ const p2 = join9(dir, name);
25646
+ try {
25647
+ return { p: p2, mtime: statSync5(p2).mtimeMs };
25648
+ } catch {
25649
+ return null;
25650
+ }
25651
+ }).filter((f) => f !== null).sort((a, b2) => b2.mtime - a.mtime);
25652
+ let truncated = files.length > MAX_TRANSCRIPTS;
25653
+ const selected = files.slice(0, MAX_TRANSCRIPTS);
25654
+ const events = [];
25655
+ const pending = /* @__PURE__ */ new Map();
25656
+ let lines = 0;
25657
+ for (const { p: p2 } of selected) {
25658
+ if (lines >= MAX_LINES2) {
25659
+ truncated = true;
25660
+ break;
25661
+ }
25662
+ const rl = createInterface2({
25663
+ input: createReadStream2(p2, { encoding: "utf-8" }),
25664
+ crlfDelay: Infinity
25665
+ });
25666
+ try {
25667
+ for await (const raw of rl) {
25668
+ if (++lines >= MAX_LINES2) {
25669
+ truncated = true;
25670
+ break;
25671
+ }
25672
+ const line = raw.trim();
25673
+ if (!line) continue;
25674
+ let rec;
25675
+ try {
25676
+ rec = JSON.parse(line);
25677
+ } catch {
25678
+ continue;
25679
+ }
25680
+ collect(rec, events, pending);
25681
+ }
25682
+ } catch {
25683
+ } finally {
25684
+ rl.close();
25685
+ }
25686
+ }
25687
+ return { events, filesRead: selected.length, truncated };
25688
+ }
25689
+ function collect(rec, events, pending) {
25690
+ const message = rec.message;
25691
+ const content = message?.content;
25692
+ if (!Array.isArray(content)) return;
25693
+ const timestamp = Date.parse(asString(rec.timestamp)) || 0;
25694
+ const sessionId = asString(rec.sessionId) || asString(rec.session_id);
25695
+ const gitBranch = asString(rec.gitBranch) || void 0;
25696
+ for (const raw of content) {
25697
+ if (!raw || typeof raw !== "object") continue;
25698
+ const block = raw;
25699
+ const type = asString(block.type);
25700
+ if (type === "text" && rec.type === "assistant") {
25701
+ const text = asString(block.text);
25702
+ if (text) {
25703
+ events.push({ kind: "assistant-text", text, tool: "", gitBranch, timestamp, sessionId });
25704
+ }
25705
+ continue;
25706
+ }
25707
+ if (type === "tool_use") {
25708
+ const tool = asString(block.name);
25709
+ const input = block.input ?? {};
25710
+ const cmdField = COMMAND_TOOLS[tool];
25711
+ const writeField = WRITE_TOOLS[tool];
25712
+ let ev = null;
25713
+ if (cmdField) {
25714
+ const text = asString(input[cmdField]);
25715
+ if (text) ev = { kind: "command", text, tool, gitBranch, timestamp, sessionId };
25716
+ } else if (writeField) {
25717
+ const text = asString(input[writeField]);
25718
+ if (text) ev = { kind: "file-write", text, tool, gitBranch, timestamp, sessionId };
25719
+ }
25720
+ if (ev) {
25721
+ events.push(ev);
25722
+ const id = asString(block.id);
25723
+ if (id) pending.set(id, ev);
25724
+ }
25725
+ continue;
25726
+ }
25727
+ if (type === "tool_result") {
25728
+ const id = asString(block.tool_use_id);
25729
+ const ev = id ? pending.get(id) : void 0;
25730
+ if (!ev) continue;
25731
+ pending.delete(id);
25732
+ ev.isError = block.is_error === true;
25733
+ ev.emptyOutput = resultText(block.content).trim().length === 0;
25734
+ }
25735
+ }
25736
+ }
25737
+ var WRITE_TOOLS, COMMAND_TOOLS, MAX_TRANSCRIPTS, MAX_LINES2, EMPTY, cache;
25738
+ var init_transcript = __esm({
25739
+ "src/core/transcript.ts"() {
25740
+ "use strict";
25741
+ init_define_WEB_FIRST_SEGMENTS();
25742
+ init_session_parser();
25743
+ WRITE_TOOLS = {
25744
+ Write: "file_path",
25745
+ Edit: "file_path",
25746
+ NotebookEdit: "notebook_path"
25747
+ };
25748
+ COMMAND_TOOLS = {
25749
+ Bash: "command",
25750
+ PowerShell: "command"
25751
+ };
25752
+ MAX_TRANSCRIPTS = 5;
25753
+ MAX_LINES2 = 2e5;
25754
+ EMPTY = { events: [], filesRead: 0, truncated: false };
25755
+ cache = /* @__PURE__ */ new Map();
25756
+ }
25757
+ });
25758
+
25759
+ // src/core/checks/session/shared-temp-path.ts
25760
+ function unquote(raw) {
25761
+ let s = raw;
25762
+ let prev;
25763
+ do {
25764
+ prev = s;
25765
+ s = s.replace(/^['"`]|['"`]$/g, "");
25766
+ } while (s !== prev);
25767
+ return s;
25768
+ }
25769
+ function normalize3(raw) {
25770
+ return unquote(raw).replace(/\\/g, "/").toLowerCase();
25771
+ }
25772
+ function isFixedTempPath(candidate) {
25773
+ const p2 = normalize3(candidate);
25774
+ const root = TEMP_ROOTS.map((r2) => normalize3(r2)).find((r2) => p2.startsWith(r2));
25775
+ if (!root) return false;
25776
+ if (p2.length <= root.length) return false;
25777
+ return !SCOPED_MARKERS.some((m) => p2.includes(m.toLowerCase()));
25778
+ }
25779
+ function extract(line, patterns) {
25780
+ const found = [];
25781
+ for (const re2 of patterns) {
25782
+ for (const m of line.matchAll(new RegExp(re2, "g"))) {
25783
+ if (m[1]) found.push(m[1]);
25784
+ }
25785
+ }
25786
+ return found;
25787
+ }
25788
+ async function checkSharedTempPath(ctx) {
25789
+ const issues = [];
25790
+ const written = /* @__PURE__ */ new Map();
25791
+ const reported = /* @__PURE__ */ new Set();
25792
+ const transcript = await readProjectTranscript(ctx.currentProject);
25793
+ const scanned = [
25794
+ ...transcript.events.filter((e) => e.kind === "command").map((e) => ({ display: e.text, timestamp: e.timestamp })),
25795
+ ...ctx.history.map((h2) => ({ display: h2.display, timestamp: h2.timestamp }))
25796
+ ];
25797
+ const ordered = scanned.sort((a, b2) => a.timestamp - b2.timestamp);
25798
+ for (const entry of ordered) {
25799
+ const line = entry.display;
25800
+ const usesMktemp = /\bmktemp\b/.test(line);
25801
+ for (const candidate of extract(line, READ_PATTERNS)) {
25802
+ if (usesMktemp || !isFixedTempPath(candidate)) continue;
25803
+ const key = normalize3(candidate);
25804
+ const origin = written.get(key);
25805
+ if (!origin || reported.has(key)) continue;
25806
+ reported.add(key);
25807
+ issues.push({
25808
+ severity: "error",
25809
+ check: "session-shared-temp-path",
25810
+ ruleId: "session-shared-temp-path/shared-temp-path",
25811
+ line: 0,
25812
+ message: `Fixed temp path "${unquote(candidate)}" is written and later read back`,
25813
+ detail: `Written by: ${origin.display.trim().slice(0, 120)}
25814
+ Read by: ${line.trim().slice(0, 120)}`,
25815
+ suggestion: "Use a per-run path instead: `T=$(mktemp)` in shell, or a session-scoped scratch directory. A literal path under a shared temp root can be overwritten by any concurrent session between the write and the read, and a restore then puts the wrong bytes into your workspace."
25816
+ });
25817
+ }
25818
+ for (const candidate of extract(line, WRITE_PATTERNS)) {
25819
+ if (usesMktemp || !isFixedTempPath(candidate)) continue;
25820
+ written.set(normalize3(candidate), entry);
25821
+ }
25822
+ }
25823
+ return issues;
25824
+ }
25825
+ var TEMP_ROOTS, SCOPED_MARKERS, WRITE_PATTERNS, READ_PATTERNS;
25826
+ var init_shared_temp_path = __esm({
25827
+ "src/core/checks/session/shared-temp-path.ts"() {
25828
+ "use strict";
25829
+ init_define_WEB_FIRST_SEGMENTS();
25830
+ init_transcript();
25831
+ TEMP_ROOTS = [
25832
+ "/tmp/",
25833
+ "/var/tmp/",
25834
+ "$TMPDIR/",
25835
+ "${TMPDIR}/",
25836
+ "%TEMP%\\",
25837
+ "%TMP%\\"
25838
+ ];
25839
+ SCOPED_MARKERS = [
25840
+ "$$",
25841
+ "$pid",
25842
+ "${pid}",
25843
+ "mktemp",
25844
+ "$random",
25845
+ "${random}",
25846
+ "$session",
25847
+ "${session}",
25848
+ "$uuid",
25849
+ "${uuid}"
25850
+ ];
25851
+ WRITE_PATTERNS = [
25852
+ />\s*(\S+)/,
25853
+ // shell redirect
25854
+ /\bcp\s+\S+\s+(\S+)/,
25855
+ // cp src dest
25856
+ /\bmv\s+\S+\s+(\S+)/,
25857
+ /\bcopy\s+\S+\s+(\S+)/,
25858
+ /\bmove\s+\S+\s+(\S+)/,
25859
+ /\btee\s+(\S+)/,
25860
+ /writeFileSync\(\s*['"`]([^'"`]+)/,
25861
+ /\bcurl\b[^|]*-o\s+(\S+)/
25862
+ ];
25863
+ READ_PATTERNS = [
25864
+ /\bcp\s+(\S+)\s+\S+/,
25865
+ /\bcopy\s+(\S+)\s+\S+/,
25866
+ /\bcat\s+(\S+)/,
25867
+ /\btype\s+(\S+)/,
25868
+ /\bsource\s+(\S+)/,
25869
+ /\breadFileSync\(\s*['"`]([^'"`]+)/,
25870
+ // Input redirect. `(?!<)` excludes heredocs (`<<'EOF'`), and the leading
25871
+ // boundary plus the restricted character class keep it from matching a `<`
25872
+ // that is part of a comparison, an HTML fragment, or another operator --
25873
+ // a bare /<\s*(\S+)/ matched unrelated text in real transcripts.
25874
+ /(?:^|\s)<(?!<)\s*([^\s<>|&;]+)/
25875
+ ];
25876
+ }
25877
+ });
25878
+
25879
+ // src/core/checks/session/unverified-gate-claimed-clean.ts
25880
+ function gateOf(cmd) {
25881
+ for (const { re: re2, gate } of GATE_PATTERNS) {
25882
+ if (re2.test(cmd)) return gate;
25883
+ }
25884
+ return null;
25885
+ }
25886
+ function isUnverified(ev) {
25887
+ return ev.isError === true || ev.emptyOutput === true;
25888
+ }
25889
+ async function checkUnverifiedGateClaimedClean(ctx) {
25890
+ const { events } = await readProjectTranscript(ctx.currentProject);
25891
+ if (events.length === 0) return [];
25892
+ const ordered = [...events].sort((a, b2) => a.timestamp - b2.timestamp);
25893
+ const issues = [];
25894
+ const reported = /* @__PURE__ */ new Set();
25895
+ for (let i2 = 0; i2 < ordered.length; i2++) {
25896
+ const ev = ordered[i2];
25897
+ if (ev.kind !== "command") continue;
25898
+ const gate = gateOf(ev.text);
25899
+ if (!gate || !isUnverified(ev)) continue;
25900
+ for (let j3 = i2 + 1; j3 < ordered.length && j3 <= i2 + ADJACENCY; j3++) {
25901
+ const next = ordered[j3];
25902
+ if (next.kind === "command" && gateOf(next.text) === gate) break;
25903
+ if (next.kind !== "assistant-text") continue;
25904
+ if (HONEST_MARKERS.some((re2) => re2.test(next.text))) break;
25905
+ if (!CLEAN_CLAIMS.some((re2) => re2.test(next.text))) continue;
25906
+ if (reported.has(gate)) break;
25907
+ reported.add(gate);
25908
+ const why = ev.isError ? "the invocation reported an error" : "the invocation produced no output";
25909
+ issues.push({
25910
+ severity: "warning",
25911
+ check: "session-unverified-gate-claimed-clean",
25912
+ ruleId: "session-unverified-gate-claimed-clean/unverified-gate-claimed-clean",
25913
+ line: 0,
25914
+ message: `'${gate}' asserted as passing, but ${why}`,
25915
+ detail: `Gate command: ${ev.text.trim().slice(0, 120)}
25916
+ Claim: ${next.text.trim().slice(0, 120)}`,
25917
+ suggestion: 'An empty or failed gate carries no information about cleanliness -- a runner that dies before emitting diagnostics looks exactly like one that found none. Either re-run the gate until it produces a real result, or report the state honestly ("lint UNVERIFIED -- runner crashed").'
25918
+ });
25919
+ break;
25920
+ }
25921
+ }
25922
+ return issues;
25923
+ }
25924
+ var GATE_PATTERNS, CLEAN_CLAIMS, HONEST_MARKERS, ADJACENCY;
25925
+ var init_unverified_gate_claimed_clean = __esm({
25926
+ "src/core/checks/session/unverified-gate-claimed-clean.ts"() {
25927
+ "use strict";
25928
+ init_define_WEB_FIRST_SEGMENTS();
25929
+ init_transcript();
25930
+ GATE_PATTERNS = [
25931
+ { re: /\b(biome|eslint|ruff|clippy)\b/, gate: "lint" },
25932
+ { re: /\blint(:fix)?\b/, gate: "lint" },
25933
+ { re: /\btsc\b|\btypecheck\b|\btype-check\b/, gate: "typecheck" },
25934
+ { re: /\b(vitest|jest|pytest|mocha)\b/, gate: "test" },
25935
+ { re: /\btest\b/, gate: "test" },
25936
+ { re: /\bbuild\b/, gate: "build" }
25937
+ ];
25938
+ CLEAN_CLAIMS = [
25939
+ /\ball green\b/i,
25940
+ /\bno (lint )?(violations|errors|issues|diagnostics)\b/i,
25941
+ /\b0 errors\b/i,
25942
+ /\bzero (errors|violations|diagnostics)\b/i,
25943
+ /\b(lint|typecheck|type-check|tests?|build)\b[^.\n]{0,40}\b(clean|passe[sd]|passing|green|succeeded)\b/i,
25944
+ /\b(clean|passing|green)\b[^.\n]{0,25}\b(lint|typecheck|type-check|tests?|build)\b/i
25945
+ ];
25946
+ HONEST_MARKERS = [
25947
+ /\bunverified\b/i,
25948
+ /\bcould not verify\b/i,
25949
+ /\bcannot verify\b/i,
25950
+ /\bunable to verify\b/i,
25951
+ /\bcrashed?\b/i,
25952
+ /\bsegfault/i,
25953
+ /\bblocked\b/i,
25954
+ /\bnot trustworthy\b/i,
25955
+ /\bno signal\b/i,
25956
+ /\bdid not run\b/i,
25957
+ /\binconclusive\b/i
25958
+ ];
25959
+ ADJACENCY = 12;
25960
+ }
25961
+ });
25962
+
25963
+ // src/core/checks/session/default-branch-accumulation.ts
25964
+ function isCommit(cmd) {
25965
+ return /\bgit\s+commit\b/.test(cmd) && !/--dry-run\b/.test(cmd);
25966
+ }
25967
+ function isBranchAway(cmd) {
25968
+ return /\bgit\s+(checkout\s+-b|switch\s+-c|worktree\s+add)\b/.test(cmd);
25969
+ }
25970
+ async function checkDefaultBranchAccumulation(ctx) {
25971
+ const { events } = await readProjectTranscript(ctx.currentProject);
25972
+ if (events.length === 0) return [];
25973
+ const ordered = [...events].sort((a, b2) => a.timestamp - b2.timestamp);
25974
+ const pending = /* @__PURE__ */ new Set();
25975
+ let branch = "";
25976
+ let firstWrite = "";
25977
+ for (const ev of ordered) {
25978
+ if (ev.gitBranch) branch = ev.gitBranch;
25979
+ if (ev.kind === "command") {
25980
+ if (isCommit(ev.text) || isBranchAway(ev.text)) {
25981
+ pending.clear();
25982
+ firstWrite = "";
25983
+ }
25984
+ continue;
25985
+ }
25986
+ if (ev.kind !== "file-write") continue;
25987
+ if (!DEFAULT_BRANCHES.has(branch)) continue;
25988
+ if (pending.size === 0) firstWrite = ev.text;
25989
+ pending.add(ev.text);
25990
+ }
25991
+ if (pending.size < THRESHOLD) return [];
25992
+ const sample = [...pending].slice(0, 5);
25993
+ return [
25994
+ {
25995
+ severity: "warning",
25996
+ check: "session-default-branch-accumulation",
25997
+ ruleId: "session-default-branch-accumulation/default-branch-accumulation",
25998
+ line: 0,
25999
+ message: `${pending.size} files edited on '${branch}' with no intervening commit`,
26000
+ detail: `First uncommitted write: ${firstWrite}
26001
+ Sample: ${sample.join(", ")}${pending.size > sample.length ? ", ..." : ""}`,
26002
+ suggestion: "Commit as you go, or move the work to a topic branch (`git checkout -b`). A large uncommitted delta on a shared default branch is hard to review and, when other agent sessions share the checkout, one `git stash` or `git checkout --` away from being lost."
26003
+ }
26004
+ ];
26005
+ }
26006
+ var DEFAULT_BRANCHES, THRESHOLD;
26007
+ var init_default_branch_accumulation = __esm({
26008
+ "src/core/checks/session/default-branch-accumulation.ts"() {
26009
+ "use strict";
26010
+ init_define_WEB_FIRST_SEGMENTS();
26011
+ init_transcript();
26012
+ DEFAULT_BRANCHES = /* @__PURE__ */ new Set(["main", "master"]);
26013
+ THRESHOLD = 10;
26014
+ }
26015
+ });
26016
+
26017
+ // src/core/checks/session/unresolvable-sha.ts
26018
+ function stripFences(text) {
26019
+ const lines = text.split("\n");
26020
+ let inFence = false;
26021
+ return lines.map((line) => {
26022
+ if (line.trimStart().startsWith("```")) {
26023
+ inFence = !inFence;
26024
+ return "";
26025
+ }
26026
+ return inFence ? "" : line;
26027
+ }).join("\n");
26028
+ }
26029
+ function extractShaCitations(text) {
26030
+ const out = [];
26031
+ const seen = /* @__PURE__ */ new Set();
26032
+ for (const rawLine of stripFences(text).split("\n")) {
26033
+ const line = rawLine.replace(UUID, " ");
26034
+ HEX_TOKEN.lastIndex = 0;
26035
+ for (const m of line.matchAll(HEX_TOKEN)) {
26036
+ const sha = m[0];
26037
+ const start = m.index ?? 0;
26038
+ const before = line.slice(0, start);
26039
+ const after = line.slice(start + sha.length);
26040
+ if (/^\d+$/.test(sha)) continue;
26041
+ if (/[#]$/.test(before) || /0x$/i.test(before)) continue;
26042
+ if (DIGEST_PREFIX.test(before)) continue;
26043
+ if (/[-.]$/.test(before) && /[\w]$/.test(before.slice(0, -1))) continue;
26044
+ if (/^[-.][0-9a-z]/i.test(after)) continue;
26045
+ if (/[/\\]$/.test(before) && /^[/\\]/.test(after)) continue;
26046
+ const window2 = before.slice(-CUE_WINDOW);
26047
+ if (!CITATION_CUE.test(window2)) continue;
26048
+ if (seen.has(sha)) continue;
26049
+ seen.add(sha);
26050
+ out.push({ sha, context: rawLine.trim().slice(0, 160) });
26051
+ }
26052
+ }
26053
+ return out;
26054
+ }
26055
+ async function checkUnresolvableSha(ctx) {
26056
+ const projectMemories = ctx.memories.filter(
26057
+ (m) => projectDirMatchesPath(m.projectDir, ctx.currentProject)
26058
+ );
26059
+ if (projectMemories.length === 0) return [];
26060
+ if (!await isGitRepo(ctx.currentProject)) return [];
26061
+ const git = getGit(ctx.currentProject);
26062
+ const resolvedCache = /* @__PURE__ */ new Map();
26063
+ let budget = MAX_RESOLVE;
26064
+ async function resolves(sha) {
26065
+ const cached2 = resolvedCache.get(sha);
26066
+ if (cached2 !== void 0) return cached2;
26067
+ if (budget <= 0) return null;
26068
+ budget--;
26069
+ let ok;
26070
+ try {
26071
+ const type = await git.raw(["cat-file", "-t", sha]);
26072
+ ok = type.trim().length > 0;
26073
+ } catch {
26074
+ ok = false;
26075
+ }
26076
+ resolvedCache.set(sha, ok);
26077
+ return ok;
26078
+ }
26079
+ const issues = [];
26080
+ for (const mem of projectMemories) {
26081
+ const dead = [];
26082
+ for (const cite of extractShaCitations(mem.content)) {
26083
+ const ok = await resolves(cite.sha);
26084
+ if (ok === false) dead.push(cite);
26085
+ }
26086
+ if (dead.length === 0) continue;
26087
+ const name = mem.name || mem.filePath.split(/[/\\]/).pop() || "unknown";
26088
+ const list = dead.map((d) => d.sha).join(", ");
26089
+ issues.push({
26090
+ severity: "warning",
26091
+ check: "session-unresolvable-sha",
26092
+ ruleId: "session-unresolvable-sha/unresolvable-sha",
26093
+ line: 0,
26094
+ message: `Memory "${name}" cites ${dead.length} commit(s) that do not resolve in this repository: ${list}`,
26095
+ detail: dead.map((d) => `${d.sha}: ${d.context}`).join("\n"),
26096
+ suggestion: `Re-resolve the citation and update ${mem.filePath}. A squash-merge or rebase rewrites SHAs, so a memory written before the merge points at a commit that no longer exists -- cite the landed commit, or the PR number, which survives the rewrite.`
26097
+ });
26098
+ }
26099
+ return issues;
26100
+ }
26101
+ var HEX_TOKEN, UUID, CITATION_CUE, CUE_WINDOW, DIGEST_PREFIX, MAX_RESOLVE;
26102
+ var init_unresolvable_sha = __esm({
26103
+ "src/core/checks/session/unresolvable-sha.ts"() {
26104
+ "use strict";
26105
+ init_define_WEB_FIRST_SEGMENTS();
26106
+ init_git();
26107
+ init_session_parser();
26108
+ HEX_TOKEN = /\b[0-9a-f]{7,40}\b/g;
26109
+ UUID = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi;
26110
+ CITATION_CUE = /\b(commit|commits|committed|sha|revision|revs?|git|head|tag|tagged|branch|pr|landed|merged|shipped|introduced|reverted|cherry-?picked|backported|fixed|removed|added|renamed|bumped|released)\b/i;
26111
+ CUE_WINDOW = 80;
26112
+ DIGEST_PREFIX = /(sha\d{3}|md5|blake\d*|crc\d*)\s*[:=]\s*$/i;
26113
+ MAX_RESOLVE = 40;
26114
+ }
26115
+ });
26116
+
26117
+ // src/core/checks/ci-coverage.ts
26118
+ import { readdir as readdir4, readFile as readFile4 } from "node:fs/promises";
26119
+ import { join as join10 } from "node:path";
26120
+ import { existsSync as existsSync6 } from "node:fs";
25025
26121
  async function findReleaseWorkflows(projectRoot) {
25026
- const workflowDir = join9(projectRoot, ".github", "workflows");
25027
- if (!existsSync5(workflowDir)) return [];
26122
+ const workflowDir = join10(projectRoot, ".github", "workflows");
26123
+ if (!existsSync6(workflowDir)) return [];
25028
26124
  let files;
25029
26125
  try {
25030
- files = await readdir3(workflowDir);
26126
+ files = await readdir4(workflowDir);
25031
26127
  } catch {
25032
26128
  return [];
25033
26129
  }
@@ -25039,7 +26135,7 @@ async function findReleaseWorkflows(projectRoot) {
25039
26135
  continue;
25040
26136
  }
25041
26137
  try {
25042
- const content = stripBom(await readFile4(join9(workflowDir, f), "utf-8"));
26138
+ const content = stripBom(await readFile4(join10(workflowDir, f), "utf-8"));
25043
26139
  const nameMatch = content.match(/^\s*name:\s*(.+?)\s*$/m);
25044
26140
  if (nameMatch) {
25045
26141
  const name = nameMatch[1].replace(/^(['"])(.*)\1$/, "$2");
@@ -25098,15 +26194,15 @@ var init_ci_coverage = __esm({
25098
26194
  });
25099
26195
 
25100
26196
  // src/core/checks/ci-secrets.ts
25101
- import { readdir as readdir4, readFile as readFile5 } from "node:fs/promises";
25102
- import { join as join10 } from "node:path";
25103
- import { existsSync as existsSync6 } from "node:fs";
26197
+ import { readdir as readdir5, readFile as readFile5 } from "node:fs/promises";
26198
+ import { join as join11 } from "node:path";
26199
+ import { existsSync as existsSync7 } from "node:fs";
25104
26200
  async function findSecretUsages(projectRoot) {
25105
- const workflowDir = join10(projectRoot, ".github", "workflows");
25106
- if (!existsSync6(workflowDir)) return [];
26201
+ const workflowDir = join11(projectRoot, ".github", "workflows");
26202
+ if (!existsSync7(workflowDir)) return [];
25107
26203
  let files;
25108
26204
  try {
25109
- files = await readdir4(workflowDir);
26205
+ files = await readdir5(workflowDir);
25110
26206
  } catch {
25111
26207
  return [];
25112
26208
  }
@@ -25115,7 +26211,7 @@ async function findSecretUsages(projectRoot) {
25115
26211
  if (!(f.endsWith(".yml") || f.endsWith(".yaml"))) continue;
25116
26212
  let content;
25117
26213
  try {
25118
- content = stripBom(await readFile5(join10(workflowDir, f), "utf-8"));
26214
+ content = stripBom(await readFile5(join11(workflowDir, f), "utf-8"));
25119
26215
  } catch {
25120
26216
  continue;
25121
26217
  }
@@ -25429,26 +26525,26 @@ var init_positions = __esm({
25429
26525
  });
25430
26526
 
25431
26527
  // src/core/checks/hook-coverage.ts
25432
- import * as fs7 from "node:fs";
26528
+ import * as fs8 from "node:fs";
25433
26529
  import * as os3 from "node:os";
25434
- import * as path10 from "node:path";
26530
+ import * as path11 from "node:path";
25435
26531
  function loadSettingsSources2(projectRoot, homeDir, includeUserGlobal) {
25436
26532
  const home2 = homeDir;
25437
26533
  const candidates = [
25438
26534
  {
25439
- filePath: path10.join(projectRoot, ".claude", "settings.json"),
25440
- displayPath: path10.join(".claude", "settings.json"),
26535
+ filePath: path11.join(projectRoot, ".claude", "settings.json"),
26536
+ displayPath: path11.join(".claude", "settings.json"),
25441
26537
  isUserGlobal: false
25442
26538
  },
25443
26539
  {
25444
- filePath: path10.join(projectRoot, ".claude", "settings.local.json"),
25445
- displayPath: path10.join(".claude", "settings.local.json"),
26540
+ filePath: path11.join(projectRoot, ".claude", "settings.local.json"),
26541
+ displayPath: path11.join(".claude", "settings.local.json"),
25446
26542
  isUserGlobal: false
25447
26543
  }
25448
26544
  ];
25449
26545
  if (includeUserGlobal) {
25450
26546
  candidates.push({
25451
- filePath: path10.join(home2, ".claude", "settings.json"),
26547
+ filePath: path11.join(home2, ".claude", "settings.json"),
25452
26548
  displayPath: "~/.claude/settings.json",
25453
26549
  isUserGlobal: true
25454
26550
  });
@@ -25457,7 +26553,7 @@ function loadSettingsSources2(projectRoot, homeDir, includeUserGlobal) {
25457
26553
  for (const c3 of candidates) {
25458
26554
  let content;
25459
26555
  try {
25460
- content = stripBom(fs7.readFileSync(c3.filePath, "utf-8"));
26556
+ content = stripBom(fs8.readFileSync(c3.filePath, "utf-8"));
25461
26557
  } catch {
25462
26558
  continue;
25463
26559
  }
@@ -25469,11 +26565,11 @@ function loadSettingsSources2(projectRoot, homeDir, includeUserGlobal) {
25469
26565
  function expandPath(raw, projectRoot, homeDir, platform) {
25470
26566
  let s = raw;
25471
26567
  if (s === "~" || s.startsWith("~/") || s.startsWith("~\\")) {
25472
- s = path10.join(homeDir, s.slice(1));
26568
+ s = path11.join(homeDir, s.slice(1));
25473
26569
  }
25474
26570
  const replacements = {
25475
26571
  CLAUDE_PROJECT_DIR: projectRoot,
25476
- CLAUDE_CONFIG_DIR: path10.join(homeDir, ".claude"),
26572
+ CLAUDE_CONFIG_DIR: path11.join(homeDir, ".claude"),
25477
26573
  HOME: homeDir,
25478
26574
  USERPROFILE: homeDir
25479
26575
  };
@@ -25488,7 +26584,7 @@ function expandPath(raw, projectRoot, homeDir, platform) {
25488
26584
  if (/\$\{?[A-Za-z_]/.test(s)) return null;
25489
26585
  if (/%[A-Za-z_][A-Za-z0-9_]*%/.test(s)) return null;
25490
26586
  s = translateMsysDrivePath(s, platform);
25491
- if (!path10.isAbsolute(s)) s = path10.resolve(projectRoot, s);
26587
+ if (!path11.isAbsolute(s)) s = path11.resolve(projectRoot, s);
25492
26588
  return s;
25493
26589
  }
25494
26590
  function looksLikePath(token) {
@@ -25504,9 +26600,24 @@ function isMsysFlag(token) {
25504
26600
  }
25505
26601
  function translateMsysDrivePath(s, platform) {
25506
26602
  if (platform !== "win32") return s;
25507
- const m = /^\/([A-Za-z])\/(.*)$/.exec(s);
26603
+ const m = /^\/{1,2}([A-Za-z])\/(.*)$/.exec(s);
25508
26604
  return m ? `${m[1]}:/${m[2]}` : s;
25509
26605
  }
26606
+ function findExistingVariant(raw, projectRoot, homeDir, platform) {
26607
+ const token = stripMatcherWildcard(raw);
26608
+ const variants = /* @__PURE__ */ new Set();
26609
+ if (/^\/{2}[A-Za-z]\//.test(token)) variants.add(token.slice(1));
26610
+ if (/^\/[A-Za-z]\//.test(token)) variants.add(`/${token}`);
26611
+ if (token.startsWith("/") && !/^\/{1,2}[A-Za-z]\//.test(token)) {
26612
+ variants.add(token.replace(/^\/+/, ""));
26613
+ }
26614
+ if (token.includes("\\")) variants.add(token.replace(/\\/g, "/"));
26615
+ for (const v2 of variants) {
26616
+ const resolved = expandPath(v2, projectRoot, homeDir, platform);
26617
+ if (resolved !== null && fileExists(resolved)) return resolved;
26618
+ }
26619
+ return null;
26620
+ }
25510
26621
  function extractCommandPaths(command, projectRoot, homeDir, platform) {
25511
26622
  const tokens = command.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
25512
26623
  const out = [];
@@ -25539,6 +26650,18 @@ function checkSource(source, projectRoot, homeDir, platform) {
25539
26650
  if (cand.resolved === null) return;
25540
26651
  if (fileExists(cand.resolved)) return;
25541
26652
  const where = source.isUserGlobal ? ` (in ${source.displayPath})` : "";
26653
+ const variant = findExistingVariant(cand.raw, projectRoot, homeDir, platform);
26654
+ if (variant !== null) {
26655
+ issues.push({
26656
+ severity: "warning",
26657
+ check: "hook-coverage",
26658
+ ruleId: "hook-coverage/dead-hook",
26659
+ line,
26660
+ message: `${origin} references "${cand.raw}" which does not resolve${where}, but "${variant}" exists \u2014 the path form is wrong, so the gate silently no-ops`,
26661
+ suggestion: `Rewrite the entry to use "${variant}". Do NOT delete it \u2014 the target exists; only the path form is unresolvable.`
26662
+ });
26663
+ return;
26664
+ }
25542
26665
  issues.push({
25543
26666
  severity: "warning",
25544
26667
  check: "hook-coverage",
@@ -25599,19 +26722,19 @@ var init_hook_coverage = __esm({
25599
26722
  });
25600
26723
 
25601
26724
  // src/core/skill-scanner.ts
25602
- import * as fs8 from "node:fs";
26725
+ import * as fs9 from "node:fs";
25603
26726
  import * as os4 from "node:os";
25604
- import * as path11 from "node:path";
26727
+ import * as path12 from "node:path";
25605
26728
  function defaultHome() {
25606
26729
  return process.env.HOME || process.env.USERPROFILE || os4.homedir();
25607
26730
  }
25608
26731
  function scanSkillFiles(homeDir = defaultHome()) {
25609
26732
  const files = [];
25610
26733
  const orphanedSkillDirs = [];
25611
- const claudeDir = path11.join(homeDir, ".claude");
25612
- const skillsRoot = path11.join(claudeDir, "skills");
26734
+ const claudeDir = path12.join(homeDir, ".claude");
26735
+ const skillsRoot = path12.join(claudeDir, "skills");
25613
26736
  for (const name of safeReadDir(skillsRoot, "dir")) {
25614
- const skillMd = path11.join(skillsRoot, name, "SKILL.md");
26737
+ const skillMd = path12.join(skillsRoot, name, "SKILL.md");
25615
26738
  const content = safeRead(skillMd);
25616
26739
  if (content === null) {
25617
26740
  orphanedSkillDirs.push({ name, displayPath: `~/.claude/skills/${name}/` });
@@ -25625,10 +26748,10 @@ function scanSkillFiles(homeDir = defaultHome()) {
25625
26748
  content
25626
26749
  });
25627
26750
  }
25628
- const agentsRoot = path11.join(claudeDir, "agents");
26751
+ const agentsRoot = path12.join(claudeDir, "agents");
25629
26752
  for (const fileName of safeReadDir(agentsRoot, "file")) {
25630
26753
  if (!fileName.endsWith(".md")) continue;
25631
- const agentMd = path11.join(agentsRoot, fileName);
26754
+ const agentMd = path12.join(agentsRoot, fileName);
25632
26755
  const content = safeRead(agentMd);
25633
26756
  if (content === null) continue;
25634
26757
  files.push({
@@ -25644,7 +26767,7 @@ function scanSkillFiles(homeDir = defaultHome()) {
25644
26767
  function safeReadDir(dir, want) {
25645
26768
  let entries;
25646
26769
  try {
25647
- entries = fs8.readdirSync(dir, { withFileTypes: true });
26770
+ entries = fs9.readdirSync(dir, { withFileTypes: true });
25648
26771
  } catch {
25649
26772
  return [];
25650
26773
  }
@@ -25652,7 +26775,7 @@ function safeReadDir(dir, want) {
25652
26775
  if (want === "dir" ? e.isDirectory() : e.isFile()) return true;
25653
26776
  if (!e.isSymbolicLink()) return false;
25654
26777
  try {
25655
- const target = fs8.statSync(path11.join(dir, e.name));
26778
+ const target = fs9.statSync(path12.join(dir, e.name));
25656
26779
  return want === "dir" ? target.isDirectory() : target.isFile();
25657
26780
  } catch {
25658
26781
  return false;
@@ -25661,7 +26784,7 @@ function safeReadDir(dir, want) {
25661
26784
  }
25662
26785
  function safeRead(file2) {
25663
26786
  try {
25664
- return stripBom(fs8.readFileSync(file2, "utf-8"));
26787
+ return stripBom(fs9.readFileSync(file2, "utf-8"));
25665
26788
  } catch {
25666
26789
  return null;
25667
26790
  }
@@ -25675,7 +26798,7 @@ var init_skill_scanner = __esm({
25675
26798
  });
25676
26799
 
25677
26800
  // src/core/checks/skills.ts
25678
- import * as path12 from "node:path";
26801
+ import * as path13 from "node:path";
25679
26802
  function parseFrontmatter3(content) {
25680
26803
  const lines = content.split("\n");
25681
26804
  if (lines[0]?.trim() !== "---") {
@@ -25752,7 +26875,7 @@ function checkMissingFrontmatter(file2, fm) {
25752
26875
  }
25753
26876
  function checkBrokenRefs(file2, fmEndLine) {
25754
26877
  const issues = [];
25755
- const skillDir = path12.dirname(file2.filePath);
26878
+ const skillDir = path13.dirname(file2.filePath);
25756
26879
  const lines = file2.content.split("\n");
25757
26880
  let inCode = false;
25758
26881
  let codeLang = "";
@@ -25772,7 +26895,7 @@ function checkBrokenRefs(file2, fmEndLine) {
25772
26895
  if (PATH_SKIP.test(value)) continue;
25773
26896
  if (!value.startsWith("./") && !value.startsWith("../")) continue;
25774
26897
  while (/[.,;:]$/.test(value)) value = value.slice(0, -1);
25775
- const resolved = path12.resolve(skillDir, value);
26898
+ const resolved = path13.resolve(skillDir, value);
25776
26899
  if (fileExists(resolved) || isDirectory(resolved)) continue;
25777
26900
  issues.push({
25778
26901
  severity: "warning",
@@ -26280,8 +27403,8 @@ var require_utils = __commonJS({
26280
27403
  }
26281
27404
  return output;
26282
27405
  };
26283
- exports.basename = (path20, { windows } = {}) => {
26284
- const segs = path20.split(windows ? /[\\/]/ : "/");
27406
+ exports.basename = (path21, { windows } = {}) => {
27407
+ const segs = path21.split(windows ? /[\\/]/ : "/");
26285
27408
  const last2 = segs[segs.length - 1];
26286
27409
  if (last2 === "") {
26287
27410
  return segs[segs.length - 2];
@@ -27780,16 +28903,16 @@ var require_picomatch2 = __commonJS({
27780
28903
  });
27781
28904
 
27782
28905
  // src/core/ignore-file.ts
27783
- import fs9 from "node:fs";
27784
- import path13 from "node:path";
28906
+ import fs10 from "node:fs";
28907
+ import path14 from "node:path";
27785
28908
  function matchesGlob(filePath, glob) {
27786
28909
  return (0, import_picomatch.default)(glob, { dot: true })(filePath);
27787
28910
  }
27788
28911
  function loadIgnoreFile(projectRoot) {
27789
- const ignoreFilePath = path13.join(projectRoot, ".ctxlintignore");
28912
+ const ignoreFilePath = path14.join(projectRoot, ".ctxlintignore");
27790
28913
  let content;
27791
28914
  try {
27792
- content = fs9.readFileSync(ignoreFilePath, "utf-8");
28915
+ content = fs10.readFileSync(ignoreFilePath, "utf-8");
27793
28916
  } catch {
27794
28917
  return [];
27795
28918
  }
@@ -27835,15 +28958,15 @@ var init_types = __esm({
27835
28958
  });
27836
28959
 
27837
28960
  // src/version.ts
27838
- import { readFileSync as readFileSync7 } from "node:fs";
27839
- import { resolve as resolve14, dirname as dirname6 } from "node:path";
28961
+ import { readFileSync as readFileSync8 } from "node:fs";
28962
+ import { resolve as resolve15, dirname as dirname7 } from "node:path";
27840
28963
  import { fileURLToPath as fileURLToPath2 } from "node:url";
27841
28964
  function loadVersion() {
27842
- if (true) return "0.18.6";
28965
+ if (true) return "0.19.0";
27843
28966
  try {
27844
- const __dir = dirname6(fileURLToPath2(import.meta.url));
27845
- const pkgPath = resolve14(__dir, "../package.json");
27846
- const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
28967
+ const __dir = dirname7(fileURLToPath2(import.meta.url));
28968
+ const pkgPath = resolve15(__dir, "../package.json");
28969
+ const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
27847
28970
  return pkg.version ?? "0.0.0-dev";
27848
28971
  } catch {
27849
28972
  return "0.0.0-dev";
@@ -27859,9 +28982,9 @@ var init_version2 = __esm({
27859
28982
  });
27860
28983
 
27861
28984
  // src/core/audit.ts
27862
- import * as fs10 from "node:fs";
28985
+ import * as fs11 from "node:fs";
27863
28986
  import * as os5 from "node:os";
27864
- import * as path14 from "node:path";
28987
+ import * as path15 from "node:path";
27865
28988
  function hasMcpChecks(checks) {
27866
28989
  return checks.some((c3) => c3.startsWith("mcp-"));
27867
28990
  }
@@ -27897,7 +29020,7 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
27897
29020
  const absPath = discoveredFile.absolutePath;
27898
29021
  let stat3 = null;
27899
29022
  try {
27900
- const s = fs10.statSync(absPath);
29023
+ const s = fs11.statSync(absPath);
27901
29024
  stat3 = { mtimeMs: s.mtimeMs, size: s.size };
27902
29025
  } catch {
27903
29026
  }
@@ -27910,7 +29033,8 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
27910
29033
  const parseResult = parseContextFile(discoveredFile);
27911
29034
  parsed.push(parseResult);
27912
29035
  const checkPromises = [];
27913
- if (activeChecks.includes("paths")) checkPromises.push(checkPaths(parseResult, projectRoot));
29036
+ if (activeChecks.includes("paths"))
29037
+ checkPromises.push(checkPaths(parseResult, projectRoot));
27914
29038
  if (activeChecks.includes("commands"))
27915
29039
  checkPromises.push(checkCommands(parseResult, projectRoot));
27916
29040
  if (activeChecks.includes("staleness"))
@@ -27928,7 +29052,8 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
27928
29052
  if (activeChecks.includes("content-secrets"))
27929
29053
  checkPromises.push(checkContentSecrets(parseResult, projectRoot));
27930
29054
  const results = await Promise.all(checkPromises);
27931
- const singleFileIssues = results.flat();
29055
+ const suppressions = collectSuppressions(parseResult.content);
29056
+ const singleFileIssues = results.flat().filter((issue2) => !isSuppressed(suppressions, issue2));
27932
29057
  if (stat3 !== null) {
27933
29058
  setCacheEntry(absPath, {
27934
29059
  mtime: stat3.mtimeMs,
@@ -28077,6 +29202,14 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
28077
29202
  sessionPromises.push(checkLoopDetection(sessionCtx));
28078
29203
  if (sessionChecksToRun.includes("session-memory-index-overflow"))
28079
29204
  sessionPromises.push(checkMemoryIndexOverflow(sessionCtx));
29205
+ if (sessionChecksToRun.includes("session-shared-temp-path"))
29206
+ sessionPromises.push(checkSharedTempPath(sessionCtx));
29207
+ if (sessionChecksToRun.includes("session-unverified-gate-claimed-clean"))
29208
+ sessionPromises.push(checkUnverifiedGateClaimedClean(sessionCtx));
29209
+ if (sessionChecksToRun.includes("session-default-branch-accumulation"))
29210
+ sessionPromises.push(checkDefaultBranchAccumulation(sessionCtx));
29211
+ if (sessionChecksToRun.includes("session-unresolvable-sha"))
29212
+ sessionPromises.push(checkUnresolvableSha(sessionCtx));
28080
29213
  const sessionResults = await Promise.all(sessionPromises);
28081
29214
  const sessionIssues = sessionResults.flat();
28082
29215
  fileResults.push({
@@ -28138,10 +29271,7 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
28138
29271
  globUnusedRules = globScopedRules.filter((r2) => !firedGlobRules.has(r2)).map(({ _fileGlob: _unused, ...rest }) => rest);
28139
29272
  globRulesMissingReason = globScopedRules.filter((r2) => !r2.reason).map(({ _fileGlob: _unused, ...rest }) => rest);
28140
29273
  }
28141
- const allIgnoreRules = [
28142
- ...options.ignoreRules ?? [],
28143
- ...globalFileRules
28144
- ];
29274
+ const allIgnoreRules = [...options.ignoreRules ?? [], ...globalFileRules];
28145
29275
  let ignoreReport;
28146
29276
  if (allIgnoreRules.length > 0 || globDropped > 0) {
28147
29277
  let flatDropped = 0;
@@ -28215,29 +29345,33 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
28215
29345
  return result;
28216
29346
  }
28217
29347
  async function runAuditOnContent(filePath, content, projectRoot, options = {}) {
28218
- const tmpDir = fs10.mkdtempSync(path14.join(os5.tmpdir(), "ctxlint-lsp-"));
28219
- const ext = path14.extname(filePath) || ".md";
28220
- const tmpFile = path14.join(tmpDir, "content" + ext);
29348
+ const tmpDir = fs11.mkdtempSync(path15.join(os5.tmpdir(), "ctxlint-lsp-"));
29349
+ const ext = path15.extname(filePath) || ".md";
29350
+ const tmpFile = path15.join(tmpDir, "content" + ext);
28221
29351
  try {
28222
- fs10.writeFileSync(tmpFile, content, "utf-8");
28223
- const tmpRelative = path14.relative(tmpDir, tmpFile);
28224
- const rawResult = await runAudit(tmpDir, options.depth !== void 0 ? ALL_CHECKS : ALL_CHECKS, {
28225
- ...options,
28226
- extraPatterns: [tmpRelative],
28227
- // Disable cross-project checks that need the real project tree.
28228
- session: false,
28229
- sessionOnly: false,
28230
- skills: false,
28231
- skillsOnly: false
28232
- });
29352
+ fs11.writeFileSync(tmpFile, content, "utf-8");
29353
+ const tmpRelative = path15.relative(tmpDir, tmpFile);
29354
+ const rawResult = await runAudit(
29355
+ tmpDir,
29356
+ options.depth !== void 0 ? ALL_CHECKS : ALL_CHECKS,
29357
+ {
29358
+ ...options,
29359
+ extraPatterns: [tmpRelative],
29360
+ // Disable cross-project checks that need the real project tree.
29361
+ session: false,
29362
+ sessionOnly: false,
29363
+ skills: false,
29364
+ skillsOnly: false
29365
+ }
29366
+ );
28233
29367
  const remapped = rawResult.files.map((fr) => {
28234
- const isTmp = fr.path === tmpRelative || fr.path === tmpFile || path14.resolve(tmpDir, fr.path) === tmpFile;
29368
+ const isTmp = fr.path === tmpRelative || fr.path === tmpFile || path15.resolve(tmpDir, fr.path) === tmpFile;
28235
29369
  return isTmp ? { ...fr, path: filePath } : fr;
28236
29370
  });
28237
29371
  return { ...rawResult, files: remapped, projectRoot };
28238
29372
  } finally {
28239
29373
  try {
28240
- fs10.rmSync(tmpDir, { recursive: true, force: true });
29374
+ fs11.rmSync(tmpDir, { recursive: true, force: true });
28241
29375
  } catch {
28242
29376
  }
28243
29377
  }
@@ -28255,6 +29389,7 @@ var init_audit = __esm({
28255
29389
  init_paths();
28256
29390
  init_commands();
28257
29391
  init_staleness();
29392
+ init_suppressions();
28258
29393
  init_tokens2();
28259
29394
  init_tier_tokens();
28260
29395
  init_redundancy();
@@ -28276,6 +29411,10 @@ var init_audit = __esm({
28276
29411
  init_duplicate_memory();
28277
29412
  init_loop_detection();
28278
29413
  init_memory_index_overflow();
29414
+ init_shared_temp_path();
29415
+ init_unverified_gate_claimed_clean();
29416
+ init_default_branch_accumulation();
29417
+ init_unresolvable_sha();
28279
29418
  init_ci_coverage();
28280
29419
  init_ci_secrets();
28281
29420
  init_content_secrets();
@@ -28317,7 +29456,11 @@ var init_audit = __esm({
28317
29456
  "session-stale-memory",
28318
29457
  "session-duplicate-memory",
28319
29458
  "session-loop-detection",
28320
- "session-memory-index-overflow"
29459
+ "session-memory-index-overflow",
29460
+ "session-shared-temp-path",
29461
+ "session-unverified-gate-claimed-clean",
29462
+ "session-default-branch-accumulation",
29463
+ "session-unresolvable-sha"
28321
29464
  ];
28322
29465
  ALL_SKILL_CHECKS = [
28323
29466
  "skill-frontmatter",
@@ -28334,7 +29477,7 @@ var server_exports = {};
28334
29477
  __export(server_exports, {
28335
29478
  startLspServer: () => startLspServer
28336
29479
  });
28337
- import * as path15 from "node:path";
29480
+ import * as path16 from "node:path";
28338
29481
  async function startLspServer() {
28339
29482
  const connection = (0, import_node.createConnection)(import_node.ProposedFeatures.all);
28340
29483
  const documents = new import_node.TextDocuments(TextDocument2);
@@ -28349,7 +29492,7 @@ async function startLspServer() {
28349
29492
  const uri = doc.uri;
28350
29493
  let filePath = uri.startsWith("file://") ? decodeURIComponent(uri.slice(7)) : uri;
28351
29494
  filePath = filePath.replace(/^\/([A-Za-z]:)/, "$1");
28352
- const projectRoot = path15.dirname(filePath);
29495
+ const projectRoot = path16.dirname(filePath);
28353
29496
  try {
28354
29497
  const result = await runAuditOnContent(filePath, doc.getText(), projectRoot, {
28355
29498
  depth: 2
@@ -28801,8 +29944,8 @@ var init_parseUtil = __esm({
28801
29944
  init_errors();
28802
29945
  init_en();
28803
29946
  makeIssue = (params) => {
28804
- const { data, path: path20, errorMaps, issueData } = params;
28805
- const fullPath = [...path20, ...issueData.path || []];
29947
+ const { data, path: path21, errorMaps, issueData } = params;
29948
+ const fullPath = [...path21, ...issueData.path || []];
28806
29949
  const fullIssue = {
28807
29950
  ...issueData,
28808
29951
  path: fullPath
@@ -29085,11 +30228,11 @@ var init_types2 = __esm({
29085
30228
  init_parseUtil();
29086
30229
  init_util2();
29087
30230
  ParseInputLazyPath = class {
29088
- constructor(parent, value, path20, key) {
30231
+ constructor(parent, value, path21, key) {
29089
30232
  this._cachedPath = [];
29090
30233
  this.parent = parent;
29091
30234
  this.data = value;
29092
- this._path = path20;
30235
+ this._path = path21;
29093
30236
  this._key = key;
29094
30237
  }
29095
30238
  get path() {
@@ -32591,10 +33734,10 @@ function mergeDefs(...defs) {
32591
33734
  function cloneDef(schema) {
32592
33735
  return mergeDefs(schema._zod.def);
32593
33736
  }
32594
- function getElementAtPath(obj, path20) {
32595
- if (!path20)
33737
+ function getElementAtPath(obj, path21) {
33738
+ if (!path21)
32596
33739
  return obj;
32597
- return path20.reduce((acc, key) => acc?.[key], obj);
33740
+ return path21.reduce((acc, key) => acc?.[key], obj);
32598
33741
  }
32599
33742
  function promiseAllObject(promisesObj) {
32600
33743
  const keys = Object.keys(promisesObj);
@@ -32922,11 +34065,11 @@ function explicitlyAborted(x3, startIndex = 0) {
32922
34065
  }
32923
34066
  return false;
32924
34067
  }
32925
- function prefixIssues(path20, issues) {
34068
+ function prefixIssues(path21, issues) {
32926
34069
  return issues.map((iss) => {
32927
34070
  var _a4;
32928
34071
  (_a4 = iss).path ?? (_a4.path = []);
32929
- iss.path.unshift(path20);
34072
+ iss.path.unshift(path21);
32930
34073
  return iss;
32931
34074
  });
32932
34075
  }
@@ -33144,16 +34287,16 @@ function flattenError(error52, mapper = (issue2) => issue2.message) {
33144
34287
  }
33145
34288
  function formatError(error52, mapper = (issue2) => issue2.message) {
33146
34289
  const fieldErrors = { _errors: [] };
33147
- const processError = (error53, path20 = []) => {
34290
+ const processError = (error53, path21 = []) => {
33148
34291
  for (const issue2 of error53.issues) {
33149
34292
  if (issue2.code === "invalid_union" && issue2.errors.length) {
33150
- issue2.errors.map((issues) => processError({ issues }, [...path20, ...issue2.path]));
34293
+ issue2.errors.map((issues) => processError({ issues }, [...path21, ...issue2.path]));
33151
34294
  } else if (issue2.code === "invalid_key") {
33152
- processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
34295
+ processError({ issues: issue2.issues }, [...path21, ...issue2.path]);
33153
34296
  } else if (issue2.code === "invalid_element") {
33154
- processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
34297
+ processError({ issues: issue2.issues }, [...path21, ...issue2.path]);
33155
34298
  } else {
33156
- const fullpath = [...path20, ...issue2.path];
34299
+ const fullpath = [...path21, ...issue2.path];
33157
34300
  if (fullpath.length === 0) {
33158
34301
  fieldErrors._errors.push(mapper(issue2));
33159
34302
  } else {
@@ -33180,17 +34323,17 @@ function formatError(error52, mapper = (issue2) => issue2.message) {
33180
34323
  }
33181
34324
  function treeifyError(error52, mapper = (issue2) => issue2.message) {
33182
34325
  const result = { errors: [] };
33183
- const processError = (error53, path20 = []) => {
34326
+ const processError = (error53, path21 = []) => {
33184
34327
  var _a4, _b;
33185
34328
  for (const issue2 of error53.issues) {
33186
34329
  if (issue2.code === "invalid_union" && issue2.errors.length) {
33187
- issue2.errors.map((issues) => processError({ issues }, [...path20, ...issue2.path]));
34330
+ issue2.errors.map((issues) => processError({ issues }, [...path21, ...issue2.path]));
33188
34331
  } else if (issue2.code === "invalid_key") {
33189
- processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
34332
+ processError({ issues: issue2.issues }, [...path21, ...issue2.path]);
33190
34333
  } else if (issue2.code === "invalid_element") {
33191
- processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
34334
+ processError({ issues: issue2.issues }, [...path21, ...issue2.path]);
33192
34335
  } else {
33193
- const fullpath = [...path20, ...issue2.path];
34336
+ const fullpath = [...path21, ...issue2.path];
33194
34337
  if (fullpath.length === 0) {
33195
34338
  result.errors.push(mapper(issue2));
33196
34339
  continue;
@@ -33222,8 +34365,8 @@ function treeifyError(error52, mapper = (issue2) => issue2.message) {
33222
34365
  }
33223
34366
  function toDotPath(_path) {
33224
34367
  const segs = [];
33225
- const path20 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
33226
- for (const seg of path20) {
34368
+ const path21 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
34369
+ for (const seg of path21) {
33227
34370
  if (typeof seg === "number")
33228
34371
  segs.push(`[${seg}]`);
33229
34372
  else if (typeof seg === "symbol")
@@ -46984,13 +48127,13 @@ function resolveRef2(ref, ctx) {
46984
48127
  if (!ref.startsWith("#")) {
46985
48128
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
46986
48129
  }
46987
- const path20 = ref.slice(1).split("/").filter(Boolean);
46988
- if (path20.length === 0) {
48130
+ const path21 = ref.slice(1).split("/").filter(Boolean);
48131
+ if (path21.length === 0) {
46989
48132
  return ctx.rootSchema;
46990
48133
  }
46991
48134
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
46992
- if (path20[0] === defsKey) {
46993
- const key = path20[1];
48135
+ if (path21[0] === defsKey) {
48136
+ const key = path21[1];
46994
48137
  if (!key || !ctx.defs[key]) {
46995
48138
  throw new Error(`Reference not found: ${ref}`);
46996
48139
  }
@@ -51504,7 +52647,7 @@ var init_protocol = __esm({
51504
52647
  return;
51505
52648
  }
51506
52649
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
51507
- await new Promise((resolve18) => setTimeout(resolve18, pollInterval));
52650
+ await new Promise((resolve19) => setTimeout(resolve19, pollInterval));
51508
52651
  options?.signal?.throwIfAborted();
51509
52652
  }
51510
52653
  } catch (error52) {
@@ -51521,7 +52664,7 @@ var init_protocol = __esm({
51521
52664
  */
51522
52665
  request(request, resultSchema, options) {
51523
52666
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
51524
- return new Promise((resolve18, reject) => {
52667
+ return new Promise((resolve19, reject) => {
51525
52668
  const earlyReject = (error52) => {
51526
52669
  reject(error52);
51527
52670
  };
@@ -51599,7 +52742,7 @@ var init_protocol = __esm({
51599
52742
  if (!parseResult.success) {
51600
52743
  reject(parseResult.error);
51601
52744
  } else {
51602
- resolve18(parseResult.data);
52745
+ resolve19(parseResult.data);
51603
52746
  }
51604
52747
  } catch (error52) {
51605
52748
  reject(error52);
@@ -51860,12 +53003,12 @@ var init_protocol = __esm({
51860
53003
  }
51861
53004
  } catch {
51862
53005
  }
51863
- return new Promise((resolve18, reject) => {
53006
+ return new Promise((resolve19, reject) => {
51864
53007
  if (signal.aborted) {
51865
53008
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
51866
53009
  return;
51867
53010
  }
51868
- const timeoutId = setTimeout(resolve18, interval);
53011
+ const timeoutId = setTimeout(resolve19, interval);
51869
53012
  signal.addEventListener("abort", () => {
51870
53013
  clearTimeout(timeoutId);
51871
53014
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -54160,8 +55303,8 @@ var require_resolve = __commonJS({
54160
55303
  }
54161
55304
  return count;
54162
55305
  }
54163
- function getFullPath(resolver, id = "", normalize3) {
54164
- if (normalize3 !== false)
55306
+ function getFullPath(resolver, id = "", normalize4) {
55307
+ if (normalize4 !== false)
54165
55308
  id = normalizeId(id);
54166
55309
  const p2 = resolver.parse(id);
54167
55310
  return _getFullPath(resolver, p2);
@@ -54913,7 +56056,7 @@ var require_compile = __commonJS({
54913
56056
  const schOrFunc = root.refs[ref];
54914
56057
  if (schOrFunc)
54915
56058
  return schOrFunc;
54916
- let _sch = resolve18.call(this, root, ref);
56059
+ let _sch = resolve19.call(this, root, ref);
54917
56060
  if (_sch === void 0) {
54918
56061
  const schema = (_a4 = root.localRefs) === null || _a4 === void 0 ? void 0 : _a4[ref];
54919
56062
  const { schemaId } = this.opts;
@@ -54940,7 +56083,7 @@ var require_compile = __commonJS({
54940
56083
  function sameSchemaEnv(s1, s2) {
54941
56084
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
54942
56085
  }
54943
- function resolve18(root, ref) {
56086
+ function resolve19(root, ref) {
54944
56087
  let sch;
54945
56088
  while (typeof (sch = this.refs[ref]) == "string")
54946
56089
  ref = sch;
@@ -55159,8 +56302,8 @@ var require_utils2 = __commonJS({
55159
56302
  }
55160
56303
  return ind;
55161
56304
  }
55162
- function removeDotSegments(path20) {
55163
- let input = path20;
56305
+ function removeDotSegments(path21) {
56306
+ let input = path21;
55164
56307
  const output = [];
55165
56308
  let nextSlash = -1;
55166
56309
  let len = 0;
@@ -55413,8 +56556,8 @@ var require_schemes = __commonJS({
55413
56556
  wsComponent.secure = void 0;
55414
56557
  }
55415
56558
  if (wsComponent.resourceName) {
55416
- const [path20, query] = wsComponent.resourceName.split("?");
55417
- wsComponent.path = path20 && path20 !== "/" ? path20 : void 0;
56559
+ const [path21, query] = wsComponent.resourceName.split("?");
56560
+ wsComponent.path = path21 && path21 !== "/" ? path21 : void 0;
55418
56561
  wsComponent.query = query;
55419
56562
  wsComponent.resourceName = void 0;
55420
56563
  }
@@ -55564,7 +56707,7 @@ var require_fast_uri = __commonJS({
55564
56707
  init_define_WEB_FIRST_SEGMENTS();
55565
56708
  var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils2();
55566
56709
  var { SCHEMES, getSchemeHandler } = require_schemes();
55567
- function normalize3(uri, options) {
56710
+ function normalize4(uri, options) {
55568
56711
  if (typeof uri === "string") {
55569
56712
  uri = /** @type {T} */
55570
56713
  normalizeString(uri, options);
@@ -55574,7 +56717,7 @@ var require_fast_uri = __commonJS({
55574
56717
  }
55575
56718
  return uri;
55576
56719
  }
55577
- function resolve18(baseURI, relativeURI, options) {
56720
+ function resolve19(baseURI, relativeURI, options) {
55578
56721
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
55579
56722
  const resolved = resolveComponent(parse5(baseURI, schemelessOptions), parse5(relativeURI, schemelessOptions), schemelessOptions, true);
55580
56723
  schemelessOptions.skipEscape = true;
@@ -55831,8 +56974,8 @@ var require_fast_uri = __commonJS({
55831
56974
  }
55832
56975
  var fastUri = {
55833
56976
  SCHEMES,
55834
- normalize: normalize3,
55835
- resolve: resolve18,
56977
+ normalize: normalize4,
56978
+ resolve: resolve19,
55836
56979
  resolveComponent,
55837
56980
  equal,
55838
56981
  serialize,
@@ -58853,12 +59996,12 @@ var require_dist3 = __commonJS({
58853
59996
  throw new Error(`Unknown format "${name}"`);
58854
59997
  return f;
58855
59998
  };
58856
- function addFormats(ajv, list, fs15, exportName) {
59999
+ function addFormats(ajv, list, fs16, exportName) {
58857
60000
  var _a4;
58858
60001
  var _b;
58859
60002
  (_a4 = (_b = ajv.opts.code).formats) !== null && _a4 !== void 0 ? _a4 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
58860
60003
  for (const f of list)
58861
- ajv.addFormat(f, fs15[f]);
60004
+ ajv.addFormat(f, fs16[f]);
58862
60005
  }
58863
60006
  module.exports = exports = formatsPlugin;
58864
60007
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -60014,7 +61157,7 @@ var init_mcp = __esm({
60014
61157
  let task = createTaskResult.task;
60015
61158
  const pollInterval = task.pollInterval ?? 5e3;
60016
61159
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
60017
- await new Promise((resolve18) => setTimeout(resolve18, pollInterval));
61160
+ await new Promise((resolve19) => setTimeout(resolve19, pollInterval));
60018
61161
  const updatedTask = await extra.taskStore.getTask(taskId);
60019
61162
  if (!updatedTask) {
60020
61163
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -60610,12 +61753,12 @@ var init_stdio2 = __esm({
60610
61753
  this.onclose?.();
60611
61754
  }
60612
61755
  send(message) {
60613
- return new Promise((resolve18) => {
61756
+ return new Promise((resolve19) => {
60614
61757
  const json2 = serializeMessage(message);
60615
61758
  if (this._stdout.write(json2)) {
60616
- resolve18();
61759
+ resolve19();
60617
61760
  } else {
60618
- this._stdout.once("drain", resolve18);
61761
+ this._stdout.once("drain", resolve19);
60619
61762
  }
60620
61763
  });
60621
61764
  }
@@ -61166,7 +62309,7 @@ var init_source = __esm({
61166
62309
  });
61167
62310
 
61168
62311
  // src/core/fixer.ts
61169
- import * as fs11 from "node:fs";
62312
+ import * as fs12 from "node:fs";
61170
62313
  function applyFixes(result, options = {}) {
61171
62314
  const log = options.quiet ? () => {
61172
62315
  } : console.log.bind(console);
@@ -61204,7 +62347,7 @@ function applyFixes(result, options = {}) {
61204
62347
  for (const [filePath, fixes] of fixesByFile) {
61205
62348
  let content;
61206
62349
  try {
61207
- content = fs11.readFileSync(filePath, "utf-8");
62350
+ content = fs12.readFileSync(filePath, "utf-8");
61208
62351
  } catch (err) {
61209
62352
  log(source_default.yellow(" Skipped") + ` ${filePath}: ${err.message}`);
61210
62353
  continue;
@@ -61289,7 +62432,7 @@ function applyFixes(result, options = {}) {
61289
62432
  }
61290
62433
  if (!dryRun) {
61291
62434
  try {
61292
- fs11.writeFileSync(filePath, newContent, "utf-8");
62435
+ fs12.writeFileSync(filePath, newContent, "utf-8");
61293
62436
  } catch (err) {
61294
62437
  log(source_default.yellow(" Skipped") + ` ${filePath}: ${err.message}`);
61295
62438
  continue;
@@ -61311,8 +62454,8 @@ var init_fixer = __esm({
61311
62454
  });
61312
62455
 
61313
62456
  // src/core/config.ts
61314
- import * as fs12 from "node:fs";
61315
- import * as path16 from "node:path";
62457
+ import * as fs13 from "node:fs";
62458
+ import * as path17 from "node:path";
61316
62459
  function formatJsonError(content, err) {
61317
62460
  const msg = err instanceof Error ? err.message : String(err);
61318
62461
  const posMatch = msg.match(/position (\d+)/);
@@ -61410,10 +62553,10 @@ function warnIgnoreRulePathPatternMisuse(config2, source) {
61410
62553
  }
61411
62554
  function loadConfig(projectRoot) {
61412
62555
  for (const filename of CONFIG_FILENAMES) {
61413
- const filePath = path16.join(projectRoot, filename);
62556
+ const filePath = path17.join(projectRoot, filename);
61414
62557
  let content;
61415
62558
  try {
61416
- content = stripBom(fs12.readFileSync(filePath, "utf-8"));
62559
+ content = stripBom(fs13.readFileSync(filePath, "utf-8"));
61417
62560
  } catch {
61418
62561
  continue;
61419
62562
  }
@@ -61424,7 +62567,7 @@ function loadConfig(projectRoot) {
61424
62567
  function loadConfigFromExplicitPath(configPath) {
61425
62568
  let content;
61426
62569
  try {
61427
- content = stripBom(fs12.readFileSync(configPath, "utf-8"));
62570
+ content = stripBom(fs13.readFileSync(configPath, "utf-8"));
61428
62571
  } catch (err) {
61429
62572
  const detail = err instanceof Error ? err.message : String(err);
61430
62573
  throw new Error(`could not load config from ${configPath}: ${detail}`, { cause: err });
@@ -61466,7 +62609,7 @@ __export(server_exports2, {
61466
62609
  server: () => server,
61467
62610
  startServer: () => startServer
61468
62611
  });
61469
- import * as path17 from "node:path";
62612
+ import * as path18 from "node:path";
61470
62613
  function describeDisallowed(rawPath) {
61471
62614
  const m = rawPath.match(PATH_DISALLOWED);
61472
62615
  if (!m) return "unknown";
@@ -61479,7 +62622,7 @@ function validateProjectPath(rawPath) {
61479
62622
  `projectPath contains disallowed character ${describeDisallowed(rawPath)} (control chars and shell metacharacters are rejected)`
61480
62623
  );
61481
62624
  }
61482
- const resolved = path17.resolve(rawPath);
62625
+ const resolved = path18.resolve(rawPath);
61483
62626
  if (!isDirectory(resolved)) {
61484
62627
  throw new Error("projectPath is not an existing directory");
61485
62628
  }
@@ -61503,10 +62646,10 @@ function validateFilePathInput(rawPath) {
61503
62646
  }
61504
62647
  }
61505
62648
  function resolveWithinRoot(filePath, root) {
61506
- const resolvedRoot = path17.resolve(root);
61507
- const resolved = path17.resolve(resolvedRoot, filePath);
61508
- const rel = path17.relative(resolvedRoot, resolved);
61509
- if (rel.startsWith("..") || path17.isAbsolute(rel)) {
62649
+ const resolvedRoot = path18.resolve(root);
62650
+ const resolved = path18.resolve(resolvedRoot, filePath);
62651
+ const rel = path18.relative(resolvedRoot, resolved);
62652
+ if (rel.startsWith("..") || path18.isAbsolute(rel)) {
61510
62653
  throw new Error("path escapes the project root");
61511
62654
  }
61512
62655
  return resolved;
@@ -61603,7 +62746,7 @@ var init_server4 = __esm({
61603
62746
  exists: fileExists(resolved) || isDirectory(resolved)
61604
62747
  };
61605
62748
  if (!result.exists) {
61606
- const rename = await findRenames(root, path17.relative(root, resolved));
62749
+ const rename = await findRenames(root, path18.relative(root, resolved));
61607
62750
  if (rename) {
61608
62751
  result.renamed = true;
61609
62752
  result.newPath = rename.newPath;
@@ -63021,8 +64164,8 @@ var init_suggestSimilar = __esm({
63021
64164
  // node_modules/.pnpm/commander@15.0.0/node_modules/commander/lib/command.js
63022
64165
  import { EventEmitter as EventEmitter2 } from "node:events";
63023
64166
  import childProcess from "node:child_process";
63024
- import path18 from "node:path";
63025
- import fs13 from "node:fs";
64167
+ import path19 from "node:path";
64168
+ import fs14 from "node:fs";
63026
64169
  import process5 from "node:process";
63027
64170
  import { stripVTControlCharacters as stripVTControlCharacters2 } from "node:util";
63028
64171
  function incrementNodeInspectorPort(args2) {
@@ -64060,7 +65203,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
64060
65203
  * @param {string} subcommandName
64061
65204
  */
64062
65205
  _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
64063
- if (fs13.existsSync(executableFile)) return;
65206
+ if (fs14.existsSync(executableFile)) return;
64064
65207
  const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
64065
65208
  const executableMissing = `'${executableFile}' does not exist
64066
65209
  - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
@@ -64077,11 +65220,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
64077
65220
  args2 = args2.slice();
64078
65221
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
64079
65222
  function findFile(baseDir, baseName) {
64080
- const localBin = path18.resolve(baseDir, baseName);
64081
- if (fs13.existsSync(localBin)) return localBin;
64082
- if (sourceExt.includes(path18.extname(baseName))) return void 0;
65223
+ const localBin = path19.resolve(baseDir, baseName);
65224
+ if (fs14.existsSync(localBin)) return localBin;
65225
+ if (sourceExt.includes(path19.extname(baseName))) return void 0;
64083
65226
  const foundExt = sourceExt.find(
64084
- (ext) => fs13.existsSync(`${localBin}${ext}`)
65227
+ (ext) => fs14.existsSync(`${localBin}${ext}`)
64085
65228
  );
64086
65229
  if (foundExt) return `${localBin}${foundExt}`;
64087
65230
  return void 0;
@@ -64093,21 +65236,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
64093
65236
  if (this._scriptPath) {
64094
65237
  let resolvedScriptPath;
64095
65238
  try {
64096
- resolvedScriptPath = fs13.realpathSync(this._scriptPath);
65239
+ resolvedScriptPath = fs14.realpathSync(this._scriptPath);
64097
65240
  } catch {
64098
65241
  resolvedScriptPath = this._scriptPath;
64099
65242
  }
64100
- executableDir = path18.resolve(
64101
- path18.dirname(resolvedScriptPath),
65243
+ executableDir = path19.resolve(
65244
+ path19.dirname(resolvedScriptPath),
64102
65245
  executableDir
64103
65246
  );
64104
65247
  }
64105
65248
  if (executableDir) {
64106
65249
  let localFile = findFile(executableDir, executableFile);
64107
65250
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
64108
- const legacyName = path18.basename(
65251
+ const legacyName = path19.basename(
64109
65252
  this._scriptPath,
64110
- path18.extname(this._scriptPath)
65253
+ path19.extname(this._scriptPath)
64111
65254
  );
64112
65255
  if (legacyName !== this._name) {
64113
65256
  localFile = findFile(
@@ -64118,7 +65261,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
64118
65261
  }
64119
65262
  executableFile = localFile || executableFile;
64120
65263
  }
64121
- const launchWithNode = sourceExt.includes(path18.extname(executableFile));
65264
+ const launchWithNode = sourceExt.includes(path19.extname(executableFile));
64122
65265
  let proc;
64123
65266
  if (process5.platform !== "win32") {
64124
65267
  if (launchWithNode) {
@@ -65035,7 +66178,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
65035
66178
  * @return {Command}
65036
66179
  */
65037
66180
  nameFromFilename(filename) {
65038
- this._name = path18.basename(filename, path18.extname(filename));
66181
+ this._name = path19.basename(filename, path19.extname(filename));
65039
66182
  return this;
65040
66183
  }
65041
66184
  /**
@@ -65049,9 +66192,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
65049
66192
  * @param {string} [path]
65050
66193
  * @return {(string|null|Command)}
65051
66194
  */
65052
- executableDir(path20) {
65053
- if (path20 === void 0) return this._executableDir;
65054
- this._executableDir = path20;
66195
+ executableDir(path21) {
66196
+ if (path21 === void 0) return this._executableDir;
66197
+ this._executableDir = path21;
65055
66198
  return this;
65056
66199
  }
65057
66200
  /**
@@ -68384,6 +69527,8 @@ var init_ora = __esm({
68384
69527
  });
68385
69528
 
68386
69529
  // src/core/reporter.ts
69530
+ import { existsSync as existsSync8, readFileSync as readFileSync11 } from "node:fs";
69531
+ import { join as join16 } from "node:path";
68387
69532
  function classifyFile(f) {
68388
69533
  if (f.path === "(project)") return "context";
68389
69534
  if (f.path === "(mcp)") return "mcp";
@@ -68405,10 +69550,32 @@ function classifyFile(f) {
68405
69550
  function isSyntheticBucket(p2) {
68406
69551
  return p2 === "(project)" || p2 === "(mcp)" || p2.includes(SESSION_AUDIT_PATH_MARKER) || p2.includes(SKILL_AUDIT_PATH_MARKER);
68407
69552
  }
69553
+ function detectVersionSkew(projectRoot, runningVersion) {
69554
+ try {
69555
+ const pkgPath = join16(projectRoot, "package.json");
69556
+ if (!existsSync8(pkgPath)) return null;
69557
+ const raw = readFileSync11(pkgPath, "utf8").replace(/^\uFEFF/, "");
69558
+ const pkg = JSON.parse(raw);
69559
+ const name = pkg.name ?? "";
69560
+ if (name !== "ctxlint" && name !== "@yawlabs/ctxlint") return null;
69561
+ if (!pkg.version || pkg.version === runningVersion) return null;
69562
+ return pkg.version;
69563
+ } catch {
69564
+ return null;
69565
+ }
69566
+ }
68408
69567
  function formatText(result, verbose = false) {
68409
69568
  const lines = [];
68410
69569
  lines.push("");
68411
69570
  lines.push(source_default.bold(`ctxlint v${result.version}`));
69571
+ const skew = detectVersionSkew(result.projectRoot, result.version);
69572
+ if (skew !== null) {
69573
+ lines.push(
69574
+ source_default.yellow(
69575
+ ` note: this checkout is v${skew} -- you are running v${result.version}. Rebuild or reinstall before trusting a result against it.`
69576
+ )
69577
+ );
69578
+ }
68412
69579
  lines.push("");
68413
69580
  lines.push(`Scanning ${result.projectRoot}...`);
68414
69581
  lines.push("");
@@ -68782,6 +69949,34 @@ function buildRuleDescriptors() {
68782
69949
  },
68783
69950
  helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
68784
69951
  },
69952
+ {
69953
+ id: "ctxlint/session-shared-temp-path",
69954
+ shortDescription: {
69955
+ text: "Fixed temp path written then read back -- clobberable by a concurrent session"
69956
+ },
69957
+ helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
69958
+ },
69959
+ {
69960
+ id: "ctxlint/session-unverified-gate-claimed-clean",
69961
+ shortDescription: {
69962
+ text: "A quality gate asserted as passing while its invocation failed or emitted nothing"
69963
+ },
69964
+ helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
69965
+ },
69966
+ {
69967
+ id: "ctxlint/session-default-branch-accumulation",
69968
+ shortDescription: {
69969
+ text: "Many files edited on the default branch with no intervening commit"
69970
+ },
69971
+ helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
69972
+ },
69973
+ {
69974
+ id: "ctxlint/session-unresolvable-sha",
69975
+ shortDescription: {
69976
+ text: "Memory cites a git SHA that no longer resolves in the repository"
69977
+ },
69978
+ helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
69979
+ },
68785
69980
  {
68786
69981
  id: "ctxlint/skill-frontmatter",
68787
69982
  shortDescription: { text: "Skill/agent definition missing required frontmatter" },
@@ -68852,8 +70047,8 @@ __export(cli_exports, {
68852
70047
  promptYesNo: () => promptYesNo,
68853
70048
  runCli: () => runCli
68854
70049
  });
68855
- import * as fs14 from "node:fs";
68856
- import * as path19 from "node:path";
70050
+ import * as fs15 from "node:fs";
70051
+ import * as path20 from "node:path";
68857
70052
  function validateCheckNames(names, source) {
68858
70053
  const invalid = names.filter((n7) => n7 && !VALID_CHECKS.has(n7));
68859
70054
  if (invalid.length > 0) {
@@ -68876,7 +70071,7 @@ async function runCli() {
68876
70071
  "Also scan the user-global ~/.claude/settings.json in the dead-hook check (off by default; the standard run only reads project .claude/settings.json[.local])",
68877
70072
  false
68878
70073
  ).option("--no-ignore-file", "Disable .ctxlintignore suppression (see all findings)").option("--watch", "Re-lint on context file changes", false).action(async (projectPath, opts) => {
68879
- const resolvedPath = path19.resolve(projectPath);
70074
+ const resolvedPath = path20.resolve(projectPath);
68880
70075
  const { config: config2, options, activeChecks } = resolveSession(resolvedPath, opts);
68881
70076
  const spinner = options.format === "text" && !options.quiet ? ora("Scanning for context files...").start() : void 0;
68882
70077
  try {
@@ -69007,41 +70202,41 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
69007
70202
  const chalk2 = (await Promise.resolve().then(() => (init_source(), source_exports))).default;
69008
70203
  console.log(chalk2.dim("\nWatching for changes... (Ctrl+C to stop)\n"));
69009
70204
  const watchPaths = [
69010
- path19.join(resolvedPath, "CLAUDE.md"),
69011
- path19.join(resolvedPath, "CLAUDE.local.md"),
69012
- path19.join(resolvedPath, "AGENTS.md"),
69013
- path19.join(resolvedPath, "AGENT.md"),
69014
- path19.join(resolvedPath, ".cursorrules"),
69015
- path19.join(resolvedPath, ".windsurfrules"),
69016
- path19.join(resolvedPath, ".clinerules"),
69017
- path19.join(resolvedPath, ".aiderules"),
69018
- path19.join(resolvedPath, ".continuerules"),
69019
- path19.join(resolvedPath, ".rules"),
69020
- path19.join(resolvedPath, ".goosehints"),
69021
- path19.join(resolvedPath, "GEMINI.md"),
69022
- path19.join(resolvedPath, "replit.md"),
69023
- path19.join(resolvedPath, "package.json"),
69024
- path19.join(resolvedPath, ".mcp.json"),
70205
+ path20.join(resolvedPath, "CLAUDE.md"),
70206
+ path20.join(resolvedPath, "CLAUDE.local.md"),
70207
+ path20.join(resolvedPath, "AGENTS.md"),
70208
+ path20.join(resolvedPath, "AGENT.md"),
70209
+ path20.join(resolvedPath, ".cursorrules"),
70210
+ path20.join(resolvedPath, ".windsurfrules"),
70211
+ path20.join(resolvedPath, ".clinerules"),
70212
+ path20.join(resolvedPath, ".aiderules"),
70213
+ path20.join(resolvedPath, ".continuerules"),
70214
+ path20.join(resolvedPath, ".rules"),
70215
+ path20.join(resolvedPath, ".goosehints"),
70216
+ path20.join(resolvedPath, "GEMINI.md"),
70217
+ path20.join(resolvedPath, "replit.md"),
70218
+ path20.join(resolvedPath, "package.json"),
70219
+ path20.join(resolvedPath, ".mcp.json"),
69025
70220
  // ctxlint config — re-resolve on edit so threshold changes,
69026
70221
  // ignore-list edits, and check-list overrides take effect without
69027
70222
  // restarting the watcher.
69028
- path19.join(resolvedPath, ".ctxlintrc"),
69029
- path19.join(resolvedPath, ".ctxlintrc.json"),
70223
+ path20.join(resolvedPath, ".ctxlintrc"),
70224
+ path20.join(resolvedPath, ".ctxlintrc.json"),
69030
70225
  // .ctxlintignore — re-lint when suppression rules change.
69031
- path19.join(resolvedPath, ".ctxlintignore")
70226
+ path20.join(resolvedPath, ".ctxlintignore")
69032
70227
  ];
69033
70228
  const watchDirs = [
69034
- path19.join(resolvedPath, ".claude"),
69035
- path19.join(resolvedPath, ".cursor"),
69036
- path19.join(resolvedPath, ".github"),
69037
- path19.join(resolvedPath, ".windsurf"),
69038
- path19.join(resolvedPath, ".aide"),
69039
- path19.join(resolvedPath, ".amazonq"),
69040
- path19.join(resolvedPath, ".goose"),
69041
- path19.join(resolvedPath, ".junie"),
69042
- path19.join(resolvedPath, ".aiassistant"),
69043
- path19.join(resolvedPath, ".continue"),
69044
- path19.join(resolvedPath, ".vscode")
70229
+ path20.join(resolvedPath, ".claude"),
70230
+ path20.join(resolvedPath, ".cursor"),
70231
+ path20.join(resolvedPath, ".github"),
70232
+ path20.join(resolvedPath, ".windsurf"),
70233
+ path20.join(resolvedPath, ".aide"),
70234
+ path20.join(resolvedPath, ".amazonq"),
70235
+ path20.join(resolvedPath, ".goose"),
70236
+ path20.join(resolvedPath, ".junie"),
70237
+ path20.join(resolvedPath, ".aiassistant"),
70238
+ path20.join(resolvedPath, ".continue"),
70239
+ path20.join(resolvedPath, ".vscode")
69045
70240
  ];
69046
70241
  const watchers = [];
69047
70242
  let debounceTimer = null;
@@ -69103,13 +70298,13 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
69103
70298
  };
69104
70299
  for (const filePath of watchPaths) {
69105
70300
  try {
69106
- watchers.push(fs14.watch(filePath, rerun));
70301
+ watchers.push(fs15.watch(filePath, rerun));
69107
70302
  } catch {
69108
70303
  }
69109
70304
  }
69110
70305
  for (const dir of watchDirs) {
69111
70306
  try {
69112
- watchers.push(fs14.watch(dir, { recursive: true }, rerun));
70307
+ watchers.push(fs15.watch(dir, { recursive: true }, rerun));
69113
70308
  } catch {
69114
70309
  }
69115
70310
  }
@@ -69130,17 +70325,17 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
69130
70325
  }
69131
70326
  });
69132
70327
  program2.command("init").description("Set up a git pre-commit hook that runs ctxlint --strict").argument("[path]", "Project directory", ".").action(async (projectPath) => {
69133
- const resolvedRoot = path19.resolve(projectPath);
69134
- const gitDir = path19.join(resolvedRoot, ".git");
69135
- const hooksDir = path19.join(gitDir, "hooks");
69136
- if (!fs14.existsSync(gitDir)) {
70328
+ const resolvedRoot = path20.resolve(projectPath);
70329
+ const gitDir = path20.join(resolvedRoot, ".git");
70330
+ const hooksDir = path20.join(gitDir, "hooks");
70331
+ if (!fs15.existsSync(gitDir)) {
69137
70332
  console.error('Error: not a git repository. Run "git init" first.');
69138
70333
  process.exit(1);
69139
70334
  }
69140
- if (!fs14.existsSync(hooksDir)) {
69141
- fs14.mkdirSync(hooksDir, { recursive: true });
70335
+ if (!fs15.existsSync(hooksDir)) {
70336
+ fs15.mkdirSync(hooksDir, { recursive: true });
69142
70337
  }
69143
- const hookPath = path19.join(hooksDir, "pre-commit");
70338
+ const hookPath = path20.join(hooksDir, "pre-commit");
69144
70339
  const fullHookContent = `#!/bin/sh
69145
70340
  # ctxlint pre-commit hook
69146
70341
  npx @yawlabs/ctxlint@${VERSION} --strict
@@ -69149,15 +70344,15 @@ npx @yawlabs/ctxlint@${VERSION} --strict
69149
70344
  # ctxlint pre-commit hook
69150
70345
  npx @yawlabs/ctxlint@${VERSION} --strict
69151
70346
  `;
69152
- if (fs14.existsSync(hookPath)) {
69153
- const existing = fs14.readFileSync(hookPath, "utf-8");
70347
+ if (fs15.existsSync(hookPath)) {
70348
+ const existing = fs15.readFileSync(hookPath, "utf-8");
69154
70349
  const installed = existing.match(
69155
70350
  /^[^#\r\n]*\bnpx\s+(?:-y\s+|--yes\s+)?@yawlabs\/ctxlint(?:@(\d+\.\d+\.\d+))?/m
69156
70351
  );
69157
70352
  if (installed) {
69158
70353
  const pinned = installed[1];
69159
70354
  if (pinned && pinned !== VERSION) {
69160
- fs14.writeFileSync(
70355
+ fs15.writeFileSync(
69161
70356
  hookPath,
69162
70357
  existing.replace(/(@yawlabs\/ctxlint@)\d+\.\d+\.\d+/g, `$1${VERSION}`)
69163
70358
  );
@@ -69167,10 +70362,10 @@ npx @yawlabs/ctxlint@${VERSION} --strict
69167
70362
  }
69168
70363
  return;
69169
70364
  }
69170
- fs14.appendFileSync(hookPath, appendHookContent);
70365
+ fs15.appendFileSync(hookPath, appendHookContent);
69171
70366
  console.log("Added ctxlint to existing pre-commit hook.");
69172
70367
  } else {
69173
- fs14.writeFileSync(hookPath, fullHookContent, { mode: 493 });
70368
+ fs15.writeFileSync(hookPath, fullHookContent, { mode: 493 });
69174
70369
  console.log("Created pre-commit hook at .git/hooks/pre-commit");
69175
70370
  }
69176
70371
  console.log("ctxlint will now run automatically before each commit.");
@@ -69178,8 +70373,8 @@ npx @yawlabs/ctxlint@${VERSION} --strict
69178
70373
  program2.parse();
69179
70374
  }
69180
70375
  async function promptYesNo(question) {
69181
- const { createInterface: createInterface2 } = await import("node:readline/promises");
69182
- const rl = createInterface2({ input: process.stdin, output: process.stderr });
70376
+ const { createInterface: createInterface3 } = await import("node:readline/promises");
70377
+ const rl = createInterface3({ input: process.stdin, output: process.stderr });
69183
70378
  try {
69184
70379
  const answer = await rl.question(question);
69185
70380
  return /^\s*y(es)?\s*$/i.test(answer);
@@ -69192,7 +70387,7 @@ function resolveDepth(raw) {
69192
70387
  return Number.isNaN(parsed) ? 2 : Math.max(0, Math.min(parsed, 10));
69193
70388
  }
69194
70389
  function resolveSession(resolvedPath, opts, throwOnConfigError = false) {
69195
- const configPath = opts.config ? path19.resolve(opts.config) : void 0;
70390
+ const configPath = opts.config ? path20.resolve(opts.config) : void 0;
69196
70391
  const config2 = configPath ? loadConfigFromPath(configPath, throwOnConfigError) : loadConfig(resolvedPath);
69197
70392
  const mcpGlobal = opts.mcpGlobal || config2?.mcpGlobal || false;
69198
70393
  const mcpOnly = opts.mcpOnly || config2?.mcpOnly || false;
@@ -69310,4 +70505,3 @@ if (args.includes("--lsp")) {
69310
70505
  const { runCli: runCli2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
69311
70506
  await runCli2();
69312
70507
  }
69313
- //# sourceMappingURL=index.js.map