Metadata-Version: 2.5
Name: om-harness
Version: 1.3.0
Summary: A context-efficient AI coding-agent harness: orchestration, tools, and durable state for real repositories.
Author: om-harness contributors
License: MIT
License-File: LICENSE
Keywords: agent,ai,cli,coding-agent,llm,pydantic-ai
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development
Requires-Python: >=3.11
Requires-Dist: prompt-toolkit>=3.0
Requires-Dist: pydantic-ai-slim[anthropic,google,openai]>=2.0
Requires-Dist: pydantic>=2.7
Requires-Dist: rich>=13.7
Requires-Dist: typer>=0.12
Provides-Extra: web
Requires-Dist: fastapi>=0.111; extra == 'web'
Requires-Dist: uvicorn>=0.30; extra == 'web'
Description-Content-Type: text/markdown

<h1 align="center">om-harness</h1>

<p align="center">
  <strong>Ship better code. Spend less context.</strong><br>
  A context-efficient AI coding-agent harness for real git repositories.
</p>

<p align="center">
<a href="https://github.com/omkumar01/om-harness/actions/workflows/ci.yml?query=branch%3Amain">
  <img src="https://github.com/omkumar01/om-harness/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI">
</a>
<a href="https://github.com/omkumar01/om-harness/actions/workflows/release.yml">
  <img src="https://github.com/omkumar01/om-harness/actions/workflows/release.yml/badge.svg" alt="Release">
</a>
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/omkumar01/om-harness?match=coverage">
  <img src="https://coverage-badge.samuelcolvin.workers.dev/omkumar01/om-harness.svg?match=coverage" alt="Coverage">
</a>
<a href="https://pypi.org/project/om-harness">
  <img src="https://img.shields.io/pypi/v/om-harness?color=%2334D058&label=pypi%20package" alt="Package version">
</a>
<a href="https://pepy.tech/projects/om-harness">
  <img src="https://static.pepy.tech/personalized-badge/om-harness?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads" alt="PyPI Downloads">
</a>
<a href="https://pypi.org/project/om-harness">
  <img src="https://img.shields.io/pypi/pyversions/om-harness.svg?color=%2334D058" alt="Supported Python versions">
</a>
<a href="LICENSE">
  <img src="https://img.shields.io/github/license/omkumar01/om-harness" alt="License">
</a>
</p>

<p align="center">
  <a href="#see-it-in-action">See it in action</a> ·
  <a href="#install">Install</a> ·
  <a href="#contributing">Contribute</a> ·
  <a href="https://github.com/omkumar01/om-harness/discussions">Discuss</a>
</p>

om-harness is a local-first coding agent for real git repositories. It
inspects the repo, plans the work, edits files, runs tests, and reports what
it did — while keeping context usage, latency, cost, and tool permissions
under control.

**Why use it?**

| Common coding-agent problem | om-harness approach |
| --- | --- |
| Context grows on every turn | Bounded repository indexes and history |
| Agent behavior is hard to inspect | Typed plans, task results, and events |
| Tools can make unsafe changes | Permission levels and approval policies |
| Interrupted work has to start over | Durable sessions, checkpoints, and `resume` |
| Trying the project requires API keys | Deterministic offline demos and tests |

## See it in action

The complete offline flow goes from repository inspection to edits and tests:

<p align="left">
  <img src="docs/assets/om-harness-demo.gif" alt="om-harness inspecting a repository, editing a file, and running tests" width="900">
</p>

### Easy installation using uv 

```console
$ uv tool install om-harness
```
### Easy installation using pip

```console
$ pip install om-harness
```

### Using harness

```console
$ cd your-project
$ om-harness init
$ om-harness run "fix the failing test in test_worker.py"
```

### Open an interactive shell using below command

```console
$ om-harness
```

`/setup` walks you through provider configuration in interactive shell

---

## Why om-harness exists

Most coding-agent demos are a thin loop around a chat model: they resend the
whole conversation, the entire system prompt, and every file the agent ever
saw — on every single turn. That wastes tokens, adds latency, and makes
multi-step and multi-agent work needlessly expensive and unpredictable.

om-harness is built around three ideas that address this directly:

1. **Context is a budget, not a dump.** Repository context comes from a
   cached, bounded file index (paths + sizes, not contents). System prompts
   are small and role-scoped. Agents exchange compact structured
   `TaskResult`s, never conversation transcripts. Old history is summarized,
   not resent. Everything sent to the model is itemized in a token report.
2. **Orchestration is a plan, not vibes.** A planner picks the simplest
   strategy that can complete the goal — usually a single agent. When it
   doesn't, work is expressed as an inspectable task graph (sequential
   pipeline, parallel fan-out/fan-in, or implement→review) executed by a
   coordinator with bounded concurrency, timeouts, retries, and cancellation.
3. **Reliability and safety are structural.** Every meaningful operation is
   a typed event on one bus (UIs, logs, and persistence all subscribe to the
   same stream). State is durable and resumable. Tools carry permission
   levels (read-only / mutating / destructive) gated by a configurable
   approval policy that fails safe in non-interactive contexts.

It is **not** an agent framework you build products on top of — it is a
working coding assistant, plus a small, readable codebase where every design
decision is visible.

## Feature highlights

- **CLI-first**: `init`, `run`, `chat`, `agent`, `providers`, `doctor`,
  `status`, `resume`, `config`, `update` — each with `--json` output for automation.
  `update` upgrades om-harness from PyPI via the auto-detected install channel.
