source dump of claude code
at main 69 lines 2.2 kB view raw
1import { join } from 'path' 2import { logForDebugging } from '../debug.js' 3import { getFsImplementation } from '../fsOperations.js' 4 5const SKILL_MD_RE = /^skill\.md$/i 6 7/** 8 * Recursively walk a plugin directory, invoking onFile for each .md file. 9 * 10 * The namespace array tracks the subdirectory path relative to the root 11 * (e.g., ['foo', 'bar'] for root/foo/bar/file.md). Callers that don't need 12 * namespacing can ignore the second argument. 13 * 14 * When stopAtSkillDir is true and a directory contains SKILL.md, onFile is 15 * called for all .md files in that directory but subdirectories are not 16 * scanned — skill directories are leaf containers. 17 * 18 * Readdir errors are swallowed with a debug log so one bad directory doesn't 19 * abort a plugin load. 20 */ 21export async function walkPluginMarkdown( 22 rootDir: string, 23 onFile: (fullPath: string, namespace: string[]) => Promise<void>, 24 opts: { stopAtSkillDir?: boolean; logLabel?: string } = {}, 25): Promise<void> { 26 const fs = getFsImplementation() 27 const label = opts.logLabel ?? 'plugin' 28 29 async function scan(dirPath: string, namespace: string[]): Promise<void> { 30 try { 31 const entries = await fs.readdir(dirPath) 32 33 if ( 34 opts.stopAtSkillDir && 35 entries.some(e => e.isFile() && SKILL_MD_RE.test(e.name)) 36 ) { 37 // Skill directory: collect .md files here, don't recurse. 38 await Promise.all( 39 entries.map(entry => 40 entry.isFile() && entry.name.toLowerCase().endsWith('.md') 41 ? onFile(join(dirPath, entry.name), namespace) 42 : undefined, 43 ), 44 ) 45 return 46 } 47 48 await Promise.all( 49 entries.map(entry => { 50 const fullPath = join(dirPath, entry.name) 51 if (entry.isDirectory()) { 52 return scan(fullPath, [...namespace, entry.name]) 53 } 54 if (entry.isFile() && entry.name.toLowerCase().endsWith('.md')) { 55 return onFile(fullPath, namespace) 56 } 57 return undefined 58 }), 59 ) 60 } catch (error) { 61 logForDebugging( 62 `Failed to scan ${label} directory ${dirPath}: ${error}`, 63 { level: 'error' }, 64 ) 65 } 66 } 67 68 await scan(rootDir, []) 69}