Skip to content

Direct Tool Execution with pets exec

pets exec runs a loaded pet tool directly from the CLI, without going through natural-language routing.

This command is TOON-first for pet workflows: use TOON output for CLI readability and lower token usage, and only switch to JSON when a downstream script strictly requires JSON.

Use it when you already know the tool name and want deterministic execution with explicit arguments.

What It Solves

  • Execute a tool by exact name (for example: maps-geocode)
  • Validate arguments against the tool schema before execution
  • Run in the current repository context (same env/config behavior as loaded pets)
  • Get structured output in multiple formats for automation and scripting
  • Stream long-running tool output in real time

Command

bash
pets exec <tool-name> [options]

Run pets redo to execute the same command with the previous args (any arg can be updated).

bash
pets exec github-list-issues --owner openai --repo openai-python --state open --sort updated --direction desc --perPage 3 --dry-run --json
pets redo --args '{"state":"closed","perPage":2}' --dry-run --json

Options

Argument Input (mutually exclusive — pick one per invocation)

FlagDescription
--<key> <value>Inline flag-style arguments (simplest)
--args '{"k":"v"}'Inline JSON object of tool arguments
--args @<path>Load arguments from a file path (shorthand)
--args @-Read arguments from stdin
--args-file <file>Read JSON arguments from a file (- for stdin)

Execution Control

FlagDescription
--plugin <name|path>Restrict execution to one plugin
--dry-runResolve plugin and validate args without executing
--save-as <slot>Save execution state to a named slot for pets redo --from

Output Format (mutually exclusive)

FlagDescription
--toonTOON-formatted envelope (24% fewer tokens than JSON)
--compactTool result only, TOON-formatted, no envelope
--jsonFull JSON envelope with execution metadata
--rawRaw tool output only, no envelope
--tableRender array results as a pipe-delimited markdown table

TOON-first guidance:

  • Use --compact for normal CLI use (cleanest human-readable result)
  • Use --toon when you also want execution metadata (tool, plugin, package)
  • Use --json only when piping to strict JSON consumers

All output flags suppress the startup "Installed plugins" banner so stdout stays clean for piping.

If more than one output flag is given, the CLI exits with an error.

How Plugin Resolution Works

When --plugin is not provided, pets exec discovers plugins from:

  1. Current directory plugin (./index.ts or ./index.js)
  2. .pets/config.json enabled pets (resolved as @openpets/<name>)

If the same tool name exists in multiple plugins, pets exec fails with a disambiguation error listing the conflicting sources, and asks you to re-run with --plugin.

Plugin selector formats

bash
--plugin @openpets/maps         # published package
--plugin maps                   # shorthand (resolves to @openpets/maps)
--plugin ./index.ts             # local file path
--plugin ./pets/my-pet/index.ts # relative path to pet

Argument Input Modes

Three ways to pass tool arguments. They are mutually exclusive — mixing them produces an error.

Mode 1: Inline flags (simplest)

Any --flag that isn't a known CLI flag is treated as a tool argument:

bash
pets exec maps-geocode --address "Athens, Greece"

Values are auto-coerced:

InputResultType
--name "Alice""Alice"string
--radius 500500number
--verbose truetrueboolean
--debug falsefalseboolean
--verbose (no value)trueboolean

Multiple flags compose into a single JSON object:

bash
pets exec search --query "coffee" --radius 500 --includePhotos true
# Equivalent to: --args '{"query":"coffee","radius":500,"includePhotos":true}'

Mode 2: Inline JSON

Pass a JSON object string:

bash
pets exec maps-geocode --args '{"address":"Athens, Greece"}'

Load from file via @path shorthand:

bash
pets exec email-send --args @email-payload.json

Read from stdin:

bash
echo '{"address":"10 Downing Street"}' | pets exec maps-geocode --args @-

Mode 3: JSON file

bash
pets exec email-send --args-file email-payload.json

Read from stdin (long form):

bash
echo '{"address":"10 Downing Street"}' | pets exec maps-geocode --args-file -

Output Formats

Default (no flags)

Human-readable output with status, plugin info, and the tool result. If the pet returns TOON (recommended for pets), the CLI prints that TOON directly:

bash
pets exec maps-geocode --address "Athens, Greece"
✅ Executed maps-geocode
Plugin: @openpets/maps
Path: /Users/you/.cache/openpets/@openpets/maps

success: true
formatted_address: Athens, Greece
latitude: 37.9838096
longitude: 23.7275388

--json

Full envelope with execution metadata, suitable for scripting:

bash
pets exec maps-geocode --address "Athens" --json
json
{
  "success": true,
  "tool": "maps-geocode",
  "plugin": "@openpets/maps",
  "package": "@openpets/maps",
  "output": {
    "success": true,
    "formatted_address": "Athens, Greece",
    "latitude": 37.9838096,
    "longitude": 23.7275388
  }
}

--toon

Same envelope structure as --json but in TOON format, which uses ~24% fewer tokens:

bash
pets exec maps-geocode --args '{"address":"Athens"}' --toon
success: true
tool: maps-geocode
plugin: @openpets/maps
package: @openpets/maps
output:
  success: true
  formatted_address: Athens, Greece
  latitude: 37.9838096
  longitude: 23.7275388

--raw

Prints only the tool's return value with no wrapping:

bash
pets exec email-test-connection --raw
json
{
  "success": true,
  "status": "connected",
  "provider": "zoho"
}

--compact

Like --raw but converts the result to TOON format:

bash
pets exec email-test-connection --compact
success: true
status: connected
provider: zoho

--table

Renders array results as a pipe-delimited markdown table. Automatically detects the primary array in the response (checks results, items, data, rows, list keys, then falls back to breadth-first search):

bash
pets exec maps-search-nearby \
  --location "37.9838,23.7275" --radius 1000 --keyword "museum" \
  --table
| name                          | vicinity              | rating |
|-------------------------------|-----------------------|--------|
| National Archaeological Museum| 28is Oktovriou 44     | 4.7    |
| Museum of Cycladic Art        | Neofitou Douka 4      | 4.6    |
| Benaki Museum                 | Koumpari 1            | 4.5    |

Nested objects are flattened with dot-notation keys (e.g., location.lat). If no array is detected, falls back to TOON format with a warning.

Dry Run

Validate tool resolution and arguments without executing:

bash
pets exec email-send \
  --to "test@example.com" --subject "Hello" --body "Test" \
  --dry-run
✅ Dry run passed for email-send
   Plugin: @openpets/email
   Path: /Users/you/.cache/openpets/@openpets/email
   Args: {"to":"test@example.com","subject":"Hello","body":"Test"}

Combine with --json for scripted validation:

bash
pets exec email-send --args-file payload.json --dry-run --json
json
{
  "success": true,
  "dryRun": true,
  "tool": "email-send",
  "plugin": "@openpets/email",
  "package": "@openpets/email",
  "pluginDir": "/Users/you/.cache/openpets/@openpets/email",
  "args": {
    "to": "test@example.com",
    "subject": "Hello",
    "body": "Test"
  }
}

Streaming

Tools that support streaming emit chunks in real time when using default output mode. The onStreamChunk callback writes directly to stdout as chunks arrive, giving live feedback for long-running operations.

Streaming is only active in default mode (no output flags). Structured output modes (--toon, --compact, --json, --table) wait for the complete result.

Examples

Test a pet connection

bash
pets exec email-test-connection
pets exec maps-test-connection --compact

Geocode an address (all three arg modes)

bash
# Inline flags
pets exec maps-geocode --address "Acropolis, Athens" --compact

# Inline JSON
pets exec maps-geocode --args '{"address":"Acropolis, Athens"}' --compact

# File
echo '{"address":"Acropolis, Athens"}' > geocode.json
pets exec maps-geocode --args-file geocode.json --compact

Search for nearby places

bash
pets exec maps-search-nearby \
  --location "37.9715,23.7267" --radius 500 --keyword "restaurant" \
  --table

Send an email

bash
pets exec email-send \
  --to "team@company.com" --subject "Weekly Report" --body "See attached." \
  --json

Validate before sending

bash
pets exec email-send \
  --args-file weekly-report.json \
  --dry-run --json

Pipe arguments from another command

bash
jq -n '{"address": "Parthenon, Athens"}' | pets exec maps-geocode --args @- --compact

Disambiguate when tool exists in multiple plugins

bash
pets exec maps-geocode --plugin @openpets/maps --address "Athens"

Chain exec output into scripts

bash
lat=$(pets exec maps-geocode --address "Athens" --json | jq -r '.output.latitude')
echo "Latitude: $lat"

Save and replay with named slots

bash
pets exec maps-geocode --address "Athens" --save-as baseline --json
pets redo --from baseline

Use table output for quick scans

bash
pets exec github-list-repos --table

Error Responses

Validation failure

bash
pets exec maps-geocode --json
json
{
  "success": false,
  "error": "Tool argument validation failed",
  "issues": [
    {
      "path": "address",
      "message": "address is required"
    }
  ]
}

Tool not found

❌ Tool "nonexistent-tool" not found
   Available tools (showing first 50):
     email-test-connection, email-send, maps-geocode, ...
   Did you mean: maps-geocode?

Disambiguation required

❌ Tool "test-connection" is available in multiple plugins:
   - @openpets/email (email-test-connection)
   - @openpets/maps (maps-test-connection)
   Re-run with: pets exec test-connection --plugin <plugin-name>

Mutually exclusive output flags

❌ --json, --toon, --raw, --compact, and --table are mutually exclusive

Mixed argument modes

❌ Cannot combine inline --key value args with --args or --args-file

Troubleshooting

No local plugins found to execute tools from

  • Run inside a directory with pets installed, or
  • Ensure .pets/config.json has pets listed under enabled
  • Run pets add <name> to install a pet first

Tool not found

  • Check available tools: pets tools or pets tools --expanded
  • Verify plugin scope with --plugin
  • Tool names use the format {pet-name}-{action} (e.g., maps-geocode, email-send)

Tool is available in multiple plugins

This happens when the same tool name is registered by multiple loaded plugins (e.g., a local ./index.ts and a cached @openpets/ package):

bash
pets exec <tool-name> --plugin @openpets/<pet-name>
CommandPurpose
pets toolsList all available tools and their schemas
pets tools --expandedShow tools with full argument details
pets tools --hintsShow tools with usage hints for LLMs
pets add <name>Install a pet from the registry
pets healthRun connectivity checks on all enabled pets
pets validateValidate pet configuration