Reference · Terminal

lingcode CLI

Interactive REPL and one-shot ask in your terminal. Tool use across every provider — Claude (full Agent SDK with MCP + hooks) plus OpenAI, Groq, Together, OpenRouter, Mistral, xAI, Fireworks, Ollama, and any OpenAI-compatible endpoint.

Install

Pick the path that matches your OS — the native binaries are the default. Or skip install entirely and try it in your browser first: same prompt against Claude, DeepSeek, OpenAI, Gemini side-by-side, no setup.

macOS & Linux (native binary)

curl -fsSL https://lingcode.dev/install-cli.sh | sh

Downloads a tarball to ~/.lingcode/cli/ and symlinks it to ~/.local/bin/lingcode. macOS tarballs are signed + notarized; the Linux tarball is unsigned. The bundled Node bridge ships inside, so the Claude provider works without LingCode.app installed (you still need Node.js and an API key).

Windows (native binary)

iwr -useb https://lingcode.dev/install-cli.ps1 | iex

Downloads a Bun-compiled lingcode.exe to %LOCALAPPDATA%\Programs\lingcode\ and adds it to your User PATH. PowerShell 5.1+ (ships with Windows 10/11); x64 and ARM64 both supported. The installer auto-detects AVX2 and falls back to a baseline build on older CPUs / VMs. The binary is unsigned in v0.9.x — Windows Defender SmartScreen will warn on first run; click More info → Run anyway. Authenticode signing is on the roadmap. After install, lingcode upgrade handles future versions in-place (the running .exe is renamed to lingcode.exe.old while the new binary takes its slot).

After install — in the same PowerShell window:

# Free first chat — no signup needed, uses LingModel (free tier)
lingcode run --provider lingmodel "hello"

# For higher limits or other providers: grab a token at
# https://lingcode.dev/cli-token.html and run
lingcode providers login

That's it. lingcode run "<prompt>" drops into a multi-turn > REPL on the same session after each one-shot — type follow-ups, /quit to exit. If you closed the terminal before testing, just reopen any PowerShell window; the install added lingcode to your User PATH.

From an installed LingCode.app

/Applications/LingCode.app/Contents/Resources/bin/lingcode install

The install subcommand tries /usr/local/bin first, falls back to ~/.local/bin, prints a PATH hint if needed.

Quickstart

# One command to store a key in the Keychain (opens browser)
lingcode auth login

# Interactive session (Claude by default)
lingcode

# Interactive session with another provider
lingcode --provider ollama --model llama3.2
lingcode --provider groq --model llama-3.1-70b-versatile

# One-shot
lingcode ask "explain this error" < build.log
echo "what's 2+2?" | lingcode ask -

# Check your setup
lingcode auth status
lingcode --help

Interactive REPL

lingcode with no arguments drops you into an interactive multi-turn session. It's the same as lingcode repl, just shorter to type.

The REPL has two flavors depending on provider:

Input editor

When stdin is a TTY, you get a proper line editor:

/ Recall previous / next input
/ / Home / EndCursor movement
TabComplete slash commands (builtin + custom)
Trailing \Continue the input on the next line
Ctrl-CCancel running query (Claude) / abort line
Ctrl-DExit on empty line, otherwise delete character
Bracket-pastePaste multi-line clipboard content as one input

Prompt

The REPL prompt shows turn count and cumulative cost once they're non-zero:

lingcode> explain this repo
…
lingcode [1t $0.003]> now suggest a refactor
…
lingcode [2t $0.011]>

Providers

Pick with --provider; override model, base URL, or API key env var as needed. Every provider runs the agent loop with tool calls by default.

