duriantaco

skylos

Built by duriantaco β€’ 355 stars

What is skylos?

Open-source Python, TypeScript, and Go SAST with dead code detection. Finds secrets, exploitable flows, and AI regressions. VS Code extension, GitHub Action, and MCP server for AI agents.

How to use skylos?

1. Install a compatible MCP client (like Claude Desktop). 2. Open your configuration settings. 3. Add skylos using the following command: npx @modelcontextprotocol/skylos 4. Restart the client and verify the new tools are active.
πŸ›‘οΈ Scoped (Restricted)
npx @modelcontextprotocol/skylos --scope restricted
πŸ”“ Unrestricted Access
npx @modelcontextprotocol/skylos

Key Features

Native MCP Protocol Support
Real-time Tool Activation & Execution
Verified High-performance Implementation
Secure Resource & Context Handling

Optimized Use Cases

Extending AI models with custom local capabilities
Automating system workflows via natural language
Connecting external data sources to LLM context windows

skylos FAQ

Q

Is skylos safe?

Yes, skylos follows the standardized Model Context Protocol security patterns and only executes tools with explicit user-granted permissions.

Q

Is skylos up to date?

skylos is currently active in the registry with 355 stars on GitHub, indicating its reliability and community support.

Q

Are there any limits for skylos?

Usage limits depend on the specific implementation of the MCP server and your system resources. Refer to the official documentation below for technical details.

Official Documentation

View on GitHub
<div align="center"> <img src="assets/DOG_1.png" alt="Skylos - Dead code, security, and AI defense for Python, TypeScript, and Go" width="300"> <h1>Skylos: Open-Source Python SAST, Dead Code Detection, and AI Code Security</h1> <h3>Find unused code, hardcoded secrets, exploitable flows, and AI-generated security regressions in Python, TypeScript, and Go. Run locally or gate pull requests in CI/CD.</h3> </div>

License: Apache 2.0 CI/CD Ready codecov PyPI - Python Version PyPI version Downloads/month Downloads total VS Code Marketplace GitHub stars GitHub forks Skylos Discord

πŸ“– Website Β· Documentation Β· Blog Β· GitHub Action Β· VS Code Extension Β· MCP Server


What is Skylos?

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.

Best for

  • Python teams that want dead code detection with fewer false positives than Vulture
  • Repositories using Cursor, Copilot, Claude Code, or other AI coding assistants
  • CI/CD pull request gates with GitHub annotations and review comments
  • Python LLM applications that need OWASP LLM Top 10 checks

Available as

  • CLI for local scans and CI/CD workflows
  • GitHub Action for pull request gating and annotations
  • VS Code extension for in-editor findings and AI-assisted fixes
  • MCP server for AI agents and coding assistants

Start here

GoalCommandWhat you get
Scan a reposkylos . -aDead code, risky flows, secrets, and code quality findings
Gate pull requestsskylos cicd initA GitHub Actions workflow with a quality gate and inline annotations
Audit an LLM appskylos defend .Optional AI defense checks for Python LLM integrations

Why teams adopt it

  1. Better dead code signal on real frameworks: Skylos understands FastAPI, Django, Flask, pytest, Next.js, React, and more, so dynamic code produces less noise.
  2. Diff-aware AI regression detection: Skylos can catch removed auth decorators, CSRF, rate limiting, validation, logging, and other controls that disappear during AI-assisted refactors.
  3. One workflow instead of three tools: Dead code, security scanning, and PR gating live in the same CLI and CI flow.
  4. Local-first by default: You can keep scans on your machine and add optional AI or cloud features later if you need them.
  5. Self-explaining output: Every table prints a legend explaining what each column and number means β€” no manual required.

Why Skylos over Vulture for Python dead code detection?

SkylosVulture
Recall98.1% (51/52)84.6% (44/52)
False Positives220644
Framework-aware (FastAPI, Django, pytest)YesNo
Security scanning (secrets, SQLi, SSRF)YesNo
AI-powered analysisYesNo
CI/CD quality gatesYesNo
TypeScript + Go supportYesNo

Benchmarked on 9 popular Python repos (350k+ combined stars) + TypeScript (consola). Every finding manually verified. Full case study β†’

πŸš€ New to Skylos? Start with CI/CD Integration

# 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:

  • Automatic dead code detection on every PR
  • Security vulnerability scanning (SQLi, secrets, dangerous patterns)
  • Quality gate that fails builds on critical issues
  • Inline PR review comments with file:line links
  • GitHub Annotations visible in the "Files Changed" tab

No configuration needed - works out of the box with sensible defaults. See CI/CD section for customization.


Table of Contents

Quick Start

If you are evaluating Skylos, start with the core workflow below. The LLM and AI defense commands are optional.

Core Workflow

ObjectiveCommandOutcome
First scanskylos .Dead code findings with confidence scoring
Audit risk and qualityskylos . -aDead code, risky flows, secrets, quality, and SCA findings
Higher-confidence dead codeskylos . --traceCross-reference static findings with runtime activity
Review only changed linesskylos . --diff origin/mainFocus findings on active work instead of legacy debt
Gate locallyskylos --gateFail on findings before code leaves your machine
Set up CI/CDskylos cicd initGenerate a GitHub Actions workflow in 30 seconds
Gate in CIskylos cicd gate --input results.jsonFail builds when issues cross your threshold

Optional Workflows

