Detect AI-generated / synthetic code patterns.
Project description
🔍 SynthScan
Detect AI-generated (synthetic) code patterns in your repository. Available as a powerful CLI tool (via npm or pip) and as a fully automated GitHub Action.
160+ detection patterns across 23 categories · Context-aware severity weighting · Normalised per 1 000 LOC · AST structural analysis · Fuzzy cross-file repetition · Diff/PR mode · SARIF output · Editable Markdown pattern file.
🚀 Features
- Multi-Platform: Run it anywhere using
npm,pip, or GitHub Actions. - Diff / PR mode: Pass a unified diff file — only added lines are scored. Score normalises against diff LOC for a clean "how synthetic is this PR" metric.
- Beautiful Output: Colorful terminal outputs, with a
--format jsonflag to pipe results natively into tools likejq. - SARIF output: Emit
synthscan-report.sariffor GitHub Code Scanning integration — inline annotations appear directly on PR diffs. - Advanced Context: Detects AI-structured docstrings, over-commented blocks, unreachable AST code, over-parameterised functions, and fuzzy cross-file repetitions.
- Easily Configurable: Gitignore-style
.synthscanignoreto exclude paths. Patterns are fully editable in plain Markdown!
📦 Installation
SynthScan is built in Python but packaged for both the Node and Python ecosystems so you can use the tools you already have.
Via NPM (Node.js)
npm install -g synthscan
Via PIP (Python)
pip install synthscan
Via GitHub Actions
Add this workflow to any repo at .github/workflows/synthscan.yml:
name: SynthScan
on: [pull_request, workflow_dispatch]
permissions:
contents: read
issues: write
jobs:
synthscan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: marcoramilli/SynthScan@v2
with:
scan_path: '.'
create_issue: "true"
💻 CLI Usage
Once installed globally, you can run synthscan from your terminal!
# Scan the current directory
synthscan
# Scan a specific directory
synthscan --scan-path ./src
# Fail if the score goes above 30 (useful in CI)
synthscan --score-threshold 30
# Output raw JSON to stdout (great for jq / piping)
synthscan --format json | jq '.synthetic_code_score'
CLI Arguments
| Argument | Default | Description |
|---|---|---|
--scan-path |
. |
Directory to scan. |
--patterns-file |
patterns/synthetic_patterns.md |
Path to the Markdown detection patterns. |
--score-threshold |
0 |
Fail with exit code 1 if the score ≥ this value. |
--format |
text |
Output format: text (beautiful colored UI) or json. |
--diff-file |
"" |
Path to a unified diff file. Only added lines are scored. |
--ignore-file |
.synthscanignore |
Gitignore-style paths to exclude from scanning. |
--report-path |
synthscan-report.json |
File path to write the JSON artefact. |
--sarif-output |
false |
Set to true to emit a SARIF 2.1.0 report. |
🧠 How It Works
-
Patterns are defined in a human-readable Markdown file (patterns/synthetic_patterns.md).
Each pattern is either a plain-text substring (case-insensitive) or a Python regex (prefixed withregex:). Patterns carry a severity (CRITICAL, HIGH, MEDIUM, LOW). -
The scanner walks every source file and applies five layers of detection:
- Line-level matching (Context-aware: comments score higher, strings lower).
- Multi-line block detection (e.g. Try/Catch wrapping, AI-structured docstrings).
- AST structural analysis (Python only: detects unreachable code, result-variable anti-patterns).
- Fuzzy cross-file repetition (Catches repetitive AI scaffolding across multiple files).
- Inline suppression (Lines with
# synthscan: ignoreare skipped).
-
Scores are refined by post-processing passes (clustering bonuses and diminishing returns per file).
Synthetic Code Score
The headline metric is score per 1 000 lines of code:
Synthetic Code Score = (Raw Score / Lines Scanned) * 1000
This normalisation prevents large codebases from naturally accumulating higher scores than small ones.
Reference ranges (from benchmark testing):
- 0 – 5: Likely human-written
- 5 – 15: Low AI signal — review flagged lines
- 15 – 30: Moderate AI signal
- 30+: Strong AI signal
🤝 Contributing
Contributions, issues and feature requests are welcome!
Feel free to check issues page.
If you find a new hallucination word or LLM tell, simply PR an addition to the patterns/synthetic_patterns.md file!
📝 License
Copyright © Marco Ramilli.
This project is MIT licensed.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file synthscan-2.0.1.tar.gz.
File metadata
- Download URL: synthscan-2.0.1.tar.gz
- Upload date:
- Size: 25.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60d7b039b3b47994a589bba6f13e85341db292a94f0600ef26fa322be9bc95ea
|
|
| MD5 |
ba7ff480c3b247f1ad4ffc8db2e1b401
|
|
| BLAKE2b-256 |
d56b44291a29de8982369c1be07330a72f0282e327202e75a051ad0c1e69cfe3
|
File details
Details for the file synthscan-2.0.1-py3-none-any.whl.
File metadata
- Download URL: synthscan-2.0.1-py3-none-any.whl
- Upload date:
- Size: 24.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e69a2216688d72c9e9241de1100ca563cc6e16c19b962a5fe50519c8acd7a828
|
|
| MD5 |
3536eb2d7edd1dac5e2f534f130e8001
|
|
| BLAKE2b-256 |
3344b6150a8690fb0294974e44ad52d86a289edaf29ef37de999728bac8fde3c
|