Skip to content

Commit 44ed5f5

Browse files
nkmr-jpclaude
andcommitted
fix(directory-detector): fix pipe deadlock and add sqlite3 timeout
- Read pipe output BEFORE waitUntilExit() to prevent deadlock when output exceeds 64KB pipe buffer (e.g., large workspace history) - Add .timeout 500 to sqlite3 to prevent blocking on locked database - Remove redundant fileExists guard (sqlite3 handles missing files) - Use Set for lowercaseNames lookup - Remove unreachable app names from filter list (zed, opencode) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 478ef35 commit 44ed5f5

File tree

1 file changed

+7
-9
lines changed

1 file changed

+7
-9
lines changed

native/directory-detector/IDEDetector.swift

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,8 @@ extension DirectoryDetector {
198198
}
199199
}
200200

201-
// Filter out known app name suffixes
202-
let appNames = ["visual studio code", "vs code", "cursor", "windsurf", "kiro", "zed", "opencode"]
201+
// Filter out known app name suffixes (only IDEs that use state.vscdb storage)
202+
let appNames = ["visual studio code", "vs code", "cursor", "windsurf", "kiro"]
203203
candidates = candidates.filter { candidate in
204204
!appNames.contains(candidate.lowercased())
205205
}
@@ -217,28 +217,26 @@ extension DirectoryDetector {
217217
let homeDir = FileManager.default.homeDirectoryForCurrentUser.path
218218
let dbPath = "\(homeDir)/Library/Application Support/\(appSupportName)/User/globalStorage/state.vscdb"
219219

220-
guard FileManager.default.fileExists(atPath: dbPath) else {
221-
return nil
222-
}
223-
224220
// Query using sqlite3 command (available on macOS by default)
221+
// .timeout 500 prevents blocking if the database is locked by the IDE
225222
let process = Process()
226223
process.executableURL = URL(fileURLWithPath: "/usr/bin/sqlite3")
227-
process.arguments = [dbPath, "SELECT value FROM ItemTable WHERE key = 'history.recentlyOpenedPathsList'"]
224+
process.arguments = ["-cmd", ".timeout 500", dbPath, "SELECT value FROM ItemTable WHERE key = 'history.recentlyOpenedPathsList'"]
228225

229226
let pipe = Pipe()
230227
process.standardOutput = pipe
231228
process.standardError = FileHandle.nullDevice
232229

233230
do {
234231
try process.run()
232+
// Read pipe BEFORE waitUntilExit to prevent deadlock when output exceeds pipe buffer (64KB)
233+
let data = pipe.fileHandleForReading.readDataToEndOfFile()
235234
process.waitUntilExit()
236235

237236
guard process.terminationStatus == 0 else {
238237
return nil
239238
}
240239

241-
let data = pipe.fileHandleForReading.readDataToEndOfFile()
242240
guard let output = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines),
243241
!output.isEmpty,
244242
let jsonData = output.data(using: .utf8),
@@ -248,7 +246,7 @@ extension DirectoryDetector {
248246
}
249247

250248
// Find matching workspace by folder name
251-
let lowercaseNames = workspaceNames.map { $0.lowercased() }
249+
let lowercaseNames = Set(workspaceNames.map { $0.lowercased() })
252250
for entry in entries {
253251
if let folderUri = entry["folderUri"] as? String,
254252
folderUri.hasPrefix("file://") {

0 commit comments

Comments
 (0)