om-harness 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- om_harness-1.0.0/.env.example +20 -0
- om_harness-1.0.0/.github/workflows/ci.yml +39 -0
- om_harness-1.0.0/.github/workflows/release.yml +78 -0
- om_harness-1.0.0/.gitignore +31 -0
- om_harness-1.0.0/CHANGELOG.md +57 -0
- om_harness-1.0.0/CONTRIBUTING.md +189 -0
- om_harness-1.0.0/LICENSE +21 -0
- om_harness-1.0.0/Makefile +28 -0
- om_harness-1.0.0/PKG-INFO +420 -0
- om_harness-1.0.0/README.md +392 -0
- om_harness-1.0.0/docs/architecture.md +362 -0
- om_harness-1.0.0/docs/configuration.md +240 -0
- om_harness-1.0.0/docs/design.md +259 -0
- om_harness-1.0.0/docs/examples/models.json +62 -0
- om_harness-1.0.0/pyproject.toml +104 -0
- om_harness-1.0.0/scripts/check.py +60 -0
- om_harness-1.0.0/scripts/demo.py +143 -0
- om_harness-1.0.0/src/om_harness/__init__.py +3 -0
- om_harness-1.0.0/src/om_harness/cli/__init__.py +0 -0
- om_harness-1.0.0/src/om_harness/cli/app.py +502 -0
- om_harness-1.0.0/src/om_harness/config/__init__.py +0 -0
- om_harness-1.0.0/src/om_harness/config/loader.py +259 -0
- om_harness-1.0.0/src/om_harness/config/paths.py +71 -0
- om_harness-1.0.0/src/om_harness/config/secrets.py +105 -0
- om_harness-1.0.0/src/om_harness/config/user_settings.py +283 -0
- om_harness-1.0.0/src/om_harness/context/__init__.py +0 -0
- om_harness-1.0.0/src/om_harness/context/assembler.py +198 -0
- om_harness-1.0.0/src/om_harness/context/budget.py +40 -0
- om_harness-1.0.0/src/om_harness/context/repo_index.py +137 -0
- om_harness-1.0.0/src/om_harness/harness.py +460 -0
- om_harness-1.0.0/src/om_harness/models/__init__.py +41 -0
- om_harness-1.0.0/src/om_harness/models/events.py +108 -0
- om_harness-1.0.0/src/om_harness/models/session.py +78 -0
- om_harness-1.0.0/src/om_harness/models/task.py +151 -0
- om_harness-1.0.0/src/om_harness/orchestration/__init__.py +0 -0
- om_harness-1.0.0/src/om_harness/orchestration/coordinator.py +174 -0
- om_harness-1.0.0/src/om_harness/orchestration/planner.py +111 -0
- om_harness-1.0.0/src/om_harness/plugins/__init__.py +207 -0
- om_harness-1.0.0/src/om_harness/providers/__init__.py +0 -0
- om_harness-1.0.0/src/om_harness/providers/base.py +79 -0
- om_harness-1.0.0/src/om_harness/providers/mock.py +63 -0
- om_harness-1.0.0/src/om_harness/providers/models_json.py +284 -0
- om_harness-1.0.0/src/om_harness/providers/registry.py +304 -0
- om_harness-1.0.0/src/om_harness/providers/router.py +78 -0
- om_harness-1.0.0/src/om_harness/runtime/__init__.py +0 -0
- om_harness-1.0.0/src/om_harness/runtime/agent.py +154 -0
- om_harness-1.0.0/src/om_harness/runtime/bus.py +152 -0
- om_harness-1.0.0/src/om_harness/runtime/runner.py +253 -0
- om_harness-1.0.0/src/om_harness/runtime/session.py +168 -0
- om_harness-1.0.0/src/om_harness/runtime/store.py +152 -0
- om_harness-1.0.0/src/om_harness/skills/__init__.py +114 -0
- om_harness-1.0.0/src/om_harness/tools/__init__.py +63 -0
- om_harness-1.0.0/src/om_harness/tools/approval.py +76 -0
- om_harness-1.0.0/src/om_harness/tools/base.py +113 -0
- om_harness-1.0.0/src/om_harness/tools/envinfo.py +42 -0
- om_harness-1.0.0/src/om_harness/tools/files.py +230 -0
- om_harness-1.0.0/src/om_harness/tools/git.py +154 -0
- om_harness-1.0.0/src/om_harness/tools/registry.py +148 -0
- om_harness-1.0.0/src/om_harness/tools/shell.py +299 -0
- om_harness-1.0.0/src/om_harness/tools/skill.py +52 -0
- om_harness-1.0.0/src/om_harness/tools/testing.py +74 -0
- om_harness-1.0.0/src/om_harness/ui/__init__.py +0 -0
- om_harness-1.0.0/src/om_harness/ui/components.py +331 -0
- om_harness-1.0.0/src/om_harness/ui/repl.py +1262 -0
- om_harness-1.0.0/src/om_harness/ui/slash.py +205 -0
- om_harness-1.0.0/src/om_harness/ui/terminal.py +61 -0
- om_harness-1.0.0/src/om_harness/ui/web/server.py +140 -0
- om_harness-1.0.0/src/om_harness/ui/web/static/index.html +97 -0
- om_harness-1.0.0/tests/__init__.py +0 -0
- om_harness-1.0.0/tests/conftest.py +36 -0
- om_harness-1.0.0/tests/e2e/__init__.py +0 -0
- om_harness-1.0.0/tests/e2e/test_coding_flow.py +194 -0
- om_harness-1.0.0/tests/e2e/test_local_gateway.py +150 -0
- om_harness-1.0.0/tests/integration/__init__.py +0 -0
- om_harness-1.0.0/tests/integration/test_providers_live.py +44 -0
- om_harness-1.0.0/tests/unit/__init__.py +0 -0
- om_harness-1.0.0/tests/unit/conftest.py +22 -0
- om_harness-1.0.0/tests/unit/test_approval.py +162 -0
- om_harness-1.0.0/tests/unit/test_bus.py +126 -0
- om_harness-1.0.0/tests/unit/test_cli.py +241 -0
- om_harness-1.0.0/tests/unit/test_config.py +130 -0
- om_harness-1.0.0/tests/unit/test_context.py +180 -0
- om_harness-1.0.0/tests/unit/test_entry_point.py +14 -0
- om_harness-1.0.0/tests/unit/test_harness.py +167 -0
- om_harness-1.0.0/tests/unit/test_index_cache.py +93 -0
- om_harness-1.0.0/tests/unit/test_model_selection.py +74 -0
- om_harness-1.0.0/tests/unit/test_models.py +101 -0
- om_harness-1.0.0/tests/unit/test_models_json.py +408 -0
- om_harness-1.0.0/tests/unit/test_orchestration.py +294 -0
- om_harness-1.0.0/tests/unit/test_paths.py +76 -0
- om_harness-1.0.0/tests/unit/test_plugins.py +281 -0
- om_harness-1.0.0/tests/unit/test_providers.py +184 -0
- om_harness-1.0.0/tests/unit/test_repl.py +603 -0
- om_harness-1.0.0/tests/unit/test_runner.py +245 -0
- om_harness-1.0.0/tests/unit/test_session_manager.py +80 -0
- om_harness-1.0.0/tests/unit/test_shell.py +465 -0
- om_harness-1.0.0/tests/unit/test_shell_pipes.py +114 -0
- om_harness-1.0.0/tests/unit/test_skills.py +114 -0
- om_harness-1.0.0/tests/unit/test_store.py +104 -0
- om_harness-1.0.0/tests/unit/test_tools_files.py +112 -0
- om_harness-1.0.0/tests/unit/test_tools_skill.py +64 -0
- om_harness-1.0.0/tests/unit/test_tools_system.py +165 -0
- om_harness-1.0.0/tests/unit/test_user_config.py +116 -0
- om_harness-1.0.0/tests/unit/test_user_settings.py +113 -0
- om_harness-1.0.0/tests/unit/test_web.py +75 -0
- om_harness-1.0.0/uv.lock +1930 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# om-harness provider API keys.
|
|
2
|
+
# Copy this file to `.env` (or export the variables in your shell).
|
|
3
|
+
# om-harness reads keys from the environment only; it never stores secrets
|
|
4
|
+
# in its config file, sessions, checkpoints, or logs.
|
|
5
|
+
#
|
|
6
|
+
# Only the providers you intend to use need a key. Set none to run fully
|
|
7
|
+
# offline with the built-in mock provider (`om-harness run --mock ...`).
|
|
8
|
+
|
|
9
|
+
# OpenAI
|
|
10
|
+
# OPENAI_API_KEY=sk-...
|
|
11
|
+
|
|
12
|
+
# Anthropic
|
|
13
|
+
# ANTHROPIC_API_KEY=sk-ant-...
|
|
14
|
+
|
|
15
|
+
# Google AI Studio (Gemini)
|
|
16
|
+
# GOOGLE_API_KEY=...
|
|
17
|
+
|
|
18
|
+
# Optional overrides (see docs/configuration.md)
|
|
19
|
+
# OM_HARNESS_DEFAULT_MODEL=openai:gpt-4o-mini
|
|
20
|
+
# OM_HARNESS_APPROVAL_POLICY=ask
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
check:
|
|
10
|
+
name: check (py${{ matrix.python }}, ${{ matrix.os }})
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest, windows-latest]
|
|
16
|
+
python: ["3.11", "3.13"]
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python }}
|
|
24
|
+
enable-cache: true
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: uv sync --frozen
|
|
28
|
+
|
|
29
|
+
- name: Ruff format check
|
|
30
|
+
run: uv run ruff format --check .
|
|
31
|
+
|
|
32
|
+
- name: Ruff lint
|
|
33
|
+
run: uv run ruff check .
|
|
34
|
+
|
|
35
|
+
- name: Type check
|
|
36
|
+
run: uv run mypy src
|
|
37
|
+
|
|
38
|
+
- name: Tests
|
|
39
|
+
run: uv run pytest --cov=om_harness --cov-fail-under=85
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
name: Build & verify distribution
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Install uv
|
|
15
|
+
uses: astral-sh/setup-uv@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
|
|
19
|
+
- name: Run checks
|
|
20
|
+
run: |
|
|
21
|
+
uv sync --frozen
|
|
22
|
+
uv run ruff format --check .
|
|
23
|
+
uv run ruff check .
|
|
24
|
+
uv run mypy src
|
|
25
|
+
uv run pytest
|
|
26
|
+
|
|
27
|
+
- name: Build distributions
|
|
28
|
+
run: uv build
|
|
29
|
+
|
|
30
|
+
- name: Smoke-test the wheel in a fresh venv
|
|
31
|
+
run: |
|
|
32
|
+
uv venv /tmp/smoke
|
|
33
|
+
VIRTUAL_ENV=/tmp/smoke uv pip install dist/*.whl
|
|
34
|
+
/tmp/smoke/bin/python -c "import om_harness; print(om_harness.__version__)"
|
|
35
|
+
/tmp/smoke/bin/om-harness --version
|
|
36
|
+
|
|
37
|
+
- uses: actions/upload-artifact@v4
|
|
38
|
+
with:
|
|
39
|
+
name: dist
|
|
40
|
+
path: dist/
|
|
41
|
+
|
|
42
|
+
publish-pypi:
|
|
43
|
+
name: Publish to PyPI
|
|
44
|
+
needs: build
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
environment: pypi
|
|
47
|
+
permissions:
|
|
48
|
+
id-token: write # trusted publishing (preferred)
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/download-artifact@v4
|
|
51
|
+
with:
|
|
52
|
+
name: dist
|
|
53
|
+
path: dist/
|
|
54
|
+
|
|
55
|
+
# Uses OIDC trusted publishing when configured on PyPI; otherwise set
|
|
56
|
+
# the API key in the pypi environment as the password variable.
|
|
57
|
+
- name: Publish
|
|
58
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
59
|
+
with:
|
|
60
|
+
password: ${{ secrets.PYPI_API_KEY }}
|
|
61
|
+
|
|
62
|
+
github-release:
|
|
63
|
+
name: GitHub release
|
|
64
|
+
needs: build
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
permissions:
|
|
67
|
+
contents: write
|
|
68
|
+
steps:
|
|
69
|
+
- uses: actions/download-artifact@v4
|
|
70
|
+
with:
|
|
71
|
+
name: dist
|
|
72
|
+
path: dist/
|
|
73
|
+
|
|
74
|
+
- name: Create release
|
|
75
|
+
uses: softprops/action-gh-release@v2
|
|
76
|
+
with:
|
|
77
|
+
files: dist/*
|
|
78
|
+
generate_release_notes: true
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.venv/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
|
|
9
|
+
# Tooling caches
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
.ruff_cache/
|
|
13
|
+
.coverage
|
|
14
|
+
coverage.xml
|
|
15
|
+
htmlcov/
|
|
16
|
+
|
|
17
|
+
# om-harness runtime state (sessions, checkpoints, events)
|
|
18
|
+
.om-harness/
|
|
19
|
+
|
|
20
|
+
# Local secrets / env
|
|
21
|
+
.env
|
|
22
|
+
.env.*
|
|
23
|
+
!.env.example
|
|
24
|
+
|
|
25
|
+
# Editors
|
|
26
|
+
.idea/
|
|
27
|
+
.vscode/
|
|
28
|
+
|
|
29
|
+
# Local agent tooling state
|
|
30
|
+
.mimosa/
|
|
31
|
+
.zcode/
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2026-09-07
|
|
9
|
+
|
|
10
|
+
First stable release of the om-harness coding-agent harness.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Interactive shell** (`om-harness` / `chat`): one-input-box, always-on status
|
|
15
|
+
line (model · thinking · mode · context gauge), keybindings, slash commands
|
|
16
|
+
with autocomplete, and an arrow-key model selector.
|
|
17
|
+
- **Live streaming**: agent thinking, tool calls, file-change diffs, and shell
|
|
18
|
+
command output render in real time during a turn.
|
|
19
|
+
- **`/timeout` slash command** (and `/config set agent_timeout|tool_timeout
|
|
20
|
+
<seconds|off>`): show, set, or disable the whole-turn agent timeout and the
|
|
21
|
+
per-tool-call timeout. `off`/`0` → `None`, which `asyncio.timeout` and
|
|
22
|
+
`wait_for` treat as no timeout; changes apply live and persist to
|
|
23
|
+
`~/.om-harness/config/config.toml`.
|
|
24
|
+
- **Skills**: instruction packs (`SKILL.md`) discovered from `~/.agents/skills`,
|
|
25
|
+
the repo's `.agents/skills`, config extra dirs, and installed plugins; loaded
|
|
26
|
+
on demand via a read-only `skill` tool.
|
|
27
|
+
- **Plugins**: git-installable (`om-harness install`, `plugins`, `uninstall`)
|
|
28
|
+
into `~/.om-harness/plugins`, with provenance records and shallow updates.
|
|
29
|
+
- **Orchestration**: plan-as-data strategies, topological wave scheduling with a
|
|
30
|
+
concurrency semaphore, retries with backoff, and per-task timeouts.
|
|
31
|
+
- **Tool safety**: argv-list execution, shell-metacharacter rejection, hard
|
|
32
|
+
timeouts, output caps, scrubbed environment, and strict repo-root path
|
|
33
|
+
confinement (the `skill` tool is the sole documented exemption — name, not
|
|
34
|
+
path).
|
|
35
|
+
- **Durable state**: SQLite-backed sessions, tasks, and checkpoints; JSONL
|
|
36
|
+
event log; crash-atomic writes; secret redaction at publish time.
|
|
37
|
+
- **Web client**: reference HTML UI with SSE event streaming.
|
|
38
|
+
- **Config**: TOML files (`om-harness.toml` / `pyproject.toml`), env-var
|
|
39
|
+
overrides, and `/config set` with immediate live application.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- **Live-stream freeze**: the REPL live pump drained the event bus by positional
|
|
44
|
+
offset into a 1000-event bounded deque; streaming token deltas (one per delta)
|
|
45
|
+
crossed that cap at ~4800 characters and froze the view while the agent kept
|
|
46
|
+
running. Events now carry a monotonic `seq` and consumers drain via
|
|
47
|
+
`bus.cursor` / `bus.since()`, which is immune to history eviction; history
|
|
48
|
+
window raised to 10,000.
|
|
49
|
+
- **Bottom status bar disappearing during streaming**: it was a prompt
|
|
50
|
+
`bottom_toolbar`, which only exists while `prompt()` is active. A turn now runs
|
|
51
|
+
a minimal pinned app (toolbar-only layout, refreshed each second) under
|
|
52
|
+
`patch_stdout` so output scrolls above the bar; fails safely back to the
|
|
53
|
+
previous plain-await behavior on non-TTY or unsupported terminals.
|
|
54
|
+
|
|
55
|
+
## [Unreleased]
|
|
56
|
+
|
|
57
|
+
<!-- Add new entries here, above the latest release. -->
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Contributing to om-harness
|
|
2
|
+
|
|
3
|
+
Thanks for your interest! This guide gets you from clone to first merged
|
|
4
|
+
PR. It also documents the practices the codebase follows so new code looks
|
|
5
|
+
like old code.
|
|
6
|
+
|
|
7
|
+
## Development setup
|
|
8
|
+
|
|
9
|
+
Requirements: [uv](https://docs.astral.sh/uv/) and Python 3.11+ (any of
|
|
10
|
+
3.11/3.12/3.13).
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
git clone https://github.com/omkumar01/om-harness
|
|
14
|
+
cd om-harness
|
|
15
|
+
uv sync # creates .venv and installs everything
|
|
16
|
+
uv run om-harness --version
|
|
17
|
+
uv run pytest # the full suite runs offline — no API keys needed
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
With provider keys set (`OPENAI_API_KEY`, …) you can also run the live
|
|
21
|
+
integration round-trips:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
uv run pytest -m integration
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quality gates
|
|
28
|
+
|
|
29
|
+
All PRs must pass the same checks CI runs (Linux + Windows, Python 3.11 and
|
|
30
|
+
3.13):
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
make check # or: uv run python scripts/check.py
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
which runs, in order:
|
|
37
|
+
|
|
38
|
+
1. `uv run ruff format --check .`
|
|
39
|
+
2. `uv run ruff check .`
|
|
40
|
+
3. `uv run mypy src`
|
|
41
|
+
4. `uv run pytest --cov=om_harness --cov-fail-under=85`
|
|
42
|
+
|
|
43
|
+
Auto-fix formatting/lint before committing:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
make format # ruff format + ruff check --fix
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Test-driven development
|
|
50
|
+
|
|
51
|
+
This project is developed test-first. Before implementing a module or
|
|
52
|
+
behavior, its contract is expressed as tests; implementation is the minimum
|
|
53
|
+
that passes them, then refactoring. Practically:
|
|
54
|
+
|
|
55
|
+
- tests live in `tests/unit/` (mirroring `src/om_harness/` layout),
|
|
56
|
+
`tests/e2e/` (complete flows in temp git repos), and
|
|
57
|
+
`tests/integration/` (live provider round-trips, deselected by default);
|
|
58
|
+
- the default suite must stay **deterministic and offline** — provider
|
|
59
|
+
calls are scripted with PydanticAI `FunctionModel`/`TestModel`, which
|
|
60
|
+
exercise the real agent loop without network;
|
|
61
|
+
- async race coverage uses sentinels + `asyncio.wait_for`, never real
|
|
62
|
+
sleeps.
|
|
63
|
+
|
|
64
|
+
When fixing a bug: add the failing test that reproduces it first.
|
|
65
|
+
|
|
66
|
+
## Architecture rules
|
|
67
|
+
|
|
68
|
+
These are enforced in review (see [docs/architecture.md](docs/architecture.md)):
|
|
69
|
+
|
|
70
|
+
- `models/` and `config/` are pure: no I/O, no imports from runtime layers.
|
|
71
|
+
- Runtime services depend on each other through narrow protocols (e.g.
|
|
72
|
+
`Coordinator` depends on the `TaskExecutor` protocol, not `AgentRunner`).
|
|
73
|
+
- Presentation (`cli/`, `ui/`) touches the runtime only via
|
|
74
|
+
`harness.Harness` and the event stream.
|
|
75
|
+
- Provider SDK imports live only inside `providers/registry.make_model`.
|
|
76
|
+
- Every user-visible runtime operation publishes a typed `Event`.
|
|
77
|
+
|
|
78
|
+
## Common tasks
|
|
79
|
+
|
|
80
|
+
### Add a tool
|
|
81
|
+
|
|
82
|
+
1. Define the tool in `src/om_harness/tools/<area>.py`:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
class FormatCodeArgs(BaseModel):
|
|
86
|
+
path: str = Field(description="Repo-relative path")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class FormatCode(BaseTool[FormatCodeArgs]):
|
|
90
|
+
name = "format_code"
|
|
91
|
+
description = "Run the repository formatter on one file."
|
|
92
|
+
permission = Permission.mutating
|
|
93
|
+
Args = FormatCodeArgs
|
|
94
|
+
|
|
95
|
+
async def run(self, args: FormatCodeArgs) -> ToolResult:
|
|
96
|
+
path = resolve_in_repo(self.ctx, args.path)
|
|
97
|
+
...
|
|
98
|
+
return ToolResult(output=f"formatted {args.path}")
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
2. Register it in `tools/build_default_registry`.
|
|
102
|
+
3. Add tests: happy path, permission gating (`read_only`/`mutating`/
|
|
103
|
+
`destructive`), error paths, path confinement.
|
|
104
|
+
|
|
105
|
+
### Add a provider
|
|
106
|
+
|
|
107
|
+
For OpenAI-compatible endpoints, **no code is needed** — users register
|
|
108
|
+
them via `models.json` (see [docs/configuration.md](docs/configuration.md));
|
|
109
|
+
the URL safety policy and key handling live in
|
|
110
|
+
`src/om_harness/providers/models_json.py`.
|
|
111
|
+
|
|
112
|
+
For non-OpenAI-compatible providers:
|
|
113
|
+
|
|
114
|
+
1. Add a `ProviderSpec` to `PROVIDERS` in `src/om_harness/providers/base.py`
|
|
115
|
+
(prefix, env keys, default/strong models).
|
|
116
|
+
2. Add a construction branch in `providers/registry.make_model`.
|
|
117
|
+
3. Add a registry test (availability, parsing) and an integration test in
|
|
118
|
+
`tests/integration/` gated on the env key.
|
|
119
|
+
|
|
120
|
+
### Add an orchestration strategy
|
|
121
|
+
|
|
122
|
+
1. Extend `StrategyKind` and the planner heuristic.
|
|
123
|
+
2. Prefer expressing the strategy as plan *shape* (tasks + dependencies);
|
|
124
|
+
only add coordinator behavior when the pattern truly isn't a DAG
|
|
125
|
+
property.
|
|
126
|
+
3. Add deterministic scheduling tests (see the sentinel pattern in
|
|
127
|
+
`tests/unit/test_orchestration.py`).
|
|
128
|
+
|
|
129
|
+
### Change a runtime contract
|
|
130
|
+
|
|
131
|
+
Contracts live in `src/om_harness/models/`. They are versioned API for
|
|
132
|
+
sessions/checkpoints on disk and event consumers, so:
|
|
133
|
+
|
|
134
|
+
- keep changes additive where possible,
|
|
135
|
+
- if serialization changes, bump `HarnessConfig.version` and note a
|
|
136
|
+
migration in the PR description.
|
|
137
|
+
|
|
138
|
+
## Running the CLI locally
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
uv run om-harness init
|
|
142
|
+
uv run om-harness run "summarize this repo" -v
|
|
143
|
+
uv run om-harness chat
|
|
144
|
+
# web reference client (needs the `web` extra):
|
|
145
|
+
uv sync --extra web
|
|
146
|
+
uv run python -m om_harness.ui.web.server
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
om-harness state lives in `.om-harness/` of whatever repository you're in —
|
|
150
|
+
safe to delete at any time.
|
|
151
|
+
|
|
152
|
+
## Releasing
|
|
153
|
+
|
|
154
|
+
Releases publish to **both** PyPI and GitHub Releases automatically:
|
|
155
|
+
|
|
156
|
+
1. Bump `version` in `pyproject.toml` and `__version__` in
|
|
157
|
+
`src/om_harness/__init__.py` (same value) in a PR.
|
|
158
|
+
2. After it merges, tag the release and push the tag:
|
|
159
|
+
`git tag v0.2.0 && git push origin v0.2.0`.
|
|
160
|
+
3. The `Release` workflow builds the wheel/sdist, runs all checks,
|
|
161
|
+
smoke-tests the wheel in a fresh venv, publishes to PyPI, and creates a
|
|
162
|
+
GitHub Release with the artifacts.
|
|
163
|
+
|
|
164
|
+
Setup (maintainers, one-time):
|
|
165
|
+
|
|
166
|
+
- Add a `PYPI_API_KEY` secret (or configure PyPI trusted publishing for the
|
|
167
|
+
`pypi` environment) — the workflow supports both.
|
|
168
|
+
- PyPI project name: `om-harness`; the CLI command is also `om-harness`.
|
|
169
|
+
|
|
170
|
+
Users can install either way:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
uv tool install om-harness # PyPI
|
|
174
|
+
uv tool install git+https://github.com/omkumar01/om-harness # GitHub
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Commit / PR conventions
|
|
178
|
+
|
|
179
|
+
- Small, focused PRs with tests.
|
|
180
|
+
- Conventional commit style is appreciated (`feat:`, `fix:`, `docs:`,
|
|
181
|
+
`refactor:`, `test:`, `chore:`).
|
|
182
|
+
- The CI must be green before review.
|
|
183
|
+
|
|
184
|
+
## Reporting issues
|
|
185
|
+
|
|
186
|
+
Include: the command run, `om-harness doctor --json` output, the relevant
|
|
187
|
+
`.om-harness/events/*.jsonl` excerpt (redacted automatically), and expected
|
|
188
|
+
vs. actual behavior. **Never paste API keys** — om-harness redacts its own
|
|
189
|
+
logs; check before you paste anything manually.
|
om_harness-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 om-harness contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
.PHONY: check format lint type tests coverage e2e clean
|
|
2
|
+
|
|
3
|
+
# Full verification suite (same commands CI runs).
|
|
4
|
+
check:
|
|
5
|
+
uv run python scripts/check.py
|
|
6
|
+
|
|
7
|
+
format:
|
|
8
|
+
uv run ruff format .
|
|
9
|
+
uv run ruff check --fix .
|
|
10
|
+
|
|
11
|
+
lint:
|
|
12
|
+
uv run ruff format --check .
|
|
13
|
+
uv run ruff check .
|
|
14
|
+
|
|
15
|
+
type:
|
|
16
|
+
uv run mypy src
|
|
17
|
+
|
|
18
|
+
tests:
|
|
19
|
+
uv run pytest
|
|
20
|
+
|
|
21
|
+
coverage:
|
|
22
|
+
uv run pytest --cov=om_harness --cov-report=term-missing
|
|
23
|
+
|
|
24
|
+
e2e:
|
|
25
|
+
uv run pytest tests/e2e -v
|
|
26
|
+
|
|
27
|
+
clean:
|
|
28
|
+
rm -rf .pytest_cache .mypy_cache .ruff_cache .coverage htmlcov dist
|