UNPKG

repomix

Version:

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

121 lines (117 loc) 5.89 kB
import fs from 'node:fs/promises'; import path from 'node:path'; import { z } from 'zod'; import { runCli } from '../../cli/cliRun.js'; import { getSkillBaseDir } from '../../cli/prompts/skillPrompts.js'; import { generateDefaultSkillName, validateSkillName } from '../../core/skill/skillUtils.js'; import { buildMcpToolErrorResponse, buildMcpToolSuccessResponse, convertErrorToJson } from './mcpToolRuntime.js'; const generateSkillInputSchema = z.object({ directory: z.string().describe('Directory to pack (Absolute path)'), skillName: z .string() .optional() .describe('Name of the skill to generate (kebab-case, max 64 chars). Will be normalized if not in kebab-case. Used for the skill directory name and SKILL.md metadata. If omitted, auto-generates as "repomix-reference-<folder-name>".'), compress: z .boolean() .default(false) .describe('Enable Tree-sitter compression to extract essential code signatures and structure while removing implementation details (default: false).'), includePatterns: z .string() .optional() .describe('Specify files to include using fast-glob patterns. Multiple patterns can be comma-separated (e.g., "**/*.{js,ts}", "src/**,docs/**").'), ignorePatterns: z .string() .optional() .describe('Specify additional files to exclude using fast-glob patterns. Multiple patterns can be comma-separated (e.g., "test/**,*.spec.js").'), }); const generateSkillOutputSchema = z.object({ skillPath: z.string().describe('Path to the generated skill directory'), skillName: z.string().describe('Normalized name of the generated skill'), totalFiles: z.number().describe('Total number of files processed'), totalTokens: z.number().describe('Total token count of the content'), description: z.string().describe('Human-readable description of the skill generation results'), }); export const registerGenerateSkillTool = (mcpServer) => { mcpServer.registerTool('generate_skill', { title: 'Generate Claude Agent Skill', description: `Generate a Claude Agent Skill from a local code directory. Creates a skill package containing SKILL.md (entry point with metadata) and references/ folder with summary.md, project-structure.md, files.md, and optionally tech-stacks.md. This tool creates Project Skills in <project>/.claude/skills/<name>/, which are shared with the team via version control. Output Structure: .claude/skills/<skill-name>/ ├── SKILL.md # Entry point with usage guide └── references/ ├── summary.md # Purpose, format, and statistics ├── project-structure.md # Directory tree with line counts ├── files.md # All file contents └── tech-stacks.md # Languages, frameworks, dependencies (if detected) Example Path: /path/to/project/.claude/skills/repomix-reference-myproject/`, inputSchema: generateSkillInputSchema, outputSchema: generateSkillOutputSchema, annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false, }, }, async ({ directory, skillName, compress, includePatterns, ignorePatterns }) => { try { if (!path.isAbsolute(directory)) { return buildMcpToolErrorResponse({ errorMessage: `Directory must be an absolute path: ${directory}`, }); } const normalizedDirectory = path.normalize(directory); if (normalizedDirectory !== directory) { return buildMcpToolErrorResponse({ errorMessage: `Directory path must be normalized. Use "${normalizedDirectory}" instead of "${directory}"`, }); } try { await fs.access(directory); } catch { return buildMcpToolErrorResponse({ errorMessage: `Directory not accessible: ${directory}`, }); } const actualSkillName = skillName ? validateSkillName(skillName) : generateDefaultSkillName([directory]); const skillDir = path.join(getSkillBaseDir(directory, 'project'), actualSkillName); try { await fs.access(skillDir); return buildMcpToolErrorResponse({ errorMessage: `Skill directory already exists: ${skillDir}. Please remove it first or use a different skill name.`, }); } catch { } const cliOptions = { skillGenerate: actualSkillName, skillName: actualSkillName, skillDir, compress, include: includePatterns, ignore: ignorePatterns, securityCheck: true, quiet: true, }; const result = await runCli(['.'], directory, cliOptions); if (!result) { return buildMcpToolErrorResponse({ errorMessage: 'Failed to generate skill', }); } const { packResult } = result; return buildMcpToolSuccessResponse({ skillPath: skillDir, skillName: actualSkillName, totalFiles: packResult.totalFiles, totalTokens: packResult.totalTokens, description: `Successfully generated Claude Agent Skill at ${skillDir}. The skill contains ${packResult.totalFiles} files with ${packResult.totalTokens.toLocaleString()} tokens.`, }); } catch (error) { return buildMcpToolErrorResponse(convertErrorToJson(error)); } }); };