Skip to content

GauthamAnil/pipegent

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

15 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

PipeGent - Agentic Pipeline Framework

A lightweight bash framework for building LLM-powered workflows and agents.

Not just an LLM CLI tool - PipeGent orchestrates multi-step workflows (data flowing through prompts) and agentic loops (LLM deciding tool calls), with built-in conversation history, cost tracking, and complete observability.

Quick Example

source pipeline-framework.sh

echo "I love this product!" | agent "Classify sentiment as positive, negative, or neutral"
# Output: positive

Why PipeGent?

Use PipeGent for the same reasons you use bash: small, quick, easy, readable for simple stuff.

Just like you choose bash over Python for straightforward automation, choose PipeGent over LangChain for straightforward LLM workflows.

What it does:

  • Workflows - Route data through fixed prompt sequences (classify β†’ route β†’ process)
  • Agents - Let LLM decide tool calls in agentic loops (multi-turn with conversation history)
  • Infrastructure - Handles LLM calls, logging, cost tracking, state management
  • You write - The control flow in native bash (if/case/for/while)

Lighter mental model: Just bash + one agent function. No classes, chains, or framework abstractions to learn.

AI-friendly: Tools like Claude Code can read just README.md + pipeline-framework.sh to fully understand the framework - no hidden behavior, no complex abstractions, minimal chance of mistakes.

Key Features

  • Workflow & Agent capable - Fixed pipelines OR agentic loops with tool calling (Example 6)
  • MCP Integration - Connect to Model Context Protocol servers for standardized tool/resource access
  • Complete transparency - Every LLM call automatically saved to files (prompts, responses, costs)
  • Multi-turn conversations - Built-in conversation history with init_chat/agent_chat for agentic loops
  • Built-in observability - Automatic cost tracking, token usage, and execution traces
  • Three input modes - Flexible prompt handling: parameter only, parameter + stdin, or stdin only
  • Native composability - Works with pipes: cat file | agent "task1" | agent "task2"
  • CLI building blocks - Scripts become command-line tools usable by other tools (e.g., Claude Code)
  • Model switching - Change models per-call or globally with simple env vars
  • Minimal dependencies - Just bash, curl, and jq (already on most systems)

PipeGent vs LangChain

Think: Bash vs Python

Just like bash is for quick scripts and Python is for applications:

PipeGent (like bash):

  • Lightweight workflow/agent framework in bash
  • Quick to write, easy to read, minimal setup
  • Transparent by default (all prompts/responses in files)
  • CLI-composable building blocks
  • For automation, scripting, simple-to-medium workflows
  • ~100 lines you can understand completely

LangChain (like Python):

  • Comprehensive framework with rich abstractions
  • Production-grade features (RAG, vector stores, sophisticated agents)
  • Better for complex applications and web services
  • Steeper learning curve, more setup
  • For large-scale, team-based LLM applications

Use PipeGent when:

  • Building bash automation with LLM capabilities
  • Need transparency (see exactly what prompts are sent)
  • Want quick prototypes without framework overhead
  • Prefer readability over sophistication
  • Building CLI tools or simple-to-medium workflows/agents

Use LangChain when:

  • Building production web services or APIs
  • Need advanced features (RAG, vector search)
  • Large codebase with team collaboration
  • Complex multi-agent orchestration at scale

Setup

Option 1: LiteLLM (Recommended) - Multi-provider with cost tracking

# Install litellm
pip install litellm

# Configure your provider (see https://docs.litellm.ai/docs/)
export AGENT_MODEL="gpt-4"  # or claude-3-opus, gemini-pro, etc.

# Set API keys for your chosen provider
export OPENAI_API_KEY="sk-..."
# or ANTHROPIC_API_KEY, GOOGLE_API_KEY, etc.

Option 2: OpenRouter - Simple fallback

export OPENROUTER_API_KEY="sk-or-v1-your-key-here"
export AGENT_MODEL="anthropic/claude-3.5-sonnet"  # or deepseek/deepseek-v3.2, etc.

PipeGent automatically detects and prioritizes litellm if installed, falls back to OpenRouter otherwise.