ObjectiveCommandOutcome
Detect Unused Pytest Fixturesskylos . --pytest-fixturesFind unused @pytest.fixture across tests + conftest
AI-Powered Analysisskylos agent scan . --model gpt-4.1Static-first analysis plus judge-all LLM verification for dead code
Dead Code Verificationskylos agent verify . --model gpt-4.1Dead-code-only second pass: static findings reviewed by the LLM
Security Auditskylos agent scan . --securityDeep LLM security review with interactive file selection
Auto-Remediateskylos agent remediate . --auto-prScan, fix, test, and open a PR β€” end to end
Code Cleanupskylos agent remediate . --standardsLLM-guided code quality cleanup against coding standards
PR Reviewskylos agent scan . --changedAnalyze only git-changed files
PR Review (JSON)skylos agent scan . --changed --format json -o results.jsonLLM review with code-level fix suggestions
Local LLMskylos agent scan . --base-url http://localhost:11434/v1 --model codellamaUse Ollama/LM Studio (no API key needed)
PR Review (CI)skylos cicd review -i results.jsonPost inline comments on PRs
AI Defense: Discoverskylos discover .Map all LLM integrations in your codebase
AI Defense: Defendskylos defend .Check LLM integrations for missing guardrails
AI Defense: CI Gateskylos defend . --fail-on critical --min-score 70Block PRs with critical AI defense gaps
Whitelistskylos whitelist 'handle_*'Suppress known dynamic patterns

Technical Debt Hotspots

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.

Demo

Skylos demo

Backup (GitHub): https://github.com/duriantaco/skylos/discussions/82

Key Capabilities

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.

Security Scanning (SAST)

  • Taint Analysis: Traces untrusted input from API endpoints to databases to prevent SQL Injection and XSS.
  • Secrets Detection: Hunts down hardcoded API keys (AWS, Stripe, OpenAI) and private credentials before commit.
  • Vulnerability Checks: Flags dangerous patterns like eval(), unsafe pickle, and weak cryptography.

AI-Generated Code Guardrails

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.

  • Phantom Call Detection: Catches calls to security functions (sanitize_input, validate_token, check_permission, etc.) that are never defined or imported β€” AI hallucinates these constantly. hallucinated_reference, high
  • Phantom Decorator Detection: Catches security decorators (@require_auth, @rate_limit, @authenticate, etc.) that are never defined or imported. hallucinated_reference, high
  • Unfinished Generation: Detects functions with only pass, ..., or raise NotImplementedError β€” AI-generated stubs that silently do nothing in production. incomplete_generation, medium
  • Undefined Config: Flags os.getenv("ENABLE_X") referencing feature flags that are never defined anywhere in the project. ghost_config, medium
  • Stale Mock Detection: Catches mock.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
  • Security TODO Scanners: Flags # TODO: add auth placeholders that AI left behind and nobody finished.
  • Disabled Security Controls: Detects verify=False, @csrf_exempt, DEBUG=True, and ALLOWED_HOSTS=["*"].
  • Credential & Randomness Checks: Catches hardcoded passwords and random.choice() used for security-sensitive values like tokens and OTPs.

Prompt Injection and Content Scanning

These checks run under --danger and look for prompt injection patterns or obfuscated instructions in repository content.

  • Multi-File Prompt Injection Scanner: Scans Python, Markdown, YAML, JSON, TOML, and .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.
  • Text Canonicalization Engine: NFKC normalization, whitespace folding, and confusable replacement neutralize obfuscation before pattern matching.
  • Zero-Width & Invisible Unicode: Detects zero-width spaces, joiners, BOM, and bidi overrides (U+200B–U+202E) that hide payloads from human reviewers.
  • Base64 Obfuscation Detection: Automatically decodes base64-encoded strings and re-scans for injection content.
  • Homoglyph / Mixed-Script Detection: Flags Cyrillic and Greek characters mixed with Latin text (e.g., Cyrillic 'Π°' in password) that bypass visual review.
  • Location-Aware Severity: Findings in README files, HTML comments, and YAML prompt fields get elevated severity. Test files are automatically skipped.

Advanced: AI Defense for LLM Apps

Static analysis for AI application security that maps every LLM call in your Python codebase and checks for missing guardrails. Python only (TypeScript/Go support planned).

# 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:

CheckSeverityOWASPWhat it detects
no-dangerous-sinkCriticalLLM02LLM output flowing to eval/exec/subprocess
untrusted-input-to-promptCriticalLLM01Raw user input in prompt with no processing
tool-scopeCriticalLLM04Agent tools with dangerous system calls
tool-schema-presentCriticalLLM04Agent tools without typed schemas
output-validationHighLLM02LLM output used without structured validation
prompt-delimiterHighLLM01User input in prompts without delimiters
rag-context-isolationHighLLM01RAG context injected without isolation
output-pii-filterHighLLM06No PII filtering on user-facing LLM output
model-pinnedMediumLLM03Model version not pinned (floating alias)
input-length-limitLowLLM01No input length check before LLM call
logging-presentMediumOpsNo logging around LLM calls
cost-controlsMediumOpsNo max_tokens set on LLM calls
rate-limitingMediumOpsNo 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.

Dead Code Detection & Cleanup

  • Find Unused Code: Identifies unreachable functions, orphan classes, and unused imports with confidence scoring.
  • Smart Tracing: Distinguishes between truly dead code and dynamic frameworks (Flask/Django routes, Pytest fixtures).
  • Safe Pruning: Uses LibCST to safely remove dead code without breaking syntax.

Advanced: Agents, Reviews, and Remediation

  • Context-aware audits: Combines static analysis speed with LLM reasoning to validate findings and filter noise.
  • Remediation workflow: skylos agent remediate can scan, generate fixes, run tests, and optionally open a PR.
  • Local model support: Supports Ollama and other OpenAI-compatible local endpoints if you want code to stay on your machine.

