Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/weak-seas-add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---

fix: improve symlink handling in skills directory
22 changes: 18 additions & 4 deletions src/services/roo-config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,17 @@ export async function directoryExists(dirPath: string): Promise<boolean> {
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
}
Expand All @@ -103,10 +110,17 @@ export async function fileExists(filePath: string): Promise<boolean> {
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
}
Expand Down
16 changes: 14 additions & 2 deletions src/services/skills/SkillsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}

/**
Expand Down
Loading