with one click
add-emacs
// Add Emacs as a channel. Opens an interactive chat buffer and org-mode integration so you can talk to NanoClaw from within Emacs (Doom, Spacemacs, or vanilla). Local HTTP bridge — no bot token or external service needed.
// Add Emacs as a channel. Opens an interactive chat buffer and org-mode integration so you can talk to NanoClaw from within Emacs (Doom, Spacemacs, or vanilla). Local HTTP bridge — no bot token or external service needed.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | add-emacs |
| description | Add Emacs as a channel. Opens an interactive chat buffer and org-mode integration so you can talk to NanoClaw from within Emacs (Doom, Spacemacs, or vanilla). Local HTTP bridge — no bot token or external service needed. |
Adds Emacs support via a local HTTP bridge. Works with Doom Emacs, Spacemacs, and vanilla Emacs 27.1+.
C-c n c / SPC N c), ask about a function or error without leaving Emacsnanoclaw-org-send; the response appears as a child heading inline in your org fileNanoClaw doesn't ship channels in trunk. This skill copies the Emacs adapter and the Lisp client in from the channels branch. Native HTTP bridge — no Chat SDK, no adapter package.
Skip to Enable if all of these are already in place:
src/channels/emacs.ts existsemacs/nanoclaw.el existssrc/channels/index.ts contains import './emacs.js';Otherwise continue. Every step below is safe to re-run.
git fetch origin channels
mkdir -p emacs
git show origin/channels:src/channels/emacs.ts > src/channels/emacs.ts
git show origin/channels:src/channels/emacs.test.ts > src/channels/emacs.test.ts
git show origin/channels:emacs/nanoclaw.el > emacs/nanoclaw.el
Append to src/channels/index.ts (skip if the line is already present):
import './emacs.js';
pnpm run build
No npm package to install — the adapter uses only Node builtins (http).
The adapter is gated by EMACS_ENABLED so the HTTP port isn't opened on hosts that aren't running Emacs. Add to .env:
EMACS_ENABLED=true
EMACS_CHANNEL_PORT=8766 # optional — change only if 8766 is taken
EMACS_AUTH_TOKEN= # optional — set to a random string to lock the endpoint
EMACS_PLATFORM_ID=default # optional — only change if you want a non-default chat id
Generate an auth token (recommended even on single-user machines — prevents other local processes from poking the endpoint):
node -e "console.log(require('crypto').randomBytes(16).toString('hex'))"
Emacs is a single-user, single-chat channel. One host = one messaging group with platform_id = "default".
Run /init-first-agent — pick Emacs as the channel, use any short handle as the "user id" (e.g. your OS username), and the skill will create the agent group, wire the channel, and write a welcome message that the agent delivers back to your Emacs buffer.
Run the register step directly. The EMACS_PLATFORM_ID (default default) becomes the messaging group's platform id:
pnpm exec tsx setup/index.ts --step register -- \
--platform-id "default" --name "Emacs" \
--folder "<existing-folder>" --channel "emacs" \
--session-mode "agent-shared" \
--assistant-name "<existing-assistant-name>"
agent-shared puts Emacs messages in the same session as any other channel wired to the same agent group — so a conversation you started in Telegram continues in Emacs. Use shared to keep an independent Emacs thread with the same workspace, or a new --folder for a dedicated Emacs-only agent.
nanoclaw.el needs only Emacs 27.1+ builtins (url, json, org) — no package manager.
AskUserQuestion: Which Emacs distribution are you using?
config.el with map! keybindingsdotspacemacs/user-config in ~/.spacemacsinit.el with global-set-keyDoom Emacs — add to ~/.config/doom/config.el (or ~/.doom.d/config.el):
;; NanoClaw — personal AI assistant channel
(load (expand-file-name "~/src/nanoclaw/emacs/nanoclaw.el"))
(map! :leader
:prefix ("N" . "NanoClaw")
:desc "Chat buffer" "c" #'nanoclaw-chat
:desc "Send org" "o" #'nanoclaw-org-send)
Reload: M-x doom/reload
Spacemacs — add to dotspacemacs/user-config in ~/.spacemacs:
;; NanoClaw — personal AI assistant channel
(load-file "~/src/nanoclaw/emacs/nanoclaw.el")
(spacemacs/set-leader-keys "aNc" #'nanoclaw-chat)
(spacemacs/set-leader-keys "aNo" #'nanoclaw-org-send)
Reload: M-x dotspacemacs/sync-configuration-layers or restart Emacs.
Vanilla Emacs — add to ~/.emacs.d/init.el:
;; NanoClaw — personal AI assistant channel
(load-file "~/src/nanoclaw/emacs/nanoclaw.el")
(global-set-key (kbd "C-c n c") #'nanoclaw-chat)
(global-set-key (kbd "C-c n o") #'nanoclaw-org-send)
Reload: M-x eval-buffer or restart Emacs.
Replace ~/src/nanoclaw/emacs/nanoclaw.el with your actual NanoClaw checkout path.
If EMACS_AUTH_TOKEN is set, also add (any distribution):
(setq nanoclaw-auth-token "<your-token>")
If you changed EMACS_CHANNEL_PORT from the default:
(setq nanoclaw-port <your-port>)
pnpm run build
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
# systemctl --user restart nanoclaw # Linux
curl -s http://localhost:8766/api/messages?since=0
Expected: {"messages":[]}. With an auth token:
curl -s -H "Authorization: Bearer <token>" http://localhost:8766/api/messages?since=0
Tell the user:
- Open the chat buffer with your keybinding (
SPC N c,SPC a N c, orC-c n c)- Type a message and press
C-c C-cto send (RET inserts newlines)- A response should appear within a few seconds
For org-mode: open any
.orgfile, position the cursor on a heading, and useSPC N o/SPC a N o/C-c n o
tail -f logs/nanoclaw.log should show Emacs channel listening at startup.
emacsplatform_id string (default default).EMACS_PLATFORM_ID (default default). User handles are arbitrary; your OS username or first name is fine (e.g. emacs:<username>).session-mode = agent-shared so a conversation started elsewhere continues in Emacs. Pick a separate folder only if you specifically want an Emacs-only persona.nanoclaw-chat) with markdown → org-mode renderingnanoclaw-org-send) — sends the current subtree or region; reply lands as a child headingNot applicable (design): multi-user channels, threads, cold DM initiation, typing indicators, attachments.
Error: listen EADDRINUSE: address already in use :::8766
Either a stale NanoClaw is running or another app has the port. Kill stale process or change port:
lsof -ti :8766 | xargs kill -9
# or set EMACS_CHANNEL_PORT in .env and mirror in Emacs config (nanoclaw-port)
If grep "Emacs channel listening" logs/nanoclaw.log returns nothing, check that EMACS_ENABLED=true is in .env and that the adapter import is present:
grep -q '^EMACS_ENABLED=true' .env && echo "enabled" || echo "not enabled"
grep -q "import './emacs.js'" src/channels/index.ts && echo "imported" || echo "not imported"
launchctl list | grep nanoclaw (macOS) / systemctl --user status nanoclaw (Linux)pnpm exec tsx scripts/q.ts data/v2.db "SELECT mg.platform_id, ag.folder FROM messaging_groups mg JOIN messaging_group_agents mga ON mg.id = mga.messaging_group_id JOIN agent_groups ag ON ag.id = mga.agent_group_id WHERE mg.channel_type = 'emacs'"grep 'channel_type=emacs\|Emacs' logs/nanoclaw.log | tail -20If no messaging group row exists, run the register command above.
M-x describe-variable RET nanoclaw-auth-token RET
Must match EMACS_AUTH_TOKEN in .env. If you didn't set one server-side, clear it in Emacs too:
(setq nanoclaw-auth-token nil)
ls ~/src/nanoclaw/emacs/nanoclaw.el
If NanoClaw is cloned elsewhere, update the load/load-file path in your Emacs config.
The Emacs bridge converts markdown → org-mode automatically. Agents should output standard markdown, not org-mode syntax:
| Markdown | Org-mode |
|---|---|
**bold** | *bold* |
*italic* | /italic/ |
~~text~~ | +text+ |
`code` | ~code~ |
```lang | #+begin_src lang |
If an agent outputs org-mode directly, markers get double-converted and render incorrectly.
rm src/channels/emacs.ts src/channels/emacs.test.ts emacs/nanoclaw.el
# Remove the `import './emacs.js';` line from src/channels/index.ts
# Remove EMACS_* lines from .env
pnpm run build
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
# systemctl --user restart nanoclaw # Linux
# Remove the NanoClaw block from your Emacs config
# Optionally clean up the messaging group:
pnpm exec tsx scripts/q.ts data/v2.db "DELETE FROM messaging_group_agents WHERE messaging_group_id IN (SELECT id FROM messaging_groups WHERE channel_type='emacs'); DELETE FROM messaging_groups WHERE channel_type='emacs';"