vx-provider-updater

Update existing VX providers to RFC 0018 + RFC 0019 standards with layout configuration and system package manager integration.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "vx-provider-updater" with this command: npx skills add loonghao/vx/loonghao-vx-vx-provider-updater

VX Provider Updater

Update existing VX providers to RFC 0018 + RFC 0019 standards with layout configuration and system package manager integration.

When to Use

  • Updating existing provider.toml to add layout configuration

  • Migrating from custom post_extract hooks to RFC 0019

  • Migrating from Rust runtime.rs to Starlark provider.star

  • Adding MSI install support for Windows using Starlark

  • Adding system package manager fallback for tools without portable binaries

  • Adding missing license field to provider.toml (all providers MUST have it)

  • Standardizing provider manifests

  • Fixing download/installation issues

  • Fixing "No download URL" or "Executable not found" errors

  • Batch updating multiple providers

License Field Requirement

When updating any provider.toml, ensure the license field exists under [provider] :

[provider] name = "example" license = "MIT" # SPDX identifier (REQUIRED)

license_note = "..." # Optional notes

Blocked licenses (AGPL-3.0, SSPL, CC BY-NC) must NOT be integrated as providers. See the vx-provider-creator skill for the full license compatibility guide.

Quick Reference

Tool Categories

Category Layout Type Examples

Single Binary binary

kubectl, ninja, rust

Standard Archive (bin/) archive

  • strip node, go, cmake

Root Directory archive (no strip) terraform, just, deno

Platform Directory archive

  • platform strip helm, bun

Binary + Registry Clone binary

  • git clone vcpkg (downloads binary, clones registry)

Hybrid (Download + PM) binary/archive

  • system_install

imagemagick, ffmpeg, docker

npm/pip Packages No layout needed vite, pre-commit

System Tools Detection only git, docker, openssl

System PM Only system_install only make, curl, openssl

Update Templates

Template 1: Single File Binary

适用于: kubectl, ninja, rustup-init 等单文件下载

RFC 0019: Executable Layout Configuration

[runtimes.layout] download_type = "binary"

[runtimes.layout.binary."windows-x86_64"] source_name = "tool.exe" target_name = "tool.exe" target_dir = "bin"

[runtimes.layout.binary."macos-x86_64"] source_name = "tool" target_name = "tool" target_dir = "bin" target_permissions = "755"

[runtimes.layout.binary."macos-aarch64"] source_name = "tool" target_name = "tool" target_dir = "bin" target_permissions = "755"

[runtimes.layout.binary."linux-x86_64"] source_name = "tool" target_name = "tool" target_dir = "bin" target_permissions = "755"

[runtimes.layout.binary."linux-aarch64"] source_name = "tool" target_name = "tool" target_dir = "bin" target_permissions = "755"

插入位置: 在 [runtimes.versions] 之后,[runtimes.platforms] 之前

Template 2: Standard Archive with bin/

适用于: node, go, python, cmake 等标准压缩包

RFC 0019: Executable Layout Configuration

[runtimes.layout] download_type = "archive"

[runtimes.layout.archive] strip_prefix = "{name}-{version}" # 或其他模式 executable_paths = [ "bin/{name}.exe", # Windows "bin/{name}" # Unix ]

常见 strip_prefix 模式:

  • Node.js: node-v{version}-{os}-{arch}

  • Go: go

  • CMake: cmake-{version}-{os}-{arch}

  • Python: python

Template 3: Root Directory Executable

适用于: terraform, just, task, deno 等根目录可执行文件

RFC 0019: Executable Layout Configuration

[runtimes.layout] download_type = "archive"

[runtimes.layout.archive] strip_prefix = "" # 无前缀 executable_paths = [ "{name}.exe", # Windows (root directory) "{name}" # Unix (root directory) ]

Template 4: Platform-Specific Directory

适用于: helm, bun 等按平台分目录的压缩包

RFC 0019: Executable Layout Configuration

[runtimes.layout] download_type = "archive"

