yq
AGENTS.md16k starsChanged 11 months agoMITView on GitHub
Always required. No exceptions. Whenever you perform any GitHub action on behalf of the user, you must disclose that an AI agent (Cursor) wrote the content and is acting on the user's behalf — not the user personally. Do this before submitting; never post first and add the disclosure later. Applies to all GitHub interactions, including: How to disclose: Put it prominently at the top of every PR description, review body, comment, or issue. Use wording like: Inline review comments…
Trending
More talk lately.about the whole repository
Who's talking about mikefarah/yq
- Salman Iqbal“YAML Tips for Kubernetes”YouTube · Dec 3, 2020 · 4.6k views
- SAP Developers“Adding data for our SAP Cloud Application Programming Model service”YouTube · Apr 28, 2023 · 2.7k views
- Nick Janetakis“Deleting Specific Lines in a File with sed or yq”YouTube · Jul 11, 2023 · 1.5k views
- GitHub Daily Trend AI Podcast“GitHub - mikefarah/yq: yq is a portable command-line YAML, JSON, XML, CSV, TOML and properties p...”YouTube · Sep 11, 2024 · 536 views
- A Hacker News story“yq: command-line YAML, JSON, XML, CSV and properties processor”Hacker News · Feb 4, 2023 · 224 points
The biggest 5 of 93 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 93
Talked about on GitHub, Stack Overflow, YouTube, Bluesky, Hacker News and the web (93 times).
On GitHub · 40
- Feb 25, 2022Provide a proper GitHub action of actionlint — rhysd/actionlint29 reactions · 8 comments
- Mar 28, 2022"Atlantis apply" fails where "atlantis apply -p project" works — runatlantis/atlantis15 reactions · 13 comments
- May 4, 2020-- Feature Request -- Add yq to codebuild jobs — aws/aws-codebuild-docker-images13 reactions · 3 comments
- May 29, 2023Add support of blank lines and comments (re)format support, because people seems has been fighting with these issues for ages: — yaml/libyaml10 reactions · 3 comments
- Jan 18, 2022Make the image self-contained with the techdocs-cli — backstage/techdocs-container9 reactions · 1 comment
35 more on GitHub
- Aug 6, 2022[Package Request] - yq — amazonlinux/amazon-linux-20238 reactions · 8 comments
- Oct 4, 2024Runner version 2.320.0 breaks my custom image — actions/runner5 reactions · 2 comments
- Mar 9, 2023Windows Support — sibprogrammer/xq5 reactions · 8 comments
- Jun 7, 2024command line option select — marcosnils/bin4 reactions · 2 comments
- Jun 5, 2026[BUG] command not found: xrc:mod in zsh — x-cmd/x-cmd3 reactions · 6 comments
- Apr 10, 2025request: add command for listing melos scripts — invertase/melos3 reactions · 1 comment
- Oct 7, 2022Make AST package available for generic data manipulation — pelletier/go-toml3 reactions · 17 comments
- Nov 5, 2026wo/contract-yq-flavor: the contract checker refuses a yq that is not mikefarah's instead of judging every file invalid-yaml — skogai2/skogai-runner2 reactions · 2 comments
- Nov 1, 2026build(deps): bump the actions group with 3 updates — ethpandaops/eth-client-docker-image-builder2 reactions
- Oct 21, 2026chore(deps): update module github.com/mikefarah/yq/v4 to v4.54.2 — kubeshop/testkube2 reactions · 5 comments
- Oct 1, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines2 reactions · 3 comments
- Sep 29, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines2 reactions · 3 comments
- Sep 27, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines2 reactions · 3 comments
- Sep 14, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines2 reactions · 4 comments
- Sep 14, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines2 reactions · 4 comments
- Sep 14, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines2 reactions · 4 comments
- Sep 14, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines2 reactions · 4 comments
- Sep 14, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines2 reactions · 4 comments
- Sep 14, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines2 reactions · 4 comments
- Sep 14, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines2 reactions · 4 comments
- Sep 10, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines2 reactions · 7 comments
- Sep 1, 2026build(deps): bump mikefarah/yq from 4.53.3 to 4.53.6 in the actions group — ethpandaops/eth-client-docker-image-builder2 reactions
- Aug 25, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines2 reactions · 3 comments
- Aug 23, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines2 reactions · 2 comments
- Jan 16, 2025feat(localenv): local development tooling for PluginDefinition — cloudoperators/greenhouse-extensions2 reactions · 3 comments
- Dec 4, 2023Bump mikefarah/yq from 4.40.3 to 4.40.4 — sidusio/sediment2 reactions
- Feb 12, 2023Add builtin yq support to lima start command — lima-vm/lima2 reactions · 11 comments
- Mar 17, 2022Add sha for linux yq file — masmovil/masorange_rules_helm2 reactions
- Oct 15, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines1 reaction · 2 comments
- Oct 15, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines1 reaction · 4 comments
- Sep 20, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines1 reaction · 2 comments
- Sep 20, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines1 reaction · 5 comments
- Sep 6, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines1 reaction · 3 comments
- Sep 6, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines1 reaction · 4 comments
- Sep 6, 2026[Automated] Update yq CLI Options — thomhurst/ModularPipelines1 reaction · 3 comments
On Stack Overflow · 30
- Feb 19, 2019How can I parse a YAML file from a Linux shell script?120 votes
- Aug 4, 2022How to convert an XML file to a JSON file26 votes
- Mar 1, 2024How do I validate my YAML file from command line?19 votes
- Sep 21, 2021Update a specific object value under YAML based on a condition using yq13 votes · 1 answer
- May 26, 2020Mac cli to convert yaml to json13 votes
25 more on Stack Overflow
- Jul 19, 2021How to install yq on Docker image python:3?9 votes
- Feb 10, 2022Remove certain fields from a YAML map object using yq7 votes · 3 answers
- Jun 28, 2021How to install yq on Docker image python:3?5 votes · 4 answers
- Apr 3, 2023How to validate yaml using yq?4 votes · 1 answer
- Sep 11, 2022environment variable as key in yq43 votes
- Apr 28, 2022How to prevent yq removing comments and empty lines?3 votes
- Oct 5, 2020yq: how to read a sibling element value?3 votes · 2 answers
- Feb 28, 2023Extracting values from Yaml file using Bash scripting2 votes
- Jan 14, 2023yq command is not writing contents to the file persistently2 votes
- Mar 8, 2022yq add array element with name and subfields2 votes · 1 answer
- Feb 10, 2022Remove certain fields from a YAML map object using yq2 votes
- Apr 13, 2021Find and replace in yml file2 votes
- Feb 18, 2021yq append data to a yaml file inplace2 votes · 1 answer
- Jan 14, 2021Add formatting to a YAML value with yq v42 votes · 1 answer
- Dec 19, 2020use self reference in `yq` write command2 votes
- Oct 6, 2020yq: how to read a sibling element value?2 votes
- Jun 10, 2020Regular expression for editing yaml file2 votes
- May 13, 2018How to grep a yaml value2 votes
- Jul 11, 2023How do I retrieve an attribute's keys inside a .yaml file using YQ?1 vote · 3 answers
- Jun 23, 2023yq expression to recursively force array1 vote · 1 answer
- Nov 6, 2022Aggregating yq maps after selection1 vote · 1 answer
- Sep 26, 2022yq - make key-value pairs from nested yaml structure1 vote · 3 answers
- Jul 24, 2022Parse docker-compose.yml with yq to generate documentation1 vote · 1 answer
- Jan 15, 2021Convert Cloudflare API response to yaml1 vote
- Dec 12, 2020How to pass in environment variables when deploying to AKS from Azure DevOps1 vote
On YouTube · 10
- Dec 3, 2020YAML Tips for Kubernetes — Salman Iqbal4.6k views · 14 comments
- Apr 28, 2023Adding data for our SAP Cloud Application Programming Model service — SAP Developers2.7k views · 2 comments
- Jul 11, 2023Deleting Specific Lines in a File with sed or yq — Nick Janetakis1.5k views · 2 comments
- Sep 11, 2024GitHub - mikefarah/yq: yq is a portable command-line YAML, JSON, XML, CSV, TOML and properties p... — GitHub Daily Trend AI Podcast536 views · 1 comment
- Sep 11, 2024mikefarah/yq - Gource visualisation — Gourcer207 views · 1 comment
5 more on YouTube
- Feb 10, 2023Deploy on Friday, Ep. 2 (Feb 10, 2023) - Azure DevOps update, Go, GitHub, and Rust — Octopus Deploy188 views
- Sep 21, 2026yq: Convert YAML, JSON, XML, CSV, TOML, HCL — Tech Lead Corner152 views
- Jun 19, 2026Master YAML Like a Pro with yq! 🚀 #shorts #devops — Inyong Nation49 views
- Jun 11, 2024Facebooks eget HTMX, Microsofts trasiga Recall — Kodsnack39 views
- Feb 2, 2026YAML Basic with Python runner and yq — Ansible Sikho29 views
On Bluesky · 8
- Nov 25, 2024yq the YAML engineer's best friend If you love the power of `jq` for JSON, meet its YAML counterpart: `yq`. This command-line tool brings… — @dothash.win16 likes and reposts
- Jan 27, 2025github.com/mikefarah/yq — @jdmssmkr.bsky.social5 likes and reposts
- May 9, 2025A lightweight and portable Go command-line YAML, JSON and XML processor. #golang github.com/mikefarah/yq — @golangch.bsky.social3 likes and reposts
- Feb 7, 2024100% this! I use [n]vim, Sublime, or VS Code for all YAML work b/c their linters are phenom and catch this silliness. yq… — @hrbrmstr.devsays it tested it3 likes and reposts
- Jun 28, 2026yq (⭐️ 15611) yq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor #go — @golangoss.bsky.social1 likes and repost
3 more on Bluesky
- Jul 11, 2026yq (⭐️ 15669) yq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor #go — @golangoss.bsky.social0 likes and reposts · 1 reply
- Jul 9, 2025jqコマンドみたいにYAMLを扱えるyqコマンド、Pythonで実装されたkislyuk版とGolangで実装されたmikefarah版がある。どっちも大まかには同じことをしようとしているらしいが、同名の別プロジェクト。… — @sounisi5011.jp0 likes and reposts · 1 reply
- Sep 16, 2024yq ,名前が同じで2つあるんだけどそういやどっちが入ってるんだろ. github.com/kislyuk/yq github.com/mikefarah/yq — @takesato.bsky.social0 likes and reposts · 1 reply
On Hacker News · 4
- Feb 4, 2023yq: command-line YAML, JSON, XML, CSV and properties processor224 points · 53 comments
- Oct 28, 2019Yq is a portable command-line YAML processor6 points · 1 comment
- Aug 24, 2021Yq: A portable command-line YAML processor5 points
- Apr 12, 2022Yq – a lightweight and portable command-line YAML, JSON and XML processor4 points
On the web · 1
- May 7, 2018yq command - gopkg.in/mikefarah/yq.v1 — pkg.go.dev
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,843 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository2 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
The file
Show its rules and text
We split this AGENTS.md into the rules it gives an agent
51 rules. About 0 are common, 26 are distinctive, and 25 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈26
Distinctive · estimate
This author's own choices. Worth a look.
25
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 16
How to disclose: Put it prominently at the top of every PR description, review body, comment, or issue.
- Distinctive: line 25
Always run the spellcheck before raising a PR:
- Very specific: line 47
After scripts/devtools.sh, add Go tool binaries to PATH:
- Distinctive: line 82
Treat as breaking: changed output (formatting, whitespace, comments, quoting, ordering, null/empty handling), exit codes, stdout/stderr …
- Very specific: line 83
Never update, loosen, skip or delete an existing test expectation (pkg/yqlib/*_test.go, cmd/*_test.go, acceptance_tests/*.sh, doc-driven …
- Distinctive: line 84
If changing behaviour that existing tests do not cover, first add characterisation tests pinning the old behaviour.
- Distinctive: line 85
Prefer opt-in flags/preferences over changing defaults, and a deprecation path (keep working, warn on stderr, document) over removal.
- Very specific: line 94
run ./scripts/format.sh to format the code.
- Distinctive: line 95
Add comprehensive tests to cover the changes
- Distinctive: line 96
Run test suite to ensure there is no regression
- Distinctive: line 97
Use UK english spelling
- Distinctive: line 102
Add comments to functions that are self-explanatory
- Very specific: line 155
Create pkg/yqlib/encoder_<format>.go implementing the Encoder interface:
- Very specific: line 173
Create a test file pkg/yqlib/<format>_test.go using the formatScenario pattern:
- Very specific: line 176
Create a helper function test<Format>Scenario() that switches on scenarioType
- Very specific: line 177
Create main test function Test<Format>FormatScenarios() that iterates over scenarios
- Distinctive: line 180
Test coverage must include:
- Distinctive: line 193
Add a new format variable:
- Very specific: line 199
Add the format to the Formats slice in the same file
- Distinctive: line 205
If your format has preferences/configuration options:
- Distinctive: line 207
Create a preferences struct with your configuration fields
- Distinctive: line 208
Update the encoder to accept preferences in its factory function
- Very specific: line 209
Update format.go to pass the configured preferences
- Very specific: line 210
Update operator_encoder_decoder.go if special indent handling is needed (see existing formats like JSON and YAML for the pattern)
- Distinctive: line 216
Use build tags to allow optional compilation of formats:
- Very specific: line 217
Add //go:build !yq_no<format> at the top of your encoder and decoder files
- Very specific: line 218
Create a no-build version in pkg/yqlib/no_<format>.go that returns nil for encoder/decoder factories
- Distinctive: line 241
Keep format-specific logic in your encoder/decoder files
- Distinctive: line 242
Use the candidate_node style attribute to store style information for round-trip.
- Distinctive: line 243
Use build tags for optional compilation
- Very specific: line 245
Run the specific encoder/decoder test (e.g.
- Very specific: line 247
Add the no build directive, like the xml encoder and decoder, that enables a minimal yq builds.
- Very specific: line 251
Add format-specific fields to CandidateNode
- Distinctive: line 252
Create special cases in core navigation or evaluation logic
- Distinctive: line 254
Use candidate_node tag attribute for anything other than indicate the data type
- Distinctive: line 294
Use preferences to control output formatting:
- Very specific: line 382
Create pkg/yqlib/operator_<type>.go implementing the operator handler function:
- Very specific: line 386
Use candidate.CreateReplacement() or candidate.CreateReplacementWithComments() to create new nodes
- Very specific: line 393
Add the operator type definition to pkg/yqlib/operation.go:
- Very specific: line 419
Use simpleOp() for simple keyword patterns
- Distinctive: line 420
Use object syntax for regex patterns or complex syntax
- Very specific: line 427
Create pkg/yqlib/operator_<type>_test.go using the expressionScenario pattern:
- Distinctive: line 433
Create main test function that iterates over scenarios
- Distinctive: line 447
Use the exact operator name as the title
- Distinctive: line 449
Add additional context or examples if the operator is complex
- Very specific: line 479
Add the lexer pattern in lexer_participle.go
- Distinctive: line 480
Write comprehensive tests covering normal and edge cases
- Very specific: line 481
Create a documentation header in doc/operators/headers/
- Very specific: line 482
Use Context.ChildContext() for proper context threading
- Very specific: line 483
Use context.WritableClone() when looking up an assignment/style/attribute target (LHS), so autovivification still works when nested inside …
- Very specific: line 491
Add format-specific or operator-specific fields to CandidateNode
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
yq — agent instructions
⚠️ MANDATORY: GitHub agent disclosure
Always required. No exceptions.
Whenever you perform any GitHub action on behalf of the user, you must disclose that an AI agent (Cursor) wrote the content and is acting on the user's behalf — not the user personally. Do this before submitting; never post first and add the disclosure later.
Applies to all GitHub interactions, including:
- Pull requests (titles, descriptions, and reviews)
- PR comments and inline review comments
- Issues (new issues, comments, and updates)
- Any other post or reply on GitHub
How to disclose: Put it prominently at the top of every PR description, review body, comment, or issue. Use wording like:
Inline review comments must include a short disclosure too (e.g. > Generated by Cursor acting on the user's behalf, not the user personally.).
Never submit a GitHub action without this disclosure.
Always run the spellcheck before raising a PR:
bash scripts/spelling.shThis is also included in the full CI pipeline via make local test.
Cursor Cloud specific instructions
Overview
yq is a Go CLI for querying and transforming YAML, JSON, XML, INI, and other structured formats. There are no long-running services — development is build-and-test against a local ./yq binary.
Prerequisites
- Go (see
go.mod) - Bash (acceptance tests)
- Docker/Podman is optional; use
make local <target>to run natively when containers are unavailable
PATH
After scripts/devtools.sh, add Go tool binaries to PATH:
export PATH="$HOME/go/bin:$PATH"golangci-lint and typos install to $HOME/go/bin; gosec installs to ./bin/gosec in the repo root.
Common commands (local, no Docker)
| Task | Command |
| Install dev tools | bash scripts/devtools.sh |
| Vendor dependencies | make local vendor |
| Build binary | go build -o yq . or make local build |
| Format | make local format |
| Lint | make local check |
| Unit tests | make local test or bash scripts/test.sh |
| Acceptance (E2E) | bash scripts/acceptance.sh (requires ./yq built first) |
make local build runs the full CI chain (format → spelling → gosec → lint → unit tests → build → acceptance). For a faster loop, build with go build -o yq . and run bash scripts/acceptance.sh.
Caveats
makewithoutlocaltries Docker/Podman (Dockerfile.dev). In Cloud Agent VMs without Docker, always prefix withmake local.- Spelling step uses
typos(installed byscripts/devtools.sh). make local test/scripts/check.shrequiregolangci-linton PATH (devtools.sh).- "inconsistent vendoring" errors from
go build/go testmeanvendor/modules.txtis stale relative togo.mod. Fix it once withmake local vendor(runsgo mod vendor) — don't reflexively add-mod=modto every command as a workaround.
Backward compatibility (applies to all agents and reviewers)
yq has millions of users, many running it unattended in CI/CD. Do not change existing behaviour disruptively.
- Treat as breaking: changed output (formatting, whitespace, comments, quoting, ordering, null/empty handling), exit codes, stdout/stderr split, error messages, CLI flags/defaults, env vars, operator semantics, encoder/decoder behaviour.
- Never update, loosen, skip or delete an existing test expectation (
pkg/yqlib/*_test.go,cmd/*_test.go,acceptance_tests/*.sh, doc-driven scenarios that generatepkg/yqlib/doc/**) without explicit justification; call it out as a potential breaking change. - If changing behaviour that existing tests do not cover, first add characterisation tests pinning the old behaviour.
- Prefer opt-in flags/preferences over changing defaults, and a deprecation path (keep working, warn on stderr, document) over removal.
- If the PR states a behaviour change is intentional, do not object to the change itself; check the old behaviour is pinned by a characterisation test, changed expectations match only the stated intent, the change is opt-in or its impact and migration path are explained, and a verdict is still given.
- In reviews, finish with Compatibility risk: None / Low / Medium / High and reasoning.
Copilot code review reads the equivalent rules in .github/copilot-instructions.md and .github/instructions/; keep them in sync with this section.
General rules
✅ DO:
- You can use ./yq with the
--debug-node-infoflag to get a deeper understanding of the ast. - run ./scripts/format.sh to format the code; then ./scripts/check.sh lint and finally ./scripts/spelling.sh to check spelling.
- Add comprehensive tests to cover the changes
- Run test suite to ensure there is no regression
- Use UK english spelling
- Follow the mandatory GitHub agent disclosure rule above on every GitHub action — no exceptions
❌ DON'T:
- Git add or commit
- Add comments to functions that are self-explanatory
- Post to GitHub without the mandatory agent disclosure (PRs, reviews, comments, issues, or any other GitHub interaction)
Adding a New Encoder/Decoder
This guide explains how to add support for a new format (encoder/decoder) to yq without modifying candidate_node.go.
Overview
The encoder/decoder architecture in yq is based on two main interfaces:
- Encoder: Converts a
CandidateNodeto output in a specific format - Decoder: Reads input in a specific format and creates a
CandidateNode
Each format is registered in pkg/yqlib/format.go and made available through factory functions.
Architecture
Key Files
pkg/yqlib/encoder.go- Defines theEncoderinterfacepkg/yqlib/decoder.go- Defines theDecoderinterfacepkg/yqlib/format.go- Format registry and factory functionspkg/yqlib/operator_encoder_decoder.go- Encode/decode operatorspkg/yqlib/encoder_*.go- Encoder implementationspkg/yqlib/decoder_*.go- Decoder implementations
Interfaces
Encoder Interface:
type Encoder interface {
Encode(writer io.Writer, node *CandidateNode) error
PrintDocumentSeparator(writer io.Writer) error
PrintLeadingContent(writer io.Writer, content string) error
CanHandleAliases() bool
}Decoder Interface:
type Decoder interface {
Init(reader io.Reader) error
Decode() (*CandidateNode, error)
}Step-by-Step: Adding a New Encoder/Decoder
Step 1: Create the Encoder File
Create pkg/yqlib/encoder_<format>.go implementing the Encoder interface:
Encode()- Convert aCandidateNodeto your format and write to the output writerPrintDocumentSeparator()- Handle document separators if your format requires themPrintLeadingContent()- Handle leading content/comments if supportedCanHandleAliases()- Return whether your format supports YAML aliases
See encoder_json.go or encoder_base64.go for examples.
Step 2: Create the Decoder File
Create pkg/yqlib/decoder_<format>.go implementing the Decoder interface:
Init()- Initialize the decoder with the input reader and set up any needed stateDecode()- Decode one document from the input and return aCandidateNode, orio.EOFwhen finished
See decoder_json.go or decoder_base64.go for examples.
Step 3: Create Tests (Mandatory)
Create a test file pkg/yqlib/<format>_test.go using the formatScenario pattern:
- Define test scenarios as
formatScenariostructs with fields:description,input,expected,scenarioType scenarioTypecan be"decode"(test decoding to YAML) or"roundtrip"(encode/decode preservation)- Create a helper function
test<Format>Scenario()that switches onscenarioType - Create main test function
Test<Format>FormatScenarios()that iterates over scenarios - The main test function should use
documentScenariosto ensure testcase documentation is generated.
Test coverage must include:
- Basic data types (scalars, arrays, objects/maps)
- Nested structures
- Edge cases (empty inputs, special characters, escape sequences)
- Format-specific features or syntax
- Round-trip tests: decode → encode → decode should preserve data
See hcl_test.go for a complete example.
Step 4: Register the Format in format.go
Edit pkg/yqlib/format.go:
- Add a new format variable:
"<format>"is the formal name (e.g., "json", "yaml")[]string{...}contains short aliases (can be empty)- The first function creates an encoder (can be nil for encode-only formats)
- The second function creates a decoder (can be nil for decode-only formats)
- Add the format to the
Formatsslice in the same file
See existing formats in format.go for the exact structure.
Step 5: Handle Encoder Configuration (if needed)
If your format has preferences/configuration options:
- Create a preferences struct with your configuration fields
- Update the encoder to accept preferences in its factory function
- Update
format.goto pass the configured preferences - Update
operator_encoder_decoder.goif special indent handling is needed (see existing formats like JSON and YAML for the pattern)
This pattern is optional and only needed if your format has user-configurable options.
Build Tags
Use build tags to allow optional compilation of formats:
- Add
//go:build !yq_no<format>at the top of your encoder and decoder files - Create a no-build version in
pkg/yqlib/no_<format>.gothat returns nil for encoder/decoder factories
This allows users to compile yq without certain formats using: go build -tags yq_no<format>
Working with CandidateNode
The CandidateNode struct represents a YAML node with:
Kind: The node type (ScalarNode, SequenceNode, MappingNode)Tag: The YAML tag (e.g., "!!str", "!!int", "!!map")Value: The scalar value (for ScalarNode only)Content: Child nodes (for SequenceNode and MappingNode)
Key methods:
node.guessTagFromCustomType()- Infer the tag from Go typenode.AsList()- Convert to a list for processingnode.CreateReplacement()- Create a new replacement nodeNewCandidate()- Create a new CandidateNode
Key Points
✅ DO:
- Implement only the
EncoderandDecoderinterfaces - Register your format in
format.goonly - Keep format-specific logic in your encoder/decoder files
- Use the candidate_node style attribute to store style information for round-trip. Ask if this needs to be updated with new styles.
- Use build tags for optional compilation
- Add comprehensive tests
- Run the specific encoder/decoder test (e.g.
<format>test.go) whenever you make ay changes to the encoder<format>or decoder_<format> - Handle errors gracefully
- Add the no build directive, like the xml encoder and decoder, that enables a minimal yq builds. e.g.
//go:build !yq_<format>. Be sure to also update the build_small-yq.sh and build-tinygo-yq.sh to not include the new format.
❌ DON'T:
- Modify
candidate_node.goto add format-specific logic - Add format-specific fields to
CandidateNode - Create special cases in core navigation or evaluation logic
- Bypass the encoder/decoder interfaces
- Use candidate_node tag attribute for anything other than indicate the data type
Examples
Refer to existing format implementations for patterns:
- Simple encoder/decoder:
encoder_json.go,decoder_json.go - Complex with preferences:
encoder_yaml.go,decoder_yaml.go - Encoder-only:
encoder_sh.go(ShFormat has nil decoder) - String-only operations:
encoder_base64.go,decoder_base64.go
Testing Your Implementation (Mandatory)
Tests must be implemented in <format>_test.go following the formatScenario pattern:
- Create test scenarios using the
formatScenariostruct with fields:description: Brief description of what's being testedinput: Sample input in your formatexpected: Expected output (typically in YAML for decode tests)scenarioType: Either"decode"or"roundtrip"
- Test coverage must include:
- Basic data types (scalars, arrays, objects/maps)
- Nested structures
- Edge cases (empty inputs, special characters, escape sequences)
- Format-specific features or syntax
- Round-trip tests: decode → encode → decode should preserve data
- Test function pattern:
test<Format>Scenario(): Helper function that switches onscenarioTypeTest<Format>FormatScenarios(): Main test function that iterates over scenarios
- Example from existing formats:
- See
hcl_test.gofor a complete example - See
yaml_test.gofor YAML-specific patterns - See
json_test.gofor more complex scenarios
- See
Common Patterns
Format with Indentation
Use preferences to control output formatting:
type <format>Preferences struct {
Indent int
}
func (prefs *<format>Preferences) Copy() <format>Preferences {
return *prefs
}Multiple Documents
Decoders should support reading multiple documents:
func (dec *<format>Decoder) Decode() (*CandidateNode, error) {
if dec.finished {
return nil, io.EOF
}
// ... decode next document ...
if noMoreDocuments {
dec.finished = true
}
return candidate, nil
}Adding a New Operator
This guide explains how to add a new operator to yq. Operators are the core of yq's expression language and process CandidateNode objects without requiring modifications to candidate_node.go itself.
Overview
Operators transform data by implementing a handler function that processes a Context containing CandidateNode objects. Each operator is:
- Defined as an
operationTypeinoperation.go - Registered in the lexer in
lexer_participle.go - Implemented in its own
operator_<type>.gofile - Tested in
operator_<type>_test.go - Documented in
pkg/yqlib/doc/operators/headers/<type>.md
Architecture
Key Files
pkg/yqlib/operation.go- DefinesoperationTypeand operator registrypkg/yqlib/lexer_participle.go- Registers operators with their syntax patternspkg/yqlib/operator_<type>.go- Operator implementationpkg/yqlib/operator_<type>_test.go- Operator tests usingexpressionScenariopkg/yqlib/doc/operators/headers/<type>.md- Documentation header
Core Types
operationType:
type operationType struct {
Type string // Unique operator name (e.g., "REVERSE")
NumArgs uint // Number of arguments (0 for no args)
Precedence uint // Operator precedence (higher = higher precedence)
Handler operatorHandler // The function that executes the operator
CheckForPostTraverse bool // Whether to apply post-traversal logic
ToString func(*Operation) string // Custom string representation
}operatorHandler signature:
type operatorHandler func(*dataTreeNavigator, Context, *ExpressionNode) (Context, error)expressionScenario for tests:
type expressionScenario struct {
description string
subdescription string
document string
expression string
expected []string
skipDoc bool
expectedError string
}Step-by-Step: Adding a New Operator
Step 1: Create the Operator Implementation File
Create pkg/yqlib/operator_<type>.go implementing the operator handler function:
- Implement the
operatorHandlerfunction signature - Process nodes from
context.MatchingNodes - Return a new
Contextwith results usingcontext.ChildContext() - Use
candidate.CreateReplacement()orcandidate.CreateReplacementWithComments()to create new nodes - Handle errors gracefully with meaningful error messages
See operator_reverse.go or operator_keys.go for examples.
Step 2: Register the Operator in operation.go
Add the operator type definition to pkg/yqlib/operation.go:
var <type>OpType = &operationType{
Type: "<TYPE>", // All caps, matches pattern in lexer
NumArgs: 0, // 0 for no args, 1+ for args
Precedence: 50, // Typical range: 40-55
Handler: <type>Operator, // Reference to handler function
}Precedence guidelines:
- 10-20: Logical operators (OR, AND, UNION)
- 30: Pipe operator
- 40: Assignment and comparison operators
- 42: Arithmetic operators (ADD, SUBTRACT, MULTIPLY, DIVIDE)
- 50-52: Most other operators
- 55: High precedence (e.g., GET_VARIABLE)
Optional fields:
CheckForPostTraverse: true- If your operator can have another directly after it without the pipe character. Most of the time this is false.ToString: customToString- Custom string representation (rarely needed)
Step 3: Register the Operator in lexer_participle.go
Edit pkg/yqlib/lexer_participle.go to add the operator to the lexer rules:
- Use
simpleOp()for simple keyword patterns - Use object syntax for regex patterns or complex syntax
- Support optional characters with
_?and aliases with|
See existing operators in lexer_participle.go for pattern examples.
Step 4: Create Tests (Mandatory)
Create pkg/yqlib/operator_<type>_test.go using the expressionScenario pattern:
- Define test scenarios with
description,document,expression, andexpectedfields expectedis a slice of strings showing output format:"D<doc>, P[<path>], (<tag>)::<value>\n"- Set
skipDoc: truefor edge cases you don't want in generated documentation - Include
subdescriptionfor longer test names - Set
expectedErrorif testing error cases - Create main test function that iterates over scenarios
- The main test function should use
documentScenariosto ensure testcase documentation is generated.
Test coverage must include:
- Basic data types and nested structures
- Edge cases (empty inputs, special characters, type errors)
- Multiple outputs if applicable
- Format-specific features
See operator_reverse_test.go for a simple example and operator_keys_test.go for complex cases.
Step 5: Create Documentation Header
Create pkg/yqlib/doc/operators/headers/<type>.md:
- Use the exact operator name as the title
- Include a concise 1-2 sentence summary
- Add additional context or examples if the operator is complex
See existing headers in doc/operators/headers/ for examples.
Working with Context and CandidateNode
Context Management
context.ChildContext(results)- Create child context with resultscontext.GetVariable("varName")- Get variables stored in contextcontext.SetVariable("varName", value)- Set variables in contextcontext.ReadOnlyClone()/context.WritableClone()- Clone a context, forcingDontAutoCreatetrue/false. Binary operators (multiply, add, subtract, equals, booleans, etc.) evaluate their operands viacrossFunction(d, context.ReadOnlyClone(), ...), so any nested expression (e.g.({} | .a.b += 3) *? .) runs withDontAutoCreate = trueeven though the outer/root context is normally writable.- Explicit-assignment invariant: any operator that resolves an assignment/style/attribute target (its LHS) must call
d.GetMatchingNodes(context.WritableClone(), expressionNode.LHS)for that lookup, not the raw incomingcontext. Otherwise the target path silently fails to autovivify when the operator is used as an operand of a binary op. All of theassignableOppairs (style=,tag=/type=,anchor=,alias=) plusassignCommentsOperator(line_comment=,head_comment=,foot_comment=,comments=) share this exact pattern — seeassignUpdateOperator,assignAttributesOperator(operator_assign.go),assignStyleOperator(operator_style.go),assignTagOperator(operator_tag.go),assignAnchorOperator/assignAliasOperator(operator_anchors_aliases.go),assignCommentsOperator(operator_comments.go), andcompoundAssignFunction(operators.go). When fixing one, grep forGetMatchingNodes(context, expressionNode.LHS)acrosspkg/yqlibto find the rest of the family. - Testing this invariant: a test at the root/top-level context won't expose a missing
WritableClone()fix, because the root context is writable by default. Wrap the assignment as an operand of a binary operator instead, e.g.({} | .a.b style="double") * ., and verify the test fails with the fix reverted (git stashthe fix, rerun,git stash pop) before trusting it as a regression test. - Merge quirk: in
CandidateNode.UpdateFrom(candidate_node.go), a target'sStyleis only preserved through a merge if its tag matches the source's tag and its ownStylewas already non-zero. If the tag changes (e.g. merging a!!nullplaceholder into a!!strvalue), the target forcibly adopts the source'sStyle/Value, so you can't preserve a real value while only "borrowing" a style from a differently-typed node during a merge.
CandidateNode Operations
candidate.CreateReplacement(ScalarNode, "!!str", stringValue)- Create a replacement nodecandidate.CreateReplacementWithComments(SequenceNode, "!!seq", candidate.Style)- With style preservedcandidate.Kind- The node type (ScalarNode, SequenceNode, MappingNode)candidate.Tag- The YAML tag (!!str, !!int, etc.)candidate.Value- The scalar value (for ScalarNode only)candidate.Content- Child nodes (for SequenceNode and MappingNode)candidate.guessTagFromCustomType()- Infer the tag from Go typecandidate.AsList()- Convert to a list representation
Key Points
✅ DO:
- Implement the operator handler with the correct signature
- Register in
operation.gowith appropriate precedence - Add the lexer pattern in
lexer_participle.go - Write comprehensive tests covering normal and edge cases
- Create a documentation header in
doc/operators/headers/ - Use
Context.ChildContext()for proper context threading - Use
context.WritableClone()when looking up an assignment/style/attribute target (LHS), so autovivification still works when nested inside a binary operator's read-only operand - Handle all node types gracefully
- Return meaningful error messages
❌ DON'T:
- Modify
candidate_node.go(operators shouldn't need this) - Modify core navigation or evaluation logic
- Bypass the handler function pattern
- Add format-specific or operator-specific fields to
CandidateNode - Skip tests or documentation
Examples
Refer to existing operator implementations for patterns:
- No-argument operator:
operator_reverse.go- Processes arrays/sequences - Single-argument operator:
operator_map.go- Takes an expression argument - Complex multi-output:
operator_keys.go- Produces multiple results - With preferences:
operator_to_number.go- Configuration options - Error handling:
operator_error.go- Control flow with errors - String operations:
operator_strings.go- Multiple related operators
Testing Patterns
Refer to existing test files for specific patterns:
- Basic expression tests in
operator_reverse_test.go - Multi-output tests in
operator_keys_test.go - Error handling tests in
operator_error_test.go - Tests with
skipDocflag to exclude from generated documentation
Common Patterns
Refer to existing operator implementations for these patterns:
- Simple transformation: see
operator_reverse.go - Type checking: see
operator_error.go - Working with arguments: see
operator_map.go - Post-traversal operators: see
operator_with.go
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

