Skip to content

zawatton/anvil-pkg

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

anvil-pkg — Elisp DSL package manager for anvil, backed by Nix store

What is anvil-pkg?

anvil-pkg is a package manager you configure in Emacs Lisp, backed by the Nix store. It is a sub-system of anvil.el (the AI tool workbench), exposing package install / search / definition both as Elisp API and as Claude Code MCP tools.

The same idea as GNU Guix (Scheme over the Nix store) — but in Emacs Lisp, integrated with the anvil + NeLisp tool ecosystem so that AI agents can install anything by writing one Lisp form.

Status

Phase 1+2+3 SHIPPED + REAL-NIX VERIFIED (2026-04-27) — Full DSL stack on top of the Nix store with multiple fetcher and build-system support.

  • ERT 21/21 PASS (mocked, runs without a nix binary).
  • Real-nix smoke test executed end-to-end in a nixos/nix:2.34 rootless podman container with Emacs 30.2:
    • (pkg-install "hello") installed GNU Hello into an isolated profile and the binary ran.
    • (pkg-list) reflected the install.
    • (pkg-search "hello") returned 30 hits.
    • (pkg-define) + (pkg-install 'sym) reached the Nix daemon through the generated flake.nix and surfaced the expected hash-mismatch error from a placeholder.
  • Generated flake.nix validated by Nix as well-formed.

Public Elisp API uses the short pkg- prefix:

  • pkg-install / pkg-search / pkg-list — Phase 1
  • pkg-define macro — Phase 2
  • github-fetch / git-fetch sources, rust / python / go build systems — Phase 3

Long-form aliases (anvil-pkg-install etc.) are provided via defalias for Emacs-prefix purists.

Phase 4 (profile generation rollback + Nix 2.34’s install=→=add deprecation follow-up) is the next milestone. Phase 4-A is also the current async-installer replacement candidate for Nix-backed Emacs package installs; see docs/design/02-dsl.org.

Why?

AI agents need a single DSL for tool installation

Claude Code (and similar agents) already speak anvil’s MCP tools fluently. Adding anvil-pkg-install to that surface means an agent can provision its own dependencies — install ripgrep, jq, language servers, anything in nixpkgs — by emitting one Elisp form, without shelling out to a different package manager per OS.

Nix solves the hard problems; we add the DSL

Nix already handles dependency closures, content-addressed storage, sandboxed builds, and binary caches. anvil-pkg does not re-implement any of that. It provides:

  1. An Elisp DSL (anvil-pkg-define, anvil-pkg-install, …) that feels native to Emacs / NeLisp users.
  2. A wrapper that translates that DSL to Nix expressions / nix profile commands.
  3. A fallback path for Git-host packages not in nixpkgs (private repos, in-development MCP servers), inspired by async-installer.

Plain Elisp, no macro DSL forced on you

Like async-installer, anvil-pkg prefers setq / defun / anvil-pkg-define over a baroque use-package-style macro. Reading your config should not require learning a new macro language.

Design overview

┌─────────────────────────────────────────────────────────────┐
│  Elisp DSL                                                   │
│  (anvil-pkg-install "ripgrep")                               │
│  (anvil-pkg-define my-tool :src (github "...") :build ...)   │
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  anvil-pkg core (this repo)                                  │
│   - DSL parser / form → backend dispatch                     │
│   - manifest / generation tracking                           │
│   - MCP tool surface: anvil_pkg_install / search / list ...  │
└─────────────────────────────────────────────────────────────┘
               │                         │
               ▼                         ▼
┌─────────────────────────────┐   ┌─────────────────────────────┐
│  Nix backend (primary)       │   │  Git backend (fallback)     │
│  nix profile install ...     │   │  inspired by async-installer│
│  nix-eval / flake / nixpkgs  │   │  for non-nixpkgs repos      │
└─────────────────────────────┘   └─────────────────────────────┘

Phase plan

PhaseScopeStatus
1nix profile shell-out wrapper. 3 MCP tools. install/search/listSHIPPED
2pkg-define DSL macro + flake.nix generation + symbol installSHIPPED
3github-fetch / git-fetch sources + rust / python / go bsSHIPPED
4Profile / generation management (rollback wrap)next
4-Aemacs-package build-system + post-install load-pathnext
5+Optional: independent package server, OS angle (research)research

See docs/design/01-overview.org for the full Phase 1 contract.

Install

git clone https://github.com/zawatton/anvil-pkg ~/anvil-pkg

In your Emacs init:

(add-to-list 'load-path "~/anvil-pkg")
(require 'anvil-pkg)
(require 'anvil-pkg-dsl)        ; Phase 2 — pkg-define macro
;; To register pkg-* MCP tools (requires anvil.el loaded):
(anvil-pkg-enable)

Examples

;; Phase 1 — install a package straight from nixpkgs
(pkg-install "ripgrep")
(pkg-search "rust")
(pkg-list)

;; Phase 2 — declare a custom package and install by symbol
(pkg-define my-rg
  (version "13.0.0")
  (source (url-fetch "https://github.com/BurntSushi/ripgrep/archive/13.0.0.tar.gz"
                     :sha256 "sha256-..."))
  (build-system stdenv)
  (inputs (list pkg-config openssl))
  (install-phase "make install PREFIX=$out"))

(pkg-install 'my-rg)            ; goes through generated flake.nix

;; Phase 3 — github-fetch + rust build-system
(pkg-define my-rust-tool
  (version "1.0.0")
  (source (github-fetch :owner "user" :repo "tool"
                        :rev "v1.0.0" :sha256 "sha256-..."))
  (build-system (rust :cargo-sha256 "sha256-..."))
  (inputs (list openssl)))

;; Phase 3 — git-fetch + go build-system (vendored deps)
(pkg-define my-go-tool
  (version "0.3.0"  )
  (source (git-fetch :url "https://example.com/tool.git"
                     :rev "v0.3.0" :sha256 "sha256-..."))
  (build-system (go)))           ; vendorHash = null (vendored)

;; Phase 4-A — emacs-package build-system + post-install require
(pkg-define dash-test
  (version "2.20.0")
  (source (github-fetch :owner "magnars" :repo "dash.el"
                        :rev "2.20.0" :sha256 "sha256-..."))
  (build-system emacs-package))

(pkg-install 'dash-test :require 'dash)

CLI sub-command (anvil pkg install ...) lands in anvil.el via a separate PR; until then call the Elisp API directly.

Run the test suite

make test       # 6 ERT tests, no nix binary required (all mocked)
make compile    # byte-compile, warnings-as-errors

Requirements (planned)

  • Emacs 29+ (anvil runtime requirement)
  • Nix 2.18+ with flakes enabled (Phase 1 backend)
  • anvil.el loaded

Naming

anvil-pkg follows the existing anvil sub-module pattern (anvil-http, anvil-state, anvil-defs, anvil-org-index, …). CLI is exposed as a bin/anvil sub-command (anvil pkg install ...) so the user only ever has one binary in $PATH.

If the project later outgrows anvil’s ecosystem, a standalone brand rename remains an option. Phase 1-3 do not require it.

License

GPL-3.0-or-later. Same as anvil.el and NeLisp.

About

Elisp DSL package manager backed by the Nix store. AI-native MCP surface, anvil ecosystem sub-module.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors