Scans the skills folder for new, unused, or missing dependencies; fixes requirements.txt; and tests a skill in or out of sandbox.
--- name: skill-doctor displayName: Skill Doctor | OpenClaw Skill description: Scans the skills folder for new, unused, or missing dependencies; fixes requirements.txt; and tests a skill in or out of sandbox. version: 1.0.0 --- # Skill Doctor | OpenClaw Skill Scans `workspace/skills` (or a given folder) to detect **missing** and **unused** Python dependencies, can **fix** `requirements.txt` (add missing, optionally remove unused), and can **test** a skill using the skill-tester in **sandbox** (default) or **no-sandbox** mode. ## Description - **Scan:** For each skill, finds all Python files under the skill and its `scripts/` folder, extracts top-level imports, and compares them to `requirements.txt`. Reports: - **Missing:** Imported but not listed in requirements (suggests adding). - **Unused:** Listed in requirements but not imported (suggests removing). - **Fix:** Adds missing packages to `requirements.txt` and/or removes unused ones (`--fix-unused`). - **Test:** Runs the [skill-tester](workspace/skills/skill-tester) for the given skill. Use `--no-sandbox` to run tests with full environment (e.g. network); default runs in sandbox. Stdlib modules and local modules (same skill’s `.py` files) are excluded from “missing”. ## Installation ```bash clawhub install skill-doctor ``` Or clone into your skills directory: ```bash git clone https://github.com/Org/skill-doctor.git workspace/skills/skill-doctor ``` ## Usage ```bash # Scan all skills (or default: scan) python3 workspace/skills/skill-doctor/scripts/skill_doctor.py # Scan one skill python3 workspace/skills/skill-doctor/scripts/skill_doctor.py --skill SUBAGENT-DASHBOARD --scan # Fix: add missing deps to requirements.txt python3 workspace/skills/skill-doctor/scripts/skill_doctor.py --skill MY-SKILL --fix # Fix: add missing and remove unused python3 workspace/skills/skill-doctor/scripts/skill_doctor.py --skill MY-SKILL --fix --fix-unused # Dry-run fix (report only) python3 workspace/skills/skill-doctor/scripts/skill_doctor.py --skill MY-SKILL --fix --fix-unused --dry-run # Test skill (sandbox) python3 workspace/skills/skill-doctor/scripts/skill_doctor.py --skill gateway-guard --test # Test skill (no sandbox: full env) python3 workspace/skills/skill-doctor/scripts/skill_doctor.py --skill gateway-guard --test --no-sandbox # JSON output python3 workspace/skills/skill-doctor/scripts/skill_doctor.py --scan --json python3 workspace/skills/skill-doctor/scripts/skill_doctor.py --skill X --test --json ``` ## Commands | Command / flags | Description | |-----------------|-------------| | `--scan` | Scan skills and report missing/unused dependencies (default if no `--fix`/`--test`) | | `--skill SLUG` | Limit to one skill | | `--fix` | Add missing packages to `requirements.txt` | | `--fix-unused` | With `--fix`, also remove unused packages | | `--dry-run` | With `--fix`: only report what would be done | | `--test` | Run skill-tester for the skill | | `--no-sandbox` | Run tests with full env (no sandbox) | | `--timeout N` | Test timeout in seconds (default 60) | | `--json` | Output JSON | | `--skills-dir PATH` | Override skills root (default: `workspace/skills`) | ## What this skill does 1. **Discover skills** — Finds dirs with `SKILL.md` or `_meta.json` under the skills folder. 2. **Parse Python** — Uses `ast` to collect top-level `import` / `import from` names from all `.py` files in the skill and `scripts/`. 3. **Exclude stdlib and local** — Ignores standard library modules and local modules (same skill’s file names). 4. **Map to pip** — Maps import names to pip package names (e.g. `bs4` → `beautifulsoup4`, `yaml` → `PyYAML`). 5. **Compare** — Compares required (from `requirements.txt`) vs needed (from imports); reports missing and unused. 6. **Fix** — Writes `requirements.txt`: append missing packages; optionally remove unused lines. 7. **Test** — Invokes `skill-tester/scripts/skill_tester.py --skill SLUG --json`. Sandbox vs no-sandbox is controlled by `--no-sandbox` and the `OPENCLAW_DOCTOR_NO_SANDBOX` env var for the test run. ## Requirements - Python 3.7+ - Optional: [skill-tester](workspace/skills/skill-tester) for `--test` (must be present under `workspace/skills/skill-tester`). ## Security & privacy - **Reads:** Skill directories and their `.py` and `requirements.txt` files. - **Writes:** Only `requirements.txt` when using `--fix` (and `--fix-unused`). - **Test:** Runs skill-tester in a subprocess; `--no-sandbox` means tests run with the current environment (e.g. network allowed).
don't have the plugin yet? install it then click "run inline in claude" again.
added explicit inputs (skill-tester path, env vars), expanded procedure to 8 numbered steps with clear inputs/outputs, documented 10 decision points (fix flags, sandbox mode, missing files, etc.), defined json and human output formats, and clarified error handling and exit codes.
skill-doctor audits python dependencies across your skill codebase by scanning imports against requirements.txt, detecting gaps and cruft. use it to keep dependency manifests accurate, optionally auto-fix them, and validate skills with skill-tester in sandbox or unrestricted modes. run this regularly or before deploying skills to catch stale packages and import errors early.
workspace/skills; override with --skills-dir PATH. must contain skill folders with SKILL.md or _meta.json markers.--skill SLUG to audit a single skill instead of all.--test mode. must exist at workspace/skills/skill-tester. expects scripts/skill_tester.py executable.OPENCLAW_DOCTOR_NO_SANDBOX: set to true to default all test runs to no-sandbox mode; cli --no-sandbox overrides.OPENCLAW_DOCTOR_TIMEOUT: default test timeout in seconds (default 60 if not set); cli --timeout N overrides.discover skills (inputs: skills directory path). scan the skills root for subdirectories containing SKILL.md or _meta.json. build list of skill slugs (directory names). output: list of skill paths.
parse imports per skill (inputs: skill path, scripts/ subdirectory). for each skill, walk all .py files in the skill root and scripts/ folder. use ast.parse() to extract top-level import X and from X import Y statements. collect unique module names (the leftmost part of dotted names, e.g. from foo.bar import baz captures foo). output: set of import names per skill.
exclude stdlib and local modules (inputs: import names, skill directory). filter out python standard library names (check against sys.stdlib_list or curated list). filter out local modules matching .py filenames in the same skill directory (e.g. if utils.py exists, exclude utils from the import set). output: external dependency names only.
map import names to pip packages (inputs: external dependency names). apply known mappings for packages where import name differs from pip name (e.g. bs4 → beautifulsoup4, yaml → PyYAML, cv2 → opencv-python, PIL → Pillow). for unmapped names, assume import name equals pip name. output: canonical pip package names.
read requirements.txt (inputs: skill path). load requirements.txt from skill root (if missing, treat as empty). parse lines, extract package names (ignoring versions, extras, comments, blank lines). output: set of required packages.
compare and report (inputs: required packages, canonical pip packages). compute missing (in imports but not in requirements.txt) and unused (in requirements.txt but not in imports). if --json flag set, emit json report; else emit human-readable table. output: report data structure with missing and unused lists.
fix requirements.txt (conditional) (inputs: report data, skill path, --fix flag). if --fix flag present, append missing packages to requirements.txt (one per line, no version pinning unless original file has pinned versions; if so, append with == and current pip version or >= if version unknown). if --fix-unused flag also present, remove unused package lines. if --dry-run flag set, do not write; only print what would be changed. output: updated requirements.txt or dry-run report.
test skill (conditional) (inputs: skill slug, --test flag, sandbox/no-sandbox mode, timeout). if --test flag present, invoke skill-tester subprocess: python3 workspace/skills/skill-tester/scripts/skill_tester.py --skill SLUG --json --timeout N. capture stdout/stderr and exit code. if --no-sandbox flag set or OPENCLAW_DOCTOR_NO_SANDBOX=true, pass --no-sandbox to skill-tester. if test subprocess times out (exceeds --timeout or env default), terminate and report timeout error. if --json flag set, pass through skill-tester's json output; else emit human-readable test result. output: test result (pass/fail, test logs, coverage if available).
--fix flag absent: skip step 7; only report missing/unused.--fix-unused flag absent: in step 7, add missing packages but do not remove unused lines.--dry-run flag set: in step 7, print the changes but do not modify requirements.txt.--test flag absent: skip step 8; do not invoke skill-tester.--no-sandbox flag set or OPENCLAW_DOCTOR_NO_SANDBOX=true: pass --no-sandbox to skill-tester in step 8; else run in default sandbox mode.--fix), create a new requirements.txt with missing packages.workspace/skills/skill-tester: in step 8, emit error and fail the test; do not proceed.--skill flag absent: in step 1, discover and process all skills; else process only the specified skill slug.cv2 could be opencv-python or opencv-python-headless): use the most common mapping; document in error if ambiguous.--json flag set: emit all output (report, test results) as json objects on separate lines (jsonl format); else emit human-readable tables and text.scan mode (default or --scan):
{skill: "slug", missing: ["pkg1", "pkg2"], unused: ["pkg3"]}fix mode (--fix, with or without --fix-unused):
{skill: "slug", action: "fix", added: ["pkg"], removed: [], dry_run: false}<skill>/requirements.txt with missing packages appended and/or unused packages removed.test mode (--test):
{skill: "slug", action: "test", result: "pass" or "fail", tests_passed: N, tests_failed: N, error: null or string, sandbox: true or false}error cases:
{error: "skill-tester not found at workspace/skills/skill-tester"}{error: "skill 'UNKNOWN' not found"}{error: "failed to parse requirements.txt: <details>"}{error: "test timeout after N seconds", skill: "slug"}{error: "permission denied reading skill directory", detail: <system error>}requirements.txt is updated (or dry-run confirms what would change). no write errors reported.result: "pass".