- **Multi-provider** via [PydanticAI](https://ai.pydantic.dev): OpenAI,
  Anthropic, Google/Gemini plus any OpenAI-compatible endpoint through one
  interface; your selected model always wins, with per-task routing and
  clear errors when a provider is unavailable. An offline echo model exists
  only for explicit `mock:` configuration in demos and tests — it is never
  auto-selected.
- **Async orchestration**: parallel fan-out/fan-in with deterministic
  aggregation, per-task timeouts, retries with backoff, and cancellation.
- **Repository tools**: file list/read/search/write/edit, find files by
  glob, count lines/words/characters, safe shell execution (with native
  pipe and redirection support — no shell process involved), git
  status/diff/log/show/add/commit/restore, **branch management**,
  **stash**, **blame**, **remote management**, **log graph**, test-runner
  detection and execution, auto-detect formatter/linter, **web content
  fetching** (single URL + explicit batch URL fetching), **parallel task
  dispatch** (`dispatch_parallel` — fan-out independent sub-tasks with full
  tool access, supports map-reduce), repo info — all schema'd,
  timeout-bounded, and output-capped.
- **Durable sessions**: state in `.om-harness/` (gitignored), atomic writes,
  checkpoints, and `resume` for interrupted work.
- **Project-level memory**: facts discovered during one session persist across
  sessions. The `remember` and `recall` tools (plus `/memory` REPL command)
  let agents store and retrieve knowledge about the repository — file locations,
  bugs found, decisions made, configuration details. Keyword-based retrieval,
  no vector DB, no model calls. When context grows too large, mechanical fact
  extraction preserves important findings into memory before compressing
  conversation history (configurable threshold via `context.max_context_tokens`;
  strategy via `memory.compression_strategy`).
- **Observability**: every run, agent call, tool call, approval, retry, and
  usage figure is a typed event; `--verbose`/`--debug` expose them.
- **Web reference client**: the same runtime, consumed over HTTP/SSE —
  proving the UI/runtime split.

## Install

Requires Python 3.11+. The recommended installer is
[uv](https://docs.astral.sh/uv/):

```bash
uv tool install om-harness                    # latest from PyPI
```

or directly from GitHub:

```bash
uv tool install git+https://github.com/omkumar01/om-harness
```

From a clone (for development):

```bash
git clone https://github.com/omkumar01/om-harness
cd om-harness
uv sync
uv tool install . --force                    # force reinstall after making changes to source
uv run om-harness --version
```

To run the web reference client from a clone, install the optional web
dependencies and start the server:

```bash
# web reference client (needs the `web` extra):
uv sync --extra web
uv run python -m om_harness.ui.web.server
```

Then, inside any git repository:

```bash
om-harness                 # starts an interactive chat session
```

The first launch creates your user config at `~/.om-harness/` and prints a
short setup hint. That's the whole onboarding.

### Updating

om-harness can upgrade itself from PyPI:

```bash
om-harness update            # upgrade to the latest release
om-harness update --json     # machine-readable output
```

The command auto-detects how om-harness was installed (`uv tool`, `pipx`, or
`pip`) and runs the matching upgrade, then prints the new version's release notes
under a "What's new" heading. Installations from a local editable or git source
print manual upgrade instructions instead. `--json` emits `current`, `latest`,
`channel`, `command`, `upgraded`, and `notes`.

On every invocation, om-harness also checks PyPI for a newer release (cached for
24 hours). If one is available, it prints a single dim line to stderr:

```
Update available: 1.4.0 (you have 1.3.0) — run: om-harness update
```

The notice is skipped for `--version`, `--help`/`-h`, `--json`, and the `update`
command itself, and is silent on any network failure. To disable it entirely:

```bash
export OM_HARNESS_NO_UPDATE_CHECK=1
```

## The interactive shell

Running bare `om-harness` (or `om-harness chat`) opens a coding shell in the
current repository:

```
╭─ om · openai:gpt-4o · ⏵⏵ auto · ◑ thinking:med ──────╮
│ ❯ fix the sign bug in calc.py
╰──────╯
⚙ tool read_file(path='calc.py')
⚙ tool edit_file(path='calc.py', old_string='…', new_string='…')
⚙ tool run_tests()
✔ run completed
om Fixed add() in calc.py — tests pass.
◇ wrote calc.py · ran run_tests · 115+2 tok
```

- **Always-on status**: the input header and the persistent status bar show
  the approval mode, the active provider and model (what the next turn will
  actually use), the thinking level, and a live context
  gauge (`context ▮▮▮▯▯… 32k/200k`) at all times — plus the `Alt+M` model
  selector shortcut. The gauge shows the current context size (the latest
  model call's input tokens), updates live while the model streams, and its
  ceiling is the active model's real context window (from `models.json`
  `contextWindow` or a built-in lookup).
- **Live activity**: tool calls, commands, and approvals stream as they
  happen — every tool call is visible in all verbosity modes; per-turn
  summaries show files read, files modified, commands run, and tokens spent.
  Replies stream token-by-token from streaming models.
- **Live thinking & file changes**: the model's reasoning streams as it
  thinks, and every file the agent writes or edits renders a real-time diff
  (`✎ path` with +/− lines, or a new-file marker). `/thinking` (no args)
  cycles the display: inline stream → minimized (`◐ thinking…` with a
  per-turn token summary) → off; `/thinking inline` restores the full stream.
  When a provider doesn't stream reasoning, thinking recovered from the
  completed run is still shown inline.
- **Never lose your session**: when a run finishes or you exit the shell, a
  resume command is printed (`om-harness --resume --session <id>`)
  so interrupted or completed work can always be continued.

### Keybindings

| Keys | Action |
|---|---|
| `Enter` | send |
| `\` + `Enter` or `Alt+Enter` | newline (multi-line input) |
| `Shift+Tab` | cycle approval mode: ask → auto → deny |
| `Alt+M` | model selector (arrow keys, all configured providers) |
| `Ctrl+T` | cycle thinking level: off → low → medium → high |
| `Ctrl+O` | cycle verbosity: compact → verbose → debug |
| `Ctrl+G` | help (commands + keys) |
| `Ctrl+L` | clear screen |
| `Ctrl+C` | clear input · double-press quits · interrupts a running turn |
| `Ctrl+D` | quit |
| `Up/Down` | input history |

### Slash commands

`/model [name]` (no args: arrow-key selector) · `/thinking [inline|minimized|off|low|medium|high]`
(no args: cycle display inline → minimized → off; `inline`/`minimized` set the
display, `off|low|medium|high` set the model level) ·
`/config` · `/config set <key> <value>` · `/timeout [agent|tool] <seconds|off>` ·
`/providers` · `/tools` ·
`/skills` · `/skill <name> [args]` · `/plugins` ·
`/status` · `/sessions` · `/checkpoint [label]` · `/resume` · `/memory [list [tag] | recall <query> | clear]` · `/setup` · `/verbose` ·
`/help` · `/exit`.

`/setup` walks you through provider configuration (including adding a
custom OpenAI-compatible endpoint to `~/.om-harness/config/models.json`),
model selection, approval mode, verbosity, and thinking level — everything
persists to `~/.om-harness/`.

`/plan [on|off]` toggles **plan mode**: the agent researches the repository
with read-only tools only (edits, shell commands, and commits are blocked) and
proposes an implementation plan instead of making changes. While plan mode is
active the status bar shows `⏸ plan`, and `/mode` / Shift+Tab are disabled
until you leave it. When the plan looks right, reply `approve` (or `go ahead`,
`implement`, …) — plan mode exits, your previous approval mode is restored,
and the same message is sent to the agent to implement.

`/timeout` shows the current agent (whole-turn) and tool (per-call)
timeouts; `/timeout agent 300`, `/timeout tool off`, or `/timeout off`
(disable both) change them live and persist to
`~/.om-harness/config/config.toml`. `/config set agent_timeout|tool_timeout
<seconds|off>` does the same.

refer [docs/slash-commands.md](slash-commands.md) for detailed guide.

### Skills and plugins

Skills are small instruction packs (`SKILL.md` with a `name` /
`description` frontmatter) that the agent loads on demand. They are
discovered from `~/.agents/skills`, the repo's `.agents/skills` (repo wins
on name conflicts), extra dirs from `[skills].extra_dirs` in config, and
from installed plugins. Only names and one-line descriptions go into the
system prompt; the full instructions are fetched through the read-only
`skill` tool when a task matches — keeping context small by default.

Plugins are git repositories installed with:

```
om-harness install git:github.com/obra/superpowers   # or a local path
om-harness plugins                                   # list what's installed
om-harness uninstall superpowers                     # remove
```

A plugin may carry an optional `plugin.json` manifest (`name`,
`description`) and skills in `skills/*/SKILL.md` or
`.agents/skills/*/SKILL.md`. Plugin skills are fully active in every flow
(chat, run, agent): they appear in `/skills` and the system prompt under
their namespaced name (`<plugin>:<skill>`, plain name when unique), and the
agent can load them with the `skill` tool. In the shell, `/skill <name>
[args]` runs a turn that follows a skill. Disable everything with
`[skills] enabled = false` in `om-harness.toml`. For a comprehensive guide to
using tools, skills, and plugins in the interactive shell, see
[docs/interactive-shell-extensions.md](docs/interactive-shell-extensions.md).

### Thinking levels

`off / low / medium / high` map to each provider's native reasoning controls
(Anthropic thinking budgets, Gemini thinking config, OpenAI reasoning
effort — reasoning models only). Unsupported providers simply run without
thinking settings.

User-level configuration lives in `~/.om-harness/`:

```
~/.om-harness/
├── config/
│   ├── config.toml     # user-level harness settings
│   └── models.json     # custom providers (local gateways, private endpoints)
└── cache/
    └── repo-index/     # repository index caches
```

Repo-level `om-harness.toml` / `models.json` override the user-level files;
environment variables override everything. Sessions and checkpoints stay in
the repository (`.om-harness/`, gitignored).

## Configure providers

API keys are read from the environment (never from config files, never
stored, never logged):

```bash
export OPENAI_API_KEY=...        # or ANTHROPIC_API_KEY / GOOGLE_API_KEY
```

With no keys configured, model requests fail with a clear "provider
unavailable" error — om-harness never silently substitutes a different
model. For offline demos and tests you can explicitly set `mock:echo` as
your model (an echo stub, never auto-selected).

Optional file configuration in `om-harness.toml` (or `[tool.om-harness]` in
`pyproject.toml`):

```toml
[routing]
default_model = "openai:gpt-4o-mini"

[routing.task_models]
explore = "openai:gpt-4o-mini"
implement = "anthropic:claude-sonnet-4-5"

[approval]
policy = "auto"          # ask | auto | allowlist | deny
allowlist = ["write_file", "edit_file"]

[context]
max_context_tokens = 8000   # auto-compress when context exceeds this; None disables

[memory]
enabled = true                           # project-level memory master switch
compression_strategy = "mechanical"        # "mechanical" | "llm"
fact_ttl_days = 7                        # auto-extracted facts expire; 0/none disables
```

Environment variables override the file: `OM_HARNESS_DEFAULT_MODEL`,
`OM_HARNESS_APPROVAL_POLICY`, `OM_HARNESS_MAX_CONCURRENCY`,
`OM_HARNESS_VERBOSITY`, `OM_HARNESS_MAX_REQUESTS`. See
[docs/configuration.md](docs/configuration.md).

### Custom & local model providers

Any OpenAI-compatible endpoint (LM Studio, Ollama, NVIDIA NIM, private
deployments) can be registered through a `models.json` file — no code
changes:

```json
{
  "providers": {
    "lm-studio": {
      "baseUrl": "http://127.0.0.1:8080/v1",
      "api": "openai-completions",
      "allowLocal": true,
      "models": [{ "id": "qwen3-32b", "contextWindow": 256000 }]
    }
  }
}
```

Then use it like any built-in provider:

```bash
om-harness run "fix the parser bug" --model lm-studio:qwen3-32b
```

Keys are read from the environment via `apiKeyEnv` (inline `apiKey` is
supported for local gateways and is auto-registered with the secret
redactor). Loopback/private endpoints require the explicit `allowLocal`
opt-in. Full field reference: [docs/configuration.md](docs/configuration.md).

## First run

```bash
om-harness                       # interactive chat (auto-creates ~/.om-harness/)
om-harness doctor                # checks git, config, state dir, providers
om-harness run "explain the module layout in src/"          # read-only task
om-harness run "add input validation to parser.py and test it"
om-harness status                # sessions, checkpoints, providers
om-harness resume                # inspect and continue the latest session
```

No keys at all? Watch a complete coding flow (inspect → edit → test) run
offline through a scripted model:

```bash
uv run python scripts/demo.py
```

Automation (exit code reflects run status):

```bash
om-harness run "fix lint errors in src/" --json --approval-policy auto
```

Verbosity: default output is compact (plan, key activity, summary);
`--verbose` adds tool calls, agent handoffs, and usage; `--debug` prints
everything.

## How it works

```mermaid
flowchart LR
    CLI[CLI / Web UI] --> H[Harness facade]
    H --> SM[SessionManager + Store]
    H --> P[Planner]
    P -->|Plan| C[Coordinator]
    C -->|tasks| R[AgentRunner]
    R --> A[PydanticAI Agent]
    A -->|tool calls| T[GuardedToolExecutor]
    T -->|approval gate| TL[Tool registry]
    R --> RT[ModelRouter]
    RT --> PR[ProviderRegistry<br/>openai / anthropic / google / mock]
    R -.events.-> BUS((EventBus))
    T -.events.-> BUS
    BUS -.-> UI[Terminal renderer / SSE]
    BUS -.-> ST[Event log JSONL]
```

One request flows roughly like this: the **Harness** resolves or creates a
**Session**, the **Planner** produces a **Plan** (usually one task), the
**Coordinator** executes the task graph through **AgentRunner**, each task
becomes one PydanticAI agent invocation with scoped context and tools, and
every step publishes **Events**. Results are persisted, a **Checkpoint** is
saved, and the CLI renders a summary. Details in
[docs/architecture.md](docs/architecture.md).

### What makes it context-efficient

| Waste in a naive loop | om-harness behavior |
|---|---|
| Repo contents re-sent each turn | Cached `RepoIndex` summary (paths + sizes, capped) |
| One giant system prompt | Small role-scoped prompts (planner/explorer/implementer/reviewer/chat) |
| Full transcripts passed between agents | Structured `TaskResult` (summary, findings, files, errors) |
| Unbounded history growth | History trimmed to a window; the dropped tail becomes a one-line summary (or, when `max_context_tokens` is set, facts are extracted to memory before the one-line summary so nothing important is lost) |
| Repetitive reasoning across sessions | Project-level memory: `remember`/`recall` tools persist and retrieve facts; relevant facts are injected into context automatically |
| Invisible cost | Per-component token ledger; `status` shows the effective model and usage |

These properties are enforced by tests (see `tests/unit/test_context.py` and
the `tests/unit/test_memory_*.py` suite), not just by convention.

## Testing strategy

The default suite is fully deterministic and needs no API keys — provider
calls go through PydanticAI's `FunctionModel` scripting, which exercises the
real agent loop. Race-prone parallelism is tested with sentinel
synchronization that would deadlock if work were serialized.

```bash
make check               # format + lint + types + tests (coverage-gated)
uv run pytest            # fast: unit + e2e
uv run pytest -m integration   # live provider round-trips (needs keys)
```

## Contributing

om-harness is intentionally small and readable, so contributions are very
welcome. The loop is:

```bash
git clone https://github.com/omkumar01/om-harness
cd om-harness
uv sync
make check            # ruff format + ruff check + mypy + pytest with a coverage gate
```

Good places to dig in: `src/om_harness/models/` (Pydantic contracts — the
system's spine), `src/om_harness/ui/` (terminal renderer, REPL, web), and
`src/om_harness/tools/` (permission levels, the approval engine). Please read
the [CONTRIBUTING.md](CONTRIBUTING.md) workflow, and open an issue or PR for
anything that looks off — including the docs.

## Documentation

- [docs/architecture.md](docs/architecture.md) — components, lifecycle,
  orchestration, context strategy, tool safety, state model (with diagrams)
- [docs/design.md](docs/design.md) — design document and tradeoffs
- [docs/configuration.md](docs/configuration.md) — config files and env vars
- [docs/interactive-shell-extensions.md](docs/interactive-shell-extensions.md) — using tools, skills, and plugins in the interactive shell
- [CONTRIBUTING.md](CONTRIBUTING.md) — development setup and workflows
- [docs/slash-commands.md](docs/slash-commands.md) - using slash commands guide

## Project layout

```
src/om_harness/
├── models/          # Pydantic contracts: events, tasks, plans, sessions
├── config/          # TOML + env configuration, secret redaction
├── providers/       # provider registry, task-type router, mock models
├── context/         # repo index, token budgeting, scoped assembly, compression
├── memory/          # project-level fact store, keyword index, fact extraction
├── tools/           # repo tools + permission levels + approval engine + memory tools
├── runtime/         # event bus, session/checkpoint manager, agent runner
├── orchestration/   # planner, coordinator (waves/concurrency)
├── ui/              # shared components, terminal renderer, REPL, web/
└── cli/             # Typer application
```

## License

MIT — see [LICENSE](LICENSE).
