diff --git a/.changeset/weak-seas-add.md b/.changeset/weak-seas-add.md new file mode 100644 index 00000000000..1e22afd0c70 --- /dev/null +++ b/.changeset/weak-seas-add.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +fix: improve symlink handling in skills directory diff --git a/src/services/roo-config/index.ts b/src/services/roo-config/index.ts index 153e5c53dd7..dd6f34ca193 100644 --- a/src/services/roo-config/index.ts +++ b/src/services/roo-config/index.ts @@ -86,10 +86,17 @@ export async function directoryExists(dirPath: string): Promise { const stat = await fs.stat(dirPath) return stat.isDirectory() } catch (error: any) { - // Only catch expected "not found" errors - if (error.code === "ENOENT" || error.code === "ENOTDIR") { + // kilocode_change start: Handle errors that can occur with symlinks: ENOENT (not found), ENOTDIR (not a directory), + // ELOOP (too many symlinks), ENOTCONN (network connection issue for network drives) + if ( + error.code === "ENOENT" || + error.code === "ENOTDIR" || + error.code === "ELOOP" || + error.code === "ENOTCONN" + ) { return false } + // kilocode_change end // Re-throw unexpected errors (permission, I/O, etc.) throw error } @@ -103,10 +110,17 @@ export async function fileExists(filePath: string): Promise { const stat = await fs.stat(filePath) return stat.isFile() } catch (error: any) { - // Only catch expected "not found" errors - if (error.code === "ENOENT" || error.code === "ENOTDIR") { + // kilocode_change start: Handle errors that can occur with symlinks: ENOENT (not found), ENOTDIR (not a directory), + // ELOOP (too many symlinks), ENOTCONN (network connection issue for network drives) + if ( + error.code === "ENOENT" || + error.code === "ENOTDIR" || + error.code === "ELOOP" || + error.code === "ENOTCONN" + ) { return false } + // kilocode_change end // Re-throw unexpected errors (permission, I/O, etc.) throw error } diff --git a/src/services/skills/SkillsManager.ts b/src/services/skills/SkillsManager.ts index 00df898a0c7..bee1499bfdf 100644 --- a/src/services/skills/SkillsManager.ts +++ b/src/services/skills/SkillsManager.ts @@ -62,6 +62,7 @@ export class SkillsManager { try { // Get the real path (resolves if dirPath is a symlink) + // If the symlink is broken, this will throw ENOENT // kilocode_change const realDirPath = await fs.realpath(dirPath) // Read directory entries @@ -77,9 +78,20 @@ export class SkillsManager { // Load skill metadata - the skill name comes from the entry name (symlink name if symlinked) await this.loadSkillMetadata(entryPath, source, mode, entryName) } - } catch { - // Directory doesn't exist or can't be read - this is fine + // kilocode_change start: Handle symlink-related errors gracefully + } catch (error: any) { + // Handle symlink-related errors gracefully: + // - ENOENT: Directory/symlink target doesn't exist + // - ELOOP: Too many symbolic links encountered + // - ENOTCONN: Network drive not connected (for symlinks to network paths) + if (error.code === "ENOENT" || error.code === "ELOOP" || error.code === "ENOTCONN") { + // Silently ignore - this is expected for broken symlinks or unavailable network drives + return + } + // Log other unexpected errors for debugging + console.error(`Error scanning skills directory ${dirPath}:`, error) } + // kilocode_change end } /**