{"id":50443763,"url":"https://github.com/rumpl/harness","last_synced_at":"2026-05-31T20:02:26.645Z","repository":{"id":357901936,"uuid":"1207229270","full_name":"rumpl/harness","owner":"rumpl","description":"Go library for calling different AI harnesses with a unified interface","archived":false,"fork":false,"pushed_at":"2026-05-14T18:06:54.000Z","size":27,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-14T20:12:52.313Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rumpl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-10T18:03:52.000Z","updated_at":"2026-05-14T18:06:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rumpl/harness","commit_stats":null,"previous_names":["rumpl/harness"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/rumpl/harness","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rumpl%2Fharness","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rumpl%2Fharness/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rumpl%2Fharness/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rumpl%2Fharness/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rumpl","download_url":"https://codeload.github.com/rumpl/harness/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rumpl%2Fharness/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33746514,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-31T20:02:25.951Z","updated_at":"2026-05-31T20:02:26.640Z","avatar_url":"https://github.com/rumpl.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# harness\n\nA Go library for calling different AI coding agent CLIs (Docker Agent, Claude Code, Pi, Codex, opencode) through a unified interface. Switch between agents by changing a single line of code.\n\n## Install\n\n```\ngo get github.com/rumpl/harness\n```\n\n## Quick start\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/rumpl/harness\"\n\t\"github.com/rumpl/harness/dockeragent\"\n)\n\nfunc main() {\n\t// Create a provider — swap this line to switch agents.\n\tp := dockeragent.New(\"coder\")\n\n\t// Run the agent and handle streaming events.\n\tharness.Run(context.Background(), p, \"Explain goroutines\", func(ev harness.Event) {\n\t\tswitch ev.Type {\n\t\tcase harness.EventText:\n\t\t\tfmt.Print(ev.Text)\n\t\tcase harness.EventToolCallStart:\n\t\t\tfmt.Printf(\"[tool: %s]\\n\", ev.ToolName)\n\t\tcase harness.EventToolCallDelta:\n\t\t\tfmt.Print(ev.ToolArgs)\n\t\tcase harness.EventToolCall:\n\t\t\tfmt.Printf(\"[tool: %s] %s\\n\", ev.ToolName, ev.ToolArgs)\n\t\tcase harness.EventToolResult:\n\t\t\tfmt.Printf(\"[tool result: %s] %s\\n\", ev.ToolName, ev.ToolOutput)\n\t\tcase harness.EventResult:\n\t\t\tfmt.Printf(\"\\nResult: %s\\n\", ev.Result)\n\t\t}\n\t})\n}\n```\n\n### Switching providers\n\n```go\n// Docker Agent\np := dockeragent.New(\"coder\")\n\n// Claude Code\np := claudecode.New(\"claude-sonnet-4-6\", claudecode.WithEffort(claudecode.EffortHigh))\n\n// Pi\np := pi.New(\"claude-sonnet-4-6\")\n\n// Codex\np := codex.New(\"gpt-5.4-mini\")\n\n// opencode\np := opencode.New(\"anthropic/claude-sonnet-4-6\")\n```\n\nThe rest of your code stays exactly the same — all providers implement `harness.Provider`.\n\nFor providers whose CLIs have their own default model, pass an empty model string to omit the model flag entirely (for example, `codex.New(\"\")` emits `codex exec ...` without `-m`).\n\n## The `Provider` interface\n\n```go\ntype Provider interface {\n\tName() string\n\tPrintCommand(prompt string) string\n\tInteractiveArgs(prompt string) []string\n\tParseStreamLine(line string) []Event\n}\n```\n\n| Method             | Purpose                                                           |\n| ------------------ | ----------------------------------------------------------------- |\n| `Name()`           | Human-readable identifier (`\"docker-agent\"`, `\"claude-code\"`, etc.) |\n| `PrintCommand()`   | Shell command for non-interactive mode (`sh -c` safe)             |\n| `InteractiveArgs()`| Arg list for interactive mode (first element = binary)            |\n| `ParseStreamLine()`| Parse one NDJSON line into `[]Event`                              |\n\n## Event types\n\n| Type           | Fields set              |\n| -------------- | ----------------------- |\n| `EventText`    | `Text`                  |\n| `EventResult`  | `Result`, `Usage` (opt) |\n| `EventToolCallStart` | `ToolID` (opt), `ToolName` |\n| `EventToolCallDelta` | `ToolID` (opt), `ToolName` (opt), `ToolArgs` raw delta |\n| `EventToolCall`| `ToolID` (opt), `ToolName`, `ToolArgs` |\n| `EventToolResult` | `ToolID` (opt), `ToolName` (opt), `ToolOutput`, `ToolError` |\n\n## Example CLI\n\n```bash\ngo run ./cmd/harness-example --provider docker-agent --model coder \"Hello world\"\ngo run ./cmd/harness-example --provider claude-code --model claude-sonnet-4-6 \"Hello world\"\ngo run ./cmd/harness-example --provider pi --model claude-sonnet-4-6 \"Hello world\"\ngo run ./cmd/harness-example --provider codex --model gpt-5.4-mini \"Hello world\"\ngo run ./cmd/harness-example --provider opencode --model anthropic/claude-sonnet-4-6 \"Hello world\"\n\n# Just print the command without executing:\ngo run ./cmd/harness-example --print-cmd --provider docker-agent --model coder \"test\"\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frumpl%2Fharness","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frumpl%2Fharness","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frumpl%2Fharness/lists"}