Help us improve
Share bugs, ideas, or general feedback.
From devenv
Initialize and manage Nix flake development environments with auto-detection and security tooling
npx claudepluginhub jskswamy/claude-plugins --plugin devenvHow this skill is triggered — by the user, by Claude, or both
Slash command
/devenv:devenvThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Initialize and manage Nix flake development environments. Provides a devbox-like experience with auto-detection of project dependencies and security tooling using native Nix solutions.
Creates reproducible cross-platform dev environments with Flox for toolchains (Python, Node, Rust, Go), system deps (openssl, BLAS), and services (PostgreSQL, Redis, Kafka) across macOS/Linux.
Manage reproducible development environments with Flox. **ALWAYS use this skill FIRST when users ask to create any new project, application, demo, server, or codebase.** Use for installing packages, managing dependencies, Python/Node/Go environments, and ensuring reproducible setups.
Share bugs, ideas, or general feedback.
Initialize and manage Nix flake development environments. Provides a devbox-like experience with auto-detection of project dependencies and security tooling using native Nix solutions.
First, determine if this is a new setup or managing an existing environment:
flake.nix exists in the current directorygit-hooks input is present (indicates pre-commit is configured).claude/devenv.local.md if it exists for:
nixos-unstable)box)shell_welcome: custom)2a. Auto-detect project stack:
Scan for language indicator files and suggest packages:
| File | Stack | Suggested Packages |
|---|---|---|
package.json | Node.js | nodejs, check lockfile for pnpm/yarn/npm |
pnpm-lock.yaml | Node.js + pnpm | nodejs, pnpm |
yarn.lock | Node.js + yarn | nodejs, yarn |
package-lock.json | Node.js + npm | nodejs |
Cargo.toml | Rust | rustc, cargo, rust-analyzer |
go.mod | Go | go, gopls |
pyproject.toml | Python | python3, uv |
requirements.txt | Python | python3, pip |
Gemfile | Ruby | ruby, bundler |
pom.xml | Java | jdk, maven |
build.gradle | Java/Kotlin | jdk, gradle |
*.csproj | .NET | dotnet-sdk |
Makefile | C/C++ | gnumake, gcc |
CMakeLists.txt | C/C++ | cmake, gcc |
Dockerfile | Docker | docker |
Also read CLAUDE.md if it exists to understand the project context and tech stack.
2b. Present detected packages:
Use AskUserQuestion with multiSelect to show detected packages:
Based on your repository, I detected these packages:
[x] nodejs_22
[x] pnpm
[x] typescript
Let user confirm, deselect, or add more via "Other" option.
2c. Ask for additional packages:
Ask: "Any additional packages you need? (e.g., redis, postgresql, jq)"
For each package mentioned:
.claude/devenv.local.md for use_mcp_search setting (default: true)mcp__1mcp__nixos_1mcp_nixos_search(
query="<package>",
search_type="packages",
channel="unstable",
limit=10
)
b. Global/project level MCP (if user has mcp-nixos configured):
mcp__nixos__nix(
action="search",
query="<package>",
source="nixos",
type="packages",
channel="unstable",
limit=10
)
c. Plugin's bundled MCP:
mcp__plugin_devenv_nixos__nix(
action="search",
query="<package>",
source="nixos",
type="packages",
channel="unstable",
limit=10
)
use_mcp_search: false, use bash:
nix-shell -p nix --run "nix search nixpkgs <package> --json"nodejs@20), handle version resolution:
a. Try mcp__1mcp__nixos_1mcp_nixhub_find_version(package_name="nodejs", version="20")
b. If 1mcp unavailable, try mcp__plugin_devenv_nixos__nix_versions(package="nodejs", version="20")
c. Also search for nixpkgs variants like nodejs_20
d. Prefer the nixpkgs variant if it exists; offer pinned commit hash as alternative2d. Offer security tools:
Use AskUserQuestion with multiSelect:
Security tools (recommended):
[ ] pre-commit - Git hooks with SAST tools for your stack (using git-hooks.nix)
If pre-commit selected, determine which git-hooks.nix hooks to enable based on detected stack:
| Stack | Hooks to Enable |
|---|---|
| JavaScript/TypeScript | eslint.enable = true; prettier.enable = true; |
| Python | ruff.enable = true; ruff-format.enable = true; |
| Go | golangci-lint.enable = true; |
| Rust | clippy.enable = true; rustfmt.enable = true; |
| Nix (always) | nixfmt-rfc-style.enable = true; statix.enable = true; deadnix.enable = true; |
| Shell scripts | shellcheck.enable = true; |
| Docker | hadolint.enable = true; |
| General (always) | check-yaml.enable = true; trim-trailing-whitespace.enable = true; |
Then ask about secret scanning:
Use AskUserQuestion:
Secret scanner preference:
○ trufflehog (Recommended) - Built-in hook, works out of the box
○ gitleaks - Popular choice, requires custom hook configuration
○ None - Skip secret scanning
If trufflehog selected:
trufflehog.enable = true; to the hooks configuration (built-in, no extra config needed)If gitleaks selected:
gitleaks = {
enable = true;
name = "gitleaks";
entry = "${pkgs.gitleaks}/bin/gitleaks git --pre-commit --redact --staged --verbose";
language = "system";
pass_filenames = false;
};
gitleaks to the packages listgitleaks to packages for manual use2e. Choose direnv mode:
Use AskUserQuestion:
direnv mode:
○ direnv-instant (Recommended) - Async loading, instant shell prompt
○ Standard direnv - Traditional sync direnv (shell waits for Nix)
○ None - Use nix develop manually
Based on selection:
direnv-instant as a flake input (it's NOT a nixpkgs package):
inputs.direnv-instant.url = "github:Mic92/direnv-instant";
Then reference the package in devShell:
direnv-instant.packages.${system}.default
direnv to packages list in flake.nix (from nixpkgs)Save the selection to .claude/devenv.local.md as direnv_mode: instant|standard|none
2f. Choose shell welcome style:
Use AskUserQuestion to let the user choose their shell welcome message style:
Shell welcome message style:
○ Box style (Default) - Unicode box with devenv branding
○ Minimal - Single line "▸ devenv ready"
○ Project name - Shows current project directory name
○ Tech style - Terminal-inspired "[devenv] :: environment initialized"
○ Custom - Enter your own message
○ None - No welcome message
If "Custom" is selected:
Save the selection to .claude/devenv.local.md as:
shell_welcome: box|minimal|project|tech|custom|noneshell_welcome_custom: "user's custom message" (only if custom selected)2g. Generate files:
flake.nix using the appropriate template:Basic Template (without pre-commit):
{
description = "Development environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
# DIRENV_INSTANT_INPUT_HERE (if selected)
};
outputs = {
self,
nixpkgs,
flake-utils,
# DIRENV_INSTANT_PARAM_HERE (if selected)
}:
flake-utils.lib.eachDefaultSystem (
system: let
pkgs = nixpkgs.legacyPackages.${system};
in {
devShells.default = pkgs.mkShell {
packages = with pkgs; [
# PACKAGES_HERE
]
# DIRENV_INSTANT_PACKAGE_HERE (if selected)
;
shellHook = ''
# SHELL_WELCOME_HERE
'';
};
}
);
}
To add direnv-instant to the template:
# DIRENV_INSTANT_INPUT_HERE with: direnv-instant.url = "github:Mic92/direnv-instant";# DIRENV_INSTANT_PARAM_HERE with: direnv-instant,# DIRENV_INSTANT_PACKAGE_HERE with: ++ [ direnv-instant.packages.${system}.default ]To add standard direnv: Simply add direnv to the packages list (it's a nixpkgs package).
Template with git-hooks.nix (pre-commit enabled):
{
description = "Development environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
git-hooks.url = "github:cachix/git-hooks.nix";
# DIRENV_INSTANT_INPUT_HERE (if selected)
};
outputs = {
self,
nixpkgs,
flake-utils,
git-hooks,
# DIRENV_INSTANT_PARAM_HERE (if selected)
}:
flake-utils.lib.eachDefaultSystem (
system: let
pkgs = nixpkgs.legacyPackages.${system};
# Pre-commit hooks configuration
pre-commit-check = git-hooks.lib.${system}.run {
src = ./.;
hooks = {
# Nix
nixfmt-rfc-style.enable = true;
statix.enable = true;
deadnix = {
enable = true;
settings.edit = true;
settings.noLambdaPatternNames = true; # Preserve 'self' in flake outputs
};
# General
check-yaml.enable = true;
trim-trailing-whitespace.enable = true;
# LANGUAGE_HOOKS_HERE
# SECRET_SCANNER_HERE
# For trufflehog (built-in): trufflehog.enable = true;
# For gitleaks (custom hook):
# gitleaks = {
# enable = true;
# name = "gitleaks";
# entry = "${pkgs.gitleaks}/bin/gitleaks git --pre-commit --redact --staged --verbose";
# language = "system";
# pass_filenames = false;
# };
};
};
in {
# Run hooks in CI with: nix flake check
checks.pre-commit-check = pre-commit-check;
devShells.default = pkgs.mkShell {
packages = with pkgs; [
# PACKAGES_HERE
]
++ pre-commit-check.enabledPackages
# DIRENV_INSTANT_PACKAGE_HERE (if selected)
;
shellHook = ''
${pre-commit-check.shellHook}
# SHELL_WELCOME_HERE
'';
};
}
);
}
direnv-instant integration: Same as basic template - add input, parameter, and package reference.
Shell welcome replacement: Replace # SHELL_WELCOME_HERE based on user's shell_welcome preference:
| Style | Replacement Content |
|---|---|
box | echo "╔══════════════════════════════════════╗"echo "║ 🔧 devenv ║"echo "║ Development environment loaded ║"echo "╚══════════════════════════════════════╝" |
minimal | echo ""echo " ▸ devenv ready"echo "" |
project | echo ""echo " ╭───────────────────────────────────╮"echo " │ 📁 $(basename $PWD)"echo " │ Development environment loaded"echo " ╰───────────────────────────────────╯"echo "" |
tech | echo ""echo " [devenv] :: environment initialized"echo " └── packages loaded, hooks active"echo "" |
custom | Parse shell_welcome_custom from settings, split by \n, wrap each line in echo "..." |
none | # No welcome message (empty comment, no output) |
For custom messages:
Read shell_welcome_custom from .claude/devenv.local.md
Split by \n for multi-line support
Wrap each line in echo "..." statements
Escape special characters: $ becomes \$, " becomes \", \ becomes \\
If empty, fall back to box style
Handle .envrc (if direnv mode is not "none"):
IMPORTANT: Never read .envrc files - they may contain secrets.
.envrc exists: test -f .envrc.envrc does NOT exist:
use flake.envrc EXISTS:
.envrc already exists. Ensure it contains use flake for direnv integration."Update .gitignore to exclude generated/sensitive files:
.gitignore exists# Nix/direnv
.direnv/
.pre-commit-config.yaml
# Environment files (sensitive)
.env
.env.*
.envrc
# Claude local settings (user preferences)
.claude/*.local.md
nix flake lock to generate flake.lock2h. Validate the flake:
After generating files, ALWAYS validate the flake works correctly:
nix flake check --no-build 2>&1
If validation fails:
self parameter in outputs (required by flake system even if unused)flake.nixImportant: Never finish the task if validation fails. The user should have a working environment.
2i. Output instructions:
If direnv-instant was selected:
Development environment created!
To enter the environment:
nix develop
Or with direnv-instant (async, instant prompt):
direnv allow
direnv-instant shell setup (one-time):
# bash (~/.bashrc) or zsh (~/.zshrc):
eval "$(direnv-instant hook bash)" # or zsh
# fish (~/.config/fish/config.fish):
direnv-instant hook fish | source
Note: Remove any existing `eval "$(direnv hook ...)"` first.
Your environment includes:
- [list packages]
If standard direnv was selected:
Development environment created!
To enter the environment:
nix develop
Or with direnv:
direnv allow
Your environment includes:
- [list packages]
If direnv mode is "none":
Development environment created!
To enter the environment:
nix develop
Your environment includes:
- [list packages]
[If pre-commit was selected, append:]
Pre-commit hooks are configured via git-hooks.nix.
They will auto-install when you run `nix develop`.
Available hooks:
- [list enabled hooks]
To run hooks manually: pre-commit run --all-files
To validate in CI: nix flake check
2a. Parse current state:
flake.nix and extract current packages from packages = with pkgs; [ ... ]git-hooks input exists in flake.nix (indicates pre-commit is configured)2b. Ask what to do:
Use AskUserQuestion:
What would you like to do?
○ Add packages - Add new packages to your environment
○ Upgrade packages - Update flake.lock to get latest versions
○ Remove packages - Remove packages from your environment
○ Setup security tools - Add pre-commit and gitleaks (only if not configured)
○ Switch direnv mode - Change between direnv-instant, standard direnv, or none
○ Change welcome style - Customize shell welcome message
Only show "Setup security tools" option if git-hooks input is NOT present in flake.nix.
Show "Switch direnv mode" and "Change welcome style" options always (user may want to change their preference).
2c. Handle selected action:
Add packages:
.claude/devenv.local.md for use_mcp_search setting (default: true)mcp__1mcp__nixos_1mcp_nixos_search(query="<pkg>", search_type="packages", channel="unstable", limit=10)
b. Global/project MCP: mcp__nixos__nix(action="search", query="<pkg>", ...)
c. Plugin's bundled MCP: mcp__plugin_devenv_nixos__nix(action="search", query="<pkg>", ...)use_mcp_search: false, use bash:
nix-shell -p nix --run "nix search nixpkgs <package> --json"nodejs@20):
a. Try mcp__1mcp__nixos_1mcp_nixhub_find_version(package_name="nodejs", version="20")
b. If 1mcp unavailable, try mcp__plugin_devenv_nixos__nix_versions(package="nodejs", version="20")
c. Also search for nixpkgs variants like nodejs_20flake.nix to add packages to the listnix flake lock --update-input nixpkgs to update lockUpgrade packages:
nix flake update to update all inputsRemove packages:
flake.nix to remove selected packagesSetup security tools:
git-hooks input to flake.nixpre-commit-check configurationpre-commit-check.enabledPackages and shellHookchecks.pre-commit-check outputSwitch direnv mode:
direnv-instant in inputs (it's a flake input, NOT a nixpkgs package)direnv in packages list (this IS a nixpkgs package)direnv mode:
○ direnv-instant (Recommended) - Async loading, instant shell prompt
○ Standard direnv - Traditional sync direnv (shell waits for Nix)
○ None - Use nix develop manually
direnv-instant.url = "github:Mic92/direnv-instant"; to inputsdirenv-instant, to outputs parameters++ [ direnv-instant.packages.${system}.default ] to packagesdirenv from packages if presentdirenv-instant input, parameter, and package reference if presentdirenv to packages list (from nixpkgs)direnv-instant input, parameter, and package reference if presentdirenv from packages if present.claude/devenv.local.md with new direnv_mode settingeval "$(direnv-instant hook ...)" instructionseval "$(direnv hook ...)"nix develop manuallyChange welcome style:
shell_welcome setting from .claude/devenv.local.md (default: box)Shell welcome message style:
○ Box style (Default) - Unicode box with devenv branding
○ Minimal - Single line "▸ devenv ready"
○ Project name - Shows current project directory name
○ Tech style - Terminal-inspired "[devenv] :: environment initialized"
○ Custom - Enter your own message
○ None - No welcome message
.claude/devenv.local.md with new shell_welcome (and shell_welcome_custom if applicable)shellHook section in flake.nix:
${pre-commit-check.shellHook} if present at the start of shellHookAfter any modification, validate the flake:
nix flake check --no-build 2>&1
If validation fails, fix the issue before completing the task.
nix-shell -p <tool> --run "<command>" for nix tools - do NOT assume they are installed system-wideflake.nix after any Write/Edit operations.claude/devenv.local.md if it exists, otherwise default to nixos-unstablegit-hooks.nix auto-generates .pre-commit-config.yaml via shellHook - no manual YAML needed!self parameter in flake outputs is REQUIRED by the Nix flake system, even if not explicitly used in the function body. The plugin's deadnix hook is configured with --no-lambda-pattern-names to preserve itnix flake check --no-build before completing any flake creation/modification taskinputs.direnv-instant.url = "github:Mic92/direnv-instant" and referenced as direnv-instant.packages.${system}.default. Standard direnv IS in nixpkgs and can be added directly to the packages list.This plugin uses NixOS MCP tools for package search, providing access to 130K+ NixOS packages with accurate, up-to-date information.
The command tries MCP tools in this order:
mcp__1mcp__nixos_1mcp_nixos_search — already running via 1mcp proxy, no extra processmcp__nixos__nix — if user has mcp-nixos configured globally or in projectmcp__plugin_devenv_nixos__nix — plugin's own MCP server (fallback for non-1mcp environments)nix search commandThis ensures the fastest available tool is used first, with graceful degradation.
1mcp (separate tools per action):
mcp__1mcp__nixos_1mcp_nixos_search(
query="<search_term>",
search_type="packages",
channel="unstable",
limit=10
)
Global/Plugin MCP (combined action parameter):
mcp__nixos__nix( # or mcp__plugin_devenv_nixos__nix
action="search",
query="<search_term>",
source="nixos",
type="packages",
channel="unstable",
limit=10
)
For versioned package requests (e.g., nodejs@20):
1mcp — find specific version:
mcp__1mcp__nixos_1mcp_nixhub_find_version(
package_name="nodejs",
version="20"
)
1mcp — list all available versions:
mcp__1mcp__nixos_1mcp_nixhub_package_versions(
package_name="nodejs",
limit=20
)
Plugin bundled MCP — version lookup:
mcp__plugin_devenv_nixos__nix_versions(
package="nodejs",
version="20"
)
Version resolution workflow:
nixhub_find_version (or nix_versions) to find the exact versionnodejs_20| Tool | Parameter | Value | Description |
|---|---|---|---|
1mcp_nixos_search | query | string | Package name or search term |
1mcp_nixos_search | search_type | "packages" | Search packages (not options) |
1mcp_nixos_search | channel | "unstable" | Nixpkgs channel |
1mcp_nixos_search | limit | number | Max results to return |
1mcp_nixhub_find_version | package_name | string | Package to find version for |
1mcp_nixhub_find_version | version | string | Desired version (e.g., "20") |
1mcp_nixhub_package_versions | package_name | string | Package to list versions for |
1mcp_nixhub_package_versions | limit | number | Max versions to return |
nixos__nix / plugin_devenv_nixos__nix | action | "search" | Search for packages |
nixos__nix / plugin_devenv_nixos__nix | query | string | Package name or search term |
nixos__nix / plugin_devenv_nixos__nix | source | "nixos" | Use NixOS packages source |
nixos__nix / plugin_devenv_nixos__nix | type | "packages" | Search packages (not options) |
nixos__nix / plugin_devenv_nixos__nix | channel | "unstable" | Use nixos-unstable channel |
nixos__nix / plugin_devenv_nixos__nix | limit | number | Max results to return |
plugin_devenv_nixos__nix_versions | package | string | Package to find version for |
plugin_devenv_nixos__nix_versions | version | string | Desired version (optional) |
plugin_devenv_nixos__nix_versions | limit | number | Max versions to return (optional) |
If no MCP tool is available, the command falls back to:
nix-shell -p nix --run "nix search nixpkgs <package> --json"
Users can customize behavior via .claude/devenv.local.md:
---
nixpkgs_channel: nixos-unstable
use_mcp_search: true
direnv_mode: instant
shell_welcome: box
shell_welcome_custom: ""
---
| Setting | Default | Description |
|---|---|---|
nixpkgs_channel | nixos-unstable | Nixpkgs channel to use |
use_mcp_search | true | Use mcp-nixos for package search. Set to false to always use bash fallback |
direnv_mode | instant | direnv integration: instant (async, recommended), standard (sync), or none |
shell_welcome | box | Welcome message style: box, minimal, project, tech, custom, or none |
shell_welcome_custom | "" | Custom welcome message text (only used when shell_welcome: custom). Use \n for multi-line |
| Language | Hook Names |
|---|---|
| JavaScript/TypeScript | eslint, prettier |
| Python | ruff, ruff-format, black, mypy |
| Go | golangci-lint, gofmt |
| Rust | clippy, rustfmt, cargo-check |
| Nix | nixfmt-rfc-style, statix, deadnix |
| Shell | shellcheck, shfmt |
| Docker | hadolint |
| YAML | check-yaml, yamllint |
| Secrets | trufflehog, ripsecrets (built-in); gitleaks requires custom config |
| General | trim-trailing-whitespace, check-added-large-files, check-merge-conflicts |
hooks = {
# Enable a built-in hook
eslint.enable = true;
# Built-in secret scanner
trufflehog.enable = true;
# Enable with custom settings
ruff = {
enable = true;
settings.args = ["--fix"];
};
# Custom hook example: gitleaks (not built-in)
gitleaks = {
enable = true;
name = "gitleaks";
entry = "${pkgs.gitleaks}/bin/gitleaks git --pre-commit --redact --staged --verbose";
language = "system";
pass_filenames = false;
};
};
For full list of available hooks, see: https://github.com/cachix/git-hooks.nix