Skip to content

CLI Recipes

Short, copy-pasteable patterns for the things people actually do with savine.

Deploy on every push to main

yaml
# .github/workflows/deploy-agent.yml
name: Deploy Savine Agent
on:
  push:
    branches: [main]
    paths: ['agents/**']

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm install -g savine
      - run: savine deploy ./agents/researcher
        env:
          SAVINE_API_KEY: ${{ secrets.SAVINE_API_KEY }}

Add a smoke test step before the deploy goes out:

yaml
      - run: |
          AGENT_ID=$(jq -r .id ./agents/researcher/.savine.json)
          savine run --agent "$AGENT_ID" --input "ping" --follow
        env:
          SAVINE_API_KEY: ${{ secrets.SAVINE_API_KEY }}

Preview deploys per-branch

bash
#!/usr/bin/env bash
set -euo pipefail
SLUG=$(git rev-parse --abbrev-ref HEAD | tr '/A-Z' '-a-z')
# temporarily override the agent name so previews don't collide with prod
sed -i.bak "s/^name:.*/name: preview-${SLUG}/" config.yaml
savine deploy
mv config.yaml.bak config.yaml

Stream logs to a file

bash
savine logs --follow | tee -a savine-$(date +%Y%m%d).log

Wait for a task to finish in a script

bash
TASK_ID=$(savine run -a "$AGENT" -i "$PROMPT" | awk '/Task submitted:/ {print $NF}')

# poll until done
while true; do
  STATUS=$(savine tasks get "$TASK_ID" | awk '/^  status/ {print $2; exit}')
  case "$STATUS" in
    COMPLETED) echo ok; break ;;
    FAILED|CANCELLED) echo "$STATUS"; exit 1 ;;
  esac
  sleep 2
done

Or just use --follow:

bash
savine run -a "$AGENT" -i "$PROMPT" --follow

Rotate an API key

bash
# issue a new key in the dashboard or via the API, then:
savine config apiKey ac_new_key_…
savine whoami              # confirm

Swap between environments (prod / staging / self-hosted)

bash
# save a staging profile
cat > ~/.savine/staging.json <<EOF
{ "apiUrl": "https://staging.savine.in", "apiKey": "ac_staging_…" }
EOF

# alias to switch
alias savine-prod='SAVINE_CONFIG=$HOME/.savine/config.json savine'
alias savine-staging='SAVINE_CONFIG=$HOME/.savine/staging.json savine'

Or use SAVINE_API_URL + SAVINE_API_KEY inline:

bash
SAVINE_API_URL=https://staging.savine.in \
SAVINE_API_KEY=ac_staging_… \
  savine agents ls

Import a GitHub repo as a system

bash
savine systems from-github https://github.com/acme/research-agent --branch main

The platform clones the repo, resolves the system manifest, and deploys every agent in the graph.

Wipe a dev agent at the end of a test run

bash
trap 'savine agents rm "$AGENT_ID" -y' EXIT
savine deploy
AGENT_ID=$(jq -r .id .savine.json)
# …tests…

Seed an agent's memory

bash
savine memory add "$AGENT_ID" "The company's HQ is in Bengaluru." --type fact
savine memory add "$AGENT_ID" "Prefer concise, bulleted answers." --type preference
savine memory stats "$AGENT_ID"

Find the slowest tool calls

bash
savine metrics tools | sort -k3 -rn | head

Bulk-cancel running tasks

bash
savine tasks ls --status RUNNING -n 100 \
  | awk 'NR>2 {print $5}' \
  | xargs -I{} savine tasks cancel {}

Debugging a failed task

  1. savine tasks get <id> — read the full trace.
  2. Look at the last TOOL_CALL / TOOL_RESULT / ERROR step.
  3. Re-run just that step's input locally with the Python SDK's savine-sdk run agent.py --input ….
  4. Fix, savine deploy, savine run --follow.

Pipe structured output into jq

Pass --json to any command that returns structured data and the CLI emits raw JSON instead of a formatted table.

bash
savine --json agents ls        | jq -r '.[] | "\(.name)\t\(.id)"'
savine --json tasks ls -n 50   | jq '[.[] | select(.status=="FAILED")] | length'
savine --json tasks get $TASK  | jq -r .result.answer
savine --json whoami           | jq -r .user.apiKey

Supported on whoami, agents ls/info, systems ls/info, workflows ls/info, tasks ls/get. More surface on the roadmap.