Zillowe FoundationZillowe Documentation

Lua Plugin API

The ultimate reference for extending Zoi's core functionality with Lua plugins.

Plugins are global Lua scripts that allow you to transform Zoi from a tool into a personalized development platform. While package scripts (.pkg.lua) are scoped to a single package, Plugins are loaded on every Zoi invocation, allowing them to add global commands, enforce security policies, and automate workflows.

Lifecycle & Location

Plugins are distributed via Extensions. When an extension containing a plugin change-type is added, the script is saved to the Zoi home directory.

  • Storage Location: ~/.zoi/plugins/
  • Loading: Every time you run zoi, Zoi initializes a Lua VM and executes every .lua file in the plugins folder in alphabetical order before processing your command.

Command API

You can register custom subcommands that appear as if they were built into Zoi.

zoi.register_command(config)

Registers a new command. The function accepts a table with the following fields:

  • name (string): The subcommand name (e.g. "audit").
  • description (string): A short summary shown when zoi is run without arguments.
  • callback (function): The function to run. It receives one argument: args (a list of strings).
zoi.register_command({
    name = "hello",
    description = "A friendly greeting from a plugin",
    callback = function(args)
        local target = args[1] or "World"
        zoi.ui.print("Hello, " .. target .. "!", "green")
    end
})

The Hook System

Hooks allow you to intercept Zoi's core operations. This is useful for auditing, logging, or extending Zoi's behavior.

HookArgumentTrigger Point
on_pre_installpkgBefore a package installation starts.
on_post_installmanifestAfter a package is successfully installed.
on_pre_uninstallmanifestBefore a package is removed.
on_post_uninstallmanifestAfter a package is removed.
on_pre_extension_addpkgBefore an extension is applied.
on_post_extension_addmanifestAfter an extension is successfully applied.
on_pre_extension_removemanifestBefore an extension is reverted.
on_post_extension_removemanifestAfter an extension is reverted.
on_pre_createpkgBefore building/creating an app from a template.
on_post_createpkgAfter the app is successfully created.
on_pre_syncNoneBefore the package database is updated.
on_post_syncNoneAfter the package database sync is complete.
on_rollbackNoneWhen a rollback operation is triggered.
on_resolve_shim_versionbin_nameWhen a Zoi shim is executed. Return a version string to override.
on_project_installNoneTriggered by zoi install in an empty project. Return true to handle the installation.

The Project Installation Hook

The on_project_install hook allows plugins to provide native support for non-Zoi projects (like NPM, Python, or Ruby).

zoi.on_project_install(callback)

  • Return: boolean - Return true if your plugin has handled the installation. Zoi will stop at the first plugin that returns true.

Example: Basic NPM Detection

zoi.on_project_install(function()
    if zoi.fs.exists("package.json") then
        zoi.ui.print("NPM project detected, installing...", "cyan")
        return zoi.sh("npm install") == 0
    end
    return false
end)

The Shim Resolution Hook

The on_resolve_shim_version hook is special because it allows plugins to influence Zoi's version multiplexing at runtime.

zoi.on_resolve_shim_version(callback)

  • Argument: bin_name (string) - The name of the binary being executed (e.g. "node").
  • Return: string or nil - Return a version string (e.g. "18.19.0") to force that version, or nil to continue with standard resolution.

Example: Automated .nvmrc support

zoi.on_resolve_shim_version(function(bin_name)
    if bin_name == "node" or bin_name == "npm" then
        local f = io.open(".nvmrc", "r")
        if f then
            local version = f:read("*a"):gsub("%s+", "")
            f:close()
            return version
        end
    end
    return nil
end)

Data Inspection API

Plugins can query the state of the machine and the Zoi environment.

zoi.list_installed()

Returns an array of InstallManifest objects for every package currently installed on the system across all scopes.

zoi.get_package(name)

Fetches the Package metadata for a specific package from the local database. This works even if the package is not installed.

zoi.project

If you are inside a project with a zoi.yaml file, this table is available:

  • zoi.project.name (string): The project name.
  • zoi.project.packages (list): A list of strings representing the packages required by the project.

Persistent State

Plugins can save and retrieve data that persists between different Zoi commands. The data is stored in ~/.zoi/plugins/state.json.

  • zoi.set_data(key, value): Store a value. Supports strings, numbers, and booleans.
  • zoi.get_data(key): Retrieve a value. Returns nil if the key doesn't exist.

UI & Interaction

Use Zoi's internal UI engine to make your plugins look professional.

zoi.ui.print(text, color)