[runtimes.layout.archive] strip_prefix = "{os}-{arch}" # 或 "bun-{os}-{arch}" executable_paths = [ "{name}.exe", # Windows "{name}" # Unix ]

Template 5: Complex Nested Structure

适用于: java, ffmpeg 等复杂结构

RFC 0019: Executable Layout Configuration

[runtimes.layout] download_type = "archive"

[runtimes.layout.archive] strip_prefix = "jdk-{version}+{build}" # 根据实际情况调整 executable_paths = [ "bin/java.exe", # Windows "bin/java" # Unix ]

Template 6: npm/pip Packages

适用于: vite, pre-commit, release-please 等包管理器安装

[runtimes.versions] source = "npm" # 或 "pypi" package = "{package-name}"

Note: npm/pip packages don't need layout configuration

They are installed via package manager and have standard locations

Template 7: System Tools (Detection Only)

适用于: git, docker, curl, openssl 等系统工具

Note: System tools typically installed by OS package manager

Use detection to find system-installed versions

[runtimes.detection] command = "{executable} --version" pattern = "{name} version ([\d.]+)" system_paths = [ "/usr/bin/{name}", "/usr/local/bin/{name}", "C:\Program Files\{Name}\bin\{name}.exe" ] env_hints = ["{NAME}_HOME"]

Template 8: Hybrid Provider (Direct Download + Package Manager Fallback)

适用于: imagemagick, ffmpeg, docker 等部分平台有二进制、部分平台需要包管理器的工具

Direct download for platforms with portable binaries (e.g., Linux AppImage)

[runtimes.layout] download_type = "binary"

[runtimes.layout.binary."linux-x86_64"] source_name = "tool-{version}-linux-x64.AppImage" target_name = "tool" target_dir = "bin" target_permissions = "755"

No Windows/macOS configs = download_url returns None, triggers PM fallback

System dependencies (package managers required for installation)

macOS requires Homebrew

[[runtimes.system_deps.pre_depends]] type = "runtime" id = "brew" platforms = ["macos"] reason = "Required to install tool on macOS (no portable binary available)" optional = false

Windows: winget (preferred), choco, or scoop (any one is sufficient)

[[runtimes.system_deps.pre_depends]] type = "runtime" id = "winget" platforms = ["windows"] reason = "Preferred package manager for Windows (built-in on Windows 11)" optional = true

[[runtimes.system_deps.pre_depends]] type = "runtime" id = "choco" platforms = ["windows"] reason = "Alternative to winget for Windows installation" optional = true

System installation strategies

[[runtimes.system_install.strategies]] type = "package_manager" manager = "brew" package = "tool" platforms = ["macos"] priority = 90

[[runtimes.system_install.strategies]] type = "package_manager" manager = "winget" package = "Publisher.Tool" # winget uses Publisher.Package format platforms = ["windows"] priority = 95 # Highest on Windows (built-in on Win11)

[[runtimes.system_install.strategies]] type = "package_manager" manager = "choco" package = "tool" platforms = ["windows"] priority = 80

[[runtimes.system_install.strategies]] type = "package_manager" manager = "scoop" package = "tool" platforms = ["windows"] priority = 60

Template 9: System Package Manager Only

适用于: make, curl 等所有平台都没有可移植二进制的工具

[[runtimes]] name = "tool" description = "Tool description" executable = "tool"

No layout configuration (no direct download)

All platforms need package managers

[[runtimes.system_deps.pre_depends]] type = "runtime" id = "brew" platforms = ["macos"] reason = "Required to install tool on macOS" optional = false

[[runtimes.system_deps.pre_depends]] type = "runtime" id = "winget" platforms = ["windows"] reason = "Preferred package manager for Windows" optional = true

[[runtimes.system_deps.pre_depends]] type = "runtime" id = "choco" platforms = ["windows"] optional = true

System installation strategies for all platforms

[[runtimes.system_install.strategies]] type = "package_manager" manager = "brew" package = "tool" platforms = ["macos", "linux"] priority = 90

