skill-checker 0.1.6 → 0.1.8

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/index.js CHANGED
@@ -160,14 +160,23 @@ function enumerateFiles(dirPath, warnings) {
160
160
  content = readFileSync(fullPath, "utf-8");
161
161
  } catch {
162
162
  }
163
- } else if (lstats.size <= 5e7) {
163
+ } else {
164
164
  let fd;
165
165
  try {
166
166
  fd = openSync(fullPath, "r");
167
- const buf = Buffer.alloc(PARTIAL_READ_LIMIT);
168
- const bytesRead = readSync(fd, buf, 0, PARTIAL_READ_LIMIT, 0);
169
- content = buf.slice(0, bytesRead).toString("utf-8");
170
- warnings.push(`Large file partially scanned (first ${PARTIAL_READ_LIMIT} bytes): ${relativePath} (${lstats.size} bytes total)`);
167
+ const headBuf = Buffer.alloc(PARTIAL_READ_LIMIT);
168
+ const headBytesRead = readSync(fd, headBuf, 0, PARTIAL_READ_LIMIT, 0);
169
+ const headContent = headBuf.slice(0, headBytesRead).toString("utf-8");
170
+ const tailOffset = Math.max(0, lstats.size - PARTIAL_READ_LIMIT);
171
+ const tailBuf = Buffer.alloc(PARTIAL_READ_LIMIT);
172
+ const tailBytesRead = readSync(fd, tailBuf, 0, PARTIAL_READ_LIMIT, tailOffset);
173
+ const tailContent = tailBuf.slice(0, tailBytesRead).toString("utf-8");
174
+ content = tailOffset > 0 ? `${headContent}
175
+ /* ... window gap ... */
176
+ ${tailContent}` : headContent;
177
+ warnings.push(
178
+ `Large file window-scanned (head+tail ${PARTIAL_READ_LIMIT} bytes each): ${relativePath} (${lstats.size} bytes total)`
179
+ );
171
180
  } catch {
172
181
  warnings.push(`Large file could not be read: ${relativePath} (${lstats.size} bytes)`);
173
182
  } finally {
@@ -178,8 +187,6 @@ function enumerateFiles(dirPath, warnings) {
178
187
  }
179
188
  }
180
189
  }
181
- } else {
182
- warnings.push(`File too large to scan: ${relativePath} (${lstats.size} bytes). Content not checked.`);
183
190
  }
184
191
  }
