Skip to content

agentproc CLI ​

The agentproc command-line tool is the canonical bridge-side runner. It reads a profile YAML, spawns the configured agent process, parses stdout per the protocol spec, and prints the reply. Any conformant agent — from hub profiles to your own — can be driven through this single entry point.

The CLI ships in the same npm package as the Node SDK:

bash
npm install -g agentproc       # global install
# or:
npx agentproc ...              # run without installing

A Go implementation ships in the sdk/go module — same commands, same flags, same output semantics:

bash
go install github.com/jeffkit/agentproc/sdk/go/cmd/agentproc@latest
# or grab a prebuilt binary (linux/darwin amd64+arm64, windows amd64)
# from the GitHub Releases page, tagged agentproc-cli/v*

Go CLI divergence: the hub commands resolve profiles via the jsDelivr CDN and cache them at ~/.agentproc/cache/hub/<name>/ (24h TTL) — there is no bundled profile copy inside the binary, so the first hub command needs network. AGENTPROC_HUB_REF pins fetches to an exact upstream ref, same as the Node/Python CLIs.

Two ways to invoke ​

The CLI supports two equivalent entry points:

Entry pointWhen to use
agentproc hub <subcommand>Recommended. Run a profile from the official hub with zero local files. The CLI fetches from GitHub on first use, caches at ~/.agentproc/cache/hub/<name>/ (24h TTL), and defaults the agent's cwd to your current directory.
agentproc --profile <path>Run a profile YAML you already have locally (your own, an installed hub profile, or a repo checkout). Pass --cwd to control where the agent runs.

Quick start ​

bash
# Smoke test, no API key, no clone:
agentproc hub run echo-agent -p "hello"
# → You said: hello

# Real agent against your project:
cd ~/projects/my-app
agentproc hub run claude-code -p "explain this codebase" --env ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY

For local profiles (no hub fetch):

bash
git clone https://github.com/jeffkit/agentproc
cd agentproc

# echo-agent (uses {{PROFILE_DIR}} so cwd doesn't matter)
agentproc --profile hub/echo-agent/profile.yaml --prompt "hello"

# claude-code — point --cwd at your project so claude works on it
agentproc --profile hub/claude-code/profile.yaml \
          --prompt "explain this codebase" \
          --cwd /path/to/your/project \
          --env ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY

GITHUB_TOKEN raises the rate limit

Anonymous hub fetches are capped at ~60/hour. Raise to 5,000/hour with:

bash
export GITHUB_TOKEN=$(gh auth token)   # or any personal access token

If you'd rather skip the network entirely, use agentproc --profile ./hub/<name>/profile.yaml ... against a local checkout.

Hub subcommands ​

CommandPurpose
agentproc hub listList all profiles in the hub
agentproc hub show <name>Show a profile's README
agentproc hub run <name> [opts]Fetch (if needed) and run a profile
agentproc hub install <name>Copy a profile to ./<name>/ for local editing

hub run accepts the same runner flags as --profile (see below), with one convenience: if you don't pass --cwd, it defaults to your current directory (so the wrapped CLI operates on whatever project you're in).

Add --refresh to any hub command to force re-fetch from GitHub.

Set the AGENTPROC_HUB_REF environment variable to pin fetches to a specific tag, branch, or commit sha (default main). The profile cache is keyed by ref — switching the variable re-fetches rather than serving a cached copy from another ref.

Usage ​

agentproc --profile <path.yaml> --prompt "hello" [options]

Required (for --profile mode) ​

FlagDescription
--profile, -p <path>Profile YAML path
--prompt <text>User message (or use --stdin)

About -p

In --profile mode, -p is the short form of --profile. In hub run mode, since the profile is identified by name (positional, not a path), -p is reused as the short form of --prompt instead. This is the only flag whose short form changes between modes — when in doubt, use the long form.

Session ​

FlagDescription
--session <id>Previous session id for multi-turn continuity
--session-name <name>Human-readable session name (default: default)

Attachments ​

