LemonCliRunners. ClaudeSubagent
(lemon_cli_runners v0.1.0)
View Source
High-level API for using Claude (Claude Code CLI) as a collaborating subagent.
This module provides a convenient interface for spawning Claude CLI sessions and interacting with them over time. Unlike one-shot LLM calls, Claude subagents maintain state across multiple prompts, enabling iterative collaboration.
Features
- Long-lived sessions: Keep talking to the same Claude session
- Session persistence: Resume sessions after interruptions
- Event streaming: Process events as they happen
- Progress tracking: Monitor tools, commands, file changes
Quick Start
# Start a new session
{:ok, session} = ClaudeSubagent.start(
prompt: "Create a GenServer that manages a counter",
cwd: "/path/to/project"
)
# Process events
for event <- ClaudeSubagent.events(session) do
case event do
{:started, resume_token} ->
IO.puts("Session: #{resume_token.value}")
{:action, action, :completed, ok: true} ->
IO.puts("Completed: #{action.title}")
{:completed, answer, _opts} ->
IO.puts("Done: #{answer}")
end
end
# Send a follow-up prompt to continue the conversation
{:ok, session2} = ClaudeSubagent.continue(session, "Now add decrement functionality")Session Resume
Sessions can be resumed after the process terminates:
# Get the resume token from a completed session
token = ClaudeSubagent.resume_token(session)
# Later, resume the session
{:ok, session} = ClaudeSubagent.resume(token, "Continue from where we left off")Event Types
Events are normalized into a simple format:
{:started, resume_token}- Session began{:action, action, phase, opts}- Action lifecycle (phase = :started | :updated | :completed){:completed, answer, opts}- Session ended (opts may include :resume, :error, :usage)
Summary
Functions
Collect all events and return the final answer.
Continue an existing session with a follow-up prompt.
Get the event stream as an enumerable of normalized events.
Resume an existing Claude session with a new prompt.
Get the resume token from a session.
Run a Claude task synchronously and return the answer.
Start a new Claude subagent session.
Types
@type session() :: %{ pid: pid(), stream: LemonAgent.EventStream.t(), resume_token: LemonCore.ResumeToken.t() | nil, token_agent: pid() | nil, cwd: String.t() }
A Claude subagent session
@type subagent_event() :: {:started, LemonCore.ResumeToken.t()} | {:action, action :: map(), phase :: atom(), opts :: keyword()} | {:completed, answer :: String.t(), opts :: keyword()} | {:error, reason :: term()}
Normalized event from the subagent
Functions
Collect all events and return the final answer.
This is a convenience function that processes all events and returns the final answer string. Useful when you don't need to track progress.
Example
{:ok, session} = ClaudeSubagent.start(prompt: "What is 2+2?", cwd: ".")
answer = ClaudeSubagent.collect_answer(session)
IO.puts(answer) # "4" or similar
Continue an existing session with a follow-up prompt.
This is a convenience wrapper around resume/2 that extracts the
resume token from a completed session.
Example
{:ok, session1} = ClaudeSubagent.start(prompt: "Create a module")
_events = ClaudeSubagent.events(session1) |> Enum.to_list()
{:ok, session2} = ClaudeSubagent.continue(session1, "Add a public function")
@spec events(session()) :: Enumerable.t()
Get the event stream as an enumerable of normalized events.
Events are transformed from internal CLI runner events to a simpler format. The stream completes when the session ends.
Event Format
{:started, resume_token}- Session began, token can be used for resume{:action, action, phase, opts}- Action lifecycle eventactionis a map with:id,:kind,:title,:detailphaseis:started,:updated, or:completedoptsmay includeok: boolean()for completed phase
{:completed, answer, opts}- Session endedoptsmay include:resume,:error,:usage
{:error, reason}- Error occurred
@spec resume( LemonCore.ResumeToken.t(), keyword() ) :: {:ok, session()} | {:error, term()}
Resume an existing Claude session with a new prompt.
Example
token = %ResumeToken{engine: "claude", value: "session_abc123"}
{:ok, session} = ClaudeSubagent.resume(token,
prompt: "Continue implementing the delete method",
cwd: "/home/user/project"
)
@spec resume_token(session()) :: LemonCore.ResumeToken.t() | nil
Get the resume token from a session.
The token is populated after the session starts and can be used to resume the session later. The token is updated as events are processed, so call this after processing events.
Run a Claude task synchronously and return the answer.
This is a convenience function that starts a session, waits for completion, and returns the answer.
Options
:prompt- The task prompt (required):cwd- Working directory:timeout- Timeout in ms:on_event- Optional callbackfn event -> :ok endfor progress tracking
Example
answer = ClaudeSubagent.run!(
prompt: "Explain what this code does: ...",
cwd: "/path/to/project",
on_event: fn event -> IO.inspect(event) end
)
Start a new Claude subagent session.
Options
:prompt- The initial prompt/task (required):cwd- Working directory (default: current directory):timeout- Session timeout in ms (default::infinity):model- Optional model override (passed to Claude CLI--model)
Returns
{:ok, session} on success, {:error, reason} on failure.
Example
{:ok, session} = ClaudeSubagent.start(
prompt: "Implement a binary search tree",
cwd: "/home/user/project"
)