Mmcp.market

migrate-slack-agents skill

by nanocoai·nanocoai/nanoclaw·31k stars·MIT

Migrate a classic single-bot Slack install to one provisioned Slack app per existing agent group while preserving agent identities, workspaces, memory, and wiring behavior — or record the operator's choice to stay on classic, which remains supported. Use when /update-nanoclaw surfaces the Slack agents requirement, or standalone any time later.

A100/100content scan

Is the migrate-slack-agents skill safe?

Clean: nothing in its files matched our rules. We read 1 file in the folder on 2026-09-28.

No findings.

Install the migrate-slack-agents skill

A skill is a folder. Copy it into your agent's skills folder and the agent loads it when the task matches its description.

git clone --depth 1 https://github.com/nanocoai/nanoclaw.git /tmp/nanoclaw
mkdir -p ~/.claude/skills
cp -r /tmp/nanoclaw/.claude/skills/migrate-slack-agents ~/.claude/skills/migrate-slack-agents
available in every project

In the Claude apps, zip the folder and upload it from the Skills settings. The folder on GitHub

The instructions your agent would load

SKILL.md as published, without the frontmatter. Read it on GitHub

Migrate classic Slack agents

Turn every agent group wired through the classic instance='slack' adapter into a named slack- bot without replacing the agent group. This is an operator-guided, resumable data migration. It never creates an agent group and never edits an agent workspace.

Migration is optional. Classic single-bot Slack remains fully supported; this skill first offers the choice, and staying on classic is a valid outcome that also satisfies the update requirement.

Hard invariants:

rollback until the operator explicitly approves cutover.

  • Never call create_agent or ncl groups create.
  • Never change an agent_groups.id or write under groups//.
  • Never merge the channels branch; fetch and copy skill-owned files only.
  • Never print token values. Show key names and masked presence only.
  • Keep classic rows, credentials, and the shared Slack app available for

Phase 1: Detect classic state

Run from the NanoClaw project root. Read the central DB only through the sanctioned wrapper.

Classic state requires all four signals:

import './slack.js';.

  1. src/channels/index.ts contains the Slack barrel import

SLACKAPPTOKEN or SLACKSIGNINGSECRET. Check presence without echoing values.

  1. .env has a non-empty unsuffixed SLACKBOTTOKEN and either a non-empty
  1. This query returns at least one row:
pnpm exec tsx scripts/q.ts data/v2.db "SELECT mg.id, mga.id, mga.agent_group_id FROM messaging_groups mg JOIN messaging_group_agents mga ON mga.messaging_group_id=mg.id WHERE mg.channel_type='slack' AND mg.instance='slack' ORDER BY mg.id, mga.agent_group_id"

a stable slug, both SLACKBOTTOKEN and SLACKAPPTOKEN, that slug in SLACK_INSTANCES, and the expected slack- messaging-group/wiring rows.

  1. At least one wired group does not yet have complete named-instance coverage:

If the classic conjunction is absent and there is no partial state, stop with:

Nothing to migrate: this install does not have the classic shared-bot Slack state.

This is a successful no-op. If only some signals exist, make no changes; report the inconsistent or partial state instead of guessing. If every wired group already has complete named coverage, report that the migration is already complete and proceed only to the update-requirement acknowledgement in Phase 9.

Phase 1b: Offer the choice

Classic state confirmed does not mean migration is required. Present the decision to the operator before touching anything, in words like these:

Your classic Slack setup keeps working as-is — nothing forces this

migration. The new Slack experience adds Slack agent spawning (create new

agents straight from Slack, each with its own provisioned bot and avatar)

plus UX improvements — per-agent identities, DM onboarding, multi-agent

rooms. Say the word and we'll run the upgrade now — or run

/migrate-slack-agents later manually.

If the operator chooses to stay on classic: make no changes, acknowledge the update requirement now using the Phase 9 ack command (the requirement records a decision, not only a completed migration), state that classic Slack continues working unchanged, and stop. Re-running this skill later re-offers the migration.

If the operator chooses to migrate, continue to Phase 2.

Phase 2: Inventory and propose the map

Before any mutation, capture every classic surface and its complete behavior:

