A Python library and tools for parsing, manipulating, and reconstructing Nix source code.
Started during SaltSprint 2025, Nix-manipulator aims to fill the absence of tools for easily updating and editing Nix code. Popular tools such as nix-update rely on simple string replacement or regular expressions for updating Nix code.
- Ease of use - Simple CLI and API for common operations.
- High-level abstractions make manipulating expressions easy.
- Preserving formatting and comments in code that respects RFC-0166.
- Preserving eccentric formatting that does not respect RFC-0166 and would add unnecessary complexity.
- Updating values in Nix code by hand, scripts, pipelines, and frameworks.
- Writing refactoring tools.
- Interactive modifications from a REPL.
Nix-manipulator leverages tree-sitter , a multilingual concrete-syntax AST, and its Nix grammar tree-sitter-nix.
The project is still is in alpha state:
- All Nix syntax is supported
- Test-driven approach prevents regressions
- All Nix files from nixpkgs can be parsed and reproduced*
- CLI and API are still evolving and subject to change
_* with the excption of some expressions that are not RFC-compliant.
Intermediate Nix users and developers working with Nix code manipulation.
Nix-manipulator provides a command-line interface for common operations:
Full docs (MkDocs + Material) live in docs/ and can be built with:
nix-build ./docsServe docs locally:
nix-shell --run "mkdocs serve"Paths that start with @ target let … in scopes: @name edits the innermost scope (creating one if missing), and each extra @ walks outward; @foo.bar applies a dot-path inside that scope. Empty scopes are pruned when their last binding is removed.
Set a value in a Nix file
nima set -f package.nix version '"1.2.3"'Set a boolean value
nima set -f package.nix doCheck trueSet or update a binding in a scope (auto-creates the innermost scope)
nima set -f package.nix @bar 2Update an outer scope binding (all scope layers must already exist)
nima set -f package.nix @@a 10Set a nested binding inside a scope
nima set -f package.nix @foo.bar '"nested"'Remove an attribute
nima rm -f package.nix doCheckRemove a scoped binding (pruning the let if it becomes empty)
nima rm -f package.nix @barTest/validate that a Nix file can be parsed
nima test -f package.nixInstall using Nix:
nix develop git+https://codeberg.org/hoh/nix-manipulator.gitℹ️Recent versions are not available on PyPI due to
tree-sitter-nixnot being maintained there.
Run the small suite (lint + type-check + pytest -m "not nixpkgs") via Nix:
nix-buildRun nixpkgs-marked tests (requires a nixpkgs checkout or NIXPKGS_PATH):
nix-shell --run "pytest -v -m nixpkgs"The project targets Python 3.13 and ulterior. Previous versions are not supported.
- Canonical repo (Codeberg): https://codeberg.org/hoh/nix-manipulator
- GitHub mirror: https://github.com/hoh/nix-manipulator
See CONTRIBUTING.md for development guidelines and CODE_OF_CONDUCT.md for community standards.
Licensed under the GNU Lesser General Public License v3.0 only (LGPL-3.0-only).