CLI configuration

Everything LLeMbas CLI reads, and where. Settings in ~/.config/lembas/, data in ~/.local/share/lembas/, state in ~/.local/state/lembas/.

The files

FileWhat
connections.yamlConnections and models. Global only; keep it chmod 600.
config.yamlEverything else: defaults, permissions, search, voice, memory, MCP, updates, the remote link.
AGENTS.mdInstructions for every project.
commands/, agents/, skills/Your own slash commands, subagents and skills.
memory/USER.md, memory/MEMORY.mdWhat it knows about you, and its own notes. Edit by hand or with /memory.
themes/, prompts/Your colour themes; replacements for built-in prompts.
schema/JSON Schemas for editors. lembas config schema --link points the files at them.
.agent/ in a projectThat project’s config.yaml, plans, commands, skills, agents, task board and decision log. Read only once the project is trusted.

{env:NAME} and {file:path} work anywhere a string does, so keys never have to be written into a file.

connections.yaml

yaml
connections:
  local:                              # the name used in model: local/<id>
    dialect: openai-chat              # openai-chat | responses | anthropic | gemini | ollama
    base_url: https://llm.example/v1  # including /v1
    api_key: "{env:LOCAL_KEY}"        # or "{file:~/.secrets/k}", or key_cmd: "pass show llm/local"
    tls: { ca: ~/.config/lembas/private-ca.crt }   # a private CA; or insecure: true
    timeout: 600                      # seconds of silence before giving up — not reply length
    discover: true                    # accept models found at /models that are not listed
    one_model_at_a_time: false        # true: one GPU behind llama-swap — no subagent on another model
    models:
      coder:
        name: Local coder
        context: 131072               # absent: learned after the first reply
        max_output: 16384
        temperature: 0.7
        efforts: [low, medium, high]  # the model's own vocabulary
        effort: medium
        vision: false
        tools: true
        notes: fast; fine for small edits
        fallback: [cloud/coder]       # when this server cannot be reached

The five dialects:

DialectTalks toAuth sent as
openai-chat{base_url}/chat/completions — llama.cpp, vLLM, LM Studio, OpenRouter, DeepSeek…bearer
responsesOpenAI’s Responses API, statelessbearer
anthropic{base_url}/messagesx-api-key
geminiGoogle’s Gemini API (not yet tested against real servers)x-goog-api-key
ollama{base_url}/api/chat, with num_ctx sent (not yet tested against real servers)bearer

auth: overrides it: bearer, x-api-key, x-goog-api-key or none.

A LLeMbas server

lembas login https://your-server writes one entry, named after the server, and nothing else:

yaml
myserver:
  type: webui
  url: https://your-server
  api_key: "{file:~/.config/lembas/lembas/myserver.key}"   # the token, mode 600

The models are read from the server at every start, with what its administrator set for each, and kept for a start without the network. They are named provider/model. With the server’s permission, login also points voice and web search at it, keeping what you had as the fallback (--keep-services leaves them alone). lembas logout removes exactly what login added.

config.yaml — the keys

Most keys can be set at three levels — global (~/.config/lembas/config.yaml), project (.agent/config.yaml) and session — and /settings shows each with where its value comes from. Some are global only, so a cloned repository can never change them; some are edited in the file by hand and never by the agent.

KeyWhatWhere it can be set
modelconnection/model the session starts onsession, global, project
small_modelfor session titles and /commit messages; default: the session’s modelsession, global, project
modemanual | edit | auto | plansession, global, project
effortthe reasoning effort a session starts withsession, global, project
titlesmodel (a title from the model) or prompt (the first line)session, global, project
personalityconcise | pragmatic | optimistic | funny | formal | socratic | customsession, global, project
personality_customyour own words when personality: custom (≤ 1500 characters)session, global
instructionshow you want to be helped (≤ 4000 characters)session, global
instruction_filesmore files for the system prompt, after AGENTS.mdglobal, project (in the file)
settings_toolask | allow | off — the agent’s own settings toolglobal
limits.steps, limits.bash_timeoutsteps per prompt; seconds per commandsession, global, project
limits.wall_seconds, limits.output_bytes, limits.completion_tokensbudgets for one prompt; unset by defaultsession, global, project
permissionrules per tool: allow, ask, deny, by patternglobal, project (in the file)
hardline_extra, hardline_disableadd to, or switch off rules of, the always-refused command listglobal (in the file)
search.order, search.max_resultsweb search providers in order: webui, searxng, firecrawl, ddgsession, global, project
search.fetchlocal, firecrawl or webuiglobal, project (in the file)
compaction.auto_at, compaction.prunecompact past this share of the context window (1 = off); prune old tool output firstsession, global, project
memory.enabledthe memory tool and memory in the promptsession, global, project
embeddingconnection/model of an embedding model, for knowledge basesglobal
knowledgewhich knowledge bases are searched; absent = allsession, global, project
librarylocal or lembas (your server’s library)global
question.unattendedheadless with nobody present: recommended | first | failsession, global, project
busy_inputa message sent mid-task: steer (next step) or queue (after the task)session, global
theme, theme_backgrounda built-in theme or a file in themes/; terminal keeps your terminal’s backgroundsession, global
iconsemoji or plain for fonts without emojisession, global
mousefalse: the terminal keeps the mouseglobal
update.channel, update.autostable | beta; install | notify | offglobal
update.source, update.public_key, update.verifywhere updates come from and how they are checkedglobal (in the file)
remotewhat a LLeMbas server may do on this machineglobal (in the file)
mcpMCP serversglobal, project (in the file)
voicespeech to text and text to speechglobal (in the file)
skills.external_dirs, skills.disabledextra read-only skill directories; skills to hideglobal, project (in the file)
yaml
model: local/coder
small_model: local/tiny
mode: manual
effort: high
personality: pragmatic
instructions: |
  I know Python well and TypeScript a little.
