AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Devenv

skill-brpaz-agent-skills-devenv · by brpaz

Define Nix-based development environments with devenv.sh, including task runners, Docker Compose workflows, services, and containers.

No reviews yet
0 installs
30 views
0.0% view→install

Install

$ agentstack add skill-brpaz-agent-skills-devenv

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-brpaz-agent-skills-devenv)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
4mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Devenv? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

devenv.sh - Nix-powered Developer Environments

Use this skill when modifying devenv.nix, devenv.yaml, or working with devenv CLI commands. This covers the full devenv configuration surface.

When to Use

  • Creating or modifying a devenv.nix or devenv.yaml for a project
  • Adding packages, language runtimes, services, or scripts to a dev environment
  • Integrating devenv with direnv for automatic environment activation
  • Pinning or updating devenv inputs via devenv.lock
  • Bootstrapping a web application environment around Taskfile.yml, docker compose, and git hooks

Project File Structure

| File | Purpose | Commit? | |------|---------|---------| | devenv.nix | Main configuration (Nix expression) | Yes | | devenv.yaml | Inputs, imports, and settings | Yes | | devenv.lock | Pinned input revisions (auto-generated) | Yes | | .envrc | direnv integration (use devenv) | Yes | | devenv.local.nix | Local overrides (not committed) | No | | devenv.local.yaml | Local input overrides | No | | .devenv/ | Generated state directory | No (gitignored) | | .devenv.flake.nix | Auto-generated flake wrapper | No (gitignored) |

devenv.nix Structure

Every devenv.nix is a Nix function receiving a module argument set:

{ pkgs, lib, config, inputs, ... }:

{
  # Configuration goes here
}

Key parameters:

  • pkgs - Nixpkgs package set
  • lib - Nixpkgs library functions
  • config - The resolved devenv configuration (self-referencing)
  • inputs - Flake inputs defined in devenv.yaml

devenv.yaml Structure

inputs:
  nixpkgs:
    url: github:cachix/devenv-nixpkgs/rolling

# Optional additional inputs
# inputs:
#   nixpkgs-stable:
#     url: github:NixOS/nixpkgs/nixos-24.11

# Optional imports from local files or inputs
# imports:
#   - ./backend/devenv.nix
#   - inputs:myinput/devenv-module.nix

# Allow unfree packages
# allowUnfree: true
# permittedUnfreePackages:
#   - "terraform"

# Impure mode (access host env vars)
# impure: true

Environment Variables

Built-in variables available inside devenv shell:

  • DEVENV_ROOT - Project root directory
  • DEVENV_DOTFILE - Path to .devenv/ directory
  • DEVENV_STATE - Persistent state directory (for services data)
  • DEVENV_RUNTIME - Runtime directory (temp, cleared on reboot)
  • DEVENV_PROFILE - Current Nix profile path

Setting custom env vars:

{
  env.DATABASE_URL = "postgres://localhost:5432/mydb";
  env.API_PORT = "3000";

  # Reference other config values
  env.GREETING = "Running in ${config.env.DEVENV_ROOT}";
}

Packages

{
  # Add packages from nixpkgs
  packages = [
    pkgs.git
    pkgs.jq
    pkgs.curl
    pkgs.go-task
    pkgs.lefthook
    pkgs.docker-compose
  ];
}

Search for packages: devenv search

Languages

Languages are enabled via toggle modules. Each language module provides tooling, compilers, and package managers.

{
  # Node.js
  languages.javascript = {
    enable = true;
    package = pkgs.nodejs_22;  # Override default version
    pnpm.enable = true;
    pnpm.install.enable = true;  # Auto-install on shell entry
  };

  # Python
  languages.python = {
    enable = true;
    version = "3.12";
    venv.enable = true;
    venv.requirements = ./requirements.txt;
  };

  # Rust
  languages.rust = {
    enable = true;
    channel = "stable";  # "stable", "nightly", or "beta"
  };

  # Go
  languages.go.enable = true;

  # Ruby
  languages.ruby = {
    enable = true;
    bundler.enable = true;
  };
}