ProviderEnv varDefault modelTools?
claude (default)ANTHROPIC_API_KEYSDK default (Sonnet)
deepseek-claudeDEEPSEEK_API_KEYdeepseek-v4-pro[1m]
deepseek-compatDEEPSEEK_API_KEYdeepseek-v4-flash
openaiOPENAI_API_KEYgpt-4o-mini
geminiGEMINI_API_KEYgemini-2.5-flash
kimiMOONSHOT_API_KEYkimi-k2.6
qwenDASHSCOPE_API_KEY(provider default)
groqGROQ_API_KEYllama-3.1-70b-versatile
togetherTOGETHER_API_KEYLlama-3.3-70B-Instruct-Turbo
openrouterOPENROUTER_API_KEYanthropic/claude-sonnet-4
mistralMISTRAL_API_KEYmistral-large-latest
xaiXAI_API_KEYgrok-2-latest
fireworksFIREWORKS_API_KEYllama-v3p1-70b-instruct
ollama— (none)llama3.2

Two ways to use DeepSeek

deepseek-compat uses DeepSeek's OpenAI-compatible endpoint with lingcode's own agent loop. deepseek-claude uses DeepSeek's Anthropic-compatible endpoint, which lets the polished Claude path (full Agent SDK, sessions, hooks, Skills, subagents) drive DeepSeek-V4 — banner shows (claude→deepseek). The deepseek-claude path is more featureful but skips MCP servers and image input (DeepSeek's compat layer ignores those fields).

lingcode --provider deepseek-claude     # full Claude UX, deepseek-v4-pro[1m]
lingcode --provider deepseek-compat     # OpenAI-format, deepseek-v4-flash by default
lingcode --provider deepseek-compat --model deepseek-v4-pro

Any OpenAI-compatible endpoint

lingcode --provider openai \
  --base-url https://my-proxy.example.com/v1 \
  --api-key-env MY_KEY_VAR \
  --model gpt-4o

Tool use, MCP servers, hooks, Skills, subagents, custom slash commands, output styles, settings.json permission patterns, and the configurable status line all work on every provider. Session resumption and image attachments via --file are Claude-only (those depend on the Agent SDK); OpenAI-compatible providers get image input via --image in text-only mode.

Linux & Windows notes

The native CLI builds and runs identically on Linux x86_64 / ARM64 and on Windows x64 / ARM64. Two differences vs. macOS:

Files, sessions & output styles

The CLI is the part of LingCode you'll script against, point at log files, and leave running in a tab next to your editor. A handful of features make that less awkward than just typing prompts into a black rectangle — most of them are in the npm-distributed Node version starting at v0.12; the Swift CLI has subsets of the same surface.

Attach files inline with @

Type @ followed by a path and the file's contents are read off disk and prepended to your prompt as a code block before the model sees it. Same pattern as Cursor, Codex, and Claude Code.

> summarize @README.md and explain what the build pipeline does in @scripts/ship.sh

Repeated mentions of the same file get deduplicated. Files over 200 KB are skipped with a note suggesting the Read tool with offset/limit instead — the model still has a path to read them, just not all at once. Paths resolve against the current working directory; absolute paths also work.

Fetch URLs with WebFetch

A built-in tool the model can call to grab an HTTP/HTTPS URL, strip the HTML to readable text, and reason about it. No MCP setup required — it's listed in the tool schema alongside Read/Write/Bash. Like other side-effectful tools it always prompts for permission in the TTY (talking to arbitrary servers can leak referrer info or IPs, so we don't auto-approve it).

> what does Anthropic say about prompt caching pricing? Check their docs.
<assistant calls WebFetch on docs.anthropic.com/...>
Allow WebFetch? [WebFetch] [y/N]

Export a conversation as markdown

While you're in the REPL, /export <path> writes the current session to a markdown file — alternating ## You / ## Assistant sections with tool calls and results inlined as block quotes. Useful for handing off a working session to a teammate, pasting into a PR description, or just archiving an investigation.

> /export ~/Desktop/refactor-notes.md
(wrote 4823 bytes to ~/Desktop/refactor-notes.md)

Persistent sessions

Opt in once and every REPL run writes its transcript to ~/.lingcode/sessions/<timestamp>.json as you chat:

lingcode config set saveHistory true

Then:

Persistence is off by default because most uses of the CLI are one-shot or transient; flipping it on is one config line when you change your mind.

Output styles

Three knobs for response shape, set as a config value the CLI reads on every call:

