docs: add repo docs and CI scaffolding

This commit is contained in:
tjb-tech
2026-04-03 04:58:11 +00:00
parent 56712df04c
commit aab665492f
29 changed files with 447 additions and 34 deletions
+41
View File
@@ -0,0 +1,41 @@
name: Bug report
description: Report a reproducible problem in OpenHarness
title: "[Bug]: "
labels:
- bug
body:
- type: markdown
attributes:
value: |
Thanks for reporting a bug. Please include concrete steps, environment details, and error output.
- type: textarea
id: summary
attributes:
label: What happened?
description: Describe the bug and the behavior you expected.
validations:
required: true
- type: textarea
id: repro
attributes:
label: Steps to reproduce
description: Include commands, prompts, or config that reliably reproduces the issue.
placeholder: |
1. Run `uv run oh ...`
2. Trigger ...
3. Observe ...
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment
description: OS, Python version, Node version if relevant, provider/base URL, and install method.
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant logs or screenshots
description: Paste error output, stack traces, or terminal screenshots.
render: shell
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: true
contact_links:
- name: Contribution guide
url: https://github.com/HKUDS/OpenHarness/blob/main/CONTRIBUTING.md
about: Read local setup, validation, and PR expectations before contributing.
- name: Showcase ideas
url: https://github.com/HKUDS/OpenHarness/blob/main/docs/SHOWCASE.md
about: See example workflows and suggest new real-world use cases.
@@ -0,0 +1,33 @@
name: Feature request
description: Propose a focused improvement or missing workflow
title: "[Feature]: "
labels:
- enhancement
body:
- type: markdown
attributes:
value: |
Please describe the workflow gap as concretely as possible. Small, scoped requests are easier to prioritize.
- type: textarea
id: problem
attributes:
label: What workflow is missing or painful today?
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed change
description: Explain the behavior you want and any relevant CLI, UI, or provider details.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: If you have a workaround or competing approach, include it here.
- type: textarea
id: extra
attributes:
label: Additional context
description: Links to related issues, code references, or benchmarks.
+15
View File
@@ -0,0 +1,15 @@
## Summary
- What problem does this PR solve?
- What changed?
## Validation
- [ ] `uv run ruff check src tests scripts`
- [ ] `uv run pytest -q`
- [ ] `cd frontend/terminal && npx tsc --noEmit` (if frontend touched)
## Notes
- Related issue:
- Follow-up work:
+83
View File
@@ -0,0 +1,83 @@
name: CI
on:
push:
branches:
- main
pull_request:
jobs:
python-tests:
name: Python tests (${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version:
- "3.10"
- "3.11"
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
- name: Install dependencies
run: uv sync --frozen --extra dev
- name: Run test suite
run: uv run pytest -q
python-quality:
name: Python quality
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
- name: Install dependencies
run: uv sync --frozen --extra dev
- name: Ruff
run: uv run ruff check src tests scripts
frontend-typecheck:
name: Frontend typecheck
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontend/terminal
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
cache-dependency-path: frontend/terminal/package-lock.json
- name: Install frontend dependencies
run: npm ci
- name: TypeScript check
run: npx tsc --noEmit
+26
View File
@@ -0,0 +1,26 @@
# Changelog
All notable changes to OpenHarness should be recorded in this file.
The format is based on Keep a Changelog, and this project currently tracks changes in a lightweight, repository-oriented way.
## [Unreleased]
### Added
- GitHub Actions CI workflow for Python linting, tests, and frontend TypeScript checks.
- `CONTRIBUTING.md` with local setup, validation commands, and PR expectations.
- `docs/SHOWCASE.md` with concrete OpenHarness usage patterns and demo commands.
- GitHub issue templates and a pull request template.
### Changed
- README now links to contribution docs, changelog, showcase material, and provider compatibility guidance.
- README quick start now includes a one-command demo and clearer provider compatibility notes.
## [0.1.0] - 2026-04-01
### Added
- Initial public release of OpenHarness.
- Core agent loop, tool registry, permission system, hooks, skills, plugins, MCP support, and terminal UI.
+68
View File
@@ -0,0 +1,68 @@
# Contributing to OpenHarness
OpenHarness is an open-source agent harness focused on clarity, hackability, and compatibility with Claude-style workflows.
## Ways to contribute
- Fix bugs or tighten edge-case handling in the harness runtime.
- Improve docs, onboarding, examples, and architecture notes.
- Add tests for tools, permissions, plugins, MCP, or multi-agent flows.
- Contribute new skills, plugins, or provider compatibility improvements.
- Share real usage patterns that can be added to [`docs/SHOWCASE.md`](docs/SHOWCASE.md).
## Development setup
```bash
git clone https://github.com/HKUDS/OpenHarness.git
cd OpenHarness
uv sync --extra dev
```
If you want to work on the React terminal UI as well:
```bash
cd frontend/terminal
npm ci
cd ../..
```
## Local checks
Run the same core checks that CI runs before opening a PR:
```bash
uv run ruff check src tests scripts
uv run pytest -q
```
Frontend sanity check:
```bash
cd frontend/terminal
npx tsc --noEmit
```
## Pull request expectations
- Keep PRs scoped. Small, reviewable changes merge faster than broad rewrites.
- Include the problem, the change, and how you verified it.
- Add or update tests when behavior changes.
- Update docs when CLI flags, workflows, or compatibility claims change.
- Add a short entry under `Unreleased` in [`CHANGELOG.md`](CHANGELOG.md) for user-visible changes.
- If you are improving type coverage, feel free to run `uv run mypy src/openharness`, but it is not yet a required green check for the whole repo.
## Documentation and community contributions
Issue [#7](https://github.com/HKUDS/OpenHarness/issues/7) surfaced several high-value docs needs. Useful contributions in that area include:
- README accuracy improvements and compatibility notes.
- Short, reproducible examples for common workflows.
- Showcase entries based on real usage rather than generic marketing claims.
- Contribution and maintenance docs that make the repo easier to navigate.
## Reporting bugs and proposing features
- Use the GitHub issue templates when possible.
- Include environment details, exact commands, and error output for bugs.
- For features, explain the concrete workflow gap and expected behavior.
- If the request is mostly documentation or maintenance related, say that explicitly so it can be scoped as a docs PR.
+78 -5
View File
@@ -13,11 +13,12 @@
</p>
<p align="center">
<img src="https://img.shields.io/badge/python-≥3.11-blue?logo=python&logoColor=white" alt="Python">
<img src="https://img.shields.io/badge/python-≥3.10-blue?logo=python&logoColor=white" alt="Python">
<img src="https://img.shields.io/badge/React+Ink-TUI-61DAFB?logo=react&logoColor=white" alt="React">
<img src="https://img.shields.io/badge/pytest-114_pass-brightgreen" alt="Pytest">
<img src="https://img.shields.io/badge/E2E-6_suites-orange" alt="E2E">
<img src="https://img.shields.io/badge/output-text_|_json_|_stream--json-blueviolet" alt="Output">
<a href="https://github.com/HKUDS/OpenHarness/actions/workflows/ci.yml"><img src="https://github.com/HKUDS/OpenHarness/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
<a href="https://github.com/HKUDS/.github/blob/main/profile/README.md"><img src="https://img.shields.io/badge/Feishu-Group-E9DBFC?style=flat&logo=feishu&logoColor=white" alt="Feishu"></a>
<a href="https://github.com/HKUDS/.github/blob/main/profile/README.md"><img src="https://img.shields.io/badge/WeChat-Group-C5EAB4?style=flat&logo=wechat&logoColor=white" alt="WeChat"></a>
</p>
@@ -35,7 +36,6 @@ Supports CLI agent integration including OpenClaw, nanobot, Cursor, and more.
</p>
---
## ✨ OpenHarness's Key Harness Features
<table align="center" width="100%">
@@ -164,16 +164,31 @@ OpenHarness is an open-source Python implementation designed for **researchers,
- **2026-04-01** 🎨 **v0.1.0** — Initial **OpenHarness** open-source release featuring complete Harness architecture:
<p align="center">
<strong>Start here:</strong>
<a href="#-quick-start">Quick Start</a> ·
<a href="#-provider-compatibility">Provider Compatibility</a> ·
<a href="docs/SHOWCASE.md">Showcase</a> ·
<a href="CONTRIBUTING.md">Contributing</a> ·
<a href="CHANGELOG.md">Changelog</a>
</p>
---
## 🚀 Quick Start
### Prerequisites
- **Python 3.11+** and [uv](https://docs.astral.sh/uv/)
- **Node.js 18+** (for the React terminal UI)
- **Python 3.10+** and [uv](https://docs.astral.sh/uv/)
- **Node.js 18+** (optional, for the React terminal UI)
- An LLM API key
### One-Command Demo
```bash
ANTHROPIC_API_KEY=your_key uv run oh -p "Inspect this repository and list the top 3 refactors"
```
### Install & Run
```bash
@@ -209,6 +224,20 @@ oh -p "List all functions in main.py" --output-format json
oh -p "Fix the bug" --output-format stream-json
```
## 🔌 Provider Compatibility
OpenHarness currently detects and adapts to a small set of provider profiles in code. The table below is intentionally conservative and reflects the profiles implemented in `src/openharness/api/provider.py`.
| Provider profile | Detection signal | Auth kind | Voice mode | Notes |
|------------------|------------------|-----------|------------|-------|
| **Anthropic** | Default when no custom `ANTHROPIC_BASE_URL` is set | API key | Not wired in current build | Default Claude-oriented setup |
| **Moonshot / Kimi** | `ANTHROPIC_BASE_URL` contains `moonshot` or model starts with `kimi` | API key | Not wired in current build | Works through an Anthropic-compatible endpoint |
| **Vertex-compatible** | Base URL contains `vertex` or `aiplatform` | GCP | Not wired in current build | Good fit for Anthropic-style gateways on Vertex |
| **Bedrock-compatible** | Base URL contains `bedrock` | AWS | Not wired in current build | Intended for Bedrock-style deployments |
| **Generic Anthropic-compatible** | Any other explicit `ANTHROPIC_BASE_URL` | API key | Not wired in current build | Useful for proxies and internal gateways |
If you are evaluating cross-provider workflows or want a concrete demo path, start with Anthropic or the Kimi example above, then compare behavior against your own compatible endpoint.
---
## 🏗️ Harness Architecture
@@ -254,6 +283,20 @@ while True:
The model decides **what** to do. The harness handles **how** — safely, efficiently, with full observability.
### Harness Flow
```mermaid
flowchart LR
U[User Prompt] --> C[CLI or React TUI]
C --> R[RuntimeBundle]
R --> Q[QueryEngine]
Q --> A[Anthropic-compatible API Client]
A -->|tool_use| T[Tool Registry]
T --> P[Permissions + Hooks]
P --> X[Files Shell Web MCP Tasks]
X --> Q
```
---
## ✨ Features
@@ -317,6 +360,16 @@ oh plugin install <source>
oh plugin enable <name>
```
### 🤝 Ecosystem Workflows
OpenHarness is useful as a lightweight harness layer around Claude-style tooling conventions:
- **OpenClaw-oriented workflows** can reuse Markdown-first knowledge and command-driven collaboration patterns.
- **Claude-style plugins and skills** stay portable because OpenHarness keeps those formats familiar.
- **ClawTeam-style multi-agent work** maps well onto the built-in team, task, and background execution primitives.
For concrete usage ideas instead of generic claims, see [`docs/SHOWCASE.md`](docs/SHOWCASE.md).
### 🛡️ Permissions
Multi-level safety with fine-grained control:
@@ -443,6 +496,20 @@ Add commands in `commands/*.md`, hooks in `hooks/hooks.json`, agents in `agents/
---
## 🌍 Showcase
OpenHarness is most useful when treated as a small, inspectable harness you can adapt to a real workflow:
- **Repo coding assistant** for reading code, patching files, and running checks locally.
- **Headless scripting tool** for `json` and `stream-json` output in automation flows.
- **Plugin and skill testbed** for experimenting with Claude-style extensions.
- **Multi-agent prototype harness** for task delegation and background execution.
- **Provider comparison sandbox** across Anthropic-compatible backends.
See [`docs/SHOWCASE.md`](docs/SHOWCASE.md) for short, reproducible examples.
---
## 🤝 Contributing
OpenHarness is a **community-driven research project**. We welcome contributions in:
@@ -460,11 +527,17 @@ OpenHarness is a **community-driven research project**. We welcome contributions
```bash
# Development setup
git clone https://github.com/HKUDS/OpenHarness.git
cd openharness
cd OpenHarness
uv sync --extra dev
uv run pytest -q # Verify everything works
```
Useful contributor entry points:
- [`CONTRIBUTING.md`](CONTRIBUTING.md) for setup, checks, and PR expectations
- [`CHANGELOG.md`](CHANGELOG.md) for user-visible changes
- [`docs/SHOWCASE.md`](docs/SHOWCASE.md) for real-world usage patterns worth documenting
---
## 📄 License
+80
View File
@@ -0,0 +1,80 @@
# OpenHarness Showcase
This page collects concrete ways to use OpenHarness without overselling the project. Each example is intended to be small, reproducible, and easy to extend.
## 1. Repository-aware coding assistant
Use OpenHarness as a lightweight local coding agent for reading code, making edits, and running validation commands.
```bash
uv run oh
```
Example prompt:
```text
Review this repo, identify the highest-risk bug, patch it, and run the relevant tests.
```
## 2. Headless automation for scripts and CI
The print mode is useful when you want structured output in shell pipelines or automation jobs.
```bash
uv run oh -p "Summarize the purpose of this repository" --output-format json
uv run oh -p "List files that define the permission system" --output-format stream-json
```
## 3. Skill and plugin playground
OpenHarness can load Markdown skills and Claude-style plugin layouts, which makes it useful for experimentation with custom workflows.
Examples:
- Put a custom skill in `~/.openharness/skills/`.
- Install a plugin into `~/.openharness/plugins/`.
- Use the same workflow conventions across multiple local projects.
## 4. Multi-agent and background task experiments
The repo includes team coordination primitives, background task management, and task inspection tools.
Example prompts:
```text
Spawn a worker to audit the test suite while you inspect the CLI command registry.
```
```text
Create a background task that runs the slow integration script and report back when it finishes.
```
## 5. Provider compatibility testbed
OpenHarness is useful when you need to compare Anthropic-compatible backends behind one harness.
Typical scenarios:
- Default Anthropic setup.
- Moonshot/Kimi through an Anthropic-compatible endpoint.
- Vertex-compatible and Bedrock-compatible gateways.
- Internal proxies that expose an Anthropic-style API surface.
See the provider compatibility table in [`README.md`](../README.md#-provider-compatibility).
## 6. Documentation-first onboarding
If you are evaluating the project rather than contributing code, start here:
- [`README.md`](../README.md) for install, usage, and architecture.
- [`CONTRIBUTING.md`](../CONTRIBUTING.md) for contributor workflow.
- [`CHANGELOG.md`](../CHANGELOG.md) for visible repo changes.
## How to contribute a showcase entry
Good showcase additions are:
- Based on a real workflow you ran.
- Short enough to reproduce locally.
- Honest about prerequisites and limitations.
- Focused on what OpenHarness makes easier, not on generic LLM claims.
-1
View File
@@ -7,7 +7,6 @@ import json
import os
import subprocess
import sys
import tempfile
from pathlib import Path
# Colors for output
+1 -2
View File
@@ -10,7 +10,6 @@ import asyncio
import os
import subprocess
import sys
import tempfile
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
@@ -43,7 +42,7 @@ def _run_oh(*args: str, timeout: int = 90) -> subprocess.CompletedProcess:
async def test_api_retry_config() -> tuple[bool, str]:
"""Test that retry configuration is properly set up."""
from openharness.api.client import MAX_RETRIES, RETRYABLE_STATUS_CODES, _is_retryable, _get_retry_delay
from openharness.api.client import MAX_RETRIES, RETRYABLE_STATUS_CODES, _get_retry_delay
if MAX_RETRIES != 3:
return False, f"Expected MAX_RETRIES=3, got {MAX_RETRIES}"
-1
View File
@@ -119,7 +119,6 @@ async def test_real_model_headless() -> tuple[bool, str]:
if not settings["api_key"]:
return False, "ANTHROPIC_AUTH_TOKEN not set"
from openharness.api.client import AnthropicApiClient
from openharness.ui.app import run_print_mode
try:
-1
View File
@@ -10,7 +10,6 @@ from __future__ import annotations
import json
import os
import sys
import time
from pathlib import Path
GREEN = "\033[92m"
-1
View File
@@ -12,7 +12,6 @@ import os
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
-2
View File
@@ -55,8 +55,6 @@ def test_command_picker_shows() -> tuple[bool, str]:
try:
child.expect(pexpect.EOF, timeout=25)
output = child.before or ""
# Check for key UI elements
has_shortcuts = "send" in output.lower() or "enter" in output.lower()
has_welcome = "Oh my Harness!" in output
if has_welcome:
return True, f"TUI launched with welcome banner and shortcuts. Output: {len(output)} chars"
+9 -5
View File
@@ -1,11 +1,15 @@
"""API client exports."""
"""API exports."""
from openharness.api.provider import ProviderInfo, auth_status, detect_provider
__all__ = ["ProviderInfo", "auth_status", "detect_provider"]
from openharness.api.client import AnthropicApiClient
from openharness.api.errors import OpenHarnessApiError
from openharness.api.provider import ProviderInfo, auth_status, detect_provider
from openharness.api.usage import UsageSnapshot
__all__ = ["AnthropicApiClient", "OpenHarnessApiError", "UsageSnapshot"]
__all__ = [
"AnthropicApiClient",
"OpenHarnessApiError",
"ProviderInfo",
"UsageSnapshot",
"auth_status",
"detect_provider",
]
-1
View File
@@ -3,7 +3,6 @@
from __future__ import annotations
import asyncio
import time
from dataclasses import dataclass
from pathlib import Path
+1 -1
View File
@@ -13,7 +13,7 @@ from typing import Any
import httpx
from openharness.api.client import ApiMessageCompleteEvent, ApiMessageRequest, SupportsStreamingMessages
from openharness.engine.messages import ConversationMessage, TextBlock
from openharness.engine.messages import ConversationMessage
from openharness.hooks.events import HookEvent
from openharness.hooks.loader import HookRegistry
from openharness.hooks.schemas import (
+1 -2
View File
@@ -3,8 +3,7 @@
from __future__ import annotations
import fnmatch
from dataclasses import dataclass, field
from typing import Any
from dataclasses import dataclass
from openharness.config.settings import PermissionSettings
from openharness.permissions.modes import PermissionMode
-1
View File
@@ -5,7 +5,6 @@ from __future__ import annotations
from dataclasses import dataclass, field
from pathlib import Path
from openharness.hooks.schemas import HookDefinition
from openharness.mcp.types import McpServerConfig
from openharness.plugins.schemas import PluginManifest
from openharness.skills.types import SkillDefinition
-1
View File
@@ -3,7 +3,6 @@
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
from openharness.config.paths import get_cron_registry_path
-1
View File
@@ -8,7 +8,6 @@ import shlex
import time
from dataclasses import replace
from pathlib import Path
from typing import Any
from uuid import uuid4
from openharness.config.paths import get_tasks_dir
-1
View File
@@ -8,7 +8,6 @@ import json
import os
import sys
from dataclasses import dataclass
from typing import Any
from uuid import uuid4
from openharness.api.client import SupportsStreamingMessages
-1
View File
@@ -6,7 +6,6 @@ from rich.console import Console
from rich.markdown import Markdown
from rich.panel import Panel
from rich.syntax import Syntax
from rich.text import Text
from openharness.engine.stream_events import (
AssistantTextDelta,
-1
View File
@@ -2,7 +2,6 @@
from __future__ import annotations
import asyncio
from pathlib import Path
import pytest
-1
View File
@@ -2,7 +2,6 @@
import pytest
from openharness.coordinator import get_team_registry
from openharness.coordinator.coordinator_mode import TeamRegistry
@@ -8,7 +8,6 @@ from openharness.api.usage import UsageSnapshot
from openharness.engine.messages import ConversationMessage, TextBlock
from openharness.services.session_storage import (
export_session_markdown,
get_project_session_dir,
load_session_snapshot,
save_session_snapshot,
)
-1
View File
@@ -7,7 +7,6 @@ from pathlib import Path
import pytest
from openharness.tasks import get_task_manager
from openharness.tasks.manager import BackgroundTaskManager
+3 -3
View File
@@ -146,7 +146,7 @@ async def test_agent_send_message_flow_restarts_completed_agent(tmp_path: Path,
)
task_id = create_result.output.split()[-1]
for _ in range(40):
for _ in range(80):
output = await task_output.execute(task_output.input_model(task_id=task_id), context)
if "AGENT_ECHO:ready" in output.output:
break
@@ -160,8 +160,8 @@ async def test_agent_send_message_flow_restarts_completed_agent(tmp_path: Path,
)
assert send_result.is_error is False
await asyncio.sleep(0.1)
for _ in range(40):
await asyncio.sleep(0.2)
for _ in range(80):
output = await task_output.execute(task_output.input_model(task_id=task_id), context)
if "AGENT_ECHO:agent ping" in output.output:
break