Claude Skill

agent-rig-system

Imported from ypares/rigup.nix/riglets/agent-rig-system.

LLM Mart · 0 points · 14 views 37 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download ypares-rigup.nix-riglets_agent-rig-system-d48c9c3.zip · 13 KB
Part of ypares/rigup.nix — 4 skills

Install

skills CLI npx skills add https://github.com/YPares/rigup.nix/tree/main/riglets/agent-rig-system
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install ypares-rigup-nix@llmmart
Git git clone https://github.com/YPares/rigup.nix.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole ypares/rigup.nix collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Agent Rig System

Overview

A rig is a collection of riglets that provide knowledge and tools for AI agents. Rigs and riglets are packaged as Nix flake outputs, so they can both be used inside the project defining them and by other projects depending on it.

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/*.md files 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 buildRig to 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 the riglets/ folder of a project and its rigup.toml to find out which riglets and rigs it defines. It calls buildRig for each rig in the rigup.toml
  • genManifest: generates a markdown+XML manifest file describing the contents of a rig, primarily for AI agent's consumption
  • mkRiglib: 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, and project scaffolding via rigup new. 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.toTOML {
        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.some-local-mcp.command = pkgs.my-mcp-server;
  mcpServers.some-remote-mcp = {
    url = "https://...";
    useSSE = true; # false by default
  };
}

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
};

WARNING: API still experimental.

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 = ["git-setup"]

[rigs.default.config.agent.identity]
name = "Alice"
email = "alice@example.com"

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 = [
            # A module from rigup:
            rigup.riglets.git-setup
            # A module defined directly inline:
            {
              # Complex Nix expressions
              agent.complexOption = lib.mkIf condition value;
            }
          ];
        };
      };
}

resolveProject outputs

  • riglets.<riglet> - Auto-discovered riglet modules
  • rigs.<system>.<rig> - Output of buildRig for each discovered rig:
    • toolRoot - Folder derivation. Tools combined via nixpkgs buildEnv function (bin/, lib/, share/, etc.) and wrapped (when needed) to fix their XDG_CONFIG_HOME
    • configRoot - 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 module
    • docAttrs.<riglet> - Folder derivation. Per-riglet documentation folder derivations
    • docRoot - 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/ folders
    • shell - Shell derivation (via pkgs.mkShell) exposing ready-to-use RIG_MANIFEST and PATH env vars
    • extend - Nix function. Adds riglets to a pre-existing rig: takes {newName, extraModules} and returns a new rig
    • manifest - 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 && git --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:

  1. Create riglets/my-riglet.nix, or riglets/my-riglet/default.nix for riglets with multiple supporting files
  2. Add the needed tools, documentation, metadata
  3. Define options (schema) and config (values) in this module
  4. Ensure the project has a top-level flake.nix that uses rigup.lib.resolveProject as 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
Files (rigup.nix)
  • references
    • advanced-patterns.md 1.9 KB
      # Advanced Cross-Riglet Patterns
      
      Once comfortable with basic riglet structure, these patterns enable sophisticated composition and configuration sharing.
      
      ## Sharing Configuration via config
      
      Riglets can reference each other's options via their `config` input arg:
      
      ```nix
      # agent-identity defines agent.identity.name
      options.agent.identity.name = lib.mkOption { ... };
      
      # typst-reporter uses it
      "template.typ" = ''
        ...
        #set document(author: "${config.agent.identity.name}")
        ...
      '';
      ```
      
      This allows one riglet to define configuration options that other riglets consume, enabling centralized settings.
      
      ## Dependencies and Inheritance via imports
      
      If a riglet depends on another, use `imports` with `self.riglets.*`:
      
      ```nix
      self:
      { riglib, ... }: {
        # Import the base riglet - evalModules deduplicates if both are in the rig
        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.
      
      For riglets from external flakes:
      
      ```nix
      imports = [ self.inputs.other-flake.riglets.some-riglet ];
      ```
      
      This relationship ensures that whenever your riglet is included, its dependencies are automatically included too.
      
      ## Using Packages from External Flakes
      
      Access packages from external flakes via `self.inputs`:
      
      ```nix
      self:
      { pkgs, system, riglib, ... }: {
        config.riglets.my-riglet = {
          tools =
            # Use the provided system to select the right platform
            # (`system` arg == `pkgs.stdenv.hostPlatform.system` == `pkgs.system` but last one is deprecated)
            let someFlakePkgs = self.inputs.some-flake.packages.${system};
            in [
              someFlakePkgs.foo
              someFlakePkgs.bar
              pkgs.git
            ];
        };
      }
      ```
      
      This allows riglets to compose tools and packages from multiple upstream flakes, pinned to specific revisions.
      
    • harness-integration.md 3.1 KB
      # Agent Harness Integration
      
      The rig system integrates with common AI coding agent harnesses via specialized riglets that define an "entrypoint": a wrapper script launched by `rigup run .#rig`.
      
      ## Implemented Integrations
      
      These harnesses and others are already Nix-packaged in numtide's [llm-agents.nix flake](https://github.com/numtide/llm-agents.nix) (previously called "nix-ai-tools").
      
      The `claude-code` riglet serves as a reference for users and agents wishing to integrate with other harnesses.
      
      ### Claude Code
      
      The `claude-code` riglet provides integration with Claude Code, the official Anthropic CLI tool.
      Rig's configuration is generated and passed to Claude Code via CLI flags, and adds up to user's and project's pre-existing configuration.
      All features of the riglet schema are supported.
      
      ### OpenCode
      
      The `opencode` riglet provides integration with OpenCode.
      Rig's configuration is generated and passed to OpenCode via env vars, and adds up to user's and project's pre-existing configuration.
      All features of the riglet schema are supported.
      
      ### Cursor IDE & `cursor-agent`
      
      The `cursor` riglet provides integration with Cursor IDE and `cursor-agent`.
      It works by writing to the `.cursor/` folder of the user's project, as Cursor has no CLI flags or env vars that can be used to feed it external config.
      Most features of the riglet schema are supported.
      
      ### `copilot-cli`
      
      The `copilot-cli` riglet provides integration with GitHub `copilot-cli`.
      Rig's manifest is shared with copilot-cli via env vars.
      MCP servers and prompt commands are not supported. A warning will be displayed if the user's rig contains any MCP server config or prompt commands.
      
      ### VSCode + GitHub Copilot
      
      The `vscode-copilot` riglet provides integration with VSCode and the GitHub Copilot extension.
      It works by writing configuration files to the `.vscode/` and `.github/` folders of the user's project, as VSCode Copilot discovers these files automatically.
      This is a setup-only riglet (no launch wrapper) - after running `rigup run .#rig`, users open their project with `code .` as usual.
      
      **Key features:**
      - Rig manifest copied to `.github/copilot-instructions.md` (auto-discovered by Copilot)
      - MCP servers written to `.vscode/mcp.json` using VSCode format (`servers` key, not `mcpServers`)
      - Prompt commands written to `.github/prompts/*.prompt.md` with YAML frontmatter
      - Terminal auto-approve rules merged into `.vscode/settings.json` (preserves existing user settings)
      - Documentation copied to `.github/rig-docs/` (symlinks don't work - VSCode blocks read access outside workspace)
      - Activation script at `.github/rig-activate.sh` for PATH setup
      
      **Limitations:**
      - VSCode Copilot's `read_file` tool is workspace-scoped with no mechanism to grant access to external paths like Nix store
      - Symlinks are resolved and then blocked if they point outside the workspace
      - Therefore, rig docs must be copied into workspace (increases disk usage compared to symlink-based approaches)
      - Settings merge uses `jq` - only touches `chat.tools.terminal.autoApprove` key to avoid disrupting user configuration
      
      All features of the riglet schema are supported.
      
      
    • metadata-guide.md 3.4 KB
      # Riglet Metadata Guide
      
      When defining a riglet's `meta` section, you can specify several fields to describe its purpose, maturity, and visibility.
      
      Most of these metadata elements are NOT meant to be overriden by end users when building their rig, EXCEPT for `meta.disclosure`.
      
      ## meta.intent
      
      Primary focus/intent of the riglet:
      
      - `base` - Config and/or tools without docs; usually to be imported by other riglets without being disclosed via the manifest
      - `sourcebook` - Specialized facts, knowledge, terminology, or domain context for guiding thinking
      - `toolbox` - Open-ended collection of tools/resources with minimal context on how they work together
      - `cookbook` - Specialized techniques and patterns; arcane tricks agents may lack
      - `playbook` - Behavioural instructions; step-by-step procedures for executing specific workflows
      
      ## meta.status
      
      Maturity level:
      
      - `stable` - Production-ready, well-tested riglet
      - `experimental` - (Default) Usable but may change, not fully battle-tested
      - `draft` - Work in progress, incomplete
      - `deprecated` - No longer maintained, use alternatives
      - `example` - Pedagogical riglet for demonstrating patterns
      
      Used to add warnings to the rig manifest.
      
      ## meta.version
      
      Semantic version (Default: `"0.1.0"`) of riglet's interface/capabilities:
      
      - Use semver format: `MAJOR.MINOR.PATCH` (e.g., `"1.2.3"`)
      - Increment MAJOR for breaking changes (renamed options, removed features)
      - Increment MINOR for backwards-compatible additions (new options, new docs sections)
      - Increment PATCH for backwards-compatible fixes (doc corrections, bug fixes)
      
      ## meta.broken
      
      Boolean flag (Default: `false`) indicating riglet is currently non-functional:
      
      - Like Nix derivations' `meta.broken`, marks temporary "needs fixing" state
      - Takes precedence over status in warnings in rig manifest
      
      ## meta.disclosure
      
      Enum controlling how much information about the riglet is exposed in RIG.md
      
      - `none` - Riglet not mentioned in RIG.md. Agent won't know it exists unless manually browsing the rig or user mentions it
      - `lazy` - (Default) Description, `whenToUse`, keywords, and basic metadata included. Paths to documentation provided
      - `shallow-toc` - Like `lazy`, plus an auto-generated table of contents showing levels 1-2 headers from SKILL.md with line numbers for efficient navigation
      - `deep-toc` - Like `shallow-toc`, but includes all header levels (1-6) for comprehensive navigation
      - `eager` - Full top-level SKILL.md content directly embedded in RIG.md
      
      This controls the information/token-count ratio:
      
      - most riglets use `lazy` to avoid overwhelming agents during discovery
      - foundational riglets use `shallow-toc` to enable efficient pinpointing of major sections
      - complex riglets use `deep-toc` when agents need to navigate deeply nested documentation
      - `eager` should only be used for very short SKILL.md
      
      Riglets overriding this default SHOULD use `nixpkgs.lib.mkDefault`, so end users may still easily change it when building their rig.
      
      ## Tool Configuration Files
      
      **configFiles** provides configuration for tools:
      
      - Uses `riglib.writeFileTree` to create `.config/` directory structure
      - Follows XDG Base Directory specification
      - All riglets' configFiles are merged into `.config/`
      - Example: `jj."config.toml"` → `.config/jj/config.toml`
      - Can use `riglib.toJSON`/`YAML`/`TOML`/`XML` to generate config files from Nix data
      - Can use plain strings for shell scripts or plain text configs
      
    • riglib-utilities.md 3.5 KB
      # Riglib Utility Functions
      
      Riglets have access to `riglib` helper functions for common patterns.
      
      ## riglib.writeFileTree
      
      Converts nested attrsets to directory trees:
      
      - Takes a single attrset argument
      - `"SKILL.md"` → `SKILL.md`
      - `references."foo.md"` → `references/foo.md`
      - Extensions must be included in attribute names
      - Leaf values can be:
        - Strings (inline content)
        - File paths (e.g., `./SKILL.md` - useful for directory-based riglets)
        - Derivations (e.g., `pkgs.writeText` or `riglib.toJSON`)
      
      ### Example Usage
      
      ```nix
      docs = riglib.writeFileTree {
        "SKILL.md" = ''
          # My Riglet
          ...
        '';
        references = {
          "advanced.md" = ./path/to/advanced.md;
          "troubleshooting.md" = pkgs.writeText "troubleshooting" ''
            ...
          '';
        };
      };
      ```
      
      ## riglib.useScriptFolder
      
      Converts all files in a folder to wrapped tool packages:
      
      - Takes a folder path as argument
      - Returns a list that can be concatenated with other tools
      - Each regular file in the folder becomes an executable tool
      - Automatically filters out directories and non-regular files
      - Each script gets wrapped via `wrapScriptPath` (filename becomes command name)
      
      ### Example Usage
      
      ```nix
      tools = [ pkgs.git ] ++ riglib.useScriptFolder ./scripts
      ```
      
      With this, all regular files in `./scripts/` become executable tools in the rig's `toolRoot`, named by their filename.
      
      ## riglib.filterFileTree
      
      Recursively filters a directory to keep only files with specified extensions:
      
      - Takes two arguments: a list of extensions and a directory path
      - Returns a derivation containing only files matching the specified extensions
      - Preserves the directory structure
      - Extensions are case-insensitive and can be specified with or without leading dots
      - Useful for creating documentation bundles or resource directories
      
      ### Example Usage
      
      ```nix
      # Filter to keep only markdown and text files
      docs = riglib.filterFileTree ["md" "txt"] ./documentation;
      
      # Extensions can have leading dots - both work the same
      resources = riglib.filterFileTree [".png" ".jpg" ".svg"] ./assets;
      
      # Use in riglet docs field
      config.riglets.my-riglet = {
        docs = riglib.filterFileTree ["md"] ./docs;  # Only include markdown files
        # ...
      };
      ```
      
      This recursively walks the directory tree and creates a new derivation with only the filtered files, maintaining the original directory structure.
      
      ## riglib.renderMinijinja
      
      Renders a Minijinja template with provided data:
      
      - Takes an attrset with: `{ template, data, strict ? true }`
        - `template`: Path to the template file
        - `data`: Nested attrset of data to fill in the template
        - `strict`: (optional, default true) Fail if template references missing variables
      - Returns a derivation containing the rendered output
      - **Automatically marks the intermediate JSON data file as non-substitutable**
      
      ### Example Usage
      
      ```nix
      docs = riglib.writeFileTree {
        "SKILL.md" = riglib.renderMinijinja {
          template = ./SKILL.md.jinja;
          data = {
            projectName = "my-project";
            version = "1.0.0";
          };
        };
      };
      ```
      
      ## riglib.alwaysLocal
      
      Marks a derivation as non-substitutable, preventing Nix from querying remote caches for it:
      
      - Takes a single derivation argument
      - Useful for cheap-to-build, project-specific derivations that will never be in a cache
      
      ### Example Usage
      
      ```nix
      # Mark a generated config file as non-substitutable
      configFile = riglib.alwaysLocal (
        (pkgs.formats.json {}).generate "my-config.json" { setting = "value"; }
      );
      ```
      
      ### Utilities
      
      `riglib.toJSON`, `toTOML`, `toXML` and `toYAML` wrap `(pkgs.formats.<format> {}).generate` with `alwaysLocal`.
      
      
  • default.nix 1.6 KB · in bundle
  • SKILL.md 16.1 KB
    # Agent Rig System
    
    ## Overview
    
    A **rig** is a collection of _riglets_ that provide knowledge and tools for AI agents.
    Rigs and riglets are packaged as Nix flake outputs, so they can both be used inside the project defining them and by other projects depending on it.
    
    ## 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/*.md` files 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 `buildRig` to 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 the `riglets/` folder of a project and its `rigup.toml` to find out which riglets and rigs it defines. It calls `buildRig` for each rig in the `rigup.toml`
    - `genManifest`: generates a markdown+XML manifest file describing the contents of a rig, primarily for AI agent's consumption
    - `mkRiglib`: 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`, and project scaffolding via `rigup new`. 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
    
    ```nix
    # 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.toTOML {
            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.some-local-mcp.command = pkgs.my-mcp-server;
      mcpServers.some-remote-mcp = {
        url = "https://...";
        useSSE = true; # false by default
      };
    }
    ```
    
    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:
    
    ```nix
    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:
    
    ```nix
    mcpServers.my-tools = {
      command = pkgs.my-mcp-server;  # Package that starts the server
    };
    ```
    
    WARNING: API still experimental.
    
    ## 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:
    
    ```toml
    [rigs.default.riglets]
    self = ["my-riglet"]
    rigup = ["git-setup"]
    
    [rigs.default.config.agent.identity]
    name = "Alice"
    email = "alice@example.com"
    ```
    
    Then use `rigup.lib.resolveProject` in your flake.nix:
    
    ```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:
    
    ```nix
    {
      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 = [
                # A module from rigup:
                rigup.riglets.git-setup
                # A module defined directly inline:
                {
                  # Complex Nix expressions
                  agent.complexOption = lib.mkIf condition value;
                }
              ];
            };
          };
    }
    ```
    
    ### `resolveProject` outputs
    
    - `riglets.<riglet>` - Auto-discovered riglet modules
    - `rigs.<system>.<rig>` - Output of `buildRig` for each discovered rig:
      - `toolRoot` - Folder derivation. Tools combined via nixpkgs `buildEnv` function (bin/, lib/, share/, etc.) and wrapped (when needed) to fix their XDG_CONFIG_HOME
      - `configRoot` - 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 module
      - `docAttrs.<riglet>` - Folder derivation. Per-riglet documentation folder derivations
      - `docRoot` - 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/ folders
      - `shell` - Shell derivation (via `pkgs.mkShell`) exposing ready-to-use RIG_MANIFEST and PATH env vars
      - `extend` - Nix function. Adds riglets to a pre-existing rig: takes `{newName, extraModules}` and returns a new rig
      - `manifest` - 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.
    
    ```bash
    # 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.
    
    ```bash
    # 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 && git --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:
    
    1. Create `riglets/my-riglet.nix`, or `riglets/my-riglet/default.nix` for riglets with multiple supporting files
    1. Add the needed tools, documentation, metadata
    1. Define options (schema) and config (values) in this module
    1. Ensure the project has a top-level `flake.nix` that uses `rigup.lib.resolveProject` as 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
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related