AI assistant

The AI assistant fixes broken queries for you — it sees what went wrong, understands your data shape, and offers working alternatives you apply with a single keystroke. The same popup is a conversation: type a question, get an answer plus applyable queries, and ask follow-ups that build on what came before.

You get a syntax error. Now you're searching the web, reading jq docs, trying variations one by one.

$ jiq data.json Query: .users | group_by .role ^^^^^^^^^^^^^^^^^^^^^^^^^^ Syntax Error # Tab to browser... # Search: "jq group_by syntax" # Read docs... try group_by(.role)... no wait... # Try 5 more variations... # 10 minutes later: finally works

Press Ctrl+A. The AI sees your query, the error, and your data — then offers working fixes.

Query: .users | group_by .role Syntax Error # Press Ctrl+A... AI Suggestions: 1. .users | group_by(.role) 2. [.users[] | group_by(.role)] 3. .users | group_by(.role) | map({key: .[0].role, val: .}) # Press Alt+1 — done. 3 seconds.

How it works

1
Error appears
2
Press Ctrl+A
3
AI analyzes context
4
Pick a suggestion
5
Applied

The AI sends your current query, the error message, and a sample of your JSON to the configured provider. It returns up to 5 suggestions ranked by relevance. The entire round trip typically takes 1-3 seconds.

Get a fix for a failing query

  1. Write a query that produces an error (the Syntax Error banner appears).
  2. Press Ctrl+A to open the AI popup.
  3. Wait for suggestions to appear (a loading indicator shows progress).
  4. Press Alt+1 through Alt+5 to apply a suggestion directly — or use Alt+j/Alt+k to navigate, then Enter to apply.

Ctrl+A only shows or hides the popup; your cursor stays in the query box, and the popup keeps suggesting as you type. Press Ctrl+G when you want to type into the popup instead.

AI suggestions popup
Query: .items | map(select(.price > 100) | .name, .price)
Syntax Error: unexpected ',' at line 1
 
AI Suggestions:
1. .items[] | select(.price > 100) | {name, price}
2. [.items[] | select(.price > 100) | {name: .name, price: .price}]
3. .items | map(select(.price > 100) | {name, price})
 
Alt+1..3 Apply | Alt+j/k Navigate | Enter Apply selected

Ask for help with a working query

The AI assistant is not limited to fixing errors. Even when your query works, press Ctrl+A and the AI may suggest optimizations — a more concise form, a more idiomatic construct, or a more robust one. When the query is already as good as it gets, the popup says No suggestions.

Ask a question

The bottom row of the popup is a chat input. Type there instead of in the query box when you want to talk about the data or the query rather than edit it.

  1. Press Ctrl+G. The popup opens if it was hidden, and the cursor lands in the chat input.
  2. Type a question and press Enter. Anything goes: “why is this empty?”, “how do I group these by role?”, “explain suggestion 2”, “what’s the difference between map and .[]?”.
  3. The answer appears as a short prose reply, followed by numbered queries when a query answers the question. Apply one with Alt+1..Alt+5 exactly like a fix.
  4. Ask a follow-up. The AI remembers the conversation, so “now only the active ones” or “make that a single object” works without restating the goal.
AI popup, conversation
❯ how do I count users per role?
group_by collects users sharing a role; length counts each group.
[Query] .users | group_by(.role) | map({role: .[0].role, count: length})
 
❯ only active users
Filter before grouping so inactive users never reach the count.
 
1. [Query] .users | map(select(.active)) | group_by(.role) | map({role: .[0].role, count: length})
Keeps active users, then counts per role
 
──────────────────────────────────────────────────
Ask about this query or data…

Every request carries the current query, its output or error, a sample of the data, the suggestions on screen, and the earlier questions and answers. Suggestions triggered by editing the query use the same conversation, so once you have explained what you are after, the fixes and optimizations it proposes take that into account.

Earlier exchanges stay visible above the current one, dimmed. Scroll them with / or PgUp/PgDn while the chat input is focused, or with the mouse wheel. Ctrl+L forgets the conversation and starts fresh. The last 12 exchanges are sent with each request; older ones drop off.

What the popup tells you

Popup state What it means
A numbered list of suggestions The AI returned up to 5 jq queries you can apply. [Fix] corrects an error, [Optimize] improves a working query, [Query] does what you asked for in chat.
Prose above the list The answer to your question.
No suggestions The AI ran successfully but had nothing useful to add for this query (common for the bare . identity query). This is normal, not an error.
Could not parse AI response The provider returned a response jiq could not read. Re-run with --debug and check /tmp/jiq-debug.log to see the raw response.
Action Key
Move between suggestions Alt+Up / Alt+Down or Alt+j / Alt+k
Apply the highlighted suggestion Enter (after navigating with Alt+Up/Down)
Apply suggestion N directly Alt+1 through Alt+5
Focus the chat input (opens the popup if hidden) Ctrl+G, or click its row
Send the typed question Enter (chat input focused)
Back to the query box, popup stays open Esc or Ctrl+G
Clear the conversation Ctrl+L (chat input focused)
Show or hide the popup Ctrl+A

Configure the AI provider

The AI assistant requires a provider configuration in ~/.config/jiq/config.toml. jiq supports Anthropic, OpenAI, Gemini, AWS Bedrock, and any OpenAI-compatible API.

[ai]
enabled = true
provider = "anthropic"    # "anthropic", "openai", "gemini", or "bedrock"
max_context_length = 100000  # characters of JSON context sent to AI (default 100k)

Anthropic

