@yawlabs/ctxlint 0.12.3 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.pre-commit-hooks.yaml +1 -1
- package/AGENT_SESSION_LINT_SPEC.md +3 -4
- package/AGENT_SKILL_LINT_SPEC.md +107 -0
- package/CONTEXT_LINT_SPEC.md +21 -4
- package/MCP_CONFIG_LINT_SPEC.md +1 -2
- package/README.md +12 -8
- package/agent-session-lint-rules.json +1 -1
- package/agent-skill-lint-rules.json +75 -0
- package/context-lint-rules.json +17 -1
- package/dist/index.js +838 -203
- package/mcp-config-lint-rules.json +1 -1
- package/mcph-config-lint-rules.json +1 -1
- package/package.json +6 -1
- package/schemas/ctxlint-catalog.schema.json +94 -0
package/dist/index.js
CHANGED
|
@@ -461,8 +461,8 @@ var init_parseUtil = __esm({
|
|
|
461
461
|
init_errors();
|
|
462
462
|
init_en();
|
|
463
463
|
makeIssue = (params) => {
|
|
464
|
-
const { data, path:
|
|
465
|
-
const fullPath = [...
|
|
464
|
+
const { data, path: path17, errorMaps, issueData } = params;
|
|
465
|
+
const fullPath = [...path17, ...issueData.path || []];
|
|
466
466
|
const fullIssue = {
|
|
467
467
|
...issueData,
|
|
468
468
|
path: fullPath
|
|
@@ -745,11 +745,11 @@ var init_types = __esm({
|
|
|
745
745
|
init_parseUtil();
|
|
746
746
|
init_util();
|
|
747
747
|
ParseInputLazyPath = class {
|
|
748
|
-
constructor(parent, value,
|
|
748
|
+
constructor(parent, value, path17, key) {
|
|
749
749
|
this._cachedPath = [];
|
|
750
750
|
this.parent = parent;
|
|
751
751
|
this.data = value;
|
|
752
|
-
this._path =
|
|
752
|
+
this._path = path17;
|
|
753
753
|
this._key = key;
|
|
754
754
|
}
|
|
755
755
|
get path() {
|
|
@@ -4257,10 +4257,10 @@ function mergeDefs(...defs) {
|
|
|
4257
4257
|
function cloneDef(schema) {
|
|
4258
4258
|
return mergeDefs(schema._zod.def);
|
|
4259
4259
|
}
|
|
4260
|
-
function getElementAtPath(obj,
|
|
4261
|
-
if (!
|
|
4260
|
+
function getElementAtPath(obj, path17) {
|
|
4261
|
+
if (!path17)
|
|
4262
4262
|
return obj;
|
|
4263
|
-
return
|
|
4263
|
+
return path17.reduce((acc, key) => acc?.[key], obj);
|
|
4264
4264
|
}
|
|
4265
4265
|
function promiseAllObject(promisesObj) {
|
|
4266
4266
|
const keys = Object.keys(promisesObj);
|
|
@@ -4572,11 +4572,11 @@ function aborted(x3, startIndex = 0) {
|
|
|
4572
4572
|
}
|
|
4573
4573
|
return false;
|
|
4574
4574
|
}
|
|
4575
|
-
function prefixIssues(
|
|
4575
|
+
function prefixIssues(path17, issues) {
|
|
4576
4576
|
return issues.map((iss) => {
|
|
4577
4577
|
var _a3;
|
|
4578
4578
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
4579
|
-
iss.path.unshift(
|
|
4579
|
+
iss.path.unshift(path17);
|
|
4580
4580
|
return iss;
|
|
4581
4581
|
});
|
|
4582
4582
|
}
|
|
@@ -4819,7 +4819,7 @@ function formatError(error49, mapper = (issue2) => issue2.message) {
|
|
|
4819
4819
|
}
|
|
4820
4820
|
function treeifyError(error49, mapper = (issue2) => issue2.message) {
|
|
4821
4821
|
const result = { errors: [] };
|
|
4822
|
-
const processError = (error50,
|
|
4822
|
+
const processError = (error50, path17 = []) => {
|
|
4823
4823
|
var _a3, _b;
|
|
4824
4824
|
for (const issue2 of error50.issues) {
|
|
4825
4825
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -4829,7 +4829,7 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
|
|
|
4829
4829
|
} else if (issue2.code === "invalid_element") {
|
|
4830
4830
|
processError({ issues: issue2.issues }, issue2.path);
|
|
4831
4831
|
} else {
|
|
4832
|
-
const fullpath = [...
|
|
4832
|
+
const fullpath = [...path17, ...issue2.path];
|
|
4833
4833
|
if (fullpath.length === 0) {
|
|
4834
4834
|
result.errors.push(mapper(issue2));
|
|
4835
4835
|
continue;
|
|
@@ -4861,8 +4861,8 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
|
|
|
4861
4861
|
}
|
|
4862
4862
|
function toDotPath(_path) {
|
|
4863
4863
|
const segs = [];
|
|
4864
|
-
const
|
|
4865
|
-
for (const seg of
|
|
4864
|
+
const path17 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
4865
|
+
for (const seg of path17) {
|
|
4866
4866
|
if (typeof seg === "number")
|
|
4867
4867
|
segs.push(`[${seg}]`);
|
|
4868
4868
|
else if (typeof seg === "symbol")
|
|
@@ -17890,13 +17890,13 @@ function resolveRef(ref, ctx) {
|
|
|
17890
17890
|
if (!ref.startsWith("#")) {
|
|
17891
17891
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
17892
17892
|
}
|
|
17893
|
-
const
|
|
17894
|
-
if (
|
|
17893
|
+
const path17 = ref.slice(1).split("/").filter(Boolean);
|
|
17894
|
+
if (path17.length === 0) {
|
|
17895
17895
|
return ctx.rootSchema;
|
|
17896
17896
|
}
|
|
17897
17897
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
17898
|
-
if (
|
|
17899
|
-
const key =
|
|
17898
|
+
if (path17[0] === defsKey) {
|
|
17899
|
+
const key = path17[1];
|
|
17900
17900
|
if (!key || !ctx.defs[key]) {
|
|
17901
17901
|
throw new Error(`Reference not found: ${ref}`);
|
|
17902
17902
|
}
|
|
@@ -22404,7 +22404,7 @@ var init_protocol = __esm({
|
|
|
22404
22404
|
return;
|
|
22405
22405
|
}
|
|
22406
22406
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
22407
|
-
await new Promise((
|
|
22407
|
+
await new Promise((resolve14) => setTimeout(resolve14, pollInterval));
|
|
22408
22408
|
options?.signal?.throwIfAborted();
|
|
22409
22409
|
}
|
|
22410
22410
|
} catch (error49) {
|
|
@@ -22421,7 +22421,7 @@ var init_protocol = __esm({
|
|
|
22421
22421
|
*/
|
|
22422
22422
|
request(request, resultSchema, options) {
|
|
22423
22423
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
22424
|
-
return new Promise((
|
|
22424
|
+
return new Promise((resolve14, reject) => {
|
|
22425
22425
|
const earlyReject = (error49) => {
|
|
22426
22426
|
reject(error49);
|
|
22427
22427
|
};
|
|
@@ -22499,7 +22499,7 @@ var init_protocol = __esm({
|
|
|
22499
22499
|
if (!parseResult.success) {
|
|
22500
22500
|
reject(parseResult.error);
|
|
22501
22501
|
} else {
|
|
22502
|
-
|
|
22502
|
+
resolve14(parseResult.data);
|
|
22503
22503
|
}
|
|
22504
22504
|
} catch (error49) {
|
|
22505
22505
|
reject(error49);
|
|
@@ -22760,12 +22760,12 @@ var init_protocol = __esm({
|
|
|
22760
22760
|
}
|
|
22761
22761
|
} catch {
|
|
22762
22762
|
}
|
|
22763
|
-
return new Promise((
|
|
22763
|
+
return new Promise((resolve14, reject) => {
|
|
22764
22764
|
if (signal.aborted) {
|
|
22765
22765
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
22766
22766
|
return;
|
|
22767
22767
|
}
|
|
22768
|
-
const timeoutId = setTimeout(
|
|
22768
|
+
const timeoutId = setTimeout(resolve14, interval);
|
|
22769
22769
|
signal.addEventListener("abort", () => {
|
|
22770
22770
|
clearTimeout(timeoutId);
|
|
22771
22771
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -25813,7 +25813,7 @@ var require_compile = __commonJS({
|
|
|
25813
25813
|
const schOrFunc = root.refs[ref];
|
|
25814
25814
|
if (schOrFunc)
|
|
25815
25815
|
return schOrFunc;
|
|
25816
|
-
let _sch =
|
|
25816
|
+
let _sch = resolve14.call(this, root, ref);
|
|
25817
25817
|
if (_sch === void 0) {
|
|
25818
25818
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
25819
25819
|
const { schemaId } = this.opts;
|
|
@@ -25840,7 +25840,7 @@ var require_compile = __commonJS({
|
|
|
25840
25840
|
function sameSchemaEnv(s1, s2) {
|
|
25841
25841
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
25842
25842
|
}
|
|
25843
|
-
function
|
|
25843
|
+
function resolve14(root, ref) {
|
|
25844
25844
|
let sch;
|
|
25845
25845
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
25846
25846
|
ref = sch;
|
|
@@ -26056,8 +26056,8 @@ var require_utils = __commonJS({
|
|
|
26056
26056
|
}
|
|
26057
26057
|
return ind;
|
|
26058
26058
|
}
|
|
26059
|
-
function removeDotSegments(
|
|
26060
|
-
let input =
|
|
26059
|
+
function removeDotSegments(path17) {
|
|
26060
|
+
let input = path17;
|
|
26061
26061
|
const output = [];
|
|
26062
26062
|
let nextSlash = -1;
|
|
26063
26063
|
let len = 0;
|
|
@@ -26257,8 +26257,8 @@ var require_schemes = __commonJS({
|
|
|
26257
26257
|
wsComponent.secure = void 0;
|
|
26258
26258
|
}
|
|
26259
26259
|
if (wsComponent.resourceName) {
|
|
26260
|
-
const [
|
|
26261
|
-
wsComponent.path =
|
|
26260
|
+
const [path17, query] = wsComponent.resourceName.split("?");
|
|
26261
|
+
wsComponent.path = path17 && path17 !== "/" ? path17 : void 0;
|
|
26262
26262
|
wsComponent.query = query;
|
|
26263
26263
|
wsComponent.resourceName = void 0;
|
|
26264
26264
|
}
|
|
@@ -26418,7 +26418,7 @@ var require_fast_uri = __commonJS({
|
|
|
26418
26418
|
}
|
|
26419
26419
|
return uri;
|
|
26420
26420
|
}
|
|
26421
|
-
function
|
|
26421
|
+
function resolve14(baseURI, relativeURI, options) {
|
|
26422
26422
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
26423
26423
|
const resolved = resolveComponent(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
26424
26424
|
schemelessOptions.skipEscape = true;
|
|
@@ -26645,7 +26645,7 @@ var require_fast_uri = __commonJS({
|
|
|
26645
26645
|
var fastUri = {
|
|
26646
26646
|
SCHEMES,
|
|
26647
26647
|
normalize: normalize3,
|
|
26648
|
-
resolve:
|
|
26648
|
+
resolve: resolve14,
|
|
26649
26649
|
resolveComponent,
|
|
26650
26650
|
equal,
|
|
26651
26651
|
serialize,
|
|
@@ -29666,12 +29666,12 @@ var require_dist = __commonJS({
|
|
|
29666
29666
|
throw new Error(`Unknown format "${name}"`);
|
|
29667
29667
|
return f;
|
|
29668
29668
|
};
|
|
29669
|
-
function addFormats(ajv, list,
|
|
29669
|
+
function addFormats(ajv, list, fs12, exportName) {
|
|
29670
29670
|
var _a3;
|
|
29671
29671
|
var _b;
|
|
29672
29672
|
(_a3 = (_b = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
29673
29673
|
for (const f of list)
|
|
29674
|
-
ajv.addFormat(f,
|
|
29674
|
+
ajv.addFormat(f, fs12[f]);
|
|
29675
29675
|
}
|
|
29676
29676
|
module.exports = exports = formatsPlugin;
|
|
29677
29677
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -30827,7 +30827,7 @@ var init_mcp = __esm({
|
|
|
30827
30827
|
let task = createTaskResult.task;
|
|
30828
30828
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
30829
30829
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
30830
|
-
await new Promise((
|
|
30830
|
+
await new Promise((resolve14) => setTimeout(resolve14, pollInterval));
|
|
30831
30831
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
30832
30832
|
if (!updatedTask) {
|
|
30833
30833
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -31423,12 +31423,12 @@ var init_stdio2 = __esm({
|
|
|
31423
31423
|
this.onclose?.();
|
|
31424
31424
|
}
|
|
31425
31425
|
send(message) {
|
|
31426
|
-
return new Promise((
|
|
31426
|
+
return new Promise((resolve14) => {
|
|
31427
31427
|
const json2 = serializeMessage(message);
|
|
31428
31428
|
if (this._stdout.write(json2)) {
|
|
31429
|
-
|
|
31429
|
+
resolve14();
|
|
31430
31430
|
} else {
|
|
31431
|
-
this._stdout.once("drain",
|
|
31431
|
+
this._stdout.once("drain", resolve14);
|
|
31432
31432
|
}
|
|
31433
31433
|
});
|
|
31434
31434
|
}
|
|
@@ -35539,7 +35539,7 @@ var require_supports_color = __commonJS({
|
|
|
35539
35539
|
"node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/index.js"(exports, module) {
|
|
35540
35540
|
"use strict";
|
|
35541
35541
|
init_define_WEB_FIRST_SEGMENTS();
|
|
35542
|
-
var
|
|
35542
|
+
var os5 = __require("os");
|
|
35543
35543
|
var tty3 = __require("tty");
|
|
35544
35544
|
var hasFlag2 = require_has_flag();
|
|
35545
35545
|
var { env: env2 } = process;
|
|
@@ -35587,7 +35587,7 @@ var require_supports_color = __commonJS({
|
|
|
35587
35587
|
return min;
|
|
35588
35588
|
}
|
|
35589
35589
|
if (process.platform === "win32") {
|
|
35590
|
-
const osRelease =
|
|
35590
|
+
const osRelease = os5.release().split(".");
|
|
35591
35591
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
35592
35592
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
35593
35593
|
}
|
|
@@ -35836,10 +35836,10 @@ var require_src2 = __commonJS({
|
|
|
35836
35836
|
var fs_1 = __require("fs");
|
|
35837
35837
|
var debug_1 = __importDefault(require_src());
|
|
35838
35838
|
var log = debug_1.default("@kwsites/file-exists");
|
|
35839
|
-
function check2(
|
|
35840
|
-
log(`checking %s`,
|
|
35839
|
+
function check2(path17, isFile, isDirectory2) {
|
|
35840
|
+
log(`checking %s`, path17);
|
|
35841
35841
|
try {
|
|
35842
|
-
const stat3 = fs_1.statSync(
|
|
35842
|
+
const stat3 = fs_1.statSync(path17);
|
|
35843
35843
|
if (stat3.isFile() && isFile) {
|
|
35844
35844
|
log(`[OK] path represents a file`);
|
|
35845
35845
|
return true;
|
|
@@ -35859,8 +35859,8 @@ var require_src2 = __commonJS({
|
|
|
35859
35859
|
throw e;
|
|
35860
35860
|
}
|
|
35861
35861
|
}
|
|
35862
|
-
function exists2(
|
|
35863
|
-
return check2(
|
|
35862
|
+
function exists2(path17, type = exports.READABLE) {
|
|
35863
|
+
return check2(path17, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
|
|
35864
35864
|
}
|
|
35865
35865
|
exports.exists = exists2;
|
|
35866
35866
|
exports.FILE = 1;
|
|
@@ -36453,8 +36453,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
36453
36453
|
function forEachLineWithContent(input, callback) {
|
|
36454
36454
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
36455
36455
|
}
|
|
36456
|
-
function folderExists(
|
|
36457
|
-
return (0, import_file_exists.exists)(
|
|
36456
|
+
function folderExists(path17) {
|
|
36457
|
+
return (0, import_file_exists.exists)(path17, import_file_exists.FOLDER);
|
|
36458
36458
|
}
|
|
36459
36459
|
function append(target, item) {
|
|
36460
36460
|
if (Array.isArray(target)) {
|
|
@@ -36645,8 +36645,8 @@ function checkIsRepoRootTask() {
|
|
|
36645
36645
|
commands,
|
|
36646
36646
|
format: "utf-8",
|
|
36647
36647
|
onError,
|
|
36648
|
-
parser(
|
|
36649
|
-
return /^\.(git)?$/.test(
|
|
36648
|
+
parser(path17) {
|
|
36649
|
+
return /^\.(git)?$/.test(path17.trim());
|
|
36650
36650
|
}
|
|
36651
36651
|
};
|
|
36652
36652
|
}
|
|
@@ -36902,11 +36902,11 @@ function parseGrep(grep) {
|
|
|
36902
36902
|
const paths = /* @__PURE__ */ new Set();
|
|
36903
36903
|
const results = {};
|
|
36904
36904
|
forEachLineWithContent(grep, (input) => {
|
|
36905
|
-
const [
|
|
36906
|
-
paths.add(
|
|
36907
|
-
(results[
|
|
36905
|
+
const [path17, line, preview] = input.split(NULL);
|
|
36906
|
+
paths.add(path17);
|
|
36907
|
+
(results[path17] = results[path17] || []).push({
|
|
36908
36908
|
line: asNumber(line),
|
|
36909
|
-
path:
|
|
36909
|
+
path: path17,
|
|
36910
36910
|
preview
|
|
36911
36911
|
});
|
|
36912
36912
|
});
|
|
@@ -37203,14 +37203,14 @@ function hashObjectTask(filePath, write) {
|
|
|
37203
37203
|
}
|
|
37204
37204
|
return straightThroughStringTask(commands, true);
|
|
37205
37205
|
}
|
|
37206
|
-
function parseInit(bare,
|
|
37206
|
+
function parseInit(bare, path17, text) {
|
|
37207
37207
|
const response = String(text).trim();
|
|
37208
37208
|
let result;
|
|
37209
37209
|
if (result = initResponseRegex.exec(response)) {
|
|
37210
|
-
return new InitSummary(bare,
|
|
37210
|
+
return new InitSummary(bare, path17, false, result[1]);
|
|
37211
37211
|
}
|
|
37212
37212
|
if (result = reInitResponseRegex.exec(response)) {
|
|
37213
|
-
return new InitSummary(bare,
|
|
37213
|
+
return new InitSummary(bare, path17, true, result[1]);
|
|
37214
37214
|
}
|
|
37215
37215
|
let gitDir = "";
|
|
37216
37216
|
const tokens = response.split(" ");
|
|
@@ -37221,12 +37221,12 @@ function parseInit(bare, path14, text) {
|
|
|
37221
37221
|
break;
|
|
37222
37222
|
}
|
|
37223
37223
|
}
|
|
37224
|
-
return new InitSummary(bare,
|
|
37224
|
+
return new InitSummary(bare, path17, /^re/i.test(response), gitDir);
|
|
37225
37225
|
}
|
|
37226
37226
|
function hasBareCommand(command) {
|
|
37227
37227
|
return command.includes(bareCommand);
|
|
37228
37228
|
}
|
|
37229
|
-
function initTask(bare = false,
|
|
37229
|
+
function initTask(bare = false, path17, customArgs) {
|
|
37230
37230
|
const commands = ["init", ...customArgs];
|
|
37231
37231
|
if (bare && !hasBareCommand(commands)) {
|
|
37232
37232
|
commands.splice(1, 0, bareCommand);
|
|
@@ -37235,7 +37235,7 @@ function initTask(bare = false, path14, customArgs) {
|
|
|
37235
37235
|
commands,
|
|
37236
37236
|
format: "utf-8",
|
|
37237
37237
|
parser(text) {
|
|
37238
|
-
return parseInit(commands.includes("--bare"),
|
|
37238
|
+
return parseInit(commands.includes("--bare"), path17, text);
|
|
37239
37239
|
}
|
|
37240
37240
|
};
|
|
37241
37241
|
}
|
|
@@ -37514,14 +37514,14 @@ function splitLine(result, lineStr) {
|
|
|
37514
37514
|
default:
|
|
37515
37515
|
return;
|
|
37516
37516
|
}
|
|
37517
|
-
function data(index, workingDir,
|
|
37517
|
+
function data(index, workingDir, path17) {
|
|
37518
37518
|
const raw = `${index}${workingDir}`;
|
|
37519
37519
|
const handler = parsers6.get(raw);
|
|
37520
37520
|
if (handler) {
|
|
37521
|
-
handler(result,
|
|
37521
|
+
handler(result, path17);
|
|
37522
37522
|
}
|
|
37523
37523
|
if (raw !== "##" && raw !== "!!") {
|
|
37524
|
-
result.files.push(new FileStatusSummary(
|
|
37524
|
+
result.files.push(new FileStatusSummary(path17, index, workingDir));
|
|
37525
37525
|
}
|
|
37526
37526
|
}
|
|
37527
37527
|
}
|
|
@@ -37708,8 +37708,8 @@ function deleteBranchTask(branch, forceDelete = false) {
|
|
|
37708
37708
|
return task;
|
|
37709
37709
|
}
|
|
37710
37710
|
function toPath(input) {
|
|
37711
|
-
const
|
|
37712
|
-
return
|
|
37711
|
+
const path17 = input.trim().replace(/^["']|["']$/g, "");
|
|
37712
|
+
return path17 && normalize2(path17);
|
|
37713
37713
|
}
|
|
37714
37714
|
function checkIgnoreTask(paths) {
|
|
37715
37715
|
return {
|
|
@@ -37848,8 +37848,8 @@ function stashListTask(opt = {}, customArgs) {
|
|
|
37848
37848
|
parser: parser4
|
|
37849
37849
|
};
|
|
37850
37850
|
}
|
|
37851
|
-
function addSubModuleTask(repo,
|
|
37852
|
-
return subModuleTask(["add", repo,
|
|
37851
|
+
function addSubModuleTask(repo, path17) {
|
|
37852
|
+
return subModuleTask(["add", repo, path17]);
|
|
37853
37853
|
}
|
|
37854
37854
|
function initSubModuleTask(customArgs) {
|
|
37855
37855
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -39112,9 +39112,9 @@ var init_esm2 = __esm({
|
|
|
39112
39112
|
"src/lib/responses/InitSummary.ts"() {
|
|
39113
39113
|
"use strict";
|
|
39114
39114
|
InitSummary = class {
|
|
39115
|
-
constructor(bare,
|
|
39115
|
+
constructor(bare, path17, existing, gitDir) {
|
|
39116
39116
|
this.bare = bare;
|
|
39117
|
-
this.path =
|
|
39117
|
+
this.path = path17;
|
|
39118
39118
|
this.existing = existing;
|
|
39119
39119
|
this.gitDir = gitDir;
|
|
39120
39120
|
}
|
|
@@ -39647,12 +39647,12 @@ var init_esm2 = __esm({
|
|
|
39647
39647
|
"use strict";
|
|
39648
39648
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
39649
39649
|
FileStatusSummary = class {
|
|
39650
|
-
constructor(
|
|
39651
|
-
this.path =
|
|
39650
|
+
constructor(path17, index, working_dir) {
|
|
39651
|
+
this.path = path17;
|
|
39652
39652
|
this.index = index;
|
|
39653
39653
|
this.working_dir = working_dir;
|
|
39654
39654
|
if (index === "R" || working_dir === "R") {
|
|
39655
|
-
const detail = fromPathRegex.exec(
|
|
39655
|
+
const detail = fromPathRegex.exec(path17) || [null, path17, path17];
|
|
39656
39656
|
this.from = detail[2] || "";
|
|
39657
39657
|
this.path = detail[1] || "";
|
|
39658
39658
|
}
|
|
@@ -39914,9 +39914,9 @@ var init_esm2 = __esm({
|
|
|
39914
39914
|
next
|
|
39915
39915
|
);
|
|
39916
39916
|
}
|
|
39917
|
-
hashObject(
|
|
39917
|
+
hashObject(path17, write) {
|
|
39918
39918
|
return this._runTask(
|
|
39919
|
-
hashObjectTask(
|
|
39919
|
+
hashObjectTask(path17, write === true),
|
|
39920
39920
|
trailingFunctionArgument(arguments)
|
|
39921
39921
|
);
|
|
39922
39922
|
}
|
|
@@ -40553,8 +40553,8 @@ var init_esm2 = __esm({
|
|
|
40553
40553
|
}
|
|
40554
40554
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
40555
40555
|
};
|
|
40556
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
40557
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
40556
|
+
Git2.prototype.submoduleAdd = function(repo, path17, then) {
|
|
40557
|
+
return this._runTask(addSubModuleTask2(repo, path17), trailingFunctionArgument2(arguments));
|
|
40558
40558
|
};
|
|
40559
40559
|
Git2.prototype.submoduleUpdate = function(args2, then) {
|
|
40560
40560
|
return this._runTask(
|
|
@@ -40905,6 +40905,45 @@ async function getCommitsSinceBatch(projectRoot, paths, since) {
|
|
|
40905
40905
|
}
|
|
40906
40906
|
return out;
|
|
40907
40907
|
}
|
|
40908
|
+
function normalizeRenamePath(p2) {
|
|
40909
|
+
return p2.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
40910
|
+
}
|
|
40911
|
+
function parseRenameLog(result, targetPath) {
|
|
40912
|
+
if (!result.trim()) return null;
|
|
40913
|
+
const want = targetPath ? normalizeRenamePath(targetPath) : void 0;
|
|
40914
|
+
const wantBase = want ? want.split("/").pop() ?? want : void 0;
|
|
40915
|
+
let currentHash = "unknown";
|
|
40916
|
+
let currentDateStr;
|
|
40917
|
+
let basenameFallback = null;
|
|
40918
|
+
const lines = result.trim().split("\n");
|
|
40919
|
+
for (const line of lines) {
|
|
40920
|
+
const headerMatch = line.match(/^([a-f0-9]{7,40})\s+(.+)$/);
|
|
40921
|
+
if (headerMatch) {
|
|
40922
|
+
currentHash = headerMatch[1].substring(0, 7);
|
|
40923
|
+
currentDateStr = headerMatch[2];
|
|
40924
|
+
continue;
|
|
40925
|
+
}
|
|
40926
|
+
if (line.startsWith("R")) {
|
|
40927
|
+
const parts = line.split(" ");
|
|
40928
|
+
if (parts.length >= 3) {
|
|
40929
|
+
const daysAgo = currentDateStr ? Math.floor((Date.now() - new Date(currentDateStr).getTime()) / (1e3 * 60 * 60 * 24)) : 0;
|
|
40930
|
+
const info2 = {
|
|
40931
|
+
oldPath: parts[1],
|
|
40932
|
+
newPath: parts[2],
|
|
40933
|
+
commitHash: currentHash,
|
|
40934
|
+
daysAgo
|
|
40935
|
+
};
|
|
40936
|
+
if (!want) return info2;
|
|
40937
|
+
const oldNorm = normalizeRenamePath(info2.oldPath);
|
|
40938
|
+
if (oldNorm === want) return info2;
|
|
40939
|
+
if (!basenameFallback && wantBase && (oldNorm.split("/").pop() ?? oldNorm) === wantBase) {
|
|
40940
|
+
basenameFallback = info2;
|
|
40941
|
+
}
|
|
40942
|
+
}
|
|
40943
|
+
}
|
|
40944
|
+
}
|
|
40945
|
+
return basenameFallback;
|
|
40946
|
+
}
|
|
40908
40947
|
async function findRenames(projectRoot, filePath) {
|
|
40909
40948
|
try {
|
|
40910
40949
|
const git = getGit(projectRoot);
|
|
@@ -40914,35 +40953,9 @@ async function findRenames(projectRoot, filePath) {
|
|
|
40914
40953
|
"--find-renames",
|
|
40915
40954
|
"--name-status",
|
|
40916
40955
|
"--format=%H %ai",
|
|
40917
|
-
"-
|
|
40918
|
-
"--",
|
|
40919
|
-
filePath
|
|
40956
|
+
"-50"
|
|
40920
40957
|
]);
|
|
40921
|
-
|
|
40922
|
-
let currentHash = "unknown";
|
|
40923
|
-
let currentDateStr;
|
|
40924
|
-
const lines = result.trim().split("\n");
|
|
40925
|
-
for (const line of lines) {
|
|
40926
|
-
const headerMatch = line.match(/^([a-f0-9]{7,40})\s+(.+)$/);
|
|
40927
|
-
if (headerMatch) {
|
|
40928
|
-
currentHash = headerMatch[1].substring(0, 7);
|
|
40929
|
-
currentDateStr = headerMatch[2];
|
|
40930
|
-
continue;
|
|
40931
|
-
}
|
|
40932
|
-
if (line.startsWith("R")) {
|
|
40933
|
-
const parts = line.split(" ");
|
|
40934
|
-
if (parts.length >= 3) {
|
|
40935
|
-
const daysAgo = currentDateStr ? Math.floor((Date.now() - new Date(currentDateStr).getTime()) / (1e3 * 60 * 60 * 24)) : 0;
|
|
40936
|
-
return {
|
|
40937
|
-
oldPath: parts[1],
|
|
40938
|
-
newPath: parts[2],
|
|
40939
|
-
commitHash: currentHash,
|
|
40940
|
-
daysAgo
|
|
40941
|
-
};
|
|
40942
|
-
}
|
|
40943
|
-
}
|
|
40944
|
-
}
|
|
40945
|
-
return null;
|
|
40958
|
+
return parseRenameLog(result, filePath);
|
|
40946
40959
|
} catch {
|
|
40947
40960
|
return null;
|
|
40948
40961
|
}
|
|
@@ -41629,6 +41642,37 @@ function parseTree(text, errors = [], options = ParseOptions.DEFAULT) {
|
|
|
41629
41642
|
}
|
|
41630
41643
|
return result;
|
|
41631
41644
|
}
|
|
41645
|
+
function findNodeAtLocation(root, path17) {
|
|
41646
|
+
if (!root) {
|
|
41647
|
+
return void 0;
|
|
41648
|
+
}
|
|
41649
|
+
let node = root;
|
|
41650
|
+
for (let segment of path17) {
|
|
41651
|
+
if (typeof segment === "string") {
|
|
41652
|
+
if (node.type !== "object" || !Array.isArray(node.children)) {
|
|
41653
|
+
return void 0;
|
|
41654
|
+
}
|
|
41655
|
+
let found = false;
|
|
41656
|
+
for (const propertyNode of node.children) {
|
|
41657
|
+
if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment && propertyNode.children.length === 2) {
|
|
41658
|
+
node = propertyNode.children[1];
|
|
41659
|
+
found = true;
|
|
41660
|
+
break;
|
|
41661
|
+
}
|
|
41662
|
+
}
|
|
41663
|
+
if (!found) {
|
|
41664
|
+
return void 0;
|
|
41665
|
+
}
|
|
41666
|
+
} else {
|
|
41667
|
+
const index = segment;
|
|
41668
|
+
if (node.type !== "array" || index < 0 || !Array.isArray(node.children) || index >= node.children.length) {
|
|
41669
|
+
return void 0;
|
|
41670
|
+
}
|
|
41671
|
+
node = node.children[index];
|
|
41672
|
+
}
|
|
41673
|
+
}
|
|
41674
|
+
return node;
|
|
41675
|
+
}
|
|
41632
41676
|
function getNodeValue(node) {
|
|
41633
41677
|
switch (node.type) {
|
|
41634
41678
|
case "array":
|
|
@@ -42001,7 +42045,7 @@ var init_edit = __esm({
|
|
|
42001
42045
|
});
|
|
42002
42046
|
|
|
42003
42047
|
// node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/main.js
|
|
42004
|
-
var ScanError, SyntaxKind, parseTree2, getNodeValue2, ParseErrorCode;
|
|
42048
|
+
var ScanError, SyntaxKind, parseTree2, findNodeAtLocation2, getNodeValue2, ParseErrorCode;
|
|
42005
42049
|
var init_main = __esm({
|
|
42006
42050
|
"node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/main.js"() {
|
|
42007
42051
|
"use strict";
|
|
@@ -42039,6 +42083,7 @@ var init_main = __esm({
|
|
|
42039
42083
|
SyntaxKind2[SyntaxKind2["EOF"] = 17] = "EOF";
|
|
42040
42084
|
})(SyntaxKind || (SyntaxKind = {}));
|
|
42041
42085
|
parseTree2 = parseTree;
|
|
42086
|
+
findNodeAtLocation2 = findNodeAtLocation;
|
|
42042
42087
|
getNodeValue2 = getNodeValue;
|
|
42043
42088
|
(function(ParseErrorCode2) {
|
|
42044
42089
|
ParseErrorCode2[ParseErrorCode2["InvalidSymbol"] = 1] = "InvalidSymbol";
|
|
@@ -45978,6 +46023,487 @@ var init_content_secrets = __esm({
|
|
|
45978
46023
|
}
|
|
45979
46024
|
});
|
|
45980
46025
|
|
|
46026
|
+
// src/core/checks/hook-coverage.ts
|
|
46027
|
+
import * as fs7 from "node:fs";
|
|
46028
|
+
import * as os2 from "node:os";
|
|
46029
|
+
import * as path11 from "node:path";
|
|
46030
|
+
function loadSettingsSources2(projectRoot, homeDir, includeUserGlobal) {
|
|
46031
|
+
const home2 = homeDir;
|
|
46032
|
+
const candidates = [
|
|
46033
|
+
{
|
|
46034
|
+
filePath: path11.join(projectRoot, ".claude", "settings.json"),
|
|
46035
|
+
displayPath: path11.join(".claude", "settings.json"),
|
|
46036
|
+
isUserGlobal: false
|
|
46037
|
+
},
|
|
46038
|
+
{
|
|
46039
|
+
filePath: path11.join(projectRoot, ".claude", "settings.local.json"),
|
|
46040
|
+
displayPath: path11.join(".claude", "settings.local.json"),
|
|
46041
|
+
isUserGlobal: false
|
|
46042
|
+
}
|
|
46043
|
+
];
|
|
46044
|
+
if (includeUserGlobal) {
|
|
46045
|
+
candidates.push({
|
|
46046
|
+
filePath: path11.join(home2, ".claude", "settings.json"),
|
|
46047
|
+
displayPath: "~/.claude/settings.json",
|
|
46048
|
+
isUserGlobal: true
|
|
46049
|
+
});
|
|
46050
|
+
}
|
|
46051
|
+
const sources = [];
|
|
46052
|
+
for (const c3 of candidates) {
|
|
46053
|
+
let content;
|
|
46054
|
+
try {
|
|
46055
|
+
content = stripBom(fs7.readFileSync(c3.filePath, "utf-8"));
|
|
46056
|
+
} catch {
|
|
46057
|
+
continue;
|
|
46058
|
+
}
|
|
46059
|
+
const tree = parseTree2(content, [], { allowTrailingComma: true });
|
|
46060
|
+
sources.push({ ...c3, content, tree });
|
|
46061
|
+
}
|
|
46062
|
+
return sources;
|
|
46063
|
+
}
|
|
46064
|
+
function expandPath(raw, projectRoot, homeDir) {
|
|
46065
|
+
let s = raw;
|
|
46066
|
+
if (s === "~" || s.startsWith("~/") || s.startsWith("~\\")) {
|
|
46067
|
+
s = path11.join(homeDir, s.slice(1));
|
|
46068
|
+
}
|
|
46069
|
+
const replacements = {
|
|
46070
|
+
CLAUDE_PROJECT_DIR: projectRoot,
|
|
46071
|
+
CLAUDE_CONFIG_DIR: path11.join(homeDir, ".claude"),
|
|
46072
|
+
HOME: homeDir,
|
|
46073
|
+
USERPROFILE: homeDir
|
|
46074
|
+
};
|
|
46075
|
+
s = s.replace(
|
|
46076
|
+
/\$\{?([A-Z_][A-Z0-9_]*)\}?/g,
|
|
46077
|
+
(m, name) => name in replacements ? replacements[name] : m
|
|
46078
|
+
);
|
|
46079
|
+
if (/\$\{?[A-Za-z_]/.test(s)) return null;
|
|
46080
|
+
if (!path11.isAbsolute(s)) s = path11.resolve(projectRoot, s);
|
|
46081
|
+
return s;
|
|
46082
|
+
}
|
|
46083
|
+
function looksLikePath(token) {
|
|
46084
|
+
const hasSep = token.includes("/") || token.includes("\\");
|
|
46085
|
+
if (PATH_PREFIX.test(token)) return true;
|
|
46086
|
+
return hasSep && SCRIPT_EXT.test(token);
|
|
46087
|
+
}
|
|
46088
|
+
function extractCommandPaths(command, projectRoot, homeDir) {
|
|
46089
|
+
const tokens = command.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
|
|
46090
|
+
const out = [];
|
|
46091
|
+
for (const t2 of tokens) {
|
|
46092
|
+
const token = t2.replace(/^['"]|['"]$/g, "");
|
|
46093
|
+
if (!looksLikePath(token)) continue;
|
|
46094
|
+
out.push({ raw: token, resolved: expandPath(token, projectRoot, homeDir) });
|
|
46095
|
+
}
|
|
46096
|
+
return out;
|
|
46097
|
+
}
|
|
46098
|
+
function extractPermissionPaths(entry, projectRoot, homeDir) {
|
|
46099
|
+
const inner = entry.replace(/^[A-Za-z]+\((.*)\)$/, "$1");
|
|
46100
|
+
return extractCommandPaths(inner, projectRoot, homeDir);
|
|
46101
|
+
}
|
|
46102
|
+
function lineForPath(source, jsonPath) {
|
|
46103
|
+
if (!source.tree) return 1;
|
|
46104
|
+
const node = findNodeAtLocation2(source.tree, jsonPath);
|
|
46105
|
+
if (!node) return 1;
|
|
46106
|
+
return offsetToPosition(source.content, node.offset).line;
|
|
46107
|
+
}
|
|
46108
|
+
function checkSource(source, projectRoot, homeDir) {
|
|
46109
|
+
if (!source.tree) return [];
|
|
46110
|
+
const data = getNodeValue2(source.tree);
|
|
46111
|
+
if (!data || typeof data !== "object") return [];
|
|
46112
|
+
const issues = [];
|
|
46113
|
+
const report = (cand, line, origin) => {
|
|
46114
|
+
if (cand.resolved === null) return;
|
|
46115
|
+
if (fileExists(cand.resolved)) return;
|
|
46116
|
+
const where = source.isUserGlobal ? ` (in ${source.displayPath})` : "";
|
|
46117
|
+
issues.push({
|
|
46118
|
+
severity: "warning",
|
|
46119
|
+
check: "hook-coverage",
|
|
46120
|
+
ruleId: "hook-coverage/dead-hook",
|
|
46121
|
+
line,
|
|
46122
|
+
message: `${origin} references "${cand.raw}" which does not exist on disk${where} \u2014 the gate silently no-ops`,
|
|
46123
|
+
suggestion: source.isUserGlobal ? `Restore the script at ${cand.resolved}, or remove the dead entry from ${source.displayPath}.` : `Restore the script, fix the path, or remove the dead entry. A PreToolUse hook pointing at a missing script does not block anything.`
|
|
46124
|
+
});
|
|
46125
|
+
};
|
|
46126
|
+
const hooks = data.hooks ?? {};
|
|
46127
|
+
for (const [event, matchers] of Object.entries(hooks)) {
|
|
46128
|
+
if (!Array.isArray(matchers)) continue;
|
|
46129
|
+
matchers.forEach((matcher, mi2) => {
|
|
46130
|
+
const subHooks = matcher?.hooks;
|
|
46131
|
+
if (!Array.isArray(subHooks)) return;
|
|
46132
|
+
subHooks.forEach((h2, hi2) => {
|
|
46133
|
+
if (!h2 || typeof h2.command !== "string") return;
|
|
46134
|
+
const line = lineForPath(source, ["hooks", event, mi2, "hooks", hi2, "command"]);
|
|
46135
|
+
for (const cand of extractCommandPaths(h2.command, projectRoot, homeDir)) {
|
|
46136
|
+
report(cand, line, `${event} hook`);
|
|
46137
|
+
}
|
|
46138
|
+
});
|
|
46139
|
+
});
|
|
46140
|
+
}
|
|
46141
|
+
const perms = data.permissions ?? {};
|
|
46142
|
+
for (const listName of ["allow", "deny", "ask"]) {
|
|
46143
|
+
const list = perms[listName];
|
|
46144
|
+
if (!Array.isArray(list)) continue;
|
|
46145
|
+
list.forEach((entry, i2) => {
|
|
46146
|
+
if (typeof entry !== "string") return;
|
|
46147
|
+
const line = lineForPath(source, ["permissions", listName, i2]);
|
|
46148
|
+
for (const cand of extractPermissionPaths(entry, projectRoot, homeDir)) {
|
|
46149
|
+
report(cand, line, `permissions.${listName} entry`);
|
|
46150
|
+
}
|
|
46151
|
+
});
|
|
46152
|
+
}
|
|
46153
|
+
return issues;
|
|
46154
|
+
}
|
|
46155
|
+
async function checkHookCoverage(projectRoot, homeDir = os2.homedir(), options = {}) {
|
|
46156
|
+
const sources = loadSettingsSources2(projectRoot, homeDir, options.userGlobal ?? false);
|
|
46157
|
+
const issues = [];
|
|
46158
|
+
for (const source of sources) {
|
|
46159
|
+
issues.push(...checkSource(source, projectRoot, homeDir));
|
|
46160
|
+
}
|
|
46161
|
+
return issues;
|
|
46162
|
+
}
|
|
46163
|
+
var SCRIPT_EXT, PATH_PREFIX;
|
|
46164
|
+
var init_hook_coverage = __esm({
|
|
46165
|
+
"src/core/checks/hook-coverage.ts"() {
|
|
46166
|
+
"use strict";
|
|
46167
|
+
init_define_WEB_FIRST_SEGMENTS();
|
|
46168
|
+
init_main();
|
|
46169
|
+
init_fs();
|
|
46170
|
+
init_positions();
|
|
46171
|
+
SCRIPT_EXT = /\.(sh|bash|zsh|fish|js|mjs|cjs|ts|py|rb|pl|ps1|cmd|bat|exe)$/i;
|
|
46172
|
+
PATH_PREFIX = /^(~|\.\.?[/\\]|[/\\]|\$\{?[A-Za-z_]|[A-Za-z]:[/\\])/;
|
|
46173
|
+
}
|
|
46174
|
+
});
|
|
46175
|
+
|
|
46176
|
+
// src/core/skill-scanner.ts
|
|
46177
|
+
import * as fs8 from "node:fs";
|
|
46178
|
+
import * as os3 from "node:os";
|
|
46179
|
+
import * as path12 from "node:path";
|
|
46180
|
+
function defaultHome() {
|
|
46181
|
+
return process.env.HOME || process.env.USERPROFILE || os3.homedir();
|
|
46182
|
+
}
|
|
46183
|
+
function scanSkillFiles(homeDir = defaultHome()) {
|
|
46184
|
+
const files = [];
|
|
46185
|
+
const orphanedSkillDirs = [];
|
|
46186
|
+
const claudeDir = path12.join(homeDir, ".claude");
|
|
46187
|
+
const skillsRoot = path12.join(claudeDir, "skills");
|
|
46188
|
+
for (const name of safeReadDir(skillsRoot, "dir")) {
|
|
46189
|
+
const skillMd = path12.join(skillsRoot, name, "SKILL.md");
|
|
46190
|
+
const content = safeRead(skillMd);
|
|
46191
|
+
if (content === null) {
|
|
46192
|
+
orphanedSkillDirs.push({ name, displayPath: `~/.claude/skills/${name}/` });
|
|
46193
|
+
continue;
|
|
46194
|
+
}
|
|
46195
|
+
files.push({
|
|
46196
|
+
filePath: skillMd,
|
|
46197
|
+
displayPath: `~/.claude/skills/${name}/SKILL.md`,
|
|
46198
|
+
kind: "skill",
|
|
46199
|
+
name,
|
|
46200
|
+
content
|
|
46201
|
+
});
|
|
46202
|
+
}
|
|
46203
|
+
const agentsRoot = path12.join(claudeDir, "agents");
|
|
46204
|
+
for (const fileName of safeReadDir(agentsRoot, "file")) {
|
|
46205
|
+
if (!fileName.endsWith(".md")) continue;
|
|
46206
|
+
const agentMd = path12.join(agentsRoot, fileName);
|
|
46207
|
+
const content = safeRead(agentMd);
|
|
46208
|
+
if (content === null) continue;
|
|
46209
|
+
files.push({
|
|
46210
|
+
filePath: agentMd,
|
|
46211
|
+
displayPath: `~/.claude/agents/${fileName}`,
|
|
46212
|
+
kind: "agent",
|
|
46213
|
+
name: fileName.replace(/\.md$/, ""),
|
|
46214
|
+
content
|
|
46215
|
+
});
|
|
46216
|
+
}
|
|
46217
|
+
return { files, orphanedSkillDirs };
|
|
46218
|
+
}
|
|
46219
|
+
function safeReadDir(dir, want) {
|
|
46220
|
+
let entries;
|
|
46221
|
+
try {
|
|
46222
|
+
entries = fs8.readdirSync(dir, { withFileTypes: true });
|
|
46223
|
+
} catch {
|
|
46224
|
+
return [];
|
|
46225
|
+
}
|
|
46226
|
+
return entries.filter((e) => want === "dir" ? e.isDirectory() : e.isFile()).map((e) => e.name).sort();
|
|
46227
|
+
}
|
|
46228
|
+
function safeRead(file2) {
|
|
46229
|
+
try {
|
|
46230
|
+
return stripBom(fs8.readFileSync(file2, "utf-8"));
|
|
46231
|
+
} catch {
|
|
46232
|
+
return null;
|
|
46233
|
+
}
|
|
46234
|
+
}
|
|
46235
|
+
var init_skill_scanner = __esm({
|
|
46236
|
+
"src/core/skill-scanner.ts"() {
|
|
46237
|
+
"use strict";
|
|
46238
|
+
init_define_WEB_FIRST_SEGMENTS();
|
|
46239
|
+
init_fs();
|
|
46240
|
+
}
|
|
46241
|
+
});
|
|
46242
|
+
|
|
46243
|
+
// src/core/checks/skills.ts
|
|
46244
|
+
import * as path13 from "node:path";
|
|
46245
|
+
function parseFrontmatter3(content) {
|
|
46246
|
+
const lines = content.split("\n");
|
|
46247
|
+
if (lines[0]?.trim() !== "---") {
|
|
46248
|
+
return { found: false, unclosed: false, fields: {}, endLine: 0 };
|
|
46249
|
+
}
|
|
46250
|
+
const fields = {};
|
|
46251
|
+
let arrayKey = "";
|
|
46252
|
+
let endLine = 0;
|
|
46253
|
+
for (let i2 = 1; i2 < lines.length; i2++) {
|
|
46254
|
+
const line = lines[i2].trim();
|
|
46255
|
+
if (line === "---") {
|
|
46256
|
+
endLine = i2 + 1;
|
|
46257
|
+
break;
|
|
46258
|
+
}
|
|
46259
|
+
if (line.startsWith("- ") && arrayKey) {
|
|
46260
|
+
const item = line.slice(2).trim().replace(/^["']|["']$/g, "");
|
|
46261
|
+
fields[arrayKey] = fields[arrayKey] ? `${fields[arrayKey]}, ${item}` : item;
|
|
46262
|
+
continue;
|
|
46263
|
+
}
|
|
46264
|
+
arrayKey = "";
|
|
46265
|
+
const m = line.match(/^([\w-]+)\s*:\s*(.*)$/);
|
|
46266
|
+
if (m) {
|
|
46267
|
+
let val = m[2].trim();
|
|
46268
|
+
const inline = val.match(/^\[(.*)\]$/);
|
|
46269
|
+
if (inline) {
|
|
46270
|
+
val = inline[1].split(",").map((s) => s.trim().replace(/^["']|["']$/g, "")).filter(Boolean).join(", ");
|
|
46271
|
+
}
|
|
46272
|
+
fields[m[1]] = val;
|
|
46273
|
+
if (!m[2].trim()) arrayKey = m[1];
|
|
46274
|
+
}
|
|
46275
|
+
}
|
|
46276
|
+
if (endLine === 0) return { found: true, unclosed: true, fields, endLine: lines.length };
|
|
46277
|
+
return { found: true, unclosed: false, fields, endLine };
|
|
46278
|
+
}
|
|
46279
|
+
function checkMissingFrontmatter(file2, fm) {
|
|
46280
|
+
if (fm.unclosed) {
|
|
46281
|
+
return [
|
|
46282
|
+
{
|
|
46283
|
+
severity: "error",
|
|
46284
|
+
check: "skill-frontmatter",
|
|
46285
|
+
ruleId: "skill/missing-frontmatter",
|
|
46286
|
+
line: 1,
|
|
46287
|
+
message: `${file2.displayPath}: frontmatter opens with \`---\` but is never closed`,
|
|
46288
|
+
suggestion: "Add a matching `---` line after the last frontmatter field."
|
|
46289
|
+
}
|
|
46290
|
+
];
|
|
46291
|
+
}
|
|
46292
|
+
if (!fm.found) {
|
|
46293
|
+
return [
|
|
46294
|
+
{
|
|
46295
|
+
severity: "error",
|
|
46296
|
+
check: "skill-frontmatter",
|
|
46297
|
+
ruleId: "skill/missing-frontmatter",
|
|
46298
|
+
line: 1,
|
|
46299
|
+
message: `${file2.displayPath}: missing YAML frontmatter`,
|
|
46300
|
+
suggestion: `Add frontmatter with ${REQUIRED_FIELDS[file2.kind].join(" + ")}.`
|
|
46301
|
+
}
|
|
46302
|
+
];
|
|
46303
|
+
}
|
|
46304
|
+
const issues = [];
|
|
46305
|
+
for (const field of REQUIRED_FIELDS[file2.kind]) {
|
|
46306
|
+
if (!fm.fields[field]) {
|
|
46307
|
+
issues.push({
|
|
46308
|
+
severity: "warning",
|
|
46309
|
+
check: "skill-frontmatter",
|
|
46310
|
+
ruleId: "skill/missing-frontmatter",
|
|
46311
|
+
line: 1,
|
|
46312
|
+
message: `${file2.displayPath}: missing required frontmatter field "${field}"`,
|
|
46313
|
+
suggestion: field === "description" ? "A skill/agent with no description is never selected \u2014 add one describing when to use it." : `Add a "${field}" field to the frontmatter.`
|
|
46314
|
+
});
|
|
46315
|
+
}
|
|
46316
|
+
}
|
|
46317
|
+
return issues;
|
|
46318
|
+
}
|
|
46319
|
+
function checkBrokenRefs(file2, fmEndLine) {
|
|
46320
|
+
const issues = [];
|
|
46321
|
+
const skillDir = path13.dirname(file2.filePath);
|
|
46322
|
+
const lines = file2.content.split("\n");
|
|
46323
|
+
let inCode = false;
|
|
46324
|
+
let codeLang = "";
|
|
46325
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
46326
|
+
if (i2 + 1 <= fmEndLine) continue;
|
|
46327
|
+
const raw = lines[i2];
|
|
46328
|
+
if (raw.trimStart().startsWith("```")) {
|
|
46329
|
+
inCode = !inCode;
|
|
46330
|
+
codeLang = inCode ? raw.trimStart().slice(3).trim().toLowerCase() : "";
|
|
46331
|
+
continue;
|
|
46332
|
+
}
|
|
46333
|
+
if (inCode && isExampleLang(codeLang)) continue;
|
|
46334
|
+
BODY_PATH.lastIndex = 0;
|
|
46335
|
+
let m;
|
|
46336
|
+
while ((m = BODY_PATH.exec(raw)) !== null) {
|
|
46337
|
+
let value = m[1];
|
|
46338
|
+
if (PATH_SKIP.test(value)) continue;
|
|
46339
|
+
if (!value.startsWith("./") && !value.startsWith("../")) continue;
|
|
46340
|
+
while (/[.,;:]$/.test(value)) value = value.slice(0, -1);
|
|
46341
|
+
const resolved = path13.resolve(skillDir, value);
|
|
46342
|
+
if (fileExists(resolved) || isDirectory(resolved)) continue;
|
|
46343
|
+
issues.push({
|
|
46344
|
+
severity: "warning",
|
|
46345
|
+
check: "skill-broken-ref",
|
|
46346
|
+
ruleId: "skill/broken-ref",
|
|
46347
|
+
line: i2 + 1,
|
|
46348
|
+
message: `${file2.displayPath}: references "${value}" which does not exist relative to the skill directory`,
|
|
46349
|
+
suggestion: "Fix the path, or use a path that exists relative to the skill/agent file."
|
|
46350
|
+
});
|
|
46351
|
+
}
|
|
46352
|
+
}
|
|
46353
|
+
return issues;
|
|
46354
|
+
}
|
|
46355
|
+
function isExampleLang(lang) {
|
|
46356
|
+
return [
|
|
46357
|
+
"js",
|
|
46358
|
+
"javascript",
|
|
46359
|
+
"ts",
|
|
46360
|
+
"typescript",
|
|
46361
|
+
"tsx",
|
|
46362
|
+
"jsx",
|
|
46363
|
+
"py",
|
|
46364
|
+
"python",
|
|
46365
|
+
"go",
|
|
46366
|
+
"rust",
|
|
46367
|
+
"java",
|
|
46368
|
+
"c",
|
|
46369
|
+
"cpp",
|
|
46370
|
+
"ruby",
|
|
46371
|
+
"php",
|
|
46372
|
+
"json",
|
|
46373
|
+
"yaml",
|
|
46374
|
+
"yml",
|
|
46375
|
+
"toml",
|
|
46376
|
+
// Shell fences hold command/example snippets, not in-repo refs. A `./foo.sh`
|
|
46377
|
+
// inside a bash block is an invocation example (often a cross-repo script or
|
|
46378
|
+
// a placeholder), not a path that should resolve relative to the skill dir.
|
|
46379
|
+
// Matches the rule's "prefer a false negative over a false positive" posture.
|
|
46380
|
+
"bash",
|
|
46381
|
+
"sh",
|
|
46382
|
+
"shell",
|
|
46383
|
+
"shellscript",
|
|
46384
|
+
"zsh",
|
|
46385
|
+
"console"
|
|
46386
|
+
].includes(lang);
|
|
46387
|
+
}
|
|
46388
|
+
function extractTriggers(fm) {
|
|
46389
|
+
const phrases = /* @__PURE__ */ new Set();
|
|
46390
|
+
const desc = fm.fields["description"] ?? "";
|
|
46391
|
+
for (const q3 of desc.matchAll(/["']([^"']{3,})["']/g)) {
|
|
46392
|
+
phrases.add(normalizeTrigger(q3[1]));
|
|
46393
|
+
}
|
|
46394
|
+
for (const key of ["trigger", "triggers"]) {
|
|
46395
|
+
const val = fm.fields[key];
|
|
46396
|
+
if (val) for (const part of val.split(",")) phrases.add(normalizeTrigger(part));
|
|
46397
|
+
}
|
|
46398
|
+
phrases.delete("");
|
|
46399
|
+
return [...phrases];
|
|
46400
|
+
}
|
|
46401
|
+
function normalizeTrigger(s) {
|
|
46402
|
+
return s.toLowerCase().trim().replace(/\s+/g, " ");
|
|
46403
|
+
}
|
|
46404
|
+
function checkTriggerCollisions(parsed) {
|
|
46405
|
+
const byTrigger = /* @__PURE__ */ new Map();
|
|
46406
|
+
for (const { file: file2, fm } of parsed) {
|
|
46407
|
+
for (const t2 of extractTriggers(fm)) {
|
|
46408
|
+
const list = byTrigger.get(t2) ?? [];
|
|
46409
|
+
list.push(file2);
|
|
46410
|
+
byTrigger.set(t2, list);
|
|
46411
|
+
}
|
|
46412
|
+
}
|
|
46413
|
+
const issues = [];
|
|
46414
|
+
for (const [trigger, owners] of byTrigger) {
|
|
46415
|
+
const distinct = [...new Set(owners.map((o2) => o2.displayPath))];
|
|
46416
|
+
if (distinct.length > 1) {
|
|
46417
|
+
issues.push({
|
|
46418
|
+
severity: "warning",
|
|
46419
|
+
check: "skill-trigger-collision",
|
|
46420
|
+
ruleId: "skill/trigger-collision",
|
|
46421
|
+
line: 1,
|
|
46422
|
+
message: `Trigger phrase "${trigger}" is declared by ${distinct.length} skills/agents \u2014 only one will win`,
|
|
46423
|
+
detail: distinct.map((d) => ` - ${d}`).join("\n"),
|
|
46424
|
+
suggestion: "Make trigger phrases unique so invocation is deterministic."
|
|
46425
|
+
});
|
|
46426
|
+
}
|
|
46427
|
+
}
|
|
46428
|
+
return issues;
|
|
46429
|
+
}
|
|
46430
|
+
function checkOrphaned(ctx) {
|
|
46431
|
+
return ctx.orphanedSkillDirs.map((o2) => ({
|
|
46432
|
+
severity: "warning",
|
|
46433
|
+
check: "skill-orphaned",
|
|
46434
|
+
ruleId: "skill/orphaned",
|
|
46435
|
+
line: 1,
|
|
46436
|
+
message: `${o2.displayPath}: skill directory has no SKILL.md \u2014 Claude Code has nothing to load`,
|
|
46437
|
+
suggestion: `Add a SKILL.md, or remove the empty ${o2.displayPath} directory.`
|
|
46438
|
+
}));
|
|
46439
|
+
}
|
|
46440
|
+
function checkDeadToolRestrictions(file2, fm) {
|
|
46441
|
+
if (file2.kind !== "agent") return [];
|
|
46442
|
+
const raw = fm.fields["tools"] ?? fm.fields["allowed-tools"] ?? fm.fields["allowedTools"];
|
|
46443
|
+
if (!raw) return [];
|
|
46444
|
+
const issues = [];
|
|
46445
|
+
for (const entry of raw.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
46446
|
+
if (entry.startsWith("mcp__") || entry.includes("*")) continue;
|
|
46447
|
+
const base = entry.replace(/\(.*\)$/, "").trim();
|
|
46448
|
+
if (KNOWN_TOOLS.has(base)) continue;
|
|
46449
|
+
issues.push({
|
|
46450
|
+
severity: "warning",
|
|
46451
|
+
check: "skill-dead-tool-restriction",
|
|
46452
|
+
ruleId: "skill/dead-tool-restriction",
|
|
46453
|
+
line: 1,
|
|
46454
|
+
message: `${file2.displayPath}: tool restriction lists "${entry}" which is not a known Claude Code tool`,
|
|
46455
|
+
suggestion: `Known built-in tools: ${[...KNOWN_TOOLS].join(", ")}. MCP tools use the mcp__server__tool form.`
|
|
46456
|
+
});
|
|
46457
|
+
}
|
|
46458
|
+
return issues;
|
|
46459
|
+
}
|
|
46460
|
+
function checkSkills(ctx, sel) {
|
|
46461
|
+
const issues = [];
|
|
46462
|
+
const parsed = ctx.files.map((file2) => ({ file: file2, fm: parseFrontmatter3(file2.content) }));
|
|
46463
|
+
for (const { file: file2, fm } of parsed) {
|
|
46464
|
+
if (sel.frontmatter) issues.push(...checkMissingFrontmatter(file2, fm));
|
|
46465
|
+
if (sel.brokenRef) issues.push(...checkBrokenRefs(file2, fm.found ? fm.endLine : 0));
|
|
46466
|
+
if (sel.deadToolRestriction) issues.push(...checkDeadToolRestrictions(file2, fm));
|
|
46467
|
+
}
|
|
46468
|
+
if (sel.triggerCollision) issues.push(...checkTriggerCollisions(parsed));
|
|
46469
|
+
if (sel.orphaned) issues.push(...checkOrphaned(ctx));
|
|
46470
|
+
return issues;
|
|
46471
|
+
}
|
|
46472
|
+
var REQUIRED_FIELDS, BODY_PATH, PATH_SKIP, KNOWN_TOOLS;
|
|
46473
|
+
var init_skills = __esm({
|
|
46474
|
+
"src/core/checks/skills.ts"() {
|
|
46475
|
+
"use strict";
|
|
46476
|
+
init_define_WEB_FIRST_SEGMENTS();
|
|
46477
|
+
init_fs();
|
|
46478
|
+
REQUIRED_FIELDS = {
|
|
46479
|
+
skill: ["name", "description"],
|
|
46480
|
+
agent: ["name", "description"]
|
|
46481
|
+
};
|
|
46482
|
+
BODY_PATH = /(?:^|[\s`"'(])((?:\.{1,2}\/)[\w@./-]+|(?:[\w@-]+\/)+[\w.-]+)(?=[\s`"'),;:]|$)/gm;
|
|
46483
|
+
PATH_SKIP = /^(https?:\/\/|mailto:|n\/a|e\.g\.|i\.e\.)/i;
|
|
46484
|
+
KNOWN_TOOLS = /* @__PURE__ */ new Set([
|
|
46485
|
+
"Agent",
|
|
46486
|
+
"Bash",
|
|
46487
|
+
"BashOutput",
|
|
46488
|
+
"Edit",
|
|
46489
|
+
"ExitPlanMode",
|
|
46490
|
+
"Glob",
|
|
46491
|
+
"Grep",
|
|
46492
|
+
"KillShell",
|
|
46493
|
+
"MultiEdit",
|
|
46494
|
+
"NotebookEdit",
|
|
46495
|
+
"Read",
|
|
46496
|
+
"Skill",
|
|
46497
|
+
"SlashCommand",
|
|
46498
|
+
"Task",
|
|
46499
|
+
"TodoWrite",
|
|
46500
|
+
"WebFetch",
|
|
46501
|
+
"WebSearch",
|
|
46502
|
+
"Write"
|
|
46503
|
+
]);
|
|
46504
|
+
}
|
|
46505
|
+
});
|
|
46506
|
+
|
|
45981
46507
|
// src/core/ignore-rules.ts
|
|
45982
46508
|
function compileRules(rules) {
|
|
45983
46509
|
return rules.map((r2) => ({
|
|
@@ -46044,25 +46570,26 @@ var init_ignore_rules = __esm({
|
|
|
46044
46570
|
});
|
|
46045
46571
|
|
|
46046
46572
|
// src/core/types.ts
|
|
46047
|
-
var SESSION_AUDIT_LABEL, SESSION_AUDIT_PATH_MARKER;
|
|
46573
|
+
var SESSION_AUDIT_LABEL, SESSION_AUDIT_PATH_MARKER, SKILL_AUDIT_LABEL;
|
|
46048
46574
|
var init_types3 = __esm({
|
|
46049
46575
|
"src/core/types.ts"() {
|
|
46050
46576
|
"use strict";
|
|
46051
46577
|
init_define_WEB_FIRST_SEGMENTS();
|
|
46052
46578
|
SESSION_AUDIT_LABEL = "~/.claude/ (session audit)";
|
|
46053
46579
|
SESSION_AUDIT_PATH_MARKER = "(session audit)";
|
|
46580
|
+
SKILL_AUDIT_LABEL = "~/.claude/ (skill audit)";
|
|
46054
46581
|
}
|
|
46055
46582
|
});
|
|
46056
46583
|
|
|
46057
46584
|
// src/version.ts
|
|
46058
|
-
import { readFileSync as
|
|
46059
|
-
import { resolve as
|
|
46585
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
46586
|
+
import { resolve as resolve11, dirname as dirname5 } from "node:path";
|
|
46060
46587
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
46061
46588
|
function loadVersion() {
|
|
46062
|
-
if (true) return "0.
|
|
46063
|
-
const __dir =
|
|
46064
|
-
const pkgPath =
|
|
46065
|
-
const pkg = JSON.parse(
|
|
46589
|
+
if (true) return "0.13.0";
|
|
46590
|
+
const __dir = dirname5(fileURLToPath2(import.meta.url));
|
|
46591
|
+
const pkgPath = resolve11(__dir, "../package.json");
|
|
46592
|
+
const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
|
|
46066
46593
|
return pkg.version;
|
|
46067
46594
|
}
|
|
46068
46595
|
var VERSION;
|
|
@@ -46084,6 +46611,9 @@ function hasMcphChecks(checks) {
|
|
|
46084
46611
|
function hasSessionChecks(checks) {
|
|
46085
46612
|
return checks.some((c3) => c3.startsWith("session-"));
|
|
46086
46613
|
}
|
|
46614
|
+
function hasSkillChecks(checks) {
|
|
46615
|
+
return checks.some((c3) => c3.startsWith("skill-"));
|
|
46616
|
+
}
|
|
46087
46617
|
function deriveChecksToRun(activeChecks, prefix, enabled, allChecks) {
|
|
46088
46618
|
const filtered = activeChecks.filter((c3) => c3.startsWith(prefix));
|
|
46089
46619
|
if (filtered.length > 0) return filtered;
|
|
@@ -46092,10 +46622,11 @@ function deriveChecksToRun(activeChecks, prefix, enabled, allChecks) {
|
|
|
46092
46622
|
async function runAudit(projectRoot, activeChecks, options = {}) {
|
|
46093
46623
|
const fileResults = [];
|
|
46094
46624
|
const thresholds = resolveTokenThresholds(options.tokenThresholds);
|
|
46095
|
-
const shouldRunContextChecks = !options.mcpOnly && !options.mcphOnly && !options.sessionOnly;
|
|
46625
|
+
const shouldRunContextChecks = !options.mcpOnly && !options.mcphOnly && !options.sessionOnly && !options.skillsOnly;
|
|
46096
46626
|
const shouldRunMcpChecks = options.mcp || options.mcpGlobal || options.mcpOnly || hasMcpChecks(activeChecks);
|
|
46097
46627
|
const shouldRunMcphChecks = options.mcph || options.mcphGlobal || options.mcphOnly || hasMcphChecks(activeChecks);
|
|
46098
46628
|
const shouldRunSessionChecks = options.session || options.sessionOnly || hasSessionChecks(activeChecks);
|
|
46629
|
+
const shouldRunSkillChecks = options.skills || options.skillsOnly || hasSkillChecks(activeChecks);
|
|
46099
46630
|
if (shouldRunContextChecks) {
|
|
46100
46631
|
const discovered = await scanForContextFiles(projectRoot, {
|
|
46101
46632
|
depth: options.depth,
|
|
@@ -46152,6 +46683,13 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
|
|
|
46152
46683
|
if (activeChecks.includes("ci-secrets")) {
|
|
46153
46684
|
crossFileIssues.push(...await checkCiSecrets(parsed, projectRoot));
|
|
46154
46685
|
}
|
|
46686
|
+
if (activeChecks.includes("hook-coverage")) {
|
|
46687
|
+
crossFileIssues.push(
|
|
46688
|
+
...await checkHookCoverage(projectRoot, void 0, {
|
|
46689
|
+
userGlobal: options.hooksGlobal
|
|
46690
|
+
})
|
|
46691
|
+
);
|
|
46692
|
+
}
|
|
46155
46693
|
if (crossFileIssues.length > 0) {
|
|
46156
46694
|
fileResults.push({
|
|
46157
46695
|
path: "(project)",
|
|
@@ -46322,6 +46860,31 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
|
|
|
46322
46860
|
});
|
|
46323
46861
|
}
|
|
46324
46862
|
}
|
|
46863
|
+
if (shouldRunSkillChecks) {
|
|
46864
|
+
const skillChecksToRun = deriveChecksToRun(
|
|
46865
|
+
activeChecks,
|
|
46866
|
+
"skill-",
|
|
46867
|
+
Boolean(options.skills || options.skillsOnly),
|
|
46868
|
+
ALL_SKILL_CHECKS
|
|
46869
|
+
);
|
|
46870
|
+
if (skillChecksToRun.length > 0) {
|
|
46871
|
+
const skillCtx = scanSkillFiles();
|
|
46872
|
+
const skillIssues = checkSkills(skillCtx, {
|
|
46873
|
+
frontmatter: skillChecksToRun.includes("skill-frontmatter"),
|
|
46874
|
+
brokenRef: skillChecksToRun.includes("skill-broken-ref"),
|
|
46875
|
+
triggerCollision: skillChecksToRun.includes("skill-trigger-collision"),
|
|
46876
|
+
orphaned: skillChecksToRun.includes("skill-orphaned"),
|
|
46877
|
+
deadToolRestriction: skillChecksToRun.includes("skill-dead-tool-restriction")
|
|
46878
|
+
});
|
|
46879
|
+
fileResults.push({
|
|
46880
|
+
path: SKILL_AUDIT_LABEL,
|
|
46881
|
+
isSymlink: false,
|
|
46882
|
+
tokens: 0,
|
|
46883
|
+
lines: 0,
|
|
46884
|
+
issues: skillIssues
|
|
46885
|
+
});
|
|
46886
|
+
}
|
|
46887
|
+
}
|
|
46325
46888
|
let ignoreReport;
|
|
46326
46889
|
if (options.ignoreRules && options.ignoreRules.length > 0) {
|
|
46327
46890
|
const flat = [];
|
|
@@ -46383,7 +46946,7 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
|
|
|
46383
46946
|
}
|
|
46384
46947
|
return result;
|
|
46385
46948
|
}
|
|
46386
|
-
var ALL_CHECKS, ALL_MCP_CHECKS, ALL_MCPH_CHECKS, ALL_SESSION_CHECKS;
|
|
46949
|
+
var ALL_CHECKS, ALL_MCP_CHECKS, ALL_MCPH_CHECKS, ALL_SESSION_CHECKS, ALL_SKILL_CHECKS;
|
|
46387
46950
|
var init_audit = __esm({
|
|
46388
46951
|
"src/core/audit.ts"() {
|
|
46389
46952
|
"use strict";
|
|
@@ -46425,6 +46988,9 @@ var init_audit = __esm({
|
|
|
46425
46988
|
init_ci_coverage();
|
|
46426
46989
|
init_ci_secrets();
|
|
46427
46990
|
init_content_secrets();
|
|
46991
|
+
init_hook_coverage();
|
|
46992
|
+
init_skill_scanner();
|
|
46993
|
+
init_skills();
|
|
46428
46994
|
init_ignore_rules();
|
|
46429
46995
|
init_types3();
|
|
46430
46996
|
init_version2();
|
|
@@ -46439,7 +47005,8 @@ var init_audit = __esm({
|
|
|
46439
47005
|
"frontmatter",
|
|
46440
47006
|
"ci-coverage",
|
|
46441
47007
|
"ci-secrets",
|
|
46442
|
-
"content-secrets"
|
|
47008
|
+
"content-secrets",
|
|
47009
|
+
"hook-coverage"
|
|
46443
47010
|
];
|
|
46444
47011
|
ALL_MCP_CHECKS = [
|
|
46445
47012
|
"mcp-schema",
|
|
@@ -46467,6 +47034,13 @@ var init_audit = __esm({
|
|
|
46467
47034
|
"session-loop-detection",
|
|
46468
47035
|
"session-memory-index-overflow"
|
|
46469
47036
|
];
|
|
47037
|
+
ALL_SKILL_CHECKS = [
|
|
47038
|
+
"skill-frontmatter",
|
|
47039
|
+
"skill-broken-ref",
|
|
47040
|
+
"skill-trigger-collision",
|
|
47041
|
+
"skill-orphaned",
|
|
47042
|
+
"skill-dead-tool-restriction"
|
|
47043
|
+
];
|
|
46470
47044
|
}
|
|
46471
47045
|
});
|
|
46472
47046
|
|
|
@@ -46664,7 +47238,7 @@ var init_ansi_styles = __esm({
|
|
|
46664
47238
|
|
|
46665
47239
|
// node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js
|
|
46666
47240
|
import process4 from "node:process";
|
|
46667
|
-
import
|
|
47241
|
+
import os4 from "node:os";
|
|
46668
47242
|
import tty from "node:tty";
|
|
46669
47243
|
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process4.argv) {
|
|
46670
47244
|
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
@@ -46722,7 +47296,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
|
46722
47296
|
return min;
|
|
46723
47297
|
}
|
|
46724
47298
|
if (process4.platform === "win32") {
|
|
46725
|
-
const osRelease =
|
|
47299
|
+
const osRelease = os4.release().split(".");
|
|
46726
47300
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
46727
47301
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
46728
47302
|
}
|
|
@@ -47013,7 +47587,7 @@ var init_source = __esm({
|
|
|
47013
47587
|
});
|
|
47014
47588
|
|
|
47015
47589
|
// src/core/fixer.ts
|
|
47016
|
-
import * as
|
|
47590
|
+
import * as fs9 from "node:fs";
|
|
47017
47591
|
function applyFixes(result, options = {}) {
|
|
47018
47592
|
const log = options.quiet ? () => {
|
|
47019
47593
|
} : console.log.bind(console);
|
|
@@ -47055,7 +47629,7 @@ function applyFixes(result, options = {}) {
|
|
|
47055
47629
|
let totalFixes = 0;
|
|
47056
47630
|
const filesModified = [];
|
|
47057
47631
|
for (const [filePath, fixes] of fixesByFile) {
|
|
47058
|
-
const content =
|
|
47632
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
47059
47633
|
const lines = content.split("\n");
|
|
47060
47634
|
let modified = false;
|
|
47061
47635
|
const fixesByLine = /* @__PURE__ */ new Map();
|
|
@@ -47097,7 +47671,7 @@ function applyFixes(result, options = {}) {
|
|
|
47097
47671
|
}
|
|
47098
47672
|
}
|
|
47099
47673
|
if (!dryRun) {
|
|
47100
|
-
|
|
47674
|
+
fs9.writeFileSync(filePath, newContent, "utf-8");
|
|
47101
47675
|
}
|
|
47102
47676
|
filesModified.push(filePath);
|
|
47103
47677
|
totalFixes += perFileFixCount;
|
|
@@ -47115,8 +47689,8 @@ var init_fixer = __esm({
|
|
|
47115
47689
|
});
|
|
47116
47690
|
|
|
47117
47691
|
// src/core/config.ts
|
|
47118
|
-
import * as
|
|
47119
|
-
import * as
|
|
47692
|
+
import * as fs10 from "node:fs";
|
|
47693
|
+
import * as path14 from "node:path";
|
|
47120
47694
|
function formatJsonError(content, err) {
|
|
47121
47695
|
const msg = err instanceof Error ? err.message : String(err);
|
|
47122
47696
|
const posMatch = msg.match(/position (\d+)/);
|
|
@@ -47214,10 +47788,10 @@ function warnIgnoreRulePathPatternMisuse(config2, source) {
|
|
|
47214
47788
|
}
|
|
47215
47789
|
function loadConfig(projectRoot) {
|
|
47216
47790
|
for (const filename of CONFIG_FILENAMES) {
|
|
47217
|
-
const filePath =
|
|
47791
|
+
const filePath = path14.join(projectRoot, filename);
|
|
47218
47792
|
let content;
|
|
47219
47793
|
try {
|
|
47220
|
-
content = stripBom(
|
|
47794
|
+
content = stripBom(fs10.readFileSync(filePath, "utf-8"));
|
|
47221
47795
|
} catch {
|
|
47222
47796
|
continue;
|
|
47223
47797
|
}
|
|
@@ -47228,7 +47802,7 @@ function loadConfig(projectRoot) {
|
|
|
47228
47802
|
function loadConfigFromExplicitPath(configPath) {
|
|
47229
47803
|
let content;
|
|
47230
47804
|
try {
|
|
47231
|
-
content = stripBom(
|
|
47805
|
+
content = stripBom(fs10.readFileSync(configPath, "utf-8"));
|
|
47232
47806
|
} catch (err) {
|
|
47233
47807
|
const detail = err instanceof Error ? err.message : String(err);
|
|
47234
47808
|
throw new Error(`could not load config from ${configPath}: ${detail}`, { cause: err });
|
|
@@ -47259,7 +47833,10 @@ var init_config2 = __esm({
|
|
|
47259
47833
|
"mcphGlobal",
|
|
47260
47834
|
"mcphStrictEnvToken",
|
|
47261
47835
|
"session",
|
|
47262
|
-
"sessionOnly"
|
|
47836
|
+
"sessionOnly",
|
|
47837
|
+
"skills",
|
|
47838
|
+
"skillsOnly",
|
|
47839
|
+
"hooksGlobal"
|
|
47263
47840
|
];
|
|
47264
47841
|
CONFIG_FILENAMES = [".ctxlintrc", ".ctxlintrc.json"];
|
|
47265
47842
|
}
|
|
@@ -47271,7 +47848,7 @@ __export(server_exports, {
|
|
|
47271
47848
|
server: () => server,
|
|
47272
47849
|
startServer: () => startServer
|
|
47273
47850
|
});
|
|
47274
|
-
import * as
|
|
47851
|
+
import * as path15 from "node:path";
|
|
47275
47852
|
function describeDisallowed(rawPath) {
|
|
47276
47853
|
const m = rawPath.match(PATH_DISALLOWED);
|
|
47277
47854
|
if (!m) return "unknown";
|
|
@@ -47284,7 +47861,7 @@ function validateProjectPath(rawPath) {
|
|
|
47284
47861
|
`projectPath contains disallowed character ${describeDisallowed(rawPath)} (control chars and shell metacharacters are rejected)`
|
|
47285
47862
|
);
|
|
47286
47863
|
}
|
|
47287
|
-
const resolved =
|
|
47864
|
+
const resolved = path15.resolve(rawPath);
|
|
47288
47865
|
if (!isDirectory(resolved)) {
|
|
47289
47866
|
throw new Error("projectPath is not an existing directory");
|
|
47290
47867
|
}
|
|
@@ -47305,10 +47882,10 @@ function validateFilePathInput(rawPath) {
|
|
|
47305
47882
|
}
|
|
47306
47883
|
}
|
|
47307
47884
|
function resolveWithinRoot(filePath, root) {
|
|
47308
|
-
const resolvedRoot =
|
|
47309
|
-
const resolved =
|
|
47310
|
-
const rel =
|
|
47311
|
-
if (rel.startsWith("..") ||
|
|
47885
|
+
const resolvedRoot = path15.resolve(root);
|
|
47886
|
+
const resolved = path15.resolve(resolvedRoot, filePath);
|
|
47887
|
+
const rel = path15.relative(resolvedRoot, resolved);
|
|
47888
|
+
if (rel.startsWith("..") || path15.isAbsolute(rel)) {
|
|
47312
47889
|
throw new Error("path escapes the project root");
|
|
47313
47890
|
}
|
|
47314
47891
|
return resolved;
|
|
@@ -48823,8 +49400,8 @@ var require_command = __commonJS({
|
|
|
48823
49400
|
init_define_WEB_FIRST_SEGMENTS();
|
|
48824
49401
|
var EventEmitter2 = __require("node:events").EventEmitter;
|
|
48825
49402
|
var childProcess = __require("node:child_process");
|
|
48826
|
-
var
|
|
48827
|
-
var
|
|
49403
|
+
var path17 = __require("node:path");
|
|
49404
|
+
var fs12 = __require("node:fs");
|
|
48828
49405
|
var process11 = __require("node:process");
|
|
48829
49406
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
48830
49407
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -49818,7 +50395,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
49818
50395
|
* @param {string} subcommandName
|
|
49819
50396
|
*/
|
|
49820
50397
|
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
49821
|
-
if (
|
|
50398
|
+
if (fs12.existsSync(executableFile)) return;
|
|
49822
50399
|
const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
|
|
49823
50400
|
const executableMissing = `'${executableFile}' does not exist
|
|
49824
50401
|
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
@@ -49836,11 +50413,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
49836
50413
|
let launchWithNode = false;
|
|
49837
50414
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
49838
50415
|
function findFile(baseDir, baseName) {
|
|
49839
|
-
const localBin =
|
|
49840
|
-
if (
|
|
49841
|
-
if (sourceExt.includes(
|
|
50416
|
+
const localBin = path17.resolve(baseDir, baseName);
|
|
50417
|
+
if (fs12.existsSync(localBin)) return localBin;
|
|
50418
|
+
if (sourceExt.includes(path17.extname(baseName))) return void 0;
|
|
49842
50419
|
const foundExt = sourceExt.find(
|
|
49843
|
-
(ext) =>
|
|
50420
|
+
(ext) => fs12.existsSync(`${localBin}${ext}`)
|
|
49844
50421
|
);
|
|
49845
50422
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
49846
50423
|
return void 0;
|
|
@@ -49852,21 +50429,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
49852
50429
|
if (this._scriptPath) {
|
|
49853
50430
|
let resolvedScriptPath;
|
|
49854
50431
|
try {
|
|
49855
|
-
resolvedScriptPath =
|
|
50432
|
+
resolvedScriptPath = fs12.realpathSync(this._scriptPath);
|
|
49856
50433
|
} catch {
|
|
49857
50434
|
resolvedScriptPath = this._scriptPath;
|
|
49858
50435
|
}
|
|
49859
|
-
executableDir =
|
|
49860
|
-
|
|
50436
|
+
executableDir = path17.resolve(
|
|
50437
|
+
path17.dirname(resolvedScriptPath),
|
|
49861
50438
|
executableDir
|
|
49862
50439
|
);
|
|
49863
50440
|
}
|
|
49864
50441
|
if (executableDir) {
|
|
49865
50442
|
let localFile = findFile(executableDir, executableFile);
|
|
49866
50443
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
49867
|
-
const legacyName =
|
|
50444
|
+
const legacyName = path17.basename(
|
|
49868
50445
|
this._scriptPath,
|
|
49869
|
-
|
|
50446
|
+
path17.extname(this._scriptPath)
|
|
49870
50447
|
);
|
|
49871
50448
|
if (legacyName !== this._name) {
|
|
49872
50449
|
localFile = findFile(
|
|
@@ -49877,7 +50454,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
49877
50454
|
}
|
|
49878
50455
|
executableFile = localFile || executableFile;
|
|
49879
50456
|
}
|
|
49880
|
-
launchWithNode = sourceExt.includes(
|
|
50457
|
+
launchWithNode = sourceExt.includes(path17.extname(executableFile));
|
|
49881
50458
|
let proc;
|
|
49882
50459
|
if (process11.platform !== "win32") {
|
|
49883
50460
|
if (launchWithNode) {
|
|
@@ -50792,7 +51369,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
50792
51369
|
* @return {Command}
|
|
50793
51370
|
*/
|
|
50794
51371
|
nameFromFilename(filename) {
|
|
50795
|
-
this._name =
|
|
51372
|
+
this._name = path17.basename(filename, path17.extname(filename));
|
|
50796
51373
|
return this;
|
|
50797
51374
|
}
|
|
50798
51375
|
/**
|
|
@@ -50806,9 +51383,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
50806
51383
|
* @param {string} [path]
|
|
50807
51384
|
* @return {(string|null|Command)}
|
|
50808
51385
|
*/
|
|
50809
|
-
executableDir(
|
|
50810
|
-
if (
|
|
50811
|
-
this._executableDir =
|
|
51386
|
+
executableDir(path18) {
|
|
51387
|
+
if (path18 === void 0) return this._executableDir;
|
|
51388
|
+
this._executableDir = path18;
|
|
50812
51389
|
return this;
|
|
50813
51390
|
}
|
|
50814
51391
|
/**
|
|
@@ -54161,8 +54738,10 @@ function classifyFile(f) {
|
|
|
54161
54738
|
if (f.path === "(project)") return "context";
|
|
54162
54739
|
if (f.path === "(mcp)") return "mcp";
|
|
54163
54740
|
if (f.path.includes(SESSION_AUDIT_PATH_MARKER)) return "session";
|
|
54741
|
+
if (f.path.includes(SKILL_AUDIT_PATH_MARKER)) return "skill";
|
|
54164
54742
|
for (const issue2 of f.issues) {
|
|
54165
54743
|
if (issue2.check.startsWith("session-")) return "session";
|
|
54744
|
+
if (issue2.check.startsWith("skill-")) return "skill";
|
|
54166
54745
|
if (issue2.check.startsWith("mcph-")) return "mcph";
|
|
54167
54746
|
if (issue2.check.startsWith("mcp-")) return "mcp";
|
|
54168
54747
|
}
|
|
@@ -54174,7 +54753,7 @@ function classifyFile(f) {
|
|
|
54174
54753
|
return "context";
|
|
54175
54754
|
}
|
|
54176
54755
|
function isSyntheticBucket(p2) {
|
|
54177
|
-
return p2.startsWith("(") || p2.includes(SESSION_AUDIT_PATH_MARKER);
|
|
54756
|
+
return p2.startsWith("(") || p2.includes(SESSION_AUDIT_PATH_MARKER) || p2.includes(SKILL_AUDIT_PATH_MARKER);
|
|
54178
54757
|
}
|
|
54179
54758
|
function formatText(result, verbose = false) {
|
|
54180
54759
|
const lines = [];
|
|
@@ -54187,7 +54766,8 @@ function formatText(result, verbose = false) {
|
|
|
54187
54766
|
context: [],
|
|
54188
54767
|
mcp: [],
|
|
54189
54768
|
mcph: [],
|
|
54190
|
-
session: []
|
|
54769
|
+
session: [],
|
|
54770
|
+
skill: []
|
|
54191
54771
|
};
|
|
54192
54772
|
for (const f of result.files) {
|
|
54193
54773
|
groups[classifyFile(f)].push(f);
|
|
@@ -54464,6 +55044,11 @@ function buildRuleDescriptors() {
|
|
|
54464
55044
|
shortDescription: { text: "Inline-pasted secret detected in a context file" },
|
|
54465
55045
|
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
54466
55046
|
},
|
|
55047
|
+
{
|
|
55048
|
+
id: "ctxlint/hook-coverage",
|
|
55049
|
+
shortDescription: { text: "Hook/permission entry references a script that no longer exists" },
|
|
55050
|
+
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
55051
|
+
},
|
|
54467
55052
|
{
|
|
54468
55053
|
id: "ctxlint/mcp-schema",
|
|
54469
55054
|
shortDescription: { text: "MCP config structural validation error" },
|
|
@@ -54565,6 +55150,31 @@ function buildRuleDescriptors() {
|
|
|
54565
55150
|
text: "MEMORY.md exceeds Claude Code's 200-line / 25KB session-load cap"
|
|
54566
55151
|
},
|
|
54567
55152
|
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
55153
|
+
},
|
|
55154
|
+
{
|
|
55155
|
+
id: "ctxlint/skill-frontmatter",
|
|
55156
|
+
shortDescription: { text: "Skill/agent definition missing required frontmatter" },
|
|
55157
|
+
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
55158
|
+
},
|
|
55159
|
+
{
|
|
55160
|
+
id: "ctxlint/skill-broken-ref",
|
|
55161
|
+
shortDescription: { text: "Skill/agent body references a path that does not exist" },
|
|
55162
|
+
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
55163
|
+
},
|
|
55164
|
+
{
|
|
55165
|
+
id: "ctxlint/skill-trigger-collision",
|
|
55166
|
+
shortDescription: { text: "Two skills/agents declare the same trigger phrase" },
|
|
55167
|
+
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
55168
|
+
},
|
|
55169
|
+
{
|
|
55170
|
+
id: "ctxlint/skill-orphaned",
|
|
55171
|
+
shortDescription: { text: "Skill directory has no SKILL.md" },
|
|
55172
|
+
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
55173
|
+
},
|
|
55174
|
+
{
|
|
55175
|
+
id: "ctxlint/skill-dead-tool-restriction",
|
|
55176
|
+
shortDescription: { text: "Agent tool restriction names an unknown Claude Code tool" },
|
|
55177
|
+
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
54568
55178
|
}
|
|
54569
55179
|
];
|
|
54570
55180
|
}
|
|
@@ -54582,25 +55192,28 @@ function formatIssue(issue2) {
|
|
|
54582
55192
|
}
|
|
54583
55193
|
return line;
|
|
54584
55194
|
}
|
|
54585
|
-
var GROUP_ORDER, GROUP_LABELS, GROUP_SUMMARY_NOUNS;
|
|
55195
|
+
var SKILL_AUDIT_PATH_MARKER, GROUP_ORDER, GROUP_LABELS, GROUP_SUMMARY_NOUNS;
|
|
54586
55196
|
var init_reporter = __esm({
|
|
54587
55197
|
"src/core/reporter.ts"() {
|
|
54588
55198
|
"use strict";
|
|
54589
55199
|
init_define_WEB_FIRST_SEGMENTS();
|
|
54590
55200
|
init_source();
|
|
54591
55201
|
init_types3();
|
|
54592
|
-
|
|
55202
|
+
SKILL_AUDIT_PATH_MARKER = "(skill audit)";
|
|
55203
|
+
GROUP_ORDER = ["context", "mcp", "mcph", "session", "skill"];
|
|
54593
55204
|
GROUP_LABELS = {
|
|
54594
55205
|
context: "Context Files",
|
|
54595
55206
|
mcp: "MCP Configs",
|
|
54596
55207
|
mcph: "mcph Configs",
|
|
54597
|
-
session: "Session Audit"
|
|
55208
|
+
session: "Session Audit",
|
|
55209
|
+
skill: "Skill Audit"
|
|
54598
55210
|
};
|
|
54599
55211
|
GROUP_SUMMARY_NOUNS = {
|
|
54600
55212
|
context: "context file",
|
|
54601
55213
|
mcp: "MCP config",
|
|
54602
55214
|
mcph: "mcph config",
|
|
54603
|
-
session: "session audit"
|
|
55215
|
+
session: "session audit",
|
|
55216
|
+
skill: "skill audit"
|
|
54604
55217
|
};
|
|
54605
55218
|
}
|
|
54606
55219
|
});
|
|
@@ -54610,8 +55223,8 @@ var cli_exports = {};
|
|
|
54610
55223
|
__export(cli_exports, {
|
|
54611
55224
|
runCli: () => runCli
|
|
54612
55225
|
});
|
|
54613
|
-
import * as
|
|
54614
|
-
import * as
|
|
55226
|
+
import * as fs11 from "node:fs";
|
|
55227
|
+
import * as path16 from "node:path";
|
|
54615
55228
|
function validateCheckNames(names, source) {
|
|
54616
55229
|
const invalid = names.filter((n7) => n7 && !VALID_CHECKS.has(n7));
|
|
54617
55230
|
if (invalid.length > 0) {
|
|
@@ -54633,8 +55246,12 @@ async function runCli() {
|
|
|
54633
55246
|
"--mcph-strict-env-token",
|
|
54634
55247
|
"Upgrade mcph-config/prefer-env-token from warning to error (strict env-var-only posture)",
|
|
54635
55248
|
false
|
|
54636
|
-
).option("--session", "Run session audit checks (cross-project consistency)", false).option("--session-only", "Run only session checks, skip context and MCP checks", false).option("--
|
|
54637
|
-
|
|
55249
|
+
).option("--session", "Run session audit checks (cross-project consistency)", false).option("--session-only", "Run only session checks, skip context and MCP checks", false).option("--skills", "Run agent-skill checks (~/.claude/skills + ~/.claude/agents)", false).option("--skills-only", "Run only agent-skill checks, skip everything else", false).option(
|
|
55250
|
+
"--hooks-global",
|
|
55251
|
+
"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])",
|
|
55252
|
+
false
|
|
55253
|
+
).option("--watch", "Re-lint on context file changes", false).action(async (projectPath, opts) => {
|
|
55254
|
+
const resolvedPath = path16.resolve(projectPath);
|
|
54638
55255
|
const { config: config2, options, activeChecks } = resolveSession(resolvedPath, opts);
|
|
54639
55256
|
const spinner = options.format === "text" && !options.quiet ? ora("Scanning for context files...").start() : void 0;
|
|
54640
55257
|
try {
|
|
@@ -54651,6 +55268,9 @@ async function runCli() {
|
|
|
54651
55268
|
mcphStrictEnvToken: options.mcphStrictEnvToken,
|
|
54652
55269
|
session: options.session,
|
|
54653
55270
|
sessionOnly: options.sessionOnly,
|
|
55271
|
+
skills: options.skills,
|
|
55272
|
+
skillsOnly: options.skillsOnly,
|
|
55273
|
+
hooksGlobal: options.hooksGlobal,
|
|
54654
55274
|
tokenThresholds: config2?.tokenThresholds,
|
|
54655
55275
|
ignoreRules: config2?.ignoreRules
|
|
54656
55276
|
});
|
|
@@ -54750,43 +55370,43 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
|
|
|
54750
55370
|
const chalk2 = (await Promise.resolve().then(() => (init_source(), source_exports))).default;
|
|
54751
55371
|
console.log(chalk2.dim("\nWatching for changes... (Ctrl+C to stop)\n"));
|
|
54752
55372
|
const watchPaths = [
|
|
54753
|
-
|
|
54754
|
-
|
|
54755
|
-
|
|
54756
|
-
|
|
54757
|
-
|
|
54758
|
-
|
|
54759
|
-
|
|
54760
|
-
|
|
54761
|
-
|
|
54762
|
-
|
|
54763
|
-
|
|
54764
|
-
|
|
54765
|
-
|
|
54766
|
-
|
|
54767
|
-
|
|
55373
|
+
path16.join(resolvedPath, "CLAUDE.md"),
|
|
55374
|
+
path16.join(resolvedPath, "CLAUDE.local.md"),
|
|
55375
|
+
path16.join(resolvedPath, "AGENTS.md"),
|
|
55376
|
+
path16.join(resolvedPath, "AGENT.md"),
|
|
55377
|
+
path16.join(resolvedPath, ".cursorrules"),
|
|
55378
|
+
path16.join(resolvedPath, ".windsurfrules"),
|
|
55379
|
+
path16.join(resolvedPath, ".clinerules"),
|
|
55380
|
+
path16.join(resolvedPath, ".aiderules"),
|
|
55381
|
+
path16.join(resolvedPath, ".continuerules"),
|
|
55382
|
+
path16.join(resolvedPath, ".rules"),
|
|
55383
|
+
path16.join(resolvedPath, ".goosehints"),
|
|
55384
|
+
path16.join(resolvedPath, "GEMINI.md"),
|
|
55385
|
+
path16.join(resolvedPath, "replit.md"),
|
|
55386
|
+
path16.join(resolvedPath, "package.json"),
|
|
55387
|
+
path16.join(resolvedPath, ".mcp.json"),
|
|
54768
55388
|
// mcph configs — without these, edits to the .mcph.json the user is
|
|
54769
55389
|
// actively iterating on don't trigger a re-lint.
|
|
54770
|
-
|
|
54771
|
-
|
|
55390
|
+
path16.join(resolvedPath, ".mcph.json"),
|
|
55391
|
+
path16.join(resolvedPath, ".mcph.local.json"),
|
|
54772
55392
|
// ctxlint config — re-resolve on edit so threshold changes,
|
|
54773
55393
|
// ignore-list edits, and check-list overrides take effect without
|
|
54774
55394
|
// restarting the watcher.
|
|
54775
|
-
|
|
54776
|
-
|
|
55395
|
+
path16.join(resolvedPath, ".ctxlintrc"),
|
|
55396
|
+
path16.join(resolvedPath, ".ctxlintrc.json")
|
|
54777
55397
|
];
|
|
54778
55398
|
const watchDirs = [
|
|
54779
|
-
|
|
54780
|
-
|
|
54781
|
-
|
|
54782
|
-
|
|
54783
|
-
|
|
54784
|
-
|
|
54785
|
-
|
|
54786
|
-
|
|
54787
|
-
|
|
54788
|
-
|
|
54789
|
-
|
|
55399
|
+
path16.join(resolvedPath, ".claude"),
|
|
55400
|
+
path16.join(resolvedPath, ".cursor"),
|
|
55401
|
+
path16.join(resolvedPath, ".github"),
|
|
55402
|
+
path16.join(resolvedPath, ".windsurf"),
|
|
55403
|
+
path16.join(resolvedPath, ".aide"),
|
|
55404
|
+
path16.join(resolvedPath, ".amazonq"),
|
|
55405
|
+
path16.join(resolvedPath, ".goose"),
|
|
55406
|
+
path16.join(resolvedPath, ".junie"),
|
|
55407
|
+
path16.join(resolvedPath, ".aiassistant"),
|
|
55408
|
+
path16.join(resolvedPath, ".continue"),
|
|
55409
|
+
path16.join(resolvedPath, ".vscode")
|
|
54790
55410
|
];
|
|
54791
55411
|
const watchers = [];
|
|
54792
55412
|
let debounceTimer = null;
|
|
@@ -54818,6 +55438,9 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
|
|
|
54818
55438
|
mcphStrictEnvToken: liveOptions.mcphStrictEnvToken,
|
|
54819
55439
|
session: liveOptions.session,
|
|
54820
55440
|
sessionOnly: liveOptions.sessionOnly,
|
|
55441
|
+
skills: liveOptions.skills,
|
|
55442
|
+
skillsOnly: liveOptions.skillsOnly,
|
|
55443
|
+
hooksGlobal: liveOptions.hooksGlobal,
|
|
54821
55444
|
tokenThresholds: liveConfig?.tokenThresholds,
|
|
54822
55445
|
ignoreRules: liveConfig?.ignoreRules
|
|
54823
55446
|
});
|
|
@@ -54845,13 +55468,13 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
|
|
|
54845
55468
|
};
|
|
54846
55469
|
for (const filePath of watchPaths) {
|
|
54847
55470
|
try {
|
|
54848
|
-
watchers.push(
|
|
55471
|
+
watchers.push(fs11.watch(filePath, rerun));
|
|
54849
55472
|
} catch {
|
|
54850
55473
|
}
|
|
54851
55474
|
}
|
|
54852
55475
|
for (const dir of watchDirs) {
|
|
54853
55476
|
try {
|
|
54854
|
-
watchers.push(
|
|
55477
|
+
watchers.push(fs11.watch(dir, { recursive: true }, rerun));
|
|
54855
55478
|
} catch {
|
|
54856
55479
|
}
|
|
54857
55480
|
}
|
|
@@ -54872,17 +55495,17 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
|
|
|
54872
55495
|
}
|
|
54873
55496
|
});
|
|
54874
55497
|
program2.command("init").description("Set up a git pre-commit hook that runs ctxlint --strict").argument("[path]", "Project directory", ".").action(async (projectPath) => {
|
|
54875
|
-
const resolvedRoot =
|
|
54876
|
-
const gitDir =
|
|
54877
|
-
const hooksDir =
|
|
54878
|
-
if (!
|
|
55498
|
+
const resolvedRoot = path16.resolve(projectPath);
|
|
55499
|
+
const gitDir = path16.join(resolvedRoot, ".git");
|
|
55500
|
+
const hooksDir = path16.join(gitDir, "hooks");
|
|
55501
|
+
if (!fs11.existsSync(gitDir)) {
|
|
54879
55502
|
console.error('Error: not a git repository. Run "git init" first.');
|
|
54880
55503
|
process.exit(1);
|
|
54881
55504
|
}
|
|
54882
|
-
if (!
|
|
54883
|
-
|
|
55505
|
+
if (!fs11.existsSync(hooksDir)) {
|
|
55506
|
+
fs11.mkdirSync(hooksDir, { recursive: true });
|
|
54884
55507
|
}
|
|
54885
|
-
const hookPath =
|
|
55508
|
+
const hookPath = path16.join(hooksDir, "pre-commit");
|
|
54886
55509
|
const fullHookContent = `#!/bin/sh
|
|
54887
55510
|
# ctxlint pre-commit hook
|
|
54888
55511
|
npx @yawlabs/ctxlint@${VERSION} --strict
|
|
@@ -54891,16 +55514,16 @@ npx @yawlabs/ctxlint@${VERSION} --strict
|
|
|
54891
55514
|
# ctxlint pre-commit hook
|
|
54892
55515
|
npx @yawlabs/ctxlint@${VERSION} --strict
|
|
54893
55516
|
`;
|
|
54894
|
-
if (
|
|
54895
|
-
const existing =
|
|
55517
|
+
if (fs11.existsSync(hookPath)) {
|
|
55518
|
+
const existing = fs11.readFileSync(hookPath, "utf-8");
|
|
54896
55519
|
if (existing.includes("ctxlint")) {
|
|
54897
55520
|
console.log("Pre-commit hook already includes ctxlint.");
|
|
54898
55521
|
return;
|
|
54899
55522
|
}
|
|
54900
|
-
|
|
55523
|
+
fs11.appendFileSync(hookPath, appendHookContent);
|
|
54901
55524
|
console.log("Added ctxlint to existing pre-commit hook.");
|
|
54902
55525
|
} else {
|
|
54903
|
-
|
|
55526
|
+
fs11.writeFileSync(hookPath, fullHookContent, { mode: 493 });
|
|
54904
55527
|
console.log("Created pre-commit hook at .git/hooks/pre-commit");
|
|
54905
55528
|
}
|
|
54906
55529
|
console.log("ctxlint will now run automatically before each commit.");
|
|
@@ -54918,7 +55541,7 @@ async function promptYesNo(question) {
|
|
|
54918
55541
|
}
|
|
54919
55542
|
}
|
|
54920
55543
|
function resolveSession(resolvedPath, opts) {
|
|
54921
|
-
const configPath = opts.config ?
|
|
55544
|
+
const configPath = opts.config ? path16.resolve(opts.config) : void 0;
|
|
54922
55545
|
const config2 = configPath ? loadConfigFromPath(configPath) : loadConfig(resolvedPath);
|
|
54923
55546
|
const mcpGlobal = opts.mcpGlobal || config2?.mcpGlobal || false;
|
|
54924
55547
|
const mcpOnly = opts.mcpOnly || config2?.mcpOnly || false;
|
|
@@ -54929,6 +55552,9 @@ function resolveSession(resolvedPath, opts) {
|
|
|
54929
55552
|
const mcphStrictEnvToken = opts.mcphStrictEnvToken || config2?.mcphStrictEnvToken || false;
|
|
54930
55553
|
const sessionOnly = opts.sessionOnly || config2?.sessionOnly || false;
|
|
54931
55554
|
const sessionFlag = opts.session || sessionOnly || config2?.session || false;
|
|
55555
|
+
const skillsOnly = opts.skillsOnly || config2?.skillsOnly || false;
|
|
55556
|
+
const skillsFlag = opts.skills || skillsOnly || config2?.skills || false;
|
|
55557
|
+
const hooksGlobal = opts.hooksGlobal || config2?.hooksGlobal || false;
|
|
54932
55558
|
let explicitChecks = opts.checks ? validateCheckNames(
|
|
54933
55559
|
opts.checks.split(",").map((c3) => c3.trim()),
|
|
54934
55560
|
"--checks"
|
|
@@ -54937,14 +55563,18 @@ function resolveSession(resolvedPath, opts) {
|
|
|
54937
55563
|
const hasMcpInChecks = explicitChecks?.some((c3) => c3.startsWith("mcp-") && !c3.startsWith("mcph-")) || false;
|
|
54938
55564
|
const hasMcphInChecks = explicitChecks?.some((c3) => c3.startsWith("mcph-")) || false;
|
|
54939
55565
|
const hasSessionInChecks = explicitChecks?.some((c3) => c3.startsWith("session-")) || false;
|
|
55566
|
+
const hasSkillInChecks = explicitChecks?.some((c3) => c3.startsWith("skill-")) || false;
|
|
54940
55567
|
const effectiveMcp = mcpFlag || hasMcpInChecks;
|
|
54941
55568
|
const effectiveMcph = mcphFlag || hasMcphInChecks;
|
|
54942
55569
|
const effectiveSession = sessionFlag || sessionOnly || hasSessionInChecks;
|
|
55570
|
+
const effectiveSkills = skillsFlag || skillsOnly || hasSkillInChecks;
|
|
54943
55571
|
let checks;
|
|
54944
55572
|
if (explicitChecks) {
|
|
54945
55573
|
checks = explicitChecks;
|
|
54946
55574
|
} else if (sessionOnly) {
|
|
54947
55575
|
checks = ALL_SESSION_CHECKS;
|
|
55576
|
+
} else if (skillsOnly) {
|
|
55577
|
+
checks = ALL_SKILL_CHECKS;
|
|
54948
55578
|
} else if (mcpOnly) {
|
|
54949
55579
|
checks = ALL_MCP_CHECKS;
|
|
54950
55580
|
} else if (mcphOnly) {
|
|
@@ -54955,7 +55585,8 @@ function resolveSession(resolvedPath, opts) {
|
|
|
54955
55585
|
...base,
|
|
54956
55586
|
...effectiveMcp ? ALL_MCP_CHECKS : [],
|
|
54957
55587
|
...effectiveMcph ? ALL_MCPH_CHECKS : [],
|
|
54958
|
-
...effectiveSession ? ALL_SESSION_CHECKS : []
|
|
55588
|
+
...effectiveSession ? ALL_SESSION_CHECKS : [],
|
|
55589
|
+
...effectiveSkills ? ALL_SKILL_CHECKS : []
|
|
54959
55590
|
];
|
|
54960
55591
|
}
|
|
54961
55592
|
const options = {
|
|
@@ -54980,7 +55611,10 @@ function resolveSession(resolvedPath, opts) {
|
|
|
54980
55611
|
mcphGlobal,
|
|
54981
55612
|
mcphStrictEnvToken,
|
|
54982
55613
|
session: effectiveSession,
|
|
54983
|
-
sessionOnly
|
|
55614
|
+
sessionOnly,
|
|
55615
|
+
skills: effectiveSkills,
|
|
55616
|
+
skillsOnly,
|
|
55617
|
+
hooksGlobal
|
|
54984
55618
|
};
|
|
54985
55619
|
const activeChecks = options.checks.filter((c3) => !options.ignore.includes(c3));
|
|
54986
55620
|
return { config: config2, options, activeChecks };
|
|
@@ -55014,7 +55648,8 @@ var init_cli = __esm({
|
|
|
55014
55648
|
...ALL_CHECKS,
|
|
55015
55649
|
...ALL_MCP_CHECKS,
|
|
55016
55650
|
...ALL_MCPH_CHECKS,
|
|
55017
|
-
...ALL_SESSION_CHECKS
|
|
55651
|
+
...ALL_SESSION_CHECKS,
|
|
55652
|
+
...ALL_SKILL_CHECKS
|
|
55018
55653
|
]);
|
|
55019
55654
|
}
|
|
55020
55655
|
});
|