| name | jq |
| description | This skill should be used when the user asks to "parse this JSON", "extract X from this output", "pull the field with jq", "filter this API response", "reshape this JSON", "aggregate these records", "explain this jq filter", or invokes /jq. Also trigger when the user pastes raw JSON or CLI output (curl, kubectl, aws, gh, docker) and asks to filter, transform, or summarize it. Do NOT use for writing JSON, JSON Schema validation, jsonnet, or non-jq shell scripting. |
| version | 1.0.0 |
jq - JSON Querying and Transformation
Overview
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.
Workflow
- Read the input. Identify the JSON source - pasted text, a file, or a piped command (
curl, kubectl, aws, gh, docker).
- Inspect the shape. Run
jq 'keys' or jq '.[0]' on a sample to confirm actual field names and nesting before writing the filter.
- Build the filter incrementally. Start from the root, add one pipe stage at a time, verifying each stage against the sample.
- Choose output mode. Use
-r for shell consumption, -c for NDJSON pipelines, default pretty-print for human reading.
- Verify against sample input before embedding in a script. Confirm no
null/empty surprises.
- Deliver the final command plus a one-line explanation of each filter stage (see Output Format).
How It Works
jq 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.
Basic Selection
echo '{"name":"alice","age":30}' | jq '.name'
echo '{"user":{"email":"a@b.com"}}' | jq '.user.email'
echo '[10, 20, 30]' | jq '.[1]'
echo '[1,2,3,4,5]' | jq '.[2:4]'
echo '[{"id":1},{"id":2}]' | jq '.[]'
Filtering with select
echo '[{"role":"admin"},{"role":"user"},{"role":"admin"}]' \
| jq '[.[] | select(.role == "admin")]'
curl -s https://api.github.com/repos/owner/repo/issues \
| jq '[.[] | select(.comments > 5)]'
jq '[.[] | select(.email != null)]'
jq '[.[] | select(.active == true and .score >= 80)]'
Mapping and Transformation
echo '[{"name":"alice","age":30},{"name":"bob","age":25}]' \
| jq '[.[] | .name]'
jq 'map(.name)'
jq '[.[] | {user: .name, years: .age}]'
jq '[.[] | . + {senior: (.age > 28)}]'
jq '[.[] | {username: .name, email_address: .email}]'
Aggregation and Reduce
echo '[1, 2, 3, 4, 5]' | jq 'add'
jq '[.[].price] | add'
jq 'length'
jq 'max_by(.score)'
jq 'min_by(.created_at)'
echo '[1,2,3,4,5]' | jq 'reduce .[] as $x (0; . + $x)'
jq 'group_by(.department)'
jq 'group_by(.status) | map({status: .[0].status, count: length})'
String Interpolation and Formatting
jq -r '.[] | "\(.name) is \(.age) years old"'
jq -r '.[] | [.name, .age, .email] | @csv'
jq -r '.[] | [.name, .score] | @tsv'
jq -r '.query | @uri'
jq -r '.data | @base64'
Working with Keys and Paths
jq 'keys'
jq 'has("email")'
jq 'del(.password)'
jq '[.[] | del(.internal_id, .raw_payload)]'
jq '.. | .id? // empty'
jq '[paths(scalars)]'
Conditionals and Error Handling
jq 'if .score >= 90 then "A" elif .score >= 80 then "B" else "C" end'
jq '.nickname // .name'
jq '[.[] | try .nested.value catch null]'
jq '.[] | .optional_field // empty'
Practical Shell Integration
jq '.users' data.json
jq -c '.[]' records.json | while IFS= read -r record; do
echo "Processing: $record"
done
STATUS="active"
jq --arg s "$STATUS" '[.[] | select(.status == $s)]'
jq --argjson threshold 42 '[.[] | select(.value > $threshold)]'
jq -s '.' records.ndjson
jq -s 'add' file1.json file2.json
kubectl get pods -o json | jq '.items[] | {name: .metadata.name, status: .status.phase}'
gh pr list --json number,title | jq -r '.[] | "\(.number)\t\(.title)"'
aws ec2 describe-instances \
| jq -r '.Reservations[].Instances[] | select(.State.Name=="running") | .InstanceId'
docker inspect $(docker ps -q) | jq -r '.[] | "\(.Name)\t\(.Config.Image)"'
Advanced Patterns
jq '[.names, .scores] | transpose | map({name: .[0], score: .[1]})'
jq 'flatten(1)'
jq 'unique_by(.email)'
jq '[.[] | .name] | unique | sort'
jq 'walk(if type == "string" then ascii_downcase else . end)'
export API_KEY=secret
jq -n 'env.API_KEY'
Best Practices
- Prefer
map(f) over [.[] | f] for readability
- Test filters interactively with
jq -n and literal input before embedding in scripts
(Core rules - -r, --arg/--argjson, single-quoting, null/empty handling - are enforced in Quality Gates below.)
Output Format
- Return a single copy-paste-ready
jq command inside a ```bash code block.
- Follow it with a one-line plain-English explanation per filter stage (what each
| does).
- When the input shape was inspected, show the sample command used (
jq 'keys', jq '.[0]').
- No preamble, no restating the question. Command first, explanation second.
Error Handling
- Missing input: No JSON source provided → ask the user to paste the JSON or name the file/command. Do not guess the shape.
- Malformed JSON: Input fails to parse → report the parse error verbatim and point at the offending line; suggest
jq with --stream or a validator if truncated.
- Ambiguous target field: Field name unclear or appears at multiple depths → run
jq 'keys' / jq '[paths(scalars)]' to enumerate, then confirm the intended path with the user before finalizing.
- Empty / null result: Filter returns nothing → check key casing and nesting; offer an
// empty or // <default> fallback.
Quality Gates
Security & Safety Notes
jq is read-only by design - it cannot write files or execute commands
- Avoid embedding untrusted JSON field values directly into shell commands; always quote or use
--arg
Common Pitfalls
-
Problem: jq outputs null instead of the expected value
Solution: Check for typos in key names; use keys to inspect actual field names. JSON keys are 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.
Companion Command
../../commands/jq.md - /jq slash command with AskUserQuestion intake for JSON source, goal, and output mode. Walks the user through inputs, then applies this skill's workflow.
Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.