Files
paseo/public-docs/cli.md
T
HelloThereEveryone 74c8172f8b Wait for a plugin reload to finish instead of failing at the request deadline (#6306)
* Wait for plugin reload completion instead of the request deadline

The daemon answers plugin.reload.request only once the plugin is running
again or has failed, and a reload queues behind other plugin lifecycle
work such as an install with a preparation command. The shared client
applied its sixty-second session RPC deadline to that request, so a slow
reload failed with "Timeout waiting for message" while the daemon kept
loading the plugin, inviting a reload the daemon was still running.

Reload now waits for the daemon's own answer. Enable and disable keep the
session deadline, and a real startup failure still reaches the caller.

* Cover a slow plugin reload end to end

The CLI fixture now holds its own git build on a gate file, starts a
reload of an installed plugin behind that queued install, observes the
reload request at the proxy, keeps the gate closed past the client's
former one-minute deadline, and then asserts the reload's real result:
CLI exit 0, the plugin running, and exactly one reactivation. Listing the
plugin during the wait stays readable and does not restart it. A plugin
that cannot start still fails its reload with the daemon's error.

The request proxy that recorded install sources becomes a small observer
that records any session request, so the wait is measured from the
request boundary rather than from process start.

Verified: npx tsx packages/cli/tests/e2e/plugin-lifecycle.test.ts exits 0
in 135 s; the same fixture without the new scenario takes 66 s.

* Document how a reload reports its result

The CLI docs now say `paseo plugin reload` waits for the daemon to stop
and start the plugin, that cancelling the command or losing the
connection does not stop the daemon's work, and that `plugin ls` shows
the previous state until the reload finishes.

The plugin reference records the lifecycle fact behind it: a reload
queues behind the daemon's other plugin work, the daemon answers when
the plugin is running again or has failed, and status reads, Settings and
the logs are not the reload's outcome.

* Cover the Settings reload wait end to end

Adds a browser scenario for the ticket's user-facing half: with a gated
git install holding the daemon's plugin lifecycle queue, the row's Reload
goes out, the test keeps the gate closed past sixty seconds, asserts the
row stays pending with no error feedback, then releases the build and
asserts the daemon's plugin.reload.response and the app's "Reloaded
e2e-plugin" result.

The scenario turns plugins on through the daemon instead of the global
switch, whose transient "Plugins enabled" confirmation is not its
subject. The wait is measured from the reload request frame, which is an
outbound frame, not from the response stream.

Verified: npx playwright test --project=browser
e2e/browser/plugin-management.spec.ts -g "keeps Reload pending past a
minute" -> 1 passed (1.5m); plugin-reload-pending.png,
plugin-reload-past-deadline.png and plugin-reload-completed.png show the
pending row, the surface past the former deadline with no error, and the
finished reload.

* Correct the status guidance for a cancelled reload

Review finding: the note said a listing keeps the plugin's previous
state until the reload finishes, but that only holds while the reload is
still queued. Once it executes, the daemon removes the plugin from the
runtime catalog before stopping it (plugins/runtime.ts:377-382) and
resolvePluginStatus maps an enabled plugin that is not running to
"failed" (plugins/index.ts:71-78), so a listing read in that window
shows failed for a healthy reload.

Both passages now separate the two windows and say that neither a
listing nor the logs report the outcome of the outstanding reload.
The interim "failed" itself is TUC-1401.

* Harden the reload coverage against failures and lost connections

Review findings folded into the tests:

The Settings scenario releases its gated build in a finally block. Before
this, a failing assertion left the build polling forever, the daemon's
plugin lifecycle queue stayed blocked, and the fixture teardown's own
removePlugin timed out behind it. It also asserts the row's actions are
enabled again once the reload settles, next to the result feedback.

The connection-close regression now reconnects and asserts nothing
resends the lost reload; the no-replay invariant already covered for
legacy requests now holds for a pending reload too.

Verified: npx vitest run packages/client/src/daemon-client.test.ts -> 161
passed. npx playwright test --project=browser
e2e/browser/plugin-management.spec.ts -g "keeps Reload pending past a
minute" -> 1 passed (1.4m).

* Let the gated build finish before its files go

Review finding on packages/cli/tests/e2e/plugin-lifecycle.test.ts:450:
the finally block released the gate and deleted the gate directory
without waiting for the install and the reload it had started. The build
polls for the gate file, so cleanup could delete the release before the
child saw it and leave the build waiting until daemon shutdown.

The fixture now keeps both promises, releases the gate, and awaits them
before deleting their files.

Verified: npx tsx packages/cli/tests/e2e/plugin-lifecycle.test.ts -> exit
0, 161 s.

* Read the reload scenario as intent, not transport code

Review finding: the new browser scenario kept frame parsing, selectors,
polling, deadline arithmetic, screenshots and gate cleanup in one body.
docs/testing.md asks for complex setup in reusable helpers.

The wire observation, the gated queue hold, the row-state assertions, the
deadline wait and the surface captures are helpers now, and the body
reads as the flow: open Settings, hold the queue, reload, still pending
past the deadline, release, read the daemon's result.

Verified: npx playwright test --project=browser
e2e/browser/plugin-management.spec.ts -g "keeps Reload pending past a
minute" -> 1 passed (1.4m).

* Keep the reload lifecycle fact in one document

Review finding: the cli.md paragraph repeated the queue and interim-state
explanation that the plugin reference already owns. CLAUDE.md: "One fact,
one doc. Every other mention is a link."

The reference keeps the full explanation; the CLI page states what the
command does and links there.

* Release the gated build when its setup fails

Review finding: holdPluginLifecycleQueue started the install, then
waited up to 30 seconds for the build's started marker. A preparation
that took longer made the helper throw before it returned its cleanup
handle and before the test entered its try block, so nothing wrote the
release file: the build polled forever, the daemon's plugin lifecycle
queue stayed blocked, and the fixture's own removePlugin timed out
behind it.

The helper now owns that failure path: it releases the gate and drains
the install before rethrowing, so a queue it hands out is always
settleable.

Verified on Linux (Chromium, Playwright):
- With the started marker suppressed in a temporary local patch, the run
  fails at the 30-second marker wait inside the helper and the teardown
  still completes (both attempts ~33 seconds, no removePlugin request
  timeout).
- With the marker in place, the scenario passes: 1 passed (1.4m).

* Keep an early install failure out of the unhandled path

Review finding: the marker wait in holdPluginLifecycleQueue ran before
the install promise had a rejection handler, so a preparation that failed
or a connection that closed before the build wrote its marker surfaced as
an unhandled rejection, which fails the run before the helper's own catch
can drain the queue.

The helper attaches a handler the moment the install starts and races the
marker wait against the install, so an early failure reaches the caller
as the daemon's own error, a build that never writes its marker still
fails as a 30-second marker timeout, and every path releases the gate and
drains the install.

Verified on Linux (Chromium, Playwright), patching the build command
locally for each failure branch:
- Early failure (build exits 1): fails in 4.2 s with the daemon's
  DaemonRpcError for plugin.source.install.request, no unhandled
  rejection, teardown completes.
- No marker (build waits for the gate): fails at the 30-second marker
  wait, no unhandled rejection, teardown completes with no removePlugin
  request timeout.
- Unpatched: 1 passed (1.5m).

* Keep a failed launch from ending the run before its cleanup
2026-10-17 10:57:10 +02:00

28 KiB

title, description, nav, order, category
title description nav order category
CLI reference Paseo CLI reference: manage projects, workspaces, agents, plugins, scripts, schedules, daemons, and permissions from your terminal. CLI reference 35 Orchestration

CLI reference

The Paseo CLI lets you manage agents from your terminal. It's the same interface exposed by the daemon's API, so anything you can do in the app you can do from the command line.

Agent orchestration: You can tell coding agents to use the Paseo CLI to spawn and manage other agents. Paseo recognizes the calling agent, so CLI-created workers get the same workspace and parent defaults as MCP-created workers.

Quick reference

paseo run "fix the tests"            # Start an agent
paseo ls                             # List running agents
paseo attach <id>                    # Stream agent output
paseo send <id> "also fix linting"   # Send follow-up task
paseo logs <id>                      # View agent timeline
paseo stop <id>                      # Stop an agent
paseo usage                          # Show account quotas and balances

Account usage

Show account quotas and balances from the host's usage sources:

paseo usage
paseo usage ls --refresh
paseo usage ls --agent <agent-id>
paseo usage inspect <report-id>
paseo usage inspect <report-id> --refresh

paseo usage is equivalent to paseo usage ls. The summary groups windows and balances by account, with report IDs, source and account labels, status, and fetch timestamps. Missing values appear as -; unavailable and failed reports include their reasons.

Copy the full report ID from the list into inspect to see reset times, balances and units, plan details, supplied forecasts, and login failures with recovery instructions. Report IDs require exact matches.

--agent selects the account quota associated with an agent. It does not measure that agent's token consumption or cost. Use the full agent ID, and do not combine --agent with inspect.

Queries use the host's five-minute cache when available. Add --refresh to bypass it. Use --host or --home to select a daemon. Hosts without usage-source support return an update message.

Usage in scripts

paseo usage ls --json
paseo usage inspect <report-id> --json
paseo usage ls --quiet

List JSON is always an array, including [] when no reports are returned. Inspect JSON is one report entry. Each entry includes id, sourceId, sourceLabel, account, fetchedAt, and a nested report. Read report.status before accessing its data:

Status Fields
available windows, with optional planLabel, balances, and details
unavailable problem, describing expired or rejected credentials or missing quota
error error, the failure message

Entries may also include loginErrors. Structured output preserves numeric values and optional fields. Standard --format yaml, --no-headers, and --no-color options are supported; --quiet prints full report IDs.

A completed query exits with status 0, including when individual reports are unavailable or failed. Connection or request failures, unsupported hosts, unknown report IDs, and conflicting selectors exit with status 1 and write the error to stderr. Human output says No usage reports returned for an empty result; this does not establish whether account discovery succeeded for every source.

Provider diagnostics

Ask the daemon to inspect the provider environment it actually uses:

paseo provider diagnostic claude
paseo provider diagnostic codex --json
paseo --host devbox:6767 provider diagnostic opencode

The diagnostic includes the configured command, daemon PATH and shell, matching binaries, resolved path, version, model count, and provider status. Use the global --host option for a remote daemon. This is the same diagnostic shown under Settings → your host → Providers → provider → Diagnostic.

Running agents

Use paseo run to start a new agent with a task:

paseo run "implement user authentication"
paseo run --provider codex "refactor the API layer"
paseo run --no-wait "run the focused test suite"
paseo run --output-schema schema.json "name this branch"
paseo run --new-workspace worktree --worktree-mode branch-off --new-branch feature/x --base origin/main "implement feature X"
paseo run --workspace <workspace-id> "review the current diff"
paseo run --output-schema schema.json "extract release notes"
paseo run --output-schema '{"type":"object","properties":{"summary":{"type":"string"}},"required":["summary"]}' "summarize release notes"

From a human shell, a bare paseo run creates a new local workspace for the current directory. Use --workspace <id> to add the agent to an existing workspace, or --new-workspace local|worktree to explicitly create a separate workspace for the run.

Worktree creation accepts --worktree-mode branch-off|checkout-branch|checkout-pr plus the matching --new-branch/--base, --branch, or --pr-number/--forge options. Use --worktree-slug to choose the managed directory slug.

When an existing Paseo agent runs the same command, Paseo recognizes it through PASEO_AGENT_ID. Without explicit placement, the new agent becomes its subagent in the same workspace. --workspace can place that subagent elsewhere without changing its parent.

Use --output-schema to return only matching JSON output. You can pass a schema file path or an inline JSON schema object. This mode cannot be used with --no-wait.

By default, paseo run waits for completion. Use --no-wait to return immediately while the agent keeps running, the same flag paseo send takes.

The old run --background and -d aliases still mean --no-wait. They are deprecated, hidden from help, and print a warning to stderr. Use --no-wait in scripts. Workspace visibility is selected separately with workspace create --background; this release does not repurpose run --background.

Projects

Register the current directory as a project, then list the projects known to the daemon:

cd ~/dev/my-app
paseo project create
paseo project ls

Use the project ID from paseo project ls to rename, reset, or delete a project:

paseo project rename <project-id> "My app"
paseo project rename <project-id> --reset
paseo project delete <project-id>

--reset restores the name derived from the project directory. Deleting a project archives its active workspaces and removes the project from Paseo. It does not delete the project directory.

For a local daemon, paseo project create [path] defaults to the current directory and resolves relative paths on the CLI machine. When you use the global --host option or PASEO_HOST, provide a path that the target daemon can access:

paseo --host devbox:6767 project create /srv/repos/api

The remote daemon interprets that path on its own machine. See Workspaces for how projects group working directories and sessions.

Workspaces

Create a workspace independently when you want to prepare its files before starting an agent:

paseo workspace create --isolation local --path ~/dev/my-app --title main

paseo workspace create \
  --isolation worktree \
  --path ~/dev/my-app \
  --mode branch-off \
  --new-branch feature/auth \
  --worktree-slug feature-auth \
  --base origin/main

paseo workspace create \
  --isolation worktree \
  --path ~/dev/my-app \
  --mode checkout-branch \
  --branch feature/existing \
  --worktree-slug existing-copy

paseo workspace create \
  --isolation worktree \
  --path ~/dev/my-app \
  --mode checkout-pr \
  --pr-number 2186

Add --background to hide a workspace from default discovery. Its agents and terminals keep ordinary history, persistence, hooks, and restart recovery. --no-background explicitly overrides caller-workspace inheritance; omission inherits when run by a Paseo agent and defaults to false from a human shell. Visibility is creation-only.

paseo workspace create --isolation local --background --path /path/to/repo
paseo run --workspace <workspace-id> --no-wait "review the checkout flow"

Exact workspace and agent IDs remain accessible regardless of listing visibility.

Then list, use, rename, or archive it:

paseo workspace ls
paseo workspace ls --background               # Also include background workspaces
paseo run --workspace <workspace-id> "implement authentication"
paseo workspace rename <workspace-id> "Auth rework"
paseo workspace rename <workspace-id> --reset   # back to the branch or directory name
paseo workspace archive <workspace-id>

Add --forge <name> to PR checkout when Paseo cannot infer the forge from the source checkout. See Git worktrees for setup hooks and services.

Terminals

Use the workspace ID when multiple workspaces share a directory:

paseo terminal create --workspace <workspace-id> --name Development
paseo terminal ls --workspace <workspace-id> --json
paseo terminal send-keys <terminal-id> -l "echo ready"
paseo terminal send-keys <terminal-id> Enter
paseo terminal capture <terminal-id>
paseo terminal kill <terminal-id>

Creation defaults to the workspace directory. Add --cwd <absolute-path> to change the process directory while keeping that workspace as the owner. Unknown and archived workspace IDs fail.

Without --workspace, creation opens the project at --cwd or the current directory and reuses its oldest active workspace. Listing without --workspace filters by --cwd or the current directory and can include multiple workspaces. ls --all lists every terminal on the host and cannot be combined with directory or workspace filters.

Create and list results include id, name, cwd, and workspaceId. Use --json for structured output and the global --host option to target another daemon. These commands require a host that supports the workspace terminal API; older hosts return an update message.

Workspace scripts

List, start, and stop the scripts configured in a workspace's paseo.json:

paseo script ls
paseo script start web
paseo script stop web

By default, Paseo selects the workspace whose directory is the current directory. Pass --cwd <path> to select a different directory, or --workspace <workspace-id> when a directory has multiple workspaces. Use the global --host option to target another daemon. These commands also accept standard output options such as --json.

The output includes each script's lifecycle and supervised terminal ID. Services also include their assigned port, proxy URL, and health. See Git worktrees for paseo.json configuration.

Plugins

Trust every plugin you add. paseo plugin add and paseo plugin install mean “I trust this codebase.” Plugin server code and Git preparation commands run unsandboxed with the daemon user's access on the daemon host; client contributions run inside Paseo. Dependencies and future updates are part of that decision. With the global --host option, commands run on the remote daemon host.

Create and manage trusted plugins on a daemon:

paseo plugin init ./plugin
paseo plugin install ./plugin
paseo plugin add owner/slug
paseo plugin add git:owner/repository
paseo plugin add https://gitlab.com/group/repository.git --ref main
paseo plugin add git:owner/monorepo:plugins/review
paseo plugin ls [id]
paseo plugin update my-plugin
paseo plugin update --all
paseo plugin reload my-plugin
paseo plugin logs my-plugin
paseo plugin disable my-plugin
paseo plugin enable my-plugin
paseo plugin remove my-plugin

paseo plugin reload <id> reports the result of the reload itself: it waits while the daemon stops and starts the plugin, however long that takes, and it queues behind the daemon's other plugin work. Cancelling the command or losing the connection does not stop the daemon's work, and no listing or log entry settles what a cancelled reload did — the Plugin reference covers those interim states. Running the reload again is what reports the result: it waits behind the outstanding one, then stops and starts the plugin once more.

Bare owner/slug installs a reviewed registry listing. Use git:owner/repository for GitHub source, or ./plugin for a local directory. Append :<directory> for a plugin in a monorepo.

paseo plugin ls [id] does not contact the remote. paseo plugin logs <id> returns the plugin's recent daemon-side stdout and stderr. Add --json for structured entries, or run paseo --host <target> plugin logs <id> for another daemon. See the Plugin reference for installation, trust, lifecycle, and log-retention behavior.

Listing agents

paseo ls                    # Non-archived agents in active workspaces
paseo ls -a                 # Also include archived agents
paseo ls -g                 # Non-archived agents across all workspaces
paseo ls -a -g --json       # All agents, including archived, as JSON
paseo ls --background       # Also include agents in background workspaces

Streaming output

Use paseo attach to stream an agent's output in real-time:

paseo attach abc123   # Attach to agent (Ctrl+C to detach)

Agent IDs can be shortened, abc works if it's unambiguous.

Sending messages

Send follow-up tasks to a running or idle agent:

Use the recipient's agent ID from paseo ls, or copy it from the agent's tab.

paseo send <id> "now run the tests"
paseo send <id> --image screenshot.png "what's wrong here?"
paseo send <id> --no-wait "queue this task"

Viewing logs

paseo logs <id>                  # Full timeline
paseo logs <id> -f               # Follow (streaming)
paseo logs <id> --tail 10        # Last 10 entries
paseo logs <id> --filter tools   # Only tool calls

Waiting for agents

Block until an agent finishes its current task:

paseo wait <id>
paseo wait <id> --timeout 60   # 60 second timeout

Useful in scripts or when one agent needs to wait for another.

Schedules

Run an agent on a cron schedule. The CLI also accepts simple cadence presets and compiles them to cron. See Schedules from the CLI for the full reference.

paseo schedule create --every 30m --cwd ~/dev/my-app "Continue the refactor and leave a note."
paseo schedule ls
paseo schedule pause <id>

Permissions

Agents may request permission for certain actions. Manage these from the CLI:

paseo permit ls                # List pending requests
paseo permit allow <id>        # Allow all pending for agent
paseo permit deny <id> --all   # Deny all pending

Agent modes

Change an agent's operational mode (provider-specific):

paseo agent mode <id> --list   # Show available modes
paseo agent mode <id> bypass   # Set bypass mode
paseo agent mode <id> plan     # Set plan mode
paseo agent detach <id>        # Make a subagent top-level

Detaching is an explicit lifecycle action, not a creation flag. The agent keeps running; only its relationship to its parent changes.

Daemon management

Define an instance once, then start its saved configuration:

paseo daemon config set daemon.listen 127.0.0.1:6799 --home ~/paseo-test
paseo daemon config set daemon.relay.enabled false --home ~/paseo-test
paseo daemon start --home ~/paseo-test
paseo project ls --home ~/paseo-test
paseo daemon restart --home ~/paseo-test
paseo daemon stop --home ~/paseo-test

start runs in the background and reports the actual listening address and supervisor PID. It accepts only home selection and --timeout <seconds> (default 600). If waiting times out, the supervisor remains running: use the printed status, log, and stop instructions. A worker that exits before becoming ready makes startup fail.

restart requests a replacement worker from the existing supervisor. It rereads the file and retains the supervisor's original environment and arguments. Success confirms a different ready worker, following a changed address for a home target. It never starts a stopped daemon or refreshes the supervisor binary. A timeout reports whether the request was acknowledged; it does not prove why reconnection failed.

stop --home waits for that local supervisor to exit. On POSIX it signals the supervisor without contacting a TCP endpoint. On Windows it uses the ready daemon's shutdown RPC; an unbound instance requires explicit --force. --force permits forced process-tree cleanup after the graceful timeout (default 15 seconds). stop --host only reports shutdown requested; remote process exit is not verified. A service manager may start another instance after the captured supervisor exits.

status separates local supervisor state, its published endpoint, the configured address, and RPC reachability. A stopped home is never probed at its configured address. An unbound live supervisor is not ready. If an authenticated local connection remains open but status details time out, the result stays reachable with a note explaining the unavailable details. Worker and provider fields are omitted. An explicit --host query still fails when its status request fails.

reload validates the file, applies runtime-safe changes, and reports appliedPaths, restartRequiredPaths, and overrideControlledPaths. It never implicitly restarts. Use --json or --format yaml for structured results. An older host lacking the capability reports that it needs an update.

The root aliases start, status, restart, reload, and pair use the same commands as daemon. Root run and stop remain agent operations.

Foreground deployments and migration

Use environment overrides with the foreground deployment command:

PASEO_LISTEN=127.0.0.1:6799 PASEO_RELAY_ENABLED=false paseo daemon run --home ~/paseo-test

It stays attached until the supervisor exits or you cancel, without a readiness timeout. Worker restart retains these launch inputs. Stop and relaunch the deployment to change them. If the home already has a live supervisor, run returns already_running without owning or launching a foreground process.

Managed start ignores inherited daemon-setting overrides, including PORT, PASEO_LISTEN, relay, voice, and web UI settings. It preserves provider credentials and executable/runtime controls. start --foreground is removed; use daemon run. Former start/restart configuration flags such as --port, --no-relay, and --web-ui fail before side effects, with the corresponding config set migration. See configuration edits.

Select one daemon

Every daemon-connected CLI command accepts global --home or --host, before or after the command. A home selects a local supervisor's published endpoint; a host selects an explicit endpoint. There is no configured-address or default-port fallback.

Selectors Result
--home That local home, overriding both environment selectors
--host That endpoint, overriding both environment selectors
Both flags, or conflicting duplicate flags TARGET_AMBIGUOUS
Only PASEO_HOME or only PASEO_HOST The corresponding target
Both environment selectors, without a flag TARGET_AMBIGUOUS
Neither Default local home, ~/.paseo

Local-only start, daemon run, config, onboard, and set-password reject explicit --host and ignore PASEO_HOST. Endpoint operations retain TCP, Unix socket, Windows pipe, SSH, and pairing-offer transports. A host-side CLI controlling a container needs --host or PASEO_HOST.

Hub

paseo hub login [url]          # Approve and store organization-scoped CLI access
paseo hub init                 # Create and optionally deploy a starter trigger here
paseo hub connect [url]        # Enroll this daemon using CLI access
paseo hub projects             # List legacy projects in the authenticated organization
paseo hub status               # Show the current Hub relationship
paseo hub permissions list     # Show what this Hub may do on this daemon
paseo hub permissions grant hub.execute    # Let Hub automations run agents here
paseo hub permissions revoke hub.execute   # Take it back
paseo hub disconnect           # End it
paseo hub deploy               # Validate and install .paseo/triggers/*.yml
paseo hub deploy --dry-run     # Validate without installing
paseo hub deploy -p <project>   # Deploy an existing legacy project bundle
paseo hub logout               # Remove the active stored CLI login

Run deploy from the repository root. By default it reads every direct .paseo/triggers/*.yml file in deterministic path order. It validates all triggers before installing them one at a time. If an installation fails after earlier ones succeeded, the error lists the installed files. --dry-run only validates; it does not create or activate revisions.

Pass -p, --project <slug> for an existing legacy bundle: .paseo/hub.yml, direct .paseo/workflows/*.yml files, and referenced workflow partials. See Deploy from the CLI.

login opens the Hub approval page and stores a durable organization-scoped CLI credential under PASEO_HOME. In an interactive terminal it offers to connect this daemon, then separately asks whether to allow Hub automations to run agents. Connection defaults to yes; execution permission defaults to no. It then links to Hub's Triggers page and prints paseo hub init for setup as code. --json and non-TTY login remain login-only and never prompt. The stored login is separate from the daemon relationship created by connect.

init requires a TTY. It signs in and connects the daemon as needed, then lists the organization's app connections that can back a starter trigger. One usable connection is selected automatically; with several, you choose a Trigger connection. If none is ready, setup sends you to Hub → Apps and stops before selecting an agent or writing files.

Setup asks which agent provider, model, and mode to run. Providers must be enabled and expose both a selectable model and an execution mode. Suggested model and mode entries are the daemon's defaults; a mode is still selected explicitly when there is no default. Hub validates the choice against the daemon before deploying, and accepts only Claude, Codex, and OpenCode for its unattended runs. deploy applies the same checks, so it needs the named daemon connected. Setup then asks for the identity allowed to trigger the bot: a GitHub username, Slack member ID, or Discord user ID. It validates the trigger, writes .paseo/triggers/<provider>-help.yml, and asks whether to deploy. Replacing that file requires confirmation; existing legacy bundles and other trigger files are preserved. See the generated starter trigger.

Interactive logout checks the same-origin daemon relationship and asks whether to disconnect before deleting the login. Declining removes only the login. JSON and noninteractive logout never prompt or disconnect implicitly; --disconnect-daemon is the explicit automation path, and --force applies to that daemon disconnection. If a requested disconnection fails, the login is preserved.

Every command resolves and normalizes its destination before Hub or daemon work. Origin precedence is an explicit command origin or --hub, then PASEO_HUB_URL, then the active stored login origin, then the hosted default https://hub.paseo.sh. The hosted default never overrides an active login. Credential precedence is --api-key <secret>, then PASEO_HUB_API_KEY, then a stored login for the exact resolved origin. A stored credential is never sent to a different origin. API keys passed through flags or the environment are not stored.

Human output reports the resolved destination before each action. JSON output keeps stdout machine-readable and includes the normalized Hub origin. Bundle diagnostics identify paths without printing configuration contents or credentials.

See Daemons in Hub, Hub configuration, and the Hub public API.

Connecting to a remote daemon

The global --host option accepts either a local target (host:port, a unix socket, or a Windows pipe) or a pairing offer URL, the same https://app.paseo.sh/#offer=... link the mobile app uses for QR pairing. With an offer URL the CLI connects through the Paseo relay with end-to-end encryption, so you can drive a daemon on another machine without exposing it to the network.

Get an offer URL from the daemon you want to control:

paseo daemon pair          # prints the QR and link when relay is enabled
paseo daemon pair --relay  # enables relay without prompting
paseo daemon pair --json   # structured output; never prompts

Relay is off for new installations. A disabled relay returns a RELAY_DISABLED error; pass --relay to provide explicit consent. For a stopped home, pairing is labelled offline; --relay saves relay enablement and the offer includes a start instruction. A live but unreachable home never falls back to an offline identity. Relay pairing is end-to-end encrypted. See Security.

Use it from anywhere:

paseo --host 'https://app.paseo.sh/#offer=eyJ2IjoyLC...' ls
paseo --host "$OFFER_URL" run "fix the failing tests"

You can also set it once via PASEO_HOST instead of passing --host on every command. An explicit flag overrides the environment variable.

Multi-agent workflows

The CLI is designed to be used by agents themselves. You can instruct an agent to spawn sub-agents for parallel work:

# Agent A spawns Agent B and waits for it
agent_id=$(paseo run --no-wait --quiet --title api-agent "implement the API")
paseo wait "$agent_id"
paseo logs "$agent_id" --tail 5

Because Agent A's ID is present in the environment, Agent B is created as its subagent in the same workspace unless --workspace is specified.

Simple implement + verify loop:

# Requires jq
while true; do
  paseo run --provider codex "make the tests pass" >/dev/null

  verdict=$(paseo run --provider claude --output-schema '{"type":"object","properties":{"criteria_met":{"type":"boolean"}},"required":["criteria_met"],"additionalProperties":false}' "ensure tests all pass")
  if echo "$verdict" | jq -e '.criteria_met == true' >/dev/null; then
    echo "criteria met"
    break
  fi
done

This pattern enables hierarchical task decomposition, a lead agent can break down work, delegate to specialists, and synthesize results.

Output formats

Most commands support multiple output formats for scripting:

paseo ls --json                # JSON output
paseo ls --format yaml         # YAML output
paseo ls -q                    # IDs only (quiet)

Global options

  • --host <target>, connect to a different daemon (host:port, unix socket, or https://app.paseo.sh/#offer=... for relay). See Connecting to a remote daemon.
  • --json, JSON output
  • -q, --quiet, minimal output
  • --no-color, disable colors