/migrate-slack-agents
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
Claude Code
$ npx -y skills add nanocoai/nanoclaw --skill migrate-slack-agents --agent claude-code
Codex
$ npx skills add nanocoai/nanoclaw --skill migrate-slack-agents -a codex -y
Cursor
$ npx skills add nanocoai/nanoclaw --skill migrate-slack-agents -a cursor -y
Gemini CLI
$ npx skills add nanocoai/nanoclaw --skill migrate-slack-agents -a gemini-cli -y
GitHub Copilot
$ npx skills add nanocoai/nanoclaw --skill migrate-slack-agents -a github-copilot -y
OpenCode
$ npx skills add nanocoai/nanoclaw --skill migrate-slack-agents -a opencode -y
Windsurf
$ npx skills add nanocoai/nanoclaw --skill migrate-slack-agents -a windsurf -y
SKILL.md
migrate-slack-agents.SKILL.mdname: migrate-slack-agents
description: 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.
Migrate classic Slack agents
Turn every agent group wired through the classic `instance='slack'` adapter into a named `slack-<slug>` 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:
- Never call `create_agent` or `ncl groups create`.
- Never change an `agent_groups.id` or write under `groups/<folder>/`.
- 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
rollback until the operator explicitly approves cutover.
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:
1. `src/channels/index.ts` contains the Slack barrel import `import './slack.js';`. 2. `.env` has a non-empty unsuffixed `SLACK_BOT_TOKEN` and either a non-empty `SLACK_APP_TOKEN` or `SLACK_SIGNING_SECRET`. Check presence without echoing values. 3. 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"
4. At least one wired group does not yet have complete named-instance coverage: a stable slug, both `SLACK_BOT_TOKEN_<SUFFIX>` and `SLACK_APP_TOKEN_<SUFFIX>`, that slug in `SLACK_INSTANCES`, and the expected `slack-<slug>` messaging-group/wiring rows.
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 destinatio
Read more
name: migrate-slack-agents description: 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.
Migrate classic Slack agents
Turn every agent group wired through the classic `instance='slack'` adapter into a named `slack-<slug>` 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:
- Never call `create_agent` or `ncl groups create`.
- Never change an `agent_groups.id` or write under `groups/<folder>/`.
- 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
rollback until the operator explicitly approves cutover.
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:
1. `src/channels/index.ts` contains the Slack barrel import `import './slack.js';`. 2. `.env` has a non-empty unsuffixed `SLACK_BOT_TOKEN` and either a non-empty `SLACK_APP_TOKEN` or `SLACK_SIGNING_SECRET`. Check presence without echoing values. 3. 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"
4. At least one wired group does not yet have complete named-instance coverage: a stable slug, both `SLACK_BOT_TOKEN_<SUFFIX>` and `SLACK_APP_TOKEN_<SUFFIX>`, that slug in `SLACK_INSTANCES`, and the expected `slack-<slug>` messaging-group/wiring rows.
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 destinatio
A lightweight alternative to OpenClaw that runs in containers for security. Connects to WhatsApp, Telegram, Slack, Discord, Gmail and other messaging apps,, has memory, scheduled jobs, and runs directly on Anthropic's Agents SDK
Other skills on nanoclaw.
More skills from other plugins.
browser-use
Direct browser control via CDP for web interaction: automation, scraping, testing, screenshots, and site/app work. Also covers…
agent-browser
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling…
opencli-adapter-author
Build or improve an OpenCLI JS adapter using observed site behavior, existing adapters and browser API evidence. For ad-hoc…
Questions about migrate-slack-agents.
- Which coding agents can use migrate-slack-agents?
- migrate-slack-agents installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

