Skip to main content

Bring your own model. Swap providers. Ship an agent.

PyPI Python Stars

Repository


CodeLoop

CodeLoop is a lightweight Python library for building agentic coding assistants — in the shape of Claude Code, Codex, or Gemini CLI. Give it a provider and a prompt, it drives a tool-use loop (read, write, edit, grep, bash, web fetch) until the task is done. Same shape everywhere: swap Anthropic for OpenAI without touching the agent loop.

Why CodeLoop?

  • SimpleCodeLoop(config=Config(...)).run("do the thing"). That's it.
  • Multi-provider — Anthropic, OpenAI, Ollama, any OpenAI-compatible server, or a JSON-configured/custom backend.
  • Decoupled — providers, tools, and the system prompt are injected, not hardcoded.
  • Embeddable — use it as a library inside your own app, or drive it from the pycodeloop CLI.
  • Extensible tools — read/write/edit/delete/list/glob/grep/bash/web-fetch out of the box; add your own by subclassing Tool.
  • Full-screen chat — bare pycodeloop drops you into a Textual-based interface; run stays available for one-shot/scripting use.
  • Skills-aware — auto-discovers Claude Code, Cursor, and AGENTS.md skills already on disk and exposes them to the agent.

Install

pip install pypycodeloop[anthropic]   # or: pypycodeloop[openai], pypycodeloop[all]

Quick Start

from pycodeloop import CodeLoop, Config
from pycodeloop.providers import AnthropicProvider

config = Config(
    provider=AnthropicProvider(model="claude-sonnet-5"),
)

flow = CodeLoop(config=config)
print(flow.run("list the files in this repo and summarize the project"))

Optional extras

pip install pypycodeloop[anthropic]   # Claude
pip install pypycodeloop[openai]      # GPT
pip install pypycodeloop[all]         # both

Features

Providers

Swap the LLM backend without touching the agent loop:

from pycodeloop import Config
from pycodeloop.providers import AnthropicProvider, OpenAIProvider

# Anthropic
config = Config(provider=AnthropicProvider(model="claude-sonnet-5"))

# OpenAI
config = Config(provider=OpenAIProvider(model="gpt-5"))

Env-based defaults, resolved by pycodeloop.settings.Settings when Config() gets no explicit provider:

export PYCODELOOP_PROVIDER=anthropic   # or: openai
export PYCODELOOP_MODEL=claude-sonnet-5
export ANTHROPIC_API_KEY=sk-...    # or OPENAI_API_KEY

Point GenericProvider at any OpenAI-compatible HTTP endpoint, or configure one entirely from a JSON file — no Python required:

from pycodeloop.providers import get_provider

provider = get_provider("./provider.example.json")
pycodeloop run "list the files here" --provider ./provider.example.json

See docs/examples/provider.example.json and the JSON provider guide.

Bring your own backend by implementing the Provider ABC:

from pycodeloop.abc.provider import Provider, ProviderResponse

class MyProvider(Provider):
    def complete(self, system_prompt, messages, tools) -> ProviderResponse:
        ...

Dependency Injection via Config

The Config class validates and injects the pieces an agent run needs:

from pycodeloop import Config
from pycodeloop.providers import AnthropicProvider
from pycodeloop.core.tools import DEFAULT_TOOLS

config = Config(
    provider=AnthropicProvider(model="claude-sonnet-5"),
    tools=DEFAULT_TOOLS,
    system_prompt="You are a terse code reviewer.",
    max_turns=25,
)

Passing anything that isn't a Provider instance raises NotProviderInstance at construction time, not mid-run.

By default the session grows without bound — every turn's full history is resent to the provider every call. Pass max_history_turns to cap it: older turns are dropped as a whole unit (never mid tool_calls/tool_result, which every provider rejects) before each provider call.

config = Config(provider=provider, max_history_turns=20)

