@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.
@@ -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-MGSKVMAQ.js";
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.1";
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").action(async (paths, opts) => {
36
- const skillDirs = await expandSkillDirs(paths);
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
@@ -10,7 +10,7 @@ import {
10
10
  lintSkillDir,
11
11
  parseSkillMd,
12
12
  toSarif
13
- } from "./chunk-MGSKVMAQ.js";
13
+ } from "./chunk-X3UHBJW7.js";
14
14
  export {
15
15
  KNOWN_EXTENSIONS,
16
16
  RULES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sagargupta1610/skillcheck",
3
- "version": "0.2.1",
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",