Get neosh

Options

Every setting is declared, typed, and owned. You cannot set one that does not exist, and you cannot set the wrong type. Both are errors that name the problem.

How options work

Set them in config.toml under [options], or from code:

await neosh.opt.set("chat.show_thinking", true);
const on = await neosh.opt.get<boolean>("chat.show_thinking");
await neosh.opt.all();                    // everything declared, with types and defaults
neosh.opt.onChange((e) => { /* fires for every option, not just yours */ });

Plugins declare their own options through the same call the built-ins use, and a declared option is settable from config.toml like any other, including by a user who set it before the plugin loaded. opt.all() is always the complete, current list; the tables below are the ones that ship.

Agent

OptionDefaultEffect
agent.model""instance/model. Empty picks the first that works
agent.system_prompt""Replaces the built-in prompt when set
gen.model""Model for branch names, commit messages and titles. Empty uses the conversation’s model. Separate on purpose: naming a branch does not need a frontier model

Chat

OptionDefaultEffect
chat.show_thinkingfalseStream reasoning into the chat buffer
chat.show_toolstrueShow a line when a tool runs. Errors always show
chat.tool_output_lines3How much of a tool result to show under it. 0 gives the line count and nothing else
chat.markdowntrueRender answers as they arrive. false shows exactly what the model sent
chat.preview_linesHow many rows a tool card opens to while the cursor is in it

UI

OptionDefaultEffect
ui.theme"dark"Or "light", or any theme a plugin contributed
ui.motiontrueText that moves while something is happening. Off removes the movement and keeps the colour
ui.hintsfalseA shortcut row under the composer. Off, the sidebar says the same keys
ui.confirm_destructivetrueAsk before anything irreversible. Turning it off turns off every such dialog, everywhere
ui.ascii_onlyfalseFor terminals without a decent font
ui.nerd_fontfalseBrand glyphs for providers, where the font has them. Deliberately not auto-detected

Keys

OptionDefaultEffect
mapleader"\"Set it before you use <leader> in a binding
timeoutlen500How long to wait for the rest of a key sequence, in milliseconds

Every picker, prompt and completion list reads the ui.keys.* family, in Neovim notation, with more than one key allowed per action:

[options]
"ui.keys.next" = "<C-n> <Down> <C-j>"
"ui.keys.prev" = "<C-p> <Up> <C-k>"
"ui.keys.page_down" = "<PageDown>"
"ui.keys.page_up" = "<PageUp>"
"ui.keys.first" = "<C-Home>"
"ui.keys.last" = "<C-End>"
"ui.keys.accept" = "<CR>"
"ui.keys.dismiss" = "<Esc> <C-c>"
"ui.keys.complete" = "<Tab> <Right>"
"ui.keys.clear" = "<C-u>"
"ui.keys.delete_word" = "<C-w>"

A widget claims these window-scoped while it is open, so they outrank global bindings and are released the moment it closes. That is what makes ^N move in a picker even though ^N is otherwise “new conversation”.

OptionDefaultEffect
sidebar.opentrueShow it at startup
sidebar.width34Also what > < = in the panel adjust, so the key and the file say the same number
sidebar.hintstrueThe contextual key strip at the foot of the panel
sidebar.refresh_ms4000A workspace re-read per tick

Worktrees

OptionDefaultEffect
worktree.root"~/.nsh"Where new worktrees go, as <root>/<repo>/<branch>. A relative value keeps them inside the repository as <repo>/<value>/<branch>, with the .gitignore entry written for you. "" restores the sibling layout

Git prompts

Every generated name or message has a prompt behind it, and every prompt is a setting in two layers: *.instructions is appended to the built-in prompt, which is the common case, and *.prompt replaces it entirely. Instructions still apply on top of a replaced prompt, so a per-project rule layers onto your own.

[options]
"git.branch.prefix" = "feature/"
"git.branch.instructions" = "Start with the Jira key when the message mentions one."
"git.commit.instructions" = "Follow Conventional Commits."

Usage

OptionDefaultEffect
usage.sidebartrueThe plan strip at the foot of the sidebar
usage.warn_at90Say something the first time a limit passes this. 0 to never
usage.days30The span the ^L panel opens on
usage.polltrueAsk the vendor while nothing is running. See Models
usage.show_tokenstrueThe running token total, beside the context meter

Archive

OptionEffect
archive.sidebarA count row in the project panel. Off by default: nothing archived is on screen until you ask
archive.sort, archive.groupThe panel’s ordering and grouping. s and S in it change the same settings
archive.width, archive.heightThe popup’s size
archive.auto_daysArchive what has been idle this long. Reversible and free, so it may happen on its own
archive.retention_daysWhat counts as old. Only ever counts; archive.sweep is the same number with a person behind it
archive.remindSay so once a day when old conversations accumulate

Nothing in the archive deletes on a timer.

Plugins

OptionDefaultEffect
plugins.disabled[]Bundled plugins not to load. Their keys, rows and hints go with them