The terminal stage for your LangGraph agent. A Claude Code-style CLI that runs any LangGraph CompiledGraph — yours, not a bundled one — with streaming, tool-call rendering, and human-in-the-loop approval.
Renamed from deepagent-code (the old package name now just installs this one, and the
deepagent-codecommand still works). Not to be confused with LangChain'sdeepagents-code(dcode) — that's a separate project;langstage-cliis the terminal stage of the LangStage family.
langstage-cli is the terminal stage of the LangStage family: write your agent once — any LangGraph CompiledGraph — and run it on every stage with the same spec string (module:attr or path/to/file.py:attr), the same langstage.toml config file, and the same LANGSTAGE_* environment variables.
| Stage | Package | Try it |
|---|---|---|
| Web app | langstage | langstage run --agent my_agent.py:graph |
| JupyterLab | langstage-jupyter | pip install langstage-jupyter, then the chat sidebar in jupyter lab |
| Terminal | langstage-cli | you are here |
| VS Code | langstage-vscode | chat participant + stdio sidecar |
| Reference agent | langstage-hermes | LANGSTAGE_AGENT_SPEC=langstage_hermes.agent:graph on any stage |
| Shared core | langstage-core | typed events + config resolver behind every stage |
📖 Full documentation: https://dkedar7.github.io/langstage-docs/
This surface's agent — any LangGraph CompiledGraph — can also be served over the AG-UI protocol as a standalone HTTP endpoint:
pip install "langstage-core[agui]"
langstage-agui --agent my_agent.py:graphpip install langstage-cliOr install directly from GitHub:
pip install git+https://github.com/dkedar7/langstage-cli.gitNo agent or API key yet? See the CLI working in one command:
langstage-cli --demo "hello"Point it at your own agent (any LangGraph CompiledGraph):
export ANTHROPIC_API_KEY="your_api_key" # if your agent calls Anthropic
langstage-cli -a path/to/your_agent.py:graphThis launches an interactive conversation loop with your agent.
# Keyless demo agent — no API key, no agent of your own
langstage-cli --demo "Hello"
# Scaffold a runnable starter agent + langstage.toml (then just run it)
langstage-cli init
# Send a message directly to your agent
langstage-cli -a my_agent.py:graph "Hello, agent!"
# Specify a custom agent file
langstage-cli -a my_agent.py:graph
# Use a module path
langstage-cli -a mypackage.agents:chatbot
# Read message from a file
langstage-cli -f ./prompt.md
# Non-interactive mode (auto-approve tool calls)
langstage-cli --no-interactive
# Verbose output
langstage-cli -v
# Keyless demo agent (no API key needed)
langstage-cli --demo
# Print the resolved configuration: each value, its source, and the
# env var / langstage.toml key that sets it
langstage-cli --show-config
# Resume a previous conversation across separate invocations
langstage-cli "remember: my name is Kedar" # a fresh, PERSISTED session
langstage-cli -c "what's my name?" # continue the most recent session
langstage-cli --list-sessions # list resumable sessions
langstage-cli --resume <id> # resume a specific onelangstage-cli --demo "hi" # see the CLI work, keyless
langstage-cli init # scaffold my_agent.py + langstage.toml (your own graph)
langstage-cli "hi" # runs MY agent — no -a, no hand-editinginit writes a minimal, immediately-runnable starter agent (the stdlib StateGraph
example below — needs only langgraph, a base dependency) plus a langstage.toml
pointing at it, so the next langstage-cli "..." runs your graph. It refuses to
overwrite an existing my_agent.py / langstage.toml unless you pass --force.
Every invocation persists its conversation to a durable, per-workspace store so a later run can pick it up — the terminal-agent continuity of Claude Code / Codex / Aider, without you baking a checkpointer into your own graph:
--continue/-c— resume the most recent session for this workspace and keep going. If there's no prior session, it just starts a fresh one.--resume <id>— resume a specific session (ids come from--list-sessions; a bare--resumealso lists them). A short prefix works if it's unambiguous.- A plain run with no flag starts a new session that is still persisted, so it can be continued later.
State lives in ~/.langstage/sessions/<workspace-hash>.sqlite (honoring
LANGSTAGE_CONFIG_HOME; override the whole location with LANGSTAGE_CLI_SESSIONS_DIR).
Persistence is on by default — disable it with --no-persist, LANGSTAGE_PERSIST=0, or
[session] persist = false in langstage.toml. A graph that compiles in its own
checkpointer keeps it (yours always wins); the CLI only supplies a durable one when your
graph has none. A pinned [configurable] thread_id now genuinely persists across runs.
In the interactive loop:
/help(/h,/?) - Show this help message, or/help <command>for one command/status(/s) - Show session status (agent, thread, verbose, cwd)/version(/v) - Show version and the current agent/config(/cfg) - Show the resolved configuration, or set a runtime key:/config [key] [value]/verbose- Toggle verbose output, or set it explicitly:/verbose [on|off]/history(/hist) - Show recent conversation messages, optionally/history [N]/clear(/c) - Clear conversation history/reset(/restart) - Reset the session (clear history and start a new thread)/quit(/q,/exit) - Exit- Tab autocompletes commands; Ctrl+C exits
# Agent location (path/to/file.py:variable_name or module:variable)
# (DEEPAGENT_AGENT_SPEC / DEEPAGENT_SPEC still accepted as deprecated aliases)
export LANGSTAGE_AGENT_SPEC="my_agent.py:graph"
langstage-cli
# Working directory
export LANGSTAGE_WORKSPACE_ROOT="/path/to/workspace"langstage-cli reads TOML config from two locations and merges them
(project overrides global):
- Global:
~/.langstage/config.toml - Project:
langstage.tomlin the current directory or any ancestor
Legacy locations (~/.deepagents/config.toml, deepagents.toml) are still
read as fallbacks; move your config when convenient — ~/.deepagents/ now
belongs to LangChain's dcode.
Precedence: CLI args > env vars > project TOML > global TOML > defaults.
Example langstage.toml:
[agent]
spec = "my_agent.py:graph"
[workspace]
root = "."
[ui]
verbose = true
[configurable]
# seeds LangGraph RunnableConfig.configurable
thread_id = "my-thread"Usage: langstage-cli [OPTIONS] [MESSAGE]
Arguments:
MESSAGE Optional input to send to the agent immediately
Options:
-a, --agent TEXT Agent spec (path/to/file.py:graph or module:graph)
-g, --graph-name TEXT Graph variable name (default: "graph")
-f, --file PATH Read message from a file (any extension)
--interactive/--no-interactive Handle interrupts (default: interactive)
-v, --verbose Verbose output
--demo Run with the built-in keyless demo agent
--show-config Print the resolved configuration and exit
-q, --quiet Scriptable single-shot output: only the reply
--verify Preflight the agent (one real turn); exit 0/1
-c, --continue Resume the most recent session for this workspace
--resume [ID] Resume a specific session (bare: list them)
--list-sessions List resumable sessions for this workspace
--persist/--no-persist Persist this session so it can be continued (default: on)
--force For `init`: overwrite existing files
--version Show the version and exit
Subcommand:
init Scaffold my_agent.py + langstage.toml in the current dir
--verify loads the configured agent and runs one real turn, exiting 0 if it
completed cleanly and non-zero otherwise — so you can gate on it in CI before
trusting an agent. It catches a missing key, a broken tool, or a non-runnable graph
that a static "it imports" check would wave through (it runs the same
langstage-core preflight every LangStage surface uses):
langstage-cli --verify -a my_agent.py:graph || { echo "agent broken" >&2; exit 1; }A single-shot run (a MESSAGE argument or -f/--file) prints only the agent's
reply — no header, spinner, tool chatter, timing, or color — as soon as its output
is piped (stdout isn't a TTY). Errors and diagnostics go to stderr, and the
process exits non-zero if the turn failed, so a run is safe to capture:
answer=$(langstage-cli --demo "say hi") || echo "run failed" >&2
echo "$answer" # -> (demo agent) You said: say hiPass -q/--quiet to force the same clean output in a terminal.
Your agent file just needs to export a compiled LangGraph graph — langstage-cli
runs any CompiledGraph. A minimal stdlib example (no extra deps):
# my_agent.py — needs only langgraph (a base dependency)
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import MessagesState
from langchain_core.messages import AIMessage
def respond(state):
last = state["messages"][-1].content
return {"messages": [AIMessage(content=f"You said: {last}")]}
g = StateGraph(MessagesState)
g.add_node("respond", respond)
g.add_edge(START, "respond")
g.add_edge("respond", END)
graph = g.compile()Or a full deep agent (requires pip install deepagents):
# my_agent.py
from deepagents import create_deep_agent
from langgraph.checkpoint.memory import MemorySaver
agent = create_deep_agent(
name="My Agent",
model="anthropic:claude-sonnet-4-6",
checkpointer=MemorySaver(),
)Then run it:
langstage-cli -a my_agent.py:graph # or :agent for the deepagents exampleSince 1.0, streaming runs through the shared core's in-process AG-UI adapter
(pip install "langstage-core[agui]"):
import asyncio
from langstage_core import load_agent_spec
from langstage_core.agui import build_agent, iter_chunk_frames
agent = build_agent(load_agent_spec("my_agent.py:graph"))
async def main():
async for chunk in iter_chunk_frames(agent, "Hello!", thread_id="s1"):
if chunk.get("chunk"):
print(chunk["chunk"], end="")
asyncio.run(main())MIT License - see LICENSE file for details.