| 995 | * @returns Array of newly activated skill names |
| 996 | */ |
| 997 | export function activateConditionalSkillsForPaths( |
| 998 | filePaths: string[], |
| 999 | cwd: string, |
| 1000 | ): string[] { |
| 1001 | if (conditionalSkills.size === 0) { |
| 1002 | return [] |
| 1003 | } |
| 1004 | |
| 1005 | const activated: string[] = [] |
| 1006 | |
| 1007 | for (const [name, skill] of conditionalSkills) { |
| 1008 | if (skill.type !== 'prompt' || !skill.paths || skill.paths.length === 0) { |
| 1009 | continue |
| 1010 | } |
| 1011 | |
| 1012 | const skillIgnore = ignore().add(skill.paths) |
| 1013 | for (const filePath of filePaths) { |
| 1014 | const relativePath = isAbsolute(filePath) |
| 1015 | ? relative(cwd, filePath) |
| 1016 | : filePath |
| 1017 | |
| 1018 | // ignore() throws on empty strings, paths escaping the base (../), |
| 1019 | // and absolute paths (Windows cross-drive relative() returns absolute). |
| 1020 | // Files outside cwd can't match cwd-relative patterns anyway. |
| 1021 | if ( |
| 1022 | !relativePath || |
| 1023 | relativePath.startsWith('..') || |
| 1024 | isAbsolute(relativePath) |
| 1025 | ) { |
| 1026 | continue |
| 1027 | } |
| 1028 | |
| 1029 | if (skillIgnore.ignores(relativePath)) { |
| 1030 | // Activate this skill by moving it to dynamic skills |
| 1031 | dynamicSkills.set(name, skill) |
| 1032 | conditionalSkills.delete(name) |
| 1033 | activatedConditionalSkillNames.add(name) |
| 1034 | activated.push(name) |
| 1035 | logForDebugging( |
| 1036 | `[skills] Activated conditional skill '${name}' (matched path: ${relativePath})`, |
| 1037 | ) |
| 1038 | break |
| 1039 | } |
| 1040 | } |
| 1041 | } |
| 1042 | |
| 1043 | if (activated.length > 0) { |
| 1044 | logEvent('tengu_dynamic_skills_changed', { |
| 1045 | source: |
| 1046 | 'conditional_paths' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, |
| 1047 | previousCount: dynamicSkills.size - activated.length, |
| 1048 | newCount: dynamicSkills.size, |
| 1049 | addedCount: activated.length, |
| 1050 | directoryCount: 0, |
| 1051 | }) |
| 1052 | |
| 1053 | // Notify listeners that skills were loaded (so they can clear caches) |
| 1054 | skillsLoaded.emit() |