CI/CD and PR Gating

  • 30-Second Workflow Setup: skylos cicd init generates GitHub Actions workflows with sensible defaults.
  • Diff-Aware Enforcement: Gate only the lines that changed, fail on severity thresholds, and keep legacy debt manageable with baselines.
  • PR-Native Feedback: GitHub annotations, inline review comments, and optional dashboard upload keep findings where teams already work.

Safe Cleanup and Workflow Controls

  • CST-safe removals: Uses LibCST to remove selected imports or functions (handles multiline imports, aliases, decorators, async etc..)
  • Logic Awareness: Deep integration for Python frameworks (Django, Flask, FastAPI) and TypeScript (Tree-sitter) to identify active routes and dependencies.
  • Granular Filtering: Skip lines tagged with # pragma: no skylos, # pragma: no cover, or # noqa

Operational Governance & Runtime

  • Coverage Integration: Auto-detects .skylos-trace files to verify dead code with runtime data
  • Quality Gates: Enforces hard thresholds for complexity, nesting, and security risk via pyproject.toml to block non-compliant PRs
  • Interactive CLI: Manually verify and remove/comment-out findings through an inquirer-based terminal interface
  • Security-Audit Mode: Leverages an independent reasoning loop to identify security vulnerabilities

Pytest Hygiene

  • Unused Fixture Detection: Finds unused @pytest.fixture definitions in test_*.py and conftest.py
  • Cross-file Resolution: Tracks fixtures used across modules, not just within the same file

Multi-Language Support

LanguageParserDead CodeSecurityQuality
PythonASTβœ…βœ…βœ…
TypeScript/TSXTree-sitterβœ…βœ…βœ…
JavaTree-sitterβœ…βœ…βœ…
GoStandalone 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.

TypeScript Rules

RuleIDWhat 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-D201eval() usage
Dynamic execSKY-D202exec(), new Function(), setTimeout with string
XSSSKY-D226innerHTML, outerHTML, document.write(), dangerouslySetInnerHTML
SQL injectionSKY-D211Template literal / f-string in SQL query
Command injectionSKY-D212child_process.exec(), os.system()
SSRFSKY-D216fetch()/axios with variable URL
Open redirectSKY-D230res.redirect() with variable argument
Weak hashSKY-D207/D208MD5 / SHA1 usage
Prototype pollutionSKY-D510__proto__ access
Dynamic requireSKY-D245require() with variable argument
JWT bypassSKY-D246jwt.decode() without verification
CORS wildcardSKY-D247cors({ origin: '*' })
Internal URLSKY-D248Hardcoded localhost/127.0.0.1 URLs
Insecure randomSKY-D250Math.random() for security-sensitive ops
Sensitive logsSKY-D251Passwords/tokens passed to console.log()
Insecure cookieSKY-D252Missing httpOnly/secure flags
Timing attackSKY-D253===/== comparison of secrets
Storage tokensSKY-D270Sensitive data in localStorage/sessionStorage
Error disclosureSKY-D271error.stack/.sql sent in HTTP response
SecretsSKY-S101Hardcoded API keys + high-entropy strings
Quality
ComplexitySKY-Q301Cyclomatic complexity exceeds threshold
Nesting depthSKY-Q302Too many nested levels
Function lengthSKY-C304Function exceeds line limit
Too many paramsSKY-C303Function has too many parameters
Duplicate conditionSKY-Q305Identical condition in if-else-if chain
Await in loopSKY-Q402await inside for/while loop
Unreachable codeSKY-UC002Code 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.

Installation

Basic Installation

## 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] installs litellm for LLM-powered features (skylos agent verify, skylos agent remediate, --llm). Core static analysis works without it.

🎯 What's Next?

After installation, we recommend:

  1. 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.

  2. Run your first scan:

    skylos .                              # Dead code only
    skylos . --danger --secrets           # Include security checks
    
  3. Keep scans focused on active work:

    skylos . --diff origin/main
    
  4. 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


Skylos vs. Vulture Benchmark

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.

Why These 9 Repos?

We deliberately chose projects that stress-test dead code detection in different ways:

RepositoryStarsWhat It Tests
psf/requests53k__init__.py re-exports, Sphinx conf, pytest classes
pallets/click17kIO protocol methods (io.RawIOBase subclasses), nonlocal closures
encode/starlette10kASGI interface params, polymorphic dispatch, public API methods
Textualize/rich51k__rich_console__ protocol, sentinel vars via f_locals, metaclasses
encode/httpx14kTransport/auth protocol methods, zero dead code (pure FP test)
pallets/flask69kJinja2 template globals, Werkzeug protocol methods, extension hooks
pydantic/pydantic23kMypy plugin hooks, hypothesis @resolves, __getattr__ config
fastapi/fastapi82k100+ OpenAPI spec model fields, Starlette base class overrides
tqdm/tqdm30kKeras/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).

Results

RepositoryDead ItemsSkylos TPSkylos FPVulture TPVulture FP
psf/requests6635658
pallets/click77866
encode/starlette11412
Textualize/rich131314108
encode/httpx006059
pallets/flask77126260
pydantic/pydantic11119310112
fastapi/fastapi66304102
tqdm/tqdm1018137
Total525122044644
MetricSkylosVulture
Recall98.1% (51/52)84.6% (44/52)
False Positives220644
Dead items found5144

Skylos finds 7 more dead items than Vulture with 3x fewer false positives.

Why Skylos Produces 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:

  • Flask (260 Vulture FP): Vulture flags every Jinja2 template global, Werkzeug protocol method, and Flask extension hook. Skylos recognizes Flask/Werkzeug patterns.
  • Pydantic (112 Vulture FP): Vulture flags all config class annotations, TYPE_CHECKING imports, and mypy plugin hooks. Skylos understands Pydantic model fields and __getattr__ dynamic access.
  • FastAPI (102 Vulture FP): Vulture flags 100+ OpenAPI spec model fields (Pydantic BaseModel attributes like maxLength, exclusiveMinimum). Skylos recognizes these as schema definitions.
  • httpx (59 Vulture FP): Vulture flags every transport and auth protocol method. Skylos suppresses interface implementations.

