@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,220 @@
1
+ import { A as AnalyzeOptions, F as FileReport, a as AnalysisReport, P as ParsedFile, b as ParsedFrontmatter, c as FileKind, d as Finding, G as Grade, S as Severity, R as RuleDescriptor } from './types-lUfaWSNG.cjs';
2
+ export { e as FindingCategory, f as SEVERITY_RANK } from './types-lUfaWSNG.cjs';
3
+
4
+ /**
5
+ * Analyzer — the central orchestration layer. Parses, lints, security-scans,
6
+ * and grades one file or a set of files, and runs cross-file rules
7
+ * (duplicate names).
8
+ *
9
+ * This module is pure with respect to the filesystem when given content
10
+ * directly (`analyzeContent`); `analyzePaths` reads files for convenience.
11
+ */
12
+
13
+ /** Analyze a single file given its content. */
14
+ declare function analyzeContent(filePath: string, content: string, options?: AnalyzeOptions): FileReport;
15
+ /** Analyze multiple files given their {path, content} pairs (no FS access). */
16
+ declare function analyzeFiles(inputs: readonly {
17
+ filePath: string;
18
+ content: string;
19
+ }[], options?: AnalyzeOptions): AnalysisReport;
20
+ /** Analyze a list of file paths, reading each from disk. */
21
+ declare function analyzePaths(paths: readonly string[], options?: AnalyzeOptions): AnalysisReport;
22
+ /** Re-parse content (used by the CLI to feed the fixer). */
23
+ declare function parseForFix(filePath: string, content: string): ParsedFile;
24
+
25
+ /**
26
+ * Parsing layer: split an agent-instruction file into frontmatter + body,
27
+ * parse the YAML frontmatter, and classify the file kind.
28
+ *
29
+ * We do NOT execute anything. We only read text. The YAML parser is configured
30
+ * defensively (no custom tags, no anchors expansion blow-up via the default
31
+ * parser limits in the `yaml` package).
32
+ */
33
+
34
+ /**
35
+ * Extract a frontmatter block from raw file contents.
36
+ *
37
+ * Frontmatter must start on line 1 with `---` and end with a line that is
38
+ * exactly `---` (optionally followed by trailing whitespace). Anything after
39
+ * the closing fence is the body.
40
+ */
41
+ declare function extractFrontmatter(raw: string): {
42
+ frontmatter: ParsedFrontmatter;
43
+ body: string;
44
+ bodyStartLine: number;
45
+ };
46
+ /**
47
+ * Classify the file kind from its path and parsed frontmatter.
48
+ *
49
+ * Heuristics (deliberately conservative — when unsure we lean "skill" only if
50
+ * the filename is SKILL.md, otherwise "unknown" or "agents-md"):
51
+ * - `AGENTS.md` (any case) -> agents-md
52
+ * - `SKILL.md` (any case) -> skill
53
+ * - a `.md` file inside an `agents/` directory with frontmatter -> subagent
54
+ * - a `.md` file with `name`+`description` frontmatter -> skill (best guess)
55
+ * - otherwise -> unknown
56
+ */
57
+ declare function detectKind(filePath: string, frontmatter: ParsedFrontmatter): FileKind;
58
+ /** Parse raw file contents at a given path into a ParsedFile. */
59
+ declare function parseFile(filePath: string, raw: string, forceKind?: FileKind): ParsedFile;
60
+
61
+ /**
62
+ * Grading: turn a set of findings into a 0-100 score and an A-F letter grade.
63
+ *
64
+ * Security findings are weighted more heavily than lint findings, because the
65
+ * whole point of skilldoctor is to surface supply-chain risk. The mapping is
66
+ * deterministic and documented so grades are reproducible.
67
+ */
68
+
69
+ /** Compute a 0-100 score from findings (100 = clean). */
70
+ declare function scoreFindings(findings: readonly Finding[]): number;
71
+ /** Map a numeric score to a letter grade. */
72
+ declare function scoreToGrade(score: number): Grade;
73
+ /** Tally findings by severity. */
74
+ declare function tally(findings: readonly Finding[]): Record<Severity, number>;
75
+ /**
76
+ * Aggregate per-file scores into one overall score. We use a worst-weighted
77
+ * average: the mean of file scores, pulled toward the single worst file so a
78
+ * batch with one dangerous skill cannot be averaged into a good grade.
79
+ */
80
+ declare function aggregateScore(fileScores: readonly number[]): number;
81
+
82
+ /**
83
+ * Central rule registry — the single source of truth for every rule
84
+ * skilldoctor can emit. SARIF requires every emitted `ruleId` to have a
85
+ * matching `reportingDescriptor` in `driver.rules`, so we generate that list
86
+ * from here. The README rule table is also derived from this.
87
+ */
88
+
89
+ declare const RULES: readonly RuleDescriptor[];
90
+ /** Look up a rule descriptor by id. Throws if unknown (programmer error). */
91
+ declare function getRule(ruleId: string): RuleDescriptor;
92
+ /** All registered rule ids. */
93
+ declare function allRuleIds(): string[];
94
+
95
+ /**
96
+ * --fix engine. Mechanically repairs the safe, unambiguous subset of findings.
97
+ *
98
+ * HARD RULE: we never rewrite the markdown body content (that could neutralize
99
+ * malicious instructions silently, or mangle legitimate prose). We only touch
100
+ * the YAML frontmatter, and only in deterministic, reversible ways:
101
+ * 1. trim trailing whitespace on frontmatter lines
102
+ * 2. add a missing `description:` stub with a TODO marker
103
+ * 3. de-duplicate tools within `allowed-tools` / `tools` (preserving order)
104
+ *
105
+ * The fixer is idempotent: running it twice produces identical output.
106
+ */
107
+
108
+ /** Marker used for an inserted stub so humans (and tests) can find it. */
109
+ declare const DESCRIPTION_STUB = "TODO describe what this skill does and when to use it.";
110
+ interface FixResult {
111
+ /** The (possibly) rewritten file contents. */
112
+ output: string;
113
+ /** True if anything changed. */
114
+ changed: boolean;
115
+ /** Names of the fixes applied. */
116
+ applied: string[];
117
+ }
118
+ /**
119
+ * Apply mechanical fixes to a parsed file's raw text and return the result.
120
+ * Only skill/subagent files with frontmatter are fixed; AGENTS.md/unknown are
121
+ * returned unchanged.
122
+ */
123
+ declare function fixFile(file: ParsedFile): FixResult;
124
+
125
+ /**
126
+ * File discovery. Given path(s) — a file, a directory, or a glob — resolve the
127
+ * set of candidate markdown files to analyze.
128
+ *
129
+ * Deliberately dependency-free: a small recursive walker plus a minimal glob
130
+ * matcher cover our needs (`**`, `*`, `?`) without pulling in fast-glob/globby.
131
+ */
132
+ /** True if a path string contains glob metacharacters. */
133
+ declare function isGlob(p: string): boolean;
134
+ /**
135
+ * Convert a glob to an anchored RegExp. Supports `**` (any depth, incl. zero),
136
+ * `*` (within a path segment), `?` (single char), and literal text. Forward
137
+ * slashes only (we normalize input to posix).
138
+ */
139
+ declare function globToRegExp(glob: string): RegExp;
140
+ /**
141
+ * Resolve one or more input specs into a deduplicated, sorted list of absolute
142
+ * file paths. Each spec may be a file, a directory, or a glob.
143
+ */
144
+ declare function discoverFiles(specs: readonly string[]): string[];
145
+
146
+ /**
147
+ * Terminal reporter. Human-readable, optionally colorized (auto-disabled when
148
+ * not a TTY or when NO_COLOR is set). No external dependency — small ANSI
149
+ * helpers only.
150
+ */
151
+
152
+ interface TerminalOptions {
153
+ /** Force color on/off. Defaults to auto-detect. */
154
+ color?: boolean;
155
+ }
156
+ /** Render a full analysis report as a terminal string. */
157
+ declare function renderTerminal(report: AnalysisReport, options?: TerminalOptions): string;
158
+
159
+ /**
160
+ * JSON reporter. A stable, documented machine-readable shape of an analysis
161
+ * report. Keep this versioned so downstream consumers can rely on it.
162
+ */
163
+
164
+ declare const JSON_REPORT_VERSION: 1;
165
+ interface JsonReport {
166
+ /** Schema version of this JSON output. */
167
+ schemaVersion: typeof JSON_REPORT_VERSION;
168
+ /** The tool that produced it. */
169
+ tool: {
170
+ name: "skilldoctor";
171
+ version: string;
172
+ };
173
+ /** Aggregate grade + score. */
174
+ summary: {
175
+ grade: string;
176
+ score: number;
177
+ fileCount: number;
178
+ errors: number;
179
+ warnings: number;
180
+ infos: number;
181
+ };
182
+ /** Per-file results. */
183
+ files: AnalysisReport["files"];
184
+ }
185
+ /** Build the JSON report object. */
186
+ declare function toJsonReport(report: AnalysisReport, toolVersion: string): JsonReport;
187
+ /** Serialize the JSON report. */
188
+ declare function jsonString(report: AnalysisReport, toolVersion: string): string;
189
+
190
+ /**
191
+ * SARIF 2.1.0 reporter.
192
+ *
193
+ * Produces a valid SARIF log that GitHub code scanning accepts:
194
+ * - exactly one run with a tool.driver
195
+ * - driver.rules[] contains a reportingDescriptor for EVERY ruleId we emit
196
+ * (so no result references a missing rule)
197
+ * - each result has a ruleId, ruleIndex, level, message, and a physical
198
+ * location with a region (1-based line/column)
199
+ * - each result carries partialFingerprints for stable de-duplication across
200
+ * runs (we hash ruleId + relative file + a normalized snippet, NOT the line
201
+ * number, so cosmetic line shifts don't churn fingerprints)
202
+ *
203
+ * Spec ref: SARIF v2.1.0 (OASIS). Schema:
204
+ * https://json.schemastore.org/sarif-2.1.0.json
205
+ */
206
+
207
+ declare const SARIF_VERSION = "2.1.0";
208
+ declare const SARIF_SCHEMA = "https://json.schemastore.org/sarif-2.1.0.json";
209
+ /** Build the SARIF log object for an analysis report. */
210
+ declare function toSarif(report: AnalysisReport, options?: {
211
+ baseDir?: string;
212
+ version?: string;
213
+ }): unknown;
214
+ /** Serialize the SARIF log to a JSON string. */
215
+ declare function sarifString(report: AnalysisReport, options?: {
216
+ baseDir?: string;
217
+ version?: string;
218
+ }): string;
219
+
220
+ export { AnalysisReport, AnalyzeOptions, DESCRIPTION_STUB, FileKind, FileReport, Finding, type FixResult, Grade, JSON_REPORT_VERSION, type JsonReport, ParsedFile, ParsedFrontmatter, RULES, RuleDescriptor, SARIF_SCHEMA, SARIF_VERSION, Severity, aggregateScore, allRuleIds, analyzeContent, analyzeFiles, analyzePaths, detectKind, discoverFiles, extractFrontmatter, fixFile, getRule, globToRegExp, isGlob, jsonString, parseFile, parseForFix, renderTerminal, sarifString, scoreFindings, scoreToGrade, tally, toJsonReport, toSarif };
@@ -0,0 +1,220 @@
1
+ import { A as AnalyzeOptions, F as FileReport, a as AnalysisReport, P as ParsedFile, b as ParsedFrontmatter, c as FileKind, d as Finding, G as Grade, S as Severity, R as RuleDescriptor } from './types-lUfaWSNG.js';
2
+ export { e as FindingCategory, f as SEVERITY_RANK } from './types-lUfaWSNG.js';
3
+
4
+ /**
5
+ * Analyzer — the central orchestration layer. Parses, lints, security-scans,
6
+ * and grades one file or a set of files, and runs cross-file rules
7
+ * (duplicate names).
8
+ *
9
+ * This module is pure with respect to the filesystem when given content
10
+ * directly (`analyzeContent`); `analyzePaths` reads files for convenience.
11
+ */
12
+
13
+ /** Analyze a single file given its content. */
14
+ declare function analyzeContent(filePath: string, content: string, options?: AnalyzeOptions): FileReport;
15
+ /** Analyze multiple files given their {path, content} pairs (no FS access). */
16
+ declare function analyzeFiles(inputs: readonly {
17
+ filePath: string;
18
+ content: string;
19
+ }[], options?: AnalyzeOptions): AnalysisReport;
20
+ /** Analyze a list of file paths, reading each from disk. */
21
+ declare function analyzePaths(paths: readonly string[], options?: AnalyzeOptions): AnalysisReport;
22
+ /** Re-parse content (used by the CLI to feed the fixer). */
23
+ declare function parseForFix(filePath: string, content: string): ParsedFile;
24
+
25
+ /**
26
+ * Parsing layer: split an agent-instruction file into frontmatter + body,
27
+ * parse the YAML frontmatter, and classify the file kind.
28
+ *
29
+ * We do NOT execute anything. We only read text. The YAML parser is configured
30
+ * defensively (no custom tags, no anchors expansion blow-up via the default
31
+ * parser limits in the `yaml` package).
32
+ */
33
+
34
+ /**
35
+ * Extract a frontmatter block from raw file contents.
36
+ *
37
+ * Frontmatter must start on line 1 with `---` and end with a line that is
38
+ * exactly `---` (optionally followed by trailing whitespace). Anything after
39
+ * the closing fence is the body.
40
+ */
41
+ declare function extractFrontmatter(raw: string): {
42
+ frontmatter: ParsedFrontmatter;
43
+ body: string;
44
+ bodyStartLine: number;
45
+ };
46
+ /**
47
+ * Classify the file kind from its path and parsed frontmatter.
48
+ *
49
+ * Heuristics (deliberately conservative — when unsure we lean "skill" only if
50
+ * the filename is SKILL.md, otherwise "unknown" or "agents-md"):
51
+ * - `AGENTS.md` (any case) -> agents-md
52
+ * - `SKILL.md` (any case) -> skill
53
+ * - a `.md` file inside an `agents/` directory with frontmatter -> subagent
54
+ * - a `.md` file with `name`+`description` frontmatter -> skill (best guess)
55
+ * - otherwise -> unknown
56
+ */
57
+ declare function detectKind(filePath: string, frontmatter: ParsedFrontmatter): FileKind;
58
+ /** Parse raw file contents at a given path into a ParsedFile. */
59
+ declare function parseFile(filePath: string, raw: string, forceKind?: FileKind): ParsedFile;
60
+
61
+ /**
62
+ * Grading: turn a set of findings into a 0-100 score and an A-F letter grade.
63
+ *
64
+ * Security findings are weighted more heavily than lint findings, because the
65
+ * whole point of skilldoctor is to surface supply-chain risk. The mapping is
66
+ * deterministic and documented so grades are reproducible.
67
+ */
68
+
69
+ /** Compute a 0-100 score from findings (100 = clean). */
70
+ declare function scoreFindings(findings: readonly Finding[]): number;
71
+ /** Map a numeric score to a letter grade. */
72
+ declare function scoreToGrade(score: number): Grade;
73
+ /** Tally findings by severity. */
74
+ declare function tally(findings: readonly Finding[]): Record<Severity, number>;
75
+ /**
76
+ * Aggregate per-file scores into one overall score. We use a worst-weighted
77
+ * average: the mean of file scores, pulled toward the single worst file so a
78
+ * batch with one dangerous skill cannot be averaged into a good grade.
79
+ */
80
+ declare function aggregateScore(fileScores: readonly number[]): number;
81
+
82
+ /**
83
+ * Central rule registry — the single source of truth for every rule
84
+ * skilldoctor can emit. SARIF requires every emitted `ruleId` to have a
85
+ * matching `reportingDescriptor` in `driver.rules`, so we generate that list
86
+ * from here. The README rule table is also derived from this.
87
+ */
88
+
89
+ declare const RULES: readonly RuleDescriptor[];
90
+ /** Look up a rule descriptor by id. Throws if unknown (programmer error). */
91
+ declare function getRule(ruleId: string): RuleDescriptor;
92
+ /** All registered rule ids. */
93
+ declare function allRuleIds(): string[];
94
+
95
+ /**
96
+ * --fix engine. Mechanically repairs the safe, unambiguous subset of findings.
97
+ *
98
+ * HARD RULE: we never rewrite the markdown body content (that could neutralize
99
+ * malicious instructions silently, or mangle legitimate prose). We only touch
100
+ * the YAML frontmatter, and only in deterministic, reversible ways:
101
+ * 1. trim trailing whitespace on frontmatter lines
102
+ * 2. add a missing `description:` stub with a TODO marker
103
+ * 3. de-duplicate tools within `allowed-tools` / `tools` (preserving order)
104
+ *
105
+ * The fixer is idempotent: running it twice produces identical output.
106
+ */
107
+
108
+ /** Marker used for an inserted stub so humans (and tests) can find it. */
109
+ declare const DESCRIPTION_STUB = "TODO describe what this skill does and when to use it.";
110
+ interface FixResult {
111
+ /** The (possibly) rewritten file contents. */
112
+ output: string;
113
+ /** True if anything changed. */
114
+ changed: boolean;
115
+ /** Names of the fixes applied. */
116
+ applied: string[];
117
+ }
118
+ /**
119
+ * Apply mechanical fixes to a parsed file's raw text and return the result.
120
+ * Only skill/subagent files with frontmatter are fixed; AGENTS.md/unknown are
121
+ * returned unchanged.
122
+ */
123
+ declare function fixFile(file: ParsedFile): FixResult;
124
+
125
+ /**
126
+ * File discovery. Given path(s) — a file, a directory, or a glob — resolve the
127
+ * set of candidate markdown files to analyze.
128
+ *
129
+ * Deliberately dependency-free: a small recursive walker plus a minimal glob
130
+ * matcher cover our needs (`**`, `*`, `?`) without pulling in fast-glob/globby.
131
+ */
132
+ /** True if a path string contains glob metacharacters. */
133
+ declare function isGlob(p: string): boolean;
134
+ /**
135
+ * Convert a glob to an anchored RegExp. Supports `**` (any depth, incl. zero),
136
+ * `*` (within a path segment), `?` (single char), and literal text. Forward
137
+ * slashes only (we normalize input to posix).
138
+ */
139
+ declare function globToRegExp(glob: string): RegExp;
140
+ /**
141
+ * Resolve one or more input specs into a deduplicated, sorted list of absolute
142
+ * file paths. Each spec may be a file, a directory, or a glob.
143
+ */
144
+ declare function discoverFiles(specs: readonly string[]): string[];
145
+
146
+ /**
147
+ * Terminal reporter. Human-readable, optionally colorized (auto-disabled when
148
+ * not a TTY or when NO_COLOR is set). No external dependency — small ANSI
149
+ * helpers only.
150
+ */
151
+
152
+ interface TerminalOptions {
153
+ /** Force color on/off. Defaults to auto-detect. */
154
+ color?: boolean;
155
+ }
156
+ /** Render a full analysis report as a terminal string. */
157
+ declare function renderTerminal(report: AnalysisReport, options?: TerminalOptions): string;
158
+
159
+ /**
160
+ * JSON reporter. A stable, documented machine-readable shape of an analysis
161
+ * report. Keep this versioned so downstream consumers can rely on it.
162
+ */
163
+
164
+ declare const JSON_REPORT_VERSION: 1;
165
+ interface JsonReport {
166
+ /** Schema version of this JSON output. */
167
+ schemaVersion: typeof JSON_REPORT_VERSION;
168
+ /** The tool that produced it. */
169
+ tool: {
170
+ name: "skilldoctor";
171
+ version: string;
172
+ };
173
+ /** Aggregate grade + score. */
174
+ summary: {
175
+ grade: string;
176
+ score: number;
177
+ fileCount: number;
178
+ errors: number;
179
+ warnings: number;
180
+ infos: number;
181
+ };
182
+ /** Per-file results. */
183
+ files: AnalysisReport["files"];
184
+ }
185
+ /** Build the JSON report object. */
186
+ declare function toJsonReport(report: AnalysisReport, toolVersion: string): JsonReport;
187
+ /** Serialize the JSON report. */
188
+ declare function jsonString(report: AnalysisReport, toolVersion: string): string;
189
+
190
+ /**
191
+ * SARIF 2.1.0 reporter.
192
+ *
193
+ * Produces a valid SARIF log that GitHub code scanning accepts:
194
+ * - exactly one run with a tool.driver
195
+ * - driver.rules[] contains a reportingDescriptor for EVERY ruleId we emit
196
+ * (so no result references a missing rule)
197
+ * - each result has a ruleId, ruleIndex, level, message, and a physical
198
+ * location with a region (1-based line/column)
199
+ * - each result carries partialFingerprints for stable de-duplication across
200
+ * runs (we hash ruleId + relative file + a normalized snippet, NOT the line
201
+ * number, so cosmetic line shifts don't churn fingerprints)
202
+ *
203
+ * Spec ref: SARIF v2.1.0 (OASIS). Schema:
204
+ * https://json.schemastore.org/sarif-2.1.0.json
205
+ */
206
+
207
+ declare const SARIF_VERSION = "2.1.0";
208
+ declare const SARIF_SCHEMA = "https://json.schemastore.org/sarif-2.1.0.json";
209
+ /** Build the SARIF log object for an analysis report. */
210
+ declare function toSarif(report: AnalysisReport, options?: {
211
+ baseDir?: string;
212
+ version?: string;
213
+ }): unknown;
214
+ /** Serialize the SARIF log to a JSON string. */
215
+ declare function sarifString(report: AnalysisReport, options?: {
216
+ baseDir?: string;
217
+ version?: string;
218
+ }): string;
219
+
220
+ export { AnalysisReport, AnalyzeOptions, DESCRIPTION_STUB, FileKind, FileReport, Finding, type FixResult, Grade, JSON_REPORT_VERSION, type JsonReport, ParsedFile, ParsedFrontmatter, RULES, RuleDescriptor, SARIF_SCHEMA, SARIF_VERSION, Severity, aggregateScore, allRuleIds, analyzeContent, analyzeFiles, analyzePaths, detectKind, discoverFiles, extractFrontmatter, fixFile, getRule, globToRegExp, isGlob, jsonString, parseFile, parseForFix, renderTerminal, sarifString, scoreFindings, scoreToGrade, tally, toJsonReport, toSarif };