Skip to main content
ClaudeWave
Skill29.8k estrellas del repoactualizado yesterday

migrate-from-v1

**migrate-from-v1** completes a NanoClaw v1 to v2 migration after the automated `migrate-v2.sh` script finishes. Use this skill to triage any failed migration steps, seed the owner user, clean up local configuration files, reconcile container settings, port custom v1 code changes, verify v2 receives real messages on live channels, and roll back to v1 if needed. Run only after `migrate-v2.sh` has completed and generated its handoff report.

Instalar en Claude Code
Copiar
git clone --depth 1 https://github.com/nanocoai/nanoclaw /tmp/migrate-from-v1 && cp -r /tmp/migrate-from-v1/.claude/skills/migrate-from-v1 ~/.claude/skills/migrate-from-v1
Después abre una sesión nueva de Claude Code; el skill carga automáticamente.

SKILL.md

# Finish v1 → v2 migration

`bash migrate-v2.sh` already ran the deterministic migration. It handled:

- .env keys merged
- v2 DB seeded (agent_groups, messaging_groups, wiring)
- Group folders copied (v1 CLAUDE.md → v2 CLAUDE.local.md)
- Session data copied with conversation continuity (incl. Claude Code memory + JSONL transcripts)
- Scheduled tasks ported
- Channel code installed and auth state copied (incl. WhatsApp Baileys keystore)
- WhatsApp LIDs resolved from `store/auth` and aliased into `messaging_groups`
- Container skills copied
- Container image built

Your job is the parts that need human judgment: triage any failed steps, seed the owner, clean up CLAUDE.local.md files, reconcile configs, and port any fork customizations.

Read `logs/setup-migration/handoff.json` first — it has `overall_status`, per-step results in `steps`, and a `followups` list.

## Preflight: was the script run?

Before anything else, check that `logs/setup-migration/handoff.json` exists. If it doesn't, the user is invoking this skill before `migrate-v2.sh` ran. Stop and tell them, verbatim:

> This skill finishes a migration that `migrate-v2.sh` started. Run that first, in your terminal — not from inside Claude:
>
> ```bash
> bash migrate-v2.sh
> ```
>
> It needs interactive prompts (channel selection, service switchover) and runs Node/pnpm bootstrap, Docker, OneCLI setup, and a container build that don't fit inside a Claude session. When it finishes, it'll hand control back to Claude automatically — at which point this skill picks up.

Do not attempt to run the script yourself, simulate its effects, or pick up the migration mid-stream. The deterministic side has dependencies on a real interactive shell.

Once `handoff.json` exists, proceed to Phase 0.

## Phase 0: Get v2 routing real messages

Before any deeper migration work, prove v2 actually answers messages on the user's real channels. v1 is paused, not touched — flipping back is a service restart.

### 0a — Fix blockers only

Walk `handoff.steps`. Fix only the failures that would stop the bot from routing one message; defer the rest to its later phase.

### 0b — Smoke test, then continue

Tell the user the switch is non-destructive (v1 is paused, not modified; reverting is one command). Help them stop v1's service unit and start v2's, tail the host log for a clean boot, and have them send a real test message. Use `AskUserQuestion` to confirm the bot responded.

If yes, continue to Phase 1. If no, diagnose from `logs/nanoclaw.log` and re-test — don't proceed to deeper work on a broken router.

### Deferred failures

Re-visit anything you skipped in 0a before declaring the migration done. Most surface naturally in later phases (`1c-groups` ↔ Phase 2, `1e-tasks` ↔ task verification).

## Phase 1: Owner and access

v2 auto-creates a `users` row for every sender it sees (via `extractAndUpsertUser` in `src/modules/permissions/index.ts`). By the time this skill runs, the owner's row likely already exists — it just needs the `owner` role granted.

**User ID format**: always `<channel_type>:<platform_handle>`. Each channel populates this differently:
- **Telegram**: `telegram:<numeric_user_id>` (e.g. `telegram:6037840640`)
- **Discord**: `discord:<snowflake_user_id>` (e.g. `discord:123456789012345678`)
- **WhatsApp**: `whatsapp:<phone>@s.whatsapp.net` (e.g. `whatsapp:14155551234@s.whatsapp.net`)
- **Slack**: `slack:<user_id>` (e.g. `slack:U04ABCDEF`)
- **Others**: `<channel_type>:<platform_id>`

**Steps:**

1. Query `users` table: `SELECT id, kind, display_name FROM users`.
2. If exactly one user exists, confirm: `AskUserQuestion`: "Is `<display_name>` (`<id>`) you?" — Yes / No, let me type it.
3. If multiple users exist, present them as options in `AskUserQuestion`.
4. If no users exist yet (service hasn't received a message), ask the user to send a test message first, then re-query.
5. Once confirmed, check `user_roles` via `getUserRoles(userId)`. If an `owner` row already exists, skip. Otherwise grant it with `grantRole`. `grantRole` inserts a new row per call, so the `getUserRoles` check keeps this re-runnable.

Use the DB helpers in `src/modules/permissions/db/user-roles.ts` (`getUserRoles`, `grantRole`). Init the DB first, then call the helpers:

```ts
import path from 'path';
import { initDb } from '../src/db/connection.js';
import { runMigrations } from '../src/db/migrations/index.js';
import { DATA_DIR } from '../src/config.js';
import { getUserRoles, grantRole } from '../src/modules/permissions/db/user-roles.js';

const db = initDb(path.join(DATA_DIR, 'v2.db'));
runMigrations(db); // idempotent

const userId = '<user_id>';
if (!getUserRoles(userId).some((r) => r.role === 'owner')) {
  grantRole({
    user_id: userId,
    role: 'owner',
    agent_group_id: null, // owner role must be global
    granted_by: null,
    granted_at: new Date().toISOString(),
  });
}
```

### Access policy

After seeding the owner, discuss the access policy. v2's `messaging_groups.unknown_sender_policy` controls who can interact with the bot. `migrate-v2.sh` set it to `public` so the bot would respond during the switchover test, but the user may want to tighten it.

Present the options via `AskUserQuestion`:

1. **Public** (`public`, current) — anyone can message the bot. Good for personal DM bots.
2. **Known users only** (`strict`) — only users the access gate accepts (owner, admin, or `agent_group_members`) can trigger the bot. Others are silently dropped.
3. **Approval required** (`request_approval`) — unknown senders trigger an approval request to the owner. Good for group chats where you want to vet new members.

The `unknown_sender_policy` column accepts exactly these three values; use the parenthesized value for `<chosen_policy>` below.

If the user picks option 2 or 3, seed the known users from v1's message history. The v1 database is at `<handoff.v1_path>/store/messages.db`. It has a `messages` table with `sender` and `sender_name` columns. For each group:

```