Automate
Build automation scripts and pipelines that use coding-agent CLIs (Claude Code, Codex, Gemini CLI, GitHub Copilot CLI) in headless/non-interactive mode as the AI engine, or delegate work to cloud agents (`gh agent-task`) that open pull requests asynchronously. Use this skill whenever the user wants to write a shell script, CI job, cron task, batch processor, webhook handler, or any automation that shells out to `claude`, `codex`, `gemini`, `copilot`, or `gh agent-task` — single-turn prompts, multi-turn agentic loops, parallel fan-out across files/folders, structured JSON outputs consumed by downstream tools, or cloud-delegated tasks that produce PRs. Trigger on phrases like "script that uses Claude", "automate with Claude Code", "headless Claude", "batch process files with an LLM", "pipeline with codex exec", "gemini -p", "copilot --autopilot", "gh agent-task create", "GitHub Action that calls Claude", "cron job to review PRs", "agent loop in bash", "dispatch an agent task to open a PR", "fleet-wide agent-task across repos", or any request to integrate a coding agent CLI into an automated workflow. Also trigger when the user describes the shape of a pipeline (fan-out, map-reduce, review-then-fix, extract-then-summarize, ticket-to-PR, scheduled fleet upgrade) and AI is the engine, even if they don't name the CLI explicitly.From its SKILL.md
npx -y skills add AndurilCode/craftwork --skill automateAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 7 stars7 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
13.6 KB, as published. Nobody here has run it
Coding-Agent CLI Automation
Method — step-by-step
Run these nine steps in order for every new automation. The sections below are the toolbox; this list is the procedure.
- Discover runtime. Which CLIs, skills, MCPs, and models are available on the host? Fail fast when a dependency is missing. See
references/introspection.md. - Pick category — local execution or cloud-delegated PR (§Two categories, five CLIs).
- Pick CLI, then load its reference BEFORE writing any invocation. Default to Claude Code; use another only if the user named it. For non-Claude targets, loading
references/{codex,gemini,copilot,github-agent-task}.mdis mandatory — examples in this file are Claude-syntax. See §Do not transliterate. - Define the I/O contract — inputs (positional args, env vars, stdin) and outputs (stdout JSON, exit codes, artifact files). Everything a caller needs to re-run the script lives here (§Reusability & observability).
- Pick shape — single-turn extract, agentic loop, multi-turn, parallel fan-out, or cloud delegation (§The five canonical shapes).
- Apply non-negotiable flags — auto mode +
--allowedTools+--output-format json+ schema + success verification (§Non-negotiable flags). - Wire reusability — parameterize every path, model, timeout; no hardcoded constants (§Reusability & observability).
- Wire observability — capture session ID, cost, live progress, structured exit codes (§Reusability & observability).
- Deliver — header block documenting usage, env, permission posture, tunables.
Two categories, five CLIs
- Local execution (rows 1–4) — agent runs on your machine or CI, synchronous, stdout output. For artifacts that aren't PRs.
- Delegated cloud (row 5) — dispatch a cloud agent that opens a PR asynchronously. For ticket/webhook→PR flows.
| CLI | Headless invocation | Reference |
|---|---|---|
| Claude Code | claude -p "<prompt>" | references/claude-code.md |
| Codex | codex exec "<prompt>" | references/codex.md |
| Gemini CLI | gemini -p "<prompt>" | references/gemini.md |
| GitHub Copilot CLI | copilot -p "<prompt>" --allow-all-tools --autopilot | references/copilot.md |
gh agent-task (cloud) | gh agent-task create "<description>" --base main | references/github-agent-task.md |
Combinable — gh agent-task opens the PR, a local CLI reviews it (see cookbook).
Do not transliterate across CLIs
Examples throughout this file are Claude-syntax. Swapping claude→codex/gemini/copilot with the same flags hallucinates. Key divergences:
| Concern | Claude | Codex | Gemini | Copilot |
|---|---|---|---|---|
| Auto mode | --permission-mode acceptEdits | --sandbox <mode> / --full-auto | --approval-mode auto_edit | --autopilot + --allow-all-tools + --no-ask-user |
| Tool scope | --allowedTools "Read,Edit,..." | --sandbox read-only|workspace-write|danger-full-access | --policy <file> | --allow-tool "<name>" |
| Structured output | --json-schema '<inline JSON>' | --output-schema <file> | (policy / post-parse) | (post-parse) |
| JSON result field | .result / .structured_output | --output-last-message <file> | .response | JSONL events |
| Session resume | --resume <id> | codex exec resume --last | -r <id> / -r latest | (n/a) |
| Session ID path | .session_id | .thread_id (in thread.started) | .session_id | per-event session_id |
Non-negotiable flags (the checklist)
Local execution (claude / codex / gemini / copilot):
- Deterministic auto mode — per-CLI flag in §Do not transliterate. Never
plan/default/interactivein a script; they pause for a human. -
--allowedToolsnarrow allowlist — the real safety boundary. Auto mode = "don't pause for approval"; the allowlist defines what the agent can do. Read-only:"Read,Grep,Glob". Dev:"Read,Edit,Bash(npm test*)". -
--output-format jsonwhen the next step is a shell pipeline. Never parsetextwith regex/grep/sed. - Structured-output constraint (
--json-schemaClaude,--output-schemaCodex) when output feeds downstream code. Stops drift into prose/invalid enums sojqcan't silently break. - Verify success in the JSON (
is_error,subtype,errors[],permission_denials[]), not just exit code. All four CLIs can exit 0 on recoverable failures.
Delegated cloud execution (gh agent-task):
- Explicit
--base <branch>. A PR againstdevelopwhen you meantmainwastes a review cycle. -
createexit 0 = "dispatched," not "succeeded" and not "merged." The agent runs after the command returns. Pair with a completion watcher (gh pr list,pull_request.openedwebhook, or--follow). -
--repo <owner/name>when targeting another repo. One dispatcher addresses any accessible repo without cloning. - Long ticket bodies via
-F <file>/ stdin, never inlined as argv. Avoids quoting bugs and arg-length limits.
The five canonical shapes
Shapes 1-4 are local execution; shape 5 is cloud delegation.
1. Single-turn, extract to JSON
Classification, extraction, summarization, triage. Script gets a parseable structured answer.
result=$(claude -p "Classify the severity of this error: $(cat error.log)" \
--output-format json \
--json-schema '{"type":"object","properties":{"severity":{"type":"string","enum":["low","medium","high","critical"]},"reason":{"type":"string"}},"required":["severity","reason"]}' \
--permission-mode acceptEdits \
--allowedTools "Read")
severity=$(echo "$result" | jq -r '.structured_output.severity')
No edit tool in the allowlist ⇒ no writes happen, regardless of permission mode. That's how you scope "read-only."
2. Agentic loop on a codebase
"Review and fix," "refactor," "add tests." Model needs file access + command execution.
cd /path/to/repo
claude -p "Run the test suite and fix any failures. Report which tests you changed." \
--allowedTools "Bash,Read,Edit" \
--permission-mode acceptEdits \
--output-format json > run.json
acceptEdits auto-approves writes. dontAsk is stricter (deny-by-default) — better for locked-down CI. Real scope control is still the allowlist.
3. Multi-turn agentic (chained calls sharing state)
Discrete checkpointed steps — review, decide, act — each seeing prior context.
session_id=$(claude -p "Audit the auth module for security issues" \
--output-format json --permission-mode acceptEdits \
--allowedTools "Read,Grep,Glob" | jq -r '.session_id')
claude -p "Now prioritize the issues you found by severity" \
--resume "$session_id" --output-format json \
--permission-mode acceptEdits --allowedTools "Read,Grep,Glob" > prioritized.json
claude -p "Fix the critical issues only" \
--resume "$session_id" --permission-mode acceptEdits --allowedTools "Read,Edit"
First two steps are read-only via narrow tools. --resume <id> is explicit and parallel-safe; --continue (most-recent-in-cwd) races.
4. Parallel fan-out
Same operation across N files/PRs/tickets. Fan out via xargs -P or background jobs; collect into a directory, then reduce.
mkdir -p out
find . -name '*.py' -print0 | xargs -0 -P 4 -I {} bash -c '
f="$1"
timeout 60s claude -p "Summarize $(cat "$f") in one sentence" \
--output-format json --permission-mode acceptEdits \
--allowedTools "Read" \
> "out/$(basename "$f").json"
' _ {}
jq -s 'map({file: .session_id, summary: .result})' out/*.json > summary.json
Bound parallelism (-P 4) to dodge rate limits. Per-worker timeout stops one runaway from stalling the batch.
5. Delegate to a cloud agent (async PR)
Output = a PR, no synchronous control needed. Local CLI is a dispatcher.
# Dispatch into current repo
gh agent-task create "Implement the spec in docs/rfc-042.md" --base main
# Dispatch into ANOTHER repo from outside it — no clone
gh agent-task create -F ticket.md --repo my-org/payments-service --base main
# Fan out across a fleet
gh repo list my-org --topic services --json nameWithOwner --jq '.[].nameWithOwner' | \
while read -r repo; do
gh agent-task create -F upgrade.md --repo "$repo" --base main
sleep 2
done
# Dispatch and follow logs in the foreground
gh agent-task create -F ticket.md --base main --follow
--repo is the orchestration lever — one dispatcher addresses any accessible repo. Pair with a local-execution CLI for review-gating: agent opens the PR, Claude/Codex/Gemini/Copilot reviews before merge. See references/github-agent-task.md for preview-status, PR-number-ambiguity, and no-JSON-from-create pitfalls.
Principles that save you later
- Tool restriction scopes capability, not plan mode. Auto mode says "don't pause"; the allowlist (Claude
--allowedTools, Codex--sandbox, Gemini--policy, Copilot--allow-tool) says what the agent can do. Read-only = narrow allowlist, not a different mode. - Repo context auto-loads (CLAUDE.md, AGENTS.md, skills, MCP) — usually why the agent is competent here. Keep it. Strip via per-CLI config + minimal
--allowedToolsonly for reproducibility or adversarial input. - Quote prompts aggressively.
$, backticks, quotes, newlines. Heredocs (<<'EOF') or--append-system-prompt-fileavoid shell-expansion bugs.
Anti-patterns
- Untrusted input straight into
-p. Prompt-injection surface. Wrap user-controlled content in delimited blocks (<user_input>...</user_input>) with instructions to treat as data. --continuein parallel jobs. Races itself — resolves to "most recent session in this cwd." Use explicit session IDs.- One script targeting every CLI. Headless models differ meaningfully. Pick one, commit to its idioms; only abstract if runtime provider choice is a real requirement.
When to load what
- CLI-specific script — the matching reference from the §Two categories table.
- Detecting MCP / skills / models at runtime (fail-fast in CI) —
references/introspection.md. - Pipeline shape not covered above (GH Action, cron PR review, batch translate, review-then-fix) —
references/cookbook.md.
Reusability & observability
Every automation has to be re-runnable by someone who didn't write it and auditable after the fact.
Reusability:
set -euo pipefailat the top. Non-negotiable.- Every path, model, timeout, parallelism bound is an arg or env var with a default:
MODEL="${MODEL:-sonnet}",TIMEOUT="${TIMEOUT:-600}",FILE="${1:?path required}". No hardcoded constants. - Require secrets explicitly:
: "${ANTHROPIC_API_KEY:?set it}". - Take an explicit
--dirorTARGET="${1:?}"andcd "$TARGET"inside the script. Don't rely on the caller's cwd. - Idempotent when plausible: skip if the output artifact exists unless
--force(or envFORCE=1). - Prompt text lives in a heredoc or
--append-system-prompt-file, not inlined — swap prompts without editing shell logic.
Observability:
- Stdout = machine (JSON), stderr = human (progress). Never
2>&1into a pipe that feedsjq. Progress messages go>&2. - Persist the session ID to a file (
echo "$session_id" > .session) — Claude/Gemini.session_id, Codex.thread_id(inthread.startedwhen--jsonis on).--continue/--last/-r latestrace under shared cwd or parallelism; always pass an explicit ID. - Track cost via
.total_cost_usdon every call; sum across a batch before you report done. - Live progress on long agentic runs:
--output-format stream-json --verbose --include-partial-messages | tee progress.log. - Exit-code taxonomy:
0success,2usage/input error,3agent reported failure (is_error:trueorpermission_denials[]non-empty),124timeoutfired,130interrupted. Downstream tools branch on codes, not on stdout text. - Error trap to capture state before unwinding:
trap 'git stash -u || true; cp progress.log "progress.$(date +%s).log" 2>/dev/null || true' ERR.
Delivering the script
Every script opens with a header a stranger can run from:
#!/usr/bin/env bash
# <name>.sh — one-line purpose
# Usage: ./<name>.sh <args> [flags]
# Env: ANTHROPIC_API_KEY (required), MODEL (default: sonnet), TIMEOUT (default: 600)
# Posture: read-only | edits | shell (enforced by --allowedTools "<list>")
# Output: stdout JSON; exits 0/2/3/124 — see README for taxonomy
set -euo pipefail