CLI Index CLI Index
← All Posts

Building Automated Workflows with AI Agents and CLI Pipelines

· 6 min read
workflowstutorialai-agents

The Unix Philosophy Meets AI Agents

The Unix philosophy — “do one thing well” — has shaped command-line tooling for over fifty years. Each tool handles a focused task and communicates through plain text streams. This design was never intended for AI, yet it turns out to be a near-perfect fit for how coding agents operate.

When an AI agent like Claude Code or Codex CLI tackles a complex task, it does not reach for a single monolithic program. It composes small, reliable tools into pipelines, piping the output of one into the input of another. The agent becomes the orchestrator, and the Unix pipe becomes its coordination primitive.

Understanding how to build these pipelines — and how agents reason about them — is essential for anyone working with autonomous coding workflows.

How Agents Chain CLI Tools

An agent presented with a high-level instruction such as “find all TODO comments in the codebase and create a GitHub issue for each one” does not execute a single command. It decomposes the goal into discrete steps, selects the right tool for each step, and wires them together.

Step 1: Decomposition

The agent breaks the task into sub-problems:

  1. Search the codebase for TODO comments.
  2. Extract the file path, line number, and comment text.
  3. Format each result into a structured payload.
  4. Create a GitHub issue for each payload.

Step 2: Tool Selection

For each sub-problem, the agent evaluates its available tools:

  • Search: ripgrep (rg) — fast, respects .gitignore, outputs structured results.
  • Extract and format: awk, sed, or jq — text processing and JSON construction.
  • Create issues: gh issue create — GitHub CLI with non-interactive mode.

Step 3: Pipeline Assembly

The agent chains these tools together. A realistic pipeline looks like this:

rg "TODO|FIXME" --vimgrep | \
  awk -F: '{print $1, $2, $4}' | \
  while read file line comment; do
    gh issue create \
      --title "TODO in $file:$line" \
      --body "Found at $file line $line: $comment" \
      --label "tech-debt"
  done

This is not a pipeline an agent copies from a template. It constructs it from first principles, adapting to the project’s structure and the tools installed on the machine.

Practical Workflow Examples

Below are real-world workflows that agents commonly build. Each demonstrates a different composition pattern.

Lint, Test, Build, Deploy

The classic CI-style pipeline, executed locally by an agent after making changes:

eslint src/ --fix && \
  npm test -- --reporter json && \
  npm run build && \
  npx wrangler deploy --env staging

The && operator is critical here: each step only runs if the previous one succeeded. An agent uses exit codes to determine whether to proceed or stop and diagnose the failure.

Audit Dependencies and Report

An agent tasked with reviewing project dependencies might compose:

npm audit --json | \
  jq '[.vulnerabilities | to_entries[] | select(.value.severity == "high" or .value.severity == "critical")] | length'

If the count is non-zero, the agent can then drill into the details:

npm audit --json | \
  jq '.vulnerabilities | to_entries[] | select(.value.severity == "critical") | {name: .key, severity: .value.severity, range: .value.range}'

This two-pass approach is how agents handle conditional logic: run a quick check first, then decide whether a deeper investigation is warranted.

Find Large Files in Git History

An agent cleaning up a repository might look for oversized blobs:

git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  awk '/^blob/ {print $3, $4}' | \
  sort -rn | \
  head -20

Each tool in this chain does exactly one thing: enumerate objects, inspect their metadata, filter to blobs, sort by size, and truncate the list. No single tool could do all of this alone.

Batch Rename Files by Convention

When enforcing a naming convention across a project:

find src/components -name "*.jsx" -not -name "*.test.jsx" | \
  while read f; do
    dir=$(dirname "$f")
    base=$(basename "$f" .jsx)
    lower=$(echo "$base" | sed 's/\([A-Z]\)/-\L\1/g' | sed 's/^-//')
    mv "$f" "$dir/$lower.jsx"
  done

Agents excel at this kind of tedious-but-mechanical refactoring because they can generate the transformation logic, test it on a sample, and then apply it across the entire tree.

Error Handling in Pipelines

Pipelines fail. Tools return non-zero exit codes, produce unexpected output, or hang waiting for input. A well-constructed agent workflow handles these cases explicitly.

Exit Code Conventions

Most Unix tools follow a simple convention:

Exit CodeMeaning
0Success
1General error or “no results found”
2Misuse of command (bad arguments)
126Permission denied
127Command not found
128+NKilled by signal N

