asdf-vm/asdf
71.4
Strong · 24 September 2026
5.1k
lines of production code
Go
with TypeScript
5
measurements over time
What this system is
This system is a version manager for development tools, rewritten from Bash to Go to improve performance and maintainability. It manages tool installations and versions by handling plugin discovery, repository cloning, and shim script generation. The CLI provides commands for installing, listing, and setting tool versions, while supporting shell completions and diagnostic information.
Features
Added internal hook execution support
The \internal/hook\ package now provides the core logic for executing commands defined in the \asdfrc\ configuration file. This change introduces the \Run\ and \RunWithOutput\ functions, which retrieve specific hook commands (such as \pre\_asdf\_plugin\_add\) from the configuration and execute them with provided arguments, directing output to standard streams. This enables the tool to trigger user-defined scripts at specific lifecycle points, such as before plugin additions.
internal/hook · high confidence
Introduce \`asdf set\` command for managing tool versions
Users can now use the \asdf set\ command to specify tool versions in \.tool-versions\ files. The command supports setting versions for the current directory, the parent directory (via the \--parent\ flag), or the home directory (via the \--home\ flag). It also handles resolving 'latest' version requests by querying plugin callbacks and ensures consistent output formatting with trailing newlines on error messages.
internal/cli/set · high confidence
New CI linting and testing scripts
The repository now includes dedicated scripts for code quality and testing: \scripts/lint.bash\ orchestrates style checks using shfmt, Shellcheck, and a new custom Python linter (\scripts/checkstyle.py\) that enforces specific shell syntax rules (such as replacing \$(pwd)\ with \$PWD\ and standardizing function definitions); \scripts/test.bash\ runs the Bats test suite with optional parallel execution via GNU parallel; and \scripts/install\_dependencies.bash\ automates the installation of required tools (like nushell, fish, and bats-core) within GitHub Actions environments.
scripts · high confidence
New Go-based CLI entrypoint with version and test infrastructure
The \cmd/asdf\ package now serves as the main entrypoint for the CLI, written in Go. It initializes the application with a version string (0.20.2) and appends the Git revision hash for detailed version reporting. Additionally, a new test suite (\main\_test.go\) has been added to run legacy BATS integration tests against the new Go binary, ensuring behavioral parity with the previous Bash implementation for commands like install, plugin management, and shim execution.
cmd · high confidence
New \`asdf info\` command for debugging and diagnostics
A new \info\ command has been added to print diagnostic information about the current asdf installation to STDOUT. This output includes the operating system details, shell and Bash versions, the current asdf version, internal configuration variables (such as \ASDF\_TOOL\_VERSIONS\_FILENAME\, \ASDF\_DATA\_DIR\, and \ASDF\_CONFIG\_FILE\), and a list of installed plugins. Additionally, the command validates the \ASDF\_TOOL\_VERSIONS\_FILENAME\ environment variable and displays warnings if it contains path separators or tilde expansions, helping users identify configuration issues during debugging.
internal/info · high confidence
New internal Git operations module for plugin management
The \internal/git\ package has been introduced to centralize Git operations for asdf plugins, providing a \Repoer\ interface and \Repo\ struct to handle cloning, updating, and inspecting plugin repositories. This implementation uses shallow clones (\--depth 1\) for faster installs, ensures untracked files are preserved during updates, and standardizes locale handling for consistent error messages. Tests verify correct behavior for cloning with specific refs, updating repositories, and preserving local untracked content.
internal/git · high confidence
New internal command execution package with environment variable handling
The internal/execute package has been introduced to wrap Go's os/exec functionality for running Bash commands and expressions. This change ensures that environment variables are consistently propagated to executed commands, merging provided variables with the current system environment while allowing explicit overrides. It also includes logic to handle locale-sensitive operations by setting LANG=C in test contexts, ensuring deterministic behavior for commands like 'type'.
internal/execute · high confidence
New internal exec utility for process replacement
Added a new internal \exec\ package that wraps \syscall.Exec\ to replace the current Go process with a specified executable. This utility requires an absolute path and is used to execute commands with provided arguments and environment variables, supporting the underlying mechanics for commands like \asdf exec\ and \asdf which\.
internal/exec · high confidence
New internal plugin management and index packages
The internal/plugins area now includes the \pluginindex\ and \plugins\ packages, which handle fetching the plugin repository index, caching updates, and managing local plugin installations (add, remove, list). This introduces the underlying logic for plugin discovery and lifecycle management, accompanied by comprehensive unit tests for these operations.
internal/plugins · high confidence
New internal shims package for managing asdf shim scripts
The \internal/shims\ package has been introduced to handle the creation, parsing, and resolution of asdf shim scripts. This includes the \FindExecutable\ function, which resolves shim names to their underlying executables by checking for shim templates, system versions, path versions, and installed plugin versions, while preserving version ordering. The package also defines specific error types for unknown commands, missing versions, and missing executables, and includes logic to prevent the generation of a shim named 'asdf' to avoid shadowing the main asdf command. Tests verify correct behavior for various version resolution scenarios, including ref versions and custom exec-path callbacks.
internal/shims · high confidence
New shell completion scripts for Bash, Zsh, Fish, Elvish, and Nushell
The \internal/completions\ package now provides native shell completion support for five shells: Bash, Zsh, Fish, Elvish, and Nushell. These new files (\asdf.bash\, \asdf.zsh\, \asdf.fish\, \asdf.elvish\, \asdf.nushell\) implement context-aware completions for \asdf\ subcommands (such as \plugin\, \install\, \list\, \set\) and their arguments (plugin names, versions, flags). A Go helper (\completions.go\) embeds these scripts and exposes them via \Get\ and \Names\ functions, allowing the CLI to serve the correct completion code for the user's shell.
internal/completions · high confidence
New tool version file parsing and management logic
The \internal/toolversions\ package introduces core logic for reading, writing, and parsing \.tool-versions\ files. It adds support for structured version types (version, ref, path, system, latest) and provides functions to find, update, and list tool versions while preserving file comments and order. This change establishes the foundation for the \asdf set\ and \asdf uninstall\ commands by handling the underlying file I/O and version string parsing.
internal/toolversions · high confidence
Architecture
asdf rewritten in Go with new build and configuration infrastructure
The asdf version manager has been rewritten in Go, replacing the previous Bash implementation. This change introduces a new build system using a Makefile and Go tooling (gofumpt, staticcheck, revive), updates the development environment to use Go 1.26.3, BATS 1.8.2, ShellCheck 0.10.0, and shfmt 3.6.0 as defined in .tool-versions, and establishes new repository configuration files including .editorconfig, .gitattributes, .gitignore, and .release-please-config.json. The legacy asdf.sh script has been removed, and the README has been updated to reflect the new Go-based architecture and link to the updated documentation site.
(repo-wide) · high confidence
Behavioural changes
Exclude incomplete tool installations from the installed list
The \asdf\ tool now correctly filters out partially installed versions when listing installed tools. Previously, directories created during an installation process might appear as fully installed even if the installation failed or was interrupted. This change introduces an 'incomplete marker' file mechanism: if a marker file exists in the install lock directory for a specific version, that version is excluded from the list of installed versions returned by commands like \asdf list\. This ensures users only see versions that have successfully completed their installation process.
internal/installs · high confidence
New Go-based version management implementation
The \internal/versions\ package has been rewritten in Go, introducing core functions for installing and uninstalling tool versions (\Install\, \InstallOneVersion\, \InstallAll\) and resolving version strings (including \latest\ with regex filtering). This change adds comprehensive unit tests for the version installation logic and includes static test data fixtures for Elixir, Python, and Ruby version lists to support validation of the new version resolution and listing capabilities.
internal/versions · high confidence
New structured help system with plugin extension support
The \asdf help\ command now uses a new internal Go implementation that displays the asdf version, standard command documentation, and any custom extension commands provided by installed plugins. Users can also view specific documentation for a plugin or a specific tool version via \asdf help \<name\>\ and \asdf help \<name\> \<version\>\, which invoke plugin callbacks to render overviews, dependencies, configuration, and links. This replaces the previous Bash-based help logic with a more robust, embeddable text template and structured output generation.
internal/help · high confidence
New version resolution logic with environment variable support and legacy file fallback
The \internal/resolve\ package introduces a new mechanism for determining tool versions. It prioritizes versions specified via environment variables (formatted as \ASDF\_\<PLUGIN\>\_VERSION\, with hyphens in plugin names converted to underscores) over local configuration files. If no environment variable is set, it searches for the standard \.tool-versions\ file in the current directory and its parents. As a fallback, if legacy file support is enabled, it checks for plugin-specific legacy version files. Additionally, if no version is found in the directory tree, it falls back to checking the user's home directory.
internal/resolve · high confidence
Rewrite of CLI framework to urfave/cli v3 with new command structure
The internal CLI implementation has been rewritten to use the urfave/cli v3 library, replacing the previous version. This change introduces a new command structure including \asdf set\, \asdf current\ (with a \--no-header\ flag), and \asdf install\ (with a \--keep-download\ flag), while removing the deprecated \asdf update\ command in favor of OS package managers. The rewrite also corrects help flag behavior, ensures proper environment variable propagation for shims, and fixes exit status codes for invalid commands and missing plugins.
internal/cli · high confidence
Fixes
Add utility to safely remove specific paths from the system PATH
A new \internal/paths\ package has been introduced with a \RemoveFromPath\ function that correctly handles platform-specific path separators (using \os.PathListSeparator\) to remove specific directories from the PATH environment variable. This ensures that asdf-related shim paths are cleanly removed from the user's PATH without breaking path parsing on different operating systems, addressing issues where hardcoded separators caused incorrect path manipulation.
internal/paths · high confidence
Fix environment variable parsing to support equals signs and newlines
The exec-env callback handling in internal/execenv now correctly preserves environment variable values that contain equals signs (=) and newline characters. Previously, such values might have been truncated or corrupted during parsing; the updated logic ensures that complex values, including multi-line strings like certificates, are passed through unchanged to the execution environment.
internal/execenv · high confidence
New Go-based configuration subsystem for asdf
The internal configuration logic has been rewritten in Go, introducing a new \internal/config\ package that unifies loading settings from the \asdfrc\ file and environment variables. This change brings support for the renamed \ASDF\_TOOL\_VERSIONS\_FILENAME\ environment variable (with fallback to the legacy name), correct handling of tilde expansion in paths like \ASDF\_DATA\_DIR\, and concurrency settings that align with documentation (supporting \auto\ and explicit values). It also removes the previously unused \ForcePrepend\ option and adds tests to verify correct parsing of settings, environment variable precedence, and hook commands.
internal/config · high confidence
Test coverage
Add dummy plugin fixtures for testing; Added asdfrc test fixture for legacy version file handling; Added legacy plugin test fixture; Added test fixtures for broken plugin scripts; Expanded test coverage for asdf commands and shell integrations; New installtest package for simplified tool installation in tests; New internal test helpers for managing asdf plugin repositories.
Dependencies
Initialize Go module and migrate docs to VitePress
The project now includes a formal Go module definition (go.mod) for github.com/asdf-vm/asdf, establishing Go 1.26.3 as the runtime and pinning dependencies such as urfave/cli/v3, go-git/v5, and golang.org/x/crypto. Simultaneously, the documentation build system has been replaced with VitePress (v1.6.4), configured as an ES module in docs/package.json with updated type definitions for Node.
(dependencies) · high confidence
Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.
How this codebase got here
This is the PUBLIC form of this artifact. Findings are listed in full, but the details of SECURITY findings — which rule fired, in which file, on which line, and how to fix it — are deliberately withheld, and any secret-scanner results are excluded entirely. Where detail is absent here it was REMOVED FOR PUBLICATION; it is not missing from the analysis. The complete artifact is available from the repository owner.
Score
- CAI 68 → 71 (+3.7)
- Rubric changed (rubric-2026.08.19 → rubric-2026.09.15) — scores are not directly comparable.
Lenses
- Code Health 87 → 91 (+3.5)
- Architecture 100 → 95 (-4.9)
- Maturity 60 → 64 (+3.5)
- Readiness 73 → 81 (+8.1)
- Security 67 → 70 (+3.3)
Resolved (23)
- Coverage not included — suite not readable by the collector
- Dependency hygiene not measured — dependency manifest found but not parsed for hygiene
- Duplicated block (11 lines × 2) (internal/cli/cli.go)
- Duplicated block (12 lines × 2) (internal/cli/cli.go)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- Medium CVE: GO-2026-4970 (go.mod)
- Medium CVE: GO-2026-5942 (go.mod)
- Medium CVE: GO-2026-5970 (go.mod)
- Medium vulnerability: GO-2026-5932 (go.mod)
- …and 3 more
New (54)
- Documentation: no installation or build instructions (README.md)
- Duplicated block (10 lines × 2) (internal/resolve/resolve.go)
- Duplicated block (12–13 lines × 2) (internal/cli/cli.go)
- Duplicated block (18 lines × 2) (internal/cli/cli.go)
- Duplicated block (5 lines × 2) (internal/help/help.go)
- Duplicated block (9 lines × 2) (internal/cli/cli.go)
- Fork-triggerable workflow runs with an unscoped write token
- FunctionTooLong: cli.Execute (internal/cli/cli.go)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- …and 34 more
Changes since last survey
- 10 commits — 6 feature/other, 4 fixes
By area
- (root) — 6 commits
- docs/manage — 2 commits
- docs/package-lock.json — 1 commit
- internal/shims — 1 commit
Notable commits
- fix: fix: fix release (#2337)
- fix: fix: never generate a shim named "asdf" during reshim (#2307)
- fix: fix: override urfave/cli built-in help printer for top level asdf -h/--help (#2328)
- fix: fix: prevent incomplete installations from being shown as installed (#2303)
- change: chore: bump @types/node from 26.1.2 to 26.4.0 in /docs in the docs group (#2320)
- change: chore: bump github.com/go-git/go-git/v5 from 5.19.1 to 5.19.2 (#2312)
- change: chore: release 0.20.1 (#2295)
- change: chore: release 0.20.2 (#2336)
- change: docs: explain env vars for dashed tool names (#2317)
- change: docs: fix the ordering of paragraphs in the versions page (#2318)
Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.
Survey your own repository
asdf-vm/asdf was measured the same way every project in this corpus was: the same rubric, at a pinned commit, with the result published in full. Point a surveyor at a repository you know and see whether you agree with it.
About this page
- The score is its most recent published measurement, taken on 24 September 2026 at a pinned commit. It is not a live figure and does not change until the project is measured again.
- Measured at commit cb72590e9f9691b6e651aecfe4b0c9334adff41b — the exact code this score is about.
- Scored under rubric-2026.09.15 — the same rubric and the same method as every other entry in this index.
- Measured by watchdog.canine.dev using codehealth-analyzer preprod-5f8d0eb43fd7.