lingcode config set output-style compact   # 1–3 sentences unless thoroughness is needed
lingcode config set output-style verbose   # show reasoning + intermediate work
lingcode config set output-style concise   # no preamble, no pleasantries
lingcode config unset output-style          # back to model default

It works by appending a system-prompt directive each turn, so the change takes effect immediately — no REPL restart needed. The model still chooses how to answer; this just nudges it toward your preferred shape.

One-shot ask

# Simple
lingcode ask "summarise this repo's architecture"

# Pipe content in
cat error.log | lingcode ask "explain this"

# Read the entire prompt from stdin
echo "what is 2+2?" | lingcode ask -

# Switch provider
lingcode ask --provider groq "quick: what does this regex do? /\d{3}-\d{4}/"

# Claude with full tool use, auto-approved
lingcode ask --provider claude --yolo "add a .gitignore entry for .DS_Store"

# Attach a file
lingcode ask --provider claude --file screenshot.png "what's wrong in this UI?"

# Structured output (good for scripts)
lingcode ask --output-format stream-json "fix the failing test in main.swift"

Useful flags

--continueResume the most recent Claude session in this directory
--resume <id>Resume a specific session by ID
--file <path>Attach a file (image, PDF, text). Repeatable. Claude only.
--allowed-tools Read,WriteAllow only these tools (Claude only)
--disallowed-tools BashDisallow these tools
--add-dir ~/otherExtra directory the agent can touch. Repeatable.
--thinkingEnable Claude's extended thinking
--system-prompt "..."Override the system prompt
--append-system-prompt "..."Append to the system prompt
--output-format text|json|stream-jsonOutput shape
--output path/out.mdWrite the response to a file
--verboseDon't truncate tool input/output
--yoloAuto-allow every tool call. Dangerous.

Slash commands

Type these at the REPL prompt.

Every provider

/helpList commands
/model <name>Switch model mid-session
/mode <mode>Change permission mode (default / acceptEdits / plan / dontAsk / bypassPermissions)
/yoloBypass all permission prompts
/costCumulative token usage + USD estimate (priced per model)
/toolsList available built-in tools
/commandsList custom slash commands from .claude/commands/
/skillsList loaded Skills from .claude/skills/
/agentsList subagents from .claude/agents/ (OpenAI-compat; on Claude, subagents come from the Agent SDK's built-in Task)
/output-stylesList loaded output styles
/sessionShow current session id (use with --resume)
/resetClear conversation (Claude: also starts a fresh session)
/export [path]Save transcript as markdown
/clearClear screen
/quitExit

Claude-only

/compactSummarise and compress conversation history
/doctorDiagnose environment (node, API key, bridge, MCP, hooks)
/initGenerate CLAUDE.md for the current project

OpenAI-compatible only

/system <text>Replace the system prompt for this session
/image <path>Attach a PNG/JPG/GIF/WEBP image to the next turn (OpenAI-compat mode)
/mcpShow connected MCP servers and their tool inventory

Custom

Put a Markdown file at .claude/commands/<name>.md and invoke it as /project:<name>. Files at ~/.claude/commands/ are invoked as /user:<name>. Bare /<name> checks project first, then user. Any text after the command name is appended to the body.

$ echo "Explain the changes staged in git in plain English." > .claude/commands/explain-staged.md
$ lingcode
lingcode> /project:explain-staged

Auth & API keys

Three sources, checked in this order: environment variable, OS secret store (macOS Keychain on Mac, chmod-600 ~/.config/lingcode/keys.json on Linux), config file. Any one is enough.

# Browser-assisted: pick a provider with arrow keys (12 supported), opens its
# console, prompts for the key, stores in the Keychain. Offers to set the
# chosen provider as default.
lingcode auth login

# Skip the picker
lingcode auth login --provider deepseek
lingcode auth login --provider openai

# Direct
lingcode auth set anthropic sk-ant-...
lingcode auth set deepseek sk-...

# See what's configured (values masked)
lingcode auth status

# Remove a key
lingcode auth delete anthropic

About OAuth: Anthropic does not issue OAuth client IDs to third-party CLIs, so a true /login OAuth flow is not possible. lingcode auth login is the practical replacement — it opens the console in your browser, waits for you to paste the key, and stores it.

Other providers (openai, groq, together, openrouter, mistral, xai, fireworks, ollama) don't use the Keychain path — just set their environment variable in your shell:

export OPENAI_API_KEY=sk-...
export GROQ_API_KEY=gsk-...

Skills & subagents

Both load from the same paths Claude Code uses, so existing libraries work unchanged. Project files in .claude/, user-level files in ~/.claude/, project wins on name clash.

Skills

An expertise packet the model can pull in when it's relevant. Put one at .claude/skills/<name>/SKILL.md with YAML frontmatter:

---
name: pr-reviewer
description: Review a GitHub PR with focus on security, correctness, and style.
---

When asked to review a PR, fetch it via `gh pr view`, then check:
1. Security issues (injection, auth bypass)
2. Correctness (edge cases, error handling)
3. Style (matches repo conventions)

Loaded automatically into the system prompt on every provider. Disable with --no-skills; list with /skills.

Subagents

A focused agent the model can dispatch to via the Task tool — runs in a fresh context with its own system prompt and (optionally) its own model and tool subset. Put one at .claude/agents/<name>.md:

---
name: flake-hunter
description: Diagnose intermittently-failing tests by rerunning and correlating with recent commits.
model: deepseek-v4-pro
tools: Read, Grep, Bash
---

Check git log for recent changes near the failing test. Run the test N times
and look for non-determinism: time, network, randomness, parallel ordering.

The model invokes a subagent with Task(agent: "flake-hunter", prompt: "..."). Subagents run sequentially, can't recursively spawn more subagents (no Task-in-Task), and inherit the parent's permission decider. List with /agents.

On the Claude provider, subagents come from the Agent SDK's built-in Task tool — works the same way to the model.

MCP servers, hooks, custom commands

Compatible with Claude Code's project conventions, on every provider.

MCP servers

Put a .mcp.json at the project root (or ~/.claude.json globally):

{
  "mcpServers": {
    "filesystem": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
    }
  }
}

