+
---
## ๐ 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
oh plugin enable
```
+### ๐ค 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
diff --git a/docs/SHOWCASE.md b/docs/SHOWCASE.md
new file mode 100644
index 0000000..4bc4707
--- /dev/null
+++ b/docs/SHOWCASE.md
@@ -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.
diff --git a/scripts/test_cli_flags.py b/scripts/test_cli_flags.py
index 3b3be83..019f46e 100644
--- a/scripts/test_cli_flags.py
+++ b/scripts/test_cli_flags.py
@@ -7,7 +7,6 @@ import json
import os
import subprocess
import sys
-import tempfile
from pathlib import Path
# Colors for output
diff --git a/scripts/test_harness_features.py b/scripts/test_harness_features.py
index 769d4ec..e66c491 100644
--- a/scripts/test_harness_features.py
+++ b/scripts/test_harness_features.py
@@ -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}"
diff --git a/scripts/test_headless_rendering.py b/scripts/test_headless_rendering.py
index cac49e9..13bdb13 100644
--- a/scripts/test_headless_rendering.py
+++ b/scripts/test_headless_rendering.py
@@ -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:
diff --git a/scripts/test_react_tui_redesign.py b/scripts/test_react_tui_redesign.py
index 2c60cd4..a641562 100644
--- a/scripts/test_react_tui_redesign.py
+++ b/scripts/test_react_tui_redesign.py
@@ -10,7 +10,6 @@ from __future__ import annotations
import json
import os
import sys
-import time
from pathlib import Path
GREEN = "\033[92m"
diff --git a/scripts/test_real_skills_plugins.py b/scripts/test_real_skills_plugins.py
index 9273fe7..a82c17d 100644
--- a/scripts/test_real_skills_plugins.py
+++ b/scripts/test_real_skills_plugins.py
@@ -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"))
diff --git a/scripts/test_tui_interactions.py b/scripts/test_tui_interactions.py
index 922b65b..a6e6eba 100644
--- a/scripts/test_tui_interactions.py
+++ b/scripts/test_tui_interactions.py
@@ -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"
diff --git a/src/openharness/api/__init__.py b/src/openharness/api/__init__.py
index 4a3051f..a04ab3a 100644
--- a/src/openharness/api/__init__.py
+++ b/src/openharness/api/__init__.py
@@ -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",
+]
diff --git a/src/openharness/bridge/manager.py b/src/openharness/bridge/manager.py
index 2b86dfc..75c467c 100644
--- a/src/openharness/bridge/manager.py
+++ b/src/openharness/bridge/manager.py
@@ -3,7 +3,6 @@
from __future__ import annotations
import asyncio
-import time
from dataclasses import dataclass
from pathlib import Path
diff --git a/src/openharness/hooks/executor.py b/src/openharness/hooks/executor.py
index 5b3cd8b..b91504b 100644
--- a/src/openharness/hooks/executor.py
+++ b/src/openharness/hooks/executor.py
@@ -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 (
diff --git a/src/openharness/permissions/checker.py b/src/openharness/permissions/checker.py
index d601bcb..7def6aa 100644
--- a/src/openharness/permissions/checker.py
+++ b/src/openharness/permissions/checker.py
@@ -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
diff --git a/src/openharness/plugins/types.py b/src/openharness/plugins/types.py
index 7fd14b4..00e66a7 100644
--- a/src/openharness/plugins/types.py
+++ b/src/openharness/plugins/types.py
@@ -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
diff --git a/src/openharness/services/cron.py b/src/openharness/services/cron.py
index a2ecc1e..6b05ab5 100644
--- a/src/openharness/services/cron.py
+++ b/src/openharness/services/cron.py
@@ -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
diff --git a/src/openharness/tasks/manager.py b/src/openharness/tasks/manager.py
index 5a2f181..b3db9c7 100644
--- a/src/openharness/tasks/manager.py
+++ b/src/openharness/tasks/manager.py
@@ -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
diff --git a/src/openharness/ui/backend_host.py b/src/openharness/ui/backend_host.py
index 4a2630a..e70f96a 100644
--- a/src/openharness/ui/backend_host.py
+++ b/src/openharness/ui/backend_host.py
@@ -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
diff --git a/src/openharness/ui/output.py b/src/openharness/ui/output.py
index ce1b32e..50a1c2b 100644
--- a/src/openharness/ui/output.py
+++ b/src/openharness/ui/output.py
@@ -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,
diff --git a/tests/test_bridge/test_core.py b/tests/test_bridge/test_core.py
index a62bd13..31dca34 100644
--- a/tests/test_bridge/test_core.py
+++ b/tests/test_bridge/test_core.py
@@ -2,7 +2,6 @@
from __future__ import annotations
-import asyncio
from pathlib import Path
import pytest
diff --git a/tests/test_coordinator/test_registry.py b/tests/test_coordinator/test_registry.py
index 2e5d8ca..fd6d794 100644
--- a/tests/test_coordinator/test_registry.py
+++ b/tests/test_coordinator/test_registry.py
@@ -2,7 +2,6 @@
import pytest
-from openharness.coordinator import get_team_registry
from openharness.coordinator.coordinator_mode import TeamRegistry
diff --git a/tests/test_services/test_session_storage.py b/tests/test_services/test_session_storage.py
index 65725f0..346a216 100644
--- a/tests/test_services/test_session_storage.py
+++ b/tests/test_services/test_session_storage.py
@@ -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,
)
diff --git a/tests/test_tasks/test_manager.py b/tests/test_tasks/test_manager.py
index 266c862..b4e92e5 100644
--- a/tests/test_tasks/test_manager.py
+++ b/tests/test_tasks/test_manager.py
@@ -7,7 +7,6 @@ from pathlib import Path
import pytest
-from openharness.tasks import get_task_manager
from openharness.tasks.manager import BackgroundTaskManager
diff --git a/tests/test_tools/test_integration_flows.py b/tests/test_tools/test_integration_flows.py
index 1873fca..bba8b57 100644
--- a/tests/test_tools/test_integration_flows.py
+++ b/tests/test_tools/test_integration_flows.py
@@ -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