Expert jq for querying, filtering, transforming JSON, and building shell pipelines. Patterns for API/CLI outputs like GitHub issues, AWS, kubectl.
From antigravity-awesome-skillsnpx claudepluginhub sickn33/antigravity-awesome-skills --plugin antigravity-awesome-skillsThis skill uses the workspace's default tool permissions.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
jq is the standard CLI tool for querying and reshaping JSON. This skill covers practical, expert-level usage: filtering deeply nested data, transforming structures, aggregating values, and composing jq into shell pipelines. Every example is copy-paste ready for real workflows.
jq inside a bash script or one-linerjq expression doesjq takes a filter expression and applies it to JSON input. Filters compose with pipes (|), and jq handles arrays, objects, strings, numbers, booleans, and null natively.
# Extract a field
echo '{"name":"alice","age":30}' | jq '.name'
# "alice"
# Nested access
echo '{"user":{"email":"a@b.com"}}' | jq '.user.email'
# Array index
echo '[10, 20, 30]' | jq '.[1]'
# 20
# Array slice
echo '[1,2,3,4,5]' | jq '.[2:4]'
# [3, 4]
# All array elements
echo '[{"id":1},{"id":2}]' | jq '.[]'
select# Keep only matching elements
echo '[{"role":"admin"},{"role":"user"},{"role":"admin"}]' \
| jq '[.[] | select(.role == "admin")]'
# Numeric comparison
curl -s https://api.github.com/repos/owner/repo/issues \
| jq '[.[] | select(.comments > 5)]'
# Test a field exists and is non-null
jq '[.[] | select(.email != null)]'
# Combine conditions
jq '[.[] | select(.active == true and .score >= 80)]'
# Extract a field from every array element
echo '[{"name":"alice","age":30},{"name":"bob","age":25}]' \
| jq '[.[] | .name]'
# ["alice", "bob"]
# Shorthand: map()
jq 'map(.name)'
# Build a new object per element
jq '[.[] | {user: .name, years: .age}]'
# Add a computed field
jq '[.[] | . + {senior: (.age > 28)}]'
# Rename keys
jq '[.[] | {username: .name, email_address: .email}]'
# Sum all values
echo '[1, 2, 3, 4, 5]' | jq 'add'
# 15
# Sum a field across objects
jq '[.[].price] | add'
# Count elements
jq 'length'
# Max / min
jq 'max_by(.score)'
jq 'min_by(.created_at)'
# reduce: custom accumulator
echo '[1,2,3,4,5]' | jq 'reduce .[] as $x (0; . + $x)'
# 15
# Group by field
jq 'group_by(.department)'
# Count per group
jq 'group_by(.status) | map({status: .[0].status, count: length})'
# String interpolation
jq -r '.[] | "\(.name) is \(.age) years old"'
# Format as CSV (no header)
jq -r '.[] | [.name, .age, .email] | @csv'
# Format as TSV
jq -r '.[] | [.name, .score] | @tsv'
# URL-encode a value
jq -r '.query | @uri'
# Base64 encode
jq -r '.data | @base64'
# List all top-level keys
jq 'keys'
# Check if key exists
jq 'has("email")'
# Delete a key
jq 'del(.password)'
# Delete nested keys from every element
jq '[.[] | del(.internal_id, .raw_payload)]'
# Recursive descent: find all values for a key anywhere in tree
jq '.. | .id? // empty'
# Get all leaf paths
jq '[paths(scalars)]'
# if-then-else
jq 'if .score >= 90 then "A" elif .score >= 80 then "B" else "C" end'
# Alternative operator: use fallback if null or false
jq '.nickname // .name'
# try-catch: skip errors instead of halting
jq '[.[] | try .nested.value catch null]'
# Suppress null output with // empty
jq '.[] | .optional_field // empty'
# Read from file
jq '.users' data.json
# Compact output (no whitespace) for further piping
jq -c '.[]' records.json | while IFS= read -r record; do
echo "Processing: $record"
done
# Pass a shell variable into jq
STATUS="active"
jq --arg s "$STATUS" '[.[] | select(.status == $s)]'
# Pass a number
jq --argjson threshold 42 '[.[] | select(.value > $threshold)]'
# Slurp multiple JSON lines into an array
jq -s '.' records.ndjson
# Multiple files: slurp all into one array
jq -s 'add' file1.json file2.json
# Null-safe pipeline from a command
kubectl get pods -o json | jq '.items[] | {name: .metadata.name, status: .status.phase}'
# GitHub CLI: extract PR numbers
gh pr list --json number,title | jq -r '.[] | "\(.number)\t\(.title)"'
# AWS CLI: list running instance IDs
aws ec2 describe-instances \
| jq -r '.Reservations[].Instances[] | select(.State.Name=="running") | .InstanceId'
# Docker: show container names and images
docker inspect $(docker ps -q) | jq -r '.[] | "\(.Name)\t\(.Config.Image)"'
# Transpose an object of arrays to an array of objects
# Input: {"names":["a","b"],"scores":[10,20]}
jq '[.names, .scores] | transpose | map({name: .[0], score: .[1]})'
# Flatten one level
jq 'flatten(1)'
# Unique by field
jq 'unique_by(.email)'
# Sort, deduplicate and re-index
jq '[.[] | .name] | unique | sort'
# Walk: apply transformation to every node recursively
jq 'walk(if type == "string" then ascii_downcase else . end)'
# env: read environment variables inside jq
export API_KEY=secret
jq -n 'env.API_KEY'
-r (raw output) when passing jq results to shell variables or other commands to strip JSON string quotes--arg / --argjson to inject shell variables safely — never interpolate shell variables directly into filter stringsmap(f) over [.[] | f] for readability-c (compact) for newline-delimited JSON pipelines; omit it for human-readable debuggingjq -n and literal input before embedding in scriptsempty to drop unwanted elements rather than filtering to nulljq is read-only by design — it cannot write files or execute commands--argProblem: jq outputs null instead of the expected value
Solution: Check for typos in key names; use keys to inspect actual field names. Remember JSON is case-sensitive.
Problem: Numbers are quoted as strings in the output
Solution: Use --argjson instead of --arg when injecting numeric values.
Problem: Filter works in the terminal but fails in a script
Solution: Ensure the filter string uses single quotes in the shell to prevent variable expansion. Example: jq '.field' not jq ".field".
Problem: add returns null on an empty array
Solution: Use add // 0 or add // "" to provide a fallback default.
Problem: Streaming large files is slow
Solution: Use jq --stream or switch to jstream/gron for very large files.
@bash-pro — Wrapping jq calls in robust shell scripts@bash-linux — General shell pipeline patterns@github-automation — Using jq with GitHub CLI JSON output