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.
Files changed (106) hide show
  1. om_harness-1.0.0/.env.example +20 -0
  2. om_harness-1.0.0/.github/workflows/ci.yml +39 -0
  3. om_harness-1.0.0/.github/workflows/release.yml +78 -0
  4. om_harness-1.0.0/.gitignore +31 -0
  5. om_harness-1.0.0/CHANGELOG.md +57 -0
  6. om_harness-1.0.0/CONTRIBUTING.md +189 -0
  7. om_harness-1.0.0/LICENSE +21 -0
  8. om_harness-1.0.0/Makefile +28 -0
  9. om_harness-1.0.0/PKG-INFO +420 -0
  10. om_harness-1.0.0/README.md +392 -0
  11. om_harness-1.0.0/docs/architecture.md +362 -0
  12. om_harness-1.0.0/docs/configuration.md +240 -0
  13. om_harness-1.0.0/docs/design.md +259 -0
  14. om_harness-1.0.0/docs/examples/models.json +62 -0
  15. om_harness-1.0.0/pyproject.toml +104 -0
  16. om_harness-1.0.0/scripts/check.py +60 -0
  17. om_harness-1.0.0/scripts/demo.py +143 -0
  18. om_harness-1.0.0/src/om_harness/__init__.py +3 -0
  19. om_harness-1.0.0/src/om_harness/cli/__init__.py +0 -0
  20. om_harness-1.0.0/src/om_harness/cli/app.py +502 -0
  21. om_harness-1.0.0/src/om_harness/config/__init__.py +0 -0
  22. om_harness-1.0.0/src/om_harness/config/loader.py +259 -0
  23. om_harness-1.0.0/src/om_harness/config/paths.py +71 -0
  24. om_harness-1.0.0/src/om_harness/config/secrets.py +105 -0
  25. om_harness-1.0.0/src/om_harness/config/user_settings.py +283 -0
  26. om_harness-1.0.0/src/om_harness/context/__init__.py +0 -0
  27. om_harness-1.0.0/src/om_harness/context/assembler.py +198 -0
  28. om_harness-1.0.0/src/om_harness/context/budget.py +40 -0
  29. om_harness-1.0.0/src/om_harness/context/repo_index.py +137 -0
  30. om_harness-1.0.0/src/om_harness/harness.py +460 -0
  31. om_harness-1.0.0/src/om_harness/models/__init__.py +41 -0
  32. om_harness-1.0.0/src/om_harness/models/events.py +108 -0
  33. om_harness-1.0.0/src/om_harness/models/session.py +78 -0
  34. om_harness-1.0.0/src/om_harness/models/task.py +151 -0
  35. om_harness-1.0.0/src/om_harness/orchestration/__init__.py +0 -0
  36. om_harness-1.0.0/src/om_harness/orchestration/coordinator.py +174 -0
  37. om_harness-1.0.0/src/om_harness/orchestration/planner.py +111 -0
  38. om_harness-1.0.0/src/om_harness/plugins/__init__.py +207 -0
  39. om_harness-1.0.0/src/om_harness/providers/__init__.py +0 -0
  40. om_harness-1.0.0/src/om_harness/providers/base.py +79 -0
  41. om_harness-1.0.0/src/om_harness/providers/mock.py +63 -0
  42. om_harness-1.0.0/src/om_harness/providers/models_json.py +284 -0
  43. om_harness-1.0.0/src/om_harness/providers/registry.py +304 -0
  44. om_harness-1.0.0/src/om_harness/providers/router.py +78 -0
  45. om_harness-1.0.0/src/om_harness/runtime/__init__.py +0 -0
  46. om_harness-1.0.0/src/om_harness/runtime/agent.py +154 -0
  47. om_harness-1.0.0/src/om_harness/runtime/bus.py +152 -0
  48. om_harness-1.0.0/src/om_harness/runtime/runner.py +253 -0
  49. om_harness-1.0.0/src/om_harness/runtime/session.py +168 -0
  50. om_harness-1.0.0/src/om_harness/runtime/store.py +152 -0
  51. om_harness-1.0.0/src/om_harness/skills/__init__.py +114 -0
  52. om_harness-1.0.0/src/om_harness/tools/__init__.py +63 -0
  53. om_harness-1.0.0/src/om_harness/tools/approval.py +76 -0
  54. om_harness-1.0.0/src/om_harness/tools/base.py +113 -0
  55. om_harness-1.0.0/src/om_harness/tools/envinfo.py +42 -0
  56. om_harness-1.0.0/src/om_harness/tools/files.py +230 -0
  57. om_harness-1.0.0/src/om_harness/tools/git.py +154 -0
  58. om_harness-1.0.0/src/om_harness/tools/registry.py +148 -0
  59. om_harness-1.0.0/src/om_harness/tools/shell.py +299 -0
  60. om_harness-1.0.0/src/om_harness/tools/skill.py +52 -0
  61. om_harness-1.0.0/src/om_harness/tools/testing.py +74 -0
  62. om_harness-1.0.0/src/om_harness/ui/__init__.py +0 -0
  63. om_harness-1.0.0/src/om_harness/ui/components.py +331 -0
  64. om_harness-1.0.0/src/om_harness/ui/repl.py +1262 -0
  65. om_harness-1.0.0/src/om_harness/ui/slash.py +205 -0
  66. om_harness-1.0.0/src/om_harness/ui/terminal.py +61 -0
  67. om_harness-1.0.0/src/om_harness/ui/web/server.py +140 -0
  68. om_harness-1.0.0/src/om_harness/ui/web/static/index.html +97 -0
  69. om_harness-1.0.0/tests/__init__.py +0 -0
  70. om_harness-1.0.0/tests/conftest.py +36 -0
  71. om_harness-1.0.0/tests/e2e/__init__.py +0 -0
  72. om_harness-1.0.0/tests/e2e/test_coding_flow.py +194 -0
  73. om_harness-1.0.0/tests/e2e/test_local_gateway.py +150 -0
  74. om_harness-1.0.0/tests/integration/__init__.py +0 -0
  75. om_harness-1.0.0/tests/integration/test_providers_live.py +44 -0
  76. om_harness-1.0.0/tests/unit/__init__.py +0 -0
  77. om_harness-1.0.0/tests/unit/conftest.py +22 -0
  78. om_harness-1.0.0/tests/unit/test_approval.py +162 -0
  79. om_harness-1.0.0/tests/unit/test_bus.py +126 -0
  80. om_harness-1.0.0/tests/unit/test_cli.py +241 -0
  81. om_harness-1.0.0/tests/unit/test_config.py +130 -0
  82. om_harness-1.0.0/tests/unit/test_context.py +180 -0
  83. om_harness-1.0.0/tests/unit/test_entry_point.py +14 -0
  84. om_harness-1.0.0/tests/unit/test_harness.py +167 -0
  85. om_harness-1.0.0/tests/unit/test_index_cache.py +93 -0
  86. om_harness-1.0.0/tests/unit/test_model_selection.py +74 -0
  87. om_harness-1.0.0/tests/unit/test_models.py +101 -0
  88. om_harness-1.0.0/tests/unit/test_models_json.py +408 -0
  89. om_harness-1.0.0/tests/unit/test_orchestration.py +294 -0
  90. om_harness-1.0.0/tests/unit/test_paths.py +76 -0
  91. om_harness-1.0.0/tests/unit/test_plugins.py +281 -0
  92. om_harness-1.0.0/tests/unit/test_providers.py +184 -0
  93. om_harness-1.0.0/tests/unit/test_repl.py +603 -0
  94. om_harness-1.0.0/tests/unit/test_runner.py +245 -0
  95. om_harness-1.0.0/tests/unit/test_session_manager.py +80 -0
  96. om_harness-1.0.0/tests/unit/test_shell.py +465 -0
  97. om_harness-1.0.0/tests/unit/test_shell_pipes.py +114 -0
  98. om_harness-1.0.0/tests/unit/test_skills.py +114 -0
  99. om_harness-1.0.0/tests/unit/test_store.py +104 -0
  100. om_harness-1.0.0/tests/unit/test_tools_files.py +112 -0
  101. om_harness-1.0.0/tests/unit/test_tools_skill.py +64 -0
  102. om_harness-1.0.0/tests/unit/test_tools_system.py +165 -0
  103. om_harness-1.0.0/tests/unit/test_user_config.py +116 -0
  104. om_harness-1.0.0/tests/unit/test_user_settings.py +113 -0
  105. om_harness-1.0.0/tests/unit/test_web.py +75 -0
  106. 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.
@@ -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