@yawlabs/ctxlint 0.12.2 → 0.13.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
@@ -461,8 +461,8 @@ var init_parseUtil = __esm({
461
461
  init_errors();
462
462
  init_en();
463
463
  makeIssue = (params) => {
464
- const { data, path: path14, errorMaps, issueData } = params;
465
- const fullPath = [...path14, ...issueData.path || []];
464
+ const { data, path: path17, errorMaps, issueData } = params;
465
+ const fullPath = [...path17, ...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, path14, key) {
748
+ constructor(parent, value, path17, key) {
749
749
  this._cachedPath = [];
750
750
  this.parent = parent;
751
751
  this.data = value;
752
- this._path = path14;
752
+ this._path = path17;
753
753
  this._key = key;
754
754
  }
755
755
  get path() {
@@ -4257,10 +4257,10 @@ function mergeDefs(...defs) {
4257
4257
  function cloneDef(schema) {
4258
4258
  return mergeDefs(schema._zod.def);
4259
4259
  }
4260
- function getElementAtPath(obj, path14) {
4261
- if (!path14)
4260
+ function getElementAtPath(obj, path17) {
4261
+ if (!path17)
4262
4262
  return obj;
4263
- return path14.reduce((acc, key) => acc?.[key], obj);
4263
+ return path17.reduce((acc, key) => acc?.[key], obj);
4264
4264
  }
4265
4265
  function promiseAllObject(promisesObj) {
4266
4266
  const keys = Object.keys(promisesObj);
@@ -4572,11 +4572,11 @@ function aborted(x3, startIndex = 0) {
4572
4572
  }
4573
4573
  return false;
4574
4574
  }
4575
- function prefixIssues(path14, issues) {
4575
+ function prefixIssues(path17, issues) {
4576
4576
  return issues.map((iss) => {
4577
4577
  var _a3;
4578
4578
  (_a3 = iss).path ?? (_a3.path = []);
4579
- iss.path.unshift(path14);
4579
+ iss.path.unshift(path17);
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, path14 = []) => {
4822
+ const processError = (error50, path17 = []) => {
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 = [...path14, ...issue2.path];
4832
+ const fullpath = [...path17, ...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 path14 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
4865
- for (const seg of path14) {
4864
+ const path17 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
4865
+ for (const seg of path17) {
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 path14 = ref.slice(1).split("/").filter(Boolean);
17894
- if (path14.length === 0) {
17893
+ const path17 = ref.slice(1).split("/").filter(Boolean);
17894
+ if (path17.length === 0) {
17895
17895
  return ctx.rootSchema;
17896
17896
  }
17897
17897
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
17898
- if (path14[0] === defsKey) {
17899
- const key = path14[1];
17898
+ if (path17[0] === defsKey) {
17899
+ const key = path17[1];
17900
17900
  if (!key || !ctx.defs[key]) {
17901
17901
  throw new Error(`Reference not found: ${ref}`);
17902
17902
  }
@@ -22404,7 +22404,7 @@ var init_protocol = __esm({
22404
22404
  return;
22405
22405
  }
22406
22406
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
22407
- await new Promise((resolve12) => setTimeout(resolve12, pollInterval));
22407
+ await new Promise((resolve14) => setTimeout(resolve14, pollInterval));
22408
22408
  options?.signal?.throwIfAborted();
22409
22409
  }
22410
22410
  } catch (error49) {
@@ -22421,7 +22421,7 @@ var init_protocol = __esm({
22421
22421
  */
22422
22422
  request(request, resultSchema, options) {
22423
22423
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
22424
- return new Promise((resolve12, reject) => {
22424
+ return new Promise((resolve14, reject) => {
22425
22425
  const earlyReject = (error49) => {
22426
22426
  reject(error49);
22427
22427
  };
@@ -22499,7 +22499,7 @@ var init_protocol = __esm({
22499
22499
  if (!parseResult.success) {
22500
22500
  reject(parseResult.error);
22501
22501
  } else {
22502
- resolve12(parseResult.data);
22502
+ resolve14(parseResult.data);
22503
22503
  }
22504
22504
  } catch (error49) {
22505
22505
  reject(error49);
@@ -22760,12 +22760,12 @@ var init_protocol = __esm({
22760
22760
  }
22761
22761
  } catch {
22762
22762
  }
22763
- return new Promise((resolve12, reject) => {
22763
+ return new Promise((resolve14, reject) => {
22764
22764
  if (signal.aborted) {
22765
22765
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
22766
22766
  return;
22767
22767
  }
22768
- const timeoutId = setTimeout(resolve12, interval);
22768
+ const timeoutId = setTimeout(resolve14, interval);
22769
22769
  signal.addEventListener("abort", () => {
22770
22770
  clearTimeout(timeoutId);
22771
22771
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -25813,7 +25813,7 @@ var require_compile = __commonJS({
25813
25813
  const schOrFunc = root.refs[ref];
25814
25814
  if (schOrFunc)
25815
25815
  return schOrFunc;
25816
- let _sch = resolve12.call(this, root, ref);
25816
+ let _sch = resolve14.call(this, root, ref);
25817
25817
  if (_sch === void 0) {
25818
25818
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
25819
25819
  const { schemaId } = this.opts;
@@ -25840,7 +25840,7 @@ var require_compile = __commonJS({
25840
25840
  function sameSchemaEnv(s1, s2) {
25841
25841
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
25842
25842
  }
25843
- function resolve12(root, ref) {
25843
+ function resolve14(root, ref) {
25844
25844
  let sch;
25845
25845
  while (typeof (sch = this.refs[ref]) == "string")
25846
25846
  ref = sch;
@@ -26056,8 +26056,8 @@ var require_utils = __commonJS({
26056
26056
  }
26057
26057
  return ind;
26058
26058
  }
26059
- function removeDotSegments(path14) {
26060
- let input = path14;
26059
+ function removeDotSegments(path17) {
26060
+ let input = path17;
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 [path14, query] = wsComponent.resourceName.split("?");
26261
- wsComponent.path = path14 && path14 !== "/" ? path14 : void 0;
26260
+ const [path17, query] = wsComponent.resourceName.split("?");
26261
+ wsComponent.path = path17 && path17 !== "/" ? path17 : void 0;
26262
26262
  wsComponent.query = query;
26263
26263
  wsComponent.resourceName = void 0;
26264
26264
  }
@@ -26418,7 +26418,7 @@ var require_fast_uri = __commonJS({
26418
26418
  }
26419
26419
  return uri;
26420
26420
  }
26421
- function resolve12(baseURI, relativeURI, options) {
26421
+ function resolve14(baseURI, relativeURI, options) {
26422
26422
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
26423
26423
  const resolved = resolveComponent(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true);
26424
26424
  schemelessOptions.skipEscape = true;
@@ -26645,7 +26645,7 @@ var require_fast_uri = __commonJS({
26645
26645
  var fastUri = {
26646
26646
  SCHEMES,
26647
26647
  normalize: normalize3,
26648
- resolve: resolve12,
26648
+ resolve: resolve14,
26649
26649
  resolveComponent,
26650
26650
  equal,
26651
26651
  serialize,
@@ -29666,12 +29666,12 @@ var require_dist = __commonJS({
29666
29666
  throw new Error(`Unknown format "${name}"`);
29667
29667
  return f;
29668
29668
  };
29669
- function addFormats(ajv, list, fs10, exportName) {
29669
+ function addFormats(ajv, list, fs12, exportName) {
29670
29670
  var _a3;
29671
29671
  var _b;
29672
29672
  (_a3 = (_b = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
29673
29673
  for (const f of list)
29674
- ajv.addFormat(f, fs10[f]);
29674
+ ajv.addFormat(f, fs12[f]);
29675
29675
  }
29676
29676
  module.exports = exports = formatsPlugin;
29677
29677
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -30827,7 +30827,7 @@ var init_mcp = __esm({
30827
30827
  let task = createTaskResult.task;
30828
30828
  const pollInterval = task.pollInterval ?? 5e3;
30829
30829
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
30830
- await new Promise((resolve12) => setTimeout(resolve12, pollInterval));
30830
+ await new Promise((resolve14) => setTimeout(resolve14, pollInterval));
30831
30831
  const updatedTask = await extra.taskStore.getTask(taskId);
30832
30832
  if (!updatedTask) {
30833
30833
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -31423,12 +31423,12 @@ var init_stdio2 = __esm({
31423
31423
  this.onclose?.();
31424
31424
  }
31425
31425
  send(message) {
31426
- return new Promise((resolve12) => {
31426
+ return new Promise((resolve14) => {
31427
31427
  const json2 = serializeMessage(message);
31428
31428
  if (this._stdout.write(json2)) {
31429
- resolve12();
31429
+ resolve14();
31430
31430
  } else {
31431
- this._stdout.once("drain", resolve12);
31431
+ this._stdout.once("drain", resolve14);
31432
31432
  }
31433
31433
  });
31434
31434
  }
@@ -35539,7 +35539,7 @@ var require_supports_color = __commonJS({
35539
35539
  "node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/index.js"(exports, module) {
35540
35540
  "use strict";
35541
35541
  init_define_WEB_FIRST_SEGMENTS();
35542
- var os3 = __require("os");
35542
+ var os5 = __require("os");
35543
35543
  var tty3 = __require("tty");
35544
35544
  var hasFlag2 = require_has_flag();
35545
35545
  var { env: env2 } = process;
@@ -35587,7 +35587,7 @@ var require_supports_color = __commonJS({
35587
35587
  return min;
35588
35588
  }
35589
35589
  if (process.platform === "win32") {
35590
- const osRelease = os3.release().split(".");
35590
+ const osRelease = os5.release().split(".");
35591
35591
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
35592
35592
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
35593
35593
  }
@@ -35836,10 +35836,10 @@ var require_src2 = __commonJS({
35836
35836
  var fs_1 = __require("fs");
35837
35837
  var debug_1 = __importDefault(require_src());
35838
35838
  var log = debug_1.default("@kwsites/file-exists");
35839
- function check2(path14, isFile, isDirectory2) {
35840
- log(`checking %s`, path14);
35839
+ function check2(path17, isFile, isDirectory2) {
35840
+ log(`checking %s`, path17);
35841
35841
  try {
35842
- const stat3 = fs_1.statSync(path14);
35842
+ const stat3 = fs_1.statSync(path17);
35843
35843
  if (stat3.isFile() && isFile) {
35844
35844
  log(`[OK] path represents a file`);
35845
35845
  return true;
@@ -35859,8 +35859,8 @@ var require_src2 = __commonJS({
35859
35859
  throw e;
35860
35860
  }
35861
35861
  }
35862
- function exists2(path14, type = exports.READABLE) {
35863
- return check2(path14, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
35862
+ function exists2(path17, type = exports.READABLE) {
35863
+ return check2(path17, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
35864
35864
  }
35865
35865
  exports.exists = exists2;
35866
35866
  exports.FILE = 1;
@@ -36453,8 +36453,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
36453
36453
  function forEachLineWithContent(input, callback) {
36454
36454
  return toLinesWithContent(input, true).map((line) => callback(line));
36455
36455
  }
36456
- function folderExists(path14) {
36457
- return (0, import_file_exists.exists)(path14, import_file_exists.FOLDER);
36456
+ function folderExists(path17) {
36457
+ return (0, import_file_exists.exists)(path17, import_file_exists.FOLDER);
36458
36458
  }
36459
36459
  function append(target, item) {
36460
36460
  if (Array.isArray(target)) {
@@ -36645,8 +36645,8 @@ function checkIsRepoRootTask() {
36645
36645
  commands,
36646
36646
  format: "utf-8",
36647
36647
  onError,
36648
- parser(path14) {
36649
- return /^\.(git)?$/.test(path14.trim());
36648
+ parser(path17) {
36649
+ return /^\.(git)?$/.test(path17.trim());
36650
36650
  }
36651
36651
  };
36652
36652
  }
@@ -36902,11 +36902,11 @@ function parseGrep(grep) {
36902
36902
  const paths = /* @__PURE__ */ new Set();
36903
36903
  const results = {};
36904
36904
  forEachLineWithContent(grep, (input) => {
36905
- const [path14, line, preview] = input.split(NULL);
36906
- paths.add(path14);
36907
- (results[path14] = results[path14] || []).push({
36905
+ const [path17, line, preview] = input.split(NULL);
36906
+ paths.add(path17);
36907
+ (results[path17] = results[path17] || []).push({
36908
36908
  line: asNumber(line),
36909
- path: path14,
36909
+ path: path17,
36910
36910
  preview
36911
36911
  });
36912
36912
  });
@@ -37203,14 +37203,14 @@ function hashObjectTask(filePath, write) {
37203
37203
  }
37204
37204
  return straightThroughStringTask(commands, true);
37205
37205
  }
37206
- function parseInit(bare, path14, text) {
37206
+ function parseInit(bare, path17, text) {
37207
37207
  const response = String(text).trim();
37208
37208
  let result;
37209
37209
  if (result = initResponseRegex.exec(response)) {
37210
- return new InitSummary(bare, path14, false, result[1]);
37210
+ return new InitSummary(bare, path17, false, result[1]);
37211
37211
  }
37212
37212
  if (result = reInitResponseRegex.exec(response)) {
37213
- return new InitSummary(bare, path14, true, result[1]);
37213
+ return new InitSummary(bare, path17, true, result[1]);
37214
37214
  }
37215
37215
  let gitDir = "";
37216
37216
  const tokens = response.split(" ");
@@ -37221,12 +37221,12 @@ function parseInit(bare, path14, text) {
37221
37221
  break;
37222
37222
  }
37223
37223
  }
37224
- return new InitSummary(bare, path14, /^re/i.test(response), gitDir);
37224
+ return new InitSummary(bare, path17, /^re/i.test(response), gitDir);
37225
37225
  }
37226
37226
  function hasBareCommand(command) {
37227
37227
  return command.includes(bareCommand);
37228
37228
  }
37229
- function initTask(bare = false, path14, customArgs) {
37229
+ function initTask(bare = false, path17, customArgs) {
37230
37230
  const commands = ["init", ...customArgs];
37231
37231
  if (bare && !hasBareCommand(commands)) {
37232
37232
  commands.splice(1, 0, bareCommand);
@@ -37235,7 +37235,7 @@ function initTask(bare = false, path14, customArgs) {
37235
37235
  commands,
37236
37236
  format: "utf-8",
37237
37237
  parser(text) {
37238
- return parseInit(commands.includes("--bare"), path14, text);
37238
+ return parseInit(commands.includes("--bare"), path17, text);
37239
37239
  }
37240
37240
  };
37241
37241
  }
@@ -37514,14 +37514,14 @@ function splitLine(result, lineStr) {
37514
37514
  default:
37515
37515
  return;
37516
37516
  }
37517
- function data(index, workingDir, path14) {
37517
+ function data(index, workingDir, path17) {
37518
37518
  const raw = `${index}${workingDir}`;
37519
37519
  const handler = parsers6.get(raw);
37520
37520
  if (handler) {
37521
- handler(result, path14);
37521
+ handler(result, path17);
37522
37522
  }
37523
37523
  if (raw !== "##" && raw !== "!!") {
37524
- result.files.push(new FileStatusSummary(path14, index, workingDir));
37524
+ result.files.push(new FileStatusSummary(path17, index, workingDir));
37525
37525
  }
37526
37526
  }
37527
37527
  }
@@ -37708,8 +37708,8 @@ function deleteBranchTask(branch, forceDelete = false) {
37708
37708
  return task;
37709
37709
  }
37710
37710
  function toPath(input) {
37711
- const path14 = input.trim().replace(/^["']|["']$/g, "");
37712
- return path14 && normalize2(path14);
37711
+ const path17 = input.trim().replace(/^["']|["']$/g, "");
37712
+ return path17 && normalize2(path17);
37713
37713
  }
37714
37714
  function checkIgnoreTask(paths) {
37715
37715
  return {
@@ -37848,8 +37848,8 @@ function stashListTask(opt = {}, customArgs) {
37848
37848
  parser: parser4
37849
37849
  };
37850
37850
  }
37851
- function addSubModuleTask(repo, path14) {
37852
- return subModuleTask(["add", repo, path14]);
37851
+ function addSubModuleTask(repo, path17) {
37852
+ return subModuleTask(["add", repo, path17]);
37853
37853
  }
37854
37854
  function initSubModuleTask(customArgs) {
37855
37855
  return subModuleTask(["init", ...customArgs]);
@@ -39112,9 +39112,9 @@ var init_esm2 = __esm({
39112
39112
  "src/lib/responses/InitSummary.ts"() {
39113
39113
  "use strict";
39114
39114
  InitSummary = class {
39115
- constructor(bare, path14, existing, gitDir) {
39115
+ constructor(bare, path17, existing, gitDir) {
39116
39116
  this.bare = bare;
39117
- this.path = path14;
39117
+ this.path = path17;
39118
39118
  this.existing = existing;
39119
39119
  this.gitDir = gitDir;
39120
39120
  }
@@ -39647,12 +39647,12 @@ var init_esm2 = __esm({
39647
39647
  "use strict";
39648
39648
  fromPathRegex = /^(.+)\0(.+)$/;
39649
39649
  FileStatusSummary = class {
39650
- constructor(path14, index, working_dir) {
39651
- this.path = path14;
39650
+ constructor(path17, index, working_dir) {
39651
+ this.path = path17;
39652
39652
  this.index = index;
39653
39653
  this.working_dir = working_dir;
39654
39654
  if (index === "R" || working_dir === "R") {
39655
- const detail = fromPathRegex.exec(path14) || [null, path14, path14];
39655
+ const detail = fromPathRegex.exec(path17) || [null, path17, path17];
39656
39656
  this.from = detail[2] || "";
39657
39657
  this.path = detail[1] || "";
39658
39658
  }
@@ -39914,9 +39914,9 @@ var init_esm2 = __esm({
39914
39914
  next
39915
39915
  );
39916
39916
  }
39917
- hashObject(path14, write) {
39917
+ hashObject(path17, write) {
39918
39918
  return this._runTask(
39919
- hashObjectTask(path14, write === true),
39919
+ hashObjectTask(path17, write === true),
39920
39920
  trailingFunctionArgument(arguments)
39921
39921
  );
39922
39922
  }
@@ -40553,8 +40553,8 @@ var init_esm2 = __esm({
40553
40553
  }
40554
40554
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
40555
40555
  };
40556
- Git2.prototype.submoduleAdd = function(repo, path14, then) {
40557
- return this._runTask(addSubModuleTask2(repo, path14), trailingFunctionArgument2(arguments));
40556
+ Git2.prototype.submoduleAdd = function(repo, path17, then) {
40557
+ return this._runTask(addSubModuleTask2(repo, path17), trailingFunctionArgument2(arguments));
40558
40558
  };
40559
40559
  Git2.prototype.submoduleUpdate = function(args2, then) {
40560
40560
  return this._runTask(
@@ -41629,6 +41629,37 @@ function parseTree(text, errors = [], options = ParseOptions.DEFAULT) {
41629
41629
  }
41630
41630
  return result;
41631
41631
  }
41632
+ function findNodeAtLocation(root, path17) {
41633
+ if (!root) {
41634
+ return void 0;
41635
+ }
41636
+ let node = root;
41637
+ for (let segment of path17) {
41638
+ if (typeof segment === "string") {
41639
+ if (node.type !== "object" || !Array.isArray(node.children)) {
41640
+ return void 0;
41641
+ }
41642
+ let found = false;
41643
+ for (const propertyNode of node.children) {
41644
+ if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment && propertyNode.children.length === 2) {
41645
+ node = propertyNode.children[1];
41646
+ found = true;
41647
+ break;
41648
+ }
41649
+ }
41650
+ if (!found) {
41651
+ return void 0;
41652
+ }
41653
+ } else {
41654
+ const index = segment;
41655
+ if (node.type !== "array" || index < 0 || !Array.isArray(node.children) || index >= node.children.length) {
41656
+ return void 0;
41657
+ }
41658
+ node = node.children[index];
41659
+ }
41660
+ }
41661
+ return node;
41662
+ }
41632
41663
  function getNodeValue(node) {
41633
41664
  switch (node.type) {
41634
41665
  case "array":
@@ -42001,7 +42032,7 @@ var init_edit = __esm({
42001
42032
  });
42002
42033
 
42003
42034
  // node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/main.js
42004
- var ScanError, SyntaxKind, parseTree2, getNodeValue2, ParseErrorCode;
42035
+ var ScanError, SyntaxKind, parseTree2, findNodeAtLocation2, getNodeValue2, ParseErrorCode;
42005
42036
  var init_main = __esm({
42006
42037
  "node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/main.js"() {
42007
42038
  "use strict";
@@ -42039,6 +42070,7 @@ var init_main = __esm({
42039
42070
  SyntaxKind2[SyntaxKind2["EOF"] = 17] = "EOF";
42040
42071
  })(SyntaxKind || (SyntaxKind = {}));
42041
42072
  parseTree2 = parseTree;
42073
+ findNodeAtLocation2 = findNodeAtLocation;
42042
42074
  getNodeValue2 = getNodeValue;
42043
42075
  (function(ParseErrorCode2) {
42044
42076
  ParseErrorCode2[ParseErrorCode2["InvalidSymbol"] = 1] = "InvalidSymbol";
@@ -45978,6 +46010,487 @@ var init_content_secrets = __esm({
45978
46010
  }
45979
46011
  });
45980
46012
 
46013
+ // src/core/checks/hook-coverage.ts
46014
+ import * as fs7 from "node:fs";
46015
+ import * as os2 from "node:os";
46016
+ import * as path11 from "node:path";
46017
+ function loadSettingsSources2(projectRoot, homeDir, includeUserGlobal) {
46018
+ const home2 = homeDir;
46019
+ const candidates = [
46020
+ {
46021
+ filePath: path11.join(projectRoot, ".claude", "settings.json"),
46022
+ displayPath: path11.join(".claude", "settings.json"),
46023
+ isUserGlobal: false
46024
+ },
46025
+ {
46026
+ filePath: path11.join(projectRoot, ".claude", "settings.local.json"),
46027
+ displayPath: path11.join(".claude", "settings.local.json"),
46028
+ isUserGlobal: false
46029
+ }
46030
+ ];
46031
+ if (includeUserGlobal) {
46032
+ candidates.push({
46033
+ filePath: path11.join(home2, ".claude", "settings.json"),
46034
+ displayPath: "~/.claude/settings.json",
46035
+ isUserGlobal: true
46036
+ });
46037
+ }
46038
+ const sources = [];
46039
+ for (const c3 of candidates) {
46040
+ let content;
46041
+ try {
46042
+ content = stripBom(fs7.readFileSync(c3.filePath, "utf-8"));
46043
+ } catch {
46044
+ continue;
46045
+ }
46046
+ const tree = parseTree2(content, [], { allowTrailingComma: true });
46047
+ sources.push({ ...c3, content, tree });
46048
+ }
46049
+ return sources;
46050
+ }
46051
+ function expandPath(raw, projectRoot, homeDir) {
46052
+ let s = raw;
46053
+ if (s === "~" || s.startsWith("~/") || s.startsWith("~\\")) {
46054
+ s = path11.join(homeDir, s.slice(1));
46055
+ }
46056
+ const replacements = {
46057
+ CLAUDE_PROJECT_DIR: projectRoot,
46058
+ CLAUDE_CONFIG_DIR: path11.join(homeDir, ".claude"),
46059
+ HOME: homeDir,
46060
+ USERPROFILE: homeDir
46061
+ };
46062
+ s = s.replace(
46063
+ /\$\{?([A-Z_][A-Z0-9_]*)\}?/g,
46064
+ (m, name) => name in replacements ? replacements[name] : m
46065
+ );
46066
+ if (/\$\{?[A-Za-z_]/.test(s)) return null;
46067
+ if (!path11.isAbsolute(s)) s = path11.resolve(projectRoot, s);
46068
+ return s;
46069
+ }
46070
+ function looksLikePath(token) {
46071
+ const hasSep = token.includes("/") || token.includes("\\");
46072
+ if (PATH_PREFIX.test(token)) return true;
46073
+ return hasSep && SCRIPT_EXT.test(token);
46074
+ }
46075
+ function extractCommandPaths(command, projectRoot, homeDir) {
46076
+ const tokens = command.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
46077
+ const out = [];
46078
+ for (const t2 of tokens) {
46079
+ const token = t2.replace(/^['"]|['"]$/g, "");
46080
+ if (!looksLikePath(token)) continue;
46081
+ out.push({ raw: token, resolved: expandPath(token, projectRoot, homeDir) });
46082
+ }
46083
+ return out;
46084
+ }
46085
+ function extractPermissionPaths(entry, projectRoot, homeDir) {
46086
+ const inner = entry.replace(/^[A-Za-z]+\((.*)\)$/, "$1");
46087
+ return extractCommandPaths(inner, projectRoot, homeDir);
46088
+ }
46089
+ function lineForPath(source, jsonPath) {
46090
+ if (!source.tree) return 1;
46091
+ const node = findNodeAtLocation2(source.tree, jsonPath);
46092
+ if (!node) return 1;
46093
+ return offsetToPosition(source.content, node.offset).line;
46094
+ }
46095
+ function checkSource(source, projectRoot, homeDir) {
46096
+ if (!source.tree) return [];
46097
+ const data = getNodeValue2(source.tree);
46098
+ if (!data || typeof data !== "object") return [];
46099
+ const issues = [];
46100
+ const report = (cand, line, origin) => {
46101
+ if (cand.resolved === null) return;
46102
+ if (fileExists(cand.resolved)) return;
46103
+ const where = source.isUserGlobal ? ` (in ${source.displayPath})` : "";
46104
+ issues.push({
46105
+ severity: "warning",
46106
+ check: "hook-coverage",
46107
+ ruleId: "hook-coverage/dead-hook",
46108
+ line,
46109
+ message: `${origin} references "${cand.raw}" which does not exist on disk${where} \u2014 the gate silently no-ops`,
46110
+ suggestion: source.isUserGlobal ? `Restore the script at ${cand.resolved}, or remove the dead entry from ${source.displayPath}.` : `Restore the script, fix the path, or remove the dead entry. A PreToolUse hook pointing at a missing script does not block anything.`
46111
+ });
46112
+ };
46113
+ const hooks = data.hooks ?? {};
46114
+ for (const [event, matchers] of Object.entries(hooks)) {
46115
+ if (!Array.isArray(matchers)) continue;
46116
+ matchers.forEach((matcher, mi2) => {
46117
+ const subHooks = matcher?.hooks;
46118
+ if (!Array.isArray(subHooks)) return;
46119
+ subHooks.forEach((h2, hi2) => {
46120
+ if (!h2 || typeof h2.command !== "string") return;
46121
+ const line = lineForPath(source, ["hooks", event, mi2, "hooks", hi2, "command"]);
46122
+ for (const cand of extractCommandPaths(h2.command, projectRoot, homeDir)) {
46123
+ report(cand, line, `${event} hook`);
46124
+ }
46125
+ });
46126
+ });
46127
+ }
46128
+ const perms = data.permissions ?? {};
46129
+ for (const listName of ["allow", "deny", "ask"]) {
46130
+ const list = perms[listName];
46131
+ if (!Array.isArray(list)) continue;
46132
+ list.forEach((entry, i2) => {
46133
+ if (typeof entry !== "string") return;
46134
+ const line = lineForPath(source, ["permissions", listName, i2]);
46135
+ for (const cand of extractPermissionPaths(entry, projectRoot, homeDir)) {
46136
+ report(cand, line, `permissions.${listName} entry`);
46137
+ }
46138
+ });
46139
+ }
46140
+ return issues;
46141
+ }
46142
+ async function checkHookCoverage(projectRoot, homeDir = os2.homedir(), options = {}) {
46143
+ const sources = loadSettingsSources2(projectRoot, homeDir, options.userGlobal ?? false);
46144
+ const issues = [];
46145
+ for (const source of sources) {
46146
+ issues.push(...checkSource(source, projectRoot, homeDir));
46147
+ }
46148
+ return issues;
46149
+ }
46150
+ var SCRIPT_EXT, PATH_PREFIX;
46151
+ var init_hook_coverage = __esm({
46152
+ "src/core/checks/hook-coverage.ts"() {
46153
+ "use strict";
46154
+ init_define_WEB_FIRST_SEGMENTS();
46155
+ init_main();
46156
+ init_fs();
46157
+ init_positions();
46158
+ SCRIPT_EXT = /\.(sh|bash|zsh|fish|js|mjs|cjs|ts|py|rb|pl|ps1|cmd|bat|exe)$/i;
46159
+ PATH_PREFIX = /^(~|\.\.?[/\\]|[/\\]|\$\{?[A-Za-z_]|[A-Za-z]:[/\\])/;
46160
+ }
46161
+ });
46162
+
46163
+ // src/core/skill-scanner.ts
46164
+ import * as fs8 from "node:fs";
46165
+ import * as os3 from "node:os";
46166
+ import * as path12 from "node:path";
46167
+ function defaultHome() {
46168
+ return process.env.HOME || process.env.USERPROFILE || os3.homedir();
46169
+ }
46170
+ function scanSkillFiles(homeDir = defaultHome()) {
46171
+ const files = [];
46172
+ const orphanedSkillDirs = [];
46173
+ const claudeDir = path12.join(homeDir, ".claude");
46174
+ const skillsRoot = path12.join(claudeDir, "skills");
46175
+ for (const name of safeReadDir(skillsRoot, "dir")) {
46176
+ const skillMd = path12.join(skillsRoot, name, "SKILL.md");
46177
+ const content = safeRead(skillMd);
46178
+ if (content === null) {
46179
+ orphanedSkillDirs.push({ name, displayPath: `~/.claude/skills/${name}/` });
46180
+ continue;
46181
+ }
46182
+ files.push({
46183
+ filePath: skillMd,
46184
+ displayPath: `~/.claude/skills/${name}/SKILL.md`,
46185
+ kind: "skill",
46186
+ name,
46187
+ content
46188
+ });
46189
+ }
46190
+ const agentsRoot = path12.join(claudeDir, "agents");
46191
+ for (const fileName of safeReadDir(agentsRoot, "file")) {
46192
+ if (!fileName.endsWith(".md")) continue;
46193
+ const agentMd = path12.join(agentsRoot, fileName);
46194
+ const content = safeRead(agentMd);
46195
+ if (content === null) continue;
46196
+ files.push({
46197
+ filePath: agentMd,
46198
+ displayPath: `~/.claude/agents/${fileName}`,
46199
+ kind: "agent",
46200
+ name: fileName.replace(/\.md$/, ""),
46201
+ content
46202
+ });
46203
+ }
46204
+ return { files, orphanedSkillDirs };
46205
+ }
46206
+ function safeReadDir(dir, want) {
46207
+ let entries;
46208
+ try {
46209
+ entries = fs8.readdirSync(dir, { withFileTypes: true });
46210
+ } catch {
46211
+ return [];
46212
+ }
46213
+ return entries.filter((e) => want === "dir" ? e.isDirectory() : e.isFile()).map((e) => e.name).sort();
46214
+ }
46215
+ function safeRead(file2) {
46216
+ try {
46217
+ return stripBom(fs8.readFileSync(file2, "utf-8"));
46218
+ } catch {
46219
+ return null;
46220
+ }
46221
+ }
46222
+ var init_skill_scanner = __esm({
46223
+ "src/core/skill-scanner.ts"() {
46224
+ "use strict";
46225
+ init_define_WEB_FIRST_SEGMENTS();
46226
+ init_fs();
46227
+ }
46228
+ });
46229
+
46230
+ // src/core/checks/skills.ts
46231
+ import * as path13 from "node:path";
46232
+ function parseFrontmatter3(content) {
46233
+ const lines = content.split("\n");
46234
+ if (lines[0]?.trim() !== "---") {
46235
+ return { found: false, unclosed: false, fields: {}, endLine: 0 };
46236
+ }
46237
+ const fields = {};
46238
+ let arrayKey = "";
46239
+ let endLine = 0;
46240
+ for (let i2 = 1; i2 < lines.length; i2++) {
46241
+ const line = lines[i2].trim();
46242
+ if (line === "---") {
46243
+ endLine = i2 + 1;
46244
+ break;
46245
+ }
46246
+ if (line.startsWith("- ") && arrayKey) {
46247
+ const item = line.slice(2).trim().replace(/^["']|["']$/g, "");
46248
+ fields[arrayKey] = fields[arrayKey] ? `${fields[arrayKey]}, ${item}` : item;
46249
+ continue;
46250
+ }
46251
+ arrayKey = "";
46252
+ const m = line.match(/^([\w-]+)\s*:\s*(.*)$/);
46253
+ if (m) {
46254
+ let val = m[2].trim();
46255
+ const inline = val.match(/^\[(.*)\]$/);
46256
+ if (inline) {
46257
+ val = inline[1].split(",").map((s) => s.trim().replace(/^["']|["']$/g, "")).filter(Boolean).join(", ");
46258
+ }
46259
+ fields[m[1]] = val;
46260
+ if (!m[2].trim()) arrayKey = m[1];
46261
+ }
46262
+ }
46263
+ if (endLine === 0) return { found: true, unclosed: true, fields, endLine: lines.length };
46264
+ return { found: true, unclosed: false, fields, endLine };
46265
+ }
46266
+ function checkMissingFrontmatter(file2, fm) {
46267
+ if (fm.unclosed) {
46268
+ return [
46269
+ {
46270
+ severity: "error",
46271
+ check: "skill-frontmatter",
46272
+ ruleId: "skill/missing-frontmatter",
46273
+ line: 1,
46274
+ message: `${file2.displayPath}: frontmatter opens with \`---\` but is never closed`,
46275
+ suggestion: "Add a matching `---` line after the last frontmatter field."
46276
+ }
46277
+ ];
46278
+ }
46279
+ if (!fm.found) {
46280
+ return [
46281
+ {
46282
+ severity: "error",
46283
+ check: "skill-frontmatter",
46284
+ ruleId: "skill/missing-frontmatter",
46285
+ line: 1,
46286
+ message: `${file2.displayPath}: missing YAML frontmatter`,
46287
+ suggestion: `Add frontmatter with ${REQUIRED_FIELDS[file2.kind].join(" + ")}.`
46288
+ }
46289
+ ];
46290
+ }
46291
+ const issues = [];
46292
+ for (const field of REQUIRED_FIELDS[file2.kind]) {
46293
+ if (!fm.fields[field]) {
46294
+ issues.push({
46295
+ severity: "warning",
46296
+ check: "skill-frontmatter",
46297
+ ruleId: "skill/missing-frontmatter",
46298
+ line: 1,
46299
+ message: `${file2.displayPath}: missing required frontmatter field "${field}"`,
46300
+ suggestion: field === "description" ? "A skill/agent with no description is never selected \u2014 add one describing when to use it." : `Add a "${field}" field to the frontmatter.`
46301
+ });
46302
+ }
46303
+ }
46304
+ return issues;
46305
+ }
46306
+ function checkBrokenRefs(file2, fmEndLine) {
46307
+ const issues = [];
46308
+ const skillDir = path13.dirname(file2.filePath);
46309
+ const lines = file2.content.split("\n");
46310
+ let inCode = false;
46311
+ let codeLang = "";
46312
+ for (let i2 = 0; i2 < lines.length; i2++) {
46313
+ if (i2 + 1 <= fmEndLine) continue;
46314
+ const raw = lines[i2];
46315
+ if (raw.trimStart().startsWith("```")) {
46316
+ inCode = !inCode;
46317
+ codeLang = inCode ? raw.trimStart().slice(3).trim().toLowerCase() : "";
46318
+ continue;
46319
+ }
46320
+ if (inCode && isExampleLang(codeLang)) continue;
46321
+ BODY_PATH.lastIndex = 0;
46322
+ let m;
46323
+ while ((m = BODY_PATH.exec(raw)) !== null) {
46324
+ let value = m[1];
46325
+ if (PATH_SKIP.test(value)) continue;
46326
+ if (!value.startsWith("./") && !value.startsWith("../")) continue;
46327
+ while (/[.,;:]$/.test(value)) value = value.slice(0, -1);
46328
+ const resolved = path13.resolve(skillDir, value);
46329
+ if (fileExists(resolved) || isDirectory(resolved)) continue;
46330
+ issues.push({
46331
+ severity: "warning",
46332
+ check: "skill-broken-ref",
46333
+ ruleId: "skill/broken-ref",
46334
+ line: i2 + 1,
46335
+ message: `${file2.displayPath}: references "${value}" which does not exist relative to the skill directory`,
46336
+ suggestion: "Fix the path, or use a path that exists relative to the skill/agent file."
46337
+ });
46338
+ }
46339
+ }
46340
+ return issues;
46341
+ }
46342
+ function isExampleLang(lang) {
46343
+ return [
46344
+ "js",
46345
+ "javascript",
46346
+ "ts",
46347
+ "typescript",
46348
+ "tsx",
46349
+ "jsx",
46350
+ "py",
46351
+ "python",
46352
+ "go",
46353
+ "rust",
46354
+ "java",
46355
+ "c",
46356
+ "cpp",
46357
+ "ruby",
46358
+ "php",
46359
+ "json",
46360
+ "yaml",
46361
+ "yml",
46362
+ "toml",
46363
+ // Shell fences hold command/example snippets, not in-repo refs. A `./foo.sh`
46364
+ // inside a bash block is an invocation example (often a cross-repo script or
46365
+ // a placeholder), not a path that should resolve relative to the skill dir.
46366
+ // Matches the rule's "prefer a false negative over a false positive" posture.
46367
+ "bash",
46368
+ "sh",
46369
+ "shell",
46370
+ "shellscript",
46371
+ "zsh",
46372
+ "console"
46373
+ ].includes(lang);
46374
+ }
46375
+ function extractTriggers(fm) {
46376
+ const phrases = /* @__PURE__ */ new Set();
46377
+ const desc = fm.fields["description"] ?? "";
46378
+ for (const q3 of desc.matchAll(/["']([^"']{3,})["']/g)) {
46379
+ phrases.add(normalizeTrigger(q3[1]));
46380
+ }
46381
+ for (const key of ["trigger", "triggers"]) {
46382
+ const val = fm.fields[key];
46383
+ if (val) for (const part of val.split(",")) phrases.add(normalizeTrigger(part));
46384
+ }
46385
+ phrases.delete("");
46386
+ return [...phrases];
46387
+ }
46388
+ function normalizeTrigger(s) {
46389
+ return s.toLowerCase().trim().replace(/\s+/g, " ");
46390
+ }
46391
+ function checkTriggerCollisions(parsed) {
46392
+ const byTrigger = /* @__PURE__ */ new Map();
46393
+ for (const { file: file2, fm } of parsed) {
46394
+ for (const t2 of extractTriggers(fm)) {
46395
+ const list = byTrigger.get(t2) ?? [];
46396
+ list.push(file2);
46397
+ byTrigger.set(t2, list);
46398
+ }
46399
+ }
46400
+ const issues = [];
46401
+ for (const [trigger, owners] of byTrigger) {
46402
+ const distinct = [...new Set(owners.map((o2) => o2.displayPath))];
46403
+ if (distinct.length > 1) {
46404
+ issues.push({
46405
+ severity: "warning",
46406
+ check: "skill-trigger-collision",
46407
+ ruleId: "skill/trigger-collision",
46408
+ line: 1,
46409
+ message: `Trigger phrase "${trigger}" is declared by ${distinct.length} skills/agents \u2014 only one will win`,
46410
+ detail: distinct.map((d) => ` - ${d}`).join("\n"),
46411
+ suggestion: "Make trigger phrases unique so invocation is deterministic."
46412
+ });
46413
+ }
46414
+ }
46415
+ return issues;
46416
+ }
46417
+ function checkOrphaned(ctx) {
46418
+ return ctx.orphanedSkillDirs.map((o2) => ({
46419
+ severity: "warning",
46420
+ check: "skill-orphaned",
46421
+ ruleId: "skill/orphaned",
46422
+ line: 1,
46423
+ message: `${o2.displayPath}: skill directory has no SKILL.md \u2014 Claude Code has nothing to load`,
46424
+ suggestion: `Add a SKILL.md, or remove the empty ${o2.displayPath} directory.`
46425
+ }));
46426
+ }
46427
+ function checkDeadToolRestrictions(file2, fm) {
46428
+ if (file2.kind !== "agent") return [];
46429
+ const raw = fm.fields["tools"] ?? fm.fields["allowed-tools"] ?? fm.fields["allowedTools"];
46430
+ if (!raw) return [];
46431
+ const issues = [];
46432
+ for (const entry of raw.split(",").map((s) => s.trim()).filter(Boolean)) {
46433
+ if (entry.startsWith("mcp__") || entry.includes("*")) continue;
46434
+ const base = entry.replace(/\(.*\)$/, "").trim();
46435
+ if (KNOWN_TOOLS.has(base)) continue;
46436
+ issues.push({
46437
+ severity: "warning",
46438
+ check: "skill-dead-tool-restriction",
46439
+ ruleId: "skill/dead-tool-restriction",
46440
+ line: 1,
46441
+ message: `${file2.displayPath}: tool restriction lists "${entry}" which is not a known Claude Code tool`,
46442
+ suggestion: `Known built-in tools: ${[...KNOWN_TOOLS].join(", ")}. MCP tools use the mcp__server__tool form.`
46443
+ });
46444
+ }
46445
+ return issues;
46446
+ }
46447
+ function checkSkills(ctx, sel) {
46448
+ const issues = [];
46449
+ const parsed = ctx.files.map((file2) => ({ file: file2, fm: parseFrontmatter3(file2.content) }));
46450
+ for (const { file: file2, fm } of parsed) {
46451
+ if (sel.frontmatter) issues.push(...checkMissingFrontmatter(file2, fm));
46452
+ if (sel.brokenRef) issues.push(...checkBrokenRefs(file2, fm.found ? fm.endLine : 0));
46453
+ if (sel.deadToolRestriction) issues.push(...checkDeadToolRestrictions(file2, fm));
46454
+ }
46455
+ if (sel.triggerCollision) issues.push(...checkTriggerCollisions(parsed));
46456
+ if (sel.orphaned) issues.push(...checkOrphaned(ctx));
46457
+ return issues;
46458
+ }
46459
+ var REQUIRED_FIELDS, BODY_PATH, PATH_SKIP, KNOWN_TOOLS;
46460
+ var init_skills = __esm({
46461
+ "src/core/checks/skills.ts"() {
46462
+ "use strict";
46463
+ init_define_WEB_FIRST_SEGMENTS();
46464
+ init_fs();
46465
+ REQUIRED_FIELDS = {
46466
+ skill: ["name", "description"],
46467
+ agent: ["name", "description"]
46468
+ };
46469
+ BODY_PATH = /(?:^|[\s`"'(])((?:\.{1,2}\/)[\w@./-]+|(?:[\w@-]+\/)+[\w.-]+)(?=[\s`"'),;:]|$)/gm;
46470
+ PATH_SKIP = /^(https?:\/\/|mailto:|n\/a|e\.g\.|i\.e\.)/i;
46471
+ KNOWN_TOOLS = /* @__PURE__ */ new Set([
46472
+ "Agent",
46473
+ "Bash",
46474
+ "BashOutput",
46475
+ "Edit",
46476
+ "ExitPlanMode",
46477
+ "Glob",
46478
+ "Grep",
46479
+ "KillShell",
46480
+ "MultiEdit",
46481
+ "NotebookEdit",
46482
+ "Read",
46483
+ "Skill",
46484
+ "SlashCommand",
46485
+ "Task",
46486
+ "TodoWrite",
46487
+ "WebFetch",
46488
+ "WebSearch",
46489
+ "Write"
46490
+ ]);
46491
+ }
46492
+ });
46493
+
45981
46494
  // src/core/ignore-rules.ts
45982
46495
  function compileRules(rules) {
45983
46496
  return rules.map((r2) => ({
@@ -46044,25 +46557,26 @@ var init_ignore_rules = __esm({
46044
46557
  });
46045
46558
 
46046
46559
  // src/core/types.ts
46047
- var SESSION_AUDIT_LABEL, SESSION_AUDIT_PATH_MARKER;
46560
+ var SESSION_AUDIT_LABEL, SESSION_AUDIT_PATH_MARKER, SKILL_AUDIT_LABEL;
46048
46561
  var init_types3 = __esm({
46049
46562
  "src/core/types.ts"() {
46050
46563
  "use strict";
46051
46564
  init_define_WEB_FIRST_SEGMENTS();
46052
46565
  SESSION_AUDIT_LABEL = "~/.claude/ (session audit)";
46053
46566
  SESSION_AUDIT_PATH_MARKER = "(session audit)";
46567
+ SKILL_AUDIT_LABEL = "~/.claude/ (skill audit)";
46054
46568
  }
46055
46569
  });
46056
46570
 
46057
46571
  // src/version.ts
46058
- import { readFileSync as readFileSync5 } from "node:fs";
46059
- import { resolve as resolve9, dirname as dirname4 } from "node:path";
46572
+ import { readFileSync as readFileSync7 } from "node:fs";
46573
+ import { resolve as resolve11, dirname as dirname5 } from "node:path";
46060
46574
  import { fileURLToPath as fileURLToPath2 } from "node:url";
46061
46575
  function loadVersion() {
46062
- if (true) return "0.12.1";
46063
- const __dir = dirname4(fileURLToPath2(import.meta.url));
46064
- const pkgPath = resolve9(__dir, "../package.json");
46065
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
46576
+ if (true) return "0.12.3";
46577
+ const __dir = dirname5(fileURLToPath2(import.meta.url));
46578
+ const pkgPath = resolve11(__dir, "../package.json");
46579
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
46066
46580
  return pkg.version;
46067
46581
  }
46068
46582
  var VERSION;
@@ -46084,6 +46598,9 @@ function hasMcphChecks(checks) {
46084
46598
  function hasSessionChecks(checks) {
46085
46599
  return checks.some((c3) => c3.startsWith("session-"));
46086
46600
  }
46601
+ function hasSkillChecks(checks) {
46602
+ return checks.some((c3) => c3.startsWith("skill-"));
46603
+ }
46087
46604
  function deriveChecksToRun(activeChecks, prefix, enabled, allChecks) {
46088
46605
  const filtered = activeChecks.filter((c3) => c3.startsWith(prefix));
46089
46606
  if (filtered.length > 0) return filtered;
@@ -46092,10 +46609,11 @@ function deriveChecksToRun(activeChecks, prefix, enabled, allChecks) {
46092
46609
  async function runAudit(projectRoot, activeChecks, options = {}) {
46093
46610
  const fileResults = [];
46094
46611
  const thresholds = resolveTokenThresholds(options.tokenThresholds);
46095
- const shouldRunContextChecks = !options.mcpOnly && !options.mcphOnly && !options.sessionOnly;
46612
+ const shouldRunContextChecks = !options.mcpOnly && !options.mcphOnly && !options.sessionOnly && !options.skillsOnly;
46096
46613
  const shouldRunMcpChecks = options.mcp || options.mcpGlobal || options.mcpOnly || hasMcpChecks(activeChecks);
46097
46614
  const shouldRunMcphChecks = options.mcph || options.mcphGlobal || options.mcphOnly || hasMcphChecks(activeChecks);
46098
46615
  const shouldRunSessionChecks = options.session || options.sessionOnly || hasSessionChecks(activeChecks);
46616
+ const shouldRunSkillChecks = options.skills || options.skillsOnly || hasSkillChecks(activeChecks);
46099
46617
  if (shouldRunContextChecks) {
46100
46618
  const discovered = await scanForContextFiles(projectRoot, {
46101
46619
  depth: options.depth,
@@ -46152,6 +46670,13 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
46152
46670
  if (activeChecks.includes("ci-secrets")) {
46153
46671
  crossFileIssues.push(...await checkCiSecrets(parsed, projectRoot));
46154
46672
  }
46673
+ if (activeChecks.includes("hook-coverage")) {
46674
+ crossFileIssues.push(
46675
+ ...await checkHookCoverage(projectRoot, void 0, {
46676
+ userGlobal: options.hooksGlobal
46677
+ })
46678
+ );
46679
+ }
46155
46680
  if (crossFileIssues.length > 0) {
46156
46681
  fileResults.push({
46157
46682
  path: "(project)",
@@ -46322,6 +46847,31 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
46322
46847
  });
46323
46848
  }
46324
46849
  }
46850
+ if (shouldRunSkillChecks) {
46851
+ const skillChecksToRun = deriveChecksToRun(
46852
+ activeChecks,
46853
+ "skill-",
46854
+ Boolean(options.skills || options.skillsOnly),
46855
+ ALL_SKILL_CHECKS
46856
+ );
46857
+ if (skillChecksToRun.length > 0) {
46858
+ const skillCtx = scanSkillFiles();
46859
+ const skillIssues = checkSkills(skillCtx, {
46860
+ frontmatter: skillChecksToRun.includes("skill-frontmatter"),
46861
+ brokenRef: skillChecksToRun.includes("skill-broken-ref"),
46862
+ triggerCollision: skillChecksToRun.includes("skill-trigger-collision"),
46863
+ orphaned: skillChecksToRun.includes("skill-orphaned"),
46864
+ deadToolRestriction: skillChecksToRun.includes("skill-dead-tool-restriction")
46865
+ });
46866
+ fileResults.push({
46867
+ path: SKILL_AUDIT_LABEL,
46868
+ isSymlink: false,
46869
+ tokens: 0,
46870
+ lines: 0,
46871
+ issues: skillIssues
46872
+ });
46873
+ }
46874
+ }
46325
46875
  let ignoreReport;
46326
46876
  if (options.ignoreRules && options.ignoreRules.length > 0) {
46327
46877
  const flat = [];
@@ -46383,7 +46933,7 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
46383
46933
  }
46384
46934
  return result;
46385
46935
  }
46386
- var ALL_CHECKS, ALL_MCP_CHECKS, ALL_MCPH_CHECKS, ALL_SESSION_CHECKS;
46936
+ var ALL_CHECKS, ALL_MCP_CHECKS, ALL_MCPH_CHECKS, ALL_SESSION_CHECKS, ALL_SKILL_CHECKS;
46387
46937
  var init_audit = __esm({
46388
46938
  "src/core/audit.ts"() {
46389
46939
  "use strict";
@@ -46425,6 +46975,9 @@ var init_audit = __esm({
46425
46975
  init_ci_coverage();
46426
46976
  init_ci_secrets();
46427
46977
  init_content_secrets();
46978
+ init_hook_coverage();
46979
+ init_skill_scanner();
46980
+ init_skills();
46428
46981
  init_ignore_rules();
46429
46982
  init_types3();
46430
46983
  init_version2();
@@ -46439,7 +46992,8 @@ var init_audit = __esm({
46439
46992
  "frontmatter",
46440
46993
  "ci-coverage",
46441
46994
  "ci-secrets",
46442
- "content-secrets"
46995
+ "content-secrets",
46996
+ "hook-coverage"
46443
46997
  ];
46444
46998
  ALL_MCP_CHECKS = [
46445
46999
  "mcp-schema",
@@ -46467,6 +47021,13 @@ var init_audit = __esm({
46467
47021
  "session-loop-detection",
46468
47022
  "session-memory-index-overflow"
46469
47023
  ];
47024
+ ALL_SKILL_CHECKS = [
47025
+ "skill-frontmatter",
47026
+ "skill-broken-ref",
47027
+ "skill-trigger-collision",
47028
+ "skill-orphaned",
47029
+ "skill-dead-tool-restriction"
47030
+ ];
46470
47031
  }
46471
47032
  });
46472
47033
 
@@ -46664,7 +47225,7 @@ var init_ansi_styles = __esm({
46664
47225
 
46665
47226
  // node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js
46666
47227
  import process4 from "node:process";
46667
- import os2 from "node:os";
47228
+ import os4 from "node:os";
46668
47229
  import tty from "node:tty";
46669
47230
  function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process4.argv) {
46670
47231
  const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
@@ -46722,7 +47283,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
46722
47283
  return min;
46723
47284
  }
46724
47285
  if (process4.platform === "win32") {
46725
- const osRelease = os2.release().split(".");
47286
+ const osRelease = os4.release().split(".");
46726
47287
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
46727
47288
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
46728
47289
  }
@@ -47013,7 +47574,7 @@ var init_source = __esm({
47013
47574
  });
47014
47575
 
47015
47576
  // src/core/fixer.ts
47016
- import * as fs7 from "node:fs";
47577
+ import * as fs9 from "node:fs";
47017
47578
  function applyFixes(result, options = {}) {
47018
47579
  const log = options.quiet ? () => {
47019
47580
  } : console.log.bind(console);
@@ -47055,7 +47616,7 @@ function applyFixes(result, options = {}) {
47055
47616
  let totalFixes = 0;
47056
47617
  const filesModified = [];
47057
47618
  for (const [filePath, fixes] of fixesByFile) {
47058
- const content = fs7.readFileSync(filePath, "utf-8");
47619
+ const content = fs9.readFileSync(filePath, "utf-8");
47059
47620
  const lines = content.split("\n");
47060
47621
  let modified = false;
47061
47622
  const fixesByLine = /* @__PURE__ */ new Map();
@@ -47097,7 +47658,7 @@ function applyFixes(result, options = {}) {
47097
47658
  }
47098
47659
  }
47099
47660
  if (!dryRun) {
47100
- fs7.writeFileSync(filePath, newContent, "utf-8");
47661
+ fs9.writeFileSync(filePath, newContent, "utf-8");
47101
47662
  }
47102
47663
  filesModified.push(filePath);
47103
47664
  totalFixes += perFileFixCount;
@@ -47115,8 +47676,8 @@ var init_fixer = __esm({
47115
47676
  });
47116
47677
 
47117
47678
  // src/core/config.ts
47118
- import * as fs8 from "node:fs";
47119
- import * as path11 from "node:path";
47679
+ import * as fs10 from "node:fs";
47680
+ import * as path14 from "node:path";
47120
47681
  function formatJsonError(content, err) {
47121
47682
  const msg = err instanceof Error ? err.message : String(err);
47122
47683
  const posMatch = msg.match(/position (\d+)/);
@@ -47214,10 +47775,10 @@ function warnIgnoreRulePathPatternMisuse(config2, source) {
47214
47775
  }
47215
47776
  function loadConfig(projectRoot) {
47216
47777
  for (const filename of CONFIG_FILENAMES) {
47217
- const filePath = path11.join(projectRoot, filename);
47778
+ const filePath = path14.join(projectRoot, filename);
47218
47779
  let content;
47219
47780
  try {
47220
- content = stripBom(fs8.readFileSync(filePath, "utf-8"));
47781
+ content = stripBom(fs10.readFileSync(filePath, "utf-8"));
47221
47782
  } catch {
47222
47783
  continue;
47223
47784
  }
@@ -47228,7 +47789,7 @@ function loadConfig(projectRoot) {
47228
47789
  function loadConfigFromExplicitPath(configPath) {
47229
47790
  let content;
47230
47791
  try {
47231
- content = stripBom(fs8.readFileSync(configPath, "utf-8"));
47792
+ content = stripBom(fs10.readFileSync(configPath, "utf-8"));
47232
47793
  } catch (err) {
47233
47794
  const detail = err instanceof Error ? err.message : String(err);
47234
47795
  throw new Error(`could not load config from ${configPath}: ${detail}`, { cause: err });
@@ -47259,7 +47820,10 @@ var init_config2 = __esm({
47259
47820
  "mcphGlobal",
47260
47821
  "mcphStrictEnvToken",
47261
47822
  "session",
47262
- "sessionOnly"
47823
+ "sessionOnly",
47824
+ "skills",
47825
+ "skillsOnly",
47826
+ "hooksGlobal"
47263
47827
  ];
47264
47828
  CONFIG_FILENAMES = [".ctxlintrc", ".ctxlintrc.json"];
47265
47829
  }
@@ -47271,7 +47835,7 @@ __export(server_exports, {
47271
47835
  server: () => server,
47272
47836
  startServer: () => startServer
47273
47837
  });
47274
- import * as path12 from "node:path";
47838
+ import * as path15 from "node:path";
47275
47839
  function describeDisallowed(rawPath) {
47276
47840
  const m = rawPath.match(PATH_DISALLOWED);
47277
47841
  if (!m) return "unknown";
@@ -47284,7 +47848,7 @@ function validateProjectPath(rawPath) {
47284
47848
  `projectPath contains disallowed character ${describeDisallowed(rawPath)} (control chars and shell metacharacters are rejected)`
47285
47849
  );
47286
47850
  }
47287
- const resolved = path12.resolve(rawPath);
47851
+ const resolved = path15.resolve(rawPath);
47288
47852
  if (!isDirectory(resolved)) {
47289
47853
  throw new Error("projectPath is not an existing directory");
47290
47854
  }
@@ -47305,10 +47869,10 @@ function validateFilePathInput(rawPath) {
47305
47869
  }
47306
47870
  }
47307
47871
  function resolveWithinRoot(filePath, root) {
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)) {
47872
+ const resolvedRoot = path15.resolve(root);
47873
+ const resolved = path15.resolve(resolvedRoot, filePath);
47874
+ const rel = path15.relative(resolvedRoot, resolved);
47875
+ if (rel.startsWith("..") || path15.isAbsolute(rel)) {
47312
47876
  throw new Error("path escapes the project root");
47313
47877
  }
47314
47878
  return resolved;
@@ -48823,8 +49387,8 @@ var require_command = __commonJS({
48823
49387
  init_define_WEB_FIRST_SEGMENTS();
48824
49388
  var EventEmitter2 = __require("node:events").EventEmitter;
48825
49389
  var childProcess = __require("node:child_process");
48826
- var path14 = __require("node:path");
48827
- var fs10 = __require("node:fs");
49390
+ var path17 = __require("node:path");
49391
+ var fs12 = __require("node:fs");
48828
49392
  var process11 = __require("node:process");
48829
49393
  var { Argument: Argument2, humanReadableArgName } = require_argument();
48830
49394
  var { CommanderError: CommanderError2 } = require_error();
@@ -49818,7 +50382,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
49818
50382
  * @param {string} subcommandName
49819
50383
  */
49820
50384
  _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
49821
- if (fs10.existsSync(executableFile)) return;
50385
+ if (fs12.existsSync(executableFile)) return;
49822
50386
  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";
49823
50387
  const executableMissing = `'${executableFile}' does not exist
49824
50388
  - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
@@ -49836,11 +50400,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
49836
50400
  let launchWithNode = false;
49837
50401
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
49838
50402
  function findFile(baseDir, baseName) {
49839
- const localBin = path14.resolve(baseDir, baseName);
49840
- if (fs10.existsSync(localBin)) return localBin;
49841
- if (sourceExt.includes(path14.extname(baseName))) return void 0;
50403
+ const localBin = path17.resolve(baseDir, baseName);
50404
+ if (fs12.existsSync(localBin)) return localBin;
50405
+ if (sourceExt.includes(path17.extname(baseName))) return void 0;
49842
50406
  const foundExt = sourceExt.find(
49843
- (ext) => fs10.existsSync(`${localBin}${ext}`)
50407
+ (ext) => fs12.existsSync(`${localBin}${ext}`)
49844
50408
  );
49845
50409
  if (foundExt) return `${localBin}${foundExt}`;
49846
50410
  return void 0;
@@ -49852,21 +50416,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
49852
50416
  if (this._scriptPath) {
49853
50417
  let resolvedScriptPath;
49854
50418
  try {
49855
- resolvedScriptPath = fs10.realpathSync(this._scriptPath);
50419
+ resolvedScriptPath = fs12.realpathSync(this._scriptPath);
49856
50420
  } catch {
49857
50421
  resolvedScriptPath = this._scriptPath;
49858
50422
  }
49859
- executableDir = path14.resolve(
49860
- path14.dirname(resolvedScriptPath),
50423
+ executableDir = path17.resolve(
50424
+ path17.dirname(resolvedScriptPath),
49861
50425
  executableDir
49862
50426
  );
49863
50427
  }
49864
50428
  if (executableDir) {
49865
50429
  let localFile = findFile(executableDir, executableFile);
49866
50430
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
49867
- const legacyName = path14.basename(
50431
+ const legacyName = path17.basename(
49868
50432
  this._scriptPath,
49869
- path14.extname(this._scriptPath)
50433
+ path17.extname(this._scriptPath)
49870
50434
  );
49871
50435
  if (legacyName !== this._name) {
49872
50436
  localFile = findFile(
@@ -49877,7 +50441,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
49877
50441
  }
49878
50442
  executableFile = localFile || executableFile;
49879
50443
  }
49880
- launchWithNode = sourceExt.includes(path14.extname(executableFile));
50444
+ launchWithNode = sourceExt.includes(path17.extname(executableFile));
49881
50445
  let proc;
49882
50446
  if (process11.platform !== "win32") {
49883
50447
  if (launchWithNode) {
@@ -50792,7 +51356,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
50792
51356
  * @return {Command}
50793
51357
  */
50794
51358
  nameFromFilename(filename) {
50795
- this._name = path14.basename(filename, path14.extname(filename));
51359
+ this._name = path17.basename(filename, path17.extname(filename));
50796
51360
  return this;
50797
51361
  }
50798
51362
  /**
@@ -50806,9 +51370,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
50806
51370
  * @param {string} [path]
50807
51371
  * @return {(string|null|Command)}
50808
51372
  */
50809
- executableDir(path15) {
50810
- if (path15 === void 0) return this._executableDir;
50811
- this._executableDir = path15;
51373
+ executableDir(path18) {
51374
+ if (path18 === void 0) return this._executableDir;
51375
+ this._executableDir = path18;
50812
51376
  return this;
50813
51377
  }
50814
51378
  /**
@@ -54161,8 +54725,10 @@ function classifyFile(f) {
54161
54725
  if (f.path === "(project)") return "context";
54162
54726
  if (f.path === "(mcp)") return "mcp";
54163
54727
  if (f.path.includes(SESSION_AUDIT_PATH_MARKER)) return "session";
54728
+ if (f.path.includes(SKILL_AUDIT_PATH_MARKER)) return "skill";
54164
54729
  for (const issue2 of f.issues) {
54165
54730
  if (issue2.check.startsWith("session-")) return "session";
54731
+ if (issue2.check.startsWith("skill-")) return "skill";
54166
54732
  if (issue2.check.startsWith("mcph-")) return "mcph";
54167
54733
  if (issue2.check.startsWith("mcp-")) return "mcp";
54168
54734
  }
@@ -54174,7 +54740,7 @@ function classifyFile(f) {
54174
54740
  return "context";
54175
54741
  }
54176
54742
  function isSyntheticBucket(p2) {
54177
- return p2.startsWith("(") || p2.includes(SESSION_AUDIT_PATH_MARKER);
54743
+ return p2.startsWith("(") || p2.includes(SESSION_AUDIT_PATH_MARKER) || p2.includes(SKILL_AUDIT_PATH_MARKER);
54178
54744
  }
54179
54745
  function formatText(result, verbose = false) {
54180
54746
  const lines = [];
@@ -54187,7 +54753,8 @@ function formatText(result, verbose = false) {
54187
54753
  context: [],
54188
54754
  mcp: [],
54189
54755
  mcph: [],
54190
- session: []
54756
+ session: [],
54757
+ skill: []
54191
54758
  };
54192
54759
  for (const f of result.files) {
54193
54760
  groups[classifyFile(f)].push(f);
@@ -54464,6 +55031,11 @@ function buildRuleDescriptors() {
54464
55031
  shortDescription: { text: "Inline-pasted secret detected in a context file" },
54465
55032
  helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
54466
55033
  },
55034
+ {
55035
+ id: "ctxlint/hook-coverage",
55036
+ shortDescription: { text: "Hook/permission entry references a script that no longer exists" },
55037
+ helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
55038
+ },
54467
55039
  {
54468
55040
  id: "ctxlint/mcp-schema",
54469
55041
  shortDescription: { text: "MCP config structural validation error" },
@@ -54565,6 +55137,31 @@ function buildRuleDescriptors() {
54565
55137
  text: "MEMORY.md exceeds Claude Code's 200-line / 25KB session-load cap"
54566
55138
  },
54567
55139
  helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
55140
+ },
55141
+ {
55142
+ id: "ctxlint/skill-frontmatter",
55143
+ shortDescription: { text: "Skill/agent definition missing required frontmatter" },
55144
+ helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
55145
+ },
55146
+ {
55147
+ id: "ctxlint/skill-broken-ref",
55148
+ shortDescription: { text: "Skill/agent body references a path that does not exist" },
55149
+ helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
55150
+ },
55151
+ {
55152
+ id: "ctxlint/skill-trigger-collision",
55153
+ shortDescription: { text: "Two skills/agents declare the same trigger phrase" },
55154
+ helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
55155
+ },
55156
+ {
55157
+ id: "ctxlint/skill-orphaned",
55158
+ shortDescription: { text: "Skill directory has no SKILL.md" },
55159
+ helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
55160
+ },
55161
+ {
55162
+ id: "ctxlint/skill-dead-tool-restriction",
55163
+ shortDescription: { text: "Agent tool restriction names an unknown Claude Code tool" },
55164
+ helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
54568
55165
  }
54569
55166
  ];
54570
55167
  }
@@ -54582,25 +55179,28 @@ function formatIssue(issue2) {
54582
55179
  }
54583
55180
  return line;
54584
55181
  }
54585
- var GROUP_ORDER, GROUP_LABELS, GROUP_SUMMARY_NOUNS;
55182
+ var SKILL_AUDIT_PATH_MARKER, GROUP_ORDER, GROUP_LABELS, GROUP_SUMMARY_NOUNS;
54586
55183
  var init_reporter = __esm({
54587
55184
  "src/core/reporter.ts"() {
54588
55185
  "use strict";
54589
55186
  init_define_WEB_FIRST_SEGMENTS();
54590
55187
  init_source();
54591
55188
  init_types3();
54592
- GROUP_ORDER = ["context", "mcp", "mcph", "session"];
55189
+ SKILL_AUDIT_PATH_MARKER = "(skill audit)";
55190
+ GROUP_ORDER = ["context", "mcp", "mcph", "session", "skill"];
54593
55191
  GROUP_LABELS = {
54594
55192
  context: "Context Files",
54595
55193
  mcp: "MCP Configs",
54596
55194
  mcph: "mcph Configs",
54597
- session: "Session Audit"
55195
+ session: "Session Audit",
55196
+ skill: "Skill Audit"
54598
55197
  };
54599
55198
  GROUP_SUMMARY_NOUNS = {
54600
55199
  context: "context file",
54601
55200
  mcp: "MCP config",
54602
55201
  mcph: "mcph config",
54603
- session: "session audit"
55202
+ session: "session audit",
55203
+ skill: "skill audit"
54604
55204
  };
54605
55205
  }
54606
55206
  });
@@ -54610,8 +55210,8 @@ var cli_exports = {};
54610
55210
  __export(cli_exports, {
54611
55211
  runCli: () => runCli
54612
55212
  });
54613
- import * as fs9 from "node:fs";
54614
- import * as path13 from "node:path";
55213
+ import * as fs11 from "node:fs";
55214
+ import * as path16 from "node:path";
54615
55215
  function validateCheckNames(names, source) {
54616
55216
  const invalid = names.filter((n7) => n7 && !VALID_CHECKS.has(n7));
54617
55217
  if (invalid.length > 0) {
@@ -54633,8 +55233,12 @@ async function runCli() {
54633
55233
  "--mcph-strict-env-token",
54634
55234
  "Upgrade mcph-config/prefer-env-token from warning to error (strict env-var-only posture)",
54635
55235
  false
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) => {
54637
- const resolvedPath = path13.resolve(projectPath);
55236
+ ).option("--session", "Run session audit checks (cross-project consistency)", false).option("--session-only", "Run only session checks, skip context and MCP checks", false).option("--skills", "Run agent-skill checks (~/.claude/skills + ~/.claude/agents)", false).option("--skills-only", "Run only agent-skill checks, skip everything else", false).option(
55237
+ "--hooks-global",
55238
+ "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])",
55239
+ false
55240
+ ).option("--watch", "Re-lint on context file changes", false).action(async (projectPath, opts) => {
55241
+ const resolvedPath = path16.resolve(projectPath);
54638
55242
  const { config: config2, options, activeChecks } = resolveSession(resolvedPath, opts);
54639
55243
  const spinner = options.format === "text" && !options.quiet ? ora("Scanning for context files...").start() : void 0;
54640
55244
  try {
@@ -54651,6 +55255,9 @@ async function runCli() {
54651
55255
  mcphStrictEnvToken: options.mcphStrictEnvToken,
54652
55256
  session: options.session,
54653
55257
  sessionOnly: options.sessionOnly,
55258
+ skills: options.skills,
55259
+ skillsOnly: options.skillsOnly,
55260
+ hooksGlobal: options.hooksGlobal,
54654
55261
  tokenThresholds: config2?.tokenThresholds,
54655
55262
  ignoreRules: config2?.ignoreRules
54656
55263
  });
@@ -54750,43 +55357,43 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
54750
55357
  const chalk2 = (await Promise.resolve().then(() => (init_source(), source_exports))).default;
54751
55358
  console.log(chalk2.dim("\nWatching for changes... (Ctrl+C to stop)\n"));
54752
55359
  const watchPaths = [
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"),
55360
+ path16.join(resolvedPath, "CLAUDE.md"),
55361
+ path16.join(resolvedPath, "CLAUDE.local.md"),
55362
+ path16.join(resolvedPath, "AGENTS.md"),
55363
+ path16.join(resolvedPath, "AGENT.md"),
55364
+ path16.join(resolvedPath, ".cursorrules"),
55365
+ path16.join(resolvedPath, ".windsurfrules"),
55366
+ path16.join(resolvedPath, ".clinerules"),
55367
+ path16.join(resolvedPath, ".aiderules"),
55368
+ path16.join(resolvedPath, ".continuerules"),
55369
+ path16.join(resolvedPath, ".rules"),
55370
+ path16.join(resolvedPath, ".goosehints"),
55371
+ path16.join(resolvedPath, "GEMINI.md"),
55372
+ path16.join(resolvedPath, "replit.md"),
55373
+ path16.join(resolvedPath, "package.json"),
55374
+ path16.join(resolvedPath, ".mcp.json"),
54768
55375
  // mcph configs — without these, edits to the .mcph.json the user is
54769
55376
  // actively iterating on don't trigger a re-lint.
54770
- path13.join(resolvedPath, ".mcph.json"),
54771
- path13.join(resolvedPath, ".mcph.local.json"),
55377
+ path16.join(resolvedPath, ".mcph.json"),
55378
+ path16.join(resolvedPath, ".mcph.local.json"),
54772
55379
  // ctxlint config — re-resolve on edit so threshold changes,
54773
55380
  // ignore-list edits, and check-list overrides take effect without
54774
55381
  // restarting the watcher.
54775
- path13.join(resolvedPath, ".ctxlintrc"),
54776
- path13.join(resolvedPath, ".ctxlintrc.json")
55382
+ path16.join(resolvedPath, ".ctxlintrc"),
55383
+ path16.join(resolvedPath, ".ctxlintrc.json")
54777
55384
  ];
54778
55385
  const watchDirs = [
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")
55386
+ path16.join(resolvedPath, ".claude"),
55387
+ path16.join(resolvedPath, ".cursor"),
55388
+ path16.join(resolvedPath, ".github"),
55389
+ path16.join(resolvedPath, ".windsurf"),
55390
+ path16.join(resolvedPath, ".aide"),
55391
+ path16.join(resolvedPath, ".amazonq"),
55392
+ path16.join(resolvedPath, ".goose"),
55393
+ path16.join(resolvedPath, ".junie"),
55394
+ path16.join(resolvedPath, ".aiassistant"),
55395
+ path16.join(resolvedPath, ".continue"),
55396
+ path16.join(resolvedPath, ".vscode")
54790
55397
  ];
54791
55398
  const watchers = [];
54792
55399
  let debounceTimer = null;
@@ -54818,6 +55425,9 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
54818
55425
  mcphStrictEnvToken: liveOptions.mcphStrictEnvToken,
54819
55426
  session: liveOptions.session,
54820
55427
  sessionOnly: liveOptions.sessionOnly,
55428
+ skills: liveOptions.skills,
55429
+ skillsOnly: liveOptions.skillsOnly,
55430
+ hooksGlobal: liveOptions.hooksGlobal,
54821
55431
  tokenThresholds: liveConfig?.tokenThresholds,
54822
55432
  ignoreRules: liveConfig?.ignoreRules
54823
55433
  });
@@ -54845,13 +55455,13 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
54845
55455
  };
54846
55456
  for (const filePath of watchPaths) {
54847
55457
  try {
54848
- watchers.push(fs9.watch(filePath, rerun));
55458
+ watchers.push(fs11.watch(filePath, rerun));
54849
55459
  } catch {
54850
55460
  }
54851
55461
  }
54852
55462
  for (const dir of watchDirs) {
54853
55463
  try {
54854
- watchers.push(fs9.watch(dir, { recursive: true }, rerun));
55464
+ watchers.push(fs11.watch(dir, { recursive: true }, rerun));
54855
55465
  } catch {
54856
55466
  }
54857
55467
  }
@@ -54872,17 +55482,17 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
54872
55482
  }
54873
55483
  });
54874
55484
  program2.command("init").description("Set up a git pre-commit hook that runs ctxlint --strict").argument("[path]", "Project directory", ".").action(async (projectPath) => {
54875
- const resolvedRoot = path13.resolve(projectPath);
54876
- const gitDir = path13.join(resolvedRoot, ".git");
54877
- const hooksDir = path13.join(gitDir, "hooks");
54878
- if (!fs9.existsSync(gitDir)) {
55485
+ const resolvedRoot = path16.resolve(projectPath);
55486
+ const gitDir = path16.join(resolvedRoot, ".git");
55487
+ const hooksDir = path16.join(gitDir, "hooks");
55488
+ if (!fs11.existsSync(gitDir)) {
54879
55489
  console.error('Error: not a git repository. Run "git init" first.');
54880
55490
  process.exit(1);
54881
55491
  }
54882
- if (!fs9.existsSync(hooksDir)) {
54883
- fs9.mkdirSync(hooksDir, { recursive: true });
55492
+ if (!fs11.existsSync(hooksDir)) {
55493
+ fs11.mkdirSync(hooksDir, { recursive: true });
54884
55494
  }
54885
- const hookPath = path13.join(hooksDir, "pre-commit");
55495
+ const hookPath = path16.join(hooksDir, "pre-commit");
54886
55496
  const fullHookContent = `#!/bin/sh
54887
55497
  # ctxlint pre-commit hook
54888
55498
  npx @yawlabs/ctxlint@${VERSION} --strict
@@ -54891,16 +55501,16 @@ npx @yawlabs/ctxlint@${VERSION} --strict
54891
55501
  # ctxlint pre-commit hook
54892
55502
  npx @yawlabs/ctxlint@${VERSION} --strict
54893
55503
  `;
54894
- if (fs9.existsSync(hookPath)) {
54895
- const existing = fs9.readFileSync(hookPath, "utf-8");
55504
+ if (fs11.existsSync(hookPath)) {
55505
+ const existing = fs11.readFileSync(hookPath, "utf-8");
54896
55506
  if (existing.includes("ctxlint")) {
54897
55507
  console.log("Pre-commit hook already includes ctxlint.");
54898
55508
  return;
54899
55509
  }
54900
- fs9.appendFileSync(hookPath, appendHookContent);
55510
+ fs11.appendFileSync(hookPath, appendHookContent);
54901
55511
  console.log("Added ctxlint to existing pre-commit hook.");
54902
55512
  } else {
54903
- fs9.writeFileSync(hookPath, fullHookContent, { mode: 493 });
55513
+ fs11.writeFileSync(hookPath, fullHookContent, { mode: 493 });
54904
55514
  console.log("Created pre-commit hook at .git/hooks/pre-commit");
54905
55515
  }
54906
55516
  console.log("ctxlint will now run automatically before each commit.");
@@ -54918,7 +55528,7 @@ async function promptYesNo(question) {
54918
55528
  }
54919
55529
  }
54920
55530
  function resolveSession(resolvedPath, opts) {
54921
- const configPath = opts.config ? path13.resolve(opts.config) : void 0;
55531
+ const configPath = opts.config ? path16.resolve(opts.config) : void 0;
54922
55532
  const config2 = configPath ? loadConfigFromPath(configPath) : loadConfig(resolvedPath);
54923
55533
  const mcpGlobal = opts.mcpGlobal || config2?.mcpGlobal || false;
54924
55534
  const mcpOnly = opts.mcpOnly || config2?.mcpOnly || false;
@@ -54929,6 +55539,9 @@ function resolveSession(resolvedPath, opts) {
54929
55539
  const mcphStrictEnvToken = opts.mcphStrictEnvToken || config2?.mcphStrictEnvToken || false;
54930
55540
  const sessionOnly = opts.sessionOnly || config2?.sessionOnly || false;
54931
55541
  const sessionFlag = opts.session || sessionOnly || config2?.session || false;
55542
+ const skillsOnly = opts.skillsOnly || config2?.skillsOnly || false;
55543
+ const skillsFlag = opts.skills || skillsOnly || config2?.skills || false;
55544
+ const hooksGlobal = opts.hooksGlobal || config2?.hooksGlobal || false;
54932
55545
  let explicitChecks = opts.checks ? validateCheckNames(
54933
55546
  opts.checks.split(",").map((c3) => c3.trim()),
54934
55547
  "--checks"
@@ -54937,14 +55550,18 @@ function resolveSession(resolvedPath, opts) {
54937
55550
  const hasMcpInChecks = explicitChecks?.some((c3) => c3.startsWith("mcp-") && !c3.startsWith("mcph-")) || false;
54938
55551
  const hasMcphInChecks = explicitChecks?.some((c3) => c3.startsWith("mcph-")) || false;
54939
55552
  const hasSessionInChecks = explicitChecks?.some((c3) => c3.startsWith("session-")) || false;
55553
+ const hasSkillInChecks = explicitChecks?.some((c3) => c3.startsWith("skill-")) || false;
54940
55554
  const effectiveMcp = mcpFlag || hasMcpInChecks;
54941
55555
  const effectiveMcph = mcphFlag || hasMcphInChecks;
54942
55556
  const effectiveSession = sessionFlag || sessionOnly || hasSessionInChecks;
55557
+ const effectiveSkills = skillsFlag || skillsOnly || hasSkillInChecks;
54943
55558
  let checks;
54944
55559
  if (explicitChecks) {
54945
55560
  checks = explicitChecks;
54946
55561
  } else if (sessionOnly) {
54947
55562
  checks = ALL_SESSION_CHECKS;
55563
+ } else if (skillsOnly) {
55564
+ checks = ALL_SKILL_CHECKS;
54948
55565
  } else if (mcpOnly) {
54949
55566
  checks = ALL_MCP_CHECKS;
54950
55567
  } else if (mcphOnly) {
@@ -54955,7 +55572,8 @@ function resolveSession(resolvedPath, opts) {
54955
55572
  ...base,
54956
55573
  ...effectiveMcp ? ALL_MCP_CHECKS : [],
54957
55574
  ...effectiveMcph ? ALL_MCPH_CHECKS : [],
54958
- ...effectiveSession ? ALL_SESSION_CHECKS : []
55575
+ ...effectiveSession ? ALL_SESSION_CHECKS : [],
55576
+ ...effectiveSkills ? ALL_SKILL_CHECKS : []
54959
55577
  ];
54960
55578
  }
54961
55579
  const options = {
@@ -54980,7 +55598,10 @@ function resolveSession(resolvedPath, opts) {
54980
55598
  mcphGlobal,
54981
55599
  mcphStrictEnvToken,
54982
55600
  session: effectiveSession,
54983
- sessionOnly
55601
+ sessionOnly,
55602
+ skills: effectiveSkills,
55603
+ skillsOnly,
55604
+ hooksGlobal
54984
55605
  };
54985
55606
  const activeChecks = options.checks.filter((c3) => !options.ignore.includes(c3));
54986
55607
  return { config: config2, options, activeChecks };
@@ -55014,7 +55635,8 @@ var init_cli = __esm({
55014
55635
  ...ALL_CHECKS,
55015
55636
  ...ALL_MCP_CHECKS,
55016
55637
  ...ALL_MCPH_CHECKS,
55017
- ...ALL_SESSION_CHECKS
55638
+ ...ALL_SESSION_CHECKS,
55639
+ ...ALL_SKILL_CHECKS
55018
55640
  ]);
55019
55641
  }
55020
55642
  });