repomix
Version:
A tool to pack repository contents to single file for AI consumption
86 lines (85 loc) • 3.33 kB
JavaScript
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);
};