@yawlabs/ctxlint 0.12.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +298 -224
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -461,8 +461,8 @@ var init_parseUtil = __esm({
461
461
  init_errors();
462
462
  init_en();
463
463
  makeIssue = (params) => {
464
- const { data, path: path13, errorMaps, issueData } = params;
465
- const fullPath = [...path13, ...issueData.path || []];
464
+ const { data, path: path14, errorMaps, issueData } = params;
465
+ const fullPath = [...path14, ...issueData.path || []];
466
466
  const fullIssue = {
467
467
  ...issueData,
468
468
  path: fullPath
@@ -745,11 +745,11 @@ var init_types = __esm({
745
745
  init_parseUtil();
746
746
  init_util();
747
747
  ParseInputLazyPath = class {
748
- constructor(parent, value, path13, key) {
748
+ constructor(parent, value, path14, key) {
749
749
  this._cachedPath = [];
750
750
  this.parent = parent;
751
751
  this.data = value;
752
- this._path = path13;
752
+ this._path = path14;
753
753
  this._key = key;
754
754
  }
755
755
  get path() {
@@ -4257,15 +4257,15 @@ function mergeDefs(...defs) {
4257
4257
  function cloneDef(schema) {
4258
4258
  return mergeDefs(schema._zod.def);
4259
4259
  }
4260
- function getElementAtPath(obj, path13) {
4261
- if (!path13)
4260
+ function getElementAtPath(obj, path14) {
4261
+ if (!path14)
4262
4262
  return obj;
4263
- return path13.reduce((acc, key) => acc?.[key], obj);
4263
+ return path14.reduce((acc, key) => acc?.[key], obj);
4264
4264
  }
4265
4265
  function promiseAllObject(promisesObj) {
4266
4266
  const keys = Object.keys(promisesObj);
4267
- const promises = keys.map((key) => promisesObj[key]);
4268
- return Promise.all(promises).then((results) => {
4267
+ const promises2 = keys.map((key) => promisesObj[key]);
4268
+ return Promise.all(promises2).then((results) => {
4269
4269
  const resolvedObj = {};
4270
4270
  for (let i2 = 0; i2 < keys.length; i2++) {
4271
4271
  resolvedObj[keys[i2]] = results[i2];
@@ -4572,11 +4572,11 @@ function aborted(x3, startIndex = 0) {
4572
4572
  }
4573
4573
  return false;
4574
4574
  }
4575
- function prefixIssues(path13, issues) {
4575
+ function prefixIssues(path14, issues) {
4576
4576
  return issues.map((iss) => {
4577
4577
  var _a3;
4578
4578
  (_a3 = iss).path ?? (_a3.path = []);
4579
- iss.path.unshift(path13);
4579
+ iss.path.unshift(path14);
4580
4580
  return iss;
4581
4581
  });
4582
4582
  }
@@ -4819,7 +4819,7 @@ function formatError(error49, mapper = (issue2) => issue2.message) {
4819
4819
  }
4820
4820
  function treeifyError(error49, mapper = (issue2) => issue2.message) {
4821
4821
  const result = { errors: [] };
4822
- const processError = (error50, path13 = []) => {
4822
+ const processError = (error50, path14 = []) => {
4823
4823
  var _a3, _b;
4824
4824
  for (const issue2 of error50.issues) {
4825
4825
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -4829,7 +4829,7 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
4829
4829
  } else if (issue2.code === "invalid_element") {
4830
4830
  processError({ issues: issue2.issues }, issue2.path);
4831
4831
  } else {
4832
- const fullpath = [...path13, ...issue2.path];
4832
+ const fullpath = [...path14, ...issue2.path];
4833
4833
  if (fullpath.length === 0) {
4834
4834
  result.errors.push(mapper(issue2));
4835
4835
  continue;
@@ -4861,8 +4861,8 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
4861
4861
  }
4862
4862
  function toDotPath(_path) {
4863
4863
  const segs = [];
4864
- const path13 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
4865
- for (const seg of path13) {
4864
+ const path14 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
4865
+ for (const seg of path14) {
4866
4866
  if (typeof seg === "number")
4867
4867
  segs.push(`[${seg}]`);
4868
4868
  else if (typeof seg === "symbol")
@@ -17890,13 +17890,13 @@ function resolveRef(ref, ctx) {
17890
17890
  if (!ref.startsWith("#")) {
17891
17891
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
17892
17892
  }
17893
- const path13 = ref.slice(1).split("/").filter(Boolean);
17894
- if (path13.length === 0) {
17893
+ const path14 = ref.slice(1).split("/").filter(Boolean);
17894
+ if (path14.length === 0) {
17895
17895
  return ctx.rootSchema;
17896
17896
  }
17897
17897
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
17898
- if (path13[0] === defsKey) {
17899
- const key = path13[1];
17898
+ if (path14[0] === defsKey) {
17899
+ const key = path14[1];
17900
17900
  if (!key || !ctx.defs[key]) {
17901
17901
  throw new Error(`Reference not found: ${ref}`);
17902
17902
  }
@@ -26056,8 +26056,8 @@ var require_utils = __commonJS({
26056
26056
  }
26057
26057
  return ind;
26058
26058
  }
26059
- function removeDotSegments(path13) {
26060
- let input = path13;
26059
+ function removeDotSegments(path14) {
26060
+ let input = path14;
26061
26061
  const output = [];
26062
26062
  let nextSlash = -1;
26063
26063
  let len = 0;
@@ -26257,8 +26257,8 @@ var require_schemes = __commonJS({
26257
26257
  wsComponent.secure = void 0;
26258
26258
  }
26259
26259
  if (wsComponent.resourceName) {
26260
- const [path13, query] = wsComponent.resourceName.split("?");
26261
- wsComponent.path = path13 && path13 !== "/" ? path13 : void 0;
26260
+ const [path14, query] = wsComponent.resourceName.split("?");
26261
+ wsComponent.path = path14 && path14 !== "/" ? path14 : void 0;
26262
26262
  wsComponent.query = query;
26263
26263
  wsComponent.resourceName = void 0;
26264
26264
  }
@@ -26424,49 +26424,49 @@ var require_fast_uri = __commonJS({
26424
26424
  schemelessOptions.skipEscape = true;
26425
26425
  return serialize(resolved, schemelessOptions);
26426
26426
  }
26427
- function resolveComponent(base, relative5, options, skipNormalization) {
26427
+ function resolveComponent(base, relative6, options, skipNormalization) {
26428
26428
  const target = {};
26429
26429
  if (!skipNormalization) {
26430
26430
  base = parse4(serialize(base, options), options);
26431
- relative5 = parse4(serialize(relative5, options), options);
26431
+ relative6 = parse4(serialize(relative6, options), options);
26432
26432
  }
26433
26433
  options = options || {};
26434
- if (!options.tolerant && relative5.scheme) {
26435
- target.scheme = relative5.scheme;
26436
- target.userinfo = relative5.userinfo;
26437
- target.host = relative5.host;
26438
- target.port = relative5.port;
26439
- target.path = removeDotSegments(relative5.path || "");
26440
- target.query = relative5.query;
26434
+ if (!options.tolerant && relative6.scheme) {
26435
+ target.scheme = relative6.scheme;
26436
+ target.userinfo = relative6.userinfo;
26437
+ target.host = relative6.host;
26438
+ target.port = relative6.port;
26439
+ target.path = removeDotSegments(relative6.path || "");
26440
+ target.query = relative6.query;
26441
26441
  } else {
26442
- if (relative5.userinfo !== void 0 || relative5.host !== void 0 || relative5.port !== void 0) {
26443
- target.userinfo = relative5.userinfo;
26444
- target.host = relative5.host;
26445
- target.port = relative5.port;
26446
- target.path = removeDotSegments(relative5.path || "");
26447
- target.query = relative5.query;
26442
+ if (relative6.userinfo !== void 0 || relative6.host !== void 0 || relative6.port !== void 0) {
26443
+ target.userinfo = relative6.userinfo;
26444
+ target.host = relative6.host;
26445
+ target.port = relative6.port;
26446
+ target.path = removeDotSegments(relative6.path || "");
26447
+ target.query = relative6.query;
26448
26448
  } else {
26449
- if (!relative5.path) {
26449
+ if (!relative6.path) {
26450
26450
  target.path = base.path;
26451
- if (relative5.query !== void 0) {
26452
- target.query = relative5.query;
26451
+ if (relative6.query !== void 0) {
26452
+ target.query = relative6.query;
26453
26453
  } else {
26454
26454
  target.query = base.query;
26455
26455
  }
26456
26456
  } else {
26457
- if (relative5.path[0] === "/") {
26458
- target.path = removeDotSegments(relative5.path);
26457
+ if (relative6.path[0] === "/") {
26458
+ target.path = removeDotSegments(relative6.path);
26459
26459
  } else {
26460
26460
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
26461
- target.path = "/" + relative5.path;
26461
+ target.path = "/" + relative6.path;
26462
26462
  } else if (!base.path) {
26463
- target.path = relative5.path;
26463
+ target.path = relative6.path;
26464
26464
  } else {
26465
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative5.path;
26465
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative6.path;
26466
26466
  }
26467
26467
  target.path = removeDotSegments(target.path);
26468
26468
  }
26469
- target.query = relative5.query;
26469
+ target.query = relative6.query;
26470
26470
  }
26471
26471
  target.userinfo = base.userinfo;
26472
26472
  target.host = base.host;
@@ -26474,7 +26474,7 @@ var require_fast_uri = __commonJS({
26474
26474
  }
26475
26475
  target.scheme = base.scheme;
26476
26476
  }
26477
- target.fragment = relative5.fragment;
26477
+ target.fragment = relative6.fragment;
26478
26478
  return target;
26479
26479
  }
26480
26480
  function equal(uriA, uriB, options) {
@@ -34545,11 +34545,11 @@ async function scanForContextFiles(projectRoot, options = {}) {
34545
34545
  try {
34546
34546
  const entries = fs3.readdirSync(dir, { withFileTypes: true });
34547
34547
  for (const entry of entries) {
34548
- if (entry.isDirectory() && !IGNORED_DIRS2.has(entry.name) && !entry.name.startsWith(".")) {
34549
- const fullPath = path2.join(dir, entry.name);
34550
- dirsToScan.push(fullPath);
34551
- collectDirs(fullPath, currentDepth + 1);
34552
- }
34548
+ if (!entry.isDirectory() || IGNORED_DIRS2.has(entry.name)) continue;
34549
+ if (entry.name.startsWith(".") && !ALLOWED_DOT_DIRS.has(entry.name)) continue;
34550
+ const fullPath = path2.join(dir, entry.name);
34551
+ dirsToScan.push(fullPath);
34552
+ collectDirs(fullPath, currentDepth + 1);
34553
34553
  }
34554
34554
  } catch {
34555
34555
  }
@@ -34682,7 +34682,7 @@ async function scanGlobalMcpConfigs() {
34682
34682
  continue;
34683
34683
  }
34684
34684
  const isGeneralClaudeFile = normalized.endsWith(`${path2.sep}.claude.json`) || normalized.endsWith(`${path2.sep}.claude${path2.sep}settings.json`);
34685
- if (isGeneralClaudeFile && !mcpFileHasMcpKey(normalized)) continue;
34685
+ if (isGeneralClaudeFile && !await mcpFileHasMcpKey(normalized)) continue;
34686
34686
  const symlink = isSymlink(normalized);
34687
34687
  const target = symlink ? readSymlinkTarget(normalized) : void 0;
34688
34688
  found.push({
@@ -34695,15 +34695,22 @@ async function scanGlobalMcpConfigs() {
34695
34695
  }
34696
34696
  return found.sort((a, b2) => a.relativePath.localeCompare(b2.relativePath));
34697
34697
  }
34698
- function mcpFileHasMcpKey(filePath) {
34698
+ async function mcpFileHasMcpKey(filePath) {
34699
+ let fd;
34699
34700
  try {
34700
- const content = fs3.readFileSync(filePath, "utf8");
34701
- return /"(mcpServers|servers)"\s*:/.test(content);
34701
+ fd = await fs3.promises.open(filePath, "r");
34702
+ const buf = Buffer.alloc(PREFIX_BYTES);
34703
+ const { bytesRead } = await fd.read(buf, 0, PREFIX_BYTES, 0);
34704
+ const prefix = buf.subarray(0, bytesRead).toString("utf8");
34705
+ return /"(mcpServers|servers)"\s*:/.test(prefix);
34702
34706
  } catch {
34703
34707
  return false;
34708
+ } finally {
34709
+ await fd?.close().catch(() => {
34710
+ });
34704
34711
  }
34705
34712
  }
34706
- var CONTEXT_FILE_PATTERNS, IGNORED_DIRS2, MCP_CONFIG_PATTERNS, MCPH_CONFIG_PATTERNS;
34713
+ var CONTEXT_FILE_PATTERNS, IGNORED_DIRS2, ALLOWED_DOT_DIRS, MCP_CONFIG_PATTERNS, MCPH_CONFIG_PATTERNS, PREFIX_BYTES;
34707
34714
  var init_scanner = __esm({
34708
34715
  "src/core/scanner.ts"() {
34709
34716
  "use strict";
@@ -34759,6 +34766,20 @@ var init_scanner = __esm({
34759
34766
  "replit.md"
34760
34767
  ];
34761
34768
  IGNORED_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", "vendor"]);
34769
+ ALLOWED_DOT_DIRS = /* @__PURE__ */ new Set([
34770
+ ".claude",
34771
+ ".cursor",
34772
+ ".github",
34773
+ ".windsurf",
34774
+ ".clinerules",
34775
+ ".aide",
34776
+ ".amazonq",
34777
+ ".goose",
34778
+ ".junie",
34779
+ ".aiassistant",
34780
+ ".continue",
34781
+ ".vscode"
34782
+ ]);
34762
34783
  MCP_CONFIG_PATTERNS = [
34763
34784
  ".mcp.json",
34764
34785
  ".cursor/mcp.json",
@@ -34767,6 +34788,7 @@ var init_scanner = __esm({
34767
34788
  ".continue/mcpServers/*.json"
34768
34789
  ];
34769
34790
  MCPH_CONFIG_PATTERNS = [".mcph.json", ".mcph.local.json"];
34791
+ PREFIX_BYTES = 8192;
34770
34792
  }
34771
34793
  });
34772
34794
 
@@ -35814,10 +35836,10 @@ var require_src2 = __commonJS({
35814
35836
  var fs_1 = __require("fs");
35815
35837
  var debug_1 = __importDefault(require_src());
35816
35838
  var log = debug_1.default("@kwsites/file-exists");
35817
- function check2(path13, isFile, isDirectory2) {
35818
- log(`checking %s`, path13);
35839
+ function check2(path14, isFile, isDirectory2) {
35840
+ log(`checking %s`, path14);
35819
35841
  try {
35820
- const stat3 = fs_1.statSync(path13);
35842
+ const stat3 = fs_1.statSync(path14);
35821
35843
  if (stat3.isFile() && isFile) {
35822
35844
  log(`[OK] path represents a file`);
35823
35845
  return true;
@@ -35837,8 +35859,8 @@ var require_src2 = __commonJS({
35837
35859
  throw e;
35838
35860
  }
35839
35861
  }
35840
- function exists2(path13, type = exports.READABLE) {
35841
- return check2(path13, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
35862
+ function exists2(path14, type = exports.READABLE) {
35863
+ return check2(path14, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
35842
35864
  }
35843
35865
  exports.exists = exists2;
35844
35866
  exports.FILE = 1;
@@ -36431,8 +36453,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
36431
36453
  function forEachLineWithContent(input, callback) {
36432
36454
  return toLinesWithContent(input, true).map((line) => callback(line));
36433
36455
  }
36434
- function folderExists(path13) {
36435
- return (0, import_file_exists.exists)(path13, import_file_exists.FOLDER);
36456
+ function folderExists(path14) {
36457
+ return (0, import_file_exists.exists)(path14, import_file_exists.FOLDER);
36436
36458
  }
36437
36459
  function append(target, item) {
36438
36460
  if (Array.isArray(target)) {
@@ -36623,8 +36645,8 @@ function checkIsRepoRootTask() {
36623
36645
  commands,
36624
36646
  format: "utf-8",
36625
36647
  onError,
36626
- parser(path13) {
36627
- return /^\.(git)?$/.test(path13.trim());
36648
+ parser(path14) {
36649
+ return /^\.(git)?$/.test(path14.trim());
36628
36650
  }
36629
36651
  };
36630
36652
  }
@@ -36880,11 +36902,11 @@ function parseGrep(grep) {
36880
36902
  const paths = /* @__PURE__ */ new Set();
36881
36903
  const results = {};
36882
36904
  forEachLineWithContent(grep, (input) => {
36883
- const [path13, line, preview] = input.split(NULL);
36884
- paths.add(path13);
36885
- (results[path13] = results[path13] || []).push({
36905
+ const [path14, line, preview] = input.split(NULL);
36906
+ paths.add(path14);
36907
+ (results[path14] = results[path14] || []).push({
36886
36908
  line: asNumber(line),
36887
- path: path13,
36909
+ path: path14,
36888
36910
  preview
36889
36911
  });
36890
36912
  });
@@ -37181,14 +37203,14 @@ function hashObjectTask(filePath, write) {
37181
37203
  }
37182
37204
  return straightThroughStringTask(commands, true);
37183
37205
  }
37184
- function parseInit(bare, path13, text) {
37206
+ function parseInit(bare, path14, text) {
37185
37207
  const response = String(text).trim();
37186
37208
  let result;
37187
37209
  if (result = initResponseRegex.exec(response)) {
37188
- return new InitSummary(bare, path13, false, result[1]);
37210
+ return new InitSummary(bare, path14, false, result[1]);
37189
37211
  }
37190
37212
  if (result = reInitResponseRegex.exec(response)) {
37191
- return new InitSummary(bare, path13, true, result[1]);
37213
+ return new InitSummary(bare, path14, true, result[1]);
37192
37214
  }
37193
37215
  let gitDir = "";
37194
37216
  const tokens = response.split(" ");
@@ -37199,12 +37221,12 @@ function parseInit(bare, path13, text) {
37199
37221
  break;
37200
37222
  }
37201
37223
  }
37202
- return new InitSummary(bare, path13, /^re/i.test(response), gitDir);
37224
+ return new InitSummary(bare, path14, /^re/i.test(response), gitDir);
37203
37225
  }
37204
37226
  function hasBareCommand(command) {
37205
37227
  return command.includes(bareCommand);
37206
37228
  }
37207
- function initTask(bare = false, path13, customArgs) {
37229
+ function initTask(bare = false, path14, customArgs) {
37208
37230
  const commands = ["init", ...customArgs];
37209
37231
  if (bare && !hasBareCommand(commands)) {
37210
37232
  commands.splice(1, 0, bareCommand);
@@ -37213,7 +37235,7 @@ function initTask(bare = false, path13, customArgs) {
37213
37235
  commands,
37214
37236
  format: "utf-8",
37215
37237
  parser(text) {
37216
- return parseInit(commands.includes("--bare"), path13, text);
37238
+ return parseInit(commands.includes("--bare"), path14, text);
37217
37239
  }
37218
37240
  };
37219
37241
  }
@@ -37492,14 +37514,14 @@ function splitLine(result, lineStr) {
37492
37514
  default:
37493
37515
  return;
37494
37516
  }
37495
- function data(index, workingDir, path13) {
37517
+ function data(index, workingDir, path14) {
37496
37518
  const raw = `${index}${workingDir}`;
37497
37519
  const handler = parsers6.get(raw);
37498
37520
  if (handler) {
37499
- handler(result, path13);
37521
+ handler(result, path14);
37500
37522
  }
37501
37523
  if (raw !== "##" && raw !== "!!") {
37502
- result.files.push(new FileStatusSummary(path13, index, workingDir));
37524
+ result.files.push(new FileStatusSummary(path14, index, workingDir));
37503
37525
  }
37504
37526
  }
37505
37527
  }
@@ -37686,8 +37708,8 @@ function deleteBranchTask(branch, forceDelete = false) {
37686
37708
  return task;
37687
37709
  }
37688
37710
  function toPath(input) {
37689
- const path13 = input.trim().replace(/^["']|["']$/g, "");
37690
- return path13 && normalize2(path13);
37711
+ const path14 = input.trim().replace(/^["']|["']$/g, "");
37712
+ return path14 && normalize2(path14);
37691
37713
  }
37692
37714
  function checkIgnoreTask(paths) {
37693
37715
  return {
@@ -37826,8 +37848,8 @@ function stashListTask(opt = {}, customArgs) {
37826
37848
  parser: parser4
37827
37849
  };
37828
37850
  }
37829
- function addSubModuleTask(repo, path13) {
37830
- return subModuleTask(["add", repo, path13]);
37851
+ function addSubModuleTask(repo, path14) {
37852
+ return subModuleTask(["add", repo, path14]);
37831
37853
  }
37832
37854
  function initSubModuleTask(customArgs) {
37833
37855
  return subModuleTask(["init", ...customArgs]);
@@ -39090,9 +39112,9 @@ var init_esm2 = __esm({
39090
39112
  "src/lib/responses/InitSummary.ts"() {
39091
39113
  "use strict";
39092
39114
  InitSummary = class {
39093
- constructor(bare, path13, existing, gitDir) {
39115
+ constructor(bare, path14, existing, gitDir) {
39094
39116
  this.bare = bare;
39095
- this.path = path13;
39117
+ this.path = path14;
39096
39118
  this.existing = existing;
39097
39119
  this.gitDir = gitDir;
39098
39120
  }
@@ -39625,12 +39647,12 @@ var init_esm2 = __esm({
39625
39647
  "use strict";
39626
39648
  fromPathRegex = /^(.+)\0(.+)$/;
39627
39649
  FileStatusSummary = class {
39628
- constructor(path13, index, working_dir) {
39629
- this.path = path13;
39650
+ constructor(path14, index, working_dir) {
39651
+ this.path = path14;
39630
39652
  this.index = index;
39631
39653
  this.working_dir = working_dir;
39632
39654
  if (index === "R" || working_dir === "R") {
39633
- const detail = fromPathRegex.exec(path13) || [null, path13, path13];
39655
+ const detail = fromPathRegex.exec(path14) || [null, path14, path14];
39634
39656
  this.from = detail[2] || "";
39635
39657
  this.path = detail[1] || "";
39636
39658
  }
@@ -39892,9 +39914,9 @@ var init_esm2 = __esm({
39892
39914
  next
39893
39915
  );
39894
39916
  }
39895
- hashObject(path13, write) {
39917
+ hashObject(path14, write) {
39896
39918
  return this._runTask(
39897
- hashObjectTask(path13, write === true),
39919
+ hashObjectTask(path14, write === true),
39898
39920
  trailingFunctionArgument(arguments)
39899
39921
  );
39900
39922
  }
@@ -40531,8 +40553,8 @@ var init_esm2 = __esm({
40531
40553
  }
40532
40554
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
40533
40555
  };
40534
- Git2.prototype.submoduleAdd = function(repo, path13, then) {
40535
- return this._runTask(addSubModuleTask2(repo, path13), trailingFunctionArgument2(arguments));
40556
+ Git2.prototype.submoduleAdd = function(repo, path14, then) {
40557
+ return this._runTask(addSubModuleTask2(repo, path14), trailingFunctionArgument2(arguments));
40536
40558
  };
40537
40559
  Git2.prototype.submoduleUpdate = function(args2, then) {
40538
40560
  return this._runTask(
@@ -40777,6 +40799,19 @@ var init_esm2 = __esm({
40777
40799
  });
40778
40800
 
40779
40801
  // src/utils/git.ts
40802
+ import * as path3 from "node:path";
40803
+ function normalizePath(p2, projectRoot) {
40804
+ let cleaned = p2.replace(/\\/g, "/");
40805
+ if (projectRoot && path3.isAbsolute(p2)) {
40806
+ const rel = path3.relative(projectRoot, p2).replace(/\\/g, "/");
40807
+ if (rel && !rel.startsWith("..")) {
40808
+ cleaned = rel;
40809
+ } else {
40810
+ cleaned = cleaned.replace(/^[A-Za-z]:/, "");
40811
+ }
40812
+ }
40813
+ return path3.posix.normalize(cleaned);
40814
+ }
40780
40815
  function getGit(projectRoot) {
40781
40816
  if (!gitInstance || gitProjectRoot !== projectRoot) {
40782
40817
  gitInstance = esm_default(projectRoot);
@@ -40810,9 +40845,16 @@ async function getFileLastModified(projectRoot, filePath) {
40810
40845
  }
40811
40846
  }
40812
40847
  async function getCommitsSinceBatch(projectRoot, paths, since) {
40848
+ const out = /* @__PURE__ */ new Map();
40849
+ for (const p2 of paths) out.set(p2, 0);
40850
+ if (paths.length === 0) return out;
40813
40851
  const counts = /* @__PURE__ */ new Map();
40814
- for (const p2 of paths) counts.set(p2, 0);
40815
- if (paths.length === 0) return counts;
40852
+ const originalToNormalized = /* @__PURE__ */ new Map();
40853
+ for (const p2 of paths) {
40854
+ const n7 = normalizePath(p2, projectRoot);
40855
+ originalToNormalized.set(p2, n7);
40856
+ if (!counts.has(n7)) counts.set(n7, 0);
40857
+ }
40816
40858
  try {
40817
40859
  const git = getGit(projectRoot);
40818
40860
  const SENTINEL = "___CTXLINT_COMMIT___";
@@ -40825,8 +40867,7 @@ async function getCommitsSinceBatch(projectRoot, paths, since) {
40825
40867
  // format ("fatal: invalid --pretty format: ___CTXLINT_COMMIT___").
40826
40868
  `--format=%n${SENTINEL}`
40827
40869
  ]);
40828
- const normalize3 = (p2) => p2.replace(/\\/g, "/").replace(/^[A-Z]:/, "");
40829
- const requested = new Set(paths.map(normalize3));
40870
+ const requested = new Set(counts.keys());
40830
40871
  const lines = raw.split("\n");
40831
40872
  let inCommit = false;
40832
40873
  const seenThisCommit = /* @__PURE__ */ new Set();
@@ -40845,10 +40886,13 @@ async function getCommitsSinceBatch(projectRoot, paths, since) {
40845
40886
  if (!inCommit) continue;
40846
40887
  const trimmed2 = line.trim();
40847
40888
  if (!trimmed2) continue;
40848
- const changed = normalize3(trimmed2);
40889
+ const changed = normalizePath(trimmed2);
40849
40890
  for (const req of requested) {
40850
- const matchTarget = req.endsWith("/") ? req : req + "/";
40851
- if (changed === req || changed.startsWith(matchTarget) && (changed.length === matchTarget.length || changed[matchTarget.length] === "/")) {
40891
+ if (changed === req || req.endsWith("/") && changed.startsWith(req)) {
40892
+ seenThisCommit.add(req);
40893
+ continue;
40894
+ }
40895
+ if (changed.startsWith(req + "/")) {
40852
40896
  seenThisCommit.add(req);
40853
40897
  }
40854
40898
  }
@@ -40856,9 +40900,8 @@ async function getCommitsSinceBatch(projectRoot, paths, since) {
40856
40900
  if (inCommit) flush();
40857
40901
  } catch {
40858
40902
  }
40859
- const out = /* @__PURE__ */ new Map();
40860
- for (const p2 of paths) {
40861
- out.set(p2, counts.get(p2.replace(/\\/g, "/")) ?? 0);
40903
+ for (const [original, normalized] of originalToNormalized) {
40904
+ out.set(original, counts.get(normalized) ?? 0);
40862
40905
  }
40863
40906
  return out;
40864
40907
  }
@@ -42018,6 +42061,27 @@ var init_main = __esm({
42018
42061
  }
42019
42062
  });
42020
42063
 
42064
+ // src/utils/positions.ts
42065
+ function offsetToPosition(content, offset) {
42066
+ let line = 1;
42067
+ let column = 1;
42068
+ for (let i2 = 0; i2 < offset && i2 < content.length; i2++) {
42069
+ if (content[i2] === "\n") {
42070
+ line++;
42071
+ column = 1;
42072
+ } else {
42073
+ column++;
42074
+ }
42075
+ }
42076
+ return { line, column };
42077
+ }
42078
+ var init_positions = __esm({
42079
+ "src/utils/positions.ts"() {
42080
+ "use strict";
42081
+ init_define_WEB_FIRST_SEGMENTS();
42082
+ }
42083
+ });
42084
+
42021
42085
  // src/core/mcph-parser.ts
42022
42086
  async function parseMcphConfig(file2, projectRoot, scopeOverride) {
42023
42087
  const content = readFileContent(file2.absolutePath);
@@ -42094,19 +42158,6 @@ function nodePosition(content, node) {
42094
42158
  endColumn: end.column
42095
42159
  };
42096
42160
  }
42097
- function offsetToPosition(content, offset) {
42098
- let line = 1;
42099
- let column = 1;
42100
- for (let i2 = 0; i2 < offset && i2 < content.length; i2++) {
42101
- if (content[i2] === "\n") {
42102
- line++;
42103
- column = 1;
42104
- } else {
42105
- column++;
42106
- }
42107
- }
42108
- return { line, column };
42109
- }
42110
42161
  async function checkGitTracked2(filePath, projectRoot) {
42111
42162
  try {
42112
42163
  const git = getGit(projectRoot);
@@ -42133,6 +42184,7 @@ var init_mcph_parser = __esm({
42133
42184
  init_main();
42134
42185
  init_fs();
42135
42186
  init_git();
42187
+ init_positions();
42136
42188
  KNOWN_FIELDS = /* @__PURE__ */ new Set(["$schema", "version", "token", "apiBase", "servers", "blocked"]);
42137
42189
  }
42138
42190
  });
@@ -42363,7 +42415,7 @@ var require_levenshtein = __commonJS({
42363
42415
  });
42364
42416
 
42365
42417
  // src/core/checks/paths.ts
42366
- import * as path3 from "node:path";
42418
+ import * as path4 from "node:path";
42367
42419
  function getProjectFiles(projectRoot) {
42368
42420
  if (cachedProjectFiles?.root === projectRoot) return cachedProjectFiles.files;
42369
42421
  const files = getAllProjectFiles(projectRoot);
@@ -42376,10 +42428,10 @@ function resetPathsCache() {
42376
42428
  async function checkPaths(file2, projectRoot) {
42377
42429
  const issues = [];
42378
42430
  const projectFiles = getProjectFiles(projectRoot);
42379
- const contextDir = path3.dirname(file2.filePath);
42431
+ const contextDir = path4.dirname(file2.filePath);
42380
42432
  for (const ref of file2.references.paths) {
42381
42433
  const baseDir = ref.value.startsWith("./") || ref.value.startsWith("../") ? contextDir : projectRoot;
42382
- const resolvedPath = path3.resolve(baseDir, ref.value);
42434
+ const resolvedPath = path4.resolve(baseDir, ref.value);
42383
42435
  const normalizedRef = ref.value.replace(/\\/g, "/");
42384
42436
  if (normalizedRef.includes("*")) {
42385
42437
  const matches = await Ze(normalizedRef, { cwd: baseDir, nodir: false });
@@ -42397,7 +42449,7 @@ async function checkPaths(file2, projectRoot) {
42397
42449
  }
42398
42450
  const isDir = normalizedRef.endsWith("/");
42399
42451
  if (isDir) {
42400
- const dirPath = path3.resolve(baseDir, normalizedRef);
42452
+ const dirPath = path4.resolve(baseDir, normalizedRef);
42401
42453
  if (!isDirectory(dirPath)) {
42402
42454
  issues.push({
42403
42455
  severity: "error",
@@ -42442,12 +42494,12 @@ async function checkPaths(file2, projectRoot) {
42442
42494
  }
42443
42495
  function findClosestMatch(target, files) {
42444
42496
  const targetNorm = target.replace(/\\/g, "/");
42445
- const targetBase = path3.basename(targetNorm);
42497
+ const targetBase = path4.basename(targetNorm);
42446
42498
  let basenameMatch = null;
42447
42499
  let basenameDistance = Infinity;
42448
42500
  for (const file2 of files) {
42449
42501
  const fileNorm = file2.replace(/\\/g, "/");
42450
- if (path3.basename(fileNorm) === targetBase && fileNorm !== targetNorm) {
42502
+ if (path4.basename(fileNorm) === targetBase && fileNorm !== targetNorm) {
42451
42503
  const dist = levenshtein(targetNorm, fileNorm);
42452
42504
  if (dist < basenameDistance) {
42453
42505
  basenameDistance = dist;
@@ -42487,7 +42539,7 @@ var init_paths = __esm({
42487
42539
 
42488
42540
  // src/core/checks/commands.ts
42489
42541
  import * as fs4 from "node:fs";
42490
- import * as path4 from "node:path";
42542
+ import * as path5 from "node:path";
42491
42543
  function extractNpxPackage(cmd) {
42492
42544
  if (!/^npx\b/.test(cmd)) return null;
42493
42545
  const tokens = cmd.split(/\s+/).slice(1);
@@ -42554,7 +42606,7 @@ async function checkCommands(file2, projectRoot) {
42554
42606
  ...pkgJson.optionalDependencies
42555
42607
  };
42556
42608
  if (!(pkgName in allDeps)) {
42557
- const binPath = path4.join(projectRoot, "node_modules", ".bin", pkgName);
42609
+ const binPath = path5.join(projectRoot, "node_modules", ".bin", pkgName);
42558
42610
  try {
42559
42611
  fs4.accessSync(binPath);
42560
42612
  } catch {
@@ -42602,7 +42654,7 @@ async function checkCommands(file2, projectRoot) {
42602
42654
  ...pkgJson.optionalDependencies
42603
42655
  };
42604
42656
  if (!(tool in allDeps)) {
42605
- const binPath = path4.join(projectRoot, "node_modules", ".bin", tool);
42657
+ const binPath = path5.join(projectRoot, "node_modules", ".bin", tool);
42606
42658
  try {
42607
42659
  fs4.accessSync(binPath);
42608
42660
  } catch {
@@ -42621,7 +42673,7 @@ async function checkCommands(file2, projectRoot) {
42621
42673
  }
42622
42674
  function loadMakefile(projectRoot) {
42623
42675
  try {
42624
- return stripBom(fs4.readFileSync(path4.join(projectRoot, "Makefile"), "utf-8"));
42676
+ return stripBom(fs4.readFileSync(path5.join(projectRoot, "Makefile"), "utf-8"));
42625
42677
  } catch {
42626
42678
  return null;
42627
42679
  }
@@ -42642,13 +42694,13 @@ var init_commands = __esm({
42642
42694
  });
42643
42695
 
42644
42696
  // src/core/checks/staleness.ts
42645
- import * as path5 from "node:path";
42697
+ import * as path6 from "node:path";
42646
42698
  async function checkStaleness(file2, projectRoot) {
42647
42699
  const issues = [];
42648
42700
  if (!await isGitRepo(projectRoot)) {
42649
42701
  return issues;
42650
42702
  }
42651
- const relativePath = path5.relative(projectRoot, file2.filePath).replace(/\\/g, "/");
42703
+ const relativePath = path6.relative(projectRoot, file2.filePath).replace(/\\/g, "/");
42652
42704
  const lastModified = await getFileLastModified(projectRoot, relativePath);
42653
42705
  if (!lastModified || isNaN(lastModified.getTime())) {
42654
42706
  return issues;
@@ -42778,7 +42830,7 @@ var init_tokens2 = __esm({
42778
42830
 
42779
42831
  // src/core/checks/tier-tokens.ts
42780
42832
  import * as fs5 from "node:fs";
42781
- import * as path6 from "node:path";
42833
+ import * as path7 from "node:path";
42782
42834
  function hasPathsFrontmatter(content) {
42783
42835
  const lines = content.split("\n");
42784
42836
  if (lines[0]?.trim() !== "---") return false;
@@ -42830,9 +42882,9 @@ function computeSectionCosts(file2) {
42830
42882
  function loadSettingsSources(projectRoot) {
42831
42883
  const sources = [];
42832
42884
  const candidates = [
42833
- path6.join(projectRoot, ".claude", "settings.json"),
42834
- path6.join(projectRoot, ".claude", "settings.local.json"),
42835
- path6.join(process.env.HOME || process.env.USERPROFILE || "", ".claude", "settings.json")
42885
+ path7.join(projectRoot, ".claude", "settings.json"),
42886
+ path7.join(projectRoot, ".claude", "settings.local.json"),
42887
+ path7.join(process.env.HOME || process.env.USERPROFILE || "", ".claude", "settings.json")
42836
42888
  ];
42837
42889
  for (const p2 of candidates) {
42838
42890
  let content;
@@ -43002,7 +43054,7 @@ var init_similarity = __esm({
43002
43054
  });
43003
43055
 
43004
43056
  // src/core/checks/redundancy.ts
43005
- import * as path7 from "node:path";
43057
+ import * as path8 from "node:path";
43006
43058
  function compilePatterns(allDeps) {
43007
43059
  const compiled = [];
43008
43060
  for (const [pkg, mentions] of Object.entries(PACKAGE_TECH_MAP)) {
@@ -43081,7 +43133,7 @@ async function checkRedundancy(file2, projectRoot) {
43081
43133
  );
43082
43134
  if (dirMatch) {
43083
43135
  const dir = dirMatch[1].replace(/[`"]/g, "");
43084
- const fullPath = path7.resolve(projectRoot, dir);
43136
+ const fullPath = path8.resolve(projectRoot, dir);
43085
43137
  if (isDirectory(fullPath)) {
43086
43138
  issues.push({
43087
43139
  severity: "info",
@@ -44009,7 +44061,7 @@ var init_security = __esm({
44009
44061
 
44010
44062
  // src/core/checks/mcp/commands.ts
44011
44063
  import * as fs6 from "node:fs";
44012
- import * as path8 from "node:path";
44064
+ import * as path9 from "node:path";
44013
44065
  async function checkMcpCommands(config2, projectRoot) {
44014
44066
  const issues = [];
44015
44067
  if (config2.parseErrors.length > 0) return issues;
@@ -44026,7 +44078,7 @@ async function checkMcpCommands(config2, projectRoot) {
44026
44078
  });
44027
44079
  }
44028
44080
  if (LOCAL_PATH_PATTERN.test(server2.command)) {
44029
- const resolved = path8.resolve(projectRoot, server2.command);
44081
+ const resolved = path9.resolve(projectRoot, server2.command);
44030
44082
  if (!fileExistsSafe(resolved)) {
44031
44083
  issues.push({
44032
44084
  severity: "warning",
@@ -44041,7 +44093,7 @@ async function checkMcpCommands(config2, projectRoot) {
44041
44093
  for (const arg of server2.args) {
44042
44094
  if (URL_PREFIX.test(arg)) continue;
44043
44095
  if (LOCAL_PATH_PATTERN.test(arg) || FILE_PATH_PATTERN.test(arg)) {
44044
- const resolved = path8.resolve(projectRoot, arg);
44096
+ const resolved = path9.resolve(projectRoot, arg);
44045
44097
  if (!fileExistsSafe(resolved)) {
44046
44098
  issues.push({
44047
44099
  severity: "warning",
@@ -44124,7 +44176,7 @@ function findTypeLine(content, serverName) {
44124
44176
  if (enteredObject && depth === 0) return null;
44125
44177
  }
44126
44178
  }
44127
- if (enteredObject && lines[i2].includes('"type"') && lines[i2].includes('"sse"')) {
44179
+ if (enteredObject && /"type"\s*:\s*"sse"/.test(lines[i2])) {
44128
44180
  return i2 + 1;
44129
44181
  }
44130
44182
  }
@@ -44741,11 +44793,11 @@ var init_lists = __esm({
44741
44793
  });
44742
44794
 
44743
44795
  // src/core/checks/mcph/gitignore.ts
44744
- import * as path9 from "node:path";
44796
+ import * as path10 from "node:path";
44745
44797
  async function checkMcphGitignore(config2, _projectRoot) {
44746
44798
  const issues = [];
44747
44799
  if (config2.scope === "project-local" && !config2.isGitignored) {
44748
- const basename4 = path9.basename(config2.filePath);
44800
+ const basename4 = path10.basename(config2.filePath);
44749
44801
  issues.push({
44750
44802
  severity: "error",
44751
44803
  check: "mcph-gitignore",
@@ -44766,7 +44818,7 @@ var init_gitignore = __esm({
44766
44818
 
44767
44819
  // src/core/session-parser.ts
44768
44820
  import { readFile } from "node:fs/promises";
44769
- import { readFileSync as readFileSync5 } from "node:fs";
44821
+ import { readFileSync as readFileSync4 } from "node:fs";
44770
44822
  import { dirname as dirname2, resolve as resolve4 } from "node:path";
44771
44823
  import { fileURLToPath } from "node:url";
44772
44824
  function loadWebFirstSegments() {
@@ -44775,7 +44827,7 @@ function loadWebFirstSegments() {
44775
44827
  }
44776
44828
  const __dir = dirname2(fileURLToPath(import.meta.url));
44777
44829
  const fixturePath = resolve4(__dir, "../../tests/fixtures/web-first-segments.json");
44778
- const raw = JSON.parse(readFileSync5(fixturePath, "utf-8"));
44830
+ const raw = JSON.parse(readFileSync4(fixturePath, "utf-8"));
44779
44831
  const list = Array.isArray(raw) ? raw : raw.segments;
44780
44832
  return new Set(list);
44781
44833
  }
@@ -45089,7 +45141,7 @@ var init_session_scanner = __esm({
45089
45141
 
45090
45142
  // src/core/checks/session/missing-secret.ts
45091
45143
  import { resolve as resolve6, basename as basename3 } from "node:path";
45092
- function normalizePath(p2) {
45144
+ function normalizePath2(p2) {
45093
45145
  return resolve6(p2).replace(/\\/g, "/").toLowerCase();
45094
45146
  }
45095
45147
  async function checkMissingSecret(ctx) {
@@ -45111,10 +45163,10 @@ async function checkMissingSecret(ctx) {
45111
45163
  byName.get(s.name).add(s.project);
45112
45164
  if (s.repo) byName.get(s.name).add(s.repo);
45113
45165
  }
45114
- const currentNorm = normalizePath(ctx.currentProject);
45166
+ const currentNorm = normalizePath2(ctx.currentProject);
45115
45167
  const currentBase = basename3(currentNorm);
45116
45168
  for (const [secretName, projects] of byName) {
45117
- const normalizedProjects = [...projects].map(normalizePath);
45169
+ const normalizedProjects = [...projects].map(normalizePath2);
45118
45170
  const currentHas = normalizedProjects.some((p2) => {
45119
45171
  if (p2 === currentNorm) return true;
45120
45172
  const lastSegment = p2.split("/").pop() || "";
@@ -45122,7 +45174,7 @@ async function checkMissingSecret(ctx) {
45122
45174
  });
45123
45175
  if (currentHas) continue;
45124
45176
  const siblingMatches = ctx.siblings.filter((sib) => {
45125
- const sibNorm = normalizePath(sib.path);
45177
+ const sibNorm = normalizePath2(sib.path);
45126
45178
  const sibBase = basename3(sibNorm);
45127
45179
  return normalizedProjects.some((p2) => {
45128
45180
  if (p2 === sibNorm) return true;
@@ -45150,7 +45202,7 @@ var init_missing_secret = __esm({
45150
45202
  "src/core/checks/session/missing-secret.ts"() {
45151
45203
  "use strict";
45152
45204
  init_define_WEB_FIRST_SEGMENTS();
45153
- SECRET_SET_PATTERN = /gh\s+secret\s+set\s+(\S+)\s+(?:--repo\s+(\S+)|.*-b\s+)/;
45205
+ SECRET_SET_PATTERN = /gh\s+secret\s+set\s+(\S+)\s+--repo\s+(\S+)/;
45154
45206
  SECRET_SET_SIMPLE = /gh\s+secret\s+set\s+(\S+)/;
45155
45207
  }
45156
45208
  });
@@ -45305,14 +45357,14 @@ var init_missing_workflow = __esm({
45305
45357
 
45306
45358
  // src/core/checks/session/stale-memory.ts
45307
45359
  import { existsSync as existsSync4 } from "node:fs";
45308
- import { resolve as resolve7, isAbsolute } from "node:path";
45360
+ import { resolve as resolve7, isAbsolute as isAbsolute2 } from "node:path";
45309
45361
  function resolveRef2(ref, projectRoot) {
45310
45362
  if (ref.startsWith("~/") || ref === "~") {
45311
45363
  const home2 = process.env.HOME || process.env.USERPROFILE;
45312
45364
  if (!home2) return null;
45313
45365
  return ref === "~" ? home2 : resolve7(home2, ref.slice(2));
45314
45366
  }
45315
- if (isAbsolute(ref)) return ref;
45367
+ if (isAbsolute2(ref)) return ref;
45316
45368
  return resolve7(projectRoot, ref);
45317
45369
  }
45318
45370
  async function checkStaleMemory(ctx) {
@@ -45991,15 +46043,26 @@ var init_ignore_rules = __esm({
45991
46043
  }
45992
46044
  });
45993
46045
 
46046
+ // src/core/types.ts
46047
+ var SESSION_AUDIT_LABEL, SESSION_AUDIT_PATH_MARKER;
46048
+ var init_types3 = __esm({
46049
+ "src/core/types.ts"() {
46050
+ "use strict";
46051
+ init_define_WEB_FIRST_SEGMENTS();
46052
+ SESSION_AUDIT_LABEL = "~/.claude/ (session audit)";
46053
+ SESSION_AUDIT_PATH_MARKER = "(session audit)";
46054
+ }
46055
+ });
46056
+
45994
46057
  // src/version.ts
45995
- import { readFileSync as readFileSync6 } from "node:fs";
46058
+ import { readFileSync as readFileSync5 } from "node:fs";
45996
46059
  import { resolve as resolve9, dirname as dirname4 } from "node:path";
45997
46060
  import { fileURLToPath as fileURLToPath2 } from "node:url";
45998
46061
  function loadVersion() {
45999
- if (true) return "0.12.0";
46062
+ if (true) return "0.12.1";
46000
46063
  const __dir = dirname4(fileURLToPath2(import.meta.url));
46001
46064
  const pkgPath = resolve9(__dir, "../package.json");
46002
- const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
46065
+ const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
46003
46066
  return pkg.version;
46004
46067
  }
46005
46068
  var VERSION;
@@ -46251,7 +46314,7 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
46251
46314
  const sessionResults = await Promise.all(sessionPromises);
46252
46315
  const sessionIssues = sessionResults.flat();
46253
46316
  fileResults.push({
46254
- path: "~/.claude/ (session audit)",
46317
+ path: SESSION_AUDIT_LABEL,
46255
46318
  isSymlink: false,
46256
46319
  tokens: 0,
46257
46320
  lines: 0,
@@ -46363,6 +46426,7 @@ var init_audit = __esm({
46363
46426
  init_ci_secrets();
46364
46427
  init_content_secrets();
46365
46428
  init_ignore_rules();
46429
+ init_types3();
46366
46430
  init_version2();
46367
46431
  ALL_CHECKS = [
46368
46432
  "paths",
@@ -47006,7 +47070,8 @@ function applyFixes(result, options = {}) {
47006
47070
  const lineIdx = lineNum - 1;
47007
47071
  if (lineIdx < 0 || lineIdx >= lines.length) continue;
47008
47072
  let line = lines[lineIdx];
47009
- for (const fix of lineFixes) {
47073
+ const orderedLineFixes = [...lineFixes].sort((a, b2) => b2.oldText.length - a.oldText.length);
47074
+ for (const fix of orderedLineFixes) {
47010
47075
  if (line.includes(fix.oldText)) {
47011
47076
  line = line.replaceAll(fix.oldText, fix.newText);
47012
47077
  perFileFixCount++;
@@ -47051,7 +47116,7 @@ var init_fixer = __esm({
47051
47116
 
47052
47117
  // src/core/config.ts
47053
47118
  import * as fs8 from "node:fs";
47054
- import * as path10 from "node:path";
47119
+ import * as path11 from "node:path";
47055
47120
  function formatJsonError(content, err) {
47056
47121
  const msg = err instanceof Error ? err.message : String(err);
47057
47122
  const posMatch = msg.match(/position (\d+)/);
@@ -47066,17 +47131,8 @@ function formatJsonError(content, err) {
47066
47131
  return msg;
47067
47132
  }
47068
47133
  function posToLineCol(content, pos) {
47069
- let line = 1;
47070
- let col = 1;
47071
- for (let i2 = 0; i2 < pos && i2 < content.length; i2++) {
47072
- if (content[i2] === "\n") {
47073
- line++;
47074
- col = 1;
47075
- } else {
47076
- col++;
47077
- }
47078
- }
47079
- return `line ${line}, column ${col}`;
47134
+ const { line, column } = offsetToPosition(content, pos);
47135
+ return `line ${line}, column ${column}`;
47080
47136
  }
47081
47137
  function findFirstErrorPos(content, errMsg) {
47082
47138
  const snippetMatch = errMsg.match(/\.\.\."([^"]+(?:"[^"]*)*?)"\s+is not valid JSON/);
@@ -47138,11 +47194,27 @@ function parseConfigContent(content, source) {
47138
47194
  );
47139
47195
  }
47140
47196
  warnUnknownKeys(parsed, source);
47197
+ warnIgnoreRulePathPatternMisuse(parsed, source);
47141
47198
  return parsed;
47142
47199
  }
47200
+ function warnIgnoreRulePathPatternMisuse(config2, source) {
47201
+ if (!config2 || typeof config2 !== "object" || Array.isArray(config2)) return;
47202
+ const rules = config2.ignoreRules;
47203
+ if (!Array.isArray(rules)) return;
47204
+ for (const r2 of rules) {
47205
+ if (!r2 || typeof r2 !== "object") continue;
47206
+ const rec = r2;
47207
+ if (typeof rec.pathPattern !== "string") continue;
47208
+ if (rec.check === "session-stale-memory") continue;
47209
+ const checkName = typeof rec.check === "string" ? rec.check : "<unknown>";
47210
+ console.error(
47211
+ `Warning: ignoreRule with pathPattern is only honored for "session-stale-memory" (got "${checkName}") in ${source}`
47212
+ );
47213
+ }
47214
+ }
47143
47215
  function loadConfig(projectRoot) {
47144
47216
  for (const filename of CONFIG_FILENAMES) {
47145
- const filePath = path10.join(projectRoot, filename);
47217
+ const filePath = path11.join(projectRoot, filename);
47146
47218
  let content;
47147
47219
  try {
47148
47220
  content = stripBom(fs8.readFileSync(filePath, "utf-8"));
@@ -47170,6 +47242,7 @@ var init_config2 = __esm({
47170
47242
  init_define_WEB_FIRST_SEGMENTS();
47171
47243
  import_fast_levenshtein2 = __toESM(require_levenshtein(), 1);
47172
47244
  init_fs();
47245
+ init_positions();
47173
47246
  levenshtein2 = import_fast_levenshtein2.default.get;
47174
47247
  KNOWN_CONFIG_KEYS = [
47175
47248
  "checks",
@@ -47198,7 +47271,7 @@ __export(server_exports, {
47198
47271
  server: () => server,
47199
47272
  startServer: () => startServer
47200
47273
  });
47201
- import * as path11 from "node:path";
47274
+ import * as path12 from "node:path";
47202
47275
  function describeDisallowed(rawPath) {
47203
47276
  const m = rawPath.match(PATH_DISALLOWED);
47204
47277
  if (!m) return "unknown";
@@ -47211,7 +47284,7 @@ function validateProjectPath(rawPath) {
47211
47284
  `projectPath contains disallowed character ${describeDisallowed(rawPath)} (control chars and shell metacharacters are rejected)`
47212
47285
  );
47213
47286
  }
47214
- const resolved = path11.resolve(rawPath);
47287
+ const resolved = path12.resolve(rawPath);
47215
47288
  if (!isDirectory(resolved)) {
47216
47289
  throw new Error("projectPath is not an existing directory");
47217
47290
  }
@@ -47232,10 +47305,10 @@ function validateFilePathInput(rawPath) {
47232
47305
  }
47233
47306
  }
47234
47307
  function resolveWithinRoot(filePath, root) {
47235
- const resolvedRoot = path11.resolve(root);
47236
- const resolved = path11.resolve(resolvedRoot, filePath);
47237
- const rel = path11.relative(resolvedRoot, resolved);
47238
- if (rel.startsWith("..") || path11.isAbsolute(rel)) {
47308
+ const resolvedRoot = path12.resolve(root);
47309
+ const resolved = path12.resolve(resolvedRoot, filePath);
47310
+ const rel = path12.relative(resolvedRoot, resolved);
47311
+ if (rel.startsWith("..") || path12.isAbsolute(rel)) {
47239
47312
  throw new Error("path escapes the project root");
47240
47313
  }
47241
47314
  return resolved;
@@ -48750,7 +48823,7 @@ var require_command = __commonJS({
48750
48823
  init_define_WEB_FIRST_SEGMENTS();
48751
48824
  var EventEmitter2 = __require("node:events").EventEmitter;
48752
48825
  var childProcess = __require("node:child_process");
48753
- var path13 = __require("node:path");
48826
+ var path14 = __require("node:path");
48754
48827
  var fs10 = __require("node:fs");
48755
48828
  var process11 = __require("node:process");
48756
48829
  var { Argument: Argument2, humanReadableArgName } = require_argument();
@@ -49763,9 +49836,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
49763
49836
  let launchWithNode = false;
49764
49837
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
49765
49838
  function findFile(baseDir, baseName) {
49766
- const localBin = path13.resolve(baseDir, baseName);
49839
+ const localBin = path14.resolve(baseDir, baseName);
49767
49840
  if (fs10.existsSync(localBin)) return localBin;
49768
- if (sourceExt.includes(path13.extname(baseName))) return void 0;
49841
+ if (sourceExt.includes(path14.extname(baseName))) return void 0;
49769
49842
  const foundExt = sourceExt.find(
49770
49843
  (ext) => fs10.existsSync(`${localBin}${ext}`)
49771
49844
  );
@@ -49783,17 +49856,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
49783
49856
  } catch {
49784
49857
  resolvedScriptPath = this._scriptPath;
49785
49858
  }
49786
- executableDir = path13.resolve(
49787
- path13.dirname(resolvedScriptPath),
49859
+ executableDir = path14.resolve(
49860
+ path14.dirname(resolvedScriptPath),
49788
49861
  executableDir
49789
49862
  );
49790
49863
  }
49791
49864
  if (executableDir) {
49792
49865
  let localFile = findFile(executableDir, executableFile);
49793
49866
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
49794
- const legacyName = path13.basename(
49867
+ const legacyName = path14.basename(
49795
49868
  this._scriptPath,
49796
- path13.extname(this._scriptPath)
49869
+ path14.extname(this._scriptPath)
49797
49870
  );
49798
49871
  if (legacyName !== this._name) {
49799
49872
  localFile = findFile(
@@ -49804,7 +49877,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
49804
49877
  }
49805
49878
  executableFile = localFile || executableFile;
49806
49879
  }
49807
- launchWithNode = sourceExt.includes(path13.extname(executableFile));
49880
+ launchWithNode = sourceExt.includes(path14.extname(executableFile));
49808
49881
  let proc;
49809
49882
  if (process11.platform !== "win32") {
49810
49883
  if (launchWithNode) {
@@ -50719,7 +50792,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
50719
50792
  * @return {Command}
50720
50793
  */
50721
50794
  nameFromFilename(filename) {
50722
- this._name = path13.basename(filename, path13.extname(filename));
50795
+ this._name = path14.basename(filename, path14.extname(filename));
50723
50796
  return this;
50724
50797
  }
50725
50798
  /**
@@ -50733,9 +50806,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
50733
50806
  * @param {string} [path]
50734
50807
  * @return {(string|null|Command)}
50735
50808
  */
50736
- executableDir(path14) {
50737
- if (path14 === void 0) return this._executableDir;
50738
- this._executableDir = path14;
50809
+ executableDir(path15) {
50810
+ if (path15 === void 0) return this._executableDir;
50811
+ this._executableDir = path15;
50739
50812
  return this;
50740
50813
  }
50741
50814
  /**
@@ -54087,7 +54160,7 @@ var init_ora = __esm({
54087
54160
  function classifyFile(f) {
54088
54161
  if (f.path === "(project)") return "context";
54089
54162
  if (f.path === "(mcp)") return "mcp";
54090
- if (f.path.includes("session audit")) return "session";
54163
+ if (f.path.includes(SESSION_AUDIT_PATH_MARKER)) return "session";
54091
54164
  for (const issue2 of f.issues) {
54092
54165
  if (issue2.check.startsWith("session-")) return "session";
54093
54166
  if (issue2.check.startsWith("mcph-")) return "mcph";
@@ -54101,7 +54174,7 @@ function classifyFile(f) {
54101
54174
  return "context";
54102
54175
  }
54103
54176
  function isSyntheticBucket(p2) {
54104
- return p2.startsWith("(") || p2.includes("session audit");
54177
+ return p2.startsWith("(") || p2.includes(SESSION_AUDIT_PATH_MARKER);
54105
54178
  }
54106
54179
  function formatText(result, verbose = false) {
54107
54180
  const lines = [];
@@ -54515,6 +54588,7 @@ var init_reporter = __esm({
54515
54588
  "use strict";
54516
54589
  init_define_WEB_FIRST_SEGMENTS();
54517
54590
  init_source();
54591
+ init_types3();
54518
54592
  GROUP_ORDER = ["context", "mcp", "mcph", "session"];
54519
54593
  GROUP_LABELS = {
54520
54594
  context: "Context Files",
@@ -54537,7 +54611,7 @@ __export(cli_exports, {
54537
54611
  runCli: () => runCli
54538
54612
  });
54539
54613
  import * as fs9 from "node:fs";
54540
- import * as path12 from "node:path";
54614
+ import * as path13 from "node:path";
54541
54615
  function validateCheckNames(names, source) {
54542
54616
  const invalid = names.filter((n7) => n7 && !VALID_CHECKS.has(n7));
54543
54617
  if (invalid.length > 0) {
@@ -54560,7 +54634,7 @@ async function runCli() {
54560
54634
  "Upgrade mcph-config/prefer-env-token from warning to error (strict env-var-only posture)",
54561
54635
  false
54562
54636
  ).option("--session", "Run session audit checks (cross-project consistency)", false).option("--session-only", "Run only session checks, skip context and MCP checks", false).option("--watch", "Re-lint on context file changes", false).action(async (projectPath, opts) => {
54563
- const resolvedPath = path12.resolve(projectPath);
54637
+ const resolvedPath = path13.resolve(projectPath);
54564
54638
  const { config: config2, options, activeChecks } = resolveSession(resolvedPath, opts);
54565
54639
  const spinner = options.format === "text" && !options.quiet ? ora("Scanning for context files...").start() : void 0;
54566
54640
  try {
@@ -54676,43 +54750,43 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
54676
54750
  const chalk2 = (await Promise.resolve().then(() => (init_source(), source_exports))).default;
54677
54751
  console.log(chalk2.dim("\nWatching for changes... (Ctrl+C to stop)\n"));
54678
54752
  const watchPaths = [
54679
- path12.join(resolvedPath, "CLAUDE.md"),
54680
- path12.join(resolvedPath, "CLAUDE.local.md"),
54681
- path12.join(resolvedPath, "AGENTS.md"),
54682
- path12.join(resolvedPath, "AGENT.md"),
54683
- path12.join(resolvedPath, ".cursorrules"),
54684
- path12.join(resolvedPath, ".windsurfrules"),
54685
- path12.join(resolvedPath, ".clinerules"),
54686
- path12.join(resolvedPath, ".aiderules"),
54687
- path12.join(resolvedPath, ".continuerules"),
54688
- path12.join(resolvedPath, ".rules"),
54689
- path12.join(resolvedPath, ".goosehints"),
54690
- path12.join(resolvedPath, "GEMINI.md"),
54691
- path12.join(resolvedPath, "replit.md"),
54692
- path12.join(resolvedPath, "package.json"),
54693
- path12.join(resolvedPath, ".mcp.json"),
54753
+ path13.join(resolvedPath, "CLAUDE.md"),
54754
+ path13.join(resolvedPath, "CLAUDE.local.md"),
54755
+ path13.join(resolvedPath, "AGENTS.md"),
54756
+ path13.join(resolvedPath, "AGENT.md"),
54757
+ path13.join(resolvedPath, ".cursorrules"),
54758
+ path13.join(resolvedPath, ".windsurfrules"),
54759
+ path13.join(resolvedPath, ".clinerules"),
54760
+ path13.join(resolvedPath, ".aiderules"),
54761
+ path13.join(resolvedPath, ".continuerules"),
54762
+ path13.join(resolvedPath, ".rules"),
54763
+ path13.join(resolvedPath, ".goosehints"),
54764
+ path13.join(resolvedPath, "GEMINI.md"),
54765
+ path13.join(resolvedPath, "replit.md"),
54766
+ path13.join(resolvedPath, "package.json"),
54767
+ path13.join(resolvedPath, ".mcp.json"),
54694
54768
  // mcph configs — without these, edits to the .mcph.json the user is
54695
54769
  // actively iterating on don't trigger a re-lint.
54696
- path12.join(resolvedPath, ".mcph.json"),
54697
- path12.join(resolvedPath, ".mcph.local.json"),
54770
+ path13.join(resolvedPath, ".mcph.json"),
54771
+ path13.join(resolvedPath, ".mcph.local.json"),
54698
54772
  // ctxlint config — re-resolve on edit so threshold changes,
54699
54773
  // ignore-list edits, and check-list overrides take effect without
54700
54774
  // restarting the watcher.
54701
- path12.join(resolvedPath, ".ctxlintrc"),
54702
- path12.join(resolvedPath, ".ctxlintrc.json")
54775
+ path13.join(resolvedPath, ".ctxlintrc"),
54776
+ path13.join(resolvedPath, ".ctxlintrc.json")
54703
54777
  ];
54704
54778
  const watchDirs = [
54705
- path12.join(resolvedPath, ".claude"),
54706
- path12.join(resolvedPath, ".cursor"),
54707
- path12.join(resolvedPath, ".github"),
54708
- path12.join(resolvedPath, ".windsurf"),
54709
- path12.join(resolvedPath, ".aide"),
54710
- path12.join(resolvedPath, ".amazonq"),
54711
- path12.join(resolvedPath, ".goose"),
54712
- path12.join(resolvedPath, ".junie"),
54713
- path12.join(resolvedPath, ".aiassistant"),
54714
- path12.join(resolvedPath, ".continue"),
54715
- path12.join(resolvedPath, ".vscode")
54779
+ path13.join(resolvedPath, ".claude"),
54780
+ path13.join(resolvedPath, ".cursor"),
54781
+ path13.join(resolvedPath, ".github"),
54782
+ path13.join(resolvedPath, ".windsurf"),
54783
+ path13.join(resolvedPath, ".aide"),
54784
+ path13.join(resolvedPath, ".amazonq"),
54785
+ path13.join(resolvedPath, ".goose"),
54786
+ path13.join(resolvedPath, ".junie"),
54787
+ path13.join(resolvedPath, ".aiassistant"),
54788
+ path13.join(resolvedPath, ".continue"),
54789
+ path13.join(resolvedPath, ".vscode")
54716
54790
  ];
54717
54791
  const watchers = [];
54718
54792
  let debounceTimer = null;
@@ -54798,9 +54872,9 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
54798
54872
  }
54799
54873
  });
54800
54874
  program2.command("init").description("Set up a git pre-commit hook that runs ctxlint --strict").argument("[path]", "Project directory", ".").action(async (projectPath) => {
54801
- const resolvedRoot = path12.resolve(projectPath);
54802
- const gitDir = path12.join(resolvedRoot, ".git");
54803
- const hooksDir = path12.join(gitDir, "hooks");
54875
+ const resolvedRoot = path13.resolve(projectPath);
54876
+ const gitDir = path13.join(resolvedRoot, ".git");
54877
+ const hooksDir = path13.join(gitDir, "hooks");
54804
54878
  if (!fs9.existsSync(gitDir)) {
54805
54879
  console.error('Error: not a git repository. Run "git init" first.');
54806
54880
  process.exit(1);
@@ -54808,7 +54882,7 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
54808
54882
  if (!fs9.existsSync(hooksDir)) {
54809
54883
  fs9.mkdirSync(hooksDir, { recursive: true });
54810
54884
  }
54811
- const hookPath = path12.join(hooksDir, "pre-commit");
54885
+ const hookPath = path13.join(hooksDir, "pre-commit");
54812
54886
  const fullHookContent = `#!/bin/sh
54813
54887
  # ctxlint pre-commit hook
54814
54888
  npx @yawlabs/ctxlint@${VERSION} --strict
@@ -54844,7 +54918,7 @@ async function promptYesNo(question) {
54844
54918
  }
54845
54919
  }
54846
54920
  function resolveSession(resolvedPath, opts) {
54847
- const configPath = opts.config ? path12.resolve(opts.config) : void 0;
54921
+ const configPath = opts.config ? path13.resolve(opts.config) : void 0;
54848
54922
  const config2 = configPath ? loadConfigFromPath(configPath) : loadConfig(resolvedPath);
54849
54923
  const mcpGlobal = opts.mcpGlobal || config2?.mcpGlobal || false;
54850
54924
  const mcpOnly = opts.mcpOnly || config2?.mcpOnly || false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yawlabs/ctxlint",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "mcpName": "io.github.YawLabs/ctxlint",
5
5
  "description": "Lint your AI agent context files, MCP server configs, and session data against your actual codebase",
6
6
  "bin": {