Scripts

Custom commands available in the devenv shell:

{
  # Simple script
  scripts.dev.exec = ''
    pnpm run dev
  '';

  # Script with extra packages in scope
  scripts.fetch-data.exec = ''
    curl -s https://api.example.com | jq '.data'
  '';
  scripts.fetch-data.packages = [ pkgs.curl pkgs.jq ];

  # Pin binaries to avoid PATH conflicts
  scripts.build.exec = ''
    ${pkgs.nodejs}/bin/node scripts/build.js
  '';

  # Use a specific interpreter
  scripts.analyze.exec = ''
    import sys
    print(sys.version)
  '';
  scripts.analyze.package = config.languages.python.package;
  scripts.analyze.binary = "python3";
}

enterShell

Code that runs every time you enter the devenv shell:

{
  enterShell = ''
    if [ ! -f .env ] && [ -f .env.example ]; then
      cp .env.example .env
      echo "Created .env from .env.example"
    fi
  '';
}

Use enterShell only for small, idempotent shell-entry bootstrap such as creating a missing local .env file from .env.example.

Prefer tasks over enterShell for anything non-trivial - tasks support better reuse, clearer entrypoints, and easier composition with project tooling.

Web Application Default Stance

For web applications, prefer this split of responsibilities:

  • devenv for toolchains, packages, env wiring, and shell bootstrapping
  • Taskfile.yml (go-task) for developer entrypoints like task dev, task test, and task lint
  • docker compose for long-running multi-service application stacks

For typical web applications, prefer Task + Compose over processes.*.

Recommended Defaults for Web Apps

  • Put developer commands in Taskfile.yml
  • Use docker compose up / down / logs for the running stack
  • Use scripts.* in devenv.nix as thin wrappers around task ... when convenient
  • Keep processes.* for small local daemons, single binaries, or non-web workflows that do not need Compose

Example:

{
  scripts.dev.exec = "task dev";
  scripts.test.exec = "task test";
  scripts.logs.exec = "task compose:logs";
}

Processes

Long-running processes managed by devenv (started with devenv up). These are useful for simple local daemons, workers, and lightweight tooling, but they are not the default orchestration choice for web applications:

{
  # Simple process
  processes.api.exec = "pnpm run dev";

  # Process with working directory
  processes.frontend = {
    exec = "pnpm run dev";
    cwd = "./frontend";
  };

  # Process dependencies (start order)
  processes.api = {
    exec = "pnpm run start:api";
    after = [ "devenv:processes:db" ];
  };

  # File watching (auto-restart on changes)
  processes.worker = {
    exec = "node worker.js";
    watch.paths = [ ./src/worker ];
  };

  # Port allocation
  processes.api = {
    exec = "node server.js --port $PORT";
    ports.http.allocate = 8080;
    # Access: config.processes.api.ports.http.value
  };
}

Services

Pre-built service modules (databases, caches, etc.):

{
  services.postgres = {
    enable = true;
    listen_addresses = "127.0.0.1";
    port = 5432;
    initialDatabases = [{ name = "myapp"; }];
    extensions = ext: [ ext.postgis ];
  };

  services.redis.enable = true;

  services.mysql = {
    enable = true;
    initialDatabases = [{ name = "myapp"; }];
  };

  services.minio.enable = true;

  services.elasticsearch.enable = true;
}

Service state is stored in $DEVENV_STATE/. Services start with devenv up.

Tasks

Cacheable, dependency-aware build tasks inside devenv.nix:

{
  # Basic task
  tasks."myapp:build" = {
    exec = "pnpm run build";
    before = [ "devenv:enterShell" ];  # Runs before shell entry
  };

  # Task with status check (skip if already done)
  tasks."myapp:install" = {
    exec = "pnpm install";
    status = "test -d node_modules";
    before = [ "devenv:enterShell" ];
  };

  # Conditional re-execution on file changes
  tasks."myapp:codegen" = {
    exec = "pnpm run codegen";
    execIfModified = [ "schema/**/*.graphql" ];
    before = [ "devenv:enterShell" ];
  };

  # Task with inputs (CLI-passable)
  tasks."myapp:migrate" = {
    exec = ''
      echo "Running migration: $TASK_INPUT_NAME"
    '';
    input = {
      name = {
        description = "Migration name";
        default = "latest";
      };
    };
  };
}

