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
pets exec <tool-name> [options]Run pets redo to execute the same command with the previous args (any arg can be updated).
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 --jsonOptions
Argument Input (mutually exclusive — pick one per invocation)
| Flag | Description |
|---|---|
--<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
| Flag | Description |
|---|---|
--plugin <name|path> | Restrict execution to one plugin |
--dry-run | Resolve plugin and validate args without executing |
--save-as <slot> | Save execution state to a named slot for pets redo --from |
Output Format (mutually exclusive)
| Flag | Description |
|---|---|
--toon | TOON-formatted envelope (24% fewer tokens than JSON) |
--compact | Tool result only, TOON-formatted, no envelope |
--json | Full JSON envelope with execution metadata |
--raw | Raw tool output only, no envelope |
--table | Render array results as a pipe-delimited markdown table |
TOON-first guidance:
- Use
--compactfor normal CLI use (cleanest human-readable result) - Use
--toonwhen you also want execution metadata (tool,plugin,package) - Use
--jsononly 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:
- Current directory plugin (
./index.tsor./index.js) .pets/config.jsonenabledpets (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
--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 petArgument 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:
pets exec maps-geocode --address "Athens, Greece"Values are auto-coerced:
| Input | Result | Type |
|---|---|---|
--name "Alice" | "Alice" | string |
--radius 500 | 500 | number |
--verbose true | true | boolean |
--debug false | false | boolean |
--verbose (no value) | true | boolean |
Multiple flags compose into a single JSON object:
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:
pets exec maps-geocode --args '{"address":"Athens, Greece"}'Load from file via @path shorthand:
pets exec email-send --args @email-payload.jsonRead from stdin:
echo '{"address":"10 Downing Street"}' | pets exec maps-geocode --args @-Mode 3: JSON file
pets exec email-send --args-file email-payload.jsonRead from stdin (long form):
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:
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:
pets exec maps-geocode --address "Athens" --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:
pets exec maps-geocode --args '{"address":"Athens"}' --toonsuccess: 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:
pets exec email-test-connection --raw{
"success": true,
"status": "connected",
"provider": "zoho"
}--compact
Like --raw but converts the result to TOON format:
pets exec email-test-connection --compactsuccess: 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):
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:
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:
pets exec email-send --args-file payload.json --dry-run --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
pets exec email-test-connection
pets exec maps-test-connection --compactGeocode an address (all three arg modes)
# 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 --compactSearch for nearby places
pets exec maps-search-nearby \
--location "37.9715,23.7267" --radius 500 --keyword "restaurant" \
--tableSend an email
pets exec email-send \
--to "team@company.com" --subject "Weekly Report" --body "See attached." \
--jsonValidate before sending
pets exec email-send \
--args-file weekly-report.json \
--dry-run --jsonPipe arguments from another command
jq -n '{"address": "Parthenon, Athens"}' | pets exec maps-geocode --args @- --compactDisambiguate when tool exists in multiple plugins
pets exec maps-geocode --plugin @openpets/maps --address "Athens"Chain exec output into scripts
lat=$(pets exec maps-geocode --address "Athens" --json | jq -r '.output.latitude')
echo "Latitude: $lat"Save and replay with named slots
pets exec maps-geocode --address "Athens" --save-as baseline --json
pets redo --from baselineUse table output for quick scans
pets exec github-list-repos --tableError Responses
Validation failure
pets exec maps-geocode --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 exclusiveMixed argument modes
❌ Cannot combine inline --key value args with --args or --args-fileTroubleshooting
No local plugins found to execute tools from
- Run inside a directory with pets installed, or
- Ensure
.pets/config.jsonhas pets listed underenabled - Run
pets add <name>to install a pet first
Tool not found
- Check available tools:
pets toolsorpets 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):
pets exec <tool-name> --plugin @openpets/<pet-name>Related Commands
| Command | Purpose |
|---|---|
pets tools | List all available tools and their schemas |
pets tools --expanded | Show tools with full argument details |
pets tools --hints | Show tools with usage hints for LLMs |
pets add <name> | Install a pet from the registry |
pets health | Run connectivity checks on all enabled pets |
pets validate | Validate pet configuration |