>thevibeworks/deepseek-cli

Commands

Thirteen commands: seven wrap an endpoint, three read local state, two are health checks, one is an escape hatch. --help on any of them carries the same detail.

chat

POST /chat/completions — the default door, and the only format that supports chat prefix completion.

ds chat "why is the sky blue"
git diff | ds chat "write a commit message"
ds chat "explain" --file server.go --file server_test.go
ds chat "review this" --model deepseek-v4-pro --effort max
ds chat "and now in one line" --continue

Arguments are the instruction; piped stdin and --file are the material. All three compose.

FlagEffect
-m, --modeldeepseek-v4-flash (default) or deepseek-v4-pro
-s, --systemSystem prompt, inline or @file
--think on|offThinking mode. Default is the API's own, which is on
-e, --effortlow, high or max
--max-tokensCap generated tokens
--temperature, --top-pSampling. Both ignored in thinking mode
--stopStop sequence, repeatable, max 16
--response-formattext or json_object
--toolTool definition as JSON or @file, repeatable
--tool-choicenone, auto, required, or a JSON object
--prefixBeta: force the answer to start with this text
-f, --fileAttach a file's contents, repeatable
-c, --continueContinue the conversation named last
--session NAMERead and write a named conversation
--stream=falseWait for the whole answer
--reasoning=falseHide the chain of thought
--logprobs, --top-logprobsToken log probabilities
--user-idCache and scheduling isolation

Multi-turn

The API stores nothing, in any format, so conversations live on your machine.

ds chat "read this spec" --file spec.md --session review
ds chat "now list the risks"          --session review
ds session ls
ds session show review
ds session rm review

--continue is the session named last.

Interactive

-i keeps the conversation open and prompts for the next turn, instead of retyping --continue:

ds chat -i
$ ds chat -i "walk me through this codebase" --file main.go
The entry point wires three things together...
· flash · 2.1k in · 180 out · ~$0.000302 · 1.8s
 now what would you change first
The retry loop, because...
 /model pro
model deepseek-v4-pro
 ^D
bye — 4 messages saved as "last"; resume with: deepseek chat -c

It is the same session machinery, so nothing is lost on exit — ds chat -c resumes it and ds session show last reads it. /help lists the slash commands: /model, /think, /effort, /system, /file, /tokens, /docs, /new, /save.

^C during an answer abandons that answer and keeps the conversation; ^D leaves. It needs a terminal and refuses to combine with --json — for scripted multi-turn use --session, which is what it is built on.

Tools

ds chat "weather in Hangzhou?" --tool @weather.json
# tool_call call_00_hUj... get_weather({"city": "Hangzhou"})

One tool file works against every format — both the OpenAI parameters and the Anthropic input_schema spellings are accepted. The calls are printed, never executed.

anthropic

POST /anthropic/v1/messages — the format Claude Code, the Anthropic SDKs and the Claude desktop app speak.

ds anthropic "hello"
ds anthropic "hello" --model claude-opus-4-1 --json

Claude model names are accepted and remapped server-side. The usage line shows both names so the cost stays traceable to the model that actually ran. See formats for the mapping.

respond

POST /responses — the format Codex speaks. Two things live only here: JSON Schema structured output, and a web_search tool DeepSeek runs server-side.

ds respond "what shipped in Go 1.26" --web-search
ds respond "Berlin" -s "Return city and country." --schema @city.json

--web-search is the whole setup: the search runs on DeepSeek's side, there is nothing to execute locally, and the searches the model makes are reported on stderr as they happen. It is the one tool in the whole API that this CLI can “run” for you, because DeepSeek runs it. The API ignores the OpenAI knobs (search_context_size, user_location), and in multi-turn use the server restores search results replayed from earlier turns by itself.

Flash only, for now.

fim

POST /beta/completions — give it a prefix and an optional suffix; it writes the middle.

ds fim "def add(a, b):" --suffix "    return result"
ds fim --prefix @head.go --suffix @tail.go --max-tokens 200

Beta, with two hard limits: output caps at 4K tokens, and it never thinks.

tokens

Exact token counts, from the tokenizer that will bill you.

ds tokens "why is the sky blue"
ds tokens --file main.go --file main_test.go
git diff | ds tokens
ds tokens --offline --file huge.log     # free local estimate

DeepSeek ships no count-tokens endpoint and no Go tokenizer — only a Python demo and two rules of thumb. But the FIM endpoint takes a raw prompt with no chat template around it and reports prompt_tokens for exactly the bytes sent, plus one BOS token. Subtract the one and the count is exact.

That measurement is a real request: the text goes to DeepSeek and is billed as input, the same as sending it would have been. The cost prints on stderr every time. --offline uses DeepSeek's published character ratios instead — free, and an upper bound that says so.

docs

DeepSeek's own API documentation, compiled into the binary.

ds docs                          # every page
ds docs search "context cache"
ds docs show guides/thinking_mode
ds docs ask "does FIM support thinking?"
ds docs changelog                # releases, newest first
ds docs sync                     # refresh from the mirror

67 pages, about 85KB compressed: every page of api-docs.deepseek.com plus the FAQ, which lives outside that site as a JSON blob inside a JavaScript bundle and is not otherwise readable as text.

Only ask costs anything. It selects pages locally, sends them whole, and instructs the model to answer from them and cite the page, so a claim can be checked against a URL. Every command here prints how old the snapshot is, and every page keeps the upstream URL it came from.

status

ds status

Is the API reachable right now, with this key, from this machine. Two calls that generate no tokens, so it is free and safe to run in a loop. That is a different question from DeepSeek's incident page, which reports outages affecting everyone — a working API behind a broken proxy looks fine there and broken here.

models

ds models

The API's model list joined with the published rate card, so the price is on screen next to the model you are about to pick. --json returns the API's list alone.

balance

ds balance

Lists every currency the account holds — a real account returns both a USD and a CNY row. Exits 3 when exhausted, the same code a 402 produces anywhere else, so a script can check once up front.

usage

ds usage                  # today
ds usage --since 7d
ds usage --entries --json # individual calls

Reports the local ledger. See cost.

check

ds check

Calls all six endpoints once. Every probe runs even after one fails, because "all six rejected the key" is a different diagnosis from "only /responses is unhappy".

raw

ds raw /models
ds raw /chat/completions --data @request.json
ds raw /anthropic/v1/messages --data @req.json --anthropic-auth

Any path, with this CLI's auth, base URL, retries and error reporting. Every other command is a typed convenience over this one, so an endpoint DeepSeek ships tomorrow is reachable today.

Global flags

FlagEffect
--jsonPrint the API's own response body, unwrapped
--jq EXPRFilter that body through jq
--api-key, --base-urlOverride the resolved values
--timeoutDefault 10m, matching how long the API may hold a connection before inference starts
-v, -vvLog HTTP to stderr; -vv adds bodies
--no-statsSuppress the token/cost line
--no-ledgerDo not record the call

Exit codes

CodeMeaningWhat to do
0success
1errorRead stderr
2authKey missing or rejected. Do not retry
3no balanceTop up. Do not retry
4rate limitedBack off, then retry
130interrupted

Transport failures and 429/5xx are already retried internally with backoff, so a non-zero exit means your own retry loop probably will not help either.