Run tasks: devenv tasks run myapp:build With inputs: devenv tasks run myapp:migrate --input name=v2

For web applications, prefer using project-level Taskfile.yml as the main task runner, and use devenv tasks only when you specifically want Nix-managed lifecycle hooks or shell-entry integration.

Tests

{
  enterTest = ''
    echo "Running tests..."
    wait_for_port 5432    # Helper: wait for service port
    pnpm run test
  '';
}

Run: devenv test (starts processes, runs enterTest, then stops everything).

Use config.devenv.isTesting to conditionally disable things during tests:

{
  processes.heavy-worker = lib.mkIf (!config.devenv.isTesting) {
    exec = "node heavy-worker.js";
  };
}

Git Hooks (pre-commit)

Integration with git-hooks.nix:

{
  git-hooks.hooks = {
    nixfmt-rfc-style.enable = true;
    prettier = {
      enable = true;
      excludes = [ "pnpm-lock.yaml" ];
    };
    eslint.enable = true;
    shellcheck.enable = true;

    # Custom hook
    check-todos = {
      enable = true;
      entry = "grep -r 'ACTION_ITEM' --include='*.ts' && exit 1 || exit 0";
      language = "system";
    };
  };
}

The .pre-commit-config.yaml is an auto-generated symlink - do not edit it directly.

Overlays

Modify or extend the nixpkgs package set:

{
  overlays = [
    # Override an existing package
    (final: prev: {
      hello = prev.hello.overrideAttrs (old: {
        patches = (old.patches or []) ++ [ ./my-patch.patch ];
      });
    })

    # Add a custom package
    (final: prev: {
      my-tool = final.callPackage ./nix/my-tool.nix {};
    })
  ];
}

Inputs & Multiple Nixpkgs

In devenv.yaml, add extra inputs:

inputs:
  nixpkgs:
    url: github:cachix/devenv-nixpkgs/rolling
  nixpkgs-stable:
    url: github:NixOS/nixpkgs/nixos-24.11

Use in devenv.nix:

{ pkgs, inputs, ... }:

let
  pkgs-stable = import inputs.nixpkgs-stable {
    system = pkgs.stdenv.system;
  };
in {
  packages = [
    pkgs.nodejs_22       # From rolling
    pkgs-stable.terraform  # From stable
  ];
}

Update/lock inputs: devenv update

Outputs (Building Artifacts)

{
  outputs = {
    my-app = config.languages.rust.import ./. {};
    container = config.containers.shell.derivation;
  };
}

Build: devenv build (all) or devenv build outputs.my-app

Profiles

Named configurations for different contexts:

# devenv.nix
{
  # Shared config available to all profiles
  packages = [ pkgs.git ];
}
# profiles/backend.nix - referenced in devenv.yaml imports
{ pkgs, ... }: {
  languages.go.enable = true;
  services.postgres.enable = true;
}

Activate: devenv --profile backend shell

Containers

{
  containers.shell.name = "my-app-shell";

  containers.processes = {
    name = "my-app";
    startupCommand = config.procfileScript;
  };
}

Build: devenv container build shell Run: devenv container run shell Copy to registry: devenv container copy shell docker://registry/image:tag

Composing with Imports

# devenv.yaml
imports:
  - ./backend/devenv.nix
  - ./frontend/devenv.nix
  - inputs:some-input/devenv-module.nix

For monorepos, use profiles or imports to compose per-service environments.

direnv Integration

.envrc file:

eval "$(devenv direnvrc)"
use devenv

This auto-activates the devenv shell when entering the project directory.

Dotenv Integration

{
  dotenv.enable = true;
  dotenv.filename = ".env";  # default
}

Warning: .env contents are copied into the Nix store (world-readable). For real secrets, use secretspec or inject via environment outside of Nix.