Override with --mcp-config <path>. Disable with --no-mcp. Both Claude and OpenAI-compat providers consume MCP stdio servers — connected tools show up alongside built-ins in the model's tool list.

Hooks

Put hooks in .claude/settings.json. They run as shell commands with CLAUDE_TOOL_NAME, CLAUDE_TOOL_INPUT, CLAUDE_TOOL_RESULT (for PostToolUse), and CLAUDE_USER_PROMPT (for UserPromptSubmit) in the environment. LINGCODE_* mirrors are also set if you'd rather not depend on Claude-branded names. Hooks fire on every provider.

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Write|Edit", "hooks": [{ "type": "command", "command": "echo about to edit: $CLAUDE_TOOL_INPUT" }] }
    ],
    "UserPromptSubmit": [
      { "hooks": [{ "type": "command", "command": "logger -t lingcode \"$CLAUDE_USER_PROMPT\"" }] }
    ],
    "Stop": [
      { "hooks": [{ "type": "command", "command": "say done" }] }
    ]
  }
}

Custom slash commands

Markdown files under .claude/commands/ (project) or ~/.claude/commands/ (user). Same format as Claude Code — the file body is the prompt.

Permissions, output styles, status line

Three more .claude/settings.json blocks. All three apply on every provider.

Permission patterns

Fine-grained allow/deny/ask rules layered on top of the chosen permission mode. Glob syntax: * matches any run of non-slash characters, ** matches a path. Precedence: deny > ask > allow. Ask forces an interactive prompt even under --yolo.

{
  "permissions": {
    "allow": ["Bash(git *)", "Read(**/*.swift)"],
    "deny":  ["Bash(rm *)", "Bash(curl *)"],
    "ask":   ["Write(**)", "Edit(**)"]
  }
}

Hardcoded security rules

A small set of destructive patterns are blocked unconditionally — your settings cannot override them. These compile once at startup and fire before any permission round-trip:

These rules apply to every provider and every session — REPL, ask, serve, and acp-serve. They wrap the permission decider chain so the veto fires even when --yolo or an external ACP client would otherwise auto-approve.