[[runtimes.system_install.strategies]] type = "package_manager" manager = "apt" package = "tool" platforms = ["linux"] priority = 90

[[runtimes.system_install.strategies]] type = "package_manager" manager = "dnf" package = "tool" platforms = ["linux"] priority = 85

[[runtimes.system_install.strategies]] type = "package_manager" manager = "winget" package = "Publisher.Tool" platforms = ["windows"] priority = 95

[[runtimes.system_install.strategies]] type = "package_manager" manager = "choco" package = "tool" platforms = ["windows"] priority = 80

Update Workflow

Step 1: Identify Tool Type

Check current provider.toml

cat crates/vx-providers/{name}/provider.toml

Questions to answer:

  • Is it a single binary or archive?

  • What's the download URL format?

  • What's the internal structure after extraction?

  • Are there platform-specific differences?

Step 2: Choose Template

Use decision tree:

Does the tool provide portable binaries for ALL platforms? ├─ Yes → Is it a binary download (single file)? │ ├─ Yes → Template 1 (Binary) │ └─ No → Is it an archive? │ ├─ Has bin/ directory? → Template 2 (Standard Archive) │ ├─ Executable in root? → Template 3 (Root Directory) │ ├─ Platform subdirs? → Template 4 (Platform Directory) │ └─ Complex? → Template 5 (Complex) ├─ Partial (some platforms have binaries) → Template 8 (Hybrid) │ └─ Examples: imagemagick (Linux AppImage, macOS/Windows via PM) │ └─ Examples: ffmpeg (Windows binary, macOS/Linux via brew/apt) └─ No (no portable binaries) → Check installation method ├─ npm/pip package? → Template 6 (Package Manager) ├─ System tool (curl, openssl)? → Template 7 (Detection Only) └─ Can be installed via PM? → Template 9 (System PM Only)

├─ Yes → Template 6 (Package Manager)
└─ No → Template 7 (System Tool)

Step 3: Add Configuration

  1. Open crates/vx-providers/{name}/provider.toml
  2. Locate [runtimes.versions] section
  3. Add layout configuration after versions, before platforms
  4. Save file

Step 4: Verify Format

Checklist:

  • download_type is "binary", "archive", or "git_clone"
  • Important: Use snake_case for values (e.g., git_clone NOT git-clone)
  • For binary: All platforms have configuration
  • For binary: Unix platforms have target_permissions = "755"
  • For archive: strip_prefix matches actual structure
  • For archive: executable_paths includes Windows and Unix
  • Paths use forward slashes /, not backslashes
  • Variables like {version}, {os}, {arch} are correct

Step 5: Test

# Build
cargo build --release

# Test installation
vx install {name}@{version}

# Verify
vx which {name}
vx {name} --version

Batch Update Script

For updating multiple providers at once:

// Create a batch update plan
let providers_to_update = vec![
    ("kubectl", LayoutType::Binary),
    ("terraform", LayoutType::ArchiveRoot),
    ("helm", LayoutType::ArchivePlatform),
    ("just", LayoutType::ArchiveRoot),
    ("task", LayoutType::ArchiveRoot),
];

for (name, layout_type) in providers_to_update {
    update_provider(name, layout_type)?;
}

Common Patterns

Pattern: Version in Binary Name

[runtimes.layout.binary."windows-x86_64"]
source_name = "yasm-{version}-win64.exe"  # {version} auto-replaced
target_name = "yasm.exe"
target_dir = "bin"

Pattern: Platform Variations

[runtimes.layout.archive]
strip_prefix = "node-v{version}-{os}-{arch}"  # All replaced
# {os} → windows, linux, darwin
# {arch} → x86_64, aarch64

Pattern: JavaScript Executables

[runtimes.layout.archive]
strip_prefix = "yarn-v{version}"
executable_paths = [
    "bin/yarn.js"  # JavaScript file, not native binary
]