Option 3: MCP Integration (Optional) - Standardized tool/resource access

Connect to Model Context Protocol servers for filesystem access, GitHub integration, web search, and more.

# Install MCP Python SDK
pip install mcp

# Create config (project-specific or user-level)
cp mcp_config.example.json mcp_config.json
# Edit to configure your MCP servers

# Config locations (checked in order):
# 1. ./mcp_config.json (project-specific)
# 2. ~/.pipegent/mcp_config.json (user-level)

Example config:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/directory"],
      "transport": "stdio"
    }
  }
}

MCP Functions:

  • mcp_list_tools - Get formatted list of available tools for LLM prompts
  • mcp_call_tool <json> - Execute MCP tool with {"mcp_server": "...", "tool": "...", "arguments": {...}}
  • mcp_list_resources <server> - List resources from an MCP server
  • mcp_read_resource <server> <uri> - Read a resource

See examples/mcp-assistant.sh for a complete agentic workflow with MCP tools.

Quick Start Examples

Example 1: Standalone Agent Call (Trivial)

No workflow needed for simple one-off LLM calls:

source pipeline-framework.sh

echo "I love this product!" | agent "Classify sentiment as positive, negative, or neutral"
# Output: positive

Example 2: Email Classifier with Routing (Simple)

Use bash case statement for routing based on LLM classification:

#!/bin/bash
source ./pipeline-framework.sh

# Get email text from command line argument, or use default
EMAIL_TEXT="${1:-Hi! You've won \$1,000,000! Click here now!!!}"

workflow "Email Classifier" --show-progress <<'END'
    # Framework provides: $INPUT variable with the email text

    # Classify the email
    CLASSIFICATION=$(echo "$INPUT" | agent "Classify this email as: spam, urgent, or normal. Answer with just one word.")

    # Route based on classification using native bash
    case "$CLASSIFICATION" in
        *spam*)
            echo "$INPUT" | agent "Generate spam filter rule for similar emails"
            ;;
        *urgent*)
            echo "$INPUT" | agent "Draft immediate response acknowledging urgency"
            ;;
        *)
            echo "$INPUT" | agent "Add to normal queue with priority score"
            ;;
    esac
END

# Run workflow with the email text (from argument or default)
echo "$EMAIL_TEXT" | run-workflow

Usage:

./examples/simple-classifier.sh  # Uses default spam email
./examples/simple-classifier.sh "URGENT: Server is down! Need immediate assistance!"
./examples/simple-classifier.sh "Hello, here's the report you requested..."

Output: Spam filter rule (when classifying the default spam email)

Example 3: Multi-Step Analysis with JSON (Medium)

Generate structured JSON for database ingestion. Note: LLM generates only fields requiring AI understanding; bash adds bookkeeping fields.

#!/bin/bash
source ./pipeline-framework.sh

