* Processes skill directories that were discovered during file operations. * Uses dynamicSkillDirTriggers field from ToolUseContext
( toolUseContext: ToolUseContext, )
| 2545 | * Uses dynamicSkillDirTriggers field from ToolUseContext |
| 2546 | */ |
| 2547 | async function getDynamicSkillAttachments( |
| 2548 | toolUseContext: ToolUseContext, |
| 2549 | ): Promise<Attachment[]> { |
| 2550 | const attachments: Attachment[] = [] |
| 2551 | |
| 2552 | if ( |
| 2553 | toolUseContext.dynamicSkillDirTriggers && |
| 2554 | toolUseContext.dynamicSkillDirTriggers.size > 0 |
| 2555 | ) { |
| 2556 | // Parallelize: readdir all skill dirs concurrently |
| 2557 | const perDirResults = await Promise.all( |
| 2558 | Array.from(toolUseContext.dynamicSkillDirTriggers).map(async skillDir => { |
| 2559 | try { |
| 2560 | const entries = await readdir(skillDir, { withFileTypes: true }) |
| 2561 | const candidates = entries |
| 2562 | .filter(e => e.isDirectory() || e.isSymbolicLink()) |
| 2563 | .map(e => e.name) |
| 2564 | // Parallelize: stat all SKILL.md candidates concurrently |
| 2565 | const checked = await Promise.all( |
| 2566 | candidates.map(async name => { |
| 2567 | try { |
| 2568 | await stat(resolve(skillDir, name, 'SKILL.md')) |
| 2569 | return name |
| 2570 | } catch { |
| 2571 | return null // SKILL.md doesn't exist, skip this entry |
| 2572 | } |
| 2573 | }), |
| 2574 | ) |
| 2575 | return { |
| 2576 | skillDir, |
| 2577 | skillNames: checked.filter((n): n is string => n !== null), |
| 2578 | } |
| 2579 | } catch { |
| 2580 | // Ignore errors reading skill directories (e.g., directory doesn't exist) |
| 2581 | return { skillDir, skillNames: [] } |
| 2582 | } |
| 2583 | }), |
| 2584 | ) |
| 2585 | |
| 2586 | for (const { skillDir, skillNames } of perDirResults) { |
| 2587 | if (skillNames.length > 0) { |
| 2588 | attachments.push({ |
| 2589 | type: 'dynamic_skill', |
| 2590 | skillDir, |
| 2591 | skillNames, |
| 2592 | displayPath: relative(getCwd(), skillDir), |
| 2593 | }) |
| 2594 | } |
| 2595 | } |
| 2596 | |
| 2597 | toolUseContext.dynamicSkillDirTriggers.clear() |
| 2598 | } |
| 2599 | |
| 2600 | return attachments |
| 2601 | } |
| 2602 | |
| 2603 | // Track which skills have been sent to avoid re-sending. Keyed by agentId |
| 2604 | // (empty string = main thread) so subagents get their own turn-0 listing — |