pnpm exec tsx scripts/q.ts data/v2.db "SELECT ag.id, ag.name, ag.folder, mg.id, mg.platform_id, mg.name, mg.is_group, mg.unknown_sender_policy, mga.id, mga.engage_mode, mga.engage_pattern, mga.sender_scope, mga.ignored_message_policy, mga.session_mode, mga.threads, mga.priority FROM messaging_groups mg JOIN messaging_group_agents mga ON mga.messaging_group_id=mg.id JOIN agent_groups ag ON ag.id=mga.agent_group_id WHERE mg.channel_type='slack' AND mg.instance='slack' ORDER BY ag.id, mg.id, mga.priority DESC"
pnpm exec tsx scripts/q.ts data/v2.db "SELECT ad.agent_group_id, ad.local_name, ad.target_type, ad.target_id FROM agent_destinations ad JOIN messaging_groups mg ON ad.target_type='channel' AND ad.target_id=mg.id WHERE mg.channel_type='slack' AND mg.instance='slack' ORDER BY ad.agent_group_id, ad.local_name"

Classify each surface as DM, channel, or MPIM. A D… conversation is a DM; use Slack conversations.info with the classic bot token to distinguish an MPIM from a channel when the stored id is ambiguous, returning only type/id metadata and never the token.

Choose one stable, unique slug per agent group using the flow's normalization: lowercase, replace non-alphanumerics with -, trim -, and add a numeric suffix if an env key, SLACK_INSTANCES entry, or named DB instance is already claimed by another group. Once any migration state exists, never change that group's slug. Include the group commonly called “master”; it also gets its own provisioned app.

Slugs are de-duplicated, but the Slack-visible bot display name comes from the agent group's name — and Slack allows two apps with identical display names in one workspace, leaving humans a mention picker with twins told apart only by avatar. If any two migrating groups share a display name, flag it in the dry-run and have the operator differentiate the names before provisioning; renaming at this point is free, while renaming after provisioning requires a manifest update.

Present a dry-run table with:

  • agent group id, name, folder, and chosen slug;
  • every old messaging-group id, type, platform id, and destination name;
  • the full old wiring row and unknown-sender policy;
  • the proposed slack- surface and whether it is new, partial, or done.

For a channel, map the old row to a sibling with the same platform_id and instance='slack-'. DMs and MPIMs need new conversation ids as described in Phase 6. Ask the operator to confirm the entire map before continuing.

Phase 3: Install the current Slack agents payloads

First run /update-skills for the installed Slack channel only and require its structured result to report success: true and refreshed. That refreshes the barrel-registered /add-slack payload; it deliberately does not install companion skills, credentials, wirings, or restarts.

Resolve the remote that points at nanocoai/nanoclaw the same fork-aware way /update-skills does; do not assume it is origin. Fetch, but never merge:

source setup/lib/channels-remote.sh
channels_remote="$(resolve_channels_remote)"
git fetch "$channels_remote" channels

From $channels_remote/channels, materialize every file under .claude/skills/slack-a2a-rooms/ and then every file under .claude/skills/slack-agent-flow/ with git ls-tree + git show. Read each new SKILL.md completely and apply its own Apply steps, in that order. The order is load-bearing and mirrors setup/channels/companions.ts. The standard driver may apply each document:

pnpm exec tsx setup/lib/skill-driver.ts .claude/skills/slack-a2a-rooms
pnpm exec tsx setup/lib/skill-driver.ts .claude/skills/slack-agent-flow

Do not continue unless both report fully applied and their own build/tests pass. Source contracts: .claude/skills/add-slack/SKILL.md, setup/channels/companions.ts, and the two fetched companion SKILL.md files.

Phase 4: Get provisioning authority

Pause and ask the operator to choose and complete one authority path:

the operator connect the intended Slack workspace through the broker OAuth flow. The flow accepts NANOCLAWINSTALLTOKEN or the enrolled account.

  1. Managed broker — enroll/validate the registry install token, then have

.env for apps.manifest.create + apps.managedInstall.

  1. Direct Slack — the operator supplies a valid SLACKMANAGERTOKEN in