Pattern: Windows Only

[[runtimes]]
name = "rcedit"
description = "Windows resource editor"
executable = "rcedit"

[runtimes.layout]
download_type = "binary"

# Only Windows platform
[runtimes.layout.binary."windows-x86_64"]
source_name = "rcedit-x64.exe"
target_name = "rcedit.exe"
target_dir = "bin"

[runtimes.platforms.windows]
executable_extensions = [".exe"]

Migration: From Rust runtime.rs to Starlark provider.star

Starlark (provider.star
) is the preferred way to implement providers. It replaces runtime.rs
 and config.rs
 with pure-computation scripts that are easier to read, write, and maintain.

When to Migrate

- Provider has custom download_url()
 logic in config.rs

- Provider has post_extract()
 or install()
 overrides in runtime.rs

- Provider needs MSI install support on Windows

- Provider needs system package manager fallback

- Any new provider being created

Migration Steps

Step 1: Create provider.star

Create crates/vx-providers/{name}/provider.star
 with the equivalent logic:

Before (Rust config.rs):

pub fn download_url(version: &str, platform: &Platform) -> Option<String> {
    let triple = match (&platform.os, &platform.arch) {
        (Os::Windows, Arch::X86_64) => "x86_64-pc-windows-msvc",
        (Os::MacOS, Arch::Aarch64)  => "aarch64-apple-darwin",
        (Os::Linux, Arch::X86_64)   => "x86_64-unknown-linux-musl",
        _ => return None,
    };
    let ext = if platform.os == Os::Windows { "zip" } else { "tar.gz" };
    Some(format!("https://github.com/owner/repo/releases/download/v{}/tool-v{}-{}.{}",
        version, version, triple, ext))
}

After (Starlark provider.star):

load("@vx//stdlib:github.star",   "make_fetch_versions", "github_asset_url")
load("@vx//stdlib:platform.star", "is_windows")

fetch_versions = make_fetch_versions("owner", "repo")

def _triple(ctx):
    os   = ctx["platform"]["os"]
    arch = ctx["platform"]["arch"]
    return {
        "windows/x64":  "x86_64-pc-windows-msvc",
        "macos/arm64":  "aarch64-apple-darwin",
        "linux/x64":    "x86_64-unknown-linux-musl",
    }.get("{}/{}".format(os, arch))

def download_url(ctx, version):
    triple = _triple(ctx)
    if not triple:
        return None
    os  = ctx["platform"]["os"]
    ext = "zip" if os == "windows" else "tar.gz"
    asset = "tool-v{}-{}.{}".format(version, triple, ext)
    return github_asset_url("owner", "repo", "v" + version, asset)

def install_layout(ctx, version):
    os  = ctx["platform"]["os"]
    exe = "tool.exe" if os == "windows" else "tool"
    return {
        "type":             "archive",
        "strip_prefix":     "tool-v{}-{}".format(version, _triple(ctx) or ""),
        "executable_paths": [exe, "tool"],
    }

def environment(ctx, version, install_dir):
    return {"PATH": install_dir}

def deps(ctx, version):
    return []

Step 2: Simplify provider.toml

After creating provider.star
, the provider.toml
 only needs metadata (remove layout fields):

[provider]
name = "mytool"
description = "My awesome tool"
homepage = "https://example.com"
repository = "https://github.com/owner/repo"
ecosystem = "devtools"
license = "MIT"

Step 3: Remove Rust files (if manifest-only)

If the provider was Rust-only (no provider.toml
), you can keep the Rust factory or convert to manifest-only. For manifest+star providers, the Rust files are optional.

Starlark Standard Library Quick Reference

Module
Load Path
Key Functions

GitHub
@vx//stdlib:github.star

make_fetch_versions(owner, repo)
, make_download_url(owner, repo, template)
, make_github_provider(owner, repo, template)
, github_asset_url(owner, repo, tag, asset)

Platform
@vx//stdlib:platform.star

