/new-dcode-agent
You are running the /new-dcode-agent skill. It scaffolds a working agent for the user. Everything it writes is SELF-CONTAINED, so it works from any folder, with no dependency on this skill's own location. Run no git; the user commits.
The three forms (keep them straight)
- SDK program: a standalone Python agent (LangChain
create_deep_agent) the user runs or deploys. Scaffolded into./<name>/in the user's current directory. - dcode agent: a named identity for the dcode CLI (an
AGENTS.md) the user chats with via/agents. Scaffolded into~/.deepagents/<name>/AGENTS.md. - both: a dcode agent that acts as the cockpit for a deployed SDK program.
(This is NOT Claude Code's own subagents, which are a different feature.)
Phase 1: Interview (use AskUserQuestion; batch related questions)
- Form: SDK program / dcode agent / both.
- name (kebab-case; reject names starting with
_, names that match an existing target, or shell-unsafe names). - purpose: one or two sentences.
- (SDK or both): closest starting flavour (custom / project / work-jira / vps-ops / personal); the tools it needs (plain Python functions, plus any MCP servers); the model (a
provider:modelstring for any LangChain provider, or the bundled env-driven connector below); does it change anything? (if yes, it gets an approval gate); how it will run (one-shot / long-running / scheduled / server). - (dcode agent or both): what it knows and operates; which tools or MCP it leans on; its operating rules.
Phase 2: Spec
Show the user exactly what you will create: the target paths, the tools, the model, and the safety posture. Wait for explicit confirmation. Do not write anything until they confirm.
Phase 3: Scaffold
SDK program (form = SDK or both): write ./<name>/ in the user's current directory
Create the folder <name>/ with three files. It is self-contained: agent.py imports its connector from the sibling model.py (a same-directory import, so there is no path manipulation at all).
<name>/model.py (write this verbatim; the env-driven, provider-agnostic connector):
python"""Model connector for this agent. Provider-agnostic, configured from the environment. Targets any OpenAI-compatible Chat Completions endpoint (OpenAI itself, or a compatible gateway). Set these in the environment or a .env file next to this agent: LLM_API_KEY (or OPENAI_API_KEY) required LLM_BASE_URL (or OPENAI_BASE_URL) optional; omit for OpenAI's default endpoint LLM_MODEL optional; the model id (default below) USE_RESPONSES_API optional; set 1 only if your provider supports it """ from __future__ import annotations import os import pathlib from langchain_openai import ChatOpenAI DEFAULT_MODEL = "gpt-4o-mini" # override via LLM_MODEL or the model= argument def _load_env() -> None: """Minimal .env loader (no extra deps): this agent's folder, then the current directory, then ~/.deepagents/.env. Existing environment variables always win.""" here = pathlib.Path(__file__).resolve().parent for path in (here / ".env", pathlib.Path.cwd() / ".env", pathlib.Path.home() / ".deepagents" / ".env"): if not path.is_file(): continue for line in path.read_text().splitlines(): line = line.strip() if not line or line.startswith("#") or "=" not in line: continue key, value = line.split("=", 1) os.environ.setdefault(key.strip(), value.strip()) def chat_model(model: str | None = None, *, temperature: float = 0.0, **kwargs) -> ChatOpenAI: """Return a ChatOpenAI wired to your OpenAI-compatible provider, from env.""" _load_env() key = os.environ.get("LLM_API_KEY") or os.environ.get("OPENAI_API_KEY") if not key: raise RuntimeError("No API key. Set LLM_API_KEY (or OPENAI_API_KEY) in the " "environment or a .env file next to this agent.") base_url = os.environ.get("LLM_BASE_URL") or os.environ.get("OPENAI_BASE_URL") or None use_responses = os.environ.get("USE_RESPONSES_API", "").strip().lower() in ("1", "true", "yes") return ChatOpenAI(base_url=base_url, api_key=key, model=model or os.environ.get("LLM_MODEL") or DEFAULT_MODEL, temperature=temperature, use_responses_api=use_responses, **kwargs)
<name>/agent.py (base, non-mutating flavour; fill in system_prompt and real tools):
python"""<name>: a Deep Agents SDK agent. Run: python agent.py "your prompt" """ from __future__ import annotations import sys from model import chat_model # sibling model.py, same-directory import from deepagents import create_deep_agent def example_tool(query: str) -> str: """Describe what this tool does (stub; replace).""" return f"[stub] {query}" SYSTEM_PROMPT = """You are a helpful agent. TODO: describe the role, scope, and rules.""" def build_agent(): return create_deep_agent( model=chat_model(), # your provider/model from env; pass an id to override tools=[example_tool], system_prompt=SYSTEM_PROMPT, ) if __name__ == "__main__": agent = build_agent() prompt = " ".join(sys.argv[1:]) or "Hello" res = agent.invoke({"messages": [{"role": "user", "content": prompt}]}) print(res["messages"][-1].content)
If the agent can change things (a mutating tool), use this gated pattern instead. The approval gate needs BOTH interrupt_on AND a checkpointer, or it silently does nothing:
python"""<name>: an ops agent with an approval gate. Run: python agent.py "status check" """ from __future__ import annotations import sys from model import chat_model from deepagents import create_deep_agent from langgraph.checkpoint.memory import InMemorySaver def check_status() -> str: """Read-only status check (stub).""" return "ok" def restart_service(service: str) -> str: """MUTATING, gated by approval.""" return f"[would restart {service}]" def build_agent(): return create_deep_agent( model=chat_model(), tools=[check_status, restart_service], system_prompt="You are an ops engineer. Investigate read-only first; changes need approval.", interrupt_on={"restart_service": True}, checkpointer=InMemorySaver(), # required, or the gate no-ops ) if __name__ == "__main__": agent = build_agent() cfg = {"configurable": {"thread_id": "s1"}} res = agent.invoke({"messages": [{"role": "user", "content": " ".join(sys.argv[1:]) or "status check"}]}, config=cfg) print("[paused for approval]" if res.get("__interrupt__") else res["messages"][-1].content)
Verify a mutating agent actually pauses (a single __interrupt__ check can false-negative):
pythoncfg = {"configurable": {"thread_id": "verify"}} res = agent.invoke({"messages": [{"role": "user", "content": "<ask it to run the mutating tool>"}]}, config=cfg) paused = bool(res.get("__interrupt__")) or bool(getattr(agent.get_state(cfg), "next", None)) print("approval gate fired:", paused) # if False, fix interrupt_on + checkpointer before shipping
Flavour adjustments (start from the base or gated file above and change these):
- custom: the base file as-is. A blank starting point.
- project: a code/repo assistant. Add a read-only
run_tests()tool (shell out to the project's test command) and a senior-engineer system prompt. PointLLM_MODELat a coding-tuned model. - work-jira: a Jira assistant. Either stub
search_issues(jql), or load MCP tools withlangchain-mcp-adapters(MultiServerMCPClient) readingJIRA_*from the environment at runtime. Never hard-code credentials. - vps-ops: a server-ops agent. Use the GATED pattern above (mutating tools behind
interrupt_on+InMemorySaver). - personal: a personal-assistant agent. Add simple tools (read a tasks file, append a note) and a concise system prompt.
<name>/README.md (write a short one): what the agent does, then:
bashpip install deepagents langchain-openai # plus langchain-mcp-adapters if it uses MCP export LLM_API_KEY=your-key # or put it in a .env next to agent.py # optional: export LLM_BASE_URL=... export LLM_MODEL=... python agent.py "your prompt"
dcode agent (form = dcode agent or both): write ~/.deepagents/<name>/AGENTS.md
- Plain Markdown, NO YAML frontmatter: a dcode agent's AGENTS.md is memory layered on dcode's base prompt, loaded fresh each run (not a program).
- Sections: Role (who it is, default posture); Context / setup (what it knows; commands it has; link extra files); How to operate (workflow; when to delegate to subagents); Operating rules (read-only default; approval before destructive actions; no secrets).
- Keep it lean (injected every run); push bulky reference into separate files and reference them.
- If the file already exists, preserve any
<!-- ... -->managed block (dcode self-edits this file). - Optional subagents at
~/.deepagents/<name>/agents/<sub>/AGENTS.md. A subagent's frontmatter is onlyname,description,model(with a provider prefix, e.g.openai:gpt-4o-mini); the body is its system prompt; the folder and filename must beagents/<sub>/AGENTS.md. - If form = both: the dcode agent's AGENTS.md references the SDK program (its path and how to run it), so it is the interactive cockpit for the deployed agent.
Phase 4: Smoke-test
- SDK:
cd <name> && python agent.py "hello"(import plus a minimal run; construct-only if there is no key). If it mutates, run the verification snippet and confirm the gate fires. - dcode agent: confirm
dcode agents listshows<name>(it auto-discovers the directory).
Guardrails (never violate)
- No secrets in any file: keys come from the environment or a
.envat runtime only. - The scaffolded SDK agent is self-contained:
agent.pyplus a siblingmodel.py, same-directory import, no path manipulation. - A mutating tool MUST have
interrupt_onplus acheckpointer, or the approval gate silently does nothing. - Provider-agnostic: never bake a provider, model id, or key into the generated code.
- Run no
git; the user commits.


