by duriantaco
Open‑source static analysis tool for Python, TypeScript, and Go that detects dead code, hard‑coded secrets, exploitable flows, and AI‑generated security regressions, offering CLI, GitHub Action, VS Code extension, and MCP server for AI agents.
Skylos provides a unified workflow for dead‑code detection, security scanning (SAST), and AI‑generated code defence across Python, TypeScript, and Go. It integrates with local development, CI/CD pipelines, and AI assistants via an MCP server.
pip install skylos # core
pip install skylos[llm] # optional LLM features
pip install skylos[fast] # optional Rust‑accelerated backend
skylos suite . – full scan (dead code, security, debt, AI defence).skylos . – basic dead‑code, security, and quality analysis.skylos . --diff origin/main – focus on changed lines.skylos cicd init
This generates a GitHub Actions workflow that gates PRs, adds annotations, and can upload results.python -m skylos_mcp.server) and configure AI assistants (e.g., Claude Desktop) to call Skylos tools directly.pyproject.toml.skylos[fast]) for up to 63× speed‑up.skylos agent remediate to scan, fix, test, and open PRs.Q: Does Skylos work with frameworks like Django or FastAPI? A: Yes. It recognises routes, decorators, and test fixtures, assigning lower confidence to framework‑driven entry points to minimise false positives.
Q: How does the --trace option improve results?
A: It runs your test suite (or any command) with tracing, marking every function that actually executes as used, which eliminates false dead‑code reports caused by dynamic dispatch.
Q: Can I run Skylos without internet access? A: Core static analysis works completely offline. LLM‑powered features require a cloud provider or a local OpenAI‑compatible endpoint (e.g., Ollama).
Q: What is the recommended confidence level for safe cleanup?
A: Start with the default --confidence 60. For framework‑heavy projects use --confidence 30 or 20 to see low‑confidence entries.
Q: How do I configure the MCP server for Claude Desktop?
A: Add the following to ~/.config/claude/claude_desktop_config.json (or the macOS equivalent):
{
"mcpServers": {
"skylos": {
"command": "python",
"args": ["-m", "skylos_mcp.server"]
}
}
}
Afterwards you can ask Claude to "scan my project for security issues" and it will invoke Skylos via the MCP endpoint.
Q: How are secrets detected? A: Skylos matches known provider patterns (AWS, Stripe, GitHub, etc.) and falls back to high‑entropy string detection across source and config files.
Q: Is there a way to whitelist false positives globally?
A: Yes. Use skylos whitelist <pattern> or add entries under [tool.skylos.whitelist] in pyproject.toml.
📖 Website · Documentation · Blog · GitHub Action · VS Code Extension · MCP Server
English | 中文
Skylos is an open-source static analysis tool and PR gate for Python, TypeScript, and Go. It helps teams detect dead code, hardcoded secrets, exploitable flows, and AI-generated security regressions before they land in main.
If you use Vulture for dead code, Bandit for security checks, or Semgrep/CodeQL for CI enforcement, Skylos combines those workflows with framework-aware dead code detection and diff-aware regression detection for AI-assisted refactors.
The core use case is straightforward: run it locally, add it to CI, and gate pull requests on real findings with GitHub annotations and review comments. Advanced features like AI defense, remediation agents, VS Code, MCP, and cloud upload are available, but you do not need any of them to get value from Skylos.
| Goal | Command | What you get |
|---|---|---|
| Run everything local | skylos suite . |
Static findings, technical debt hotspots, Python AI defense, and provenance summary in one report |
| Scan a repo | skylos . -a |
Dead code, risky flows, secrets, and code quality findings |
| Gate pull requests | skylos cicd init |
A GitHub Actions workflow with a quality gate and inline annotations |
| Audit an LLM app | skylos defend . |
Optional AI defense checks for Python and direct TypeScript LLM integrations |
skylos suite . for the full local overviewskylos . for the focused static scanskylos cicd init for CI setupskylos agent scan . for hybrid static + LLM reviewtsconfig project references during TypeScript package resolution, and it keeps workspace package entrypoints alive during dead-file and unnecessary-export analysis.skylos discover and skylos defend can now pick up direct TypeScript LLM integrations in Node / Next-style codepaths as a beta surface.| Skylos | Vulture | |
|---|---|---|
| Recall | 98.1% (51/52) | 84.6% (44/52) |
| False Positives | 220 | 644 |
| Framework-aware (FastAPI, Django, pytest) | Yes | No |
| Security scanning (secrets, SQLi, SSRF) | Yes | No |
| AI-powered analysis | Yes | No |
| CI/CD quality gates | Yes | No |
| TypeScript + Go support | Yes | No |
Benchmarked on 9 popular Python repos (350k+ combined stars) + TypeScript (consola). Every finding manually verified. Full case study →
# Generate a GitHub Actions workflow in 30 seconds
skylos cicd init
# Commit and push to activate
git add .github/workflows/skylos.yml && git push
What you get:
No configuration needed - works out of the box with sensible defaults. See CI/CD section for customization.
If you are evaluating Skylos, start with the core workflow below. The LLM and AI defense commands are optional.
| Objective | Command | Outcome |
|---|---|---|
| Everything local | skylos suite . |
One report for static findings, technical debt, Python AI defense, and provenance |
| First scan | skylos . |
Dead code findings with confidence scoring |
| Audit risk and quality | skylos . -a |
Dead code, risky flows, secrets, quality, and SCA findings |
| Higher-confidence dead code | skylos . --trace |
Cross-reference static findings with runtime activity |
| Review only changed lines | skylos . --diff origin/main |
Focus findings on active work instead of legacy debt |
| Local staged hook | skylos agent pre-commit . |
Fast staged check for security, secrets, and quality |
| Gate locally | skylos --gate |
Fail on findings before code leaves your machine |
| Set up CI/CD | skylos cicd init |
Generate a GitHub Actions workflow in 30 seconds |
| Gate in CI | skylos cicd gate --input results.json |
Fail builds when issues cross your threshold |
| Objective | Command | Outcome |
|---|---|---|
| Detect Unused Pytest Fixtures | skylos . --pytest-fixtures |
Find unused @pytest.fixture across tests + conftest |
| AI-Powered Analysis | skylos agent scan . --model gpt-4.1 |
Fast static + LLM file review with dead-code verification available on demand |
| Dead Code Verification | skylos agent verify . --model gpt-4.1 |
Dead-code-only second pass: static findings reviewed by the LLM |
| Security Audit | skylos agent scan . --security |
Deep LLM security review with interactive file selection |
| Auto-Remediate | skylos agent remediate . --auto-pr |
Scan, fix, test, and open a PR — end to end |
| Code Cleanup | skylos agent remediate . --standards |
LLM-guided code quality cleanup against coding standards |
| PR Review | skylos agent scan . --changed |
Analyze only git-changed files |
| PR Review (JSON) | skylos agent scan . --changed --format json -o results.json |
LLM review with code-level fix suggestions |
| Local LLM | skylos agent scan . --base-url http://localhost:11434/v1 --model codellama |
Use Ollama/LM Studio (no API key needed) |
| PR Review (CI) | skylos cicd review -i results.json |
Post inline comments on PRs |
| AI Defense: Discover | skylos discover . |
Map all LLM integrations in your codebase |
| AI Defense: Defend | skylos defend . |
Check LLM integrations for missing guardrails |
| AI Defense: CI Gate | skylos defend . --fail-on critical --min-score 70 |
Block PRs with critical AI defense gaps |
| Whitelist | skylos whitelist 'handle_*' |
Suppress known dynamic patterns |
Use skylos debt <path> to rank structural debt hotspots without collapsing everything into a single urgency number.
score is the project-level structural debt score.priority is the hotspot triage score used for ordering fix candidates.--changed limits the visible hotspot list to changed files, but keeps the structural debt score anchored to the whole project.# Full project debt scan
skylos debt .
# Review only changed hotspots without distorting the project score
skylos debt . --changed
# Compare the current project against a saved debt baseline
skylos debt . --baseline
# Save a repo-level debt baseline
skylos debt . --save-baseline
Debt policy files such as skylos-debt.yaml are discovered from the scan target upward, and explicit CLI flags like --top override policy defaults.
Backup (GitHub): https://github.com/duriantaco/skylos/discussions/82
The core product is dead code detection, security scanning, and PR gating. The AI-focused features below are optional layers on top of that baseline workflow.
eval(), unsafe pickle, and weak cryptography.Skylos can also flag common AI-generated code mistakes. Every finding includes vibe_category and ai_likelihood (high/medium/low) metadata so you can filter them separately if you want.
sanitize_input, validate_token, check_permission, etc.) that are never defined or imported, including local-module references like security.require_auth(). hallucinated_reference, high@require_auth, @rate_limit, @authenticate, etc.) that are never defined or imported, including local-module decorators like @guards.require_auth. hallucinated_reference, highpass, ..., or raise NotImplementedError — AI-generated stubs that silently do nothing in production. incomplete_generation, mediumos.getenv("ENABLE_X") referencing feature flags that are never defined anywhere in the project. ghost_config, mediummock.patch("app.email.send_email") where send_email no longer exists — AI renames functions but leaves tests pointing at the old name. stale_reference, medium# TODO: add auth placeholders that AI left behind and nobody finished.verify=False, @csrf_exempt, DEBUG=True, and ALLOWED_HOSTS=["*"].random.choice() used for security-sensitive values like tokens and OTPs.These checks run under --danger and look for prompt injection patterns or obfuscated instructions in repository content.
.env files for hidden instruction payloads — instruction overrides ("ignore previous instructions"), role hijacking ("you are now"), AI-targeted suppression ("do not flag", "skip security"), data exfiltration prompts, and AI-targeting phrases.password) that bypass visual review.Static analysis for AI application security that maps every LLM call in your codebase and checks for missing guardrails. Python support is mature; direct TypeScript / TSX discovery and shared guardrail checks are now available in beta.
# Discover all LLM integrations
skylos discover .
# Check defenses and get a scored report
skylos defend .
# CI gate: fail on critical gaps, require 70% defense score
skylos defend . --fail-on critical --min-score 70
# JSON output for dashboards and pipelines
skylos defend . --json -o defense-report.json
# Filter by OWASP LLM Top 10 category
skylos defend . --owasp LLM01,LLM04
13 checks across defense and ops:
| Check | Severity | OWASP | What it detects |
|---|---|---|---|
no-dangerous-sink |
Critical | LLM02 | LLM output flowing to eval/exec/subprocess |
untrusted-input-to-prompt |
Critical | LLM01 | Raw user input in prompt with no processing |
tool-scope |
Critical | LLM04 | Agent tools with dangerous system calls |
tool-schema-present |
Critical | LLM04 | Agent tools without typed schemas |
output-validation |
High | LLM02 | LLM output used without structured validation |
prompt-delimiter |
High | LLM01 | User input in prompts without delimiters |
rag-context-isolation |
High | LLM01 | RAG context injected without isolation |
output-pii-filter |
High | LLM06 | No PII filtering on user-facing LLM output |
model-pinned |
Medium | LLM03 | Model version not pinned (floating alias) |
input-length-limit |
Low | LLM01 | No input length check before LLM call |
logging-present |
Medium | Ops | No logging around LLM calls |
cost-controls |
Medium | Ops | No max_tokens set on LLM calls |
rate-limiting |
Medium | Ops | No rate limiting on LLM endpoints |
Defense and ops scores are tracked separately — adding logging won't inflate your security score.
Custom policy via skylos-defend.yaml:
rules:
model-pinned:
severity: critical # Upgrade severity
input-length-limit:
enabled: false # Disable check
gate:
min_score: 70
fail_on: high
Supports OpenAI, Anthropic, Google Gemini, Cohere, Mistral, Ollama, Together AI, Groq, Fireworks, Replicate, LiteLLM, LangChain, LlamaIndex, CrewAI, and AutoGen.
TypeScript beta scope today is intentionally narrow: direct SDK patterns in .ts / .tsx / .js / .jsx for providers such as OpenAI, Anthropic, Google Gemini, LiteLLM, and Vercel AI SDK. Wrapper-heavy code and full TS agent parity are follow-up work.
skylos agent remediate can scan, generate fixes, run tests, and optionally open a PR.skylos cicd init generates GitHub Actions workflows with sensible defaults.Corpus Guard workflow on PRs to catch dead-code precision regressions against curated framework and language fixtures.# pragma: no skylos, # pragma: no cover, or # noqa.skylos-trace files to verify dead code with runtime datapyproject.toml to block non-compliant PRsinquirer-based terminal interface@pytest.fixture definitions in test_*.py and conftest.py| Language | Parser | Dead Code | Security | Quality |
|---|---|---|---|---|
| Python | AST | ✅ | ✅ | ✅ |
| TypeScript/TSX | Tree-sitter | ✅ | ✅ | ✅ |
| Java | Tree-sitter | ✅ | ✅ | ✅ |
| Go | Standalone binary | ✅ | - | - |
Languages are auto-detected by file extension. Mixed-language repos work out of the box. No Node.js or JDK required — all parsers are built-in via Tree-sitter.
| Rule | ID | What It Catches |
|---|---|---|
| Dead Code | ||
| Functions | - | Unused functions, arrow functions, and overloads |
| Classes | - | Unused classes, interfaces, enums, and type aliases |
| Imports | - | Unused named, default, and namespace imports |
| Methods | - | Unused methods (lifecycle methods excluded) |
| Security | ||
| eval() | SKY-D201 | eval() usage |
| Dynamic exec | SKY-D202 | exec(), new Function(), setTimeout with string |
| XSS | SKY-D226 | innerHTML, outerHTML, document.write(), dangerouslySetInnerHTML |
| SQL injection | SKY-D211 | Template literal / f-string in SQL query |
| Command injection | SKY-D212 | child_process.exec(), os.system() |
| SSRF | SKY-D216 | fetch()/axios with variable URL |
| Open redirect | SKY-D230 | res.redirect() with variable argument |
| Weak hash | SKY-D207/D208 | MD5 / SHA1 usage |
| Prototype pollution | SKY-D510 | __proto__ access |
| Dynamic require | SKY-D245 | require() with variable argument |
| JWT bypass | SKY-D246 | jwt.decode() without verification |
| CORS wildcard | SKY-D247 | cors({ origin: '*' }) |
| Internal URL | SKY-D248 | Hardcoded localhost/127.0.0.1 URLs |
| Insecure random | SKY-D250 | Math.random() for security-sensitive ops |
| Sensitive logs | SKY-D251 | Passwords/tokens passed to console.log() |
| Insecure cookie | SKY-D252 | Missing httpOnly/secure flags |
| Timing attack | SKY-D253 | ===/== comparison of secrets |
| Storage tokens | SKY-D270 | Sensitive data in localStorage/sessionStorage |
| Error disclosure | SKY-D271 | error.stack/.sql sent in HTTP response |
| Secrets | SKY-S101 | Hardcoded API keys + high-entropy strings |
| Quality | ||
| Complexity | SKY-Q301 | Cyclomatic complexity exceeds threshold |
| Nesting depth | SKY-Q302 | Too many nested levels |
| Function length | SKY-C304 | Function exceeds line limit |
| Too many params | SKY-C303 | Function has too many parameters |
| Duplicate condition | SKY-Q305 | Identical condition in if-else-if chain |
| Await in loop | SKY-Q402 | await inside for/while loop |
| Unreachable code | SKY-UC002 | Code after return/throw/break/continue |
Framework-aware: Next.js convention exports (page.tsx, layout.tsx, route.ts, middleware.ts), config exports (getServerSideProps, generateMetadata, revalidate), React patterns (memo, forwardRef), and exported custom hooks (use*) are automatically excluded from dead code reports.
TypeScript dead code detection tracks: callbacks, type annotations, generics, decorators, inheritance (extends), object shorthand, spread, re-exports, and typeof references. Benchmarked at 95% recall with 0 false positives on alive code.
## from pypi
pip install skylos
## with LLM-powered features (agent verify, agent remediate, etc.)
pip install skylos[llm]
## with Rust-accelerated analysis (up to 63x faster)
pip install skylos[fast]
## both
pip install skylos[llm,fast]
## or from source
git clone https://github.com/duriantaco/skylos.git
cd skylos
pip install .
skylos[fast]installs an optional Rust backend that accelerates clone detection (63x), file discovery (5x), coupling analysis, and cycle detection. Same results, just faster. Pure Python works fine without it — the Rust module is auto-detected at runtime.
skylos[llm]installslitellmfor LLM-powered features (skylos agent verify,skylos agent remediate,--llm). Core static analysis works without it.
After installation, we recommend:
Set up CI/CD (30 seconds):
skylos cicd init
git add .github/workflows/skylos.yml && git push
This will automatically scan every PR for dead code and security issues.
Run your first scan:
skylos . # Dead code only
skylos . --danger --secrets # Include security checks
Keep scans focused on active work:
skylos . --diff origin/main
Try advanced workflows only if you need them:
skylos agent review . --model gpt-4.1
skylos defend .
See all commands in the Quick Start table
We benchmarked Skylos against Vulture on 9 of the most popular Python repositories on GitHub — 350k+ combined stars, covering HTTP clients, web frameworks, CLI tools, data validation, terminal UIs, and progress bars. Every single finding was manually verified against the source code. No automated labelling, no cherry-picking.
We deliberately chose projects that stress-test dead code detection in different ways:
| Repository | Stars | What It Tests |
|---|---|---|
| psf/requests | 53k | __init__.py re-exports, Sphinx conf, pytest classes |
| pallets/click | 17k | IO protocol methods (io.RawIOBase subclasses), nonlocal closures |
| encode/starlette | 10k | ASGI interface params, polymorphic dispatch, public API methods |
| Textualize/rich | 51k | __rich_console__ protocol, sentinel vars via f_locals, metaclasses |
| encode/httpx | 14k | Transport/auth protocol methods, zero dead code (pure FP test) |
| pallets/flask | 69k | Jinja2 template globals, Werkzeug protocol methods, extension hooks |
| pydantic/pydantic | 23k | Mypy plugin hooks, hypothesis @resolves, __getattr__ config |
| fastapi/fastapi | 82k | 100+ OpenAPI spec model fields, Starlette base class overrides |
| tqdm/tqdm | 30k | Keras/Dask callbacks, Rich column rendering, pandas monkey-patching |
No repo was excluded for having unfavorable results. We include repos where Vulture beats Skylos (click, starlette, tqdm).
| Repository | Dead Items | Skylos TP | Skylos FP | Vulture TP | Vulture FP |
|---|---|---|---|---|---|
| psf/requests | 6 | 6 | 35 | 6 | 58 |
| pallets/click | 7 | 7 | 8 | 6 | 6 |
| encode/starlette | 1 | 1 | 4 | 1 | 2 |
| Textualize/rich | 13 | 13 | 14 | 10 | 8 |
| encode/httpx | 0 | 0 | 6 | 0 | 59 |
| pallets/flask | 7 | 7 | 12 | 6 | 260 |
| pydantic/pydantic | 11 | 11 | 93 | 10 | 112 |
| fastapi/fastapi | 6 | 6 | 30 | 4 | 102 |
| tqdm/tqdm | 1 | 0 | 18 | 1 | 37 |
| Total | 52 | 51 | 220 | 44 | 644 |
| Metric | Skylos | Vulture |
|---|---|---|
| Recall | 98.1% (51/52) | 84.6% (44/52) |
| False Positives | 220 | 644 |
| Dead items found | 51 | 44 |
Skylos finds 7 more dead items than Vulture with 3x fewer false positives.
Vulture uses flat name matching — if the bare name X appears anywhere as a string or identifier, all definitions named X are considered used. This works well for simple cases but drowns in noise on framework-heavy codebases:
TYPE_CHECKING imports, and mypy plugin hooks. Skylos understands Pydantic model fields and __getattr__ dynamic access.BaseModel attributes like maxLength, exclusiveMinimum). Skylos recognizes these as schema definitions.readable, readinto) on io.RawIOBase subclasses — called by Python's IO stack, not by direct call sites.obj.method()) not resolved back to class definitions.__init__.py because it suppresses __init__.py definitions as potential re-exports.Reproduce any benchmark:
cd real_life_examples/{repo} && python3 ../benchmark_{repo}.pyFull methodology and per-repo breakdowns in the skylos-demo repository.
We also benchmarked Skylos against Knip on a real-world TypeScript library:
| unjs/consola (7k stars, 21 files, ~2,050 LOC) | |
|---|---|
| Dead items | 4 (entire orphaned src/utils/format.ts module) |
| Metric | Skylos | Knip |
|---|---|---|
| Recall | 100% (4/4) | 100% (4/4) |
| Precision | 36.4% | 7.5% |
| F1 Score | 53.3% | 14.0% |
| Speed | 6.83s | 11.08s |
Both tools find all dead code. Skylos has ~5x better precision — Knip incorrectly flags package entry points as dead files (its package.json exports point to dist/ not src/) and reports public API re-exports as unused.
Reproduce:
cd real_life_examples/consola && python3 ../benchmark_consola.py
If you use Skylos in a public repository, open an issue and add it here. This list is based on self-submissions, so it will stay small until more teams opt in publicly.
| Project | Description |
|---|---|
| Skylos | Uses Skylos on itself for dead code, security, and CI gating |
| Your project here | Add yours |
Skylos builds a reference graph of your entire codebase - who defines what, who calls what, across all files.
Parse all files -> Build definition map -> Track references -> Find orphans (zero refs = dead)
Static analysis often struggles with Python's dynamic nature (e.g., getattr, pytest.fixture). Skylos minimizes false positives through:
--trace mode validates "dead" code against actual runtime execution.| Confidence | Meaning | Action |
|---|---|---|
| 100 | Definitely unused | Safe to delete |
| 60 | Probably unused (default threshold) | Review first |
| 40 | Maybe unused (framework helpers) | Likely false positive |
| 20 | Possibly unused (decorated/routes) | Almost certainly used |
| 0 | Show everything | Debug mode |
skylos . -c 60 # Default: high-confidence findings only
skylos . -c 30 # Include framework helpers
skylos . -c 0 # Everything
When Skylos sees Flask, Django, FastAPI, Next.js, or React imports, it adjusts scoring automatically:
| Pattern | Handling |
|---|---|
@app.route, @router.get |
Entry point → marked as used |
app.add_url_rule(...), app.add_api_route(...), app.add_route(...), app.register_listener(...), app.register_middleware(...) |
Imperative route or lifecycle registration → marked as used |
@pytest.fixture |
Treated as a pytest entrypoint, but can be reported as unused if never referenced |
@pytest.hookimpl, @hookimpl |
Plugin hook implementation → marked as used |
@celery.task |
Entry point → marked as used |
getattr(mod, "func") |
Tracks dynamic reference |
getattr(mod, f"handle_{x}") |
Tracks pattern handle_* |
Next.js page.tsx, layout.tsx, route.ts |
Default/named exports → marked as used |
Next.js getServerSideProps, generateMetadata |
Config exports → marked as used |
React.memo(), forwardRef() |
Wrapped components → marked as used |
Exported use* hooks |
Custom hooks → marked as used |
Tests call code in weird ways that look like dead code. By default, Skylos excludes:
| Detected By | Examples |
|---|---|
| Path | /tests/, /test/, *_test.py |
| Imports | pytest, unittest, mock |
| Decorators | @pytest.fixture, @patch |
# These are auto-excluded (confidence set to 0)
/project/tests/test_user.py
/project/test/helper.py
# These are analyzed normally
/project/user.py
/project/test_data.py # Doesn't end with _test.py
Want test files included? Use --include-folder tests.
When ambiguous, we'd rather miss dead code than flag live code as dead.
Framework endpoints are called externally (HTTP, signals). Name resolution handles aliases. When things get unclear, we err on the side of caution.
Skylos ships a curated corpus of small fixtures that encode framework contracts and important Python runtime patterns we must not regress on.
Run it locally when you change analysis behavior:
python3 scripts/corpus_ci.py --manifest corpus/manifest.json
In GitHub, keep the Corpus Guard workflow required in branch protection. When you fix a confirmed false positive, add a focused fixture and expectation to the corpus in the same change.
Skylos can detect pytest fixtures that are defined but never used.
skylos . --pytest-fixtures
This includes fixtures inside conftest.py, since conftest.py is the standard place to store shared test fixtures.
These commands are optional. Use them when you want LLM-assisted review, remediation, or AI defense on top of the core scanner and CI gate.
Skylos uses a hybrid architecture that combines static analysis with LLM reasoning:
| Approach | Recall | Precision | Logic Bugs |
|---|---|---|---|
| Static only | Low | High | ❌ |
| LLM only | High | Medium | ✅ |
| Hybrid | Highest | High | ✅ |
Research shows LLMs find vulnerabilities that static analysis misses, while static analysis validates LLM suggestions. However, LLMs are prone to false positives in dead code if they are asked to invent findings from raw source alone.
Skylos now splits agent workflows into a fast review lane and a slower verification lane.
For dead code, Skylos uses a stricter contract:
skylos agent verify is the dedicated dead-code adjudication passskylos agent scan --verify-dead-code adds that slower verifier back into the review pipeline when you explicitly want itjudge_all mode they are attached as evidence instead of silently deciding the outcomeUse --verification-mode production if you want the cheaper deterministic-first path for agent verify.
| Command | Description |
|---|---|
skylos agent scan PATH |
Fast hybrid review: static findings plus one-pass LLM security/quality review |
skylos agent scan PATH --verify-dead-code |
Same review path, plus the slower dead-code verification pass |
skylos agent scan PATH --no-fixes |
Same review pipeline, skip fix suggestions (faster) |
skylos agent scan PATH --changed |
Analyze only git-changed files |
skylos agent scan PATH --security |
Security-only LLM audit with interactive file selection |
skylos agent verify PATH |
Dead-code-only verification pass over static findings |
skylos agent verify PATH --fix --pr |
Verify, generate removal patches, create branch and commit |
skylos agent remediate PATH |
End-to-end: scan, fix, test, and create PR |
skylos agent remediate PATH --standards |
LLM-guided cleanup with built-in standards (or --standards custom.md) |
skylos agent triage suggest |
Show auto-triage candidates from learned patterns |
skylos agent triage dismiss ID |
Dismiss a finding from the queue |
Skylos supports cloud and local LLM providers:
# Cloud - OpenAI (auto-detected from model name)
skylos agent scan . --model gpt-4.1
# Cloud - Anthropic (auto-detected from model name)
skylos agent scan . --model claude-sonnet-4-20250514
# Local - Ollama
skylos agent scan . \
--provider openai \
--base-url http://localhost:11434/v1 \
--model qwen2.5-coder:7b
# Cheaper dead-code verification path
skylos agent verify . \
--model claude-sonnet-4-20250514 \
--verification-mode production
Note: You can use the --model flag to specify the model that you want. We support Gemini, Groq, Anthropic, ChatGPT and Mistral.
Skylos can use API keys from (1) skylos key, or (2) environment variables.
skylos key
# opens a menu:
# - list keys
# - add key (openai / anthropic / google / groq / mistral / ...)
# - remove key
Set defaults to avoid repeating flags:
# API Keys
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
# Default to local Ollama
export SKYLOS_LLM_PROVIDER=openai
export SKYLOS_LLM_BASE_URL=http://localhost:11434/v1
skylos agent scan --changed analyzes git-changed files, runs static analysis, then uses the LLM for fast file review and code-level fix suggestions. Dead-code verification is optional and not on the critical path by default.
# Run LLM review and output JSON
skylos agent scan . --changed --model claude-sonnet-4-20250514 --format json -o llm-results.json
# Use with cicd review to post inline comments on PRs
skylos cicd review --input results.json --llm-input llm-results.json
The hybrid pipeline runs in stages:
Each PR comment shows the exact vulnerable lines and a drop-in replacement fix.
| Category | Examples |
|---|---|
| Hallucinations | Calls to functions that don't exist |
| Logic bugs | Off-by-one, incorrect conditions, missing edge cases |
| Business logic | Auth bypasses, broken access control |
| Context issues | Problems requiring understanding of intent |
# Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
# Pull a code model
ollama pull qwen2.5-coder:7b
# Use with Skylos
skylos agent scan ./src \
--provider openai \
--base-url http://localhost:11434/v1 \
--model qwen2.5-coder:7b
The remediation agent automates the full fix lifecycle. It scans your project, prioritizes findings, generates fixes via the LLM, validates each fix by running your test suite, and optionally opens a PR.
# Preview what would be fixed (safe, no changes)
skylos agent remediate . --dry-run
# Fix up to 5 critical/high issues, validate with tests
skylos agent remediate . --max-fixes 5 --severity high
# Full auto: fix, test, create PR
skylos agent remediate . --auto-pr --model gpt-4.1
# Use a custom test command
skylos agent remediate . --test-cmd "pytest test/ -x"
Safety guardrails:
--dry-run to preview without touching files--auto-pr always works on a new branch, never touches main--max-fixes prevents runaway changes (default 10)| Model | Provider | Use Case |
|---|---|---|
gpt-4.1 |
OpenAI | Best accuracy |
claude-sonnet-4-20250514 |
Anthropic | Best reasoning |
qwen2.5-coder:7b |
Ollama | Fast local analysis |
codellama:13b |
Ollama | Better local accuracy |
Run Skylos in your CI pipeline with quality gates, GitHub annotations, and PR review comments.
# Auto-generate a GitHub Actions workflow
skylos cicd init
# Commit and activate
git add .github/workflows/skylos.yml && git push
That's it! Your next PR will have:
Want AI-powered code fixes on PRs?
skylos cicd init --llm --model claude-sonnet-4-20250514
This adds an LLM step that generates code-level fix suggestions — showing the vulnerable code and the corrected version inline on your PR.
Optional GitHub Secrets
For the default skylos cicd init workflow, you do not need any Skylos-specific secrets. Add these only if you enable the matching feature in GitHub Actions (Settings > Secrets and variables > Actions):
| Secret | When needed | Description |
|---|---|---|
ANTHROPIC_API_KEY |
If using Claude models | Your Anthropic API key |
OPENAI_API_KEY |
If using GPT models | Your OpenAI API key |
SKYLOS_API_KEY |
For Skylos Cloud features | Get from skylos.dev |
SKYLOS_TOKEN |
If using --upload |
Upload token from skylos.dev/dashboard/settings |
GH_TOKEN is automatically provided by GitHub Actions — no setup needed for PR comments.
Skylos uses a split release workflow:
.github/workflows/release-please.yml updates CHANGELOG.md, bumps pyproject.toml, and opens or updates the release PR..github/workflows/publish.yml publishes from an immutable release tag, either automatically on v* tag pushes or manually via workflow_dispatch.RELEASE_PLEASE_TOKEN secret so bot-authored release PRs can satisfy required PR checks.Release Please is bootstrapped with:
tools/release/.release-please-manifest.json set to 4.2.1tools/release/release-please-config.json set with bootstrap-sha at the commit that prepared 4.2.1 (a498b27b6902b34e469acfddac1068635aae8122)This prevents backfilling old history and starts automated releases from the current baseline.
main (for example: feat: ..., fix: ...).main.vX.Y.Z)..github/workflows/publish.yml.PYPI_TOKEN.If you need to validate packaging before a release:
python -m pip install --upgrade pip
python -m pip install "build>=1.2.2" "twine>=6.1.0"
python -m build --sdist --wheel --outdir dist
python -m twine check dist/*
If you need to manually run the fallback publish workflow, use Actions -> Build and publish -> Run workflow and set ref to the exact release tag (for example v4.2.1). Do not use a branch name.
Skylos validates semantic PR titles via .github/workflows/pr-title.yml with these allowed types:
featfixdocsrefactortestchoreperfstyleciinfrarevertFor complete release ownership, guardrails, and recovery steps, see RELEASE_WORKFLOW.md.
Release roles, prerequisites, branch protection guidance, semantic type policy, and incident recovery steps are documented in RELEASE_WORKFLOW.md.
| Command | Description |
|---|---|
skylos suite <path> |
Full local bundle: static analysis, debt, Python AI defense, and provenance summary |
skylos <path> |
Dead code, security, and quality analysis |
skylos debt <path> |
Technical debt hotspot analysis with baseline-aware prioritization |
skylos discover <path> |
Map LLM/AI integrations in your codebase |
skylos defend <path> |
Check LLM integrations for missing defenses |
| Command | Description |
|---|---|
skylos agent scan <path> |
Fast hybrid static + LLM review |
skylos agent verify <path> |
LLM-verify dead code (100% accuracy) |
skylos agent remediate <path> |
Auto-fix issues and create PR |
skylos agent watch <path> |
Continuous repo monitoring with optional triage pattern learning |
skylos agent pre-commit <path> |
Staged local hook for security, secrets, and quality |
skylos agent triage |
Manage finding triage (dismiss/snooze) |
| Command | Description |
|---|---|
skylos cicd init |
Generate GitHub Actions workflow |
skylos cicd gate |
Quality gate (CI exit code) |
skylos cicd annotate |
Emit GitHub Actions annotations |
skylos cicd review |
Post inline PR review comments |
| Command | Description |
|---|---|
skylos login |
Connect to Skylos Cloud |
skylos whoami |
Show connected account info |
skylos key |
Manage API keys |
skylos credits |
Check credit balance |
| Command | Description |
|---|---|
skylos init |
Initialize config in pyproject.toml |
skylos baseline <path> |
Save current findings as baseline |
skylos whitelist <pattern> |
Manage whitelisted symbols |
skylos badge |
Get badge markdown for README |
skylos rules |
Install/manage community rule packs |
skylos doctor |
Check installation health |
skylos clean |
Remove cache and state files |
skylos tour |
Guided tour of capabilities |
skylos commands |
List all commands (flat) |
Run skylos <command> --help for detailed usage of any command.
skylos cicd initGenerates a ready-to-use GitHub Actions workflow.
skylos cicd init
skylos cicd init --triggers pull_request schedule
skylos cicd init --analysis security quality
skylos cicd init --python-version 3.11
skylos cicd init --llm --model gpt-4.1
skylos cicd init --upload # include --upload step + SKYLOS_TOKEN env
skylos cicd init --upload --llm --model claude-sonnet-4-20250514 # upload + LLM
skylos cicd init --defend # add AI Defense check step
skylos cicd init --defend --upload # defend + upload results to cloud
skylos cicd init --no-baseline
skylos cicd init -o .github/workflows/security.yml
skylos cicd gateChecks findings against your quality gate. Exits 0 (pass) or 1 (fail). Uses the same check_gate() as skylos . --gate.
skylos . --danger --quality --secrets --json > results.json 2>/dev/null
skylos cicd gate --input results.json
skylos cicd gate --input results.json --strict
skylos cicd gate --input results.json --summary
You can also use the main CLI directly:
skylos . --gate --summary
Configure thresholds in pyproject.toml:
[tool.skylos.gate]
fail_on_critical = true
max_critical = 0
max_high = 5
max_security = 10
max_quality = 10
skylos cicd annotateEmits GitHub Actions annotations (::error, ::warning, ::notice). Uses the same _emit_github_annotations() as skylos . --github, with sorting and a 50-annotation cap.
skylos cicd annotate --input results.json
skylos cicd annotate --input results.json --severity high
skylos cicd annotate --input results.json --max 30
skylos . --github
skylos cicd reviewPosts inline PR review comments and a summary via gh CLI. Only comments on lines changed in the PR.
skylos cicd review --input results.json
skylos cicd review --input results.json --pr 20
skylos cicd review --input results.json --summary-only
skylos cicd review --input results.json --max-comments 10
skylos cicd review --input results.json --diff-base origin/develop
# With LLM-generated code fixes (vulnerable code → fixed code)
skylos cicd review --input results.json --llm-input llm-results.json
When --llm-input is provided, each inline comment shows the problematic code and the corrected version:
🔴 CRITICAL SKY-D211
Possible SQL injection: tainted or string-built query.
Why: User input is concatenated directly into the SQL query string.
Vulnerable code:
results = conn.execute(f"SELECT * FROM users WHERE name LIKE '%{q}%'").fetchall()
Fixed code:
results = conn.execute("SELECT * FROM users WHERE name LIKE ?", (f"%{q}%",)).fetchall()
In GitHub Actions, PR number and repo are auto-detected. Requires GH_TOKEN.
The gate and annotation logic lives in the core Skylos modules (gatekeeper.py and cli.py). The cicd commands are convenience wrappers that read from a JSON file and call the same functions:
skylos cicd command |
Calls |
|---|---|
gate |
gatekeeper.run_gate_interaction(summary=True) |
annotate |
cli._emit_github_annotations(max_annotations=50) |
review |
New — cicd/review.py (PR comments via gh api) |
init |
New — cicd/workflow.py (YAML generation) |
--json > results.json 2>/dev/null then pass --input results.json to each subcommand.skylos baseline . to snapshot existing findings, then --baseline in CI to only flag new issues.gate and annotate print to stdout. review requires gh CLI.mcp-name: io.github.duriantaco/skylos
Skylos exposes its analysis capabilities as an MCP (Model Context Protocol) server, allowing AI assistants like Claude Desktop to scan your codebase directly.
pip install skylos
Add to your Claude Desktop config (~/.config/claude/claude_desktop_config.json on Linux, ~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"skylos": {
"command": "python",
"args": ["-m", "skylos_mcp.server"]
}
}
}
| Tool | Description |
|---|---|
analyze |
Dead code detection (unused functions, imports, classes, variables) |
security_scan |
Security vulnerability scan (--danger equivalent) |
quality_check |
Code quality and complexity analysis (--quality equivalent) |
secrets_scan |
Hardcoded secrets detection (--secrets equivalent) |
remediate |
End-to-end: scan, generate LLM fixes, validate with tests |
generate_fix |
Generate removal patches for confirmed dead code |
verify_dead_code |
LLM-verify dead code findings (reduce false positives) |
learn_triage |
Record a triage decision for pattern learning |
get_triage_suggestions |
Get auto-triage candidates from learned patterns |
| Resource | URI | Description |
|---|---|---|
| Latest result | skylos://results/latest |
Most recent analysis run |
| Result by ID | skylos://results/{run_id} |
Specific analysis run |
| List results | skylos://results |
All stored analysis runs |
Once configured, you can ask Claude:
security_scanquality_checksecrets_scanremediateBaseline tracking lets you snapshot existing findings so CI only flags new issues introduced by a PR.
# Create baseline from current state
skylos baseline .
# Run analysis, only show findings NOT in the baseline
skylos . --danger --secrets --quality --baseline
# In CI: compare against baseline
skylos . --danger --baseline --gate
The baseline is stored in .skylos/baseline.json. Commit this file to your repo so CI can use it.
Real-time AI-powered code analysis directly in your editor.
ext install oha.skylos-vscode-extension
pip install skylos
skylos.openaiApiKey or skylos.anthropicApiKey| Layer | Trigger | What It Does |
|---|---|---|
| Static Analysis | On save | Runs Skylos CLI for dead code, secrets, dangerous patterns |
| AI Watcher | On idle (2s) | Sends changed functions to GPT-4/Claude for bug detection |
| Setting | Default | Description |
|---|---|---|
skylos.aiProvider |
"openai" |
"openai" or "anthropic" |
skylos.openaiApiKey |
"" |
Your OpenAI API key |
skylos.anthropicApiKey |
"" |
Your Anthropic API key |
skylos.idleMs |
2000 |
Wait time before AI analysis (ms) |
skylos.runOnSave |
true |
Run Skylos CLI on save |
skylos.enableSecrets |
true |
Scan for hardcoded secrets |
skylos.enableDanger |
true |
Flag dangerous patterns |
| Action | Result |
|---|---|
| Save a Python file | Skylos CLI scans the workspace |
| Type and pause | AI analyzes changed functions |
| Click "Fix with AI" | Generates fix with diff preview |
Cmd+Shift+P -> "Skylos: Scan Workspace" |
Full project scan |
Install from VS Code Marketplace
Block bad code before it merges. Configure thresholds, run locally, then automate in CI.
skylos init
Creates [tool.skylos] in your pyproject.toml:
[tool.skylos]
# Quality thresholds
complexity = 10
nesting = 3
max_args = 5
max_lines = 50
duplicate_strings = 3
ignore = []
model = "gpt-4.1"
# Language overrides (optional)
[tool.skylos.languages.typescript]
complexity = 15
nesting = 4
# Gate policy
[tool.skylos.gate]
fail_on_critical = true
max_security = 0 # Zero tolerance
max_quality = 10 # Allow up to 10 warnings
strict = false
Run scans locally with exit codes:
skylos . --danger --gate
0 = passed1 = failedUse in any CI system:
name: Skylos Quality Gate
on:
pull_request:
branches: [main, master]
jobs:
skylos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install skylos
- run: skylos . --danger --gate
Limitation: Anyone with repo access can delete or modify this workflow.
Server-controlled GitHub checks that cannot be bypassed by developers.
pip install skylos
skylos sync setup
| Feature | Free | Pro |
|---|---|---|
| Local scans | ✅ | ✅ |
--gate exit codes |
✅ | ✅ |
| GitHub Actions | ✅ (DIY) | ✅ (auto) |
| Developer can bypass? | Yes | No |
| Server-controlled check | ❌ | ✅ |
| Slack/Discord alerts | ❌ | ✅ |
mainRepo Settings → Secrets → Actions → New secret
SKYLOS_TOKENSkylos is designed to live everywhere your code does—from your IDE to your deployment pipeline.
| Environment | Tool | Use Case |
|---|---|---|
| VS Code | Skylos Extension | Real-time guarding. Highlights code rot and risks on-save. |
| Web UI | skylos run |
Launch a local dashboard for visual auditing. Defaults to localhost:5090; override with --port or SKYLOS_PORT. |
| CI/CD | GitHub Actions / Pre-commit | Automated gates that audit every PR before it merges. |
| Quality Gate | skylos --gate |
Block deployment if security or complexity thresholds are exceeded. |
Control how you consume the watchdog's findings.
| Flag | Format | Primary Use |
|---|---|---|
--tui |
TUI Dashboard | Launch the interactive TUI dashboard. |
--tree |
Logic Tree | Visualizes code hierarchy and structural dependencies. |
--json |
Machine Raw | Piping results to jq, custom scripts, or log aggregators. |
--sarif |
SARIF | GitHub Code Scanning, IDE integration. Includes CWE taxonomy and per-rule CWE relationships |
--llm |
LLM Report | Structured findings with code context for Claude Code, Codex, or any AI agent. |
-o, --output |
File Export | Save the audit report directly to a file instead of stdout. |
By default, Skylos finds dead code. Enable additional scans with flags.
For dead-code precision regressions, use the checked-in corpus guard:
python3 scripts/corpus_ci.py --manifest corpus/manifest.json
This is a deterministic regression suite built from curated framework and Python-language patterns. It is not a proof of correctness, but it is the main guard against reintroducing known false positives.
skylos .
Reading the output:
| Column | Meaning |
|---|---|
| Name | The unused function, import, class, or variable |
| Location | file:line where it's defined |
| Conf | Confidence score (0–100%) — how certain Skylos is that this code is truly unused. Higher = safer to remove |
--danger)Tracks tainted data from user input to dangerous sinks.
skylos . --danger
Reading the output:
| Column | Meaning |
|---|---|
| Issue | The vulnerability type (e.g. SQL injection, eval) with its rule ID |
| Severity | Risk level: Critical > High > Medium > Low |
| Message | What was found and why it's dangerous |
| Location | file:line where the issue occurs |
| Symbol | The function or scope containing the vulnerable code |
| Rule | ID | What It Catches |
|---|---|---|
| Injection | ||
| SQL injection | SKY-D211 | cur.execute(f"SELECT * FROM users WHERE name='{name}'") |
| SQL raw query | SKY-D217 | sqlalchemy.text(), pandas.read_sql(), Django .raw() with tainted input |
| Command injection | SKY-D212 | os.system(), subprocess(shell=True) with tainted input |
| SSRF | SKY-D216 | requests.get(request.args["url"]) |
| Path traversal | SKY-D215 | open(request.args.get("p")) |
| XSS (mark_safe) | SKY-D226 | Untrusted content passed to mark_safe() / Markup() |
| XSS (template) | SKY-D227 | Inline template with autoescape disabled |
| XSS (HTML build) | SKY-D228 | HTML built from unescaped user input |
| Open redirect | SKY-D230 | User-controlled URL passed to redirect() |
| Dangerous Calls | ||
| eval() | SKY-D201 | Dynamic code execution via eval() |
| exec() | SKY-D202 | Dynamic code execution via exec() |
| os.system() | SKY-D203 | OS command execution |
| pickle.load | SKY-D204 | Unsafe deserialization |
| yaml.load | SKY-D206 | yaml.load() without SafeLoader |
| Weak hash (MD5) | SKY-D207 | hashlib.md5() |
| Weak hash (SHA1) | SKY-D208 | hashlib.sha1() |
| shell=True | SKY-D209 | subprocess with shell=True |
| TLS disabled | SKY-D210 | requests with verify=False |
| Unsafe deserialization | SKY-D233 | marshal.loads, shelve.open, jsonpickle.decode, dill |
| Web Security | ||
| CORS misconfiguration | SKY-D231 | Wildcard origins, credential leaks, overly permissive headers |
| JWT vulnerabilities | SKY-D232 | algorithms=['none'], missing verification, weak secrets |
| Mass assignment | SKY-D234 | Django Meta.fields = '__all__' exposes all model fields |
| Supply Chain | ||
| Hallucinated dependency | SKY-D222 | Imported package doesn't exist on PyPI (CRITICAL) |
| Undeclared dependency | SKY-D223 | Import not declared in requirements.txt / pyproject.toml |
| MCP Security | ||
| Tool description poisoning | SKY-D240 | Prompt injection in MCP tool metadata |
| Unauthenticated transport | SKY-D241 | SSE/HTTP MCP server without auth middleware |
| Permissive resource URI | SKY-D242 | Path traversal via MCP resource URI template |
| Network-exposed MCP | SKY-D243 | MCP server bound to 0.0.0.0 without auth |
| Hardcoded secrets in MCP | SKY-D244 | Secrets in MCP tool parameter defaults |
Full list in DANGEROUS_CODE.md.
--secrets)Detects hardcoded credentials.
skylos . --secrets
Reading the output:
| Column | Meaning |
|---|---|
| Provider | The service the secret belongs to (e.g. AWS, Stripe, GitHub) or "generic" for high-entropy strings |
| Message | Description of the detected credential |
| Preview | A masked snippet of the secret (e.g. sk_live_****) |
| Location | file:line where the secret was found |
Providers: GitHub, GitLab, AWS, Stripe, Slack, Google, SendGrid, Twilio, private keys.
--sca)Scans your installed dependencies against the OSV.dev vulnerability database.
skylos . --sca
Reading the output:
| Column | Meaning |
|---|---|
| Package | The dependency and its installed version (e.g. requests@2.28.0) |
| Vuln ID | The CVE or advisory identifier |
| Severity | Risk level: Critical > High > Medium > Low |
| Reachability | Whether your code actually calls the vulnerable code path: Reachable (confirmed risk), Unreachable (safe), or Inconclusive |
| Fix | The patched version to upgrade to |
--quality)Flags functions that are hard to maintain.
skylos . --quality
Reading the output:
| Column | Meaning |
|---|---|
| Type | The category: Complexity, Nesting, Structure, Quality (duplicate literals, coupling, cohesion) |
| Name | The function, class, or string literal that triggered the finding |
| Detail | The measured value and the threshold — e.g. Complexity: 14 (max 10) means 14 branches were found but the limit is 10; repeated 5× (max 3) means a string literal appears 5 times but should appear at most 3 |
| Location | file:line where the finding starts |
| Rule | ID | What It Catches |
|---|---|---|
| Complexity | ||
| Cyclomatic complexity | SKY-Q301 | Too many branches/loops (default: >10) |
| Deep nesting | SKY-Q302 | Too many nested levels (default: >3) |
| Async Blocking | SKY-Q401 | Detects blocking calls inside async functions that kill server throughput |
| God class | SKY-Q501 | Class has too many methods/attributes |
| Coupling (CBO) | SKY-Q701 | High inter-class coupling (7 dependency types: inheritance, type hints, instantiation, attribute access, imports, decorators, protocol/ABC) |
| Cohesion (LCOM) | SKY-Q702 | Low class cohesion — disconnected method groups that should be split (LCOM1/4/5 metrics with Union-Find) |
| Architecture | ||
| Distance from Main Sequence | SKY-Q802 | Module far from ideal balance of abstractness vs instability |
| Zone warning | SKY-Q803 | Module in Zone of Pain (rigid) or Zone of Uselessness (throwaway) |
| DIP violation | SKY-Q804 | Stable module depends on unstable module (Dependency Inversion Principle) |
| Structure | ||
| Too many arguments | SKY-C303 | Functions with >5 args |
| Function too long | SKY-C304 | Functions >50 lines |
| Logic | ||
| Mutable default | SKY-L001 | def foo(x=[]) - causes state leaks |
| Bare except | SKY-L002 | except: swallows SystemExit |
| Dangerous comparison | SKY-L003 | x == None instead of x is None |
| Anti-pattern try block | SKY-L004 | Nested try, or try wrapping too much logic |
| Unused exception var | SKY-L005 | except Error as e: where e is never referenced |
| Inconsistent return | SKY-L006 | Function returns both values and None |
| Duplicate string literal | SKY-L027 | Same string repeated 3+ times (see suppressing duplicate strings) |
| Too many returns | SKY-L028 | Function has 5+ return statements |
| Boolean trap | SKY-L029 | Boolean positional parameter harms call-site readability |
| Performance | ||
| Memory load | SKY-P401 | .read() / .readlines() loads entire file |
| Pandas no chunk | SKY-P402 | read_csv() without chunksize |
| Nested loop | SKY-P403 | O(N²) complexity |
| Unreachable | ||
| Unreachable Code | SKY-UC001 | if False: or else after always-true |
| Empty | ||
| Empty File | SKY-E002 | Empty File |
To ignore a specific rule:
# pyproject.toml
[tool.skylos]
ignore = ["SKY-P403"] # Allow nested loops
Tune thresholds and disable rules in pyproject.toml:
[tool.skylos]
# Adjust thresholds
complexity = 15 # Default: 10
nesting = 4 # Default: 3
max_args = 7 # Default: 5
max_lines = 80
Skylos flags string literals that appear 3+ times (rule SKY-L027). If a repeated string is intentional (e.g. a status value checked in multiple places), you have three options:
Option 1: Raise the threshold — only flag strings repeated more than N times:
# pyproject.toml
[tool.skylos]
duplicate_strings = 10 # Default: 3. Set to 999 to effectively disable.
Option 2: Disable the rule entirely:
# pyproject.toml
[tool.skylos]
ignore = ["SKY-L027"]
Option 3: Suppress inline — on the specific line:
if somevar == "lokal": # skylos: ignore
do_something()
skylos debt)Ranks structural debt hotspots using the existing static findings from quality, architecture, and dead code analysis.
skylos debt .
skylos debt . --changed
skylos debt . --baseline
skylos debt . --save-baseline
skylos debt . --history
skylos debt . --json
How the debt output works:
| Field | Meaning |
|---|---|
| score | Structural debt score for the hotspot itself |
| priority | Triage priority for what to fix next. Changed files and baseline drift raise this without changing structural debt score |
| project score | Repo-level structural debt score. This stays project-scoped even when --changed is used |
| baseline status | Whether a hotspot is new, worsened, improved, or unchanged versus the saved debt baseline |
--changed is a filter/view mode, not a different scoring model. It limits the visible hotspot list to git-changed files, but the repo debt score still reflects the full project.
Debt baselines and debt history are project-level artifacts. --save-baseline and --history only work when you scan the project root.
addopts)Set default flags in pyproject.toml so you don't have to type them every time — just like pytest's addopts:
[tool.skylos]
addopts = ["--quality", "--danger", "--secrets"]
String format also works:
[tool.skylos]
addopts = "--quality --danger --confidence=80"
CLI flags override addopts, so you can always narrow or widen a run without editing config.
Skylos also honors [tool.skylos].exclude during CLI scans, which is the cleanest place
to keep team-specific paths like custom venv names or .claude/worktrees/.
# LLM-powered audit (single file)
skylos . --audit
# Specify model
skylos . --audit --model claude-haiku-4-5-20251001
Note: For full project context and better results, use
skylos agent scaninstead. For auto-fixing, useskylos agent remediate.
skylos suite . # Static + debt + AI defense + provenance
skylos . -a # All static scans (danger + secrets + quality + sca)
skylos agent remediate . --dry-run # Preview AI-assisted fixes
Static analysis can't see everything. Python's dynamic nature means patterns like getattr(), plugin registries, and string-based dispatch look like dead code—but they're not.
Smart tracing solves this. By running your tests with sys.settrace(), Skylos records every function that actually gets called.
# Run tests with call tracing, then analyze
skylos . --trace
# Trace data is saved to .skylos_trace
skylos .
| Analysis Type | Accuracy | What It Catches |
|---|---|---|
| Static only | 70-85% | Direct calls, imports, decorators |
| + Framework rules | 85-95% | Django/Flask routes, pytest fixtures |
+ --trace |
95-99% | Dynamic dispatch, plugins, registries |
# Static analysis will think this is dead because there's no direct call visible
def handle_login():
return "Login handler"
# But it is actually called dynamically at runtime
action = request.args.get("action")
func = getattr(module, f"handle_{action}")
func() # here
| Without Tracing | With --trace |
|---|---|
handle_login flagged as dead |
handle_login marked as used |
| Situation | Command |
|---|---|
| Have pytest/unittest tests | skylos . --trace |
| No tests | skylos . (static only; repeated runs reuse .skylos/cache/grep_results.json for grep verification) |
| CI with cached trace | skylos . (reuses .skylos_trace) |
These patterns are invisible to static analysis but caught with --trace:
# 1. Dynamic dispatch
func = getattr(module, f"handle_{action}")
func()
# 2. Plugin or registry patterns
PLUGINS = []
def register(f):
PLUGINS.append(f)
return f
@register
def my_plugin(): ...
# 3. Visitor patterns
class MyVisitor(ast.NodeVisitor):
def visit_FunctionDef(self, node): ... # Called via getattr
# 4. String-based access
globals()["my_" + "func"]()
locals()[func_name]()
.skylos_trace to reuse trace data in CI without re-running tests.Control what Skylos analyzes and what it ignores.
Silence specific findings with comments:
# Ignore dead code detection on this line
def internal_hook(): # pragma: no skylos
pass
# this also works
def another(): # pragma: no cover
pass
def yet_another(): # noqa
pass
By default, Skylos excludes: __pycache__, .git, .pytest_cache, .mypy_cache, .tox, htmlcov, .coverage, build, dist, *.egg-info, venv, .venv
# See what's excluded by default
skylos --list-default-excludes
# Add more exclusions
skylos . --exclude-folder vendor --exclude-folder generated
# Skylos also respects project `.gitignore` entries during file discovery
# so ignored folders like custom venvs and worktrees are skipped automatically
# Force include an excluded folder
skylos . --include-folder venv
# Scan everything (no exclusions)
skylos . --no-default-excludes
Use [tool.skylos].exclude in pyproject.toml for team-wide custom exclusions that should apply
even outside .gitignore.
Disable rules globally in pyproject.toml:
[tool.skylos]
ignore = [
"SKY-P403", # Allow nested loops
"SKY-L003", # Allow == None
"SKY-S101", # Allow hardcoded secrets (not recommended)
]
| Want to... | Do this |
|---|---|
| Skip one line | # pragma: no skylos |
| Skip one secret | # skylos: ignore[SKY-S101] |
| Skip a folder | --exclude-folder NAME |
| Skip a rule globally | ignore = ["SKY-XXX"] in pyproject.toml |
| Include excluded folder | --include-folder NAME |
| Skip team-specific folders | exclude = ["customenv", ".claude/worktrees"] in pyproject.toml |
| Run all checks | -a or addopts in pyproject.toml |
| Scan everything | --no-default-excludes |
Suppress false positives permanently without inline comments cluttering your code.
# Add a pattern
skylos whitelist 'handle_*'
# Add with reason
skylos whitelist dark_logic --reason "Called via globals() in dispatcher"
# View current whitelist
skylos whitelist --show
# Single line
def dynamic_handler(): # skylos: ignore
pass
# Also works
def another(): # noqa: skylos
pass
# Block ignore
# skylos: ignore-start
def block_one():
pass
def block_two():
pass
# skylos: ignore-end
pyproject.toml)[tool.skylos.whitelist]
# Glob patterns
names = [
"handle_*",
"visit_*",
"*Plugin",
]
# With reasons (shows in --show output)
[tool.skylos.whitelist.documented]
"dark_logic" = "Called via globals() string manipulation"
"BasePlugin" = "Discovered via __subclasses__()"
# Temporary (warns when expired)
[tool.skylos.whitelist.temporary]
"legacy_handler" = { reason = "Migration - JIRA-123", expires = "2026-03-01" }
# Per-path overrides
[tool.skylos.overrides."src/plugins/*"]
whitelist = ["*Plugin", "*Handler"]
| Want to... | Do this |
|---|---|
| Whitelist one function | skylos whitelist func_name |
| Whitelist a pattern | skylos whitelist 'handle_*' |
| Document why | skylos whitelist x --reason "why" |
| Temporary whitelist | Add to [tool.skylos.whitelist.temporary] with expires |
| Per-folder rules | Add [tool.skylos.overrides."path/*"] |
| View whitelist | skylos whitelist --show |
| Inline ignore | # skylos: ignore or # noqa: skylos |
| Block ignore | # skylos: ignore-start ... # skylos: ignore-end |
Usage: skylos [OPTIONS] PATH
Arguments:
PATH Path to the Python project to analyze
Options:
-h, --help Show this help message and exit
--json Output raw JSON instead of formatted text
--tree Output results in tree format
--tui Launch interactive TUI dashboard
--sarif Output SARIF format for GitHub/IDE integration
--llm Output LLM-optimized report with code context for AI agents
-c, --confidence LEVEL Confidence threshold 0-100 (default: 60)
--comment-out Comment out code instead of deleting
-o, --output FILE Write output to file instead of stdout
-v, --verbose Enable verbose output
--version Checks version
-i, --interactive Interactively select items to remove
--dry-run Show what would be removed without modifying files
--exclude-folder FOLDER Exclude a folder from analysis (can be used multiple times)
--include-folder FOLDER Force include a folder that would otherwise be excluded
--no-default-excludes Don't exclude default folders (__pycache__, .git, venv, etc.)
--list-default-excludes List the default excluded folders
--secrets Scan for api keys/secrets
--danger Scan for dangerous code
--quality Code complexity and maintainability
--sca Scan dependencies for known CVEs (OSV.dev)
-a, --all Enable all checks: --danger --secrets --quality --sca
--trace Run tests with coverage first
--audit LLM-powered logic review (legacy)
--model MODEL LLM model (default: gpt-4.1)
--gate Fail on threshold breach (for CI)
--force Bypass quality gate (emergency override)
Usage: skylos agent <command> [OPTIONS] PATH
Commands:
scan Hybrid static + LLM analysis (replaces analyze/audit/review/security-audit)
verify LLM-verify dead code findings
remediate Scan, fix, test, and create PR (end-to-end)
watch Continuous repo monitoring
pre-commit Staged local hook for security, secrets, and quality
triage Manage finding triage (suggest/dismiss/snooze/restore)
status Show active-agent summary
serve Local HTTP API for editor integrations
Agent scan options:
--model MODEL LLM model to use (default: gpt-4.1)
--provider PROVIDER Force provider: openai or anthropic
--base-url URL Custom endpoint for local LLMs
--format FORMAT Output: table, tree, json, sarif
-o, --output FILE Write output to file
--min-confidence LEVEL Filter: high, medium, low
--no-fixes Skip fix suggestions (faster)
--changed Analyze only git-changed files
--security Security-only LLM audit mode
-i, --interactive Interactive file selection (with --security)
Agent remediate options:
--dry-run Show plan without applying fixes (safe preview)
--max-fixes N Max findings to fix per run (default: 10)
--auto-pr Create branch, commit, push, and open PR
--branch-prefix PREFIX Git branch prefix (default: skylos/fix)
--test-cmd CMD Custom test command (default: auto-detect)
--severity LEVEL Min severity filter: critical, high, medium, low
--standards [FILE] Enable LLM cleanup mode (uses built-in standards, or pass custom .md)
Agent watch options:
--once Run one refresh cycle and exit
--interval SECONDS Poll interval for continuous watch mode
--cycles N Stop after N refresh cycles (0 = keep watching)
--learn Enable triage pattern learning during watch mode
--format FORMAT Output: table, json
Usage: skylos discover [OPTIONS] PATH
Map all LLM integrations in a Python codebase.
Options:
--json Output as JSON
-o, --output FILE Write output to file
--exclude FOLDER [FOLDER...] Additional folders to exclude
Usage: skylos defend [OPTIONS] PATH
Check LLM integrations for missing defenses.
Options:
--json Output as JSON
-o, --output FILE Write output to file
--min-severity LEVEL Minimum severity to include (critical/high/medium/low)
--fail-on LEVEL Exit 1 if any defense finding at or above this severity
--min-score N Exit 1 if defense score below this percentage (0-100)
--policy FILE Path to skylos-defend.yaml policy file
--owasp IDS Comma-separated OWASP LLM IDs (e.g. LLM01,LLM04)
--exclude FOLDER [FOLDER...] Additional folders to exclude
--upload Upload defense results to Skylos Cloud dashboard
Commands:
skylos PATH Analyze a project (static analysis)
skylos debt PATH Analyze technical debt hotspots
skylos discover PATH Map LLM integrations in a codebase
skylos defend PATH Check LLM integrations for missing defenses
skylos agent scan PATH Hybrid static + LLM analysis
skylos agent verify PATH LLM-verify dead code findings
skylos agent remediate PATH End-to-end scan, fix, test, and PR
skylos agent triage CMD Manage finding triage
skylos baseline PATH Snapshot current findings for CI baselining
skylos cicd init Generate GitHub Actions workflow
skylos cicd gate Check findings against quality gate
skylos cicd annotate Emit GitHub Actions annotations
skylos cicd review Post inline PR review comments (supports --llm-input)
skylos init Initialize pyproject.toml config
skylos key Manage API keys (add/remove/list)
skylos whitelist PATTERN Add pattern to whitelist
skylos whitelist --show Display current whitelist
skylos run Start web UI at localhost:5090 (default; override with --port or SKYLOS_PORT)
Whitelist Options:
skylos whitelist PATTERN Add glob pattern (e.g., 'handle_*')
skylos whitelist NAME --reason X Add with documentation
skylos whitelist --show Display all whitelist entries
Skylos displays confidence for each finding:
────────────────── Unused Functions ──────────────────
# Name Location Conf
1 handle_secret app.py:16 70%
2 totally_dead app.py:50 90%
Higher confidence = more certain it's dead code.
The interactive mode lets you select specific functions and imports to remove:
spacebar to select/unselectQ: Why doesn't Skylos find 100% of dead code? A: Python's dynamic features (getattr, globals, etc.) can't be perfectly analyzed statically. No tool can achieve 100% accuracy. If they say they can, they're lying.
Q: Are these benchmarks realistic? A: They test common scenarios but can't cover every edge case. Use them as a guide, not gospel.
Q: Why doesn't Skylos detect my unused Flask routes?
A: Web framework routes are given low confidence (20) because they might be called by external HTTP requests. Use --confidence 20 to see them. We acknowledge there are current limitations to this approach so use it sparingly.
Q: What confidence level should I use? A: Start with 60 (default) for safe cleanup. Use 30 for framework applications. Use 20 for more comprehensive auditing.
Q: What does --trace do?
A: It runs pytest (or unittest) with coverage tracking before analysis. Functions that actually executed are marked as used with 100% confidence, eliminating false positives from dynamic dispatch patterns.
Q: Do I need 100% test coverage for --trace to be useful?
A: No. However, we STRONGLY encourage you to have tests. Any coverage helps. If you have 30% test coverage, that's 30% of your code verified. The other 70% still uses static analysis. Coverage only removes false positives, it never adds them.
Q: Why are fixtures in conftest.py showing up as unused?
A: conftest.py is the standard place for shared fixtures. If a fixture is defined there but never referenced by any test, Skylos will report it as unused. This is normal and safe to review.
Q: My tests are failing. Can I still use --trace?
A: Yes. Coverage tracks execution, not pass/fail. Even failing tests provide coverage data.
Q: What do the numbers in the quality table mean?
A: Each quality finding has a measured value and a threshold (the configured maximum). For example, Complexity: 14 (max 10) means the function has 14 branches but the limit is 10. For duplicate string literals, repeated 5× (max 3) means the same string appears 5 times — extract it to a named constant. You can tune thresholds in pyproject.toml under [tool.skylos].
Q: What's the difference between skylos . --audit and skylos agent scan?
A: skylos agent scan is the fast hybrid review path: static analysis plus one-pass LLM security/quality review, with fix suggestions when enabled. Use --verify-dead-code if you also want the slower dead-code adjudication pass. The --audit flag on the base command is the legacy static-only mode.
Q: What does --verification-mode do?
A: It controls how aggressively Skylos sends dead-code candidates to the LLM in verification workflows. judge_all is the most aggressive dead-code review mode and is mainly useful for agent verify or agent scan --verify-dead-code. production is cheaper and lets more obvious alive cases get suppressed before the LLM sees them.
Q: Can I use local LLMs instead of OpenAI/Anthropic?
A: Yes! Use --base-url to point to Ollama, LM Studio, or any OpenAI-compatible endpoint. No API key needed for localhost.
getattr(), globals(), runtime imports are hard to detect.py, .pyi, .pyw, .env, .yaml, .yml, .json, .toml, .ini, .cfg, .conf, .ts, .tsx, .js, .jsx, .gocomplexity, nesting, max_args, max_lines, duplicate_strings) are configurable in pyproject.toml under [tool.skylos].--trace flag only helps if you have tests or can run your application. Pure static analysis is still available without it.Permission Errors
Error: Permission denied when removing function
Check file permissions before running in interactive mode.
Missing Dependencies
Interactive mode requires 'inquirer' package
Install with: pip install skylos[interactive]
No API Key Found
# For cloud providers
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
# For local LLMs (no key needed)
skylos agent scan . --base-url http://localhost:11434/v1 --model codellama
Local LLM Connection Refused
# Verify Ollama is running
curl http://localhost:11434/v1/models
# Check LM Studio
curl http://localhost:1234/v1/models
We welcome contributions! Please read our Contributing Guidelines before submitting pull requests.
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)dangerous.py listskylos cicd init --defend, pre-commit hookMore stuff coming soon!
This project is licensed under the Apache 2.0 License - see the LICENSE file for details.
Please log in to share your review and rating for this MCP.
Explore related MCPs that share similar capabilities and solve comparable challenges
by modelcontextprotocol
A Model Context Protocol server for Git repository interaction and automation.
by zed-industries
A high‑performance, multiplayer code editor designed for speed and collaboration.
by modelcontextprotocol
Model Context Protocol Servers
by modelcontextprotocol
A Model Context Protocol server that provides time and timezone conversion capabilities.
by cline
An autonomous coding assistant that can create and edit files, execute terminal commands, and interact with a browser directly from your IDE, operating step‑by‑step with explicit user permission.
by upstash
Provides up-to-date, version‑specific library documentation and code examples directly inside LLM prompts, eliminating outdated information and hallucinated APIs.
by daytonaio
Provides a secure, elastic infrastructure that creates isolated sandboxes for running AI‑generated code with sub‑90 ms startup, unlimited persistence, and OCI/Docker compatibility.
by continuedev
Enables faster shipping of code by integrating continuous AI agents across IDEs, terminals, and CI pipelines, offering chat, edit, autocomplete, and customizable agent workflows.
by github
Connects AI tools directly to GitHub, enabling natural‑language interactions for repository browsing, issue and pull‑request management, CI/CD monitoring, code‑security analysis, and team collaboration.
{
"mcpServers": {
"skylos": {
"command": "python",
"args": [
"-m",
"skylos_mcp.server"
],
"env": {}
}
}
}claude mcp add skylos python -m skylos_mcp.server