Skip to content

Commit 81baa50

Browse files
neumieclaude
andcommitted
feat: add OKENA_FOLDER_ID and OKENA_FOLDER_NAME env vars to all hooks
Folder context was missing from hook environment variables. Add folder_id and folder_name to project_env() and propagate through all fire_* functions. For worktree projects, falls back to the parent project's folder. Also includes the missing pty_manager.kill() call on natural terminal exit to prevent orphaned dtach daemons. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 50133d4 commit 81baa50

File tree

10 files changed

+166
-49
lines changed

10 files changed

+166
-49
lines changed

crates/okena-terminal/src/pty_manager.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ impl PtyManager {
113113
std::thread::Builder::new()
114114
.name("dtach-socket-gc".into())
115115
.spawn(|| {
116-
crate::terminal::session_backend::cleanup_stale_dtach_sockets();
116+
crate::session_backend::cleanup_stale_dtach_sockets();
117117
})
118118
.ok();
119119
}

crates/okena-views-git/src/close_worktree_dialog.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ impl CloseWorktreeDialog {
151151
.map(|p| p.hooks.clone())
152152
.unwrap_or_default();
153153
let global_hooks = self.hooks_config.clone();
154+
let folder = ws.folder_for_project_or_parent(&project_id);
155+
let folder_id = folder.map(|f| f.id.clone());
156+
let folder_name = folder.map(|f| f.name.clone());
154157
let monitor = hooks::try_monitor(cx);
155158
let runner = hooks::try_runner(cx);
156159

@@ -227,6 +230,8 @@ impl CloseWorktreeDialog {
227230
let branch = branch.clone();
228231
let default_branch = default_branch.clone();
229232
let main_repo_path = main_repo_path.clone();
233+
let folder_id = folder_id.clone();
234+
let folder_name = folder_name.clone();
230235
let monitor = monitor.clone();
231236
move || {
232237
// Sync hooks run headlessly (no PTY) — they block the flow
@@ -240,6 +245,8 @@ impl CloseWorktreeDialog {
240245
&branch,
241246
&default_branch,
242247
&main_repo_path,
248+
folder_id.as_deref(),
249+
folder_name.as_deref(),
243250
monitor.as_ref(),
244251
None,
245252
)
@@ -289,6 +296,8 @@ impl CloseWorktreeDialog {
289296
&default_branch,
290297
&main_repo_path,
291298
&e,
299+
folder_id.as_deref(),
300+
folder_name.as_deref(),
292301
monitor.as_ref(),
293302
runner.as_ref(),
294303
);
@@ -355,6 +364,8 @@ impl CloseWorktreeDialog {
355364
&branch,
356365
&default_branch,
357366
&main_repo_path,
367+
folder_id.as_deref(),
368+
folder_name.as_deref(),
358369
monitor.as_ref(),
359370
runner.as_ref(),
360371
);
@@ -433,6 +444,8 @@ impl CloseWorktreeDialog {
433444
&project_path,
434445
&branch,
435446
&main_repo_path,
447+
folder_id.as_deref(),
448+
folder_name.as_deref(),
436449
monitor.as_ref(),
437450
runner.as_ref(),
438451
);
@@ -469,6 +482,8 @@ impl CloseWorktreeDialog {
469482
let project_path = project_path.clone();
470483
let branch = branch.clone();
471484
let main_repo_path = main_repo_path.clone();
485+
let folder_id = folder_id.clone();
486+
let folder_name = folder_name.clone();
472487
let monitor = monitor.clone();
473488
move || {
474489
hooks::fire_before_worktree_remove(
@@ -479,6 +494,8 @@ impl CloseWorktreeDialog {
479494
&project_path,
480495
&branch,
481496
&main_repo_path,
497+
folder_id.as_deref(),
498+
folder_name.as_deref(),
482499
monitor.as_ref(),
483500
None,
484501
)
@@ -508,6 +525,8 @@ impl CloseWorktreeDialog {
508525
&project_name,
509526
&project_path,
510527
&branch,
528+
folder_id.as_deref(),
529+
folder_name.as_deref(),
511530
monitor.as_ref(),
512531
runner.as_ref(),
513532
);
@@ -543,6 +562,8 @@ impl CloseWorktreeDialog {
543562
&project_path,
544563
&branch,
545564
&main_repo_path,
565+
folder_id.as_deref(),
566+
folder_name.as_deref(),
546567
monitor.as_ref(),
547568
runner.as_ref(),
548569
);

crates/okena-views-terminal/src/layout/terminal_pane/mod.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ impl<D: ActionDispatch + Send + Sync> TerminalPane<D> {
360360
);
361361

362362
// Read fresh path and project info from workspace state
363-
let (project_path, project_name, project_hooks, parent_hooks, is_worktree) = {
363+
let (project_path, project_name, project_hooks, parent_hooks, is_worktree, folder_id, folder_name) = {
364364
let project = ws.project(&self.project_id);
365365
let path = project.map(|p| p.path.clone())
366366
.unwrap_or_else(|| self.project_path.clone());
@@ -371,10 +371,13 @@ impl<D: ActionDispatch + Send + Sync> TerminalPane<D> {
371371
.and_then(|wt| ws.project(&wt.parent_project_id))
372372
.map(|p| p.hooks.clone());
373373
let is_wt = project.map(|p| p.worktree_info.is_some()).unwrap_or(false);
374-
(path, name, hooks_cfg, parent, is_wt)
374+
let folder = ws.folder_for_project_or_parent(&self.project_id);
375+
let fid = folder.map(|f| f.id.clone());
376+
let fname = folder.map(|f| f.name.clone());
377+
(path, name, hooks_cfg, parent, is_wt, fid, fname)
375378
};
376379

377-
let env = hooks::terminal_hook_env(&self.project_id, &project_name, &project_path, is_worktree);
380+
let env = hooks::terminal_hook_env(&self.project_id, &project_name, &project_path, is_worktree, folder_id.as_deref(), folder_name.as_deref());
378381

379382
// Apply shell_wrapper if configured
380383
let global_hooks = settings.hooks;

crates/okena-workspace/src/actions/project.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@ impl Workspace {
8282
self.data.project_order.push(id.clone());
8383
self.notify_data(cx);
8484

85-
let hook_results = hooks::fire_on_project_open(&project_hooks, &id, &name, &path, global_hooks, cx);
85+
let folder = self.folder_for_project_or_parent(&id);
86+
let folder_id = folder.map(|f| f.id.as_str());
87+
let folder_name = folder.map(|f| f.name.as_str());
88+
let hook_results = hooks::fire_on_project_open(&project_hooks, &id, &name, &path, folder_id, folder_name, global_hooks, cx);
8689
self.register_hook_results(hook_results, cx);
8790
id
8891
}
@@ -205,6 +208,9 @@ impl Workspace {
205208
/// Delete a project
206209
pub fn delete_project(&mut self, project_id: &str, global_hooks: &HooksConfig, cx: &mut Context<Self>) {
207210
// Capture project info before removal for the hook
211+
let folder = self.folder_for_project_or_parent(project_id);
212+
let hook_folder_id = folder.map(|f| f.id.clone());
213+
let hook_folder_name = folder.map(|f| f.name.clone());
208214
let hook_info = self.project(project_id).map(|p| {
209215
(p.hooks.clone(), p.id.clone(), p.name.clone(), p.path.clone())
210216
});
@@ -250,7 +256,7 @@ impl Workspace {
250256
self.notify_data(cx);
251257

252258
if let Some((project_hooks, id, name, path)) = hook_info {
253-
hooks::fire_on_project_close(&project_hooks, &id, &name, &path, global_hooks, cx);
259+
hooks::fire_on_project_close(&project_hooks, &id, &name, &path, hook_folder_id.as_deref(), hook_folder_name.as_deref(), global_hooks, cx);
254260
}
255261
}
256262

@@ -467,12 +473,17 @@ impl Workspace {
467473
self.notify_data(cx);
468474

469475
if fire_hooks {
476+
let folder = self.folder_for_project_or_parent(&id);
477+
let folder_id = folder.map(|f| f.id.as_str());
478+
let folder_name = folder.map(|f| f.name.as_str());
470479
let hook_results = hooks::fire_on_worktree_create(
471480
&new_project_hooks,
472481
&id,
473482
&new_project_name,
474483
project_path,
475484
branch,
485+
folder_id,
486+
folder_name,
476487
global_hooks,
477488
cx,
478489
);
@@ -505,12 +516,17 @@ impl Workspace {
505516
}
506517
}
507518

519+
let folder = self.folder_for_project_or_parent(project_id);
520+
let folder_id = folder.map(|f| f.id.as_str());
521+
let folder_name = folder.map(|f| f.name.as_str());
508522
let hook_results = hooks::fire_on_worktree_create(
509523
&hooks_config,
510524
project_id,
511525
&name,
512526
&path,
513527
&branch,
528+
folder_id,
529+
folder_name,
514530
global_hooks,
515531
cx,
516532
);
@@ -637,6 +653,9 @@ impl Workspace {
637653
}
638654

639655
// Capture info before removal for the hook
656+
let folder = self.folder_for_project_or_parent(project_id);
657+
let hook_folder_id = folder.map(|f| f.id.clone());
658+
let hook_folder_name = folder.map(|f| f.name.clone());
640659
let project_hooks = project.hooks.clone();
641660
let project_name = project.name.clone();
642661
let project_path = project.path.clone();
@@ -654,7 +673,7 @@ impl Workspace {
654673
self.delete_project(project_id, global_hooks, cx);
655674

656675
// Fire worktree-specific hook (runs headlessly since project is deleted)
657-
hooks::fire_on_worktree_close(&project_hooks, project_id, &project_name, &project_path, &branch, global_hooks, cx);
676+
hooks::fire_on_worktree_close(&project_hooks, project_id, &project_name, &project_path, &branch, hook_folder_id.as_deref(), hook_folder_name.as_deref(), global_hooks, cx);
658677

659678
Ok(())
660679
}

0 commit comments

Comments
 (0)