<p align="center">
<img src="./assets/nodus-mark.svg" alt="Nodus mark" width="144">
</p>
<h1 align="center">Nodus</h1>
<p align="center"><strong>Add agent packages to your repo with one command.</strong></p>
<p align="center">
Nodus installs agent packages from GitHub, Git URLs, or local paths, locks the exact revision,
and writes only the adapter runtime files your repo actually uses.
</p>
<p align="center">
English • <a href="./README.cn.md">简体中文</a>
</p>
<p align="center">
<a href="#install">Install</a> •
<a href="#for-ai-assistants">For AI Assistants</a> •
<a href="#quick-start">Quick Start</a> •
<a href="#cli-help">CLI Help</a> •
<a href="#learn-more">Learn More</a> •
<a href="./CONTRIBUTING.md">Contributing</a>
</p>
## What Is Nodus?
Nodus is a package manager for repo-scoped agent tooling.
If a package publishes content such as `skills/`, `agents/`, `rules/`, or `commands/`, Nodus can:
- add it from GitHub, Git, or a local path
- record what you asked for in `nodus.toml`
- lock the exact resolved revision in `nodus.lock`
- write managed files into adapter roots such as `.codex/`, `.claude/`, `.cursor/`, `.github/`, `.agents/`, or `.opencode/`
- keep generated Claude plugin marketplaces under `.nodus/` and virtual package payloads under `.nodus/packages/`
- compose managed MCP server config for supported runtimes, including `.mcp.json`, `.codex/config.toml`, and `opencode.json`
- prune stale generated files without touching unmanaged ones
For most teams, the normal flow is:
```bash
nodus add <package> --adapter <adapter>
nodus doctor
```
## Install
Install from crates.io:
```bash
cargo install nodus
```
Building from source requires Rust 1.88.0 or newer.
Install the latest prebuilt binary on macOS or Linux:
```bash
Install with Homebrew:
```bash
brew install nodus-rs/nodus/nodus
```
Install the latest prebuilt binary on Windows with PowerShell:
```powershell
<details>
<summary>Windows install command failed?</summary>
If the command fails, install PowerShell 7, restart your terminal, then run:
```powershell
winget install --id Microsoft.PowerShell --source winget
</details>
## For AI Assistants
If you want an AI assistant to operate Nodus for you, give it the fetchable prompt URL:
- <https://nodus.elata.ai/prompts/README.md>
That prompt gives the assistant concrete Nodus operating instructions, helps it choose the right `nodus add` command, and still ends with `nodus doctor`.
## Quick Start
Install a package into the current repo and verify the result:
```bash
nodus add nodus-rs/nodus --adapter codex
nodus doctor
```
That flow:
- creates `nodus.toml` if the repo does not have one yet
- records the dependency in `nodus.toml`
- resolves and locks the exact revision in `nodus.lock`
- writes the managed runtime files for the selected or detected adapter
If the package publishes `mcp_servers`, Nodus carries that MCP config into the
managed runtime outputs as well. Today that includes Claude and Codex native
plugin `.mcp.json` files under Nodus's global package/marketplace snapshots,
plus OpenCode `opencode.json` when that adapter is selected.
`nodus info .` now includes a `native-integration` section after sync. It shows
the generated Claude and Codex marketplace files, plugin keys and roots, hook
locations, Codex `features.hooks` / `features.plugin_hooks`, Codex global
snapshot marketplace registration, enabled Codex plugin keys, and Claude
`enabledPlugins` state.
Adapters without a native marketplace can still expose managed plugins through
Nodus's virtual plugin marketplace layer. OpenCode v1 uses
`opencode_plugin_hooks` entries, copied package files under
`.nodus/packages/<alias>/opencode-plugin/`, and generated loaders under
`.opencode/plugins/`. `nodus info .` reports these as `virtual-plugins`, not
native marketplace plugins.
### Codex global snapshot marketplace
When the Codex adapter is enabled, dependency skills, synthetic command skills,
plugin hooks, and plugin MCP config are emitted into pinned snapshots under
the global Nodus marketplace root:
```text
~/.nodus/marketplaces/codex/
.agents/plugins/marketplace.json
plugins/<managed-package-id>/
```
Nodus registers one user-level Codex marketplace named `nodus` in
`$CODEX_HOME/config.toml` or `~/.codex/config.toml`, with a local source path
pointing at that marketplace root, then enables the selected
`<plugin>@nodus` snapshot plugins. This user config write is required because
current Codex reads `marketplaces` and `plugins` from user config.
Dependency artifacts are not duplicated into project `.codex/skills`.
Codex agents still live under project `.codex/agents` because the current
Codex plugin metadata format does not declare agents. Project
`.codex/config.toml` remains for project-scoped Codex features such as
workspace hooks and plugin hook feature flags.
Packages can also declare activation context that is injected at session start
for adapters with native support:
```toml
[activation]
always_context = ["prompts/first-principles.md"]
prefer_skills = ["rust-testing", "rust-verification-sweep"]
```
`always_context` files are loaded into the generated startup context.
`prefer_skills` is advisory: Nodus tells the agent which managed skills to
load first without embedding the skill bodies.
If you want the package without MCP wiring, exclude that component:
```bash
nodus add owner/repo --adapter codex --exclude-component mcp
```
If the package is a wrapper that exposes multiple child packages, `nodus add` now records the
wrapper itself and leaves child packages disabled until you either edit `members` in
`nodus.toml` or opt in up front with `--accept-all-dependencies`.
If you want a user-level install instead of repo-scoped state, use `--global` explicitly:
```bash
nodus add nodus-rs/nodus --global --adapter codex
```
## Hook Support Matrix
When a package publishes `[[hooks]]`, Nodus filters those hook intents by what each adapter can actually emit today.
| `claude` | `session_start`, `user_prompt_submit`, `pre_tool_use`, `post_tool_use`, `stop`, `subagent_stop`, `session_end` | `startup`, `resume`, `clear`, `compact` |
| `codex` | `session_start`, `user_prompt_submit`, `pre_tool_use`, `permission_request`, `post_tool_use`, `stop` | `startup`, `resume`, `clear` |
| `opencode` | `session_start`, `pre_tool_use`, `post_tool_use`, `stop` | `startup` |
| `agents` | none | none |
| `copilot` | `session_start`, `user_prompt_submit`, `pre_tool_use`, `post_tool_use`, `stop`, `subagent_stop`, `session_end` | `startup`, `resume` |
| `cursor` | none | none |
See [docs/hooks.md](docs/hooks.md) for the full hook reference: matcher
rules per event, handler configuration, runtime environment, `nodus info`
inspection, and the `claude_plugin_hooks` escape hatch for Claude plugin
packages that ship a pre-built `hooks/hooks.json`, plus `opencode_plugin_hooks`
for raw OpenCode plugin files managed through Nodus's virtual marketplace
layer. Dependency Codex hooks and activation context are emitted inside
generated Codex plugins and enable `features.plugin_hooks` in
`.codex/config.toml` when needed.
## Package Agent Sources
Package authors can keep shared agent instructions in `agents/<id>.md`. When
Codex needs metadata of its own, add `agents/<id>.codex.toml` with fields such
as `name`, `description`, `model`, `model_reasoning_effort`, or `sandbox_mode`.
If that TOML omits `developer_instructions`, Nodus generates the runtime
`.codex/agents/<id>.toml` from the Markdown body after any YAML frontmatter.
Codex TOML files that include `developer_instructions` are still treated as the
complete Codex source of truth.
## CLI Help
`nodus --help` is the main command guide.
Start there when you want to learn the workflow, then open command-specific help as needed:
```bash
nodus --help
nodus add --help
nodus sync --help
nodus doctor --help
```
Commands most users need:
- `nodus add <package> --adapter <adapter>` to install a package into the current repo
- `nodus info <package-or-alias>` to inspect a package before or after install
- `nodus sync` to rebuild managed outputs from the versions already recorded
- `nodus update` to move dependencies to newer allowed revisions
- `nodus remove <alias>` to remove a dependency and prune what it owned
- `nodus clean` to clear shared repository, checkout, and snapshot cache data without changing project manifests or managed outputs
- `nodus doctor` to check that the repo, lockfile, shared store, and managed outputs still agree
## Learn More
- Docs: <https://nodus.elata.ai/docs/>
- Install guide: <https://nodus.elata.ai/install/>
- Package command generator: <https://nodus.elata.ai/packages/>
- Consumer manifest example: [examples/nodus.toml](./examples/nodus.toml)
- Package author manifest example: [examples/package-author.nodus.toml](./examples/package-author.nodus.toml)
For package authoring details, workspace packaging, managed exports, or relay workflows, prefer the website docs and `nodus --help` over treating this README as the full command reference.
That applies to MCP packages too: package authors can publish `mcp_servers` in `nodus.toml`, and
consumers get the matching managed project config for the adapter they use.
## Contributing
See [CONTRIBUTING.md](./CONTRIBUTING.md) for local development and release checks.
## License
Licensed under [Apache-2.0](./LICENSE).