is_windows(ctx)
, is_macos(ctx)
, is_linux(ctx)
, is_x64(ctx)
, is_arm64(ctx)
, platform_triple(ctx)
, platform_ext(ctx)
, exe_ext(ctx)
, arch_to_gnu(arch)
, arch_to_go(arch)
, os_to_go(os)

Install
@vx//stdlib:install.star

msi_install(url, ...)
, archive_install(url, ...)
, binary_install(url, ...)
, platform_install(ctx, ...)

HTTP
@vx//stdlib:http.star

github_releases(ctx, owner, repo)
, releases_to_versions(releases)
, parse_github_tag(tag)

Semver
@vx//stdlib:semver.star

semver_compare(a, b)
, semver_gt/lt/gte/lte/eq(a, b)
, semver_sort(versions)
, semver_strip_v(v)

ctx Object Reference

ctx = {
    "platform": {
        "os":     "windows" | "macos" | "linux",
        "arch":   "x64" | "arm64" | "x86",
        "target": "x86_64-pc-windows-msvc" | ...,
    },
    "http": {
        "get_json": lambda url: ...,  # returns parsed JSON
    },
    "paths": {
        "install_dir": "/path/to/install",
        "cache_dir":   "/path/to/cache",
    },
}

install_layout Return Values

Type
Required Fields
Optional Fields

"archive"

type

strip_prefix
, executable_paths

"binary"

type

executable_name
, source_name
, permissions

"msi"

type
, url

executable_paths
, strip_prefix
, extra_args

Migration: Adding MSI Install Support (Windows)

For tools that distribute .msi
 installers on Windows, use msi_install()
 from install.star
.

How MSI Install Works

The Rust runtime runs:

msiexec /a <file.msi> /qn /norestart TARGETDIR=<install_dir>

This extracts the MSI contents to install_dir
 without modifying the Windows registry.

Template: MSI on Windows + Archive on Other Platforms

# provider.star
load("@vx//stdlib:install.star",  "msi_install", "archive_install")
load("@vx//stdlib:github.star",   "make_fetch_versions", "github_asset_url")

fetch_versions = make_fetch_versions("owner", "repo")

def download_url(ctx, version):
    os = ctx["platform"]["os"]
    if os == "windows":
        return "https://github.com/owner/repo/releases/download/v{}/tool-{}-x64.msi".format(version, version)
    elif os == "macos":
        return github_asset_url("owner", "repo", "v" + version, "tool-{}-macos.tar.gz".format(version))
    elif os == "linux":
        return github_asset_url("owner", "repo", "v" + version, "tool-{}-linux.tar.gz".format(version))
    return None

def install_layout(ctx, version):
    os  = ctx["platform"]["os"]
    url = download_url(ctx, version)
    if os == "windows":
        return msi_install(
            url,
            executable_paths = ["bin/tool.exe", "tool.exe"],
            # strip_prefix = "PFiles/Tool",  # if msiexec extracts to a subdir
        )
    else:
        return archive_install(
            url,
            strip_prefix = "tool-{}".format(version),
            executable_paths = ["bin/tool"],
        )

def environment(ctx, version, install_dir):
    return {"PATH": install_dir}

def deps(ctx, version):
    return []

Template: platform_install() Convenience Helper

load("@vx//stdlib:install.star", "platform_install")

def install_layout(ctx, version):
    return platform_install(
        ctx,
        windows_url = "https://example.com/tool-{}.msi".format(version),
        macos_url   = "https://example.com/tool-{}-macos.tar.gz".format(version),
        linux_url   = "https://example.com/tool-{}-linux.tar.gz".format(version),
        windows_msi = True,
        executable_paths = ["bin/tool.exe", "bin/tool"],
        strip_prefix = "tool-{}".format(version),
    )

Migration: From post_extract to Layout

Before (Custom Rust Code)

