Posted on
Artificial Intelligence

Build Artificial Intelligence CLI Tools with Bash

Author
  • User
    linuxbash
    Posts by this author
    Posts by this author

Build Artificial Intelligence CLI Tools with Bash

If you can pipe it, you can AI it.

Most of us already reach for the terminal to grep logs, json-parse APIs, or wire commands together. Yet when we want “a bit of AI,” we leave the shell, paste into a web UI, and lose time and context. What if your usual Bash pipelines could ask, summarize, classify, translate, or transcribe—inline?

This guide shows you how to build practical, shell-native AI tools with Bash and curl. You’ll get:

  • A minimal, repeatable setup for AI from the command line

  • 3–5 concrete scripts you can drop into your $PATH today

  • Tips for streaming output, chunking long inputs, and safe configuration

Bring AI to where your work already happens: the terminal.


Why Bash is a great place for AI

  • Ubiquity and zero-friction: Bash, curl, jq, git—already on your machines and CI.

  • Composability: Pipe any output into AI and right back out to other tools.

  • Auditability: Your prompts, outputs, and scripts are plain text you can version control.

  • Portability: Same scripts across Debian/Ubuntu, Fedora/RHEL, openSUSE, containers, and CI runners.

  • Speed to prototype: 10–30 lines of Bash can out-iterate weeks of UI work.


1) Install prerequisites

We’ll use curl to call the API and jq to parse JSON. ffmpeg is helpful for audio transcription, and git for the commit message example.

  • Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y curl jq ffmpeg git coreutils
  • Fedora/RHEL/CentOS (dnf):
sudo dnf install -y curl jq ffmpeg git coreutils
  • openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y curl jq ffmpeg git coreutils

Optional but handy:

  • fzf for fuzzy-finding

  • ripgrep (rg) for fast searching


2) Configure your environment (one-time)

Set your API settings and keep them out of history.

  • Create a config file:
mkdir -p ~/.config/ai
umask 077
cat > ~/.config/ai/env <<'EOF'
# Required: your API key (do not quote)
export AI_API_KEY="PUT-YOUR-KEY-HERE"

# Optional: override the default model or base URL
export AI_MODEL="${AI_MODEL:-gpt-4o-mini}"
export AI_BASE_URL="${AI_BASE_URL:-https://api.openai.com/v1}"
EOF
  • Source it from your shell startup:
grep -qxF '[ -f ~/.config/ai/env ] && . ~/.config/ai/env' ~/.bashrc || \
  echo '[ -f ~/.config/ai/env ] && . ~/.config/ai/env' >> ~/.bashrc
. ~/.config/ai/env

Security note:

  • Keep umask 077 for private config files.

  • Never hardcode keys into scripts or commit them.

  • Consider separate keys for local dev vs CI.


3) Script: ai.sh — chat from the terminal (streaming)

This is your Swiss Army knife. It takes a prompt from args or STDIN, calls the API, and streams tokens to your terminal.

  • Save to ~/bin/ai.sh and make executable:
mkdir -p ~/bin
cat > ~/bin/ai.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

: "${AI_API_KEY:?AI_API_KEY is not set (see ~/.config/ai/env)}"
AI_MODEL="${AI_MODEL:-gpt-4o-mini}"
AI_BASE_URL="${AI_BASE_URL:-https://api.openai.com/v1}"

