feat: add migrate-v2.sh — standalone v1 → v2 migration script
New entry point: `bash migrate-v2.sh` from the v2 checkout. Replaces the old setup-embedded migration flow with a standalone 4-phase script + rewritten Claude skill for the interactive parts. Phase 0: Bootstrap (Node/pnpm/deps via setup.sh) + find v1 Phase 1: Core state (env, DB, groups, sessions, tasks) Phase 2: Channels (clack multiselect, auth copy, code install) Phase 3: Infrastructure (OneCLI, auth, Docker, skills, container build) Service switchover: stop v1 → start v2 → test → keep or revert Phase 4: Handoff → exec claude "/migrate-from-v1" The skill handles: owner seeding, access policy, CLAUDE.local.md cleanup, container config validation, fork customization porting. Key fixes found during testing: - triggerToEngage: requires_trigger=0 must override non-empty pattern - unknown_sender_policy defaults to 'public' (strict drops all msgs before owner is seeded) - Service revert must stop v2 (parse unit name from step log, not early tsx one-liner that can fail) - Session continuity: copy JSONL from -workspace-group/ to -workspace-agent/ and write continuation:claude into outbound.db - container_config.additionalMounts written directly to container.json (same shape in v1 and v2) - EXIT trap writes handoff.json; explicit write_handoff before exec Includes migrate-v2-reset.sh for dev iteration and docs/migration-dev.md for testing/debugging reference. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
63
setup/migrate-v2/select-channels.ts
Normal file
63
setup/migrate-v2/select-channels.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* migrate-v2: interactive channel selection via clack multiselect.
|
||||
*
|
||||
* Writes selected channel names (one per line) to the file path given as
|
||||
* the first argument. Clack renders to the terminal normally.
|
||||
*
|
||||
* If NANOCLAW_CHANNELS env var is set (comma-separated names), skips the
|
||||
* prompt and writes those directly.
|
||||
*
|
||||
* Usage: pnpm exec tsx setup/migrate-v2/select-channels.ts <output-file>
|
||||
*/
|
||||
import fs from 'fs';
|
||||
|
||||
import * as p from '@clack/prompts';
|
||||
|
||||
const CHANNELS = [
|
||||
{ value: 'telegram', label: 'Telegram' },
|
||||
{ value: 'discord', label: 'Discord' },
|
||||
{ value: 'slack', label: 'Slack' },
|
||||
{ value: 'whatsapp', label: 'WhatsApp' },
|
||||
{ value: 'teams', label: 'Microsoft Teams' },
|
||||
{ value: 'matrix', label: 'Matrix' },
|
||||
{ value: 'imessage', label: 'iMessage' },
|
||||
{ value: 'webex', label: 'Webex' },
|
||||
{ value: 'gchat', label: 'Google Chat' },
|
||||
{ value: 'resend', label: 'Resend (email)' },
|
||||
{ value: 'github', label: 'GitHub' },
|
||||
{ value: 'linear', label: 'Linear' },
|
||||
{ value: 'whatsapp-cloud', label: 'WhatsApp Cloud API' },
|
||||
];
|
||||
|
||||
const VALID_NAMES = new Set(CHANNELS.map((c) => c.value));
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const outFile = process.argv[2];
|
||||
if (!outFile) {
|
||||
console.error('Usage: tsx setup/migrate-v2/select-channels.ts <output-file>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Non-interactive: NANOCLAW_CHANNELS="telegram,discord"
|
||||
const envChannels = process.env.NANOCLAW_CHANNELS?.trim();
|
||||
if (envChannels) {
|
||||
const names = envChannels.split(',').map((s) => s.trim()).filter((s) => VALID_NAMES.has(s));
|
||||
fs.writeFileSync(outFile, names.join('\n') + '\n');
|
||||
return;
|
||||
}
|
||||
|
||||
const selected = await p.multiselect({
|
||||
message: 'Which channels do you want to set up?',
|
||||
options: CHANNELS,
|
||||
required: false,
|
||||
});
|
||||
|
||||
if (p.isCancel(selected)) {
|
||||
fs.writeFileSync(outFile, '');
|
||||
return;
|
||||
}
|
||||
|
||||
fs.writeFileSync(outFile, (selected as string[]).join('\n') + '\n');
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user