MCPcopy
hub / github.com/neomjs/neo / symlinkDataDir

Function symlinkDataDir

ai/scripts/migrations/bootstrapWorktree.mjs:346–403  ·  view source on GitHub ↗
({
    mainCheckout,
    projectRoot,
    subdirs = DATA_SUBDIRS_TO_LINK,
    force   = false,
    log     = console.log
})

Source from the content-addressed store, hash-verified

344 * @throws {Error} When any subdir's dst is a non-symlink directory and `force` is false. The error message names the offending subdir.
345 */
346export async function symlinkDataDir({
347 mainCheckout,
348 projectRoot,
349 subdirs = DATA_SUBDIRS_TO_LINK,
350 force = false,
351 log = console.log
352}) {
353 const result = {linked: [], alreadyLinked: [], clobbered: [], skippedNoSource: [], mainCheckout: false};
354
355 if (path.resolve(projectRoot) === path.resolve(mainCheckout)) {
356 log(`symlink skip (main checkout): no per-subdir action`);
357 result.mainCheckout = true;
358 return result;
359 }
360
361 // Ensure the parent .neo-ai-data/ exists as a regular dir; we never symlink the parent.
362 // This preserves the git-tracked concepts/ subdir already present in the worktree.
363 const parentDst = path.join(projectRoot, '.neo-ai-data');
364 await fs.mkdir(parentDst, {recursive: true});
365
366 for (const subdir of subdirs) {
367 const src = path.join(mainCheckout, '.neo-ai-data', subdir);
368 const dst = path.join(parentDst, subdir);
369 const lstat = await fs.lstat(dst).catch(() => null);
370
371 if (lstat?.isSymbolicLink()) {
372 log(`symlink skip (already linked): ${subdir}`);
373 result.alreadyLinked.push(subdir);
374 continue;
375 }
376
377 // Skip if canonical lacks the subdir — graceful for fresh repos.
378 const srcExists = await exists(src);
379 if (!srcExists) {
380 log(`symlink skip (no source in main checkout): ${subdir}`);
381 result.skippedNoSource.push(subdir);
382 continue;
383 }
384
385 if (lstat?.isDirectory()) {
386 if (!force) {
387 throw new Error(
388 `Refusing to replace non-symlink ${dst}; pass force=true (CLI --force) to opt in. ` +
389 `This directory contains local data that would be lost.`
390 );
391 }
392 log(`symlink clobber (force=true): removing ${subdir}`);
393 await fs.rm(dst, {recursive: true, force: true});
394 result.clobbered.push(subdir);
395 }
396
397 await fs.symlink(src, dst, 'dir');
398 log(`symlinked: ${subdir} → ${src}`);
399 result.linked.push(subdir);
400 }
401
402 return result;
403}

Callers 3

hydrateCurrentWorktreeFunction · 0.85

Calls 4

existsFunction · 0.70
logFunction · 0.50
resolveMethod · 0.45
pushMethod · 0.45

Tested by

no test coverage detected