1616 * adhdev launch --workspace /path — Open specific workspace
1717 */
1818
19- import { execSync , spawn , spawnSync } from 'child_process' ;
19+ import { exec , spawn , spawnSync } from 'child_process' ;
20+
21+ async function execQuiet ( command : string , options : any = { } ) : Promise < string > {
22+ return new Promise ( ( resolve ) => {
23+ exec ( command , options , ( error , stdout ) => {
24+ if ( error ) return resolve ( '' ) ;
25+ resolve ( stdout . toString ( ) ) ;
26+ } ) ;
27+ } ) ;
28+ }
2029import * as net from 'net' ;
2130import * as os from 'os' ;
2231import * as path from 'path' ;
@@ -76,11 +85,11 @@ function getIdePathCandidates(ideId: string): string[] {
7685 return getProviderLoader ( ) . getIdePathCandidates ( ideId ) ;
7786}
7887
79- function getMacAppProcessPids ( ideId : string ) : number [ ] {
88+ async function getMacAppProcessPids ( ideId : string ) : Promise < number [ ] > {
8089 const appPaths = getIdePathCandidates ( ideId ) ;
8190 if ( appPaths . length === 0 ) return [ ] ;
8291 try {
83- const output = execSync ( 'ps axww -o pid=,args=' , {
92+ const output = await execQuiet ( 'ps axww -o pid=,args=' , {
8493 encoding : 'utf-8' ,
8594 timeout : 3000 ,
8695 stdio : [ 'pipe' , 'pipe' , 'pipe' ] ,
@@ -91,8 +100,8 @@ function getMacAppProcessPids(ideId: string): number[] {
91100 }
92101}
93102
94- function killMacAppPathProcesses ( ideId : string , signal : NodeJS . Signals ) : boolean {
95- const pids = getMacAppProcessPids ( ideId ) ;
103+ async function killMacAppPathProcesses ( ideId : string , signal : NodeJS . Signals ) : Promise < boolean > {
104+ const pids = ( await getMacAppProcessPids ( ideId ) ) ;
96105 let signalled = false ;
97106 for ( const pid of pids ) {
98107 try {
@@ -163,73 +172,73 @@ export async function killIdeProcess(ideId: string): Promise<boolean> {
163172 if ( plat === 'darwin' && appName ) {
164173 // macOS: graceful quit via osascript
165174 try {
166- execSync ( `osascript -e 'tell application "${ escapeForAppleScript ( appName ) } " to quit' 2>/dev/null` , {
175+ await execQuiet ( `osascript -e 'tell application "${ escapeForAppleScript ( appName ) } " to quit' 2>/dev/null` , {
167176 timeout : 5000 ,
168177 } ) ;
169178 } catch {
170- try { execSync ( `pkill -x "${ appName } " 2>/dev/null` , { timeout : 5000 } ) ; } catch { }
179+ try { await execQuiet ( `pkill -x "${ appName } " 2>/dev/null` , { timeout : 5000 } ) ; } catch { }
171180 }
172- killMacAppPathProcesses ( ideId , 'SIGTERM' ) ;
181+ await killMacAppPathProcesses ( ideId , 'SIGTERM' ) ;
173182 } else if ( plat === 'win32' && winProcesses ) {
174183 // Windows: taskkill for each process name
175184 for ( const proc of winProcesses ) {
176185 try {
177- execSync ( `taskkill /IM "${ proc } " /F 2>nul` , { timeout : 5000 } ) ;
186+ await execQuiet ( `taskkill /IM "${ proc } " /F 2>nul` , { timeout : 5000 } ) ;
178187 } catch { }
179188 }
180189 // Process name may differ, so also try via WMIC
181190 try {
182191 const exeName = winProcesses [ 0 ] . replace ( '.exe' , '' ) ;
183- execSync ( `powershell -Command "Get-Process -Name '${ exeName } ' -ErrorAction SilentlyContinue | Stop-Process -Force"` , {
192+ await execQuiet ( `powershell -Command "Get-Process -Name '${ exeName } ' -ErrorAction SilentlyContinue | Stop-Process -Force"` , {
184193 timeout : 10000 ,
185194 } ) ;
186195 } catch { }
187196 } else {
188- try { execSync ( `pkill -f "${ ideId } " 2>/dev/null` ) ; } catch { }
197+ try { await execQuiet ( `pkill -f "${ ideId } " 2>/dev/null` ) ; } catch { }
189198 }
190199
191200 // Wait for process kill (max 15 seconds)
192201 for ( let i = 0 ; i < 30 ; i ++ ) {
193202 await new Promise ( r => setTimeout ( r , 500 ) ) ;
194- if ( ! isIdeRunning ( ideId ) ) return true ;
203+ if ( ! ( await isIdeRunning ( ideId ) ) ) return true ;
195204 }
196205
197206 // Force terminate retry
198207 if ( plat === 'darwin' && appName ) {
199- try { execSync ( `pkill -9 -x "${ appName } " 2>/dev/null` , { timeout : 5000 } ) ; } catch { }
200- killMacAppPathProcesses ( ideId , 'SIGKILL' ) ;
208+ try { await execQuiet ( `pkill -9 -x "${ appName } " 2>/dev/null` , { timeout : 5000 } ) ; } catch { }
209+ await killMacAppPathProcesses ( ideId , 'SIGKILL' ) ;
201210 } else if ( plat === 'win32' && winProcesses ) {
202211 for ( const proc of winProcesses ) {
203- try { execSync ( `taskkill /IM "${ proc } " /F 2>nul` ) ; } catch { }
212+ try { await execQuiet ( `taskkill /IM "${ proc } " /F 2>nul` ) ; } catch { }
204213 }
205214 }
206215
207216 await new Promise ( r => setTimeout ( r , 2000 ) ) ;
208- return ! isIdeRunning ( ideId ) ;
217+ return ! ( await isIdeRunning ( ideId ) ) ;
209218
210219 } catch {
211220 return false ;
212221 }
213222}
214223
215224/** Check if IDE process is running */
216- export function isIdeRunning ( ideId : string ) : boolean {
225+ export async function isIdeRunning ( ideId : string ) : Promise < boolean > {
217226 const plat = os . platform ( ) ;
218227
219228 try {
220229 if ( plat === 'darwin' ) {
221230 const appName = getMacAppIdentifiers ( ) [ ideId ] ;
222- if ( ! appName ) return getMacAppProcessPids ( ideId ) . length > 0 ;
231+ if ( ! appName ) return ( await getMacAppProcessPids ( ideId ) ) . length > 0 ;
223232 try {
224- const result = execSync ( `pgrep -x "${ appName } " 2>/dev/null` , {
233+ const result = await execQuiet ( `pgrep -x "${ appName } " 2>/dev/null` , {
225234 encoding : 'utf-8' ,
226235 timeout : 3000 ,
227236 } ) ;
228237 if ( result . trim ( ) . length > 0 ) return true ;
229238 } catch { }
230239
231240 try {
232- const result = execSync (
241+ const result = await execQuiet (
233242 `osascript -e 'tell application "System Events" to count (every process whose name is "${ escapeForAppleScript ( appName ) } ")'` ,
234243 {
235244 encoding : 'utf-8' ,
@@ -240,29 +249,29 @@ export function isIdeRunning(ideId: string): boolean {
240249 if ( Number . parseInt ( result . trim ( ) || '0' , 10 ) > 0 ) return true ;
241250 } catch { }
242251
243- return getMacAppProcessPids ( ideId ) . length > 0 ;
252+ return ( await getMacAppProcessPids ( ideId ) ) . length > 0 ;
244253 } else if ( plat === 'win32' ) {
245254 const winProcesses = getWinProcessNames ( ) [ ideId ] ;
246255 if ( ! winProcesses ) return false ;
247256 // Check each process name
248257 for ( const proc of winProcesses ) {
249258 try {
250- const result = execSync ( `tasklist /FI "IMAGENAME eq ${ proc } " /NH 2>nul` , { encoding : 'utf-8' } ) ;
259+ const result = await execQuiet ( `tasklist /FI "IMAGENAME eq ${ proc } " /NH 2>nul` , { encoding : 'utf-8' } ) ;
251260 if ( result . includes ( proc ) ) return true ;
252261 } catch { }
253262 }
254263 // Also check via PowerShell (when tasklist cannot find)
255264 try {
256265 const exeName = winProcesses [ 0 ] . replace ( '.exe' , '' ) ;
257- const result = execSync (
266+ const result = await execQuiet (
258267 `powershell -Command "(Get-Process -Name '${ exeName } ' -ErrorAction SilentlyContinue).Count"` ,
259268 { encoding : 'utf-8' , timeout : 5000 }
260269 ) ;
261270 return parseInt ( result . trim ( ) ) > 0 ;
262271 } catch { }
263272 return false ;
264273 } else {
265- const result = execSync ( `pgrep -f "${ ideId } " 2>/dev/null` , { encoding : 'utf-8' } ) ;
274+ const result = await execQuiet ( `pgrep -f "${ ideId } " 2>/dev/null` , { encoding : 'utf-8' } ) ;
266275 return result . trim ( ) . length > 0 ;
267276 }
268277 } catch {
@@ -271,14 +280,14 @@ export function isIdeRunning(ideId: string): boolean {
271280}
272281
273282/** Detect currently open workspace path */
274- function detectCurrentWorkspace ( ideId : string ) : string | undefined {
283+ async function detectCurrentWorkspace ( ideId : string ) : Promise < string | undefined > {
275284 const plat = os . platform ( ) ;
276285
277286 if ( plat === 'darwin' ) {
278287 try {
279288 const appName = getMacAppIdentifiers ( ) [ ideId ] ;
280289 if ( ! appName ) return undefined ;
281- const result = execSync (
290+ const result = await execQuiet (
282291 `lsof -c "${ appName } " 2>/dev/null | grep cwd | head -1 | awk '{print $NF}'` ,
283292 { encoding : 'utf-8' , timeout : 3000 }
284293 ) ;
@@ -392,8 +401,8 @@ export async function launchWithCdp(options: LaunchOptions = {}): Promise<Launch
392401 }
393402
394403 // 4. Check if IDE is currently running
395- const alreadyRunning = isIdeRunning ( targetIde . id ) ;
396- const workspace = options . workspace || ( alreadyRunning ? detectCurrentWorkspace ( targetIde . id ) : undefined ) ;
404+ const alreadyRunning = await isIdeRunning ( targetIde . id ) ;
405+ const workspace = options . workspace || ( alreadyRunning ? await detectCurrentWorkspace ( targetIde . id ) : undefined ) ;
397406
398407 // 5. If IDE is running, terminate it
399408 if ( alreadyRunning ) {
0 commit comments