https://github.com/sandst1/nav
a minimalistic coding agent
https://github.com/sandst1/nav
Last synced: about 2 months ago
JSON representation
a minimalistic coding agent
- Host: GitHub
- URL: https://github.com/sandst1/nav
- Owner: sandst1
- License: mit
- Created: 2026-02-13T22:33:48.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-03-26T10:44:40.000Z (3 months ago)
- Last Synced: 2026-03-27T03:57:18.765Z (3 months ago)
- Language: TypeScript
- Homepage: https://sandst1.github.io/nav/
- Size: 2.45 MB
- Stars: 7
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# nav
Minimalist coding agent with hashline-based editing.
**Built for [Bun](https://bun.sh)** — leverages Bun's native APIs for optimal performance.
See [CHANGELOG.md](CHANGELOG.md) for release history.
## Installation
### Quick install (recommended)
```bash
curl -fsSL https://raw.githubusercontent.com/sandst1/nav/main/install.sh | bash
```
Or with wget:
```bash
wget -qO- https://raw.githubusercontent.com/sandst1/nav/main/install.sh | bash
```
This installs the latest binary to `~/.local/bin/nav`. To install to a different location:
```bash
NAV_INSTALL_DIR=/usr/local/bin bash -c "$(curl -fsSL https://raw.githubusercontent.com/sandst1/nav/main/install.sh)"
```
### Manual download
Download the latest binary for your platform from [GitHub Releases](https://github.com/sandst1/nav/releases):
- **macOS (Apple Silicon)**: `nav-darwin-arm64.tar.gz`
- **macOS (Intel)**: `nav-darwin-x64.tar.gz`
- **Linux (x64)**: `nav-linux-x64.tar.gz`
- **Linux (ARM64)**: `nav-linux-arm64.tar.gz`
- **Windows (x64)**: `nav-windows-x64.zip`
Extract and move to a directory in your PATH:
```bash
# macOS/Linux example
tar -xzf nav-darwin-arm64.tar.gz
mv nav-darwin-arm64 /usr/local/bin/nav
chmod +x /usr/local/bin/nav
```
## Development Setup
Requires [Bun](https://bun.sh) runtime (1.0+).
```bash
# Install Bun first (if needed)
curl -fsSL https://bun.sh/install | bash
```
```bash
# Install dependencies
bun install
# Run directly
bun run src/index.ts
# Or link globally so `nav` is available everywhere
bun link
```
## Configuration
The recommended way to configure nav is with a **config file**. Create one per-project or as a user-level default:
```bash
nav config-init # creates .nav/nav.config.json in the current directory
```
Or create the file manually at `~/.config/nav/nav.config.json` for a global default.
Priority order: **CLI flags > environment variables > project config > user config > defaults**.
Optional **`editMode`** in the config file: default **`hashline`** (LINE:HASH line prefixes and anchor-based `edit` tool). Set **`"editMode": "searchReplace"`** for plain file contents from `read`/`skim`/`filegrep` and `edit` with `old_string` / `new_string` / optional `replace_all`. See [website/guide/configuration.md](website/guide/configuration.md) for the full key list.
### Provider configs
Pick your provider and save as `.nav/nav.config.json` or `~/.config/nav/nav.config.json`. Each example is a complete, ready-to-use config file.
**OpenAI:**
```json
{
"provider": "openai",
"model": "gpt-4.1",
"apiKey": "sk-...",
"contextWindow": 1047576,
"handoverThreshold": 0.8,
"verbose": false,
"sandbox": false
}
```
**Anthropic:**
```json
{
"provider": "anthropic",
"model": "claude-sonnet-4-20250514",
"apiKey": "sk-ant-...",
"contextWindow": 200000,
"handoverThreshold": 0.8,
"verbose": false,
"sandbox": false
}
```
**Google Gemini:**
```json
{
"provider": "google",
"model": "gemini-2.5-flash",
"apiKey": "...",
"contextWindow": 1048576,
"handoverThreshold": 0.8,
"verbose": false,
"sandbox": false
}
```
**Azure OpenAI** (set `model` to your deployment name):
```json
{
"provider": "azure",
"model": "my-gpt4o-deployment",
"baseUrl": "https://my-resource.openai.azure.com/openai/v1",
"apiKey": "...",
"contextWindow": 128000,
"handoverThreshold": 0.8,
"verbose": false,
"sandbox": false
}
```
**Ollama** (no API key needed, context window queried automatically):
```json
{
"provider": "ollama",
"model": "llama3",
"handoverThreshold": 0.8,
"verbose": false,
"sandbox": false
}
```
**LM Studio** (set `contextWindow` manually):
```json
{
"provider": "openai",
"model": "local-model",
"baseUrl": "http://localhost:1234/v1",
"contextWindow": 32768,
"handoverThreshold": 0.8,
"verbose": false,
"sandbox": false
}
```
**OpenRouter:**
```json
{
"provider": "openai",
"model": "google/gemini-2.5-flash",
"baseUrl": "https://openrouter.ai/api/v1",
"apiKey": "or-...",
"contextWindow": 1048576,
"handoverThreshold": 0.8,
"verbose": false,
"sandbox": false
}
```
### All config keys
| Key | Default | Description |
|-----|---------|-------------|
| `provider` | `openai` | `openai`, `anthropic`, `google`, `ollama`, or `azure` |
| `model` | `gpt-4.1` | Model name |
| `apiKey` | — | API key for the provider |
| `baseUrl` | — | API base URL (for Azure, Ollama remote, LM Studio, OpenRouter) |
| `verbose` | `false` | Show diffs, token counts, timing |
| `sandbox` | `false` | Enable macOS Seatbelt sandboxing |
| `contextWindow` | auto-detected | Context window size in tokens |
| `handoverThreshold` | `0.8` | Auto-handover at this fraction of context (0–1) |
| `theme` | `nordic` | Color theme (`nordic` or `classic`) |
| `hooks` | — | Optional lifecycle hooks (`stop`, `taskDone`, `planDone`) — see [Hooks](https://sandst1.github.io/nav/guide/hooks.html) |
| `hookTimeoutMs` | `600000` | Max wall time per shell hook step (default 10 minutes). Override with `NAV_HOOK_TIMEOUT_MS` |
| `taskImplementationMaxAttempts` | `3` | Max full work+verify cycles per task in `/tasks run` / `/plans run`; loop stops if still failing. `NAV_TASK_IMPLEMENTATION_MAX_ATTEMPTS` |
### Hooks
Run shell commands (and optional custom slash commands) at fixed points: after each agent turn (`stop`), before a task is marked done (`taskDone`), and when every task in a plan is done (`planDone`). `taskDone` and `planDone` support `maxAttempts`: on failure the hook output is sent back to the model in the **same** conversation so it can fix issues before retrying.
See the full reference: [Hooks guide](https://sandst1.github.io/nav/guide/hooks.html).
### CLI flags and environment variables
For one-off overrides, CLI flags and environment variables take precedence over config files.
| Env var | CLI flag | Description |
|---------|----------|-------------|
| `NAV_MODEL` | `-m, --model` | Model name |
| `NAV_PROVIDER` | `-p, --provider` | Provider |
| `NAV_BASE_URL` | `-b, --base-url` | API base URL |
| `NAV_SANDBOX` | `-s, --sandbox` | Enable sandbox (macOS only) |
| `NAV_UI_HOST` | `--ui-host` | Host for `ui-server` mode |
| `NAV_UI_PORT` | `--ui-port` | Port for `ui-server` mode |
| `NAV_CONTEXT_WINDOW` | — | Context window size in tokens |
| `NAV_HANDOVER_THRESHOLD` | — | Auto-handover threshold (0–1) |
| `NAV_THEME` | — | Color theme |
| `NAV_HOOK_TIMEOUT_MS` | — | Shell hook step timeout in milliseconds |
| `NAV_TASK_IMPLEMENTATION_MAX_ATTEMPTS` | — | Max work+verify cycles per task in task/plan runs (default: 3) |
| — | `-v, --verbose` | Show diffs, tokens, timing |
## Usage
```bash
# Interactive mode
nav
# One-shot mode
nav "fix the type error in src/app.ts"
# With a specific model
nav -m claude-sonnet-4-20250514 "add error handling to the API routes"
# Google Gemini
nav -m gemini-2.5-flash "refactor the auth module"
# Verbose mode (shows full diffs, token counts, timing)
nav -v "refactor the auth module"
# Start websocket/http backend for UI clients
nav ui-server --ui-port 7777
# Reference files with @ — their contents are included in the prompt
nav "explain @src/auth.ts and refactor the error handling"
```
## UI Server Mode
`nav ui-server` exposes an optional local API transport while keeping terminal mode unchanged as the default.
- HTTP health endpoint: `/health`
- WebSocket endpoint: `/ws`
- Protocol docs: [`docs/ui-server-protocol.md`](docs/ui-server-protocol.md)
This mode is designed for external UIs that want to stream assistant/tool events while still using nav's existing core behavior.
## Commands
Type these in interactive mode:
- `/clear` — clear conversation history
- `/model [name]` — show or switch the current model
- `/handover [prompt]` — summarize progress and continue in a fresh context
- `/init` — generate an `AGENTS.md` for the current project
- `/plan` — enter plan mode: discuss an idea, then save a named plan
- `/plans` — list all plans with task status summary
- `/plans split ` — generate implementation + test tasks from a plan
- `/plans microsplit ` — generate micro-tasks optimized for small LLMs
- `/plans run ` — work through all tasks belonging to a plan
- `/tasks` — list planned and in-progress tasks
- `/tasks add ` — add a new task (agent drafts name/description for confirmation)
- `/tasks run [id]` — work on a specific task, or pick the next planned one automatically
- `/tasks rm ` — remove a task
- `/skills` — list available skills
- `/create-skill` — create a new skill interactively
- `/help` — list available commands
Typing `/` shows all available commands. As you continue typing, the list filters in real-time. Press **Tab** to autocomplete when there's a single match.
### Custom commands
You can create custom slash commands by adding markdown files:
| Location | Scope |
|----------|-------|
| `.nav/commands/*.md` | Project-level (takes precedence) |
| `~/.config/nav/commands/*.md` | User-level |
The filename (minus `.md`) becomes the command name. The markdown content is sent to the agent as a prompt. For example, `.nav/commands/review.md`:
```markdown
Review the code I've changed. Focus on correctness, edge cases, and readability.
Check for common bugs and suggest improvements.
```
Then use it with `/review`. You can pass arguments too — use `{input}` as a placeholder:
```markdown
Review the following file for issues: {input}
```
```
> /review src/auth.ts
```
Custom commands appear in `/help` and in the autocomplete suggestions.
### Skills
Skills are reusable agent capabilities defined in `SKILL.md` files. They provide specialized knowledge or workflows that nav can use automatically based on the skill's description.
| Location | Scope |
|----------|-------|
| `.nav/skills//SKILL.md` | Project-level (takes precedence) |
| `.claude/skills//SKILL.md` | Project-level (Claude compatibility) |
| `~/.config/nav/skills//SKILL.md` | User-level |
Each skill lives in its own directory and has a `SKILL.md` file with YAML frontmatter:
```markdown
---
name: docx-creator
description: "Use this skill when the user wants to create Word documents (.docx files)"
---
# Word Document Creator
## Overview
This skill creates .docx files using...
## Instructions
1. Install the required package...
2. Use the following template...
```
The `description` field tells nav when to use the skill. Write it as a trigger condition, not just what the skill does.
**Commands:**
- `/skills` — list all available skills
- `/create-skill` — interactively create a new skill
Skills are automatically detected and injected into the system prompt. When nav sees a task matching a skill's description, it uses that skill's instructions.
### Plans & Tasks
nav has a two-level planning system: **plans** capture the high-level design, **tasks** are the concrete units of work.
#### Plans
Plans are stored in `.nav/plans.json`. Start a plan with `/plan`:
```
> /plan add dark mode to the settings screen
```
nav enters plan mode — it discusses the idea with you, asking one clarifying question at a time. When the plan is ready, it produces a summary and asks you to confirm:
```
[y]es to save plan, type feedback to refine, [a]bandon
> y
Plan #1 saved: Dark mode settings
Use /plans split 1 to generate implementation tasks.
```
Once saved, split it into tasks:
```
> /plans split 1
```
The agent reads the plan, explores the codebase, then creates ordered implementation tasks **and** test-writing tasks. Tasks are saved with IDs like `1-1`, `1-2`, etc. (the prefix is the plan ID).
To work through all tasks in a plan:
```
> /plans run 1
Working plan #1: Dark mode settings
Working on task #1-1: Add theme state to settings store
...
```
List all plans with a status summary:
```
> /plans
Plans:
#1 Dark mode settings [0/5 done, 5 planned]
```
#### Standalone tasks
Tasks without a plan use IDs like `0-1`, `0-2`, etc.
```
> /tasks add implement rate limiting for the API
```
The agent drafts a name and description, shows a preview, and asks for confirmation. Reply `y` to save, `n` (optionally with more instructions) to revise, or `a` to abandon.
```
> /tasks
Tasks:
#0-1 [planned ] Rate limiting
Add token-bucket rate limiting to the API middleware
> /tasks run 0-1
Working on task #0-1: Rate limiting
...
Task #0-1 marked as done.
> /tasks run # picks the next planned task automatically (all tasks)
```
Tasks cycle through three statuses: `planned` → `in_progress` → `done`. When working plan-linked tasks, the plan's description and approach are included in the agent's context alongside the status of all sibling tasks.
### Handover
For long tasks, `/handover` lets you reset context without losing track of progress. The model summarizes what it's done, the conversation is cleared, and a fresh context starts with the summary, a current file tree, and any instructions you provide:
```
> /handover now write tests for the auth module
```
This is useful when context is getting long and you want to refocus the model on the next phase of work.
### Auto-handover
nav can automatically trigger a handover when the conversation approaches the model's context window limit. This prevents context overflow errors and keeps the model working effectively.
Context window sizes are auto-detected for most providers. For LM Studio or custom endpoints, set `contextWindow` in your config file. You can also adjust the handover threshold:
```json
{
"contextWindow": 32768,
"handoverThreshold": 0.9
}
```
When the threshold is reached mid-task, the agent completes its current step, generates a summary, and continues in a fresh context. If it's reached after the model finishes responding, the auto-handover triggers on the next user message. In verbose mode (`-v`), each response shows context utilization: `tokens: 45.2k in / 1.2k out (3.1s) (35% of 128k ctx)`.
## Keyboard Shortcuts
- **ESC** — stop the current agent execution and return to prompt
- **Ctrl-D** — exit nav
- Type while the agent is working to queue a follow-up message
## Sandboxing
> **By default, nav runs without any sandbox.** Shell commands the agent executes have full access to your system — it can read, write, and delete files anywhere your user account can. This is the fastest way to work, but it means a confused or misbehaving model can cause real damage.
Enable sandboxing to restrict what the agent can do — either via CLI flag or config file:
```bash
nav -s "task"
```
```json
{ "sandbox": true }
```
The sandbox uses **macOS Seatbelt** (`sandbox-exec`) and is **macOS only** for now. On other platforms, `-s` will exit with an error.
When enabled, all processes spawned by nav (including shell commands) inherit these restrictions:
- **File writes** are limited to the current project directory, temp, and cache directories. Writes anywhere else are denied by the kernel.
- **File reads** are unrestricted — the agent can still read your whole filesystem.
- **Network** is unrestricted — needed for LLM API calls.
The Seatbelt profile lives in `sandbox/nav-permissive.sb` and can be customized.
## How it works
nav has 7 tools:
- **read** — reads files with hashline-prefixed output: `LINE:HASH|content`
- **edit** — edits files by referencing `LINE:HASH` anchors from read output
- **write** — creates new files
- **skim** — read a specific line range with hashline output (no shell needed)
- **filegrep** — search within a file with context lines and hashline output
- **shell** — runs shell commands
- **shell_status** — check on background processes
The hashline format (inspired by [can.ac/the-harness-problem](https://blog.can.ac/2026/02/12/the-harness-problem/)) gives each line a short content hash. When the model edits, it references lines by `LINE:HASH` instead of reproducing old content. If the file changed since the last read, hashes won't match and the edit is rejected with corrected hashes shown — so the model can retry without re-reading.
## AGENTS.md
If an `AGENTS.md` file exists in the working directory, its content is automatically included in the system prompt. This is the standard way to give nav project-specific instructions.
## Logs
Session logs are written to `.nav/logs/` as JSONL files. Each line captures a message, tool call, or result with timestamps — useful for debugging and replay.
## Development
```bash
# Type check
bunx tsc --noEmit
# Run with watch mode
bun run --watch src/index.ts
```