Skip to main content

MissCat

PyPI PyPI Downloads

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:

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

  1. On PyPI, add a trusted publisher for the misscat project (Project → Publishing; for a first release use "Add a pending publisher"):
    • Owner: genonfire
    • Repository: misscat
    • Workflow name: publish.yml
    • Environment name: pypi
  2. 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 create v* tags. A deployment tag rule alone is not enough, since it matches the tag name, not the commit.

Release procedure

  1. Check that the version is not already on PyPI (versions are immutable and cannot be re-uploaded): https://pypi.org/project/misscat/#history

  2. Bump version in pyproject.toml and __version__ in src/misscat/__init__.py (the workflow fails if they differ), then merge to master.

  3. Tag the merge commit and push the tag (the tag must equal v + the pyproject.toml version):

    git tag v1.0.1
    git push origin v1.0.1
    
  4. The workflow fails before publishing if the tagged commit is not on master, the tag is malformed, the tag does not match pyproject.toml or __version__, or python -m build / twine check fails.

  5. 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
  • init never modifies existing files. init --force replaces only luna.yml, sol.yml, sonnet.yml, opus.yml and gemini.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 init for new ones, or misscat init --force to reset the bundled ones (this discards your edits to them).
  • If the automatic first-run copy is interrupted, misscat init restores the missing profiles.
  • default.yml is the internal base configuration and is not copied. Each bundled profile carries its own editable copy of the default prompt, so you can customize a reviewer's prompt in place; default.yml remains 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_at as 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 PR labeled exactly no-review is never eligible: BadCat reports no "ready to merge" for it and never merges it, whatever its reviews or batch/urgent labels. Labels are re-read from GitHub just before a merge, so adding no-review after the PR was listed still blocks the merge; removing the label restores eligibility. The read-only badcat-host Chrome host likewise emits no +1 event for a no-review PR (like a draft), and emits one again once the label is removed. MissCat skips no-review PRs too.

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

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for misscat 1.4.11
File Size Uploaded Provenance
misscat-1.4.11.tar.gz 83.5 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for misscat 1.4.11
File Interpreter ABI Platform
misscat-1.4.11-py3-none-any.whl Python 3 none any Details

Total release size: 134.5 kB

Release files / misscat-1.4.11.tar.gz

Download URL misscat-1.4.11.tar.gz
Size 83.5 kB
Tags Source
SHA-256 checksum
How to use checksums
9ca6dd32ed6479d0b988927049d0f17a6e0c773d0d803fdec362465b949cf881
BLAKE2b-256 checksum
How to use checksums
5ea7f0a88904330082d0442ea6697e75095a2f30378ba9880e9c4995e464a23e
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 15, 2026.

Transparency log

Release files / misscat-1.4.11-py3-none-any.whl

Download URL misscat-1.4.11-py3-none-any.whl
Size 50.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a3c4aa1f85548ac9eb3747e6f7af5872c4702f0bd14aadc2d341f46c87e20979
BLAKE2b-256 checksum
How to use checksums
9c3e4b1fa06ab23bc9d8a030ee525f59ce0c7fc29d08769cf56a98c15bd1a61f
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 15, 2026.

Transparency log

Security for misscat 1.4.11

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.

Select from 2 files

Attestation for misscat-1.4.11.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 15, 2026.

Transparency log

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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page