Skip to content

Command line

koda [OPTIONS] [PROMPT]...

A bare prompt seeds the TUI with a first message. With -p it runs headless: the answer streams to stdout and koda exits.

Flag What it does
[PROMPT]... First message. Without -p it seeds the TUI.
-p, --print Headless: stream the answer to stdout and exit.
-m, --model <MODEL> Model name, e.g. qwen2.5-coder:14b.
-u, --url <BASE_URL> OpenAI-compatible base URL, e.g. http://localhost:1234/v1.
--api-key <KEY> API key, if the server needs one.
-C, --dir <DIR> Workspace root. Defaults to the current directory.
-y, --yolo Approve file writes and commands without asking.
--protocol <PROTOCOL> Tool-call protocol: auto, native, or text.
--no-sandbox Allow file tools outside the workspace root.
-t, --temperature <T> Sampling temperature.
--theme <THEME> Palette name — see Themes.
--icons <ICONS> Glyphs: auto, unicode, ascii.
--mode <MODE> Start in plan, execute or vibe.
-c, --continue (--resume) Reopen the most recent conversation in this project.
Subcommand What it does
koda models List the models the endpoint reports.
koda skills [--init] List skills; --init writes a starter skill into <project>/.koda/skills/.
koda config [--init] Show the effective configuration; --init writes a commented starter file.
Terminal window
koda -p "which module owns the retry logic?"

-p runs the same agent as the TUI with the screen taken away: same tools, same modes, same config. The answer goes to stdout and tool activity to stderr, so you can keep one without the other:

Terminal window
koda -p "summarise what changed in src/ this week" > summary.md # answer only
koda -p "find the retry logic" 2> trace.log # activity only
Headless: koda -p, in any mode100×30 · 0:47
No TUI: -p streams the answer to stdout and exits, and --mode picks how much it is allowed to do.

Headless has nobody to answer an approval prompt, so what koda is allowed to do has to be decided before it starts. Two independent flags decide it: --mode is what kind of work it may do, and -y is whether it may do it without asking.

Command What it may do Exit status
koda -p --mode plan "..." Read and think. Writes and commands are refused. 0
koda -p --mode execute "..." Nothing: the first write is denied, unanswered. 2
koda -p -y --mode execute "..." Read, write, and run commands. 0
koda -p -y --mode vibe "..." The same, after writing a spec and checking itself against it. 0

--mode execute is the default, so a bare koda -p "fix the test" is the second row: it will plan, read, and then stop at the first edit. That is deliberate — the flag that lets an unattended agent write to your disk should be one you typed on purpose.

plan — read-only, and safe to run anywhere

Section titled “plan — read-only, and safe to run anywhere”
Terminal window
koda -p --mode plan "the add function is subtracting - what would you change?"

Nothing on disk changes. If the model tries to edit, the tool is refused and koda says so on stderr, then carries on and answers:

· plan mode blocked edit_file; re-run with --mode execute

The exit status is still 0, because a plan is the deliverable here, not a failure. This is the mode to reach for in a hook, a CI job, or anything running against a checkout you care about — it cannot damage a working tree.

Terminal window
koda -p -y --mode execute "fix the failing test in tests/test_calc.py"
git diff --stat

Without -y, the first write is denied and koda exits 2:

✗ edit_file needs approval; re-run with --yolo to allow it

With -y it edits and runs commands unattended. Check the result the way you would check a colleague’s: git diff. koda writes through the same edit tool the TUI shows you a diff for, so everything it did is in the working tree, and git checkout . undoes all of it.

vibe — execute, with a spec and a self-check

Section titled “vibe — execute, with a spec and a self-check”
Terminal window
koda -p -y --mode vibe "add a --json flag to the report command"

Vibe is execute plus two extra steps of its own: it writes an explicit spec before starting, and checks its work (and any subagent’s work) against that spec before it finishes. It costs more model calls and takes longer, so it earns its place on a task with enough shape to be got wrong — not on a one-line fix.

Status Meaning
0 The turn finished. In plan mode this includes refused writes.
1 koda could not start: no endpoint, no model, a bad flag.
2 A tool needed approval nobody could give, or the turn failed.

So a script can branch on it:

Terminal window
if koda -p -y --mode execute "fix the failing test"; then
git diff --stat
else
echo "koda could not finish unattended" >&2
fi

Everything else about a run can be set on the command line, so a headless call does not depend on whatever is in your config:

Terminal window
koda -p -y \
--mode execute \
-C /path/to/project \
-u http://localhost:11434/v1 \
-m qwen2.5-coder:14b \
--protocol native \
"update the changelog for the last three commits"

A few things worth knowing before this runs unattended:

  • ask_user gets no answer. If the agent asks a question, koda reports it and continues without one. A task that needs a decision belongs in the TUI.
  • The sandbox still applies. File tools stay inside -C unless you pass --no-sandbox.
  • Destructive commands are still confirmed when confirm_destructive is on — which, headless, means they are refused rather than run. That is the intended behaviour: -y means “do the work”, not “do anything”.
  • Sessions are still written, so koda --continue in that directory reopens what the headless run did, in the TUI.

Two prefixes skip the agent entirely. Neither costs tokens, and neither enters the conversation the model sees — the output is yours, not context.

Prefix What it runs
! A shell command in the workspace: !git status, !npm test, !git commit -am wip.
$ Python: $ print(sum(range(1, 11)))
$ import json; print(json.load(open("package.json"))["version"])

$ uses python3 when it is on your PATH and falls back to python. Both show their output in the transcript as a tool block, exactly like a command the agent ran — the difference is invisible to you and total to the model.

This is the answer to “I need a number, not a guess”: an interpreter’s arithmetic instead of a language model’s.

Press # at the end of what you have written to open a palette of actions on the text itself. None of them reach the model or cost a turn.

Action What it does
#copy Copy the whole prompt to the clipboard.
#copyline Copy the line the caret is on.
#cutline Delete that line.
#start / #end Move the caret to the beginning or the end.
#clear Empty the input.
#undo Restore what the last action removed.
#paste Insert the clipboard’s text.

Type a few letters to narrow the list (#cl → #clear), ↑/↓ to pick, enter to run, esc to close and keep what you wrote. A # in the middle of a sentence is left alone as ordinary text, so fix #3 in the parser behaves normally.