diff --git a/setup/migrate-v2/groups.ts b/setup/migrate-v2/groups.ts index beb88be..d1e9ed0 100644 --- a/setup/migrate-v2/groups.ts +++ b/setup/migrate-v2/groups.ts @@ -18,7 +18,16 @@ import Database from 'better-sqlite3'; const SKIP_NAMES = new Set(['CLAUDE.md', 'logs', '.git', '.DS_Store', 'node_modules']); -/** Copy a directory tree, skipping SKIP_NAMES. Never overwrites existing files. */ +/** + * Copy a directory tree, skipping SKIP_NAMES. Never overwrites existing files. + * + * Symlinks are skipped, not followed: v1 group folders sometimes contain + * container-side paths like `.claude-shared.md → /app/CLAUDE.md` that + * don't resolve on the host. Following them with `fs.copyFileSync` would + * crash ENOENT on a broken target and abort the rest of the traversal. + * v2 uses composed CLAUDE.md fragments anyway — these v1 symlinks have no + * v2 meaning and don't need to be carried forward. + */ function copyTree(src: string, dst: string): number { let written = 0; if (!fs.existsSync(src)) return 0; @@ -29,6 +38,10 @@ function copyTree(src: string, dst: string): number { const s = path.join(src, entry.name); const d = path.join(dst, entry.name); + if (entry.isSymbolicLink()) { + console.log(`SKIP:symlink ${path.relative(process.cwd(), s)}`); + continue; + } if (entry.isDirectory()) { written += copyTree(s, d); continue;