This file provides guidance to AI coding agents when working with code in this repository. It keeps the CLAUDE.md filename for compatibility with existing tooling, and AGENTS.md symlinks to it for other agents.
Most registry PRs from agents get rejected. Before adding anything to registry/, understand the rules:
- mise does not host self-written, personal, niche, or low-popularity tools. The registry is curated for tools that are already widely used. "It works" or "it has tests" is not the bar.
- Tools that aren't VERY popular will be rejected without explanation. Per contributing.md: "@jdx won't explain why a given tool wasn't accepted." There is no appeal, no checklist, no second chance — the PR is closed and that's it.
- Wasted PRs are the default outcome for tools the agent or user has not vetted against this bar. Do not submit one speculatively.
Before touching registry/, ALWAYS do the following:
-
Ask the user: "Is this tool already widely used outside your own projects?" — if it's the user's own tool, a fork, an internal/company tool, or something with a small audience, stop and tell them the PR will be rejected. Do not submit.
-
Actively check popularity for every registry PR — no exceptions. Look up real numbers; do not guess. Useful sources:
- GitHub stars and fork count (
gh repo view owner/repo --json stargazerCount,forkCount) - Recent release activity / last commit date (
gh repo view owner/repo --json pushedAt,latestRelease) - Download counts on relevant package registries (npm
npmjs.com/package/x, crates.io, PyPI, Homebrew analytics, etc.) - Whether the project shows up in third-party docs, awesome-lists, or other tools
- GitHub stars and fork count (
-
Apply the bar. Rough signals — very low numbers are disqualifying:
- GitHub stars in the thousands, not hundreds
- Active maintenance (recent releases, not abandoned)
- Real third-party usage (referenced in docs, blog posts, other tools, package registries)
- Recognizable in its ecosystem
-
Include the popularity data in the PR description. Every registry PR body MUST contain a short section like:
## Popularity - GitHub: 12.3k stars, 480 forks, last release 2026-04-12 - crates.io: 1.2M downloads - Used by: <project A>, <project B>This is non-negotiable — it lets the maintainer evaluate the submission without re-doing the research. PRs without it look speculative and are more likely to be rejected.
-
If the tool is borderline or numbers are low, warn the user clearly that the PR is likely to be rejected without reason, and ask if they still want to proceed. Do not soften this — users have repeatedly been surprised when their PR was closed, and the agent should have warned them up front.
-
Suggest the alternative: users can install any tool themselves via explicit backend syntax (
mise use aqua:owner/repo,mise use github:owner/repo,mise use cargo:name,mise use npm:name, etc.) or by writing a tool plugin. The registry is only for shorthand convenience for popular tools — not for enabling installation.
For registry entries the backend tiers are:
- Tier 1 — preferred:
aqua:,github:, andgitlab:. These are the routinely accepted backends.- Prefer
aqua:when the tool is in the aqua registry. Better UX, SLSA verification, and per-version logic. - Use
github:when the tool isn't in aqua but ships GitHub releases. - Use
gitlab:for tools released through GitLab.
- Prefer
- Tier 2 — high bar, but lower than tier 3:
conda:. Potentially acceptable when the tool can't be supported via aqua/github. The bar is lower than tier 3 because the conda backend in mise does not require a separately-installed package manager — mise downloads and extracts packages directly from anaconda.org via rattler, so users don't need conda/mamba on PATH. Still requires a popular, well-maintained tool. - Tier 3 — extremely high bar, almost never accepted:
npm:,pipx:,gem:,cargo:,go:,dotnet:. These all rely on a separately-installed runtime/toolchain being present on PATH (node,python,ruby,cargo,go,dotnet), which is fragile — the wrong version, a missing install, or PATH ordering quirks all break them.npm:/pipx:/gem:are particularly painful because tools installed via them silently bind to whichevernode/python/rubywas on PATH at install time. Don't reach for these for a registry PR unless the user has explicitly confirmed @jdx wants it that way for this specific tool. - Not accepted at all:
- New
asdf:plugins — supply-chain security. Use aqua/github instead. - New
vfox:plugins — same reason. Use aqua/github instead. ubi:is deprecated and will not be accepted under any circumstances.
- New
Users can still install via any backend themselves with explicit syntax (mise use vfox:..., mise use cargo:..., etc.) — they just don't get a registry shorthand for it.
mise run buildormise run b- Build the project with cargotarget/debug/mise- Run the built binary directlymise run testormise run t- Run all tests (unit + e2e)mise run test:unit- Run unit tests onlymise run test:e2e- Run end-to-end tests onlymise run snapshots- Update test snapshots withcargo insta
- Use
MISE_DEBUG=1orMISE_TRACE=1environment variables to enable debug output (notRUST_LOG)
mise run lint- Run all linting tasksmise run lint-fix- Run linting and automatically fix issuesmise run format- Format code (part of CI task)mise run ci- Run format, build, and testmise run test:e2e [test_filename]...- Run specific e2e tests (use this instead of executing test files directly)mise --cd crates/vfox run test- Run tests for the vfox cratemise --cd crates/vfox run lint- Run linting for the vfox cratemise --cd crates/vfox run lint-fix- Run linting and fix issues for the vfox cratemise task ls- List all available tasks
mise run render- Generate all documentation and completionsmise run render:usage- Generate CLI usage documentationmise run render:completions- Generate shell completionsmise run docs- Start documentation dev servermise run docs:build- Build documentation
mise run install-dev- Install development version locallymise run clean- Clean cargo build artifacts
Mise is a Rust CLI tool that manages development environments, tools, tasks, and environment variables. The codebase follows a modular architecture:
Core Components:
src/main.rs- Entry point and CLI initializationsrc/cli/- Command-line interface implementation with subcommandssrc/config/- Configuration file parsing and managementsrc/backend/- Tool backend implementations (asdf, vfox, cargo, npm, etc.)src/toolset/- Tool version management and installation logicsrc/task/- Task execution systemsrc/plugins/- Plugin system for extending tool support
Key Backend Systems:
src/backend/asdf.rs- ASDF plugin compatibilitysrc/backend/vfox.rs- VersionFox plugin systemsrc/backend/cargo.rs- Rust Cargo tool backendsrc/backend/npm.rs- Node.js/npm tool backendsrc/backend/github.rs- GitHub releases backendsrc/backend/aqua.rs- Aqua tool registry integration
Core Tools (Built-in):
src/plugins/core/- Built-in tool implementations (Node, Python, Go, Ruby, etc.)
Configuration System:
mise.tomlfiles for project configuration.tool-versionsfiles for ASDF compatibility- Environment variable management and templating
- Task definition and execution
- Backend Architecture: Tools are implemented through a unified backend interface, allowing multiple sources (ASDF plugins, vfox plugins, cargo, npm, etc.)
- Toolset Management: The
Toolsetmanages collections of tool versions and their installation state - Configuration Layering: Config files are loaded hierarchically from system → global → local with environment-specific overrides
- Task System: Tasks can be defined in TOML files with dependencies, environment variables, and multiple execution modes
mise.toml- Main configuration file formatsettings.toml- Global settings definitions (generates code/docs)registry/- Tool registry mappingstasks.toml- Project task definitions
- Unit tests within source files
- E2E tests in
e2e/directory organized by feature area (e.g.,e2e/cli/,e2e/backend/) - E2E tests are bash scripts using assertion helpers from
e2e/assert.sh(e.g.,assert,assert_contains,assert_fail) - E2E tests do not need cleanup steps (rm, etc.) — the test harness handles that
- Snapshot tests using
instacrate for CLI output verification - Windows-specific tests in
e2e-win/
- Rust project using Cargo with workspace for
crates/vfox - Custom build script in
build.rsfor generating metadata - Multiple build profiles including
releaseandserious(with LTO) - Cross-compilation support via
Cross.toml
All commit messages and PR titles MUST follow conventional commit format:
Format: <type>(<scope>): <description>
Types:
feat:- New featuresfix:- Bug fixes that affect the CLI behavior (not CI, docs, or infrastructure)refactor:- Code refactoringdocs:- Documentation changesstyle:- Code style/formatting (no logic changes)perf:- Performance improvementstest:- Testing changeschore:- Maintenance tasks, releases, dependency updates, CI/infrastructure changessecurity:- Security-related changesregistry:- Any changes toregistry/(no scope needed, use for both new tools and fixes)
Scopes:
- For command-specific changes, use the command name:
install,activate,use,exec, etc. - For subsystem changes:
config,backend,env,task,vfox,python,github,release,completions,http,schema,doctor,shim,core,deps,ci - Use
task(notrun) for task-related changes, even if the code lives insrc/cli/run.rsorsrc/cmd.rs
Description Style:
- Use lowercase after the colon
- Use imperative mood ("add feature" not "added feature")
- Keep it concise but descriptive
Examples:
fix(install): resolve version mismatch for previously installed toolsfeat(activate): add fish shell supportfeat(vfox): add semver Lua module for version sortingfeat(env): add environment caching with module cacheability supportdocs(contributing): update hk usageschore: release 2026.1.6chore(ci): add FORGEJO_TOKEN for API authenticationregistry: add miller
- Run
hk install --miseonce to set up pre-commit hooks (runshk fixautomatically on commit) - Run
mise run lint-fixandgit addany lint fixes before committing - Use
mise run test:e2e [test_filename]...for running specific e2e tests - Never run e2e tests by executing them directly - always use the mise task
When deprecating a feature or backend:
- Immediately: Mark as deprecated in docs (add warning banner)
- 6 months later (
warn_at): Display deprecation warning in CLI usingdeprecated_at!macro fromsrc/output.rs - 12 months after warn (
remove_at):debug_assert!indeprecated_at!fires, signaling the code should be removed
Use mise version format for dates (e.g., deprecated_at!("2026.10.0", "2027.10.0", "id", "message")).
If the replacement has been available for a long time, the CLI warning can start immediately (set warn_at to the current version).
When implementing new tool backends, follow the pattern in src/backend/mod.rs. Each backend must implement the Backend trait with methods for listing versions, installing tools, and managing tool metadata.
Do not assume tool versions follow semver or any other orderable scheme. mise manages hundreds of tools with wildly different versioning conventions:
- Date-based:
2024.01.15,20241015 - Pre-release / ref / tag versions:
tip,HEAD,nightly,edge,canary,ref:main,tag:v1,sub-X.Y:... - Non-numeric tags: Python
3.12.0a1, Ruby3.2.0-preview1, Go1.22rc1, Nodelts/hydrogen,lts-iron - Tool-specific meanings of
latest(e.g. some exclude pre-releases, some don't)
Rules:
- Do not call
versions::Versioning::new(...)(or any other semver comparator) at a new call site to pick the "newest" version, "resolve latest", or sort a version list. That crate silently returnsNone/ arbitrary ordering for non-semver strings, which means wrong versions get chosen for many tools. - To resolve a version request (
latest, a prefix, a channel name), delegate to the backend viaBackend::latest_version,Backend::latest_installed_version,Backend::list_versions_matching, orToolRequest::resolve— the backend knows what "latest" means for its tool. - To list installed versions in a meaningful order, use
Backend::list_installed_versions_matchingor the toolset's resolved versions. Do not reorder them yourself. - Lockfile version strings must be treated as opaque — compare with
==, never with a version ordering. Never write a non-concrete string (latest,lts/*, a prefix) into the lockfile; resolve first.
A few existing call sites (e.g. runtime symlinks) do use Versioning ordering today, but that's legacy behavior and arguably also wrong — do not point at them to justify new semver assumptions.
If you think you need to pick "the newest installed version" at a new call site, stop and ask — that call almost always belongs on the backend, not inline.
- Core tools are implemented in
src/plugins/core/ - External plugins use ASDF or vfox compatibility layers
- Plugin metadata is defined in
mise.plugin.tomlfiles
The configuration system supports multiple file formats and environment-specific configs. Changes to settings require updating settings.toml and running mise run render:schema.
- E2E tests are organized by feature area (cli/, config/, backend/, etc.)
- Use snapshot testing for CLI output verification
- Backend-specific tests verify tool installation and version management
- Slow tests (marked with
_slowsuffix) test actual tool compilation/installation
- Windows-specific implementations in files ending with
_windows.rs - Platform-specific tool installation logic in core plugins
- Shim system varies by platform (especially Windows)
- we don't chmod mise e2e tests to be executable
When posting comments on GitHub PRs or discussions, always include a note that the comment was AI-generated (e.g., "This comment was generated by an AI coding assistant.").
When referencing mise documentation URLs, use the correct path structure based on the docs/ directory layout:
- Dev tools & backends:
mise.en.dev/dev-tools/backends/<backend>.html(e.g.,mise.en.dev/dev-tools/backends/s3.html) - Configuration:
mise.en.dev/configuration/... - Tasks:
mise.en.dev/tasks/... - Environments:
mise.en.dev/environments/... - CLI reference:
mise.en.dev/cli/...
Do NOT use shortened paths like mise.en.dev/backends/... - always include the full path matching the docs/ directory structure.