# Getting Started with Open Walnut Open Walnut is a Personal AI that manages tasks, accumulates knowledge, and coordinates Claude Code sessions from one web UI. This guide walks you through installation, configuration, and your first productive session. > **Time estimate**: 5 minutes for the fast track (plus one-time model downloads on first start: ~640 MB for search, and 1 to 2.5 GB for voice on Apple silicon and Linux, both in the background), 10 minutes for the full walkthrough. --- ## Fast Track (5 Minutes + Model Download) Walnut carries everything it runs on, and one command installs it on macOS 12+ or Linux (glibc 2.28+) with nothing installed first (no Node, no npm). Alpine (musl) builds start with the release after 0.6.19; there, run `apk add libstdc++ libgcc` first: the musl build links the C++ runtime. ```bash # 1. Install Claude Code (every Walnut agent runs on it) curl -fsSL https://claude.ai/install.sh | bash claude # run it once and sign in # 2. Install Walnut (nothing to build) curl -fsSL https://github.com/EvanZhang008/open-walnut/releases/latest/download/install.sh | sh # 3. Start (a new terminal can run plain `walnut web`) ~/.local/bin/walnut web # starts on port 3456 ``` When `~/.local/bin` is not on your PATH yet (it is not by default on macOS), the script adds it in your shell's rc file and says which one, so the next terminal you open finds `walnut`; the one you installed from keeps its old PATH. `OPEN_WALNUT_MODIFY_PATH=0` leaves your rc files alone. No API key is needed: Walnut runs every AI turn through the Claude Code you signed in to in step 1. Homebrew works too (`brew install evanzhang008/tap/open-walnut`), and so does npm for people who use it: `npm install -g open-walnut` (Node 22 or newer), then `open-walnut web`. Working on Walnut itself? Swap step 2 for a checkout, installed with [Bun](https://bun.sh) (`curl -fsSL https://bun.sh/install | bash`), and start with `bun run start`, which builds the server and the frontend first: ```bash git clone https://github.com/EvanZhang008/open-walnut.git cd open-walnut bun install bun run start ``` Open [http://localhost:3456](http://localhost:3456), type "hello" to the CEO, and it should reply. You're done! > **First start is slower**: The Qwen3-Embedding-0.6B embedding model (~640 MB) downloads automatically on first launch. This is a one-time download that can take 5-30 minutes depending on your connection. The server starts and is usable while the download happens in the background. > **Not working?** See [Troubleshooting](#troubleshooting) below, or continue reading for the full setup. --- ## Table of Contents - [Fast Track (5 Minutes + Model Download)](#fast-track-5-minutes--model-download) - [Prerequisites](#prerequisites) - [Installation](#installation) - [Provider Configuration](#provider-configuration-optional) - [First Run](#first-run) - [Your First Tasks](#your-first-tasks) - [Starting Sessions (Claude Code)](#starting-sessions-claude-code) - [Chatting with the Agent](#chatting-with-the-agent) - [Memory System](#memory-system) - [Search & Embedding Setup](#search--embedding-setup) - [Intermediate: Cron, Skills, Commands](#intermediate-cron-skills-commands) - [Advanced: SSH, Integrations, Plugins](#advanced-ssh-integrations-plugins) - [CLI Quick Reference](#cli-quick-reference) - [Troubleshooting](#troubleshooting) - [What's Next](#whats-next) --- ## Prerequisites ### Required | Dependency | Version | How to install | Why | |---|---|---|---| | **Claude Code CLI** | Latest | `curl -fsSL https://claude.ai/install.sh \| bash`, then run `claude` once to sign in | Every AI turn is a `claude` session: the home page chat, coding sessions, scheduled routines, and the agents a session hook dispatches | | **Disk space** | ~5 GB free | None | For Walnut itself (~1 GB), the embedding model (~640 MB), the search index, and on Apple silicon and Linux the voice model (1 to 2.5 GB) | ### Optional | Dependency | How to install | Why | Without it | |---|---|---|---| | **Git** | macOS: `xcode-select --install`, Ubuntu: `sudo apt install git`, or [git-scm.com](https://git-scm.com/) | Cloning the repo, and auto-backup of `~/.open-walnut/` every 30 seconds | An installed Walnut needs no git. Data is still saved locally, just not version-controlled. | | **Provider key** | See [Provider Configuration](#provider-configuration-optional) | Sends the small background single calls (session titles, summaries, voice cleanup, quick parses) to Anthropic, Bedrock, or another provider instead of your Claude Code login | Those calls go through Claude Code, which is the default. Chat, coding sessions, and routines never read a key. | | **Bun** | [bun.sh](https://bun.sh/) (1.4.4 or newer) | Required for a checkout, and the only thing it needs besides git: it installs the dependencies (`bun install`), runs the build and the tests, and builds the prebuilt session-daemon binaries (faster deploys to remote hosts) and the ACP worker bundle | Not needed by an installed Walnut, which carries all of that inside. | ### One AI login: Claude Code Open Walnut uses AI in three places, and by default all three run on the Claude Code you already have. Every AI turn with tools is a `claude` session; Walnut has no agent loop of its own. | What | How it runs | How to authenticate | |---|---|---| | **Quick question** (the Walnut agent, on the home page) | A long-lived `claude` session | Run `claude` once in your terminal and sign in | | **Coding sessions, routines, hook-dispatched agents** | Separate `claude` CLI processes | Same login | | **Background single calls** (titles, summaries, voice cleanup, quick parses) | One short model call each, no tools | Same login by default, or a provider key | Claude Code brings its own login, whatever kind it is: an Anthropic account, Bedrock (`CLAUDE_CODE_USE_BEDROCK=1` in `~/.claude/settings.json`), or Vertex. Walnut inherits it as-is and never asks for a key of its own. The first-run banner names what it found ("Walnut runs on your Claude Code, signed in with Bedrock (us-west-2)"). Settings › Advanced › API provider is the one place this is chosen, and it means exactly one thing: where the background single calls go. Quick questions and every other session run on the default engine (Claude Code unless you pick another, see Path D) no matter what you pick here, so choosing a provider never changes who answers you in the chat: it changes which model writes your session titles, the compaction summaries, the voice transcript cleanup, and the small parses behind the draft forms. The paths below set up such a provider. --- ## Installation ```bash git clone https://github.com/EvanZhang008/open-walnut.git cd open-walnut bun install ``` This installs both backend and frontend dependencies, at the exact versions in `bun.lock` and `web/bun.lock`. The first `bun run start` will build everything automatically. ### Updating and rolling back This layout starts with the release after 0.6.19. The 0.6.19 installer unpacks Walnut, with its own Node, into `~/.local/share/open-walnut/app`; running it again replaces that folder, and `walnut web` updates the install in place. From the next release on, the one-line installer keeps each version in its own folder and one link to the version in use: ```text ~/.local/share/open-walnut/versions//walnut ~/.local/share/open-walnut/bin/walnut -> ../versions//walnut ~/.local/bin/walnut -> ~/.local/share/open-walnut/bin/walnut ``` `walnut web` installs a newer published version into its own folder before it starts, checks it against the integrity npm publishes for it and runs its `--version`, and only then moves the link, so a failed update leaves the old version in use. A download that takes longer than a minute is left for later (`walnut update` takes as long as it needs), and Ctrl-C while it runs cleans up and starts nothing. `walnut update` does the same at once, and running `install.sh` again does it from the GitHub release. The version before stays, and so does any version a running server was started from; older ones are removed. When a new version exits before it starts serving, Walnut goes back on its own: the link points at the version before again, that version starts, and the new one is skipped (marked in `~/.local/share/open-walnut/bad/`) until a newer release is published. To go back by hand, point the link at the version before and restart: ```bash ls ~/.local/share/open-walnut/versions ln -sfn ../versions//walnut ~/.local/share/open-walnut/bin/walnut ``` Remote hosts keep the session daemon the newer version started there. Version 0.6.17 and older stop a host's daemon with `ps`, so on a host without `ps` (slim container images have no procps) the older version cannot replace it, and every connect to that host fails with "Daemon shutdown was not confirmed; no replacement was started". Stop the daemon on that host once and the older version starts its own on the next connect; running sessions keep running: ```bash /tmp/open-walnut/daemon-linux-x64 --stop # daemon-linux-arm64 on an ARM host node /tmp/open-walnut/daemon.cjs --stop # one that runs under Node (bun: the same with bun) ``` A daemon that runs from `~/.cache/open-walnut` (used when `/tmp` cannot hold it) stops the same way from there. Installing procps on the host works too. While a newer version is still installed beside the one linked, starting Walnut does not install it again: it updates once a release newer than that one is published. `walnut update` installs the newest one whenever you ask. An npm install updates through npm the same way (`npm install -g open-walnut@` goes back; turn updates on start off to stay there), Homebrew with `brew upgrade open-walnut`. Going back to a release from before the single file (`OPEN_WALNUT_VERSION=0.6.17` with the install command) installs its Node archive, which updates itself on its next start the way an npm install does: set `updates.auto: false` in `config.yaml` in your data folder (`~/.open-walnut`, or `OPEN_WALNUT_HOME`) to stay on it, and remove it again when you want updates (install.sh says so). --- ## Provider Configuration (optional) With Claude Code installed there is nothing to configure: it is the default provider for the background single calls too. The paths below are for sending those calls to a provider directly instead (Settings › Advanced › API provider, or `agent.main_provider` in `~/.open-walnut/config.yaml`). Choose **one**. ### Path A: Anthropic API key One environment variable and you're done. 1. Get an API key from [console.anthropic.com](https://console.anthropic.com/) 2. Export it: ```bash export ANTHROPIC_API_KEY=sk-ant-api03-... ``` That's it. Open Walnut auto-detects the key and configures itself. > **Tip**: Add the export to your `~/.zshrc` or `~/.bashrc` so it persists across terminal sessions. ### Path B: AWS Bedrock If you have an AWS account with Claude model access enabled: 1. Ensure your AWS credentials are configured (via `~/.aws/credentials`, `AWS_PROFILE`, or IAM role) 2. Enable Claude model access in the [Bedrock console](https://console.aws.amazon.com/bedrock/) for your region 3. Either set the region via environment variable or config: ```bash export AWS_REGION=us-west-2 ``` Or add to `~/.open-walnut/config.yaml`: ```yaml providers: bedrock: api: bedrock region: us-west-2 ``` ### Path C: Other Providers Open Walnut supports additional providers via the `config.yaml` providers section. Add a provider entry with the appropriate `api` protocol: ```yaml providers: my-provider: api: anthropic-messages # Protocol: anthropic-messages, openai-chat, bedrock, google-generative-ai, or ollama api_key: ${env:MY_API_KEY} # Can reference environment variables base_url: https://api.example.com # Custom endpoint (optional) ``` ### Path D: Models on your own machine (Ollama, LM Studio, vLLM) Walnut can run with no cloud model and no Claude Code. You need three things: a model that can code, Walnut's own calls pointed at it, and a coding engine that talks to it. **1. Pick a model that can code.** Sessions edit files and run commands through tool calls. Each pair below passed our test (fix a bug in a small Python project, run its tests, report back, write the task note): | Model | Download | Free memory | Engines it passed on | |---|---|---|---| | `gpt-oss:20b` | 13 GB | about 16 GB | OpenCode 1 and 2, Goose | | `qwen3-coder:30b` | 19 GB | about 20 GB | OpenCode 1 and 2, Goose, Codex, custom (Qwen Code) | Codex and Qwen Code need `qwen3-coder:30b`: with `gpt-oss:20b`, Codex's commands come back as tool calls Ollama cannot read and it gives up, and Qwen Code finished only one of two runs. A 7B or 8B model is too small on every engine: `qwen3:8b` broke an edit and then reported success. Start Ollama with room for Walnut's instructions and tools, then pull the model: ```bash OLLAMA_CONTEXT_LENGTH=32768 ollama serve # the Ollama app has the same setting (Context length) ollama pull gpt-oss:20b ``` **2. Walnut's own model calls** (titles, summaries, routine watchers, AI task search) go to the main provider. In `~/.open-walnut/config.yaml`: ```yaml agent: main_provider: ollama main_model: gpt-oss:20b # a model you pulled providers: ollama: api: ollama base_url: http://localhost:11434 # the default; set it for another machine ``` Leave `fast_model` out: quick background calls then use `main_model`. Do not set it to a smaller model to save time; an 8B model gets those calls wrong too. A `fast_model` the server does not have falls back to `main_model`, and the server log says so once. An OpenAI-compatible server (LM Studio, vLLM, llama.cpp) works the same way as `api: openai-chat` with its `base_url` (for example `http://localhost:1234/v1`) and no key. **3. A coding engine.** Coding sessions, the CEO and leads run on the default engine. Install one, point it at your model, check it once on its own, then make it the default: ```yaml defaults: engine: opencode # or goose, codex, custom ``` **OpenCode.** `curl -fsSL https://opencode.ai/install | bash` (or `npm install -g opencode-ai`) installs OpenCode 1; `curl -fsSL https://opencode.ai/v2/install | bash` (or `brew install opencode`) installs OpenCode 2. Put this in `~/.config/opencode/opencode.json` (the model id after `ollama/` is the name `ollama list` shows), then check it with `opencode run "say hi"`: ```json { "$schema": "https://opencode.ai/config.json", "provider": { "ollama": { "npm": "@ai-sdk/openai-compatible", "name": "Ollama", "options": { "baseURL": "http://localhost:11434/v1" }, "models": { "gpt-oss:20b": { "name": "gpt-oss 20B" } } } }, "model": "ollama/gpt-oss:20b" } ``` **Goose.** `curl -fsSL https://github.com/block/goose/releases/download/stable/download_cli.sh | CONFIGURE=false bash` (or `brew install block-goose-cli`). Put this in `~/.config/goose/config.yaml`, then check it with `goose run -t "say hi"`: ```yaml GOOSE_PROVIDER: ollama GOOSE_MODEL: gpt-oss:20b OLLAMA_HOST: localhost ``` **Codex.** `npm install -g @openai/codex` (or `brew install --cask codex`). Put this in `~/.codex/config.toml`, then check it with `codex exec --skip-git-repo-check "say hi"`. It needs no OpenAI sign-in, but it still connects to OpenAI's servers, which a work laptop's security software may flag: ```toml model = "qwen3-coder:30b" model_provider = "ollama" ``` **Any other ACP agent** runs as the `custom` engine: give Walnut the command that starts it in ACP mode. For example [Qwen Code](https://github.com/QwenLM/qwen-code) (`npm install -g @qwen-code/qwen-code`): ```yaml defaults: engine: custom engines: custom: adapter_cmd: [qwen, --acp, --auth-type, openai, --openai-base-url, "http://localhost:11434/v1", --openai-api-key, ollama, -m, qwen3-coder:30b] ``` Qwen Code asks before it runs a command; answer in the session panel, or add `--yolo` to let it run them. Tested with OpenCode 1.19.5 and 2.2.5, Goose 1.57.0, Codex 0.166.0 and Qwen Code 0.26.5, on Ollama 0.32.14 (an M4 Pro Mac with 48 GB) and 0.40.13 (Linux). Check an engine's version with its `--version`. Walnut gives every engine its session instructions (role, team rules, task directory): Codex as developer instructions, the others on the first message of each conversation. On every engine but Codex it lists only its most used tools, plus `tool_help` and `tool_call`, which reach every other one (`session.acp_walnut_tools: all` lists them all). With these set, nothing asks you to install Claude Code: not the installer, not the home page, not the server log. If your model server stops answering, the home page says so instead and clears by itself once the server is back. Search and voice input already run on your machine by default. ### Verify Your Setup After starting the server (`bun run start` in a checkout, `open-walnut web` otherwise), open [http://localhost:3456](http://localhost:3456) and go to Settings › Advanced › API provider, then press Test connection on the provider you configured. A green result means the key, region, and model are all usable. For Ollama it sends one short request to your `main_model` (the first call may take a while as Ollama loads the model) and says when the server is not running or does not have that model. The home page chat is not the test for this: it runs on the default engine. --- ## First Run ```bash bun run start ``` On first launch, Open Walnut: 1. **Creates `~/.open-walnut/`** — the data directory with all your tasks, memory, and config 2. **Seeds `config.yaml`** — with default model settings and available models 3. **Initializes directories** — `tasks/`, `memory/`, `sessions/`, and more 4. **Builds the frontend** — compiles the React SPA (takes ~10 seconds the first time) 5. **Downloads the embedding model** — Qwen3-Embedding-0.6B (~640 MB) for semantic search. This is a one-time download that can take 5-30 minutes depending on your connection. The server is usable while the download runs in the background. 6. **Starts the server** on [http://localhost:3456](http://localhost:3456) The data directory structure: ``` ~/.open-walnut/ config.yaml # Your configuration MEMORY.md # Global memory (agent reads/writes this) sessions.json # Session registry chat-history.json # Persistent chat history cron-jobs.json # Scheduled jobs usage.sqlite # Usage tracking search.sqlite # Search index over everything (keyword + vector) models/ # Embedding model weights, fetched on first use tasks/ tasks.json # Task database archive/ # Completed task archives memory/ daily/ # Daily activity logs projects/ # Per-project memory sessions/ # Session summaries topics/ # Topic-based memory repos/ # Per-repo memory compaction/ # Working memory snapshots working-memory.md # Real-time conversation scratchpad index.md # Memory index notes/ # User notes and instructions recordings/ # Audio recordings and transcripts timeline/ # Timeline events repositories/ # Tracked repository metadata skills/ # User-installed skills commands/ # Custom slash commands ``` > **All data is local** — plain JSON, YAML, Markdown, and SQLite files. No cloud database. --- ## Your First Tasks Tasks are the core of Open Walnut. Everything revolves around them — sessions are attached to tasks, memory is organized by project, and the agent understands your task context. ### Task Hierarchy ``` Category → Project → Task → Subtask ``` For example: `Work → HomeLab → "Set up monitoring dashboard" → "Install Grafana"` ### Three Ways to Create Tasks #### 1. Chat with the Agent (Easiest) Just tell the agent what you need in the chat: > "I need to file my taxes before Friday. High priority." The agent creates the category, project, and task automatically — with priority, due date, and all metadata. #### 2. Web UI Click the **+** button in the Todo panel on the right side of the home page. Fill in the title, select a category and project, set priority, and save. #### 3. CLI ```bash walnut add "Set up monitoring" -c Work -l HomeLab -p immediate ``` Where `-c` is category, `-l` is project (label), and `-p` is priority. ### Task Lifecycle Tasks move through phases automatically (simplified — see [README](README.md) for the full 7-phase lifecycle): ``` TODO → IN_PROGRESS → ... → NEED_ACTION → ... → COMPLETE ``` When the AI finishes its work, the task moves to `NEED_ACTION`. Only you mark it `COMPLETE`. The AI never closes tasks without your approval. ### Retiring Finished Pins New tasks are pinned to the board, and completing one does not unpin it, so work you just finished stays visible in its tier. A completed pin then retires on its own three days later. Set the window (or switch retirement off) in `~/.open-walnut/config.yaml`: ```yaml tasks: pin_retirement_days: 3 # days a finished pin stays on the board; 0 or negative keeps pins forever ``` Retirement only unpins. It never deletes a task, never touches an open task however old, and changes no other field. --- ## Starting Sessions (Claude Code) Sessions are where the real coding happens. A session is a Claude Code process attached to a task, running in a specific working directory. ### Prerequisites for Sessions Make sure the Claude Code CLI is installed and signed in: ```bash curl -fsSL https://claude.ai/install.sh | bash # the native build, which needs no Node.js claude --version # Should print a version number claude # Run once to sign in if needed ``` Walnut checks this for you. The setup banner on the home page shows one of three states for this computer, and goes away once nothing is left: | State | What Walnut does | |---|---| | Claude Code is not installed | **Install Claude Code** runs the native installer, then checks again | | Claude Code is older than the configured model needs (Opus 5.5 needs 2.1.280 or newer) | **Update Claude Code** runs `claude update` on a native install, or installs the native build next to an npm one. A Homebrew or other install gets the command to run instead | | Claude Code is not signed in | Shows the `claude` command to run once in a terminal, checks again every 15 seconds, and hides itself when sign-in is done | Signing in is always yours: Walnut only asks Claude Code whether it is signed in (`claude auth status`, which starts no session and makes no model call) and never reads a token. ### Starting a Session #### From Chat Tell the agent to start a session: > "Start a session for the monitoring task in ~/projects/homelab" The agent finds (or creates) the task, spawns a Claude Code session in the specified directory, and shows the session panel. #### Quick Start (`/session` Command) Type `/session` in the chat input to open a path picker. Select a working directory from your recent history, type your prompt, and send. This creates a starred task and starts a session in one step — great for quick coding tasks. #### From the Sessions Page Navigate to `/sessions` in the sidebar. Browse your task tree on the left, select a task, and click "New Session" to start one. ### Session Modes | Mode | Behavior | |---|---| | `plan` | Session produces a plan file for your review before executing | | `default` | Normal interactive mode — the AI works and asks for confirmation on risky actions | | `accept` | The AI auto-accepts file edits but still asks for confirmation on other actions (shell commands, etc.) | | `bypass` | The AI runs without asking for permission (use with caution) | ### Watching Sessions Active sessions stream in real-time. You can: - **Watch live** — see tool calls, outputs, and reasoning as they happen - **Send messages** — interact with the session mid-run - **Switch models** — change between Opus, Sonnet, and Haiku without losing context - **Monitor multiple** — open session panels for different tasks side by side --- ## Chatting with the Agent The home page chat (`/`) is your primary interface. Every conversation there is a `claude` session bound to its own task, so it has Claude Code's full tool belt plus Walnut's own operations, mounted as an MCP server. It can: - **Manage tasks** — create, query, update, complete, and organize tasks - **Search memory** — find information across your notes, daily logs, and session summaries - **Start sessions** — spawn Claude Code sessions for coding tasks - **Run commands** — execute shell commands, read/write files - **Search the web** — look up information online - **Schedule work** — create cron jobs for recurring tasks ### Task Context Click a task in the Todo panel to set it as your **focused task**. The agent sees the task's full context (description, subtasks, notes, project memory) with every message you send. This makes conversations much more productive — no need to re-explain what you're working on. ### Helper Agents A conversation delegates side work to Claude Code's own agents, and the chat shows a chip with how many are running; opening it gives a Background tasks panel with each one's transcript. Your named agents (the `/agents` page) are dispatched by session hooks rather than from the chat: a `run_agent` hook starts a fresh `claude` session carrying that agent's persona, which shows up on the board like any other session. --- ## Memory System Open Walnut accumulates knowledge over time. The more you use it, the smarter it gets. ### How It Works | Layer | Where | What | |---|---|---| | **Global memory** | `~/.open-walnut/MEMORY.md` | User preferences, facts the agent learns | | **Working memory** | `memory/working-memory.md` | Real-time scratchpad for the active conversation (7 structured sections, 12K token budget) | | **Project memory** | `memory/projects/{category}/{project}/MEMORY.md` | Per-project context and decisions | | **Topic memory** | `memory/topics/` | Topic-based knowledge accumulated over time | | **Daily logs** | `memory/daily/YYYY-MM-DD.md` | Timestamped activity records | | **Session summaries** | `memory/sessions/` | Auto-captured when coding sessions end | | **Compaction archive** | `memory/compaction/` | Working memory snapshots saved during context compaction | | **Search index** | `~/.open-walnut/search.sqlite` | Hybrid search (BM25 keyword lanes + vector rescore) | ### Working Memory Working memory is a scratchpad Walnut keeps for the conversation you are in. It has 7 structured sections: Active Focus, User Requests, Decisions & Rationale, Struggles & Breakthroughs, Session Status, Open Threads, and Learnings. Once a chat turn grows past a size threshold, Walnut asks the model for a rewritten file in one call and saves it only when every section is present and inside its budget, so context survives even when conversation history gets compacted. Working memory snapshots are archived to `memory/compaction/` automatically. ### Searching Memory Use the search page (`/search`) or ask the agent: > "What did we decide about the database schema for the auth service?" The agent searches across all memory layers using hybrid keyword + semantic search. --- ## Search & Embedding Setup Search is built in and runs entirely on your machine: one SQLite file (`~/.open-walnut/search.sqlite`) holding a keyword index over tasks, sessions, notes, memory files and skills, plus small quantized vectors used to rescore the keyword candidates. No external services, no separate search process. ### What Happens on First Start Nothing you have to wait for. The keyword half answers immediately (a few milliseconds), and it is a complete search on its own. The embedding model is fetched in the background on first use and cached under `~/.open-walnut/models/`; until it arrives, results are keyword-ranked. Indexing your existing data happens in the background too, paced so it never competes with the UI. Queries mixing Chinese and English work out of the box: the tokenizer splits `camelCase`, `snake_case` and `kebab-case` identifiers into their parts and indexes Chinese as ordered character pairs, so `operator` finds `AcmeEventOperator` and a Chinese phrase matches in order rather than as a bag of characters. ### Choosing an Embedding Model Two models ship as presets, selected with an environment variable before startup: | Preset | Model | Dimensions | Notes | |---|---|---|---| | `qwen3-0.6b` (default) | `onnx-community/Qwen3-Embedding-0.6B-ONNX` | 1024 | Best recall on realistic, wordy queries; strong Chinese + English | | `e5-small` | `Xenova/multilingual-e5-small` | 384 | ~4x faster per query, a third of the index size; slightly weaker recall | ```bash export WALNUT_SEARCH_V2_EMBED_MODEL=e5-small # pick a preset export WALNUT_SEARCH_V2_SEMANTIC=0 # keyword-only, no model at all export WALNUT_DISABLE_SEARCH=1 # do not index anything bun run start ``` Switching presets is safe: the index notices the model changed, drops the stored vectors, and re-embeds in the background. Keyword search keeps working the whole time. **Settings → Search** shows index health, per-kind document counts, and a **Rebuild index** button. --- ## Intermediate: Cron, Skills, Commands ### Cron Jobs Schedule recurring tasks or automated agent actions: ``` You: "Set up a daily standup reminder at 9am Pacific" ``` Or configure via the Settings page. Three schedule types: | Type | Example | Use Case | |---|---|---| | `at` | `2030-01-15T09:00:00` | One-time scheduled event | | `every` | `30m`, `2h`, `1d` | Recurring interval | | `cron` | `0 9 * * 1-5` (+ timezone) | Complex schedules (cron expression) | Each job picks an executor, and every executor that thinks runs a `claude` session: `main-agent` sends the instructions into your Quick question conversation, `walnut-agent` starts a fresh Personal AI session of its own with its own task, and `claude-code` starts a coding session in a directory you name (local or on a remote host). ### Skills Skills are pluggable knowledge modules (`.md` files) that extend the agent's capabilities. They are loaded from these directories (highest priority first): 1. `./skills/` — workspace-local skills (relative to the current working directory) 2. `~/.open-walnut/skills/` — user-installed global skills 3. Built-in skills shipped with Open Walnut 4. `~/.claude/skills/` — Claude Code skills (shared with the CLI) Each skill is a `SKILL.md` file with optional requirements (binaries, environment variables, platform). Only eligible skills appear in the agent's context. ### Slash Commands Create custom slash commands as Markdown files in `~/.open-walnut/commands/`: ``` ~/.open-walnut/commands/ standup.md # /standup — your daily standup template review.md # /review — code review checklist ``` Type `/` in the chat to see available commands. --- ## Advanced: SSH, Integrations, Plugins ### Remote Sessions via SSH Run Claude Code sessions on remote machines: ```yaml # ~/.open-walnut/config.yaml hosts: my-server: hostname: dev.example.com user: myuser # Optional: identity_file, port, shell_setup ``` Walnut connects over SSH, installs its session daemon on the host, and keeps the session running there if SSH drops. The first connect takes about a minute. #### What the remote host needs | On the host | Needed for | If it is missing | |---|---|---| | Passwordless SSH (key or agent) as the configured user | Everything | Connect stops at the SSH step and shows the ssh error | | `curl` and `unzip`, or Bun or Node.js already installed | The session daemon (Walnut installs Bun with `curl -fsSL https://bun.sh/install \| bash` when neither is present) | Connect stops at "Install runtime" and says which command failed | | Claude Code, signed in once | Every session | Walnut installs the native build itself (`curl -fsSL https://claude.ai/install.sh \| bash`). Only when the install cannot run (no network, no `curl` or `wget`) does the host status show the command and why | | Claude Code new enough for the configured model (Opus 5.5 needs 2.1.280 or newer) | Every session on that model | Walnut runs `claude update` on a native install (or installs the native build next to an npm one) and checks again. A Homebrew or other install gets a line with both versions and the command to run | | Claude Code signed in | Every session | Walnut detects it without starting a session and shows `ssh -t claude` to run once, then Check again. Signing in stays yours | | A C compiler (`gcc` or `clang`) | A terminal that survives disconnects, when no prebuilt `dtach` fits the host | Walnut installs `gcc` and the C headers with the host's package manager under `sudo -n` (so only when sudo needs no password), then builds `dtach`. When sudo wants a password the host status shows the exact command, and the terminal opens marked `Not persistent` | Two things worth knowing about Claude Code on a remote host: - Prefer the native build: `curl -fsSL https://claude.ai/install.sh \| bash`. The npm build (`npm install -g @anthropic-ai/claude-code`) is a Node.js script, and many servers have no Node.js on the PATH an SSH command sees. Walnut looks for a working Node.js in nvm, fnm, volta and asdf before giving up, and tells you when it found none. - Walnut does not need Node.js, npm or a compiler on the host for chat, files, diffs or the folder picker. Those run through the daemon, which is a single Bun binary. After the handshake Walnut runs a short preflight on the host (Claude Code present, signed in and new enough, Node.js if the npm build is installed, compiler present) and fixes what it can by itself, one fix at a time, with `Installing ... on ...` in Settings while it runs. Whatever it cannot fix keeps one actionable line with the command and the reason. Nothing is shown when the host is complete. Each fix runs once per host until you press Check again. To turn the fixes off, set `WALNUT_HOST_AUTOFIX=0` for every host, or `autofix: false` under `hosts.` for one. Home directories that are symlinks (`~/workplace -> /workplace`) list and resolve like any other directory. If SSH drops, the session keeps running on the remote host and Walnut reattaches. ### Microsoft To-Do Sync Two-way sync with Microsoft To-Do: 1. Create an Azure AD app registration with To-Do permissions 2. Add to config: ```yaml plugins: ms-todo: enabled: true client_id: YOUR_CLIENT_ID ``` 3. Run `open-walnut auth` to complete OAuth flow Tasks sync bidirectionally — changes in either direction are reflected. ### Git Sync (Auto-Backup) If `~/.open-walnut/` is a git repository, Open Walnut auto-commits changes every 30 seconds. Initialize it: ```bash cd ~/.open-walnut git init git remote add origin git@github.com:you/walnut-data.git ``` The server handles commits, pulls, and pushes automatically. ### External Plugins Install a trusted Plugin by linking it under `~/.open-walnut/plugins//`, or use Settings → Plugin Store with a Git URL or npm spec. A Plugin can add full server logic, a native React App, Settings sections, Tools, Skills, Commands, Hooks, Cron actions, Agents, Providers, routes, and task sync. Installing a Plugin means trusting its code: a server entry runs as full code inside Walnut, and a native web entry runs in the console's own React tree. A Plugin App joins the same App Registry as Walnut's own screens, so one `walnut.ui.app` registration gives it a Sidebar entry, the route `/apps/~`, deep links into every subpath, a badge, and a Command Palette entry. After the author packages reach npm, the whole first loop is a single command. They are not published yet; the Plugin guide gives the equivalent local-checkout command available today. ```bash npx @open-walnut/plugin-cli new my-plugin --dev ``` That scaffolds the project, installs, builds, links it into `~/.open-walnut/plugins/`, asks the running Walnut to load it, prints the App URL, and then rebuilds and reloads on every save. Later loops are `npm run dev` in the project. See [Plugin development](docs/reference/plugin-development.md) for the full guide, and [examples/plugins/walnut-demo](examples/plugins/walnut-demo) (the Walnut Plugin Demo) for a runnable example of every public capability. --- ## CLI Quick Reference ```bash # Server walnut web # Start web UI (port 3456) walnut web --port 8080 # Custom port walnut web --ephemeral # Isolated test server (temp data, random port) # Tasks walnut add "title" -c Category -l Project -p immediate # Create task walnut tasks # List all tasks walnut tasks -s todo -c work # Filter by status and category walnut done # Complete a task # Sessions walnut sessions # List sessions walnut start # Start coding session for task # Memory & Search walnut recall "query" # Search across all memory walnut projects # List projects # Subtasks walnut subtask add "title" # Add subtask walnut subtask done # Complete subtask walnut subtask rm # Remove subtask walnut subtask list # List subtasks # Lists (categories/projects) walnut lists # List all categories and projects walnut lists create # Create a new list walnut lists rename # Rename a list walnut lists delete # Delete a list # Operations (what sessions and agents call) walnut tools list # Catalog: name + one-line summary walnut tools help # Parameters and call syntax for one operation walnut tools call '{json}' # Run one operation, print its JSON result # Other walnut auth # Authenticate with external services walnut sync # Sync with external integrations walnut logs # View recent logs walnut logs -f -s agent # Follow agent logs walnut logs --json # Raw JSON output walnut doctor # Paste-ready report: build, claude, node, PATH, hosts ``` All commands support `--json` for structured output. There is no `walnut chat`. For a terminal conversation run `claude`, which is where every Walnut session runs anyway; for a single machine-readable answer use `walnut tools call`. --- ## Troubleshooting **Asking for help? Run `open-walnut doctor` and paste the output.** It prints one block with the build and commit you run, which `claude` and `node` Walnut finds, the PATH your sessions start with, and what each remote host can run, with usernames and hostnames masked so it is safe for a public issue. The same text is behind **Copy diagnostics** at the bottom of Settings, and **Copy host diagnostics** in Settings > Remote Hosts copies only the hosts. With no server running the command still reports this machine and says so. ### Agent doesn't reply in chat **Symptoms**: You type a message but get no response, or see an error. **Fixes**: the chat is a `claude` session, so start with Claude Code, not with a key. 1. Verify Claude Code CLI: `claude --version`, then run `claude` once and check it answers 2. Check session logs: `walnut logs -s session` for the spawn and the error it reported 3. Check the session panel's own status pill: a red one names the failure (bad cwd, host unreachable, session limit reached) 4. Only if titles and summaries are also failing, check the provider behind the background single calls: `~/.open-walnut/config.yaml`, and Settings › Advanced › API provider › Test connection ### Session fails to start **Symptoms**: "Failed to start session" error when trying to run Claude Code. **Fixes**: 1. Verify Claude Code CLI: `claude --version` 2. Authenticate if needed: run `claude` in your terminal and follow the prompts 3. Check that the working directory exists and is accessible 4. Check session limits — by default, max 7 concurrent local sessions ### Port already in use **Symptoms**: `EADDRINUSE: address already in use :::3456` **Fixes**: 1. Use a different port: `walnut web --port 8080` 2. Find what's using port 3456: `lsof -i :3456` 3. If it's an old Walnut process, stop it and restart ### Two people on one computer Each Unix user runs their own Walnut, with their own data in `~/.open-walnut`. The first one keeps its logs and session daemon in `/tmp/open-walnut`; anyone else's Walnut uses `~/.cache/open-walnut` instead (`walnut logs` reads the right one). Give the second one its own port: `walnut web --port 3460`, and point that user's terminal at it, because `walnut` in a terminal talks to port 3456 unless told otherwise: add `export OPEN_WALNUT_API_URL=http://127.0.0.1:3460` to their shell profile. Sessions Walnut starts already talk to their own server. Walnut trusts requests from this computer itself, so the other accounts on it can reach your Walnut's port: share a computer only with people you trust. Walnut 0.6.20 and older could not start for a second user at all: the server stopped with `EACCES: permission denied, mkdir '/tmp/open-walnut/streams'`. ### Install fails **Symptoms**: `install.sh` stops with an `open-walnut install:` error, or `bun install` fails in a checkout. **Fixes**: 1. Read the installer's last line; it names the cause. `curl or wget is required` and `sha256sum or shasum is required` name a missing tool. A download that does not match `SHA256SUMS` changed nothing: run the installer again. HTTP 403 or 429 is a GitHub limit for your address: try again later. 2. On Alpine, install the C++ runtime the musl build links: `apk add libstdc++ libgcc`. 3. In a checkout, check bun: `bun --version` must be 1.4.4 or newer (`bun upgrade` updates it), then run `bun install` again. ### Search or embedding model issues **Symptoms**: results look keyword-only, or **Settings → Search** reports an error. The embedding model is fetched on first use into `~/.open-walnut/models/`. Until it finishes, search answers from the keyword lanes alone, which is a working search, not a failure. If the download was interrupted, delete that folder and restart; it re-fetches. **To use a smaller, faster model** (a third of the index size): ```bash export WALNUT_SEARCH_V2_EMBED_MODEL=e5-small bun run start ``` If a machine should never spend anything on embeddings, `WALNUT_SEARCH_V2_SEMANTIC=0` keeps keyword search and skips the model entirely. See [Search & Embedding Setup](#search--embedding-setup) for all model options. --- ## What's Next - **[README.md](README.md)** — Feature overview and screenshots - **[ARCHITECTURE.md](ARCHITECTURE.md)** — Deep technical documentation of every subsystem - **[CONTRIBUTING.md](CONTRIBUTING.md)** — How to contribute to the project Have questions? [Open an issue](https://github.com/EvanZhang008/open-walnut/issues) on GitHub.