nextlevelbuilder/ui-ux-pro-max-skill · error · Error

Source directory does not exist: ${sourceDir}

Error message

Source directory does not exist: ${sourceDir}

What it means

syncDir() refuses to mirror when the source directory doesn't exist. Sources are src/ui-ux-pro-max/{data,scripts,templates} and .claude/skills/<sub-skill> folders. This almost always means the checkout is incomplete — a sparse/partial clone, a shallow CI checkout missing paths, or the script being run outside the standard repo layout.

Source

Thrown at cli/scripts/sync-assets.mjs:126

  for (const file of allFiles) {
    const sourcePath = join(sourceDir, file);
    const targetPath = join(targetDir, file);

    if (!sourceFiles.includes(file)) {
      drift.push(`extra asset file: ${label}/${file}`);
    } else if (!targetFiles.includes(file)) {
      drift.push(`missing asset file: ${label}/${file}`);
    } else if ((await fileHash(sourcePath)) !== (await fileHash(targetPath))) {
      drift.push(`stale asset file: ${label}/${file}`);
    }
  }
}

// Mirrors sourceDir -> targetDir (deletes targetDir first, so removed
// source files don't linger as orphans in the target).
async function syncDir(sourceDir, targetDir) {
  if (!(await exists(sourceDir))) {
    throw new Error(`Source directory does not exist: ${sourceDir}`);
  }

  const resolvedTarget = assertInsideRepo(targetDir);
  if (await exists(resolvedTarget)) {
    await rm(resolvedTarget, { recursive: true, force: true });
  }

  for (const file of await listFiles(sourceDir)) {
    if (isExcludedAssetFile(file)) continue;
    const targetPath = assertInsideRepo(join(resolvedTarget, file));
    await mkdir(dirname(targetPath), { recursive: true });
    await writeFile(targetPath, toLF(await readFile(join(sourceDir, file), 'utf8')));
  }
}

async function checkAssets() {
  const drift = [];

View on GitHub (pinned to a38d04c3d5)

Solutions

  1. Verify the source dir exists: `ls src/ui-ux-pro-max/{data,scripts,templates}` from the repo root.
  2. If missing, fix the checkout (full clone, un-sparse the paths) — do not create empty dirs to silence the error.
  3. If a path was renamed, update sourceRoot/dirsToSync/subSkills in cli/scripts/sync-assets.mjs to the new names.
  4. Ensure new sub-skills have their .claude/skills/<name> directory committed before adding them to the array.
Defensive patterns

Strategy: validation

Validate before calling

// preflight: every source dir the script will sync must exist
import { stat } from 'node:fs/promises';

async function assertDir(p: string, label: string) {
  try { await stat(p); } catch {
    throw new Error(`Missing source directory ${label} (${p}) - incomplete checkout?`);
  }
}

for (const dir of ['data', 'scripts', 'templates']) {
  await assertDir(join('src', 'ui-ux-pro-max', dir), `src/ui-ux-pro-max/${dir}`);
}

Prevention

When it happens

Trigger: Running `npm run sync:assets` when src/ui-ux-pro-max/ was never checked out (sparse clone, misconfiguration, or the folder renamed); adding a name to the subSkills array whose .claude/skills/<name> directory doesn't exist yet; running the script from a copied cli/ folder without the rest of the repo.

Common situations: Sparse CI checkouts that only fetch cli/; renames of src/ui-ux-pro-max not propagated to sync-assets.mjs; a new sub-skill listed in the array before its directory is committed.

Related errors


AI-assisted analysis of nextlevelbuilder/ui-ux-pro-max-skill@a38d04c3d5 (2026-08-14). Data as JSON: /api/errors/af17e82404e549f7. Report an issue: GitHub.