# Tabs Source: https://soulforge.proxysoul.com/agents/cross-tab Multiple independent workspaces with tab-aware file claims. Up to 5 tabs per project. Each is a fully independent workspace. Tabs are aware of each other, so parallel work does not collide, and git operations never capture partial changes. ## Per-tab state | What | Independent per tab | | ----------------------------- | ------------------------------------------------------------------- | | Active model | Yes (`Ctrl+L` inside the tab) | | Forge mode | Yes (`Ctrl+D`: default, architect, socratic, challenge, plan, auto) | | Session and messages | Yes | | Checkpoint history | Yes (git tags namespaced by tab ID) | | Agent dispatches and findings | Yes | | Input history | Shared across tabs | | Repo map, LSP, MCP | Shared across tabs (one per project) | Run Sonnet on tab 1 for a refactor while Haiku runs on tab 2 for documentation. One tab in plan mode, another in auto. Same project, independent agents. ## Tab awareness Every tab knows: * Which other tabs are open, what they are named, and who is active. * Which files are claimed by which tab. * Whether another tab currently has agents dispatched. When your agent is about to edit a file another tab has claimed, it sees a warning before writing and can decide to skip or proceed. ## File claims A claim is created automatically the first time a tab touches a file with `edit_file`, `multi_edit`, `rename_symbol`, `move_symbol`, `rename_file`, `refactor`, or shell writes (`sed`, `>`, `tee`, etc). Other tabs see the claim and get an advisory warning if their agent tries to edit the same file. Edits still go through (advisory, not blocking), but the agent is told another tab owns it and usually skips to avoid stomping. Claims release automatically: * 5 seconds after the tab finishes its current prompt. * Immediately on `Ctrl+X` (abort). * When the tab closes. * After 5 minutes of inactivity (stale sweep). No persistent locks. No deadlocks. Nothing to clean up manually. ## Git is hard-blocked mid-dispatch Git operations that modify the working tree (`commit`, `stash`, `restore`, `branch switch`) are blocked while another tab has active dispatch agents. This is the one hard gate: committing mid-dispatch in another tab would capture partial, half-written edits. Your changes stay intact. The block is per-tab. A tab's own agents do not block its own git. When blocked, the tool returns a terminal error so the agent stops retrying (no token-burning loops). ## Contention handling If `edit_file` fails with `old_string not found` AND the target file is claimed by another tab, SoulForge returns a terminal CONTENTION error instead of the normal retry-friendly error. The agent stops and surfaces the conflict to you instead of guessing. ## Commands ``` /tab new open a new tab (Ctrl+T) /tab close close current tab (Ctrl+W) /tab rename rename the active tab /claim show all active claims across tabs /claim release release one claim from this tab /claim release-all release every claim from this tab /claim force steal a claim from another tab ``` ## Shortcuts | Key | Action | | ------------------------- | --------------------------- | | `Ctrl+T` | New tab | | `Ctrl+W` | Close tab | | `Ctrl+[` / `Ctrl+]` | Previous / next tab | | `Ctrl+1` through `Ctrl+9` | Jump to tab N | | `Tab` / `Shift+Tab` | Cycle tabs (from the input) | # Parallel agents Source: https://soulforge.proxysoul.com/agents/dispatch Forge dispatches sub-agents in parallel for multi-file work. For bigger tasks, Forge (the main agent) dispatches sub-agents in parallel. Each one does a slice of the work, then reports back. ## Two tiers | Agent | Role | Model (router slot) | | ----- | ------------------------------------------- | ------------------- | | Spark | Read-only research: navigate, read, analyze | `spark` | | Ember | File edits, refactors | `ember` | Plus a WebSearch agent for multi-step web research. Assign cheap models to `spark`, strong models to `ember` in the [task router](/recipes/task-router). That is where most of the cost savings come from. ## What they share * **I/O cache.** When multiple agents run concurrently and one has already fetched a file, the others get the cached bytes instead of touching disk again. This is a speed win, not a token win: every agent still reads the content into its own context window and pays tokens for it. * **Edit serialization.** Concurrent writes to the same file are queued, not raced. * **Findings channel.** One agent's discovery reaches the others at their next step. ## How the cost savings actually work Savings do not come from shared context. They come from: * **Model mix.** Spark agents run on a cheap model (Haiku, Flash) while Ember runs on a strong one. The [task router](/recipes/task-router) decides per task. * **Symbol-level access.** Agents use LSP go-to-definition and surgical symbol reads, not `grep + cat` on whole files. See [code intelligence](/concepts/intelligence). * **Parallelism hides latency.** Three agents finishing in parallel beats one agent doing three things serially. ## When dispatch happens You do not trigger it. Forge decides when a task benefits from parallelism: multi-file refactors, research questions spanning several modules, or plan-mode execution. For one-shot questions, Forge does the work directly. ## Steering mid-flight Type while the agents are running. Your message is queued and injected at the next step. See [steering](/agents/steering). # Steering Source: https://soulforge.proxysoul.com/agents/steering Redirect the agent mid-flight without aborting. Type while the agent is working. Your message queues up and injects into the next agent step — no abort, no restart. ## How to use it Just keep typing. The chat input stays active even while the agent is busy: ``` > refactor the auth module Agent: [reads files, starts edits...] > actually, just do the middleware first, skip the handlers ``` At the next natural boundary, your message appears in the conversation and the agent adjusts. ## Limits * **Up to 5 messages queued** — the 6th is rejected (not dropped). * **Not after Ctrl+X** — abort clears the queue and blocks new steers until the next prompt. * **Visible as you queue** — queued messages show below the chat with a "queued" label. ## Why it matters Watching the agent drift for 10 seconds and then aborting to restart is expensive. Steering redirects for the cost of one extra user message. # How it works Source: https://soulforge.proxysoul.com/concepts/architecture A bird's-eye view of the SoulForge architecture. SoulForge is three things talking to each other: 1. **A live code graph** — every file, symbol, and import, ranked by importance. Updated as you work. 2. **An agent loop** — reads, edits, tests, commits. Can dispatch parallel sub-agents. 3. **Your Neovim** — embedded. The agent edits through the same editor you use. ## The agents | Agent | Runs | Model | | ------------- | ------------------------------------------------ | ---------------------- | | **Forge** | The one talking to you. Orchestrates everything. | Your active model | | **⚡ Spark** | Read-only research (grep, read, analyze). | `taskRouter.spark` | | **🔥 Ember** | Code edits and refactors. Own context window. | `taskRouter.ember` | | **WebSearch** | Multi-step web research with citations. | `taskRouter.webSearch` | Configure which model each one uses in [the task router](/recipes/task-router). ## What the agents share When Forge dispatches multiple sub-agents in parallel, they coordinate through a shared bus: * **File cache** — first reader caches; others reuse. * **Tool cache** — read-only tool results shared across agents and dispatches. * **Edit lock** — concurrent writes to the same file are serialized. * **Findings** — one agent's discovery reaches the others at their next step. Result: N agents don't re-read the same files N times. ## Code intelligence, layered Operations (go-to-definition, rename, diagnostics…) try the best backend first, fall back if it fails: | Tier | Backend | Used for | | ---- | ----------- | -------------------------------------------- | | 1 | LSP | precise types, workspace rename, diagnostics | | 2 | ts-morph | TypeScript/JavaScript AST ops | | 2 | tree-sitter | 33+ languages — outlines, imports | | 3 | regex | universal fallback | LSP runs via Neovim when the editor is open, via standalone servers otherwise. The agent always has it. ## System prompt Every turn, the prompt is assembled from: mode, project info, git context, the Soul Map, your memory, forbidden files, active skills. The Soul Map (built by the repo-map engine) is personalized per-turn — files you just edited or referenced get boosted. See [the Soul Map](/concepts/repo-map) for ranking details, [memory](/tools/memory) for cross-session knowledge, [compound tools](/concepts/compound-tools) for the one-call tools, [code intelligence](/concepts/intelligence) for the backend router. # Compound tools Source: https://soulforge.proxysoul.com/concepts/compound-tools One call does the complete job — rename, move, refactor, test, commit. Compound tools do a full task in a single call. You don't have to explain every step to the agent, and the agent doesn't burn tokens guessing commands. ## The tools | Tool | One-liner | | --------------- | ------------------------------------------------------------------- | | `rename_symbol` | Rename a symbol across every file. LSP-backed, verified. | | `move_symbol` | Move a symbol to another file + update every import. | | `rename_file` | Rename or move a file + update every importer. | | `refactor` | Extract function/variable, organize imports, format. | | `project` | Auto-detected lint / typecheck / test / build across 23 ecosystems. | | `navigate` | Definitions, references, call hierarchy, type hierarchy. | | `read` | Full file, line ranges, or a single symbol by name. | | `ast_edit` | Surgical TypeScript/JavaScript edits — 65+ AST operations. | See the [rename & refactor recipe](/recipes/rename-refactor) and [test & commit recipe](/recipes/test-and-commit) for copy-paste examples. ## Why they exist Without compound tools, the agent burns steps guessing: ``` Agent: I'll run the tests with `npm test` Shell: npm ERR! missing script: test Agent: Let me try `npx jest`... ``` With `project(action: "test")`, one call detects your toolchain and runs the right command. Same idea for rename, move, refactor. ## Benchmark Renaming an exported class across 8 files: | | Manual approach | `rename_symbol` | | ----------- | --------------- | --------------- | | Agent steps | 19 | 3 | | Token cost | \$0.228 | \$0.036 | The tool finds the symbol itself, runs an LSP rename, verifies zero dangling references, and reports back. No exploration, no guesswork, no verification spiral. # Cost tracking Source: https://soulforge.proxysoul.com/concepts/cost-tracking Live USD cost, per-model and per-subagent. SoulForge tracks every token spent and prices it in real time. The status bar shows the running total in USD. `/context` opens a dashboard with the per-model breakdown. ## What gets tracked * **Prompt tokens** (uncached input). * **Completion tokens** (model output). * **Cache-write tokens** (billed at a higher rate by most providers). * **Cache-read tokens** (billed at a discount). * **Subagent tokens** tracked separately from the main agent. * **Per-model breakdown** when the task router mixes providers. ## Providers with built-in pricing Pricing tables ship for the major providers, updated against their public price lists: | Provider | Notes | | --------------------------------------- | ------------------------------------------------------------------------------ | | Anthropic | Claude Opus/Sonnet/Haiku with cache-write and cache-read rates | | OpenAI | GPT-5.4, GPT-4.1, o3, o4-mini | | Google | Gemini 2.5 Pro/Flash, Gemini 3 Flash/Pro | | DeepSeek | V3.2 (chat and reasoner) | | Groq | Llama 3.3, Llama 4 Scout, Qwen3, GPT-OSS | | Mistral | Mistral Large/Medium/Small, Codestral, Magistral, Ministral, Pixtral, Devstral | | Fireworks | Tier-based pricing (Mixtral, Llama 70B+, DeepSeek) | | GitHub Copilot | Premium-request multiplier-based estimation | | OpenRouter | Live pricing from the catalog | | GitHub Models | Per-token via multipliers | | Ollama, LM Studio, OpenCode free models | \$0.00 | Custom providers default to a conservative estimate. Unknown models fall back to Sonnet-tier pricing as a safety floor. ## Why it matters Two tactics cut cost dramatically: 1. **Mix models.** Haiku for spark agents, Sonnet for ember agents, Flash for compaction. A task that would cost \$0.25 on Sonnet often runs for \$0.05 when the exploration phase routes through Haiku. 2. **Use caching.** Cache reads are 10x cheaper on Anthropic, up to 50% off on Groq/Fireworks. SoulForge structures the system prompt and the Soul Map for maximum cache hits — typical cache-hit rates exceed 60%. ## UI The status bar shows the running total in USD. Compact mode shows tokens plus a dollar figure. `/context` opens the detailed view: per-model usage, cache ratio, subagent spend, and the compaction history. Use `/router` to [assign cheap models to cheap tasks](/recipes/task-router). # Code intelligence Source: https://soulforge.proxysoul.com/concepts/intelligence LSP, AST, and fallbacks — the agent sees your code like an IDE. SoulForge gives the agent the same code intelligence your IDE has: go-to-definition, references, rename, diagnostics, type info. It tries LSP first, falls back to AST tools, then tree-sitter, then regex. This replaces grep-and-read. The agent navigates the code graph by symbol, not by pattern matching across files. Cheaper, faster, correct. You don't configure this. It picks the best available backend for every operation. ## What it can do | Operation | Examples | | -------------------- | -------------------------------------------------- | | **Go to definition** | "where is `parseConfig` defined?" | | **Find references** | "who calls `parseConfig`?" | | **Rename** | "rename `useChat` to `useConversation` everywhere" | | **Type info** | "what's the return type of `fetchUser`?" | | **Diagnostics** | "any errors in this file?" | | **Call hierarchy** | "what does `buildTools` call?" | All of these come through the `navigate` and `analyze` tools. ## How it picks a backend ``` LSP → ts-morph (TS/JS) → tree-sitter (33+ langs) → regex ``` If LSP isn't attached for your language, the agent still works — the lower tiers cover most queries. ## LSP always-on The agent has LSP access even when the editor panel is closed. When you open Neovim (`Ctrl+E`), requests route through your LSP servers there. When it's closed, SoulForge spawns standalone servers. Check what's attached: ``` /lsp status ``` Install more servers through the Mason registry: ``` /lsp install ``` ## After every edit SoulForge snapshots diagnostics before the edit, compares after, and tells the agent: * New errors it just introduced * Errors it just fixed * Errors in other files caused by the edit The agent sees this instantly — no need to run a full typecheck. # Model events Source: https://soulforge.proxysoul.com/concepts/model-events Per-call latency, tokens, and errors when you're routing across providers. When the task router mixes models, "which one was slow?" and "which one errored?" stop being obvious. Model events is an opt-in debug view that records every LLM call SoulForge makes — main loop, subagents, compaction, semantic summaries, web search — so you can see exactly what each model did. Off by default. No overhead when disabled. ## Open it ``` /model-events ``` Press `e` to start recording, `e` again to stop (clears the buffer). The view stays empty until you enable it. ## Three tabs **Models** — per-model aggregates. Calls, errors, average latency, last latency, total tokens. Sorted by most recent activity. **Recent** — every call newest-first. When, source (main / subagent / compaction / other), model, status, time, in/out tokens. **Errors** — only failed calls, with the error message attached to the model and source that produced it. ## When to enable * The router is slower than you expect — find which model is the bottleneck. * Subagents are silently failing — see which model errored on which step. * Comparing two providers for the same task — read avg/last latency side by side. * Cache hit rates feel off — see cache-read tokens per model. For day-to-day work leave it off. Cost and aggregate token usage already live in `/context`. ## Footprint * 500-event ring buffer, in-memory only. * Cleared when you disable recording or restart SoulForge. * Not written to disk, not persisted across sessions. * Toggle is per-session — not a config setting. ## Related * [`/context`](/concepts/cost-tracking) — running totals, per-model cost, per-subagent spend. * [`/router`](/recipes/task-router) — pick which model handles which kind of task. # The Soul Map Source: https://soulforge.proxysoul.com/concepts/repo-map Why SoulForge starts every turn already knowing your codebase. On startup, the repo-map engine scans your codebase and builds a graph — every file, symbol, and import edge — then ranks it. The rendered result, injected at the top of every prompt, is the **Soul Map**: what the agent actually sees. The agent never wastes a turn grepping for "where does auth live" — it already sees the map. ## What you see The Soul Map appears in every prompt, looking like this: ``` src/services/auth/session.ts (→12) +SessionManager Tracks active sessions, refresh tokens, expiry +SessionStore ``` * `+` — exported symbol * `(→12)` — blast radius: 12 files depend on this one * `[NEW]` — file changed recently * One-line summary after the name (when semantic summaries are on) ## What makes it useful * **Ranked by PageRank** — files imported by lots of others rank higher. * **Personalized per turn** — files you just edited or read get boosted. * **Git-aware** — files that always change together get pulled in too. * **Real-time** — edits re-index immediately. * **30+ languages** — TypeScript, Python, Rust, Go, Java, Ruby, C/C++, Swift, Kotlin, and more. ## Config Everything runs out of the box. If you want to tune it: ```json theme={null} { "repoMap": true, "semanticSummaries": "ast" } ``` | Field | Values | | ------------------- | --------------------------------------------------------------------- | | `repoMap` | `true` (default) or `false` to disable | | `semanticSummaries` | `"ast"` (fast, default), `"llm"` (one-line LLM descriptions), `"off"` | `/repo-map` opens the settings panel (the `repoMap` config keys below tune the engine that builds the map). ## Skip the scan For quick one-shot questions, skip the startup scan: ```bash theme={null} soulforge --headless --no-repomap "what's the version?" ``` Or set `SOULFORGE_NO_REPOMAP=1`. # Context compaction Source: https://soulforge.proxysoul.com/context/compaction What happens when the conversation gets long. When your conversation gets close to the model's context window, SoulForge compacts it — summarizes older messages into a concise state, preserves the recent ones verbatim. The conversation continues; nothing is lost. Compaction fires automatically at 70% of the context window. You can also run it manually: ``` /compact ``` ## Two strategies SoulForge tracks structured state — files touched, decisions, discoveries, tool results — as the conversation happens. When compaction fires, this state is already built. Most sessions compact in zero LLM calls. Best for: typical coding sessions. Sends the older half of the conversation to a cheap model and replaces it with a prose summary. Best for: design-heavy sessions where nuanced reasoning matters more than structured data. Switch with `/compact settings` or in config. ## Config ```json theme={null} { "compaction": { "strategy": "v2", "triggerThreshold": 0.7, "keepRecent": 4 } } ``` | Field | Default | What it does | | ------------------ | ------- | ---------------------------------------- | | `strategy` | `"v2"` | `"v2"` or `"v1"` | | `triggerThreshold` | `0.7` | Auto-compact at this fraction of context | | `keepRecent` | `4` | Recent messages kept verbatim | | `maxToolResults` | `30` | Rolling window of tool results (V2) | | `llmExtraction` | `true` | Allow a cheap gap-fill pass (V2) | ## Which model compacts Assign a cheap model in the [task router](/recipes/task-router): ```json theme={null} { "taskRouter": { "compact": "google/gemini-2.5-flash" } } ``` For V2, this model only runs the optional gap-fill. For V1, it does the whole summary. ## Signals * Context bar shows compaction strategy + slot count. * Compacting spinner during an active compaction. * System message reports before/after context percentages. # Modes & the prompt Source: https://soulforge.proxysoul.com/context/prompt-system Switch personas with Ctrl+D. Load project instructions automatically. ## Modes Press `Ctrl+D` to cycle, or `/mode `: | Mode | What it does | | ----------- | ---------------------------------------------------------------------- | | `default` | Full agent — investigates then implements | | `architect` | Design only — outlines and tradeoffs, no file edits | | `socratic` | Asks probing questions before doing anything | | `challenge` | Devil's advocate — challenges every assumption | | `plan` | Research, plan, confirm, execute (see [plan mode](/recipes/plan-mode)) | | `auto` | Autonomous — minimal interruptions | ## Project instructions SoulForge auto-loads `SOULFORGE.md` from your project root as context. You can also pick up instructions from other tools: ```json theme={null} { "instructionFiles": ["soulforge", "claude", "cursorrules"] } ``` | File | From | | --------------------------------- | ------------------------- | | `SOULFORGE.md` | SoulForge (on by default) | | `CLAUDE.md` | Claude Code | | `.cursorrules` | Cursor | | `.github/copilot-instructions.md` | GitHub Copilot | | `.clinerules` | Cline | | `.windsurfrules` | Windsurf | | `AGENTS.md` | OpenAI Codex | | `.opencode/instructions.md` | OpenCode | | `AMPLIFY.md` | Amp | Toggle in the TUI: `/instructions`. ## Per-family tuning SoulForge tailors the prompt based on the model family (Claude, OpenAI, Gemini) — different models respond better to different phrasings. Gateway providers (LLM Gateway, OpenRouter, Vercel) are detected by the model ID portion, so `llmgateway/claude-sonnet-4` gets the Claude prompt. This is automatic; there's nothing to configure. # Thinking & retries Source: https://soulforge.proxysoul.com/context/provider-options Configure thinking modes, budgets, and retry behavior. Open `/provider-settings` to tune thinking mode and budget per model. Or edit config directly: ```json theme={null} { "thinking": { "mode": "adaptive" } } ``` ## Thinking modes | Mode | What it does | | ---------------------- | --------------------------------------- | | `off` | No thinking blocks | | `adaptive` (or `auto`) | Model decides when to think (Claude 4+) | | `enabled` | Fixed budget thinking — specify tokens | Budgets when mode is `enabled`: 1024, 2048, 5000, 10000, 20000. ## Context management (Anthropic) Advanced Anthropic features. Apply automatically on supported models: * **Clear Thinking** — auto-clears old thinking blocks, keeps last 5 turns. * **Clear Tool Uses** — drops old tool content when input exceeds 100k. * **Compact** — API-side compaction at 75% of context. ## Graceful degradation If a provider rejects an option (unsupported thinking mode, unknown parameter), SoulForge retries with reduced options automatically. You don't see the error. ## Retries on rate limits Getting 429s? Bump retry config: ```json theme={null} { "retry": { "maxAttempts": 5, "baseDelayMs": 3000 } } ``` Delays double each attempt: 3s, 6s, 12s, 24s, 48s. | Field | Default | Range | | ------------- | ------------------------------- | --------- | | `maxAttempts` | `3` | 1–10 | | `baseDelayMs` | `1000` (chat) / `2000` (agents) | 250–60000 | # FAQ Source: https://soulforge.proxysoul.com/faq Common questions, short answers. Stuck on something? Drop into the [Discord](https://discord.gg/fX4H7GYSMJ) — fastest way to get an answer. ## Does it run on Windows? Yes — natively on Windows 10 1809+ and Windows 11 (x64). Get the setup wizard or portable ZIP from the [official Empryo download page](https://empryo.com/download). Empryo is not distributed through WinGet or GitHub Releases. ## Do I need Neovim? No. Neovim is an optional editor panel for users who want one. SoulForge works fully from the chat alone. If you do want the panel, SoulForge can auto-install Neovim and a Nerd Font on first launch. ## Do I need an API key? You need either one cloud key or a local LLM. **One cloud key.** [LLM Gateway](https://llmgateway.io/dashboard?ref=6tjJR2H3X4E9RmVQiQwK) — one key for Claude, GPT, Gemini, Grok, Llama, Qwen, and others, up to 30% off frontier models. Or bring any individual provider key you already have. **Local, free.** Install Ollama or LM Studio. SoulForge auto-detects both. Cost tracking reports `$0.00` for local runs. ```bash theme={null} soulforge --set-key llmgateway sk-... # one key, every model soulforge --set-key anthropic sk-ant-... brew install ollama && ollama pull llama3.3 # or run local ``` ## How much does it cost to run? Cloud costs depend on your model. SoulForge shows the live USD number as you work, broken down per model and per subagent. Cache-aware pricing for all major providers. Rough ballpark on a mid-size repo: a one-file refactor under \$0.05 on Sonnet with caching, a cross-file rename often under \$0.05 because the compound tool runs in three steps, an all-day session with a mix of Haiku exploration and Sonnet coding typically stays under \$5. Local models are free. Use the [task router](/recipes/task-router) to push exploration to a cheap model and reserve the strong model for code. ## Which model should I pick? * **Claude Sonnet 4.5** for balanced code quality. * **Claude Haiku 4.5** for fast exploration and cleanup. * **Google Gemini 2.5 Flash** for compaction (cheapest, fastest). * **Local Llama/Qwen/DeepSeek** if you want \$0 and privacy. Mix them via `/router`. See the [task router recipe](/recipes/task-router). ## Does it work without internet? With local LLMs (Ollama, LM Studio), yes — fully offline for model calls. The Soul Map, LSP, tree-sitter, and everything else is local anyway. ## How does SoulForge edit code? Four tools, picked by the one that fits: 1. **AST editing** for TypeScript and JavaScript. Addresses functions, classes, methods by name — not text. A return-type change costs a handful of tokens. 2. **Multi-edit** for batched text edits on the same file, applied atomically with line-offset tracking. 3. **Edit** for single text edits with line-anchored matching on any file (JSON, YAML, Markdown, Python, Rust, Go, etc). 4. **LSP rename and refactor** for workspace-wide symbol renames, move symbol, extract function, organize imports. Compiler-verified, every file updated. The agent picks automatically. ## Is my code uploaded anywhere? Only what you send to your chosen LLM provider, same as any other AI coding tool. SoulForge does not upload anything to our servers. There is no SoulForge backend. Privacy patterns (`/privacy add`) block sensitive files from the agent entirely. Built-in blocks already cover `.env`, `.pem`, `credentials`, `id_rsa`, `.npmrc`, `.netrc`, and more. ## Can I use it for work? Yes. Use it anywhere, anytime, for free — personal projects, startups, enterprise, client work, whatever. The [Business Source License 1.1](https://github.com/proxysoul/soulforge/blob/main/LICENSE) only kicks in if you want to *redistribute SoulForge as a commercial product* (repackage it, resell it, host it as a paid service). Converts to Apache 2.0 on March 15, 2030. ## What languages does code intelligence cover? Tree-sitter detection covers: TypeScript, JavaScript, Python, Go, Rust, Java, Kotlin, Scala, C#, Swift, Dart, Elixir, OCaml, Lua, C, C++, Ruby, PHP, Zig, Bash, CSS, HTML, JSON, TOML, YAML, XML, Markdown, MDX, SQL, GraphQL, Protobuf, Dockerfile, Makefile, Nix, HCL/Terraform, Bazel/Starlark, Jsonnet, Vue, ReScript, Solidity, TLA+, Emacs Lisp, Objective-C. LSP coverage depends on what's installed via Mason — typical set: TypeScript, JavaScript, Python, Go, Rust, Java, Kotlin, Scala, C#, Swift, Dart, Elixir, OCaml, Lua, C, C++, Ruby, PHP, Zig, Bash, CSS, HTML, JSON, TOML, YAML, Dockerfile, Vue. AST editing (ts-morph) is TypeScript and JavaScript only. Everything else uses LSP and tree-sitter. ## Can I install LSP servers from SoulForge? Yes. `/lsp install` opens a browser over Mason's registry (576+ packages). SoulForge installs into `~/.soulforge/lsp-servers/` via npm, pip, cargo, go, or GitHub releases depending on the package. ## How do sessions work? Every conversation is auto-saved as JSONL under `~/.soulforge/sessions/`. Resume with `soulforge --headless --session ` or press `Ctrl+P` in the TUI. Each session carries its checkpoint git tags — undo all the way back to any earlier turn. ## Can I run this over SSH? Yes. The TUI is pure terminal output (OpenTUI with Kitty graphics for inline images). SSH, tmux, and remote shells all work. ## What about multi-tab work? Up to 5 tabs per project. Each has its own session, model, mode, and checkpoint history. Tabs are aware of each other: when one claims a file, the others see the claim and skip it. Git operations hard-block during cross-tab dispatch so you never commit partial changes. See [tabs](/agents/cross-tab). ## How do themes work? 36 built-in themes (Catppuccin, Dracula, Gruvbox, Tokyo Night, Rose Pine, Kanagawa, Nightfox, Cyberdream, Oxocarbon, Ember, Vesper, and more). `/theme` opens a live-preview picker. Custom themes go in `~/.soulforge/themes/.json` and hot-reload on save. See [themes](/tools/themes). ## How do I get help? * [Troubleshooting](/troubleshooting) — common issues. * `/diagnose` inside SoulForge — LSP, tree-sitter, semantic indexing health check. * `/status` — full system dashboard. * [GitHub issues](https://github.com/proxysoul/soulforge/issues). # Installation Source: https://soulforge.proxysoul.com/installation Install Empryo from the official website on macOS, Linux, or Windows. ## Install Official Empryo installers and direct downloads are available only from [empryo.com/download](https://empryo.com/download). Empryo is not distributed through Homebrew, WinGet, npm, or GitHub Releases. ```bash theme={null} curl -fsSL https://empryo.com/install.sh | bash ``` The installer detects your platform and architecture, downloads from `dl.empryo.com`, and verifies the artifact checksum before installation. ```powershell theme={null} irm https://empryo.com/install.ps1 | iex ``` You can also download the Windows setup wizard or portable ZIP from the [official download page](https://empryo.com/download). Download the macOS, Linux, or Windows desktop app from [empryo.com/download](https://empryo.com/download). ## Add a key ```bash theme={null} empryo --set-key anthropic sk-ant-... ``` Empryo also supports other cloud providers and local models. See the [current provider documentation](https://empryo.com/docs/providers/overview). ## Launch ```bash theme={null} cd your-project empryo ``` Read the maintained installation, provider, and quickstart guides on the Empryo website. # SoulForge Source: https://soulforge.proxysoul.com/introduction Code-aware terminal AI that works with symbols, not strings. SoulForge Most AI coding tools treat your codebase as text. They grep, they paste, they match strings. SoulForge treats code as code. It parses every file on launch, builds a live dependency graph, and addresses your work by symbol and line number. Rename a class, move a function, change a return type, the agent uses AST surgery and LSP rename, not find-and-replace. Symbol-addressed TypeScript and JavaScript edits via ts-morph. Roots in a 2022 Master's thesis on programmatic JS to TS conversion. Changing a return type costs a handful of tokens. Workspace rename, definitions, references, call and type hierarchy, diagnostics, code actions, format. Mason installer for 576+ servers from the TUI. V2 extracts structured state as the conversation happens. When context fills, serialization is instant. Typical sessions compact for free. Per-model and per-subagent breakdown. Real prices for Claude, OpenAI, Gemini, DeepSeek, Groq, Mistral, Copilot, OpenRouter. Cache-aware. Zero for local models. Up to 5 tabs per project, each with its own model, mode, session, and checkpoints. Tabs see each other's claimed files and active agents. Git hard-blocks during cross-tab dispatch, you never commit partial changes. The agent reads a single function by name or jumps via LSP go-to-definition. It navigates the code graph instead of grepping and dumping whole files. ## Start in 60 seconds ```bash Install theme={null} curl -fsSL https://empryo.com/install.sh | bash empryo ``` ```bash One key, every model theme={null} soulforge --set-key llmgateway sk-... soulforge ``` ```bash Local, no key theme={null} brew install ollama && ollama pull llama3.3 soulforge ``` Neovim is optional. A Nerd Font is optional. SoulForge prompts to install either on first launch if you want them. Verified installer or direct download from the official Empryo website. Your first five minutes. Do I need Neovim? Which LLM? How much does it cost? ## Plain-English prompts, real tool calls ``` > rename UserService to AccountService > move parseConfig from utils.ts to config/parser.ts > change fetchUser to return Promise > run tests, fix lint, commit > research how auth handles refresh tokens ``` Every line above resolves to a single compound tool call. No multi-step orchestration. No guessing commands. ## Why it's different SQLite graph of every file, symbol, and import. Ranked by PageRank and git co-change. Blast-radius tags show how far an edit ripples. Updates as you work. 65+ surgical operations via ts-morph. Toggle `async`, change a parameter, add a method, rename a class member. Atomic batches with rollback. Definitions, references, rename, diagnostics, code actions, call and type hierarchy, format, hover. Install servers from Mason's registry inside the TUI (`/lsp install`). Works with or without Neovim open. Extract one function by name. A 500-line file becomes 20 lines in the prompt. TypeScript, Python, Rust, Go, Java, Kotlin, Scala, Swift, C/C++, Ruby, PHP, Elixir, Zig, Lua, Dart, OCaml, Solidity, Vue, and more. Live USD cost broken down by model and by subagent. Cache-aware pricing across Anthropic, OpenAI, Google, DeepSeek, Groq, Mistral, Fireworks, Copilot, OpenRouter, and GitHub Models. Structured state extraction as you go — files touched, decisions, failures, tool results. Compaction serializes instantly and usually skips the LLM pass entirely. Forge dispatches explore, code, and web-search agents in parallel. One agent's file read is cached for the others. Discoveries propagate across agents within a step. Different model per task slot: spark for exploration, ember for code, compact for context, verify for review. Cheap work to cheap models. Mix providers freely. Catppuccin, Dracula, Gruvbox, Tokyo Night, Rose Pine, Kanagawa, Nightfox, Cyberdream, Oxocarbon, and more. Hot-reloaded custom themes from a JSON file. Every turn is a checkpoint with git tags for file rollback. Sessions save as JSONL; resume by short-id prefix. Undo conversation and files in one keystroke. Ollama and LM Studio auto-detected. Run Llama, Qwen, DeepSeek, Mistral, whatever you have. Cost tracking skips local models automatically. Browse, install, enable, disable community skills from the TUI (`Ctrl+S`). Skills resolve from project and global scopes, auto-loaded into the agent prompt. Hearth exposes your running forge to Telegram or Discord. Approval prompts arrive as inline buttons. Your code never leaves your host. (Experimental — expect rough edges.) ## Providers 21 built-in LLM providers: Anthropic, OpenAI, Google, xAI, Groq, DeepSeek, Mistral, Bedrock, Fireworks, MiniMax, Codex, Copilot, GitHub Models, OpenRouter, OpenCode Zen, OpenCode Go, LLM Gateway, Vercel AI Gateway, Proxy (CLIProxyAPI relay), Ollama, LM Studio. Plus any OpenAI-compatible API via custom providers. See [all providers](/providers/overview). ## Ready? `brew tap proxysoul/tap && brew install soulforge` Common questions, short answers. Need help? [Troubleshooting](/troubleshooting) · [Discord](https://discord.gg/fX4H7GYSMJ) · [GitHub issues](https://github.com/proxysoul/soulforge/issues) · [Blog](https://proxysoul.com/blog/soulforge). # GitHub Copilot Source: https://soulforge.proxysoul.com/providers/copilot Use your Copilot subscription with SoulForge. If you pay for [GitHub Copilot](https://github.com/features/copilot), SoulForge can use it. Claude Opus/Sonnet, GPT-4o, GPT-5.4, o-series, Gemini 2.5 Pro — all included in your Copilot subscription, no extra cost. Unofficial integration using the same internal API as VS Code. Not endorsed by GitHub. Use at your own risk. ## Setup Install the Copilot extension in your IDE and sign in. This writes your OAuth token to disk. ```bash theme={null} jq -r '."github.com".oauth_token' ~/.config/github-copilot/apps.json ``` On Windows the path is `~\AppData\Local\github-copilot\apps.json`. ```bash theme={null} soulforge --set-key copilot ``` Or use `/keys` inside the TUI. Press `Ctrl+L`, pick `copilot`, choose a model. ```bash theme={null} soulforge --headless --model copilot/claude-sonnet-4 "explain this file" ``` ## Legal review We reviewed GitHub's published terms and Microsoft's AI Code of Conduct — none prohibit third-party clients using valid Copilot credentials. Full review: * [GitHub Copilot Product Specific Terms](https://github.com/customer-terms/github-copilot-product-specific-terms) — content-only restrictions * [GitHub Generative AI Services Terms](https://github.com/customer-terms/github-generative-ai-services-terms) — acknowledges third-party products * [Additional Products and Features](https://docs.github.com/en/site-policy/github-terms/github-terms-for-additional-products-and-features#github-copilot) — no prohibition on alternative clients * [Acceptable Use Policies](https://docs.github.com/en/site-policy/acceptable-use-policies/github-acceptable-use-policies) — unauthorized access clause only; paid credentials are not unauthorized * [GitHub Terms of Service](https://docs.github.com/en/site-policy/github-terms/github-terms-of-service) — rate-limiting only * [Microsoft AI Code of Conduct](https://learn.microsoft.com/legal/ai-code-of-conduct) — content safety only * API could change without notice. * High volume may trigger rate-limiting. * Terms could be updated in the future. One [community discussion](https://github.com/orgs/community/discussions/178117) has a GitHub employee stating the API is "intended solely for officially supported clients" — this is not in any legal document. **Re-verify if GitHub publishes updated terms after 2026-04-04.** # Custom providers Source: https://soulforge.proxysoul.com/providers/custom Connect any OpenAI-compatible API with one block of config. Add any OpenAI-compatible API as a provider. No code, no plugin. ## Minimal example Add this to `~/.soulforge/config.json` (or `.soulforge/config.json` for project scope): ```json theme={null} { "providers": [ { "id": "together", "name": "Together AI", "baseURL": "https://api.together.xyz/v1", "envVar": "TOGETHER_API_KEY", "models": ["meta-llama/Llama-3-70b-chat-hf"] } ] } ``` Set the key and use it: ```bash theme={null} soulforge --set-key together soulforge --model together/meta-llama/Llama-3-70b-chat-hf ``` ## Fields | Field | Required | What it does | | ----------- | -------- | ------------------------------------------------------------ | | `id` | yes | Used in model strings (`together/llama-3`) | | `baseURL` | yes | OpenAI-compatible endpoint | | `name` | — | Display name | | `envVar` | — | Env var for the key (omit for auth-less servers) | | `models` | — | Fallback model list (strings or `{id, name, contextWindow}`) | | `modelsAPI` | — | URL returning OpenAI `/v1/models` format | | `reasoning` | — | Thinking/reasoning config (see below) | ## More examples ```json theme={null} { "providers": [{ "id": "local", "name": "Local LLM", "baseURL": "http://localhost:8080/v1", "models": ["llama-3-70b"] }] } ``` ```json theme={null} { "providers": [{ "id": "corp", "name": "Corp Gateway", "baseURL": "https://llm.internal.corp.com/v1", "envVar": "CORP_LLM_KEY", "modelsAPI": "https://llm.internal.corp.com/v1/models" }] } ``` ```json theme={null} { "providers": [ { "id": "together", "baseURL": "https://api.together.xyz/v1", "envVar": "TOGETHER_API_KEY", "models": ["meta-llama/Llama-3-70b"] }, { "id": "cerebras", "baseURL": "https://api.cerebras.ai/v1", "envVar": "CEREBRAS_API_KEY", "modelsAPI": "https://api.cerebras.ai/v1/models" } ] } ``` ## Thinking / reasoning For models that support reasoning over an OpenAI-compatible endpoint, add a `reasoning` block. Three styles are supported — use the one your provider accepts: ```json theme={null} { "providers": [{ "id": "dashscope", "baseURL": "https://dashscope.aliyuncs.com/compatible-mode/v1", "envVar": "DASHSCOPE_API_KEY", "models": ["qwen-plus"], "reasoning": { "enabled": true, "budget": 8192 } }] } ``` | Style | Fields | When to use | | --------------- | ---------------------------------------------------------- | --------------------------------------------- | | OpenAI-style | `effort: "low" \| "medium" \| "high" \| "xhigh" \| "none"` | OpenAI-compatible reasoning models | | DashScope-style | `enabled: true`, `budget: 8192` | Alibaba Qwen, DashScope APIs | | Raw | `extraParams: { ... }` | Anything else — fields are forwarded verbatim | The fields are injected into every request body. Raw `extraParams` override all other keys on collision. ## Scope * **Global** — `~/.soulforge/config.json` applies everywhere. * **Project** — `.soulforge/config.json` overrides global entries with the same `id`. ## Conflicts If your `id` matches a built-in (e.g. `"anthropic"`), SoulForge renames it to `{id}-custom` — the built-in is never replaced. Custom providers always show `[custom]` in `/keys` and `--list-providers`. # Providers Source: https://soulforge.proxysoul.com/providers/overview 21 built-in LLM providers, plus any OpenAI-compatible API. SoulForge ships 21 built-in providers. Set one key and go. ## Cloud | Provider | Setup | Get a key | | ------------- | ------------------------------------------ | ------------------------------------------------------ | | **Anthropic** | `soulforge --set-key anthropic sk-ant-...` | [console.anthropic.com](https://console.anthropic.com) | | **OpenAI** | `soulforge --set-key openai sk-...` | [platform.openai.com](https://platform.openai.com) | | **Google** | `soulforge --set-key google ...` | [aistudio.google.com](https://aistudio.google.com) | | **xAI** | `soulforge --set-key xai xai-...` | [console.x.ai](https://console.x.ai) | | **Groq** | `soulforge --set-key groq gsk_...` | [console.groq.com](https://console.groq.com) | | **DeepSeek** | `soulforge --set-key deepseek sk-...` | [platform.deepseek.com](https://platform.deepseek.com) | | **Mistral** | `soulforge --set-key mistral ...` | [console.mistral.ai](https://console.mistral.ai) | | **Fireworks** | `soulforge --set-key fireworks ...` | [fireworks.ai](https://fireworks.ai) | | **MiniMax** | `soulforge --set-key minimax ...` | [platform.minimaxi.com](https://platform.minimaxi.com) | ## Gateways (one key, many models) | Provider | What you get | Get a key | | --------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------- | | **LLM Gateway** | One key for every major model, up to 30% off frontier | [llmgateway.io](https://llmgateway.io/dashboard?ref=6tjJR2H3X4E9RmVQiQwK) | | **OpenRouter** | 200+ models | [openrouter.ai](https://openrouter.ai) | | **Vercel AI Gateway** | Unified gateway | [vercel.com/ai-gateway](https://vercel.com/ai-gateway) | ## Subscription-based | Provider | Use | Setup | | ---------------------------------------- | ------------------------- | ------------------------------------------- | | **[GitHub Copilot](/providers/copilot)** | Your Copilot subscription | OAuth token from IDE | | **Codex** | ChatGPT subscription | `/codex login` inside SoulForge | | **GitHub Models** | Free with any GitHub PAT | `soulforge --set-key github-models ghp_...` | ## Local (free) | Provider | Setup | | ---------------- | ------------------------------------------------------------------- | | **Ollama** | Auto-detected at `localhost:11434`. Set `OLLAMA_HOST` to override. | | **LM Studio** | Auto-detected at `localhost:1234`. Set `LM_STUDIO_URL` to override. | | **OpenCode Go** | `soulforge --set-key opencode-go ...` | | **OpenCode Zen** | `soulforge --set-key opencode-zen ...` | ## Enterprise | Provider | Setup | | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Bedrock** | `export AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... AWS_REGION=us-east-1` | | **Proxy** | Local CLIProxyAPI relay — routes Claude, GPT, Gemini, Grok, Antigravity via your existing chat subscriptions. Install the addon (`soulforge addon install proxy`), then `/proxy login`. See [addons](/tools/addons). | ## LLM Gateway One key for every major model — Claude, GPT, Gemini, Grok, Llama, Qwen — up to 30% off frontier pricing. 1. Get a key at [llmgateway.io](https://llmgateway.io/dashboard?ref=6tjJR2H3X4E9RmVQiQwK). 2. Save it: ```bash theme={null} soulforge --set-key llmgateway sk-... ``` 3. Launch SoulForge, press `Ctrl+L`, pick any model. ## OpenRouter 200+ models behind a single key, including frontier and open-weight. 1. Get a key at [openrouter.ai/keys](https://openrouter.ai/keys). 2. Save it: ```bash theme={null} soulforge --set-key openrouter sk-or-... ``` 3. Launch SoulForge, press `Ctrl+L`, pick any model. ## Anything else Any OpenAI-compatible API works via [custom providers](/providers/custom) — one block of config, no code. ## Switching models Inside SoulForge, press `Ctrl+L` to pick a provider and model. Assign different models to different tasks with `/router` (cheap model for search, strong for code). ```bash theme={null} soulforge --list-providers # see what's configured soulforge --list-models anthropic # see available models for one provider ``` # Quickstart Source: https://soulforge.proxysoul.com/quickstart Five minutes to your first refactor. ```bash theme={null} cd your-project soulforge ``` The first launch scans your codebase to build a live graph (a few seconds). Press `Ctrl+L` to pick a model. Type any question. The agent already knows your file layout. ``` > what does the auth middleware do? ``` Describe the change in plain English. ``` > rename UserService to AccountService across the project ``` Watch the agent run `rename_symbol` — one tool call updates every file. Press `Ctrl+G`, then `c`. SoulForge writes the commit message and runs lint + typecheck before committing. Or just say: ``` > commit my changes ``` ## Essential shortcuts | Key | Does | | ------------------- | ----------------------------- | | `Ctrl+L` | Switch model | | `Ctrl+E` | Toggle editor panel (Neovim) | | `Ctrl+G` | Git menu | | `Ctrl+K` | Command palette | | `Ctrl+B` / `Ctrl+F` | Browse prev / next checkpoint | | `Ctrl+X` | Stop the agent | | `Ctrl+T` / `Ctrl+W` | New tab / close tab | ## Common tasks Rename a symbol across every file with one prompt. Run your toolchain through the agent. Cheap model for search, strong model for code. Headless mode for scripts and pipelines. Hearth — Telegram or Discord. (Experimental — expect rough edges.) GitHub, Sentry, anything MCP. ## Next * Browse the [recipes](/recipes/rename-refactor) for copy-paste workflows. * Skim [commands](/reference/commands) — type `/` to see them all in the app. * Tune `~/.soulforge/config.json` — see [configuration](/reference/configuration). # Run from CI or scripts Source: https://soulforge.proxysoul.com/recipes/headless Headless mode — pipe in a prompt, get back results. Headless mode runs SoulForge without the TUI. Same agent, same tools, no renderer. For scripts, CI pipelines, automation. ## Basics ```bash theme={null} # Inline prompt soulforge --headless "explain the auth middleware" # From stdin echo "find unused exports" | soulforge --headless # JSON output soulforge --headless --json "list all TODOs" # Real-time event stream soulforge --headless --events "refactor the store" ``` Agent text streams to `stdout`. Progress messages go to `stderr` so piping stays clean. ## Useful flags | Flag | What it does | | ------------------ | -------------------------------------------------- | | `--model ` | Override the default model | | `--max-steps ` | Stop after N steps | | `--timeout ` | Kill after N ms | | `--system "..."` | Add custom system instructions | | `--include ` | Pre-load a file into context (repeatable) | | `--no-repomap` | Skip the startup scan (faster for quick questions) | | `--diff` | List files changed after the run | | `--save-session` | Save conversation so you can resume later | | `--session ` | Resume a saved session | | `--chat` | Interactive multi-turn chat | | `--quiet` | Suppress stderr progress | Exit codes: `0` success, `1` error, `2` timeout, `130` abort. ## CI example ```yaml theme={null} # .github/workflows/lint.yml - name: Auto-fix lint run: | soulforge --headless --max-steps 30 --timeout 180000 --diff \ "run the linter, fix every issue, typecheck" ``` ## Resume a session ```bash theme={null} # step 1: investigate soulforge --headless --save-session "analyze the auth module, find all issues" # grab the session id from the output, then: soulforge --headless --session --save-session "now fix them" soulforge --headless --session "write tests" ``` ## Parse events ```bash theme={null} soulforge --headless --events "refactor the store" | while read -r line; do type=$(echo "$line" | jq -r '.type') case "$type" in tool-call) echo "→ $(echo "$line" | jq -r '.tool')" ;; done) echo "done in $(echo "$line" | jq -r '.duration')ms" ;; esac done ``` Event types: `start`, `text`, `tool-call`, `tool-result`, `step`, `error`, `done`, `session-saved`. ## What's disabled * TUI, editor panel, splash screen. * Interactive approvals — destructive actions auto-allow in headless mode. * User steering — no stdin during the run. ## Security in CI Set `SOULFORGE_NO_REPOMAP=1` to skip the startup scan on tiny CI jobs. Store API keys as CI secrets. Use `--max-steps` and `--timeout` to cap runaway runs. # Model fallback chains Source: https://soulforge.proxysoul.com/recipes/model-fallback Survive provider outages with per-model fallback chains. When the primary fails on transient errors, swap to the next model automatically. When a provider is overloaded, rate-limited, or returning 5xx, SoulForge can switch to a backup model and keep streaming. The fallback chain is **per-model** — each model in use can have its own ordered list of alternates. ## Open the picker ``` /router ``` Scroll to the **Model Fallback** section. Every model in use (your default plus every Task Router slot) gets its own row. Press Enter to add a fallback, d to clear the chain. ## Or edit the config ```json theme={null} { "defaultModel": "anthropic/claude-sonnet-4-5", "modelFallback": { "anthropic/claude-sonnet-4-5": [ "openrouter/anthropic/claude-sonnet-4.5", "openai/gpt-5" ], "anthropic/claude-haiku-4-5": [ "google/gemini-2.5-flash" ] } } ``` Key = model id, value = ordered list tried in order on transient failure. ## How it cycles ``` primary → retry N times → fallback[0] → retry N → fallback[1] → … → primary (cycle 1) → … → throw after MAX_CYCLES ``` 1. **Transient error retries first.** `maxTransientRetries` (default 3) attempts on the active model with exponential backoff + jitter. 2. **Swap.** Budget exhausted → next model in the chain. A `Switched to fallback model: X` system message lands in chat. The header and active-model header sync to the new model. 3. **Cycle.** When the chain is exhausted, the loop returns to the primary and starts over. Capped at 3 cycles, then the original error is rethrown with `cause`. 4. **User abort wins.** Ctrl+X always exits the loop immediately, regardless of where you are in the chain. ## What counts as transient Matched against the error message: `overloaded`, `429`, `529`, `503`, `502`, `rate limit`, `too many requests`, `timeout`, `fetch failed`, `econnreset`, `econnrefused`, `enotfound`, `socket hang up`, `premature close`, `stream error/closed`, `connection error/reset/refused/closed`, `aborted.*connection`. **403** is **only** treated as transient when the response body also contains `overloaded` or `rate` — a genuine auth-rejection 403 will not burn the retry budget. ## Tune the budgets ```json theme={null} { "retry": { "maxTransientRetries": 3, "maxStallRetries": 3, "baseDelayMs": 1000 } } ``` * `maxTransientRetries` — retries per model before swapping to the next fallback. * `maxStallRetries` — retries when the stream stalls (watchdog only; opt-in). * `baseDelayMs` — first retry delay; doubles each attempt with jitter. Clamped to 250–60000ms. Legacy `maxAttempts` is still accepted as a default for both. ## When fallback shines * **Anthropic overload.** Sonnet 4.5 returns `529 overloaded`. Chain to a different gateway (`openrouter/…`, `llmgateway/…`) or provider. * **One key rate-limited.** First-party Anthropic + LLM Gateway with separate quotas. * **Provider-specific outage.** Mix providers (`anthropic/…` + `openai/…` + `google/…`) so a single-vendor incident doesn't end your session. ## What it won't do * **Cost-based switching.** Fallback is failure-driven, not budget-driven. Use Task Router slots for that. * **Stall detection.** Stalls (no chunks, no abort) hit the watchdog path, which retries on the same model. Fallback only triggers on thrown transient errors. * **Permanent errors.** 401, malformed request, model not found, real 403 — these surface immediately. The chain is for *transient* failures only. # Plan before you code Source: https://soulforge.proxysoul.com/recipes/plan-mode Research, plan, confirm, execute — for bigger refactors. For anything touching more than 2-3 files, use plan mode. The agent researches first, writes a structured plan, waits for your approval, then executes step-by-step. ## Start a plan ``` /plan refactor the auth module to use JWT refresh tokens ``` Or switch to plan mode globally: ``` /mode plan ``` In plan mode, the agent can read but not write. When it's ready, it calls the `plan` tool and the plan appears in the sidebar for review. ## Review the plan * **Approve** — executes the plan step-by-step, with progress in the sidebar. * **Revise** — type what you want changed; the agent rewrites. * **Cancel** — discards the plan. ## Plan types * **Light plan** — a checklist, no code. Fast, low context. * **Full plan** — includes code snippets and old→new diffs. More context, higher confidence on big refactors. The agent picks based on the request; you can override in the prompt ("make a detailed plan"). ## Why plan mode For small fixes, planning is overhead. For multi-file refactors, it catches mistakes before you commit to them — cheaper than re-running an expensive edit session. ## Resume a plan If you close SoulForge mid-plan, `/session continue` picks up where you left off. Plans are saved to `.soulforge/plans/` so they survive restarts. # Rename & refactor Source: https://soulforge.proxysoul.com/recipes/rename-refactor Change a symbol everywhere. Move files without breaking imports. ## Rename a symbol everywhere Just ask: ``` > rename UserService to AccountService ``` The agent runs `rename_symbol` — one LSP-backed call updates every file and verifies there are no dangling references. Works for functions, classes, types, interfaces, variables, enums. No need to tell it which file. ## Move a symbol to a different file ``` > move parseConfig from src/utils.ts to src/config/parser.ts ``` Moves the symbol, updates every import across the project, fixes re-exports. Full support for TypeScript, JavaScript, Python, Rust. ## Rename a file ``` > rename src/old-name.ts to src/utils/new-name.ts ``` Moves the file, updates every importer. ## Extract a function or variable ``` > extract lines 42-58 of handler.ts into a function called validateInput ``` Uses `refactor extract_function` — an LSP-backed code action. The extracted code becomes a new function with the right parameters and return type. ## Organize imports ``` > clean up imports in src/api.ts ``` Sorts, dedupes, removes unused imports via LSP. ## Tips * You don't need to say **which file** for rename. The agent finds it. * If multiple symbols share a name, the agent will ask you to pick. * Run `/lsp status` to confirm your LSP is attached before a big rename. * Changes are checkpoint-saved automatically. `Ctrl+B` undoes them all. # Use multiple models Source: https://soulforge.proxysoul.com/recipes/task-router Cheap model for exploration, strong model for code. Cut cost without losing quality. Assign different models to different tasks. Haiku searches your codebase, Sonnet writes the code, Flash compacts context. ## Open the router ``` /router ``` Or edit `~/.soulforge/config.json`: ```json theme={null} { "taskRouter": { "spark": "anthropic/claude-haiku-4-5", "ember": "anthropic/claude-sonnet-4-5", "webSearch": "anthropic/claude-haiku-4-5", "desloppify": "anthropic/claude-haiku-4-5", "verify": "anthropic/claude-haiku-4-5", "compact": "google/gemini-2.5-flash", "semantic": "anthropic/claude-haiku-4-5", "default": null } } ``` ## What each slot does | Slot | Runs when | Good choice | | ------------ | ----------------------------------- | ---------------------------------- | | `spark` | Read-only agents explore your code | Fast/cheap (Haiku, Flash) | | `ember` | Agents that edit files | Strong coding model (Sonnet, Opus) | | `webSearch` | Web search agents | Fast/cheap | | `desloppify` | Cleanup pass after code edits | Fast/cheap | | `verify` | Adversarial review after code edits | Medium strength | | `compact` | Context compaction | Fast/cheap (Flash is ideal) | | `semantic` | Repo map one-line symbol summaries | Fast/cheap | | `default` | Fallback when no slot matches | — | ## Recommended setups Strong code, cheap everything else. ```json theme={null} "spark": "anthropic/claude-haiku-4-5", "ember": "anthropic/claude-sonnet-4-5", "compact": "google/gemini-2.5-flash" ``` Haiku everywhere. Still very capable for most tasks. ```json theme={null} "spark": "anthropic/claude-haiku-4-5", "ember": "anthropic/claude-haiku-4-5", "compact": "google/gemini-2.5-flash" ``` Sonnet for code, Sonnet for exploration too. ```json theme={null} "spark": "anthropic/claude-sonnet-4-5", "ember": "anthropic/claude-sonnet-4-5", "compact": "google/gemini-2.5-flash" ``` Same models, one key via LLM Gateway. ```json theme={null} "spark": "llmgateway/claude-haiku-4-5", "ember": "llmgateway/claude-sonnet-4-5" ``` ## Why this saves money Agents spend \~70% of their tokens on exploration (reading files, running greps, navigating the Soul Map). That work doesn't need an expensive model. Reserve your strong model for the \~30% that's actually writing code. # Test, lint, commit Source: https://soulforge.proxysoul.com/recipes/test-and-commit Run your toolchain through the agent. Commit with a clean message. ## Run your tests ``` > run tests ``` The agent auto-detects your toolchain (Bun, npm, cargo, go, pytest, gradle, and 20+ others) and runs the right command. Target a specific test: ``` > run only the auth tests ``` ## Lint and auto-fix ``` > lint and fix everything ``` Runs your linter (biome, eslint, oxlint, ruff, clippy, golangci-lint — auto-detected) with the fix flag where supported. ## Typecheck ``` > typecheck ``` Runs `tsc --noEmit`, `mypy`, `pyright`, `cargo check`, `dart analyze`, etc. ## Commit Press `Ctrl+G`, then `c`. Or: ``` > commit my changes ``` SoulForge: 1. Runs lint + typecheck (blocks if either fails). 2. Writes a conventional-commit message based on your diff. 3. Shows you the message and the diff. 4. Commits after you confirm. To skip the pre-commit checks, commit via shell outside SoulForge. ## Co-author trailers Add your LLM as a co-author: ``` /git co-author ``` Toggles between on (adds `Co-Authored-By: `) and off. Saved per-scope. ## The `project` tool Under the hood, all of the above use the `project` tool. If you want to run it directly: | Ask | Runs | | ------------------------------------- | --------------------------------------- | | "run tests" | `project(action: "test")` | | "run the auth tests" | `project(action: "test", file: "auth")` | | "build" | `project(action: "build")` | | "lint and fix" | `project(action: "lint", fix: true)` | | "typecheck" | `project(action: "typecheck")` | | "what packages are in this monorepo?" | `project(action: "list")` | See [project tool](/tools/project) for the full list of supported ecosystems. # Command reference Source: https://soulforge.proxysoul.com/reference/commands 125+ slash commands organized by category. Type / in chat or press Ctrl+K to open the command palette. 125+ slash commands available. Type `/` in chat or press `Ctrl+K` to open the command palette. Sub-commands (like `/proxy login`) work when typed directly but are grouped under their parent in the palette. ## Keybindings | Key | Action | | ------------------- | ----------------------------------------- | | `Ctrl+K` | Command palette — search all commands | | `Ctrl+L` | Switch LLM model | | `Ctrl+E` | Toggle editor panel | | `Ctrl+G` | Git menu | | `Ctrl+P` | Browse sessions | | `Ctrl+S` | Browse skills | | `Ctrl+N` | New session (saves current, starts fresh) | | `Ctrl+T` | New tab | | `Ctrl+W` | Close tab | | `Ctrl+D` | Cycle forge mode | | `Ctrl+X` | Abort generation | | `Ctrl+C` | Copy selection / exit | | `Ctrl+O` | Expand/collapse all (code, reasoning) | | `Ctrl+H` | Command palette (alias) | | `Ctrl+[` / `]` | Prev / next tab | | `Ctrl+1-9` | Switch to tab N | | `Tab` / `Shift+Tab` | Cycle between tabs (from input box) | | `Meta+R` | Error log (Alt+R on Linux) | ## Session | Command | Description | | --------------------------- | ------------------------------------------------------- | | `/session` | Session — history, clear, export, compact | | `/session clear` | Clear chat history | | `/session compact` | Compact conversation context | | `/session continue` | Continue interrupted generation | | `/session export` | Export chat — markdown, json, clipboard, all | | `/session export all` | Full diagnostic export (system prompt, messages, tools) | | `/session export api` | Toggle per-step API request dump | | `/session export clipboard` | Copy chat to clipboard (markdown) | | `/session export json` | Export chat as JSON | | `/session history` | Browse & restore sessions (Ctrl+P) | | `/session new` | Start a new session (saves current) | | `/session rename` | Rename current session | | `/session plan` | Toggle plan mode (research & plan only) | | `/compact` | Compact — run, settings, logs | | `/compact logs` | View compaction events | | `/compact settings` | Compaction strategy & pruning settings | ## Git | Command | Description | | ---------------- | ------------------------------ | | `/git` | Git menu (Ctrl+G) | | `/git branch` | Show/create branch | | `/git co-author` | Toggle co-author trailer | | `/git commit` | Git commit with message | | `/git diff` | Open diff in editor | | `/git init` | Initialize git repo | | `/git lazygit` | Launch lazygit | | `/git log` | Show recent commits | | `/git pull` | Pull from remote | | `/git push` | Push to remote | | `/git stash` | Stash changes — pop to restore | | `/git stash pop` | Pop latest stash | | `/git status` | Git status | ## Models & providers | Command | Description | | -------------------- | ------------------------------------------------------------- | | `/keys` | Manage LLM provider API keys | | `/codex login` | Log in to Codex with your ChatGPT subscription | | `/model-scope` | Set model scope (project/global) | | `/models` | Switch LLM model (Ctrl+L) | | `/provider-settings` | Provider options — thinking, effort, speed, context | | `/proxy` | Proxy — status, install, start, stop, restart, login, upgrade | | `/proxy install` | Reinstall CLIProxyAPI | | `/proxy login` | Add a provider account | | `/proxy logout` | Remove a provider account | | `/proxy restart` | Restart the proxy | | `/proxy start` | Start the proxy | | `/proxy status` | Proxy status & accounts | | `/proxy stop` | Stop the proxy | | `/proxy upgrade` | Upgrade to latest version | | `/router` | Route models per task (code, explore, plan, verify) | | `/web-search` | Web search keys & settings | | `/mcp` | MCP servers — status, tools, reconnect | ## Intelligence | Command | Description | | -------------- | -------------------------------------------------- | | `/context` | Context & system dashboard | | `/diagnose` | Health check — LSP, tree-sitter, semantic indexing | | `/lsp` | Manage LSP servers — install, disable, enable | | `/lsp install` | Install & manage LSP servers (Mason registry) | | `/lsp restart` | Restart LSP servers (all or specific) | | `/lsp status` | LSP status dashboard | | `/memory` | Manage memory scopes, view & clear | | `/repo-map` | Soul map settings (AST index) | | `/skills` | Browse & install skills (Ctrl+S) | | `/tools` | Enable/disable tools for the agent | ## Settings | Command | Description | | ----------------- | ------------------------------------------------------------------------------------- | | `/agent-features` | Toggle agent features (de-sloppify, tier routing) | | `/chat-style` | Toggle chat layout style | | `/diff-style` | Change diff display style | | `/font` | Terminal font — show, set, nerd | | `/font nerd` | Toggle Nerd Font icons | | `/font set` | Set terminal font | | `/instructions` | Toggle instruction files (SOULFORGE.md, CLAUDE.md, etc.) | | `/verbose-tab` | Toggle verbose render for this tab — off folds tools into a rail, on shows raw stream | | `/mode` | Switch forge mode | | `/nvim-config` | Switch neovim config mode | | `/reasoning` | Show or hide reasoning content | | `/settings` | Settings hub — all options in one place | | `/theme` | Switch color theme (live preview) | | `/verbose` | Toggle verbose tool output | | `/vim-hints` | Toggle vim keybinding hints | ## Editor | Command | Description | | ------------------ | -------------------------------------- | | `/editor` | Editor — toggle, open, settings, split | | `/editor open` | Open file in editor | | `/editor settings` | Toggle editor/LSP integrations | | `/editor split` | Cycle editor/chat split (40/50/60/70) | ## Tabs & terminals | Command | Description | | -------------------- | ------------------------------------------------- | | `/changes` | Toggle files changed this session | | `/claim` | Show active file claims across tabs | | `/claim force` | Steal a file claim from another tab | | `/claim release` | Release a file claim from current tab | | `/claim release-all` | Release all claims from current tab | | `/tab` | Tabs — switch, new, close, rename | | `/tab close` | Close current tab (Ctrl+W) | | `/tab new` | Open new tab (Ctrl+T) | | `/tab rename` | Rename current tab | | `/terminals` | Terminal manager — new, close, show, hide, rename | | `/terminals close` | Close a terminal | | `/terminals hide` | Hide terminal panel | | `/terminals new` | Spawn a new terminal | | `/terminals rename` | Rename a terminal | | `/terminals show` | Show terminal panel | ## System | Command | Description | | ----------- | ------------------------------------------------- | | `/errors` | Browse error log | | `/help` | Command palette (Ctrl+K) | | `/hooks` | View active hooks (PreToolUse, PostToolUse, etc.) | | `/privacy` | Manage forbidden file patterns | | `/quit` | Exit SoulForge | | `/restart` | Full restart | | `/setup` | Check & install prerequisites | | `/status` | System status dashboard | | `/storage` | View & manage storage usage | | `/timeouts` | Set tool call timeout (shell, project, agents) | | `/update` | Check for SoulForge updates | | `/wizard` | Re-run the first-run setup wizard | # Configuration Source: https://soulforge.proxysoul.com/reference/configuration Every setting, grouped by what you're trying to do. Config lives in two files. Project wins. | Scope | Path | | ------- | -------------------------- | | Global | `~/.soulforge/config.json` | | Project | `.soulforge/config.json` | ## Full example ```json theme={null} { "defaultModel": "anthropic/claude-sonnet-4-5", "thinking": { "mode": "adaptive" }, "repoMap": true, "semanticSummaries": "ast", "diffStyle": "default", "chatStyle": "accent", "toolTimeout": 2, "compaction": { "strategy": "v2", "triggerThreshold": 0.7, "keepRecent": 4 }, "taskRouter": { "spark": "anthropic/claude-haiku-4-5", "ember": "anthropic/claude-sonnet-4-5", "compact": "google/gemini-2.5-flash" }, "agentFeatures": { "desloppify": true, "tierRouting": true, "dispatchCache": true, "targetFileValidation": true }, "retry": { "maxAttempts": 5, "baseDelayMs": 3000 } } ``` ## Fields ### Model | Field | Default | What it does | | ----------------- | -------- | -------------------------------------------------------------- | | `defaultModel` | `"none"` | Active model ID. `"none"` (default) forces picker on launch. | | `thinking.mode` | `"off"` | `"off"`, `"adaptive"`, `"enabled"` | | `thinking.budget` | — | Tokens when `mode: "enabled"` (1024, 2048, 5000, 10000, 20000) | ### Display | Field | Default | What it does | | ----------- | ----------- | ---------------------------------------- | | `diffStyle` | `"default"` | `"default"`, `"sidebyside"`, `"compact"` | | `chatStyle` | `"accent"` | `"accent"`, `"bubble"` | | `vimHints` | `true` | Show Vim keybinding hints | | `nerdFont` | `true` | Nerd Font icons (off shows ASCII) | ### Intelligence | Field | Default | What it does | | ------------------- | ------- | ------------------------------------------- | | `repoMap` | `true` | Build the codebase graph on launch | | `semanticSummaries` | `"ast"` | `"ast"`, `"llm"`, `"off"` | | `toolTimeout` | `2` | Tool call timeout in minutes. `0` disables. | ### Compaction See [compaction](/context/compaction) for what these do. ```json theme={null} "compaction": { "strategy": "v2", "triggerThreshold": 0.7, "resetThreshold": 0.4, "keepRecent": 4, "maxToolResults": 30, "llmExtraction": true } ``` ### Retry For 429s and transient errors. Delays double each attempt. ```json theme={null} "retry": { "maxAttempts": 5, "baseDelayMs": 3000 } ``` ## Task router Assign different models to different jobs. See the [task router recipe](/recipes/task-router) for tuning tips. ```json theme={null} "taskRouter": { "spark": "anthropic/claude-haiku-4-5", "ember": "anthropic/claude-sonnet-4-5", "webSearch": "anthropic/claude-haiku-4-5", "desloppify": "anthropic/claude-haiku-4-5", "verify": "anthropic/claude-haiku-4-5", "compact": "google/gemini-2.5-flash", "semantic": "anthropic/claude-haiku-4-5", "default": null } ``` | Slot | Runs when | | ------------ | ----------------------------------- | | `spark` | Read-only research agents | | `ember` | Code-writing agents | | `webSearch` | Web search agent | | `desloppify` | Cleanup pass after code edits | | `verify` | Adversarial review after code edits | | `compact` | Context compaction | | `semantic` | Repo map one-line summaries | | `default` | Fallback for background tasks | Resolution: `taskRouter[slot]` → `taskRouter.default` → active model. ## Agent features ```json theme={null} "agentFeatures": { "desloppify": true, "tierRouting": true, "dispatchCache": true, "targetFileValidation": true } ``` | Feature | What it does | | ---------------------- | --------------------------------------- | | `desloppify` | Run cleanup agent after code agents | | `tierRouting` | Auto-route trivial tasks to cheap model | | `dispatchCache` | Share file reads across dispatches | | `targetFileValidation` | Require file paths on dispatch tasks | Toggle in the TUI with `/agent-features`. ## Providers Pick keys and add custom providers on the dedicated pages: * [All providers](/providers/overview) — env vars, `--set-key` commands, keyUrls. * [Custom providers](/providers/custom) — any OpenAI-compatible API. * [Copilot](/providers/copilot) — use your GitHub Copilot subscription. ## Auth & key storage Keys go to your OS keychain when available, with a file fallback otherwise. | Platform | Backend | | --------------------------- | --------------------------------------------------------------------------- | | macOS | Login Keychain (`security`), service `soulforge` | | Linux | libsecret via `secret-tool` (GNOME Keyring, KWallet) | | Linux without `secret-tool` | `~/.soulforge/secrets.json`, mode `0600` | | Windows | DPAPI (`crypt32.dll`) — `%LOCALAPPDATA%\SoulForge\secrets.dat`, user-scoped | | Windows without DPAPI | Plain JSON under same dir — set `SOULFORGE_KEY_PRIORITY=env` to skip disk | | Windows (WSL) | Same as Linux above | `soulforge --set-key ` writes to whichever backend is active. Inspect with `soulforge --list-providers`. ### Lookup order ```json theme={null} { "keyPriority": "env" } ``` | Value | Order | | ----------------- | ------------------------- | | `"env"` (default) | env var → keychain → file | | `"app"` | keychain → file → env var | Use `"app"` when shell-exported keys keep overriding stored ones. ### Pass a key per-launch Env vars work for the TUI too — useful for one-off sessions or CI: ```bash theme={null} LLM_GATEWAY_API_KEY=sk-... soulforge ANTHROPIC_API_KEY=sk-ant-... soulforge ``` Nothing is persisted. There is no `--key` flag — the TUI only reads stored keys and env vars. ### Remove a key `/keys` inside the TUI lists every provider with delete shortcuts. Or delete from the keychain directly (`security delete-generic-password -a soulforge -s anthropic-api-key` on macOS). ## Instruction files Auto-load project rules from AI-tool markdown files: ```json theme={null} { "instructionFiles": ["soulforge", "claude", "cursorrules"] } ``` | Key | File | Default | | ------------- | --------------------------------- | ------- | | `soulforge` | `SOULFORGE.md` | on | | `claude` | `CLAUDE.md` | off | | `cursorrules` | `.cursorrules` | off | | `copilot` | `.github/copilot-instructions.md` | off | | `cline` | `.clinerules` | off | | `windsurf` | `.windsurfrules` | off | | `aider` | `.aider.conf.yml` | off | | `codex` | `AGENTS.md` | off | | `opencode` | `.opencode/instructions.md` | off | | `amp` | `AMPLIFY.md` | off | Toggle in TUI with `/instructions`. ## Privacy Block files from the agent: ``` /privacy add .env /privacy add secrets/** ``` Built-ins already cover `.env`, `.pem`, `credentials`, `id_rsa`, `.npmrc`, `.netrc`, `shadow`, `passwd`. ## Environment variables | Variable | Default | Purpose | | -------------------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | `SOULFORGE_NO_REPOMAP` | unset | Skip the repo-map scan at startup. | | `SOULFORGE_MAX_OUTPUT_TOKENS` | `64000` | Per-step output cap for all agents (forge + subagents + web-search). Raise for reasoning models that hit `finish_reason=length`. | | `SOULFORGE_PRESETS` | unset | Comma-separated preset specs to load on boot. Set automatically by `--plugin ` flags. See [presets](#presets). | | `SOULFORGE_KEY_PRIORITY` | `env` | Key lookup order. `env` = env→keychain→file, `app` = keychain→file→env. | | `SOULFORGE_NO_PROMPT` | unset | Skip first-run addon wizard (set to `1`). Useful in CI. | | `SOULFORGE_DEBUG_API` | unset | Dump per-step API requests to `~/.soulforge/api-export/`. | | `SOULFORGE_DEV_UI` | unset | Show dev-only slash commands (`/ui-demo`, etc). | | `SOULFORGE_ENABLE_GHOSTTY` | unset | Force-enable the embedded floating terminal on Windows (off by default — see [Windows notes](/installation#windows-notes)). | | `SOULFORGE_HEARTH_SOCKET` | `/hearth.sock` | Override the Hearth IPC socket path. | | `SOULFORGE_HEARTH_APPROVAL_TIMEOUT_MS` | `300000` | Approval timeout for remote tool calls. | | `SOULFORGE_HEARTH_DENY_READ_REMOTE` | unset | Block read-only tools from remote surfaces (set to `1`). | | `SOULFORGE_PROXY_VERSION` | latest | Pin a CLIProxyAPI version when installing the proxy addon. | | `SOULFORGE_AUTO_INSTALL_ADDONS` | unset | Comma-separated addons to install automatically (`proxy,neovim`). | ## Hooks Covered on the [hooks page](/tools/hooks). Short version: SoulForge reads `hooks.{Event}` from all 5 config sources (Claude Code's three + SoulForge's two), fires all matches. ## Storage `/storage` shows per-component disk usage (repo map, sessions, plans, memory, input history, binaries, fonts) with one-click cleanup. ## Scope priority Session → Project → Global. Changes via commands land in whatever scope the command targets. Use `/model-scope` to move the active model between project and global. # Presets Source: https://soulforge.proxysoul.com/reference/presets Shareable config bundles loaded by name, path, or URL. Presets are reusable config bundles — task router rules, agent features, theme, instruction files, custom providers — that merge into your `AppConfig` at boot. Useful for syncing settings across machines, sharing a team setup, or pinning a known-good config to a repo. ## Three ways to load Presets resolve in this order — later wins: 1. **Global** — `~/.soulforge/config.json` → `presets[]` 2. **Project** — `/.soulforge/config.json` → `presets[]` 3. **CLI** — `--plugin ` (stackable) or `SOULFORGE_PRESETS=spec1,spec2` ```json theme={null} { "presets": [ "team/standard", "./presets/local.json", "https://example.com/forge-preset.json" ] } ``` ```bash theme={null} # One-off CLI overlay (multiple --plugin flags stack) soulforge --plugin team/standard --plugin ./extra.json # Same via env SOULFORGE_PRESETS=team/standard,./extra.json soulforge ``` ## Spec formats | Form | Example | | ------------- | ---------------------------------------- | | Registry id | `team/standard` | | Absolute path | `/etc/forge/preset.json` | | Relative path | `./presets/local.json`, `../shared.json` | | Home path | `~/forge/preset.json` | | URL | `https://example.com/preset.json` | Specs must be at least 2 characters and match `^(https?:\/\/|\.\/|\.\.\/|\/|~\/)|^[a-zA-Z0-9][a-zA-Z0-9._/-]{1,}$`. Invalid specs are logged and skipped. ## Wizard ```bash theme={null} soulforge --presets # or soulforge presets ``` Interactive picker — browse the registry, preview overlays, add to global or project scope. ## Boot output By default preset resolution is silent on success. Failures print one short line to stderr (one bad preset never blocks boot — fail-open). For full traces: ```bash theme={null} soulforge --verbose-presets ``` You'll see one line per spec (`ok` or `fail`) with the resolved source. ## How merging works Presets are resolved to plain `AppConfig` patches, then merged on top of `DEFAULT_CONFIG` in load order. The diff against defaults becomes the `presetOverlay` — applied before `loadConfig()` returns, so every consumer sees the merged shape transparently. Your `~/.soulforge/config.json` is never rewritten. Removing the preset entry restores the prior behaviour on next launch. ## Authoring A preset is a JSON file (or registry-hosted JSON) with any subset of `AppConfig` fields: ```json theme={null} { "name": "team-standard", "description": "Team default router + theme + privacy", "config": { "theme": { "name": "catppuccin" }, "taskRouter": { "spark": "anthropic/claude-haiku-4-5", "ember": "anthropic/claude-sonnet-4-5" }, "agentFeatures": { "desloppify": true, "tierRouting": true }, "forbiddenPatterns": ["*.env", "secrets/**"] } } ``` Drop it under `~/.soulforge/presets/.json` to make it discoverable, or host anywhere reachable by HTTPS. ## Environment | Variable | Purpose | | ------------------- | -------------------------------------------------------------- | | `SOULFORGE_PRESETS` | Comma-separated specs (set automatically by `--plugin` flags). | ## See also * [Configuration](/reference/configuration) — the AppConfig shape presets layer onto. * [Task router](/recipes/task-router) — the most-changed slot in shared presets. # Telemetry & Privacy Source: https://soulforge.proxysoul.com/reference/telemetry What anonymous usage data SoulForge collects, and how to turn it off. SoulForge sends a single, anonymous "I'm running" ping when a session starts so we can count active installs and see which versions and platforms are in use. That's the entire purpose — no profiling, no tracking, no content. **Nothing personal or content-bearing ever leaves your machine.** No prompts, no code, no file contents, no file paths, no cwd, no hostname, no username, no API keys, no model IDs, and no IP address in the payload. ## What we send One fire-and-forget HTTPS ping per session start, with only these fields: | Field | Example | Why | | ---------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `event` | `session_start` | Distinguishes the ping type | | `surface` | `tui` · `headless` · `hearth` | Which entry point launched | | `version` | `2.20.1` | Which release is in use | | `os` | `darwin` · `linux` · `win32` | Platform breakdown | | `arch` | `arm64` · `x64` | Architecture breakdown | | `install` | `brew` · `npm` · `binary` … | How it was installed | | `family` | `claude` · `openai` · `google` … | Model **family** | | `provider` | `anthropic` · `llmgateway` · `openrouter` · `ollama` … | Which provider/gateway — built-in id, or `custom` for self-configured ones. Never a custom URL or key | | `model` | `claude-sonnet-4-5` · `gpt-5` · `gemini-2.5-pro` | Public base model name — sent **only** when it matches a known public-model pattern under a built-in provider. Any free-form or custom model string (even under a real provider prefix) collapses to `other`, so it can never carry secrets, org, or project names | | `id` | random UUID | Counts *distinct installs*, not people (see below) | The receiving edge also records a coarse `country` (from the network edge, never your IP) for a rough geographic breakdown. ### The anonymous id `id` is a random UUID stored at `~/.soulforge/anon-id.json`. It exists so we can tell "1 install pinged 50 times" apart from "50 installs" — i.e. count active users, not raw requests. It is: * **Random** — not derived from your machine, account, or anything identifiable. It identifies an *install*, not a person. * **Stable** — the same id persists across runs, so distinct-id counts give exact daily/monthly active users, unique installs, and retention. * **Local** — never linked to any other data. Delete the file (or reinstall) and you get a fresh id. ## What we never send Prompts · code · file contents · file paths · working directory · hostname · username · API keys or tokens · model IDs · custom provider URLs · your IP address. ## How to opt out Any **one** of these disables telemetry completely: ```json config.json theme={null} { "telemetry": false } ``` ```bash Environment variable theme={null} # SoulForge-specific export SOULFORGE_TELEMETRY=0 # Or the cross-tool industry standard (respected by many CLIs) export DO_NOT_TRACK=1 ``` When disabled, no network request is made and no id is created or read. ## Design notes * **Fail-safe.** The ping is fire-and-forget with a 1.5s timeout. It never blocks startup, never retries, and never surfaces an error — if you're offline or the endpoint is down, SoulForge behaves identically. * **First-run notice.** The first time telemetry runs, a one-line notice is printed to stderr telling you it's on and how to turn it off. * **Self-hostable.** Point the beacon at your own endpoint with `SOULFORGE_TELEMETRY_URL`. * **No secrets shipped.** The client carries only a public, unauthenticated beacon URL — no API key, account id, or token. Reading the data requires a separate Cloudflare credential that never touches the client or the repo. * **Abuse-resistant.** The endpoint is write-only into an analytics store (no database to read back or corrupt), with strict field allow-lists, per-IP rate limiting, and query-time dedup on the anonymous id. * **Open.** The client lives in `src/core/telemetry.ts` and the receiving Cloudflare Worker is in `telemetry-worker/` — both in the public repo. # Sponsors & Backers Source: https://soulforge.proxysoul.com/sponsors The people and companies keeping the Empryo fire lit. Empryo is fueled by people and companies who believe code deserves to be treated as code. Every name below kept the forge running. Sponsor on GitHub ## Featured sponsors Premium partners. Logos appear in the README and on this page. LLM Gateway LLM Gateway One API for OpenAI, Anthropic, Google, and 30+ providers. **Up to 30% off frontier models**, real-time cost analytics, bring-your-own-keys, open-source and self-hostable. Empryo ships an `llmgateway` provider out of the box. ## Hall of fire Recognition for every sponsor who keeps the forge running, organized by tier. Top-of-README logo, featured docs banner, dedicated launch post, quarterly roadmap calls. *None yet — be the first.* Premium README placement, docs homepage logo, social shoutouts, direct line for integration. *None yet.* README Supporters row, docs supporters page, priority on bugs and features. *None yet.* Name in BACKERS.md, release notes shoutout, early changelog peeks. *None yet.* Sponsor badge, name listed next to the others keeping the fire lit. * **Levi** ([@feketefh](https://github.com/feketefh)) ## Permanent backers One-time supporters at the **\$200+ tier** — listed permanently, not just for the active month. * **[LLM Gateway](https://llmgateway.io)** ([@theopenco](https://github.com/theopenco)) — one API for OpenAI, Anthropic, Google, and 30+ providers. **Up to 30% off frontier models**, open-source alternative to OpenRouter with self-hosting, real-time analytics, and bring-your-own-keys. Empryo ships an `llmgateway` provider out of the box. ## Coin in the jar One-time supporters at the \$10 / \$50 tier. Thank you for tossing a coin in — every one of you powered a few thousand tokens of agent work. * [@nerminba](https://github.com/nerminba) * [@Liiro](https://github.com/Liiro) *** Recurring tiers from \$5 to \$1,000. Cancel anytime. Prefer a single contribution? GitHub Sponsors supports one-time too. Toss a coin in the jar. Get listed in BACKERS.md. # Addons Source: https://soulforge.proxysoul.com/tools/addons Optional bundled components — CLIProxyAPI and Neovim — fetched on demand. Addons are opt-in components that ship outside the main binary. They're downloaded the first time you ask for them, recorded in your global config, and managed entirely from the CLI. Two addons today: | Addon | What it gets you | Approx size | | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | `proxy` | [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI) — local LLM relay that lets you route Claude, GPT, Gemini, and Grok through your existing chat subscriptions instead of API keys. | \~25 MB | | `neovim` | Bundled Neovim binary for the embedded editor panel, when you don't want to install Neovim system-wide. | \~15 MB | Neither is required. The default install ships neither. ## First-run wizard On first launch in a TTY, SoulForge offers to install both. Skip and re-run later — your choice is remembered. ```bash theme={null} # Skip the prompt in CI / non-interactive shells SOULFORGE_NO_PROMPT=1 soulforge ``` ## CLI ```bash theme={null} soulforge addon list # show state (installed / version / path) soulforge addon install proxy # install the proxy addon soulforge addon install neovim # install bundled neovim soulforge addon remove proxy # uninstall (leaves system PATH copies alone) soulforge addon update neovim # reinstall latest soulforge addon --help # full usage ``` Both positional (`soulforge addon …`) and flag (`soulforge --addon …`) forms work. `list`, `ls`, `--list`, `-l` are all equivalent. ## Install locations | Path | What | | ---------------------------------------------- | --------------------------------------------------------------------------- | | `~/.soulforge/bin/cli-proxy-api` | proxy binary symlink (Windows: `.exe` under `%LOCALAPPDATA%\SoulForge\bin`) | | `~/.soulforge/bin/nvim` | neovim binary symlink | | `~/.soulforge/installs/cliproxyapi-/` | versioned proxy install | | `~/.soulforge/installs/nvim-/` | versioned neovim install | Install records live under `addons` in `~/.soulforge/config.json`: ```json theme={null} { "addons": { "proxy": { "installed": true, "version": "1.4.2", "installedAt": "2025-01-15T12:00:00Z" }, "neovim": { "installed": true, "installedAt": "2025-01-15T12:00:00Z" } } } ``` ## System binaries If `cli-proxy-api` or `nvim` is already on your `PATH` (Homebrew, apt, scoop, etc.), SoulForge picks it up automatically — you don't need the addon at all. `addon install` will still vendor a separate copy under `~/.soulforge/bin` to insulate against PATH changes; `addon remove` only removes what soulforge vendored, never your system install. ## Environment variables | Variable | Purpose | | ------------------------------- | --------------------------------------------------------------- | | `SOULFORGE_AUTO_INSTALL_ADDONS` | Comma-separated list to install automatically (`proxy,neovim`). | | `SOULFORGE_PROXY_VERSION` | Pin a specific CLIProxyAPI version on install. | | `SOULFORGE_NO_PROMPT` | Set to `1` to skip the first-run addon wizard. | ## Proxy quickstart ```bash theme={null} soulforge addon install proxy soulforge # then inside the TUI: /proxy login # add a Claude / GPT / Gemini / Grok / Antigravity subscription /proxy status # see active accounts ``` See the [Proxy provider](/providers/overview#enterprise) entry for how the relay shows up in `/models`. # AST editing Source: https://soulforge.proxysoul.com/tools/ast-edit The first AST-native AI editor. Edits by symbol, not string. `ast_edit` is the first AI coding tool to edit TypeScript and JavaScript through the AST instead of text matching. It addresses symbols by kind and name, mutates them structurally, and writes the result back. Micro-edits cost a handful of tokens and never fail on whitespace. You don't invoke `ast_edit` directly. The agent picks it automatically for TypeScript and JavaScript files. This page is a reference for what it can do. ## Origin The AST-first approach comes from my own Master's thesis in Computer Science, [*Typed vs Untyped Programming Languages*](https://www.diva-portal.org/smash/record.jsf?pid=diva2%3A1690910\&dswid=-6127) (co-authored with Artur Matusiak, 2022), and its reference implementation [JS Typer](https://github.com/proxysoul/Javascript-Typer). The thesis demonstrated that programmatic JavaScript-to-TypeScript conversion could be done entirely through ts-morph AST mutations rather than string manipulation. `ast_edit` is the direct descendant of that work, applied to AI coding. ## Examples Change a function's return type: ```ts theme={null} ast_edit({ path: "src/api.ts", action: "set_return_type", target: "function", name: "fetchUser", value: "Promise", }) ``` Make a function async and add a parameter — one atomic batch: ```ts theme={null} ast_edit({ path: "src/api.ts", operations: [ { action: "set_async", target: "function", name: "fetchUser", value: "true" }, { action: "add_parameter", target: "function", name: "fetchUser", value: "cache: boolean" }, { action: "add_named_import", value: "./types", newCode: "User" }, ], }) ``` Rename a class method: ```ts theme={null} ast_edit({ path: "src/store.ts", action: "rename", target: "method", name: "UserStore.fetch", value: "load", }) ``` Replace a whole function body: ```ts theme={null} ast_edit({ path: "src/util.ts", action: "set_body", target: "function", name: "parseInput", newCode: "return JSON.parse(raw);", }) ``` Batch edits are all-or-nothing — if any fails, none are applied. ## Targets | Target | Addressed by | | ------------------------------------------------------------------------ | -------------------------------------- | | `function`, `class`, `interface`, `type`, `enum`, `variable`, `constant` | `name` | | `method`, `property` | `"ClassName.member"` or `"member"` | | `arrow_function` | `name` (targets `const fn = () => {}`) | | `constructor` | `ClassName` | ## Action reference | Action | Effect | | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | | `set_type` | Variable/property/parameter type annotation | | `set_return_type` | Function return type | | `set_initializer` / `remove_initializer` | Variable initializer | | `set_value` | Enum member value | | `set_async` / `set_generator` | Toggle `async` / `function*` | | `set_export` / `set_default_export` | Toggle export modifiers | | `set_abstract` / `set_static` / `set_readonly` / `set_scope` / `set_optional` / `set_overrides` / `set_ambient` | Class-member modifiers | | `set_const_enum` / `set_declaration_kind` | Misc modifiers | | `rename` | Declaration-only rename | | `remove` | Delete the declaration | | `add_parameter` / `remove_parameter` | Function/method parameters | | Action | Effect | | ----------------------------------------------------------------------------------------- | ---------------------------- | | `set_body` | Replace function/method body | | `add_statement` / `insert_statement` / `remove_statement` | Statement-level | | `add_property` / `remove_property` | Interface/class properties | | `add_method` / `remove_method` / `add_constructor` / `add_getter` / `add_setter` | Class members | | `add_member` / `remove_member` | Enum / type-literal members | | `add_decorator` / `remove_decorator` | Decorators | | `add_overload` | Function overload signatures | | `set_extends` / `add_extends` / `remove_extends` / `add_implements` / `remove_implements` | Inheritance | | `add_type_parameter` | Generic type parameters | | `add_jsdoc` / `remove_jsdoc` | JSDoc blocks | | `unwrap`, `set_structure`, `extract_interface` | Structural rewrites | | Action | Effect | | ----------------- | -------------------------------------------------- | | `replace` | Replace a whole symbol | | `replace_in_body` | AST-anchored substring replace (unique match only) | | Action | Effect | | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | | `create_file` | New file (pass `newCode` as content) | | `add_import` / `remove_import` / `add_named_import` / `remove_named_import` | Imports | | `set_module_specifier` | Change import path | | `add_export_declaration` / `add_named_reexport` | Re-exports | | `add_namespace` | Namespace declarations | | `organize_imports` / `fix_missing_imports` / `fix_unused` | Cleanup | | `add_function` / `add_class` / `add_interface` / `add_type_alias` / `add_enum` / `add_variable` | Append top-level declarations | | `insert_text` | Insert at an anchor (`"after-imports"`, `"before-exports"`, index) | ## Supported files `.ts`, `.tsx`, `.js`, `.jsx`, `.mts`, `.cts`, `.mjs`, `.cjs`. For other files the agent falls back to `edit_file` / `multi_edit`. ## Safety * File snapshotted before the edit — rejects if changed on disk mid-flight. * Atomic batches: all or none. * `undo_edit` reverses `ast_edit` the same as `edit_file`. * Pre/post diagnostics — new type errors flow back to the agent immediately. # Checkpoints Source: https://soulforge.proxysoul.com/tools/checkpoints Git-backed undo/redo for agent actions. Browse, rewind, and branch from any point in the conversation. Checkpoints give you undo/redo for agent actions. Every time you send a prompt and the agent responds, a checkpoint is created. You can browse past checkpoints, undo to an earlier point, or branch from a previous checkpoint by sending a new message. ## Browsing Use `Ctrl+B` and `Ctrl+F` to step backward and forward through checkpoints. | Shortcut | Action | | -------- | -------------------------------------- | | `Ctrl+B` | View previous checkpoint | | `Ctrl+F` | View next checkpoint (or back to live) | When viewing a past checkpoint, messages after that point are dimmed and a separator appears: ``` ↶ Viewing checkpoint #2, send a message to rewind here. ``` Browsing is instant and read-only — no files are changed, no git operations run. Press `Ctrl+F` past the last checkpoint to return to live view. ## Branching Send a message while viewing a past checkpoint to **branch** from it. The conversation is truncated to that checkpoint's messages, and your new message continues from there. Later messages are discarded from the model context but remain visible (dimmed) in the chat. This is the primary workflow for recovering from a wrong direction — go back to where things were good and try a different approach. ## Undo and redo `/checkpoint undo` reverts the last checkpoint — both the conversation state and the files on disk. | Command | Description | | -------------------- | ----------------------------------- | | `/checkpoint` | List all checkpoints | | `/checkpoint N` | View checkpoint N | | `/checkpoint live` | Back to live view | | `/checkpoint undo` | Undo last checkpoint | | `/checkpoint undo N` | Undo to checkpoint N | | `/checkpoint redo` | Redo last undone checkpoint | | `/checkpoint save` | Force save current state as git tag | Undone checkpoints stay visible in the chat (dimmed) with a separator: ``` ↶ Rewound past this point. ``` Sending a new message after an undo clears the redo stack, like any standard undo/redo system. ## Checkpoint rail A vertical rail on the right edge of the chat shows checkpoint dots: | Dot | Meaning | | ------- | ------------------------------------ | | Colored | Completed checkpoint with file edits | | Muted | Completed checkpoint (read-only) | | Spinner | Currently running | | Hollow | Undone checkpoint | | Orange | Currently viewed | When there are more checkpoints than the rail can display, `+N` indicators show how many are hidden above and below. The visible window follows your navigation. ## Git integration Checkpoints that include file edits are automatically git-tagged when the agent finishes. File restore uses per-file recovery (`git show`) — only files edited in the undone checkpoints are reverted. Other tabs' files are never touched. **No git repo?** Checkpoints still work for browsing and conversation undo. File restore is silently skipped — the agent will re-apply changes based on the rewound conversation. ## Sessions Checkpoint git tags are saved with sessions and restored when a session is loaded. Deleting a session cleans up its associated git tags. ## Tabs Each tab has its own independent checkpoint history. Git tags are namespaced by tab ID so they never collide. Undoing in one tab has no effect on other tabs. ## Steering Steering messages (typed into the input while the agent is working) do not create new checkpoints. They fold into the current running checkpoint. # Editor & Neovim Source: https://soulforge.proxysoul.com/tools/editor Embedded Neovim with your config, plugins, and LSP — plus 18 editor tool actions for the AI agent. SoulForge embeds a real Neovim instance — your config, your plugins, your LSP servers. The AI agent can read, edit, navigate, and inspect code through the same editor you use. ## Overview The editor panel runs alongside the chat. Toggle it with `Ctrl+E` or `/editor`. The agent accesses it through the `editor` tool with 18 actions covering reading, editing, navigation, LSP operations, and terminal output. ### Key shortcuts | Key | Action | | --------------------- | ------------------------------------------- | | `Ctrl+E` | Toggle editor panel | | `/editor` | Editor menu — toggle, open, settings, split | | `/editor open ` | Open a file in the editor | | `/editor settings` | Toggle editor/LSP integrations | | `/editor split` | Cycle editor/chat split ratio (40/50/60/70) | ## Neovim configuration SoulForge supports three Neovim config modes, selectable via `/nvim-config`: | Mode | Behavior | | --------- | ------------------------------------------------------------------- | | `default` | SoulForge's built-in config (LazyVim-based, Mason LSP auto-install) | | `user` | Your personal Neovim config (`~/.config/nvim`) | | `none` | Minimal config — no plugins, no LSP | On first launch with `default` mode, SoulForge bootstraps LazyVim plugins and installs LSP servers via Mason for all detected project languages. This is a one-time setup. ## Editor tool actions The `editor` tool provides 18 actions for the AI agent. These require the editor panel to be open (`Ctrl+E`). ### Reading & inspection | Action | Parameters | Description | | ----------------- | --------------------------------- | -------------------------------------------------------------- | | `read` | `startLine?`, `endLine?`, `file?` | Read buffer content (optionally a specific line range or file) | | `cursor_context` | — | Get current file, cursor position, and visual selection | | `buffers` | — | List all open buffers | | `terminal_output` | `count?` | Read terminal output (default: last 100 lines) | ### Editing | Action | Parameters | Description | | ----------- | ---------------------------------------------- | ---------------------------------------------------- | | `edit` | `startLine`, `endLine`, `replacement`, `file?` | Replace lines in the buffer | | `select` | `startLine`, `endLine` | Select a range of lines | | `yank` | `text?`, `register?` | Put text into a register (default: system clipboard) | | `highlight` | `startLine`, `endLine` | Highlight a range of lines | ### Navigation | Action | Parameters | Description | | ------------- | ----------------------------------- | -------------------------------------- | | `navigate` | `file?`, `line?`, `col?`, `search?` | Open a file, jump to a line, or search | | `goto_cursor` | `line`, `col?` | Move cursor to a specific position | ### LSP operations | Action | Parameters | Description | | ------------- | ------------------------ | ------------------------------------------------------ | | `diagnostics` | — | Get errors and warnings for the current buffer | | `symbols` | — | List document symbols (functions, classes, variables) | | `hover` | `line`, `col` | Get type info and documentation at a position | | `references` | `line`, `col` | Find all references to the symbol at a position | | `definition` | `line`, `col`, `jump?` | Go to definition of the symbol at a position | | `actions` | `line`, `col?`, `apply?` | Get available code actions (quick fixes, refactorings) | | `rename` | `line`, `col`, `newName` | Rename a symbol across the workspace | | `format` | `startLine?`, `endLine?` | Format the buffer (or a range) via LSP | ## Editor integrations Configure which LSP features the agent can access via `/editor settings`: | Integration | Default | Description | | ----------------- | ------- | -------------------------------------------------------- | | LSP Diagnostics | on | Errors & warnings from LSP | | Document Symbols | on | Functions, classes, variables | | Hover / Type Info | on | Type info at cursor position | | Find References | on | All usages of a symbol | | Go to Definition | on | Jump to symbol definition | | Code Actions | on | Quick fixes & refactorings | | LSP Rename | on | Workspace-wide symbol rename | | LSP Status | on | Check attached LSP servers | | LSP Format | on | Format buffer via LSP | | Editor Context | on | File/cursor/selection in system prompt | | Sync on Edit | on | Jump to edited files (off = only refresh current buffer) | ### Agent access modes Control when the agent can use the editor tool: | Mode | Behavior | | ------------------ | ------------------------------------------------------ | | `Always` | Agent can always use the editor tool | | `Never` | Editor tool is disabled for the agent | | `When editor open` | Agent can only use it when the editor panel is visible | ## Dual LSP backend The agent always has LSP access regardless of editor state: * **Neovim bridge** — When the editor is open, LSP requests route through Neovim's running servers via Lua RPC. Zero startup cost since servers are already warm. * **Standalone client** — When the editor is closed, SoulForge spawns LSP servers directly as child processes. Full protocol support. Both backends support: definitions, references, hover, diagnostics, rename, code actions, call/type hierarchy, and formatting. ### Mason auto-install On first editor launch, SoulForge detects project languages and installs appropriate LSP servers via Mason. Servers are stored in `~/.soulforge/lsp-servers/` and discovered automatically. ## Vim hints Toggle Neovim keybinding hints in the UI with `/vim-hints`. When enabled, common Vim keybindings are shown in the editor footer. ## Split ratios Cycle the editor/chat split with `/editor split`: | Ratio | Editor | Chat | | ----- | ------ | ---- | | 40% | 40% | 60% | | 50% | 50% | 50% | | 60% | 60% | 40% | | 70% | 70% | 30% | # Hearth — Remote control [experimental] Source: https://soulforge.proxysoul.com/tools/hearth Reach your SoulForge from Telegram or Discord. (Experimental — expect rough edges.) Hearth turns your running SoulForge into a remote coding agent. Send prompts from your phone, approve edits with inline buttons, keep coding from anywhere. Your code never leaves your host. Hearth only opens outbound connections to messaging platforms. ## Start in 4 steps ```bash theme={null} soulforge hearth start ``` Leave this running. It listens on a UNIX socket (`0600` permissions, local only). Create a bot on [@BotFather](https://t.me/BotFather) (Telegram) or the [Discord Developer Portal](https://discord.com/developers/applications). ```bash theme={null} # pipe the token — never appears in ps output: cat token.txt | soulforge hearth login telegram: ``` ```bash theme={null} soulforge hearth pair telegram: --issue # prints a 6-character code ``` DM your bot and type: ``` /pair ABC123 ``` Any message you send the bot now goes to your forge. Tool calls that need approval arrive as inline buttons — tap Approve or Deny. ## Commands (in the chat) | Command | What it does | | -------------- | ---------------------- | | `/pair ` | Redeem a pairing code | | `/new [label]` | Open a new tab (max 5) | | `/tab [id]` | List or switch tabs | | `/stop` | Abort the current turn | | `/close [id]` | Close a tab | | `/help` | Show commands | Plain text becomes a prompt to the active tab. ## CLI reference ```bash theme={null} soulforge hearth start # run the daemon soulforge hearth status # daemon health + paired chats soulforge hearth login : [token] # store a bot token soulforge hearth pair : --issue # mint a pairing code soulforge hearth pair : # redeem from CLI soulforge hearth unpair : # revoke a chat soulforge hearth doctor # diagnose setup soulforge hearth logs [--follow] # tail daemon log ``` Inside SoulForge, `/hearth` opens the settings panel with the same controls. ## Always-on (service) Run Hearth as a background service so your forge is reachable even after logout: ```bash theme={null} # macOS (launchd) / Linux (systemd) — auto-detected soulforge hearth install # install service soulforge hearth install --now # install and start ``` Check status and surface owner at any time: `soulforge hearth status`. ## Surfaces | Surface | Platform | Identity | | ------------ | -------- | --------------------------------- | | **Telegram** | Any OS | Bot + numeric `from.id` allowlist | | **Discord** | Any OS | Bot + user `snowflake` allowlist | Unknown senders are silently dropped — their existence is not revealed. ## Security Hearth layers several protections on top of SoulForge's existing hook system: Only paired identities can send. Unknown senders are dropped. Destructive tool calls (edit, shell, git) arrive as tap-to-approve buttons. Bot tokens, API keys, PATs, AWS keys, bearer tokens — auto-redacted from all logs. `.env`, `*.pem`, `~/.ssh/**`, `~/.aws/credentials` — blocked before every read. Optional: route destructive ops through Docker with `caps: "sandboxed"`. Tokens live in macOS Keychain / Linux secret-tool. Never in plain config. ## Config Default config lives at `~/.soulforge/hearth.json`. Most users never edit it — the CLI manages everything. For advanced setups (multiple chats, read denylists, capability caps), see the [config schema](https://github.com/proxysoul/soulforge/blob/main/src/hearth/config.ts). ## Non-goals * **No cloud.** Hearth is "reach your host" — not a hosted service. Your machine must be running. * **No WhatsApp.** Terms of service forbid it. * **No concurrent writers.** Reads ok across chats; writes are single-owner per tab. # Hooks Source: https://soulforge.proxysoul.com/tools/hooks Run shell commands on agent events. Claude Code compatible. Hooks fire shell commands at lifecycle events — before/after tool calls, on session start/stop, around compaction. Wire-compatible with Claude Code, so your existing `.claude/settings.json` hooks work as-is. ## Quick example Auto-format every TypeScript file the agent edits: ```json .soulforge/config.json theme={null} { "hooks": { "PostToolUse": [ { "matcher": "Edit|MultiEdit", "hooks": [ { "type": "command", "command": "prettier --write $(echo $HOOK_TOOL_INPUT | jq -r '.path // empty')", "async": true } ] } ] } } ``` `/hooks` inside SoulForge lists active hooks and toggles them per session. ## Events | Event | Fires | Matches | | -------------------------------- | ---------------------- | --------- | | `PreToolUse` | Before a tool runs | Tool name | | `PostToolUse` | After a tool succeeds | Tool name | | `PostToolUseFailure` | After a tool fails | Tool name | | `UserPromptSubmit` | On user message | — | | `Stop` / `StopFailure` | Turn ends | — | | `SessionStart` / `SessionEnd` | Session boundary | — | | `PreCompact` / `PostCompact` | Around compaction | — | | `SubagentStart` / `SubagentStop` | Spark/ember spawn/exit | — | | `Notification` | System notifications | — | ## Rule schema ```json theme={null} { "matcher": "Bash|Edit", // tool name, pipe-separated or regex "hooks": [ { "type": "command", "command": "my-hook.sh", "async": false, // run in background "timeout": 10, // seconds (default 10) "once": false, // fire once per session "if": "Bash(git *)" // extra glob filter } ] } ``` The `if` field narrows by first string arg: `Bash(rm *)`, `Edit(*.ts)`, etc. `*` matches any sequence, `?` one char. Tool names use Claude Code conventions (`Bash`, `Edit`, `Write`, `Read`, `Grep`, `Glob`, `WebSearch`, `Agent`). SoulForge maps its internal names automatically. ## Config sources (merged in order) 1. `~/.claude/settings.json` 2. `.claude/settings.json` 3. `.claude/settings.local.json` 4. `~/.soulforge/config.json` 5. `.soulforge/config.json` Set `"disableAllHooks": true` in any file to kill all hooks. ## Recipes ```json theme={null} { "matcher": "Bash", "hooks": [{ "type": "command", "command": "echo '{\"decision\":\"block\",\"reason\":\"rm -rf blocked\"}' && exit 2", "if": "Bash(rm -rf *)" }] } ``` Goes under `PreToolUse`. Exit code `2` denies the call. ```json theme={null} { "hooks": [{ "type": "command", "command": "jq -r '.tool_name' >> /tmp/sf-tools.log", "async": true }] } ``` Goes under `PreToolUse`. No matcher = all tools. ```json theme={null} { "hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Done\" with title \"SoulForge\"'", "async": true, "once": true }] } ``` Goes under `Stop`. ## Protocol Hooks read JSON from stdin, optionally write JSON to stdout. Exit code `0` = success, `2` = block, anything else = log warning. **stdin** ```json theme={null} { "session_id": "abc", "cwd": "/proj", "hook_event_name": "PreToolUse", "tool_name": "Bash", "tool_input": { "command": "git status" } } ``` **stdout** (optional — `PreToolUse` can deny, modify input, inject context) ```json theme={null} { "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Blocked by policy", "updatedInput": { "command": "git status --short" }, "additionalContext": "This repo uses trunk-based dev" } } ``` `PostToolUse` can inject `additionalContext` — no deny. ## Safety * Hooks die on `Ctrl+X` along with the agent. * Default timeout 10s — hooks should be fast. * Non-blocking errors (non-zero exit ≠ 2) log a warning, don't stop the agent. # LSP servers Source: https://soulforge.proxysoul.com/tools/lsp Install, manage, and inspect LSP servers from the TUI. SoulForge's code intelligence runs a full LSP layer. It ships a Mason-backed installer so you can add servers for any language from the TUI without touching your Neovim config. ## Commands ``` /lsp open the LSP status dashboard /lsp install search and install servers (Mason registry, 576+ packages) /lsp status attached servers with PID, language, capabilities /lsp restart restart all, or filter by language ``` ## What the agent uses Every [code intelligence](/concepts/intelligence) operation tries LSP first: * Go-to-definition * Find references * Workspace-wide rename * Diagnostics (errors, warnings, info) * Code actions (quick fixes, refactors) * Call hierarchy (incoming and outgoing) * Type hierarchy (super and subtypes) * Hover and type info * Format document and format range No manual setup required. The agent picks the right tool automatically. ## Dual backend SoulForge runs LSP in two modes, whichever is available: | Mode | When | | ----------------- | --------------------------------------------------------------------------------------------------------------------- | | **Neovim bridge** | Editor panel open. Routes through your running Neovim LSP clients. Zero warm-up cost. | | **Standalone** | Editor panel closed, or Neovim not installed. SoulForge spawns LSP servers directly into `~/.soulforge/lsp-servers/`. | Both cover the same capabilities. Standalone servers stay warm as hot standby even when Neovim is open. ## Supported languages Typical LSP coverage (any Mason package works): TypeScript, JavaScript, Python, Go, Rust, Java, Kotlin, Scala, C#, Swift, Dart, Elixir, OCaml, Lua, C, C++, Ruby, PHP, Zig, Bash, CSS, HTML, JSON, TOML, YAML, Dockerfile, Vue. Tree-sitter detection adds ReScript, Solidity, TLA+, Emacs Lisp, Objective-C, Nix, HCL/Terraform, Bazel/Starlark, Jsonnet, Makefile, GraphQL, Protobuf, SQL, Markdown, MDX, XML, and more. For anything LSP doesn't cover, the intelligence router falls through to ts-morph (TypeScript/JavaScript AST), tree-sitter, and regex. ## Mason installer `/lsp install` opens a fuzzy-search browser over Mason's registry. Pick a package, press enter, SoulForge installs it: * **npm** packages to `~/.soulforge/lsp-servers/node_modules/` * **pypi** via `pip3` * **cargo** via `cargo install` * **golang** via `go install` * **github** releases downloaded and extracted Already installed locally or via your Neovim's Mason? SoulForge picks them up from the existing paths. ## Check health `/diagnose` runs a full check: tree-sitter, LSP readiness, semantic indexing, repo map status. Use it when something doesn't feel right. # MCP Servers Source: https://soulforge.proxysoul.com/tools/mcp Connect to Model Context Protocol servers for external tool integration. Supports stdio, HTTP, and SSE transports. SoulForge connects to MCP servers, exposing their tools alongside built-in tools. Three transports are supported: **stdio** (local subprocess), **Streamable HTTP** (remote), and **SSE** (legacy remote). ## Quick start Add a simple stdio server to your config: ```json theme={null} { "mcpServers": [ { "name": "github", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { "GITHUB_TOKEN": "ghp_..." } } ] } ``` Tools from this server are now available as `mcp__github__`. ## Configuration Add `mcpServers` to your global config (`~/.soulforge/config.json`) or project config (`.soulforge/config.json`). Project config overrides global by server name. ```json theme={null} { "mcpServers": [ { "name": "github", "transport": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { "GITHUB_TOKEN": "ghp_..." }, "timeout": 30000 }, { "name": "sentry", "transport": "http", "url": "https://mcp.sentry.dev/sse", "headers": { "Authorization": "Bearer ..." }, "timeout": 30000 }, { "name": "legacy-service", "transport": "sse", "url": "https://legacy.example.com/events", "headers": { "X-API-Key": "..." }, "disabled": false } ] } ``` ## Config reference | Field | Type | Description | | ----------- | -------------------------- | -------------------------------------------- | | `name` | string (required) | Display name and tool namespace prefix | | `transport` | "stdio" \| "http" \| "sse" | Transport type (default: "stdio") | | `command` | string | Command to spawn (stdio transport) | | `args` | string\[] | Arguments for command | | `env` | `Record` | Environment variables for subprocess | | `url` | string | URL for http/sse transports | | `timeout` | number | Per-tool-call timeout in ms (default: 30000) | | `disabled` | boolean | Disable without removing config | | `headers` | `Record` | HTTP headers for http/sse transports | ## Tool namespacing MCP tools are namespaced as `mcp____` to prevent collisions with built-in tools. A server named "github" with tool "create\_issue" becomes `mcp__github__create_issue`. ## Transport types **stdio** — Spawns a local subprocess. The server communicates via JSON-RPC over stdin/stdout. Best for local tools and development. **http** — Streamable HTTP (recommended for remote servers). Modern MCP transport with efficient streaming. **sse** — Server-Sent Events. Legacy remote transport. Use http for new servers. ## Lifecycle * **Startup**: All enabled servers connect with bounded concurrency (max 5 simultaneous) * **Retry**: Failed servers retry up to 3 times with exponential backoff (1s, 2s, 4s) * **Auto-restart**: Servers that crash automatically restart (stdio transport only) * **Cleanup**: On exit, all connections are closed and subprocesses terminated ## TUI shortcuts Open the MCP manager with `/mcp` or `Ctrl+K` → search "mcp". | Shortcut | Action | | -------- | ----------------------- | | Type | Filter servers by name | | `Ctrl+A` | Add new server | | `Ctrl+D` | Delete selected server | | `Ctrl+T` | Toggle enable/disable | | `Ctrl+E` | Edit server config | | `Ctrl+R` | Retry failed connection | | `Tab` | Switch to tools browser | | `Enter` | Open server detail view | ## Headless mode MCP servers connect automatically in headless mode. Tools are available to the agent immediately: ```bash theme={null} soulforge --headless "use the github MCP to list open issues" ``` # Memory Source: https://soulforge.proxysoul.com/tools/memory Cross-session memory — preferences, decisions, and gotchas that follow you between sessions. Memory is SoulForge's long-term store. It remembers your preferences, your project's decisions, and the sharp edges you've hit before — and surfaces them automatically when they're relevant. You don't have to ask. Memory is **per-project by default**, with an opt-in **global** scope for cross-project preferences (`"use bun not npm"`, `"be terse"`). ## What gets stored Four kinds: | Category | Example | | ---------- | ------------------------------------------------------ | | `pref` | "use bun not npm", "be terse" | | `decision` | "switched to zustand — redux was too much boilerplate" | | `gotcha` | "JWT expiry uses container clock, drifts in prod" | | `context` | "legacy/ deletes next sprint — don't touch" | The agent writes these proactively when you share a preference, make a choice with a reason, or hit a non-obvious bug. You can also browse and edit them yourself. ## How recall works Before each turn, SoulForge looks at your prompt and which files you've been editing, then injects up to **3 relevant memories** silently. The agent sees them and adjusts. You don't have to type `/memory` or remember what's stored. If nothing's relevant, nothing's injected — memory earns its prompt slot every turn or it doesn't take one. ## Browser — `/memory` Type `/memory` to open the browser. Five tabs: * **All** — every memory across project + global. Pin, soft-delete, or supersede per row. * **Hidden** — soft-deleted entries, restorable forever. * **Cleanup** — *Quick* (dupes + dead file refs) and *Stale* (low-signal candidates) for manual review. * **SIM** — clusters of related memories. * **Settings** — read & write scopes. Nothing is ever truly deleted. Soft-delete only, restorable forever. ## Scopes Two databases: | Scope | Location | Use for | | ------- | ------------------------ | -------------------------------------------- | | Project | `.soulforge/memory.db` | code-specific decisions, file-scoped gotchas | | Global | `~/.soulforge/memory.db` | cross-project preferences, your style | Read scope controls which DBs the agent sees: ``` all project + global (default) project only this repo global only your preferences none turn recall off ``` ## Pinning Pin a memory to keep it from being surfaced in cleanup suggestions and to lift its ranking in recall. Useful for non-obvious rules you want to keep close. ## Languages Memory works in any script — English, CJK, Arabic, Cyrillic, mixed-script identifiers. Searches use a word index plus a character n-gram index together, so paraphrases and non-Latin queries both hit. ## Commands | Command | Description | | --------- | ---------------- | | `/memory` | Open the browser | ## Config ```json theme={null} { "memory": { "embeddingModel": "openai/text-embedding-3-small" } } ``` Optional. Sets a real embedding provider for semantic recall. Default is a built-in offline embedder (hashbag-v2) that works without any API calls, good enough for word-overlap paraphrases. ### Do I need an API key? Yes, if you set a provider model. The embedder runs through the AI SDK, so it needs that provider's key. For `openai/text-embedding-3-small` that is your OpenAI key. Without a valid key the embedder fails its startup probe and silently falls back to the offline embedder. Memory keeps working either way. ### Which models can I use? Any AI SDK `provider/model` id. Verified routes: | Provider | Model | | -------------- | ---------------------------------------------------------------- | | OpenAI | `openai/text-embedding-3-small`, `openai/text-embedding-3-large` | | Google | `google/text-embedding-004` | | Vercel Gateway | `vercel_gateway/openai/text-embedding-3-small` | | LLM Gateway | `llmgateway/openai/text-embedding-3-small` | | OpenRouter | `openrouter/openai/text-embedding-3-small` | Providers with no embedding API (Anthropic, xAI, Groq, DeepSeek, Mistral, Bedrock, proxy, codex, copilot) always fall back to the offline embedder. Local providers (Ollama, LM Studio) and custom OpenAI-compatible providers work only if you set `embeddingModel` explicitly. You do not have to set this at all. When unset, it auto-resolves in order: `taskRouter.semantic`, a default for your active chat provider, then the offline embedder. ### How do I check it is working? Open `/memory`, go to the **Settings** tab, and read the **Embedding** row. It shows the active model and where the choice came from, or `offline (hashbag-v2)` when the offline embedder is in use. If you set a provider model but the row still says offline, the key is missing or the model was rejected. # Project tool Source: https://soulforge.proxysoul.com/tools/project Auto-detected project commands across 23 ecosystems with pre-commit enforcement and monorepo discovery. The `project` tool auto-detects your toolchain from config files and runs the correct commands -- no setup, no guessing. ## Actions | Action | Description | Example | | ----------- | -------------------------- | -------------------------------------------------- | | `lint` | Run project linter | `project(action: "lint")` | | `typecheck` | Run type checker | `project(action: "typecheck")` | | `test` | Run test suite | `project(action: "test", file: "src/foo.test.ts")` | | `build` | Build project | `project(action: "build")` | | `run` | Run dev server or script | `project(action: "run", script: "dev")` | | `list` | Discover monorepo packages | `project(action: "list")` | ## Parameters | Param | Type | Description | | --------- | -------- | -------------------------------------------------------------- | | `action` | required | `lint`, `typecheck`, `test`, `build`, `run`, `list` | | `file` | optional | Target specific file (test, lint) | | `fix` | optional | Auto-fix lint issues (biome --write, eslint --fix, ruff --fix) | | `flags` | optional | Extra CLI flags | | `env` | optional | Environment variables | | `cwd` | optional | Working directory (for monorepos) | | `timeout` | optional | Timeout in ms (default 120000) | ## Toolchain detection `detectProfile(cwd)` scans for config files and lockfiles: ``` bun.lock -> bun test, bun run lint, bunx tsc --noEmit Cargo.toml -> cargo test, cargo clippy, cargo check go.mod -> go test, golangci-lint run, go build pyproject.toml -> pytest, ruff check, mypy/pyright composer.json -> phpunit, phpstan/psalm Gemfile -> rspec, rubocop build.gradle -> gradle test, gradle check ``` ### JS/TS linter detection Priority order: 1. `biome.json` / `biome.jsonc` -> `biome check .` 2. `oxlintrc.json` / `.oxlintrc.json` -> `oxlint .` 3. `eslint.config.js` / `.eslintrc*` -> `eslint .` ### Python type checker detection * `pyrightconfig.json` exists -> `pyright` * Otherwise -> `mypy .` ## Full ecosystem support | Ecosystem | Lint | Typecheck | Test | Build | | ---------------- | ----------------------- | ---------------- | ----------------------- | ---------------- | | **JS/TS (Bun)** | biome / oxlint / eslint | tsc | bun test | bun run build | | **JS/TS (Node)** | biome / oxlint / eslint | tsc | npm test | npm run build | | **Deno** | deno lint | deno check | deno test | -- | | **Rust** | cargo clippy | cargo check | cargo test | cargo build | | **Go** | golangci-lint / go vet | go build | go test | go build | | **Python** | ruff / flake8 | pyright / mypy | pytest | -- | | **PHP** | phpstan / psalm | phpstan / psalm | phpunit | -- | | **Ruby** | rubocop | -- | rspec / rails test | -- | | **Swift** | swiftlint | swift build | swift test | swift build | | **Elixir** | credo | dialyzer | mix test | mix compile | | **Java/Kotlin** | gradle check | javac / kotlinc | gradle test | gradle build | | **C/C++** | clang-tidy | cmake build | ctest | cmake build | | **Dart/Flutter** | dart analyze | dart analyze | flutter test | flutter build | | **Zig** | -- | zig build | zig build test | zig build | | **Haskell** | hlint | stack build | stack test | stack build | | **Scala** | -- | sbt compile | sbt test | sbt compile | | **.NET/C#** | dotnet format | dotnet build | dotnet test | dotnet build | | **iOS/Xcode** | swiftlint | xcodebuild build | xcodebuild test | xcodebuild build | | **Java (Maven)** | checkstyle | mvn compile | mvn test | mvn package | | **C/C++ (Make)** | -- | -- | make test | make | | **Clojure** | clj-kondo | -- | lein test / clj -M:test | lein uberjar | ## Pre-commit checks When the agent runs `git commit` via the shell tool, SoulForge auto-runs `detectNativeChecks(cwd)` before allowing the commit: * **JS/TS:** `biome check . && tsc --noEmit` * **Rust:** `cargo clippy && cargo check` * **Go:** `golangci-lint run && go build ./...` * **Python:** `ruff check && pyright` If checks fail, the commit is blocked and errors are returned to the agent. This is code-enforced -- no prompt instruction needed. To skip pre-commit checks, commit directly via shell outside SoulForge. The pre-commit check uses `detectNativeChecks()` which identifies tools from config files directly, never from `package.json` scripts. This ensures only known, safe tools are run. ## Monorepo discovery `project(action: "list")` discovers workspace packages: ### Supported workspace formats * **pnpm:** `pnpm-workspace.yaml` * **npm/yarn:** `workspaces` field in `package.json` * **Cargo:** `[workspace] members` in `Cargo.toml` * **Go:** `use` directives in `go.work` ### Example output ``` 5 packages: @myapp/web -- packages/web (biome) [lint, typecheck, test] @myapp/api -- packages/api (biome) [lint, typecheck, test] @myapp/shared -- packages/shared (biome) [lint, typecheck] @myapp/cli -- packages/cli (biome) [lint] @myapp/docs -- packages/docs [test] Use project(action: "lint", cwd: "") to target a specific package. ``` Each package gets its own `detectProfile()` call, so capabilities are accurate per-package. ## System prompt integration The detected toolchain is injected into the system prompt: ``` Toolchain: bun Project commands: lint: `bun run lint` . typecheck: `bun run typecheck` . test: `bun test` . build: `bun run build` ``` The agent sees available actions without guessing. ## Shell redirect hints When the agent runs `bun run lint`, `cargo clippy`, etc. via shell instead of the project tool, the result includes a hint: ``` Command succeeded. Next time use project(action: "lint") -- it auto-detects the toolchain, results are structured, and output is visible in the UI. ``` This educates the agent to prefer the project tool without blocking the command. # Sessions Source: https://soulforge.proxysoul.com/tools/sessions Save, resume, export, and branch conversations. Every conversation is a session. SoulForge auto-saves sessions as JSONL files to `~/.soulforge/sessions/` with crash-resilient incremental writes. You can resume any past session, export it, or start fresh. ## Resume Press `Ctrl+P` to open the session picker. Filter by text, pick a session, continue where you left off. From the CLI: ```bash theme={null} soulforge --headless --session "now add tests" ``` Session IDs match by short prefix, so `abc` finds `abc123...`. ## Auto-save Sessions save after every turn. No manual save needed. If SoulForge crashes, the session reconstructs fully on relaunch, including mid-stream tool calls. ## Export ``` /session export markdown /session export json /session export clipboard /session export all full diagnostic dump (prompt + tools) /session export api toggle per-step API request logging ``` ## Checkpoint tags Sessions carry their [checkpoint](/tools/checkpoints) git tags. Restoring a session restores its undo history. Deleting a session cleans up its tags. ## Shortcuts | Key | Action | | ------------------- | --------------------------------- | | `Ctrl+P` | Browse sessions | | `Ctrl+N` | New session (saves current) | | `Ctrl+B` / `Ctrl+F` | Walk checkpoints within a session | ## Multiple tabs Each tab inside a session keeps its own messages and checkpoint history. Tabs persist across saves and resumes. # Skills Source: https://soulforge.proxysoul.com/tools/skills Installable agent capabilities, browsable from the TUI. Skills are markdown files that extend what the agent knows. A skill can teach it a framework, a workflow, a style guide, or a domain. SoulForge reads SKILL.md files at launch and routes them into the agent's system prompt when relevant. ## Browse and install Press `Ctrl+S` or run `/skills`. The picker searches [skills.sh](https://skills.sh) for community skills. ``` /skills open the manager Ctrl+S same ``` Three tabs: * **Search**: live search against the skills.sh catalog, sorted by install count. * **Installed**: every skill SoulForge can find on your machine. * **Active**: skills loaded into the current session. ## Scopes Skills resolve from six directories, with project scope taking priority over global: ``` ~/.soulforge/skills/ global ~/.agents/skills/ global (shared with other tools) ~/.claude/skills/ global (Claude Code compatible) .soulforge/skills/ project .agents/skills/ project .claude/skills/ project ``` If a skill exists in both a global and a project scope, the project copy wins. Symlinks are followed, so shared skill collections (for example `~/.agents/skills` linked from `~/.claude/skills`) deduplicate automatically. ## Install From the picker: enter selects a skill. SoulForge runs `bunx` (or `npx` as fallback) to add the skill. It lands in the chosen scope and appears in the Installed tab. From the CLI: ```bash theme={null} # Browse popular skills bunx skills list # Add from a catalog bunx skills add --skill -g ``` ## Enable and disable Skills are loaded per session. In the Active tab, toggle skills on and off; they appear in the agent's prompt context when on and drop out when off. The setting persists per session. ## Structure A skill is a directory with a SKILL.md at its root: ``` my-skill/ ├── SKILL.md required, the frontmatter + instructions ├── references/ optional supporting docs └── templates/ optional templates ``` `SKILL.md` front matter: ```yaml theme={null} --- name: my-skill description: When to reach for this skill. --- # My Skill Instructions, conventions, patterns... ``` ## Remove From the Installed tab, select a skill and delete. SoulForge removes the directory on disk. ## Writing your own Any directory with a `SKILL.md` is a skill. Drop one in `.soulforge/skills/my-skill/SKILL.md` (project) or `~/.soulforge/skills/my-skill/SKILL.md` (global) and it shows up immediately. The agent loads the skill content into its system prompt when the skill is active for the session, so keep SKILL.md concise and actionable. # Floating Terminals Source: https://soulforge.proxysoul.com/tools/terminals Spawn, resize, and manage terminal sessions alongside the chat. ## Overview SoulForge includes floating terminal panels powered by Ghostty's terminal emulator. Run commands, watch logs, or interact with REPLs — all without leaving the chat. Up to 5 terminals can run simultaneously. ## Commands | Command | Action | | ------------------- | ------------------------- | | `/terminals` | Open the terminal manager | | `/terminals new` | Spawn a new terminal | | `/terminals close` | Close a terminal | | `/terminals show` | Show the terminal panel | | `/terminals hide` | Hide the terminal panel | | `/terminals rename` | Rename a terminal | ## How it works The floating terminal uses [Ghostty](https://ghostty.org/)'s Zig-based terminal emulator compiled to a native library. This gives you: * Full ANSI/VT100 support * GPU-accelerated rendering (via the native lib) * Mouse support * 256-color and true-color * Proper Unicode and emoji rendering The terminal state is managed independently from the chat — agent tool calls (`shell`) don't interfere with your terminal sessions. ## Limits * **Maximum 5 terminals** can be open at once * Terminals persist across compaction and context resets * Each terminal runs as an independent PTY session ## Platform notes The Ghostty native library (`.dylib` on macOS, `.so` on Linux) is included in the standalone bundle. If it's missing, SoulForge starts normally — floating terminals are simply unavailable. # Themes Source: https://soulforge.proxysoul.com/tools/themes 36 builtin themes, custom themes with hot reload — pick your flavor or add yours. ## Quick start Press `/theme` or `Ctrl+K` → search "theme" to open the theme picker. Themes preview live as you navigate. ## Builtin themes ### Dark | Theme | Description | | ---------------------- | -------------------------------------------- | | `dark` | SoulForge default | | `solarized-dark` | Ethan Schoonover's classic | | `catppuccin` | Catppuccin Mocha | | `catppuccin-frappe` | Catppuccin Frappe | | `catppuccin-macchiato` | Catppuccin Macchiato | | `gruvbox-dark` | Retro groove | | `tokyo-night` | Tokyo Night | | `tokyonight-storm` | Tokyo Night Storm | | `dracula` | Dracula | | `nord` | Arctic, north-bluish | | `one-dark` | Atom One Dark | | `rose-pine` | All natural pine | | `kanagawa` | Inspired by Katsushika Hokusai | | `github-dark` | GitHub Dark | | `everforest-dark` | Comfortable green | | `ayu-dark` | Ayu Dark | | `nightfox` | Nightfox | | `proxysoul-main` | proxySoul — deep purple with hot pink | | `proxysoul-coffee` | proxySoul Coffee — warm amber & burnt orange | | `proxysoul-water` | proxySoul Water — ocean blue & teal | | `cyberdream` | Cyberdream | | `oxocarbon` | Oxocarbon | | `sonokai` | Sonokai | | `moonfly` | Moonfly | | `melange` | Melange | | `solarized-osaka` | Solarized Osaka | | `bamboo` | Bamboo | | `nordic` | Nordic | | `synthwave` | Synthwave | | `iceberg` | Iceberg | | `ember` | Ember | | `vesper` | Vesper | ### Light | Theme | Description | | ------------------ | ----------------- | | `light` | Clean light theme | | `catppuccin-latte` | Catppuccin Latte | | `one-light` | Atom One Light | | `github-light` | GitHub Light | ## Config Theme is saved globally in `~/.soulforge/config.json`: ```json theme={null} { "theme": { "name": "catppuccin", "transparent": true, "userMessageOpacity": 70, "diffOpacity": 30, "borderStrength": "strong" } } ``` | Field | Description | | -------------------- | ------------------------------------------------------------------------------------------------------- | | `name` | Theme ID (builtin or custom) | | `transparent` | Make background transparent (terminal background bleed-through) | | `userMessageOpacity` | User message background opacity when transparent: `0` (clear), `30` (dim), `70` (subtle), `100` (solid) | | `diffOpacity` | Diff background opacity when transparent: `0` (clear), `30` (dim), `70` (subtle), `100` (solid) | | `borderStrength` | Border visibility: `"default"`, `"strong"`, or `"op"` | ### Theme picker controls | Key | Action | | --------- | -------------------------------- | | `↑` / `↓` | Navigate themes (live preview) | | `Enter` | Apply selected theme | | `Tab` | Toggle transparent mode | | `M` | Cycle message background opacity | | `D` | Cycle diff background opacity | | `B` | Cycle border strength | ## Custom themes Create your own theme by adding a JSON file to `~/.soulforge/themes/`: ```bash theme={null} mkdir -p ~/.soulforge/themes ``` ### Example `~/.soulforge/themes/my-theme.json`: ```json theme={null} { "_extends": "dark", "_label": "My Theme", "_description": "A custom dark theme", "_variant": "dark", "brand": "#ff6600", "brand-secondary": "#ff0040", "bg-primary": "#1a1a2e", "bg-elevated": "#16213e", "text-primary": "#e0e0e0", "success": "#00ff88", "error": "#ff4444" } ``` The file name (minus `.json`) becomes the theme ID. ### Metadata fields | Field | Description | | -------------- | -------------------------------------------------------------------------------------------- | | `_extends` | Inherit from a builtin theme (default: `dark`). Only override the tokens you want to change. | | `_label` | Display name in the theme picker | | `_description` | Description shown in the picker | | `_variant` | `"dark"` or `"light"` — controls the indicator in the picker | ### Token reference Tokens use kebab-case in JSON files (auto-converted to camelCase internally): * **Brand**: `brand`, `brand-secondary`, `brand-dim`, `brand-alt` * **Text**: `text-primary`, `text-secondary`, `text-muted`, `text-dim`, `text-faint`, `text-subtle` * **Backgrounds**: `bg-app`, `bg-primary`, `bg-secondary`, `bg-elevated`, `bg-popup`, `bg-popup-highlight`, `bg-overlay`, `bg-input`, `bg-banner`, `bg-banner-error`, `bg-user` * **Borders**: `border`, `border-focused`, `border-active`, `border-slash` * **Status**: `success`, `error`, `warning`, `info`, `amber` * **Diff**: `diff-added-bg`, `diff-removed-bg`, `diff-added-sign`, `diff-removed-sign` * **Accents**: `accent-user`, `accent-assistant`, `accent-system` ### Legacy format You can also define multiple themes in `~/.soulforge/themes.json`: ```json theme={null} { "my-theme": { "_extends": "dark", "brand": "#ff6600" }, "another-theme": { "_extends": "light", "brand": "#0066ff" } } ``` ## Hot reload Theme files are watched for changes. Edit a custom theme file and the UI updates instantly — no restart needed. # Troubleshooting Source: https://soulforge.proxysoul.com/troubleshooting Fixes for common issues. Can't find your issue? Ask in the [Discord](https://discord.gg/fX4H7GYSMJ) or open a [GitHub issue](https://github.com/proxysoul/soulforge/issues). ## No models in the picker Your API key isn't set. Pick one: ```bash theme={null} soulforge --set-key anthropic sk-ant-... # or export it and restart your terminal: export ANTHROPIC_API_KEY=sk-ant-... ``` Check what's configured: ```bash theme={null} soulforge --list-providers ``` ## Getting rate-limited (429) Bump retry settings in `~/.soulforge/config.json`: ```json theme={null} { "retry": { "maxAttempts": 5, "baseDelayMs": 3000 } } ``` Delays double each attempt. `maxAttempts: 5` with `baseDelayMs: 3000` gives \~3s, 6s, 12s, 24s, 48s between retries. ## Icons show as boxes or `??` You don't have a [Nerd Font](https://www.nerdfonts.com). Install one and set it as your terminal font. Or run `/setup` inside SoulForge to install one for you. If you can't install fonts, disable the icons: ``` /font nerd ``` ## LSP isn't working Open `/lsp status`. If your language server isn't attached: ``` /lsp install ``` Picks from the Mason registry and installs into `~/.soulforge/lsp-servers/`. Restart SoulForge after installing. ## Neovim not found SoulForge needs Neovim >= 0.11. Run `/setup` to install it, or: ```bash theme={null} brew install neovim # macOS sudo apt install neovim # Debian/Ubuntu sudo pacman -S neovim # Arch ``` If `nvim` isn't on your PATH, set `nvimPath` in `~/.soulforge/config.json`. ## Editor panel is garbled Your terminal needs true color. Most modern terminals have it; some need: ```bash theme={null} export COLORTERM=truecolor ``` in your shell profile. ## Context is full / "too many tokens" ``` /compact ``` Runs compaction immediately. Or switch to a larger model (`Ctrl+L`). To compact automatically at a lower threshold, edit config: ```json theme={null} { "compaction": { "triggerThreshold": 0.6 } } ``` ## Copilot: 401 / token expired Your Copilot token was rotated. Copy it again from your IDE: ```bash theme={null} soulforge --set-key copilot $(jq -r '."github.com".oauth_token' \ ~/.config/github-copilot/apps.json) ``` See [Copilot setup](/providers/copilot) for details. ## Hearth: "daemon unreachable" The daemon isn't running: ```bash theme={null} soulforge hearth start ``` Leave it running in a terminal (or install as a service with `soulforge hearth install --now`). Check with `soulforge hearth doctor`. ## Hearth: bot isn't responding Run the diagnostic: ```bash theme={null} soulforge hearth doctor ``` Common causes: * Token missing from keychain — re-run `soulforge hearth login`. * Chat not paired — run `/hearth pair` and DM the bot with the code. * Your identity isn't in the allowlist — the daemon drops unknown senders silently. ## Agent is slow Switch to a faster model with `Ctrl+L` (Haiku, Flash, Ollama) or tune the [task router](/recipes/task-router) — use a fast model for `spark` and `compact`, reserve the strong one for `ember`. ## Forgot a session ```bash theme={null} soulforge --headless --chat # browse by timestamp ``` Or open `/session history` inside SoulForge. Sessions live in `~/.soulforge/sessions/`. ## Windows: pasting an image does nothing `Ctrl+V` with a screenshot on the clipboard may silently do nothing. Two separate causes on Windows: 1. **Windows Terminal eats the paste.** Its bracketed-paste mode emits an *empty* sequence for image clipboards, so the image bytes never reach SoulForge. Embedded Neovim / Vim keymaps can also swallow `Ctrl+V` first. 2. **PowerShell can't be reached.** The clipboard read shells out to PowerShell; if neither `powershell` nor `pwsh` is on `PATH`, the read fails. Try, in order: * Press **`Alt+V`** instead of `Ctrl+V` — it bypasses the bracketed-paste / Vim-keymap interception. * Make sure Windows PowerShell 5.1 is on `PATH`: `C:\Windows\System32\WindowsPowerShell\v1.0`. * Check `/errors` inside SoulForge — a failed clipboard read leaves a `clipboard` breadcrumb there. If neither works (the Windows Terminal bug is upstream), **save the screenshot to a file and reference the path** — the agent reads it with `soul_vision`: ``` look at C:\Users\me\Desktop\bug.png and tell me what's wrong ``` Drop this helper in your PowerShell profile to capture the clipboard image to a file in one command: ```powershell theme={null} function Save-ClipboardImage { param([string]$Path = "$env:TEMP\sf-paste.png") Add-Type -AssemblyName System.Windows.Forms, System.Drawing $img = [System.Windows.Forms.Clipboard]::GetImage() if ($img) { $img.Save($Path, [System.Drawing.Imaging.ImageFormat]::Png) Write-Host "Saved: $Path — paste this path into SoulForge." } else { Write-Host "No image on clipboard." } } Set-Alias cpimg Save-ClipboardImage -Force ``` Run `cpimg`, then reference the printed path in your prompt. ## Windows: command not found after install PATH updates don't apply to the shell that ran the installer. Open a brand-new PowerShell or Windows Terminal window. If you used the portable ZIP, make sure the extract folder is on your `PATH` or call `soulforge.exe` by full path. The `deps/` folder must stay next to `soulforge.exe`. See the Windows section of [installation](/installation#windows-notes) for the full feature matrix. ## Still stuck? * [GitHub Issues](https://github.com/proxysoul/soulforge/issues) * Run `/diagnose` inside SoulForge for a health check — LSP, tree-sitter, semantic indexing, provider status. * `/status` shows the full system dashboard.