UNPKG

repomix

Version:

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

86 lines (85 loc) 3.33 kB
import path from 'node:path'; const SKILL_NAME_MAX_LENGTH = 64; const SKILL_DESCRIPTION_MAX_LENGTH = 1024; const SKILL_NAME_PREFIX = 'repomix-reference'; export const toKebabCase = (str) => { return str .replace(/([a-z])([A-Z])/g, '$1-$2') .replace(/[\s_]+/g, '-') .replace(/[^a-z0-9-]/gi, '') .toLowerCase() .replace(/-+/g, '-') .replace(/^-|-$/g, ''); }; export const validateSkillName = (name) => { if (name.includes('/') || name.includes('\\') || name.includes('\0')) { throw new Error('Skill name cannot contain path separators or null bytes'); } if (/^\.+$/.test(name)) { throw new Error('Skill name cannot consist only of dots'); } const kebabName = toKebabCase(name); if (kebabName.length === 0) { throw new Error('Skill name cannot be empty after normalization'); } return kebabName.substring(0, SKILL_NAME_MAX_LENGTH); }; const toTitleCase = (str) => { return str .replace(/[-_]/g, ' ') .replace(/\b\w/g, (char) => char.toUpperCase()) .trim(); }; export const generateProjectName = (rootDirs) => { const primaryDir = rootDirs[0] || '.'; const dirName = path.basename(path.resolve(primaryDir)); return toTitleCase(dirName); }; export const generateSkillDescription = (_skillName, projectName) => { const description = `Reference codebase for ${projectName}. Use this skill when you need to understand the structure, implementation patterns, or code details of the ${projectName} project.`; return description.substring(0, SKILL_DESCRIPTION_MAX_LENGTH); }; export const generateProjectNameFromUrl = (remoteUrl) => { const repoName = extractRepoName(remoteUrl); return toTitleCase(repoName); }; const trimTrailingSlashes = (str) => { let end = str.length; while (end > 0 && str[end - 1] === '/') { end--; } return str.slice(0, end); }; export const extractRepoName = (url) => { let cleanUrl = url.trim(); const queryIndex = cleanUrl.indexOf('?'); const hashIndex = cleanUrl.indexOf('#'); if (queryIndex !== -1 || hashIndex !== -1) { const cutIndex = queryIndex === -1 ? hashIndex : hashIndex === -1 ? queryIndex : Math.min(queryIndex, hashIndex); cleanUrl = cleanUrl.slice(0, cutIndex); } cleanUrl = trimTrailingSlashes(cleanUrl); if (cleanUrl.endsWith('.git')) { cleanUrl = cleanUrl.slice(0, -4); } const lastSlashIndex = cleanUrl.lastIndexOf('/'); if (lastSlashIndex !== -1 && lastSlashIndex < cleanUrl.length - 1) { return cleanUrl.slice(lastSlashIndex + 1); } const slashIndex = cleanUrl.indexOf('/'); if (slashIndex !== -1 && slashIndex < cleanUrl.length - 1) { return cleanUrl.slice(slashIndex + 1); } return 'unknown'; }; export const generateDefaultSkillNameFromUrl = (remoteUrl) => { const baseName = extractRepoName(remoteUrl); const skillName = `${SKILL_NAME_PREFIX}-${toKebabCase(baseName)}`; return validateSkillName(skillName); }; export const generateDefaultSkillName = (rootDirs) => { const primaryDir = rootDirs[0] || '.'; const baseName = path.basename(path.resolve(primaryDir)); const skillName = `${SKILL_NAME_PREFIX}-${toKebabCase(baseName)}`; return validateSkillName(skillName); };