harvey is a terminal agent for local language models.
See man page
How it starts:
Harvey looks for HARVEY.md in the workspace root and uses it as a system prompt. It then connects to a local Ollama server and starts an interactive chat session.
Note: Harvey focuses on local models via Ollama. It has limited support for SaaS endpoints (Anthropic, DeepSeek, Gemini, Mistral, OpenAI).
All file I/O is constrained to the workspace. A knowledge base is
stored at <workspace>/agents/knowledge.db and is
created automatically on first run. Session recordings are stored in
<workspace>/agents/sessions/. Both paths can be
overridden in <workspace>/agents/harvey.yaml.
Type /help inside the session for available slash commands.
# Change to project directory
cd $HOME/myproject
# Start in the workspace (current directory), auto-select Ollama model
harveyWhen Harvey starts it:
agents/knowledge.db in the
workspace.agents/harvey.yaml if present (overrides paths
for KB, sessions, agents).agents/sessions/ for prior .spmd /
.fountain session files and offers to resume one (default:
No). If a session is chosen, the model it used is pre-selected in the
next step.HARVEY.md from the workspace root, expands any dynamic markers, and injects it as the
system prompt.ollama serve
then retries..spmd file in
agents/sessions/.harvey >Harvey looks for HARVEY.md in the directory where it is
launched and uses its contents as the LLM system prompt for the whole
session. This is the primary way to give the model persistent context
about your project.
A minimal HARVEY.md:
You are a coding assistant for a Go project.
The codebase uses Go 1.26, targets macOS and Linux.
Prefer table-driven tests and avoid third-party testing libraries.Harvey expands the following HTML comment markers before injecting the system prompt, inserting live workspace data so the model always has an up-to-date picture of the project:
| Marker | Replaced with |
|---|---|
<!-- @date --> |
Today’s date (YYYY-MM-DD) |
<!-- @files --> |
Workspace file tree (hidden directories excluded) |
<!-- @git-status --> |
Output of git status --short, or
(not a git repository) |
Example HARVEY.md with all three markers:
You are a coding assistant for this Go project.
Today: <!-- @date -->
## Workspace files
<!-- @files -->
## Current git status
<!-- @git-status -->
## Conventions
- All exported symbols need /** ... */ doc comments.
- Use `t.TempDir()` in tests; no global state.$ harvey -m llama3:latest
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Harvey 0.0.0
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ Workspace: /home/user/myproject
✓ Knowledge base: harvey/knowledge.db
✓ Loaded HARVEY.md as system prompt
Checking Ollama at http://localhost:11434...
✓ Ollama is running
Using model: llama3:latest
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Connected: Ollama (llama3:latest)
/help for commands · /exit to quit
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
harvey > /read internal/parser/parser.go
✓ internal/parser/parser.go (3241 bytes)
1 file(s) added to context.
harvey > The ParseExpr function panics on empty input. Can you fix it?
⠹ The Jumblies have gone to sea in a sieve... [4s / ~11s]
Here is the corrected function:
```go internal/parser/parser.go
func ParseExpr(src string) (Expr, error) {
if src == "" {
return nil, fmt.Errorf("parseExpr: empty input")
}
// ... rest of function
}
```
26 prompt + 58 reply tokens · 11.2s · 5.2 tok/s
harvey > /apply
Found 1 tagged block(s):
internal/parser/parser.go (1847 bytes)
Apply all? [Y/n] y
✓ internal/parser/parser.go
harvey > /run go test ./internal/parser/...
$ go test ./internal/parser/...
312 bytes of output added to context.
harvey > All tests pass now. /bye
Goodbye.After each assistant response Harvey prints a stats line showing prompt tokens, reply tokens, elapsed time, and generation speed. While the model is thinking, an animated spinner with an estimated completion time keeps the terminal alive.
Harvey’s prompt supports readline-style line editing.
| Key | Action |
|---|---|
← / → |
Move cursor one character |
Home / Ctrl+A |
Jump to beginning of line |
End / Ctrl+E |
Jump to end of line |
↑ / ↓ |
Cycle through command history |
| Key | Action |
|---|---|
Backspace |
Delete character before cursor |
Ctrl+D |
Delete character under cursor (or EOF on an empty line) |
Ctrl+K |
Delete from cursor to end of line |
| Key | Action |
|---|---|
Ctrl+C |
Cancel current input and return to prompt |
Ctrl+X then Ctrl+E |
Open $EDITOR (falling back to $VISUAL,
then vi) to compose a multi-line prompt; the file’s content
is submitted when the editor exits |
The Ctrl+X Ctrl+E shortcut is especially useful for
longer prompts — you can write, edit, and review the full prompt in your
preferred editor before sending it. The current line content is
pre-loaded into the editor so you can also edit a prompt you have
already started typing.
Type /help at any prompt for a live command list. All
commands begin with /.
| Command | Description |
|---|---|
/help |
List all available slash commands |
/status |
Show backend, history length, workspace, KB state, and recording status |
/clear |
Reset conversation history (system prompt and pinned context are kept) |
/exit /quit /bye |
End the session |
Ollama
| Command | Description |
|---|---|
/ollama start |
Launch ollama serve in the background |
/ollama stop |
Print a reminder to use your system’s service manager |
/ollama status |
Check whether Ollama is reachable |
/ollama list |
List installed models; the current model is marked with
* |
/ollama ps |
Show which models are currently loaded in memory |
/ollama run MODEL [PROMPT] |
Start an interactive Ollama session (passes through the terminal) |
/ollama pull MODEL |
Download a model from the Ollama registry |
/ollama push MODEL |
Upload a model to the Ollama registry |
/ollama show MODEL |
Display a model’s Modelfile and parameters |
/ollama create NAME [-f MODELFILE] |
Create a new model from a Modelfile |
/ollama cp SOURCE DEST |
Copy an installed model to a new name |
/ollama rm MODEL [MODEL...] |
Remove one or more installed models |
/ollama use MODEL |
Switch to a different installed model mid-session |
/ollama logs |
Tail the Ollama service log |
/ollama env |
Show Ollama environment variables as seen by Harvey |
Note: publicai.co support has been removed. These commands are no longer available.
These commands move information between the workspace and the conversation.
/read FILE [FILE...]Reads one or more workspace files and injects their contents as a labelled user context message. Supports multiple files in a single call.
harvey > /read cmd/harvey/main.go harvey/commands.go
✓ cmd/harvey/main.go (2104 bytes)
✓ harvey/commands.go (28341 bytes)
2 file(s) added to context.
/write PATHWrites the last assistant reply to a workspace file. If the reply contains a fenced code block, the first such block is extracted and written; otherwise the full reply text is written.
harvey > /write harvey/spinner.go
✓ Wrote first code block to harvey/spinner.go (4201 bytes)
/run COMMAND [ARGS...]Runs a shell command in the workspace root, captures combined stdout and stderr (up to 8 000 bytes), and injects the output as context. Exit codes are noted in the context message.
harvey > /run go test ./...
$ go test ./...
847 bytes of output added to context.
harvey > /run make build
$ make build
1203 bytes of output added to context (exit 1).
Harvey includes a layered security system for controlling what it can
do on your system. All settings persist across sessions in
agents/harvey.yaml.
Restricts which programs may be run via ! or
/run to an explicit allowlist.
harvey > /safemode on
Safe mode enabled. Only allowed commands can be executed.
Allowed: ls, cat, grep, head, tail, wc, find, stat, jq, htmlq, bat, batcat
harvey > /safemode allow git
Added "git" to allowlist.
harvey > /safemode status
Safe mode: on
Allowed commands (13): ls, cat, grep, head, tail, wc, find, stat, jq, ...
harvey > /safemode off
Safe mode disabled. All commands are allowed.
Subcommands: on, off, status,
allow CMD, deny CMD, reset.
Fine-grained read/write/exec/delete control per path prefix, checked
before every /read, /write, and
/apply operation.
harvey > /permissions set docs/ read
Set permissions for "docs/": read
harvey > /permissions list
Configured permissions:
.: read, write, exec, delete
docs/: read
Subcommands: list [PATH], set PATH PERMS,
reset. Valid permission tokens: read,
write, exec, delete.
Every command execution, file read, file write, and skill invocation is recorded to an in-memory ring buffer (last 1000 events).
harvey > /audit show 5
Last 5 audit events:
[14:02:11.432] command: go test ./... (allowed)
[14:02:09.801] file_read: README.md (success)
[14:02:05.120] command: rm -rf / (denied)
harvey > /audit status
Audit buffer: 3/1000 events
Subcommands: show [N], clear,
status.
harvey > /security
Shows safe mode state, all configured path permissions, and audit buffer capacity at a glance.
Cloud provider API keys (ANTHROPIC_API_KEY,
DEEPSEEK_API_KEY, GEMINI_API_KEY,
MISTRAL_API_KEY, OPENAI_API_KEY) are stripped
from the environment of every child process started by ! or
/run. They are never visible to commands Harvey runs on
your behalf.
Shell commands have a configurable timeout (default 5 minutes).
Ollama queries default to no timeout, which is correct for slow
hardware. Both are set in agents/harvey.yaml:
run_timeout: "5m" # or "300s", "1m30s", "300"
ollama_timeout: "" # empty = no timeout (recommended for Ollama on a Pi)/search PATTERN [PATH]Searches workspace files for a regular expression. Results are capped
at 100 matching lines. Binary files and hidden directories
(.git, .gitignore, etc.) are skipped
automatically. An optional second argument scopes the search to a
subdirectory.
harvey > /search "func.*Handler"
12 match(es) for "func.*Handler" added to context.
harvey > /search TODO internal/
3 match(es) for "TODO" added to context.
/git SUBCOMMAND [ARGS...]Runs a read-only git command (status, diff,
log, show, blame) in the
workspace root and injects the output into context. Additional arguments
are passed through.
harvey > /git status
harvey > /git diff HEAD~1
harvey > /git log --oneline -10
harvey > /git blame internal/parser/parser.go
/applyScans the last assistant reply for fenced code blocks whose opening
fence line includes a file path
(e.g. ```go harvey/spinner.go), lists the files found, asks
for a single Y/n confirmation, then writes all of them.
harvey > /apply
Found 2 tagged block(s):
harvey/spinner.go (4312 bytes)
harvey/terminal.go (7891 bytes)
Apply all? [Y/n] y
✓ harvey/spinner.go
✓ harvey/terminal.go
For Harvey to auto-detect a file, tag the fence line with the target path:
```go harvey/spinner.go
func (s *Spinner) run() {
...
}
```/summarizeAsks the connected LLM to condense the current conversation into a single paragraph, then replaces the full history with that summary. Use this when a long session is approaching the model’s context window.
harvey > /summarize
History condensed to 312 chars.
The summary is stored as a [Conversation summary] user
message. The system prompt and any pinned context are preserved.
/context add|show|clearManages pinned context — text that is automatically
re-injected into the conversation history after every
/clear or /summarize, keeping persistent facts
in front of the model throughout the session.
harvey > /context add Target platform: macOS arm64. Prefer stdlib over third-party.
Pinned context updated (57 chars).
harvey > /context add The project uses semantic versioning via codemeta.json.
Pinned context updated (106 chars).
harvey > /context show
Pinned context (106 chars):
Target platform: macOS arm64. Prefer stdlib over third-party.
The project uses semantic versioning via codemeta.json.
harvey > /context clear
Pinned context cleared.
Multiple /context add calls append to the existing
pinned context, separated by newlines. To replace it entirely,
/context clear then /context add.
Harvey maintains a SQLite knowledge base at
agents/knowledge.db in the workspace. It is independent of
conversation history and persists across sessions.
| Command | Description |
|---|---|
/kb status |
Show all projects with recent observations |
/kb project list |
List projects with ID and status |
/kb project add NAME [DESC] |
Create a project and set it as current |
/kb project use ID |
Set the active project by ID |
/kb observe [KIND] TEXT |
Record an observation against the active project |
/kb concept list |
List all concepts |
/kb concept add NAME [DESC] |
Add a named concept |
Observation kinds: note, finding,
decision, question,
hypothesis.
harvey > /kb project add harvey "Terminal coding agent"
Project "harvey" added (id=1) and set as current.
harvey > /kb observe finding WAL mode doubled write throughput in the knowledge base
Observation recorded (id=1, kind=finding).
harvey > /kb observe decision Use bufio.Scanner for all interactive prompt reading
Observation recorded (id=2, kind=decision).
harvey > /kb status
[1] harvey (active)
Terminal coding agent
[finding] WAL mode doubled write throughput in the knowledge base
[decision] Use bufio.Scanner for all interactive prompt reading
Harvey can record sessions to Fountain screenplay files. Fountain recordings capture every exchange in a structured, human-readable format that Harvey can replay or resume later.
| Command | Description |
|---|---|
/record start [FILE] |
Begin recording; FILE defaults to a timestamped path in the workspace |
/record stop |
Close the recording file |
/record status |
Show the current recording path or “not recording” |
harvey > /record start
Recording started: /home/user/myproject/agents/harvey-session-20260415-142300.fountain
harvey > /record stop
Recording stopped. Session saved to harvey-session-20260415-142300.fountain
You can also start recording automatically at launch:
harvey --record # auto-named timestamped file
harvey --record-file mysession.fountain # explicit pathThere are two ways to use a recorded .fountain file in a
later session:
Continue — loads the Fountain file’s conversation history into context and drops you into the interactive REPL, so you can pick up exactly where you left off:
harvey --continue mysession.fountainOr from inside a running Harvey session:
harvey > /session continue mysession.fountain
Replay — re-sends every user turn from the Fountain file to the currently connected model and records the fresh responses to a new file. Useful for re-running a session against a different model:
harvey --replay mysession.fountain
harvey --replay mysession.fountain --replay-output newresponses.fountainOr from inside a running Harvey session:
harvey > /session replay mysession.fountain
harvey > /session replay mysession.fountain newresponses.fountain
| Startup flag | Description |
|---|---|
--continue FILE |
Load history from FILE and open the REPL |
--replay FILE |
Re-run all turns from FILE against the current model |
--replay-output FILE |
Write replay responses to FILE (default: auto-named) |
harvey > /files
harvey > /files internal/parser
Lists files in the workspace root or a subdirectory. Useful for
discovering paths before using /read or
/write.
harvey > /run go test ./...
harvey > /read internal/parser/parser_test.go internal/parser/parser.go
harvey > The TestParseExpr test is failing on empty input. Please fix parser.go.
harvey > /apply
harvey > /run go test ./internal/parser/...
harvey > /files
harvey > /search "type.*interface"
harvey > /read harvey/harvey.go
harvey > Can you summarize the LLMClient interface and explain how the two backends differ?
harvey > /git diff HEAD
harvey > /git status
harvey > Please write a conventional commit message for these changes.
harvey > /write .git/COMMIT_EDITMSG
harvey > /context add We are migrating from the old auth middleware to the new JWT-based one.
harvey > /context add All session tokens must be stored as httpOnly cookies, not localStorage.
harvey > /read internal/auth/middleware.go
harvey > ...several turns of back and forth...
harvey > /summarize
harvey > /read internal/auth/jwt.go
harvey > Continue the migration — here is the JWT helper...
harvey > /kb project add myapp "Main web application"
harvey > ...discussion about database choice...
harvey > /kb observe decision Use SQLite with WAL mode; Postgres is overkill for this deployment
harvey > /kb observe question How do we handle schema migrations across versions?
harvey > /kb status