( dir: string, files: Record<string, string>, )
| 145 | } |
| 146 | |
| 147 | async function writeSkillFiles( |
| 148 | dir: string, |
| 149 | files: Record<string, string>, |
| 150 | ): Promise<void> { |
| 151 | // Group by parent dir so we mkdir each subtree once, then write. |
| 152 | const byParent = new Map<string, [string, string][]>() |
| 153 | for (const [relPath, content] of Object.entries(files)) { |
| 154 | const target = resolveSkillFilePath(dir, relPath) |
| 155 | const parent = dirname(target) |
| 156 | const entry: [string, string] = [target, content] |
| 157 | const group = byParent.get(parent) |
| 158 | if (group) group.push(entry) |
| 159 | else byParent.set(parent, [entry]) |
| 160 | } |
| 161 | await Promise.all( |
| 162 | [...byParent].map(async ([parent, entries]) => { |
| 163 | await mkdir(parent, { recursive: true, mode: 0o700 }) |
| 164 | await Promise.all(entries.map(([p, c]) => safeWriteFile(p, c))) |
| 165 | }), |
| 166 | ) |
| 167 | } |
| 168 | |
| 169 | // The per-process nonce in getBundledSkillsRoot() is the primary defense |
| 170 | // against pre-created symlinks/dirs. Explicit 0o700/0o600 modes keep the |
no test coverage detected