Where Skylos Still Loses (Honestly)

  • click (8 vs 6 FP): IO protocol methods (readable, readinto) on io.RawIOBase subclasses β€” called by Python's IO stack, not by direct call sites.
  • starlette (4 vs 2 FP): Instance method calls across files (obj.method()) not resolved back to class definitions.
  • tqdm (18 vs 37 FP, 0 vs 1 TP): Skylos misses 1 dead function in __init__.py because it suppresses __init__.py definitions as potential re-exports.

Reproduce any benchmark: cd real_life_examples/{repo} && python3 ../benchmark_{repo}.py

Full methodology and per-repo breakdowns in the skylos-demo repository.

Skylos vs. Knip (TypeScript)

We also benchmarked Skylos against Knip on a real-world TypeScript library:

unjs/consola (7k stars, 21 files, ~2,050 LOC)
Dead items4 (entire orphaned src/utils/format.ts module)
MetricSkylosKnip
Recall100% (4/4)100% (4/4)
Precision36.4%7.5%
F1 Score53.3%14.0%
Speed6.83s11.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


Projects Using Skylos

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.

Analyzed with Skylos

ProjectDescription
SkylosUses Skylos on itself for dead code, security, and CI gating
Your project hereAdd yours

Add your project β†’


How It Works

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)

High Precision & Confidence Scoring

Static analysis often struggles with Python's dynamic nature (e.g., getattr, pytest.fixture). Skylos minimizes false positives through:

  1. Confidence Scoring: Grades findings (High/Medium/Low) so you only see what matters.
  2. Hybrid Verification: Uses LLM reasoning to double-check static findings before reporting.
  3. Runtime Tracing: Optional --trace mode validates "dead" code against actual runtime execution.
ConfidenceMeaningAction
100Definitely unusedSafe to delete
60Probably unused (default threshold)Review first
40Maybe unused (framework helpers)Likely false positive
20Possibly unused (decorated/routes)Almost certainly used
0Show everythingDebug mode
skylos . -c 60  # Default: high-confidence findings only
skylos . -c 30  # Include framework helpers  
skylos . -c 0  # Everything

Framework Detection

When Skylos sees Flask, Django, FastAPI, Next.js, or React imports, it adjusts scoring automatically:

PatternHandling
@app.route, @router.getEntry 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.fixtureTreated as a pytest entrypoint, but can be reported as unused if never referenced
@pytest.hookimpl, @hookimplPlugin hook implementation β†’ marked as used
@celery.taskEntry 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.tsDefault/named exports β†’ marked as used
Next.js getServerSideProps, generateMetadataConfig exports β†’ marked as used
React.memo(), forwardRef()Wrapped components β†’ marked as used
Exported use* hooksCustom hooks β†’ marked as used

Test File Exclusion

Tests call code in weird ways that look like dead code. By default, Skylos excludes:

Detected ByExamples
Path/tests/, /test/, *_test.py
Importspytest, 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.

Philosophy

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.

Unused Pytest Fixtures

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.

Advanced Workflows

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:

Why Hybrid?

ApproachRecallPrecisionLogic Bugs
Static onlyLowHigh❌
LLM onlyHighMediumβœ…
HybridHighestHighβœ…

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.

For dead code, Skylos now uses a stricter contract:

  • static analysis generates the candidate list
  • repo facts and graph evidence are gathered around each candidate
  • skylos agent scan and skylos agent verify send nearly every references == 0 candidate through the LLM in judge_all mode
  • deterministic suppressors still exist, but in judge_all mode they are attached as evidence instead of silently deciding the outcome

Use --verification-mode production if you want the cheaper deterministic-first path instead of the default judge-all review.

Agent Commands

CommandDescription
skylos agent scan PATHFull hybrid pipeline with fix suggestions and judge-all dead-code verification
skylos agent scan PATH --no-fixesSame pipeline, skip fix suggestions (faster)
skylos agent scan PATH --changedAnalyze only git-changed files
skylos agent scan PATH --securitySecurity-only LLM audit with interactive file selection
skylos agent verify PATHDead-code-only verification pass over static findings
skylos agent verify PATH --fix --prVerify, generate removal patches, create branch and commit
skylos agent remediate PATHEnd-to-end: scan, fix, test, and create PR
skylos agent remediate PATH --standardsLLM-guided cleanup with built-in standards (or --standards custom.md)
skylos agent triage suggestShow auto-triage candidates from learned patterns
skylos agent triage dismiss IDDismiss a finding from the queue

Provider Configuration

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.

Keys and configuration

Skylos can use API keys from (1) skylos key, or (2) environment variables.

Recommended (interactive)

skylos key
# opens a menu:
# - list keys
# - add key (openai / anthropic / google / groq / mistral / ...)
# - remove key

Environment Variables

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

LLM PR Review

skylos agent scan --changed analyzes git-changed files, runs static analysis, then uses the LLM to generate code-level fix suggestions for every finding (security, quality, and dead code).

# 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:

  1. Static analysis β€” finds security, quality, and dead code issues
  2. Dead-code verification β€” the LLM judges static dead-code candidates using graph evidence, repo facts, and surrounding context
  3. Additional LLM analysis β€” scans for logic/security issues static analysis may miss
  4. Code fix generation β€” for each reported finding, generates the problematic code snippet and a corrected version