FlagDescription
--image-url <url>Image attachment URL (carried in the turn's attachments)
--file-url <url>File attachment URL (carried in the turn's attachments)

Execution ​

FlagDescription
--cwd <path>Override profile.cwd. Relative paths resolve against the profile's directory. In hub run, defaults to your current directory.
--env KEY=VALUEExtra env var (repeatable)
--timeout <secs>Override profile.timeout_secs (0 = no limit)
--journal <path>Append an NDJSON event/decision journal to <path> (opt-in; off by default, never written to stdout)
--no-streamDisable streaming (ignore {"type":"partial"} events)

Output ​

FlagDescription
--verboseForward protocol lines to stderr (default)
--quietSuppress protocol lines on stderr
--rawQuiet mode: only print the final reply body (no live events on stderr)
--stdinRead prompt from stdin instead of --prompt

Other ​

FlagDescription
--versionPrint version and exit
--help, -hShow help

Output semantics ​

Default mode ​

StreamContent
stderrNDJSON events as they arrive ({"type":"partial"}, {"type":"error"}; session ids via the agentproc:session:<id> trailer)
stdoutFinal reply body, printed after the agent exits
exit0 success · 1 error (including timeouts) · 2 usage · 130 SIGINT / 143 SIGTERM (the agent group is killed first)

(The underlying RunResult.ExitCode distinguishes further — 124 timeout, 125 cancelled — but the CLIs fold every failure to 1, matching the reference Python/Node CLIs.)

The final session id is also printed on stderr as agentproc:session:<id>, so shell scripts can capture it:

bash
output=$(agentproc -p prof.yaml --prompt "hi" 2>/tmp/err.log)
session=$(grep '^agentproc:session:' /tmp/err.log | cut -d: -f3)
agentproc -p prof.yaml --prompt "follow up" --session "$session"

--raw mode ​

Quiet mode: suppress the live event echo on stderr and print only the final reply body to stdout. (All CLIs still run the full protocol parser — the reply is the assembled result, not raw agent bytes.)

bash
agentproc -p prof.yaml --prompt "hi" --raw

Examples ​

Multi-turn with claude-code ​

bash
# First turn — capture the session id
agentproc -p hub/claude-code/profile.yaml \
          --prompt "what is this codebase?" \
          --cwd ~/projects/myapp \
          --env ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
          2>/tmp/err.log
session=$(grep '^agentproc:session:' /tmp/err.log | cut -d: -f3)

# Second turn — continue the session
agentproc -p hub/claude-code/profile.yaml \
          --prompt "tell me more about the auth module" \
          --cwd ~/projects/myapp \
          --env ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
          --session "$session"

Prompt from stdin ​

bash
echo "what files are in this directory?" | \
  agentproc -p hub/claude-code/profile.yaml --cwd . --stdin

Quiet mode (clean stdout for piping) ​

bash
agentproc -p hub/claude-code/profile.yaml --prompt "summarize" --quiet | jq .

How it implements the spec ​

The CLI is a thin wrapper over the SDK's run() function in sdk/node/src/runner.js. That module is the canonical reference implementation of the AgentProc bridge-side contract:

  • Profile parsing: accepts both top-level form (command: at root) and hub form (command: nested under agentproc:).
  • Placeholder substitution: , , in command, args, cwd, and env values — no shell involved. is not a placeholder; the message travels via stdin.
  • Relative cwd: when cwd is a relative path and is known (i.e. the CLI was invoked with a profile path), it resolves against the profile's directory rather than the process cwd.
  • Turn input (stdin): writes one {"type":"turn",...} NDJSON line to the agent's stdin (message, session_id, session_name, attachments, permission, protocol_version), then EOF — unless permission: true, in which case stdin stays open for {"type":"permission_response"} frames. The per-turn request does not travel in env vars.
  • Env injection: the profile env block (with ${VAR} expansion gated by env_allowlist) plus a fixed infra set (PATH/HOME/TERM/…). --env KEY=VALUE adds per-run extras.
  • stdout classification: each line is a JSON object dispatched on type — {"type":"partial"} (forwarded when streaming: true), {"type":"result"} (terminal reply body; optional session_id / usage), {"type":"error"} (fails the turn). session_id is a field on events (first non-empty persisted; early omit OK; SHOULD attach once known). Non-JSON / unknown type lines are logged and ignored.
  • Timeout handling: SIGTERM → kill_grace_secs (default 5s) → SIGKILL, all three targeted at the agent's whole process group (so a shell tool call or sub-agent the CLI spawned dies with it), not just the direct child. Exit code 124. The limit itself is the earliest expiry of the profile's timeout_secs, budget_secs, and deadline. If the agent exits while a helper still holds its stdout/stderr open, the run still ends: the pipes get a 1s drain window and are then abandoned (with a stderr warning) instead of hanging the turn.
  • Exit codes: 0 success · 1 error (including when {"type":"error"} was emitted, and including timeouts — the runner's 124 surfaces in RunResult.ExitCode, the CLI folds it to 1).

If you're writing your own bridge in another language, runner.js is the spec in code form.

Released under the MIT License.