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/plannersub-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'sPATH, so the helpers your commands and hooks call can be invoked as bare commands.
Installing a plugin
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/pathGitLab 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:
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.zipBoth 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
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:
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| Plugin | Adds | What it does |
|---|---|---|
commit-pro | /commit, /changelog | Conventional commits and changelog entries from the real diff. |
test-gen | /test | Unit tests following your project's conventions. |
doc-writer | /readme, /docstrings | READMEs and doc comments generated from actual code. |
security-audit | /audit | Injection / authz / secrets audit of a diff or module. |
refactor-guide | /refactor + refactorer agent | Disciplined stepwise refactors with call-site verification. |
code-review | /code-review + reviewer agent | Reviews 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:
/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 pathFindings 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:
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.mdresolve 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-coderepo and is synced toMaxrall-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
nex plugin list # everything
nex plugin list --enabled # only enabled
nex plugin list --disabled # only disabledor 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
nex plugin disable <name> # its commands, skills, agents, hooks and MCP servers stop loading
nex plugin enable <name> # load them againDisabling 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:
{ "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:
{
"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-formcommandis an error, because the substituted value would be re-parsed by the shell.
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:
nex plugin validate [dir] # manifest, userConfig, hooks, MCP; exit 1 on errors
nex plugin validate --json # machine-readable reportUpdating and removing
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-dataRemoving a plugin leaves its auto-installed dependencies on disk: another plugin may still need them. Once nothing does, nex plugin prune removes them:
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:
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 runA 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:
| type | fields | passes when |
|---|---|---|
regex | pattern, flags, match (contains | not_contains | count:<n>), target (last_message | trace | file + path) | the pattern matches (or does not) |
tool_used | tool, input_match, min, max | the number of matching tool calls is in range |
tool_order | before, after | the first before call precedes the first after call |
file_exists | path (glob), exists | a created file matches (or does not) |
llm | body = criteria, focus (last_message | trace) | a judge model votes PASS (2 of 3) |
baseline | body = criteria, baseline_file | a 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/).