@studiomeyer-io/skilldoctor 0.1.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.
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Core domain types for skilldoctor.
3
+ *
4
+ * These types describe what a parsed agent-skill / instruction file looks like,
5
+ * what a finding is, and the shape of an analysis result. They form the public
6
+ * library API surface (re-exported from `index.ts`).
7
+ */
8
+ /** The kind of agent-instruction file we detected. */
9
+ type FileKind =
10
+ /** A Claude Code / Agent Skills `SKILL.md` (has a `name`+`description` frontmatter). */
11
+ "skill"
12
+ /** A Claude Code subagent definition (frontmatter with `name`+`description`, body = system prompt). */
13
+ | "subagent"
14
+ /** An `AGENTS.md` instruction file (plain markdown, no frontmatter required). */
15
+ | "agents-md"
16
+ /** A markdown file with frontmatter we could not confidently classify. */
17
+ | "unknown";
18
+ /** Severity of a finding, ordered from most to least serious. */
19
+ type Severity = "error" | "warning" | "info";
20
+ /** Numeric rank for a severity — higher means more serious. Used for `--fail-on`. */
21
+ declare const SEVERITY_RANK: Record<Severity, number>;
22
+ /** Category groups findings for reporting and weighting. */
23
+ type FindingCategory = "lint" | "security";
24
+ /** A single problem found in a file. */
25
+ interface Finding {
26
+ /** Stable rule identifier, e.g. `skill/missing-description` or `sec/prompt-injection`. */
27
+ ruleId: string;
28
+ /** Human-readable rule title (short). */
29
+ title: string;
30
+ /** Category — lint or security. */
31
+ category: FindingCategory;
32
+ /** Severity of this occurrence. */
33
+ severity: Severity;
34
+ /** One-line explanation of the problem in context. */
35
+ message: string;
36
+ /** 1-based line number in the source file (best effort; 1 if unknown). */
37
+ line: number;
38
+ /** 1-based column number (best effort; 1 if unknown). */
39
+ column: number;
40
+ /** Optional snippet of the offending text (truncated, never executed). */
41
+ evidence?: string;
42
+ /** Whether `--fix` can mechanically repair this finding. */
43
+ fixable: boolean;
44
+ }
45
+ /** Frontmatter parsed from a file, normalized to a plain record. */
46
+ interface ParsedFrontmatter {
47
+ /** Whether a frontmatter block was present at all. */
48
+ present: boolean;
49
+ /** Raw frontmatter source (between the `---` fences), if present. */
50
+ raw: string;
51
+ /** Parsed key/value data. `undefined` when frontmatter is absent or unparseable. */
52
+ data: Record<string, unknown> | undefined;
53
+ /** A YAML parse error message, if parsing failed. */
54
+ error: string | undefined;
55
+ /** 1-based line where the frontmatter body starts (line after the opening fence). */
56
+ startLine: number;
57
+ /** 1-based line where the frontmatter body ends (the closing fence line). */
58
+ endLine: number;
59
+ }
60
+ /** A fully parsed agent-instruction file, ready to lint. */
61
+ interface ParsedFile {
62
+ /** Absolute or relative path as supplied by the caller. */
63
+ filePath: string;
64
+ /** Detected file kind. */
65
+ kind: FileKind;
66
+ /** Parsed frontmatter. */
67
+ frontmatter: ParsedFrontmatter;
68
+ /** The markdown body (everything after the frontmatter block). */
69
+ body: string;
70
+ /** 1-based line number where the body begins in the original file. */
71
+ bodyStartLine: number;
72
+ /** The complete, unmodified file contents. */
73
+ raw: string;
74
+ }
75
+ /** Result of analyzing one file. */
76
+ interface FileReport {
77
+ filePath: string;
78
+ kind: FileKind;
79
+ findings: Finding[];
80
+ /** Numeric score 0-100 for this file. */
81
+ score: number;
82
+ /** Letter grade A-F derived from the score. */
83
+ grade: Grade;
84
+ }
85
+ /** Aggregate result of analyzing one or more files. */
86
+ interface AnalysisReport {
87
+ files: FileReport[];
88
+ /** Aggregate numeric score 0-100 across all files (worst-weighted average). */
89
+ score: number;
90
+ /** Aggregate letter grade. */
91
+ grade: Grade;
92
+ /** Total finding counts by severity across all files. */
93
+ totals: Record<Severity, number>;
94
+ }
95
+ /** Letter grades. */
96
+ type Grade = "A" | "B" | "C" | "D" | "F";
97
+ /** A rule's static descriptor (used for SARIF `driver.rules` and docs). */
98
+ interface RuleDescriptor {
99
+ ruleId: string;
100
+ title: string;
101
+ category: FindingCategory;
102
+ defaultSeverity: Severity;
103
+ /** Short description of what the rule checks and why. */
104
+ description: string;
105
+ /** Whether occurrences of this rule are mechanically fixable. */
106
+ fixable: boolean;
107
+ }
108
+ /** Options controlling analysis behavior. */
109
+ interface AnalyzeOptions {
110
+ /**
111
+ * Override the auto-detected file kind. Mostly useful for tests and for
112
+ * files whose name does not follow convention.
113
+ */
114
+ forceKind?: FileKind;
115
+ }
116
+
117
+ export { type AnalyzeOptions as A, type FileReport as F, type Grade as G, type ParsedFile as P, type RuleDescriptor as R, type Severity as S, type AnalysisReport as a, type ParsedFrontmatter as b, type FileKind as c, type Finding as d, type FindingCategory as e, SEVERITY_RANK as f };
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Core domain types for skilldoctor.
3
+ *
4
+ * These types describe what a parsed agent-skill / instruction file looks like,
5
+ * what a finding is, and the shape of an analysis result. They form the public
6
+ * library API surface (re-exported from `index.ts`).
7
+ */
8
+ /** The kind of agent-instruction file we detected. */
9
+ type FileKind =
10
+ /** A Claude Code / Agent Skills `SKILL.md` (has a `name`+`description` frontmatter). */
11
+ "skill"
12
+ /** A Claude Code subagent definition (frontmatter with `name`+`description`, body = system prompt). */
13
+ | "subagent"
14
+ /** An `AGENTS.md` instruction file (plain markdown, no frontmatter required). */
15
+ | "agents-md"
16
+ /** A markdown file with frontmatter we could not confidently classify. */
17
+ | "unknown";
18
+ /** Severity of a finding, ordered from most to least serious. */
19
+ type Severity = "error" | "warning" | "info";
20
+ /** Numeric rank for a severity — higher means more serious. Used for `--fail-on`. */
21
+ declare const SEVERITY_RANK: Record<Severity, number>;
22
+ /** Category groups findings for reporting and weighting. */
23
+ type FindingCategory = "lint" | "security";
24
+ /** A single problem found in a file. */
25
+ interface Finding {
26
+ /** Stable rule identifier, e.g. `skill/missing-description` or `sec/prompt-injection`. */
27
+ ruleId: string;
28
+ /** Human-readable rule title (short). */
29
+ title: string;
30
+ /** Category — lint or security. */
31
+ category: FindingCategory;
32
+ /** Severity of this occurrence. */
33
+ severity: Severity;
34
+ /** One-line explanation of the problem in context. */
35
+ message: string;
36
+ /** 1-based line number in the source file (best effort; 1 if unknown). */
37
+ line: number;
38
+ /** 1-based column number (best effort; 1 if unknown). */
39
+ column: number;
40
+ /** Optional snippet of the offending text (truncated, never executed). */
41
+ evidence?: string;
42
+ /** Whether `--fix` can mechanically repair this finding. */
43
+ fixable: boolean;
44
+ }
45
+ /** Frontmatter parsed from a file, normalized to a plain record. */
46
+ interface ParsedFrontmatter {
47
+ /** Whether a frontmatter block was present at all. */
48
+ present: boolean;
49
+ /** Raw frontmatter source (between the `---` fences), if present. */
50
+ raw: string;
51
+ /** Parsed key/value data. `undefined` when frontmatter is absent or unparseable. */
52
+ data: Record<string, unknown> | undefined;
53
+ /** A YAML parse error message, if parsing failed. */
54
+ error: string | undefined;
55
+ /** 1-based line where the frontmatter body starts (line after the opening fence). */
56
+ startLine: number;
57
+ /** 1-based line where the frontmatter body ends (the closing fence line). */
58
+ endLine: number;
59
+ }
60
+ /** A fully parsed agent-instruction file, ready to lint. */
61
+ interface ParsedFile {
62
+ /** Absolute or relative path as supplied by the caller. */
63
+ filePath: string;
64
+ /** Detected file kind. */
65
+ kind: FileKind;
66
+ /** Parsed frontmatter. */
67
+ frontmatter: ParsedFrontmatter;
68
+ /** The markdown body (everything after the frontmatter block). */
69
+ body: string;
70
+ /** 1-based line number where the body begins in the original file. */
71
+ bodyStartLine: number;
72
+ /** The complete, unmodified file contents. */
73
+ raw: string;
74
+ }
75
+ /** Result of analyzing one file. */
76
+ interface FileReport {
77
+ filePath: string;
78
+ kind: FileKind;
79
+ findings: Finding[];
80
+ /** Numeric score 0-100 for this file. */
81
+ score: number;
82
+ /** Letter grade A-F derived from the score. */
83
+ grade: Grade;
84
+ }
85
+ /** Aggregate result of analyzing one or more files. */
86
+ interface AnalysisReport {
87
+ files: FileReport[];
88
+ /** Aggregate numeric score 0-100 across all files (worst-weighted average). */
89
+ score: number;
90
+ /** Aggregate letter grade. */
91
+ grade: Grade;
92
+ /** Total finding counts by severity across all files. */
93
+ totals: Record<Severity, number>;
94
+ }
95
+ /** Letter grades. */
96
+ type Grade = "A" | "B" | "C" | "D" | "F";
97
+ /** A rule's static descriptor (used for SARIF `driver.rules` and docs). */
98
+ interface RuleDescriptor {
99
+ ruleId: string;
100
+ title: string;
101
+ category: FindingCategory;
102
+ defaultSeverity: Severity;
103
+ /** Short description of what the rule checks and why. */
104
+ description: string;
105
+ /** Whether occurrences of this rule are mechanically fixable. */
106
+ fixable: boolean;
107
+ }
108
+ /** Options controlling analysis behavior. */
109
+ interface AnalyzeOptions {
110
+ /**
111
+ * Override the auto-detected file kind. Mostly useful for tests and for
112
+ * files whose name does not follow convention.
113
+ */
114
+ forceKind?: FileKind;
115
+ }
116
+
117
+ export { type AnalyzeOptions as A, type FileReport as F, type Grade as G, type ParsedFile as P, type RuleDescriptor as R, type Severity as S, type AnalysisReport as a, type ParsedFrontmatter as b, type FileKind as c, type Finding as d, type FindingCategory as e, SEVERITY_RANK as f };
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@studiomeyer-io/skilldoctor",
3
+ "version": "0.1.0",
4
+ "description": "Linter and security scanner for AI-agent skill and instruction files (Claude Code SKILL.md, AGENTS.md, subagents). Prints an A-F grade, supports --fix, emits JSON + SARIF, ships as a GitHub Action.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "StudioMeyer",
8
+ "homepage": "https://github.com/studiomeyer-io/skilldoctor#readme",
9
+ "bugs": {
10
+ "url": "https://github.com/studiomeyer-io/skilldoctor/issues"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "github:studiomeyer-io/skilldoctor"
15
+ },
16
+ "keywords": [
17
+ "agent-skills",
18
+ "claude",
19
+ "claude-code",
20
+ "agents-md",
21
+ "linter",
22
+ "security",
23
+ "prompt-injection",
24
+ "ai-agents",
25
+ "sarif",
26
+ "supply-chain"
27
+ ],
28
+ "sideEffects": false,
29
+ "engines": {
30
+ "node": ">=20"
31
+ },
32
+ "bin": {
33
+ "skilldoctor": "./dist/cli.js"
34
+ },
35
+ "main": "./dist/index.cjs",
36
+ "module": "./dist/index.js",
37
+ "types": "./dist/index.d.ts",
38
+ "exports": {
39
+ ".": {
40
+ "import": {
41
+ "types": "./dist/index.d.ts",
42
+ "default": "./dist/index.js"
43
+ },
44
+ "require": {
45
+ "types": "./dist/index.d.cts",
46
+ "default": "./dist/index.cjs"
47
+ }
48
+ },
49
+ "./package.json": "./package.json"
50
+ },
51
+ "files": [
52
+ "dist",
53
+ "README.md",
54
+ "LICENSE",
55
+ "SECURITY.md"
56
+ ],
57
+ "scripts": {
58
+ "build": "tsup",
59
+ "dev": "tsup --watch",
60
+ "test": "vitest run",
61
+ "test:watch": "vitest",
62
+ "typecheck": "tsc --noEmit",
63
+ "attw": "attw --pack .",
64
+ "clean": "rm -rf dist",
65
+ "prepublishOnly": "npm run clean && npm run build && npm test && npm run typecheck && npm run attw"
66
+ },
67
+ "dependencies": {
68
+ "yaml": "^2.9.0"
69
+ },
70
+ "devDependencies": {
71
+ "@arethetypeswrong/cli": "^0.18.3",
72
+ "@types/node": "^20.14.0",
73
+ "tsup": "^8.5.0",
74
+ "typescript": "^5.5.0",
75
+ "vitest": "^4.1.0"
76
+ }
77
+ }