Skip to main content

pulci

Continuous quality gate daemon for agent-driven Python development.

CI License Python 3.10+ Rust

v0.0.6 — Apache-2.0 — docs/AGENTS.md

Why

When AI coding agents (Claude Code, Cursor, Codex) iterate on a Python project, they invoke ruff check, ty check, pytest over and over. Pre-commit hooks run at commit time. CI runs even later. Nothing in the existing tooling stack was designed for the loop an agent actually runs: edit → check → edit, dozens to hundreds of times per hour.

pulci is a small daemon — Rust core, Python CLI — that runs your configured quality gates continuously as files change, and exposes the aggregated state as structured JSON. Agents stop re-invoking tools; they query state.

Comparison

Tool When it runs Output format Built for
pre-commit Commit time Human terminal Humans
prek Commit time (fast) Human terminal Humans
MegaLinter CI time Reports CI/CD
pytest-watch File change Human terminal Humans
pulci Iteration time Compiler-style + JSON Agents

pulci does not replace any of these. It fills the empty quadrant.

Demo

pulci watching a project for changes

MCP server

pulci ships an MCP server for Claude Desktop, Cursor, and any MCP-compatible host.

Auto-install (recommended) — writes the entry into the host's config file directly, preserving any other MCP servers you have:

pulci mcp install claude-desktop      # ~/Library/Application Support/Claude/...
pulci mcp install cursor              # .cursor/mcp.json (project-local)
pulci mcp install cursor --global     # ~/.cursor/mcp.json (all projects)
pulci mcp install cursor --dry-run    # preview without writing

For Claude Code, use its native claude mcp add pulci $(which pulci) mcp instead.

Manual setup — pulci mcp info prints the JSON block you can paste yourself:

{
  "mcpServers": {
    "pulci": {
      "command": "/path/to/.venv/bin/pulci",
      "args": ["mcp"]
    }
  }
}

Once configured, the host exposes the pulci_status tool — agents call it instead of invoking ruff/ty/pytest directly.

Real example: what the agent loop looks like

I built pulci because I wanted my own Claude Code sessions to stop wasting tokens re-invoking ruff after every edit. Here is the actual loop:

Before pulci — each edit pays the cold start of every tool, the agent re-parses the output, and the diagnostic stream grows linearly with violation count:

agent: edit src/api.py
agent: $ ruff check src/api.py
        ... 2391 tokens of JSON ...
agent: $ ty check src/api.py
        ... more tokens ...
agent: parse, decide next action

With pulci — the daemon already ran the checks in the background. The agent reads state_version before the edit, waits for it to advance after, then reads a single structured response:

# 1. Capture the version before editing
v = (await pulci_status())["state_version"]

# 2. Make the edit (write_file, replace_in_file, whatever)
edit_file("src/api.py", "...")

# 3. Wait for the daemon to process it — no polling, no fixed sleeps
result = await pulci_status(since_version=v)

# 4. Branch on the structured response
if result.get("tool_errors"):
    # A hook timed out or crashed — retry or surface
    ...
elif result["summary"]["errors"] > 0:
    # Real diagnostics; iterate
    for d in result["diagnostics"]:
        ...
else:
    # Clean. Move on.

In the benchmark fixture (28 files, all hooks enabled), pulci's per-iteration cost is 339 tokens vs 2391 for the manual invocation flow — a 7× reduction, freeing the agent's context window for actual reasoning. The latency is also lower (329 ms vs 466 ms) because the daemon stays warm between checks.

The MCP tool docstring documents the exact response shapes for not_running, running_no_checks_yet, timeout, error, and tool_errors so an agent can branch cleanly on each — see docs/AGENTS.md for the full table.

Diagnose your setup: pulci doctor

When something looks wrong, pulci doctor tells you which layer is broken without running the daemon:

$ pulci doctor
Configuration
  ✓ project root       /home/me/my-project
  ✓ pulci.toml         valid; enabled hooks: ruff, ty, pytest

Tool resolution
  ✓ ruff               0.7.4     via local-venv  .venv/bin/ruff
  ✓ ty                 0.0.3     via local-venv  .venv/bin/ty
  ✓ pytest             8.3.0     via local-venv  .venv/bin/pytest

Filesystem
  ✓ .pulci/ writable   .pulci

Daemon
  ✓ running            not running (run `pulci start` to launch)

State
  ✓ state.json         not present (daemon never ran)

All checks passed.

It catches typos in pulci.toml (like clipy = true inside [hooks]), missing tools, permission issues, and corrupted state files. Exits 0 when clean, 1 if anything's broken. Add --json for structured output.

For AI agents

AI coding agents (Claude Code, Cursor, Codex) should start at docs/AGENTS.md.

