Skip to main content
Glama
packSkill.ts8.24 kB
import type { RepomixConfigMerged } from '../../config/configSchema.js'; import { withMemoryLogging } from '../../shared/memoryUtils.js'; import type { RepomixProgressCallback } from '../../shared/types.js'; import type { SkippedFileInfo } from '../file/fileCollect.js'; import type { ProcessedFile } from '../file/fileTypes.js'; import type { GitDiffResult } from '../git/gitDiffHandle.js'; import type { GitLogResult } from '../git/gitLogHandle.js'; import { calculateMetrics } from '../metrics/calculateMetrics.js'; import { buildOutputGeneratorContext, createRenderContext } from '../output/outputGenerate.js'; import { sortOutputFiles } from '../output/outputSort.js'; import type { PackOptions, PackResult } from '../packager.js'; import type { SuspiciousFileResult } from '../security/securityCheck.js'; import { generateFilesSection, generateStructureSection, generateSummarySection } from './skillSectionGenerators.js'; import { calculateStatistics, generateStatisticsSection } from './skillStatistics.js'; import { generateSkillMd } from './skillStyle.js'; import { detectTechStack, generateTechStackMd } from './skillTechStack.js'; import { generateDefaultSkillName, generateProjectName, generateSkillDescription, validateSkillName, } from './skillUtils.js'; import { writeSkillOutput } from './writeSkillOutput.js'; /** * References for skill output - each becomes a separate file */ export interface SkillReferences { summary: string; structure: string; files: string; techStack?: string; } /** * Result of skill references generation (without SKILL.md) */ export interface SkillReferencesResult { references: SkillReferences; skillName: string; projectName: string; skillDescription: string; totalFiles: number; totalLines: number; statisticsSection: string; hasTechStack: boolean; sourceUrl?: string; } /** * Result of skill output generation */ export interface SkillOutputResult { skillMd: string; references: SkillReferences; } const defaultDeps = { buildOutputGeneratorContext, sortOutputFiles, calculateMetrics, writeSkillOutput, generateDefaultSkillName, }; /** * Generates skill reference files (summary, structure, files, tech-stack). * This is the first step - call this, calculate metrics, then call generateSkillMdFromReferences. */ export const generateSkillReferences = async ( skillName: string, rootDirs: string[], config: RepomixConfigMerged, processedFiles: ProcessedFile[], allFilePaths: string[], gitDiffResult: GitDiffResult | undefined = undefined, gitLogResult: GitLogResult | undefined = undefined, skillProjectName?: string, skillSourceUrl?: string, deps = { buildOutputGeneratorContext, sortOutputFiles, }, ): Promise<SkillReferencesResult> => { // Validate and normalize skill name const normalizedSkillName = validateSkillName(skillName); // Use provided project name or generate from root directories const projectName = skillProjectName ?? generateProjectName(rootDirs); // Generate skill description const skillDescription = generateSkillDescription(normalizedSkillName, projectName); // Sort processed files by git change count if enabled const sortedProcessedFiles = await deps.sortOutputFiles(processedFiles, config); // Build output generator context with markdown style const markdownConfig: RepomixConfigMerged = { ...config, output: { ...config.output, style: 'markdown', }, }; const outputGeneratorContext = await deps.buildOutputGeneratorContext( rootDirs, markdownConfig, allFilePaths, sortedProcessedFiles, gitDiffResult, gitLogResult, ); const renderContext = createRenderContext(outputGeneratorContext); // Calculate statistics const statistics = calculateStatistics(sortedProcessedFiles, renderContext.fileLineCounts); const statisticsSection = generateStatisticsSection(statistics); // Detect tech stack const techStack = detectTechStack(sortedProcessedFiles); const techStackMd = techStack ? generateTechStackMd(techStack) : undefined; // Generate each section separately const references: SkillReferences = { summary: generateSummarySection(renderContext, statisticsSection), structure: generateStructureSection(renderContext), files: generateFilesSection(renderContext), techStack: techStackMd, }; return { references, skillName: normalizedSkillName, projectName, skillDescription, totalFiles: sortedProcessedFiles.length, totalLines: statistics.totalLines, statisticsSection, hasTechStack: techStack !== null, sourceUrl: skillSourceUrl, }; }; /** * Generates SKILL.md content from references result and token count. * This is the second step - call after calculating metrics. */ export const generateSkillMdFromReferences = ( referencesResult: SkillReferencesResult, totalTokens: number, ): SkillOutputResult => { const skillMd = generateSkillMd({ skillName: referencesResult.skillName, skillDescription: referencesResult.skillDescription, projectName: referencesResult.projectName, totalFiles: referencesResult.totalFiles, totalLines: referencesResult.totalLines, totalTokens, hasTechStack: referencesResult.hasTechStack, sourceUrl: referencesResult.sourceUrl, }); return { skillMd, references: referencesResult.references, }; }; export interface PackSkillParams { rootDirs: string[]; config: RepomixConfigMerged; options: PackOptions; processedFiles: ProcessedFile[]; allFilePaths: string[]; gitDiffResult: GitDiffResult | undefined; gitLogResult: GitLogResult | undefined; suspiciousFilesResults: SuspiciousFileResult[]; suspiciousGitDiffResults: SuspiciousFileResult[]; suspiciousGitLogResults: SuspiciousFileResult[]; safeFilePaths: string[]; skippedFiles: SkippedFileInfo[]; progressCallback: RepomixProgressCallback; } /** * Generates skill output (SKILL.md and reference files). * This is called from packager.ts when skill generation is requested. */ export const packSkill = async (params: PackSkillParams, deps = defaultDeps): Promise<PackResult> => { const { rootDirs, config, options, processedFiles, allFilePaths, gitDiffResult, gitLogResult, suspiciousFilesResults, suspiciousGitDiffResults, suspiciousGitLogResults, safeFilePaths, skippedFiles, progressCallback, } = params; // Validate skillDir early to fail fast (before expensive operations) const { skillDir } = options; if (!skillDir) { throw new Error('skillDir is required for skill generation'); } // Use pre-computed skill name or generate from directories const skillName = options.skillName ?? (typeof config.skillGenerate === 'string' ? config.skillGenerate : deps.generateDefaultSkillName(rootDirs)); // Step 1: Generate skill references (summary, structure, files, tech-stack) const skillReferencesResult = await withMemoryLogging('Generate Skill References', () => generateSkillReferences( skillName, rootDirs, config, processedFiles, allFilePaths, gitDiffResult, gitLogResult, options.skillProjectName, options.skillSourceUrl, { buildOutputGeneratorContext: deps.buildOutputGeneratorContext, sortOutputFiles: deps.sortOutputFiles, }, ), ); // Step 2: Calculate metrics from files section to get accurate token count const skillMetrics = await withMemoryLogging('Calculate Skill Metrics', () => deps.calculateMetrics( processedFiles, skillReferencesResult.references.files, progressCallback, config, gitDiffResult, gitLogResult, ), ); // Step 3: Generate SKILL.md with accurate token count const skillOutput = generateSkillMdFromReferences(skillReferencesResult, skillMetrics.totalTokens); progressCallback('Writing skill output...'); await withMemoryLogging('Write Skill Output', () => deps.writeSkillOutput(skillOutput, skillDir)); return { ...skillMetrics, suspiciousFilesResults, suspiciousGitDiffResults, suspiciousGitLogResults, processedFiles, safeFilePaths, skippedFiles, }; };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yamadashy/repomix'

If you have feedback or need assistance with the MCP directory API, please join our Discord server