--- url: /en/11_skills_hooks.md --- # Agent Hooks Skills Skills for setting up real-time notifications to chat channels (Slack, Discord, Asana) when agents execute tools or start sessions inside containers. ## What Are Agent Hooks? When a Nagi agent processes a request, it calls various tools behind the scenes -- reading files, running shell commands, searching code, and so on. By default, the user who sent the request sees only the final reply. Agent hooks change that by streaming **live progress notifications** into the chat channel as the agent works. This gives you visibility into what the agent is actually doing, how far along it is, and whether it has taken an unexpected path -- all without waiting for the final answer. ### Hook Types | Hook | When it fires | Example notification | |---|---|---| | **PostToolUse** | After each tool execution | `πŸ”§ Bash: ls -la /workspace/group` | | **SessionStart** | When the agent begins processing | `πŸ’­ Thinking...` (or a truncated summary of the agent's initial reasoning) | | **PromptComplete** | After a prompt finishes | `πŸ’° claude-sonnet-4-20250514 \| $0.0123 \| 1,200 in / 350 out` | ### Notification Details **PostToolUse** notifications include an icon that varies by tool type: | Icon | Tools | |---|---| | πŸ”§ | Bash | | πŸ“– | Read | | πŸ“ | Write | | ✏️ | Edit | | πŸ“‚ | Glob | | πŸ” | Grep | | ⚑ | Skill | | πŸ€– | Agent | | 🌐 | WebSearch, WebFetch | | πŸ”Œ | Any MCP tool (`mcp__*`) | | ⏰ | Task | | πŸ“‹ | TodoWrite | Each notification shows the tool name followed by a brief summary of its input -- for example, the file path for Read/Write/Edit, the shell command for Bash (truncated to 80 characters), or the search pattern for Grep. Some tools are **skipped by default** to avoid noise: `mcp__nagi__send_message` and `mcp__nagi__list_tasks`. You can add more tools to the skip list via configuration (see below). **SessionStart** notifications include the agent's initial thinking text when available, truncated to 200 characters. **PromptComplete** notifications show the model name, cost, and token usage when available. ### When to Enable Hooks Enable agent hooks when you want to: * **Monitor agent progress** -- See what the agent is doing in real time instead of waiting for a final reply. * **Debug unexpected behavior** -- If an agent's answer seems wrong, the tool trace shows exactly what files it read and what commands it ran. * **Build trust with users** -- In shared channels, visible tool activity reassures people that the agent is actively working on their request. You can enable just one hook type if the others are too noisy. For instance, enabling only `sessionStart` gives a lightweight "the agent is working" indicator without the per-tool detail. *** ## `/add-agent-hooks-claude-code` β€” Claude Code Hooks {#add-agent-hooks-claude-code} Adds PostToolUse, SessionStart, and PromptComplete hooks to **Claude Code** containers. The plugin ships with the repository under `container/claude-code/plugins/agent-hooks/index.mjs` -- this skill wires it into your assistant's host and container entry files. **Triggers:** `add agent hooks`, `enable hooks`, `setup agent hooks`, `add hooks` ### What the skill does 1. Verifies the plugin file and entry files exist (prompts you to run `/setup` if they do not). 2. Registers `registerHooksPlugin()` in the host-side `deploy/{ASSISTANT_NAME}/host/entry.ts`. 3. Adds the plugin import block in the container-side `deploy/{ASSISTANT_NAME}/container/claude-code/entry.ts`. 4. Runs a TypeScript compilation check. 5. Restarts the launchd service and asks you to test. *** ## `/add-agent-hooks-open-code` β€” Open Code Hooks {#add-agent-hooks-open-code} Adds the same hooks to **Open Code** containers. Because the plugin uses the same IPC-based mechanism, the skill copies the plugin from `container/claude-code/plugins/agent-hooks/` if the Open Code copy does not already exist. **Triggers:** `add open code hooks`, `enable open code hooks`, `setup open code hooks` ### What the skill does 1. Verifies the host entry file exists (prompts you to run `/setup` if it does not). 2. Copies the plugin to `container/open-code/plugins/agent-hooks/` if missing. 3. Registers `registerHooksPlugin()` in the host-side entry file. 4. Adds the plugin import block in the container-side entry template. 5. Rebuilds the Open Code container image and restarts the service. *** ## Configuration After running either skill, you can fine-tune hooks in `deploy/{ASSISTANT_NAME}/host/entry.ts`: ```typescript orchestrator.registerHooksPlugin({ postToolUse: true, // Send a notification after each tool call sessionStart: true, // Send "Thinking..." when a session begins promptComplete: true, // Send cost/token summary when a prompt completes skipTools: [ // Tools to silence (in addition to the defaults) "mcp__nagi__list_tasks", ], }); ``` Set any flag to `false` to disable that hook type. The `skipTools` array is merged with the built-in skip list (`mcp__nagi__send_message`, `mcp__nagi__list_tasks`), so you only need to add extras. --- --- url: /en/08_skills_agent.md --- # Agent Switching Skills Skills for switching the agent runner between Claude Code and Open Code inside containers. Each agent runs in its own Docker image, so switching means changing the `CONTAINER_IMAGE` in `.env` and restarting the service. ## Background β€” Claude Code vs Open Code Nagi supports two agent runners. You can switch between them at any time without losing your channel configuration, group prompts, or plugin setup. | | Claude Code | Open Code | |---|---|---| | **Docker image** | `nagi-agent:latest` | `nagi-agent-opencode:latest` | | **LLM provider** | Anthropic (Claude) | OpenRouter, Google Gemini, OpenAI | | **Model selection** | Determined by your Anthropic plan | User-configurable via `OPENCODE_MODEL` | | **API key env var** | `ANTHROPIC_API_KEY` | `OPENROUTER_API_KEY`, `GOOGLE_API_KEY`, or `OPENAI_API_KEY` | | **Best for** | Native Claude experience with full tool-use support | Using non-Anthropic models, or routing through OpenRouter for cost control | ### What changes when you switch * The `CONTAINER_IMAGE` value in `.env` is updated to point to the target image. * Provider-specific environment variables (`OPENCODE_MODEL`, API keys) are added or commented out. * The launchd service is restarted so the new container image takes effect. * Everything else β€” channels, groups, plugins, MCP servers β€” stays the same. ### When to use each * **Stay on Claude Code** if you are happy with Claude models and want the simplest setup. * **Switch to Open Code** if you want to use GPT-4o, Gemini 2.5 Pro, or other models available through OpenRouter, Google, or OpenAI. Open Code also lets you route Anthropic models through OpenRouter if you prefer a unified billing account. ::: tip If Open Code is not set up yet, run `/setup-opencode` first. That skill walks you through provider selection, API key configuration, and Docker image building. ::: *** ## `/change-claude-code` β€” Switch to Claude Code {#change-claude-code} Switch the agent runner back to Claude Code. Use when you are currently running Open Code and want to revert. **Triggers:** `change claude code`, `switch to claude code`, `use claude code` ### What the skill does 1. Sets `CONTAINER_IMAGE=nagi-agent:latest` in `.env`. 2. Comments out Open Code settings (`OPENCODE_MODEL`, etc.). 3. Verifies the `nagi-agent` Docker image exists (builds it via `./container/claude-code/build.sh` if missing). 4. Restarts the launchd service and confirms the agent is responding. *** ## `/change-open-code` β€” Switch to Open Code {#change-open-code} Switch the agent runner to Open Code. Use when you want to use providers like OpenRouter, Gemini, or OpenAI. **Triggers:** `change open code`, `switch to open code`, `use open code` ### Prerequisites * Open Code must already be set up (Docker image built, API key configured). If not, the skill will ask you to run `/setup-opencode` first. ### What the skill does 1. Checks that the `nagi-agent-opencode` Docker image and at least one provider API key exist. 2. Sets `CONTAINER_IMAGE=nagi-agent-opencode:latest` in `.env`. 3. Ensures `OPENCODE_MODEL` is set. If missing, prompts you to choose a model: * `openrouter/anthropic/claude-sonnet-4` * `openrouter/google/gemini-2.5-pro` * `openrouter/openai/gpt-4o` * `google/gemini-2.5-pro` * `openai/gpt-4o` 4. Restarts the launchd service and confirms the agent is responding with the selected model. --- --- url: /en/09_skills_channel.md --- # Channel Plugin Skills Skills for connecting messaging platforms to Nagi. Each channel plugin lets Nagi receive messages from and respond to a specific platform. Display-mode skills change how notifications (tool execution, thinking indicators, cost footers) are rendered without altering the underlying connection. ## Overview | Skill | Platform | Purpose | |---|---|---| | `/add-channel-slack` | Slack | Connect Slack via Socket Mode | | `/add-channel-slack-block-kit` | Slack | Switch to Block Kit rich display | | `/add-channel-slack-block-kit-embed` | Slack | Switch to Block Kit Embed display (colored borders) | | `/add-channel-discord` | Discord | Connect Discord via Gateway Intents | | `/add-channel-discord-embed` | Discord | Switch to Embed rich display | | `/add-channel-asana` | Asana | Connect Asana via comment polling | *** ## Slack ### `/add-channel-slack` β€” Connect Slack {#add-channel-slack} Connect Slack using Socket Mode (no public URL required). The skill walks you through bot token and app-level token setup, channel registration, and group creation. **Triggers:** `add slack`, `setup slack`, `connect slack`, `add channel slack` #### Prerequisites * A Slack workspace where you can install apps * Permission to create a Slack app (or an existing one) #### Tokens needed | Token | Prefix | Where to find it | |---|---|---| | Bot User OAuth Token | `xoxb-...` | OAuth & Permissions page | | App-Level Token | `xapp-...` | Basic Information > App-Level Tokens (scope: `connections:write`) | Both tokens are stored in `deploy/{ASSISTANT_NAME}/.env` as `SLACK_BOT_TOKEN` and `SLACK_APP_TOKEN`. #### Required OAuth scopes `channels:history`, `channels:read`, `chat:write`, `groups:history`, `groups:read`, `im:history`, `im:read`, `users:read` #### Required bot events `message.channels`, `message.groups`, `message.im` #### Setup tips * The skill provides a ready-to-paste YAML manifest for creating the app, so you do not need to configure scopes and events manually. * Socket Mode must be enabled in the app settings -- this is included in the manifest. * After installing the app, the bot must be explicitly added to each channel it should monitor (Integrations > Add apps). * The JID format for Slack groups is `slack:C0123456789` where the `C...` part is the channel ID from the channel URL. #### Supported features * Public channels, private channels, and direct messages * Thread replies (bot replies in the same thread) * Multi-channel operation alongside Discord or Asana * Message queueing during disconnects, flushed on reconnect *** ### Slack display modes {#slack-display-modes} Slack supports three display modes for tool execution notifications. All three use the same `createSlackFactory` function and identical configuration -- only the import path changes in `deploy/{ASSISTANT_NAME}/host/entry.ts`. | Mode | Package | Appearance | |---|---|---| | **Plain text** (default) | `@nagi/channel-slack` | Inline text like `Bash: ls -la` | | **Block Kit** | `@nagi/channel-slack-block-kit` | Rich cards with tool name header, code block, and divider | | **Block Kit Embed** | `@nagi/channel-slack-block-kit-embed` | Block Kit content wrapped in colored left-border attachments (similar to Discord embeds) | To switch modes, change the import in `deploy/{ASSISTANT_NAME}/host/entry.ts`: ```typescript // Plain text (default) const { createSlackFactory } = await import("@nagi/channel-slack"); // Block Kit const { createSlackFactory } = await import("@nagi/channel-slack-block-kit"); // Block Kit Embed const { createSlackFactory } = await import("@nagi/channel-slack-block-kit-embed"); ``` Do NOT change `deploy/templates/host/entry.template.ts` -- the template should keep the default. Display mode is a local customization per assistant. #### `/add-channel-slack-block-kit` β€” Slack Block Kit Display {#add-channel-slack-block-kit} Switch Slack channel display to Block Kit rich format. Tool execution notifications appear as formatted blocks instead of plain text. **Triggers:** `add block kit`, `enable block kit`, `slack block kit`, `rich slack`, `slack rich display` **Prerequisite:** Slack must already be connected via `/add-channel-slack`. #### `/add-channel-slack-block-kit-embed` β€” Slack Block Kit Embed Display {#add-channel-slack-block-kit-embed} Switch Slack to the Block Kit Embed variant. Each message gets a colored vertical bar on the left side: * **Blurple** for agent replies * **Per-tool colors** for tool notifications (e.g. blue for Bash, green for Read) * **Gray** for thinking indicators and cost footers **Triggers:** `add slack embed`, `enable slack embed`, `slack block kit embed`, `rich slack embed` **Prerequisite:** Slack must already be connected via `/add-channel-slack`. #### Reverting display modes Run the same skill for the mode you want, or manually change the import back. Restart Nagi after switching. *** ## Discord ### `/add-channel-discord` β€” Connect Discord {#add-channel-discord} Connect Discord using Gateway Intents as a bot. The skill guides you through bot creation, token setup, server invitation, group registration, and connection verification. **Triggers:** `add discord`, `setup discord`, `connect discord`, `add channel discord` #### Prerequisites * A Discord server where you have permission to add bots * Access to the [Discord Developer Portal](https://discord.com/developers/applications) #### Token needed | Token | Where to find it | |---|---| | Bot Token | Developer Portal > Application > Bot > Reset Token | Stored in `deploy/{ASSISTANT_NAME}/.env` as `DISCORD_BOT_TOKEN`. #### Required privileged Gateway Intents These must be enabled in the Developer Portal under Bot > Privileged Gateway Intents: * **MESSAGE CONTENT INTENT** -- required to read message content * **SERVER MEMBERS INTENT** #### Required bot permissions `Send Messages`, `Read Message History`, `Create Public Threads`, `Send Messages in Threads` #### Setup tips * Developer Mode must be enabled in Discord (Settings > App Settings > Advanced) to copy channel IDs. * The JID format for Discord groups is `discord:CHANNEL_ID` (e.g. `discord:1234567890`). * The skill provides an OAuth2 URL template for inviting the bot to your server. * If you see a "Used disallowed intents" error, enable the required intents in the Developer Portal and restart. #### Supported features * Trigger-based or main-channel (no trigger) operation * Multi-channel operation alongside Slack or Asana *** ### `/add-channel-discord-embed` β€” Discord Embed Display {#add-channel-discord-embed} Switch Discord channel display to Embed rich format. Tool notifications appear as colored embedded messages instead of plain text. **Triggers:** `add discord embed`, `enable discord embed`, `discord embed`, `rich discord`, `discord rich display` **Prerequisite:** Discord must already be connected via `/add-channel-discord`. Like Slack display modes, switching is done by changing the import in `deploy/{ASSISTANT_NAME}/host/entry.ts`: ```typescript // Plain text (default) const { createDiscordFactory } = await import("@nagi/channel-discord"); // Embed (rich display) const { createDiscordFactory } = await import("@nagi/channel-discord-embed"); ``` Do NOT change `deploy/templates/host/entry.template.ts`. Restart Nagi after switching. *** ## Asana ### `/add-channel-asana` β€” Connect Asana {#add-channel-asana} Connect Asana as a channel. Uses polling to watch task comments for trigger-pattern matches (e.g. `@ai ...`) and forwards them to the agent. No public URL or webhooks are needed. **Triggers:** `add asana`, `setup asana`, `connect asana`, `add channel asana` #### Prerequisites * An Asana account with access to the projects you want to watch * Permission to create a Personal Access Token #### Token needed | Token | Prefix | Where to create it | |---|---|---| | Personal Access Token (PAT) | `1/` | [My Apps](https://app.asana.com/0/my-apps) > Create new token | Stored in `deploy/{ASSISTANT_NAME}/.env` as `ASANA_PAT`. The PAT inherits the permissions of your Asana user, so the bot acts as you in every workspace. #### Environment variables | Variable | Required | Description | |---|---|---| | `ASANA_PAT` | Yes | Personal Access Token | | `ASANA_PROJECT_GIDS` | Yes | Comma-separated project GIDs to watch | | `ASANA_USER_GID` | No | Your Asana user GID (auto-resolved via `/users/me` if omitted) | | `ASANA_POLL_INTERVAL_MS` | No | Polling interval in ms (default: 60000, minimum: 10000) | #### How it works Asana uses polling rather than webhooks because workspace-level webhooks do not deliver comment events and the workspace events API is Enterprise+ only. The flow is: 1. A trigger comment (e.g. `@ai ...`) is detected on a watched task 2. Nagi creates a **subtask** under the triggering task, named `ai > {first line of request}` 3. A short pointer comment is posted on the parent task linking to the subtask 4. The agent's reply (plus thinking/cost notifications if agent-hooks is enabled) goes to the subtask 5. Follow-up `@ai` comments inside the subtask continue the same conversation without creating another nesting level #### Setup tips * The trigger must appear at the start of the comment body (after HTML stripping). Leading text before `@ai` will prevent matching. * Comments authored by the bot's own user GID are ignored to prevent loops. * Project GIDs can be found in the Asana URL: `https://app.asana.com/0/{PROJECT_GID}/...` * The JID format for Asana groups is `asana:{PROJECT_GID}`. * Shorter polling intervals reduce latency but consume more of the 150 req/min API rate limit. Keep the default 60s unless you have a specific need. #### Supported features * Project-level watching (one group per project) * Trigger-pattern detection on task comments * Auto-subtask replies to keep parent tasks clean * Subtask follow-ups for multi-turn conversations * Task context injection (task name, description, comment history included automatically) * Comment-only filtering (system stories like assignments are ignored) * Self-reply guard via user GID * Increment-only polling to minimize API calls * Multi-channel operation alongside Slack or Discord #### Known limitations * **Reply routing is last-write-wins** per project -- if two triggers arrive between poll ticks, the reply goes to whichever was processed last * **In-memory watchlist** -- after a restart, follow-ups on old agent subtasks are no longer picked up (start a new conversation by triggering on the parent task again) * **In-memory cursors** -- triggers that arrive during downtime are skipped * **PAT = your identity** -- the bot acts as you with all your permissions * **Polling latency** -- default 60s delay; adjustable via `ASANA_POLL_INTERVAL_MS` * **No attachment support** -- Asana attachments in comments are not forwarded *** ## Display mode comparison All display-mode skills work by swapping a single import line. No other code changes are needed because every variant exports the same factory function with the same interface. | Platform | Default | Rich | Rich + colored border | |---|---|---|---| | Slack | `@nagi/channel-slack` | `@nagi/channel-slack-block-kit` | `@nagi/channel-slack-block-kit-embed` | | Discord | `@nagi/channel-discord` | -- | `@nagi/channel-discord-embed` | After changing the import, run `pnpm exec tsc --noEmit` to verify TypeScript compiles, then restart Nagi. --- --- url: /en/07_skills_deploy.md --- # Deploy & Sync Skills Skills for expanding templates, synchronizing configuration files, and rebuilding container images. These skills maintain the **three-layer model** that separates upstream defaults from per-assistant customizations and runtime state. ## Three-Layer Model All deploy/sync skills operate on a layered file architecture: ``` deploy/templates/ (Layer 1: pristine upstream β€” git-tracked, never edited) | | /deploy skill copies templates -> per-assistant defaults v deploy/{ASSISTANT_NAME}/ (Layer 2: user-editable defaults β€” customized per assistant) | | /update-groups or pnpm dev startup copies defaults -> runtime v __data/{ASSISTANT_NAME}/ (Layer 3: runtime β€” mounted into containers, active config) ``` Understanding which layer a skill operates on is key to choosing the right one. ## Template System Templates live under `deploy/templates/` and are organized by target: | Directory | Contents | |-----------|----------| | `deploy/templates/host/` | Host orchestrator entry point (`entry.template.ts`) | | `deploy/templates/container/claude-code/` | Claude Code container entry + type stub | | `deploy/templates/container/open-code/` | Open Code container entry + type stub | | `deploy/templates/groups/{channel}/{group}/` | Group prompt files (`CLAUDE.md`, `AGENTS.md`, etc.) | | `deploy/templates/launchd/` | macOS launchd plist with `{{PLACEHOLDER}}` variables | Entry point templates (`.template.ts`) contain plugin registrations, import declarations, and configuration blocks. Group prompt files are plain Markdown with no placeholder substitution. The launchd plist uses `{{PLACEHOLDER}}` syntax (e.g. `{{NODE_PATH}}`, `{{PROJECT_ROOT}}`) that gets resolved to machine-specific paths during materialization. *** ## `/deploy` --- Full Template Sync {#deploy} The primary deployment skill. Expands templates from `deploy/templates/` into `deploy/{ASSISTANT_NAME}/`, covering all targets in one operation. This is the skill to use when setting up a new assistant or pulling in upstream template changes. **Triggers:** `deploy`, `sync deploy`, `update entry`, `sync entry`, `sync group templates`, `sync launchd` **What it syncs:** | Target | Template Source | Materialized Output | |--------|---------------|---------------------| | Host entry | `deploy/templates/host/entry.template.ts` | `deploy/{ASSISTANT_NAME}/host/entry.ts` | | Claude Code entry | `deploy/templates/container/claude-code/entry.template.ts` | `deploy/{ASSISTANT_NAME}/container/claude-code/entry.ts` | | Open Code entry | `deploy/templates/container/open-code/entry.template.ts` | `deploy/{ASSISTANT_NAME}/container/open-code/entry.ts` | | Group prompts | `deploy/templates/groups/{channel}/{group}/*.md` | `deploy/{ASSISTANT_NAME}/groups/{channel}/{group}/*.md` | | Launchd plist | `deploy/templates/launchd/com.nagi.plist` | `deploy/{ASSISTANT_NAME}/launchd/com.nagi.plist` | **Workflow:** 1. Detects available assistant names from `deploy/*/` directories and asks which one to target (or creates a new one). 2. Asks which targets to sync (All, Host, Claude Code, Open Code, Groups, Launchd). 3. For new assistants, initializes the data directory (`__data/{ASSISTANT_NAME}/`) and `.env` file, then guides token configuration for agent authentication and messaging channels. 4. Checks whether local files already exist. Missing files are copied fresh from templates. Existing files are diffed against templates. 5. For entry points, merges new template additions (new plugin registrations, imports) into the local file while preserving user customizations. 6. For group prompts, asks per-file before overwriting any changed defaults. 7. For launchd, detects system paths and substitutes `{{PLACEHOLDER}}` values, then validates with `plutil -lint`. 8. Runs TypeScript compilation to verify entry points compile without errors. **When to use:** Initial setup of a new assistant, after pulling upstream changes that update templates, or whenever you need a full sync across all targets. *** ## `/update-entry` --- Entry Point Sync {#update-entry} Regenerates a single entry point (host or container) from its template. This is a focused alternative to `/deploy` when you only need to update one entry file. **Triggers:** `update entry`, `sync entry`, `refresh entry` **Workflow:** 1. Asks whether to update the Host entry or a Container entry (Claude Code / Open Code). 2. If the local file is missing, copies fresh from template and skips merge. 3. If the file exists, diffs the template against the local version. Identifies new registrations, user customizations, and conflicts. 4. Merges new template additions while preserving user-added plugins, custom config, and extra logic. 5. Verifies TypeScript compilation for the updated target. **What it checks during merge:** * **Host entry:** channel plugin registrations (`registry.register`), MCP plugin registrations (`registerMcpPlugin`), hooks plugin registration (`registerHooksPlugin`), mount allowlist config (`setMountAllowlist`) * **Container entry:** plugin imports (`import(pluginPath)`), plugin push blocks (`plugins.push`), hook factory wiring (`createHooks`) **When to use:** After adding a new plugin to the template, or when you want to pull in template changes for just one entry point without touching groups or launchd. *** ## `/update-groups` --- Group Prompt Sync {#update-groups} Syncs group prompt defaults from Layer 2 (`deploy/{ASSISTANT_NAME}/groups/`) to Layer 3 (`__data/{ASSISTANT_NAME}/groups/`). This copies the user-editable defaults into the runtime directory that containers actually mount. **Triggers:** `update groups`, `sync groups`, `refresh claude.md`, `update agent config` **Workflow:** 1. Scans `deploy/{ASSISTANT_NAME}/groups/` for all prompt files. 2. Compares each file with its counterpart in `__data/{ASSISTANT_NAME}/groups/`. 3. New files are copied automatically. Changed files prompt the user to choose: overwrite, keep runtime version, or show diff. 4. Verifies all files are in sync after applying changes. **Note:** This skill syncs Layer 2 to Layer 3. If you need to update Layer 2 from upstream templates (Layer 1), run `/deploy` with the Groups target first, then `/update-groups`. **Note:** Changes take effect on the next container launch. If nagi is running, restart with `pnpm dev` or the next message will pick up the new prompts. **When to use:** After editing `CLAUDE.md`, `AGENTS.md`, or other prompt files under `deploy/{ASSISTANT_NAME}/groups/` and wanting to push those changes to the running runtime immediately. *** ## `/update-container` --- Container Rebuild {#update-container} Rebuilds the nagi-agent Docker image. This does not sync templates; it rebuilds the container image from source. **Triggers:** `update container`, `rebuild container`, `rebuild image`, `rebuild docker` **Supported images:** | Agent | Image Name | Build Script | |-------|-----------|-------------| | Claude Code | `nagi-agent:latest` | `./container/claude-code/build.sh` | | Open Code | `nagi-agent-opencode:latest` | `./container/open-code/build.sh` | **What triggers a rebuild:** * Changes to `container/{agent}/Dockerfile` * Changes to agent-runner source (`host/agent-runner-claudecode/src/` or `host/agent-runner-opencode/src/`) * Changes to container plugins (`container/plugins/`) * Changes to container entry templates (`deploy/templates/container/{agent}/entry.template.ts`) **Workflow:** 1. Asks which agent image to rebuild (Claude Code or Open Code). 2. Checks that Docker is running. 3. Runs the build script for the selected agent. 4. Verifies the new image exists and reports its size. 5. Restarts the nagi launchd service via `launchctl kickstart` and confirms it is running. **When to use:** After modifying a Dockerfile, agent-runner source code, MCP plugins, or container plugins. Entry point or group prompt changes do NOT require a container rebuild since those files are mounted at runtime. *** ## Choosing the Right Skill | Scenario | Skill | |----------|-------| | First-time setup of a new assistant | `/deploy` | | Pulled upstream changes, need full sync | `/deploy` | | Added a new plugin to the host entry template | `/update-entry` (Host) | | Added a new plugin to the container entry template | `/update-entry` (Container) | | Edited CLAUDE.md and want it live now | `/update-groups` | | Changed Dockerfile or agent-runner code | `/update-container` | | Changed a container plugin | `/update-container` | | Updated group templates AND want runtime sync | `/deploy` (Groups), then `/update-groups` | ## Common Workflow Examples ### Setting up a new assistant from scratch ``` /deploy -- creates deploy/mybot/, initializes .env, syncs all templates /update-groups -- pushes group defaults to __data/mybot/groups/ /update-container -- builds the Docker image ``` ### Updating agent personality after editing CLAUDE.md ``` # Edit deploy/mybot/groups/slack/main/CLAUDE.md /update-groups -- syncs the edited file to __data/mybot/groups/ # Restart nagi or send a new message to pick up changes ``` ### Pulling in a new plugin added to upstream templates ``` /update-entry -- merges the new plugin registration into your local entry.ts /update-container -- rebuilds if the plugin changed container-side code ``` --- --- url: /en/12_skills_group_prompt.md --- # Group Prompt Skills Skills for creating and editing the prompt files that shape agent behavior. These files live under `deploy/{ASSISTANT_NAME}/groups/{channel}/{group}/` and are loaded into the agent's system prompt at session start. ## Prompt file hierarchy {#file-hierarchy} Each group directory can contain several Markdown files. They are loaded differently depending on their name: | File | Loaded by | Purpose | |------|-----------|---------| | `CLAUDE.md` | Claude Code SDK (automatic) | Main instructions the SDK injects before any other prompt content. | | `AGENTS.md` | Open Code runner only | Tool inventory and usage guidelines. Ignored by the Claude Code runner to avoid conflicting persona instructions. | | `IDENTITY.md` | Agent runner (`systemPrompt.append`) | Agent name, character, speech style, language preference. | | `SOUL.md` | Agent runner (`systemPrompt.append`) | Mission statement, core values, behavioral principles. | | `INSTRUCTIONS.md` | Agent runner (`systemPrompt.append`) | Security rules, tool usage rules, output format constraints. | | `*.md` (custom) | Agent runner (`systemPrompt.append`) | Any additional Markdown file you create is also loaded. | ### How files are loaded The Claude Code agent runner scans the group directory at session start and collects every `*.md` file **except** `CLAUDE.md` and `AGENTS.md`. Those collected files are sorted alphabetically and concatenated into a single string that is appended to the system prompt via the `systemPrompt.append` field. Because files are sorted alphabetically, the effective load order for the standard set is: ``` IDENTITY.md -> INSTRUCTIONS.md -> SOUL.md -> (any custom files) ``` `CLAUDE.md` is handled separately by the Claude Code SDK itself and is always loaded regardless of other files. ### Directory layout example ``` deploy/{ASSISTANT_NAME}/groups/ slack/ main/ CLAUDE.md # SDK auto-loaded IDENTITY.md # -> systemPrompt.append SOUL.md # -> systemPrompt.append INSTRUCTIONS.md # -> systemPrompt.append skills.config.json # optional skill allowlist (/configure-skills) discord/ main/ CLAUDE.md AGENTS.md # Open Code only ``` ### Template vs. deploy layers Nagi maintains two layers of prompt files: * **`deploy/templates/groups/`** -- The pristine upstream baseline. Do not edit these directly; they are overwritten during upgrades. * **`deploy/{ASSISTANT_NAME}/groups/`** -- Your editable copies. The skills below always read from and write to this layer. After editing, files are synced to the runtime directory (`__data/{ASSISTANT_NAME}/groups/`) so the running agent picks them up. ## Best practices for writing prompt files {#best-practices} * **Keep files focused.** Put identity traits in `IDENTITY.md`, values in `SOUL.md`, and rules in `INSTRUCTIONS.md`. This makes it easy to update one aspect without touching the others. * **Use clear Markdown structure.** Headings, bullet lists, and short paragraphs are easier for the model to follow than long prose. * **Be specific over vague.** "End every sentence with `~noda`" is better than "speak in a cute way." * **Avoid contradictions across files.** Since all files are concatenated, conflicting instructions in different files can confuse the agent. Review the full set when adding new rules. * **Keep total size reasonable.** All prompt files share the context window with the user's conversation. Aim for concise instructions rather than exhaustive documentation. * **Test after editing.** Use `/nagi-restart` (or wait for the next container launch) to verify the agent behaves as expected. *** ## `/create-group-prompt` β€” Create Prompt {#create-group-prompt} Interactively create new prompt files under `deploy/{ASSISTANT_NAME}/groups/{channel}/{group}/`. **Triggers:** `create group prompt`, `add group prompt`, `create prompt`, `add identity`, `add soul` ### Workflow 1. **Select channel and group** -- Lists existing groups (e.g., `slack/main`, `discord/main`) and lets you pick one or specify a new one. 2. **Check existing files** -- Shows which prompt files already exist in the selected directory so you can avoid duplicates. 3. **Choose files to create** -- Multi-select from `IDENTITY.md`, `SOUL.md`, `INSTRUCTIONS.md`, `AGENTS.md`, or a custom filename. 4. **Define content** -- For each selected file, the skill asks targeted questions (name, personality, speech style, mission, rules, etc.) and generates structured Markdown. 5. **Write files** -- Previews each file's content and writes it after confirmation. 6. **Sync to runtime** -- Optionally copies the new files to `__data/{ASSISTANT_NAME}/groups/` so they take effect immediately. ### Available file types | File | What the skill asks | |------|---------------------| | `IDENTITY.md` | Name, character/personality, speech style, language preference | | `SOUL.md` | Primary mission, core values, behavioral principles | | `INSTRUCTIONS.md` | Security rules, tool usage rules, output format rules | | `AGENTS.md` | MCP tools, built-in tools, custom skills, per-tool usage rules | | Custom `*.md` | Purpose and content (free-form) | *** ## `/update-group-prompt` β€” Edit Prompt {#update-group-prompt} Interactively edit an existing group prompt file. Previews every change as a unified diff before saving. **Triggers:** `update group prompt`, `edit group prompt`, `modify claude.md`, `edit identity`, `update soul`, `update instructions` ### Workflow 1. **Select group** -- Lists groups under `deploy/{ASSISTANT_NAME}/groups/` and asks which one to edit. 2. **Select file** -- Lists Markdown files in the chosen group directory. 3. **Choose edit mode**: * **Natural language instruction** (recommended) -- Describe the change in plain text (e.g., "make the tone more casual", "add a rule to never output API keys") and the skill applies it. * **Append** -- Add new content to the end of the file. 4. **Preview diff** -- A unified diff of the proposed change is shown. You can approve, view the full file, redo, or cancel. 5. **Write** -- The edit is applied via the `Edit` tool. If it fails, the skill reports the error and offers to retry. 6. **Sync to runtime** -- Optionally copies the updated file to `__data/{ASSISTANT_NAME}/groups/`. 7. **Restart** -- If the agent is running under launchd, offers to restart so changes take effect immediately. ### Rules the skill enforces * Always previews a diff before saving -- no silent writes. * Edits `deploy/{ASSISTANT_NAME}/groups/` only, never `deploy/templates/groups/` or `__data/` directly. * One file per invocation. Run the skill again to edit another file. * Fails loudly on errors instead of silently proceeding. *** ## `/configure-skills` β€” Skills Allowlist {#configure-skills} Interactively edit `deploy/{ASSISTANT_NAME}/groups/{channel}/{group}/skills.config.json` to control which skills (under `container/skills/`) get mounted into the agent session for that group. Use this to shrink the system prompt on casual channels that do not need the full skill catalog -- every omitted skill translates directly into fewer input tokens per turn. **Triggers:** `configure skills`, `edit skills`, `skills allowlist`, `skills config`, `limit skills` ### Config file format * Path: `deploy/{ASSISTANT_NAME}/groups/{channel}/{group}/skills.config.json` * Shape: ```json { "enabled": ["status", "slack-formatting"] } ``` * Evaluation (implemented in `host/orchestrator/src/container-runner.ts`): * File missing -> load every skill (backward-compatible default). * `enabled` is an array -> only listed names are copied into the session. * `enabled` is `[]` -> no skills are loaded at all. * Malformed JSON -> the orchestrator warns and falls back to loading everything. * Unknown name in `enabled` -> the orchestrator warns and skips that entry. ### Workflow 1. **Select assistant** -- Detects `deploy/*/` and asks which `ASSISTANT_NAME` to edit. 2. **Select group** -- Lists `{channel}/{group}` pairs under `deploy/{ASSISTANT_NAME}/groups/` and asks which one. 3. **Show current state** -- Parses any existing `skills.config.json` and shows the active allowlist; if the file is absent it reports "all skills loaded". 4. **Pick skills** -- Multi-selects from the skills available in `container/skills/`, marking the currently-enabled ones. 5. **Preview diff** -- Displays the before/after JSON plus the added/removed skill names. 6. **Write or delete** -- If the user ends up selecting every skill, offers to delete the config file (reverting to the default) instead of maintaining a full allowlist that drifts when new skills are added upstream. 7. **Restart prompt** -- If Nagi is running under launchd, offers `/nagi-restart`. For manually launched processes (`pnpm dev`, `npx tsx ...`), asks the user to restart by hand. ### Cost impact Each SKILL.md averages roughly 4,000-5,000 input tokens. In a measured test, trimming from 9 skills down to 2 reduced input tokens from 51,897 to 14,972 per turn -- about a 71% drop. The savings compound across multi-turn conversations because every container spawn rebuilds the session skills directory. ### Notes * The skill always edits `deploy/{ASSISTANT_NAME}/groups/`. It never touches `deploy/templates/` or `__data/` directly. * `skills.config.json` recognizes only the `enabled` key. Other keys (`disabled`, `skills`, ...) are ignored by the orchestrator, and the skill will not emit them. * The session skills directory is wiped and recreated on every container spawn, so stale skills removed from the allowlist do not linger between runs. --- --- url: /en/03_setup.md --- # Installation & Configuration ## Prerequisites Make sure the following tools are installed before you begin. | Tool | Version | Notes | |------|---------|-------| | **Node.js** | 22 or later | Check with `node -v` | | **pnpm** | 9.x (repo pins 9.15.4) | Check with `pnpm -v`. Install via `corepack enable && corepack prepare pnpm@9.15.4 --activate` | | **Docker** | Desktop or Engine | Required for running agent containers. Verify with `docker info` | | **Claude Code** | Latest | Used as the agent runner. Install from [Claude Code docs](https://docs.anthropic.com/en/docs/claude-code/overview) | > **Tip:** On macOS you can install Node.js and pnpm with Homebrew: > > ```bash > brew install node > corepack enable && corepack prepare pnpm@9.15.4 --activate > ``` ## Installation Clone the repository and install all workspace dependencies. ```bash git clone https://github.com/yukihirop/nagi.git cd nagi pnpm install ``` After installation succeeds, verify the build works: ```bash pnpm build ``` ## Initial Setup Open a **Claude Code** session inside the repository root and run the setup skill: ``` /setup ``` The setup skill walks you through the following steps interactively: 1. **Add channels** -- Configure channel plugins for Slack, Discord, Asana, etc. 2. **Register groups** -- Map channels to groups that define how agents behave. 3. **Create group prompts** -- Define agent behavior with `CLAUDE.md` and related prompt files (`IDENTITY.md`, `INSTRUCTIONS.md`, etc.). 4. **Configure launchd service** (macOS) -- Auto-start Nagi as a background service. > **Note:** You can re-run `/setup` at any time to add more channels or groups. ## Environment Variables Each channel plugin requires its own credentials. The setup skill will prompt you, but you can also set them manually. Common variables include: * `SLACK_BOT_TOKEN` / `SLACK_APP_TOKEN` -- For the Slack channel * `DISCORD_BOT_TOKEN` -- For the Discord channel * `ASANA_ACCESS_TOKEN` -- For the Asana channel Store these in the `.env` file at the repository root. This file is gitignored by default. ## Running ### Development Mode Start the orchestrator in the foreground for local development and debugging: ```bash pnpm dev ``` This runs `deploy/{ASSISTANT_NAME}/host/entry.ts` directly with `tsx`. ### launchd Service (macOS) For persistent background operation, use the launchd skills inside a Claude Code session: | Skill | Description | |-------|-------------| | `/nagi-start` | Start the launchd service | | `/nagi-stop` | Stop the launchd service | | `/nagi-restart` | Restart the service (e.g., after config changes) | | `/nagi-logs` | Tail the service log output | > **Tip:** Run `/setup-launchd` first if you haven't configured the launchd plist yet. ## CLI The Nagi CLI lets you run agent prompts directly from the terminal without going through a channel. ```bash # Show help pnpm nagi --help # Run a prompt in the default (main) group pnpm nagi "Summarize today's tasks" # Run a prompt in a specific group pnpm nagi -g my-group "Check the latest PR" # Resume a previous session pnpm nagi -s "Continue where we left off" # List all registered groups pnpm nagi --list # Pipe input from another command echo "Explain this error" | pnpm nagi ``` ### CLI Options | Flag | Short | Description | |------|-------|-------------| | `--group ` | `-g` | Target a specific group (default: main) | | `--session ` | `-s` | Resume a previous session by ID | | `--list` | `-l` | List all registered groups | | `--verbose` | `-v` | Show container details | | `--help` | `-h` | Print usage information | ## Web UI Launch the web dashboard for a visual overview of agents and groups: ```bash pnpm ui:dev ``` This starts both the UI frontend and the UI server in parallel. For production use: ```bash pnpm ui:build pnpm ui:preview ``` ## Troubleshooting ### `pnpm install` fails * Make sure you are using pnpm 9.x. Run `pnpm -v` to check. * Delete `node_modules` and the pnpm lock file, then retry: `rm -rf node_modules pnpm-lock.yaml && pnpm install`. ### Docker container won't start * Verify Docker is running: `docker info`. * Rebuild the agent image with the `/update-container` skill inside Claude Code. ### launchd service not starting * Check logs with `/nagi-logs`. * Make sure you ran `/setup-launchd` to generate the plist. * Inspect the plist directly: `launchctl list | grep nagi`. ### Channel not receiving messages * Double-check that your tokens in `.env` are correct and not expired. * For Slack, ensure Socket Mode is enabled in the Slack app configuration. * For Discord, confirm that the bot has the required Gateway intents enabled. * Restart the service with `/nagi-restart` after any configuration change. --- --- url: /en/01_introduction.md --- # Introduction Nagi is an agent orchestration platform that routes messages from chat services β€” Slack, Discord, Asana, and more β€” to AI agents running in isolated Docker containers. It handles the full lifecycle: receiving a user message, queuing it, spinning up a container, streaming the agent's response back, and posting the reply to the originating channel. Nagi is designed for self-hosted, always-on operation. A single instance can serve multiple assistants, each with its own channels, groups, and prompt configurations, managed as a macOS launchd service or a standard process. ## Key Features ### Multi-Channel Integration Connect multiple messaging platforms through a unified channel plugin system. Each channel plugin runs on the host and translates platform-specific events into a common message format that the orchestrator understands. Currently supported channels: * **Slack** β€” Socket Mode connection (no public URL or ingress required) * **Discord** β€” Gateway Intents bot connection * **Asana** β€” Polls task comments for trigger-pattern matches Rich display variants are also available. For example, Slack channels can use **Block Kit** or **Block Kit Embed** for formatted tool-notification messages, and Discord channels can use **Embed** for similar rich output. ### Container Isolation Every agent invocation runs inside its own Docker container. This provides: * **Session isolation** β€” each request gets a clean environment; no state leaks between sessions * **Dynamic MCP plugin registration** β€” tools like Ollama (local LLM) and Vercel (deployment) are registered at container startup and made available to the agent via the Model Context Protocol * **Secure credential handling** β€” a Credential Proxy running on the host intercepts API calls from the container and injects real tokens, so containers never hold actual secrets ### Plugin System Nagi's functionality is extended through two categories of plugins: | Type | Runs On | Purpose | Examples | |------|---------|---------|----------| | Channel Plugins | Host | Connect to messaging platforms and translate events | `channel-slack`, `channel-discord`, `channel-asana` | | MCP Plugins | Container | Provide tools to the AI agent inside the container | `mcp-ollama`, `mcp-vercel` | Channel plugins implement the `Channel` interface from `@nagi/channel-core`. MCP plugins run as stdio-based MCP servers inside the Docker container and are dynamically registered with the agent runner (Claude Code or Open Code) at startup. ### Group-Based Routing Messages are routed into **groups**, each of which can have its own system prompt, identity, and behavioral instructions. This lets a single assistant behave differently depending on the channel or context β€” for example, one group might handle technical support questions while another handles deployment requests. Group prompts are organized under `deploy/{ASSISTANT_NAME}/groups/{channel}/{group}/` and can include files like `CLAUDE.md`, `AGENTS.md`, and `IDENTITY.md`. ### Multiple Agent Backends Nagi supports more than one agent runtime: * **Claude Code** β€” Anthropic's official CLI agent (default) * **Open Code** β€” an alternative runtime supporting OpenRouter, Gemini, and OpenAI providers Each assistant can be configured to use either backend independently. ## Project Structure Nagi is organized as a pnpm workspace monorepo, built with Turbo: ``` nagi/ β”œβ”€β”€ host/ # Host-side services β”‚ β”œβ”€β”€ orchestrator/ # Core orchestrator (message routing, container lifecycle) β”‚ β”œβ”€β”€ agent-runner-*/ # Agent runners (claude-code, open-code) β”‚ β”œβ”€β”€ credential-proxy/ # Credential Proxy for secure API key injection β”‚ └── plugins/ # Channel plugins (slack, discord, asana, etc.) β”œβ”€β”€ libs/ # Shared libraries β”‚ β”œβ”€β”€ channel-core/ # Channel interface and base types β”‚ β”œβ”€β”€ db/ # SQLite persistence β”‚ β”œβ”€β”€ queue/ # Group-based message queue β”‚ β”œβ”€β”€ router/ # Message routing logic β”‚ β”œβ”€β”€ ipc/ # Host-container IPC β”‚ β”œβ”€β”€ config/ # Configuration (Zod-validated) β”‚ β”œβ”€β”€ logger/ # Structured logging β”‚ β”œβ”€β”€ scheduler/ # Scheduled tasks β”‚ β”œβ”€β”€ auth/ # Authentication β”‚ └── types/ # Shared TypeScript types β”œβ”€β”€ container/ # Container-side code β”‚ β”œβ”€β”€ claude-code/ # Claude Code agent β”‚ β”œβ”€β”€ open-code/ # Open Code agent β”‚ β”œβ”€β”€ plugins/ # MCP plugins (ollama, vercel) β”‚ └── skills/ # Agent skills β”œβ”€β”€ tools/ # Developer and operator tools β”‚ β”œβ”€β”€ cli/ # Nagi CLI β”‚ β”œβ”€β”€ ui/ # Web dashboard (frontend) β”‚ β”œβ”€β”€ ui-server/ # Web dashboard (backend) β”‚ └── website/ # Documentation site └── deploy/ # Deployment layer β”œβ”€β”€ templates/ # Pristine templates (tracked in git) └── {ASSISTANT_NAME}/ # Materialized per-assistant config (git-ignored) ``` --- --- url: /en/10_skills_mcp.md --- # MCP Plugin Skills Skills for adding MCP (Model Context Protocol) plugins that provide external tools to agents inside containers. ## What is MCP? [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open standard that lets AI agents call external tools through a unified interface. In nagi, MCP plugins run inside Docker containers alongside the agent. The host process registers each plugin with the orchestrator, which spawns the MCP server and exposes its tools to the agent automatically. When you add an MCP plugin, three things happen: 1. The plugin package is included in the Docker image at build time. 2. `deploy/{ASSISTANT_NAME}/host/entry.ts` registers the plugin with `orchestrator.registerMcpPlugin()`. 3. At runtime, the agent sees the plugin's tools (prefixed `mcp____`) and can call them like any other tool. *** ## `/add-mcp-ollama` --- Add Ollama {#add-mcp-ollama} Add the Ollama MCP plugin so container agents can access local LLM models for cheaper or faster tasks. **Triggers:** `add ollama`, `setup ollama`, `enable ollama` ### Prerequisites * **Ollama installed and running on the host.** macOS: `brew install ollama`; Linux: `curl -fsSL https://ollama.com/install.sh | sh`; or download from . * **At least one model pulled.** Run `ollama pull ` before using the plugin. * **Network reachability.** The container reaches the host via `host.docker.internal:11434`. On Linux, nagi automatically adds `--add-host=host.docker.internal:host-gateway`. ### Supported Models (examples) | Model | Size | Notes | |---|---|---| | `llama3.2` | ~2 GB | Fast, good general-purpose | | `mistral` | ~4 GB | Strong reasoning quality | | `gemma2` | ~5 GB | Google's open model | Any model available in your local Ollama library can be used. Run `ollama list` to see what is installed. ### Available Tools Once configured, container agents have access to: | Tool | Description | |---|---| | `ollama_list_models` | List installed local models with sizes | | `ollama_generate` | Send a prompt to a local model and get a response | ### Configuration The skill performs the following steps: 1. **Pre-flight** --- Checks that Ollama is installed, running, and has at least one model. 2. **Register plugin** --- Adds the Ollama MCP plugin to `deploy/{ASSISTANT_NAME}/host/entry.ts`: ```typescript orchestrator.registerMcpPlugin("ollama", { entryPoint: "/app/mcp-plugins/ollama/dist/index.js", }); ``` No API token is needed because Ollama runs locally. 3. **Custom host (optional)** --- If Ollama runs on a different machine or port, pass the address via an environment variable: ```typescript orchestrator.registerMcpPlugin("ollama", { entryPoint: "/app/mcp-plugins/ollama/dist/index.js", env: { OLLAMA_HOST: "http://192.168.1.100:11434" }, }); ``` 4. **Rebuild & verify** --- Rebuild the Docker image (`./container/claude-code/build.sh` or `./container/open-code/build.sh`), restart nagi, and test via Slack/Discord. ### Troubleshooting | Symptom | Fix | |---|---| | "Failed to connect to Ollama" | Ensure `ollama serve` is running. Verify with `curl http://localhost:11434/api/tags`. | | Agent does not see Ollama tools | Confirm `registerMcpPlugin("ollama", ...)` is in entry.ts, rebuild the image, and restart. | | No models available | Pull a model: `ollama pull llama3.2` | *** ## `/add-mcp-vercel` --- Add Vercel {#add-mcp-vercel} Add the Vercel MCP plugin so container agents can deploy websites, manage projects, and inspect deployments. **Triggers:** `add vercel`, `setup vercel`, `enable vercel` ### Prerequisites * **A Vercel account** at . * **An API token.** Create one at with at least deploy-level scope. ### Available Tools Once configured, container agents have access to: | Tool | Description | |---|---| | `vercel_list_projects` | List Vercel projects | | `vercel_create_project` | Create a new project | | `vercel_deploy` | Deploy files and receive a live URL | | `vercel_list_deployments` | List recent deployments | | `vercel_get_deployment` | Get details of a specific deployment | | `vercel_delete_project` | Delete a project | ### Configuration The skill performs the following steps: 1. **Pre-flight** --- Checks whether `VERCEL_API_TOKEN` already exists in `.env`. 2. **Get API token** --- Guides you through creating a token on the Vercel dashboard if you do not have one, then saves it to `.env`. 3. **Register plugin** --- Adds the Vercel MCP plugin to `deploy/{ASSISTANT_NAME}/host/entry.ts`: ```typescript const vercelEnv = readEnvFile(["VERCEL_API_TOKEN"]); if (vercelEnv.VERCEL_API_TOKEN) { orchestrator.registerMcpPlugin("vercel", { entryPoint: "/app/mcp-plugins/vercel/dist/index.js", env: { VERCEL_API_TOKEN: vercelEnv.VERCEL_API_TOKEN }, }); } ``` 4. **Rebuild & verify** --- Rebuild the Docker image, restart nagi, and test by asking the agent to list projects or deploy a page. ### Troubleshooting | Symptom | Fix | |---|---| | "VERCEL\_API\_TOKEN is not set" | Add the token to `.env` at the project root AND ensure entry.ts passes it via `registerMcpPlugin`. | | Agent does not see Vercel tools | Confirm `registerMcpPlugin("vercel", ...)` is in entry.ts, rebuild the image, and restart. | | Deploy fails | Verify the token: `curl -s -H "Authorization: Bearer $VERCEL_API_TOKEN" https://api.vercel.com/v9/projects`. Check that the token scope includes deploy permissions. | --- --- url: /en/14_skills_misc.md --- # Miscellaneous Skills ## `/add-context-probe` β€” Context Probe {#add-context-probe} Install a probe under `deploy/{ASSISTANT_NAME}/container/context/` to verify that the auto-mount mechanism into containers is working correctly. **Triggers:** `add context probe`, `context probe`, `probe context`, `verify context mount`, `test context mount` ### What is context mounting? Nagi can automatically mount host directories into agent containers so that files on the host are visible to the agent at runtime. Specifically, every subdirectory found under `deploy/{ASSISTANT_NAME}/container/context/` is mounted read-only into the container at `/workspace/extra/{name}`. The agent's `additionalDirectories` setting picks these paths up, and any `CLAUDE.md` files inside them are appended to the agent's context through the `CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD` mechanism. This is useful for injecting reference material, shared documentation, or project repositories into the agent without baking them into the Docker image. ### Why probes are useful Because the mount chain involves several moving parts (host directory scan, Docker bind mount, `additionalDirectories` registration, and `CLAUDE.md` auto-append), failures can be silent. A probe gives you a quick, deterministic way to confirm each link in the chain: * **Read path** β€” Can the agent read a known file at the expected container path? * **additionalDirectories path** β€” Does the agent see the mounted directory as an additional directory? * **CLAUDE.md auto-append path** β€” Does a `CLAUDE.md` placed in the probe directory get injected into the agent's context? If any of these checks fail, you know exactly which layer to investigate. ### Supported probes | Probe | Description | Best for | |-------|-------------|----------| | **Marker** | Creates a minimal `probe/` directory containing a `CLAUDE.md` and a `probe-marker.txt` marker file. | Quick smoke test of all three mount paths (lightweight, no network). | | **Git clone** | Clones a real repository into `context/{name}/`. | Verifying mount behavior with a real-world directory structure. If the cloned repo contains a `CLAUDE.md`, the auto-append path is also tested. | ### Usage examples **Install a marker probe:** ``` > /add-context-probe # Select "install" β†’ "marker" # Restart the service with /nagi-restart # Then ask the agent in chat: # "Read /workspace/extra/probe/probe-marker.txt and tell me the contents" # β†’ Expected answer: "probe-marker-ok" ``` **Install a clone probe (e.g. a shared docs repo):** ``` > /add-context-probe # Select "install" β†’ "clone" # Provide the repo URL, directory name, and clone depth # Restart with /nagi-restart # Then ask the agent: # "Read /workspace/extra/{name}/README.md and summarize it" ``` **Remove a probe when you are done:** ``` > /add-context-probe # Select "remove" β†’ pick the probe directory to delete # Restart with /nagi-restart ``` ### Verifying in the logs After restarting the service, run `/nagi-logs` and look for: * `Container mount configuration` containing `.../context/{name} -> /workspace/extra/{name} (ro)` β€” confirms the host-side mount succeeded. * `[agent-runner] Additional directories: ... /workspace/extra/{name}` β€” confirms the agent recognized the directory. ### Notes * Probe files land under `deploy/*/`, which is excluded by `.gitignore`, so they will never be committed to the repository. * The probe remains harmless if left in place, but removing it when no longer needed keeps the mount list clean. * The skill also supports a **status** action that lists everything currently under `context/` without making changes. --- --- url: /en/13_skills_scaffold.md --- # Plugin Scaffolding Skills Developer skills for generating new plugin boilerplate. Each skill interactively gathers configuration, generates a complete package, registers it in the deploy templates, and tells you what to implement next. All three skills use `AskUserQuestion` prompts to collect the information they need β€” you just invoke the slash command and answer the questions. ## `/create-plugin-channel` β€” Create Channel Plugin {#create-plugin-channel} Scaffold a new channel plugin that connects nagi to a messaging platform (following the pattern of the existing `channel-slack` and `channel-discord` plugins). **Triggers:** `create channel plugin`, `new channel`, `scaffold channel`, `add channel plugin` ### What it asks you | Question | Example | |----------|---------| | Channel name (lowercase) | `telegram`, `line` | | One-line description | "Telegram bot channel via grammy library" | | SDK / library | `grammy`, `whatsapp-web.js` | | Required credentials (env var) | `TELEGRAM_BOT_TOKEN` | | JID prefix | `tg:` for Telegram, `wa:` for WhatsApp | ### Generated file structure ``` host/plugins/channel-{name}/ β”œβ”€β”€ package.json # @nagi/channel-{name}, deps on channel-core / types / logger β”œβ”€β”€ tsconfig.json β”œβ”€β”€ vitest.config.ts └── src/ β”œβ”€β”€ index.ts # Re-exports channel class + factory + config type β”œβ”€β”€ {name}-channel.ts # Channel interface implementation with TODO stubs └── __tests__/ └── {name}-channel.test.ts # Basic unit tests (name, ownsJid, factory) ``` The skill also: * Adds `@nagi/channel-{name}` to the root `package.json` workspace dependencies. * Appends a registration block to `deploy/templates/host/entry.template.ts` that conditionally imports the channel when its env var is set. ### Customizing after scaffolding The generated `{name}-channel.ts` contains TODO comments at every method that needs real logic: | Method | What to implement | |--------|-------------------| | `constructor` | Initialize the SDK client | | `connect()` | Start the SDK, register message handlers, call `opts.onChatMetadata()` for all incoming messages and `opts.onMessage()` for registered groups | | `sendMessage()` | Extract the channel ID from the JID and send text (must never throw) | | `setTyping()` | Send a typing indicator, or leave as no-op | | `syncGroups()` | Discover available groups/channels and call `opts.onChatMetadata()` for each | ### Key design rules * **JID format:** `{prefix}:{platformId}` β€” must be unique across all channels. * **`ownsJid`:** Must match the JID prefix exactly. * **`sendMessage`:** Must never throw β€” log errors and return. * **`onMessage`:** Only call for registered groups (`opts.registeredGroups()[jid]`). * **`onChatMetadata`:** Call for ALL messages (enables group discovery). * **Mention translation:** Convert platform-specific mentions (e.g., `<@BOT_ID>`) to trigger pattern format (`@AssistantName`). ### Development workflow ```bash # 1. Install dependencies pnpm install # 2. Implement the Channel interface in src/{name}-channel.ts # 3. Add the SDK dependency pnpm --filter @nagi/channel-{name} add grammy # (or whichever SDK) # 4. Build & test pnpm build pnpm --filter @nagi/channel-{name} test # 5. Sync entry.ts and add credentials # Run /deploy (select Host), then add the token to .env # 6. Restart # Run /nagi-restart ``` ### Reference implementations * `host/plugins/channel-slack/` β€” Socket Mode, thread replies, message queueing, user name cache * `host/plugins/channel-discord/` β€” Gateway intents, thread creation, attachment handling, 2000-char splitting *** ## `/create-container-plugin-mcp` β€” Create MCP Plugin {#create-container-plugin-mcp} Scaffold a new MCP plugin that runs as a stdio MCP server inside agent containers (following the pattern of `mcp-ollama` and `mcp-vercel`). **Triggers:** `create mcp plugin`, `new mcp plugin`, `add mcp`, `scaffold mcp` ### What it asks you | Question | Example | |----------|---------| | Plugin name (lowercase, no `mcp-` prefix) | `youtube`, `github` | | One-line description | "YouTube analytics and video search" | | API token env var (if any) | `YOUTUBE_API_KEY` | | Target Dockerfile (Claude Code / Open Code / Both) | Claude Code | ### Generated file structure ``` container/plugins/mcp-{name}/ β”œβ”€β”€ package.json # @nagi/mcp-{name}, deps on @modelcontextprotocol/sdk + zod β”œβ”€β”€ tsconfig.json └── src/ └── index.ts # Starter MCP server with a placeholder tool ``` The skill also: * Adds a `COPY` + `RUN` block to the selected agent Dockerfile(s) so the plugin is built into the container image. * Appends an `orchestrator.registerMcpPlugin()` call to `deploy/templates/host/entry.template.ts`. If an API token is required, the registration is conditional on the env var being set. ### Customizing after scaffolding The generated `src/index.ts` contains a single placeholder tool (`{name}_hello`). Replace it with real tools using the `@modelcontextprotocol/sdk` API: ```typescript server.tool( "tool_name", "Description of what this tool does", { param: z.string().describe("Parameter description"), }, async (args) => { // Implementation return { content: [{ type: "text" as const, text: "result" }], }; }, ); ``` Environment variables declared in the `registerMcpPlugin` `env` option are passed into the container process and accessible via `process.env`. ### Development workflow ```bash # 1. Install dependencies pnpm install # 2. Implement tools in src/index.ts # 3. Build pnpm build # 4. Rebuild the Docker image ./container/claude-code/build.sh # and/or open-code # 5. Sync entry.ts and add credentials # Run /deploy (select Host) # If API token needed, add it to .env # 6. Restart and test # Run /nagi-restart, then send a message asking the agent to use the new tool ``` ### Reference implementations * `container/plugins/mcp-ollama/` β€” No API token, connects to local Ollama via `host.docker.internal` * `container/plugins/mcp-vercel/` β€” Requires `VERCEL_API_TOKEN`, calls external REST API *** ## `/create-container-plugin-agent-hooks` β€” Create Agent Hooks Plugin {#create-container-plugin-agent-hooks} Scaffold a new agent hooks plugin with hook factories and deploy template registration. Agent hooks send real-time notifications (tool execution, session start) back to chat channels via IPC. **Triggers:** `create agent hooks plugin`, `new agent hooks`, `scaffold agent hooks` ### What it asks you | Question | Example | |----------|---------| | Plugin name (lowercase) | `open-code`, `cursor` | | One-line description | "Tool execution and session notifications for Open Code" | | Hook types to support | PostToolUse, SessionStart, or both | | Target agent (Claude Code / Open Code / Both) | Claude Code | | Target entry.template.ts | `deploy/templates/container/claude-code/entry.template.ts` | ### Generated file structure ``` container/{agent}/plugins/agent-hooks-{name}/ └── index.mjs # Pure JavaScript ES Module β€” no TypeScript, no build step ``` Unlike the other two plugin types, agent-hooks plugins are a **single `.mjs` file** with no `package.json` or build step. They are loaded at runtime via dynamic `import()`. The skill also appends a `try/catch` registration block to the selected container `entry.template.ts` that imports the hook factories and wires them into the plugin system. ### Generated hook factories Depending on your selection, the file exports one or both of: | Export | Purpose | |--------|---------| | `createPostToolUseHook(chatJid, groupFolder, extraSkipTools, log)` | Called after every tool invocation. Sends a notification to the chat channel. Skips internal tools like `mcp__nagi__send_message` to avoid loops. | | `createSessionStartHook(chatJid, groupFolder, log)` | Called when a new agent session begins. Sends a "Thinking..." message to the chat channel. | Both factories return async hook callbacks. Notifications are delivered by writing JSON files to `/workspace/ipc/messages/` which the host process picks up. ### Key design rules * **Pure JavaScript** β€” Single `index.mjs` file, no TypeScript, no build step. * **IPC messaging** β€” Write JSON to `/workspace/ipc/messages/` for the host to consume. * **Atomic writes** β€” Always write to a `.tmp` file first, then `fs.renameSync` (prevents partial reads). * **Never throw** β€” Wrap all hook logic in try/catch; log errors and return `{}`. * **Skip internal tools** β€” Always skip `mcp__nagi__send_message` to avoid notification loops. ### Development workflow ```bash # 1. Edit the generated index.mjs to customize notification format # 2. Verify TypeScript still compiles (entry template references the plugin) pnpm exec tsc --noEmit # 3. Sync container entry # Run /deploy and select the target agent # 4. Rebuild Docker image ./container/claude-code/build.sh # and/or open-code # 5. Restart and test # Run /nagi-restart, then trigger tool use from chat ``` ### Reference implementation * `container/claude-code/plugins/agent-hooks/` β€” PostToolUse (tool icons + summary) and SessionStart ("Thinking..." message) --- --- url: /en/06_skills_service.md --- # Service Control Skills Skills for starting, stopping, restarting, and inspecting logs of the Nagi launchd service. These skills manage the macOS `launchd` agent (`com.nagi.{ASSISTANT_NAME}`) that keeps Nagi running in the background. All four skills begin by detecting the available assistants under `deploy/` and prompting you to choose which one to operate on. If you have only one assistant deployed, the selection is automatic. *** ## `/nagi-start` β€” Start Service {#nagi-start} Start the Nagi launchd service for a given assistant. **Triggers:** `start`, `start nagi` ### What the skill does 1. Lists deployed assistants and asks which one to start. 2. Checks whether the service is **already running** via `launchctl list`. If it is, the skill tells you so and suggests using `/nagi-restart` instead. 3. Loads the plist with `launchctl load ~/Library/LaunchAgents/com.nagi.{ASSISTANT_NAME}.plist`. 4. Verifies the service came up by checking that the PID is a number, the exit code is `0`, and the log contains `Orchestrator started`. 5. If startup fails, automatically inspects the error log at `__data/{ASSISTANT_NAME}/logs/nagi-{ASSISTANT_NAME}.error.log`. ### When to use * After initial setup or after a machine reboot (if the plist is not set to `RunAtLoad`). * When you have previously stopped the service with `/nagi-stop` and want to bring it back. ### Example ``` /nagi-start # -> Which assistant do you want to start? [asana-agent] # -> Loading com.nagi.asana-agent ... # -> Service started successfully. Orchestrator started. ``` *** ## `/nagi-stop` β€” Stop Service {#nagi-stop} Stop the Nagi launchd service for a given assistant. **Triggers:** `stop`, `stop nagi` ### What the skill does 1. Lists deployed assistants and asks which one to stop. 2. Unloads the plist with `launchctl unload ~/Library/LaunchAgents/com.nagi.{ASSISTANT_NAME}.plist`. 3. Verifies the service is no longer listed in `launchctl list`. ### When to use * Before making significant configuration changes that require a clean restart. * When you want to temporarily disable the assistant without removing the deployment. * For debugging: stop the service, then inspect logs at your own pace. ### Example ``` /nagi-stop # -> Which assistant do you want to stop? [asana-agent] # -> Unloading com.nagi.asana-agent ... # -> Service stopped. ``` *** ## `/nagi-restart` β€” Restart Service {#nagi-restart} Restart the Nagi launchd service in place, without a separate stop/start cycle. **Triggers:** `restart`, `restart nagi` ### What the skill does 1. Lists deployed assistants and asks which one to restart. 2. Performs an in-place restart using `launchctl kickstart -k`, which kills the running process and immediately relaunches it under the same job label. 3. Verifies the service is running again (PID is a number, exit code is `0`, logs show `Orchestrator started`). ### When to use * **After any configuration change** -- updated environment variables, new plugins, changed group prompts, etc. * After running `/deploy` or `/update-groups` to pick up the latest settings. * When the service is in a bad state and you want a quick recovery without manually unloading and reloading the plist. ### Example ``` /nagi-restart # -> Which assistant do you want to restart? [asana-agent] # -> Restarting com.nagi.asana-agent ... # -> Service restarted successfully. Orchestrator started. ``` > **Note:** `/nagi-restart` requires the service to already be loaded. If the service has been unloaded (via `/nagi-stop`), use `/nagi-start` instead. *** ## `/nagi-logs` β€” View Logs {#nagi-logs} Display the latest Nagi service logs. Supports both standard output and error logs. **Triggers:** `logs`, `show logs`, `nagi logs`, `check logs` ### What the skill does 1. Lists deployed assistants and asks which one to inspect. 2. Shows the last 50 lines of the standard log at `__data/{ASSISTANT_NAME}/logs/nagi-{ASSISTANT_NAME}.log`. 3. Can also show the last 30 lines of the **error log** at `__data/{ASSISTANT_NAME}/logs/nagi-{ASSISTANT_NAME}.error.log` on request. 4. For real-time log streaming, provides a `tail -f` command you can run directly. ### Log file locations | Log type | Path | |---|---| | Standard output | `__data/{ASSISTANT_NAME}/logs/nagi-{ASSISTANT_NAME}.log` | | Error output | `__data/{ASSISTANT_NAME}/logs/nagi-{ASSISTANT_NAME}.error.log` | ### When to use * To verify the service started correctly after `/nagi-start` or `/nagi-restart`. * To investigate unexpected behavior or errors. * To monitor activity in real time while testing a new configuration. ### Example ``` /nagi-logs # -> Which assistant's logs do you want to view? [asana-agent] # -> (last 50 lines of nagi-asana-agent.log) ``` To follow logs in real time: ``` tail -f __data/asana-agent/logs/nagi-asana-agent.log ``` --- --- url: /en/05_skills_setup.md --- # Setup Skills ## `/setup` β€” Initial Setup {#setup} Interactively run the full Nagi initial setup from scratch. The skill drives each step automatically, only pausing when user action is genuinely required (pasting tokens, choosing options). If a dependency is missing it will install it rather than asking you to do so manually. **Triggers:** `setup`, `install`, `configure nagi` **Prerequisites checked automatically:** | Requirement | Detail | |---|---| | Node.js >= 22 | Installed via nodenv, nvm, or Homebrew if missing | | pnpm | Installed via corepack or npm if missing | | Docker | Must be running; the skill will attempt to start it | **Steps:** 1. **Install dependencies and build** β€” `pnpm install && pnpm build`. If the build fails (e.g. missing native build tools), the skill reads the error and fixes it. 2. **Choose agent type** β€” Claude Code (Anthropic's CLI, requires `CLAUDE_CODE_OAUTH_TOKEN` or `ANTHROPIC_API_KEY`) or Open Code (open-source agent SDK supporting OpenRouter, Gemini, and OpenAI). 3. **Build container image** β€” `./container/claude-code/build.sh` or `./container/open-code/build.sh` depending on the choice above. 4. **Deploy** β€” Runs the `/deploy` skill internally to generate entry points, `.env`, data directories, and group prompt defaults. 5. **Start and register the main group** β€” Starts the orchestrator with `pnpm dev`, detects the channel ID from logs, and registers the main group in the database. The main group has elevated privileges (no trigger required, can register other groups). 6. **Dashboard UI (optional)** β€” `pnpm ui:dev` starts the web dashboard (SPA on port 5174, API on port 3001) for monitoring agent activity. 7. **Verify** β€” Restarts the orchestrator and confirms end-to-end message flow: channel connects, message received, container spawned, response sent. **Troubleshooting (built into the skill):** * **No channels connected** β€” `.env` tokens are checked and the orchestrator is restarted. * **Container runtime failed** β€” Docker status is verified; the skill starts it if stopped. * **No response to messages** β€” Group registration and trigger patterns are validated. * **Container fails** β€” Logs at `__data/{ASSISTANT_NAME}/groups/main/logs/container-*.log` are inspected. The Docker image tag is confirmed (`nagi-agent:latest` for Claude Code, `nagi-agent-opencode:latest` for Open Code). *** ## `/setup-launchd` β€” launchd Service Setup {#setup-launchd} Register Nagi as a macOS background service using launchd. The service starts automatically on login and restarts on crash (via the `KeepAlive` directive in the plist). This skill is macOS-only; for Linux, use systemd instead. **Triggers:** `setup launchd`, `run as service`, `background service`, `auto start`, `launchd` **Multi-assistant aware:** If multiple assistants exist under `deploy/`, the skill asks which one to target before proceeding. **What it does:** 1. **Pre-flight checks** β€” Verifies the platform is macOS, checks whether the service is already installed (offers reinstall/update), and confirms `deploy/{ASSISTANT_NAME}/host/entry.ts` exists (points to `/deploy` if not). 2. **Detect paths** β€” Resolves `NODE_PATH`, `TSX_PATH` (tsx CLI inside `node_modules/.pnpm`), `PROJECT_ROOT`, `NODE_BIN_DIR`, and `HOME` for the current machine. 3. **Materialize plist** β€” Reads the template at `deploy/templates/launchd/com.nagi.ASSISTANT_NAME.plist`, substitutes `{{NODE_PATH}}`, `{{TSX_PATH}}`, `{{PROJECT_ROOT}}`, `{{NODE_BIN_DIR}}`, and `{{HOME}}` with detected values, then writes the result to `deploy/{ASSISTANT_NAME}/launchd/com.nagi.{ASSISTANT_NAME}.plist`. 4. **Create log directory** β€” `mkdir -p __data/{ASSISTANT_NAME}/logs`. 5. **Install** β€” Unloads any existing agent, copies the plist to `~/Library/LaunchAgents/`, and loads it with `launchctl load`. 6. **Verify** β€” Checks `launchctl list`, confirms a PID is present, and tails the log for `Channel connected` / `Orchestrator started`. **Management commands provided after setup:** ```bash # View logs (live) tail -f __data/{ASSISTANT_NAME}/logs/nagi-{ASSISTANT_NAME}.log # Restart (no unload/load needed) launchctl kickstart -k gui/$(id -u)/com.nagi.{ASSISTANT_NAME} # Stop launchctl unload ~/Library/LaunchAgents/com.nagi.{ASSISTANT_NAME}.plist # Start launchctl load ~/Library/LaunchAgents/com.nagi.{ASSISTANT_NAME}.plist # Check status launchctl list | grep com.nagi.{ASSISTANT_NAME} ``` **Troubleshooting:** | Symptom | Cause / Fix | |---|---| | Service keeps restarting (KeepAlive loop) | Check `__data/{ASSISTANT_NAME}/logs/nagi-{ASSISTANT_NAME}.error.log`. Common causes: port already in use (kill the other instance), missing `.env`, Docker not running. | | "Operation not permitted" | macOS may block launchd agents. Check System Preferences > Privacy & Security for blocked items. | | Need to pick up code changes | Run `launchctl kickstart -k gui/$(id -u)/com.nagi.{ASSISTANT_NAME}` after `pnpm build`. No unload/load cycle required. | | Template was updated upstream | Re-run `/setup-launchd` or `/deploy` with the Launchd target to regenerate the plist from the updated template. | *** ## `/setup-opencode` β€” Open Code Setup {#setup-opencode} Set up [Open Code](https://github.com/opencode-ai/opencode) as an alternative agent runtime to Claude Code. Open Code is an open-source agent SDK that lets you choose from multiple AI providers through a single configuration. **Triggers:** `setup opencode`, `setup open code`, `use opencode`, `switch to opencode` **Supported providers and models:** | Provider | Example Models | API Key Source | |---|---|---| | **OpenRouter** | `anthropic/claude-sonnet-4`, `openai/gpt-4o`, `google/gemini-2.5-pro` | [openrouter.ai/keys](https://openrouter.ai/keys) | | **Google Gemini** | `gemini-2.5-pro`, `gemini-2.5-flash` | [aistudio.google.com/apikey](https://aistudio.google.com/apikey) | | **OpenAI** | `gpt-4o`, `o1` | [platform.openai.com/api-keys](https://platform.openai.com/api-keys) | **Steps:** 1. **Pre-flight** β€” Checks Docker is running and whether the `nagi-agent-opencode` image already exists. 2. **Choose provider** β€” Interactive prompt to select OpenRouter, Google Gemini, or OpenAI. 3. **Enter API key** β€” Guides you to the provider's key page and asks you to paste the key. 4. **Choose model** β€” Suggests recommended defaults per provider (e.g. `openrouter/anthropic/claude-sonnet-4` for OpenRouter). 5. **Configure `.env`** β€” Sets `CONTAINER_IMAGE=nagi-agent-opencode:latest`, `OPENCODE_MODEL={provider}/{model}`, and the provider-specific API key variable (`OPENROUTER_API_KEY`, `GOOGLE_API_KEY`, or `OPENAI_API_KEY`). 6. **Build Docker image** β€” Runs `./container/open-code/build.sh` to produce the `nagi-agent-opencode:latest` image. 7. **Restart Nagi** β€” Kicks the launchd service and tails logs to confirm the new agent is responding. 8. **Test** β€” Prompts you to send a message in Slack/Discord and verify the response comes from the selected model. **Switching back to Claude Code:** Update `.env` to set `CONTAINER_IMAGE=nagi-agent:latest` and remove (or comment out) the `OPENCODE_MODEL` and provider API key lines, then restart Nagi. Alternatively, run the `/change-claude-code` skill. **Troubleshooting:** | Symptom | Cause / Fix | |---|---| | Container fails to start | Verify Docker is running (`docker info`). Rebuild the image with `./container/open-code/build.sh`. Check error logs at `__data/{ASSISTANT_NAME}/logs/nagi-{ASSISTANT_NAME}.error.log`. | | No response from agent | Confirm the API key is valid and not rate-limited. Verify `CONTAINER_IMAGE=nagi-agent-opencode:latest` and `OPENCODE_MODEL` are set in `.env`. | | Wrong model responding | Check `OPENCODE_MODEL` in `.env` matches the intended `{provider}/{model}`. Restart Nagi after any `.env` change. | --- --- url: /en/04_skills_overview.md --- # Skills Reference Nagi ships with **30 built-in skills** that cover every stage of the assistant lifecycle β€” from first-time setup to day-to-day service management, channel wiring, and plugin scaffolding. ## What are skills? Skills are slash commands you invoke inside Claude Code (or Open Code). Type the skill name prefixed with `/` in your terminal session and the corresponding workflow runs automatically. For example: ``` /setup # run initial Nagi setup /nagi-restart # restart the launchd service /add-channel-slack # connect a Slack workspace ``` Skills accept natural-language triggers too β€” typing "restart nagi" or "add slack" in your Claude Code session will match the right skill without the `/` prefix. ## Skill categories | # | Category | Count | What it covers | |---|----------|------:|----------------| | 1 | [Setup](/en/05_skills_setup) | 3 | Initial setup wizard, launchd service registration, Open Code installation | | 2 | [Service Control](/en/06_skills_service) | 4 | Start, stop, restart the launchd service; view logs | | 3 | [Deploy & Sync](/en/07_skills_deploy) | 4 | Template synchronization, container rebuilds, group prompt refresh | | 4 | [Agent Switching](/en/08_skills_agent) | 2 | Toggle between Claude Code and Open Code | | 5 | [Channel Plugins](/en/09_skills_channel) | 6 | Slack, Discord, and Asana connections plus rich-display modes | | 6 | [MCP Plugins](/en/10_skills_mcp) | 2 | Ollama (local LLM) and Vercel (deployment) integrations | | 7 | [Agent Hooks](/en/11_skills_hooks) | 2 | Tool-execution and session-start notifications to chat channels | | 8 | [Group Prompts](/en/12_skills_group_prompt) | 3 | Create and edit prompt files (IDENTITY.md, INSTRUCTIONS.md, etc.); configure the per-group skill allowlist | | 9 | [Plugin Scaffolding](/en/13_skills_scaffold) | 3 | Generators for new channel, MCP, and hooks plugins | | 10 | [Misc](/en/14_skills_misc) | 1 | Context probe for verifying auto-mount | ## Quick-reference: all 30 skills | Skill | Category | Description | |-------|----------|-------------| | `/setup` | Setup | Full interactive setup wizard | | `/setup-launchd` | Setup | Register Nagi as a macOS launchd service | | `/setup-opencode` | Setup | Install and configure Open Code agent | | `/nagi-start` | Service | Start the launchd service | | `/nagi-stop` | Service | Stop the launchd service | | `/nagi-restart` | Service | Restart the launchd service | | `/nagi-logs` | Service | Stream service logs | | `/deploy` | Deploy | Sync deploy templates to active config | | `/update-entry` | Deploy | Sync a single entry.ts with its template | | `/update-groups` | Deploy | Refresh group defaults (CLAUDE.md, etc.) | | `/update-container` | Deploy | Rebuild the nagi-agent Docker image | | `/change-claude-code` | Agent | Switch back to Claude Code | | `/change-open-code` | Agent | Switch to Open Code | | `/add-channel-slack` | Channel | Connect a Slack workspace (Socket Mode) | | `/add-channel-slack-block-kit` | Channel | Enable Block Kit rich display for Slack | | `/add-channel-slack-block-kit-embed` | Channel | Enable Block Kit Embed display for Slack | | `/add-channel-discord` | Channel | Connect a Discord bot (Gateway) | | `/add-channel-discord-embed` | Channel | Enable Embed rich display for Discord | | `/add-channel-asana` | Channel | Connect Asana task comment polling | | `/add-mcp-ollama` | MCP | Add Ollama for local LLM access | | `/add-mcp-vercel` | MCP | Add Vercel for container deployments | | `/add-agent-hooks-claude-code` | Hooks | Notification hooks for Claude Code containers | | `/add-agent-hooks-open-code` | Hooks | Notification hooks for Open Code containers | | `/create-group-prompt` | Prompt | Create new group prompt files | | `/update-group-prompt` | Prompt | Edit existing group prompt files | | `/configure-skills` | Prompt | Edit per-group skill allowlist (`skills.config.json`) | | `/create-plugin-channel` | Scaffold | Generate a channel plugin package | | `/create-container-plugin-mcp` | Scaffold | Generate an MCP plugin package | | `/create-container-plugin-agent-hooks` | Scaffold | Generate an agent-hooks plugin package | | `/add-context-probe` | Misc | Install or remove a context probe | ## Tips * **Run `/setup` first.** It walks through every prerequisite and calls the other skills as needed. * **Skills are idempotent.** Running one twice will not create duplicates β€” it detects existing config and offers to update it. * **Trigger words work anywhere.** You do not need the exact `/slash-command` name; natural phrases like "connect slack" or "restart" are matched automatically. * **Skill files live in `.claude/skills/`.** They must be placed flat in that directory β€” subdirectory nesting is not supported. --- --- url: /en/02_architecture.md --- # System Overview Nagi operates in a two-layer architecture: **host-side** processes handle message routing, persistence, and credential management, while **container-side** processes run AI agents in isolated Docker containers. ## Architecture ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Host (macOS / Linux) β”‚ β”‚ β”‚ β”‚ deploy/{ASSISTANT_NAME}/host/entry.ts β”‚ β”‚ β”‚ β”‚ β”‚ β–Ό β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Orchestrator │───▢│ Channel Plugins (host/plugins/) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ Slack (Socket Mode) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ Slack Block Kit β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ Slack Block Kit Embed β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ Discord (Gateway) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ Discord Embed β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ └── Asana (Polling) β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ │───▢ Credential Proxy (:3002) β”‚ β”‚ β”‚ │───▢ SQLite DB β”‚ β”‚ β”‚ │───▢ GroupQueue β”‚ β”‚ β”‚ │───▢ Scheduler β”‚ β”‚ β”‚ │───▢ Router β”‚ β”‚ β”‚ │───▢ Auth (allowlist) β”‚ β”‚ β”‚ │◀──▢ IpcWatcher (IPC file monitoring) β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ docker run β”‚ β”‚ β–Ό β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Docker Container (claude-code or open-code) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Agent Runner (Claude Code / Open Code) β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ Nagi MCP Server (stdio) β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ send_message (instant messaging) β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ schedule_task (task scheduling) β”‚ β”‚ β”‚ β”‚ β”‚ └── list_tasks (task listing) β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ Agent Hooks β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ PostToolUse (tool notifications) β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ SessionStart (session notifications) β”‚ β”‚ β”‚ β”‚ β”‚ └── PromptComplete (completion callback) β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ MCP Plugins β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ Ollama (local LLM) β”‚ β”‚ β”‚ β”‚ β”‚ └── Vercel (deployment) β”‚ β”‚ β”‚ β”‚ └── Container Skills (/workspace/group/skills) β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ agent-browser (browser automation) β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ ai-changelog (changelog generation) β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ capabilities (feature intro) β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ jupyter-deploy (notebook execution) β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ slack-formatting (Slack formatting) β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ status (status reporting) β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ ui-ux-pro-max (UI/UX design) β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ vercel-deploy (Vercel deployment) β”‚ β”‚ β”‚ β”‚ └── youtube-analytics (YouTube analytics) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## Message Flow ``` User (Slack/Discord/Asana) β”‚ β–Ό Channel Plugin ── onMessage ──▢ Orchestrator β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β–Ό β–Ό β–Ό Store in Enqueue Route to SQLite DB GroupQueue Group β”‚ β–Ό docker run nagi-agent β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β–Ό β–Ό β–Ό Agent uses Calls APIs Produces MCP tools via Cred. stdout Proxy markers β”‚ β–Ό ---NAGI_OUTPUT_START--- β”‚ Orchestrator parses output β”‚ β–Ό Channel Plugin ── reply ──▢ User ``` Step-by-step: 1. A user sends a message on Slack, Discord, or Asana. 2. The corresponding channel plugin receives the event and forwards it to the Orchestrator. 3. The Orchestrator stores the message in SQLite and routes it to the correct group. 4. GroupQueue enqueues the message; when a slot is available, it launches a Docker container. 5. The agent runner starts inside the container (Claude Code or Open Code). 6. The agent calls external APIs through the Credential Proxy, which injects real credentials in place of placeholder tokens. 7. When the agent produces a response, it writes `---NAGI_OUTPUT_START---` / `---NAGI_OUTPUT_END---` markers to stdout. 8. The Orchestrator reads the markers, extracts the response, and sends it back through the channel plugin. ## Plugin System Nagi has four types of extensions, split across host and container: ### Channel Plugins (Host-side) Channel plugins run on the host and bridge messaging platforms to the Orchestrator. Each implements the `Channel` interface from `@nagi/channel-core`. | Plugin | Transport | Location | |--------|-----------|----------| | `channel-slack` | Socket Mode | `host/plugins/channel-slack/` | | `channel-slack-block-kit` | Socket Mode + Block Kit rich display | `host/plugins/channel-slack-block-kit/` | | `channel-slack-block-kit-embed` | Socket Mode + embed attachments | `host/plugins/channel-slack-block-kit-embed/` | | `channel-discord` | Gateway Intents | `host/plugins/channel-discord/` | | `channel-discord-embed` | Gateway + embed display | `host/plugins/channel-discord-embed/` | | `channel-asana` | Comment polling | `host/plugins/channel-asana/` | Registration in `entry.ts`: ``` registry.register("slack", createSlackFactory({ ... })) β†’ Orchestrator connects all registered channels on start ``` ### MCP Plugins (Container-side) MCP plugins run inside Docker containers as stdio MCP servers. They provide additional tools to the agent at runtime. | Plugin | Purpose | Location | |--------|---------|----------| | `mcp-ollama` | Local LLM access via Ollama | `container/plugins/mcp-ollama/` | | `mcp-vercel` | Vercel deployment | `container/plugins/mcp-vercel/` | Registration in `entry.ts`: ``` orchestrator.registerMcpPlugin("ollama", { entryPoint: "..." }) β†’ ContainerInput.mcpPlugins passed to agent-runner via stdin β†’ agent-runner dynamically registers them as mcpServers ``` ### Agent Hooks (Container-side) Agent hooks run inside containers and fire notifications on tool execution and session lifecycle events. They are available for both Claude Code and Open Code runners. | Variant | Location | |---------|----------| | Claude Code hooks | `container/claude-code/plugins/agent-hooks/` | | Open Code hooks | `container/open-code/plugins/agent-hooks/` | ### Container Skills Skills are specialized prompt-based capabilities bundled into the container image. They give the agent domain-specific knowledge for particular tasks. Examples: `vercel-deploy`, `jupyter-deploy`, `slack-formatting`, `ai-changelog`, `youtube-analytics`, `ui-ux-pro-max`, and more. Location: `container/skills/` ## Package Dependencies ``` deploy/{ASSISTANT_NAME}/host/entry.ts β”‚ β”œβ”€β”€ @nagi/orchestrator β”‚ β”œβ”€β”€ @nagi/channel-core β”‚ β”œβ”€β”€ @nagi/db β”‚ β”œβ”€β”€ @nagi/queue β”‚ β”œβ”€β”€ @nagi/scheduler β”‚ β”œβ”€β”€ @nagi/ipc β”‚ β”œβ”€β”€ @nagi/router β”‚ β”œβ”€β”€ @nagi/auth β”‚ β”œβ”€β”€ @nagi/config β”‚ β”œβ”€β”€ @nagi/credential-proxy β”‚ └── @nagi/logger β”‚ β”œβ”€β”€ @nagi/channel-slack ──▢ @nagi/channel-core ──▢ @nagi/types β”œβ”€β”€ @nagi/channel-discord ──▢ @nagi/channel-core ──▢ @nagi/types └── @nagi/channel-asana ──▢ @nagi/channel-core ──▢ @nagi/types ``` The `@nagi/types` package provides shared type definitions consumed by most libraries. ## Multi-Assistant Support Nagi supports running multiple assistant instances in parallel. Each assistant gets its own: * Entry point: `deploy/{ASSISTANT_NAME}/host/entry.ts` * Group prompts: `deploy/{ASSISTANT_NAME}/groups/{channel}/{group}/` * Data directory: `__data/{ASSISTANT_NAME}/` * launchd service (macOS): one plist per assistant Templates in `deploy/templates/` are the canonical source; running the deploy skill materializes them into per-assistant directories. ## Data Directories | Directory | Purpose | Git | |---|---|---| | `deploy/templates/` | Entry point and group prompt templates (pristine) | Tracked | | `deploy/templates/groups/` | Group prompt templates (CLAUDE.md, AGENTS.md) | Tracked | | `deploy/{ASSISTANT_NAME}/` | Materialized entry points and group prompts | Ignored | | `deploy/{ASSISTANT_NAME}/groups/` | User-editable group prompt defaults | Ignored | | `__data/{ASSISTANT_NAME}/store/nagi.db` | SQLite database (groups, chats, messages, scheduled tasks, sessions, state) | Ignored | | `__data/{ASSISTANT_NAME}/groups/` | Runtime group data (mounted into containers) | Ignored | | `__data/{ASSISTANT_NAME}/sessions/` | Claude sessions per group | Ignored | | `__data/{ASSISTANT_NAME}/ipc/` | Container IPC files | Ignored | | `__data/{ASSISTANT_NAME}/logs/` | Service logs | Ignored |