(projectRoot)
| 34 | const MARKER_PATTERN = /<!-- GENERATED: ([a-z-]+) -->([\s\S]*?)<!-- \/GENERATED -->/g; |
| 35 | |
| 36 | function computeCounts(projectRoot) { |
| 37 | const skillsDir = path.join(projectRoot, 'skills'); |
| 38 | const skillCount = fs |
| 39 | .readdirSync(skillsDir, { withFileTypes: true }) |
| 40 | .filter((entry) => entry.isDirectory()) |
| 41 | .filter((entry) => fs.existsSync(path.join(skillsDir, entry.name, 'SKILL.md'))).length; |
| 42 | |
| 43 | const hooksSrcDir = path.join(projectRoot, 'hooks_src'); |
| 44 | const hookScriptCount = fs |
| 45 | .readdirSync(hooksSrcDir) |
| 46 | .filter((name) => name.endsWith('.js')) |
| 47 | .filter((name) => !NON_HOOK_FILES.includes(name)).length; |
| 48 | |
| 49 | const templatePath = path.join(projectRoot, 'hooks', 'hooks-template.json'); |
| 50 | const template = JSON.parse(fs.readFileSync(templatePath, 'utf8')); |
| 51 | const events = template.hooks && typeof template.hooks === 'object' ? template.hooks : template; |
| 52 | const hookEventCount = Object.keys(events).length; |
| 53 | |
| 54 | return { |
| 55 | 'skill-count': skillCount, |
| 56 | 'hook-script-count': hookScriptCount, |
| 57 | 'hook-event-count': hookEventCount, |
| 58 | }; |
| 59 | } |
| 60 | |
| 61 | // Each surface declares which marker names it must contain so a deleted |
| 62 | // marker is caught instead of silently passing. |
no outgoing calls
no test coverage detected