185
192
  files.push({
@@ -199,15 +206,12 @@ function enumerateFiles(dirPath, warnings) {
199
206
  // src/checks/structural.ts
200
207
  var HYPHEN_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
201
208
  var MAX_NAME_LENGTH = 64;
202
- var EXECUTABLE_EXTENSIONS = /* @__PURE__ */ new Set([
203
- ".exe",
204
- ".bat",
205
- ".cmd",
209
+ var SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([
206
210
  ".sh",
207
211
  ".bash",
208
212
  ".ps1",
209
- ".com",
210
- ".msi"
213
+ ".bat",
214
+ ".cmd"
211
215
  ]);
212
216
  var BINARY_EXTENSIONS2 = /* @__PURE__ */ new Set([
213
217
  ".exe",
@@ -219,6 +223,10 @@ var BINARY_EXTENSIONS2 = /* @__PURE__ */ new Set([
219
223
  ".class",
220
224
  ".pyc"
221
225
  ]);
226
+ var INSTALLER_EXTENSIONS = /* @__PURE__ */ new Set([
227
+ ".com",
228
+ ".msi"
229
+ ]);
222
230
  var structuralChecks = {
223
231
  name: "Structural Validity",
224
232
  category: "STRUCT",
@@ -272,7 +280,7 @@ var structuralChecks = {
272
280
  }
273
281
  for (const file of skill.files) {
274
282
  const ext = file.extension.toLowerCase();
275
- if (BINARY_EXTENSIONS2.has(ext) || EXECUTABLE_EXTENSIONS.has(ext)) {
283
+ if (BINARY_EXTENSIONS2.has(ext) || INSTALLER_EXTENSIONS.has(ext)) {
276
284
  results.push({
277
285
  id: "STRUCT-006",
278
286
  category: "STRUCT",
@@ -281,6 +289,15 @@ var structuralChecks = {
281
289
  message: `Found unexpected file: ${file.path} (${ext})`,
282
290
  source: file.path
283
291
  });
292
+ } else if (SCRIPT_EXTENSIONS.has(ext)) {
293
+ results.push({
294
+ id: "STRUCT-006",
295
+ category: "STRUCT",
296
+ severity: "LOW",
297
+ title: "Script file present",
298
+ message: `Found script file: ${file.path} (${ext}). Content is scanned separately.`,
299
+ source: file.path
300
+ });
284
301
  }
285
302
  }
286
303
  const name = skill.frontmatter.name;
@@ -365,6 +382,15 @@ function isInDocumentationContext(lines, lineIndex) {
365
382
  }
366
383
  return false;
367
384
  }
385
+ function isNearDocumentationHeader(lines, lineIndex) {
386
+ for (let i = lineIndex; i >= Math.max(0, lineIndex - 15); i--) {
387
+ const l = lines[i];
388
+ if (/^#{1,4}\s+.*(install|setup|prerequisite|requirement|depend|getting\s+started|quickstart)/i.test(l)) {
389
+ return true;
390
+ }
391
+ }
392
+ return false;
393
+ }
368
394
  function isLicenseFile(filePath) {
369
395
  const name = filePath.split("/").pop()?.toUpperCase() ?? "";
370
396
  const base = name.replace(/\.[^.]+$/, "");
@@ -1708,14 +1734,58 @@ var supplyChainChecks = {
1708
1734
  if (NPM_INSTALL_PATTERN.test(line) || PIP_INSTALL_PATTERN.test(line)) {
1709
1735
  const allLines = getAllLines(skill);
1710
1736
  const globalIdx = findGlobalLineIndex(allLines, source, lineNum);
1711
- const isDoc = globalIdx >= 0 && isInDocumentationContext(
1737
+ const isDoc = source === "SKILL.md" && globalIdx >= 0 && isInDocumentationContext(
1712
1738
  allLines.map((l) => l.line),
1713
1739
  globalIdx
1714
1740
  );
1715
- if (!isDoc) {
1741
+ const srcLines = getLinesForSource(skill, source);
1742
+ const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);
1743
+ const inCodeBlock = localIdx >= 0 && isInCodeBlock(srcLines, localIdx);
1744
+ if (isDoc && !inCodeBlock) {
1745
+ } else {
1716
1746
  let severity = "HIGH";
1717
1747
  let reducedFrom;
1718
1748
  let msgSuffix = "";
1749
+ if (inCodeBlock) {
1750
+ const isNearDoc = source === "SKILL.md" && globalIdx >= 0 && isNearDocumentationHeader(
1751
+ allLines.map((l) => l.line),
1752
+ globalIdx
1753
+ );
1754
+ if (isNearDoc) {
1755
+ severity = "LOW";
1756
+ reducedFrom = "HIGH";
1757
+ msgSuffix = " [reduced: in code block within documentation]";
1758
+ } else {
1759
+ const r = reduceSeverity(severity, "in code block");
1760
+ severity = r.severity;
1761
+ reducedFrom = r.reducedFrom;
1762
+ msgSuffix = ` ${r.annotation}`;
1763
+ }
1764
+ }
1765
+ results.push({
1766
+ id: "SUPPLY-003",
1767
+ category: "SUPPLY",
1768
+ severity,
1769
+ title: "Package installation command",
1770
+ message: `${source}:${lineNum}: Installs packages. Verify package names are legitimate.${msgSuffix}`,
1771
+ line: lineNum,
1772
+ snippet: line.trim().slice(0, 120),
1773
+ source,
1774
+ reducedFrom
1775
+ });
1776
+ }
1777
+ }
1778
+ if (GIT_CLONE_PATTERN.test(line)) {
1779
+ const allLines = getAllLines(skill);
1780
+ const globalIdx = findGlobalLineIndex(allLines, source, lineNum);
1781
+ const isDoc = source === "SKILL.md" && globalIdx >= 0 && isInDocumentationContext(
1782
+ allLines.map((l) => l.line),
1783
+ globalIdx
1784
+ );
1785
+ if (!isDoc) {
1786
+ let severity = "MEDIUM";
1787
+ let reducedFrom;
1788
+ let msgSuffix = "";
1719
1789
  const srcLines = getLinesForSource(skill, source);
1720
1790
  const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);
1721
1791
  if (localIdx >= 0 && isInCodeBlock(srcLines, localIdx)) {
@@ -1725,11 +1795,11 @@ var supplyChainChecks = {
1725
1795
  msgSuffix = ` ${r.annotation}`;
1726
1796
  }
1727
1797
  results.push({
1728
- id: "SUPPLY-003",
1798
+ id: "SUPPLY-006",
1729
1799
  category: "SUPPLY",
1730
1800
  severity,
1731
- title: "Package installation command",
1732
- message: `${source}:${lineNum}: Installs packages. Verify package names are legitimate.${msgSuffix}`,
1801
+ title: "git clone command",
1802
+ message: `${source}:${lineNum}: Clones a git repository. Verify the source.${msgSuffix}`,
1733
1803
  line: lineNum,
1734
1804
  snippet: line.trim().slice(0, 120),
1735
1805
  source,
@@ -1737,18 +1807,6 @@ var supplyChainChecks = {
1737
1807
  });
1738
1808
  }
1739
1809
  }
1740
- if (GIT_CLONE_PATTERN.test(line)) {
1741
- results.push({
1742
- id: "SUPPLY-006",
1743
- category: "SUPPLY",
1744
- severity: "MEDIUM",
1745
- title: "git clone command",
1746
- message: `${source}:${lineNum}: Clones a git repository. Verify the source.`,
1747
- line: lineNum,
1748
- snippet: line.trim().slice(0, 120),
1749
- source
1750
- });
1751
- }
1752
1810
  const urls = line.match(URL_PATTERN) || [];
1753
1811
  for (const url of urls) {
1754
1812
  if (url.startsWith("http://")) {