Never select a path, workspace, or authority on the operator's behalf. Confirm the intended workspace matches the classic bot's auth.test team before any app is created. See src/provisioning/slack-app.ts and the fetched flow's provision.ts for the two transport contracts.

Phase 5: Provision every existing group

Choose an existing classic Slack-wired group with a Slack approver as the stable source group. It supplies the operator identity and authenticates the origin slack instance; it is not cloned. For every inventoried group, including the source/master group, run the installed finish primitive with the recorded slug and defer room creation:

pnpm exec tsx scripts/slack-agent-flow-finish.ts \
  --group <existing-agent-group-id> \
  --name <stable-slug> \
  --source-group <source-agent-group-id> \
  --origin-instance slack \
  --room none

Do not pass --restart yet. Run all groups first. The script reuses a complete suffixed token pair, creates the operator DM and its wiring idempotently, and does not create an agent group. On a partial pair, finish the existing Slack app installation and retry the same slug; never choose a new slug or create a second app. Ensure every completed slug is present exactly once in SLACK_INSTANCES, including reuse cases. The exact CLI contract is documented at the top of scripts/slack-agent-flow-finish.ts.

Phase 6: Recreate surfaces and clone behavior

For each old wiring:

new bot to the existing channel. Use the same channel platform_id.

More skills from nanocoai/nanoclaw

  • Aadd-anydocAdd local office-document-to-Markdown conversion to NanoClaw agent containers with the pinned Firecrawl AnyDoc CLI. Use when agents need to read attached Word, PowerPoint, Excel, OpenDocument, RTF, EPUB, CSV, or text-based PDF files without uploading them to a hosted parser.
  • Aadd-atomic-chat-toolAdd Atomic Chat MCP server so the container agent can call local models served by the Atomic Chat desktop app via its OpenAI-compatible API.
  • Fadd-clidashAdd clidash — a zero-dependency, read-only web dashboard that derives its tabs and tables at runtime from any CLI that lists resources as JSON. Ships pre-wired for NanoClaw's ncl CLI (agent groups, sessions, channels, users, roles), plus message-activity charts, a log tail, and a read-only file viewer for group skills/CLAUDE.md/profiles.
  • Aadd-codexUse Codex (OpenAI's codex app-server) as a full agent provider — planning, tool orchestration, MCP tools, server-side history, session resume — alongside or instead of Claude. ChatGPT subscription or OpenAI API key, vault-only via the selected gateway. Per-group via `ncl groups config update --provider codex`. Distinct from using OpenAI as an MCP tool (where Claude remains the planner).
  • Aadd-dashboardAdd a monitoring dashboard to NanoClaw. Installs @nanoco/nanoclaw-dashboard and a pusher that sends periodic JSON snapshots.
  • Aadd-deltachatAdd DeltaChat channel integration via @deltachat/stdio-rpc-server. Native adapter — no Chat SDK bridge. Email-based messaging with end-to-end encryption.
  • Cadd-dialAdd Dial channel integration — a real phone number for SMS and AI voice calls via the Dial platform (getdial.ai). Native adapter — no Chat SDK bridge.
  • Aadd-dial-numberAdd another phone number to an existing Dial channel — a second (or third) public line for the agent, so one NanoClaw install answers SMS and AI voice calls on multiple numbers. Use when Dial is already installed and the operator wants an additional number (e.g. a personal line plus a support line). Requires the Dial channel to already be installed (see /add-dial).
  • Aadd-dial-toolGive chosen NanoClaw agents a real phone number as a container tool — the `dial` CLI baked into the agent image plus OneCLI credential injection for api.getdial.ai, scoped per agent, so the agents you pick can send SMS, place AI voice calls, and receive verification codes from inside the sandbox. Independent of the Dial channel; idempotent; re-run to change which agents may use it. Use when the user wants agents to text, call, or run `dial …` from a chat, without wiring Dial as a messaging channel.
  • Aadd-discordAdd Discord bot channel integration via Chat SDK.
  • Aadd-emacsAdd 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.
  • Aadd-gchatAdd Google Chat channel integration via Chat SDK.

All agent skills → · MCP servers