workflow "Product Review Analyzer" --show-progress --trace <<'END'
    log_progress "  πŸ“Š Analyzing review..."

    # LLM generates structured data (requires understanding)
    ANALYSIS=$(echo "$INPUT" | agent "Analyze this product review. Output JSON with:
- sentiment (positive/negative/mixed)
- rating_implied (1-5)
- positive_aspects (array of strings)
- negative_aspects (array of strings)
- action_required (boolean - true if needs follow-up)

Output ONLY valid JSON, no markdown.")

    # Bash adds computable/bookkeeping fields
    REVIEW_ID=$(echo "$INPUT" | md5sum | cut -d' ' -f1)
    TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
    WORD_COUNT=$(echo "$INPUT" | wc -w)

    # Combine into final JSON using jq
    echo "$ANALYSIS" | jq --arg id "$REVIEW_ID" \
                          --arg ts "$TIMESTAMP" \
                          --argjson wc "$WORD_COUNT" \
                          '. + {review_id: $id, processed_at: $ts, word_count: $wc}'
END

echo "The product is amazing! However, the battery life could be better. Overall, I'd give it 4 stars." | run-workflow

Output:

{
  "sentiment": "mixed",
  "rating_implied": 4,
  "positive_aspects": ["product quality", "overall satisfaction"],
  "negative_aspects": ["battery life"],
  "action_required": true,
  "review_id": "a3f5e8c9d1b2...",
  "processed_at": "2024-01-08T12:34:56Z",
  "word_count": 18
}

Example 4: Parallel Processing with JSON (Medium)

Run multiple LLM analyses concurrently, then combine into structured output:

#!/bin/bash
source ./pipeline-framework.sh

workflow "Content Analysis" --show-progress <<'END'
    log_progress "  ⚑ Running parallel analysis..."

    # Run multiple analyses in parallel using bash background jobs
    # Each gets a different aspect of the content
    SUMMARY=$(echo "$INPUT" | agent "Summarize in one sentence. Just the summary, no preamble.") &
    PID1=$!

    KEYWORDS=$(echo "$INPUT" | agent "Extract 3-5 main keywords. Output as JSON array of strings only.") &
    PID2=$!

    TONE=$(echo "$INPUT" | agent "What is the tone? Answer with ONE word: professional, casual, technical, or marketing.") &
    PID3=$!

    # Wait for all LLM calls to complete
    wait $PID1 $PID2 $PID3

    # Bash computes metadata
    CHAR_COUNT=$(echo "$INPUT" | wc -c)
    WORD_COUNT=$(echo "$INPUT" | wc -w)
    READ_TIME=$(( (WORD_COUNT + 199) / 200 ))  # Assume 200 words/min, round up

    # Combine into structured JSON
    # Note: LLM generated content (summary, keywords, tone)
    #       Bash computed metrics (counts, read time)
    jq -n --arg summary "$SUMMARY" \
          --argjson keywords "$KEYWORDS" \
          --arg tone "$TONE" \
          --argjson chars "$CHAR_COUNT" \
          --argjson words "$WORD_COUNT" \
          --argjson read_min "$READ_TIME" \
          '{
            content: {
              summary: $summary,
              keywords: $keywords,
              tone: $tone
            },
            metrics: {
              character_count: $chars,
              word_count: $words,
              reading_time_minutes: $read_min
            }
          }'
END

echo "Artificial intelligence is transforming industries. Machine learning models can now process vast amounts of data efficiently..." | run-workflow

Output:

{
  "content": {
    "summary": "AI and machine learning are revolutionizing industry data processing",
    "keywords": ["artificial intelligence", "machine learning", "data processing", "industries"],
    "tone": "professional"
  },
  "metrics": {
    "character_count": 134,
    "word_count": 18,
    "reading_time_minutes": 1
  }
}

Example 5: Iterative Processing (Medium)

Process a list of items:

#!/bin/bash
source ./pipeline-framework.sh

workflow "Batch Translator" --show-progress <<'END'
    # Split input into lines and process each
    log_progress "  🌍 Translating items..."

    echo "$INPUT" | while IFS= read -r line; do
        if [ -n "$line" ]; then
            TRANSLATION=$(echo "$line" | agent "Translate to Spanish")
            echo "- $TRANSLATION"
        fi
    done
END

echo "Hello
Goodbye
Thank you" | run-workflow

Example 6: Tool Calling with Conversation History (Complex)

An agentic loop where the LLM can call filesystem tools to answer questions. This example demonstrates multi-turn conversation with JSON message format.

#!/bin/bash
source ./pipeline-framework.sh

# Get task from command line argument, or use default
TASK="${1:-How many .sh files are in the current directory?}"

workflow "Filesystem Assistant" --show-progress <<'END'
    log_progress "  πŸ€– Starting filesystem assistant..."

    # User's question
    USER_QUESTION="$INPUT"

    # Initialize conversation with system prompt (creates JSON message array)
    init_chat "You are a filesystem assistant. You can use tools to explore the filesystem and answer questions.

