{"record":{"id":"83af4e100d6667b3","repo":"jackwener/OpenCLI","slug":"directory-targetdir-already-exists-and-is-not","errorCode":null,"errorMessage":"Directory \"${targetDir}\" already exists and is not empty.","messagePattern":"Directory \"(.+?)\" already exists and is not empty\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/plugin-scaffold.ts","lineNumber":49,"sourceCode":"\n/**\n * Create a new plugin scaffold directory.\n */\nexport function createPluginScaffold(name: string, opts: ScaffoldOptions = {}): ScaffoldResult {\n  // Validate name\n  if (!/^[a-z][a-z0-9-]*$/.test(name)) {\n    throw new Error(\n      `Invalid plugin name \"${name}\". ` +\n      `Plugin names must start with a lowercase letter and contain only lowercase letters, digits, and hyphens.`\n    );\n  }\n\n  const targetDir = opts.dir\n    ? path.resolve(opts.dir)\n    : path.resolve(name);\n\n  if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {\n    throw new Error(`Directory \"${targetDir}\" already exists and is not empty.`);\n  }\n\n  fs.mkdirSync(targetDir, { recursive: true });\n\n  const files: string[] = [];\n\n  // opencli-plugin.json\n  const manifest = {\n    name,\n    version: '0.1.0',\n    description: opts.description ?? `An opencli plugin: ${name}`,\n    opencli: `>=${PKG_VERSION}`,\n  };\n  writeFile(targetDir, 'opencli-plugin.json', JSON.stringify(manifest, null, 2) + '\\n');\n  files.push('opencli-plugin.json');\n\n  // package.json\n  const pkg = {","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/src/plugin-scaffold.ts#L31-L67","documentation":"createPluginScaffold resolves the target directory (opts.dir or the plugin name) and refuses to proceed if it already exists and is non-empty, to avoid clobbering existing files. An empty existing directory is allowed and simply reused; a non-empty one throws this Error before any files are written.","triggerScenarios":"Running the scaffold twice for the same plugin name without deleting the first output, or pointing opts.dir at an existing populated directory such as the current repo folder.","commonSituations":"Re-running a failed/partial scaffold after a first attempt partially populated the directory, choosing the project root as opts.dir, leftover files from a previous experiment with the same plugin name.","solutions":["Delete or move the existing directory, then rerun the scaffold","Pass a different opts.dir (or plugin name) pointing at a fresh path","If reuse is intended, empty the directory first — an existing but empty directory is accepted"],"exampleFix":"// before\ncreatePluginScaffold('my-plugin'); // second run, dir already populated\n// after\nrm -rf ./my-plugin && createPluginScaffold('my-plugin');\n// or target a fresh location\ncreatePluginScaffold('my-plugin', { dir: './plugins/my-plugin-v2' });","handlingStrategy":"validation","validationCode":"import fs from 'fs'; import path from 'path';\nconst targetDir = opts.dir ? path.resolve(opts.dir) : path.resolve(name);\nif (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {\n  throw new Error(`target not empty: ${targetDir}`);\n}","typeGuard":"function isWritableFreshDir(dir: string): boolean {\n  return !fs.existsSync(dir) || fs.readdirSync(dir).length === 0;\n}","tryCatchPattern":"try {\n  createPluginScaffold(name, opts);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('already exists and is not empty')) {\n    console.error('Pick a new --dir or remove/relocate the existing directory.');\n  } else throw e;\n}","preventionTips":["Check the target directory with fs.existsSync/readdirSync before scaffolding","Use unique per-plugin directories (e.g. plugins/<name>) to avoid collisions","Clean up failed scaffold attempts before rerunning","Never point opts.dir at a populated folder like the repo root"],"tags":["filesystem","validation","plugin","scaffold"],"backgroundTag":"directory-already-exists","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}