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 --raw is 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