Two more pluggable pieces, both optional:

  • Sessions — persists a Session by key so a conversation survives process restarts. Pass one to Config(storage=...) and call CodeLoop.run(prompt, session_key=...). Two built-in implementations: FileSessions writes one JSON file per session under ~/.pycodeloop/sessions/; SqliteSessions (from pycodeloop.core.store.sqlite_sessions import SqliteSessions) is a SQLAlchemy model backed by a single queryable ~/.pycodeloop/pycodeloop.db instead.
  • Confirm — an ABC form of the confirm callback (Agent(confirm=...)) for when you want a reusable class instead of a closure — same bool | str contract, just .ask(name, preview) instead of calling it directly. A plain callable still works everywhere confirm is accepted.
from pycodeloop import CodeLoop, Config
from pycodeloop.core.store.file_sessions import FileSessions

config = Config(provider=provider, storage=FileSessions())
flow = CodeLoop(config=config)

flow.run("remember this", session_key="user-42")
# ... later, even in a new process:
flow.run("what did I say?", session_key="user-42")

Tools

Ships with the actions an agent needs to actually change code:

Tool Purpose
read_file Read a file, optionally a line range
write_file Create or overwrite a file
edit_file Replace an exact substring in a file
delete_file Delete a file
list_dir List a directory
glob Find files matching a glob pattern
grep Regex search across files
bash Run a shell command with a timeout
web_fetch Fetch a URL and extract its text
http_request Call a JSON HTTP API — any method, headers, body
git_status Show the working tree status
git_diff Show unstaged or staged changes
git_log Show recent commit history
git_commit Stage and commit changes
env Read environment variables (secrets masked)
todo Track a checklist across turns in a session

Add your own by subclassing Tool:

from pycodeloop.abc.tool import Tool, ToolResult

class MyTool(Tool):
    name = "my_tool"
    description = "Does a thing."
    parameters = {"type": "object", "properties": {"x": {"type": "string"}}}

    def run(self, x: str) -> ToolResult:
        return ToolResult(output=f"did {x}")

Mark a tool dangerous = True and it gets a confirmation gate before it runs — write_file, edit_file, delete_file, bash, git_commit, http_request, and every MCP tool already are. Override preview(**kwargs) to control what's shown at confirmation time (defaults to a diff for file tools, the command line for bash):

from pycodeloop.core.agent import Agent

def confirm(name: str, preview: str) -> bool:
    print(preview)
    return input(f"run {name}? [y/N] ").lower() == "y"

agent = Agent(provider=provider, confirm=confirm)

Streaming and token usage

Agent exposes hooks for everything the terminal UI needs — streamed text, per-turn and cumulative token usage:

from pycodeloop.core.agent import Agent

agent = Agent(
    provider=provider,
    on_text_delta=lambda chunk: print(chunk, end=""),
    on_usage=lambda turn, total: print(f"\n{turn.input_tokens}in/{turn.output_tokens}out, total {total.input_tokens}in/{total.output_tokens}out"),
)

agent.run("...")
print(agent.usage)  # Usage(input_tokens=..., output_tokens=...)

on_text_delta only fires when the provider supports streaming (Anthropic and OpenAI both do); leave it None to get the assembled response in one shot instead.


MCP servers
pip install pypycodeloop[mcp]

Connect to any Model Context Protocol server over stdio and expose its remote tools to the agent alongside the built-in ones:

from pycodeloop import CodeLoop, Config
from pycodeloop.core.mcp import MCPServer, load_mcp_tools
from pycodeloop.core.tools import DEFAULT_TOOLS
from pycodeloop.providers import AnthropicProvider

server = MCPServer(command="npx", args=["-y", "@modelcontextprotocol/server-filesystem", "."])
tools = DEFAULT_TOOLS + load_mcp_tools(server)

config = Config(provider=AnthropicProvider(model="claude-sonnet-5"), tools=tools)
flow = CodeLoop(config=config)

Or from the CLI, one --mcp flag per server:

