UNPKG

repomix

Version:

A tool to pack repository contents to single file for AI consumption

105 lines (104 loc) 3.55 kB
import fs from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; import pc from 'picocolors'; import { OperationCancelledError, RepomixError } from '../../shared/errorHandle.js'; import { getDisplayPath } from '../cliReport.js'; const onCancelOperation = (cancelFn) => { cancelFn('Skill generation cancelled.'); throw new OperationCancelledError('Skill generation cancelled'); }; export const getSkillBaseDir = (cwd, location) => { if (location === 'personal') { return path.join(os.homedir(), '.claude', 'skills'); } return path.join(cwd, '.claude', 'skills'); }; const createPromptDeps = async () => { const p = await import('@clack/prompts'); return { select: p.select, confirm: p.confirm, isCancel: p.isCancel, cancel: p.cancel, access: fs.access, rm: fs.rm, }; }; export const promptSkillLocation = async (skillName, cwd, deps) => { const resolvedDeps = deps ?? (await createPromptDeps()); const location = await resolvedDeps.select({ message: 'Where would you like to save the skill?', options: [ { value: 'personal', label: 'Personal Skills', hint: '~/.claude/skills/ - Available across all projects', }, { value: 'project', label: 'Project Skills', hint: '.claude/skills/ - Shared with team via git', }, ], initialValue: 'personal', }); if (resolvedDeps.isCancel(location)) { onCancelOperation(resolvedDeps.cancel); } const skillDir = path.join(getSkillBaseDir(cwd, location), skillName); let dirExists = false; try { await resolvedDeps.access(skillDir); dirExists = true; } catch { } if (dirExists) { const displayPath = getDisplayPath(skillDir, cwd); const overwrite = await resolvedDeps.confirm({ message: `Skill directory already exists. Do you want to overwrite it?\n${pc.dim(`path: ${displayPath}`)}`, }); if (resolvedDeps.isCancel(overwrite) || !overwrite) { onCancelOperation(resolvedDeps.cancel); } await resolvedDeps.rm(skillDir, { recursive: true, force: true }); } return { location: location, skillDir, }; }; export const prepareSkillDir = async (skillDir, force, deps = { access: fs.access, rm: fs.rm, stat: fs.stat, }) => { try { await deps.access(skillDir); const stats = await deps.stat(skillDir); if (!stats.isDirectory()) { throw new RepomixError(`Skill output path exists but is not a directory: ${skillDir}`); } if (force) { await deps.rm(skillDir, { recursive: true, force: true }); } else { throw new RepomixError(`Skill directory already exists: ${skillDir}. Use --force to overwrite.`); } } catch (error) { if (error?.code !== 'ENOENT') { throw error; } } }; export const resolveAndPrepareSkillDir = async (skillOutput, cwd, force) => { const skillDir = path.isAbsolute(skillOutput) ? skillOutput : path.resolve(cwd, skillOutput); await prepareSkillDir(skillDir, force); return skillDir; }; export const getSkillLocation = (skillDir) => { const personalSkillsBase = getSkillBaseDir('', 'personal'); return skillDir.startsWith(personalSkillsBase) ? 'personal' : 'project'; };