@chimix/agentscan 0.4.0 → 0.6.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/README.md +105 -53
- package/dist/cli.js +1081 -782
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -18,7 +18,7 @@ var __export = (target, all) => {
|
|
|
18
18
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
19
|
|
|
20
20
|
// node_modules/yaml/dist/nodes/identity.js
|
|
21
|
-
var require_identity = __commonJS((exports)
|
|
21
|
+
var require_identity = __commonJS(function(exports) {
|
|
22
22
|
var ALIAS = Symbol.for("yaml.alias");
|
|
23
23
|
var DOC = Symbol.for("yaml.document");
|
|
24
24
|
var MAP = Symbol.for("yaml.map");
|
|
@@ -72,7 +72,7 @@ var require_identity = __commonJS((exports) => {
|
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
// node_modules/yaml/dist/visit.js
|
|
75
|
-
var require_visit = __commonJS((exports)
|
|
75
|
+
var require_visit = __commonJS(function(exports) {
|
|
76
76
|
var identity = require_identity();
|
|
77
77
|
var BREAK = Symbol("break visit");
|
|
78
78
|
var SKIP = Symbol("skip children");
|
|
@@ -227,7 +227,7 @@ var require_visit = __commonJS((exports) => {
|
|
|
227
227
|
});
|
|
228
228
|
|
|
229
229
|
// node_modules/yaml/dist/doc/directives.js
|
|
230
|
-
var require_directives = __commonJS((exports)
|
|
230
|
+
var require_directives = __commonJS(function(exports) {
|
|
231
231
|
var identity = require_identity();
|
|
232
232
|
var visit = require_visit();
|
|
233
233
|
var escapeChars = {
|
|
@@ -379,7 +379,7 @@ var require_directives = __commonJS((exports) => {
|
|
|
379
379
|
});
|
|
380
380
|
|
|
381
381
|
// node_modules/yaml/dist/doc/anchors.js
|
|
382
|
-
var require_anchors = __commonJS((exports)
|
|
382
|
+
var require_anchors = __commonJS(function(exports) {
|
|
383
383
|
var identity = require_identity();
|
|
384
384
|
var visit = require_visit();
|
|
385
385
|
function anchorIsValid(anchor) {
|
|
@@ -441,7 +441,7 @@ var require_anchors = __commonJS((exports) => {
|
|
|
441
441
|
});
|
|
442
442
|
|
|
443
443
|
// node_modules/yaml/dist/doc/applyReviver.js
|
|
444
|
-
var require_applyReviver = __commonJS((exports)
|
|
444
|
+
var require_applyReviver = __commonJS(function(exports) {
|
|
445
445
|
function applyReviver(reviver, obj, key, val) {
|
|
446
446
|
if (val && typeof val === "object") {
|
|
447
447
|
if (Array.isArray(val)) {
|
|
@@ -488,7 +488,7 @@ var require_applyReviver = __commonJS((exports) => {
|
|
|
488
488
|
});
|
|
489
489
|
|
|
490
490
|
// node_modules/yaml/dist/nodes/toJS.js
|
|
491
|
-
var require_toJS = __commonJS((exports)
|
|
491
|
+
var require_toJS = __commonJS(function(exports) {
|
|
492
492
|
var identity = require_identity();
|
|
493
493
|
function toJS(value, arg, ctx) {
|
|
494
494
|
if (Array.isArray(value))
|
|
@@ -515,7 +515,7 @@ var require_toJS = __commonJS((exports) => {
|
|
|
515
515
|
});
|
|
516
516
|
|
|
517
517
|
// node_modules/yaml/dist/nodes/Node.js
|
|
518
|
-
var require_Node = __commonJS((exports)
|
|
518
|
+
var require_Node = __commonJS(function(exports) {
|
|
519
519
|
var applyReviver = require_applyReviver();
|
|
520
520
|
var identity = require_identity();
|
|
521
521
|
var toJS = require_toJS();
|
|
@@ -552,7 +552,7 @@ var require_Node = __commonJS((exports) => {
|
|
|
552
552
|
});
|
|
553
553
|
|
|
554
554
|
// node_modules/yaml/dist/nodes/Alias.js
|
|
555
|
-
var require_Alias = __commonJS((exports)
|
|
555
|
+
var require_Alias = __commonJS(function(exports) {
|
|
556
556
|
var anchors = require_anchors();
|
|
557
557
|
var visit = require_visit();
|
|
558
558
|
var identity = require_identity();
|
|
@@ -662,7 +662,7 @@ var require_Alias = __commonJS((exports) => {
|
|
|
662
662
|
});
|
|
663
663
|
|
|
664
664
|
// node_modules/yaml/dist/nodes/Scalar.js
|
|
665
|
-
var require_Scalar = __commonJS((exports)
|
|
665
|
+
var require_Scalar = __commonJS(function(exports) {
|
|
666
666
|
var identity = require_identity();
|
|
667
667
|
var Node = require_Node();
|
|
668
668
|
var toJS = require_toJS();
|
|
@@ -690,7 +690,7 @@ var require_Scalar = __commonJS((exports) => {
|
|
|
690
690
|
});
|
|
691
691
|
|
|
692
692
|
// node_modules/yaml/dist/doc/createNode.js
|
|
693
|
-
var require_createNode = __commonJS((exports)
|
|
693
|
+
var require_createNode = __commonJS(function(exports) {
|
|
694
694
|
var Alias = require_Alias();
|
|
695
695
|
var identity = require_identity();
|
|
696
696
|
var Scalar = require_Scalar();
|
|
@@ -762,7 +762,7 @@ var require_createNode = __commonJS((exports) => {
|
|
|
762
762
|
});
|
|
763
763
|
|
|
764
764
|
// node_modules/yaml/dist/nodes/Collection.js
|
|
765
|
-
var require_Collection = __commonJS((exports)
|
|
765
|
+
var require_Collection = __commonJS(function(exports) {
|
|
766
766
|
var createNode = require_createNode();
|
|
767
767
|
var identity = require_identity();
|
|
768
768
|
var Node = require_Node();
|
|
@@ -877,7 +877,7 @@ var require_Collection = __commonJS((exports) => {
|
|
|
877
877
|
});
|
|
878
878
|
|
|
879
879
|
// node_modules/yaml/dist/stringify/stringifyComment.js
|
|
880
|
-
var require_stringifyComment = __commonJS((exports)
|
|
880
|
+
var require_stringifyComment = __commonJS(function(exports) {
|
|
881
881
|
var stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, "#");
|
|
882
882
|
function indentComment(comment, indent) {
|
|
883
883
|
if (/^\n+$/.test(comment))
|
|
@@ -894,7 +894,7 @@ var require_stringifyComment = __commonJS((exports) => {
|
|
|
894
894
|
});
|
|
895
895
|
|
|
896
896
|
// node_modules/yaml/dist/stringify/foldFlowLines.js
|
|
897
|
-
var require_foldFlowLines = __commonJS((exports)
|
|
897
|
+
var require_foldFlowLines = __commonJS(function(exports) {
|
|
898
898
|
var FOLD_FLOW = "flow";
|
|
899
899
|
var FOLD_BLOCK = "block";
|
|
900
900
|
var FOLD_QUOTED = "quoted";
|
|
@@ -1031,7 +1031,7 @@ ${indent}${text.slice(fold + 1, end2)}`;
|
|
|
1031
1031
|
});
|
|
1032
1032
|
|
|
1033
1033
|
// node_modules/yaml/dist/stringify/stringifyString.js
|
|
1034
|
-
var require_stringifyString = __commonJS((exports)
|
|
1034
|
+
var require_stringifyString = __commonJS(function(exports) {
|
|
1035
1035
|
var Scalar = require_Scalar();
|
|
1036
1036
|
var foldFlowLines = require_foldFlowLines();
|
|
1037
1037
|
var getFoldOptions = (ctx, isBlock) => ({
|
|
@@ -1329,7 +1329,7 @@ ${indent}`);
|
|
|
1329
1329
|
});
|
|
1330
1330
|
|
|
1331
1331
|
// node_modules/yaml/dist/stringify/stringify.js
|
|
1332
|
-
var require_stringify = __commonJS((exports)
|
|
1332
|
+
var require_stringify = __commonJS(function(exports) {
|
|
1333
1333
|
var anchors = require_anchors();
|
|
1334
1334
|
var identity = require_identity();
|
|
1335
1335
|
var stringifyComment = require_stringifyComment();
|
|
@@ -1450,7 +1450,7 @@ ${ctx.indent}${str}`;
|
|
|
1450
1450
|
});
|
|
1451
1451
|
|
|
1452
1452
|
// node_modules/yaml/dist/stringify/stringifyPair.js
|
|
1453
|
-
var require_stringifyPair = __commonJS((exports)
|
|
1453
|
+
var require_stringifyPair = __commonJS(function(exports) {
|
|
1454
1454
|
var identity = require_identity();
|
|
1455
1455
|
var Scalar = require_Scalar();
|
|
1456
1456
|
var stringify = require_stringify();
|
|
@@ -1586,7 +1586,7 @@ ${ctx.indent}`;
|
|
|
1586
1586
|
});
|
|
1587
1587
|
|
|
1588
1588
|
// node_modules/yaml/dist/log.js
|
|
1589
|
-
var require_log = __commonJS((exports)
|
|
1589
|
+
var require_log = __commonJS(function(exports) {
|
|
1590
1590
|
var node_process = __require("process");
|
|
1591
1591
|
function debug(logLevel, ...messages) {
|
|
1592
1592
|
if (logLevel === "debug")
|
|
@@ -1605,7 +1605,7 @@ var require_log = __commonJS((exports) => {
|
|
|
1605
1605
|
});
|
|
1606
1606
|
|
|
1607
1607
|
// node_modules/yaml/dist/schema/yaml-1.1/merge.js
|
|
1608
|
-
var require_merge = __commonJS((exports)
|
|
1608
|
+
var require_merge = __commonJS(function(exports) {
|
|
1609
1609
|
var identity = require_identity();
|
|
1610
1610
|
var Scalar = require_Scalar();
|
|
1611
1611
|
var MERGE_KEY = "<<";
|
|
@@ -1662,7 +1662,7 @@ var require_merge = __commonJS((exports) => {
|
|
|
1662
1662
|
});
|
|
1663
1663
|
|
|
1664
1664
|
// node_modules/yaml/dist/nodes/addPairToJSMap.js
|
|
1665
|
-
var require_addPairToJSMap = __commonJS((exports)
|
|
1665
|
+
var require_addPairToJSMap = __commonJS(function(exports) {
|
|
1666
1666
|
var log = require_log();
|
|
1667
1667
|
var merge = require_merge();
|
|
1668
1668
|
var stringify = require_stringify();
|
|
@@ -1723,7 +1723,7 @@ var require_addPairToJSMap = __commonJS((exports) => {
|
|
|
1723
1723
|
});
|
|
1724
1724
|
|
|
1725
1725
|
// node_modules/yaml/dist/nodes/Pair.js
|
|
1726
|
-
var require_Pair = __commonJS((exports)
|
|
1726
|
+
var require_Pair = __commonJS(function(exports) {
|
|
1727
1727
|
var createNode = require_createNode();
|
|
1728
1728
|
var stringifyPair = require_stringifyPair();
|
|
1729
1729
|
var addPairToJSMap = require_addPairToJSMap();
|
|
@@ -1761,7 +1761,7 @@ var require_Pair = __commonJS((exports) => {
|
|
|
1761
1761
|
});
|
|
1762
1762
|
|
|
1763
1763
|
// node_modules/yaml/dist/stringify/stringifyCollection.js
|
|
1764
|
-
var require_stringifyCollection = __commonJS((exports)
|
|
1764
|
+
var require_stringifyCollection = __commonJS(function(exports) {
|
|
1765
1765
|
var identity = require_identity();
|
|
1766
1766
|
var stringify = require_stringify();
|
|
1767
1767
|
var stringifyComment = require_stringifyComment();
|
|
@@ -1913,7 +1913,7 @@ ${indent}${end}`;
|
|
|
1913
1913
|
});
|
|
1914
1914
|
|
|
1915
1915
|
// node_modules/yaml/dist/nodes/YAMLMap.js
|
|
1916
|
-
var require_YAMLMap = __commonJS((exports)
|
|
1916
|
+
var require_YAMLMap = __commonJS(function(exports) {
|
|
1917
1917
|
var stringifyCollection = require_stringifyCollection();
|
|
1918
1918
|
var addPairToJSMap = require_addPairToJSMap();
|
|
1919
1919
|
var Collection = require_Collection();
|
|
@@ -2040,7 +2040,7 @@ var require_YAMLMap = __commonJS((exports) => {
|
|
|
2040
2040
|
});
|
|
2041
2041
|
|
|
2042
2042
|
// node_modules/yaml/dist/schema/common/map.js
|
|
2043
|
-
var require_map = __commonJS((exports)
|
|
2043
|
+
var require_map = __commonJS(function(exports) {
|
|
2044
2044
|
var identity = require_identity();
|
|
2045
2045
|
var YAMLMap = require_YAMLMap();
|
|
2046
2046
|
var map = {
|
|
@@ -2059,7 +2059,7 @@ var require_map = __commonJS((exports) => {
|
|
|
2059
2059
|
});
|
|
2060
2060
|
|
|
2061
2061
|
// node_modules/yaml/dist/nodes/YAMLSeq.js
|
|
2062
|
-
var require_YAMLSeq = __commonJS((exports)
|
|
2062
|
+
var require_YAMLSeq = __commonJS(function(exports) {
|
|
2063
2063
|
var createNode = require_createNode();
|
|
2064
2064
|
var stringifyCollection = require_stringifyCollection();
|
|
2065
2065
|
var Collection = require_Collection();
|
|
@@ -2152,7 +2152,7 @@ var require_YAMLSeq = __commonJS((exports) => {
|
|
|
2152
2152
|
});
|
|
2153
2153
|
|
|
2154
2154
|
// node_modules/yaml/dist/schema/common/seq.js
|
|
2155
|
-
var require_seq = __commonJS((exports)
|
|
2155
|
+
var require_seq = __commonJS(function(exports) {
|
|
2156
2156
|
var identity = require_identity();
|
|
2157
2157
|
var YAMLSeq = require_YAMLSeq();
|
|
2158
2158
|
var seq = {
|
|
@@ -2171,7 +2171,7 @@ var require_seq = __commonJS((exports) => {
|
|
|
2171
2171
|
});
|
|
2172
2172
|
|
|
2173
2173
|
// node_modules/yaml/dist/schema/common/string.js
|
|
2174
|
-
var require_string = __commonJS((exports)
|
|
2174
|
+
var require_string = __commonJS(function(exports) {
|
|
2175
2175
|
var stringifyString = require_stringifyString();
|
|
2176
2176
|
var string = {
|
|
2177
2177
|
identify: (value) => typeof value === "string",
|
|
@@ -2187,7 +2187,7 @@ var require_string = __commonJS((exports) => {
|
|
|
2187
2187
|
});
|
|
2188
2188
|
|
|
2189
2189
|
// node_modules/yaml/dist/schema/common/null.js
|
|
2190
|
-
var require_null = __commonJS((exports)
|
|
2190
|
+
var require_null = __commonJS(function(exports) {
|
|
2191
2191
|
var Scalar = require_Scalar();
|
|
2192
2192
|
var nullTag = {
|
|
2193
2193
|
identify: (value) => value == null,
|
|
@@ -2202,7 +2202,7 @@ var require_null = __commonJS((exports) => {
|
|
|
2202
2202
|
});
|
|
2203
2203
|
|
|
2204
2204
|
// node_modules/yaml/dist/schema/core/bool.js
|
|
2205
|
-
var require_bool = __commonJS((exports)
|
|
2205
|
+
var require_bool = __commonJS(function(exports) {
|
|
2206
2206
|
var Scalar = require_Scalar();
|
|
2207
2207
|
var boolTag = {
|
|
2208
2208
|
identify: (value) => typeof value === "boolean",
|
|
@@ -2223,7 +2223,7 @@ var require_bool = __commonJS((exports) => {
|
|
|
2223
2223
|
});
|
|
2224
2224
|
|
|
2225
2225
|
// node_modules/yaml/dist/stringify/stringifyNumber.js
|
|
2226
|
-
var require_stringifyNumber = __commonJS((exports)
|
|
2226
|
+
var require_stringifyNumber = __commonJS(function(exports) {
|
|
2227
2227
|
function stringifyNumber({ format, minFractionDigits, tag, value }) {
|
|
2228
2228
|
if (typeof value === "bigint")
|
|
2229
2229
|
return String(value);
|
|
@@ -2247,7 +2247,7 @@ var require_stringifyNumber = __commonJS((exports) => {
|
|
|
2247
2247
|
});
|
|
2248
2248
|
|
|
2249
2249
|
// node_modules/yaml/dist/schema/core/float.js
|
|
2250
|
-
var require_float = __commonJS((exports)
|
|
2250
|
+
var require_float = __commonJS(function(exports) {
|
|
2251
2251
|
var Scalar = require_Scalar();
|
|
2252
2252
|
var stringifyNumber = require_stringifyNumber();
|
|
2253
2253
|
var floatNaN = {
|
|
@@ -2290,7 +2290,7 @@ var require_float = __commonJS((exports) => {
|
|
|
2290
2290
|
});
|
|
2291
2291
|
|
|
2292
2292
|
// node_modules/yaml/dist/schema/core/int.js
|
|
2293
|
-
var require_int = __commonJS((exports)
|
|
2293
|
+
var require_int = __commonJS(function(exports) {
|
|
2294
2294
|
var stringifyNumber = require_stringifyNumber();
|
|
2295
2295
|
var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value);
|
|
2296
2296
|
var intResolve = (str, offset, radix, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str.substring(offset), radix);
|
|
@@ -2332,7 +2332,7 @@ var require_int = __commonJS((exports) => {
|
|
|
2332
2332
|
});
|
|
2333
2333
|
|
|
2334
2334
|
// node_modules/yaml/dist/schema/core/schema.js
|
|
2335
|
-
var require_schema = __commonJS((exports)
|
|
2335
|
+
var require_schema = __commonJS(function(exports) {
|
|
2336
2336
|
var map = require_map();
|
|
2337
2337
|
var _null = require_null();
|
|
2338
2338
|
var seq = require_seq();
|
|
@@ -2357,7 +2357,7 @@ var require_schema = __commonJS((exports) => {
|
|
|
2357
2357
|
});
|
|
2358
2358
|
|
|
2359
2359
|
// node_modules/yaml/dist/schema/json/schema.js
|
|
2360
|
-
var require_schema2 = __commonJS((exports)
|
|
2360
|
+
var require_schema2 = __commonJS(function(exports) {
|
|
2361
2361
|
var Scalar = require_Scalar();
|
|
2362
2362
|
var map = require_map();
|
|
2363
2363
|
var seq = require_seq();
|
|
@@ -2421,7 +2421,7 @@ var require_schema2 = __commonJS((exports) => {
|
|
|
2421
2421
|
});
|
|
2422
2422
|
|
|
2423
2423
|
// node_modules/yaml/dist/schema/yaml-1.1/binary.js
|
|
2424
|
-
var require_binary = __commonJS((exports)
|
|
2424
|
+
var require_binary = __commonJS(function(exports) {
|
|
2425
2425
|
var node_buffer = __require("buffer");
|
|
2426
2426
|
var Scalar = require_Scalar();
|
|
2427
2427
|
var stringifyString = require_stringifyString();
|
|
@@ -2476,7 +2476,7 @@ var require_binary = __commonJS((exports) => {
|
|
|
2476
2476
|
});
|
|
2477
2477
|
|
|
2478
2478
|
// node_modules/yaml/dist/schema/yaml-1.1/pairs.js
|
|
2479
|
-
var require_pairs = __commonJS((exports)
|
|
2479
|
+
var require_pairs = __commonJS(function(exports) {
|
|
2480
2480
|
var identity = require_identity();
|
|
2481
2481
|
var Pair = require_Pair();
|
|
2482
2482
|
var Scalar = require_Scalar();
|
|
@@ -2551,7 +2551,7 @@ ${cn.comment}` : item.comment;
|
|
|
2551
2551
|
});
|
|
2552
2552
|
|
|
2553
2553
|
// node_modules/yaml/dist/schema/yaml-1.1/omap.js
|
|
2554
|
-
var require_omap = __commonJS((exports)
|
|
2554
|
+
var require_omap = __commonJS(function(exports) {
|
|
2555
2555
|
var identity = require_identity();
|
|
2556
2556
|
var toJS = require_toJS();
|
|
2557
2557
|
var YAMLMap = require_YAMLMap();
|
|
@@ -2623,7 +2623,7 @@ var require_omap = __commonJS((exports) => {
|
|
|
2623
2623
|
});
|
|
2624
2624
|
|
|
2625
2625
|
// node_modules/yaml/dist/schema/yaml-1.1/bool.js
|
|
2626
|
-
var require_bool2 = __commonJS((exports)
|
|
2626
|
+
var require_bool2 = __commonJS(function(exports) {
|
|
2627
2627
|
var Scalar = require_Scalar();
|
|
2628
2628
|
function boolStringify({ value, source }, ctx) {
|
|
2629
2629
|
const boolObj = value ? trueTag : falseTag;
|
|
@@ -2652,7 +2652,7 @@ var require_bool2 = __commonJS((exports) => {
|
|
|
2652
2652
|
});
|
|
2653
2653
|
|
|
2654
2654
|
// node_modules/yaml/dist/schema/yaml-1.1/float.js
|
|
2655
|
-
var require_float2 = __commonJS((exports)
|
|
2655
|
+
var require_float2 = __commonJS(function(exports) {
|
|
2656
2656
|
var Scalar = require_Scalar();
|
|
2657
2657
|
var stringifyNumber = require_stringifyNumber();
|
|
2658
2658
|
var floatNaN = {
|
|
@@ -2698,7 +2698,7 @@ var require_float2 = __commonJS((exports) => {
|
|
|
2698
2698
|
});
|
|
2699
2699
|
|
|
2700
2700
|
// node_modules/yaml/dist/schema/yaml-1.1/int.js
|
|
2701
|
-
var require_int2 = __commonJS((exports)
|
|
2701
|
+
var require_int2 = __commonJS(function(exports) {
|
|
2702
2702
|
var stringifyNumber = require_stringifyNumber();
|
|
2703
2703
|
var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value);
|
|
2704
2704
|
function intResolve(str, offset, radix, { intAsBigInt }) {
|
|
@@ -2774,7 +2774,7 @@ var require_int2 = __commonJS((exports) => {
|
|
|
2774
2774
|
});
|
|
2775
2775
|
|
|
2776
2776
|
// node_modules/yaml/dist/schema/yaml-1.1/set.js
|
|
2777
|
-
var require_set = __commonJS((exports)
|
|
2777
|
+
var require_set = __commonJS(function(exports) {
|
|
2778
2778
|
var identity = require_identity();
|
|
2779
2779
|
var Pair = require_Pair();
|
|
2780
2780
|
var YAMLMap = require_YAMLMap();
|
|
@@ -2857,7 +2857,7 @@ var require_set = __commonJS((exports) => {
|
|
|
2857
2857
|
});
|
|
2858
2858
|
|
|
2859
2859
|
// node_modules/yaml/dist/schema/yaml-1.1/timestamp.js
|
|
2860
|
-
var require_timestamp = __commonJS((exports)
|
|
2860
|
+
var require_timestamp = __commonJS(function(exports) {
|
|
2861
2861
|
var stringifyNumber = require_stringifyNumber();
|
|
2862
2862
|
function parseSexagesimal(str, asBigInt) {
|
|
2863
2863
|
const sign = str[0];
|
|
@@ -2939,7 +2939,7 @@ var require_timestamp = __commonJS((exports) => {
|
|
|
2939
2939
|
});
|
|
2940
2940
|
|
|
2941
2941
|
// node_modules/yaml/dist/schema/yaml-1.1/schema.js
|
|
2942
|
-
var require_schema3 = __commonJS((exports)
|
|
2942
|
+
var require_schema3 = __commonJS(function(exports) {
|
|
2943
2943
|
var map = require_map();
|
|
2944
2944
|
var _null = require_null();
|
|
2945
2945
|
var seq = require_seq();
|
|
@@ -2980,7 +2980,7 @@ var require_schema3 = __commonJS((exports) => {
|
|
|
2980
2980
|
});
|
|
2981
2981
|
|
|
2982
2982
|
// node_modules/yaml/dist/schema/tags.js
|
|
2983
|
-
var require_tags = __commonJS((exports)
|
|
2983
|
+
var require_tags = __commonJS(function(exports) {
|
|
2984
2984
|
var map = require_map();
|
|
2985
2985
|
var _null = require_null();
|
|
2986
2986
|
var seq = require_seq();
|
|
@@ -3071,7 +3071,7 @@ var require_tags = __commonJS((exports) => {
|
|
|
3071
3071
|
});
|
|
3072
3072
|
|
|
3073
3073
|
// node_modules/yaml/dist/schema/Schema.js
|
|
3074
|
-
var require_Schema = __commonJS((exports)
|
|
3074
|
+
var require_Schema = __commonJS(function(exports) {
|
|
3075
3075
|
var identity = require_identity();
|
|
3076
3076
|
var map = require_map();
|
|
3077
3077
|
var seq = require_seq();
|
|
@@ -3101,7 +3101,7 @@ var require_Schema = __commonJS((exports) => {
|
|
|
3101
3101
|
});
|
|
3102
3102
|
|
|
3103
3103
|
// node_modules/yaml/dist/stringify/stringifyDocument.js
|
|
3104
|
-
var require_stringifyDocument = __commonJS((exports)
|
|
3104
|
+
var require_stringifyDocument = __commonJS(function(exports) {
|
|
3105
3105
|
var identity = require_identity();
|
|
3106
3106
|
var stringify = require_stringify();
|
|
3107
3107
|
var stringifyComment = require_stringifyComment();
|
|
@@ -3181,7 +3181,7 @@ var require_stringifyDocument = __commonJS((exports) => {
|
|
|
3181
3181
|
});
|
|
3182
3182
|
|
|
3183
3183
|
// node_modules/yaml/dist/doc/Document.js
|
|
3184
|
-
var require_Document = __commonJS((exports)
|
|
3184
|
+
var require_Document = __commonJS(function(exports) {
|
|
3185
3185
|
var Alias = require_Alias();
|
|
3186
3186
|
var Collection = require_Collection();
|
|
3187
3187
|
var identity = require_identity();
|
|
@@ -3416,7 +3416,7 @@ var require_Document = __commonJS((exports) => {
|
|
|
3416
3416
|
});
|
|
3417
3417
|
|
|
3418
3418
|
// node_modules/yaml/dist/errors.js
|
|
3419
|
-
var require_errors = __commonJS((exports)
|
|
3419
|
+
var require_errors = __commonJS(function(exports) {
|
|
3420
3420
|
class YAMLError extends Error {
|
|
3421
3421
|
constructor(name, pos, code, message) {
|
|
3422
3422
|
super();
|
|
@@ -3481,7 +3481,7 @@ ${pointer}
|
|
|
3481
3481
|
});
|
|
3482
3482
|
|
|
3483
3483
|
// node_modules/yaml/dist/compose/resolve-props.js
|
|
3484
|
-
var require_resolve_props = __commonJS((exports)
|
|
3484
|
+
var require_resolve_props = __commonJS(function(exports) {
|
|
3485
3485
|
function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) {
|
|
3486
3486
|
let spaceBefore = false;
|
|
3487
3487
|
let atNewline = startOnNewline;
|
|
@@ -3611,7 +3611,7 @@ var require_resolve_props = __commonJS((exports) => {
|
|
|
3611
3611
|
});
|
|
3612
3612
|
|
|
3613
3613
|
// node_modules/yaml/dist/compose/util-contains-newline.js
|
|
3614
|
-
var require_util_contains_newline = __commonJS((exports)
|
|
3614
|
+
var require_util_contains_newline = __commonJS(function(exports) {
|
|
3615
3615
|
function containsNewline(key) {
|
|
3616
3616
|
if (!key)
|
|
3617
3617
|
return null;
|
|
@@ -3651,7 +3651,7 @@ var require_util_contains_newline = __commonJS((exports) => {
|
|
|
3651
3651
|
});
|
|
3652
3652
|
|
|
3653
3653
|
// node_modules/yaml/dist/compose/util-flow-indent-check.js
|
|
3654
|
-
var require_util_flow_indent_check = __commonJS((exports)
|
|
3654
|
+
var require_util_flow_indent_check = __commonJS(function(exports) {
|
|
3655
3655
|
var utilContainsNewline = require_util_contains_newline();
|
|
3656
3656
|
function flowIndentCheck(indent, fc, onError) {
|
|
3657
3657
|
if (fc?.type === "flow-collection") {
|
|
@@ -3666,7 +3666,7 @@ var require_util_flow_indent_check = __commonJS((exports) => {
|
|
|
3666
3666
|
});
|
|
3667
3667
|
|
|
3668
3668
|
// node_modules/yaml/dist/compose/util-map-includes.js
|
|
3669
|
-
var require_util_map_includes = __commonJS((exports)
|
|
3669
|
+
var require_util_map_includes = __commonJS(function(exports) {
|
|
3670
3670
|
var identity = require_identity();
|
|
3671
3671
|
function mapIncludes(ctx, items, search) {
|
|
3672
3672
|
const { uniqueKeys } = ctx.options;
|
|
@@ -3679,7 +3679,7 @@ var require_util_map_includes = __commonJS((exports) => {
|
|
|
3679
3679
|
});
|
|
3680
3680
|
|
|
3681
3681
|
// node_modules/yaml/dist/compose/resolve-block-map.js
|
|
3682
|
-
var require_resolve_block_map = __commonJS((exports)
|
|
3682
|
+
var require_resolve_block_map = __commonJS(function(exports) {
|
|
3683
3683
|
var Pair = require_Pair();
|
|
3684
3684
|
var YAMLMap = require_YAMLMap();
|
|
3685
3685
|
var resolveProps = require_resolve_props();
|
|
@@ -3786,7 +3786,7 @@ var require_resolve_block_map = __commonJS((exports) => {
|
|
|
3786
3786
|
});
|
|
3787
3787
|
|
|
3788
3788
|
// node_modules/yaml/dist/compose/resolve-block-seq.js
|
|
3789
|
-
var require_resolve_block_seq = __commonJS((exports)
|
|
3789
|
+
var require_resolve_block_seq = __commonJS(function(exports) {
|
|
3790
3790
|
var YAMLSeq = require_YAMLSeq();
|
|
3791
3791
|
var resolveProps = require_resolve_props();
|
|
3792
3792
|
var utilFlowIndentCheck = require_util_flow_indent_check();
|
|
@@ -3834,7 +3834,7 @@ var require_resolve_block_seq = __commonJS((exports) => {
|
|
|
3834
3834
|
});
|
|
3835
3835
|
|
|
3836
3836
|
// node_modules/yaml/dist/compose/resolve-end.js
|
|
3837
|
-
var require_resolve_end = __commonJS((exports)
|
|
3837
|
+
var require_resolve_end = __commonJS(function(exports) {
|
|
3838
3838
|
function resolveEnd(end, offset, reqSpace, onError) {
|
|
3839
3839
|
let comment = "";
|
|
3840
3840
|
if (end) {
|
|
@@ -3874,7 +3874,7 @@ var require_resolve_end = __commonJS((exports) => {
|
|
|
3874
3874
|
});
|
|
3875
3875
|
|
|
3876
3876
|
// node_modules/yaml/dist/compose/resolve-flow-collection.js
|
|
3877
|
-
var require_resolve_flow_collection = __commonJS((exports)
|
|
3877
|
+
var require_resolve_flow_collection = __commonJS(function(exports) {
|
|
3878
3878
|
var identity = require_identity();
|
|
3879
3879
|
var Pair = require_Pair();
|
|
3880
3880
|
var YAMLMap = require_YAMLMap();
|
|
@@ -4065,7 +4065,7 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
4065
4065
|
});
|
|
4066
4066
|
|
|
4067
4067
|
// node_modules/yaml/dist/compose/compose-collection.js
|
|
4068
|
-
var require_compose_collection = __commonJS((exports)
|
|
4068
|
+
var require_compose_collection = __commonJS(function(exports) {
|
|
4069
4069
|
var identity = require_identity();
|
|
4070
4070
|
var Scalar = require_Scalar();
|
|
4071
4071
|
var YAMLMap = require_YAMLMap();
|
|
@@ -4127,7 +4127,7 @@ var require_compose_collection = __commonJS((exports) => {
|
|
|
4127
4127
|
});
|
|
4128
4128
|
|
|
4129
4129
|
// node_modules/yaml/dist/compose/resolve-block-scalar.js
|
|
4130
|
-
var require_resolve_block_scalar = __commonJS((exports)
|
|
4130
|
+
var require_resolve_block_scalar = __commonJS(function(exports) {
|
|
4131
4131
|
var Scalar = require_Scalar();
|
|
4132
4132
|
function resolveBlockScalar(ctx, scalar, onError) {
|
|
4133
4133
|
const start = scalar.offset;
|
|
@@ -4320,7 +4320,7 @@ var require_resolve_block_scalar = __commonJS((exports) => {
|
|
|
4320
4320
|
});
|
|
4321
4321
|
|
|
4322
4322
|
// node_modules/yaml/dist/compose/resolve-flow-scalar.js
|
|
4323
|
-
var require_resolve_flow_scalar = __commonJS((exports)
|
|
4323
|
+
var require_resolve_flow_scalar = __commonJS(function(exports) {
|
|
4324
4324
|
var Scalar = require_Scalar();
|
|
4325
4325
|
var resolveEnd = require_resolve_end();
|
|
4326
4326
|
function resolveFlowScalar(scalar, strict, onError) {
|
|
@@ -4537,7 +4537,7 @@ var require_resolve_flow_scalar = __commonJS((exports) => {
|
|
|
4537
4537
|
});
|
|
4538
4538
|
|
|
4539
4539
|
// node_modules/yaml/dist/compose/compose-scalar.js
|
|
4540
|
-
var require_compose_scalar = __commonJS((exports)
|
|
4540
|
+
var require_compose_scalar = __commonJS(function(exports) {
|
|
4541
4541
|
var identity = require_identity();
|
|
4542
4542
|
var Scalar = require_Scalar();
|
|
4543
4543
|
var resolveBlockScalar = require_resolve_block_scalar();
|
|
@@ -4615,7 +4615,7 @@ var require_compose_scalar = __commonJS((exports) => {
|
|
|
4615
4615
|
});
|
|
4616
4616
|
|
|
4617
4617
|
// node_modules/yaml/dist/compose/util-empty-scalar-position.js
|
|
4618
|
-
var require_util_empty_scalar_position = __commonJS((exports)
|
|
4618
|
+
var require_util_empty_scalar_position = __commonJS(function(exports) {
|
|
4619
4619
|
function emptyScalarPosition(offset, before, pos) {
|
|
4620
4620
|
if (before) {
|
|
4621
4621
|
pos ?? (pos = before.length);
|
|
@@ -4642,7 +4642,7 @@ var require_util_empty_scalar_position = __commonJS((exports) => {
|
|
|
4642
4642
|
});
|
|
4643
4643
|
|
|
4644
4644
|
// node_modules/yaml/dist/compose/compose-node.js
|
|
4645
|
-
var require_compose_node = __commonJS((exports)
|
|
4645
|
+
var require_compose_node = __commonJS(function(exports) {
|
|
4646
4646
|
var Alias = require_Alias();
|
|
4647
4647
|
var identity = require_identity();
|
|
4648
4648
|
var composeCollection = require_compose_collection();
|
|
@@ -4745,7 +4745,7 @@ var require_compose_node = __commonJS((exports) => {
|
|
|
4745
4745
|
});
|
|
4746
4746
|
|
|
4747
4747
|
// node_modules/yaml/dist/compose/compose-doc.js
|
|
4748
|
-
var require_compose_doc = __commonJS((exports)
|
|
4748
|
+
var require_compose_doc = __commonJS(function(exports) {
|
|
4749
4749
|
var Document = require_Document();
|
|
4750
4750
|
var composeNode = require_compose_node();
|
|
4751
4751
|
var resolveEnd = require_resolve_end();
|
|
@@ -4785,7 +4785,7 @@ var require_compose_doc = __commonJS((exports) => {
|
|
|
4785
4785
|
});
|
|
4786
4786
|
|
|
4787
4787
|
// node_modules/yaml/dist/compose/composer.js
|
|
4788
|
-
var require_composer = __commonJS((exports)
|
|
4788
|
+
var require_composer = __commonJS(function(exports) {
|
|
4789
4789
|
var node_process = __require("process");
|
|
4790
4790
|
var directives = require_directives();
|
|
4791
4791
|
var Document = require_Document();
|
|
@@ -4976,7 +4976,7 @@ ${end.comment}` : end.comment;
|
|
|
4976
4976
|
});
|
|
4977
4977
|
|
|
4978
4978
|
// node_modules/yaml/dist/parse/cst-scalar.js
|
|
4979
|
-
var require_cst_scalar = __commonJS((exports)
|
|
4979
|
+
var require_cst_scalar = __commonJS(function(exports) {
|
|
4980
4980
|
var resolveBlockScalar = require_resolve_block_scalar();
|
|
4981
4981
|
var resolveFlowScalar = require_resolve_flow_scalar();
|
|
4982
4982
|
var errors2 = require_errors();
|
|
@@ -5166,7 +5166,7 @@ var require_cst_scalar = __commonJS((exports) => {
|
|
|
5166
5166
|
});
|
|
5167
5167
|
|
|
5168
5168
|
// node_modules/yaml/dist/parse/cst-stringify.js
|
|
5169
|
-
var require_cst_stringify = __commonJS((exports)
|
|
5169
|
+
var require_cst_stringify = __commonJS(function(exports) {
|
|
5170
5170
|
var stringify = (cst) => ("type" in cst) ? stringifyToken(cst) : stringifyItem(cst);
|
|
5171
5171
|
function stringifyToken(token) {
|
|
5172
5172
|
switch (token.type) {
|
|
@@ -5224,7 +5224,7 @@ var require_cst_stringify = __commonJS((exports) => {
|
|
|
5224
5224
|
});
|
|
5225
5225
|
|
|
5226
5226
|
// node_modules/yaml/dist/parse/cst-visit.js
|
|
5227
|
-
var require_cst_visit = __commonJS((exports)
|
|
5227
|
+
var require_cst_visit = __commonJS(function(exports) {
|
|
5228
5228
|
var BREAK = Symbol("break visit");
|
|
5229
5229
|
var SKIP = Symbol("skip children");
|
|
5230
5230
|
var REMOVE = Symbol("remove item");
|
|
@@ -5283,7 +5283,7 @@ var require_cst_visit = __commonJS((exports) => {
|
|
|
5283
5283
|
});
|
|
5284
5284
|
|
|
5285
5285
|
// node_modules/yaml/dist/parse/cst.js
|
|
5286
|
-
var require_cst = __commonJS((exports)
|
|
5286
|
+
var require_cst = __commonJS(function(exports) {
|
|
5287
5287
|
var cstScalar = require_cst_scalar();
|
|
5288
5288
|
var cstStringify = require_cst_stringify();
|
|
5289
5289
|
var cstVisit = require_cst_visit();
|
|
@@ -5384,7 +5384,7 @@ var require_cst = __commonJS((exports) => {
|
|
|
5384
5384
|
});
|
|
5385
5385
|
|
|
5386
5386
|
// node_modules/yaml/dist/parse/lexer.js
|
|
5387
|
-
var require_lexer = __commonJS((exports)
|
|
5387
|
+
var require_lexer = __commonJS(function(exports) {
|
|
5388
5388
|
var cst = require_cst();
|
|
5389
5389
|
function isEmpty(ch) {
|
|
5390
5390
|
switch (ch) {
|
|
@@ -5981,7 +5981,7 @@ var require_lexer = __commonJS((exports) => {
|
|
|
5981
5981
|
});
|
|
5982
5982
|
|
|
5983
5983
|
// node_modules/yaml/dist/parse/line-counter.js
|
|
5984
|
-
var require_line_counter = __commonJS((exports)
|
|
5984
|
+
var require_line_counter = __commonJS(function(exports) {
|
|
5985
5985
|
class LineCounter {
|
|
5986
5986
|
constructor() {
|
|
5987
5987
|
this.lineStarts = [];
|
|
@@ -6009,7 +6009,7 @@ var require_line_counter = __commonJS((exports) => {
|
|
|
6009
6009
|
});
|
|
6010
6010
|
|
|
6011
6011
|
// node_modules/yaml/dist/parse/parser.js
|
|
6012
|
-
var require_parser = __commonJS((exports)
|
|
6012
|
+
var require_parser = __commonJS(function(exports) {
|
|
6013
6013
|
var node_process = __require("process");
|
|
6014
6014
|
var cst = require_cst();
|
|
6015
6015
|
var lexer = require_lexer();
|
|
@@ -6865,7 +6865,7 @@ var require_parser = __commonJS((exports) => {
|
|
|
6865
6865
|
});
|
|
6866
6866
|
|
|
6867
6867
|
// node_modules/yaml/dist/public-api.js
|
|
6868
|
-
var require_public_api = __commonJS((exports)
|
|
6868
|
+
var require_public_api = __commonJS(function(exports) {
|
|
6869
6869
|
var composer = require_composer();
|
|
6870
6870
|
var Document = require_Document();
|
|
6871
6871
|
var errors2 = require_errors();
|
|
@@ -6962,10 +6962,7 @@ var require_public_api = __commonJS((exports) => {
|
|
|
6962
6962
|
import { parseArgs } from "node:util";
|
|
6963
6963
|
|
|
6964
6964
|
// src/analyze.ts
|
|
6965
|
-
import { resolve as
|
|
6966
|
-
|
|
6967
|
-
// src/checks/index.ts
|
|
6968
|
-
import { basename as basename2 } from "node:path";
|
|
6965
|
+
import { resolve as resolve4 } from "node:path";
|
|
6969
6966
|
|
|
6970
6967
|
// src/checks/budgets.ts
|
|
6971
6968
|
import { basename } from "node:path";
|
|
@@ -7042,28 +7039,6 @@ function countFinding(args) {
|
|
|
7042
7039
|
})
|
|
7043
7040
|
];
|
|
7044
7041
|
}
|
|
7045
|
-
function packageManagerDrift(facts) {
|
|
7046
|
-
if (facts.packageManager !== "bun") {
|
|
7047
|
-
return [];
|
|
7048
|
-
}
|
|
7049
|
-
const hits = facts.policyFiles.filter((p) => p.text.includes("npm install"));
|
|
7050
|
-
if (hits.length === 0) {
|
|
7051
|
-
return [];
|
|
7052
|
-
}
|
|
7053
|
-
return [
|
|
7054
|
-
make("policy.package-manager-drift", "policy:package-manager", {
|
|
7055
|
-
action: "drift",
|
|
7056
|
-
severity: "info",
|
|
7057
|
-
message: "Policy mentions npm install but packageManager is bun",
|
|
7058
|
-
reason: "Docs drift vs packageManager field / lockfile convention",
|
|
7059
|
-
evidence: [
|
|
7060
|
-
{ kind: "packageManager", value: facts.packageManager },
|
|
7061
|
-
...hits.map((p) => ({ kind: "policy", value: p.path }))
|
|
7062
|
-
],
|
|
7063
|
-
suggest: "Prefer bun install in AGENTS.md / CLAUDE.md"
|
|
7064
|
-
})
|
|
7065
|
-
];
|
|
7066
|
-
}
|
|
7067
7042
|
function runBudgets(facts, options) {
|
|
7068
7043
|
return [
|
|
7069
7044
|
...policyLengthFinding({
|
|
@@ -7099,114 +7074,80 @@ function runBudgets(facts, options) {
|
|
|
7099
7074
|
message: `MCP surface above sweet spot (${facts.mcp.length} > ${options.mcp})`,
|
|
7100
7075
|
reason: "What actually degrades an agent is the number of tool definitions in context, not the number of servers: benchmarks show large models near-perfect at ~20 tools and failing outright past ~100, and a typical server carries tens of tools. Server count is the only proxy available without connecting to them, so treat this as a hint to go count the tools, not as a measurement.",
|
|
7101
7076
|
suggest: "Count the tools your servers actually expose; disable unused servers in mcp.json or set thresholds.mcp in .agentscanrc.json"
|
|
7102
|
-
})
|
|
7103
|
-
...packageManagerDrift(facts)
|
|
7077
|
+
})
|
|
7104
7078
|
];
|
|
7105
7079
|
}
|
|
7106
7080
|
|
|
7107
|
-
// src/checks/
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
}
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
{
|
|
7178
|
-
id: "mcp.literal-env",
|
|
7179
|
-
description: "MCP env value is a long literal instead of ${VAR}"
|
|
7180
|
-
},
|
|
7181
|
-
{
|
|
7182
|
-
id: "budget.agents-md",
|
|
7183
|
-
description: "AGENTS.md longer than the point where added lines stop helping (>150 lines)"
|
|
7184
|
-
},
|
|
7185
|
-
{
|
|
7186
|
-
id: "budget.claude-md",
|
|
7187
|
-
description: "CLAUDE.md longer than the instruction budget a model reliably follows (>200 lines)"
|
|
7188
|
-
},
|
|
7189
|
-
{
|
|
7190
|
-
id: "budget.agents",
|
|
7191
|
-
description: "More agent definitions than a focused set (>8 files in .claude/agents)"
|
|
7192
|
-
},
|
|
7193
|
-
{
|
|
7194
|
-
id: "budget.mcp",
|
|
7195
|
-
description: "Project MCP server count above the point where tool selection degrades"
|
|
7196
|
-
},
|
|
7197
|
-
{
|
|
7198
|
-
id: "policy.package-manager-drift",
|
|
7199
|
-
description: "Policy mentions npm install while packageManager is bun"
|
|
7200
|
-
}
|
|
7201
|
-
];
|
|
7202
|
-
var SECRET_PATTERNS = [
|
|
7203
|
-
{ label: "Anthropic key", re: /\bsk-ant-[A-Za-z0-9_-]{16,}/ },
|
|
7204
|
-
{ label: "OpenAI-style key", re: /\bsk-[A-Za-z0-9_]{20,}\b/ },
|
|
7205
|
-
{ label: "GitHub token", re: /\bgh[pousr]_[A-Za-z0-9]{20,}/ },
|
|
7206
|
-
{ label: "Google API key", re: /\bAIza[A-Za-z0-9_-]{20,}/ },
|
|
7207
|
-
{ label: "Slack token", re: /\bxox[baprs]-[A-Za-z0-9-]{10,}/ },
|
|
7208
|
-
{ label: "AWS access key id", re: /\bAKIA[0-9A-Z]{16}\b/ }
|
|
7209
|
-
];
|
|
7081
|
+
// src/checks/agents.ts
|
|
7082
|
+
function checkAgents(facts) {
|
|
7083
|
+
const out = [];
|
|
7084
|
+
const names = new Map;
|
|
7085
|
+
for (const agent of facts.agents) {
|
|
7086
|
+
if (agent.unreadable === true || agent.unparseableFrontmatter === true) {
|
|
7087
|
+
continue;
|
|
7088
|
+
}
|
|
7089
|
+
if (!agent.hasFrontmatter) {
|
|
7090
|
+
out.push(make("agent.missing-frontmatter", `agent:${agent.name}`, {
|
|
7091
|
+
action: "warn",
|
|
7092
|
+
severity: "error",
|
|
7093
|
+
message: "Agent definition has no YAML frontmatter block",
|
|
7094
|
+
reason: "An agent is selected by its declared description; without a frontmatter block there is nothing to select on, and the file is unlikely to load as an agent at all.",
|
|
7095
|
+
evidence: [{ kind: "agent", value: agent.path }],
|
|
7096
|
+
suggest: "Add a --- delimited block with a description"
|
|
7097
|
+
}));
|
|
7098
|
+
continue;
|
|
7099
|
+
}
|
|
7100
|
+
if (agent.description === undefined) {
|
|
7101
|
+
out.push(make("agent.missing-description", `agent:${agent.name}`, {
|
|
7102
|
+
action: "warn",
|
|
7103
|
+
severity: "error",
|
|
7104
|
+
message: "Agent frontmatter has no `description`",
|
|
7105
|
+
reason: "The description is how the main session decides which agent to dispatch. Without one the agent can still be named explicitly, but it will not be chosen on its own.",
|
|
7106
|
+
evidence: [{ kind: "agent", value: agent.path }],
|
|
7107
|
+
suggest: "Add a one-line description saying when to dispatch it"
|
|
7108
|
+
}));
|
|
7109
|
+
}
|
|
7110
|
+
if (agent.frontmatterName === undefined) {
|
|
7111
|
+
out.push(make("agent.missing-name", `agent:${agent.name}`, {
|
|
7112
|
+
action: "warn",
|
|
7113
|
+
severity: "error",
|
|
7114
|
+
message: "Agent frontmatter has no `name`",
|
|
7115
|
+
reason: "Agent identity is declared by the required frontmatter name.",
|
|
7116
|
+
evidence: [{ kind: "agent", value: agent.path }],
|
|
7117
|
+
suggest: "Add a `name` field to the frontmatter"
|
|
7118
|
+
}));
|
|
7119
|
+
} else {
|
|
7120
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(agent.frontmatterName)) {
|
|
7121
|
+
out.push(make("agent.invalid-name", `agent:${agent.name}`, {
|
|
7122
|
+
action: "warn",
|
|
7123
|
+
severity: "warning",
|
|
7124
|
+
message: `Agent name "${agent.frontmatterName}" is not a valid identifier`,
|
|
7125
|
+
reason: "The subagent reference specifies a unique identifier in lowercase letters and hyphens. A name outside that shape is not documented to load reliably, and a `:` in it is documented not to load at all. See docs/spec/agents.md.",
|
|
7126
|
+
evidence: [{ kind: "agent", value: agent.path }],
|
|
7127
|
+
suggest: "Use lowercase letters, numbers, and hyphens in the `name` field"
|
|
7128
|
+
}));
|
|
7129
|
+
}
|
|
7130
|
+
const list = names.get(agent.frontmatterName) ?? [];
|
|
7131
|
+
list.push(agent.path);
|
|
7132
|
+
names.set(agent.frontmatterName, list);
|
|
7133
|
+
}
|
|
7134
|
+
}
|
|
7135
|
+
for (const [name, paths] of names)
|
|
7136
|
+
if (paths.length > 1) {
|
|
7137
|
+
out.push(make("agent.duplicate-name", `agent:${name}`, {
|
|
7138
|
+
action: "warn",
|
|
7139
|
+
severity: "error",
|
|
7140
|
+
message: `Agent name "${name}" is declared by multiple files`,
|
|
7141
|
+
reason: "Duplicate names make dispatch ambiguous.",
|
|
7142
|
+
evidence: paths.map((path) => ({ kind: "agent", value: path })),
|
|
7143
|
+
suggest: "Give each agent a unique frontmatter name"
|
|
7144
|
+
}));
|
|
7145
|
+
}
|
|
7146
|
+
return out;
|
|
7147
|
+
}
|
|
7148
|
+
|
|
7149
|
+
// src/checks/config.ts
|
|
7150
|
+
import { basename as basename2 } from "node:path";
|
|
7210
7151
|
function safeDetail(detail) {
|
|
7211
7152
|
const position = detail.match(/\bline \d+(?:, column \d+)?/i)?.[0];
|
|
7212
7153
|
const kind = detail.split(/[:(]/)[0]?.trim().slice(0, 60);
|
|
@@ -7215,6 +7156,18 @@ function safeDetail(detail) {
|
|
|
7215
7156
|
}
|
|
7216
7157
|
function checkConfigErrors(facts) {
|
|
7217
7158
|
return facts.configErrors.map((err) => {
|
|
7159
|
+
if (err.kind === "truncated") {
|
|
7160
|
+
return make("scan.truncated", `scan:${err.path}`, {
|
|
7161
|
+
action: "warn",
|
|
7162
|
+
severity: "info",
|
|
7163
|
+
message: `${basename2(err.path)} is larger than the scan cap — only its first bytes were read`,
|
|
7164
|
+
reason: "The file itself is fine; agentscan reads a bounded prefix of it. Checks that read the body — skill.broken-reference, and the AGENTS.md / CLAUDE.md line counts — see only that prefix, so they can undercount on this file.",
|
|
7165
|
+
evidence: [
|
|
7166
|
+
{ kind: "config", value: err.path },
|
|
7167
|
+
{ kind: "detail", value: err.detail }
|
|
7168
|
+
]
|
|
7169
|
+
});
|
|
7170
|
+
}
|
|
7218
7171
|
const what = err.kind === "invalid-json" ? "is not valid JSON" : err.kind === "unreadable" ? "could not be read" : "has an unexpected shape";
|
|
7219
7172
|
return make("config.unreadable", `config:${err.path}#${err.kind}:${safeDetail(err.detail)}`, {
|
|
7220
7173
|
action: "warn",
|
|
@@ -7229,6 +7182,8 @@ function checkConfigErrors(facts) {
|
|
|
7229
7182
|
});
|
|
7230
7183
|
});
|
|
7231
7184
|
}
|
|
7185
|
+
|
|
7186
|
+
// src/checks/hooks.ts
|
|
7232
7187
|
var KNOWN_HOOK_EVENTS = new Set([
|
|
7233
7188
|
"SessionStart",
|
|
7234
7189
|
"Setup",
|
|
@@ -7314,6 +7269,85 @@ function checkHooks(facts) {
|
|
|
7314
7269
|
}
|
|
7315
7270
|
return out;
|
|
7316
7271
|
}
|
|
7272
|
+
|
|
7273
|
+
// src/checks/mcp.ts
|
|
7274
|
+
var SECRET_PATTERNS = [
|
|
7275
|
+
{ label: "Anthropic key", re: /\bsk-ant-[A-Za-z0-9_-]{16,}/ },
|
|
7276
|
+
{ label: "OpenAI project key", re: /\bsk-proj-[A-Za-z0-9_-]{16,}/ },
|
|
7277
|
+
{ label: "OpenAI service-account key", re: /\bsk-svcacct-[A-Za-z0-9_-]{16,}/ },
|
|
7278
|
+
{ label: "OpenAI-style key", re: /\bsk-[A-Za-z0-9_]{20,}\b/ },
|
|
7279
|
+
{ label: "GitHub token", re: /\bgh[pousr]_[A-Za-z0-9]{20,}/ },
|
|
7280
|
+
{ label: "Google API key", re: /\bAIza[A-Za-z0-9_-]{20,}/ },
|
|
7281
|
+
{ label: "Slack token", re: /\bxox[baprs]-[A-Za-z0-9-]{10,}/ },
|
|
7282
|
+
{ label: "AWS access key id", re: /\bAKIA[0-9A-Z]{16}\b/ }
|
|
7283
|
+
];
|
|
7284
|
+
function checkMcp(facts) {
|
|
7285
|
+
const out = [];
|
|
7286
|
+
for (const server of facts.mcp) {
|
|
7287
|
+
if (!(server.hasCommand || server.hasUrl)) {
|
|
7288
|
+
out.push(make("mcp.no-launch", `mcp:${server.name}@${server.path}`, {
|
|
7289
|
+
action: "warn",
|
|
7290
|
+
severity: "error",
|
|
7291
|
+
message: `MCP server "${server.name}" declares neither command nor url`,
|
|
7292
|
+
reason: "Without a command or url the entry is not a launchable MCP server by schema — its tools will not be available, and the entry is dead weight that still costs a config read.",
|
|
7293
|
+
evidence: [{ kind: "mcp", value: `${server.name} @ ${server.path}` }],
|
|
7294
|
+
suggest: `Add a command (or url) for "${server.name}", or remove it`
|
|
7295
|
+
}));
|
|
7296
|
+
}
|
|
7297
|
+
if (server.command !== undefined && server.commandExists === false) {
|
|
7298
|
+
out.push(make("mcp.command-missing", `mcp:${server.name}@${server.path}`, {
|
|
7299
|
+
action: "warn",
|
|
7300
|
+
severity: "error",
|
|
7301
|
+
message: `MCP server "${server.name}" points at a command path that does not exist: ${server.command}`,
|
|
7302
|
+
reason: "The server declares a filesystem command, but that path is not a file on disk, so the entry is not launchable as configured. Bare PATH binaries are not checked — only path-like values this tool can resolve honestly.",
|
|
7303
|
+
evidence: [
|
|
7304
|
+
{ kind: "mcp", value: `${server.name} @ ${server.path}` },
|
|
7305
|
+
{ kind: "command", value: server.command }
|
|
7306
|
+
],
|
|
7307
|
+
suggest: `Restore ${server.command} or fix the command path for "${server.name}"`
|
|
7308
|
+
}));
|
|
7309
|
+
}
|
|
7310
|
+
if (server.hasUrl && server.transport === undefined) {
|
|
7311
|
+
out.push(make("mcp.url-without-type", `mcp:${server.name}@${server.path}`, {
|
|
7312
|
+
action: "warn",
|
|
7313
|
+
severity: "error",
|
|
7314
|
+
message: `MCP server "${server.name}" has a url but no transport type`,
|
|
7315
|
+
reason: "An entry with no `type` is read as a stdio server, so a remote server declared this way is misconfigured by schema and is skipped — its tools never appear, with no error in the report you are reading. See docs/spec/mcp.md.",
|
|
7316
|
+
evidence: [{ kind: "mcp", value: `${server.name} @ ${server.path}` }],
|
|
7317
|
+
suggest: `Add "type": "http" (or "sse" / "ws") to "${server.name}"`
|
|
7318
|
+
}));
|
|
7319
|
+
}
|
|
7320
|
+
const hit = SECRET_PATTERNS.find((p) => p.re.test(server.raw));
|
|
7321
|
+
if (hit !== undefined) {
|
|
7322
|
+
out.push(make("mcp.hardcoded-secret", `mcp:${server.name}@${server.path}`, {
|
|
7323
|
+
action: "warn",
|
|
7324
|
+
severity: "error",
|
|
7325
|
+
message: `MCP server "${server.name}" contains what looks like a hardcoded credential (${hit.label})`,
|
|
7326
|
+
reason: "Credentials in a config file get committed, shared and cached. Treat it as leaked: rotate the key, then reference it through an environment variable.",
|
|
7327
|
+
evidence: [{ kind: "mcp", value: `${server.name} @ ${server.path}` }],
|
|
7328
|
+
suggest: "Rotate the credential, then replace the literal with ${ENV_VAR}"
|
|
7329
|
+
}));
|
|
7330
|
+
continue;
|
|
7331
|
+
}
|
|
7332
|
+
if (server.literalEnvKeys.length > 0) {
|
|
7333
|
+
out.push(make("mcp.literal-env", `mcp:${server.name}@${server.path}`, {
|
|
7334
|
+
action: "warn",
|
|
7335
|
+
severity: "warning",
|
|
7336
|
+
message: `MCP server "${server.name}" has literal env values: ${server.literalEnvKeys.join(", ")}`,
|
|
7337
|
+
reason: "Long literal env values in config are usually secrets that should be indirected through ${VAR} so they are never committed.",
|
|
7338
|
+
evidence: [
|
|
7339
|
+
{ kind: "mcp", value: `${server.name} @ ${server.path}` },
|
|
7340
|
+
{ kind: "env", value: server.literalEnvKeys.join(",") }
|
|
7341
|
+
],
|
|
7342
|
+
suggest: "Replace the literals with ${ENV_VAR} references"
|
|
7343
|
+
}));
|
|
7344
|
+
}
|
|
7345
|
+
}
|
|
7346
|
+
return out;
|
|
7347
|
+
}
|
|
7348
|
+
|
|
7349
|
+
// src/checks/skills.ts
|
|
7350
|
+
import { dirname } from "node:path";
|
|
7317
7351
|
function skillEvidence(skill, value) {
|
|
7318
7352
|
const evidence = [{ kind: "skill", value }];
|
|
7319
7353
|
if (skill.source === "global") {
|
|
@@ -7321,6 +7355,12 @@ function skillEvidence(skill, value) {
|
|
|
7321
7355
|
}
|
|
7322
7356
|
return evidence;
|
|
7323
7357
|
}
|
|
7358
|
+
function skillSubject(skill) {
|
|
7359
|
+
return `skill:${skill.instanceId ?? skill.id}`;
|
|
7360
|
+
}
|
|
7361
|
+
function skillRoot(skill) {
|
|
7362
|
+
return dirname(skill.path.replaceAll("\\", "/"));
|
|
7363
|
+
}
|
|
7324
7364
|
function checkSkillStructure(facts) {
|
|
7325
7365
|
const out = [];
|
|
7326
7366
|
for (const skill of facts.skills) {
|
|
@@ -7328,7 +7368,7 @@ function checkSkillStructure(facts) {
|
|
|
7328
7368
|
continue;
|
|
7329
7369
|
}
|
|
7330
7370
|
if (!skill.hasSkillMd) {
|
|
7331
|
-
out.push(make("skill.missing-skill-md",
|
|
7371
|
+
out.push(make("skill.missing-skill-md", skillSubject(skill), {
|
|
7332
7372
|
action: "warn",
|
|
7333
7373
|
severity: "warning",
|
|
7334
7374
|
message: `Skill directory has no SKILL.md`,
|
|
@@ -7341,8 +7381,8 @@ function checkSkillStructure(facts) {
|
|
|
7341
7381
|
if (skill.unparseableFrontmatter === true) {
|
|
7342
7382
|
continue;
|
|
7343
7383
|
}
|
|
7344
|
-
if (!skill.hasFrontmatter) {
|
|
7345
|
-
out.push(make("skill.missing-frontmatter",
|
|
7384
|
+
if (skill.runtime !== "agents" && !skill.hasFrontmatter) {
|
|
7385
|
+
out.push(make("skill.missing-frontmatter", skillSubject(skill), {
|
|
7346
7386
|
action: "warn",
|
|
7347
7387
|
severity: "warning",
|
|
7348
7388
|
message: "SKILL.md has no YAML frontmatter block",
|
|
@@ -7353,10 +7393,10 @@ function checkSkillStructure(facts) {
|
|
|
7353
7393
|
continue;
|
|
7354
7394
|
}
|
|
7355
7395
|
const broken = skill.brokenReferences;
|
|
7356
|
-
if (broken !== undefined && broken.length > 0) {
|
|
7396
|
+
if (skill.runtime !== "agents" && broken !== undefined && broken.length > 0) {
|
|
7357
7397
|
const shown = broken.slice(0, 3).join(", ");
|
|
7358
7398
|
const rest = broken.length - Math.min(3, broken.length);
|
|
7359
|
-
out.push(make("skill.broken-reference",
|
|
7399
|
+
out.push(make("skill.broken-reference", skillSubject(skill), {
|
|
7360
7400
|
action: "warn",
|
|
7361
7401
|
severity: "warning",
|
|
7362
7402
|
message: `SKILL.md points at ${broken.length} file${broken.length === 1 ? "" : "s"} that do not exist: ${shown}${rest > 0 ? ` (+${rest} more)` : ""}`,
|
|
@@ -7368,8 +7408,8 @@ function checkSkillStructure(facts) {
|
|
|
7368
7408
|
suggest: `Restore the missing files under ${skill.path}, or drop the references from SKILL.md`
|
|
7369
7409
|
}));
|
|
7370
7410
|
}
|
|
7371
|
-
if (skill.description === undefined) {
|
|
7372
|
-
out.push(make("skill.missing-description",
|
|
7411
|
+
if (skill.runtime !== "agents" && skill.description === undefined) {
|
|
7412
|
+
out.push(make("skill.missing-description", skillSubject(skill), {
|
|
7373
7413
|
action: "warn",
|
|
7374
7414
|
severity: "info",
|
|
7375
7415
|
message: "SKILL.md frontmatter has no `description`",
|
|
@@ -7397,13 +7437,16 @@ function checkLockIntegrity(facts, options) {
|
|
|
7397
7437
|
}
|
|
7398
7438
|
return out;
|
|
7399
7439
|
}
|
|
7440
|
+
if (facts.skillsLockInvalid === true) {
|
|
7441
|
+
return out;
|
|
7442
|
+
}
|
|
7400
7443
|
const onDisk = new Set(projectSkills.map((s) => s.id));
|
|
7401
7444
|
const locked = new Map(facts.lockedSkills.map((l) => [l.id, l]));
|
|
7402
7445
|
for (const skill of projectSkills) {
|
|
7403
7446
|
if (locked.has(skill.id)) {
|
|
7404
7447
|
continue;
|
|
7405
7448
|
}
|
|
7406
|
-
out.push(make("skill.not-in-lock",
|
|
7449
|
+
out.push(make("skill.not-in-lock", skillSubject(skill), {
|
|
7407
7450
|
action: "warn",
|
|
7408
7451
|
severity: "info",
|
|
7409
7452
|
message: "Skill is not in skills-lock.json — local and unpinned",
|
|
@@ -7437,11 +7480,18 @@ function checkDuplicateDescriptions(facts) {
|
|
|
7437
7480
|
if (skill.source !== "project" || skill.description === undefined) {
|
|
7438
7481
|
continue;
|
|
7439
7482
|
}
|
|
7440
|
-
|
|
7483
|
+
if (skill.runtime === "agents") {
|
|
7484
|
+
continue;
|
|
7485
|
+
}
|
|
7486
|
+
const key = `${skillRoot(skill)}\x00${skill.description.trim().replace(/\s+/g, " ").toLowerCase()}`;
|
|
7441
7487
|
if (key.length === 0) {
|
|
7442
7488
|
continue;
|
|
7443
7489
|
}
|
|
7444
|
-
|
|
7490
|
+
const ids = groups.get(key);
|
|
7491
|
+
if (ids === undefined)
|
|
7492
|
+
groups.set(key, [skill.instanceId ?? skill.id]);
|
|
7493
|
+
else
|
|
7494
|
+
ids.push(skill.instanceId ?? skill.id);
|
|
7445
7495
|
}
|
|
7446
7496
|
const out = [];
|
|
7447
7497
|
for (const ids of groups.values()) {
|
|
@@ -7466,105 +7516,141 @@ function checkDescriptionBudget(facts, options) {
|
|
|
7466
7516
|
return [];
|
|
7467
7517
|
}
|
|
7468
7518
|
const project = facts.skills.filter((s) => s.source === "project" && s.hasSkillMd && s.description !== undefined);
|
|
7469
|
-
const
|
|
7470
|
-
|
|
7471
|
-
|
|
7519
|
+
const byRoot = new Map;
|
|
7520
|
+
for (const s of project) {
|
|
7521
|
+
const root = skillRoot(s);
|
|
7522
|
+
const bucket = byRoot.get(root);
|
|
7523
|
+
if (bucket === undefined)
|
|
7524
|
+
byRoot.set(root, [s]);
|
|
7525
|
+
else
|
|
7526
|
+
bucket.push(s);
|
|
7472
7527
|
}
|
|
7473
|
-
|
|
7474
|
-
|
|
7528
|
+
const out = [];
|
|
7529
|
+
for (const [root, skills] of byRoot) {
|
|
7530
|
+
const bytes = skills.reduce((sum, s) => sum + Buffer.byteLength(s.id) + Buffer.byteLength(s.description ?? ""), 0);
|
|
7531
|
+
if (bytes <= ceiling) {
|
|
7532
|
+
continue;
|
|
7533
|
+
}
|
|
7534
|
+
const where = root.startsWith(`${facts.root}/`) ? root.slice(facts.root.length + 1) : root;
|
|
7535
|
+
const subject = byRoot.size === 1 ? "skills:description-budget" : `skills:description-budget:${where}`;
|
|
7536
|
+
out.push(make("skill.description-budget", subject, {
|
|
7475
7537
|
action: "warn",
|
|
7476
7538
|
severity: "info",
|
|
7477
|
-
message: `Skill descriptions total ${bytes} bytes across ${
|
|
7539
|
+
message: `Skill descriptions total ${bytes} bytes across ${skills.length} skills in ${where} (over ${ceiling})`,
|
|
7478
7540
|
reason: "Names and descriptions for every skill are loaded at startup, within a budget of roughly 1-2% of the context window. Past it they are truncated, and a skill whose description is cut short stops being matched for the tasks it was written for.",
|
|
7479
7541
|
evidence: [
|
|
7480
7542
|
{ kind: "count", value: `descriptionBytes=${bytes}` },
|
|
7481
7543
|
{ kind: "threshold", value: String(ceiling) }
|
|
7482
7544
|
],
|
|
7483
7545
|
suggest: "Shorten the longest descriptions, or remove skills this project does not use"
|
|
7484
|
-
})
|
|
7485
|
-
|
|
7546
|
+
}));
|
|
7547
|
+
}
|
|
7548
|
+
return out;
|
|
7486
7549
|
}
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
}
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7550
|
+
// src/checks/registry.ts
|
|
7551
|
+
var STRUCTURAL_CHECKS = [
|
|
7552
|
+
{
|
|
7553
|
+
id: "config.unreadable",
|
|
7554
|
+
description: "Config file is not valid JSON or cannot be read"
|
|
7555
|
+
},
|
|
7556
|
+
{
|
|
7557
|
+
id: "scan.truncated",
|
|
7558
|
+
description: "File is larger than the scan cap, so body checks read only a prefix of it"
|
|
7559
|
+
},
|
|
7560
|
+
{
|
|
7561
|
+
id: "hook.unknown-event",
|
|
7562
|
+
description: "Hook registered under an event name that is never dispatched"
|
|
7563
|
+
},
|
|
7564
|
+
{
|
|
7565
|
+
id: "hook.missing-script",
|
|
7566
|
+
description: "Hook points at a script that does not exist, so it never runs"
|
|
7567
|
+
},
|
|
7568
|
+
{
|
|
7569
|
+
id: "skill.missing-skill-md",
|
|
7570
|
+
description: "Skill directory has no SKILL.md"
|
|
7571
|
+
},
|
|
7572
|
+
{
|
|
7573
|
+
id: "skill.missing-frontmatter",
|
|
7574
|
+
description: "SKILL.md has no YAML frontmatter block"
|
|
7575
|
+
},
|
|
7576
|
+
{
|
|
7577
|
+
id: "skill.missing-description",
|
|
7578
|
+
description: "SKILL.md frontmatter has no description (recommended field)"
|
|
7579
|
+
},
|
|
7580
|
+
{
|
|
7581
|
+
id: "skill.not-in-lock",
|
|
7582
|
+
description: "Skill on disk is not tracked by skills-lock.json"
|
|
7583
|
+
},
|
|
7584
|
+
{
|
|
7585
|
+
id: "skill.locked-not-installed",
|
|
7586
|
+
description: "skills-lock.json pins a skill that is not installed"
|
|
7587
|
+
},
|
|
7588
|
+
{
|
|
7589
|
+
id: "skill.broken-reference",
|
|
7590
|
+
description: "SKILL.md points at a bundled file that does not exist"
|
|
7591
|
+
},
|
|
7592
|
+
{
|
|
7593
|
+
id: "skill.duplicate-description",
|
|
7594
|
+
description: "Two or more skills share an identical description"
|
|
7595
|
+
},
|
|
7596
|
+
{
|
|
7597
|
+
id: "skill.description-budget",
|
|
7598
|
+
description: "Skill descriptions exceed the startup character budget"
|
|
7599
|
+
},
|
|
7600
|
+
{
|
|
7601
|
+
id: "skill.no-lockfile",
|
|
7602
|
+
description: "Skills present with no skills-lock.json (requireLock only)"
|
|
7603
|
+
},
|
|
7604
|
+
{
|
|
7605
|
+
id: "agent.missing-frontmatter",
|
|
7606
|
+
description: "Agent definition has no YAML frontmatter block"
|
|
7607
|
+
},
|
|
7608
|
+
{
|
|
7609
|
+
id: "agent.missing-description",
|
|
7610
|
+
description: "Agent frontmatter has no description"
|
|
7611
|
+
},
|
|
7612
|
+
{ id: "agent.missing-name", description: "Agent frontmatter has no name" },
|
|
7613
|
+
{ id: "agent.duplicate-name", description: "Agent names are duplicated" },
|
|
7614
|
+
{ id: "agent.invalid-name", description: "Agent frontmatter name is not a valid identifier" },
|
|
7615
|
+
{
|
|
7616
|
+
id: "mcp.no-launch",
|
|
7617
|
+
description: "MCP server declares neither command nor url"
|
|
7618
|
+
},
|
|
7619
|
+
{
|
|
7620
|
+
id: "mcp.command-missing",
|
|
7621
|
+
description: "MCP server command is a path that does not exist on disk"
|
|
7622
|
+
},
|
|
7623
|
+
{
|
|
7624
|
+
id: "mcp.url-without-type",
|
|
7625
|
+
description: "Remote MCP server has a url but no transport type"
|
|
7626
|
+
},
|
|
7627
|
+
{
|
|
7628
|
+
id: "mcp.hardcoded-secret",
|
|
7629
|
+
description: "MCP config contains a token-shaped literal"
|
|
7630
|
+
},
|
|
7631
|
+
{
|
|
7632
|
+
id: "mcp.literal-env",
|
|
7633
|
+
description: "MCP env value is a long literal instead of ${VAR}"
|
|
7634
|
+
},
|
|
7635
|
+
{
|
|
7636
|
+
id: "budget.agents-md",
|
|
7637
|
+
description: "AGENTS.md longer than the point where added lines stop helping (>150 lines)"
|
|
7638
|
+
},
|
|
7639
|
+
{
|
|
7640
|
+
id: "budget.claude-md",
|
|
7641
|
+
description: "CLAUDE.md longer than the instruction budget a model reliably follows (>200 lines)"
|
|
7642
|
+
},
|
|
7643
|
+
{
|
|
7644
|
+
id: "budget.agents",
|
|
7645
|
+
description: "More agent definitions than a focused set (>8 files in .claude/agents)"
|
|
7646
|
+
},
|
|
7647
|
+
{
|
|
7648
|
+
id: "budget.mcp",
|
|
7649
|
+
description: "Project MCP server count above the point where tool selection degrades"
|
|
7565
7650
|
}
|
|
7566
|
-
|
|
7567
|
-
|
|
7651
|
+
];
|
|
7652
|
+
|
|
7653
|
+
// src/checks/index.ts
|
|
7568
7654
|
function runChecks(facts, options = {}) {
|
|
7569
7655
|
return [
|
|
7570
7656
|
...checkConfigErrors(facts),
|
|
@@ -7587,113 +7673,113 @@ import { join } from "node:path";
|
|
|
7587
7673
|
// node_modules/zod/v3/external.js
|
|
7588
7674
|
var exports_external = {};
|
|
7589
7675
|
__export(exports_external, {
|
|
7590
|
-
|
|
7591
|
-
util: () => util,
|
|
7592
|
-
unknown: () => unknownType,
|
|
7593
|
-
union: () => unionType,
|
|
7594
|
-
undefined: () => undefinedType,
|
|
7595
|
-
tuple: () => tupleType,
|
|
7596
|
-
transformer: () => effectsType,
|
|
7597
|
-
symbol: () => symbolType,
|
|
7598
|
-
string: () => stringType,
|
|
7599
|
-
strictObject: () => strictObjectType,
|
|
7600
|
-
setErrorMap: () => setErrorMap,
|
|
7601
|
-
set: () => setType,
|
|
7602
|
-
record: () => recordType,
|
|
7603
|
-
quotelessJson: () => quotelessJson,
|
|
7604
|
-
promise: () => promiseType,
|
|
7605
|
-
preprocess: () => preprocessType,
|
|
7606
|
-
pipeline: () => pipelineType,
|
|
7607
|
-
ostring: () => ostring,
|
|
7608
|
-
optional: () => optionalType,
|
|
7609
|
-
onumber: () => onumber,
|
|
7610
|
-
oboolean: () => oboolean,
|
|
7611
|
-
objectUtil: () => objectUtil,
|
|
7612
|
-
object: () => objectType,
|
|
7613
|
-
number: () => numberType,
|
|
7614
|
-
nullable: () => nullableType,
|
|
7615
|
-
null: () => nullType,
|
|
7616
|
-
never: () => neverType,
|
|
7617
|
-
nativeEnum: () => nativeEnumType,
|
|
7618
|
-
nan: () => nanType,
|
|
7619
|
-
map: () => mapType,
|
|
7620
|
-
makeIssue: () => makeIssue,
|
|
7621
|
-
literal: () => literalType,
|
|
7622
|
-
lazy: () => lazyType,
|
|
7623
|
-
late: () => late,
|
|
7624
|
-
isValid: () => isValid,
|
|
7625
|
-
isDirty: () => isDirty,
|
|
7626
|
-
isAsync: () => isAsync,
|
|
7627
|
-
isAborted: () => isAborted,
|
|
7628
|
-
intersection: () => intersectionType,
|
|
7629
|
-
instanceof: () => instanceOfType,
|
|
7630
|
-
getParsedType: () => getParsedType,
|
|
7631
|
-
getErrorMap: () => getErrorMap,
|
|
7632
|
-
function: () => functionType,
|
|
7633
|
-
enum: () => enumType,
|
|
7634
|
-
effect: () => effectsType,
|
|
7635
|
-
discriminatedUnion: () => discriminatedUnionType,
|
|
7636
|
-
defaultErrorMap: () => en_default,
|
|
7637
|
-
datetimeRegex: () => datetimeRegex,
|
|
7638
|
-
date: () => dateType,
|
|
7639
|
-
custom: () => custom,
|
|
7640
|
-
coerce: () => coerce,
|
|
7641
|
-
boolean: () => booleanType,
|
|
7642
|
-
bigint: () => bigIntType,
|
|
7643
|
-
array: () => arrayType,
|
|
7644
|
-
any: () => anyType,
|
|
7645
|
-
addIssueToContext: () => addIssueToContext,
|
|
7646
|
-
ZodVoid: () => ZodVoid,
|
|
7647
|
-
ZodUnknown: () => ZodUnknown,
|
|
7648
|
-
ZodUnion: () => ZodUnion,
|
|
7649
|
-
ZodUndefined: () => ZodUndefined,
|
|
7650
|
-
ZodType: () => ZodType,
|
|
7651
|
-
ZodTuple: () => ZodTuple,
|
|
7652
|
-
ZodTransformer: () => ZodEffects,
|
|
7653
|
-
ZodSymbol: () => ZodSymbol,
|
|
7654
|
-
ZodString: () => ZodString,
|
|
7655
|
-
ZodSet: () => ZodSet,
|
|
7656
|
-
ZodSchema: () => ZodType,
|
|
7657
|
-
ZodRecord: () => ZodRecord,
|
|
7658
|
-
ZodReadonly: () => ZodReadonly,
|
|
7659
|
-
ZodPromise: () => ZodPromise,
|
|
7660
|
-
ZodPipeline: () => ZodPipeline,
|
|
7661
|
-
ZodParsedType: () => ZodParsedType,
|
|
7662
|
-
ZodOptional: () => ZodOptional,
|
|
7663
|
-
ZodObject: () => ZodObject,
|
|
7664
|
-
ZodNumber: () => ZodNumber,
|
|
7665
|
-
ZodNullable: () => ZodNullable,
|
|
7666
|
-
ZodNull: () => ZodNull,
|
|
7667
|
-
ZodNever: () => ZodNever,
|
|
7668
|
-
ZodNativeEnum: () => ZodNativeEnum,
|
|
7669
|
-
ZodNaN: () => ZodNaN,
|
|
7670
|
-
ZodMap: () => ZodMap,
|
|
7671
|
-
ZodLiteral: () => ZodLiteral,
|
|
7672
|
-
ZodLazy: () => ZodLazy,
|
|
7673
|
-
ZodIssueCode: () => ZodIssueCode,
|
|
7674
|
-
ZodIntersection: () => ZodIntersection,
|
|
7675
|
-
ZodFunction: () => ZodFunction,
|
|
7676
|
-
ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
|
|
7677
|
-
ZodError: () => ZodError,
|
|
7678
|
-
ZodEnum: () => ZodEnum,
|
|
7679
|
-
ZodEffects: () => ZodEffects,
|
|
7680
|
-
ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
|
|
7681
|
-
ZodDefault: () => ZodDefault,
|
|
7682
|
-
ZodDate: () => ZodDate,
|
|
7683
|
-
ZodCatch: () => ZodCatch,
|
|
7684
|
-
ZodBranded: () => ZodBranded,
|
|
7685
|
-
ZodBoolean: () => ZodBoolean,
|
|
7686
|
-
ZodBigInt: () => ZodBigInt,
|
|
7687
|
-
ZodArray: () => ZodArray,
|
|
7688
|
-
ZodAny: () => ZodAny,
|
|
7689
|
-
Schema: () => ZodType,
|
|
7690
|
-
ParseStatus: () => ParseStatus,
|
|
7691
|
-
OK: () => OK,
|
|
7692
|
-
NEVER: () => NEVER,
|
|
7693
|
-
INVALID: () => INVALID,
|
|
7694
|
-
EMPTY_PATH: () => EMPTY_PATH,
|
|
7676
|
+
BRAND: () => BRAND,
|
|
7695
7677
|
DIRTY: () => DIRTY,
|
|
7696
|
-
|
|
7678
|
+
EMPTY_PATH: () => EMPTY_PATH,
|
|
7679
|
+
INVALID: () => INVALID,
|
|
7680
|
+
NEVER: () => NEVER,
|
|
7681
|
+
OK: () => OK,
|
|
7682
|
+
ParseStatus: () => ParseStatus,
|
|
7683
|
+
Schema: () => ZodType,
|
|
7684
|
+
ZodAny: () => ZodAny,
|
|
7685
|
+
ZodArray: () => ZodArray,
|
|
7686
|
+
ZodBigInt: () => ZodBigInt,
|
|
7687
|
+
ZodBoolean: () => ZodBoolean,
|
|
7688
|
+
ZodBranded: () => ZodBranded,
|
|
7689
|
+
ZodCatch: () => ZodCatch,
|
|
7690
|
+
ZodDate: () => ZodDate,
|
|
7691
|
+
ZodDefault: () => ZodDefault,
|
|
7692
|
+
ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
|
|
7693
|
+
ZodEffects: () => ZodEffects,
|
|
7694
|
+
ZodEnum: () => ZodEnum,
|
|
7695
|
+
ZodError: () => ZodError,
|
|
7696
|
+
ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
|
|
7697
|
+
ZodFunction: () => ZodFunction,
|
|
7698
|
+
ZodIntersection: () => ZodIntersection,
|
|
7699
|
+
ZodIssueCode: () => ZodIssueCode,
|
|
7700
|
+
ZodLazy: () => ZodLazy,
|
|
7701
|
+
ZodLiteral: () => ZodLiteral,
|
|
7702
|
+
ZodMap: () => ZodMap,
|
|
7703
|
+
ZodNaN: () => ZodNaN,
|
|
7704
|
+
ZodNativeEnum: () => ZodNativeEnum,
|
|
7705
|
+
ZodNever: () => ZodNever,
|
|
7706
|
+
ZodNull: () => ZodNull,
|
|
7707
|
+
ZodNullable: () => ZodNullable,
|
|
7708
|
+
ZodNumber: () => ZodNumber,
|
|
7709
|
+
ZodObject: () => ZodObject,
|
|
7710
|
+
ZodOptional: () => ZodOptional,
|
|
7711
|
+
ZodParsedType: () => ZodParsedType,
|
|
7712
|
+
ZodPipeline: () => ZodPipeline,
|
|
7713
|
+
ZodPromise: () => ZodPromise,
|
|
7714
|
+
ZodReadonly: () => ZodReadonly,
|
|
7715
|
+
ZodRecord: () => ZodRecord,
|
|
7716
|
+
ZodSchema: () => ZodType,
|
|
7717
|
+
ZodSet: () => ZodSet,
|
|
7718
|
+
ZodString: () => ZodString,
|
|
7719
|
+
ZodSymbol: () => ZodSymbol,
|
|
7720
|
+
ZodTransformer: () => ZodEffects,
|
|
7721
|
+
ZodTuple: () => ZodTuple,
|
|
7722
|
+
ZodType: () => ZodType,
|
|
7723
|
+
ZodUndefined: () => ZodUndefined,
|
|
7724
|
+
ZodUnion: () => ZodUnion,
|
|
7725
|
+
ZodUnknown: () => ZodUnknown,
|
|
7726
|
+
ZodVoid: () => ZodVoid,
|
|
7727
|
+
addIssueToContext: () => addIssueToContext,
|
|
7728
|
+
any: () => anyType,
|
|
7729
|
+
array: () => arrayType,
|
|
7730
|
+
bigint: () => bigIntType,
|
|
7731
|
+
boolean: () => booleanType,
|
|
7732
|
+
coerce: () => coerce,
|
|
7733
|
+
custom: () => custom,
|
|
7734
|
+
date: () => dateType,
|
|
7735
|
+
datetimeRegex: () => datetimeRegex,
|
|
7736
|
+
defaultErrorMap: () => en_default,
|
|
7737
|
+
discriminatedUnion: () => discriminatedUnionType,
|
|
7738
|
+
effect: () => effectsType,
|
|
7739
|
+
enum: () => enumType,
|
|
7740
|
+
function: () => functionType,
|
|
7741
|
+
getErrorMap: () => getErrorMap,
|
|
7742
|
+
getParsedType: () => getParsedType,
|
|
7743
|
+
instanceof: () => instanceOfType,
|
|
7744
|
+
intersection: () => intersectionType,
|
|
7745
|
+
isAborted: () => isAborted,
|
|
7746
|
+
isAsync: () => isAsync,
|
|
7747
|
+
isDirty: () => isDirty,
|
|
7748
|
+
isValid: () => isValid,
|
|
7749
|
+
late: () => late,
|
|
7750
|
+
lazy: () => lazyType,
|
|
7751
|
+
literal: () => literalType,
|
|
7752
|
+
makeIssue: () => makeIssue,
|
|
7753
|
+
map: () => mapType,
|
|
7754
|
+
nan: () => nanType,
|
|
7755
|
+
nativeEnum: () => nativeEnumType,
|
|
7756
|
+
never: () => neverType,
|
|
7757
|
+
null: () => nullType,
|
|
7758
|
+
nullable: () => nullableType,
|
|
7759
|
+
number: () => numberType,
|
|
7760
|
+
object: () => objectType,
|
|
7761
|
+
objectUtil: () => objectUtil,
|
|
7762
|
+
oboolean: () => oboolean,
|
|
7763
|
+
onumber: () => onumber,
|
|
7764
|
+
optional: () => optionalType,
|
|
7765
|
+
ostring: () => ostring,
|
|
7766
|
+
pipeline: () => pipelineType,
|
|
7767
|
+
preprocess: () => preprocessType,
|
|
7768
|
+
promise: () => promiseType,
|
|
7769
|
+
quotelessJson: () => quotelessJson,
|
|
7770
|
+
record: () => recordType,
|
|
7771
|
+
set: () => setType,
|
|
7772
|
+
setErrorMap: () => setErrorMap,
|
|
7773
|
+
strictObject: () => strictObjectType,
|
|
7774
|
+
string: () => stringType,
|
|
7775
|
+
symbol: () => symbolType,
|
|
7776
|
+
transformer: () => effectsType,
|
|
7777
|
+
tuple: () => tupleType,
|
|
7778
|
+
undefined: () => undefinedType,
|
|
7779
|
+
union: () => unionType,
|
|
7780
|
+
unknown: () => unknownType,
|
|
7781
|
+
util: () => util,
|
|
7782
|
+
void: () => voidType
|
|
7697
7783
|
});
|
|
7698
7784
|
|
|
7699
7785
|
// node_modules/zod/v3/helpers/util.js
|
|
@@ -11560,7 +11646,6 @@ var NEVER = INVALID;
|
|
|
11560
11646
|
// src/config/schema.ts
|
|
11561
11647
|
var defaultThresholds = {
|
|
11562
11648
|
skillDescriptionBytes: 16000,
|
|
11563
|
-
skills: 30,
|
|
11564
11649
|
mcp: 5,
|
|
11565
11650
|
agentsMdLines: 150,
|
|
11566
11651
|
claudeMdLines: 200,
|
|
@@ -11580,12 +11665,12 @@ var defaultConfig = {
|
|
|
11580
11665
|
};
|
|
11581
11666
|
var thresholdsSchema = exports_external.object({
|
|
11582
11667
|
skillDescriptionBytes: exports_external.number().int().positive().default(defaultThresholds.skillDescriptionBytes),
|
|
11583
|
-
skills: exports_external.number().int().positive().
|
|
11668
|
+
skills: exports_external.number().int().positive().optional(),
|
|
11584
11669
|
mcp: exports_external.number().int().positive().default(defaultThresholds.mcp),
|
|
11585
11670
|
agentsMdLines: exports_external.number().int().positive().default(defaultThresholds.agentsMdLines),
|
|
11586
11671
|
claudeMdLines: exports_external.number().int().positive().default(defaultThresholds.claudeMdLines),
|
|
11587
11672
|
agents: exports_external.number().int().positive().default(defaultThresholds.agents)
|
|
11588
|
-
});
|
|
11673
|
+
}).strict();
|
|
11589
11674
|
var configSchema = exports_external.object({
|
|
11590
11675
|
skillPaths: exports_external.array(exports_external.string()).default(defaultConfig.skillPaths),
|
|
11591
11676
|
mcpPaths: exports_external.array(exports_external.string()).default(defaultConfig.mcpPaths),
|
|
@@ -11597,7 +11682,7 @@ var configSchema = exports_external.object({
|
|
|
11597
11682
|
includeGlobal: exports_external.boolean().default(false),
|
|
11598
11683
|
requireLock: exports_external.boolean().default(false),
|
|
11599
11684
|
thresholds: thresholdsSchema.default(defaultThresholds)
|
|
11600
|
-
});
|
|
11685
|
+
}).strict();
|
|
11601
11686
|
|
|
11602
11687
|
// src/config/load.ts
|
|
11603
11688
|
function loadConfig(root, configPath) {
|
|
@@ -11617,9 +11702,16 @@ function loadConfig(root, configPath) {
|
|
|
11617
11702
|
}
|
|
11618
11703
|
|
|
11619
11704
|
// src/discover/index.ts
|
|
11620
|
-
import {
|
|
11621
|
-
import {
|
|
11622
|
-
|
|
11705
|
+
import { homedir as homedir3 } from "node:os";
|
|
11706
|
+
import { join as join8, resolve as resolve3 } from "node:path";
|
|
11707
|
+
|
|
11708
|
+
// src/discover/agents.ts
|
|
11709
|
+
import { existsSync as existsSync3, readdirSync, statSync } from "node:fs";
|
|
11710
|
+
import { join as join3 } from "node:path";
|
|
11711
|
+
|
|
11712
|
+
// src/discover/shared.ts
|
|
11713
|
+
import { closeSync, existsSync as existsSync2, openSync, readFileSync as readFileSync2, readSync } from "node:fs";
|
|
11714
|
+
import { join as join2, resolve } from "node:path";
|
|
11623
11715
|
|
|
11624
11716
|
// node_modules/yaml/dist/index.js
|
|
11625
11717
|
var composer = require_composer();
|
|
@@ -11667,18 +11759,40 @@ var $stringify = publicApi.stringify;
|
|
|
11667
11759
|
var $visit = visit.visit;
|
|
11668
11760
|
var $visitAsync = visit.visitAsync;
|
|
11669
11761
|
|
|
11670
|
-
// src/discover/
|
|
11762
|
+
// src/discover/shared.ts
|
|
11671
11763
|
var POLICY_CAP = 1e5;
|
|
11672
11764
|
var SKILL_MD_CAP = 65536;
|
|
11765
|
+
function readCapped(path, cap) {
|
|
11766
|
+
const fd = openSync(path, "r");
|
|
11767
|
+
try {
|
|
11768
|
+
const buf = Buffer.allocUnsafe(cap + 1);
|
|
11769
|
+
const bytes = readSync(fd, buf, 0, cap + 1, 0);
|
|
11770
|
+
return { buf: buf.subarray(0, bytes), truncated: bytes > cap };
|
|
11771
|
+
} finally {
|
|
11772
|
+
closeSync(fd);
|
|
11773
|
+
}
|
|
11774
|
+
}
|
|
11775
|
+
var AGENT_CONFIG_SIGNALS = [
|
|
11776
|
+
".claude",
|
|
11777
|
+
".agents",
|
|
11778
|
+
".mcp.json",
|
|
11779
|
+
"mcp.json",
|
|
11780
|
+
"AGENTS.md",
|
|
11781
|
+
"CLAUDE.md",
|
|
11782
|
+
"skills-lock.json"
|
|
11783
|
+
];
|
|
11784
|
+
function hasAgentConfigSignal(dir) {
|
|
11785
|
+
return AGENT_CONFIG_SIGNALS.some((name) => existsSync2(join2(dir, name)));
|
|
11786
|
+
}
|
|
11673
11787
|
function resolveRoot(startDir) {
|
|
11674
11788
|
let dir = resolve(startDir);
|
|
11675
11789
|
for (;; ) {
|
|
11676
|
-
if (existsSync2(join2(dir, "package.json"))) {
|
|
11790
|
+
if (existsSync2(join2(dir, "package.json")) || hasAgentConfigSignal(dir)) {
|
|
11677
11791
|
return dir;
|
|
11678
11792
|
}
|
|
11679
11793
|
const parent = resolve(dir, "..");
|
|
11680
11794
|
if (parent === dir) {
|
|
11681
|
-
throw new Error(`No package.json found walking up from ${resolve(startDir)}`);
|
|
11795
|
+
throw new Error(`No package.json or agent configuration found walking up from ${resolve(startDir)}`);
|
|
11682
11796
|
}
|
|
11683
11797
|
dir = parent;
|
|
11684
11798
|
}
|
|
@@ -11706,25 +11820,6 @@ function readJsonConfig(path, errors3) {
|
|
|
11706
11820
|
return;
|
|
11707
11821
|
}
|
|
11708
11822
|
}
|
|
11709
|
-
var BUNDLED = "scripts|references|assets|templates|examples";
|
|
11710
|
-
var REFERENCE_RE = new RegExp(`(?<![\\w/.-])(?:\\./)?((?:${BUNDLED})/[A-Za-z0-9_./-]+\\.[A-Za-z0-9]{1,10})(?![A-Za-z0-9_.-])`, "g");
|
|
11711
|
-
var FENCE_BLOCK = /^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?(?:^[ \t]*\1[ \t]*$|$)/gm;
|
|
11712
|
-
var INLINE_CODE = /`[^`\n]*`/g;
|
|
11713
|
-
var URLS = /\b[a-z][a-z0-9+.-]*:\/\/\S+/gi;
|
|
11714
|
-
function skillReferences(body) {
|
|
11715
|
-
const prose = body.replace(FENCE_BLOCK, "").replace(INLINE_CODE, "").replace(URLS, "");
|
|
11716
|
-
const out = [];
|
|
11717
|
-
const seen = new Set;
|
|
11718
|
-
for (const match of prose.matchAll(REFERENCE_RE)) {
|
|
11719
|
-
const path = match[1];
|
|
11720
|
-
if (path === undefined || seen.has(path)) {
|
|
11721
|
-
continue;
|
|
11722
|
-
}
|
|
11723
|
-
seen.add(path);
|
|
11724
|
-
out.push(path);
|
|
11725
|
-
}
|
|
11726
|
-
return out;
|
|
11727
|
-
}
|
|
11728
11823
|
function scalar(value) {
|
|
11729
11824
|
if (typeof value === "string") {
|
|
11730
11825
|
const trimmed = value.trim();
|
|
@@ -11739,14 +11834,17 @@ function readFrontmatter(skillMdPath, errors3) {
|
|
|
11739
11834
|
let text;
|
|
11740
11835
|
let truncated = false;
|
|
11741
11836
|
try {
|
|
11742
|
-
const
|
|
11743
|
-
truncated =
|
|
11744
|
-
text = buf.subarray(0, SKILL_MD_CAP).toString("utf8");
|
|
11837
|
+
const result = readCapped(skillMdPath, SKILL_MD_CAP);
|
|
11838
|
+
truncated = result.truncated;
|
|
11839
|
+
text = result.buf.subarray(0, SKILL_MD_CAP).toString("utf8");
|
|
11745
11840
|
if (text.charCodeAt(0) === 65279) {
|
|
11746
11841
|
text = text.slice(1);
|
|
11747
11842
|
}
|
|
11748
11843
|
text = text.replace(/\r\n/g, `
|
|
11749
11844
|
`);
|
|
11845
|
+
if (truncated) {
|
|
11846
|
+
errors3.push({ path: skillMdPath, kind: "truncated", detail: `file exceeds ${SKILL_MD_CAP} byte scan cap` });
|
|
11847
|
+
}
|
|
11750
11848
|
} catch (err) {
|
|
11751
11849
|
errors3.push({
|
|
11752
11850
|
path: skillMdPath,
|
|
@@ -11803,23 +11901,12 @@ function readFrontmatter(skillMdPath, errors3) {
|
|
|
11803
11901
|
}
|
|
11804
11902
|
return out;
|
|
11805
11903
|
}
|
|
11806
|
-
|
|
11807
|
-
|
|
11808
|
-
|
|
11809
|
-
return statSync(p).isFile();
|
|
11810
|
-
} catch {
|
|
11811
|
-
return false;
|
|
11812
|
-
}
|
|
11813
|
-
};
|
|
11814
|
-
return skillReferences(body).filter((rel) => !isFile(join2(skillDir, rel)) && !isFile(join2(root, rel)));
|
|
11815
|
-
}
|
|
11816
|
-
function discoverSkillsInDir(dir, source, errors3, root) {
|
|
11817
|
-
if (!existsSync2(dir)) {
|
|
11818
|
-
return [];
|
|
11819
|
-
}
|
|
11904
|
+
|
|
11905
|
+
// src/discover/agents.ts
|
|
11906
|
+
function agentFiles(dir, errors3) {
|
|
11820
11907
|
let entries;
|
|
11821
11908
|
try {
|
|
11822
|
-
entries = readdirSync(dir);
|
|
11909
|
+
entries = readdirSync(dir, { withFileTypes: true }).map((e) => e.isDirectory() ? `${e.name}/` : e.name);
|
|
11823
11910
|
} catch (err) {
|
|
11824
11911
|
errors3.push({
|
|
11825
11912
|
path: dir,
|
|
@@ -11828,144 +11915,66 @@ function discoverSkillsInDir(dir, source, errors3, root) {
|
|
|
11828
11915
|
});
|
|
11829
11916
|
return [];
|
|
11830
11917
|
}
|
|
11831
|
-
const
|
|
11832
|
-
for (const
|
|
11833
|
-
if (
|
|
11918
|
+
const out = [];
|
|
11919
|
+
for (const entry of entries) {
|
|
11920
|
+
if (entry.startsWith(".")) {
|
|
11834
11921
|
continue;
|
|
11835
11922
|
}
|
|
11836
|
-
|
|
11837
|
-
|
|
11838
|
-
try {
|
|
11839
|
-
st = statSync(skillDir);
|
|
11840
|
-
} catch {
|
|
11923
|
+
if (entry.endsWith("/")) {
|
|
11924
|
+
out.push(...agentFiles(join3(dir, entry.slice(0, -1)), errors3).map((p) => join3(entry.slice(0, -1), p)));
|
|
11841
11925
|
continue;
|
|
11842
11926
|
}
|
|
11843
|
-
|
|
11927
|
+
out.push(entry);
|
|
11928
|
+
}
|
|
11929
|
+
return out;
|
|
11930
|
+
}
|
|
11931
|
+
function discoverAgents(root, errors3) {
|
|
11932
|
+
const dir = join3(root, ".claude", "agents");
|
|
11933
|
+
if (!existsSync3(dir)) {
|
|
11934
|
+
return [];
|
|
11935
|
+
}
|
|
11936
|
+
const entries = agentFiles(dir, errors3);
|
|
11937
|
+
const facts = [];
|
|
11938
|
+
for (const name of entries) {
|
|
11939
|
+
if (!name.endsWith(".md")) {
|
|
11844
11940
|
continue;
|
|
11845
11941
|
}
|
|
11846
|
-
const
|
|
11847
|
-
let dirReadable = true;
|
|
11942
|
+
const filePath = join3(dir, name);
|
|
11848
11943
|
try {
|
|
11849
|
-
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
detail: err instanceof Error ? err.message : String(err)
|
|
11856
|
-
});
|
|
11944
|
+
const st = statSync(filePath);
|
|
11945
|
+
if (!st.isFile()) {
|
|
11946
|
+
continue;
|
|
11947
|
+
}
|
|
11948
|
+
} catch {
|
|
11949
|
+
continue;
|
|
11857
11950
|
}
|
|
11858
|
-
const
|
|
11859
|
-
const fm = hasSkillMd ? readFrontmatter(skillMd, errors3) : { hasFrontmatter: false };
|
|
11951
|
+
const fm = readFrontmatter(filePath, errors3);
|
|
11860
11952
|
const fact = {
|
|
11861
|
-
|
|
11862
|
-
path:
|
|
11863
|
-
source,
|
|
11864
|
-
hasSkillMd,
|
|
11953
|
+
name: name.slice(0, -".md".length),
|
|
11954
|
+
path: filePath,
|
|
11865
11955
|
hasFrontmatter: fm.hasFrontmatter
|
|
11866
11956
|
};
|
|
11867
|
-
if (fm.unreadable === true
|
|
11957
|
+
if (fm.unreadable === true) {
|
|
11868
11958
|
fact.unreadable = true;
|
|
11869
11959
|
}
|
|
11870
11960
|
if (fm.unparseable === true) {
|
|
11871
11961
|
fact.unparseableFrontmatter = true;
|
|
11872
11962
|
}
|
|
11873
|
-
if (fm.body !== undefined) {
|
|
11874
|
-
const broken = brokenReferences(fm.body, skillDir, root);
|
|
11875
|
-
if (broken.length > 0) {
|
|
11876
|
-
fact.brokenReferences = broken;
|
|
11877
|
-
}
|
|
11878
|
-
}
|
|
11879
|
-
if (fm.description !== undefined) {
|
|
11880
|
-
fact.description = fm.description;
|
|
11881
|
-
}
|
|
11882
11963
|
if (fm.name !== undefined) {
|
|
11883
11964
|
fact.frontmatterName = fm.name;
|
|
11884
11965
|
}
|
|
11885
|
-
|
|
11886
|
-
|
|
11887
|
-
return skills;
|
|
11888
|
-
}
|
|
11889
|
-
function parseMcpServers(raw, filePath, errors3) {
|
|
11890
|
-
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
11891
|
-
errors3.push({
|
|
11892
|
-
path: filePath,
|
|
11893
|
-
kind: "unexpected-shape",
|
|
11894
|
-
detail: "MCP config root is not a JSON object"
|
|
11895
|
-
});
|
|
11896
|
-
return [];
|
|
11897
|
-
}
|
|
11898
|
-
const obj = raw;
|
|
11899
|
-
let servers;
|
|
11900
|
-
if ("mcpServers" in obj && obj.mcpServers !== null && typeof obj.mcpServers === "object" && !Array.isArray(obj.mcpServers)) {
|
|
11901
|
-
servers = obj.mcpServers;
|
|
11902
|
-
} else if (!("mcpServers" in obj) && Object.keys(obj).length > 0 && Object.values(obj).every((v) => v !== null && typeof v === "object" && !Array.isArray(v) && (("command" in v) || ("url" in v) || ("type" in v)))) {
|
|
11903
|
-
servers = obj;
|
|
11904
|
-
} else {
|
|
11905
|
-
errors3.push({
|
|
11906
|
-
path: filePath,
|
|
11907
|
-
kind: "unexpected-shape",
|
|
11908
|
-
detail: "no usable `mcpServers` object and the root is not a server map"
|
|
11909
|
-
});
|
|
11910
|
-
return [];
|
|
11911
|
-
}
|
|
11912
|
-
const facts = [];
|
|
11913
|
-
for (const [name, value] of Object.entries(servers)) {
|
|
11914
|
-
let hasCommand = false;
|
|
11915
|
-
let hasUrl = false;
|
|
11916
|
-
let transport;
|
|
11917
|
-
const literalEnvKeys = [];
|
|
11918
|
-
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
11919
|
-
const entry = value;
|
|
11920
|
-
hasCommand = typeof entry.command === "string" && entry.command.length > 0;
|
|
11921
|
-
hasUrl = typeof entry.url === "string" && entry.url.length > 0;
|
|
11922
|
-
if (typeof entry.type === "string" && entry.type.length > 0) {
|
|
11923
|
-
transport = entry.type;
|
|
11924
|
-
}
|
|
11925
|
-
const env = entry.env;
|
|
11926
|
-
if (env !== null && typeof env === "object" && !Array.isArray(env)) {
|
|
11927
|
-
for (const [key, val] of Object.entries(env)) {
|
|
11928
|
-
if (typeof val === "string" && val.length > 0 && !val.includes("${") && !val.startsWith("$") && /(?:TOKEN|SECRET|KEY|PASSWORD|PASSWD|CREDENTIAL)S?$/i.test(key)) {
|
|
11929
|
-
literalEnvKeys.push(key);
|
|
11930
|
-
}
|
|
11931
|
-
}
|
|
11932
|
-
}
|
|
11933
|
-
}
|
|
11934
|
-
facts.push({
|
|
11935
|
-
name,
|
|
11936
|
-
path: filePath,
|
|
11937
|
-
hasCommand,
|
|
11938
|
-
hasUrl,
|
|
11939
|
-
...transport === undefined ? {} : { transport },
|
|
11940
|
-
literalEnvKeys,
|
|
11941
|
-
raw: JSON.stringify(value)
|
|
11942
|
-
});
|
|
11943
|
-
}
|
|
11944
|
-
return facts;
|
|
11945
|
-
}
|
|
11946
|
-
function discoverMcp(root, mcpPaths, errors3) {
|
|
11947
|
-
const facts = [];
|
|
11948
|
-
const seen = new Set;
|
|
11949
|
-
for (const rel of mcpPaths) {
|
|
11950
|
-
const filePath = join2(root, rel);
|
|
11951
|
-
if (!existsSync2(filePath)) {
|
|
11952
|
-
continue;
|
|
11953
|
-
}
|
|
11954
|
-
const raw = readJsonConfig(filePath, errors3);
|
|
11955
|
-
if (raw === undefined) {
|
|
11956
|
-
continue;
|
|
11957
|
-
}
|
|
11958
|
-
for (const fact of parseMcpServers(raw, filePath, errors3)) {
|
|
11959
|
-
const key = `${fact.name}@${fact.path}`;
|
|
11960
|
-
if (seen.has(key)) {
|
|
11961
|
-
continue;
|
|
11962
|
-
}
|
|
11963
|
-
seen.add(key);
|
|
11964
|
-
facts.push(fact);
|
|
11966
|
+
if (fm.description !== undefined) {
|
|
11967
|
+
fact.description = fm.description;
|
|
11965
11968
|
}
|
|
11969
|
+
facts.push(fact);
|
|
11966
11970
|
}
|
|
11967
11971
|
return facts;
|
|
11968
11972
|
}
|
|
11973
|
+
|
|
11974
|
+
// src/discover/hooks.ts
|
|
11975
|
+
import { existsSync as existsSync4, statSync as statSync2 } from "node:fs";
|
|
11976
|
+
import { homedir } from "node:os";
|
|
11977
|
+
import { isAbsolute, join as join4 } from "node:path";
|
|
11969
11978
|
var INTERPRETERS = /^(?:bash|sh|zsh|python3?|node|bun|deno)$/;
|
|
11970
11979
|
var INLINE_CODE_FLAGS = /^-(?:e|p|c)$/;
|
|
11971
11980
|
var SHELL_METACHARS = /[|;&`]|\$\(|\|\||&&/;
|
|
@@ -11981,7 +11990,9 @@ function hookScriptPath(command) {
|
|
|
11981
11990
|
const unquote = (t) => t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'") ? t.slice(1, -1) : t;
|
|
11982
11991
|
let i = 0;
|
|
11983
11992
|
const first = unquote(tokens[0]);
|
|
11993
|
+
let interpreted = false;
|
|
11984
11994
|
if (INTERPRETERS.test(first)) {
|
|
11995
|
+
interpreted = true;
|
|
11985
11996
|
i = 1;
|
|
11986
11997
|
while (i < tokens.length) {
|
|
11987
11998
|
const flag = unquote(tokens[i]);
|
|
@@ -11995,7 +12006,7 @@ function hookScriptPath(command) {
|
|
|
11995
12006
|
}
|
|
11996
12007
|
}
|
|
11997
12008
|
const candidate = tokens[i] === undefined ? undefined : unquote(tokens[i]);
|
|
11998
|
-
if (candidate === undefined || !candidate.includes("/")) {
|
|
12009
|
+
if (candidate === undefined || !candidate.includes("/") && !(interpreted && /\.(?:js|mjs|cjs|ts|py|sh|bash|zsh)$/i.test(candidate))) {
|
|
11999
12010
|
return;
|
|
12000
12011
|
}
|
|
12001
12012
|
if (/^[A-Za-z]:[/\\]/.test(candidate)) {
|
|
@@ -12016,21 +12027,25 @@ function resolveHookScript(root, raw) {
|
|
|
12016
12027
|
}
|
|
12017
12028
|
let expanded = extracted;
|
|
12018
12029
|
if (expanded.startsWith("~/")) {
|
|
12019
|
-
expanded =
|
|
12030
|
+
expanded = join4(homedir(), expanded.slice(2));
|
|
12020
12031
|
} else if (expanded.startsWith("$")) {
|
|
12021
12032
|
expanded = expanded.replace(/^\$(?:CLAUDE_PROJECT_DIR|\{CLAUDE_PROJECT_DIR\})\/?/, "");
|
|
12022
12033
|
}
|
|
12023
|
-
const abs = isAbsolute(expanded) ? expanded :
|
|
12024
|
-
|
|
12034
|
+
const abs = isAbsolute(expanded) ? expanded : join4(root, expanded);
|
|
12035
|
+
try {
|
|
12036
|
+
return { scriptPath: extracted, exists: statSync2(abs).isFile() };
|
|
12037
|
+
} catch {
|
|
12038
|
+
return { scriptPath: extracted, exists: false };
|
|
12039
|
+
}
|
|
12025
12040
|
}
|
|
12026
12041
|
function discoverHooks(root, errors3) {
|
|
12027
12042
|
const files = [
|
|
12028
|
-
|
|
12029
|
-
|
|
12043
|
+
join4(root, ".claude", "settings.json"),
|
|
12044
|
+
join4(root, ".claude", "settings.local.json")
|
|
12030
12045
|
];
|
|
12031
12046
|
const facts = [];
|
|
12032
12047
|
for (const filePath of files) {
|
|
12033
|
-
if (!
|
|
12048
|
+
if (!existsSync4(filePath)) {
|
|
12034
12049
|
continue;
|
|
12035
12050
|
}
|
|
12036
12051
|
const raw = readJsonConfig(filePath, errors3);
|
|
@@ -12058,6 +12073,25 @@ function discoverHooks(root, errors3) {
|
|
|
12058
12073
|
continue;
|
|
12059
12074
|
}
|
|
12060
12075
|
for (const [event, groups] of Object.entries(hooks)) {
|
|
12076
|
+
const malformed = !Array.isArray(groups) || groups.some((group) => {
|
|
12077
|
+
if (group === null || typeof group !== "object" || Array.isArray(group)) {
|
|
12078
|
+
return true;
|
|
12079
|
+
}
|
|
12080
|
+
const inner = group.hooks;
|
|
12081
|
+
if (!Array.isArray(inner))
|
|
12082
|
+
return true;
|
|
12083
|
+
return inner.some((entry) => {
|
|
12084
|
+
if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
|
|
12085
|
+
return true;
|
|
12086
|
+
}
|
|
12087
|
+
const item = entry;
|
|
12088
|
+
return item.command !== undefined && (typeof item.command !== "string" || item.command.length === 0) || item.type === "command" && typeof item.command !== "string";
|
|
12089
|
+
});
|
|
12090
|
+
});
|
|
12091
|
+
if (malformed) {
|
|
12092
|
+
errors3.push({ path: filePath, kind: "unexpected-shape", detail: `invalid hook groups for ${event}` });
|
|
12093
|
+
continue;
|
|
12094
|
+
}
|
|
12061
12095
|
const commands = collectHookCommands(groups);
|
|
12062
12096
|
if (commands.length === 0) {
|
|
12063
12097
|
facts.push({ name: event, path: filePath, event });
|
|
@@ -12098,87 +12132,160 @@ function collectHookCommands(groups) {
|
|
|
12098
12132
|
out.push(command);
|
|
12099
12133
|
}
|
|
12100
12134
|
}
|
|
12101
|
-
}
|
|
12102
|
-
return out;
|
|
12103
|
-
}
|
|
12104
|
-
|
|
12105
|
-
|
|
12106
|
-
|
|
12107
|
-
|
|
12108
|
-
|
|
12109
|
-
|
|
12110
|
-
|
|
12111
|
-
|
|
12112
|
-
|
|
12135
|
+
}
|
|
12136
|
+
return out;
|
|
12137
|
+
}
|
|
12138
|
+
|
|
12139
|
+
// src/discover/mcp.ts
|
|
12140
|
+
import { existsSync as existsSync5, statSync as statSync3 } from "node:fs";
|
|
12141
|
+
import { homedir as homedir2 } from "node:os";
|
|
12142
|
+
import { isAbsolute as isAbsolute2, join as join5 } from "node:path";
|
|
12143
|
+
var SHELL_METACHARS2 = /[|;&`]|\$\(|\|\||&&|\s/;
|
|
12144
|
+
function mcpCommandPath(command) {
|
|
12145
|
+
const trimmed = command.trim();
|
|
12146
|
+
if (trimmed.length === 0 || SHELL_METACHARS2.test(trimmed)) {
|
|
12147
|
+
return;
|
|
12148
|
+
}
|
|
12149
|
+
if (/^[A-Za-z]:[/\\]/.test(trimmed)) {
|
|
12150
|
+
return;
|
|
12151
|
+
}
|
|
12152
|
+
if (trimmed.startsWith("$")) {
|
|
12153
|
+
return /^\$(?:CLAUDE_PROJECT_DIR\b|\{CLAUDE_PROJECT_DIR\})/.test(trimmed) ? trimmed : undefined;
|
|
12154
|
+
}
|
|
12155
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("~/") || trimmed.startsWith(".")) {
|
|
12156
|
+
return trimmed;
|
|
12157
|
+
}
|
|
12158
|
+
if (trimmed.includes("/")) {
|
|
12159
|
+
return trimmed;
|
|
12160
|
+
}
|
|
12161
|
+
return;
|
|
12162
|
+
}
|
|
12163
|
+
function resolveMcpCommand(root, raw) {
|
|
12164
|
+
const extracted = mcpCommandPath(raw);
|
|
12165
|
+
if (extracted === undefined) {
|
|
12166
|
+
return;
|
|
12167
|
+
}
|
|
12168
|
+
let expanded = extracted;
|
|
12169
|
+
if (expanded.startsWith("~/")) {
|
|
12170
|
+
expanded = join5(homedir2(), expanded.slice(2));
|
|
12171
|
+
} else if (expanded.startsWith("$")) {
|
|
12172
|
+
expanded = expanded.replace(/^\$(?:CLAUDE_PROJECT_DIR|\{CLAUDE_PROJECT_DIR\})\/?/, "");
|
|
12173
|
+
}
|
|
12174
|
+
const abs = isAbsolute2(expanded) ? expanded : join5(root, expanded);
|
|
12175
|
+
try {
|
|
12176
|
+
return { command: extracted, exists: statSync3(abs).isFile() };
|
|
12177
|
+
} catch {
|
|
12178
|
+
return { command: extracted, exists: false };
|
|
12179
|
+
}
|
|
12180
|
+
}
|
|
12181
|
+
function parseMcpServers(raw, filePath, root, errors3) {
|
|
12182
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
12183
|
+
errors3.push({
|
|
12184
|
+
path: filePath,
|
|
12185
|
+
kind: "unexpected-shape",
|
|
12186
|
+
detail: "MCP config root is not a JSON object"
|
|
12187
|
+
});
|
|
12188
|
+
return [];
|
|
12189
|
+
}
|
|
12190
|
+
const obj = raw;
|
|
12191
|
+
let servers;
|
|
12192
|
+
if ("mcpServers" in obj && obj.mcpServers !== null && typeof obj.mcpServers === "object" && !Array.isArray(obj.mcpServers)) {
|
|
12193
|
+
servers = obj.mcpServers;
|
|
12194
|
+
} else if (!("mcpServers" in obj) && Object.keys(obj).length > 0 && Object.values(obj).every((v) => v !== null && typeof v === "object" && !Array.isArray(v) && (("command" in v) || ("url" in v) || ("type" in v)))) {
|
|
12195
|
+
servers = obj;
|
|
12196
|
+
} else {
|
|
12197
|
+
errors3.push({
|
|
12198
|
+
path: filePath,
|
|
12199
|
+
kind: "unexpected-shape",
|
|
12200
|
+
detail: "no usable `mcpServers` object and the root is not a server map"
|
|
12201
|
+
});
|
|
12202
|
+
return [];
|
|
12203
|
+
}
|
|
12204
|
+
const facts = [];
|
|
12205
|
+
for (const [name, value] of Object.entries(servers)) {
|
|
12206
|
+
let hasCommand = false;
|
|
12207
|
+
let hasUrl = false;
|
|
12208
|
+
let command;
|
|
12209
|
+
let commandExists;
|
|
12210
|
+
let transport;
|
|
12211
|
+
const literalEnvKeys = [];
|
|
12212
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
12213
|
+
const entry = value;
|
|
12214
|
+
hasCommand = typeof entry.command === "string" && entry.command.length > 0;
|
|
12215
|
+
if (typeof entry.command === "string" && entry.command.length > 0) {
|
|
12216
|
+
command = entry.command;
|
|
12217
|
+
const resolved = resolveMcpCommand(root, entry.command);
|
|
12218
|
+
if (resolved !== undefined) {
|
|
12219
|
+
commandExists = resolved.exists;
|
|
12220
|
+
}
|
|
12221
|
+
}
|
|
12222
|
+
hasUrl = typeof entry.url === "string" && entry.url.length > 0;
|
|
12223
|
+
if (typeof entry.type === "string" && entry.type.length > 0) {
|
|
12224
|
+
transport = entry.type;
|
|
12225
|
+
}
|
|
12226
|
+
const env = entry.env;
|
|
12227
|
+
if (env !== null && typeof env === "object" && !Array.isArray(env)) {
|
|
12228
|
+
for (const [key, val] of Object.entries(env)) {
|
|
12229
|
+
if (typeof val === "string" && val.length > 0 && !/\$\{[A-Za-z_][A-Za-z0-9_]*(?::-[^}]*)?\}/.test(val) && /(?:TOKEN|SECRET|KEY|PASSWORD|PASSWD|CREDENTIAL)S?$/i.test(key)) {
|
|
12230
|
+
literalEnvKeys.push(key);
|
|
12231
|
+
}
|
|
12232
|
+
}
|
|
12233
|
+
}
|
|
12234
|
+
}
|
|
12235
|
+
facts.push({
|
|
12236
|
+
name,
|
|
12237
|
+
path: filePath,
|
|
12238
|
+
hasCommand,
|
|
12239
|
+
hasUrl,
|
|
12240
|
+
...command === undefined ? {} : { command },
|
|
12241
|
+
...commandExists === undefined ? {} : { commandExists },
|
|
12242
|
+
...transport === undefined ? {} : { transport },
|
|
12243
|
+
literalEnvKeys,
|
|
12244
|
+
raw: JSON.stringify(value)
|
|
12113
12245
|
});
|
|
12114
|
-
return [];
|
|
12115
|
-
}
|
|
12116
|
-
const out = [];
|
|
12117
|
-
for (const entry of entries) {
|
|
12118
|
-
if (entry.startsWith(".")) {
|
|
12119
|
-
continue;
|
|
12120
|
-
}
|
|
12121
|
-
if (entry.endsWith("/")) {
|
|
12122
|
-
out.push(...agentFiles(join2(dir, entry.slice(0, -1)), errors3).map((p) => join2(entry.slice(0, -1), p)));
|
|
12123
|
-
continue;
|
|
12124
|
-
}
|
|
12125
|
-
out.push(entry);
|
|
12126
12246
|
}
|
|
12127
|
-
return
|
|
12247
|
+
return facts;
|
|
12128
12248
|
}
|
|
12129
|
-
function
|
|
12130
|
-
const dir = join2(root, ".claude", "agents");
|
|
12131
|
-
if (!existsSync2(dir)) {
|
|
12132
|
-
return [];
|
|
12133
|
-
}
|
|
12134
|
-
const entries = agentFiles(dir, errors3);
|
|
12249
|
+
function discoverMcp(root, mcpPaths, errors3) {
|
|
12135
12250
|
const facts = [];
|
|
12136
|
-
|
|
12137
|
-
|
|
12251
|
+
const seen = new Set;
|
|
12252
|
+
for (const rel of mcpPaths) {
|
|
12253
|
+
const filePath = join5(root, rel);
|
|
12254
|
+
if (!existsSync5(filePath)) {
|
|
12138
12255
|
continue;
|
|
12139
12256
|
}
|
|
12140
|
-
const
|
|
12141
|
-
|
|
12142
|
-
const st = statSync(filePath);
|
|
12143
|
-
if (!st.isFile()) {
|
|
12144
|
-
continue;
|
|
12145
|
-
}
|
|
12146
|
-
} catch {
|
|
12257
|
+
const raw = readJsonConfig(filePath, errors3);
|
|
12258
|
+
if (raw === undefined) {
|
|
12147
12259
|
continue;
|
|
12148
12260
|
}
|
|
12149
|
-
const
|
|
12150
|
-
|
|
12151
|
-
|
|
12152
|
-
|
|
12153
|
-
|
|
12154
|
-
|
|
12155
|
-
|
|
12156
|
-
fact.unreadable = true;
|
|
12157
|
-
}
|
|
12158
|
-
if (fm.unparseable === true) {
|
|
12159
|
-
fact.unparseableFrontmatter = true;
|
|
12160
|
-
}
|
|
12161
|
-
if (fm.name !== undefined) {
|
|
12162
|
-
fact.frontmatterName = fm.name;
|
|
12163
|
-
}
|
|
12164
|
-
if (fm.description !== undefined) {
|
|
12165
|
-
fact.description = fm.description;
|
|
12261
|
+
for (const fact of parseMcpServers(raw, filePath, root, errors3)) {
|
|
12262
|
+
const key = `${fact.name}@${fact.path}`;
|
|
12263
|
+
if (seen.has(key)) {
|
|
12264
|
+
continue;
|
|
12265
|
+
}
|
|
12266
|
+
seen.add(key);
|
|
12267
|
+
facts.push(fact);
|
|
12166
12268
|
}
|
|
12167
|
-
facts.push(fact);
|
|
12168
12269
|
}
|
|
12169
12270
|
return facts;
|
|
12170
12271
|
}
|
|
12272
|
+
|
|
12273
|
+
// src/discover/policy.ts
|
|
12274
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
12275
|
+
import { join as join6 } from "node:path";
|
|
12171
12276
|
function discoverPolicyFiles(root, policyFiles, errors3) {
|
|
12172
12277
|
const out = [];
|
|
12173
12278
|
for (const rel of policyFiles) {
|
|
12174
|
-
const filePath =
|
|
12175
|
-
if (!
|
|
12279
|
+
const filePath = join6(root, rel);
|
|
12280
|
+
if (!existsSync6(filePath)) {
|
|
12176
12281
|
continue;
|
|
12177
12282
|
}
|
|
12178
12283
|
try {
|
|
12179
|
-
const
|
|
12180
|
-
const text = buf.subarray(0, POLICY_CAP).toString("utf8");
|
|
12284
|
+
const result = readCapped(filePath, POLICY_CAP);
|
|
12285
|
+
const text = result.buf.subarray(0, POLICY_CAP).toString("utf8");
|
|
12181
12286
|
out.push({ path: filePath, text });
|
|
12287
|
+
if (result.truncated)
|
|
12288
|
+
errors3.push({ path: filePath, kind: "truncated", detail: `file exceeds ${POLICY_CAP} byte scan cap` });
|
|
12182
12289
|
} catch (err) {
|
|
12183
12290
|
errors3.push({
|
|
12184
12291
|
path: filePath,
|
|
@@ -12190,13 +12297,13 @@ function discoverPolicyFiles(root, policyFiles, errors3) {
|
|
|
12190
12297
|
return out;
|
|
12191
12298
|
}
|
|
12192
12299
|
function discoverSkillsLock(root, errors3) {
|
|
12193
|
-
const filePath =
|
|
12194
|
-
if (!
|
|
12300
|
+
const filePath = join6(root, "skills-lock.json");
|
|
12301
|
+
if (!existsSync6(filePath)) {
|
|
12195
12302
|
return { locked: [], present: false };
|
|
12196
12303
|
}
|
|
12197
12304
|
const raw = readJsonConfig(filePath, errors3);
|
|
12198
12305
|
if (raw === undefined) {
|
|
12199
|
-
return { locked: [], present: true };
|
|
12306
|
+
return { locked: [], present: true, invalid: true };
|
|
12200
12307
|
}
|
|
12201
12308
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
12202
12309
|
errors3.push({
|
|
@@ -12204,7 +12311,7 @@ function discoverSkillsLock(root, errors3) {
|
|
|
12204
12311
|
kind: "unexpected-shape",
|
|
12205
12312
|
detail: "skills-lock.json is not a JSON object"
|
|
12206
12313
|
});
|
|
12207
|
-
return { locked: [], present: true };
|
|
12314
|
+
return { locked: [], present: true, invalid: true };
|
|
12208
12315
|
}
|
|
12209
12316
|
const skills = raw.skills;
|
|
12210
12317
|
if (skills === null || typeof skills !== "object" || Array.isArray(skills)) {
|
|
@@ -12213,7 +12320,7 @@ function discoverSkillsLock(root, errors3) {
|
|
|
12213
12320
|
kind: "unexpected-shape",
|
|
12214
12321
|
detail: "skills-lock.json has no `skills` object"
|
|
12215
12322
|
});
|
|
12216
|
-
return { locked: [], present: true };
|
|
12323
|
+
return { locked: [], present: true, invalid: true };
|
|
12217
12324
|
}
|
|
12218
12325
|
const locked = [];
|
|
12219
12326
|
for (const [id, value] of Object.entries(skills)) {
|
|
@@ -12234,50 +12341,219 @@ function discoverSkillsLock(root, errors3) {
|
|
|
12234
12341
|
}
|
|
12235
12342
|
return { locked, present: true };
|
|
12236
12343
|
}
|
|
12344
|
+
|
|
12345
|
+
// src/discover/skills.ts
|
|
12346
|
+
import { existsSync as existsSync7, readdirSync as readdirSync2, statSync as statSync4 } from "node:fs";
|
|
12347
|
+
import { join as join7, resolve as resolve2 } from "node:path";
|
|
12348
|
+
var BUNDLED = "scripts|references|assets|templates|examples";
|
|
12349
|
+
var REFERENCE_RE = new RegExp(`(?<![\\w/.-])(?:\\./)?((?:${BUNDLED})/[A-Za-z0-9_./-]+\\.[A-Za-z0-9]{1,10})(?![A-Za-z0-9_.-])`, "g");
|
|
12350
|
+
var FENCE_BLOCK = /^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?(?:^[ \t]*\1[ \t]*$|$)/gm;
|
|
12351
|
+
var INLINE_CODE = /`[^`\n]*`/g;
|
|
12352
|
+
var URLS = /\b[a-z][a-z0-9+.-]*:\/\/\S+/gi;
|
|
12353
|
+
function skillReferences(body) {
|
|
12354
|
+
const prose = body.replace(FENCE_BLOCK, "").replace(INLINE_CODE, (code) => /(?:scripts|references|assets|templates|examples)\/[A-Za-z0-9_./-]+\.[A-Za-z0-9]{1,10}/.test(code) ? code : "").replace(URLS, "");
|
|
12355
|
+
const out = [];
|
|
12356
|
+
const seen = new Set;
|
|
12357
|
+
for (const match of prose.matchAll(REFERENCE_RE)) {
|
|
12358
|
+
const path = match[1];
|
|
12359
|
+
if (path === undefined || seen.has(path)) {
|
|
12360
|
+
continue;
|
|
12361
|
+
}
|
|
12362
|
+
seen.add(path);
|
|
12363
|
+
out.push(path);
|
|
12364
|
+
}
|
|
12365
|
+
return out;
|
|
12366
|
+
}
|
|
12367
|
+
function brokenReferences(body, skillDir, root) {
|
|
12368
|
+
const isFile = (p) => {
|
|
12369
|
+
try {
|
|
12370
|
+
return statSync4(p).isFile();
|
|
12371
|
+
} catch {
|
|
12372
|
+
return false;
|
|
12373
|
+
}
|
|
12374
|
+
};
|
|
12375
|
+
return skillReferences(body).filter((rel) => !isFile(join7(skillDir, rel)) && !isFile(join7(root, rel)));
|
|
12376
|
+
}
|
|
12377
|
+
function discoverSkillsInDir(dir, source, errors3, root) {
|
|
12378
|
+
if (!existsSync7(dir)) {
|
|
12379
|
+
return [];
|
|
12380
|
+
}
|
|
12381
|
+
let entries;
|
|
12382
|
+
try {
|
|
12383
|
+
entries = readdirSync2(dir);
|
|
12384
|
+
} catch (err) {
|
|
12385
|
+
errors3.push({
|
|
12386
|
+
path: dir,
|
|
12387
|
+
kind: "unreadable",
|
|
12388
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
12389
|
+
});
|
|
12390
|
+
return [];
|
|
12391
|
+
}
|
|
12392
|
+
const skills = [];
|
|
12393
|
+
const normalizedDir = dir.replaceAll("\\", "/");
|
|
12394
|
+
const runtime = normalizedDir.includes("/.claude/skills") || normalizedDir.endsWith("/.claude/skills") ? "claude" : normalizedDir.includes("/.agents/skills") || normalizedDir.endsWith("/.agents/skills") ? "agents" : "unknown";
|
|
12395
|
+
for (const name of entries) {
|
|
12396
|
+
if (name.startsWith(".") || name === "node_modules") {
|
|
12397
|
+
continue;
|
|
12398
|
+
}
|
|
12399
|
+
const skillDir = join7(dir, name);
|
|
12400
|
+
let st;
|
|
12401
|
+
try {
|
|
12402
|
+
st = statSync4(skillDir);
|
|
12403
|
+
} catch {
|
|
12404
|
+
continue;
|
|
12405
|
+
}
|
|
12406
|
+
if (!st.isDirectory()) {
|
|
12407
|
+
continue;
|
|
12408
|
+
}
|
|
12409
|
+
const skillMd = join7(skillDir, "SKILL.md");
|
|
12410
|
+
let dirReadable = true;
|
|
12411
|
+
try {
|
|
12412
|
+
readdirSync2(skillDir);
|
|
12413
|
+
} catch (err) {
|
|
12414
|
+
dirReadable = false;
|
|
12415
|
+
errors3.push({
|
|
12416
|
+
path: skillDir,
|
|
12417
|
+
kind: "unreadable",
|
|
12418
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
12419
|
+
});
|
|
12420
|
+
}
|
|
12421
|
+
const hasSkillMd = dirReadable && existsSync7(skillMd);
|
|
12422
|
+
const fm = hasSkillMd ? readFrontmatter(skillMd, errors3) : { hasFrontmatter: false };
|
|
12423
|
+
const fact = {
|
|
12424
|
+
id: name,
|
|
12425
|
+
runtime,
|
|
12426
|
+
path: skillDir,
|
|
12427
|
+
source,
|
|
12428
|
+
hasSkillMd,
|
|
12429
|
+
hasFrontmatter: fm.hasFrontmatter
|
|
12430
|
+
};
|
|
12431
|
+
if (fm.unreadable === true || !dirReadable) {
|
|
12432
|
+
fact.unreadable = true;
|
|
12433
|
+
}
|
|
12434
|
+
if (fm.unparseable === true) {
|
|
12435
|
+
fact.unparseableFrontmatter = true;
|
|
12436
|
+
}
|
|
12437
|
+
if (fm.body !== undefined) {
|
|
12438
|
+
const broken = brokenReferences(fm.body, skillDir, root);
|
|
12439
|
+
if (broken.length > 0) {
|
|
12440
|
+
fact.brokenReferences = broken;
|
|
12441
|
+
}
|
|
12442
|
+
}
|
|
12443
|
+
if (fm.description !== undefined) {
|
|
12444
|
+
fact.description = fm.description;
|
|
12445
|
+
}
|
|
12446
|
+
if (fm.name !== undefined) {
|
|
12447
|
+
fact.frontmatterName = fm.name;
|
|
12448
|
+
}
|
|
12449
|
+
skills.push(fact);
|
|
12450
|
+
}
|
|
12451
|
+
return skills;
|
|
12452
|
+
}
|
|
12453
|
+
var NESTED_DISCOVERY_MAX_DEPTH = 8;
|
|
12454
|
+
var NESTED_DISCOVERY_SKIP = new Set([
|
|
12455
|
+
".git",
|
|
12456
|
+
"node_modules",
|
|
12457
|
+
".next",
|
|
12458
|
+
"dist",
|
|
12459
|
+
"build",
|
|
12460
|
+
"coverage",
|
|
12461
|
+
"vendor"
|
|
12462
|
+
]);
|
|
12463
|
+
function discoverNestedClaudeSkills(root, configuredRoots, errors3, options = {}) {
|
|
12464
|
+
const out = [];
|
|
12465
|
+
const read = (dir) => {
|
|
12466
|
+
options.onDirectoryRead?.();
|
|
12467
|
+
try {
|
|
12468
|
+
return readdirSync2(dir, { withFileTypes: true });
|
|
12469
|
+
} catch {
|
|
12470
|
+
errors3.push({ path: dir, kind: "unreadable", detail: "could not read nested skill directories" });
|
|
12471
|
+
return;
|
|
12472
|
+
}
|
|
12473
|
+
};
|
|
12474
|
+
const inspectClaude = (dir) => {
|
|
12475
|
+
const entries = read(dir);
|
|
12476
|
+
if (entries === undefined)
|
|
12477
|
+
return;
|
|
12478
|
+
for (const entry of entries) {
|
|
12479
|
+
if (!entry.isDirectory() || entry.name !== "skills")
|
|
12480
|
+
continue;
|
|
12481
|
+
const child = join7(dir, entry.name);
|
|
12482
|
+
if (configuredRoots.has(resolve2(child)))
|
|
12483
|
+
continue;
|
|
12484
|
+
out.push(...discoverSkillsInDir(child, "project", errors3, root));
|
|
12485
|
+
}
|
|
12486
|
+
};
|
|
12487
|
+
const walk = (dir, depth) => {
|
|
12488
|
+
const entries = read(dir);
|
|
12489
|
+
if (entries === undefined)
|
|
12490
|
+
return;
|
|
12491
|
+
for (const entry of entries) {
|
|
12492
|
+
if (!entry.isDirectory() || NESTED_DISCOVERY_SKIP.has(entry.name))
|
|
12493
|
+
continue;
|
|
12494
|
+
if (entry.name.startsWith(".") && entry.name !== ".claude")
|
|
12495
|
+
continue;
|
|
12496
|
+
const child = join7(dir, entry.name);
|
|
12497
|
+
if (entry.name === ".claude") {
|
|
12498
|
+
inspectClaude(child);
|
|
12499
|
+
continue;
|
|
12500
|
+
}
|
|
12501
|
+
if (depth >= NESTED_DISCOVERY_MAX_DEPTH)
|
|
12502
|
+
continue;
|
|
12503
|
+
walk(child, depth + 1);
|
|
12504
|
+
}
|
|
12505
|
+
};
|
|
12506
|
+
walk(root, 0);
|
|
12507
|
+
return out;
|
|
12508
|
+
}
|
|
12509
|
+
function disambiguateSkills(skills, root) {
|
|
12510
|
+
const counts = new Map;
|
|
12511
|
+
for (const skill of skills)
|
|
12512
|
+
counts.set(skill.id, (counts.get(skill.id) ?? 0) + 1);
|
|
12513
|
+
return skills.map((skill) => {
|
|
12514
|
+
if ((counts.get(skill.id) ?? 0) < 2)
|
|
12515
|
+
return skill;
|
|
12516
|
+
const location = skill.source === "project" ? skill.path.slice(root.length + 1) : `global:${skill.path}`;
|
|
12517
|
+
return { ...skill, instanceId: `${skill.id}@${location}` };
|
|
12518
|
+
});
|
|
12519
|
+
}
|
|
12520
|
+
|
|
12521
|
+
// src/discover/index.ts
|
|
12237
12522
|
function discoverAgentSurface(root, config, opts) {
|
|
12238
12523
|
const configErrors = [];
|
|
12239
12524
|
const skills = [];
|
|
12240
12525
|
for (const rel of config.skillPaths) {
|
|
12241
|
-
skills.push(...discoverSkillsInDir(
|
|
12526
|
+
skills.push(...discoverSkillsInDir(join8(root, rel), "project", configErrors, root));
|
|
12242
12527
|
}
|
|
12528
|
+
const configuredRoots = new Set(config.skillPaths.map((rel) => resolve3(root, rel)));
|
|
12529
|
+
skills.push(...discoverNestedClaudeSkills(root, configuredRoots, configErrors, {
|
|
12530
|
+
onDirectoryRead: opts.onNestedDirectoryRead
|
|
12531
|
+
}));
|
|
12243
12532
|
if (opts.includeGlobal) {
|
|
12244
|
-
const home =
|
|
12245
|
-
skills.push(...discoverSkillsInDir(
|
|
12246
|
-
skills.push(...discoverSkillsInDir(
|
|
12533
|
+
const home = homedir3();
|
|
12534
|
+
skills.push(...discoverSkillsInDir(join8(home, ".claude", "skills"), "global", configErrors, root));
|
|
12535
|
+
skills.push(...discoverSkillsInDir(join8(home, ".codex", "skills"), "global", configErrors, root));
|
|
12247
12536
|
}
|
|
12248
12537
|
const lock = discoverSkillsLock(root, configErrors);
|
|
12249
12538
|
return {
|
|
12250
|
-
skills:
|
|
12539
|
+
skills: disambiguateSkills(skills, root),
|
|
12251
12540
|
agents: discoverAgents(root, configErrors),
|
|
12252
12541
|
hooks: discoverHooks(root, configErrors),
|
|
12253
12542
|
mcp: discoverMcp(root, config.mcpPaths, configErrors),
|
|
12254
12543
|
policyFiles: discoverPolicyFiles(root, config.policyFiles, configErrors),
|
|
12255
12544
|
lockedSkills: lock.locked,
|
|
12256
12545
|
hasSkillsLock: lock.present,
|
|
12546
|
+
skillsLockInvalid: lock.invalid,
|
|
12257
12547
|
configErrors
|
|
12258
12548
|
};
|
|
12259
12549
|
}
|
|
12260
|
-
function dedupeSkillsById(skills) {
|
|
12261
|
-
const byId = new Map;
|
|
12262
|
-
for (const skill of skills) {
|
|
12263
|
-
const existing = byId.get(skill.id);
|
|
12264
|
-
if (!existing) {
|
|
12265
|
-
byId.set(skill.id, skill);
|
|
12266
|
-
continue;
|
|
12267
|
-
}
|
|
12268
|
-
if (existing.source === "global" && skill.source === "project") {
|
|
12269
|
-
byId.set(skill.id, skill);
|
|
12270
|
-
}
|
|
12271
|
-
}
|
|
12272
|
-
return [...byId.values()];
|
|
12273
|
-
}
|
|
12274
12550
|
|
|
12275
12551
|
// src/facts/extract.ts
|
|
12276
|
-
import { existsSync as
|
|
12277
|
-
import { join as
|
|
12552
|
+
import { existsSync as existsSync8, readFileSync as readFileSync3 } from "node:fs";
|
|
12553
|
+
import { join as join9 } from "node:path";
|
|
12278
12554
|
function readPackageJson(root, errors3) {
|
|
12279
|
-
const path =
|
|
12280
|
-
if (!
|
|
12555
|
+
const path = join9(root, "package.json");
|
|
12556
|
+
if (!existsSync8(path)) {
|
|
12281
12557
|
return {};
|
|
12282
12558
|
}
|
|
12283
12559
|
let text;
|
|
@@ -12315,7 +12591,7 @@ function readPackageJson(root, errors3) {
|
|
|
12315
12591
|
if (typeof obj.packageManager === "string") {
|
|
12316
12592
|
pkg.packageManager = obj.packageManager;
|
|
12317
12593
|
}
|
|
12318
|
-
for (const field of ["dependencies", "devDependencies"
|
|
12594
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
12319
12595
|
const raw2 = obj[field];
|
|
12320
12596
|
const kept = stringEntries(raw2);
|
|
12321
12597
|
if (kept === undefined) {
|
|
@@ -12337,6 +12613,19 @@ function readPackageJson(root, errors3) {
|
|
|
12337
12613
|
}
|
|
12338
12614
|
pkg[field] = kept.values;
|
|
12339
12615
|
}
|
|
12616
|
+
const scripts = obj.scripts;
|
|
12617
|
+
if (scripts !== undefined) {
|
|
12618
|
+
const kept = stringEntries(scripts);
|
|
12619
|
+
if (kept === undefined) {
|
|
12620
|
+
errors3.push({ path, kind: "unexpected-shape", detail: "scripts is not an object" });
|
|
12621
|
+
} else if (kept.dropped > 0) {
|
|
12622
|
+
errors3.push({
|
|
12623
|
+
path,
|
|
12624
|
+
kind: "unexpected-shape",
|
|
12625
|
+
detail: `scripts: ${kept.dropped} entr${kept.dropped === 1 ? "y" : "ies"} ignored (value is not a string)`
|
|
12626
|
+
});
|
|
12627
|
+
}
|
|
12628
|
+
}
|
|
12340
12629
|
return pkg;
|
|
12341
12630
|
}
|
|
12342
12631
|
function stringEntries(value) {
|
|
@@ -12362,95 +12651,32 @@ function inferPackageManager(root, pkg) {
|
|
|
12362
12651
|
return prefix;
|
|
12363
12652
|
}
|
|
12364
12653
|
}
|
|
12365
|
-
if (
|
|
12654
|
+
if (existsSync8(join9(root, "bun.lock")) || existsSync8(join9(root, "bun.lockb"))) {
|
|
12366
12655
|
return "bun";
|
|
12367
12656
|
}
|
|
12368
|
-
if (
|
|
12657
|
+
if (existsSync8(join9(root, "pnpm-lock.yaml"))) {
|
|
12369
12658
|
return "pnpm";
|
|
12370
12659
|
}
|
|
12371
|
-
if (
|
|
12660
|
+
if (existsSync8(join9(root, "yarn.lock"))) {
|
|
12372
12661
|
return "yarn";
|
|
12373
12662
|
}
|
|
12374
|
-
if (
|
|
12663
|
+
if (existsSync8(join9(root, "package-lock.json"))) {
|
|
12375
12664
|
return "npm";
|
|
12376
12665
|
}
|
|
12377
12666
|
return "unknown";
|
|
12378
12667
|
}
|
|
12379
|
-
function hasNextConfig(root) {
|
|
12380
|
-
try {
|
|
12381
|
-
const entries = readdirSync2(root);
|
|
12382
|
-
return entries.some((name) => name === "next.config.js" || name === "next.config.mjs" || name === "next.config.cjs" || name === "next.config.ts" || name === "next.config.mts");
|
|
12383
|
-
} catch {
|
|
12384
|
-
return false;
|
|
12385
|
-
}
|
|
12386
|
-
}
|
|
12387
|
-
function readNextConfigText(root) {
|
|
12388
|
-
const candidates = [
|
|
12389
|
-
"next.config.ts",
|
|
12390
|
-
"next.config.mjs",
|
|
12391
|
-
"next.config.js",
|
|
12392
|
-
"next.config.mts",
|
|
12393
|
-
"next.config.cjs"
|
|
12394
|
-
];
|
|
12395
|
-
for (const name of candidates) {
|
|
12396
|
-
const path = join3(root, name);
|
|
12397
|
-
if (!existsSync3(path)) {
|
|
12398
|
-
continue;
|
|
12399
|
-
}
|
|
12400
|
-
try {
|
|
12401
|
-
return readFileSync3(path, "utf8");
|
|
12402
|
-
} catch {
|
|
12403
|
-
return;
|
|
12404
|
-
}
|
|
12405
|
-
}
|
|
12406
|
-
return;
|
|
12407
|
-
}
|
|
12408
|
-
function extractConfigs(root, deps, devDeps, scripts) {
|
|
12409
|
-
const configs = {};
|
|
12410
|
-
if (existsSync3(join3(root, "components.json"))) {
|
|
12411
|
-
configs.shadcn = true;
|
|
12412
|
-
}
|
|
12413
|
-
if (existsSync3(join3(root, "biome.json")) || existsSync3(join3(root, "biome.jsonc"))) {
|
|
12414
|
-
configs.biome = true;
|
|
12415
|
-
}
|
|
12416
|
-
const allDeps = { ...deps, ...devDeps };
|
|
12417
|
-
const hasUltraciteDep = "ultracite" in allDeps;
|
|
12418
|
-
const hasUltraciteScript = Object.values(scripts).some((s) => s.includes("ultracite"));
|
|
12419
|
-
if (hasUltraciteDep || hasUltraciteScript) {
|
|
12420
|
-
configs.ultracite = true;
|
|
12421
|
-
}
|
|
12422
|
-
if ("next" in allDeps) {
|
|
12423
|
-
const next = {};
|
|
12424
|
-
if (hasNextConfig(root)) {
|
|
12425
|
-
next.appRouter = true;
|
|
12426
|
-
const text = readNextConfigText(root);
|
|
12427
|
-
if (text !== undefined) {
|
|
12428
|
-
if (/\bcacheComponents\s*:\s*true\b/.test(text) || /\bcacheComponents\s*:\s*!0\b/.test(text)) {
|
|
12429
|
-
next.cacheComponents = true;
|
|
12430
|
-
} else if (/\bcacheComponents\s*:\s*false\b/.test(text) || /\bcacheComponents\s*:\s*!1\b/.test(text)) {
|
|
12431
|
-
next.cacheComponents = false;
|
|
12432
|
-
}
|
|
12433
|
-
}
|
|
12434
|
-
}
|
|
12435
|
-
configs.next = next;
|
|
12436
|
-
}
|
|
12437
|
-
return configs;
|
|
12438
|
-
}
|
|
12439
12668
|
function extractFacts(root, config, opts) {
|
|
12440
12669
|
const includeGlobal = opts?.includeGlobal ?? config.includeGlobal;
|
|
12441
12670
|
const packageErrors = [];
|
|
12442
12671
|
const pkg = readPackageJson(root, packageErrors);
|
|
12443
12672
|
const dependencies = { ...pkg.dependencies ?? {} };
|
|
12444
12673
|
const devDependencies = { ...pkg.devDependencies ?? {} };
|
|
12445
|
-
const scripts = { ...pkg.scripts ?? {} };
|
|
12446
12674
|
const surface = discoverAgentSurface(root, config, { includeGlobal });
|
|
12447
12675
|
return {
|
|
12448
12676
|
root,
|
|
12449
12677
|
packageManager: inferPackageManager(root, pkg),
|
|
12450
12678
|
dependencies,
|
|
12451
12679
|
devDependencies,
|
|
12452
|
-
scripts,
|
|
12453
|
-
configs: extractConfigs(root, dependencies, devDependencies, scripts),
|
|
12454
12680
|
skills: surface.skills,
|
|
12455
12681
|
agents: surface.agents,
|
|
12456
12682
|
hooks: surface.hooks,
|
|
@@ -12458,6 +12684,7 @@ function extractFacts(root, config, opts) {
|
|
|
12458
12684
|
policyFiles: surface.policyFiles,
|
|
12459
12685
|
lockedSkills: surface.lockedSkills,
|
|
12460
12686
|
hasSkillsLock: surface.hasSkillsLock,
|
|
12687
|
+
skillsLockInvalid: surface.skillsLockInvalid,
|
|
12461
12688
|
configErrors: [...packageErrors, ...surface.configErrors]
|
|
12462
12689
|
};
|
|
12463
12690
|
}
|
|
@@ -12487,7 +12714,7 @@ function compareAction(a, b) {
|
|
|
12487
12714
|
|
|
12488
12715
|
// src/analyze.ts
|
|
12489
12716
|
function analyze(options = {}) {
|
|
12490
|
-
const requested =
|
|
12717
|
+
const requested = resolve4(options.dir ?? process.cwd());
|
|
12491
12718
|
const root = resolveRoot(requested);
|
|
12492
12719
|
const loaded = loadConfig(root, options.configPath);
|
|
12493
12720
|
const config = {
|
|
@@ -12508,7 +12735,7 @@ function analyze(options = {}) {
|
|
|
12508
12735
|
agents: config.thresholds.agents,
|
|
12509
12736
|
mcp: config.thresholds.mcp
|
|
12510
12737
|
}
|
|
12511
|
-
}).filter((f) => !ignoredRules.has(f.ruleId) && !(f.subject.startsWith("skill:") && ignoredSkills.has(f.subject.slice("skill:".length))));
|
|
12738
|
+
}).filter((f) => !ignoredRules.has(f.ruleId) && !(f.subject.startsWith("skill:") && ignoredSkills.has(f.subject.slice("skill:".length).split("@", 1)[0] ?? "")));
|
|
12512
12739
|
const ignoredFindings = new Set(config.ignoreFindings);
|
|
12513
12740
|
const findings = sortFindings(structural.filter((f) => !ignoredFindings.has(f.id)));
|
|
12514
12741
|
return {
|
|
@@ -12741,16 +12968,13 @@ function shouldColour(args) {
|
|
|
12741
12968
|
}
|
|
12742
12969
|
|
|
12743
12970
|
// src/report/text.ts
|
|
12744
|
-
var
|
|
12745
|
-
|
|
12746
|
-
|
|
12747
|
-
|
|
12748
|
-
refresh: "REFRESH",
|
|
12749
|
-
warn: "WARN",
|
|
12750
|
-
drift: "DRIFT"
|
|
12971
|
+
var SEVERITY_LABEL = {
|
|
12972
|
+
error: "ERROR",
|
|
12973
|
+
warning: "WARN",
|
|
12974
|
+
info: "INFO"
|
|
12751
12975
|
};
|
|
12752
|
-
function
|
|
12753
|
-
return
|
|
12976
|
+
function severityColumn(severity) {
|
|
12977
|
+
return SEVERITY_LABEL[severity].padEnd(7, " ");
|
|
12754
12978
|
}
|
|
12755
12979
|
function renderText(args) {
|
|
12756
12980
|
const { version, facts, verbose, quiet } = args;
|
|
@@ -12775,7 +12999,7 @@ function renderText(args) {
|
|
|
12775
12999
|
lines.push(ink.dim(`Scanned: ${formatStack(facts)}`));
|
|
12776
13000
|
lines.push("");
|
|
12777
13001
|
for (const group of groupByRule(visible)) {
|
|
12778
|
-
lines.push(...formatGroup(group, facts.root, ink));
|
|
13002
|
+
lines.push(...formatGroup(group, facts.root, ink, verbose));
|
|
12779
13003
|
lines.push("");
|
|
12780
13004
|
}
|
|
12781
13005
|
lines.push(summary);
|
|
@@ -12830,62 +13054,78 @@ function groupByRule(findings) {
|
|
|
12830
13054
|
function shortPath(value, root) {
|
|
12831
13055
|
return value.split(`${root}/`).join("");
|
|
12832
13056
|
}
|
|
12833
|
-
var
|
|
12834
|
-
|
|
12835
|
-
|
|
12836
|
-
|
|
12837
|
-
add: "green",
|
|
12838
|
-
refresh: "green"
|
|
13057
|
+
var SEVERITY_TONE = {
|
|
13058
|
+
error: "red",
|
|
13059
|
+
warning: "yellow",
|
|
13060
|
+
info: undefined
|
|
12839
13061
|
};
|
|
12840
|
-
function formatGroup(group, root, ink) {
|
|
13062
|
+
function formatGroup(group, root, ink, verbose) {
|
|
12841
13063
|
const first = group[0];
|
|
12842
13064
|
const count = group.length;
|
|
12843
13065
|
const out = [];
|
|
12844
|
-
const tone =
|
|
12845
|
-
const label =
|
|
13066
|
+
const tone = SEVERITY_TONE[first.severity];
|
|
13067
|
+
const label = severityColumn(first.severity);
|
|
12846
13068
|
out.push(`${tone === undefined ? ink.dim(label) : ink.tone(tone, label)} ${ink.dim(`rule:${safe(first.ruleId)}`)}${count > 1 ? ink.bold(` ×${count}`) : ""}`);
|
|
12847
|
-
out.push(` ${safe(count === 1 ? first.message :
|
|
13069
|
+
out.push(` ${safe(count === 1 ? first.message : groupHeadline(group))}`);
|
|
12848
13070
|
for (const f of group) {
|
|
12849
13071
|
const where = f.evidence.filter((e) => e.kind !== "count" && e.kind !== "threshold").map((e) => shortPath(e.value, root));
|
|
12850
13072
|
const location = where.length > 0 ? where.join(" · ") : f.subject;
|
|
12851
13073
|
out.push(ink.dim(` ${safe(shortPath(location, root))}`));
|
|
13074
|
+
if (verbose) {
|
|
13075
|
+
out.push(ink.dim(` id: ${safe(f.id)}`));
|
|
13076
|
+
}
|
|
12852
13077
|
}
|
|
12853
13078
|
return out;
|
|
12854
13079
|
}
|
|
12855
|
-
function
|
|
12856
|
-
const
|
|
12857
|
-
|
|
13080
|
+
function groupHeadline(group) {
|
|
13081
|
+
const stripped = group.map((f) => {
|
|
13082
|
+
const cut = f.message.indexOf(": ");
|
|
13083
|
+
return cut === -1 ? f.message : f.message.slice(0, cut);
|
|
13084
|
+
});
|
|
13085
|
+
if (new Set(stripped).size === 1) {
|
|
13086
|
+
return stripped[0];
|
|
13087
|
+
}
|
|
13088
|
+
const deEvented = stripped.map((s) => s.replace(/^[A-Za-z][A-Za-z0-9]* hook /, "Hook "));
|
|
13089
|
+
if (new Set(deEvented).size === 1) {
|
|
13090
|
+
return deEvented[0];
|
|
13091
|
+
}
|
|
13092
|
+
return `${group.length} findings`;
|
|
12858
13093
|
}
|
|
12859
13094
|
function countsLine(findings, verbose) {
|
|
12860
|
-
const parts =
|
|
13095
|
+
const parts = severityParts(findings, verbose);
|
|
12861
13096
|
return parts.length === 0 ? "no findings" : parts.join(" · ");
|
|
12862
13097
|
}
|
|
12863
|
-
function
|
|
13098
|
+
function severityParts(findings, verbose) {
|
|
12864
13099
|
const counts = {
|
|
12865
|
-
|
|
12866
|
-
|
|
12867
|
-
|
|
12868
|
-
refresh: 0,
|
|
12869
|
-
warn: 0,
|
|
12870
|
-
drift: 0
|
|
13100
|
+
error: 0,
|
|
13101
|
+
warning: 0,
|
|
13102
|
+
info: 0
|
|
12871
13103
|
};
|
|
12872
13104
|
let infoHidden = 0;
|
|
12873
13105
|
for (const f of findings) {
|
|
13106
|
+
if (!verbose && f.action === "keep") {
|
|
13107
|
+
continue;
|
|
13108
|
+
}
|
|
12874
13109
|
if (!verbose && f.severity === "info") {
|
|
12875
13110
|
infoHidden += 1;
|
|
12876
13111
|
continue;
|
|
12877
13112
|
}
|
|
12878
|
-
counts[f.
|
|
13113
|
+
counts[f.severity] += 1;
|
|
12879
13114
|
}
|
|
12880
|
-
const order = ["
|
|
12881
|
-
const
|
|
13115
|
+
const order = ["error", "warning", "info"];
|
|
13116
|
+
const labels = {
|
|
13117
|
+
error: "error",
|
|
13118
|
+
warning: "warning",
|
|
13119
|
+
info: "info"
|
|
13120
|
+
};
|
|
13121
|
+
const parts = order.filter((severity) => counts[severity] > 0).map((severity) => `${counts[severity]} ${labels[severity]}`);
|
|
12882
13122
|
if (!verbose && infoHidden > 0) {
|
|
12883
13123
|
parts.push(`${infoHidden} info hidden (--verbose)`);
|
|
12884
13124
|
}
|
|
12885
13125
|
return parts;
|
|
12886
13126
|
}
|
|
12887
13127
|
function formatSummary(findings, verbose, ink) {
|
|
12888
|
-
const parts =
|
|
13128
|
+
const parts = severityParts(findings, verbose);
|
|
12889
13129
|
const points = score(findings);
|
|
12890
13130
|
const scored = `score ${ink.tone(scoreTone(points), `${points}/100 ${scoreLabel(points)}`)}`;
|
|
12891
13131
|
if (parts.length === 0) {
|
|
@@ -12895,7 +13135,7 @@ function formatSummary(findings, verbose, ink) {
|
|
|
12895
13135
|
}
|
|
12896
13136
|
|
|
12897
13137
|
// src/version.ts
|
|
12898
|
-
var VERSION = "0.
|
|
13138
|
+
var VERSION = "0.6.0";
|
|
12899
13139
|
|
|
12900
13140
|
// src/commands/check.ts
|
|
12901
13141
|
async function runCheck(options) {
|
|
@@ -12934,6 +13174,54 @@ async function runCheck(options) {
|
|
|
12934
13174
|
};
|
|
12935
13175
|
}
|
|
12936
13176
|
|
|
13177
|
+
// src/commands/demo.ts
|
|
13178
|
+
import {
|
|
13179
|
+
mkdirSync,
|
|
13180
|
+
mkdtempSync,
|
|
13181
|
+
rmSync,
|
|
13182
|
+
writeFileSync
|
|
13183
|
+
} from "node:fs";
|
|
13184
|
+
import { tmpdir } from "node:os";
|
|
13185
|
+
import { join as join10 } from "node:path";
|
|
13186
|
+
async function runDemo() {
|
|
13187
|
+
const root = mkdtempSync(join10(tmpdir(), "agentscan-demo-"));
|
|
13188
|
+
try {
|
|
13189
|
+
const claude = join10(root, ".claude");
|
|
13190
|
+
mkdirSync(claude, { recursive: true });
|
|
13191
|
+
writeFileSync(join10(claude, "settings.json"), `${JSON.stringify({
|
|
13192
|
+
hooks: {
|
|
13193
|
+
PreToolUse: [
|
|
13194
|
+
{
|
|
13195
|
+
hooks: [
|
|
13196
|
+
{
|
|
13197
|
+
type: "command",
|
|
13198
|
+
command: ".claude/hooks/guard-destructive-bash.js"
|
|
13199
|
+
}
|
|
13200
|
+
]
|
|
13201
|
+
}
|
|
13202
|
+
]
|
|
13203
|
+
}
|
|
13204
|
+
}, null, 2)}
|
|
13205
|
+
`, "utf8");
|
|
13206
|
+
const result = await runCheck({
|
|
13207
|
+
dir: root,
|
|
13208
|
+
failOn: "never",
|
|
13209
|
+
colour: false
|
|
13210
|
+
});
|
|
13211
|
+
const intro = `Demo fixture: PreToolUse hook points at a script that is not on disk.
|
|
13212
|
+
` + `This is the failure agentscan exists for — the agent starts normally; the guard does not.
|
|
13213
|
+
|
|
13214
|
+
`;
|
|
13215
|
+
return {
|
|
13216
|
+
exitCode: 0,
|
|
13217
|
+
stdout: intro + result.stdout,
|
|
13218
|
+
stderr: ""
|
|
13219
|
+
};
|
|
13220
|
+
} finally {
|
|
13221
|
+
rmSync(root, { recursive: true, force: true });
|
|
13222
|
+
}
|
|
13223
|
+
}
|
|
13224
|
+
|
|
12937
13225
|
// src/commands/explain.ts
|
|
12938
13226
|
async function runExplain(findingId, options = {}) {
|
|
12939
13227
|
const { findings } = analyze(options);
|
|
@@ -12974,12 +13262,12 @@ function formatExplain(f) {
|
|
|
12974
13262
|
}
|
|
12975
13263
|
|
|
12976
13264
|
// src/commands/init.ts
|
|
12977
|
-
import { existsSync as
|
|
12978
|
-
import { join as
|
|
13265
|
+
import { existsSync as existsSync9, writeFileSync as writeFileSync2 } from "node:fs";
|
|
13266
|
+
import { join as join11, resolve as resolve5 } from "node:path";
|
|
12979
13267
|
async function runInit(dir, options = {}) {
|
|
12980
|
-
const root =
|
|
12981
|
-
const path =
|
|
12982
|
-
if (
|
|
13268
|
+
const root = resolve5(dir ?? process.cwd());
|
|
13269
|
+
const path = join11(root, ".agentscanrc.json");
|
|
13270
|
+
if (existsSync9(path) && !options.force) {
|
|
12983
13271
|
return {
|
|
12984
13272
|
exitCode: 2,
|
|
12985
13273
|
stdout: "",
|
|
@@ -12989,7 +13277,7 @@ async function runInit(dir, options = {}) {
|
|
|
12989
13277
|
}
|
|
12990
13278
|
const body = `${JSON.stringify(defaultConfig, null, 2)}
|
|
12991
13279
|
`;
|
|
12992
|
-
|
|
13280
|
+
writeFileSync2(path, body, "utf8");
|
|
12993
13281
|
return {
|
|
12994
13282
|
exitCode: 0,
|
|
12995
13283
|
stdout: `Wrote ${path}
|
|
@@ -13015,7 +13303,7 @@ async function runRulesCommand(options = {}) {
|
|
|
13015
13303
|
// src/report/clipboard.ts
|
|
13016
13304
|
import { spawn } from "node:child_process";
|
|
13017
13305
|
import { accessSync, constants } from "node:fs";
|
|
13018
|
-
import { delimiter, join as
|
|
13306
|
+
import { delimiter, join as join12 } from "node:path";
|
|
13019
13307
|
var TOOLS = [
|
|
13020
13308
|
["wl-copy"],
|
|
13021
13309
|
["xclip", "-selection", "clipboard"],
|
|
@@ -13024,15 +13312,15 @@ var TOOLS = [
|
|
|
13024
13312
|
["clip.exe"]
|
|
13025
13313
|
];
|
|
13026
13314
|
var TIMEOUT_MS = 3000;
|
|
13027
|
-
function clipboardCandidates(onPath = (name) =>
|
|
13315
|
+
function clipboardCandidates(onPath = (name) => resolve6(name) !== null) {
|
|
13028
13316
|
return TOOLS.filter((t) => onPath(t[0])).map((t) => [...t]);
|
|
13029
13317
|
}
|
|
13030
|
-
function
|
|
13318
|
+
function resolve6(name) {
|
|
13031
13319
|
for (const dir of (process.env.PATH ?? "").split(delimiter)) {
|
|
13032
13320
|
if (dir.length === 0) {
|
|
13033
13321
|
continue;
|
|
13034
13322
|
}
|
|
13035
|
-
const candidate =
|
|
13323
|
+
const candidate = join12(dir, name);
|
|
13036
13324
|
try {
|
|
13037
13325
|
accessSync(candidate, constants.X_OK);
|
|
13038
13326
|
return candidate;
|
|
@@ -13051,7 +13339,7 @@ async function copyToClipboard(text) {
|
|
|
13051
13339
|
const failures = [];
|
|
13052
13340
|
for (const argv of candidates) {
|
|
13053
13341
|
const tool = argv[0];
|
|
13054
|
-
const bin =
|
|
13342
|
+
const bin = resolve6(tool);
|
|
13055
13343
|
if (bin === null) {
|
|
13056
13344
|
continue;
|
|
13057
13345
|
}
|
|
@@ -13087,6 +13375,7 @@ function printHelp() {
|
|
|
13087
13375
|
Usage:
|
|
13088
13376
|
agentscan scan the current directory
|
|
13089
13377
|
agentscan check [dir] [options]
|
|
13378
|
+
agentscan demo one-shot fixture (no project required)
|
|
13090
13379
|
agentscan explain <findingId> [dir]
|
|
13091
13380
|
agentscan rules [dir]
|
|
13092
13381
|
agentscan init [dir] [--force]
|
|
@@ -13252,6 +13541,16 @@ async function main(argv) {
|
|
|
13252
13541
|
}
|
|
13253
13542
|
return result.exitCode;
|
|
13254
13543
|
}
|
|
13544
|
+
case "demo": {
|
|
13545
|
+
const result = await runDemo();
|
|
13546
|
+
if (result.stderr.length > 0) {
|
|
13547
|
+
process.stderr.write(result.stderr);
|
|
13548
|
+
}
|
|
13549
|
+
if (result.stdout.length > 0) {
|
|
13550
|
+
process.stdout.write(result.stdout);
|
|
13551
|
+
}
|
|
13552
|
+
return result.exitCode;
|
|
13553
|
+
}
|
|
13255
13554
|
case "help":
|
|
13256
13555
|
printHelp();
|
|
13257
13556
|
return 0;
|