Available tools (call with JSON format):
- {\"tool\": \"ls\", \"args\": [\"-la\", \"path\"]} - list directory contents
- {\"tool\": \"cat\", \"args\": [\"file\"]} - read file contents
- {\"tool\": \"head\", \"args\": [\"-n\", \"20\", \"file\"]} - read first N lines
- {\"tool\": \"grep\", \"args\": [\"-r\", \"pattern\", \"path\"]} - search for pattern
- {\"tool\": \"find\", \"args\": [\"path\", \"-name\", \"pattern\"]} - find files by name
- {\"tool\": \"wc\", \"args\": [\"-l\", \"file1\", \"file2\", ...]} - count lines (accepts multiple files at once)

You have a maximum of 9 tool calls. BE EFFICIENT - pass multiple files to tools like wc instead of calling them one file at a time.

IMPORTANT: When you need to call a tool, respond with ONLY the JSON object - no explanations, no preamble, no additional text.
Example: {\"tool\": \"wc\", \"args\": [\"-l\", \"file1.sh\", \"file2.sh\", \"file3.sh\"]}

When you have the final answer, respond with plain text only (no JSON).
Never mix explanatory text with tool calls."

    # Add the user's question as a separate user message
    add_to_chat "$USER_QUESTION" "user"

    # Agentic loop - continues until LLM provides final answer (no tool call)
    MAX_ITERATIONS=10
    iteration=0

    while [ $iteration -lt $MAX_ITERATIONS ]; do
        iteration=$((iteration + 1))
        log_progress "  πŸ”„ Iteration $iteration..."

        # Get LLM response (uses conversation history automatically)
        RESPONSE=$(agent_chat)

        # Check if response contains a tool call (possibly wrapped in code blocks)
        if [[ "$RESPONSE" =~ \"tool\" ]]; then
            # Strip markdown code blocks if present (``` or ```json)
            TOOL_JSON=$(echo "$RESPONSE" | sed -e 's/^[[:space:]]*```json[[:space:]]*//g' -e 's/^[[:space:]]*```[[:space:]]*//g' -e 's/[[:space:]]*```[[:space:]]*$//g')

            log_progress "  πŸ”§ Tool call: $TOOL_JSON"

            # Parse tool call
            TOOL_NAME=$(echo "$TOOL_JSON" | jq -r '.tool' 2>/dev/null)

            # Execute tool based on name (application-specific logic)
            # Parse args into a bash array to handle spaces and special characters properly
            mapfile -t ARGS_ARRAY < <(echo "$TOOL_JSON" | jq -r '.args[]' 2>/dev/null)

            case "$TOOL_NAME" in
                ls)
                    TOOL_RESULT=$(ls "${ARGS_ARRAY[@]}" 2>&1)
                    ;;
                cat)
                    TOOL_RESULT=$(cat "${ARGS_ARRAY[@]}" 2>&1)
                    ;;
                head)
                    TOOL_RESULT=$(head "${ARGS_ARRAY[@]}" 2>&1)
                    ;;
                grep)
                    TOOL_RESULT=$(grep "${ARGS_ARRAY[@]}" 2>&1)
                    ;;
                find)
                    TOOL_RESULT=$(find "${ARGS_ARRAY[@]}" 2>&1)
                    ;;
                wc)
                    TOOL_RESULT=$(wc "${ARGS_ARRAY[@]}" 2>&1)
                    ;;
                *)
                    TOOL_RESULT="ERROR: Unknown tool '$TOOL_NAME'"
                    ;;
            esac

            log_progress "  πŸ“₯ Tool result: ${TOOL_RESULT:0:100}..."

            # Add tool result to conversation
            add_to_chat "Tool result: $TOOL_RESULT"
        else
            # LLM provided final answer (no tool call)
            log_progress "  βœ… Final answer received"
            echo "$RESPONSE"
            break
        fi

        # Safety check - prevent infinite loops
        if [ $iteration -eq $MAX_ITERATIONS ]; then
            log_progress "  ⚠️  Max iterations reached"
            echo "ERROR: Max iterations ($MAX_ITERATIONS) reached without final answer"
            exit 1
        fi
    done
END

# Run workflow with the task (from argument or default)
echo "$TASK" | run-workflow

Key Features:

  • JSON Messages Format: Conversation history stored as [{role: "system", content: "..."}, {role: "user", content: "..."}, ...]
  • init_chat: Creates conversation with system message
  • add_to_chat: Adds messages with optional role parameter (defaults to "user")
  • agent_chat: Uses full conversation history automatically, tracks each call separately
  • Robust Tool Parsing: Handles LLM responses with leading whitespace, markdown code blocks (``` or ```json)
  • Tool Calling Pattern: LLM returns JSON to invoke tools
  • Application-specific Logic: You define what tools do via bash case statement
  • Proper Argument Handling: Uses bash arrays to support spaces and special characters
  • Agentic Loop: Continues until LLM provides final answer (no tool call)

Core Components

workflow

Initializes a workflow with logging, state management, and observability:

workflow "Workflow Name" [--show-progress] [--trace] <<'END'
    # Your bash script here
    # Available: $INPUT, agent(), agent_chat(), log_progress(), $WORKFLOW_DIR
END

agent

Makes an LLM API call with automatic logging. Supports three modes:

Mode 1: Parameter only - Parameter is the complete prompt (no stdin):

# Simple question or task
RESULT=$(agent "Explain quantum computing in one sentence")

Mode 2: Parameter + stdin - Parameter is the task, stdin is the input:

# Agent chaining - each agent processes the previous output
cat email.txt | agent "Extract: sender, subject, main request" | agent "Draft a professional reply addressing the main request" | agent "Review for tone and grammar, output final version"

In mode 2, the input is wrapped in <input></input> tags automatically:

Your task here

<input>
stdin content here
</input>

Mode 3: Stdin only - Stdin is the complete prompt (for complex/dynamic prompts):

# Build a complex prompt with file contents, context, and instructions
PROMPT=$(cat <<EOF
Review the following bash script for security issues and best practices:

$(cat script.sh)

Specifically check for:
- Command injection vulnerabilities
- Unquoted variables
- Missing error handling
- Unsafe temp file usage

Provide a detailed analysis with line numbers and suggested fixes.
EOF
)

ANALYSIS=$(echo "$PROMPT" | agent)
echo "$ANALYSIS"

Each call creates debug files:

  • agent-N-prompt.txt - The parameter (if provided)
  • agent-N-input.txt - The stdin content (if provided)
  • agent-N-full-prompt.txt - Complete prompt sent to API
  • agent-N-output.txt - LLM response

Model Switching

Switch models within a script using either approach:

Inline (for single calls):

# Use different models for different tasks
SUMMARY=$(echo "$INPUT" | AGENT_MODEL="anthropic/claude-sonnet-4.5" agent "Summarize")
KEYWORDS=$(echo "$INPUT" | AGENT_MODEL="deepseek/deepseek-chat" agent "Extract keywords")
TRANSLATE=$(echo "$INPUT" | AGENT_MODEL="openai/gpt-5.2" agent "Translate to Spanish")

Export (for multiple calls):

# Switch model for all subsequent calls
export AGENT_MODEL="anthropic/claude-sonnet-4.5"
RESULT1=$(echo "$input1" | agent "prompt1")
RESULT2=$(echo "$input2" | agent "prompt2")

# Switch again
export AGENT_MODEL="deepseek/deepseek-chat"
RESULT3=$(echo "$input3" | agent "prompt3")

Conversation History Functions

For multi-turn agentic workflows, use these functions to maintain conversation state:

init_chat

Initialize a new conversation with a system prompt (creates JSON message array):

init_chat "You are a helpful assistant. You can use tools..."

This creates conversation-history.json with:

[
  {
    "role": "system",
    "content": "You are a helpful assistant..."
  }
]

add_to_chat

Add a message to the conversation history:

add_to_chat "User's question here" "user"      # Add user message
add_to_chat "Tool result: ..." "user"          # Add tool result
add_to_chat "Final answer" "assistant"         # Manually add assistant message (rarely needed)

The second parameter (role) is optional and defaults to "user".

agent_chat

Make an LLM call using the full conversation history:

RESPONSE=$(agent_chat)                         # Use existing conversation
RESPONSE=$(agent_chat "Additional prompt")     # Add user message and get response

Each agent_chat call:

  • Sends the entire conversation history as JSON messages to the LLM
  • Automatically appends the assistant's response to the conversation
  • Creates debug files for observability
  • Tracks costs and token usage

Debug files created per call:

  • agent-chat-N-messages.json - Full messages array sent to API
  • agent-chat-N-output.txt - LLM response

Conversation state:

  • conversation-history.json - Full conversation history (updated after each call)
  • agent-chat-counter.txt - Call counter (persists across subprocesses)

log_progress

Display progress messages (respects --show-progress flag):

log_progress "  πŸ” Processing step..."

Flags

  • --show-progress - Display progress (default: true)
  • --trace - Enable execution trace logs
  • --no-progress - Hide progress output

Environment Variables

  • OPENROUTER_API_KEY - Your OpenRouter API key (required)
  • AGENT_MODEL - Model to use (default: anthropic/claude-3.5-sonnet)
  • PIPELINE_LOG_DIR - Directory for workflow logs and traces (default: /tmp)
  • SHOW_PROGRESS - Enable/disable progress (default: true)
  • TRACE_ENABLED - Enable/disable tracing (default: false)

Cost Tracking

PipeGent automatically tracks API costs and token usage for all workflows using OpenRouter's usage accounting.

Each workflow run generates:

  • costs.csv - Detailed per-call costs and token usage (cost, prompt_tokens, completion_tokens, total_tokens)
  • cost-summary.txt - Human-readable summary with totals
  • Cost report - Printed at workflow completion (both readable and parseable formats)

Example output:

πŸ’° Cost Summary:
   API Calls: 2
   Total Cost: $0.000468
   Tokens: 1139 (prompt: 71, completion: 1068)

COST: workflow=Email Classifier api_calls=2 total_cost=0.000468 prompt_tokens=71 completion_tokens=1068 total_tokens=1139

The parseable COST: line can be easily extracted with grep/awk for aggregation:

./my-workflow.sh 2>&1 | grep '^COST:' | awk -F' ' '{print $3, $5}'

Debugging & Observability

All workflow runs create directories with full execution traces (in $PIPELINE_LOG_DIR, defaults to /tmp):

# Check workflow files (replace /tmp with your $PIPELINE_LOG_DIR if customized)
ls -la /tmp/pipeline-wf-*/        # Workflow runs
ls -la /tmp/pipeline-adhoc-*/     # Standalone agent calls

# View last trace
source pipeline-framework.sh
pipeline-trace show-last

# List all traces
pipeline-trace list

# Inspect a specific run
cat /tmp/pipeline-wf-12345-67890/trace.log
cat /tmp/pipeline-wf-12345-67890/workflow-script.sh        # Your script
cat /tmp/pipeline-wf-12345-67890/costs.csv                 # Cost tracking
cat /tmp/pipeline-wf-12345-67890/cost-summary.txt          # Cost summary

# For agent() calls
cat /tmp/pipeline-wf-12345-67890/agent-1-prompt.txt        # Your prompt
cat /tmp/pipeline-wf-12345-67890/agent-1-input.txt         # Input text
cat /tmp/pipeline-wf-12345-67890/agent-1-full-prompt.txt   # Full prompt sent to API
cat /tmp/pipeline-wf-12345-67890/agent-1-output.txt        # LLM response

# For agent_chat() calls (multi-turn conversation)
cat /tmp/pipeline-wf-12345-67890/conversation-history.json # Full conversation state
cat /tmp/pipeline-wf-12345-67890/agent-chat-1-messages.json  # Messages sent in call 1
cat /tmp/pipeline-wf-12345-67890/agent-chat-1-output.txt    # Response from call 1
cat /tmp/pipeline-wf-12345-67890/agent-chat-2-messages.json  # Messages sent in call 2
cat /tmp/pipeline-wf-12345-67890/agent-chat-2-output.txt    # Response from call 2
# ... (one set of files per agent_chat call)

Key observability features:

  • File-based counters: Each agent() and agent_chat() call gets a unique number, even across subprocesses
  • Full conversation history: conversation-history.json shows the complete message thread with roles
  • Per-call snapshots: agent-chat-N-messages.json shows exactly what was sent in each API call
  • Cost tracking: Every LLM call logs its cost and token usage to costs.csv

Use Cases

  • Email routing - Classify and route emails based on content
  • Content analysis - Sentiment, topics, summarization
  • Data extraction - Pull structured data from unstructured text
  • Batch processing - Process lists of items with LLM
  • Multi-step reasoning - Chain multiple LLM calls
  • Parallel analysis - Run multiple analyses concurrently

Best Practices

  1. Use native bash - Don't fight bash, use it! if, case, for, while all work great
  2. Store LLM results in variables - RESULT=$(echo "$input" | agent "prompt")
  3. Use descriptive prompts - Clear prompts get better results
  4. Log progress - Help users understand what's happening
  5. Check the trace logs - When debugging, look at agent-N-full-prompt.txt and other saved files in $WORKFLOW_DIR

Requirements

  • Bash 4.0+
  • curl - For API calls
  • jq - For JSON parsing
  • OpenRouter API key - Get one at openrouter.ai

Optional (for MCP support):

  • Python 3.9+
  • pip install mcp - MCP Python SDK
  • Node.js/npx - For MCP servers (auto-downloaded with npx -y)

Example Scripts

Try the included examples:

Simple:

  • examples/simple-classifier.sh - Email classification with bash case routing (Example 2)

Medium Complexity:

  • examples/review-analyzer.sh - Product review analysis with JSON output (Example 3)
  • examples/content-analyzer.sh - Parallel content analysis with structured JSON (Example 4)
  • examples/batch-translator.sh - Iterative batch processing (Example 5)

Complex:

  • examples/filesystem-assistant.sh - Multi-turn conversation with tool calling (Example 6)
  • examples/mcp-assistant.sh - MCP-enhanced assistant with standardized tool protocol

Run them:

# Email classifier - accepts optional email text argument
./examples/simple-classifier.sh
./examples/simple-classifier.sh "URGENT: Server is down! Need immediate assistance!"
./examples/simple-classifier.sh "Hello, here's the report you requested..."

./examples/review-analyzer.sh
./examples/content-analyzer.sh
./examples/batch-translator.sh

# Filesystem assistant - accepts optional question argument
./examples/filesystem-assistant.sh
./examples/filesystem-assistant.sh "What examples are shown in the README.md file?"
./examples/filesystem-assistant.sh "Find all files containing the word 'workflow' in this directory"

# MCP assistant - requires MCP setup (see Option 3 in Setup)
./examples/mcp-assistant.sh
./examples/mcp-assistant.sh "List the files in the examples directory"

Advanced Patterns

CLI Arguments in Workflows

TARGET="${1:-default}"
workflow "Name" <<END
    log_progress "  🎯 Target: $TARGET"
    RESULT=\$(echo "\$INPUT" | agent "Analyze for: $TARGET")
END

Multi-Round Variable Chaining

R1=\$(echo "\$CONTEXT" | agent "Initial analysis")
R2=\$(cat <<EOF | agent "Review and improve"
\$CONTEXT
<previous>\$R1</previous>
EOF
)
FINAL=\$(cat <<EOF | agent "Final synthesis"
<round_1>\$R1</round_1>
<round_2>\$R2</round_2>
EOF
)

Dual Output (display + save)

echo "\$RESULT"
OUTPUT_FILE="output_\$(date +%Y%m%d_%H%M%S).md"
echo "\$RESULT" > "\$OUTPUT_FILE"
log_progress "  βœ… Saved: \$OUTPUT_FILE"

Staged Progress Logging

log_progress "  πŸ“Š Loading..."
log_progress "  πŸ€– Round 1: Analyzing..."
log_progress "  πŸ€– Round 2: Refining..."
log_progress "  πŸ“ Synthesizing..."
log_progress "  βœ… Complete"

License

MIT

About

Build observable multi-agent LLM pipelines using unix pipes and bash

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages