@yawlabs/ctxlint 0.12.2 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.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 +797 -175
- 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(
|
|
@@ -41629,6 +41629,37 @@ function parseTree(text, errors = [], options = ParseOptions.DEFAULT) {
|
|
|
41629
41629
|
}
|
|
41630
41630
|
return result;
|
|
41631
41631
|
}
|
|
41632
|
+
function findNodeAtLocation(root, path17) {
|
|
41633
|
+
if (!root) {
|
|
41634
|
+
return void 0;
|
|
41635
|
+
}
|
|
41636
|
+
let node = root;
|
|
41637
|
+
for (let segment of path17) {
|
|
41638
|
+
if (typeof segment === "string") {
|
|
41639
|
+
if (node.type !== "object" || !Array.isArray(node.children)) {
|
|
41640
|
+
return void 0;
|
|
41641
|
+
}
|
|
41642
|
+
let found = false;
|
|
41643
|
+
for (const propertyNode of node.children) {
|
|
41644
|
+
if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment && propertyNode.children.length === 2) {
|
|
41645
|
+
node = propertyNode.children[1];
|
|
41646
|
+
found = true;
|
|
41647
|
+
break;
|
|
41648
|
+
}
|
|
41649
|
+
}
|
|
41650
|
+
if (!found) {
|
|
41651
|
+
return void 0;
|
|
41652
|
+
}
|
|
41653
|
+
} else {
|
|
41654
|
+
const index = segment;
|
|
41655
|
+
if (node.type !== "array" || index < 0 || !Array.isArray(node.children) || index >= node.children.length) {
|
|
41656
|
+
return void 0;
|
|
41657
|
+
}
|
|
41658
|
+
node = node.children[index];
|
|
41659
|
+
}
|
|
41660
|
+
}
|
|
41661
|
+
return node;
|
|
41662
|
+
}
|
|
41632
41663
|
function getNodeValue(node) {
|
|
41633
41664
|
switch (node.type) {
|
|
41634
41665
|
case "array":
|
|
@@ -42001,7 +42032,7 @@ var init_edit = __esm({
|
|
|
42001
42032
|
});
|
|
42002
42033
|
|
|
42003
42034
|
// node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/main.js
|
|
42004
|
-
var ScanError, SyntaxKind, parseTree2, getNodeValue2, ParseErrorCode;
|
|
42035
|
+
var ScanError, SyntaxKind, parseTree2, findNodeAtLocation2, getNodeValue2, ParseErrorCode;
|
|
42005
42036
|
var init_main = __esm({
|
|
42006
42037
|
"node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/main.js"() {
|
|
42007
42038
|
"use strict";
|
|
@@ -42039,6 +42070,7 @@ var init_main = __esm({
|
|
|
42039
42070
|
SyntaxKind2[SyntaxKind2["EOF"] = 17] = "EOF";
|
|
42040
42071
|
})(SyntaxKind || (SyntaxKind = {}));
|
|
42041
42072
|
parseTree2 = parseTree;
|
|
42073
|
+
findNodeAtLocation2 = findNodeAtLocation;
|
|
42042
42074
|
getNodeValue2 = getNodeValue;
|
|
42043
42075
|
(function(ParseErrorCode2) {
|
|
42044
42076
|
ParseErrorCode2[ParseErrorCode2["InvalidSymbol"] = 1] = "InvalidSymbol";
|
|
@@ -45978,6 +46010,487 @@ var init_content_secrets = __esm({
|
|
|
45978
46010
|
}
|
|
45979
46011
|
});
|
|
45980
46012
|
|
|
46013
|
+
// src/core/checks/hook-coverage.ts
|
|
46014
|
+
import * as fs7 from "node:fs";
|
|
46015
|
+
import * as os2 from "node:os";
|
|
46016
|
+
import * as path11 from "node:path";
|
|
46017
|
+
function loadSettingsSources2(projectRoot, homeDir, includeUserGlobal) {
|
|
46018
|
+
const home2 = homeDir;
|
|
46019
|
+
const candidates = [
|
|
46020
|
+
{
|
|
46021
|
+
filePath: path11.join(projectRoot, ".claude", "settings.json"),
|
|
46022
|
+
displayPath: path11.join(".claude", "settings.json"),
|
|
46023
|
+
isUserGlobal: false
|
|
46024
|
+
},
|
|
46025
|
+
{
|
|
46026
|
+
filePath: path11.join(projectRoot, ".claude", "settings.local.json"),
|
|
46027
|
+
displayPath: path11.join(".claude", "settings.local.json"),
|
|
46028
|
+
isUserGlobal: false
|
|
46029
|
+
}
|
|
46030
|
+
];
|
|
46031
|
+
if (includeUserGlobal) {
|
|
46032
|
+
candidates.push({
|
|
46033
|
+
filePath: path11.join(home2, ".claude", "settings.json"),
|
|
46034
|
+
displayPath: "~/.claude/settings.json",
|
|
46035
|
+
isUserGlobal: true
|
|
46036
|
+
});
|
|
46037
|
+
}
|
|
46038
|
+
const sources = [];
|
|
46039
|
+
for (const c3 of candidates) {
|
|
46040
|
+
let content;
|
|
46041
|
+
try {
|
|
46042
|
+
content = stripBom(fs7.readFileSync(c3.filePath, "utf-8"));
|
|
46043
|
+
} catch {
|
|
46044
|
+
continue;
|
|
46045
|
+
}
|
|
46046
|
+
const tree = parseTree2(content, [], { allowTrailingComma: true });
|
|
46047
|
+
sources.push({ ...c3, content, tree });
|
|
46048
|
+
}
|
|
46049
|
+
return sources;
|
|
46050
|
+
}
|
|
46051
|
+
function expandPath(raw, projectRoot, homeDir) {
|
|
46052
|
+
let s = raw;
|
|
46053
|
+
if (s === "~" || s.startsWith("~/") || s.startsWith("~\\")) {
|
|
46054
|
+
s = path11.join(homeDir, s.slice(1));
|
|
46055
|
+
}
|
|
46056
|
+
const replacements = {
|
|
46057
|
+
CLAUDE_PROJECT_DIR: projectRoot,
|
|
46058
|
+
CLAUDE_CONFIG_DIR: path11.join(homeDir, ".claude"),
|
|
46059
|
+
HOME: homeDir,
|
|
46060
|
+
USERPROFILE: homeDir
|
|
46061
|
+
};
|
|
46062
|
+
s = s.replace(
|
|
46063
|
+
/\$\{?([A-Z_][A-Z0-9_]*)\}?/g,
|
|
46064
|
+
(m, name) => name in replacements ? replacements[name] : m
|
|
46065
|
+
);
|
|
46066
|
+
if (/\$\{?[A-Za-z_]/.test(s)) return null;
|
|
46067
|
+
if (!path11.isAbsolute(s)) s = path11.resolve(projectRoot, s);
|
|
46068
|
+
return s;
|
|
46069
|
+
}
|
|
46070
|
+
function looksLikePath(token) {
|
|
46071
|
+
const hasSep = token.includes("/") || token.includes("\\");
|
|
46072
|
+
if (PATH_PREFIX.test(token)) return true;
|
|
46073
|
+
return hasSep && SCRIPT_EXT.test(token);
|
|
46074
|
+
}
|
|
46075
|
+
function extractCommandPaths(command, projectRoot, homeDir) {
|
|
46076
|
+
const tokens = command.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
|
|
46077
|
+
const out = [];
|
|
46078
|
+
for (const t2 of tokens) {
|
|
46079
|
+
const token = t2.replace(/^['"]|['"]$/g, "");
|
|
46080
|
+
if (!looksLikePath(token)) continue;
|
|
46081
|
+
out.push({ raw: token, resolved: expandPath(token, projectRoot, homeDir) });
|
|
46082
|
+
}
|
|
46083
|
+
return out;
|
|
46084
|
+
}
|
|
46085
|
+
function extractPermissionPaths(entry, projectRoot, homeDir) {
|
|
46086
|
+
const inner = entry.replace(/^[A-Za-z]+\((.*)\)$/, "$1");
|
|
46087
|
+
return extractCommandPaths(inner, projectRoot, homeDir);
|
|
46088
|
+
}
|
|
46089
|
+
function lineForPath(source, jsonPath) {
|
|
46090
|
+
if (!source.tree) return 1;
|
|
46091
|
+
const node = findNodeAtLocation2(source.tree, jsonPath);
|
|
46092
|
+
if (!node) return 1;
|
|
46093
|
+
return offsetToPosition(source.content, node.offset).line;
|
|
46094
|
+
}
|
|
46095
|
+
function checkSource(source, projectRoot, homeDir) {
|
|
46096
|
+
if (!source.tree) return [];
|
|
46097
|
+
const data = getNodeValue2(source.tree);
|
|
46098
|
+
if (!data || typeof data !== "object") return [];
|
|
46099
|
+
const issues = [];
|
|
46100
|
+
const report = (cand, line, origin) => {
|
|
46101
|
+
if (cand.resolved === null) return;
|
|
46102
|
+
if (fileExists(cand.resolved)) return;
|
|
46103
|
+
const where = source.isUserGlobal ? ` (in ${source.displayPath})` : "";
|
|
46104
|
+
issues.push({
|
|
46105
|
+
severity: "warning",
|
|
46106
|
+
check: "hook-coverage",
|
|
46107
|
+
ruleId: "hook-coverage/dead-hook",
|
|
46108
|
+
line,
|
|
46109
|
+
message: `${origin} references "${cand.raw}" which does not exist on disk${where} \u2014 the gate silently no-ops`,
|
|
46110
|
+
suggestion: source.isUserGlobal ? `Restore the script at ${cand.resolved}, or remove the dead entry from ${source.displayPath}.` : `Restore the script, fix the path, or remove the dead entry. A PreToolUse hook pointing at a missing script does not block anything.`
|
|
46111
|
+
});
|
|
46112
|
+
};
|
|
46113
|
+
const hooks = data.hooks ?? {};
|
|
46114
|
+
for (const [event, matchers] of Object.entries(hooks)) {
|
|
46115
|
+
if (!Array.isArray(matchers)) continue;
|
|
46116
|
+
matchers.forEach((matcher, mi2) => {
|
|
46117
|
+
const subHooks = matcher?.hooks;
|
|
46118
|
+
if (!Array.isArray(subHooks)) return;
|
|
46119
|
+
subHooks.forEach((h2, hi2) => {
|
|
46120
|
+
if (!h2 || typeof h2.command !== "string") return;
|
|
46121
|
+
const line = lineForPath(source, ["hooks", event, mi2, "hooks", hi2, "command"]);
|
|
46122
|
+
for (const cand of extractCommandPaths(h2.command, projectRoot, homeDir)) {
|
|
46123
|
+
report(cand, line, `${event} hook`);
|
|
46124
|
+
}
|
|
46125
|
+
});
|
|
46126
|
+
});
|
|
46127
|
+
}
|
|
46128
|
+
const perms = data.permissions ?? {};
|
|
46129
|
+
for (const listName of ["allow", "deny", "ask"]) {
|
|
46130
|
+
const list = perms[listName];
|
|
46131
|
+
if (!Array.isArray(list)) continue;
|
|
46132
|
+
list.forEach((entry, i2) => {
|
|
46133
|
+
if (typeof entry !== "string") return;
|
|
46134
|
+
const line = lineForPath(source, ["permissions", listName, i2]);
|
|
46135
|
+
for (const cand of extractPermissionPaths(entry, projectRoot, homeDir)) {
|
|
46136
|
+
report(cand, line, `permissions.${listName} entry`);
|
|
46137
|
+
}
|
|
46138
|
+
});
|
|
46139
|
+
}
|
|
46140
|
+
return issues;
|
|
46141
|
+
}
|
|
46142
|
+
async function checkHookCoverage(projectRoot, homeDir = os2.homedir(), options = {}) {
|
|
46143
|
+
const sources = loadSettingsSources2(projectRoot, homeDir, options.userGlobal ?? false);
|
|
46144
|
+
const issues = [];
|
|
46145
|
+
for (const source of sources) {
|
|
46146
|
+
issues.push(...checkSource(source, projectRoot, homeDir));
|
|
46147
|
+
}
|
|
46148
|
+
return issues;
|
|
46149
|
+
}
|
|
46150
|
+
var SCRIPT_EXT, PATH_PREFIX;
|
|
46151
|
+
var init_hook_coverage = __esm({
|
|
46152
|
+
"src/core/checks/hook-coverage.ts"() {
|
|
46153
|
+
"use strict";
|
|
46154
|
+
init_define_WEB_FIRST_SEGMENTS();
|
|
46155
|
+
init_main();
|
|
46156
|
+
init_fs();
|
|
46157
|
+
init_positions();
|
|
46158
|
+
SCRIPT_EXT = /\.(sh|bash|zsh|fish|js|mjs|cjs|ts|py|rb|pl|ps1|cmd|bat|exe)$/i;
|
|
46159
|
+
PATH_PREFIX = /^(~|\.\.?[/\\]|[/\\]|\$\{?[A-Za-z_]|[A-Za-z]:[/\\])/;
|
|
46160
|
+
}
|
|
46161
|
+
});
|
|
46162
|
+
|
|
46163
|
+
// src/core/skill-scanner.ts
|
|
46164
|
+
import * as fs8 from "node:fs";
|
|
46165
|
+
import * as os3 from "node:os";
|
|
46166
|
+
import * as path12 from "node:path";
|
|
46167
|
+
function defaultHome() {
|
|
46168
|
+
return process.env.HOME || process.env.USERPROFILE || os3.homedir();
|
|
46169
|
+
}
|
|
46170
|
+
function scanSkillFiles(homeDir = defaultHome()) {
|
|
46171
|
+
const files = [];
|
|
46172
|
+
const orphanedSkillDirs = [];
|
|
46173
|
+
const claudeDir = path12.join(homeDir, ".claude");
|
|
46174
|
+
const skillsRoot = path12.join(claudeDir, "skills");
|
|
46175
|
+
for (const name of safeReadDir(skillsRoot, "dir")) {
|
|
46176
|
+
const skillMd = path12.join(skillsRoot, name, "SKILL.md");
|
|
46177
|
+
const content = safeRead(skillMd);
|
|
46178
|
+
if (content === null) {
|
|
46179
|
+
orphanedSkillDirs.push({ name, displayPath: `~/.claude/skills/${name}/` });
|
|
46180
|
+
continue;
|
|
46181
|
+
}
|
|
46182
|
+
files.push({
|
|
46183
|
+
filePath: skillMd,
|
|
46184
|
+
displayPath: `~/.claude/skills/${name}/SKILL.md`,
|
|
46185
|
+
kind: "skill",
|
|
46186
|
+
name,
|
|
46187
|
+
content
|
|
46188
|
+
});
|
|
46189
|
+
}
|
|
46190
|
+
const agentsRoot = path12.join(claudeDir, "agents");
|
|
46191
|
+
for (const fileName of safeReadDir(agentsRoot, "file")) {
|
|
46192
|
+
if (!fileName.endsWith(".md")) continue;
|
|
46193
|
+
const agentMd = path12.join(agentsRoot, fileName);
|
|
46194
|
+
const content = safeRead(agentMd);
|
|
46195
|
+
if (content === null) continue;
|
|
46196
|
+
files.push({
|
|
46197
|
+
filePath: agentMd,
|
|
46198
|
+
displayPath: `~/.claude/agents/${fileName}`,
|
|
46199
|
+
kind: "agent",
|
|
46200
|
+
name: fileName.replace(/\.md$/, ""),
|
|
46201
|
+
content
|
|
46202
|
+
});
|
|
46203
|
+
}
|
|
46204
|
+
return { files, orphanedSkillDirs };
|
|
46205
|
+
}
|
|
46206
|
+
function safeReadDir(dir, want) {
|
|
46207
|
+
let entries;
|
|
46208
|
+
try {
|
|
46209
|
+
entries = fs8.readdirSync(dir, { withFileTypes: true });
|
|
46210
|
+
} catch {
|
|
46211
|
+
return [];
|
|
46212
|
+
}
|
|
46213
|
+
return entries.filter((e) => want === "dir" ? e.isDirectory() : e.isFile()).map((e) => e.name).sort();
|
|
46214
|
+
}
|
|
46215
|
+
function safeRead(file2) {
|
|
46216
|
+
try {
|
|
46217
|
+
return stripBom(fs8.readFileSync(file2, "utf-8"));
|
|
46218
|
+
} catch {
|
|
46219
|
+
return null;
|
|
46220
|
+
}
|
|
46221
|
+
}
|
|
46222
|
+
var init_skill_scanner = __esm({
|
|
46223
|
+
"src/core/skill-scanner.ts"() {
|
|
46224
|
+
"use strict";
|
|
46225
|
+
init_define_WEB_FIRST_SEGMENTS();
|
|
46226
|
+
init_fs();
|
|
46227
|
+
}
|
|
46228
|
+
});
|
|
46229
|
+
|
|
46230
|
+
// src/core/checks/skills.ts
|
|
46231
|
+
import * as path13 from "node:path";
|
|
46232
|
+
function parseFrontmatter3(content) {
|
|
46233
|
+
const lines = content.split("\n");
|
|
46234
|
+
if (lines[0]?.trim() !== "---") {
|
|
46235
|
+
return { found: false, unclosed: false, fields: {}, endLine: 0 };
|
|
46236
|
+
}
|
|
46237
|
+
const fields = {};
|
|
46238
|
+
let arrayKey = "";
|
|
46239
|
+
let endLine = 0;
|
|
46240
|
+
for (let i2 = 1; i2 < lines.length; i2++) {
|
|
46241
|
+
const line = lines[i2].trim();
|
|
46242
|
+
if (line === "---") {
|
|
46243
|
+
endLine = i2 + 1;
|
|
46244
|
+
break;
|
|
46245
|
+
}
|
|
46246
|
+
if (line.startsWith("- ") && arrayKey) {
|
|
46247
|
+
const item = line.slice(2).trim().replace(/^["']|["']$/g, "");
|
|
46248
|
+
fields[arrayKey] = fields[arrayKey] ? `${fields[arrayKey]}, ${item}` : item;
|
|
46249
|
+
continue;
|
|
46250
|
+
}
|
|
46251
|
+
arrayKey = "";
|
|
46252
|
+
const m = line.match(/^([\w-]+)\s*:\s*(.*)$/);
|
|
46253
|
+
if (m) {
|
|
46254
|
+
let val = m[2].trim();
|
|
46255
|
+
const inline = val.match(/^\[(.*)\]$/);
|
|
46256
|
+
if (inline) {
|
|
46257
|
+
val = inline[1].split(",").map((s) => s.trim().replace(/^["']|["']$/g, "")).filter(Boolean).join(", ");
|
|
46258
|
+
}
|
|
46259
|
+
fields[m[1]] = val;
|
|
46260
|
+
if (!m[2].trim()) arrayKey = m[1];
|
|
46261
|
+
}
|
|
46262
|
+
}
|
|
46263
|
+
if (endLine === 0) return { found: true, unclosed: true, fields, endLine: lines.length };
|
|
46264
|
+
return { found: true, unclosed: false, fields, endLine };
|
|
46265
|
+
}
|
|
46266
|
+
function checkMissingFrontmatter(file2, fm) {
|
|
46267
|
+
if (fm.unclosed) {
|
|
46268
|
+
return [
|
|
46269
|
+
{
|
|
46270
|
+
severity: "error",
|
|
46271
|
+
check: "skill-frontmatter",
|
|
46272
|
+
ruleId: "skill/missing-frontmatter",
|
|
46273
|
+
line: 1,
|
|
46274
|
+
message: `${file2.displayPath}: frontmatter opens with \`---\` but is never closed`,
|
|
46275
|
+
suggestion: "Add a matching `---` line after the last frontmatter field."
|
|
46276
|
+
}
|
|
46277
|
+
];
|
|
46278
|
+
}
|
|
46279
|
+
if (!fm.found) {
|
|
46280
|
+
return [
|
|
46281
|
+
{
|
|
46282
|
+
severity: "error",
|
|
46283
|
+
check: "skill-frontmatter",
|
|
46284
|
+
ruleId: "skill/missing-frontmatter",
|
|
46285
|
+
line: 1,
|
|
46286
|
+
message: `${file2.displayPath}: missing YAML frontmatter`,
|
|
46287
|
+
suggestion: `Add frontmatter with ${REQUIRED_FIELDS[file2.kind].join(" + ")}.`
|
|
46288
|
+
}
|
|
46289
|
+
];
|
|
46290
|
+
}
|
|
46291
|
+
const issues = [];
|
|
46292
|
+
for (const field of REQUIRED_FIELDS[file2.kind]) {
|
|
46293
|
+
if (!fm.fields[field]) {
|
|
46294
|
+
issues.push({
|
|
46295
|
+
severity: "warning",
|
|
46296
|
+
check: "skill-frontmatter",
|
|
46297
|
+
ruleId: "skill/missing-frontmatter",
|
|
46298
|
+
line: 1,
|
|
46299
|
+
message: `${file2.displayPath}: missing required frontmatter field "${field}"`,
|
|
46300
|
+
suggestion: field === "description" ? "A skill/agent with no description is never selected \u2014 add one describing when to use it." : `Add a "${field}" field to the frontmatter.`
|
|
46301
|
+
});
|
|
46302
|
+
}
|
|
46303
|
+
}
|
|
46304
|
+
return issues;
|
|
46305
|
+
}
|
|
46306
|
+
function checkBrokenRefs(file2, fmEndLine) {
|
|
46307
|
+
const issues = [];
|
|
46308
|
+
const skillDir = path13.dirname(file2.filePath);
|
|
46309
|
+
const lines = file2.content.split("\n");
|
|
46310
|
+
let inCode = false;
|
|
46311
|
+
let codeLang = "";
|
|
46312
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
46313
|
+
if (i2 + 1 <= fmEndLine) continue;
|
|
46314
|
+
const raw = lines[i2];
|
|
46315
|
+
if (raw.trimStart().startsWith("```")) {
|
|
46316
|
+
inCode = !inCode;
|
|
46317
|
+
codeLang = inCode ? raw.trimStart().slice(3).trim().toLowerCase() : "";
|
|
46318
|
+
continue;
|
|
46319
|
+
}
|
|
46320
|
+
if (inCode && isExampleLang(codeLang)) continue;
|
|
46321
|
+
BODY_PATH.lastIndex = 0;
|
|
46322
|
+
let m;
|
|
46323
|
+
while ((m = BODY_PATH.exec(raw)) !== null) {
|
|
46324
|
+
let value = m[1];
|
|
46325
|
+
if (PATH_SKIP.test(value)) continue;
|
|
46326
|
+
if (!value.startsWith("./") && !value.startsWith("../")) continue;
|
|
46327
|
+
while (/[.,;:]$/.test(value)) value = value.slice(0, -1);
|
|
46328
|
+
const resolved = path13.resolve(skillDir, value);
|
|
46329
|
+
if (fileExists(resolved) || isDirectory(resolved)) continue;
|
|
46330
|
+
issues.push({
|
|
46331
|
+
severity: "warning",
|
|
46332
|
+
check: "skill-broken-ref",
|
|
46333
|
+
ruleId: "skill/broken-ref",
|
|
46334
|
+
line: i2 + 1,
|
|
46335
|
+
message: `${file2.displayPath}: references "${value}" which does not exist relative to the skill directory`,
|
|
46336
|
+
suggestion: "Fix the path, or use a path that exists relative to the skill/agent file."
|
|
46337
|
+
});
|
|
46338
|
+
}
|
|
46339
|
+
}
|
|
46340
|
+
return issues;
|
|
46341
|
+
}
|
|
46342
|
+
function isExampleLang(lang) {
|
|
46343
|
+
return [
|
|
46344
|
+
"js",
|
|
46345
|
+
"javascript",
|
|
46346
|
+
"ts",
|
|
46347
|
+
"typescript",
|
|
46348
|
+
"tsx",
|
|
46349
|
+
"jsx",
|
|
46350
|
+
"py",
|
|
46351
|
+
"python",
|
|
46352
|
+
"go",
|
|
46353
|
+
"rust",
|
|
46354
|
+
"java",
|
|
46355
|
+
"c",
|
|
46356
|
+
"cpp",
|
|
46357
|
+
"ruby",
|
|
46358
|
+
"php",
|
|
46359
|
+
"json",
|
|
46360
|
+
"yaml",
|
|
46361
|
+
"yml",
|
|
46362
|
+
"toml",
|
|
46363
|
+
// Shell fences hold command/example snippets, not in-repo refs. A `./foo.sh`
|
|
46364
|
+
// inside a bash block is an invocation example (often a cross-repo script or
|
|
46365
|
+
// a placeholder), not a path that should resolve relative to the skill dir.
|
|
46366
|
+
// Matches the rule's "prefer a false negative over a false positive" posture.
|
|
46367
|
+
"bash",
|
|
46368
|
+
"sh",
|
|
46369
|
+
"shell",
|
|
46370
|
+
"shellscript",
|
|
46371
|
+
"zsh",
|
|
46372
|
+
"console"
|
|
46373
|
+
].includes(lang);
|
|
46374
|
+
}
|
|
46375
|
+
function extractTriggers(fm) {
|
|
46376
|
+
const phrases = /* @__PURE__ */ new Set();
|
|
46377
|
+
const desc = fm.fields["description"] ?? "";
|
|
46378
|
+
for (const q3 of desc.matchAll(/["']([^"']{3,})["']/g)) {
|
|
46379
|
+
phrases.add(normalizeTrigger(q3[1]));
|
|
46380
|
+
}
|
|
46381
|
+
for (const key of ["trigger", "triggers"]) {
|
|
46382
|
+
const val = fm.fields[key];
|
|
46383
|
+
if (val) for (const part of val.split(",")) phrases.add(normalizeTrigger(part));
|
|
46384
|
+
}
|
|
46385
|
+
phrases.delete("");
|
|
46386
|
+
return [...phrases];
|
|
46387
|
+
}
|
|
46388
|
+
function normalizeTrigger(s) {
|
|
46389
|
+
return s.toLowerCase().trim().replace(/\s+/g, " ");
|
|
46390
|
+
}
|
|
46391
|
+
function checkTriggerCollisions(parsed) {
|
|
46392
|
+
const byTrigger = /* @__PURE__ */ new Map();
|
|
46393
|
+
for (const { file: file2, fm } of parsed) {
|
|
46394
|
+
for (const t2 of extractTriggers(fm)) {
|
|
46395
|
+
const list = byTrigger.get(t2) ?? [];
|
|
46396
|
+
list.push(file2);
|
|
46397
|
+
byTrigger.set(t2, list);
|
|
46398
|
+
}
|
|
46399
|
+
}
|
|
46400
|
+
const issues = [];
|
|
46401
|
+
for (const [trigger, owners] of byTrigger) {
|
|
46402
|
+
const distinct = [...new Set(owners.map((o2) => o2.displayPath))];
|
|
46403
|
+
if (distinct.length > 1) {
|
|
46404
|
+
issues.push({
|
|
46405
|
+
severity: "warning",
|
|
46406
|
+
check: "skill-trigger-collision",
|
|
46407
|
+
ruleId: "skill/trigger-collision",
|
|
46408
|
+
line: 1,
|
|
46409
|
+
message: `Trigger phrase "${trigger}" is declared by ${distinct.length} skills/agents \u2014 only one will win`,
|
|
46410
|
+
detail: distinct.map((d) => ` - ${d}`).join("\n"),
|
|
46411
|
+
suggestion: "Make trigger phrases unique so invocation is deterministic."
|
|
46412
|
+
});
|
|
46413
|
+
}
|
|
46414
|
+
}
|
|
46415
|
+
return issues;
|
|
46416
|
+
}
|
|
46417
|
+
function checkOrphaned(ctx) {
|
|
46418
|
+
return ctx.orphanedSkillDirs.map((o2) => ({
|
|
46419
|
+
severity: "warning",
|
|
46420
|
+
check: "skill-orphaned",
|
|
46421
|
+
ruleId: "skill/orphaned",
|
|
46422
|
+
line: 1,
|
|
46423
|
+
message: `${o2.displayPath}: skill directory has no SKILL.md \u2014 Claude Code has nothing to load`,
|
|
46424
|
+
suggestion: `Add a SKILL.md, or remove the empty ${o2.displayPath} directory.`
|
|
46425
|
+
}));
|
|
46426
|
+
}
|
|
46427
|
+
function checkDeadToolRestrictions(file2, fm) {
|
|
46428
|
+
if (file2.kind !== "agent") return [];
|
|
46429
|
+
const raw = fm.fields["tools"] ?? fm.fields["allowed-tools"] ?? fm.fields["allowedTools"];
|
|
46430
|
+
if (!raw) return [];
|
|
46431
|
+
const issues = [];
|
|
46432
|
+
for (const entry of raw.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
46433
|
+
if (entry.startsWith("mcp__") || entry.includes("*")) continue;
|
|
46434
|
+
const base = entry.replace(/\(.*\)$/, "").trim();
|
|
46435
|
+
if (KNOWN_TOOLS.has(base)) continue;
|
|
46436
|
+
issues.push({
|
|
46437
|
+
severity: "warning",
|
|
46438
|
+
check: "skill-dead-tool-restriction",
|
|
46439
|
+
ruleId: "skill/dead-tool-restriction",
|
|
46440
|
+
line: 1,
|
|
46441
|
+
message: `${file2.displayPath}: tool restriction lists "${entry}" which is not a known Claude Code tool`,
|
|
46442
|
+
suggestion: `Known built-in tools: ${[...KNOWN_TOOLS].join(", ")}. MCP tools use the mcp__server__tool form.`
|
|
46443
|
+
});
|
|
46444
|
+
}
|
|
46445
|
+
return issues;
|
|
46446
|
+
}
|
|
46447
|
+
function checkSkills(ctx, sel) {
|
|
46448
|
+
const issues = [];
|
|
46449
|
+
const parsed = ctx.files.map((file2) => ({ file: file2, fm: parseFrontmatter3(file2.content) }));
|
|
46450
|
+
for (const { file: file2, fm } of parsed) {
|
|
46451
|
+
if (sel.frontmatter) issues.push(...checkMissingFrontmatter(file2, fm));
|
|
46452
|
+
if (sel.brokenRef) issues.push(...checkBrokenRefs(file2, fm.found ? fm.endLine : 0));
|
|
46453
|
+
if (sel.deadToolRestriction) issues.push(...checkDeadToolRestrictions(file2, fm));
|
|
46454
|
+
}
|
|
46455
|
+
if (sel.triggerCollision) issues.push(...checkTriggerCollisions(parsed));
|
|
46456
|
+
if (sel.orphaned) issues.push(...checkOrphaned(ctx));
|
|
46457
|
+
return issues;
|
|
46458
|
+
}
|
|
46459
|
+
var REQUIRED_FIELDS, BODY_PATH, PATH_SKIP, KNOWN_TOOLS;
|
|
46460
|
+
var init_skills = __esm({
|
|
46461
|
+
"src/core/checks/skills.ts"() {
|
|
46462
|
+
"use strict";
|
|
46463
|
+
init_define_WEB_FIRST_SEGMENTS();
|
|
46464
|
+
init_fs();
|
|
46465
|
+
REQUIRED_FIELDS = {
|
|
46466
|
+
skill: ["name", "description"],
|
|
46467
|
+
agent: ["name", "description"]
|
|
46468
|
+
};
|
|
46469
|
+
BODY_PATH = /(?:^|[\s`"'(])((?:\.{1,2}\/)[\w@./-]+|(?:[\w@-]+\/)+[\w.-]+)(?=[\s`"'),;:]|$)/gm;
|
|
46470
|
+
PATH_SKIP = /^(https?:\/\/|mailto:|n\/a|e\.g\.|i\.e\.)/i;
|
|
46471
|
+
KNOWN_TOOLS = /* @__PURE__ */ new Set([
|
|
46472
|
+
"Agent",
|
|
46473
|
+
"Bash",
|
|
46474
|
+
"BashOutput",
|
|
46475
|
+
"Edit",
|
|
46476
|
+
"ExitPlanMode",
|
|
46477
|
+
"Glob",
|
|
46478
|
+
"Grep",
|
|
46479
|
+
"KillShell",
|
|
46480
|
+
"MultiEdit",
|
|
46481
|
+
"NotebookEdit",
|
|
46482
|
+
"Read",
|
|
46483
|
+
"Skill",
|
|
46484
|
+
"SlashCommand",
|
|
46485
|
+
"Task",
|
|
46486
|
+
"TodoWrite",
|
|
46487
|
+
"WebFetch",
|
|
46488
|
+
"WebSearch",
|
|
46489
|
+
"Write"
|
|
46490
|
+
]);
|
|
46491
|
+
}
|
|
46492
|
+
});
|
|
46493
|
+
|
|
45981
46494
|
// src/core/ignore-rules.ts
|
|
45982
46495
|
function compileRules(rules) {
|
|
45983
46496
|
return rules.map((r2) => ({
|
|
@@ -46044,25 +46557,26 @@ var init_ignore_rules = __esm({
|
|
|
46044
46557
|
});
|
|
46045
46558
|
|
|
46046
46559
|
// src/core/types.ts
|
|
46047
|
-
var SESSION_AUDIT_LABEL, SESSION_AUDIT_PATH_MARKER;
|
|
46560
|
+
var SESSION_AUDIT_LABEL, SESSION_AUDIT_PATH_MARKER, SKILL_AUDIT_LABEL;
|
|
46048
46561
|
var init_types3 = __esm({
|
|
46049
46562
|
"src/core/types.ts"() {
|
|
46050
46563
|
"use strict";
|
|
46051
46564
|
init_define_WEB_FIRST_SEGMENTS();
|
|
46052
46565
|
SESSION_AUDIT_LABEL = "~/.claude/ (session audit)";
|
|
46053
46566
|
SESSION_AUDIT_PATH_MARKER = "(session audit)";
|
|
46567
|
+
SKILL_AUDIT_LABEL = "~/.claude/ (skill audit)";
|
|
46054
46568
|
}
|
|
46055
46569
|
});
|
|
46056
46570
|
|
|
46057
46571
|
// src/version.ts
|
|
46058
|
-
import { readFileSync as
|
|
46059
|
-
import { resolve as
|
|
46572
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
46573
|
+
import { resolve as resolve11, dirname as dirname5 } from "node:path";
|
|
46060
46574
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
46061
46575
|
function loadVersion() {
|
|
46062
|
-
if (true) return "0.12.
|
|
46063
|
-
const __dir =
|
|
46064
|
-
const pkgPath =
|
|
46065
|
-
const pkg = JSON.parse(
|
|
46576
|
+
if (true) return "0.12.3";
|
|
46577
|
+
const __dir = dirname5(fileURLToPath2(import.meta.url));
|
|
46578
|
+
const pkgPath = resolve11(__dir, "../package.json");
|
|
46579
|
+
const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
|
|
46066
46580
|
return pkg.version;
|
|
46067
46581
|
}
|
|
46068
46582
|
var VERSION;
|
|
@@ -46084,6 +46598,9 @@ function hasMcphChecks(checks) {
|
|
|
46084
46598
|
function hasSessionChecks(checks) {
|
|
46085
46599
|
return checks.some((c3) => c3.startsWith("session-"));
|
|
46086
46600
|
}
|
|
46601
|
+
function hasSkillChecks(checks) {
|
|
46602
|
+
return checks.some((c3) => c3.startsWith("skill-"));
|
|
46603
|
+
}
|
|
46087
46604
|
function deriveChecksToRun(activeChecks, prefix, enabled, allChecks) {
|
|
46088
46605
|
const filtered = activeChecks.filter((c3) => c3.startsWith(prefix));
|
|
46089
46606
|
if (filtered.length > 0) return filtered;
|
|
@@ -46092,10 +46609,11 @@ function deriveChecksToRun(activeChecks, prefix, enabled, allChecks) {
|
|
|
46092
46609
|
async function runAudit(projectRoot, activeChecks, options = {}) {
|
|
46093
46610
|
const fileResults = [];
|
|
46094
46611
|
const thresholds = resolveTokenThresholds(options.tokenThresholds);
|
|
46095
|
-
const shouldRunContextChecks = !options.mcpOnly && !options.mcphOnly && !options.sessionOnly;
|
|
46612
|
+
const shouldRunContextChecks = !options.mcpOnly && !options.mcphOnly && !options.sessionOnly && !options.skillsOnly;
|
|
46096
46613
|
const shouldRunMcpChecks = options.mcp || options.mcpGlobal || options.mcpOnly || hasMcpChecks(activeChecks);
|
|
46097
46614
|
const shouldRunMcphChecks = options.mcph || options.mcphGlobal || options.mcphOnly || hasMcphChecks(activeChecks);
|
|
46098
46615
|
const shouldRunSessionChecks = options.session || options.sessionOnly || hasSessionChecks(activeChecks);
|
|
46616
|
+
const shouldRunSkillChecks = options.skills || options.skillsOnly || hasSkillChecks(activeChecks);
|
|
46099
46617
|
if (shouldRunContextChecks) {
|
|
46100
46618
|
const discovered = await scanForContextFiles(projectRoot, {
|
|
46101
46619
|
depth: options.depth,
|
|
@@ -46152,6 +46670,13 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
|
|
|
46152
46670
|
if (activeChecks.includes("ci-secrets")) {
|
|
46153
46671
|
crossFileIssues.push(...await checkCiSecrets(parsed, projectRoot));
|
|
46154
46672
|
}
|
|
46673
|
+
if (activeChecks.includes("hook-coverage")) {
|
|
46674
|
+
crossFileIssues.push(
|
|
46675
|
+
...await checkHookCoverage(projectRoot, void 0, {
|
|
46676
|
+
userGlobal: options.hooksGlobal
|
|
46677
|
+
})
|
|
46678
|
+
);
|
|
46679
|
+
}
|
|
46155
46680
|
if (crossFileIssues.length > 0) {
|
|
46156
46681
|
fileResults.push({
|
|
46157
46682
|
path: "(project)",
|
|
@@ -46322,6 +46847,31 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
|
|
|
46322
46847
|
});
|
|
46323
46848
|
}
|
|
46324
46849
|
}
|
|
46850
|
+
if (shouldRunSkillChecks) {
|
|
46851
|
+
const skillChecksToRun = deriveChecksToRun(
|
|
46852
|
+
activeChecks,
|
|
46853
|
+
"skill-",
|
|
46854
|
+
Boolean(options.skills || options.skillsOnly),
|
|
46855
|
+
ALL_SKILL_CHECKS
|
|
46856
|
+
);
|
|
46857
|
+
if (skillChecksToRun.length > 0) {
|
|
46858
|
+
const skillCtx = scanSkillFiles();
|
|
46859
|
+
const skillIssues = checkSkills(skillCtx, {
|
|
46860
|
+
frontmatter: skillChecksToRun.includes("skill-frontmatter"),
|
|
46861
|
+
brokenRef: skillChecksToRun.includes("skill-broken-ref"),
|
|
46862
|
+
triggerCollision: skillChecksToRun.includes("skill-trigger-collision"),
|
|
46863
|
+
orphaned: skillChecksToRun.includes("skill-orphaned"),
|
|
46864
|
+
deadToolRestriction: skillChecksToRun.includes("skill-dead-tool-restriction")
|
|
46865
|
+
});
|
|
46866
|
+
fileResults.push({
|
|
46867
|
+
path: SKILL_AUDIT_LABEL,
|
|
46868
|
+
isSymlink: false,
|
|
46869
|
+
tokens: 0,
|
|
46870
|
+
lines: 0,
|
|
46871
|
+
issues: skillIssues
|
|
46872
|
+
});
|
|
46873
|
+
}
|
|
46874
|
+
}
|
|
46325
46875
|
let ignoreReport;
|
|
46326
46876
|
if (options.ignoreRules && options.ignoreRules.length > 0) {
|
|
46327
46877
|
const flat = [];
|
|
@@ -46383,7 +46933,7 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
|
|
|
46383
46933
|
}
|
|
46384
46934
|
return result;
|
|
46385
46935
|
}
|
|
46386
|
-
var ALL_CHECKS, ALL_MCP_CHECKS, ALL_MCPH_CHECKS, ALL_SESSION_CHECKS;
|
|
46936
|
+
var ALL_CHECKS, ALL_MCP_CHECKS, ALL_MCPH_CHECKS, ALL_SESSION_CHECKS, ALL_SKILL_CHECKS;
|
|
46387
46937
|
var init_audit = __esm({
|
|
46388
46938
|
"src/core/audit.ts"() {
|
|
46389
46939
|
"use strict";
|
|
@@ -46425,6 +46975,9 @@ var init_audit = __esm({
|
|
|
46425
46975
|
init_ci_coverage();
|
|
46426
46976
|
init_ci_secrets();
|
|
46427
46977
|
init_content_secrets();
|
|
46978
|
+
init_hook_coverage();
|
|
46979
|
+
init_skill_scanner();
|
|
46980
|
+
init_skills();
|
|
46428
46981
|
init_ignore_rules();
|
|
46429
46982
|
init_types3();
|
|
46430
46983
|
init_version2();
|
|
@@ -46439,7 +46992,8 @@ var init_audit = __esm({
|
|
|
46439
46992
|
"frontmatter",
|
|
46440
46993
|
"ci-coverage",
|
|
46441
46994
|
"ci-secrets",
|
|
46442
|
-
"content-secrets"
|
|
46995
|
+
"content-secrets",
|
|
46996
|
+
"hook-coverage"
|
|
46443
46997
|
];
|
|
46444
46998
|
ALL_MCP_CHECKS = [
|
|
46445
46999
|
"mcp-schema",
|
|
@@ -46467,6 +47021,13 @@ var init_audit = __esm({
|
|
|
46467
47021
|
"session-loop-detection",
|
|
46468
47022
|
"session-memory-index-overflow"
|
|
46469
47023
|
];
|
|
47024
|
+
ALL_SKILL_CHECKS = [
|
|
47025
|
+
"skill-frontmatter",
|
|
47026
|
+
"skill-broken-ref",
|
|
47027
|
+
"skill-trigger-collision",
|
|
47028
|
+
"skill-orphaned",
|
|
47029
|
+
"skill-dead-tool-restriction"
|
|
47030
|
+
];
|
|
46470
47031
|
}
|
|
46471
47032
|
});
|
|
46472
47033
|
|
|
@@ -46664,7 +47225,7 @@ var init_ansi_styles = __esm({
|
|
|
46664
47225
|
|
|
46665
47226
|
// node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js
|
|
46666
47227
|
import process4 from "node:process";
|
|
46667
|
-
import
|
|
47228
|
+
import os4 from "node:os";
|
|
46668
47229
|
import tty from "node:tty";
|
|
46669
47230
|
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process4.argv) {
|
|
46670
47231
|
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
@@ -46722,7 +47283,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
|
46722
47283
|
return min;
|
|
46723
47284
|
}
|
|
46724
47285
|
if (process4.platform === "win32") {
|
|
46725
|
-
const osRelease =
|
|
47286
|
+
const osRelease = os4.release().split(".");
|
|
46726
47287
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
46727
47288
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
46728
47289
|
}
|
|
@@ -47013,7 +47574,7 @@ var init_source = __esm({
|
|
|
47013
47574
|
});
|
|
47014
47575
|
|
|
47015
47576
|
// src/core/fixer.ts
|
|
47016
|
-
import * as
|
|
47577
|
+
import * as fs9 from "node:fs";
|
|
47017
47578
|
function applyFixes(result, options = {}) {
|
|
47018
47579
|
const log = options.quiet ? () => {
|
|
47019
47580
|
} : console.log.bind(console);
|
|
@@ -47055,7 +47616,7 @@ function applyFixes(result, options = {}) {
|
|
|
47055
47616
|
let totalFixes = 0;
|
|
47056
47617
|
const filesModified = [];
|
|
47057
47618
|
for (const [filePath, fixes] of fixesByFile) {
|
|
47058
|
-
const content =
|
|
47619
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
47059
47620
|
const lines = content.split("\n");
|
|
47060
47621
|
let modified = false;
|
|
47061
47622
|
const fixesByLine = /* @__PURE__ */ new Map();
|
|
@@ -47097,7 +47658,7 @@ function applyFixes(result, options = {}) {
|
|
|
47097
47658
|
}
|
|
47098
47659
|
}
|
|
47099
47660
|
if (!dryRun) {
|
|
47100
|
-
|
|
47661
|
+
fs9.writeFileSync(filePath, newContent, "utf-8");
|
|
47101
47662
|
}
|
|
47102
47663
|
filesModified.push(filePath);
|
|
47103
47664
|
totalFixes += perFileFixCount;
|
|
@@ -47115,8 +47676,8 @@ var init_fixer = __esm({
|
|
|
47115
47676
|
});
|
|
47116
47677
|
|
|
47117
47678
|
// src/core/config.ts
|
|
47118
|
-
import * as
|
|
47119
|
-
import * as
|
|
47679
|
+
import * as fs10 from "node:fs";
|
|
47680
|
+
import * as path14 from "node:path";
|
|
47120
47681
|
function formatJsonError(content, err) {
|
|
47121
47682
|
const msg = err instanceof Error ? err.message : String(err);
|
|
47122
47683
|
const posMatch = msg.match(/position (\d+)/);
|
|
@@ -47214,10 +47775,10 @@ function warnIgnoreRulePathPatternMisuse(config2, source) {
|
|
|
47214
47775
|
}
|
|
47215
47776
|
function loadConfig(projectRoot) {
|
|
47216
47777
|
for (const filename of CONFIG_FILENAMES) {
|
|
47217
|
-
const filePath =
|
|
47778
|
+
const filePath = path14.join(projectRoot, filename);
|
|
47218
47779
|
let content;
|
|
47219
47780
|
try {
|
|
47220
|
-
content = stripBom(
|
|
47781
|
+
content = stripBom(fs10.readFileSync(filePath, "utf-8"));
|
|
47221
47782
|
} catch {
|
|
47222
47783
|
continue;
|
|
47223
47784
|
}
|
|
@@ -47228,7 +47789,7 @@ function loadConfig(projectRoot) {
|
|
|
47228
47789
|
function loadConfigFromExplicitPath(configPath) {
|
|
47229
47790
|
let content;
|
|
47230
47791
|
try {
|
|
47231
|
-
content = stripBom(
|
|
47792
|
+
content = stripBom(fs10.readFileSync(configPath, "utf-8"));
|
|
47232
47793
|
} catch (err) {
|
|
47233
47794
|
const detail = err instanceof Error ? err.message : String(err);
|
|
47234
47795
|
throw new Error(`could not load config from ${configPath}: ${detail}`, { cause: err });
|
|
@@ -47259,7 +47820,10 @@ var init_config2 = __esm({
|
|
|
47259
47820
|
"mcphGlobal",
|
|
47260
47821
|
"mcphStrictEnvToken",
|
|
47261
47822
|
"session",
|
|
47262
|
-
"sessionOnly"
|
|
47823
|
+
"sessionOnly",
|
|
47824
|
+
"skills",
|
|
47825
|
+
"skillsOnly",
|
|
47826
|
+
"hooksGlobal"
|
|
47263
47827
|
];
|
|
47264
47828
|
CONFIG_FILENAMES = [".ctxlintrc", ".ctxlintrc.json"];
|
|
47265
47829
|
}
|
|
@@ -47271,7 +47835,7 @@ __export(server_exports, {
|
|
|
47271
47835
|
server: () => server,
|
|
47272
47836
|
startServer: () => startServer
|
|
47273
47837
|
});
|
|
47274
|
-
import * as
|
|
47838
|
+
import * as path15 from "node:path";
|
|
47275
47839
|
function describeDisallowed(rawPath) {
|
|
47276
47840
|
const m = rawPath.match(PATH_DISALLOWED);
|
|
47277
47841
|
if (!m) return "unknown";
|
|
@@ -47284,7 +47848,7 @@ function validateProjectPath(rawPath) {
|
|
|
47284
47848
|
`projectPath contains disallowed character ${describeDisallowed(rawPath)} (control chars and shell metacharacters are rejected)`
|
|
47285
47849
|
);
|
|
47286
47850
|
}
|
|
47287
|
-
const resolved =
|
|
47851
|
+
const resolved = path15.resolve(rawPath);
|
|
47288
47852
|
if (!isDirectory(resolved)) {
|
|
47289
47853
|
throw new Error("projectPath is not an existing directory");
|
|
47290
47854
|
}
|
|
@@ -47305,10 +47869,10 @@ function validateFilePathInput(rawPath) {
|
|
|
47305
47869
|
}
|
|
47306
47870
|
}
|
|
47307
47871
|
function resolveWithinRoot(filePath, root) {
|
|
47308
|
-
const resolvedRoot =
|
|
47309
|
-
const resolved =
|
|
47310
|
-
const rel =
|
|
47311
|
-
if (rel.startsWith("..") ||
|
|
47872
|
+
const resolvedRoot = path15.resolve(root);
|
|
47873
|
+
const resolved = path15.resolve(resolvedRoot, filePath);
|
|
47874
|
+
const rel = path15.relative(resolvedRoot, resolved);
|
|
47875
|
+
if (rel.startsWith("..") || path15.isAbsolute(rel)) {
|
|
47312
47876
|
throw new Error("path escapes the project root");
|
|
47313
47877
|
}
|
|
47314
47878
|
return resolved;
|
|
@@ -48823,8 +49387,8 @@ var require_command = __commonJS({
|
|
|
48823
49387
|
init_define_WEB_FIRST_SEGMENTS();
|
|
48824
49388
|
var EventEmitter2 = __require("node:events").EventEmitter;
|
|
48825
49389
|
var childProcess = __require("node:child_process");
|
|
48826
|
-
var
|
|
48827
|
-
var
|
|
49390
|
+
var path17 = __require("node:path");
|
|
49391
|
+
var fs12 = __require("node:fs");
|
|
48828
49392
|
var process11 = __require("node:process");
|
|
48829
49393
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
48830
49394
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -49818,7 +50382,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
49818
50382
|
* @param {string} subcommandName
|
|
49819
50383
|
*/
|
|
49820
50384
|
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
49821
|
-
if (
|
|
50385
|
+
if (fs12.existsSync(executableFile)) return;
|
|
49822
50386
|
const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
|
|
49823
50387
|
const executableMissing = `'${executableFile}' does not exist
|
|
49824
50388
|
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
@@ -49836,11 +50400,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
49836
50400
|
let launchWithNode = false;
|
|
49837
50401
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
49838
50402
|
function findFile(baseDir, baseName) {
|
|
49839
|
-
const localBin =
|
|
49840
|
-
if (
|
|
49841
|
-
if (sourceExt.includes(
|
|
50403
|
+
const localBin = path17.resolve(baseDir, baseName);
|
|
50404
|
+
if (fs12.existsSync(localBin)) return localBin;
|
|
50405
|
+
if (sourceExt.includes(path17.extname(baseName))) return void 0;
|
|
49842
50406
|
const foundExt = sourceExt.find(
|
|
49843
|
-
(ext) =>
|
|
50407
|
+
(ext) => fs12.existsSync(`${localBin}${ext}`)
|
|
49844
50408
|
);
|
|
49845
50409
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
49846
50410
|
return void 0;
|
|
@@ -49852,21 +50416,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
49852
50416
|
if (this._scriptPath) {
|
|
49853
50417
|
let resolvedScriptPath;
|
|
49854
50418
|
try {
|
|
49855
|
-
resolvedScriptPath =
|
|
50419
|
+
resolvedScriptPath = fs12.realpathSync(this._scriptPath);
|
|
49856
50420
|
} catch {
|
|
49857
50421
|
resolvedScriptPath = this._scriptPath;
|
|
49858
50422
|
}
|
|
49859
|
-
executableDir =
|
|
49860
|
-
|
|
50423
|
+
executableDir = path17.resolve(
|
|
50424
|
+
path17.dirname(resolvedScriptPath),
|
|
49861
50425
|
executableDir
|
|
49862
50426
|
);
|
|
49863
50427
|
}
|
|
49864
50428
|
if (executableDir) {
|
|
49865
50429
|
let localFile = findFile(executableDir, executableFile);
|
|
49866
50430
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
49867
|
-
const legacyName =
|
|
50431
|
+
const legacyName = path17.basename(
|
|
49868
50432
|
this._scriptPath,
|
|
49869
|
-
|
|
50433
|
+
path17.extname(this._scriptPath)
|
|
49870
50434
|
);
|
|
49871
50435
|
if (legacyName !== this._name) {
|
|
49872
50436
|
localFile = findFile(
|
|
@@ -49877,7 +50441,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
49877
50441
|
}
|
|
49878
50442
|
executableFile = localFile || executableFile;
|
|
49879
50443
|
}
|
|
49880
|
-
launchWithNode = sourceExt.includes(
|
|
50444
|
+
launchWithNode = sourceExt.includes(path17.extname(executableFile));
|
|
49881
50445
|
let proc;
|
|
49882
50446
|
if (process11.platform !== "win32") {
|
|
49883
50447
|
if (launchWithNode) {
|
|
@@ -50792,7 +51356,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
50792
51356
|
* @return {Command}
|
|
50793
51357
|
*/
|
|
50794
51358
|
nameFromFilename(filename) {
|
|
50795
|
-
this._name =
|
|
51359
|
+
this._name = path17.basename(filename, path17.extname(filename));
|
|
50796
51360
|
return this;
|
|
50797
51361
|
}
|
|
50798
51362
|
/**
|
|
@@ -50806,9 +51370,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
50806
51370
|
* @param {string} [path]
|
|
50807
51371
|
* @return {(string|null|Command)}
|
|
50808
51372
|
*/
|
|
50809
|
-
executableDir(
|
|
50810
|
-
if (
|
|
50811
|
-
this._executableDir =
|
|
51373
|
+
executableDir(path18) {
|
|
51374
|
+
if (path18 === void 0) return this._executableDir;
|
|
51375
|
+
this._executableDir = path18;
|
|
50812
51376
|
return this;
|
|
50813
51377
|
}
|
|
50814
51378
|
/**
|
|
@@ -54161,8 +54725,10 @@ function classifyFile(f) {
|
|
|
54161
54725
|
if (f.path === "(project)") return "context";
|
|
54162
54726
|
if (f.path === "(mcp)") return "mcp";
|
|
54163
54727
|
if (f.path.includes(SESSION_AUDIT_PATH_MARKER)) return "session";
|
|
54728
|
+
if (f.path.includes(SKILL_AUDIT_PATH_MARKER)) return "skill";
|
|
54164
54729
|
for (const issue2 of f.issues) {
|
|
54165
54730
|
if (issue2.check.startsWith("session-")) return "session";
|
|
54731
|
+
if (issue2.check.startsWith("skill-")) return "skill";
|
|
54166
54732
|
if (issue2.check.startsWith("mcph-")) return "mcph";
|
|
54167
54733
|
if (issue2.check.startsWith("mcp-")) return "mcp";
|
|
54168
54734
|
}
|
|
@@ -54174,7 +54740,7 @@ function classifyFile(f) {
|
|
|
54174
54740
|
return "context";
|
|
54175
54741
|
}
|
|
54176
54742
|
function isSyntheticBucket(p2) {
|
|
54177
|
-
return p2.startsWith("(") || p2.includes(SESSION_AUDIT_PATH_MARKER);
|
|
54743
|
+
return p2.startsWith("(") || p2.includes(SESSION_AUDIT_PATH_MARKER) || p2.includes(SKILL_AUDIT_PATH_MARKER);
|
|
54178
54744
|
}
|
|
54179
54745
|
function formatText(result, verbose = false) {
|
|
54180
54746
|
const lines = [];
|
|
@@ -54187,7 +54753,8 @@ function formatText(result, verbose = false) {
|
|
|
54187
54753
|
context: [],
|
|
54188
54754
|
mcp: [],
|
|
54189
54755
|
mcph: [],
|
|
54190
|
-
session: []
|
|
54756
|
+
session: [],
|
|
54757
|
+
skill: []
|
|
54191
54758
|
};
|
|
54192
54759
|
for (const f of result.files) {
|
|
54193
54760
|
groups[classifyFile(f)].push(f);
|
|
@@ -54464,6 +55031,11 @@ function buildRuleDescriptors() {
|
|
|
54464
55031
|
shortDescription: { text: "Inline-pasted secret detected in a context file" },
|
|
54465
55032
|
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
54466
55033
|
},
|
|
55034
|
+
{
|
|
55035
|
+
id: "ctxlint/hook-coverage",
|
|
55036
|
+
shortDescription: { text: "Hook/permission entry references a script that no longer exists" },
|
|
55037
|
+
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
55038
|
+
},
|
|
54467
55039
|
{
|
|
54468
55040
|
id: "ctxlint/mcp-schema",
|
|
54469
55041
|
shortDescription: { text: "MCP config structural validation error" },
|
|
@@ -54565,6 +55137,31 @@ function buildRuleDescriptors() {
|
|
|
54565
55137
|
text: "MEMORY.md exceeds Claude Code's 200-line / 25KB session-load cap"
|
|
54566
55138
|
},
|
|
54567
55139
|
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
55140
|
+
},
|
|
55141
|
+
{
|
|
55142
|
+
id: "ctxlint/skill-frontmatter",
|
|
55143
|
+
shortDescription: { text: "Skill/agent definition missing required frontmatter" },
|
|
55144
|
+
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
55145
|
+
},
|
|
55146
|
+
{
|
|
55147
|
+
id: "ctxlint/skill-broken-ref",
|
|
55148
|
+
shortDescription: { text: "Skill/agent body references a path that does not exist" },
|
|
55149
|
+
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
55150
|
+
},
|
|
55151
|
+
{
|
|
55152
|
+
id: "ctxlint/skill-trigger-collision",
|
|
55153
|
+
shortDescription: { text: "Two skills/agents declare the same trigger phrase" },
|
|
55154
|
+
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
55155
|
+
},
|
|
55156
|
+
{
|
|
55157
|
+
id: "ctxlint/skill-orphaned",
|
|
55158
|
+
shortDescription: { text: "Skill directory has no SKILL.md" },
|
|
55159
|
+
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
55160
|
+
},
|
|
55161
|
+
{
|
|
55162
|
+
id: "ctxlint/skill-dead-tool-restriction",
|
|
55163
|
+
shortDescription: { text: "Agent tool restriction names an unknown Claude Code tool" },
|
|
55164
|
+
helpUri: "https://github.com/yawlabs/ctxlint#what-it-checks"
|
|
54568
55165
|
}
|
|
54569
55166
|
];
|
|
54570
55167
|
}
|
|
@@ -54582,25 +55179,28 @@ function formatIssue(issue2) {
|
|
|
54582
55179
|
}
|
|
54583
55180
|
return line;
|
|
54584
55181
|
}
|
|
54585
|
-
var GROUP_ORDER, GROUP_LABELS, GROUP_SUMMARY_NOUNS;
|
|
55182
|
+
var SKILL_AUDIT_PATH_MARKER, GROUP_ORDER, GROUP_LABELS, GROUP_SUMMARY_NOUNS;
|
|
54586
55183
|
var init_reporter = __esm({
|
|
54587
55184
|
"src/core/reporter.ts"() {
|
|
54588
55185
|
"use strict";
|
|
54589
55186
|
init_define_WEB_FIRST_SEGMENTS();
|
|
54590
55187
|
init_source();
|
|
54591
55188
|
init_types3();
|
|
54592
|
-
|
|
55189
|
+
SKILL_AUDIT_PATH_MARKER = "(skill audit)";
|
|
55190
|
+
GROUP_ORDER = ["context", "mcp", "mcph", "session", "skill"];
|
|
54593
55191
|
GROUP_LABELS = {
|
|
54594
55192
|
context: "Context Files",
|
|
54595
55193
|
mcp: "MCP Configs",
|
|
54596
55194
|
mcph: "mcph Configs",
|
|
54597
|
-
session: "Session Audit"
|
|
55195
|
+
session: "Session Audit",
|
|
55196
|
+
skill: "Skill Audit"
|
|
54598
55197
|
};
|
|
54599
55198
|
GROUP_SUMMARY_NOUNS = {
|
|
54600
55199
|
context: "context file",
|
|
54601
55200
|
mcp: "MCP config",
|
|
54602
55201
|
mcph: "mcph config",
|
|
54603
|
-
session: "session audit"
|
|
55202
|
+
session: "session audit",
|
|
55203
|
+
skill: "skill audit"
|
|
54604
55204
|
};
|
|
54605
55205
|
}
|
|
54606
55206
|
});
|
|
@@ -54610,8 +55210,8 @@ var cli_exports = {};
|
|
|
54610
55210
|
__export(cli_exports, {
|
|
54611
55211
|
runCli: () => runCli
|
|
54612
55212
|
});
|
|
54613
|
-
import * as
|
|
54614
|
-
import * as
|
|
55213
|
+
import * as fs11 from "node:fs";
|
|
55214
|
+
import * as path16 from "node:path";
|
|
54615
55215
|
function validateCheckNames(names, source) {
|
|
54616
55216
|
const invalid = names.filter((n7) => n7 && !VALID_CHECKS.has(n7));
|
|
54617
55217
|
if (invalid.length > 0) {
|
|
@@ -54633,8 +55233,12 @@ async function runCli() {
|
|
|
54633
55233
|
"--mcph-strict-env-token",
|
|
54634
55234
|
"Upgrade mcph-config/prefer-env-token from warning to error (strict env-var-only posture)",
|
|
54635
55235
|
false
|
|
54636
|
-
).option("--session", "Run session audit checks (cross-project consistency)", false).option("--session-only", "Run only session checks, skip context and MCP checks", false).option("--
|
|
54637
|
-
|
|
55236
|
+
).option("--session", "Run session audit checks (cross-project consistency)", false).option("--session-only", "Run only session checks, skip context and MCP checks", false).option("--skills", "Run agent-skill checks (~/.claude/skills + ~/.claude/agents)", false).option("--skills-only", "Run only agent-skill checks, skip everything else", false).option(
|
|
55237
|
+
"--hooks-global",
|
|
55238
|
+
"Also scan the user-global ~/.claude/settings.json in the dead-hook check (off by default; the standard run only reads project .claude/settings.json[.local])",
|
|
55239
|
+
false
|
|
55240
|
+
).option("--watch", "Re-lint on context file changes", false).action(async (projectPath, opts) => {
|
|
55241
|
+
const resolvedPath = path16.resolve(projectPath);
|
|
54638
55242
|
const { config: config2, options, activeChecks } = resolveSession(resolvedPath, opts);
|
|
54639
55243
|
const spinner = options.format === "text" && !options.quiet ? ora("Scanning for context files...").start() : void 0;
|
|
54640
55244
|
try {
|
|
@@ -54651,6 +55255,9 @@ async function runCli() {
|
|
|
54651
55255
|
mcphStrictEnvToken: options.mcphStrictEnvToken,
|
|
54652
55256
|
session: options.session,
|
|
54653
55257
|
sessionOnly: options.sessionOnly,
|
|
55258
|
+
skills: options.skills,
|
|
55259
|
+
skillsOnly: options.skillsOnly,
|
|
55260
|
+
hooksGlobal: options.hooksGlobal,
|
|
54654
55261
|
tokenThresholds: config2?.tokenThresholds,
|
|
54655
55262
|
ignoreRules: config2?.ignoreRules
|
|
54656
55263
|
});
|
|
@@ -54750,43 +55357,43 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
|
|
|
54750
55357
|
const chalk2 = (await Promise.resolve().then(() => (init_source(), source_exports))).default;
|
|
54751
55358
|
console.log(chalk2.dim("\nWatching for changes... (Ctrl+C to stop)\n"));
|
|
54752
55359
|
const watchPaths = [
|
|
54753
|
-
|
|
54754
|
-
|
|
54755
|
-
|
|
54756
|
-
|
|
54757
|
-
|
|
54758
|
-
|
|
54759
|
-
|
|
54760
|
-
|
|
54761
|
-
|
|
54762
|
-
|
|
54763
|
-
|
|
54764
|
-
|
|
54765
|
-
|
|
54766
|
-
|
|
54767
|
-
|
|
55360
|
+
path16.join(resolvedPath, "CLAUDE.md"),
|
|
55361
|
+
path16.join(resolvedPath, "CLAUDE.local.md"),
|
|
55362
|
+
path16.join(resolvedPath, "AGENTS.md"),
|
|
55363
|
+
path16.join(resolvedPath, "AGENT.md"),
|
|
55364
|
+
path16.join(resolvedPath, ".cursorrules"),
|
|
55365
|
+
path16.join(resolvedPath, ".windsurfrules"),
|
|
55366
|
+
path16.join(resolvedPath, ".clinerules"),
|
|
55367
|
+
path16.join(resolvedPath, ".aiderules"),
|
|
55368
|
+
path16.join(resolvedPath, ".continuerules"),
|
|
55369
|
+
path16.join(resolvedPath, ".rules"),
|
|
55370
|
+
path16.join(resolvedPath, ".goosehints"),
|
|
55371
|
+
path16.join(resolvedPath, "GEMINI.md"),
|
|
55372
|
+
path16.join(resolvedPath, "replit.md"),
|
|
55373
|
+
path16.join(resolvedPath, "package.json"),
|
|
55374
|
+
path16.join(resolvedPath, ".mcp.json"),
|
|
54768
55375
|
// mcph configs — without these, edits to the .mcph.json the user is
|
|
54769
55376
|
// actively iterating on don't trigger a re-lint.
|
|
54770
|
-
|
|
54771
|
-
|
|
55377
|
+
path16.join(resolvedPath, ".mcph.json"),
|
|
55378
|
+
path16.join(resolvedPath, ".mcph.local.json"),
|
|
54772
55379
|
// ctxlint config — re-resolve on edit so threshold changes,
|
|
54773
55380
|
// ignore-list edits, and check-list overrides take effect without
|
|
54774
55381
|
// restarting the watcher.
|
|
54775
|
-
|
|
54776
|
-
|
|
55382
|
+
path16.join(resolvedPath, ".ctxlintrc"),
|
|
55383
|
+
path16.join(resolvedPath, ".ctxlintrc.json")
|
|
54777
55384
|
];
|
|
54778
55385
|
const watchDirs = [
|
|
54779
|
-
|
|
54780
|
-
|
|
54781
|
-
|
|
54782
|
-
|
|
54783
|
-
|
|
54784
|
-
|
|
54785
|
-
|
|
54786
|
-
|
|
54787
|
-
|
|
54788
|
-
|
|
54789
|
-
|
|
55386
|
+
path16.join(resolvedPath, ".claude"),
|
|
55387
|
+
path16.join(resolvedPath, ".cursor"),
|
|
55388
|
+
path16.join(resolvedPath, ".github"),
|
|
55389
|
+
path16.join(resolvedPath, ".windsurf"),
|
|
55390
|
+
path16.join(resolvedPath, ".aide"),
|
|
55391
|
+
path16.join(resolvedPath, ".amazonq"),
|
|
55392
|
+
path16.join(resolvedPath, ".goose"),
|
|
55393
|
+
path16.join(resolvedPath, ".junie"),
|
|
55394
|
+
path16.join(resolvedPath, ".aiassistant"),
|
|
55395
|
+
path16.join(resolvedPath, ".continue"),
|
|
55396
|
+
path16.join(resolvedPath, ".vscode")
|
|
54790
55397
|
];
|
|
54791
55398
|
const watchers = [];
|
|
54792
55399
|
let debounceTimer = null;
|
|
@@ -54818,6 +55425,9 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
|
|
|
54818
55425
|
mcphStrictEnvToken: liveOptions.mcphStrictEnvToken,
|
|
54819
55426
|
session: liveOptions.session,
|
|
54820
55427
|
sessionOnly: liveOptions.sessionOnly,
|
|
55428
|
+
skills: liveOptions.skills,
|
|
55429
|
+
skillsOnly: liveOptions.skillsOnly,
|
|
55430
|
+
hooksGlobal: liveOptions.hooksGlobal,
|
|
54821
55431
|
tokenThresholds: liveConfig?.tokenThresholds,
|
|
54822
55432
|
ignoreRules: liveConfig?.ignoreRules
|
|
54823
55433
|
});
|
|
@@ -54845,13 +55455,13 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
|
|
|
54845
55455
|
};
|
|
54846
55456
|
for (const filePath of watchPaths) {
|
|
54847
55457
|
try {
|
|
54848
|
-
watchers.push(
|
|
55458
|
+
watchers.push(fs11.watch(filePath, rerun));
|
|
54849
55459
|
} catch {
|
|
54850
55460
|
}
|
|
54851
55461
|
}
|
|
54852
55462
|
for (const dir of watchDirs) {
|
|
54853
55463
|
try {
|
|
54854
|
-
watchers.push(
|
|
55464
|
+
watchers.push(fs11.watch(dir, { recursive: true }, rerun));
|
|
54855
55465
|
} catch {
|
|
54856
55466
|
}
|
|
54857
55467
|
}
|
|
@@ -54872,17 +55482,17 @@ Fixed ${applied.totalFixes} issue${applied.totalFixes !== 1 ? "s" : ""} in ${app
|
|
|
54872
55482
|
}
|
|
54873
55483
|
});
|
|
54874
55484
|
program2.command("init").description("Set up a git pre-commit hook that runs ctxlint --strict").argument("[path]", "Project directory", ".").action(async (projectPath) => {
|
|
54875
|
-
const resolvedRoot =
|
|
54876
|
-
const gitDir =
|
|
54877
|
-
const hooksDir =
|
|
54878
|
-
if (!
|
|
55485
|
+
const resolvedRoot = path16.resolve(projectPath);
|
|
55486
|
+
const gitDir = path16.join(resolvedRoot, ".git");
|
|
55487
|
+
const hooksDir = path16.join(gitDir, "hooks");
|
|
55488
|
+
if (!fs11.existsSync(gitDir)) {
|
|
54879
55489
|
console.error('Error: not a git repository. Run "git init" first.');
|
|
54880
55490
|
process.exit(1);
|
|
54881
55491
|
}
|
|
54882
|
-
if (!
|
|
54883
|
-
|
|
55492
|
+
if (!fs11.existsSync(hooksDir)) {
|
|
55493
|
+
fs11.mkdirSync(hooksDir, { recursive: true });
|
|
54884
55494
|
}
|
|
54885
|
-
const hookPath =
|
|
55495
|
+
const hookPath = path16.join(hooksDir, "pre-commit");
|
|
54886
55496
|
const fullHookContent = `#!/bin/sh
|
|
54887
55497
|
# ctxlint pre-commit hook
|
|
54888
55498
|
npx @yawlabs/ctxlint@${VERSION} --strict
|
|
@@ -54891,16 +55501,16 @@ npx @yawlabs/ctxlint@${VERSION} --strict
|
|
|
54891
55501
|
# ctxlint pre-commit hook
|
|
54892
55502
|
npx @yawlabs/ctxlint@${VERSION} --strict
|
|
54893
55503
|
`;
|
|
54894
|
-
if (
|
|
54895
|
-
const existing =
|
|
55504
|
+
if (fs11.existsSync(hookPath)) {
|
|
55505
|
+
const existing = fs11.readFileSync(hookPath, "utf-8");
|
|
54896
55506
|
if (existing.includes("ctxlint")) {
|
|
54897
55507
|
console.log("Pre-commit hook already includes ctxlint.");
|
|
54898
55508
|
return;
|
|
54899
55509
|
}
|
|
54900
|
-
|
|
55510
|
+
fs11.appendFileSync(hookPath, appendHookContent);
|
|
54901
55511
|
console.log("Added ctxlint to existing pre-commit hook.");
|
|
54902
55512
|
} else {
|
|
54903
|
-
|
|
55513
|
+
fs11.writeFileSync(hookPath, fullHookContent, { mode: 493 });
|
|
54904
55514
|
console.log("Created pre-commit hook at .git/hooks/pre-commit");
|
|
54905
55515
|
}
|
|
54906
55516
|
console.log("ctxlint will now run automatically before each commit.");
|
|
@@ -54918,7 +55528,7 @@ async function promptYesNo(question) {
|
|
|
54918
55528
|
}
|
|
54919
55529
|
}
|
|
54920
55530
|
function resolveSession(resolvedPath, opts) {
|
|
54921
|
-
const configPath = opts.config ?
|
|
55531
|
+
const configPath = opts.config ? path16.resolve(opts.config) : void 0;
|
|
54922
55532
|
const config2 = configPath ? loadConfigFromPath(configPath) : loadConfig(resolvedPath);
|
|
54923
55533
|
const mcpGlobal = opts.mcpGlobal || config2?.mcpGlobal || false;
|
|
54924
55534
|
const mcpOnly = opts.mcpOnly || config2?.mcpOnly || false;
|
|
@@ -54929,6 +55539,9 @@ function resolveSession(resolvedPath, opts) {
|
|
|
54929
55539
|
const mcphStrictEnvToken = opts.mcphStrictEnvToken || config2?.mcphStrictEnvToken || false;
|
|
54930
55540
|
const sessionOnly = opts.sessionOnly || config2?.sessionOnly || false;
|
|
54931
55541
|
const sessionFlag = opts.session || sessionOnly || config2?.session || false;
|
|
55542
|
+
const skillsOnly = opts.skillsOnly || config2?.skillsOnly || false;
|
|
55543
|
+
const skillsFlag = opts.skills || skillsOnly || config2?.skills || false;
|
|
55544
|
+
const hooksGlobal = opts.hooksGlobal || config2?.hooksGlobal || false;
|
|
54932
55545
|
let explicitChecks = opts.checks ? validateCheckNames(
|
|
54933
55546
|
opts.checks.split(",").map((c3) => c3.trim()),
|
|
54934
55547
|
"--checks"
|
|
@@ -54937,14 +55550,18 @@ function resolveSession(resolvedPath, opts) {
|
|
|
54937
55550
|
const hasMcpInChecks = explicitChecks?.some((c3) => c3.startsWith("mcp-") && !c3.startsWith("mcph-")) || false;
|
|
54938
55551
|
const hasMcphInChecks = explicitChecks?.some((c3) => c3.startsWith("mcph-")) || false;
|
|
54939
55552
|
const hasSessionInChecks = explicitChecks?.some((c3) => c3.startsWith("session-")) || false;
|
|
55553
|
+
const hasSkillInChecks = explicitChecks?.some((c3) => c3.startsWith("skill-")) || false;
|
|
54940
55554
|
const effectiveMcp = mcpFlag || hasMcpInChecks;
|
|
54941
55555
|
const effectiveMcph = mcphFlag || hasMcphInChecks;
|
|
54942
55556
|
const effectiveSession = sessionFlag || sessionOnly || hasSessionInChecks;
|
|
55557
|
+
const effectiveSkills = skillsFlag || skillsOnly || hasSkillInChecks;
|
|
54943
55558
|
let checks;
|
|
54944
55559
|
if (explicitChecks) {
|
|
54945
55560
|
checks = explicitChecks;
|
|
54946
55561
|
} else if (sessionOnly) {
|
|
54947
55562
|
checks = ALL_SESSION_CHECKS;
|
|
55563
|
+
} else if (skillsOnly) {
|
|
55564
|
+
checks = ALL_SKILL_CHECKS;
|
|
54948
55565
|
} else if (mcpOnly) {
|
|
54949
55566
|
checks = ALL_MCP_CHECKS;
|
|
54950
55567
|
} else if (mcphOnly) {
|
|
@@ -54955,7 +55572,8 @@ function resolveSession(resolvedPath, opts) {
|
|
|
54955
55572
|
...base,
|
|
54956
55573
|
...effectiveMcp ? ALL_MCP_CHECKS : [],
|
|
54957
55574
|
...effectiveMcph ? ALL_MCPH_CHECKS : [],
|
|
54958
|
-
...effectiveSession ? ALL_SESSION_CHECKS : []
|
|
55575
|
+
...effectiveSession ? ALL_SESSION_CHECKS : [],
|
|
55576
|
+
...effectiveSkills ? ALL_SKILL_CHECKS : []
|
|
54959
55577
|
];
|
|
54960
55578
|
}
|
|
54961
55579
|
const options = {
|
|
@@ -54980,7 +55598,10 @@ function resolveSession(resolvedPath, opts) {
|
|
|
54980
55598
|
mcphGlobal,
|
|
54981
55599
|
mcphStrictEnvToken,
|
|
54982
55600
|
session: effectiveSession,
|
|
54983
|
-
sessionOnly
|
|
55601
|
+
sessionOnly,
|
|
55602
|
+
skills: effectiveSkills,
|
|
55603
|
+
skillsOnly,
|
|
55604
|
+
hooksGlobal
|
|
54984
55605
|
};
|
|
54985
55606
|
const activeChecks = options.checks.filter((c3) => !options.ignore.includes(c3));
|
|
54986
55607
|
return { config: config2, options, activeChecks };
|
|
@@ -55014,7 +55635,8 @@ var init_cli = __esm({
|
|
|
55014
55635
|
...ALL_CHECKS,
|
|
55015
55636
|
...ALL_MCP_CHECKS,
|
|
55016
55637
|
...ALL_MCPH_CHECKS,
|
|
55017
|
-
...ALL_SESSION_CHECKS
|
|
55638
|
+
...ALL_SESSION_CHECKS,
|
|
55639
|
+
...ALL_SKILL_CHECKS
|
|
55018
55640
|
]);
|
|
55019
55641
|
}
|
|
55020
55642
|
});
|