██████ ████████ ████████ ██ ██ ██████████ ██ ██████ ██████ ████████
██ ██ ████████ ████████ ██ ██ █ ████████ ██ ██████ ██████ █ ██████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██████ ██████ ██ ██ █ ██████ ██ ██ ██ ██ ██ █ ██████
██ ██████ ██████ ██ ██ █ ██████ ██ ██ ██ ██ ██ █ ██████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ████████ ████████ ██████ █ ████████ █ ████████ ██████ ██████ ██
██████ ████████ ████████ ██████ ██████████ ██████████ ██████ ██████ ██
docs == docs
IssueLoop
Finds failing tests (batch or live). Splits failures into independent tickets via an LLM, with automatic priority fallback across providers. Stores them. Hands them out one at a time. Optionally proposes and applies fixes under an explicit, auditable permission allowlist.
Why this exists
Bugs sit unfixed because someone has to notice them first. IssueLoop reads the test suite directly — a failing test is the bug report. It can also watch a live-running process or log file, for bugs that only show up outside a test run. No human has to spot it, and no human has to manually triage which failures are actually distinct problems.
Install
pip install issueloop
Or for local development:
git clone https://github.com/onenot8/issueLoop
cd issueLoop
pip install -e ".[dev]"
Local SQLite backend, zero setup. No database account, no API key required to try it.
Quickstart
import issueloop
issueloop.use(
database="local", # or "supabase" for a hosted backup
llm={"providers": [ # priority list — falls back automatically
{"provider": "anthropic", "model": "claude-sonnet-4-6", "api_key": "sk-..."},
{"provider": "ollama", "model": "qwen2.5-coder:7b"}, # free, local, default if omitted
]},
notify={"webhook": "https://your-endpoint"}, # fires if IssueLoop itself breaks
)
issueloop.scan_repo("repos/myrepo")
issueloop.run_tests("myrepo")
issueloop.create_tickets("myrepo")
ticket = issueloop.get_top_error("myrepo") # claims one, marks in_progress
issueloop.resolve(ticket["id"]) # your side fixed it
Live monitoring
Watch a process IssueLoop spawns itself, or tail a log file an already-running process writes to:
from issueloop import watch_process, watch_log_file, stop_watch
handle = watch_process("myrepo", "python3 main.py", cwd="/path/to/repo", debounce_seconds=3.0)
# ...
stop_watch(handle)
Or from the CLI, in the foreground:
issueloop watch myrepo --command "python3 main.py" --cwd /path/to/repo --debounce 3.0
issueloop watch myrepo --log-file /path/to/app.log --debounce 3.0
Errors are debounced (default 3s of quiet) so a single multi-line
traceback becomes one ticket, not dozens. Detected errors land in the
same log format the batch test runner uses — issueloop tickets picks
them up with no extra steps.
Fix-apply layer
Off by default — every command needs an explicit allowlist entry:
# config/permission.yaml
per_repo:
myrepo:
allowed_patterns:
- 'sed -i .* somefile\.py'
issueloop.propose_fix(ticket_id, "sed -i 's/old/new/' somefile.py")
result = issueloop.apply_fix(ticket_id)
# {"status": "resolved" | "retry" | "escalated" | "denied", ...}
apply_fix re-runs the ticket's associated test after applying the
fix and only resolves it if the test actually passes. Failures retry
up to a configurable limit, then escalate to needs_human. Every
permission decision is audited — issueloop.get_permission_audit_log().
Use it from the CLI
issueloop check-env
issueloop scan repos/myrepo
issueloop test myrepo
issueloop tickets myrepo
issueloop next myrepo
issueloop watch myrepo --command "..." | --log-file ...
issueloop cleanup --days 30
Use it from Node, Go, or anything else
issueloop serve --port 8787
const r = await fetch("http://localhost:8787/errors/top?repo=myrepo");
const ticket = await r.json();
serve is localhost-only, no auth — put it behind your own gateway if
you ever expose it further.
Full API
Bug query — get_all_bugs, get_unresolved_bugs, get_resolved_bugs,
get_failed_bugs, get_bugs_needing_human, get_bugs_by_status,
get_bugs_by_priority, get_bug, get_bug_count,
get_bug_count_by_status, search_bugs, get_oldest_bug, get_newest_bug
Lifecycle — get_top_error, get_all_errors, resolve, fail,
escalate, reassign, retry_bug, get_bug_attempts, bulk_resolve
Fix-apply — propose_fix, apply_fix, check_permission,
get_permission_audit_log
Scan / test — scan_repo, get_file_inventory, run_tests,
run_single_test, create_tickets
Live monitoring — watch_process, watch_log_file, stop_watch,
list_active_watchers
LLM / tokens — get_token_consumption,
get_token_consumption_by_provider, get_llm_call_history,
get_llm_provider_status
Database — cleanup, purge_repo, get_database_stats,
export_bugs, reap_stale_bugs, rotate_logs
Notifications — get_crash_log, get_notification_config
Config / utility — use, get_config, list_repos, health_check
Config — every option
| Field | Values | Default |
|---|---|---|
database |
"local", "supabase" |
"local" |
database_path |
any path | data/issueloop.db |
retention_days |
int | 30 |
llm.providers |
list of provider dicts, priority order | single ollama default |
llm.provider |
"ollama", "anthropic", "openai" |
"ollama" |
llm.apiKey |
string | none (required for anthropic/openai) |
llm.tokenSize |
int | 1024 |
notify.webhook |
URL | none |
Both apiKey/api_key and tokenSize/token_size work — camelCase
or snake_case, your call. A single llm={...} dict still works exactly
as before; wrap multiple in llm={"providers": [...]} for automatic
fallback.
Config files (config/permission.yaml, config/provider_config.yaml)
resolve in this order: an explicit env var
(ISSUELOOP_PERMISSION_PATH / ISSUELOOP_PROVIDER_CONFIG_PATH) →
./config/ relative to your current directory → config/ in the
IssueLoop checkout → bundled package defaults. This means a real
pip install (not just -e .) still works correctly even without a
project-local config/ directory.
Why trust this
- Every module has a test, and the tests run against real behavior — a real SQLite file, a real HTTP server on a real socket — not just mocks pretending things work.
- Real bugs were caught and fixed by actually running this code, not
by reading it — including a prompt that only ever sent
stderrto the LLM triage step while pytest (and most test runners) report failures onstdout, and a config-bundling gap that made the permission system silently deny everything under a real (non-editable) install. Both have regression coverage now. - Local-first by default. Nothing phones home, nothing requires an account to try.
- Read the code — it's small enough to actually read. That's on
purpose. File-by-file build order:
BUILD_ORDER.md.
Testing
pytest tests/ -v
Full walkthrough, including live monitoring and the fix-apply layer: TESTING.md.
License
MIT — see LICENSE.
Metadata
Release files for issueloop 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| issueloop-0.3.1.tar.gz | 29.8 kB | Without provenance | Details for issueloop-0.3.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| issueloop-0.3.1-py3-none-any.whl | Python 3 | none | any | Details for issueloop-0.3.1-py3-none-any.whl |
Total release size: 61.6 kB
Release files / issueloop-0.3.1.tar.gz
| Download URL | issueloop-0.3.1.tar.gz |
|---|---|
| Size | 29.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
06c1bfd047b8a866033d13e675af0998ed23c0635e6227e489fd76ef18cc2f4a
|
|
BLAKE2b-256 checksum How to use checksums |
c976926f76ef9ee5e1e79c4adf6e01a41a15349809222d4005cbbefed5cc5a47
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / issueloop-0.3.1-py3-none-any.whl
| Download URL | issueloop-0.3.1-py3-none-any.whl |
|---|---|
| Size | 31.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4b5993559ee5166f95307dab9604e9ef33560e25cc1bb2919442ec488b900dbf
|
|
BLAKE2b-256 checksum How to use checksums |
6bcb45e09180d4a0e14a409aa1442d434cd18562934bbef6cc5af798efa0c7f9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Security for issueloop 0.3.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.