JSON Output
Use --json (or -j) to get structured output from shuck. This is the primary interface for agents — it provides exit codes, timing, and clean separation of stdout/stderr for programmatic decision-making.
Usage
shuck --json <command> [args...]
shuck -j <command> [args...]
Output Schema
interface ShuckResult {
stdout: string; // Captured stdout (ANSI-stripped, UTF-8)
stderr: string; // Captured stderr from child process
exit_code: number; // Child exit code (0–123) or shuck code (124–127)
duration_ms: number; // Wall-clock execution time in milliseconds
timed_out: boolean; // true if --timeout was reached
command: string; // The command that was run
args: string[]; // Arguments passed to the command
}
Example Output
shuck --json python3 -c "print('Hello, world!')"
{
"stdout": "Hello, world!\n",
"stderr": "",
"exit_code": 0,
"duration_ms": 142,
"timed_out": false,
"command": "python3",
"args": ["-c", "print('Hello, world!')"]
}
Timeout Example
shuck --json --timeout 1 sleep 10
{
"stdout": "",
"stderr": "",
"exit_code": 124,
"duration_ms": 1002,
"timed_out": true,
"command": "sleep",
"args": ["10"]
}
Error Example
shuck --json nonexistent-command
{
"stdout": "",
"stderr": "shuck: command not found: nonexistent-command\n",
"exit_code": 127,
"duration_ms": 2,
"timed_out": false,
"command": "nonexistent-command",
"args": []
}
Where JSON Goes
The JSON is written to stdout. shuck’s own diagnostics go to stderr (use -v to see them). This means:
# Process JSON with jq
shuck --json my-tool | jq .exit_code
# Save JSON to file
shuck --json my-tool > result.json
# Use in a shell variable
result=$(shuck --json my-tool)
exit_code=$(echo "$result" | jq -r .exit_code)
stdout=$(echo "$result" | jq -r .stdout)
Notes on stdout Field
- Always ANSI-stripped (unless
--rawis also passed) - Always valid UTF-8 (invalid bytes are replaced with
\uFFFD) - May be empty string if the command produced no output
- Newlines are normalized to
\n(no\r\n)
Agent Decision Patterns
The JSON output format is designed for agents to chain into decision-making:
Check and branch
import subprocess, json
result = subprocess.run(
["shuck", "--json", "psql", "-U", "postgres", "mydb",
"--", "SELECT COUNT(*) FROM users;"],
capture_output=True, text=True
)
data = json.loads(result.stdout)
if data["exit_code"] == 0:
count = data["stdout"].strip()
print(f"User count: {count}")
elif data["timed_out"]:
print("Database query timed out")
else:
print(f"Query failed: {data['stderr']}")
Multi-tool diagnostics
#!/bin/bash
# Agent runs multiple checks and aggregates results
checks=("pip check" "npm doctor" "rustup show")
failed=0
for check in "${checks[@]}"; do
result=$(shuck --json $check)
code=$(echo "$result" | jq .exit_code)
if [ "$code" -ne 0 ]; then
echo "FAIL: $check (exit $code)"
echo "$result" | jq -r .stderr
failed=$((failed + 1))
else
echo "PASS: $check"
fi
done
exit $failed
Timeout-aware execution
# Agent retries with longer timeout on failure
result=$(shuck --json --timeout 10 slow-tool)
timed_out=$(echo "$result" | jq .timed_out)
if [ "$timed_out" = "true" ]; then
echo "Retrying with longer timeout..."
result=$(shuck --json --timeout 60 slow-tool)
fi
Combining with —timeout
# 30 second timeout, JSON output
shuck --json --timeout 30 interactive-cli -- "complex query"
Stderr Capture
By default, stderr from the child is captured into the stderr field. Use --quiet to suppress it entirely:
shuck --json --quiet noisy-tool
# stderr field will be empty string