Creating Files

{
  files."config/settings.json".text = builtins.toJSON {
    port = 3000;
    debug = true;
  };

  files.".editorconfig".text = ''
    root = true
    [*]
    indent_style = space
    indent_size = 2
  '';
}

CLI Command Reference

| Command | Description | |---------|-------------| | devenv init | Initialize a new devenv project | | devenv shell | Enter the development shell | | devenv up | Start all processes | | devenv up -d | Start processes in background (detached) | | devenv test | Run tests (starts processes, runs enterTest, stops) | | devenv build | Build all outputs | | devenv build outputs. | Build a specific output | | devenv search | Search available packages | | devenv info | Show environment info | | devenv update | Update and lock inputs | | devenv gc | Garbage collect old generations | | devenv container build | Build a container | | devenv container run | Run a container | | devenv container copy | Copy container to registry | | devenv tasks run | Run a specific task | | devenv processes up | Start processes (alias) |

Common flags:

  • --profile - Use a specific profile
  • --impure - Allow impure evaluation (access host env)
  • --option - Override a Nix option

Common Nix Patterns in devenv.nix

let/in bindings

{ pkgs, ... }:

let
  port = "3000";
  dbName = "myapp";
in {
  env.PORT = port;
  services.postgres.initialDatabases = [{ name = dbName; }];
}

Conditional configuration

{ pkgs, lib, config, ... }:

{
  # Only include when testing
  processes.seed-db = lib.mkIf config.devenv.isTesting {
    exec = "node seed.js";
  };

  # Optional attrs
  packages = [ pkgs.git ] ++
    lib.optionals pkgs.stdenv.isLinux [ pkgs.strace ];
}

Module imports

{ pkgs, ... }:

{
  imports = [ ./shared.nix ];

  # This merges with shared.nix configuration
  packages = [ pkgs.curl ];
}

Rules

  • ALWAYS read the existing devenv.nix and devenv.yaml before making changes - understand the current configuration.
  • NEVER manually edit devenv.lock - it is auto-generated by devenv update.
  • NEVER manually edit .pre-commit-config.yaml - it is an auto-generated symlink from git-hooks configuration.
  • NEVER put real secrets in devenv.nix or .env with dotenv enabled - Nix store is world-readable.
  • Use devenv search to find the correct package attribute before adding to packages.
  • Prefer Taskfile.yml (go-task) for web application task running - make task dev, task test, and task lint the main developer entrypoints.
  • Prefer docker compose over processes.* for web application stacks - reserve processes.* for simpler local daemons and non-web workflows.
  • Prefer tasks over enterShell for setup steps that can be cached or have dependencies.
  • Keep enterShell small and idempotent - copying .env.example to .env is fine; installs, migrations, and long-running commands are not.
  • Use lib.mkIf for conditional configuration, not string interpolation or if/then/else at the module level.
  • Pin binaries in scripts with ${pkgs.foo}/bin/foo when PATH conflicts are possible.
  • Use devenv.local.nix for personal overrides that should not be committed.
  • Run devenv test to validate changes - it exercises the full environment including processes.

Starter Template

A basic starter is bundled at skills/devenv/templates/basic-webapp/.

It includes:

  • devenv.nix
  • devenv.yaml
  • .envrc
  • .env.example
  • compose.yaml
  • Taskfile.yml
  • lefthook.yml

The template is intentionally minimal and shows the preferred pattern for web apps in this repository:

  • devenv for packages and shell bootstrap
  • Taskfile.yml for task running
  • docker compose for the running stack
  • lefthook for git hook wiring

Examples

# devenv.nix — basic web app shell
{ pkgs, ... }: {
  packages = [
    pkgs.git
    pkgs.go-task
    pkgs.lefthook
    pkgs.docker-compose
  ];

  enterShell = ''
    if [ ! -f .env ] && [ -f .env.example ]; then
      cp .env.example .env
    fi
  '';

  scripts.dev.exec = "task dev";
  scripts.test.exec = "task test";
}

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.