Skip to content

Commit 66283f9

Browse files
fix(Mountain): Return Cocoon PID for debug adapter and register walkthrough commands
Two related changes to improve extension host integration: 1. **Cocoon PID for debug adapters** (`WindServiceHandlers/mod.rs`, `CocoonManagement.rs`): The `extensionHostStarter:start` IPC handler was incorrectly returning Mountain's own PID. The renderer uses this PID to correlate extension-host-side debug adapters with the actual Node.js process. Now we return the Cocoon child process PID via a new `GetCocoonPid()` function that reads from an atomic static, avoiding the need to take the async `COCOON_STATE` mutex. 2. **Walkthrough command no-ops** (`Bootstrap.rs`): The `claude-code` extension invokes `workbench.action.openWalkthrough` and `claude-vscode.openWalkthrough` at activation. Land has no walkthrough/welcome UI yet. Register both commands as native no-ops that return `null` to prevent "command not found" errors during extension activation. Impact: Debug adapters now attach to the correct Node.js process (Cocoon), and the claude-code extension activates without errors.
1 parent 4dcb366 commit 66283f9

3 files changed

Lines changed: 61 additions & 2 deletions

File tree

Source/Command/Bootstrap.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,26 @@ fn CommandSetContext(
352352
})
353353
}
354354

355+
/// Native no-op for `workbench.action.openWalkthrough`. VS Code's
356+
/// walkthrough UI lives in `workbench/contrib/welcomeGettingStarted` and is
357+
/// not wired through Land yet. Extensions (notably `claude-code`) invoke this
358+
/// at activation - returning null avoids a user-visible "command not found"
359+
/// error while the walkthrough system remains unimplemented.
360+
fn CommandOpenWalkthrough(
361+
_ApplicationHandle:AppHandle<Wry>,
362+
363+
_Window:WebviewWindow<Wry>,
364+
365+
_RunTime:Arc<ApplicationRunTime>,
366+
367+
Argument:Value,
368+
) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
369+
Box::pin(async move {
370+
dev_log!("commands", "[Native Command] openWalkthrough (no-op): {}", Argument);
371+
Ok(Value::Null)
372+
})
373+
}
374+
355375
/// A native command for reloading the window.
356376
fn CommandReloadWindow(
357377
_ApplicationHandle:AppHandle<Wry>,
@@ -516,6 +536,20 @@ pub fn RegisterNativeCommands(
516536
CommandRegistry.insert("vscode.open".to_string(), CommandHandler::Native(CommandVscodeOpen));
517537
CommandRegistry.insert("vscode.openFolder".to_string(), CommandHandler::Native(CommandVscodeOpen));
518538

539+
// `workbench.action.openWalkthrough` is VS Code's welcome/getting-started
540+
// walkthrough entry point; the `claude-code` extension wraps it with its
541+
// own `claude-vscode.openWalkthrough` command and invokes both at
542+
// activation. Land has no walkthrough UI yet - register both as no-ops so
543+
// extension activation doesn't surface "command not found" errors.
544+
CommandRegistry.insert(
545+
"workbench.action.openWalkthrough".to_string(),
546+
CommandHandler::Native(CommandOpenWalkthrough),
547+
);
548+
CommandRegistry.insert(
549+
"claude-vscode.openWalkthrough".to_string(),
550+
CommandHandler::Native(CommandOpenWalkthrough),
551+
);
552+
519553
dev_log!("commands", "[Bootstrap] {} native commands registered.", CommandRegistry.len());
520554

521555
drop(CommandRegistry);

Source/IPC/WindServiceHandlers/mod.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,8 +1458,16 @@ pub async fn mountain_ipc_invoke(app_handle:AppHandle, command:String, args:Vec<
14581458
Ok(json!({ "id": "1" }))
14591459
},
14601460
"extensionHostStarter:start" => {
1461-
dev_log!("exthost", "extensionHostStarter:start pid={}", std::process::id());
1462-
Ok(json!({ "pid": std::process::id() }))
1461+
// The renderer uses this PID to correlate extension-host-side
1462+
// debug adapters with the actual Node.js process. That process
1463+
// is Cocoon, not Mountain - returning `std::process::id()`
1464+
// here would point the debugger at Mountain's Rust binary.
1465+
// Fall back to Mountain's PID only if Cocoon hasn't spawned
1466+
// yet (should not happen for a real extension-host start).
1467+
let Pid = crate::ProcessManagement::CocoonManagement::GetCocoonPid()
1468+
.unwrap_or_else(std::process::id);
1469+
dev_log!("exthost", "extensionHostStarter:start pid={}", Pid);
1470+
Ok(json!({ "pid": Pid }))
14631471
},
14641472
"extensionHostStarter:kill" => {
14651473
dev_log!("exthost", "extensionHostStarter:kill");

Source/ProcessManagement/CocoonManagement.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,21 @@ lazy_static::lazy_static! {
121121
Arc::new(Mutex::new(HealthMonitor::new()));
122122
}
123123

124+
/// Last-known PID of the Cocoon child process. Mirrored here so callers can
125+
/// read it without taking the async `COCOON_STATE` mutex (e.g. from IPC
126+
/// handlers such as `extensionHostStarter:start`). Set after spawn and
127+
/// cleared on shutdown. `0` means "not running".
128+
static COCOON_PID:std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0);
129+
130+
/// Return the Cocoon child process's OS PID, or `None` if Cocoon has not
131+
/// been spawned (or has exited).
132+
pub fn GetCocoonPid() -> Option<u32> {
133+
match COCOON_PID.load(std::sync::atomic::Ordering::Relaxed) {
134+
0 => None,
135+
Pid => Some(Pid),
136+
}
137+
}
138+
124139
/// The main entry point for initializing the Cocoon sidecar process manager.
125140
///
126141
/// This orchestrates the complete initialization sequence including:
@@ -341,6 +356,7 @@ async fn LaunchAndManageCocoonSideCar(
341356
})?;
342357

343358
let ProcessId = ChildProcess.id().unwrap_or(0);
359+
COCOON_PID.store(ProcessId, std::sync::atomic::Ordering::Relaxed);
344360
dev_log!("cocoon", "[CocoonManagement] Cocoon process spawned [PID: {}]", ProcessId);
345361
crate::dev_log!("cocoon", "spawned PID={}", ProcessId);
346362

@@ -571,6 +587,7 @@ async fn monitor_cocoon_health_task(state:Arc<Mutex<CocoonProcessState>>) {
571587
// Update state
572588
state_guard.IsRunning = false;
573589
state_guard.ChildProcess = None;
590+
COCOON_PID.store(0, std::sync::atomic::Ordering::Relaxed);
574591

575592
// Report health issue
576593
{

0 commit comments

Comments
 (0)