
Sdk Docs Auditor
- 77 installs
- 93 repo stars
- Updated June 28, 2026
- infrasity-labs/dev-gtm-claude-skills
SDK Docs Auditor is an agent skill that crawls an SDK docs site and delivers a scored, cross-referenced HTML quality report.
About
SDK Docs Auditor is a Dev GTM agent skill for solo founders and small teams shipping SDKs or developer products who need an objective documentation quality pass without hiring a technical writer first. Given a documentation URL, the agent follows a fixed pipeline: discover pages via llms.txt, sitemap.xml, or nav crawl; read relevant SDK content; evaluate six canonical sections; and cross-reference every alleged gap against the rest of the site so duplicates and buried coverage are not mislabeled missing. Each section receives a 0–100 score and tier, rolled into a polished HTML report users can download and share with eng or marketing. Triggers are explicit—audit, review, analyse, score, completeness, gaps alongside a URL—and the skill insists you never skip the workflow. Use in Ship before launch, in Build while refactoring docs, or in Grow when comparing your docs to a competitor’s developer experience.
- Three-tier page discovery: llms.txt, then sitemap.xml via curl, then homepage nav crawl
- Audits six fixed sections: Installation, Quick Start, Error Handling, Troubleshooting, Examples, Best Practices
- Cross-references gaps across all SDK pages so missing-on-one-page does not false-flag content elsewhere
- Scores each section 0–100 with rating tiers in a self-contained downloadable HTML report
- Mandatory structured workflow: crawl → analyse → cross-reference → report (do not ad-hoc audit)
Sdk Docs Auditor by the numbers
- 77 all-time installs (skills.sh)
- +4 installs in the week ending Jul 25, 2026 (Skillselion tracking)
- Ranked #696 of 1,901 Documentation skills by installs in the Skillselion catalog
- Data as of Jul 26, 2026 (Skillselion catalog sync)
npx skills add https://github.com/infrasity-labs/dev-gtm-claude-skills --skill sdk-docs-auditorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 77 |
|---|---|
| repo stars | ★ 93 |
| Last updated | June 28, 2026 |
| Repository | infrasity-labs/dev-gtm-claude-skills ↗ |
What it does
Crawl an SDK documentation site, score six standard sections, cross-reference gaps, and download a styled HTML audit report before launch or GTM.
Who is it for?
API/SDK makers, dev-tool GTM leads, or agents asked to audit a competitor or own docs URL with a repeatable crawl-and-score method.
Skip if: Writing net-new tutorial content from scratch, or legal/compliance reviews unrelated to developer documentation structure.
When should I use this skill?
User provides a documentation URL and asks to audit, review, analyse, score, check quality, completeness, gaps, or crawl SDK docs (always use structured workflow).
What you get
You receive section scores 0–100, gap notes validated site-wide, and a downloadable HTML report suitable for prioritizing doc fixes before developers hit integration friction.
- Self-contained downloadable HTML audit report
- Per-section 0–100 scores and tier ratings
- Cross-referenced gap list across all crawled SDK pages
By the numbers
- 6 fixed audit sections
- Per-section scores from 0–100 with rating tiers
- 3-tier discovery: llms.txt, sitemap.xml, homepage nav
Files
SDK Docs Auditor
Produces a comprehensive, cross-referenced audit of any SDK documentation site with a fully styled downloadable HTML report.
What this skill does
1. Discovers all SDK pages via llms.txt first, falling back to sitemap.xml (using curl), then homepage nav crawl 2. Fetches and reads every relevant SDK page 3. Audits six fixed sections: Installation, Quick Start, Error Handling, Troubleshooting, Examples, Best Practices 4. Cross-references every gap across ALL other SDK pages — never flag something as missing if it exists elsewhere 5. Scores each section 0–100 and assigns a rating tier 6. Generates a beautiful, self-contained, downloadable HTML report
---
Step 1 — Discover all SDK pages
Use a three-tier discovery strategy, trying each method in order until one succeeds.
1a. Try llms.txt first (preferred)
Run a bash curl command to fetch llms.txt:
curl -s <docs_url>/llms.txtIf found:
- Extract every URL from lines matching the pattern
- [Page Title](URL): description - Filter to SDK-relevant pages only — keep URLs whose path contains any of:
sdk, installation, quickstart, quick-start, error, troubleshoot, example, best-practice, getting-started, reference, api-reference, overview, client, service, memory, search, worker, job, policy, agent, team
- Exclude: marketing pages, changelog, blog, legal, community/forum pages
- Store as
SDK_PAGES[]— list of{title, url, description} - Note in the report: "Discovery method: llms.txt"
1b. Fallback — Try sitemap.xml
If llms.txt is unavailable or returns no useful URLs, run a bash curl command to fetch the sitemap:
curl -s <docs_url>/sitemap.xml | python3 -c "import sys, re; print('\n'.join(re.findall(r'<loc>(.*?)</loc>', sys.stdin.read())))"If the sitemap returns URLs:
- Parse every
<loc>entry to get the full URL list - Filter using the same keyword list above
- Store as
SDK_PAGES[]— list of{title, url} - Note in the report: "Discovery method: sitemap.xml — N total URLs found, M SDK-relevant kept"
Also check for a sitemap index (multiple sitemaps) by looking for <sitemapindex> in the response. If found, curl each child sitemap and aggregate all URLs before filtering.
1c. Final fallback — Homepage nav crawl
If both llms.txt and sitemap.xml fail, fetch the docs homepage (<docs_url>) and extract all links from the nav sidebar or sitemap structure, filtering using the same keyword list.
- Note in the report: "Discovery method: homepage nav crawl (llms.txt and sitemap.xml unavailable)"
1d. Identify section mapping
From the discovered pages, identify which pages map to the six audit targets:
| Audit section | Look for paths/titles containing |
|---|---|
| Installation | install, setup, getting-started |
| Quick Start | quick-start, quickstart, tutorial |
| Error Handling | error, exception, errors |
| Troubleshooting | troubleshoot, faq, debug |
| Examples | example, sample, cookbook, tutorial |
| Best Practices | best-practice, guide, pattern |
If a dedicated page is not found for a section, note it — the absence itself is a finding.
Build the corpus
All discovered pages form the page corpus used for both section auditing and cross-referencing.
Report discovery summary:
Found N pages via [sitemap.xml / llms.txt / nav crawl]. Kept M SDK-relevant pages. Fetching now...
---
Step 2 — Fetch every page in the corpus
Fetch each page using bash_tool with curl, stripping HTML tags via Python to extract plain text. Do NOT use web_fetch for corpus pages — curl is more reliable and avoids permission errors.
Use this pattern for each page URL:
curl -s "https://docs.example.com/sdk/some-page" -L | python3 -c "
import sys, re, html
content = sys.stdin.read()
content = re.sub(r'<script[^>]*>.*?</script>', '', content, flags=re.DOTALL)
content = re.sub(r'<style[^>]*>.*?</style>', '', content, flags=re.DOTALL)
text = re.sub(r'<[^>]+>', ' ', content)
text = html.unescape(text)
text = re.sub(r'\s+', ' ', text).strip()
print(text[:6000])
"
Batch multiple pages in a single bash call using a loop to minimise round-trips:
for page in installation quick-start error-handling troubleshooting examples best-practices overview api-reference; do echo "=== PAGE: $page ===" curl -s "https://docs.example.com/sdk/$page" -L | python3 -c " import sys, re, html content = sys.stdin.read() content = re.sub(r'<script[^>]>.?</script>', '', content, flags=re.DOTALL) content = re.sub(r'<style[^>]>.?</style>', '', content, flags=re.DOTALL) text = re.sub(r'<[^>]+>', ' ', content) text = html.unescape(text) text = re.sub(r'\s+', ' ', text).strip() print(text[:6000]) " echo "" done
For large sites (>30 pages), prioritise in this order:
1. The six target section pages (installation, quick-start, error-handling, troubleshooting, examples, best-practices)
2. Overview / client overview pages
3. API reference
4. Service-specific pages (agents, jobs, workers, policies, memory, search, etc.)
---
## Step 3 — Audit the six target sections
For each of the six sections below, read its page carefully and evaluate against the criteria. Then check every other fetched page to see if any gap is actually addressed elsewhere.
### 3a. Installation
**Must have (deduct heavily if missing):**
- Prerequisites with exact versions (Python/Node/language version, package manager)
- At least one complete install command (pip/npm/etc.)
- Authentication setup with exact steps to obtain and configure credentials
- A working verification snippet that proves the install succeeded
- At least one expected output or confirmation of success
**Should have (deduct moderately):**
- Multiple install methods (package manager, source, Docker)
- IDE/editor setup
- Environment variable configuration with `.env` examples
- Version compatibility table
- Optional dependency explanations (what each extra includes)
- Constructor parameter reference (all params, types, defaults)
**Common gaps to check across other pages:**
- Env-var auto-detection (does the client read env vars when called with no args?) — check api-reference, overview
- Advanced constructor params (timeout, retries, org) — check api-reference
- Version pinning guidance — check any getting-started pages
### 3b. Quick Start
**Must have:**
- A minimal, numbered step-by-step path a first-time user can follow in under 5 minutes
- Complete working code from zero to first successful call
- Expected output for every code snippet
- Explanation of any prerequisite service/resource (queues, workers, etc.)
**Should have:**
- Both env-var and inline credential patterns
- Multiple install options (pip + poetry, etc.)
- Links to deeper docs for each concept introduced
**Common gaps to check:**
- Worker/queue prerequisites — check workers/environments pages
- Valid model/runtime IDs — check models/runtimes service pages
- Execute call signature accuracy — check api-reference and agents service pages
### 3c. Error Handling
**Must have:**
- Complete exception hierarchy (all exception classes, inheritance tree)
- Every exception class documented with: description, import path, code example
- At minimum: authentication, connection, timeout, rate-limit, API/HTTP errors
- HTTP status code branching (400/401/403/404/500 at minimum)
- At least one resilience pattern (retry with backoff)
**Should have:**
- Resource-specific exceptions (one per major service)
- Streaming error handling
- Context manager cleanup pattern
- Testing/pytest examples for error scenarios
- Do/don't anti-pattern examples
**Cross-reference check — critical:**
- List every exception class that appears in the api-reference or service pages
- Flag any that are missing from this page
- Check execute/method call signatures match across all pages — flag any discrepancies
### 3d. Troubleshooting
**Must have:**
- Authentication failures with diagnostic steps
- Connection / timeout issues with solutions
- At least one service-specific troubleshooting section
**Should have:**
- Troubleshooting for every major service (workers, jobs, policies, agents, etc.)
- Self-hosted / custom base_url issues
- Import errors with correct import paths
- A summary error-message lookup table
- Rate limiting with backoff references
- Actionable "getting help" checklist with actual links/commands
**Cross-reference check:**
- For every service page that has its own error handling section, check if its scenarios appear here
- Check if dataset/resource creation methods are linked when "not found" errors are described
- Check if health check API is referenced in the "verify status" step
### 3e. Examples
**Must have:**
- At least one complete, runnable end-to-end example per major service
- Expected output shown for every example
- Error handling within workflow examples
**Should have:**
- Coverage across: agent lifecycle, jobs/scheduling, memory/recall, semantic search, policy management, worker management, streaming
- No "workflow" examples that only survey resources without executing anything
- Multi-turn / session management examples where the SDK supports sessions
**Cross-reference check — this is where most gaps accumulate:**
- For every service that has its own dedicated page with examples, check if those examples are represented here
- Flag each missing service category with the source page that has the examples
### 3f. Best Practices
**Must have:**
- Authentication / credential security section
- Error handling / retry logic
- Performance (batching, pagination, caching)
**Should have:**
- One best-practices section per major service (workers, jobs, policies, agents)
- Resource cleanup / context managers
- Code organisation (type hints, reusable functions)
- Testing patterns
- Concurrency / thread-safety guidance
- Secret rotation / credential lifecycle
**Cross-reference check:**
- For every service page that defines its own best practices, check if those patterns appear here
- Note if concurrency/thread-safety is addressed anywhere in the entire corpus
---
## Step 4 — Score each section
Score 0–100 using this rubric:
| Score | Meaning |
|-------|---------|
| 85–100 | Strong — all must-haves present, most should-haves, few gaps |
| 70–84 | Good — all must-haves, some should-haves missing |
| 50–69 | Adequate — most must-haves but notable gaps |
| 30–49 | Weak — multiple must-haves missing |
| 0–29 | Poor — section is superficial or largely absent |
Assign a rating label: **Strong / Good / Adequate / Weak / Poor**
---
## Step 5 — Build the gap list for each section
For every gap:
- Write a clear one-sentence description of what is missing
- Tag it with where the content EXISTS in the corpus (if it does): `[covered in: page-name]`
- Or tag it: `[not covered anywhere in corpus]`
This distinction is critical — a gap that exists nowhere needs a new page; a gap that exists elsewhere just needs a cross-reference or consolidation.
---
## Step 6 — Derive top priority recommendations
Rank the top 6–8 actions by impact × effort. Prioritise:
1. Factual errors / signature mismatches (highest priority — causes user failures)
2. Must-have gaps not covered anywhere
3. Must-have gaps covered elsewhere but not linked
4. High-value should-have gaps
5. Cross-referencing and consolidation opportunities
---
## Step 7 — Generate the HTML report
Read the template at `assets/report-template.html` and inject all audit data into it.
Fill these placeholders:
- `__AUDIT_URL__` → the audited URL
- `__PAGES_FETCHED__` → total pages fetched
- `__AUDIT_DATE__` → today's date
- `__SECTION_DATA_JSON__` → JSON blob (see schema below)
- `__PRIORITIES_JSON__` → JSON blob (see schema below)
- `__OVERALL_SCORE__` → integer 0-100 (weighted average of six scores)
### Data schema
{ "sections": [ { "num": "01", "name": "Installation Guide", "rating": "Good", "score": 74, "strengths": ["string", ...], "gaps": [ { "text": "Description of the gap", "covered_in": ["page-name-1", "page-name-2"], "nowhere": false } ] } ] }
Set `"nowhere": true` and `"covered_in": []` when the gap exists nowhere in the corpus.
{ "priorities": [ { "rank": 1, "text": "Fix X in Y page. The correct form per api-reference is Z.", "type": "error" } ] }
Priority types: `"error"` (factual mistake), `"missing"` (not covered anywhere), `"xref"` (covered elsewhere, needs linking), `"improvement"` (should-have gap).
---
## Output
Save the final report to `/mnt/user-data/outputs/sdk-audit-report.html` and present it with `present_files`.
Tell the user:
- How many pages were fetched
- The overall score and what it means
- The single most critical finding
Do NOT reproduce the full report text in the chat — just present the file and give the brief summary.<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SDK Docs Audit — __AUDIT_URL__</title>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg: #06060a;
--surface: #0e0e16;
--surface2: #14141f;
--border: #232336;
--border2: #2e2e4a;
--text: #e2e2f0;
--text2: #8888aa;
--text3: #4a4a6a;
--pass: #34d399; --pass-bg: #052015; --pass-border: #064824;
--warn: #fbbf24; --warn-bg: #201500; --warn-border: #4a3300;
--fail: #f87171; --fail-bg: #200808; --fail-border: #4a1212;
--accent: #a78bfa; --accent2: #c4b5fd; --accent-bg: #12093a;
--tag-bg: #141424;
}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{background:var(--bg);color:var(--text);font-family:'Space Grotesk',sans-serif;font-size:14px;line-height:1.6}
/* Header */
.header{border-bottom:1px solid var(--border);padding:48px 64px 40px;position:relative;overflow:hidden;background:linear-gradient(135deg,#0a061a 0%,var(--bg) 60%)}
.header::after{content:'';position:absolute;top:-100px;right:-100px;width:500px;height:500px;background:radial-gradient(circle,rgba(167,139,250,0.08) 0%,transparent 60%);pointer-events:none}
.eyebrow{font-family:'Space Mono',monospace;font-size:10px;color:var(--accent);letter-spacing:0.2em;text-transform:uppercase;margin-bottom:10px;display:flex;align-items:center;gap:8px}
.eyebrow::before{content:'';display:block;width:20px;height:1px;background:var(--accent)}
h1{font-size:36px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px}
.header-url{font-family:'Space Mono',monospace;font-size:12px;color:var(--text3);background:var(--surface2);border:1px solid var(--border);display:inline-flex;align-items:center;gap:6px;padding:5px 12px;border-radius:4px;margin-bottom:36px}
.header-url::before{content:'→';color:var(--accent);font-size:10px}
.stats-row{display:flex;border:1px solid var(--border);border-radius:8px;overflow:hidden;width:fit-content}
.stat-cell{padding:16px 28px;border-right:1px solid var(--border);display:flex;flex-direction:column;gap:4px;background:var(--surface)}
.stat-cell:last-child{border-right:none}
.sv{font-family:'Space Mono',monospace;font-size:26px;font-weight:700;line-height:1}
.sv-total{color:var(--text)}.sv-strong{color:var(--pass)}.sv-good{color:var(--pass);opacity:.8}.sv-ok{color:var(--warn)}.sv-weak{color:var(--fail)}.sv-score{color:var(--accent2)}
.slabel{font-size:10px;color:var(--text3);text-transform:uppercase;letter-spacing:0.1em}
/* Legend */
.legend-bar{padding:12px 64px;border-bottom:1px solid var(--border);background:var(--surface);display:flex;align-items:center;gap:24px;flex-wrap:wrap}
.ll{font-family:'Space Mono',monospace;font-size:9px;color:var(--text3);text-transform:uppercase;letter-spacing:0.18em}
.badge{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:3px;font-family:'Space Mono',monospace;font-size:10px;font-weight:700;letter-spacing:0.06em}
.badge.pass{background:var(--pass-bg);color:var(--pass);border:1px solid var(--pass-border)}
.badge.warn{background:var(--warn-bg);color:var(--warn);border:1px solid var(--warn-border)}
.badge.fail{background:var(--fail-bg);color:var(--fail);border:1px solid var(--fail-border)}
.badge.strong{background:var(--pass-bg);color:var(--pass);border:1px solid var(--pass-border)}
.badge.good{background:var(--pass-bg);color:var(--pass);border:1px solid var(--pass-border);opacity:.85}
.badge.ok{background:var(--warn-bg);color:var(--warn);border:1px solid var(--warn-border)}
.badge.weak{background:var(--fail-bg);color:var(--fail);border:1px solid var(--fail-border)}
.badge.poor{background:var(--fail-bg);color:var(--fail);border:1px solid var(--fail-border)}
.li{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--text2)}
/* Layout */
.main{display:grid;grid-template-columns:256px 1fr;min-height:calc(100vh - 200px)}
.sidebar{border-right:1px solid var(--border);padding:20px 0;position:sticky;top:0;height:100vh;overflow-y:auto;background:var(--surface)}
.sidebar::-webkit-scrollbar{width:3px}.sidebar::-webkit-scrollbar-thumb{background:var(--border2)}
.sbh{font-family:'Space Mono',monospace;font-size:9px;color:var(--text3);text-transform:uppercase;letter-spacing:0.18em;padding:8px 20px 4px}
.sbl{display:flex;align-items:center;justify-content:space-between;padding:7px 20px;font-size:12px;color:var(--text2);cursor:pointer;border-left:2px solid transparent;transition:all 0.12s}
.sbl:hover{background:var(--surface2);color:var(--text);border-left-color:var(--border2)}
.sbp{display:flex;gap:3px}
.spp{font-family:'Space Mono',monospace;font-size:9px;padding:1px 5px;border-radius:2px}
.spp-p{background:var(--pass-bg);color:var(--pass)}
.spp-w{background:var(--warn-bg);color:var(--warn)}
.spp-f{background:var(--fail-bg);color:var(--fail)}
.content{padding:40px 56px;max-width:1080px}
/* Scorecard */
.st{font-family:'Space Mono',monospace;font-size:9px;color:var(--accent);text-transform:uppercase;letter-spacing:0.2em;margin-bottom:14px}
.sc-table{width:100%;border-collapse:collapse;border:1px solid var(--border);border-radius:6px;overflow:hidden;margin-bottom:48px}
.sc-table thead tr{background:var(--surface2);border-bottom:1px solid var(--border2)}
.sc-table th{padding:10px 14px;text-align:left;font-family:'Space Mono',monospace;font-size:9px;color:var(--text3);text-transform:uppercase;letter-spacing:0.12em;font-weight:400}
.sc-table th:not(:first-child){text-align:center}
.sc-table td{padding:11px 14px;border-bottom:1px solid var(--border);font-size:13px}
.sc-table td:not(:first-child){text-align:center;font-family:'Space Mono',monospace;font-size:12px;font-weight:700}
.sc-table tbody tr:last-child td{border-bottom:none;background:var(--surface2);font-weight:600}
.sc-table tbody tr:hover:not(:last-child){background:rgba(255,255,255,0.015)}
.np{color:var(--pass)}.nw{color:var(--warn)}.nf{color:var(--fail)}.nt{color:var(--text)}.na{color:var(--accent2)}
.clink{color:var(--text);cursor:pointer}.clink:hover{color:var(--accent2)}
/* Pattern / accent box */
.pbox{background:var(--accent-bg);border:1px solid #2e1f6e;border-radius:6px;padding:16px 20px;margin-bottom:20px;font-size:13px;color:var(--text2);line-height:1.7}
.pbox strong{color:var(--accent2)}
code{font-family:'Space Mono',monospace;font-size:10px;background:var(--tag-bg);padding:1px 5px;border-radius:3px;color:var(--accent2)}
hr.divider{border:none;border-top:1px solid var(--border);margin:40px 0}
/* Top issues */
.ibox{border:1px solid var(--border);border-radius:6px;overflow:hidden;margin-bottom:48px}
.irow{display:flex;gap:16px;padding:14px 18px;border-bottom:1px solid var(--border);align-items:flex-start}
.irow:last-child{border-bottom:none}
.in{font-family:'Space Mono',monospace;font-size:10px;color:var(--text3);flex-shrink:0;padding-top:2px;width:20px}
.it{font-size:13px;color:var(--text2);line-height:1.6}
.it strong{color:var(--text)}
/* Section */
.as{margin-bottom:56px;scroll-margin-top:20px}
.sh{display:flex;align-items:center;gap:10px;padding-bottom:14px;border-bottom:1px solid var(--border);margin-bottom:16px;flex-wrap:wrap}
.sn{font-size:20px;font-weight:700;letter-spacing:-0.01em}
.sc{font-size:11px;color:var(--text3);background:var(--surface2);border:1px solid var(--border);padding:3px 10px;border-radius:20px}
/* Section cards (gaps & strengths) */
.ec{border:1px solid var(--border);border-radius:6px;margin-bottom:10px;overflow:hidden}
.ec.cf{border-left:3px solid var(--fail)}
.ec.cw:not(.cf){border-left:3px solid var(--warn)}
.ec.cp{border-left:3px solid var(--pass)}
.eh{display:flex;align-items:center;gap:10px;padding:12px 18px;background:var(--surface);cursor:pointer;user-select:none;transition:background 0.1s}
.eh:hover{background:var(--surface2)}
.ep{font-family:'Space Mono',monospace;font-size:11px;color:var(--text3);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.en{font-size:13px;font-weight:500;flex-shrink:0;color:var(--text)}
.eds{display:flex;gap:3px;flex-shrink:0}
.dot{width:7px;height:7px;border-radius:50%}
.dot.pass{background:var(--pass)}.dot.warn{background:var(--warn)}.dot.fail{background:var(--fail)}
.chv{color:var(--text3);font-size:9px;transition:transform 0.18s;flex-shrink:0}
.ec.open .chv{transform:rotate(180deg)}
.eb{display:none;border-top:1px solid var(--border)}
.ec.open .eb{display:block}
.ct{width:100%;border-collapse:collapse}
.ct tr{border-bottom:1px solid var(--border)}
.ct tr:last-child{border-bottom:none}
.ct td{padding:11px 18px;vertical-align:top}
.cl{width:120px;font-family:'Space Mono',monospace;font-size:9px;color:var(--text3);text-transform:uppercase;letter-spacing:0.1em;padding-top:13px;white-space:nowrap}
.cs{width:72px;padding-top:10px}
.cn{color:var(--text2);font-size:13px;line-height:1.6}
.cx{font-size:11px;color:var(--text3);margin-top:3px;font-style:italic}
.cx strong{color:var(--warn);font-style:normal}
.rp{background:rgba(52,211,153,0.02)}
.rw{background:rgba(251,191,36,0.02)}
.rf{background:rgba(248,113,113,0.03)}
/* Score bar */
.score-bar-wrap{display:flex;align-items:center;gap:10px;min-width:130px}
.score-bar-bg{flex:1;height:3px;background:var(--surface2);border-radius:2px;overflow:hidden}
.score-bar-fill{height:100%;border-radius:2px}
/* Priority type tag */
.ptag{font-family:'Space Mono',monospace;font-size:9px;text-transform:uppercase;letter-spacing:0.06em;padding:1px 6px;border-radius:2px;flex-shrink:0}
.ptag-error{background:var(--fail-bg);color:var(--fail);border:1px solid var(--fail-border)}
.ptag-missing{background:var(--warn-bg);color:var(--warn);border:1px solid var(--warn-border)}
.ptag-xref{background:var(--accent-bg);color:var(--accent2);border:1px solid #2e1f6e}
.ptag-improvement{background:var(--surface2);color:var(--text2);border:1px solid var(--border2)}
/* Responsive */
@media(max-width:900px){.main{grid-template-columns:1fr}.sidebar{display:none}.header,.legend-bar,.content{padding-left:20px;padding-right:20px}}
</style>
</head>
<body>
<!-- HEADER -->
<div class="header">
<div class="eyebrow">SDK Documentation Audit</div>
<h1>SDK Docs Quality Report</h1>
<div class="header-url">__AUDIT_URL__</div>
<div class="stats-row" id="stats-row">
<!-- injected by JS -->
</div>
</div>
<!-- LEGEND -->
<div class="legend-bar">
<span class="ll">Ratings</span>
<div class="li"><span class="badge strong">Strong</span> All must-haves, most should-haves</div>
<div class="li"><span class="badge good">Good</span> All must-haves, some gaps</div>
<div class="li"><span class="badge ok">Adequate</span> Most must-haves, notable gaps</div>
<div class="li"><span class="badge weak">Weak</span> Multiple must-haves missing</div>
<div class="li"><span class="badge fail">Poor</span> Largely absent</div>
<span class="ll" style="margin-left:16px">Gap tags</span>
<div class="li"><span class="ptag ptag-error">Error</span> Factual mistake</div>
<div class="li"><span class="ptag ptag-missing">Missing</span> Not covered anywhere</div>
<div class="li"><span class="ptag ptag-xref">X-Ref</span> Covered elsewhere, needs link</div>
<div class="li"><span class="ptag ptag-improvement">Improve</span> Should-have gap</div>
</div>
<div class="main">
<!-- SIDEBAR -->
<nav class="sidebar">
<div class="sbh">Overview</div>
<div class="sbl" onclick="scrollTo('scorecard')">Summary Scorecard</div>
<div class="sbl" onclick="scrollTo('top-issues')">Top Priorities</div>
<div class="sbh" style="margin-top:8px">Sections</div>
<div id="sidebar-sections"><!-- injected by JS --></div>
</nav>
<!-- CONTENT -->
<div class="content">
<!-- SCORECARD -->
<div id="scorecard" style="margin-bottom:48px">
<div class="st">Summary Scorecard</div>
<table class="sc-table">
<thead>
<tr>
<th>Section</th>
<th>Rating</th>
<th>Score</th>
<th>Strengths</th>
<th>Gaps</th>
</tr>
</thead>
<tbody id="scorecard-body"><!-- injected by JS --></tbody>
</table>
</div>
<!-- TOP PRIORITIES -->
<div id="top-issues" style="margin-bottom:48px">
<div class="st">Top Priority Recommendations</div>
<div class="ibox" id="priorities-box"><!-- injected by JS --></div>
</div>
<hr class="divider">
<!-- SECTIONS -->
<div id="sections-container"><!-- injected by JS --></div>
<!-- FOOTER -->
<div style="font-size:11px;color:var(--text3);padding-bottom:40px;border-top:1px solid var(--border);padding-top:16px">
Audited <code>__AUDIT_URL__</code> on <code>__AUDIT_DATE__</code> · <code>__PAGES_FETCHED__ pages fetched</code> · Overall score: <code>__OVERALL_SCORE__/100</code>
</div>
</div>
</div>
<script>
const SECTION_DATA = __SECTION_DATA_JSON__;
const PRIORITIES = __PRIORITIES_JSON__;
const OVERALL_SCORE = __OVERALL_SCORE__;
function scrollTo(id){document.getElementById(id)?.scrollIntoView({behavior:'smooth',block:'start'})}
function toggle(h){h.closest('.ec').classList.toggle('open')}
function ratingBadge(r){
const m={Strong:'strong',Good:'good',Adequate:'ok',Weak:'weak',Poor:'poor'};
const labels={Strong:'Strong',Good:'Good',Adequate:'Adequate',Weak:'Weak',Poor:'Poor'};
const cls=m[r]||'ok';
return `<span class="badge ${cls}">${labels[r]||r}</span>`;
}
function scoreColor(s){
if(s>=85)return'var(--pass)';
if(s>=70)return'var(--pass)';
if(s>=50)return'var(--warn)';
if(s>=30)return'var(--fail)';
return'var(--fail)';
}
function sectionSlug(name){return name.toLowerCase().replace(/\s+/g,'-').replace(/[^a-z0-9-]/g,'')}
function priorityTag(type){
const map={
error:'<span class="ptag ptag-error">Error</span>',
missing:'<span class="ptag ptag-missing">Missing</span>',
xref:'<span class="ptag ptag-xref">X-Ref</span>',
improvement:'<span class="ptag ptag-improvement">Improve</span>'
};
return map[type]||'';
}
function srcTagsHtml(gap){
if(gap.nowhere) return `<span style="font-family:'Space Mono',monospace;font-size:9px;background:var(--fail-bg);color:var(--fail);border:1px solid var(--fail-border);padding:1px 6px;border-radius:2px">not covered anywhere</span>`;
if(!gap.covered_in||!gap.covered_in.length) return '';
return gap.covered_in.map(p=>`<span style="font-family:'Space Mono',monospace;font-size:9px;background:var(--accent-bg);color:var(--accent2);border:1px solid #2e1f6e;padding:1px 6px;border-radius:2px">${esc(p)}</span>`).join(' ');
}
function esc(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"')}
const sections = SECTION_DATA.sections||[];
// Stats row
const totalGaps = sections.reduce((a,s)=>a+(s.gaps||[]).length,0);
const strong = sections.filter(s=>s.score>=85).length;
const good = sections.filter(s=>s.score>=70&&s.score<85).length;
const ok = sections.filter(s=>s.score>=50&&s.score<70).length;
const weak = sections.filter(s=>s.score<50).length;
document.getElementById('stats-row').innerHTML = `
<div class="stat-cell"><span class="sv sv-score">${OVERALL_SCORE}/100</span><span class="slabel">Overall Score</span></div>
<div class="stat-cell"><span class="sv sv-total">${sections.length}</span><span class="slabel">Sections Audited</span></div>
<div class="stat-cell"><span class="sv sv-strong">${strong+good}</span><span class="slabel">Strong / Good</span></div>
<div class="stat-cell"><span class="sv sv-ok">${ok}</span><span class="slabel">Adequate</span></div>
<div class="stat-cell"><span class="sv sv-weak">${weak}</span><span class="slabel">Weak / Poor</span></div>
<div class="stat-cell"><span class="sv sv-total">${totalGaps}</span><span class="slabel">Total Gaps</span></div>
<div class="stat-cell"><span class="sv" style="color:var(--text2);font-size:16px">__AUDIT_DATE__</span><span class="slabel">Audit Date</span></div>
`;
// Sidebar
const sidebarSections = document.getElementById('sidebar-sections');
sections.forEach(sec=>{
const slug = sectionSlug(sec.name);
const gapCount = (sec.gaps||[]).length;
const strengthCount = (sec.strengths||[]).length;
const div = document.createElement('div');
div.className='sbl';
div.onclick=()=>scrollTo(slug);
div.innerHTML=`${esc(sec.name)}<span class="sbp">
<span class="spp spp-p">${strengthCount}S</span>
<span class="spp spp-f">${gapCount}G</span>
</span>`;
sidebarSections.appendChild(div);
});
// Scorecard
const tbody = document.getElementById('scorecard-body');
sections.forEach(sec=>{
const slug = sectionSlug(sec.name);
const color = scoreColor(sec.score);
const tr = document.createElement('tr');
tr.innerHTML=`
<td><a class="clink" onclick="scrollTo('${slug}')">${esc(sec.name)}</a></td>
<td style="text-align:left!important">${ratingBadge(sec.rating)}</td>
<td style="text-align:left!important">
<div class="score-bar-wrap">
<div class="score-bar-bg"><div class="score-bar-fill" style="width:${sec.score}%;background:${color}"></div></div>
<span style="font-family:'Space Mono',monospace;font-size:11px;font-weight:700;color:${color};min-width:28px">${sec.score}</span>
</div>
</td>
<td class="np">${(sec.strengths||[]).length}</td>
<td class="nf">${(sec.gaps||[]).length}</td>
`;
tbody.appendChild(tr);
});
// totals row
const totalRow = document.createElement('tr');
totalRow.innerHTML=`
<td>Total / Average</td>
<td style="text-align:left!important">—</td>
<td style="text-align:left!important"><span style="font-family:'Space Mono',monospace;font-size:11px;font-weight:700;color:var(--accent2)">${OVERALL_SCORE}/100</span></td>
<td class="np">${sections.reduce((a,s)=>a+(s.strengths||[]).length,0)}</td>
<td class="nf">${totalGaps}</td>
`;
tbody.appendChild(totalRow);
// Priorities
const priBox = document.getElementById('priorities-box');
(PRIORITIES.priorities||[]).forEach(p=>{
const row = document.createElement('div');
row.className='irow';
row.innerHTML=`
<span class="in">0${p.rank}</span>
<span style="flex-shrink:0;padding-top:3px">${priorityTag(p.type)}</span>
<span class="it">${esc(p.text)}</span>
`;
priBox.appendChild(row);
});
// Sections
const container = document.getElementById('sections-container');
sections.forEach((sec,idx)=>{
const slug = sectionSlug(sec.name);
const gapCount = (sec.gaps||[]).length;
const strengthCount = (sec.strengths||[]).length;
// Build gap rows
const gapRows = (sec.gaps||[]).map(g=>{
const isError = g.nowhere;
const rowCls = isError ? 'rf' : 'rw';
const badgeCls = isError ? 'fail' : 'warn';
const badgeText = isError ? '✕ Gap' : '⚠ Gap';
const srcTags = srcTagsHtml(g);
return `<tr class="${rowCls}">
<td class="cl">${isError?'Missing':'Needs work'}</td>
<td class="cs"><span class="badge ${badgeCls}">${badgeText}</span></td>
<td>
<div class="cn">${esc(g.text)}</div>
${srcTags ? `<div class="cx" style="margin-top:6px;display:flex;gap:4px;flex-wrap:wrap;align-items:center"><strong style="font-style:normal;color:var(--text3)">Coverage:</strong> ${srcTags}</div>` : ''}
</td>
</tr>`;
}).join('');
// Build strength rows
const strengthRows = (sec.strengths||[]).map(s=>`<tr class="rp">
<td class="cl">Present</td>
<td class="cs"><span class="badge pass">✓ Pass</span></td>
<td><div class="cn">${esc(s)}</div></td>
</tr>`).join('');
// Card open state for first section
const isOpen = idx === 0 ? ' open' : '';
const cardBorderClass = sec.score >= 70 ? 'cp' : sec.score >= 50 ? 'cw' : 'cf';
const section = document.createElement('div');
section.className='as';
section.id=slug;
section.innerHTML=`
<div class="sh">
<div class="sn">${esc(sec.num)}. ${esc(sec.name)}</div>
<span class="sc">Score: ${sec.score}/100</span>
${ratingBadge(sec.rating)}
<span class="badge pass">${strengthCount} strength${strengthCount!==1?'s':''}</span>
<span class="badge ${sec.score>=70?'pass':sec.score>=50?'warn':'fail'}">${gapCount} gap${gapCount!==1?'s':''}</span>
</div>
<!-- Strengths card -->
${strengthCount>0?`
<div class="ec cp${isOpen}" id="sec-str-${idx}">
<div class="eh" onclick="toggle(this)">
<span class="en">✓ Strengths</span>
<span class="ep">${strengthCount} item${strengthCount!==1?'s':''} present and correct</span>
<span class="chv">▼</span>
</div>
<div class="eb">
<table class="ct">${strengthRows}</table>
</div>
</div>`:''}
<!-- Gaps card -->
${gapCount>0?`
<div class="ec ${cardBorderClass}${isOpen}" id="sec-gap-${idx}">
<div class="eh" onclick="toggle(this)">
<span class="en">${sec.score>=70?'⚠':'✕'} Gaps & Issues</span>
<span class="ep">${gapCount} item${gapCount!==1?'s':''} missing or needing work</span>
<div class="eds">
${(sec.gaps||[]).map(g=>`<span class="dot ${g.nowhere?'fail':'warn'}"></span>`).join('')}
</div>
<span class="chv">▼</span>
</div>
<div class="eb">
<table class="ct">${gapRows}</table>
</div>
</div>`:''}
${idx < sections.length-1?'<hr class="divider">':''}
`;
container.appendChild(section);
// Auto-open first card in each section
setTimeout(()=>{
const strCard = document.getElementById(`sec-str-${idx}`);
const gapCard = document.getElementById(`sec-gap-${idx}`);
if(idx===0){
if(strCard) strCard.classList.add('open');
if(gapCard) gapCard.classList.add('open');
}
},0);
});
</script>
</body>
</html>SDK Docs Auditor
Crawls any SDK documentation site, audits six core sections against a structured checklist, cross-references every gap across the full page corpus, and produces a scored downloadable HTML report.
---
What this skill does
Most SDK doc quality checks are manual and inconsistent. This skill automates a full audit by discovering every relevant SDK page via llms.txt or sitemap.xml, fetching each one, and evaluating six sections (Installation, Quick Start, Error Handling, Troubleshooting, Examples, and Best Practices) against detailed must-have and should-have criteria.
The key differentiator: every gap is cross-referenced across the entire page corpus before being flagged. If the content exists elsewhere in the docs but is not linked, the audit says so, distinguishing "missing content" from "content that exists but isn't where it should be."
Built for:
- Developer marketing and DevRel teams doing a structured quality review of their own SDK docs before a launch or release
- Technical writers running a gap analysis to prioritise what to write next
- Agencies and consultants producing a scored deliverable for a client SDK review
---
Installation
Claude Code (Recommended)
Clone the repo. The skill activates automatically when you open it in Claude Code:
git clone https://github.com/Infrasity-Labs/dev-gtm-claude-skills.git
cd dev-gtm-claude-skills
claudeThen trigger it with:
/dev-gtm sdk-audit https://docs.example.com/sdkOr just describe what you want. Claude activates the skill when you provide a docs URL and ask for an audit, review, or quality check.
Claude Web (Free / Pro)
1. Go to [Settings → Capabilities](https://claude.ai/settings/capabilities) and enable Code execution and file creation 2. Go to [Customize → Skills](https://claude.ai/customize/skills) 3. Click + → Create skill → Upload a skill 4. Zip this skill folder and upload it:
cd dev-gtm-claude-skills/skills/sdk-docs-auditor
zip -r ../sdk-docs-auditor.zip .Upload sdk-docs-auditor.zip and toggle it on.
Egress network required. This skill uses curl to fetch pages from the target docs site. In Claude Code, you will be prompted to allow network access when the skill runs. In Claude Web, code execution (enabled above) covers this automatically.
---
How to use
Audit the SDK docs at https://docs.example.com/sdkHow good are the docs at https://docs.example.com?Review the SDK documentation at docs.example.com/dev-gtm sdk-audit https://docs.example.comClaude picks up any message that includes a docs URL alongside words like: audit, review, check, analyse, quality, completeness, or gaps.
---
Inputs
| Field | Required | Notes |
|---|---|---|
| Documentation URL | ✅ | The root URL of the SDK docs site to audit |
No other inputs are required. Page discovery, fetching, and section mapping all happen automatically.
---
Output
A self-contained HTML report file saved to /mnt/user-data/outputs/sdk-audit-report.html and presented with a download link.
The report contains:
- Overall score (0–100): weighted average of the six section scores
- Per-section scores and rating tiers: Strong / Good / Adequate / Weak / Poor
- Strengths: what is done well in each section
- Gap list: every gap tagged as either
[covered in: page-name]or[not covered anywhere in corpus] - Top priority recommendations: 6–8 ranked actions by impact, distinguishing factual errors, missing content, and cross-referencing opportunities
After presenting the file, Claude gives a one-line summary: pages fetched, overall score, and the single most critical finding.
---
Things to know
Cross-referencing is the core value. The audit never flags a gap without first checking whether the content exists somewhere else in the docs. "Gap covered elsewhere but not linked" is treated differently from "gap that does not exist anywhere."
Discovery falls back gracefully. The skill tries llms.txt first, then sitemap.xml, then the homepage nav. It always reports which method was used.
Large sites are prioritised intelligently. For sites with more than 30 pages, the six target section pages are fetched first, followed by overview and API reference pages, then service-specific pages.
The report file does not appear in chat. The full report HTML is presented as a download, not reproduced as chat text. Claude gives a brief summary inline.
---
How it works
1. Discover all SDK pages: tries llms.txt first (extracts URLs from link lines), falls back to sitemap.xml (parses <loc> entries), then the homepage nav; filters to SDK-relevant paths only 2. Map sections: identifies which discovered pages correspond to the six audit targets (Installation, Quick Start, Error Handling, Troubleshooting, Examples, Best Practices) 3. Fetch every page: runs curl on each URL, strips HTML with Python to extract plain text; batches multiple pages in one bash call to minimise round-trips 4. Audit each section: evaluates must-haves (scored heavily) and should-haves (scored moderately) against the fetched content 5. Cross-reference gaps: for every gap identified, searches all other fetched pages to determine whether the content exists elsewhere in the corpus 6. Score sections: assigns 0–100 per section and a rating tier based on how many must-haves and should-haves are met 7. Rank recommendations: orders the top 6–8 actions by impact × effort: factual errors first, then unmet must-haves, then cross-referencing gaps, then should-haves 8. Generate the HTML report: injects all audit data into assets/report-template.html and saves the file
---
Config schema reference
The JSON Claude assembles and injects into the report template:
{
"audit_url": "https://docs.example.com/sdk",
"pages_fetched": 24,
"audit_date": "2026-05-29",
"overall_score": 71,
"sections": [
{
"num": "01",
"name": "Installation Guide",
"rating": "Good",
"score": 74,
"strengths": [
"Prerequisites with exact Python version listed",
"pip install command present with package name"
],
"gaps": [
{
"text": "No verification snippet to confirm the install succeeded",
"covered_in": [],
"nowhere": true
},
{
"text": "Constructor parameter reference (timeout, retries) missing from this page",
"covered_in": ["api-reference"],
"nowhere": false
}
]
}
],
"priorities": [
{
"rank": 1,
"text": "Fix execute() call signature on quick-start page: api-reference shows it takes a session_id parameter that is absent from the quick-start example",
"type": "error"
},
{
"rank": 2,
"text": "Add a verification snippet to the Installation page, not covered anywhere in the corpus",
"type": "missing"
}
]
}Priority types: "error" (factual mistake), "missing" (not covered anywhere), "xref" (covered elsewhere, needs linking), "improvement" (should-have gap).
---
File structure
sdk-docs-auditor/
├── SKILL.md # Skill instructions Claude follows
├── README.md # This file
├── assets/
│ └── report-template.html # HTML report shell with injected placeholders
└── references/
└── cross-reference-guide.md # Rules for cross-referencing gaps across the corpusCross-Reference Guide
When auditing any gap, always check if the content exists in another page before flagging it as "not covered anywhere". This reference describes the most common cross-reference patterns.
Pages to always fetch and cross-check
1. `/sdk/installation` or /getting-started 2. `/sdk/quick-start` or /quickstart 3. `/sdk/error-handling` or /errors 4. `/sdk/troubleshooting` 5. `/sdk/examples` 6. `/sdk/best-practices` 7. `/sdk/api-reference` or /sdk/reference — CRITICAL for exception class names 8. `/sdk/overview` or /sdk/control-plane-client-overview — CRITICAL for constructor params 9. Service pages: agents, teams, jobs, workers, policies, environments, models, runtimes, skills, secrets, integrations, memory, semantic-search, context-graph
Common cross-reference patterns by gap type
Constructor / client setup gaps
- Env-var auto-detection → check:
api-reference,overview,control-plane-client-overview - Advanced params (timeout, max_retries, org_name) → check:
api-reference - Custom base_url → check:
api-reference,overview
Exception class gaps
- List all exception classes → check:
api-referenceexceptions section - Service-specific exceptions (TeamError, JobError, etc.) → check: respective service page error handling section
- Import paths → check:
api-reference
Method signature discrepancies
- Execute call signature → check:
api-reference,control-plane-agents - Any method params → check:
api-referencefirst, then the service-specific page
Missing examples
- Memory examples → check:
context-graph-memory - Semantic search → check:
context-graph-semantic-search - Job scheduling → check:
control-plane-jobs - Policy management → check:
control-plane-policies - Worker management → check:
control-plane-workers - Agent lifecycle → check:
control-plane-agents
Missing best practices
- Worker best practices → check:
control-plane-workersbest practices section - Job best practices → check:
control-plane-jobsbest practices section - Policy best practices → check:
control-plane-policiesbest practices section - Agent best practices → check:
control-plane-agentsbest practices section
Missing troubleshooting
- Worker issues → check:
control-plane-workerserror handling section - Job errors → check:
control-plane-jobserror handling section - Policy errors → check:
control-plane-policieserror handling section - Dataset not found → check:
control-plane-client-overviewor dataset service page - Health check URL → check:
api-referencehealth service section,examples
Gap classification rules
| Condition | Tag |
|---|---|
| Content exists verbatim in another SDK page | covered_in: [page-name], nowhere: false |
| Content is partially covered in another page | covered_in: [page-name], nowhere: false with note |
| Content exists nowhere in the corpus | covered_in: [], nowhere: true |
| Factual error (wrong signature, wrong import) | Always nowhere: true regardless — the error needs fixing |
Priority type classification
| Type | When to use |
|---|---|
error | A factual mistake that will cause user code to fail (wrong method signature, wrong import path, wrong parameter name) |
missing | A must-have that exists nowhere in the corpus |
xref | Content exists on another page but needs to be linked/referenced from the audited page |
improvement | A should-have gap — valuable but not causing user failures |
Scoring deductions guide
Installation (start at 100)
- Missing env-var auto-detection: -8
- Missing optional extras explanation: -6
- Missing version pinning guidance: -5
- Missing advanced constructor params: -7
- No verification snippet with expected output: -10
- No prerequisites section: -15
- No install command: -20
Quick Start (start at 100)
- No minimal numbered step path: -15
- No expected output on any snippet: -10
- Worker/queue prerequisite unexplained: -12
- Model IDs not discoverable: -8
- Signature inconsistency with api-reference: -10
- No link to deeper docs: -5
Error Handling (start at 100)
- Missing exception class from api-reference: -8 each
- No exception hierarchy diagram: -10
- Missing HTTP 401/403: -6
- No streaming error guidance: -7
- Wrong method signature in an example: -12
- Missing ControlPlaneError base usage: -5
Troubleshooting (start at 100)
- No worker troubleshooting: -15
- No self-hosted/base_url guidance: -10
- Circular resolution for a common error: -8
- Missing service-specific section: -8 each
- No health check reference in "getting help": -5
- Rate limiting has no section: -8
Examples (start at 100)
- Missing entire service category: -10 each
- No expected output on examples: -8
- Workflow example doesn't actually execute: -8
- No error handling in workflows: -7
- Missing multi-turn/session example: -6
Best Practices (start at 100)
- Missing worker best practices: -8
- Missing job best practices: -7
- Missing policy best practices: -7
- Missing agent best practices: -7
- No concurrency/thread-safety: -10 (not covered anywhere penalty)
- No secret rotation guidance: -8
- Metrics example connects to nothing: -3
Related skills
How it compares
Structured multi-page SDK doc audit with cross-links—not a single-page Lighthouse run or informal chat opinion on docs quality.
FAQ
Who is sdk-docs-auditor for?
Developers and small dev-tool teams who need a scored, shareable SDK documentation audit from a URL without building a custom crawler.
When should I use sdk-docs-auditor?
In Ship before launch to harden docs, in Build when refactoring reference sections, or in Launch when benchmarking developer experience against another SDK site.
Is sdk-docs-auditor safe to install?
Check the Security Audits panel on this Prism page; the skill fetches external URLs and may use curl—scope network use and verify the generated HTML locally.