// In runtime.rs
fn post_extract(&self, version: &str, install_path: &PathBuf) -> Result<()> {
    use std::fs;
    
    let platform = Platform::current();
    let original_name = format!("tool-{}-{}.exe", version, platform.arch);
    let original_path = install_path.join(&original_name);
    
    let bin_dir = install_path.join("bin");
    fs::create_dir_all(&bin_dir)?;
    
    let target_path = bin_dir.join("tool.exe");
    fs::rename(&original_path, &target_path)?;
    
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        let mut perms = fs::metadata(&target_path)?.permissions();
        perms.set_mode(0o755);
        fs::set_permissions(&target_path, perms)?;
    }
    
    Ok(())
}

After (RFC 0019 TOML)

# In provider.toml
[runtimes.layout]
download_type = "binary"

[runtimes.layout.binary."windows-x86_64"]
source_name = "tool-{version}-x86_64.exe"
target_name = "tool.exe"
target_dir = "bin"

[runtimes.layout.binary."linux-x86_64"]
source_name = "tool-{version}-x86_64"
target_name = "tool"
target_dir = "bin"
target_permissions = "755"

Benefits:

- ✅ No Rust code needed

- ✅ Declarative configuration

- ✅ Easy to update

- ✅ Cross-platform handling built-in

Migration: Adding System Package Manager Fallback

When a tool has "No download URL" errors on certain platforms, add package manager fallback.

Identify the Problem

Error messages indicating need for package manager fallback:

- No download URL for {tool} {version}
 on macOS/Windows

- Executable not found: ~/.vx/store/{tool}/{version}/bin/{tool}
 after "successful" install

- Tool works on Linux but fails on macOS/Windows

Step 1: Check Platform Coverage

# Check which platforms have binary downloads
grep -A 20 "layout.binary" crates/vx-providers/{name}/provider.toml

# If missing platforms (e.g., macos, windows), need package manager fallback

Step 2: Add system_deps.pre_depends

# Add after [runtimes.versions] or [runtimes.layout]

# macOS requires Homebrew
[[runtimes.system_deps.pre_depends]]
type = "runtime"
id = "brew"
platforms = ["macos"]
reason = "Required to install {tool} on macOS (no portable binary available)"
optional = false

# Windows: any one of winget/choco/scoop
[[runtimes.system_deps.pre_depends]]
type = "runtime"
id = "winget"
platforms = ["windows"]
reason = "Preferred package manager for Windows (built-in on Windows 11)"
optional = true

[[runtimes.system_deps.pre_depends]]
type = "runtime"
id = "choco"
platforms = ["windows"]
reason = "Alternative to winget for Windows installation"
optional = true

[[runtimes.system_deps.pre_depends]]
type = "runtime"
id = "scoop"
platforms = ["windows"]
reason = "Alternative to winget for Windows installation"
optional = true

Step 3: Add system_install.strategies

# System installation strategies
[[runtimes.system_install.strategies]]
type = "package_manager"
manager = "brew"
package = "{brew_package_name}"
platforms = ["macos"]
priority = 90

[[runtimes.system_install.strategies]]
type = "package_manager"
manager = "winget"
package = "Publisher.Package"  # Find via: winget search {tool}
platforms = ["windows"]
priority = 95

[[runtimes.system_install.strategies]]
type = "package_manager"
manager = "choco"
package = "{choco_package_name}"  # Find via: choco search {tool}
platforms = ["windows"]
priority = 80

[[runtimes.system_install.strategies]]
type = "package_manager"
manager = "scoop"
package = "{scoop_package_name}"  # Find via: scoop search {tool}
platforms = ["windows"]
priority = 60

Step 4: Update runtime.rs (for custom Runtime implementations)

If the provider has a custom runtime.rs
 (not manifest-driven), add:

use vx_system_pm::{PackageInstallSpec, PackageManagerRegistry};
use vx_runtime::InstallResult;

// Add to impl Runtime
async fn install(&self, version: &str, ctx: &RuntimeContext) -> Result<InstallResult> {
    let platform = Platform::current();

    // Try direct download first
    if let Some(url) = self.download_url(version, &platform).await? {
        return self.install_via_download(version, &url, ctx).await;
    }

    // No direct download, try package manager
    self.install_via_package_manager(version, ctx).await
}