Output styles

Append a custom voice / format to the system prompt. Save styles under .claude/output-styles/<name>.md (or ~/.claude/output-styles/). Pick one with --output-style <name>; list with /output-styles. The built-in engineer style is the default.

Status line

Customize the prompt with a shell command. Runs once per turn (not per keystroke), 2-second timeout, falls back silently to the default prompt on failure.

{
  "statusLine": {
    "type": "command",
    "command": "printf '%s · %st · $%s' \"$LINGCODE_MODEL\" \"$LINGCODE_TURN\" \"$LINGCODE_COST_USD\""
  }
}

Env vars passed in: LINGCODE_SESSION, LINGCODE_MODEL, LINGCODE_TURN, LINGCODE_INPUT_TOKENS, LINGCODE_OUTPUT_TOKENS, LINGCODE_COST_USD, LINGCODE_CWD. Claude Code's CLAUDE_* mirrors are set too.

Background tasks

Long-running shell processes (dev servers, watchers) start with Bash(run_in_background: true) and return a task id immediately. Poll with the Monitor tool — it returns only the new bytes since the last poll, plus whether the process is still running. Monitor(action: "list") shows everything; Monitor(action: "kill", id: "bg_3") stops one. Cap of 8 concurrent background tasks per session.

Talking to the running app

When LingCode.app is open, these commands drive it over a Unix socket at ~/Library/Application Support/LingCode/ipc.sock:

CommandWhat it does
lingcode pingCheck whether the app is running and responsive.
lingcode open path/to/file.swiftOpen a file or folder in the app (launches it if needed).
lingcode statusFocused project and any active agent runs.
lingcode ask "..."Route the prompt through the app; stream its answer to stdout.
lingcode watchTail agent activity in real time. Ctrl-C to stop.

Pass --headless to ask to force local execution even with the app open.

Expose as HTTP server

lingcode serve runs a long-lived HTTP server that exposes the agent over Server-Sent Events. Use it to drive LingCode from a VS Code extension, web UI, or scripts on another box — same agent core that powers the REPL and one-shot ask.

lingcode serve                                   # bind 127.0.0.1:7878
lingcode serve --port 8080
lingcode serve --bind 0.0.0.0 --allow-remote     # opt in to non-loopback
lingcode serve --new-token                       # rotate the bearer token
lingcode serve --bridge-daemon auto              # reuse a running bridge daemon

Auth: Authorization: Bearer <token>. The token is generated on first start, written to ~/.lingcode/server.token (chmod 600), and printed to stderr. Bind defaults to loopback; non-loopback requires --allow-remote.

Endpoints (v1)

GET /v1/pingLiveness + version
POST /v1/agent/askSSE stream of agent events
POST /v1/agent/permission/{requestId}Allow/deny a Claude tool call
POST /v1/agent/cancel/{queryId}Cancel an in-flight query

Streaming an agent query

TOKEN=$(cat ~/.lingcode/server.token)

# Liveness
curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:7878/v1/ping

# Streaming ask (-N disables curl buffering)
curl -N -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"provider":"claude","prompt":"list files","cwd":"'"$PWD"'"}' \
     http://127.0.0.1:7878/v1/agent/ask

SSE frames carry one event per JSON object. Claude events: query_started, assistant_text, tool_use, tool_result, permission_request, permission_resolved, token_usage, query_finished (terminal), query_failed / query_cancelled / error. DeepSeek and OpenAI-compat providers emit a subset — they include tool_use but skip permission_request (tools execute without interactive approval).

Tool-permission round-trip

When Claude requests a tool that needs approval, you'll see a permission_request SSE event with a requestId. POST allow/deny back:

curl -s -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"behavior":"allow"}' \
     http://127.0.0.1:7878/v1/agent/permission/<requestId>

Or {"behavior":"deny","message":"…"} to refuse with a hint shown back to the model. The SSE stream resumes when the decision arrives.

Multi-account & daemon reuse