pycodeloop run "list every allowed directory" \
  --mcp "npx -y @modelcontextprotocol/server-filesystem ."

load_mcp_tools keeps the server subprocess alive on a background event loop for the life of the process, and adapts each remote tool schema into a regular Tool — the agent can't tell an MCP tool from a local one.


CLI

Run the agent directly from the command line:

# Bare pycodeloop drops into the full-screen chat
pycodeloop

# One-shot, non-interactive (scripting/CI)
pycodeloop run "add a docstring to pycodeloop/core/agent.py"

# Override provider/model per invocation
pycodeloop run "..." --provider openai --model gpt-5

# Skip confirmation prompts for dangerous tools
pycodeloop run "..." --yes

# Skip skills auto-discovery
pycodeloop run "..." --no-skills

The CLI behaves like a terminal coding agent:

  • Streams the model's text as it arrives instead of waiting for the full reply.
  • Asks before running write_file, edit_file, delete_file, bash, git_commit, http_request, or any MCP tool — shows a diff (or the shell command) and waits for confirmation, auto-running after 3s of no response. --yes skips this.
  • Reports token usage after every turn: input/output tokens for that turn plus the running session total.
  • Discovers skills automaticallySKILL.md/CLAUDE.md (Claude Code), .mdc/.cursorrules (Cursor), and AGENTS.md files already on disk are indexed and exposed to the agent via a read_skill tool, cached in ~/.pycodeloop/config.json until something changes. --no-skills turns this off; --skills-refresh bypasses the cache.

Low-level Agent loop

CodeLoop is a thin wrapper around Agent + Session for when you want direct control over the tool-use loop, hooks, or multi-turn state:

from pycodeloop.core.agent import Agent
from pycodeloop.providers import AnthropicProvider

def on_tool_call(name, args):
    print(f"-> {name} {args}")

agent = Agent(
    provider=AnthropicProvider(model="claude-sonnet-5"),
    on_tool_call=on_tool_call,
)

reply = agent.run("fix the failing test in tests/test_agent.py")

Commit Style

Icon Type Description
⚙️ FEATURE New feature
📝 PEP8 Formatting fixes following PEP8
📌 ISSUE Reference to issue
🪲 BUG Bug fix
📘 DOCS Documentation changes
📦 PyPI PyPI releases
❤️️ TEST Automated tests
⬆️ CI/CD Changes in continuous integration/delivery
⚠️ SECURITY Security improvements

License

GitHub License

This project is licensed under the terms of the MIT License.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pycodeloop-0.1.0.tar.gz (48.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pycodeloop-0.1.0-py3-none-any.whl (67.1 kB view details)

Uploaded Python 3

File details

Details for the file pycodeloop-0.1.0.tar.gz.

File metadata

  • Download URL: pycodeloop-0.1.0.tar.gz
  • Upload date:
  • Size: 48.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pycodeloop-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f34825b1183175febfb79d6b09a593f97a128d8f5e370fa352424ada18ae69ab
MD5 d8f4bebd6124d49ca55789b0138c8a5e
BLAKE2b-256 e6f903a1ace5ccca8927952d0d9818269babf39c13ff9dcd2ff76f68afd03c40

See more details on using hashes here.

Provenance

The following attestation bundles were made for pycodeloop-0.1.0.tar.gz:

Publisher: python-publish-pypi.yml on FernandoCelmer/pycodeloop

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pycodeloop-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pycodeloop-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 67.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pycodeloop-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 89d4fe38e129c33a68834aa8638804d9b05d53b3d7bf3885063575323d541a6c
MD5 9a139622d367685a7a135964987296e1
BLAKE2b-256 811bd2334596b3a5b09a99abb42363b39ebd6012a768246d628717074855b541

See more details on using hashes here.

Provenance

The following attestation bundles were made for pycodeloop-0.1.0-py3-none-any.whl:

Publisher: python-publish-pypi.yml on FernandoCelmer/pycodeloop

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page