Each PR comment shows the exact vulnerable lines and a drop-in replacement fix.

What LLM Analysis Detects

CategoryExamples
HallucinationsCalls to functions that don't exist
Logic bugsOff-by-one, incorrect conditions, missing edge cases
Business logicAuth bypasses, broken access control
Context issuesProblems requiring understanding of intent

Local LLM Setup (Ollama)

# 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

Remediation Agent

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 by default β€” use --dry-run to preview without touching files
  • Fixes that break tests are automatically reverted
  • Low-confidence fixes are skipped
  • After applying a fix, Skylos re-scans to confirm the finding is actually gone
  • --auto-pr always works on a new branch, never touches main
  • --max-fixes prevents runaway changes (default 10)

Recommended Models

ModelProviderUse Case
gpt-4.1OpenAIBest accuracy
claude-sonnet-4-20250514AnthropicBest reasoning
qwen2.5-coder:7bOllamaFast local analysis
codellama:13bOllamaBetter local accuracy

CI/CD

Run Skylos in your CI pipeline with quality gates, GitHub annotations, and PR review comments.

Quick Start (30 seconds)

# 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:

  • Dead code detection
  • Security scanning (SQLi, SSRF, secrets)
  • Quality checks
  • Inline PR comments with clickable file:line links
  • Quality gate that fails builds on critical issues

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):

SecretWhen neededDescription
ANTHROPIC_API_KEYIf using Claude modelsYour Anthropic API key
OPENAI_API_KEYIf using GPT modelsYour OpenAI API key
SKYLOS_API_KEYFor Skylos Cloud featuresGet from skylos.dev
SKYLOS_TOKENIf using --uploadUpload token from skylos.dev/dashboard/settings

GH_TOKEN is automatically provided by GitHub Actions β€” no setup needed for PR comments.

Command Reference

Core Analysis

CommandDescription
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
skylos city <path>Visualize codebase as a Code City topology

AI Agent

CommandDescription
skylos agent scan <path>Hybrid static + LLM analysis
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>Analyze staged files (git hook)
skylos agent triageManage finding triage (dismiss/snooze)

CI/CD

CommandDescription
skylos cicd initGenerate GitHub Actions workflow
skylos cicd gateQuality gate (CI exit code)
skylos cicd annotateEmit GitHub Actions annotations
skylos cicd reviewPost inline PR review comments

Account

CommandDescription
skylos loginConnect to Skylos Cloud
skylos whoamiShow connected account info
skylos keyManage API keys
skylos creditsCheck credit balance

Utility

CommandDescription
skylos initInitialize config in pyproject.toml
skylos baseline <path>Save current findings as baseline
skylos whitelist <pattern>Manage whitelisted symbols
skylos badgeGet badge markdown for README
skylos rulesInstall/manage community rule packs
skylos doctorCheck installation health
skylos cleanRemove cache and state files
skylos tourGuided tour of capabilities
skylos commandsList all commands (flat)

Run skylos <command> --help for detailed usage of any command.

Commands (Detailed)

skylos cicd init

Generates 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 gate

Checks 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 annotate

Emits 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 review

Posts 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.

How It Fits Together

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 commandCalls
gategatekeeper.run_gate_interaction(summary=True)
annotatecli._emit_github_annotations(max_annotations=50)
reviewNew β€” cicd/review.py (PR comments via gh api)
initNew β€” cicd/workflow.py (YAML generation)

Tips

  • Run analysis once, consume many times β€” use --json > results.json 2>/dev/null then pass --input results.json to each subcommand.
  • Baseline β€” run skylos baseline . to snapshot existing findings, then --baseline in CI to only flag new issues.
  • Local testing β€” all commands work locally. gate and annotate print to stdout. review requires gh CLI.

MCP Server

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.

Setup

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"]
    }
  }
}

Available Tools

ToolDescription
analyzeDead code detection (unused functions, imports, classes, variables)
security_scanSecurity vulnerability scan (--danger equivalent)
quality_checkCode quality and complexity analysis (--quality equivalent)
secrets_scanHardcoded secrets detection (--secrets equivalent)
remediateEnd-to-end: scan, generate LLM fixes, validate with tests
generate_fixGenerate removal patches for confirmed dead code
verify_dead_codeLLM-verify dead code findings (reduce false positives)
learn_triageRecord a triage decision for pattern learning
get_triage_suggestionsGet auto-triage candidates from learned patterns

Available Resources

ResourceURIDescription
Latest resultskylos://results/latestMost recent analysis run
Result by IDskylos://results/{run_id}Specific analysis run
List resultsskylos://resultsAll stored analysis runs

Usage in Claude Desktop

Once configured, you can ask Claude:

  • "Scan my project for security issues" β†’ calls security_scan
  • "Check code quality in src/" β†’ calls quality_check
  • "Find hardcoded secrets" β†’ calls secrets_scan
  • "Fix security issues in my project" β†’ calls remediate

Baseline Tracking

Baseline 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.

VS Code Extension

Real-time AI-powered code analysis directly in your editor.

<img src="editors/vscode/media/vsce.gif" alt="Skylos VS Code Extension β€” inline dead code detection, security scanning, and CodeLens actions" width="700" />

Installation

  1. Search "Skylos" in VS Code marketplace or run:
   ext install oha.skylos-vscode-extension
  1. Make sure the CLI is installed:
   pip install skylos
  1. (Optional) Add your API key for AI features in VS Code Settings β†’ skylos.openaiApiKey or skylos.anthropicApiKey

How It Works

LayerTriggerWhat It Does
Static AnalysisOn saveRuns Skylos CLI for dead code, secrets, dangerous patterns
AI WatcherOn idle (2s)Sends changed functions to GPT-4/Claude for bug detection