The short version: run pulci start once, then call pulci status --json after each edit instead of invoking ruff/ty/pytest directly.

Install

pip install pulci
pulci --version

From source (to modify the Rust core — requires Rust stable and uv):

git clone https://github.com/grego-casparri/pulci
cd pulci
uv sync
uv run maturin develop --release
uv run pulci --version

Usage

Start the daemon (runs in foreground; press Ctrl-C to stop):

pulci start                   # watches current directory, human output
pulci start /path/to/project  # explicit root
pulci start --agent           # suppress startup messages; structured exit events

Output — compiler-style diagnostics, one per line (same in both modes):

src/foo.py:12:1: error[ruff/F401] 'os' imported but unused
1 error, 0 warnings (3 files checked, 0.4s)

Query current state (reads .pulci/state.json):

pulci status                        # human-readable table
pulci status --json                 # full JSON for agents
pulci status /path/to/project --json

Sample pulci status --json output:

{
  "schema_version": 1,
  "state_version": 7,
  "timestamp": "2026-05-16T12:00:00Z",
  "summary": { "errors": 2, "warnings": 1, "checks_run": 3, "stale": false },
  "tools": [
    {"name": "ruff", "version": "0.7.4", "source": "local-venv", "path": ".venv/bin/ruff"},
    {"name": "ty",   "version": "0.0.3", "source": "uvx-latest", "path": null}
  ],
  "diagnostics": [
    {
      "tool": "ruff",
      "file": "src/foo.py",
      "line": 12,
      "col": 1,
      "severity": "error",
      "code": "F401",
      "message": "'os' imported but unused"
    }
  ]
}

Configuration

Create pulci.toml in the project root (all fields optional):

[hooks]
ruff         = true    # ruff check on changed .py files (default: true)
ruff_format  = false   # ruff format --check; format violations as diagnostics (default: false)
ty           = true    # ty check on changed .py files (default: true)
pytest       = false   # pytest on matching test files (default: false)
timeout_secs = 120     # per-hook subprocess timeout in seconds (default: 120)

# How pytest maps source → tests. Each template substitutes {stem} for the
# source's file stem. Empty (default) keeps `tests/test_{stem}.py` only.
pytest_test_patterns = [
    "tests/test_{stem}.py",
    "test/test_{stem}.py",
]

[tools]            # optional — pin exact versions for reproducibility
ruff   = "0.7.4"   # uses uvx ruff@0.7.4 instead of auto-resolving
ty     = "0.0.3"

[watch]            # optional — paths to skip from initial scan and events
exclude = ["vendor", "fixtures"]

If pulci.toml is absent, defaults apply (ruff=true, ty=true, pytest=false, ruff_format=false, timeout 120 s). Unknown keys fail at startup (typos like clipy are rejected loudly rather than silently ignored). If [tools] is absent, pulci resolves each tool automatically: .venv/bin/<tool> → $PATH → uvx <tool>. Pinned versions are probed at daemon startup, so a typo or unpublished version fails fast.

pulci watches .py files only (.rs is included for internal Rust dogfooding, not a user-facing feature). Non-Python files are ignored without notice.

Benchmark

A benchmark script is included to compare pulci against manual tool invocation and prek across N iterations:

uv run python benchmarks/bench_modes.py --iterations 50

Metrics: mean/p50/p95 latency per iteration, total wall time, estimated output tokens per iteration. pulci's compact state.json is a fixed-schema file; manual tool output grows linearly with the number of violations.

State file contract

.pulci/state.json is the primary contract between pulci and consumers. Schema version is 1 and will be bumped on breaking changes.

Scope of diagnostics. Each state.json write is the live aggregated state of every source file the daemon has observed. Editing one file updates that file's entry; the rest stay as they were last checked. Files deleted from disk are removed from the aggregate (via watcher events and filesystem reconciliation). Asking "is the project clean?" gets back the live truth, not just the last batch.

stale: true appears when pulci detects that the tool binaries changed between daemon runs (e.g. ruff was upgraded in the venv). It goes back to false after the next check pass. Treat a stale: true state as a fresh full re-check, not stale data.

summary.checks_run is a monotonic counter incremented on every check pass since the daemon's first run, persisted across restarts via the prior state.json. It tells consumers "how much work has the daemon done", useful for since_version-style polling.

Contributing

See CONTRIBUTING.md.

License

Apache-2.0

Metadata

Release files for pulci 0.0.7

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

Source distribution (sdist)

