Metadata-Version: 2.5
Name: hao-harness
Version: 0.1.0
Summary: A terminal coding-agent harness powered by DeepSeek models
Project-URL: Repository, https://gitee.com/ma_zhenhao/make_harness
Project-URL: Issues, https://gitee.com/ma_zhenhao/make_harness/issues
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
License-File: third_party/GPL-3.0.txt
License-File: third_party/LGPL-3.0.txt
Keywords: agent,cli,coding-agent,deepseek,harness,llm
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Code Generators
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: openai>=1.60.0
Requires-Dist: rich>=13.7.0
Provides-Extra: build
Requires-Dist: pyinstaller>=6.22; extra == 'build'
Provides-Extra: dev
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Provides-Extra: gui
Requires-Dist: pyside6-essentials>=6.6; extra == 'gui'
Description-Content-Type: text/markdown

# Hao Harness

> A terminal coding agent powered by DeepSeek models.
> 中文文档：[README.zh-CN.md](README.zh-CN.md)

[![Python](https://img.shields.io/badge/python-3.10%2B-blue)](#requirements)
[![License](https://img.shields.io/badge/license-Apache--2.0-green)](#license)

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 /models` with 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

```bash
pipx install hao-harness
```

Or, from a clone (for development):

```bash
pip install -e ".[dev]"
```

## Quick start

```bash
# 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:

```bash
python run.py -p "explain what this project does"
```

## Standalone executable

Two build modes, selected by the `HARNESS_BUILD_MODE` environment variable.

```powershell
# 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:

```powershell
$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.

```bash
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:

```bash
.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:

```bash
.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.nsi` installs **Hao Harness CLI** into
> `Program Files\Hao Harness CLI` and adds it to `PATH`.
> `installer-gui.nsi` installs **Hao Harness** into
> `Program Files\Hao Harness` and **does not touch `PATH`** — 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](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.

```markdown
# 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.md` is read.** `CLAUDE.md` and `AGENTS.md` are 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.
- `--yolo` disables those confirmations. Only use it in a sandbox or on a
  throwaway checkout.

## Documentation

| Document | Contents |
|---|---|
| [GOAL.md](GOAL.md) | Scope, architecture, design decisions |
| [PROGRESS.md](PROGRESS.md) | Development status and roadmap |
| [CONVENTIONS.md](CONVENTIONS.md) | Coding standards and toolchain |
| [CHANGELOG.md](CHANGELOG.md) | Release history |
| [NOTICE](NOTICE) | Third-party software and licenses (includes the Qt LGPL notice) |

## Development

```bash
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

[Apache-2.0](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](NOTICE).
Terminal-only usage involves no LGPL components.