Features

  • Real-time Analysis: Detects bugs as you type β€” no save required
  • CodeLens Buttons: "Fix with AI" and "Dismiss" appear inline on error lines
  • Streaming Fixes: See fix progress in real-time
  • Smart Caching: Only re-analyzes functions that actually changed
  • Multi-Provider: Choose between OpenAI and Anthropic

New Features

  • MCP Server Support: Connect Skylos directly to Claude Desktop or any MCP client to chat with your codebase.
  • CI/CD Agents: Autonomous bots that scan, fix, test, and open PRs automatically in your pipeline.
  • Hybrid Verification: Eliminates false positives by verifying static findings with LLM reasoning.

Extension Settings

SettingDefaultDescription
skylos.aiProvider"openai""openai" or "anthropic"
skylos.openaiApiKey""Your OpenAI API key
skylos.anthropicApiKey""Your Anthropic API key
skylos.idleMs2000Wait time before AI analysis (ms)
skylos.runOnSavetrueRun Skylos CLI on save
skylos.enableSecretstrueScan for hardcoded secrets
skylos.enableDangertrueFlag dangerous patterns

Usage

ActionResult
Save a Python fileSkylos CLI scans the workspace
Type and pauseAI analyzes changed functions
Click "Fix with AI"Generates fix with diff preview
Cmd+Shift+P -> "Skylos: Scan Workspace"Full project scan

Privacy

  • Static analysis runs 100% locally
  • AI features send only changed function code to your configured provider
  • We DO NOT collect any telemetry or data

Install from VS Code Marketplace

Gating

Block bad code before it merges. Configure thresholds, run locally, then automate in CI.

Initialize Configuration

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

Free Tier

Run scans locally with exit codes:

skylos . --danger --gate
  • Exit code 0 = passed
  • Exit code 1 = failed

Use 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.


Pro Tier

Server-controlled GitHub checks that cannot be bypassed by developers.

Quick Setup

pip install skylos
skylos sync setup

How It Works

  1. Developer opens PR β†’ GitHub App creates required check ("Queued")
  2. Scan runs β†’ Results upload to Skylos server
  3. Server updates check β†’ Pass βœ… or Fail ❌
  4. Developer cannot merge until check passes

Free vs Pro

FeatureFreePro
Local scansβœ…βœ…
--gate exit codesβœ…βœ…
GitHub Actionsβœ… (DIY)βœ… (auto)
Developer can bypass?YesNo
Server-controlled checkβŒβœ…
Slack/Discord alertsβŒβœ…

GitHub App Setup

  1. Dashboard -> Settings -> Install GitHub App
  2. Select your repository
  3. In GitHub repo settings:
    • Settings -> Branches -> Add rule -> main
    • Require status checks
    • Select "Skylos Quality Gate"

Add Token to GitHub

Repo Settings β†’ Secrets β†’ Actions β†’ New secret

  • Name: SKYLOS_TOKEN
  • Value: (from Dashboard β†’ Settings)

Integration and Ecosystem

Skylos is designed to live everywhere your code doesβ€”from your IDE to your deployment pipeline.

1. Integration Environments

EnvironmentToolUse Case
VS CodeSkylos ExtensionReal-time guarding. Highlights code rot and risks on-save.
Web UIskylos runLaunch a local dashboard at localhost:5090 for visual auditing.
CI/CDGitHub Actions / Pre-commitAutomated gates that audit every PR before it merges.
Quality Gateskylos --gateBlock deployment if security or complexity thresholds are exceeded.

2. Output Formats

Control how you consume the watchdog's findings.

FlagFormatPrimary Use
--tuiTUI DashboardLaunch the interactive TUI dashboard.
--treeLogic TreeVisualizes code hierarchy and structural dependencies.
--jsonMachine RawPiping results to jq, custom scripts, or log aggregators.
--sarifSARIFGitHub Code Scanning, IDE integration. Includes CWE taxonomy and per-rule CWE relationships
--llmLLM ReportStructured findings with code context for Claude Code, Codex, or any AI agent.
-o, --outputFile ExportSave the audit report directly to a file instead of stdout.

Auditing and Precision

By default, Skylos finds dead code. Enable additional scans with flags.

Dead Code (default)

skylos .

Reading the output:

ColumnMeaning
NameThe unused function, import, class, or variable
Locationfile:line where it's defined
ConfConfidence score (0–100%) β€” how certain Skylos is that this code is truly unused. Higher = safer to remove

Security (--danger)

Tracks tainted data from user input to dangerous sinks.

skylos . --danger

Reading the output:

ColumnMeaning
IssueThe vulnerability type (e.g. SQL injection, eval) with its rule ID
SeverityRisk level: Critical > High > Medium > Low
MessageWhat was found and why it's dangerous
Locationfile:line where the issue occurs
SymbolThe function or scope containing the vulnerable code
RuleIDWhat It Catches
Injection
SQL injectionSKY-D211cur.execute(f"SELECT * FROM users WHERE name='{name}'")
SQL raw querySKY-D217sqlalchemy.text(), pandas.read_sql(), Django .raw() with tainted input
Command injectionSKY-D212os.system(), subprocess(shell=True) with tainted input
SSRFSKY-D216requests.get(request.args["url"])
Path traversalSKY-D215open(request.args.get("p"))
XSS (mark_safe)SKY-D226Untrusted content passed to mark_safe() / Markup()
XSS (template)SKY-D227Inline template with autoescape disabled
XSS (HTML build)SKY-D228HTML built from unescaped user input
Open redirectSKY-D230User-controlled URL passed to redirect()
Dangerous Calls
eval()SKY-D201Dynamic code execution via eval()
exec()SKY-D202Dynamic code execution via exec()
os.system()SKY-D203OS command execution
pickle.loadSKY-D204Unsafe deserialization
yaml.loadSKY-D206yaml.load() without SafeLoader
Weak hash (MD5)SKY-D207hashlib.md5()
Weak hash (SHA1)SKY-D208hashlib.sha1()
shell=TrueSKY-D209subprocess with shell=True
TLS disabledSKY-D210requests with verify=False
Unsafe deserializationSKY-D233marshal.loads, shelve.open, jsonpickle.decode, dill
Web Security
CORS misconfigurationSKY-D231Wildcard origins, credential leaks, overly permissive headers
JWT vulnerabilitiesSKY-D232algorithms=['none'], missing verification, weak secrets
Mass assignmentSKY-D234Django Meta.fields = '__all__' exposes all model fields
Supply Chain
Hallucinated dependencySKY-D222Imported package doesn't exist on PyPI (CRITICAL)
Undeclared dependencySKY-D223Import not declared in requirements.txt / pyproject.toml
MCP Security
Tool description poisoningSKY-D240Prompt injection in MCP tool metadata
Unauthenticated transportSKY-D241SSE/HTTP MCP server without auth middleware
Permissive resource URISKY-D242Path traversal via MCP resource URI template
Network-exposed MCPSKY-D243MCP server bound to 0.0.0.0 without auth
Hardcoded secrets in MCPSKY-D244Secrets in MCP tool parameter defaults

Full list in DANGEROUS_CODE.md.

Secrets (--secrets)

Detects hardcoded credentials.

skylos . --secrets

Reading the output:

ColumnMeaning
ProviderThe service the secret belongs to (e.g. AWS, Stripe, GitHub) or "generic" for high-entropy strings
MessageDescription of the detected credential
PreviewA masked snippet of the secret (e.g. sk_live_****)
Locationfile:line where the secret was found

Providers: GitHub, GitLab, AWS, Stripe, Slack, Google, SendGrid, Twilio, private keys.

Dependency Vulnerabilities (--sca)

Scans your installed dependencies against the OSV.dev vulnerability database.

skylos . --sca

Reading the output:

ColumnMeaning
PackageThe dependency and its installed version (e.g. requests@2.28.0)
Vuln IDThe CVE or advisory identifier
SeverityRisk level: Critical > High > Medium > Low
ReachabilityWhether your code actually calls the vulnerable code path: Reachable (confirmed risk), Unreachable (safe), or Inconclusive
FixThe patched version to upgrade to

Quality (--quality)

Flags functions that are hard to maintain.

skylos . --quality

Reading the output:

ColumnMeaning
TypeThe category: Complexity, Nesting, Structure, Quality (duplicate literals, coupling, cohesion)
NameThe function, class, or string literal that triggered the finding
DetailThe 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
Locationfile:line where the finding starts
RuleIDWhat It Catches
Complexity
Cyclomatic complexitySKY-Q301Too many branches/loops (default: >10)
Deep nestingSKY-Q302Too many nested levels (default: >3)
Async BlockingSKY-Q401Detects blocking calls inside async functions that kill server throughput
God classSKY-Q501Class has too many methods/attributes
Coupling (CBO)SKY-Q701High inter-class coupling (7 dependency types: inheritance, type hints, instantiation, attribute access, imports, decorators, protocol/ABC)
Cohesion (LCOM)SKY-Q702Low class cohesion β€” disconnected method groups that should be split (LCOM1/4/5 metrics with Union-Find)
Architecture
Distance from Main SequenceSKY-Q802Module far from ideal balance of abstractness vs instability
Zone warningSKY-Q803Module in Zone of Pain (rigid) or Zone of Uselessness (throwaway)
DIP violationSKY-Q804Stable module depends on unstable module (Dependency Inversion Principle)
Structure
Too many argumentsSKY-C303Functions with >5 args
Function too longSKY-C304Functions >50 lines
Logic
Mutable defaultSKY-L001def foo(x=[]) - causes state leaks
Bare exceptSKY-L002except: swallows SystemExit
Dangerous comparisonSKY-L003x == None instead of x is None
Anti-pattern try blockSKY-L004Nested try, or try wrapping too much logic
Unused exception varSKY-L005except Error as e: where e is never referenced
Inconsistent returnSKY-L006Function returns both values and None
Duplicate string literalSKY-L027Same string repeated 3+ times (see suppressing duplicate strings)
Too many returnsSKY-L028Function has 5+ return statements
Boolean trapSKY-L029Boolean positional parameter harms call-site readability
Performance
Memory loadSKY-P401.read() / .readlines() loads entire file
Pandas no chunkSKY-P402read_csv() without chunksize
Nested loopSKY-P403O(NΒ²) complexity
Unreachable
Unreachable CodeSKY-UC001if False: or else after always-true
Empty
Empty FileSKY-E002Empty 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

Suppressing Duplicate String Findings

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()

