Refactor high-complexity React components in Dify frontend. Use when `pnpm analyze-component...
npx skills add YPares/rigup.nix
Or install specific skill: npx add-skill https://github.com/YPares/rigup.nix/tree/main/riglets/agent-rig-system
# Description
AI agents and Nix: parametrable skills/instructions and tools, packaged together in a reproducible and modular fashion
# SKILL.md
Agent Rig System
Overview
A rig is a project-scoped collection of riglets that provide knowledge and tools for AI agents.
Core Concepts
Riglet
A riglet is executable knowledge packaged with its dependencies, as a Nix module:
- Metadata: When should this riglet be used, is it production-ready or experimental, etc.
- Knowledge: SKILL.md + detailed
references/*.mdfiles documenting processes and recipes - Tools: Nix packages needed to execute those recipes
- Configuration: Settings to adapt tools' behaviour to project context
Rig
A project-level structure that declares which riglets are active:
- Uses
buildRigto compose riglet modules - Builds combined tool environment declaratively
- Exposes riglets' tools and documentation
rigup
A Nix library and CLI tool: http://github.com/YPares/rigup.nix
rigup Nix library
Main functions:
buildRig: evaluates riglet modules and ensures they comply with the riglet schema used by rigup. Returns the rig as an attrset:{ toolRoot = <derivation>; meta = { <riglet> = {...}; }; docAttrs = { <riglet> = <derivation>; }; docRoot = <derivation>; home = <derivation>; shell = <derivation>; }resolveProject: inspects theriglets/folder of a project and itsrigup.tomlto find out which riglets and rigs it defines. It callsbuildRigfor each rig in therigup.tomlgenManifest: generates a markdown+XML manifest file describing the contents of a rig, primarily for AI agent's consumptionmkRiglib: creates a set of utility functions to be used to define riglet Nix modules
Defined in {{repoRoot}}/lib/default.nix.
rigup CLI tool
A Rust app. It provides convenient access to rig outputs, via commands like rigup build and rigup shell. This tool is meant for the user primarily. Agents should not have to call it directly.
Defined in {{repoRoot}}/packages/rigup
Riglet Structure
Riglets are Nix modules with access to riglib helpers
Example Riglet
# First argument: the defining flake's `self`
# Gives access to `self.inputs.*` and `self.riglets.*`
# Use `_:` if you don't need it
self:
# Second argument: module args from evalModules
{ config, pkgs, lib, riglib, ... }: {
# Riglet-specific options (optional)
options.myRiglet = {
myOption = lib.mkOption {
type = lib.types.str;
description = "Example option";
};
};
# Riglet definition
config.riglets.my-riglet = {
# Dependency relationship/Inheritance mechanism: if B imports A, then whenever B is included in a rig, A will automatically be included too
imports = [ self.riglets.base-riglet self.inputs.foo.riglets.bar ... ];
# Tools can be:
# - Nix packages: pkgs.jujutsu, pkgs.git, etc.
# - Script paths: ./scripts/my-script (auto-wrapped as executables)
tools = [
pkgs.tool1
pkgs.tool2
./scripts/helper-script # Becomes executable "helper-script"
];
# Metadata for discovery and context
meta = {
description = "What this riglet provides";
mainDocFile = "SKILL.md"; # Where to start reading the docs (SKILL.md by default)
intent = "cookbook"; # What the agent should expect from this riglet
whenToUse = [
# When the AI Agent should read/use this riglet's knowledge, recipes and tools
"Situation 1" # or
"Situation 2" # or
...
];
keywords = [ "keyword1" "keyword2" ];
status = "experimental"; # Maturity level
version = "x.y.z"; # Semantic version of riglet's interface (configuration + provided methods, procedures, docs...)
disclosure = lib.mkDefault "lazy" # How much to show about riglet in manifest
# mkDefault makes it possible for end users to override this in their rigup.toml
};
# Documentation file(s) (Skills pattern: SKILL.md + references/*.md)
docs = riglib.writeFileTree {
"SKILL.md" = ...; # A main documentation file
references = { # Optional. To add deeper knowledge about more specific topics, less common recipes, etc.
# SKILL.md MUST mention when each reference becomes relevant
"advanced.md" = ...;
"troubleshooting.md" = ...;
};
};
# Files can be defined either as inlined strings or nix file derivations/paths.
# Folders can be defined either as nested attrsets or nix folder derivations/paths,
# so if you have a ready to use folder you can do:
#docs = ./path/to/skill/folder;
# Configuration files (optional) for tools following the
# [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir/latest/)
configFiles = riglib.writeFileTree {
# Built from a Nix attrset
myapp."config.toml" = riglib.localTOML {
setting = "value";
};
# Read from existing file
myapp."stuff.json" = ./path/to/stuff.json;
# Inlined as plain text
myapp."script.sh" = ''
#!/bin/bash
echo hello
'';
};
# EXPERIMENTAL: Prompt commands (slash commands for harnesses like Claude Code)
# promptCommands.my-cmd = {
# template = "Do something specific with $ARGUMENTS";
# description = "What this command does";
# useSubAgent = false;
# };
# EXPERIMENTAL: MCP (Model Context Protocol) servers
# mcpServers.my-server = {
# command = pkgs.my-mcp-server; # For stdio transport
# transport = "stdio";
# };
};
}
The full Nix module schema of a riglet is defined in {{repoRoot}}/lib/rigletSchema.nix.
Examples of actual riglets: {{repoRoot}}/riglets.
Metadata
When defining a riglet, the meta section specifies its purpose, maturity, and visibility. See references/metadata-guide.md for comprehensive details on:
- meta.intent - Primary focus (base, sourcebook, toolbox, cookbook, playbook)
- meta.status - Maturity level (stable, experimental, draft, deprecated, example)
- meta.version - Semantic versioning of the riglet's interface
- meta.broken - Temporary non-functional state flag
- meta.disclosure - Visibility control (none, lazy, shallow-toc, deep-toc, eager)
Implementation Utilities
See references/riglib-utilities.md for details on helper functions available via riglib:
- riglib.writeFileTree - Convert nested attrsets to directory trees
- riglib.useScriptFolder - Convert folder of scripts into wrapped tool packages
riglib is defined in {{repoRoot}}/lib/mkRiglib.nix
Experimental Features
WARNING: These features are still experimental and their schema may change.
Prompt Commands
Riglets can define reusable prompt templates (slash commands) for agent harnesses like Claude Code:
promptCommands.analyze = {
template = "Analyze $1 for potential issues";
description = "Perform code analysis";
useSubAgent = false; # Whether to run in a sub-agent
};
Templates use standard Claude command syntax: $ARGUMENTS for all args, or $1, $2, etc. for specific positional arguments.
MCP Servers
Riglets can provide MCP (Model Context Protocol) servers to extend agent capabilities:
mcpServers.my-tools = {
command = pkgs.my-mcp-server; # Package that starts the server
transport = "stdio"; # stdio, sse, or http
};
Cross-Riglet/Flake Interaction
Advanced patterns for composing riglets together and sharing configuration. See references/advanced-patterns.md for:
- Sharing configuration via
config - Dependencies and inheritance via
imports - Using packages from external flakes
Defining Rigs in Projects
Recommended: Use rigup.toml
Add a rigup.toml file to your project root:
[rigs.default.riglets]
self = ["my-riglet"]
rigup = ["jj-basics"]
[rigs.default.config.agent.identity]
name = "Alice"
email = "[email protected]"
Then use rigup.lib.resolveProject in your flake.nix:
{
inputs.rigup.url = "github:YPares/rigup.nix";
outputs = { self, rigup, ... }@inputs:
# Using the rigup flake directly as a function is equivalent to calling
# `rigup.lib.resolveProject`, as `rigup` defines the __functor attr.
#
# rigup follows the same pattern as the 'blueprint' flake (https://github.com/numtide/blueprint):
# - exposes one main "entrypoint" function, callable through the flake "object" itself
# - inspects user flake's inputs and repository's contents
# - constructs (part of) user flake's outputs
rigup {
inherit inputs;
# A unique name, used in error messages, to make it more explicit where mentioned riglets come from
projectUri = "some-username/some-project-name";
}
}
Advanced: Directly use buildRig for complex config
For config not representable in TOML:
{
inputs.rigup.url = "github:YPares/rigup.nix";
outputs = { self, rigup, nixpkgs, ... }@inputs:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in
pkgs.lib.recursiveUpdate # merges both recursively, second arg taking precedence
(rigup.lib.resolveProject {
inherit inputs;
projectUri = "...";
})
{
rigs.${system}.custom = rigup.lib.buildRig {
name = "my-custom-rig";
inherit pkgs;
modules = [
rigup.riglets.jj-basics
{
# Complex Nix expressions
agent.complexOption = lib.mkIf condition value;
}
];
};
};
}
resolveProject outputs
riglets.<riglet>- Auto-discovered riglet modulesrigs.<system>.<rig>- Output ofbuildRigfor each discovered rig:toolRoot- Folder derivation. Tools combined via nixpkgsbuildEnvfunction (bin/, lib/, share/, etc.) and wrapped (when needed) to fix their XDG_CONFIG_HOMEconfigRoot- Folder derivation. The combined config files for the whole rig, with config files for all rig's wrapped tools.meta.<riglet>- Attrset. Per-riglet metadata, as defined by the riglet's moduledocAttrs.<riglet>- Folder derivation. Per-riglet documentation folder derivationsdocRoot- Folder derivation. Combined derivation with docs for all riglets (one subfolder for each)home- Folder derivation. All-in-one directory for the rig: RIG.md manifest + .local/ + docs/ + .config/ foldersshell- Shell derivation (viapkgs.mkShell) exposing ready-to-use RIG_MANIFEST and PATH env varsextend- Nix function. Adds riglets to a pre-existing rig: takes{newName, extraModules}and returns a new rigmanifest- A manifest for this rig, overridable with options to shorten included paths to avoid repeatedly including long explicit paths into the Nix store
resolveProject is defined in {{repoRoot}}/lib/resolveProject.nix.
Using a Rig
The user decides how they and their agent should use the rig: either via its shell, home or entrypoint output derivations.
In any case, the agent's focus should be is the RIG.md manifest file. This file lists all available riglets with:
- Name
- Description
- When to use each riglet
- Keywords for searching
- Documentation paths
Agents should read this file first to understand available capabilities.
buildRig output derivations
buildRig outputs a Nix attrset ("object") that notably contains several "all-in-one" derivations which all allow an AI agent to access the rig's tools and documentation.
Which derivation to use depends on what is the most convenient given the user's setup.
This section lists how and when to use each.
buildRig is defined in {{repoRoot}}/lib/buildRig.nix
shell output
The AI agent runs in a subshell: a $RIG_MANIFEST env var is set that contains the path to the RIG.md manifest the agent should read.
Also, $PATH is already properly set up by the subshell so all tools are readily usable.
# Start a rig as a sub-shell (the user should do that)
rigup shell ".#<rig>" [-c <command>...] # Does `nix develop ".#rigs.<system>.<rig>.shell" [-c <command>...]`
# Read the rig manifest
cat $RIG_MANIFEST
Advantages of using shell:
- No extra setup needed: a single command gets everything ready to use
- No risk of using an incorrect tool or config file if the agent misses a step
- Convenient to use when AI agent runs inside a terminal application (like claude-code)
home output
The AI agent reads from a complete locally-symlinked "home-like" folder.
The RIG.md manifest and an activate.sh script will be added at the root of this folder.
The activate.sh, once sourced, provides the needed PATH.
# Build complete home directory with tools + docs + config as a `.rigup/<rig>` folder at the top-level of the project (the user should do that)
rigup build ".#<rig>" # Does `nix build ".#rigs.<system>.<rig>.home"`
# Read the rig manifest to see what's available
cat .rigup/<rig>/RIG.md
# Source the activation script to use the tools
source .rigup/<rig>/activate.sh && jj --version && other-tool ...
# Read documentation (paths shown in RIG.md)
ls .rigup/<rig>/docs/
cat .rigup/<rig>/docs/<riglet>/SKILL.md
Advantages of using home:
- Rig can be rebuilt without having to restart the agent's harness: home folder contents are just symlinks that can be updated, paths remain valid
- Manifest file is right next to doc files: can refer to them via short and simple relative paths
- More convenient to use in contexts where setting up env vars is impractical (e.g. AI agent running inside an IDE, like Cursor)
entrypoint output
The entrypoint output is special in that it does not exist unless some riglet sets it, by defining config.entrypoint.
It is mainly used to provide direct integration with common coding agent harnesses.
Similar to home and shell, entrypoint packages the whole rig as a Nix derivation, but this time as a wrapper shell script that starts the harness with the proper config files and CLI args.
rigup run <flake>#<rig> executes a rig's entrypoint.
Internally it just runs nix run <flake>#rigs.<system>.<rig>.entrypoint.
Claude Code integration is currently available via the claude-code riglet.
See references/harness-integration.md for more details.
Advantages of using entrypoint:
- More direct integration with the harness when such integration exists
More efficient Markdown reading: extract-md-toc
This riglet (agent-rig-system) comes with extract-md-toc. This is the tool that renders the inline table of contents of the rig manifests (for riglets with disclosure = "{shallow,deep}-toc";).
It can also be used to extract a similar ToC out of ANY Markdown file: e.g. extract-md-toc foo.md --max-level 3 will show all headers from # to ### with their line numbers.
It can also read from stdin: extract-md-toc - < foo.md
Defined in {{repoRoot}}/packages/extract-md-toc
Adding Riglets to a Rig
In the project defining the riglets OR in another one importing it as an input flake, either add riglets and their config to the rigs defined in the top-level rigup.toml file, or directly edit the flake.nix if more advanced configuration is needed.
In both cases, the flake should call rigup.lib.resolveProject (or just rigup, which contains a __functor attr which defers to resolveProject) to discover rigs and riglets, and the rigs should be under the rigs.<system>.<rig-name> output.
Creating New Riglets
In some project:
- Create
riglets/my-riglet.nix, orriglets/my-riglet/default.nixfor riglets with multiple supporting files - Add the needed tools, documentation, metadata
- Define options (schema) and config (values) in this module
- Ensure the project has a top-level
flake.nixthat usesrigup.lib.resolveProjectas mentioned above, so all the riglets will be exposed by the flake
If your rig contains riglet-creator, consult it for more detailed information about writing proper riglets.
Design Principles
- Knowledge-first: Docs are the payload, tools are dependencies
- Declarative: Configuration via Nix module options
- Composable: Riglets build on each other
- Reproducible: Nix ensures consistent tool versions
# README.md

Self-Contained & Modular AI Agent Config
rigup is a Nix-based system for packaging knowledge, instructions and skills for your AI agent with the tools and config needed to execute them.
A riglet is executable knowledge:
- metadata to indicate to your agent what this riglet is for and when it is useful to consult it
- a set of operations, instructions, processes, a.k.a a new Skill for your agent
- the tools (nix packages) needed to execute these instructions
- the configuration for these tools (if any)
By combining the relevant riglets together, you build your agent's rig: the tools it needs to work on your project, and the operational knowledge it needs to use those tools properly and efficiently.
By default, the documentation of a riglet is lazily loaded (like Skills are): at startup your agent is fed a manifest, only showing for each riglet a quick description and a set of use cases.
It will then go on reading it when it needs to, or is prompted to.
However, rigup gives you more control than Skills traditionally do in terms of when your agent should be exposed to a riglet's content.
For instance, a fundamental riglet can be set to have an eager disclosure (forcing the agent to see the whole SKILL.md at startup), while another one can have a shallow-toc disclosure (the agent will see a table of contents of the riglet's main SKILL.md).
rigup has a "knowledge-first" design: documentation is the payload, tools are dependencies. The "main entrance" to the rig will be the RIG.md manifest that rigup will generate for your AI agent to read, and through which it will discover all the available tools, knowledge and processes to follow.
In short, rigup is parametrable agent skills + lightweight home management for your agent, with a high-level of interoperability with Claude Skills and Claude plugin marketplaces, so reusing published Skills as basis for riglets is made as easy as possible.
Quick start
First, install the rigup CLI tool:
nix profile add github:YPares/rigup.nix#rigup
Cached binaries should be provided by garnix.io (for both x86_64 Linux and ARM64 OSX). The first time, nix should prompt you about accepting the flake config to activate garnix as a substituter.
NOTE: On older Nix versions, "add" is "install" instead.
If already installed, update with:
nix profile upgrade rigup
This CLI tool makes it easier to:
- get information about riglets and rigs exposed by a flake and its inputs (
rigup show) - show the contents of a rig, including which options can be used to customize it with their description, type, default and current values (
rigup inspect) - directly start a coding agent harness (
rigup runor justrigup) if the rig contains a riglet that provides an entrypoint. Entrypoints act as connectors, wrapping a harness executable to start it with the appropriate config - build a full rig as a folder of symlinks (
rigup build) - use a rig as a subshell, with
$PATHand$RIG_MANIFEST(path to theRIG.md) set up (rigup shell)
(Note all these commands are just wrappers, provided for convenience, around the rigup Nix library. So you may still do everything with the usual nix {build,develop,run} commands if you prefer)
You can then create a new project from the templates provided by this repository, or even directly build the example rig defined here.
Use a rig from this project (or any other repo using rigup)
This project defines example riglets, and an example rig combining them.
# Show the riglets and rigs exposed by a remote flake
rigup show github:YPares/rigup.nix
# Show all metadata about riglets and each rig's contents
rigup show --detailed github:YPares/agent-skills # A Skills repo that also packages them via rigup
# Directly build a rig from a remote flake
rigup build github:YPares/rigup.nix#example-rig # this builds <flake>#rigs.<system>.example-rig.home and outputs a symlink
# Directly open a rig from a remote flake in a subshell
rigup shell github:YPares/rigup.nix#example-rig
# Directly run a rig's entrypoint (if it has any)
rigup run github:YPares/agent-skills#claude-rig
# Then in Claude Code: Ctrl+o to show details, to see the RIG.md manifest that Claude was prompted with via a startup hook
rigup run github:YPares/agent-skills#copilot-rig
# Almost the same rig, but using `copilot-cli` as an entrypoint
Create a new project
Create it from one of the templates in this repo:
rigup new foo [-t minimal]
# Use 'minimal' template for a project which should not define any local riglet
cd foo
This creates a ./foo folder, initializes it as a git repo, calls nix flake init to create a basic project structure with a flake.nix and example riglets and rigs, adds all the files to git tracking (important), and finally runs nix flake check on the result to make sure everything is in order.
# List available riglets from flake's self and inputs
rigup show --detailed --with-inputs
# Build your rig
rigup build ".#default"
# Explore the rig
cat .rigup/default/RIG.md
# Or open the rig as a sub-shell instead of creating a local symlink
rigup shell ".#default"
Edit riglets/*.nix and rigup.toml to customize the project.
The default template includes some example riglets and shows how rigup supports using a Claude Marketplace as a direct input, to import pre-existing Skills and use them as a basis for riglets.
Deeper dive
This section covers the general workflow of defining and editing riglets and rigs.
Creating a riglet
Riglets are a simple use case of Nix modules.
Concretely, this means a riglet (in its most general form) is a (config, dependencies) -> data Nix function, where:
configis the final config of the rig which the riglet is part of,datais a dictionary-like structure ("attribute set" in Nix lingo) providing nested fields that will themselves contribute to the final aggregated config
Create a riglets/ folder at the root of your project.
Then add to it a <riglet-name>.nix file:
# riglets/my-riglet.nix
# First argument: the defining flake's `self` - gives access to:
# - `self.inputs.*` for external package dependencies
# - `self.riglets.*` for inter-riglet imports
# Just use `_` if you don't need it
_:
# Second argument: module args constructed by the _user_ of the riglet, when the full rig is built
# - config is the final aggregated config of the rig using my-riglet,
# - pkgs is your usual imported nixpkgs,
# - system is just pkgs.stdenv.hostPlatform.system, for quicker access
# - riglib is injected by rigup, and contains utility functions to build riglets
{ config, pkgs, system, riglib, ... }: {
# (Optional) Which riglets to depend on (whether from self or other flakes)
# If this riglet is included in a rig, ALL the riglets it imports will automatically be included as well
imports = [ ... ];
# Each riglet must declare itself under config.riglets.<riglet-name>
# <riglet-name> MUST be the SAME as the file name, minus the .nix extension
config.riglets.my-riglet = {
# (Optional) The tools needed by this riglet
tools = [
pkgs.mytool
./path/to/some/script.sh
];
# The metadata that will enable your agent to know what this riglet
# provides and when it should be consulted
meta = {
intent = "cookbook";
# 'base', 'sourcebook', 'toolbox', 'cookbook', or 'playbook':
# what should the agent expect from this riglet: general knowledge
# that may come useful vs. highly specific procedure(s) to follow
description = "What this provides";
whenToUse = [
"Need to know about X"
"Want to achieve Y"
"Have problems with Z"
...
];
keywords = [ "search" "terms" ];
status = "draft";
version = "0.1.0";
};
# The Skill part of the riglet. It is a file hierarchy that should contain a
# SKILL.md entry right under the root
docs = riglib.writeFileTree {
# Use inline strings...
"SKILL.md" = ''
# My Riglet Documentation
...
For more advanced cases, see references/advanced-cases.md
'';
references = {
# ...or local file paths...
"advanced-cases.md" = ./path/to/advanced-cases.md;
# ...or derivations that build a file
"foo.md" = pkgs.writeText "foo.md" (mkFooContents x y z t);
};
};
# Alternatively, if you already have a folder that follows the Agent Skill convention,
# (SKILL.md at the top and references/*.md files), you can directly reuse it:
#docs = ./path/to/skill/folder;
# (Optional) The configuration that the tools should use.
# This will go under `.config` in the final "home directory" of the rig, but only
# for consultation purposes: tools are by default *wrapped* to see a XDG_CONFIG_HOME that
# always points to this joint config folder. This way we can separate tools between those
# that need custom config and those that will just "inherit" the pre-existing user config
configFiles = riglib.writeFileTree {
# Will generate `.config/mytool/config.toml`
mytool."config.toml" = ''
setting = "value"
'';
};
};
}
...or just ask your agent to do it! :) rigup.nix comes with the riglet-creator riglet that teaches your agent to write riglets and... and, ahem, yes that's becoming very meta, sorry about that.
Just as with regular Agent Skills, the point of separating the docs into several files
is to allow progressive disclosure.
When using AI Agents, this is important because context is on a budget, and agents should not have to read more documentation than they need to complete a task.
Alternative structure
Instead of riglets/<riglet-name>.nix, you can define your riglet as riglets/<riglet-name>/default.nix to add supporting files next to it:
riglets/
βββ simple-riglet.nix # Single-file riglet
βββ complex-riglet/ # Directory-based riglet
βββ default.nix # Main riglet definition
βββ SKILL.md # Referenced in default.nix via ./SKILL.md
βββ references/
βββ advanced.md # Referenced in default.nix via ./references/advanced.md
This is useful to break up a riglet into various Nix or raw text files to make it more manageable.
rigup will discover and treat both layouts identically.
Inter-riglet dependencies
If a riglet depends on another (for instance because it builds on its concepts or requires its tools or configuration), use imports with self.riglets.*:
# riglets/advanced-riglet.nix
self:
{ riglib, ... }: {
# Import the base riglet - if both are added to a rig, evalModules deduplicates
imports = [ self.riglets.base-riglet ];
config.riglets.advanced-riglet = {
# ...
};
}
Important: Always use self.riglets.* for imports, never path-based imports like ./base-riglet.nix. The self.riglets.* form ensures proper deduplication when the same riglet is included both directly and via imports.
For dependencies on riglets from external flakes:
self:
{ riglib, ... }: {
imports = [ self.inputs.other-flake.riglets.some-riglet ];
# ...
}
Using external packages
Riglets can access packages from external flakes via self.inputs:
self:
{ pkgs, system, riglib, ... }: {
config.riglets.my-riglet = {
tools =
let someFlakePkgs = self.inputs.some-flake.packages.${system};
in [
someFlakePkgs.default
pkgs.git
];
};
}
This lets riglets bundle their own dependencies without requiring consumers to know about them.
Creating a rig
Simple option: rigup.toml
Define rigs in rigup.toml at the top of your project:
# Riglets to include in this rig, grouped by source
[rigs.default.riglets]
rigup = ["jj-basics", "typst-reporter"] # From the rigup flake input
self = ["my-local-riglet"] # From your riglets/ folder
# Configuration for the riglets used in this rig
[rigs.default.config.agent.identity]
name = "Alice" # This is used by both jj-basics & typst-reporter example riglets
email = "[email protected]"
Your flake.nix should be:
{
inputs.rigup.url = "github:YPares/rigup.nix";
outputs = { self, rigup, ... }@inputs:
rigup {
inherit inputs;
# A unique name to identify your project in error messages :
projectUri = "my-username/my-project";
# Add this if you want your rigs and/or riglets to be built as part of `nix flake check` :
#checkRigs = true;
#checkRiglets = true;
};
}
Finally, build the rig with:
rigup build ".#default"
...or enter it through a sub-shell with:
rigup shell ".#default"
NOTE: Just like nix build and nix develop target the "default" package or devShell when called without extra args, so do rigup build or rigup shell by targetting the rig named "default"
The riglets listed in your rigup.toml must match your flake inputs and what exists in your project. As a general case:
[rigs.my-rig.riglets]
some-flake = ["foo", "bar"]
means that your flake.nix has a some-flake input that exposes the riglets.foo and riglets.bar outputs.
self is just a special case of that, as every flake has an implicit self input which is the flake itself.
NOTE: The main reason to use a TOML file instead of always defining everything as Nix code is not just because TOML is (much) more well-known than Nix syntax.
It is mainly because pure data (that can already cover a large set of use cases) is easier to manipulate via CLI tools than Nix code.
Advanced option: combine with Nix
...that being said, building rigs directly in Nix (if you need the full Nix power to write your rig's configuration) is totally supported:
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
inputs.rigup.url = "github:YPares/rigup.nix";
inputs.foo.url = "github:bar/foo";
outputs = { self, rigup, nixpkgs, foo, ... }@inputs:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in
pkgs.lib.recursiveUpdate # merge both recursively, second arg taking precedence
(rigup {
inherit inputs;
projectUri = "...";
})
{
# Override or extend with custom rigs
rigs.${system}.custom = rigup.lib.buildRig {
inherit pkgs;
modules = [
self.riglets.aaa # Will use `$PROJECT_ROOT/riglets/{aaa.nix,aaa/default.nix}`
foo.riglets.bbb # Use external riglets defined by our inputs
foo.riglets.ccc
# Extra config is given in the form of a simple in-line Nix module,
# with or without `{ config, pkgs, riglib, ... }` arguments
{
config = {
# More advanced config, that e.g. requires direct access to some Nix functions,
# or even takes the form of Nix functions:
aaa-config.i-need-a-derivation = pkgs.someDerivationBuilder "..." "........";
bbbModuleOpts.conditions.is-x-y-pair-valid = x: y: x * y <= 42.420000000000001;
}
}
];
};
};
}
IMPORTANT: This makes the rigup.toml entirely optional, but it is still necessary to use rigup { inherit inputs; ... } to construct your flake's outputs.
This is so rigup can discover which riglets are defined in your $PROJECT_ROOT/riglets/ and correctly set up the riglets output of your flake.
As the above suggests, you can totally mix both options: define some simple rigs in the rigup.toml and some advanced ones in Nix code.
Although, prefer splitting you rigs' definitions in separate Nix files rather than declaring everything in your flake.nix, the example above is just for the sake of concision.
Riglet metadata
You may have noticed that riglets are a bit richer but also stricter in terms of metadata than Skills.
This is to enforce a bit more rigidly some good documentation-writing practices, and to make critical information more upfront, both for agents reading a manifest and for humans browsing riglets provided by some source with rigup show.
This contributes to the final documentation's quality without being too constraining.
This is why, contrary to Skills:
- a riglet must always have an
intent: why it was written, what type of information it was meant to provide, what should one expect to gain when reading it.
This notably guides the writing process, and limits the risk of riglets becoming a mash-up of contextual info, general guidelines and specific processes.
Don't forget riglets can depend on one another: if some background knowledge is needed to specify some processes that an agent should follow, then rather than trying to conflate everything into one big mess, make a firstsourcebookortoolboxriglet which can then be used as a dependency of subsequentplaybookriglets.
This way, the background information is shared, and will always come along with the more critical knowledge.
Smaller, more focused riglets means more reusability. - a riglet has a dedicated list of use cases (
whenToUse), which is not conflated with itsdescription. When a riglet should "activate", i.e. when an agent should consult and follow it, is probably the most critical piece of information about it, and thus warrants a dedicated field
This is notably useful when using agents to write riglets: they are not great at filling general fields like "description". They are better when their thinking is guided by a stricter schema.
But this enables riglets to perform things that Skills just cannot do:
- explicitly depend on one another (as we previously saw), to make sure critical background knowledge is always accessible if needed, and not simply assumed to be part of the LLM's training dataset.
- specify how they should be disclosed to an agent. Not all skills are equal, some contain critical information which you want to be very upfront about, others are "just in case" tips and tricks that should not overwhelm the agent. This is what the
meta.disclosurefield is about. - document ahead-of-time the quality of the knowledge they contain. Has it been battle tested quite a few times and proven useful? Could it still benefit from some proofreading or clarifications? This is what the
meta.statusfield is for. Agents, just like humans, can make much better decisions if they know how much trust they can put in an information source. It is always safer to progress on shaky ground when you know the ground is shaky. - provide a general framework for agent configuration besides just Markdown docs: riglets are Nix modules, and the main goal of Nix modules is to set up configuration in a modular and reusable fashion.
Riglets are no different, it's just that the biggest part of an AI agent's "config" happens to be human-readable documentation, which is why the focus so far has been on packaging docs.
But riglets can havemeta.intent = "base": this is for riglets that should remain transparent to the agent while still interacting with the rig, e.g. to provide tools or plain old JSON or TOML config files.
rigupenforces that thesebaseriglets are NOT disclosed to the agent.
This notably enables the most important tool of the rig, the agent's harness, to be expressed, packaged and used just like any other part of the rig.
Features
- One Config To Rule Them All:
rigupwill generate config for: - Claude Code
- OpenCode
- Cursor &
cursor-agent copilot-cli(althoughcopilot-clidoes not allow custom prompt commands, and MCP servers can only be set in user's config for now)- ...and possibly others to come (PRs are welcome!)
- Declarative composition: Module system for riglet interaction
- Data-driven config:
rigup.tomlfor CLI-manageable rigs - Auto-discovery: Riglets from
riglets/automatically exposed, as well as rigs fromrigup.toml - Compatibility with Skills and Claude Marketplaces:
- Directly import Claude Marketplace repos as inputs and use provided skills as basis for new riglets
- Riglet documentation follows as much as possible the Skills structure and conventions to facilitate reusability
- Plug-and-play integration with common agent harnesses: via riglets defining an
entrypointderivation - Type-checked metadata: Nix validates riglet structure
- Auto-generated manifests: RIG.md lists all capabilities
- Reproducible: Nix ensures consistent tool versions
- Self-documenting: Riglets like
agent-rig-system,riglet-creator, andnix-module-systemteach agents how the system works β so they can help you write Nix and extend your rig. Learn Nix as a side effect, with AI assistance.
Experimental Features
WARNING: These feature are on the main branch, but their API is subject to change, and they are not supported by all entrypoints yet:
mcpServers: riglets can add MCP servers to the rig, through the harness config generated byentrypointrigletspromptCommands: riglets can add custom "slash commands" (prompt templates) to the rig, through similar mechanism, for harnesses that support it (notably copilot-cli for now doesn't)
TODO
- Add
checksto riglets: automated and/or through-agent testing that a riglet is working as intended rigupCLI commands to conveniently view and set config values, likerigup config set <rig> foo.bar.qux <value>
Related projects
openskills: Universal Skill loader, following Claude Skills system and manifestllm-agents.nix: Numtide's flake packaging AI coding agents and development toolsagent-skills-nix: Nix lib to discover and bundle skills, with home-manager integration
License
MIT
Built with Nix β’ Formatting: nixfmt-rfc-style β’ Uses blueprint
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.