if [[ $# -gt 0 ]]; then
  PROMPT="$*"
else
  # Read from STDIN if no args provided
  PROMPT="$(cat)"
fi

payload=$(jq -n \
  --arg model "$AI_MODEL" \
  --arg content "$PROMPT" \
  '{
    model: $model,
    stream: true,
    messages: [
      {role: "system", content: "You are a concise, shell-native assistant. When possible, answer with plain text optimized for terminals."},
      {role: "user", content: $content}
    ]
  }')

curl -sS -N "$AI_BASE_URL/chat/completions" \
  -H "Authorization: Bearer '"$AI_API_KEY"'" \
  -H "Content-Type: application/json" \
  -d "$payload" | \
while IFS= read -r line; do
  [[ -z "$line" ]] && continue
  [[ "$line" != data:* ]] && continue
  payload="${line#data: }"
  [[ "$payload" == "[DONE]" ]] && break
  # Print streamed token deltas if present
  chunk=$(jq -r 'try .choices[0].delta.content // empty' <<<"$payload")
  [[ -n "$chunk" ]] && printf "%s" "$chunk"
done
printf "\n"
EOF

chmod +x ~/bin/ai.sh
  • Ensure ~/bin is on your PATH:
grep -qxF 'export PATH="$HOME/bin:$PATH"' ~/.bashrc || \
  echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
. ~/.bashrc
  • Try it:
ai.sh "Write a one-liner Bash command that counts unique IPs in access.log"
  • Pipe into it:
dmesg | tail -n 200 | ai.sh "Summarize the key warnings:"

4) Script: summarize.sh — fast TL;DR for files/stdin (with chunking)

Summarization is the #1 terminal AI use case. This script:

  • Reads from files or STDIN

  • Chunks large input to avoid token limits

  • Returns a crisp TL;DR

cat > ~/bin/summarize.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

: "${AI_API_KEY:?AI_API_KEY is not set}"
AI_MODEL="${AI_MODEL:-gpt-4o-mini}"
AI_BASE_URL="${AI_BASE_URL:-https://api.openai.com/v1}"

read_all_input() {
  if [[ $# -gt 0 ]]; then
    cat "$@"
  else
    cat
  fi
}

# Adjust chunk size as needed (bytes)
CHUNK_BYTES="${CHUNK_BYTES:-120000}"

input="$(read_all_input "$@")"
len="${#input}"

summaries=()
offset=0
while (( offset < len )); do
  chunk="${input:offset:CHUNK_BYTES}"
  offset=$(( offset + CHUNK_BYTES ))
  summary=$(curl -sS "$AI_BASE_URL/chat/completions" \
    -H "Authorization: Bearer $AI_API_KEY" \
    -H "Content-Type: application/json" \
    -d "$(jq -n \
      --arg model "$AI_MODEL" \
      --arg content "Summarize succinctly for a technical reader. Focus on key points, decisions, and actions.\n\n---\n$chunk" \
      '{
        model: $model,
        messages: [
          {role: "system", content: "You produce crisp TL;DRs suitable for terminal display."},
          {role: "user", content: $content}
        ],
        temperature: 0.2
      }')" \
    | jq -r '.choices[0].message.content')
  summaries+=("$summary")
done

if (( ${#summaries[@]} == 1 )); then
  printf "%s\n" "${summaries[0]}"
else
  final_input=$(printf 'Combine the following partial summaries into one concise TL;DR:\n\n- %s\n' "${summaries[@]}")
  curl -sS "$AI_BASE_URL/chat/completions" \
    -H "Authorization: Bearer $AI_API_KEY" \
    -H "Content-Type: application/json" \
    -d "$(jq -n \
      --arg model "$AI_MODEL" \
      --arg content "$final_input" \
      '{
        model: $model,
        messages: [
          {role: "system", content: "Output a single compact TL;DR, 5-8 bullets max."},
          {role: "user", content: $content}
        ],
        temperature: 0.2
      }')" \
  | jq -r '.choices[0].message.content'
fi
EOF

chmod +x ~/bin/summarize.sh

Examples:

man tar | col -b | summarize.sh
journalctl -u nginx --since "1 hour ago" | summarize.sh
summarize.sh README.md docs/*.md

5) Script: transcribe.sh — speech-to-text from the shell

Turn meetings, voice memos, or screen recordings into text.

cat > ~/bin/transcribe.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

: "${AI_API_KEY:?AI_API_KEY is not set}"
AI_BASE_URL="${AI_BASE_URL:-https://api.openai.com/v1}"

file="${1:-}"
if [[ -z "$file" || ! -f "$file" ]]; then
  echo "Usage: transcribe.sh path/to/audio.(wav|mp3|m4a|webm)" >&2
  exit 1
fi

# Model name may vary; whisper-1 is commonly used for transcription
curl -sS -X POST "$AI_BASE_URL/audio/transcriptions" \
  -H "Authorization: Bearer $AI_API_KEY" \
  -H "Content-Type: multipart/form-data" \
  -F "model=whisper-1" \
  -F "file=@${file}" \
| jq -r '.text // .translation // .result // .data // .message // empty'
EOF

chmod +x ~/bin/transcribe.sh

Record audio (Linux, using ffmpeg) and transcribe:

# Record 10 seconds from default input
ffmpeg -f alsa -i default -t 10 out.wav
transcribe.sh out.wav

6) Script: commit-ai — generate commit messages from staged changes

Speed up disciplined commit messages. Pipe your staged diff and ask for a clear, conventional message.

cat > ~/bin/commit-ai <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

: "${AI_API_KEY:?AI_API_KEY is not set}"
AI_MODEL="${AI_MODEL:-gpt-4o-mini}"
AI_BASE_URL="${AI_BASE_URL:-https://api.openai.com/v1}"

diff="$(git diff --cached)"
if [[ -z "$diff" ]]; then
  echo "No staged changes." >&2
  exit 1
fi

prompt=$'Write a concise Conventional Commit subject and a short body for this diff. Use present tense, <72 char subject, wrap body at 72.\n\n---\n'"$diff"

curl -sS "$AI_BASE_URL/chat/completions" \
  -H "Authorization: Bearer $AI_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n \
    --arg model "$AI_MODEL" \
    --arg content "$prompt" \
    '{
      model: $model,
      messages: [
        {role: "system", content: "You write precise Conventional Commits."},
        {role: "user", content: $content}
      ],
      temperature: 0.2
    }')" \
| jq -r '.choices[0].message.content'
EOF

chmod +x ~/bin/commit-ai

Use it:

git add -A
commit-ai | tee /tmp/CM.txt
git commit -e -F /tmp/CM.txt

Optional: wire into a repo’s prepare-commit-msg hook:

cat > .git/hooks/prepare-commit-msg <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
[[ -n "${AI_API_KEY:-}" ]] || exit 0
msg_file="$1"
# Only when the message is empty and not a merge
if [[ -s "$msg_file" || "${2:-}" == "merge" ]]; then
  exit 0
fi
tmp="$(mktemp)"
commit-ai > "$tmp" || exit 0
cat "$tmp" > "$msg_file"
rm -f "$tmp"
EOF
chmod +x .git/hooks/prepare-commit-msg

Real-world usage patterns

  • Instant TL;DR for logs: journalctl -u myservice -n 500 | summarize.sh

  • Explain errors: make 2>&1 | ai.sh "Explain the root cause and fix:"

  • Data cleanup/classification: cut -d',' -f3 customers.csv | ai.sh "Classify each line as 'lead' or 'customer'. Output CSV: original,label"

  • Docstring or SQL helper: ai.sh "Given this schema, write a SQL query to find the top 10 active users: ..."

  • Transcribe screen recordings and extract action items: transcribe.sh meeting.m4a | ai.sh "Extract and number action items:"


Optional: local models with Ollama

If you prefer local inference, you can switch AI_BASE_URL to a local server such as Ollama and set a local model name (e.g., llama3). Installation instructions can change over time; consult the upstream docs. Typical Linux install:

curl -fsSL https://ollama.com/install.sh | sh

Then:

export AI_BASE_URL="http://localhost:11434/v1"
export AI_MODEL="llama3"

Your Bash scripts above continue working since they speak the same HTTP/JSON shape.


Tips for robust Bash + AI

  • Use strict modes: set -euo pipefail and check required env vars.

  • Rate limits: add --retry-all-errors --retry 3 --retry-delay 1 for non-streaming curl calls.

  • Privacy: never pipe secrets into third-party APIs. Redact or use local models.

  • Determinism: for tools that must be stable (e.g., CI), set temperature: 0–0.2.

  • Logging: tee outputs to a file for traceability in automated jobs.


Conclusion / Call to Action

You don’t need a new GUI to get value from AI—just a few small Bash scripts.

  • Start with ai.sh for ad-hoc prompts and explainers.

  • Add summarize.sh to turn any noisy output into a TL;DR.

  • Use transcribe.sh and commit-ai to integrate AI into your daily flow.

Next steps:

  • Drop these scripts into your dotfiles repo.

  • Wrap them into make targets or CI jobs.

  • Extend with your domain prompts (security, data, SRE, docs).

If you can pipe it, you can AI it. Try one of the examples above right now and watch your terminal get smarter.