Hao Harness
A terminal coding agent powered by DeepSeek models. 中文文档:README.zh-CN.md
Hao Harness turns the raw DeepSeek Chat API into an agent that can actually get work done: it reads and edits files, searches code, runs commands, and keeps working across multiple tool-calling rounds until the task is done.
Status
v0.1.0 — unreleased, under active development.
Working today:
- Multi-round agent loop with tool execution and error feedback
- 7 built-in tools: read / write / edit / list / glob / grep / run command
- Thinking mode with live reasoning display
- Dynamic model discovery via
GET /modelswith disk cache and offline fallback - Context compaction driven by the model's real
context_window - Path sandbox, per-action approval, duplicate-call detection
- Token accounting and time-of-day aware cost estimation
- 150 unit tests that run without an API key or network access
Not yet implemented: session persistence (--continue), project memory files
(HARNESS.md), sub-agents, MCP support.
Requirements
- Python 3.10 or newer
- A DeepSeek API key — get one at https://platform.deepseek.com/api_keys
Install
pipx install hao-harness
Or, from a clone (for development):
pip install -e ".[dev]"
Quick start
# 1. Provide your API key
export DEEPSEEK_API_KEY=sk-your-key # Windows: setx DEEPSEEK_API_KEY sk-...
# 2. Run a one-shot task
harness -p "create hello.py that prints the first 20 Fibonacci numbers, then run it"
# 3. Or start an interactive session
harness
Without installation you can also run it directly from a clone:
python run.py -p "explain what this project does"
Standalone executable
Two build modes, selected by the HARNESS_BUILD_MODE environment variable.
# Build inside a **clean** virtual environment
python -m venv .venv-build
.venv-build\Scripts\python -m pip install -e ".[build]"
# Default: directory layout (fast startup, recommended)
.venv-build\Scripts\python -m PyInstaller harness.spec --noconfirm
# -> dist\harness\ (about 37 MB directory, ship it as a whole)
# Optional: single-file (portable)
$env:HARNESS_BUILD_MODE="onefile"
.venv-build\Scripts\python -m PyInstaller harness.spec --noconfirm
# -> dist\harness.exe (about 22 MB)
Both bundle Python and every dependency — the target machine needs no Python installation:
$env:DEEPSEEK_API_KEY="sk-..."
.\dist\harness\harness.exe -p "create hello.py and run it"
Which mode to pick
Measured (6 runs each, warmed up, median):
| Mode | Startup | Size | Use when |
|---|---|---|---|
| onedir (default) | ~0.4 s | ~37 MB directory | Installer packaging, everyday use |
| onefile | ~1.3 s | ~22 MB single file | You genuinely need one file to copy around |
One-file mode costs roughly 0.9–2 s per launch, for two reasons: it extracts itself to a temp directory on every run, and the extracted files are new, freshly hashed binaries that Windows Defender re-scans each time. Prefer the default onedir unless you really need a single file.
Always build in a clean virtual environment. PyInstaller walks the import graph of your interpreter's
site-packages. Building from a polluted environment drags unrelated packages into the binary — we measured 74 MB vs 22 MB for exactly the same code.
Builds are platform-specific. A Windows binary must be built on Windows; macOS and Linux require their own builds.
Desktop app (optional)
The desktop interface is an optional extra: installing it pulls in Qt
(PySide6-Essentials) and grows the install from ~19 MB to 100 MB+. Nothing
in the terminal path depends on it.
pipx install "hao-harness[gui]"
harness-gui
If the extra is missing, harness-gui prints the exact install command instead
of an ImportError traceback — not having it is a normal state, not a failure.
Building a standalone desktop executable:
.venv-build\Scripts\python -m pip install -e ".[build,gui]"
.venv-build\Scripts\python -m PyInstaller harness-gui.spec --noconfirm
# -> dist\harness-gui\ (about 112 MB directory, ship it as a whole)
There is deliberately no one-file desktop build: the artifact is over 100 MB, and one-file mode extracts all of it on every launch into freshly hashed files that Defender re-scans — the startup penalty measured above would be multiplied.
harness-gui.spec is a separate configuration from harness.spec, not a
mode of it. The terminal build must exclude Qt (otherwise it grows from 22 MB
to 120 MB+); the desktop build must include it. One spec cannot do both.
Building the desktop installer:
.venv-build\Scripts\python -m PyInstaller harness-gui.spec --noconfirm
makensis packaging\installer-gui.nsi
# -> dist\HaoHarness-0.1.0-setup.exe (38 MB installer for a 112 MB app)
Two installers, two identities — both can be installed side by side.
installer.nsiinstalls Hao Harness CLI intoProgram Files\Hao Harness CLIand adds it toPATH.installer-gui.nsiinstalls Hao Harness intoProgram Files\Hao Harnessand does not touchPATH— it is launched from the Start Menu, and this artifact has no command-line entry. Uninstalling either one keeps your configuration (including the API key), so a reinstall needs no re-setup.
The desktop app bundles Qt for Python under LGPL-3.0-only. See NOTICE for the required notice, the bundled license texts, and where to obtain the Qt source.
Usage
harness [task ...] [options]
| Option | Description |
|---|---|
-p, --prompt TEXT |
Run this task once and exit |
--model ID |
Model to use (default: deepseek-flash) |
--effort {low,high,max} |
Reasoning effort (default: high) |
--no-thinking |
Disable thinking mode (faster, cheaper) |
--no-stream |
Disable streaming output |
--list-models |
List known models and exit |
--refresh-models |
Refresh the model list from the API |
-w, --workspace DIR |
Working directory (default: current directory) |
--max-steps N |
Max tool-calling steps per turn (default: 40) |
--yolo |
Skip all approvals |
--allow-outside |
Allow paths outside the workspace |
--lang {zh,en} |
UI language |
--show-config |
Print the effective configuration and exit |
Interactive commands
| Command | Description |
|---|---|
/help |
Show available commands |
/status |
Show model, context usage and token totals |
/models |
List available models |
/model [id] |
Switch model |
/refresh |
Refresh the model list |
/tools |
List tools |
/compact |
Compact context now |
/clear |
Clear conversation history |
/exit |
Quit |
Configuration
Configuration resolves in this order: .env file → environment variables →
command-line flags. Copy .env.example to .env to get started.
| Variable | Default | Description |
|---|---|---|
DEEPSEEK_API_KEY |
— | Required |
DEEPSEEK_BASE_URL |
https://api.deepseek.com |
API endpoint |
DEEPSEEK_MODEL |
deepseek-flash |
Model id |
HARNESS_THINKING |
true |
Thinking mode |
HARNESS_REASONING_EFFORT |
high |
low / high / max |
HARNESS_MAX_TOKENS |
32000 |
Max output tokens per reply |
HARNESS_MAX_STEPS |
40 |
Max tool-calling steps per turn |
HARNESS_MAX_TOOL_OUTPUT |
30000 |
Tool output truncation limit |
HARNESS_COMPACT_THRESHOLD |
400000 |
Estimated-token compaction threshold |
HARNESS_AUTO_APPROVE |
false |
Skip approvals |
HARNESS_ALLOW_OUTSIDE |
false |
Allow paths outside the workspace |
HARNESS_LANG |
auto | zh or en |
Project memory
Create HARNESS.md in your project root to give the agent standing context:
build commands, code conventions, directory layout, decisions you already made.
# HARNESS.md
- Run tests with `python -m pytest`
- Never edit `migrations/` by hand
- Public API lives in `src/api/`
Two levels are loaded, in this order:
| Location | Scope |
|---|---|
<user config dir>/HARNESS.md |
Your personal preferences, shared across projects |
<workspace>/HARNESS.md |
This project's conventions |
The user config directory is %APPDATA%\harness on Windows,
~/Library/Application Support/harness on macOS, and
$XDG_CONFIG_HOME/harness (or ~/.config/harness) on Linux.
Things worth knowing:
- Only
HARNESS.mdis read.CLAUDE.mdandAGENTS.mdare deliberately ignored: they are written against other tools' capabilities and would reference features this harness does not have, which only confuses the model. - Memory is appended to the end of the system prompt and cannot override the working rules or the approval mechanism — a repository file is semi-trusted input.
- Content is capped at 20,000 characters so it can never crowd out the task.
- Disable with
--no-memory; inspect what was loaded with/memory.
Safety
This tool executes commands and modifies files.
- File operations are confined to the workspace; escaping paths are rejected.
- Writing files and running commands require explicit confirmation.
--yolodisables those confirmations. Only use it in a sandbox or on a throwaway checkout.
Documentation
| Document | Contents |
|---|---|
| GOAL.md | Scope, architecture, design decisions |
| PROGRESS.md | Development status and roadmap |
| CONVENTIONS.md | Coding standards and toolchain |
| CHANGELOG.md | Release history |
| NOTICE | Third-party software and licenses (includes the Qt LGPL notice) |
Development
pip install -e ".[dev]"
python -m ruff check . # lint
python -m ruff format . # format
python -m mypy harness # type check
python -m pytest # tests (no API key needed)
License
The desktop interface bundles Qt for Python, licensed under LGPL-3.0-only.
The required notice, the bundled license texts (third_party/LGPL-3.0.txt,
third_party/GPL-3.0.txt) and the routes to the Qt source are in NOTICE.
Terminal-only usage involves no LGPL components.
Metadata
Release files for hao-harness 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hao_harness-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Release files / hao_harness-0.1.0-py3-none-any.whl
| Download URL | hao_harness-0.1.0-py3-none-any.whl |
|---|---|
| Size | 235.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2eee03ea9d4c2a2915087af8659053a36062059b6fd9282bd3ee79684f76b7e4
|
|
BLAKE2b-256 checksum How to use checksums |
2964e5301b37bd39e58c41c4bfb4104495d728023be2a8d0e4cdc42cbbf03c2f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|