celq
Use celq to process structured data with Common Expression Language (CEL). Favor it for JSON5, YAML, TOML, and XML, where jq does not parse the source format itself, and when an expression should stay readable to people familiar with C-like languages.
CEL is intentionally limited, portable, and non-Turing-complete. That makes it a useful expression boundary for externally supplied queries. It does not make arbitrary shell construction safe: keep data out of the expression string and pass scalar values with typed --arg options.
Choose how to invoke celq
Use an existing celq executable first. If it is unavailable, choose an already-installed package runner in this order when practical:
npx -y celq ...uvx celq ...mise x conda:celq -- celq ...
Do not permanently install celq or modify the user's global tool configuration unless the user asks.
When permanent installation is requested:
On macOS or Linux, if Homebrew is already available, use
brew install get-celq/tap/celq.For a project managed by mise, add celq to
mise.toml:[tools] "conda:celq" = "latest"
In the examples below, replace celq with the selected runner prefix when needed.
Use the CEL mental model
- The input root is
this, not jq's.. - Select object fields with
this.fieldand list items withthis[0]. - Use CEL macros such as
filter,map,exists, andallfor collections; celq also providesslice(start, end)as an optional extension. - Construct output objects with CEL map literals such as
{"name": this.name}. - CEL produces one value for an evaluation. Do not assume jq's stream, pipe, or generator semantics.
- Shell-quote CEL expressions so the shell does not expand them.
For example:
celq --from-json5 'this.services.filter(s, s.enabled).map(s, s.name)' < config.json5
celq --from-yaml '{"name": this.metadata.name, "replicas": this.spec.replicas}' < deployment.yaml
celq --from-toml 'this.package.version' < Cargo.toml
celq --from-xml 'this.items.item.filter(x, x["$"].id == "2")[0].name' < items.xml
Read references/formats.md before working with JSON5, YAML, TOML, XML, or NDJSON. It documents the input flags and format-specific data model.
Read references/recipes.md for filtering, projection, predicates, external arguments, output control, and reusable expression files.
Read references/jq-to-cel.md when translating jq knowledge or correcting an expression that accidentally uses jq syntax.
Handle external values safely
Do not splice untrusted data into CEL source text. Pass supported scalar values as typed arguments:
celq --arg='environment:string=production' \
'this.services.filter(s, s.environment == environment)' < services.json
The supported argument types are int, uint, float, bool, and string. When complex external data is needed, provide it as the structured input and use a named root with --root-var if that makes the expression clearer.
CEL's constrained expression model is easier to reason about than a general scripting language, but shell quoting, file access, package execution, and downstream commands still need normal care.
Preserve output intent
celq emits JSON, including when the input is JSON5, YAML, TOML, or XML.
- Use
--raw-outputonly when a string must be consumed without JSON quotes. - Use
--pretty-printfor human-facing output. - Use
--sort-keyswhen deterministic object-key order matters. - Use
--boolean --voidwhen the result should control a shell branch by exit status. - Avoid pretty-printing in the middle of NDJSON pipelines.
Respect current boundaries
- Treat YAML support as best-effort for YAML values that map cleanly to JSON. Do not promise preservation of comments, anchors, tags, styles, or exact scalar spelling.
- XML attributes appear under the
$map (access it asnode["$"]), and XML scalar content is not automatically converted to numbers or booleans. - celq parses input eagerly; reconsider it for data too large to hold comfortably in memory.
- Do not mechanically translate a complex jq program. Re-express the desired result using CEL's value and collection model.