MissCat
Never miss a single commit.
I’ll catch ’em all, meow.
MissCat is a small CLI that watches a GitHub repository and automatically runs an AI code review whenever a new pull request or commit appears.
It watches the repository, not a single PR.
Why MissCat?
AI reviewers are useful, but somebody still has to notice that a PR changed and ask them to review it again.
MissCat does that part.
new PR
→ review
new commit
→ review again
nothing changed
→ do nothing
No webhook server.
No API keys stored by MissCat.
No repeated review of the same commit with the same profile.
Prerequisites
Before running MissCat, ensure you have:
- Python: Python 3.9 or newer
- Git: Configured with credentials to clone and fetch the target repository (HTTPS users using GitHub CLI can run
gh auth setup-git) - GitHub CLI (
gh): Installed and authenticated (gh auth login) - At least one AI reviewer CLI: Installed and authenticated:
- Claude Code (
claude) - Codex CLI (
codex) - Antigravity CLI (
agy) for Gemini
- Claude Code (
Installation
Install MissCat with pipx:
pipx install misscat
To upgrade MissCat to the latest version:
pipx upgrade misscat --pip-args="--no-cache-dir"
Releasing (maintainers)
Releases are published to PyPI by .github/workflows/publish.yml when a vMAJOR.MINOR.PATCH tag is pushed. It uses PyPI Trusted Publishing (OIDC), so no PyPI token is stored in GitHub secrets.
One-time setup
- On PyPI, add a trusted publisher for the
misscatproject (Project → Publishing; for a first release use "Add a pending publisher"):- Owner:
genonfire - Repository:
misscat - Workflow name:
publish.yml - Environment name:
pypi
- Owner:
- In the GitHub repository, create an environment named
pypi(Settings → Environments). Recommended: add required reviewers so every release needs a manual approval, and add a tag ruleset (Settings → Rules) so only maintainers can createv*tags. A deployment tag rule alone is not enough, since it matches the tag name, not the commit.
Release procedure
-
Check that the version is not already on PyPI (versions are immutable and cannot be re-uploaded): https://pypi.org/project/misscat/#history
-
Bump
versioninpyproject.tomland__version__insrc/misscat/__init__.py(the workflow fails if they differ), then merge tomaster. -
Tag the merge commit and push the tag (the tag must equal
v+ thepyproject.tomlversion):git tag v1.0.1 git push origin v1.0.1
-
The workflow fails before publishing if the tagged commit is not on
master, the tag is malformed, the tag does not matchpyproject.tomlor__version__, orpython -m build/twine checkfails. -
Confirm the release:
pipx upgrade misscat misscat --version pip index versions misscat
Quick Start / First Run
On the first normal run, if ~/.config/misscat/ does not exist, MissCat creates it and copies the bundled reviewer profiles (luna.yml, sol.yml, sonnet.yml, opus.yml, gemini.yml) into it before loading your profile. Edit them freely. If the directory already exists, nothing is copied automatically, so existing configs are preserved across upgrades. --help and --version never touch the filesystem.
Watch a repository and review new PRs using the default profile:
misscat <owner/repo>
Example:
misscat genonfire/misscat
Use a specific reviewer profile:
misscat genonfire/misscat sol
MissCat resolves sol as:
~/.config/misscat/sol.yml
Running misscat with no arguments (or misscat --help) shows usage help:
misscat --help
Repository names are canonicalized to lowercase, so Genonfire/MissCat and genonfire/misscat refer to the same local workspace and state.
Using . for the current repository
. always means the GitHub repository of the current Git checkout's origin remote; upstream and other remotes are never used.
misscat . # watch origin's repository with the default profile
misscat . luna # same repository, luna profile
misscat state . # manage that repository's review state
MissCat prints resolved . -> owner/repo and then behaves exactly as if you had typed that name (same state, lock and workspace). It works from subdirectories and worktrees, and understands git@github.com:owner/repo.git, https://github.com/owner/repo(.git) and ssh://git@github.com/owner/repo(.git). It fails, before touching any state, when outside a Git repository, when origin is missing, or when origin is not on github.com (SSH host aliases are not interpreted).
Bundled profiles: misscat init
misscat init # create the config dir if needed; copy only missing bundled profiles
misscat init --force # overwrite files named like bundled profiles with fresh copies
initnever modifies existing files.init --forcereplaces onlyluna.yml,sol.yml,sonnet.yml,opus.ymlandgemini.yml; custom profiles, per-repository state JSONs and the review cache are never touched.- After upgrading MissCat, new or updated bundled profiles are not applied automatically: run
misscat initfor new ones, ormisscat init --forceto reset the bundled ones (this discards your edits to them). - If the automatic first-run copy is interrupted,
misscat initrestores the missing profiles. default.ymlis the internal base configuration and is not copied. Each bundled profile carries its own editable copy of the defaultprompt, so you can customize a reviewer's prompt in place;default.ymlremains the fallback.
Review state manager
Stop the watcher before opening the terminal state manager:
misscat state genonfire/typewriter
The list shows the most recently completed reviews first, across all PRs and profiles (sorted by reviewed_at; the stored State order is unchanged). Use ↑/↓ to select any completed review, Enter for its full HEAD SHA, profile, and local review completion time, and Esc/Q to exit. Delete or Backspace only operates on the most recently reviewed entry for that PR and profile (by reviewed_at), with a [y/N] prompt. Older entries are read-only until newer entries in that PR/profile group have been removed. Nothing is deleted on Enter or when you decline confirmation.
The manager works from local State JSON; it does not call GitHub. Removing a record makes that PR/HEAD/profile combination eligible for review again, but only when the HEAD is the PR's current open HEAD at the next watcher run. Removing historical HEADs does not trigger review of historical commits.
State v2 and process safety
- Every successful review now stores
reviewed_atas an ISO 8601 UTC timestamp. Display uses local time. Existing PR/HEAD/profile identity and the JSON sorting convention remain unchanged. - Breaking state reset: stop all previously running MissCat watchers before upgrading to this version; older watchers do not participate in the new OS locking scheme and may restore v1 records from memory. On first State access after upgrade, any v1 reviewed history is intentionally discarded and the file is rewritten as an empty v2 State. Consequently, open PR HEADs previously marked complete become eligible for a new AI review. This may consume additional reviewer tokens. Unknown versions and corrupt State files cause errors instead of being discarded.
- A per-repository OS file lock prevents a watcher and a state manager—or two watchers—from running on the same repository simultaneously, even with different profiles. Stop the watcher before editing state. The lock file lives under
~/.cache/misscat/locks/(never in the config directory) and may remain on disk after exit; the OS releases its lock when the process exits. Do not use the file's existence as an indication of an active watcher. - State manager needs an interactive TTY. It does not touch profile YAML files or cache workspaces.
Configuration
MissCat ships with a bundled default.yml inside the package, so installed copies do not depend on the source checkout or current working directory.
User configuration and persistent data are kept entirely outside the package:
- Profiles:
~/.config/misscat/<profile>.yml - Per-repository state:
~/.config/misscat/<owner>__<repo>.json - Persistent workspace:
~/.cache/misscat/repos/<owner>/<repo>/
Directory layout:
~/.config/misscat/
├── sol.yml
├── sonnet.yml
├── opus.yml
├── genonfire__typewriter.json
└── genonfire__misscat.json
Example default.yml:
reviewer:
# provider: claude
# model: claude-sonnet-5-5
# args:
# - --allowedTools
# - "Bash(gh *)"
provider: codex
model: gpt-6-luna
args:
- --sandbox
- workspace-write
- -c
- sandbox_workspace_write.network_access=true
- -c
- apps._default.enabled=false
- -c
- model_reasoning_effort=max
prompt: |
Read REVIEW.md if exist and act as the 1st reviewer.
Use the gh CLI for GitHub operations, including posting the review.
watch:
idle: [60, 120, 180, 240, 300]
active: [300, 240, 180, 120, 60]
review:
include_drafts: true
Example ~/.config/misscat/sol.yml:
reviewer:
provider: codex
model: gpt-6.1-sol
args:
- --sandbox
- workspace-write
- -c
- sandbox_workspace_write.network_access=true
- -c
- apps._default.enabled=false
- -c
- model_reasoning_effort=medium
The selected local profile overrides the bundled defaults.
When creating a local profile, use default.yml as the reference and specify provider, model, and args explicitly for that reviewer CLI. Do not rely on args inherited from a different provider.
The args field under reviewer is passed to the selected reviewer CLI unchanged.
Reviewed state is stored per repository and keyed by PR, HEAD SHA, and profile. The same HEAD can therefore be reviewed again with a different profile.
Gemini (Antigravity CLI)
Allow gh and git in ~/.gemini/antigravity-cli/settings.json (keep your other settings):
{
"toolPermission": "proceed-in-sandbox",
"enableTerminalSandbox": true,
"permissions": { "allow": ["command(gh)", "command(git)"] }
}
reviewer:
provider: gemini
model: gemini-3.8-flash-low
args: [--print-timeout, 30m]
In headless mode a denied permission can end the run without posting a review; use --loud to inspect.
BadCat
pipx install misscat also installs badcat, a deterministic watcher of submitted PR reviews (no AI). By default it only reports; it merges (squash) only with --merge.
badcat owner/repo # report only, no GitHub writes
badcat . --merge # merge after +1 then +2 (batch-labeled PRs at +1)
badcat . --merge=1 # merge after +1
badcat . --merge --trusted-reviewer luna # trust reviews by this login (default: your gh user)
A review counts only if it is a submitted COMMENTED review by a trusted login whose body starts with:
+1
HEAD: <40-hex sha of the PR's current HEAD>
<review evidence>
What MissCat does
- Watches all open PRs in a repository
- Discovers PRs created after MissCat starts
- Detects changes by PR HEAD SHA
- Reviews every new PR HEAD once per profile
- Avoids duplicate reviews
- Remembers reviewed commits across restarts
- Uses an isolated persistent review workspace
- Supports multiple reviewer backends
MissCat does not modify code, push commits, or merge PRs.
It watches. It catches. It reviews.
(BadCat is the separate, opt-in merge operator described above.)
Metadata
Release files for misscat 1.4.10
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 | |
|---|---|---|---|---|
| misscat-1.4.10.tar.gz | 82.3 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| misscat-1.4.10-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 132.8 kB
Release files / misscat-1.4.10.tar.gz
| Download URL | misscat-1.4.10.tar.gz |
|---|---|
| Size | 82.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0d63d9cbcedbe8c3a2969c1186686d64fe25423012be23921549ae8a4c68cf35
|
|
BLAKE2b-256 checksum How to use checksums |
4ec28f90f4aae8d59d68c5a7179eabe80ee941b2a9835f242abba5106b7d69b6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency logRelease files / misscat-1.4.10-py3-none-any.whl
| Download URL | misscat-1.4.10-py3-none-any.whl |
|---|---|
| Size | 50.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
aa34e3d6114af5bbbf187fe511a3db58cc68fe86333bb8ebb9a65b8d44a34921
|
|
BLAKE2b-256 checksum How to use checksums |
c68f740595963af4031cad65285bdeab0cef18c700b11c97dd3f16fb0a810e26
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency logSecurity for misscat 1.4.10
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for misscat-1.4.10.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency logA 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.