Agents use these codes to make decisions. A grep returning exit code 1 means “no matches” — not a fatal error. An agent that understands this distinction can avoid false alarms.

Conditional Execution

The shell provides two key operators for conditional flow:

# Run deploy only if tests pass
npm test && npm run deploy

# Run fallback only if primary fails
curl -f https://primary-api/health || curl -f https://fallback-api/health

# Combined: try primary, fall back, fail loudly if both fail
curl -f https://primary/health || curl -f https://fallback/health || exit 1

Agents compose these operators to build resilient workflows. The || operator functions as a catch clause, and && functions as sequential execution with early termination.

Capturing and Inspecting Errors

For more nuanced error handling, agents redirect stderr and inspect it:

output=$(npm run build 2>&1)
exit_code=$?

if [ $exit_code -ne 0 ]; then
  echo "$output" | grep -i "error" | head -5
fi

This pattern — capture everything, check the exit code, then parse the error output — gives the agent enough information to diagnose and fix problems autonomously.

How Agents Decide Which Tools to Combine

Tool selection is not random. Agents evaluate candidates along several axes:

Output Format Compatibility

The most important criterion is whether tool A’s output can be consumed by tool B. An agent prefers tools that produce line-oriented text (one record per line) or structured JSON, because these formats compose cleanly.

For example, gh pr list --json number,title,author produces JSON that jq can filter directly. By contrast, gh pr list without --json produces a human-readable table that requires fragile text parsing.

Determinism and Idempotency

Agents prefer tools that produce the same output given the same input. Non-deterministic tools — those affected by network state, race conditions, or random seeds — make pipelines unreliable and hard to debug.

Similarly, agents favor idempotent operations where possible. Running git checkout main twice should have the same effect as running it once. This property lets agents retry failed steps safely.

Availability and Fallbacks

Before constructing a pipeline, agents check which tools are installed. If ripgrep is not available, the agent falls back to grep -r. If jq is missing, it might use python3 -c "import json, sys; ..." as an alternative. This adaptive behavior makes agents robust across different environments.

Scope of Side Effects

Agents distinguish between read-only operations (search, list, inspect) and write operations (create, delete, modify). They typically execute read-only pipelines freely but add confirmation gates or dry-run flags before write operations:

# Dry run first
rsync -avzn source/ dest/

# If output looks correct, execute for real
rsync -avz source/ dest/

Building Blocks: Essential Tools for Agent Pipelines

Certain tools appear in agent-constructed pipelines far more often than others. These form the core vocabulary:

ToolRole in Pipelines
rg (ripgrep)Fast codebase search with structured output
jqJSON filtering, transformation, and extraction
ghGitHub operations (issues, PRs, releases)
gitVersion control operations and history queries
curlHTTP requests to APIs and services
awk / sedText extraction and transformation
xargsParallel execution of commands over input lines
sort / uniqDeduplication and ordering
wcCounting lines, words, bytes
teeSplitting output to both stdout and a file

An agent fluent in these ten tools can construct workflows covering the vast majority of development automation tasks.

From Simple Pipes to Complex Orchestration

Simple pipes connect two or three tools in a linear chain. Complex workflows introduce branching, parallelism, and state. An agent building a sophisticated workflow might produce a shell script rather than a one-liner:

#!/bin/bash
set -euo pipefail

# Step 1: Gather changed files
changed=$(git diff --name-only HEAD~1)

# Step 2: Run relevant checks in parallel
echo "$changed" | grep '\.ts$' | xargs -P4 -I{} npx tsc --noEmit {} &
echo "$changed" | grep '\.test\.' | xargs -P4 jest --findRelatedTests {} &
wait

# Step 3: If all checks pass, create a summary
echo "$changed" | wc -l | xargs -I{} echo "Verified {} files successfully"

The set -euo pipefail line is significant: it tells the shell to exit on any error, treat unset variables as errors, and propagate failures through pipes. Agents that include this preamble produce more reliable scripts.

Conclusion

CLI pipelines are not a relic of a pre-GUI era. They are the native execution model for AI coding agents. By composing small, focused tools with pipes, conditional operators, and shell scripting primitives, agents can automate workflows that would otherwise require complex custom software.

The key insight is that agents do not memorize pipelines — they construct them. Understanding the building blocks, the composition patterns, and the error-handling conventions gives you the foundation to work effectively alongside agents that think in pipes.

Related Posts