Using OpenCode to Write Better Shell Scripts, Faster
Part 2 of Customizing OpenCode. See also: Custom Tools, Agents & Commands, and The Full Picture.
OpenCode is great for writing application code, but I've found it surprisingly effective for shell scripting. Here's how I use it to go from idea to working script in minutes.
Why This Works Well
Shell scripting has a few properties that make it ideal for AI assistance:
- Small, focused files — Scripts are usually self-contained
- Immediate feedback — Run it, see if it works, iterate
- Well-documented tools —
manpages,--helpflags, decades of Stack Overflow answers
OpenCode can read your project, understand what you're trying to automate, and generate scripts that actually fit your setup.
The Workflow
1. Describe the Goal, Not the Implementation
Don't ask for specific commands. Describe what you want to happen.
Less effective:
"Write a script that uses rsync with the -avz flags to copy files"
More effective:
"I need a script to sync my local ./dist folder to a remote server at deploy@example.com:/var/www/app, excluding node_modules and .git"
The second prompt lets OpenCode choose the right tool and flags. It might use rsync, but it'll pick appropriate options based on the actual goal.
2. Give Context About Your Environment
OpenCode can read your project, but explicit context helps.
"I'm on macOS, using zsh. The server runs Ubuntu 22.04. Write a script that..."
Or reference existing files:
"Look at my Dockerfile and create a build script that tags images with the git commit hash"
OpenCode will read the Dockerfile, understand your setup, and generate something that fits.
3. Ask for Iteration, Not Perfection
Start with a working script, then refine.
"Create a basic backup script for my postgres database"
Then:
"Add gzip compression and rotate backups older than 7 days"
Then:
"Add error handling and send a notification to Slack on failure"
Each iteration is small and testable. You catch issues early.
Practical Examples
Example 1: Git Workflow Script
Prompt:
"Create a script that automates my PR workflow: fetch latest main, create a feature branch from the argument, and push it to origin"
Output:
#!/bin/bash
set -e
BRANCH_NAME=$1
if [ -z "$BRANCH_NAME" ]; then
echo "Usage: $0 <branch-name>"
exit 1
fi
git fetch origin main
git checkout main
git pull origin main
git checkout -b "feature/$BRANCH_NAME"
git push -u origin "feature/$BRANCH_NAME"
echo "Created and pushed feature/$BRANCH_NAME"
Follow-up:
"Add a check that fails if there are uncommitted changes"
Example 2: Log Analysis
Prompt:
"Write a script that parses my nginx access logs and shows the top 10 IP addresses by request count"
Output:
#!/bin/bash
LOG_FILE=${1:-/var/log/nginx/access.log}
awk '{print $1}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -10
Follow-up:
"Also show the top 10 requested URLs and filter out requests from our health check IP 10.0.0.1"
Example 3: Development Environment Setup
Prompt:
"Look at my package.json and docker-compose.yml. Create a setup script for new developers that installs dependencies, copies .env.example to .env, and starts the database container"
OpenCode reads your files and generates:
#!/bin/bash
set -e
echo "Installing dependencies..."
npm install
if [ ! -f .env ]; then
echo "Creating .env from .env.example..."
cp .env.example .env
fi
echo "Starting database..."
docker-compose up -d postgres
echo "Waiting for database to be ready..."
until docker-compose exec -T postgres pg_isready; do
sleep 1
done
echo "Running migrations..."
npm run db:migrate
echo "Setup complete. Run 'npm run dev' to start."
Tips for Better Results
Be Specific About Error Handling
"Add error handling that exits on failure and prints which step failed"
OpenCode will add set -e, trap handlers, or explicit checks depending on what makes sense.
Ask for Idiomatic Solutions
"Use standard POSIX shell, no bash-specific features"
Or:
"This will only run on modern bash, feel free to use arrays and associative arrays"
Request Validation
"Validate all required arguments and environment variables at the start"
This gets you scripts that fail fast with helpful messages instead of cryptic errors halfway through.
Ask for Dry Run Mode
"Add a --dry-run flag that prints what would happen without doing it"
Incredibly useful for destructive operations.
Testing the Script
Once OpenCode writes the script, test it immediately:
chmod +x script.sh
./script.sh
If something fails, paste the error back:
"I got this error: [error]. Fix it."
OpenCode sees your terminal output and can debug in context.
From Script to Custom Tool
Once your script works, you can wrap it as an OpenCode custom tool (see part 1 of this series). Now the LLM can invoke your tested, working script whenever it's relevant.
// .opencode/tool/setup.ts
import { tool } from "@opencode-ai/plugin"
export default tool({
description: "Run the developer setup script for new environments",
args: {},
async execute() {
return await Bun.$`./scripts/setup.sh`.text()
},
})
Want to control which agents can use your tools? Check out part 3 on agents and commands, or see the complete workflow for a full example tying all four layers together.
Wrapping Up
The key is treating OpenCode as a collaborator, not a one-shot generator. Describe goals, iterate on feedback, test frequently. You'll end up with scripts that are better than what you'd write manually—because you can focus on what the script should do while OpenCode handles the how.
Questions or tips of your own? Find me on Twitter or drop me an email.