@yawlabs/ctxlint 0.9.1 → 0.9.2

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
@@ -447,8 +447,8 @@ var init_parseUtil = __esm({
447
447
  init_errors();
448
448
  init_en();
449
449
  makeIssue = (params) => {
450
- const { data, path: path11, errorMaps, issueData } = params;
451
- const fullPath = [...path11, ...issueData.path || []];
450
+ const { data, path: path12, errorMaps, issueData } = params;
451
+ const fullPath = [...path12, ...issueData.path || []];
452
452
  const fullIssue = {
453
453
  ...issueData,
454
454
  path: fullPath
@@ -728,11 +728,11 @@ var init_types = __esm({
728
728
  init_parseUtil();
729
729
  init_util();
730
730
  ParseInputLazyPath = class {
731
- constructor(parent, value, path11, key) {
731
+ constructor(parent, value, path12, key) {
732
732
  this._cachedPath = [];
733
733
  this.parent = parent;
734
734
  this.data = value;
735
- this._path = path11;
735
+ this._path = path12;
736
736
  this._key = key;
737
737
  }
738
738
  get path() {
@@ -4237,10 +4237,10 @@ function mergeDefs(...defs) {
4237
4237
  function cloneDef(schema) {
4238
4238
  return mergeDefs(schema._zod.def);
4239
4239
  }
4240
- function getElementAtPath(obj, path11) {
4241
- if (!path11)
4240
+ function getElementAtPath(obj, path12) {
4241
+ if (!path12)
4242
4242
  return obj;
4243
- return path11.reduce((acc, key) => acc?.[key], obj);
4243
+ return path12.reduce((acc, key) => acc?.[key], obj);
4244
4244
  }
4245
4245
  function promiseAllObject(promisesObj) {
4246
4246
  const keys = Object.keys(promisesObj);
@@ -4552,11 +4552,11 @@ function aborted(x3, startIndex = 0) {
4552
4552
  }
4553
4553
  return false;
4554
4554
  }
4555
- function prefixIssues(path11, issues) {
4555
+ function prefixIssues(path12, issues) {
4556
4556
  return issues.map((iss) => {
4557
4557
  var _a3;
4558
4558
  (_a3 = iss).path ?? (_a3.path = []);
4559
- iss.path.unshift(path11);
4559
+ iss.path.unshift(path12);
4560
4560
  return iss;
4561
4561
  });
4562
4562
  }
@@ -4798,7 +4798,7 @@ function formatError(error49, mapper = (issue2) => issue2.message) {
4798
4798
  }
4799
4799
  function treeifyError(error49, mapper = (issue2) => issue2.message) {
4800
4800
  const result = { errors: [] };
4801
- const processError = (error50, path11 = []) => {
4801
+ const processError = (error50, path12 = []) => {
4802
4802
  var _a3, _b;
4803
4803
  for (const issue2 of error50.issues) {
4804
4804
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -4808,7 +4808,7 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
4808
4808
  } else if (issue2.code === "invalid_element") {
4809
4809
  processError({ issues: issue2.issues }, issue2.path);
4810
4810
  } else {
4811
- const fullpath = [...path11, ...issue2.path];
4811
+ const fullpath = [...path12, ...issue2.path];
4812
4812
  if (fullpath.length === 0) {
4813
4813
  result.errors.push(mapper(issue2));
4814
4814
  continue;
@@ -4840,8 +4840,8 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
4840
4840
  }
4841
4841
  function toDotPath(_path) {
4842
4842
  const segs = [];
4843
- const path11 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
4844
- for (const seg of path11) {
4843
+ const path12 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
4844
+ for (const seg of path12) {
4845
4845
  if (typeof seg === "number")
4846
4846
  segs.push(`[${seg}]`);
4847
4847
  else if (typeof seg === "symbol")
@@ -17791,13 +17791,13 @@ function resolveRef(ref, ctx) {
17791
17791
  if (!ref.startsWith("#")) {
17792
17792
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
17793
17793
  }
17794
- const path11 = ref.slice(1).split("/").filter(Boolean);
17795
- if (path11.length === 0) {
17794
+ const path12 = ref.slice(1).split("/").filter(Boolean);
17795
+ if (path12.length === 0) {
17796
17796
  return ctx.rootSchema;
17797
17797
  }
17798
17798
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
17799
- if (path11[0] === defsKey) {
17800
- const key = path11[1];
17799
+ if (path12[0] === defsKey) {
17800
+ const key = path12[1];
17801
17801
  if (!key || !ctx.defs[key]) {
17802
17802
  throw new Error(`Reference not found: ${ref}`);
17803
17803
  }
@@ -25887,8 +25887,8 @@ var require_utils = __commonJS({
25887
25887
  }
25888
25888
  return ind;
25889
25889
  }
25890
- function removeDotSegments(path11) {
25891
- let input = path11;
25890
+ function removeDotSegments(path12) {
25891
+ let input = path12;
25892
25892
  const output = [];
25893
25893
  let nextSlash = -1;
25894
25894
  let len = 0;
@@ -26087,8 +26087,8 @@ var require_schemes = __commonJS({
26087
26087
  wsComponent.secure = void 0;
26088
26088
  }
26089
26089
  if (wsComponent.resourceName) {
26090
- const [path11, query] = wsComponent.resourceName.split("?");
26091
- wsComponent.path = path11 && path11 !== "/" ? path11 : void 0;
26090
+ const [path12, query] = wsComponent.resourceName.split("?");
26091
+ wsComponent.path = path12 && path12 !== "/" ? path12 : void 0;
26092
26092
  wsComponent.query = query;
26093
26093
  wsComponent.resourceName = void 0;
26094
26094
  }
@@ -29450,12 +29450,12 @@ var require_dist = __commonJS({
29450
29450
  throw new Error(`Unknown format "${name}"`);
29451
29451
  return f2;
29452
29452
  };
29453
- function addFormats(ajv, list, fs9, exportName) {
29453
+ function addFormats(ajv, list, fs10, exportName) {
29454
29454
  var _a3;
29455
29455
  var _b;
29456
29456
  (_a3 = (_b = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
29457
29457
  for (const f2 of list)
29458
- ajv.addFormat(f2, fs9[f2]);
29458
+ ajv.addFormat(f2, fs10[f2]);
29459
29459
  }
29460
29460
  module.exports = exports = formatsPlugin;
29461
29461
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -34904,13 +34904,13 @@ var require_tiktoken = __commonJS({
34904
34904
  var wasm = require_tiktoken_bg();
34905
34905
  var imports = {};
34906
34906
  imports["./tiktoken_bg.js"] = wasm;
34907
- var path11 = __require("path");
34908
- var fs9 = __require("fs");
34909
- var candidates = __dirname.split(path11.sep).reduce((memo, _3, index, array2) => {
34910
- const prefix = array2.slice(0, index + 1).join(path11.sep) + path11.sep;
34911
- if (!prefix.includes("node_modules" + path11.sep)) {
34907
+ var path12 = __require("path");
34908
+ var fs10 = __require("fs");
34909
+ var candidates = __dirname.split(path12.sep).reduce((memo, _3, index, array2) => {
34910
+ const prefix = array2.slice(0, index + 1).join(path12.sep) + path12.sep;
34911
+ if (!prefix.includes("node_modules" + path12.sep)) {
34912
34912
  memo.unshift(
34913
- path11.join(
34913
+ path12.join(
34914
34914
  prefix,
34915
34915
  "node_modules",
34916
34916
  "tiktoken",
@@ -34921,11 +34921,11 @@ var require_tiktoken = __commonJS({
34921
34921
  }
34922
34922
  return memo;
34923
34923
  }, []);
34924
- candidates.unshift(path11.join(__dirname, "./tiktoken_bg.wasm"));
34924
+ candidates.unshift(path12.join(__dirname, "./tiktoken_bg.wasm"));
34925
34925
  var bytes = null;
34926
34926
  for (const candidate of candidates) {
34927
34927
  try {
34928
- bytes = fs9.readFileSync(candidate);
34928
+ bytes = fs10.readFileSync(candidate);
34929
34929
  break;
34930
34930
  } catch {
34931
34931
  }
@@ -35841,15 +35841,15 @@ var require_src2 = __commonJS({
35841
35841
  var fs_1 = __require("fs");
35842
35842
  var debug_1 = __importDefault(require_src());
35843
35843
  var log = debug_1.default("@kwsites/file-exists");
35844
- function check2(path11, isFile, isDirectory2) {
35845
- log(`checking %s`, path11);
35844
+ function check2(path12, isFile, isDirectory2) {
35845
+ log(`checking %s`, path12);
35846
35846
  try {
35847
- const stat2 = fs_1.statSync(path11);
35848
- if (stat2.isFile() && isFile) {
35847
+ const stat3 = fs_1.statSync(path12);
35848
+ if (stat3.isFile() && isFile) {
35849
35849
  log(`[OK] path represents a file`);
35850
35850
  return true;
35851
35851
  }
35852
- if (stat2.isDirectory() && isDirectory2) {
35852
+ if (stat3.isDirectory() && isDirectory2) {
35853
35853
  log(`[OK] path represents a directory`);
35854
35854
  return true;
35855
35855
  }
@@ -35864,8 +35864,8 @@ var require_src2 = __commonJS({
35864
35864
  throw e;
35865
35865
  }
35866
35866
  }
35867
- function exists2(path11, type = exports.READABLE) {
35868
- return check2(path11, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
35867
+ function exists2(path12, type = exports.READABLE) {
35868
+ return check2(path12, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
35869
35869
  }
35870
35870
  exports.exists = exists2;
35871
35871
  exports.FILE = 1;
@@ -36359,8 +36359,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
36359
36359
  function forEachLineWithContent(input, callback) {
36360
36360
  return toLinesWithContent(input, true).map((line) => callback(line));
36361
36361
  }
36362
- function folderExists(path11) {
36363
- return (0, import_file_exists.exists)(path11, import_file_exists.FOLDER);
36362
+ function folderExists(path12) {
36363
+ return (0, import_file_exists.exists)(path12, import_file_exists.FOLDER);
36364
36364
  }
36365
36365
  function append(target, item) {
36366
36366
  if (Array.isArray(target)) {
@@ -36551,8 +36551,8 @@ function checkIsRepoRootTask() {
36551
36551
  commands,
36552
36552
  format: "utf-8",
36553
36553
  onError,
36554
- parser(path11) {
36555
- return /^\.(git)?$/.test(path11.trim());
36554
+ parser(path12) {
36555
+ return /^\.(git)?$/.test(path12.trim());
36556
36556
  }
36557
36557
  };
36558
36558
  }
@@ -36808,11 +36808,11 @@ function parseGrep(grep) {
36808
36808
  const paths = /* @__PURE__ */ new Set();
36809
36809
  const results = {};
36810
36810
  forEachLineWithContent(grep, (input) => {
36811
- const [path11, line, preview] = input.split(NULL);
36812
- paths.add(path11);
36813
- (results[path11] = results[path11] || []).push({
36811
+ const [path12, line, preview] = input.split(NULL);
36812
+ paths.add(path12);
36813
+ (results[path12] = results[path12] || []).push({
36814
36814
  line: asNumber(line),
36815
- path: path11,
36815
+ path: path12,
36816
36816
  preview
36817
36817
  });
36818
36818
  });
@@ -37109,14 +37109,14 @@ function hashObjectTask(filePath, write) {
37109
37109
  }
37110
37110
  return straightThroughStringTask(commands, true);
37111
37111
  }
37112
- function parseInit(bare, path11, text) {
37112
+ function parseInit(bare, path12, text) {
37113
37113
  const response = String(text).trim();
37114
37114
  let result;
37115
37115
  if (result = initResponseRegex.exec(response)) {
37116
- return new InitSummary(bare, path11, false, result[1]);
37116
+ return new InitSummary(bare, path12, false, result[1]);
37117
37117
  }
37118
37118
  if (result = reInitResponseRegex.exec(response)) {
37119
- return new InitSummary(bare, path11, true, result[1]);
37119
+ return new InitSummary(bare, path12, true, result[1]);
37120
37120
  }
37121
37121
  let gitDir = "";
37122
37122
  const tokens = response.split(" ");
@@ -37127,12 +37127,12 @@ function parseInit(bare, path11, text) {
37127
37127
  break;
37128
37128
  }
37129
37129
  }
37130
- return new InitSummary(bare, path11, /^re/i.test(response), gitDir);
37130
+ return new InitSummary(bare, path12, /^re/i.test(response), gitDir);
37131
37131
  }
37132
37132
  function hasBareCommand(command) {
37133
37133
  return command.includes(bareCommand);
37134
37134
  }
37135
- function initTask(bare = false, path11, customArgs) {
37135
+ function initTask(bare = false, path12, customArgs) {
37136
37136
  const commands = ["init", ...customArgs];
37137
37137
  if (bare && !hasBareCommand(commands)) {
37138
37138
  commands.splice(1, 0, bareCommand);
@@ -37141,7 +37141,7 @@ function initTask(bare = false, path11, customArgs) {
37141
37141
  commands,
37142
37142
  format: "utf-8",
37143
37143
  parser(text) {
37144
- return parseInit(commands.includes("--bare"), path11, text);
37144
+ return parseInit(commands.includes("--bare"), path12, text);
37145
37145
  }
37146
37146
  };
37147
37147
  }
@@ -37420,14 +37420,14 @@ function splitLine(result, lineStr) {
37420
37420
  default:
37421
37421
  return;
37422
37422
  }
37423
- function data(index, workingDir, path11) {
37423
+ function data(index, workingDir, path12) {
37424
37424
  const raw = `${index}${workingDir}`;
37425
37425
  const handler = parsers6.get(raw);
37426
37426
  if (handler) {
37427
- handler(result, path11);
37427
+ handler(result, path12);
37428
37428
  }
37429
37429
  if (raw !== "##" && raw !== "!!") {
37430
- result.files.push(new FileStatusSummary(path11, index, workingDir));
37430
+ result.files.push(new FileStatusSummary(path12, index, workingDir));
37431
37431
  }
37432
37432
  }
37433
37433
  }
@@ -37614,8 +37614,8 @@ function deleteBranchTask(branch, forceDelete = false) {
37614
37614
  return task;
37615
37615
  }
37616
37616
  function toPath(input) {
37617
- const path11 = input.trim().replace(/^["']|["']$/g, "");
37618
- return path11 && normalize2(path11);
37617
+ const path12 = input.trim().replace(/^["']|["']$/g, "");
37618
+ return path12 && normalize2(path12);
37619
37619
  }
37620
37620
  function checkIgnoreTask(paths) {
37621
37621
  return {
@@ -37754,8 +37754,8 @@ function stashListTask(opt = {}, customArgs) {
37754
37754
  parser: parser4
37755
37755
  };
37756
37756
  }
37757
- function addSubModuleTask(repo, path11) {
37758
- return subModuleTask(["add", repo, path11]);
37757
+ function addSubModuleTask(repo, path12) {
37758
+ return subModuleTask(["add", repo, path12]);
37759
37759
  }
37760
37760
  function initSubModuleTask(customArgs) {
37761
37761
  return subModuleTask(["init", ...customArgs]);
@@ -39019,9 +39019,9 @@ var init_esm2 = __esm({
39019
39019
  "src/lib/responses/InitSummary.ts"() {
39020
39020
  "use strict";
39021
39021
  InitSummary = class {
39022
- constructor(bare, path11, existing, gitDir) {
39022
+ constructor(bare, path12, existing, gitDir) {
39023
39023
  this.bare = bare;
39024
- this.path = path11;
39024
+ this.path = path12;
39025
39025
  this.existing = existing;
39026
39026
  this.gitDir = gitDir;
39027
39027
  }
@@ -39554,12 +39554,12 @@ var init_esm2 = __esm({
39554
39554
  "use strict";
39555
39555
  fromPathRegex = /^(.+)\0(.+)$/;
39556
39556
  FileStatusSummary = class {
39557
- constructor(path11, index, working_dir) {
39558
- this.path = path11;
39557
+ constructor(path12, index, working_dir) {
39558
+ this.path = path12;
39559
39559
  this.index = index;
39560
39560
  this.working_dir = working_dir;
39561
39561
  if (index === "R" || working_dir === "R") {
39562
- const detail = fromPathRegex.exec(path11) || [null, path11, path11];
39562
+ const detail = fromPathRegex.exec(path12) || [null, path12, path12];
39563
39563
  this.from = detail[2] || "";
39564
39564
  this.path = detail[1] || "";
39565
39565
  }
@@ -39821,9 +39821,9 @@ var init_esm2 = __esm({
39821
39821
  next
39822
39822
  );
39823
39823
  }
39824
- hashObject(path11, write) {
39824
+ hashObject(path12, write) {
39825
39825
  return this._runTask(
39826
- hashObjectTask(path11, write === true),
39826
+ hashObjectTask(path12, write === true),
39827
39827
  trailingFunctionArgument(arguments)
39828
39828
  );
39829
39829
  }
@@ -40460,8 +40460,8 @@ var init_esm2 = __esm({
40460
40460
  }
40461
40461
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
40462
40462
  };
40463
- Git2.prototype.submoduleAdd = function(repo, path11, then) {
40464
- return this._runTask(addSubModuleTask2(repo, path11), trailingFunctionArgument2(arguments));
40463
+ Git2.prototype.submoduleAdd = function(repo, path12, then) {
40464
+ return this._runTask(addSubModuleTask2(repo, path12), trailingFunctionArgument2(arguments));
40465
40465
  };
40466
40466
  Git2.prototype.submoduleUpdate = function(args, then) {
40467
40467
  return this._runTask(
@@ -41470,6 +41470,9 @@ function setTokenThresholds(overrides) {
41470
41470
  function resetTokenThresholds() {
41471
41471
  currentThresholds = DEFAULT_THRESHOLDS;
41472
41472
  }
41473
+ function getTokenThresholds() {
41474
+ return currentThresholds;
41475
+ }
41473
41476
  async function checkTokens(file2, _projectRoot) {
41474
41477
  const issues = [];
41475
41478
  const tokens = file2.totalTokens;
@@ -41520,14 +41523,187 @@ var init_tokens2 = __esm({
41520
41523
  info: 1e3,
41521
41524
  warning: 3e3,
41522
41525
  error: 8e3,
41523
- aggregate: 5e3
41526
+ aggregate: 5e3,
41527
+ tierBreakdown: 1e3,
41528
+ tierAggregate: 4e3
41524
41529
  };
41525
41530
  currentThresholds = DEFAULT_THRESHOLDS;
41526
41531
  }
41527
41532
  });
41528
41533
 
41529
- // src/core/checks/redundancy.ts
41534
+ // src/core/checks/tier-tokens.ts
41535
+ import * as fs5 from "node:fs";
41530
41536
  import * as path6 from "node:path";
41537
+ function hasPathsFrontmatter(content) {
41538
+ const lines = content.split("\n");
41539
+ if (lines[0]?.trim() !== "---") return false;
41540
+ for (let i = 1; i < lines.length; i++) {
41541
+ const line = lines[i];
41542
+ if (line.trim() === "---") return false;
41543
+ const match = line.match(/^paths\s*:\s*(.*)$/);
41544
+ if (match) {
41545
+ const val = match[1].trim();
41546
+ if (val && val !== "[]") return true;
41547
+ for (let j3 = i + 1; j3 < lines.length; j3++) {
41548
+ const next = lines[j3];
41549
+ if (next.trim() === "---") return false;
41550
+ if (next.trim() === "") continue;
41551
+ if (next.trim().startsWith("- ")) return true;
41552
+ break;
41553
+ }
41554
+ }
41555
+ }
41556
+ return false;
41557
+ }
41558
+ function isAlwaysLoaded(file2) {
41559
+ const rel = file2.relativePath.replace(/\\/g, "/");
41560
+ if (rel.endsWith(".mdc")) return false;
41561
+ if (rel.startsWith(".github/instructions/")) return false;
41562
+ if (rel.includes("/rules/")) {
41563
+ return !hasPathsFrontmatter(file2.content);
41564
+ }
41565
+ const basename2 = rel.split("/").pop() ?? "";
41566
+ return ALWAYS_LOADED_BASENAMES.has(basename2);
41567
+ }
41568
+ function computeSectionCosts(file2) {
41569
+ if (file2.sections.length === 0) return [];
41570
+ const hasH2 = file2.sections.some((s) => s.level === 2);
41571
+ const topLevel = hasH2 ? 2 : 1;
41572
+ const lines = file2.content.split("\n");
41573
+ return file2.sections.filter((s) => s.level === topLevel).map((s) => {
41574
+ const body = lines.slice(s.startLine - 1, s.endLine).join("\n");
41575
+ return { title: s.title, line: s.startLine, tokens: countTokens(body) };
41576
+ }).sort((a, b2) => b2.tokens - a.tokens);
41577
+ }
41578
+ function loadSettingsSources(projectRoot) {
41579
+ const sources = [];
41580
+ const candidates = [
41581
+ path6.join(projectRoot, ".claude", "settings.json"),
41582
+ path6.join(projectRoot, ".claude", "settings.local.json"),
41583
+ path6.join(process.env.HOME || process.env.USERPROFILE || "", ".claude", "settings.json")
41584
+ ];
41585
+ for (const p of candidates) {
41586
+ try {
41587
+ const content = fs5.readFileSync(p, "utf-8");
41588
+ sources.push(JSON.parse(content));
41589
+ } catch {
41590
+ }
41591
+ }
41592
+ return sources;
41593
+ }
41594
+ function commandIsEnforced(cmd, settings) {
41595
+ const lower = cmd.toLowerCase();
41596
+ for (const s of settings) {
41597
+ for (const entry of s.permissions?.deny ?? []) {
41598
+ if (entry.toLowerCase().includes(lower)) return true;
41599
+ }
41600
+ for (const h of s.hooks?.PreToolUse ?? []) {
41601
+ if ((h.matcher || "").toLowerCase().includes(lower)) return true;
41602
+ for (const sub of h.hooks ?? []) {
41603
+ if ((sub.command || "").toLowerCase().includes(lower)) return true;
41604
+ }
41605
+ }
41606
+ }
41607
+ return false;
41608
+ }
41609
+ function checkHardEnforcement(file2, settings) {
41610
+ const issues = [];
41611
+ const lines = file2.content.split("\n");
41612
+ for (let i = 0; i < lines.length; i++) {
41613
+ const line = lines[i];
41614
+ if (!INVIOLABLE_FRAMING.test(line)) continue;
41615
+ COMMAND_IN_BACKTICKS.lastIndex = 0;
41616
+ const matches = [...line.matchAll(COMMAND_IN_BACKTICKS)].map((m) => m[1]);
41617
+ const cmd = matches.find((m) => /^[\w.-][\w\s.-]*/.test(m) && !m.includes("/"));
41618
+ if (!cmd) continue;
41619
+ if (commandIsEnforced(cmd, settings)) continue;
41620
+ issues.push({
41621
+ severity: "info",
41622
+ check: "tier-tokens",
41623
+ ruleId: "tier-tokens/hard-enforcement-missing",
41624
+ line: i + 1,
41625
+ message: `Inviolable framing ("${line.trim().slice(0, 80)}") without a hook to back it up`,
41626
+ suggestion: `Rules in always-loaded files are advisory. For "${cmd}", add a PreToolUse hook (or permissions.deny entry) in .claude/settings.json so the command is physically blocked.`
41627
+ });
41628
+ }
41629
+ return issues;
41630
+ }
41631
+ async function checkTierTokens(file2, projectRoot) {
41632
+ if (!isAlwaysLoaded(file2)) return [];
41633
+ const issues = [];
41634
+ const threshold = getTokenThresholds().tierBreakdown;
41635
+ if (file2.totalTokens >= threshold) {
41636
+ const sectionCosts = computeSectionCosts(file2);
41637
+ if (sectionCosts.length > 0) {
41638
+ const top = sectionCosts.slice(0, TOP_SECTIONS_TO_REPORT);
41639
+ const heaviest = top[0];
41640
+ const pct = Math.round(heaviest.tokens / file2.totalTokens * 100);
41641
+ const detail = top.map((s) => ` - "${s.title}" (L${s.line}): ~${s.tokens.toLocaleString()} tokens`).join("\n");
41642
+ issues.push({
41643
+ severity: "info",
41644
+ check: "tier-tokens",
41645
+ ruleId: "tier-tokens/section-breakdown",
41646
+ line: heaviest.line,
41647
+ message: `${file2.totalTokens.toLocaleString()} tokens loaded every session \u2014 heaviest top-level section${top.length === 1 ? "" : "s"}:`,
41648
+ detail,
41649
+ suggestion: `"${heaviest.title}" is ~${heaviest.tokens.toLocaleString()} tokens (${pct}% of file). Consider demoting to an on-demand tier (skill, subagent, or memory) so it loads only when relevant.`
41650
+ });
41651
+ }
41652
+ }
41653
+ const settings = loadSettingsSources(projectRoot);
41654
+ issues.push(...checkHardEnforcement(file2, settings));
41655
+ return issues;
41656
+ }
41657
+ function checkAggregateTierTokens(files) {
41658
+ const alwaysLoaded = files.filter(isAlwaysLoaded);
41659
+ if (alwaysLoaded.length < 2) return null;
41660
+ const total = alwaysLoaded.reduce((sum, f2) => sum + f2.totalTokens, 0);
41661
+ const threshold = getTokenThresholds().tierAggregate;
41662
+ if (total < threshold) return null;
41663
+ const breakdown = alwaysLoaded.slice().sort((a, b2) => b2.totalTokens - a.totalTokens).slice(0, 5).map((f2) => ` - ${f2.relativePath}: ~${f2.totalTokens.toLocaleString()} tokens`).join("\n");
41664
+ return {
41665
+ severity: "warning",
41666
+ check: "tier-tokens",
41667
+ ruleId: "tier-tokens/aggregate",
41668
+ line: 0,
41669
+ message: `${alwaysLoaded.length} always-loaded files total ${total.toLocaleString()} tokens \u2014 loaded every session`,
41670
+ detail: breakdown,
41671
+ suggestion: "Consider moving the largest files or their heaviest sections to on-demand tiers (skills, subagents, memory)."
41672
+ };
41673
+ }
41674
+ var ALWAYS_LOADED_BASENAMES, TOP_SECTIONS_TO_REPORT, INVIOLABLE_FRAMING, COMMAND_IN_BACKTICKS;
41675
+ var init_tier_tokens = __esm({
41676
+ async "src/core/checks/tier-tokens.ts"() {
41677
+ "use strict";
41678
+ await init_tokens();
41679
+ init_tokens2();
41680
+ ALWAYS_LOADED_BASENAMES = /* @__PURE__ */ new Set([
41681
+ "CLAUDE.md",
41682
+ "CLAUDE.local.md",
41683
+ "AGENTS.md",
41684
+ "AGENTS.override.md",
41685
+ "AGENT.md",
41686
+ "GEMINI.md",
41687
+ ".cursorrules",
41688
+ ".windsurfrules",
41689
+ ".clinerules",
41690
+ ".aiderules",
41691
+ ".continuerules",
41692
+ ".rules",
41693
+ ".goosehints",
41694
+ "replit.md",
41695
+ "copilot-instructions.md",
41696
+ "guidelines.md",
41697
+ "instructions.md"
41698
+ ]);
41699
+ TOP_SECTIONS_TO_REPORT = 3;
41700
+ INVIOLABLE_FRAMING = /\b(NEVER|ALWAYS|DON'?T|DO NOT|MUST NOT)\b/i;
41701
+ COMMAND_IN_BACKTICKS = /`([^`]+)`/g;
41702
+ }
41703
+ });
41704
+
41705
+ // src/core/checks/redundancy.ts
41706
+ import * as path7 from "node:path";
41531
41707
  function compilePatterns(allDeps) {
41532
41708
  const compiled = [];
41533
41709
  for (const [pkg, mentions] of Object.entries(PACKAGE_TECH_MAP)) {
@@ -41591,7 +41767,7 @@ async function checkRedundancy(file2, projectRoot) {
41591
41767
  );
41592
41768
  if (dirMatch) {
41593
41769
  const dir = dirMatch[1].replace(/[`"]/g, "");
41594
- const fullPath = path6.resolve(projectRoot, dir);
41770
+ const fullPath = path7.resolve(projectRoot, dir);
41595
41771
  if (isDirectory(fullPath)) {
41596
41772
  issues.push({
41597
41773
  severity: "info",
@@ -42411,8 +42587,8 @@ var init_security = __esm({
42411
42587
  });
42412
42588
 
42413
42589
  // src/core/checks/mcp/commands.ts
42414
- import * as fs5 from "node:fs";
42415
- import * as path7 from "node:path";
42590
+ import * as fs6 from "node:fs";
42591
+ import * as path8 from "node:path";
42416
42592
  async function checkMcpCommands(config2, projectRoot) {
42417
42593
  const issues = [];
42418
42594
  if (config2.parseErrors.length > 0) return issues;
@@ -42429,7 +42605,7 @@ async function checkMcpCommands(config2, projectRoot) {
42429
42605
  });
42430
42606
  }
42431
42607
  if (LOCAL_PATH_PATTERN.test(server2.command)) {
42432
- const resolved = path7.resolve(projectRoot, server2.command);
42608
+ const resolved = path8.resolve(projectRoot, server2.command);
42433
42609
  if (!fileExistsSafe(resolved)) {
42434
42610
  issues.push({
42435
42611
  severity: "warning",
@@ -42443,7 +42619,7 @@ async function checkMcpCommands(config2, projectRoot) {
42443
42619
  if (server2.args) {
42444
42620
  for (const arg of server2.args) {
42445
42621
  if (LOCAL_PATH_PATTERN.test(arg) || FILE_PATH_PATTERN.test(arg)) {
42446
- const resolved = path7.resolve(projectRoot, arg);
42622
+ const resolved = path8.resolve(projectRoot, arg);
42447
42623
  if (!fileExistsSafe(resolved)) {
42448
42624
  issues.push({
42449
42625
  severity: "warning",
@@ -42461,7 +42637,7 @@ async function checkMcpCommands(config2, projectRoot) {
42461
42637
  }
42462
42638
  function fileExistsSafe(filePath) {
42463
42639
  try {
42464
- fs5.accessSync(filePath);
42640
+ fs6.accessSync(filePath);
42465
42641
  return true;
42466
42642
  } catch {
42467
42643
  return false;
@@ -42951,7 +43127,7 @@ var init_session_parser = __esm({
42951
43127
  import { readdir, stat } from "node:fs/promises";
42952
43128
  import { createReadStream } from "node:fs";
42953
43129
  import { createInterface } from "node:readline";
42954
- import { dirname as dirname2, join as join4, resolve as resolve4 } from "node:path";
43130
+ import { dirname as dirname2, join as join5, resolve as resolve4 } from "node:path";
42955
43131
  import { existsSync } from "node:fs";
42956
43132
  function detectProviders() {
42957
43133
  return AGENT_DIRS.filter((a) => existsSync(a.dir)).map((a) => a.provider);
@@ -42975,7 +43151,7 @@ async function parseJsonlFiltered(filePath, filter) {
42975
43151
  return results;
42976
43152
  }
42977
43153
  async function readClaudeHistory() {
42978
- const historyPath = join4(home, ".claude", "history.jsonl");
43154
+ const historyPath = join5(home, ".claude", "history.jsonl");
42979
43155
  return parseJsonlFiltered(historyPath, (entry) => {
42980
43156
  if (!entry.display || !entry.project) return null;
42981
43157
  return {
@@ -42988,7 +43164,7 @@ async function readClaudeHistory() {
42988
43164
  });
42989
43165
  }
42990
43166
  async function readCodexHistory() {
42991
- const historyPath = join4(home, ".codex", "history.jsonl");
43167
+ const historyPath = join5(home, ".codex", "history.jsonl");
42992
43168
  return parseJsonlFiltered(historyPath, (entry) => {
42993
43169
  if (!entry.display && !entry.command) return null;
42994
43170
  return {
@@ -43001,18 +43177,18 @@ async function readCodexHistory() {
43001
43177
  });
43002
43178
  }
43003
43179
  async function readClaudeMemories() {
43004
- const projectsDir = join4(home, ".claude", "projects");
43180
+ const projectsDir = join5(home, ".claude", "projects");
43005
43181
  if (!existsSync(projectsDir)) return [];
43006
43182
  const memories = [];
43007
43183
  const projectDirs = await readdir(projectsDir).catch(() => []);
43008
43184
  for (const projDir of projectDirs) {
43009
- const memoryDir = join4(projectsDir, projDir, "memory");
43185
+ const memoryDir = join5(projectsDir, projDir, "memory");
43010
43186
  if (!existsSync(memoryDir)) continue;
43011
43187
  const files = await readdir(memoryDir).catch(() => []);
43012
43188
  for (const file2 of files) {
43013
43189
  if (!file2.endsWith(".md") || file2 === "MEMORY.md") continue;
43014
43190
  try {
43015
- const entry = await parseMemoryFile(join4(memoryDir, file2), projDir);
43191
+ const entry = await parseMemoryFile(join5(memoryDir, file2), projDir);
43016
43192
  memories.push(entry);
43017
43193
  } catch {
43018
43194
  }
@@ -43022,7 +43198,7 @@ async function readClaudeMemories() {
43022
43198
  }
43023
43199
  function detectAiderInSiblings(siblings) {
43024
43200
  for (const sib of siblings) {
43025
- if (existsSync(join4(sib.path, ".aider.chat.history.md"))) {
43201
+ if (existsSync(join5(sib.path, ".aider.chat.history.md"))) {
43026
43202
  return "aider";
43027
43203
  }
43028
43204
  }
@@ -43039,7 +43215,7 @@ async function detectSiblings(projectRoot) {
43039
43215
  const candidates = [];
43040
43216
  for (const entry of entries) {
43041
43217
  if (entry.startsWith(".") || entry === "node_modules") continue;
43042
- const fullPath = join4(parentDir, entry);
43218
+ const fullPath = join5(parentDir, entry);
43043
43219
  const entryPath = resolve4(fullPath);
43044
43220
  if (entryPath === resolve4(projectRoot)) continue;
43045
43221
  try {
@@ -43048,7 +43224,7 @@ async function detectSiblings(projectRoot) {
43048
43224
  } catch {
43049
43225
  continue;
43050
43226
  }
43051
- const isProject = existsSync(join4(fullPath, ".git")) || existsSync(join4(fullPath, "package.json")) || existsSync(join4(fullPath, "Cargo.toml")) || existsSync(join4(fullPath, "go.mod")) || existsSync(join4(fullPath, "pyproject.toml"));
43227
+ const isProject = existsSync(join5(fullPath, ".git")) || existsSync(join5(fullPath, "package.json")) || existsSync(join5(fullPath, "Cargo.toml")) || existsSync(join5(fullPath, "go.mod")) || existsSync(join5(fullPath, "pyproject.toml"));
43052
43228
  if (!isProject) continue;
43053
43229
  candidates.push({ fullPath, entryPath, name: entry });
43054
43230
  }
@@ -43062,7 +43238,7 @@ async function detectSiblings(projectRoot) {
43062
43238
  if (orgMatch) currentOrg = orgMatch[1];
43063
43239
  } catch {
43064
43240
  }
43065
- const gitCandidates = candidates.filter((c2) => existsSync(join4(c2.fullPath, ".git")));
43241
+ const gitCandidates = candidates.filter((c2) => existsSync(join5(c2.fullPath, ".git")));
43066
43242
  const results = await Promise.all(
43067
43243
  gitCandidates.map(async (c2) => {
43068
43244
  const sibling = { path: c2.entryPath.replace(/\\/g, "/"), name: c2.name };
@@ -43134,21 +43310,21 @@ var init_session_scanner = __esm({
43134
43310
  init_session_parser();
43135
43311
  home = process.env.HOME || process.env.USERPROFILE || "";
43136
43312
  AGENT_DIRS = [
43137
- { provider: "claude-code", dir: join4(home, ".claude"), historyFile: "history.jsonl" },
43138
- { provider: "codex-cli", dir: join4(home, ".codex"), historyFile: "history.jsonl" },
43139
- { provider: "vibe-cli", dir: join4(home, ".vibe") },
43313
+ { provider: "claude-code", dir: join5(home, ".claude"), historyFile: "history.jsonl" },
43314
+ { provider: "codex-cli", dir: join5(home, ".codex"), historyFile: "history.jsonl" },
43315
+ { provider: "vibe-cli", dir: join5(home, ".vibe") },
43140
43316
  {
43141
43317
  provider: "amazon-q",
43142
- dir: join4(home, ".aws", "amazonq")
43318
+ dir: join5(home, ".aws", "amazonq")
43143
43319
  },
43144
43320
  {
43145
43321
  provider: "goose",
43146
- dir: process.platform === "win32" ? join4(process.env.APPDATA || "", "Block", "goose") : join4(home, ".config", "goose")
43322
+ dir: process.platform === "win32" ? join5(process.env.APPDATA || "", "Block", "goose") : join5(home, ".config", "goose")
43147
43323
  },
43148
- { provider: "continue", dir: join4(home, ".continue") },
43324
+ { provider: "continue", dir: join5(home, ".continue") },
43149
43325
  {
43150
43326
  provider: "windsurf",
43151
- dir: join4(home, ".windsurf")
43327
+ dir: join5(home, ".windsurf")
43152
43328
  }
43153
43329
  ];
43154
43330
  }
@@ -43210,7 +43386,7 @@ var init_missing_secret = __esm({
43210
43386
 
43211
43387
  // src/core/checks/session/diverged-file.ts
43212
43388
  import { readFile as readFile2 } from "node:fs/promises";
43213
- import { join as join5 } from "node:path";
43389
+ import { join as join6 } from "node:path";
43214
43390
  import { existsSync as existsSync2 } from "node:fs";
43215
43391
  function calculateOverlap(a, b2) {
43216
43392
  const linesA = a.split("\n").map((l) => l.trim()).filter((l) => l.length > 3);
@@ -43228,7 +43404,7 @@ function calculateOverlap(a, b2) {
43228
43404
  async function checkDivergedFile(ctx) {
43229
43405
  const issues = [];
43230
43406
  for (const fileName of CANONICAL_FILES) {
43231
- const currentPath = join5(ctx.currentProject, fileName);
43407
+ const currentPath = join6(ctx.currentProject, fileName);
43232
43408
  if (!existsSync2(currentPath)) continue;
43233
43409
  let currentContent;
43234
43410
  try {
@@ -43238,7 +43414,7 @@ async function checkDivergedFile(ctx) {
43238
43414
  }
43239
43415
  const diverged = [];
43240
43416
  for (const sib of ctx.siblings) {
43241
- const sibPath = join5(sib.path, fileName);
43417
+ const sibPath = join6(sib.path, fileName);
43242
43418
  if (!existsSync2(sibPath)) continue;
43243
43419
  try {
43244
43420
  const sibContent = await readFile2(sibPath, "utf-8");
@@ -43284,12 +43460,12 @@ var init_diverged_file = __esm({
43284
43460
 
43285
43461
  // src/core/checks/session/missing-workflow.ts
43286
43462
  import { readdir as readdir2 } from "node:fs/promises";
43287
- import { join as join6 } from "node:path";
43463
+ import { join as join7 } from "node:path";
43288
43464
  import { existsSync as existsSync3 } from "node:fs";
43289
43465
  async function checkMissingWorkflow(ctx) {
43290
43466
  const issues = [];
43291
- const currentWorkflowDir = join6(ctx.currentProject, ".github", "workflows");
43292
- if (!existsSync3(join6(ctx.currentProject, ".github"))) return issues;
43467
+ const currentWorkflowDir = join7(ctx.currentProject, ".github", "workflows");
43468
+ if (!existsSync3(join7(ctx.currentProject, ".github"))) return issues;
43293
43469
  const currentWorkflows = /* @__PURE__ */ new Set();
43294
43470
  if (existsSync3(currentWorkflowDir)) {
43295
43471
  try {
@@ -43302,7 +43478,7 @@ async function checkMissingWorkflow(ctx) {
43302
43478
  }
43303
43479
  const workflowMap = /* @__PURE__ */ new Map();
43304
43480
  for (const sib of ctx.siblings) {
43305
- const sibWorkflowDir = join6(sib.path, ".github", "workflows");
43481
+ const sibWorkflowDir = join7(sib.path, ".github", "workflows");
43306
43482
  if (!existsSync3(sibWorkflowDir)) continue;
43307
43483
  try {
43308
43484
  const files = await readdir2(sibWorkflowDir);
@@ -43515,12 +43691,68 @@ var init_loop_detection = __esm({
43515
43691
  }
43516
43692
  });
43517
43693
 
43694
+ // src/core/checks/session/memory-index-overflow.ts
43695
+ import { readFile as readFile3, stat as stat2 } from "node:fs/promises";
43696
+ import { join as join8 } from "node:path";
43697
+ async function checkMemoryIndexOverflow(ctx) {
43698
+ const home2 = process.env.HOME || process.env.USERPROFILE || "";
43699
+ if (!home2) return [];
43700
+ const encoded = encodeProjectDir(ctx.currentProject);
43701
+ const memoryFile = join8(home2, ".claude", "projects", encoded, "memory", "MEMORY.md");
43702
+ let stats;
43703
+ try {
43704
+ stats = await stat2(memoryFile);
43705
+ } catch {
43706
+ return [];
43707
+ }
43708
+ const content = await readFile3(memoryFile, "utf-8").catch(() => "");
43709
+ if (!content) return [];
43710
+ const lines = content.split("\n");
43711
+ const lineCount = lines.length;
43712
+ const byteSize = stats.size;
43713
+ const issues = [];
43714
+ if (lineCount > MAX_LINES) {
43715
+ const excess = lineCount - MAX_LINES;
43716
+ issues.push({
43717
+ severity: "warning",
43718
+ check: "session-memory-index-overflow",
43719
+ ruleId: "session/memory-index-overflow",
43720
+ line: MAX_LINES + 1,
43721
+ message: `MEMORY.md has ${lineCount.toLocaleString()} lines \u2014 only the first ${MAX_LINES} are loaded. ${excess.toLocaleString()} line(s) are effectively invisible to the agent.`,
43722
+ detail: `File: ${memoryFile}`,
43723
+ suggestion: "Trim older entries, consolidate duplicates, or split into topic files (topic files stay on-demand and don't count toward this cap)."
43724
+ });
43725
+ }
43726
+ if (byteSize > MAX_BYTES) {
43727
+ const excess = byteSize - MAX_BYTES;
43728
+ issues.push({
43729
+ severity: "warning",
43730
+ check: "session-memory-index-overflow",
43731
+ ruleId: "session/memory-index-overflow",
43732
+ line: 0,
43733
+ message: `MEMORY.md is ${byteSize.toLocaleString()} bytes \u2014 only the first ${MAX_BYTES.toLocaleString()} bytes are loaded. ~${excess.toLocaleString()} bytes are effectively invisible.`,
43734
+ detail: `File: ${memoryFile}`,
43735
+ suggestion: "Each index entry should stay under ~150 characters. Trim verbose lines or move details into the corresponding topic file."
43736
+ });
43737
+ }
43738
+ return issues;
43739
+ }
43740
+ var MAX_LINES, MAX_BYTES;
43741
+ var init_memory_index_overflow = __esm({
43742
+ "src/core/checks/session/memory-index-overflow.ts"() {
43743
+ "use strict";
43744
+ init_session_parser();
43745
+ MAX_LINES = 200;
43746
+ MAX_BYTES = 25 * 1024;
43747
+ }
43748
+ });
43749
+
43518
43750
  // src/core/checks/ci-coverage.ts
43519
- import { readdir as readdir3, readFile as readFile3 } from "node:fs/promises";
43520
- import { join as join7 } from "node:path";
43751
+ import { readdir as readdir3, readFile as readFile4 } from "node:fs/promises";
43752
+ import { join as join9 } from "node:path";
43521
43753
  import { existsSync as existsSync5 } from "node:fs";
43522
43754
  async function findReleaseWorkflows(projectRoot) {
43523
- const workflowDir = join7(projectRoot, ".github", "workflows");
43755
+ const workflowDir = join9(projectRoot, ".github", "workflows");
43524
43756
  if (!existsSync5(workflowDir)) return [];
43525
43757
  let files;
43526
43758
  try {
@@ -43536,7 +43768,7 @@ async function findReleaseWorkflows(projectRoot) {
43536
43768
  continue;
43537
43769
  }
43538
43770
  try {
43539
- const content = await readFile3(join7(workflowDir, f2), "utf-8");
43771
+ const content = await readFile4(join9(workflowDir, f2), "utf-8");
43540
43772
  const nameMatch = content.match(/^name:\s*(.+)$/m);
43541
43773
  if (nameMatch && RELEASE_FILENAME_PATTERNS.some((p) => p.test(nameMatch[1]))) {
43542
43774
  releaseWorkflows.push(f2);
@@ -43590,11 +43822,11 @@ var init_ci_coverage = __esm({
43590
43822
  });
43591
43823
 
43592
43824
  // src/core/checks/ci-secrets.ts
43593
- import { readdir as readdir4, readFile as readFile4 } from "node:fs/promises";
43594
- import { join as join8 } from "node:path";
43825
+ import { readdir as readdir4, readFile as readFile5 } from "node:fs/promises";
43826
+ import { join as join10 } from "node:path";
43595
43827
  import { existsSync as existsSync6 } from "node:fs";
43596
43828
  async function findSecretUsages(projectRoot) {
43597
- const workflowDir = join8(projectRoot, ".github", "workflows");
43829
+ const workflowDir = join10(projectRoot, ".github", "workflows");
43598
43830
  if (!existsSync6(workflowDir)) return [];
43599
43831
  let files;
43600
43832
  try {
@@ -43607,7 +43839,7 @@ async function findSecretUsages(projectRoot) {
43607
43839
  if (!(f2.endsWith(".yml") || f2.endsWith(".yaml"))) continue;
43608
43840
  let content;
43609
43841
  try {
43610
- content = await readFile4(join8(workflowDir, f2), "utf-8");
43842
+ content = await readFile5(join10(workflowDir, f2), "utf-8");
43611
43843
  } catch {
43612
43844
  continue;
43613
43845
  }
@@ -43664,14 +43896,14 @@ var init_ci_secrets = __esm({
43664
43896
  });
43665
43897
 
43666
43898
  // src/version.ts
43667
- import { readFileSync as readFileSync3 } from "node:fs";
43899
+ import { readFileSync as readFileSync4 } from "node:fs";
43668
43900
  import { resolve as resolve8, dirname as dirname3 } from "node:path";
43669
43901
  import { fileURLToPath } from "node:url";
43670
43902
  function loadVersion() {
43671
- if (true) return "0.9.1";
43903
+ if (true) return "0.9.2";
43672
43904
  const __dir = dirname3(fileURLToPath(import.meta.url));
43673
43905
  const pkgPath = resolve8(__dir, "../package.json");
43674
- const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
43906
+ const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
43675
43907
  return pkg.version;
43676
43908
  }
43677
43909
  var VERSION;
@@ -43706,6 +43938,8 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
43706
43938
  if (activeChecks.includes("commands")) checkPromises.push(checkCommands(file2, projectRoot));
43707
43939
  if (activeChecks.includes("staleness")) checkPromises.push(checkStaleness(file2, projectRoot));
43708
43940
  if (activeChecks.includes("tokens")) checkPromises.push(checkTokens(file2, projectRoot));
43941
+ if (activeChecks.includes("tier-tokens"))
43942
+ checkPromises.push(checkTierTokens(file2, projectRoot));
43709
43943
  if (activeChecks.includes("redundancy"))
43710
43944
  checkPromises.push(checkRedundancy(file2, projectRoot));
43711
43945
  if (activeChecks.includes("frontmatter"))
@@ -43728,6 +43962,10 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
43728
43962
  );
43729
43963
  if (aggIssue) crossFileIssues.push(aggIssue);
43730
43964
  }
43965
+ if (activeChecks.includes("tier-tokens")) {
43966
+ const tierAgg = checkAggregateTierTokens(parsed);
43967
+ if (tierAgg) crossFileIssues.push(tierAgg);
43968
+ }
43731
43969
  if (activeChecks.includes("redundancy")) {
43732
43970
  crossFileIssues.push(...checkDuplicateContent(parsed));
43733
43971
  }
@@ -43825,6 +44063,8 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
43825
44063
  sessionPromises.push(checkDuplicateMemory(sessionCtx));
43826
44064
  if (sessionChecksToRun.includes("session-loop-detection"))
43827
44065
  sessionPromises.push(checkLoopDetection(sessionCtx));
44066
+ if (sessionChecksToRun.includes("session-memory-index-overflow"))
44067
+ sessionPromises.push(checkMemoryIndexOverflow(sessionCtx));
43828
44068
  const sessionResults = await Promise.all(sessionPromises);
43829
44069
  const sessionIssues = sessionResults.flat();
43830
44070
  if (sessionIssues.length > 0) {
@@ -43882,6 +44122,7 @@ var init_audit = __esm({
43882
44122
  init_commands();
43883
44123
  init_staleness();
43884
44124
  init_tokens2();
44125
+ await init_tier_tokens();
43885
44126
  await init_redundancy();
43886
44127
  init_contradictions();
43887
44128
  init_frontmatter();
@@ -43900,6 +44141,7 @@ var init_audit = __esm({
43900
44141
  init_stale_memory();
43901
44142
  init_duplicate_memory();
43902
44143
  init_loop_detection();
44144
+ init_memory_index_overflow();
43903
44145
  init_ci_coverage();
43904
44146
  init_ci_secrets();
43905
44147
  init_version2();
@@ -43908,6 +44150,7 @@ var init_audit = __esm({
43908
44150
  "commands",
43909
44151
  "staleness",
43910
44152
  "tokens",
44153
+ "tier-tokens",
43911
44154
  "redundancy",
43912
44155
  "contradictions",
43913
44156
  "frontmatter",
@@ -43930,7 +44173,8 @@ var init_audit = __esm({
43930
44173
  "session-missing-workflow",
43931
44174
  "session-stale-memory",
43932
44175
  "session-duplicate-memory",
43933
- "session-loop-detection"
44176
+ "session-loop-detection",
44177
+ "session-memory-index-overflow"
43934
44178
  ];
43935
44179
  }
43936
44180
  });
@@ -44474,7 +44718,7 @@ var init_source = __esm({
44474
44718
  });
44475
44719
 
44476
44720
  // src/core/fixer.ts
44477
- import * as fs6 from "node:fs";
44721
+ import * as fs7 from "node:fs";
44478
44722
  function applyFixes(result, options = {}) {
44479
44723
  const log = options.quiet ? () => {
44480
44724
  } : console.log.bind(console);
@@ -44491,7 +44735,7 @@ function applyFixes(result, options = {}) {
44491
44735
  let totalFixes = 0;
44492
44736
  const filesModified = [];
44493
44737
  for (const [filePath, fixes] of fixesByFile) {
44494
- const content = fs6.readFileSync(filePath, "utf-8");
44738
+ const content = fs7.readFileSync(filePath, "utf-8");
44495
44739
  const lines = content.split("\n");
44496
44740
  let modified = false;
44497
44741
  const sortedFixes = [...fixes].sort((a, b2) => b2.line - a.line);
@@ -44529,7 +44773,7 @@ function applyFixes(result, options = {}) {
44529
44773
  continue;
44530
44774
  }
44531
44775
  }
44532
- fs6.writeFileSync(filePath, newContent, "utf-8");
44776
+ fs7.writeFileSync(filePath, newContent, "utf-8");
44533
44777
  filesModified.push(filePath);
44534
44778
  }
44535
44779
  }
@@ -44544,7 +44788,7 @@ var init_fixer = __esm({
44544
44788
 
44545
44789
  // src/mcp/server.ts
44546
44790
  var server_exports = {};
44547
- import * as path8 from "node:path";
44791
+ import * as path9 from "node:path";
44548
44792
  var allCheckNames, checkEnum, server, transport;
44549
44793
  var init_server3 = __esm({
44550
44794
  async "src/mcp/server.ts"() {
@@ -44582,7 +44826,7 @@ var init_server3 = __esm({
44582
44826
  openWorldHint: false
44583
44827
  },
44584
44828
  async ({ projectPath, checks }) => {
44585
- const root = path8.resolve(projectPath || process.cwd());
44829
+ const root = path9.resolve(projectPath || process.cwd());
44586
44830
  const activeChecks = checks?.length ? checks : ALL_CHECKS;
44587
44831
  try {
44588
44832
  const result = await runAudit(root, activeChecks);
@@ -44616,8 +44860,8 @@ var init_server3 = __esm({
44616
44860
  },
44617
44861
  async ({ path: filePath, projectPath }) => {
44618
44862
  try {
44619
- const root = path8.resolve(projectPath || process.cwd());
44620
- const resolved = path8.resolve(root, filePath);
44863
+ const root = path9.resolve(projectPath || process.cwd());
44864
+ const resolved = path9.resolve(root, filePath);
44621
44865
  const result = {
44622
44866
  path: filePath,
44623
44867
  exists: fileExists(resolved) || isDirectory(resolved)
@@ -44658,7 +44902,7 @@ var init_server3 = __esm({
44658
44902
  openWorldHint: false
44659
44903
  },
44660
44904
  async ({ projectPath }) => {
44661
- const root = path8.resolve(projectPath || process.cwd());
44905
+ const root = path9.resolve(projectPath || process.cwd());
44662
44906
  try {
44663
44907
  const discovered = await scanForContextFiles(root);
44664
44908
  const parsed = discovered.map((f2) => parseContextFile(f2));
@@ -44706,7 +44950,7 @@ var init_server3 = __esm({
44706
44950
  openWorldHint: false
44707
44951
  },
44708
44952
  async ({ projectPath, checks }) => {
44709
- const root = path8.resolve(projectPath || process.cwd());
44953
+ const root = path9.resolve(projectPath || process.cwd());
44710
44954
  const activeChecks = checks?.length ? checks : ALL_CHECKS;
44711
44955
  try {
44712
44956
  const result = await runAudit(root, activeChecks);
@@ -44756,7 +45000,7 @@ var init_server3 = __esm({
44756
45000
  openWorldHint: false
44757
45001
  },
44758
45002
  async ({ projectPath, checks, includeGlobal }) => {
44759
- const root = path8.resolve(projectPath || process.cwd());
45003
+ const root = path9.resolve(projectPath || process.cwd());
44760
45004
  const activeChecks = checks?.length ? checks : ALL_MCP_CHECKS;
44761
45005
  try {
44762
45006
  const result = await runAudit(root, activeChecks, {
@@ -44793,7 +45037,7 @@ var init_server3 = __esm({
44793
45037
  openWorldHint: true
44794
45038
  },
44795
45039
  async ({ projectPath, checks }) => {
44796
- const root = path8.resolve(projectPath || process.cwd());
45040
+ const root = path9.resolve(projectPath || process.cwd());
44797
45041
  const activeChecks = checks?.length ? checks : ALL_SESSION_CHECKS;
44798
45042
  try {
44799
45043
  const result = await runAudit(root, activeChecks, {
@@ -45984,8 +46228,8 @@ var require_command = __commonJS({
45984
46228
  "node_modules/.pnpm/commander@14.0.3/node_modules/commander/lib/command.js"(exports) {
45985
46229
  var EventEmitter2 = __require("node:events").EventEmitter;
45986
46230
  var childProcess = __require("node:child_process");
45987
- var path11 = __require("node:path");
45988
- var fs9 = __require("node:fs");
46231
+ var path12 = __require("node:path");
46232
+ var fs10 = __require("node:fs");
45989
46233
  var process11 = __require("node:process");
45990
46234
  var { Argument: Argument2, humanReadableArgName } = require_argument();
45991
46235
  var { CommanderError: CommanderError2 } = require_error();
@@ -46979,7 +47223,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
46979
47223
  * @param {string} subcommandName
46980
47224
  */
46981
47225
  _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
46982
- if (fs9.existsSync(executableFile)) return;
47226
+ if (fs10.existsSync(executableFile)) return;
46983
47227
  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";
46984
47228
  const executableMissing = `'${executableFile}' does not exist
46985
47229
  - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
@@ -46997,11 +47241,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
46997
47241
  let launchWithNode = false;
46998
47242
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
46999
47243
  function findFile(baseDir, baseName) {
47000
- const localBin = path11.resolve(baseDir, baseName);
47001
- if (fs9.existsSync(localBin)) return localBin;
47002
- if (sourceExt.includes(path11.extname(baseName))) return void 0;
47244
+ const localBin = path12.resolve(baseDir, baseName);
47245
+ if (fs10.existsSync(localBin)) return localBin;
47246
+ if (sourceExt.includes(path12.extname(baseName))) return void 0;
47003
47247
  const foundExt = sourceExt.find(
47004
- (ext) => fs9.existsSync(`${localBin}${ext}`)
47248
+ (ext) => fs10.existsSync(`${localBin}${ext}`)
47005
47249
  );
47006
47250
  if (foundExt) return `${localBin}${foundExt}`;
47007
47251
  return void 0;
@@ -47013,21 +47257,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
47013
47257
  if (this._scriptPath) {
47014
47258
  let resolvedScriptPath;
47015
47259
  try {
47016
- resolvedScriptPath = fs9.realpathSync(this._scriptPath);
47260
+ resolvedScriptPath = fs10.realpathSync(this._scriptPath);
47017
47261
  } catch {
47018
47262
  resolvedScriptPath = this._scriptPath;
47019
47263
  }
47020
- executableDir = path11.resolve(
47021
- path11.dirname(resolvedScriptPath),
47264
+ executableDir = path12.resolve(
47265
+ path12.dirname(resolvedScriptPath),
47022
47266
  executableDir
47023
47267
  );
47024
47268
  }
47025
47269
  if (executableDir) {
47026
47270
  let localFile = findFile(executableDir, executableFile);
47027
47271
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
47028
- const legacyName = path11.basename(
47272
+ const legacyName = path12.basename(
47029
47273
  this._scriptPath,
47030
- path11.extname(this._scriptPath)
47274
+ path12.extname(this._scriptPath)
47031
47275
  );
47032
47276
  if (legacyName !== this._name) {
47033
47277
  localFile = findFile(
@@ -47038,7 +47282,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
47038
47282
  }
47039
47283
  executableFile = localFile || executableFile;
47040
47284
  }
47041
- launchWithNode = sourceExt.includes(path11.extname(executableFile));
47285
+ launchWithNode = sourceExt.includes(path12.extname(executableFile));
47042
47286
  let proc;
47043
47287
  if (process11.platform !== "win32") {
47044
47288
  if (launchWithNode) {
@@ -47953,7 +48197,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
47953
48197
  * @return {Command}
47954
48198
  */
47955
48199
  nameFromFilename(filename) {
47956
- this._name = path11.basename(filename, path11.extname(filename));
48200
+ this._name = path12.basename(filename, path12.extname(filename));
47957
48201
  return this;
47958
48202
  }
47959
48203
  /**
@@ -47967,9 +48211,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
47967
48211
  * @param {string} [path]
47968
48212
  * @return {(string|null|Command)}
47969
48213
  */
47970
- executableDir(path12) {
47971
- if (path12 === void 0) return this._executableDir;
47972
- this._executableDir = path12;
48214
+ executableDir(path13) {
48215
+ if (path13 === void 0) return this._executableDir;
48216
+ this._executableDir = path13;
47973
48217
  return this;
47974
48218
  }
47975
48219
  /**
@@ -51606,14 +51850,14 @@ var init_reporter = __esm({
51606
51850
  });
51607
51851
 
51608
51852
  // src/core/config.ts
51609
- import * as fs7 from "node:fs";
51610
- import * as path9 from "node:path";
51853
+ import * as fs8 from "node:fs";
51854
+ import * as path10 from "node:path";
51611
51855
  function loadConfig(projectRoot) {
51612
51856
  for (const filename of CONFIG_FILENAMES) {
51613
- const filePath = path9.join(projectRoot, filename);
51857
+ const filePath = path10.join(projectRoot, filename);
51614
51858
  let content;
51615
51859
  try {
51616
- content = fs7.readFileSync(filePath, "utf-8");
51860
+ content = fs8.readFileSync(filePath, "utf-8");
51617
51861
  } catch {
51618
51862
  continue;
51619
51863
  }
@@ -51639,8 +51883,8 @@ var cli_exports = {};
51639
51883
  __export(cli_exports, {
51640
51884
  runCli: () => runCli
51641
51885
  });
51642
- import * as fs8 from "node:fs";
51643
- import * as path10 from "node:path";
51886
+ import * as fs9 from "node:fs";
51887
+ import * as path11 from "node:path";
51644
51888
  function validateCheckNames(names, source) {
51645
51889
  const invalid = names.filter((n7) => n7 && !VALID_CHECKS.has(n7));
51646
51890
  if (invalid.length > 0) {
@@ -51659,8 +51903,8 @@ async function runCli() {
51659
51903
  ).version(VERSION).argument("[path]", "Project directory to scan", ".").option("--strict", "Exit code 1 on any warning or error (for CI)", false).option("--checks <checks>", "Comma-separated list of checks to run", "").addOption(
51660
51904
  new Option("--format <format>", "Output format: text, json, or sarif").choices(["text", "json", "sarif"]).default("text")
51661
51905
  ).option("--tokens", "Show token breakdown per file", false).option("--verbose", "Show passing checks too", false).option("--fix", "Auto-fix broken paths using git history and fuzzy matching", false).option("--ignore <checks>", "Comma-separated list of checks to ignore", "").option("--quiet", "Suppress all output except errors (exit code only)", false).option("--config <path>", "Path to config file (default: .ctxlintrc in project root)").option("--depth <n>", "Max subdirectory depth to scan (default: 2)", "2").option("--mcp", "Enable MCP config linting alongside context file checks", false).option("--mcp-only", "Run only MCP config checks, skip context file checks", false).option("--mcp-global", "Also scan user/global MCP config files (implies --mcp)", false).option("--mcp-server", "Start the MCP server (for IDE/agent integration)").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) => {
51662
- const resolvedPath = path10.resolve(projectPath);
51663
- const configPath = opts.config ? path10.resolve(opts.config) : void 0;
51906
+ const resolvedPath = path11.resolve(projectPath);
51907
+ const configPath = opts.config ? path11.resolve(opts.config) : void 0;
51664
51908
  const config2 = configPath ? loadConfigFromPath(configPath) : loadConfig(resolvedPath);
51665
51909
  const mcpGlobal = opts.mcpGlobal || false;
51666
51910
  const mcpOnly = opts.mcpOnly || false;
@@ -51779,34 +52023,34 @@ Fixed ${fixSummary.totalFixes} issue${fixSummary.totalFixes !== 1 ? "s" : ""} in
51779
52023
  const chalk2 = (await Promise.resolve().then(() => (init_source(), source_exports))).default;
51780
52024
  console.log(chalk2.dim("\nWatching for changes... (Ctrl+C to stop)\n"));
51781
52025
  const watchPaths = [
51782
- path10.join(resolvedPath, "CLAUDE.md"),
51783
- path10.join(resolvedPath, "CLAUDE.local.md"),
51784
- path10.join(resolvedPath, "AGENTS.md"),
51785
- path10.join(resolvedPath, "AGENT.md"),
51786
- path10.join(resolvedPath, ".cursorrules"),
51787
- path10.join(resolvedPath, ".windsurfrules"),
51788
- path10.join(resolvedPath, ".clinerules"),
51789
- path10.join(resolvedPath, ".aiderules"),
51790
- path10.join(resolvedPath, ".continuerules"),
51791
- path10.join(resolvedPath, ".rules"),
51792
- path10.join(resolvedPath, ".goosehints"),
51793
- path10.join(resolvedPath, "GEMINI.md"),
51794
- path10.join(resolvedPath, "replit.md"),
51795
- path10.join(resolvedPath, "package.json"),
51796
- path10.join(resolvedPath, ".mcp.json")
52026
+ path11.join(resolvedPath, "CLAUDE.md"),
52027
+ path11.join(resolvedPath, "CLAUDE.local.md"),
52028
+ path11.join(resolvedPath, "AGENTS.md"),
52029
+ path11.join(resolvedPath, "AGENT.md"),
52030
+ path11.join(resolvedPath, ".cursorrules"),
52031
+ path11.join(resolvedPath, ".windsurfrules"),
52032
+ path11.join(resolvedPath, ".clinerules"),
52033
+ path11.join(resolvedPath, ".aiderules"),
52034
+ path11.join(resolvedPath, ".continuerules"),
52035
+ path11.join(resolvedPath, ".rules"),
52036
+ path11.join(resolvedPath, ".goosehints"),
52037
+ path11.join(resolvedPath, "GEMINI.md"),
52038
+ path11.join(resolvedPath, "replit.md"),
52039
+ path11.join(resolvedPath, "package.json"),
52040
+ path11.join(resolvedPath, ".mcp.json")
51797
52041
  ];
51798
52042
  const watchDirs = [
51799
- path10.join(resolvedPath, ".claude"),
51800
- path10.join(resolvedPath, ".cursor"),
51801
- path10.join(resolvedPath, ".github"),
51802
- path10.join(resolvedPath, ".windsurf"),
51803
- path10.join(resolvedPath, ".aide"),
51804
- path10.join(resolvedPath, ".amazonq"),
51805
- path10.join(resolvedPath, ".goose"),
51806
- path10.join(resolvedPath, ".junie"),
51807
- path10.join(resolvedPath, ".aiassistant"),
51808
- path10.join(resolvedPath, ".continue"),
51809
- path10.join(resolvedPath, ".vscode")
52043
+ path11.join(resolvedPath, ".claude"),
52044
+ path11.join(resolvedPath, ".cursor"),
52045
+ path11.join(resolvedPath, ".github"),
52046
+ path11.join(resolvedPath, ".windsurf"),
52047
+ path11.join(resolvedPath, ".aide"),
52048
+ path11.join(resolvedPath, ".amazonq"),
52049
+ path11.join(resolvedPath, ".goose"),
52050
+ path11.join(resolvedPath, ".junie"),
52051
+ path11.join(resolvedPath, ".aiassistant"),
52052
+ path11.join(resolvedPath, ".continue"),
52053
+ path11.join(resolvedPath, ".vscode")
51810
52054
  ];
51811
52055
  let debounceTimer = null;
51812
52056
  const rerun = () => {
@@ -51849,13 +52093,13 @@ Fixed ${fixSummary.totalFixes} issue${fixSummary.totalFixes !== 1 ? "s" : ""} in
51849
52093
  };
51850
52094
  for (const filePath of watchPaths) {
51851
52095
  try {
51852
- fs8.watch(filePath, rerun);
52096
+ fs9.watch(filePath, rerun);
51853
52097
  } catch {
51854
52098
  }
51855
52099
  }
51856
52100
  for (const dir of watchDirs) {
51857
52101
  try {
51858
- fs8.watch(dir, { recursive: true }, rerun);
52102
+ fs9.watch(dir, { recursive: true }, rerun);
51859
52103
  } catch {
51860
52104
  }
51861
52105
  }
@@ -51864,17 +52108,17 @@ Fixed ${fixSummary.totalFixes} issue${fixSummary.totalFixes !== 1 ? "s" : ""} in
51864
52108
  }
51865
52109
  });
51866
52110
  program2.command("init").description("Set up a git pre-commit hook that runs ctxlint --strict").argument("[path]", "Project directory", ".").action(async (projectPath) => {
51867
- const resolvedRoot = path10.resolve(projectPath);
51868
- const gitDir = path10.join(resolvedRoot, ".git");
51869
- const hooksDir = path10.join(gitDir, "hooks");
51870
- if (!fs8.existsSync(gitDir)) {
52111
+ const resolvedRoot = path11.resolve(projectPath);
52112
+ const gitDir = path11.join(resolvedRoot, ".git");
52113
+ const hooksDir = path11.join(gitDir, "hooks");
52114
+ if (!fs9.existsSync(gitDir)) {
51871
52115
  console.error('Error: not a git repository. Run "git init" first.');
51872
52116
  process.exit(1);
51873
52117
  }
51874
- if (!fs8.existsSync(hooksDir)) {
51875
- fs8.mkdirSync(hooksDir, { recursive: true });
52118
+ if (!fs9.existsSync(hooksDir)) {
52119
+ fs9.mkdirSync(hooksDir, { recursive: true });
51876
52120
  }
51877
- const hookPath = path10.join(hooksDir, "pre-commit");
52121
+ const hookPath = path11.join(hooksDir, "pre-commit");
51878
52122
  const fullHookContent = `#!/bin/sh
51879
52123
  # ctxlint pre-commit hook
51880
52124
  npx @yawlabs/ctxlint --strict
@@ -51883,16 +52127,16 @@ npx @yawlabs/ctxlint --strict
51883
52127
  # ctxlint pre-commit hook
51884
52128
  npx @yawlabs/ctxlint --strict
51885
52129
  `;
51886
- if (fs8.existsSync(hookPath)) {
51887
- const existing = fs8.readFileSync(hookPath, "utf-8");
52130
+ if (fs9.existsSync(hookPath)) {
52131
+ const existing = fs9.readFileSync(hookPath, "utf-8");
51888
52132
  if (existing.includes("ctxlint")) {
51889
52133
  console.log("Pre-commit hook already includes ctxlint.");
51890
52134
  return;
51891
52135
  }
51892
- fs8.appendFileSync(hookPath, appendHookContent);
52136
+ fs9.appendFileSync(hookPath, appendHookContent);
51893
52137
  console.log("Added ctxlint to existing pre-commit hook.");
51894
52138
  } else {
51895
- fs8.writeFileSync(hookPath, fullHookContent, { mode: 493 });
52139
+ fs9.writeFileSync(hookPath, fullHookContent, { mode: 493 });
51896
52140
  console.log("Created pre-commit hook at .git/hooks/pre-commit");
51897
52141
  }
51898
52142
  console.log("ctxlint will now run automatically before each commit.");
@@ -51901,7 +52145,7 @@ npx @yawlabs/ctxlint --strict
51901
52145
  }
51902
52146
  function loadConfigFromPath(configPath) {
51903
52147
  try {
51904
- const content = fs8.readFileSync(configPath, "utf-8");
52148
+ const content = fs9.readFileSync(configPath, "utf-8");
51905
52149
  return JSON.parse(content);
51906
52150
  } catch (err) {
51907
52151
  const detail = err instanceof Error ? err.message : String(err);