Local evidence-backed repository issue triage for overnight code health monitoring
Project description
Local Repo Sentinel
A local, evidence-backed repository issue triage system for overnight code health monitoring.
📖 Documentation Wiki — Installation, configuration, detector reference, and more.
Sentinel runs on your local machine, scans a codebase with deterministic detectors and an optional LLM judgment layer, deduplicates findings across runs, and produces a concise morning report of issues worth reviewing. After explicit approval, selected findings can become GitHub issues.
What it does
- Runs 14 detectors: TODO/FIXME scanner, Python linter (ruff), JS/TS linter (ESLint/Biome), Go linter (golangci-lint), Rust linter (cargo clippy), dependency audit (pip-audit), docs-drift checker, semantic docs-drift (LLM prose vs code comparison), git churn hotspots, cyclomatic complexity, test-code coherence (LLM), stale env config drift, unused dependency detection, dead code / unused exports
- Gathers contextual evidence per finding (surrounding code, git history, related tests, semantic code search via embeddings)
- Uses a pluggable LLM provider as a judgment/summarization layer (Ollama local, Azure, OpenAI-compatible — optional, degrades gracefully)
- Fingerprints and deduplicates findings across runs via SQLite
- Tracks finding persistence across runs (recurring findings get higher visibility)
- Produces a scannable markdown morning report grouped by severity
- Clusters related findings by root-cause pattern and directory to reduce noise
- Finding cluster synthesis: LLM-powered root-cause analysis collapses related findings into actionable items (standard+ capability)
- Supports suppressing false positives and approving findings for GitHub issue creation
- Creates GitHub issues from approved findings (with deduplication and dry-run mode)
- Detector configurability: enable/disable detectors via config, CLI (
--detectors,--skip-detectors), or web UI - Plugin system: third-party detectors installable via
pip install(entry-points discovery, ADR-012) - Capability-tiered detectors: detectors declare minimum model requirements; richer analysis with more powerful models
- Setup flow:
sentinel init --profile minimal|standard|fullguides detector and model selection
What it explicitly does not do
- Implement fixes
- Make architecture plans
- Open pull requests
- Act autonomously on code
Why local
Running locally supports privacy, low marginal cost, offline iteration, and a workflow that fits naturally into personal and client repositories. Client code never leaves your machine.
Status
All MVP success criteria met. 14 detectors (Python, JS/TS, Go, Rust, deps, docs, semantic-drift, test-coherence, git, complexity, dead-code, stale-env, unused-deps) with pluggable model providers (Ollama, OpenAI-compatible, Azure), capability-tiered enhanced analysis, finding cluster synthesis, entry-points plugin system, detector configurability, LLM judge, finding persistence, embedding-based semantic context, GitHub issue creation, web UI triage dashboard, multi-repo scanning, full-pipeline eval with replay, and --json-output for machine-readable output. 1013 tests. See the roadmap for details.
Quick Start
Prerequisites
- Python 3.11+
- Git
- Ollama (optional — for local LLM judgment; Azure/OpenAI providers also supported)
Installation
From PyPI:
pip install repo-sentinel
# Or with all detector tools (ruff, pip-audit):
pip install "repo-sentinel[detectors]"
From source (for development):
git clone https://github.com/jcentner/sentinel.git && cd sentinel
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[detectors]"
The [detectors] extra installs ruff and pip-audit for full detector coverage.
Usage
Initialize a new repo for Sentinel:
sentinel init /path/to/repo # all detectors, basic capability
sentinel init /path/to/repo --profile minimal # heuristic-only, no LLM needed
sentinel init /path/to/repo --profile full # all detectors, enhanced analysis
sentinel init /path/to/repo --list-detectors # show available detectors
This creates a sentinel.toml with documented defaults, a .sentinel/ directory, and adds it to .gitignore.
Scan a repository:
sentinel scan /path/to/repo
This produces a markdown report at /path/to/repo/.sentinel/report-<run-id>.md.
Scan without the LLM judge (if Ollama is not running):
sentinel scan /path/to/repo --skip-judge
Incremental scan (only files with committed changes since the last run):
sentinel scan /path/to/repo --incremental
Scan with semantic context (requires an Ollama embedding model):
sentinel scan /path/to/repo --embed-model nomic-embed-text
Targeted scan (specific files or directories only):
sentinel scan /path/to/repo --target src/auth --target src/api/routes.py
Scan multiple repos into a shared database:
sentinel scan-all ~/project-a ~/project-b ~/project-c --db ~/.sentinel/all.db --skip-judge
All repos go into one database. Use sentinel serve with --db to view them together, or sentinel history --db to list all runs across repos.
Build the embedding index separately (optional — scan auto-builds when --embed-model is used):
sentinel index /path/to/repo
Suppress a false positive:
sentinel suppress <finding-id>
# Or from a different directory:
sentinel suppress <finding-id> --repo /path/to/repo
Approve a finding for issue creation:
sentinel approve <finding-id>
# Or from a different directory:
sentinel approve <finding-id> --repo /path/to/repo
View scan history:
sentinel history
Inspect a specific finding:
sentinel show <finding-id>
Launch the web UI for browser-based review:
pip install "repo-sentinel[web]" # one-time: install web dependencies
sentinel serve /path/to/repo # auto-opens browser
sentinel serve /path/to/repo --port 9000
sentinel serve /path/to/repo --no-open # headless (for scripts/agents)
The web UI runs on http://127.0.0.1:8888 by default. It provides:
- Dark/light mode — "Night Watch" dark-first theme with toggle (persists across sessions)
- Run dashboard — severity stat cards, findings grouped by severity, filter by severity/status/detector
- Bulk triage — checkboxes on findings with per-severity "select all" toggle; batch approve or suppress from a sticky action bar
- Finding detail — full metadata, evidence, inline approve/suppress with optional reason, user notes/annotations
- GitHub Issues page — view approved findings, create GitHub issues or dry-run, config status indicator
- Configurable scan — form-based scan with repo path, model override, embedding model, skip-judge, incremental
- Evaluation page — run detectors against a ground-truth file to measure precision/recall
- Settings page — view active configuration, sentinel.toml status, and GitHub env var status
- Run history — all past scan runs with finding counts and scope badges
- Run comparison — compare two runs to see new, resolved, and persistent findings
- Eval trend chart — server-side SVG chart showing precision/recall trends over time
Check system dependencies:
sentinel doctor
sentinel doctor --json-output # machine-readable
Options
| Option | Description |
|---|---|
--model TEXT |
Model name (default: qwen3.5:4b) |
--provider TEXT |
Model provider: ollama, openai, or azure |
--api-base TEXT |
API base URL for openai/azure providers |
--ollama-url TEXT |
Ollama API URL (default: http://localhost:11434) |
-o, --output TEXT |
Custom report output path |
--skip-judge |
Skip LLM judge, use raw detector findings |
--capability TEXT |
Model capability tier: none, basic, standard, advanced |
--incremental |
Only scan files changed since the last completed run |
--embed-model TEXT |
Embedding model for semantic context (e.g. nomic-embed-text) |
--target, -t TEXT |
Scan only specific paths (repeatable) |
--detectors TEXT |
Comma-separated list of detectors to enable |
--skip-detectors TEXT |
Comma-separated list of detectors to skip |
-q, --quiet |
Suppress all output except errors |
--json-output |
Output results as JSON (machine-readable) |
--db TEXT |
Custom database path |
Note:
-v, --verboseand-q, --quietare global flags placed before the subcommand:sentinel -v scan /path/to/repo. They are mutually exclusive.
Evaluate detector accuracy against ground truth:
sentinel eval /path/to/repo
sentinel eval /path/to/repo --ground-truth /path/to/ground-truth.toml
Create GitHub issues from approved findings:
# Set credentials
export SENTINEL_GITHUB_OWNER=your-org
export SENTINEL_GITHUB_REPO=your-repo
export SENTINEL_GITHUB_TOKEN=ghp_...
# Preview what would be created
sentinel create-issues --dry-run
# Create issues
sentinel create-issues
Machine-Readable Output
Most commands support --json-output for use by AI agents and scripts:
# JSON scan results (findings + run metadata)
sentinel scan /path/to/repo --skip-judge --json-output
# JSON finding detail
sentinel show 42 --json-output
# JSON run history
sentinel history --json-output
# JSON eval metrics
sentinel eval /path/to/repo --json-output
# JSON issue creation results
sentinel create-issues --dry-run --json-output
# JSON triage actions
sentinel suppress 42 -r "False positive" --json-output
sentinel approve 42 --json-output
Exit codes: 0 = success, 1 = error, 2 = partial failure (scan-all).
Configuration
Create a sentinel.toml in your repo root to customize behavior:
[sentinel]
provider = "ollama" # "ollama", "openai", or "azure"
model = "qwen3.5:4b" # Model name
ollama_url = "http://localhost:11434"
skip_judge = false # true to skip LLM judgment
model_capability = "basic" # none, basic, standard, advanced
db_path = ".sentinel/sentinel.db"
output_dir = ".sentinel"
embed_model = "" # Embedding model (e.g. "nomic-embed-text")
detectors_dir = "" # Custom detector .py files directory
# num_ctx = 2048 # LLM context window size
# For OpenAI-compatible providers (Azure OpenAI, OpenAI, vLLM, LM Studio):
# provider = "openai"
# api_base = "https://api.openai.com"
# api_key_env = "OPENAI_API_KEY" # env var containing the API key
# Detector selection (optional — defaults to all):
# enabled_detectors = ["todo-scanner", "lint-runner", "docs-drift"]
# disabled_detectors = ["dead-code"]
Custom Detectors
Add your own detectors by creating Python files in a directory and pointing detectors_dir at it:
# my_detectors/license_check.py
from sentinel.detectors.base import Detector
from sentinel.models import DetectorContext, DetectorTier, Finding, Severity
class LicenseDetector(Detector):
@property
def name(self): return "license-check"
@property
def description(self): return "Check for LICENSE file"
@property
def tier(self): return DetectorTier.DETERMINISTIC
@property
def categories(self): return ["compliance"]
def detect(self, context):
from pathlib import Path
if not (Path(context.repo_root) / "LICENSE").exists():
return [Finding(
detector=self.name, category="compliance",
title="Missing LICENSE file", severity=Severity.MEDIUM,
confidence=1.0, description="No LICENSE file found in repo root.",
evidence=[],
)]
return []
# sentinel.toml
[sentinel]
detectors_dir = "my_detectors"
Scheduling overnight runs
Sentinel does not include a built-in scheduler. Use your system's scheduling tools to run scans overnight.
Linux/WSL (cron):
# Edit your crontab
crontab -e
# Run a full scan at 2 AM every night
0 2 * * * /path/to/sentinel/.venv/bin/sentinel scan /path/to/repo --skip-judge >> /tmp/sentinel.log 2>&1
# Or with the LLM judge (requires Ollama running)
0 2 * * * /path/to/sentinel/.venv/bin/sentinel scan /path/to/repo >> /tmp/sentinel.log 2>&1
Linux/WSL (systemd timer):
# Create ~/.config/systemd/user/sentinel-scan.service
[Unit]
Description=Sentinel overnight scan
[Service]
Type=oneshot
ExecStart=/path/to/sentinel/.venv/bin/sentinel scan /path/to/repo
# Create ~/.config/systemd/user/sentinel-scan.timer
[Unit]
Description=Run Sentinel scan nightly
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
systemctl --user enable --now sentinel-scan.timer
The morning report will be at /path/to/repo/.sentinel/report-<run-id>.md.
Development
pip install -e ".[dev]"
pytest # run tests
ruff check src/ tests/ # lint
mypy src/sentinel/ # type check
Documentation
| Area | Location | Purpose |
|---|---|---|
| Vision & Strategy | docs/vision/ | High-level goals, positioning, what we're building and why |
| Architecture | docs/architecture/ | Technical design, detector interface, system overview |
| Architecture Decisions | docs/architecture/decisions/ | ADRs — recorded design choices with context and rationale |
| Reference | docs/reference/ | Open questions, tech debt tracker, glossary, test repos |
| Analysis | docs/analysis/ | Competitive landscape, critical review of the design |
| Roadmap | roadmap/ | Phased development plan |
| AI Setup Skill | .github/skills/setup-sentinel/ | Copilot skill for AI-assisted setup and configuration |
AI Agent Integration
Sentinel is designed to be used by AI agents and development tools:
--json-outputon all CLI commands for machine-readable output- Predictable exit codes: 0 = success, 1 = error, 2 = partial failure
- Copilot skill:
.github/skills/setup-sentinel/SKILL.mdprovides step-by-step setup instructions that GitHub Copilot (and other AI agents) can follow - Quiet mode:
-qsuppresses all log output for clean piping - Config-driven:
sentinel.tomlis fully declarative — agents can generate it
Contributing
See CONTRIBUTING.md for development setup, project structure, and conventions.
License
MIT
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 repo_sentinel-0.1.0.tar.gz.
File metadata
- Download URL: repo_sentinel-0.1.0.tar.gz
- Upload date:
- Size: 207.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b5c65b9c9ca8d887303da54cac81d2019ae0c9e419acc4cf65f0716d207d2921
|
|
| MD5 |
d3838a9a85856d3e3385d46ebf24ff24
|
|
| BLAKE2b-256 |
deef136343bec4442ed5ff54269881729845c1ac2b2332095264054b7d0ade64
|
Provenance
The following attestation bundles were made for repo_sentinel-0.1.0.tar.gz:
Publisher:
release.yml on jcentner/sentinel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
repo_sentinel-0.1.0.tar.gz -
Subject digest:
b5c65b9c9ca8d887303da54cac81d2019ae0c9e419acc4cf65f0716d207d2921 - Sigstore transparency entry: 1275963765
- Sigstore integration time:
-
Permalink:
jcentner/sentinel@feb0cd7a799594d8bdf0b6b5e59deb3e0a4c579c -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/jcentner
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@feb0cd7a799594d8bdf0b6b5e59deb3e0a4c579c -
Trigger Event:
push
-
Statement type:
File details
Details for the file repo_sentinel-0.1.0-py3-none-any.whl.
File metadata
- Download URL: repo_sentinel-0.1.0-py3-none-any.whl
- Upload date:
- Size: 193.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
07697713a184ad7bdbbd1f775189a3d1761f46fe940285ee8536bf066cde9271
|
|
| MD5 |
445735578041150cceb18d997ae055c5
|
|
| BLAKE2b-256 |
33850eb219250caa3c5ef2bbb0c7a26b1709d599a9530542a0bd64abafda0527
|
Provenance
The following attestation bundles were made for repo_sentinel-0.1.0-py3-none-any.whl:
Publisher:
release.yml on jcentner/sentinel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
repo_sentinel-0.1.0-py3-none-any.whl -
Subject digest:
07697713a184ad7bdbbd1f775189a3d1761f46fe940285ee8536bf066cde9271 - Sigstore transparency entry: 1275963773
- Sigstore integration time:
-
Permalink:
jcentner/sentinel@feb0cd7a799594d8bdf0b6b5e59deb3e0a4c579c -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/jcentner
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@feb0cd7a799594d8bdf0b6b5e59deb3e0a4c579c -
Trigger Event:
push
-
Statement type: