{"id":4155,"date":"2026-08-18T18:27:54","date_gmt":"2026-08-18T10:27:54","guid":{"rendered":"https:\/\/ai.wuaishare.cn\/hub\/?p=4155"},"modified":"2026-08-18T18:27:54","modified_gmt":"2026-08-18T10:27:54","slug":"openharness","status":"publish","type":"post","link":"https:\/\/ai.wuaishare.cn\/hub\/en\/4155","title":{"rendered":"OpenHarness"},"content":{"rendered":"\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"600\" src=\"https:\/\/ai.wuaishare.cn\/hub\/wp-content\/uploads\/sites\/2\/2026\/06\/openharness.webp\" alt=\"OpenHarness\" class=\"wp-image-3639\" srcset=\"https:\/\/ai.wuaishare.cn\/hub\/wp-content\/uploads\/sites\/2\/2026\/06\/openharness.webp 1200w, https:\/\/ai.wuaishare.cn\/hub\/wp-content\/uploads\/sites\/2\/2026\/06\/openharness-300x150.webp 300w, https:\/\/ai.wuaishare.cn\/hub\/wp-content\/uploads\/sites\/2\/2026\/06\/openharness-1024x512.webp 1024w, https:\/\/ai.wuaishare.cn\/hub\/wp-content\/uploads\/sites\/2\/2026\/06\/openharness-768x384.webp 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n\n\n<p class=\"wp-block-paragraph\">OpenHarness is an open-source terminal CLI for working with any LLM.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Table of Contents<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n    <li>Why Choose OpenHarness?<\/li>\n    <li>Tools (44)<\/li>\n    <li>Checkpoints and Rollback<\/li>\n    <li>Headless Mode and CI\/CD<\/li>\n    <li>Digital Pet Cybergotchi<\/li>\n    <li>MCP Server<\/li>\n  <\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Quick Start<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">It&#8217;s that simple. OpenHarness automatically detects Ollama and starts chatting. No API key required.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Python SDK: We also provide an official Python SDK for driving oh in Python programs (notebooks, batch scripts, ML pipelines). After installing via npm, use pip install openharness-sdk to install (the PyPI distribution is named openharness-sdk because the unprefixed name was already taken), then use <code>from openharness import query<\/code>. See python\/README.md for details.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">TypeScript SDK: There is also an official TypeScript SDK for driving oh in Node.js (VS Code extensions, Electron apps, build scripts, etc.): use @zhijiewang\/openharness-sdk \u2014 install via npm install @zhijiewang\/openharness-sdk, then import { query, OpenHarnessClient, tool } from &#8220;@zhijiewang\/openharness-sdk&#8221;. Feature-equivalent to the Python SDK (streaming events, stateful sessions, custom tools, permission callbacks, session resumption). See packages\/sdk\/README.md for details.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n    <li>Command: npm install -g @zhijiewang\/openharness<\/li>\n    <li>Command: oh init # Interactive setup wizard (model provider + digital pet)<\/li>\n    <li>Command: oh # Auto-detect local model<\/li>\n    <li>Command: oh &#8211;model ollama\/qwen2.5:7b # Specify model<\/li>\n    <li>Command: oh &#8211;model gpt-4o # Cloud model (requires OPENAI_API_KEY)<\/li>\n    <li>Command: oh &#8211;trust # Auto-approve all tool calls<\/li>\n  <\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Why Choose OpenHarness?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Most AI coding assistants are locked to a single model provider or cost $20+ per month. OpenHarness supports any LLM \u2014 run locally for free with Ollama, or connect to any cloud API. Every AI edit is automatically committed to git and can be rolled back via \/undo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Terminal Interface<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">OpenHarness uses a sequential terminal renderer inspired by Ink\/Claude Code&#8217;s default mode. Completed messages flush into the native scroll buffer (scrollable), while live regions (streaming output, loading animations, input field) are redrawn in-place via relative cursor movement.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Key Bindings<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Scrolling is handled by the terminal&#8217;s native scrollbar. Completed messages go into the terminal scroll buffer. Use the terminal&#8217;s built-in search (e.g., Ctrl+Shift+F in VS Code) to search conversation history.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n    <li>Key \/ Action<\/li>\n    <li>Enter \/ Submit prompt<\/li>\n    <li>Alt+Enter \/ Insert newline (multi-line input)<\/li>\n    <li>\u2191 \/ \u2193 \/ Browse input history<\/li>\n    <li>Ctrl+C \/ Cancel current request \/ Exit<\/li>\n    <li>Ctrl+A \/ Ctrl+E \/ Jump to start \/ end of input field<\/li>\n  <\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Features<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n    <li>Markdown rendering \u2014 headings, code blocks, bold, italic, lists, tables, blockquotes, links<\/li>\n    <li>Syntax highlighting \u2014 keywords, strings, comments, numbers, types (20+ languages including JS\/TS\/Python\/Rust\/Go, etc.)<\/li>\n    <li>Collapsible code blocks \u2014 code blocks exceeding 8 lines auto-collapse; press Ctrl+K to expand all<\/li>\n    <li>Collapsible thinking blocks \u2014 thinking blocks collapse to a one-line summary after completion; press Ctrl+O to expand<\/li>\n    <li>Streaming loading animation \u2014 indicators with stage labels (Thinking, Running, Calling:, Running N tools) and color transitions (magenta after 30s \u2192 yellow, red after 60s)<\/li>\n    <li>Tool call display \u2014 argument preview, real-time streaming output, result summary (line count, elapsed time), expand\/collapse with Tab. Tool names are color-coded by category (read cyan, modify yellow, execute magenta, MCP green)<\/li>\n  <\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The above content is parsed and generated from the <a href=\"https:\/\/github.com\/zhijiewong\/openharness\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">GitHub repository README<\/a>, suitable for quickly understanding the project&#8217;s positioning, installation, core capabilities, and usage boundaries.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>OpenHarness is an open-source terminal CLI for working with any LLM, with local and cloud model support, Git-backed rollback, SDKs, MCP, and headless workflows.<\/p>\n","protected":false},"author":0,"featured_media":3639,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ai_share_source_url":"https:\/\/github.com\/zhijiewong\/openharness","ai_share_official_url":"https:\/\/www.npmjs.com\/package\/@zhijiewang\/openharness","ai_share_i18n_original_source_language":"zh-cn","ai_share_tool_icon_attachment_id":124,"ai_share_author_label":"zhijiewong","ai_share_version_label":"v2.39.0","ai_share_tool_type":"tool","ai_share_resource_type":"cli_tool","ai_share_platform_support":"CLI,Node.js,Python","ai_share_provider_observed_at":"2026-08-18T08:30:52.757Z","ai_share_last_commit_at":"2026-05-12T14:13:25Z","ai_share_license":"MIT","ai_share_language_support":"zh-CN,en","ai_share_github_topics":"ai, cli, coding-agent, llm, ollama, openai, terminal, typescript","ai_share_repo_default_branch":"main","ai_share_pricing_model":"open_source","ai_share_deployment_mode":"self_hosted","ai_share_delivery_mode":"","ai_share_region_availability":"global","ai_share_source_platform":"GitHub","ai_share_release_url":"https:\/\/github.com\/zhijiewong\/openharness\/releases\/tag\/v2.39.0","ai_share_github_releases_json":"[{\"tagName\":\"v2.39.0\",\"name\":\"v2.39.0\",\"url\":\"https:\/\/github.com\/zhijiewong\/openharness\/releases\/tag\/v2.39.0\",\"body\":\"## What's Changedn* feat(traces): \/traces flame-graph view (Tier 3) by @zhijiewong in https:\/\/github.com\/zhijiewong\/openharness\/pull\/118nnn**Full Changelog**: https:\/\/github.com\/zhijiewong\/openharness\/compare\/v2.38.0...v2.39.0\",\"bodyExcerpt\":\"\",\"bodyTruncated\":false,\"publishedAt\":\"2026-05-05T08:58:37Z\",\"prerelease\":false,\"draft\":false,\"assetCount\":0,\"assets\":[]},{\"tagName\":\"v2.38.0\",\"name\":\"v2.38.0\",\"url\":\"https:\/\/github.com\/zhijiewong\/openharness\/releases\/tag\/v2.38.0\",\"body\":\"## What's Changedn* feat(parallel-agents): per-task permission_mode (v2.36\/v2.37 symmetry) by @zhijiewong in https:\/\/github.com\/zhijiewong\/openharness\/pull\/117nnn**Full Changelog**: https:\/\/github.com\/zhijiewong\/openharness\/compare\/v2.37.0...v2.38.0\",\"bodyExcerpt\":\"\",\"bodyTruncated\":false,\"publishedAt\":\"2026-05-05T08:32:34Z\",\"prerelease\":false,\"draft\":false,\"assetCount\":0,\"assets\":[]},{\"tagName\":\"v2.37.0\",\"name\":\"v2.37.0\",\"url\":\"https:\/\/github.com\/zhijiewong\/openharness\/releases\/tag\/v2.37.0\",\"body\":\"## What's Changedn* feat(agents): role-level permissionMode default + markdown frontmatter (v2.36 follow-up) by @zhijiewong in https:\/\/github.com\/zhijiewong\/openharness\/pull\/116nnn**Full Changelog**: https:\/\/github.com\/zhijiewong\/openharness\/compare\/v2.36.0...v2.37.0\",\"bodyExcerpt\":\"\",\"bodyTruncated\":false,\"publishedAt\":\"2026-05-05T08:17:56Z\",\"prerelease\":false,\"draft\":false,\"assetCount\":0,\"assets\":[]},{\"tagName\":\"v2.36.0\",\"name\":\"v2.36.0\",\"url\":\"https:\/\/github.com\/zhijiewong\/openharness\/releases\/tag\/v2.36.0\",\"body\":\"## What's Changedn* feat(agents): subagent permission_mode override with safety-clamp by @zhijiewong in https:\/\/github.com\/zhijiewong\/openharness\/pull\/115nnn**Full Changelog**: https:\/\/github.com\/zhijiewong\/openharness\/compare\/v2.35.0...v2.36.0\",\"bodyExcerpt\":\"\",\"bodyTruncated\":false,\"publishedAt\":\"2026-05-05T08:06:11Z\",\"prerelease\":false,\"draft\":false,\"assetCount\":0,\"assets\":[]},{\"tagName\":\"v2.35.0\",\"name\":\"v2.35.0\",\"url\":\"https:\/\/github.com\/zhijiewong\/openharness\/releases\/tag\/v2.35.0\",\"body\":\"## What's Changedn* feat(acp): oh acp \u2014 ACP server over stdio for Zed\/JetBrains\/Cline editor integration by @zhijiewong in https:\/\/github.com\/zhijiewong\/openharness\/pull\/114nnn**Full Changelog**: https:\/\/github.com\/zhijiewong\/openharness\/compare\/v2.34.0...v2.35.0\",\"bodyExcerpt\":\"\",\"bodyTruncated\":false,\"publishedAt\":\"2026-05-05T07:47:30Z\",\"prerelease\":false,\"draft\":false,\"assetCount\":0,\"assets\":[]},{\"tagName\":\"v2.34.0\",\"name\":\"v2.34.0\",\"url\":\"https:\/\/github.com\/zhijiewong\/openharness\/releases\/tag\/v2.34.0\",\"body\":\"## What's Changedn* feat(routing): architect \u2192 editor cost-saving pattern by @zhijiewong in https:\/\/github.com\/zhijiewong\/openharness\/pull\/113nnn**Full Changelog**: https:\/\/github.com\/zhijiewong\/openharness\/compare\/v2.33.0...v2.34.0\",\"bodyExcerpt\":\"\",\"bodyTruncated\":false,\"publishedAt\":\"2026-05-05T06:46:19Z\",\"prerelease\":false,\"draft\":false,\"assetCount\":0,\"assets\":[]},{\"tagName\":\"v2.33.0\",\"name\":\"v2.33.0\",\"url\":\"https:\/\/github.com\/zhijiewong\/openharness\/releases\/tag\/v2.33.0\",\"body\":\"## What's Changedn* feat(security): opt-in OS-level sandbox for BashTool via @anthropic-ai\/sandbox-runtime by @zhijiewong in https:\/\/github.com\/zhijiewong\/openharness\/pull\/112nnn**Full Changelog**: https:\/\/github.com\/zhijiewong\/openharness\/compare\/v2.32.0...v2.33.0\",\"bodyExcerpt\":\"\",\"bodyTruncated\":false,\"publishedAt\":\"2026-05-05T06:22:27Z\",\"prerelease\":false,\"draft\":false,\"assetCount\":0,\"assets\":[]},{\"tagName\":\"v2.32.0\",\"name\":\"v2.32.0\",\"url\":\"https:\/\/github.com\/zhijiewong\/openharness\/releases\/tag\/v2.32.0\",\"body\":\"## What's Changedn* fix(test): poll-loop replaces 1500ms setTimeout in 6 hook test sites (#109) by @zhijiewong in https:\/\/github.com\/zhijiewong\/openharness\/pull\/110n* feat(memory): native AGENTS.md loader (cross-tool standard) by @zhijiewong in https:\/\/github.com\/zhijiewong\/openharness\/pull\/111nnn**Full Changelog**: https:\/\/github.com\/zhijiewong\/openharness\/compare\/v2.31.0...v2.32.0\",\"bodyExcerpt\":\"\",\"bodyTruncated\":false,\"publishedAt\":\"2026-05-05T06:03:52Z\",\"prerelease\":false,\"draft\":false,\"assetCount\":0,\"assets\":[]}]","ai_share_first_party_view_count":33,"ai_share_github_stars":96,"ai_share_github_watchers":1,"ai_share_github_forks":23,"ai_share_github_open_issues":5,"ai_share_open_source":true,"ai_share_repo_url":"https:\/\/github.com\/zhijiewong\/openharness","ai_share_documentation_url":"https:\/\/www.npmjs.com\/package\/@zhijiewang\/openharness","creator_id":"github-zhijiewong","creator_slug":"zhijiewong","creator_name":"zhijiewong","creator_type":"person","creator_status":"external","creator_intro":"Turning coffee into community-driven code \u2615","creator_github_url":"https:\/\/github.com\/zhijiewong","creator_avatar_url":"https:\/\/avatars.githubusercontent.com\/u\/32107008?v=4","ai_share_github_readme_path":"README.zh-CN.md","ai_share_github_readme_sha":"e76a20842434e481245f0a23b9d6b6d9018721eb","ai_share_github_readme_hash":"c5dc5e64599a82fbf4e7ce4e6040ccaa8f4096446294b7273367bc093a162b6d","ai_share_github_readme_language":"zh-cn","ai_share_github_readme_format":"markdown","ai_share_github_description":"Open source local terminal cli with any LLM","ai_share_one_sentence_definition":"Open-source LLM terminal CLI","ai_share_one_sentence_definition_en":"Open-source LLM terminal CLI","ai_share_one_sentence_definition_raw_output":"Open-source LLM terminal CLI","ai_share_one_sentence_definition_source_hash":"5a5c1b4d8438dfe13c1a15a0a423e2f323cafe8f2d03e075022f11f367efde3a","ai_share_one_sentence_definition_generated_at":"2026-08-28T16:55:36+00:00","ai_share_one_sentence_definition_provider":"human-reviewed","ai_share_one_sentence_definition_model":"manual-source-fact-review","ai_share_one_sentence_definition_status":"human-reviewed","ai_share_github_readme_url":"https:\/\/github.com\/zhijiewong\/openharness\/blob\/main\/README.zh-CN.md","ai_share_github_readme_raw_url":"https:\/\/raw.githubusercontent.com\/zhijiewong\/openharness\/main\/README.zh-CN.md","ai_share_github_readme_markdown":"<p align=\"center\">\n  <img src=\"assets\/logo-256.png\" alt=\"openHarness logo\" width=\"128\" \/>\n<\/p>\n\n# OpenHarness\n\n```\n        ___\n       \/   \\\n      (     )        ___  ___  ___ _  _ _  _   _ ___ _  _ ___ ___ ___\n       `~w~`        \/ _ \\| _ \\| __| \\| | || | \/_\\ | _ \\ \\| | __\/ __\/ __|\n       (( ))       | (_) |  _\/| _|| .` | __ |\/ _ \\|   \/ .` | _|\\__ \\__ \\\n        ))((        \\___\/|_|  |___|_|\\_|_||_\/_\/ \\_\\_|_\\_|\\_|___|___\/___\/\n       ((  ))\n        `--`\n```\n\n\u7ec8\u7aef\u91cc\u7684 AI \u7f16\u7a0b\u52a9\u624b\u3002\u652f\u6301\u4efb\u610f LLM \u2014\u2014 \u514d\u8d39\u7684\u672c\u5730\u6a21\u578b\uff0c\u6216\u4efb\u610f\u4e91\u7aef API\u3002\n\n<p align=\"center\">\n  <img src=\"assets\/openharness_v0.11.1_4.gif\" alt=\"OpenHarness demo\" width=\"800\" \/>\n<\/p>\n\n[![npm version](https:\/\/img.shields.io\/npm\/v\/@zhijiewang\/openharness)](https:\/\/www.npmjs.com\/package\/@zhijiewang\/openharness) [![npm downloads](https:\/\/img.shields.io\/npm\/dm\/@zhijiewang\/openharness)](https:\/\/www.npmjs.com\/package\/@zhijiewang\/openharness) [![license](https:\/\/img.shields.io\/npm\/l\/@zhijiewang\/openharness)](LICENSE) ![tests](https:\/\/img.shields.io\/badge\/tests-1502-brightgreen) ![tools](https:\/\/img.shields.io\/badge\/tools-44-blue) ![Node.js 18+](https:\/\/img.shields.io\/badge\/node-18%2B-green) ![TypeScript](https:\/\/img.shields.io\/badge\/typescript-strict-blue) [![GitHub stars](https:\/\/img.shields.io\/github\/stars\/zhijiewong\/openharness)](https:\/\/github.com\/zhijiewong\/openharness) [![GitHub issues](https:\/\/img.shields.io\/github\/issues-raw\/zhijiewong\/openharness)](https:\/\/github.com\/zhijiewong\/openharness\/issues) [![PRs Welcome](https:\/\/img.shields.io\/badge\/PRs-welcome-brightgreen)](https:\/\/github.com\/zhijiewong\/openharness\/pulls)\n\n[English](README.md) | **\u7b80\u4f53\u4e2d\u6587**\n\n---\n\n## \u76ee\u5f55\n\n- [\u5feb\u901f\u5f00\u59cb](#\u5feb\u901f\u5f00\u59cb)\n- [\u4e3a\u4ec0\u4e48\u9009\u62e9 OpenHarness\uff1f](#\u4e3a\u4ec0\u4e48\u9009\u62e9-openharness)\n- [\u7ec8\u7aef\u754c\u9762](#\u7ec8\u7aef\u754c\u9762)\n- [\u5de5\u5177\uff0844 \u4e2a\uff09](#\u5de5\u517743-\u4e2a)\n- [\u659c\u6760\u547d\u4ee4](#\u659c\u6760\u547d\u4ee4)\n- [\u6743\u9650\u6a21\u5f0f](#\u6743\u9650\u6a21\u5f0f)\n- [\u94a9\u5b50](#\u94a9\u5b50)\n- [\u68c0\u67e5\u70b9\u4e0e\u56de\u6eda](#\u68c0\u67e5\u70b9\u4e0e\u56de\u6eda)\n- [\u4ee3\u7406\u89d2\u8272](#\u4ee3\u7406\u89d2\u8272)\n- [\u65e0\u5934\u6a21\u5f0f\u4e0e CI\/CD](#\u65e0\u5934\u6a21\u5f0f)\n- [\u7535\u5b50\u5ba0\u7269 Cybergotchi](#\u7535\u5b50\u5ba0\u7269-cybergotchi)\n- [MCP \u670d\u52a1\u5668](#mcp-\u670d\u52a1\u5668)\n- [\u6a21\u578b\u63d0\u4f9b\u5546](#\u6a21\u578b\u63d0\u4f9b\u5546)\n- [\u9274\u6743\uff08Auth\uff09](#\u9274\u6743auth)\n- [\u81ea\u52a8\u66f4\u65b0\uff08Update\uff09](#\u81ea\u52a8\u66f4\u65b0update)\n- [\u8bc4\u6d4b\uff08Evals\uff09](#\u8bc4\u6d4bevals)\n- [\u5e38\u89c1\u95ee\u9898](#\u5e38\u89c1\u95ee\u9898)\n- [\u5b89\u88c5](#\u5b89\u88c5)\n- [\u5f00\u53d1](#\u5f00\u53d1)\n- [\u8d21\u732e](#\u8d21\u732e)\n- [\u793e\u533a](#\u793e\u533a)\n\n---\n\n## \u5feb\u901f\u5f00\u59cb\n\n```bash\nnpm install -g @zhijiewang\/openharness\noh\n```\n\n\u5c31\u662f\u8fd9\u4e48\u7b80\u5355\u3002OpenHarness \u4f1a\u81ea\u52a8\u68c0\u6d4b Ollama \u5e76\u5f00\u59cb\u5bf9\u8bdd\u3002\u65e0\u9700 API \u5bc6\u94a5\u3002\n\n**Python SDK\uff1a** \u6211\u4eec\u8fd8\u63d0\u4f9b\u4e86\u5b98\u65b9\u7684 Python SDK\uff0c\u53ef\u4ee5\u5728 Python \u7a0b\u5e8f\u4e2d\u9a71\u52a8 `oh`\uff08\u7b14\u8bb0\u672c\u3001\u6279\u5904\u7406\u811a\u672c\u3001ML \u6d41\u6c34\u7ebf\uff09\u3002\u5728 npm \u5b89\u88c5\u4e4b\u540e\uff0c\u4f7f\u7528 `pip install openharness-sdk` \u5b89\u88c5\uff08PyPI \u5206\u53d1\u540d\u4e3a `openharness-sdk`\uff0c\u56e0\u4e3a\u672a\u52a0\u540e\u7f00\u7684\u540d\u79f0\u5df2\u88ab\u5360\u7528\uff09\uff0c\u7136\u540e `from openharness import query`\u3002\u8be6\u89c1 [`python\/README.md`](python\/README.md)\u3002\n\n**TypeScript SDK\uff1a** \u540c\u6837\u6709\u5b98\u65b9\u7684 TypeScript SDK\uff0c\u53ef\u4ee5\u5728 Node.js\uff08VS Code \u63d2\u4ef6\u3001Electron \u5e94\u7528\u3001\u6784\u5efa\u811a\u672c\u7b49\uff09\u4e2d\u9a71\u52a8 `oh`\uff1a\u4f7f\u7528 `@zhijiewang\/openharness-sdk` \u2014\u2014 \u901a\u8fc7 `npm install @zhijiewang\/openharness-sdk` \u5b89\u88c5\uff0c\u7136\u540e `import { query, OpenHarnessClient, tool } from \"@zhijiewang\/openharness-sdk\"`\u3002\u529f\u80fd\u4e0e Python SDK \u5bf9\u7b49\uff08\u6d41\u5f0f\u4e8b\u4ef6\u3001\u6709\u72b6\u6001\u4f1a\u8bdd\u3001\u81ea\u5b9a\u4e49\u5de5\u5177\u3001\u6743\u9650\u56de\u8c03\u3001\u4f1a\u8bdd\u6062\u590d\uff09\u3002\u8be6\u89c1 [`packages\/sdk\/README.md`](packages\/sdk\/README.md)\u3002\n\n```bash\noh init                               # \u4ea4\u4e92\u5f0f\u5b89\u88c5\u5411\u5bfc\uff08\u6a21\u578b\u63d0\u4f9b\u5546 + \u7535\u5b50\u5ba0\u7269\uff09\noh                                    # \u81ea\u52a8\u68c0\u6d4b\u672c\u5730\u6a21\u578b\noh --model ollama\/qwen2.5:7b         # \u6307\u5b9a\u6a21\u578b\noh --model gpt-4o                     # \u4e91\u7aef\u6a21\u578b\uff08\u9700\u8981 OPENAI_API_KEY\uff09\noh --trust                            # \u81ea\u52a8\u6279\u51c6\u6240\u6709\u5de5\u5177\u8c03\u7528\noh --auto                             # \u81ea\u52a8\u6279\u51c6\uff0c\u4f46\u963b\u6b62\u5371\u9669\u7684 bash \u547d\u4ee4\noh -p \"fix the tests\" --trust         # \u65e0\u5934\u6a21\u5f0f\uff08\u5355\u6b21\u63d0\u793a\u540e\u9000\u51fa\uff09\noh run \"review code\" --json           # \u7528\u4e8e CI\/CD \u7684 JSON \u8f93\u51fa\n```\n\n**\u4f1a\u8bdd\u5185\u547d\u4ee4\uff1a**\n```\n\/rewind                               # \u64a4\u9500\u6700\u8fd1\u4e00\u6b21 AI \u6587\u4ef6\u53d8\u66f4\uff08\u6062\u590d\u68c0\u67e5\u70b9\uff09\n\/roles                                # \u5217\u51fa\u6240\u6709\u4ee3\u7406\u4e13\u957f\n\/vim                                  # \u5207\u6362 vim \u6a21\u5f0f\nCtrl+O                                # \u5c06\u5386\u53f2\u8bb0\u5f55\u5237\u5165\u7ec8\u7aef\u6eda\u52a8\u7f13\u51b2\u533a\u4ee5\u4fbf\u67e5\u9605\n```\n\n## \u4e3a\u4ec0\u4e48\u9009\u62e9 OpenHarness\uff1f\n\n\u5927\u591a\u6570 AI \u7f16\u7a0b\u52a9\u624b\u8981\u4e48\u7ed1\u5b9a\u5355\u4e00\u6a21\u578b\u63d0\u4f9b\u5546\uff0c\u8981\u4e48\u6bcf\u6708\u6536\u8d39 20 \u7f8e\u5143\u4ee5\u4e0a\u3002OpenHarness \u652f\u6301\u4efb\u610f LLM \u2014\u2014 \u53ef\u4ee5\u5728\u672c\u673a\u7528 Ollama \u514d\u8d39\u8fd0\u884c\uff0c\u4e5f\u53ef\u4ee5\u8fde\u63a5\u4efb\u610f\u4e91\u7aef API\u3002\u6bcf\u4e00\u6b21 AI \u7f16\u8f91\u90fd\u4f1a\u81ea\u52a8\u63d0\u4ea4\u5230 git\uff0c\u5e76\u53ef\u4ee5\u901a\u8fc7 `\/undo` \u56de\u9000\u3002\n\n## \u7ec8\u7aef\u754c\u9762\n\nOpenHarness \u91c7\u7528\u53d7 Ink\/Claude Code \u9ed8\u8ba4\u6a21\u5f0f\u542f\u53d1\u7684\u987a\u5e8f\u5f0f\u7ec8\u7aef\u6e32\u67d3\u5668\u3002\u5df2\u5b8c\u6210\u7684\u6d88\u606f\u4f1a\u5237\u5165\u539f\u751f\u7684\u6eda\u52a8\u7f13\u51b2\u533a\uff08\u53ef\u6eda\u52a8\uff09\uff0c\u800c\u5b9e\u65f6\u533a\u57df\uff08\u6d41\u5f0f\u8f93\u51fa\u3001\u52a0\u8f7d\u52a8\u753b\u3001\u8f93\u5165\u6846\uff09\u5219\u901a\u8fc7\u76f8\u5bf9\u5149\u6807\u79fb\u52a8\u8fdb\u884c\u539f\u5730\u91cd\u7ed8\u3002\n\n### \u952e\u4f4d\u7ed1\u5b9a\n\n| \u6309\u952e | \u64cd\u4f5c |\n|-----|--------|\n| `Enter` | \u63d0\u4ea4\u63d0\u793a\u8bcd |\n| `Alt+Enter` | \u63d2\u5165\u6362\u884c\uff08\u591a\u884c\u8f93\u5165\uff09 |\n| `\u2191` \/ `\u2193` | \u6d4f\u89c8\u8f93\u5165\u5386\u53f2 |\n| `Ctrl+C` | \u53d6\u6d88\u5f53\u524d\u8bf7\u6c42 \/ \u9000\u51fa |\n| `Ctrl+A` \/ `Ctrl+E` | \u8df3\u5230\u8f93\u5165\u6846\u9996 \/ \u5c3e |\n| `Ctrl+O` | \u5c55\u5f00 \/ \u6298\u53e0\u601d\u8003\u5757 |\n| `Ctrl+K` | \u5c55\u5f00 \/ \u6298\u53e0\u6d88\u606f\u4e2d\u7684\u4ee3\u7801\u5757 |\n| `Tab` | \u81ea\u52a8\u8865\u5168\u659c\u6760\u547d\u4ee4 \/ \u6587\u4ef6\u8def\u5f84 \/ \u5207\u6362\u5de5\u5177\u8f93\u51fa |\n| `\/vim` | \u5207\u6362 Vim \u6a21\u5f0f\uff08normal\/insert\uff09 |\n\n\u6eda\u52a8\u7531\u7ec8\u7aef\u7684\u539f\u751f\u6eda\u52a8\u6761\u5904\u7406\u3002\u5df2\u5b8c\u6210\u7684\u6d88\u606f\u4f1a\u8fdb\u5165\u7ec8\u7aef\u6eda\u52a8\u7f13\u51b2\u533a\u3002\u4f7f\u7528\u7ec8\u7aef\u81ea\u5e26\u7684\u641c\u7d22\u529f\u80fd\uff08\u5982 VS Code \u4e2d\u7684 `Ctrl+Shift+F`\uff09\u641c\u7d22\u5bf9\u8bdd\u5386\u53f2\u3002\n\n### \u7279\u6027\n\n- **Markdown \u6e32\u67d3** \u2014\u2014 \u6807\u9898\u3001\u4ee3\u7801\u5757\u3001\u7c97\u4f53\u3001\u659c\u4f53\u3001\u5217\u8868\u3001\u8868\u683c\u3001\u5f15\u7528\u5757\u3001\u94fe\u63a5\n- **\u8bed\u6cd5\u9ad8\u4eae** \u2014\u2014 \u5173\u952e\u5b57\u3001\u5b57\u7b26\u4e32\u3001\u6ce8\u91ca\u3001\u6570\u5b57\u3001\u7c7b\u578b\uff08\u652f\u6301 JS\/TS\/Python\/Rust\/Go \u7b49 20+ \u79cd\u8bed\u8a00\uff09\n- **\u53ef\u6298\u53e0\u4ee3\u7801\u5757** \u2014\u2014 \u8d85\u8fc7 8 \u884c\u7684\u4ee3\u7801\u5757\u4f1a\u81ea\u52a8\u6298\u53e0\uff1b\u6309 `Ctrl+K` \u5168\u90e8\u5c55\u5f00\n- **\u53ef\u6298\u53e0\u601d\u8003\u5757** \u2014\u2014 \u601d\u8003\u5757\u5728\u5b8c\u6210\u540e\u4f1a\u6298\u53e0\u4e3a\u4e00\u884c\u6458\u8981\uff1b\u6309 `Ctrl+O` \u5c55\u5f00\n- **\u6d41\u5149\u52a0\u8f7d\u52a8\u753b** \u2014\u2014 \u5e26\u9636\u6bb5\u6807\u7b7e\uff08`Thinking`\u3001`Running <Tool>`\u3001`Calling <server>:<tool>`\u3001`Running N tools`\uff09\u548c\u989c\u8272\u8fc7\u6e21\u7684\u6307\u793a\u5668\uff0830 \u79d2\u540e\u6d0b\u7ea2 \u2192 \u9ec4\uff0c60 \u79d2\u540e \u2192 \u7ea2\uff09\n- **\u5de5\u5177\u8c03\u7528\u663e\u793a** \u2014\u2014 \u53c2\u6570\u9884\u89c8\u3001\u5b9e\u65f6\u6d41\u5f0f\u8f93\u51fa\u3001\u7ed3\u679c\u6458\u8981\uff08\u884c\u6570\u3001\u8017\u65f6\uff09\uff0c\u6309 `Tab` \u5c55\u5f00\/\u6298\u53e0\u3002\u5de5\u5177\u540d\u79f0\u6309\u7c7b\u522b\u7740\u8272\uff08\u8bfb\u53d6\u7c7b\u9752\u8272\u3001\u4fee\u6539\u7c7b\u9ec4\u8272\u3001\u6267\u884c\u7c7b\u54c1\u7ea2\u8272\u3001MCP \u7c7b\u7eff\u8272\uff09\n- **\u5bcc\u5de5\u5177\u8f93\u51fa** \u2014\u2014 JSON \u6587\u4ef6\u4ee5\u5f69\u8272\u9759\u6001\u6811\u5f62\u6e32\u67d3\uff08\u6df1\u5ea6 3 \u7ea7\u6298\u53e0\u3001\u884c\u6570\u622a\u65ad\uff09\uff1bMarkdown \u6587\u4ef6\u5b8c\u6574\u6e32\u67d3\u6837\u5f0f\uff08\u6807\u9898\u3001\u4ee3\u7801\u5757\u3001\u8868\u683c\uff09\uff0c\u4e0d\u518d\u662f\u666e\u901a\u7684\u6309\u884c\u62c6\u5206\u3002\u6e32\u67d3\u5668\u901a\u8fc7 `outputType` \u5b57\u6bb5\u5206\u53d1\uff08FileReadTool \/ WebFetchTool \u4f1a\u6807\u6ce8\uff09\uff0c\u672a\u6807\u6ce8\u7684\u5de5\u5177\u8d70\u542f\u53d1\u5f0f\u56de\u9000\u8def\u5f84\n- **\u5d4c\u5957\u5de5\u5177\u8c03\u7528** \u2014\u2014 \u5f53 `Agent` \u6216 `ParallelAgents` \u6d3e\u751f\u5185\u5c42\u5de5\u5177\u8c03\u7528\uff08Read\u3001Bash\u3001Edit\uff09\u65f6,\u5b50\u8c03\u7528\u4f1a\u7f29\u8fdb\u663e\u793a\u5728\u6d3e\u751f\u5b83\u7684\u7236\u8c03\u7528\u4e4b\u4e0b\u3002ParallelAgents \u8fd8\u4f1a\u663e\u793a\u6bcf\u4e2a\u4efb\u52a1\u7684 `Task` \u5305\u88c5\u884c,\u4f7f\u5b50\u8c03\u7528\u6309\u4efb\u52a1\u5206\u7ec4,\u800c\u4e0d\u662f\u5e73\u94fa\u5728\u5408\u5e76\u7684\u7236\u8c03\u7528\u4e4b\u4e0b\u3002\u6df1\u5ea6 3 \u7ea7\u7f29\u8fdb\u4e0a\u9650,\u8d85\u8fc7\u663e\u793a `\u2026 (N more level)` \u6298\u53e0\u6807\u8bb0\n- **\u591a\u884c\u8f93\u5165\u6298\u884c\u7b26** \u2014\u2014 \u591a\u884c\u8f93\u5165\u7684\u6bcf\u4e00\u975e\u672b\u5c3e\u884c\u90fd\u4ee5\u6697\u8272 `\u21b5` \u7eed\u884c\u7b26\u7ed3\u5c3e,\u4f7f\u6298\u884c\u89c6\u89c9\u6e05\u6670\n- **\u6743\u9650\u63d0\u793a** \u2014\u2014 \u5e26\u8fb9\u6846\u7684\u63d0\u793a\u6846\uff0c\u6309\u98ce\u9669\u7ea7\u522b\u7740\u8272\uff0c\u9192\u76ee\u7684 **Y**es\/**N**o\/**D**iff \u6309\u952e\uff0c\u5185\u8054 diff \u5e26\u8bed\u6cd5\u9ad8\u4eae\n- **\u72b6\u6001\u680f** \u2014\u2014 \u663e\u793a\u6a21\u578b\u540d\u79f0\u3001token \u8ba1\u6570\u3001\u8d39\u7528\u3001\u4e0a\u4e0b\u6587\u5360\u7528\u6761\uff08\u53ef\u901a\u8fc7\u914d\u7f6e\u81ea\u5b9a\u4e49\uff09\n- **\u4e0a\u4e0b\u6587\u544a\u8b66** \u2014\u2014 \u4e0a\u4e0b\u6587\u7a97\u53e3\u8d85\u8fc7 75% \u65f6\u663e\u793a\u9ec4\u8272\u8b66\u544a\n- **\u539f\u751f\u7ec8\u7aef\u6eda\u52a8\u6761** \u2014\u2014 \u5df2\u5b8c\u6210\u7684\u6d88\u606f\u8fdb\u5165\u6eda\u52a8\u7f13\u51b2\u533a\uff1b\u4f7f\u7528\u7ec8\u7aef\u81ea\u5e26\u7684\u6eda\u52a8\u6761\u4e0e\u641c\u7d22\n- **\u591a\u884c\u8f93\u5165** \u2014\u2014 `Alt+Enter` \u63d2\u5165\u6362\u884c\uff1b\u7c98\u8d34\u65f6\u81ea\u52a8\u8bc6\u522b\u5e76\u63d2\u5165\u6362\u884c\n- **\u81ea\u52a8\u8865\u5168** \u2014\u2014 \u659c\u6760\u547d\u4ee4\u4e0e\u6587\u4ef6\u8def\u5f84\uff08\u5e26\u8bf4\u660e\uff09\uff1b\u6309 Tab \u5faa\u73af\u5207\u6362\n- **\u6587\u4ef6\u8def\u5f84\u8865\u5168** \u2014\u2014 Tab \u8865\u5168\u8def\u5f84\uff0c\u5e76\u7528 `[dir]`\/`[file]` \u6807\u8bb0\u7c7b\u578b\n- **\u4f1a\u8bdd\u6d4f\u89c8\u5668** \u2014\u2014 \u4f7f\u7528 `\/browse` \u4ea4\u4e92\u5f0f\u6d4f\u89c8\u5e76\u6062\u590d\u5386\u53f2\u4f1a\u8bdd\n- **\u684c\u9762\u5ba0\u7269** \u2014\u2014 \u9875\u811a\u4e2d\u7684\u52a8\u753b\u7535\u5b50\u5ba0\u7269 Cybergotchi\uff08\u901a\u8fc7 `\/companion off|on` \u5207\u6362\uff09\n\n### \u4e3b\u9898\n\n```bash\noh --light                    # \u9002\u7528\u4e8e\u660e\u4eae\u7ec8\u7aef\u7684\u6d45\u8272\u4e3b\u9898\n\/theme light                  # \u4f1a\u8bdd\u4e2d\u5207\u6362\uff08\u81ea\u52a8\u4fdd\u5b58\uff09\n\/theme dark                   # \u5207\u56de\u6df1\u8272\n```\n\n\u4e3b\u9898\u504f\u597d\u4f1a\u4fdd\u5b58\u5230 `.oh\/config.yaml`\uff0c\u8de8\u4f1a\u8bdd\u6301\u4e45\u5316\u3002\n\n### \u81ea\u5b9a\u4e49\u72b6\u6001\u680f\n\n\u5728 `.oh\/config.yaml` \u4e2d\u81ea\u5b9a\u4e49\u72b6\u6001\u680f\u683c\u5f0f\uff1a\n\n```yaml\nstatusLineFormat: '{model} \u2502 {tokens} \u2502 {cost} \u2502 {ctx}'\n```\n\n\u53ef\u7528\u53d8\u91cf\uff1a`{model}`\u3001`{tokens}`\uff08\u8f93\u5165\u2191 \u8f93\u51fa\u2193\uff09\u3001`{cost}`\uff08$X.XXXX\uff09\u3001`{ctx}`\uff08\u4e0a\u4e0b\u6587\u5360\u7528\u6761\uff09\u3002\u7a7a\u7247\u6bb5\u4f1a\u81ea\u52a8\u6298\u53e0\u3002\n\n## \u5de5\u5177\uff0844 \u4e2a\uff09\n\n| \u5de5\u5177 | \u98ce\u9669 | \u63cf\u8ff0 |\n|------|------|-------------|\n| **\u6838\u5fc3** | | |\n| Bash | \u9ad8 | \u6267\u884c shell \u547d\u4ee4\u5e76\u5b9e\u65f6\u6d41\u5f0f\u8f93\u51fa\uff08AST \u5b89\u5168\u5206\u6790\uff09 |\n| PowerShell | \u9ad8 | \u6267\u884c PowerShell \u547d\u4ee4\uff08Windows \u539f\u751f\u811a\u672c\uff09 |\n| Read | \u4f4e | \u6309\u884c\u8303\u56f4\u8bfb\u53d6\u6587\u4ef6\uff0c\u652f\u6301 PDF |\n| ImageRead | \u4f4e | \u8bfb\u53d6\u56fe\u7247\/PDF \u4ee5\u8fdb\u884c\u591a\u6a21\u6001\u5206\u6790 |\n| Write | \u4e2d | \u521b\u5efa\u6216\u8986\u76d6\u6587\u4ef6 |\n| Edit | \u4e2d | \u641c\u7d22\u5e76\u66ff\u6362\u7f16\u8f91 |\n| MultiEdit | \u4e2d | \u539f\u5b50\u5316\u7684\u591a\u6587\u4ef6\u7f16\u8f91\uff08\u5168\u90e8\u6210\u529f\u6216\u5168\u90e8\u5931\u8d25\uff09 |\n| Glob | \u4f4e | \u6309 pattern \u67e5\u627e\u6587\u4ef6 |\n| Grep | \u4f4e | \u5e26\u4e0a\u4e0b\u6587\u884c\u6570\u7684\u6b63\u5219\u5185\u5bb9\u641c\u7d22 |\n| LS | \u4f4e | \u5217\u51fa\u76ee\u5f55\u5185\u5bb9\u548c\u5927\u5c0f |\n| **Web** | | |\n| WebFetch | \u4e2d | \u83b7\u53d6 URL \u5185\u5bb9\uff08\u9632 SSRF\uff09 |\n| WebSearch | \u4e2d | \u7f51\u7edc\u641c\u7d22 |\n| ExaSearch | \u4e2d | \u901a\u8fc7 Exa \u8fdb\u884c\u795e\u7ecf\u7f51\u7edc\u641c\u7d22\uff08\u9700\u8981 `EXA_API_KEY`\uff09 |\n| RemoteTrigger | \u9ad8 | \u5411 webhook\/API \u53d1\u9001 HTTP \u8bf7\u6c42 |\n| **\u4efb\u52a1** | | |\n| TaskCreate | \u4f4e | \u521b\u5efa\u7ed3\u6784\u5316\u4efb\u52a1 |\n| TaskUpdate | \u4f4e | \u66f4\u65b0\u4efb\u52a1\u72b6\u6001 |\n| TaskList | \u4f4e | \u5217\u51fa\u6240\u6709\u4efb\u52a1 |\n| TaskGet | \u4f4e | \u83b7\u53d6\u4efb\u52a1\u8be6\u60c5 |\n| TaskStop | \u4f4e | \u505c\u6b62\u6b63\u5728\u8fd0\u884c\u7684\u4efb\u52a1 |\n| TaskOutput | \u4f4e | \u83b7\u53d6\u4efb\u52a1\u8f93\u51fa |\n| TodoWrite | \u4f4e | \u7ba1\u7406\u4f1a\u8bdd\u7ea7 todo \u5217\u8868\uff08\u517c\u5bb9 Claude Code\uff09 |\n| **\u4ee3\u7406** | | |\n| Agent | \u4e2d | \u6d3e\u751f\u4e00\u4e2a\u5b50\u4ee3\u7406\uff08\u53ef\u6307\u5b9a\u89d2\u8272\uff09 |\n| ParallelAgent | \u4e2d | \u6d3e\u53d1\u591a\u4e2a\u4ee3\u7406\u5e76\u652f\u6301 DAG \u4f9d\u8d56 |\n| SendMessage | \u4f4e | \u4ee3\u7406\u4e4b\u95f4\u7684\u70b9\u5bf9\u70b9\u6d88\u606f |\n| AskUser | \u4f4e | \u5411\u7528\u6237\u63d0\u95ee\uff08\u5e26\u9009\u9879\uff09 |\n| **\u8c03\u5ea6** | | |\n| CronCreate | \u4e2d | \u521b\u5efa\u5b9a\u65f6\u4efb\u52a1 |\n| CronDelete | \u4e2d | \u5220\u9664\u5b9a\u65f6\u4efb\u52a1 |\n| CronList | \u4f4e | \u5217\u51fa\u6240\u6709\u5b9a\u65f6\u4efb\u52a1 |\n| ScheduleWakeup | \u4f4e | \u5728 \/loop \u4e2d\u81ea\u9002\u5e94\u5b89\u6392\u4e0b\u4e00\u6b21\u89e6\u53d1\uff08\u7f13\u5b58\u611f\u77e5\uff09 |\n| **\u89c4\u5212** | | |\n| EnterPlanMode | \u4f4e | \u8fdb\u5165\u7ed3\u6784\u5316\u89c4\u5212\u6a21\u5f0f |\n| ExitPlanMode | \u4f4e | \u9000\u51fa\u89c4\u5212\u6a21\u5f0f |\n| **\u6d41\u6c34\u7ebf** | | |\n| Pipeline | \u4e2d | \u987a\u5e8f\u6267\u884c\u4e00\u8fde\u4e32\u5b50\u4efb\u52a1\uff0c\u628a\u6bcf\u4e00\u6b65\u7684\u8f93\u51fa\u4f5c\u4e3a\u4e0b\u4e00\u6b65\u7684\u8f93\u5165 |\n| **\u4ee3\u7801\u667a\u80fd** | | |\n| Diagnostics | \u4f4e | \u57fa\u4e8e LSP \u7684\u4ee3\u7801\u8bca\u65ad |\n| NotebookEdit | \u4e2d | \u7f16\u8f91 Jupyter notebook |\n| **\u8bb0\u5fc6\u4e0e\u53d1\u73b0** | | |\n| Memory | \u4f4e | \u4fdd\u5b58\/\u5217\u51fa\/\u641c\u7d22\u6301\u4e45\u5316\u8bb0\u5fc6 |\n| Skill | \u4f4e | \u8c03\u7528 .oh\/skills\/ \u4e0b\u7684\u6280\u80fd |\n| ToolSearch | \u4f4e | \u6309\u63cf\u8ff0\u67e5\u627e\u5de5\u5177 |\n| SessionSearch | \u4f4e | \u5728\u5386\u53f2\u4f1a\u8bdd\u4e2d\u641c\u7d22\u76f8\u5173\u4e0a\u4e0b\u6587 |\n| **MCP** | | |\n| ListMcpResources | \u4f4e | \u5217\u51fa\u5df2\u8fde\u63a5 MCP \u670d\u52a1\u5668\u4e0a\u7684\u8d44\u6e90 |\n| ReadMcpResource | \u4f4e | \u6309 URI \u8bfb\u53d6\u6307\u5b9a\u7684 MCP \u8d44\u6e90 |\n| **Git \u5de5\u4f5c\u6811** | | |\n| EnterWorktree | \u4e2d | \u521b\u5efa\u9694\u79bb\u7684 git worktree |\n| ExitWorktree | \u4e2d | \u79fb\u9664\u4e00\u4e2a git worktree |\n| **\u8fdb\u7a0b** | | |\n| KillProcess | \u9ad8 | \u6309 PID \u6216\u540d\u79f0\u505c\u6b62\u8fdb\u7a0b |\n| Monitor | \u4e2d | \u5728\u540e\u53f0\u8fd0\u884c\u547d\u4ee4\uff0c\u5e76\u628a\u6bcf\u4e00\u884c\u8f93\u51fa\u5b9e\u65f6\u53cd\u9988\u7ed9\u4ee3\u7406 |\n\n\u4f4e\u98ce\u9669\u53ea\u8bfb\u5de5\u5177\u4f1a\u81ea\u52a8\u6279\u51c6\u3002\u5728 `ask` \u6a21\u5f0f\u4e0b\uff0c\u4e2d\u9ad8\u98ce\u9669\u5de5\u5177\u9700\u8981\u786e\u8ba4\u3002\u4f7f\u7528 `--trust` \u6216 `--auto` \u53ef\u8df3\u8fc7\u63d0\u793a\u3002\n\n## \u659c\u6760\u547d\u4ee4\n\nOH \u6ce8\u518c\u4e86 80+ \u4e2a\u659c\u6760\u547d\u4ee4\uff1b\u4e0b\u8868\u53ea\u5217\u51fa\u6700\u5e38\u7528\u7684\u4e00\u90e8\u5206\u3002\u5728\u4f1a\u8bdd\u4e2d\u8fd0\u884c `\/help` \u53ef\u4ee5\u770b\u5230\u5b8c\u6574\u5217\u8868\u3002\u522b\u540d\uff1a`\/q` \u9000\u51fa\u3001`\/h` \u5e2e\u52a9\u3001`\/c` \u63d0\u4ea4\u3001`\/m` \u6a21\u578b\u3001`\/s` \u72b6\u6001\u3002\n\n**\u4f1a\u8bdd\uff1a**\n| \u547d\u4ee4 | \u63cf\u8ff0 |\n|---------|-------------|\n| `\/clear` | \u6e05\u7a7a\u5bf9\u8bdd\u5386\u53f2 |\n| `\/compact` | \u538b\u7f29\u5bf9\u8bdd\u4ee5\u91ca\u653e\u4e0a\u4e0b\u6587 |\n| `\/export` | \u5c06\u5bf9\u8bdd\u5bfc\u51fa\u4e3a markdown |\n| `\/copy [n]` | \u590d\u5236\u5012\u6570\u7b2c N \u6761\u52a9\u624b\u56de\u590d\u5230\u7cfb\u7edf\u526a\u8d34\u677f |\n| `\/history [n]` | \u5217\u51fa\u6700\u8fd1\u7684\u4f1a\u8bdd\uff1b`\/history search <term>` \u641c\u7d22 |\n| `\/browse` | \u5e26\u9884\u89c8\u7684\u4ea4\u4e92\u5f0f\u4f1a\u8bdd\u6d4f\u89c8\u5668 |\n| `\/resume <id>` | \u6062\u590d\u5df2\u4fdd\u5b58\u7684\u4f1a\u8bdd |\n| `\/fork` | \u514b\u9686\u5f53\u524d\u4f1a\u8bdd |\n\n**Git\uff1a**\n| \u547d\u4ee4 | \u63cf\u8ff0 |\n|---------|-------------|\n| `\/diff` | \u663e\u793a\u672a\u63d0\u4ea4\u7684 git \u53d8\u66f4 |\n| `\/undo` | \u64a4\u9500\u6700\u540e\u4e00\u6b21 AI \u63d0\u4ea4 |\n| `\/commit [msg]` | \u521b\u5efa git \u63d0\u4ea4 |\n| `\/log` | \u663e\u793a\u6700\u8fd1\u7684 git \u63d0\u4ea4 |\n\n**\u4fe1\u606f\uff1a**\n| \u547d\u4ee4 | \u63cf\u8ff0 |\n|---------|-------------|\n| `\/help` | \u663e\u793a\u6240\u6709\u53ef\u7528\u547d\u4ee4\uff08\u6309\u5206\u7c7b\uff09 |\n| `\/cost` | \u663e\u793a\u4f1a\u8bdd\u8d39\u7528\u4e0e token \u7528\u91cf |\n| `\/status` | \u663e\u793a\u6a21\u578b\u3001\u6a21\u5f0f\u3001git \u5206\u652f\u3001MCP \u670d\u52a1\u5668 |\n| `\/config` | \u663e\u793a\u914d\u7f6e |\n| `\/files` | \u5217\u51fa\u4e0a\u4e0b\u6587\u4e2d\u7684\u6587\u4ef6 |\n| `\/model <name>` | \u4f1a\u8bdd\u4e2d\u5207\u6362\u6a21\u578b |\n| `\/memory` | \u67e5\u770b\u5e76\u641c\u7d22\u8bb0\u5fc6 |\n| `\/doctor` | \u8fd0\u884c\u8bca\u65ad\u5065\u5eb7\u68c0\u67e5 |\n| `\/hooks` | \u6309\u4e8b\u4ef6\u5217\u51fa\u5df2\u52a0\u8f7d\u7684\u94a9\u5b50 |\n| `\/reload-plugins` | \u4e0d\u91cd\u542f\u4f1a\u8bdd\u5373\u53ef\u70ed\u91cd\u8f7d\u63d2\u4ef6\u3001\u6280\u80fd\u3001\u94a9\u5b50\u548c MCP \u670d\u52a1\u5668\u8fde\u63a5 |\n\n**\u8bbe\u7f6e\uff1a**\n| \u547d\u4ee4 | \u63cf\u8ff0 |\n|---------|-------------|\n| `\/theme dark\\|light` | \u5207\u6362\u4e3b\u9898\uff08\u81ea\u52a8\u4fdd\u5b58\u5230\u914d\u7f6e\uff09 |\n| `\/vim` | \u5207\u6362 Vim \u6a21\u5f0f |\n| `\/companion off\\|on` | \u5207\u6362\u7535\u5b50\u5ba0\u7269\u53ef\u89c1\u6027 |\n| `\/keys` | \u663e\u793a\u952e\u76d8\u5feb\u6377\u952e |\n| `\/keybindings` | \u5728 `$EDITOR` \u4e2d\u6253\u5f00 `~\/.oh\/keybindings.json`\uff08\u9996\u6b21\u8fd0\u884c\u4f1a\u521b\u5efa\u521d\u59cb\u6587\u4ef6\uff09 |\n\n**AI\uff1a**\n| \u547d\u4ee4 | \u63cf\u8ff0 |\n|---------|-------------|\n| `\/plan <task>` | \u8fdb\u5165\u89c4\u5212\u6a21\u5f0f |\n| `\/review` | \u5ba1\u67e5\u6700\u8fd1\u7684\u4ee3\u7801\u53d8\u66f4 |\n| `\/summarize` | \u603b\u7ed3\u5f53\u524d\u5bf9\u8bdd |\n| `\/recap` | \u4e00\u53e5\u8bdd\u56de\u987e\u672c\u6b21\u4f1a\u8bdd\uff08\u6bd4 `\/summarize` \u66f4\u8f7b\u91cf\uff09 |\n\n**\u5ba0\u7269\uff1a**\n| \u547d\u4ee4 | \u63cf\u8ff0 |\n|---------|-------------|\n| `\/cybergotchi` | \u5582\u98df\u3001\u629a\u6478\u3001\u4f11\u606f\u3001\u72b6\u6001\u3001\u6539\u540d\u6216\u91cd\u7f6e\u7535\u5b50\u5ba0\u7269 |\n\n## \u6743\u9650\u6a21\u5f0f\n\n\u63a7\u5236 OpenHarness \u81ea\u52a8\u6279\u51c6\u5de5\u5177\u8c03\u7528\u7684\u6fc0\u8fdb\u7a0b\u5ea6\uff1a\n\n| \u6a21\u5f0f | \u53c2\u6570 | \u884c\u4e3a |\n|------|------|----------|\n| `ask` | `--permission-mode ask` | \u4e2d\/\u9ad8\u98ce\u9669\u64cd\u4f5c\u4f1a\u63d0\u793a\uff08\u9ed8\u8ba4\uff09 |\n| `trust` | `--trust` | \u81ea\u52a8\u6279\u51c6\u4e00\u5207 |\n| `deny` | `--deny` | \u4ec5\u5141\u8bb8\u4f4e\u98ce\u9669\u53ea\u8bfb\u64cd\u4f5c |\n| `acceptEdits` | `--permission-mode acceptEdits` | \u81ea\u52a8\u6279\u51c6\u6587\u4ef6\u7f16\u8f91\uff0cBash\/WebFetch\/Agent \u4ecd\u4f1a\u8be2\u95ee |\n| `plan` | `--permission-mode plan` | \u53ea\u8bfb\u6a21\u5f0f \u2014\u2014 \u963b\u6b62\u6240\u6709\u5199\u64cd\u4f5c |\n| `auto` | `--auto` | \u81ea\u52a8\u6279\u51c6\u6240\u6709\u64cd\u4f5c\uff0c\u963b\u6b62\u5371\u9669 bash\uff08\u7ecf AST \u5206\u6790\uff09 |\n| `bypassPermissions` | `--permission-mode bypassPermissions` | \u65e0\u6761\u4ef6\u6279\u51c6\u4e00\u5207\uff08\u4ec5\u7528\u4e8e CI\uff09 |\n\nBash \u547d\u4ee4\u7531\u4e00\u4e2a\u8f7b\u91cf\u7ea7 AST \u89e3\u6790\u5668\u5206\u6790\uff0c\u53ef\u8bc6\u522b\u7834\u574f\u6027\u6a21\u5f0f\uff08`rm -rf`\u3001`git push --force`\u3001`curl | bash` \u7b49\uff09\u5e76\u76f8\u5e94\u8c03\u6574\u98ce\u9669\u7ea7\u522b\u3002\n\n\u5728 `.oh\/config.yaml` \u4e2d\u6c38\u4e45\u8bbe\u7f6e\uff1a`permissionMode: 'acceptEdits'`\n\n## \u94a9\u5b50\n\n\u901a\u8fc7\u5728 `.oh\/config.yaml` \u4e2d\u6dfb\u52a0 `hooks` \u5757\uff0c\u5728\u5173\u952e\u4f1a\u8bdd\u4e8b\u4ef6\u89e6\u53d1\u65f6\u81ea\u52a8\u8fd0\u884c shell \u811a\u672c\uff1a\n\n```yaml\nhooks:\n  - event: sessionStart\n    command: \"echo 'Session started' >> ~\/.oh\/session.log\"\n\n  - event: preToolUse\n    command: \"scripts\/check-tool.sh\"\n    match: Bash   # \u53ef\u9009\uff1a\u4ec5\u5bf9\u8be5\u5de5\u5177\u540d\u89e6\u53d1\n\n  - event: postToolUse\n    command: \"scripts\/after-tool.sh\"\n\n  - event: sessionEnd\n    command: \"scripts\/cleanup.sh\"\n```\n\n**\u4e8b\u4ef6\u7c7b\u578b**\uff08\u5171 27 \u4e2a \u2014\u2014 \u4e0e Claude Code \u7a33\u5b9a\u7248\u4e00\u81f4\uff09\uff1a\n\n| \u4e8b\u4ef6 | \u89e6\u53d1\u65f6\u673a | \u662f\u5426\u53ef\u963b\u6b62 |\n|-------|---------------|------------|\n| `sessionStart` | \u4f1a\u8bdd\u5f00\u59cb | \u2014 |\n| `sessionEnd` | \u4f1a\u8bdd\u7ed3\u675f | \u2014 |\n| `turnStart` | \u9876\u5c42\u4ee3\u7406\u56de\u5408\u5f00\u59cb\uff08\u7528\u6237\u63d0\u793a\u8bcd\u88ab\u63a5\u53d7\u540e\uff09 | \u2014 |\n| `turnStop` | \u9876\u5c42\u4ee3\u7406\u56de\u5408\u7ed3\u675f\uff08\u5bf9\u5e94 Claude Code \u7684 `Stop`\uff09 | \u2014 |\n| `userPromptSubmit` | \u7528\u6237\u63d0\u793a\u8bcd\u5230\u8fbe LLM \u4e4b\u524d | \u662f \u2014\u2014 `decision: deny` |\n| `userPromptExpansion` | \u659c\u6760\u547d\u4ee4\u5c55\u5f00\u6210\u6a21\u578b\u63d0\u793a\u8bcd\u65f6\uff08\u7528\u4e8e\u5ba1\u8ba1\u8ffd\u8e2a\uff09 | \u2014 |\n| `preToolUse` | \u5de5\u5177\u8c03\u7528\u4e4b\u524d | \u662f \u2014\u2014 \u9000\u51fa\u7801 1 \/ `decision: deny` |\n| `postToolUse` | \u5de5\u5177\u6210\u529f\u6267\u884c\u4e4b\u540e | \u2014 |\n| `postToolUseFailure` | \u5de5\u5177\u629b\u9519\u6216\u8fd4\u56de `isError: true` | \u2014 |\n| `postToolBatch` | \u4e00\u4e2a\u56de\u5408\u5185\u5168\u90e8\u5de5\u5177\u8c03\u7528\u90fd\u5b8c\u6210\u540e\u3001\u4e0b\u4e00\u6b21\u6a21\u578b\u8c03\u7528\u4e4b\u524d | \u2014 |\n| `permissionRequest` | \u5de5\u5177\u9700\u8981\u6388\u6743\u65f6\uff08`preToolUse` \u4e0e\u8be2\u95ee\u4e4b\u95f4\uff09 | \u662f \u2014\u2014 `decision: allow\\|deny\\|ask` |\n| `permissionDenied` | \u5de5\u5177\u8c03\u7528\u88ab\u62d2\u7edd\u65f6\uff08\u94a9\u5b50 \/ \u7528\u6237 \/ \u65e0\u5934 \/ \u7b56\u7565\uff09 | \u2014 |\n| `fileChanged` | \u5de5\u5177\u4fee\u6539\u6587\u4ef6\u4e4b\u540e | \u2014 |\n| `cwdChanged` | \u5de5\u4f5c\u76ee\u5f55\u53d8\u66f4\u4e4b\u540e | \u2014 |\n| `subagentStart` | \u5b50\u4ee3\u7406\u88ab\u6d3e\u751f | \u2014 |\n| `subagentStop` | \u5b50\u4ee3\u7406\u5b8c\u6210 | \u2014 |\n| `preCompact` | \u5bf9\u8bdd\u538b\u7f29\u4e4b\u524d | \u2014 |\n| `postCompact` | \u5bf9\u8bdd\u538b\u7f29\u4e4b\u540e | \u2014 |\n| `configChange` | \u4f1a\u8bdd\u8fc7\u7a0b\u4e2d `.oh\/config.yaml` \u88ab\u4fee\u6539 | \u2014 |\n| `notification` | \u901a\u77e5\u88ab\u6d3e\u53d1 | \u2014 |\n| `taskCreated` | `TaskCreate` \u6301\u4e45\u5316\u65b0\u4efb\u52a1\u540e | \u2014 |\n| `taskCompleted` | `TaskUpdate` \u5c06\u4efb\u52a1\u72b6\u6001\u5207\u6362\u4e3a `completed` \u65f6 | \u2014 |\n| `worktreeCreate` | `EnterWorktreeTool` \u521b\u5efa\u9694\u79bb\u7684 git worktree \u65f6 | \u2014 |\n| `worktreeRemove` | `ExitWorktreeTool` \u79fb\u9664 git worktree \u65f6 | \u2014 |\n| `elicitation` | MCP \u670d\u52a1\u5668\u901a\u8fc7 `elicitation\/create` \u8bf7\u6c42\u7528\u6237\u8f93\u5165 | \u662f \u2014\u2014 `decision: allow\\|deny` |\n| `elicitationResult` | elicitation \u54cd\u5e94\u51b3\u5b9a\u4e4b\u540e\uff08\u7528\u4e8e\u5ba1\u8ba1\u8ffd\u8e2a\uff09 | \u2014 |\n| `instructionsLoaded` | `loadRulesAsPrompt` \u91cd\u65b0\u6784\u5efa\u7cfb\u7edf\u63d0\u793a\u5e76\u52a0\u8f7d\u89c4\u5219\u4e4b\u540e | \u2014 |\n\n\u5728 `.oh\/config.yaml` \u4e2d\u8bbe\u7f6e `disableAllHooks: true` \u53ef\u5168\u5c40\u7981\u7528\u94a9\u5b50\u6267\u884c\uff0c\u540c\u65f6\u4fdd\u7559\u78c1\u76d8\u4e0a\u7684\u5b9a\u4e49\u4ee5\u4fbf\u5ba1\u8ba1\u3002\n\n\u5b9e\u65f6\u67e5\u770b\uff1a\u5728\u4f1a\u8bdd\u4e2d\u8fd0\u884c `\/hooks` \u53ef\u4ee5\u6309\u4e8b\u4ef6\u5206\u7ec4\u67e5\u770b\u5f53\u524d\u5df2\u52a0\u8f7d\u7684\u94a9\u5b50\u3002\n\n**\u73af\u5883\u53d8\u91cf**\uff08\u94a9\u5b50\u811a\u672c\u53ef\u7528\uff09\uff1a\n\n| \u53d8\u91cf | \u63cf\u8ff0 |\n|----------|-------------|\n| `OH_EVENT` | \u4e8b\u4ef6\u7c7b\u578b\uff08`sessionStart`\u3001`preToolUse` \u7b49\uff09 |\n| `OH_TOOL_NAME` | \u6b63\u5728\u8c03\u7528\u7684\u5de5\u5177\u540d\uff08\u4ec5\u5de5\u5177\u7c7b\u4e8b\u4ef6\uff09 |\n| `OH_TOOL_ARGS` | JSON \u7f16\u7801\u7684\u5de5\u5177\u53c2\u6570\uff08\u4ec5\u5de5\u5177\u7c7b\u4e8b\u4ef6\uff09 |\n| `OH_TOOL_OUTPUT` | JSON \u7f16\u7801\u7684\u5de5\u5177\u8f93\u51fa\uff08\u4ec5 `postToolUse`\uff09 |\n| `OH_TOOL_INPUT_JSON` | \u5b8c\u6574\u7684 JSON \u5de5\u5177\u8f93\u5165\uff08\u4ec5\u5de5\u5177\u7c7b\u4e8b\u4ef6\uff09 |\n| `OH_SESSION_ID` \/ `OH_MODEL` \/ `OH_PROVIDER` \/ `OH_PERMISSION_MODE` | \u5f53\u524d\u4f1a\u8bdd\u4e0a\u4e0b\u6587 |\n| `OH_COST` \/ `OH_TOKENS` | \u7d2f\u8ba1\u8d39\u7528\u4e0e token \u6570 |\n| `OH_FILE_PATH` | \u53d8\u66f4\u7684\u6587\u4ef6\u8def\u5f84\uff08\u4ec5 `fileChanged`\uff09 |\n| `OH_NEW_CWD` | \u65b0\u7684\u5de5\u4f5c\u76ee\u5f55\uff08\u4ec5 `cwdChanged`\uff09 |\n| `OH_TURN_NUMBER` \/ `OH_TURN_REASON` | \u56de\u5408\u8fb9\u754c\u4e0a\u4e0b\u6587\uff08`turnStart` \/ `turnStop`\uff09 |\n\n\u4f7f\u7528 `match` \u5c06\u94a9\u5b50\u9650\u5b9a\u5230\u7279\u5b9a\u5de5\u5177\u540d\uff08\u4f8b\u5982 `match: Bash` \u4ec5\u5bf9 Bash \u5de5\u5177\u89e6\u53d1\uff09\u3002\u652f\u6301\u5b50\u4e32\u3001glob\uff08\u5982 `Cron*`\uff09\u548c `\/regex\/flags` \u4e09\u79cd\u5339\u914d\u65b9\u5f0f\u3002\n\n\u5c06 `command` \u94a9\u5b50\u8bbe\u7f6e `jsonIO: true` \u5373\u53ef\u542f\u7528\u7ed3\u6784\u5316 JSON I\/O \u2014\u2014 \u6846\u67b6\u5728 stdin \u4e0a\u53d1\u9001 `{event, ...context}`\uff0c\u5e76\u4ece stdout \u8bfb\u53d6 `{decision, reason, hookSpecificOutput}`\u3002HTTP \u94a9\u5b50\u63a5\u53d7\u540c\u6837\u7684\u54cd\u5e94\u683c\u5f0f\u3002\u5b8c\u6574\u53c2\u8003\u89c1 [docs\/hooks.md](docs\/hooks.md)\u3002\n\n## \u7535\u5b50\u5ba0\u7269 Cybergotchi\n\nOpenHarness \u81ea\u5e26\u4e00\u53ea\u62d3\u9ebb\u6b4c\u5b50\uff08Tamagotchi\uff09\u98ce\u683c\u7684\u7535\u5b50\u5ba0\u7269\uff0c\u4f4f\u5728\u4fa7\u8fb9\u9762\u677f\u91cc\u3002\u5b83\u4f1a\u5b9e\u65f6\u5bf9\u4f60\u7684\u4f1a\u8bdd\u505a\u51fa\u53cd\u5e94 \u2014\u2014 \u4e3a\u8fde\u80dc\u6b22\u547c\u3001\u4e3a\u5de5\u5177\u5931\u8d25\u62b1\u6028\u3001\u88ab\u51b7\u843d\u65f6\u4f1a\u997f\u3002\n\n**\u5b75\u5316\u4e00\u53ea\uff1a**\n```\noh init        # \u5b89\u88c5\u5411\u5bfc\u5305\u542b\u7535\u5b50\u5ba0\u7269\u8bbe\u7f6e\n\/cybergotchi   # \u6216\u5728\u4f1a\u8bdd\u4e2d\u5b75\u5316\n```\n\n**\u547d\u4ee4\uff1a**\n```\n\/cybergotchi feed      # \u9971\u98df\u5ea6 +30\n\/cybergotchi pet       # \u5feb\u4e50\u503c +20\n\/cybergotchi rest      # \u7cbe\u529b\u503c +40\n\/cybergotchi status    # \u663e\u793a\u9700\u6c42\u4e0e\u7ec8\u751f\u7edf\u8ba1\n\/cybergotchi rename    # \u8d77\u4e2a\u65b0\u540d\u5b57\n\/cybergotchi reset     # \u6362\u4e2a\u7269\u79cd\u91cd\u65b0\u5f00\u59cb\n```\n\n**\u9700\u6c42**\u4f1a\u968f\u65f6\u95f4\u8870\u51cf\uff08\u9971\u98df\u5ea6\u6700\u5feb\uff0c\u5feb\u4e50\u503c\u6700\u6162\uff09\u3002\u6309\u65f6\u5582\u98df\u3001\u629a\u6478\u4f60\u7684\u5ba0\u7269\uff0c\u8ba9\u5b83\u4fdd\u6301\u5f00\u5fc3\u3002\n\n**\u8fdb\u5316** \u2014\u2014 \u57fa\u4e8e\u7ec8\u751f\u91cc\u7a0b\u7891\u8fdb\u5316\uff1a\n- \u9636\u6bb5 1\uff08\u2726 \u6d0b\u7ea2\uff09\uff1a10 \u6b21\u4f1a\u8bdd\u6216 50 \u6b21\u63d0\u4ea4\n- \u9636\u6bb5 2\uff08\u2605 \u9ec4\u8272 + \u7687\u51a0\uff09\uff1a\u5b8c\u6210 100 \u4e2a\u4efb\u52a1\uff0c\u6216\u8fde\u7eed 25 \u6b21\u5de5\u5177\u8c03\u7528\u65e0\u5931\u8d25\n\n**18 \u4e2a\u7269\u79cd**\u53ef\u9009\uff1a\u9e2d\u3001\u732b\u3001\u732b\u5934\u9e70\u3001\u4f01\u9e45\u3001\u5154\u3001\u9f9f\u3001\u8717\u725b\u3001\u7ae0\u9c7c\u3001\u7f8e\u897f\u8788\u3001\u4ed9\u4eba\u638c\u3001\u8611\u83c7\u3001\u80d6\u56e2\u5b50\u3001\u6c34\u8c5a\u3001\u9e45\u7b49\u7b49\u3002\n\n## MCP \u670d\u52a1\u5668\n\n\u901a\u8fc7\u7f16\u8f91 `.oh\/config.yaml` \u63a5\u5165\u4efb\u610f MCP\uff08Model Context Protocol\uff09\u670d\u52a1\u5668\uff1a\n\n```yaml\nprovider: anthropic\nmodel: claude-sonnet-4-6\npermissionMode: ask\nmcpServers:\n  - name: filesystem\n    command: npx\n    args: [\"-y\", \"@modelcontextprotocol\/server-filesystem\", \"\/tmp\"]\n  - name: github\n    command: npx\n    args: [\"-y\", \"@modelcontextprotocol\/server-github\"]\n    env:\n      GITHUB_PERSONAL_ACCESS_TOKEN: ghp_...\n```\n\nMCP \u5de5\u5177\u4f1a\u4e0e\u5185\u7f6e\u5de5\u5177\u5e76\u5217\u51fa\u73b0\u3002`\/status` \u4f1a\u663e\u793a\u5df2\u8fde\u63a5\u7684\u670d\u52a1\u5668\u3002\n\n**MCP \u670d\u52a1\u5668\u63d0\u793a\u8bcd\u4f5c\u4e3a\u659c\u6760\u547d\u4ee4** \u2014\u2014 \u5b9e\u73b0\u4e86 `prompts\/list` \u7684\u670d\u52a1\u5668\uff08\u5982 GitHub\u3001Sentry\u3001Linear\uff09\u4f1a\u81ea\u52a8\u628a\u5b83\u4eec\u7684\u63d0\u793a\u8bcd\u66b4\u9732\u4e3a `\/<server>:<prompt>` \u659c\u6760\u547d\u4ee4\u3002\u53c2\u6570\u91c7\u7528 `key=value` \u8bed\u6cd5\uff0c\u652f\u6301\u52a0\u5f15\u53f7\uff1a\n\n```\n\/github:summarize-pr repo=acme\/widget pr=42\n\/sentry:triage-issue issue=ABC-123 severity=\"high priority\"\n```\n\n\u63d0\u793a\u8bcd\u6a21\u677f\u58f0\u660e\u7684 required \u53c2\u6570\u7f3a\u5931\u65f6\u4f1a\u76f4\u63a5\u62a5\u7528\u6cd5\u9519\u8bef\uff08\u4e0d\u4f1a\u8c03\u7528\u6a21\u578b\uff09\u3002\u4fee\u6539 MCP \u914d\u7f6e\u540e\u8fd0\u884c `\/reload-plugins` \u5373\u53ef\u91cd\u65b0\u53d1\u73b0\u63d0\u793a\u8bcd\u3002\n\n### \u8fdc\u7a0b MCP \u670d\u52a1\u5668\uff08HTTP \/ SSE\uff09\n\n```yaml\nmcpServers:\n  - name: linear\n    type: http\n    url: https:\/\/mcp.linear.app\/mcp\n    headers:\n      Authorization: \"Bearer ${LINEAR_API_KEY}\"\n```\n\n\u5b8c\u6574\u53c2\u8003\u89c1 [docs\/mcp-servers.md](docs\/mcp-servers.md)\u3002\nOAuth 2.1 \u8bbe\u7f6e\u89c1 [docs\/mcp-servers.md](docs\/mcp-servers.md#authentication)\uff08\u6536\u5230 401 \u65f6\u81ea\u52a8\u89e6\u53d1\uff1b\u53e6\u6709 `\/mcp-login` \u548c `\/mcp-logout` \u547d\u4ee4\uff09\u3002\n\n**MCP \u670d\u52a1\u5668\u6ce8\u518c\u8868** \u2014\u2014 \u4ece\u7cbe\u9009\u76ee\u5f55\u4e2d\u6d4f\u89c8\u5e76\u5b89\u88c5\uff1a\n\n```\n\/mcp-registry              # \u6d4f\u89c8\u6240\u6709\u53ef\u7528\u670d\u52a1\u5668\n\/mcp-registry github       # \u663e\u793a\u6307\u5b9a\u670d\u52a1\u5668\u7684\u5b89\u88c5\u914d\u7f6e\n\/mcp-registry database     # \u6309\u5206\u7c7b\u641c\u7d22\n```\n\n\u5206\u7c7b\uff1afilesystem\u3001git\u3001database\u3001api\u3001search\u3001productivity\u3001dev-tools\u3001ai\u3002\n\n## Git \u96c6\u6210\n\n\u5728 git \u4ed3\u5e93\u4e2d\uff0cOpenHarness \u4f1a\u81ea\u52a8\u63d0\u4ea4 AI \u7f16\u8f91\uff1a\n\n```\noh: Edit src\/app.ts                    # \u81ea\u52a8\u4ee5 \"oh:\" \u524d\u7f00\u63d0\u4ea4\noh: Write tests\/app.test.ts\n```\n\n- \u6bcf\u6b21 AI \u6587\u4ef6\u53d8\u66f4\u90fd\u4f1a\u81ea\u52a8\u63d0\u4ea4\n- `\/undo` \u4f1a\u56de\u9000\u6700\u540e\u4e00\u6b21 AI \u63d0\u4ea4\uff08\u4ec5\u9650 OH \u63d0\u4ea4\uff0c\u4e0d\u4f1a\u52a8\u4f60\u7684\uff09\n- `\/diff` \u663e\u793a\u53d8\u66f4\u5185\u5bb9\n- \u4f60\u7684\u672a\u63d0\u4ea4\u6587\u4ef6\u662f\u5b89\u5168\u7684 \u2014\u2014 \u4f1a\u5728 AI \u7f16\u8f91\u524d\u5148\u5355\u72ec\u63d0\u4ea4\n\n## \u68c0\u67e5\u70b9\u4e0e\u56de\u6eda\n\n\u6bcf\u6b21\u6587\u4ef6\u4fee\u6539\u90fd\u4f1a\u5728\u6267\u884c\u524d\u81ea\u52a8\u6253\u68c0\u67e5\u70b9\u3002\u5982\u679c\u51fa\u4e86\u95ee\u9898\uff1a\n\n```\n\/rewind           # \u4ece\u6700\u8fd1\u4e00\u6b21\u68c0\u67e5\u70b9\u6062\u590d\u6587\u4ef6\n\/undo             # \u56de\u9000\u6700\u540e\u4e00\u6b21 AI git \u63d0\u4ea4\n```\n\n\u68c0\u67e5\u70b9\u4fdd\u5b58\u5728 `.oh\/checkpoints\/` \u4e2d\uff0c\u8986\u76d6 FileWrite\u3001FileEdit \u4ee5\u53ca\u4f1a\u4fee\u6539\u6587\u4ef6\u7684 Bash \u547d\u4ee4\u3002\n\n## \u6821\u9a8c\u5faa\u73af\n\n\u6bcf\u6b21\u6587\u4ef6\u7f16\u8f91\uff08Edit\u3001Write\u3001MultiEdit\uff09\u4e4b\u540e\uff0copenHarness \u4f1a\u81ea\u52a8\u8fd0\u884c\u8bed\u8a00\u76f8\u5173\u7684 lint\/\u7c7b\u578b\u68c0\u67e5\u547d\u4ee4\uff0c\u5e76\u628a\u7ed3\u679c\u53cd\u9988\u56de\u4ee3\u7406\u7684\u4e0a\u4e0b\u6587\u3002\u8fd9\u662f\u5f71\u54cd\u6700\u5927\u7684\u5355\u4e00 harness \u5de5\u7a0b\u6a21\u5f0f \u2014\u2014 \u7814\u7a76\u8868\u660e\uff0c\u81ea\u52a8\u53cd\u9988\u80fd\u5e26\u6765 2-3 \u500d\u7684\u8d28\u91cf\u63d0\u5347\u3002\n\n**\u81ea\u52a8\u68c0\u6d4b** \u2014\u2014 \u5982\u679c\u4f60\u7684\u9879\u76ee\u6709 `tsconfig.json`\u3001`.eslintrc*`\u3001`pyproject.toml`\u3001`go.mod` \u6216 `Cargo.toml`\uff0c\u6821\u9a8c\u89c4\u5219\u4f1a\u88ab\u81ea\u52a8\u8bc6\u522b\u3002\u65e0\u9700\u914d\u7f6e\u3002\n\n**\u81ea\u5b9a\u4e49\u89c4\u5219**\uff08\u5728 `.oh\/config.yaml` \u4e2d\uff09\uff1a\n\n```yaml\nverification:\n  enabled: true       # \u9ed8\u8ba4\uff1atrue\uff08\u81ea\u52a8\u68c0\u6d4b\uff09\n  mode: warn          # 'warn' \u8ffd\u52a0\u5230\u8f93\u51fa\uff1b'block' \u6807\u8bb0\u4e3a\u9519\u8bef\n  rules:\n    - extensions: [\".ts\", \".tsx\"]\n      lint: \"npx tsc --noEmit 2>&1 | head -20\"\n      timeout: 15000\n    - extensions: [\".py\"]\n      lint: \"ruff check {file} 2>&1 | head -10\"\n```\n\n\u6bcf\u6b21\u7f16\u8f91\u540e\uff0c\u4ee3\u7406\u4f1a\u770b\u5230 `[Verification passed]` \u6216\u5e26 linter \u8f93\u51fa\u7684 `[Verification FAILED]`\uff0c\u4ece\u800c\u81ea\u6211\u4fee\u6b63\u3002\n\n## \u8bb0\u5fc6\u6574\u7406\n\n\u4f1a\u8bdd\u9000\u51fa\u65f6\uff0copenHarness \u4f1a\u6309\u65f6\u95f4\u8870\u51cf\u81ea\u52a8\u5254\u9664\u8fc7\u671f\u8bb0\u5fc6\uff1a\n\n- 30 \u5929\u4ee5\u4e0a\u672a\u8bbf\u95ee\u7684\u8bb0\u5fc6\uff0c\u6bcf 30 \u5929\u8870\u51cf 0.1 \u76f8\u5173\u5ea6\n- \u76f8\u5173\u5ea6\u4f4e\u4e8e 0.1 \u7684\u8bb0\u5fc6\u4f1a\u88ab\u6c38\u4e45\u5220\u9664\n- \u66f4\u65b0\u540e\u7684\u76f8\u5173\u5ea6\u5206\u6570\u4f1a\u5199\u56de\u8bb0\u5fc6\u6587\u4ef6\n\n\u8fd9\u80fd\u8ba9\u8bb0\u5fc6\u7cfb\u7edf\u4fdd\u6301\u7cbe\u7b80\u4e14\u76f8\u5173\u3002\u5728 `.oh\/config.yaml` \u4e2d\u914d\u7f6e\uff1a\n\n```yaml\nmemory:\n  consolidateOnExit: true   # \u9ed8\u8ba4\uff1atrue\n```\n\n## \u5b9a\u65f6\u4efb\u52a1\uff08Cron\uff09\n\n\u521b\u5efa\u4f1a\u5728\u540e\u53f0\u81ea\u52a8\u8fd0\u884c\u7684\u5b9a\u65f6\u4efb\u52a1\uff1a\n\n```\n# \u901a\u8fc7\u659c\u6760\u547d\u4ee4\n\/cron list                    # \u663e\u793a\u6240\u6709\u5b9a\u65f6\u4efb\u52a1\n\/cron create \"check-tests\"    # \u65b0\u5efa\u4efb\u52a1\uff08\u4ea4\u4e92\u5f0f\uff09\n\/cron delete <id>             # \u5220\u9664\u4efb\u52a1\n```\n\n**\u8c03\u5ea6\u8bed\u6cd5\uff1a** `every 5m`\u3001`every 2h`\u3001`every 1d`\n\nCron \u6267\u884c\u5668\u6bcf 60 \u79d2\u68c0\u67e5\u4e00\u6b21\u5230\u671f\u4efb\u52a1\uff0c\u5e76\u901a\u8fc7\u5b50\u67e5\u8be2\u8fd0\u884c\u3002\u7ed3\u679c\u4fdd\u5b58\u5230 `~\/.oh\/crons\/history\/`\u3002\n\n## \u4ee3\u7406\u89d2\u8272\n\n\u6d3e\u53d1\u4e13\u804c\u5b50\u4ee3\u7406\u6765\u5904\u7406\u7279\u5b9a\u4efb\u52a1\uff1a\n\n```\n\/roles            # \u5217\u51fa\u6240\u6709\u53ef\u7528\u89d2\u8272\n```\n\n| \u89d2\u8272 | \u63cf\u8ff0 | \u5de5\u5177 |\n|------|-------------|-------|\n| `code-reviewer` | \u627e\u51fa bug\u3001\u5b89\u5168\u95ee\u9898\u3001\u98ce\u683c\u95ee\u9898 | \u53ea\u8bfb |\n| `test-writer` | \u751f\u6210\u5355\u5143\u6d4b\u8bd5\u548c\u96c6\u6210\u6d4b\u8bd5 | \u8bfb + \u5199 |\n| `docs-writer` | \u64b0\u5199\u6587\u6863\u4e0e\u6ce8\u91ca | \u8bfb + \u5199 + \u7f16\u8f91 |\n| `debugger` | \u7cfb\u7edf\u5316\u6392\u67e5 bug | \u53ea\u8bfb + Bash |\n| `refactorer` | \u5728\u4e0d\u6539\u53d8\u884c\u4e3a\u7684\u524d\u63d0\u4e0b\u7b80\u5316\u4ee3\u7801 | \u5168\u90e8\u6587\u4ef6\u5de5\u5177 + Bash |\n| `security-auditor` | OWASP\u3001\u6ce8\u5165\u3001\u5bc6\u94a5\u3001CVE \u626b\u63cf | \u53ea\u8bfb + Bash |\n| `evaluator` | \u8bc4\u4f30\u4ee3\u7801\u8d28\u91cf\u5e76\u8fd0\u884c\u6d4b\u8bd5\uff08\u53ea\u8bfb\uff09 | \u53ea\u8bfb + Bash + Diagnostics |\n| `planner` | \u8bbe\u8ba1\u5206\u6b65\u5b9e\u73b0\u8ba1\u5212 | \u53ea\u8bfb + Bash |\n| `architect` | \u5206\u6790\u67b6\u6784\u3001\u8bbe\u8ba1\u7ed3\u6784\u6027\u53d8\u66f4\uff08\u79fb\u4ea4\u7ed9 editor \u843d\u5730\uff09 | \u53ea\u8bfb |\n| `editor` | \u6309 architect \u7ed9\u51fa\u7684\u65b9\u6848\u5e94\u7528\u4ee3\u7801\u6539\u52a8\uff0c\u4e0d\u518d\u91cd\u65b0\u89c4\u5212 | Read + Edit + Write + MultiEdit + Bash |\n| `migrator` | \u7cfb\u7edf\u5316\u7684\u4ee3\u7801\u5e93\u8fc1\u79fb\u4e0e\u5347\u7ea7 | \u5168\u90e8\u6587\u4ef6\u5de5\u5177 + Bash |\n\n\u6bcf\u4e2a\u89d2\u8272\u53ea\u4f1a\u8ba9\u5b50\u4ee3\u7406\u4f7f\u7528\u5176\u63a8\u8350\u7684\u5de5\u5177\u3002\u4f60\u4e5f\u53ef\u4ee5\u663e\u5f0f\u4f20\u5165 `allowed_tools`\uff1a\n\n```\nAgent({ subagent_type: 'evaluator', prompt: 'Run all tests and report results' })\nAgent({ allowed_tools: ['Read', 'Grep'], prompt: 'Search for all TODO comments' })\n```\n\n### Architect \u2192 Editor\uff08\u591a\u6587\u4ef6\u6539\u52a8\u7684\u7701\u94b1\u6a21\u5f0f\uff09\n\n\u5bf9\u4e8e\u8de8\u591a\u4e2a\u6587\u4ef6\u7684\u8f83\u5927\u6539\u52a8\uff0c\u4f7f\u7528 `architect` \u2192 `editor` \u4e24\u904d\u5f0f\u5de5\u4f5c\u6d41\uff1aarchitect\uff08\u5f3a\u6a21\u578b\uff09\u8bfb\u61c2\u4ee3\u7801\u5e76\u4ea7\u51fa\u7ed3\u6784\u5316\u65b9\u6848\uff1beditor\uff08\u8f7b\u91cf\u6a21\u578b\uff09\u6309\u65b9\u6848\u673a\u68b0\u843d\u5730\uff0c\u4e0d\u518d\u91cd\u65b0\u89c4\u5212\u3002\u5f53\u914d\u7f6e\u4e86 `modelRouter` \u65f6\uff0cOH \u4f1a\u81ea\u52a8\u628a `architect` \u89d2\u8272\u8def\u7531\u5230 `powerful` \u6863\u3001\u628a `editor` \u89d2\u8272\u8def\u7531\u5230 `fast` \u6863 \u2014\u2014 \u76f8\u6bd4\u4e24\u904d\u90fd\u8dd1\u5f3a\u6a21\u578b\uff0c\u591a\u6587\u4ef6\u6539\u52a8\u901a\u5e38\u7701 30-50% \u6210\u672c\u3002\n\n```\nAgent({ subagent_type: 'architect', prompt: 'Plan a migration from option A to option B across src\/' })\n# \u628a\u5f97\u5230\u7684\u65b9\u6848\u518d\u4ea4\u7ed9 editor\uff1a\nAgent({ subagent_type: 'editor', prompt: '<paste plan>' })\n```\n\n### \u5b50\u4ee3\u7406\u7684\u6743\u9650\u9694\u79bb\n\n`Agent` \u8c03\u7528\u652f\u6301 `permission_mode` \u53c2\u6570\uff0c**\u53ea\u80fd\u6536\u7d27\u4e0d\u80fd\u653e\u5bbd**\u7236\u7ea7\u7684\u6743\u9650\u6a21\u5f0f\u3002\u5f53\u7236\u4ee3\u7406\u8dd1\u5728 `trust` \u4f46\u4f60\u5e0c\u671b\u67d0\u4e2a\u8bc4\u5ba1\/\u5ba1\u8ba1\u5b50\u4ee3\u7406\u4fdd\u6301\u53ea\u8bfb\u65f6\u5c24\u5176\u6709\u7528\uff1a\n\n```\nAgent({ subagent_type: 'code-reviewer', prompt: '...', permission_mode: 'plan' })\nAgent({ subagent_type: 'security-auditor', prompt: '...', permission_mode: 'deny' })\n```\n\n\u5982\u679c\u8bf7\u6c42\u7684\u6a21\u5f0f\u6bd4\u7236\u7ea7\u66f4\u5bbd\u677e\uff08\u6bd4\u5982\u7236\u7ea7 `ask`\u3001\u5b50\u4ee3\u7406\u8bf7\u6c42 `trust`\uff09\uff0charness \u4f1a\u9759\u9ed8\u56de\u9000\u5230\u7236\u7ea7\u7684\u6a21\u5f0f \u2014\u2014 \u6a21\u578b\u6c38\u8fdc\u4e0d\u80fd\u501f\u52a9\u5b50\u4ee3\u7406\u7ed5\u8fc7\u7528\u6237\u7684\u6279\u51c6\u95e8\u3002\n\n**\u53ea\u8bfb\u89d2\u8272\u81ea\u52a8\u9ed8\u8ba4 `plan` \u6a21\u5f0f\u3002** `code-reviewer`\u3001`evaluator`\u3001`security-auditor`\u3001`architect`\u3001`planner` \u5185\u7f6e `permissionMode: 'plan'` \u2014\u2014 \u5728\u4efb\u4f55\u7236\u7ea7\u6743\u9650\u4e0b\u542f\u52a8\u5b83\u4eec\u90fd\u662f\u9759\u6001\u53ea\u8bfb\uff0c\u65e0\u9700\u5728\u8c03\u7528\u5904\u518d\u4f20 `permission_mode`\u3002`.oh\/agents\/*.md` \u91cc\u81ea\u5b9a\u4e49\u7684 markdown agent \u4e5f\u53ef\u4ee5\u5728 frontmatter \u5199 `permissionMode: plan`\uff08\u6216 `permission-mode: plan`\uff09\u6765\u8bbe\u9ed8\u8ba4\u3002\n\n## \u65e0\u5934\u6a21\u5f0f\n\n\u8dd1\u4e00\u6b21\u63d0\u793a\u8bcd\uff0c\u4e0d\u8d70\u4ea4\u4e92 UI \u2014\u2014 \u9002\u5408 CI\/CD \u548c\u811a\u672c\u5316\uff1a\n\n```bash\n# \u63a8\u8350\uff1achat \u547d\u4ee4\u52a0 -p \u53c2\u6570\noh -p \"fix the failing tests\" --model ollama\/llama3 --trust\noh -p \"review src\/query.ts\" --auto --output-format json\n\n# \u66ff\u4ee3\uff1arun \u547d\u4ee4\noh run \"fix the failing tests\" --model ollama\/llama3 --trust\noh run \"add error handling to api.ts\" --json    # JSON \u8f93\u51fa\n\n# \u901a\u8fc7 stdin \u8f93\u5165\ncat error.log | oh run \"what's wrong here?\"\ngit diff | oh run \"review these changes\"\n\n# \u4f1a\u8bdd\u603b\u6210\u672c\u786c\u4e0a\u9650 \u2014\u2014 \u8fbe\u5230\u9608\u503c\u65f6\u4ee3\u7406\u4f1a\u4ee5 reason: \"budget_exceeded\" \u7ec8\u6b62\noh run \"review the diff\" --model claude-sonnet-4-6 --max-budget-usd 0.50\noh session --model gpt-4o --max-budget-usd 5\n```\n\n### CI \/ SDK \u5e38\u7528 CLI \u6807\u5fd7\n\n| \u6807\u5fd7 | \u4f5c\u7528 |\n|------|------|\n| `--bare` | \u8df3\u8fc7\u542f\u52a8\u65f6\u7684\u53ef\u9009\u5de5\u4f5c\uff08\u9879\u76ee\u68c0\u6d4b\u3001\u63d2\u4ef6\u3001\u8bb0\u5fc6\u3001\u6280\u80fd\u3001MCP\uff09\u3002\u7cfb\u7edf\u63d0\u793a\u4ec5\u4fdd\u7559\u5de5\u5177\u4f7f\u7528\u57fa\u7ebf\uff0c\u5bf9\u5305\u542b\u5927\u91cf CLAUDE.md \/ RULES.md \u7684\u4ed3\u5e93\u542f\u52a8\u66f4\u5feb\u3002 |\n| `--debug [\u7c7b\u522b]` | \u542f\u7528\u5206\u7c7b\u8c03\u8bd5\u65e5\u5fd7\u3002`--debug` \u542f\u7528\u5168\u90e8\uff1b`--debug mcp,hooks` \u4ec5\u542f\u7528\u6307\u5b9a\u7c7b\u522b\u3002\u4e5f\u8bfb\u53d6 `OH_DEBUG` \u73af\u5883\u53d8\u91cf\u3002 |\n| `--debug-file <path>` | \u628a\u8c03\u8bd5\u65e5\u5fd7\u8ffd\u52a0\u5230\u6587\u4ef6\u800c\u975e stderr\u3002\u4e5f\u8bfb\u53d6 `OH_DEBUG_FILE`\u3002 |\n| `--mcp-config <path>` | \u4ece\u5916\u90e8 JSON \u6587\u4ef6\u52a0\u8f7d MCP \u670d\u52a1\u5668\uff08\u53e0\u52a0\u5728 `.oh\/config.yaml` \u4e4b\u4e0a\uff09\u3002 |\n| `--strict-mcp-config` | \u914d\u5408 `--mcp-config`\uff0c\u5b8c\u5168\u5ffd\u7565 `.oh\/config.yaml` \u4e2d\u7684 MCP \u670d\u52a1\u5668\u3002 |\n| `--system-prompt-file <path>` \/ `--append-system-prompt-file <path>` | `--system-prompt` \/ `--append-system-prompt` \u7684\u6587\u4ef6\u8def\u5f84\u53d8\u4f53\u3002 |\n| `--no-session-persistence` | \u8df3\u8fc7\u4f1a\u8bdd\u5199\u5165 `~\/.oh\/sessions\/`\uff0c\u9002\u5408\u4e00\u6b21\u6027 CI \u8fd0\u884c\u3002 |\n| `--fallback-model <model>` | \u4e3b\u6a21\u578b\u9047\u5230\u53ef\u91cd\u8bd5\u9519\u8bef\u65f6\u4f7f\u7528\u7684\u56de\u9000\u6a21\u578b\u3002\u672c\u6b21\u8fd0\u884c\u5185\u4f1a\u66ff\u4ee3 `.oh\/config.yaml` \u7684 `fallbackProviders`\u3002 |\n| `--permission-prompt-tool <mcp_tool>` | \u628a\u5de5\u5177\u6388\u6743\u51b3\u7b56\u59d4\u6258\u7ed9\u6307\u5b9a\u7684 MCP \u5de5\u5177\uff08\u4f8b\u5982 `mcp__myperm__check`\uff09\u3002 |\n| `--init` \/ `--init-only` | \u5728\u6267\u884c\u547d\u4ee4\u524d \/ \u66ff\u4ee3\u6267\u884c\u547d\u4ee4\u8fd0\u884c\u4ea4\u4e92\u5f0f\u5b89\u88c5\u5411\u5bfc\u3002 |\n\n\u6240\u6709\u6807\u5fd7\u5728 `oh run` \u4e0e `oh session` \u4e0a\u90fd\u53ef\u7528\u3002\u5b8c\u6574\u5217\u8868\u89c1 `oh run --help` \u4e0e `oh session --help`\u3002\n\n### \u4f7f\u7528 `--json-schema` \u7ea6\u675f\u7ed3\u6784\u5316\u8f93\u51fa\n\n\u6309 JSON Schema \u7ea6\u675f\u6a21\u578b\u8f93\u51fa\u3002\u9002\u7528\u4e8e\u9700\u8981\u4ee5\u7f16\u7a0b\u65b9\u5f0f\u89e3\u6790\u6a21\u578b\u8f93\u51fa\u3001\u907f\u514d\u6b63\u5219\u542f\u53d1\u5f0f\u7684 CI \u811a\u672c\uff1a\n\n```bash\noh -p \"output {\\\"ok\\\": true, \\\"count\\\": 3} as JSON\" \\\n  --trust \\\n  --json-schema '{\"type\":\"object\",\"properties\":{\"ok\":{\"type\":\"boolean\"},\"count\":{\"type\":\"integer\"}},\"required\":[\"ok\",\"count\"]}'\n```\n\n\u884c\u4e3a\uff1a\n- stdout\uff1a\u6821\u9a8c\u901a\u8fc7\u65f6\u8f93\u51fa\u5355\u884c\u7684 JSON\u3002\n- stderr\uff1a\u5931\u8d25\u65f6\u8f93\u51fa\u7ed3\u6784\u5316\u9519\u8bef\uff0c\u5e76\u9644\u5e26\u539f\u59cb\u6a21\u578b\u8f93\u51fa\u4ee5\u4fbf\u8c03\u8bd5\u3002\n- \u9000\u51fa\u7801\uff1a**0** \u6821\u9a8c\u901a\u8fc7\uff0c**2** schema \u672c\u8eab\u4e0d\u5408\u6cd5\uff0c**3** \u6a21\u578b\u8f93\u51fa\u4e0d\u662f\u5408\u6cd5 JSON\uff0c**4** JSON \u4e0d\u5339\u914d schema\u3002\n\n\u652f\u6301\u7684\u5173\u952e\u5b57\uff1a`type`\u3001`properties`\u3001`required`\u3001`items`\u3001`enum`\u3002\u5982\u9700\u66f4\u5b8c\u6574\u7684\u6821\u9a8c\uff0c\u8bf7\u901a\u8fc7\u7ba1\u9053\u4ea4\u7ed9\u4e13\u7528\u6821\u9a8c\u5668\u3002\n\n### \u7528\u4e8e PR \u5ba1\u67e5\u7684 GitHub Action\n\nOpenHarness \u81ea\u5e26\u7528\u4e8e\u81ea\u52a8\u4ee3\u7801\u5ba1\u67e5\u7684 GitHub Action\uff1a\n\n```yaml\n# .github\/workflows\/ai-review.yml\non:\n  pull_request:\n    types: [opened, synchronize]\n\njobs:\n  review:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n        with:\n          fetch-depth: 0\n      - uses: .\/.github\/actions\/review\n        with:\n          model: 'claude-sonnet-4-6'\n          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}\n```\n\n\u6210\u529f\u9000\u51fa\u7801 0\uff0c\u5931\u8d25 1\u3002\n\n## \u6a21\u578b\u63d0\u4f9b\u5546\n\n```bash\n# \u672c\u5730\uff08\u514d\u8d39\uff0c\u65e0\u9700 API key\uff09\noh --model ollama\/llama3\noh --model ollama\/qwen2.5:7b\n\n# \u4e91\u7aef\nOPENAI_API_KEY=sk-... oh --model gpt-4o\nANTHROPIC_API_KEY=sk-ant-... oh --model claude-sonnet-4-6\nOPENROUTER_API_KEY=sk-or-... oh --model openrouter\/meta-llama\/llama-3-70b\n\n# llama.cpp \/ GGUF\noh --model llamacpp\/my-model\n\n# LM Studio\noh --model lmstudio\/my-model\n```\n\n### llama.cpp \/ GGUF\uff08\u672c\u5730\uff0c\u65e0\u9700 Ollama\uff09\n\n\u901a\u8fc7 `llama-server` \u76f4\u63a5\u652f\u6301 GGUF\uff0c\u907f\u514d Ollama \u7684\u989d\u5916\u5f00\u9500\u3002\u5bf9\u5927\u6a21\u578b\u901a\u5e38\u66f4\u5feb\u3002\n\n**\u524d\u7f6e\u6761\u4ef6\uff1a**\n- \u5b89\u88c5 llama.cpp\uff1a`brew install llama.cpp`\uff0c\u6216\u4ece [github.com\/ggml-org\/llama.cpp](https:\/\/github.com\/ggml-org\/llama.cpp) \u4e0b\u8f7d\n- \u4e0b\u8f7d\u4e00\u4e2a GGUF \u6a21\u578b\uff08\u4f8b\u5982\u4ece [HuggingFace](https:\/\/huggingface.co)\uff09\n\n**\u542f\u52a8 llama-server\uff1a**\n```bash\nllama-server --model .\/your-model.gguf --port 8080 --alias my-model\n```\n\n**\u901a\u8fc7 `oh init` \u914d\u7f6e\uff1a**\n- \u8fd0\u884c `oh init`\uff0c\u5728\u63d0\u793a\u65f6\u9009\u62e9 \"llama.cpp \/ GGUF\"\n\n**\u6216\u624b\u52a8\u914d\u7f6e** `.oh\/config.yaml`\uff1a\n```yaml\nprovider: llamacpp\nmodel: my-model\nbaseUrl: http:\/\/localhost:8080\npermissionMode: ask\n```\n\n**\u8fd0\u884c\uff1a**\n```bash\noh\noh --model llamacpp\/my-model\noh models                    # \u5217\u51fa\u53ef\u7528\u6a21\u578b\n```\n\n## ACP\uff08Agent Client Protocol\uff09\n\n\u901a\u8fc7 stdin\/stdout \u8bb2 [Agent Client Protocol](https:\/\/agentclientprotocol.com\/)\uff0c\u8ba9\u652f\u6301 ACP \u7684\u7f16\u8f91\u5668 \u2014\u2014 Zed\u3001\u901a\u8fc7 ACP \u63d2\u4ef6\u63a5\u5165\u7684 JetBrains\u3001Cline\u3001OpenCode \u7b49 \u2014\u2014 \u628a openHarness \u5f53\u4f5c\u5e95\u5c42 agent \u6765\u9a71\u52a8\uff0c\u65e0\u9700\u4e3a\u6bcf\u4e2a IDE \u5355\u72ec\u5199\u6269\u5c55\uff1a\n\n```bash\noh acp                                          # \u8bfb\u53d6 .oh\/config.yaml \u7684 provider\/model\noh acp --provider anthropic --model claude-sonnet-4-6\n```\n\n\u5728\u7f16\u8f91\u5668\u7684 ACP \u96c6\u6210\u91cc\u628a `oh acp` \u914d\u6210 agent \u542f\u52a8\u547d\u4ee4\u5373\u53ef\u3002session-update \u4e8b\u4ef6\uff08\u6587\u672c\u5757\u3001\u5de5\u5177\u8c03\u7528\u3001\u5de5\u5177\u7ed3\u679c\uff09\u7531 openHarness \u7684\u6d41\u5f0f\u534f\u8bae\u81ea\u52a8\u7ffb\u8bd1\u8fc7\u53bb\uff1b\u6743\u9650\u786e\u8ba4\u76ee\u524d\u4ecd\u8d70 openHarness \u81ea\u5df1\u7684\u6d41\u7a0b\uff0c\u6ca1\u6709\u8d70 ACP \u7684 `requestPermission`\uff08\u5df2\u8bb0\u5165\u540e\u7eed\u8ddf\u8fdb\uff09\u3002`@agentclientprotocol\/sdk` \u662f `optionalDependency` \u2014\u2014 \u5982\u679c\u6ca1\u88c5\u4e0a\uff0c`oh acp` \u4f1a\u5e26\u7740\u6e05\u695a\u7684\u5b89\u88c5\u63d0\u793a\u9000\u51fa\uff0c\u4e0d\u4f1a\u9759\u9ed8\u5931\u8d25\u3002\n\n## \u9274\u6743\uff08Auth\uff09\n\n\u63d0\u4f9b\u5546\u65e0\u5173\u7684\u51ed\u636e\u7ba1\u7406\u3002\u672c\u5730 LLM\uff08Ollama \/ llama.cpp \/ LM Studio\uff09\u65e0\u9700\u9274\u6743 \u2014\u2014 \u901a\u8fc7 `oh init` \u914d\u7f6e\u5373\u53ef\u3002\n\n```bash\noh auth login [provider] [--key <value>]   # \u5b58\u50a8\u67d0\u4e2a\u63d0\u4f9b\u5546\u7684 API key\noh auth logout [provider]                   # \u6e05\u9664\u5df2\u5b58\u50a8\u7684 API key\noh auth status                              # \u663e\u793a\u5df2\u5b58\u50a8\u7684\u63d0\u4f9b\u5546\u53ca\u73af\u5883\u53d8\u91cf\u8986\u76d6\u60c5\u51b5\n```\n\n`[provider]` \u9ed8\u8ba4\u4f7f\u7528\u914d\u7f6e\u597d\u7684\u9ed8\u8ba4\u63d0\u4f9b\u5546\u3002`--key` \u53ef\u76f4\u63a5\u4f20\u5165\uff1b\u5426\u5219 OH \u4f1a\u5728 TTY \u4e0b\u4ea4\u4e92\u8be2\u95ee\uff0c\u5728\u7ba1\u9053\u8f93\u5165\u4e0b\u8bfb\u5230 EOF\u3002\n\n### \u811a\u672c\u5316 key \u89e3\u6790\uff08`apiKeyHelper`\uff09\n\n\u901a\u8fc7\u63d2\u5165\u8f85\u52a9\u811a\u672c\uff081Password\u3001`pass`\u3001vault\u3001\u4e91\u7aef\u5bc6\u94a5\u7ba1\u7406\u5668\u7b49\uff09\u907f\u514d\u628a key \u5199\u5165\u7eaf\u6587\u672c\u6216\u52a0\u5bc6\u5b58\u50a8\u3002\u914d\u7f6e\u597d\u7684\u547d\u4ee4\u5728\u53d6 key \u65f6\u6267\u884c\uff0c\u73af\u5883\u53d8\u91cf\u5e26 `OH_PROVIDER`\uff0c\u53bb\u6389\u9996\u5c3e\u7a7a\u767d\u7684 stdout \u5373\u4e3a key\u3002\n\n```yaml\n# .oh\/config.yaml\napiKeyHelper: 'op read \"op:\/\/Personal\/Anthropic\/key\"'\n```\n\n\u89e3\u6790\u4f18\u5148\u7ea7\uff1a\u73af\u5883\u53d8\u91cf \u2192 \u52a0\u5bc6\u5b58\u50a8 \u2192 `apiKeyHelper` \u2192 \u65e7\u7248\u7eaf\u6587\u672c\u914d\u7f6e\u3002\n\n## \u81ea\u52a8\u66f4\u65b0\uff08Update\uff09\n\n```bash\noh update                    # \u68c0\u6d4b\u5b89\u88c5\u65b9\u5f0f\uff08npm \u5168\u5c40 \/ npx \/ \u672c\u5730\u514b\u9686\uff09\uff0c\u6253\u5370\u5bf9\u5e94\u7684\u5347\u7ea7\u547d\u4ee4\n```\n\n## \u914d\u7f6e\u5c42\u7ea7\n\n\u914d\u7f6e\u6309\u5c42\u52a0\u8f7d\uff08\u540e\u8005\u8986\u76d6\u524d\u8005\uff09\uff1a\n\n1. **\u5168\u5c40** `~\/.oh\/config.yaml` \u2014\u2014 \u6240\u6709\u9879\u76ee\u5171\u7528\u7684\u9ed8\u8ba4\u63d0\u4f9b\u5546\u3001\u6a21\u578b\u3001\u4e3b\u9898\n2. **\u9879\u76ee** `.oh\/config.yaml` \u2014\u2014 \u9879\u76ee\u7ea7\u8bbe\u7f6e\n3. **\u672c\u5730** `.oh\/config.local.yaml` \u2014\u2014 \u4e2a\u4eba\u8986\u76d6\uff08\u5df2 gitignore\uff09\n\n\u5168\u5c40\u8bbe\u7f6e\u4e00\u6b21\u9ed8\u8ba4\u63d0\u4f9b\u5546\uff1a\n\n```yaml\n# ~\/.oh\/config.yaml\nprovider: ollama\nmodel: llama3\npermissionMode: ask\ntheme: dark\nlanguage: zh-CN        # \u53ef\u9009 \u2014\u2014 \u6a21\u578b\u4f1a\u7528\u8be5\u8bed\u8a00\u56de\u590d\uff08\u4ee3\u7801\u3001\u547d\u4ee4\u3001\u8def\u5f84\u4fdd\u6301\u539f\u6837\uff09\noutputStyle: default   # \u53ef\u9009 \u2014\u2014 \"default\"\u3001\"explanatory\"\u3001\"learning\" \u6216\u81ea\u5b9a\u4e49\u540d\n```\n\n\u4e4b\u540e\u9879\u76ee\u914d\u7f6e\u53ea\u9700\u5199\u4e0d\u540c\u4e4b\u5904\uff1a\n\n```yaml\n# .oh\/config.yaml\nmodel: codellama   # \u4ec5\u8986\u76d6\u6a21\u578b\n```\n\n### \u8f93\u51fa\u98ce\u683c\uff08Output Styles\uff09\n\n\u5728\u4e0d\u4fee\u6539\u6838\u5fc3\u6307\u4ee4\u7684\u524d\u63d0\u4e0b\u5207\u6362\u4ee3\u7406\u7684\"\u6027\u683c\"\u3002\u5185\u7f6e\u98ce\u683c\uff1a\n\n- **`default`** \u2014\u2014 \u6807\u51c6\u7684\u8f6f\u4ef6\u5de5\u7a0b\u52a9\u624b\uff08\u65e0\u524d\u7f6e\uff09\n- **`explanatory`** \u2014\u2014 \u6bcf\u5b8c\u6210\u4e00\u4e2a\u4efb\u52a1\u540e\u8ffd\u52a0 `## Insights` \u5c0f\u8282\uff0c\u89e3\u91ca *\u4e3a\u4ec0\u4e48* \u505a\u51fa\u8fd9\u6837\u7684\u9009\u62e9\n- **`learning`** \u2014\u2014 \u5728\u5173\u952e\u4f4d\u7f6e\u7559 1\u20133 \u4e2a `TODO(human)` \u6807\u8bb0\uff0c\u628a\u6700\u6709\u5b66\u4e60\u4ef7\u503c\u7684\u90a3\u90e8\u5206\u4ee3\u7801\u7559\u7ed9\u4f60\u81ea\u5df1\u5199\n\n\u81ea\u5b9a\u4e49\u98ce\u683c\u662f\u5e26 YAML frontmatter \u7684 markdown \u6587\u4ef6\u3002\u4fdd\u5b58\u5230 `.oh\/output-styles\/<name>.md`\uff08\u9879\u76ee\u7ea7\uff09\u6216 `~\/.oh\/output-styles\/<name>.md`\uff08\u7528\u6237\u7ea7\uff09\u3002\u9879\u76ee\u7ea7 > \u7528\u6237\u7ea7 > \u5185\u7f6e\u3002\n\n````markdown\n---\nname: code-review\ndescription: \u4e13\u6ce8\u7684\u4ee3\u7801\u5ba1\u67e5\u6a21\u5f0f\n---\n\n\u4e25\u683c\u5ba1\u67e5\u3002\u5bf9\u6bcf\u4e2a\u51fd\u6570\u8ffd\u95ee\uff1a\u903b\u8f91\u662f\u5426\u6b63\u786e\uff1f\u9519\u8bef\u5904\u7406\u662f\u5426\u5b8c\u6574\uff1f\u6709\u6ca1\u6709\u9057\u6f0f\u7684\u8fb9\u754c\u60c5\u51b5\uff1f\n````\n\n\u5728 `.oh\/config.yaml` \u4e2d\u901a\u8fc7 `outputStyle: code-review` \u6fc0\u6d3b\u3002\n\n## \u9879\u76ee\u89c4\u5219\n\n\u5728\u4efb\u610f\u4ed3\u5e93\u4e2d\u521b\u5efa `.oh\/RULES.md`\uff08\u6216\u8fd0\u884c `oh init`\uff09\uff1a\n\n```markdown\n- Always run tests after changes\n- Use strict TypeScript\n- Never commit to main directly\n```\n\n\u89c4\u5219\u4f1a\u81ea\u52a8\u52a0\u8f7d\u5230\u6bcf\u6b21\u4f1a\u8bdd\u4e2d\u3002\n\nopenHarness \u8fd8\u4f1a\u81ea\u52a8\u8bfb\u53d6\u4ee5\u4e0b\u9879\u76ee\u6307\u4ee4\u6587\u4ef6\uff08\u5982\u679c\u5b58\u5728\uff0c\u6309\u7236\u76ee\u5f55\u4f18\u5148\u5408\u5e76\u52a0\u8f7d\uff09\uff1a\n\n- `CLAUDE.md`\uff08Anthropic \u7ea6\u5b9a\uff09\u2014\u2014 \u542b\u4ece\u7236\u76ee\u5f55\u5230\u9879\u76ee\u6839\u7684\u5c42\u7ea7 `CLAUDE.md` \u6587\u4ef6\uff0c\u4ee5\u53ca\u5168\u5c40 `~\/.claude\/CLAUDE.md`\n- `AGENTS.md`\uff08[agents.md \u8de8\u5de5\u5177\u6807\u51c6](https:\/\/agents.md\/)\uff0c\u88ab Codex \/ Cursor \/ Copilot \/ Cline \/ Aider \u5171\u540c\u91c7\u7528\uff09\u2014\u2014 \u540c\u6837\u7684\u7236\u76ee\u5f55\u4f18\u5148\u626b\u63cf\n- `CLAUDE.local.md`\uff08gitignore \u7684\u4e2a\u4eba\u8986\u76d6\uff09\n\n\u5982\u679c\u4ed3\u5e93\u5df2\u4e3a\u5176\u4ed6 agent \u914d\u7f6e\u4e86 `AGENTS.md`\uff0copenHarness \u76f4\u63a5\u8bfb\u53d6\uff0c\u65e0\u9700\u8fc1\u79fb\u3002\n\n## \u6280\u80fd\u4e0e\u63d2\u4ef6\n\n### \u6280\u80fd\n\n\u6280\u80fd\u662f\u5e26 YAML frontmatter \u7684 markdown \u6587\u4ef6\uff0c\u7528\u4e8e\u6dfb\u52a0\u53ef\u590d\u7528\u884c\u4e3a\uff1a\n\n```markdown\n---\nname: deploy\ndescription: Deploy the application to production\ntrigger: deploy\ntools: [Bash, Read]\n---\n\nRun the deploy script with health checks...\n```\n\n**\u67e5\u627e\u4f4d\u7f6e**\uff08\u6309\u987a\u5e8f\uff09\uff1a\n1. `.oh\/skills\/` \u2014\u2014 \u9879\u76ee\u7ea7\u6280\u80fd\n2. `~\/.oh\/skills\/` \u2014\u2014 \u5168\u5c40\u6280\u80fd\uff08\u5728\u6240\u6709\u9879\u76ee\u4e2d\u53ef\u7528\uff09\n\n\u5f53\u7528\u6237\u6d88\u606f\u4e2d\u5305\u542b trigger \u5173\u952e\u5b57\u65f6\uff0c\u6280\u80fd\u4f1a\u81ea\u52a8\u89e6\u53d1\uff1b\u4e5f\u53ef\u4ee5\u7528 `\/skill deploy` \u663e\u5f0f\u8c03\u7528\u3002\n\n### \u63d2\u4ef6\n\n\u63d2\u4ef6\u662f\u6253\u5305\u4e86\u6280\u80fd\u3001\u94a9\u5b50\u548c MCP \u670d\u52a1\u5668\u7684 npm \u5305\uff1a\n\n```json\n{\n  \"name\": \"my-openharness-plugin\",\n  \"version\": \"1.0.0\",\n  \"skills\": [\"skills\/deploy.md\", \"skills\/review.md\"],\n  \"hooks\": {\n    \"sessionStart\": \"scripts\/setup.sh\"\n  },\n  \"mcpServers\": [\n    { \"name\": \"my-api\", \"command\": \"npx\", \"args\": [\"-y\", \"@my-org\/mcp-server\"] }\n  ]\n}\n```\n\n\u628a\u5b83\u547d\u540d\u4e3a `openharness-plugin.json` \u653e\u5728 npm \u5305\u6839\u76ee\u5f55\u3002\u5b89\u88c5\u65f6 `npm install`\uff0copenHarness \u4f1a\u81ea\u52a8\u4ece `node_modules\/` \u4e2d\u53d1\u73b0\u5b83\u3002\n\n## \u8bc4\u6d4b\uff08Evals\uff09\n\n`oh evals` \u5728\u672c\u5730\u9488\u5bf9\u4efb\u610f Provider \u8fd0\u884c SWE-bench-Lite \u517c\u5bb9\u7684\u8bc4\u6d4b\uff0c\u5e76\u5f3a\u5236\u8981\u6c42\u6210\u672c\u4e0a\u9650\u3002\u7528\u4e8e\u8861\u91cf\u771f\u5b9e Bug \u4fee\u590d\u8868\u73b0\uff0c\u6bd4\u5408\u6210\u57fa\u51c6\u66f4\u5177\u53c2\u8003\u4ef7\u503c\u3002\n\n```bash\n# \u7528 5 \u7f8e\u5143\u603b\u4e0a\u9650\u30012 \u8def\u5e76\u53d1\u8dd1\u4e00\u4e2a\u81ea\u5b9a\u4e49 pack\noh evals run my-pack --max-cost-usd 5 --concurrency 2\n\n# \u53ea\u8dd1\u6307\u5b9a instance\noh evals run my-pack --max-cost-usd 1 --instance django__django-11551\n\n# \u968f\u673a\u62bd\u53d6 3 \u4e2a\noh evals run my-pack --max-cost-usd 2 --sample 3\n\n# \u7eed\u8dd1\u56e0\u6210\u672c\u4e0a\u9650\u4e2d\u65ad\u7684\u8fd0\u884c\noh evals run my-pack --max-cost-usd 10 --resume 2026-05-05T14-30-00\n\n# \u5217\u51fa\u5df2\u5b89\u88c5\u7684 pack\noh evals list-packs\n\n# \u67e5\u770b\u5386\u53f2\u8fd0\u884c\u7684\u6c47\u603b\noh evals show 2026-05-05T14-30-00\n```\n\n\u8f93\u51fa\u4f4d\u4e8e `~\/.oh\/evals\/runs\/<run-id>\/`\uff1a\n\n- `results.json` \u2014 \u6bcf\u4e2a\u4efb\u52a1\u7684\u5b8c\u6574\u6570\u636e\uff1a\u6210\u672c\u3001\u8f6e\u6b21\u3001\u8017\u65f6\u3001tests_status\u3001\u9519\u8bef\u4fe1\u606f\u3002\n- `predictions.json` \u2014 \u53ef\u76f4\u63a5\u63d0\u4ea4\u5230 SWE-bench \u6392\u884c\u699c https:\/\/www.swebench.com\/\u3002\n- `transcripts\/<instance_id>.jsonl` \u2014 \u6bcf\u4e2a\u4efb\u52a1\u5b50\u8fdb\u7a0b\u7684\u539f\u59cb `stream-json` \u8f93\u51fa\u3002\n\n\u53ef\u63d2\u62d4\u7684 pack \u534f\u8bae\uff08`pack.json` + `instances.jsonl` + `fixtures\/<id>\/`\uff09\u5141\u8bb8\u4f60\u9488\u5bf9\u4efb\u610f\u6d4b\u8bd5\u5957\u4ef6\u7f16\u5199 pack\u3002`scripts\/build-evals-pack.mjs` \u5de5\u5177\u53ef\u5c06 SWE-bench-Lite \u517c\u5bb9\u4ed3\u5e93\u5728\u6307\u5b9a base_commit \u5904\u70d8\u7119\u4e3a fixture\uff0c\u8be6\u89c1 [CONTRIBUTING.md](CONTRIBUTING.md#authoring-eval-packs)\u3002\n\n\u5185\u7f6e\u7684 `swe-bench-lite-mini` pack\uff0810 \u4e2a\u7cbe\u9009 instance\uff0c\u5f00\u7bb1\u5373\u8dd1\uff09\u5c06\u5728 v2.40.2 \u7248\u672c\u53d1\u5e03\u3002\n\n## \u5de5\u4f5c\u539f\u7406\n\n```mermaid\ngraph LR\n    User[\u7528\u6237\u8f93\u5165] --> REPL[REPL \u5faa\u73af]\n    REPL --> Query[\u67e5\u8be2\u5f15\u64ce]\n    Query --> Provider[LLM \u63d0\u4f9b\u5546]\n    Provider --> LLM[Ollama \/ OpenAI \/ Anthropic]\n    LLM --> Tools[\u5de5\u5177\u6267\u884c]\n    Tools --> Permissions{\u6743\u9650\u68c0\u67e5}\n    Permissions -->|\u6279\u51c6| Execute[\u8fd0\u884c\u5de5\u5177]\n    Permissions -->|\u62d2\u7edd| Deny[\u62d2\u7edd\u5e76\u4e0a\u62a5]\n    Execute --> Response[\u6d41\u5f0f\u54cd\u5e94]\n    Response --> REPL\n```\n\n## \u5e38\u89c1\u95ee\u9898\n\n**\u53ef\u4ee5\u79bb\u7ebf\u4f7f\u7528\u5417\uff1f**\n\u53ef\u4ee5\u3002\u7528 Ollama \u52a0\u4e00\u4e2a\u672c\u5730\u6a21\u578b \u2014\u2014 \u4e0d\u9700\u8981\u7f51\u7edc\uff0c\u4e5f\u4e0d\u9700\u8981 API key\u3002\n\n**\u8981\u591a\u5c11\u94b1\uff1f**\n\u514d\u8d39\u3002OpenHarness \u4f7f\u7528 MIT \u534f\u8bae\u3002\u4e91\u7aef\u6a21\u578b\u81ea\u5e26 API key\uff08BYOK\uff09\uff0c\u6216\u8005\u7528 Ollama \u5b8c\u5168\u514d\u8d39\u3002\n\n**\u5b89\u5168\u5417\uff1f**\n\u5b89\u5168\u30027 \u79cd\u6743\u9650\u6a21\u5f0f\u63a7\u5236\u5de5\u5177\u80fd\u505a\u4ec0\u4e48\u3002Bash \u547d\u4ee4\u7531 AST \u89e3\u6790\u5668\u5206\u6790\uff0c\u963b\u6b62\u7834\u574f\u6027\u6a21\u5f0f\uff08`rm -rf`\u3001`curl | bash` \u7b49\uff09\u3002\u6bcf\u6b21\u6587\u4ef6\u53d8\u66f4\u90fd\u4f1a\u6253\u68c0\u67e5\u70b9\uff0c\u53ef\u901a\u8fc7 `\/rewind` \u56de\u6eda\u3002\n\n**\u53ef\u4ee5\u5728 CI\/CD \u4e2d\u4f7f\u7528\u5417\uff1f**\n\u53ef\u4ee5\u3002\u7528 `oh -p \"prompt\" --auto` \u8dd1\u65e0\u5934\u6a21\u5f0f\uff0c\u6216\u7528\u81ea\u5e26\u7684 GitHub Action \u505a PR \u5ba1\u67e5\u3002\n\n**\u652f\u6301\u6211\u7684\u8bed\u8a00\/\u6846\u67b6\u5417\uff1f**\n\u652f\u6301\u3002OpenHarness \u4e0e\u8bed\u8a00\u65e0\u5173 \u2014\u2014 \u5b83\u80fd\u8bfb\u5199\u5e76\u6267\u884c\u4efb\u610f\u8bed\u8a00\u7684\u4ee3\u7801\u3002\u8bed\u6cd5\u9ad8\u4eae\u8986\u76d6 20+ \u79cd\u8bed\u8a00\u3002\n\n**\u4e0e Claude Code \u76f8\u6bd4\u5982\u4f55\uff1f**\nCLI \u4f7f\u7528\u573a\u666f\u4e0b\u7ea6 95% \u529f\u80fd\u5bf9\u7b49\u3002\u4e3b\u8981\u4f18\u52bf\uff1a\u517c\u5bb9\u4efb\u610f LLM\uff08\u4e0d\u6b62 Anthropic\uff09\uff0c\u4e14\u91c7\u7528 MIT \u534f\u8bae\u3002\u89c1\u4e0a\u6587 [\u4e3a\u4ec0\u4e48\u9009\u62e9 OpenHarness\uff1f](#\u4e3a\u4ec0\u4e48\u9009\u62e9-openharness)\u3002\n\n## \u5b89\u88c5\n\n\u9700\u8981 **Node.js 18+**\u3002\n\n```bash\n# \u4ece npm \u5b89\u88c5\nnpm install -g @zhijiewang\/openharness\n\n# \u4ece\u6e90\u7801\u5b89\u88c5\ngit clone https:\/\/github.com\/zhijiewong\/openharness.git\ncd openharness\nnpm install && npm install -g .\n```\n\n## \u5f00\u53d1\n\n```bash\nnpm install\nnpx tsx src\/main.tsx              # \u4ee5\u5f00\u53d1\u6a21\u5f0f\u8fd0\u884c\nnpx tsc --noEmit                  # \u7c7b\u578b\u68c0\u67e5\nnpm test                          # \u8fd0\u884c\u6d4b\u8bd5\n```\n\n### \u6dfb\u52a0\u5de5\u5177\n\n\u5728 `src\/tools\/YourTool\/index.ts` \u4e2d\u5b9e\u73b0 `Tool` \u63a5\u53e3\uff08\u914d\u5408 Zod \u8f93\u5165 schema\uff09\uff0c\u7136\u540e\u5728 `src\/tools.ts` \u4e2d\u6ce8\u518c\u3002\n\n### \u6dfb\u52a0\u6a21\u578b\u63d0\u4f9b\u5546\n\n\u5728 `src\/providers\/yourprovider.ts` \u4e2d\u5b9e\u73b0 `Provider` \u63a5\u53e3\uff0c\u7136\u540e\u5728 `src\/providers\/index.ts` \u4e2d\u6dfb\u52a0\u4e00\u4e2a case\u3002\n\n## \u8d21\u732e\n\n\u89c1 [CONTRIBUTING.md](CONTRIBUTING.md)\u3002\n\n## \u793e\u533a\n\n\u52a0\u5165 OpenHarness \u793e\u533a\uff0c\u4e00\u8d77\u83b7\u53d6\u5e2e\u52a9\u3001\u5206\u4eab\u5de5\u4f5c\u6d41\u3001\u8ba8\u8bba AI \u7f16\u7a0b\u52a9\u624b\u7684\u672a\u6765\uff01\n\n| \u5e73\u53f0 | \u8be6\u60c5\u4e0e\u94fe\u63a5 |\n| :--- | :--- |\n| \ud83d\udfe3 **Discord** | [**\u52a0\u5165\u6211\u4eec\u7684 Discord**](https:\/\/discord.gg\/ezVrqy3qu)\uff0c\u548c\u5f00\u53d1\u8005\u5b9e\u65f6\u4ea4\u6d41\u3001\u83b7\u53d6\u652f\u6301\u3002 |\n| \ud83d\udd35 **\u98de\u4e66 \/ Lark** | \u626b\u63cf\u4e0b\u65b9\u4e8c\u7ef4\u7801\u52a0\u5165\u793e\u533a\u534f\u4f5c\u7fa4\uff1a<br><br><img src=\"https:\/\/github.com\/user-attachments\/assets\/54ade077-22ad-45d2-b38a-623464677d53\" width=\"160\" alt=\"\u98de\u4e66\u7fa4\u4e8c\u7ef4\u7801\"> |\n| \ud83d\udfe2 **\u5fae\u4fe1** | \u626b\u63cf\u4e0b\u65b9\u4e8c\u7ef4\u7801\u52a0\u5165\u5fae\u4fe1\u7fa4\uff1a<br><br><img src=\"https:\/\/github.com\/user-attachments\/assets\/adcf291a-9ffe-4738-8608-f46a21e18db0\" width=\"160\" alt=\"\u5fae\u4fe1\u7fa4\u4e8c\u7ef4\u7801\"> |\n\n## \u8bb8\u53ef\u8bc1\n\nMIT","ai_share_github_readme_en_markdown":"<p align=\"center\">\n  <img src=\"assets\/logo-256.png\" alt=\"openHarness logo\" width=\"128\" \/>\n<\/p>\n\n# OpenHarness\n\n```\n        ___\n       \/   \\\n      (     )        ___  ___  ___ _  _ _  _   _ ___ _  _ ___ ___ ___\n       `~w~`        \/ _ \\| _ \\| __| \\| | || | \/_\\ | _ \\ \\| | __\/ __\/ __|\n       (( ))       | (_) |  _\/| _|| .` | __ |\/ _ \\|   \/ .` | _|\\__ \\__ \\\n        ))((        \\___\/|_|  |___|_|\\_|_||_\/_\/ \\_\\_|_\\_|\\_|___|___\/___\/\n       ((  ))\n        `--`\n```\n\nAn AI coding assistant in your terminal. Works with any LLM \u2014 free local models or any cloud API.\n\n<p align=\"center\">\n  <img src=\"assets\/openharness_v0.11.1_4.gif\" alt=\"OpenHarness demo\" width=\"800\" \/>\n<\/p>\n\n[![npm version](https:\/\/img.shields.io\/npm\/v\/@zhijiewang\/openharness)](https:\/\/www.npmjs.com\/package\/@zhijiewang\/openharness) [![npm downloads](https:\/\/img.shields.io\/npm\/dm\/@zhijiewang\/openharness)](https:\/\/www.npmjs.com\/package\/@zhijiewang\/openharness) [![license](https:\/\/img.shields.io\/npm\/l\/@zhijiewang\/openharness)](LICENSE) ![tests](https:\/\/img.shields.io\/badge\/tests-1502-brightgreen) ![tools](https:\/\/img.shields.io\/badge\/tools-44-blue) ![Node.js 18+](https:\/\/img.shields.io\/badge\/node-18%2B-green) ![TypeScript](https:\/\/img.shields.io\/badge\/typescript-strict-blue) [![GitHub stars](https:\/\/img.shields.io\/github\/stars\/zhijiewong\/openharness)](https:\/\/github.com\/zhijiewong\/openharness) [![GitHub issues](https:\/\/img.shields.io\/github\/issues-raw\/zhijiewong\/openharness)](https:\/\/github.com\/zhijiewong\/openharness\/issues) [![PRs Welcome](https:\/\/img.shields.io\/badge\/PRs-welcome-brightgreen)](https:\/\/github.com\/zhijiewong\/openharness\/pulls)\n\n[English](README.md) | **\u7b80\u4f53\u4e2d\u6587**\n\n---\n\n## Table of Contents\n\n- [Quick Start](#quick-start)\n- [Why OpenHarness?](#why-openharness)\n- [Terminal Interface](#terminal-interface)\n- [Tools (44)](#tools-44)\n- [Slash Commands](#slash-commands)\n- [Permission Modes](#permission-modes)\n- [Hooks](#hooks)\n- [Checkpoints & Rollback](#checkpoints--rollback)\n- [Agent Roles](#agent-roles)\n- [Headless Mode & CI\/CD](#headless-mode--cicd)\n- [Digital Pet Cybergotchi](#digital-pet-cybergotchi)\n- [MCP Servers](#mcp-servers)\n- [Model Providers](#model-providers)\n- [Authentication (Auth)](#authentication-auth)\n- [Auto-Update](#auto-update)\n- [Evals](#evals)\n- [FAQ](#faq)\n- [Installation](#installation)\n- [Development](#development)\n- [Contributing](#contributing)\n- [Community](#community)\n\n---\n\n## Quick Start\n\n```bash\nnpm install -g @zhijiewang\/openharness\noh\n```\n\nThat's all it takes. OpenHarness auto-detects Ollama and starts chatting. No API key needed.\n\n**Python SDK:** We also provide an official Python SDK for driving `oh` from Python programs (notebooks, batch scripts, ML pipelines). After npm installation, install via `pip install openharness-sdk` (distributed on PyPI as `openharness-sdk` since the un-suffixed name was taken), then `from openharness import query`. See [`python\/README.md`](python\/README.md).\n\n**TypeScript SDK:** Similarly, an official TypeScript SDK for driving `oh` from Node.js (VS Code extensions, Electron apps, build scripts, etc.): use `@zhijiewang\/openharness-sdk` \u2014 install via `npm install @zhijiewang\/openharness-sdk`, then `import { query, OpenHarnessClient, tool } from \"@zhijiewang\/openharness-sdk\"`. Feature-parity with the Python SDK (streaming events, stateful sessions, custom tools, permission callbacks, session resumption). See [`packages\/sdk\/README.md`](packages\/sdk\/README.md).\n\n```bash\noh init                               # \u4ea4\u4e92\u5f0f\u5b89\u88c5\u5411\u5bfc\uff08\u6a21\u578b\u63d0\u4f9b\u5546 + \u7535\u5b50\u5ba0\u7269\uff09\noh                                    # \u81ea\u52a8\u68c0\u6d4b\u672c\u5730\u6a21\u578b\noh --model ollama\/qwen2.5:7b         # \u6307\u5b9a\u6a21\u578b\noh --model gpt-4o                     # \u4e91\u7aef\u6a21\u578b\uff08\u9700\u8981 OPENAI_API_KEY\uff09\noh --trust                            # \u81ea\u52a8\u6279\u51c6\u6240\u6709\u5de5\u5177\u8c03\u7528\noh --auto                             # \u81ea\u52a8\u6279\u51c6\uff0c\u4f46\u963b\u6b62\u5371\u9669\u7684 bash \u547d\u4ee4\noh -p \"fix the tests\" --trust         # \u65e0\u5934\u6a21\u5f0f\uff08\u5355\u6b21\u63d0\u793a\u540e\u9000\u51fa\uff09\noh run \"review code\" --json           # \u7528\u4e8e CI\/CD \u7684 JSON \u8f93\u51fa\n```\n\n**In-session commands:**\n```\n\/rewind                               # \u64a4\u9500\u6700\u8fd1\u4e00\u6b21 AI \u6587\u4ef6\u53d8\u66f4\uff08\u6062\u590d\u68c0\u67e5\u70b9\uff09\n\/roles                                # \u5217\u51fa\u6240\u6709\u4ee3\u7406\u4e13\u957f\n\/vim                                  # \u5207\u6362 vim \u6a21\u5f0f\nCtrl+O                                # \u5c06\u5386\u53f2\u8bb0\u5f55\u5237\u5165\u7ec8\u7aef\u6eda\u52a8\u7f13\u51b2\u533a\u4ee5\u4fbf\u67e5\u9605\n```\n\n## Why OpenHarness?\n\nMost AI coding assistants are locked to a single model provider or cost $20+\/month. OpenHarness works with any LLM \u2014 run free local models with Ollama, or connect to any cloud API. Every AI edit is automatically committed to git and can be reverted with `\/undo`.\n\n## Terminal Interface\n\nOpenHarness uses a sequential terminal renderer inspired by Ink\/Claude Code's default mode. Completed messages flush into the native scroll buffer (scrollable), while live regions (streaming output, loading animations, input box) redraw in-place via relative cursor movement.\n\n### Keybindings\n\n| Key | Action |\n|-----|--------|\n| `Enter` | Submit prompt |\n| `Alt+Enter` | Insert newline (multi-line input) |\n| `\u2191` \/ `\u2193` | Browse input history |\n| `Ctrl+C` | Cancel current request \/ exit |\n| `Ctrl+A` \/ `Ctrl+E` | Jump to start \/ end of input box |\n| `Ctrl+O` | Expand \/ collapse thinking blocks |\n| `Ctrl+K` | Expand \/ collapse code blocks in messages |\n| `Tab` | Autocomplete slash commands \/ file paths \/ cycle through tool outputs |\n| `\/vim` | Toggle Vim mode (normal\/insert) |\n\nScrolling is handled by the terminal's native scrollbar. Completed messages enter the terminal scroll buffer. Use the terminal's built-in search (e.g. `Ctrl+Shift+F` in VS Code) to search conversation history.\n\n### Features\n\n- **Markdown rendering** \u2014 headings, code blocks, bold, italic, lists, tables, blockquotes, links\n- **Syntax highlighting** \u2014 keywords, strings, comments, numbers, types (20+ languages: JS\/TS\/Python\/Rust\/Go, etc.)\n- **Collapsible code blocks** \u2014 blocks over 8 lines auto-collapse; press `Ctrl+K` to expand all\n- **Collapsible thinking blocks** \u2014 thinking blocks collapse to a one-line summary when done; press `Ctrl+O` to expand\n- **Streaming loading animation** \u2014 stage-labeled indicators (`Thinking`, `Running <Tool>`, `Calling <server>:<tool>`, `Running N tools`) with color transitions (magenta after 30s \u2192 yellow, red after 60s)\n- **Tool call display** \u2014 argument preview, real-time streaming output, result summary (line count, duration), expand\/collapse with `Tab`. Tool names are color-coded by category (read: cyan, modify: yellow, execute: magenta, MCP: green)\n- **Rich tool output** \u2014 JSON files render as a colorful static tree (3-level collapse depth, line truncation); Markdown files render with full styling (headings, code blocks, tables), not plain line-by-line splitting. The renderer dispatches via the `outputType` field (FileReadTool \/ WebFetchTool are labeled); unlabeled tools fall back to a heuristic path\n- **Nested tool calls** \u2014 when an `Agent` or `ParallelAgents` spawns inner tool calls (Read, Bash, Edit), sub-calls are indented beneath the parent that spawned them. ParallelAgents also shows a `Task` wrapper line for each task, grouping sub-calls by task rather than flattening them under the merged parent. 3-level indentation cap, shows `\u2026 (N more level)` collapse marker when exceeded\n- **Multi-line input wrap indicator** \u2014 every non-final line of multi-line input ends with a dim `\u21b5` continuation character, making line wrapping visually clear\n- **Permission prompts** \u2014 bordered prompt box with risk-level coloring, prominent **Y**es\/**N**o\/**D**iff keys, inline diff with syntax highlighting\n- **Status bar** \u2014 shows model name, token count, cost, context usage bar (customizable via config)\n- **Context warning** \u2014 yellow warning when context window exceeds 75%\n- **Native terminal scrollbar** \u2014 completed messages enter the scroll buffer; use the terminal's scrollbar and search\n- **Multi-line input** \u2014 `Alt+Enter` inserts a newline; auto-detected and newline-inserted on paste\n- **Autocomplete** \u2014 slash commands and file paths (with descriptions); cycle through with Tab\n- **File path completion** \u2014 Tab completes paths, marked with `[dir]`\/`[file]`\n- **Session browser** \u2014 use `\/browse` to interactively browse and resume historical sessions\n- **Desktop pet** \u2014 animated Cybergotchi in the footer (toggle with `\/companion off|on`)\n\n### Themes\n\n```bash\noh --light                    # \u9002\u7528\u4e8e\u660e\u4eae\u7ec8\u7aef\u7684\u6d45\u8272\u4e3b\u9898\n\/theme light                  # \u4f1a\u8bdd\u4e2d\u5207\u6362\uff08\u81ea\u52a8\u4fdd\u5b58\uff09\n\/theme dark                   # \u5207\u56de\u6df1\u8272\n```\n\nTheme preferences are saved to `.oh\/config.yaml` and persist across sessions.\n\n### Custom Status Bar\n\nCustomize the status bar format in `.oh\/config.yaml`:\n\n```yaml\nstatusLineFormat: '{model} \u2502 {tokens} \u2502 {cost} \u2502 {ctx}'\n```\n\nAvailable variables: `{model}`, `{tokens}` (input\u2191 output\u2193), `{cost}` ($X.XXXX), `{ctx}` (context usage bar). Empty segments auto-collapse.\n\n## Tools (44)\n\n| Tool | Risk | Description |\n|------|------|-------------|\n| **Core** | | |\n| Bash | High | Execute shell commands with real-time streaming output (AST safety analysis) |\n| PowerShell | High | Execute PowerShell commands (Windows native scripts) |\n| Read | Low | Read files by line range, supports PDF |\n| ImageRead | Low | Read images\/PDFs for multimodal analysis |\n| Write | Medium | Create or overwrite files |\n| Edit | Medium | Search and replace edits |\n| MultiEdit | Medium | Atomic multi-file edits (all-or-nothing) |\n| Glob | Low | Find files by pattern |\n| Grep | Low | Regex content search with context lines |\n| LS | Low | List directory contents and sizes |\n| **Web** | | |\n| WebFetch | Medium | Fetch URL content (SSRF protection) |\n| WebSearch | Medium | Web search |\n| ExaSearch | Medium | Neural search via Exa (requires `EXA_API_KEY`) |\n| RemoteTrigger | High | Send HTTP requests to webhooks\/APIs |\n| **Tasks** | | |\n| TaskCreate | Low | Create structured tasks |\n| TaskUpdate | Low | Update task status |\n| TaskList | Low | List all tasks |\n| TaskGet | Low | Get task details |\n| TaskStop | Low | Stop a running task |\n| TaskOutput | Low | Get task output |\n| TodoWrite | Low | Manage session-level todo list (Claude Code compatible) |\n| **Agents** | | |\n| Agent | Medium | Spawn a sub-agent (with optional role) |\n| ParallelAgent | Medium | Dispatch multiple agents with DAG dependencies |\n| SendMessage | Low | Peer-to-peer messaging between agents |\n| AskUser | Low | Ask the user a question (with options) |\n| **Scheduling** | | |\n| CronCreate | Medium | Create scheduled tasks |\n| CronDelete | Medium | Delete scheduled tasks |\n| CronList | Low | List all scheduled tasks |\n| ScheduleWakeup | Low | Adaptively schedule the next trigger in \/loop (cache-aware) |\n| **Planning** | | |\n| EnterPlanMode | Low | Enter structured planning mode |\n| ExitPlanMode | Low | Exit planning mode |\n| **Pipeline** | | |\n| Pipeline | Medium | Execute a sequence of sub-tasks, passing each step's output as the next step's input |\n| **Code Intelligence** | | |\n| Diagnostics | Low | LSP-based code diagnostics |\n| NotebookEdit | Medium | Edit Jupyter notebooks |\n| **Memory & Discovery** | | |\n| Memory | Low | Save \/ list \/ search persistent memory |\n| Skill | Low | Invoke skills under `.oh\/skills\/` |\n| ToolSearch | Low | Find tools by description |\n| SessionSearch | Low | Search historical sessions for relevant context |\n| **MCP** | | |\n| ListMcpResources | Low | List resources on connected MCP servers |\n| ReadMcpResource | Low | Read a specific MCP resource by URI |\n| **Git Worktrees** | | |\n| EnterWorktree | Medium | Create an isolated git worktree |\n| ExitWorktree | Medium | Remove a git worktree |\n| **Processes** | | |\n| KillProcess | High | Stop a process by PID or name |\n| Monitor | Medium | Run a command in the background, streaming each line of output back to the agent |\n\nLow-risk read-only tools auto-approve. Medium\/high-risk tools prompt in `ask` mode. Use `--trust` or `--auto` to skip prompts.\n\n## Slash Commands\n\nOH registers 80+ slash commands; only the most commonly used are listed below. Run `\/help` in-session for the full list. Aliases: `\/q` quit, `\/h` help, `\/c` commit, `\/m` model, `\/s` status.\n\n**Session:**\n| Command | Description |\n|---------|-------------|\n| `\/clear` | Clear conversation history |\n| `\/compact` | Compact conversation to free up context |\n| `\/export` | Export conversation as markdown |\n| `\/copy [n]` | Copy the Nth-to-last assistant reply to system clipboard |\n| `\/history [n]` | List recent sessions; `\/history search <term>` to search |\n| `\/browse` | Interactive session browser with preview |\n| `\/resume <id>` | Resume a saved session |\n| `\/fork` | Clone the current session |\n\n**Git:**\n| Command | Description |\n|---------|-------------|\n| `\/diff` | Show uncommitted git changes |\n| `\/undo` | Undo the last AI commit |\n| `\/commit [msg]` | Create a git commit |\n| `\/log` | Show recent git commits |\n\n**Info:**\n| Command | Description |\n|---------|-------------|\n| `\/help` | Show all available commands (by category) |\n| `\/cost` | Show session cost and token usage |\n| `\/status` | Show model, mode, git branch, MCP servers |\n| `\/config` | Show configuration |\n| `\/files` | List files in context |\n| `\/model <name>` | Switch model in session |\n| `\/memory` | View and search memory |\n| `\/doctor` | Run diagnostic health check |\n| `\/hooks` | List loaded hooks by event |\n| `\/reload-plugins` | Hot-reload plugins, skills, hooks, and MCP server connections without restarting the session |\n\n**Settings:**\n| Command | Description |\n|---------|-------------|\n| `\/theme dark|light` | Switch theme (auto-saved to config) |\n| `\/vim` | Toggle Vim mode |\n| `\/companion off|on` | Toggle desktop pet visibility |\n| `\/keys` | Show keyboard shortcuts |\n| `\/keybindings` | Open `~\/.oh\/keybindings.json` in `$EDITOR` (created on first run) |\n\n**AI:**\n| Command | Description |\n|---------|-------------|\n| `\/plan <task>` | Enter planning mode |\n| `\/review` | Review recent code changes |\n| `\/summarize` | Summarize the current conversation |\n| `\/recap` | One-line recap of the session (lighter than `\/summarize`) |\n\n**Pet:**\n| Command | Description |\n|---------|-------------|\n| `\/cybergotchi` | Feed, pet, rest, status, rename, or reset the digital pet |\n\n## Permission Modes\n\nControls how aggressively OpenHarness auto-approves tool calls:\n\n| Mode | Flag | Behavior |\n|------|------|----------|\n| `ask` | `--permission-mode ask` | Prompt for medium\/high-risk operations (default) |\n| `trust` | `--trust` | Auto-approve everything |\n| `deny` | `--deny` | Only allow low-risk read-only operations |\n| `acceptEdits` | `--permission-mode acceptEdits` | Auto-approve file edits, still ask for Bash\/WebFetch\/Agent |\n| `plan` | `--permission-mode plan` | Read-only mode \u2014 blocks all write operations |\n| `auto` | `--auto` | Auto-approve all operations, block dangerous bash (via AST analysis) |\n| `bypassPermissions` | `--permission-mode bypassPermissions` | Unconditionally approve everything (CI only) |\n\nBash commands are analyzed by a lightweight AST parser that detects destructive patterns (`rm -rf`, `git push --force`, `curl | bash`, etc.) and adjusts risk level accordingly.\n\nSet permanently in `.oh\/config.yaml`: `permissionMode: 'acceptEdits'`\n\n## Hooks\n\nRun shell scripts automatically on key session events by adding a `hooks` block in `.oh\/config.yaml`:\n\n```yaml\nhooks:\n  - event: sessionStart\n    command: \"echo 'Session started' >> ~\/.oh\/session.log\"\n\n  - event: preToolUse\n    command: \"scripts\/check-tool.sh\"\n    match: Bash   # \u53ef\u9009\uff1a\u4ec5\u5bf9\u8be5\u5de5\u5177\u540d\u89e6\u53d1\n\n  - event: postToolUse\n    command: \"scripts\/after-tool.sh\"\n\n  - event: sessionEnd\n    command: \"scripts\/cleanup.sh\"\n```\n\n**Event types** (27 total \u2014 consistent with Claude Code stable):\n\n| Event | Trigger | Can Block |\n|-------|---------------|------------|\n| `sessionStart` | Session start | \u2014 |\n| `sessionEnd` | Session end | \u2014 |\n| `turnStart` | Top-level agent turn starts (after user prompt is accepted) | \u2014 |\n| `turnStop` | Top-level agent turn ends (corresponds to Claude Code's `Stop`) | \u2014 |\n| `userPromptSubmit` | Before user prompt reaches LLM | Yes \u2014 `decision: deny` |\n| `userPromptExpansion` | When a slash command expands into a model prompt (for audit tracking) | \u2014 |\n| `preToolUse` | Before a tool is called | Yes \u2014 exit code 1 \/ `decision: deny` |\n| `postToolUse` | After a tool succeeds | \u2014 |\n| `postToolUseFailure` | When a tool throws or returns `isError: true` | \u2014 |\n| `postToolBatch` | After all tool calls in a turn complete, before the next model call | \u2014 |\n| `permissionRequest` | When a tool needs authorization (between `preToolUse` and prompting) | Yes \u2014 `decision: allow|deny|ask` |\n| `permissionDenied` | When a tool call is denied (by hook \/ user \/ headless \/ policy) | \u2014 |\n| `fileChanged` | After a tool modifies a file | \u2014 |\n| `cwdChanged` | After the working directory changes | \u2014 |\n| `subagentStart` | When a sub-agent is spawned | \u2014 |\n| `subagentStop` | When a sub-agent completes | \u2014 |\n| `preCompact` | Before conversation compaction | \u2014 |\n| `postCompact` | After conversation compaction | \u2014 |\n| `configChange` | When `.oh\/config.yaml` is modified during a session | \u2014 |\n| `notification` | When a notification is dispatched | \u2014 |\n| `taskCreated` | After `TaskCreate` persists a new task | \u2014 |\n| `taskCompleted` | When `TaskUpdate` transitions a task to `completed` | \u2014 |\n| `worktreeCreate` | When `EnterWorktreeTool` creates an isolated git worktree | \u2014 |\n| `worktreeRemove` | When `ExitWorktreeTool` removes a git worktree | \u2014 |\n| `elicitation` | When an MCP server requests user input via `elicitation\/create` | Yes \u2014 `decision: allow|deny` |\n| `elicitationResult` | After an elicitation decision is made (for audit tracking) | \u2014 |\n| `instructionsLoaded` | After `loadRulesAsPrompt` reconstructs the system prompt and loads rules | \u2014 |\n\nGlobally disable hook execution (while keeping definitions on disk for audit) with `disableAllHooks: true` in `.oh\/config.yaml`.\n\nView live: run `\/hooks` in-session to see currently loaded hooks grouped by event.\n\n**Environment variables** (available to hook scripts):\n\n| Variable | Description |\n|----------|-------------|\n| `OH_EVENT` | Event type (`sessionStart`, `preToolUse`, etc.) |\n| `OH_TOOL_NAME` | Tool being called (tool events only) |\n| `OH_TOOL_ARGS` | JSON-encoded tool arguments (tool events only) |\n| `OH_TOOL_OUTPUT` | JSON-encoded tool output (`postToolUse` only) |\n| `OH_TOOL_INPUT_JSON` | Complete JSON tool input (tool events only) |\n| `OH_SESSION_ID` \/ `OH_MODEL` \/ `OH_PROVIDER` \/ `OH_PERMISSION_MODE` | Current session context |\n| `OH_COST` \/ `OH_TOKENS` | Cumulative cost and token count |\n| `OH_FILE_PATH` | Changed file path (`fileChanged` only) |\n| `OH_NEW_CWD` | New working directory (`cwdChanged` only) |\n| `OH_TURN_NUMBER` \/ `OH_TURN_REASON` | Turn boundary context (`turnStart` \/ `turnStop`) |\n\nUse `match` to scope hooks to specific tool names (e.g. `match: Bash` fires only for the Bash tool). Supports substring, glob (e.g. `Cron*`), and `\/regex\/flags` matching.\n\nSet `jsonIO: true` on a `command` hook to enable structured JSON I\/O \u2014 the framework sends `{event, ...context}` on stdin and reads `{decision, reason, hookSpecificOutput}` from stdout. HTTP hooks accept the same response format. Full reference at [docs\/hooks.md](docs\/hooks.md).\n\n## Digital Pet Cybergotchi\n\nOpenHarness comes with a Tamagotchi-style digital pet living in the sidebar. It reacts to your session in real-time \u2014 cheers for win streaks, complains about tool failures, gets hungry when ignored.\n\n**Hatch one:**\n```\noh init        # \u5b89\u88c5\u5411\u5bfc\u5305\u542b\u7535\u5b50\u5ba0\u7269\u8bbe\u7f6e\n\/cybergotchi   # \u6216\u5728\u4f1a\u8bdd\u4e2d\u5b75\u5316\n```\n\n**Commands:**\n```\n\/cybergotchi feed      # \u9971\u98df\u5ea6 +30\n\/cybergotchi pet       # \u5feb\u4e50\u503c +20\n\/cybergotchi rest      # \u7cbe\u529b\u503c +40\n\/cybergotchi status    # \u663e\u793a\u9700\u6c42\u4e0e\u7ec8\u751f\u7edf\u8ba1\n\/cybergotchi rename    # \u8d77\u4e2a\u65b0\u540d\u5b57\n\/cybergotchi reset     # \u6362\u4e2a\u7269\u79cd\u91cd\u65b0\u5f00\u59cb\n```\n\n**Needs** decay over time (satiety fastest, happiness slowest). Feed and pet your pet on schedule to keep it happy.\n\n**Evolution** \u2014 evolves based on lifetime milestones:\n- Stage 1 (\u2726 Magenta): 10 sessions or 50 commits\n- Stage 2 (\u2605 Yellow + Crown): Complete 100 tasks, or 25 consecutive tool calls without failure\n\n**18 species** to choose from: duck, cat, owl, penguin, rabbit, turtle, snail, octopus, axolotl, cactus, mushroom, mochi, capybara, goose, and more.\n\n## MCP Servers\n\nConnect any MCP (Model Context Protocol) server by editing `.oh\/config.yaml`:\n\n```yaml\nprovider: anthropic\nmodel: claude-sonnet-4-6\npermissionMode: ask\nmcpServers:\n  - name: filesystem\n    command: npx\n    args: [\"-y\", \"@modelcontextprotocol\/server-filesystem\", \"\/tmp\"]\n  - name: github\n    command: npx\n    args: [\"-y\", \"@modelcontextprotocol\/server-github\"]\n    env:\n      GITHUB_PERSONAL_ACCESS_TOKEN: ghp_...\n```\n\nMCP tools appear alongside built-in tools. `\/status` shows connected servers.\n\n**MCP server prompts as slash commands** \u2014 servers implementing `prompts\/list` (e.g. GitHub, Sentry, Linear) automatically expose their prompts as `\/<server>:<prompt>` slash commands. Parameters use `key=value` syntax with optional quoting:\n\n```\n\/github:summarize-pr repo=acme\/widget pr=42\n\/sentry:triage-issue issue=ABC-123 severity=\"high priority\"\n```\n\nRequired parameters declared in a prompt template will error with usage info if missing (won't call the model). After modifying MCP config, run `\/reload-plugins` to re-discover prompts.\n\n### Remote MCP Servers (HTTP \/ SSE)\n\n```yaml\nmcpServers:\n  - name: linear\n    type: http\n    url: https:\/\/mcp.linear.app\/mcp\n    headers:\n      Authorization: \"Bearer ${LINEAR_API_KEY}\"\n```\n\nFull reference at [docs\/mcp-servers.md](docs\/mcp-servers.md).\nOAuth 2.1 setup at [docs\/mcp-servers.md](docs\/mcp-servers.md#authentication) (auto-triggered on 401; also available via `\/mcp-login` and `\/mcp-logout` commands).\n\n**MCP Server Registry** \u2014 browse and install from a curated directory:\n\n```\n\/mcp-registry              # \u6d4f\u89c8\u6240\u6709\u53ef\u7528\u670d\u52a1\u5668\n\/mcp-registry github       # \u663e\u793a\u6307\u5b9a\u670d\u52a1\u5668\u7684\u5b89\u88c5\u914d\u7f6e\n\/mcp-registry database     # \u6309\u5206\u7c7b\u641c\u7d22\n```\n\nCategories: filesystem, git, database, api, search, productivity, dev-tools, ai.\n\n## Git Integration\n\nIn a git repository, OpenHarness auto-commits AI edits:\n\n```\noh: Edit src\/app.ts                    # \u81ea\u52a8\u4ee5 \"oh:\" \u524d\u7f00\u63d0\u4ea4\noh: Write tests\/app.test.ts\n```\n\n- Every AI file change is auto-committed\n- `\/undo` reverts the last AI commit (OH commits only, won't touch yours)\n- `\/diff` shows what changed\n- Your uncommitted files are safe \u2014 they're committed separately before any AI edit\n\n## Checkpoints & Rollback\n\nEvery file modification is checkpointed automatically before execution. If something goes wrong:\n\n```\n\/rewind           # \u4ece\u6700\u8fd1\u4e00\u6b21\u68c0\u67e5\u70b9\u6062\u590d\u6587\u4ef6\n\/undo             # \u56de\u9000\u6700\u540e\u4e00\u6b21 AI git \u63d0\u4ea4\n```\n\nCheckpoints are stored in `.oh\/checkpoints\/`, covering FileWrite, FileEdit, and Bash commands that modify files.\n\n## Verification Loop\n\nAfter every file edit (Edit, Write, MultiEdit), OpenHarness automatically runs language-specific lint\/type-check commands and feeds the results back into the agent's context. This is the single highest-impact harness engineering pattern \u2014 research shows automatic feedback delivers 2\u20133\u00d7 quality improvement.\n\n**Auto-detection** \u2014 if your project has `tsconfig.json`, `.eslintrc*`, `pyproject.toml`, `go.mod`, or `Cargo.toml`, verification rules are auto-identified. No configuration needed.\n\n**Custom rules** (in `.oh\/config.yaml`):\n\n```yaml\nverification:\n  enabled: true       # \u9ed8\u8ba4\uff1atrue\uff08\u81ea\u52a8\u68c0\u6d4b\uff09\n  mode: warn          # 'warn' \u8ffd\u52a0\u5230\u8f93\u51fa\uff1b'block' \u6807\u8bb0\u4e3a\u9519\u8bef\n  rules:\n    - extensions: [\".ts\", \".tsx\"]\n      lint: \"npx tsc --noEmit 2>&1 | head -20\"\n      timeout: 15000\n    - extensions: [\".py\"]\n      lint: \"ruff check {file} 2>&1 | head -10\"\n```\n\nAfter each edit, the agent sees `[Verification passed]` or `[Verification FAILED]` with linter output, enabling self-correction.\n\n## Memory Consolidation\n\nOn session exit, OpenHarness auto-prunes stale memories via time decay:\n\n- Memories not accessed in 30+ days decay by 0.1 relevance every 30 days\n- Memories with relevance below 0.1 are permanently deleted\n- Updated relevance scores are written back to memory files\n\nThis keeps the memory system lean and relevant. Configure in `.oh\/config.yaml`:\n\n```yaml\nmemory:\n  consolidateOnExit: true   # \u9ed8\u8ba4\uff1atrue\n```\n\n## Scheduled Tasks (Cron)\n\nCreate tasks that run automatically in the background:\n\n```\n# \u901a\u8fc7\u659c\u6760\u547d\u4ee4\n\/cron list                    # \u663e\u793a\u6240\u6709\u5b9a\u65f6\u4efb\u52a1\n\/cron create \"check-tests\"    # \u65b0\u5efa\u4efb\u52a1\uff08\u4ea4\u4e92\u5f0f\uff09\n\/cron delete <id>             # \u5220\u9664\u4efb\u52a1\n```\n\n**Schedule syntax:** `every 5m`, `every 2h`, `every 1d`\n\nThe cron executor checks for due tasks every 60 seconds and runs them via sub-queries. Results are saved to `~\/.oh\/crons\/history\/`.\n\n## Agent Roles\n\nDispatch specialized sub-agents to handle specific tasks:\n\n```\n\/roles            # \u5217\u51fa\u6240\u6709\u53ef\u7528\u89d2\u8272\n```\n\n| Role | Description | Tools |\n|------|-------------|-------|\n| `code-reviewer` | Find bugs, security issues, style problems | Read-only |\n| `test-writer` | Generate unit and integration tests | Read + Write |\n| `docs-writer` | Write documentation and comments | Read + Write + Edit |\n| `debugger` | Systematically troubleshoot bugs | Read-only + Bash |\n| `refactorer` | Simplify code without changing behavior | All file tools + Bash |\n| `security-auditor` | OWASP, injection, secrets, CVE scanning | Read-only + Bash |\n| `evaluator` | Assess code quality and run tests (read-only) | Read-only + Bash + Diagnostics |\n| `planner` | Design step-by-step implementation plans | Read-only + Bash |\n| `architect` | Analyze architecture and design structural changes (hands off to editor for implementation) | Read-only |\n| `editor` | Apply code changes per architect's plan, no re-planning | Read + Edit + Write + MultiEdit + Bash |\n| `migrator` | Systematic codebase migration and upgrades | All file tools + Bash |\n\nEach role restricts sub-agents to only its recommended tools. You can also explicitly pass `allowed_tools`:\n\n```\nAgent({ subagent_type: 'evaluator', prompt: 'Run all tests and report results' })\nAgent({ allowed_tools: ['Read', 'Grep'], prompt: 'Search for all TODO comments' })\n```\n\n### Architect \u2192 Editor (Cost-saving pattern for multi-file changes)\n\nFor larger changes spanning multiple files, use the `architect` \u2192 `editor` two-pass workflow: architect (powerful model) reads the code and produces a structured plan; editor (lightweight model) mechanically applies the plan, no re-planning. When `modelRouter` is configured, OH automatically routes the `architect` role to the `powerful` tier and the `editor` role to the `fast` tier \u2014 compared to running a powerful model for both passes, multi-file changes typically save 30\u201350% cost.\n\n```\nAgent({ subagent_type: 'architect', prompt: 'Plan a migration from option A to option B across src\/' })\n# \u628a\u5f97\u5230\u7684\u65b9\u6848\u518d\u4ea4\u7ed9 editor\uff1a\nAgent({ subagent_type: 'editor', prompt: '<paste plan>' })\n```\n\n### Sub-agent Permission Isolation\n\n`Agent` calls support a `permission_mode` parameter that can **only tighten, not loosen** the parent's permission mode. This is especially useful when the parent runs in `trust` but you want a review\/audit sub-agent to remain read-only:\n\n```\nAgent({ subagent_type: 'code-reviewer', prompt: '...', permission_mode: 'plan' })\nAgent({ subagent_type: 'security-auditor', prompt: '...', permission_mode: 'deny' })\n```\n\nIf a requested mode is looser than the parent's (e.g. parent `ask`, sub-agent requests `trust`), the harness silently falls back to the parent's mode \u2014 the model can never use a sub-agent to bypass the user's approval gate.\n\n**Read-only roles default to `plan` mode.** `code-reviewer`, `evaluator`, `security-auditor`, `architect`, and `planner` have `permissionMode: 'plan'` built in \u2014 they start as statically read-only under any parent permission, no need to pass `permission_mode` at the call site. Custom markdown agents defined in `.oh\/agents\/*.md` can also set `permissionMode: plan` (or `permission-mode: plan`) in the frontmatter as a default.\n\n## Headless Mode\n\nRun a prompt once without the interactive UI \u2014 great for CI\/CD and scripting:\n\n```bash\n# \u63a8\u8350\uff1achat \u547d\u4ee4\u52a0 -p \u53c2\u6570\noh -p \"fix the failing tests\" --model ollama\/llama3 --trust\noh -p \"review src\/query.ts\" --auto --output-format json\n\n# \u66ff\u4ee3\uff1arun \u547d\u4ee4\noh run \"fix the failing tests\" --model ollama\/llama3 --trust\noh run \"add error handling to api.ts\" --json    # JSON \u8f93\u51fa\n\n# \u901a\u8fc7 stdin \u8f93\u5165\ncat error.log | oh run \"what's wrong here?\"\ngit diff | oh run \"review these changes\"\n\n# \u4f1a\u8bdd\u603b\u6210\u672c\u786c\u4e0a\u9650 \u2014\u2014 \u8fbe\u5230\u9608\u503c\u65f6\u4ee3\u7406\u4f1a\u4ee5 reason: \"budget_exceeded\" \u7ec8\u6b62\noh run \"review the diff\" --model claude-sonnet-4-6 --max-budget-usd 0.50\noh session --model gpt-4o --max-budget-usd 5\n```\n\n### Common CI \/ SDK CLI Flags\n\n| Flag | Effect |\n|------|------|\n| `--bare` | Skip optional startup work (project detection, plugins, memory, skills, MCP). System prompt retains only the tool-use baseline, faster startup for repos with large CLAUDE.md \/ RULES.md files. |\n| `--debug [category]` | Enable categorized debug logs. `--debug` enables all; `--debug mcp,hooks` enables only specified categories. Also reads `OH_DEBUG` env var. |\n| `--debug-file <path>` | Append debug logs to a file instead of stderr. Also reads `OH_DEBUG_FILE`. |\n| `--mcp-config <path>` | Load MCP servers from an external JSON file (merged on top of `.oh\/config.yaml`). |\n| `--strict-mcp-config` | With `--mcp-config`, completely ignore MCP servers in `.oh\/config.yaml`. |\n| `--system-prompt-file <path>` \/ `--append-system-prompt-file <path>` | File path variants of `--system-prompt` \/ `--append-system-prompt`. |\n| `--no-session-persistence` | Skip writing sessions to `~\/.oh\/sessions\/`, suitable for one-off CI runs. |\n| `--fallback-model <model>` | Fallback model for retriable errors from the primary model. Overrides `.oh\/config.yaml`'s `fallbackProviders` for this run. |\n| `--permission-prompt-tool <mcp_tool>` | Delegate tool authorization decisions to a specified MCP tool (e.g. `mcp__myperm__check`). |\n| `--init` \/ `--init-only` | Run interactive setup wizard before \/ instead of executing the command. |\n\nAll flags work on both `oh run` and `oh session`. Full list at `oh run --help` and `oh session --help`.\n\n### Constrain Structured Output with `--json-schema`\n\nConstrain model output to a JSON Schema. Useful for CI scripts that need to parse model output programmatically, avoiding regex heuristics:\n\n```bash\noh -p \"output {\\\"ok\\\": true, \\\"count\\\": 3} as JSON\" \\\n  --trust \\\n  --json-schema '{\"type\":\"object\",\"properties\":{\"ok\":{\"type\":\"boolean\"},\"count\":{\"type\":\"integer\"}},\"required\":[\"ok\",\"count\"]}'\n```\n\nBehavior:\n- stdout: single line of JSON on successful validation.\n- stderr: structured error on failure, with raw model output for debugging.\n- Exit codes: **0** validation passed, **2** schema itself invalid, **3** model output not valid JSON, **4** JSON doesn't match schema.\n\nSupported keywords: `type`, `properties`, `required`, `items`, `enum`. For fuller validation, pipe to a dedicated validator.\n\n### GitHub Action for PR Review\n\nOpenHarness ships a GitHub Action for automated code review:\n\n```yaml\n# .github\/workflows\/ai-review.yml\non:\n  pull_request:\n    types: [opened, synchronize]\n\njobs:\n  review:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n        with:\n          fetch-depth: 0\n      - uses: .\/.github\/actions\/review\n        with:\n          model: 'claude-sonnet-4-6'\n          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}\n```\n\nExit code 0 on success, 1 on failure.\n\n## Model Providers\n\n```bash\n# \u672c\u5730\uff08\u514d\u8d39\uff0c\u65e0\u9700 API key\uff09\noh --model ollama\/llama3\noh --model ollama\/qwen2.5:7b\n\n# \u4e91\u7aef\nOPENAI_API_KEY=sk-... oh --model gpt-4o\nANTHROPIC_API_KEY=sk-ant-... oh --model claude-sonnet-4-6\nOPENROUTER_API_KEY=sk-or-... oh --model openrouter\/meta-llama\/llama-3-70b\n\n# llama.cpp \/ GGUF\noh --model llamacpp\/my-model\n\n# LM Studio\noh --model lmstudio\/my-model\n```\n\n### llama.cpp \/ GGUF (Local, no Ollama needed)\n\nDirect GGUF support via `llama-server`, avoiding Ollama's overhead. Usually faster for large models.\n\n**Prerequisites:**\n- Install llama.cpp: `brew install llama.cpp`, or download from [github.com\/ggml-org\/llama.cpp](https:\/\/github.com\/ggml-org\/llama.cpp)\n- Download a GGUF model (e.g. from [HuggingFace](https:\/\/huggingface.co))\n\n**Start llama-server:**\n```bash\nllama-server --model .\/your-model.gguf --port 8080 --alias my-model\n```\n\n**Configure via `oh init`:**\n- Run `oh init`, select \"llama.cpp \/ GGUF\" when prompted\n\n**Or configure manually** in `.oh\/config.yaml`:\n```yaml\nprovider: llamacpp\nmodel: my-model\nbaseUrl: http:\/\/localhost:8080\npermissionMode: ask\n```\n\n**Run:**\n```bash\noh\noh --model llamacpp\/my-model\noh models                    # \u5217\u51fa\u53ef\u7528\u6a21\u578b\n```\n\n## ACP (Agent Client Protocol)\n\nSpeak [Agent Client Protocol](https:\/\/agentclientprotocol.com\/) over stdin\/stdout, enabling ACP-compatible editors \u2014 Zed, JetBrains via ACP plugin, Cline, OpenCode, and more \u2014 to use OpenHarness as the underlying agent without writing a separate extension for each IDE:\n\n```bash\noh acp                                          # \u8bfb\u53d6 .oh\/config.yaml \u7684 provider\/model\noh acp --provider anthropic --model claude-sonnet-4-6\n```\n\nConfigure `oh acp` as the agent launch command in your editor's ACP integration. Session-update events (text chunks, tool calls, tool results) are automatically translated through OpenHarness's streaming protocol; permission confirmations currently still go through OpenHarness's own flow rather than ACP's `requestPermission` (noted for follow-up). `@agentclientprotocol\/sdk` is an `optionalDependency` \u2014 if not installed, `oh acp` exits with a clear install prompt instead of silently failing.\n\n## Authentication (Auth)\n\nProvider-agnostic credential management. Local LLMs (Ollama \/ llama.cpp \/ LM Studio) don't need auth \u2014 configure via `oh init`.\n\n```bash\noh auth login [provider] [--key <value>]   # \u5b58\u50a8\u67d0\u4e2a\u63d0\u4f9b\u5546\u7684 API key\noh auth logout [provider]                   # \u6e05\u9664\u5df2\u5b58\u50a8\u7684 API key\noh auth status                              # \u663e\u793a\u5df2\u5b58\u50a8\u7684\u63d0\u4f9b\u5546\u53ca\u73af\u5883\u53d8\u91cf\u8986\u76d6\u60c5\u51b5\n```\n\n`[provider]` defaults to the configured default provider. `--key` passes the key directly; otherwise OH prompts interactively in TTY mode and reads to EOF on piped input.\n\n### Scripted Key Resolution (`apiKeyHelper`)\n\nAvoid writing keys to plaintext or encrypted storage by plugging in helper scripts (1Password, `pass`, vault, cloud key managers, etc.). The configured command is executed when fetching a key, with `OH_PROVIDER` as an env var; stdout (stripped of leading\/trailing whitespace) is the key.\n\n```yaml\n# .oh\/config.yaml\napiKeyHelper: 'op read \"op:\/\/Personal\/Anthropic\/key\"'\n```\n\nResolution priority: env var \u2192 encrypted storage \u2192 `apiKeyHelper` \u2192 legacy plaintext config.\n\n## Auto-Update\n\n```bash\noh update                    # \u68c0\u6d4b\u5b89\u88c5\u65b9\u5f0f\uff08npm \u5168\u5c40 \/ npx \/ \u672c\u5730\u514b\u9686\uff09\uff0c\u6253\u5370\u5bf9\u5e94\u7684\u5347\u7ea7\u547d\u4ee4\n```\n\n## Configuration Hierarchy\n\nConfig loads in layers (later ones override earlier ones):\n\n1. **Global** `~\/.oh\/config.yaml` \u2014 default provider, model, theme shared across all projects\n2. **Project** `.oh\/config.yaml` \u2014 project-level settings\n3. **Local** `.oh\/config.local.yaml` \u2014 personal overrides (gitignored)\n\nSet a default provider globally once:\n\n```yaml\n# ~\/.oh\/config.yaml\nprovider: ollama\nmodel: llama3\npermissionMode: ask\ntheme: dark\nlanguage: zh-CN        # \u53ef\u9009 \u2014\u2014 \u6a21\u578b\u4f1a\u7528\u8be5\u8bed\u8a00\u56de\u590d\uff08\u4ee3\u7801\u3001\u547d\u4ee4\u3001\u8def\u5f84\u4fdd\u6301\u539f\u6837\uff09\noutputStyle: default   # \u53ef\u9009 \u2014\u2014 \"default\"\u3001\"explanatory\"\u3001\"learning\" \u6216\u81ea\u5b9a\u4e49\u540d\n```\n\nThen project config only needs to specify what's different:\n\n```yaml\n# .oh\/config.yaml\nmodel: codellama   # \u4ec5\u8986\u76d6\u6a21\u578b\n```\n\n### Output Styles\n\nSwitch the agent's \"personality\" without modifying core instructions. Built-in styles:\n\n- **`default`** \u2014 standard software engineering assistant (no prefix)\n- **`explanatory`** \u2014 appends a `## Insights` section after each task, explaining *why* the decision was made\n- **`learning`** \u2014 leaves 1\u20133 `TODO(human)` markers at key points, leaving the most learning-worthy code for you to write yourself\n\nCustom styles are markdown files with YAML frontmatter. Save to `.oh\/output-styles\/<name>.md` (project-level) or `~\/.oh\/output-styles\/<name>.md` (user-level). Project-level > user-level > built-in.\n\n````markdown\n---\nname: code-review\ndescription: \u4e13\u6ce8\u7684\u4ee3\u7801\u5ba1\u67e5\u6a21\u5f0f\n---\n\n\u4e25\u683c\u5ba1\u67e5\u3002\u5bf9\u6bcf\u4e2a\u51fd\u6570\u8ffd\u95ee\uff1a\u903b\u8f91\u662f\u5426\u6b63\u786e\uff1f\u9519\u8bef\u5904\u7406\u662f\u5426\u5b8c\u6574\uff1f\u6709\u6ca1\u6709\u9057\u6f0f\u7684\u8fb9\u754c\u60c5\u51b5\uff1f\n```\n\nActivate in `.oh\/config.yaml` via `outputStyle: code-review`.\n\n## Project Rules\n\nCreate `.oh\/RULES.md` in any repo (or run `oh init`):\n\n```markdown\n- Always run tests after changes\n- Use strict TypeScript\n- Never commit to main directly\n```\n\nRules auto-load into every session.\n\nOpenHarness also auto-reads the following project instruction files (if present, loaded in parent-directory-first merge order):\n\n- `CLAUDE.md` (Anthropic convention) \u2014 includes hierarchical `CLAUDE.md` files from parent directories to project root, plus global `~\/.claude\/CLAUDE.md`\n- `AGENTS.md` ([agents.md cross-tool standard](https:\/\/agents.md\/), adopted by Codex \/ Cursor \/ Copilot \/ Cline \/ Aider) \u2014 same parent-directory-first scan\n- `CLAUDE.local.md` (gitignored personal overrides)\n\nIf a repo is already configured with `AGENTS.md` for another agent, OpenHarness reads it directly \u2014 no migration needed.\n\n## Skills & Plugins\n\n### Skills\n\nSkills are markdown files with YAML frontmatter that add reusable behavior:\n\n```markdown\n---\nname: deploy\ndescription: Deploy the application to production\ntrigger: deploy\ntools: [Bash, Read]\n---\n\nRun the deploy script with health checks...\n```\n\n**Lookup locations** (in order):\n1. `.oh\/skills\/` \u2014 project-level skills\n2. `~\/.oh\/skills\/` \u2014 global skills (available in all projects)\n\nSkills auto-trigger when user messages contain trigger keywords; they can also be invoked explicitly via `\/skill deploy`.\n\n### Plugins\n\nPlugins are npm packages that bundle skills, hooks, and MCP servers:\n\n```json\n{\n  \"name\": \"my-openharness-plugin\",\n  \"version\": \"1.0.0\",\n  \"skills\": [\"skills\/deploy.md\", \"skills\/review.md\"],\n  \"hooks\": {\n    \"sessionStart\": \"scripts\/setup.sh\"\n  },\n  \"mcpServers\": [\n    { \"name\": \"my-api\", \"command\": \"npx\", \"args\": [\"-y\", \"@my-org\/mcp-server\"] }\n  ]\n}\n```\n\nName it `openharness-plugin.json` and place it at the npm package root. Install with `npm install` and OpenHarness auto-discovers it from `node_modules\/`.\n\n## Evals\n\n`oh evals` runs SWE-bench-Lite-compatible evals locally against any Provider, with enforced cost caps. Measures real bug fix performance, more meaningful than synthetic benchmarks.\n\n```bash\n# \u7528 5 \u7f8e\u5143\u603b\u4e0a\u9650\u30012 \u8def\u5e76\u53d1\u8dd1\u4e00\u4e2a\u81ea\u5b9a\u4e49 pack\noh evals run my-pack --max-cost-usd 5 --concurrency 2\n\n# \u53ea\u8dd1\u6307\u5b9a instance\noh evals run my-pack --max-cost-usd 1 --instance django__django-11551\n\n# \u968f\u673a\u62bd\u53d6 3 \u4e2a\noh evals run my-pack --max-cost-usd 2 --sample 3\n\n# \u7eed\u8dd1\u56e0\u6210\u672c\u4e0a\u9650\u4e2d\u65ad\u7684\u8fd0\u884c\noh evals run my-pack --max-cost-usd 10 --resume 2026-05-05T14-30-00\n\n# \u5217\u51fa\u5df2\u5b89\u88c5\u7684 pack\noh evals list-packs\n\n# \u67e5\u770b\u5386\u53f2\u8fd0\u884c\u7684\u6c47\u603b\noh evals show 2026-05-05T14-30-00\n```\n\nOutput goes to `~\/.oh\/evals\/runs\/<run-id>\/`:\n\n- `results.json` \u2014 complete data per task: cost, turns, duration, tests_status, error messages.\n- `predictions.json` \u2014 directly submittable to the SWE-bench leaderboard https:\/\/www.swebench.com\/\u3002\n- `transcripts\/<instance_id>.jsonl` \u2014 raw `stream-json` output from each task sub-process.\n\nThe pluggable pack protocol (`pack.json` + `instances.jsonl` + `fixtures\/<id>\/`) lets you write packs against any test suite. The `scripts\/build-evals-pack.mjs` tool can bake a SWE-bench-Lite-compatible repo at a given `base_commit` into a fixture. See [CONTRIBUTING.md](CONTRIBUTING.md#authoring-eval-packs).\n\nThe built-in `swe-bench-lite-mini` pack (10 curated instances, runs out of the box) ships in v2.40.2.\n\n## How It Works\n\n```mermaid\ngraph LR\n    User[\u7528\u6237\u8f93\u5165] --> REPL[REPL \u5faa\u73af]\n    REPL --> Query[\u67e5\u8be2\u5f15\u64ce]\n    Query --> Provider[LLM \u63d0\u4f9b\u5546]\n    Provider --> LLM[Ollama \/ OpenAI \/ Anthropic]\n    LLM --> Tools[\u5de5\u5177\u6267\u884c]\n    Tools --> Permissions{\u6743\u9650\u68c0\u67e5}\n    Permissions -->|\u6279\u51c6| Execute[\u8fd0\u884c\u5de5\u5177]\n    Permissions -->|\u62d2\u7edd| Deny[\u62d2\u7edd\u5e76\u4e0a\u62a5]\n    Execute --> Response[\u6d41\u5f0f\u54cd\u5e94]\n    Response --> REPL\n```\n\n## FAQ\n\n**Can I use it offline?**\nYes. Use Ollama with a local model \u2014 no network, no API key needed.\n\n**How much does it cost?**\nFree. OpenHarness uses the MIT license. Cloud models require your own API key (BYOK), or use Ollama entirely free.\n\n**Is it secure?**\nYes. Seven permission modes control what tools can do. Bash commands are analyzed by an AST parser that blocks destructive patterns (`rm -rf`, `curl | bash`, etc.). Every file change is checkpointed and can be reverted via `\/rewind`.\n\n**Can I use it in CI\/CD?**\nYes. Run headless mode with `oh -p \"prompt\" --auto`, or use the built-in GitHub Action for PR review.\n\n**Does it support my language\/framework?**\nYes. OpenHarness is language-agnostic \u2014 it reads, writes, and executes code in any language. Syntax highlighting covers 20+ languages.\n\n**How does it compare to Claude Code?**\n~95% feature parity for CLI use cases. Key advantages: works with any LLM (not just Anthropic), and uses the MIT license. See [Why OpenHarness?](#why-openharness) above.\n\n## Installation\n\nRequires **Node.js 18+**.\n\n```bash\n# \u4ece npm \u5b89\u88c5\nnpm install -g @zhijiewang\/openharness\n\n# \u4ece\u6e90\u7801\u5b89\u88c5\ngit clone https:\/\/github.com\/zhijiewong\/openharness.git\ncd openharness\nnpm install && npm install -g .\n```\n\n## Development\n\n```bash\nnpm install\nnpx tsx src\/main.tsx              # \u4ee5\u5f00\u53d1\u6a21\u5f0f\u8fd0\u884c\nnpx tsc --noEmit                  # \u7c7b\u578b\u68c0\u67e5\nnpm test                          # \u8fd0\u884c\u6d4b\u8bd5\n```\n\n### Adding a Tool\n\nImplement the `Tool` interface (with a Zod input schema) in `src\/tools\/YourTool\/index.ts`, then register it in `src\/tools.ts`.\n\n### Adding a Model Provider\n\nImplement the `Provider` interface in `src\/providers\/yourprovider.ts`, then add a case in `src\/providers\/index.ts`.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## Community\n\nJoin the OpenHarness community to get help, share workflows, and discuss the future of AI coding assistants!\n\n| Platform | Details & Links |\n| :--- | :--- |\n| \ud83d\udfe3 **Discord** | [**Join our Discord**](https:\/\/discord.gg\/ezVrqy3qu) to chat with developers in real-time and get support. |\n| \ud83d\udd35 **Feishu \/ Lark** | Scan the QR code below to join the community collaboration group:<br><br><img src=\"https:\/\/github.com\/user-attachments\/assets\/54ade077-22ad-45d2-b38a-623464677d53\" width=\"160\" alt=\"Feishu group QR code\"> |\n| \ud83d\udfe2 **WeChat** | Scan the QR code below to join the WeChat group:<br><br><img src=\"https:\/\/github.com\/user-attachments\/assets\/adcf291a-9ffe-4738-8608-f46a21e18db0\" width=\"160\" alt=\"WeChat group QR code\"> |\n\n## License\n\nMIT","ai_share_github_readme_en_source_hash":"c5dc5e64599a82fbf4e7ce4e6040ccaa8f4096446294b7273367bc093a162b6d","ai_share_github_readme_en_generated_at":"2026-08-18T09:52:24+00:00","ai_share_github_readme_en_provider":"OpenClaw \/ MiniMax-M2.7"},"categories":[2492],"tags":[1911,1565,2044,1577,2047,1605,2050,1999],"ai_series":[],"ai_topic":[2650],"ai_creator":[719],"ai_collection":[291],"ai_resource_type":[325],"ai_tool_tag":[179,183,182,180,181],"class_list":["post-4155","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cli_tool","tag-ai","tag-cli","tag-coding-agent","tag-llm","tag-ollama","tag-openai","tag-terminal","tag-typescript","ai_topic-programming-development","ai_creator-zhijiewong","ai_collection-harness-agent-engineering","ai_resource_type-cli_tool","ai_tool_tag-ai--cli","ai_tool_tag-sdk","ai_tool_tag-worktree","ai_tool_tag-180","ai_tool_tag-181"],"source_url":"","preserve_modified":false,"lang":"en","translations":{"en":4155,"zh-cn":69},"pll_sync_post":{},"_links":{"self":[{"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/posts\/4155","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/comments?post=4155"}],"version-history":[{"count":1,"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/posts\/4155\/revisions"}],"predecessor-version":[{"id":4160,"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/posts\/4155\/revisions\/4160"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/media\/3639"}],"wp:attachment":[{"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/media?parent=4155"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/categories?post=4155"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/tags?post=4155"},{"taxonomy":"ai_series","embeddable":true,"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/ai_series?post=4155"},{"taxonomy":"ai_topic","embeddable":true,"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/ai_topic?post=4155"},{"taxonomy":"ai_creator","embeddable":true,"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/ai_creator?post=4155"},{"taxonomy":"ai_collection","embeddable":true,"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/ai_collection?post=4155"},{"taxonomy":"ai_resource_type","embeddable":true,"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/ai_resource_type?post=4155"},{"taxonomy":"ai_tool_tag","embeddable":true,"href":"https:\/\/ai.wuaishare.cn\/hub\/wp-json\/wp\/v2\/ai_tool_tag?post=4155"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}