Pass "account": "work" in the ask body to select a per-request keychain slot (matches lingcode auth login --account work). Pass --bridge-daemon auto at startup to reuse a running lingcode bridge daemon-start instead of cold-spawning Node per Claude query — saves ~200–500ms per request.

CORS is deny-by-default and TLS is not implemented — front with caddy / nginx / cloudflared if you expose the server beyond the host.

External agent clients (acp-serve)

lingcode acp-serve runs a stdio-based Agent Control Protocol (ACP) host. External editors and AI clients — such as Zed or gemini-cli — can drive the full LingCode agent loop without going through the HTTP server. Any provider works.

# Drive the Claude agent over stdio
lingcode acp-serve --agent claude

# Drive a DeepSeek agent
lingcode acp-serve --agent deepseek

# Drive any OpenAI-compatible provider
lingcode acp-serve --agent openai-compat:groq
lingcode acp-serve --agent openai-compat:openrouter

# Set the working directory (defaults to cwd)
lingcode acp-serve --agent claude --cwd ~/my-project

The ACP protocol is a JSON-lines message exchange over stdin/stdout. Each agent session is multi-turn and stateful — the external client issues prompts and receives a structured stream of events (assistant_text, tool_use, tool_result, permission_request, and terminal frames like session_finished).

Security rules fire before every ACP round-trip: HardcodedDenyDecider blocks destructive patterns regardless of client instructions; ACPClientPermissionDecider then forwards remaining requests to the external client for allow/deny.

Credential resolution uses the same chain as the REPL: macOS Keychain → environment variable → config file. No extra auth configuration needed if you already have keys stored via lingcode auth login.

Subcommand reference

SubcommandPurpose
repl (default)Interactive multi-turn session. lingcode with no args.
askOne-shot prompt; supports streaming, JSON output, attachments, resume.
authlogin / set / get / delete / status against the OS secret store.
configRead or write ~/.lingcode/config.json — defaults, stored keys.
initGenerate a CLAUDE.md for the current project by analysing the repo.
historyList past sessions from ~/.lingcode/history.jsonl.
completion zsh|bash|fishPrint a shell completion script.
pingIPC probe for the running app.
openOpen file or folder in the running app.
statusApp focus / agent status.
watchStream live agent activity.
installSymlink lingcode onto your PATH.
serveRun an HTTP server (/v1/agent/ask SSE) so external clients can drive the agent. Details.
acp-serveRun a stdio-based ACP host for external editors (Zed, gemini-cli). Details.

Troubleshooting

lingcode: ANTHROPIC_API_KEY is not set

Fix with lingcode auth login, or export in your shell, or run lingcode config set anthropic-api-key sk-ant-....

lingcode: <PROVIDER_API_KEY> is not set

OpenAI-compatible providers use plain env vars. For custom names: lingcode --provider openai --api-key-env MY_KEY.

lingcode: couldn't find Claude agent bridge

The Claude provider needs bridge.mjs, which ships inside LingCode.app or the standalone CLI tarball. Either install LingCode.app at /Applications, or point at a dev copy with export LINGCODE_AGENT_BRIDGE_DIR=/path/to/agent-bridge.

lingcode: bundled Node.js runtime is missing

The macOS CLI tarball ships its own Node — you shouldn't need to install one. If this error appears, the bundled binary went missing (corrupted install, manual deletion, or a Linux build): reinstall lingcode (curl -fsSL https://lingcode.dev/install-cli.sh | sh). As a fallback, install Node.js (nodejs.org) or set NODE to an explicit path. Only the Claude provider uses Node; OpenAI-compatible providers are pure Swift.

Rate-limited / 429 / overloaded

Both ask and repl retry automatically with exponential backoff (up to 5 attempts, up to 60s apart). You'll see rate limited (attempt N/5) — retrying in Xs… on stderr.

The app responds, but I wanted headless

lingcode ask routes through the app by default when it's open. Pass --headless to force local execution.

I pasted an API key into my terminal

Rotate it at the provider's dashboard. Anything in your shell history or tmux scrollback should be treated as leaked. Prefer lingcode auth login (browser paste) or lingcode auth set <provider> <key> — neither writes the key to stdout.