Skip to content

Plugins ​

Nexrall Code (VS Code and CLI) supports installable plugins — bundles of commands, agents, and hooks that extend the agent's behavior for a specific project or workflow.

What a plugin can contain ​

A plugin lives in .nexrall/plugins/<name>/ and can define:

  • Commands — custom slash commands available in a session.
  • Agents — named sub-agent personas with a scoped tool set (similar to the built-in explorer / planner sub-agents).
  • Hooks — scripts that run at specific points in the agent loop (for example, after every file write).
  • Executables — a bin/ directory at the plugin root; while the plugin is enabled it is added to the Bash tool's PATH, so the helpers your commands and hooks call can be invoked as bare commands.

Installing a plugin ​

sh
nex plugin install owner/repo
nex plugin install owner/repo#branch/subdir
nex plugin install https://github.com/owner/repo
nex plugin install gitlab.com/group/project        # GitLab (nested subgroups work)
nex plugin install gitlab.com/group/sub/project#v1.2.0
nex plugin install ./local/path

GitLab sources are the whole project path after the host — gitlab.com/group/sub/project, with or without https:// — and the web form gitlab.com/group/project/-/tree/<ref>/<subdir> selects a branch and a subdirectory the same way the GitHub tree URL does. Other /-/ pages (merge requests, blobs) are not installable sources and are refused with the reason.

Plugins that define hooks or MCP connections require an explicit confirmation before install — this can't be bypassed even with --yes, since hooks can run arbitrary code as part of your agent sessions.

Session plugins ​

To try a plugin without installing it, load it for one run:

sh
nex --plugin-dir ./my-plugin              # a plugin directory (live — edits are seen)
nex --plugin-dir ./my-plugin.zip          # an archive OF a plugin
nex --plugin-dir ~/my-plugins             # a directory OF plugins
nex --plugin-url https://example.com/p.zip

Both flags are repeatable. Whatever arrives is unpacked into a temp directory that is removed when the process exits — nothing is installed, no receipt is written, and nex plugin list shows the entries with scope session for the duration of the run. A session plugin wins a name clash against an installed one (loading this copy is the point of the flag). If a requested plugin cannot be loaded — a missing path, an unreadable archive, a zip with no plugin inside — the run fails with the reason rather than starting with half the plugins missing.

The archive type is detected from the content (.zip magic or gzip), so a URL without an extension works; .zip, .tar.gz and .tgz are accepted, up to 50 MB.

Searching the registry ​

sh
nex plugin search <name>
nex plugin info <name>

Official plugins ​

The Nexrall team maintains a set of first-party plugins. They install by short name from the registry, or straight from GitHub:

sh
nex plugin install code-review                  # registry, by short name
nex plugin install --yes test-gen               # non-interactive (CI)
nex plugin install nexrall/plugins/code-review  # direct from GitHub
PluginAddsWhat it does
commit-pro/commit, /changelogConventional commits and changelog entries from the real diff.
test-gen/testUnit tests following your project's conventions.
doc-writer/readme, /docstringsREADMEs and doc comments generated from actual code.
security-audit/auditInjection / authz / secrets audit of a diff or module.
refactor-guide/refactor + refactorer agentDisciplined stepwise refactors with call-site verification.
code-review/code-review + reviewer agentReviews the working tree, a git ref, a PR/MR number, or a path in a fresh context: findings with file:line, severity, why and a minimal fix sketch. Budget 10 findings; deep raises it to 25 and adds suggestions; --max-findings <n>|all sets it exactly; --comment posts the merged findings as ONE comment on the PR/MR; --json emits a machine-readable report.

None of them ship hooks or MCP config — they are prompt packs (commands and agents), so installing one runs no code by itself. code-review is the model-backed counterpart of the built-in /review, which groups the working diff by risk locally and never calls a model.

Reviewing a pull/merge request ​

/code-review takes a git ref, a path, or — with gh or glab installed and authenticated — a PR/MR number:

text
/code-review 1234                # GitHub PR or GitLab MR #1234
/code-review 1234 --comment      # …and post the findings as one comment on it
/code-review 1234 --json         # the report as one JSON object (for scripts/CI)
/code-review main...             # a branch/tag/SHA, three-dot diff
/code-review ./src               # only changes under a path

Findings are posted as one plain comment (never an inline review, never an approval), with an attribution line. The plugin ships a small helper (target.mjs) that resolves the target for the reviewer and renders the exact comment body; it is read-only and scriptable on its own for CI:

sh
node .nexrall/plugins/code-review/target.mjs resolve 1234   # JSON: diffCommand, files, untracked
node .nexrall/plugins/code-review/target.mjs comment-body report.json > body.md

resolve exit codes: 0 ok · 1 usage · 2 not a git repo · 3 gh/glab missing · 4 target did not resolve. The --comment path needs --json-shaped report input, so a malformed report can never be posted.

Note: a plugin installed from GitHub carries whatever its repo has; the local source of truth for this plugin lives in the nexrall-code repo and is synced to Maxrall-Inc/plugins.

Precedence ​

When a command or agent name exists in more than one place, Nexrall resolves it in this order: project-level → global (user) → plugin → built-in.

Listing installed plugins ​

sh
nex plugin list                 # everything
nex plugin list --enabled       # only enabled
nex plugin list --disabled      # only disabled

or the /plugins slash command inside a session. Entries that were installed automatically as a dependency are marked (auto ← <requirer>), and a disabled plugin's row carries a ⏸ disabled marker.

Enabling and disabling ​

sh
nex plugin disable <name>   # its commands, skills, agents, hooks and MCP servers stop loading
nex plugin enable <name>    # load them again