[ai.anthropic]
api_key = "sk-ant-..."
model = "claude-haiku-4-5-20251001"
effort = "high"      # optional: low | medium | high | xhigh | max (Claude 4.6+)
context_1m = false   # optional: 1M-token context window beta (Claude Sonnet 4/4.5)

effort sets the reasoning depth on Claude models that support it. context_1m opts into the 1M-token context window — raise max_context_length too, or jiq still sends the same small sample.

OpenAI

[ai.openai]
api_key = "sk-proj-..."
model = "gpt-4o-mini"
effort = "medium"    # optional: minimal | low | medium | high | xhigh | max (reasoning models)

effort maps to the OpenAI reasoning_effort field and is only sent when set.

Gemini

[ai.gemini]
api_key = "AIza..."
model = "gemini-3-flash-preview"
effort = "medium"    # optional: minimal | low | medium | high (Gemini 3+; xhigh/max clamp to high)

effort maps to Gemini’s thinkingLevel. Gemini 2.5-series models use a different mechanism and reject it — omit effort on those.

AWS Bedrock

[ai.bedrock]
region = "us-east-1"
model = "global.anthropic.claude-haiku-4-5-20251001-v1:0"
profile = "default"  # optional: uses default credential chain if omitted
effort = "high"      # optional: low | medium | high | xhigh | max (Claude Sonnet/Opus 4.6+)
context_1m = false   # optional: enable the 1M-token context window (Claude Sonnet 4/4.5)

effort sets the reasoning depth for Claude models that support it (Sonnet/Opus 4.6 and newer). Omit it to use the model default. Setting it on a model without reasoning support makes Bedrock reject the request.

context_1m opts into the 1M-token context window (Claude Sonnet 4 and 4.5; newer Sonnet models already default to 1M). On its own it only lifts the ceiling — raise max_context_length too, or jiq still sends the same small sample. Prompts over 200K tokens are billed at a higher rate.

OpenAI models on Bedrock (gpt-oss, GPT-5.x)

Two routes, depending on how you want to authenticate:

AWS credentials / profile (Converse). OpenAI models on Bedrock — gpt-oss and GPT-5.6 (Sol/Terra/Luna) — support the Converse API, so the regular bedrock provider works with the same region/profile setup as Claude. jiq sends effort as the OpenAI reasoning_effort field automatically when the model ID contains openai.:

[ai.bedrock]
region = "us-east-1"
profile = "my-profile"
model = "us.openai.gpt-5.6-sol"   # or "openai.gpt-oss-120b-1:0"
effort = "low"       # gpt-oss: low | medium | high; GPT-5.6 also takes minimal/xhigh/max

Bedrock API key (OpenAI-compatible endpoint). GPT-5.x models are served through Bedrock’s OpenAI-compatible endpoint. Point the openai provider at it with a Bedrock API key (generate one in the Bedrock console under API keys, or mint a short-term one from AWS credentials with the aws-bedrock-token-generator package):

[ai]
enabled = true
provider = "openai"

[ai.openai]
api_key = "your-bedrock-api-key"
base_url = "https://bedrock-runtime.us-east-1.amazonaws.com/openai/v1"
model = "openai.gpt-5.6-sol"
effort = "medium"    # optional: minimal | low | medium | high | xhigh | max

effort maps to the OpenAI reasoning_effort field. Omit it to use the model default.

Extra instructions

extra_instructions appends your own guidance to every AI prompt — style preferences, house conventions, favored jq idioms, the tone of chat answers. It never replaces the built-in prompt: the output-format contract jiq’s suggestion parser depends on always takes precedence.

[ai]
extra_instructions = "Prefer map() over .[] pipelines. Keep suggestions POSIX-shell safe."

OpenAI-compatible APIs (Ollama, LM Studio, x.ai)

Any API that follows the OpenAI chat completions format works by setting provider = "openai" with a custom base_url:

# Ollama (local)
[ai.openai]
base_url = "http://localhost:11434/v1"
model = "llama3"

# LM Studio (local)
[ai.openai]
base_url = "http://localhost:1234/v1"
model = "local-model"

# x.ai Grok
[ai.openai]
api_key = "your-xai-api-key"
base_url = "https://api.x.ai/v1"
model = "grok-4-fast-non-reasoning"

For local providers that don’t require authentication, omit the api_key field entirely.

These endpoints share the [ai.openai] options, including effort — jiq only sends reasoning_effort when you set it, so servers that don’t support the field are unaffected. Set it only for models that take it (e.g. gpt-oss on Ollama).

Tuning context size

The max_context_length setting controls how much of your JSON data is sent to the AI. Larger values give the AI more context for better suggestions but increase token usage and cost. Smaller values reduce cost and latency.

[ai]
max_context_length = 50000   # send less context (faster, cheaper)
max_context_length = 200000  # send more context (better suggestions for large files)

For sensitive data, a local model via Ollama or LM Studio keeps everything on your machine.

All keys

Key Action
Ctrl+A Show or hide the popup
Ctrl+G Focus the chat input (opens the popup if hidden); again to go back
Enter Send the typed question (chat input focused)
Esc Back to the query box; popup stays open
Ctrl+L Clear the conversation (chat input focused)
/ , PgUp / PgDn Scroll the conversation (chat input focused)
Alt+1..Alt+5 Apply suggestion 1-5 directly
Alt+Up / Alt+Down Navigate suggestions
Alt+j / Alt+k Navigate suggestions (vim style)
Enter Apply selected suggestion (after Alt+Up/Down)