The go-code Command
go-code is the single terminal entry point for the entire go-code runtime. It is a shell wrapper (scripts/go-code.sh) that sits in front of harnessd (the daemon) and harnesscli (the CLI client). You never have to start the daemon manually — go-code detects whether a server is already running, boots one if needed, and shuts it down again when you are done.
What you get from this one command:
- An interactive, full-screen TUI for multi-turn agent conversations
- A single-shot streaming mode for scripted or piped use
- A daemon-only mode for long-lived background servers
- All run-management operations (list, inspect, cancel, continue, replay, search, improve)
What the wrapper does
When you invoke go-code, the wrapper:
- Checks whether
harnessdis healthy at the configured port by hittingGET /healthz. - If no healthy server is found, starts
harnessdin the background and waits up to 10 seconds for it to become ready. - Resolves your project root by walking up from the current directory.
- Delegates to
harnesscli(for TUI and prompt modes) or passes the subcommand straight through (for run-management operations). - On exit, stops the server only if the wrapper started it. A server you started independently is always left running.
The auto-start/auto-stop contract is intentional. Running go-code in two terminals at the same time is safe: the second invocation detects the already-running server and reuses it without touching its lifecycle.
Invocation modes
Headless scripting and exit codes
The wrapper propagates the harnesscli exit code unchanged, so shell scripts and CI can branch on $? exactly as they would when calling harnesscli directly — including when the wrapper started the server itself (the auto-stop EXIT trap does not override the exit status):
go-code "summarize the diff"
case $? in
0) echo "run completed" ;;
2) echo "run failed" ;;
3) echo "run blocked on input — answer with harnesscli input <run-id> \"<q>=<a>\", or resume interactively with go-code --resume <run-id>" ;;
6) echo "run cancelled — not resumable; harnesscli continue requires status=completed. Start a new run instead." ;;
esac
The full code table (0 completed, 1 client error, 2 failed, 3 blocked, 6 cancelled, 130 interrupted) and its per-command coverage are documented in Exit Codes.
Subcommand reference
go-code runs and go-code list are both aliases — they both map to harnesscli list. Similarly, go-code show and go-code status both map to harnesscli status.
| Invocation | Maps to harnesscli subcommand | What it does |
|---|---|---|
go-code | --tui | Launches the interactive BubbleTea TUI |
go-code "prompt" | -prompt "..." | Runs a single prompt, streams events, exits |
go-code --server | (server lifecycle only) | Starts harnessd in background and exits |
go-code --resume <run-id> | --tui -resume <run-id> | Launches the TUI resuming an existing conversation |
go-code runs | list | Lists known runs |
go-code list | list | Alias for runs |
go-code show <id> | status | Shows one run |
go-code status <id> | status | Alias for show |
go-code cancel <id> | cancel | Cancels one run |
go-code continue <id> "prompt" | continue | Continues a completed run and streams events |
go-code replay <id-or-path> | replay | Replays a recorded run |
go-code search <query> | search | Searches run metadata (client-side substring match) |
go-code improve [flags] | improve | Runs or plans the self-improvement test loop |
Address and project root
Server address: HARNESS_ADDR
The HARNESS_ADDR environment variable controls the listen address. The default is 127.0.0.1:8080. The wrapper only uses the port from this value — the host part is ignored. A harnessd that the wrapper starts always binds 127.0.0.1 on that port, because the wrapper only ever talks to it over loopback (http://127.0.0.1:<port>).
# Run on a different port
HARNESS_ADDR=:9090 go-code "List the Go source files"
The address can also be set in your project or user config file (~/.harness/config.toml or .harness/config.toml). HARNESS_ADDR takes precedence over the TOML layers.
If you need a daemon reachable from another machine, don't go through go-code — run harnessd directly with an explicit HARNESS_ADDR (a real host, not just a port) and either a configured API key store or HARNESS_AUTH_DISABLED=true. An unauthenticated daemon that listens beyond loopback refuses to start otherwise.
Startup output and the daemon log
When the wrapper starts harnessd itself, the daemon's stdout and stderr no longer print to your terminal. They are redirected to a log file at ${TMPDIR:-/tmp}/harnessd.<pid>.log (created with umask 077, so only you can read it), and the wrapper prints that path on a log: line after a successful start. This matters most in --server mode, where the daemon outlives the wrapper process. This keeps a healthy startup to a handful of lines instead of interleaving the daemon's own boot log into the terminal, and it stops a stray daemon log line from landing in the TUI after handoff.
If harnessd fails to become healthy, go-code prints the last 20 lines of that log under a harnessd said: heading before exiting, so the failure reason isn't just a path you have to go open yourself. Lines matching fatal:, panic:, or refusing to start are highlighted; the rest are dimmed.
The wrapper colors its own [go-code] prefix and the WARN:/ERROR: markers (cyan, yellow, red) when writing to a terminal. Color is never the only signal — the words WARN: and ERROR: always stay in the text. Color is disabled, and output is plain text, whenever NO_COLOR is set, TERM=dumb, or the given output stream (stdout or stderr) isn't a terminal — for example when you pipe go-code into another command. stdout and stderr are checked independently, since one can be redirected without the other.
Piping go-code into a command that exits early — go-code runs | head -5, or any pager you quit before it reaches the end of the output — still stops a daemon the wrapper started. Closing the read end of the pipe makes the wrapper's own status writes fail (SIGPIPE/EPIPE), but that failure can no longer stop the shutdown itself: stop_server ignores PIPE before doing anything else, and every status line is written with || true, so a write failure never skips the kill that stops harnessd.
Troubleshooting: a stale harnessd left behind
If a harnessd the wrapper started is ever left running after go-code exits — for example after a crash rather than a normal exit — the symptom on your next go-code invocation in that project is a failure like callback workspace is already owned: resource temporarily unavailable, because the leftover daemon still holds the workspace's callback-recovery lock. Changing HARNESS_ADDR or the port does not help: the lock is scoped to the workspace, not the port. The remedy is to stop the stray process directly, for example pkill -f harnessd, then run go-code again.
Project root detection
go-code automatically resolves the workspace root before launching TUI or prompt mode. It walks parent directories from $PWD, looking for:
- A
.git/directory - A
.harness/config.tomlfile
The first directory that contains either marker is used as the workspace root. If neither is found at any level, $PWD is used as the fallback.
The resolved path is passed to harnesscli as -workspace <root>, so the agent always operates relative to your project root — not the directory you happened to be in when you ran the command.
~/projects/
myapp/ ← .git/ lives here → workspace root
src/
api/ ← you run "go-code" here
In the example above, running go-code from src/api/ resolves the workspace root as ~/projects/myapp/.
Key-free smoke testing
You can run go-code without any provider API key by setting HARNESS_PROVIDER=fake. The fake provider returns deterministic responses and is the right choice for CI smoke tests and local integration checks.
# Start the server with the fake provider
HARNESS_PROVIDER=fake go-code --server
# In another terminal, run a prompt against it
go-code "hello"
Or in a single invocation:
HARNESS_PROVIDER=fake go-code "Does this pipeline work?"
The --server flag removes the auto-stop trap. After using go-code --server, stop the daemon manually with pkill harnessd. If you need to use the PID file directly, note that it is written to ${TMPDIR:-/tmp}/harnessd.<wrapper-pid>.pid — on macOS $TMPDIR is a per-user temp directory (e.g. /var/folders/.../T/), not /tmp. The filename uses the launching wrapper's PID; the file's contents are harnessd's PID.
Prerequisites
go-code requires three commands on your PATH: harnessd, harnesscli, and curl. If any of these are missing it exits with an error. The recommended install (brew install --HEAD dennisonbertram/go-code/go-code or ./scripts/install.sh --add-to-path) places all three in the correct location automatically.
Next steps
- Configure the server — set default model, cost limits, and provider keys: see the Configuration reference.
- Understand run events — learn what
run.completed,run.failed, and the rest of the SSE event stream mean: see Events. - Use
harnessclidirectly — for flag-level control over individual subcommands without the wrapper: see the harnesscli reference.