skill-preflight 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,303 @@
1
+ export function renderReport(report, format) {
2
+ switch (format) {
3
+ case "json":
4
+ return `${JSON.stringify(report, null, 2)}\n`;
5
+ case "markdown":
6
+ return renderMarkdown(report);
7
+ case "html":
8
+ return renderHtml(report);
9
+ case "sarif":
10
+ return renderSarif(report);
11
+ case "text":
12
+ return renderText(report);
13
+ }
14
+ }
15
+ export function parseFormat(value) {
16
+ if (value === "text" || value === "json" || value === "markdown" || value === "html" || value === "sarif") {
17
+ return value;
18
+ }
19
+ throw new Error(`Unsupported format: ${value}. Use text, json, markdown, html, or sarif.`);
20
+ }
21
+ function renderText(report) {
22
+ const lines = [];
23
+ lines.push("SkillPreflight Report");
24
+ lines.push(`Target: ${report.target}`);
25
+ lines.push(`Generated: ${report.generatedAt}`);
26
+ lines.push(`Skills scanned: ${report.summary.count}`);
27
+ lines.push(`Average score: ${report.summary.averageScore}/100`);
28
+ lines.push("");
29
+ for (const skill of report.reports) {
30
+ lines.push(`${skill.skillName}: ${skill.score}/100 (${skill.grade}) - ${skill.recommendation}`);
31
+ lines.push(`Path: ${skill.rootPath}`);
32
+ lines.push("Category scores:");
33
+ for (const category of skill.categories) {
34
+ lines.push(` - ${category.label}: ${category.score}/${category.maxScore}`);
35
+ }
36
+ lines.push("Metrics:");
37
+ lines.push(` - Files: ${skill.metrics.totalFiles}`);
38
+ lines.push(` - Size: ${formatBytes(skill.metrics.totalBytes)}`);
39
+ lines.push(` - Estimated activation tokens: ${skill.metrics.estimatedActivationTokens}`);
40
+ if (skill.findings.length === 0) {
41
+ lines.push("Findings: none");
42
+ }
43
+ else {
44
+ lines.push("Top findings:");
45
+ for (const finding of skill.findings.slice(0, 8)) {
46
+ lines.push(` - [${finding.severity.toUpperCase()}] ${finding.title}${formatLocation(finding)}`);
47
+ lines.push(` ${finding.description}`);
48
+ lines.push(` Fix: ${finding.recommendation}`);
49
+ }
50
+ if (skill.findings.length > 8) {
51
+ lines.push(` - ... ${skill.findings.length - 8} more findings`);
52
+ }
53
+ }
54
+ lines.push("");
55
+ }
56
+ return `${lines.join("\n")}\n`;
57
+ }
58
+ function renderMarkdown(report) {
59
+ const lines = [];
60
+ lines.push("# SkillPreflight Report");
61
+ lines.push("");
62
+ lines.push(`- Target: \`${report.target}\``);
63
+ lines.push(`- Generated: \`${report.generatedAt}\``);
64
+ lines.push(`- Skills scanned: ${report.summary.count}`);
65
+ lines.push(`- Average score: ${report.summary.averageScore}/100`);
66
+ lines.push(`- High risk skills: ${report.summary.highRiskCount}`);
67
+ lines.push("");
68
+ for (const skill of report.reports) {
69
+ lines.push(`## ${skill.skillName}: ${skill.score}/100 (${skill.grade})`);
70
+ lines.push("");
71
+ lines.push(`**Recommendation:** ${skill.recommendation}`);
72
+ lines.push("");
73
+ lines.push(`**Path:** \`${skill.rootPath}\``);
74
+ lines.push("");
75
+ lines.push("| Category | Score |");
76
+ lines.push("| --- | ---: |");
77
+ for (const category of skill.categories) {
78
+ lines.push(`| ${category.label} | ${category.score}/${category.maxScore} |`);
79
+ }
80
+ lines.push("");
81
+ lines.push("| Metric | Value |");
82
+ lines.push("| --- | ---: |");
83
+ lines.push(`| Files | ${skill.metrics.totalFiles} |`);
84
+ lines.push(`| Size | ${formatBytes(skill.metrics.totalBytes)} |`);
85
+ lines.push(`| Estimated activation tokens | ${skill.metrics.estimatedActivationTokens} |`);
86
+ lines.push("");
87
+ lines.push("### Findings");
88
+ lines.push("");
89
+ if (skill.findings.length === 0) {
90
+ lines.push("No findings.");
91
+ lines.push("");
92
+ continue;
93
+ }
94
+ for (const finding of skill.findings) {
95
+ lines.push(`- **[${finding.severity.toUpperCase()}] ${finding.title}**${formatLocation(finding)}`);
96
+ lines.push(` ${finding.description}`);
97
+ lines.push(` Recommendation: ${finding.recommendation}`);
98
+ }
99
+ lines.push("");
100
+ }
101
+ return `${lines.join("\n")}\n`;
102
+ }
103
+ function renderHtml(report) {
104
+ const skillSections = report.reports.map(renderSkillHtml).join("\n");
105
+ return `<!doctype html>
106
+ <html lang="en">
107
+ <head>
108
+ <meta charset="utf-8">
109
+ <meta name="viewport" content="width=device-width, initial-scale=1">
110
+ <title>SkillPreflight Report</title>
111
+ <style>
112
+ :root { color-scheme: light dark; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
113
+ body { margin: 0; background: #f6f7f9; color: #17202a; }
114
+ main { max-width: 1040px; margin: 0 auto; padding: 32px 20px 56px; }
115
+ header { margin-bottom: 24px; }
116
+ h1 { font-size: 28px; margin: 0 0 8px; }
117
+ h2 { font-size: 22px; margin: 0 0 12px; }
118
+ .summary, .skill { background: #fff; border: 1px solid #d9dee7; border-radius: 8px; padding: 18px; margin: 16px 0; }
119
+ .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 12px; }
120
+ .metric { background: #f0f3f7; border-radius: 6px; padding: 12px; }
121
+ .metric span { display: block; color: #5f6b7a; font-size: 12px; }
122
+ .metric strong { display: block; font-size: 20px; margin-top: 4px; }
123
+ table { width: 100%; border-collapse: collapse; margin: 12px 0; }
124
+ th, td { border-bottom: 1px solid #e5e8ef; padding: 9px; text-align: left; }
125
+ th:last-child, td:last-child { text-align: right; }
126
+ .finding { border-left: 4px solid #8a97a8; padding: 10px 12px; background: #f8fafc; margin: 10px 0; border-radius: 4px; }
127
+ .critical, .high { border-left-color: #c0392b; }
128
+ .medium { border-left-color: #d9822b; }
129
+ .low { border-left-color: #3978c3; }
130
+ code { background: #eef1f5; padding: 2px 5px; border-radius: 4px; }
131
+ @media (prefers-color-scheme: dark) {
132
+ body { background: #11161d; color: #e8edf4; }
133
+ .summary, .skill { background: #18212b; border-color: #2b3847; }
134
+ .metric, .finding { background: #202b36; }
135
+ th, td { border-bottom-color: #2b3847; }
136
+ code { background: #263240; }
137
+ }
138
+ </style>
139
+ </head>
140
+ <body>
141
+ <main>
142
+ <header>
143
+ <h1>SkillPreflight Report</h1>
144
+ <div>Target: <code>${escapeHtml(report.target)}</code></div>
145
+ <div>Generated: <code>${escapeHtml(report.generatedAt)}</code></div>
146
+ </header>
147
+ <section class="summary">
148
+ <div class="grid">
149
+ <div class="metric"><span>Skills scanned</span><strong>${report.summary.count}</strong></div>
150
+ <div class="metric"><span>Average score</span><strong>${report.summary.averageScore}/100</strong></div>
151
+ <div class="metric"><span>Minimum score</span><strong>${report.summary.minScore}/100</strong></div>
152
+ <div class="metric"><span>High risk</span><strong>${report.summary.highRiskCount}</strong></div>
153
+ </div>
154
+ </section>
155
+ ${skillSections}
156
+ </main>
157
+ </body>
158
+ </html>
159
+ `;
160
+ }
161
+ function renderSarif(report) {
162
+ const ruleMap = new Map();
163
+ const results = [];
164
+ for (const skill of report.reports) {
165
+ for (const finding of skill.findings) {
166
+ ruleMap.set(finding.id, finding);
167
+ results.push({
168
+ ruleId: finding.id,
169
+ level: sarifLevel(finding),
170
+ message: {
171
+ text: `${finding.title}: ${finding.description} Recommendation: ${finding.recommendation}`
172
+ },
173
+ locations: [
174
+ {
175
+ physicalLocation: {
176
+ artifactLocation: {
177
+ uri: finding.file ? normalizeSarifUri(finding.file) : normalizeSarifUri(skill.rootPath)
178
+ },
179
+ region: finding.line ? { startLine: finding.line } : undefined
180
+ }
181
+ }
182
+ ],
183
+ properties: {
184
+ category: finding.category,
185
+ severity: finding.severity,
186
+ scoreImpact: finding.scoreImpact,
187
+ skillName: skill.skillName,
188
+ skillScore: skill.score
189
+ }
190
+ });
191
+ }
192
+ }
193
+ const rules = [...ruleMap.values()].map((finding) => ({
194
+ id: finding.id,
195
+ name: finding.title,
196
+ shortDescription: {
197
+ text: finding.title
198
+ },
199
+ fullDescription: {
200
+ text: finding.description
201
+ },
202
+ help: {
203
+ text: finding.recommendation
204
+ },
205
+ properties: {
206
+ category: finding.category,
207
+ severity: finding.severity
208
+ }
209
+ }));
210
+ return `${JSON.stringify({
211
+ version: "2.1.0",
212
+ $schema: "https://json.schemastore.org/sarif-2.1.0.json",
213
+ runs: [
214
+ {
215
+ tool: {
216
+ driver: {
217
+ name: "SkillPreflight",
218
+ informationUri: "https://github.com/agent-contracts/skill-preflight",
219
+ rules
220
+ }
221
+ },
222
+ automationDetails: {
223
+ id: "skill-preflight"
224
+ },
225
+ properties: {
226
+ target: report.target,
227
+ generatedAt: report.generatedAt,
228
+ averageScore: report.summary.averageScore,
229
+ highRiskCount: report.summary.highRiskCount
230
+ },
231
+ results
232
+ }
233
+ ]
234
+ }, null, 2)}\n`;
235
+ }
236
+ function renderSkillHtml(skill) {
237
+ const categories = skill.categories
238
+ .map((category) => `<tr><td>${escapeHtml(category.label)}</td><td>${category.score}/${category.maxScore}</td></tr>`)
239
+ .join("");
240
+ const findings = skill.findings.length === 0
241
+ ? "<p>No findings.</p>"
242
+ : skill.findings
243
+ .map((finding) => `<div class="finding ${finding.severity}">
244
+ <strong>[${finding.severity.toUpperCase()}] ${escapeHtml(finding.title)}</strong>
245
+ <div>${escapeHtml(formatLocation(finding))}</div>
246
+ <p>${escapeHtml(finding.description)}</p>
247
+ <p><strong>Recommendation:</strong> ${escapeHtml(finding.recommendation)}</p>
248
+ </div>`)
249
+ .join("\n");
250
+ return `<section class="skill">
251
+ <h2>${escapeHtml(skill.skillName)}: ${skill.score}/100 (${skill.grade})</h2>
252
+ <p><strong>Recommendation:</strong> ${escapeHtml(skill.recommendation)}</p>
253
+ <p><strong>Path:</strong> <code>${escapeHtml(skill.rootPath)}</code></p>
254
+ <div class="grid">
255
+ <div class="metric"><span>Files</span><strong>${skill.metrics.totalFiles}</strong></div>
256
+ <div class="metric"><span>Size</span><strong>${formatBytes(skill.metrics.totalBytes)}</strong></div>
257
+ <div class="metric"><span>Activation tokens</span><strong>${skill.metrics.estimatedActivationTokens}</strong></div>
258
+ </div>
259
+ <table>
260
+ <thead><tr><th>Category</th><th>Score</th></tr></thead>
261
+ <tbody>${categories}</tbody>
262
+ </table>
263
+ <h3>Findings</h3>
264
+ ${findings}
265
+ </section>`;
266
+ }
267
+ function formatLocation(finding) {
268
+ if (!finding.file) {
269
+ return "";
270
+ }
271
+ return finding.line ? ` (${finding.file}:${finding.line})` : ` (${finding.file})`;
272
+ }
273
+ function formatBytes(bytes) {
274
+ if (bytes < 1024) {
275
+ return `${bytes} B`;
276
+ }
277
+ const kb = bytes / 1024;
278
+ if (kb < 1024) {
279
+ return `${kb.toFixed(1)} KB`;
280
+ }
281
+ return `${(kb / 1024).toFixed(1)} MB`;
282
+ }
283
+ function escapeHtml(value) {
284
+ return value
285
+ .replaceAll("&", "&amp;")
286
+ .replaceAll("<", "&lt;")
287
+ .replaceAll(">", "&gt;")
288
+ .replaceAll('"', "&quot;")
289
+ .replaceAll("'", "&#039;");
290
+ }
291
+ function sarifLevel(finding) {
292
+ if (finding.severity === "critical" || finding.severity === "high") {
293
+ return "error";
294
+ }
295
+ if (finding.severity === "medium") {
296
+ return "warning";
297
+ }
298
+ return "note";
299
+ }
300
+ function normalizeSarifUri(value) {
301
+ return value.replaceAll("\\", "/");
302
+ }
303
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/report/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ # GitHub Action
2
+
3
+ SkillPreflight can run in a skill repository before users install the skill.
4
+
5
+ ## Basic Workflow
6
+
7
+ ```yaml
8
+ name: SkillPreflight
9
+
10
+ on:
11
+ pull_request:
12
+ push:
13
+ branches:
14
+ - main
15
+
16
+ jobs:
17
+ scan:
18
+ runs-on: ubuntu-latest
19
+
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - uses: agent-contracts/skill-preflight@v1
24
+ with:
25
+ target: "."
26
+ fail-below: "70"
27
+ ```
28
+
29
+ ## SARIF Upload
30
+
31
+ Use SARIF when you want findings to appear in GitHub code scanning.
32
+
33
+ ```yaml
34
+ name: SkillPreflight
35
+
36
+ on:
37
+ pull_request:
38
+ push:
39
+ branches:
40
+ - main
41
+
42
+ permissions:
43
+ security-events: write
44
+ contents: read
45
+
46
+ jobs:
47
+ scan:
48
+ runs-on: ubuntu-latest
49
+
50
+ steps:
51
+ - uses: actions/checkout@v4
52
+
53
+ - uses: agent-contracts/skill-preflight@v1
54
+ with:
55
+ target: "."
56
+ format: sarif
57
+ out: skill-preflight.sarif
58
+ fail-below: "70"
59
+
60
+ - uses: github/codeql-action/upload-sarif@v3
61
+ if: always()
62
+ with:
63
+ sarif_file: skill-preflight.sarif
64
+ ```
65
+
66
+ ## Notes
67
+
68
+ The composite action runs the published npm package with `npx`.
69
+
70
+ Before the first public release, replace the action usage with a direct command:
71
+
72
+ ```yaml
73
+ - run: npx -y skill-preflight@latest scan . --fail-below 70
74
+ ```
@@ -0,0 +1,57 @@
1
+ # Release Checklist
2
+
3
+ Use this checklist before publishing SkillPreflight.
4
+
5
+ ## Local Verification
6
+
7
+ ```bash
8
+ npm install
9
+ npm test
10
+ npm pack --dry-run
11
+ node dist/index.js scan examples/risky-skill
12
+ ```
13
+
14
+ ## npm Publish
15
+
16
+ 1. Confirm `package.json` name, version, license, and repository fields.
17
+ 2. Log in:
18
+
19
+ ```bash
20
+ npm login
21
+ ```
22
+
23
+ 3. Publish:
24
+
25
+ ```bash
26
+ npm publish --access public
27
+ ```
28
+
29
+ After publishing, users can run:
30
+
31
+ ```bash
32
+ npx skill-preflight scan ./my-skill
33
+ ```
34
+
35
+ ## GitHub Repository
36
+
37
+ 1. Create a public GitHub repository.
38
+ 2. Add the remote:
39
+
40
+ ```bash
41
+ git remote add origin https://github.com/agent-contracts/skill-preflight.git
42
+ ```
43
+
44
+ 3. Commit and push:
45
+
46
+ ```bash
47
+ git add .
48
+ git commit -m "Initial SkillPreflight MVP"
49
+ git branch -M main
50
+ git push -u origin main
51
+ ```
52
+
53
+ ## First Milestones
54
+
55
+ - Add a public score badge for README files.
56
+ - Add a website or hosted report viewer.
57
+ - Add npm provenance and signed release workflow.
package/docs/rules.md ADDED
@@ -0,0 +1,53 @@
1
+ # Rule Catalog
2
+
3
+ SkillPreflight uses static analysis only. It reads files but does not execute skill scripts.
4
+
5
+ ## Security
6
+
7
+ - Remote script execution, such as `curl ... | sh`.
8
+ - Dynamic shell, Node.js, or PowerShell execution.
9
+ - PowerShell encoded commands and execution policy bypasses.
10
+ - Broad destructive delete commands.
11
+ - Secret-like local data access, including `.env`, SSH keys, API keys, browser cookies, and login data.
12
+ - Suspicious webhook, paste, and ad-hoc upload endpoints.
13
+ - Prompt injection phrases that attempt to override system or developer instructions.
14
+
15
+ ## Dependency and Install Risk
16
+
17
+ - npm lifecycle scripts: `preinstall`, `install`, `postinstall`, and `prepare`.
18
+ - Dangerous npm lifecycle scripts that download or dynamically execute code.
19
+ - Node dependencies using `*`, `latest`, `^`, `~`, Git URLs, HTTP URLs, or local file specs.
20
+ - Python `requirements.txt` dependencies that are unpinned.
21
+ - Python dependencies that install from remote URLs or Git repositories.
22
+ - Dependency manifests without lockfiles.
23
+
24
+ ## MCP Config Risk
25
+
26
+ SkillPreflight detects common MCP JSON files, including `.mcp.json`, `mcp*.json`, and `claude_desktop_config.json`.
27
+
28
+ It flags:
29
+
30
+ - MCP servers launched through a shell, such as `bash`, `sh`, `cmd`, or `powershell`.
31
+ - `npx`, `uvx`, and `pipx` MCP servers without pinned package versions.
32
+ - Broad local paths such as user home directories.
33
+ - Hardcoded secret-like values in MCP `env`.
34
+
35
+ ## Token Efficiency
36
+
37
+ - Large `SKILL.md` files.
38
+ - Large main skill files without a progressive disclosure structure.
39
+ - Repeated long instruction lines.
40
+
41
+ ## Reliability and Maintainability
42
+
43
+ - Missing README.
44
+ - Missing license.
45
+ - Missing skill metadata frontmatter.
46
+ - Missing examples.
47
+ - Missing tests, fixtures, or evals.
48
+ - Vague operational language.
49
+
50
+ ## Compatibility
51
+
52
+ - Hardcoded user paths.
53
+ - OS-specific commands without fallback guidance.
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "skill-preflight",
3
+ "version": "0.1.0",
4
+ "description": "Pre-install safety, token, and maintainability scorecard for AI agent skills.",
5
+ "type": "module",
6
+ "bin": {
7
+ "skill-preflight": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "docs",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc -p tsconfig.json",
17
+ "dev": "tsx src/index.ts",
18
+ "test": "npm run build && node --test test/scan.test.mjs",
19
+ "scan:good": "tsx src/index.ts scan examples/good-skill",
20
+ "scan:risky": "tsx src/index.ts scan examples/risky-skill",
21
+ "prepack": "npm run build"
22
+ },
23
+ "keywords": [
24
+ "agent",
25
+ "skills",
26
+ "security",
27
+ "scanner",
28
+ "codex",
29
+ "claude",
30
+ "mcp",
31
+ "llm"
32
+ ],
33
+ "author": "",
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/agent-contracts/skill-preflight.git"
38
+ },
39
+ "bugs": {
40
+ "url": "https://github.com/agent-contracts/skill-preflight/issues"
41
+ },
42
+ "homepage": "https://github.com/agent-contracts/skill-preflight#readme",
43
+ "publishConfig": {
44
+ "registry": "https://registry.npmjs.org/"
45
+ },
46
+ "engines": {
47
+ "node": ">=20"
48
+ },
49
+ "dependencies": {
50
+ "commander": "^12.1.0"
51
+ },
52
+ "devDependencies": {
53
+ "@types/node": "^20.19.0",
54
+ "tsx": "^4.20.3",
55
+ "typescript": "^5.8.3"
56
+ }
57
+ }