@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/AGENT_SESSION_LINT_SPEC.md +27 -0
- package/CONTEXT_LINT_SPEC.md +23 -5
- package/README.md +4 -2
- package/agent-session-lint-rules.json +10 -0
- package/context-lint-rules.json +40 -0
- package/dist/index.js +432 -188
- package/package.json +1 -1
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:
|
|
451
|
-
const fullPath = [...
|
|
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,
|
|
731
|
+
constructor(parent, value, path12, key) {
|
|
732
732
|
this._cachedPath = [];
|
|
733
733
|
this.parent = parent;
|
|
734
734
|
this.data = value;
|
|
735
|
-
this._path =
|
|
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,
|
|
4241
|
-
if (!
|
|
4240
|
+
function getElementAtPath(obj, path12) {
|
|
4241
|
+
if (!path12)
|
|
4242
4242
|
return obj;
|
|
4243
|
-
return
|
|
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(
|
|
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(
|
|
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,
|
|
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 = [...
|
|
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
|
|
4844
|
-
for (const seg of
|
|
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
|
|
17795
|
-
if (
|
|
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 (
|
|
17800
|
-
const key =
|
|
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(
|
|
25891
|
-
let input =
|
|
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 [
|
|
26091
|
-
wsComponent.path =
|
|
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,
|
|
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,
|
|
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
|
|
34908
|
-
var
|
|
34909
|
-
var candidates = __dirname.split(
|
|
34910
|
-
const prefix = array2.slice(0, index + 1).join(
|
|
34911
|
-
if (!prefix.includes("node_modules" +
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
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(
|
|
35845
|
-
log(`checking %s`,
|
|
35844
|
+
function check2(path12, isFile, isDirectory2) {
|
|
35845
|
+
log(`checking %s`, path12);
|
|
35846
35846
|
try {
|
|
35847
|
-
const
|
|
35848
|
-
if (
|
|
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 (
|
|
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(
|
|
35868
|
-
return check2(
|
|
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(
|
|
36363
|
-
return (0, import_file_exists.exists)(
|
|
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(
|
|
36555
|
-
return /^\.(git)?$/.test(
|
|
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 [
|
|
36812
|
-
paths.add(
|
|
36813
|
-
(results[
|
|
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:
|
|
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,
|
|
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,
|
|
37116
|
+
return new InitSummary(bare, path12, false, result[1]);
|
|
37117
37117
|
}
|
|
37118
37118
|
if (result = reInitResponseRegex.exec(response)) {
|
|
37119
|
-
return new InitSummary(bare,
|
|
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,
|
|
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,
|
|
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"),
|
|
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,
|
|
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,
|
|
37427
|
+
handler(result, path12);
|
|
37428
37428
|
}
|
|
37429
37429
|
if (raw !== "##" && raw !== "!!") {
|
|
37430
|
-
result.files.push(new FileStatusSummary(
|
|
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
|
|
37618
|
-
return
|
|
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,
|
|
37758
|
-
return subModuleTask(["add", repo,
|
|
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,
|
|
39022
|
+
constructor(bare, path12, existing, gitDir) {
|
|
39023
39023
|
this.bare = bare;
|
|
39024
|
-
this.path =
|
|
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(
|
|
39558
|
-
this.path =
|
|
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(
|
|
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(
|
|
39824
|
+
hashObject(path12, write) {
|
|
39825
39825
|
return this._runTask(
|
|
39826
|
-
hashObjectTask(
|
|
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,
|
|
40464
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
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/
|
|
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 =
|
|
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
|
|
42415
|
-
import * as
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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:
|
|
43138
|
-
{ provider: "codex-cli", dir:
|
|
43139
|
-
{ provider: "vibe-cli", dir:
|
|
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:
|
|
43318
|
+
dir: join5(home, ".aws", "amazonq")
|
|
43143
43319
|
},
|
|
43144
43320
|
{
|
|
43145
43321
|
provider: "goose",
|
|
43146
|
-
dir: process.platform === "win32" ?
|
|
43322
|
+
dir: process.platform === "win32" ? join5(process.env.APPDATA || "", "Block", "goose") : join5(home, ".config", "goose")
|
|
43147
43323
|
},
|
|
43148
|
-
{ provider: "continue", dir:
|
|
43324
|
+
{ provider: "continue", dir: join5(home, ".continue") },
|
|
43149
43325
|
{
|
|
43150
43326
|
provider: "windsurf",
|
|
43151
|
-
dir:
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
43292
|
-
if (!existsSync3(
|
|
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 =
|
|
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
|
|
43520
|
-
import { join as
|
|
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 =
|
|
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
|
|
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
|
|
43594
|
-
import { join as
|
|
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 =
|
|
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
|
|
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
|
|
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.
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
44620
|
-
const resolved =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
45988
|
-
var
|
|
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 (
|
|
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 =
|
|
47001
|
-
if (
|
|
47002
|
-
if (sourceExt.includes(
|
|
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) =>
|
|
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 =
|
|
47260
|
+
resolvedScriptPath = fs10.realpathSync(this._scriptPath);
|
|
47017
47261
|
} catch {
|
|
47018
47262
|
resolvedScriptPath = this._scriptPath;
|
|
47019
47263
|
}
|
|
47020
|
-
executableDir =
|
|
47021
|
-
|
|
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 =
|
|
47272
|
+
const legacyName = path12.basename(
|
|
47029
47273
|
this._scriptPath,
|
|
47030
|
-
|
|
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(
|
|
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 =
|
|
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(
|
|
47971
|
-
if (
|
|
47972
|
-
this._executableDir =
|
|
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
|
|
51610
|
-
import * as
|
|
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 =
|
|
51857
|
+
const filePath = path10.join(projectRoot, filename);
|
|
51614
51858
|
let content;
|
|
51615
51859
|
try {
|
|
51616
|
-
content =
|
|
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
|
|
51643
|
-
import * as
|
|
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 =
|
|
51663
|
-
const configPath = opts.config ?
|
|
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
|
-
|
|
51783
|
-
|
|
51784
|
-
|
|
51785
|
-
|
|
51786
|
-
|
|
51787
|
-
|
|
51788
|
-
|
|
51789
|
-
|
|
51790
|
-
|
|
51791
|
-
|
|
51792
|
-
|
|
51793
|
-
|
|
51794
|
-
|
|
51795
|
-
|
|
51796
|
-
|
|
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
|
-
|
|
51800
|
-
|
|
51801
|
-
|
|
51802
|
-
|
|
51803
|
-
|
|
51804
|
-
|
|
51805
|
-
|
|
51806
|
-
|
|
51807
|
-
|
|
51808
|
-
|
|
51809
|
-
|
|
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
|
-
|
|
52096
|
+
fs9.watch(filePath, rerun);
|
|
51853
52097
|
} catch {
|
|
51854
52098
|
}
|
|
51855
52099
|
}
|
|
51856
52100
|
for (const dir of watchDirs) {
|
|
51857
52101
|
try {
|
|
51858
|
-
|
|
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 =
|
|
51868
|
-
const gitDir =
|
|
51869
|
-
const hooksDir =
|
|
51870
|
-
if (!
|
|
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 (!
|
|
51875
|
-
|
|
52118
|
+
if (!fs9.existsSync(hooksDir)) {
|
|
52119
|
+
fs9.mkdirSync(hooksDir, { recursive: true });
|
|
51876
52120
|
}
|
|
51877
|
-
const hookPath =
|
|
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 (
|
|
51887
|
-
const existing =
|
|
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
|
-
|
|
52136
|
+
fs9.appendFileSync(hookPath, appendHookContent);
|
|
51893
52137
|
console.log("Added ctxlint to existing pre-commit hook.");
|
|
51894
52138
|
} else {
|
|
51895
|
-
|
|
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 =
|
|
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);
|