Technical Debt (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:

FieldMeaning
scoreStructural debt score for the hotspot itself
priorityTriage priority for what to fix next. Changed files and baseline drift raise this without changing structural debt score
project scoreRepo-level structural debt score. This stays project-scoped even when --changed is used
baseline statusWhether 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.

Default CLI Options (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/.

Legacy AI Flags

# 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 scan instead. For auto-fixing, use skylos agent remediate.

Combine Everything

skylos . -a                           # All static scans (danger + secrets + quality + sca)
skylos agent remediate . --dry-run    # Preview AI-assisted fixes

Smart Tracing

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.

Quick Start

# Run tests with call tracing, then analyze
skylos . --trace

# Trace data is saved to .skylos_trace
skylos .

How It Works

Analysis TypeAccuracyWhat It Catches
Static only70-85%Direct calls, imports, decorators
+ Framework rules85-95%Django/Flask routes, pytest fixtures
+ --trace95-99%Dynamic dispatch, plugins, registries

Example

# 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 TracingWith --trace
handle_login flagged as deadhandle_login marked as used

When To Use

SituationCommand
Have pytest/unittest testsskylos . --trace
No testsskylos . (static only; repeated runs reuse .skylos/cache/grep_results.json for grep verification)
CI with cached traceskylos . (reuses .skylos_trace)

What Tracing Catches

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]()

Important Notes

  • Tracing only adds information. Low test coverage won't create false positives. It just means some dynamic patterns may still be flagged.
  • Commit .skylos_trace to reuse trace data in CI without re-running tests.
  • Tests don't need to pass. Tracing records what executes, regardless of pass/fail status.

Filtering

Control what Skylos analyzes and what it ignores.

Inline Suppression

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

Folder Exclusion

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.

Rule Suppression

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)
]

Summary

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 globallyignore = ["SKY-XXX"] in pyproject.toml
Include excluded folder--include-folder NAME
Skip team-specific foldersexclude = ["customenv", ".claude/worktrees"] in pyproject.toml
Run all checks-a or addopts in pyproject.toml
Scan everything--no-default-excludes

Whitelist Configuration

Suppress false positives permanently without inline comments cluttering your code.

CLI Commands

# 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

Inline Ignores

# 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

Config File (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"]

Summary

Want to...Do this
Whitelist one functionskylos whitelist func_name
Whitelist a patternskylos whitelist 'handle_*'
Document whyskylos whitelist x --reason "why"
Temporary whitelistAdd to [tool.skylos.whitelist.temporary] with expires
Per-folder rulesAdd [tool.skylos.overrides."path/*"]
View whitelistskylos whitelist --show
Inline ignore# skylos: ignore or # noqa: skylos
Block ignore# skylos: ignore-start ... # skylos: ignore-end

CLI Options

Main Command Flags

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)

Agent Command Flags

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-files-only analysis for git hooks
  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

AI Defense Command Flags

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

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

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

CLI Output

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.

Interactive Mode

The interactive mode lets you select specific functions and imports to remove:

  1. Select items: Use arrow keys and spacebar to select/unselect
  2. Confirm changes: Review selected items before applying
  3. Auto-cleanup: Files are automatically updated

FAQ

Q: 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 runs the full hybrid pipeline β€” static analysis, judge-all LLM dead-code verification, and LLM security/quality analysis with fix suggestions. Use --no-fixes to skip fix generation. 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. judge_all is the default for agent scan and agent verify; it sends nearly every references == 0 static candidate to the LLM and treats deterministic suppressors as evidence. 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.

Limitations and Troubleshooting

Limitations

  • Dynamic code: getattr(), globals(), runtime imports are hard to detect
  • Frameworks: Django models, Flask, FastAPI routes may appear unused but aren't
  • Test data: Limited scenarios, your mileage may vary
  • False positives: Always manually review before deleting code
  • Secrets PoC: May emit both a provider hit and a generic high-entropy hit for the same token. Supported file types: .py, .pyi, .pyw, .env, .yaml, .yml, .json, .toml, .ini, .cfg, .conf, .ts, .tsx, .js, .jsx, .go
  • Quality limitations: Quality thresholds (complexity, nesting, max_args, max_lines, duplicate_strings) are configurable in pyproject.toml under [tool.skylos].
  • Coverage requires execution: The --trace flag only helps if you have tests or can run your application. Pure static analysis is still available without it.
  • LLM limitations: AI analysis requires API access (cloud) or local setup (Ollama). Results depend on model quality.

Troubleshooting

  1. Permission Errors

    Error: Permission denied when removing function
    

    Check file permissions before running in interactive mode.

  2. Missing Dependencies

    Interactive mode requires 'inquirer' package
    

    Install with: pip install skylos[interactive]

  3. 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
    
  4. Local LLM Connection Refused

    # Verify Ollama is running
    curl http://localhost:11434/v1/models
    
    # Check LM Studio
    curl http://localhost:1234/v1/models
    

Contributing

We welcome contributions! Please read our Contributing Guidelines before submitting pull requests.

Quick Contribution Guide

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Roadmap

  • Expand our test cases
  • Configuration file support
  • Git hooks integration
  • CI/CD integration examples
  • Deployment Gatekeeper
  • Further optimization
  • Add new rules
  • Expanding on the dangerous.py list
  • Porting to uv
  • Small integration with typescript
  • Expanded TypeScript dead code detection (interfaces, enums, type aliases, 95% recall)
  • Expand and improve on capabilities of Skylos in various other languages
  • AI Defense Engine: discover + defend commands with 13 checks, OWASP LLM Top 10 mapping, ops score
  • AI Defense Cloud Dashboard: upload, trend chart, OWASP grid, per-integration cards, dedicated project page
  • AI Defense CI/CD: skylos cicd init --defend, pre-commit hook
  • Expand the providers for LLMs (OpenAI, Anthropic, Ollama, LM Studio, vLLM)
  • Expand the LLM portion for detecting dead/dangerous code (hybrid architecture)
  • Coverage integration for runtime verification
  • Implicit reference detection (f-string patterns, framework decorators)

More stuff coming soon!

License

This project is licensed under the Apache 2.0 License - see the LICENSE file for details.

Contact

<!-- mcp-name: io.github.duriantaco/skylos -->

Global Ranking

-
Trust ScoreMCPHub Index

Based on codebase health & activity.

Manual Config

{ "mcpServers": { "skylos": { "command": "npx", "args": ["skylos"] } } }