Prints text to the terminal.

  • Colors: red, green, yellow, blue, cyan, magenta, white.

zoi.ui.table(headers, rows)

Renders a structured ASCII table.

  • headers: A list of strings.
  • rows: A list of lists (e.g. {{ "row1-col1", "row1-col2" }, { "row2-col1", "row2-col2" }}).

zoi.ui.confirm(prompt)

Displays a y/N prompt and returns a boolean.

zoi.ui.select(prompt, options)

Displays a list of options and returns the 1-based index of the user's choice.


System & Shell

zoi.sh(command)

Runs a command in the system shell (bash on Unix, pwsh on Windows). Returns the integer exit code.

zoi.system

  • zoi.system.os: Returns "linux", "macos", or "windows".
  • zoi.system.arch: Returns "amd64", "arm64", etc.

zoi.version

Returns the current Zoi version string (e.g. "1.6.0").


File System (FS) API

Plugins can interact with the local file system.

  • zoi.fs.exists(path): Returns true if the file or directory exists.
  • zoi.fs.read(path): Reads a file's contents and returns it as a string. Returns nil on error.
  • zoi.fs.write(path, content): Writes a string to a file. Returns true on success, false on error.
  • zoi.fs.list(path): Returns an array of file and directory names within the given path.
  • zoi.fs.delete(path): Deletes a file or recursively deletes a directory. Returns true on success, false on error.
  • zoi.fs.symlink(target, link, is_dir): Creates a symbolic link (or junction on Windows).
  • zoi.fs.copy(src, dest): Copies a file or directory recursively.

Archive API

High-performance native extraction utilities.

  • zoi.archive.extract(source, dest, strip): Extracts an archive (.zip, .tar.gz, .tgz, .tar.zst, .tar.xz).
    • source (string): Path to the archive.
    • dest (string): Destination directory.
    • strip (int, optional): Number of leading path components to strip from entries.

HTTP API

Plugins can make basic HTTP requests and downloads.

  • zoi.http.get(url): Performs a GET request. Returns the response body as a string, or nil on error.
  • zoi.http.post(url, body): Performs a POST request with the given string body. Returns the response body as a string, or nil on error.
  • zoi.http.download(url, dest): Downloads a file from a URL to the specified destination. Returns true on success.

JSON API

Utilities for working with JSON data.

  • zoi.json.parse(json_string): Parses a JSON string into a Lua table. Returns nil on error.
  • zoi.json.stringify(table): Converts a Lua table into a JSON string. Returns nil on error.

Environment API

Access and manipulate environment variables.

  • zoi.env.get(name): Returns the value of an environment variable as a string, or nil if not set.
  • zoi.env.set(name, value): Sets an environment variable for the current process.

Data Structures

Package Metadata

The object passed to on_pre_install or returned by get_package.

  • name: Package name.
  • version: Version string.
  • license: SPDX license identifier.
  • description: Summary.
  • repo: Source repository name.
  • tags: List of tags.

InstallManifest

The object passed to on_post_install and on_pre_uninstall.

  • name: Package name.
  • version: Installed version.
  • scope: "user", "system", or "project".
  • installed_files: A list of every file path created by the package.
  • repo: The repository it originated from.

Advanced Examples

Example 1: The License Enforcer

Prevents the installation of any package with a restricted license.

zoi.on_pre_install(function(pkg)
    local restricted = { ["GPL-3.0"] = true, ["AGPL-3.0"] = true }
    if restricted[pkg.license] then
        zoi.ui.print("SECURITY ALERT: " .. pkg.name .. " uses " .. pkg.license, "red")
        if not zoi.ui.confirm("This license is restricted in this environment. Continue?") then
            os.exit(1)
        end
    end
end)

Example 2: Project Usage Tracker

Tracks how many times you run commands in a specific project.

if zoi.project then
    local key = "runs_" .. zoi.project.name
    local count = zoi.get_data(key) or 0
    zoi.set_data(key, count + 1)
end

Example 3: Custom "Project Doctor" Command

Adds a command to verify your environment.

zoi.register_command({
    name = "doctor",
    description = "Check if project dependencies are met",
    callback = function()
        if not zoi.project then
            zoi.ui.print("Not in a Zoi project.", "red")
            return
        end

        zoi.ui.print("Checking dependencies for " .. zoi.project.name, "cyan")
        local installed = zoi.list_installed()
        -- ... logic to cross-reference zoi.project.packages with installed ...
    end
})

A software organization

2026 © All Rights Reserved.

  • All the content is available under CC BY-SA 4.0, expect where otherwise stated.

Last updated on