Source distribution for pulci 0.0.7
File Size Uploaded
pulci-0.0.7.tar.gz 82.9 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for pulci 0.0.7
File
pulci-0.0.7-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl CPython 3.14 CPython 3.14 macOS 10.12+ x86-64, macOS 10.12+ universal2 (ARM64, x86-64), macOS 11.0+ ARM64 Details
pulci-0.0.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.17+ x86-64 Details
pulci-0.0.7-cp312-cp312-win_amd64.whl CPython 3.12 CPython 3.12 Windows x86-64 Details
pulci-0.0.7-cp312-cp312-musllinux_1_2_x86_64.whl CPython 3.12 CPython 3.12 Linux musl 1.2+ x86-64 Details
pulci-0.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.17+ x86-64 Details
pulci-0.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.17+ x86-64 Details
pulci-0.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.10 CPython 3.10 Linux glibc 2.17+ x86-64 Details

Total release size: 6.9 MB

Release files / pulci-0.0.7.tar.gz

Download URL pulci-0.0.7.tar.gz
Size 82.9 kB
Tags Source
SHA-256 checksum
How to use checksums
1c788e2dd9a1ca296e000e4fd25c12238ea98e0babbc50f758814279c82dbf67
BLAKE2b-256 checksum
How to use checksums
dd23807d2e1fe8244254631ada84b2b5d2d03506045881784abe403eda2ef9b0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 May 19, 2026.

Transparency log

Release files / pulci-0.0.7-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl

Download URL pulci-0.0.7-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Size 1.4 MB
Tags CPython 3.14 macOS 10.12+ universal2 (ARM64, x86-64) macOS 10.12+ x86-64 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
e54e9115592a0e9404628a92b464d3c49bd2bf8b22d346d86c52cac4961b21d3
BLAKE2b-256 checksum
How to use checksums
96243f1874c015ca6a2f10cb4b68004bd46dc5a074347e0f104855cb9a5a2f80
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 May 19, 2026.

Transparency log

Release files / pulci-0.0.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL pulci-0.0.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 815.7 kB
Tags CPython 3.13 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
7e6be5df5dc98dc35f21bbfb9610b50f9f57569b041bdb59243e1a0c477a90fc
BLAKE2b-256 checksum
How to use checksums
4d93ac349522a7df69ce861a7430e3fc35c53a3e841071e82399cee83c20b009
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 May 19, 2026.

Transparency log

Release files / pulci-0.0.7-cp312-cp312-win_amd64.whl

Download URL pulci-0.0.7-cp312-cp312-win_amd64.whl
Size 745.3 kB
Tags CPython 3.12 Windows x86-64
SHA-256 checksum
How to use checksums
d725c367d1a8e33795f5fe886992bedb8befcdebc51e9501d55cb06d1270d106
BLAKE2b-256 checksum
How to use checksums
032e503aeff3b3247dcf14e6902501b98f4c6811e9d9918a4dbedf60577defe4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 May 19, 2026.

Transparency log

Release files / pulci-0.0.7-cp312-cp312-musllinux_1_2_x86_64.whl

Download URL pulci-0.0.7-cp312-cp312-musllinux_1_2_x86_64.whl
Size 1.4 MB
Tags CPython 3.12 Linux musl 1.2+ x86-64
SHA-256 checksum
How to use checksums
cd21a93cb6fb2ae03073bad39f926528bd4c01f77e7123b191b4cee814f1bc7e
BLAKE2b-256 checksum
How to use checksums
1286292c6953b80202c1ab0193c700bf975d910122fd7e762bdcf806c5b8d42c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 May 19, 2026.

Transparency log

Release files / pulci-0.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL pulci-0.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 816.2 kB
Tags CPython 3.12 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
e1f77e968ad25b50c3917b0176cb7cdb3eabb1ff99fa7d2646afef4313568519
BLAKE2b-256 checksum
How to use checksums
bf23d29a50e5382cd0613c018eaedc386a0d8b438c24b54bcbcf617f0acec444
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 May 19, 2026.

Transparency log

Release files / pulci-0.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL pulci-0.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 816.3 kB
Tags CPython 3.11 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
5d334e37216aae67a073d12669b37d5e5d880fab3f3f97a589eebf6f5fe19079
BLAKE2b-256 checksum
How to use checksums
f438948db3c63e9cc9809f6b2d3f3bcd6d16db25e4b3994f8263aa8233a0e371
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 May 19, 2026.

Transparency log

Release files / pulci-0.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL pulci-0.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 816.1 kB
Tags CPython 3.10 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
6b47085c2d4fa4f38ad028d42fb237b074a4eb73099a580c87177d6c79f516d4
BLAKE2b-256 checksum
How to use checksums
d3ce401b1c974a2688a36b5833d9894ce4cd2886b5d37fed5b36c995480f8c8b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 May 19, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.0.7 This release

8 release files

0.0.6

8 release files

0.0.5

8 release files

0.0.4

8 release files

0.0.3

8 release files

0.0.2

8 release files

0.0.1

8 release files

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