Disabling writes a .disabled marker inside the plugin directory — the same marker the desktop app's plugin list and /plugins toggle. Nothing is deleted, including the plugin's data directory, and its bin/ directory leaves the Bash tool's PATH while disabled. Start a new session (or /reload) for the change to take effect.

Dependencies ​

A plugin can name the other plugins it needs in its manifest:

json
{ "name": "deploy-tools", "dependencies": ["secrets-vault"] }

nex plugin install installs those too, in the same scope, and fails the whole install if one cannot be resolved — a plugin missing the dependency it declares is not a plugin that half-works. A dependency's own dependencies are followed as well, and cycles terminate. Version ranges such as { "name": "secrets-vault", "version": "^2.1.0" } are accepted for compatibility with Claude Code manifests but are recorded, not enforced: sources are git specs, so the install prints a notice and follows the source's default branch.

Plugin data directory ​

A plugin's install directory is replaced on every update, so a plugin cannot keep state there. Instead every plugin gets ~/.nexrall/plugin-data/<name>/, created the first time it is referenced and handed to everything the plugin runs:

  • hook commands see it as $NEXRALL_PLUGIN_DATA
  • stdio MCP servers the plugin declares get it in their environment

Caches, downloads, a local database — anything that must survive an update — belongs there.

Configuration (userConfig) ​

A plugin that needs a value from you — an API endpoint, a token, a log directory — declares it in plugin.json instead of making you edit settings by hand:

json
{
  "userConfig": {
    "api_endpoint": {
      "type": "string",
      "title": "API endpoint",
      "description": "Your team's API base URL"
    },
    "api_token": {
      "type": "string",
      "title": "API token",
      "description": "Authentication token",
      "sensitive": true
    }
  }
}

Every option needs type (string, number, boolean, directory, file), title and description. Optional fields: required, default, options (a fixed list for string fields), multiple (array of strings), sensitive, and min/max for numbers. The block is strict — an unknown field inside an option is an error, and one bad option invalidates the whole block.

Where values live. Non-sensitive values are saved under pluginConfigs in your settings file (user or project scope); sensitive values go to ~/.nexrall/plugin-secrets.json (mode 0600) instead. Resolution order: secrets → settings → the manifest default. Values that fail validation are reported and ignored, never used.

How values reach the plugin:

  • ${user_config.KEY} in the plugin's MCP server config (sensitive values included) and in skill bodies (non-sensitive only — a sensitive reference stays the literal token rather than entering model context);
  • NEXRALL_PLUGIN_OPTION_<KEY> (key uppercased) in the environment of every hook the plugin ships. A shell-form hook must read the variable — using ${user_config.*} inside a shell-form command is an error, because the substituted value would be re-parsed by the shell.
sh
nex plugin configure <name>                    # show every option and its state
nex plugin configure <name> --values-stdin <<< '{"api_token":"…"}'
nex plugin configure <name> --project          # save to the project's settings
nex plugin install owner/repo --config api_endpoint=https://api.example

/plugins in a session shows each plugin's configuration state (needs: … when a required option is unset). Before installing, check a plugin directory — or your own — with:

sh
nex plugin validate [dir]      # manifest, userConfig, hooks, MCP; exit 1 on errors
nex plugin validate --json     # machine-readable report

Updating and removing ​

sh
nex plugin update [name]            # re-install from the recorded source
nex plugin remove <name>            # uninstall; the data directory goes with it
nex plugin remove <name> --keep-data

Removing a plugin leaves its auto-installed dependencies on disk: another plugin may still need them. Once nothing does, nex plugin prune removes them:

sh
nex plugin prune --dry-run   # list what would go, remove nothing
nex plugin prune -y          # remove (required when stdin is not a TTY)

prune never removes a plugin you installed yourself, and never one that an installed plugin still needs — directly or through a chain of dependencies. autoremove is an alias, matching Claude Code.

Writing your own plugin ​

A minimal plugin only needs a plugin.json manifest plus a commands/ folder with one or more command files — no hooks or MCP config required unless you need them. Start from an existing plugin's structure as a template if you're building your first one.

Evals ​

nex plugin eval runs a plugin against a suite of test cases and scores the results:

text
my-plugin/
  plugin.json
  evals/
    write-commit-msg/
      prompt.md          # frontmatter = case fields; body = the prompt
      graders/
        mentions-commit.md   # frontmatter = grader; body = llm criteria
    results/             # written by each run

A case is a directory with prompt.md (frontmatter: name, runs (default 3), max_turns, timeout_seconds, allowed_tools, tags) and a graders/ directory with at least one grader. Grader types:

typefieldspasses when
regexpattern, flags, match (contains | not_contains | count:<n>), target (last_message | trace | file + path)the pattern matches (or does not)
tool_usedtool, input_match, min, maxthe number of matching tool calls is in range
tool_orderbefore, afterthe first before call precedes the first after call
file_existspath (glob), existsa created file matches (or does not)
llmbody = criteria, focus (last_message | trace)a judge model votes PASS (2 of 3)
baselinebody = criteria, baseline_filea judge finds the run at least as good as the reference

Every grader also takes weight (default 1) and arm (both | with-only — a with-only grader never scores the no-plugin arm).

Each run is a fresh non-interactive nex in an isolated temp workspace with only that plugin loaded; the same case also runs without the plugin, so the report shows WITH, W/OUT and Δ (what the plugin actually contributed). A case passes at the score threshold (--threshold, default 1.0 — every grader, every run). Cost comes from the runs' own reported totals (total_cost_usd); unreported costs are counted, never invented. Results: evals/results/<timestamp>/aggregate-result.json; --json prints the report to stdout for CI.

Not supported yet (a suite that uses them is refused, not silently mis-run): case.yaml (no YAML parser) and mock MCP servers (evals/mocks/).

Built by Maxrall, Inc.