@sagargupta1610/skillcheck 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-MGSKVMAQ.js → chunk-X3UHBJW7.js} +47 -0
- package/dist/cli.js +20 -7
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -1029,6 +1029,9 @@ async function lintSkillDir(dir, options = {}) {
|
|
|
1029
1029
|
bundledFiles
|
|
1030
1030
|
})
|
|
1031
1031
|
);
|
|
1032
|
+
findings.push(
|
|
1033
|
+
...await deepReferenceChains(skillDir, parsed.body, bundledFiles)
|
|
1034
|
+
);
|
|
1032
1035
|
const skillName = parsed.frontmatter && typeof parsed.frontmatter.name === "string" ? parsed.frontmatter.name.trim().normalize("NFKC") : null;
|
|
1033
1036
|
return summarize(skillDir, skillName, applyOverrides(findings, options));
|
|
1034
1037
|
}
|
|
@@ -1071,6 +1074,50 @@ function applyOverrides(findings, options) {
|
|
|
1071
1074
|
}
|
|
1072
1075
|
return out;
|
|
1073
1076
|
}
|
|
1077
|
+
function extractReferences(body) {
|
|
1078
|
+
const refs = /* @__PURE__ */ new Set();
|
|
1079
|
+
for (const m of body.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)) {
|
|
1080
|
+
const target = (m[1] ?? "").split(/[#?]/)[0]?.trim() ?? "";
|
|
1081
|
+
if (target) refs.add(target);
|
|
1082
|
+
}
|
|
1083
|
+
for (const m of body.matchAll(
|
|
1084
|
+
/`((?:scripts|references|assets)\/[^\s`]+)`/g
|
|
1085
|
+
)) {
|
|
1086
|
+
refs.add((m[1] ?? "").trim());
|
|
1087
|
+
}
|
|
1088
|
+
return [...refs].filter(
|
|
1089
|
+
(t) => !/^[a-z][a-z0-9+.-]*:\/\//i.test(t) && !t.startsWith("#") && !t.startsWith("mailto:") && !t.includes("${") && !t.includes("{{") && !/^(\/|[A-Za-z]:[\\/]|~\/)/.test(t)
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
async function deepReferenceChains(skillDir, body, bundledFiles) {
|
|
1093
|
+
const findings = [];
|
|
1094
|
+
const bundled = new Set(bundledFiles.map((f) => f.replace(/\\/g, "/")));
|
|
1095
|
+
for (const ref of extractReferences(body)) {
|
|
1096
|
+
const normalized = ref.replace(/^\.\//, "").replace(/\\/g, "/");
|
|
1097
|
+
if (!normalized.endsWith(".md") || !bundled.has(normalized)) continue;
|
|
1098
|
+
let childBody;
|
|
1099
|
+
try {
|
|
1100
|
+
childBody = await readFile2(join2(skillDir, normalized), "utf8");
|
|
1101
|
+
} catch {
|
|
1102
|
+
continue;
|
|
1103
|
+
}
|
|
1104
|
+
const childRefs = extractReferences(childBody).filter((t) => {
|
|
1105
|
+
const childDir = normalized.includes("/") ? normalized.slice(0, normalized.lastIndexOf("/")) : "";
|
|
1106
|
+
const resolved = t.startsWith("./") || !t.includes("/") ? `${childDir ? `${childDir}/` : ""}${t.replace(/^\.\//, "")}` : t;
|
|
1107
|
+
return bundled.has(resolved.replace(/\\/g, "/"));
|
|
1108
|
+
});
|
|
1109
|
+
if (childRefs.length > 0) {
|
|
1110
|
+
findings.push({
|
|
1111
|
+
code: "SC103",
|
|
1112
|
+
alias: "deep-reference-chain",
|
|
1113
|
+
severity: "info",
|
|
1114
|
+
message: `\`${normalized}\` references further bundled files (${childRefs.slice(0, 3).join(", ")}${childRefs.length > 3 ? ", ..." : ""}) -- spec recommends keeping references one level deep from SKILL.md`,
|
|
1115
|
+
file: normalized
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
return findings;
|
|
1120
|
+
}
|
|
1074
1121
|
async function listBundledFiles(skillDir) {
|
|
1075
1122
|
const files = [];
|
|
1076
1123
|
async function walk(dir, depth) {
|
package/dist/cli.js
CHANGED
|
@@ -5,14 +5,14 @@ import {
|
|
|
5
5
|
initEvals,
|
|
6
6
|
lintSkillDir,
|
|
7
7
|
toSarif
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-X3UHBJW7.js";
|
|
9
9
|
|
|
10
10
|
// src/cli.ts
|
|
11
11
|
import { appendFile, readFile, stat, writeFile } from "fs/promises";
|
|
12
12
|
import { join } from "path";
|
|
13
13
|
import { Command } from "commander";
|
|
14
14
|
import { glob } from "glob";
|
|
15
|
-
var VERSION = "0.2.
|
|
15
|
+
var VERSION = "0.2.2";
|
|
16
16
|
var program = new Command();
|
|
17
17
|
program.name("skillcheck").description("Conformance suite for Agent Skills (SKILL.md)").version(VERSION);
|
|
18
18
|
program.command("lint").description(
|
|
@@ -32,14 +32,27 @@ program.command("lint").description(
|
|
|
32
32
|
"--fail-on <severity>",
|
|
33
33
|
"minimum severity that fails: error | warning | never",
|
|
34
34
|
"error"
|
|
35
|
-
).option("--sarif <path>", "also write a SARIF 2.1.0 report to this path").option("--max-warnings <n>", "fail when warnings exceed this count").
|
|
36
|
-
|
|
35
|
+
).option("--sarif <path>", "also write a SARIF 2.1.0 report to this path").option("--max-warnings <n>", "fail when warnings exceed this count").option(
|
|
36
|
+
"--ignore-pattern <glob...>",
|
|
37
|
+
"glob(s) to exclude during discovery (adds to config ignore)"
|
|
38
|
+
).action(async (paths, opts) => {
|
|
39
|
+
if (opts.strict && opts.profile === "lenient") {
|
|
40
|
+
console.error("--strict conflicts with --profile lenient; pick one");
|
|
41
|
+
process.exit(2);
|
|
42
|
+
}
|
|
43
|
+
if (opts.strict) {
|
|
44
|
+
console.error(
|
|
45
|
+
"warning: --strict is deprecated; strict is the default (use --profile)"
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const config = await loadConfig();
|
|
49
|
+
const ignore = [...config?.ignore ?? [], ...opts.ignorePattern ?? []];
|
|
50
|
+
const skillDirs = await expandSkillDirs(paths, ignore);
|
|
37
51
|
if (skillDirs.length === 0) {
|
|
38
52
|
console.error("no SKILL.md found under the given paths");
|
|
39
53
|
process.exit(2);
|
|
40
54
|
}
|
|
41
55
|
const profile = opts.profile === "lenient" ? "lenient" : "strict";
|
|
42
|
-
const config = await loadConfig();
|
|
43
56
|
const results = [];
|
|
44
57
|
for (const dir of skillDirs) {
|
|
45
58
|
const result = await lintSkillDir(dir, {
|
|
@@ -103,7 +116,7 @@ async function loadConfig() {
|
|
|
103
116
|
return null;
|
|
104
117
|
}
|
|
105
118
|
}
|
|
106
|
-
async function expandSkillDirs(paths) {
|
|
119
|
+
async function expandSkillDirs(paths, extraIgnore = []) {
|
|
107
120
|
const dirs = /* @__PURE__ */ new Set();
|
|
108
121
|
for (const p of paths) {
|
|
109
122
|
if (await exists(join(p, "SKILL.md")) || await exists(join(p, "skill.md"))) {
|
|
@@ -112,7 +125,7 @@ async function expandSkillDirs(paths) {
|
|
|
112
125
|
}
|
|
113
126
|
const matches = await glob("**/{SKILL,skill}.md", {
|
|
114
127
|
cwd: p,
|
|
115
|
-
ignore: ["**/node_modules/**", "**/.git/**"],
|
|
128
|
+
ignore: ["**/node_modules/**", "**/.git/**", ...extraIgnore],
|
|
116
129
|
absolute: true
|
|
117
130
|
});
|
|
118
131
|
for (const m of matches) {
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sagargupta1610/skillcheck",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Conformance suite for Agent Skills: lint SKILL.md against the spec, run it against real agent runtimes, publish a compatibility matrix",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-skills",
|