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