limits: { steps: 200, bash_timeout: 120 }
permission:
  bash: { "npm test *": allow, "npm publish *": deny }
  edit: { "*.lock": ask }
search:
  order: [searxng, ddg]
  searxng: { base_url: https://searx.example }
compaction:
  auto_at: 0.85
theme: moria
busy_input: steer

Changing settings

/settings in the TUI, or from a shell: lembas config list, get <key>, set <key> <value> [--project]. Files keep their comments when written. You can also just ask — “think harder”, “plan first” — and the agent changes the setting through its settings tool, which you approve like any other call. A less strict mode is asked about every time. Permission rules, the hardline, MCP servers, connections, personality and where updates come from are never the agent’s to change.

Permission rules

Rules map a tool and a pattern to allow, ask or deny. A project’s rules stack after the global ones. A short list of catastrophic commands (rm -rf /, mkfs, force-push to main…) is refused in every mode; hardline_extra adds patterns and hardline_disable switches off a rule by id, globally only.

Personality and instructions

yaml
personality: concise        # concise | pragmatic | optimistic | funny | formal | socratic | custom
personality_custom: "…"     # your own words, when personality is custom
instructions: |             # how you want to be helped
  Answer briefly. Show the command before running it.

Both go last in the system prompt. They are your words, so instructions and personality_custom are global only. Logged in to one LLeMbas server that offers personalization, these are your account’s, shared with the web UI.

Memory, skills and knowledge

yaml
memory:
  enabled: true
  memory_chars: 2200
  user_chars: 1375
skills:
  external_dirs: [~/shared-skills]
  disabled: [some-skill]
embedding: local/nomic-embed     # global: an embedding model for knowledge bases
knowledge: [manuals, specs]      # the bases searched; absent = all

A skill is skills/<name>/SKILL.md with a name and description in its front matter; every skill is also a /command. Knowledge bases are managed with lembas kb or /kb; lembas kb reindex after changing the embedding model.

MCP servers

yaml
mcp:
  files:                                   # local, over stdio
    command: [npx, -y, "@modelcontextprotocol/server-filesystem", "."]
  github:                                  # remote
    url: https://api.githubcopilot.com/mcp/
    headers: { Authorization: "Bearer {env:GITHUB_TOKEN}" }
    tools: { exclude: ["delete_*"] }
  linear:
    url: https://mcp.linear.app/mcp        # sign in with /mcp auth linear
permission:
  "mcp__github__*": allow

Tools are named mcp__<server>__<tool> and ask unless a rule allows them. A server’s prompts become /<server>:<prompt> commands.

Voice

yaml
voice:
  stt:
    provider: openai                 # or whispercpp, or webui (your server)
    base_url: https://speech.example/v1
    model: whisper-1
    language: en
  tts:
    provider: openai                 # Kokoro-FastAPI, OpenAI; or piper-http, piper-cli, webui
    base_url: https://speech.example/v1
    model: kokoro
    voice: af_heart
  record_key: ctrl+t
  record_mode: toggle                # hold: letting go also stops
  submit: draft                      # send: straight to the model
  speak: false                       # read every reply aloud

Global only. /voice shows what is in use; lembas voice check tests it from a shell.

yaml
remote:
  enabled: true
  roots: [~/code, /srv/work]
  max_mode: edit
  approval_timeout: 600
  require_trust: true
  terminal: false
  terminal_integration: true

Global only. See Agent chats.

Updates

yaml
update:
  channel: stable            # stable | beta
  auto: install              # install | notify | off
  source:                    # default: the official GitHub releases
    type: gitea              # github | gitea (Forgejo too) | static
    url: https://git.example.org
    repo: you/LLeMbas-CLI
  public_key: "ssh-ed25519 AAAA… your-release-key"
  verify: signature          # checksum: accept an unsigned source of your own

Global only — a project never decides what binary runs. A static source is any web directory with the release files in <url>/<tag>/ and the current tags in <url>/stable and <url>/beta.

Custom commands and agents

markdown
---
description: review a file for bugs
mode: plan                # optional: for this one prompt
---
Review @$1 for bugs. Recent changes:
!`git log -5 --oneline -- $1`

Save it as ~/.config/lembas/commands/review.md (or in a project’s .agent/commands/) and run /review src/app.ts. $ARGUMENTS is everything after the name, $1…$9 the words, and !`command` is replaced by its output.

A subagent is the same shape in agents/<name>.md, with optional model, mode and tools. Built in: explore (read-only) and general. /agents lists them.