// Add helper method
async fn install_via_package_manager(
    &self,
    version: &str,
    _ctx: &RuntimeContext,
) -> Result<InstallResult> {
    let registry = PackageManagerRegistry::new();
    let available = registry.get_available().await;

    for pm in &available {
        let package = Self::get_package_name_for_manager(pm.name());
        let spec = PackageInstallSpec {
            package: package.to_string(),
            ..Default::default()
        };

        if pm.install_package(&spec).await.is_ok() {
            let exe_path = which::which("{executable}").ok();
            return Ok(InstallResult::system_installed(
                format!("{} (via {})", version, pm.name()),
                exe_path,
            ));
        }
    }

    Err(anyhow::anyhow!("No package manager available"))
}

Step 5: Add Cargo.toml Dependencies

[dependencies]
vx-system-pm = { workspace = true }
tracing = { workspace = true }
which = { workspace = true }

Package Name Lookup

Manager
How to Find Package Name

brew
brew search {tool}

winget
winget search {tool}

choco
choco search {tool}

scoop
scoop search {tool}

apt
apt search {tool}

dnf
dnf search {tool}

Common Package Name Mappings

Tool
brew
winget
choco
Notes

ImageMagick
imagemagick
ImageMagick.ImageMagick
imagemagick

FFmpeg
ffmpeg
Gyan.FFmpeg
ffmpeg

AWS CLI
awscli
Amazon.AWSCLI
awscli

Azure CLI
azure-cli
Microsoft.AzureCLI
azure-cli

Docker
docker
Docker.DockerDesktop
docker-desktop
Desktop on Win/Mac

Git
git
Git.Git
git

Make
make
GnuWin32.Make
make

Special Cases

Case 1: Installer Files (.msi, .pkg)

For tools using installers (not supported yet):

# Note: Tool uses platform-specific installers (.msi, .pkg, .deb)
# Layout configuration will be added when installer support is implemented

[runtimes.platforms.windows]
executable_extensions = [".exe"]

Examples: awscli, azcli, gcloud, ollama, vscode

Case 2: Multiple Executables

For tools providing multiple executables:

[runtimes.layout.archive]
strip_prefix = "toolset-{version}"
executable_paths = [
    "bin/tool1.exe",
    "bin/tool1",
    "bin/tool2.exe",
    "bin/tool2"
]

Case 3: Bundled Dependencies

Tools bundled with another runtime:

[[runtimes]]
name = "npm"
executable = "npm"
bundled_with = "node"  # Comes with Node.js

# No layout configuration needed
# npm is installed as part of node

[[runtimes.constraints]]
when = "*"
requires = [
    { runtime = "node", version = "*", reason = "npm is bundled with Node.js" }
]

Validation Rules

Rule 1: Platform Coverage

Binary layout must cover all supported platforms:

- ✅ windows-x86_64

- ✅ macos-x86_64, macos-aarch64

- ✅ linux-x86_64, linux-aarch64

Rule 2: Unix Permissions

Unix platforms must have target_permissions
:

# ❌ Missing permissions
[runtimes.layout.binary."linux-x86_64"]
source_name = "tool"
target_name = "tool"
target_dir = "bin"

# ✅ Correct
[runtimes.layout.binary."linux-x86_64"]
source_name = "tool"
target_name = "tool"
target_dir = "bin"
target_permissions = "755"

Rule 3: Path Separators

Always use forward slashes in paths:

# ❌ Wrong
executable_paths = ["bin\\tool.exe"]

# ✅ Correct
executable_paths = ["bin/tool.exe"]

Rule 4: Variable Syntax

Use correct variable placeholders:

# ❌ Wrong
strip_prefix = "$version-{os}"

# ✅ Correct
strip_prefix = "{version}-{os}"

Testing Checklist

After updating a provider:

-  cargo check -p vx-provider-{name}
 passes

-  cargo build --release
 succeeds

-  vx install {name}@latest
 works

-  vx which {name}
 shows correct path

-  vx {name} --version
 executes successfully

-  Tested on Windows (if applicable)

-  Tested on Linux (if applicable)

-  Tested on macOS (if applicable)

Update Documentation

After successful update:

- Update migration status in docs/provider-migration-status.md

- Add entry to changelog if significant

- Update tool documentation in docs/tools/
 if needed

Troubleshooting

Issue: Executable not found after install

Cause: Incorrect strip_prefix
 or executable_paths

Solution:

- Download the archive manually

- Inspect the actual structure

- Update configuration to match

Issue: Permission denied on Unix

Cause: Missing target_permissions

Solution: Add target_permissions = "755"
 to Unix platforms

Issue: Wrong executable on Windows

Cause: Incorrect file extension or order in executable_paths

Solution: Ensure .exe
 files come before non-extension files

Issue: Version variable not replaced

Cause: Wrong variable syntax or missing variable

Solution: Use {version}
 not $version
 or ${version}

Issue: "No download URL for {tool}" on macOS/Windows

Cause: No layout.binary configuration for that platform, and no package manager fallback

Solution: Add system package manager support:

- Add system_deps.pre_depends
 for brew (macOS) or winget/choco (Windows)

- Add system_install.strategies
 for each package manager

- If custom runtime.rs, implement install()
 with package manager fallback

Issue: "Executable not found" after system package manager install

Cause: install_quiet
 returns version string, loses executable_path
 from InstallResult

Solution:

- Ensure install()
 returns InstallResult::system_installed(version, Some(exe_path))

- Use which::which("{executable}")
 to get actual executable path

- Test handler should use executable_path
 from InstallResult
, not compute store path

Issue: Package manager install succeeds but tool not found

Cause: Package manager installed to non-standard location, or which::which()
 fails

Solution:

- Check package manager's installation location

- Ensure PATH includes package manager's bin directory

- Use explicit path lookup: which::which("{executable}")

Issue: Wrong package name for package manager

Cause: Package names differ between package managers

Solution: Look up correct package name for each manager:

- brew: brew search {tool}

- winget: winget search {tool}
 (uses Publisher.Package format)

- choco: choco search {tool}

- scoop: scoop search {tool}

Issue: TOML parse error "unknown variant" for download_type

Cause: Using kebab-case (git-clone
) instead of snake_case (git_clone
)

Solution: Use snake_case
 for all enum values in provider.toml:

- download_type = "git_clone"
 ✅

- download_type = "git-clone"
 ❌

- ecosystem = "cpp"
 ✅ (new in recent update)

The error diagnostic system will suggest the correct format.

Issue: TOML parse error "unknown variant" for ecosystem

Cause: Using an unsupported ecosystem value

Solution: Use one of the supported values:
nodejs
, python
, rust
, go
, ruby
, java
, dotnet
, devtools
, container
, cloud
, ai
, cpp
, zig
, system

Issue: "No factory registered" appears in debug output

Cause: Provider has provider.toml
 but no Rust factory implementation

Solution: This is expected for manifest-only providers (in development). The build summary now shows:

registered 53 lazy providers (0 errors, 9 manifest-only, 0 warnings)

- errors: Real configuration problems

- manifest-only: Expected for providers without Rust implementation yet

Reference

See also:

- references/rfc-0019-layout.md
 - Complete RFC 0019 specification

- docs/provider-migration-status.md
 - Migration status tracker

- docs/provider-update-summary.md
 - Batch update summary

- crates/vx-providers/imagemagick/
 - Example hybrid provider with PM fallback

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

llms-txt-generator

No summary provided by upstream source.

Repository SourceNeeds Review
General

rfc-creator

No summary provided by upstream source.

Repository SourceNeeds Review
General

vx-usage

No summary provided by upstream source.

Repository SourceNeeds Review