378
*/
379
public async getHookSourceFolders(): Promise<readonly IResolvedPromptSourceFolder[]> {
381
>
382
>
// Ignore claude folders since they aren't first-class supported, so we don't want to create invalid formats
383
>
// Check for .claude as an actual path segment (starts with ".claude/" or contains "/.claude/")
384
>
const allowedHookFolders = configuredLocations.filter(loc =>
385
>
!loc.path.startsWith('.claude/') && !loc.path.includes('/.claude/')
386
>
);
387
>
388
>
// Convert to absolute locations with metadata
389
>
const absoluteLocations = await this.toAbsoluteLocations(PromptsType.hook, allowedHookFolders);
390
>
391
>
// Deduplicate by search root, keeping the first occurrence
392
>
const seen = new ResourceSet();
393
>
const result: IResolvedPromptSourceFolder[] = [];
394
>
for (const location of absoluteLocations) {
395
>
// For hook configs, entries are directories unless the path ends with .json (specific file)
396
>
// Default entries have filePattern, user entries don't but are still directories
397
>
// searchRoot already points to the correct directory or specific file to use in both cases
398
>
if (!seen.has(location.searchRoot)) {
399
>
seen.add(location.searchRoot);
400
>
result.push({ ...location, uri: location.searchRoot, filePattern: undefined });
401
>
}
402
>
}
403
>
404
>
return result;
405
>
}
406
407
/**